xstate 6.0.0-alpha.63 → 6.0.0-alpha.65
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 +45 -1
- package/dist/{createActor-f71670cf.development.cjs.js → createActor-1df42c9e.development.cjs.js} +238 -36
- package/dist/{createActor-523a98c7.esm.js → createActor-4629a290.esm.js} +162 -34
- package/dist/{createActor-48f753a9.development.esm.js → createActor-8f57b47b.development.esm.js} +234 -37
- package/dist/{createActor-eba8226e.cjs.js → createActor-b987e971.cjs.js} +166 -33
- package/dist/declarations/src/SimulatedClock.d.ts +1 -1
- package/dist/declarations/src/State.d.ts +25 -3
- package/dist/declarations/src/StateMachine.d.ts +20 -7
- package/dist/declarations/src/actors/promise.d.ts +5 -5
- package/dist/declarations/src/createMachine.d.ts +8 -6
- package/dist/declarations/src/createMachineFromConfig.d.ts +13 -2
- package/dist/declarations/src/durable/index.d.ts +13 -0
- package/dist/declarations/src/eventTypes.d.ts +8 -0
- package/dist/declarations/src/graph/index.d.ts +1 -0
- package/dist/declarations/src/graph/types.d.ts +20 -2
- package/dist/declarations/src/index.d.ts +2 -1
- package/dist/declarations/src/machineVersions.d.ts +2 -2
- package/dist/declarations/src/serialize.d.ts +7 -2
- package/dist/declarations/src/setup.d.ts +208 -30
- package/dist/declarations/src/systemTransition.d.ts +103 -0
- package/dist/declarations/src/types.d.ts +47 -26
- package/dist/declarations/src/types.v6.d.ts +18 -8
- package/dist/declarations/src/utils.d.ts +9 -0
- package/dist/{effectDescriptor-bc9e3e81.cjs.js → effectDescriptor-4cf24593.cjs.js} +1 -1
- package/dist/{effectDescriptor-ad75e430.esm.js → effectDescriptor-973fc1ba.esm.js} +1 -1
- package/dist/{effectDescriptor-8c87a3e7.development.cjs.js → effectDescriptor-b2b7deaa.development.cjs.js} +1 -1
- package/dist/{effectDescriptor-eadf1b25.development.esm.js → effectDescriptor-c4671afc.development.esm.js} +1 -1
- package/dist/{index-83f11f07.cjs.js → index-0cf33789.development.cjs.js} +37 -15
- package/dist/{index-2a4be1f2.development.esm.js → index-15382daf.development.esm.js} +37 -15
- package/dist/{index-211a5dce.esm.js → index-52ad3d45.esm.js} +37 -15
- package/dist/{index-3cb56f46.development.cjs.js → index-f201d440.cjs.js} +37 -15
- package/dist/xstate-actors.cjs.js +2 -2
- package/dist/xstate-actors.development.cjs.js +2 -2
- package/dist/xstate-actors.development.esm.js +2 -2
- package/dist/xstate-actors.esm.js +2 -2
- package/dist/xstate-actors.umd.min.js +1 -1
- package/dist/xstate-actors.umd.min.js.map +1 -1
- package/dist/xstate-durable.cjs.js +36 -4
- package/dist/xstate-durable.development.cjs.js +36 -4
- package/dist/xstate-durable.development.esm.js +36 -4
- package/dist/xstate-durable.esm.js +36 -4
- package/dist/xstate-durable.umd.min.js +1 -1
- package/dist/xstate-durable.umd.min.js.map +1 -1
- package/dist/xstate-graph.cjs.js +8 -4
- package/dist/xstate-graph.cjs.mjs +1 -0
- package/dist/xstate-graph.development.cjs.js +8 -4
- package/dist/xstate-graph.development.cjs.mjs +1 -0
- package/dist/xstate-graph.development.esm.js +8 -4
- package/dist/xstate-graph.esm.js +8 -4
- package/dist/xstate-graph.umd.min.js +1 -1
- package/dist/xstate-graph.umd.min.js.map +1 -1
- package/dist/xstate-validation.cjs.js +24 -7
- package/dist/xstate-validation.development.cjs.js +24 -7
- package/dist/xstate-validation.development.esm.js +24 -7
- package/dist/xstate-validation.esm.js +24 -7
- package/dist/xstate-validation.umd.min.js +1 -1
- package/dist/xstate-validation.umd.min.js.map +1 -1
- package/dist/xstate.cjs.js +1316 -291
- package/dist/xstate.cjs.mjs +3 -0
- package/dist/xstate.development.cjs.js +1335 -291
- package/dist/xstate.development.cjs.mjs +3 -0
- package/dist/xstate.development.esm.js +1335 -294
- package/dist/xstate.esm.js +1316 -294
- package/dist/xstate.umd.min.js +1 -1
- package/dist/xstate.umd.min.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -17,12 +17,44 @@ XState is a state management and orchestration solution for JavaScript and TypeS
|
|
|
17
17
|
|
|
18
18
|
It uses event-driven programming, state machines, statecharts, and the actor model to handle complex logic in predictable, robust, and visual ways. XState provides a powerful and flexible way to manage application and workflow state by allowing developers to model logic as actors and state machines.
|
|
19
19
|
|
|
20
|
+
<!-- experimental whole-system transitions from packages/core/src/systemTransition.ts -->
|
|
21
|
+
|
|
22
|
+
For pure actor-system simulation, `initialSystemTransition`, `systemTransition`
|
|
23
|
+
and `advanceSystemTime` return an immutable system snapshot and external effects
|
|
24
|
+
as data. Branch or replay actors, messages and virtual timers without starting live
|
|
25
|
+
actors. See [pure system transitions](../../docs/system-transitions.md).
|
|
26
|
+
|
|
20
27
|
<!-- durable execution and checkpoint restoration from packages/core/src/durable/index.ts -->
|
|
21
28
|
|
|
22
29
|
For durable hosts, the experimental `xstate/durable` entry point provides
|
|
23
30
|
`createDurable`. Resume a checkpoint with `execution.restore(persistedSnapshot)`
|
|
24
31
|
and execute the returned effects to restart active embedded children and pending
|
|
25
|
-
timers without replaying entry actions.
|
|
32
|
+
timers without replaying entry actions. Await `executeEffects()` before persisting
|
|
33
|
+
to retain accepted timer deadlines. The host handles root error snapshots;
|
|
34
|
+
`run()` rejects with the root error. See [durable execution](../../docs/durable-execution.md).
|
|
35
|
+
|
|
36
|
+
<!-- state context refinements from packages/core/src/setup.ts and packages/core/src/State.ts -->
|
|
37
|
+
|
|
38
|
+
For [typestates](../../docs/typestates.md), declare state-level `schemas.context`
|
|
39
|
+
in `setup({ states })`. State functions receive narrowed context, transitions
|
|
40
|
+
check the target context requirements, and `snapshot.matches(...)` narrows
|
|
41
|
+
context when reading snapshots.
|
|
42
|
+
|
|
43
|
+
<!-- typed inline invocation from packages/core/src/setup.ts -->
|
|
44
|
+
|
|
45
|
+
Use `s.createInvoke({ src, input, onDone })` inline in `s.createMachine(...)` for an actor used in one state. It infers the actor's input and output and the enclosing state's narrowed context without registering that actor in `setup.actors`. See [typed inline invokes](../../docs/invoke.md#typed-inline-invokes).
|
|
46
|
+
|
|
47
|
+
Pass an async function directly as `src`, with `schemas.input`, `schemas.output`, and `schemas.error` on the same invoke. Output also infers from the async return when its schema is omitted. Existing logic values use their own schemas.
|
|
48
|
+
|
|
49
|
+
The helper requires input for actors whose input excludes `undefined`, and checks child IDs and source compatibility against `schemas.children` declared in the setup or machine.
|
|
50
|
+
|
|
51
|
+
<!-- graph traversal events from packages/core/src/graph/index.ts -->
|
|
52
|
+
|
|
53
|
+
`getAllOwnEvents(snapshot)` from `xstate/graph` includes invoke completions, errors, and delayed events. Compose it with custom traversal payloads: supplied `events` replace the defaults. See [model-based testing](../../docs/model-based-testing.md).
|
|
54
|
+
|
|
55
|
+
<!-- JSON loading and runtime validation from packages/core/src/createMachineFromConfig.ts -->
|
|
56
|
+
|
|
57
|
+
Revive JSON definitions with `createMachineFromConfig(json, { schemas, validator, ...sources })` to restore runtime validation. The loader validates structural keys and references and copies its input; serialized definitions are independent copies. See [serialization](../../docs/serialization.md).
|
|
26
58
|
|
|
27
59
|
For flat finite state machines, `xstate/fsm` provides a tiny pure transition
|
|
28
60
|
table with context and TypeScript support:
|
|
@@ -618,3 +650,15 @@ For these reasons, it is impractical for our team to be bound by decisions taken
|
|
|
618
650
|
### Packages
|
|
619
651
|
|
|
620
652
|
Most of the packages in the XState family declare a peer dependency on XState itself. We’ll be cautious about maintaining compatibility with already-released packages when releasing a new version of XState, **but** each release of packages depending on XState will always adjust the declared peer dependency range to include the latest version of XState. For example, you should always be able to update `xstate` without `@xstate/react`. But when you update `@xstate/react`, we highly recommend updating `xstate` too.
|
|
653
|
+
|
|
654
|
+
<!-- public input validation contract from src/StateMachine.ts -->
|
|
655
|
+
Validate external input with `machine.eventSchema` before `actor.send()`.
|
|
656
|
+
It validates complete public events, excludes internal/runtime events, and allows
|
|
657
|
+
explicitly configured routes. Payload checks require runtime schemas;
|
|
658
|
+
`types<T>()` supplies types only. See [public input validation](../../docs/internal-events.md#validating-public-input).
|
|
659
|
+
|
|
660
|
+
<!-- entry effects and terminal transition behavior from packages/core/src/stateUtils.ts and transitionActions.ts -->
|
|
661
|
+
|
|
662
|
+
Functions passed to `enq(...)` run as effects; their returned context or child patches are ignored, with a development warning. Return a computed patch from the transition, entry, or exit function instead. Entry/exit functions using enqueue must declare exactly `(args, enq)`; default/rest parameter wrappers receive a development diagnostic. See [actions](../../docs/actions.md).
|
|
663
|
+
|
|
664
|
+
Pure transitions preserve terminal snapshots and return no effects. `snapshot.can(...)` returns `false` for terminal snapshots; active dry runs propagate evaluation errors. See [snapshots](../../docs/snapshots.md) and [states](../../docs/states.md).
|
package/dist/{createActor-f71670cf.development.cjs.js → createActor-1df42c9e.development.cjs.js}
RENAMED
|
@@ -183,6 +183,20 @@ function reportUnhandledError(err) {
|
|
|
183
183
|
|
|
184
184
|
const symbolObservable = (() => typeof Symbol === 'function' && Symbol.observable || '@@observable')();
|
|
185
185
|
|
|
186
|
+
// Runtime metadata stays outside pure snapshots. Timer objects survive unrelated
|
|
187
|
+
// transitions; a cancelled/replaced timer gets a new object and a new deadline.
|
|
188
|
+
const starts = new WeakMap();
|
|
189
|
+
|
|
190
|
+
/** @internal */
|
|
191
|
+
function getTimerStart(timer) {
|
|
192
|
+
return starts.get(timer) ?? timer.startedAt;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/** @internal */
|
|
196
|
+
function recordTimerStart(timer, startedAt) {
|
|
197
|
+
starts.set(timer, startedAt);
|
|
198
|
+
}
|
|
199
|
+
|
|
186
200
|
function assertSendToEvent(event) {
|
|
187
201
|
if (typeof event === 'string') {
|
|
188
202
|
throw new Error(// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
|
|
@@ -241,6 +255,9 @@ function resolveRegisteredActorSource(actors, logic) {
|
|
|
241
255
|
return cache.get(logic);
|
|
242
256
|
}
|
|
243
257
|
|
|
258
|
+
/** Static scheduling metadata on actor definitions, never on live actors. @internal */
|
|
259
|
+
const systemLogicMetadata = Symbol('xstate.systemLogicMetadata');
|
|
260
|
+
|
|
244
261
|
const subscriptions = /* #__PURE__ */new WeakMap();
|
|
245
262
|
|
|
246
263
|
/**
|
|
@@ -259,7 +276,7 @@ id) {
|
|
|
259
276
|
error: undefined,
|
|
260
277
|
input
|
|
261
278
|
}, []];
|
|
262
|
-
|
|
279
|
+
const logic = {
|
|
263
280
|
id,
|
|
264
281
|
start: (state, {
|
|
265
282
|
self,
|
|
@@ -323,6 +340,12 @@ id) {
|
|
|
323
340
|
getPersistedSnapshot: snapshot => snapshot,
|
|
324
341
|
restoreSnapshot: snapshot => snapshot
|
|
325
342
|
};
|
|
343
|
+
Object.defineProperty(logic, systemLogicMetadata, {
|
|
344
|
+
value: {
|
|
345
|
+
kind: id
|
|
346
|
+
}
|
|
347
|
+
});
|
|
348
|
+
return logic;
|
|
326
349
|
}
|
|
327
350
|
|
|
328
351
|
/**
|
|
@@ -507,7 +530,33 @@ function withActorScope(args, actorScope) {
|
|
|
507
530
|
}
|
|
508
531
|
|
|
509
532
|
function execCustomEffect() {
|
|
510
|
-
|
|
533
|
+
const result = this.action?.(...this.args);
|
|
534
|
+
{
|
|
535
|
+
warnOnIgnoredEffectResult(this, result);
|
|
536
|
+
}
|
|
537
|
+
return result;
|
|
538
|
+
}
|
|
539
|
+
const warnedEffectResults = new WeakSet();
|
|
540
|
+
|
|
541
|
+
/**
|
|
542
|
+
* A function passed to `enq(...)` runs as an effect after the transition is
|
|
543
|
+
* computed, so a `{ context }` or `{ children }` it returns is never applied.
|
|
544
|
+
* Development builds say so, once per function.
|
|
545
|
+
*/
|
|
546
|
+
function warnOnIgnoredEffectResult(effect, result) {
|
|
547
|
+
if (!effect.action || result === null || typeof result !== 'object' || warnedEffectResults.has(effect.action)) {
|
|
548
|
+
return;
|
|
549
|
+
}
|
|
550
|
+
const prototype = Object.getPrototypeOf(result);
|
|
551
|
+
if (prototype !== Object.prototype && prototype !== null) {
|
|
552
|
+
return;
|
|
553
|
+
}
|
|
554
|
+
const keys = ['context', 'children'].filter(key => key in result);
|
|
555
|
+
if (!keys.length) {
|
|
556
|
+
return;
|
|
557
|
+
}
|
|
558
|
+
warnedEffectResults.add(effect.action);
|
|
559
|
+
console.warn(`Action "${effect.type || '(anonymous)'}" returned { ${keys.join(', ')} }, which is ignored: functions passed to enq(...) run as effects after the transition. Return the patch from the transition, entry or exit function instead; a named action can be called there directly and its result returned.`);
|
|
511
560
|
}
|
|
512
561
|
function execEmitEffect(runtime = this.source.system) {
|
|
513
562
|
return runtime.emitEvent(this.source, this.event);
|
|
@@ -1036,6 +1085,16 @@ function createTransitionEnqueue(actorScope, actions, internalEvents, actorSubsc
|
|
|
1036
1085
|
props[key] = guard(props[key]);
|
|
1037
1086
|
}
|
|
1038
1087
|
const enqueue = createEnqueueObject(props, guard((action, ...args) => {
|
|
1088
|
+
// A named action is `undefined` when it has no implementation. Skip it:
|
|
1089
|
+
// an `{ action, args }` record without a function would be emitted as
|
|
1090
|
+
// an event. Speculative passes (`can()`, conflict resolution) run with
|
|
1091
|
+
// `createActors: false`; warn only when the transition really runs.
|
|
1092
|
+
if (typeof action !== 'function') {
|
|
1093
|
+
if (createActors) {
|
|
1094
|
+
warnNotAFunction(action);
|
|
1095
|
+
}
|
|
1096
|
+
return;
|
|
1097
|
+
}
|
|
1039
1098
|
pushBuiltInAction(actions, action, ...args);
|
|
1040
1099
|
}));
|
|
1041
1100
|
enqueueClosers.set(enqueue, () => {
|
|
@@ -1060,6 +1119,10 @@ function lateEnqueueCall() {
|
|
|
1060
1119
|
throw new Error('enq.* called after the transition function returned');
|
|
1061
1120
|
}
|
|
1062
1121
|
}
|
|
1122
|
+
function warnNotAFunction(value) {
|
|
1123
|
+
const received = typeof value === 'string' ? `the string "${value}"` : value !== null && typeof value === 'object' ? 'an object' : String(value);
|
|
1124
|
+
console.warn(`enq(...) received ${received} instead of a function, so nothing was enqueued. A named action is undefined when it has no implementation: implement it in setup({ actions }) or the machine config, or pass it to machine.provide({ actions }).`);
|
|
1125
|
+
}
|
|
1063
1126
|
function getBuiltInActionFields(action, args) {
|
|
1064
1127
|
switch (action) {
|
|
1065
1128
|
case builtInActions['@xstate.spawn']:
|
|
@@ -2265,6 +2328,10 @@ function getDelayedTransitions(stateNode) {
|
|
|
2265
2328
|
}));
|
|
2266
2329
|
}
|
|
2267
2330
|
function formatTransition(stateNode, descriptor, transitionConfig) {
|
|
2331
|
+
const removedKey = getRemovedTransitionKey(transitionConfig);
|
|
2332
|
+
if (removedKey) {
|
|
2333
|
+
throw new Error(`Transition "${descriptor || 'always'}" in state "${stateNode.id}" uses ${removedKey}, which was removed in v6. Use a transition function.`);
|
|
2334
|
+
}
|
|
2268
2335
|
const normalizedTarget = normalizeTarget(transitionConfig.target);
|
|
2269
2336
|
const reenter = transitionConfig.reenter ?? false;
|
|
2270
2337
|
const target = resolveTarget(stateNode, normalizedTarget);
|
|
@@ -2275,14 +2342,46 @@ function formatTransition(stateNode, descriptor, transitionConfig) {
|
|
|
2275
2342
|
source: stateNode,
|
|
2276
2343
|
reenter,
|
|
2277
2344
|
eventType: descriptor,
|
|
2278
|
-
toJSON: () => (
|
|
2279
|
-
...transition,
|
|
2280
|
-
source: `#${stateNode.id}`,
|
|
2281
|
-
target: target ? target.map(t => `#${t.id}`) : undefined
|
|
2282
|
-
})
|
|
2345
|
+
toJSON: () => transitionDefinitionToJSON(transition)
|
|
2283
2346
|
};
|
|
2284
2347
|
return transition;
|
|
2285
2348
|
}
|
|
2349
|
+
function transitionDefinitionToJSON(transition) {
|
|
2350
|
+
const {
|
|
2351
|
+
definition: _,
|
|
2352
|
+
...serializableTransition
|
|
2353
|
+
} = transition;
|
|
2354
|
+
return {
|
|
2355
|
+
...serializableTransition,
|
|
2356
|
+
source: `#${transition.source.id}`,
|
|
2357
|
+
target: transition.target?.map(target => `#${target.id}`)
|
|
2358
|
+
};
|
|
2359
|
+
}
|
|
2360
|
+
|
|
2361
|
+
/**
|
|
2362
|
+
* Returns the v5 key of a transition object that v6 would misread, in every
|
|
2363
|
+
* build: `cond` is never read, so the transition would always be taken,
|
|
2364
|
+
* transition `actions` would not run as written, and only a function `guard`
|
|
2365
|
+
* can be called. Development builds explain each key earlier, in
|
|
2366
|
+
* `diagnoseAuthorConfig`. Compiled configs (JSON, SCXML) never set them.
|
|
2367
|
+
*/
|
|
2368
|
+
function getRemovedTransitionKey(transitionConfig) {
|
|
2369
|
+
const {
|
|
2370
|
+
cond,
|
|
2371
|
+
actions,
|
|
2372
|
+
guard
|
|
2373
|
+
} = transitionConfig;
|
|
2374
|
+
if (cond !== undefined) {
|
|
2375
|
+
return '"cond"';
|
|
2376
|
+
}
|
|
2377
|
+
if (actions !== undefined) {
|
|
2378
|
+
return '"actions"';
|
|
2379
|
+
}
|
|
2380
|
+
if (guard !== undefined && typeof guard !== 'function') {
|
|
2381
|
+
return 'an object-form "guard"';
|
|
2382
|
+
}
|
|
2383
|
+
return undefined;
|
|
2384
|
+
}
|
|
2286
2385
|
function isStateNodeDescendantOf(stateNode, ancestor) {
|
|
2287
2386
|
for (let current = stateNode.parent; current; current = current.parent) {
|
|
2288
2387
|
if (current === ancestor) {
|
|
@@ -2625,12 +2724,18 @@ function removeConflictingTransitions(enabledTransitions, stateNodeSet, snapshot
|
|
|
2625
2724
|
}
|
|
2626
2725
|
function createTransitionResultResolver(snapshot, event, actorScope, resolveActions, selectionResults) {
|
|
2627
2726
|
let cache;
|
|
2628
|
-
return transition => {
|
|
2629
|
-
if (!transition.to) {
|
|
2630
|
-
|
|
2631
|
-
|
|
2632
|
-
|
|
2633
|
-
|
|
2727
|
+
return (transition, applied) => {
|
|
2728
|
+
if (!transition.to && !applied) {
|
|
2729
|
+
// An object transition's targets are static, so reading them must not
|
|
2730
|
+
// run its `context` and `input` mappers
|
|
2731
|
+
return {
|
|
2732
|
+
targets: transition.target,
|
|
2733
|
+
context: undefined,
|
|
2734
|
+
reenter: transition.reenter,
|
|
2735
|
+
actions: undefined,
|
|
2736
|
+
internalEvents: undefined,
|
|
2737
|
+
input: undefined
|
|
2738
|
+
};
|
|
2634
2739
|
}
|
|
2635
2740
|
let result = cache?.get(transition);
|
|
2636
2741
|
if (!result) {
|
|
@@ -2858,13 +2963,20 @@ function microstep(transitions, currentSnapshot, actorScope, event, isInitial, i
|
|
|
2858
2963
|
children: args.children,
|
|
2859
2964
|
actions: args.actions,
|
|
2860
2965
|
actors: args.actors,
|
|
2966
|
+
guards: currentSnapshot.machine.sources.guards,
|
|
2967
|
+
delays: currentSnapshot.machine.sources.delays,
|
|
2861
2968
|
input,
|
|
2862
2969
|
stateNode
|
|
2863
2970
|
}, actorScope) : {
|
|
2864
2971
|
...args,
|
|
2972
|
+
guards: currentSnapshot.machine.sources.guards,
|
|
2973
|
+
delays: currentSnapshot.machine.sources.delays,
|
|
2865
2974
|
input,
|
|
2866
2975
|
stateNode
|
|
2867
|
-
},
|
|
2976
|
+
},
|
|
2977
|
+
// This function runs during action resolution, where enqueued
|
|
2978
|
+
// effects are no longer collected.
|
|
2979
|
+
createIgnoredEnqueue(transitionFn, kind, stateNode.id) ), '_special' in transitionFn ? {
|
|
2868
2980
|
_special: true
|
|
2869
2981
|
} : {});
|
|
2870
2982
|
return [[wrappedAction], undefined, undefined];
|
|
@@ -2902,7 +3014,7 @@ function microstep(transitions, currentSnapshot, actorScope, event, isInitial, i
|
|
|
2902
3014
|
}
|
|
2903
3015
|
for (const exitStateNode of statesToExit) {
|
|
2904
3016
|
const stateInput = getStateInput(currentSnapshot, exitStateNode.id);
|
|
2905
|
-
const [exitActions, nextContext, internalEvents] = exitStateNode.exit ? getStateActionsAndContext(exitStateNode.exit, nextState.context,
|
|
3017
|
+
const [exitActions, nextContext, internalEvents] = exitStateNode.exit ? getStateActionsAndContext(exitStateNode.exit, nextState.context, nextState.children, stateInput, 'exit', exitStateNode) : [[], undefined, undefined];
|
|
2906
3018
|
if (internalEvents?.length) {
|
|
2907
3019
|
internalQueue.push(...internalEvents);
|
|
2908
3020
|
}
|
|
@@ -2932,11 +3044,20 @@ function microstep(transitions, currentSnapshot, actorScope, event, isInitial, i
|
|
|
2932
3044
|
let context = nextState.context;
|
|
2933
3045
|
const transitionActions = [];
|
|
2934
3046
|
const internalEvents = [];
|
|
3047
|
+
const resolvedTransitions = [];
|
|
2935
3048
|
for (const t of filteredTransitions) {
|
|
2936
|
-
|
|
2937
|
-
|
|
3049
|
+
const res = getCurrentTransitionResult(t, true);
|
|
3050
|
+
if (t.to) {
|
|
3051
|
+
const resolvedTransition = {
|
|
3052
|
+
...t,
|
|
3053
|
+
target: res.targets,
|
|
3054
|
+
definition: t
|
|
3055
|
+
};
|
|
3056
|
+
resolvedTransition.toJSON = () => transitionDefinitionToJSON(resolvedTransition);
|
|
3057
|
+
resolvedTransitions.push(resolvedTransition);
|
|
3058
|
+
} else {
|
|
3059
|
+
resolvedTransitions.push(t);
|
|
2938
3060
|
}
|
|
2939
|
-
const res = getCurrentTransitionResult(t);
|
|
2940
3061
|
if (res.context !== undefined) {
|
|
2941
3062
|
context = mergeContextPatch(context, res.context);
|
|
2942
3063
|
}
|
|
@@ -3092,7 +3213,7 @@ function microstep(transitions, currentSnapshot, actorScope, event, isInitial, i
|
|
|
3092
3213
|
const {
|
|
3093
3214
|
targets,
|
|
3094
3215
|
input
|
|
3095
|
-
} = getCurrentTransitionResult(transition);
|
|
3216
|
+
} = getCurrentTransitionResult(transition, true);
|
|
3096
3217
|
if (input && targets) {
|
|
3097
3218
|
for (const targetNode of targets.filter(targetNode => enteredTargetsByTransition.get(transition)?.has(targetNode))) {
|
|
3098
3219
|
stateInputMap[targetNode.id] = input;
|
|
@@ -3101,12 +3222,12 @@ function microstep(transitions, currentSnapshot, actorScope, event, isInitial, i
|
|
|
3101
3222
|
}
|
|
3102
3223
|
}
|
|
3103
3224
|
const completedNodes = new Set();
|
|
3104
|
-
const children = {
|
|
3105
|
-
...nextState.children
|
|
3106
|
-
};
|
|
3107
3225
|
for (const stateNodeToEnter of [...statesToEnter].sort((a, b) => a.order - b.order)) {
|
|
3108
3226
|
mutStateNodeSet.add(stateNodeToEnter);
|
|
3109
3227
|
const actions = [];
|
|
3228
|
+
// Read the children for each state: the entry actions of the states
|
|
3229
|
+
// entered before this one may have spawned or stopped children.
|
|
3230
|
+
let children = nextState.children;
|
|
3110
3231
|
|
|
3111
3232
|
// Final states are inert, so (as in SCXML) their invocations never
|
|
3112
3233
|
// start.
|
|
@@ -3145,7 +3266,10 @@ function microstep(transitions, currentSnapshot, actorScope, event, isInitial, i
|
|
|
3145
3266
|
args: [actor]
|
|
3146
3267
|
});
|
|
3147
3268
|
if (invokeDef.id) {
|
|
3148
|
-
children
|
|
3269
|
+
children = {
|
|
3270
|
+
...children,
|
|
3271
|
+
[invokeDef.id]: actor
|
|
3272
|
+
};
|
|
3149
3273
|
}
|
|
3150
3274
|
}
|
|
3151
3275
|
if (invoked) {
|
|
@@ -3265,7 +3389,12 @@ function microstep(transitions, currentSnapshot, actorScope, event, isInitial, i
|
|
|
3265
3389
|
nextStateNodesToExit.forEach(stateNode => {
|
|
3266
3390
|
if (stateNode.exit) {
|
|
3267
3391
|
const stateInput = getStateInput(nextState, stateNode.id);
|
|
3268
|
-
const [exitActions
|
|
3392
|
+
const [exitActions, nextContext, nextInternalEvents] = getStateActionsAndContext(stateNode.exit, nextState.context, nextState.children, stateInput, 'exit', stateNode);
|
|
3393
|
+
if (nextContext) {
|
|
3394
|
+
nextState = cloneMachineSnapshot(nextState, {
|
|
3395
|
+
context: nextContext
|
|
3396
|
+
});
|
|
3397
|
+
}
|
|
3269
3398
|
allExitActions.push(...exitActions);
|
|
3270
3399
|
if (nextInternalEvents?.length) {
|
|
3271
3400
|
internalQueue.push(...nextInternalEvents);
|
|
@@ -3286,14 +3415,14 @@ function microstep(transitions, currentSnapshot, actorScope, event, isInitial, i
|
|
|
3286
3415
|
// If context was changed (e.g. by entry actions during self-transition),
|
|
3287
3416
|
// clone to ensure reference inequality for eventless transition re-evaluation
|
|
3288
3417
|
if (nextState.context !== originalContext) {
|
|
3289
|
-
return [nextState === currentSnapshot ? cloneMachineSnapshot(nextState) : nextState, executableActions];
|
|
3418
|
+
return [nextState === currentSnapshot ? cloneMachineSnapshot(nextState) : nextState, executableActions, resolvedTransitions];
|
|
3290
3419
|
}
|
|
3291
|
-
return [nextState, executableActions];
|
|
3420
|
+
return [nextState, executableActions, resolvedTransitions];
|
|
3292
3421
|
}
|
|
3293
3422
|
return [cloneMachineSnapshot(nextState, {
|
|
3294
3423
|
_nodes: nextStateNodes,
|
|
3295
3424
|
historyValue
|
|
3296
|
-
}), executableActions];
|
|
3425
|
+
}), executableActions, resolvedTransitions];
|
|
3297
3426
|
}
|
|
3298
3427
|
}
|
|
3299
3428
|
|
|
@@ -3403,6 +3532,15 @@ class InfiniteTransitionError extends Error {
|
|
|
3403
3532
|
}
|
|
3404
3533
|
}
|
|
3405
3534
|
function macrostep(snapshot, event, actorScope, internalQueue, initialMicrosteps = []) {
|
|
3535
|
+
// A done, error or stopped snapshot processes no events, as in an actor:
|
|
3536
|
+
// every event is unhandled. The initial macrostep passes `initialMicrosteps`
|
|
3537
|
+
// and may start from a snapshot that is already done.
|
|
3538
|
+
if (snapshot.status !== 'active' && !initialMicrosteps.length) {
|
|
3539
|
+
return {
|
|
3540
|
+
snapshot,
|
|
3541
|
+
microsteps: [[snapshot, [], []]]
|
|
3542
|
+
};
|
|
3543
|
+
}
|
|
3406
3544
|
let nextSnapshot = snapshot;
|
|
3407
3545
|
const microsteps = initialMicrosteps.slice();
|
|
3408
3546
|
// Whether a transition handled the external event. A handled event always
|
|
@@ -3471,6 +3609,7 @@ function macrostep(snapshot, event, actorScope, internalQueue, initialMicrosteps
|
|
|
3471
3609
|
};
|
|
3472
3610
|
}
|
|
3473
3611
|
function addMicrostep(step, transitions) {
|
|
3612
|
+
transitions = step[2] ?? transitions;
|
|
3474
3613
|
// collect microsteps; their transitions are surfaced on the enclosing
|
|
3475
3614
|
// '@xstate.transition' event via its `microsteps[]` facet (there is no
|
|
3476
3615
|
// standalone microstep event) and returned by `getMicrosteps()`
|
|
@@ -3626,6 +3765,42 @@ function assertSyncTransitionResult(res, event, sourceId, kind) {
|
|
|
3626
3765
|
throw new Error(kind ? `${kind === 'entry' ? 'Entry' : 'Exit'} functions must be synchronous. The ${kind} function of state "${sourceId}" returned a promise (event "${event.type}"). Move async work into an invoked or spawned actor, or enq.effect.` : `Transition functions must be synchronous. Transition for event "${event.type}" in state "${sourceId}" returned a promise. Move async work into an invoked or spawned actor, or enq.effect.`);
|
|
3627
3766
|
}
|
|
3628
3767
|
}
|
|
3768
|
+
const ignoredEnqueues = new WeakMap();
|
|
3769
|
+
|
|
3770
|
+
/**
|
|
3771
|
+
* The enqueue object that an entry or exit function gets in development when it
|
|
3772
|
+
* is not declared with exactly two parameters. Such a function runs during
|
|
3773
|
+
* action resolution, where enqueued effects are no longer collected, so every
|
|
3774
|
+
* call is ignored. This one warns, once per function.
|
|
3775
|
+
*/
|
|
3776
|
+
function createIgnoredEnqueue(fn, kind, stateNodeId) {
|
|
3777
|
+
let enqueue = ignoredEnqueues.get(fn);
|
|
3778
|
+
if (enqueue) {
|
|
3779
|
+
return enqueue;
|
|
3780
|
+
}
|
|
3781
|
+
let warned = false;
|
|
3782
|
+
const ignore = (method, result) => (..._args) => {
|
|
3783
|
+
if (!warned) {
|
|
3784
|
+
warned = true;
|
|
3785
|
+
console.warn(`The ${kind} function of state "${stateNodeId}" called ${method}(...), which was ignored. Only ${kind} functions declared with exactly two parameters, (args, enq) => ..., get a working enq. This one has fn.length ${fn.length}; fn.length stops counting at a default or rest parameter.`);
|
|
3786
|
+
}
|
|
3787
|
+
return result?.();
|
|
3788
|
+
};
|
|
3789
|
+
const emptyActor = () => ({});
|
|
3790
|
+
enqueue = createEnqueueObject({
|
|
3791
|
+
cancel: ignore('enq.cancel'),
|
|
3792
|
+
emit: ignore('enq.emit'),
|
|
3793
|
+
log: ignore('enq.log'),
|
|
3794
|
+
raise: ignore('enq.raise'),
|
|
3795
|
+
sendTo: ignore('enq.sendTo'),
|
|
3796
|
+
stop: ignore('enq.stop'),
|
|
3797
|
+
spawn: ignore('enq.spawn', emptyActor),
|
|
3798
|
+
listen: ignore('enq.listen', emptyActor),
|
|
3799
|
+
subscribeTo: ignore('enq.subscribeTo', emptyActor)
|
|
3800
|
+
}, ignore('enq'));
|
|
3801
|
+
ignoredEnqueues.set(fn, enqueue);
|
|
3802
|
+
return enqueue;
|
|
3803
|
+
}
|
|
3629
3804
|
let transitionEffectEnqueue;
|
|
3630
3805
|
function getTransitionEffectEnqueue() {
|
|
3631
3806
|
return transitionEffectEnqueue ??= createEnqueueObject({
|
|
@@ -3955,6 +4130,17 @@ function isMachineSnapshot(value) {
|
|
|
3955
4130
|
return !!value && typeof value === 'object' && 'machine' in value && 'value' in value;
|
|
3956
4131
|
}
|
|
3957
4132
|
|
|
4133
|
+
/**
|
|
4134
|
+
* The values that `snapshot.matches(...)` accepts for a state value: a state
|
|
4135
|
+
* key, a dot-delimited path to a descendant state, or a partial state value.
|
|
4136
|
+
* Non-literal state values accept any value.
|
|
4137
|
+
*/
|
|
4138
|
+
|
|
4139
|
+
/**
|
|
4140
|
+
* `unknown` if `matches(...)` accepts `TTestStateValue` for `TStateValue`,
|
|
4141
|
+
* otherwise `never`. Non-literal test values are always accepted.
|
|
4142
|
+
*/
|
|
4143
|
+
|
|
3958
4144
|
/** @public */
|
|
3959
4145
|
|
|
3960
4146
|
const machineSnapshotMatches = function matches(testValue) {
|
|
@@ -4158,7 +4344,7 @@ function getPersistedSnapshot(snapshot, options) {
|
|
|
4158
4344
|
// readings (a simulated clock, a monotonic counter) are meaningless in any
|
|
4159
4345
|
// other process, and restoring them under the wall clock would fire every
|
|
4160
4346
|
// pending delay instantly. Pure-transition snapshots have no local
|
|
4161
|
-
// schedule
|
|
4347
|
+
// schedule; durable executions record an accepted start separately.
|
|
4162
4348
|
const scheduledTimers = snapshotActor?.system?._clock?.now ? undefined : snapshotActor?.system?._snapshot?._scheduledTimers;
|
|
4163
4349
|
for (const id in timers) {
|
|
4164
4350
|
const timer = timers[id];
|
|
@@ -4189,10 +4375,10 @@ function getPersistedSnapshot(snapshot, options) {
|
|
|
4189
4375
|
// deadline. Derived from dueAt, not the scheduling moment, so repeated
|
|
4190
4376
|
// persist/restore cycles keep the same deadline. Without a live schedule
|
|
4191
4377
|
// (a restored-but-never-started actor, a pure-transition snapshot) the
|
|
4192
|
-
// timer's
|
|
4193
|
-
//
|
|
4378
|
+
// timer's accepted durable start or carried-in start passes through,
|
|
4379
|
+
// so re-persisting cannot push the deadline back.
|
|
4194
4380
|
const scheduled = scheduledTimers?.[`${snapshotActor.sessionId}.${id}`];
|
|
4195
|
-
const startedAt = scheduled ? scheduled.dueAt - timer.delay : timer
|
|
4381
|
+
const startedAt = scheduled ? scheduled.dueAt - timer.delay : getTimerStart(timer);
|
|
4196
4382
|
timersJson[id] = {
|
|
4197
4383
|
id: timer.id,
|
|
4198
4384
|
delay: timer.delay,
|
|
@@ -4479,7 +4665,14 @@ function getAllOwnEventDescriptors(snapshot) {
|
|
|
4479
4665
|
return [...new Set([...snapshot.nodes.flatMap(sn => sn.ownEvents)])];
|
|
4480
4666
|
}
|
|
4481
4667
|
|
|
4482
|
-
/**
|
|
4668
|
+
/**
|
|
4669
|
+
* Returns one event per transition of the active state nodes of `snapshot`,
|
|
4670
|
+
* including the internal events for invoked actors (`xstate.done.actor`,
|
|
4671
|
+
* `xstate.error.actor`) and delayed transitions (`xstate.after`). Graph
|
|
4672
|
+
* traversal sends these when the `events` option is not given.
|
|
4673
|
+
*
|
|
4674
|
+
* @public
|
|
4675
|
+
*/
|
|
4483
4676
|
function getAllOwnEvents(snapshot) {
|
|
4484
4677
|
const events = snapshot.nodes.flatMap(stateNode => [...stateNode.transitions.values()].flatMap(transitions => transitions.map(transition => {
|
|
4485
4678
|
const event = {
|
|
@@ -6040,7 +6233,10 @@ class Actor {
|
|
|
6040
6233
|
const [nextSnapshot, effects] = nextState;
|
|
6041
6234
|
if (nextSnapshot === previousSnapshot && !effects.length &&
|
|
6042
6235
|
// State machine snapshots only; other logic may ignore events freely.
|
|
6043
|
-
'machine' in previousSnapshot &&
|
|
6236
|
+
'machine' in previousSnapshot && (
|
|
6237
|
+
// `xstate.route` is sent by users, so a route that matches nothing is
|
|
6238
|
+
// reported like any other unhandled event.
|
|
6239
|
+
!event.type.startsWith('xstate.') || event.type === 'xstate.route')) {
|
|
6044
6240
|
this._reportUnhandledEvent(event);
|
|
6045
6241
|
}
|
|
6046
6242
|
snapshot = nextSnapshot;
|
|
@@ -6061,10 +6257,11 @@ class Actor {
|
|
|
6061
6257
|
_reportUnhandledEvent(event) {
|
|
6062
6258
|
safeCall(() => this.options.onUnhandledEvent?.(event, this._snapshot));
|
|
6063
6259
|
{
|
|
6260
|
+
const to = event.type === 'xstate.route' ? ` to ${JSON.stringify(event.to)}` : '';
|
|
6064
6261
|
const warned = this._warnedUnhandledTypes ??= new Set();
|
|
6065
|
-
if (!warned.has(event.type)) {
|
|
6066
|
-
warned.add(event.type);
|
|
6067
|
-
console.warn(`Actor ${this.id} received event "${event.type}" in state ${JSON.stringify(this._snapshot.value)} with no matching transition`);
|
|
6262
|
+
if (!warned.has(event.type + to)) {
|
|
6263
|
+
warned.add(event.type + to);
|
|
6264
|
+
console.warn(`Actor ${this.id} received event "${event.type}"${to} in state ${JSON.stringify(this._snapshot.value)} with no matching transition`);
|
|
6068
6265
|
}
|
|
6069
6266
|
}
|
|
6070
6267
|
}
|
|
@@ -6395,7 +6592,9 @@ exports.cloneMachineSnapshot = cloneMachineSnapshot;
|
|
|
6395
6592
|
exports.createActor = createActor;
|
|
6396
6593
|
exports.createCustomEffect = createCustomEffect;
|
|
6397
6594
|
exports.createDeadLetterEffect = createDeadLetterEffect;
|
|
6595
|
+
exports.createDoneActorEvent = createDoneActorEvent;
|
|
6398
6596
|
exports.createEmitEffect = createEmitEffect;
|
|
6597
|
+
exports.createErrorActorEvent = createErrorActorEvent;
|
|
6399
6598
|
exports.createErrorPlatformEvent = createErrorPlatformEvent;
|
|
6400
6599
|
exports.createInertActorScope = createInertActorScope;
|
|
6401
6600
|
exports.createInitEvent = createInitEvent;
|
|
@@ -6430,6 +6629,7 @@ exports.getRootActorId = getRootActorId;
|
|
|
6430
6629
|
exports.getSnapshotActorRef = getSnapshotActorRef;
|
|
6431
6630
|
exports.getStateNodeByPath = getStateNodeByPath;
|
|
6432
6631
|
exports.getStateNodes = getStateNodes;
|
|
6632
|
+
exports.getTimerStart = getTimerStart;
|
|
6433
6633
|
exports.getTransitionResult = getTransitionResult;
|
|
6434
6634
|
exports.hasAmbientInspector = hasAmbientInspector;
|
|
6435
6635
|
exports.hasEffect = hasEffect;
|
|
@@ -6453,6 +6653,7 @@ exports.mergeActorIdCounters = mergeActorIdCounters;
|
|
|
6453
6653
|
exports.parseDelayToMilliseconds = parseDelayToMilliseconds;
|
|
6454
6654
|
exports.parseGeneratedActorId = parseGeneratedActorId;
|
|
6455
6655
|
exports.pathToStateValue = pathToStateValue;
|
|
6656
|
+
exports.recordTimerStart = recordTimerStart;
|
|
6456
6657
|
exports.reserveChildId = reserveChildId;
|
|
6457
6658
|
exports.resolveActionsWithContext = resolveActionsWithContext;
|
|
6458
6659
|
exports.resolveReferencedActor = resolveReferencedActor;
|
|
@@ -6463,6 +6664,7 @@ exports.setInertActorScopeSnapshot = setInertActorScopeSnapshot;
|
|
|
6463
6664
|
exports.setSnapshotActorRef = setSnapshotActorRef;
|
|
6464
6665
|
exports.stopActor = stopActor;
|
|
6465
6666
|
exports.subscriptionLogic = subscriptionLogic;
|
|
6667
|
+
exports.systemLogicMetadata = systemLogicMetadata;
|
|
6466
6668
|
exports.takeSpawnAllocationCounters = takeSpawnAllocationCounters;
|
|
6467
6669
|
exports.terminateActor = terminateActor;
|
|
6468
6670
|
exports.toArray = toArray;
|