Files
30-seconds-of-code/node_modules/xstate/es/StateNode.js
2019-08-20 15:52:05 +02:00

1401 lines
61 KiB
JavaScript

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 };
}
};
};
import { getEventType, toStatePath, toStateValue, mapValues, path, toStatePaths, pathToStateValue, flatten, mapFilterValues, nestedPath, toArray, keys, isBuiltInEvent, partition, updateHistoryValue, updateContext, warn, isArray, isFunction, isString, toGuard, isMachine } from './utils';
import { ActionTypes } from './types';
import { matchesState } from './utils';
import { State, stateValuesEqual } from './State';
import * as actionTypes from './actionTypes';
import { start, stop, toEventObject, toActivityDefinition, send, cancel, after, raise, done, doneInvoke, error, toActionObject, resolveSend, initEvent, toActionObjects } from './actions';
import { StateTree } from './StateTree';
import { IS_PRODUCTION } from './environment';
import { DEFAULT_GUARD_TYPE } from './constants';
import { getValue, getConfiguration } from './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: updateContext
};
};
var StateNode = /** @class */ /*#__PURE__*/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 && keys(_config.states).length ? 'compound' : _config.history ? 'history' : 'atomic');
if (!IS_PRODUCTION) {
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 ? 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 = toArray(_config.entry || _config.onEntry).map(function (action) {
return toActionObject(action);
});
// TODO: deprecate (exit)
this.onExit = toArray(_config.exit || _config.onExit).map(function (action) {
return toActionObject(action);
});
this.meta = _config.meta;
this.data = this.type === 'final' ? _config.data : undefined;
this.invoke = toArray(_config.invoke).map(function (invokeConfig, i) {
var _a, _b;
if (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 = toArray(_config.activities).concat(this.invoke).map(function (activity) {
return 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: 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 flatten(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 (isArray(afterConfig)) {
return afterConfig.map(function (delayedTransition, i) {
var delay = delayedTransition.delay,
target = delayedTransition.target;
var delayRef;
if (isFunction(delay)) {
delayRef = _this.id + ":delay[" + i + "]";
_this.options.delays[delayRef] = delay; // TODO: util function
} else {
delayRef = delay;
}
var event = after(delayRef, _this.id);
_this.onEntry.push(send(event, { delay: delay }));
_this.onExit.push(cancel(event));
return __assign({ event: event }, delayedTransition, { source: _this, target: target === undefined ? undefined : toArray(target), cond: toGuard(delayedTransition.cond, guards), actions: toArray(delayedTransition.actions).map(function (action) {
return toActionObject(action);
}) });
});
}
var allDelayedTransitions = flatten(keys(afterConfig).map(function (delayKey) {
var delayedTransition = afterConfig[delayKey];
var delay = isNaN(+delayKey) ? delayKey : +delayKey;
var event = after(delay, _this.id);
_this.onEntry.push(send(event, { delay: delay }));
_this.onExit.push(cancel(event));
if (isString(delayedTransition)) {
return [{
source: _this,
target: [delayedTransition],
delay: delay,
event: event,
actions: []
}];
}
var delayedTransitions = toArray(delayedTransition);
return delayedTransitions.map(function (transition) {
return __assign({ event: event,
delay: delay }, transition, { source: _this, target: transition.target === undefined ? transition.target : toArray(transition.target), cond: toGuard(transition.cond, guards), actions: toArray(transition.actions).map(function (action) {
return toActionObject(action);
}) });
});
}));
allDelayedTransitions.sort(function (a, b) {
return isString(a) || 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 ? state.value : toStateValue(state, this.delimiter);
if (isString(stateValue)) {
var initialStateValue = this.getStateNode(stateValue).initial;
return initialStateValue !== undefined ? this.getStateNodes((_a = {}, _a[stateValue] = initialStateValue, _a)) : [this.states[stateValue]];
}
var subStateKeys = 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 = 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(__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 = 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(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 = keys(transitionMap).map(function (key) {
return transitionMap[key];
});
var enabledTransitions = 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 = 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 = getValue(this.machine, getConfiguration(prevNodes, targetNodes));
// console.log(sv);
var combinedTree = new 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 = flatten(keys(transitionMap).map(function (key) {
return transitionMap[key].configuration;
}));
// External transition that escapes orthogonal region
if (allPaths.length === 1 && !matchesState(toStateValue(this.path, this.delimiter), combinedTree.value)) {
return {
tree: combinedTree,
transitions: enabledTransitions,
configuration: configuration,
source: state,
actions: flatten(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: flatten(keys(transitionMap).map(function (key) {
return transitionMap[key].actions;
}))
};
};
StateNode.prototype._transition = function (stateValue, state, event) {
// leaf node
if (isString(stateValue)) {
return this.transitionLeafNode(stateValue, state, event);
}
// hierarchical node
if (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 ? isString(stateIn) && isStateId(stateIn) ? // Check if in state by ID
state.matches(toStateValue(this.getStateNodeById(stateIn).path, this.delimiter)) : // Check if in state by relative grandparent
matchesState(toStateValue(stateIn, this.delimiter), 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(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(this, path(this.path)(state.value)).absolute : undefined,
transitions: [selectedTransition],
configuration: selectedTransition && state.value ? [this] : [],
source: state,
actions: actions
};
}
var nextStateNodes = 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 ? [] : 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 = toStateValue(this.path, this.delimiter);
return new 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(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 === 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([flatten(Array.from(entryStates).map(function (stateNode) {
return __spread(stateNode.activities.map(function (activity) {
return start(activity);
}), stateNode.onEntry);
})).concat(doneEvents.map(raise)), flatten(Array.from(exitStates).map(function (stateNode) {
return __spread(stateNode.onExit, stateNode.activities.map(function (activity) {
return stop(activity);
}));
}))], 2),
entryActions = _a[0],
exitActions = _a[1];
var actions = 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) {
currentState = context === undefined ? state : this.resolveState(State.from(state, context));
} else {
var resolvedStateValue = isString(state) ? this.resolve(pathToStateValue(this.getResolvedPath(state))) : this.resolve(state);
var resolvedContext = context ? context : this.machine.context;
currentState = this.resolveState(State.from(resolvedStateValue, resolvedContext));
}
var eventObject = toEventObject(event);
var eventType = eventObject.type;
if (this.strict) {
if (this.events.indexOf(eventType) === -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: ActionTypes.Init };
if (!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_1 = __values(actions), actions_1_1 = actions_1.next(); !actions_1_1.done; actions_1_1 = actions_1.next()) {
var action = actions_1_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_1_1 && !actions_1_1.done && (_a = actions_1.return)) _a.call(actions_1);
} finally {
if (e_3) throw e_3.error;
}
}
var _c = __read(partition(actions, function (action) {
return action.type === actionTypes.raise || action.type === actionTypes.nullEvent;
}), 2),
raisedEvents = _c[0],
otherActions = _c[1];
var _d = __read(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 = flatten(nonEventActions.map(function (actionObject) {
if (actionObject.type === actionTypes.send) {
var sendAction = resolveSend(actionObject, updatedContext, eventObject || { type: ActionTypes.Init }); // TODO: fix ActionTypes.Init
if (isString(sendAction.delay)) {
if (!_this.machine.options.delays || _this.machine.options.delays[sendAction.delay] === undefined) {
if (!IS_PRODUCTION) {
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: ActionTypes.Init });
}
return sendAction;
}
if (actionObject.type === ActionTypes.Pure) {
return actionObject.get(updatedContext, eventObject) || [];
}
return 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({
value: resolvedStateValue || currentState.value,
context: updatedContext,
event: eventObject || initEvent,
historyValue: resolvedStateValue ? historyValue ? 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 || !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 (!IS_PRODUCTION) {
var visitedParents = new Map();
var stateNodes = 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 = 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 mapValues(this.initialStateValue, function (subStateValue, subStateKey) {
return subStateValue ? _this.getStateNode(subStateKey).resolve(stateValue[subStateKey] || subStateValue) : EMPTY_OBJECT;
});
case 'compound':
if (isString(stateValue)) {
var subStateNode = this.getStateNode(stateValue);
if (subStateNode.type === 'parallel' || subStateNode.type === 'compound') {
return _a = {}, _a[stateValue] = subStateNode.initialStateValue, _a;
}
return stateValue;
}
if (!keys(stateValue).length) {
return this.initialStateValue || {};
}
return 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] = 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 toStatePath(stateIdentifier, this.delimiter);
};
Object.defineProperty(StateNode.prototype, "initialStateValue", {
get: function () {
if (this.__cache.initialStateValue) {
return this.__cache.initialStateValue;
}
var initialStateValue = this.type === 'parallel' ? mapFilterValues(this.states, function (state) {
return state.initialStateValue || EMPTY_OBJECT;
}, function (stateNode) {
return !(stateNode.type === 'history');
}) : 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 && isString(historyConfig.target)) {
target = isStateId(historyConfig.target) ? 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 (isString(stateValue)) {
return [this.states[stateValue]];
}
var stateNodes = [];
try {
for (var _b = __values(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 (isString(relativeStateId) && isStateId(relativeStateId)) {
var unresolvedStateNode = this.getStateNodeById(relativeStateId);
return resolve ? unresolvedStateNode.type === 'history' ? unresolvedStateNode.resolveHistory(historyValue) : unresolvedStateNode.initialStateNodes : [unresolvedStateNode];
}
var statePath = toStatePath(relativeStateId, this.delimiter);
var rootStateNode = this.parent || this;
var unresolvedStateNodes = rootStateNode.getFromRelativePath(statePath, historyValue);
if (!resolve) {
return unresolvedStateNodes;
}
return 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 (!IS_PRODUCTION) {
warn(false, "Compound state node '" + this.id + "' has no initial state.");
}
return [this];
}
var initialStateNodePaths = toStatePaths(this.initialStateValue);
return 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 (!keys(this.states).length) {
return undefined;
}
return {
current: relativeStateValue || this.initialStateValue,
states: mapFilterValues(this.states, function (stateNode, key) {
if (!relativeStateValue) {
return stateNode.historyValue();
}
var subStateValue = 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 ? flatten(toStatePaths(this.target).map(function (relativeChildPath) {
return parent.getFromRelativePath(relativeChildPath);
})) : parent.initialStateNodes;
}
var subHistoryValue = nestedPath(parent.path, 'states')(historyValue).current;
if (isString(subHistoryValue)) {
return [parent.getStateNode(subHistoryValue)];
}
return flatten(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 = flatten(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(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(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 = toArray(target);
var guards = this.machine.options.guards;
// Format targets to their full string path
var formattedTargets = targets.map(function (_target) {
if (!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: toActionObjects(toArray(transitionConfig.actions)), cond: 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["" + done(this.id)] = this.config.onDone, _a) : undefined;
var invokeConfig = this.invoke.reduce(function (acc, invokeDef) {
if (invokeDef.onDone) {
acc[doneInvoke(invokeDef.id)] = invokeDef.onDone;
}
if (invokeDef.onError) {
acc[error(invokeDef.id)] = invokeDef.onError;
}
return acc;
}, {});
var delayedTransitions = this.after;
var formattedTransitions = mapValues(__assign({}, onConfig, doneConfig, invokeConfig), function (value, event) {
var e_9, _a;
if (value === undefined) {
return [{ target: undefined, event: event, actions: [], internal: true }];
}
if (isArray(value)) {
return value.map(function (targetTransitionConfig) {
return _this.formatTransition(targetTransitionConfig.target, targetTransitionConfig, event);
});
}
if (isString(value) || isMachine(value)) {
return [_this.formatTransition([value], undefined, event)];
}
if (!IS_PRODUCTION) {
try {
for (var _b = __values(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;
}();
export { StateNode };
//# sourceMappingURL=StateNode.js.map