xstate 6.0.0-alpha.15 → 6.0.0-alpha.17

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 (34) hide show
  1. package/bin/xstate.js +36 -0
  2. package/dist/{StateMachine-0fa9c094.development.esm.js → StateMachine-114b1598.development.esm.js} +121 -19
  3. package/dist/{StateMachine-98411dc5.development.cjs.js → StateMachine-4d17c47e.development.cjs.js} +121 -19
  4. package/dist/{StateMachine-e3b1aa2d.cjs.js → StateMachine-a10528bb.cjs.js} +111 -24
  5. package/dist/{StateMachine-8dbd20ad.esm.js → StateMachine-c2d32ddd.esm.js} +111 -24
  6. package/dist/declarations/src/createActor.d.ts +3 -0
  7. package/dist/declarations/src/createMachineFromConfig.d.ts +1 -0
  8. package/dist/declarations/src/transitionActions.d.ts +3 -1
  9. package/dist/declarations/src/types.d.ts +57 -22
  10. package/dist/declarations/src/types.v6.d.ts +7 -1
  11. package/dist/declarations/src/utils.d.ts +2 -2
  12. package/dist/{index-0d940f2c.cjs.js → index-1ac35571.cjs.js} +408 -422
  13. package/dist/{index-362125ce.development.esm.js → index-31e8ad34.development.esm.js} +407 -424
  14. package/dist/{index-07b19ed0.development.cjs.js → index-415cdd3a.development.cjs.js} +409 -423
  15. package/dist/{index-2635a437.esm.js → index-addc01a2.esm.js} +406 -423
  16. package/dist/xstate-actors.cjs.js +1 -1
  17. package/dist/xstate-actors.development.cjs.js +1 -1
  18. package/dist/xstate-actors.development.esm.js +1 -1
  19. package/dist/xstate-actors.esm.js +1 -1
  20. package/dist/xstate-actors.umd.min.js +1 -1
  21. package/dist/xstate-actors.umd.min.js.map +1 -1
  22. package/dist/xstate-graph.cjs.js +2 -2
  23. package/dist/xstate-graph.development.cjs.js +2 -2
  24. package/dist/xstate-graph.development.esm.js +2 -2
  25. package/dist/xstate-graph.esm.js +2 -2
  26. package/dist/xstate-graph.umd.min.js +1 -1
  27. package/dist/xstate-graph.umd.min.js.map +1 -1
  28. package/dist/xstate.cjs.js +68 -21
  29. package/dist/xstate.development.cjs.js +68 -21
  30. package/dist/xstate.development.esm.js +70 -23
  31. package/dist/xstate.esm.js +70 -23
  32. package/dist/xstate.umd.min.js +1 -1
  33. package/dist/xstate.umd.min.js.map +1 -1
  34. package/package.json +6 -2
package/bin/xstate.js ADDED
@@ -0,0 +1,36 @@
1
+ #!/usr/bin/env node
2
+ // Lightweight `xstate` CLI. Heavy commands are delegated to dedicated
3
+ // packages fetched on demand so the core package stays dependency-free.
4
+ const { spawnSync } = require('child_process');
5
+
6
+ const [command, ...rest] = process.argv.slice(2);
7
+
8
+ switch (command) {
9
+ case 'migrate': {
10
+ // Delegates to @xstate/codemod (ts-morph based v5 → v6 codemods).
11
+ const result = spawnSync(
12
+ process.platform === 'win32' ? 'npx.cmd' : 'npx',
13
+ ['-y', '@xstate/codemod', 'migrate', ...rest],
14
+ { stdio: 'inherit' }
15
+ );
16
+ process.exit(result.status ?? 1);
17
+ break;
18
+ }
19
+ case '--help':
20
+ case 'help':
21
+ case undefined:
22
+ console.log(
23
+ [
24
+ 'Usage: xstate <command>',
25
+ '',
26
+ 'Commands:',
27
+ ' migrate [globs...] [--dry] Migrate source files to the current XState version',
28
+ ' (delegates to @xstate/codemod)'
29
+ ].join('\n')
30
+ );
31
+ process.exit(command ? 0 : 1);
32
+ break;
33
+ default:
34
+ console.error(`Unknown command: ${command}`);
35
+ process.exit(1);
36
+ }
@@ -1,4 +1,4 @@
1
- import { P as ProcessingStatus, G as resolveReferencedActor, d 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, k as getStateNodes, W as createMachineSnapshot, Y as isInFinalState, e as macrostep, Z as cloneMachineSnapshot, _ as transitionNode, m as matchesEventDescriptor, r as resolveActionsWithContext, f as createInitEvent, g as initialMicrostep, $ as toStatePath, a0 as isStateId, a1 as getStateNodeByPath, a2 as getPersistedSnapshot, a3 as $$ACTOR_TYPE } from './index-362125ce.development.esm.js';
1
+ import { P as ProcessingStatus, G as resolveReferencedActor, d 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, k as getStateNodes, W as createMachineSnapshot, Y as isInFinalState, e as macrostep, Z as cloneMachineSnapshot, _ as transitionNode, m as matchesEventDescriptor, $ as getTransitionResult, a0 as hasEffect, a1 as createErrorPlatformEvent, r as resolveActionsWithContext, f as createInitEvent, g as initialMicrostep, a2 as toStatePath, a3 as isStateId, a4 as getStateNodeByPath, a5 as getPersistedSnapshot, a6 as $$ACTOR_TYPE } from './index-31e8ad34.development.esm.js';
2
2
 
3
3
  function createSpawner(actorScope, {
4
4
  machine,
@@ -250,11 +250,13 @@ function validateStateNodeConfig(stateNode) {
250
250
  return;
251
251
  }
252
252
  if (typeof config.choice !== 'function') {
253
- throw new Error(`Choice state "${stateNode.id}" must declare a \`choice\` function.`);
253
+ throw new Error(`Choice state "${stateNode.id}" must declare a \`choice\` function.` );
254
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}\`.`);
255
+ {
256
+ for (const key of CHOICE_CONFIG_KEYS) {
257
+ if (config[key] !== undefined) {
258
+ throw new Error(`Choice state "${stateNode.id}" cannot declare \`${key}\`.`);
259
+ }
258
260
  }
259
261
  }
260
262
  }
@@ -262,11 +264,13 @@ function formatChoiceTransitions(stateNode) {
262
264
  const choice = stateNode.config.choice;
263
265
  const validateChoiceResult = result => {
264
266
  if (!result || result.target === undefined) {
265
- throw new Error(`Choice state "${stateNode.id}" must resolve to a target.`);
267
+ throw new Error(`Choice state "${stateNode.id}" must resolve to a target.` );
266
268
  }
267
- for (const key of ['actions', 'to']) {
268
- if (result[key] !== undefined) {
269
- throw new Error(`Choice state "${stateNode.id}" cannot declare \`${key}\` on a choice.`);
269
+ {
270
+ for (const key of ['actions', 'to']) {
271
+ if (result[key] !== undefined) {
272
+ throw new Error(`Choice state "${stateNode.id}" cannot declare \`${key}\` on a choice.`);
273
+ }
270
274
  }
271
275
  }
272
276
  return result;
@@ -288,7 +292,7 @@ function formatTransitions(stateNode) {
288
292
  if (stateNode.config.on) {
289
293
  for (const descriptor of Object.keys(stateNode.config.on)) {
290
294
  if (descriptor === NULL_EVENT) {
291
- throw new Error('Null events ("") cannot be specified as a transition key. Use `always: { ... }` instead.');
295
+ throw new Error('Null events ("") cannot be specified as a transition key. Use `always: { ... }` instead.' );
292
296
  }
293
297
  const transitionsConfig = stateNode.config.on[descriptor];
294
298
  transitions.set(descriptor, mapTransitionConfigs(transitionsConfig, transition => formatTransition(stateNode, descriptor, transition)));
@@ -298,6 +302,10 @@ function formatTransitions(stateNode) {
298
302
  const descriptor = `xstate.done.state.${stateNode.id}`;
299
303
  transitions.set(descriptor, mapTransitionConfigs(stateNode.config.onDone, transition => formatTransition(stateNode, descriptor, transition)));
300
304
  }
305
+ if (stateNode.config.onError) {
306
+ const descriptor = 'xstate.error.*';
307
+ transitions.set(descriptor, mapTransitionConfigs(stateNode.config.onError, transition => formatTransition(stateNode, descriptor, transition)));
308
+ }
301
309
  const createCancelInvokeTimeoutTransition = (descriptor, timeoutEventType) => formatTransition(stateNode, descriptor, {
302
310
  to: (_args, enq) => {
303
311
  enq.cancel(timeoutEventType);
@@ -384,7 +392,7 @@ function formatInitialTransition(stateNode, _target) {
384
392
  const input = typeof _target === 'object' && _target !== null ? _target.input : undefined;
385
393
  const resolvedTarget = typeof targetString === 'string' ? stateNode.states[targetString] : undefined;
386
394
  if (!resolvedTarget && targetString) {
387
- throw new Error(`Initial state node "${targetString}" not found on parent state node #${stateNode.id}`);
395
+ throw new Error(`Initial state node "${targetString}" not found on parent state node #${stateNode.id}` );
388
396
  }
389
397
  const transition = {
390
398
  source: stateNode,
@@ -395,6 +403,45 @@ function formatInitialTransition(stateNode, _target) {
395
403
  }
396
404
 
397
405
  const STATE_IDENTIFIER = '#';
406
+ let emptyCanActor;
407
+ let emptyCanActorScope;
408
+ function getEmptyCanActor() {
409
+ // A minimal inert actor used purely as the `self`/`parent` argument when
410
+ // dry-running transitions for `snapshot.can(...)`. Intentionally not built
411
+ // on `createLogic` so `can()` does not pull that machinery into bundles.
412
+ return emptyCanActor ??= createActor({
413
+ transition: snapshot => [snapshot, []],
414
+ initialTransition: () => [{
415
+ status: 'active',
416
+ output: undefined,
417
+ error: undefined
418
+ }, []],
419
+ getInitialSnapshot: () => ({
420
+ status: 'active',
421
+ output: undefined,
422
+ error: undefined
423
+ }),
424
+ getPersistedSnapshot: snapshot => snapshot
425
+ });
426
+ }
427
+ function getEmptyCanActorScope() {
428
+ if (emptyCanActorScope) {
429
+ return emptyCanActorScope;
430
+ }
431
+ const actor = getEmptyCanActor();
432
+ emptyCanActorScope = {
433
+ self: actor,
434
+ logger: () => {},
435
+ id: '',
436
+ sessionId: '',
437
+ defer: () => {},
438
+ system: actor.system,
439
+ stopChild: () => {},
440
+ emit: () => {},
441
+ actionExecutor: () => {}
442
+ };
443
+ return emptyCanActorScope;
444
+ }
398
445
  class StateMachine {
399
446
  constructor(/** The raw config used to create the machine. */
400
447
  config, implementations) {
@@ -461,9 +508,6 @@ class StateMachine {
461
508
  this.root._refreshEventMetadata();
462
509
  this.states = this.root.states; // TODO: remove!
463
510
  this.events = this.root.events;
464
- if (!('output' in this.root) && Object.values(this.states).some(state => state.type === 'final' && 'output' in state)) {
465
- console.warn('Missing `machine.output` declaration (top-level final state with output detected)');
466
- }
467
511
  }
468
512
 
469
513
  /**
@@ -603,6 +647,49 @@ class StateMachine {
603
647
  return false;
604
648
  }
605
649
 
650
+ /**
651
+ * Determines whether sending the `event` to the given snapshot would select a
652
+ * non-forbidden transition. Backs `snapshot.can(...)`; lives here so that
653
+ * non-machine bundles don't pay for the transition-resolution machinery.
654
+ *
655
+ * @internal
656
+ */
657
+ _canTransition(snapshot, event) {
658
+ const emptyActor = getEmptyCanActor();
659
+ const emptyActorScope = getEmptyCanActorScope();
660
+ const transitionData = this.getTransitionData(snapshot, event, emptyActor);
661
+ if (!transitionData?.length) {
662
+ return false;
663
+ }
664
+
665
+ // Check that at least one transition is not forbidden
666
+ for (const transition of transitionData) {
667
+ if (transition.target !== undefined) {
668
+ return true;
669
+ }
670
+ const res = getTransitionResult(transition, snapshot, event, emptyActorScope, {
671
+ resolveActions: false
672
+ });
673
+ if (res.targets?.length || res.context || hasEffect(transition, snapshot.context, event, snapshot, emptyActor)) {
674
+ return true;
675
+ }
676
+ }
677
+ return false;
678
+ }
679
+
680
+ /**
681
+ * Returns the error event that the actor should transition with to recover
682
+ * from an execution error, if any active state node declares `onError`.
683
+ *
684
+ * @internal
685
+ */
686
+ getExecutionErrorEvent(snapshot, error) {
687
+ if (snapshot?.status !== 'active' || !snapshot._nodes?.some(stateNode => stateNode.config.onError)) {
688
+ return undefined;
689
+ }
690
+ return createErrorPlatformEvent('execution', error);
691
+ }
692
+
606
693
  /**
607
694
  * The initial state _before_ evaluating any microsteps. This "pre-initial"
608
695
  * state is provided to initial actions executed in the initial state.
@@ -654,11 +741,26 @@ class StateMachine {
654
741
  const initEvent = createInitEvent(input); // TODO: fix;
655
742
  const internalQueue = [];
656
743
  const preInitialState = this._getPreInitialState(actorScope, initEvent);
657
- const [nextState, initialActions] = initialMicrostep(this.root, preInitialState, actorScope, initEvent, internalQueue);
658
- const {
659
- snapshot: macroState,
660
- microsteps
661
- } = macrostep(nextState, initEvent, actorScope, internalQueue);
744
+ let nextState;
745
+ let initialActions = [];
746
+ let microsteps = [];
747
+ let macroState;
748
+ try {
749
+ [nextState, initialActions] = initialMicrostep(this.root, preInitialState, actorScope, initEvent, internalQueue);
750
+ ({
751
+ snapshot: macroState,
752
+ microsteps
753
+ } = macrostep(nextState, initEvent, actorScope, internalQueue));
754
+ } catch (err) {
755
+ if (!this.root.config.onError) {
756
+ throw err;
757
+ }
758
+ const errorEvent = createErrorPlatformEvent('execution', err);
759
+ const errorMacrostep = macrostep(preInitialState, errorEvent, actorScope, []);
760
+ macroState = errorMacrostep.snapshot;
761
+ microsteps = errorMacrostep.microsteps;
762
+ initialActions = [];
763
+ }
662
764
  return [macroState, [...initialActions, ...microsteps.flatMap(([, actions]) => actions)]];
663
765
  }
664
766
  start(snapshot) {
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var dist_xstateActors = require('./index-07b19ed0.development.cjs.js');
3
+ var dist_xstateActors = require('./index-415cdd3a.development.cjs.js');
4
4
 
5
5
  function createSpawner(actorScope, {
6
6
  machine,
@@ -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']) {
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) {
@@ -463,9 +510,6 @@ class StateMachine {
463
510
  this.root._refreshEventMetadata();
464
511
  this.states = this.root.states; // TODO: remove!
465
512
  this.events = this.root.events;
466
- if (!('output' in this.root) && Object.values(this.states).some(state => state.type === 'final' && 'output' in state)) {
467
- console.warn('Missing `machine.output` declaration (top-level final state with output detected)');
468
- }
469
513
  }
470
514
 
471
515
  /**
@@ -605,6 +649,49 @@ class StateMachine {
605
649
  return false;
606
650
  }
607
651
 
652
+ /**
653
+ * Determines whether sending the `event` to the given snapshot would select a
654
+ * non-forbidden transition. Backs `snapshot.can(...)`; lives here so that
655
+ * non-machine bundles don't pay for the transition-resolution machinery.
656
+ *
657
+ * @internal
658
+ */
659
+ _canTransition(snapshot, event) {
660
+ const emptyActor = getEmptyCanActor();
661
+ const emptyActorScope = getEmptyCanActorScope();
662
+ const transitionData = this.getTransitionData(snapshot, event, emptyActor);
663
+ if (!transitionData?.length) {
664
+ return false;
665
+ }
666
+
667
+ // Check that at least one transition is not forbidden
668
+ for (const transition of transitionData) {
669
+ if (transition.target !== undefined) {
670
+ return true;
671
+ }
672
+ const res = dist_xstateActors.getTransitionResult(transition, snapshot, event, emptyActorScope, {
673
+ resolveActions: false
674
+ });
675
+ if (res.targets?.length || res.context || dist_xstateActors.hasEffect(transition, snapshot.context, event, snapshot, emptyActor)) {
676
+ return true;
677
+ }
678
+ }
679
+ return false;
680
+ }
681
+
682
+ /**
683
+ * Returns the error event that the actor should transition with to recover
684
+ * from an execution error, if any active state node declares `onError`.
685
+ *
686
+ * @internal
687
+ */
688
+ getExecutionErrorEvent(snapshot, error) {
689
+ if (snapshot?.status !== 'active' || !snapshot._nodes?.some(stateNode => stateNode.config.onError)) {
690
+ return undefined;
691
+ }
692
+ return dist_xstateActors.createErrorPlatformEvent('execution', error);
693
+ }
694
+
608
695
  /**
609
696
  * The initial state _before_ evaluating any microsteps. This "pre-initial"
610
697
  * state is provided to initial actions executed in the initial state.
@@ -656,11 +743,26 @@ class StateMachine {
656
743
  const initEvent = dist_xstateActors.createInitEvent(input); // TODO: fix;
657
744
  const internalQueue = [];
658
745
  const preInitialState = this._getPreInitialState(actorScope, initEvent);
659
- const [nextState, initialActions] = dist_xstateActors.initialMicrostep(this.root, preInitialState, actorScope, initEvent, internalQueue);
660
- const {
661
- snapshot: macroState,
662
- microsteps
663
- } = dist_xstateActors.macrostep(nextState, initEvent, actorScope, internalQueue);
746
+ let nextState;
747
+ let initialActions = [];
748
+ let microsteps = [];
749
+ let macroState;
750
+ try {
751
+ [nextState, initialActions] = dist_xstateActors.initialMicrostep(this.root, preInitialState, actorScope, initEvent, internalQueue);
752
+ ({
753
+ snapshot: macroState,
754
+ microsteps
755
+ } = dist_xstateActors.macrostep(nextState, initEvent, actorScope, internalQueue));
756
+ } catch (err) {
757
+ if (!this.root.config.onError) {
758
+ throw err;
759
+ }
760
+ const errorEvent = dist_xstateActors.createErrorPlatformEvent('execution', err);
761
+ const errorMacrostep = dist_xstateActors.macrostep(preInitialState, errorEvent, actorScope, []);
762
+ macroState = errorMacrostep.snapshot;
763
+ microsteps = errorMacrostep.microsteps;
764
+ initialActions = [];
765
+ }
664
766
  return [macroState, [...initialActions, ...microsteps.flatMap(([, actions]) => actions)]];
665
767
  }
666
768
  start(snapshot) {
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var dist_xstateActors = require('./index-0d940f2c.cjs.js');
3
+ var dist_xstateActors = require('./index-1ac35571.cjs.js');
4
4
 
5
5
  function createSpawner(actorScope, {
6
6
  machine,
@@ -66,7 +66,6 @@ function memo(object, key, fn) {
66
66
  }
67
67
 
68
68
  const EMPTY_OBJECT = {};
69
- const CHOICE_CONFIG_KEYS = ['invoke', 'after', 'on', 'entry', 'exit', 'always', 'states', 'initial', 'onDone', 'timeout', 'onTimeout', 'history', 'target', 'output'];
70
69
  class StateNode {
71
70
  constructor(/** The raw config used to create the machine. */
72
71
  config, options) {
@@ -246,30 +245,17 @@ class StateNode {
246
245
  function validateStateNodeConfig(stateNode) {
247
246
  const config = stateNode.config;
248
247
  if (stateNode.type !== 'choice') {
249
- if (config.choice !== undefined) {
250
- throw new Error(`State "${stateNode.id}" has \`choice\`, but \`choice\` can only be used with \`type: 'choice'\`.`);
251
- }
252
248
  return;
253
249
  }
254
250
  if (typeof config.choice !== 'function') {
255
- throw new Error(`Choice state "${stateNode.id}" must declare a \`choice\` function.`);
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}\`.`);
260
- }
251
+ throw new Error(`Missing \`choice\` function on "${stateNode.id}"`);
261
252
  }
262
253
  }
263
254
  function formatChoiceTransitions(stateNode) {
264
255
  const choice = stateNode.config.choice;
265
256
  const validateChoiceResult = result => {
266
257
  if (!result || result.target === undefined) {
267
- throw new Error(`Choice state "${stateNode.id}" must resolve to a target.`);
268
- }
269
- for (const key of ['actions', 'to']) {
270
- if (result[key] !== undefined) {
271
- throw new Error(`Choice state "${stateNode.id}" cannot declare \`${key}\` on a choice.`);
272
- }
258
+ throw new Error(`Choice "${stateNode.id}" has no target`);
273
259
  }
274
260
  return result;
275
261
  };
@@ -290,7 +276,7 @@ function formatTransitions(stateNode) {
290
276
  if (stateNode.config.on) {
291
277
  for (const descriptor of Object.keys(stateNode.config.on)) {
292
278
  if (descriptor === dist_xstateActors.NULL_EVENT) {
293
- throw new Error('Null events ("") cannot be specified as a transition key. Use `always: { ... }` instead.');
279
+ throw new Error('Null event transition key');
294
280
  }
295
281
  const transitionsConfig = stateNode.config.on[descriptor];
296
282
  transitions.set(descriptor, mapTransitionConfigs(transitionsConfig, transition => dist_xstateActors.formatTransition(stateNode, descriptor, transition)));
@@ -300,6 +286,10 @@ function formatTransitions(stateNode) {
300
286
  const descriptor = `xstate.done.state.${stateNode.id}`;
301
287
  transitions.set(descriptor, mapTransitionConfigs(stateNode.config.onDone, transition => dist_xstateActors.formatTransition(stateNode, descriptor, transition)));
302
288
  }
289
+ if (stateNode.config.onError) {
290
+ const descriptor = 'xstate.error.*';
291
+ transitions.set(descriptor, mapTransitionConfigs(stateNode.config.onError, transition => dist_xstateActors.formatTransition(stateNode, descriptor, transition)));
292
+ }
303
293
  const createCancelInvokeTimeoutTransition = (descriptor, timeoutEventType) => dist_xstateActors.formatTransition(stateNode, descriptor, {
304
294
  to: (_args, enq) => {
305
295
  enq.cancel(timeoutEventType);
@@ -386,7 +376,7 @@ function formatInitialTransition(stateNode, _target) {
386
376
  const input = typeof _target === 'object' && _target !== null ? _target.input : undefined;
387
377
  const resolvedTarget = typeof targetString === 'string' ? stateNode.states[targetString] : undefined;
388
378
  if (!resolvedTarget && targetString) {
389
- throw new Error(`Initial state node "${targetString}" not found on parent state node #${stateNode.id}`);
379
+ throw new Error(`Initial state "${targetString}" not found on "#${stateNode.id}"`);
390
380
  }
391
381
  const transition = {
392
382
  source: stateNode,
@@ -397,6 +387,45 @@ function formatInitialTransition(stateNode, _target) {
397
387
  }
398
388
 
399
389
  const STATE_IDENTIFIER = '#';
390
+ let emptyCanActor;
391
+ let emptyCanActorScope;
392
+ function getEmptyCanActor() {
393
+ // A minimal inert actor used purely as the `self`/`parent` argument when
394
+ // dry-running transitions for `snapshot.can(...)`. Intentionally not built
395
+ // on `createLogic` so `can()` does not pull that machinery into bundles.
396
+ return emptyCanActor ??= dist_xstateActors.createActor({
397
+ transition: snapshot => [snapshot, []],
398
+ initialTransition: () => [{
399
+ status: 'active',
400
+ output: undefined,
401
+ error: undefined
402
+ }, []],
403
+ getInitialSnapshot: () => ({
404
+ status: 'active',
405
+ output: undefined,
406
+ error: undefined
407
+ }),
408
+ getPersistedSnapshot: snapshot => snapshot
409
+ });
410
+ }
411
+ function getEmptyCanActorScope() {
412
+ if (emptyCanActorScope) {
413
+ return emptyCanActorScope;
414
+ }
415
+ const actor = getEmptyCanActor();
416
+ emptyCanActorScope = {
417
+ self: actor,
418
+ logger: () => {},
419
+ id: '',
420
+ sessionId: '',
421
+ defer: () => {},
422
+ system: actor.system,
423
+ stopChild: () => {},
424
+ emit: () => {},
425
+ actionExecutor: () => {}
426
+ };
427
+ return emptyCanActorScope;
428
+ }
400
429
  class StateMachine {
401
430
  constructor(/** The raw config used to create the machine. */
402
431
  config, implementations) {
@@ -591,6 +620,49 @@ class StateMachine {
591
620
  return false;
592
621
  }
593
622
 
623
+ /**
624
+ * Determines whether sending the `event` to the given snapshot would select a
625
+ * non-forbidden transition. Backs `snapshot.can(...)`; lives here so that
626
+ * non-machine bundles don't pay for the transition-resolution machinery.
627
+ *
628
+ * @internal
629
+ */
630
+ _canTransition(snapshot, event) {
631
+ const emptyActor = getEmptyCanActor();
632
+ const emptyActorScope = getEmptyCanActorScope();
633
+ const transitionData = this.getTransitionData(snapshot, event, emptyActor);
634
+ if (!transitionData?.length) {
635
+ return false;
636
+ }
637
+
638
+ // Check that at least one transition is not forbidden
639
+ for (const transition of transitionData) {
640
+ if (transition.target !== undefined) {
641
+ return true;
642
+ }
643
+ const res = dist_xstateActors.getTransitionResult(transition, snapshot, event, emptyActorScope, {
644
+ resolveActions: false
645
+ });
646
+ if (res.targets?.length || res.context || dist_xstateActors.hasEffect(transition, snapshot.context, event, snapshot, emptyActor)) {
647
+ return true;
648
+ }
649
+ }
650
+ return false;
651
+ }
652
+
653
+ /**
654
+ * Returns the error event that the actor should transition with to recover
655
+ * from an execution error, if any active state node declares `onError`.
656
+ *
657
+ * @internal
658
+ */
659
+ getExecutionErrorEvent(snapshot, error) {
660
+ if (snapshot?.status !== 'active' || !snapshot._nodes?.some(stateNode => stateNode.config.onError)) {
661
+ return undefined;
662
+ }
663
+ return dist_xstateActors.createErrorPlatformEvent('execution', error);
664
+ }
665
+
594
666
  /**
595
667
  * The initial state _before_ evaluating any microsteps. This "pre-initial"
596
668
  * state is provided to initial actions executed in the initial state.
@@ -642,11 +714,26 @@ class StateMachine {
642
714
  const initEvent = dist_xstateActors.createInitEvent(input); // TODO: fix;
643
715
  const internalQueue = [];
644
716
  const preInitialState = this._getPreInitialState(actorScope, initEvent);
645
- const [nextState, initialActions] = dist_xstateActors.initialMicrostep(this.root, preInitialState, actorScope, initEvent, internalQueue);
646
- const {
647
- snapshot: macroState,
648
- microsteps
649
- } = dist_xstateActors.macrostep(nextState, initEvent, actorScope, internalQueue);
717
+ let nextState;
718
+ let initialActions = [];
719
+ let microsteps = [];
720
+ let macroState;
721
+ try {
722
+ [nextState, initialActions] = dist_xstateActors.initialMicrostep(this.root, preInitialState, actorScope, initEvent, internalQueue);
723
+ ({
724
+ snapshot: macroState,
725
+ microsteps
726
+ } = dist_xstateActors.macrostep(nextState, initEvent, actorScope, internalQueue));
727
+ } catch (err) {
728
+ if (!this.root.config.onError) {
729
+ throw err;
730
+ }
731
+ const errorEvent = dist_xstateActors.createErrorPlatformEvent('execution', err);
732
+ const errorMacrostep = dist_xstateActors.macrostep(preInitialState, errorEvent, actorScope, []);
733
+ macroState = errorMacrostep.snapshot;
734
+ microsteps = errorMacrostep.microsteps;
735
+ initialActions = [];
736
+ }
650
737
  return [macroState, [...initialActions, ...microsteps.flatMap(([, actions]) => actions)]];
651
738
  }
652
739
  start(snapshot) {