xstate 6.0.0-alpha.2 → 6.0.0-alpha.21

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 (54) hide show
  1. package/README.md +19 -17
  2. package/bin/xstate.js +36 -0
  3. package/dist/{StateMachine-64813f46.cjs.js → StateMachine-1b155670.cjs.js} +182 -40
  4. package/dist/{StateMachine-2dcdb1f5.esm.js → StateMachine-3c17fe3e.esm.js} +182 -40
  5. package/dist/{StateMachine-8d22a79c.development.cjs.js → StateMachine-550ff7f2.development.cjs.js} +193 -36
  6. package/dist/{StateMachine-2ea0a7fa.development.esm.js → StateMachine-e25dd61f.development.esm.js} +193 -36
  7. package/dist/declarations/src/State.d.ts +13 -2
  8. package/dist/declarations/src/StateMachine.d.ts +23 -13
  9. package/dist/declarations/src/actions.d.ts +2 -1
  10. package/dist/declarations/src/actors/promise.d.ts +4 -2
  11. package/dist/declarations/src/createActor.d.ts +9 -11
  12. package/dist/declarations/src/createMachine.d.ts +14 -11
  13. package/dist/declarations/src/createMachineFromConfig.d.ts +61 -28
  14. package/dist/declarations/src/fsm.d.ts +74 -0
  15. package/dist/declarations/src/graph/graph.d.ts +3 -3
  16. package/dist/declarations/src/graph/shortestPaths.d.ts +2 -2
  17. package/dist/declarations/src/graph/simplePaths.d.ts +2 -2
  18. package/dist/declarations/src/index.d.ts +6 -5
  19. package/dist/declarations/src/serialize.d.ts +10 -18
  20. package/dist/declarations/src/setup.d.ts +277 -65
  21. package/dist/declarations/src/spawn.d.ts +3 -3
  22. package/dist/declarations/src/stateUtils.d.ts +4 -1
  23. package/dist/declarations/src/system.d.ts +1 -1
  24. package/dist/declarations/src/transition.d.ts +8 -5
  25. package/dist/declarations/src/transitionActions.d.ts +12 -0
  26. package/dist/declarations/src/types.d.ts +237 -102
  27. package/dist/declarations/src/types.v6.d.ts +132 -58
  28. package/dist/declarations/src/utils.d.ts +4 -3
  29. package/dist/{index-93edd3bd.esm.js → index-3f87408c.esm.js} +936 -1389
  30. package/dist/{index-e61170ba.development.esm.js → index-d39fb138.development.esm.js} +968 -1421
  31. package/dist/{index-9cb3b3a0.cjs.js → index-e694228c.cjs.js} +945 -1394
  32. package/dist/{index-f6ef20d1.development.cjs.js → index-ec7c7a2d.development.cjs.js} +977 -1426
  33. package/dist/xstate-actors.cjs.js +1 -1
  34. package/dist/xstate-actors.development.cjs.js +1 -1
  35. package/dist/xstate-actors.development.esm.js +1 -1
  36. package/dist/xstate-actors.esm.js +1 -1
  37. package/dist/xstate-actors.umd.min.js +1 -1
  38. package/dist/xstate-actors.umd.min.js.map +1 -1
  39. package/dist/xstate-graph.cjs.js +2 -2
  40. package/dist/xstate-graph.development.cjs.js +2 -2
  41. package/dist/xstate-graph.development.esm.js +2 -2
  42. package/dist/xstate-graph.esm.js +2 -2
  43. package/dist/xstate-graph.umd.min.js +1 -1
  44. package/dist/xstate-graph.umd.min.js.map +1 -1
  45. package/dist/xstate.cjs.js +1100 -182
  46. package/dist/xstate.cjs.mjs +3 -5
  47. package/dist/xstate.development.cjs.js +1100 -182
  48. package/dist/xstate.development.cjs.mjs +3 -5
  49. package/dist/xstate.development.esm.js +1101 -181
  50. package/dist/xstate.esm.js +1101 -181
  51. package/dist/xstate.umd.min.js +1 -1
  52. package/dist/xstate.umd.min.js.map +1 -1
  53. package/package.json +6 -2
  54. package/dist/declarations/src/atom.d.ts +0 -81
@@ -1,4 +1,4 @@
1
- import { P as ProcessingStatus, G as resolveReferencedActor, c as createActor, S as STATE_DELIMITER, H as mapValues, t as toArray, I as createInvokeId, J as getDelayedTransitions, K as evaluateCandidate, L as formatTransition, N as NULL_EVENT, M as toTransitionConfigArray, O as createInvokeTimeoutEvent, Q as getCandidates, R as formatRouteTransitions, U as resolveStateValue, V as getAllStateNodes, n as getStateNodes, W as createMachineSnapshot, X as isInFinalState, a as macrostep, Y as transitionNode, m as matchesEventDescriptor, Z as resolveAndExecuteActionsWithContext, b as createInitEvent, d as initialMicrostep, _ as toStatePath, $ as isStateId, a0 as getStateNodeByPath, a1 as getPersistedSnapshot, a2 as $$ACTOR_TYPE } from './index-93edd3bd.esm.js';
1
+ import { P as ProcessingStatus, H as resolveReferencedActor, e as createActor, S as STATE_DELIMITER, I as mapValues, t as toArray, J as createInvokeId, K as getDelayedTransitions, L as evaluateCandidate, M as formatTransition, N as NULL_EVENT, O as toTransitionConfigArray, Q as createInvokeTimeoutEvent, R as getCandidates, U as formatRouteTransitions, V as resolveStateValue, W as getAllStateNodes, l as getStateNodes, Y as createMachineSnapshot, Z as isInFinalState, f as macrostep, d as deriveDeferredStarts, _ as cloneMachineSnapshot, $ as transitionNode, m as matchesEventDescriptor, a0 as getTransitionResult, a1 as hasEffect, a2 as createErrorPlatformEvent, r as resolveActionsWithContext, g as createInitEvent, h as initialMicrostep, a3 as toStatePath, a4 as isStateId, a5 as getStateNodeByPath, a6 as getPersistedSnapshot, a7 as $$ACTOR_TYPE } from './index-3f87408c.esm.js';
2
2
 
3
3
  function createSpawner(actorScope, {
4
4
  machine,
@@ -20,7 +20,7 @@ function createSpawner(actorScope, {
20
20
  self: actorScope.self
21
21
  }) : options?.input,
22
22
  src,
23
- systemId: options?.systemId
23
+ registryKey: options?.registryKey
24
24
  });
25
25
  spawnedChildren[actor.id] = actor;
26
26
  return actor;
@@ -31,7 +31,7 @@ function createSpawner(actorScope, {
31
31
  syncSnapshot: options?.syncSnapshot,
32
32
  input: options?.input,
33
33
  src,
34
- systemId: options?.systemId
34
+ registryKey: options?.registryKey
35
35
  });
36
36
  return actor;
37
37
  }
@@ -64,7 +64,6 @@ function memo(object, key, fn) {
64
64
  }
65
65
 
66
66
  const EMPTY_OBJECT = {};
67
- const CHOICE_CONFIG_KEYS = ['invoke', 'after', 'on', 'entry', 'exit', 'always', 'states', 'initial', 'onDone', 'timeout', 'onTimeout', 'history', 'target', 'output'];
68
67
  class StateNode {
69
68
  constructor(/** The raw config used to create the machine. */
70
69
  config, options) {
@@ -171,7 +170,7 @@ class StateNode {
171
170
  this.invoke = toArray(this.config.invoke).map((invokeConfig, i) => {
172
171
  const {
173
172
  src,
174
- systemId
173
+ registryKey
175
174
  } = invokeConfig;
176
175
  const invokeId = createInvokeId(this.id, i);
177
176
  const resolvedId = invokeConfig.id ?? invokeId;
@@ -184,7 +183,7 @@ class StateNode {
184
183
  src: sourceName,
185
184
  logic: src,
186
185
  id: resolvedId,
187
- systemId
186
+ registryKey
188
187
  };
189
188
  });
190
189
  }
@@ -244,30 +243,17 @@ class StateNode {
244
243
  function validateStateNodeConfig(stateNode) {
245
244
  const config = stateNode.config;
246
245
  if (stateNode.type !== 'choice') {
247
- if (config.choice !== undefined) {
248
- throw new Error(`State "${stateNode.id}" has \`choice\`, but \`choice\` can only be used with \`type: 'choice'\`.`);
249
- }
250
246
  return;
251
247
  }
252
248
  if (typeof config.choice !== 'function') {
253
- throw new Error(`Choice state "${stateNode.id}" must declare a \`choice\` function.`);
254
- }
255
- for (const key of CHOICE_CONFIG_KEYS) {
256
- if (config[key] !== undefined) {
257
- throw new Error(`Choice state "${stateNode.id}" cannot declare \`${key}\`.`);
258
- }
249
+ throw new Error(`Missing \`choice\` function on "${stateNode.id}"`);
259
250
  }
260
251
  }
261
252
  function formatChoiceTransitions(stateNode) {
262
253
  const choice = stateNode.config.choice;
263
254
  const validateChoiceResult = result => {
264
255
  if (!result || result.target === undefined) {
265
- throw new Error(`Choice state "${stateNode.id}" must resolve to a target.`);
266
- }
267
- for (const key of ['actions', 'to', 'context']) {
268
- if (result[key] !== undefined) {
269
- throw new Error(`Choice state "${stateNode.id}" cannot declare \`${key}\` on a choice.`);
270
- }
256
+ throw new Error(`Choice "${stateNode.id}" has no target`);
271
257
  }
272
258
  return result;
273
259
  };
@@ -288,7 +274,7 @@ function formatTransitions(stateNode) {
288
274
  if (stateNode.config.on) {
289
275
  for (const descriptor of Object.keys(stateNode.config.on)) {
290
276
  if (descriptor === NULL_EVENT) {
291
- throw new Error('Null events ("") cannot be specified as a transition key. Use `always: { ... }` instead.');
277
+ throw new Error('Null event transition key');
292
278
  }
293
279
  const transitionsConfig = stateNode.config.on[descriptor];
294
280
  transitions.set(descriptor, mapTransitionConfigs(transitionsConfig, transition => formatTransition(stateNode, descriptor, transition)));
@@ -298,6 +284,10 @@ function formatTransitions(stateNode) {
298
284
  const descriptor = `xstate.done.state.${stateNode.id}`;
299
285
  transitions.set(descriptor, mapTransitionConfigs(stateNode.config.onDone, transition => formatTransition(stateNode, descriptor, transition)));
300
286
  }
287
+ if (stateNode.config.onError) {
288
+ const descriptor = 'xstate.error.*';
289
+ transitions.set(descriptor, mapTransitionConfigs(stateNode.config.onError, transition => formatTransition(stateNode, descriptor, transition)));
290
+ }
301
291
  const createCancelInvokeTimeoutTransition = (descriptor, timeoutEventType) => formatTransition(stateNode, descriptor, {
302
292
  to: (_args, enq) => {
303
293
  enq.cancel(timeoutEventType);
@@ -384,7 +374,7 @@ function formatInitialTransition(stateNode, _target) {
384
374
  const input = typeof _target === 'object' && _target !== null ? _target.input : undefined;
385
375
  const resolvedTarget = typeof targetString === 'string' ? stateNode.states[targetString] : undefined;
386
376
  if (!resolvedTarget && targetString) {
387
- throw new Error(`Initial state node "${targetString}" not found on parent state node #${stateNode.id}`);
377
+ throw new Error(`Initial state "${targetString}" not found on "#${stateNode.id}"`);
388
378
  }
389
379
  const transition = {
390
380
  source: stateNode,
@@ -395,6 +385,45 @@ function formatInitialTransition(stateNode, _target) {
395
385
  }
396
386
 
397
387
  const STATE_IDENTIFIER = '#';
388
+ let emptyCanActor;
389
+ let emptyCanActorScope;
390
+ function getEmptyCanActor() {
391
+ // A minimal inert actor used purely as the `self`/`parent` argument when
392
+ // dry-running transitions for `snapshot.can(...)`. Intentionally not built
393
+ // on `createLogic` so `can()` does not pull that machinery into bundles.
394
+ return emptyCanActor ??= createActor({
395
+ transition: snapshot => [snapshot, []],
396
+ initialTransition: () => [{
397
+ status: 'active',
398
+ output: undefined,
399
+ error: undefined
400
+ }, []],
401
+ getInitialSnapshot: () => ({
402
+ status: 'active',
403
+ output: undefined,
404
+ error: undefined
405
+ }),
406
+ getPersistedSnapshot: snapshot => snapshot
407
+ });
408
+ }
409
+ function getEmptyCanActorScope() {
410
+ if (emptyCanActorScope) {
411
+ return emptyCanActorScope;
412
+ }
413
+ const actor = getEmptyCanActor();
414
+ emptyCanActorScope = {
415
+ self: actor,
416
+ logger: () => {},
417
+ id: '',
418
+ sessionId: '',
419
+ defer: () => {},
420
+ system: actor.system,
421
+ stopChild: () => {},
422
+ emit: () => {},
423
+ actionExecutor: () => {}
424
+ };
425
+ return emptyCanActorScope;
426
+ }
398
427
  class StateMachine {
399
428
  constructor(/** The raw config used to create the machine. */
400
429
  config, implementations) {
@@ -422,7 +451,7 @@ class StateMachine {
422
451
  this._json = void 0;
423
452
  this.id = config.id || '(machine)';
424
453
  this.implementations = {
425
- actors: config.actors ?? {},
454
+ actorSources: config.actorSources ?? {},
426
455
  actions: config.actions ?? {},
427
456
  delays: config.delays ?? {},
428
457
  guards: config.guards ?? {},
@@ -436,6 +465,7 @@ class StateMachine {
436
465
  ...this.config.options
437
466
  };
438
467
  this.transition = this.transition.bind(this);
468
+ this.initialTransition = this.initialTransition.bind(this);
439
469
  this.getInitialSnapshot = this.getInitialSnapshot.bind(this);
440
470
  this.getPersistedSnapshot = this.getPersistedSnapshot.bind(this);
441
471
  this.restoreSnapshot = this.restoreSnapshot.bind(this);
@@ -454,15 +484,15 @@ class StateMachine {
454
484
  /**
455
485
  * Clones this state machine with the provided implementations.
456
486
  *
457
- * @param implementations Options (`actions`, `guards`, `actors`, `delays`) to
458
- * recursively merge with the existing options.
487
+ * @param implementations Options (`actions`, `guards`, `actorSources`,
488
+ * `delays`) to recursively merge with the existing options.
459
489
  * @returns A new `StateMachine` instance with the provided implementations.
460
490
  */
461
491
  provide(implementations) {
462
492
  const {
463
493
  actions,
464
494
  guards,
465
- actors,
495
+ actorSources,
466
496
  delays
467
497
  } = this.implementations;
468
498
  const provided = new StateMachine(this.config, {
@@ -474,9 +504,9 @@ class StateMachine {
474
504
  ...guards,
475
505
  ...implementations.guards
476
506
  },
477
- actors: {
478
- ...actors,
479
- ...implementations.actors
507
+ actorSources: {
508
+ ...actorSources,
509
+ ...implementations.actorSources
480
510
  },
481
511
  delays: {
482
512
  ...delays,
@@ -511,7 +541,56 @@ class StateMachine {
511
541
  * @param event The received event
512
542
  */
513
543
  transition(snapshot, event, actorScope) {
514
- return macrostep(snapshot, event, actorScope, []).snapshot;
544
+ const fastSnapshot = this._transitionFast(snapshot, event, actorScope);
545
+ if (fastSnapshot) {
546
+ return [fastSnapshot, []];
547
+ }
548
+ const {
549
+ snapshot: nextSnapshot,
550
+ microsteps
551
+ } = macrostep(snapshot, event, actorScope, []);
552
+ return [nextSnapshot, this._collectEffects(microsteps)];
553
+ }
554
+ _collectEffects(microsteps, initialActions = []) {
555
+ const effects = [...initialActions, ...microsteps.flatMap(([, actions]) => actions)];
556
+ return [...effects, ...deriveDeferredStarts(effects)];
557
+ }
558
+ _transitionFast(snapshot, event, actorScope) {
559
+ if (snapshot.status !== 'active' || typeof snapshot.value !== 'string' || this.root.always?.length) {
560
+ return undefined;
561
+ }
562
+ const sourceNode = this.root.states[snapshot.value];
563
+ if (!sourceNode || sourceNode.type !== 'atomic' || sourceNode.exit || sourceNode.invoke.length || sourceNode.always?.length || sourceNode.after?.length) {
564
+ return undefined;
565
+ }
566
+ const transitions = sourceNode.transitions.get(event.type);
567
+ if (transitions?.length !== 1) {
568
+ return undefined;
569
+ }
570
+ const selected = transitions[0];
571
+ if (selected.guard || selected.actions || selected.to || selected.reenter || selected.input || typeof selected.context === 'function' || selected.target && selected.target.length !== 1) {
572
+ return undefined;
573
+ }
574
+ const targetNode = selected.target?.[0] ?? sourceNode;
575
+ const stateChanged = targetNode !== sourceNode;
576
+ if (targetNode.parent !== this.root || targetNode.type !== 'atomic' || stateChanged && (targetNode.entry || targetNode.invoke.length || targetNode.always?.length || targetNode.after?.length)) {
577
+ return undefined;
578
+ }
579
+ const context = selected.context !== undefined ? {
580
+ ...snapshot.context,
581
+ ...selected.context
582
+ } : snapshot.context;
583
+ const collectedMicrosteps = actorScope.self._collectedMicrosteps || [];
584
+ collectedMicrosteps.push(selected);
585
+ actorScope.self._collectedMicrosteps = collectedMicrosteps;
586
+ return cloneMachineSnapshot(snapshot, {
587
+ ...(context !== snapshot.context ? {
588
+ context
589
+ } : {}),
590
+ ...(stateChanged ? {
591
+ _nodes: [this.root, targetNode]
592
+ } : {})
593
+ });
515
594
  }
516
595
 
517
596
  /**
@@ -543,6 +622,49 @@ class StateMachine {
543
622
  return false;
544
623
  }
545
624
 
625
+ /**
626
+ * Determines whether sending the `event` to the given snapshot would select a
627
+ * non-forbidden transition. Backs `snapshot.can(...)`; lives here so that
628
+ * non-machine bundles don't pay for the transition-resolution machinery.
629
+ *
630
+ * @internal
631
+ */
632
+ _canTransition(snapshot, event) {
633
+ const emptyActor = getEmptyCanActor();
634
+ const emptyActorScope = getEmptyCanActorScope();
635
+ const transitionData = this.getTransitionData(snapshot, event, emptyActor);
636
+ if (!transitionData?.length) {
637
+ return false;
638
+ }
639
+
640
+ // Check that at least one transition is not forbidden
641
+ for (const transition of transitionData) {
642
+ if (transition.target !== undefined) {
643
+ return true;
644
+ }
645
+ const res = getTransitionResult(transition, snapshot, event, emptyActorScope, {
646
+ resolveActions: false
647
+ });
648
+ if (res.targets?.length || res.context || hasEffect(transition, snapshot.context, event, snapshot, emptyActor)) {
649
+ return true;
650
+ }
651
+ }
652
+ return false;
653
+ }
654
+
655
+ /**
656
+ * Returns the error event that the actor should transition with to recover
657
+ * from an execution error, if any active state node declares `onError`.
658
+ *
659
+ * @internal
660
+ */
661
+ getExecutionErrorEvent(snapshot, error) {
662
+ if (snapshot?.status !== 'active' || !snapshot._nodes?.some(stateNode => stateNode.config.onError)) {
663
+ return undefined;
664
+ }
665
+ return createErrorPlatformEvent('execution', error);
666
+ }
667
+
546
668
  /**
547
669
  * The initial state _before_ evaluating any microsteps. This "pre-initial"
548
670
  * state is provided to initial actions executed in the initial state.
@@ -566,9 +688,9 @@ class StateMachine {
566
688
  spawn,
567
689
  input: initEvent.input,
568
690
  self: actorScope.self,
569
- actors: this.implementations.actors
691
+ actorSources: this.implementations.actorSources
570
692
  });
571
- const nextState = resolveAndExecuteActionsWithContext(preInitial, initEvent, actorScope, []);
693
+ const [nextState] = resolveActionsWithContext(preInitial, initEvent, actorScope, []);
572
694
  if (resolvedContext) {
573
695
  nextState.context = resolvedContext;
574
696
  }
@@ -588,14 +710,33 @@ class StateMachine {
588
710
  * `ActorRef`.
589
711
  */
590
712
  getInitialSnapshot(actorScope, input) {
713
+ return this.initialTransition(input, actorScope)[0];
714
+ }
715
+ initialTransition(input, actorScope) {
591
716
  const initEvent = createInitEvent(input); // TODO: fix;
592
717
  const internalQueue = [];
593
718
  const preInitialState = this._getPreInitialState(actorScope, initEvent);
594
- const [nextState] = initialMicrostep(this.root, preInitialState, actorScope, initEvent, internalQueue);
595
- const {
596
- snapshot: macroState
597
- } = macrostep(nextState, initEvent, actorScope, internalQueue);
598
- return macroState;
719
+ let nextState;
720
+ let initialActions = [];
721
+ let microsteps = [];
722
+ let macroState;
723
+ try {
724
+ [nextState, initialActions] = initialMicrostep(this.root, preInitialState, actorScope, initEvent, internalQueue);
725
+ ({
726
+ snapshot: macroState,
727
+ microsteps
728
+ } = macrostep(nextState, initEvent, actorScope, internalQueue));
729
+ } catch (err) {
730
+ if (!this.root.config.onError) {
731
+ throw err;
732
+ }
733
+ const errorEvent = createErrorPlatformEvent('execution', err);
734
+ const errorMacrostep = macrostep(preInitialState, errorEvent, actorScope, []);
735
+ macroState = errorMacrostep.snapshot;
736
+ microsteps = errorMacrostep.microsteps;
737
+ initialActions = [];
738
+ }
739
+ return [macroState, this._collectEffects(microsteps, initialActions)];
599
740
  }
600
741
  start(snapshot) {
601
742
  // Start rehydrated children that were active when persisted. Freshly
@@ -649,7 +790,7 @@ class StateMachine {
649
790
  syncSnapshot: actorData.syncSnapshot,
650
791
  snapshot: childState,
651
792
  src,
652
- systemId: actorData.systemId
793
+ registryKey: actorData.registryKey
653
794
  });
654
795
  // Mark so `start()` knows to start this child (freshly invoked/spawned
655
796
  // children are started via deferred `@xstate.start` actions instead).
@@ -693,7 +834,8 @@ class StateMachine {
693
834
  children,
694
835
  _nodes: nodes,
695
836
  value: snapshotData.value,
696
- historyValue: revivedHistoryValue
837
+ historyValue: revivedHistoryValue,
838
+ _stateInputs: snapshotData.stateInputs ?? {}
697
839
  }, this);
698
840
  const seen = new WeakSet();
699
841
  function reviveContext(contextPart) {