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