xstate 3.3.2 → 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.
- package/README.md +21 -1
- package/dist/xstate.js +1 -1
- package/dist/xstate.utils.js +1 -1
- package/es/State.d.ts +8 -7
- package/es/State.js +3 -2
- package/es/StateNode.d.ts +35 -20
- package/es/StateNode.js +119 -46
- package/es/graph.d.ts +4 -1
- package/es/graph.js +5 -4
- package/es/patterns.js +1 -1
- package/es/scxml.d.ts +2 -1
- package/es/scxml.js +9 -8
- package/es/types.d.ts +4 -7
- package/es/utils.d.ts +6 -1
- package/es/utils.js +17 -1
- package/lib/State.d.ts +8 -7
- package/lib/State.js +3 -2
- package/lib/StateNode.d.ts +35 -20
- package/lib/StateNode.js +118 -45
- package/lib/graph.d.ts +4 -1
- package/lib/graph.js +5 -3
- package/lib/patterns.js +1 -1
- package/lib/scxml.d.ts +2 -1
- package/lib/scxml.js +9 -8
- package/lib/types.d.ts +4 -7
- package/lib/utils.d.ts +6 -1
- package/lib/utils.js +18 -1
- package/package.json +3 -3
- package/src/State.ts +4 -1
- package/src/StateNode.ts +221 -89
- package/src/graph.ts +14 -12
- package/src/scxml.ts +52 -49
- package/src/types.ts +6 -9
- package/src/utils.ts +25 -4
- package/test/activities.test.ts +28 -1
- package/test/deterministic.test.ts +1 -1
- package/test/history.test.ts +139 -0
- package/test/invalid.test.ts +48 -0
- package/test/parallel.test.ts +229 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "xstate",
|
|
3
|
-
"version": "3.3.
|
|
3
|
+
"version": "3.3.3",
|
|
4
4
|
"description": "Simple JavaScript Finite State Machines and Statecharts",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"module": "es/index.js",
|
|
@@ -36,11 +36,11 @@
|
|
|
36
36
|
"@types/node": "^8.9.4",
|
|
37
37
|
"chai": "^4.1.2",
|
|
38
38
|
"mocha": "^3.5.3",
|
|
39
|
-
"prettier": "^1.
|
|
39
|
+
"prettier": "^1.13.5",
|
|
40
40
|
"scxml-test-framework": "^1.0.2",
|
|
41
41
|
"ts-node": "^3.3.0",
|
|
42
42
|
"tslint": "^5.7.0",
|
|
43
|
-
"typescript": "^2.
|
|
43
|
+
"typescript": "^2.9.2",
|
|
44
44
|
"webpack": "^3.5.6",
|
|
45
45
|
"xml-js": "^1.6.2"
|
|
46
46
|
},
|
package/src/State.ts
CHANGED
|
@@ -3,7 +3,8 @@ import {
|
|
|
3
3
|
ActivityMap,
|
|
4
4
|
EventObject,
|
|
5
5
|
Action,
|
|
6
|
-
StateInterface
|
|
6
|
+
StateInterface,
|
|
7
|
+
HistoryValue
|
|
7
8
|
} from './types';
|
|
8
9
|
import { STATE_DELIMITER, EMPTY_ACTIVITY_MAP } from './constants';
|
|
9
10
|
|
|
@@ -22,6 +23,7 @@ export class State implements StateInterface {
|
|
|
22
23
|
}
|
|
23
24
|
return new State(
|
|
24
25
|
stateValue.value,
|
|
26
|
+
stateValue.historyValue,
|
|
25
27
|
stateValue.history,
|
|
26
28
|
[],
|
|
27
29
|
stateValue.activities
|
|
@@ -33,6 +35,7 @@ export class State implements StateInterface {
|
|
|
33
35
|
|
|
34
36
|
constructor(
|
|
35
37
|
public value: StateValue,
|
|
38
|
+
public historyValue?: HistoryValue | undefined,
|
|
36
39
|
public history?: State,
|
|
37
40
|
public actions: Action[] = [],
|
|
38
41
|
public activities: ActivityMap = EMPTY_ACTIVITY_MAP,
|
package/src/StateNode.ts
CHANGED
|
@@ -9,7 +9,8 @@ import {
|
|
|
9
9
|
pathToStateValue,
|
|
10
10
|
getActionType,
|
|
11
11
|
flatMap,
|
|
12
|
-
mapFilterValues
|
|
12
|
+
mapFilterValues,
|
|
13
|
+
nestedPath
|
|
13
14
|
} from './utils';
|
|
14
15
|
import {
|
|
15
16
|
Event,
|
|
@@ -20,7 +21,6 @@ import {
|
|
|
20
21
|
StandardMachine,
|
|
21
22
|
ParallelMachine,
|
|
22
23
|
SimpleOrCompoundStateNodeConfig,
|
|
23
|
-
MachineConfig,
|
|
24
24
|
ParallelMachineConfig,
|
|
25
25
|
EventType,
|
|
26
26
|
StandardMachineConfig,
|
|
@@ -30,14 +30,15 @@ import {
|
|
|
30
30
|
ConditionalTransitionConfig,
|
|
31
31
|
EntryExitStates,
|
|
32
32
|
TargetTransitionConfig,
|
|
33
|
-
|
|
33
|
+
StateTransition,
|
|
34
34
|
ActionObject,
|
|
35
35
|
StateValueMap,
|
|
36
36
|
MachineOptions,
|
|
37
37
|
Condition,
|
|
38
38
|
ConditionPredicate,
|
|
39
39
|
EventObject,
|
|
40
|
-
HistoryStateNodeConfig
|
|
40
|
+
HistoryStateNodeConfig,
|
|
41
|
+
HistoryValue
|
|
41
42
|
} from './types';
|
|
42
43
|
import { matchesState } from './matchesState';
|
|
43
44
|
import { State } from './State';
|
|
@@ -48,16 +49,11 @@ const HISTORY_KEY = '$history';
|
|
|
48
49
|
const NULL_EVENT = '';
|
|
49
50
|
const STATE_IDENTIFIER = '#';
|
|
50
51
|
const isStateId = (str: string) => str[0] === STATE_IDENTIFIER;
|
|
51
|
-
// const emptyActions: ActionMap = Object.freeze({
|
|
52
|
-
// onEntry: [],
|
|
53
|
-
// onExit: [],
|
|
54
|
-
// actions: []
|
|
55
|
-
// });
|
|
56
52
|
const defaultOptions: MachineOptions = {
|
|
57
53
|
guards: {}
|
|
58
54
|
};
|
|
59
55
|
|
|
60
|
-
class StateNode
|
|
56
|
+
class StateNode {
|
|
61
57
|
public key: string;
|
|
62
58
|
public id: string;
|
|
63
59
|
public path: string[];
|
|
@@ -106,21 +102,21 @@ class StateNode implements StateNode {
|
|
|
106
102
|
this.initial = config.initial;
|
|
107
103
|
this.parallel = !!config.parallel;
|
|
108
104
|
this.states = (config.states
|
|
109
|
-
? mapValues<
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
105
|
+
? mapValues<SimpleOrCompoundStateNodeConfig, StateNode>(
|
|
106
|
+
config.states,
|
|
107
|
+
(stateConfig, key) => {
|
|
108
|
+
const stateNode = new StateNode({
|
|
109
|
+
...stateConfig,
|
|
110
|
+
key,
|
|
111
|
+
parent: this
|
|
112
|
+
});
|
|
113
|
+
Object.assign(this.idMap, {
|
|
114
|
+
[stateNode.id]: stateNode,
|
|
115
|
+
...stateNode.idMap
|
|
116
|
+
});
|
|
117
|
+
return stateNode;
|
|
118
|
+
}
|
|
119
|
+
)
|
|
124
120
|
: {}) as Record<string, StateNode>;
|
|
125
121
|
|
|
126
122
|
// History config
|
|
@@ -177,12 +173,12 @@ class StateNode implements StateNode {
|
|
|
177
173
|
|
|
178
174
|
return this.events.indexOf(eventType) !== -1;
|
|
179
175
|
}
|
|
180
|
-
|
|
176
|
+
private _transitionLeafNode(
|
|
181
177
|
stateValue: string,
|
|
182
178
|
state: State,
|
|
183
179
|
event: Event,
|
|
184
180
|
extendedState?: any
|
|
185
|
-
):
|
|
181
|
+
): StateTransition {
|
|
186
182
|
const stateNode = this.getStateNode(stateValue);
|
|
187
183
|
const next = stateNode._next(state, event, extendedState);
|
|
188
184
|
|
|
@@ -201,7 +197,7 @@ class StateNode implements StateNode {
|
|
|
201
197
|
stateNode,
|
|
202
198
|
...(entryExitStates
|
|
203
199
|
? Array.from(entryExitStates.exit)
|
|
204
|
-
: [] as StateNode[])
|
|
200
|
+
: ([] as StateNode[]))
|
|
205
201
|
])
|
|
206
202
|
},
|
|
207
203
|
actions,
|
|
@@ -211,12 +207,12 @@ class StateNode implements StateNode {
|
|
|
211
207
|
|
|
212
208
|
return next;
|
|
213
209
|
}
|
|
214
|
-
|
|
210
|
+
private _transitionHierarchicalNode(
|
|
215
211
|
stateValue: StateValueMap,
|
|
216
212
|
state: State,
|
|
217
213
|
event: Event,
|
|
218
214
|
extendedState?: any
|
|
219
|
-
):
|
|
215
|
+
): StateTransition {
|
|
220
216
|
const subStateKeys = Object.keys(stateValue);
|
|
221
217
|
|
|
222
218
|
const stateNode = this.getStateNode(subStateKeys[0]);
|
|
@@ -245,7 +241,7 @@ class StateNode implements StateNode {
|
|
|
245
241
|
stateNode,
|
|
246
242
|
...(entryExitStates
|
|
247
243
|
? Array.from(entryExitStates.exit)
|
|
248
|
-
: [] as StateNode[])
|
|
244
|
+
: ([] as StateNode[]))
|
|
249
245
|
])
|
|
250
246
|
},
|
|
251
247
|
actions,
|
|
@@ -255,14 +251,15 @@ class StateNode implements StateNode {
|
|
|
255
251
|
|
|
256
252
|
return next;
|
|
257
253
|
}
|
|
258
|
-
|
|
254
|
+
private _transitionOrthogonalNode(
|
|
259
255
|
stateValue: StateValueMap,
|
|
260
256
|
state: State,
|
|
261
257
|
event: Event,
|
|
262
258
|
extendedState?: any
|
|
263
|
-
):
|
|
259
|
+
): StateTransition {
|
|
264
260
|
const noTransitionKeys: string[] = [];
|
|
265
|
-
const transitionMap: Record<string,
|
|
261
|
+
const transitionMap: Record<string, StateTransition> = {};
|
|
262
|
+
|
|
266
263
|
Object.keys(stateValue).forEach(subStateKey => {
|
|
267
264
|
const subStateValue = stateValue[subStateKey];
|
|
268
265
|
|
|
@@ -351,12 +348,11 @@ class StateNode implements StateNode {
|
|
|
351
348
|
const allResolvedPaths = flatMap(
|
|
352
349
|
Object.keys(transitionMap).map(key => {
|
|
353
350
|
const transition = transitionMap[key];
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
return transition.paths;
|
|
351
|
+
const value = transition.value || state.value;
|
|
352
|
+
|
|
353
|
+
return toStatePaths(path(this.path)(value)[key]).map(statePath =>
|
|
354
|
+
this.path.concat(key, statePath)
|
|
355
|
+
);
|
|
360
356
|
})
|
|
361
357
|
);
|
|
362
358
|
|
|
@@ -404,7 +400,7 @@ class StateNode implements StateNode {
|
|
|
404
400
|
state: State,
|
|
405
401
|
event: Event,
|
|
406
402
|
extendedState?: any
|
|
407
|
-
):
|
|
403
|
+
): StateTransition {
|
|
408
404
|
// leaf node
|
|
409
405
|
if (typeof stateValue === 'string') {
|
|
410
406
|
return this._transitionLeafNode(stateValue, state, event, extendedState);
|
|
@@ -428,11 +424,11 @@ class StateNode implements StateNode {
|
|
|
428
424
|
extendedState
|
|
429
425
|
);
|
|
430
426
|
}
|
|
431
|
-
|
|
427
|
+
private _next(
|
|
432
428
|
state: State,
|
|
433
429
|
event: Event,
|
|
434
430
|
extendedState?: any
|
|
435
|
-
):
|
|
431
|
+
): StateTransition {
|
|
436
432
|
const eventType = getEventType(event);
|
|
437
433
|
const candidates = this.on[eventType];
|
|
438
434
|
const actions: Action[] = this.transient
|
|
@@ -497,7 +493,7 @@ class StateNode implements StateNode {
|
|
|
497
493
|
|
|
498
494
|
const nextStateNodes = flatMap(
|
|
499
495
|
nextStateStrings.map(str =>
|
|
500
|
-
this.getRelativeStateNodes(str, state.
|
|
496
|
+
this.getRelativeStateNodes(str, state.historyValue)
|
|
501
497
|
)
|
|
502
498
|
);
|
|
503
499
|
|
|
@@ -529,7 +525,9 @@ class StateNode implements StateNode {
|
|
|
529
525
|
pathsToStateValue(
|
|
530
526
|
flatMap(
|
|
531
527
|
nextStateStrings.map(str =>
|
|
532
|
-
this.getRelativeStateNodes(str, state.
|
|
528
|
+
this.getRelativeStateNodes(str, state.historyValue).map(
|
|
529
|
+
s => s.path
|
|
530
|
+
)
|
|
533
531
|
)
|
|
534
532
|
)
|
|
535
533
|
)
|
|
@@ -539,7 +537,7 @@ class StateNode implements StateNode {
|
|
|
539
537
|
paths: nextStatePaths
|
|
540
538
|
};
|
|
541
539
|
}
|
|
542
|
-
|
|
540
|
+
private _getEntryExitStates(
|
|
543
541
|
nextStateNode: StateNode,
|
|
544
542
|
internal: boolean
|
|
545
543
|
): EntryExitStates {
|
|
@@ -602,8 +600,9 @@ class StateNode implements StateNode {
|
|
|
602
600
|
if (typeof condition === 'string') {
|
|
603
601
|
if (!this.machine.options.guards[condition]) {
|
|
604
602
|
throw new Error(
|
|
605
|
-
`String condition '${condition}' is not defined on machine '${
|
|
606
|
-
.machine.id
|
|
603
|
+
`String condition '${condition}' is not defined on machine '${
|
|
604
|
+
this.machine.id
|
|
605
|
+
}'`
|
|
607
606
|
);
|
|
608
607
|
}
|
|
609
608
|
|
|
@@ -614,7 +613,7 @@ class StateNode implements StateNode {
|
|
|
614
613
|
|
|
615
614
|
return condFn(extendedState, eventObject, interimState);
|
|
616
615
|
}
|
|
617
|
-
private _getActions(transition:
|
|
616
|
+
private _getActions(transition: StateTransition): Action[] {
|
|
618
617
|
const entryExitActions = {
|
|
619
618
|
entry: transition.entryExitStates
|
|
620
619
|
? flatMap(
|
|
@@ -646,7 +645,7 @@ class StateNode implements StateNode {
|
|
|
646
645
|
}
|
|
647
646
|
private _getActivities(
|
|
648
647
|
state: State,
|
|
649
|
-
transition:
|
|
648
|
+
transition: StateTransition
|
|
650
649
|
): ActivityMap {
|
|
651
650
|
if (!transition.entryExitStates) {
|
|
652
651
|
return {};
|
|
@@ -654,23 +653,23 @@ class StateNode implements StateNode {
|
|
|
654
653
|
|
|
655
654
|
const activityMap = { ...state.activities };
|
|
656
655
|
|
|
657
|
-
Array.from(transition.entryExitStates.
|
|
656
|
+
Array.from(transition.entryExitStates.exit).forEach(stateNode => {
|
|
658
657
|
if (!stateNode.activities) {
|
|
659
658
|
return; // TODO: fixme
|
|
660
659
|
}
|
|
661
660
|
|
|
662
661
|
stateNode.activities.forEach(activity => {
|
|
663
|
-
activityMap[getActionType(activity)] =
|
|
662
|
+
activityMap[getActionType(activity)] = false;
|
|
664
663
|
});
|
|
665
664
|
});
|
|
666
665
|
|
|
667
|
-
Array.from(transition.entryExitStates.
|
|
666
|
+
Array.from(transition.entryExitStates.entry).forEach(stateNode => {
|
|
668
667
|
if (!stateNode.activities) {
|
|
669
668
|
return; // TODO: fixme
|
|
670
669
|
}
|
|
671
670
|
|
|
672
671
|
stateNode.activities.forEach(activity => {
|
|
673
|
-
activityMap[getActionType(activity)] =
|
|
672
|
+
activityMap[getActionType(activity)] = true;
|
|
674
673
|
});
|
|
675
674
|
});
|
|
676
675
|
|
|
@@ -684,7 +683,9 @@ class StateNode implements StateNode {
|
|
|
684
683
|
const resolvedStateValue =
|
|
685
684
|
typeof state === 'string'
|
|
686
685
|
? this.resolve(pathToStateValue(this.getResolvedPath(state)))
|
|
687
|
-
: state instanceof State
|
|
686
|
+
: state instanceof State
|
|
687
|
+
? state
|
|
688
|
+
: this.resolve(state);
|
|
688
689
|
|
|
689
690
|
const eventType = getEventType(event);
|
|
690
691
|
|
|
@@ -698,6 +699,15 @@ class StateNode implements StateNode {
|
|
|
698
699
|
|
|
699
700
|
const currentState = State.from(resolvedStateValue);
|
|
700
701
|
|
|
702
|
+
const historyValue =
|
|
703
|
+
resolvedStateValue instanceof State
|
|
704
|
+
? resolvedStateValue.historyValue
|
|
705
|
+
? resolvedStateValue.historyValue
|
|
706
|
+
: (this.machine.historyValue(
|
|
707
|
+
resolvedStateValue.value
|
|
708
|
+
) as HistoryValue)
|
|
709
|
+
: (this.machine.historyValue(resolvedStateValue) as HistoryValue);
|
|
710
|
+
|
|
701
711
|
const stateTransition = this._transition(
|
|
702
712
|
currentState.value,
|
|
703
713
|
currentState,
|
|
@@ -741,12 +751,10 @@ class StateNode implements StateNode {
|
|
|
741
751
|
data[stateNode.id] = stateNode.data;
|
|
742
752
|
});
|
|
743
753
|
|
|
744
|
-
// Dispose of previous histories to prevent memory leaks
|
|
745
|
-
delete currentState.history;
|
|
746
|
-
|
|
747
754
|
const nextState = stateTransition.value
|
|
748
755
|
? new State(
|
|
749
756
|
stateTransition.value,
|
|
757
|
+
StateNode.updateHistoryValue(historyValue, stateTransition.value),
|
|
750
758
|
currentState,
|
|
751
759
|
nonEventActions,
|
|
752
760
|
activities,
|
|
@@ -760,6 +768,9 @@ class StateNode implements StateNode {
|
|
|
760
768
|
return State.inert(currentState);
|
|
761
769
|
}
|
|
762
770
|
|
|
771
|
+
// Dispose of previous histories to prevent memory leaks
|
|
772
|
+
delete currentState.history;
|
|
773
|
+
|
|
763
774
|
let maybeNextState = nextState;
|
|
764
775
|
while (raisedEvents.length) {
|
|
765
776
|
const currentActions = maybeNextState.actions;
|
|
@@ -791,10 +802,11 @@ class StateNode implements StateNode {
|
|
|
791
802
|
}
|
|
792
803
|
|
|
793
804
|
throw new Error(
|
|
794
|
-
`State node '${stateNode.id}' shares parent '${
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
805
|
+
`State node '${stateNode.id}' shares parent '${
|
|
806
|
+
marker.parent.id
|
|
807
|
+
}' with state node '${visitedParents
|
|
808
|
+
.get(marker.parent)!
|
|
809
|
+
.map(a => a.id)}'`
|
|
798
810
|
);
|
|
799
811
|
}
|
|
800
812
|
|
|
@@ -815,8 +827,9 @@ class StateNode implements StateNode {
|
|
|
815
827
|
|
|
816
828
|
if (!this.states) {
|
|
817
829
|
throw new Error(
|
|
818
|
-
`Unable to retrieve child state '${stateKey}' from '${
|
|
819
|
-
.id
|
|
830
|
+
`Unable to retrieve child state '${stateKey}' from '${
|
|
831
|
+
this.id
|
|
832
|
+
}'; no child states exist.`
|
|
820
833
|
);
|
|
821
834
|
}
|
|
822
835
|
|
|
@@ -843,6 +856,16 @@ class StateNode implements StateNode {
|
|
|
843
856
|
|
|
844
857
|
return stateNode;
|
|
845
858
|
}
|
|
859
|
+
public getStateNodeByPath(statePath: string | string[]): StateNode {
|
|
860
|
+
const arrayStatePath = toStatePath(statePath, this.delimiter);
|
|
861
|
+
let currentStateNode: StateNode = this;
|
|
862
|
+
while (arrayStatePath.length) {
|
|
863
|
+
const key = arrayStatePath.shift()!;
|
|
864
|
+
currentStateNode = currentStateNode.getStateNode(key);
|
|
865
|
+
}
|
|
866
|
+
|
|
867
|
+
return currentStateNode;
|
|
868
|
+
}
|
|
846
869
|
private resolve(stateValue: StateValue): StateValue {
|
|
847
870
|
if (typeof stateValue === 'string') {
|
|
848
871
|
const subStateNode = this.getStateNode(stateValue);
|
|
@@ -951,7 +974,34 @@ class StateNode implements StateNode {
|
|
|
951
974
|
}
|
|
952
975
|
});
|
|
953
976
|
|
|
954
|
-
|
|
977
|
+
// TODO: deduplicate - DRY (from this.transition())
|
|
978
|
+
const raisedEvents = actions.filter(
|
|
979
|
+
action =>
|
|
980
|
+
typeof action === 'object' &&
|
|
981
|
+
(action.type === actionTypes.raise || action.type === actionTypes.null)
|
|
982
|
+
) as ActionObject[];
|
|
983
|
+
|
|
984
|
+
const initialState = new State(
|
|
985
|
+
initialStateValue,
|
|
986
|
+
undefined,
|
|
987
|
+
undefined,
|
|
988
|
+
actions,
|
|
989
|
+
activityMap
|
|
990
|
+
);
|
|
991
|
+
|
|
992
|
+
let maybeNextState = initialState;
|
|
993
|
+
while (raisedEvents.length) {
|
|
994
|
+
const currentActions = maybeNextState.actions;
|
|
995
|
+
const raisedEvent = raisedEvents.shift()!;
|
|
996
|
+
maybeNextState = this.transition(
|
|
997
|
+
maybeNextState,
|
|
998
|
+
raisedEvent.type === actionTypes.null ? NULL_EVENT : raisedEvent.event,
|
|
999
|
+
undefined // TODO: consider initial state given external state
|
|
1000
|
+
);
|
|
1001
|
+
maybeNextState.actions.unshift(...currentActions);
|
|
1002
|
+
}
|
|
1003
|
+
|
|
1004
|
+
return maybeNextState;
|
|
955
1005
|
}
|
|
956
1006
|
public get target(): StateValue | undefined {
|
|
957
1007
|
let target;
|
|
@@ -995,10 +1045,9 @@ class StateNode implements StateNode {
|
|
|
995
1045
|
*/
|
|
996
1046
|
public getRelativeStateNodes(
|
|
997
1047
|
relativeStateId: string | string[],
|
|
998
|
-
|
|
1048
|
+
historyValue?: HistoryValue,
|
|
999
1049
|
resolve: boolean = true
|
|
1000
1050
|
): StateNode[] {
|
|
1001
|
-
const historyValue = history ? history.value : undefined;
|
|
1002
1051
|
if (typeof relativeStateId === 'string' && isStateId(relativeStateId)) {
|
|
1003
1052
|
const unresolvedStateNode = this.getStateNodeById(relativeStateId);
|
|
1004
1053
|
|
|
@@ -1039,9 +1088,15 @@ class StateNode implements StateNode {
|
|
|
1039
1088
|
)
|
|
1040
1089
|
);
|
|
1041
1090
|
}
|
|
1091
|
+
/**
|
|
1092
|
+
* Retrieves state nodes from a relative path to this state node.
|
|
1093
|
+
*
|
|
1094
|
+
* @param relativePath The relative path from this state node
|
|
1095
|
+
* @param historyValue
|
|
1096
|
+
*/
|
|
1042
1097
|
public getFromRelativePath(
|
|
1043
1098
|
relativePath: string[],
|
|
1044
|
-
historyValue?:
|
|
1099
|
+
historyValue?: HistoryValue
|
|
1045
1100
|
): StateNode[] {
|
|
1046
1101
|
if (!relativePath.length) {
|
|
1047
1102
|
return [this];
|
|
@@ -1060,7 +1115,10 @@ class StateNode implements StateNode {
|
|
|
1060
1115
|
if (!historyValue) {
|
|
1061
1116
|
return [this];
|
|
1062
1117
|
}
|
|
1063
|
-
|
|
1118
|
+
|
|
1119
|
+
const subHistoryValue = nestedPath<HistoryValue>(this.path, 'states')(
|
|
1120
|
+
historyValue
|
|
1121
|
+
).current;
|
|
1064
1122
|
|
|
1065
1123
|
if (typeof subHistoryValue === 'string') {
|
|
1066
1124
|
return this.states[subHistoryValue].getFromRelativePath(
|
|
@@ -1070,7 +1128,7 @@ class StateNode implements StateNode {
|
|
|
1070
1128
|
}
|
|
1071
1129
|
|
|
1072
1130
|
return flatMap(
|
|
1073
|
-
Object.keys(subHistoryValue).map(key => {
|
|
1131
|
+
Object.keys(subHistoryValue!).map(key => {
|
|
1074
1132
|
return this.states[key].getFromRelativePath(xs, historyValue);
|
|
1075
1133
|
})
|
|
1076
1134
|
);
|
|
@@ -1088,7 +1146,73 @@ class StateNode implements StateNode {
|
|
|
1088
1146
|
|
|
1089
1147
|
return this.states[x].getFromRelativePath(xs, historyValue);
|
|
1090
1148
|
}
|
|
1091
|
-
|
|
1149
|
+
public static updateHistoryValue(
|
|
1150
|
+
hist: HistoryValue,
|
|
1151
|
+
stateValue: StateValue
|
|
1152
|
+
): HistoryValue {
|
|
1153
|
+
function update(
|
|
1154
|
+
_hist: HistoryValue,
|
|
1155
|
+
_sv: StateValue
|
|
1156
|
+
): Record<string, HistoryValue | undefined> {
|
|
1157
|
+
return mapValues(_hist.states, (subHist, key) => {
|
|
1158
|
+
if (!subHist) {
|
|
1159
|
+
return undefined;
|
|
1160
|
+
}
|
|
1161
|
+
const subStateValue =
|
|
1162
|
+
(typeof _sv === 'string' ? undefined : _sv[key]) ||
|
|
1163
|
+
(subHist ? subHist.current : undefined);
|
|
1164
|
+
|
|
1165
|
+
if (!subStateValue) {
|
|
1166
|
+
return undefined;
|
|
1167
|
+
}
|
|
1168
|
+
|
|
1169
|
+
return {
|
|
1170
|
+
current: subStateValue,
|
|
1171
|
+
states: update(subHist, subStateValue)
|
|
1172
|
+
};
|
|
1173
|
+
});
|
|
1174
|
+
}
|
|
1175
|
+
return {
|
|
1176
|
+
current: stateValue,
|
|
1177
|
+
states: update(hist, stateValue)
|
|
1178
|
+
};
|
|
1179
|
+
}
|
|
1180
|
+
public historyValue(
|
|
1181
|
+
relativeStateValue?: StateValue | undefined
|
|
1182
|
+
): HistoryValue | undefined {
|
|
1183
|
+
if (!Object.keys(this.states).length) {
|
|
1184
|
+
return undefined;
|
|
1185
|
+
}
|
|
1186
|
+
|
|
1187
|
+
return {
|
|
1188
|
+
current: relativeStateValue || this.initialStateValue,
|
|
1189
|
+
states: mapFilterValues(
|
|
1190
|
+
this.states,
|
|
1191
|
+
(stateNode, key) => {
|
|
1192
|
+
if (!relativeStateValue) {
|
|
1193
|
+
return stateNode.historyValue();
|
|
1194
|
+
}
|
|
1195
|
+
|
|
1196
|
+
const subStateValue =
|
|
1197
|
+
typeof relativeStateValue === 'string'
|
|
1198
|
+
? undefined
|
|
1199
|
+
: relativeStateValue[key];
|
|
1200
|
+
|
|
1201
|
+
return stateNode.historyValue(
|
|
1202
|
+
subStateValue || stateNode.initialStateValue
|
|
1203
|
+
);
|
|
1204
|
+
},
|
|
1205
|
+
stateNode => !stateNode.history
|
|
1206
|
+
)
|
|
1207
|
+
};
|
|
1208
|
+
}
|
|
1209
|
+
/**
|
|
1210
|
+
* Resolves to the historical value(s) of the parent state node,
|
|
1211
|
+
* represented by state nodes.
|
|
1212
|
+
*
|
|
1213
|
+
* @param historyValue
|
|
1214
|
+
*/
|
|
1215
|
+
private resolveHistory(historyValue?: HistoryValue): StateNode[] {
|
|
1092
1216
|
if (!this.history) {
|
|
1093
1217
|
return [this];
|
|
1094
1218
|
}
|
|
@@ -1099,25 +1223,27 @@ class StateNode implements StateNode {
|
|
|
1099
1223
|
return this.target
|
|
1100
1224
|
? flatMap(
|
|
1101
1225
|
toStatePaths(this.target).map(relativeChildPath =>
|
|
1102
|
-
parent.getFromRelativePath(relativeChildPath
|
|
1226
|
+
parent.getFromRelativePath(relativeChildPath)
|
|
1103
1227
|
)
|
|
1104
1228
|
)
|
|
1105
1229
|
: this.parent!.initialStateNodes;
|
|
1106
|
-
}
|
|
1107
|
-
const subHistoryValue = path(parent.path)(historyValue);
|
|
1230
|
+
}
|
|
1108
1231
|
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1232
|
+
const subHistoryValue = nestedPath<HistoryValue>(parent.path, 'states')(
|
|
1233
|
+
historyValue
|
|
1234
|
+
).current;
|
|
1112
1235
|
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
return this.history === 'deep'
|
|
1116
|
-
? parent.getFromRelativePath(subStatePath)
|
|
1117
|
-
: [parent.states[subStatePath[0]]];
|
|
1118
|
-
})
|
|
1119
|
-
);
|
|
1236
|
+
if (typeof subHistoryValue === 'string') {
|
|
1237
|
+
return [parent.getStateNode(subHistoryValue)];
|
|
1120
1238
|
}
|
|
1239
|
+
|
|
1240
|
+
return flatMap(
|
|
1241
|
+
toStatePaths(subHistoryValue!).map(subStatePath => {
|
|
1242
|
+
return this.history === 'deep'
|
|
1243
|
+
? parent.getFromRelativePath(subStatePath)
|
|
1244
|
+
: [parent.states[subStatePath[0]]];
|
|
1245
|
+
})
|
|
1246
|
+
);
|
|
1121
1247
|
}
|
|
1122
1248
|
get events(): EventType[] {
|
|
1123
1249
|
if (this.__cache.events) {
|
|
@@ -1194,11 +1320,17 @@ class StateNode implements StateNode {
|
|
|
1194
1320
|
}
|
|
1195
1321
|
}
|
|
1196
1322
|
|
|
1197
|
-
export function Machine
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1323
|
+
export function Machine<
|
|
1324
|
+
T extends StandardMachineConfig | ParallelMachineConfig
|
|
1325
|
+
>(
|
|
1326
|
+
config: T,
|
|
1327
|
+
options?: MachineOptions
|
|
1328
|
+
): T extends ParallelMachineConfig
|
|
1329
|
+
? ParallelMachine
|
|
1330
|
+
: T extends StandardMachineConfig ? StandardMachine : never {
|
|
1331
|
+
return new StateNode(config, options) as T extends ParallelMachineConfig
|
|
1332
|
+
? ParallelMachine
|
|
1333
|
+
: T extends StandardMachineConfig ? StandardMachine : never;
|
|
1202
1334
|
}
|
|
1203
1335
|
|
|
1204
1336
|
export { StateNode };
|
package/src/graph.ts
CHANGED
|
@@ -16,20 +16,21 @@ const EMPTY_MAP = {};
|
|
|
16
16
|
|
|
17
17
|
export function getNodes(node: StateNode): StateNode[] {
|
|
18
18
|
const { states } = node;
|
|
19
|
-
const nodes = Object.keys(
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
19
|
+
const nodes = Object.keys(states).reduce(
|
|
20
|
+
(accNodes: StateNode[], stateKey) => {
|
|
21
|
+
const subState = states[stateKey];
|
|
22
|
+
const subNodes = getNodes(states[stateKey]);
|
|
23
|
+
|
|
24
|
+
accNodes.push(subState, ...subNodes);
|
|
25
|
+
return accNodes;
|
|
26
|
+
},
|
|
27
|
+
[]
|
|
28
|
+
);
|
|
28
29
|
|
|
29
30
|
return nodes;
|
|
30
31
|
}
|
|
31
32
|
|
|
32
|
-
function getEventEdges(node: StateNode, event: string): Edge[] {
|
|
33
|
+
export function getEventEdges(node: StateNode, event: string): Edge[] {
|
|
33
34
|
const transitions = node.on[event]!;
|
|
34
35
|
|
|
35
36
|
return flatMap(
|
|
@@ -55,10 +56,11 @@ function getEventEdges(node: StateNode, event: string): Edge[] {
|
|
|
55
56
|
);
|
|
56
57
|
}
|
|
57
58
|
|
|
58
|
-
export function getEdges(node: StateNode): Edge[] {
|
|
59
|
+
export function getEdges(node: StateNode, options?: { deep: boolean }): Edge[] {
|
|
60
|
+
const { deep = true } = options || {};
|
|
59
61
|
const edges: Edge[] = [];
|
|
60
62
|
|
|
61
|
-
if (node.states) {
|
|
63
|
+
if (node.states && deep) {
|
|
62
64
|
Object.keys(node.states).forEach(stateKey => {
|
|
63
65
|
edges.push(...getEdges(node.states[stateKey]));
|
|
64
66
|
});
|