xstate 3.2.1 → 3.3.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. package/.vscode/launch.json +15 -13
  2. package/README.md +37 -9
  3. package/dist/xstate.js +1 -1
  4. package/dist/xstate.utils.js +1 -1
  5. package/es/Machine.d.ts +2 -2
  6. package/es/Machine.js +2 -2
  7. package/es/State.d.ts +8 -7
  8. package/es/State.js +3 -2
  9. package/es/StateNode.d.ts +50 -13
  10. package/es/StateNode.js +617 -412
  11. package/es/graph.d.ts +9 -6
  12. package/es/graph.js +31 -24
  13. package/es/patterns.js +1 -1
  14. package/es/scxml.d.ts +2 -1
  15. package/es/scxml.js +33 -10
  16. package/es/types.d.ts +38 -7
  17. package/es/utils.d.ts +14 -1
  18. package/es/utils.js +33 -5
  19. package/lib/Machine.d.ts +2 -2
  20. package/lib/Machine.js +2 -2
  21. package/lib/State.d.ts +8 -7
  22. package/lib/State.js +3 -2
  23. package/lib/StateNode.d.ts +50 -13
  24. package/lib/StateNode.js +616 -411
  25. package/lib/graph.d.ts +9 -6
  26. package/lib/graph.js +30 -22
  27. package/lib/patterns.js +1 -1
  28. package/lib/scxml.d.ts +2 -1
  29. package/lib/scxml.js +33 -10
  30. package/lib/types.d.ts +38 -7
  31. package/lib/utils.d.ts +14 -1
  32. package/lib/utils.js +35 -5
  33. package/package.json +3 -3
  34. package/src/Machine.ts +5 -3
  35. package/src/State.ts +10 -2
  36. package/src/StateNode.ts +966 -590
  37. package/src/graph.ts +60 -31
  38. package/src/scxml.ts +80 -49
  39. package/src/types.ts +48 -7
  40. package/src/utils.ts +52 -7
  41. package/test/actions.test.ts +24 -1
  42. package/test/activities.test.ts +165 -0
  43. package/test/deep.test.ts +14 -16
  44. package/test/deterministic.test.ts +26 -5
  45. package/test/examples/6.17.test.ts +64 -0
  46. package/test/fixtures/id.ts +1 -1
  47. package/test/graph.test.ts +39 -16
  48. package/test/guards.test.ts +172 -15
  49. package/test/history.test.ts +193 -58
  50. package/test/invalid.test.ts +48 -0
  51. package/test/multiple.test.ts +12 -18
  52. package/test/parallel.test.ts +472 -1
  53. package/test/scxml.test.ts +13 -4
  54. package/test/stateIn.test.ts +1 -1
  55. package/test/transient.test.ts +183 -1
package/es/StateNode.js CHANGED
@@ -6,7 +6,7 @@ var __assign = (this && this.__assign) || Object.assign || function(t) {
6
6
  }
7
7
  return t;
8
8
  };
9
- import { getEventType, toStatePath, toStateValue, mapValues, path, toStatePaths, pathsToStateValue, pathToStateValue } from './utils';
9
+ import { getEventType, toStatePath, toStateValue, mapValues, path, toStatePaths, pathsToStateValue, pathToStateValue, getActionType, flatMap, mapFilterValues, nestedPath } from './utils';
10
10
  import { matchesState } from './matchesState';
11
11
  import { State } from './State';
12
12
  import { start, stop, toEventObject, actionTypes } from './actions';
@@ -15,65 +15,15 @@ var HISTORY_KEY = '$history';
15
15
  var NULL_EVENT = '';
16
16
  var STATE_IDENTIFIER = '#';
17
17
  var isStateId = function (str) { return str[0] === STATE_IDENTIFIER; };
18
- var emptyActions = Object.freeze({
19
- onEntry: [],
20
- onExit: [],
21
- actions: []
22
- });
23
- /**
24
- * Given a StateNode, walk up the parent chain until we find an
25
- * orthogonal region of a parallel state, or the top level machine
26
- * itself
27
- */
28
- var regionOf = function (node) {
29
- // If we reach the top of the state machine, we're a "region".
30
- // If our parent is a parallel state, we're a region.
31
- while (node.parent && !node.parent.parallel) {
32
- node = node.parent;
33
- }
34
- return node;
35
- };
36
- /**
37
- * Ensure that the passed in StateNode instance belongs to a region
38
- * that previously had not been used, or that matches the existing
39
- * StateNode for the orthogonal regions. This function is used to
40
- * verify that a transition that has multiple targets ends doesn't try
41
- * to target several states in the same orthogonal region. The passed
42
- * state is added to the regions data structure using the state's
43
- * _region_ (see regionOf), and the region's parent. If there is
44
- * already an object in the structure which is not already the state
45
- * in question, an Error is thrown, otherwise the state is added to
46
- * the structure, and the _region_ is returned.
47
- *
48
- * @param sourceState the state in which the event was triggered (used
49
- * to report error messages)
50
- * @param event the event that triggered the transition (used to
51
- * report error messages)
52
- * @param regions A data structure that retains the current set of
53
- * orthogonal regions (their IDs), grouped by their parallel state
54
- * (their IDs), with the values being the chosen states
55
- * @param state A state to add to the structure if possible.
56
- * @returns The region of the state, in order for the caller to repeat the process for the parent.
57
- * @throws Error if the region found already exists in the regions
58
- */
59
- var ensureTargetStateIsInCorrectRegion = function (sourceState, event, regions, stateToCheck) {
60
- var region = regionOf(stateToCheck);
61
- var parent = region.parent;
62
- var parentId = parent ? parent.id : ''; // '' == machine
63
- regions[parentId] = regions[parentId] || {};
64
- if (regions[parentId][region.id] &&
65
- regions[parentId][region.id] !== stateToCheck) {
66
- throw new Error("Event '" + event + "' on state '" + sourceState.id + "' leads to an invalid configuration: " +
67
- ("Two or more states in the orthogonal region '" + region.id + "'."));
68
- }
69
- // Keep track of which state was chosen in a particular region.
70
- regions[parentId][region.id] = stateToCheck;
71
- return region;
18
+ var defaultOptions = {
19
+ guards: {}
72
20
  };
73
21
  var StateNode = /** @class */ (function () {
74
- function StateNode(config) {
22
+ function StateNode(config, options) {
23
+ if (options === void 0) { options = defaultOptions; }
75
24
  var _this = this;
76
25
  this.config = config;
26
+ this.options = options;
77
27
  this.__cache = {
78
28
  events: undefined,
79
29
  relativeValue: new Map(),
@@ -96,25 +46,31 @@ var StateNode = /** @class */ (function () {
96
46
  this.parallel = !!config.parallel;
97
47
  this.states = (config.states
98
48
  ? mapValues(config.states, function (stateConfig, key) {
49
+ var _a;
99
50
  var stateNode = new StateNode(__assign({}, stateConfig, { key: key, parent: _this }));
100
51
  Object.assign(_this.idMap, __assign((_a = {}, _a[stateNode.id] = stateNode, _a), stateNode.idMap));
101
52
  return stateNode;
102
- var _a;
103
53
  })
104
54
  : {});
55
+ // History config
56
+ this.history =
57
+ config.history === true ? 'shallow' : config.history || false;
105
58
  this.on = config.on ? this.formatTransitions(config.on) : {};
59
+ this.transient = !!this.on[NULL_EVENT];
106
60
  this.strict = !!config.strict;
107
61
  this.onEntry = config.onEntry
108
62
  ? [].concat(config.onEntry)
109
- : undefined;
110
- this.onExit = config.onExit
111
- ? [].concat(config.onExit)
112
- : undefined;
63
+ : [];
64
+ this.onExit = config.onExit ? [].concat(config.onExit) : [];
113
65
  this.data = config.data;
114
66
  this.activities = config.activities;
115
67
  }
116
68
  StateNode.prototype.getStateNodes = function (state) {
117
69
  var _this = this;
70
+ var _a;
71
+ if (!state) {
72
+ return [];
73
+ }
118
74
  var stateValue = state instanceof State
119
75
  ? state.value
120
76
  : toStateValue(state, this.delimiter);
@@ -132,86 +88,406 @@ var StateNode = /** @class */ (function () {
132
88
  var subStateNode = _this.getStateNode(subStateKey).getStateNodes(stateValue[subStateKey]);
133
89
  return allSubStateNodes.concat(subStateNode);
134
90
  }, []));
135
- var _a;
136
91
  };
137
92
  StateNode.prototype.handles = function (event) {
138
93
  var eventType = getEventType(event);
139
94
  return this.events.indexOf(eventType) !== -1;
140
95
  };
96
+ StateNode.prototype._transitionLeafNode = function (stateValue, state, event, extendedState) {
97
+ var stateNode = this.getStateNode(stateValue);
98
+ var next = stateNode._next(state, event, extendedState);
99
+ if (!next.value) {
100
+ var _a = this._next(state, event, extendedState), value = _a.value, entryExitStates = _a.entryExitStates, actions = _a.actions, paths = _a.paths;
101
+ return {
102
+ value: value,
103
+ entryExitStates: {
104
+ entry: entryExitStates ? entryExitStates.entry : new Set(),
105
+ exit: new Set([
106
+ stateNode
107
+ ].concat((entryExitStates
108
+ ? Array.from(entryExitStates.exit)
109
+ : [])))
110
+ },
111
+ actions: actions,
112
+ paths: paths
113
+ };
114
+ }
115
+ return next;
116
+ };
117
+ StateNode.prototype._transitionHierarchicalNode = function (stateValue, state, event, extendedState) {
118
+ var subStateKeys = Object.keys(stateValue);
119
+ var stateNode = this.getStateNode(subStateKeys[0]);
120
+ var next = stateNode._transition(stateValue[subStateKeys[0]], state, event, extendedState);
121
+ if (!next.value) {
122
+ var _a = this._next(state, event, extendedState), value = _a.value, entryExitStates = _a.entryExitStates, actions = _a.actions, paths = _a.paths;
123
+ return {
124
+ value: value,
125
+ entryExitStates: {
126
+ entry: entryExitStates ? entryExitStates.entry : new Set(),
127
+ exit: new Set((next.entryExitStates
128
+ ? Array.from(next.entryExitStates.exit)
129
+ : []).concat([
130
+ stateNode
131
+ ], (entryExitStates
132
+ ? Array.from(entryExitStates.exit)
133
+ : [])))
134
+ },
135
+ actions: actions,
136
+ paths: paths
137
+ };
138
+ }
139
+ return next;
140
+ };
141
+ StateNode.prototype._transitionOrthogonalNode = function (stateValue, state, event, extendedState) {
142
+ var _this = this;
143
+ var noTransitionKeys = [];
144
+ var transitionMap = {};
145
+ Object.keys(stateValue).forEach(function (subStateKey) {
146
+ var subStateValue = stateValue[subStateKey];
147
+ if (!subStateValue) {
148
+ return;
149
+ }
150
+ var next = _this.getStateNode(subStateKey)._transition(subStateValue, state, event, extendedState);
151
+ if (!next.value) {
152
+ noTransitionKeys.push(subStateKey);
153
+ }
154
+ transitionMap[subStateKey] = next;
155
+ });
156
+ var willTransition = Object.keys(transitionMap).some(function (key) { return transitionMap[key].value !== undefined; });
157
+ if (!willTransition) {
158
+ var _a = this._next(state, event, extendedState), value = _a.value, entryExitStates = _a.entryExitStates, actions = _a.actions, paths = _a.paths;
159
+ return {
160
+ value: value,
161
+ entryExitStates: {
162
+ entry: entryExitStates ? entryExitStates.entry : new Set(),
163
+ exit: new Set(Object.keys(this.states).map(function (key) { return _this.states[key]; }).concat((entryExitStates ? Array.from(entryExitStates.exit) : [])))
164
+ },
165
+ actions: actions,
166
+ paths: paths
167
+ };
168
+ }
169
+ var allPaths = flatMap(Object.keys(transitionMap).map(function (key) { return transitionMap[key].paths; }));
170
+ // External transition that escapes orthogonal region
171
+ if (allPaths.length === 1 &&
172
+ !matchesState(pathToStateValue(this.path), pathToStateValue(allPaths[0]))) {
173
+ return {
174
+ value: this.machine.resolve(pathsToStateValue(allPaths)),
175
+ entryExitStates: Object.keys(transitionMap)
176
+ .map(function (key) { return transitionMap[key].entryExitStates; })
177
+ .reduce(function (allEntryExitStates, entryExitStates) {
178
+ var _a = entryExitStates, entry = _a.entry, exit = _a.exit;
179
+ return {
180
+ entry: new Set(Array.from(allEntryExitStates.entry).concat(Array.from(entry))),
181
+ exit: new Set(Array.from(allEntryExitStates.exit).concat(Array.from(exit)))
182
+ };
183
+ }, { entry: new Set(), exit: new Set() }),
184
+ actions: flatMap(Object.keys(transitionMap).map(function (key) {
185
+ return transitionMap[key].actions;
186
+ })),
187
+ paths: allPaths
188
+ };
189
+ }
190
+ var allResolvedPaths = flatMap(Object.keys(transitionMap).map(function (key) {
191
+ var transition = transitionMap[key];
192
+ var value = transition.value || state.value;
193
+ return toStatePaths(path(_this.path)(value)[key]).map(function (statePath) {
194
+ return _this.path.concat(key, statePath);
195
+ });
196
+ }));
197
+ var nextStateValue = this.machine.resolve(pathsToStateValue(allResolvedPaths));
198
+ return {
199
+ value: nextStateValue,
200
+ entryExitStates: Object.keys(transitionMap).reduce(function (allEntryExitStates, key) {
201
+ var _a = transitionMap[key], subStateValue = _a.value, entryExitStates = _a.entryExitStates;
202
+ // If the event was not handled (no subStateValue),
203
+ // machine should still be in state without reentry/exit.
204
+ if (!subStateValue || !entryExitStates) {
205
+ return allEntryExitStates;
206
+ }
207
+ var entry = entryExitStates.entry, exit = entryExitStates.exit;
208
+ return {
209
+ entry: new Set(Array.from(allEntryExitStates.entry).concat(Array.from(entry))),
210
+ exit: new Set(Array.from(allEntryExitStates.exit).concat(Array.from(exit)))
211
+ };
212
+ }, { entry: new Set(), exit: new Set() }),
213
+ actions: flatMap(Object.keys(transitionMap).map(function (key) {
214
+ return transitionMap[key].actions;
215
+ })),
216
+ paths: toStatePaths(nextStateValue)
217
+ };
218
+ };
219
+ StateNode.prototype._transition = function (stateValue, state, event, extendedState) {
220
+ // leaf node
221
+ if (typeof stateValue === 'string') {
222
+ return this._transitionLeafNode(stateValue, state, event, extendedState);
223
+ }
224
+ // hierarchical node
225
+ if (Object.keys(stateValue).length === 1) {
226
+ return this._transitionHierarchicalNode(stateValue, state, event, extendedState);
227
+ }
228
+ // orthogonal node
229
+ return this._transitionOrthogonalNode(stateValue, state, event, extendedState);
230
+ };
231
+ StateNode.prototype._next = function (state, event, extendedState) {
232
+ var _this = this;
233
+ var eventType = getEventType(event);
234
+ var candidates = this.on[eventType];
235
+ var actions = this.transient
236
+ ? [{ type: actionTypes.null }]
237
+ : [];
238
+ if (!candidates || !candidates.length) {
239
+ return {
240
+ value: undefined,
241
+ entryExitStates: undefined,
242
+ actions: actions,
243
+ paths: []
244
+ };
245
+ }
246
+ var nextStateStrings = [];
247
+ var selectedTransition;
248
+ for (var _i = 0, candidates_1 = candidates; _i < candidates_1.length; _i++) {
249
+ var candidate = candidates_1[_i];
250
+ var _a = candidate, cond = _a.cond, stateIn = _a.in
251
+ // actions: transitionActions
252
+ ;
253
+ var extendedStateObject = extendedState || {};
254
+ var eventObject = toEventObject(event);
255
+ var isInState = stateIn
256
+ ? matchesState(toStateValue(stateIn, this.delimiter), path(this.path.slice(0, -2))(state.value))
257
+ : true;
258
+ if ((!cond ||
259
+ this._evaluateCond(cond, extendedStateObject, eventObject, state.value)) &&
260
+ (!stateIn || isInState)) {
261
+ nextStateStrings = Array.isArray(candidate.target)
262
+ ? candidate.target
263
+ : [candidate.target];
264
+ actions.push.apply(actions, (candidate.actions ? candidate.actions : [])); // TODO: fixme;
265
+ selectedTransition = candidate;
266
+ break;
267
+ }
268
+ }
269
+ if (nextStateStrings.length === 0) {
270
+ return {
271
+ value: undefined,
272
+ entryExitStates: undefined,
273
+ actions: actions,
274
+ paths: []
275
+ };
276
+ }
277
+ var nextStateNodes = flatMap(nextStateStrings.map(function (str) {
278
+ return _this.getRelativeStateNodes(str, state.historyValue);
279
+ }));
280
+ var nextStatePaths = nextStateNodes.map(function (stateNode) { return stateNode.path; });
281
+ var entryExitStates = nextStateNodes.reduce(function (allEntryExitStates, nextStateNode) {
282
+ var _a = _this._getEntryExitStates(nextStateNode, !!selectedTransition.internal), entry = _a.entry, exit = _a.exit;
283
+ return {
284
+ entry: new Set(Array.from(allEntryExitStates.entry).concat(Array.from(entry))),
285
+ exit: new Set(Array.from(allEntryExitStates.exit).concat(Array.from(exit)))
286
+ };
287
+ }, { entry: new Set(), exit: new Set() });
288
+ return {
289
+ value: this.machine.resolve(pathsToStateValue(flatMap(nextStateStrings.map(function (str) {
290
+ return _this.getRelativeStateNodes(str, state.historyValue).map(function (s) { return s.path; });
291
+ })))),
292
+ entryExitStates: entryExitStates,
293
+ actions: actions,
294
+ paths: nextStatePaths
295
+ };
296
+ };
297
+ StateNode.prototype._getEntryExitStates = function (nextStateNode, internal) {
298
+ var entryExitStates = {
299
+ entry: [],
300
+ exit: []
301
+ };
302
+ var fromPath = this.path;
303
+ var toPath = nextStateNode.path;
304
+ var parent = this.machine;
305
+ for (var i = 0; i < Math.min(fromPath.length, toPath.length); i++) {
306
+ var fromPathSegment = fromPath[i];
307
+ var toPathSegment = toPath[i];
308
+ if (fromPathSegment === toPathSegment) {
309
+ parent = parent.getStateNode(fromPathSegment);
310
+ }
311
+ else {
312
+ break;
313
+ }
314
+ }
315
+ var commonAncestorPath = parent.path;
316
+ var marker = parent;
317
+ for (var _i = 0, _a = fromPath.slice(commonAncestorPath.length); _i < _a.length; _i++) {
318
+ var segment = _a[_i];
319
+ marker = marker.getStateNode(segment);
320
+ entryExitStates.exit.unshift(marker);
321
+ }
322
+ // Child node
323
+ if (parent === this) {
324
+ if (!internal) {
325
+ entryExitStates.exit.push(this);
326
+ entryExitStates.entry.push(this);
327
+ }
328
+ }
329
+ marker = parent;
330
+ for (var _b = 0, _c = toPath.slice(commonAncestorPath.length); _b < _c.length; _b++) {
331
+ var segment = _c[_b];
332
+ marker = marker.getStateNode(segment);
333
+ entryExitStates.entry.push(marker);
334
+ }
335
+ return {
336
+ entry: new Set(entryExitStates.entry),
337
+ exit: new Set(entryExitStates.exit)
338
+ };
339
+ };
340
+ StateNode.prototype._evaluateCond = function (condition, extendedState, eventObject, interimState) {
341
+ var condFn;
342
+ if (typeof condition === 'string') {
343
+ if (!this.machine.options.guards[condition]) {
344
+ throw new Error("String condition '" + condition + "' is not defined on machine '" + this.machine.id + "'");
345
+ }
346
+ condFn = this.machine.options.guards[condition];
347
+ }
348
+ else {
349
+ condFn = condition;
350
+ }
351
+ return condFn(extendedState, eventObject, interimState);
352
+ };
353
+ StateNode.prototype._getActions = function (transition) {
354
+ var entryExitActions = {
355
+ entry: transition.entryExitStates
356
+ ? flatMap(Array.from(transition.entryExitStates.entry).map(function (n) { return n.onEntry.concat((n.activities
357
+ ? n.activities.map(function (activity) { return start(activity); })
358
+ : [])); }))
359
+ : [],
360
+ exit: transition.entryExitStates
361
+ ? flatMap(Array.from(transition.entryExitStates.exit).map(function (n) { return n.onExit.concat((n.activities
362
+ ? n.activities.map(function (activity) { return stop(activity); })
363
+ : [])); }))
364
+ : []
365
+ };
366
+ var actions = (entryExitActions.exit || [])
367
+ .concat(transition.actions || [])
368
+ .concat(entryExitActions.entry || []);
369
+ return actions;
370
+ };
371
+ StateNode.prototype._getActivities = function (state, transition) {
372
+ if (!transition.entryExitStates) {
373
+ return {};
374
+ }
375
+ var activityMap = __assign({}, state.activities);
376
+ Array.from(transition.entryExitStates.exit).forEach(function (stateNode) {
377
+ if (!stateNode.activities) {
378
+ return; // TODO: fixme
379
+ }
380
+ stateNode.activities.forEach(function (activity) {
381
+ activityMap[getActionType(activity)] = false;
382
+ });
383
+ });
384
+ Array.from(transition.entryExitStates.entry).forEach(function (stateNode) {
385
+ if (!stateNode.activities) {
386
+ return; // TODO: fixme
387
+ }
388
+ stateNode.activities.forEach(function (activity) {
389
+ activityMap[getActionType(activity)] = true;
390
+ });
391
+ });
392
+ return activityMap;
393
+ };
141
394
  StateNode.prototype.transition = function (state, event, extendedState) {
395
+ var _a;
142
396
  var resolvedStateValue = typeof state === 'string'
143
397
  ? this.resolve(pathToStateValue(this.getResolvedPath(state)))
144
- : state instanceof State ? state : this.resolve(state);
398
+ : state instanceof State
399
+ ? state
400
+ : this.resolve(state);
401
+ var eventType = getEventType(event);
145
402
  if (this.strict) {
146
- var eventType = getEventType(event);
147
403
  if (this.events.indexOf(eventType) === -1) {
148
404
  throw new Error("Machine '" + this.id + "' does not accept event '" + eventType + "'");
149
405
  }
150
406
  }
151
407
  var currentState = State.from(resolvedStateValue);
152
- var stateTransition = this.transitionStateValue(currentState, event, currentState, extendedState);
153
- var nextState = this.stateTransitionToState(stateTransition, currentState);
408
+ var historyValue = resolvedStateValue instanceof State
409
+ ? resolvedStateValue.historyValue
410
+ ? resolvedStateValue.historyValue
411
+ : this.machine.historyValue(resolvedStateValue.value)
412
+ : this.machine.historyValue(resolvedStateValue);
413
+ var stateTransition = this._transition(currentState.value, currentState, event, extendedState);
414
+ try {
415
+ this.ensureValidPaths(stateTransition.paths);
416
+ }
417
+ catch (e) {
418
+ throw new Error("Event '" + eventType + "' leads to an invalid configuration: " + e.message);
419
+ }
420
+ var actions = this._getActions(stateTransition);
421
+ var activities = this._getActivities(currentState, stateTransition);
422
+ var raisedEvents = actions.filter(function (action) {
423
+ return typeof action === 'object' &&
424
+ (action.type === actionTypes.raise || action.type === actionTypes.null);
425
+ });
426
+ var nonEventActions = actions.filter(function (action) {
427
+ return typeof action !== 'object' ||
428
+ (action.type !== actionTypes.raise && action.type !== actionTypes.null);
429
+ });
430
+ var stateNodes = stateTransition.value
431
+ ? this.getStateNodes(stateTransition.value)
432
+ : [];
433
+ var isTransient = stateNodes.some(function (stateNode) { return stateNode.transient; });
434
+ if (isTransient) {
435
+ raisedEvents.push({ type: actionTypes.null });
436
+ }
437
+ var data = {};
438
+ stateNodes.forEach(function (stateNode) {
439
+ data[stateNode.id] = stateNode.data;
440
+ });
441
+ var nextState = stateTransition.value
442
+ ? new State(stateTransition.value, StateNode.updateHistoryValue(historyValue, stateTransition.value), currentState, nonEventActions, activities, data, raisedEvents)
443
+ : undefined;
154
444
  if (!nextState) {
445
+ // Unchanged state should be returned with no actions
155
446
  return State.inert(currentState);
156
447
  }
448
+ // Dispose of previous histories to prevent memory leaks
449
+ delete currentState.history;
157
450
  var maybeNextState = nextState;
158
- var raisedEvents = nextState.actions.filter(function (action) { return typeof action === 'object' && action.type === actionTypes.raise; });
159
- if (raisedEvents.length) {
160
- var raisedEvent = raisedEvents[0].event;
161
- nextState = this.transition(nextState, raisedEvent, extendedState);
162
- (_a = nextState.actions).unshift.apply(_a, nextState.actions);
163
- return nextState;
164
- }
165
- if (stateTransition.events.length) {
166
- var raised = stateTransition.events[0].type === actionTypes.raise
167
- ? stateTransition.events[0].event
168
- : undefined;
169
- var nullEvent = stateTransition.events[0].type === actionTypes.null;
170
- if (raised || nullEvent) {
171
- maybeNextState = this.transition(nextState, nullEvent ? NULL_EVENT : raised, extendedState);
172
- (_b = maybeNextState.actions).unshift.apply(_b, nextState.actions);
173
- return maybeNextState;
174
- }
451
+ while (raisedEvents.length) {
452
+ var currentActions = maybeNextState.actions;
453
+ var raisedEvent = raisedEvents.shift();
454
+ maybeNextState = this.transition(maybeNextState, raisedEvent.type === actionTypes.null ? NULL_EVENT : raisedEvent.event, extendedState);
455
+ (_a = maybeNextState.actions).unshift.apply(_a, currentActions);
175
456
  }
176
- return nextState;
177
- var _a, _b;
457
+ return maybeNextState;
178
458
  };
179
- StateNode.prototype.stateTransitionToState = function (stateTransition, prevState) {
180
- var nextStatePaths = stateTransition.statePaths, nextActions = stateTransition.actions, nextActivities = stateTransition.activities, events = stateTransition.events;
181
- if (!nextStatePaths.length) {
182
- return undefined;
183
- }
184
- var prevActivities = prevState instanceof State ? prevState.activities : undefined;
185
- var activities = __assign({}, prevActivities, nextActivities);
186
- var nextStateValue = this.resolve(pathsToStateValue(nextStatePaths));
187
- return new State(
188
- // next state value
189
- nextStateValue,
190
- // history
191
- State.from(prevState),
192
- // effects
193
- nextActions
194
- ? nextActions.onExit
195
- .concat(nextActions.actions)
196
- .concat(nextActions.onEntry)
197
- : [],
198
- // activities
199
- activities,
200
- // data
201
- this.getStateNodes(nextStateValue).reduce(function (data, stateNode) {
202
- if (stateNode.data !== undefined) {
203
- data[stateNode.id] = stateNode.data;
459
+ StateNode.prototype.ensureValidPaths = function (paths) {
460
+ var _this = this;
461
+ var visitedParents = new Map();
462
+ var stateNodes = flatMap(paths.map(function (_path) { return _this.getRelativeStateNodes(_path); }));
463
+ outer: for (var _i = 0, stateNodes_1 = stateNodes; _i < stateNodes_1.length; _i++) {
464
+ var stateNode = stateNodes_1[_i];
465
+ var marker = stateNode;
466
+ while (marker.parent) {
467
+ if (visitedParents.has(marker.parent)) {
468
+ if (marker.parent.parallel) {
469
+ continue outer;
470
+ }
471
+ throw new Error("State node '" + stateNode.id + "' shares parent '" + marker.parent.id + "' with state node '" + visitedParents
472
+ .get(marker.parent)
473
+ .map(function (a) { return a.id; }) + "'");
474
+ }
475
+ if (!visitedParents.get(marker.parent)) {
476
+ visitedParents.set(marker.parent, [stateNode]);
477
+ }
478
+ else {
479
+ visitedParents.get(marker.parent).push(stateNode);
480
+ }
481
+ marker = marker.parent;
204
482
  }
205
- return data;
206
- }, {}), events);
483
+ }
207
484
  };
208
485
  StateNode.prototype.getStateNode = function (stateKey) {
209
486
  if (isStateId(stateKey)) {
210
487
  return this.machine.getStateNodeById(stateKey);
211
488
  }
212
489
  if (!this.states) {
213
- throw new Error("Unable to retrieve child state '" + stateKey + "' from '" + this
214
- .id + "'; no child states exist.");
490
+ throw new Error("Unable to retrieve child state '" + stateKey + "' from '" + this.id + "'; no child states exist.");
215
491
  }
216
492
  var result = this.states[stateKey];
217
493
  if (!result) {
@@ -223,14 +499,24 @@ var StateNode = /** @class */ (function () {
223
499
  var resolvedStateId = isStateId(stateId)
224
500
  ? stateId.slice(STATE_IDENTIFIER.length)
225
501
  : stateId;
226
- var stateNode = this.idMap[resolvedStateId];
502
+ var stateNode = this.machine.idMap[resolvedStateId];
227
503
  if (!stateNode) {
228
504
  throw new Error("Substate '#" + resolvedStateId + "' does not exist on '" + this.id + "'");
229
505
  }
230
506
  return stateNode;
231
507
  };
508
+ StateNode.prototype.getStateNodeByPath = function (statePath) {
509
+ var arrayStatePath = toStatePath(statePath, this.delimiter);
510
+ var currentStateNode = this;
511
+ while (arrayStatePath.length) {
512
+ var key = arrayStatePath.shift();
513
+ currentStateNode = currentStateNode.getStateNode(key);
514
+ }
515
+ return currentStateNode;
516
+ };
232
517
  StateNode.prototype.resolve = function (stateValue) {
233
518
  var _this = this;
519
+ var _a;
234
520
  if (typeof stateValue === 'string') {
235
521
  var subStateNode = this.getStateNode(stateValue);
236
522
  return subStateNode.initial
@@ -238,290 +524,24 @@ var StateNode = /** @class */ (function () {
238
524
  }
239
525
  if (this.parallel) {
240
526
  return mapValues(this.initialStateValue, function (subStateValue, subStateKey) {
241
- return _this.getStateNode(subStateKey).resolve(stateValue[subStateKey] || subStateValue);
527
+ return subStateValue
528
+ ? _this.getStateNode(subStateKey).resolve(stateValue[subStateKey] || subStateValue)
529
+ : {};
242
530
  });
243
531
  }
244
532
  return mapValues(stateValue, function (subStateValue, subStateKey) {
245
- return _this.getStateNode(subStateKey).resolve(subStateValue);
246
- });
247
- var _a;
248
- };
249
- StateNode.prototype.transitionStateValue = function (state, event, fullState, extendedState) {
250
- var _this = this;
251
- var history = state.history;
252
- var stateValue = state.value;
253
- if (typeof stateValue === 'string') {
254
- var subStateNode = this.getStateNode(stateValue);
255
- var result = subStateNode.next(event, fullState, history ? history.value : undefined, extendedState);
256
- // If a machine substate returns no potential transitions,
257
- // check on the machine itself.
258
- if (!result.statePaths.length && !this.parent) {
259
- return this.next(event, fullState, history ? history.value : undefined, extendedState);
260
- }
261
- return result;
262
- }
263
- // Potential transition tuples from parent state nodes
264
- var potentialStateTransitions = [];
265
- var willTransition = false;
266
- var nextStateTransitionMap = mapValues(stateValue, function (subStateValue, subStateKey) {
267
- var subStateNode = _this.getStateNode(subStateKey);
268
- var subHistory = history ? history.value[subStateKey] : undefined;
269
- var subState = new State(subStateValue, subHistory ? State.from(subHistory) : undefined);
270
- var subStateTransition = subStateNode.transitionStateValue(subState, event, fullState, extendedState);
271
- if (!subStateTransition.statePaths.length) {
272
- potentialStateTransitions.push(subStateNode.next(event, fullState, history ? history.value : undefined, extendedState));
273
- }
274
- else {
275
- willTransition = true;
276
- }
277
- return subStateTransition;
278
- });
279
- if (!willTransition) {
280
- if (this.parallel) {
281
- if (potentialStateTransitions.length) {
282
- // Select the first potential state transition to take
283
- return potentialStateTransitions[0];
284
- }
285
- return {
286
- statePaths: [],
287
- actions: emptyActions,
288
- activities: undefined,
289
- events: []
290
- };
291
- }
292
- var subStateKey = Object.keys(nextStateTransitionMap)[0];
293
- // try with parent
294
- var _a = this.getStateNode(subStateKey).next(event, fullState, history ? history.value : undefined, extendedState), parentStatePaths = _a.statePaths, parentNextActions = _a.actions, parentActivities = _a.activities;
295
- var nextActions = nextStateTransitionMap[subStateKey].actions;
296
- var activities = nextStateTransitionMap[subStateKey].activities;
297
- var allActivities = __assign({}, activities, parentActivities);
298
- var allActions = parentNextActions
299
- ? nextActions
300
- ? {
301
- onEntry: nextActions.onEntry.concat(parentNextActions.onEntry),
302
- actions: nextActions.actions.concat(parentNextActions.actions),
303
- onExit: nextActions.onExit.concat(parentNextActions.onExit)
304
- }
305
- : parentNextActions
306
- : nextActions;
307
- return {
308
- statePaths: parentStatePaths,
309
- actions: allActions,
310
- activities: allActivities,
311
- events: []
312
- };
313
- }
314
- if (this.parallel) {
315
- nextStateTransitionMap = __assign({}, mapValues(this.initialState.value, function (subStateValue, key) {
316
- var subStateTransition = nextStateTransitionMap[key];
317
- return {
318
- statePaths: subStateTransition && subStateTransition.statePaths.length
319
- ? subStateTransition.statePaths
320
- : toStatePaths(stateValue[key] || subStateValue).map(function (subPath) { return _this.getStateNode(key).path.concat(subPath); }),
321
- actions: subStateTransition && subStateTransition.actions
322
- ? subStateTransition.actions
323
- : {
324
- onEntry: [],
325
- onExit: [],
326
- actions: []
327
- },
328
- activities: undefined,
329
- events: []
330
- };
331
- }));
332
- }
333
- var finalActions = {
334
- onEntry: [],
335
- actions: [],
336
- onExit: []
337
- };
338
- var finalActivities = {};
339
- mapValues(nextStateTransitionMap, function (subStateTransition) {
340
- var
341
- // statePaths: nextSubStatePaths,
342
- nextSubActions = subStateTransition.actions, nextSubActivities = subStateTransition.activities;
343
- if (nextSubActions) {
344
- if (nextSubActions.onEntry) {
345
- (_a = finalActions.onEntry).push.apply(_a, nextSubActions.onEntry);
346
- }
347
- if (nextSubActions.actions) {
348
- (_b = finalActions.actions).push.apply(_b, nextSubActions.actions);
349
- }
350
- if (nextSubActions.onExit) {
351
- (_c = finalActions.onExit).push.apply(_c, nextSubActions.onExit);
352
- }
353
- }
354
- if (nextSubActivities) {
355
- Object.assign(finalActivities, nextSubActivities);
356
- }
357
- var _a, _b, _c;
358
- });
359
- return {
360
- statePaths: Object.keys(nextStateTransitionMap)
361
- .map(function (stateKey) { return nextStateTransitionMap[stateKey].statePaths; })
362
- .reduce(function (a, b) { return a.concat(b); }, []),
363
- actions: finalActions,
364
- activities: finalActivities,
365
- events: []
366
- };
367
- };
368
- StateNode.prototype.next = function (event, fullState, history, extendedState) {
369
- var _this = this;
370
- var eventType = getEventType(event);
371
- var actionMap = { onEntry: [], onExit: [], actions: [] };
372
- var activityMap = {};
373
- var candidates = this.on[eventType];
374
- if (this.onExit) {
375
- actionMap.onExit = this.onExit;
376
- }
377
- if (this.activities) {
378
- this.activities.forEach(function (activity) {
379
- activityMap[getEventType(activity)] = false;
380
- actionMap.onExit = actionMap.onExit.concat(stop(activity));
381
- });
382
- }
383
- if (!candidates) {
384
- return {
385
- statePaths: [],
386
- actions: actionMap,
387
- activities: activityMap,
388
- events: []
389
- };
390
- }
391
- var nextStateStrings = [];
392
- for (var _i = 0, candidates_1 = candidates; _i < candidates_1.length; _i++) {
393
- var candidate = candidates_1[_i];
394
- var _a = candidate, cond = _a.cond, stateIn = _a.in, transitionActions = _a.actions;
395
- var extendedStateObject = extendedState || {};
396
- var eventObject = toEventObject(event);
397
- var isInState = stateIn
398
- ? matchesState(toStateValue(stateIn, this.delimiter), path(this.path.slice(0, -2))(fullState.value))
399
- : true;
400
- if ((!cond || cond(extendedStateObject, eventObject)) &&
401
- (!stateIn || isInState)) {
402
- nextStateStrings = Array.isArray(candidate.target)
403
- ? candidate.target
404
- : [candidate.target];
405
- if (transitionActions) {
406
- actionMap.actions = actionMap.actions.concat(transitionActions);
407
- }
408
- break;
409
- }
410
- }
411
- if (nextStateStrings.length === 0) {
412
- return {
413
- statePaths: [],
414
- actions: actionMap,
415
- activities: activityMap,
416
- events: []
417
- };
418
- }
419
- var finalPaths = [];
420
- var raisedEvents = [];
421
- var usedRegions = {};
422
- nextStateStrings.forEach(function (nextStateString) {
423
- var nextStatePath = _this.getResolvedPath(nextStateString);
424
- var currentState = isStateId(nextStateString)
425
- ? _this.machine
426
- : _this.parent;
427
- var currentHistory = history;
428
- var currentPath = _this.key;
429
- nextStatePath.forEach(function (subPath) {
430
- if (subPath === '') {
431
- actionMap.onExit = [];
432
- currentState = _this;
433
- return;
434
- }
435
- if (!currentState || !currentState.states) {
436
- throw new Error("Unable to read '" + subPath + "' from '" + _this.id + "'");
437
- }
438
- if (subPath === HISTORY_KEY) {
439
- if (!Object.keys(currentState.states).length) {
440
- subPath = '';
441
- }
442
- else if (currentHistory) {
443
- subPath =
444
- typeof currentHistory === 'object'
445
- ? Object.keys(currentHistory)[0]
446
- : currentHistory;
447
- }
448
- else if (currentState.initial) {
449
- subPath = currentState.initial;
450
- }
451
- else {
452
- throw new Error("Cannot read '" + HISTORY_KEY + "' from state '" + currentState.id + "': missing 'initial'");
453
- }
454
- }
455
- try {
456
- if (subPath !== '') {
457
- currentState = currentState.getStateNode(subPath);
458
- }
459
- }
460
- catch (e) {
461
- throw new Error("Event '" + event + "' on state '" + currentPath + "' leads to undefined state '" + nextStatePath.join(_this.delimiter) + "'.");
462
- }
463
- if (currentState.onEntry) {
464
- actionMap.onEntry = actionMap.onEntry.concat(currentState.onEntry);
465
- }
466
- if (currentState.activities) {
467
- currentState.activities.forEach(function (activity) {
468
- activityMap[getEventType(activity)] = true;
469
- actionMap.onEntry = actionMap.onEntry.concat(start(activity));
470
- });
471
- }
472
- currentPath = subPath;
473
- if (currentHistory) {
474
- currentHistory = currentHistory[subPath];
475
- }
476
- });
477
- if (!currentState) {
478
- throw new Error('no state');
479
- }
480
- var region = ensureTargetStateIsInCorrectRegion(_this, event, usedRegions, currentState);
481
- while (region.parent) {
482
- region = ensureTargetStateIsInCorrectRegion(_this, event, usedRegions, region.parent);
483
- }
484
- var paths = [currentState.path];
485
- if (currentState.initial || currentState.parallel) {
486
- var initialState = currentState.initialState;
487
- actionMap.onEntry = actionMap.onEntry.concat(initialState.actions);
488
- paths = toStatePaths(initialState.value).map(function (subPath) {
489
- return currentState.path.concat(subPath);
490
- });
491
- }
492
- finalPaths.push.apply(finalPaths, paths);
493
- while (currentState.initial) {
494
- if (!currentState || !currentState.states) {
495
- throw new Error("Invalid initial state");
496
- }
497
- currentState = currentState.states[currentState.initial];
498
- if (currentState.activities) {
499
- currentState.activities.forEach(function (activity) {
500
- activityMap[getEventType(activity)] = true;
501
- actionMap.onEntry = actionMap.onEntry.concat(start(activity));
502
- });
503
- }
504
- }
505
- var myActions = (currentState.onEntry
506
- ? currentState.onEntry.filter(function (action) {
507
- return typeof action === 'object' && action.type === actionTypes.raise;
508
- })
509
- : []).concat(currentState.on[NULL_EVENT] ? { type: actionTypes.null } : []);
510
- myActions.forEach(function (action) { return raisedEvents.push(action); });
533
+ return subStateValue
534
+ ? _this.getStateNode(subStateKey).resolve(subStateValue)
535
+ : {};
511
536
  });
512
- return {
513
- statePaths: finalPaths,
514
- actions: actionMap,
515
- activities: activityMap,
516
- events: raisedEvents
517
- };
518
537
  };
519
538
  Object.defineProperty(StateNode.prototype, "resolvedStateValue", {
520
539
  get: function () {
540
+ var _a, _b;
521
541
  var key = this.key;
522
542
  if (this.parallel) {
523
543
  return _a = {},
524
- _a[key] = mapValues(this.states, function (stateNode) { return stateNode.resolvedStateValue[stateNode.key]; }),
544
+ _a[key] = mapFilterValues(this.states, function (stateNode) { return stateNode.resolvedStateValue[stateNode.key]; }, function (stateNode) { return !stateNode.history; }),
525
545
  _a;
526
546
  }
527
547
  if (!this.initial) {
@@ -531,7 +551,6 @@ var StateNode = /** @class */ (function () {
531
551
  return _b = {},
532
552
  _b[key] = this.states[this.initial].resolvedStateValue,
533
553
  _b;
534
- var _a, _b;
535
554
  },
536
555
  enumerable: true,
537
556
  configurable: true
@@ -548,12 +567,14 @@ var StateNode = /** @class */ (function () {
548
567
  };
549
568
  Object.defineProperty(StateNode.prototype, "initialStateValue", {
550
569
  get: function () {
551
- var initialStateValue = this.__cache.initialState ||
552
- (this.parallel
553
- ? mapValues(this.states, function (state) { return state.initialStateValue; })
554
- : typeof this.resolvedStateValue === 'string'
555
- ? undefined
556
- : this.resolvedStateValue[this.key]);
570
+ if (this.__cache.initialState) {
571
+ return this.__cache.initialState;
572
+ }
573
+ var initialStateValue = (this.parallel
574
+ ? mapFilterValues(this.states, function (state) { return state.initialStateValue || {}; }, function (stateNode) { return !stateNode.history; })
575
+ : typeof this.resolvedStateValue === 'string'
576
+ ? undefined
577
+ : this.resolvedStateValue[this.key]);
557
578
  this.__cache.initialState = initialStateValue;
558
579
  return this.__cache.initialState;
559
580
  },
@@ -562,6 +583,7 @@ var StateNode = /** @class */ (function () {
562
583
  });
563
584
  Object.defineProperty(StateNode.prototype, "initialState", {
564
585
  get: function () {
586
+ var _a;
565
587
  var initialStateValue = this.initialStateValue;
566
588
  if (!initialStateValue) {
567
589
  throw new Error("Cannot retrieve initial state from simple state '" + this.id + ".'");
@@ -579,7 +601,42 @@ var StateNode = /** @class */ (function () {
579
601
  });
580
602
  }
581
603
  });
582
- return new State(initialStateValue, undefined, actions, activityMap);
604
+ // TODO: deduplicate - DRY (from this.transition())
605
+ var raisedEvents = actions.filter(function (action) {
606
+ return typeof action === 'object' &&
607
+ (action.type === actionTypes.raise || action.type === actionTypes.null);
608
+ });
609
+ var initialState = new State(initialStateValue, undefined, undefined, actions, activityMap);
610
+ var maybeNextState = initialState;
611
+ while (raisedEvents.length) {
612
+ var currentActions = maybeNextState.actions;
613
+ var raisedEvent = raisedEvents.shift();
614
+ maybeNextState = this.transition(maybeNextState, raisedEvent.type === actionTypes.null ? NULL_EVENT : raisedEvent.event, undefined // TODO: consider initial state given external state
615
+ );
616
+ (_a = maybeNextState.actions).unshift.apply(_a, currentActions);
617
+ }
618
+ return maybeNextState;
619
+ },
620
+ enumerable: true,
621
+ configurable: true
622
+ });
623
+ Object.defineProperty(StateNode.prototype, "target", {
624
+ get: function () {
625
+ var target;
626
+ if (this.history) {
627
+ var historyConfig = this.config;
628
+ if (historyConfig.target && typeof historyConfig.target === 'string') {
629
+ target = isStateId(historyConfig.target)
630
+ ? pathToStateValue(this.machine
631
+ .getStateNodeById(historyConfig.target)
632
+ .path.slice(this.path.length - 1))
633
+ : historyConfig.target;
634
+ }
635
+ else {
636
+ target = historyConfig.target;
637
+ }
638
+ }
639
+ return target;
583
640
  },
584
641
  enumerable: true,
585
642
  configurable: true
@@ -595,22 +652,151 @@ var StateNode = /** @class */ (function () {
595
652
  });
596
653
  return stateNodes;
597
654
  };
598
- StateNode.prototype.getState = function (relativeStateId) {
655
+ /**
656
+ * Returns the leaf nodes from a state path relative to this state node.
657
+ *
658
+ * @param relativeStateId The relative state path to retrieve the state nodes
659
+ * @param history The previous state to retrieve history
660
+ * @param resolve Whether state nodes should resolve to initial child state nodes
661
+ */
662
+ StateNode.prototype.getRelativeStateNodes = function (relativeStateId, historyValue, resolve) {
663
+ if (resolve === void 0) { resolve = true; }
599
664
  if (typeof relativeStateId === 'string' && isStateId(relativeStateId)) {
600
- return this.getStateNodeById(relativeStateId);
665
+ var unresolvedStateNode = this.getStateNodeById(relativeStateId);
666
+ return resolve
667
+ ? unresolvedStateNode.history
668
+ ? unresolvedStateNode.resolveHistory(historyValue)
669
+ : unresolvedStateNode.initialStateNodes
670
+ : [unresolvedStateNode];
601
671
  }
602
672
  var statePath = toStatePath(relativeStateId, this.delimiter);
603
- try {
604
- return statePath.reduce(function (subState, subPath) {
605
- if (!subState.states) {
606
- throw new Error("Cannot retrieve subPath '" + subPath + "' from node with no states");
673
+ var rootStateNode = this.parent || this;
674
+ var unresolvedStateNodes = rootStateNode.getFromRelativePath(statePath, historyValue);
675
+ if (!resolve) {
676
+ return unresolvedStateNodes;
677
+ }
678
+ return flatMap(unresolvedStateNodes.map(function (stateNode) { return stateNode.initialStateNodes; }));
679
+ };
680
+ Object.defineProperty(StateNode.prototype, "initialStateNodes", {
681
+ get: function () {
682
+ var _this = this;
683
+ // todo - isLeafNode or something
684
+ if (!this.parallel && !this.initial) {
685
+ return [this];
686
+ }
687
+ var initialState = this.initialState;
688
+ var initialStateNodePaths = toStatePaths(initialState.value);
689
+ return flatMap(initialStateNodePaths.map(function (initialPath) {
690
+ return _this.getFromRelativePath(initialPath);
691
+ }));
692
+ },
693
+ enumerable: true,
694
+ configurable: true
695
+ });
696
+ /**
697
+ * Retrieves state nodes from a relative path to this state node.
698
+ *
699
+ * @param relativePath The relative path from this state node
700
+ * @param historyValue
701
+ */
702
+ StateNode.prototype.getFromRelativePath = function (relativePath, historyValue) {
703
+ var _this = this;
704
+ if (!relativePath.length) {
705
+ return [this];
706
+ }
707
+ var x = relativePath[0], xs = relativePath.slice(1);
708
+ if (!this.states) {
709
+ throw new Error("Cannot retrieve subPath '" + x + "' from node with no states");
710
+ }
711
+ // TODO: remove (4.0)
712
+ if (x === HISTORY_KEY) {
713
+ if (!historyValue) {
714
+ return [this];
715
+ }
716
+ var subHistoryValue = nestedPath(this.path, 'states')(historyValue).current;
717
+ if (typeof subHistoryValue === 'string') {
718
+ return this.states[subHistoryValue].getFromRelativePath(xs, historyValue);
719
+ }
720
+ return flatMap(Object.keys(subHistoryValue).map(function (key) {
721
+ return _this.states[key].getFromRelativePath(xs, historyValue);
722
+ }));
723
+ }
724
+ var childStateNode = this.getStateNode(x);
725
+ if (childStateNode.history) {
726
+ return childStateNode.resolveHistory(historyValue);
727
+ }
728
+ if (!this.states[x]) {
729
+ throw new Error("Child state '" + x + "' does not exist on '" + this.id + "'");
730
+ }
731
+ return this.states[x].getFromRelativePath(xs, historyValue);
732
+ };
733
+ StateNode.updateHistoryValue = function (hist, stateValue) {
734
+ function update(_hist, _sv) {
735
+ return mapValues(_hist.states, function (subHist, key) {
736
+ if (!subHist) {
737
+ return undefined;
738
+ }
739
+ var subStateValue = (typeof _sv === 'string' ? undefined : _sv[key]) ||
740
+ (subHist ? subHist.current : undefined);
741
+ if (!subStateValue) {
742
+ return undefined;
607
743
  }
608
- return subState.states[subPath];
609
- }, this);
744
+ return {
745
+ current: subStateValue,
746
+ states: update(subHist, subStateValue)
747
+ };
748
+ });
610
749
  }
611
- catch (e) {
612
- throw new Error("State '" + relativeStateId + " does not exist on machine '" + this.id + "'");
750
+ return {
751
+ current: stateValue,
752
+ states: update(hist, stateValue)
753
+ };
754
+ };
755
+ StateNode.prototype.historyValue = function (relativeStateValue) {
756
+ if (!Object.keys(this.states).length) {
757
+ return undefined;
758
+ }
759
+ return {
760
+ current: relativeStateValue || this.initialStateValue,
761
+ states: mapFilterValues(this.states, function (stateNode, key) {
762
+ if (!relativeStateValue) {
763
+ return stateNode.historyValue();
764
+ }
765
+ var subStateValue = typeof relativeStateValue === 'string'
766
+ ? undefined
767
+ : relativeStateValue[key];
768
+ return stateNode.historyValue(subStateValue || stateNode.initialStateValue);
769
+ }, function (stateNode) { return !stateNode.history; })
770
+ };
771
+ };
772
+ /**
773
+ * Resolves to the historical value(s) of the parent state node,
774
+ * represented by state nodes.
775
+ *
776
+ * @param historyValue
777
+ */
778
+ StateNode.prototype.resolveHistory = function (historyValue) {
779
+ var _this = this;
780
+ if (!this.history) {
781
+ return [this];
613
782
  }
783
+ var parent = this.parent;
784
+ if (!historyValue) {
785
+ return this.target
786
+ ? flatMap(toStatePaths(this.target).map(function (relativeChildPath) {
787
+ return parent.getFromRelativePath(relativeChildPath);
788
+ }))
789
+ : this.parent.initialStateNodes;
790
+ }
791
+ var subHistoryValue = nestedPath(parent.path, 'states')(historyValue).current;
792
+ if (typeof subHistoryValue === 'string') {
793
+ return [parent.getStateNode(subHistoryValue)];
794
+ }
795
+ return flatMap(toStatePaths(subHistoryValue).map(function (subStatePath) {
796
+ return _this.history === 'deep'
797
+ ? parent.getFromRelativePath(subStatePath)
798
+ : [parent.states[subStatePath[0]]];
799
+ }));
614
800
  };
615
801
  Object.defineProperty(StateNode.prototype, "events", {
616
802
  get: function () {
@@ -635,25 +821,44 @@ var StateNode = /** @class */ (function () {
635
821
  enumerable: true,
636
822
  configurable: true
637
823
  });
824
+ StateNode.prototype.formatTransition = function (targets, transitionConfig) {
825
+ var _this = this;
826
+ var internal = transitionConfig ? transitionConfig.internal : false;
827
+ // Format targets to their full string path
828
+ var formattedTargets = targets.map(function (target) {
829
+ var internalTarget = typeof target === 'string' && target[0] === _this.delimiter;
830
+ internal = internal || internalTarget;
831
+ // If internal target is defined on machine,
832
+ // do not include machine key on target
833
+ if (internalTarget && !_this.parent) {
834
+ return target.slice(1);
835
+ }
836
+ return internalTarget ? _this.key + target : target;
837
+ });
838
+ return __assign({}, transitionConfig, { target: formattedTargets, internal: internal });
839
+ };
638
840
  StateNode.prototype.formatTransitions = function (onConfig) {
841
+ var _this = this;
639
842
  return mapValues(onConfig, function (value) {
640
843
  if (value === undefined) {
641
844
  return [];
642
845
  }
643
846
  if (Array.isArray(value)) {
644
- return value;
847
+ return value.map(function (targetTransitionConfig) {
848
+ return _this.formatTransition([].concat(targetTransitionConfig.target), targetTransitionConfig);
849
+ });
645
850
  }
646
851
  if (typeof value === 'string') {
647
- return [{ target: value }];
852
+ return [_this.formatTransition([value])];
648
853
  }
649
854
  return Object.keys(value).map(function (target) {
650
- return __assign({ target: target }, value[target]);
855
+ return _this.formatTransition([target], value[target]);
651
856
  });
652
857
  });
653
858
  };
654
859
  return StateNode;
655
860
  }());
656
- export function Machine(config) {
657
- return new StateNode(config);
861
+ export function Machine(config, options) {
862
+ return new StateNode(config, options);
658
863
  }
659
864
  export { StateNode };