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.
- package/README.md +19 -17
- package/bin/xstate.js +36 -0
- package/dist/{StateMachine-2dcdb1f5.esm.js → StateMachine-42f1d26c.esm.js} +182 -40
- package/dist/{StateMachine-8d22a79c.development.cjs.js → StateMachine-6661eb0d.development.cjs.js} +193 -36
- package/dist/{StateMachine-2ea0a7fa.development.esm.js → StateMachine-ba3ccedf.development.esm.js} +193 -36
- package/dist/{StateMachine-64813f46.cjs.js → StateMachine-f2b70732.cjs.js} +182 -40
- package/dist/declarations/src/State.d.ts +13 -2
- package/dist/declarations/src/StateMachine.d.ts +23 -13
- package/dist/declarations/src/actions.d.ts +2 -1
- package/dist/declarations/src/actors/promise.d.ts +4 -2
- package/dist/declarations/src/createActor.d.ts +9 -11
- package/dist/declarations/src/createMachine.d.ts +14 -11
- package/dist/declarations/src/createMachineFromConfig.d.ts +61 -28
- package/dist/declarations/src/fsm.d.ts +74 -0
- package/dist/declarations/src/graph/graph.d.ts +3 -3
- package/dist/declarations/src/graph/shortestPaths.d.ts +2 -2
- package/dist/declarations/src/graph/simplePaths.d.ts +2 -2
- package/dist/declarations/src/index.d.ts +6 -5
- package/dist/declarations/src/serialize.d.ts +10 -18
- package/dist/declarations/src/setup.d.ts +266 -46
- package/dist/declarations/src/spawn.d.ts +3 -3
- package/dist/declarations/src/stateUtils.d.ts +4 -1
- package/dist/declarations/src/system.d.ts +1 -1
- package/dist/declarations/src/transition.d.ts +8 -5
- package/dist/declarations/src/transitionActions.d.ts +12 -0
- package/dist/declarations/src/types.d.ts +237 -102
- package/dist/declarations/src/types.v6.d.ts +129 -58
- package/dist/declarations/src/utils.d.ts +3 -2
- package/dist/{index-9cb3b3a0.cjs.js → index-42956779.cjs.js} +977 -1419
- package/dist/{index-f6ef20d1.development.cjs.js → index-49293864.development.cjs.js} +981 -1423
- package/dist/{index-93edd3bd.esm.js → index-d88b0c55.esm.js} +968 -1414
- package/dist/{index-e61170ba.development.esm.js → index-dc3443b6.development.esm.js} +972 -1418
- 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 +1100 -182
- package/dist/xstate.cjs.mjs +3 -5
- package/dist/xstate.development.cjs.js +1100 -182
- package/dist/xstate.development.cjs.mjs +3 -5
- package/dist/xstate.development.esm.js +1101 -181
- package/dist/xstate.esm.js +1101 -181
- package/dist/xstate.umd.min.js +1 -1
- package/dist/xstate.umd.min.js.map +1 -1
- package/package.json +6 -2
- package/dist/declarations/src/atom.d.ts +0 -81
package/README.md
CHANGED
|
@@ -119,7 +119,7 @@ npm install xstate
|
|
|
119
119
|
```
|
|
120
120
|
|
|
121
121
|
```ts
|
|
122
|
-
import { createMachine, createActor
|
|
122
|
+
import { createMachine, createActor } from 'xstate';
|
|
123
123
|
|
|
124
124
|
// State machine
|
|
125
125
|
const toggleMachine = createMachine({
|
|
@@ -135,7 +135,9 @@ const toggleMachine = createMachine({
|
|
|
135
135
|
}
|
|
136
136
|
},
|
|
137
137
|
active: {
|
|
138
|
-
entry:
|
|
138
|
+
entry: ({ context }) => ({
|
|
139
|
+
context: { count: context.count + 1 }
|
|
140
|
+
}),
|
|
139
141
|
on: {
|
|
140
142
|
TOGGLE: { target: 'inactive' }
|
|
141
143
|
}
|
|
@@ -164,7 +166,7 @@ toggleActor.send({ type: 'TOGGLE' });
|
|
|
164
166
|
- Deploy to Stately Sky
|
|
165
167
|
- Generate & modify machines with Stately AI
|
|
166
168
|
|
|
167
|
-
<a href="stately.ai/registry/new?ref=github" title="Stately Studio">
|
|
169
|
+
<a href="https://stately.ai/registry/new?ref=github" title="Stately Studio">
|
|
168
170
|
<img src="https://github.com/statelyai/xstate/assets/1093738/74ed9cbc-b824-4ed7-a16d-f104947af8a7" alt="XState Viz" width="800" />
|
|
169
171
|
</a>
|
|
170
172
|
|
|
@@ -365,10 +367,10 @@ const wordMachine = createMachine({
|
|
|
365
367
|
initial: 'off',
|
|
366
368
|
states: {
|
|
367
369
|
on: {
|
|
368
|
-
on: { TOGGLE_BOLD: 'off' }
|
|
370
|
+
on: { TOGGLE_BOLD: { target: 'off' } }
|
|
369
371
|
},
|
|
370
372
|
off: {
|
|
371
|
-
on: { TOGGLE_BOLD: 'on' }
|
|
373
|
+
on: { TOGGLE_BOLD: { target: 'on' } }
|
|
372
374
|
}
|
|
373
375
|
}
|
|
374
376
|
},
|
|
@@ -376,10 +378,10 @@ const wordMachine = createMachine({
|
|
|
376
378
|
initial: 'off',
|
|
377
379
|
states: {
|
|
378
380
|
on: {
|
|
379
|
-
on: { TOGGLE_UNDERLINE: 'off' }
|
|
381
|
+
on: { TOGGLE_UNDERLINE: { target: 'off' } }
|
|
380
382
|
},
|
|
381
383
|
off: {
|
|
382
|
-
on: { TOGGLE_UNDERLINE: 'on' }
|
|
384
|
+
on: { TOGGLE_UNDERLINE: { target: 'on' } }
|
|
383
385
|
}
|
|
384
386
|
}
|
|
385
387
|
},
|
|
@@ -387,10 +389,10 @@ const wordMachine = createMachine({
|
|
|
387
389
|
initial: 'off',
|
|
388
390
|
states: {
|
|
389
391
|
on: {
|
|
390
|
-
on: { TOGGLE_ITALICS: 'off' }
|
|
392
|
+
on: { TOGGLE_ITALICS: { target: 'off' } }
|
|
391
393
|
},
|
|
392
394
|
off: {
|
|
393
|
-
on: { TOGGLE_ITALICS: 'on' }
|
|
395
|
+
on: { TOGGLE_ITALICS: { target: 'on' } }
|
|
394
396
|
}
|
|
395
397
|
}
|
|
396
398
|
},
|
|
@@ -399,20 +401,20 @@ const wordMachine = createMachine({
|
|
|
399
401
|
states: {
|
|
400
402
|
none: {
|
|
401
403
|
on: {
|
|
402
|
-
BULLETS: 'bullets',
|
|
403
|
-
NUMBERS: 'numbers'
|
|
404
|
+
BULLETS: { target: 'bullets' },
|
|
405
|
+
NUMBERS: { target: 'numbers' }
|
|
404
406
|
}
|
|
405
407
|
},
|
|
406
408
|
bullets: {
|
|
407
409
|
on: {
|
|
408
|
-
NONE: 'none',
|
|
409
|
-
NUMBERS: 'numbers'
|
|
410
|
+
NONE: { target: 'none' },
|
|
411
|
+
NUMBERS: { target: 'numbers' }
|
|
410
412
|
}
|
|
411
413
|
},
|
|
412
414
|
numbers: {
|
|
413
415
|
on: {
|
|
414
|
-
BULLETS: 'bullets',
|
|
415
|
-
NONE: 'none'
|
|
416
|
+
BULLETS: { target: 'bullets' },
|
|
417
|
+
NONE: { target: 'none' }
|
|
416
418
|
}
|
|
417
419
|
}
|
|
418
420
|
}
|
|
@@ -493,10 +495,10 @@ const paymentMachine = createMachine({
|
|
|
493
495
|
},
|
|
494
496
|
hist: { type: 'history' }
|
|
495
497
|
},
|
|
496
|
-
on: { NEXT: 'review' }
|
|
498
|
+
on: { NEXT: { target: 'review' } }
|
|
497
499
|
},
|
|
498
500
|
review: {
|
|
499
|
-
on: { PREVIOUS: 'method.hist' }
|
|
501
|
+
on: { PREVIOUS: { target: 'method.hist' } }
|
|
500
502
|
}
|
|
501
503
|
}
|
|
502
504
|
});
|
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,
|
|
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-d88b0c55.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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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(`
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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`, `
|
|
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
|
-
|
|
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
|
-
|
|
478
|
-
...
|
|
479
|
-
...implementations.
|
|
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
|
-
|
|
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
|
-
|
|
691
|
+
actorSources: this.implementations.actorSources
|
|
570
692
|
});
|
|
571
|
-
const nextState =
|
|
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
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
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
|
-
|
|
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) {
|