xstate 6.0.0-alpha.2 → 6.0.0-alpha.20

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-2dcdb1f5.esm.js → StateMachine-42f1d26c.esm.js} +182 -40
  4. package/dist/{StateMachine-8d22a79c.development.cjs.js → StateMachine-6661eb0d.development.cjs.js} +193 -36
  5. package/dist/{StateMachine-2ea0a7fa.development.esm.js → StateMachine-ba3ccedf.development.esm.js} +193 -36
  6. package/dist/{StateMachine-64813f46.cjs.js → StateMachine-f2b70732.cjs.js} +182 -40
  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 +266 -46
  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 +129 -58
  28. package/dist/declarations/src/utils.d.ts +3 -2
  29. package/dist/{index-9cb3b3a0.cjs.js → index-42956779.cjs.js} +977 -1419
  30. package/dist/{index-f6ef20d1.development.cjs.js → index-49293864.development.cjs.js} +981 -1423
  31. package/dist/{index-93edd3bd.esm.js → index-d88b0c55.esm.js} +968 -1414
  32. package/dist/{index-e61170ba.development.esm.js → index-dc3443b6.development.esm.js} +972 -1418
  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,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var dist_xstateActors = require('./index-f6ef20d1.development.cjs.js');
3
+ var dist_xstateActors = require('./index-49293864.development.cjs.js');
4
4
 
5
5
  function createSpawner(actorScope, {
6
6
  machine,
@@ -22,7 +22,7 @@ function createSpawner(actorScope, {
22
22
  self: actorScope.self
23
23
  }) : options?.input,
24
24
  src,
25
- systemId: options?.systemId
25
+ registryKey: options?.registryKey
26
26
  });
27
27
  spawnedChildren[actor.id] = actor;
28
28
  return actor;
@@ -33,7 +33,7 @@ function createSpawner(actorScope, {
33
33
  syncSnapshot: options?.syncSnapshot,
34
34
  input: options?.input,
35
35
  src,
36
- systemId: options?.systemId
36
+ registryKey: options?.registryKey
37
37
  });
38
38
  return actor;
39
39
  }
@@ -173,7 +173,7 @@ class StateNode {
173
173
  this.invoke = dist_xstateActors.toArray(this.config.invoke).map((invokeConfig, i) => {
174
174
  const {
175
175
  src,
176
- systemId
176
+ registryKey
177
177
  } = invokeConfig;
178
178
  const invokeId = dist_xstateActors.createInvokeId(this.id, i);
179
179
  const resolvedId = invokeConfig.id ?? invokeId;
@@ -186,7 +186,7 @@ class StateNode {
186
186
  src: sourceName,
187
187
  logic: src,
188
188
  id: resolvedId,
189
- systemId
189
+ registryKey
190
190
  };
191
191
  });
192
192
  }
@@ -252,11 +252,13 @@ function validateStateNodeConfig(stateNode) {
252
252
  return;
253
253
  }
254
254
  if (typeof config.choice !== 'function') {
255
- throw new Error(`Choice state "${stateNode.id}" must declare a \`choice\` function.`);
255
+ throw new Error(`Choice state "${stateNode.id}" must declare a \`choice\` function.` );
256
256
  }
257
- for (const key of CHOICE_CONFIG_KEYS) {
258
- if (config[key] !== undefined) {
259
- throw new Error(`Choice state "${stateNode.id}" cannot declare \`${key}\`.`);
257
+ {
258
+ for (const key of CHOICE_CONFIG_KEYS) {
259
+ if (config[key] !== undefined) {
260
+ throw new Error(`Choice state "${stateNode.id}" cannot declare \`${key}\`.`);
261
+ }
260
262
  }
261
263
  }
262
264
  }
@@ -264,11 +266,13 @@ function formatChoiceTransitions(stateNode) {
264
266
  const choice = stateNode.config.choice;
265
267
  const validateChoiceResult = result => {
266
268
  if (!result || result.target === undefined) {
267
- throw new Error(`Choice state "${stateNode.id}" must resolve to a target.`);
269
+ throw new Error(`Choice state "${stateNode.id}" must resolve to a target.` );
268
270
  }
269
- for (const key of ['actions', 'to', 'context']) {
270
- if (result[key] !== undefined) {
271
- throw new Error(`Choice state "${stateNode.id}" cannot declare \`${key}\` on a choice.`);
271
+ {
272
+ for (const key of ['actions', 'to']) {
273
+ if (result[key] !== undefined) {
274
+ throw new Error(`Choice state "${stateNode.id}" cannot declare \`${key}\` on a choice.`);
275
+ }
272
276
  }
273
277
  }
274
278
  return result;
@@ -290,7 +294,7 @@ function formatTransitions(stateNode) {
290
294
  if (stateNode.config.on) {
291
295
  for (const descriptor of Object.keys(stateNode.config.on)) {
292
296
  if (descriptor === dist_xstateActors.NULL_EVENT) {
293
- throw new Error('Null events ("") cannot be specified as a transition key. Use `always: { ... }` instead.');
297
+ throw new Error('Null events ("") cannot be specified as a transition key. Use `always: { ... }` instead.' );
294
298
  }
295
299
  const transitionsConfig = stateNode.config.on[descriptor];
296
300
  transitions.set(descriptor, mapTransitionConfigs(transitionsConfig, transition => dist_xstateActors.formatTransition(stateNode, descriptor, transition)));
@@ -300,6 +304,10 @@ function formatTransitions(stateNode) {
300
304
  const descriptor = `xstate.done.state.${stateNode.id}`;
301
305
  transitions.set(descriptor, mapTransitionConfigs(stateNode.config.onDone, transition => dist_xstateActors.formatTransition(stateNode, descriptor, transition)));
302
306
  }
307
+ if (stateNode.config.onError) {
308
+ const descriptor = 'xstate.error.*';
309
+ transitions.set(descriptor, mapTransitionConfigs(stateNode.config.onError, transition => dist_xstateActors.formatTransition(stateNode, descriptor, transition)));
310
+ }
303
311
  const createCancelInvokeTimeoutTransition = (descriptor, timeoutEventType) => dist_xstateActors.formatTransition(stateNode, descriptor, {
304
312
  to: (_args, enq) => {
305
313
  enq.cancel(timeoutEventType);
@@ -386,7 +394,7 @@ function formatInitialTransition(stateNode, _target) {
386
394
  const input = typeof _target === 'object' && _target !== null ? _target.input : undefined;
387
395
  const resolvedTarget = typeof targetString === 'string' ? stateNode.states[targetString] : undefined;
388
396
  if (!resolvedTarget && targetString) {
389
- throw new Error(`Initial state node "${targetString}" not found on parent state node #${stateNode.id}`);
397
+ throw new Error(`Initial state node "${targetString}" not found on parent state node #${stateNode.id}` );
390
398
  }
391
399
  const transition = {
392
400
  source: stateNode,
@@ -397,6 +405,45 @@ function formatInitialTransition(stateNode, _target) {
397
405
  }
398
406
 
399
407
  const STATE_IDENTIFIER = '#';
408
+ let emptyCanActor;
409
+ let emptyCanActorScope;
410
+ function getEmptyCanActor() {
411
+ // A minimal inert actor used purely as the `self`/`parent` argument when
412
+ // dry-running transitions for `snapshot.can(...)`. Intentionally not built
413
+ // on `createLogic` so `can()` does not pull that machinery into bundles.
414
+ return emptyCanActor ??= dist_xstateActors.createActor({
415
+ transition: snapshot => [snapshot, []],
416
+ initialTransition: () => [{
417
+ status: 'active',
418
+ output: undefined,
419
+ error: undefined
420
+ }, []],
421
+ getInitialSnapshot: () => ({
422
+ status: 'active',
423
+ output: undefined,
424
+ error: undefined
425
+ }),
426
+ getPersistedSnapshot: snapshot => snapshot
427
+ });
428
+ }
429
+ function getEmptyCanActorScope() {
430
+ if (emptyCanActorScope) {
431
+ return emptyCanActorScope;
432
+ }
433
+ const actor = getEmptyCanActor();
434
+ emptyCanActorScope = {
435
+ self: actor,
436
+ logger: () => {},
437
+ id: '',
438
+ sessionId: '',
439
+ defer: () => {},
440
+ system: actor.system,
441
+ stopChild: () => {},
442
+ emit: () => {},
443
+ actionExecutor: () => {}
444
+ };
445
+ return emptyCanActorScope;
446
+ }
400
447
  class StateMachine {
401
448
  constructor(/** The raw config used to create the machine. */
402
449
  config, implementations) {
@@ -424,7 +471,7 @@ class StateMachine {
424
471
  this._json = void 0;
425
472
  this.id = config.id || '(machine)';
426
473
  this.implementations = {
427
- actors: config.actors ?? {},
474
+ actorSources: config.actorSources ?? {},
428
475
  actions: config.actions ?? {},
429
476
  delays: config.delays ?? {},
430
477
  guards: config.guards ?? {},
@@ -433,7 +480,7 @@ class StateMachine {
433
480
  {
434
481
  // The `@xstate.` prefix is reserved for built-in serialized action and
435
482
  // guard descriptors — user implementation names must not collide.
436
- for (const kind of ['actions', 'guards', 'actors', 'delays']) {
483
+ for (const kind of ['actions', 'guards', 'actorSources', 'delays']) {
437
484
  for (const key of Object.keys(this.implementations[kind])) {
438
485
  if (key.startsWith('@xstate.')) {
439
486
  throw new Error(`Invalid ${kind} name '${key}': the '@xstate.' prefix is reserved for built-in descriptors.`);
@@ -449,6 +496,7 @@ class StateMachine {
449
496
  ...this.config.options
450
497
  };
451
498
  this.transition = this.transition.bind(this);
499
+ this.initialTransition = this.initialTransition.bind(this);
452
500
  this.getInitialSnapshot = this.getInitialSnapshot.bind(this);
453
501
  this.getPersistedSnapshot = this.getPersistedSnapshot.bind(this);
454
502
  this.restoreSnapshot = this.restoreSnapshot.bind(this);
@@ -462,23 +510,20 @@ class StateMachine {
462
510
  this.root._refreshEventMetadata();
463
511
  this.states = this.root.states; // TODO: remove!
464
512
  this.events = this.root.events;
465
- if (!('output' in this.root) && Object.values(this.states).some(state => state.type === 'final' && 'output' in state)) {
466
- console.warn('Missing `machine.output` declaration (top-level final state with output detected)');
467
- }
468
513
  }
469
514
 
470
515
  /**
471
516
  * Clones this state machine with the provided implementations.
472
517
  *
473
- * @param implementations Options (`actions`, `guards`, `actors`, `delays`) to
474
- * recursively merge with the existing options.
518
+ * @param implementations Options (`actions`, `guards`, `actorSources`,
519
+ * `delays`) to recursively merge with the existing options.
475
520
  * @returns A new `StateMachine` instance with the provided implementations.
476
521
  */
477
522
  provide(implementations) {
478
523
  const {
479
524
  actions,
480
525
  guards,
481
- actors,
526
+ actorSources,
482
527
  delays
483
528
  } = this.implementations;
484
529
  const provided = new StateMachine(this.config, {
@@ -490,9 +535,9 @@ class StateMachine {
490
535
  ...guards,
491
536
  ...implementations.guards
492
537
  },
493
- actors: {
494
- ...actors,
495
- ...implementations.actors
538
+ actorSources: {
539
+ ...actorSources,
540
+ ...implementations.actorSources
496
541
  },
497
542
  delays: {
498
543
  ...delays,
@@ -527,7 +572,56 @@ class StateMachine {
527
572
  * @param event The received event
528
573
  */
529
574
  transition(snapshot, event, actorScope) {
530
- return dist_xstateActors.macrostep(snapshot, event, actorScope, []).snapshot;
575
+ const fastSnapshot = this._transitionFast(snapshot, event, actorScope);
576
+ if (fastSnapshot) {
577
+ return [fastSnapshot, []];
578
+ }
579
+ const {
580
+ snapshot: nextSnapshot,
581
+ microsteps
582
+ } = dist_xstateActors.macrostep(snapshot, event, actorScope, []);
583
+ return [nextSnapshot, this._collectEffects(microsteps)];
584
+ }
585
+ _collectEffects(microsteps, initialActions = []) {
586
+ const effects = [...initialActions, ...microsteps.flatMap(([, actions]) => actions)];
587
+ return [...effects, ...dist_xstateActors.deriveDeferredStarts(effects)];
588
+ }
589
+ _transitionFast(snapshot, event, actorScope) {
590
+ if (snapshot.status !== 'active' || typeof snapshot.value !== 'string' || this.root.always?.length) {
591
+ return undefined;
592
+ }
593
+ const sourceNode = this.root.states[snapshot.value];
594
+ if (!sourceNode || sourceNode.type !== 'atomic' || sourceNode.exit || sourceNode.invoke.length || sourceNode.always?.length || sourceNode.after?.length) {
595
+ return undefined;
596
+ }
597
+ const transitions = sourceNode.transitions.get(event.type);
598
+ if (transitions?.length !== 1) {
599
+ return undefined;
600
+ }
601
+ const selected = transitions[0];
602
+ if (selected.guard || selected.actions || selected.to || selected.reenter || selected.input || typeof selected.context === 'function' || selected.target && selected.target.length !== 1) {
603
+ return undefined;
604
+ }
605
+ const targetNode = selected.target?.[0] ?? sourceNode;
606
+ const stateChanged = targetNode !== sourceNode;
607
+ if (targetNode.parent !== this.root || targetNode.type !== 'atomic' || stateChanged && (targetNode.entry || targetNode.invoke.length || targetNode.always?.length || targetNode.after?.length)) {
608
+ return undefined;
609
+ }
610
+ const context = selected.context !== undefined ? {
611
+ ...snapshot.context,
612
+ ...selected.context
613
+ } : snapshot.context;
614
+ const collectedMicrosteps = actorScope.self._collectedMicrosteps || [];
615
+ collectedMicrosteps.push(selected);
616
+ actorScope.self._collectedMicrosteps = collectedMicrosteps;
617
+ return dist_xstateActors.cloneMachineSnapshot(snapshot, {
618
+ ...(context !== snapshot.context ? {
619
+ context
620
+ } : {}),
621
+ ...(stateChanged ? {
622
+ _nodes: [this.root, targetNode]
623
+ } : {})
624
+ });
531
625
  }
532
626
 
533
627
  /**
@@ -559,6 +653,49 @@ class StateMachine {
559
653
  return false;
560
654
  }
561
655
 
656
+ /**
657
+ * Determines whether sending the `event` to the given snapshot would select a
658
+ * non-forbidden transition. Backs `snapshot.can(...)`; lives here so that
659
+ * non-machine bundles don't pay for the transition-resolution machinery.
660
+ *
661
+ * @internal
662
+ */
663
+ _canTransition(snapshot, event) {
664
+ const emptyActor = getEmptyCanActor();
665
+ const emptyActorScope = getEmptyCanActorScope();
666
+ const transitionData = this.getTransitionData(snapshot, event, emptyActor);
667
+ if (!transitionData?.length) {
668
+ return false;
669
+ }
670
+
671
+ // Check that at least one transition is not forbidden
672
+ for (const transition of transitionData) {
673
+ if (transition.target !== undefined) {
674
+ return true;
675
+ }
676
+ const res = dist_xstateActors.getTransitionResult(transition, snapshot, event, emptyActorScope, {
677
+ resolveActions: false
678
+ });
679
+ if (res.targets?.length || res.context || dist_xstateActors.hasEffect(transition, snapshot.context, event, snapshot, emptyActor)) {
680
+ return true;
681
+ }
682
+ }
683
+ return false;
684
+ }
685
+
686
+ /**
687
+ * Returns the error event that the actor should transition with to recover
688
+ * from an execution error, if any active state node declares `onError`.
689
+ *
690
+ * @internal
691
+ */
692
+ getExecutionErrorEvent(snapshot, error) {
693
+ if (snapshot?.status !== 'active' || !snapshot._nodes?.some(stateNode => stateNode.config.onError)) {
694
+ return undefined;
695
+ }
696
+ return dist_xstateActors.createErrorPlatformEvent('execution', error);
697
+ }
698
+
562
699
  /**
563
700
  * The initial state _before_ evaluating any microsteps. This "pre-initial"
564
701
  * state is provided to initial actions executed in the initial state.
@@ -582,9 +719,9 @@ class StateMachine {
582
719
  spawn,
583
720
  input: initEvent.input,
584
721
  self: actorScope.self,
585
- actors: this.implementations.actors
722
+ actorSources: this.implementations.actorSources
586
723
  });
587
- const nextState = dist_xstateActors.resolveAndExecuteActionsWithContext(preInitial, initEvent, actorScope, []);
724
+ const [nextState] = dist_xstateActors.resolveActionsWithContext(preInitial, initEvent, actorScope, []);
588
725
  if (resolvedContext) {
589
726
  nextState.context = resolvedContext;
590
727
  }
@@ -604,14 +741,33 @@ class StateMachine {
604
741
  * `ActorRef`.
605
742
  */
606
743
  getInitialSnapshot(actorScope, input) {
744
+ return this.initialTransition(input, actorScope)[0];
745
+ }
746
+ initialTransition(input, actorScope) {
607
747
  const initEvent = dist_xstateActors.createInitEvent(input); // TODO: fix;
608
748
  const internalQueue = [];
609
749
  const preInitialState = this._getPreInitialState(actorScope, initEvent);
610
- const [nextState] = dist_xstateActors.initialMicrostep(this.root, preInitialState, actorScope, initEvent, internalQueue);
611
- const {
612
- snapshot: macroState
613
- } = dist_xstateActors.macrostep(nextState, initEvent, actorScope, internalQueue);
614
- return macroState;
750
+ let nextState;
751
+ let initialActions = [];
752
+ let microsteps = [];
753
+ let macroState;
754
+ try {
755
+ [nextState, initialActions] = dist_xstateActors.initialMicrostep(this.root, preInitialState, actorScope, initEvent, internalQueue);
756
+ ({
757
+ snapshot: macroState,
758
+ microsteps
759
+ } = dist_xstateActors.macrostep(nextState, initEvent, actorScope, internalQueue));
760
+ } catch (err) {
761
+ if (!this.root.config.onError) {
762
+ throw err;
763
+ }
764
+ const errorEvent = dist_xstateActors.createErrorPlatformEvent('execution', err);
765
+ const errorMacrostep = dist_xstateActors.macrostep(preInitialState, errorEvent, actorScope, []);
766
+ macroState = errorMacrostep.snapshot;
767
+ microsteps = errorMacrostep.microsteps;
768
+ initialActions = [];
769
+ }
770
+ return [macroState, this._collectEffects(microsteps, initialActions)];
615
771
  }
616
772
  start(snapshot) {
617
773
  // Start rehydrated children that were active when persisted. Freshly
@@ -665,7 +821,7 @@ class StateMachine {
665
821
  syncSnapshot: actorData.syncSnapshot,
666
822
  snapshot: childState,
667
823
  src,
668
- systemId: actorData.systemId
824
+ registryKey: actorData.registryKey
669
825
  });
670
826
  // Mark so `start()` knows to start this child (freshly invoked/spawned
671
827
  // children are started via deferred `@xstate.start` actions instead).
@@ -712,7 +868,8 @@ class StateMachine {
712
868
  children,
713
869
  _nodes: nodes,
714
870
  value: snapshotData.value,
715
- historyValue: revivedHistoryValue
871
+ historyValue: revivedHistoryValue,
872
+ _stateInputs: snapshotData.stateInputs ?? {}
716
873
  }, this);
717
874
  const seen = new WeakSet();
718
875
  function reviveContext(contextPart) {