xstate 6.0.0-alpha.64 → 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 +27 -0
- package/dist/{createActor-521c8805.development.cjs.js → createActor-1df42c9e.development.cjs.js} +205 -31
- package/dist/{createActor-8493caa9.esm.js → createActor-4629a290.esm.js} +133 -28
- package/dist/{createActor-bd2527dc.development.esm.js → createActor-8f57b47b.development.esm.js} +205 -31
- package/dist/{createActor-29adb8a4.cjs.js → createActor-b987e971.cjs.js} +133 -28
- 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 +5 -3
- package/dist/declarations/src/createMachineFromConfig.d.ts +13 -2
- 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/machineVersions.d.ts +2 -2
- package/dist/declarations/src/serialize.d.ts +7 -2
- package/dist/declarations/src/setup.d.ts +60 -19
- package/dist/declarations/src/types.d.ts +44 -23
- package/dist/declarations/src/types.v6.d.ts +9 -7
- package/dist/declarations/src/utils.d.ts +9 -0
- package/dist/{effectDescriptor-6dbc400a.cjs.js → effectDescriptor-4cf24593.cjs.js} +1 -1
- package/dist/{effectDescriptor-85ea06c9.esm.js → effectDescriptor-973fc1ba.esm.js} +1 -1
- package/dist/{effectDescriptor-62b53c6d.development.cjs.js → effectDescriptor-b2b7deaa.development.cjs.js} +1 -1
- package/dist/{effectDescriptor-f76948fc.development.esm.js → effectDescriptor-c4671afc.development.esm.js} +1 -1
- package/dist/{index-5587e13b.cjs.js → index-0cf33789.development.cjs.js} +29 -14
- package/dist/{index-b0dc2b39.esm.js → index-15382daf.development.esm.js} +29 -14
- package/dist/{index-90ebf5ae.development.esm.js → index-52ad3d45.esm.js} +29 -14
- package/dist/{index-2afc57c3.development.cjs.js → index-f201d440.cjs.js} +29 -14
- 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 +2 -2
- package/dist/xstate-durable.development.cjs.js +2 -2
- package/dist/xstate-durable.development.esm.js +2 -2
- package/dist/xstate-durable.esm.js +2 -2
- 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 +475 -257
- package/dist/xstate.development.cjs.js +497 -260
- package/dist/xstate.development.esm.js +499 -262
- package/dist/xstate.esm.js +477 -259
- 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
|
@@ -33,6 +33,13 @@ timers without replaying entry actions. Await `executeEffects()` before persisti
|
|
|
33
33
|
to retain accepted timer deadlines. The host handles root error snapshots;
|
|
34
34
|
`run()` rejects with the root error. See [durable execution](../../docs/durable-execution.md).
|
|
35
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
|
+
|
|
36
43
|
<!-- typed inline invocation from packages/core/src/setup.ts -->
|
|
37
44
|
|
|
38
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).
|
|
@@ -41,6 +48,14 @@ Pass an async function directly as `src`, with `schemas.input`, `schemas.output`
|
|
|
41
48
|
|
|
42
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.
|
|
43
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).
|
|
58
|
+
|
|
44
59
|
For flat finite state machines, `xstate/fsm` provides a tiny pure transition
|
|
45
60
|
table with context and TypeScript support:
|
|
46
61
|
|
|
@@ -635,3 +650,15 @@ For these reasons, it is impractical for our team to be bound by decisions taken
|
|
|
635
650
|
### Packages
|
|
636
651
|
|
|
637
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-521c8805.development.cjs.js → createActor-1df42c9e.development.cjs.js}
RENAMED
|
@@ -530,7 +530,33 @@ function withActorScope(args, actorScope) {
|
|
|
530
530
|
}
|
|
531
531
|
|
|
532
532
|
function execCustomEffect() {
|
|
533
|
-
|
|
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.`);
|
|
534
560
|
}
|
|
535
561
|
function execEmitEffect(runtime = this.source.system) {
|
|
536
562
|
return runtime.emitEvent(this.source, this.event);
|
|
@@ -1059,6 +1085,16 @@ function createTransitionEnqueue(actorScope, actions, internalEvents, actorSubsc
|
|
|
1059
1085
|
props[key] = guard(props[key]);
|
|
1060
1086
|
}
|
|
1061
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
|
+
}
|
|
1062
1098
|
pushBuiltInAction(actions, action, ...args);
|
|
1063
1099
|
}));
|
|
1064
1100
|
enqueueClosers.set(enqueue, () => {
|
|
@@ -1083,6 +1119,10 @@ function lateEnqueueCall() {
|
|
|
1083
1119
|
throw new Error('enq.* called after the transition function returned');
|
|
1084
1120
|
}
|
|
1085
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
|
+
}
|
|
1086
1126
|
function getBuiltInActionFields(action, args) {
|
|
1087
1127
|
switch (action) {
|
|
1088
1128
|
case builtInActions['@xstate.spawn']:
|
|
@@ -2288,6 +2328,10 @@ function getDelayedTransitions(stateNode) {
|
|
|
2288
2328
|
}));
|
|
2289
2329
|
}
|
|
2290
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
|
+
}
|
|
2291
2335
|
const normalizedTarget = normalizeTarget(transitionConfig.target);
|
|
2292
2336
|
const reenter = transitionConfig.reenter ?? false;
|
|
2293
2337
|
const target = resolveTarget(stateNode, normalizedTarget);
|
|
@@ -2298,14 +2342,46 @@ function formatTransition(stateNode, descriptor, transitionConfig) {
|
|
|
2298
2342
|
source: stateNode,
|
|
2299
2343
|
reenter,
|
|
2300
2344
|
eventType: descriptor,
|
|
2301
|
-
toJSON: () => (
|
|
2302
|
-
...transition,
|
|
2303
|
-
source: `#${stateNode.id}`,
|
|
2304
|
-
target: target ? target.map(t => `#${t.id}`) : undefined
|
|
2305
|
-
})
|
|
2345
|
+
toJSON: () => transitionDefinitionToJSON(transition)
|
|
2306
2346
|
};
|
|
2307
2347
|
return transition;
|
|
2308
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
|
+
}
|
|
2309
2385
|
function isStateNodeDescendantOf(stateNode, ancestor) {
|
|
2310
2386
|
for (let current = stateNode.parent; current; current = current.parent) {
|
|
2311
2387
|
if (current === ancestor) {
|
|
@@ -2648,12 +2724,18 @@ function removeConflictingTransitions(enabledTransitions, stateNodeSet, snapshot
|
|
|
2648
2724
|
}
|
|
2649
2725
|
function createTransitionResultResolver(snapshot, event, actorScope, resolveActions, selectionResults) {
|
|
2650
2726
|
let cache;
|
|
2651
|
-
return transition => {
|
|
2652
|
-
if (!transition.to) {
|
|
2653
|
-
|
|
2654
|
-
|
|
2655
|
-
|
|
2656
|
-
|
|
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
|
+
};
|
|
2657
2739
|
}
|
|
2658
2740
|
let result = cache?.get(transition);
|
|
2659
2741
|
if (!result) {
|
|
@@ -2881,13 +2963,20 @@ function microstep(transitions, currentSnapshot, actorScope, event, isInitial, i
|
|
|
2881
2963
|
children: args.children,
|
|
2882
2964
|
actions: args.actions,
|
|
2883
2965
|
actors: args.actors,
|
|
2966
|
+
guards: currentSnapshot.machine.sources.guards,
|
|
2967
|
+
delays: currentSnapshot.machine.sources.delays,
|
|
2884
2968
|
input,
|
|
2885
2969
|
stateNode
|
|
2886
2970
|
}, actorScope) : {
|
|
2887
2971
|
...args,
|
|
2972
|
+
guards: currentSnapshot.machine.sources.guards,
|
|
2973
|
+
delays: currentSnapshot.machine.sources.delays,
|
|
2888
2974
|
input,
|
|
2889
2975
|
stateNode
|
|
2890
|
-
},
|
|
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 ? {
|
|
2891
2980
|
_special: true
|
|
2892
2981
|
} : {});
|
|
2893
2982
|
return [[wrappedAction], undefined, undefined];
|
|
@@ -2925,7 +3014,7 @@ function microstep(transitions, currentSnapshot, actorScope, event, isInitial, i
|
|
|
2925
3014
|
}
|
|
2926
3015
|
for (const exitStateNode of statesToExit) {
|
|
2927
3016
|
const stateInput = getStateInput(currentSnapshot, exitStateNode.id);
|
|
2928
|
-
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];
|
|
2929
3018
|
if (internalEvents?.length) {
|
|
2930
3019
|
internalQueue.push(...internalEvents);
|
|
2931
3020
|
}
|
|
@@ -2955,11 +3044,20 @@ function microstep(transitions, currentSnapshot, actorScope, event, isInitial, i
|
|
|
2955
3044
|
let context = nextState.context;
|
|
2956
3045
|
const transitionActions = [];
|
|
2957
3046
|
const internalEvents = [];
|
|
3047
|
+
const resolvedTransitions = [];
|
|
2958
3048
|
for (const t of filteredTransitions) {
|
|
2959
|
-
|
|
2960
|
-
|
|
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);
|
|
2961
3060
|
}
|
|
2962
|
-
const res = getCurrentTransitionResult(t);
|
|
2963
3061
|
if (res.context !== undefined) {
|
|
2964
3062
|
context = mergeContextPatch(context, res.context);
|
|
2965
3063
|
}
|
|
@@ -3115,7 +3213,7 @@ function microstep(transitions, currentSnapshot, actorScope, event, isInitial, i
|
|
|
3115
3213
|
const {
|
|
3116
3214
|
targets,
|
|
3117
3215
|
input
|
|
3118
|
-
} = getCurrentTransitionResult(transition);
|
|
3216
|
+
} = getCurrentTransitionResult(transition, true);
|
|
3119
3217
|
if (input && targets) {
|
|
3120
3218
|
for (const targetNode of targets.filter(targetNode => enteredTargetsByTransition.get(transition)?.has(targetNode))) {
|
|
3121
3219
|
stateInputMap[targetNode.id] = input;
|
|
@@ -3124,12 +3222,12 @@ function microstep(transitions, currentSnapshot, actorScope, event, isInitial, i
|
|
|
3124
3222
|
}
|
|
3125
3223
|
}
|
|
3126
3224
|
const completedNodes = new Set();
|
|
3127
|
-
const children = {
|
|
3128
|
-
...nextState.children
|
|
3129
|
-
};
|
|
3130
3225
|
for (const stateNodeToEnter of [...statesToEnter].sort((a, b) => a.order - b.order)) {
|
|
3131
3226
|
mutStateNodeSet.add(stateNodeToEnter);
|
|
3132
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;
|
|
3133
3231
|
|
|
3134
3232
|
// Final states are inert, so (as in SCXML) their invocations never
|
|
3135
3233
|
// start.
|
|
@@ -3168,7 +3266,10 @@ function microstep(transitions, currentSnapshot, actorScope, event, isInitial, i
|
|
|
3168
3266
|
args: [actor]
|
|
3169
3267
|
});
|
|
3170
3268
|
if (invokeDef.id) {
|
|
3171
|
-
children
|
|
3269
|
+
children = {
|
|
3270
|
+
...children,
|
|
3271
|
+
[invokeDef.id]: actor
|
|
3272
|
+
};
|
|
3172
3273
|
}
|
|
3173
3274
|
}
|
|
3174
3275
|
if (invoked) {
|
|
@@ -3288,7 +3389,12 @@ function microstep(transitions, currentSnapshot, actorScope, event, isInitial, i
|
|
|
3288
3389
|
nextStateNodesToExit.forEach(stateNode => {
|
|
3289
3390
|
if (stateNode.exit) {
|
|
3290
3391
|
const stateInput = getStateInput(nextState, stateNode.id);
|
|
3291
|
-
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
|
+
}
|
|
3292
3398
|
allExitActions.push(...exitActions);
|
|
3293
3399
|
if (nextInternalEvents?.length) {
|
|
3294
3400
|
internalQueue.push(...nextInternalEvents);
|
|
@@ -3309,14 +3415,14 @@ function microstep(transitions, currentSnapshot, actorScope, event, isInitial, i
|
|
|
3309
3415
|
// If context was changed (e.g. by entry actions during self-transition),
|
|
3310
3416
|
// clone to ensure reference inequality for eventless transition re-evaluation
|
|
3311
3417
|
if (nextState.context !== originalContext) {
|
|
3312
|
-
return [nextState === currentSnapshot ? cloneMachineSnapshot(nextState) : nextState, executableActions];
|
|
3418
|
+
return [nextState === currentSnapshot ? cloneMachineSnapshot(nextState) : nextState, executableActions, resolvedTransitions];
|
|
3313
3419
|
}
|
|
3314
|
-
return [nextState, executableActions];
|
|
3420
|
+
return [nextState, executableActions, resolvedTransitions];
|
|
3315
3421
|
}
|
|
3316
3422
|
return [cloneMachineSnapshot(nextState, {
|
|
3317
3423
|
_nodes: nextStateNodes,
|
|
3318
3424
|
historyValue
|
|
3319
|
-
}), executableActions];
|
|
3425
|
+
}), executableActions, resolvedTransitions];
|
|
3320
3426
|
}
|
|
3321
3427
|
}
|
|
3322
3428
|
|
|
@@ -3426,6 +3532,15 @@ class InfiniteTransitionError extends Error {
|
|
|
3426
3532
|
}
|
|
3427
3533
|
}
|
|
3428
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
|
+
}
|
|
3429
3544
|
let nextSnapshot = snapshot;
|
|
3430
3545
|
const microsteps = initialMicrosteps.slice();
|
|
3431
3546
|
// Whether a transition handled the external event. A handled event always
|
|
@@ -3494,6 +3609,7 @@ function macrostep(snapshot, event, actorScope, internalQueue, initialMicrosteps
|
|
|
3494
3609
|
};
|
|
3495
3610
|
}
|
|
3496
3611
|
function addMicrostep(step, transitions) {
|
|
3612
|
+
transitions = step[2] ?? transitions;
|
|
3497
3613
|
// collect microsteps; their transitions are surfaced on the enclosing
|
|
3498
3614
|
// '@xstate.transition' event via its `microsteps[]` facet (there is no
|
|
3499
3615
|
// standalone microstep event) and returned by `getMicrosteps()`
|
|
@@ -3649,6 +3765,42 @@ function assertSyncTransitionResult(res, event, sourceId, kind) {
|
|
|
3649
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.`);
|
|
3650
3766
|
}
|
|
3651
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
|
+
}
|
|
3652
3804
|
let transitionEffectEnqueue;
|
|
3653
3805
|
function getTransitionEffectEnqueue() {
|
|
3654
3806
|
return transitionEffectEnqueue ??= createEnqueueObject({
|
|
@@ -3978,6 +4130,17 @@ function isMachineSnapshot(value) {
|
|
|
3978
4130
|
return !!value && typeof value === 'object' && 'machine' in value && 'value' in value;
|
|
3979
4131
|
}
|
|
3980
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
|
+
|
|
3981
4144
|
/** @public */
|
|
3982
4145
|
|
|
3983
4146
|
const machineSnapshotMatches = function matches(testValue) {
|
|
@@ -4502,7 +4665,14 @@ function getAllOwnEventDescriptors(snapshot) {
|
|
|
4502
4665
|
return [...new Set([...snapshot.nodes.flatMap(sn => sn.ownEvents)])];
|
|
4503
4666
|
}
|
|
4504
4667
|
|
|
4505
|
-
/**
|
|
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
|
+
*/
|
|
4506
4676
|
function getAllOwnEvents(snapshot) {
|
|
4507
4677
|
const events = snapshot.nodes.flatMap(stateNode => [...stateNode.transitions.values()].flatMap(transitions => transitions.map(transition => {
|
|
4508
4678
|
const event = {
|
|
@@ -6063,7 +6233,10 @@ class Actor {
|
|
|
6063
6233
|
const [nextSnapshot, effects] = nextState;
|
|
6064
6234
|
if (nextSnapshot === previousSnapshot && !effects.length &&
|
|
6065
6235
|
// State machine snapshots only; other logic may ignore events freely.
|
|
6066
|
-
'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')) {
|
|
6067
6240
|
this._reportUnhandledEvent(event);
|
|
6068
6241
|
}
|
|
6069
6242
|
snapshot = nextSnapshot;
|
|
@@ -6084,10 +6257,11 @@ class Actor {
|
|
|
6084
6257
|
_reportUnhandledEvent(event) {
|
|
6085
6258
|
safeCall(() => this.options.onUnhandledEvent?.(event, this._snapshot));
|
|
6086
6259
|
{
|
|
6260
|
+
const to = event.type === 'xstate.route' ? ` to ${JSON.stringify(event.to)}` : '';
|
|
6087
6261
|
const warned = this._warnedUnhandledTypes ??= new Set();
|
|
6088
|
-
if (!warned.has(event.type)) {
|
|
6089
|
-
warned.add(event.type);
|
|
6090
|
-
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`);
|
|
6091
6265
|
}
|
|
6092
6266
|
}
|
|
6093
6267
|
}
|