UNPKG

redux-automata

Version:
398 lines (342 loc) 11.9 kB
'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); var _inheritsLoose = require('@babel/runtime/helpers/inheritsLoose'); function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; } var _inheritsLoose__default = /*#__PURE__*/_interopDefaultLegacy(_inheritsLoose); var ACTION_TYPE_PREFIX = "@@AUTOMATA"; var StateOptionsEx = /*#__PURE__*/function () { function StateOptionsEx(options, stateOptions) { this.options = options; this.stateOptions = stateOptions; } var _proto = StateOptionsEx.prototype; _proto.in = function _in(state) { return this.options.in(state); }; _proto.on = function on(actionFunc) { return this.stateOptions.on(actionFunc); }; return StateOptionsEx; }(); var ActionOptions = /*#__PURE__*/function () { function ActionOptions(sourceStates, actionType, smOptions, stateOptions) { this.sourceStates = sourceStates; this.actionType = actionType; this.smOptions = smOptions; this.stateOptions = stateOptions; this.transitions = []; } var _proto = ActionOptions.prototype; _proto.goTo = function goTo(state) { this.targetState = state.stateName; return new StateOptionsEx(this.smOptions, this.stateOptions); }; _proto.execute = function execute(transition) { this.transitions.push(transition); return this; }; _proto.noop = function noop() { return new StateOptionsEx(this.smOptions, this.stateOptions); }; _proto.createArcs = function createArcs() { var sourceStates = this.sourceStates, actionType = this.actionType, targetState = this.targetState, transitions = this.transitions; return sourceStates.map(function (sourceState) { return { actionType: actionType, sourceState: sourceState, targetState: targetState, transitions: transitions }; }); }; return ActionOptions; }(); var StateOptions = /*#__PURE__*/function () { function StateOptions(sourceStates, smOptions) { this.sourceStates = sourceStates; this.smOptions = smOptions; this.builders = []; } var _proto = StateOptions.prototype; _proto.on = function on(action) { var builder = new ActionOptions(this.sourceStates, action.actionType, this.smOptions, this); this.builders.push(builder); return builder; }; _proto.or = function or(state) { this.sourceStates.push(state.stateName); return this; }; _proto.getArcs = function getArcs() { return this.builders.reduce(function (a, b) { return a.concat(b.createArcs()); }, []); }; return StateOptions; }(); var Automata = /*#__PURE__*/function () { function Automata(automataName) { this.automataName = automataName; this.states = []; this.options = []; this.initial = Object.assign({}); this.current = this.initial; } var _proto = Automata.prototype; _proto.in = function _in(state) { var existingState = this.states.find(function (_) { return _.stateName === state.stateName; }); if (!existingState) throw new Error("State should be defined using this.state(...) method."); var option = new StateOptions([existingState.stateName], this); this.options.push(option); return option; }; _proto.inAny = function inAny() { var option = new StateOptions(this.states.map(function (_) { return _.stateName; }), this); this.options.push(option); return option; }; _proto.beginWith = function beginWith(state) { this.graphCache = undefined; var existingState = this.states.find(function (_) { return _.stateName === state.stateName; }); if (!existingState) throw new Error("State should be previously defined using this.state(...) method."); this.initial = Object.assign(state({}, undefined), { __sm_state: existingState.stateName }); }; _proto.state = function state(name, reducer) { this.graphCache = undefined; if (!name) throw new Error("State name can't be empty, null or undefined."); var duplicate = this.states.find(function (_) { return _.stateName === name; }); if (duplicate) throw new Error("State with the same name already exist: " + name); var newState = Object.assign(reducer, { stateName: name }); this.states.push(newState); return newState; }; _proto.action = function action(type) { var _this = this; this.graphCache = undefined; var actionType = ACTION_TYPE_PREFIX + " " + this.automataName + " / " + type; var func = function func(payload) { var action = { payload: payload, type: actionType }; return action; }; var isInvocable = function isInvocable(state) { return _this.hasTransition(state.__sm_state || "", actionType); }; return Object.assign(func, { actionType: actionType, isInvocable: isInvocable }); }; _proto.getGraph = function getGraph() { if (this.graphCache) return this.graphCache; var arcs = this.options.reduce(function (a, b) { return a.concat(b.getArcs()); }, new Array()); this.graphCache = this.states.map(function (entry) { var actions = arcs.filter(function (_) { return _.sourceState === entry.stateName; }).map(function (_) { return { actionType: _.actionType, targetState: _.targetState, transitions: _.transitions }; }); actions = actions.filter(function (_, i) { return actions.findIndex(function (a) { return a.actionType === _.actionType; }) === i; }); return { actions: actions, entry: entry }; }); return this.graphCache; }; _proto.hasTransition = function hasTransition(stateName, actionType) { var graph = this.getGraph(); var node = graph.find(function (_) { return _.entry.stateName === stateName; }); return node !== undefined && node.actions.findIndex(function (_) { return _.actionType === actionType; }) > -1; }; _proto.mergeState = function mergeState(state) { var nextState = Object.assign({}, this.current, state); return nextState; }; return Automata; }(); function automataMiddleware(api) { return function (next) { return function (action) { if (!action.type.startsWith(ACTION_TYPE_PREFIX)) return next(action); var dispatching = true; Object.assign(action, { dispatch: function dispatch(a) { if (dispatching) setTimeout(function () { return api.dispatch(a); }, 0);else return api.dispatch(a); return a; } }); var deferred = next(action); dispatching = false; return deferred; }; }; } function automataReducer(automata) { if (automata.initial.__sm_state === undefined) throw new Error("No initial state specified. Use BeginWith() method to specify initial state."); automata.current = automata.initial; var nodes = automata.getGraph(); var currentNode = nodes.find(function (_) { return _.entry.stateName === automata.current.__sm_state; }); if (!currentNode) throw new Error("Can't find initial state."); return function (state, action) { if (state === void 0) { state = automata.initial; } if (typeof action.type !== "string" || !action.type.startsWith(ACTION_TYPE_PREFIX)) return state; var node = nodes.find(function (_) { return _.entry.stateName === state.__sm_state; }); if (!node) return state; var stateAction = node.actions.find(function (_) { return _.actionType === action.type; }); if (!stateAction) return state; var newState = state; if (stateAction.targetState) { var nextNode = nodes.find(function (_) { return _.entry.stateName === stateAction.targetState; }); if (!nextNode) throw new Error("Can't find state " + stateAction.targetState); automata.current = state; newState = nextNode.entry(state, action.payload); newState.__sm_state = nextNode.entry.stateName; automata.current = newState; } if (!action.dispatch) { throw new Error("Dispatch is not defined to perform transitions. It seems `automataMiddleware` was not applied."); } var localStore = Object.assign(action.dispatch, { dispatch: action.dispatch, getState: function getState() { return automata.current; } }); stateAction.transitions.forEach(function (transition) { return transition(localStore, action.payload); }); return newState; }; } var TaskAutomata = /*#__PURE__*/function (_Automata) { _inheritsLoose__default['default'](TaskAutomata, _Automata); function TaskAutomata(Name, Process, OnSuccess, OnFailure) { var _this; _this = _Automata.call(this, Name + " Automata") || this; _this.Name = Name; _this.Process = Process; _this.OnSuccess = OnSuccess; _this.OnFailure = OnFailure; _this.Idle = _this.state("Idle", function () { return _this.getDefaultState(); }); _this.Processing = _this.state("Processing", function () { return _this.mergeState({ error: null, isProcessing: true }); }); _this.Completed = _this.state("Completed", function (state, result) { return _this.mergeState({ error: null, isProcessing: false, result: result }); }); _this.Failure = _this.state("Failure", function (state, error) { return _this.mergeState({ error: error, isProcessing: false }); }); _this.Start = _this.action(_this.Name + " Start"); _this.Restart = _this.action(_this.Name + " Restart"); _this.Cancel = _this.action(_this.Name + " Cancel"); _this.End = _this.action(_this.Name + " Success"); _this.Fail = _this.action(_this.Name + " Fail"); _this.BeginProcessing = function (dispatch, input) { return _this.Process(input).then(function (_) { var result = dispatch(_this.End(_)); if (_this.OnSuccess) _this.OnSuccess(dispatch, _, input); return result; }).catch(function (_) { var result = dispatch(_this.Fail(_)); if (_this.OnFailure) _this.OnFailure(dispatch, _, input); return result; }); }; return _this; } var _proto = TaskAutomata.prototype; _proto.setupProcessIn = function setupProcessIn(state) { var Idle = this.Idle, Processing = this.Processing, Completed = this.Completed, Failure = this.Failure, Start = this.Start, Restart = this.Restart, End = this.End, Fail = this.Fail, Cancel = this.Cancel, BeginProcessing = this.BeginProcessing; this.in(state).on(Start).execute(BeginProcessing).goTo(Processing).in(Processing).on(End).goTo(Completed).on(Fail).goTo(Failure).on(Cancel).goTo(Idle).in(Failure).on(Cancel).goTo(Idle).in(Completed).or(Failure).on(Restart).execute(BeginProcessing).goTo(Processing); }; _proto.getDefaultState = function getDefaultState() { return { isProcessing: false }; }; return TaskAutomata; }(Automata); function createTaskAutomation(dataName, processTask, onSuccess, onFailure) { var automata = new TaskAutomata(dataName, processTask, onSuccess, onFailure); automata.setupProcessIn(automata.Idle); automata.beginWith(automata.Idle); var reducer = automataReducer(automata); return { cancel: automata.Cancel, reducer: reducer, restart: automata.Restart, start: automata.Start }; } exports.ACTION_TYPE_PREFIX = ACTION_TYPE_PREFIX; exports.Automata = Automata; exports.TaskAutomata = TaskAutomata; exports.automataMiddleware = automataMiddleware; exports.automataReducer = automataReducer; exports.createTaskAutomation = createTaskAutomation;