UNPKG

attadux

Version:

Implementation of the redux-modular-ducks, forked from the extensible-duck implementation and extended to include spected validators, state machines, helpers, web workers, effect handling, action multipliers, action enhancers, action throttling/debouncing

460 lines (355 loc) 21.7 kB
'use strict'; exports.__esModule = true; exports.isActionTypeInCurrentState = exports.getNextState = exports.getCurrentState = exports.getDefaultStateForMachines = exports.getStateMachinesPropPath = exports.createMachines = exports.createMachineStates = exports.isTransitionPossible = exports.isEachTransitionAmongMachineStates = exports.areStateMachineInputsActionTypes = exports.areInputsAndTransitionsStrings = exports.areStateNamesStrings = exports.invalidStateMachineInputs = exports.getStateInputsForAllMachines = exports.getStateInputsForMachine = exports.getTransitionsForMachine = exports.addTransitionsToState = exports.noMachines = undefined; var _any = require('ramda/src/any'); var _any2 = _interopRequireDefault(_any); var _pair = require('ramda/src/pair'); var _pair2 = _interopRequireDefault(_pair); var _mergeDeepRight = require('ramda/src/mergeDeepRight'); var _mergeDeepRight2 = _interopRequireDefault(_mergeDeepRight); var _always = require('ramda/src/always'); var _always2 = _interopRequireDefault(_always); var _when = require('ramda/src/when'); var _when2 = _interopRequireDefault(_when); var _split = require('ramda/src/split'); var _split2 = _interopRequireDefault(_split); var _ifElse = require('ramda/src/ifElse'); var _ifElse2 = _interopRequireDefault(_ifElse); var _both = require('ramda/src/both'); var _both2 = _interopRequireDefault(_both); var _last = require('ramda/src/last'); var _last2 = _interopRequireDefault(_last); var _ = require('ramda/src/__'); var _2 = _interopRequireDefault(_); var _contains = require('ramda/src/contains'); var _contains2 = _interopRequireDefault(_contains); var _head = require('ramda/src/head'); var _head2 = _interopRequireDefault(_head); var _pick = require('ramda/src/pick'); var _pick2 = _interopRequireDefault(_pick); var _reduce = require('ramda/src/reduce'); var _reduce2 = _interopRequireDefault(_reduce); var _path = require('ramda/src/path'); var _path2 = _interopRequireDefault(_path); var _has = require('ramda/src/has'); var _has2 = _interopRequireDefault(_has); var _useWith = require('ramda/src/useWith'); var _useWith2 = _interopRequireDefault(_useWith); var _toPairs = require('ramda/src/toPairs'); var _toPairs2 = _interopRequireDefault(_toPairs); var _either = require('ramda/src/either'); var _either2 = _interopRequireDefault(_either); var _allPass = require('ramda/src/allPass'); var _allPass2 = _interopRequireDefault(_allPass); var _is = require('ramda/src/is'); var _is2 = _interopRequireDefault(_is); var _all = require('ramda/src/all'); var _all2 = _interopRequireDefault(_all); var _difference = require('ramda/src/difference'); var _difference2 = _interopRequireDefault(_difference); var _converge = require('ramda/src/converge'); var _converge2 = _interopRequireDefault(_converge); var _prop = require('ramda/src/prop'); var _prop2 = _interopRequireDefault(_prop); var _keys = require('ramda/src/keys'); var _keys2 = _interopRequireDefault(_keys); var _map = require('ramda/src/map'); var _map2 = _interopRequireDefault(_map); var _filter = require('ramda/src/filter'); var _filter2 = _interopRequireDefault(_filter); var _values = require('ramda/src/values'); var _values2 = _interopRequireDefault(_values); var _flatten = require('ramda/src/flatten'); var _flatten2 = _interopRequireDefault(_flatten); var _uniq = require('ramda/src/uniq'); var _uniq2 = _interopRequireDefault(_uniq); var _defaultTo = require('ramda/src/defaultTo'); var _defaultTo2 = _interopRequireDefault(_defaultTo); var _assocPath = require('ramda/src/assocPath'); var _assocPath2 = _interopRequireDefault(_assocPath); var _unless = require('ramda/src/unless'); var _unless2 = _interopRequireDefault(_unless); var _compose = require('ramda/src/compose'); var _compose2 = _interopRequireDefault(_compose); var _curry = require('ramda/src/curry'); var _curry2 = _interopRequireDefault(_curry); var _isEmpty = require('ramda/src/isEmpty'); var _isEmpty2 = _interopRequireDefault(_isEmpty); var _isNil = require('ramda/src/isNil'); var _isNil2 = _interopRequireDefault(_isNil); var _anyPass = require('ramda/src/anyPass'); var _anyPass2 = _interopRequireDefault(_anyPass); var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; var _util = require('../util'); var _types = require('../types'); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } /** * Simple check to see whether a given duck's machines is empty. * * @func * @sig {k: v} -> Boolean * @param {Object} machines A duck's collection of state machines * @returns {Boolean} whether or not the machines are empty */ var noMachines = exports.noMachines = (0, _anyPass2['default'])([_isNil2['default'], _isEmpty2['default']]); /** * Adds an object containing the current state of all the state machines onto * a given section of the redux store * * @func * @sig {k: v} -> {k: v} -> {k: v} * @param {Object} duck A duck instance, containing the state machines and the * prop name/path to the section of the store where their state is tracked * @param {*} state The current state of this portion of the store * @returns {Object} original state (or initialState, if state is nil) plus * current state of all the state machines */ var addTransitionsToState = exports.addTransitionsToState = (0, _curry2['default'])(function (state, _ref) { var _ref$initialState = _ref.initialState; var initialState = _ref$initialState === undefined ? {} : _ref$initialState; var _ref$stateMachinesPro = _ref.stateMachinesPropName; var stateMachinesPropName = _ref$stateMachinesPro === undefined ? ['states'] : _ref$stateMachinesPro; return (0, _compose2['default'])((0, _unless2['default'])((0, _util.hasNestedProp)(stateMachinesPropName), (0, _assocPath2['default'])(stateMachinesPropName, {})), (0, _defaultTo2['default'])(initialState))(state); }); /** * Retrieves a unique list all the state transitions possible for a given machine. * State machines are objects whose keys are states and whose values are objects * inside of which the transition-to state is the value of each of its key/value pairs. * * @func * @sig {k: v} -> [String] * @param {Object} machine A single state machine * @returns {String[]} A list of unique state transitions for the provided machine */ var getTransitionsForMachine = exports.getTransitionsForMachine = (0, _compose2['default'])(_uniq2['default'], (0, _filter2['default'])(_util.isStringieThingie), _flatten2['default'], (0, _map2['default'])(_values2['default']), _values2['default']); /** * Retrieves a unique list all the inputs for a given state machine (which correspond to Action Types). * State machines are objects whose keys are states and whose values are objects * inside of which the input value is the key of each of its key/value pairs. * * @func * @sig {k: v} -> [String] * @param {Object} machine A single state machine * @returns {String[]} A list of unique inputs to the provided state machine */ var getStateInputsForMachine = exports.getStateInputsForMachine = (0, _compose2['default'])(_uniq2['default'], (0, _filter2['default'])(_util.isStringieThingie), _flatten2['default'], (0, _map2['default'])(_keys2['default']), _values2['default']); /** * Retrieves a unique list all the inputs for all of a given duck's state machines. * State machines are objects whose keys are states and whose values are objects * inside of which the input value is the key of each of its key/value pairs. * * @func * @sig {k: v} -> [String] * @param {Object} duck A duck containing one or more state machines * @returns {String[]} A list of unique inputs to all the state machines for the provided duck */ var getStateInputsForAllMachines = exports.getStateInputsForAllMachines = (0, _compose2['default'])(_uniq2['default'], _flatten2['default'], (0, _map2['default'])(getStateInputsForMachine), _values2['default'], (0, _prop2['default'])('machines')); /** * Retrieves any invalid inputs from a given duck's state machines. * State machines are objects whose keys are states and whose values are objects * inside of which the input value is the key of each of its key/value pairs. * * @func * @sig {k: v} -> [String] * @param {Object} duck A duck containing one or more state machines and action types * @returns {String[]} A list of any invalid inputs for the state machines of the provided duck */ var invalidStateMachineInputs = exports.invalidStateMachineInputs = (0, _converge2['default'])(_difference2['default'], [getStateInputsForAllMachines, _types.getTypes]); /** * Checks whether all a given state machine's states are string values. * State machines are objects whose keys are states and whose values are objects * inside of which the input value is the key of each of its key/value pairs. * * @func * @sig {k: v} -> Boolean * @param {Object} machine A single state machine * @returns {Boolean} whether or not the states for the state machine are strings */ var areStateNamesStrings = exports.areStateNamesStrings = (0, _compose2['default'])((0, _all2['default'])((0, _is2['default'])(String)), _keys2['default']); /** * Checks whether all a given state machine's inputs and transition names are string values. * State machines are objects whose keys are states and whose values are objects * inside of which the input value is the key and transition is the value of each of its key/value pairs. * * @func * @sig {k: v} -> Boolean * @param {Object} machine A single state machine * @returns {Boolean} whether or not the inputs and transitions for the state machine are strings */ var areInputsAndTransitionsStrings = exports.areInputsAndTransitionsStrings = (0, _compose2['default'])((0, _all2['default'])((0, _allPass2['default'])([_util.isPlainObj, (0, _either2['default'])(_isEmpty2['default'], (0, _compose2['default'])((0, _all2['default'])((0, _all2['default'])((0, _is2['default'])(String))), _toPairs2['default']))])), _values2['default']); /** * Checks whether all the state machine inputs for a given duck correspond to its action types. * State machines are objects whose keys are states and whose values are objects * inside of which the input value is the key of each of its key/value pairs. * * @func * @sig {k: v} -> Boolean * @param {Object} duck A duck containing one or more state machines and action types * @returns {Boolean} whether or not all the inputs for the state machines are among the duck's action types */ var areStateMachineInputsActionTypes = exports.areStateMachineInputsActionTypes = (0, _compose2['default'])((0, _all2['default'])(_isEmpty2['default']), invalidStateMachineInputs); /** * Checks if all a state machine's transitions are states in that machine. * State machines are objects whose keys are states and whose values are objects * inside of which the transition-to state is the value of each of its key/value pairs. * * @func * @sig {k: v} -> Boolean * @param {Object} machine A single state machine * @returns {Boolean} whether or not all the transition names are also states for a given state machine */ var isEachTransitionAmongMachineStates = exports.isEachTransitionAmongMachineStates = (0, _useWith2['default'])(_difference2['default'], [getTransitionsForMachine, _keys2['default']]); /** * Checks to see whether a given machine allows for a given transition, provided its current state * * @func * @sig String -> String -> {k: v} -> Boolean * @param {String} transitionName A string value representing a potential state transition * @param {String} currentState A string value representing the current state * @param {Object} machine A state machine which contains the possible states and their associated transitions * @returns {Boolean} whether or not the transition is possible from the current state */ var isTransitionPossible = exports.isTransitionPossible = (0, _curry2['default'])(function (transitionName, currentState, machine) { return (0, _compose2['default'])((0, _has2['default'])((0, _util.coerceToString)(transitionName)), (0, _defaultTo2['default'])({}), (0, _path2['default'])([(0, _util.coerceToString)(currentState)]), (0, _defaultTo2['default'])({}))(machine); }); /** * Creates a "machine state" object, which can be combined with others to create a single state machine. * The keys for a machine state object (called inputs or input values) correspond to the names of redux action types. * The values for a machine state object (called transitions) are merely * the names of fellow machine state objects (also created by this function). * * @func * @sig {k: v} -> {k: v} -> {k: v} * @param {Object} machine A single state machine * @param {Object} types An object whose keys/values correspond * to redux actions and will be used to set up inputs for the state machine * @returns {Object} a validated, immutable state machine */ var createMachineStates = exports.createMachineStates = (0, _curry2['default'])(function () { var machine = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0]; var types = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1]; return (0, _compose2['default'])(Object.freeze, (0, _reduce2['default'])(_util.listOfPairsToOneObject, {}), (0, _map2['default'])(function (_ref2) { var state = _ref2[0]; var transitions = _ref2[1]; return [state, (0, _pick2['default'])((0, _compose2['default'])((0, _map2['default'])(_head2['default']), (0, _filter2['default'])((0, _compose2['default'])((0, _contains2['default'])(_2['default'], (0, _values2['default'])(types)), _head2['default'])), (0, _filter2['default'])((0, _compose2['default'])((0, _has2['default'])(_2['default'], machine), _last2['default'])), (0, _filter2['default'])((0, _all2['default'])((0, _is2['default'])(String))), _toPairs2['default'])(transitions), transitions)]; }), (0, _filter2['default'])((0, _both2['default'])((0, _compose2['default'])((0, _is2['default'])(String), _head2['default']), (0, _compose2['default'])(_util.isPlainObj, _last2['default']))), _toPairs2['default'])(machine); }); /** * Creates a frozen (immutable) object whose keys are all the possible "current state" * values for the machine and whose values are objects containing each action type that * may produce a transition to another state. * * @func * @sig {k: v} -> {k: v} -> {k: v} * @param {Object} machines An object full of one or more state machines * @param {Object} duck A duck instance * @returns {Object} an object of validated, immutable state machines */ var createMachines = exports.createMachines = (0, _curry2['default'])(function (machines, duck) { return (0, _compose2['default'])(Object.freeze, (0, _reduce2['default'])(_util.listOfPairsToOneObject, {}), (0, _map2['default'])(function (_ref3) { var name = _ref3[0]; var machine = _ref3[1]; return [name, createMachineStates(machine, duck.types)]; }), _toPairs2['default'])(machines); }); /** * Creates an string path (array) that the state machine functions will use to * lookup and alter the current state of one or more of the state machines. * The path supplied to this function can be a single prop name (String) or * an Array of prop names that represent a nested prop's path. * And if a String is passed in, a dot-separated format is also supported. * * @func * @sig String -> [String] * @param {String|String[]} path The prop name or path to the section of the * store where the state machines current state is tracked. * @returns {String[]} an array of strings representing the nested path to the * prop where state machines current state is tracked in the redux store */ var getStateMachinesPropPath = exports.getStateMachinesPropPath = (0, _ifElse2['default'])((0, _allPass2['default'])([(0, _is2['default'])(String), (0, _contains2['default'])('.'), _util.isValidPropName]), (0, _split2['default'])('.'), (0, _compose2['default'])((0, _when2['default'])(_isEmpty2['default'], (0, _always2['default'])(['states'])), (0, _filter2['default'])(_util.isNotBlankString), (0, _when2['default'])((0, _is2['default'])(String), Array))); /** * Builds a flattened object where the keys are the names of each of the duck's state machines, * and the values are each set to 'initial'. * * @func * @sig {k: v} -> {k: v} * @param {Object} machines An object containing the definitions for one or more state machines * @returns {Object} a flattened object where the current state of each state machine will be tracked */ var getDefaultStateForMachines = exports.getDefaultStateForMachines = function getDefaultStateForMachines() { var machines = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0]; return (0, _reduce2['default'])(function (obj, key) { var _extends2; return _extends({}, obj, (_extends2 = {}, _extends2[key] = 'initial', _extends2)); }, {}, (0, _keys2['default'])(machines || {})); }; /** * Retrieves the section of the redux store where the current state is being * tracked for one or more state machines. The shape of this section of the store * is usually just a flattened object with a single value for each named state * machine, representing its current state. Each possible state for any of those * is defined in the Ducks. * * @func * @sig {k: v} -> {k: v} -> {k: v} * @param {Object} duck A duck instance, containing the state machines and the * prop name/path to the section of the store where their state is tracked * @param {*} state The current state of this portion of the store * @returns {Object} A flattened Object containing the names of each state * machine (String) and its current state (String) as key/value pairs */ var getCurrentState = exports.getCurrentState = function getCurrentState() { var _ref4 = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0]; var _ref4$machines = _ref4.machines; var machines = _ref4$machines === undefined ? {} : _ref4$machines; var stateMachinesPropName = _ref4.stateMachinesPropName; return (0, _compose2['default'])((0, _mergeDeepRight2['default'])((0, _map2['default'])((0, _always2['default'])('initial'), machines)), (0, _pick2['default'])((0, _keys2['default'])(machines)), (0, _defaultTo2['default'])({}), (0, _path2['default'])((0, _util.coerceToArray)(stateMachinesPropName))); }; /** * Based on the current state of the machines (managed in the store itself) and * the rules defined for each state machine (in the ducks), an action is processed * and may cause one or more ore the state machines to transition. * * @func * @sig {k: v} -> {k: v} -> {k: v} * @param {Object|*} state The current state of this portion of the store * @param {Object} action A dispatched redux action * @param {Object} duck A duck instance, containing the state machines and the * prop name/path to the section of the store where their state is tracked * @returns {Object} A flattened Object containing the names of each state * machine (String) and its current state (String) as key/value pairs */ var getNextState = exports.getNextState = (0, _curry2['default'])(function (state, action, _ref5) { var _ref5$machines = _ref5.machines; var machines = _ref5$machines === undefined ? {} : _ref5$machines; var stateMachinesPropName = _ref5.stateMachinesPropName; var currentState = getCurrentState({ machines: machines, stateMachinesPropName: stateMachinesPropName })(state); return (0, _compose2['default'])((0, _reduce2['default'])(_util.listOfPairsToOneObject, {}), (0, _map2['default'])(function (_ref6) { var name = _ref6[0]; var machine = _ref6[1]; return (0, _compose2['default'])((0, _pair2['default'])(name), (0, _unless2['default'])(_util.isNotNil, (0, _always2['default'])(currentState[name])), (0, _prop2['default'])(action.type), (0, _defaultTo2['default'])({}), (0, _prop2['default'])(currentState[name]))(machine); }), _toPairs2['default'])(machines); }); /** * Checks whether a dispatched action's type is listed among the * current state's input values for one or more of the state machines. * * @func * @sig {k: v} -> {k: v} -> {k: v} -> Boolean * @param {Object|*} state The current state of this portion of the store * @param {Object} action A dispatch redux action * @param {Object} duck A duck, which contains both state machines and the * corresponding prop name/path in the redux store where their current state is tracked * @returns {Boolean} whether or not the dispatched action is listed as an input * value for the state machine(s) current state */ var isActionTypeInCurrentState = exports.isActionTypeInCurrentState = (0, _curry2['default'])(function (state, action, _ref7) { var machines = _ref7.machines; var stateMachinesPropName = _ref7.stateMachinesPropName; var currentState = getCurrentState({ machines: machines, stateMachinesPropName: stateMachinesPropName })(state); return (0, _compose2['default'])((0, _any2['default'])(function (_ref8) { var name = _ref8[0]; var machine = _ref8[1]; return (0, _compose2['default'])(_util.isNotNil, (0, _prop2['default'])(action.type), (0, _defaultTo2['default'])({}), (0, _prop2['default'])(_2['default'], machine), (0, _prop2['default'])(name))(currentState); }), _toPairs2['default'])(machines); });