xstate 6.0.0-alpha.48 → 6.0.0-alpha.49

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 (62) hide show
  1. package/README.md +23 -0
  2. package/dist/{StateMachine-b09d2b00.cjs.js → StateMachine-0adb3a70.cjs.js} +1 -1
  3. package/dist/{StateMachine-b3a890bf.development.cjs.js → StateMachine-29450e6d.development.cjs.js} +1 -1
  4. package/dist/{StateMachine-8329769a.esm.js → StateMachine-2af31c11.esm.js} +1 -1
  5. package/dist/{StateMachine-88b3c26a.development.esm.js → StateMachine-faf50c5c.development.esm.js} +1 -1
  6. package/dist/{createActor-27287c65.esm.js → createActor-268165d4.esm.js} +1 -1
  7. package/dist/{createActor-309d77eb.development.esm.js → createActor-88709f74.development.esm.js} +1 -1
  8. package/dist/{createActor-cc3d6dde.development.cjs.js → createActor-a1e0dffb.development.cjs.js} +0 -3
  9. package/dist/{createActor-9b41ade9.cjs.js → createActor-b4dd05ae.cjs.js} +0 -3
  10. package/dist/declarations/src/fsm/createFSMActor.d.ts +67 -0
  11. package/dist/declarations/src/fsm/index.d.ts +2 -0
  12. package/dist/declarations/src/fsm.d.ts +5 -4
  13. package/dist/{effectDescriptor-b1f5deb6.esm.js → effectDescriptor-30c9a744.esm.js} +1 -1
  14. package/dist/{effectDescriptor-7707794b.development.esm.js → effectDescriptor-39d22100.development.esm.js} +1 -1
  15. package/dist/{effectDescriptor-a1678d06.development.cjs.js → effectDescriptor-7ff6802f.development.cjs.js} +1 -1
  16. package/dist/{effectDescriptor-bd36a33e.cjs.js → effectDescriptor-be348e50.cjs.js} +1 -1
  17. package/dist/fsm-7a2e9697.cjs.js +777 -0
  18. package/dist/fsm-b804b3e9.esm.js +775 -0
  19. package/dist/fsm-c1316948.development.cjs.js +777 -0
  20. package/dist/fsm-e18a7cb4.development.esm.js +775 -0
  21. package/dist/{types-81d03bef.esm.js → types-3c3455f7.esm.js} +1 -1
  22. package/dist/{types-1793b8b6.development.esm.js → types-adda3842.development.esm.js} +1 -1
  23. package/dist/{types-7e468202.development.cjs.js → types-b3fea5bd.development.cjs.js} +1 -1
  24. package/dist/{types-9c1bc85f.cjs.js → types-fbabaeee.cjs.js} +1 -1
  25. package/dist/xstate-actors.cjs.js +1 -1
  26. package/dist/xstate-actors.development.cjs.js +1 -1
  27. package/dist/xstate-actors.development.esm.js +2 -2
  28. package/dist/xstate-actors.esm.js +2 -2
  29. package/dist/xstate-durable.cjs.js +2 -2
  30. package/dist/xstate-durable.development.cjs.js +2 -2
  31. package/dist/xstate-durable.development.esm.js +2 -2
  32. package/dist/xstate-durable.esm.js +2 -2
  33. package/dist/xstate-fsm.cjs.d.mts +2 -0
  34. package/dist/xstate-fsm.cjs.d.ts +2 -0
  35. package/dist/xstate-fsm.cjs.js +409 -0
  36. package/dist/xstate-fsm.cjs.mjs +5 -0
  37. package/dist/xstate-fsm.development.cjs.js +409 -0
  38. package/dist/xstate-fsm.development.cjs.mjs +5 -0
  39. package/dist/xstate-fsm.development.esm.js +403 -0
  40. package/dist/xstate-fsm.esm.js +403 -0
  41. package/dist/xstate-fsm.umd.min.js +2 -0
  42. package/dist/xstate-fsm.umd.min.js.map +1 -0
  43. package/dist/xstate-graph.cjs.js +2 -2
  44. package/dist/xstate-graph.development.cjs.js +2 -2
  45. package/dist/xstate-graph.development.esm.js +2 -2
  46. package/dist/xstate-graph.esm.js +2 -2
  47. package/dist/xstate-scxml.cjs.js +3 -3
  48. package/dist/xstate-scxml.development.cjs.js +3 -3
  49. package/dist/xstate-scxml.development.esm.js +3 -3
  50. package/dist/xstate-scxml.esm.js +3 -3
  51. package/dist/xstate-validation.cjs.js +1 -1
  52. package/dist/xstate-validation.development.cjs.js +1 -1
  53. package/dist/xstate-validation.development.esm.js +1 -1
  54. package/dist/xstate-validation.esm.js +1 -1
  55. package/dist/xstate.cjs.js +6 -380
  56. package/dist/xstate.development.cjs.js +6 -380
  57. package/dist/xstate.development.esm.js +10 -384
  58. package/dist/xstate.esm.js +10 -384
  59. package/dist/xstate.umd.min.js +1 -1
  60. package/dist/xstate.umd.min.js.map +1 -1
  61. package/fsm/package.json +8 -0
  62. package/package.json +18 -2
@@ -0,0 +1,777 @@
1
+ 'use strict';
2
+
3
+ var createActor = require('./createActor-a1e0dffb.development.cjs.js');
4
+
5
+ const allocations = new WeakMap();
6
+ function beginFSMEffects(scope, snapshot) {
7
+ allocations.set(scope, {
8
+ ids: new Set(Object.keys(snapshot.children)),
9
+ counters: {
10
+ ...snapshot._nextActorIds
11
+ }
12
+ });
13
+ }
14
+ function execute(runtime = this.source?.system ?? this.actor?.system) {
15
+ switch (this.type) {
16
+ case '@xstate.spawn':
17
+ return runtime.spawnActor(this.source, this.actor);
18
+ case '@xstate.start':
19
+ return runtime.startActor(this.actor);
20
+ case '@xstate.stop':
21
+ return runtime.stopActor(this.actor);
22
+ case '@xstate.terminate':
23
+ return runtime.terminateActor(this.actor, this);
24
+ case '@xstate.raise':
25
+ case '@xstate.sendTo':
26
+ {
27
+ if (this.delay === undefined) {
28
+ return runtime.sendEvent(this.source, this.target, this.event);
29
+ }
30
+ const timer = this.source.getSnapshot().timers[this.id];
31
+ if (timer && !runtime._clock.now) {
32
+ timer.startedAt = Date.now();
33
+ }
34
+ return runtime.scheduleTimer(this.source, this.id, this.delay);
35
+ }
36
+ case '@xstate.cancel':
37
+ return runtime.cancelTimer(this.source, this.id);
38
+ case '@xstate.emit':
39
+ return runtime.emitEvent(this.source, this.event);
40
+ default:
41
+ return this.action?.(...this.args);
42
+ }
43
+ }
44
+ function effect(type, fields = {}) {
45
+ return {
46
+ kind: 'builtin',
47
+ type,
48
+ params: undefined,
49
+ args: [],
50
+ exec: execute,
51
+ ...fields
52
+ };
53
+ }
54
+ function createFSMEnqueue(scope, effects, internalEvents) {
55
+ const enqueue = (action, ...args) => {
56
+ effects.push(effect(action.name || '(anonymous)', {
57
+ kind: 'action',
58
+ action,
59
+ args
60
+ }));
61
+ };
62
+ enqueue.cancel = id => {
63
+ effects.push(effect('@xstate.cancel', {
64
+ source: scope.self,
65
+ id
66
+ }));
67
+ };
68
+ enqueue.emit = event => {
69
+ effects.push(effect('@xstate.emit', {
70
+ kind: 'emit',
71
+ type: '@xstate.emit',
72
+ source: scope.self,
73
+ event
74
+ }));
75
+ };
76
+ enqueue.log = (...args) => {
77
+ enqueue(scope.logger, ...args);
78
+ };
79
+ enqueue.raise = (event, options) => {
80
+ if (typeof event === 'string') {
81
+ throw new Error('Only event objects may be used with raise');
82
+ }
83
+ if (options?.delay === undefined) {
84
+ internalEvents.push(event);
85
+ } else {
86
+ effects.push(effect('@xstate.raise', {
87
+ source: scope.self,
88
+ event,
89
+ id: options.id,
90
+ delay: options.delay
91
+ }));
92
+ }
93
+ };
94
+ enqueue.sendTo = (target, event, options) => {
95
+ if (!target) {
96
+ internalEvents.push({
97
+ type: 'xstate.error.communication',
98
+ error: {
99
+ message: 'Unable to send event to an undefined actor',
100
+ event
101
+ }
102
+ });
103
+ return;
104
+ }
105
+ effects.push(effect('@xstate.sendTo', {
106
+ source: scope.self,
107
+ target,
108
+ event,
109
+ id: options?.id,
110
+ delay: options?.delay
111
+ }));
112
+ };
113
+ const spawn = (logic, options = {}) => {
114
+ if (typeof logic === 'string') {
115
+ throw new Error(`Actor source "${logic}" not found`);
116
+ }
117
+ const allocation = allocations.get(scope);
118
+ let id = options.id;
119
+ let prefix;
120
+ let next;
121
+ if (id === undefined) {
122
+ prefix = logic.id ?? 'x';
123
+ if (prefix === '(machine)') {
124
+ prefix = 'x';
125
+ }
126
+ next = allocation.counters[prefix] ?? 0;
127
+ do {
128
+ id = `${prefix}:${next++}`;
129
+ } while (allocation.ids.has(id));
130
+ allocation.counters[prefix] = next;
131
+ }
132
+ if (allocation.ids.has(id)) {
133
+ throw new Error(`Actor with id "${id}" already exists`);
134
+ }
135
+ allocation.ids.add(id);
136
+ const actor = scope.system.createActorRef(logic, {
137
+ ...options,
138
+ id,
139
+ parent: scope.self
140
+ });
141
+ effects.push(effect('@xstate.spawn', {
142
+ source: scope.self,
143
+ actor,
144
+ id,
145
+ logic,
146
+ src: logic,
147
+ input: options.input,
148
+ childUpdate: {
149
+ type: 'add',
150
+ actor,
151
+ id,
152
+ prefix,
153
+ next
154
+ }
155
+ }));
156
+ return actor;
157
+ };
158
+ enqueue.spawn = spawn;
159
+ enqueue.stop = actor => {
160
+ if (actor) {
161
+ allocations.get(scope)?.ids.delete(actor.id);
162
+ effects.push(effect('@xstate.stop', {
163
+ source: scope.self,
164
+ actor,
165
+ id: actor.id,
166
+ childUpdate: {
167
+ type: 'remove',
168
+ actor: actor
169
+ }
170
+ }));
171
+ }
172
+ };
173
+ const attach = (logic, input) => {
174
+ const actor = scope.system.createActorRef(logic, {
175
+ input,
176
+ parent: scope.self
177
+ });
178
+ effects.push(effect('@xstate.spawn', {
179
+ source: scope.self,
180
+ actor,
181
+ id: actor.id,
182
+ logic,
183
+ src: logic,
184
+ input
185
+ }));
186
+ return actor;
187
+ };
188
+ enqueue.listen = (actor, eventType, mapper) => attach(createActor.listenerLogic, {
189
+ actor,
190
+ eventType,
191
+ mapper
192
+ });
193
+ enqueue.subscribeTo = (actor, mappers) => attach(createActor.subscriptionLogic, {
194
+ actor,
195
+ mappers: typeof mappers === 'function' ? {
196
+ snapshot: mappers
197
+ } : mappers
198
+ });
199
+ return enqueue;
200
+ }
201
+ function resolveFSMEffects(snapshot, effects, scope) {
202
+ let nextSnapshot = snapshot;
203
+ for (const current of effects) {
204
+ const update = current.childUpdate;
205
+ if (update?.type === 'add') {
206
+ nextSnapshot = {
207
+ ...nextSnapshot,
208
+ children: {
209
+ ...nextSnapshot.children,
210
+ [update.id]: update.actor
211
+ },
212
+ ...(update.prefix && {
213
+ _nextActorIds: {
214
+ ...nextSnapshot._nextActorIds,
215
+ [update.prefix]: Math.max(nextSnapshot._nextActorIds?.[update.prefix] ?? 0, update.next)
216
+ }
217
+ })
218
+ };
219
+ } else if (update?.type === 'remove') {
220
+ const children = {
221
+ ...nextSnapshot.children
222
+ };
223
+ let owned = update.actor._parent === scope.self;
224
+ for (const id of Object.keys(children)) {
225
+ if (children[id] === update.actor) {
226
+ owned = true;
227
+ delete children[id];
228
+ }
229
+ }
230
+ if (!owned) {
231
+ throw new Error(`Cannot stop non-child actor ${update.actor.id}`);
232
+ }
233
+ nextSnapshot = {
234
+ ...nextSnapshot,
235
+ children
236
+ };
237
+ }
238
+ if (current.type === '@xstate.cancel') {
239
+ if (nextSnapshot.timers[current.id]) {
240
+ const timers = {
241
+ ...nextSnapshot.timers
242
+ };
243
+ delete timers[current.id];
244
+ nextSnapshot = {
245
+ ...nextSnapshot,
246
+ timers
247
+ };
248
+ }
249
+ } else if ((current.type === '@xstate.raise' || current.type === '@xstate.sendTo') && current.delay !== undefined) {
250
+ const timer = current;
251
+ const generated = timer.id === undefined;
252
+ const id = timer.id ?? `xstate.timer.auto.${nextSnapshot._nextTimerId}`;
253
+ timer.id = id;
254
+ nextSnapshot = {
255
+ ...nextSnapshot,
256
+ timers: {
257
+ ...nextSnapshot.timers,
258
+ [id]: {
259
+ id,
260
+ delay: timer.delay,
261
+ type: timer.type,
262
+ event: timer.event,
263
+ target: timer.type === '@xstate.raise' || timer.target === scope.self ? 'self' : timer.target
264
+ }
265
+ },
266
+ _nextTimerId: generated ? nextSnapshot._nextTimerId + 1 : nextSnapshot._nextTimerId
267
+ };
268
+ }
269
+ }
270
+ return [nextSnapshot, effects];
271
+ }
272
+ function appendFSMStarts(effects) {
273
+ const ordered = [];
274
+ const attached = [];
275
+ const children = [];
276
+ for (const current of effects) {
277
+ if (current.type === '@xstate.stop') {
278
+ for (const starts of [attached, children]) {
279
+ const index = starts.findIndex(start => start.actor === current.actor);
280
+ if (index !== -1) {
281
+ ordered.push(starts.splice(index, 1)[0]);
282
+ }
283
+ }
284
+ }
285
+ ordered.push(current);
286
+ if (current.type !== '@xstate.spawn') {
287
+ continue;
288
+ }
289
+ const start = effect('@xstate.start', {
290
+ actor: current.actor,
291
+ id: current.id,
292
+ source: current.source
293
+ });
294
+ (current.logic === createActor.listenerLogic || current.logic === createActor.subscriptionLogic ? attached : children).push(start);
295
+ }
296
+ return ordered.concat(attached, children);
297
+ }
298
+ function createFSMSendEffect(scope, target, event) {
299
+ return effect('@xstate.sendTo', {
300
+ source: scope.self,
301
+ target,
302
+ event
303
+ });
304
+ }
305
+ function finalizeFSMEffects(scope, previous, result) {
306
+ const [snapshot, effects] = result;
307
+ if ((snapshot.status === 'done' || snapshot.status === 'error') && previous?.status !== 'done' && previous?.status !== 'error') {
308
+ effects.push(effect('@xstate.terminate', {
309
+ source: scope.self,
310
+ actor: scope.self,
311
+ id: scope.self.id,
312
+ status: snapshot.status,
313
+ output: snapshot.output,
314
+ error: snapshot.error
315
+ }));
316
+ }
317
+ return [snapshot, effects];
318
+ }
319
+
320
+ const emptySources = {
321
+ actions: {},
322
+ actors: {},
323
+ guards: {},
324
+ delays: {}
325
+ };
326
+ const emptyExecutableActions = [];
327
+ const emptyFSMActions = [];
328
+ function assertPersistableFSM(children, timers) {
329
+ if (Object.keys(children).length) {
330
+ throw new Error('FSM child persistence requires registered sources.');
331
+ }
332
+ for (const id in timers) {
333
+ if (timers[id].target !== 'self') {
334
+ throw new Error(`FSM timer '${id}' must target self to be persisted.`);
335
+ }
336
+ }
337
+ }
338
+ function toArray(value) {
339
+ return value === undefined ? [] : Array.isArray(value) ? value : [value];
340
+ }
341
+ function resolveContext(context, input) {
342
+ return typeof context === 'function' ? context({
343
+ input
344
+ }) : context ?? {};
345
+ }
346
+ function resolveTransitionContext(context, args) {
347
+ return typeof context === 'function' ? context(args) : context;
348
+ }
349
+ function resolveInput(input, context, event) {
350
+ return typeof input === 'function' ? input({
351
+ context,
352
+ event
353
+ }) : input;
354
+ }
355
+ function mergeContextPatch(context, patch) {
356
+ for (const key of Object.keys(patch)) {
357
+ if (!Object.prototype.hasOwnProperty.call(context, key) || !Object.is(context[key], patch[key])) {
358
+ return {
359
+ ...context,
360
+ ...patch
361
+ };
362
+ }
363
+ }
364
+ return context;
365
+ }
366
+ function createSnapshot(value, context, input, machine, stateInput) {
367
+ return {
368
+ status: 'active',
369
+ output: undefined,
370
+ error: undefined,
371
+ value,
372
+ context,
373
+ input,
374
+ children: {},
375
+ timers: {},
376
+ _nextTimerId: 0,
377
+ _stateInput: stateInput,
378
+ machine
379
+ };
380
+ }
381
+ function cloneSnapshot(snapshot, value, context, stateInput) {
382
+ return {
383
+ status: snapshot.status,
384
+ output: snapshot.output,
385
+ error: snapshot.error,
386
+ value,
387
+ context,
388
+ input: snapshot.input,
389
+ children: snapshot.children,
390
+ timers: snapshot.timers,
391
+ _nextTimerId: snapshot._nextTimerId,
392
+ // Carried across state changes: dropping these would let a later spawn
393
+ // reuse the id of a still-running child.
394
+ _nextActorIds: snapshot._nextActorIds,
395
+ _stateInput: stateInput,
396
+ machine: snapshot.machine
397
+ };
398
+ }
399
+ function stopSnapshot(snapshot) {
400
+ return {
401
+ status: 'stopped',
402
+ output: undefined,
403
+ error: undefined,
404
+ value: snapshot.value,
405
+ context: snapshot.context,
406
+ input: undefined,
407
+ children: snapshot.children,
408
+ timers: snapshot.timers,
409
+ _nextTimerId: snapshot._nextTimerId,
410
+ _nextActorIds: snapshot._nextActorIds,
411
+ _stateInput: snapshot._stateInput,
412
+ machine: snapshot.machine
413
+ };
414
+ }
415
+ function cleanupFSM(snapshot, actorScope) {
416
+ const effects = [];
417
+ const enqueue = createFSMEnqueue(actorScope, effects, []);
418
+ for (const child of Object.values(snapshot.children)) {
419
+ enqueue.stop(child);
420
+ }
421
+ for (const id of Object.keys(snapshot.timers)) {
422
+ enqueue.cancel(id);
423
+ }
424
+ return resolveFSMEffects(snapshot, effects, actorScope);
425
+ }
426
+ function completeFinalState(snapshot, event, actorScope, internalQueue, states, runStateActions) {
427
+ const completed = {
428
+ ...snapshot,
429
+ status: 'done',
430
+ output: undefined,
431
+ error: undefined
432
+ };
433
+ const [exited, exitEffects] = runStateActions(completed, event, actorScope, states[completed.value]?.exit, completed._stateInput, internalQueue);
434
+ const [cleaned, cleanupEffects] = cleanupFSM(exited, actorScope);
435
+ return [cleaned, [...exitEffects, ...cleanupEffects]];
436
+ }
437
+ function invalidStringTarget(path, target) {
438
+ throw new Error(`Invalid transition for "${path}": use { target: "${target}" } instead of a string target.`);
439
+ }
440
+ function assertNoStringTransitions(config) {
441
+ for (const [stateKey, stateConfig] of Object.entries(config.states)) {
442
+ for (const [eventType, transitionConfig] of Object.entries(stateConfig.on ?? {})) {
443
+ for (const transition of toArray(transitionConfig)) {
444
+ if (typeof transition === 'string') {
445
+ invalidStringTarget(`${stateKey}.${eventType}`, transition);
446
+ }
447
+ }
448
+ }
449
+ if (stateConfig.always) {
450
+ for (const transition of toArray(stateConfig.always)) {
451
+ if (typeof transition === 'string') {
452
+ invalidStringTarget(`${stateKey}.always`, transition);
453
+ }
454
+ }
455
+ }
456
+ }
457
+ }
458
+ function createFSM(config) {
459
+ const machine = {
460
+ id: config.id ?? '(fsm)',
461
+ sources: emptySources
462
+ };
463
+ assertNoStringTransitions(config);
464
+ const runActions = (snapshot, event, actorScope, actionsConfig, internalQueue) => {
465
+ if (!actionsConfig) {
466
+ return [snapshot, emptyExecutableActions];
467
+ }
468
+ const effects = [];
469
+ const enqueue = createFSMEnqueue(actorScope, effects, internalQueue);
470
+ let context = snapshot.context;
471
+ for (const action of toArray(actionsConfig)) {
472
+ const result = action(createActor.withActorScope({
473
+ context,
474
+ event: event,
475
+ input: snapshot._stateInput,
476
+ value: snapshot.value,
477
+ children: snapshot.children
478
+ }, actorScope), enqueue);
479
+ if (result?.context) {
480
+ context = mergeContextPatch(context, result.context);
481
+ }
482
+ }
483
+ return resolveFSMEffects(context === snapshot.context ? snapshot : cloneSnapshot(snapshot, snapshot.value, context, snapshot._stateInput), effects, actorScope);
484
+ };
485
+ const runStateActions = (snapshot, event, actorScope, actionsConfig, stateInput, internalQueue) => {
486
+ if (!actionsConfig) {
487
+ return [snapshot, emptyExecutableActions];
488
+ }
489
+ const actions = [];
490
+ const enq = createFSMEnqueue(actorScope, actions, internalQueue);
491
+ let context;
492
+ const actionCount = Array.isArray(actionsConfig) ? actionsConfig.length : 1;
493
+ for (let i = 0; i < actionCount; i++) {
494
+ const action = Array.isArray(actionsConfig) ? actionsConfig[i] : actionsConfig;
495
+ const result = action(createActor.withActorScope({
496
+ context: context ?? snapshot.context,
497
+ event: event,
498
+ input: stateInput,
499
+ value: snapshot.value,
500
+ children: snapshot.children
501
+ }, actorScope), enq);
502
+ if (result?.context !== undefined) {
503
+ const currentContext = context ?? snapshot.context;
504
+ const nextContext = mergeContextPatch(currentContext, result.context);
505
+ if (nextContext !== currentContext) {
506
+ context = nextContext;
507
+ }
508
+ }
509
+ }
510
+ const nextSnapshot = context !== undefined ? cloneSnapshot(snapshot, snapshot.value, context, snapshot._stateInput) : snapshot;
511
+ return resolveFSMEffects(nextSnapshot, actions, actorScope);
512
+ };
513
+ const selectTransition = (snapshot, event, actorScope, internalQueue) => {
514
+ const state = config.states[snapshot.value];
515
+ const transitionsConfig = event.type === '' ? state?.always : state?.on?.[event.type];
516
+ if (!transitionsConfig) {
517
+ return undefined;
518
+ }
519
+ const transitionCount = Array.isArray(transitionsConfig) ? transitionsConfig.length : 1;
520
+ for (let i = 0; i < transitionCount; i++) {
521
+ const transition = Array.isArray(transitionsConfig) ? transitionsConfig[i] : transitionsConfig;
522
+ const args = createActor.withActorScope({
523
+ context: snapshot.context,
524
+ event,
525
+ input: snapshot.input,
526
+ value: snapshot.value,
527
+ children: snapshot.children
528
+ }, actorScope);
529
+ if (typeof transition === 'function') {
530
+ const actions = [];
531
+ const enq = createFSMEnqueue(actorScope, actions, internalQueue);
532
+ const result = transition(args, enq);
533
+ if (!result) {
534
+ if (actions.length) {
535
+ return {
536
+ effects: actions
537
+ };
538
+ }
539
+ continue;
540
+ }
541
+ return {
542
+ target: result.target,
543
+ context: result.context,
544
+ input: result.input,
545
+ effects: actions
546
+ };
547
+ }
548
+ if ('guard' in transition && transition.guard && !transition.guard(args)) {
549
+ continue;
550
+ }
551
+ return {
552
+ target: transition.target,
553
+ context: resolveTransitionContext(transition.context, args),
554
+ input: transition.input,
555
+ actions: 'actions' in transition && transition.actions ? toArray(transition.actions) : emptyFSMActions
556
+ };
557
+ }
558
+ return undefined;
559
+ };
560
+ const transitionCore = (snapshot, event, actorScope) => {
561
+ if (snapshot.status !== 'active') {
562
+ return [snapshot, []];
563
+ }
564
+ if (event.type === createActor.XSTATE_STOP) {
565
+ const [cleaned, cleanupEffects] = cleanupFSM(snapshot, actorScope);
566
+ return [stopSnapshot(cleaned), cleanupEffects];
567
+ }
568
+ if (event.type === createActor.XSTATE_TIMER) {
569
+ const timer = snapshot.timers[event.id];
570
+ if (!timer) {
571
+ return [snapshot, emptyExecutableActions];
572
+ }
573
+ const timers = {
574
+ ...snapshot.timers
575
+ };
576
+ delete timers[timer.id];
577
+ const nextSnapshot = {
578
+ ...snapshot,
579
+ timers
580
+ };
581
+ if (timer.type === '@xstate.raise') {
582
+ return transitionCore(nextSnapshot, timer.event, actorScope);
583
+ }
584
+ const target = timer.target === 'self' ? actorScope.self : timer.target;
585
+ return [nextSnapshot, [createFSMSendEffect(actorScope, target, timer.event)]];
586
+ }
587
+ const stateConfig = config.states[snapshot.value];
588
+ const directTransition = event.type === '' ? stateConfig?.always : stateConfig?.on?.[event.type];
589
+ if (!directTransition) {
590
+ return [snapshot, emptyExecutableActions];
591
+ }
592
+ if (!Array.isArray(directTransition) && typeof directTransition !== 'function' && !('guard' in directTransition) && !('actions' in directTransition && directTransition.actions) && typeof directTransition.input !== 'function') {
593
+ const target = directTransition.target ?? snapshot.value;
594
+ const targetState = config.states[target];
595
+ const stateChanged = target !== snapshot.value;
596
+ if (targetState?.always || stateChanged && (stateConfig.exit || targetState?.entry || targetState?.type === 'final')) ; else {
597
+ const hasContext = directTransition.context !== undefined;
598
+ const hasInput = directTransition.input !== undefined;
599
+ const resolvedContext = !createActor.isLazyActorScope(actorScope) ? resolveTransitionContext(directTransition.context, {
600
+ context: snapshot.context,
601
+ event,
602
+ input: snapshot.input,
603
+ value: snapshot.value,
604
+ self: actorScope.self,
605
+ system: actorScope.system,
606
+ parent: actorScope.self._parent,
607
+ children: snapshot.children
608
+ }) : typeof directTransition.context === 'function' ? directTransition.context(createActor.withActorScope({
609
+ context: snapshot.context,
610
+ event,
611
+ input: snapshot.input,
612
+ value: snapshot.value,
613
+ children: snapshot.children
614
+ }, actorScope)) : directTransition.context;
615
+ const context = hasContext && resolvedContext ? mergeContextPatch(snapshot.context, resolvedContext) : snapshot.context;
616
+ if (!stateChanged && context === snapshot.context && !hasInput && snapshot._stateInput === undefined) {
617
+ return [snapshot, emptyExecutableActions];
618
+ }
619
+ return [cloneSnapshot(snapshot, target, context, hasInput ? resolveInput(directTransition.input, context, event) : undefined), emptyExecutableActions];
620
+ }
621
+ }
622
+ let selectedTransition;
623
+ if (typeof directTransition === 'function' && directTransition.length < 2 && !stateConfig?.exit) {
624
+ const result = directTransition(createActor.withActorScope({
625
+ context: snapshot.context,
626
+ event,
627
+ input: snapshot.input,
628
+ value: snapshot.value,
629
+ children: snapshot.children
630
+ }, actorScope), undefined);
631
+ if (!result) {
632
+ return [snapshot, emptyExecutableActions];
633
+ }
634
+ const target = result.target ?? snapshot.value;
635
+ const targetState = config.states[target];
636
+ if (!targetState?.entry && !targetState?.always && targetState?.type !== 'final') {
637
+ const hasContext = result.context !== undefined;
638
+ const hasInput = result.input !== undefined;
639
+ const context = hasContext && result.context ? mergeContextPatch(snapshot.context, result.context) : snapshot.context;
640
+ if (target === snapshot.value && context === snapshot.context && !hasInput && snapshot._stateInput === undefined) {
641
+ return [snapshot, emptyExecutableActions];
642
+ }
643
+ return [cloneSnapshot(snapshot, target, context, hasInput ? resolveInput(result.input, context, event) : undefined), emptyExecutableActions];
644
+ }
645
+ selectedTransition = {
646
+ target: result.target,
647
+ context: result.context,
648
+ input: result.input,
649
+ actions: emptyFSMActions
650
+ };
651
+ }
652
+ let nextSnapshot = snapshot;
653
+ const executableActions = [];
654
+ const internalQueue = [event];
655
+ let iterations = 0;
656
+ while (internalQueue.length) {
657
+ if (++iterations > 1000) {
658
+ throw new Error('FSM microstep count exceeded 1000');
659
+ }
660
+ const nextEvent = internalQueue.shift();
661
+ const selected = selectedTransition ?? selectTransition(nextSnapshot, nextEvent, actorScope, internalQueue);
662
+ selectedTransition = undefined;
663
+ if (!selected) {
664
+ continue;
665
+ }
666
+ const nextValue = selected.target ?? nextSnapshot.value;
667
+ const nextState = config.states[nextValue];
668
+ const stateChanged = nextValue !== nextSnapshot.value;
669
+ if (stateChanged) {
670
+ const [exited, exitActions] = runStateActions(nextSnapshot, nextEvent, actorScope, config.states[nextSnapshot.value]?.exit, nextSnapshot._stateInput, internalQueue);
671
+ nextSnapshot = exited;
672
+ executableActions.push(...exitActions);
673
+ }
674
+ let context = nextSnapshot.context;
675
+ if (selected.context !== undefined) {
676
+ context = mergeContextPatch(context, selected.context);
677
+ }
678
+ const hasInput = selected.input !== undefined;
679
+ const stateInput = hasInput ? resolveInput(selected.input, context, nextEvent) : undefined;
680
+ if (stateChanged || context !== nextSnapshot.context || hasInput || nextSnapshot._stateInput !== undefined) {
681
+ nextSnapshot = cloneSnapshot(nextSnapshot, nextValue, context, stateInput);
682
+ }
683
+ const transitionResult = selected.effects ? resolveFSMEffects(nextSnapshot, selected.effects, actorScope) : runActions(nextSnapshot, nextEvent, actorScope, selected.actions, internalQueue);
684
+ const [afterTransition, transitionActions] = transitionResult;
685
+ nextSnapshot = afterTransition;
686
+ executableActions.push(...transitionActions);
687
+ if (stateChanged) {
688
+ const [entered, entryActions] = runStateActions(nextSnapshot, nextEvent, actorScope, nextState?.entry, stateInput, internalQueue);
689
+ nextSnapshot = entered;
690
+ executableActions.push(...entryActions);
691
+ }
692
+ if (nextState?.type === 'final') {
693
+ const [completed, completionEffects] = completeFinalState(nextSnapshot, nextEvent, actorScope, internalQueue, config.states, runStateActions);
694
+ nextSnapshot = completed;
695
+ executableActions.push(...completionEffects);
696
+ break;
697
+ }
698
+ if (nextState?.always) {
699
+ internalQueue.unshift({
700
+ type: ''
701
+ });
702
+ }
703
+ }
704
+ return [nextSnapshot, executableActions];
705
+ };
706
+ const transition = (...args) => {
707
+ beginFSMEffects(args[2], args[0]);
708
+ const [nextSnapshot, effects] = transitionCore(...args);
709
+ return finalizeFSMEffects(args[2], args[0], [nextSnapshot, appendFSMStarts(effects)]);
710
+ };
711
+ const logic = {
712
+ id: config.id,
713
+ config,
714
+ transition,
715
+ initialTransition: (input, actorScope) => {
716
+ const initialState = config.states[config.initial];
717
+ const context = resolveContext(config.context, input);
718
+ const snapshot = createSnapshot(config.initial, context, input, machine);
719
+ beginFSMEffects(actorScope, snapshot);
720
+ const internalQueue = [];
721
+ let [nextSnapshot, actions] = runStateActions(snapshot, {
722
+ type: createActor.XSTATE_INIT
723
+ }, actorScope, initialState?.entry, undefined, internalQueue);
724
+ if (!actions.length) {
725
+ actions = [];
726
+ }
727
+ if (initialState?.type === 'final') {
728
+ const [completed, completionEffects] = completeFinalState(nextSnapshot, {
729
+ type: createActor.XSTATE_INIT
730
+ }, actorScope, internalQueue, config.states, runStateActions);
731
+ nextSnapshot = completed;
732
+ actions.push(...completionEffects);
733
+ } else {
734
+ if (initialState?.always) {
735
+ internalQueue.unshift({
736
+ type: ''
737
+ });
738
+ }
739
+ while (internalQueue.length) {
740
+ const [raisedSnapshot, raisedActions] = transitionCore(nextSnapshot, internalQueue.shift(), actorScope);
741
+ nextSnapshot = raisedSnapshot;
742
+ actions.push(...raisedActions);
743
+ }
744
+ }
745
+ return finalizeFSMEffects(actorScope, undefined, [nextSnapshot, appendFSMStarts(actions)]);
746
+ },
747
+ getInitialSnapshot: (actorScope, input) => logic.initialTransition(input, actorScope)[0],
748
+ getPersistedSnapshot: currentSnapshot => {
749
+ const {
750
+ machine: _,
751
+ children,
752
+ ...snapshot
753
+ } = currentSnapshot;
754
+ assertPersistableFSM(children, snapshot.timers);
755
+ return {
756
+ ...snapshot,
757
+ children: {}
758
+ };
759
+ },
760
+ restoreSnapshot: snapshot => {
761
+ const persisted = snapshot;
762
+ const children = persisted.children ?? {};
763
+ const timers = persisted.timers ?? {};
764
+ assertPersistableFSM(children, timers);
765
+ const restored = {
766
+ ...persisted,
767
+ children: {},
768
+ timers,
769
+ machine: machine
770
+ };
771
+ return restored;
772
+ }
773
+ };
774
+ return logic;
775
+ }
776
+
777
+ exports.createFSM = createFSM;