xstate 6.0.0-beta.0 → 6.0.0-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (81) hide show
  1. package/bin/xstate.js +10 -4
  2. package/dist/{createActor-551369be.development.esm.js → createActor-0250284a.development.esm.js} +244 -41
  3. package/dist/{createActor-cbe3abac.esm.js → createActor-20a97166.esm.js} +243 -40
  4. package/dist/{createActor-a49ae0ce.development.cjs.js → createActor-c029d29f.development.cjs.js} +245 -40
  5. package/dist/{createActor-25900f76.cjs.js → createActor-c91c9ffe.cjs.js} +244 -39
  6. package/dist/declarations/src/createActor.d.ts +3 -1
  7. package/dist/declarations/src/createMachine.d.ts +6 -3
  8. package/dist/declarations/src/durable/index.d.ts +43 -8
  9. package/dist/declarations/src/effectDescriptor.d.ts +2 -2
  10. package/dist/declarations/src/machineVersion.types.d.ts +2 -2
  11. package/dist/declarations/src/setup.d.ts +5 -5
  12. package/dist/declarations/src/systemTransition.d.ts +1 -1
  13. package/dist/declarations/src/types.d.ts +11 -3
  14. package/dist/declarations/src/types.v6.d.ts +37 -2
  15. package/dist/{effectDescriptor-f73da4ec.development.cjs.js → effectDescriptor-3fff5c51.development.cjs.js} +5 -5
  16. package/dist/{effectDescriptor-2fa27c54.cjs.js → effectDescriptor-47f73e7e.cjs.js} +5 -5
  17. package/dist/{effectDescriptor-321a8ce9.development.esm.js → effectDescriptor-5ac7dccb.development.esm.js} +5 -5
  18. package/dist/{effectDescriptor-d8b0f749.esm.js → effectDescriptor-e93df910.esm.js} +5 -5
  19. package/dist/{machineSchemas-88d01821.cjs.js → legacySnapshot-8d595687.cjs.js} +117 -1
  20. package/dist/{machineSchemas-45c21bc2.esm.js → legacySnapshot-9e06feb4.esm.js} +116 -2
  21. package/dist/{machineSchemas-b5736804.development.esm.js → legacySnapshot-ae6594a7.development.esm.js} +117 -3
  22. package/dist/{machineSchemas-6d96bef4.development.cjs.js → legacySnapshot-ebf0b2b8.development.cjs.js} +118 -2
  23. package/dist/xstate-actors.cjs.js +1 -1
  24. package/dist/xstate-actors.development.cjs.js +1 -1
  25. package/dist/xstate-actors.development.esm.js +2 -2
  26. package/dist/xstate-actors.esm.js +2 -2
  27. package/dist/xstate-actors.umd.min.js +1 -1
  28. package/dist/xstate-actors.umd.min.js.map +1 -1
  29. package/dist/xstate-durable.cjs.js +120 -10
  30. package/dist/xstate-durable.development.cjs.js +120 -10
  31. package/dist/xstate-durable.development.esm.js +120 -10
  32. package/dist/xstate-durable.esm.js +120 -10
  33. package/dist/xstate-durable.umd.min.js +1 -1
  34. package/dist/xstate-durable.umd.min.js.map +1 -1
  35. package/dist/xstate-fsm.cjs.js +4 -4
  36. package/dist/xstate-fsm.development.cjs.js +4 -4
  37. package/dist/xstate-fsm.development.esm.js +4 -4
  38. package/dist/xstate-fsm.esm.js +4 -4
  39. package/dist/xstate-fsm.umd.min.js +1 -1
  40. package/dist/xstate-fsm.umd.min.js.map +1 -1
  41. package/dist/xstate-graph.cjs.js +1 -1
  42. package/dist/xstate-graph.development.cjs.js +1 -1
  43. package/dist/xstate-graph.development.esm.js +2 -2
  44. package/dist/xstate-graph.esm.js +2 -2
  45. package/dist/xstate-graph.umd.min.js +1 -1
  46. package/dist/xstate-graph.umd.min.js.map +1 -1
  47. package/dist/xstate-persist.cjs.js +23 -12
  48. package/dist/xstate-persist.development.cjs.js +23 -12
  49. package/dist/xstate-persist.development.esm.js +17 -6
  50. package/dist/xstate-persist.esm.js +17 -6
  51. package/dist/xstate-persist.umd.min.js +1 -1
  52. package/dist/xstate-persist.umd.min.js.map +1 -1
  53. package/dist/xstate-validation.cjs.js +1 -1
  54. package/dist/xstate-validation.development.cjs.js +1 -1
  55. package/dist/xstate-validation.development.esm.js +1 -1
  56. package/dist/xstate-validation.esm.js +1 -1
  57. package/dist/xstate.cjs.js +63 -51
  58. package/dist/xstate.development.cjs.js +63 -51
  59. package/dist/xstate.development.esm.js +53 -41
  60. package/dist/xstate.esm.js +53 -41
  61. package/dist/xstate.umd.min.js +1 -1
  62. package/dist/xstate.umd.min.js.map +1 -1
  63. package/docs/agents.md +9 -0
  64. package/docs/api-stability.json +273 -249
  65. package/docs/backend-workflows.md +3 -3
  66. package/docs/durable-execution.md +72 -48
  67. package/docs/errors.md +2 -2
  68. package/docs/examples.md +34 -34
  69. package/docs/fsm.md +2 -1
  70. package/docs/guards.md +1 -1
  71. package/docs/index.md +1 -1
  72. package/docs/invoke.md +34 -2
  73. package/docs/persistence.md +16 -10
  74. package/docs/serialization.md +1 -1
  75. package/docs/setup-and-provide.md +3 -3
  76. package/docs/spawn.md +1 -1
  77. package/docs/system-transitions.md +6 -6
  78. package/docs/tutorial.md +5 -5
  79. package/docs/typescript.md +9 -0
  80. package/docs/xstate-v5-to-v6.md +176 -19
  81. package/package.json +1 -1
@@ -85,7 +85,7 @@ const machine = machineFromJSON(definition, {
85
85
 
86
86
  The second argument supplies the runtime sources the definition refers to: `actions`, `guards`, `actors`, `delays`, and `evaluators` for code expressions. Invokes keep their string `src` names, so a machine built this way can be cloned with different actors through [`provide()`](setup-and-provide.md), including when restoring its children. Deserialization still requires an implementation for every referenced actor; provide the initial implementations in the second argument, then override them by name.
87
87
 
88
- Actions in JSON are objects. The built-in vocabulary is `{ type: '@xstate.raise', event, id?, delay? }`, `{ type: '@xstate.emit', event }`, `{ type: '@xstate.assign', context }`, `{ type: '@xstate.cancel', id }` and `{ type: '@xstate.log', args }`. Any other `{ type, params }` object is a custom action resolved against the `actions` sources. Guards are `{ type, params? }` references resolved against `guards`. Two guard types are built in: `{ type: '@xstate.not', params: { guard } }` negates another guard reference or expression, and `{ type: '@xstate.stateIn', params: { stateId: '#id' } }` is true while the state node with that `id` is active (the same check as `snapshot.matches('#id')`). Source names, and the names of `actions`, `guards` and `delays` defined in the JSON, may not start with `xstate.` or `@xstate.`; those prefixes are reserved for built-in names. Because the runtime invokes these references itself, a JSON-referenced guard receives the transition arguments object first and the declared `params` second — unlike code-authored machines, where guards are plain functions called by your own transition code.
88
+ Actions in JSON are objects. The built-in vocabulary is `{ type: '@xstate.raise', event, id?, delay? }`, `{ type: '@xstate.emit', event }`, `{ type: '@xstate.assign', context }`, `{ type: '@xstate.cancel', id }` and `{ type: '@xstate.log', args }`. Any other `{ type, params }` object is a custom action resolved against the `actions` sources. Guards are `{ type, params? }` references resolved against `guards`. Two guard types are built in: `{ type: '@xstate.not', params: { guard } }` negates another guard reference or expression, and `{ type: '@xstate.stateIn', params: { stateId: '#id' } }` is true while the state node with that `id` is active (the same check as `snapshot.matches('#id')`). Source names, and the names of `actions`, `guards` and `delays` defined in the JSON, may not start with `xstate.` or `@xstate.`; those prefixes are reserved for built-in names. Because the runtime invokes these references itself, a JSON-referenced guard receives the transition arguments object first and the declared `params` second. This differs from code-authored machines, where guards are plain functions called by your own transition code.
89
89
 
90
90
  Machines built from JSON round-trip losslessly. The loader copies the definition, and each serialization returns a fresh copy. Mutating either copy does not change the machine.
91
91
 
@@ -140,8 +140,8 @@ Validation can be installed, replaced or disabled by a derived setup. Installing
140
140
 
141
141
  Where a validation failure surfaces depends on which side of the delivery boundary produced the invalid value:
142
142
 
143
- - **Events arriving from outside the actor** — from `actor.send` or from another actor — are rejected at the boundary when their payload fails its schema, or when the event type is undeclared and `unknownEvents` is `'error'`. The event is never delivered: the actor does not transition, does not error, and no API throws. The rejection is reported to the `onRejectedEvent` dead-letter hook on `createActor` options and as a development-mode console warning. In pure `transition(...)` calls, the snapshot is returned unchanged together with a `@xstate.deadLetter` effect carrying the rejection.
144
- - **Values the actor produces itself** — input, context, output, emitted events and delayed raised events — error the actor when they fail their schema, and pure `transition(...)`/`initialTransition(...)` throw an `ActorValidationError`. These are machine bugs.
143
+ - **Events arriving from outside the actor** (from `actor.send` or from another actor) are rejected at the boundary when their payload fails its schema, or when the event type is undeclared and `unknownEvents` is `'error'`. The event is never delivered: the actor does not transition, does not error, and no API throws. The rejection is reported to the `onRejectedEvent` dead-letter hook on `createActor` options and as a development-mode console warning. In pure `transition(...)` calls, the snapshot is returned unchanged together with a `@xstate.deadLetter` effect carrying the rejection.
144
+ - **Values the actor produces itself** (input, context, output, emitted events and delayed raised events) error the actor when they fail their schema, and pure `transition(...)`/`initialTransition(...)` throw an `ActorValidationError`. These are machine bugs.
145
145
 
146
146
  ## Sources on the machine
147
147
 
@@ -175,7 +175,7 @@ idle: {
175
175
  }
176
176
  ```
177
177
 
178
- Named actions and guards are plain functions called with only their params — nothing is injected. Pass values from `context` or the event explicitly. Declare param types on the function itself, or with `schemas.actions` and `schemas.guards` so the params are checked before the implementations exist:
178
+ Named actions and guards are plain functions called with only their params. Nothing is injected. Pass values from `context` or the event explicitly. Declare param types on the function itself, or with `schemas.actions` and `schemas.guards` so the params are checked before the implementations exist:
179
179
 
180
180
  ```ts
181
181
  setup({
package/docs/spawn.md CHANGED
@@ -46,7 +46,7 @@ The name is checked against `actors`; its required input and returned actor refe
46
46
 
47
47
  Passing the logic value remains convenient: `enq.spawn(actors.upload, options)`. XState recovers its registered name when possible. Inline unregistered logic may run but cannot be durably persisted.
48
48
 
49
- Spawning with an `id` that a live child of the same parent already uses throws: an address names at most one live actor. Stop the existing child first — an id stopped earlier in the same transition is free to reuse, and a child that completed on its own frees its id for the transition handling its completion. Generate ids from something stable, such as a file or participant id.
49
+ Spawning with an `id` that a live child of the same parent already uses throws: an address names at most one live actor. Stop the existing child first. An id stopped earlier in the same transition is free to reuse, and a child that completed on its own frees its id for the transition handling its completion. Generate ids from something stable, such as a file or participant id.
50
50
 
51
51
  ## Where you can spawn
52
52
 
@@ -67,8 +67,8 @@ actor's macrostep. Microsteps are atomic; system snapshots contain no unfinished
67
67
  microstep or continuation state. A UI presenting individual completed
68
68
  microsteps within a macrostep needs a separate tracing interface.
69
69
 
70
- Snapshot actor references are data: `{ $actor, incarnation }`, the same shape as `SerializedActorRef`, with a string `incarnation`. Use `$actor`
71
- as `actorPath`, for example `snapshot.actors[snapshot.root].snapshot.children.worker.$actor`.
70
+ Snapshot actor references are data: `{ '@actor': address, incarnation }`, the same shape as `SerializedActorRef`, with a string `incarnation`. Use `@actor`
71
+ as `actorPath`, for example `snapshot.actors[snapshot.root].snapshot.children.worker["@actor"]`.
72
72
  Paths encode IDs; avoid concatenating unescaped IDs. Replacing an actor at the
73
73
  same path allocates a fresh incarnation.
74
74
 
@@ -101,7 +101,7 @@ Select a pending timer in a simulator with its current occurrence:
101
101
 
102
102
  ```ts
103
103
  const selected = snapshot.timers.find((timer) => timer.dueAt === 6000)!;
104
- const [next] = systemTransition(systemLogic, snapshot, selected.source.$actor, {
104
+ const [next] = systemTransition(systemLogic, snapshot, selected.source["@actor"], {
105
105
  type: 'xstate.timer',
106
106
  occurrence: selected.occurrence
107
107
  });
@@ -128,7 +128,7 @@ using the source actor's definition and saved input; the host owns the
128
128
  external implementation. Its result reenters as a correlated input:
129
129
 
130
130
  ```ts
131
- const [resolved] = systemTransition(systemLogic, pendingSnapshot, effect.source.$actor, {
131
+ const [resolved] = systemTransition(systemLogic, pendingSnapshot, effect.source["@actor"], {
132
132
  type: 'xstate.system.effect.result',
133
133
  effectId: effect.id,
134
134
  event: { type: 'xstate.async.resolve', data: result }
@@ -150,8 +150,8 @@ input, outputs, events and effect arguments are JSON-compatible. Actor reference
150
150
  state-node references, errors and named mappers use data markers. Errors retain
151
151
  name/message. Undefined properties follow ordinary JSON rules. Cycles,
152
152
  unregistered functions, live actors, class instances, symbols, bigint and
153
- nonfinite numbers are rejected. `$actor`, `$stateNode`, `$error` and
154
- `$systemMapper` marker shapes are reserved. Logic versions must match on replay.
153
+ nonfinite numbers are rejected. `@actor`, `@stateNode`, `@error` and
154
+ `@systemMapper` marker shapes are reserved. Logic versions must match on replay.
155
155
 
156
156
  Transition functions, guards and mappers must be deterministic and pure.
157
157
  Read external values from input/events and virtual time from `system._clock.now()`.
package/docs/tutorial.md CHANGED
@@ -430,7 +430,7 @@ Nothing about the machine changes. A UI reads snapshots and sends events.
430
430
 
431
431
  ```html
432
432
  <p id="status">stopped</p>
433
- <p id="title">—</p>
433
+ <p id="title"></p>
434
434
  <button id="play">Play</button> <button id="pause">Pause</button>
435
435
  <button id="stop">Stop</button> <button id="previous">Previous</button>
436
436
  <button id="next">Next</button> <button id="mute">Mute</button>
@@ -475,7 +475,7 @@ Frameworks work the same way, with a hook or store binding instead of manual sub
475
475
 
476
476
  You built one machine with context, guards, invoked async logic, hierarchy, parallel regions, delayed events, emitted events and a UI. From here:
477
477
 
478
- - [Statecharts](statecharts.md) — nested, parallel and history states
479
- - [Actors](actors.md) — spawning, communication and actor systems
480
- - [Frameworks](frameworks.md) — bindings for React, Vue, Svelte and Solid
481
- - [Testing](testing.md) — testing machines and the actors that run them
478
+ - [Statecharts](statecharts.md): nested, parallel and history states
479
+ - [Actors](actors.md): spawning, communication and actor systems
480
+ - [Frameworks](frameworks.md): bindings for React, Vue, Svelte and Solid
481
+ - [Testing](testing.md): testing machines and the actors that run them
@@ -49,6 +49,12 @@ adding explicit type annotations.
49
49
 
50
50
  For an actor used in one state, `invoke: s.createInvoke({ src, input, onDone })` infers input and completion output from `src`. Keep the helper inline in `s.createMachine(...)` to also infer that state's narrowed context, ancestor context, state input and transition target requirements. The helper works alongside named actor sources. See [typed inline invokes](invoke.md#typed-inline-invokes).
51
51
 
52
+ A bare inline `invoke: { src: logic, onDone }` cannot correlate its source with
53
+ the output; with no registered actor sources it has `event.output: unknown`.
54
+ Use `s.createInvoke(...)` or a source registered in `setup({ actors })` when
55
+ reading the output. This is especially important for generated machine code:
56
+ the bare form cannot infer the output type from `src`.
57
+
52
58
  An async function can be used directly as `src`. Declare `schemas.input`, `schemas.output`, and `schemas.error` on the invoke to type the function and its lifecycle handlers together. Without `schemas.output`, `onDone.event.output` infers from the async return value.
53
59
 
54
60
  Public schema event keys create typed methods on `actor.trigger`; internal
@@ -84,6 +90,9 @@ When `schemas.events` is declared, each key in an `on` map must match a
84
90
  declared event type. Matching wildcards (`'*'`, `'user.*'`) and known reserved
85
91
  machine descriptors are allowed; misspelled or invented reserved prefixes are rejected. Without `schemas.events`, any key is accepted.
86
92
 
93
+ An empty `schemas.events: {}` means the machine has no public events. Final
94
+ state output callbacks still receive the declared context and input types.
95
+
87
96
  ```ts
88
97
  on: {
89
98
  toggel: { target: 'active' }
@@ -79,12 +79,12 @@ Beyond simplifying the action/guard surface, v6 introduces a number of features
79
79
 
80
80
  ## 1. Inline functions replace action creators
81
81
 
82
- This is the largest change. Action creators (`assign`, `raise`, `sendTo`, `emit`, `log`, `cancel`, `enqueueActions`, `spawnChild`, `stopChild`, `forwardTo`, `sendParent`) are **no longer exported**.
82
+ This is the largest change. Action creators (`assign`, `raise`, `sendTo`, `emit`, `log`, `cancel`, `enqueueActions`, `spawnChild`, `stopChild`, `forwardTo`, `sendParent`) are no longer exported.
83
83
 
84
- In v6, every `entry`, `exit`, and transition handler is a **single function** that receives `(args, enq)` where `enq` is a queue of side effects. The function can also **return** an object that the engine applies after it runs:
84
+ In v6, every `entry`, `exit`, and transition handler is a **single function** that receives `(args, enq)` where `enq` is a queue of side effects. The function can also return an object that the engine applies after it runs:
85
85
 
86
86
  - **Transition handlers** (`on`, `always`, `after`, `onTimeout`, `onDone`, `onError`) - may return a target, a new `context`, `reenter`, or `meta`.
87
- - **Entry / exit actions** - may return a new `context` (or `children`). They **cannot** return a `target`; entry/exit cannot transition.
87
+ - **Entry / exit actions** - may return a new `context` (or `children`). They cannot return a `target`; entry/exit cannot transition.
88
88
 
89
89
  Returned `context` values are patches. XState merges a patch into the current context at the top level (`{ ...context, ...patch }`): omitted keys keep their current values, and a nested object in the patch replaces the current value of that key. If a transition targets a state with a narrower `schemas.context`, the patch type requires the keys needed to satisfy that state's context.
90
90
 
@@ -241,7 +241,7 @@ An **entry / exit action** may return:
241
241
  | `context` | shallow context patch |
242
242
  | `children` | replacement children record |
243
243
 
244
- Returning nothing (`undefined`) means "no changes". For transition handlers specifically, returning nothing without calling `enq` also means **the event is treated as unhandled at this state** - useful for inline guarding. A handler that calls `enq` is taken even when it returns nothing, so check the condition first:
244
+ Returning nothing (`undefined`) means "no changes". For transition handlers specifically, returning nothing without calling `enq` also means the event is treated as unhandled at this state - useful for inline guarding. A handler that calls `enq` is taken even when it returns nothing, so check the condition first:
245
245
 
246
246
  ```ts
247
247
  // v6 - guard inline by returning undefined
@@ -294,6 +294,26 @@ on: {
294
294
  }
295
295
  ```
296
296
 
297
+ ### Transitions inside parallel regions
298
+
299
+ A transition whose targets stay inside one region of a parallel state exits
300
+ and reenters that region only. In v5 the same transition, when it reentered
301
+ its source or targeted a child state, exited the whole parallel state and
302
+ reset every sibling region to its initial state. v6 leaves the siblings where
303
+ they are. A transition declared on a sibling region for the same event still
304
+ runs, because the two exit sets no longer overlap.
305
+
306
+ ```ts
307
+ // Parallel state `p` with regions `a` and `b`.
308
+ // Event `t`: `a` reenters itself; `b.b1` moves to `b.b2`.
309
+ // v5 after `t`: { a: 'a1', b: 'b1' }: `b` was reset when `p` was exited.
310
+ // v6 after `t`: { a: 'a1', b: 'b2' }: only `a` was exited.
311
+ ```
312
+
313
+ Reset the sibling regions explicitly if the v5 behavior was intended: target
314
+ the parallel state itself, or list a target in every region.
315
+ `@xstate/scxml` keeps the SCXML exit semantics for SCXML sources.
316
+
297
317
  ---
298
318
 
299
319
  ## 2. Guards
@@ -552,7 +572,7 @@ These inferred typestates work as follows. See [Typestates](typestates.md) for t
552
572
 
553
573
  <!-- setup state contracts from SetupStateSchema in packages/core/src/setup.ts -->
554
574
 
555
- `setup()` still exists in v6 and still accepts `actions`, `guards`, `actors`, and `delays` (merged into every machine created from it). What changed: `types` is replaced by `schemas`, and `setup()` gains a `states` key for declaring **state contracts**. A contract can provide state-level schemas plus structural metadata and defaults such as `type`, `initial`, `history`, `target`, `id`, and `route`. This types `createMachine` and `createStateConfig`, including the `initial: { target, input }` form and transitions targeting those states. Setups that declare only schemas remain permissive for compatibility.
575
+ `setup()` still exists in v6 and still accepts `actions`, `guards`, `actors`, and `delays` (merged into every machine created from it). What changed: `types` is replaced by `schemas`, and `setup()` gains a `states` key for declaring **state contracts**. A contract can provide state-level schemas plus structural metadata and defaults such as `type`, `initial`, `history`, `target`, `id`, and `route`. This types `createMachine` and `createStateConfig`, including the `initial: { target, input }` form and transitions targeting those states. Setups that declare only schemas don't require state contracts, but unknown literal transition targets are still type errors.
556
576
 
557
577
  ```ts
558
578
  // v6 - setup with root schemas and state-level input schemas
@@ -717,7 +737,7 @@ const fetchUser = createAsyncLogic({
717
737
  });
718
738
  ```
719
739
 
720
- `enq.step(key, exec)` records each step's outcome on the snapshot under `effects[key]`, so on rehydration the step is **not re-executed** - the previous result is replayed. This makes async logic durable across restarts.
740
+ `enq.step(key, exec)` records each step's outcome on the snapshot under `effects[key]`, so on rehydration the step is not re-executed - the previous result is replayed. This makes async logic durable across restarts.
721
741
 
722
742
  When `timeout` elapses, the logic aborts and rejects with `TimeoutError` (also exported):
723
743
 
@@ -922,8 +942,8 @@ on: {
922
942
 
923
943
  ## 12. Internal events
924
944
 
925
- Events declared in `schemas.internalEvents` can be **raised** internally but
926
- are **rejected** by the public actor protocol. Wildcard patterns are supported.
945
+ Events declared in `schemas.internalEvents` can be raised internally but
946
+ are rejected by the public actor protocol. Wildcard patterns are supported.
927
947
 
928
948
  ```ts
929
949
  const machine = createMachine({
@@ -994,7 +1014,7 @@ states: {
994
1014
 
995
1015
  The function must resolve to a target - returning `undefined` throws "must resolve to a target".
996
1016
 
997
- Choice states are entered automatically by routing **into** them. They cannot themselves declare `invoke`, `after`, `on`, or `entry`/`exit` (machine creation rejects these).
1017
+ Choice states are entered automatically by routing into them. They cannot themselves declare `invoke`, `after`, `on`, or `entry`/`exit` (machine creation rejects these).
998
1018
 
999
1019
  ---
1000
1020
 
@@ -1026,7 +1046,7 @@ State `timeout` and `createAsyncLogic({ timeout })` accept `number` (ms) or `str
1026
1046
  | Seconds | `'5s'`, `'1.5s'` | integer or decimal + `s` suffix |
1027
1047
  | ISO 8601 duration | `'PT5S'`, `'PT1M30S'`, `'PT2H'`, `'P1D'`, `'P1W'`, `'P1DT12H'` | always starts with `P` |
1028
1048
 
1029
- Plain `'5m'`, `'1h'`, `'1d'`, `'1w'` (without the `P`/`PT` prefix) are **not** accepted - use the ISO 8601 form for anything beyond seconds and milliseconds.
1049
+ Plain `'5m'`, `'1h'`, `'1d'`, `'1w'` (without the `P`/`PT` prefix) are not accepted - use the ISO 8601 form for anything beyond seconds and milliseconds.
1030
1050
 
1031
1051
  `after` delays accept the same duration formats. A string delay is resolved against the machine's named `delays` first; only strings that don't match a named delay are parsed as durations.
1032
1052
 
@@ -1058,13 +1078,18 @@ Transition arrays are not accepted by the authoring APIs. Use a single
1058
1078
  transition function to select among targets. Serialized transition arrays are
1059
1079
  still accepted by `machineFromJSON(...)`.
1060
1080
 
1081
+ A chain of eventless transitions that never settles ends the actor in v6. The
1082
+ actor processes at most 1000 microsteps per event and then fails with
1083
+ `InfiniteTransitionError`, so `snapshot.status` is `'error'`. v5 ran such a
1084
+ loop until the process ran out of memory.
1085
+
1061
1086
  ---
1062
1087
 
1063
1088
  ## 16. Removed top-level exports
1064
1089
 
1065
1090
  <!-- exports removed from and added to packages/core/src/index.ts on the v6 branch (relative to main / v5) -->
1066
1091
 
1067
- These exports have been **removed** from `xstate`:
1092
+ These exports have been removed from `xstate`:
1068
1093
 
1069
1094
  - Action creators (entire `actions.ts` module): `assign`, `raise`, `sendTo`, `sendParent`, `forwardTo`, `emit`, `log`, `cancel`, `enqueueActions`, `spawnChild`, `stopChild`, `stop`, plus their `*Action`/`*Params` types
1070
1095
  - Guard combinators and helpers: `and`, `or`, `not`, `stateIn`
@@ -1091,7 +1116,7 @@ These exports have been **removed** from `xstate`:
1091
1116
 
1092
1117
  `SetupReturn`, the type returned by `setup(...)`, is still exported, but its type parameters changed, so a v5 `SetupReturn<TContext, TEvent, ...>` annotation no longer type-checks. Use `typeof yourSetup` to name the type of a specific setup.
1093
1118
 
1094
- These exports have been **added**:
1119
+ These exports have been added:
1095
1120
 
1096
1121
  - `setup` (reshaped - see §4) and `createSystem` for typed system registries
1097
1122
  - Setup state contract types: `SetupStateSchema`, `SetupStateSchemas`, `SetupStateType`
@@ -1250,7 +1275,7 @@ on: {
1250
1275
 
1251
1276
  ## 20. Persistence / rehydration
1252
1277
 
1253
- Persisted snapshots round-trip through `JSON.stringify` and rehydrate via `restoreActor(machine, persisted)` from `xstate/persist`. The internal shape changed, so v5 persisted snapshots are **not** binary-compatible with v6 - drain or migrate stored state during your rollout.
1278
+ Persisted snapshots round-trip through `JSON.stringify` and rehydrate via `restoreActor(machine, persisted)` from `xstate/persist`. The internal shape changed, so v5 persisted snapshots are not binary-compatible with v6 - drain or migrate stored state during your rollout.
1254
1279
 
1255
1280
  ```ts
1256
1281
  import { restoreActor } from 'xstate/persist';
@@ -1271,7 +1296,7 @@ Child actors, async logic with effects, and listener-resume semantics are all pa
1271
1296
 
1272
1297
  The host serializes the payload. Development builds warn when `context`, `output`, `error`, or state input holds a value that does not survive a JSON round-trip: functions, symbols, `BigInt`, `Map`, `Set`, cycles, `NaN`, and `Infinity`. Persisting a snapshot whose `context` contains a circular reference throws an error that names the actor and the context path. See [Persistence](persistence.md) for the format rules.
1273
1298
 
1274
- Actor refs in persisted `context` are stored as `{ "$actor": "<address>" }`. v5 stored them as `{ "xstate$$type": 1, "id": "<id>" }`; v6 does not read that shape, so convert those entries with [`machineVersions`](persistence.md#migrate-machine-versions) before restoring v5 snapshots.
1299
+ Actor refs in persisted `context` are stored as `{ "@actor": "<address>" }`. v5 stored them as `{ "xstate$$type": 1, "id": "<id>" }`; v6 does not read that shape, so convert those entries with [`machineVersions`](persistence.md#migrate-machine-versions) before restoring v5 snapshots.
1275
1300
 
1276
1301
  ### Snapshot versioning
1277
1302
 
@@ -1606,13 +1631,143 @@ It runs these transforms in order. `--transform name,name` selects a subset.
1606
1631
  | --------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
1607
1632
  | `rename-imports` | Renames `interpret` to `createActor`, `Interpreter` to `Actor`, and `fromCallback`, `fromObservable`, and `fromEventObservable` to their `create*Logic` names, and the executable action types to their executable effect names, including usages. Import aliases are kept. |
1608
1633
  | `string-targets` | Wraps bare string transition values under `on`, `after`, `onDone`, `onError`, and `always` in `{ target: '...' }` inside `createMachine` and `createStateConfig` configs. |
1609
- | `types-to-schemas` | Converts `types: {} as { ... }` to `schemas` with `types<T>()` entries. Events become a map only when they are written as an inline union literal. |
1634
+ | `types-to-schemas` | Converts `types: {} as { ... }` and the per-key form `types: { context: {} as C, events: {} as E }` to `schemas` with `types<T>()` entries. Events become a map when they are written as a union of object literals with a string-literal `type`, inline or through type aliases declared in the same file. Other event types become `events: types<E>()`, which loads but does not type-check as a v6 event map, so the entry is reported as a manual site to rewrite as a map by hand. A `types` value in any other shape, such as a variable, is reported as a manual site. |
1635
+ | `lower-actions` | Rewrites transition objects that have `actions` or `guard`, and transition arrays, as one transition function `(args, enq) => { ... }`; non-function `entry` and `exit` values as one function; and the action creators and guard combinators inside them (`assign`, `enqueueActions`, `raise`, `sendTo`, `sendParent`, `forwardTo`, `emit`, `log`, `cancel`, `stopChild`, `spawnChild`, `and`, `or`, `not`, `stateIn`) as `enq` calls, context patches and boolean expressions, following named implementations and module-level constants defined in the same file. Named actions and guards keep their v5 signatures; the generated code calls them as `enq(args.actions.name, args, params)` and `args.guards.name(args, params)`. A named `assign` action becomes a function that returns a context patch. Moves an object-literal second `createMachine` argument into the config, and rewrites `fromPromise(fn)` as `createAsyncLogic({ run: fn })` when `fn` does not read `system` or `emit`. Removes the `'xstate'` imports it leaves unused. A transition, `entry`, or `exit` value that contains anything it cannot rewrite, such as an `enqueueActions` callback that passes `enqueue` as a value or returns early, a guard or action defined in another file, or a named `assign` that `provide()` overrides, is left unchanged and reported by the transforms below. |
1610
1636
  | `report-removed-apis` | Reports, without rewriting, uses of `assign`, `raise`, `sendTo`, `sendParent`, `forwardTo`, `emit`, `log`, `cancel`, `spawnChild`, `stop`, `stopChild`, `enqueueActions`, `and`, `or`, `not`, `stateIn`, `fromPromise`, `fromTransition`, `toObserver`, `pathToStateValue`, and `SpecialTargets`, with a suggested replacement. |
1611
1637
  | `report-id-path-targets` | Reports, without rewriting, `#a.b` targets and `matches('#a.b')` or `stateIn('#a.b')` arguments that are not whole state ids of the machine config that contains them. References outside a `createMachine` or `createStateConfig` config are always reported. v6 resolves `#` targets by exact id only. |
1638
+ | `suffixed-lifecycle-events` | Rewrites `on` keys that use suffixed v5 lifecycle event types to the category type with `matches`: `'xstate.done.actor.job': { target: 'done' }` becomes `'xstate.done.actor': { matches: { actorId: 'job' }, target: 'done' }`, and the same for `xstate.error.actor.<id>`, `xstate.snapshot.<id>` and `xstate.done.state.<id>` (with `stateId`). A function transition moves under `to`. Reports `xstate.after.<delay>.<id>` keys, keys whose category type is already handled in the same `on`, transitions that are not object or function literals, and suffixed type strings elsewhere in the file, such as `event.type === 'xstate.done.actor.job'`. See [Internal lifecycle events](#internal-lifecycle-events). |
1639
+ | `report-inspection-events` | Reports, without rewriting, string literals naming v5 inspection event types: `@xstate.event`, `@xstate.snapshot`, `@xstate.action`, `@xstate.microstep`, and `@xstate.deadletter`. v6 emits only `@xstate.actor` and `@xstate.transition`, so comparisons against the old types never match; TypeScript reports them only when the handler is typed with `InspectionEvent`. Scans every file, including files that do not import `xstate`. |
1640
+ | `report-action-shapes` | Reports, without rewriting, `entry` and `exit` values that are not functions (strings, arrays, objects, identifiers, or calls such as `assign(...)`), and `actions`, `guard`, and `cond` keys on transition objects under `on`, `after`, `always`, `onDone`, `onError`, `onSnapshot`, or an object `initial`, including each element of a transition array, and the transition arrays themselves. v6 rejects these shapes: `entry` and `exit` take a single function `(args, enq) => { ... }`, and transitions queue effects on `enq` and return `{ target }` or `undefined` instead of listing `actions` and `guard`. |
1641
+ | `report-implementations` | Reports, without rewriting, a second argument to `createMachine(config, implementations)` that `lower-actions` did not move into the config, such as a variable. v6 ignores the second argument. |
1642
+
1643
+ `string-targets` wraps string elements inside transition arrays but keeps the arrays. `lower-actions` rewrites a transition array as one function when any element has `actions` or `guard`; `report-action-shapes` reports the arrays that remain. The v6 `createMachine` types reject transition arrays in `on` and `always` (for example, `on: { X: [ … ] }`); replace each with one transition function (§15, §16).
1644
+
1645
+ `lower-actions` converts the action creators and guard combinators it can resolve in the file. Rewrite the reported ones as inline functions by hand (§1, §2). The transforms read and write imports from `'xstate'` only.
1646
+
1647
+ ### Exit codes and JSON output
1648
+
1649
+ `migrate` exits with one of these codes:
1650
+
1651
+ | Code | Meaning |
1652
+ | --- | --- |
1653
+ | `0` | No manual migration is needed. |
1654
+ | `1` | Usage error, or the transformed output failed the syntax check and was not written. |
1655
+ | `2` | Manual migration is needed. At least one transform reported a site. |
1656
+
1657
+ `--json` prints one JSON report to stdout instead of the text summary. `manualSites` lists each reported site:
1658
+
1659
+ ```json
1660
+ {
1661
+ "filesChanged": 1,
1662
+ "files": [{ "filePath": "...", "changed": true, "transforms": [{ "name": "...", "changed": true, "notes": [] }] }],
1663
+ "manualSites": [{ "file": "...", "line": 12, "column": 7, "api": "assign", "hint": "...", "transform": "report-removed-apis" }]
1664
+ }
1665
+ ```
1666
+
1667
+ ### Check behavior with `baseline` and `verify`
1668
+
1669
+ <!-- commands, flags and report fields from packages/xstate-codemod/src/verify/cli.ts, run.ts, diff.ts and deltas.ts -->
1670
+
1671
+ `baseline` and `verify` check that a migrated machine behaves like the v5 original. `baseline` loads the v5 module under XState v5, generates event sequences, and records a trace of each one. `verify` loads the migrated module under XState v6, replays the same sequences, and compares the traces step by step. The codemod package includes both XState versions and resolves `'xstate'` imports in the loaded file to the matching one, so the project can have either version installed.
1672
+
1673
+ Record the baseline before `migrate` rewrites the file:
1674
+
1675
+ ```bash
1676
+ npx @xstate/codemod baseline src/checkout.ts --out .xstate-migrate/checkout.json
1677
+ npx @xstate/codemod migrate src/checkout.ts
1678
+ # fix the reported manual sites
1679
+ npx @xstate/codemod verify --baseline .xstate-migrate/checkout.json src/checkout.ts
1680
+ ```
1681
+
1682
+ When both versions exist as separate files, `verify v5-file v6-file` records the baseline and replays it in one run.
1683
+
1684
+ The loaded module must export the machine. `--export name` selects an export. Otherwise the commands use the `default` export, then the first export that is actor logic. When the module exports more than one machine and `--export` is not passed, the baseline's `notes` and the text output name the exports and the one used.
1685
+
1686
+ #### Sequences
1687
+
1688
+ Sequences come from the v5 machine's shortest paths, its simple paths, and seeded random walks. Random walks also advance a simulated clock to the next pending timer, so they cover delayed transitions. `--walks` (default `25`), `--steps` (default `12`), and `--seed` (default `1`) control the walks. `--max-sequences` (default `200`) caps the total. Simple paths grow quickly with payload cases and parallel states; when `baseline` fails with `CHILD_TIMEOUT`, pass `--no-simple-paths` to skip them. The baseline stores the setting as `simplePaths: false` in `options`.
1689
+
1690
+ While an actor runs, in both versions, `Date.now()` returns `1700000000000` plus the simulated clock's time, `performance.now()` returns the simulated clock's time, and `Math.random()` returns values from a generator seeded by `--seed` and the sequence index. A machine that stores these values in `context` records the same values under v5 and v6. `new Date()` without arguments still reads the real clock.
1691
+
1692
+ Events are sent as `{ type }` unless an events file supplies payloads. Pass `--events events.json`, an object that maps each event type to a list of payloads:
1693
+
1694
+ ```json
1695
+ { "ADD_ITEM": [{ "type": "ADD_ITEM", "sku": "A1" }, { "type": "ADD_ITEM", "sku": "B2" }] }
1696
+ ```
1697
+
1698
+ Event types without cases are listed in the report under `eventsWithoutCases`. When a guard throws because an event has no payload, the graph traversal that hit it is dropped and the event is left out of the random walks that reach it; each such case is listed under `generationErrors`. Add payload cases for those events to cover the transitions behind them. A graph traversal that hits its limit, usually because `context` changes on every step, is also listed there; random walks still cover those paths.
1699
+
1700
+ #### Trace
1701
+
1702
+ The baseline records, for the state after start and after each step:
1703
+
1704
+ - `value`, `context`, `status`, `output`, and `error`
1705
+ - `processed`: every event the root actor processed during the step, in order, including raised, delayed, and done events
1706
+ - `children`: the live child actors, by id and source
1707
+ - `sent`: events sent to other actors, with the target id and delay
1708
+ - `emitted`: events emitted with `emit`
1709
+ - `actions`: custom actions run, by type and params
1710
+
1711
+ Before comparison, events are normalized: suffixed v5 lifecycle event types such as `xstate.done.actor.<id>` become the v6 category types with their `actorId`, `stateId`, or `delay` fields, `xstate.init` is dropped, `sessionId` fields are removed, and generated child ids are replaced by `(anon)`. Functions and symbols in `context` and `output` are replaced by markers and counted under `nonSerializable`.
1712
+
1713
+ `verify` compares every field except `actions` by default. `--compare +actions` adds it. Inline v6 actions have no stable type, so `actions` differ after most migrations. When both versions end a step with `status: 'error'`, only `status` and `error` are compared for that step, because a v6 errored snapshot has no `value`. For each sequence, the first mismatching field at a step is reported as one diff. An unclassified diff ends that sequence. A classified diff is recorded and the comparison continues, so an intentional difference cannot hide a later one.
1714
+
1715
+ #### Stubs
1716
+
1717
+ Invoked and spawned actors that are referenced by name are replaced by stubs. A stub defaults to `"pending"`, a promise that never settles. Pass `--stubs stubs.json` to script outcomes and delays:
1718
+
1719
+ ```json
1720
+ {
1721
+ "actors": { "fetchUser": { "done": { "id": 1 } }, "saveOrder": { "error": "boom" }, "poll": "pending" },
1722
+ "delays": { "RETRY": 100 }
1723
+ }
1724
+ ```
1725
+
1726
+ `done` resolves with the given output, and `error` rejects with the given value. `{}` resolves with `undefined`, and `{ "done": null }` resolves with `null`. `delays` sets named delays on both versions.
1727
+
1728
+ A baseline records one outcome per actor. To cover both the `onDone` and `onError` paths, record a second baseline with a different `--stubs` file.
1729
+
1730
+ #### Input
1731
+
1732
+ Pass `--input input.json` when the machine reads `input`. The file's JSON value is passed as `createActor(logic, { input })` in both versions. `baseline` stores the input in the baseline file, and `verify --baseline` uses it unless `--input` is passed.
1733
+
1734
+ When the machine errors at start, `baseline` and `verify` print `start: machine errored at start: <error>`, and `baseline --json` reports the message as `startError`. A machine that builds `context` from `input` errors this way when no input is passed.
1735
+
1736
+ #### Results
1737
+
1738
+ `verify` exits with one of these codes:
1739
+
1740
+ | Code | Meaning |
1741
+ | --- | --- |
1742
+ | `0` | Equivalent. Every diff, if any, matched an intentional delta. |
1743
+ | `1` | Usage error. |
1744
+ | `3` | Diverged. At least one diff is `unclassified`. |
1745
+ | `4` | A module failed to load or run. `error` has the phase (`baseline` or `replay`), a code such as `EXPORT_NOT_FOUND`, and the message. |
1746
+
1747
+ The default output is a summary and the first five unclassified diffs. `--json` prints the full report. Each entry in `diffs` has the sequence id, the step, the `field`, the v5 value as `expected`, the v6 value as `actual`, a `witness` (the events and clock advances that reproduce the step), and a `classification`. A `status` diff where either side errored also has a `detail` with the error message of each errored side.
1748
+
1749
+ A diff that matches a known v6 change is classified with a rule instead of `unclassified`:
1750
+
1751
+ | Rule | Diff |
1752
+ | --- | --- |
1753
+ | `anonymous-child-id` | Child or send-target ids that differ only by a generated suffix. |
1754
+ | `error-string-shape` | Error strings with the same message after the prefix is removed. |
1755
+ | `context-reference` | `context` that is equal after removing actor-reference keys (`@actor`). |
1756
+ | `done-event-session` | Processed events that are equal after dropping `sessionId`. |
1757
+ | `final-state-output` | `output` that is undefined in v5 and set in v6, because v6 uses a top-level final state's `output` when the root has none (see [Machine output](#machine-output)). |
1758
+ | `root-send-parent` | `status` and `error` where v5 errored with `Unable to send event to actor '#_parent'` and v6 did not, because v6 makes a send to a missing parent a dead letter (see [Sending to a missing target](#sending-to-a-missing-target)). Later fields of that step, such as `value`, can still differ. |
1759
+
1760
+ Fix the v6 code for each unclassified diff. Do not edit the baseline.
1612
1761
 
1613
- `string-targets` wraps string elements inside transition arrays but keeps the arrays. The v6 `createMachine` types reject transition arrays in `on` and `always` (for example, `on: { X: [ … ] }`), and no transform reports them. Find them by hand and replace each with one transition function (§15, §16).
1762
+ #### Limits
1614
1763
 
1615
- The codemod does not convert action creators or guard combinators. Rewrite each reported `assign`, `raise`, `sendTo`, or other action creator as an inline function by hand (§1, §2). The transforms read and write imports from `'xstate'` only.
1764
+ - Actor logic written inline in `invoke.src` or `spawn` cannot be stubbed. It runs for real in both versions, and its source is listed under `unstubbed`.
1765
+ - Actions run for real. Side effects inside them, such as network requests or writes, happen during `baseline` and `verify`.
1766
+ - `actions` are not compared unless `--compare +actions` is passed. v6 records an `emit` as an action named after the emitted event, so `+actions` reports a diff on every emitting step.
1767
+ - Random walks advance the simulated clock to the next pending timer only. The v6 clock also fires timers scheduled during a jump and the v5 clock does not, so a longer jump would diverge on clock behavior alone.
1768
+ - An equivalent result covers the generated sequences only. Behavior that needs an event payload or actor outcome missing from the events and stubs files is not exercised.
1769
+ - A transition that reenters its own parallel region reports a `value` diff that is not classified. v5 reset the sibling regions and v6 leaves them in place (see "Transitions inside parallel regions"). Confirm that the v6 value is the intended one, or add explicit targets for the siblings.
1770
+ - A machine whose eventless transitions never settle makes `baseline` fail with `CHILD_CRASH` and an out-of-memory message, because v5 has no microstep limit. v6 ends the actor with `InfiniteTransitionError` instead. Fix the loop in the v5 machine before recording a baseline.
1616
1771
 
1617
1772
  ### Running v5 and v6 side by side
1618
1773
 
@@ -1629,7 +1784,7 @@ import { createMachine as createV5Machine } from 'xstate';
1629
1784
  import { createMachine } from 'xstate-v6';
1630
1785
  ```
1631
1786
 
1632
- Migrate one file at a time: run the codemod on it, finish the manual changes, then change its `'xstate'` imports to `'xstate-v6'`. When no v5 imports remain, remove the alias and install `xstate@beta` as `xstate`.
1787
+ Migrate one file at a time: run the codemod on it, finish the manual changes, then change its `'xstate'` imports to `'xstate-v6'`. `verify` resolves `'xstate'`, `'xstate/*'`, `'xstate-v6'`, and `'xstate-v6/*'` imports in the migrated file to its own XState v6, so it works before or after the import change. When no v5 imports remain, remove the alias and install `xstate@beta` as `xstate`.
1633
1788
 
1634
1789
  ---
1635
1790
 
@@ -1685,7 +1840,7 @@ Code written against a v6 alpha uses names that changed before the beta. The old
1685
1840
  | `xstate.system.cancelEffect`, `xstate.logic.cleanup` effects | `@xstate.cancelEffect`, `@xstate.cleanup` |
1686
1841
  | `xstate.logic.effect`, `xstate.log` system effects | `@xstate.effect`, `@xstate.log` |
1687
1842
  | suffixed `xstate.done.actor.<id>` etc. descriptors | category types with `matches` (see [Internal lifecycle events](#internal-lifecycle-events)) |
1688
- | `{ xstate$type: 'actorRef', id, address, src }` | `{ $actor: address }` (`SerializedActorRef`) |
1843
+ | `{ xstate$type: 'actorRef', id, address, src }` | `{ '@actor': address }` (`SerializedActorRef`; `$actor` in 6.0.0-beta.1) |
1689
1844
  | persisted timer `startedAt` | `scheduledAt` |
1690
1845
  | `snapshot.timers` record keyed by generated ids (`xstate.after.<delay>.<stateId>`, `xstate.timeout.state.<stateId>`, `xstate.timer.auto.<n>`) | list; built-in timers have no `id`, delayed sends without an `id` carry `seq` |
1691
1846
  | `scheduleTimer(source, id, delay)`, `cancelTimer(source, id)`, `{ type: 'xstate.timer', id }` | `scheduleTimer(source, timer, delay)`, `cancelTimer(source, timer)`, `{ type: 'xstate.timer', ...timer }` (`TimerKey`) |
@@ -1825,7 +1980,9 @@ In v5, an undeclared delay name resolved to `undefined`, and the delayed transit
1825
1980
 
1826
1981
  ## Migration checklist
1827
1982
 
1983
+ - [ ] Record a behavior baseline for each machine file with `xstate-codemod baseline` (§27)
1828
1984
  - [ ] Run `xstate-codemod migrate --dry`, apply it, and migrate the APIs it reports by hand (§27)
1985
+ - [ ] Run `xstate-codemod verify --baseline` on each migrated file and fix every unclassified diff (§27)
1829
1986
  - [ ] Replace every `assign({...})` with an inline function returning a shallow `{ context: {...} }` patch
1830
1987
  - [ ] Replace every `raise`, `sendTo`, `sendParent`, `forwardTo`, `emit`, `log`, `cancel`, `spawnChild`, `stopChild` with the corresponding `enq.*` call
1831
1988
  - [ ] Replace `enqueueActions(...)` with a regular inline `(args, enq) => { ... }` function
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "xstate",
3
- "version": "6.0.0-beta.0",
3
+ "version": "6.0.0-beta.2",
4
4
  "description": "Finite State Machines and Statecharts for the Modern Web.",
5
5
  "main": "dist/xstate.cjs.js",
6
6
  "bin": {