xstate 5.0.0-beta.36 → 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.
Files changed (41) hide show
  1. package/actions/dist/xstate-actions.cjs.js +3 -3
  2. package/actions/dist/xstate-actions.development.cjs.js +3 -3
  3. package/actions/dist/xstate-actions.development.esm.js +3 -3
  4. package/actions/dist/xstate-actions.esm.js +3 -3
  5. package/actions/dist/xstate-actions.umd.min.js +1 -1
  6. package/actions/dist/xstate-actions.umd.min.js.map +1 -1
  7. package/actors/dist/xstate-actors.cjs.js +1 -1
  8. package/actors/dist/xstate-actors.development.cjs.js +1 -1
  9. package/actors/dist/xstate-actors.development.esm.js +1 -1
  10. package/actors/dist/xstate-actors.esm.js +1 -1
  11. package/actors/dist/xstate-actors.umd.min.js +1 -1
  12. package/actors/dist/xstate-actors.umd.min.js.map +1 -1
  13. package/dist/declarations/src/typegenTypes.d.ts +2 -0
  14. package/dist/declarations/src/types.d.ts +1 -1
  15. package/dist/declarations/src/utils.d.ts +6 -6
  16. package/dist/declarations/src/waitFor.d.ts +1 -1
  17. package/dist/{interpreter-5c4e6634.development.esm.js → interpreter-1c52b23c.development.esm.js} +13 -23
  18. package/dist/{interpreter-de5217bc.esm.js → interpreter-8def682e.esm.js} +13 -23
  19. package/dist/{interpreter-69605bf0.cjs.js → interpreter-97aff8d2.cjs.js} +13 -23
  20. package/dist/{interpreter-d3567419.development.cjs.js → interpreter-e58ca48d.development.cjs.js} +13 -23
  21. package/dist/{raise-106ea558.development.cjs.js → raise-1fd59c65.development.cjs.js} +71 -40
  22. package/dist/{raise-ffe1014a.esm.js → raise-21c417c1.esm.js} +71 -40
  23. package/dist/{raise-c51b81a3.cjs.js → raise-800296d7.cjs.js} +71 -40
  24. package/dist/{raise-5b7ad3b7.development.esm.js → raise-e342a840.development.esm.js} +71 -40
  25. package/dist/{send-778692de.cjs.js → send-4cc29786.cjs.js} +6 -3
  26. package/dist/{send-e93554d6.development.esm.js → send-83ccc98b.development.esm.js} +6 -3
  27. package/dist/{send-0a7aa74e.esm.js → send-92854675.esm.js} +6 -3
  28. package/dist/{send-25e70bd4.development.cjs.js → send-b309ef4e.development.cjs.js} +6 -3
  29. package/dist/xstate.cjs.js +5 -13
  30. package/dist/xstate.development.cjs.js +5 -13
  31. package/dist/xstate.development.esm.js +8 -16
  32. package/dist/xstate.esm.js +8 -16
  33. package/dist/xstate.umd.min.js +1 -1
  34. package/dist/xstate.umd.min.js.map +1 -1
  35. package/guards/dist/xstate-guards.cjs.js +2 -2
  36. package/guards/dist/xstate-guards.development.cjs.js +2 -2
  37. package/guards/dist/xstate-guards.development.esm.js +2 -2
  38. package/guards/dist/xstate-guards.esm.js +2 -2
  39. package/guards/dist/xstate-guards.umd.min.js +1 -1
  40. package/guards/dist/xstate-guards.umd.min.js.map +1 -1
  41. package/package.json +1 -1
@@ -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
- // something could have been prepended in the meantime
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}:invocation[${index}]`;
316
+ return `${stateNodeId}[${index}]`;
337
317
  }
338
- function resolveReferencedActor(referenced) {
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
@@ -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
- // something could have been prepended in the meantime
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}:invocation[${index}]`;
319
+ return `${stateNodeId}[${index}]`;
340
320
  }
341
- function resolveReferencedActor(referenced) {
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-d3567419.development.cjs.js');
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.implementations.actors[src]);
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
- // TODO: recheck why this one has to be deferred
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
- return state.configuration.some(sn => sn.id === stateValue.slice(1));
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 resolveHistoryTarget(stateNode) {
607
+ function resolveHistoryDefaultTransition(stateNode) {
596
608
  const normalizedTarget = interpreter.normalizeTarget(stateNode.config.target);
597
609
  if (!normalizedTarget) {
598
- return stateNode.parent.initial.target;
610
+ return stateNode.parent.initial;
599
611
  }
600
- return normalizedTarget.map(t => typeof t === 'string' ? getStateNodeByPath(stateNode.parent, t) : t);
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 null;
854
+ return;
843
855
  }
844
- if (!transition.reenter && transition.source.type !== 'parallel' && targetStates.every(targetStateNode => isDescendant(targetStateNode, transition.source))) {
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);
@@ -928,6 +943,9 @@ function getMachineOutput(state, event, actorCtx, rootNode, rootCompletionNode)
928
943
  function enterStates(currentState, event, actorCtx, filteredTransitions, mutConfiguration, internalQueue, historyValue, isInitial) {
929
944
  let nextState = currentState;
930
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
931
949
  const statesForDefaultEntry = new Set();
932
950
  computeEntrySet(filteredTransitions, historyValue, statesForDefaultEntry, statesToEnter);
933
951
 
@@ -952,20 +970,16 @@ function enterStates(currentState, event, actorCtx, filteredTransitions, mutConf
952
970
  nextState = resolveActionsAndContext(nextState, event, actorCtx, actions, internalQueue, stateNodeToEnter.invoke.map(invokeDef => invokeDef.id));
953
971
  if (stateNodeToEnter.type === 'final') {
954
972
  const parent = stateNodeToEnter.parent;
955
- if (completedNodes.has(parent)) {
956
- continue;
957
- }
958
- completedNodes.add(parent);
959
- let rootCompletionNode = parent?.type === 'parallel' ? parent : stateNodeToEnter;
960
- let ancestorMarker = parent?.parent;
961
- if (ancestorMarker) {
973
+ let ancestorMarker = parent?.type === 'parallel' ? parent : parent?.parent;
974
+ let rootCompletionNode = ancestorMarker || stateNodeToEnter;
975
+ if (parent?.type === 'compound') {
962
976
  internalQueue.push(interpreter.createDoneStateEvent(parent.id, stateNodeToEnter.output ? interpreter.resolveOutput(stateNodeToEnter.output, nextState.context, event, actorCtx.self) : undefined));
963
- while (ancestorMarker?.type === 'parallel' && !completedNodes.has(ancestorMarker) && isInFinalState(mutConfiguration, ancestorMarker)) {
964
- completedNodes.add(ancestorMarker);
965
- internalQueue.push(interpreter.createDoneStateEvent(ancestorMarker.id));
966
- rootCompletionNode = ancestorMarker;
967
- ancestorMarker = ancestorMarker.parent;
968
- }
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;
969
983
  }
970
984
  if (ancestorMarker) {
971
985
  continue;
@@ -980,13 +994,24 @@ function enterStates(currentState, event, actorCtx, filteredTransitions, mutConf
980
994
  }
981
995
  function computeEntrySet(transitions, historyValue, statesForDefaultEntry, statesToEnter) {
982
996
  for (const t of transitions) {
997
+ const domain = getTransitionDomain(t, historyValue);
983
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
+ }
984
1010
  addDescendantStatesToEnter(s, historyValue, statesForDefaultEntry, statesToEnter);
985
1011
  }
986
- const ancestor = getTransitionDomain(t, historyValue);
987
1012
  const targetStates = getEffectiveTargetStates(t, historyValue);
988
1013
  for (const s of targetStates) {
989
- addAncestorStatesToEnter(s, ancestor, statesToEnter, historyValue, statesForDefaultEntry);
1014
+ addAncestorStatesToEnter(s, domain, statesToEnter, historyValue, statesForDefaultEntry);
990
1015
  }
991
1016
  }
992
1017
  }
@@ -995,37 +1020,42 @@ function addDescendantStatesToEnter(stateNode, historyValue, statesForDefaultEnt
995
1020
  if (historyValue[stateNode.id]) {
996
1021
  const historyStateNodes = historyValue[stateNode.id];
997
1022
  for (const s of historyStateNodes) {
1023
+ statesToEnter.add(s);
998
1024
  addDescendantStatesToEnter(s, historyValue, statesForDefaultEntry, statesToEnter);
999
1025
  }
1000
1026
  for (const s of historyStateNodes) {
1001
1027
  addAncestorStatesToEnter(s, stateNode.parent, statesToEnter, historyValue, statesForDefaultEntry);
1002
- for (const stateForDefaultEntry of statesForDefaultEntry) {
1003
- statesForDefaultEntry.add(stateForDefaultEntry);
1004
- }
1005
1028
  }
1006
1029
  } else {
1007
- const targets = resolveHistoryTarget(stateNode);
1008
- for (const s of targets) {
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
+ }
1009
1036
  addDescendantStatesToEnter(s, historyValue, statesForDefaultEntry, statesToEnter);
1010
1037
  }
1011
- for (const s of targets) {
1038
+ for (const s of historyDefaultTransition.target) {
1012
1039
  addAncestorStatesToEnter(s, stateNode, statesToEnter, historyValue, statesForDefaultEntry);
1013
- for (const stateForDefaultEntry of statesForDefaultEntry) {
1014
- statesForDefaultEntry.add(stateForDefaultEntry);
1015
- }
1016
1040
  }
1017
1041
  }
1018
1042
  } else {
1019
- statesToEnter.add(stateNode);
1020
1043
  if (stateNode.type === 'compound') {
1021
- statesForDefaultEntry.add(stateNode);
1022
1044
  const [initialState] = stateNode.initial.target;
1045
+ if (!isHistoryNode(initialState)) {
1046
+ statesToEnter.add(initialState);
1047
+ statesForDefaultEntry.add(initialState);
1048
+ }
1023
1049
  addDescendantStatesToEnter(initialState, historyValue, statesForDefaultEntry, statesToEnter);
1024
1050
  addAncestorStatesToEnter(initialState, stateNode, statesToEnter, historyValue, statesForDefaultEntry);
1025
1051
  } else {
1026
1052
  if (stateNode.type === 'parallel') {
1027
1053
  for (const child of getChildren(stateNode).filter(sn => !isHistoryNode(sn))) {
1028
1054
  if (![...statesToEnter].some(s => isDescendant(s, child))) {
1055
+ if (!isHistoryNode(child)) {
1056
+ statesToEnter.add(child);
1057
+ statesForDefaultEntry.add(child);
1058
+ }
1029
1059
  addDescendantStatesToEnter(child, historyValue, statesForDefaultEntry, statesToEnter);
1030
1060
  }
1031
1061
  }
@@ -1040,6 +1070,7 @@ function addAncestorStatesToEnter(stateNode, toStateNode, statesToEnter, history
1040
1070
  if (anc.type === 'parallel') {
1041
1071
  for (const child of getChildren(anc).filter(sn => !isHistoryNode(sn))) {
1042
1072
  if (![...statesToEnter].some(s => isDescendant(s, child))) {
1073
+ statesToEnter.add(child);
1043
1074
  addDescendantStatesToEnter(child, historyValue, statesForDefaultEntry, statesToEnter);
1044
1075
  }
1045
1076
  }
@@ -1210,7 +1241,7 @@ function selectEventlessTransitions(nextState, event) {
1210
1241
  const enabledTransitionSet = new Set();
1211
1242
  const atomicStates = nextState.configuration.filter(isAtomicStateNode);
1212
1243
  for (const stateNode of atomicStates) {
1213
- loop: for (const s of [stateNode].concat(getProperAncestors(stateNode, null))) {
1244
+ loop: for (const s of [stateNode].concat(getProperAncestors(stateNode, undefined))) {
1214
1245
  if (!s.always) {
1215
1246
  continue;
1216
1247
  }
@@ -1,4 +1,4 @@
1
- import { r as resolveReferencedActor, d as createActor, f as ActorStatus, j as createErrorActorEvent, k as toStateValue, l as STATE_IDENTIFIER, n as normalizeTarget, t as toArray, N as NULL_EVENT, a as toTransitionConfigArray, S as STATE_DELIMITER, o as toStatePath, q as createDoneStateEvent, s as resolveOutput, X as XSTATE_STOP, u as XSTATE_INIT, W as WILDCARD, v as createAfterEvent, w as flatten, e as matchesState, $ as $$ACTOR_TYPE } from './interpreter-de5217bc.esm.js';
1
+ import { r as resolveReferencedActor, d as createActor, f as ActorStatus, j as createErrorActorEvent, k as toStateValue, l as STATE_IDENTIFIER, n as normalizeTarget, t as toArray, N as NULL_EVENT, a as toTransitionConfigArray, S as STATE_DELIMITER, o as toStatePath, q as createDoneStateEvent, s as resolveOutput, X as XSTATE_STOP, u as XSTATE_INIT, W as WILDCARD, v as createAfterEvent, w as flatten, e as matchesState, $ as $$ACTOR_TYPE } from './interpreter-8def682e.esm.js';
2
2
 
3
3
  const cache = new WeakMap();
4
4
  function memo(object, key, fn) {
@@ -47,14 +47,17 @@ function resolveInvoke(actorContext, state, actionArgs, {
47
47
  input,
48
48
  syncSnapshot
49
49
  }) {
50
- const referenced = resolveReferencedActor(state.machine.implementations.actors[src]);
50
+ const referenced = typeof src === 'string' ? resolveReferencedActor(state.machine, src) : {
51
+ src,
52
+ input: undefined
53
+ };
51
54
  let actorRef;
52
55
  if (referenced) {
53
56
  // TODO: inline `input: undefined` should win over the referenced one
54
57
  const configuredInput = input || referenced.input;
55
58
  actorRef = createActor(referenced.src, {
56
59
  id,
57
- src,
60
+ src: typeof src === 'string' ? src : undefined,
58
61
  parent: actorContext?.self,
59
62
  systemId,
60
63
  input: typeof configuredInput === 'function' ? configuredInput({
@@ -151,11 +154,16 @@ function executeStop(actorContext, actorRef) {
151
154
  if (!actorRef) {
152
155
  return;
153
156
  }
157
+ // this allows us to prevent an actor from being started if it gets stopped within the same macrostep
158
+ // this can happen, for example, when the invoking state is being exited immediately by an always transition
154
159
  if (actorRef.status !== ActorStatus.Running) {
155
160
  actorContext.stopChild(actorRef);
156
161
  return;
157
162
  }
158
- // TODO: recheck why this one has to be deferred
163
+ // stopping a child enqueues a stop event in the child actor's mailbox
164
+ // we need for all of the already enqueued events to be processed before we stop the child
165
+ // the parent itself might want to send some events to a child (for example from exit actions on the invoking state)
166
+ // and we don't want to ignore those events
159
167
  actorContext.defer(() => {
160
168
  actorContext.stopChild(actorRef);
161
169
  });
@@ -179,7 +187,8 @@ function checkStateIn(state, _, {
179
187
  stateValue
180
188
  }) {
181
189
  if (typeof stateValue === 'string' && isStateId(stateValue)) {
182
- return state.configuration.some(sn => sn.id === stateValue.slice(1));
190
+ const target = state.machine.getStateNodeById(stateValue);
191
+ return state.configuration.some(sn => sn === target);
183
192
  }
184
193
  return state.matches(stateValue);
185
194
  }
@@ -283,6 +292,9 @@ function getChildren(stateNode) {
283
292
  }
284
293
  function getProperAncestors(stateNode, toStateNode) {
285
294
  const ancestors = [];
295
+ if (toStateNode === stateNode) {
296
+ return ancestors;
297
+ }
286
298
 
287
299
  // add all ancestors
288
300
  let m = stateNode.parent;
@@ -559,12 +571,14 @@ function resolveTarget(stateNode, targets) {
559
571
  }
560
572
  });
561
573
  }
562
- function resolveHistoryTarget(stateNode) {
574
+ function resolveHistoryDefaultTransition(stateNode) {
563
575
  const normalizedTarget = normalizeTarget(stateNode.config.target);
564
576
  if (!normalizedTarget) {
565
- return stateNode.parent.initial.target;
577
+ return stateNode.parent.initial;
566
578
  }
567
- return normalizedTarget.map(t => typeof t === 'string' ? getStateNodeByPath(stateNode.parent, t) : t);
579
+ return {
580
+ target: normalizedTarget.map(t => typeof t === 'string' ? getStateNodeByPath(stateNode.parent, t) : t)
581
+ };
568
582
  }
569
583
  function isHistoryNode(stateNode) {
570
584
  return stateNode.type === 'history';
@@ -791,9 +805,7 @@ function getEffectiveTargetStates(transition, historyValue) {
791
805
  targets.add(node);
792
806
  }
793
807
  } else {
794
- for (const node of getEffectiveTargetStates({
795
- target: resolveHistoryTarget(targetNode)
796
- }, historyValue)) {
808
+ for (const node of getEffectiveTargetStates(resolveHistoryDefaultTransition(targetNode), historyValue)) {
797
809
  targets.add(node);
798
810
  }
799
811
  }
@@ -806,9 +818,9 @@ function getEffectiveTargetStates(transition, historyValue) {
806
818
  function getTransitionDomain(transition, historyValue) {
807
819
  const targetStates = getEffectiveTargetStates(transition, historyValue);
808
820
  if (!targetStates) {
809
- return null;
821
+ return;
810
822
  }
811
- if (!transition.reenter && transition.source.type !== 'parallel' && targetStates.every(targetStateNode => isDescendant(targetStateNode, transition.source))) {
823
+ if (!transition.reenter && targetStates.every(target => target === transition.source || isDescendant(target, transition.source))) {
812
824
  return transition.source;
813
825
  }
814
826
  const lcca = findLCCA(targetStates.concat(transition.source));
@@ -819,6 +831,9 @@ function computeExitSet(transitions, configuration, historyValue) {
819
831
  for (const t of transitions) {
820
832
  if (t.target?.length) {
821
833
  const domain = getTransitionDomain(t, historyValue);
834
+ if (t.reenter && t.source === domain) {
835
+ statesToExit.add(domain);
836
+ }
822
837
  for (const stateNode of configuration) {
823
838
  if (isDescendant(stateNode, domain)) {
824
839
  statesToExit.add(stateNode);
@@ -895,6 +910,9 @@ function getMachineOutput(state, event, actorCtx, rootNode, rootCompletionNode)
895
910
  function enterStates(currentState, event, actorCtx, filteredTransitions, mutConfiguration, internalQueue, historyValue, isInitial) {
896
911
  let nextState = currentState;
897
912
  const statesToEnter = new Set();
913
+ // those are states that were directly targeted or indirectly targeted by the explicit target
914
+ // in other words, those are states for which initial actions should be executed
915
+ // when we target `#deep_child` initial actions of its ancestors shouldn't be executed
898
916
  const statesForDefaultEntry = new Set();
899
917
  computeEntrySet(filteredTransitions, historyValue, statesForDefaultEntry, statesToEnter);
900
918
 
@@ -919,20 +937,16 @@ function enterStates(currentState, event, actorCtx, filteredTransitions, mutConf
919
937
  nextState = resolveActionsAndContext(nextState, event, actorCtx, actions, internalQueue, stateNodeToEnter.invoke.map(invokeDef => invokeDef.id));
920
938
  if (stateNodeToEnter.type === 'final') {
921
939
  const parent = stateNodeToEnter.parent;
922
- if (completedNodes.has(parent)) {
923
- continue;
924
- }
925
- completedNodes.add(parent);
926
- let rootCompletionNode = parent?.type === 'parallel' ? parent : stateNodeToEnter;
927
- let ancestorMarker = parent?.parent;
928
- if (ancestorMarker) {
940
+ let ancestorMarker = parent?.type === 'parallel' ? parent : parent?.parent;
941
+ let rootCompletionNode = ancestorMarker || stateNodeToEnter;
942
+ if (parent?.type === 'compound') {
929
943
  internalQueue.push(createDoneStateEvent(parent.id, stateNodeToEnter.output ? resolveOutput(stateNodeToEnter.output, nextState.context, event, actorCtx.self) : undefined));
930
- while (ancestorMarker?.type === 'parallel' && !completedNodes.has(ancestorMarker) && isInFinalState(mutConfiguration, ancestorMarker)) {
931
- completedNodes.add(ancestorMarker);
932
- internalQueue.push(createDoneStateEvent(ancestorMarker.id));
933
- rootCompletionNode = ancestorMarker;
934
- ancestorMarker = ancestorMarker.parent;
935
- }
944
+ }
945
+ while (ancestorMarker?.type === 'parallel' && !completedNodes.has(ancestorMarker) && isInFinalState(mutConfiguration, ancestorMarker)) {
946
+ completedNodes.add(ancestorMarker);
947
+ internalQueue.push(createDoneStateEvent(ancestorMarker.id));
948
+ rootCompletionNode = ancestorMarker;
949
+ ancestorMarker = ancestorMarker.parent;
936
950
  }
937
951
  if (ancestorMarker) {
938
952
  continue;
@@ -947,13 +961,24 @@ function enterStates(currentState, event, actorCtx, filteredTransitions, mutConf
947
961
  }
948
962
  function computeEntrySet(transitions, historyValue, statesForDefaultEntry, statesToEnter) {
949
963
  for (const t of transitions) {
964
+ const domain = getTransitionDomain(t, historyValue);
950
965
  for (const s of t.target || []) {
966
+ if (!isHistoryNode(s) && (
967
+ // if the target is different than the source then it will *definitely* be entered
968
+ t.source !== s ||
969
+ // we know that the domain can't lie within the source
970
+ // 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
971
+ t.source !== domain ||
972
+ // reentering transitions always enter the target, even if it's the source itself
973
+ t.reenter)) {
974
+ statesToEnter.add(s);
975
+ statesForDefaultEntry.add(s);
976
+ }
951
977
  addDescendantStatesToEnter(s, historyValue, statesForDefaultEntry, statesToEnter);
952
978
  }
953
- const ancestor = getTransitionDomain(t, historyValue);
954
979
  const targetStates = getEffectiveTargetStates(t, historyValue);
955
980
  for (const s of targetStates) {
956
- addAncestorStatesToEnter(s, ancestor, statesToEnter, historyValue, statesForDefaultEntry);
981
+ addAncestorStatesToEnter(s, domain, statesToEnter, historyValue, statesForDefaultEntry);
957
982
  }
958
983
  }
959
984
  }
@@ -962,37 +987,42 @@ function addDescendantStatesToEnter(stateNode, historyValue, statesForDefaultEnt
962
987
  if (historyValue[stateNode.id]) {
963
988
  const historyStateNodes = historyValue[stateNode.id];
964
989
  for (const s of historyStateNodes) {
990
+ statesToEnter.add(s);
965
991
  addDescendantStatesToEnter(s, historyValue, statesForDefaultEntry, statesToEnter);
966
992
  }
967
993
  for (const s of historyStateNodes) {
968
994
  addAncestorStatesToEnter(s, stateNode.parent, statesToEnter, historyValue, statesForDefaultEntry);
969
- for (const stateForDefaultEntry of statesForDefaultEntry) {
970
- statesForDefaultEntry.add(stateForDefaultEntry);
971
- }
972
995
  }
973
996
  } else {
974
- const targets = resolveHistoryTarget(stateNode);
975
- for (const s of targets) {
997
+ const historyDefaultTransition = resolveHistoryDefaultTransition(stateNode);
998
+ for (const s of historyDefaultTransition.target) {
999
+ statesToEnter.add(s);
1000
+ if (historyDefaultTransition === stateNode.parent?.initial) {
1001
+ statesForDefaultEntry.add(stateNode.parent);
1002
+ }
976
1003
  addDescendantStatesToEnter(s, historyValue, statesForDefaultEntry, statesToEnter);
977
1004
  }
978
- for (const s of targets) {
1005
+ for (const s of historyDefaultTransition.target) {
979
1006
  addAncestorStatesToEnter(s, stateNode, statesToEnter, historyValue, statesForDefaultEntry);
980
- for (const stateForDefaultEntry of statesForDefaultEntry) {
981
- statesForDefaultEntry.add(stateForDefaultEntry);
982
- }
983
1007
  }
984
1008
  }
985
1009
  } else {
986
- statesToEnter.add(stateNode);
987
1010
  if (stateNode.type === 'compound') {
988
- statesForDefaultEntry.add(stateNode);
989
1011
  const [initialState] = stateNode.initial.target;
1012
+ if (!isHistoryNode(initialState)) {
1013
+ statesToEnter.add(initialState);
1014
+ statesForDefaultEntry.add(initialState);
1015
+ }
990
1016
  addDescendantStatesToEnter(initialState, historyValue, statesForDefaultEntry, statesToEnter);
991
1017
  addAncestorStatesToEnter(initialState, stateNode, statesToEnter, historyValue, statesForDefaultEntry);
992
1018
  } else {
993
1019
  if (stateNode.type === 'parallel') {
994
1020
  for (const child of getChildren(stateNode).filter(sn => !isHistoryNode(sn))) {
995
1021
  if (![...statesToEnter].some(s => isDescendant(s, child))) {
1022
+ if (!isHistoryNode(child)) {
1023
+ statesToEnter.add(child);
1024
+ statesForDefaultEntry.add(child);
1025
+ }
996
1026
  addDescendantStatesToEnter(child, historyValue, statesForDefaultEntry, statesToEnter);
997
1027
  }
998
1028
  }
@@ -1007,6 +1037,7 @@ function addAncestorStatesToEnter(stateNode, toStateNode, statesToEnter, history
1007
1037
  if (anc.type === 'parallel') {
1008
1038
  for (const child of getChildren(anc).filter(sn => !isHistoryNode(sn))) {
1009
1039
  if (![...statesToEnter].some(s => isDescendant(s, child))) {
1040
+ statesToEnter.add(child);
1010
1041
  addDescendantStatesToEnter(child, historyValue, statesForDefaultEntry, statesToEnter);
1011
1042
  }
1012
1043
  }
@@ -1174,7 +1205,7 @@ function selectEventlessTransitions(nextState, event) {
1174
1205
  const enabledTransitionSet = new Set();
1175
1206
  const atomicStates = nextState.configuration.filter(isAtomicStateNode);
1176
1207
  for (const stateNode of atomicStates) {
1177
- loop: for (const s of [stateNode].concat(getProperAncestors(stateNode, null))) {
1208
+ loop: for (const s of [stateNode].concat(getProperAncestors(stateNode, undefined))) {
1178
1209
  if (!s.always) {
1179
1210
  continue;
1180
1211
  }