xstate 6.0.0-alpha.2 → 6.0.0-alpha.21

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 (54) hide show
  1. package/README.md +19 -17
  2. package/bin/xstate.js +36 -0
  3. package/dist/{StateMachine-64813f46.cjs.js → StateMachine-1b155670.cjs.js} +182 -40
  4. package/dist/{StateMachine-2dcdb1f5.esm.js → StateMachine-3c17fe3e.esm.js} +182 -40
  5. package/dist/{StateMachine-8d22a79c.development.cjs.js → StateMachine-550ff7f2.development.cjs.js} +193 -36
  6. package/dist/{StateMachine-2ea0a7fa.development.esm.js → StateMachine-e25dd61f.development.esm.js} +193 -36
  7. package/dist/declarations/src/State.d.ts +13 -2
  8. package/dist/declarations/src/StateMachine.d.ts +23 -13
  9. package/dist/declarations/src/actions.d.ts +2 -1
  10. package/dist/declarations/src/actors/promise.d.ts +4 -2
  11. package/dist/declarations/src/createActor.d.ts +9 -11
  12. package/dist/declarations/src/createMachine.d.ts +14 -11
  13. package/dist/declarations/src/createMachineFromConfig.d.ts +61 -28
  14. package/dist/declarations/src/fsm.d.ts +74 -0
  15. package/dist/declarations/src/graph/graph.d.ts +3 -3
  16. package/dist/declarations/src/graph/shortestPaths.d.ts +2 -2
  17. package/dist/declarations/src/graph/simplePaths.d.ts +2 -2
  18. package/dist/declarations/src/index.d.ts +6 -5
  19. package/dist/declarations/src/serialize.d.ts +10 -18
  20. package/dist/declarations/src/setup.d.ts +277 -65
  21. package/dist/declarations/src/spawn.d.ts +3 -3
  22. package/dist/declarations/src/stateUtils.d.ts +4 -1
  23. package/dist/declarations/src/system.d.ts +1 -1
  24. package/dist/declarations/src/transition.d.ts +8 -5
  25. package/dist/declarations/src/transitionActions.d.ts +12 -0
  26. package/dist/declarations/src/types.d.ts +237 -102
  27. package/dist/declarations/src/types.v6.d.ts +132 -58
  28. package/dist/declarations/src/utils.d.ts +4 -3
  29. package/dist/{index-93edd3bd.esm.js → index-3f87408c.esm.js} +936 -1389
  30. package/dist/{index-e61170ba.development.esm.js → index-d39fb138.development.esm.js} +968 -1421
  31. package/dist/{index-9cb3b3a0.cjs.js → index-e694228c.cjs.js} +945 -1394
  32. package/dist/{index-f6ef20d1.development.cjs.js → index-ec7c7a2d.development.cjs.js} +977 -1426
  33. package/dist/xstate-actors.cjs.js +1 -1
  34. package/dist/xstate-actors.development.cjs.js +1 -1
  35. package/dist/xstate-actors.development.esm.js +1 -1
  36. package/dist/xstate-actors.esm.js +1 -1
  37. package/dist/xstate-actors.umd.min.js +1 -1
  38. package/dist/xstate-actors.umd.min.js.map +1 -1
  39. package/dist/xstate-graph.cjs.js +2 -2
  40. package/dist/xstate-graph.development.cjs.js +2 -2
  41. package/dist/xstate-graph.development.esm.js +2 -2
  42. package/dist/xstate-graph.esm.js +2 -2
  43. package/dist/xstate-graph.umd.min.js +1 -1
  44. package/dist/xstate-graph.umd.min.js.map +1 -1
  45. package/dist/xstate.cjs.js +1100 -182
  46. package/dist/xstate.cjs.mjs +3 -5
  47. package/dist/xstate.development.cjs.js +1100 -182
  48. package/dist/xstate.development.cjs.mjs +3 -5
  49. package/dist/xstate.development.esm.js +1101 -181
  50. package/dist/xstate.esm.js +1101 -181
  51. package/dist/xstate.umd.min.js +1 -1
  52. package/dist/xstate.umd.min.js.map +1 -1
  53. package/package.json +6 -2
  54. package/dist/declarations/src/atom.d.ts +0 -81
@@ -1,7 +1,7 @@
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-93edd3bd.esm.js';
2
- export { A as Actor, T as TimeoutError, o as __unsafe_getAllOwnEventDescriptors, r as checkStateIn, c as createActor, h as createAsyncAtom, B as createAsyncLogic, e as createAtom, f as createAtomConfig, w as createCallbackLogic, v as createEmptyActor, y as createEventObservableLogic, C as createListenerLogic, z as createLogic, x as createObservableLogic, j as createReducerAtom, E as createSubscriptionLogic, n as getStateNodes, k as isAtom, l as isMachineSnapshot, D as listenerLogic, q as matchesState, s as pathToStateValue, F as subscriptionLogic, u as toObserver } from './index-93edd3bd.esm.js';
3
- import { S as StateMachine } from './StateMachine-2dcdb1f5.esm.js';
4
- export { S as StateMachine, a as StateNode } from './StateMachine-2dcdb1f5.esm.js';
1
+ import { m as matchesEventDescriptor, t as toArray$1, b as builtInActions, X as XSTATE_INIT, d as deriveDeferredStarts, c as createTransitionEnqueue, a as XSTATE_STOP, r as resolveActionsWithContext, p as parseDelayToMilliseconds, i as isAtomicStateNode, e as createActor, f as macrostep, g as createInitEvent, h as initialMicrostep, j as getProperAncestors } from './index-3f87408c.esm.js';
2
+ export { A as Actor, T as TimeoutError, n as __unsafe_getAllOwnEventDescriptors, q as checkStateIn, e as createActor, C as createAsyncLogic, x as createCallbackLogic, w as createEmptyActor, z as createEventObservableLogic, D as createListenerLogic, B as createLogic, y as createObservableLogic, F as createSubscriptionLogic, l as getStateNodes, v as isBuiltInExecutableAction, k as isMachineSnapshot, E as listenerLogic, o as matchesState, s as pathToStateValue, G as subscriptionLogic, u as toObserver } from './index-3f87408c.esm.js';
3
+ import { S as StateMachine } from './StateMachine-3c17fe3e.esm.js';
4
+ export { S as StateMachine, a as StateNode } from './StateMachine-3c17fe3e.esm.js';
5
5
 
6
6
  /**
7
7
  * Asserts that the given event object is of the specified type or types. Throws
@@ -27,7 +27,7 @@ export { S as StateMachine, a as StateNode } from './StateMachine-2dcdb1f5.esm.j
27
27
  * ```
28
28
  */
29
29
  function assertEvent(event, type) {
30
- const types = toArray(type);
30
+ const types = toArray$1(type);
31
31
  const matches = types.some(descriptor => matchesEventDescriptor(event.type, descriptor));
32
32
  if (!matches) {
33
33
  const typesText = types.length === 1 ? `type matching "${types[0]}"` : `one of types matching "${types.join('", "')}"`;
@@ -89,11 +89,354 @@ function createStateConfig(config) {
89
89
  return config;
90
90
  }
91
91
 
92
+ const emptyImplementations = {
93
+ actions: {},
94
+ actorSources: {},
95
+ guards: {},
96
+ delays: {}
97
+ };
98
+ const emptyExecutableActions = [];
99
+ const emptyRawActions = [];
100
+ const builtInActionSet = new Set(Object.values(builtInActions));
101
+ function toArray(value) {
102
+ return value === undefined ? [] : Array.isArray(value) ? value : [value];
103
+ }
104
+ function resolveContext(context, input) {
105
+ return typeof context === 'function' ? context({
106
+ input
107
+ }) : context ?? {};
108
+ }
109
+ function resolveTransitionContext(context, args) {
110
+ return typeof context === 'function' ? context(args) : context;
111
+ }
112
+ function resolveInput(input, context, event) {
113
+ return typeof input === 'function' ? input({
114
+ context,
115
+ event
116
+ }) : input;
117
+ }
118
+ function mergeContextPatch(context, patch) {
119
+ for (const key of Object.keys(patch)) {
120
+ if (!Object.prototype.hasOwnProperty.call(context, key) || !Object.is(context[key], patch[key])) {
121
+ return {
122
+ ...context,
123
+ ...patch
124
+ };
125
+ }
126
+ }
127
+ return context;
128
+ }
129
+ function createSnapshot(value, context, input, machine, stateInput) {
130
+ return {
131
+ status: 'active',
132
+ output: undefined,
133
+ error: undefined,
134
+ value,
135
+ context,
136
+ input,
137
+ children: {},
138
+ _stateInput: stateInput,
139
+ machine
140
+ };
141
+ }
142
+ function cloneSnapshot(snapshot, value, context, stateInput) {
143
+ return {
144
+ status: snapshot.status,
145
+ output: snapshot.output,
146
+ error: snapshot.error,
147
+ value,
148
+ context,
149
+ input: snapshot.input,
150
+ children: snapshot.children,
151
+ _stateInput: stateInput,
152
+ machine: snapshot.machine
153
+ };
154
+ }
155
+ function stopSnapshot(snapshot) {
156
+ return {
157
+ status: 'stopped',
158
+ output: undefined,
159
+ error: undefined,
160
+ value: snapshot.value,
161
+ context: snapshot.context,
162
+ input: undefined,
163
+ children: snapshot.children,
164
+ _stateInput: snapshot._stateInput,
165
+ machine: snapshot.machine
166
+ };
167
+ }
168
+ function assertNoStringTransitions(config) {
169
+ for (const [stateKey, stateConfig] of Object.entries(config.states)) {
170
+ for (const [eventType, transitionConfig] of Object.entries(stateConfig.on ?? {})) {
171
+ for (const transition of toArray(transitionConfig)) {
172
+ if (typeof transition === 'string') {
173
+ const target = transition;
174
+ throw new Error(`Invalid transition for "${stateKey}.${eventType}": use { target: "${target}" } instead of a string target.`);
175
+ }
176
+ }
177
+ }
178
+ }
179
+ }
180
+ function resolveSimpleEnqueuedActions(rawActions) {
181
+ const executableActions = [];
182
+ for (const action of rawActions) {
183
+ if (!action || typeof action !== 'object' || !('action' in action) || typeof action.action !== 'function' || builtInActionSet.has(action.action) || '_special' in action.action) {
184
+ return undefined;
185
+ }
186
+ executableActions.push({
187
+ type: action.action.name || '(anonymous)',
188
+ params: undefined,
189
+ args: action.args,
190
+ exec: action.args.length ? action.action.bind(null, ...action.args) : action.action
191
+ });
192
+ }
193
+ return executableActions;
194
+ }
195
+ function createFSM(config) {
196
+ const machine = {
197
+ id: config.id ?? '(fsm)',
198
+ implementations: emptyImplementations
199
+ };
200
+ assertNoStringTransitions(config);
201
+ const runActions = (snapshot, event, actorScope, rawActions) => {
202
+ if (!rawActions?.length) {
203
+ return [snapshot, emptyExecutableActions];
204
+ }
205
+ const simpleExecutableActions = resolveSimpleEnqueuedActions(rawActions);
206
+ if (simpleExecutableActions) {
207
+ return [snapshot, simpleExecutableActions];
208
+ }
209
+ return resolveActionsWithContext(snapshot, event, actorScope, rawActions);
210
+ };
211
+ const runStateActions = (snapshot, event, actorScope, actionsConfig, stateInput, internalQueue) => {
212
+ if (!actionsConfig) {
213
+ return [snapshot, emptyExecutableActions];
214
+ }
215
+ const actions = [];
216
+ const enq = createTransitionEnqueue(actorScope, actions, internalQueue, true);
217
+ let context;
218
+ const actionCount = Array.isArray(actionsConfig) ? actionsConfig.length : 1;
219
+ for (let i = 0; i < actionCount; i++) {
220
+ const action = Array.isArray(actionsConfig) ? actionsConfig[i] : actionsConfig;
221
+ const result = action({
222
+ context: context ?? snapshot.context,
223
+ event: event,
224
+ input: stateInput,
225
+ value: snapshot.value,
226
+ self: actorScope.self,
227
+ system: actorScope.system,
228
+ parent: actorScope.self._parent,
229
+ children: snapshot.children
230
+ }, enq);
231
+ if (result?.context !== undefined) {
232
+ const currentContext = context ?? snapshot.context;
233
+ const nextContext = mergeContextPatch(currentContext, result.context);
234
+ if (nextContext !== currentContext) {
235
+ context = nextContext;
236
+ }
237
+ }
238
+ }
239
+ const nextSnapshot = context !== undefined ? cloneSnapshot(snapshot, snapshot.value, context, snapshot._stateInput) : snapshot;
240
+ return runActions(nextSnapshot, event, actorScope, actions);
241
+ };
242
+ const selectTransition = (snapshot, event, actorScope, internalQueue) => {
243
+ const state = config.states[snapshot.value];
244
+ const transitionsConfig = state?.on?.[event.type];
245
+ if (!transitionsConfig) {
246
+ return undefined;
247
+ }
248
+ const transitionCount = Array.isArray(transitionsConfig) ? transitionsConfig.length : 1;
249
+ for (let i = 0; i < transitionCount; i++) {
250
+ const transition = Array.isArray(transitionsConfig) ? transitionsConfig[i] : transitionsConfig;
251
+ const args = {
252
+ context: snapshot.context,
253
+ event,
254
+ input: snapshot.input,
255
+ value: snapshot.value,
256
+ self: actorScope.self,
257
+ system: actorScope.system,
258
+ parent: actorScope.self._parent,
259
+ children: snapshot.children
260
+ };
261
+ if (typeof transition === 'function') {
262
+ const actions = [];
263
+ const enq = createTransitionEnqueue(actorScope, actions, internalQueue, true);
264
+ const result = transition(args, enq);
265
+ if (!result) {
266
+ if (actions.length) {
267
+ return {
268
+ actions
269
+ };
270
+ }
271
+ continue;
272
+ }
273
+ return {
274
+ target: result.target,
275
+ context: result.context,
276
+ input: result.input,
277
+ actions
278
+ };
279
+ }
280
+ if ('guard' in transition && transition.guard && !transition.guard(args)) {
281
+ continue;
282
+ }
283
+ return {
284
+ target: transition.target,
285
+ context: resolveTransitionContext(transition.context, args),
286
+ input: transition.input,
287
+ actions: 'actions' in transition && transition.actions ? toArray(transition.actions) : emptyRawActions
288
+ };
289
+ }
290
+ return undefined;
291
+ };
292
+ const transitionCore = (snapshot, event, actorScope) => {
293
+ if (snapshot.status !== 'active') {
294
+ return [snapshot, []];
295
+ }
296
+ if (event.type === XSTATE_STOP) {
297
+ return [stopSnapshot(snapshot), emptyExecutableActions];
298
+ }
299
+ const stateConfig = config.states[snapshot.value];
300
+ const directTransition = stateConfig?.on?.[event.type];
301
+ if (!directTransition) {
302
+ return [snapshot, emptyExecutableActions];
303
+ }
304
+ if (!Array.isArray(directTransition) && typeof directTransition !== 'function' && !('guard' in directTransition) && !('actions' in directTransition && directTransition.actions) && typeof directTransition.input !== 'function') {
305
+ const target = directTransition.target ?? snapshot.value;
306
+ const stateChanged = target !== snapshot.value;
307
+ if (stateChanged && (stateConfig.exit || config.states[target]?.entry)) ; else {
308
+ const hasContext = directTransition.context !== undefined;
309
+ const hasInput = directTransition.input !== undefined;
310
+ const resolvedContext = resolveTransitionContext(directTransition.context, {
311
+ context: snapshot.context,
312
+ event,
313
+ input: snapshot.input,
314
+ value: snapshot.value,
315
+ self: actorScope.self,
316
+ system: actorScope.system,
317
+ parent: actorScope.self._parent,
318
+ children: snapshot.children
319
+ });
320
+ const context = hasContext && resolvedContext ? mergeContextPatch(snapshot.context, resolvedContext) : snapshot.context;
321
+ if (!stateChanged && context === snapshot.context && !hasInput && snapshot._stateInput === undefined) {
322
+ return [snapshot, emptyExecutableActions];
323
+ }
324
+ return [cloneSnapshot(snapshot, target, context, hasInput ? resolveInput(directTransition.input, context, event) : undefined), emptyExecutableActions];
325
+ }
326
+ }
327
+ if (typeof directTransition === 'function' && directTransition.length < 2 && !stateConfig?.exit) {
328
+ const result = directTransition({
329
+ context: snapshot.context,
330
+ event,
331
+ input: snapshot.input,
332
+ value: snapshot.value,
333
+ self: actorScope.self,
334
+ system: actorScope.system,
335
+ parent: actorScope.self._parent,
336
+ children: snapshot.children
337
+ }, undefined);
338
+ if (result) {
339
+ const target = result.target ?? snapshot.value;
340
+ if (!config.states[target]?.entry) {
341
+ const hasContext = result.context !== undefined;
342
+ const hasInput = result.input !== undefined;
343
+ const context = hasContext && result.context ? mergeContextPatch(snapshot.context, result.context) : snapshot.context;
344
+ if (target === snapshot.value && context === snapshot.context && !hasInput && snapshot._stateInput === undefined) {
345
+ return [snapshot, emptyExecutableActions];
346
+ }
347
+ return [cloneSnapshot(snapshot, target, context, hasInput ? resolveInput(result.input, context, event) : undefined), emptyExecutableActions];
348
+ }
349
+ }
350
+ }
351
+ let nextSnapshot = snapshot;
352
+ const executableActions = [];
353
+ const internalQueue = [event];
354
+ let iterations = 0;
355
+ while (internalQueue.length) {
356
+ if (++iterations > 1000) {
357
+ throw new Error('FSM microstep count exceeded 1000');
358
+ }
359
+ const nextEvent = internalQueue.shift();
360
+ const selected = selectTransition(nextSnapshot, nextEvent, actorScope, internalQueue);
361
+ if (!selected) {
362
+ continue;
363
+ }
364
+ const nextValue = selected.target ?? nextSnapshot.value;
365
+ const stateChanged = nextValue !== nextSnapshot.value;
366
+ if (stateChanged) {
367
+ const [exited, exitActions] = runStateActions(nextSnapshot, nextEvent, actorScope, config.states[nextSnapshot.value]?.exit, nextSnapshot._stateInput, internalQueue);
368
+ nextSnapshot = exited;
369
+ executableActions.push(...exitActions);
370
+ }
371
+ let context = nextSnapshot.context;
372
+ if (selected.context !== undefined) {
373
+ context = mergeContextPatch(context, selected.context);
374
+ }
375
+ const hasInput = selected.input !== undefined;
376
+ const stateInput = hasInput ? resolveInput(selected.input, context, nextEvent) : undefined;
377
+ if (stateChanged || context !== nextSnapshot.context || hasInput || nextSnapshot._stateInput !== undefined) {
378
+ nextSnapshot = cloneSnapshot(nextSnapshot, nextValue, context, stateInput);
379
+ }
380
+ const [afterTransition, transitionActions] = runActions(nextSnapshot, nextEvent, actorScope, selected.actions);
381
+ nextSnapshot = afterTransition;
382
+ executableActions.push(...transitionActions);
383
+ if (stateChanged) {
384
+ const [entered, entryActions] = runStateActions(nextSnapshot, nextEvent, actorScope, config.states[nextValue]?.entry, stateInput, internalQueue);
385
+ nextSnapshot = entered;
386
+ executableActions.push(...entryActions);
387
+ }
388
+ }
389
+ return [nextSnapshot, executableActions];
390
+ };
391
+ const transition = (...args) => {
392
+ const [nextSnapshot, effects] = transitionCore(...args);
393
+ return [nextSnapshot, [...effects, ...deriveDeferredStarts(effects)]];
394
+ };
395
+ const logic = {
396
+ id: config.id,
397
+ config,
398
+ transition,
399
+ initialTransition: (input, actorScope) => {
400
+ const context = resolveContext(config.context, input);
401
+ const snapshot = createSnapshot(config.initial, context, input, machine);
402
+ const internalQueue = [];
403
+ let [nextSnapshot, actions] = runStateActions(snapshot, {
404
+ type: XSTATE_INIT
405
+ }, actorScope, config.states[config.initial]?.entry, undefined, internalQueue);
406
+ if (!actions.length) {
407
+ actions = [];
408
+ }
409
+ while (internalQueue.length) {
410
+ const [raisedSnapshot, raisedActions] = transitionCore(nextSnapshot, internalQueue.shift(), actorScope);
411
+ nextSnapshot = raisedSnapshot;
412
+ actions.push(...raisedActions);
413
+ }
414
+ return [nextSnapshot, [...actions, ...deriveDeferredStarts(actions)]];
415
+ },
416
+ getInitialSnapshot: (actorScope, input) => logic.initialTransition(input, actorScope)[0],
417
+ getPersistedSnapshot: ({
418
+ machine: _,
419
+ ...snapshot
420
+ }) => snapshot,
421
+ restoreSnapshot: snapshot => ({
422
+ ...snapshot,
423
+ machine: machine
424
+ })
425
+ };
426
+ return logic;
427
+ }
428
+
92
429
  function delayToMs(delay) {
93
430
  const parsedDelay = parseDelayToMilliseconds(delay);
94
431
  if (parsedDelay !== undefined) return parsedDelay;
95
432
  return typeof delay === 'string' ? parseFloat(delay) || 0 : delay;
96
433
  }
434
+ function normalizeScxmlEventType(eventType) {
435
+ return /^error(\.|$)/.test(eventType) ? `xstate.${eventType}` : eventType;
436
+ }
437
+ function toScxmlEventName(eventType) {
438
+ return eventType.startsWith('xstate.error.') ? eventType.slice('xstate.'.length) : eventType;
439
+ }
97
440
 
98
441
  /**
99
442
  * Isolated executable-content block. Errors in nested actions stop this block
@@ -101,48 +444,23 @@ function delayToMs(delay) {
101
444
  * separate <onentry>/<onexit> blocks: each is its own block per spec.
102
445
  */
103
446
 
104
- function isUnserializableMarker(value) {
105
- return !!value && typeof value === 'object' && '$unserializable' in value && typeof value.$unserializable === 'string';
447
+ function isExpression(value) {
448
+ return !!value && typeof value === 'object' && typeof value['@expr'] === 'string';
106
449
  }
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
- }
450
+ function isCode(value) {
451
+ return !!value && typeof value === 'object' && typeof value['@code'] === 'string';
129
452
  }
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
- }
453
+ function isResolvable(value) {
454
+ return isExpression(value) || isCode(value);
455
+ }
456
+ function isBuiltInActionType(type) {
457
+ return type.startsWith('@xstate.');
458
+ }
459
+ function isScxmlActionType(type) {
460
+ return type.startsWith('scxml.');
461
+ }
462
+ function toPath(parent, key) {
463
+ return typeof key === 'number' ? `${parent}[${key}]` : `${parent}.${key}`;
146
464
  }
147
465
  function extractSerializableDelays(delays) {
148
466
  const result = {};
@@ -153,6 +471,8 @@ function extractSerializableDelays(delays) {
153
471
  const value = delays[key];
154
472
  if (typeof value === 'number') {
155
473
  result[key] = value;
474
+ } else if (value && typeof value === 'object' && typeof value.duration === 'number') {
475
+ result[key] = value.duration;
156
476
  }
157
477
  }
158
478
  return result;
@@ -161,14 +481,254 @@ function mergeImplementations(json, implementations) {
161
481
  return {
162
482
  actions: implementations.actions ?? {},
163
483
  guards: implementations.guards ?? {},
164
- actors: implementations.actors ?? {},
484
+ actorSources: implementations.actorSources ?? {},
165
485
  delays: {
166
486
  ...extractSerializableDelays(json.delays),
167
487
  ...(implementations.delays ?? {})
488
+ },
489
+ evaluators: implementations.evaluators ?? {}
490
+ };
491
+ }
492
+ function createExpressionResolver(expressionLanguage, implementations) {
493
+ function getEvaluator(value, path) {
494
+ const lang = value['@lang'] ?? expressionLanguage;
495
+ if (!lang) {
496
+ throw new Error(`Missing @exprLang for expression at ${path}`);
497
+ }
498
+ const evaluator = implementations.evaluators[lang];
499
+ if (!evaluator) {
500
+ throw new Error(`Missing evaluator for @lang '${lang}' at ${path}`);
501
+ }
502
+ return evaluator;
503
+ }
504
+ function evaluateResolvable(value, slot, scope, path) {
505
+ const kind = isExpression(value) ? 'expr' : 'code';
506
+ const source = isExpression(value) ? value['@expr'] : value['@code'];
507
+ return getEvaluator(value, path)({
508
+ source,
509
+ kind,
510
+ slot,
511
+ scope,
512
+ path
513
+ });
514
+ }
515
+ function resolveValue(value, slot, scope, path) {
516
+ if (isResolvable(value)) {
517
+ return evaluateResolvable(value, slot, scope, path);
518
+ }
519
+ if (Array.isArray(value)) {
520
+ return value.map((item, index) => resolveValue(item, slot, scope, toPath(path, index)));
521
+ }
522
+ if (!value || typeof value !== 'object') {
523
+ return value;
524
+ }
525
+ const result = {};
526
+ for (const key of Object.keys(value)) {
527
+ result[key] = resolveValue(value[key], slot, scope, toPath(path, key));
168
528
  }
529
+ return result;
530
+ }
531
+ function makeScope(x, extra) {
532
+ return {
533
+ context: x.context,
534
+ event: x.event,
535
+ input: x.input,
536
+ self: x.self,
537
+ children: x.children,
538
+ params: x.params,
539
+ ...extra
540
+ };
541
+ }
542
+ function getDurationConfig(value, path) {
543
+ if (!isResolvable(value)) {
544
+ return value;
545
+ }
546
+ return args => delayToMs(resolveValue(value, 'delay', makeScope(args), path));
547
+ }
548
+ function assertResolvable(value, path) {
549
+ if (isResolvable(value)) {
550
+ getEvaluator(value, path);
551
+ return;
552
+ }
553
+ if (Array.isArray(value)) {
554
+ value.forEach((item, index) => assertResolvable(item, toPath(path, index)));
555
+ return;
556
+ }
557
+ if (!value || typeof value !== 'object') {
558
+ return;
559
+ }
560
+ for (const key of Object.keys(value)) {
561
+ assertResolvable(value[key], toPath(path, key));
562
+ }
563
+ }
564
+ return {
565
+ evaluateResolvable,
566
+ resolveValue,
567
+ makeScope,
568
+ getDurationConfig,
569
+ assertResolvable
169
570
  };
170
571
  }
171
- let _scxmlSessionId = 'session_' + Math.random().toString(36).slice(2);
572
+ function toActionArray(actions) {
573
+ return actions === undefined ? [] : Array.isArray(actions) ? actions : [actions];
574
+ }
575
+ function validateChoiceConfig(choice, path) {
576
+ if (!Array.isArray(choice)) {
577
+ return;
578
+ }
579
+ choice.forEach((branch, index) => {
580
+ if (branch.when === undefined && index !== choice.length - 1) {
581
+ throw new Error(`Choice fallback branch at ${path}[${index}] must be last.`);
582
+ }
583
+ });
584
+ }
585
+ function assertMachineJSON(json, resolvedImplementations, expressionResolver) {
586
+ const {
587
+ assertResolvable
588
+ } = expressionResolver;
589
+ function assertCondition(condition, path) {
590
+ if (!condition) {
591
+ return;
592
+ }
593
+ if (isResolvable(condition)) {
594
+ assertResolvable(condition, path);
595
+ return;
596
+ }
597
+ assertResolvable(condition.params, `${path}.params`);
598
+ if (json.guards?.[condition.type]) {
599
+ assertCondition(json.guards[condition.type].when, `$.guards.${condition.type}.when`);
600
+ return;
601
+ }
602
+ if (!resolvedImplementations.guards[condition.type] && !['scxml.cond', 'xstate.stateIn', 'xstate.not'].includes(condition.type)) {
603
+ throw new Error(`Missing guard implementation "${condition.type}"`);
604
+ }
605
+ }
606
+ function assertAction(action, path, stack = []) {
607
+ if (isResolvable(action)) {
608
+ assertResolvable(action, path);
609
+ return;
610
+ }
611
+ if (!action || typeof action.type !== 'string') {
612
+ throw new Error(`Invalid action at ${path}`);
613
+ }
614
+ assertResolvable(action.params, `${path}.params`);
615
+ if (isBuiltInActionType(action.type) || isScxmlActionType(action.type)) {
616
+ assertResolvable(action, path);
617
+ return;
618
+ }
619
+ const definition = json.actions?.[action.type];
620
+ if (definition) {
621
+ if (stack.includes(action.type)) {
622
+ throw new Error(`Circular action reference: ${stack.concat(action.type).join(' -> ')}`);
623
+ }
624
+ const definitions = Array.isArray(definition) ? definition : [definition];
625
+ definitions.forEach((item, index) => assertAction(item, `${path}.actions.${action.type}${definitions.length > 1 ? `[${index}]` : ''}`, stack.concat(action.type)));
626
+ return;
627
+ }
628
+ if (!resolvedImplementations.actions[action.type]) {
629
+ throw new Error(`Missing action implementation "${action.type}"`);
630
+ }
631
+ }
632
+ function assertActions(actions, path) {
633
+ toActionArray(actions).forEach((action, index) => assertAction(action, `${path}[${index}]`));
634
+ }
635
+ function assertTransition(transition, path) {
636
+ const transitions = Array.isArray(transition) ? transition : transition ? [transition] : [];
637
+ transitions.forEach((t, index) => {
638
+ const transitionPath = Array.isArray(transition) ? `${path}[${index}]` : path;
639
+ if (isResolvable(t)) {
640
+ assertResolvable(t, transitionPath);
641
+ return;
642
+ }
643
+ assertCondition(t.guard, `${transitionPath}.guard`);
644
+ assertActions(t.actions, `${transitionPath}.actions`);
645
+ assertResolvable(t.context, `${transitionPath}.context`);
646
+ assertResolvable(t.input, `${transitionPath}.input`);
647
+ });
648
+ }
649
+ function assertStateNode(node, path) {
650
+ assertResolvable(node.context, `${path}.context`);
651
+ assertResolvable(node.input, `${path}.input`);
652
+ assertResolvable(node.output, `${path}.output`);
653
+ assertResolvable(node.timeout, `${path}.timeout`);
654
+ assertActions(node.entry, `${path}.entry`);
655
+ assertActions(node.exit, `${path}.exit`);
656
+ if (node.type === 'choice') {
657
+ if (!node.choice) {
658
+ throw new Error(`Choice state at ${path} must declare choice.`);
659
+ }
660
+ if (Array.isArray(node.choice)) {
661
+ validateChoiceConfig(node.choice, `${path}.choice`);
662
+ node.choice.forEach((branch, index) => {
663
+ assertCondition(branch.when, `${path}.choice[${index}].when`);
664
+ assertResolvable(branch.context, `${path}.choice[${index}].context`);
665
+ assertResolvable(branch.input, `${path}.choice[${index}].input`);
666
+ });
667
+ } else {
668
+ assertResolvable(node.choice, `${path}.choice`);
669
+ }
670
+ }
671
+ if (isResolvable(node.route)) {
672
+ assertResolvable(node.route, `${path}.route`);
673
+ }
674
+ if (node.invoke) {
675
+ const invokes = Array.isArray(node.invoke) ? node.invoke : [node.invoke];
676
+ invokes.forEach((invoke, index) => {
677
+ const invokePath = `${path}.invoke${Array.isArray(node.invoke) ? `[${index}]` : ''}`;
678
+ const extInv = invoke;
679
+ if (!extInv._nestedMachineJSON && !resolvedImplementations.actorSources[invoke.src]) {
680
+ throw new Error(`Missing actorSource implementation "${invoke.src}"`);
681
+ }
682
+ assertResolvable(invoke.input, `${invokePath}.input`);
683
+ assertResolvable(invoke.timeout, `${invokePath}.timeout`);
684
+ assertTransition(invoke.onDone, `${invokePath}.onDone`);
685
+ assertTransition(invoke.onError, `${invokePath}.onError`);
686
+ assertTransition(invoke.onSnapshot, `${invokePath}.onSnapshot`);
687
+ assertTransition(invoke.onTimeout, `${invokePath}.onTimeout`);
688
+ });
689
+ }
690
+ if (node.on) {
691
+ for (const descriptor of Object.keys(node.on)) {
692
+ assertTransition(node.on[descriptor], `${path}.on.${descriptor}`);
693
+ }
694
+ }
695
+ if (node.after) {
696
+ for (const delay of Object.keys(node.after)) {
697
+ if (Number.isNaN(Number(delay)) && parseDelayToMilliseconds(delay) === undefined && !json.delays?.[delay] && !resolvedImplementations.delays[delay]) {
698
+ throw new Error(`Missing delay implementation "${delay}"`);
699
+ }
700
+ assertTransition(node.after[delay], `${path}.after.${delay}`);
701
+ }
702
+ }
703
+ assertTransition(node.always, `${path}.always`);
704
+ assertTransition(node.onError, `${path}.onError`);
705
+ assertTransition(node.onTimeout, `${path}.onTimeout`);
706
+ if (node.states) {
707
+ for (const key of Object.keys(node.states)) {
708
+ assertStateNode(node.states[key], `${path}.states.${key}`);
709
+ }
710
+ }
711
+ }
712
+ if (json.actions) {
713
+ for (const key of Object.keys(json.actions)) {
714
+ const actions = Array.isArray(json.actions[key]) ? json.actions[key] : [json.actions[key]];
715
+ actions.forEach((action, index) => assertAction(action, `$.actions.${key}${actions.length > 1 ? `[${index}]` : ''}`, [key]));
716
+ }
717
+ }
718
+ if (json.guards) {
719
+ for (const key of Object.keys(json.guards)) {
720
+ assertCondition(json.guards[key].when, `$.guards.${key}.when`);
721
+ }
722
+ }
723
+ if (json.delays) {
724
+ for (const key of Object.keys(json.delays)) {
725
+ const delay = json.delays[key];
726
+ assertResolvable(delay && typeof delay === 'object' && 'duration' in delay ? delay.duration : delay, `$.delays.${key}`);
727
+ }
728
+ }
729
+ assertStateNode(json, '$');
730
+ }
731
+ let _scxmlSessionId = 'session_scxml';
172
732
  let _scxmlMachineName = '';
173
733
 
174
734
  /** Evaluates an SCXML expression with context variables available via `with`. */
@@ -193,7 +753,7 @@ with (context) {
193
753
  }
194
754
  return false;
195
755
  };
196
- const SCXML_ORIGIN = '#_scxml_session';
756
+ const SCXML_ORIGIN = `#_scxml_${_scxmlSessionId}`;
197
757
  const SCXML_ORIGIN_TYPE = 'http://www.w3.org/TR/scxml/#SCXMLEventProcessor';
198
758
  const isExternal = event && event._scxmlExternal;
199
759
  const isInitEvent = event && event.type === '@xstate.init';
@@ -205,7 +765,7 @@ with (context) {
205
765
  const isDoneEvent = event && typeof event.type === 'string' && event.type.startsWith('xstate.done.');
206
766
  const explicitType = event && event._scxmlEventType;
207
767
  const scxmlEvent = event && !isInitEvent ? {
208
- name: event.type,
768
+ name: event._scxmlEventName ?? toScxmlEventName(event.type),
209
769
  type: explicitType || (isExternal ? 'external' : 'internal'),
210
770
  sendid: event._scxmlSendId,
211
771
  origin: isExternal ? SCXML_ORIGIN : undefined,
@@ -241,9 +801,64 @@ return updates;
241
801
  return fn(context, updates);
242
802
  }
243
803
  function createMachineFromConfig(json, implementations = {}) {
244
- assertRootImplementationMarkers(json, implementations);
245
- assertNoUnresolvedMarkers(json);
246
804
  const resolvedImplementations = mergeImplementations(json, implementations);
805
+ const expressionResolver = createExpressionResolver(json['@exprLang'], resolvedImplementations);
806
+ const {
807
+ evaluateResolvable,
808
+ resolveValue,
809
+ makeScope,
810
+ getDurationConfig
811
+ } = expressionResolver;
812
+ function resolveCondition(condition, slot, path) {
813
+ if (!condition) {
814
+ return undefined;
815
+ }
816
+ if (isResolvable(condition)) {
817
+ return args => !!evaluateResolvable(condition, slot, makeScope(args), path);
818
+ }
819
+ return args => {
820
+ const params = resolveValue(condition.params, slot, makeScope(args), `${path}.params`);
821
+ const declarativeGuard = json.guards?.[condition.type];
822
+ if (declarativeGuard) {
823
+ const guard = resolveCondition(declarativeGuard.when, 'guard', `$.guards.${condition.type}.when`);
824
+ return !!guard?.({
825
+ ...args,
826
+ params
827
+ });
828
+ }
829
+ const guardImpl = args.guards?.[condition.type];
830
+ if (!guardImpl) {
831
+ throw new Error(getMissingGuardMessage(condition.type, args.guards ?? {}));
832
+ }
833
+ return guardImpl(args, params);
834
+ };
835
+ }
836
+ function makeChoiceConfig(choice, path) {
837
+ if (!choice) {
838
+ return undefined;
839
+ }
840
+ if (isResolvable(choice)) {
841
+ return args => evaluateResolvable(choice, 'choice', makeScope(args), path);
842
+ }
843
+ validateChoiceConfig(choice, path);
844
+ return args => {
845
+ for (let index = 0; index < choice.length; index++) {
846
+ const branch = choice[index];
847
+ const guard = resolveCondition(branch.when, 'choice', `${path}[${index}].when`);
848
+ if (!guard || guard(args)) {
849
+ return {
850
+ target: branch.target,
851
+ context: branch.context ? resolveValue(branch.context, 'transitionContext', makeScope(args), `${path}[${index}].context`) : undefined,
852
+ input: branch.input !== undefined ? resolveValue(branch.input, 'input', makeScope(args), `${path}[${index}].input`) : undefined,
853
+ description: branch.description,
854
+ reenter: branch.reenter,
855
+ meta: branch.meta
856
+ };
857
+ }
858
+ }
859
+ throw new Error(`Choice state at ${path} did not match any branch.`);
860
+ };
861
+ }
247
862
 
248
863
  // Pending transition actions: set by .to functions, consumed by entry functions.
249
864
  // This bridges SCXML's exit→transition→entry action ordering with XState's
@@ -263,8 +878,30 @@ function createMachineFromConfig(json, implementations = {}) {
263
878
  // <transition cond>). Drained by the next state's entry into the internal
264
879
  // event queue so they're processed before any external events.
265
880
  const pendingPlatformErrors = [];
881
+ function getMissingGuardMessage(type, guards) {
882
+ return `Guard '${type}' is not implemented in machine '${json.id ?? '(machine)'}'. Available guards: ${Object.keys(guards).map(key => `'${key}'`).join(', ') || '(none)'}.`;
883
+ }
884
+ function resolveRouteConfig(route, path) {
885
+ if (!route || typeof route === 'function') {
886
+ return route;
887
+ }
888
+ if (isResolvable(route)) {
889
+ return args => evaluateResolvable(route, 'transition', makeScope(args), path);
890
+ }
891
+ const {
892
+ guard,
893
+ ...routeConfig
894
+ } = route;
895
+ if (!guard) {
896
+ return routeConfig;
897
+ }
898
+ const resolvedGuard = resolveCondition({
899
+ type: guard
900
+ }, 'guard', path);
901
+ return args => resolvedGuard(args) ? routeConfig : undefined;
902
+ }
266
903
  function iterNode(node, nodeKey) {
267
- const originalEntryActions = node.entry;
904
+ const originalEntryActions = toActionArray(node.entry);
268
905
  const stateId = node.id || nodeKey;
269
906
 
270
907
  // Wrap entry to first execute any pending transition actions, then normal entry.
@@ -278,8 +915,10 @@ function createMachineFromConfig(json, implementations = {}) {
278
915
  while (pendingPlatformErrors.length) {
279
916
  const err = pendingPlatformErrors.shift();
280
917
  enq.raise({
281
- type: 'error.execution',
918
+ type: 'xstate.error.execution',
919
+ error: err,
282
920
  _scxmlEventType: 'platform',
921
+ _scxmlEventName: 'error.execution',
283
922
  _scxmlEventData: err
284
923
  });
285
924
  }
@@ -327,7 +966,7 @@ function createMachineFromConfig(json, implementations = {}) {
327
966
  }
328
967
 
329
968
  // Execute normal entry actions
330
- if (originalEntryActions?.length) {
969
+ if (originalEntryActions.length) {
331
970
  const mergedX = context ? {
332
971
  ...x,
333
972
  context
@@ -350,7 +989,7 @@ function createMachineFromConfig(json, implementations = {}) {
350
989
  description: node.description,
351
990
  tags: node.tags,
352
991
  input: node.input,
353
- timeout: node.timeout,
992
+ timeout: getDurationConfig(node.timeout, `$.states.${nodeKey}.timeout`),
354
993
  states: node.states ? Object.entries(node.states).reduce((acc, [key, value]) => {
355
994
  acc[key] = iterNode(value, key);
356
995
  return acc;
@@ -360,18 +999,27 @@ function createMachineFromConfig(json, implementations = {}) {
360
999
  return acc;
361
1000
  }, {}) : undefined,
362
1001
  always: node.always ? getTransitionConfig(node.always) : undefined,
363
- choice: node.choice ? getTransitionConfig(node.choice) : undefined,
364
- route: node.route,
1002
+ onError: node.onError ? getTransitionConfig(node.onError) : undefined,
1003
+ choice: makeChoiceConfig(node.choice, `$.states.${nodeKey}.choice`),
1004
+ route: resolveRouteConfig(node.route, `$.states.${nodeKey}.route`),
365
1005
  after: node.after ? Object.entries(node.after).reduce((acc, [key, value]) => {
366
1006
  acc[key] = getTransitionConfig(value);
367
1007
  return acc;
368
1008
  }, {}) : undefined,
369
1009
  onTimeout: node.onTimeout ? getTransitionConfig(node.onTimeout) : undefined,
370
- entry: entryFn,
371
- exit: node.exit ? iterActions(node.exit) : undefined,
1010
+ entry: node.type === 'choice' ? undefined : entryFn,
1011
+ exit: node.exit ? iterActions(toActionArray(node.exit)) : undefined,
372
1012
  invoke: node.invoke ? iterInvokeConfigs(node.invoke) : undefined,
373
1013
  meta: node.meta,
374
- output: node._scxmlDonedata ? makeDonedataOutput(node._scxmlDonedata) : node.output
1014
+ output: node._scxmlDonedata ? makeDonedataOutput(node._scxmlDonedata) : isResolvable(node.output) ? ({
1015
+ context,
1016
+ event,
1017
+ self
1018
+ }) => evaluateResolvable(node.output, 'output', {
1019
+ context,
1020
+ event,
1021
+ self
1022
+ }, `$.states.${nodeKey}.output`) : node.output
375
1023
  };
376
1024
  return nodeConfig;
377
1025
  }
@@ -424,23 +1072,18 @@ function createMachineFromConfig(json, implementations = {}) {
424
1072
  let src;
425
1073
  if (extInv._nestedMachineJSON) {
426
1074
  src = createMachineFromConfig(extInv._nestedMachineJSON, resolvedImplementations);
427
- } else if (isUnserializableMarker(inv.src)) {
428
- src = inv.src.id ? resolvedImplementations.actors[inv.src.id] : undefined;
429
- if (!src) {
430
- throw new Error(`Missing actors.${inv.src.id ?? ''}`);
431
- }
432
1075
  } else {
433
- src = inv.src;
1076
+ src = resolvedImplementations.actorSources[inv.src] ?? inv.src;
434
1077
  }
435
1078
  return {
436
1079
  src,
437
1080
  id: inv.id,
438
- systemId: inv.systemId,
439
- input: inv.input,
1081
+ registryKey: inv.registryKey,
1082
+ input: inv.input !== undefined ? args => resolveValue(inv.input, 'input', makeScope(args), '$.invoke.input') : undefined,
440
1083
  onDone: inv.onDone ? getTransitionConfig(inv.onDone) : undefined,
441
1084
  onError: inv.onError ? getTransitionConfig(inv.onError) : undefined,
442
1085
  onSnapshot: inv.onSnapshot ? getTransitionConfig(inv.onSnapshot) : undefined,
443
- timeout: inv.timeout,
1086
+ timeout: getDurationConfig(inv.timeout, '$.invoke.timeout'),
444
1087
  onTimeout: inv.onTimeout ? getTransitionConfig(inv.onTimeout) : undefined
445
1088
  };
446
1089
  });
@@ -454,8 +1097,16 @@ function createMachineFromConfig(json, implementations = {}) {
454
1097
  function raiseErrorExecution(state, tagname, err) {
455
1098
  const message = err instanceof Error ? err.message : typeof err === 'string' ? err : 'unknown error';
456
1099
  state.enq.raise({
457
- type: 'error.execution',
1100
+ type: 'xstate.error.execution',
1101
+ error: {
1102
+ tagname,
1103
+ message,
1104
+ line: NaN,
1105
+ column: NaN,
1106
+ reason: message
1107
+ },
458
1108
  _scxmlEventType: 'platform',
1109
+ _scxmlEventName: 'error.execution',
459
1110
  _scxmlEventData: {
460
1111
  tagname,
461
1112
  message,
@@ -466,8 +1117,31 @@ function createMachineFromConfig(json, implementations = {}) {
466
1117
  });
467
1118
  state.errored = true;
468
1119
  }
1120
+ function executeActionDefinition(action, x, enq, params, parentState, stack) {
1121
+ const definition = json.actions?.[action.type];
1122
+ if (!definition) {
1123
+ enq(x.actions[action.type], params);
1124
+ return {
1125
+ context: undefined,
1126
+ errored: parentState.errored
1127
+ };
1128
+ }
1129
+ if (stack.includes(action.type)) {
1130
+ raiseErrorExecution(parentState, 'action', new Error(`Circular action reference: ${stack.concat(action.type).join(' -> ')}`));
1131
+ return {
1132
+ context: undefined,
1133
+ errored: true
1134
+ };
1135
+ }
1136
+ const definitions = Array.isArray(definition) ? definition : [definition];
1137
+ return executeActions(definitions, {
1138
+ ...x,
1139
+ params
1140
+ }, enq, parentState, stack.concat(action.type));
1141
+ }
1142
+
469
1143
  /** Execute an array of SCXML action JSON descriptors with context and enqueue. */
470
- function executeActions(actions, x, enq, parentState) {
1144
+ function executeActions(actions, x, enq, parentState, stack = []) {
471
1145
  const state = parentState ?? {
472
1146
  enq,
473
1147
  errored: false
@@ -475,22 +1149,34 @@ function createMachineFromConfig(json, implementations = {}) {
475
1149
  let context;
476
1150
  for (const action of actions) {
477
1151
  if (state.errored) break;
478
- if (isBuiltInActionJSON(action)) {
1152
+ if (isResolvable(action)) {
1153
+ const result = evaluateResolvable(action, 'action', makeScope(x, {
1154
+ params: x.params,
1155
+ enq
1156
+ }), '$.actions');
1157
+ if (result && typeof result === 'object' && 'context' in result && result.context) {
1158
+ context ??= {};
1159
+ Object.assign(context, result.context);
1160
+ }
1161
+ } else if (isBuiltInActionJSON(action)) {
479
1162
  switch (action.type) {
480
1163
  case '@xstate.raise':
481
1164
  {
482
1165
  // Tag as external if it has a delay (from <send>, not <raise>).
483
1166
  // Attach _scxmlSendId so _event.sendid can be read in expressions.
484
1167
  const isExternal = action.delay !== undefined;
1168
+ const resolvedEvent = resolveValue(action.event, 'actionParams', makeScope(x, {
1169
+ params: x.params
1170
+ }), '$.actions.event');
485
1171
  const event = isExternal || action.id !== undefined ? {
486
- ...action.event,
1172
+ ...resolvedEvent,
487
1173
  ...(isExternal ? {
488
1174
  _scxmlExternal: true
489
1175
  } : {}),
490
1176
  ...(action.id !== undefined ? {
491
1177
  _scxmlSendId: action.id
492
1178
  } : {})
493
- } : action.event;
1179
+ } : resolvedEvent;
494
1180
  enq.raise(event, {
495
1181
  id: action.id,
496
1182
  delay: action.delay
@@ -504,11 +1190,15 @@ function createMachineFromConfig(json, implementations = {}) {
504
1190
  enq.log(...action.args);
505
1191
  break;
506
1192
  case '@xstate.emit':
507
- enq.emit(action.event);
1193
+ enq.emit(resolveValue(action.event, 'actionParams', makeScope(x, {
1194
+ params: x.params
1195
+ }), '$.actions.event'));
508
1196
  break;
509
1197
  case '@xstate.assign':
510
1198
  context ??= {};
511
- Object.assign(context, action.context);
1199
+ Object.assign(context, resolveValue(action.context, 'transitionContext', makeScope(x, {
1200
+ params: x.params
1201
+ }), '$.actions.context'));
512
1202
  break;
513
1203
  default:
514
1204
  throw new Error(`Unknown built-in action: ${action.type}`);
@@ -547,8 +1237,10 @@ function createMachineFromConfig(json, implementations = {}) {
547
1237
  let delay;
548
1238
  try {
549
1239
  eventType = scxmlAction.eventexpr ? evaluateExpr(mergedContext, scxmlAction.eventexpr, x.event, x.self) : scxmlAction.event || 'unknown';
1240
+ eventType = normalizeScxmlEventType(eventType);
550
1241
  eventData = {
551
- type: eventType
1242
+ type: eventType,
1243
+ _scxmlEventName: toScxmlEventName(eventType)
552
1244
  };
553
1245
  if (scxmlAction.params) {
554
1246
  for (const param of scxmlAction.params) {
@@ -588,25 +1280,50 @@ function createMachineFromConfig(json, implementations = {}) {
588
1280
  const childRef = x.children?.[childId];
589
1281
  if (childRef) {
590
1282
  enq.sendTo(childRef, eventData);
1283
+ } else if (childId.startsWith('scxml_') && childId !== `scxml_${_scxmlSessionId}`) {
1284
+ const message = `Unable to dispatch event to target: ${target}`;
1285
+ enq.raise({
1286
+ type: 'xstate.error.communication',
1287
+ error: {
1288
+ tagname: 'send',
1289
+ message,
1290
+ line: NaN,
1291
+ column: NaN,
1292
+ reason: message
1293
+ },
1294
+ _scxmlEventType: 'platform',
1295
+ _scxmlEventName: 'error.communication',
1296
+ _scxmlEventData: {
1297
+ tagname: 'send',
1298
+ message,
1299
+ line: NaN,
1300
+ column: NaN,
1301
+ reason: message
1302
+ }
1303
+ });
591
1304
  } else {
592
- // Not a known child (e.g. #_scxml_sessionid) → self-raise
593
- if (delay !== undefined) {
594
- eventData._scxmlExternal = true;
595
- }
596
- enq.raise(eventData, {
1305
+ // This SCXML session id uses the external event queue.
1306
+ eventData._scxmlExternal = true;
1307
+ enq.sendTo(x.self, eventData, {
597
1308
  id: scxmlAction.id,
598
1309
  delay
599
1310
  });
600
1311
  }
601
1312
  } else {
602
- // Self-send or no special target: raise as external event
603
- if (delay !== undefined) {
1313
+ // #_internal uses the internal queue; self-send/no target use the
1314
+ // external event queue.
1315
+ if (isInternalTarget) {
1316
+ enq.raise(eventData, {
1317
+ id: scxmlAction.id,
1318
+ delay
1319
+ });
1320
+ } else {
604
1321
  eventData._scxmlExternal = true;
1322
+ enq.sendTo(x.self, eventData, {
1323
+ id: scxmlAction.id,
1324
+ delay
1325
+ });
605
1326
  }
606
- enq.raise(eventData, {
607
- id: scxmlAction.id,
608
- delay
609
- });
610
1327
  }
611
1328
  } else if (action.type === 'scxml.cancel') {
612
1329
  const scxmlAction = action;
@@ -736,7 +1453,14 @@ function createMachineFromConfig(json, implementations = {}) {
736
1453
  }
737
1454
  }
738
1455
  } else {
739
- enq(x.actions[action.type], action.params);
1456
+ const params = resolveValue(action.params, 'actionParams', makeScope(x, {
1457
+ params: x.params
1458
+ }), '$.actions.params');
1459
+ const result = executeActionDefinition(action, x, enq, params, state, stack);
1460
+ if (result.context) {
1461
+ context ??= {};
1462
+ Object.assign(context, result.context);
1463
+ }
740
1464
  }
741
1465
  }
742
1466
  return {
@@ -757,30 +1481,63 @@ function createMachineFromConfig(json, implementations = {}) {
757
1481
  // a separate XState transition with its own guard and optional .to.
758
1482
  // This ensures guards are evaluated by XState's evaluateCandidate (once,
759
1483
  // with pre-exit context) and NOT re-evaluated in computeEntrySet.
760
- return transitions.map(t => {
1484
+ return transitions.map((t, index) => {
1485
+ if (isResolvable(t)) {
1486
+ return (x, enq) => evaluateResolvable(t, 'transition', makeScope(x, {
1487
+ enq
1488
+ }), `$.transition${transitions.length > 1 ? `[${index}]` : ''}`);
1489
+ }
761
1490
  const target = Array.isArray(t.target) ? t.target[0] : t.target;
762
1491
  const targetConfig = t.target;
1492
+ const guard = resolveCondition(t.guard, 'guard', '$.transition.guard');
1493
+ const resolveTransitionContext = x => t.context ? resolveValue(t.context, 'transitionContext', makeScope(x), '$.transition.context') : undefined;
1494
+ const resolveTransitionInput = x => t.input !== undefined ? resolveValue(t.input, 'input', makeScope(x), '$.transition.input') : undefined;
763
1495
 
764
1496
  // No guard and no actions: simple static config
765
- if (!t.guard && !t.actions?.length) {
1497
+ if (!guard && !t.actions?.length) {
1498
+ if (t.context) {
1499
+ return {
1500
+ to: x => ({
1501
+ target: targetConfig,
1502
+ context: resolveTransitionContext(x),
1503
+ reenter: t.reenter
1504
+ }),
1505
+ description: t.description,
1506
+ meta: t.meta,
1507
+ input: t.input !== undefined ? resolveTransitionInput : undefined
1508
+ };
1509
+ }
766
1510
  return {
767
1511
  target: targetConfig,
768
1512
  description: t.description,
769
1513
  reenter: t.reenter,
770
1514
  meta: t.meta,
771
- input: t.input
1515
+ input: t.input !== undefined ? resolveTransitionInput : undefined
772
1516
  };
773
1517
  }
774
1518
 
775
1519
  // Guard but no actions: static config with guard
776
- if (t.guard && !t.actions?.length) {
1520
+ if (guard && !t.actions?.length) {
1521
+ if (t.context) {
1522
+ return {
1523
+ guard,
1524
+ to: x => ({
1525
+ target: targetConfig,
1526
+ context: resolveTransitionContext(x),
1527
+ reenter: t.reenter
1528
+ }),
1529
+ description: t.description,
1530
+ meta: t.meta,
1531
+ input: t.input !== undefined ? resolveTransitionInput : undefined
1532
+ };
1533
+ }
777
1534
  return {
778
1535
  target: targetConfig,
779
- guard: t.guard,
1536
+ guard,
780
1537
  description: t.description,
781
1538
  reenter: t.reenter,
782
1539
  meta: t.meta,
783
- input: t.input
1540
+ input: t.input !== undefined ? resolveTransitionInput : undefined
784
1541
  };
785
1542
  }
786
1543
 
@@ -789,12 +1546,13 @@ function createMachineFromConfig(json, implementations = {}) {
789
1546
  // For parallel (entries exist), first entry re-executes all in document order.
790
1547
  if (!target) {
791
1548
  return {
792
- guard: t.guard,
1549
+ guard,
793
1550
  description: t.description,
794
1551
  reenter: t.reenter,
795
1552
  meta: t.meta,
796
- input: t.input,
1553
+ input: t.input !== undefined ? resolveTransitionInput : undefined,
797
1554
  to: (x, enq) => {
1555
+ const context = resolveTransitionContext(x);
798
1556
  if (t.actions?.length) {
799
1557
  // Track for parallel re-execution (dedup by reference)
800
1558
  if (!allTransitionActions.includes(t.actions)) {
@@ -806,11 +1564,16 @@ function createMachineFromConfig(json, implementations = {}) {
806
1564
  const result = executeActions(t.actions, x, enq);
807
1565
  if (result.context) {
808
1566
  return {
809
- context: result.context
1567
+ context: {
1568
+ ...context,
1569
+ ...result.context
1570
+ }
810
1571
  };
811
1572
  }
812
1573
  }
813
- return {};
1574
+ return context ? {
1575
+ context
1576
+ } : {};
814
1577
  }
815
1578
  };
816
1579
  }
@@ -820,12 +1583,13 @@ function createMachineFromConfig(json, implementations = {}) {
820
1583
  // already validated it. The .to just stores actions for entry to execute.
821
1584
  // Map by target state ID so parallel transitions each get their own actions.
822
1585
  return {
823
- guard: t.guard,
1586
+ guard,
824
1587
  description: t.description,
825
1588
  reenter: t.reenter,
826
1589
  meta: t.meta,
827
- input: t.input,
1590
+ input: t.input !== undefined ? resolveTransitionInput : undefined,
828
1591
  to: (_x, _enq) => {
1592
+ const context = resolveTransitionContext(_x);
829
1593
  if (t.actions?.length) {
830
1594
  const targetId = target.replace(/^#/, '');
831
1595
  pendingTransitionActionsMap[targetId] = t.actions;
@@ -836,18 +1600,32 @@ function createMachineFromConfig(json, implementations = {}) {
836
1600
  }
837
1601
  return {
838
1602
  target: targetConfig,
1603
+ context,
839
1604
  reenter: t.reenter
840
1605
  };
841
1606
  }
842
1607
  };
843
1608
  });
844
1609
  }
1610
+ if (json.delays) {
1611
+ for (const key of Object.keys(json.delays)) {
1612
+ const delay = json.delays[key];
1613
+ if (typeof delay === 'number') {
1614
+ resolvedImplementations.delays[key] = delay;
1615
+ } else if (typeof delay === 'string') {
1616
+ resolvedImplementations.delays[key] = delayToMs(delay);
1617
+ } else if (delay && typeof delay === 'object') {
1618
+ resolvedImplementations.delays[key] = args => delayToMs(resolveValue(delay.duration, 'delay', makeScope(args), `$.delays.${key}.duration`));
1619
+ }
1620
+ }
1621
+ }
1622
+ assertMachineJSON(json, resolvedImplementations, expressionResolver);
845
1623
  const rootNodeConfig = iterNode(json);
846
1624
  const contextConfig = json.context ? {
847
- context: json.context
1625
+ context: args => resolveValue(json.context, 'context', args, '$.context')
848
1626
  } : {};
849
1627
  _scxmlMachineName = json.id || '';
850
- _scxmlSessionId = 'session_' + Math.random().toString(36).slice(2);
1628
+ _scxmlSessionId = 'session_scxml';
851
1629
  const machine = createMachine({
852
1630
  ...rootNodeConfig,
853
1631
  ...contextConfig,
@@ -892,7 +1670,7 @@ function createMachineFromConfig(json, implementations = {}) {
892
1670
  },
893
1671
  'xstate.not': (args, params) => {
894
1672
  const inner = params?.guard;
895
- const innerImpl = inner && providedGuards[inner.type];
1673
+ const innerImpl = inner && args.guards?.[inner.type];
896
1674
  if (!innerImpl) {
897
1675
  throw new Error(`Guard '${inner?.type}' referenced by 'xstate.not' is not implemented.`);
898
1676
  }
@@ -901,7 +1679,7 @@ function createMachineFromConfig(json, implementations = {}) {
901
1679
  };
902
1680
  const provided = machine.provide({
903
1681
  actions: resolvedImplementations.actions,
904
- actors: resolvedImplementations.actors,
1682
+ actorSources: resolvedImplementations.actorSources,
905
1683
  guards: {
906
1684
  ...providedGuards,
907
1685
  ...resolvedImplementations.guards
@@ -915,7 +1693,7 @@ function createMachineFromConfig(json, implementations = {}) {
915
1693
  return provided;
916
1694
  }
917
1695
  function isBuiltInActionJSON(action) {
918
- return action.type.startsWith('@xstate.');
1696
+ return !isResolvable(action) && action.type.startsWith('@xstate.');
919
1697
  }
920
1698
 
921
1699
  /**
@@ -923,24 +1701,17 @@ function isBuiltInActionJSON(action) {
923
1701
  *
924
1702
  * `machineConfigToJSON` converts a machine config into a JSON-safe structure
925
1703
  * (the `MachineJSON` shape accepted by `createMachineFromConfig`). The boundary
926
- * between serializable structure and runtime implementations is explicit: any
927
- * value that cannot be represented as data (inline functions, actor logic,
928
- * runtime schemas) is replaced with an `{ "$unserializable": <kind> }` marker
929
- * rather than silently dropped.
930
- *
931
- * A machine definition is fully portable iff its JSON contains no
932
- * `$unserializable` markers; reviving a definition that has markers requires
933
- * re-providing those implementations (e.g. via `machine.provide(...)` or the
934
- * `actions`/`guards`/`actors` maps on `createMachineFromConfig` revival).
1704
+ * between serializable structure and runtime implementations is explicit:
1705
+ * functions are represented as `@code` source objects, while actor logic,
1706
+ * runtime schemas, and other non-data runtime values are omitted.
935
1707
  */
936
1708
 
937
1709
  /**
938
1710
  * Returns the JSON-serializable definition of a machine.
939
1711
  *
940
- * Inline functions, actor logic, and runtime schemas cannot be represented as
941
- * data; they appear as `{ "$unserializable": ... }` markers. A machine created
942
- * via `createMachineFromConfig` returns its original JSON config (lossless
943
- * round-trip):
1712
+ * Inline functions are represented as `{ "@code": string, "@lang": "ts" }`. A
1713
+ * machine created via `createMachineFromConfig` returns its original JSON
1714
+ * config (lossless round-trip):
944
1715
  *
945
1716
  * ```ts
946
1717
  * import { serializeMachine, createMachineFromConfig } from 'xstate';
@@ -955,48 +1726,57 @@ function serializeMachine(machine) {
955
1726
  return machine._json ?? machineConfigToJSON(machine.config);
956
1727
  }
957
1728
 
958
- /**
959
- * State-node config keys that are part of the serializable definition. Keys
960
- * outside this list (unknown/internal properties) are dropped.
961
- */
962
- const STATE_NODE_KEYS = ['id', 'key', 'type', 'history', 'description', 'target', 'version', 'initial', 'context', 'on', 'after', 'always', 'choice', 'route', 'onTimeout', 'timeout', 'entry', 'exit', 'meta', 'output', 'tags', 'input', '_scxmlDonedata'];
963
- function marker(kind, id) {
964
- const m = {
965
- $unserializable: kind
1729
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
1730
+ function codeExpression(fn) {
1731
+ return {
1732
+ '@code': fn.toString(),
1733
+ '@lang': 'ts'
966
1734
  };
967
- if (id) {
968
- m.id = id;
969
- }
970
- return m;
1735
+ }
1736
+ function isActorLogic(value) {
1737
+ return !!value && typeof value === 'object' && typeof value.transition === 'function' && typeof value.getInitialSnapshot === 'function';
1738
+ }
1739
+ function isRuntimeSchema(value) {
1740
+ return !!value && typeof value === 'object' && '~standard' in value;
971
1741
  }
972
1742
 
973
- /** JSON-safe deep copy of a plain value; functions become markers. */
1743
+ /** JSON-safe deep copy of a plain value; functions become code expressions. */
974
1744
  function valueToJSON(value) {
975
1745
  if (typeof value === 'function') {
976
- return marker('function', value.name || undefined);
1746
+ return codeExpression(value);
977
1747
  }
978
1748
  if (value === null || typeof value !== 'object') {
979
- return typeof value === 'bigint' || typeof value === 'symbol' ? marker('value') : value;
1749
+ return typeof value === 'bigint' || typeof value === 'symbol' ? undefined : value;
1750
+ }
1751
+ if (isActorLogic(value)) {
1752
+ return undefined;
1753
+ }
1754
+ if (isRuntimeSchema(value)) {
1755
+ return undefined;
980
1756
  }
981
1757
  if (Array.isArray(value)) {
982
- return value.map(valueToJSON);
1758
+ return value.map(valueToJSON).filter(item => item !== undefined);
983
1759
  }
984
1760
  if (value.constructor !== Object && value.constructor !== undefined) {
985
1761
  // Class instances (actor logic, schemas, dates, ...) are not portable.
986
- return marker('value');
1762
+ return undefined;
987
1763
  }
988
1764
  const result = {};
989
1765
  for (const key of Object.keys(value)) {
990
1766
  const v = value[key];
991
1767
  if (v !== undefined) {
992
- result[key] = valueToJSON(v);
1768
+ const jsonValue = valueToJSON(v);
1769
+ if (jsonValue !== undefined) {
1770
+ result[key] = jsonValue;
1771
+ }
993
1772
  }
994
1773
  }
995
1774
  return result;
996
1775
  }
997
1776
  function invokeToJSON(invoke) {
998
1777
  if (Array.isArray(invoke)) {
999
- return invoke.map(invokeToJSON);
1778
+ const values = invoke.map(invokeToJSON).filter(value => value !== undefined);
1779
+ return values.length ? values : undefined;
1000
1780
  }
1001
1781
  const def = invoke;
1002
1782
  const result = {};
@@ -1005,37 +1785,47 @@ function invokeToJSON(invoke) {
1005
1785
  if (value === undefined) {
1006
1786
  continue;
1007
1787
  }
1008
- result[key] = key === 'src' && typeof value !== 'string' ?
1009
- // Actor logic keeps its dedicated marker kind so revival knows the
1010
- // contract is an actor, not a plain function.
1011
- marker('actor', value.id) : valueToJSON(value);
1788
+ result[key] = key === 'src' && typeof value !== 'string' ? value.id : valueToJSON(value);
1789
+ if (result[key] === undefined) {
1790
+ delete result[key];
1791
+ }
1012
1792
  }
1013
- return result;
1793
+ return result.src === undefined ? undefined : result;
1014
1794
  }
1015
- function implementationsToJSON(map, kind) {
1795
+ function implementationsToJSON(map) {
1016
1796
  if (!map) {
1017
1797
  return undefined;
1018
1798
  }
1019
- // Keys are preserved — they are the contract a revived machine must
1020
- // fulfill via provide() — while values become markers (or stay, for
1021
- // JSON-safe values like numeric delays).
1022
1799
  const result = {};
1023
1800
  for (const key of Object.keys(map)) {
1024
1801
  const value = map[key];
1025
- result[key] = typeof value === 'function' ? marker('function', key) : value && typeof value === 'object' ? marker(kind, key) : value;
1802
+ if (typeof value === 'function') {
1803
+ result[key] = undefined;
1804
+ } else {
1805
+ result[key] = valueToJSON(value);
1806
+ }
1807
+ if (result[key] === undefined) {
1808
+ delete result[key];
1809
+ }
1026
1810
  }
1027
1811
  return result;
1028
1812
  }
1029
1813
  function stateNodeConfigToJSON(config) {
1030
1814
  const result = {};
1031
- for (const key of STATE_NODE_KEYS) {
1815
+ for (const key of Object.keys(config)) {
1816
+ if (key === 'states' || key === 'invoke') {
1817
+ continue;
1818
+ }
1032
1819
  const value = config[key];
1033
1820
  if (value !== undefined) {
1034
1821
  result[key] = valueToJSON(value);
1035
1822
  }
1036
1823
  }
1037
1824
  if (config.invoke !== undefined) {
1038
- result.invoke = invokeToJSON(config.invoke);
1825
+ const invoke = invokeToJSON(config.invoke);
1826
+ if (invoke !== undefined) {
1827
+ result.invoke = invoke;
1828
+ }
1039
1829
  }
1040
1830
  if (config.states) {
1041
1831
  const states = {};
@@ -1049,7 +1839,7 @@ function stateNodeConfigToJSON(config) {
1049
1839
 
1050
1840
  /**
1051
1841
  * Converts a machine config (as passed to `createMachine`) to its JSON-safe
1052
- * definition. See module docs for the `$unserializable` marker contract.
1842
+ * definition.
1053
1843
  */
1054
1844
  function machineConfigToJSON(config) {
1055
1845
  const result = stateNodeConfigToJSON(config);
@@ -1062,16 +1852,24 @@ function machineConfigToJSON(config) {
1062
1852
  const value = config.schemas[key];
1063
1853
  if (value && typeof value === 'object' && !('~standard' in value)) {
1064
1854
  // Map-form schemas (events/emitted): preserve event-type keys.
1065
- schemas[key] = implementationsToJSON(value, 'schema');
1855
+ schemas[key] = implementationsToJSON(value);
1066
1856
  } else {
1067
- schemas[key] = marker('schema', key);
1857
+ schemas[key] = valueToJSON(value);
1858
+ }
1859
+ if (schemas[key] === undefined) {
1860
+ delete schemas[key];
1068
1861
  }
1069
1862
  }
1070
1863
  result.schemas = schemas;
1071
1864
  }
1072
- for (const key of ['actions', 'guards', 'actors', 'delays']) {
1865
+ for (const key of ['actions', 'guards', 'actorSources', 'delays']) {
1073
1866
  if (config[key]) {
1074
- result[key] = implementationsToJSON(config[key], key === 'actors' ? 'actor' : 'function');
1867
+ const value = implementationsToJSON(config[key]);
1868
+ if (value && Object.keys(value).length) {
1869
+ result[key] = value;
1870
+ } else {
1871
+ delete result[key];
1872
+ }
1075
1873
  }
1076
1874
  }
1077
1875
  return result;
@@ -1174,6 +1972,28 @@ function isTypeSchema(value) {
1174
1972
 
1175
1973
  /** State schema with optional schemas.input and nested states */
1176
1974
 
1975
+ /**
1976
+ * The sibling state schemas of a dotted path: the children of the path's
1977
+ * parent. A bare transition target resolves relative to the parent, so the
1978
+ * valid targets for a path's config are its siblings. For a dotless (top-level)
1979
+ * path the siblings are the root states.
1980
+ */
1981
+
1982
+ // dotless path: siblings are the current level's states
1983
+
1984
+ /**
1985
+ * Resolves a dotted state path (e.g. `'parent.child'`) into the leaf
1986
+ * `SetupStateSchema` it addresses, or `never` if any segment is missing.
1987
+ */
1988
+
1989
+ /** Union of every addressable dotted path into a setup states tree */
1990
+
1991
+ /**
1992
+ * Shared body of both `createStateConfig` overloads: the
1993
+ * `StateNodeConfigWithNestedInput` instantiation whose only varying parts are
1994
+ * the leading sibling-schemas and state-schema arguments.
1995
+ */
1996
+
1177
1997
  /** Extracts input type from a state schema */
1178
1998
 
1179
1999
  /**
@@ -1229,33 +2049,142 @@ function isTypeSchema(value) {
1229
2049
  * });
1230
2050
  * ```
1231
2051
  */
2052
+
1232
2053
  function setup(config = {}) {
1233
2054
  const {
1234
2055
  states = {},
1235
- schemas
2056
+ schemas,
2057
+ actions,
2058
+ actorSources,
2059
+ guards,
2060
+ delays
1236
2061
  } = config;
1237
2062
  return {
2063
+ extend(extension) {
2064
+ return setup(mergeSetupConfigs(config, extension));
2065
+ },
1238
2066
  createMachine(machineConfig) {
1239
2067
  const configSchemas = machineConfig.schemas;
1240
- const mergedSchemas = schemas || configSchemas ? {
1241
- ...configSchemas,
1242
- ...schemas
1243
- } : undefined;
1244
- return new StateMachine(mergedSchemas ? {
2068
+ const mergedSchemas = mergeSchemas(configSchemas, schemas);
2069
+ const mergedActions = mergeMaps(actions, machineConfig.actions);
2070
+ const mergedActorSources = mergeMaps(actorSources, machineConfig.actorSources);
2071
+ const mergedGuards = mergeMaps(guards, machineConfig.guards);
2072
+ const mergedDelays = mergeMaps(delays, machineConfig.delays);
2073
+ return new StateMachine({
1245
2074
  ...machineConfig,
1246
- schemas: mergedSchemas
1247
- } : machineConfig);
2075
+ ...(mergedSchemas ? {
2076
+ schemas: mergedSchemas
2077
+ } : undefined),
2078
+ ...(mergedActions ? {
2079
+ actions: mergedActions
2080
+ } : undefined),
2081
+ ...(mergedActorSources ? {
2082
+ actorSources: mergedActorSources
2083
+ } : undefined),
2084
+ ...(mergedGuards ? {
2085
+ guards: mergedGuards
2086
+ } : undefined),
2087
+ ...(mergedDelays ? {
2088
+ delays: mergedDelays
2089
+ } : undefined)
2090
+ });
2091
+ },
2092
+ createStateConfig(...args) {
2093
+ return args.length > 1 ? args[1] : args[0];
2094
+ },
2095
+ states,
2096
+ schemas: schemas ?? {}
2097
+ };
2098
+ }
2099
+ function createSystem(_config = {}) {
2100
+ const runtimeRef = {};
2101
+ const pendingObservers = [];
2102
+ const flushObservers = () => {
2103
+ const runtime = runtimeRef.current;
2104
+ if (!runtime) {
2105
+ return;
2106
+ }
2107
+ for (const entry of pendingObservers) {
2108
+ if (entry.active && !entry.subscription) {
2109
+ entry.subscription = runtime.inspect(entry.observer);
2110
+ }
2111
+ }
2112
+ };
2113
+ return {
2114
+ createActor(logic, options) {
2115
+ const actor = createActor(logic, {
2116
+ ...options,
2117
+ _systemRef: runtimeRef
2118
+ });
2119
+ flushObservers();
2120
+ return actor;
2121
+ },
2122
+ get(key) {
2123
+ return runtimeRef.current?.get(key);
1248
2124
  },
1249
- createStateConfig(stateConfig) {
1250
- return stateConfig;
2125
+ getAll() {
2126
+ return runtimeRef.current?.getAll() ?? {};
2127
+ },
2128
+ inspect(observer) {
2129
+ const runtime = runtimeRef.current;
2130
+ if (runtime) {
2131
+ return runtime.inspect(observer);
2132
+ }
2133
+ const entry = {
2134
+ observer: observer,
2135
+ active: true
2136
+ };
2137
+ pendingObservers.push(entry);
2138
+ return {
2139
+ unsubscribe() {
2140
+ entry.active = false;
2141
+ entry.subscription?.unsubscribe();
2142
+ }
2143
+ };
1251
2144
  },
1252
- states
2145
+ setup(config) {
2146
+ return config ? setup(config) : setup();
2147
+ }
2148
+ };
2149
+ }
2150
+ function mergeMaps(left, right) {
2151
+ return left || right ? {
2152
+ ...left,
2153
+ ...right
2154
+ } : undefined;
2155
+ }
2156
+ function mergeSchemas(left, right) {
2157
+ if (!left && !right) {
2158
+ return undefined;
2159
+ }
2160
+ return {
2161
+ ...left,
2162
+ ...right,
2163
+ events: mergeMaps(left?.events, right?.events),
2164
+ actions: mergeMaps(left?.actions, right?.actions),
2165
+ guards: mergeMaps(left?.guards, right?.guards),
2166
+ emitted: mergeMaps(left?.emitted, right?.emitted),
2167
+ children: mergeMaps(left?.children, right?.children)
2168
+ };
2169
+ }
2170
+ function mergeSetupConfigs(base, extension) {
2171
+ return {
2172
+ ...base,
2173
+ ...extension,
2174
+ schemas: mergeSchemas(base.schemas, extension.schemas),
2175
+ states: mergeMaps(base.states, extension.states),
2176
+ actions: mergeMaps(base.actions, extension.actions),
2177
+ actorSources: mergeMaps(base.actorSources, extension.actorSources),
2178
+ guards: mergeMaps(base.guards, extension.guards),
2179
+ delays: mergeMaps(base.delays, extension.delays)
1253
2180
  };
1254
2181
  }
1255
2182
 
1256
2183
  /** @internal */
1257
2184
  function createInertActorScope(actorLogic) {
1258
- const self = createActor(actorLogic);
2185
+ const self = createActor(actorLogic, {
2186
+ _inert: true
2187
+ });
1259
2188
  const inertActorScope = {
1260
2189
  self: self,
1261
2190
  defer: () => {},
@@ -1273,7 +2202,7 @@ function createInertActorScope(actorLogic) {
1273
2202
  /** @deprecated Use `initialTransition(…)` instead. */
1274
2203
  function getInitialSnapshot(actorLogic, ...[input]) {
1275
2204
  const actorScope = createInertActorScope(actorLogic);
1276
- return actorLogic.getInitialSnapshot(actorScope, input);
2205
+ return actorLogic.initialTransition(input, actorScope)[0];
1277
2206
  }
1278
2207
 
1279
2208
  /**
@@ -1563,14 +2492,9 @@ let SpecialTargets = /*#__PURE__*/function (SpecialTargets) {
1563
2492
  * This is a pure function that does not execute `actions`.
1564
2493
  */
1565
2494
  function transition(logic, snapshot, event) {
1566
- const executableActions = [];
1567
2495
  const actorScope = createInertActorScope(logic);
1568
- actorScope.actionExecutor = action => {
1569
- executableActions.push(action);
1570
- };
1571
- const transitionResult = logic.transition(snapshot, event, actorScope);
1572
- const [nextSnapshot, effects] = Array.isArray(transitionResult) ? transitionResult : [transitionResult, []];
1573
- return [nextSnapshot, executableActions.concat(effects)];
2496
+ const [nextSnapshot, effects] = logic.transition(snapshot, event, actorScope);
2497
+ return [nextSnapshot, effects];
1574
2498
  }
1575
2499
 
1576
2500
  /**
@@ -1581,12 +2505,8 @@ function transition(logic, snapshot, event) {
1581
2505
  * This is a pure function that does not execute `actions`.
1582
2506
  */
1583
2507
  function initialTransition(logic, ...[input]) {
1584
- const executableActions = [];
1585
2508
  const actorScope = createInertActorScope(logic);
1586
- actorScope.actionExecutor = action => {
1587
- executableActions.push(action);
1588
- };
1589
- const nextSnapshot = logic.getInitialSnapshot(actorScope, input);
2509
+ const [nextSnapshot, executableActions] = logic.initialTransition(input, actorScope);
1590
2510
  return [nextSnapshot, executableActions];
1591
2511
  }
1592
2512
 
@@ -1780,4 +2700,4 @@ function waitFor(actorRef, predicate, options) {
1780
2700
  });
1781
2701
  }
1782
2702
 
1783
- export { SimulatedClock, SpecialTargets, assertEvent, createMachine, createMachineFromConfig, createStateConfig, getInitialMicrosteps, getInitialSnapshot, getMicrosteps, getNextSnapshot, getNextTransitions, initialTransition, isTypeSchema, machineConfigToJSON, mapState, serializeMachine, setup, toPromise, transition, types, waitFor };
2703
+ export { SimulatedClock, SpecialTargets, assertEvent, createFSM, createMachine, createMachineFromConfig, createStateConfig, createSystem, getInitialMicrosteps, getInitialSnapshot, getMicrosteps, getNextSnapshot, getNextTransitions, initialTransition, isTypeSchema, machineConfigToJSON, mapState, serializeMachine, setup, toPromise, transition, types, waitFor };