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.
Files changed (43) 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/State.d.ts +0 -1
  14. package/dist/declarations/src/stateUtils.d.ts +3 -3
  15. package/dist/declarations/src/typegenTypes.d.ts +2 -0
  16. package/dist/declarations/src/types.d.ts +1 -2
  17. package/dist/declarations/src/utils.d.ts +6 -6
  18. package/dist/declarations/src/waitFor.d.ts +1 -1
  19. package/dist/{interpreter-5c4e6634.development.esm.js → interpreter-1c52b23c.development.esm.js} +13 -23
  20. package/dist/{interpreter-de5217bc.esm.js → interpreter-8def682e.esm.js} +13 -23
  21. package/dist/{interpreter-69605bf0.cjs.js → interpreter-97aff8d2.cjs.js} +13 -23
  22. package/dist/{interpreter-d3567419.development.cjs.js → interpreter-e58ca48d.development.cjs.js} +13 -23
  23. package/dist/{raise-26e4d83c.development.cjs.js → raise-1fd59c65.development.cjs.js} +129 -102
  24. package/dist/{raise-511399cc.esm.js → raise-21c417c1.esm.js} +129 -102
  25. package/dist/{raise-0ff57677.cjs.js → raise-800296d7.cjs.js} +129 -102
  26. package/dist/{raise-cdcdf834.development.esm.js → raise-e342a840.development.esm.js} +129 -102
  27. package/dist/{send-19ffc568.cjs.js → send-4cc29786.cjs.js} +7 -4
  28. package/dist/{send-1de74f4d.development.esm.js → send-83ccc98b.development.esm.js} +7 -4
  29. package/dist/{send-211a2a94.esm.js → send-92854675.esm.js} +7 -4
  30. package/dist/{send-894c4b18.development.cjs.js → send-b309ef4e.development.cjs.js} +7 -4
  31. package/dist/xstate.cjs.js +11 -19
  32. package/dist/xstate.development.cjs.js +11 -19
  33. package/dist/xstate.development.esm.js +14 -22
  34. package/dist/xstate.esm.js +14 -22
  35. package/dist/xstate.umd.min.js +1 -1
  36. package/dist/xstate.umd.min.js.map +1 -1
  37. package/guards/dist/xstate-guards.cjs.js +2 -2
  38. package/guards/dist/xstate-guards.development.cjs.js +2 -2
  39. package/guards/dist/xstate-guards.development.esm.js +2 -2
  40. package/guards/dist/xstate-guards.esm.js +2 -2
  41. package/guards/dist/xstate-guards.umd.min.js +1 -1
  42. package/guards/dist/xstate-guards.umd.min.js.map +1 -1
  43. 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
- // something could have been prepended in the meantime
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}:invocation[${index}]`;
314
+ return `${stateNodeId}[${index}]`;
335
315
  }
336
- function resolveReferencedActor(referenced) {
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
- // 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);
@@ -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 microstate = microstepProcedure(transitions, currentState, mutConfiguration, event, actorCtx, isInitial);
880
- return cloneState(microstate, {
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
- const internalQueue = [...currentState._internalQueue];
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
- internalQueue.push(...nextState._internalQueue);
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
- if (completedNodes.has(parent)) {
959
- continue;
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
- while (ancestorMarker?.type === 'parallel' && !completedNodes.has(ancestorMarker) && isInFinalState(mutConfiguration, ancestorMarker)) {
967
- completedNodes.add(ancestorMarker);
968
- internalQueue.push(interpreter.createDoneStateEvent(ancestorMarker.id));
969
- rootCompletionNode = ancestorMarker;
970
- ancestorMarker = ancestorMarker.parent;
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, ancestor, statesToEnter, historyValue, statesForDefaultEntry);
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 targets = resolveHistoryTarget(stateNode);
1011
- 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
+ }
1012
1036
  addDescendantStatesToEnter(s, historyValue, statesForDefaultEntry, statesToEnter);
1013
1037
  }
1014
- for (const s of targets) {
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
- historyValue[historyNode.id] = Array.from(mutConfiguration).filter(predicate);
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, deferredActorIds && {
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 = stopStep(event, nextState, actorCtx);
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 (!nextState._internalQueue.length) {
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
- } else {
1188
- nextState = microstep(enabledTransitions, nextState, actorCtx, nextEvent, false);
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
- // Perform the stop step to ensure that child actors are stopped
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 stopStep(event, nextState, actorCtx) {
1202
- const actions = [];
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, null))) {
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
- return [typeof resolvedDelay !== 'number' ? cloneState(state, {
1488
- _internalQueue: state._internalQueue.concat(resolvedEvent)
1489
- }) : state, {
1513
+ if (typeof resolvedDelay !== 'number') {
1514
+ internalQueue.push(resolvedEvent);
1515
+ }
1516
+ return [state, {
1490
1517
  event: resolvedEvent,
1491
1518
  id,
1492
1519
  delay: resolvedDelay