"use strict"; var __assign = (this && this.__assign) || function () { __assign = Object.assign || function(t) { for (var s, i = 1, n = arguments.length; i < n; i++) { s = arguments[i]; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; } return t; }; return __assign.apply(this, arguments); }; var __rest = (this && this.__rest) || function (s, e) { var t = {}; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) t[p] = s[p]; if (s != null && typeof Object.getOwnPropertySymbols === "function") for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) if (e.indexOf(p[i]) < 0) t[p[i]] = s[p[i]]; return t; }; var __read = (this && this.__read) || function (o, n) { var m = typeof Symbol === "function" && o[Symbol.iterator]; if (!m) return o; var i = m.call(o), r, ar = [], e; try { while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value); } catch (error) { e = { error: error }; } finally { try { if (r && !r.done && (m = i["return"])) m.call(i); } finally { if (e) throw e.error; } } return ar; }; var __spread = (this && this.__spread) || function () { for (var ar = [], i = 0; i < arguments.length; i++) ar = ar.concat(__read(arguments[i])); return ar; }; var __values = (this && this.__values) || function (o) { var m = typeof Symbol === "function" && o[Symbol.iterator], i = 0; if (m) return m.call(o); return { next: function () { if (o && i >= o.length) o = void 0; return { value: o && o[i++], done: !o }; } }; }; Object.defineProperty(exports, "__esModule", { value: true }); var utils_1 = require("./utils"); var types_1 = require("./types"); var utils_2 = require("./utils"); var State_1 = require("./State"); var actionTypes = require("./actionTypes"); var actions_1 = require("./actions"); var StateTree_1 = require("./StateTree"); var environment_1 = require("./environment"); var constants_1 = require("./constants"); var stateUtils_1 = require("./stateUtils"); var STATE_DELIMITER = '.'; var NULL_EVENT = ''; var STATE_IDENTIFIER = '#'; var TARGETLESS_KEY = ''; var EMPTY_OBJECT = {}; var isStateId = function (str) { return str[0] === STATE_IDENTIFIER; }; var createDefaultOptions = function () { return ({ actions: {}, guards: {}, services: {}, activities: {}, delays: {}, updater: utils_1.updateContext }); }; var StateNode = /** @class */ (function () { function StateNode(_config, options, /** * The initial extended state */ context) { var _this = this; this.context = context; this.__xstatenode = true; this.__cache = { events: undefined, relativeValue: new Map(), initialStateValue: undefined, initialState: undefined, transitions: undefined }; this.idMap = {}; var parent = _config.parent, config = __rest(_config, ["parent"]); this.config = config; this.parent = parent; this.options = __assign({}, createDefaultOptions(), options); this.key = _config.key || _config.id || '(machine)'; this.machine = this.parent ? this.parent.machine : this; this.path = this.parent ? this.parent.path.concat(this.key) : []; this.delimiter = _config.delimiter || (this.parent ? this.parent.delimiter : STATE_DELIMITER); this.id = _config.id || (this.machine ? __spread([this.machine.key], this.path).join(this.delimiter) : this.key); this.version = this.parent ? this.parent.version : _config.version; this.type = _config.type || (_config.parallel ? 'parallel' : _config.states && utils_1.keys(_config.states).length ? 'compound' : _config.history ? 'history' : 'atomic'); if (!environment_1.IS_PRODUCTION) { utils_1.warn(!('parallel' in _config), "The \"parallel\" property is deprecated and will be removed in version 4.1. " + (_config.parallel ? "Replace with `type: 'parallel'`" : "Use `type: '" + this.type + "'`") + " in the config for state node '" + this.id + "' instead."); } this.initial = _config.initial; this.order = _config.order || -1; this.states = (_config.states ? utils_1.mapValues(_config.states, function (stateConfig, key, _, i) { var _a; var stateNode = new StateNode(__assign({}, stateConfig, { key: key, order: stateConfig.order === undefined ? i : stateConfig.order, parent: _this })); Object.assign(_this.idMap, __assign((_a = {}, _a[stateNode.id] = stateNode, _a), stateNode.idMap)); return stateNode; }) : EMPTY_OBJECT); // History config this.history = _config.history === true ? 'shallow' : _config.history || false; this._transient = !!(_config.on && _config.on[NULL_EVENT]); this.strict = !!_config.strict; // TODO: deprecate (entry) this.onEntry = utils_1.toArray(_config.entry || _config.onEntry).map(function (action) { return actions_1.toActionObject(action); }); // TODO: deprecate (exit) this.onExit = utils_1.toArray(_config.exit || _config.onExit).map(function (action) { return actions_1.toActionObject(action); }); this.meta = _config.meta; this.data = this.type === 'final' ? _config.data : undefined; this.invoke = utils_1.toArray(_config.invoke).map(function (invokeConfig, i) { var _a, _b; if (utils_1.isMachine(invokeConfig)) { (_this.parent || _this).options.services = __assign((_a = {}, _a[invokeConfig.id] = invokeConfig, _a), (_this.parent || _this).options.services); return { type: actionTypes.invoke, src: invokeConfig.id, id: invokeConfig.id }; } else if (typeof invokeConfig.src !== 'string') { var invokeSrc = _this.id + ":invocation[" + i + "]"; // TODO: util function _this.machine.options.services = __assign((_b = {}, _b[invokeSrc] = invokeConfig.src, _b), _this.machine.options.services); return __assign({ type: actionTypes.invoke, id: invokeSrc }, invokeConfig, { src: invokeSrc }); } else { return __assign({}, invokeConfig, { type: actionTypes.invoke, id: invokeConfig.id || invokeConfig.src, src: invokeConfig.src }); } }); this.activities = utils_1.toArray(_config.activities) .concat(this.invoke) .map(function (activity) { return actions_1.toActivityDefinition(activity); }); this.after = this.getDelayedTransitions(); } /** * Clones this state machine with custom options and context. * * @param options Options (actions, guards, activities, services) to recursively merge with the existing options. * @param context Custom context (will override predefined context) */ StateNode.prototype.withConfig = function (options, context) { if (context === void 0) { context = this.context; } var _a = this.options, actions = _a.actions, activities = _a.activities, guards = _a.guards, services = _a.services, delays = _a.delays; return new StateNode(this.config, { actions: __assign({}, actions, options.actions), activities: __assign({}, activities, options.activities), guards: __assign({}, guards, options.guards), services: __assign({}, services, options.services), delays: __assign({}, delays, options.delays) }, context); }; /** * Clones this state machine with custom context. * * @param context Custom context (will override predefined context, not recursive) */ StateNode.prototype.withContext = function (context) { return new StateNode(this.config, this.options, context); }; Object.defineProperty(StateNode.prototype, "definition", { /** * The well-structured state node definition. */ get: function () { return { id: this.id, key: this.key, version: this.version, type: this.type, initial: this.initial, history: this.history, states: utils_1.mapValues(this.states, function (state) { return state.definition; }), on: this.on, onEntry: this.onEntry, onExit: this.onExit, activities: this.activities || [], meta: this.meta, order: this.order || -1, data: this.data, invoke: this.invoke }; }, enumerable: true, configurable: true }); StateNode.prototype.toJSON = function () { return this.definition; }; Object.defineProperty(StateNode.prototype, "on", { /** * The mapping of events to transitions. */ get: function () { return (this.__cache.transitions || ((this.__cache.transitions = this.formatTransitions()), this.__cache.transitions)); }, enumerable: true, configurable: true }); Object.defineProperty(StateNode.prototype, "transitions", { /** * All the transitions that can be taken from this state node. */ get: function () { var _this = this; return utils_1.flatten(utils_1.keys(this.on).map(function (event) { return _this.on[event]; })); }, enumerable: true, configurable: true }); /** * All delayed transitions from the config. */ StateNode.prototype.getDelayedTransitions = function () { var _this = this; if (this.after) { return this.after; } var afterConfig = this.config.after; var guards = this.machine.options.guards; if (!afterConfig) { return []; } if (utils_1.isArray(afterConfig)) { return afterConfig.map(function (delayedTransition, i) { var delay = delayedTransition.delay, target = delayedTransition.target; var delayRef; if (utils_1.isFunction(delay)) { delayRef = _this.id + ":delay[" + i + "]"; _this.options.delays[delayRef] = delay; // TODO: util function } else { delayRef = delay; } var event = actions_1.after(delayRef, _this.id); _this.onEntry.push(actions_1.send(event, { delay: delay })); _this.onExit.push(actions_1.cancel(event)); return __assign({ event: event }, delayedTransition, { source: _this, target: target === undefined ? undefined : utils_1.toArray(target), cond: utils_1.toGuard(delayedTransition.cond, guards), actions: utils_1.toArray(delayedTransition.actions).map(function (action) { return actions_1.toActionObject(action); }) }); }); } var allDelayedTransitions = utils_1.flatten(utils_1.keys(afterConfig).map(function (delayKey) { var delayedTransition = afterConfig[delayKey]; var delay = isNaN(+delayKey) ? delayKey : +delayKey; var event = actions_1.after(delay, _this.id); _this.onEntry.push(actions_1.send(event, { delay: delay })); _this.onExit.push(actions_1.cancel(event)); if (utils_1.isString(delayedTransition)) { return [ { source: _this, target: [delayedTransition], delay: delay, event: event, actions: [] } ]; } var delayedTransitions = utils_1.toArray(delayedTransition); return delayedTransitions.map(function (transition) { return (__assign({ event: event, delay: delay }, transition, { source: _this, target: transition.target === undefined ? transition.target : utils_1.toArray(transition.target), cond: utils_1.toGuard(transition.cond, guards), actions: utils_1.toArray(transition.actions).map(function (action) { return actions_1.toActionObject(action); }) })); }); })); allDelayedTransitions.sort(function (a, b) { return utils_1.isString(a) || utils_1.isString(b) ? 0 : +a.delay - +b.delay; }); return allDelayedTransitions; }; /** * Returns the state nodes represented by the current state value. * * @param state The state value or State instance */ StateNode.prototype.getStateNodes = function (state) { var _this = this; var _a; if (!state) { return []; } var stateValue = state instanceof State_1.State ? state.value : utils_1.toStateValue(state, this.delimiter); if (utils_1.isString(stateValue)) { var initialStateValue = this.getStateNode(stateValue).initial; return initialStateValue !== undefined ? this.getStateNodes((_a = {}, _a[stateValue] = initialStateValue, _a)) : [this.states[stateValue]]; } var subStateKeys = utils_1.keys(stateValue); var subStateNodes = subStateKeys.map(function (subStateKey) { return _this.getStateNode(subStateKey); }); return subStateNodes.concat(subStateKeys.reduce(function (allSubStateNodes, subStateKey) { var subStateNode = _this.getStateNode(subStateKey).getStateNodes(stateValue[subStateKey]); return allSubStateNodes.concat(subStateNode); }, [])); }; /** * Returns `true` if this state node explicitly handles the given event. * * @param event The event in question */ StateNode.prototype.handles = function (event) { var eventType = utils_1.getEventType(event); return this.events.indexOf(eventType) !== -1; }; /** * Resolves the given `state` to a new `State` instance relative to this machine. * * This ensures that `.events` and `.nextEvents` represent the correct values. * * @param state The state to resolve */ StateNode.prototype.resolveState = function (state) { return new State_1.State(__assign({}, state, { value: this.resolve(state.value), tree: this.getStateTree(state.value) })); }; StateNode.prototype.transitionLeafNode = function (stateValue, state, eventObject) { var stateNode = this.getStateNode(stateValue); var next = stateNode.next(state, eventObject); if (!next.tree) { var _a = this.next(state, eventObject), actions = _a.actions, tree = _a.tree, transitions = _a.transitions, configuration = _a.configuration; return { tree: tree, transitions: transitions, configuration: configuration, source: state, actions: actions }; } return next; }; StateNode.prototype.transitionCompoundNode = function (stateValue, state, eventObject) { var subStateKeys = utils_1.keys(stateValue); var stateNode = this.getStateNode(subStateKeys[0]); var next = stateNode._transition(stateValue[subStateKeys[0]], state, eventObject); if (!next.tree) { var _a = this.next(state, eventObject), actions = _a.actions, tree = _a.tree, transitions = _a.transitions, configuration = _a.configuration; return { tree: tree, transitions: transitions, configuration: configuration, source: state, actions: actions }; } return next; }; StateNode.prototype.transitionParallelNode = function (stateValue, state, eventObject) { var e_1, _a; var noTransitionKeys = []; var transitionMap = {}; try { for (var _b = __values(utils_1.keys(stateValue)), _c = _b.next(); !_c.done; _c = _b.next()) { var subStateKey = _c.value; var subStateValue = stateValue[subStateKey]; if (!subStateValue) { continue; } var subStateNode = this.getStateNode(subStateKey); var next = subStateNode._transition(subStateValue, state, eventObject); if (!next.tree) { noTransitionKeys.push(subStateKey); } transitionMap[subStateKey] = next; } } catch (e_1_1) { e_1 = { error: e_1_1 }; } finally { try { if (_c && !_c.done && (_a = _b.return)) _a.call(_b); } finally { if (e_1) throw e_1.error; } } var stateTransitions = utils_1.keys(transitionMap).map(function (key) { return transitionMap[key]; }); var enabledTransitions = utils_1.flatten(stateTransitions.map(function (st) { return st.transitions; })); var willTransition = stateTransitions.some(function (transition) { return transition.tree !== undefined; }); if (!willTransition) { var _d = this.next(state, eventObject), actions = _d.actions, tree = _d.tree, transitions = _d.transitions, _configuration = _d.configuration; return { tree: tree, transitions: transitions, configuration: _configuration, source: state, actions: actions }; } var targetNodes = utils_1.flatten(stateTransitions.map(function (st) { return st.configuration; })); var prevNodes = this.getStateNodes(stateValue); // console.log(targetNodes.map(t => t.id)); // console.log([...getConfiguration(prevNodes, targetNodes)].map(c => c.id)); var stateValueFromConfiguration = stateUtils_1.getValue(this.machine, stateUtils_1.getConfiguration(prevNodes, targetNodes)); // console.log(sv); var combinedTree = new StateTree_1.StateTree(this.machine, stateValueFromConfiguration); // const allTrees = keys(transitionMap) // .map(key => transitionMap[key].tree) // .filter(t => t !== undefined) as StateTree[]; // const combinedTree = allTrees.reduce((acc, t) => { // return acc.combine(t); // }); var allPaths = combinedTree.paths; var configuration = utils_1.flatten(utils_1.keys(transitionMap).map(function (key) { return transitionMap[key].configuration; })); // External transition that escapes orthogonal region if (allPaths.length === 1 && !utils_2.matchesState(utils_1.toStateValue(this.path, this.delimiter), combinedTree.value)) { return { tree: combinedTree, transitions: enabledTransitions, configuration: configuration, source: state, actions: utils_1.flatten(utils_1.keys(transitionMap).map(function (key) { return transitionMap[key].actions; })) }; } // const allResolvedTrees = keys(transitionMap).map(key => { // const { tree } = transitionMap[key]; // if (tree) { // return tree; // } // const subValue = path(this.path)(state.value)[key]; // return new StateTree(this.getStateNode(key), subValue).absolute; // }); // const finalCombinedTree = allResolvedTrees.reduce((acc, t) => { // return acc.combine(t); // }); return { tree: combinedTree, transitions: enabledTransitions, configuration: configuration, source: state, actions: utils_1.flatten(utils_1.keys(transitionMap).map(function (key) { return transitionMap[key].actions; })) }; }; StateNode.prototype._transition = function (stateValue, state, event) { // leaf node if (utils_1.isString(stateValue)) { return this.transitionLeafNode(stateValue, state, event); } // hierarchical node if (utils_1.keys(stateValue).length === 1) { return this.transitionCompoundNode(stateValue, state, event); } // orthogonal node return this.transitionParallelNode(stateValue, state, event); }; StateNode.prototype.next = function (state, eventObject) { var _this = this; var e_2, _a; var eventType = eventObject.type; var candidates = this.on[eventType]; if (!candidates || !candidates.length) { return { tree: undefined, transitions: [], configuration: [], source: state, actions: [] }; } var actions = this._transient ? [{ type: actionTypes.nullEvent }] : []; var nextStateStrings = []; var selectedTransition; try { for (var candidates_1 = __values(candidates), candidates_1_1 = candidates_1.next(); !candidates_1_1.done; candidates_1_1 = candidates_1.next()) { var candidate = candidates_1_1.value; var cond = candidate.cond, stateIn = candidate.in; var resolvedContext = state.context; var isInState = stateIn ? utils_1.isString(stateIn) && isStateId(stateIn) ? // Check if in state by ID state.matches(utils_1.toStateValue(this.getStateNodeById(stateIn).path, this.delimiter)) : // Check if in state by relative grandparent utils_2.matchesState(utils_1.toStateValue(stateIn, this.delimiter), utils_1.path(this.path.slice(0, -2))(state.value)) : true; var guardPassed = false; try { guardPassed = !cond || this.evaluateGuard(cond, resolvedContext, eventObject, state); } catch (err) { throw new Error("Unable to evaluate guard '" + (cond.name || cond .type) + "' in transition for event '" + eventType + "' in state node '" + this.id + "':\n" + err.message); } if (guardPassed && isInState) { if (candidate.target !== undefined) { nextStateStrings = candidate.target; } actions.push.apply(actions, __spread(utils_1.toArray(candidate.actions))); selectedTransition = candidate; break; } } } catch (e_2_1) { e_2 = { error: e_2_1 }; } finally { try { if (candidates_1_1 && !candidates_1_1.done && (_a = candidates_1.return)) _a.call(candidates_1); } finally { if (e_2) throw e_2.error; } } if (!nextStateStrings.length) { return { tree: selectedTransition && state.value // targetless transition ? new StateTree_1.StateTree(this, utils_1.path(this.path)(state.value)).absolute : undefined, transitions: [selectedTransition], configuration: selectedTransition && state.value ? [this] : [], source: state, actions: actions }; } var nextStateNodes = utils_1.flatten(nextStateStrings.map(function (str) { if (str instanceof StateNode) { return str; // TODO: fix anys } return _this.getRelativeStateNodes(str, state.historyValue); })); var isInternal = !!selectedTransition.internal; var reentryNodes = isInternal ? [] : utils_1.flatten(nextStateNodes.map(function (n) { return _this.nodesFromChild(n); })); var trees = nextStateNodes.map(function (stateNode) { return stateNode.tree; }); var combinedTree = trees.reduce(function (acc, t) { return acc.combine(t); }); reentryNodes.forEach(function (reentryNode) { return combinedTree.addReentryNode(reentryNode); }); return { tree: combinedTree, transitions: [selectedTransition], configuration: nextStateNodes, source: state, actions: actions }; }; Object.defineProperty(StateNode.prototype, "tree", { /** * The state tree represented by this state node. */ get: function () { var stateValue = utils_1.toStateValue(this.path, this.delimiter); return new StateTree_1.StateTree(this.machine, stateValue); }, enumerable: true, configurable: true }); StateNode.prototype.nodesFromChild = function (childStateNode) { if (childStateNode.escapes(this)) { return []; } var nodes = []; var marker = childStateNode; while (marker && marker !== this) { nodes.push(marker); marker = marker.parent; } nodes.push(this); // inclusive return nodes; }; StateNode.prototype.getStateTree = function (stateValue) { return new StateTree_1.StateTree(this, stateValue); }; /** * Whether the given state node "escapes" this state node. If the `stateNode` is equal to or the parent of * this state node, it does not escape. */ StateNode.prototype.escapes = function (stateNode) { if (this === stateNode) { return false; } var parent = this.parent; while (parent) { if (parent === stateNode) { return false; } parent = parent.parent; } return true; }; StateNode.prototype.evaluateGuard = function (guard, context, eventObject, state) { var condFn; var guards = this.machine.options.guards; var guardMeta = { state: state, cond: guard }; // TODO: do not hardcode! if (guard.type === constants_1.DEFAULT_GUARD_TYPE) { return guard.predicate(context, eventObject, guardMeta); } if (!guards[guard.type]) { throw new Error("Guard '" + guard.type + "' is not implemented on machine '" + this.machine.id + "'."); } condFn = guards[guard.type]; return condFn(context, eventObject, guardMeta); }; StateNode.prototype.getActions = function (transition, prevState) { var entryExitStates = transition.tree ? transition.tree.resolved.getEntryExitStates(prevState ? this.getStateTree(prevState.value) : undefined) : { entry: [], exit: [] }; var doneEvents = transition.tree ? transition.tree.getDoneEvents(new Set(entryExitStates.entry)) : []; if (!transition.source) { entryExitStates.exit = []; // Ensure that root StateNode (machine) is entered entryExitStates.entry.unshift(this); } var entryStates = new Set(entryExitStates.entry); var exitStates = new Set(entryExitStates.exit); var _a = __read([ utils_1.flatten(Array.from(entryStates).map(function (stateNode) { return __spread(stateNode.activities.map(function (activity) { return actions_1.start(activity); }), stateNode.onEntry); })).concat(doneEvents.map(actions_1.raise)), utils_1.flatten(Array.from(exitStates).map(function (stateNode) { return __spread(stateNode.onExit, stateNode.activities.map(function (activity) { return actions_1.stop(activity); })); })) ], 2), entryActions = _a[0], exitActions = _a[1]; var actions = actions_1.toActionObjects(exitActions.concat(transition.actions).concat(entryActions), this.machine.options.actions); return actions; }; /** * Determines the next state given the current `state` and sent `event`. * * @param state The current State instance or state value * @param event The event that was sent at the current state * @param context The current context (extended state) of the current state */ StateNode.prototype.transition = function (state, event, context) { var currentState; if (state instanceof State_1.State) { currentState = context === undefined ? state : this.resolveState(State_1.State.from(state, context)); } else { var resolvedStateValue = utils_1.isString(state) ? this.resolve(utils_1.pathToStateValue(this.getResolvedPath(state))) : this.resolve(state); var resolvedContext = context ? context : this.machine.context; currentState = this.resolveState(State_1.State.from(resolvedStateValue, resolvedContext)); } var eventObject = actions_1.toEventObject(event); var eventType = eventObject.type; if (this.strict) { if (this.events.indexOf(eventType) === -1 && !utils_1.isBuiltInEvent(eventType)) { throw new Error("Machine '" + this.id + "' does not accept event '" + eventType + "'"); } } var stateTransition = this._transition(currentState.value, currentState, eventObject); if (stateTransition.tree) { stateTransition.tree = stateTransition.tree.resolved; } // const prevConfig = this.machine.getStateNodes(currentState.value); // const cv = getValue( // this.machine, // getConfiguration(prevConfig, stateTransition.configuration) // ); // if (stateTransition.tree) { // const eq = stateValuesEqual(cv, stateTransition.tree.value); // console.log(eq); // } // if (!eq) { // console.log('prevConfig', prevConfig.map(c => c.id)); // console.log('config', [...stateTransition.configuration].map(c => c.id)); // console.log(cv, stateTransition.tree!.value); // } return this.resolveTransition(stateTransition, currentState, eventObject); }; StateNode.prototype.resolveTransition = function (stateTransition, currentState, _eventObject) { var _this = this; var e_3, _a, _b; var resolvedStateValue = stateTransition.tree ? stateTransition.tree.value : undefined; var historyValue = currentState ? currentState.historyValue ? currentState.historyValue : stateTransition.source ? this.machine.historyValue(currentState.value) : undefined : undefined; var currentContext = currentState ? currentState.context : stateTransition.context || this.machine.context; var eventObject = _eventObject || { type: types_1.ActionTypes.Init }; if (!environment_1.IS_PRODUCTION && stateTransition.tree) { try { this.ensureValidPaths(stateTransition.tree.paths); // TODO: ensure code coverage for this } catch (e) { throw new Error("Event '" + (eventObject ? eventObject.type : 'none') + "' leads to an invalid configuration: " + e.message); } } var actions = this.getActions(stateTransition, currentState); var activities = currentState ? __assign({}, currentState.activities) : {}; try { for (var actions_2 = __values(actions), actions_2_1 = actions_2.next(); !actions_2_1.done; actions_2_1 = actions_2.next()) { var action = actions_2_1.value; if (action.type === actionTypes.start) { activities[action.activity.type] = action; } else if (action.type === actionTypes.stop) { activities[action.activity.type] = false; } } } catch (e_3_1) { e_3 = { error: e_3_1 }; } finally { try { if (actions_2_1 && !actions_2_1.done && (_a = actions_2.return)) _a.call(actions_2); } finally { if (e_3) throw e_3.error; } } var _c = __read(utils_1.partition(actions, function (action) { return action.type === actionTypes.raise || action.type === actionTypes.nullEvent; }), 2), raisedEvents = _c[0], otherActions = _c[1]; var _d = __read(utils_1.partition(otherActions, function (action) { return action.type === actionTypes.assign; }), 2), assignActions = _d[0], nonEventActions = _d[1]; var updatedContext = assignActions.length ? this.options.updater(currentContext, eventObject, assignActions) : currentContext; var resolvedActions = utils_1.flatten(nonEventActions.map(function (actionObject) { if (actionObject.type === actionTypes.send) { var sendAction = actions_1.resolveSend(actionObject, updatedContext, eventObject || { type: types_1.ActionTypes.Init }); // TODO: fix ActionTypes.Init if (utils_1.isString(sendAction.delay)) { if (!_this.machine.options.delays || _this.machine.options.delays[sendAction.delay] === undefined) { if (!environment_1.IS_PRODUCTION) { utils_1.warn(false, // tslint:disable-next-line:max-line-length "No delay reference for delay expression '" + sendAction.delay + "' was found on machine '" + _this.machine.id + "'"); } // Do not send anything return sendAction; } var delayExpr = _this.machine.options.delays[sendAction.delay]; sendAction.delay = typeof delayExpr === 'number' ? delayExpr : delayExpr(updatedContext, eventObject || { type: types_1.ActionTypes.Init }); } return sendAction; } if (actionObject.type === types_1.ActionTypes.Pure) { return (actionObject.get(updatedContext, eventObject) || []); } return actions_1.toActionObject(actionObject, _this.options.actions); })); var stateNodes = resolvedStateValue ? this.getStateNodes(resolvedStateValue) : []; var isTransient = stateNodes.some(function (stateNode) { return stateNode._transient; }); if (isTransient) { raisedEvents.push({ type: actionTypes.nullEvent }); } var meta = __spread([this], stateNodes).reduce(function (acc, stateNode) { if (stateNode.meta !== undefined) { acc[stateNode.id] = stateNode.meta; } return acc; }, {}); var nextState = new State_1.State({ value: resolvedStateValue || currentState.value, context: updatedContext, event: eventObject || actions_1.initEvent, historyValue: resolvedStateValue ? historyValue ? utils_1.updateHistoryValue(historyValue, resolvedStateValue) : undefined : currentState ? currentState.historyValue : undefined, history: !resolvedStateValue || stateTransition.source ? currentState : undefined, actions: resolvedStateValue ? resolvedActions : [], activities: resolvedStateValue ? activities : currentState ? currentState.activities : {}, meta: resolvedStateValue ? meta : currentState ? currentState.meta : undefined, events: resolvedStateValue ? raisedEvents : [], tree: resolvedStateValue ? stateTransition.tree : currentState ? currentState.tree : undefined }); nextState.changed = eventObject.type === actionTypes.update || !!assignActions.length; // Dispose of penultimate histories to prevent memory leaks var history = nextState.history; if (history) { delete history.history; } if (!resolvedStateValue) { return nextState; } var maybeNextState = nextState; while (raisedEvents.length) { var currentActions = maybeNextState.actions; var raisedEvent = raisedEvents.shift(); maybeNextState = this.transition(maybeNextState, raisedEvent.type === actionTypes.nullEvent ? NULL_EVENT : raisedEvent.event, maybeNextState.context); // Save original event to state maybeNextState.event = eventObject; (_b = maybeNextState.actions).unshift.apply(_b, __spread(currentActions)); } // Detect if state changed var changed = maybeNextState.changed || (history ? !!maybeNextState.actions.length || !!assignActions.length || typeof history.value !== typeof maybeNextState.value || !State_1.stateValuesEqual(maybeNextState.value, history.value) : undefined); maybeNextState.changed = changed; // Preserve original history after raised events maybeNextState.historyValue = nextState.historyValue; maybeNextState.history = history; return maybeNextState; }; StateNode.prototype.ensureValidPaths = function (paths) { var _this = this; var e_4, _a; if (!environment_1.IS_PRODUCTION) { var visitedParents = new Map(); var stateNodes = utils_1.flatten(paths.map(function (_path) { return _this.getRelativeStateNodes(_path); })); try { outer: for (var stateNodes_1 = __values(stateNodes), stateNodes_1_1 = stateNodes_1.next(); !stateNodes_1_1.done; stateNodes_1_1 = stateNodes_1.next()) { var stateNode = stateNodes_1_1.value; var marker = stateNode; while (marker.parent) { if (visitedParents.has(marker.parent)) { if (marker.parent.type === 'parallel') { continue outer; } throw new Error("State node '" + stateNode.id + "' shares parent '" + marker.parent.id + "' with state node '" + visitedParents .get(marker.parent) .map(function (a) { return a.id; }) + "'"); } if (!visitedParents.get(marker.parent)) { visitedParents.set(marker.parent, [stateNode]); } else { visitedParents.get(marker.parent).push(stateNode); } marker = marker.parent; } } } catch (e_4_1) { e_4 = { error: e_4_1 }; } finally { try { if (stateNodes_1_1 && !stateNodes_1_1.done && (_a = stateNodes_1.return)) _a.call(stateNodes_1); } finally { if (e_4) throw e_4.error; } } } else { return; } }; /** * Returns the child state node from its relative `stateKey`, or throws. */ StateNode.prototype.getStateNode = function (stateKey) { if (isStateId(stateKey)) { return this.machine.getStateNodeById(stateKey); } if (!this.states) { throw new Error("Unable to retrieve child state '" + stateKey + "' from '" + this.id + "'; no child states exist."); } var result = this.states[stateKey]; if (!result) { throw new Error("Child state '" + stateKey + "' does not exist on '" + this.id + "'"); } return result; }; /** * Returns the state node with the given `stateId`, or throws. * * @param stateId The state ID. The prefix "#" is removed. */ StateNode.prototype.getStateNodeById = function (stateId) { var resolvedStateId = isStateId(stateId) ? stateId.slice(STATE_IDENTIFIER.length) : stateId; if (resolvedStateId === this.id) { return this; } var stateNode = this.machine.idMap[resolvedStateId]; if (!stateNode) { throw new Error("Child state node '#" + resolvedStateId + "' does not exist on machine '" + this.id + "'"); } return stateNode; }; /** * Returns the relative state node from the given `statePath`, or throws. * * @param statePath The string or string array relative path to the state node. */ StateNode.prototype.getStateNodeByPath = function (statePath) { if (typeof statePath === 'string' && isStateId(statePath)) { try { return this.getStateNodeById(statePath.slice(1)); } catch (e) { // try individual paths // throw e; } } var arrayStatePath = utils_1.toStatePath(statePath, this.delimiter).slice(); var currentStateNode = this; while (arrayStatePath.length) { var key = arrayStatePath.shift(); if (!key.length) { break; } currentStateNode = currentStateNode.getStateNode(key); } return currentStateNode; }; /** * Resolves a partial state value with its full representation in this machine. * * @param stateValue The partial state value to resolve. */ StateNode.prototype.resolve = function (stateValue) { var _this = this; var _a; if (!stateValue) { return this.initialStateValue || EMPTY_OBJECT; // TODO: type-specific properties } switch (this.type) { case 'parallel': return utils_1.mapValues(this.initialStateValue, function (subStateValue, subStateKey) { return subStateValue ? _this.getStateNode(subStateKey).resolve(stateValue[subStateKey] || subStateValue) : EMPTY_OBJECT; }); case 'compound': if (utils_1.isString(stateValue)) { var subStateNode = this.getStateNode(stateValue); if (subStateNode.type === 'parallel' || subStateNode.type === 'compound') { return _a = {}, _a[stateValue] = subStateNode.initialStateValue, _a; } return stateValue; } if (!utils_1.keys(stateValue).length) { return this.initialStateValue || {}; } return utils_1.mapValues(stateValue, function (subStateValue, subStateKey) { return subStateValue ? _this.getStateNode(subStateKey).resolve(subStateValue) : EMPTY_OBJECT; }); default: return stateValue || EMPTY_OBJECT; } }; Object.defineProperty(StateNode.prototype, "resolvedStateValue", { get: function () { var _a, _b; var key = this.key; if (this.type === 'parallel') { return _a = {}, _a[key] = utils_1.mapFilterValues(this.states, function (stateNode) { return stateNode.resolvedStateValue[stateNode.key]; }, function (stateNode) { return !(stateNode.type === 'history'); }), _a; } if (this.initial === undefined) { // If leaf node, value is just the state node's key return key; } if (!this.states[this.initial]) { throw new Error("Initial state '" + this.initial + "' not found on '" + key + "'"); } return _b = {}, _b[key] = this.states[this.initial].resolvedStateValue, _b; }, enumerable: true, configurable: true }); StateNode.prototype.getResolvedPath = function (stateIdentifier) { if (isStateId(stateIdentifier)) { var stateNode = this.machine.idMap[stateIdentifier.slice(STATE_IDENTIFIER.length)]; if (!stateNode) { throw new Error("Unable to find state node '" + stateIdentifier + "'"); } return stateNode.path; } return utils_1.toStatePath(stateIdentifier, this.delimiter); }; Object.defineProperty(StateNode.prototype, "initialStateValue", { get: function () { if (this.__cache.initialStateValue) { return this.__cache.initialStateValue; } var initialStateValue = (this.type === 'parallel' ? utils_1.mapFilterValues(this.states, function (state) { return state.initialStateValue || EMPTY_OBJECT; }, function (stateNode) { return !(stateNode.type === 'history'); }) : utils_1.isString(this.resolvedStateValue) ? undefined : this.resolvedStateValue[this.key]); this.__cache.initialStateValue = initialStateValue; return this.__cache.initialStateValue; }, enumerable: true, configurable: true }); StateNode.prototype.getInitialState = function (stateValue, context) { if (context === void 0) { context = this.machine.context; } var tree = this.getStateTree(stateValue); var configuration = this.getStateNodes(stateValue); configuration.forEach(function (stateNode) { tree.addReentryNode(stateNode); }); return this.resolveTransition({ tree: tree, configuration: configuration, transitions: [], source: undefined, actions: [], context: context }); }; Object.defineProperty(StateNode.prototype, "initialState", { /** * The initial State instance, which includes all actions to be executed from * entering the initial state. */ get: function () { if (this.__cache.initialState) { return this.__cache.initialState; } var initialStateValue = this.initialStateValue; if (!initialStateValue) { throw new Error("Cannot retrieve initial state from simple state '" + this.id + "'."); } this.__cache.initialState = this.getInitialState(initialStateValue); return this.__cache.initialState; }, enumerable: true, configurable: true }); Object.defineProperty(StateNode.prototype, "target", { /** * The target state value of the history state node, if it exists. This represents the * default state value to transition to if no history value exists yet. */ get: function () { var target; if (this.type === 'history') { var historyConfig = this.config; if (historyConfig.target && utils_1.isString(historyConfig.target)) { target = isStateId(historyConfig.target) ? utils_1.pathToStateValue(this.machine .getStateNodeById(historyConfig.target) .path.slice(this.path.length - 1)) : historyConfig.target; } else { target = historyConfig.target; } } return target; }, enumerable: true, configurable: true }); StateNode.prototype.getStates = function (stateValue) { var e_5, _a; if (utils_1.isString(stateValue)) { return [this.states[stateValue]]; } var stateNodes = []; try { for (var _b = __values(utils_1.keys(stateValue)), _c = _b.next(); !_c.done; _c = _b.next()) { var key = _c.value; stateNodes.push.apply(stateNodes, __spread(this.states[key].getStates(stateValue[key]))); } } catch (e_5_1) { e_5 = { error: e_5_1 }; } finally { try { if (_c && !_c.done && (_a = _b.return)) _a.call(_b); } finally { if (e_5) throw e_5.error; } } return stateNodes; }; /** * Returns the leaf nodes from a state path relative to this state node. * * @param relativeStateId The relative state path to retrieve the state nodes * @param history The previous state to retrieve history * @param resolve Whether state nodes should resolve to initial child state nodes */ StateNode.prototype.getRelativeStateNodes = function (relativeStateId, historyValue, resolve) { if (resolve === void 0) { resolve = true; } if (utils_1.isString(relativeStateId) && isStateId(relativeStateId)) { var unresolvedStateNode = this.getStateNodeById(relativeStateId); return resolve ? unresolvedStateNode.type === 'history' ? unresolvedStateNode.resolveHistory(historyValue) : unresolvedStateNode.initialStateNodes : [unresolvedStateNode]; } var statePath = utils_1.toStatePath(relativeStateId, this.delimiter); var rootStateNode = this.parent || this; var unresolvedStateNodes = rootStateNode.getFromRelativePath(statePath, historyValue); if (!resolve) { return unresolvedStateNodes; } return utils_1.flatten(unresolvedStateNodes.map(function (stateNode) { return stateNode.initialStateNodes; })); }; Object.defineProperty(StateNode.prototype, "initialStateNodes", { get: function () { var _this = this; if (this.type === 'atomic' || this.type === 'final') { return [this]; } // Case when state node is compound but no initial state is defined if (this.type === 'compound' && !this.initial) { if (!environment_1.IS_PRODUCTION) { utils_1.warn(false, "Compound state node '" + this.id + "' has no initial state."); } return [this]; } var initialStateNodePaths = utils_1.toStatePaths(this.initialStateValue); return utils_1.flatten(initialStateNodePaths.map(function (initialPath) { return _this.getFromRelativePath(initialPath); })); }, enumerable: true, configurable: true }); /** * Retrieves state nodes from a relative path to this state node. * * @param relativePath The relative path from this state node * @param historyValue */ StateNode.prototype.getFromRelativePath = function (relativePath, historyValue) { if (!relativePath.length) { return [this]; } var _a = __read(relativePath), stateKey = _a[0], childStatePath = _a.slice(1); if (!this.states) { throw new Error("Cannot retrieve subPath '" + stateKey + "' from node with no states"); } var childStateNode = this.getStateNode(stateKey); if (childStateNode.type === 'history') { return childStateNode.resolveHistory(historyValue); } if (!this.states[stateKey]) { throw new Error("Child state '" + stateKey + "' does not exist on '" + this.id + "'"); } return this.states[stateKey].getFromRelativePath(childStatePath, historyValue); }; StateNode.prototype.historyValue = function (relativeStateValue) { if (!utils_1.keys(this.states).length) { return undefined; } return { current: relativeStateValue || this.initialStateValue, states: utils_1.mapFilterValues(this.states, function (stateNode, key) { if (!relativeStateValue) { return stateNode.historyValue(); } var subStateValue = utils_1.isString(relativeStateValue) ? undefined : relativeStateValue[key]; return stateNode.historyValue(subStateValue || stateNode.initialStateValue); }, function (stateNode) { return !stateNode.history; }) }; }; /** * Resolves to the historical value(s) of the parent state node, * represented by state nodes. * * @param historyValue */ StateNode.prototype.resolveHistory = function (historyValue) { var _this = this; if (this.type !== 'history') { return [this]; } var parent = this.parent; if (!historyValue) { return this.target ? utils_1.flatten(utils_1.toStatePaths(this.target).map(function (relativeChildPath) { return parent.getFromRelativePath(relativeChildPath); })) : parent.initialStateNodes; } var subHistoryValue = utils_1.nestedPath(parent.path, 'states')(historyValue).current; if (utils_1.isString(subHistoryValue)) { return [parent.getStateNode(subHistoryValue)]; } return utils_1.flatten(utils_1.toStatePaths(subHistoryValue).map(function (subStatePath) { return _this.history === 'deep' ? parent.getFromRelativePath(subStatePath) : [parent.states[subStatePath[0]]]; })); }; Object.defineProperty(StateNode.prototype, "stateIds", { /** * All the state node IDs of this state node and its descendant state nodes. */ get: function () { var _this = this; var childStateIds = utils_1.flatten(utils_1.keys(this.states).map(function (stateKey) { return _this.states[stateKey].stateIds; })); return [this.id].concat(childStateIds); }, enumerable: true, configurable: true }); Object.defineProperty(StateNode.prototype, "events", { /** * All the event types accepted by this state node and its descendants. */ get: function () { var e_6, _a, e_7, _b; if (this.__cache.events) { return this.__cache.events; } var states = this.states; var events = new Set(this.ownEvents); if (states) { try { for (var _c = __values(utils_1.keys(states)), _d = _c.next(); !_d.done; _d = _c.next()) { var stateId = _d.value; var state = states[stateId]; if (state.states) { try { for (var _e = __values(state.events), _f = _e.next(); !_f.done; _f = _e.next()) { var event_1 = _f.value; events.add("" + event_1); } } catch (e_7_1) { e_7 = { error: e_7_1 }; } finally { try { if (_f && !_f.done && (_b = _e.return)) _b.call(_e); } finally { if (e_7) throw e_7.error; } } } } } catch (e_6_1) { e_6 = { error: e_6_1 }; } finally { try { if (_d && !_d.done && (_a = _c.return)) _a.call(_c); } finally { if (e_6) throw e_6.error; } } } return (this.__cache.events = Array.from(events)); }, enumerable: true, configurable: true }); Object.defineProperty(StateNode.prototype, "ownEvents", { /** * All the events that have transitions directly from this state node. * * Excludes any inert events. */ get: function () { var _this = this; var events = new Set(utils_1.keys(this.on).filter(function (key) { var transitions = _this.on[key]; return transitions.some(function (transition) { return !(!transition.target && !transition.actions.length && transition.internal); }); })); return Array.from(events); }, enumerable: true, configurable: true }); StateNode.prototype.formatTransition = function (target, transitionConfig, event) { var _this = this; var internal = transitionConfig ? transitionConfig.internal : undefined; var targets = utils_1.toArray(target); var guards = this.machine.options.guards; // Format targets to their full string path var formattedTargets = targets.map(function (_target) { if (!utils_1.isString(_target)) { return "#" + _target.id; } var isInternalTarget = _target[0] === _this.delimiter; internal = internal === undefined ? isInternalTarget : internal; // If internal target is defined on machine, // do not include machine key on target if (isInternalTarget && !_this.parent) { return "#" + _this.getStateNodeByPath(_target.slice(1)).id; } var resolvedTarget = isInternalTarget ? _this.key + _target : "" + _target; if (_this.parent) { try { var targetStateNode = _this.parent.getStateNodeByPath(resolvedTarget); return "#" + targetStateNode.id; } catch (err) { throw new Error("Invalid transition for state node '" + _this.id + "' on event '" + event + "':\n" + err.message); } } else { return "#" + _this.getStateNodeByPath(resolvedTarget).id; } }); if (transitionConfig === undefined) { return { target: target === undefined ? undefined : formattedTargets, source: this, actions: [], internal: target === undefined || internal, event: event }; } // Check if there is no target (targetless) // An undefined transition signals that the state node should not transition from that event. var isTargetless = target === undefined || target === TARGETLESS_KEY; return __assign({}, transitionConfig, { actions: actions_1.toActionObjects(utils_1.toArray(transitionConfig.actions)), cond: utils_1.toGuard(transitionConfig.cond, guards), target: isTargetless ? undefined : formattedTargets, source: this, internal: (isTargetless && internal === undefined) || internal, event: event }); }; StateNode.prototype.formatTransitions = function () { var _this = this; var _a, e_8, _b; var onConfig = this.config.on || EMPTY_OBJECT; var doneConfig = this.config.onDone ? (_a = {}, _a["" + actions_1.done(this.id)] = this.config.onDone, _a) : undefined; var invokeConfig = this.invoke.reduce(function (acc, invokeDef) { if (invokeDef.onDone) { acc[actions_1.doneInvoke(invokeDef.id)] = invokeDef.onDone; } if (invokeDef.onError) { acc[actions_1.error(invokeDef.id)] = invokeDef.onError; } return acc; }, {}); var delayedTransitions = this.after; var formattedTransitions = utils_1.mapValues(__assign({}, onConfig, doneConfig, invokeConfig), function (value, event) { var e_9, _a; if (value === undefined) { return [{ target: undefined, event: event, actions: [], internal: true }]; } if (utils_1.isArray(value)) { return value.map(function (targetTransitionConfig) { return _this.formatTransition(targetTransitionConfig.target, targetTransitionConfig, event); }); } if (utils_1.isString(value) || utils_1.isMachine(value)) { return [_this.formatTransition([value], undefined, event)]; } if (!environment_1.IS_PRODUCTION) { try { for (var _b = __values(utils_1.keys(value)), _c = _b.next(); !_c.done; _c = _b.next()) { var key = _c.value; if (['target', 'actions', 'internal', 'in', 'cond', 'event'].indexOf(key) === -1) { throw new Error( // tslint:disable-next-line:max-line-length "State object mapping of transitions is deprecated. Check the config for event '" + event + "' on state '" + _this.id + "'."); } } } catch (e_9_1) { e_9 = { error: e_9_1 }; } finally { try { if (_c && !_c.done && (_a = _b.return)) _a.call(_b); } finally { if (e_9) throw e_9.error; } } } return [ _this.formatTransition(value.target, value, event) ]; }); try { for (var delayedTransitions_1 = __values(delayedTransitions), delayedTransitions_1_1 = delayedTransitions_1.next(); !delayedTransitions_1_1.done; delayedTransitions_1_1 = delayedTransitions_1.next()) { var delayedTransition = delayedTransitions_1_1.value; formattedTransitions[delayedTransition.event] = formattedTransitions[delayedTransition.event] || []; formattedTransitions[delayedTransition.event].push(delayedTransition); } } catch (e_8_1) { e_8 = { error: e_8_1 }; } finally { try { if (delayedTransitions_1_1 && !delayedTransitions_1_1.done && (_b = delayedTransitions_1.return)) _b.call(delayedTransitions_1); } finally { if (e_8) throw e_8.error; } } return formattedTransitions; }; return StateNode; }()); exports.StateNode = StateNode; //# sourceMappingURL=StateNode.js.map