xstate 6.0.0-alpha.4 → 6.0.0-alpha.41

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 (100) hide show
  1. package/README.md +19 -17
  2. package/bin/xstate.js +36 -0
  3. package/dist/StateMachine-8e73d782.cjs.js +1200 -0
  4. package/dist/StateMachine-db4e6d4b.development.cjs.js +1232 -0
  5. package/dist/StateMachine-ee89d49f.esm.js +1197 -0
  6. package/dist/StateMachine-f60abe05.development.esm.js +1229 -0
  7. package/dist/createActor-20615484.development.cjs.js +5717 -0
  8. package/dist/createActor-a3f7750f.esm.js +5578 -0
  9. package/dist/createActor-c97c04c9.development.esm.js +5620 -0
  10. package/dist/createActor-f3afd774.cjs.js +5675 -0
  11. package/dist/declarations/src/State.d.ts +17 -4
  12. package/dist/declarations/src/StateMachine.d.ts +37 -16
  13. package/dist/declarations/src/StateNode.d.ts +5 -2
  14. package/dist/declarations/src/actions.d.ts +10 -7
  15. package/dist/declarations/src/actors/callback.d.ts +2 -0
  16. package/dist/declarations/src/actors/logic.d.ts +7 -24
  17. package/dist/declarations/src/actors/observable.d.ts +3 -0
  18. package/dist/declarations/src/actors/promise.d.ts +6 -2
  19. package/dist/declarations/src/createActor.d.ts +64 -20
  20. package/dist/declarations/src/createMachine.d.ts +21 -13
  21. package/dist/declarations/src/createMachineFromConfig.d.ts +64 -30
  22. package/dist/declarations/src/durable/index.d.ts +133 -0
  23. package/dist/declarations/src/effectDescriptor.d.ts +81 -0
  24. package/dist/declarations/src/fsm.d.ts +77 -0
  25. package/dist/declarations/src/index.d.ts +15 -8
  26. package/dist/declarations/src/inspection.d.ts +11 -1
  27. package/dist/declarations/src/machineVersion.types.d.ts +38 -0
  28. package/dist/declarations/src/machineVersions.d.ts +100 -0
  29. package/dist/declarations/src/runtimeHelpers.d.ts +35 -0
  30. package/dist/declarations/src/schema.types.d.ts +5 -0
  31. package/dist/declarations/src/serialize.d.ts +9 -22
  32. package/dist/declarations/src/setup.d.ts +317 -64
  33. package/dist/declarations/src/spawn.d.ts +5 -5
  34. package/dist/declarations/src/stateUtils.d.ts +19 -7
  35. package/dist/declarations/src/system.d.ts +74 -11
  36. package/dist/declarations/src/transitionActions.d.ts +14 -2
  37. package/dist/declarations/src/types.d.ts +318 -134
  38. package/dist/declarations/src/types.v6.d.ts +306 -52
  39. package/dist/declarations/src/utils.d.ts +5 -3
  40. package/dist/declarations/src/validation/index.d.ts +37 -0
  41. package/dist/declarations/src/validation.types.d.ts +22 -0
  42. package/dist/effectDescriptor-57ae11fa.esm.js +250 -0
  43. package/dist/effectDescriptor-b7c48ceb.development.esm.js +250 -0
  44. package/dist/effectDescriptor-ba9e14f0.development.cjs.js +257 -0
  45. package/dist/effectDescriptor-e2bb61e2.cjs.js +257 -0
  46. package/dist/index-7fd92683.cjs.js +941 -0
  47. package/dist/index-a8ce72f1.esm.js +932 -0
  48. package/dist/index-b5b1b2bc.development.esm.js +932 -0
  49. package/dist/index-d42efc07.development.cjs.js +941 -0
  50. package/dist/xstate-actors.cjs.js +6 -5
  51. package/dist/xstate-actors.development.cjs.js +6 -5
  52. package/dist/xstate-actors.development.esm.js +2 -1
  53. package/dist/xstate-actors.esm.js +2 -1
  54. package/dist/xstate-actors.umd.min.js +1 -1
  55. package/dist/xstate-actors.umd.min.js.map +1 -1
  56. package/dist/xstate-durable.cjs.d.mts +2 -0
  57. package/dist/xstate-durable.cjs.d.ts +2 -0
  58. package/dist/xstate-durable.cjs.js +327 -0
  59. package/dist/xstate-durable.cjs.mjs +5 -0
  60. package/dist/xstate-durable.development.cjs.js +327 -0
  61. package/dist/xstate-durable.development.cjs.mjs +5 -0
  62. package/dist/xstate-durable.development.esm.js +321 -0
  63. package/dist/xstate-durable.esm.js +321 -0
  64. package/dist/xstate-durable.umd.min.js +2 -0
  65. package/dist/xstate-durable.umd.min.js.map +1 -0
  66. package/dist/xstate-graph.cjs.js +16 -19
  67. package/dist/xstate-graph.development.cjs.js +16 -19
  68. package/dist/xstate-graph.development.esm.js +13 -16
  69. package/dist/xstate-graph.esm.js +13 -16
  70. package/dist/xstate-graph.umd.min.js +1 -1
  71. package/dist/xstate-graph.umd.min.js.map +1 -1
  72. package/dist/xstate-validation.cjs.d.mts +2 -0
  73. package/dist/xstate-validation.cjs.d.ts +2 -0
  74. package/dist/xstate-validation.cjs.js +277 -0
  75. package/dist/xstate-validation.cjs.mjs +5 -0
  76. package/dist/xstate-validation.development.cjs.js +277 -0
  77. package/dist/xstate-validation.development.cjs.mjs +5 -0
  78. package/dist/xstate-validation.development.esm.js +271 -0
  79. package/dist/xstate-validation.esm.js +271 -0
  80. package/dist/xstate-validation.umd.min.js +2 -0
  81. package/dist/xstate-validation.umd.min.js.map +1 -0
  82. package/dist/xstate.cjs.js +1361 -412
  83. package/dist/xstate.cjs.mjs +9 -0
  84. package/dist/xstate.development.cjs.js +1361 -412
  85. package/dist/xstate.development.cjs.mjs +9 -0
  86. package/dist/xstate.development.esm.js +1332 -392
  87. package/dist/xstate.esm.js +1332 -392
  88. package/dist/xstate.umd.min.js +1 -1
  89. package/dist/xstate.umd.min.js.map +1 -1
  90. package/durable/package.json +8 -0
  91. package/package.json +39 -3
  92. package/validation/package.json +8 -0
  93. package/dist/StateMachine-445a8480.cjs.js +0 -732
  94. package/dist/StateMachine-4deb4090.development.esm.js +0 -746
  95. package/dist/StateMachine-f310bc72.esm.js +0 -729
  96. package/dist/StateMachine-f83f01f9.development.cjs.js +0 -749
  97. package/dist/index-918ab496.cjs.js +0 -4397
  98. package/dist/index-9cce3480.esm.js +0 -4342
  99. package/dist/index-ccbe7c0b.development.esm.js +0 -4381
  100. package/dist/index-ebaf6fd8.development.cjs.js +0 -4436
@@ -1,7 +1,9 @@
1
- import { m as matchesEventDescriptor, t as toArray, p as parseDelayToMilliseconds, i as isAtomicStateNode, c as createActor, a as macrostep, b as createInitEvent, d as initialMicrostep, g as getProperAncestors } from './index-9cce3480.esm.js';
2
- export { A as Actor, T as TimeoutError, h as __unsafe_getAllOwnEventDescriptors, k as checkStateIn, c as createActor, w as createAsyncLogic, r as createCallbackLogic, q as createEmptyActor, u as createEventObservableLogic, x as createListenerLogic, v as createLogic, s as createObservableLogic, z as createSubscriptionLogic, f as getStateNodes, o as isBuiltInExecutableAction, e as isMachineSnapshot, y as listenerLogic, j as matchesState, l as pathToStateValue, B as subscriptionLogic, n as toObserver } from './index-9cce3480.esm.js';
3
- import { S as StateMachine } from './StateMachine-f310bc72.esm.js';
4
- export { S as StateMachine, a as StateNode } from './StateMachine-f310bc72.esm.js';
1
+ export { T as TimeoutError, c as createAsyncLogic, a as createCallbackLogic, b as createEmptyActor, d as createEventObservableLogic, e as createLogic, f as createObservableLogic } from './index-a8ce72f1.esm.js';
2
+ import { m as matchesEventDescriptor, t as toArray$1, b as builtInActions, a as beginSpawnAllocation, X as XSTATE_INIT, f as finalizeTransitionResult, d as deriveDeferredStarts, c as createTransitionEnqueue, w as withActorScope, e as XSTATE_STOP, r as resolveActionsWithContext, g as XSTATE_TIMER, h as createSendToEffect, i as isLazyActorScope, p as parseDelayToMilliseconds, j as isAtomicStateNode, k as createActor } from './createActor-a3f7750f.esm.js';
3
+ export { A as Actor, y as __unsafe_getAllOwnEventDescriptors, B as checkStateIn, k as createActor, l as createListenerLogic, n as createSubscriptionLogic, G as deliverEvent, E as executeEffects, q as getInitialSnapshot, u as getNextSnapshot, x as getStateNodes, F as isBuiltInExecutableAction, v as isMachineSnapshot, o as listenerLogic, z as matchesState, C as pathToStateValue, H as runStep, I as stopActor, s as subscriptionLogic, J as terminateActor, D as toObserver } from './createActor-a3f7750f.esm.js';
4
+ import { S as StateMachine } from './StateMachine-ee89d49f.esm.js';
5
+ export { S as StateMachine, a as StateNode } from './StateMachine-ee89d49f.esm.js';
6
+ export { c as getEffectDescriptor, a as getInitialMicrosteps, g as getMicrosteps, b as getNextTransitions, i as initialTransition, t as transition } from './effectDescriptor-57ae11fa.esm.js';
5
7
 
6
8
  /**
7
9
  * Asserts that the given event object is of the specified type or types. Throws
@@ -27,7 +29,7 @@ export { S as StateMachine, a as StateNode } from './StateMachine-f310bc72.esm.j
27
29
  * ```
28
30
  */
29
31
  function assertEvent(event, type) {
30
- const types = toArray(type);
32
+ const types = toArray$1(type);
31
33
  const matches = types.some(descriptor => matchesEventDescriptor(event.type, descriptor));
32
34
  if (!matches) {
33
35
  const typesText = types.length === 1 ? `type matching "${types[0]}"` : `one of types matching "${types.join('", "')}"`;
@@ -75,7 +77,7 @@ function assertEvent(event, type) {
75
77
  *
76
78
  * @param config The state machine configuration.
77
79
  * @param options DEPRECATED: use `setup({ ... })` or `machine.provide({ ... })`
78
- * to provide machine implementations instead.
80
+ * to provide machine sources instead.
79
81
  */
80
82
  // Overload 1: With schemas.context — context type inferred from schema
81
83
 
@@ -89,11 +91,392 @@ function createStateConfig(config) {
89
91
  return config;
90
92
  }
91
93
 
94
+ const emptySources = {
95
+ actions: {},
96
+ actors: {},
97
+ guards: {},
98
+ delays: {}
99
+ };
100
+ const emptyExecutableActions = [];
101
+ const emptyRawActions = [];
102
+ const builtInActionSet = new Set(Object.values(builtInActions));
103
+ function toArray(value) {
104
+ return value === undefined ? [] : Array.isArray(value) ? value : [value];
105
+ }
106
+ function resolveContext(context, input) {
107
+ return typeof context === 'function' ? context({
108
+ input
109
+ }) : context ?? {};
110
+ }
111
+ function resolveTransitionContext(context, args) {
112
+ return typeof context === 'function' ? context(args) : context;
113
+ }
114
+ function resolveInput(input, context, event) {
115
+ return typeof input === 'function' ? input({
116
+ context,
117
+ event
118
+ }) : input;
119
+ }
120
+ function mergeContextPatch(context, patch) {
121
+ for (const key of Object.keys(patch)) {
122
+ if (!Object.prototype.hasOwnProperty.call(context, key) || !Object.is(context[key], patch[key])) {
123
+ return {
124
+ ...context,
125
+ ...patch
126
+ };
127
+ }
128
+ }
129
+ return context;
130
+ }
131
+ function createSnapshot(value, context, input, machine, stateInput) {
132
+ return {
133
+ status: 'active',
134
+ output: undefined,
135
+ error: undefined,
136
+ value,
137
+ context,
138
+ input,
139
+ children: {},
140
+ timers: {},
141
+ _nextTimerId: 0,
142
+ _stateInput: stateInput,
143
+ machine
144
+ };
145
+ }
146
+ function cloneSnapshot(snapshot, value, context, stateInput) {
147
+ return {
148
+ status: snapshot.status,
149
+ output: snapshot.output,
150
+ error: snapshot.error,
151
+ value,
152
+ context,
153
+ input: snapshot.input,
154
+ children: snapshot.children,
155
+ timers: snapshot.timers,
156
+ _nextTimerId: snapshot._nextTimerId,
157
+ // Carried across state changes: dropping these would let a later spawn
158
+ // reuse the id of a still-running child.
159
+ _nextActorIds: snapshot._nextActorIds,
160
+ _stateInput: stateInput,
161
+ machine: snapshot.machine
162
+ };
163
+ }
164
+ function stopSnapshot(snapshot) {
165
+ return {
166
+ status: 'stopped',
167
+ output: undefined,
168
+ error: undefined,
169
+ value: snapshot.value,
170
+ context: snapshot.context,
171
+ input: undefined,
172
+ children: snapshot.children,
173
+ timers: snapshot.timers,
174
+ _nextTimerId: snapshot._nextTimerId,
175
+ _nextActorIds: snapshot._nextActorIds,
176
+ _stateInput: snapshot._stateInput,
177
+ machine: snapshot.machine
178
+ };
179
+ }
180
+ function assertNoStringTransitions(config) {
181
+ for (const [stateKey, stateConfig] of Object.entries(config.states)) {
182
+ for (const [eventType, transitionConfig] of Object.entries(stateConfig.on ?? {})) {
183
+ for (const transition of toArray(transitionConfig)) {
184
+ if (typeof transition === 'string') {
185
+ const target = transition;
186
+ throw new Error(`Invalid transition for "${stateKey}.${eventType}": use { target: "${target}" } instead of a string target.`);
187
+ }
188
+ }
189
+ }
190
+ }
191
+ }
192
+ function resolveSimpleEnqueuedActions(rawActions) {
193
+ const executableActions = [];
194
+ for (const action of rawActions) {
195
+ if (!action || typeof action !== 'object' || !('action' in action) || typeof action.action !== 'function' || builtInActionSet.has(action.action) || '_special' in action.action) {
196
+ return undefined;
197
+ }
198
+ executableActions.push({
199
+ kind: 'action',
200
+ type: action.action.name || '(anonymous)',
201
+ params: undefined,
202
+ args: action.args,
203
+ action: action.action,
204
+ exec() {
205
+ return this.action?.(...this.args);
206
+ }
207
+ });
208
+ }
209
+ return executableActions;
210
+ }
211
+ function createFSM(config) {
212
+ const machine = {
213
+ id: config.id ?? '(fsm)',
214
+ sources: emptySources
215
+ };
216
+ assertNoStringTransitions(config);
217
+ const runActions = (snapshot, event, actorScope, rawActions) => {
218
+ if (!rawActions?.length) {
219
+ return [snapshot, emptyExecutableActions];
220
+ }
221
+ const simpleExecutableActions = resolveSimpleEnqueuedActions(rawActions);
222
+ if (simpleExecutableActions) {
223
+ return [snapshot, simpleExecutableActions];
224
+ }
225
+ return resolveActionsWithContext(snapshot, event, actorScope, rawActions);
226
+ };
227
+ const runStateActions = (snapshot, event, actorScope, actionsConfig, stateInput, internalQueue) => {
228
+ if (!actionsConfig) {
229
+ return [snapshot, emptyExecutableActions];
230
+ }
231
+ const actions = [];
232
+ const enq = createTransitionEnqueue(actorScope, actions, internalQueue, true);
233
+ let context;
234
+ const actionCount = Array.isArray(actionsConfig) ? actionsConfig.length : 1;
235
+ for (let i = 0; i < actionCount; i++) {
236
+ const action = Array.isArray(actionsConfig) ? actionsConfig[i] : actionsConfig;
237
+ const result = action(withActorScope({
238
+ context: context ?? snapshot.context,
239
+ event: event,
240
+ input: stateInput,
241
+ value: snapshot.value,
242
+ children: snapshot.children
243
+ }, actorScope), enq);
244
+ if (result?.context !== undefined) {
245
+ const currentContext = context ?? snapshot.context;
246
+ const nextContext = mergeContextPatch(currentContext, result.context);
247
+ if (nextContext !== currentContext) {
248
+ context = nextContext;
249
+ }
250
+ }
251
+ }
252
+ const nextSnapshot = context !== undefined ? cloneSnapshot(snapshot, snapshot.value, context, snapshot._stateInput) : snapshot;
253
+ return runActions(nextSnapshot, event, actorScope, actions);
254
+ };
255
+ const selectTransition = (snapshot, event, actorScope, internalQueue) => {
256
+ const state = config.states[snapshot.value];
257
+ const transitionsConfig = state?.on?.[event.type];
258
+ if (!transitionsConfig) {
259
+ return undefined;
260
+ }
261
+ const transitionCount = Array.isArray(transitionsConfig) ? transitionsConfig.length : 1;
262
+ for (let i = 0; i < transitionCount; i++) {
263
+ const transition = Array.isArray(transitionsConfig) ? transitionsConfig[i] : transitionsConfig;
264
+ const args = withActorScope({
265
+ context: snapshot.context,
266
+ event,
267
+ input: snapshot.input,
268
+ value: snapshot.value,
269
+ children: snapshot.children
270
+ }, actorScope);
271
+ if (typeof transition === 'function') {
272
+ const actions = [];
273
+ const enq = createTransitionEnqueue(actorScope, actions, internalQueue, true);
274
+ const result = transition(args, enq);
275
+ if (!result) {
276
+ if (actions.length) {
277
+ return {
278
+ actions
279
+ };
280
+ }
281
+ continue;
282
+ }
283
+ return {
284
+ target: result.target,
285
+ context: result.context,
286
+ input: result.input,
287
+ actions
288
+ };
289
+ }
290
+ if ('guard' in transition && transition.guard && !transition.guard(args)) {
291
+ continue;
292
+ }
293
+ return {
294
+ target: transition.target,
295
+ context: resolveTransitionContext(transition.context, args),
296
+ input: transition.input,
297
+ actions: 'actions' in transition && transition.actions ? toArray(transition.actions) : emptyRawActions
298
+ };
299
+ }
300
+ return undefined;
301
+ };
302
+ const transitionCore = (snapshot, event, actorScope) => {
303
+ if (snapshot.status !== 'active') {
304
+ return [snapshot, []];
305
+ }
306
+ if (event.type === XSTATE_STOP) {
307
+ const actions = [];
308
+ const enqueue = createTransitionEnqueue(actorScope, actions, []);
309
+ for (const id of Object.keys(snapshot.timers)) {
310
+ enqueue.cancel(id);
311
+ }
312
+ const [withoutTimers, cancelEffects] = resolveActionsWithContext(snapshot, event, actorScope, actions);
313
+ return [stopSnapshot(withoutTimers), cancelEffects];
314
+ }
315
+ if (event.type === XSTATE_TIMER) {
316
+ const timer = snapshot.timers[event.id];
317
+ if (!timer) {
318
+ return [snapshot, emptyExecutableActions];
319
+ }
320
+ const timers = {
321
+ ...snapshot.timers
322
+ };
323
+ delete timers[timer.id];
324
+ const nextSnapshot = {
325
+ ...snapshot,
326
+ timers
327
+ };
328
+ if (timer.type === '@xstate.raise') {
329
+ return transitionCore(nextSnapshot, timer.event, actorScope);
330
+ }
331
+ const target = timer.target === 'self' ? actorScope.self : timer.target;
332
+ return [nextSnapshot, [createSendToEffect(actorScope, target, timer.event)]];
333
+ }
334
+ const stateConfig = config.states[snapshot.value];
335
+ const directTransition = stateConfig?.on?.[event.type];
336
+ if (!directTransition) {
337
+ return [snapshot, emptyExecutableActions];
338
+ }
339
+ if (!Array.isArray(directTransition) && typeof directTransition !== 'function' && !('guard' in directTransition) && !('actions' in directTransition && directTransition.actions) && typeof directTransition.input !== 'function') {
340
+ const target = directTransition.target ?? snapshot.value;
341
+ const stateChanged = target !== snapshot.value;
342
+ if (stateChanged && (stateConfig.exit || config.states[target]?.entry)) ; else {
343
+ const hasContext = directTransition.context !== undefined;
344
+ const hasInput = directTransition.input !== undefined;
345
+ const resolvedContext = !isLazyActorScope(actorScope) ? resolveTransitionContext(directTransition.context, {
346
+ context: snapshot.context,
347
+ event,
348
+ input: snapshot.input,
349
+ value: snapshot.value,
350
+ self: actorScope.self,
351
+ system: actorScope.system,
352
+ parent: actorScope.self._parent,
353
+ children: snapshot.children
354
+ }) : typeof directTransition.context === 'function' ? directTransition.context(withActorScope({
355
+ context: snapshot.context,
356
+ event,
357
+ input: snapshot.input,
358
+ value: snapshot.value,
359
+ children: snapshot.children
360
+ }, actorScope)) : directTransition.context;
361
+ const context = hasContext && resolvedContext ? mergeContextPatch(snapshot.context, resolvedContext) : snapshot.context;
362
+ if (!stateChanged && context === snapshot.context && !hasInput && snapshot._stateInput === undefined) {
363
+ return [snapshot, emptyExecutableActions];
364
+ }
365
+ return [cloneSnapshot(snapshot, target, context, hasInput ? resolveInput(directTransition.input, context, event) : undefined), emptyExecutableActions];
366
+ }
367
+ }
368
+ if (typeof directTransition === 'function' && directTransition.length < 2 && !stateConfig?.exit) {
369
+ const result = directTransition(withActorScope({
370
+ context: snapshot.context,
371
+ event,
372
+ input: snapshot.input,
373
+ value: snapshot.value,
374
+ children: snapshot.children
375
+ }, actorScope), undefined);
376
+ if (result) {
377
+ const target = result.target ?? snapshot.value;
378
+ if (!config.states[target]?.entry) {
379
+ const hasContext = result.context !== undefined;
380
+ const hasInput = result.input !== undefined;
381
+ const context = hasContext && result.context ? mergeContextPatch(snapshot.context, result.context) : snapshot.context;
382
+ if (target === snapshot.value && context === snapshot.context && !hasInput && snapshot._stateInput === undefined) {
383
+ return [snapshot, emptyExecutableActions];
384
+ }
385
+ return [cloneSnapshot(snapshot, target, context, hasInput ? resolveInput(result.input, context, event) : undefined), emptyExecutableActions];
386
+ }
387
+ }
388
+ }
389
+ let nextSnapshot = snapshot;
390
+ const executableActions = [];
391
+ const internalQueue = [event];
392
+ let iterations = 0;
393
+ while (internalQueue.length) {
394
+ if (++iterations > 1000) {
395
+ throw new Error('FSM microstep count exceeded 1000');
396
+ }
397
+ const nextEvent = internalQueue.shift();
398
+ const selected = selectTransition(nextSnapshot, nextEvent, actorScope, internalQueue);
399
+ if (!selected) {
400
+ continue;
401
+ }
402
+ const nextValue = selected.target ?? nextSnapshot.value;
403
+ const stateChanged = nextValue !== nextSnapshot.value;
404
+ if (stateChanged) {
405
+ const [exited, exitActions] = runStateActions(nextSnapshot, nextEvent, actorScope, config.states[nextSnapshot.value]?.exit, nextSnapshot._stateInput, internalQueue);
406
+ nextSnapshot = exited;
407
+ executableActions.push(...exitActions);
408
+ }
409
+ let context = nextSnapshot.context;
410
+ if (selected.context !== undefined) {
411
+ context = mergeContextPatch(context, selected.context);
412
+ }
413
+ const hasInput = selected.input !== undefined;
414
+ const stateInput = hasInput ? resolveInput(selected.input, context, nextEvent) : undefined;
415
+ if (stateChanged || context !== nextSnapshot.context || hasInput || nextSnapshot._stateInput !== undefined) {
416
+ nextSnapshot = cloneSnapshot(nextSnapshot, nextValue, context, stateInput);
417
+ }
418
+ const [afterTransition, transitionActions] = runActions(nextSnapshot, nextEvent, actorScope, selected.actions);
419
+ nextSnapshot = afterTransition;
420
+ executableActions.push(...transitionActions);
421
+ if (stateChanged) {
422
+ const [entered, entryActions] = runStateActions(nextSnapshot, nextEvent, actorScope, config.states[nextValue]?.entry, stateInput, internalQueue);
423
+ nextSnapshot = entered;
424
+ executableActions.push(...entryActions);
425
+ }
426
+ }
427
+ return [nextSnapshot, executableActions];
428
+ };
429
+ const transition = (...args) => {
430
+ beginSpawnAllocation(args[2]);
431
+ const [nextSnapshot, effects] = transitionCore(...args);
432
+ return finalizeTransitionResult(args[2], args[0], [nextSnapshot, [...effects, ...deriveDeferredStarts(effects)]]);
433
+ };
434
+ const logic = {
435
+ id: config.id,
436
+ config,
437
+ transition,
438
+ initialTransition: (input, actorScope) => {
439
+ beginSpawnAllocation(actorScope);
440
+ const context = resolveContext(config.context, input);
441
+ const snapshot = createSnapshot(config.initial, context, input, machine);
442
+ const internalQueue = [];
443
+ let [nextSnapshot, actions] = runStateActions(snapshot, {
444
+ type: XSTATE_INIT
445
+ }, actorScope, config.states[config.initial]?.entry, undefined, internalQueue);
446
+ if (!actions.length) {
447
+ actions = [];
448
+ }
449
+ while (internalQueue.length) {
450
+ const [raisedSnapshot, raisedActions] = transitionCore(nextSnapshot, internalQueue.shift(), actorScope);
451
+ nextSnapshot = raisedSnapshot;
452
+ actions.push(...raisedActions);
453
+ }
454
+ return finalizeTransitionResult(actorScope, undefined, [nextSnapshot, [...actions, ...deriveDeferredStarts(actions)]]);
455
+ },
456
+ getInitialSnapshot: (actorScope, input) => logic.initialTransition(input, actorScope)[0],
457
+ getPersistedSnapshot: ({
458
+ machine: _,
459
+ ...snapshot
460
+ }) => snapshot,
461
+ restoreSnapshot: snapshot => ({
462
+ ...snapshot,
463
+ machine: machine
464
+ })
465
+ };
466
+ return logic;
467
+ }
468
+
92
469
  function delayToMs(delay) {
93
470
  const parsedDelay = parseDelayToMilliseconds(delay);
94
471
  if (parsedDelay !== undefined) return parsedDelay;
95
472
  return typeof delay === 'string' ? parseFloat(delay) || 0 : delay;
96
473
  }
474
+ function normalizeScxmlEventType(eventType) {
475
+ return /^error(\.|$)/.test(eventType) ? `xstate.${eventType}` : eventType;
476
+ }
477
+ function toScxmlEventName(eventType) {
478
+ return eventType.startsWith('xstate.error.') ? eventType.slice('xstate.'.length) : eventType;
479
+ }
97
480
 
98
481
  /**
99
482
  * Isolated executable-content block. Errors in nested actions stop this block
@@ -101,48 +484,23 @@ function delayToMs(delay) {
101
484
  * separate <onentry>/<onexit> blocks: each is its own block per spec.
102
485
  */
103
486
 
104
- function isUnserializableMarker(value) {
105
- return !!value && typeof value === 'object' && '$unserializable' in value && typeof value.$unserializable === 'string';
487
+ function isExpression(value) {
488
+ return !!value && typeof value === 'object' && typeof value['@expr'] === 'string';
106
489
  }
107
- function assertNoUnresolvedMarkers(value, path = '$') {
108
- if (isUnserializableMarker(value)) {
109
- if (value.$unserializable === 'actor' && path.endsWith('.src')) {
110
- return;
111
- }
112
- throw new Error(`Unresolved ${value.$unserializable} at ${path}`);
113
- }
114
- if (!value || typeof value !== 'object') {
115
- return;
116
- }
117
- if (Array.isArray(value)) {
118
- value.forEach((item, index) => assertNoUnresolvedMarkers(item, `${path}[${index}]`));
119
- return;
120
- }
121
- for (const key of Object.keys(value)) {
122
- const child = value[key];
123
- const isImplementationMarker = path === '$' && (key === 'actions' || key === 'guards' || key === 'actors' || key === 'delays' || key === 'schemas');
124
- if (isImplementationMarker) {
125
- continue;
126
- }
127
- assertNoUnresolvedMarkers(child, `${path}.${key}`);
128
- }
490
+ function isCode(value) {
491
+ return !!value && typeof value === 'object' && typeof value['@code'] === 'string';
129
492
  }
130
- function assertRootImplementationMarkers(json, implementations) {
131
- for (const kind of ['actions', 'guards', 'actors', 'delays']) {
132
- const map = json[kind];
133
- if (!map) {
134
- continue;
135
- }
136
- for (const key of Object.keys(map)) {
137
- const value = map[key];
138
- if (!isUnserializableMarker(value)) {
139
- continue;
140
- }
141
- if (!implementations[kind]?.[key]) {
142
- throw new Error(`Missing ${kind}.${key}`);
143
- }
144
- }
145
- }
493
+ function isResolvable(value) {
494
+ return isExpression(value) || isCode(value);
495
+ }
496
+ function isBuiltInActionType(type) {
497
+ return type.startsWith('@xstate.');
498
+ }
499
+ function isScxmlActionType(type) {
500
+ return type.startsWith('scxml.');
501
+ }
502
+ function toPath(parent, key) {
503
+ return typeof key === 'number' ? `${parent}[${key}]` : `${parent}.${key}`;
146
504
  }
147
505
  function extractSerializableDelays(delays) {
148
506
  const result = {};
@@ -153,22 +511,267 @@ function extractSerializableDelays(delays) {
153
511
  const value = delays[key];
154
512
  if (typeof value === 'number') {
155
513
  result[key] = value;
514
+ } else if (value && typeof value === 'object' && typeof value.duration === 'number') {
515
+ result[key] = value.duration;
156
516
  }
157
517
  }
158
518
  return result;
159
519
  }
160
- function mergeImplementations(json, implementations) {
520
+ function mergeSources(json, sources) {
161
521
  return {
162
- actions: implementations.actions ?? {},
163
- guards: implementations.guards ?? {},
164
- actors: implementations.actors ?? {},
522
+ actions: sources.actions ?? {},
523
+ guards: sources.guards ?? {},
524
+ actors: sources.actors ?? {},
165
525
  delays: {
166
526
  ...extractSerializableDelays(json.delays),
167
- ...(implementations.delays ?? {})
527
+ ...(sources.delays ?? {})
528
+ },
529
+ evaluators: sources.evaluators ?? {}
530
+ };
531
+ }
532
+ function createExpressionResolver(expressionLanguage, sources) {
533
+ function getEvaluator(value, path) {
534
+ const lang = value['@lang'] ?? expressionLanguage;
535
+ if (!lang) {
536
+ throw new Error(`Missing @exprLang for expression at ${path}`);
537
+ }
538
+ const evaluator = sources.evaluators[lang];
539
+ if (!evaluator) {
540
+ throw new Error(`Missing evaluator for @lang '${lang}' at ${path}`);
168
541
  }
542
+ return evaluator;
543
+ }
544
+ function evaluateResolvable(value, slot, scope, path) {
545
+ const kind = isExpression(value) ? 'expr' : 'code';
546
+ const source = isExpression(value) ? value['@expr'] : value['@code'];
547
+ return getEvaluator(value, path)({
548
+ source,
549
+ kind,
550
+ slot,
551
+ scope,
552
+ path
553
+ });
554
+ }
555
+ function resolveValue(value, slot, scope, path) {
556
+ if (isResolvable(value)) {
557
+ return evaluateResolvable(value, slot, scope, path);
558
+ }
559
+ if (Array.isArray(value)) {
560
+ return value.map((item, index) => resolveValue(item, slot, scope, toPath(path, index)));
561
+ }
562
+ if (!value || typeof value !== 'object') {
563
+ return value;
564
+ }
565
+ const result = {};
566
+ for (const key of Object.keys(value)) {
567
+ result[key] = resolveValue(value[key], slot, scope, toPath(path, key));
568
+ }
569
+ return result;
570
+ }
571
+ function makeScope(x, extra) {
572
+ return {
573
+ context: x.context,
574
+ event: x.event,
575
+ input: x.input,
576
+ self: x.self,
577
+ children: x.children,
578
+ params: x.params,
579
+ ...extra
580
+ };
581
+ }
582
+ function getDurationConfig(value, path) {
583
+ if (!isResolvable(value)) {
584
+ return value;
585
+ }
586
+ return args => delayToMs(resolveValue(value, 'delay', makeScope(args), path));
587
+ }
588
+ function assertResolvable(value, path) {
589
+ if (isResolvable(value)) {
590
+ getEvaluator(value, path);
591
+ return;
592
+ }
593
+ if (Array.isArray(value)) {
594
+ value.forEach((item, index) => assertResolvable(item, toPath(path, index)));
595
+ return;
596
+ }
597
+ if (!value || typeof value !== 'object') {
598
+ return;
599
+ }
600
+ for (const key of Object.keys(value)) {
601
+ assertResolvable(value[key], toPath(path, key));
602
+ }
603
+ }
604
+ return {
605
+ evaluateResolvable,
606
+ resolveValue,
607
+ makeScope,
608
+ getDurationConfig,
609
+ assertResolvable
169
610
  };
170
611
  }
171
- let _scxmlSessionId = 'session_' + Math.random().toString(36).slice(2);
612
+ function toActionArray(actions) {
613
+ return actions === undefined ? [] : Array.isArray(actions) ? actions : [actions];
614
+ }
615
+ function validateChoiceConfig(choice, path) {
616
+ if (!Array.isArray(choice)) {
617
+ return;
618
+ }
619
+ choice.forEach((branch, index) => {
620
+ if (branch.when === undefined && index !== choice.length - 1) {
621
+ throw new Error(`Choice fallback branch at ${path}[${index}] must be last.`);
622
+ }
623
+ });
624
+ }
625
+ function assertMachineJSON(json, resolvedSources, expressionResolver) {
626
+ const {
627
+ assertResolvable
628
+ } = expressionResolver;
629
+ function assertCondition(condition, path) {
630
+ if (!condition) {
631
+ return;
632
+ }
633
+ if (isResolvable(condition)) {
634
+ assertResolvable(condition, path);
635
+ return;
636
+ }
637
+ assertResolvable(condition.params, `${path}.params`);
638
+ if (json.guards?.[condition.type]) {
639
+ assertCondition(json.guards[condition.type].when, `$.guards.${condition.type}.when`);
640
+ return;
641
+ }
642
+ if (!resolvedSources.guards[condition.type] && !['scxml.cond', 'xstate.stateIn', 'xstate.not'].includes(condition.type)) {
643
+ throw new Error(`Missing guard source "${condition.type}"`);
644
+ }
645
+ }
646
+ function assertAction(action, path, stack = []) {
647
+ if (isResolvable(action)) {
648
+ assertResolvable(action, path);
649
+ return;
650
+ }
651
+ if (!action || typeof action.type !== 'string') {
652
+ throw new Error(`Invalid action at ${path}`);
653
+ }
654
+ assertResolvable(action.params, `${path}.params`);
655
+ if (isBuiltInActionType(action.type) || isScxmlActionType(action.type)) {
656
+ assertResolvable(action, path);
657
+ return;
658
+ }
659
+ const definition = json.actions?.[action.type];
660
+ if (definition) {
661
+ if (stack.includes(action.type)) {
662
+ throw new Error(`Circular action reference: ${stack.concat(action.type).join(' -> ')}`);
663
+ }
664
+ const definitions = Array.isArray(definition) ? definition : [definition];
665
+ definitions.forEach((item, index) => assertAction(item, `${path}.actions.${action.type}${definitions.length > 1 ? `[${index}]` : ''}`, stack.concat(action.type)));
666
+ return;
667
+ }
668
+ if (!resolvedSources.actions[action.type]) {
669
+ throw new Error(`Missing action source "${action.type}"`);
670
+ }
671
+ }
672
+ function assertActions(actions, path) {
673
+ toActionArray(actions).forEach((action, index) => assertAction(action, `${path}[${index}]`));
674
+ }
675
+ function assertTransition(transition, path) {
676
+ const transitions = Array.isArray(transition) ? transition : transition ? [transition] : [];
677
+ transitions.forEach((t, index) => {
678
+ const transitionPath = Array.isArray(transition) ? `${path}[${index}]` : path;
679
+ if (isResolvable(t)) {
680
+ assertResolvable(t, transitionPath);
681
+ return;
682
+ }
683
+ assertCondition(t.guard, `${transitionPath}.guard`);
684
+ assertActions(t.actions, `${transitionPath}.actions`);
685
+ assertResolvable(t.context, `${transitionPath}.context`);
686
+ assertResolvable(t.input, `${transitionPath}.input`);
687
+ });
688
+ }
689
+ function assertStateNode(node, path) {
690
+ if ((node.type === 'history' || node.history !== undefined) && !(typeof node.target === 'string' && node.target.trim().length > 0 || Array.isArray(node.target) && node.target.length > 0 && node.target.every(target => target.trim().length > 0))) {
691
+ throw new Error(`History state at ${path} must declare a non-empty target.`);
692
+ }
693
+ assertResolvable(node.context, `${path}.context`);
694
+ assertResolvable(node.input, `${path}.input`);
695
+ assertResolvable(node.output, `${path}.output`);
696
+ assertResolvable(node.timeout, `${path}.timeout`);
697
+ assertActions(node.entry, `${path}.entry`);
698
+ assertActions(node.exit, `${path}.exit`);
699
+ if (node.type === 'choice') {
700
+ if (!node.choice) {
701
+ throw new Error(`Choice state at ${path} must declare choice.`);
702
+ }
703
+ if (Array.isArray(node.choice)) {
704
+ validateChoiceConfig(node.choice, `${path}.choice`);
705
+ node.choice.forEach((branch, index) => {
706
+ assertCondition(branch.when, `${path}.choice[${index}].when`);
707
+ assertResolvable(branch.context, `${path}.choice[${index}].context`);
708
+ assertResolvable(branch.input, `${path}.choice[${index}].input`);
709
+ });
710
+ } else {
711
+ assertResolvable(node.choice, `${path}.choice`);
712
+ }
713
+ }
714
+ if (isResolvable(node.route)) {
715
+ assertResolvable(node.route, `${path}.route`);
716
+ }
717
+ if (node.invoke) {
718
+ const invokes = Array.isArray(node.invoke) ? node.invoke : [node.invoke];
719
+ invokes.forEach((invoke, index) => {
720
+ const invokePath = `${path}.invoke${Array.isArray(node.invoke) ? `[${index}]` : ''}`;
721
+ const extInv = invoke;
722
+ if (!extInv._nestedMachineJSON && !resolvedSources.actors[invoke.src]) {
723
+ throw new Error(`Missing actor source "${invoke.src}"`);
724
+ }
725
+ assertResolvable(invoke.input, `${invokePath}.input`);
726
+ assertResolvable(invoke.timeout, `${invokePath}.timeout`);
727
+ assertTransition(invoke.onDone, `${invokePath}.onDone`);
728
+ assertTransition(invoke.onError, `${invokePath}.onError`);
729
+ assertTransition(invoke.onSnapshot, `${invokePath}.onSnapshot`);
730
+ assertTransition(invoke.onTimeout, `${invokePath}.onTimeout`);
731
+ });
732
+ }
733
+ if (node.on) {
734
+ for (const descriptor of Object.keys(node.on)) {
735
+ assertTransition(node.on[descriptor], `${path}.on.${descriptor}`);
736
+ }
737
+ }
738
+ if (node.after) {
739
+ for (const delay of Object.keys(node.after)) {
740
+ if (Number.isNaN(Number(delay)) && parseDelayToMilliseconds(delay) === undefined && !json.delays?.[delay] && !resolvedSources.delays[delay]) {
741
+ throw new Error(`Missing delay source "${delay}"`);
742
+ }
743
+ assertTransition(node.after[delay], `${path}.after.${delay}`);
744
+ }
745
+ }
746
+ assertTransition(node.always, `${path}.always`);
747
+ assertTransition(node.onError, `${path}.onError`);
748
+ assertTransition(node.onTimeout, `${path}.onTimeout`);
749
+ if (node.states) {
750
+ for (const key of Object.keys(node.states)) {
751
+ assertStateNode(node.states[key], `${path}.states.${key}`);
752
+ }
753
+ }
754
+ }
755
+ if (json.actions) {
756
+ for (const key of Object.keys(json.actions)) {
757
+ const actions = Array.isArray(json.actions[key]) ? json.actions[key] : [json.actions[key]];
758
+ actions.forEach((action, index) => assertAction(action, `$.actions.${key}${actions.length > 1 ? `[${index}]` : ''}`, [key]));
759
+ }
760
+ }
761
+ if (json.guards) {
762
+ for (const key of Object.keys(json.guards)) {
763
+ assertCondition(json.guards[key].when, `$.guards.${key}.when`);
764
+ }
765
+ }
766
+ if (json.delays) {
767
+ for (const key of Object.keys(json.delays)) {
768
+ const delay = json.delays[key];
769
+ assertResolvable(delay && typeof delay === 'object' && 'duration' in delay ? delay.duration : delay, `$.delays.${key}`);
770
+ }
771
+ }
772
+ assertStateNode(json, '$');
773
+ }
774
+ let _scxmlSessionId = 'session_scxml';
172
775
  let _scxmlMachineName = '';
173
776
 
174
777
  /** Evaluates an SCXML expression with context variables available via `with`. */
@@ -184,16 +787,16 @@ with (context) {
184
787
  if (!self) return false;
185
788
  try {
186
789
  const snap = self.getSnapshot?.();
187
- if (snap?._nodes) {
790
+ if (snap?.nodes) {
188
791
  const sanitized = stateId.replace(/\./g, '$');
189
- return snap._nodes.some(node => node.id === sanitized || node.id.endsWith('.' + sanitized));
792
+ return snap.nodes.some(node => node.id === sanitized || node.id.endsWith('.' + sanitized));
190
793
  }
191
794
  } catch {
192
795
  // ignore
193
796
  }
194
797
  return false;
195
798
  };
196
- const SCXML_ORIGIN = '#_scxml_session';
799
+ const SCXML_ORIGIN = `#_scxml_${_scxmlSessionId}`;
197
800
  const SCXML_ORIGIN_TYPE = 'http://www.w3.org/TR/scxml/#SCXMLEventProcessor';
198
801
  const isExternal = event && event._scxmlExternal;
199
802
  const isInitEvent = event && event.type === '@xstate.init';
@@ -205,7 +808,7 @@ with (context) {
205
808
  const isDoneEvent = event && typeof event.type === 'string' && event.type.startsWith('xstate.done.');
206
809
  const explicitType = event && event._scxmlEventType;
207
810
  const scxmlEvent = event && !isInitEvent ? {
208
- name: event.type,
811
+ name: event._scxmlEventName ?? toScxmlEventName(event.type),
209
812
  type: explicitType || (isExternal ? 'external' : 'internal'),
210
813
  sendid: event._scxmlSendId,
211
814
  origin: isExternal ? SCXML_ORIGIN : undefined,
@@ -240,10 +843,65 @@ return updates;
240
843
  const fn = new Function('context', 'updates', fnBody);
241
844
  return fn(context, updates);
242
845
  }
243
- function createMachineFromConfig(json, implementations = {}) {
244
- assertRootImplementationMarkers(json, implementations);
245
- assertNoUnresolvedMarkers(json);
246
- const resolvedImplementations = mergeImplementations(json, implementations);
846
+ function createMachineFromConfig(json, sources = {}) {
847
+ const resolvedSources = mergeSources(json, sources);
848
+ const expressionResolver = createExpressionResolver(json['@exprLang'], resolvedSources);
849
+ const {
850
+ evaluateResolvable,
851
+ resolveValue,
852
+ makeScope,
853
+ getDurationConfig
854
+ } = expressionResolver;
855
+ function resolveCondition(condition, slot, path) {
856
+ if (!condition) {
857
+ return undefined;
858
+ }
859
+ if (isResolvable(condition)) {
860
+ return args => !!evaluateResolvable(condition, slot, makeScope(args), path);
861
+ }
862
+ return args => {
863
+ const params = resolveValue(condition.params, slot, makeScope(args), `${path}.params`);
864
+ const declarativeGuard = json.guards?.[condition.type];
865
+ if (declarativeGuard) {
866
+ const guard = resolveCondition(declarativeGuard.when, 'guard', `$.guards.${condition.type}.when`);
867
+ return !!guard?.({
868
+ ...args,
869
+ params
870
+ });
871
+ }
872
+ const guardImpl = args.guards?.[condition.type];
873
+ if (!guardImpl) {
874
+ throw new Error(getMissingGuardMessage(condition.type, args.guards ?? {}));
875
+ }
876
+ return guardImpl(args, params);
877
+ };
878
+ }
879
+ function makeChoiceConfig(choice, path) {
880
+ if (!choice) {
881
+ return undefined;
882
+ }
883
+ if (isResolvable(choice)) {
884
+ return args => evaluateResolvable(choice, 'choice', makeScope(args), path);
885
+ }
886
+ validateChoiceConfig(choice, path);
887
+ return args => {
888
+ for (let index = 0; index < choice.length; index++) {
889
+ const branch = choice[index];
890
+ const guard = resolveCondition(branch.when, 'choice', `${path}[${index}].when`);
891
+ if (!guard || guard(args)) {
892
+ return {
893
+ target: branch.target,
894
+ context: branch.context ? resolveValue(branch.context, 'transitionContext', makeScope(args), `${path}[${index}].context`) : undefined,
895
+ input: branch.input !== undefined ? resolveValue(branch.input, 'input', makeScope(args), `${path}[${index}].input`) : undefined,
896
+ description: branch.description,
897
+ reenter: branch.reenter,
898
+ meta: branch.meta
899
+ };
900
+ }
901
+ }
902
+ throw new Error(`Choice state at ${path} did not match any branch.`);
903
+ };
904
+ }
247
905
 
248
906
  // Pending transition actions: set by .to functions, consumed by entry functions.
249
907
  // This bridges SCXML's exit→transition→entry action ordering with XState's
@@ -266,22 +924,13 @@ function createMachineFromConfig(json, implementations = {}) {
266
924
  function getMissingGuardMessage(type, guards) {
267
925
  return `Guard '${type}' is not implemented in machine '${json.id ?? '(machine)'}'. Available guards: ${Object.keys(guards).map(key => `'${key}'`).join(', ') || '(none)'}.`;
268
926
  }
269
- function resolveGuardConfig(guard) {
270
- if (!guard) {
271
- return undefined;
272
- }
273
- return args => {
274
- const guardImpl = args.guards?.[guard.type];
275
- if (!guardImpl) {
276
- throw new Error(getMissingGuardMessage(guard.type, args.guards ?? {}));
277
- }
278
- return guardImpl(args, guard.params);
279
- };
280
- }
281
- function resolveRouteConfig(route) {
927
+ function resolveRouteConfig(route, path) {
282
928
  if (!route || typeof route === 'function') {
283
929
  return route;
284
930
  }
931
+ if (isResolvable(route)) {
932
+ return args => evaluateResolvable(route, 'transition', makeScope(args), path);
933
+ }
285
934
  const {
286
935
  guard,
287
936
  ...routeConfig
@@ -289,13 +938,13 @@ function createMachineFromConfig(json, implementations = {}) {
289
938
  if (!guard) {
290
939
  return routeConfig;
291
940
  }
292
- const resolvedGuard = resolveGuardConfig({
941
+ const resolvedGuard = resolveCondition({
293
942
  type: guard
294
- });
943
+ }, 'guard', path);
295
944
  return args => resolvedGuard(args) ? routeConfig : undefined;
296
945
  }
297
946
  function iterNode(node, nodeKey) {
298
- const originalEntryActions = node.entry;
947
+ const originalEntryActions = toActionArray(node.entry);
299
948
  const stateId = node.id || nodeKey;
300
949
 
301
950
  // Wrap entry to first execute any pending transition actions, then normal entry.
@@ -309,8 +958,10 @@ function createMachineFromConfig(json, implementations = {}) {
309
958
  while (pendingPlatformErrors.length) {
310
959
  const err = pendingPlatformErrors.shift();
311
960
  enq.raise({
312
- type: 'error.execution',
961
+ type: 'xstate.error.execution',
962
+ error: err,
313
963
  _scxmlEventType: 'platform',
964
+ _scxmlEventName: 'error.execution',
314
965
  _scxmlEventData: err
315
966
  });
316
967
  }
@@ -358,7 +1009,7 @@ function createMachineFromConfig(json, implementations = {}) {
358
1009
  }
359
1010
 
360
1011
  // Execute normal entry actions
361
- if (originalEntryActions?.length) {
1012
+ if (originalEntryActions.length) {
362
1013
  const mergedX = context ? {
363
1014
  ...x,
364
1015
  context
@@ -381,7 +1032,7 @@ function createMachineFromConfig(json, implementations = {}) {
381
1032
  description: node.description,
382
1033
  tags: node.tags,
383
1034
  input: node.input,
384
- timeout: node.timeout,
1035
+ timeout: getDurationConfig(node.timeout, `$.states.${nodeKey}.timeout`),
385
1036
  states: node.states ? Object.entries(node.states).reduce((acc, [key, value]) => {
386
1037
  acc[key] = iterNode(value, key);
387
1038
  return acc;
@@ -391,18 +1042,27 @@ function createMachineFromConfig(json, implementations = {}) {
391
1042
  return acc;
392
1043
  }, {}) : undefined,
393
1044
  always: node.always ? getTransitionConfig(node.always) : undefined,
394
- choice: node.choice ? getTransitionConfig(node.choice) : undefined,
395
- route: resolveRouteConfig(node.route),
1045
+ onError: node.onError ? getTransitionConfig(node.onError) : undefined,
1046
+ choice: makeChoiceConfig(node.choice, `$.states.${nodeKey}.choice`),
1047
+ route: resolveRouteConfig(node.route, `$.states.${nodeKey}.route`),
396
1048
  after: node.after ? Object.entries(node.after).reduce((acc, [key, value]) => {
397
1049
  acc[key] = getTransitionConfig(value);
398
1050
  return acc;
399
1051
  }, {}) : undefined,
400
1052
  onTimeout: node.onTimeout ? getTransitionConfig(node.onTimeout) : undefined,
401
- entry: entryFn,
402
- exit: node.exit ? iterActions(node.exit) : undefined,
1053
+ entry: node.type === 'choice' ? undefined : entryFn,
1054
+ exit: node.exit ? iterActions(toActionArray(node.exit)) : undefined,
403
1055
  invoke: node.invoke ? iterInvokeConfigs(node.invoke) : undefined,
404
1056
  meta: node.meta,
405
- output: node._scxmlDonedata ? makeDonedataOutput(node._scxmlDonedata) : node.output
1057
+ output: node._scxmlDonedata ? makeDonedataOutput(node._scxmlDonedata) : isResolvable(node.output) ? ({
1058
+ context,
1059
+ event,
1060
+ self
1061
+ }) => evaluateResolvable(node.output, 'output', {
1062
+ context,
1063
+ event,
1064
+ self
1065
+ }, `$.states.${nodeKey}.output`) : node.output
406
1066
  };
407
1067
  return nodeConfig;
408
1068
  }
@@ -454,24 +1114,19 @@ function createMachineFromConfig(json, implementations = {}) {
454
1114
  // Create child machine from nested SCXML JSON
455
1115
  let src;
456
1116
  if (extInv._nestedMachineJSON) {
457
- src = createMachineFromConfig(extInv._nestedMachineJSON, resolvedImplementations);
458
- } else if (isUnserializableMarker(inv.src)) {
459
- src = inv.src.id ? resolvedImplementations.actors[inv.src.id] : undefined;
460
- if (!src) {
461
- throw new Error(`Missing actors.${inv.src.id ?? ''}`);
462
- }
1117
+ src = createMachineFromConfig(extInv._nestedMachineJSON, resolvedSources);
463
1118
  } else {
464
- src = inv.src;
1119
+ src = resolvedSources.actors[inv.src] ?? inv.src;
465
1120
  }
466
1121
  return {
467
1122
  src,
468
1123
  id: inv.id,
469
- systemId: inv.systemId,
470
- input: inv.input,
1124
+ registryKey: inv.registryKey,
1125
+ input: inv.input !== undefined ? args => resolveValue(inv.input, 'input', makeScope(args), '$.invoke.input') : undefined,
471
1126
  onDone: inv.onDone ? getTransitionConfig(inv.onDone) : undefined,
472
1127
  onError: inv.onError ? getTransitionConfig(inv.onError) : undefined,
473
1128
  onSnapshot: inv.onSnapshot ? getTransitionConfig(inv.onSnapshot) : undefined,
474
- timeout: inv.timeout,
1129
+ timeout: getDurationConfig(inv.timeout, '$.invoke.timeout'),
475
1130
  onTimeout: inv.onTimeout ? getTransitionConfig(inv.onTimeout) : undefined
476
1131
  };
477
1132
  });
@@ -485,8 +1140,16 @@ function createMachineFromConfig(json, implementations = {}) {
485
1140
  function raiseErrorExecution(state, tagname, err) {
486
1141
  const message = err instanceof Error ? err.message : typeof err === 'string' ? err : 'unknown error';
487
1142
  state.enq.raise({
488
- type: 'error.execution',
1143
+ type: 'xstate.error.execution',
1144
+ error: {
1145
+ tagname,
1146
+ message,
1147
+ line: NaN,
1148
+ column: NaN,
1149
+ reason: message
1150
+ },
489
1151
  _scxmlEventType: 'platform',
1152
+ _scxmlEventName: 'error.execution',
490
1153
  _scxmlEventData: {
491
1154
  tagname,
492
1155
  message,
@@ -497,8 +1160,31 @@ function createMachineFromConfig(json, implementations = {}) {
497
1160
  });
498
1161
  state.errored = true;
499
1162
  }
1163
+ function executeActionDefinition(action, x, enq, params, parentState, stack) {
1164
+ const definition = json.actions?.[action.type];
1165
+ if (!definition) {
1166
+ enq(x.actions[action.type], params);
1167
+ return {
1168
+ context: undefined,
1169
+ errored: parentState.errored
1170
+ };
1171
+ }
1172
+ if (stack.includes(action.type)) {
1173
+ raiseErrorExecution(parentState, 'action', new Error(`Circular action reference: ${stack.concat(action.type).join(' -> ')}`));
1174
+ return {
1175
+ context: undefined,
1176
+ errored: true
1177
+ };
1178
+ }
1179
+ const definitions = Array.isArray(definition) ? definition : [definition];
1180
+ return executeActions(definitions, {
1181
+ ...x,
1182
+ params
1183
+ }, enq, parentState, stack.concat(action.type));
1184
+ }
1185
+
500
1186
  /** Execute an array of SCXML action JSON descriptors with context and enqueue. */
501
- function executeActions(actions, x, enq, parentState) {
1187
+ function executeActions(actions, x, enq, parentState, stack = []) {
502
1188
  const state = parentState ?? {
503
1189
  enq,
504
1190
  errored: false
@@ -506,22 +1192,34 @@ function createMachineFromConfig(json, implementations = {}) {
506
1192
  let context;
507
1193
  for (const action of actions) {
508
1194
  if (state.errored) break;
509
- if (isBuiltInActionJSON(action)) {
1195
+ if (isResolvable(action)) {
1196
+ const result = evaluateResolvable(action, 'action', makeScope(x, {
1197
+ params: x.params,
1198
+ enq
1199
+ }), '$.actions');
1200
+ if (result && typeof result === 'object' && 'context' in result && result.context) {
1201
+ context ??= {};
1202
+ Object.assign(context, result.context);
1203
+ }
1204
+ } else if (isBuiltInActionJSON(action)) {
510
1205
  switch (action.type) {
511
1206
  case '@xstate.raise':
512
1207
  {
513
1208
  // Tag as external if it has a delay (from <send>, not <raise>).
514
1209
  // Attach _scxmlSendId so _event.sendid can be read in expressions.
515
1210
  const isExternal = action.delay !== undefined;
1211
+ const resolvedEvent = resolveValue(action.event, 'actionParams', makeScope(x, {
1212
+ params: x.params
1213
+ }), '$.actions.event');
516
1214
  const event = isExternal || action.id !== undefined ? {
517
- ...action.event,
1215
+ ...resolvedEvent,
518
1216
  ...(isExternal ? {
519
1217
  _scxmlExternal: true
520
1218
  } : {}),
521
1219
  ...(action.id !== undefined ? {
522
1220
  _scxmlSendId: action.id
523
1221
  } : {})
524
- } : action.event;
1222
+ } : resolvedEvent;
525
1223
  enq.raise(event, {
526
1224
  id: action.id,
527
1225
  delay: action.delay
@@ -535,11 +1233,15 @@ function createMachineFromConfig(json, implementations = {}) {
535
1233
  enq.log(...action.args);
536
1234
  break;
537
1235
  case '@xstate.emit':
538
- enq.emit(action.event);
1236
+ enq.emit(resolveValue(action.event, 'actionParams', makeScope(x, {
1237
+ params: x.params
1238
+ }), '$.actions.event'));
539
1239
  break;
540
1240
  case '@xstate.assign':
541
1241
  context ??= {};
542
- Object.assign(context, action.context);
1242
+ Object.assign(context, resolveValue(action.context, 'transitionContext', makeScope(x, {
1243
+ params: x.params
1244
+ }), '$.actions.context'));
543
1245
  break;
544
1246
  default:
545
1247
  throw new Error(`Unknown built-in action: ${action.type}`);
@@ -578,8 +1280,10 @@ function createMachineFromConfig(json, implementations = {}) {
578
1280
  let delay;
579
1281
  try {
580
1282
  eventType = scxmlAction.eventexpr ? evaluateExpr(mergedContext, scxmlAction.eventexpr, x.event, x.self) : scxmlAction.event || 'unknown';
1283
+ eventType = normalizeScxmlEventType(eventType);
581
1284
  eventData = {
582
- type: eventType
1285
+ type: eventType,
1286
+ _scxmlEventName: toScxmlEventName(eventType)
583
1287
  };
584
1288
  if (scxmlAction.params) {
585
1289
  for (const param of scxmlAction.params) {
@@ -619,25 +1323,50 @@ function createMachineFromConfig(json, implementations = {}) {
619
1323
  const childRef = x.children?.[childId];
620
1324
  if (childRef) {
621
1325
  enq.sendTo(childRef, eventData);
1326
+ } else if (childId.startsWith('scxml_') && childId !== `scxml_${_scxmlSessionId}`) {
1327
+ const message = `Unable to dispatch event to target: ${target}`;
1328
+ enq.raise({
1329
+ type: 'xstate.error.communication',
1330
+ error: {
1331
+ tagname: 'send',
1332
+ message,
1333
+ line: NaN,
1334
+ column: NaN,
1335
+ reason: message
1336
+ },
1337
+ _scxmlEventType: 'platform',
1338
+ _scxmlEventName: 'error.communication',
1339
+ _scxmlEventData: {
1340
+ tagname: 'send',
1341
+ message,
1342
+ line: NaN,
1343
+ column: NaN,
1344
+ reason: message
1345
+ }
1346
+ });
622
1347
  } else {
623
- // Not a known child (e.g. #_scxml_sessionid) → self-raise
624
- if (delay !== undefined) {
625
- eventData._scxmlExternal = true;
626
- }
627
- enq.raise(eventData, {
1348
+ // This SCXML session id uses the external event queue.
1349
+ eventData._scxmlExternal = true;
1350
+ enq.sendTo(x.self, eventData, {
628
1351
  id: scxmlAction.id,
629
1352
  delay
630
1353
  });
631
1354
  }
632
1355
  } else {
633
- // Self-send or no special target: raise as external event
634
- if (delay !== undefined) {
1356
+ // #_internal uses the internal queue; self-send/no target use the
1357
+ // external event queue.
1358
+ if (isInternalTarget) {
1359
+ enq.raise(eventData, {
1360
+ id: scxmlAction.id,
1361
+ delay
1362
+ });
1363
+ } else {
635
1364
  eventData._scxmlExternal = true;
1365
+ enq.sendTo(x.self, eventData, {
1366
+ id: scxmlAction.id,
1367
+ delay
1368
+ });
636
1369
  }
637
- enq.raise(eventData, {
638
- id: scxmlAction.id,
639
- delay
640
- });
641
1370
  }
642
1371
  } else if (action.type === 'scxml.cancel') {
643
1372
  const scxmlAction = action;
@@ -767,7 +1496,14 @@ function createMachineFromConfig(json, implementations = {}) {
767
1496
  }
768
1497
  }
769
1498
  } else {
770
- enq(x.actions[action.type], action.params);
1499
+ const params = resolveValue(action.params, 'actionParams', makeScope(x, {
1500
+ params: x.params
1501
+ }), '$.actions.params');
1502
+ const result = executeActionDefinition(action, x, enq, params, state, stack);
1503
+ if (result.context) {
1504
+ context ??= {};
1505
+ Object.assign(context, result.context);
1506
+ }
771
1507
  }
772
1508
  }
773
1509
  return {
@@ -788,31 +1524,67 @@ function createMachineFromConfig(json, implementations = {}) {
788
1524
  // a separate XState transition with its own guard and optional .to.
789
1525
  // This ensures guards are evaluated by XState's evaluateCandidate (once,
790
1526
  // with pre-exit context) and NOT re-evaluated in computeEntrySet.
791
- return transitions.map(t => {
1527
+ return transitions.map((t, index) => {
1528
+ if (isResolvable(t)) {
1529
+ return (x, enq) => evaluateResolvable(t, 'transition', makeScope(x, {
1530
+ enq
1531
+ }), `$.transition${transitions.length > 1 ? `[${index}]` : ''}`);
1532
+ }
792
1533
  const target = Array.isArray(t.target) ? t.target[0] : t.target;
793
1534
  const targetConfig = t.target;
794
- const guard = resolveGuardConfig(t.guard);
1535
+ const guard = resolveCondition(t.guard, 'guard', '$.transition.guard');
1536
+ const resolveTransitionContext = x => t.context ? resolveValue(t.context, 'transitionContext', makeScope(x), '$.transition.context') : undefined;
1537
+ const resolveTransitionInput = x => t.input !== undefined ? resolveValue(t.input, 'input', makeScope(x), '$.transition.input') : undefined;
795
1538
 
796
1539
  // No guard and no actions: simple static config
797
1540
  if (!guard && !t.actions?.length) {
1541
+ if (t.context) {
1542
+ return {
1543
+ matches: t.matches,
1544
+ to: x => ({
1545
+ target: targetConfig,
1546
+ context: resolveTransitionContext(x),
1547
+ reenter: t.reenter
1548
+ }),
1549
+ description: t.description,
1550
+ meta: t.meta,
1551
+ input: t.input !== undefined ? resolveTransitionInput : undefined
1552
+ };
1553
+ }
798
1554
  return {
1555
+ matches: t.matches,
799
1556
  target: targetConfig,
800
1557
  description: t.description,
801
1558
  reenter: t.reenter,
802
1559
  meta: t.meta,
803
- input: t.input
1560
+ input: t.input !== undefined ? resolveTransitionInput : undefined
804
1561
  };
805
1562
  }
806
1563
 
807
1564
  // Guard but no actions: static config with guard
808
1565
  if (guard && !t.actions?.length) {
1566
+ if (t.context) {
1567
+ return {
1568
+ matches: t.matches,
1569
+ guard,
1570
+ to: x => ({
1571
+ target: targetConfig,
1572
+ context: resolveTransitionContext(x),
1573
+ reenter: t.reenter
1574
+ }),
1575
+ description: t.description,
1576
+ meta: t.meta,
1577
+ input: t.input !== undefined ? resolveTransitionInput : undefined
1578
+ };
1579
+ }
809
1580
  return {
1581
+ matches: t.matches,
810
1582
  target: targetConfig,
811
1583
  guard,
812
1584
  description: t.description,
813
1585
  reenter: t.reenter,
814
1586
  meta: t.meta,
815
- input: t.input
1587
+ input: t.input !== undefined ? resolveTransitionInput : undefined
816
1588
  };
817
1589
  }
818
1590
 
@@ -821,12 +1593,14 @@ function createMachineFromConfig(json, implementations = {}) {
821
1593
  // For parallel (entries exist), first entry re-executes all in document order.
822
1594
  if (!target) {
823
1595
  return {
1596
+ matches: t.matches,
824
1597
  guard,
825
1598
  description: t.description,
826
1599
  reenter: t.reenter,
827
1600
  meta: t.meta,
828
- input: t.input,
1601
+ input: t.input !== undefined ? resolveTransitionInput : undefined,
829
1602
  to: (x, enq) => {
1603
+ const context = resolveTransitionContext(x);
830
1604
  if (t.actions?.length) {
831
1605
  // Track for parallel re-execution (dedup by reference)
832
1606
  if (!allTransitionActions.includes(t.actions)) {
@@ -838,11 +1612,16 @@ function createMachineFromConfig(json, implementations = {}) {
838
1612
  const result = executeActions(t.actions, x, enq);
839
1613
  if (result.context) {
840
1614
  return {
841
- context: result.context
1615
+ context: {
1616
+ ...context,
1617
+ ...result.context
1618
+ }
842
1619
  };
843
1620
  }
844
1621
  }
845
- return {};
1622
+ return context ? {
1623
+ context
1624
+ } : {};
846
1625
  }
847
1626
  };
848
1627
  }
@@ -852,12 +1631,14 @@ function createMachineFromConfig(json, implementations = {}) {
852
1631
  // already validated it. The .to just stores actions for entry to execute.
853
1632
  // Map by target state ID so parallel transitions each get their own actions.
854
1633
  return {
1634
+ matches: t.matches,
855
1635
  guard,
856
1636
  description: t.description,
857
1637
  reenter: t.reenter,
858
1638
  meta: t.meta,
859
- input: t.input,
1639
+ input: t.input !== undefined ? resolveTransitionInput : undefined,
860
1640
  to: (_x, _enq) => {
1641
+ const context = resolveTransitionContext(_x);
861
1642
  if (t.actions?.length) {
862
1643
  const targetId = target.replace(/^#/, '');
863
1644
  pendingTransitionActionsMap[targetId] = t.actions;
@@ -868,25 +1649,39 @@ function createMachineFromConfig(json, implementations = {}) {
868
1649
  }
869
1650
  return {
870
1651
  target: targetConfig,
1652
+ context,
871
1653
  reenter: t.reenter
872
1654
  };
873
1655
  }
874
1656
  };
875
1657
  });
876
1658
  }
1659
+ if (json.delays) {
1660
+ for (const key of Object.keys(json.delays)) {
1661
+ const delay = json.delays[key];
1662
+ if (typeof delay === 'number') {
1663
+ resolvedSources.delays[key] = delay;
1664
+ } else if (typeof delay === 'string') {
1665
+ resolvedSources.delays[key] = delayToMs(delay);
1666
+ } else if (delay && typeof delay === 'object') {
1667
+ resolvedSources.delays[key] = args => delayToMs(resolveValue(delay.duration, 'delay', makeScope(args), `$.delays.${key}.duration`));
1668
+ }
1669
+ }
1670
+ }
1671
+ assertMachineJSON(json, resolvedSources, expressionResolver);
877
1672
  const rootNodeConfig = iterNode(json);
878
1673
  const contextConfig = json.context ? {
879
- context: json.context
1674
+ context: args => resolveValue(json.context, 'context', args, '$.context')
880
1675
  } : {};
881
1676
  _scxmlMachineName = json.id || '';
882
- _scxmlSessionId = 'session_' + Math.random().toString(36).slice(2);
1677
+ _scxmlSessionId = 'session_scxml';
883
1678
  const machine = createMachine({
884
1679
  ...rootNodeConfig,
885
1680
  ...contextConfig,
886
1681
  version: json.version
887
1682
  });
888
1683
 
889
- // Register SCXML guard implementations
1684
+ // Register SCXML guard sources
890
1685
  const providedGuards = {
891
1686
  'scxml.cond': ({
892
1687
  context,
@@ -917,8 +1712,8 @@ function createMachineFromConfig(json, implementations = {}) {
917
1712
  if (!stateId) return false;
918
1713
  const normalizedId = stateId.replace(/^#/, '');
919
1714
  const snapshot = args._snapshot;
920
- if (snapshot?._nodes) {
921
- return snapshot._nodes.some(node => node.id === normalizedId);
1715
+ if (snapshot?.nodes) {
1716
+ return snapshot.nodes.some(node => node.id === normalizedId);
922
1717
  }
923
1718
  return false;
924
1719
  },
@@ -932,13 +1727,13 @@ function createMachineFromConfig(json, implementations = {}) {
932
1727
  }
933
1728
  };
934
1729
  const provided = machine.provide({
935
- actions: resolvedImplementations.actions,
936
- actors: resolvedImplementations.actors,
1730
+ actions: resolvedSources.actions,
1731
+ actors: resolvedSources.actors,
937
1732
  guards: {
938
1733
  ...providedGuards,
939
- ...resolvedImplementations.guards
1734
+ ...resolvedSources.guards
940
1735
  },
941
- delays: resolvedImplementations.delays
1736
+ delays: resolvedSources.delays
942
1737
  });
943
1738
 
944
1739
  // Keep the original JSON so `serializeMachine(machine)`
@@ -947,7 +1742,7 @@ function createMachineFromConfig(json, implementations = {}) {
947
1742
  return provided;
948
1743
  }
949
1744
  function isBuiltInActionJSON(action) {
950
- return action.type.startsWith('@xstate.');
1745
+ return !isResolvable(action) && action.type.startsWith('@xstate.');
951
1746
  }
952
1747
 
953
1748
  /**
@@ -955,24 +1750,17 @@ function isBuiltInActionJSON(action) {
955
1750
  *
956
1751
  * `machineConfigToJSON` converts a machine config into a JSON-safe structure
957
1752
  * (the `MachineJSON` shape accepted by `createMachineFromConfig`). The boundary
958
- * between serializable structure and runtime implementations is explicit:
959
- * functions are represented as code expressions, while actor logic, runtime
960
- * schemas, and other non-code runtime values are replaced with an `{
961
- * "$unserializable": <kind> }` marker rather than silently dropped.
962
- *
963
- * A machine definition is fully portable iff its JSON contains no
964
- * `$unserializable` markers; reviving a definition that has markers requires
965
- * re-providing those implementations (e.g. via `machine.provide(...)` or the
966
- * `actions`/`guards`/`actors` maps on `createMachineFromConfig` revival).
1753
+ * between serializable structure and runtime sources is explicit: functions are
1754
+ * represented as `@code` source objects, while actor logic, runtime schemas,
1755
+ * and other non-data runtime values are omitted.
967
1756
  */
968
1757
 
969
1758
  /**
970
1759
  * Returns the JSON-serializable definition of a machine.
971
1760
  *
972
- * Inline functions are represented as `{ "@type": "code", lang: "ts", expr }`.
973
- * Actor logic and runtime schemas appear as `{ "$unserializable": ... }`
974
- * markers. A machine created via `createMachineFromConfig` returns its original
975
- * JSON config (lossless round-trip):
1761
+ * Inline functions are represented as `{ "@code": string, "@lang": "ts" }`. A
1762
+ * machine created via `createMachineFromConfig` returns its original JSON
1763
+ * config (lossless round-trip):
976
1764
  *
977
1765
  * ```ts
978
1766
  * import { serializeMachine, createMachineFromConfig } from 'xstate';
@@ -986,22 +1774,12 @@ function isBuiltInActionJSON(action) {
986
1774
  function serializeMachine(machine) {
987
1775
  return machine._json ?? machineConfigToJSON(machine.config);
988
1776
  }
989
- function marker(kind, id) {
990
- const m = {
991
- $unserializable: kind
992
- };
993
- if (id) {
994
- m.id = id;
995
- }
996
- return m;
997
- }
998
1777
 
999
1778
  // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
1000
1779
  function codeExpression(fn) {
1001
1780
  return {
1002
- '@type': 'code',
1003
- lang: 'ts',
1004
- expr: fn.toString()
1781
+ '@code': fn.toString(),
1782
+ '@lang': 'ts'
1005
1783
  };
1006
1784
  }
1007
1785
  function isActorLogic(value) {
@@ -1017,33 +1795,37 @@ function valueToJSON(value) {
1017
1795
  return codeExpression(value);
1018
1796
  }
1019
1797
  if (value === null || typeof value !== 'object') {
1020
- return typeof value === 'bigint' || typeof value === 'symbol' ? marker('value') : value;
1798
+ return typeof value === 'bigint' || typeof value === 'symbol' ? undefined : value;
1021
1799
  }
1022
1800
  if (isActorLogic(value)) {
1023
- return marker('actor', value.id);
1801
+ return undefined;
1024
1802
  }
1025
1803
  if (isRuntimeSchema(value)) {
1026
- return marker('schema');
1804
+ return undefined;
1027
1805
  }
1028
1806
  if (Array.isArray(value)) {
1029
- return value.map(valueToJSON);
1807
+ return value.map(valueToJSON).filter(item => item !== undefined);
1030
1808
  }
1031
1809
  if (value.constructor !== Object && value.constructor !== undefined) {
1032
1810
  // Class instances (actor logic, schemas, dates, ...) are not portable.
1033
- return marker('value');
1811
+ return undefined;
1034
1812
  }
1035
1813
  const result = {};
1036
1814
  for (const key of Object.keys(value)) {
1037
1815
  const v = value[key];
1038
1816
  if (v !== undefined) {
1039
- result[key] = valueToJSON(v);
1817
+ const jsonValue = valueToJSON(v);
1818
+ if (jsonValue !== undefined) {
1819
+ result[key] = jsonValue;
1820
+ }
1040
1821
  }
1041
1822
  }
1042
1823
  return result;
1043
1824
  }
1044
1825
  function invokeToJSON(invoke) {
1045
1826
  if (Array.isArray(invoke)) {
1046
- return invoke.map(invokeToJSON);
1827
+ const values = invoke.map(invokeToJSON).filter(value => value !== undefined);
1828
+ return values.length ? values : undefined;
1047
1829
  }
1048
1830
  const def = invoke;
1049
1831
  const result = {};
@@ -1052,32 +1834,28 @@ function invokeToJSON(invoke) {
1052
1834
  if (value === undefined) {
1053
1835
  continue;
1054
1836
  }
1055
- result[key] = key === 'src' && typeof value !== 'string' ?
1056
- // Actor logic keeps its dedicated marker kind so revival knows the
1057
- // contract is an actor, not a plain function.
1058
- marker('actor', value.id) : valueToJSON(value);
1837
+ result[key] = key === 'src' && typeof value !== 'string' ? value.id : valueToJSON(value);
1838
+ if (result[key] === undefined) {
1839
+ delete result[key];
1840
+ }
1059
1841
  }
1060
- return result;
1842
+ return result.src === undefined ? undefined : result;
1061
1843
  }
1062
- function implementationsToJSON(map, kind) {
1844
+ function sourcesToJSON(map) {
1063
1845
  if (!map) {
1064
1846
  return undefined;
1065
1847
  }
1066
- // Keys are preserved — they are the contract a revived machine must
1067
- // fulfill via provide() — while values become markers (or stay, for
1068
- // JSON-safe values like numeric delays).
1069
1848
  const result = {};
1070
1849
  for (const key of Object.keys(map)) {
1071
1850
  const value = map[key];
1072
- if (kind === 'schema') {
1073
- result[key] = marker('schema', key);
1074
- } else if (typeof value === 'function') {
1075
- result[key] = codeExpression(value);
1076
- } else if (kind === 'actor' && value && typeof value === 'object') {
1077
- result[key] = marker('actor', key);
1851
+ if (typeof value === 'function') {
1852
+ result[key] = undefined;
1078
1853
  } else {
1079
1854
  result[key] = valueToJSON(value);
1080
1855
  }
1856
+ if (result[key] === undefined) {
1857
+ delete result[key];
1858
+ }
1081
1859
  }
1082
1860
  return result;
1083
1861
  }
@@ -1093,7 +1871,10 @@ function stateNodeConfigToJSON(config) {
1093
1871
  }
1094
1872
  }
1095
1873
  if (config.invoke !== undefined) {
1096
- result.invoke = invokeToJSON(config.invoke);
1874
+ const invoke = invokeToJSON(config.invoke);
1875
+ if (invoke !== undefined) {
1876
+ result.invoke = invoke;
1877
+ }
1097
1878
  }
1098
1879
  if (config.states) {
1099
1880
  const states = {};
@@ -1107,7 +1888,7 @@ function stateNodeConfigToJSON(config) {
1107
1888
 
1108
1889
  /**
1109
1890
  * Converts a machine config (as passed to `createMachine`) to its JSON-safe
1110
- * definition. See module docs for the `$unserializable` marker contract.
1891
+ * definition.
1111
1892
  */
1112
1893
  function machineConfigToJSON(config) {
1113
1894
  const result = stateNodeConfigToJSON(config);
@@ -1120,16 +1901,24 @@ function machineConfigToJSON(config) {
1120
1901
  const value = config.schemas[key];
1121
1902
  if (value && typeof value === 'object' && !('~standard' in value)) {
1122
1903
  // Map-form schemas (events/emitted): preserve event-type keys.
1123
- schemas[key] = implementationsToJSON(value, 'schema');
1904
+ schemas[key] = sourcesToJSON(value);
1124
1905
  } else {
1125
- schemas[key] = marker('schema', key);
1906
+ schemas[key] = valueToJSON(value);
1907
+ }
1908
+ if (schemas[key] === undefined) {
1909
+ delete schemas[key];
1126
1910
  }
1127
1911
  }
1128
1912
  result.schemas = schemas;
1129
1913
  }
1130
1914
  for (const key of ['actions', 'guards', 'actors', 'delays']) {
1131
1915
  if (config[key]) {
1132
- result[key] = implementationsToJSON(config[key], key === 'actors' ? 'actor' : 'function');
1916
+ const value = sourcesToJSON(config[key]);
1917
+ if (value && Object.keys(value).length) {
1918
+ result[key] = value;
1919
+ } else {
1920
+ delete result[key];
1921
+ }
1133
1922
  }
1134
1923
  }
1135
1924
  return result;
@@ -1164,7 +1953,7 @@ function mapState(snapshot, mapper) {
1164
1953
  return mapper;
1165
1954
  };
1166
1955
  const visited = new Set();
1167
- for (const atomicNode of snapshot._nodes.filter(isAtomicStateNode)) {
1956
+ for (const atomicNode of snapshot.nodes.filter(isAtomicStateNode)) {
1168
1957
  let current = atomicNode;
1169
1958
  while (current && !visited.has(current)) {
1170
1959
  visited.add(current);
@@ -1181,8 +1970,235 @@ function mapState(snapshot, mapper) {
1181
1970
  return results;
1182
1971
  }
1183
1972
 
1973
+ function isObject(value) {
1974
+ return value !== null && typeof value === 'object';
1975
+ }
1976
+ function isVersionedStateMachine(entry) {
1977
+ return 'transition' in entry && typeof entry.transition === 'function';
1978
+ }
1979
+ function hasSnapshotSchema(entry) {
1980
+ return 'snapshotSchema' in entry && entry.snapshotSchema !== undefined;
1981
+ }
1982
+ function hasEventSchema(entry) {
1983
+ return 'eventSchema' in entry && entry.eventSchema !== undefined;
1984
+ }
1985
+ function getSnapshotSource(raw, defaultId) {
1986
+ if (!isObject(raw)) {
1987
+ return {};
1988
+ }
1989
+ if (isObject(raw.machine)) {
1990
+ return {
1991
+ id: typeof raw.machine.id === 'string' ? raw.machine.id : undefined,
1992
+ version: typeof raw.machine.version === 'string' ? raw.machine.version : undefined
1993
+ };
1994
+ }
1995
+ return {
1996
+ id: typeof raw.version === 'string' ? defaultId : undefined,
1997
+ version: typeof raw.version === 'string' ? raw.version : undefined
1998
+ };
1999
+ }
2000
+ async function validate(schema, value, description) {
2001
+ if (!schema) {
2002
+ return value;
2003
+ }
2004
+ const result = await schema['~standard'].validate(value);
2005
+ if (result.issues) {
2006
+ throw new Error(`Invalid ${description}: ${result.issues[0]?.message}`);
2007
+ }
2008
+ return result.value;
2009
+ }
2010
+ async function finalizeSnapshot(snapshot, target) {
2011
+ return await validate(target.snapshotSchema, {
2012
+ ...snapshot,
2013
+ machine: {
2014
+ id: target.id,
2015
+ version: target.version
2016
+ },
2017
+ version: target.version
2018
+ }, `snapshot for machine '${target.id}' version '${target.version}'`);
2019
+ }
2020
+ async function validateEvents(events, source) {
2021
+ return Promise.all(events.map(async (event, index) => {
2022
+ const result = await source.eventSchema['~standard'].validate(event);
2023
+ if (result.issues) {
2024
+ const message = result.issues[0]?.message;
2025
+ if (message?.startsWith('Unknown event ')) {
2026
+ throw new Error(message);
2027
+ }
2028
+ const type = isObject(event) ? event.type : undefined;
2029
+ throw new Error(typeof type === 'string' ? `Invalid event '${type}' at index ${index}${message ? `: ${message}` : '.'}` : `Invalid event at index ${index}${message ? `: ${message}` : '.'}`);
2030
+ }
2031
+ const validated = result.value;
2032
+ if (!isObject(validated) || typeof validated.type !== 'string') {
2033
+ throw new Error(`Invalid event at index ${index}.`);
2034
+ }
2035
+ return validated;
2036
+ }));
2037
+ }
2038
+
2039
+ /** Creates migration and adaptation utilities for versions of one machine. */
2040
+ function machineVersions(entries, options) {
2041
+ const byIdentity = new Map();
2042
+ const machinesByIdentity = new Map();
2043
+ const machineId = entries[0].id;
2044
+ for (const entry of entries) {
2045
+ if (entry.version === undefined) {
2046
+ throw new Error(`Machine '${entry.id}' must define a version.`);
2047
+ }
2048
+ if (entry.version === '*') {
2049
+ throw new Error("Machine version '*' is reserved for wildcard migrations.");
2050
+ }
2051
+ if (entry.id !== machineId) {
2052
+ throw new Error(`Machine '${entry.id}' does not match machine ID '${machineId}'.`);
2053
+ }
2054
+ const key = `${entry.id}\0${entry.version}`;
2055
+ if (byIdentity.has(key)) {
2056
+ throw new Error(`Duplicate machine identity '${entry.id}' version '${entry.version}'.`);
2057
+ }
2058
+ byIdentity.set(key, entry);
2059
+ if (isVersionedStateMachine(entry)) {
2060
+ machinesByIdentity.set(key, entry);
2061
+ }
2062
+ }
2063
+ if (options?.unversioned !== undefined && !byIdentity.has(`${machineId}\0${options.unversioned}`)) {
2064
+ throw new Error(`Unversioned snapshot version '${options.unversioned}' is not retained for machine '${machineId}'.`);
2065
+ }
2066
+ const parseSnapshot = async raw => {
2067
+ if (!isObject(raw)) {
2068
+ throw new Error('Persisted snapshot is missing machine identity.');
2069
+ }
2070
+ let id;
2071
+ let version;
2072
+ if (isObject(raw.machine)) {
2073
+ ({
2074
+ id,
2075
+ version
2076
+ } = raw.machine);
2077
+ if (typeof id !== 'string' || typeof version !== 'string') {
2078
+ throw new Error('Persisted snapshot has an invalid machine identity.');
2079
+ }
2080
+ } else if (typeof raw.version === 'string') {
2081
+ id = machineId;
2082
+ version = raw.version;
2083
+ } else if (raw.version === undefined && options?.unversioned !== undefined) {
2084
+ id = machineId;
2085
+ version = options.unversioned;
2086
+ } else {
2087
+ throw new Error('Persisted snapshot is missing machine identity.');
2088
+ }
2089
+ if (raw.version !== undefined && typeof raw.version !== 'string') {
2090
+ throw new Error('Persisted snapshot has an invalid version.');
2091
+ }
2092
+ if (raw.version !== undefined && raw.version !== version) {
2093
+ throw new Error(`Persisted snapshot version '${raw.version}' conflicts with machine version '${version}'.`);
2094
+ }
2095
+ const source = byIdentity.get(`${id}\0${version}`);
2096
+ if (!source) {
2097
+ throw new Error(`Unknown machine identity '${id}' version '${version}'.`);
2098
+ }
2099
+ if (!hasSnapshotSchema(source)) {
2100
+ throw new Error(`Machine version '${version}' does not define a snapshot schema.`);
2101
+ }
2102
+ const snapshot = await validate(source.snapshotSchema, raw, `snapshot for machine '${id}' version '${version}'`);
2103
+ return {
2104
+ source,
2105
+ ...(isVersionedStateMachine(source) && {
2106
+ machine: source
2107
+ }),
2108
+ snapshot: {
2109
+ ...snapshot,
2110
+ machine: {
2111
+ id,
2112
+ version
2113
+ }
2114
+ }
2115
+ };
2116
+ };
2117
+ return {
2118
+ parseSnapshot,
2119
+ async adaptEvents(events, adaptationOptions) {
2120
+ const target = machinesByIdentity.get(`${machineId}\0${adaptationOptions.to}`);
2121
+ if (!target) {
2122
+ throw new Error(`Target version '${adaptationOptions.to}' is not backed by a machine for '${machineId}'.`);
2123
+ }
2124
+ const sourceId = adaptationOptions.from.id ?? machineId;
2125
+ const source = byIdentity.get(`${sourceId}\0${adaptationOptions.from.version}`);
2126
+ let sourceError;
2127
+ let sourceValidationFailed = false;
2128
+ const hasTypedSource = source && hasEventSchema(source);
2129
+ if (hasTypedSource) {
2130
+ let sourceEvents;
2131
+ try {
2132
+ sourceEvents = await validateEvents(events, source);
2133
+ } catch (error) {
2134
+ sourceError = error;
2135
+ sourceValidationFailed = true;
2136
+ }
2137
+ if (sourceEvents) {
2138
+ if (source === target) {
2139
+ return sourceEvents;
2140
+ }
2141
+ const adapter = adaptationOptions.adapters[source.version];
2142
+ if (adapter) {
2143
+ return validateEvents(await adapter(sourceEvents), target);
2144
+ }
2145
+ }
2146
+ }
2147
+ const wildcardAdapter = adaptationOptions.adapters['*'];
2148
+ if (wildcardAdapter) {
2149
+ return validateEvents(await wildcardAdapter(events, adaptationOptions.from), target);
2150
+ }
2151
+ if (hasTypedSource) {
2152
+ if (sourceValidationFailed) {
2153
+ throw sourceError instanceof Error ? sourceError : new Error(`Unable to validate event history for machine '${source.id}' version '${source.version}'.`, {
2154
+ cause: sourceError
2155
+ });
2156
+ }
2157
+ throw new Error(`No event adapter from version '${source.version}' to '${target.version}' for machine '${target.id}'.`);
2158
+ }
2159
+ if (source) {
2160
+ throw new Error(`Machine version '${source.version}' does not define an event schema; only the '*' adapter can handle its event history.`);
2161
+ }
2162
+ throw new Error(`Unknown event history source '${sourceId}' version '${adaptationOptions.from.version}'.`);
2163
+ },
2164
+ async migrateSnapshot(raw, migrationOptions) {
2165
+ const target = machinesByIdentity.get(`${machineId}\0${migrationOptions.to}`);
2166
+ if (!target) {
2167
+ throw new Error(`Target version '${migrationOptions.to}' is not backed by a machine for '${machineId}'.`);
2168
+ }
2169
+ let source;
2170
+ let parseError;
2171
+ try {
2172
+ source = await parseSnapshot(raw);
2173
+ } catch (error) {
2174
+ parseError = error;
2175
+ }
2176
+ if (source) {
2177
+ if (source.source.version === target.version) {
2178
+ return finalizeSnapshot(source.snapshot, target);
2179
+ }
2180
+ const exactMigration = migrationOptions.migrations[source.source.version];
2181
+ if (exactMigration) {
2182
+ return finalizeSnapshot(await exactMigration(source.snapshot), target);
2183
+ }
2184
+ }
2185
+ const wildcardMigration = migrationOptions.migrations['*'];
2186
+ if (!wildcardMigration) {
2187
+ if (source) {
2188
+ throw new Error(`No snapshot migration from version '${source.source.version}' to '${target.version}' for machine '${target.id}'.`);
2189
+ }
2190
+ throw parseError;
2191
+ }
2192
+ const snapshot = await wildcardMigration(raw, getSnapshotSource(raw, machineId));
2193
+ return finalizeSnapshot(snapshot, target);
2194
+ }
2195
+ };
2196
+ }
2197
+
1184
2198
  /** The Standard Schema interface. */
1185
2199
 
2200
+ /** Schemas that can be declared for an individual state node. */
2201
+
1186
2202
  /** A type-only Standard Schema produced by {@link types}. */
1187
2203
 
1188
2204
  /**
@@ -1232,6 +2248,28 @@ function isTypeSchema(value) {
1232
2248
 
1233
2249
  /** State schema with optional schemas.input and nested states */
1234
2250
 
2251
+ /**
2252
+ * The sibling state schemas of a dotted path: the children of the path's
2253
+ * parent. A bare transition target resolves relative to the parent, so the
2254
+ * valid targets for a path's config are its siblings. For a dotless (top-level)
2255
+ * path the siblings are the root states.
2256
+ */
2257
+
2258
+ // dotless path: siblings are the current level's states
2259
+
2260
+ /**
2261
+ * Resolves a dotted state path (e.g. `'parent.child'`) into the leaf
2262
+ * `SetupStateSchema` it addresses, or `never` if any segment is missing.
2263
+ */
2264
+
2265
+ /** Union of every addressable dotted path into a setup states tree */
2266
+
2267
+ /**
2268
+ * Shared body of both `createStateConfig` overloads: the
2269
+ * `StateNodeConfigWithNestedInput` instantiation whose only varying parts are
2270
+ * the leading sibling-schemas and state-schema arguments.
2271
+ */
2272
+
1235
2273
  /** Extracts input type from a state schema */
1236
2274
 
1237
2275
  /**
@@ -1287,8 +2325,9 @@ function isTypeSchema(value) {
1287
2325
  * });
1288
2326
  * ```
1289
2327
  */
1290
- function setup(config = {}) {
2328
+ const setup = function setupImplementation(config = {}) {
1291
2329
  const {
2330
+ validator,
1292
2331
  states = {},
1293
2332
  schemas,
1294
2333
  actions,
@@ -1303,6 +2342,7 @@ function setup(config = {}) {
1303
2342
  createMachine(machineConfig) {
1304
2343
  const configSchemas = machineConfig.schemas;
1305
2344
  const mergedSchemas = mergeSchemas(configSchemas, schemas);
2345
+ const mergedStates = mergeStateSchemas(machineConfig.states, states);
1306
2346
  const mergedActions = mergeMaps(actions, machineConfig.actions);
1307
2347
  const mergedActors = mergeMaps(actors, machineConfig.actors);
1308
2348
  const mergedGuards = mergeMaps(guards, machineConfig.guards);
@@ -1312,6 +2352,9 @@ function setup(config = {}) {
1312
2352
  ...(mergedSchemas ? {
1313
2353
  schemas: mergedSchemas
1314
2354
  } : undefined),
2355
+ ...(mergedStates ? {
2356
+ states: mergedStates
2357
+ } : undefined),
1315
2358
  ...(mergedActions ? {
1316
2359
  actions: mergedActions
1317
2360
  } : undefined),
@@ -1324,12 +2367,62 @@ function setup(config = {}) {
1324
2367
  ...(mergedDelays ? {
1325
2368
  delays: mergedDelays
1326
2369
  } : undefined)
2370
+ }, undefined, validator);
2371
+ },
2372
+ createStateConfig(...args) {
2373
+ return args.length > 1 ? args[1] : args[0];
2374
+ },
2375
+ states,
2376
+ schemas: schemas ?? {}
2377
+ };
2378
+ };
2379
+ function createSystem(_config = {}) {
2380
+ const runtimeRef = {};
2381
+ const pendingObservers = [];
2382
+ const flushObservers = () => {
2383
+ const runtime = runtimeRef.current;
2384
+ if (!runtime) {
2385
+ return;
2386
+ }
2387
+ for (const entry of pendingObservers) {
2388
+ if (entry.active && !entry.subscription) {
2389
+ entry.subscription = runtime.inspect(entry.observer);
2390
+ }
2391
+ }
2392
+ };
2393
+ return {
2394
+ createActor(logic, options) {
2395
+ const actor = createActor(logic, {
2396
+ ...options,
2397
+ _systemRef: runtimeRef
1327
2398
  });
2399
+ flushObservers();
2400
+ return actor;
2401
+ },
2402
+ get(key) {
2403
+ return runtimeRef.current?.get(key);
2404
+ },
2405
+ getAll() {
2406
+ return runtimeRef.current?.getAll() ?? {};
1328
2407
  },
1329
- createStateConfig(stateConfig) {
1330
- return stateConfig;
2408
+ inspect(observer) {
2409
+ const runtime = runtimeRef.current;
2410
+ if (runtime) {
2411
+ return runtime.inspect(observer);
2412
+ }
2413
+ const entry = {
2414
+ observer: observer,
2415
+ active: true
2416
+ };
2417
+ pendingObservers.push(entry);
2418
+ return {
2419
+ unsubscribe() {
2420
+ entry.active = false;
2421
+ entry.subscription?.unsubscribe();
2422
+ }
2423
+ };
1331
2424
  },
1332
- states
2425
+ setup: setup
1333
2426
  };
1334
2427
  }
1335
2428
  function mergeMaps(left, right) {
@@ -1346,10 +2439,39 @@ function mergeSchemas(left, right) {
1346
2439
  ...left,
1347
2440
  ...right,
1348
2441
  events: mergeMaps(left?.events, right?.events),
2442
+ actions: mergeMaps(left?.actions, right?.actions),
2443
+ guards: mergeMaps(left?.guards, right?.guards),
1349
2444
  emitted: mergeMaps(left?.emitted, right?.emitted),
1350
2445
  children: mergeMaps(left?.children, right?.children)
1351
2446
  };
1352
2447
  }
2448
+
2449
+ /**
2450
+ * Setup schemas win over inline config schemas (same precedence as root
2451
+ * `mergeSchemas`); setup states with no matching config state are skipped.
2452
+ */
2453
+ function mergeStateSchemas(configStates, setupStates) {
2454
+ if (!configStates || !setupStates) {
2455
+ return configStates;
2456
+ }
2457
+ return Object.fromEntries(Object.entries(configStates).map(([key, configState]) => {
2458
+ const setupState = setupStates[key];
2459
+ if (!setupState) {
2460
+ return [key, configState];
2461
+ }
2462
+ const schemas = mergeMaps(configState.schemas, setupState.schemas);
2463
+ const states = mergeStateSchemas(configState.states, setupState.states);
2464
+ return [key, {
2465
+ ...configState,
2466
+ ...(schemas ? {
2467
+ schemas
2468
+ } : undefined),
2469
+ ...(states ? {
2470
+ states
2471
+ } : undefined)
2472
+ }];
2473
+ }));
2474
+ }
1353
2475
  function mergeSetupConfigs(base, extension) {
1354
2476
  return {
1355
2477
  ...base,
@@ -1363,72 +2485,8 @@ function mergeSetupConfigs(base, extension) {
1363
2485
  };
1364
2486
  }
1365
2487
 
1366
- /** @internal */
1367
- function createInertActorScope(actorLogic) {
1368
- const self = createActor(actorLogic);
1369
- const inertActorScope = {
1370
- self: self,
1371
- defer: () => {},
1372
- id: '',
1373
- logger: () => {},
1374
- sessionId: '',
1375
- stopChild: () => {},
1376
- system: self.system,
1377
- emit: () => {},
1378
- actionExecutor: () => {}
1379
- };
1380
- return inertActorScope;
1381
- }
1382
-
1383
- /** @deprecated Use `initialTransition(…)` instead. */
1384
- function getInitialSnapshot(actorLogic, ...[input]) {
1385
- const actorScope = createInertActorScope(actorLogic);
1386
- return actorLogic.initialTransition(input, actorScope)[0];
1387
- }
1388
-
1389
- /**
1390
- * Determines the next snapshot for the given `actorLogic` based on the given
1391
- * `snapshot` and `event`.
1392
- *
1393
- * If the `snapshot` is `undefined`, the initial snapshot of the `actorLogic` is
1394
- * used.
1395
- *
1396
- * @deprecated Use `transition(…)` instead.
1397
- * @example
1398
- *
1399
- * ```ts
1400
- * import { getNextSnapshot } from 'xstate';
1401
- * import { trafficLightMachine } from './trafficLightMachine.ts';
1402
- *
1403
- * const nextSnapshot = getNextSnapshot(
1404
- * trafficLightMachine, // actor logic
1405
- * undefined, // snapshot (or initial state if undefined)
1406
- * { type: 'TIMER' }
1407
- * ); // event object
1408
- *
1409
- * console.log(nextSnapshot.value);
1410
- * // => 'yellow'
1411
- *
1412
- * const nextSnapshot2 = getNextSnapshot(
1413
- * trafficLightMachine, // actor logic
1414
- * nextSnapshot, // snapshot
1415
- * { type: 'TIMER' }
1416
- * ); // event object
1417
- *
1418
- * console.log(nextSnapshot2.value);
1419
- * // =>'red'
1420
- * ```
1421
- */
1422
- function getNextSnapshot(actorLogic, snapshot, event) {
1423
- const inertActorScope = createInertActorScope(actorLogic);
1424
- inertActorScope.self._snapshot = snapshot;
1425
- const transitionResult = actorLogic.transition(snapshot, event, inertActorScope);
1426
- return Array.isArray(transitionResult) ? transitionResult[0] : transitionResult;
1427
- }
1428
-
1429
- // eslint-disable-next-line @typescript-eslint/no-unsafe-declaration-merging
2488
+ // oxlint-disable-next-line typescript/no-unsafe-declaration-merging
1430
2489
 
1431
- // eslint-disable-next-line @typescript-eslint/no-unsafe-declaration-merging
1432
2490
  class SimulatedClock {
1433
2491
  constructor() {
1434
2492
  this.timeouts = new Map();
@@ -1535,6 +2593,7 @@ function toPromise(actor) {
1535
2593
  }
1536
2594
 
1537
2595
  // this is needed to make JSDoc `@link` work properly
2596
+ // oxlint-disable-next-line no-unused-vars
1538
2597
 
1539
2598
  /**
1540
2599
  * @remarks
@@ -1582,23 +2641,16 @@ let SpecialTargets = /*#__PURE__*/function (SpecialTargets) {
1582
2641
  return SpecialTargets;
1583
2642
  }({});
1584
2643
 
1585
- /**
1586
- * A pending effect (delayed event/transition that has not fired yet) captured
1587
- * in a persisted snapshot — a serialized built-in action descriptor plus its
1588
- * runtime progress, with enough information to restore it under any strategy.
1589
- */
2644
+ /** A deterministic delayed-delivery declaration owned by a logic snapshot. */
2645
+
2646
+ /** The logical input delivered to a timer's source when its runtime delay ends. */
2647
+
2648
+ /** A persisted snapshot tied to a versioned actor logic identity. */
1590
2649
 
1591
2650
  /**
1592
- * How pending effects (timers) from a persisted snapshot are restored on
1593
- * `actor.start()`:
1594
- *
1595
- * - `'resume'` (default) — the clock was "frozen" while persisted: a 5-minute
1596
- * timer persisted with 1 minute elapsed fires after 4 more minutes.
1597
- * - `'restart'` — timers start over with their full delay.
1598
- * - `'absolute'` — timers honor the original wall-clock expiry (`startedAt +
1599
- * delay`); already-expired timers fire immediately.
1600
- * - A custom function receiving the pending effect and returning the remaining
1601
- * delay in ms.
2651
+ * A persisted snapshot restorable into the given actor logic: any snapshot
2652
+ * created by logic with the same machine `id` is accepted, regardless of
2653
+ * version (version mismatches are handled at runtime, e.g. by `migrate`).
1602
2654
  */
1603
2655
 
1604
2656
  /** @deprecated Use `AnyActor` instead. */
@@ -1636,17 +2688,15 @@ let SpecialTargets = /*#__PURE__*/function (SpecialTargets) {
1636
2688
  /**
1637
2689
  * The actor's own full self handle.
1638
2690
  *
1639
- * Internals and action/guard implementations receive this shape because `self`
1640
- * can participate in runtime-owned behavior while still being usable wherever
1641
- * an `ActorRef` is expected.
2691
+ * Internals and action/guard sources receive this shape because `self` can
2692
+ * participate in runtime-owned behavior while still being usable wherever an
2693
+ * `ActorRef` is expected.
1642
2694
  */
1643
2695
 
1644
2696
  // TODO: in v6, this should only accept AnyActorLogic, like ActorRefFromLogic
1645
2697
 
1646
2698
  /** The concrete actor instance type produced from actor logic. */
1647
2699
 
1648
- // TODO: in v6, this should only accept AnyActorLogic, like ActorRefFromLogic
1649
-
1650
2700
  /**
1651
2701
  * Represents logic which can be used by an actor.
1652
2702
  *
@@ -1664,121 +2714,11 @@ let SpecialTargets = /*#__PURE__*/function (SpecialTargets) {
1664
2714
 
1665
2715
  /** @deprecated */
1666
2716
 
1667
- /** Mappers for subscribeTo - maps lifecycle events to machine events */
1668
-
1669
- /**
1670
- * Given actor `logic`, a `snapshot`, and an `event`, returns a tuple of the
1671
- * `nextSnapshot` and `actions` to execute.
1672
- *
1673
- * This is a pure function that does not execute `actions`.
1674
- */
1675
- function transition(logic, snapshot, event) {
1676
- const actorScope = createInertActorScope(logic);
1677
- const [nextSnapshot, effects] = logic.transition(snapshot, event, actorScope);
1678
- return [nextSnapshot, effects];
1679
- }
2717
+ /** The terminal result published when an actor completes or errors. */
1680
2718
 
1681
- /**
1682
- * Given actor `logic` and optional `input`, returns a tuple of the
1683
- * `nextSnapshot` and `actions` to execute from the initial transition (no
1684
- * previous state).
1685
- *
1686
- * This is a pure function that does not execute `actions`.
1687
- */
1688
- function initialTransition(logic, ...[input]) {
1689
- const actorScope = createInertActorScope(logic);
1690
- const [nextSnapshot, executableActions] = logic.initialTransition(input, actorScope);
1691
- return [nextSnapshot, executableActions];
1692
- }
2719
+ /** An executable effect that publishes an actor's terminal result. */
1693
2720
 
1694
- /**
1695
- * Given a state `machine`, a `snapshot`, and an `event`, returns an array of
1696
- * microsteps, where each microstep is a tuple of `[snapshot, actions]`.
1697
- *
1698
- * This is a pure function that does not execute `actions`.
1699
- */
1700
- function getMicrosteps(machine, snapshot, event) {
1701
- const actorScope = createInertActorScope(machine);
1702
- const {
1703
- microsteps
1704
- } = macrostep(snapshot, event, actorScope, []);
1705
- return microsteps;
1706
- }
1707
-
1708
- /**
1709
- * Given a state `machine` and optional `input`, returns an array of microsteps
1710
- * from the initial transition, where each microstep is a tuple of `[snapshot,
1711
- * actions]`.
1712
- *
1713
- * This is a pure function that does not execute `actions`.
1714
- */
1715
- function getInitialMicrosteps(machine, ...[input]) {
1716
- const actorScope = createInertActorScope(machine);
1717
- const initEvent = createInitEvent(input);
1718
- const internalQueue = [];
1719
- const preInitialSnapshot = machine._getPreInitialState(actorScope, initEvent);
1720
- const first = initialMicrostep(machine.root, preInitialSnapshot, actorScope, initEvent, internalQueue);
1721
- const {
1722
- microsteps
1723
- } = macrostep(first[0], initEvent, actorScope, internalQueue);
1724
- return [first, ...microsteps];
1725
- }
1726
-
1727
- /**
1728
- * Gets all potential next transitions from the current state.
1729
- *
1730
- * Returns all transitions that are available from the current state, including:
1731
- *
1732
- * - All transitions from atomic states (leaf states in the current state
1733
- * configuration)
1734
- * - All transitions from ancestor states (parent states that may handle events)
1735
- * - All guarded transitions (regardless of whether their guards would pass)
1736
- * - Always (eventless) transitions
1737
- * - After (delayed) transitions
1738
- *
1739
- * The order of transitions is deterministic:
1740
- *
1741
- * 1. Atomic states are processed in document order
1742
- * 2. For each atomic state, transitions are collected from the state itself first,
1743
- * then its ancestors
1744
- * 3. Within each state node, transitions are in the order they appear in the state
1745
- * definition
1746
- *
1747
- * @param state - The current machine snapshot
1748
- * @returns Array of transition definitions from the current state, in
1749
- * deterministic order
1750
- */
1751
- function getNextTransitions(state) {
1752
- const potentialTransitions = [];
1753
- const atomicStates = state._nodes.filter(isAtomicStateNode);
1754
- const visited = new Set();
1755
-
1756
- // Collect all transitions from atomic states and their ancestors
1757
- // Process atomic states in document order (as they appear in state._nodes)
1758
- for (const stateNode of atomicStates) {
1759
- // For each atomic state, process the state itself first, then its ancestors
1760
- // This ensures child state transitions come before parent state transitions
1761
- for (const s of [stateNode].concat(getProperAncestors(stateNode, undefined))) {
1762
- if (visited.has(s.id)) {
1763
- continue;
1764
- }
1765
- visited.add(s.id);
1766
-
1767
- // Get all transitions for each event type
1768
- // Include ALL transitions, even if the same event type appears in multiple state nodes
1769
- // This is important for guarded transitions - all are "potential" regardless of guard evaluation
1770
- for (const [, transitions] of s.transitions.entries()) {
1771
- potentialTransitions.push(...transitions);
1772
- }
1773
-
1774
- // Also include always (eventless) transitions
1775
- if (s.always) {
1776
- potentialTransitions.push(...s.always);
1777
- }
1778
- }
1779
- }
1780
- return potentialTransitions;
1781
- }
2721
+ /** Mappers for subscribeTo - maps lifecycle events to machine events */
1782
2722
 
1783
2723
  const defaultWaitForOptions = {
1784
2724
  timeout: Infinity // much more than 10 seconds
@@ -1815,7 +2755,7 @@ function waitFor(actorRef, predicate, options) {
1815
2755
  signal
1816
2756
  } = resolvedOptions;
1817
2757
  if (signal?.aborted) {
1818
- // eslint-disable-next-line @typescript-eslint/prefer-promise-reject-errors
2758
+ // oxlint-disable-next-line typescript/prefer-promise-reject-errors
1819
2759
  rej(signal.reason);
1820
2760
  return;
1821
2761
  }
@@ -1844,7 +2784,7 @@ function waitFor(actorRef, predicate, options) {
1844
2784
  * `abort` event
1845
2785
  */
1846
2786
  let abortListener;
1847
- // eslint-disable-next-line prefer-const
2787
+ // oxlint-disable-next-line prefer-const
1848
2788
  let sub; // avoid TDZ when disposing synchronously
1849
2789
 
1850
2790
  // See if the current snapshot already matches the predicate
@@ -1858,7 +2798,7 @@ function waitFor(actorRef, predicate, options) {
1858
2798
  abortListener = () => {
1859
2799
  dispose();
1860
2800
  // XState does not "own" the signal, so we should reject with its reason (if any)
1861
- // eslint-disable-next-line @typescript-eslint/prefer-promise-reject-errors
2801
+ // oxlint-disable-next-line typescript/prefer-promise-reject-errors
1862
2802
  rej(signal.reason);
1863
2803
  };
1864
2804
  signal.addEventListener('abort', abortListener);
@@ -1867,7 +2807,7 @@ function waitFor(actorRef, predicate, options) {
1867
2807
  next: checkEmitted,
1868
2808
  error: err => {
1869
2809
  dispose();
1870
- // eslint-disable-next-line @typescript-eslint/prefer-promise-reject-errors
2810
+ // oxlint-disable-next-line typescript/prefer-promise-reject-errors
1871
2811
  rej(err);
1872
2812
  },
1873
2813
  complete: () => {
@@ -1881,4 +2821,4 @@ function waitFor(actorRef, predicate, options) {
1881
2821
  });
1882
2822
  }
1883
2823
 
1884
- export { SimulatedClock, SpecialTargets, assertEvent, createMachine, createMachineFromConfig, createStateConfig, getInitialMicrosteps, getInitialSnapshot, getMicrosteps, getNextSnapshot, getNextTransitions, initialTransition, isTypeSchema, machineConfigToJSON, mapState, serializeMachine, setup, toPromise, transition, types, waitFor };
2824
+ export { SimulatedClock, SpecialTargets, assertEvent, createFSM, createMachine, createMachineFromConfig, createStateConfig, createSystem, isTypeSchema, machineConfigToJSON, machineVersions, mapState, serializeMachine, setup, toPromise, types, waitFor };