xstate 5.32.2 → 6.0.0-alpha.10

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 (121) hide show
  1. package/README.md +25 -24
  2. package/dist/StateMachine-0aa98f54.development.esm.js +746 -0
  3. package/dist/StateMachine-aef700df.cjs.js +732 -0
  4. package/dist/StateMachine-d4931336.esm.js +729 -0
  5. package/dist/StateMachine-da872dde.development.cjs.js +749 -0
  6. package/dist/declarations/src/State.d.ts +11 -13
  7. package/dist/declarations/src/StateMachine.d.ts +40 -26
  8. package/dist/declarations/src/StateNode.d.ts +18 -54
  9. package/dist/declarations/src/actions.d.ts +14 -9
  10. package/dist/declarations/src/actors/callback.d.ts +42 -16
  11. package/dist/declarations/src/actors/index.d.ts +9 -6
  12. package/dist/declarations/src/actors/listener.d.ts +18 -0
  13. package/dist/declarations/src/actors/logic.d.ts +105 -0
  14. package/dist/declarations/src/actors/observable.d.ts +72 -27
  15. package/dist/declarations/src/actors/promise.d.ts +133 -48
  16. package/dist/declarations/src/actors/subscription.d.ts +22 -0
  17. package/dist/declarations/src/createActor.d.ts +33 -34
  18. package/dist/declarations/src/createMachine.d.ts +38 -7
  19. package/dist/declarations/src/createMachineFromConfig.d.ts +177 -0
  20. package/dist/declarations/src/graph/graph.d.ts +6 -4
  21. package/dist/declarations/src/graph/shortestPaths.d.ts +2 -2
  22. package/dist/declarations/src/graph/simplePaths.d.ts +2 -2
  23. package/dist/declarations/src/index.d.ts +13 -9
  24. package/dist/declarations/src/inspection.d.ts +84 -30
  25. package/dist/declarations/src/mapState.d.ts +4 -2
  26. package/dist/declarations/src/schema.types.d.ts +90 -0
  27. package/dist/declarations/src/serialize.d.ts +50 -0
  28. package/dist/declarations/src/setup.d.ts +396 -136
  29. package/dist/declarations/src/spawn.d.ts +7 -32
  30. package/dist/declarations/src/stateUtils.d.ts +22 -19
  31. package/dist/declarations/src/system.d.ts +8 -7
  32. package/dist/declarations/src/transition.d.ts +8 -5
  33. package/dist/declarations/src/transitionActions.d.ts +4 -0
  34. package/dist/declarations/src/types.d.ts +494 -429
  35. package/dist/declarations/src/types.v6.d.ts +423 -0
  36. package/dist/declarations/src/utils.d.ts +5 -3
  37. package/dist/index-219cb621.development.cjs.js +4476 -0
  38. package/dist/index-2f2fbd9b.development.esm.js +4421 -0
  39. package/dist/index-559ceab3.cjs.js +4437 -0
  40. package/dist/index-8828376f.esm.js +4382 -0
  41. package/dist/xstate-actors.cjs.js +12 -838
  42. package/dist/xstate-actors.cjs.mjs +10 -5
  43. package/dist/xstate-actors.development.cjs.js +12 -838
  44. package/dist/xstate-actors.development.cjs.mjs +10 -5
  45. package/dist/xstate-actors.development.esm.js +1 -836
  46. package/dist/xstate-actors.esm.js +1 -836
  47. package/dist/xstate-actors.umd.min.js +1 -1
  48. package/dist/xstate-actors.umd.min.js.map +1 -1
  49. package/dist/xstate-graph.cjs.js +18 -29
  50. package/dist/xstate-graph.development.cjs.js +18 -29
  51. package/dist/xstate-graph.development.esm.js +15 -26
  52. package/dist/xstate-graph.esm.js +15 -26
  53. package/dist/xstate-graph.umd.min.js +1 -1
  54. package/dist/xstate-graph.umd.min.js.map +1 -1
  55. package/dist/xstate.cjs.js +1477 -122
  56. package/dist/xstate.cjs.mjs +19 -22
  57. package/dist/xstate.development.cjs.js +1477 -122
  58. package/dist/xstate.development.cjs.mjs +19 -22
  59. package/dist/xstate.development.esm.js +1442 -86
  60. package/dist/xstate.esm.js +1442 -86
  61. package/dist/xstate.umd.min.js +1 -1
  62. package/dist/xstate.umd.min.js.map +1 -1
  63. package/package.json +3 -47
  64. package/dist/StateMachine-23c204bb.development.esm.js +0 -566
  65. package/dist/StateMachine-67a0f463.development.cjs.js +0 -569
  66. package/dist/StateMachine-94d215e3.cjs.js +0 -563
  67. package/dist/StateMachine-d1d80308.esm.js +0 -560
  68. package/dist/assign-43b1b558.development.esm.js +0 -133
  69. package/dist/assign-54b312bc.esm.js +0 -127
  70. package/dist/assign-b5bc78f7.cjs.js +0 -129
  71. package/dist/assign-f8f66278.development.cjs.js +0 -135
  72. package/dist/declarations/src/actions/assign.d.ts +0 -45
  73. package/dist/declarations/src/actions/cancel.d.ts +0 -39
  74. package/dist/declarations/src/actions/emit.d.ts +0 -44
  75. package/dist/declarations/src/actions/enqueueActions.d.ts +0 -57
  76. package/dist/declarations/src/actions/log.d.ts +0 -16
  77. package/dist/declarations/src/actions/raise.d.ts +0 -21
  78. package/dist/declarations/src/actions/send.d.ts +0 -42
  79. package/dist/declarations/src/actions/spawnChild.d.ts +0 -34
  80. package/dist/declarations/src/actions/stopChild.d.ts +0 -19
  81. package/dist/declarations/src/actors/transition.d.ts +0 -143
  82. package/dist/declarations/src/dev/index.d.ts +0 -14
  83. package/dist/declarations/src/guards.d.ts +0 -128
  84. package/dist/log-9b02c355.esm.js +0 -401
  85. package/dist/log-c94995c7.cjs.js +0 -409
  86. package/dist/log-d9f3f459.development.cjs.js +0 -437
  87. package/dist/log-db444cad.development.esm.js +0 -429
  88. package/dist/raise-235108c6.esm.js +0 -2762
  89. package/dist/raise-45f11c00.development.esm.js +0 -2824
  90. package/dist/raise-c445379d.cjs.js +0 -2817
  91. package/dist/raise-eca73993.development.cjs.js +0 -2879
  92. package/dist/xstate-actions.cjs.d.mts +0 -2
  93. package/dist/xstate-actions.cjs.d.ts +0 -2
  94. package/dist/xstate-actions.cjs.js +0 -23
  95. package/dist/xstate-actions.cjs.mjs +0 -14
  96. package/dist/xstate-actions.development.cjs.js +0 -23
  97. package/dist/xstate-actions.development.cjs.mjs +0 -14
  98. package/dist/xstate-actions.development.esm.js +0 -4
  99. package/dist/xstate-actions.esm.js +0 -4
  100. package/dist/xstate-actions.umd.min.js +0 -2
  101. package/dist/xstate-actions.umd.min.js.map +0 -1
  102. package/dist/xstate-dev.cjs.d.mts +0 -2
  103. package/dist/xstate-dev.cjs.d.ts +0 -2
  104. package/dist/xstate-dev.cjs.js +0 -48
  105. package/dist/xstate-dev.cjs.mjs +0 -5
  106. package/dist/xstate-dev.development.cjs.js +0 -51
  107. package/dist/xstate-dev.development.cjs.mjs +0 -5
  108. package/dist/xstate-dev.development.esm.js +0 -45
  109. package/dist/xstate-dev.esm.js +0 -42
  110. package/dist/xstate-dev.umd.min.js +0 -2
  111. package/dist/xstate-dev.umd.min.js.map +0 -1
  112. package/dist/xstate-guards.cjs.d.mts +0 -2
  113. package/dist/xstate-guards.cjs.d.ts +0 -2
  114. package/dist/xstate-guards.cjs.js +0 -14
  115. package/dist/xstate-guards.cjs.mjs +0 -7
  116. package/dist/xstate-guards.development.cjs.js +0 -14
  117. package/dist/xstate-guards.development.cjs.mjs +0 -7
  118. package/dist/xstate-guards.development.esm.js +0 -2
  119. package/dist/xstate-guards.esm.js +0 -2
  120. package/dist/xstate-guards.umd.min.js +0 -2
  121. package/dist/xstate-guards.umd.min.js.map +0 -1
@@ -0,0 +1,4476 @@
1
+ 'use strict';
2
+
3
+ class Mailbox {
4
+ constructor(_process) {
5
+ this._process = _process;
6
+ this._active = false;
7
+ this._current = null;
8
+ this._last = null;
9
+ }
10
+ start() {
11
+ this._active = true;
12
+ this.flush();
13
+ }
14
+ clear() {
15
+ // we can't set _current to null because we might be currently processing
16
+ // and enqueue following clear shouldn't start processing the enqueued item immediately
17
+ if (this._current) {
18
+ this._current.next = null;
19
+ this._last = this._current;
20
+ }
21
+ }
22
+ enqueue(event) {
23
+ const enqueued = {
24
+ value: event,
25
+ next: null
26
+ };
27
+ if (this._current) {
28
+ this._last.next = enqueued;
29
+ this._last = enqueued;
30
+ return;
31
+ }
32
+ this._current = enqueued;
33
+ this._last = enqueued;
34
+ if (this._active) {
35
+ this.flush();
36
+ }
37
+ }
38
+ flush() {
39
+ while (this._current) {
40
+ // atm the given _process is responsible for implementing proper try/catch handling
41
+ // we assume here that this won't throw in a way that can affect this mailbox
42
+ const consumed = this._current;
43
+ this._process(consumed.value);
44
+ this._current = consumed.next;
45
+ }
46
+ this._last = null;
47
+ }
48
+ }
49
+
50
+ const STATE_DELIMITER = '.';
51
+ const TARGETLESS_KEY = '';
52
+ const NULL_EVENT = '';
53
+ const STATE_IDENTIFIER = '#';
54
+ const WILDCARD = '*';
55
+ const XSTATE_INIT = '@xstate.init';
56
+ const XSTATE_STOP = '@xstate.stop';
57
+
58
+ /**
59
+ * Returns an event that represents an implicit event that is sent after the
60
+ * specified `delay`.
61
+ *
62
+ * @param delayRef The delay in milliseconds
63
+ * @param id The state node ID where this event is handled
64
+ */
65
+ function createAfterEvent(delayRef, id) {
66
+ return {
67
+ type: `xstate.after.${delayRef}.${id}`
68
+ };
69
+ }
70
+
71
+ /**
72
+ * Returns an event that represents an implicit state-level timeout. Fired when
73
+ * a state's `timeout` duration elapses without the state being exited.
74
+ *
75
+ * @param id The state node ID where this timeout is configured
76
+ */
77
+ function createTimeoutEvent(id) {
78
+ return {
79
+ type: `xstate.timeout.${id}`
80
+ };
81
+ }
82
+
83
+ /**
84
+ * Returns an event that represents an implicit invoke-level timeout. Fired when
85
+ * an invoked actor has not completed within its `timeout` duration.
86
+ *
87
+ * @param invokeId The invoked actor's ID
88
+ */
89
+ function createInvokeTimeoutEvent(invokeId) {
90
+ return {
91
+ type: `xstate.timeout.actor.${invokeId}`
92
+ };
93
+ }
94
+
95
+ /**
96
+ * Returns an event that represents that a final state node has been reached in
97
+ * the parent state node.
98
+ *
99
+ * @param id The final state node's parent state node `id`
100
+ * @param output The data to pass into the event
101
+ */
102
+ function createDoneStateEvent(id, output) {
103
+ return {
104
+ type: `xstate.done.state.${id}`,
105
+ output
106
+ };
107
+ }
108
+
109
+ /**
110
+ * Returns an event that represents that an invoked service has terminated.
111
+ *
112
+ * An invoked service is terminated when it has reached a top-level final state
113
+ * node, but not when it is canceled.
114
+ *
115
+ * @param invokeId The invoked service ID
116
+ * @param output The data to pass into the event
117
+ */
118
+ function createDoneActorEvent(invokeId, output) {
119
+ return {
120
+ type: `xstate.done.actor.${invokeId}`,
121
+ output,
122
+ actorId: invokeId
123
+ };
124
+ }
125
+ function createErrorActorEvent(id, error) {
126
+ return {
127
+ type: `xstate.error.actor.${id}`,
128
+ error,
129
+ actorId: id
130
+ };
131
+ }
132
+ function createInitEvent(input) {
133
+ return {
134
+ type: XSTATE_INIT,
135
+ input
136
+ };
137
+ }
138
+
139
+ /**
140
+ * This function makes sure that unhandled errors are thrown in a separate
141
+ * macrotask. It allows those errors to be detected by global error handlers and
142
+ * reported to bug tracking services without interrupting our own stack of
143
+ * execution.
144
+ *
145
+ * @param err Error to be thrown
146
+ */
147
+ function reportUnhandledError(err) {
148
+ setTimeout(() => {
149
+ throw err;
150
+ });
151
+ }
152
+
153
+ const symbolObservable = (() => typeof Symbol === 'function' && Symbol.observable || '@@observable')();
154
+
155
+ const builtInActions = {
156
+ ['@xstate.start']: actor => {
157
+ actor.start();
158
+ },
159
+ ['@xstate.raise']: (actorScope, event, options) => {
160
+ actorScope.system.scheduler.schedule(actorScope.self, actorScope.self, event, options?.delay ?? 0, options?.id);
161
+ },
162
+ ['@xstate.sendTo']: (actorScope, actor, event, options) => {
163
+ if (typeof event === 'string') {
164
+ throw new Error(// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
165
+ `Only event objects may be used with sendTo; use sendTo({ type: "${event}" }) instead` );
166
+ }
167
+ if (options?.delay !== undefined) {
168
+ actorScope.system.scheduler.schedule(actorScope.self, actor, event, options?.delay ?? 0, options?.id);
169
+ } else {
170
+ actorScope.defer(() => {
171
+ actorScope.system._relay(actorScope.self, actor, event);
172
+ });
173
+ }
174
+ },
175
+ ['@xstate.cancel']: (actorScope, sendId) => {
176
+ actorScope.system.scheduler.cancel(actorScope.self, sendId);
177
+ },
178
+ ['@xstate.stop']: (actorScope, actor) => {
179
+ actorScope.stopChild(actor);
180
+ }
181
+ };
182
+
183
+ const subscriptions = /* #__PURE__ */new WeakMap();
184
+
185
+ /**
186
+ * Internal factory for actor logic that attaches to another actor on start
187
+ * (`enq.listen()` / `enq.subscribeTo()`) and detaches when stopped.
188
+ */
189
+ function createAttachedLogic(attach) {
190
+ const initialTransition = (input, _) => [{
191
+ status: 'active',
192
+ output: undefined,
193
+ error: undefined,
194
+ input
195
+ }, []];
196
+ return {
197
+ start: (state, {
198
+ self,
199
+ system
200
+ }) => {
201
+ // Don't attach if the target doesn't exist or is stopped.
202
+ const target = state.input.actor;
203
+ if (!target || target.getSnapshot().status === 'stopped') {
204
+ return;
205
+ }
206
+ const subscription = attach(state.input, {
207
+ self,
208
+ system
209
+ });
210
+ if (!subscription) {
211
+ return;
212
+ }
213
+
214
+ // Attached actors are not registered as children (their `input` holds
215
+ // non-serializable function mappers), so a parent stop does not cascade
216
+ // to them. Subscribe to the parent and tear down when it stops/errors,
217
+ // so the subscription never relays to a stopped parent or leaks the
218
+ // source subscription.
219
+ let parentSubscription;
220
+ let torndown = false;
221
+ const teardown = () => {
222
+ if (torndown) {
223
+ return;
224
+ }
225
+ torndown = true;
226
+ subscription.unsubscribe();
227
+ parentSubscription?.unsubscribe();
228
+ };
229
+ const parent = self._parent;
230
+ if (parent) {
231
+ parentSubscription = parent.subscribe({
232
+ complete: teardown,
233
+ error: teardown
234
+ });
235
+ }
236
+ subscriptions.set(self, {
237
+ unsubscribe: teardown
238
+ });
239
+ },
240
+ transition: (state, event, {
241
+ self
242
+ }) => {
243
+ if (event.type === XSTATE_STOP) {
244
+ subscriptions.get(self)?.unsubscribe();
245
+ subscriptions.delete(self);
246
+ return [{
247
+ ...state,
248
+ status: 'stopped',
249
+ error: undefined
250
+ }, []];
251
+ }
252
+ return [state, []];
253
+ },
254
+ initialTransition,
255
+ getInitialSnapshot: (actorScope, input) => initialTransition(input)[0],
256
+ getPersistedSnapshot: snapshot => snapshot,
257
+ restoreSnapshot: snapshot => snapshot
258
+ };
259
+ }
260
+
261
+ /**
262
+ * Relays a mapped event to the attached actor's parent, unless the attached
263
+ * actor has been stopped (the mapper is not called in that case).
264
+ */
265
+ function relayMappedToParent(self, system, getEvent) {
266
+ if (self.getSnapshot().status === 'stopped' || !self._parent) {
267
+ return;
268
+ }
269
+ system._relay(self, self._parent, getEvent());
270
+ }
271
+
272
+ /**
273
+ * Creates actor logic for listening to emitted events from another actor. Used
274
+ * internally by `enq.listen()`.
275
+ */
276
+ function createListenerLogic() {
277
+ return createAttachedLogic(({
278
+ actor,
279
+ eventType,
280
+ mapper
281
+ }, {
282
+ self,
283
+ system
284
+ }) => {
285
+ // Determine the subscription type:
286
+ // - For exact matches or '*', subscribe directly
287
+ // - For partial wildcards ('data.*'), subscribe to '*' and filter
288
+ const isPartialWildcard = eventType !== '*' && eventType.endsWith('.*');
289
+ return actor.on(isPartialWildcard ? '*' : eventType, emittedEvent => {
290
+ if (isPartialWildcard && !matchesEventDescriptor(emittedEvent.type, eventType)) {
291
+ return;
292
+ }
293
+ relayMappedToParent(self, system, () => mapper(emittedEvent));
294
+ });
295
+ });
296
+ }
297
+
298
+ // Singleton logic instance
299
+ const listenerLogic = /* #__PURE__ */createListenerLogic();
300
+
301
+ /**
302
+ * Creates actor logic for subscribing to lifecycle events (done/error/snapshot)
303
+ * from another actor. Used internally by `enq.subscribeTo()`.
304
+ */
305
+ function createSubscriptionLogic() {
306
+ return createAttachedLogic(({
307
+ actor,
308
+ mappers
309
+ }, {
310
+ self,
311
+ system
312
+ }) => {
313
+ const {
314
+ done,
315
+ error,
316
+ snapshot: onSnapshot
317
+ } = mappers;
318
+ return actor.subscribe({
319
+ next: snapshot => {
320
+ if (snapshot.status === 'done' && done) {
321
+ relayMappedToParent(self, system, () => done(snapshot.output));
322
+ } else if (snapshot.status === 'error' && error) {
323
+ relayMappedToParent(self, system, () => error(snapshot.error));
324
+ } else if (snapshot.status === 'active' && onSnapshot) {
325
+ relayMappedToParent(self, system, () => onSnapshot(snapshot));
326
+ }
327
+ },
328
+ error: err => {
329
+ if (error) {
330
+ relayMappedToParent(self, system, () => error(err));
331
+ }
332
+ },
333
+ complete: () => {
334
+ // Actor completed without output (stopped); no action needed
335
+ }
336
+ });
337
+ });
338
+ }
339
+
340
+ // Singleton logic instance
341
+ const subscriptionLogic = /* #__PURE__ */createSubscriptionLogic();
342
+
343
+ function mergeContextPatch$1(context, patch) {
344
+ return {
345
+ ...context,
346
+ ...patch
347
+ };
348
+ }
349
+ function pushBuiltInAction(actions, action, ...args) {
350
+ actions.push({
351
+ action,
352
+ args
353
+ });
354
+ }
355
+ function registerSpawnedChild(actor, id) {
356
+ return Object.assign(function registerChild(args) {
357
+ return {
358
+ children: {
359
+ ...args.children,
360
+ [id]: actor
361
+ }
362
+ };
363
+ }, {
364
+ _special: true
365
+ });
366
+ }
367
+ function unregisterChild(actor) {
368
+ return Object.assign(function removeChild(args) {
369
+ const children = {
370
+ ...args.children
371
+ };
372
+ for (const key of Object.keys(children)) {
373
+ if (children[key] === actor) {
374
+ delete children[key];
375
+ }
376
+ }
377
+ return {
378
+ children
379
+ };
380
+ }, {
381
+ _special: true
382
+ });
383
+ }
384
+ function pushStartedChild(actions, actor, id) {
385
+ pushBuiltInAction(actions, builtInActions['@xstate.start'], actor);
386
+ actions.push(registerSpawnedChild(actor, id ?? actor.id));
387
+ }
388
+ function createTransitionEnqueue(actorScope, actions, internalEvents, actorSubscriptions = false) {
389
+ const props = {
390
+ cancel: id => {
391
+ pushBuiltInAction(actions, builtInActions['@xstate.cancel'], actorScope, id);
392
+ },
393
+ emit: emittedEvent => {
394
+ actions.push(emittedEvent);
395
+ },
396
+ log: (...args) => {
397
+ pushBuiltInAction(actions, actorScope.logger, ...args);
398
+ },
399
+ raise: (raisedEvent, options) => {
400
+ if (typeof raisedEvent === 'string') {
401
+ throw new Error(`Only event objects may be used with raise; use raise({ type: "${raisedEvent}" }) instead` );
402
+ }
403
+ if (options?.delay !== undefined) {
404
+ pushBuiltInAction(actions, builtInActions['@xstate.raise'], actorScope, raisedEvent, options);
405
+ } else {
406
+ internalEvents.push(raisedEvent);
407
+ }
408
+ },
409
+ spawn: (logic, options) => {
410
+ const actor = createActor(logic, {
411
+ ...options,
412
+ parent: actorScope.self
413
+ });
414
+ pushStartedChild(actions, actor, options?.id);
415
+ return actor;
416
+ },
417
+ sendTo: (actor, event, options) => {
418
+ if (actor) {
419
+ pushBuiltInAction(actions, builtInActions['@xstate.sendTo'], actorScope, actor, event, options);
420
+ }
421
+ },
422
+ stop: actor => {
423
+ if (actor) {
424
+ pushBuiltInAction(actions, builtInActions['@xstate.stop'], actorScope, actor);
425
+ actions.push(unregisterChild(actor));
426
+ }
427
+ }
428
+ };
429
+ if (actorSubscriptions) {
430
+ Object.assign(props, {
431
+ listen: (actor, eventType, mapper) => {
432
+ const input = {
433
+ actor,
434
+ eventType,
435
+ mapper
436
+ };
437
+ const listenerActor = createActor(listenerLogic, {
438
+ input,
439
+ parent: actorScope.self
440
+ });
441
+ pushBuiltInAction(actions, builtInActions['@xstate.start'], listenerActor);
442
+ return listenerActor;
443
+ },
444
+ subscribeTo: (actor, mappers) => {
445
+ const normalizedMappers = typeof mappers === 'function' ? {
446
+ snapshot: mappers
447
+ } : mappers;
448
+ const input = {
449
+ actor,
450
+ mappers: normalizedMappers
451
+ };
452
+ const subscriptionActor = createActor(subscriptionLogic, {
453
+ input,
454
+ parent: actorScope.self
455
+ });
456
+ pushBuiltInAction(actions, builtInActions['@xstate.start'], subscriptionActor);
457
+ return subscriptionActor;
458
+ }
459
+ });
460
+ }
461
+ return createEnqueueObject$1(props, (action, ...args) => {
462
+ pushBuiltInAction(actions, action, ...args);
463
+ });
464
+ }
465
+ function getBuiltInActionFields(action, args) {
466
+ switch (action) {
467
+ case builtInActions['@xstate.start']:
468
+ {
469
+ const [actor] = args;
470
+ return {
471
+ actor,
472
+ id: actor.id,
473
+ logic: actor.logic,
474
+ src: actor.src,
475
+ input: actor.options?.input
476
+ };
477
+ }
478
+ case builtInActions['@xstate.raise']:
479
+ {
480
+ const [, event, options] = args;
481
+ return {
482
+ event,
483
+ id: options?.id,
484
+ delay: options?.delay
485
+ };
486
+ }
487
+ case builtInActions['@xstate.sendTo']:
488
+ {
489
+ const [, target, event, options] = args;
490
+ return {
491
+ target,
492
+ event,
493
+ id: options?.id,
494
+ delay: options?.delay
495
+ };
496
+ }
497
+ case builtInActions['@xstate.cancel']:
498
+ {
499
+ const [, id] = args;
500
+ return {
501
+ id
502
+ };
503
+ }
504
+ case builtInActions['@xstate.stop']:
505
+ {
506
+ const [, actor] = args;
507
+ return {
508
+ actor
509
+ };
510
+ }
511
+ default:
512
+ return undefined;
513
+ }
514
+ }
515
+ function isBuiltInExecutableAction(action) {
516
+ return Object.prototype.hasOwnProperty.call(builtInActions, action.type);
517
+ }
518
+ function resolveActionsWithContext(currentSnapshot, event, actorScope, actions) {
519
+ let intermediateSnapshot = currentSnapshot;
520
+ const executableActions = [];
521
+ for (const action of actions) {
522
+ const isInline = typeof action === 'function';
523
+ const resolvedAction = isInline ? action : typeof action === 'object' && 'action' in action && typeof action.action === 'function' ? action.action.bind(null, ...action.args) : false;
524
+ const actionArgs = {
525
+ context: intermediateSnapshot.context,
526
+ event,
527
+ output: getEventOutput(event),
528
+ self: actorScope.self,
529
+ system: actorScope.system,
530
+ children: intermediateSnapshot.children,
531
+ parent: actorScope.self._parent,
532
+ actions: currentSnapshot.machine.implementations.actions,
533
+ actorSources: currentSnapshot.machine.implementations.actorSources
534
+ };
535
+ let actionParams = undefined;
536
+ if (typeof action === 'object' && action !== null) {
537
+ const {
538
+ type: _,
539
+ ...emittedEventParams
540
+ } = action;
541
+ actionParams = emittedEventParams;
542
+ }
543
+ if (resolvedAction && '_special' in resolvedAction) {
544
+ executableActions.push({
545
+ type: typeof action === 'object' ? 'action' in action && typeof action.action === 'function' ? action.action.name ?? '(anonymous)' : action.type ?? '(anonymous)' : action.name || '(anonymous)',
546
+ params: actionParams,
547
+ args: [],
548
+ exec: undefined
549
+ });
550
+ const specialAction = resolvedAction;
551
+ const res = specialAction(actionArgs, emptyEnqueueObject);
552
+ if (res && ('context' in res || 'children' in res)) {
553
+ intermediateSnapshot = cloneMachineSnapshot(intermediateSnapshot, {
554
+ ...(res.context !== undefined ? {
555
+ context: mergeContextPatch$1(intermediateSnapshot.context, res.context)
556
+ } : {}),
557
+ ...('children' in res ? {
558
+ children: res.children
559
+ } : {})
560
+ });
561
+ }
562
+ continue;
563
+ }
564
+ if (!resolvedAction || !('resolve' in resolvedAction)) {
565
+ const builtInFields = typeof action === 'object' && action !== null && 'action' in action && typeof action.action === 'function' ? getBuiltInActionFields(action.action, action.args) : undefined;
566
+ executableActions.push({
567
+ type: typeof action === 'object' ? 'action' in action && typeof action.action === 'function' ? action.action.name ?? '(anonymous)' : action.type : action.name || '(anonymous)',
568
+ params: actionParams,
569
+ args: typeof action === 'object' && 'action' in action ? action.args : [],
570
+ exec: resolvedAction || (typeof action === 'object' && action !== null ? () => actorScope.defer(() => actorScope.emit(action)) : undefined),
571
+ ...builtInFields
572
+ });
573
+ continue;
574
+ }
575
+ }
576
+ return [intermediateSnapshot, executableActions];
577
+ }
578
+ function createEnqueueObject$1(props, action) {
579
+ const enqueueFn = (fn, ...args) => {
580
+ action(fn, ...args);
581
+ };
582
+ Object.assign(enqueueFn, {
583
+ cancel: () => {},
584
+ emit: () => {},
585
+ log: () => {},
586
+ raise: () => {},
587
+ spawn: () => ({}),
588
+ sendTo: () => {},
589
+ stop: () => {},
590
+ listen: () => ({}),
591
+ subscribeTo: () => ({}),
592
+ ...props
593
+ });
594
+ return enqueueFn;
595
+ }
596
+ const emptyEnqueueObject = createEnqueueObject$1({}, () => {});
597
+
598
+ function parseDurationToMilliseconds(duration) {
599
+ const normalizedDuration = duration.trim();
600
+ const millisecondsMatch = normalizedDuration.match(/^(\d+)ms$/i);
601
+ if (millisecondsMatch) {
602
+ return parseInt(millisecondsMatch[1], 10);
603
+ }
604
+ const secondsMatch = normalizedDuration.match(/^(\d*)(\.?)(\d*)s$/i);
605
+ if (secondsMatch) {
606
+ const wholePart = secondsMatch[1] ? parseInt(secondsMatch[1], 10) : 0;
607
+ const hasDecimal = !!secondsMatch[2];
608
+ const fracPart = secondsMatch[3] ? parseInt(secondsMatch[3].padEnd(3, '0').slice(0, 3), 10) : 0;
609
+ return wholePart * 1000 + (hasDecimal ? fracPart : 0);
610
+ }
611
+ const iso8601DurationMatch = normalizedDuration.match(/^P(?:(?<weeks>\d+(?:[.,]\d+)?)W)?(?:(?<days>\d+(?:[.,]\d+)?)D)?(?:T(?:(?<hours>\d+(?:[.,]\d+)?)H)?(?:(?<minutes>\d+(?:[.,]\d+)?)M)?(?:(?<seconds>\d+(?:[.,]\d+)?)S)?)?$/i);
612
+ if (!iso8601DurationMatch?.groups) {
613
+ return undefined;
614
+ }
615
+ const {
616
+ weeks,
617
+ days,
618
+ hours,
619
+ minutes,
620
+ seconds
621
+ } = iso8601DurationMatch.groups;
622
+ if (!weeks && !days && !hours && !minutes && !seconds) {
623
+ return undefined;
624
+ }
625
+ const toNumber = value => value ? Number(value.replace(',', '.')) : 0;
626
+ return toNumber(weeks) * 7 * 24 * 60 * 60 * 1000 + toNumber(days) * 24 * 60 * 60 * 1000 + toNumber(hours) * 60 * 60 * 1000 + toNumber(minutes) * 60 * 1000 + toNumber(seconds) * 1000;
627
+ }
628
+ function parseDelayToMilliseconds(delay) {
629
+ if (delay === undefined) {
630
+ return undefined;
631
+ }
632
+ if (typeof delay === 'number') {
633
+ return delay;
634
+ }
635
+ return parseDurationToMilliseconds(delay);
636
+ }
637
+
638
+ function getConfiguredDelayValue(delay, delaySource) {
639
+ if (typeof delay !== 'string') {
640
+ return delay;
641
+ }
642
+ const referencedDelay = delaySource[delay];
643
+ if (referencedDelay !== undefined) {
644
+ return referencedDelay;
645
+ }
646
+ return parseDurationToMilliseconds(delay) ?? delay;
647
+ }
648
+ function resolveDelay(delay, delaySource, args) {
649
+ if (typeof delay === 'function') {
650
+ return delay(args);
651
+ }
652
+ const configuredDelay = getConfiguredDelayValue(delay, delaySource);
653
+ if (typeof configuredDelay === 'function') {
654
+ return configuredDelay(args);
655
+ }
656
+ return configuredDelay;
657
+ }
658
+ function isAtomicStateNode(stateNode) {
659
+ return stateNode.type === 'atomic' || stateNode.type === 'final' || stateNode.type === 'choice';
660
+ }
661
+ function getChildren(stateNode) {
662
+ return Object.values(stateNode.states).filter(sn => sn.type !== 'history');
663
+ }
664
+ function getProperAncestors(stateNode, toStateNode) {
665
+ const ancestors = [];
666
+ if (toStateNode === stateNode) {
667
+ return ancestors;
668
+ }
669
+
670
+ // add all ancestors
671
+ let m = stateNode.parent;
672
+ while (m && m !== toStateNode) {
673
+ ancestors.push(m);
674
+ m = m.parent;
675
+ }
676
+ return ancestors;
677
+ }
678
+ function getAllStateNodes(stateNodes) {
679
+ const nodeSet = new Set(stateNodes);
680
+ const activeParentSet = new Set();
681
+ for (const stateNode of nodeSet) {
682
+ if (stateNode.parent) {
683
+ activeParentSet.add(stateNode.parent);
684
+ }
685
+ }
686
+
687
+ // add descendants
688
+ for (const s of nodeSet) {
689
+ // if previously active, add existing child nodes
690
+ if (s.type === 'compound' && !activeParentSet.has(s)) {
691
+ for (const sn of getInitialStateNodes(s)) {
692
+ nodeSet.add(sn);
693
+ }
694
+ continue;
695
+ }
696
+ if (s.type === 'parallel') {
697
+ for (const child of getChildren(s)) {
698
+ if (!nodeSet.has(child)) {
699
+ const initialStates = getInitialStateNodes(child);
700
+ for (const initialStateNode of initialStates) {
701
+ nodeSet.add(initialStateNode);
702
+ }
703
+ }
704
+ }
705
+ }
706
+ }
707
+
708
+ // add all ancestors
709
+ // Stop walking up as soon as we hit a node already in the set: its own
710
+ // ancestor chain is guaranteed to be added when it is visited by this loop
711
+ // (Set iteration visits elements added during iteration), so shared ancestor
712
+ // chains are never re-walked once per descendant.
713
+ for (const s of nodeSet) {
714
+ let m = s.parent;
715
+ while (m && !nodeSet.has(m)) {
716
+ nodeSet.add(m);
717
+ m = m.parent;
718
+ }
719
+ }
720
+ return nodeSet;
721
+ }
722
+ function getStateValue(rootNode, stateNodes) {
723
+ const config = getAllStateNodes(stateNodes);
724
+ const adjList = new Map();
725
+ for (const s of config) {
726
+ if (!adjList.has(s)) {
727
+ adjList.set(s, []);
728
+ }
729
+ if (!s.parent) {
730
+ continue;
731
+ }
732
+ if (!adjList.has(s.parent)) {
733
+ adjList.set(s.parent, []);
734
+ }
735
+ adjList.get(s.parent).push(s);
736
+ }
737
+ const getValueFromAdj = baseNode => {
738
+ const childStateNodes = adjList.get(baseNode);
739
+ if (!childStateNodes) {
740
+ return {}; // todo: fix?
741
+ }
742
+ if (baseNode.type === 'compound') {
743
+ const childStateNode = childStateNodes[0];
744
+ if (!childStateNode) {
745
+ return {};
746
+ }
747
+ if (isAtomicStateNode(childStateNode)) {
748
+ return childStateNode.key;
749
+ }
750
+ }
751
+ const stateValue = {};
752
+ for (const childStateNode of childStateNodes) {
753
+ stateValue[childStateNode.key] = getValueFromAdj(childStateNode);
754
+ }
755
+ return stateValue;
756
+ };
757
+ return getValueFromAdj(rootNode);
758
+ }
759
+ function isInFinalState(stateNodeSet, stateNode) {
760
+ if (stateNode.type === 'compound') {
761
+ return getChildren(stateNode).some(s => s.type === 'final' && stateNodeSet.has(s));
762
+ }
763
+ if (stateNode.type === 'parallel') {
764
+ return getChildren(stateNode).every(sn => isInFinalState(stateNodeSet, sn));
765
+ }
766
+ return stateNode.type === 'final';
767
+ }
768
+ const isStateId = str => str[0] === STATE_IDENTIFIER;
769
+ function getCandidates(stateNode, receivedEventType) {
770
+ const exactMatch = stateNode.transitions.get(receivedEventType);
771
+ const wildcardCandidates = [...stateNode.transitions.keys()].filter(eventDescriptor => eventDescriptor !== receivedEventType && matchesEventDescriptor(receivedEventType, eventDescriptor)).sort((a, b) => b.length - a.length).flatMap(key => stateNode.transitions.get(key));
772
+ return exactMatch ? [...exactMatch, ...wildcardCandidates] : wildcardCandidates;
773
+ }
774
+ function scheduleDelayedEvent(stateNode, event, resolveScheduledDelay) {
775
+ const eventType = event.type;
776
+ const oldEntry = stateNode.entry;
777
+ stateNode.entry = (x, enq) => {
778
+ enq.raise(event, {
779
+ id: eventType,
780
+ delay: resolveScheduledDelay(x)
781
+ });
782
+ return typeof oldEntry === 'function' ? oldEntry(x, enq) : undefined;
783
+ };
784
+ const oldExit = stateNode.exit;
785
+ stateNode.exit = (_, enq) => {
786
+ enq.cancel(eventType);
787
+ return typeof oldExit === 'function' ? oldExit(_, enq) : undefined;
788
+ };
789
+ return eventType;
790
+ }
791
+
792
+ /** All delayed transitions from the config. */
793
+ function getDelayedTransitions(stateNode) {
794
+ const afterConfig = stateNode.config.after;
795
+ const timeoutConfig = stateNode.config.timeout;
796
+ const onTimeoutConfig = stateNode.config.onTimeout;
797
+ const invokeDefs = stateNode.invoke.filter(invokeDef => invokeDef.timeout !== undefined);
798
+ if (!afterConfig && timeoutConfig === undefined && invokeDefs.length === 0) {
799
+ return [];
800
+ }
801
+ if (timeoutConfig !== undefined && !onTimeoutConfig) {
802
+ throw new Error(`State "${stateNode.id}" has \`timeout\` but no \`onTimeout\` transition.`);
803
+ }
804
+ {
805
+ for (const invokeDef of invokeDefs) {
806
+ if (!invokeDef.onTimeout) {
807
+ throw new Error(`Invoke on state "${stateNode.id}" has \`timeout\` but no \`onTimeout\` transition.`);
808
+ }
809
+ }
810
+ }
811
+
812
+ // Every delayed transition — `after`, state-level `timeout`/`onTimeout`, and
813
+ // invoke-level `timeout`/`onTimeout` — has the same shape: an event raised on
814
+ // entry (and cancelled on exit) plus a transition that catches it. They
815
+ // differ only in the event raised, the transition(s) caught, and whether the
816
+ // delay resolves against the machine's `delays` map (invoke timeouts do not).
817
+ //
818
+ // `xstate.timeout.<id>` is a dedicated event so a state-level `timeout` cannot
819
+ // collide with an explicit `after` entry on the same state. Invoke timeouts
820
+ // are scheduled on the enclosing state; completion transitions cancel the
821
+ // timer separately, so it clears even when the parent state stays active.
822
+ const sources = [];
823
+ if (afterConfig) {
824
+ for (const key of Object.keys(afterConfig)) {
825
+ const delay = Number.isNaN(+key) ? key : +key;
826
+ sources.push({
827
+ event: createAfterEvent(delay, stateNode.id),
828
+ delay,
829
+ transitions: afterConfig[key],
830
+ fromDelaysMap: true
831
+ });
832
+ }
833
+ }
834
+ if (timeoutConfig !== undefined && onTimeoutConfig) {
835
+ sources.push({
836
+ event: createTimeoutEvent(stateNode.id),
837
+ delay: timeoutConfig,
838
+ transitions: onTimeoutConfig,
839
+ fromDelaysMap: true
840
+ });
841
+ }
842
+ for (const invokeDef of invokeDefs) {
843
+ sources.push({
844
+ event: createInvokeTimeoutEvent(invokeDef.id),
845
+ delay: invokeDef.timeout,
846
+ transitions: invokeDef.onTimeout,
847
+ fromDelaysMap: false
848
+ });
849
+ }
850
+
851
+ // `delay` here is the raw config value, retained only as metadata on the
852
+ // transition definition — the live delay is resolved at runtime in the
853
+ // scheduled entry action, so no eager resolution is needed.
854
+ const delayedTransitions = [];
855
+ for (const {
856
+ event,
857
+ delay,
858
+ transitions,
859
+ fromDelaysMap
860
+ } of sources) {
861
+ const eventType = scheduleDelayedEvent(stateNode, event, x => resolveDelay(delay, fromDelaysMap ? x.delays : {}, {
862
+ context: x.context,
863
+ event: x.event,
864
+ stateNode
865
+ }));
866
+ for (const transition of toTransitionConfigArray(transitions)) {
867
+ delayedTransitions.push({
868
+ ...transition,
869
+ event: eventType,
870
+ delay
871
+ });
872
+ }
873
+ }
874
+ return delayedTransitions.map(delayedTransition => ({
875
+ ...formatTransition(stateNode, delayedTransition.event, delayedTransition),
876
+ delay: delayedTransition.delay
877
+ }));
878
+ }
879
+ function formatTransition(stateNode, descriptor, transitionConfig) {
880
+ const normalizedTarget = normalizeTarget(transitionConfig.target);
881
+ const reenter = transitionConfig.reenter ?? false;
882
+ const target = resolveTarget(stateNode, normalizedTarget);
883
+ const transition = {
884
+ ...transitionConfig,
885
+ target,
886
+ source: stateNode,
887
+ reenter,
888
+ eventType: descriptor,
889
+ toJSON: () => ({
890
+ ...transition,
891
+ source: `#${stateNode.id}`,
892
+ target: target ? target.map(t => `#${t.id}`) : undefined
893
+ })
894
+ };
895
+ return transition;
896
+ }
897
+
898
+ /**
899
+ * Collects route transitions from all descendants with explicit IDs. Called
900
+ * once on the root node to avoid O(N²) repeated traversals.
901
+ */
902
+ function formatRouteTransitions(rootStateNode) {
903
+ const routeTransitions = [];
904
+ const collectRoutes = states => {
905
+ Object.values(states).forEach(sn => {
906
+ if (sn.config.route && sn.config.id) {
907
+ const routeId = sn.config.id;
908
+ const routeConfig = sn.config.route;
909
+ const routeMatches = ({
910
+ event
911
+ }) => event.to === `#${routeId}`;
912
+ if (typeof routeConfig === 'function') {
913
+ // Transition-style route: the function is the guard and resolver.
914
+ // Returning undefined/false blocks the route; returning true or a
915
+ // config object allows it.
916
+ routeTransitions.push(formatTransition(rootStateNode, 'xstate.route', {
917
+ guard: routeMatches,
918
+ to: args => {
919
+ const result = routeConfig(args);
920
+ if (!result) {
921
+ return undefined;
922
+ }
923
+ return {
924
+ ...(result === true ? {} : result),
925
+ target: `#${routeId}`
926
+ };
927
+ }
928
+ }));
929
+ if (sn.states) {
930
+ collectRoutes(sn.states);
931
+ }
932
+ return;
933
+ }
934
+ if ('$unserializable' in routeConfig) {
935
+ throw new Error(`State "${sn.id}" has a route that is not serializable. Re-provide the route function when reviving this machine.`);
936
+ }
937
+ const {
938
+ guard: _guard,
939
+ ...routeOptions
940
+ } = routeConfig;
941
+ const transition = {
942
+ ...routeOptions,
943
+ guard: routeMatches,
944
+ target: `#${routeId}`
945
+ };
946
+ routeTransitions.push(formatTransition(rootStateNode, 'xstate.route', transition));
947
+ }
948
+ if (sn.states) {
949
+ collectRoutes(sn.states);
950
+ }
951
+ });
952
+ };
953
+ collectRoutes(rootStateNode.states);
954
+ if (routeTransitions.length > 0) {
955
+ rootStateNode.transitions.set('xstate.route', routeTransitions);
956
+ }
957
+ }
958
+ function resolveTarget(stateNode, targets) {
959
+ if (targets === undefined) {
960
+ // an undefined target signals that the state node should not transition from that state when receiving that event
961
+ return undefined;
962
+ }
963
+ return targets.map(target => {
964
+ if (typeof target !== 'string') {
965
+ return target;
966
+ }
967
+ if (isStateId(target)) {
968
+ return stateNode.machine.getStateNodeById(target);
969
+ }
970
+ const isInternalTarget = target[0] === STATE_DELIMITER;
971
+ // If internal target is defined on machine,
972
+ // do not include machine key on target
973
+ if (isInternalTarget && !stateNode.parent) {
974
+ return getStateNodeByPath(stateNode, target.slice(1));
975
+ }
976
+ const resolvedTarget = isInternalTarget ? stateNode.key + target : target;
977
+ if (stateNode.parent) {
978
+ try {
979
+ const targetStateNode = getStateNodeByPath(stateNode.parent, resolvedTarget);
980
+ return targetStateNode;
981
+ } catch (err) {
982
+ throw new Error(`Invalid transition definition for state node '${stateNode.id}':\n${err.message}`);
983
+ }
984
+ } else {
985
+ throw new Error(`Invalid target: "${target}" is not a valid target from the root node. Did you mean ".${target}"?` );
986
+ }
987
+ });
988
+ }
989
+ function resolveHistoryDefaultTransition(stateNode) {
990
+ const normalizedTarget = normalizeTarget(stateNode.config.target);
991
+ if (!normalizedTarget) {
992
+ return stateNode.parent.initial;
993
+ }
994
+ return {
995
+ target: normalizedTarget.map(t => typeof t === 'string' ? getStateNodeByPath(stateNode.parent, t) : t),
996
+ source: stateNode,
997
+ reenter: false,
998
+ eventType: ''
999
+ };
1000
+ }
1001
+ function isHistoryNode(stateNode) {
1002
+ return stateNode.type === 'history';
1003
+ }
1004
+ function getInitialStateNodes(stateNode) {
1005
+ const set = new Set();
1006
+ function iter(descStateNode) {
1007
+ if (set.has(descStateNode)) {
1008
+ return;
1009
+ }
1010
+ set.add(descStateNode);
1011
+ if (descStateNode.type === 'compound') {
1012
+ iter(descStateNode.initial.target[0]);
1013
+ } else if (descStateNode.type === 'parallel') {
1014
+ for (const child of getChildren(descStateNode)) {
1015
+ iter(child);
1016
+ }
1017
+ }
1018
+ }
1019
+ iter(stateNode);
1020
+ for (const initialState of set) {
1021
+ for (const ancestor of getProperAncestors(initialState, stateNode)) {
1022
+ set.add(ancestor);
1023
+ }
1024
+ }
1025
+ return set;
1026
+ }
1027
+
1028
+ /** Returns the child state node from its relative `stateKey`, or throws. */
1029
+ function getStateNode(stateNode, stateKey) {
1030
+ if (isStateId(stateKey)) {
1031
+ return stateNode.machine.getStateNodeById(stateKey);
1032
+ }
1033
+ if (!stateNode.states) {
1034
+ throw new Error(`Unable to retrieve child state '${stateKey}' from '${stateNode.id}'; no child states exist.`);
1035
+ }
1036
+ const result = stateNode.states[stateKey];
1037
+ if (!result) {
1038
+ throw new Error(`Child state '${stateKey}' does not exist on '${stateNode.id}'`);
1039
+ }
1040
+ return result;
1041
+ }
1042
+
1043
+ /**
1044
+ * Returns the relative state node from the given `statePath`, or throws.
1045
+ *
1046
+ * @param statePath The string or string array relative path to the state node.
1047
+ */
1048
+ function getStateNodeByPath(stateNode, statePath) {
1049
+ if (typeof statePath === 'string' && isStateId(statePath)) {
1050
+ try {
1051
+ return stateNode.machine.getStateNodeById(statePath);
1052
+ } catch {
1053
+ // try individual paths
1054
+ // throw e;
1055
+ }
1056
+ }
1057
+ const arrayStatePath = toStatePath(statePath).slice();
1058
+ let currentStateNode = stateNode;
1059
+ while (arrayStatePath.length) {
1060
+ const key = arrayStatePath.shift();
1061
+ if (!key.length) {
1062
+ break;
1063
+ }
1064
+ currentStateNode = getStateNode(currentStateNode, key);
1065
+ }
1066
+ return currentStateNode;
1067
+ }
1068
+
1069
+ /**
1070
+ * Returns the state nodes represented by the current state value.
1071
+ *
1072
+ * @param stateValue The state value or State instance
1073
+ */
1074
+ function getStateNodes(stateNode, stateValue) {
1075
+ if (typeof stateValue === 'string') {
1076
+ const childStateNode = stateNode.states[stateValue];
1077
+ if (!childStateNode) {
1078
+ throw new Error(`State '${stateValue}' does not exist on '${stateNode.id}'`);
1079
+ }
1080
+ return [stateNode, childStateNode];
1081
+ }
1082
+ const childStateKeys = Object.keys(stateValue);
1083
+ const childStateNodes = new Array(childStateKeys.length);
1084
+ const allStateNodes = [stateNode.machine.root, stateNode];
1085
+ for (let i = 0; i < childStateKeys.length; i++) {
1086
+ const subStateNode = getStateNode(stateNode, childStateKeys[i]);
1087
+ childStateNodes[i] = subStateNode;
1088
+ allStateNodes.push(subStateNode);
1089
+ }
1090
+ for (let i = 0; i < childStateKeys.length; i++) {
1091
+ allStateNodes.push(...getStateNodes(childStateNodes[i], stateValue[childStateKeys[i]]));
1092
+ }
1093
+ return allStateNodes;
1094
+ }
1095
+ function transitionNode(stateNode, stateValue, snapshot, event, self) {
1096
+ // leaf node
1097
+ if (typeof stateValue === 'string') {
1098
+ const childStateNode = getStateNode(stateNode, stateValue);
1099
+ const next = childStateNode.next(snapshot, event, self);
1100
+ if (!next || !next.length) {
1101
+ return stateNode.next(snapshot, event, self);
1102
+ }
1103
+ return next;
1104
+ }
1105
+ const subStateKeys = Object.keys(stateValue);
1106
+ const subStateKey = subStateKeys[0];
1107
+ if (subStateKeys.length === 1) {
1108
+ const childStateNode = getStateNode(stateNode, subStateKey);
1109
+ const next = transitionNode(childStateNode, stateValue[subStateKey], snapshot, event, self);
1110
+ if (!next || !next.length) {
1111
+ return stateNode.next(snapshot, event, self);
1112
+ }
1113
+ return next;
1114
+ }
1115
+ const allInnerTransitions = [];
1116
+ for (const subStateKey of subStateKeys) {
1117
+ const subStateValue = stateValue[subStateKey];
1118
+ if (!subStateValue) {
1119
+ continue;
1120
+ }
1121
+ const subStateNode = getStateNode(stateNode, subStateKey);
1122
+ const innerTransitions = transitionNode(subStateNode, subStateValue, snapshot, event, self);
1123
+ if (innerTransitions) {
1124
+ allInnerTransitions.push(...innerTransitions);
1125
+ }
1126
+ }
1127
+ if (!allInnerTransitions.length) {
1128
+ return stateNode.next(snapshot, event, self);
1129
+ }
1130
+ return allInnerTransitions;
1131
+ }
1132
+ function isDescendant(childStateNode, parentStateNode) {
1133
+ let marker = childStateNode;
1134
+ while (marker.parent && marker.parent !== parentStateNode) {
1135
+ marker = marker.parent;
1136
+ }
1137
+ return marker.parent === parentStateNode;
1138
+ }
1139
+ function hasDescendantState(stateNodes, parentStateNode) {
1140
+ for (const stateNode of stateNodes) {
1141
+ if (isDescendant(stateNode, parentStateNode)) {
1142
+ return true;
1143
+ }
1144
+ }
1145
+ return false;
1146
+ }
1147
+ function hasIntersection(s1, s2) {
1148
+ const s1Size = s1 instanceof Set ? s1.size : Array.isArray(s1) ? s1.length : undefined;
1149
+ const s2Size = s2 instanceof Set ? s2.size : Array.isArray(s2) ? s2.length : undefined;
1150
+ if (s1Size !== undefined && s2Size !== undefined && s2Size < s1Size) {
1151
+ [s1, s2] = [s2, s1];
1152
+ }
1153
+ const set1 = s1 instanceof Set ? s1 : new Set(s1);
1154
+ for (const item of s2) {
1155
+ if (set1.has(item)) {
1156
+ return true;
1157
+ }
1158
+ }
1159
+ return false;
1160
+ }
1161
+ function removeConflictingTransitions(enabledTransitions, stateNodeSet, snapshot, event, actorScope) {
1162
+ const filteredTransitions = new Set();
1163
+ const exitSets = new Map();
1164
+ const getExitSet = transition => {
1165
+ let exitSet = exitSets.get(transition);
1166
+ if (!exitSet) {
1167
+ exitSet = computeExitSet([transition], stateNodeSet, snapshot, event, actorScope);
1168
+ exitSets.set(transition, exitSet);
1169
+ }
1170
+ return exitSet;
1171
+ };
1172
+ for (const t1 of enabledTransitions) {
1173
+ let t1Preempted = false;
1174
+ const transitionsToRemove = new Set();
1175
+ for (const t2 of filteredTransitions) {
1176
+ if (hasIntersection(getExitSet(t1), getExitSet(t2))) {
1177
+ if (isDescendant(t1.source, t2.source)) {
1178
+ transitionsToRemove.add(t2);
1179
+ } else {
1180
+ t1Preempted = true;
1181
+ break;
1182
+ }
1183
+ }
1184
+ }
1185
+ if (!t1Preempted) {
1186
+ for (const t3 of transitionsToRemove) {
1187
+ filteredTransitions.delete(t3);
1188
+ }
1189
+ filteredTransitions.add(t1);
1190
+ }
1191
+ }
1192
+ return Array.from(filteredTransitions);
1193
+ }
1194
+ function getEffectiveTargetStates(transition, snapshot, event, actorScope) {
1195
+ const historyValue = snapshot.historyValue;
1196
+ const {
1197
+ targets
1198
+ } = getTransitionResult(transition, snapshot, event, actorScope);
1199
+ if (!targets) {
1200
+ return [];
1201
+ }
1202
+ const targetSet = new Set();
1203
+ for (const targetNode of targets) {
1204
+ if (isHistoryNode(targetNode)) {
1205
+ if (historyValue[targetNode.id]) {
1206
+ for (const node of historyValue[targetNode.id]) {
1207
+ targetSet.add(node);
1208
+ }
1209
+ } else {
1210
+ for (const node of getEffectiveTargetStates(resolveHistoryDefaultTransition(targetNode), snapshot, event, actorScope)) {
1211
+ targetSet.add(node);
1212
+ }
1213
+ }
1214
+ } else {
1215
+ targetSet.add(targetNode);
1216
+ }
1217
+ }
1218
+ return [...targetSet];
1219
+ }
1220
+ function getTransitionDomain(transition, snapshot, event, actorScope) {
1221
+ const targetStates = getEffectiveTargetStates(transition, snapshot, event, actorScope);
1222
+ const {
1223
+ reenter
1224
+ } = getTransitionResult(transition, snapshot, event, actorScope);
1225
+ if (!reenter && targetStates.every(target => target === transition.source || isDescendant(target, transition.source))) {
1226
+ return transition.source;
1227
+ }
1228
+ const [head, ...tail] = targetStates.concat(transition.source);
1229
+ // Find the least common ancestor (LCA) of the source and effective targets.
1230
+ for (const ancestor of getProperAncestors(head, undefined)) {
1231
+ if (tail.every(sn => isDescendant(sn, ancestor))) {
1232
+ return ancestor;
1233
+ }
1234
+ }
1235
+
1236
+ // at this point we know that it's a root transition since LCA couldn't be found
1237
+ if (reenter) {
1238
+ return;
1239
+ }
1240
+ return transition.source.machine.root;
1241
+ }
1242
+ function computeExitSet(transitions, stateNodeSet, snapshot, event, actorScope) {
1243
+ const statesToExit = new Set();
1244
+ for (const transition of transitions) {
1245
+ const {
1246
+ targets
1247
+ } = getTransitionResult(transition, snapshot, event, actorScope);
1248
+ if (targets?.length) {
1249
+ const domain = getTransitionDomain(transition, snapshot, event, actorScope);
1250
+ if (transition.reenter && transition.source === domain) {
1251
+ statesToExit.add(domain);
1252
+ }
1253
+ for (const stateNode of stateNodeSet) {
1254
+ if (isDescendant(stateNode, domain)) {
1255
+ statesToExit.add(stateNode);
1256
+ }
1257
+ }
1258
+ }
1259
+ }
1260
+ return [...statesToExit];
1261
+ }
1262
+ function initialMicrostep(root, preInitialState, actorScope, initEvent, internalQueue) {
1263
+ return microstep([{
1264
+ target: [...getInitialStateNodes(root)],
1265
+ source: root,
1266
+ reenter: true,
1267
+ eventType: null,
1268
+ toJSON: null
1269
+ }], preInitialState, actorScope, initEvent, true, internalQueue);
1270
+ }
1271
+ function mergeContextPatch(context, patch) {
1272
+ return {
1273
+ ...context,
1274
+ ...patch
1275
+ };
1276
+ }
1277
+
1278
+ /** https://www.w3.org/TR/scxml/#microstepProcedure */
1279
+ function microstep(transitions, currentSnapshot, actorScope, event, isInitial, internalQueue) {
1280
+ const executableActions = [];
1281
+ if (!transitions.length) {
1282
+ return [currentSnapshot, executableActions];
1283
+ }
1284
+ {
1285
+ const mutStateNodeSet = new Set(currentSnapshot._nodes);
1286
+ let historyValue = currentSnapshot.historyValue;
1287
+ const originalContext = currentSnapshot.context;
1288
+ const filteredTransitions = removeConflictingTransitions(transitions, mutStateNodeSet, currentSnapshot, event, actorScope);
1289
+ const getCurrentTransitionResult = transition => getTransitionResult(transition, currentSnapshot, event, actorScope);
1290
+ const getStateActionsAndContext = (transitionFn, context, children, input) => {
1291
+ if (transitionFn.length === 2) {
1292
+ // enqueue action; retrieve
1293
+ const actions = [];
1294
+ const internalEvents = [];
1295
+ let updatedContext;
1296
+ const enqueue = createTransitionEnqueue(actorScope, actions, internalEvents, true);
1297
+ const res = transitionFn({
1298
+ context,
1299
+ event,
1300
+ parent: actorScope.self._parent,
1301
+ self: actorScope.self,
1302
+ children,
1303
+ system: actorScope.system,
1304
+ actions: currentSnapshot.machine.implementations.actions,
1305
+ actorSources: currentSnapshot.machine.implementations.actorSources,
1306
+ guards: currentSnapshot.machine.implementations.guards,
1307
+ delays: currentSnapshot.machine.implementations.delays,
1308
+ input
1309
+ }, enqueue);
1310
+ if (res?.context !== undefined) {
1311
+ updatedContext = mergeContextPatch(context, res.context);
1312
+ }
1313
+ return [actions, updatedContext, internalEvents];
1314
+ }
1315
+
1316
+ // For 1-argument actions, wrap them to include input
1317
+ // Preserve _special flag if present (for entry/exit actions)
1318
+ const wrappedAction = Object.assign((args, enqueue) => transitionFn({
1319
+ ...args,
1320
+ input
1321
+ }, enqueue), '_special' in transitionFn ? {
1322
+ _special: true
1323
+ } : {});
1324
+ return [[wrappedAction], undefined, undefined];
1325
+ };
1326
+ let nextState = currentSnapshot;
1327
+ const exitStates = () => {
1328
+ const statesToExit = computeExitSet(filteredTransitions, mutStateNodeSet, currentSnapshot, event, actorScope);
1329
+ statesToExit.sort((a, b) => b.order - a.order);
1330
+ let changedHistory;
1331
+ const currentStateNodes = [...mutStateNodeSet];
1332
+
1333
+ // From SCXML algorithm: https://www.w3.org/TR/scxml/#exitStates
1334
+ for (const exitStateNode of statesToExit) {
1335
+ for (const historyNode of Object.values(exitStateNode.states)) {
1336
+ if (historyNode.type !== 'history') {
1337
+ continue;
1338
+ }
1339
+ const predicate = historyNode.history === 'deep' ? sn => isAtomicStateNode(sn) && isDescendant(sn, exitStateNode) : sn => sn.parent === exitStateNode;
1340
+ changedHistory ??= {
1341
+ ...historyValue
1342
+ };
1343
+ changedHistory[historyNode.id] = currentStateNodes.filter(predicate);
1344
+ }
1345
+ }
1346
+ for (const exitStateNode of statesToExit) {
1347
+ const stateInput = currentSnapshot._stateInputs?.[exitStateNode.id];
1348
+ const [exitActions, nextContext, internalEvents] = exitStateNode.exit ? getStateActionsAndContext(exitStateNode.exit, nextState.context, currentSnapshot.children, stateInput) : [[]];
1349
+ if (internalEvents?.length) {
1350
+ internalQueue.push(...internalEvents);
1351
+ }
1352
+ if (nextContext) {
1353
+ nextState = cloneMachineSnapshot(nextState, {
1354
+ context: nextContext
1355
+ });
1356
+ }
1357
+ const [resolvedState, resolvedActions] = resolveActionsWithContext(nextState, event, actorScope, exitActions);
1358
+ nextState = resolvedState;
1359
+ executableActions.push(...resolvedActions);
1360
+ for (const def of exitStateNode.invoke) {
1361
+ const childActor = nextState.children[def.id];
1362
+ if (childActor && !childActor._isExternal) {
1363
+ actorScope.stopChild(childActor);
1364
+ }
1365
+ delete nextState.children[def.id];
1366
+ }
1367
+ mutStateNodeSet.delete(exitStateNode);
1368
+ }
1369
+ historyValue = changedHistory || historyValue;
1370
+ };
1371
+
1372
+ // Exit states
1373
+ if (!isInitial) {
1374
+ exitStates();
1375
+ }
1376
+ let context = nextState.context;
1377
+ const transitionActions = [];
1378
+ const internalEvents = [];
1379
+ for (const t of filteredTransitions) {
1380
+ if (t.actions) {
1381
+ transitionActions.push(...toArray(t.actions));
1382
+ }
1383
+ const res = getCurrentTransitionResult(t);
1384
+ if (res.context !== undefined) {
1385
+ context = mergeContextPatch(context, res.context);
1386
+ }
1387
+ if (res.actions) {
1388
+ transitionActions.push(...res.actions);
1389
+ }
1390
+ if (res.internalEvents) {
1391
+ internalEvents.push(...res.internalEvents);
1392
+ }
1393
+ }
1394
+ if (internalEvents.length) {
1395
+ internalQueue.push(...internalEvents);
1396
+ }
1397
+ const enterStates = () => {
1398
+ const getMachineOutput = rootCompletionNode => {
1399
+ const rootNode = nextState.machine.root;
1400
+ if (rootNode.output === undefined) {
1401
+ return;
1402
+ }
1403
+ let completionOutput;
1404
+ if (rootCompletionNode.output !== undefined && rootCompletionNode.parent) {
1405
+ completionOutput = resolveOutput(rootCompletionNode.output, nextState.context, event, actorScope.self);
1406
+ } else if (rootCompletionNode.type === 'parallel') {
1407
+ const parallelDoneType = `xstate.done.state.${rootCompletionNode.id}`;
1408
+ const parallelDoneEvent = internalQueue.find(e => e.type === parallelDoneType);
1409
+ completionOutput = parallelDoneEvent?.output;
1410
+ }
1411
+ return resolveOutput(rootNode.output, nextState.context, createDoneStateEvent(rootCompletionNode.id, completionOutput), actorScope.self);
1412
+ };
1413
+ const statesToEnter = new Set();
1414
+ // those are states that were directly targeted or indirectly targeted by the explicit target
1415
+ // in other words, those are states for which initial actions should be executed
1416
+ // when we target `#deep_child` initial actions of its ancestors shouldn't be executed
1417
+ const statesForDefaultEntry = new Set();
1418
+ const addAncestorStatesToEnter = (ancestors, reentrancyDomain) => {
1419
+ for (const anc of ancestors) {
1420
+ if (!reentrancyDomain || isDescendant(anc, reentrancyDomain)) {
1421
+ statesToEnter.add(anc);
1422
+ }
1423
+ if (anc.type === 'parallel') {
1424
+ for (const child of getChildren(anc)) {
1425
+ if (!hasDescendantState(statesToEnter, child)) {
1426
+ statesToEnter.add(child);
1427
+ addDescendantStatesToEnter(child);
1428
+ }
1429
+ }
1430
+ }
1431
+ }
1432
+ };
1433
+ const addDescendantStatesToEnter = stateNode => {
1434
+ if (isHistoryNode(stateNode)) {
1435
+ if (historyValue[stateNode.id]) {
1436
+ const historyStateNodes = historyValue[stateNode.id];
1437
+ for (const s of historyStateNodes) {
1438
+ statesToEnter.add(s);
1439
+ addDescendantStatesToEnter(s);
1440
+ }
1441
+ for (const s of historyStateNodes) {
1442
+ addAncestorStatesToEnter(getProperAncestors(s, stateNode.parent), undefined);
1443
+ }
1444
+ } else {
1445
+ const historyDefaultTransition = resolveHistoryDefaultTransition(stateNode);
1446
+ const {
1447
+ targets
1448
+ } = getCurrentTransitionResult(historyDefaultTransition);
1449
+ for (const s of targets ?? []) {
1450
+ statesToEnter.add(s);
1451
+ if (historyDefaultTransition === stateNode.parent?.initial) {
1452
+ statesForDefaultEntry.add(stateNode.parent);
1453
+ }
1454
+ addDescendantStatesToEnter(s);
1455
+ }
1456
+ for (const s of targets ?? []) {
1457
+ addAncestorStatesToEnter(getProperAncestors(s, stateNode.parent), undefined);
1458
+ }
1459
+ }
1460
+ return;
1461
+ }
1462
+ if (stateNode.type === 'compound') {
1463
+ const [initialState] = getCurrentTransitionResult(stateNode.initial).targets;
1464
+ if (!isHistoryNode(initialState)) {
1465
+ statesToEnter.add(initialState);
1466
+ statesForDefaultEntry.add(initialState);
1467
+ }
1468
+ addDescendantStatesToEnter(initialState);
1469
+ addAncestorStatesToEnter(getProperAncestors(initialState, stateNode), undefined);
1470
+ return;
1471
+ }
1472
+ if (stateNode.type === 'parallel') {
1473
+ for (const child of getChildren(stateNode)) {
1474
+ if (!hasDescendantState(statesToEnter, child)) {
1475
+ statesToEnter.add(child);
1476
+ statesForDefaultEntry.add(child);
1477
+ addDescendantStatesToEnter(child);
1478
+ }
1479
+ }
1480
+ }
1481
+ };
1482
+ for (const transition of filteredTransitions) {
1483
+ const domain = getTransitionDomain(transition, currentSnapshot, event, actorScope);
1484
+ const {
1485
+ targets,
1486
+ reenter
1487
+ } = getCurrentTransitionResult(transition);
1488
+ for (const targetNode of targets ?? []) {
1489
+ if (!isHistoryNode(targetNode) && (transition.source !== targetNode || transition.source !== domain || reenter)) {
1490
+ statesToEnter.add(targetNode);
1491
+ statesForDefaultEntry.add(targetNode);
1492
+ }
1493
+ addDescendantStatesToEnter(targetNode);
1494
+ }
1495
+ const targetStates = getEffectiveTargetStates(transition, currentSnapshot, event, actorScope);
1496
+ for (const s of targetStates) {
1497
+ const ancestors = getProperAncestors(s, domain);
1498
+ if (domain?.type === 'parallel') {
1499
+ ancestors.push(domain);
1500
+ }
1501
+ addAncestorStatesToEnter(ancestors, !transition.source.parent && reenter ? undefined : domain);
1502
+ }
1503
+ }
1504
+ if (isInitial) {
1505
+ statesForDefaultEntry.add(currentSnapshot.machine.root);
1506
+ }
1507
+ const stateInputMap = {
1508
+ ...currentSnapshot._stateInputs
1509
+ };
1510
+ for (const transition of filteredTransitions) {
1511
+ const {
1512
+ targets,
1513
+ input
1514
+ } = getCurrentTransitionResult(transition);
1515
+ if (input && targets) {
1516
+ for (const targetNode of targets) {
1517
+ stateInputMap[targetNode.id] = input;
1518
+ }
1519
+ }
1520
+ }
1521
+ const completedNodes = new Set();
1522
+ const children = {
1523
+ ...currentSnapshot.children
1524
+ };
1525
+ for (const stateNodeToEnter of [...statesToEnter].sort((a, b) => a.order - b.order)) {
1526
+ mutStateNodeSet.add(stateNodeToEnter);
1527
+ const actions = [];
1528
+ let invoked = false;
1529
+ for (const invokeDef of stateNodeToEnter.invoke) {
1530
+ invoked = true;
1531
+ let src = invokeDef.logic;
1532
+ if (typeof src === 'function') {
1533
+ src = src({
1534
+ actorSources: currentSnapshot.machine.implementations.actorSources,
1535
+ context: nextState.context,
1536
+ event,
1537
+ self: actorScope.self
1538
+ });
1539
+ }
1540
+ const logic = typeof src === 'string' ? resolveReferencedActor(currentSnapshot.machine, src) : src;
1541
+ if (!logic) {
1542
+ throw new Error(`Actor logic '${typeof src === 'string' ? src : 'inline'}' not implemented in machine '${currentSnapshot.machine.id}'`);
1543
+ }
1544
+ const input = typeof invokeDef.input === 'function' ? invokeDef.input({
1545
+ self: actorScope.self,
1546
+ context: nextState.context,
1547
+ event,
1548
+ output: getEventOutput(event)
1549
+ }) : invokeDef.input;
1550
+ const actor = createActor(logic, {
1551
+ ...invokeDef,
1552
+ input,
1553
+ parent: actorScope.self,
1554
+ syncSnapshot: !!invokeDef.onSnapshot
1555
+ });
1556
+ actions.push({
1557
+ action: builtInActions['@xstate.start'],
1558
+ args: [actor]
1559
+ });
1560
+ if (invokeDef.id) {
1561
+ children[invokeDef.id] = actor;
1562
+ }
1563
+ }
1564
+ if (invoked) {
1565
+ nextState = cloneMachineSnapshot(nextState, {
1566
+ children
1567
+ });
1568
+ }
1569
+ let context = nextState.context;
1570
+ let contextChangedByEntry = false;
1571
+ const stateInput = stateInputMap[stateNodeToEnter.id];
1572
+ if (stateNodeToEnter.entry) {
1573
+ const [resultActions, nextContext, nextInternalEvents] = getStateActionsAndContext(stateNodeToEnter.entry, context, children, stateInput);
1574
+ actions.push(...resultActions);
1575
+ if (nextInternalEvents?.length) {
1576
+ internalQueue.push(...nextInternalEvents);
1577
+ }
1578
+ if (nextContext) {
1579
+ context = nextContext;
1580
+ contextChangedByEntry = true;
1581
+ }
1582
+ }
1583
+ if (contextChangedByEntry) {
1584
+ nextState.context = context;
1585
+ }
1586
+ if (statesForDefaultEntry.has(stateNodeToEnter)) {
1587
+ const {
1588
+ actions: initialActions,
1589
+ input: initialInput
1590
+ } = getTransitionResult(stateNodeToEnter.initial, nextState, event, actorScope);
1591
+ if (initialActions) {
1592
+ actions.push(...initialActions);
1593
+ }
1594
+ if (initialInput && stateNodeToEnter.initial?.target) {
1595
+ for (const targetNode of stateNodeToEnter.initial.target) {
1596
+ stateInputMap[targetNode.id] = initialInput;
1597
+ }
1598
+ }
1599
+ }
1600
+ const [resolvedState, resolvedActions] = resolveActionsWithContext(nextState, event, actorScope, actions);
1601
+ nextState = resolvedState;
1602
+ actions.length = 0;
1603
+ executableActions.push(...resolvedActions);
1604
+ if (stateNodeToEnter.type !== 'final') {
1605
+ continue;
1606
+ }
1607
+ const parent = stateNodeToEnter.parent;
1608
+ let ancestorMarker = parent?.type === 'parallel' ? parent : parent?.parent;
1609
+ let rootCompletionNode = ancestorMarker || stateNodeToEnter;
1610
+ if (parent?.type === 'compound') {
1611
+ internalQueue.push(createDoneStateEvent(parent.id, stateNodeToEnter.output !== undefined ? resolveOutput(stateNodeToEnter.output, nextState.context, event, actorScope.self) : undefined));
1612
+ }
1613
+ while (ancestorMarker?.type === 'parallel' && !completedNodes.has(ancestorMarker) && isInFinalState(mutStateNodeSet, ancestorMarker)) {
1614
+ completedNodes.add(ancestorMarker);
1615
+ const regionOutput = {};
1616
+ for (const region of getChildren(ancestorMarker)) {
1617
+ if (region.type === 'final') {
1618
+ regionOutput[region.key] = region.output !== undefined ? resolveOutput(region.output, nextState.context, event, actorScope.self) : undefined;
1619
+ continue;
1620
+ }
1621
+ if (region.type === 'parallel') {
1622
+ const regionDoneType = `xstate.done.state.${region.id}`;
1623
+ const regionDoneEvent = internalQueue.find(e => e.type === regionDoneType);
1624
+ regionOutput[region.key] = regionDoneEvent?.output;
1625
+ continue;
1626
+ }
1627
+ const finalChild = getChildren(region).find(s => s.type === 'final' && mutStateNodeSet.has(s));
1628
+ regionOutput[region.key] = finalChild?.output !== undefined ? resolveOutput(finalChild.output, nextState.context, event, actorScope.self) : undefined;
1629
+ }
1630
+ internalQueue.push(createDoneStateEvent(ancestorMarker.id, regionOutput));
1631
+ rootCompletionNode = ancestorMarker;
1632
+ ancestorMarker = ancestorMarker.parent;
1633
+ }
1634
+ if (ancestorMarker) {
1635
+ continue;
1636
+ }
1637
+ nextState = cloneMachineSnapshot(nextState, {
1638
+ status: 'done',
1639
+ output: getMachineOutput(rootCompletionNode)
1640
+ });
1641
+ }
1642
+ const inputChanged = JSON.stringify(stateInputMap) !== JSON.stringify(currentSnapshot._stateInputs || {});
1643
+ if (inputChanged) {
1644
+ nextState = cloneMachineSnapshot(nextState, {
1645
+ _stateInputs: stateInputMap
1646
+ });
1647
+ }
1648
+ };
1649
+
1650
+ // Execute transition content
1651
+ const [resolvedTransitionState, transitionExecutableActions] = resolveActionsWithContext(nextState, event, actorScope, transitionActions);
1652
+ nextState = resolvedTransitionState;
1653
+ executableActions.push(...transitionExecutableActions);
1654
+ if (context && context !== currentSnapshot.context) {
1655
+ nextState = cloneMachineSnapshot(nextState, {
1656
+ context
1657
+ });
1658
+ }
1659
+
1660
+ // Enter states
1661
+ enterStates();
1662
+ const nextStateNodes = [...mutStateNodeSet];
1663
+ if (nextState.status === 'done') {
1664
+ const allExitActions = [];
1665
+ const nextStateNodesToExit = nextStateNodes.sort((a, b) => b.order - a.order);
1666
+ nextStateNodesToExit.forEach(stateNode => {
1667
+ if (stateNode.exit) {
1668
+ const stateInput = nextState._stateInputs?.[stateNode.id];
1669
+ const [exitActions,, nextInternalEvents] = getStateActionsAndContext(stateNode.exit, nextState.context, nextState.children, stateInput);
1670
+ allExitActions.push(...exitActions);
1671
+ if (nextInternalEvents?.length) {
1672
+ internalQueue.push(...nextInternalEvents);
1673
+ }
1674
+ }
1675
+ });
1676
+ const [resolvedState, resolvedActions] = resolveActionsWithContext(nextState, event, actorScope, allExitActions);
1677
+ nextState = resolvedState;
1678
+ executableActions.push(...resolvedActions);
1679
+ }
1680
+ if (historyValue === currentSnapshot.historyValue && currentSnapshot._nodes.length === mutStateNodeSet.size && currentSnapshot._nodes.every(node => mutStateNodeSet.has(node))) {
1681
+ // If context was changed (e.g. by entry actions during self-transition),
1682
+ // clone to ensure reference inequality for eventless transition re-evaluation
1683
+ if (nextState.context !== originalContext) {
1684
+ return [cloneMachineSnapshot(nextState), executableActions];
1685
+ }
1686
+ return [nextState, executableActions];
1687
+ }
1688
+ return [cloneMachineSnapshot(nextState, {
1689
+ _nodes: nextStateNodes,
1690
+ historyValue
1691
+ }), executableActions];
1692
+ }
1693
+ }
1694
+
1695
+ /**
1696
+ * Gets the transition result for a given transition without executing the
1697
+ * transition.
1698
+ */
1699
+ function getTransitionResult(transition, snapshot, event, actorScope) {
1700
+ if (transition.to) {
1701
+ const actions = [];
1702
+ const internalEvents = [];
1703
+ const enqueue = createTransitionEnqueue(actorScope, actions, internalEvents);
1704
+ const res = transition.to({
1705
+ context: snapshot.context,
1706
+ event,
1707
+ output: getEventOutput(event),
1708
+ value: snapshot.value,
1709
+ children: snapshot.children,
1710
+ system: actorScope.system,
1711
+ parent: actorScope.self._parent,
1712
+ self: actorScope.self,
1713
+ actions: snapshot.machine.implementations.actions,
1714
+ actorSources: snapshot.machine.implementations.actorSources,
1715
+ guards: snapshot.machine.implementations.guards,
1716
+ delays: snapshot.machine.implementations.delays
1717
+ }, enqueue);
1718
+ const targets = res?.target ? resolveTarget(transition.source, toArray(res.target)) : undefined;
1719
+ // Resolve input for .to transitions
1720
+ const resolvedInput = typeof transition.input === 'function' ? transition.input({
1721
+ context: snapshot.context,
1722
+ event,
1723
+ output: getEventOutput(event)
1724
+ }) : transition.input;
1725
+ return {
1726
+ targets: targets,
1727
+ context: res?.context,
1728
+ reenter: res?.reenter,
1729
+ actions,
1730
+ internalEvents,
1731
+ input: resolvedInput
1732
+ };
1733
+ }
1734
+
1735
+ // Resolve input for regular transitions
1736
+ const resolvedInput = typeof transition.input === 'function' ? transition.input({
1737
+ context: snapshot.context,
1738
+ event,
1739
+ output: getEventOutput(event)
1740
+ }) : transition.input;
1741
+ return {
1742
+ targets: transition.target,
1743
+ context: transition.context,
1744
+ reenter: transition.reenter,
1745
+ actions: undefined,
1746
+ internalEvents: undefined,
1747
+ input: resolvedInput
1748
+ };
1749
+ }
1750
+ function macrostep(snapshot, event, actorScope, internalQueue) {
1751
+ let nextSnapshot = snapshot;
1752
+ const microsteps = [];
1753
+ function addMicrostep(step, transitions) {
1754
+ // collect microsteps; surfaced on the enclosing '@xstate.transition' event
1755
+ // via its `microsteps[]` facet (there is no standalone microstep event)
1756
+ const collectedMicrosteps = actorScope.self._collectedMicrosteps || [];
1757
+ collectedMicrosteps.push(...transitions);
1758
+ actorScope.self._collectedMicrosteps = collectedMicrosteps;
1759
+ microsteps.push(step);
1760
+ }
1761
+
1762
+ // Handle stop event
1763
+ if (event.type === XSTATE_STOP) {
1764
+ nextSnapshot = cloneMachineSnapshot(stopChildren(nextSnapshot, actorScope), {
1765
+ status: 'stopped'
1766
+ });
1767
+ addMicrostep([nextSnapshot, []], []);
1768
+ return {
1769
+ snapshot: nextSnapshot,
1770
+ microsteps
1771
+ };
1772
+ }
1773
+ let nextEvent = event;
1774
+
1775
+ // Assume the state is at rest (no raised events)
1776
+ // Determine the next state based on the next microstep
1777
+ if (nextEvent.type !== XSTATE_INIT) {
1778
+ const currentEvent = nextEvent;
1779
+ const isErr = isErrorActorEvent(currentEvent);
1780
+ const transitions = nextSnapshot.machine.getTransitionData(nextSnapshot, currentEvent, actorScope.self);
1781
+ if (isErr && !transitions.length) {
1782
+ // TODO: we should likely only allow transitions selected by very explicit descriptors
1783
+ // `*` shouldn't be matched, likely `xstate.error.*` shouldn't be either
1784
+ // similarly `xstate.error.actor.*` and `xstate.error.actor.todo.*` have to be considered too
1785
+ nextSnapshot = cloneMachineSnapshot(snapshot, {
1786
+ status: 'error',
1787
+ error: currentEvent.error
1788
+ });
1789
+ addMicrostep([nextSnapshot, []], []);
1790
+ return {
1791
+ snapshot: nextSnapshot,
1792
+ microsteps
1793
+ };
1794
+ }
1795
+ const step = microstep(transitions, snapshot, actorScope, nextEvent, false,
1796
+ // isInitial
1797
+ internalQueue);
1798
+ nextSnapshot = step[0];
1799
+ addMicrostep(step, transitions);
1800
+ }
1801
+ let shouldSelectEventlessTransitions = true;
1802
+ const maxIterations = snapshot.machine.options?.maxIterations ?? Infinity;
1803
+ let iterationCount = 0;
1804
+ let microstepCount = 0;
1805
+ while (nextSnapshot.status === 'active') {
1806
+ microstepCount++;
1807
+ if (microstepCount > 1000) {
1808
+ throw new Error('Microstep count exceeded 1000');
1809
+ }
1810
+ iterationCount++;
1811
+ if (iterationCount > maxIterations) {
1812
+ throw new Error(`Infinite loop detected: the machine has processed more than ${maxIterations} microsteps without reaching a stable state. This usually happens when there's a cycle of transitions (e.g., eventless transitions or raised events causing state A -> B -> C -> A).` );
1813
+ }
1814
+ let enabledTransitions = shouldSelectEventlessTransitions ? selectEventlessTransitions(nextSnapshot, nextEvent, actorScope) : [];
1815
+
1816
+ // eventless transitions should always be selected after selecting *regular* transitions
1817
+ // by assigning `undefined` to `previousState` we ensure that `shouldSelectEventlessTransitions` gets always computed to true in such a case
1818
+ const previousState = enabledTransitions.length ? nextSnapshot : undefined;
1819
+ if (!enabledTransitions.length) {
1820
+ if (!internalQueue.length) {
1821
+ break;
1822
+ }
1823
+ nextEvent = internalQueue.shift();
1824
+ enabledTransitions = nextSnapshot.machine.getTransitionData(nextSnapshot, nextEvent, actorScope.self);
1825
+ }
1826
+ const step = microstep(enabledTransitions, nextSnapshot, actorScope, nextEvent, false, internalQueue);
1827
+ nextSnapshot = step[0];
1828
+ shouldSelectEventlessTransitions = nextSnapshot !== previousState;
1829
+ addMicrostep(step, enabledTransitions);
1830
+ }
1831
+ if (nextSnapshot.status !== 'active' && nextSnapshot.children) {
1832
+ stopChildren(nextSnapshot, actorScope);
1833
+ }
1834
+ return {
1835
+ snapshot: nextSnapshot,
1836
+ microsteps
1837
+ };
1838
+ }
1839
+
1840
+ /**
1841
+ * Resolves a partial state value with its full representation in the state
1842
+ * node's machine.
1843
+ *
1844
+ * @param stateValue The partial state value to resolve.
1845
+ */
1846
+ function resolveStateValue(rootNode, stateValue) {
1847
+ const allStateNodes = getAllStateNodes(getStateNodes(rootNode, stateValue));
1848
+ return getStateValue(rootNode, allStateNodes);
1849
+ }
1850
+ function createEnqueueObject(props, action) {
1851
+ const enqueueFn = (fn, ...args) => {
1852
+ action(fn, ...args);
1853
+ };
1854
+ Object.assign(enqueueFn, {
1855
+ cancel: () => {},
1856
+ emit: () => {},
1857
+ log: () => {},
1858
+ raise: () => {},
1859
+ spawn: () => ({}),
1860
+ sendTo: () => {},
1861
+ stop: () => {},
1862
+ listen: () => ({}),
1863
+ subscribeTo: () => ({}),
1864
+ ...props
1865
+ });
1866
+ return enqueueFn;
1867
+ }
1868
+ function hasEffect(transition, context, event, snapshot, self) {
1869
+ if (transition.to) {
1870
+ return transitionToHasEffect(transition.to, context, event, snapshot, self, snapshot.machine.implementations);
1871
+ }
1872
+ return false;
1873
+ }
1874
+ function transitionToHasEffect(transitionTo, context, event, snapshot, self, implementations) {
1875
+ let hasEffect = false;
1876
+ let res;
1877
+ try {
1878
+ const triggerEffect = () => {
1879
+ hasEffect = true;
1880
+ throw new Error('Effect triggered');
1881
+ };
1882
+ res = transitionTo({
1883
+ context,
1884
+ event,
1885
+ output: getEventOutput(event),
1886
+ self,
1887
+ system: self.system,
1888
+ value: snapshot.value,
1889
+ children: snapshot.children,
1890
+ parent: {
1891
+ send: triggerEffect
1892
+ },
1893
+ actions: implementations.actions,
1894
+ actorSources: implementations.actorSources,
1895
+ guards: implementations.guards,
1896
+ delays: implementations.delays
1897
+ }, createEnqueueObject({
1898
+ emit: triggerEffect,
1899
+ cancel: triggerEffect,
1900
+ log: triggerEffect,
1901
+ raise: triggerEffect,
1902
+ spawn: triggerEffect,
1903
+ sendTo: triggerEffect,
1904
+ stop: triggerEffect
1905
+ }, triggerEffect));
1906
+ } catch (err) {
1907
+ if (hasEffect) {
1908
+ return true;
1909
+ }
1910
+ throw err;
1911
+ }
1912
+ return res !== undefined;
1913
+ }
1914
+ function stopChildren(snapshot, actorScope) {
1915
+ let children;
1916
+ if (!snapshot.children || (children = Object.values(snapshot.children).filter(Boolean)).length === 0) {
1917
+ return snapshot;
1918
+ }
1919
+ for (const child of children) {
1920
+ actorScope.stopChild(child);
1921
+ }
1922
+ return cloneMachineSnapshot(snapshot, {
1923
+ children: {}
1924
+ });
1925
+ }
1926
+ function selectEventlessTransitions(snapshot, event, actorScope) {
1927
+ const enabledTransitionSet = new Set();
1928
+ const atomicStates = snapshot._nodes.filter(isAtomicStateNode);
1929
+ for (const atomicStateNode of atomicStates) {
1930
+ loop: for (let stateNode = atomicStateNode; stateNode; stateNode = stateNode.parent) {
1931
+ if (!stateNode.always) {
1932
+ continue;
1933
+ }
1934
+ for (const transition of stateNode.always) {
1935
+ if (evaluateCandidate(transition, event, snapshot, stateNode, actorScope.self)) {
1936
+ enabledTransitionSet.add(transition);
1937
+ break loop;
1938
+ }
1939
+ }
1940
+ }
1941
+ }
1942
+ return removeConflictingTransitions(Array.from(enabledTransitionSet), new Set(snapshot._nodes), snapshot, event, actorScope);
1943
+ }
1944
+ function evaluateCandidate(candidate, event, snapshot, stateNode, self) {
1945
+ if (candidate.guard) {
1946
+ const guardArgs = {
1947
+ context: snapshot.context,
1948
+ event,
1949
+ output: getEventOutput(event),
1950
+ self,
1951
+ parent: self._parent,
1952
+ children: snapshot.children,
1953
+ actions: stateNode.machine.implementations.actions,
1954
+ actorSources: stateNode.machine.implementations.actorSources,
1955
+ guards: stateNode.machine.implementations.guards,
1956
+ delays: stateNode.machine.implementations.delays,
1957
+ _snapshot: snapshot
1958
+ };
1959
+ if (!candidate.guard(guardArgs)) {
1960
+ return false;
1961
+ }
1962
+ }
1963
+ if (candidate.to) {
1964
+ return transitionToHasEffect(candidate.to, snapshot.context, event, snapshot, self, stateNode.machine.implementations);
1965
+ }
1966
+ return true;
1967
+ }
1968
+
1969
+ function isMachineSnapshot(value) {
1970
+ return !!value && typeof value === 'object' && 'machine' in value && 'value' in value;
1971
+ }
1972
+ let emptyCanActor;
1973
+ let emptyCanActorScope;
1974
+ function getEmptyCanActor() {
1975
+ return emptyCanActor ??= createEmptyActor();
1976
+ }
1977
+ function getEmptyCanActorScope() {
1978
+ if (emptyCanActorScope) {
1979
+ return emptyCanActorScope;
1980
+ }
1981
+ const actor = getEmptyCanActor();
1982
+ emptyCanActorScope = {
1983
+ self: actor,
1984
+ logger: () => {},
1985
+ id: '',
1986
+ sessionId: '',
1987
+ defer: () => {},
1988
+ system: actor.system,
1989
+ stopChild: () => {},
1990
+ emit: () => {},
1991
+ actionExecutor: () => {}
1992
+ };
1993
+ return emptyCanActorScope;
1994
+ }
1995
+ const machineSnapshotMatches = function matches(testValue) {
1996
+ return matchesState(testValue, this.value);
1997
+ };
1998
+ const machineSnapshotHasTag = function hasTag(tag) {
1999
+ return this.tags.has(tag);
2000
+ };
2001
+ const machineSnapshotCan = function can(event) {
2002
+ if (!this.machine) {
2003
+ console.warn(`state.can(...) used outside of a machine-created State object; this will always return false.`);
2004
+ }
2005
+ const emptyActor = getEmptyCanActor();
2006
+ const emptyActorScope = getEmptyCanActorScope();
2007
+ const transitionData = this.machine.getTransitionData(this, event, emptyActor);
2008
+ if (!transitionData?.length) {
2009
+ return false;
2010
+ }
2011
+
2012
+ // Check that at least one transition is not forbidden
2013
+ for (const transition of transitionData) {
2014
+ if (transition.target !== undefined) {
2015
+ return true;
2016
+ }
2017
+ const res = getTransitionResult(transition, this, event, emptyActorScope);
2018
+ if (res.targets?.length || res.context || hasEffect(transition, this.context, event, this, emptyActor)) {
2019
+ return true;
2020
+ }
2021
+ }
2022
+ return false;
2023
+ };
2024
+ const machineSnapshotToJSON = function toJSON() {
2025
+ const {
2026
+ _nodes: nodes,
2027
+ _stateInputs,
2028
+ tags,
2029
+ machine,
2030
+ getMeta,
2031
+ getInputs,
2032
+ toJSON,
2033
+ can,
2034
+ hasTag,
2035
+ matches,
2036
+ ...jsonValues
2037
+ } = this;
2038
+ return {
2039
+ ...jsonValues,
2040
+ tags: Array.from(tags)
2041
+ };
2042
+ };
2043
+ const machineSnapshotGetMeta = function getMeta() {
2044
+ const meta = {};
2045
+ for (const stateNode of this._nodes) {
2046
+ if (stateNode.meta !== undefined) {
2047
+ meta[stateNode.id] = stateNode.meta;
2048
+ }
2049
+ }
2050
+ return meta;
2051
+ };
2052
+ const machineSnapshotGetInputs = function getInputs() {
2053
+ return this._stateInputs;
2054
+ };
2055
+ function collectTags(stateNodes) {
2056
+ const tags = new Set();
2057
+ for (const stateNode of stateNodes) {
2058
+ for (const tag of stateNode.tags) {
2059
+ tags.add(tag);
2060
+ }
2061
+ }
2062
+ return tags;
2063
+ }
2064
+ function createMachineSnapshot(config, machine) {
2065
+ return {
2066
+ status: config.status,
2067
+ output: config.output,
2068
+ error: config.error,
2069
+ machine,
2070
+ context: config.context,
2071
+ _nodes: config._nodes,
2072
+ value: config.value ?? getStateValue(machine.root, config._nodes),
2073
+ tags: collectTags(config._nodes),
2074
+ children: config.children,
2075
+ historyValue: config.historyValue || {},
2076
+ _stateInputs: config._stateInputs || {},
2077
+ matches: machineSnapshotMatches,
2078
+ hasTag: machineSnapshotHasTag,
2079
+ can: machineSnapshotCan,
2080
+ getMeta: machineSnapshotGetMeta,
2081
+ getInputs: machineSnapshotGetInputs,
2082
+ toJSON: machineSnapshotToJSON
2083
+ };
2084
+ }
2085
+ function cloneMachineSnapshot(snapshot, config = {}) {
2086
+ const configWithSnapshot = {
2087
+ ...snapshot,
2088
+ ...config
2089
+ };
2090
+ if ((config._nodes ?? snapshot._nodes) === snapshot._nodes) {
2091
+ return {
2092
+ status: configWithSnapshot.status,
2093
+ output: configWithSnapshot.output,
2094
+ error: configWithSnapshot.error,
2095
+ machine: snapshot.machine,
2096
+ context: configWithSnapshot.context,
2097
+ _nodes: snapshot._nodes,
2098
+ value: snapshot.value,
2099
+ tags: snapshot.tags,
2100
+ children: configWithSnapshot.children,
2101
+ historyValue: configWithSnapshot.historyValue || {},
2102
+ _stateInputs: configWithSnapshot._stateInputs || {},
2103
+ matches: machineSnapshotMatches,
2104
+ hasTag: machineSnapshotHasTag,
2105
+ can: machineSnapshotCan,
2106
+ getMeta: machineSnapshotGetMeta,
2107
+ getInputs: machineSnapshotGetInputs,
2108
+ toJSON: machineSnapshotToJSON
2109
+ };
2110
+ }
2111
+ return createMachineSnapshot({
2112
+ ...configWithSnapshot,
2113
+ value: undefined
2114
+ }, snapshot.machine);
2115
+ }
2116
+ function serializeHistoryValue(historyValue) {
2117
+ const result = {};
2118
+ for (const key in historyValue) {
2119
+ const value = historyValue[key];
2120
+ if (Array.isArray(value)) {
2121
+ result[key] = value.map(item => ({
2122
+ id: item.id
2123
+ }));
2124
+ }
2125
+ }
2126
+ return result;
2127
+ }
2128
+ function getPersistedSnapshot(snapshot, options) {
2129
+ const {
2130
+ _nodes: nodes,
2131
+ _stateInputs,
2132
+ tags,
2133
+ machine,
2134
+ children,
2135
+ context,
2136
+ can,
2137
+ hasTag,
2138
+ matches,
2139
+ getMeta,
2140
+ getInputs,
2141
+ toJSON,
2142
+ ...jsonValues
2143
+ } = snapshot;
2144
+ const childrenJson = {};
2145
+ for (const id in children) {
2146
+ const child = children[id];
2147
+ if (typeof child.src !== 'string' && (!options || !('__unsafeAllowInlineActors' in options))) {
2148
+ throw new Error('An inline child actor cannot be persisted.');
2149
+ }
2150
+ childrenJson[id] = {
2151
+ snapshot: child.getPersistedSnapshot(options),
2152
+ src: child.src,
2153
+ registryKey: child.registryKey,
2154
+ syncSnapshot: child._syncSnapshot
2155
+ };
2156
+ }
2157
+ const persisted = {
2158
+ ...jsonValues,
2159
+ context: persistContext(context),
2160
+ children: childrenJson,
2161
+ historyValue: serializeHistoryValue(jsonValues.historyValue)
2162
+ };
2163
+ if (machine.version !== undefined) {
2164
+ persisted.version = machine.version;
2165
+ }
2166
+ return persisted;
2167
+ }
2168
+ function persistContext(contextPart) {
2169
+ let copy;
2170
+ for (const key in contextPart) {
2171
+ const value = contextPart[key];
2172
+ if (value && typeof value === 'object') {
2173
+ if ('sessionId' in value && 'send' in value && 'ref' in value) {
2174
+ copy ??= Array.isArray(contextPart) ? contextPart.slice() : {
2175
+ ...contextPart
2176
+ };
2177
+ copy[key] = {
2178
+ xstate$$type: $$ACTOR_TYPE,
2179
+ id: value.id
2180
+ };
2181
+ } else {
2182
+ const result = persistContext(value);
2183
+ if (result !== value) {
2184
+ copy ??= Array.isArray(contextPart) ? contextPart.slice() : {
2185
+ ...contextPart
2186
+ };
2187
+ copy[key] = result;
2188
+ }
2189
+ }
2190
+ }
2191
+ }
2192
+ return copy ?? contextPart;
2193
+ }
2194
+
2195
+ function matchesState(parentStateId, childStateId) {
2196
+ const parentStateValue = toStateValue(parentStateId);
2197
+ const childStateValue = toStateValue(childStateId);
2198
+ if (typeof childStateValue === 'string') {
2199
+ if (typeof parentStateValue === 'string') {
2200
+ return childStateValue === parentStateValue;
2201
+ }
2202
+
2203
+ // Parent more specific than child
2204
+ return false;
2205
+ }
2206
+ if (typeof parentStateValue === 'string') {
2207
+ return parentStateValue in childStateValue;
2208
+ }
2209
+ return Object.keys(parentStateValue).every(key => {
2210
+ if (!(key in childStateValue)) {
2211
+ return false;
2212
+ }
2213
+ return matchesState(parentStateValue[key], childStateValue[key]);
2214
+ });
2215
+ }
2216
+ function checkStateIn(snapshot, stateValue) {
2217
+ if (typeof stateValue === 'string' && isStateId(stateValue)) {
2218
+ const target = snapshot.machine.getStateNodeById(stateValue);
2219
+ return snapshot._nodes.some(sn => sn === target);
2220
+ }
2221
+ return snapshot.matches(stateValue);
2222
+ }
2223
+ function toStatePath(stateId) {
2224
+ if (isArray(stateId)) {
2225
+ return stateId;
2226
+ }
2227
+ const result = [];
2228
+ let segment = '';
2229
+ for (let i = 0; i < stateId.length; i++) {
2230
+ const char = stateId.charCodeAt(i);
2231
+ switch (char) {
2232
+ // \
2233
+ case 92:
2234
+ // consume the next character
2235
+ segment += stateId[i + 1];
2236
+ // and skip over it
2237
+ i++;
2238
+ continue;
2239
+ // .
2240
+ case 46:
2241
+ result.push(segment);
2242
+ segment = '';
2243
+ continue;
2244
+ }
2245
+ segment += stateId[i];
2246
+ }
2247
+ result.push(segment);
2248
+ return result;
2249
+ }
2250
+ function toStateValue(stateValue) {
2251
+ if (isMachineSnapshot(stateValue)) {
2252
+ return stateValue.value;
2253
+ }
2254
+ if (typeof stateValue !== 'string') {
2255
+ return stateValue;
2256
+ }
2257
+ const statePath = toStatePath(stateValue);
2258
+ return pathToStateValue(statePath);
2259
+ }
2260
+ function pathToStateValue(statePath) {
2261
+ if (statePath.length === 1) {
2262
+ return statePath[0];
2263
+ }
2264
+ const value = {};
2265
+ let marker = value;
2266
+ for (let i = 0; i < statePath.length - 1; i++) {
2267
+ if (i === statePath.length - 2) {
2268
+ marker[statePath[i]] = statePath[i + 1];
2269
+ } else {
2270
+ const previous = marker;
2271
+ marker = {};
2272
+ previous[statePath[i]] = marker;
2273
+ }
2274
+ }
2275
+ return value;
2276
+ }
2277
+ function mapValues(collection, iteratee) {
2278
+ const result = {};
2279
+ const collectionKeys = Object.keys(collection);
2280
+ for (let i = 0; i < collectionKeys.length; i++) {
2281
+ const key = collectionKeys[i];
2282
+ result[key] = iteratee(collection[key], key, collection, i);
2283
+ }
2284
+ return result;
2285
+ }
2286
+ function toArrayStrict(value) {
2287
+ if (isArray(value)) {
2288
+ return value;
2289
+ }
2290
+ return [value];
2291
+ }
2292
+ function toArray(value) {
2293
+ if (value === undefined) {
2294
+ return [];
2295
+ }
2296
+ return toArrayStrict(value);
2297
+ }
2298
+ function resolveOutput(mapper, context, event, self) {
2299
+ if (typeof mapper === 'function') {
2300
+ const outputMapper = mapper;
2301
+ const args = {
2302
+ context,
2303
+ event,
2304
+ output: getEventOutput(event),
2305
+ self
2306
+ };
2307
+ return outputMapper(args);
2308
+ }
2309
+ if (!!mapper && typeof mapper === 'object' && Object.values(mapper).some(val => typeof val === 'function')) {
2310
+ console.warn(`Dynamically mapping values to individual properties is deprecated. Use a single function that returns the mapped object instead.\nFound object containing properties whose values are possibly mapping functions: ${Object.entries(mapper).filter(([, value]) => typeof value === 'function').map(([key, value]) => `\n - ${key}: ${value.toString().replace(/\n\s*/g, '')}`).join('')}`);
2311
+ }
2312
+ return mapper;
2313
+ }
2314
+ function getEventOutput(event) {
2315
+ if (isDoneEvent(event)) {
2316
+ const doneEvent = event;
2317
+ return doneEvent.output;
2318
+ }
2319
+ return undefined;
2320
+ }
2321
+ function isDoneEvent(event) {
2322
+ return event.type.startsWith('xstate.done.actor.') || event.type.startsWith('xstate.done.state.');
2323
+ }
2324
+ function isArray(value) {
2325
+ return Array.isArray(value);
2326
+ }
2327
+ function isErrorActorEvent(event) {
2328
+ return event.type.startsWith('xstate.error.actor');
2329
+ }
2330
+ function toTransitionConfigArray(configLike) {
2331
+ return toArrayStrict(configLike).map(transitionLike => {
2332
+ if (typeof transitionLike === 'undefined' || typeof transitionLike === 'string') {
2333
+ return {
2334
+ target: transitionLike
2335
+ };
2336
+ }
2337
+ if (typeof transitionLike === 'function') {
2338
+ return {
2339
+ to: transitionLike
2340
+ };
2341
+ }
2342
+ return transitionLike;
2343
+ });
2344
+ }
2345
+ function normalizeTarget(target) {
2346
+ if (target === undefined || target === TARGETLESS_KEY) {
2347
+ return undefined;
2348
+ }
2349
+ return toArray(target);
2350
+ }
2351
+ function toObserver(nextHandler, errorHandler, completionHandler) {
2352
+ const isObserver = typeof nextHandler === 'object';
2353
+ const self = isObserver ? nextHandler : undefined;
2354
+ return {
2355
+ next: (isObserver ? nextHandler.next : nextHandler)?.bind(self),
2356
+ error: (isObserver ? nextHandler.error : errorHandler)?.bind(self),
2357
+ complete: (isObserver ? nextHandler.complete : completionHandler)?.bind(self)
2358
+ };
2359
+ }
2360
+ function createInvokeId(stateNodeId, index) {
2361
+ return `${index}.${stateNodeId}`;
2362
+ }
2363
+ function resolveReferencedActor(machine, src) {
2364
+ const match = src.match(/^xstate\.invoke\.(\d+)\.(.*)/);
2365
+ if (!match) {
2366
+ return machine.implementations.actorSources[src];
2367
+ }
2368
+ const [, indexStr, nodeId] = match;
2369
+ const node = machine.getStateNodeById(nodeId);
2370
+ const invokeConfig = node.config.invoke;
2371
+ const configSrc = (Array.isArray(invokeConfig) ? invokeConfig[indexStr] : invokeConfig).src;
2372
+ // A referenced actor may itself be registered by name.
2373
+ return typeof configSrc === 'string' ? machine.implementations.actorSources[configSrc] : configSrc;
2374
+ }
2375
+ function getAllOwnEventDescriptors(snapshot) {
2376
+ return [...new Set([...snapshot._nodes.flatMap(sn => sn.ownEvents)])];
2377
+ }
2378
+
2379
+ /**
2380
+ * Checks if an event type matches an event descriptor, supporting wildcards.
2381
+ * Event descriptors can be:
2382
+ *
2383
+ * - Exact matches: "event.type"
2384
+ * - Wildcard: "*"
2385
+ * - Partial matches: "event.*"
2386
+ *
2387
+ * @param eventType - The actual event type string
2388
+ * @param descriptor - The event descriptor to match against
2389
+ * @returns True if the event type matches the descriptor
2390
+ */
2391
+ function matchesEventDescriptor(eventType, descriptor) {
2392
+ if (descriptor === eventType) {
2393
+ return true;
2394
+ }
2395
+ if (descriptor === WILDCARD) {
2396
+ return true;
2397
+ }
2398
+ if (!descriptor.endsWith('.*')) {
2399
+ return false;
2400
+ }
2401
+ if (/.*\*.+/.test(descriptor)) {
2402
+ console.warn(`Wildcards can only be the last token of an event descriptor (e.g., "event.*") or the entire event descriptor ("*"). Check the "${descriptor}" event.`);
2403
+ }
2404
+ const partialEventTokens = descriptor.split('.');
2405
+ const eventTokens = eventType.split('.');
2406
+ for (let tokenIndex = 0; tokenIndex < partialEventTokens.length; tokenIndex++) {
2407
+ const partialEventToken = partialEventTokens[tokenIndex];
2408
+ const eventToken = eventTokens[tokenIndex];
2409
+ if (partialEventToken === '*') {
2410
+ const isLastToken = tokenIndex === partialEventTokens.length - 1;
2411
+ if (!isLastToken) {
2412
+ console.warn(`Infix wildcards in transition events are not allowed. Check the "${descriptor}" transition.`);
2413
+ }
2414
+ return isLastToken;
2415
+ }
2416
+ if (partialEventToken !== eventToken) {
2417
+ return false;
2418
+ }
2419
+ }
2420
+ return true;
2421
+ }
2422
+
2423
+ function createScheduledEventId(actor, id) {
2424
+ return `${actor.sessionId}.${id}`;
2425
+ }
2426
+ function createRuntimeSystem(rootActor, options) {
2427
+ let idCounter = 0;
2428
+ const children = new Map();
2429
+ const keyedActors = new Map();
2430
+ const reverseKeyedActors = new WeakMap();
2431
+ const inspectionObservers = new Set();
2432
+ const timerMap = {};
2433
+ const {
2434
+ clock,
2435
+ logger
2436
+ } = options;
2437
+
2438
+ // Records a send on the *sender's* transition for the `sent[]` inspection
2439
+ // facet. Captures the send when it is initiated (including delayed sends that
2440
+ // may never deliver), keyed to the source actor's in-flight transition.
2441
+ const recordSent = (source, target, event, delay, id) => {
2442
+ if (!inspectionObservers.size || !source) {
2443
+ return;
2444
+ }
2445
+ const collected = source._collectedSent ??= [];
2446
+ collected.push({
2447
+ targetRef: target,
2448
+ targetId: target.id,
2449
+ event,
2450
+ delay,
2451
+ id
2452
+ });
2453
+ };
2454
+ const scheduler = {
2455
+ schedule: (source, target, event, delay, id = Math.random().toString(36).slice(2)) => {
2456
+ recordSent(source, target, event, delay, id);
2457
+ const scheduledEvent = {
2458
+ source,
2459
+ target,
2460
+ event,
2461
+ delay,
2462
+ id,
2463
+ startedAt: Date.now()
2464
+ };
2465
+ const scheduledEventId = createScheduledEventId(source, id);
2466
+ system._snapshot._scheduledEvents[scheduledEventId] = scheduledEvent;
2467
+ const timeout = clock.setTimeout(() => {
2468
+ delete timerMap[scheduledEventId];
2469
+ delete system._snapshot._scheduledEvents[scheduledEventId];
2470
+
2471
+ // The send was already recorded at schedule time on the sender's
2472
+ // transition, so deliver without re-recording.
2473
+ deliver(source, target, event);
2474
+ }, delay);
2475
+ timerMap[scheduledEventId] = timeout;
2476
+ },
2477
+ cancel: (source, id) => {
2478
+ const scheduledEventId = createScheduledEventId(source, id);
2479
+ const timeout = timerMap[scheduledEventId];
2480
+ delete timerMap[scheduledEventId];
2481
+ delete system._snapshot._scheduledEvents[scheduledEventId];
2482
+ if (timeout !== undefined) {
2483
+ clock.clearTimeout(timeout);
2484
+ }
2485
+ },
2486
+ cancelAll: actor => {
2487
+ for (const scheduledEventId in system._snapshot._scheduledEvents) {
2488
+ const scheduledEvent = system._snapshot._scheduledEvents[scheduledEventId];
2489
+ if (scheduledEvent.source === actor) {
2490
+ scheduler.cancel(actor, scheduledEvent.id);
2491
+ }
2492
+ }
2493
+ }
2494
+ };
2495
+ // Delivers an event to the target actor. Used by both `_relay` (which also
2496
+ // records the send) and the scheduler's timer (which already recorded it).
2497
+ const deliver = (source, target, event) => {
2498
+ const targetMachine = target.logic;
2499
+ const isInternalEvent = typeof targetMachine?.isInternalEventType === 'function' && targetMachine.isInternalEventType(event.type);
2500
+ if (isInternalEvent && source !== target) {
2501
+ throw new Error(`Internal event "${event.type}" cannot be sent to actor "${target.id}" from outside.`);
2502
+ }
2503
+
2504
+ // remember the last source for unified transition inspect event
2505
+ target._lastSourceRef = source;
2506
+ target._send(event);
2507
+ };
2508
+ const sendInspectionEvent = event => {
2509
+ if (!inspectionObservers.size) {
2510
+ return;
2511
+ }
2512
+ const resolvedInspectionEvent = {
2513
+ ...event,
2514
+ rootId: rootActor.sessionId
2515
+ };
2516
+ inspectionObservers.forEach(observer => observer.next?.(resolvedInspectionEvent));
2517
+ };
2518
+ const system = {
2519
+ children,
2520
+ reverseKeyedActors,
2521
+ keyedActors,
2522
+ _snapshot: {
2523
+ _scheduledEvents: (options?.snapshot && options.snapshot.scheduler) ?? {}
2524
+ },
2525
+ _bookId: () => `x:${idCounter++}`,
2526
+ _register: (sessionId, actor) => {
2527
+ children.set(sessionId, actor);
2528
+ return sessionId;
2529
+ },
2530
+ _unregister: actor => {
2531
+ children.delete(actor.sessionId);
2532
+ const registryKey = reverseKeyedActors.get(actor);
2533
+ if (registryKey !== undefined) {
2534
+ keyedActors.delete(registryKey);
2535
+ reverseKeyedActors.delete(actor);
2536
+ }
2537
+ },
2538
+ get: registryKey => {
2539
+ return keyedActors.get(registryKey);
2540
+ },
2541
+ getAll: () => {
2542
+ return Object.fromEntries(keyedActors.entries());
2543
+ },
2544
+ _set: (registryKey, actor) => {
2545
+ const existing = keyedActors.get(registryKey);
2546
+ if (existing && existing !== actor) {
2547
+ throw new Error(`Actor with registry key '${registryKey}' already exists.`);
2548
+ }
2549
+ keyedActors.set(registryKey, actor);
2550
+ reverseKeyedActors.set(actor, registryKey);
2551
+ },
2552
+ inspect: observerOrFn => {
2553
+ const observer = toObserver(observerOrFn);
2554
+ inspectionObservers.add(observer);
2555
+ return {
2556
+ unsubscribe() {
2557
+ inspectionObservers.delete(observer);
2558
+ }
2559
+ };
2560
+ },
2561
+ _sendInspectionEvent: sendInspectionEvent,
2562
+ _relay: (source, target, event) => {
2563
+ recordSent(source, target, event);
2564
+ deliver(source, target, event);
2565
+ },
2566
+ scheduler,
2567
+ getSnapshot: () => {
2568
+ return {
2569
+ _scheduledEvents: {
2570
+ ...system._snapshot._scheduledEvents
2571
+ }
2572
+ };
2573
+ },
2574
+ start: () => {
2575
+ const scheduledEvents = system._snapshot._scheduledEvents;
2576
+ system._snapshot._scheduledEvents = {};
2577
+ for (const scheduledId in scheduledEvents) {
2578
+ const {
2579
+ source,
2580
+ target,
2581
+ event,
2582
+ delay,
2583
+ id
2584
+ } = scheduledEvents[scheduledId];
2585
+ scheduler.schedule(source, target, event, delay, id);
2586
+ }
2587
+ },
2588
+ _clock: clock,
2589
+ _logger: logger,
2590
+ _timerStrategy: options.timers
2591
+ };
2592
+ return system;
2593
+ }
2594
+
2595
+ const effectStates = new WeakMap();
2596
+ const XSTATE_LOGIC_EFFECT_RESOLVE = 'xstate.logic.effect.resolve';
2597
+ const XSTATE_LOGIC_EFFECT_REJECT = 'xstate.logic.effect.reject';
2598
+ const XSTATE_LOGIC_EFFECT_START = 'xstate.logic.effect.start';
2599
+ function getEffectState(self) {
2600
+ let state = effectStates.get(self);
2601
+ if (!state) {
2602
+ state = new Map();
2603
+ effectStates.set(self, state);
2604
+ }
2605
+ return state;
2606
+ }
2607
+ function executeLogicEffects(effects, actorScope) {
2608
+ if (!effects?.length) {
2609
+ return;
2610
+ }
2611
+ for (const effect of effects) {
2612
+ switch (effect.type) {
2613
+ case 'emit':
2614
+ actorScope.emit(effect.event);
2615
+ break;
2616
+ case 'sendBack':
2617
+ {
2618
+ const parent = actorScope.self._parent;
2619
+ if (parent) {
2620
+ actorScope.system._relay(actorScope.self, parent, effect.event);
2621
+ }
2622
+ break;
2623
+ }
2624
+ case 'raise':
2625
+ actorScope.system._relay(actorScope.self, actorScope.self, effect.event);
2626
+ break;
2627
+ case 'effect':
2628
+ {
2629
+ if (!effect.key) {
2630
+ const cleanup = effect.exec();
2631
+ if (typeof cleanup === 'function') {
2632
+ getEffectState(actorScope.self).set(Symbol(), {
2633
+ cleanup
2634
+ });
2635
+ }
2636
+ break;
2637
+ }
2638
+ const state = getEffectState(actorScope.self);
2639
+ if (state.has(effect.key)) {
2640
+ break;
2641
+ }
2642
+ const cleanup = effect.exec();
2643
+ state.set(effect.key, {
2644
+ cleanup: typeof cleanup === 'function' ? cleanup : undefined
2645
+ });
2646
+ break;
2647
+ }
2648
+ case 'cleanupEffects':
2649
+ {
2650
+ const state = effectStates.get(actorScope.self);
2651
+ if (!state) {
2652
+ break;
2653
+ }
2654
+ for (const {
2655
+ cleanup
2656
+ } of state.values()) {
2657
+ cleanup?.();
2658
+ }
2659
+ effectStates.delete(actorScope.self);
2660
+ break;
2661
+ }
2662
+ }
2663
+ }
2664
+ }
2665
+ function resolveContext(context, input) {
2666
+ return typeof context === 'function' ? context({
2667
+ input
2668
+ }) : context;
2669
+ }
2670
+ function createLogic(config) {
2671
+ const transition = (snapshot, event, actorScope) => {
2672
+ if (snapshot.status !== 'active') {
2673
+ return [snapshot, []];
2674
+ }
2675
+ if (event.type === XSTATE_STOP) {
2676
+ return [{
2677
+ ...snapshot,
2678
+ status: 'stopped',
2679
+ input: undefined
2680
+ }, [{
2681
+ type: 'cleanupEffects'
2682
+ }]];
2683
+ }
2684
+ if (event.type === XSTATE_LOGIC_EFFECT_START) {
2685
+ return [{
2686
+ ...snapshot,
2687
+ effects: {
2688
+ ...snapshot.effects,
2689
+ [event.key]: {
2690
+ status: 'active'
2691
+ }
2692
+ }
2693
+ }, []];
2694
+ }
2695
+ if (event.type === XSTATE_LOGIC_EFFECT_RESOLVE) {
2696
+ return [{
2697
+ ...snapshot,
2698
+ effects: {
2699
+ ...snapshot.effects,
2700
+ [event.key]: {
2701
+ status: 'done',
2702
+ output: event.output
2703
+ }
2704
+ }
2705
+ }, []];
2706
+ }
2707
+ if (event.type === XSTATE_LOGIC_EFFECT_REJECT) {
2708
+ return [{
2709
+ ...snapshot,
2710
+ effects: {
2711
+ ...snapshot.effects,
2712
+ [event.key]: {
2713
+ status: 'error',
2714
+ error: event.error
2715
+ }
2716
+ }
2717
+ }, []];
2718
+ }
2719
+ const effects = [];
2720
+ const trackedEffects = {};
2721
+ const enqueueEffect = (key, exec) => {
2722
+ if (snapshot.effects?.[key]) {
2723
+ return;
2724
+ }
2725
+ effects.push({
2726
+ type: 'effect',
2727
+ key,
2728
+ exec
2729
+ });
2730
+ trackedEffects[key] = {
2731
+ status: 'active'
2732
+ };
2733
+ };
2734
+ const enq = {
2735
+ emit: emitted => {
2736
+ effects.push({
2737
+ type: 'emit',
2738
+ event: emitted
2739
+ });
2740
+ },
2741
+ sendBack: sentEvent => {
2742
+ effects.push({
2743
+ type: 'sendBack',
2744
+ event: sentEvent
2745
+ });
2746
+ },
2747
+ raise: raisedEvent => {
2748
+ effects.push({
2749
+ type: 'raise',
2750
+ event: raisedEvent
2751
+ });
2752
+ },
2753
+ effect: (keyOrExec, maybeExec) => {
2754
+ if (typeof keyOrExec === 'string') {
2755
+ enqueueEffect(keyOrExec, maybeExec);
2756
+ return;
2757
+ }
2758
+ effects.push({
2759
+ type: 'effect',
2760
+ exec: keyOrExec
2761
+ });
2762
+ }
2763
+ };
2764
+ const patch = config.run({
2765
+ context: snapshot.context,
2766
+ event,
2767
+ input: snapshot.input,
2768
+ system: actorScope.system,
2769
+ self: actorScope.self,
2770
+ emit: actorScope.emit
2771
+ }, enq);
2772
+ const nextSnapshot = {
2773
+ ...snapshot,
2774
+ ...(patch || {})
2775
+ };
2776
+ if ('context' in snapshot || patch?.context !== undefined) {
2777
+ nextSnapshot.context = patch?.context ?? snapshot.context;
2778
+ }
2779
+ if (patch?.effects || Object.keys(trackedEffects).length) {
2780
+ nextSnapshot.effects = {
2781
+ ...snapshot.effects,
2782
+ ...patch?.effects,
2783
+ ...trackedEffects
2784
+ };
2785
+ }
2786
+ return [nextSnapshot, effects];
2787
+ };
2788
+ const logic = {
2789
+ id: config.id,
2790
+ config,
2791
+ transition,
2792
+ start: (snapshot, actorScope) => {
2793
+ const [nextSnapshot, effects] = transition(snapshot, createInitEvent(snapshot.input), actorScope);
2794
+ Object.assign(snapshot, nextSnapshot);
2795
+ executeLogicEffects(effects, actorScope);
2796
+ },
2797
+ initialTransition: (input, _) => {
2798
+ const context = resolveContext(config.context, input);
2799
+ const snapshot = {
2800
+ status: 'active',
2801
+ output: undefined,
2802
+ error: undefined,
2803
+ input
2804
+ };
2805
+ if (context !== undefined) {
2806
+ snapshot.context = context;
2807
+ }
2808
+ return [{
2809
+ ...snapshot
2810
+ }, []];
2811
+ },
2812
+ getInitialSnapshot: (actorScope, input) => logic.initialTransition(input, actorScope)[0],
2813
+ getPersistedSnapshot: snapshot => snapshot,
2814
+ restoreSnapshot: snapshot => snapshot
2815
+ };
2816
+ return logic;
2817
+ }
2818
+
2819
+ // those are needed to make JSDoc `@link` work properly
2820
+
2821
+ let executingCustomAction = false;
2822
+ const $$ACTOR_TYPE = 1;
2823
+
2824
+ // those values are currently used by @xstate/react directly so it's important to keep the assigned values in sync
2825
+ let ProcessingStatus = /*#__PURE__*/function (ProcessingStatus) {
2826
+ ProcessingStatus[ProcessingStatus["NotStarted"] = 0] = "NotStarted";
2827
+ ProcessingStatus[ProcessingStatus["Running"] = 1] = "Running";
2828
+ ProcessingStatus[ProcessingStatus["Stopped"] = 2] = "Stopped";
2829
+ return ProcessingStatus;
2830
+ }({});
2831
+ const defaultOptions = {
2832
+ clock: {
2833
+ setTimeout: (fn, ms) => {
2834
+ return setTimeout(fn, ms);
2835
+ },
2836
+ clearTimeout: id => {
2837
+ return clearTimeout(id);
2838
+ }
2839
+ },
2840
+ logger: console.log.bind(console)
2841
+ };
2842
+ function isExecutableActionObject(effect) {
2843
+ return typeof effect === 'object' && effect !== null && 'args' in effect && 'exec' in effect;
2844
+ }
2845
+ function executeExecutableEffects(effects, actorScope) {
2846
+ if (!effects?.length) {
2847
+ return [];
2848
+ }
2849
+ const logicEffects = [];
2850
+ for (const effect of effects) {
2851
+ if (isExecutableActionObject(effect)) {
2852
+ actorScope.actionExecutor(effect);
2853
+ } else {
2854
+ logicEffects.push(effect);
2855
+ }
2856
+ }
2857
+ return logicEffects;
2858
+ }
2859
+
2860
+ /**
2861
+ * An Actor is a running process that can receive events, send events and change
2862
+ * its behavior based on the events it receives, which can cause effects outside
2863
+ * of the actor. When you run a state machine, it becomes an actor.
2864
+ *
2865
+ * An `Actor` is the concrete runtime instance with lifecycle methods and
2866
+ * system-owned internals. It also satisfies the narrower `ActorRef` contract,
2867
+ * so consumer APIs should accept `ActorRef` when they only need to send events
2868
+ * or read snapshots.
2869
+ */
2870
+ class Actor {
2871
+ /**
2872
+ * Creates a new actor instance for the given logic with the provided options,
2873
+ * if any.
2874
+ *
2875
+ * @param logic The logic to create an actor from
2876
+ * @param options Actor options
2877
+ */
2878
+ constructor(logic, options) {
2879
+ this.logic = logic;
2880
+ /** The current internal state of the actor. */
2881
+ this._snapshot = void 0;
2882
+ /**
2883
+ * The clock that is responsible for setting and clearing timeouts, such as
2884
+ * delayed events and transitions.
2885
+ */
2886
+ this.clock = void 0;
2887
+ this.options = void 0;
2888
+ /** The unique identifier for this actor relative to its parent. */
2889
+ this.id = void 0;
2890
+ this._boundProcess = this._process.bind(this);
2891
+ this.mailbox = new Mailbox(this._boundProcess);
2892
+ this.observers = new Set();
2893
+ this.eventListeners = new Map();
2894
+ this.logger = void 0;
2895
+ /** @internal */
2896
+ this._processingStatus = ProcessingStatus.NotStarted;
2897
+ this._forceDeferredActions = false;
2898
+ // Actor Ref
2899
+ this._parent = void 0;
2900
+ /** @internal */
2901
+ this._syncSnapshot = void 0;
2902
+ this.ref = void 0;
2903
+ // TODO: add typings for system
2904
+ this._actorScope = void 0;
2905
+ /** @internal */
2906
+ this._lastSourceRef = void 0;
2907
+ /** @internal */
2908
+ this._collectedMicrosteps = [];
2909
+ /** @internal Actions executed during the in-flight transition. */
2910
+ this._collectedActions = [];
2911
+ /** @internal Events relayed to other actors during the in-flight transition. */
2912
+ this._collectedSent = [];
2913
+ this._initialEffects = void 0;
2914
+ this.registryKey = void 0;
2915
+ /** The globally unique process ID for this invocation. */
2916
+ this.sessionId = void 0;
2917
+ /** The system to which this actor belongs. */
2918
+ this.system = void 0;
2919
+ this.trigger = void 0;
2920
+ this.src = void 0;
2921
+ // array of functions to defer
2922
+ this._deferred = [];
2923
+ // pending effects (timers) from a persisted snapshot, rescheduled on `start()`
2924
+ this._pendingEffects = void 0;
2925
+ const resolvedOptions = {
2926
+ ...defaultOptions,
2927
+ ...options
2928
+ };
2929
+ const {
2930
+ clock,
2931
+ logger,
2932
+ parent,
2933
+ syncSnapshot,
2934
+ id,
2935
+ registryKey,
2936
+ inspect
2937
+ } = resolvedOptions;
2938
+ this.system = parent ? parent.system : resolvedOptions._systemRef?.current ?? createRuntimeSystem(this, {
2939
+ clock,
2940
+ logger,
2941
+ timers: resolvedOptions.timers
2942
+ });
2943
+ if (!parent && resolvedOptions._systemRef && !resolvedOptions._systemRef.current) {
2944
+ resolvedOptions._systemRef.current = this.system;
2945
+ }
2946
+ if (inspect && !parent) {
2947
+ // Always inspect at the system-level
2948
+ this.system.inspect(toObserver(inspect));
2949
+ }
2950
+ this.sessionId = this.system._bookId();
2951
+ this.id = id ?? this.sessionId;
2952
+ this.logger = options?.logger ?? this.system._logger;
2953
+ this.clock = options?.clock ?? this.system._clock;
2954
+ this._parent = parent;
2955
+ this._syncSnapshot = syncSnapshot;
2956
+ this.options = resolvedOptions;
2957
+ this.src = resolvedOptions.src ?? logic;
2958
+ this.ref = this;
2959
+ this._actorScope = {
2960
+ self: this,
2961
+ id: this.id,
2962
+ sessionId: this.sessionId,
2963
+ logger: this.logger,
2964
+ defer: fn => {
2965
+ this._deferred.push(fn);
2966
+ },
2967
+ system: this.system,
2968
+ stopChild: child => {
2969
+ if (child._parent !== this) {
2970
+ throw new Error(`Cannot stop child actor ${child.id} of ${this.id} because it is not a child`);
2971
+ }
2972
+ child._stop();
2973
+ },
2974
+ emit: emittedEvent => {
2975
+ const listeners = this.eventListeners.get(emittedEvent.type);
2976
+ const wildcardListener = this.eventListeners.get('*');
2977
+ if (!listeners && !wildcardListener) {
2978
+ return;
2979
+ }
2980
+ if (listeners) {
2981
+ for (const handler of listeners) {
2982
+ try {
2983
+ handler(emittedEvent);
2984
+ } catch (err) {
2985
+ reportUnhandledError(err);
2986
+ }
2987
+ }
2988
+ }
2989
+ if (!wildcardListener) {
2990
+ return;
2991
+ }
2992
+ for (const handler of wildcardListener) {
2993
+ try {
2994
+ handler(emittedEvent);
2995
+ } catch (err) {
2996
+ reportUnhandledError(err);
2997
+ }
2998
+ }
2999
+ },
3000
+ actionExecutor: action => {
3001
+ const exec = () => {
3002
+ // Record every executed action for the '@xstate.transition' inspection
3003
+ // event's `actions[]` facet (replaces the v5 '@xstate.action' event).
3004
+ this._collectedActions.push({
3005
+ type: action.type,
3006
+ params: action.params
3007
+ });
3008
+ if (!action.exec) {
3009
+ return;
3010
+ }
3011
+ const saveExecutingCustomAction = executingCustomAction;
3012
+ try {
3013
+ executingCustomAction = true;
3014
+ action.exec();
3015
+ } finally {
3016
+ executingCustomAction = saveExecutingCustomAction;
3017
+ }
3018
+ };
3019
+ if (this._processingStatus === ProcessingStatus.Running && !this._forceDeferredActions) {
3020
+ exec();
3021
+ } else {
3022
+ this._deferred.push(exec);
3023
+ }
3024
+ }
3025
+ };
3026
+
3027
+ // Ensure that the send method is bound to this Actor instance
3028
+ // if destructured
3029
+ this.send = this.send.bind(this);
3030
+ this.trigger = new Proxy({}, {
3031
+ get: (_, eventType) => {
3032
+ return payload => {
3033
+ this.send({
3034
+ ...payload,
3035
+ type: eventType
3036
+ });
3037
+ };
3038
+ }
3039
+ });
3040
+
3041
+ // unified '@xstate.transition' event replaces '@xstate.actor'
3042
+
3043
+ const resolvedRegistryKey = registryKey;
3044
+ if (resolvedRegistryKey) {
3045
+ this.registryKey = resolvedRegistryKey;
3046
+ this.system._set(resolvedRegistryKey, this);
3047
+ }
3048
+
3049
+ // prepare to collect initial microsteps during initialTransition
3050
+ this._collectedMicrosteps = [];
3051
+ let persistedState = options?.snapshot ?? options?.state;
3052
+ if (persistedState && typeof persistedState === 'object' && '_pendingEffects' in persistedState) {
3053
+ // Pending effects are restored by this actor on `start()`; the logic
3054
+ // only sees the rest of the persisted snapshot.
3055
+ const {
3056
+ _pendingEffects,
3057
+ ...rest
3058
+ } = persistedState;
3059
+ this._pendingEffects = _pendingEffects;
3060
+ persistedState = rest;
3061
+ }
3062
+ try {
3063
+ if (persistedState) {
3064
+ this._snapshot = this.logic.restoreSnapshot ? this.logic.restoreSnapshot(persistedState, this._actorScope) : persistedState;
3065
+ } else {
3066
+ const [snapshot, effects] = this.logic.initialTransition(this.options?.input, this._actorScope);
3067
+ this._snapshot = snapshot;
3068
+ this._initialEffects = effects;
3069
+ }
3070
+ } catch (err) {
3071
+ // if we get here then it means that we assign a value to this._snapshot that is not of the correct type
3072
+ // we can't get the true `TSnapshot & { status: 'error'; }`, it's impossible
3073
+ // so right now this is a lie of sorts
3074
+ this._snapshot = {
3075
+ status: 'error',
3076
+ output: undefined,
3077
+ error: err
3078
+ };
3079
+ // discard any functions deferred during the failed initial snapshot
3080
+ // computation so they can't run against an inconsistent actor
3081
+ this._deferred.length = 0;
3082
+ }
3083
+ if (resolvedRegistryKey && this._snapshot.status !== 'active') {
3084
+ this.system._unregister(this);
3085
+ }
3086
+
3087
+ // Announce actor topology: emitted once for every actor (root and every
3088
+ // spawned/invoked child) so the actor graph can be drawn before any
3089
+ // transitions occur. This is the only place actor identity is announced.
3090
+ this.system._sendInspectionEvent({
3091
+ type: '@xstate.actor',
3092
+ actorRef: this,
3093
+ parentRef: this._parent,
3094
+ id: this.id,
3095
+ src: this.src,
3096
+ snapshot: this._snapshot
3097
+ });
3098
+ }
3099
+ _setErrorSnapshot(err, snapshot = this._snapshot) {
3100
+ this._snapshot = {
3101
+ ...snapshot,
3102
+ status: 'error',
3103
+ error: err
3104
+ };
3105
+ }
3106
+ _next(snapshot) {
3107
+ for (const observer of this.observers) {
3108
+ try {
3109
+ observer.next?.(snapshot);
3110
+ } catch (err) {
3111
+ reportUnhandledError(err);
3112
+ }
3113
+ }
3114
+ }
3115
+ update(snapshot, event) {
3116
+ // Update state
3117
+ this._snapshot = snapshot;
3118
+
3119
+ // Execute deferred effects
3120
+ for (let i = 0; i < this._deferred.length; i++) {
3121
+ const deferredFn = this._deferred[i];
3122
+ try {
3123
+ deferredFn();
3124
+ } catch (err) {
3125
+ // this error can only be caught when executing *initial* actions
3126
+ // it's the only time when we call actions provided by the user through those deferreds
3127
+ // when the actor is already running we always execute them synchronously while transitioning
3128
+ // no "builtin deferred" should actually throw an error since they are either safe
3129
+ // or the control flow is passed through the mailbox and errors should be caught by the `_process` used by the mailbox
3130
+ this._deferred.length = 0;
3131
+ this._setErrorSnapshot(err, snapshot);
3132
+ break;
3133
+ }
3134
+ }
3135
+ this._deferred.length = 0;
3136
+ switch (this._snapshot.status) {
3137
+ case 'active':
3138
+ this._next(snapshot);
3139
+ break;
3140
+ case 'done':
3141
+ {
3142
+ // next observers are meant to be notified about done snapshots
3143
+ // this can be seen as something that is different from how observable work
3144
+ // but with observables `complete` callback is called without any arguments
3145
+ // it's more ergonomic for XState to treat a done snapshot as a "next" value
3146
+ // and the completion event as something that is separate,
3147
+ // something that merely follows emitting that done snapshot
3148
+ this._next(snapshot);
3149
+ this._stopProcedure();
3150
+ this._complete();
3151
+ const doneEvent = createDoneActorEvent(this.id, this._snapshot.output);
3152
+ if (this._parent) {
3153
+ this.system._relay(this, this._parent, doneEvent);
3154
+ }
3155
+ break;
3156
+ }
3157
+ case 'error':
3158
+ this._error(this._snapshot.error);
3159
+ break;
3160
+ }
3161
+ this.system._sendInspectionEvent({
3162
+ type: '@xstate.transition',
3163
+ actorRef: this,
3164
+ event,
3165
+ sourceRef: this._lastSourceRef,
3166
+ targetRef: this,
3167
+ snapshot,
3168
+ microsteps: this._collectedMicrosteps,
3169
+ actions: this._collectedActions,
3170
+ sent: this._collectedSent,
3171
+ eventType: event.type
3172
+ });
3173
+ // reset facets after emission
3174
+ this._collectedMicrosteps = [];
3175
+ this._collectedActions = [];
3176
+ this._collectedSent = [];
3177
+ }
3178
+ _flushInitialEffects() {
3179
+ if (!this._initialEffects) {
3180
+ return true;
3181
+ }
3182
+ this._forceDeferredActions = true;
3183
+ try {
3184
+ const logicEffects = executeExecutableEffects(this._initialEffects, this._actorScope);
3185
+ executeLogicEffects(logicEffects, this._actorScope);
3186
+ this._initialEffects = undefined;
3187
+ return true;
3188
+ } catch (err) {
3189
+ this._initialEffects = undefined;
3190
+ this._deferred.length = 0;
3191
+ this._setErrorSnapshot(err);
3192
+ this._error(err);
3193
+ return false;
3194
+ } finally {
3195
+ this._forceDeferredActions = false;
3196
+ }
3197
+ }
3198
+
3199
+ /**
3200
+ * Subscribe an observer to an actor’s snapshot values.
3201
+ *
3202
+ * @remarks
3203
+ * The observer will receive the actor’s snapshot value when it is emitted.
3204
+ * The observer can be:
3205
+ *
3206
+ * - A plain function that receives the latest snapshot, or
3207
+ * - An observer object whose `.next(snapshot)` method receives the latest
3208
+ * snapshot
3209
+ *
3210
+ * @example
3211
+ *
3212
+ * ```ts
3213
+ * // Observer as a plain function
3214
+ * const subscription = actor.subscribe((snapshot) => {
3215
+ * console.log(snapshot);
3216
+ * });
3217
+ * ```
3218
+ *
3219
+ * @example
3220
+ *
3221
+ * ```ts
3222
+ * // Observer as an object
3223
+ * const subscription = actor.subscribe({
3224
+ * next(snapshot) {
3225
+ * console.log(snapshot);
3226
+ * },
3227
+ * error(err) {
3228
+ * // ...
3229
+ * },
3230
+ * complete() {
3231
+ * // ...
3232
+ * }
3233
+ * });
3234
+ * ```
3235
+ *
3236
+ * The return value of `actor.subscribe(observer)` is a subscription object
3237
+ * that has an `.unsubscribe()` method. You can call
3238
+ * `subscription.unsubscribe()` to unsubscribe the observer:
3239
+ *
3240
+ * @example
3241
+ *
3242
+ * ```ts
3243
+ * const subscription = actor.subscribe((snapshot) => {
3244
+ * // ...
3245
+ * });
3246
+ *
3247
+ * // Unsubscribe the observer
3248
+ * subscription.unsubscribe();
3249
+ * ```
3250
+ *
3251
+ * When the actor is stopped, all of its observers will automatically be
3252
+ * unsubscribed.
3253
+ *
3254
+ * @param observer - Either a plain function that receives the latest
3255
+ * snapshot, or an observer object whose `.next(snapshot)` method receives
3256
+ * the latest snapshot
3257
+ */
3258
+
3259
+ subscribe(nextListenerOrObserver, errorListener, completeListener) {
3260
+ const observer = toObserver(nextListenerOrObserver, errorListener, completeListener);
3261
+ if (this._processingStatus !== ProcessingStatus.Stopped) {
3262
+ this.observers.add(observer);
3263
+ } else {
3264
+ switch (this._snapshot.status) {
3265
+ case 'done':
3266
+ try {
3267
+ observer.complete?.();
3268
+ } catch (err) {
3269
+ reportUnhandledError(err);
3270
+ }
3271
+ break;
3272
+ case 'error':
3273
+ {
3274
+ const err = this._snapshot.error;
3275
+ if (!observer.error) {
3276
+ reportUnhandledError(err);
3277
+ } else {
3278
+ try {
3279
+ observer.error(err);
3280
+ } catch (err) {
3281
+ reportUnhandledError(err);
3282
+ }
3283
+ }
3284
+ break;
3285
+ }
3286
+ }
3287
+ }
3288
+ return {
3289
+ unsubscribe: () => {
3290
+ this.observers.delete(observer);
3291
+ }
3292
+ };
3293
+ }
3294
+ on(type, handler) {
3295
+ let listeners = this.eventListeners.get(type);
3296
+ if (!listeners) {
3297
+ listeners = new Set();
3298
+ this.eventListeners.set(type, listeners);
3299
+ }
3300
+ listeners.add(handler);
3301
+ return {
3302
+ unsubscribe: () => {
3303
+ listeners.delete(handler);
3304
+ }
3305
+ };
3306
+ }
3307
+ select(selector, equalityFn = Object.is) {
3308
+ return {
3309
+ subscribe: (observerOrFn, errorListener, completeListener) => {
3310
+ const observer = toObserver(observerOrFn, errorListener, completeListener);
3311
+ let selected = selector(this.getSnapshot());
3312
+ return this.subscribe({
3313
+ next: snapshot => {
3314
+ const next = selector(snapshot);
3315
+ if (!equalityFn(selected, next)) {
3316
+ selected = next;
3317
+ observer.next?.(next);
3318
+ }
3319
+ },
3320
+ error: observer.error,
3321
+ complete: observer.complete
3322
+ });
3323
+ },
3324
+ get: () => selector(this.getSnapshot())
3325
+ };
3326
+ }
3327
+
3328
+ /** Starts the Actor from the initial state */
3329
+ start() {
3330
+ if (this._processingStatus === ProcessingStatus.Running) {
3331
+ // Do not restart the service if it is already started
3332
+ return this;
3333
+ }
3334
+ if (this._syncSnapshot) {
3335
+ this.subscribe({
3336
+ next: snapshot => {
3337
+ if (snapshot.status === 'active') {
3338
+ this.system._relay(this, this._parent, {
3339
+ type: `xstate.snapshot.${this.id}`,
3340
+ snapshot
3341
+ });
3342
+ }
3343
+ },
3344
+ error: () => {}
3345
+ });
3346
+ }
3347
+ this.system._register(this.sessionId, this);
3348
+ if (this.registryKey) {
3349
+ this.system._set(this.registryKey, this);
3350
+ }
3351
+ this._processingStatus = ProcessingStatus.Running;
3352
+
3353
+ // TODO: this isn't correct when rehydrating
3354
+ const initEvent = createInitEvent(this.options.input);
3355
+ // remember source of init as parent for unified transition event
3356
+ this._lastSourceRef = this._parent;
3357
+ const status = this._snapshot.status;
3358
+ switch (status) {
3359
+ case 'done':
3360
+ // a state machine can be "done" upon initialization (it could reach a final state using initial microsteps)
3361
+ // we still need to complete observers, flush deferreds etc
3362
+ if (!this._flushInitialEffects()) {
3363
+ return this;
3364
+ }
3365
+ this.update(this._snapshot, initEvent);
3366
+ // TODO: rethink cleanup of observers, mailbox, etc
3367
+ return this;
3368
+ case 'error':
3369
+ this._error(this._snapshot.error);
3370
+ return this;
3371
+ }
3372
+ if (!this._parent) {
3373
+ this.system.start();
3374
+ }
3375
+ if (this.logic.start) {
3376
+ try {
3377
+ this.logic.start(this._snapshot, this._actorScope);
3378
+ } catch (err) {
3379
+ this._setErrorSnapshot(err);
3380
+ this._error(err);
3381
+ return this;
3382
+ }
3383
+ }
3384
+ if (!this._flushInitialEffects()) {
3385
+ return this;
3386
+ }
3387
+
3388
+ // TODO: this notifies all subscribers but usually this is redundant
3389
+ // there is no real change happening here
3390
+ // we need to rethink if this needs to be refactored
3391
+ this.update(this._snapshot, initEvent);
3392
+ if (this._pendingEffects) {
3393
+ const strategy = this.options.timers ?? this.system._timerStrategy ?? 'resume';
3394
+ for (const effect of this._pendingEffects) {
3395
+ // Re-execute the pending effect with its remaining delay. Only
3396
+ // self-targeted raises exist today; other effect types are ignored.
3397
+ if (effect.type === '@xstate.raise') {
3398
+ this.system.scheduler.schedule(this, this, effect.event, resolveRestoredTimerDelay(strategy, effect), effect.id);
3399
+ }
3400
+ }
3401
+ this._pendingEffects = undefined;
3402
+ }
3403
+ this.mailbox.start();
3404
+ return this;
3405
+ }
3406
+ _process(event) {
3407
+ let nextState;
3408
+ let caughtError;
3409
+ try {
3410
+ nextState = this.logic.transition(this._snapshot, event, this._actorScope);
3411
+ } catch (err) {
3412
+ // we wrap it in a box so we can rethrow it later even if falsy value gets caught here
3413
+ caughtError = {
3414
+ err
3415
+ };
3416
+ }
3417
+ if (caughtError) {
3418
+ const {
3419
+ err
3420
+ } = caughtError;
3421
+ this._setErrorSnapshot(err);
3422
+ this._error(err);
3423
+ return;
3424
+ }
3425
+ try {
3426
+ const [snapshot, effects] = nextState;
3427
+ const logicEffects = executeExecutableEffects(effects, this._actorScope);
3428
+ this.update(snapshot, event);
3429
+ executeLogicEffects(logicEffects, this._actorScope);
3430
+ } catch (err) {
3431
+ this._setErrorSnapshot(err);
3432
+ this._error(err);
3433
+ return;
3434
+ }
3435
+ if (event.type === XSTATE_STOP) {
3436
+ this._stopProcedure();
3437
+ this._complete();
3438
+ }
3439
+ }
3440
+
3441
+ /** @internal */
3442
+ _stop() {
3443
+ if (this._processingStatus === ProcessingStatus.Stopped) {
3444
+ return this;
3445
+ }
3446
+ this.mailbox.clear();
3447
+ if (this._processingStatus === ProcessingStatus.NotStarted) {
3448
+ this._processingStatus = ProcessingStatus.Stopped;
3449
+ return this;
3450
+ }
3451
+ this.mailbox.enqueue({
3452
+ type: XSTATE_STOP
3453
+ });
3454
+ this.system._unregister(this);
3455
+ return this;
3456
+ }
3457
+
3458
+ /** Stops the Actor and unsubscribe all listeners. */
3459
+ stop() {
3460
+ if (this._parent) {
3461
+ throw new Error('A non-root actor cannot be stopped directly.');
3462
+ }
3463
+ return this._stop();
3464
+ }
3465
+ _complete() {
3466
+ for (const observer of this.observers) {
3467
+ try {
3468
+ observer.complete?.();
3469
+ } catch (err) {
3470
+ reportUnhandledError(err);
3471
+ }
3472
+ }
3473
+ this.observers.clear();
3474
+ this.eventListeners.clear();
3475
+ }
3476
+ _error(err) {
3477
+ this._stopProcedure();
3478
+ if (!this.observers.size) {
3479
+ if (!this._parent) {
3480
+ reportUnhandledError(err);
3481
+ }
3482
+ this.eventListeners.clear();
3483
+ } else {
3484
+ let reportError = false;
3485
+ for (const observer of this.observers) {
3486
+ const errorListener = observer.error;
3487
+ reportError ||= !errorListener;
3488
+ try {
3489
+ errorListener?.(err);
3490
+ } catch (err2) {
3491
+ reportUnhandledError(err2);
3492
+ }
3493
+ }
3494
+ this.observers.clear();
3495
+ this.eventListeners.clear();
3496
+ if (reportError) {
3497
+ reportUnhandledError(err);
3498
+ }
3499
+ }
3500
+ if (this._parent) {
3501
+ this.system._relay(this, this._parent, createErrorActorEvent(this.id, err));
3502
+ }
3503
+ }
3504
+ // TODO: atm children don't belong entirely to the actor so
3505
+ // in a way - it's not even super aware of them
3506
+ // so we can't stop them from here but we really should!
3507
+ // right now, they are being stopped within the machine's transition
3508
+ // but that could throw and leave us with "orphaned" active actors
3509
+ _stopProcedure() {
3510
+ if (this._processingStatus !== ProcessingStatus.Running) {
3511
+ // Actor already stopped; do nothing
3512
+ return;
3513
+ }
3514
+
3515
+ // Cancel all delayed events
3516
+ this.system.scheduler.cancelAll(this);
3517
+
3518
+ // TODO: mailbox.reset
3519
+ this.mailbox.clear();
3520
+ // TODO: after `stop` we must prepare ourselves for receiving events again
3521
+ // events sent *after* stop signal must be queued
3522
+ // it seems like this should be the common behavior for all of our consumers
3523
+ // so perhaps this should be unified somehow for all of them
3524
+ this.mailbox = new Mailbox(this._boundProcess);
3525
+ this._processingStatus = ProcessingStatus.Stopped;
3526
+ this.system._unregister(this);
3527
+ }
3528
+
3529
+ /** @internal */
3530
+ _send(event) {
3531
+ if (this._processingStatus === ProcessingStatus.Stopped) {
3532
+ // do nothing
3533
+ {
3534
+ // TODO: circular serialization issues
3535
+ // const eventString = ''; //JSON.stringify(event);
3536
+
3537
+ console.warn(`Event "${event.type}" was sent to stopped actor "${this.id} (${this.sessionId})". This actor has already reached its final state, and will not transition.`);
3538
+ }
3539
+ return;
3540
+ }
3541
+ this.mailbox.enqueue(event);
3542
+ }
3543
+
3544
+ /**
3545
+ * Sends an event to the running Actor to trigger a transition.
3546
+ *
3547
+ * @param event The event to send
3548
+ */
3549
+ send(event) {
3550
+ if (typeof event === 'string') {
3551
+ throw new Error(`Only event objects may be sent to actors; use .send({ type: "${event}" }) instead`);
3552
+ }
3553
+ this.system._relay(undefined, this, event);
3554
+ }
3555
+ toJSON() {
3556
+ return {
3557
+ xstate$$type: $$ACTOR_TYPE,
3558
+ id: this.id
3559
+ };
3560
+ }
3561
+
3562
+ /**
3563
+ * Obtain the internal state of the actor, which can be persisted.
3564
+ *
3565
+ * @remarks
3566
+ * The internal state can be persisted from any actor, not only machines.
3567
+ *
3568
+ * Note that the persisted state is not the same as the snapshot from
3569
+ * {@link Actor.getSnapshot}. Persisted state represents the internal state of
3570
+ * the actor, while snapshots represent the actor's last emitted value.
3571
+ *
3572
+ * Can be restored with {@link ActorOptions.state}
3573
+ * @see https://stately.ai/docs/persistence
3574
+ */
3575
+
3576
+ getPersistedSnapshot(options) {
3577
+ const persisted = this.logic.getPersistedSnapshot(this._snapshot, options);
3578
+
3579
+ // Capture this actor's pending self-targeted effects (delayed
3580
+ // events/transitions) as serialized action descriptors plus their
3581
+ // runtime progress, so they can be restored on `start()`.
3582
+ const scheduledEvents = this.system._snapshot._scheduledEvents;
3583
+ let pendingEffects;
3584
+ const now = Date.now();
3585
+ for (const key in scheduledEvents) {
3586
+ const scheduled = scheduledEvents[key];
3587
+ if (scheduled.source === this && scheduled.target === this) {
3588
+ (pendingEffects ??= []).push({
3589
+ type: '@xstate.raise',
3590
+ event: scheduled.event,
3591
+ id: scheduled.id,
3592
+ delay: scheduled.delay,
3593
+ startedAt: scheduled.startedAt,
3594
+ elapsed: Math.max(0, now - scheduled.startedAt)
3595
+ });
3596
+ }
3597
+ }
3598
+
3599
+ // If this actor was restored but not yet started, its pending effects
3600
+ // are not in the scheduler yet — carry them through as-is.
3601
+ pendingEffects ??= this._pendingEffects;
3602
+ return pendingEffects ? {
3603
+ ...persisted,
3604
+ _pendingEffects: pendingEffects
3605
+ } : persisted;
3606
+ }
3607
+ [symbolObservable]() {
3608
+ return this;
3609
+ }
3610
+
3611
+ /**
3612
+ * Read an actor’s snapshot synchronously.
3613
+ *
3614
+ * @remarks
3615
+ * The snapshot represent an actor's last emitted value.
3616
+ *
3617
+ * When an actor receives an event, its internal state may change. An actor
3618
+ * may emit a snapshot when a state transition occurs.
3619
+ *
3620
+ * Note that some actors, such as callback actors generated with
3621
+ * `createCallbackLogic`, will not emit snapshots.
3622
+ * @see {@link Actor.subscribe} to subscribe to an actor’s snapshot values.
3623
+ * @see {@link Actor.getPersistedSnapshot} to persist the internal state of an actor (which is more than just a snapshot).
3624
+ */
3625
+ getSnapshot() {
3626
+ if (!this._snapshot) {
3627
+ throw new Error(`Snapshot can't be read while the actor initializes itself`);
3628
+ }
3629
+ return this._snapshot;
3630
+ }
3631
+ }
3632
+ function resolveRestoredTimerDelay(strategy, effect) {
3633
+ if (typeof strategy === 'function') {
3634
+ return strategy(effect);
3635
+ }
3636
+ switch (strategy) {
3637
+ case 'restart':
3638
+ return effect.delay;
3639
+ case 'absolute':
3640
+ return Math.max(0, effect.startedAt + effect.delay - Date.now());
3641
+ case 'resume':
3642
+ default:
3643
+ return Math.max(0, effect.delay - effect.elapsed);
3644
+ }
3645
+ }
3646
+ /**
3647
+ * Creates a new actor instance for the given actor logic with the provided
3648
+ * options, if any.
3649
+ *
3650
+ * @remarks
3651
+ * When you create an actor from actor logic via `createActor(logic)`, you
3652
+ * implicitly create an actor system where the created actor is the root actor.
3653
+ * Any actors spawned from this root actor and its descendants are part of that
3654
+ * actor system.
3655
+ * @example
3656
+ *
3657
+ * ```ts
3658
+ * import { createActor } from 'xstate';
3659
+ * import { someActorLogic } from './someActorLogic.ts';
3660
+ *
3661
+ * // Creating the actor, which implicitly creates an actor system with itself as the root actor
3662
+ * const actor = createActor(someActorLogic);
3663
+ *
3664
+ * actor.subscribe((snapshot) => {
3665
+ * console.log(snapshot);
3666
+ * });
3667
+ *
3668
+ * // Actors must be started by calling `actor.start()`, which will also start the actor system.
3669
+ * actor.start();
3670
+ *
3671
+ * // Actors can receive events
3672
+ * actor.send({ type: 'someEvent' });
3673
+ *
3674
+ * // You can stop root actors by calling `actor.stop()`, which will also stop the actor system and all actors in that system.
3675
+ * actor.stop();
3676
+ * ```
3677
+ *
3678
+ * @param logic - The actor logic to create an actor from. For a state machine
3679
+ * actor logic creator, see {@link createMachine}. Other actor logic creators
3680
+ * include {@link createCallbackLogic}, {@link createEventObservableLogic},
3681
+ * {@link createObservableLogic}, {@link createLogic}, and
3682
+ * {@link createAsyncLogic}.
3683
+ * @param options - Actor options
3684
+ */
3685
+ function createActor(logic, options) {
3686
+ return new Actor(logic, options);
3687
+ }
3688
+
3689
+ const instanceStates = /* #__PURE__ */new WeakMap();
3690
+
3691
+ /**
3692
+ * Represents an actor created by `createCallbackLogic`.
3693
+ *
3694
+ * The type of `self` within the actor's logic.
3695
+ *
3696
+ * @example
3697
+ *
3698
+ * ```ts
3699
+ * import { createCallbackLogic, createActor } from 'xstate';
3700
+ *
3701
+ * // The events the actor receives.
3702
+ * type Event = { type: 'someEvent' };
3703
+ * // The actor's input.
3704
+ * type Input = { name: string };
3705
+ *
3706
+ * // Actor logic that logs whenever it receives an event of type `someEvent`.
3707
+ * const logic = createCallbackLogic<Event, Input>(
3708
+ * ({ self, input, receive }) => {
3709
+ * self;
3710
+ * // ^? CallbackActorRef<Event, Input>
3711
+ *
3712
+ * receive((event) => {
3713
+ * if (event.type === 'someEvent') {
3714
+ * console.log(`${input.name}: received "someEvent" event`);
3715
+ * // logs 'myActor: received "someEvent" event'
3716
+ * }
3717
+ * });
3718
+ * }
3719
+ * );
3720
+ *
3721
+ * const actor = createActor(logic, { input: { name: 'myActor' } });
3722
+ * // ^? CallbackActorRef<Event, Input>
3723
+ * ```
3724
+ *
3725
+ * @see {@link createCallbackLogic}
3726
+ */
3727
+
3728
+ /**
3729
+ * An actor logic creator which returns callback logic as defined by a callback
3730
+ * function.
3731
+ *
3732
+ * @remarks
3733
+ * Useful for subscription-based or other free-form logic that can send events
3734
+ * back to the parent actor.
3735
+ *
3736
+ * Actors created from callback logic (“callback actors”) can:
3737
+ *
3738
+ * - Receive events via the `receive` function
3739
+ * - Send events to the parent actor via the `sendBack` function
3740
+ *
3741
+ * Callback actors are a bit different from other actors in that they:
3742
+ *
3743
+ * - Do not work with `onDone`
3744
+ * - Do not produce a snapshot using `.getSnapshot()`
3745
+ * - Do not emit values when used with `.subscribe()`
3746
+ * - Can not be stopped with `.stop()`
3747
+ *
3748
+ * @example
3749
+ *
3750
+ * ```typescript
3751
+ * const callbackLogic = createCallbackLogic(({ sendBack, receive }) => {
3752
+ * let lockStatus = 'unlocked';
3753
+ *
3754
+ * const handler = (event) => {
3755
+ * if (lockStatus === 'locked') {
3756
+ * return;
3757
+ * }
3758
+ * sendBack(event);
3759
+ * };
3760
+ *
3761
+ * receive((event) => {
3762
+ * if (event.type === 'lock') {
3763
+ * lockStatus = 'locked';
3764
+ * } else if (event.type === 'unlock') {
3765
+ * lockStatus = 'unlocked';
3766
+ * }
3767
+ * });
3768
+ *
3769
+ * document.body.addEventListener('click', handler);
3770
+ *
3771
+ * return () => {
3772
+ * document.body.removeEventListener('click', handler);
3773
+ * };
3774
+ * });
3775
+ * ```
3776
+ *
3777
+ * @param callback - The callback function used to describe the callback logic
3778
+ * The callback function is passed an object with the following properties:
3779
+ *
3780
+ * - `receive` - A function that can send events back to the parent actor; the
3781
+ * listener is then called whenever events are received by the callback
3782
+ * actor
3783
+ * - `sendBack` - A function that can send events back to the parent actor
3784
+ * - `input` - Data that was provided to the callback actor
3785
+ * - `self` - The parent actor of the callback actor
3786
+ * - `system` - The actor system to which the callback actor belongs The callback
3787
+ * function can (optionally) return a cleanup function, which is called
3788
+ * when the actor is stopped.
3789
+ *
3790
+ * @returns Callback logic
3791
+ * @see {@link CallbackLogicFunction} for more information about the callback function and its object argument
3792
+ * @see {@link https://stately.ai/docs/input | Input docs} for more information about how input is passed
3793
+ */
3794
+
3795
+ function createCallbackLogic(callbackOrConfig) {
3796
+ const callback = typeof callbackOrConfig === 'function' ? callbackOrConfig : callbackOrConfig.run;
3797
+ const schemas = typeof callbackOrConfig === 'function' ? undefined : callbackOrConfig.schemas;
3798
+ return createLogic({
3799
+ schemas,
3800
+ context: undefined,
3801
+ run: (args, enq) => {
3802
+ const {
3803
+ event,
3804
+ input,
3805
+ self,
3806
+ system
3807
+ } = args;
3808
+ const emit = args.emit;
3809
+ const callbackState = instanceStates.get(self);
3810
+ callbackState?.receivers?.forEach(receiver => receiver(event));
3811
+ enq.effect('callback', () => {
3812
+ const callbackState = {
3813
+ receivers: undefined,
3814
+ dispose: undefined
3815
+ };
3816
+ instanceStates.set(self, callbackState);
3817
+ callbackState.dispose = callback({
3818
+ input,
3819
+ system,
3820
+ self: self,
3821
+ sendBack: event => {
3822
+ if (self.getSnapshot().status === 'stopped') {
3823
+ return;
3824
+ }
3825
+ const parent = self._parent;
3826
+ if (parent) {
3827
+ system._relay(self, parent, event);
3828
+ }
3829
+ },
3830
+ receive: listener => {
3831
+ callbackState.receivers ??= new Set();
3832
+ callbackState.receivers.add(listener);
3833
+ },
3834
+ emit: emit
3835
+ });
3836
+ return () => {
3837
+ instanceStates.delete(self);
3838
+ callbackState.receivers?.clear();
3839
+ callbackState.dispose?.();
3840
+ };
3841
+ });
3842
+ }
3843
+ });
3844
+ }
3845
+
3846
+ const XSTATE_OBSERVABLE_NEXT = 'xstate.observable.next';
3847
+ const XSTATE_OBSERVABLE_ERROR = 'xstate.observable.error';
3848
+ const XSTATE_OBSERVABLE_COMPLETE = 'xstate.observable.complete';
3849
+
3850
+ /**
3851
+ * Represents an actor created by `createObservableLogic` or
3852
+ * `createEventObservableLogic`.
3853
+ *
3854
+ * The type of `self` within the actor's logic.
3855
+ *
3856
+ * @example
3857
+ *
3858
+ * ```ts
3859
+ * import { createObservableLogic, createActor } from 'xstate';
3860
+ * import { interval } from 'rxjs';
3861
+ *
3862
+ * // The type of the value observed by the actor's logic.
3863
+ * type Context = number;
3864
+ * // The actor's input.
3865
+ * type Input = { period?: number };
3866
+ *
3867
+ * // Actor logic that observes a number incremented every `input.period`
3868
+ * // milliseconds (default: 1_000).
3869
+ * const logic = createObservableLogic<Context, Input>(
3870
+ * ({ input, self }) => {
3871
+ * self;
3872
+ * // ^? ObservableActor<Event, Input>
3873
+ *
3874
+ * return interval(input.period ?? 1_000);
3875
+ * }
3876
+ * );
3877
+ *
3878
+ * const actor = createActor(logic, { input: { period: 2_000 } });
3879
+ * // ^? ObservableActor<Event, Input>
3880
+ * ```
3881
+ *
3882
+ * @see {@link createObservableLogic}
3883
+ * @see {@link createEventObservableLogic}
3884
+ */
3885
+
3886
+ /**
3887
+ * Observable actor logic is described by an observable stream of values. Actors
3888
+ * created from observable logic (“observable actors”) can:
3889
+ *
3890
+ * - Emit snapshots of the observable’s emitted value
3891
+ *
3892
+ * The observable’s emitted value is used as its observable actor’s `context`.
3893
+ *
3894
+ * Sending events to observable actors will have no effect.
3895
+ *
3896
+ * @example
3897
+ *
3898
+ * ```ts
3899
+ * import { createObservableLogic, createActor } from 'xstate';
3900
+ * import { interval } from 'rxjs';
3901
+ *
3902
+ * const logic = createObservableLogic((obj) => interval(1000));
3903
+ *
3904
+ * const actor = createActor(logic);
3905
+ *
3906
+ * actor.subscribe((snapshot) => {
3907
+ * console.log(snapshot.context);
3908
+ * });
3909
+ *
3910
+ * actor.start();
3911
+ * // At every second:
3912
+ * // Logs 0
3913
+ * // Logs 1
3914
+ * // Logs 2
3915
+ * // ...
3916
+ * ```
3917
+ *
3918
+ * @param observableCreator A function that creates an observable. It receives
3919
+ * one argument, an object with the following properties:
3920
+ *
3921
+ * - `input` - Data that was provided to the observable actor
3922
+ * - `self` - The parent actor
3923
+ * - `system` - The actor system to which the observable actor belongs
3924
+ *
3925
+ * It should return a {@link Subscribable}, which is compatible with an RxJS
3926
+ * Observable, although RxJS is not required to create them.
3927
+ * @see {@link https://rxjs.dev} for documentation on RxJS Observable and observable creators.
3928
+ * @see {@link Subscribable} interface in XState, which is based on and compatible with RxJS Observable.
3929
+ */
3930
+
3931
+ function createObservableLogic(observableCreatorOrConfig) {
3932
+ const observableCreator = typeof observableCreatorOrConfig === 'function' ? observableCreatorOrConfig : observableCreatorOrConfig.run;
3933
+ const schemas = typeof observableCreatorOrConfig === 'function' ? undefined : observableCreatorOrConfig.schemas;
3934
+ return createLogic({
3935
+ schemas,
3936
+ context: undefined,
3937
+ run: (args, enq) => {
3938
+ const {
3939
+ event,
3940
+ input,
3941
+ self,
3942
+ system
3943
+ } = args;
3944
+ const emit = args.emit;
3945
+ switch (event.type) {
3946
+ case XSTATE_OBSERVABLE_NEXT:
3947
+ {
3948
+ return {
3949
+ context: event.data
3950
+ };
3951
+ }
3952
+ case XSTATE_OBSERVABLE_ERROR:
3953
+ return {
3954
+ status: 'error',
3955
+ error: event.data,
3956
+ input: undefined,
3957
+ effects: {
3958
+ observable: {
3959
+ status: 'error',
3960
+ error: event.data
3961
+ }
3962
+ }
3963
+ };
3964
+ case XSTATE_OBSERVABLE_COMPLETE:
3965
+ return {
3966
+ status: 'done',
3967
+ input: undefined,
3968
+ effects: {
3969
+ observable: {
3970
+ status: 'done'
3971
+ }
3972
+ }
3973
+ };
3974
+ }
3975
+ enq.effect('observable', () => {
3976
+ const actorSelf = self;
3977
+ const subscription = observableCreator({
3978
+ input,
3979
+ system,
3980
+ self: self,
3981
+ emit: emit
3982
+ }).subscribe({
3983
+ next: value => {
3984
+ system._relay(actorSelf, actorSelf, {
3985
+ type: XSTATE_OBSERVABLE_NEXT,
3986
+ data: value
3987
+ });
3988
+ },
3989
+ error: err => {
3990
+ system._relay(actorSelf, actorSelf, {
3991
+ type: XSTATE_OBSERVABLE_ERROR,
3992
+ data: err
3993
+ });
3994
+ },
3995
+ complete: () => {
3996
+ system._relay(actorSelf, actorSelf, {
3997
+ type: XSTATE_OBSERVABLE_COMPLETE
3998
+ });
3999
+ }
4000
+ });
4001
+ return () => subscription.unsubscribe();
4002
+ });
4003
+ }
4004
+ });
4005
+ }
4006
+
4007
+ /**
4008
+ * Creates event observable logic that listens to an observable that delivers
4009
+ * event objects.
4010
+ *
4011
+ * Event observable actor logic is described by an observable stream of
4012
+ * {@link https://stately.ai/docs/transitions#event-objects | event objects}.
4013
+ * Actors created from event observable logic (“event observable actors”) can:
4014
+ *
4015
+ * - Implicitly send events to its parent actor
4016
+ * - Emit snapshots of its emitted event objects
4017
+ *
4018
+ * Sending events to event observable actors will have no effect.
4019
+ *
4020
+ * @example
4021
+ *
4022
+ * ```ts
4023
+ * import {
4024
+ * createEventObservableLogic,
4025
+ * Subscribable,
4026
+ * EventObject,
4027
+ * createMachine,
4028
+ * createActor
4029
+ * } from 'xstate';
4030
+ * import { fromEvent } from 'rxjs';
4031
+ *
4032
+ * const mouseClickLogic = createEventObservableLogic(
4033
+ * () => fromEvent(document.body, 'click') as Subscribable<EventObject>
4034
+ * );
4035
+ *
4036
+ * const canvasMachine = createMachine({
4037
+ * invoke: {
4038
+ * // Will send mouse `click` events to the canvas actor
4039
+ * src: mouseClickLogic
4040
+ * }
4041
+ * });
4042
+ *
4043
+ * const canvasActor = createActor(canvasMachine);
4044
+ * canvasActor.start();
4045
+ * ```
4046
+ *
4047
+ * @param lazyObservable A function that creates an observable that delivers
4048
+ * event objects. It receives one argument, an object with the following
4049
+ * properties:
4050
+ *
4051
+ * - `input` - Data that was provided to the event observable actor
4052
+ * - `self` - The parent actor
4053
+ * - `system` - The actor system to which the event observable actor belongs.
4054
+ *
4055
+ * It should return a {@link Subscribable}, which is compatible with an RxJS
4056
+ * Observable, although RxJS is not required to create them.
4057
+ */
4058
+
4059
+ function createEventObservableLogic(lazyObservableOrConfig) {
4060
+ const lazyObservable = typeof lazyObservableOrConfig === 'function' ? lazyObservableOrConfig : lazyObservableOrConfig.run;
4061
+
4062
+ // TODO: event types
4063
+ const logic = {
4064
+ config: lazyObservable,
4065
+ transition: (state, event) => {
4066
+ if (state.status !== 'active') {
4067
+ return [state, []];
4068
+ }
4069
+ switch (event.type) {
4070
+ case XSTATE_OBSERVABLE_ERROR:
4071
+ return [{
4072
+ ...state,
4073
+ status: 'error',
4074
+ error: event.data,
4075
+ input: undefined,
4076
+ _subscription: undefined
4077
+ }, []];
4078
+ case XSTATE_OBSERVABLE_COMPLETE:
4079
+ return [{
4080
+ ...state,
4081
+ status: 'done',
4082
+ input: undefined,
4083
+ _subscription: undefined
4084
+ }, []];
4085
+ case XSTATE_STOP:
4086
+ state._subscription.unsubscribe();
4087
+ return [{
4088
+ ...state,
4089
+ status: 'stopped',
4090
+ input: undefined,
4091
+ _subscription: undefined
4092
+ }, []];
4093
+ default:
4094
+ return [state, []];
4095
+ }
4096
+ },
4097
+ initialTransition: (input, _) => [{
4098
+ status: 'active',
4099
+ output: undefined,
4100
+ error: undefined,
4101
+ context: undefined,
4102
+ input,
4103
+ _subscription: undefined
4104
+ }, []],
4105
+ getInitialSnapshot: (actorScope, input) => logic.initialTransition(input, actorScope)[0],
4106
+ start: (state, {
4107
+ self,
4108
+ system,
4109
+ emit
4110
+ }) => {
4111
+ if (state.status === 'done') {
4112
+ // Do not restart a completed observable
4113
+ return;
4114
+ }
4115
+ state._subscription = lazyObservable({
4116
+ input: state.input,
4117
+ system,
4118
+ self,
4119
+ emit
4120
+ }).subscribe({
4121
+ next: value => {
4122
+ if (self._parent) {
4123
+ system._relay(self, self._parent, value);
4124
+ }
4125
+ },
4126
+ error: err => {
4127
+ system._relay(self, self, {
4128
+ type: XSTATE_OBSERVABLE_ERROR,
4129
+ data: err
4130
+ });
4131
+ },
4132
+ complete: () => {
4133
+ system._relay(self, self, {
4134
+ type: XSTATE_OBSERVABLE_COMPLETE
4135
+ });
4136
+ }
4137
+ });
4138
+ },
4139
+ getPersistedSnapshot: ({
4140
+ _subscription,
4141
+ ...snapshot
4142
+ }) => snapshot,
4143
+ restoreSnapshot: snapshot => ({
4144
+ ...snapshot,
4145
+ _subscription: undefined
4146
+ })
4147
+ };
4148
+ return logic;
4149
+ }
4150
+
4151
+ const XSTATE_ASYNC_RESOLVE = 'xstate.async.resolve';
4152
+ const XSTATE_ASYNC_REJECT = 'xstate.async.reject';
4153
+ class TimeoutError extends Error {
4154
+ constructor(timeout) {
4155
+ super(`Async logic timed out after ${timeout}.`);
4156
+ this.name = 'TimeoutError';
4157
+ }
4158
+ }
4159
+
4160
+ /**
4161
+ * Represents an actor created by `createAsyncLogic`.
4162
+ *
4163
+ * The type of `self` within the actor's logic.
4164
+ *
4165
+ * @example
4166
+ *
4167
+ * ```ts
4168
+ * import { createAsyncLogic, createActor } from 'xstate';
4169
+ *
4170
+ * // The actor's resolved output
4171
+ * type Output = string;
4172
+ * // The actor's input.
4173
+ * type Input = { message: string };
4174
+ *
4175
+ * // Actor logic that fetches the url of an image of a cat saying `input.message`.
4176
+ * const logic = createAsyncLogic<Output, Input>({
4177
+ * run: async ({ input, self }, enq) => {
4178
+ * self;
4179
+ * // ^? AsyncActor<Output, Input>
4180
+ * enq.emit({ type: 'request.start' });
4181
+ *
4182
+ * const data = await fetch(
4183
+ * `https://cataas.com/cat/says/${input.message}`
4184
+ * );
4185
+ * const url = await data.json();
4186
+ * return url;
4187
+ * }
4188
+ * });
4189
+ *
4190
+ * const actor = createActor(logic, { input: { message: 'hello world' } });
4191
+ * // ^? AsyncActor<Output, Input>
4192
+ * ```
4193
+ *
4194
+ * @see {@link createAsyncLogic}
4195
+ */
4196
+
4197
+ /**
4198
+ * An actor logic creator which returns async logic as defined by an async
4199
+ * process that resolves or rejects after some time.
4200
+ *
4201
+ * Actors created from async actor logic can:
4202
+ *
4203
+ * - Output the resolved value of the async process
4204
+ * - Error with the rejected value of the async process
4205
+ * - Abort when stopped or timed out
4206
+ *
4207
+ * Sending events to async actors will have no effect.
4208
+ *
4209
+ * @example
4210
+ *
4211
+ * ```ts
4212
+ * const asyncLogic = createAsyncLogic({
4213
+ * id: 'fetch-user',
4214
+ * timeout: '30s',
4215
+ * run: async ({ signal }, enq) => {
4216
+ * enq.emit({ type: 'request.start' });
4217
+ * const result = await fetch('https://example.com/...', {
4218
+ * signal
4219
+ * }).then((data) => data.json());
4220
+ *
4221
+ * return result;
4222
+ * }
4223
+ * });
4224
+ *
4225
+ * const asyncActor = createActor(asyncLogic);
4226
+ * asyncActor.subscribe((snapshot) => {
4227
+ * console.log(snapshot);
4228
+ * });
4229
+ * asyncActor.start();
4230
+ * // => {
4231
+ * // output: undefined,
4232
+ * // status: 'active'
4233
+ * // ...
4234
+ * // }
4235
+ *
4236
+ * // After promise resolves
4237
+ * // => {
4238
+ * // output: { ... },
4239
+ * // status: 'done',
4240
+ * // ...
4241
+ * // }
4242
+ * ```
4243
+ *
4244
+ * @param asyncLogic A config object with a `run` function which returns a
4245
+ * Promise, and accepts an object with the following properties:
4246
+ *
4247
+ * - `input` - Data that was provided to the async actor
4248
+ * - `self` - The async actor ref
4249
+ * - `system` - The actor system to which the async actor belongs
4250
+ * - `signal` - An abort signal for cancellation
4251
+ *
4252
+ * `run` also receives an `enq` object with the following properties:
4253
+ *
4254
+ * - `emit` - Emits an event that can be observed with `actor.on(...)`
4255
+ *
4256
+ * @see {@link https://stately.ai/docs/input | Input docs} for more information about how input is passed
4257
+ */
4258
+
4259
+ function createAsyncLogic(asyncLogic) {
4260
+ const config = asyncLogic;
4261
+ function waitForEffect(self, key) {
4262
+ return new Promise((resolve, reject) => {
4263
+ const subscription = self.subscribe(snapshot => {
4264
+ const effect = snapshot.effects?.[key];
4265
+ if (effect?.status === 'done') {
4266
+ subscription.unsubscribe();
4267
+ resolve(effect.output);
4268
+ } else if (effect?.status === 'error') {
4269
+ subscription.unsubscribe();
4270
+ reject(effect.error);
4271
+ }
4272
+ });
4273
+ });
4274
+ }
4275
+ return createLogic({
4276
+ id: config.id,
4277
+ schemas: config.schemas,
4278
+ context: undefined,
4279
+ run: ({
4280
+ event,
4281
+ input,
4282
+ self,
4283
+ system
4284
+ }, enq) => {
4285
+ switch (event.type) {
4286
+ case XSTATE_ASYNC_RESOLVE:
4287
+ {
4288
+ const resolvedValue = event.data ?? event.output;
4289
+ return {
4290
+ status: 'done',
4291
+ output: resolvedValue,
4292
+ input: undefined,
4293
+ effects: {
4294
+ async: {
4295
+ status: 'done',
4296
+ output: resolvedValue
4297
+ }
4298
+ }
4299
+ };
4300
+ }
4301
+ case XSTATE_ASYNC_REJECT:
4302
+ {
4303
+ const error = event.data ?? event.error;
4304
+ return {
4305
+ status: 'error',
4306
+ error,
4307
+ input: undefined,
4308
+ effects: {
4309
+ async: {
4310
+ status: 'error',
4311
+ error
4312
+ }
4313
+ }
4314
+ };
4315
+ }
4316
+ }
4317
+ if (event.type !== XSTATE_INIT) {
4318
+ return;
4319
+ }
4320
+ enq.effect(() => {
4321
+ const actorSelf = self;
4322
+ const controller = new AbortController();
4323
+ const timeout = config.timeout;
4324
+ const timeoutMs = parseDelayToMilliseconds(timeout);
4325
+ const timeoutId = timeoutMs === undefined ? undefined : system._clock.setTimeout(() => {
4326
+ if (self.getSnapshot().status !== 'active') {
4327
+ return;
4328
+ }
4329
+ controller.abort();
4330
+ system._relay(actorSelf, actorSelf, {
4331
+ type: XSTATE_ASYNC_REJECT,
4332
+ data: new TimeoutError(timeout)
4333
+ });
4334
+ }, timeoutMs);
4335
+ const clearTimeout = () => {
4336
+ if (timeoutId !== undefined) {
4337
+ system._clock.clearTimeout(timeoutId);
4338
+ }
4339
+ };
4340
+ const resolvedPromise = Promise.resolve(config.run({
4341
+ input,
4342
+ system,
4343
+ self: self,
4344
+ signal: controller.signal
4345
+ }, {
4346
+ emit: enq.emit,
4347
+ step: async (key, exec) => {
4348
+ const effect = self.getSnapshot().effects?.[key];
4349
+ if (effect?.status === 'done') {
4350
+ return effect.output;
4351
+ }
4352
+ if (effect?.status === 'error') {
4353
+ throw effect.error;
4354
+ }
4355
+ if (effect?.status === 'active') {
4356
+ return waitForEffect(self, key);
4357
+ }
4358
+ system._relay(actorSelf, actorSelf, {
4359
+ type: XSTATE_LOGIC_EFFECT_START,
4360
+ key
4361
+ });
4362
+ try {
4363
+ const output = await exec();
4364
+ system._relay(actorSelf, actorSelf, {
4365
+ type: XSTATE_LOGIC_EFFECT_RESOLVE,
4366
+ key,
4367
+ output
4368
+ });
4369
+ return output;
4370
+ } catch (error) {
4371
+ system._relay(actorSelf, actorSelf, {
4372
+ type: XSTATE_LOGIC_EFFECT_REJECT,
4373
+ key,
4374
+ error
4375
+ });
4376
+ throw error;
4377
+ }
4378
+ }
4379
+ }));
4380
+ resolvedPromise.then(response => {
4381
+ clearTimeout();
4382
+ if (self.getSnapshot().status !== 'active') {
4383
+ return;
4384
+ }
4385
+ system._relay(actorSelf, actorSelf, {
4386
+ type: XSTATE_ASYNC_RESOLVE,
4387
+ data: response
4388
+ });
4389
+ }, errorData => {
4390
+ clearTimeout();
4391
+ if (self.getSnapshot().status !== 'active') {
4392
+ return;
4393
+ }
4394
+ system._relay(actorSelf, actorSelf, {
4395
+ type: XSTATE_ASYNC_REJECT,
4396
+ data: errorData
4397
+ });
4398
+ });
4399
+ return () => {
4400
+ controller.abort();
4401
+ clearTimeout();
4402
+ };
4403
+ });
4404
+ return {
4405
+ effects: {
4406
+ async: {
4407
+ status: 'active'
4408
+ }
4409
+ }
4410
+ };
4411
+ }
4412
+ });
4413
+ }
4414
+
4415
+ const emptyLogic = /* #__PURE__ */createLogic({
4416
+ context: undefined,
4417
+ run: () => undefined
4418
+ });
4419
+ function createEmptyActor() {
4420
+ return createActor(emptyLogic);
4421
+ }
4422
+
4423
+ exports.$$ACTOR_TYPE = $$ACTOR_TYPE;
4424
+ exports.Actor = Actor;
4425
+ exports.NULL_EVENT = NULL_EVENT;
4426
+ exports.ProcessingStatus = ProcessingStatus;
4427
+ exports.STATE_DELIMITER = STATE_DELIMITER;
4428
+ exports.TimeoutError = TimeoutError;
4429
+ exports.XSTATE_INIT = XSTATE_INIT;
4430
+ exports.checkStateIn = checkStateIn;
4431
+ exports.createActor = createActor;
4432
+ exports.createAsyncLogic = createAsyncLogic;
4433
+ exports.createCallbackLogic = createCallbackLogic;
4434
+ exports.createEmptyActor = createEmptyActor;
4435
+ exports.createEventObservableLogic = createEventObservableLogic;
4436
+ exports.createInitEvent = createInitEvent;
4437
+ exports.createInvokeId = createInvokeId;
4438
+ exports.createInvokeTimeoutEvent = createInvokeTimeoutEvent;
4439
+ exports.createListenerLogic = createListenerLogic;
4440
+ exports.createLogic = createLogic;
4441
+ exports.createMachineSnapshot = createMachineSnapshot;
4442
+ exports.createObservableLogic = createObservableLogic;
4443
+ exports.createSubscriptionLogic = createSubscriptionLogic;
4444
+ exports.evaluateCandidate = evaluateCandidate;
4445
+ exports.formatRouteTransitions = formatRouteTransitions;
4446
+ exports.formatTransition = formatTransition;
4447
+ exports.getAllOwnEventDescriptors = getAllOwnEventDescriptors;
4448
+ exports.getAllStateNodes = getAllStateNodes;
4449
+ exports.getCandidates = getCandidates;
4450
+ exports.getDelayedTransitions = getDelayedTransitions;
4451
+ exports.getPersistedSnapshot = getPersistedSnapshot;
4452
+ exports.getProperAncestors = getProperAncestors;
4453
+ exports.getStateNodeByPath = getStateNodeByPath;
4454
+ exports.getStateNodes = getStateNodes;
4455
+ exports.initialMicrostep = initialMicrostep;
4456
+ exports.isAtomicStateNode = isAtomicStateNode;
4457
+ exports.isBuiltInExecutableAction = isBuiltInExecutableAction;
4458
+ exports.isInFinalState = isInFinalState;
4459
+ exports.isMachineSnapshot = isMachineSnapshot;
4460
+ exports.isStateId = isStateId;
4461
+ exports.listenerLogic = listenerLogic;
4462
+ exports.macrostep = macrostep;
4463
+ exports.mapValues = mapValues;
4464
+ exports.matchesEventDescriptor = matchesEventDescriptor;
4465
+ exports.matchesState = matchesState;
4466
+ exports.parseDelayToMilliseconds = parseDelayToMilliseconds;
4467
+ exports.pathToStateValue = pathToStateValue;
4468
+ exports.resolveActionsWithContext = resolveActionsWithContext;
4469
+ exports.resolveReferencedActor = resolveReferencedActor;
4470
+ exports.resolveStateValue = resolveStateValue;
4471
+ exports.subscriptionLogic = subscriptionLogic;
4472
+ exports.toArray = toArray;
4473
+ exports.toObserver = toObserver;
4474
+ exports.toStatePath = toStatePath;
4475
+ exports.toTransitionConfigArray = toTransitionConfigArray;
4476
+ exports.transitionNode = transitionNode;