xstate 6.0.0-beta.2 → 6.0.0-beta.3

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 (65) hide show
  1. package/README.md +4 -2
  2. package/dist/{createActor-20a97166.esm.js → createActor-49ea55ba.esm.js} +17 -8
  3. package/dist/{createActor-c91c9ffe.cjs.js → createActor-73aa92ba.cjs.js} +17 -7
  4. package/dist/{createActor-0250284a.development.esm.js → createActor-a085b20b.development.esm.js} +17 -8
  5. package/dist/{createActor-c029d29f.development.cjs.js → createActor-dfb6b790.development.cjs.js} +17 -7
  6. package/dist/declarations/src/fsm.d.ts +7 -7
  7. package/dist/{effectDescriptor-47f73e7e.cjs.js → effectDescriptor-3402f63e.cjs.js} +1 -1
  8. package/dist/{effectDescriptor-e93df910.esm.js → effectDescriptor-65821048.development.esm.js} +1 -1
  9. package/dist/{effectDescriptor-5ac7dccb.development.esm.js → effectDescriptor-72c62058.esm.js} +1 -1
  10. package/dist/{effectDescriptor-3fff5c51.development.cjs.js → effectDescriptor-d0ea44ad.development.cjs.js} +1 -1
  11. package/dist/{legacySnapshot-ae6594a7.development.esm.js → legacySnapshot-11aba2d2.development.esm.js} +1 -1
  12. package/dist/{legacySnapshot-ebf0b2b8.development.cjs.js → legacySnapshot-2e2c1d3b.development.cjs.js} +1 -1
  13. package/dist/{legacySnapshot-8d595687.cjs.js → legacySnapshot-a85dfbe2.cjs.js} +1 -1
  14. package/dist/{legacySnapshot-9e06feb4.esm.js → legacySnapshot-fffe81a0.esm.js} +1 -1
  15. package/dist/xstate-actors.cjs.js +30 -5
  16. package/dist/xstate-actors.development.cjs.js +30 -5
  17. package/dist/xstate-actors.development.esm.js +31 -6
  18. package/dist/xstate-actors.esm.js +31 -6
  19. package/dist/xstate-actors.umd.min.js +1 -1
  20. package/dist/xstate-actors.umd.min.js.map +1 -1
  21. package/dist/xstate-durable.cjs.js +3 -3
  22. package/dist/xstate-durable.development.cjs.js +3 -3
  23. package/dist/xstate-durable.development.esm.js +3 -3
  24. package/dist/xstate-durable.esm.js +3 -3
  25. package/dist/xstate-durable.umd.min.js +1 -1
  26. package/dist/xstate-durable.umd.min.js.map +1 -1
  27. package/dist/xstate-fsm.cjs.js +61 -31
  28. package/dist/xstate-fsm.development.cjs.js +61 -31
  29. package/dist/xstate-fsm.development.esm.js +61 -31
  30. package/dist/xstate-fsm.esm.js +61 -31
  31. package/dist/xstate-fsm.umd.min.js +1 -1
  32. package/dist/xstate-fsm.umd.min.js.map +1 -1
  33. package/dist/xstate-graph.cjs.js +1 -1
  34. package/dist/xstate-graph.development.cjs.js +1 -1
  35. package/dist/xstate-graph.development.esm.js +2 -2
  36. package/dist/xstate-graph.esm.js +2 -2
  37. package/dist/xstate-persist.cjs.js +2 -2
  38. package/dist/xstate-persist.development.cjs.js +2 -2
  39. package/dist/xstate-persist.development.esm.js +3 -3
  40. package/dist/xstate-persist.esm.js +3 -3
  41. package/dist/xstate-persist.umd.min.js +1 -1
  42. package/dist/xstate-persist.umd.min.js.map +1 -1
  43. package/dist/xstate-validation.cjs.js +1 -1
  44. package/dist/xstate-validation.development.cjs.js +1 -1
  45. package/dist/xstate-validation.development.esm.js +1 -1
  46. package/dist/xstate-validation.esm.js +1 -1
  47. package/dist/xstate.cjs.js +26 -8
  48. package/dist/xstate.development.cjs.js +26 -8
  49. package/dist/xstate.development.esm.js +28 -10
  50. package/dist/xstate.esm.js +28 -10
  51. package/dist/xstate.umd.min.js +1 -1
  52. package/dist/xstate.umd.min.js.map +1 -1
  53. package/docs/actor-logic.md +1 -1
  54. package/docs/api-stability.json +93 -81
  55. package/docs/faq.md +1 -1
  56. package/docs/fsm.md +53 -10
  57. package/docs/index.md +2 -1
  58. package/docs/inspect-actor-systems.md +20 -3
  59. package/docs/inspection.md +5 -1
  60. package/docs/invoke.md +2 -2
  61. package/docs/migrate-inspector.md +141 -0
  62. package/docs/system-transitions.md +2 -0
  63. package/docs/transitions.md +5 -1
  64. package/docs/xstate-v5-to-v6.md +6 -4
  65. package/package.json +1 -1
package/docs/faq.md CHANGED
@@ -25,7 +25,7 @@ No typegen step is needed. Types come from `schemas`: declare `context`, `events
25
25
 
26
26
  ## What happened to `@xstate/immer` and `@xstate/inspect`?
27
27
 
28
- Both packages were removed. Instead of `@xstate/immer`, return an updated `context` patch directly from the transition function; if you want Immer drafts, call Immer's `produce` yourself inside that function. Instead of `@xstate/inspect`, use `actor.subscribe(...)` or the `inspect` option on `createActor(...)`, and use [`@statelyai/inspect`](https://github.com/statelyai/inspect) for a visual inspector. See [inspection](inspection.md) and [inspect actor systems](inspect-actor-systems.md).
28
+ Both packages were removed. Instead of `@xstate/immer`, return an updated `context` patch directly from the transition function; if you want Immer drafts, call Immer's `produce` yourself inside that function. Instead of `@xstate/inspect`, use `actor.subscribe(...)` or the `inspect` option on `createActor(...)`, and use [`@statelyai/sdk/inspect`](migrate-inspector.md) for a visual inspector. See [inspection](inspection.md) and [inspect actor systems](inspect-actor-systems.md).
29
29
 
30
30
  ## Can I run v5 and v6 side by side?
31
31
 
package/docs/fsm.md CHANGED
@@ -17,8 +17,8 @@ import { createFSM } from 'xstate/fsm';
17
17
  const machine = createFSM({
18
18
  initial: 'inactive',
19
19
  states: {
20
- inactive: { on: { toggle: 'active' } },
21
- active: { on: { toggle: 'inactive' } }
20
+ inactive: { on: { toggle: { target: 'active' } } },
21
+ active: { on: { toggle: { target: 'inactive' } } }
22
22
  }
23
23
  });
24
24
 
@@ -121,8 +121,8 @@ import { createFSM } from 'xstate/fsm';
121
121
  const machine = createFSM({
122
122
  initial: 'inactive',
123
123
  states: {
124
- inactive: { on: { toggle: 'active' } },
125
- active: { on: { toggle: 'inactive' } }
124
+ inactive: { on: { toggle: { target: 'active' } } },
125
+ active: { on: { toggle: { target: 'inactive' } } }
126
126
  }
127
127
  });
128
128
 
@@ -143,23 +143,66 @@ from `xstate` adds the runtime to your bundle.
143
143
 
144
144
  `createFSM` is pure. It does not create or run actors:
145
145
 
146
- - `machine.initialState` is the initial `{ status: 'active', value, context }`.
147
- FSM snapshots are always `'active'`. The `output` and `error` fields of the
148
- snapshot type are always `undefined` and are not set on the object.
146
+ - `machine.initialState` is the settled initial
147
+ `{ status: 'active', value, context }` snapshot. FSM snapshots are always
148
+ `'active'`. The `output` and `error` fields are always `undefined`.
149
149
  - `machine.getInitialSnapshot()` returns `machine.initialState`.
150
150
  - `machine.transition(state, event)` returns `[nextState, effects]`. `effects`
151
151
  is always empty.
152
152
  - Missing transitions return the current state unchanged.
153
- - A transition can be a state name, `{ target, context }`, or a function that
153
+ - A transition can be `{ target, context }` or a function that
154
154
  receives `{ context, event }` and returns `{ target, context }`. Return
155
155
  `undefined` from a function for no transition.
156
- - Only own entries in `states` and `on` define transitions. A state inherited from the `states` prototype has no outgoing transitions, like an undeclared state. Transition targets are not validated.
156
+ - Only own entries in `states` and `on` define transitions. A state inherited from the `states` prototype has no outgoing transitions, like an undeclared state. Target state names are not validated at runtime. Bare string transitions are rejected.
157
157
  - Context updates are shallow patches. A patch adds its own enumerable string and symbol keys even when a new value is `undefined` or matches an inherited value. If the target is unchanged, a patch whose keys already exist with `Object.is`-equal values keeps the current snapshot.
158
158
  - `types<T>()` adds types without runtime validation. Setup schema values are
159
159
  ignored by the FSM runtime; state `schemas.input` is accepted for upgrade
160
160
  compatibility but no input is consumed by this pure API.
161
161
 
162
+ ## Eventless transitions
163
+
164
+ A state can declare `always` with the same object or function form as
165
+ an event transition. Return `undefined` from a function when no transition
166
+ is enabled:
167
+
168
+ ```ts
169
+ const machine = createFSM<{ attempts: number }>({
170
+ initial: 'checking',
171
+ context: { attempts: 0 },
172
+ states: {
173
+ checking: {
174
+ always: ({ context }) =>
175
+ context.attempts < 3
176
+ ? { context: { attempts: context.attempts + 1 } }
177
+ : { target: 'ready' }
178
+ },
179
+ ready: {}
180
+ }
181
+ });
182
+
183
+ machine.initialState.value; // 'ready'
184
+ ```
185
+
186
+ The presence of `always` transitions is detected once during construction.
187
+ Define all `always` transitions before calling `createFSM`.
188
+
189
+ The machine follows enabled `always` transitions during construction and after
190
+ each handled event until none is enabled. Ignored events and callbacks that
191
+ return `undefined` do not start another eventless chain. Subscribers receive
192
+ only the settled snapshot. During construction, callbacks receive
193
+ `{ type: 'xstate.init' }`; afterward, they receive the triggering event.
194
+
195
+ There is no transition-count limit. An unconditional cycle does not terminate.
196
+ Each state supports one `always` transition. Transition arrays and a separate
197
+ guard API are not supported.
198
+
162
199
  The package intentionally has no actions, guards, effects, timers, child
163
- actors, hierarchical or parallel states, or eventless transitions. Use
200
+ actors, hierarchical or parallel states. Use
164
201
  `createMachine` and `createActor` from `xstate` when the state machine needs
165
202
  those statechart or runtime features.
203
+
204
+ ## Transition target migration
205
+
206
+ `on` and `always` require transition objects or functions returning transition objects. Replace `on: { GO: 'ready' }` with `on: { GO: { target: 'ready' } }` and `always: 'ready'` with `always: { target: 'ready' }`. `initial: 'idle'` stays a string. Invalid string transitions throw a `TypeError`.
207
+
208
+ The `@xstate/codemod` `string-targets` transform updates inline `createFSM` and `setup(...).createFSM` configurations, including aliased `createFSM` imports. Review configurations stored separately and functions returning strings manually.
package/docs/index.md CHANGED
@@ -1,4 +1,4 @@
1
- # XState 6.0.0-beta.2
1
+ # XState 6.0.0-beta.3
2
2
 
3
3
  These docs match the installed xstate package. Start with the [quick start](quick-start.md) or the [agent guide](agents.md).
4
4
  For differences from v5, read the [migration guide](xstate-v5-to-v6.md).
@@ -29,6 +29,7 @@ The [export stability manifest](api-stability.json) classifies the installed sou
29
29
  - [Test XState logic](testing.md)
30
30
  - [Model-based testing](model-based-testing.md)
31
31
  - [Inspect actor systems](inspect-actor-systems.md)
32
+ - [Migrate to SDK inspection](migrate-inspector.md)
32
33
  - [Troubleshooting](troubleshooting.md)
33
34
  - [Error codes](errors.md)
34
35
  - [Machine configuration](configuration.md)
@@ -103,13 +103,30 @@ Redact before sending. Events and context often carry addresses, card details an
103
103
 
104
104
  ## 5. Use the visual inspector
105
105
 
106
- [`@statelyai/inspect`](https://github.com/statelyai/inspect) consumes the same inspection events and draws the running system as a statechart instead of a log.
106
+ Use `createInspector` from `@statelyai/sdk/inspect` to visualize the running actor system.
107
107
 
108
108
  ```bash
109
- npm install @statelyai/inspect
109
+ npm install @statelyai/sdk
110
110
  ```
111
111
 
112
- Create its inspector and pass it to the same `inspect` option, in development only.
112
+ ```ts
113
+ import { createActor } from 'xstate';
114
+ import { createInspector } from '@statelyai/sdk/inspect';
115
+ import { checkoutMachine } from './checkoutMachine';
116
+
117
+ const inspector = createInspector();
118
+ const actor = createActor(checkoutMachine, { inspect: inspector.inspect });
119
+ actor.start();
120
+
121
+ await inspector.ready;
122
+ console.log(inspector.inspectorUrl);
123
+ ```
124
+
125
+ Open the logged URL to view the system. The SDK connects to the hosted relay without an API key and does not open a browser by default. In Node, pass `launch: 'browser'` to open the viewer automatically. In a browser, expose the URL as a link after `inspector.ready` resolves and let the user open it.
126
+
127
+ Snapshot context is omitted by default. Use `serializeSnapshot` to include selected context fields. Call `inspector.destroy()` when inspection ends; this disconnects inspection without stopping your actors. Configure inspection in development only.
128
+
129
+ If you use `@statelyai/inspect`, follow [Migrate to SDK inspection](migrate-inspector.md). The SDK has a different API and transport protocol.
113
130
 
114
131
  ## What next?
115
132
 
@@ -44,7 +44,9 @@ Both event types carry `rootId`, the session ID of the root actor, and `actorRef
44
44
  | `snapshot` | The resulting snapshot. |
45
45
  | `microsteps` | The microstep transition definitions taken. |
46
46
  | `actions` | The executed actions, as `{ type, params }`. |
47
- | `sent` | Events relayed to other actors, as `{ targetRef, targetId, event, delay, id }`. |
47
+ | `sent` | Events sent by the actor, including events sent to itself, as `{ targetRef, targetId, event, delay, id }`. |
48
+
49
+ Callback `sendBack`, event observable values, and events mapped by `enq.listen()` or `enq.subscribeTo()` enter the source actor's mailbox before reaching the parent. The source reports a transition with the parent event in `sent`. A `createEffectEventStreamLogic` actor reports its stream items the same way.
48
50
 
49
51
  Each transition retains the source of its own event, including events queued before the actor starts or while another transition runs. Events sent through the public `actor.send(...)` method have an `undefined` source. Child initialization transitions identify the parent as their source.
50
52
 
@@ -73,3 +75,5 @@ event.microsteps, event.actions, event.sent; // '@xstate.transition'
73
75
  ```
74
76
 
75
77
  Microstep transition records contain the targets selected by function transitions. Inspection and `getMicrosteps()` expose resolved copies with a `definition` reference to the shared transition; the machine's shared transition definitions remain reusable.
78
+
79
+ For visual inspection, use [`@statelyai/sdk/inspect`](inspect-actor-systems.md#5-use-the-visual-inspector). See [Migrate to SDK inspection](migrate-inspector.md) if you use the legacy `@statelyai/inspect` package.
package/docs/invoke.md CHANGED
@@ -154,9 +154,9 @@ Invoked actors belong to the state that starts them:
154
154
  - leaving the state stops it
155
155
  - re-entering the state stops the old actor and starts a new one
156
156
 
157
- A state is re-entered by an external transition back to itself, or by a transition with `reenter: true`. A transition that stays inside the invoking state does not restart the child.
157
+ A state is re-entered by an external transition back to itself, or by a targeted transition with `reenter: true`. A transition that stays inside the invoking state does not restart the child. Targetless transitions do not restart the child, even with `reenter: true`.
158
158
 
159
- Invoke on the machine root runs for the whole lifetime of the actor. Root invocations survive ordinary transitions between top-level states, and restart only on a root [transition](transitions.md) with `reenter: true`.
159
+ Invoke on the machine root runs for the whole lifetime of the actor. Root invocations survive ordinary transitions between top-level states, and restart only on a root [transition](transitions.md) with an explicit target and `reenter: true`.
160
160
 
161
161
  ```ts
162
162
  const machine = createMachine({
@@ -0,0 +1,141 @@
1
+ ---
2
+ title: Migrate to SDK inspection
3
+ description: Replace legacy Stately Inspect integrations with the Stately SDK.
4
+ ---
5
+
6
+ Use `@statelyai/sdk/inspect` for visual inspection with XState v6. The SDK accepts XState v5 and v6 inspection events. The legacy `@statelyai/inspect` package remains available for existing XState v5 integrations.
7
+
8
+ The SDK is not a drop-in replacement. Check the options and transport changes below before removing the legacy package. This guide describes `@statelyai/sdk` 0.48.0.
9
+
10
+ ## Install and connect
11
+
12
+ ```bash
13
+ npm install @statelyai/sdk
14
+ ```
15
+
16
+ Replace the browser factory with the SDK factory:
17
+
18
+ ```ts
19
+ // Before: XState v5
20
+ import { createBrowserInspector } from '@statelyai/inspect';
21
+
22
+ const inspector = createBrowserInspector();
23
+ ```
24
+
25
+ ```ts
26
+ // After: XState v6
27
+ import { createActor } from 'xstate';
28
+ import { createInspector } from '@statelyai/sdk/inspect';
29
+ import { machine } from './machine';
30
+
31
+ const inspector = createInspector();
32
+ const actor = createActor(machine, { inspect: inspector.inspect });
33
+ actor.start();
34
+
35
+ await inspector.ready;
36
+ console.log(inspector.inspectorUrl);
37
+ ```
38
+
39
+ The SDK connects to the hosted relay without an API key. `inspector.ready` resolves after relay registration and the initial-state flush. Open `inspector.inspectorUrl` after it resolves. If you supply a custom transport, the URL is `null` and your transport controls the viewer connection.
40
+
41
+ The SDK's `inspect` property is a callback. Pass it directly to `createActor` instead of calling the legacy observer's `inspect.next(...)` method. To inspect an actor that already exists, call `inspector.attach(actor)`.
42
+
43
+ The SDK has no XState peer dependency restriction, but currently depends on XState v5 internally. An application using XState v6 can have both versions installed.
44
+
45
+ ## Open the viewer
46
+
47
+ The SDK does not open a popup automatically. In a browser, expose `inspector.inspectorUrl` as a link after `inspector.ready` resolves. Let the user open it from a click or another user gesture.
48
+
49
+ In Node, request automatic browser launch:
50
+
51
+ ```ts
52
+ import { createInspector } from '@statelyai/sdk/inspect';
53
+
54
+ const inspector = createInspector({ launch: 'browser' });
55
+ ```
56
+
57
+ `launch: 'browser'` is supported from Node, not from a browser runtime.
58
+
59
+ ## Include selected context
60
+
61
+ The default snapshot serializer sends `value`, `status`, and available tags. It omits context. Use `serializeSnapshot` to include fields that your viewer needs:
62
+
63
+ ```ts
64
+ import { createInspector } from '@statelyai/sdk/inspect';
65
+
66
+ const inspector = createInspector({
67
+ serializeSnapshot: (snapshot) => {
68
+ const context = snapshot.context;
69
+ const orderId =
70
+ context !== null &&
71
+ typeof context === 'object' &&
72
+ 'orderId' in context &&
73
+ typeof context.orderId === 'string'
74
+ ? context.orderId
75
+ : undefined;
76
+
77
+ return {
78
+ value: snapshot.value,
79
+ status: snapshot.status,
80
+ tags: snapshot.tags ? Array.from(snapshot.tags) : [],
81
+ context: { orderId }
82
+ };
83
+ }
84
+ });
85
+ ```
86
+
87
+ Return JSON-serializable data. Avoid sending actor references, DOM nodes, credentials, or personal data. This serializer controls snapshots only; it does not redact event payloads or machine definitions.
88
+
89
+ ## Disconnect inspection
90
+
91
+ Replace the legacy `inspector.stop()` teardown call with:
92
+
93
+ ```ts
94
+ inspector.destroy();
95
+ ```
96
+
97
+ This closes inspection and its subscriptions. It does not stop the application actors. The SDK's `stop(actorId)` method marks a manually inspected actor as stopped; it is not a teardown method. The SDK has no equivalent of the legacy `autoStart: false` and `start()` lifecycle.
98
+
99
+ ## Migrate manual inspection
100
+
101
+ Both packages support manual actors, events, and snapshots, but actor registration has a different shape:
102
+
103
+ ```ts
104
+ // Before
105
+ inspector.actor('request', { status: 'active' }, { parentId: 'server' });
106
+ ```
107
+
108
+ ```ts
109
+ // After
110
+ inspector.actor('request', {
111
+ snapshot: { status: 'active' },
112
+ parent: 'server'
113
+ });
114
+ ```
115
+
116
+ Pass actor IDs as strings. Replace actor-reference arguments with their corresponding manual IDs. Map a legacy `definition` to the SDK's `machine` option, and provide a machine config or source string rather than assuming the old serialized definition is accepted.
117
+
118
+ `inspector.snapshot(id, snapshot, event)` takes the triggering event directly as its third argument. The legacy API takes `{ event }` there. `inspector.event(id, event, { source })` continues to accept a source, but the SDK expects the source actor's ID as a string.
119
+
120
+ ## Check unsupported options and transports
121
+
122
+ | Legacy API | SDK migration |
123
+ | --------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
124
+ | `createInspector(adapter, options)` | `createInspector(options)` has a different signature. A custom SDK `transport` must implement the SDK transport interface. |
125
+ | `createWebSocketInspector({ url })` | Use `createInspector({ url })` with a compatible SDK relay. The SDK `url` is a relay URL, not a viewer URL. |
126
+ | `createInspectorServer` from `@statelyai/inspect/server` | For local inspection, use `createNodeInspectionRelay` from `@statelyai/sdk/node-relay`. Install its `ws` peer dependency and start the relay before connecting. |
127
+ | `createBrowserReceiver`, `createWebSocketReceiver` | There are no matching SDK exports. Keep the legacy receiver until its integration supports the SDK protocol. |
128
+ | `iframe`, custom `window`, browser `send` override | There is no equivalent option on the SDK inspector factory. Keep the legacy integration until the viewer integration is migrated. |
129
+ | `filter`, `serialize`, `sanitizeEvent`, `sanitizeContext` | There are no matching SDK options. `serializeSnapshot` only controls snapshots. Keep the legacy integration if it requires event filtering or sanitization that has not been replaced. |
130
+ | `maxDeferredEvents`, `serializationDepthLimit` | There are no matching SDK options. Do not pass them and assume they take effect. |
131
+ | `createSkyInspector({ apiKey })` | The SDK has an `apiKey` option for hosted session ownership, but it uses a different session and transport protocol. Migrate the hosted integration explicitly. |
132
+
133
+ The legacy popup, WebSocket receiver, and local bridge exchange legacy inspection messages. SDK relays exchange room and producer messages. Changing the package name or server URL does not make these protocols compatible.
134
+
135
+ After migrating and verifying your integration, remove `@statelyai/inspect` if no other code uses it:
136
+
137
+ ```bash
138
+ npm uninstall @statelyai/inspect
139
+ ```
140
+
141
+ See [Inspect actor systems](inspect-actor-systems.md) for the v6 inspection workflow and [Inspection](inspection.md) for XState's event types.
@@ -79,6 +79,8 @@ such as `actor.send`, `actor.start`, `actor.on` and `actor.subscribe` throw.
79
79
  For `enq.listen`/`enq.subscribeTo`, register named pure mapper functions in
80
80
  `systemLogic.mappers`; mapped communication and attachment lifetimes belong
81
81
  to the snapshot too.
82
+ Mapped events transition through their listener or subscription actor before
83
+ reaching the owner.
82
84
  Subscription `done`/`error` mappings enter the owner's message queue before
83
85
  the native child completion/error notification, matching live actor ordering.
84
86
 
@@ -35,7 +35,7 @@ schemas: {
35
35
 
36
36
  There is no `guard` property. Conditions live inside the transition function, which returns `undefined` to reject the event. Check the condition before calling `enq`: a function that calls `enq` is taken even if it returns `undefined`. See [guards](guards.md).
37
37
 
38
- A targetless transition can update context and run effects without leaving the current state. Set `reenter: true` when a self-transition should run exit and entry behavior again.
38
+ A targetless transition can update context and run effects without leaving the current state. It does not run exit or entry behavior, even with `reenter: true`. Specify a target and set `reenter: true` when a self-transition should run exit and entry behavior again.
39
39
 
40
40
  ```ts
41
41
  on: {
@@ -101,6 +101,10 @@ Transition functions must be synchronous. A transition function that returns a p
101
101
 
102
102
  `always` runs without an external event. `after` runs after a delay. `onDone`, `onError` and `onTimeout` handle actor outcomes.
103
103
 
104
+ When an `after` timer, state timeout, or delayed `enq.raise(...)` becomes due, the delayed event is processed before `always` is reevaluated. Eventless transitions then settle before immediately raised events are processed. Timer bookkeeping does not itself run `always`: a delayed `enq.sendTo(...)` delivers its event without reevaluating eventless transitions in the sender. If the sender is also the recipient, it processes the delivered event and then reevaluates `always`.
105
+
106
+ If an `always` condition depends only on context updated through transitions, it is reevaluated during those updates and cannot become enabled silently while the machine waits. This also applies to observable values whose updates reach the machine as events and update context. The timer ordering issue concerns conditions that read outside mutable values or depend on the current event. `always` does not poll outside values for changes.
107
+
104
108
  Use targetless transitions for edits that keep a form on the same step. Use re-entering transitions to restart a timer, subscription or invoked request.
105
109
 
106
110
  Put shared transitions on a parent state. A child can set an event to `undefined` to forbid that parent transition. Wildcards such as `pointer.*` match an event family when no exact transition matches.
@@ -34,7 +34,7 @@ This guide is organized by area. Skim the **Quick reference** below and read [§
34
34
  | `types: {} as { context: ..., events: ...}` | `schemas: { context, events, ... }` (Zod / Standard Schema, or `types<T>()` for type-only) |
35
35
  | `actor.send({ type: 'INC' })` | `actor.send(...)` keeps working; new typed `actor.trigger.INC()` |
36
36
  | `@xstate/immer` | removed - return updated `context` patches directly |
37
- | `@xstate/inspect` | removed - use `inspect` option on `createActor`, `actor.subscribe`, or [`@statelyai/inspect`](https://github.com/statelyai/inspect) |
37
+ | `@xstate/inspect` | removed - use `inspect` option on `createActor`, `actor.subscribe`, or [`@statelyai/sdk/inspect`](migrate-inspector.md) |
38
38
  | `sendTo('child', ev)` with no running `child` | dead letter with reason `'missingTarget'`; the sender stays `active` (§26) |
39
39
  | `tsTypes` (typegen) | removed; declare types with `schemas` (§3) |
40
40
  | `machine.implementations` | `machine.sources` |
@@ -1146,7 +1146,7 @@ See [compact finite state machines](fsm.md) for its exact supported surface.
1146
1146
  - The executable action types are now executable effect types: `ExecutableActionObject` is `ExecutableEffect`, `ExecutableActionsFrom` is `ExecutableEffectFrom`, `SpecialExecutableAction` is `SpecialExecutableEffect`, `ExecutableSpawnAction` is `SpawnExecutableEffect`, and `ToExecutableAction` is `ToExecutableEffect`. The new v6 types and helpers use the same naming, for example `BuiltInExecutableEffect` and `isBuiltInExecutableEffect`.
1147
1147
  - `snapshot._nodes` is now `snapshot.nodes`. It lists the active state nodes.
1148
1148
  - Implicit state ids no longer use the `(machine)` placeholder. A state's implicit id is its path from the root joined by `.`, prefixed by the machine `id` when the machine has one: `shop.checkout` for `id: 'shop'`, and `checkout` without an id (v5: `(machine).checkout`). `machine.id` and the root's id are `''` for a machine without an `id`, so `#` cannot reference that root; give the machine an `id` to target it. An explicit state `id` that equals another state's id throws.
1149
- - Bare string transition values (`on: { GO: 'next' }`, `after: { 1000: 'idle' }`, `always: 'ready'`, `onDone: 'done'`) throw when the machine is created: `Transition for "GO" in state "idle" is the string "next". Use { target: 'next' }.` Write `{ target: 'next' }`; the `string-targets` codemod transform rewrites them. `initial` keeps its string form, and `xstate/fsm` still accepts string targets.
1149
+ - Bare string transition values (`on: { GO: 'next' }`, `after: { 1000: 'idle' }`, `always: 'ready'`, `onDone: 'done'`) throw when the machine is created: `Transition for "GO" in state "idle" is the string "next". Use { target: 'next' }.` Write `{ target: 'next' }`; the `string-targets` codemod transform rewrites them. `initial` keeps its string form. `xstate/fsm` also requires object targets for `on` and `always`.
1150
1150
  - `#` targets and `matches('#…')` resolve by exact id. v5 split the text after `#` at the first `.` and resolved the rest as a path below that id, so `#flow.review` meant "child `review` of the state with id `flow`". In v6, the whole string after `#` is the id: use the target state's own explicit `id`, or its full implicit id. This also applies to `@xstate.stateIn` guards in JSON. Dots in an explicit id are matched literally (`#checkout.v2` for `id: 'checkout.v2'`); v5 required `#checkout\\.v2`. An unresolvable target throws when the machine is created, and suggests the target's id when the old form resolves:
1151
1151
 
1152
1152
  ```ts
@@ -1204,6 +1204,8 @@ If you want Immer-style drafts, call Immer's `produce` yourself inside the inlin
1204
1204
 
1205
1205
  ### `@xstate/inspect` - removed
1206
1206
 
1207
+ For a visual inspector, use `createInspector` from `@statelyai/sdk/inspect`. See [Migrate to SDK inspection](migrate-inspector.md) for installation and changes from `@statelyai/inspect`.
1208
+
1207
1209
  The standalone inspector package and its `inspect()` entry point are gone. Inspection now flows through:
1208
1210
 
1209
1211
  - `actor.subscribe(observer)` - the snapshot stream
@@ -1630,7 +1632,7 @@ It runs these transforms in order. `--transform name,name` selects a subset.
1630
1632
  | Transform | Effect |
1631
1633
  | --------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
1632
1634
  | `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. |
1633
- | `string-targets` | Wraps bare string transition values under `on`, `after`, `onDone`, `onError`, and `always` in `{ target: '...' }` inside `createMachine` and `createStateConfig` configs. |
1635
+ | `string-targets` | Wraps bare string transition values under `on`, `after`, `onDone`, `onError`, and `always` in `{ target: '...' }` inside `createMachine`, `createStateConfig`, and `createFSM` configs. |
1634
1636
  | `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
1637
  | `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. |
1636
1638
  | `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. |
@@ -1994,7 +1996,7 @@ In v5, an undeclared delay name resolved to `undefined`, and the delayed transit
1994
1996
  - [ ] If you used `events` as a **union**, restructure to a **map keyed by type**
1995
1997
  - [ ] Move implementations passed as the second argument of `createMachine(config, implementations)` (deprecated in v5) into `setup({ ... })`, the `createMachine({ ... })` config, or `machine.provide({ ... })` (§4). v6's `createMachine` takes one argument and throws when it receives a second one
1996
1998
  - [ ] Audit `invoke.src` references - `src` may be a logic object, a registered name, or a resolver function
1997
- - [ ] Drop dependencies on `@xstate/immer` and `@xstate/inspect`; update inspection to `actor.subscribe`, the `inspect` option, or `@statelyai/inspect`
1999
+ - [ ] Drop dependencies on `@xstate/immer` and `@xstate/inspect`; update inspection to `actor.subscribe`, the `inspect` option, or `@statelyai/sdk/inspect`
1998
2000
  - [ ] Remove imports of `GuardArgs`, `GuardPredicate`, `Inspected*Event`, `PromiseActorLogic`, and `fromPromise` (use `createAsyncLogic`), and update the type arguments of any `SetupReturn<...>` annotation (§16)
1999
2001
  - [ ] Drain/migrate any v5 persisted snapshots - the v6 snapshot shape is not binary-compatible
2000
2002
  - [ ] Check that persisted `context` holds only JSON values; development builds warn on functions, symbols, `BigInt`, `Map`, `Set`, cycles, `NaN`, and `Infinity`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "xstate",
3
- "version": "6.0.0-beta.2",
3
+ "version": "6.0.0-beta.3",
4
4
  "description": "Finite State Machines and Statecharts for the Modern Web.",
5
5
  "main": "dist/xstate.cjs.js",
6
6
  "bin": {