1401 lines
61 KiB
JavaScript
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
|