xstate 5.0.0-beta.35 → 5.0.0-beta.37
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/actions/dist/xstate-actions.cjs.js +3 -3
- package/actions/dist/xstate-actions.development.cjs.js +3 -3
- package/actions/dist/xstate-actions.development.esm.js +3 -3
- package/actions/dist/xstate-actions.esm.js +3 -3
- package/actions/dist/xstate-actions.umd.min.js +1 -1
- package/actions/dist/xstate-actions.umd.min.js.map +1 -1
- package/actors/dist/xstate-actors.cjs.js +1 -1
- package/actors/dist/xstate-actors.development.cjs.js +1 -1
- package/actors/dist/xstate-actors.development.esm.js +1 -1
- package/actors/dist/xstate-actors.esm.js +1 -1
- package/actors/dist/xstate-actors.umd.min.js +1 -1
- package/actors/dist/xstate-actors.umd.min.js.map +1 -1
- package/dist/declarations/src/State.d.ts +0 -1
- package/dist/declarations/src/stateUtils.d.ts +3 -3
- package/dist/declarations/src/typegenTypes.d.ts +2 -0
- package/dist/declarations/src/types.d.ts +1 -2
- package/dist/declarations/src/utils.d.ts +6 -6
- package/dist/declarations/src/waitFor.d.ts +1 -1
- package/dist/{interpreter-5c4e6634.development.esm.js → interpreter-1c52b23c.development.esm.js} +13 -23
- package/dist/{interpreter-de5217bc.esm.js → interpreter-8def682e.esm.js} +13 -23
- package/dist/{interpreter-69605bf0.cjs.js → interpreter-97aff8d2.cjs.js} +13 -23
- package/dist/{interpreter-d3567419.development.cjs.js → interpreter-e58ca48d.development.cjs.js} +13 -23
- package/dist/{raise-26e4d83c.development.cjs.js → raise-1fd59c65.development.cjs.js} +129 -102
- package/dist/{raise-511399cc.esm.js → raise-21c417c1.esm.js} +129 -102
- package/dist/{raise-0ff57677.cjs.js → raise-800296d7.cjs.js} +129 -102
- package/dist/{raise-cdcdf834.development.esm.js → raise-e342a840.development.esm.js} +129 -102
- package/dist/{send-19ffc568.cjs.js → send-4cc29786.cjs.js} +7 -4
- package/dist/{send-1de74f4d.development.esm.js → send-83ccc98b.development.esm.js} +7 -4
- package/dist/{send-211a2a94.esm.js → send-92854675.esm.js} +7 -4
- package/dist/{send-894c4b18.development.cjs.js → send-b309ef4e.development.cjs.js} +7 -4
- package/dist/xstate.cjs.js +11 -19
- package/dist/xstate.development.cjs.js +11 -19
- package/dist/xstate.development.esm.js +14 -22
- package/dist/xstate.esm.js +14 -22
- package/dist/xstate.umd.min.js +1 -1
- package/dist/xstate.umd.min.js.map +1 -1
- package/guards/dist/xstate-guards.cjs.js +2 -2
- package/guards/dist/xstate-guards.development.cjs.js +2 -2
- package/guards/dist/xstate-guards.development.esm.js +2 -2
- package/guards/dist/xstate-guards.esm.js +2 -2
- package/guards/dist/xstate-guards.umd.min.js +1 -1
- package/guards/dist/xstate-guards.umd.min.js.map +1 -1
- package/package.json +1 -1
|
@@ -28,22 +28,6 @@ class Mailbox {
|
|
|
28
28
|
this._last = this._current;
|
|
29
29
|
}
|
|
30
30
|
}
|
|
31
|
-
|
|
32
|
-
// TODO: rethink this design
|
|
33
|
-
prepend(event) {
|
|
34
|
-
if (!this._current) {
|
|
35
|
-
this.enqueue(event);
|
|
36
|
-
return;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
// we know that something is already queued up
|
|
40
|
-
// so the mailbox is already flushing or it's inactive
|
|
41
|
-
// therefore the only thing that we need to do is to reassign `this._current`
|
|
42
|
-
this._current = {
|
|
43
|
-
value: event,
|
|
44
|
-
next: this._current
|
|
45
|
-
};
|
|
46
|
-
}
|
|
47
31
|
enqueue(event) {
|
|
48
32
|
const enqueued = {
|
|
49
33
|
value: event,
|
|
@@ -66,11 +50,7 @@ class Mailbox {
|
|
|
66
50
|
// we assume here that this won't throw in a way that can affect this mailbox
|
|
67
51
|
const consumed = this._current;
|
|
68
52
|
this._process(consumed.value);
|
|
69
|
-
|
|
70
|
-
// so we need to be defensive here to avoid skipping over a prepended item
|
|
71
|
-
if (consumed === this._current) {
|
|
72
|
-
this._current = this._current.next;
|
|
73
|
-
}
|
|
53
|
+
this._current = consumed.next;
|
|
74
54
|
}
|
|
75
55
|
this._last = null;
|
|
76
56
|
}
|
|
@@ -331,9 +311,19 @@ function toObserver(nextHandler, errorHandler, completionHandler) {
|
|
|
331
311
|
};
|
|
332
312
|
}
|
|
333
313
|
function createInvokeId(stateNodeId, index) {
|
|
334
|
-
return `${stateNodeId}
|
|
314
|
+
return `${stateNodeId}[${index}]`;
|
|
335
315
|
}
|
|
336
|
-
function resolveReferencedActor(
|
|
316
|
+
function resolveReferencedActor(machine, src) {
|
|
317
|
+
if (src.startsWith('xstate#')) {
|
|
318
|
+
const [, indexStr] = src.match(/\[(\d+)\]$/);
|
|
319
|
+
const node = machine.getStateNodeById(src.slice(7, -(indexStr.length + 2)));
|
|
320
|
+
const invokeConfig = node.config.invoke;
|
|
321
|
+
return {
|
|
322
|
+
src: (Array.isArray(invokeConfig) ? invokeConfig[indexStr] : invokeConfig).src,
|
|
323
|
+
input: undefined
|
|
324
|
+
};
|
|
325
|
+
}
|
|
326
|
+
const referenced = machine.implementations.actors[src];
|
|
337
327
|
return referenced ? 'transition' in referenced ? {
|
|
338
328
|
src: referenced,
|
|
339
329
|
input: undefined
|
|
@@ -30,22 +30,6 @@ class Mailbox {
|
|
|
30
30
|
this._last = this._current;
|
|
31
31
|
}
|
|
32
32
|
}
|
|
33
|
-
|
|
34
|
-
// TODO: rethink this design
|
|
35
|
-
prepend(event) {
|
|
36
|
-
if (!this._current) {
|
|
37
|
-
this.enqueue(event);
|
|
38
|
-
return;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
// we know that something is already queued up
|
|
42
|
-
// so the mailbox is already flushing or it's inactive
|
|
43
|
-
// therefore the only thing that we need to do is to reassign `this._current`
|
|
44
|
-
this._current = {
|
|
45
|
-
value: event,
|
|
46
|
-
next: this._current
|
|
47
|
-
};
|
|
48
|
-
}
|
|
49
33
|
enqueue(event) {
|
|
50
34
|
const enqueued = {
|
|
51
35
|
value: event,
|
|
@@ -68,11 +52,7 @@ class Mailbox {
|
|
|
68
52
|
// we assume here that this won't throw in a way that can affect this mailbox
|
|
69
53
|
const consumed = this._current;
|
|
70
54
|
this._process(consumed.value);
|
|
71
|
-
|
|
72
|
-
// so we need to be defensive here to avoid skipping over a prepended item
|
|
73
|
-
if (consumed === this._current) {
|
|
74
|
-
this._current = this._current.next;
|
|
75
|
-
}
|
|
55
|
+
this._current = consumed.next;
|
|
76
56
|
}
|
|
77
57
|
this._last = null;
|
|
78
58
|
}
|
|
@@ -333,9 +313,19 @@ function toObserver(nextHandler, errorHandler, completionHandler) {
|
|
|
333
313
|
};
|
|
334
314
|
}
|
|
335
315
|
function createInvokeId(stateNodeId, index) {
|
|
336
|
-
return `${stateNodeId}
|
|
316
|
+
return `${stateNodeId}[${index}]`;
|
|
337
317
|
}
|
|
338
|
-
function resolveReferencedActor(
|
|
318
|
+
function resolveReferencedActor(machine, src) {
|
|
319
|
+
if (src.startsWith('xstate#')) {
|
|
320
|
+
const [, indexStr] = src.match(/\[(\d+)\]$/);
|
|
321
|
+
const node = machine.getStateNodeById(src.slice(7, -(indexStr.length + 2)));
|
|
322
|
+
const invokeConfig = node.config.invoke;
|
|
323
|
+
return {
|
|
324
|
+
src: (Array.isArray(invokeConfig) ? invokeConfig[indexStr] : invokeConfig).src,
|
|
325
|
+
input: undefined
|
|
326
|
+
};
|
|
327
|
+
}
|
|
328
|
+
const referenced = machine.implementations.actors[src];
|
|
339
329
|
return referenced ? 'transition' in referenced ? {
|
|
340
330
|
src: referenced,
|
|
341
331
|
input: undefined
|
package/dist/{interpreter-d3567419.development.cjs.js → interpreter-e58ca48d.development.cjs.js}
RENAMED
|
@@ -30,22 +30,6 @@ class Mailbox {
|
|
|
30
30
|
this._last = this._current;
|
|
31
31
|
}
|
|
32
32
|
}
|
|
33
|
-
|
|
34
|
-
// TODO: rethink this design
|
|
35
|
-
prepend(event) {
|
|
36
|
-
if (!this._current) {
|
|
37
|
-
this.enqueue(event);
|
|
38
|
-
return;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
// we know that something is already queued up
|
|
42
|
-
// so the mailbox is already flushing or it's inactive
|
|
43
|
-
// therefore the only thing that we need to do is to reassign `this._current`
|
|
44
|
-
this._current = {
|
|
45
|
-
value: event,
|
|
46
|
-
next: this._current
|
|
47
|
-
};
|
|
48
|
-
}
|
|
49
33
|
enqueue(event) {
|
|
50
34
|
const enqueued = {
|
|
51
35
|
value: event,
|
|
@@ -68,11 +52,7 @@ class Mailbox {
|
|
|
68
52
|
// we assume here that this won't throw in a way that can affect this mailbox
|
|
69
53
|
const consumed = this._current;
|
|
70
54
|
this._process(consumed.value);
|
|
71
|
-
|
|
72
|
-
// so we need to be defensive here to avoid skipping over a prepended item
|
|
73
|
-
if (consumed === this._current) {
|
|
74
|
-
this._current = this._current.next;
|
|
75
|
-
}
|
|
55
|
+
this._current = consumed.next;
|
|
76
56
|
}
|
|
77
57
|
this._last = null;
|
|
78
58
|
}
|
|
@@ -336,9 +316,19 @@ function toObserver(nextHandler, errorHandler, completionHandler) {
|
|
|
336
316
|
};
|
|
337
317
|
}
|
|
338
318
|
function createInvokeId(stateNodeId, index) {
|
|
339
|
-
return `${stateNodeId}
|
|
319
|
+
return `${stateNodeId}[${index}]`;
|
|
340
320
|
}
|
|
341
|
-
function resolveReferencedActor(
|
|
321
|
+
function resolveReferencedActor(machine, src) {
|
|
322
|
+
if (src.startsWith('xstate#')) {
|
|
323
|
+
const [, indexStr] = src.match(/\[(\d+)\]$/);
|
|
324
|
+
const node = machine.getStateNodeById(src.slice(7, -(indexStr.length + 2)));
|
|
325
|
+
const invokeConfig = node.config.invoke;
|
|
326
|
+
return {
|
|
327
|
+
src: (Array.isArray(invokeConfig) ? invokeConfig[indexStr] : invokeConfig).src,
|
|
328
|
+
input: undefined
|
|
329
|
+
};
|
|
330
|
+
}
|
|
331
|
+
const referenced = machine.implementations.actors[src];
|
|
342
332
|
return referenced ? 'transition' in referenced ? {
|
|
343
333
|
src: referenced,
|
|
344
334
|
input: undefined
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var interpreter = require('./interpreter-
|
|
3
|
+
var interpreter = require('./interpreter-e58ca48d.development.cjs.js');
|
|
4
4
|
|
|
5
5
|
const cache = new WeakMap();
|
|
6
6
|
function memo(object, key, fn) {
|
|
@@ -52,14 +52,17 @@ function resolveInvoke(actorContext, state, actionArgs, {
|
|
|
52
52
|
input,
|
|
53
53
|
syncSnapshot
|
|
54
54
|
}) {
|
|
55
|
-
const referenced = interpreter.resolveReferencedActor(state.machine
|
|
55
|
+
const referenced = typeof src === 'string' ? interpreter.resolveReferencedActor(state.machine, src) : {
|
|
56
|
+
src,
|
|
57
|
+
input: undefined
|
|
58
|
+
};
|
|
56
59
|
let actorRef;
|
|
57
60
|
if (referenced) {
|
|
58
61
|
// TODO: inline `input: undefined` should win over the referenced one
|
|
59
62
|
const configuredInput = input || referenced.input;
|
|
60
63
|
actorRef = interpreter.createActor(referenced.src, {
|
|
61
64
|
id,
|
|
62
|
-
src,
|
|
65
|
+
src: typeof src === 'string' ? src : undefined,
|
|
63
66
|
parent: actorContext?.self,
|
|
64
67
|
systemId,
|
|
65
68
|
input: typeof configuredInput === 'function' ? configuredInput({
|
|
@@ -162,11 +165,16 @@ function executeStop(actorContext, actorRef) {
|
|
|
162
165
|
if (!actorRef) {
|
|
163
166
|
return;
|
|
164
167
|
}
|
|
168
|
+
// this allows us to prevent an actor from being started if it gets stopped within the same macrostep
|
|
169
|
+
// this can happen, for example, when the invoking state is being exited immediately by an always transition
|
|
165
170
|
if (actorRef.status !== interpreter.ActorStatus.Running) {
|
|
166
171
|
actorContext.stopChild(actorRef);
|
|
167
172
|
return;
|
|
168
173
|
}
|
|
169
|
-
//
|
|
174
|
+
// stopping a child enqueues a stop event in the child actor's mailbox
|
|
175
|
+
// we need for all of the already enqueued events to be processed before we stop the child
|
|
176
|
+
// the parent itself might want to send some events to a child (for example from exit actions on the invoking state)
|
|
177
|
+
// and we don't want to ignore those events
|
|
170
178
|
actorContext.defer(() => {
|
|
171
179
|
actorContext.stopChild(actorRef);
|
|
172
180
|
});
|
|
@@ -193,7 +201,8 @@ function checkStateIn(state, _, {
|
|
|
193
201
|
stateValue
|
|
194
202
|
}) {
|
|
195
203
|
if (typeof stateValue === 'string' && isStateId(stateValue)) {
|
|
196
|
-
|
|
204
|
+
const target = state.machine.getStateNodeById(stateValue);
|
|
205
|
+
return state.configuration.some(sn => sn === target);
|
|
197
206
|
}
|
|
198
207
|
return state.matches(stateValue);
|
|
199
208
|
}
|
|
@@ -305,6 +314,9 @@ function getChildren(stateNode) {
|
|
|
305
314
|
}
|
|
306
315
|
function getProperAncestors(stateNode, toStateNode) {
|
|
307
316
|
const ancestors = [];
|
|
317
|
+
if (toStateNode === stateNode) {
|
|
318
|
+
return ancestors;
|
|
319
|
+
}
|
|
308
320
|
|
|
309
321
|
// add all ancestors
|
|
310
322
|
let m = stateNode.parent;
|
|
@@ -592,12 +604,14 @@ function resolveTarget(stateNode, targets) {
|
|
|
592
604
|
}
|
|
593
605
|
});
|
|
594
606
|
}
|
|
595
|
-
function
|
|
607
|
+
function resolveHistoryDefaultTransition(stateNode) {
|
|
596
608
|
const normalizedTarget = interpreter.normalizeTarget(stateNode.config.target);
|
|
597
609
|
if (!normalizedTarget) {
|
|
598
|
-
return stateNode.parent.initial
|
|
610
|
+
return stateNode.parent.initial;
|
|
599
611
|
}
|
|
600
|
-
return
|
|
612
|
+
return {
|
|
613
|
+
target: normalizedTarget.map(t => typeof t === 'string' ? getStateNodeByPath(stateNode.parent, t) : t)
|
|
614
|
+
};
|
|
601
615
|
}
|
|
602
616
|
function isHistoryNode(stateNode) {
|
|
603
617
|
return stateNode.type === 'history';
|
|
@@ -824,9 +838,7 @@ function getEffectiveTargetStates(transition, historyValue) {
|
|
|
824
838
|
targets.add(node);
|
|
825
839
|
}
|
|
826
840
|
} else {
|
|
827
|
-
for (const node of getEffectiveTargetStates({
|
|
828
|
-
target: resolveHistoryTarget(targetNode)
|
|
829
|
-
}, historyValue)) {
|
|
841
|
+
for (const node of getEffectiveTargetStates(resolveHistoryDefaultTransition(targetNode), historyValue)) {
|
|
830
842
|
targets.add(node);
|
|
831
843
|
}
|
|
832
844
|
}
|
|
@@ -839,9 +851,9 @@ function getEffectiveTargetStates(transition, historyValue) {
|
|
|
839
851
|
function getTransitionDomain(transition, historyValue) {
|
|
840
852
|
const targetStates = getEffectiveTargetStates(transition, historyValue);
|
|
841
853
|
if (!targetStates) {
|
|
842
|
-
return
|
|
854
|
+
return;
|
|
843
855
|
}
|
|
844
|
-
if (!transition.reenter &&
|
|
856
|
+
if (!transition.reenter && targetStates.every(target => target === transition.source || isDescendant(target, transition.source))) {
|
|
845
857
|
return transition.source;
|
|
846
858
|
}
|
|
847
859
|
const lcca = findLCCA(targetStates.concat(transition.source));
|
|
@@ -852,6 +864,9 @@ function computeExitSet(transitions, configuration, historyValue) {
|
|
|
852
864
|
for (const t of transitions) {
|
|
853
865
|
if (t.target?.length) {
|
|
854
866
|
const domain = getTransitionDomain(t, historyValue);
|
|
867
|
+
if (t.reenter && t.source === domain) {
|
|
868
|
+
statesToExit.add(domain);
|
|
869
|
+
}
|
|
855
870
|
for (const stateNode of configuration) {
|
|
856
871
|
if (isDescendant(stateNode, domain)) {
|
|
857
872
|
statesToExit.add(stateNode);
|
|
@@ -861,6 +876,17 @@ function computeExitSet(transitions, configuration, historyValue) {
|
|
|
861
876
|
}
|
|
862
877
|
return [...statesToExit];
|
|
863
878
|
}
|
|
879
|
+
function areConfigurationsEqual(previousConfiguration, nextConfigurationSet) {
|
|
880
|
+
if (previousConfiguration.length !== nextConfigurationSet.size) {
|
|
881
|
+
return false;
|
|
882
|
+
}
|
|
883
|
+
for (const node of previousConfiguration) {
|
|
884
|
+
if (!nextConfigurationSet.has(node)) {
|
|
885
|
+
return false;
|
|
886
|
+
}
|
|
887
|
+
}
|
|
888
|
+
return true;
|
|
889
|
+
}
|
|
864
890
|
|
|
865
891
|
/**
|
|
866
892
|
* https://www.w3.org/TR/scxml/#microstepProcedure
|
|
@@ -870,50 +896,36 @@ function computeExitSet(transitions, configuration, historyValue) {
|
|
|
870
896
|
* @param currentState
|
|
871
897
|
* @param mutConfiguration
|
|
872
898
|
*/
|
|
873
|
-
|
|
874
|
-
function microstep(transitions, currentState, actorCtx, event, isInitial) {
|
|
875
|
-
const mutConfiguration = new Set(currentState.configuration);
|
|
899
|
+
function microstep(transitions, currentState, actorCtx, event, isInitial, internalQueue) {
|
|
876
900
|
if (!transitions.length) {
|
|
877
901
|
return currentState;
|
|
878
902
|
}
|
|
879
|
-
const
|
|
880
|
-
|
|
881
|
-
value: {} // TODO: make optional
|
|
882
|
-
});
|
|
883
|
-
}
|
|
884
|
-
|
|
885
|
-
function microstepProcedure(transitions, currentState, mutConfiguration, event, actorCtx, isInitial) {
|
|
886
|
-
const historyValue = {
|
|
887
|
-
...currentState.historyValue
|
|
888
|
-
};
|
|
903
|
+
const mutConfiguration = new Set(currentState.configuration);
|
|
904
|
+
let historyValue = currentState.historyValue;
|
|
889
905
|
const filteredTransitions = removeConflictingTransitions(transitions, mutConfiguration, historyValue);
|
|
890
|
-
|
|
891
|
-
// TODO: this `cloneState` is really just a hack to prevent infinite loops
|
|
892
|
-
// we need to take another look at how internal queue is managed
|
|
893
|
-
let nextState = cloneState(currentState, {
|
|
894
|
-
_internalQueue: []
|
|
895
|
-
});
|
|
906
|
+
let nextState = currentState;
|
|
896
907
|
|
|
897
908
|
// Exit states
|
|
898
909
|
if (!isInitial) {
|
|
899
|
-
nextState = exitStates(nextState, event, actorCtx, filteredTransitions, mutConfiguration, historyValue);
|
|
910
|
+
[nextState, historyValue] = exitStates(nextState, event, actorCtx, filteredTransitions, mutConfiguration, historyValue, internalQueue);
|
|
900
911
|
}
|
|
901
912
|
|
|
902
913
|
// Execute transition content
|
|
903
|
-
nextState = resolveActionsAndContext(nextState, event, actorCtx, filteredTransitions.flatMap(t => t.actions));
|
|
914
|
+
nextState = resolveActionsAndContext(nextState, event, actorCtx, filteredTransitions.flatMap(t => t.actions), internalQueue);
|
|
904
915
|
|
|
905
916
|
// Enter states
|
|
906
917
|
nextState = enterStates(nextState, event, actorCtx, filteredTransitions, mutConfiguration, internalQueue, historyValue, isInitial);
|
|
907
918
|
const nextConfiguration = [...mutConfiguration];
|
|
908
919
|
if (nextState.status === 'done') {
|
|
909
|
-
nextState = resolveActionsAndContext(nextState, event, actorCtx, nextConfiguration.sort((a, b) => b.order - a.order).flatMap(state => state.exit));
|
|
920
|
+
nextState = resolveActionsAndContext(nextState, event, actorCtx, nextConfiguration.sort((a, b) => b.order - a.order).flatMap(state => state.exit), internalQueue);
|
|
910
921
|
}
|
|
911
922
|
try {
|
|
912
|
-
|
|
923
|
+
if (historyValue === currentState.historyValue && areConfigurationsEqual(currentState.configuration, mutConfiguration)) {
|
|
924
|
+
return nextState;
|
|
925
|
+
}
|
|
913
926
|
return cloneState(nextState, {
|
|
914
927
|
configuration: nextConfiguration,
|
|
915
|
-
historyValue
|
|
916
|
-
_internalQueue: internalQueue
|
|
928
|
+
historyValue
|
|
917
929
|
});
|
|
918
930
|
} catch (e) {
|
|
919
931
|
// TODO: Refactor this once proper error handling is implemented.
|
|
@@ -931,6 +943,9 @@ function getMachineOutput(state, event, actorCtx, rootNode, rootCompletionNode)
|
|
|
931
943
|
function enterStates(currentState, event, actorCtx, filteredTransitions, mutConfiguration, internalQueue, historyValue, isInitial) {
|
|
932
944
|
let nextState = currentState;
|
|
933
945
|
const statesToEnter = new Set();
|
|
946
|
+
// those are states that were directly targeted or indirectly targeted by the explicit target
|
|
947
|
+
// in other words, those are states for which initial actions should be executed
|
|
948
|
+
// when we target `#deep_child` initial actions of its ancestors shouldn't be executed
|
|
934
949
|
const statesForDefaultEntry = new Set();
|
|
935
950
|
computeEntrySet(filteredTransitions, historyValue, statesForDefaultEntry, statesToEnter);
|
|
936
951
|
|
|
@@ -952,23 +967,19 @@ function enterStates(currentState, event, actorCtx, filteredTransitions, mutConf
|
|
|
952
967
|
const initialActions = stateNodeToEnter.initial.actions;
|
|
953
968
|
actions.push(...initialActions);
|
|
954
969
|
}
|
|
955
|
-
nextState = resolveActionsAndContext(nextState, event, actorCtx, actions, stateNodeToEnter.invoke.map(invokeDef => invokeDef.id));
|
|
970
|
+
nextState = resolveActionsAndContext(nextState, event, actorCtx, actions, internalQueue, stateNodeToEnter.invoke.map(invokeDef => invokeDef.id));
|
|
956
971
|
if (stateNodeToEnter.type === 'final') {
|
|
957
972
|
const parent = stateNodeToEnter.parent;
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
completedNodes.add(parent);
|
|
962
|
-
let rootCompletionNode = parent?.type === 'parallel' ? parent : stateNodeToEnter;
|
|
963
|
-
let ancestorMarker = parent?.parent;
|
|
964
|
-
if (ancestorMarker) {
|
|
973
|
+
let ancestorMarker = parent?.type === 'parallel' ? parent : parent?.parent;
|
|
974
|
+
let rootCompletionNode = ancestorMarker || stateNodeToEnter;
|
|
975
|
+
if (parent?.type === 'compound') {
|
|
965
976
|
internalQueue.push(interpreter.createDoneStateEvent(parent.id, stateNodeToEnter.output ? interpreter.resolveOutput(stateNodeToEnter.output, nextState.context, event, actorCtx.self) : undefined));
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
977
|
+
}
|
|
978
|
+
while (ancestorMarker?.type === 'parallel' && !completedNodes.has(ancestorMarker) && isInFinalState(mutConfiguration, ancestorMarker)) {
|
|
979
|
+
completedNodes.add(ancestorMarker);
|
|
980
|
+
internalQueue.push(interpreter.createDoneStateEvent(ancestorMarker.id));
|
|
981
|
+
rootCompletionNode = ancestorMarker;
|
|
982
|
+
ancestorMarker = ancestorMarker.parent;
|
|
972
983
|
}
|
|
973
984
|
if (ancestorMarker) {
|
|
974
985
|
continue;
|
|
@@ -983,13 +994,24 @@ function enterStates(currentState, event, actorCtx, filteredTransitions, mutConf
|
|
|
983
994
|
}
|
|
984
995
|
function computeEntrySet(transitions, historyValue, statesForDefaultEntry, statesToEnter) {
|
|
985
996
|
for (const t of transitions) {
|
|
997
|
+
const domain = getTransitionDomain(t, historyValue);
|
|
986
998
|
for (const s of t.target || []) {
|
|
999
|
+
if (!isHistoryNode(s) && (
|
|
1000
|
+
// if the target is different than the source then it will *definitely* be entered
|
|
1001
|
+
t.source !== s ||
|
|
1002
|
+
// we know that the domain can't lie within the source
|
|
1003
|
+
// if it's different than the source then it's outside of it and it means that the target has to be entered as well
|
|
1004
|
+
t.source !== domain ||
|
|
1005
|
+
// reentering transitions always enter the target, even if it's the source itself
|
|
1006
|
+
t.reenter)) {
|
|
1007
|
+
statesToEnter.add(s);
|
|
1008
|
+
statesForDefaultEntry.add(s);
|
|
1009
|
+
}
|
|
987
1010
|
addDescendantStatesToEnter(s, historyValue, statesForDefaultEntry, statesToEnter);
|
|
988
1011
|
}
|
|
989
|
-
const ancestor = getTransitionDomain(t, historyValue);
|
|
990
1012
|
const targetStates = getEffectiveTargetStates(t, historyValue);
|
|
991
1013
|
for (const s of targetStates) {
|
|
992
|
-
addAncestorStatesToEnter(s,
|
|
1014
|
+
addAncestorStatesToEnter(s, domain, statesToEnter, historyValue, statesForDefaultEntry);
|
|
993
1015
|
}
|
|
994
1016
|
}
|
|
995
1017
|
}
|
|
@@ -998,37 +1020,42 @@ function addDescendantStatesToEnter(stateNode, historyValue, statesForDefaultEnt
|
|
|
998
1020
|
if (historyValue[stateNode.id]) {
|
|
999
1021
|
const historyStateNodes = historyValue[stateNode.id];
|
|
1000
1022
|
for (const s of historyStateNodes) {
|
|
1023
|
+
statesToEnter.add(s);
|
|
1001
1024
|
addDescendantStatesToEnter(s, historyValue, statesForDefaultEntry, statesToEnter);
|
|
1002
1025
|
}
|
|
1003
1026
|
for (const s of historyStateNodes) {
|
|
1004
1027
|
addAncestorStatesToEnter(s, stateNode.parent, statesToEnter, historyValue, statesForDefaultEntry);
|
|
1005
|
-
for (const stateForDefaultEntry of statesForDefaultEntry) {
|
|
1006
|
-
statesForDefaultEntry.add(stateForDefaultEntry);
|
|
1007
|
-
}
|
|
1008
1028
|
}
|
|
1009
1029
|
} else {
|
|
1010
|
-
const
|
|
1011
|
-
for (const s of
|
|
1030
|
+
const historyDefaultTransition = resolveHistoryDefaultTransition(stateNode);
|
|
1031
|
+
for (const s of historyDefaultTransition.target) {
|
|
1032
|
+
statesToEnter.add(s);
|
|
1033
|
+
if (historyDefaultTransition === stateNode.parent?.initial) {
|
|
1034
|
+
statesForDefaultEntry.add(stateNode.parent);
|
|
1035
|
+
}
|
|
1012
1036
|
addDescendantStatesToEnter(s, historyValue, statesForDefaultEntry, statesToEnter);
|
|
1013
1037
|
}
|
|
1014
|
-
for (const s of
|
|
1038
|
+
for (const s of historyDefaultTransition.target) {
|
|
1015
1039
|
addAncestorStatesToEnter(s, stateNode, statesToEnter, historyValue, statesForDefaultEntry);
|
|
1016
|
-
for (const stateForDefaultEntry of statesForDefaultEntry) {
|
|
1017
|
-
statesForDefaultEntry.add(stateForDefaultEntry);
|
|
1018
|
-
}
|
|
1019
1040
|
}
|
|
1020
1041
|
}
|
|
1021
1042
|
} else {
|
|
1022
|
-
statesToEnter.add(stateNode);
|
|
1023
1043
|
if (stateNode.type === 'compound') {
|
|
1024
|
-
statesForDefaultEntry.add(stateNode);
|
|
1025
1044
|
const [initialState] = stateNode.initial.target;
|
|
1045
|
+
if (!isHistoryNode(initialState)) {
|
|
1046
|
+
statesToEnter.add(initialState);
|
|
1047
|
+
statesForDefaultEntry.add(initialState);
|
|
1048
|
+
}
|
|
1026
1049
|
addDescendantStatesToEnter(initialState, historyValue, statesForDefaultEntry, statesToEnter);
|
|
1027
1050
|
addAncestorStatesToEnter(initialState, stateNode, statesToEnter, historyValue, statesForDefaultEntry);
|
|
1028
1051
|
} else {
|
|
1029
1052
|
if (stateNode.type === 'parallel') {
|
|
1030
1053
|
for (const child of getChildren(stateNode).filter(sn => !isHistoryNode(sn))) {
|
|
1031
1054
|
if (![...statesToEnter].some(s => isDescendant(s, child))) {
|
|
1055
|
+
if (!isHistoryNode(child)) {
|
|
1056
|
+
statesToEnter.add(child);
|
|
1057
|
+
statesForDefaultEntry.add(child);
|
|
1058
|
+
}
|
|
1032
1059
|
addDescendantStatesToEnter(child, historyValue, statesForDefaultEntry, statesToEnter);
|
|
1033
1060
|
}
|
|
1034
1061
|
}
|
|
@@ -1043,16 +1070,18 @@ function addAncestorStatesToEnter(stateNode, toStateNode, statesToEnter, history
|
|
|
1043
1070
|
if (anc.type === 'parallel') {
|
|
1044
1071
|
for (const child of getChildren(anc).filter(sn => !isHistoryNode(sn))) {
|
|
1045
1072
|
if (![...statesToEnter].some(s => isDescendant(s, child))) {
|
|
1073
|
+
statesToEnter.add(child);
|
|
1046
1074
|
addDescendantStatesToEnter(child, historyValue, statesForDefaultEntry, statesToEnter);
|
|
1047
1075
|
}
|
|
1048
1076
|
}
|
|
1049
1077
|
}
|
|
1050
1078
|
}
|
|
1051
1079
|
}
|
|
1052
|
-
function exitStates(currentState, event, actorCtx, transitions, mutConfiguration, historyValue) {
|
|
1080
|
+
function exitStates(currentState, event, actorCtx, transitions, mutConfiguration, historyValue, internalQueue) {
|
|
1053
1081
|
let nextState = currentState;
|
|
1054
1082
|
const statesToExit = computeExitSet(transitions, mutConfiguration, historyValue);
|
|
1055
1083
|
statesToExit.sort((a, b) => b.order - a.order);
|
|
1084
|
+
let changedHistory;
|
|
1056
1085
|
|
|
1057
1086
|
// From SCXML algorithm: https://www.w3.org/TR/scxml/#exitStates
|
|
1058
1087
|
for (const exitStateNode of statesToExit) {
|
|
@@ -1065,14 +1094,17 @@ function exitStates(currentState, event, actorCtx, transitions, mutConfiguration
|
|
|
1065
1094
|
return sn.parent === exitStateNode;
|
|
1066
1095
|
};
|
|
1067
1096
|
}
|
|
1068
|
-
|
|
1097
|
+
changedHistory ??= {
|
|
1098
|
+
...historyValue
|
|
1099
|
+
};
|
|
1100
|
+
changedHistory[historyNode.id] = Array.from(mutConfiguration).filter(predicate);
|
|
1069
1101
|
}
|
|
1070
1102
|
}
|
|
1071
1103
|
for (const s of statesToExit) {
|
|
1072
|
-
nextState = resolveActionsAndContext(nextState, event, actorCtx, [...s.exit, ...s.invoke.map(def => stop(def.id))]);
|
|
1104
|
+
nextState = resolveActionsAndContext(nextState, event, actorCtx, [...s.exit, ...s.invoke.map(def => stop(def.id))], internalQueue);
|
|
1073
1105
|
mutConfiguration.delete(s);
|
|
1074
1106
|
}
|
|
1075
|
-
return nextState;
|
|
1107
|
+
return [nextState, changedHistory || historyValue];
|
|
1076
1108
|
}
|
|
1077
1109
|
function resolveActionsAndContextWorker(currentState, event, actorCtx, actions, extra, retries) {
|
|
1078
1110
|
const {
|
|
@@ -1137,9 +1169,10 @@ function resolveActionsAndContextWorker(currentState, event, actorCtx, actions,
|
|
|
1137
1169
|
}
|
|
1138
1170
|
return intermediateState;
|
|
1139
1171
|
}
|
|
1140
|
-
function resolveActionsAndContext(currentState, event, actorCtx, actions, deferredActorIds) {
|
|
1172
|
+
function resolveActionsAndContext(currentState, event, actorCtx, actions, internalQueue, deferredActorIds) {
|
|
1141
1173
|
const retries = deferredActorIds ? [] : undefined;
|
|
1142
|
-
const nextState = resolveActionsAndContextWorker(currentState, event, actorCtx, actions,
|
|
1174
|
+
const nextState = resolveActionsAndContextWorker(currentState, event, actorCtx, actions, {
|
|
1175
|
+
internalQueue,
|
|
1143
1176
|
deferredActorIds
|
|
1144
1177
|
}, retries);
|
|
1145
1178
|
retries?.forEach(([builtinAction, params]) => {
|
|
@@ -1147,7 +1180,7 @@ function resolveActionsAndContext(currentState, event, actorCtx, actions, deferr
|
|
|
1147
1180
|
});
|
|
1148
1181
|
return nextState;
|
|
1149
1182
|
}
|
|
1150
|
-
function macrostep(state, event, actorCtx) {
|
|
1183
|
+
function macrostep(state, event, actorCtx, internalQueue = []) {
|
|
1151
1184
|
if (event.type === interpreter.WILDCARD) {
|
|
1152
1185
|
throw new Error(`An event cannot have the wildcard type ('${interpreter.WILDCARD}')`);
|
|
1153
1186
|
}
|
|
@@ -1156,7 +1189,7 @@ function macrostep(state, event, actorCtx) {
|
|
|
1156
1189
|
|
|
1157
1190
|
// Handle stop event
|
|
1158
1191
|
if (event.type === interpreter.XSTATE_STOP) {
|
|
1159
|
-
nextState =
|
|
1192
|
+
nextState = stopChildren(nextState, event, actorCtx);
|
|
1160
1193
|
states.push(nextState);
|
|
1161
1194
|
return {
|
|
1162
1195
|
state: nextState,
|
|
@@ -1169,44 +1202,37 @@ function macrostep(state, event, actorCtx) {
|
|
|
1169
1202
|
// Determine the next state based on the next microstep
|
|
1170
1203
|
if (nextEvent.type !== interpreter.XSTATE_INIT) {
|
|
1171
1204
|
const transitions = selectTransitions(nextEvent, nextState);
|
|
1172
|
-
nextState = microstep(transitions, state, actorCtx, nextEvent, false);
|
|
1205
|
+
nextState = microstep(transitions, state, actorCtx, nextEvent, false, internalQueue);
|
|
1173
1206
|
states.push(nextState);
|
|
1174
1207
|
}
|
|
1208
|
+
let shouldSelectEventlessTransitions = true;
|
|
1175
1209
|
while (nextState.status === 'active') {
|
|
1176
|
-
let enabledTransitions = selectEventlessTransitions(nextState, nextEvent);
|
|
1210
|
+
let enabledTransitions = shouldSelectEventlessTransitions ? selectEventlessTransitions(nextState, nextEvent) : [];
|
|
1211
|
+
|
|
1212
|
+
// eventless transitions should always be selected after selecting *regular* transitions
|
|
1213
|
+
// by assigning `undefined` to `previousState` we ensure that `shouldSelectEventlessTransitions` gets always computed to true in such a case
|
|
1214
|
+
const previousState = enabledTransitions.length ? nextState : undefined;
|
|
1177
1215
|
if (!enabledTransitions.length) {
|
|
1178
|
-
if (!
|
|
1216
|
+
if (!internalQueue.length) {
|
|
1179
1217
|
break;
|
|
1180
|
-
} else {
|
|
1181
|
-
nextEvent = nextState._internalQueue[0];
|
|
1182
|
-
const transitions = selectTransitions(nextEvent, nextState);
|
|
1183
|
-
nextState = microstep(transitions, nextState, actorCtx, nextEvent, false);
|
|
1184
|
-
nextState._internalQueue.shift();
|
|
1185
|
-
states.push(nextState);
|
|
1186
1218
|
}
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
states.push(nextState);
|
|
1219
|
+
nextEvent = internalQueue.shift();
|
|
1220
|
+
enabledTransitions = selectTransitions(nextEvent, nextState);
|
|
1190
1221
|
}
|
|
1222
|
+
nextState = microstep(enabledTransitions, nextState, actorCtx, nextEvent, false, internalQueue);
|
|
1223
|
+
shouldSelectEventlessTransitions = nextState !== previousState;
|
|
1224
|
+
states.push(nextState);
|
|
1191
1225
|
}
|
|
1192
1226
|
if (nextState.status !== 'active') {
|
|
1193
|
-
|
|
1194
|
-
stopStep(nextEvent, nextState, actorCtx);
|
|
1227
|
+
stopChildren(nextState, nextEvent, actorCtx);
|
|
1195
1228
|
}
|
|
1196
1229
|
return {
|
|
1197
1230
|
state: nextState,
|
|
1198
1231
|
microstates: states
|
|
1199
1232
|
};
|
|
1200
1233
|
}
|
|
1201
|
-
function
|
|
1202
|
-
|
|
1203
|
-
for (const stateNode of nextState.configuration.sort((a, b) => b.order - a.order)) {
|
|
1204
|
-
actions.push(...stateNode.exit);
|
|
1205
|
-
}
|
|
1206
|
-
for (const child of Object.values(nextState.children)) {
|
|
1207
|
-
actions.push(stop(child));
|
|
1208
|
-
}
|
|
1209
|
-
return resolveActionsAndContext(nextState, event, actorCtx, actions);
|
|
1234
|
+
function stopChildren(nextState, event, actorCtx) {
|
|
1235
|
+
return resolveActionsAndContext(nextState, event, actorCtx, Object.values(nextState.children).map(child => stop(child)), []);
|
|
1210
1236
|
}
|
|
1211
1237
|
function selectTransitions(event, nextState) {
|
|
1212
1238
|
return nextState.machine.getTransitionData(nextState, event);
|
|
@@ -1215,7 +1241,7 @@ function selectEventlessTransitions(nextState, event) {
|
|
|
1215
1241
|
const enabledTransitionSet = new Set();
|
|
1216
1242
|
const atomicStates = nextState.configuration.filter(isAtomicStateNode);
|
|
1217
1243
|
for (const stateNode of atomicStates) {
|
|
1218
|
-
loop: for (const s of [stateNode].concat(getProperAncestors(stateNode,
|
|
1244
|
+
loop: for (const s of [stateNode].concat(getProperAncestors(stateNode, undefined))) {
|
|
1219
1245
|
if (!s.always) {
|
|
1220
1246
|
continue;
|
|
1221
1247
|
}
|
|
@@ -1312,11 +1338,9 @@ class State {
|
|
|
1312
1338
|
this.error = void 0;
|
|
1313
1339
|
this.context = void 0;
|
|
1314
1340
|
this.historyValue = {};
|
|
1315
|
-
this._internalQueue = void 0;
|
|
1316
1341
|
this.configuration = void 0;
|
|
1317
1342
|
this.children = void 0;
|
|
1318
1343
|
this.context = config.context;
|
|
1319
|
-
this._internalQueue = config._internalQueue ?? [];
|
|
1320
1344
|
this.historyValue = config.historyValue || {};
|
|
1321
1345
|
this.matches = this.matches.bind(this);
|
|
1322
1346
|
this.toStrings = this.toStrings.bind(this);
|
|
@@ -1471,6 +1495,8 @@ function resolveRaise(_, state, args, {
|
|
|
1471
1495
|
event: eventOrExpr,
|
|
1472
1496
|
id,
|
|
1473
1497
|
delay
|
|
1498
|
+
}, {
|
|
1499
|
+
internalQueue
|
|
1474
1500
|
}) {
|
|
1475
1501
|
const delaysMap = state.machine.implementations.delays;
|
|
1476
1502
|
if (typeof eventOrExpr === 'string') {
|
|
@@ -1484,9 +1510,10 @@ function resolveRaise(_, state, args, {
|
|
|
1484
1510
|
} else {
|
|
1485
1511
|
resolvedDelay = typeof delay === 'function' ? delay(args) : delay;
|
|
1486
1512
|
}
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
}
|
|
1513
|
+
if (typeof resolvedDelay !== 'number') {
|
|
1514
|
+
internalQueue.push(resolvedEvent);
|
|
1515
|
+
}
|
|
1516
|
+
return [state, {
|
|
1490
1517
|
event: resolvedEvent,
|
|
1491
1518
|
id,
|
|
1492
1519
|
delay: resolvedDelay
|