New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

cornflux

Package Overview
Dependencies
Maintainers
1
Versions
6
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

cornflux - npm Package Compare versions

Comparing version

to
2.0.0-beta.1

.eslintrc.js

19

CHANGELOG.md

@@ -1,5 +0,16 @@

## 1.1.0
## 2.0.0
- ActionProvider now accepts (and defaults to) an option `passDispatchProp:
Boolean` that will provide the decorated component with `dispatch` as a prop
like ActionEmitters would.
- ActionProvider now provides the decorated component with `dispatch` as a prop
like ActionEmitters would
- **BREAKING** ActionProvider now requires every action handler to yield a
Promise value. As a result, the option `serviceWrapper` has been removed.
- **BREAKING** ActionProvider option `reducer` has been renamed to `reduce`
- **BREAKING** ActionProvider option `verbose` has been removed
- **BREAKING** Action payload can now have an arbitrary arity (up from 1)
- **BREAKING** Action aliasing has been removed, as a result the `ActionProxy`
symbol is no longer exported
- ActionEmitter will now throw an error if initialized with missing `actions`
argument or one that is not an array
- ActionEmitter's warning for unknown actions is removed in the production
build now
- No longer using React.createClass, React 15.4.1+ is now required

@@ -10,6 +10,31 @@ const path = require('path');

'karma-webpack',
'karma-coverage',
],
browsers: [ 'Chrome' ],
browsers: [ 'ChromeWithoutSandbox' ],
customLaunchers: {
ChromeWithoutSandbox: {
base: 'ChromeHeadless',
flags: [
'--no-default-browser-check',
'--no-first-run',
'--disable-default-apps',
'--disable-popup-blocking',
'--disable-translate',
'--disable-web-security',
]
}
},
coverageReporter: {
dir: path.join(root, 'coverage'),
subdir: '.',
reporters: [
{ type: 'json', file: 'report.json' },
{ type: 'html' },
{ type: 'text-summary' }
]
},
frameworks: [

@@ -27,12 +52,23 @@ 'mocha'

reporters: [ 'dots' ],
reporters: [ 'dots', 'coverage' ],
webpack: {
externals: { 'Promise': true },
mode: 'development',
module: {
loaders: [
rules: [
{
test: /\.js$/,
include: path.join(root, 'src'),
loader: 'babel'
loader: 'babel-loader',
options: {
babelrc: false,
presets: [ 'env', 'react' ],
plugins: [
['istanbul', {
exclude: [
"**/__tests__"
]
}]
]
}
}

@@ -54,5 +90,7 @@ ]

webpackServer: {
noInfo: true
noInfo: true,
quiet: true,
stats: false
}
});
};
};
{
"name": "cornflux",
"version": "1.1.0",
"version": "2.0.0-beta.1",
"description": "A dispatching library for React applications promoting data encapsulation.",
"main": "src/index.js",
"main": "dist/cornflux.js",
"module": "src/index.js",
"scripts": {
"build": "babel src -d lib",
"build": "NODE_ENV=production webpack",
"lint": "eslint src",
"prepublish": "npm run lint && npm run test -- --single-run && npm run build",
"test": "karma start"

@@ -29,11 +32,15 @@ },

"babel-cli": "6.18.0",
"babel-core": "6.21.0",
"babel-loader": "6.2.10",
"babel-preset-es2015": "6.18.0",
"babel-preset-react": "6.16.0",
"babel-core": "6.26.3",
"babel-loader": "7.1.4",
"babel-plugin-istanbul": "4.1.6",
"babel-preset-env": "1.7.0",
"babel-preset-react": "6.24.1",
"chai": "3.5.0",
"karma": "1.3.0",
"karma-chrome-launcher": "2.0.0",
"eslint": "5.0.1",
"eslint-plugin-react": "7.10.0",
"karma": "2.0.4",
"karma-chrome-launcher": "2.2.0",
"karma-coverage": "1.1.2",
"karma-mocha": "1.3.0",
"karma-webpack": "1.8.1",
"karma-webpack": "3.0.0",
"mocha": "3.2.0",

@@ -44,6 +51,9 @@ "react": "15.4.1",

"react-schema": "2.0.0",
"webpack": "1.14.0"
"sinon": "6.0.1",
"webpack": "4.14.0",
"webpack-bundle-analyzer": "2.13.1",
"webpack-cli": "3.0.8"
},
"peerDependencies": {
"react": "0.14 || 15",
"react": ">= 15.4.1",
"react-dom": "0.14 || 15",

@@ -53,4 +63,5 @@ "react-schema": "2"

"dependencies": {
"invariant": "2.2.4",
"warning": "3.0.0"
}
}

@@ -1,11 +0,11 @@

# cornflux (beta)
# cornflux
A library for dispatching events in a React application using the react [context](https://facebook.github.io/react/docs/context.html) as a data bus.
## Motivation
The goal of this library is to achieve a layer of _data isolation_ where
application state is confined to "data components" that can only be interfaced
with by other components via action functions.
The TL;DR version: data isolation. Protect your application data and state
storage in components that can only be interfaced with via _actions_.
The longer version can be found in [this post on medium](https://medium.com/@amireh/on-privacy-with-react-context-aa77ffd08509#.qz4awmpol)
The longer version can be found in [this post on
medium](https://medium.com/@amireh/on-privacy-with-react-context-aa77ffd08509#.qz4awmpol)
if you have the patience.

@@ -17,6 +17,11 @@

# make sure you have the dependencies first:
npm install --save react react-dom react-schema
npm install --save react react-dom
npm install --save cornflux
```
The source code is not transpiled; you will need a transpiler like
[Babel.js](http://babeljs.io) to consume it. However, a built-version is
provided under `dist/` but it expects `React` and `ReactDOM` to be available on
`window`.
## Usage

@@ -32,5 +37,4 @@

Construct an "acting" version of the component that should be performing
side-effects with the `ActionProvider` decorator and define the action
handlers:
Use the `ActionProvider` decorator to construct a version of the component that
is able to receive action requests and carry them out.

@@ -64,5 +68,5 @@ ```javascript

Construct an "emitting" version of the component that needs to request an
action be performed with the `ActionEmitter` decorator. You must explicitly
specify the list of action names that it will trigger.
Use the `ActionEmitter` decorator to construct a version of a component that
needs to dispatch action requests. You must explicitly specify which actions
the component is allowed to dispatch.

@@ -114,58 +118,32 @@ ```javascript

state: Object,
payload: Object,
Object.<{
payload: ...Any,
delegate: {
dispatch: (String, Object) -> Any,
propagate: () -> Any
}>
) -> Any
}
) -> Promise
```
The first argument, `state`, is either the reduced state of the container
if a `reducer` was defined, otherwise it's the component instance itself.
The first argument, `state`, is either the reduced state of the container if
`reduce` was defined, otherwise it's the component instance itself.
The `payload` argument is the action payload that was provided when the action
was emitted.
was emitted. It is "spread out" to as many arguments the dispatch call was
provided by an emitter (vararg).
The third argument is an object containing two functions:
The last argument is an object containing two functions:
- `dispatch` which lets your handler dispatch other events. The signature is
is similar to emitter's `dispatch`.
- `propagate` is a callback for yielding (or "bubbling") the action to a
provider higher in the chain. Note that it accepts NO parameters, the action
is yielded as-is.
- `dispatch` to dispatch other events to the same provider. The signature is is
similar to emitter's `dispatch`.
- `propagate` to yield (or "bubble") the action to a provider higher in the
tree. Note that it accepts NO parameters; the action is yielded as-is.
#### ?displayName: `String`
#### ?reduce: `(component) -> Object`
A custom display name for the decorated component.
Given the rendered component instance, generate the state to pass to action
handlers.
Defaults to: `ActionProvider($ORIGINAL_COMPONENT_DISPLAY_NAME)`
Defaults to: the identity function where the component instance itself is
passed through (which exposes `props`, `state`, `setState`, etc.)
#### ?reducer: `(component) -> Object`
A funcion for "reducing" the component instance into some state that the action
handlers need.
This option gives you a greater degree of what the action handlers may end up
touching, if you're really paranoid.
Defaults to: `(x) -> x`. The identity function where the component instance
itself is passed through.
#### ?serviceWrapper: `(Any) -> Any`
Compatibility option for applications that use promises or relied on earlier
dispatchers always generating promises (or something alike.)
The function will receive the return value of the action handler and can
augment it in any way it sees fit.
Defaults to: `(x) -> x`
#### ?verbose: `Boolean`
Turn this on if you want diagnostic messages be output to the console for
debugging.
Defaults to: `false`
### ActionEmitter: `(Component, options: Object) -> Component`

@@ -172,0 +150,0 @@

import React, { PropTypes } from 'react';
import warning from 'warning';
import invariant from 'invariant';
const ActionEmitter = (Component, { actions, propName = 'dispatch' }) => React.createClass({
displayName: `ActionEmitter(${Component.displayName})`,
const ActionEmitter = (Component, { actions, propName = 'dispatch' }) => {
invariant(Array.isArray(actions),
`Missing required argument "actions" by ActionEmitter for component "${Component.displayName}}".`
)
contextTypes: {
availableActions: PropTypes.arrayOf(PropTypes.string),
dispatch: PropTypes.func.isRequired,
},
class WithActions extends React.Component {
componentWillMount() {
// istanbul ignore else
if (process.env.NODE_ENV !== 'production') {
const availableActions = this.context.availableActions;
const unknownActions = actions.filter(action => {
return availableActions.indexOf(action) === -1;
});
componentWillMount() {
const availableActions = this.context.availableActions || [];
const unknownActions = actions.filter(action => {
return availableActions.indexOf(action) === -1;
});
warning(unknownActions.length === 0,
`The component "${Component.displayName}" may emit actions that are not ` +
`known to be supported by any provider:\n` +
stringifyList(unknownActions, " ")
);
}
}
warning(unknownActions.length === 0,
`The component "${Component.displayName}" may emit actions that are not ` +
`known to be supported by any provider.\n` +
`These actions are:\n${stringifyList(unknownActions, " ")}.`
);
},
render() {
return (
<Component
{...this.props}
{...{ [propName]: this.context.dispatch }}
/>
)
}
};
render() {
const dispatchingProps = {};
WithActions.displayName = `ActionEmitter(${Component.displayName})`;
WithActions.contextTypes = {
availableActions: PropTypes.arrayOf(PropTypes.string).isRequired,
dispatch: PropTypes.func.isRequired,
};
dispatchingProps[propName] = this.dispatch;
return WithActions;
}
return <Component {...this.props} {...dispatchingProps} />
},
dispatch(type, payload) {
return this.context.dispatch(type, payload);
}
});
function stringifyList(list, indent = "") {

@@ -39,0 +47,0 @@ return list.map(x => `${indent}- ${x}`).join('\n')

import React, { PropTypes } from 'react';
import Promise from 'Promise';
import ActionProxy from './ActionProxy';
import invariant from 'invariant';
const Identity = x => x;
const createActionProxy = alias => new ActionProxy(alias);
const ActionProvider = function(Component, {
actions,
displayName,
reducer = Identity,
serviceWrapper = Identity,
verbose = false,
passDispatchProp = true,
}) {
const debugLog = verbose ? console.debug.bind(console) : Function.prototype;
return React.createClass({
displayName: displayName || `ActionProvider(${Component.displayName})`,
contextTypes: {
availableActions: PropTypes.arrayOf(PropTypes.string),
dispatch: PropTypes.func,
},
childContextTypes: {
availableActions: PropTypes.arrayOf(PropTypes.string),
dispatch: PropTypes.func,
},
const ActionProvider = function(Component, { actions, reduce = Identity }) {
const actionNames = Object.keys(actions);
class WithActions extends React.Component {
getChildContext() {
return {
availableActions: (this.context.availableActions || []).concat(Object.keys(actions)),
dispatch: this.dispatchAction,
availableActions: (this.context.availableActions || []).concat(actionNames),
dispatch: this.bindings.dispatchAction,
}
},
}
componentWillMount() {
constructor() {
super()
this.unmounted = false;
this.actionBuffer = [];
},
this.bindings = {
dispatchAction: this.dispatchAction.bind(this)
};
}
componentDidMount() {
this.flushQueuedActions();
},
}
componentDidUpdate() {
this.flushQueuedActions();
},
componentWillUnmount() {
this.bindings = null;
this.unmounted = true;
// TODO: possible leak here? what about pending promises?
this.actionBuffer = null;
},
}
render() {
const decoratorProps = {};
return (
<Component
ref="component"
dispatch={this.bindings.dispatchAction}
{...this.props}
/>
)
}
if (passDispatchProp) {
decoratorProps.dispatch = this.dispatchAction;
}
return <Component ref="container" {...decoratorProps} {...this.props} />
},
dispatchAction(type, payload) {
dispatchAction(type, ...payload) {
// Not an action we provide? Propagate
if (!actions.hasOwnProperty(type)) {
return this.propagateAction(type, payload);
return this.propagateAction(type, ...payload);
}
// An alias? resolve it and re-dispatch
else if (actions[type] instanceof ActionProxy) {
return this.dispatchAction(actions[type].type, payload);
}
// Not ready yet? Queue it until we are. This happens if a child emitter

@@ -78,3 +55,3 @@ // dispatches an action during the componentWillMount or componentDidMount

// ready at that point yet.
else if (!this.refs.container) {
else if (!this.refs.component) {
return this.dispatchActionWhenReady(type, payload);

@@ -84,22 +61,24 @@ }

const actionHandler = actions[type];
const state = reducer(this.refs.container);
const state = reduce(this.refs.component);
debugLog(`Dispatching action "${type}":`, payload);
// TODO: validate payload
return serviceWrapper(
actionHandler(state, payload, {
propagate: this.propagateAction.bind(null, type, payload),
dispatch: this.dispatchAction,
})
);
const returnValue = actionHandler(state, ...payload, {
propagate: this.propagateAction.bind(this, type, ...payload),
dispatch: this.bindings.dispatchAction,
})
invariant(returnValue && typeof returnValue.then === 'function',
`Handler for action "${type}" yielded a non-Promise value`
)
return returnValue;
}
},
}
dispatchActionWhenReady(type, payload) {
debugLog(`Deferring action "${type}" until container is ready...`);
return new Promise((resolve, reject) => {
if (!this.isMounted()) {
// possible race condition; child component dispatches an action while
// the provider is being unmounted
if (this.unmounted) {
reject();

@@ -111,7 +90,7 @@ }

});
},
}
propagateAction(type, payload) {
propagateAction(type, ...payload) {
if (this.context.dispatch) {
return this.context.dispatch(type, payload);
return this.context.dispatch(type, ...payload);
}

@@ -121,17 +100,24 @@ else {

}
},
}
flushQueuedActions() {
this.actionBuffer.splice(0).forEach(({ type, payload, promise }) => {
debugLog(`Dispatching deferred action "${type}".`);
this.dispatchAction(type, payload).then(promise.resolve, promise.reject);
this.dispatchAction(type, ...payload).then(promise.resolve, promise.reject);
});
}
})
};
WithActions.displayName = `ActionProvider(${Component.displayName})`;
WithActions.contextTypes = {
availableActions: PropTypes.arrayOf(PropTypes.string),
dispatch: PropTypes.func,
};
WithActions.childContextTypes = {
availableActions: PropTypes.arrayOf(PropTypes.string),
dispatch: PropTypes.func,
};
return WithActions;
}
ActionProvider.ActionProxy = createActionProxy;
export default ActionProvider;
export { createActionProxy as ActionProxy };

@@ -1,10 +0,7 @@

import ActionProvider, { ActionProxy } from './ActionProvider';
import ActionProvider from './ActionProvider';
import ActionEmitter from './ActionEmitter';
import { PropTypes as Types } from 'react-schema';
export {
ActionProvider,
ActionProxy,
ActionEmitter,
Types,
};
const webpack = require('webpack');
const common = require('./common');
const path = require('path');
const root = path.resolve(__dirname);
// const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
module.exports = {
devtool: 'eval',
mode: 'production',
resolve: common.resolve,
output: {
filename: 'cornflux.js',
path: path.join(root, 'dist'),
},
externals: {
'react': 'React',
'react-dom': 'ReactDOM',
},
module: {
rules: [
{
test: /\.js$/,
include: [ path.join(root, 'src') ],
exclude: [ /\.test.js$/ ],
loader: 'babel-loader',
options: {
presets: [ 'env', 'react' ]
}
}
]
},
plugins: [
new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV || 'development'),
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV),
}),
// new BundleAnalyzerPlugin(),
],
module: common.getModuleConfigWithCustomLoaders(x => {
if (x.id === 'js') {
return Object.assign({}, x, {
loaders: undefined,
loader: 'babel',
});
}
else {
return x;
}
}),
};