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.
- package/bin/xstate.js +36 -0
- package/dist/{StateMachine-0fa9c094.development.esm.js → StateMachine-114b1598.development.esm.js} +121 -19
- package/dist/{StateMachine-98411dc5.development.cjs.js → StateMachine-4d17c47e.development.cjs.js} +121 -19
- package/dist/{StateMachine-e3b1aa2d.cjs.js → StateMachine-a10528bb.cjs.js} +111 -24
- package/dist/{StateMachine-8dbd20ad.esm.js → StateMachine-c2d32ddd.esm.js} +111 -24
- package/dist/declarations/src/createActor.d.ts +3 -0
- package/dist/declarations/src/createMachineFromConfig.d.ts +1 -0
- package/dist/declarations/src/transitionActions.d.ts +3 -1
- package/dist/declarations/src/types.d.ts +57 -22
- package/dist/declarations/src/types.v6.d.ts +7 -1
- package/dist/declarations/src/utils.d.ts +2 -2
- package/dist/{index-0d940f2c.cjs.js → index-1ac35571.cjs.js} +408 -422
- package/dist/{index-362125ce.development.esm.js → index-31e8ad34.development.esm.js} +407 -424
- package/dist/{index-07b19ed0.development.cjs.js → index-415cdd3a.development.cjs.js} +409 -423
- package/dist/{index-2635a437.esm.js → index-addc01a2.esm.js} +406 -423
- package/dist/xstate-actors.cjs.js +1 -1
- package/dist/xstate-actors.development.cjs.js +1 -1
- package/dist/xstate-actors.development.esm.js +1 -1
- package/dist/xstate-actors.esm.js +1 -1
- package/dist/xstate-actors.umd.min.js +1 -1
- package/dist/xstate-actors.umd.min.js.map +1 -1
- package/dist/xstate-graph.cjs.js +2 -2
- package/dist/xstate-graph.development.cjs.js +2 -2
- package/dist/xstate-graph.development.esm.js +2 -2
- package/dist/xstate-graph.esm.js +2 -2
- package/dist/xstate-graph.umd.min.js +1 -1
- package/dist/xstate-graph.umd.min.js.map +1 -1
- package/dist/xstate.cjs.js +68 -21
- package/dist/xstate.development.cjs.js +68 -21
- package/dist/xstate.development.esm.js +70 -23
- package/dist/xstate.esm.js +70 -23
- package/dist/xstate.umd.min.js +1 -1
- package/dist/xstate.umd.min.js.map +1 -1
- 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
|
+
}
|
package/dist/{StateMachine-0fa9c094.development.esm.js → StateMachine-114b1598.development.esm.js}
RENAMED
|
@@ -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,
|
|
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
|
-
|
|
256
|
-
|
|
257
|
-
|
|
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
|
-
|
|
268
|
-
|
|
269
|
-
|
|
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
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
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) {
|
package/dist/{StateMachine-98411dc5.development.cjs.js → StateMachine-4d17c47e.development.cjs.js}
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var dist_xstateActors = require('./index-
|
|
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
|
-
|
|
258
|
-
|
|
259
|
-
|
|
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
|
-
|
|
270
|
-
|
|
271
|
-
|
|
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
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
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-
|
|
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(`
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
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) {
|