xstate 5.0.0-beta.34 → 5.0.0-beta.36

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/README.md +1 -1
  2. package/actions/dist/xstate-actions.cjs.js +3 -3
  3. package/actions/dist/xstate-actions.development.cjs.js +3 -3
  4. package/actions/dist/xstate-actions.development.esm.js +3 -3
  5. package/actions/dist/xstate-actions.esm.js +3 -3
  6. package/actions/dist/xstate-actions.umd.min.js +1 -1
  7. package/actions/dist/xstate-actions.umd.min.js.map +1 -1
  8. package/actors/dist/xstate-actors.cjs.js +1 -1
  9. package/actors/dist/xstate-actors.development.cjs.js +1 -1
  10. package/actors/dist/xstate-actors.development.esm.js +1 -1
  11. package/actors/dist/xstate-actors.esm.js +1 -1
  12. package/actors/dist/xstate-actors.umd.min.js +1 -1
  13. package/actors/dist/xstate-actors.umd.min.js.map +1 -1
  14. package/dist/declarations/src/State.d.ts +0 -1
  15. package/dist/declarations/src/interpreter.d.ts +2 -0
  16. package/dist/declarations/src/stateUtils.d.ts +8 -7
  17. package/dist/declarations/src/types.d.ts +3 -6
  18. package/dist/{interpreter-dee56dc8.development.esm.js → interpreter-5c4e6634.development.esm.js} +59 -57
  19. package/dist/{interpreter-1301970f.cjs.js → interpreter-69605bf0.cjs.js} +59 -57
  20. package/dist/{interpreter-70ed62f2.development.cjs.js → interpreter-d3567419.development.cjs.js} +59 -57
  21. package/dist/{interpreter-83f7f2d4.esm.js → interpreter-de5217bc.esm.js} +59 -57
  22. package/dist/{raise-38b707c0.development.cjs.js → raise-106ea558.development.cjs.js} +160 -160
  23. package/dist/{raise-05f8b2a6.development.esm.js → raise-5b7ad3b7.development.esm.js} +160 -160
  24. package/dist/{raise-1dd65455.cjs.js → raise-c51b81a3.cjs.js} +157 -160
  25. package/dist/{raise-b5cfe1bb.esm.js → raise-ffe1014a.esm.js} +157 -160
  26. package/dist/{send-0b5eda0c.esm.js → send-0a7aa74e.esm.js} +5 -3
  27. package/dist/{send-3764c866.development.cjs.js → send-25e70bd4.development.cjs.js} +5 -3
  28. package/dist/{send-fe94de2b.cjs.js → send-778692de.cjs.js} +5 -3
  29. package/dist/{send-9526366e.development.esm.js → send-e93554d6.development.esm.js} +5 -3
  30. package/dist/xstate.cjs.js +32 -13
  31. package/dist/xstate.development.cjs.js +32 -13
  32. package/dist/xstate.development.esm.js +35 -16
  33. package/dist/xstate.esm.js +35 -16
  34. package/dist/xstate.umd.min.js +1 -1
  35. package/dist/xstate.umd.min.js.map +1 -1
  36. package/guards/dist/xstate-guards.cjs.js +2 -2
  37. package/guards/dist/xstate-guards.development.cjs.js +2 -2
  38. package/guards/dist/xstate-guards.development.esm.js +2 -2
  39. package/guards/dist/xstate-guards.esm.js +2 -2
  40. package/guards/dist/xstate-guards.umd.min.js.map +1 -1
  41. package/package.json +1 -1
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var interpreter = require('./interpreter-70ed62f2.development.cjs.js');
3
+ var interpreter = require('./interpreter-d3567419.development.cjs.js');
4
4
 
5
5
  const cache = new WeakMap();
6
6
  function memo(object, key, fn) {
@@ -299,20 +299,6 @@ function evaluateGuard(guard, context, event, state) {
299
299
  );
300
300
  }
301
301
 
302
- function getOutput(configuration, context, event, self) {
303
- const {
304
- machine
305
- } = configuration[0];
306
- const {
307
- root
308
- } = machine;
309
- if (!root.output) {
310
- return undefined;
311
- }
312
- const finalChildStateNode = configuration.find(stateNode => stateNode.type === 'final' && stateNode.parent === machine.root);
313
- const doneStateEvent = interpreter.createDoneStateEvent(finalChildStateNode.id, finalChildStateNode.output ? interpreter.resolveOutput(finalChildStateNode.output, context, event, self) : undefined);
314
- return interpreter.resolveOutput(root.output, context, doneStateEvent, self);
315
- }
316
302
  const isAtomicStateNode = stateNode => stateNode.type === 'atomic' || stateNode.type === 'final';
317
303
  function getChildren(stateNode) {
318
304
  return Object.values(stateNode.states).filter(sn => sn.type !== 'history');
@@ -337,7 +323,7 @@ function getConfiguration(stateNodes) {
337
323
  for (const s of configuration) {
338
324
  // if previously active, add existing child nodes
339
325
  if (s.type === 'compound' && (!adjList.get(s) || !adjList.get(s).length)) {
340
- getInitialStateNodes(s).forEach(sn => configurationSet.add(sn));
326
+ getInitialStateNodesWithTheirAncestors(s).forEach(sn => configurationSet.add(sn));
341
327
  } else {
342
328
  if (s.type === 'parallel') {
343
329
  for (const child of getChildren(s)) {
@@ -345,7 +331,8 @@ function getConfiguration(stateNodes) {
345
331
  continue;
346
332
  }
347
333
  if (!configurationSet.has(child)) {
348
- for (const initialStateNode of getInitialStateNodes(child)) {
334
+ const initialStates = getInitialStateNodesWithTheirAncestors(child);
335
+ for (const initialStateNode of initialStates) {
349
336
  configurationSet.add(initialStateNode);
350
337
  }
351
338
  }
@@ -405,14 +392,14 @@ function getStateValue(rootNode, configuration) {
405
392
  const config = getConfiguration(configuration);
406
393
  return getValueFromAdj(rootNode, getAdjList(config));
407
394
  }
408
- function isInFinalState(configuration, stateNode = configuration[0].machine.root) {
395
+ function isInFinalState(configuration, stateNode) {
409
396
  if (stateNode.type === 'compound') {
410
- return getChildren(stateNode).some(s => s.type === 'final' && configuration.includes(s));
397
+ return getChildren(stateNode).some(s => s.type === 'final' && configuration.has(s));
411
398
  }
412
399
  if (stateNode.type === 'parallel') {
413
400
  return getChildren(stateNode).every(sn => isInFinalState(configuration, sn));
414
401
  }
415
- return false;
402
+ return stateNode.type === 'final';
416
403
  }
417
404
  const isStateId = str => str[0] === interpreter.STATE_IDENTIFIER;
418
405
  function getCandidates(stateNode, receivedEventType) {
@@ -468,13 +455,7 @@ function getDelayedTransitions(stateNode) {
468
455
  stateNode.exit.push(cancel(eventType));
469
456
  return eventType;
470
457
  };
471
- const delayedTransitions = interpreter.isArray(afterConfig) ? afterConfig.map((transition, i) => {
472
- const eventType = mutateEntryExit(transition.delay, i);
473
- return {
474
- ...transition,
475
- event: eventType
476
- };
477
- }) : Object.keys(afterConfig).flatMap((delay, i) => {
458
+ const delayedTransitions = Object.keys(afterConfig).flatMap((delay, i) => {
478
459
  const configTransition = afterConfig[delay];
479
460
  const resolvedTransition = typeof configTransition === 'string' ? {
480
461
  target: configTransition
@@ -562,43 +543,23 @@ function formatTransitions(stateNode) {
562
543
  return transitions;
563
544
  }
564
545
  function formatInitialTransition(stateNode, _target) {
565
- if (typeof _target === 'string' || interpreter.isArray(_target)) {
566
- const targets = interpreter.toArray(_target).map(t => {
567
- // Resolve state string keys (which represent children)
568
- // to their state node
569
- const descStateNode = typeof t === 'string' ? isStateId(t) ? stateNode.machine.getStateNodeById(t) : stateNode.states[t] : t;
570
- if (!descStateNode) {
571
- throw new Error(`Initial state node "${t}" not found on parent state node #${stateNode.id}`);
572
- }
573
- if (!isDescendant(descStateNode, stateNode)) {
574
- throw new Error(`Invalid initial target: state node #${descStateNode.id} is not a descendant of #${stateNode.id}`);
575
- }
576
- return descStateNode;
577
- });
578
- const resolvedTarget = resolveTarget(stateNode, targets);
579
- const transition = {
580
- source: stateNode,
581
- actions: [],
582
- eventType: null,
583
- reenter: false,
584
- target: resolvedTarget,
585
- toJSON: () => ({
586
- ...transition,
587
- source: `#${stateNode.id}`,
588
- target: resolvedTarget ? resolvedTarget.map(t => `#${t.id}`) : undefined
589
- })
590
- };
591
- return transition;
546
+ const resolvedTarget = typeof _target === 'string' ? stateNode.states[_target] : _target ? stateNode.states[_target.target] : undefined;
547
+ if (!resolvedTarget && _target) {
548
+ throw new Error(`Initial state node "${_target}" not found on parent state node #${stateNode.id}`);
592
549
  }
593
- return formatTransition(stateNode, '__INITIAL__', {
594
- target: interpreter.toArray(_target.target).map(t => {
595
- if (typeof t === 'string') {
596
- return isStateId(t) ? t : `${interpreter.STATE_DELIMITER}${t}`;
597
- }
598
- return t;
599
- }),
600
- actions: _target.actions
601
- });
550
+ const transition = {
551
+ source: stateNode,
552
+ actions: !_target || typeof _target === 'string' ? [] : interpreter.toArray(_target.actions),
553
+ eventType: null,
554
+ reenter: false,
555
+ target: resolvedTarget ? [resolvedTarget] : [],
556
+ toJSON: () => ({
557
+ ...transition,
558
+ source: `#${stateNode.id}`,
559
+ target: resolvedTarget ? [`#${resolvedTarget.id}`] : []
560
+ })
561
+ };
562
+ return transition;
602
563
  }
603
564
  function resolveTarget(stateNode, targets) {
604
565
  if (targets === undefined) {
@@ -641,6 +602,15 @@ function resolveHistoryTarget(stateNode) {
641
602
  function isHistoryNode(stateNode) {
642
603
  return stateNode.type === 'history';
643
604
  }
605
+ function getInitialStateNodesWithTheirAncestors(stateNode) {
606
+ const states = getInitialStateNodes(stateNode);
607
+ for (const initialState of states) {
608
+ for (const ancestor of getProperAncestors(initialState, stateNode)) {
609
+ states.add(ancestor);
610
+ }
611
+ }
612
+ return states;
613
+ }
644
614
  function getInitialStateNodes(stateNode) {
645
615
  const set = new Set();
646
616
  function iter(descStateNode) {
@@ -649,12 +619,7 @@ function getInitialStateNodes(stateNode) {
649
619
  }
650
620
  set.add(descStateNode);
651
621
  if (descStateNode.type === 'compound') {
652
- for (const targetStateNode of descStateNode.initial.target) {
653
- for (const a of getProperAncestors(targetStateNode, stateNode)) {
654
- set.add(a);
655
- }
656
- iter(targetStateNode);
657
- }
622
+ iter(descStateNode.initial.target[0]);
658
623
  } else if (descStateNode.type === 'parallel') {
659
624
  for (const child of getChildren(descStateNode)) {
660
625
  iter(child);
@@ -662,7 +627,7 @@ function getInitialStateNodes(stateNode) {
662
627
  }
663
628
  }
664
629
  iter(stateNode);
665
- return [...set];
630
+ return set;
666
631
  }
667
632
  /**
668
633
  * Returns the child state node from its relative `stateKey`, or throws.
@@ -896,6 +861,17 @@ function computeExitSet(transitions, configuration, historyValue) {
896
861
  }
897
862
  return [...statesToExit];
898
863
  }
864
+ function areConfigurationsEqual(previousConfiguration, nextConfigurationSet) {
865
+ if (previousConfiguration.length !== nextConfigurationSet.size) {
866
+ return false;
867
+ }
868
+ for (const node of previousConfiguration) {
869
+ if (!nextConfigurationSet.has(node)) {
870
+ return false;
871
+ }
872
+ }
873
+ return true;
874
+ }
899
875
 
900
876
  /**
901
877
  * https://www.w3.org/TR/scxml/#microstepProcedure
@@ -905,56 +881,36 @@ function computeExitSet(transitions, configuration, historyValue) {
905
881
  * @param currentState
906
882
  * @param mutConfiguration
907
883
  */
908
-
909
- function microstep(transitions, currentState, actorCtx, event, isInitial) {
910
- const mutConfiguration = new Set(currentState.configuration);
884
+ function microstep(transitions, currentState, actorCtx, event, isInitial, internalQueue) {
911
885
  if (!transitions.length) {
912
886
  return currentState;
913
887
  }
914
- const microstate = microstepProcedure(transitions, currentState, mutConfiguration, event, actorCtx, isInitial);
915
- return cloneState(microstate, {
916
- value: {} // TODO: make optional
917
- });
918
- }
919
-
920
- function microstepProcedure(transitions, currentState, mutConfiguration, event, actorCtx, isInitial) {
921
- const historyValue = {
922
- ...currentState.historyValue
923
- };
888
+ const mutConfiguration = new Set(currentState.configuration);
889
+ let historyValue = currentState.historyValue;
924
890
  const filteredTransitions = removeConflictingTransitions(transitions, mutConfiguration, historyValue);
925
- const internalQueue = [...currentState._internalQueue];
926
- // TODO: this `cloneState` is really just a hack to prevent infinite loops
927
- // we need to take another look at how internal queue is managed
928
- let nextState = cloneState(currentState, {
929
- _internalQueue: []
930
- });
891
+ let nextState = currentState;
931
892
 
932
893
  // Exit states
933
894
  if (!isInitial) {
934
- nextState = exitStates(nextState, event, actorCtx, filteredTransitions, mutConfiguration, historyValue);
895
+ [nextState, historyValue] = exitStates(nextState, event, actorCtx, filteredTransitions, mutConfiguration, historyValue, internalQueue);
935
896
  }
936
897
 
937
898
  // Execute transition content
938
- nextState = resolveActionsAndContext(nextState, event, actorCtx, filteredTransitions.flatMap(t => t.actions));
899
+ nextState = resolveActionsAndContext(nextState, event, actorCtx, filteredTransitions.flatMap(t => t.actions), internalQueue);
939
900
 
940
901
  // Enter states
941
902
  nextState = enterStates(nextState, event, actorCtx, filteredTransitions, mutConfiguration, internalQueue, historyValue, isInitial);
942
903
  const nextConfiguration = [...mutConfiguration];
943
- const done = isInFinalState(nextConfiguration);
944
- if (done) {
945
- nextState = resolveActionsAndContext(nextState, event, actorCtx, nextConfiguration.sort((a, b) => b.order - a.order).flatMap(state => state.exit));
904
+ if (nextState.status === 'done') {
905
+ nextState = resolveActionsAndContext(nextState, event, actorCtx, nextConfiguration.sort((a, b) => b.order - a.order).flatMap(state => state.exit), internalQueue);
946
906
  }
947
907
  try {
948
- const output = done ? getOutput(nextConfiguration, nextState.context, event, actorCtx.self) : undefined;
949
- internalQueue.push(...nextState._internalQueue);
950
- return cloneState(currentState, {
908
+ if (historyValue === currentState.historyValue && areConfigurationsEqual(currentState.configuration, mutConfiguration)) {
909
+ return nextState;
910
+ }
911
+ return cloneState(nextState, {
951
912
  configuration: nextConfiguration,
952
- historyValue,
953
- _internalQueue: internalQueue,
954
- context: nextState.context,
955
- status: done ? 'done' : currentState.status,
956
- output,
957
- children: nextState.children
913
+ historyValue
958
914
  });
959
915
  } catch (e) {
960
916
  // TODO: Refactor this once proper error handling is implemented.
@@ -962,6 +918,13 @@ function microstepProcedure(transitions, currentState, mutConfiguration, event,
962
918
  throw e;
963
919
  }
964
920
  }
921
+ function getMachineOutput(state, event, actorCtx, rootNode, rootCompletionNode) {
922
+ if (!rootNode.output) {
923
+ return;
924
+ }
925
+ const doneStateEvent = interpreter.createDoneStateEvent(rootCompletionNode.id, rootCompletionNode.output && rootCompletionNode.parent ? interpreter.resolveOutput(rootCompletionNode.output, state.context, event, actorCtx.self) : undefined);
926
+ return interpreter.resolveOutput(rootNode.output, state.context, doneStateEvent, actorCtx.self);
927
+ }
965
928
  function enterStates(currentState, event, actorCtx, filteredTransitions, mutConfiguration, internalQueue, historyValue, isInitial) {
966
929
  let nextState = currentState;
967
930
  const statesToEnter = new Set();
@@ -972,6 +935,7 @@ function enterStates(currentState, event, actorCtx, filteredTransitions, mutConf
972
935
  if (isInitial) {
973
936
  statesForDefaultEntry.add(currentState.machine.root);
974
937
  }
938
+ const completedNodes = new Set();
975
939
  for (const stateNodeToEnter of [...statesToEnter].sort((a, b) => a.order - b.order)) {
976
940
  mutConfiguration.add(stateNodeToEnter);
977
941
  const actions = [];
@@ -982,26 +946,34 @@ function enterStates(currentState, event, actorCtx, filteredTransitions, mutConf
982
946
  actions.push(invoke(invokeDef));
983
947
  }
984
948
  if (statesForDefaultEntry.has(stateNodeToEnter)) {
985
- for (const stateNode of statesForDefaultEntry) {
986
- const initialActions = stateNode.initial.actions;
987
- actions.push(...initialActions);
988
- }
949
+ const initialActions = stateNodeToEnter.initial.actions;
950
+ actions.push(...initialActions);
989
951
  }
990
- nextState = resolveActionsAndContext(nextState, event, actorCtx, actions, stateNodeToEnter.invoke.map(invokeDef => invokeDef.id));
952
+ nextState = resolveActionsAndContext(nextState, event, actorCtx, actions, internalQueue, stateNodeToEnter.invoke.map(invokeDef => invokeDef.id));
991
953
  if (stateNodeToEnter.type === 'final') {
992
954
  const parent = stateNodeToEnter.parent;
993
- if (!parent.parent) {
955
+ if (completedNodes.has(parent)) {
994
956
  continue;
995
957
  }
996
- internalQueue.push(interpreter.createDoneStateEvent(parent.id, stateNodeToEnter.output ? interpreter.resolveOutput(stateNodeToEnter.output, nextState.context, event, actorCtx.self) : undefined));
997
- if (parent.parent) {
998
- const grandparent = parent.parent;
999
- if (grandparent.type === 'parallel') {
1000
- if (getChildren(grandparent).every(parentNode => isInFinalState([...mutConfiguration], parentNode))) {
1001
- internalQueue.push(interpreter.createDoneStateEvent(grandparent.id));
1002
- }
958
+ completedNodes.add(parent);
959
+ let rootCompletionNode = parent?.type === 'parallel' ? parent : stateNodeToEnter;
960
+ let ancestorMarker = parent?.parent;
961
+ if (ancestorMarker) {
962
+ 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;
1003
968
  }
1004
969
  }
970
+ if (ancestorMarker) {
971
+ continue;
972
+ }
973
+ nextState = cloneState(nextState, {
974
+ status: 'done',
975
+ output: getMachineOutput(nextState, event, actorCtx, currentState.configuration[0].machine.root, rootCompletionNode)
976
+ });
1005
977
  }
1006
978
  }
1007
979
  return nextState;
@@ -1047,13 +1019,9 @@ function addDescendantStatesToEnter(stateNode, historyValue, statesForDefaultEnt
1047
1019
  statesToEnter.add(stateNode);
1048
1020
  if (stateNode.type === 'compound') {
1049
1021
  statesForDefaultEntry.add(stateNode);
1050
- const initialStates = stateNode.initial.target;
1051
- for (const initialState of initialStates) {
1052
- addDescendantStatesToEnter(initialState, historyValue, statesForDefaultEntry, statesToEnter);
1053
- }
1054
- for (const initialState of initialStates) {
1055
- addAncestorStatesToEnter(initialState, stateNode, statesToEnter, historyValue, statesForDefaultEntry);
1056
- }
1022
+ const [initialState] = stateNode.initial.target;
1023
+ addDescendantStatesToEnter(initialState, historyValue, statesForDefaultEntry, statesToEnter);
1024
+ addAncestorStatesToEnter(initialState, stateNode, statesToEnter, historyValue, statesForDefaultEntry);
1057
1025
  } else {
1058
1026
  if (stateNode.type === 'parallel') {
1059
1027
  for (const child of getChildren(stateNode).filter(sn => !isHistoryNode(sn))) {
@@ -1078,10 +1046,11 @@ function addAncestorStatesToEnter(stateNode, toStateNode, statesToEnter, history
1078
1046
  }
1079
1047
  }
1080
1048
  }
1081
- function exitStates(currentState, event, actorCtx, transitions, mutConfiguration, historyValue) {
1049
+ function exitStates(currentState, event, actorCtx, transitions, mutConfiguration, historyValue, internalQueue) {
1082
1050
  let nextState = currentState;
1083
1051
  const statesToExit = computeExitSet(transitions, mutConfiguration, historyValue);
1084
1052
  statesToExit.sort((a, b) => b.order - a.order);
1053
+ let changedHistory;
1085
1054
 
1086
1055
  // From SCXML algorithm: https://www.w3.org/TR/scxml/#exitStates
1087
1056
  for (const exitStateNode of statesToExit) {
@@ -1094,14 +1063,17 @@ function exitStates(currentState, event, actorCtx, transitions, mutConfiguration
1094
1063
  return sn.parent === exitStateNode;
1095
1064
  };
1096
1065
  }
1097
- historyValue[historyNode.id] = Array.from(mutConfiguration).filter(predicate);
1066
+ changedHistory ??= {
1067
+ ...historyValue
1068
+ };
1069
+ changedHistory[historyNode.id] = Array.from(mutConfiguration).filter(predicate);
1098
1070
  }
1099
1071
  }
1100
1072
  for (const s of statesToExit) {
1101
- nextState = resolveActionsAndContext(nextState, event, actorCtx, [...s.exit, ...s.invoke.map(def => stop(def.id))]);
1073
+ nextState = resolveActionsAndContext(nextState, event, actorCtx, [...s.exit, ...s.invoke.map(def => stop(def.id))], internalQueue);
1102
1074
  mutConfiguration.delete(s);
1103
1075
  }
1104
- return nextState;
1076
+ return [nextState, changedHistory || historyValue];
1105
1077
  }
1106
1078
  function resolveActionsAndContextWorker(currentState, event, actorCtx, actions, extra, retries) {
1107
1079
  const {
@@ -1166,9 +1138,10 @@ function resolveActionsAndContextWorker(currentState, event, actorCtx, actions,
1166
1138
  }
1167
1139
  return intermediateState;
1168
1140
  }
1169
- function resolveActionsAndContext(currentState, event, actorCtx, actions, deferredActorIds) {
1141
+ function resolveActionsAndContext(currentState, event, actorCtx, actions, internalQueue, deferredActorIds) {
1170
1142
  const retries = deferredActorIds ? [] : undefined;
1171
- const nextState = resolveActionsAndContextWorker(currentState, event, actorCtx, actions, deferredActorIds && {
1143
+ const nextState = resolveActionsAndContextWorker(currentState, event, actorCtx, actions, {
1144
+ internalQueue,
1172
1145
  deferredActorIds
1173
1146
  }, retries);
1174
1147
  retries?.forEach(([builtinAction, params]) => {
@@ -1176,7 +1149,7 @@ function resolveActionsAndContext(currentState, event, actorCtx, actions, deferr
1176
1149
  });
1177
1150
  return nextState;
1178
1151
  }
1179
- function macrostep(state, event, actorCtx) {
1152
+ function macrostep(state, event, actorCtx, internalQueue = []) {
1180
1153
  if (event.type === interpreter.WILDCARD) {
1181
1154
  throw new Error(`An event cannot have the wildcard type ('${interpreter.WILDCARD}')`);
1182
1155
  }
@@ -1185,7 +1158,7 @@ function macrostep(state, event, actorCtx) {
1185
1158
 
1186
1159
  // Handle stop event
1187
1160
  if (event.type === interpreter.XSTATE_STOP) {
1188
- nextState = stopStep(event, nextState, actorCtx);
1161
+ nextState = stopChildren(nextState, event, actorCtx);
1189
1162
  states.push(nextState);
1190
1163
  return {
1191
1164
  state: nextState,
@@ -1198,44 +1171,37 @@ function macrostep(state, event, actorCtx) {
1198
1171
  // Determine the next state based on the next microstep
1199
1172
  if (nextEvent.type !== interpreter.XSTATE_INIT) {
1200
1173
  const transitions = selectTransitions(nextEvent, nextState);
1201
- nextState = microstep(transitions, state, actorCtx, nextEvent, false);
1174
+ nextState = microstep(transitions, state, actorCtx, nextEvent, false, internalQueue);
1202
1175
  states.push(nextState);
1203
1176
  }
1177
+ let shouldSelectEventlessTransitions = true;
1204
1178
  while (nextState.status === 'active') {
1205
- let enabledTransitions = selectEventlessTransitions(nextState, nextEvent);
1179
+ let enabledTransitions = shouldSelectEventlessTransitions ? selectEventlessTransitions(nextState, nextEvent) : [];
1180
+
1181
+ // eventless transitions should always be selected after selecting *regular* transitions
1182
+ // by assigning `undefined` to `previousState` we ensure that `shouldSelectEventlessTransitions` gets always computed to true in such a case
1183
+ const previousState = enabledTransitions.length ? nextState : undefined;
1206
1184
  if (!enabledTransitions.length) {
1207
- if (!nextState._internalQueue.length) {
1185
+ if (!internalQueue.length) {
1208
1186
  break;
1209
- } else {
1210
- nextEvent = nextState._internalQueue[0];
1211
- const transitions = selectTransitions(nextEvent, nextState);
1212
- nextState = microstep(transitions, nextState, actorCtx, nextEvent, false);
1213
- nextState._internalQueue.shift();
1214
- states.push(nextState);
1215
1187
  }
1216
- } else {
1217
- nextState = microstep(enabledTransitions, nextState, actorCtx, nextEvent, false);
1218
- states.push(nextState);
1188
+ nextEvent = internalQueue.shift();
1189
+ enabledTransitions = selectTransitions(nextEvent, nextState);
1219
1190
  }
1191
+ nextState = microstep(enabledTransitions, nextState, actorCtx, nextEvent, false, internalQueue);
1192
+ shouldSelectEventlessTransitions = nextState !== previousState;
1193
+ states.push(nextState);
1220
1194
  }
1221
1195
  if (nextState.status !== 'active') {
1222
- // Perform the stop step to ensure that child actors are stopped
1223
- stopStep(nextEvent, nextState, actorCtx);
1196
+ stopChildren(nextState, nextEvent, actorCtx);
1224
1197
  }
1225
1198
  return {
1226
1199
  state: nextState,
1227
1200
  microstates: states
1228
1201
  };
1229
1202
  }
1230
- function stopStep(event, nextState, actorCtx) {
1231
- const actions = [];
1232
- for (const stateNode of nextState.configuration.sort((a, b) => b.order - a.order)) {
1233
- actions.push(...stateNode.exit);
1234
- }
1235
- for (const child of Object.values(nextState.children)) {
1236
- actions.push(stop(child));
1237
- }
1238
- return resolveActionsAndContext(nextState, event, actorCtx, actions);
1203
+ function stopChildren(nextState, event, actorCtx) {
1204
+ return resolveActionsAndContext(nextState, event, actorCtx, Object.values(nextState.children).map(child => stop(child)), []);
1239
1205
  }
1240
1206
  function selectTransitions(event, nextState) {
1241
1207
  return nextState.machine.getTransitionData(nextState, event);
@@ -1341,11 +1307,9 @@ class State {
1341
1307
  this.error = void 0;
1342
1308
  this.context = void 0;
1343
1309
  this.historyValue = {};
1344
- this._internalQueue = void 0;
1345
1310
  this.configuration = void 0;
1346
1311
  this.children = void 0;
1347
1312
  this.context = config.context;
1348
- this._internalQueue = config._internalQueue ?? [];
1349
1313
  this.historyValue = config.historyValue || {};
1350
1314
  this.matches = this.matches.bind(this);
1351
1315
  this.toStrings = this.toStrings.bind(this);
@@ -1447,11 +1411,15 @@ function getPersistedState(state) {
1447
1411
  tags,
1448
1412
  machine,
1449
1413
  children,
1414
+ context,
1450
1415
  ...jsonValues
1451
1416
  } = state;
1452
1417
  const childrenJson = {};
1453
1418
  for (const id in children) {
1454
1419
  const child = children[id];
1420
+ if (typeof child.src !== 'string') {
1421
+ throw new Error('An inline child actor cannot be persisted.');
1422
+ }
1455
1423
  childrenJson[id] = {
1456
1424
  state: child.getPersistedState?.(),
1457
1425
  src: child.src
@@ -1459,14 +1427,45 @@ function getPersistedState(state) {
1459
1427
  }
1460
1428
  return {
1461
1429
  ...jsonValues,
1430
+ // TODO: this makes `PersistedMachineState`'s type kind of a lie
1431
+ // it doesn't truly use `TContext` but rather some kind of a derived form of it
1432
+ context: persistContext(context),
1462
1433
  children: childrenJson
1463
1434
  };
1464
1435
  }
1436
+ function persistContext(contextPart) {
1437
+ let copy;
1438
+ for (const key in contextPart) {
1439
+ const value = contextPart[key];
1440
+ if (value && typeof value === 'object') {
1441
+ if ('sessionId' in value && 'send' in value && 'ref' in value) {
1442
+ copy ??= Array.isArray(contextPart) ? contextPart.slice() : {
1443
+ ...contextPart
1444
+ };
1445
+ copy[key] = {
1446
+ xstate$$type: interpreter.$$ACTOR_TYPE,
1447
+ id: value.id
1448
+ };
1449
+ } else {
1450
+ const result = persistContext(value);
1451
+ if (result !== value) {
1452
+ copy ??= Array.isArray(contextPart) ? contextPart.slice() : {
1453
+ ...contextPart
1454
+ };
1455
+ copy[key] = result;
1456
+ }
1457
+ }
1458
+ }
1459
+ }
1460
+ return copy ?? contextPart;
1461
+ }
1465
1462
 
1466
1463
  function resolveRaise(_, state, args, {
1467
1464
  event: eventOrExpr,
1468
1465
  id,
1469
1466
  delay
1467
+ }, {
1468
+ internalQueue
1470
1469
  }) {
1471
1470
  const delaysMap = state.machine.implementations.delays;
1472
1471
  if (typeof eventOrExpr === 'string') {
@@ -1480,9 +1479,10 @@ function resolveRaise(_, state, args, {
1480
1479
  } else {
1481
1480
  resolvedDelay = typeof delay === 'function' ? delay(args) : delay;
1482
1481
  }
1483
- return [typeof resolvedDelay !== 'number' ? cloneState(state, {
1484
- _internalQueue: state._internalQueue.concat(resolvedEvent)
1485
- }) : state, {
1482
+ if (typeof resolvedDelay !== 'number') {
1483
+ internalQueue.push(resolvedEvent);
1484
+ }
1485
+ return [state, {
1486
1486
  event: resolvedEvent,
1487
1487
  id,
1488
1488
  delay: resolvedDelay
@@ -1527,10 +1527,10 @@ exports.getCandidates = getCandidates;
1527
1527
  exports.getConfiguration = getConfiguration;
1528
1528
  exports.getDelayedTransitions = getDelayedTransitions;
1529
1529
  exports.getInitialConfiguration = getInitialConfiguration;
1530
+ exports.getInitialStateNodes = getInitialStateNodes;
1530
1531
  exports.getPersistedState = getPersistedState;
1531
1532
  exports.getStateNodeByPath = getStateNodeByPath;
1532
1533
  exports.getStateNodes = getStateNodes;
1533
- exports.isAtomicStateNode = isAtomicStateNode;
1534
1534
  exports.isInFinalState = isInFinalState;
1535
1535
  exports.isStateId = isStateId;
1536
1536
  exports.macrostep = macrostep;