xstate 6.0.0-alpha.2 → 6.0.0-alpha.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +19 -17
- package/bin/xstate.js +36 -0
- package/dist/{StateMachine-64813f46.cjs.js → StateMachine-1b155670.cjs.js} +182 -40
- package/dist/{StateMachine-2dcdb1f5.esm.js → StateMachine-3c17fe3e.esm.js} +182 -40
- package/dist/{StateMachine-8d22a79c.development.cjs.js → StateMachine-550ff7f2.development.cjs.js} +193 -36
- package/dist/{StateMachine-2ea0a7fa.development.esm.js → StateMachine-e25dd61f.development.esm.js} +193 -36
- package/dist/declarations/src/State.d.ts +13 -2
- package/dist/declarations/src/StateMachine.d.ts +23 -13
- package/dist/declarations/src/actions.d.ts +2 -1
- package/dist/declarations/src/actors/promise.d.ts +4 -2
- package/dist/declarations/src/createActor.d.ts +9 -11
- package/dist/declarations/src/createMachine.d.ts +14 -11
- package/dist/declarations/src/createMachineFromConfig.d.ts +61 -28
- package/dist/declarations/src/fsm.d.ts +74 -0
- package/dist/declarations/src/graph/graph.d.ts +3 -3
- package/dist/declarations/src/graph/shortestPaths.d.ts +2 -2
- package/dist/declarations/src/graph/simplePaths.d.ts +2 -2
- package/dist/declarations/src/index.d.ts +6 -5
- package/dist/declarations/src/serialize.d.ts +10 -18
- package/dist/declarations/src/setup.d.ts +277 -65
- package/dist/declarations/src/spawn.d.ts +3 -3
- package/dist/declarations/src/stateUtils.d.ts +4 -1
- package/dist/declarations/src/system.d.ts +1 -1
- package/dist/declarations/src/transition.d.ts +8 -5
- package/dist/declarations/src/transitionActions.d.ts +12 -0
- package/dist/declarations/src/types.d.ts +237 -102
- package/dist/declarations/src/types.v6.d.ts +132 -58
- package/dist/declarations/src/utils.d.ts +4 -3
- package/dist/{index-93edd3bd.esm.js → index-3f87408c.esm.js} +936 -1389
- package/dist/{index-e61170ba.development.esm.js → index-d39fb138.development.esm.js} +968 -1421
- package/dist/{index-9cb3b3a0.cjs.js → index-e694228c.cjs.js} +945 -1394
- package/dist/{index-f6ef20d1.development.cjs.js → index-ec7c7a2d.development.cjs.js} +977 -1426
- package/dist/xstate-actors.cjs.js +1 -1
- package/dist/xstate-actors.development.cjs.js +1 -1
- package/dist/xstate-actors.development.esm.js +1 -1
- package/dist/xstate-actors.esm.js +1 -1
- package/dist/xstate-actors.umd.min.js +1 -1
- package/dist/xstate-actors.umd.min.js.map +1 -1
- package/dist/xstate-graph.cjs.js +2 -2
- package/dist/xstate-graph.development.cjs.js +2 -2
- package/dist/xstate-graph.development.esm.js +2 -2
- package/dist/xstate-graph.esm.js +2 -2
- package/dist/xstate-graph.umd.min.js +1 -1
- package/dist/xstate-graph.umd.min.js.map +1 -1
- package/dist/xstate.cjs.js +1100 -182
- package/dist/xstate.cjs.mjs +3 -5
- package/dist/xstate.development.cjs.js +1100 -182
- package/dist/xstate.development.cjs.mjs +3 -5
- package/dist/xstate.development.esm.js +1101 -181
- package/dist/xstate.esm.js +1101 -181
- package/dist/xstate.umd.min.js +1 -1
- package/dist/xstate.umd.min.js.map +1 -1
- package/package.json +6 -2
- package/dist/declarations/src/atom.d.ts +0 -81
|
@@ -1,18 +1,5 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
/**
|
|
4
|
-
* Reactivity interop between actors and atoms.
|
|
5
|
-
*
|
|
6
|
-
* `Actor#get()` calls this hook (when installed) so that computed atoms can
|
|
7
|
-
* track actor reads as dependencies. The hook is installed by `atom.ts` on
|
|
8
|
-
* module evaluation, which only happens when atoms are actually used — apps
|
|
9
|
-
* that never import atoms don't bundle the reactive system.
|
|
10
|
-
*/
|
|
11
|
-
let onActorRead;
|
|
12
|
-
function installActorReadHook(hook) {
|
|
13
|
-
onActorRead = hook;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
3
|
class Mailbox {
|
|
17
4
|
constructor(_process) {
|
|
18
5
|
this._process = _process;
|
|
@@ -66,6 +53,8 @@ const NULL_EVENT = '';
|
|
|
66
53
|
const STATE_IDENTIFIER = '#';
|
|
67
54
|
const WILDCARD = '*';
|
|
68
55
|
const XSTATE_INIT = '@xstate.init';
|
|
56
|
+
const XSTATE_START = '@xstate.start';
|
|
57
|
+
const XSTATE_SPAWN = '@xstate.spawn';
|
|
69
58
|
const XSTATE_STOP = '@xstate.stop';
|
|
70
59
|
|
|
71
60
|
/**
|
|
@@ -142,6 +131,12 @@ function createErrorActorEvent(id, error) {
|
|
|
142
131
|
actorId: id
|
|
143
132
|
};
|
|
144
133
|
}
|
|
134
|
+
function createErrorPlatformEvent(kind, error) {
|
|
135
|
+
return {
|
|
136
|
+
type: `xstate.error.${kind}`,
|
|
137
|
+
error
|
|
138
|
+
};
|
|
139
|
+
}
|
|
145
140
|
function createInitEvent(input) {
|
|
146
141
|
return {
|
|
147
142
|
type: XSTATE_INIT,
|
|
@@ -166,6 +161,9 @@ function reportUnhandledError(err) {
|
|
|
166
161
|
const symbolObservable = (() => typeof Symbol === 'function' && Symbol.observable || '@@observable')();
|
|
167
162
|
|
|
168
163
|
const builtInActions = {
|
|
164
|
+
// No-op: creation/registration are eager and pure; the start is a separate
|
|
165
|
+
// deferred `@xstate.start` effect (see `deriveDeferredStarts`).
|
|
166
|
+
['@xstate.spawn']: _actor => {},
|
|
169
167
|
['@xstate.start']: actor => {
|
|
170
168
|
actor.start();
|
|
171
169
|
},
|
|
@@ -188,7 +186,7 @@ const builtInActions = {
|
|
|
188
186
|
['@xstate.cancel']: (actorScope, sendId) => {
|
|
189
187
|
actorScope.system.scheduler.cancel(actorScope.self, sendId);
|
|
190
188
|
},
|
|
191
|
-
['@xstate.
|
|
189
|
+
['@xstate.stop']: (actorScope, actor) => {
|
|
192
190
|
actorScope.stopChild(actor);
|
|
193
191
|
}
|
|
194
192
|
};
|
|
@@ -200,15 +198,20 @@ const subscriptions = /* #__PURE__ */new WeakMap();
|
|
|
200
198
|
* (`enq.listen()` / `enq.subscribeTo()`) and detaches when stopped.
|
|
201
199
|
*/
|
|
202
200
|
function createAttachedLogic(attach) {
|
|
201
|
+
const initialTransition = (input, _) => [{
|
|
202
|
+
status: 'active',
|
|
203
|
+
output: undefined,
|
|
204
|
+
error: undefined,
|
|
205
|
+
input
|
|
206
|
+
}, []];
|
|
203
207
|
return {
|
|
204
208
|
start: (state, {
|
|
205
209
|
self,
|
|
206
210
|
system
|
|
207
211
|
}) => {
|
|
208
|
-
// Don't attach if the target doesn't exist
|
|
209
|
-
// Atoms have no `getSnapshot` / lifecycle, so they always attach.
|
|
212
|
+
// Don't attach if the target doesn't exist or is stopped.
|
|
210
213
|
const target = state.input.actor;
|
|
211
|
-
if (!target ||
|
|
214
|
+
if (!target || target.getSnapshot().status === 'stopped') {
|
|
212
215
|
return;
|
|
213
216
|
}
|
|
214
217
|
const subscription = attach(state.input, {
|
|
@@ -251,20 +254,16 @@ function createAttachedLogic(attach) {
|
|
|
251
254
|
if (event.type === XSTATE_STOP) {
|
|
252
255
|
subscriptions.get(self)?.unsubscribe();
|
|
253
256
|
subscriptions.delete(self);
|
|
254
|
-
return {
|
|
257
|
+
return [{
|
|
255
258
|
...state,
|
|
256
259
|
status: 'stopped',
|
|
257
260
|
error: undefined
|
|
258
|
-
};
|
|
261
|
+
}, []];
|
|
259
262
|
}
|
|
260
|
-
return state;
|
|
263
|
+
return [state, []];
|
|
261
264
|
},
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
output: undefined,
|
|
265
|
-
error: undefined,
|
|
266
|
-
input
|
|
267
|
-
}),
|
|
265
|
+
initialTransition,
|
|
266
|
+
getInitialSnapshot: (actorScope, input) => initialTransition(input)[0],
|
|
268
267
|
getPersistedSnapshot: snapshot => snapshot,
|
|
269
268
|
restoreSnapshot: snapshot => snapshot
|
|
270
269
|
};
|
|
@@ -310,620 +309,6 @@ function createListenerLogic() {
|
|
|
310
309
|
// Singleton logic instance
|
|
311
310
|
const listenerLogic = /* #__PURE__ */createListenerLogic();
|
|
312
311
|
|
|
313
|
-
/* eslint-disable */
|
|
314
|
-
// Adapted from Alien Signals
|
|
315
|
-
// https://github.com/stackblitz/alien-signals/
|
|
316
|
-
|
|
317
|
-
let ReactiveFlags = /*#__PURE__*/function (ReactiveFlags) {
|
|
318
|
-
ReactiveFlags[ReactiveFlags["None"] = 0] = "None";
|
|
319
|
-
ReactiveFlags[ReactiveFlags["Mutable"] = 1] = "Mutable";
|
|
320
|
-
ReactiveFlags[ReactiveFlags["Watching"] = 2] = "Watching";
|
|
321
|
-
ReactiveFlags[ReactiveFlags["RecursedCheck"] = 4] = "RecursedCheck";
|
|
322
|
-
ReactiveFlags[ReactiveFlags["Recursed"] = 8] = "Recursed";
|
|
323
|
-
ReactiveFlags[ReactiveFlags["Dirty"] = 16] = "Dirty";
|
|
324
|
-
ReactiveFlags[ReactiveFlags["Pending"] = 32] = "Pending";
|
|
325
|
-
return ReactiveFlags;
|
|
326
|
-
}({});
|
|
327
|
-
function createReactiveSystem({
|
|
328
|
-
update,
|
|
329
|
-
notify,
|
|
330
|
-
unwatched
|
|
331
|
-
}) {
|
|
332
|
-
return {
|
|
333
|
-
link,
|
|
334
|
-
unlink,
|
|
335
|
-
propagate,
|
|
336
|
-
checkDirty,
|
|
337
|
-
shallowPropagate
|
|
338
|
-
};
|
|
339
|
-
function link(dep, sub, version) {
|
|
340
|
-
const prevDep = sub.depsTail;
|
|
341
|
-
if (prevDep !== undefined && prevDep.dep === dep) {
|
|
342
|
-
return;
|
|
343
|
-
}
|
|
344
|
-
const nextDep = prevDep !== undefined ? prevDep.nextDep : sub.deps;
|
|
345
|
-
if (nextDep !== undefined && nextDep.dep === dep) {
|
|
346
|
-
nextDep.version = version;
|
|
347
|
-
sub.depsTail = nextDep;
|
|
348
|
-
return;
|
|
349
|
-
}
|
|
350
|
-
const prevSub = dep.subsTail;
|
|
351
|
-
if (prevSub !== undefined && prevSub.version === version && prevSub.sub === sub) {
|
|
352
|
-
return;
|
|
353
|
-
}
|
|
354
|
-
const newLink = sub.depsTail = dep.subsTail = {
|
|
355
|
-
version,
|
|
356
|
-
dep,
|
|
357
|
-
sub,
|
|
358
|
-
prevDep,
|
|
359
|
-
nextDep,
|
|
360
|
-
prevSub,
|
|
361
|
-
nextSub: undefined
|
|
362
|
-
};
|
|
363
|
-
if (nextDep !== undefined) {
|
|
364
|
-
nextDep.prevDep = newLink;
|
|
365
|
-
}
|
|
366
|
-
if (prevDep !== undefined) {
|
|
367
|
-
prevDep.nextDep = newLink;
|
|
368
|
-
} else {
|
|
369
|
-
sub.deps = newLink;
|
|
370
|
-
}
|
|
371
|
-
if (prevSub !== undefined) {
|
|
372
|
-
prevSub.nextSub = newLink;
|
|
373
|
-
} else {
|
|
374
|
-
dep.subs = newLink;
|
|
375
|
-
}
|
|
376
|
-
}
|
|
377
|
-
function unlink(link, sub = link.sub) {
|
|
378
|
-
const dep = link.dep;
|
|
379
|
-
const prevDep = link.prevDep;
|
|
380
|
-
const nextDep = link.nextDep;
|
|
381
|
-
const nextSub = link.nextSub;
|
|
382
|
-
const prevSub = link.prevSub;
|
|
383
|
-
if (nextDep !== undefined) {
|
|
384
|
-
nextDep.prevDep = prevDep;
|
|
385
|
-
} else {
|
|
386
|
-
sub.depsTail = prevDep;
|
|
387
|
-
}
|
|
388
|
-
if (prevDep !== undefined) {
|
|
389
|
-
prevDep.nextDep = nextDep;
|
|
390
|
-
} else {
|
|
391
|
-
sub.deps = nextDep;
|
|
392
|
-
}
|
|
393
|
-
if (nextSub !== undefined) {
|
|
394
|
-
nextSub.prevSub = prevSub;
|
|
395
|
-
} else {
|
|
396
|
-
dep.subsTail = prevSub;
|
|
397
|
-
}
|
|
398
|
-
if (prevSub !== undefined) {
|
|
399
|
-
prevSub.nextSub = nextSub;
|
|
400
|
-
} else if ((dep.subs = nextSub) === undefined) {
|
|
401
|
-
unwatched(dep);
|
|
402
|
-
}
|
|
403
|
-
return nextDep;
|
|
404
|
-
}
|
|
405
|
-
function propagate(link) {
|
|
406
|
-
let next = link.nextSub;
|
|
407
|
-
let stack;
|
|
408
|
-
top: do {
|
|
409
|
-
const sub = link.sub;
|
|
410
|
-
let flags = sub.flags;
|
|
411
|
-
if (!(flags & (ReactiveFlags.RecursedCheck | ReactiveFlags.Recursed | ReactiveFlags.Dirty | ReactiveFlags.Pending))) {
|
|
412
|
-
sub.flags = flags | ReactiveFlags.Pending;
|
|
413
|
-
} else if (!(flags & (ReactiveFlags.RecursedCheck | ReactiveFlags.Recursed))) {
|
|
414
|
-
flags = ReactiveFlags.None;
|
|
415
|
-
} else if (!(flags & ReactiveFlags.RecursedCheck)) {
|
|
416
|
-
sub.flags = flags & ~ReactiveFlags.Recursed | ReactiveFlags.Pending;
|
|
417
|
-
} else if (!(flags & (ReactiveFlags.Dirty | ReactiveFlags.Pending)) && isValidLink(link, sub)) {
|
|
418
|
-
sub.flags = flags | (ReactiveFlags.Recursed | ReactiveFlags.Pending);
|
|
419
|
-
flags &= ReactiveFlags.Mutable;
|
|
420
|
-
} else {
|
|
421
|
-
flags = ReactiveFlags.None;
|
|
422
|
-
}
|
|
423
|
-
if (flags & ReactiveFlags.Watching) {
|
|
424
|
-
notify(sub);
|
|
425
|
-
}
|
|
426
|
-
if (flags & ReactiveFlags.Mutable) {
|
|
427
|
-
const subSubs = sub.subs;
|
|
428
|
-
if (subSubs !== undefined) {
|
|
429
|
-
const nextSub = (link = subSubs).nextSub;
|
|
430
|
-
if (nextSub !== undefined) {
|
|
431
|
-
stack = {
|
|
432
|
-
value: next,
|
|
433
|
-
prev: stack
|
|
434
|
-
};
|
|
435
|
-
next = nextSub;
|
|
436
|
-
}
|
|
437
|
-
continue;
|
|
438
|
-
}
|
|
439
|
-
}
|
|
440
|
-
if ((link = next) !== undefined) {
|
|
441
|
-
next = link.nextSub;
|
|
442
|
-
continue;
|
|
443
|
-
}
|
|
444
|
-
while (stack !== undefined) {
|
|
445
|
-
link = stack.value;
|
|
446
|
-
stack = stack.prev;
|
|
447
|
-
if (link !== undefined) {
|
|
448
|
-
next = link.nextSub;
|
|
449
|
-
continue top;
|
|
450
|
-
}
|
|
451
|
-
}
|
|
452
|
-
break;
|
|
453
|
-
} while (true);
|
|
454
|
-
}
|
|
455
|
-
function checkDirty(link, sub) {
|
|
456
|
-
let stack;
|
|
457
|
-
let checkDepth = 0;
|
|
458
|
-
let dirty = false;
|
|
459
|
-
top: do {
|
|
460
|
-
const dep = link.dep;
|
|
461
|
-
const flags = dep.flags;
|
|
462
|
-
if (sub.flags & ReactiveFlags.Dirty) {
|
|
463
|
-
dirty = true;
|
|
464
|
-
} else if ((flags & (ReactiveFlags.Mutable | ReactiveFlags.Dirty)) === (ReactiveFlags.Mutable | ReactiveFlags.Dirty)) {
|
|
465
|
-
if (update(dep)) {
|
|
466
|
-
const subs = dep.subs;
|
|
467
|
-
if (subs.nextSub !== undefined) {
|
|
468
|
-
shallowPropagate(subs);
|
|
469
|
-
}
|
|
470
|
-
dirty = true;
|
|
471
|
-
}
|
|
472
|
-
} else if ((flags & (ReactiveFlags.Mutable | ReactiveFlags.Pending)) === (ReactiveFlags.Mutable | ReactiveFlags.Pending)) {
|
|
473
|
-
if (link.nextSub !== undefined || link.prevSub !== undefined) {
|
|
474
|
-
stack = {
|
|
475
|
-
value: link,
|
|
476
|
-
prev: stack
|
|
477
|
-
};
|
|
478
|
-
}
|
|
479
|
-
link = dep.deps;
|
|
480
|
-
sub = dep;
|
|
481
|
-
++checkDepth;
|
|
482
|
-
continue;
|
|
483
|
-
}
|
|
484
|
-
if (!dirty) {
|
|
485
|
-
const nextDep = link.nextDep;
|
|
486
|
-
if (nextDep !== undefined) {
|
|
487
|
-
link = nextDep;
|
|
488
|
-
continue;
|
|
489
|
-
}
|
|
490
|
-
}
|
|
491
|
-
while (checkDepth--) {
|
|
492
|
-
const firstSub = sub.subs;
|
|
493
|
-
const hasMultipleSubs = firstSub.nextSub !== undefined;
|
|
494
|
-
if (hasMultipleSubs) {
|
|
495
|
-
link = stack.value;
|
|
496
|
-
stack = stack.prev;
|
|
497
|
-
} else {
|
|
498
|
-
link = firstSub;
|
|
499
|
-
}
|
|
500
|
-
if (dirty) {
|
|
501
|
-
if (update(sub)) {
|
|
502
|
-
if (hasMultipleSubs) {
|
|
503
|
-
shallowPropagate(firstSub);
|
|
504
|
-
}
|
|
505
|
-
sub = link.sub;
|
|
506
|
-
continue;
|
|
507
|
-
}
|
|
508
|
-
dirty = false;
|
|
509
|
-
} else {
|
|
510
|
-
sub.flags &= ~ReactiveFlags.Pending;
|
|
511
|
-
}
|
|
512
|
-
sub = link.sub;
|
|
513
|
-
const nextDep = link.nextDep;
|
|
514
|
-
if (nextDep !== undefined) {
|
|
515
|
-
link = nextDep;
|
|
516
|
-
continue top;
|
|
517
|
-
}
|
|
518
|
-
}
|
|
519
|
-
return dirty;
|
|
520
|
-
} while (true);
|
|
521
|
-
}
|
|
522
|
-
function shallowPropagate(link) {
|
|
523
|
-
do {
|
|
524
|
-
const sub = link.sub;
|
|
525
|
-
const flags = sub.flags;
|
|
526
|
-
if ((flags & (ReactiveFlags.Pending | ReactiveFlags.Dirty)) === ReactiveFlags.Pending) {
|
|
527
|
-
sub.flags = flags | ReactiveFlags.Dirty;
|
|
528
|
-
if ((flags & (ReactiveFlags.Watching | ReactiveFlags.RecursedCheck)) === ReactiveFlags.Watching) {
|
|
529
|
-
notify(sub);
|
|
530
|
-
}
|
|
531
|
-
}
|
|
532
|
-
} while ((link = link.nextSub) !== undefined);
|
|
533
|
-
}
|
|
534
|
-
function isValidLink(checkLink, sub) {
|
|
535
|
-
let link = sub.depsTail;
|
|
536
|
-
while (link !== undefined) {
|
|
537
|
-
if (link === checkLink) {
|
|
538
|
-
return true;
|
|
539
|
-
}
|
|
540
|
-
link = link.prevDep;
|
|
541
|
-
}
|
|
542
|
-
return false;
|
|
543
|
-
}
|
|
544
|
-
}
|
|
545
|
-
|
|
546
|
-
/**
|
|
547
|
-
* Nominal brand (phantom, compile-time only) marking a value as an XState atom.
|
|
548
|
-
* Real atoms get the runtime brand ({@link $$atom}) in `createAtom`; this type
|
|
549
|
-
* brand keeps atom-only APIs (e.g. `enq.subscribeTo`) from structurally
|
|
550
|
-
* accepting plain readables like `actor.select(...)` that would be dispatched
|
|
551
|
-
* down the non-atom path at runtime.
|
|
552
|
-
*/
|
|
553
|
-
|
|
554
|
-
/** Brand marking an object as an atom, so it can be detected structurally. */
|
|
555
|
-
const $$atom = /* #__PURE__ */Symbol.for('xstate.atom');
|
|
556
|
-
|
|
557
|
-
/**
|
|
558
|
-
* Returns `true` if the value is an atom (writable, computed, async, or
|
|
559
|
-
* reducer). Lets actor-consuming APIs (e.g. `enq.subscribeTo`) accept atoms
|
|
560
|
-
* directly.
|
|
561
|
-
*/
|
|
562
|
-
function isAtom(value) {
|
|
563
|
-
return !!value && typeof value === 'object' && $$atom in value;
|
|
564
|
-
}
|
|
565
|
-
const queuedEffects = [];
|
|
566
|
-
let cycle = 0;
|
|
567
|
-
// Initialized lazily on first atom creation (instead of at module evaluation)
|
|
568
|
-
// so the reactive system stays tree-shakeable for apps that never use atoms —
|
|
569
|
-
|
|
570
|
-
// would be retained by consumer bundlers even when atoms are unused.
|
|
571
|
-
let link;
|
|
572
|
-
let unlink;
|
|
573
|
-
let propagate;
|
|
574
|
-
let checkDirty;
|
|
575
|
-
let shallowPropagate;
|
|
576
|
-
let reactiveSystemInstalled = false;
|
|
577
|
-
function ensureReactiveSystem() {
|
|
578
|
-
if (reactiveSystemInstalled) {
|
|
579
|
-
return;
|
|
580
|
-
}
|
|
581
|
-
reactiveSystemInstalled = true;
|
|
582
|
-
({
|
|
583
|
-
link,
|
|
584
|
-
unlink,
|
|
585
|
-
propagate,
|
|
586
|
-
checkDirty,
|
|
587
|
-
shallowPropagate
|
|
588
|
-
} = createReactiveSystem({
|
|
589
|
-
update(atom) {
|
|
590
|
-
return atom._update();
|
|
591
|
-
},
|
|
592
|
-
notify(effect) {
|
|
593
|
-
queuedEffects[queuedEffectsLength++] = effect;
|
|
594
|
-
effect.flags &= ~ReactiveFlags.Watching;
|
|
595
|
-
},
|
|
596
|
-
unwatched(atom) {
|
|
597
|
-
if (atom.depsTail !== undefined) {
|
|
598
|
-
atom.depsTail = undefined;
|
|
599
|
-
atom.flags = ReactiveFlags.Mutable | ReactiveFlags.Dirty;
|
|
600
|
-
purgeDeps(atom);
|
|
601
|
-
}
|
|
602
|
-
}
|
|
603
|
-
}));
|
|
604
|
-
}
|
|
605
|
-
let notifyIndex = 0;
|
|
606
|
-
let queuedEffectsLength = 0;
|
|
607
|
-
let activeSub;
|
|
608
|
-
function purgeDeps(sub) {
|
|
609
|
-
const depsTail = sub.depsTail;
|
|
610
|
-
let dep = depsTail !== undefined ? depsTail.nextDep : sub.deps;
|
|
611
|
-
while (dep !== undefined) {
|
|
612
|
-
dep = unlink(dep, sub);
|
|
613
|
-
}
|
|
614
|
-
}
|
|
615
|
-
function flush() {
|
|
616
|
-
while (notifyIndex < queuedEffectsLength) {
|
|
617
|
-
const effect = queuedEffects[notifyIndex];
|
|
618
|
-
queuedEffects[notifyIndex++] = undefined;
|
|
619
|
-
effect.notify();
|
|
620
|
-
}
|
|
621
|
-
notifyIndex = 0;
|
|
622
|
-
queuedEffectsLength = 0;
|
|
623
|
-
}
|
|
624
|
-
|
|
625
|
-
/** The current state of an async atom. */
|
|
626
|
-
|
|
627
|
-
/** Options passed to an async atom getter. */
|
|
628
|
-
|
|
629
|
-
function compareAsyncAtomState(a, b) {
|
|
630
|
-
if (a.status !== b.status) {
|
|
631
|
-
return false;
|
|
632
|
-
}
|
|
633
|
-
if (a.status === 'done' && b.status === 'done') {
|
|
634
|
-
return Object.is(a.data, b.data);
|
|
635
|
-
}
|
|
636
|
-
if (a.status === 'error' && b.status === 'error') {
|
|
637
|
-
return Object.is(a.error, b.error);
|
|
638
|
-
}
|
|
639
|
-
// both 'pending'
|
|
640
|
-
return true;
|
|
641
|
-
}
|
|
642
|
-
function updateAsyncAtom(atom, nextValue) {
|
|
643
|
-
if (atom._update(nextValue)) {
|
|
644
|
-
const subs = atom.subs;
|
|
645
|
-
if (subs !== undefined) {
|
|
646
|
-
propagate(subs);
|
|
647
|
-
shallowPropagate(subs);
|
|
648
|
-
flush();
|
|
649
|
-
}
|
|
650
|
-
}
|
|
651
|
-
}
|
|
652
|
-
|
|
653
|
-
/**
|
|
654
|
-
* Creates a read-only atom whose value is loaded from an async getter.
|
|
655
|
-
*
|
|
656
|
-
* The getter receives an `AbortSignal`; when the async atom recomputes, the
|
|
657
|
-
* previous signal is aborted and stale resolutions are ignored.
|
|
658
|
-
*/
|
|
659
|
-
function createAsyncAtom(getValue, options) {
|
|
660
|
-
const ref = {};
|
|
661
|
-
let currentController;
|
|
662
|
-
let currentRunId = 0;
|
|
663
|
-
const atom = createAtom(() => {
|
|
664
|
-
currentController?.abort();
|
|
665
|
-
const controller = new AbortController();
|
|
666
|
-
const runId = ++currentRunId;
|
|
667
|
-
currentController = controller;
|
|
668
|
-
getValue({
|
|
669
|
-
signal: controller.signal
|
|
670
|
-
}).then(data => {
|
|
671
|
-
if (runId !== currentRunId || controller.signal.aborted) {
|
|
672
|
-
return;
|
|
673
|
-
}
|
|
674
|
-
updateAsyncAtom(ref.current, {
|
|
675
|
-
status: 'done',
|
|
676
|
-
data
|
|
677
|
-
});
|
|
678
|
-
}, error => {
|
|
679
|
-
if (runId !== currentRunId || controller.signal.aborted) {
|
|
680
|
-
return;
|
|
681
|
-
}
|
|
682
|
-
updateAsyncAtom(ref.current, {
|
|
683
|
-
status: 'error',
|
|
684
|
-
error
|
|
685
|
-
});
|
|
686
|
-
});
|
|
687
|
-
return {
|
|
688
|
-
status: 'pending'
|
|
689
|
-
};
|
|
690
|
-
}, {
|
|
691
|
-
compare: compareAsyncAtomState,
|
|
692
|
-
...options
|
|
693
|
-
});
|
|
694
|
-
ref.current = atom;
|
|
695
|
-
return atom;
|
|
696
|
-
}
|
|
697
|
-
|
|
698
|
-
/**
|
|
699
|
-
* Mirror atoms that bump a version whenever a tracked actor emits a snapshot,
|
|
700
|
-
* letting computed atoms depend on `actor.get()` reads.
|
|
701
|
-
*
|
|
702
|
-
* The hook is installed lazily on first atom creation (not at module
|
|
703
|
-
* evaluation) so that it remains tree-shakeable for apps that never use atoms —
|
|
704
|
-
* any tracked read necessarily happens inside an atom computation, so first-use
|
|
705
|
-
* installation is always early enough.
|
|
706
|
-
*/
|
|
707
|
-
let actorInteropInstalled = false;
|
|
708
|
-
function ensureActorInterop() {
|
|
709
|
-
if (actorInteropInstalled) {
|
|
710
|
-
return;
|
|
711
|
-
}
|
|
712
|
-
actorInteropInstalled = true;
|
|
713
|
-
const actorVersions = new WeakMap();
|
|
714
|
-
installActorReadHook(actorRef => {
|
|
715
|
-
if (activeSub === undefined) {
|
|
716
|
-
return;
|
|
717
|
-
}
|
|
718
|
-
let version = actorVersions.get(actorRef);
|
|
719
|
-
if (version === undefined) {
|
|
720
|
-
version = createAtom(0);
|
|
721
|
-
actorVersions.set(actorRef, version);
|
|
722
|
-
actorRef.subscribe({
|
|
723
|
-
next: () => version.set(v => v + 1),
|
|
724
|
-
error: () => {}
|
|
725
|
-
});
|
|
726
|
-
}
|
|
727
|
-
version.get();
|
|
728
|
-
});
|
|
729
|
-
}
|
|
730
|
-
|
|
731
|
-
/**
|
|
732
|
-
* Creates an atom.
|
|
733
|
-
*
|
|
734
|
-
* Pass a value for a writable atom or a getter for a computed read-only atom.
|
|
735
|
-
*/
|
|
736
|
-
|
|
737
|
-
function createAtom(valueOrFn, optionsOrInput) {
|
|
738
|
-
ensureReactiveSystem();
|
|
739
|
-
ensureActorInterop();
|
|
740
|
-
const isComputed = typeof valueOrFn === 'function';
|
|
741
|
-
const getter = valueOrFn;
|
|
742
|
-
|
|
743
|
-
// Create plain object atom
|
|
744
|
-
const atom = {
|
|
745
|
-
_snapshot: isComputed ? undefined : valueOrFn,
|
|
746
|
-
subs: undefined,
|
|
747
|
-
subsTail: undefined,
|
|
748
|
-
deps: undefined,
|
|
749
|
-
depsTail: undefined,
|
|
750
|
-
flags: isComputed ? ReactiveFlags.None : ReactiveFlags.Mutable,
|
|
751
|
-
get() {
|
|
752
|
-
if (activeSub !== undefined) {
|
|
753
|
-
link(atom, activeSub, cycle);
|
|
754
|
-
}
|
|
755
|
-
return atom._snapshot;
|
|
756
|
-
},
|
|
757
|
-
subscribe(observerOrFn) {
|
|
758
|
-
const observer = typeof observerOrFn === 'function' ? {
|
|
759
|
-
next: observerOrFn
|
|
760
|
-
} : observerOrFn;
|
|
761
|
-
const observed = {
|
|
762
|
-
current: false
|
|
763
|
-
};
|
|
764
|
-
const e = effect(() => {
|
|
765
|
-
atom.get();
|
|
766
|
-
if (!observed.current) {
|
|
767
|
-
observed.current = true;
|
|
768
|
-
} else {
|
|
769
|
-
const prevSub = activeSub;
|
|
770
|
-
activeSub = undefined;
|
|
771
|
-
try {
|
|
772
|
-
observer.next?.(atom._snapshot);
|
|
773
|
-
} catch (err) {
|
|
774
|
-
reportUnhandledError(err);
|
|
775
|
-
} finally {
|
|
776
|
-
activeSub = prevSub;
|
|
777
|
-
}
|
|
778
|
-
|
|
779
|
-
// If the observer synchronously updates any of our deps we'll be
|
|
780
|
-
// marked as dirty preventing this effect from re-running. Request
|
|
781
|
-
// the value again to reconcile any dirty deps.
|
|
782
|
-
atom.get();
|
|
783
|
-
}
|
|
784
|
-
});
|
|
785
|
-
return {
|
|
786
|
-
unsubscribe: () => {
|
|
787
|
-
e.stop();
|
|
788
|
-
}
|
|
789
|
-
};
|
|
790
|
-
},
|
|
791
|
-
_update(getValue) {
|
|
792
|
-
const prevSub = activeSub;
|
|
793
|
-
const compare = optionsOrInput?.compare ?? Object.is;
|
|
794
|
-
activeSub = atom;
|
|
795
|
-
++cycle;
|
|
796
|
-
atom.depsTail = undefined;
|
|
797
|
-
if (isComputed) {
|
|
798
|
-
atom.flags = ReactiveFlags.Mutable | ReactiveFlags.RecursedCheck;
|
|
799
|
-
}
|
|
800
|
-
try {
|
|
801
|
-
const oldValue = atom._snapshot;
|
|
802
|
-
const newValue = typeof getValue === 'function' ? getValue(oldValue) : getValue === undefined && isComputed ? getter(oldValue) : getValue;
|
|
803
|
-
if (oldValue === undefined || !compare(oldValue, newValue)) {
|
|
804
|
-
atom._snapshot = newValue;
|
|
805
|
-
return true;
|
|
806
|
-
}
|
|
807
|
-
return false;
|
|
808
|
-
} finally {
|
|
809
|
-
activeSub = prevSub;
|
|
810
|
-
if (isComputed) {
|
|
811
|
-
atom.flags &= ~ReactiveFlags.RecursedCheck;
|
|
812
|
-
}
|
|
813
|
-
purgeDeps(atom);
|
|
814
|
-
}
|
|
815
|
-
}
|
|
816
|
-
};
|
|
817
|
-
if (isComputed) {
|
|
818
|
-
atom.flags = ReactiveFlags.Mutable | ReactiveFlags.Dirty;
|
|
819
|
-
atom.get = function () {
|
|
820
|
-
const flags = atom.flags;
|
|
821
|
-
if (flags & ReactiveFlags.Dirty || flags & ReactiveFlags.Pending && checkDirty(atom.deps, atom)) {
|
|
822
|
-
if (atom._update()) {
|
|
823
|
-
const subs = atom.subs;
|
|
824
|
-
if (subs !== undefined) {
|
|
825
|
-
shallowPropagate(subs);
|
|
826
|
-
}
|
|
827
|
-
}
|
|
828
|
-
} else if (flags & ReactiveFlags.Pending) {
|
|
829
|
-
atom.flags = flags & ~ReactiveFlags.Pending;
|
|
830
|
-
}
|
|
831
|
-
if (activeSub !== undefined) {
|
|
832
|
-
link(atom, activeSub, cycle);
|
|
833
|
-
}
|
|
834
|
-
return atom._snapshot;
|
|
835
|
-
};
|
|
836
|
-
} else {
|
|
837
|
-
atom.set = function (valueOrFn) {
|
|
838
|
-
if (atom._update(valueOrFn)) {
|
|
839
|
-
const subs = atom.subs;
|
|
840
|
-
if (subs !== undefined) {
|
|
841
|
-
propagate(subs);
|
|
842
|
-
shallowPropagate(subs);
|
|
843
|
-
flush();
|
|
844
|
-
}
|
|
845
|
-
}
|
|
846
|
-
};
|
|
847
|
-
}
|
|
848
|
-
atom[$$atom] = true;
|
|
849
|
-
return atom;
|
|
850
|
-
}
|
|
851
|
-
|
|
852
|
-
/**
|
|
853
|
-
* Creates an inert atom config that can be instantiated later.
|
|
854
|
-
*
|
|
855
|
-
* Useful for framework hooks that need to create a stable local atom from
|
|
856
|
-
* component input.
|
|
857
|
-
*/
|
|
858
|
-
|
|
859
|
-
function createAtomConfig(initialValueOrFn, options) {
|
|
860
|
-
return {
|
|
861
|
-
createAtom(input) {
|
|
862
|
-
const initialValue = typeof initialValueOrFn === 'function' ? initialValueOrFn(input) : initialValueOrFn;
|
|
863
|
-
return createAtom(initialValue, options);
|
|
864
|
-
}
|
|
865
|
-
};
|
|
866
|
-
}
|
|
867
|
-
|
|
868
|
-
/** Creates an atom whose updates are handled by a reducer function. */
|
|
869
|
-
function createReducerAtom(initialValue, reducer, options) {
|
|
870
|
-
const atom = createAtom(initialValue, options);
|
|
871
|
-
return {
|
|
872
|
-
[$$atom]: true,
|
|
873
|
-
get: atom.get.bind(atom),
|
|
874
|
-
subscribe: atom.subscribe.bind(atom),
|
|
875
|
-
send(event) {
|
|
876
|
-
const prevSub = activeSub;
|
|
877
|
-
activeSub = undefined;
|
|
878
|
-
let nextState;
|
|
879
|
-
try {
|
|
880
|
-
nextState = reducer(atom.get(), event);
|
|
881
|
-
} finally {
|
|
882
|
-
activeSub = prevSub;
|
|
883
|
-
}
|
|
884
|
-
atom.set(nextState);
|
|
885
|
-
}
|
|
886
|
-
};
|
|
887
|
-
}
|
|
888
|
-
function effect(fn) {
|
|
889
|
-
const run = () => {
|
|
890
|
-
const prevSub = activeSub;
|
|
891
|
-
activeSub = effectObj;
|
|
892
|
-
++cycle;
|
|
893
|
-
effectObj.depsTail = undefined;
|
|
894
|
-
effectObj.flags = ReactiveFlags.Watching | ReactiveFlags.RecursedCheck;
|
|
895
|
-
try {
|
|
896
|
-
return fn();
|
|
897
|
-
} finally {
|
|
898
|
-
activeSub = prevSub;
|
|
899
|
-
effectObj.flags &= ~ReactiveFlags.RecursedCheck;
|
|
900
|
-
purgeDeps(effectObj);
|
|
901
|
-
}
|
|
902
|
-
};
|
|
903
|
-
const effectObj = {
|
|
904
|
-
deps: undefined,
|
|
905
|
-
depsTail: undefined,
|
|
906
|
-
subs: undefined,
|
|
907
|
-
subsTail: undefined,
|
|
908
|
-
flags: ReactiveFlags.Watching | ReactiveFlags.RecursedCheck,
|
|
909
|
-
notify() {
|
|
910
|
-
const flags = this.flags;
|
|
911
|
-
if (flags & ReactiveFlags.Dirty || flags & ReactiveFlags.Pending && checkDirty(this.deps, this)) {
|
|
912
|
-
run();
|
|
913
|
-
} else {
|
|
914
|
-
this.flags = ReactiveFlags.Watching;
|
|
915
|
-
}
|
|
916
|
-
},
|
|
917
|
-
stop() {
|
|
918
|
-
this.flags = ReactiveFlags.None;
|
|
919
|
-
this.depsTail = undefined;
|
|
920
|
-
purgeDeps(this);
|
|
921
|
-
}
|
|
922
|
-
};
|
|
923
|
-
run();
|
|
924
|
-
return effectObj;
|
|
925
|
-
}
|
|
926
|
-
|
|
927
312
|
/**
|
|
928
313
|
* Creates actor logic for subscribing to lifecycle events (done/error/snapshot)
|
|
929
314
|
* from another actor. Used internally by `enq.subscribeTo()`.
|
|
@@ -941,15 +326,6 @@ function createSubscriptionLogic() {
|
|
|
941
326
|
error,
|
|
942
327
|
snapshot: onSnapshot
|
|
943
328
|
} = mappers;
|
|
944
|
-
|
|
945
|
-
// Atoms emit a raw value (no `status`) and have no done/error lifecycle —
|
|
946
|
-
// pass the value straight to the `snapshot` mapper.
|
|
947
|
-
if (isAtom(actor)) {
|
|
948
|
-
if (!onSnapshot) {
|
|
949
|
-
return;
|
|
950
|
-
}
|
|
951
|
-
return actor.subscribe(value => relayMappedToParent(self, system, () => onSnapshot(value)));
|
|
952
|
-
}
|
|
953
329
|
return actor.subscribe({
|
|
954
330
|
next: snapshot => {
|
|
955
331
|
if (snapshot.status === 'done' && done) {
|
|
@@ -975,6 +351,19 @@ function createSubscriptionLogic() {
|
|
|
975
351
|
// Singleton logic instance
|
|
976
352
|
const subscriptionLogic = /* #__PURE__ */createSubscriptionLogic();
|
|
977
353
|
|
|
354
|
+
const builtInExecutableAction = Symbol('xstate.builtInExecutableAction');
|
|
355
|
+
function brandBuiltInExecutableAction(action) {
|
|
356
|
+
Object.defineProperty(action, builtInExecutableAction, {
|
|
357
|
+
value: true
|
|
358
|
+
});
|
|
359
|
+
return action;
|
|
360
|
+
}
|
|
361
|
+
function mergeContextPatch(context, patch) {
|
|
362
|
+
return {
|
|
363
|
+
...context,
|
|
364
|
+
...patch
|
|
365
|
+
};
|
|
366
|
+
}
|
|
978
367
|
function pushBuiltInAction(actions, action, ...args) {
|
|
979
368
|
actions.push({
|
|
980
369
|
action,
|
|
@@ -1010,11 +399,11 @@ function unregisterChild(actor) {
|
|
|
1010
399
|
_special: true
|
|
1011
400
|
});
|
|
1012
401
|
}
|
|
1013
|
-
function
|
|
1014
|
-
pushBuiltInAction(actions, builtInActions['@xstate.
|
|
402
|
+
function pushSpawnedChild(actions, actor, id) {
|
|
403
|
+
pushBuiltInAction(actions, builtInActions['@xstate.spawn'], actor);
|
|
1015
404
|
actions.push(registerSpawnedChild(actor, id ?? actor.id));
|
|
1016
405
|
}
|
|
1017
|
-
function createTransitionEnqueue(actorScope, actions, internalEvents, actorSubscriptions = false) {
|
|
406
|
+
function createTransitionEnqueue(actorScope, actions, internalEvents, actorSubscriptions = false, createActors = true) {
|
|
1018
407
|
const props = {
|
|
1019
408
|
cancel: id => {
|
|
1020
409
|
pushBuiltInAction(actions, builtInActions['@xstate.cancel'], actorScope, id);
|
|
@@ -1036,21 +425,32 @@ function createTransitionEnqueue(actorScope, actions, internalEvents, actorSubsc
|
|
|
1036
425
|
}
|
|
1037
426
|
},
|
|
1038
427
|
spawn: (logic, options) => {
|
|
428
|
+
if (!createActors) {
|
|
429
|
+
// TODO: replace this speculative placeholder with a typed inert actor ref.
|
|
430
|
+
return {
|
|
431
|
+
id: options?.id ?? options?.registryKey ?? logic.id
|
|
432
|
+
};
|
|
433
|
+
}
|
|
1039
434
|
const actor = createActor(logic, {
|
|
1040
435
|
...options,
|
|
1041
436
|
parent: actorScope.self
|
|
1042
437
|
});
|
|
1043
|
-
|
|
438
|
+
pushSpawnedChild(actions, actor, options?.id);
|
|
1044
439
|
return actor;
|
|
1045
440
|
},
|
|
1046
441
|
sendTo: (actor, event, options) => {
|
|
1047
|
-
if (actor) {
|
|
1048
|
-
|
|
442
|
+
if (!actor) {
|
|
443
|
+
internalEvents.push(createErrorPlatformEvent('communication', {
|
|
444
|
+
message: 'Unable to send event to an undefined actor',
|
|
445
|
+
event
|
|
446
|
+
}));
|
|
447
|
+
return;
|
|
1049
448
|
}
|
|
449
|
+
pushBuiltInAction(actions, builtInActions['@xstate.sendTo'], actorScope, actor, event, options);
|
|
1050
450
|
},
|
|
1051
451
|
stop: actor => {
|
|
1052
452
|
if (actor) {
|
|
1053
|
-
pushBuiltInAction(actions, builtInActions['@xstate.
|
|
453
|
+
pushBuiltInAction(actions, builtInActions['@xstate.stop'], actorScope, actor);
|
|
1054
454
|
actions.push(unregisterChild(actor));
|
|
1055
455
|
}
|
|
1056
456
|
}
|
|
@@ -1067,7 +467,7 @@ function createTransitionEnqueue(actorScope, actions, internalEvents, actorSubsc
|
|
|
1067
467
|
input,
|
|
1068
468
|
parent: actorScope.self
|
|
1069
469
|
});
|
|
1070
|
-
pushBuiltInAction(actions, builtInActions['@xstate.
|
|
470
|
+
pushBuiltInAction(actions, builtInActions['@xstate.spawn'], listenerActor);
|
|
1071
471
|
return listenerActor;
|
|
1072
472
|
},
|
|
1073
473
|
subscribeTo: (actor, mappers) => {
|
|
@@ -1082,77 +482,175 @@ function createTransitionEnqueue(actorScope, actions, internalEvents, actorSubsc
|
|
|
1082
482
|
input,
|
|
1083
483
|
parent: actorScope.self
|
|
1084
484
|
});
|
|
1085
|
-
pushBuiltInAction(actions, builtInActions['@xstate.
|
|
485
|
+
pushBuiltInAction(actions, builtInActions['@xstate.spawn'], subscriptionActor);
|
|
1086
486
|
return subscriptionActor;
|
|
1087
487
|
}
|
|
1088
488
|
});
|
|
1089
489
|
}
|
|
1090
|
-
return createEnqueueObject
|
|
490
|
+
return createEnqueueObject(props, (action, ...args) => {
|
|
1091
491
|
pushBuiltInAction(actions, action, ...args);
|
|
1092
492
|
});
|
|
1093
493
|
}
|
|
1094
|
-
function
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
494
|
+
function getBuiltInActionFields(action, args) {
|
|
495
|
+
switch (action) {
|
|
496
|
+
case builtInActions['@xstate.spawn']:
|
|
497
|
+
{
|
|
498
|
+
const [actor] = args;
|
|
499
|
+
return {
|
|
500
|
+
actor,
|
|
501
|
+
id: actor.id,
|
|
502
|
+
logic: actor.logic,
|
|
503
|
+
src: actor.src,
|
|
504
|
+
input: actor.options?.input
|
|
505
|
+
};
|
|
506
|
+
}
|
|
507
|
+
case builtInActions['@xstate.raise']:
|
|
508
|
+
{
|
|
509
|
+
const [, event, options] = args;
|
|
510
|
+
return {
|
|
511
|
+
event,
|
|
512
|
+
id: options?.id,
|
|
513
|
+
delay: options?.delay
|
|
514
|
+
};
|
|
515
|
+
}
|
|
516
|
+
case builtInActions['@xstate.sendTo']:
|
|
517
|
+
{
|
|
518
|
+
const [, target, event, options] = args;
|
|
519
|
+
return {
|
|
520
|
+
target,
|
|
521
|
+
event,
|
|
522
|
+
id: options?.id,
|
|
523
|
+
delay: options?.delay
|
|
524
|
+
};
|
|
525
|
+
}
|
|
526
|
+
case builtInActions['@xstate.cancel']:
|
|
527
|
+
{
|
|
528
|
+
const [, id] = args;
|
|
529
|
+
return {
|
|
530
|
+
id
|
|
531
|
+
};
|
|
532
|
+
}
|
|
533
|
+
case builtInActions['@xstate.stop']:
|
|
534
|
+
{
|
|
535
|
+
const [, actor] = args;
|
|
536
|
+
return {
|
|
537
|
+
actor
|
|
538
|
+
};
|
|
539
|
+
}
|
|
540
|
+
default:
|
|
541
|
+
return undefined;
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
function createStartEffect(actor) {
|
|
545
|
+
const action = builtInActions['@xstate.start'];
|
|
546
|
+
const args = [actor];
|
|
547
|
+
return brandBuiltInExecutableAction({
|
|
548
|
+
type: XSTATE_START,
|
|
549
|
+
params: {
|
|
550
|
+
action,
|
|
551
|
+
args
|
|
552
|
+
},
|
|
553
|
+
args,
|
|
554
|
+
exec: action.bind(null, actor),
|
|
555
|
+
actor,
|
|
556
|
+
id: actor.id
|
|
557
|
+
});
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
/**
|
|
561
|
+
* Attached (listener/subscription) starts are ordered before child starts so
|
|
562
|
+
* that a listener/subscription captures events emitted synchronously as its
|
|
563
|
+
* target actor starts.
|
|
564
|
+
*/
|
|
565
|
+
function deriveDeferredStarts(effects) {
|
|
566
|
+
const attachedStarts = [];
|
|
567
|
+
const childStarts = [];
|
|
568
|
+
for (const effect of effects) {
|
|
569
|
+
if (!isBuiltInExecutableAction(effect) || effect.type !== XSTATE_SPAWN) {
|
|
570
|
+
continue;
|
|
571
|
+
}
|
|
572
|
+
const {
|
|
573
|
+
actor,
|
|
574
|
+
logic
|
|
575
|
+
} = effect;
|
|
576
|
+
const start = createStartEffect(actor);
|
|
577
|
+
if (logic === listenerLogic || logic === subscriptionLogic) {
|
|
578
|
+
attachedStarts.push(start);
|
|
579
|
+
} else {
|
|
580
|
+
childStarts.push(start);
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
return [...attachedStarts, ...childStarts];
|
|
584
|
+
}
|
|
585
|
+
function isBuiltInExecutableAction(action) {
|
|
586
|
+
return action[builtInExecutableAction] === true;
|
|
587
|
+
}
|
|
588
|
+
function resolveActionsWithContext(currentSnapshot, event, actorScope, actions) {
|
|
589
|
+
let intermediateSnapshot = currentSnapshot;
|
|
590
|
+
const executableActions = [];
|
|
591
|
+
for (const action of actions) {
|
|
592
|
+
const isInline = typeof action === 'function';
|
|
593
|
+
const resolvedAction = isInline ? action : typeof action === 'object' && 'action' in action && typeof action.action === 'function' ? action.action.bind(null, ...action.args) : false;
|
|
594
|
+
const actionArgs = {
|
|
595
|
+
context: intermediateSnapshot.context,
|
|
596
|
+
event,
|
|
597
|
+
output: getEventOutput(event),
|
|
598
|
+
self: actorScope.self,
|
|
599
|
+
system: actorScope.system,
|
|
600
|
+
children: intermediateSnapshot.children,
|
|
601
|
+
parent: actorScope.self._parent,
|
|
602
|
+
actions: currentSnapshot.machine.implementations.actions,
|
|
603
|
+
actorSources: currentSnapshot.machine.implementations.actorSources
|
|
604
|
+
};
|
|
605
|
+
let actionParams = undefined;
|
|
606
|
+
if (typeof action === 'object' && action !== null) {
|
|
607
|
+
const {
|
|
608
|
+
type: _,
|
|
609
|
+
...emittedEventParams
|
|
610
|
+
} = action;
|
|
611
|
+
actionParams = emittedEventParams;
|
|
612
|
+
}
|
|
613
|
+
if (resolvedAction && '_special' in resolvedAction) {
|
|
614
|
+
executableActions.push({
|
|
615
|
+
type: typeof action === 'object' ? 'action' in action && typeof action.action === 'function' ? action.action.name ?? '(anonymous)' : action.type ?? '(anonymous)' : action.name || '(anonymous)',
|
|
616
|
+
params: actionParams,
|
|
617
|
+
args: [],
|
|
618
|
+
exec: undefined
|
|
619
|
+
});
|
|
620
|
+
const specialAction = resolvedAction;
|
|
621
|
+
const res = specialAction(actionArgs, emptyEnqueueObject);
|
|
622
|
+
if (res && ('context' in res || 'children' in res)) {
|
|
623
|
+
// Special-action patches never change `_nodes`, so a shallow clone is
|
|
624
|
+
// equivalent to `cloneMachineSnapshot` — and keeps this module (and
|
|
625
|
+
// non-machine logic like `createFSM`) independent of State.ts.
|
|
626
|
+
intermediateSnapshot = {
|
|
627
|
+
...intermediateSnapshot,
|
|
628
|
+
...(res.context !== undefined ? {
|
|
629
|
+
context: mergeContextPatch(intermediateSnapshot.context, res.context)
|
|
630
|
+
} : {}),
|
|
631
|
+
...('children' in res ? {
|
|
632
|
+
children: res.children
|
|
633
|
+
} : {})
|
|
634
|
+
};
|
|
1140
635
|
}
|
|
1141
636
|
continue;
|
|
1142
637
|
}
|
|
1143
638
|
if (!resolvedAction || !('resolve' in resolvedAction)) {
|
|
1144
|
-
|
|
639
|
+
const builtInFields = typeof action === 'object' && action !== null && 'action' in action && typeof action.action === 'function' ? getBuiltInActionFields(action.action, action.args) : undefined;
|
|
640
|
+
const executableAction = {
|
|
1145
641
|
type: typeof action === 'object' ? 'action' in action && typeof action.action === 'function' ? action.action.name ?? '(anonymous)' : action.type : action.name || '(anonymous)',
|
|
1146
642
|
params: actionParams,
|
|
1147
643
|
args: typeof action === 'object' && 'action' in action ? action.args : [],
|
|
1148
|
-
exec: resolvedAction
|
|
1149
|
-
|
|
644
|
+
exec: resolvedAction || (typeof action === 'object' && action !== null ? () => actorScope.defer(() => actorScope.emit(action)) : undefined),
|
|
645
|
+
...builtInFields
|
|
646
|
+
};
|
|
647
|
+
executableActions.push(builtInFields ? brandBuiltInExecutableAction(executableAction) : executableAction);
|
|
1150
648
|
continue;
|
|
1151
649
|
}
|
|
1152
650
|
}
|
|
1153
|
-
return intermediateSnapshot;
|
|
651
|
+
return [intermediateSnapshot, executableActions];
|
|
1154
652
|
}
|
|
1155
|
-
function createEnqueueObject
|
|
653
|
+
function createEnqueueObject(props, action) {
|
|
1156
654
|
const enqueueFn = (fn, ...args) => {
|
|
1157
655
|
action(fn, ...args);
|
|
1158
656
|
};
|
|
@@ -1170,7 +668,7 @@ function createEnqueueObject$1(props, action) {
|
|
|
1170
668
|
});
|
|
1171
669
|
return enqueueFn;
|
|
1172
670
|
}
|
|
1173
|
-
const emptyEnqueueObject = createEnqueueObject
|
|
671
|
+
const emptyEnqueueObject = createEnqueueObject({}, () => {});
|
|
1174
672
|
|
|
1175
673
|
function parseDurationToMilliseconds(duration) {
|
|
1176
674
|
const normalizedDuration = duration.trim();
|
|
@@ -1232,6 +730,9 @@ function resolveDelay(delay, delaySource, args) {
|
|
|
1232
730
|
}
|
|
1233
731
|
return configuredDelay;
|
|
1234
732
|
}
|
|
733
|
+
function getStateInput(snapshot, stateNodeId) {
|
|
734
|
+
return snapshot._stateInputs?.[stateNodeId];
|
|
735
|
+
}
|
|
1235
736
|
function isAtomicStateNode(stateNode) {
|
|
1236
737
|
return stateNode.type === 'atomic' || stateNode.type === 'final' || stateNode.type === 'choice';
|
|
1237
738
|
}
|
|
@@ -1344,22 +845,9 @@ function isInFinalState(stateNodeSet, stateNode) {
|
|
|
1344
845
|
}
|
|
1345
846
|
const isStateId = str => str[0] === STATE_IDENTIFIER;
|
|
1346
847
|
function getCandidates(stateNode, receivedEventType) {
|
|
1347
|
-
const
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
}
|
|
1351
|
-
const matchingDescriptors = [];
|
|
1352
|
-
for (const eventDescriptor of stateNode.transitions.keys()) {
|
|
1353
|
-
if (matchesEventDescriptor(receivedEventType, eventDescriptor)) {
|
|
1354
|
-
matchingDescriptors.push(eventDescriptor);
|
|
1355
|
-
}
|
|
1356
|
-
}
|
|
1357
|
-
matchingDescriptors.sort((a, b) => b.length - a.length);
|
|
1358
|
-
const wildcardCandidates = [];
|
|
1359
|
-
for (const descriptor of matchingDescriptors) {
|
|
1360
|
-
wildcardCandidates.push(...stateNode.transitions.get(descriptor));
|
|
1361
|
-
}
|
|
1362
|
-
return wildcardCandidates;
|
|
848
|
+
const exactMatch = stateNode.transitions.get(receivedEventType);
|
|
849
|
+
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));
|
|
850
|
+
return exactMatch ? [...exactMatch, ...wildcardCandidates] : wildcardCandidates;
|
|
1363
851
|
}
|
|
1364
852
|
function scheduleDelayedEvent(stateNode, event, resolveScheduledDelay) {
|
|
1365
853
|
const eventType = event.type;
|
|
@@ -1398,61 +886,69 @@ function getDelayedTransitions(stateNode) {
|
|
|
1398
886
|
}
|
|
1399
887
|
}
|
|
1400
888
|
}
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
...transition,
|
|
1414
|
-
event,
|
|
1415
|
-
delay
|
|
1416
|
-
});
|
|
1417
|
-
}
|
|
1418
|
-
};
|
|
889
|
+
|
|
890
|
+
// Every delayed transition — `after`, state-level `timeout`/`onTimeout`, and
|
|
891
|
+
// invoke-level `timeout`/`onTimeout` — has the same shape: an event raised on
|
|
892
|
+
// entry (and cancelled on exit) plus a transition that catches it. They
|
|
893
|
+
// differ only in the event raised, the transition(s) caught, and whether the
|
|
894
|
+
// delay resolves against the machine's `delays` map (invoke timeouts do not).
|
|
895
|
+
//
|
|
896
|
+
// `xstate.timeout.<id>` is a dedicated event so a state-level `timeout` cannot
|
|
897
|
+
// collide with an explicit `after` entry on the same state. Invoke timeouts
|
|
898
|
+
// are scheduled on the enclosing state; completion transitions cancel the
|
|
899
|
+
// timer separately, so it clears even when the parent state stays active.
|
|
900
|
+
const sources = [];
|
|
1419
901
|
if (afterConfig) {
|
|
1420
|
-
for (const
|
|
1421
|
-
const
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
902
|
+
for (const key of Object.keys(afterConfig)) {
|
|
903
|
+
const delay = Number.isNaN(+key) ? key : +key;
|
|
904
|
+
sources.push({
|
|
905
|
+
event: createAfterEvent(delay, stateNode.id),
|
|
906
|
+
delay,
|
|
907
|
+
transitions: afterConfig[key],
|
|
908
|
+
fromDelaysMap: true
|
|
909
|
+
});
|
|
1426
910
|
}
|
|
1427
911
|
}
|
|
1428
|
-
|
|
1429
|
-
// Desugar state-level `timeout` + `onTimeout` into a delayed transition.
|
|
1430
|
-
// Uses a dedicated `xstate.timeout.<id>` event so it cannot collide with
|
|
1431
|
-
// explicit `after` entries on the same state.
|
|
1432
912
|
if (timeoutConfig !== undefined && onTimeoutConfig) {
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
})
|
|
1439
|
-
const resolvedDelay = typeof timeoutConfig === 'function' ? timeoutConfig : getConfiguredDelayValue(timeoutConfig, stateNode.machine.implementations.delays);
|
|
1440
|
-
addDelayedTransitions(onTimeoutConfig, timeoutEventType, resolvedDelay);
|
|
913
|
+
sources.push({
|
|
914
|
+
event: createTimeoutEvent(stateNode.id),
|
|
915
|
+
delay: timeoutConfig,
|
|
916
|
+
transitions: onTimeoutConfig,
|
|
917
|
+
fromDelaysMap: true
|
|
918
|
+
});
|
|
1441
919
|
}
|
|
1442
|
-
|
|
1443
|
-
// Desugar invoke-level `timeout` + `onTimeout` into a delayed transition on
|
|
1444
|
-
// the enclosing state. Completion transitions cancel this timer separately,
|
|
1445
|
-
// so the timeout is cleared even when the parent state stays active.
|
|
1446
920
|
for (const invokeDef of invokeDefs) {
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
921
|
+
sources.push({
|
|
922
|
+
event: createInvokeTimeoutEvent(invokeDef.id),
|
|
923
|
+
delay: invokeDef.timeout,
|
|
924
|
+
transitions: invokeDef.onTimeout,
|
|
925
|
+
fromDelaysMap: false
|
|
926
|
+
});
|
|
927
|
+
}
|
|
928
|
+
|
|
929
|
+
// `delay` here is the raw config value, retained only as metadata on the
|
|
930
|
+
// transition definition — the live delay is resolved at runtime in the
|
|
931
|
+
// scheduled entry action, so no eager resolution is needed.
|
|
932
|
+
const delayedTransitions = [];
|
|
933
|
+
for (const {
|
|
934
|
+
event,
|
|
935
|
+
delay,
|
|
936
|
+
transitions,
|
|
937
|
+
fromDelaysMap
|
|
938
|
+
} of sources) {
|
|
939
|
+
const eventType = scheduleDelayedEvent(stateNode, event, x => resolveDelay(delay, fromDelaysMap ? x.delays : {}, {
|
|
1450
940
|
context: x.context,
|
|
1451
941
|
event: x.event,
|
|
1452
|
-
stateNode
|
|
942
|
+
stateNode,
|
|
943
|
+
input: x.input
|
|
1453
944
|
}));
|
|
1454
|
-
const
|
|
1455
|
-
|
|
945
|
+
for (const transition of toTransitionConfigArray(transitions)) {
|
|
946
|
+
delayedTransitions.push({
|
|
947
|
+
...transition,
|
|
948
|
+
event: eventType,
|
|
949
|
+
delay
|
|
950
|
+
});
|
|
951
|
+
}
|
|
1456
952
|
}
|
|
1457
953
|
return delayedTransitions.map(delayedTransition => ({
|
|
1458
954
|
...formatTransition(stateNode, delayedTransition.event, delayedTransition),
|
|
@@ -1514,30 +1010,13 @@ function formatRouteTransitions(rootStateNode) {
|
|
|
1514
1010
|
}
|
|
1515
1011
|
return;
|
|
1516
1012
|
}
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
// Object-form route. A `guard` (string or function) on the object is
|
|
1522
|
-
// only produced by the JSON layer (createMachineFromConfig) —
|
|
1523
|
-
// authoring uses the function form above.
|
|
1524
|
-
const userGuard = routeConfig.guard;
|
|
1013
|
+
const {
|
|
1014
|
+
guard: _guard,
|
|
1015
|
+
...routeOptions
|
|
1016
|
+
} = routeConfig;
|
|
1525
1017
|
const transition = {
|
|
1526
|
-
...
|
|
1527
|
-
guard:
|
|
1528
|
-
if (!routeMatches(args)) return false;
|
|
1529
|
-
if (typeof userGuard === 'function') return userGuard(args);
|
|
1530
|
-
if (typeof userGuard === 'string') {
|
|
1531
|
-
const guardImpl = args.guards?.[userGuard];
|
|
1532
|
-
if (!guardImpl) {
|
|
1533
|
-
// A typo'd guard name must fail loudly — silently passing
|
|
1534
|
-
// would allow the route and corrupt machine behavior.
|
|
1535
|
-
throw new Error(`Guard '${userGuard}' is not implemented in machine '${rootStateNode.machine.id}'. Available guards: ${Object.keys(args.guards ?? {}).map(key => `'${key}'`).join(', ') || '(none)'}.` );
|
|
1536
|
-
}
|
|
1537
|
-
return guardImpl(args);
|
|
1538
|
-
}
|
|
1539
|
-
return true;
|
|
1540
|
-
} : routeMatches,
|
|
1018
|
+
...routeOptions,
|
|
1019
|
+
guard: routeMatches,
|
|
1541
1020
|
target: `#${routeId}`
|
|
1542
1021
|
};
|
|
1543
1022
|
routeTransitions.push(formatTransition(rootStateNode, 'xstate.route', transition));
|
|
@@ -1576,7 +1055,7 @@ function resolveTarget(stateNode, targets) {
|
|
|
1576
1055
|
const targetStateNode = getStateNodeByPath(stateNode.parent, resolvedTarget);
|
|
1577
1056
|
return targetStateNode;
|
|
1578
1057
|
} catch (err) {
|
|
1579
|
-
throw new Error(`Invalid transition definition for state node '${stateNode.id}':\n${err.message}`);
|
|
1058
|
+
throw new Error(`Invalid transition definition for state node '${stateNode.id}':\n${err.message}` );
|
|
1580
1059
|
}
|
|
1581
1060
|
} else {
|
|
1582
1061
|
throw new Error(`Invalid target: "${target}" is not a valid target from the root node. Did you mean ".${target}"?` );
|
|
@@ -1586,6 +1065,14 @@ function resolveTarget(stateNode, targets) {
|
|
|
1586
1065
|
function resolveHistoryDefaultTransition(stateNode) {
|
|
1587
1066
|
const normalizedTarget = normalizeTarget(stateNode.config.target);
|
|
1588
1067
|
if (!normalizedTarget) {
|
|
1068
|
+
if (stateNode.parent.type === 'parallel') {
|
|
1069
|
+
return {
|
|
1070
|
+
target: [stateNode.parent],
|
|
1071
|
+
source: stateNode,
|
|
1072
|
+
reenter: false,
|
|
1073
|
+
eventType: ''
|
|
1074
|
+
};
|
|
1075
|
+
}
|
|
1589
1076
|
return stateNode.parent.initial;
|
|
1590
1077
|
}
|
|
1591
1078
|
return {
|
|
@@ -1627,12 +1114,9 @@ function getStateNode(stateNode, stateKey) {
|
|
|
1627
1114
|
if (isStateId(stateKey)) {
|
|
1628
1115
|
return stateNode.machine.getStateNodeById(stateKey);
|
|
1629
1116
|
}
|
|
1630
|
-
|
|
1631
|
-
throw new Error(`Unable to retrieve child state '${stateKey}' from '${stateNode.id}'; no child states exist.`);
|
|
1632
|
-
}
|
|
1633
|
-
const result = stateNode.states[stateKey];
|
|
1117
|
+
const result = stateNode.states?.[stateKey];
|
|
1634
1118
|
if (!result) {
|
|
1635
|
-
throw new Error(`Child state '${stateKey}' does not exist on '${stateNode.id}'`);
|
|
1119
|
+
throw new Error(!stateNode.states ? `Unable to retrieve child state '${stateKey}' from '${stateNode.id}'; no child states exist.` : `Child state '${stateKey}' does not exist on '${stateNode.id}'`);
|
|
1636
1120
|
}
|
|
1637
1121
|
return result;
|
|
1638
1122
|
}
|
|
@@ -1773,6 +1257,11 @@ function removeConflictingTransitions(enabledTransitions, stateNodeSet, snapshot
|
|
|
1773
1257
|
if (hasIntersection(getExitSet(t1), getExitSet(t2))) {
|
|
1774
1258
|
if (isDescendant(t1.source, t2.source)) {
|
|
1775
1259
|
transitionsToRemove.add(t2);
|
|
1260
|
+
} else if (t2.source.type === 'final' && t1.source.type !== 'final') {
|
|
1261
|
+
// A transition sourced in a final state yields to a conflicting
|
|
1262
|
+
// transition from a live state, so a done region doesn't keep
|
|
1263
|
+
// consuming events its siblings can still handle.
|
|
1264
|
+
transitionsToRemove.add(t2);
|
|
1776
1265
|
} else {
|
|
1777
1266
|
t1Preempted = true;
|
|
1778
1267
|
break;
|
|
@@ -1792,7 +1281,9 @@ function getEffectiveTargetStates(transition, snapshot, event, actorScope) {
|
|
|
1792
1281
|
const historyValue = snapshot.historyValue;
|
|
1793
1282
|
const {
|
|
1794
1283
|
targets
|
|
1795
|
-
} = getTransitionResult(transition, snapshot, event, actorScope
|
|
1284
|
+
} = getTransitionResult(transition, snapshot, event, actorScope, {
|
|
1285
|
+
resolveActions: false
|
|
1286
|
+
});
|
|
1796
1287
|
if (!targets) {
|
|
1797
1288
|
return [];
|
|
1798
1289
|
}
|
|
@@ -1814,19 +1305,44 @@ function getEffectiveTargetStates(transition, snapshot, event, actorScope) {
|
|
|
1814
1305
|
}
|
|
1815
1306
|
return [...targetSet];
|
|
1816
1307
|
}
|
|
1308
|
+
|
|
1309
|
+
/**
|
|
1310
|
+
* Narrows a parallel domain to the single child region that contains every
|
|
1311
|
+
* effective target, so that a transition confined to one region does not exit
|
|
1312
|
+
* (and reset) its sibling regions.
|
|
1313
|
+
*/
|
|
1314
|
+
function narrowParallelDomain(domain, targetStates) {
|
|
1315
|
+
let narrowed = domain;
|
|
1316
|
+
while (narrowed.type === 'parallel') {
|
|
1317
|
+
const region = getChildren(narrowed).find(child => targetStates.every(target => target === child || isDescendant(target, child)));
|
|
1318
|
+
if (!region) {
|
|
1319
|
+
break;
|
|
1320
|
+
}
|
|
1321
|
+
narrowed = region;
|
|
1322
|
+
}
|
|
1323
|
+
return narrowed;
|
|
1324
|
+
}
|
|
1817
1325
|
function getTransitionDomain(transition, snapshot, event, actorScope) {
|
|
1818
1326
|
const targetStates = getEffectiveTargetStates(transition, snapshot, event, actorScope);
|
|
1819
1327
|
const {
|
|
1820
1328
|
reenter
|
|
1821
|
-
} = getTransitionResult(transition, snapshot, event, actorScope
|
|
1822
|
-
|
|
1823
|
-
|
|
1329
|
+
} = getTransitionResult(transition, snapshot, event, actorScope, {
|
|
1330
|
+
resolveActions: false
|
|
1331
|
+
});
|
|
1332
|
+
if (targetStates.every(target => target === transition.source || isDescendant(target, transition.source))) {
|
|
1333
|
+
// Targets are contained within the source. A reentering transition
|
|
1334
|
+
// exits and reenters the source itself; otherwise the domain narrows
|
|
1335
|
+
// past parallel nodes so sibling regions are left untouched.
|
|
1336
|
+
return reenter ? transition.source : narrowParallelDomain(transition.source, targetStates);
|
|
1824
1337
|
}
|
|
1825
1338
|
const [head, ...tail] = targetStates.concat(transition.source);
|
|
1826
1339
|
// Find the least common ancestor (LCA) of the source and effective targets.
|
|
1827
1340
|
for (const ancestor of getProperAncestors(head, undefined)) {
|
|
1828
1341
|
if (tail.every(sn => isDescendant(sn, ancestor))) {
|
|
1829
|
-
|
|
1342
|
+
// A cross-region transition (source in one parallel region, targets in
|
|
1343
|
+
// another) only exits the region containing its targets; the source
|
|
1344
|
+
// region and other sibling regions stay put unless it reenters.
|
|
1345
|
+
return reenter ? ancestor : narrowParallelDomain(ancestor, targetStates);
|
|
1830
1346
|
}
|
|
1831
1347
|
}
|
|
1832
1348
|
|
|
@@ -1834,17 +1350,20 @@ function getTransitionDomain(transition, snapshot, event, actorScope) {
|
|
|
1834
1350
|
if (reenter) {
|
|
1835
1351
|
return;
|
|
1836
1352
|
}
|
|
1837
|
-
return transition.source.machine.root;
|
|
1353
|
+
return narrowParallelDomain(transition.source.machine.root, targetStates);
|
|
1838
1354
|
}
|
|
1839
1355
|
function computeExitSet(transitions, stateNodeSet, snapshot, event, actorScope) {
|
|
1840
1356
|
const statesToExit = new Set();
|
|
1841
1357
|
for (const transition of transitions) {
|
|
1842
1358
|
const {
|
|
1843
|
-
targets
|
|
1844
|
-
|
|
1359
|
+
targets,
|
|
1360
|
+
reenter
|
|
1361
|
+
} = getTransitionResult(transition, snapshot, event, actorScope, {
|
|
1362
|
+
resolveActions: false
|
|
1363
|
+
});
|
|
1845
1364
|
if (targets?.length) {
|
|
1846
1365
|
const domain = getTransitionDomain(transition, snapshot, event, actorScope);
|
|
1847
|
-
if (
|
|
1366
|
+
if (reenter && transition.source === domain) {
|
|
1848
1367
|
statesToExit.add(domain);
|
|
1849
1368
|
}
|
|
1850
1369
|
for (const stateNode of stateNodeSet) {
|
|
@@ -1868,16 +1387,11 @@ function initialMicrostep(root, preInitialState, actorScope, initEvent, internal
|
|
|
1868
1387
|
|
|
1869
1388
|
/** https://www.w3.org/TR/scxml/#microstepProcedure */
|
|
1870
1389
|
function microstep(transitions, currentSnapshot, actorScope, event, isInitial, internalQueue) {
|
|
1871
|
-
const
|
|
1390
|
+
const executableActions = [];
|
|
1872
1391
|
if (!transitions.length) {
|
|
1873
|
-
return [currentSnapshot,
|
|
1392
|
+
return [currentSnapshot, executableActions];
|
|
1874
1393
|
}
|
|
1875
|
-
|
|
1876
|
-
actorScope.actionExecutor = action => {
|
|
1877
|
-
actions.push(action);
|
|
1878
|
-
originalExecutor(action);
|
|
1879
|
-
};
|
|
1880
|
-
try {
|
|
1394
|
+
{
|
|
1881
1395
|
const mutStateNodeSet = new Set(currentSnapshot._nodes);
|
|
1882
1396
|
let historyValue = currentSnapshot.historyValue;
|
|
1883
1397
|
const originalContext = currentSnapshot.context;
|
|
@@ -1898,13 +1412,13 @@ function microstep(transitions, currentSnapshot, actorScope, event, isInitial, i
|
|
|
1898
1412
|
children,
|
|
1899
1413
|
system: actorScope.system,
|
|
1900
1414
|
actions: currentSnapshot.machine.implementations.actions,
|
|
1901
|
-
|
|
1415
|
+
actorSources: currentSnapshot.machine.implementations.actorSources,
|
|
1902
1416
|
guards: currentSnapshot.machine.implementations.guards,
|
|
1903
1417
|
delays: currentSnapshot.machine.implementations.delays,
|
|
1904
1418
|
input
|
|
1905
1419
|
}, enqueue);
|
|
1906
|
-
if (res?.context) {
|
|
1907
|
-
updatedContext = res.context;
|
|
1420
|
+
if (res?.context !== undefined) {
|
|
1421
|
+
updatedContext = mergeContextPatch(context, res.context);
|
|
1908
1422
|
}
|
|
1909
1423
|
return [actions, updatedContext, internalEvents];
|
|
1910
1424
|
}
|
|
@@ -1940,7 +1454,7 @@ function microstep(transitions, currentSnapshot, actorScope, event, isInitial, i
|
|
|
1940
1454
|
}
|
|
1941
1455
|
}
|
|
1942
1456
|
for (const exitStateNode of statesToExit) {
|
|
1943
|
-
const stateInput = currentSnapshot
|
|
1457
|
+
const stateInput = getStateInput(currentSnapshot, exitStateNode.id);
|
|
1944
1458
|
const [exitActions, nextContext, internalEvents] = exitStateNode.exit ? getStateActionsAndContext(exitStateNode.exit, nextState.context, currentSnapshot.children, stateInput) : [[]];
|
|
1945
1459
|
if (internalEvents?.length) {
|
|
1946
1460
|
internalQueue.push(...internalEvents);
|
|
@@ -1950,7 +1464,9 @@ function microstep(transitions, currentSnapshot, actorScope, event, isInitial, i
|
|
|
1950
1464
|
context: nextContext
|
|
1951
1465
|
});
|
|
1952
1466
|
}
|
|
1953
|
-
|
|
1467
|
+
const [resolvedState, resolvedActions] = resolveActionsWithContext(nextState, event, actorScope, exitActions);
|
|
1468
|
+
nextState = resolvedState;
|
|
1469
|
+
executableActions.push(...resolvedActions);
|
|
1954
1470
|
for (const def of exitStateNode.invoke) {
|
|
1955
1471
|
const childActor = nextState.children[def.id];
|
|
1956
1472
|
if (childActor && !childActor._isExternal) {
|
|
@@ -1975,8 +1491,8 @@ function microstep(transitions, currentSnapshot, actorScope, event, isInitial, i
|
|
|
1975
1491
|
transitionActions.push(...toArray(t.actions));
|
|
1976
1492
|
}
|
|
1977
1493
|
const res = getCurrentTransitionResult(t);
|
|
1978
|
-
if (res.context) {
|
|
1979
|
-
context = res.context;
|
|
1494
|
+
if (res.context !== undefined) {
|
|
1495
|
+
context = mergeContextPatch(context, res.context);
|
|
1980
1496
|
}
|
|
1981
1497
|
if (res.actions) {
|
|
1982
1498
|
transitionActions.push(...res.actions);
|
|
@@ -1991,17 +1507,18 @@ function microstep(transitions, currentSnapshot, actorScope, event, isInitial, i
|
|
|
1991
1507
|
const enterStates = () => {
|
|
1992
1508
|
const getMachineOutput = rootCompletionNode => {
|
|
1993
1509
|
const rootNode = nextState.machine.root;
|
|
1994
|
-
if (rootNode.output === undefined) {
|
|
1995
|
-
return;
|
|
1996
|
-
}
|
|
1997
1510
|
let completionOutput;
|
|
1998
1511
|
if (rootCompletionNode.output !== undefined && rootCompletionNode.parent) {
|
|
1999
|
-
|
|
1512
|
+
const rootDoneEvent = internalQueue.find(e => e.type === `xstate.done.state.${rootNode.id}`);
|
|
1513
|
+
completionOutput = rootDoneEvent && rootCompletionNode.parent === rootNode ? rootDoneEvent.output : resolveOutput(rootCompletionNode.output, nextState.context, event, actorScope.self, stateInputMap[rootCompletionNode.id]);
|
|
2000
1514
|
} else if (rootCompletionNode.type === 'parallel') {
|
|
2001
1515
|
const parallelDoneType = `xstate.done.state.${rootCompletionNode.id}`;
|
|
2002
1516
|
const parallelDoneEvent = internalQueue.find(e => e.type === parallelDoneType);
|
|
2003
1517
|
completionOutput = parallelDoneEvent?.output;
|
|
2004
1518
|
}
|
|
1519
|
+
if (rootNode.output === undefined) {
|
|
1520
|
+
return rootCompletionNode.parent === rootNode ? completionOutput : undefined;
|
|
1521
|
+
}
|
|
2005
1522
|
return resolveOutput(rootNode.output, nextState.context, createDoneStateEvent(rootCompletionNode.id, completionOutput), actorScope.self);
|
|
2006
1523
|
};
|
|
2007
1524
|
const statesToEnter = new Set();
|
|
@@ -2078,7 +1595,9 @@ function microstep(transitions, currentSnapshot, actorScope, event, isInitial, i
|
|
|
2078
1595
|
const {
|
|
2079
1596
|
targets,
|
|
2080
1597
|
reenter
|
|
2081
|
-
} =
|
|
1598
|
+
} = getTransitionResult(transition, currentSnapshot, event, actorScope, {
|
|
1599
|
+
resolveActions: false
|
|
1600
|
+
});
|
|
2082
1601
|
for (const targetNode of targets ?? []) {
|
|
2083
1602
|
if (!isHistoryNode(targetNode) && (transition.source !== targetNode || transition.source !== domain || reenter)) {
|
|
2084
1603
|
statesToEnter.add(targetNode);
|
|
@@ -2094,6 +1613,12 @@ function microstep(transitions, currentSnapshot, actorScope, event, isInitial, i
|
|
|
2094
1613
|
}
|
|
2095
1614
|
addAncestorStatesToEnter(ancestors, !transition.source.parent && reenter ? undefined : domain);
|
|
2096
1615
|
}
|
|
1616
|
+
if (reenter && domain === transition.source) {
|
|
1617
|
+
// A reentering transition whose domain is its own source exits and
|
|
1618
|
+
// reenters the source, so the source must be entered explicitly —
|
|
1619
|
+
// it has no ancestor inside the domain to add it.
|
|
1620
|
+
statesToEnter.add(transition.source);
|
|
1621
|
+
}
|
|
2097
1622
|
}
|
|
2098
1623
|
if (isInitial) {
|
|
2099
1624
|
statesForDefaultEntry.add(currentSnapshot.machine.root);
|
|
@@ -2101,14 +1626,18 @@ function microstep(transitions, currentSnapshot, actorScope, event, isInitial, i
|
|
|
2101
1626
|
const stateInputMap = {
|
|
2102
1627
|
...currentSnapshot._stateInputs
|
|
2103
1628
|
};
|
|
1629
|
+
let stateInputsChanged = false;
|
|
2104
1630
|
for (const transition of filteredTransitions) {
|
|
2105
1631
|
const {
|
|
2106
1632
|
targets,
|
|
2107
1633
|
input
|
|
2108
|
-
} =
|
|
1634
|
+
} = getTransitionResult(transition, currentSnapshot, event, actorScope, {
|
|
1635
|
+
resolveActions: false
|
|
1636
|
+
});
|
|
2109
1637
|
if (input && targets) {
|
|
2110
1638
|
for (const targetNode of targets) {
|
|
2111
1639
|
stateInputMap[targetNode.id] = input;
|
|
1640
|
+
stateInputsChanged = true;
|
|
2112
1641
|
}
|
|
2113
1642
|
}
|
|
2114
1643
|
}
|
|
@@ -2125,7 +1654,7 @@ function microstep(transitions, currentSnapshot, actorScope, event, isInitial, i
|
|
|
2125
1654
|
let src = invokeDef.logic;
|
|
2126
1655
|
if (typeof src === 'function') {
|
|
2127
1656
|
src = src({
|
|
2128
|
-
|
|
1657
|
+
actorSources: currentSnapshot.machine.implementations.actorSources,
|
|
2129
1658
|
context: nextState.context,
|
|
2130
1659
|
event,
|
|
2131
1660
|
self: actorScope.self
|
|
@@ -2133,12 +1662,13 @@ function microstep(transitions, currentSnapshot, actorScope, event, isInitial, i
|
|
|
2133
1662
|
}
|
|
2134
1663
|
const logic = typeof src === 'string' ? resolveReferencedActor(currentSnapshot.machine, src) : src;
|
|
2135
1664
|
if (!logic) {
|
|
2136
|
-
throw new Error(`Actor logic '${typeof src === 'string' ? src : 'inline'}' not implemented in machine '${currentSnapshot.machine.id}'`);
|
|
1665
|
+
throw new Error(`Actor logic '${typeof src === 'string' ? src : 'inline'}' not implemented in machine '${currentSnapshot.machine.id}'` );
|
|
2137
1666
|
}
|
|
2138
1667
|
const input = typeof invokeDef.input === 'function' ? invokeDef.input({
|
|
2139
1668
|
self: actorScope.self,
|
|
2140
1669
|
context: nextState.context,
|
|
2141
|
-
event
|
|
1670
|
+
event,
|
|
1671
|
+
output: getEventOutput(event)
|
|
2142
1672
|
}) : invokeDef.input;
|
|
2143
1673
|
const actor = createActor(logic, {
|
|
2144
1674
|
...invokeDef,
|
|
@@ -2147,7 +1677,7 @@ function microstep(transitions, currentSnapshot, actorScope, event, isInitial, i
|
|
|
2147
1677
|
syncSnapshot: !!invokeDef.onSnapshot
|
|
2148
1678
|
});
|
|
2149
1679
|
actions.push({
|
|
2150
|
-
action: builtInActions['@xstate.
|
|
1680
|
+
action: builtInActions['@xstate.spawn'],
|
|
2151
1681
|
args: [actor]
|
|
2152
1682
|
});
|
|
2153
1683
|
if (invokeDef.id) {
|
|
@@ -2159,18 +1689,23 @@ function microstep(transitions, currentSnapshot, actorScope, event, isInitial, i
|
|
|
2159
1689
|
children
|
|
2160
1690
|
});
|
|
2161
1691
|
}
|
|
2162
|
-
let context;
|
|
1692
|
+
let context = nextState.context;
|
|
1693
|
+
let contextChangedByEntry = false;
|
|
2163
1694
|
const stateInput = stateInputMap[stateNodeToEnter.id];
|
|
2164
1695
|
if (stateNodeToEnter.entry) {
|
|
2165
|
-
const [resultActions, nextContext, nextInternalEvents] = getStateActionsAndContext(stateNodeToEnter.entry,
|
|
1696
|
+
const [resultActions, nextContext, nextInternalEvents] = getStateActionsAndContext(stateNodeToEnter.entry, context, children, stateInput);
|
|
2166
1697
|
actions.push(...resultActions);
|
|
2167
1698
|
if (nextInternalEvents?.length) {
|
|
2168
1699
|
internalQueue.push(...nextInternalEvents);
|
|
2169
1700
|
}
|
|
2170
1701
|
if (nextContext) {
|
|
2171
1702
|
context = nextContext;
|
|
1703
|
+
contextChangedByEntry = true;
|
|
2172
1704
|
}
|
|
2173
1705
|
}
|
|
1706
|
+
if (contextChangedByEntry) {
|
|
1707
|
+
nextState.context = context;
|
|
1708
|
+
}
|
|
2174
1709
|
if (statesForDefaultEntry.has(stateNodeToEnter)) {
|
|
2175
1710
|
const {
|
|
2176
1711
|
actions: initialActions,
|
|
@@ -2182,13 +1717,14 @@ function microstep(transitions, currentSnapshot, actorScope, event, isInitial, i
|
|
|
2182
1717
|
if (initialInput && stateNodeToEnter.initial?.target) {
|
|
2183
1718
|
for (const targetNode of stateNodeToEnter.initial.target) {
|
|
2184
1719
|
stateInputMap[targetNode.id] = initialInput;
|
|
1720
|
+
stateInputsChanged = true;
|
|
2185
1721
|
}
|
|
2186
1722
|
}
|
|
2187
1723
|
}
|
|
2188
|
-
|
|
2189
|
-
|
|
2190
|
-
|
|
2191
|
-
|
|
1724
|
+
const [resolvedState, resolvedActions] = resolveActionsWithContext(nextState, event, actorScope, actions);
|
|
1725
|
+
nextState = resolvedState;
|
|
1726
|
+
actions.length = 0;
|
|
1727
|
+
executableActions.push(...resolvedActions);
|
|
2192
1728
|
if (stateNodeToEnter.type !== 'final') {
|
|
2193
1729
|
continue;
|
|
2194
1730
|
}
|
|
@@ -2196,14 +1732,14 @@ function microstep(transitions, currentSnapshot, actorScope, event, isInitial, i
|
|
|
2196
1732
|
let ancestorMarker = parent?.type === 'parallel' ? parent : parent?.parent;
|
|
2197
1733
|
let rootCompletionNode = ancestorMarker || stateNodeToEnter;
|
|
2198
1734
|
if (parent?.type === 'compound') {
|
|
2199
|
-
internalQueue.push(createDoneStateEvent(parent.id, stateNodeToEnter.output !== undefined ? resolveOutput(stateNodeToEnter.output, nextState.context, event, actorScope.self) : undefined));
|
|
1735
|
+
internalQueue.push(createDoneStateEvent(parent.id, stateNodeToEnter.output !== undefined ? resolveOutput(stateNodeToEnter.output, nextState.context, event, actorScope.self, stateInput) : undefined));
|
|
2200
1736
|
}
|
|
2201
1737
|
while (ancestorMarker?.type === 'parallel' && !completedNodes.has(ancestorMarker) && isInFinalState(mutStateNodeSet, ancestorMarker)) {
|
|
2202
1738
|
completedNodes.add(ancestorMarker);
|
|
2203
1739
|
const regionOutput = {};
|
|
2204
1740
|
for (const region of getChildren(ancestorMarker)) {
|
|
2205
1741
|
if (region.type === 'final') {
|
|
2206
|
-
regionOutput[region.key] = region.output !== undefined ? resolveOutput(region.output, nextState.context, event, actorScope.self) : undefined;
|
|
1742
|
+
regionOutput[region.key] = region.output !== undefined ? resolveOutput(region.output, nextState.context, event, actorScope.self, stateInputMap[region.id]) : undefined;
|
|
2207
1743
|
continue;
|
|
2208
1744
|
}
|
|
2209
1745
|
if (region.type === 'parallel') {
|
|
@@ -2213,7 +1749,7 @@ function microstep(transitions, currentSnapshot, actorScope, event, isInitial, i
|
|
|
2213
1749
|
continue;
|
|
2214
1750
|
}
|
|
2215
1751
|
const finalChild = getChildren(region).find(s => s.type === 'final' && mutStateNodeSet.has(s));
|
|
2216
|
-
regionOutput[region.key] = finalChild?.output !== undefined ? resolveOutput(finalChild.output, nextState.context, event, actorScope.self) : undefined;
|
|
1752
|
+
regionOutput[region.key] = finalChild?.output !== undefined ? resolveOutput(finalChild.output, nextState.context, event, actorScope.self, stateInputMap[finalChild.id]) : undefined;
|
|
2217
1753
|
}
|
|
2218
1754
|
internalQueue.push(createDoneStateEvent(ancestorMarker.id, regionOutput));
|
|
2219
1755
|
rootCompletionNode = ancestorMarker;
|
|
@@ -2227,8 +1763,7 @@ function microstep(transitions, currentSnapshot, actorScope, event, isInitial, i
|
|
|
2227
1763
|
output: getMachineOutput(rootCompletionNode)
|
|
2228
1764
|
});
|
|
2229
1765
|
}
|
|
2230
|
-
|
|
2231
|
-
if (inputChanged) {
|
|
1766
|
+
if (stateInputsChanged) {
|
|
2232
1767
|
nextState = cloneMachineSnapshot(nextState, {
|
|
2233
1768
|
_stateInputs: stateInputMap
|
|
2234
1769
|
});
|
|
@@ -2236,7 +1771,9 @@ function microstep(transitions, currentSnapshot, actorScope, event, isInitial, i
|
|
|
2236
1771
|
};
|
|
2237
1772
|
|
|
2238
1773
|
// Execute transition content
|
|
2239
|
-
|
|
1774
|
+
const [resolvedTransitionState, transitionExecutableActions] = resolveActionsWithContext(nextState, event, actorScope, transitionActions);
|
|
1775
|
+
nextState = resolvedTransitionState;
|
|
1776
|
+
executableActions.push(...transitionExecutableActions);
|
|
2240
1777
|
if (context && context !== currentSnapshot.context) {
|
|
2241
1778
|
nextState = cloneMachineSnapshot(nextState, {
|
|
2242
1779
|
context
|
|
@@ -2251,7 +1788,7 @@ function microstep(transitions, currentSnapshot, actorScope, event, isInitial, i
|
|
|
2251
1788
|
const nextStateNodesToExit = nextStateNodes.sort((a, b) => b.order - a.order);
|
|
2252
1789
|
nextStateNodesToExit.forEach(stateNode => {
|
|
2253
1790
|
if (stateNode.exit) {
|
|
2254
|
-
const stateInput = nextState
|
|
1791
|
+
const stateInput = getStateInput(nextState, stateNode.id);
|
|
2255
1792
|
const [exitActions,, nextInternalEvents] = getStateActionsAndContext(stateNode.exit, nextState.context, nextState.children, stateInput);
|
|
2256
1793
|
allExitActions.push(...exitActions);
|
|
2257
1794
|
if (nextInternalEvents?.length) {
|
|
@@ -2259,22 +1796,22 @@ function microstep(transitions, currentSnapshot, actorScope, event, isInitial, i
|
|
|
2259
1796
|
}
|
|
2260
1797
|
}
|
|
2261
1798
|
});
|
|
2262
|
-
|
|
1799
|
+
const [resolvedState, resolvedActions] = resolveActionsWithContext(nextState, event, actorScope, allExitActions);
|
|
1800
|
+
nextState = resolvedState;
|
|
1801
|
+
executableActions.push(...resolvedActions);
|
|
2263
1802
|
}
|
|
2264
1803
|
if (historyValue === currentSnapshot.historyValue && currentSnapshot._nodes.length === mutStateNodeSet.size && currentSnapshot._nodes.every(node => mutStateNodeSet.has(node))) {
|
|
2265
1804
|
// If context was changed (e.g. by entry actions during self-transition),
|
|
2266
1805
|
// clone to ensure reference inequality for eventless transition re-evaluation
|
|
2267
1806
|
if (nextState.context !== originalContext) {
|
|
2268
|
-
return [cloneMachineSnapshot(nextState),
|
|
1807
|
+
return [cloneMachineSnapshot(nextState), executableActions];
|
|
2269
1808
|
}
|
|
2270
|
-
return [nextState,
|
|
1809
|
+
return [nextState, executableActions];
|
|
2271
1810
|
}
|
|
2272
1811
|
return [cloneMachineSnapshot(nextState, {
|
|
2273
1812
|
_nodes: nextStateNodes,
|
|
2274
1813
|
historyValue
|
|
2275
|
-
}),
|
|
2276
|
-
} finally {
|
|
2277
|
-
actorScope.actionExecutor = originalExecutor;
|
|
1814
|
+
}), executableActions];
|
|
2278
1815
|
}
|
|
2279
1816
|
}
|
|
2280
1817
|
|
|
@@ -2282,29 +1819,33 @@ function microstep(transitions, currentSnapshot, actorScope, event, isInitial, i
|
|
|
2282
1819
|
* Gets the transition result for a given transition without executing the
|
|
2283
1820
|
* transition.
|
|
2284
1821
|
*/
|
|
2285
|
-
function getTransitionResult(transition, snapshot, event, actorScope) {
|
|
1822
|
+
function getTransitionResult(transition, snapshot, event, actorScope, options) {
|
|
1823
|
+
const transitionArgs = {
|
|
1824
|
+
context: snapshot.context,
|
|
1825
|
+
event,
|
|
1826
|
+
output: getEventOutput(event),
|
|
1827
|
+
value: snapshot.value,
|
|
1828
|
+
children: snapshot.children,
|
|
1829
|
+
system: actorScope.system,
|
|
1830
|
+
parent: actorScope.self._parent,
|
|
1831
|
+
self: actorScope.self,
|
|
1832
|
+
actions: snapshot.machine.implementations.actions,
|
|
1833
|
+
actorSources: snapshot.machine.implementations.actorSources,
|
|
1834
|
+
guards: snapshot.machine.implementations.guards,
|
|
1835
|
+
delays: snapshot.machine.implementations.delays,
|
|
1836
|
+
input: getStateInput(snapshot, transition.source.id)
|
|
1837
|
+
};
|
|
2286
1838
|
if (transition.to) {
|
|
2287
1839
|
const actions = [];
|
|
2288
1840
|
const internalEvents = [];
|
|
2289
|
-
const enqueue = createTransitionEnqueue(actorScope, actions, internalEvents);
|
|
2290
|
-
const res = transition.to(
|
|
2291
|
-
context: snapshot.context,
|
|
2292
|
-
event,
|
|
2293
|
-
value: snapshot.value,
|
|
2294
|
-
children: snapshot.children,
|
|
2295
|
-
parent: actorScope.self._parent,
|
|
2296
|
-
self: actorScope.self,
|
|
2297
|
-
actions: snapshot.machine.implementations.actions,
|
|
2298
|
-
actors: snapshot.machine.implementations.actors,
|
|
2299
|
-
guards: snapshot.machine.implementations.guards,
|
|
2300
|
-
delays: snapshot.machine.implementations.delays
|
|
2301
|
-
}, enqueue);
|
|
1841
|
+
const enqueue = createTransitionEnqueue(actorScope, actions, internalEvents, false, options?.resolveActions ?? true);
|
|
1842
|
+
const res = transition.to(transitionArgs, enqueue);
|
|
2302
1843
|
const targets = res?.target ? resolveTarget(transition.source, toArray(res.target)) : undefined;
|
|
2303
|
-
|
|
2304
|
-
const resolvedInput = typeof transition.input === 'function' ? transition.input({
|
|
1844
|
+
const resolvedInput = res?.input ?? (typeof transition.input === 'function' ? transition.input({
|
|
2305
1845
|
context: snapshot.context,
|
|
2306
|
-
event
|
|
2307
|
-
|
|
1846
|
+
event,
|
|
1847
|
+
output: getEventOutput(event)
|
|
1848
|
+
}) : transition.input);
|
|
2308
1849
|
return {
|
|
2309
1850
|
targets: targets,
|
|
2310
1851
|
context: res?.context,
|
|
@@ -2318,11 +1859,13 @@ function getTransitionResult(transition, snapshot, event, actorScope) {
|
|
|
2318
1859
|
// Resolve input for regular transitions
|
|
2319
1860
|
const resolvedInput = typeof transition.input === 'function' ? transition.input({
|
|
2320
1861
|
context: snapshot.context,
|
|
2321
|
-
event
|
|
1862
|
+
event,
|
|
1863
|
+
output: getEventOutput(event)
|
|
2322
1864
|
}) : transition.input;
|
|
1865
|
+
const resolvedContext = typeof transition.context === 'function' ? transition.context(transitionArgs) : transition.context;
|
|
2323
1866
|
return {
|
|
2324
1867
|
targets: transition.target,
|
|
2325
|
-
context:
|
|
1868
|
+
context: resolvedContext,
|
|
2326
1869
|
reenter: transition.reenter,
|
|
2327
1870
|
actions: undefined,
|
|
2328
1871
|
internalEvents: undefined,
|
|
@@ -2358,7 +1901,7 @@ function macrostep(snapshot, event, actorScope, internalQueue) {
|
|
|
2358
1901
|
// Determine the next state based on the next microstep
|
|
2359
1902
|
if (nextEvent.type !== XSTATE_INIT) {
|
|
2360
1903
|
const currentEvent = nextEvent;
|
|
2361
|
-
const isErr =
|
|
1904
|
+
const isErr = isErrorEvent(currentEvent);
|
|
2362
1905
|
const transitions = nextSnapshot.machine.getTransitionData(nextSnapshot, currentEvent, actorScope.self);
|
|
2363
1906
|
if (isErr && !transitions.length) {
|
|
2364
1907
|
// TODO: we should likely only allow transitions selected by very explicit descriptors
|
|
@@ -2429,31 +1972,13 @@ function resolveStateValue(rootNode, stateValue) {
|
|
|
2429
1972
|
const allStateNodes = getAllStateNodes(getStateNodes(rootNode, stateValue));
|
|
2430
1973
|
return getStateValue(rootNode, allStateNodes);
|
|
2431
1974
|
}
|
|
2432
|
-
function createEnqueueObject(props, action) {
|
|
2433
|
-
const enqueueFn = (fn, ...args) => {
|
|
2434
|
-
action(fn, ...args);
|
|
2435
|
-
};
|
|
2436
|
-
Object.assign(enqueueFn, {
|
|
2437
|
-
cancel: () => {},
|
|
2438
|
-
emit: () => {},
|
|
2439
|
-
log: () => {},
|
|
2440
|
-
raise: () => {},
|
|
2441
|
-
spawn: () => ({}),
|
|
2442
|
-
sendTo: () => {},
|
|
2443
|
-
stop: () => {},
|
|
2444
|
-
listen: () => ({}),
|
|
2445
|
-
subscribeTo: () => ({}),
|
|
2446
|
-
...props
|
|
2447
|
-
});
|
|
2448
|
-
return enqueueFn;
|
|
2449
|
-
}
|
|
2450
1975
|
function hasEffect(transition, context, event, snapshot, self) {
|
|
2451
1976
|
if (transition.to) {
|
|
2452
|
-
return transitionToHasEffect(transition.to, context, event, snapshot, self, snapshot.machine.implementations);
|
|
1977
|
+
return transitionToHasEffect(transition.to, context, event, snapshot, self, snapshot.machine.implementations, transition.source.id);
|
|
2453
1978
|
}
|
|
2454
1979
|
return false;
|
|
2455
1980
|
}
|
|
2456
|
-
function transitionToHasEffect(transitionTo, context, event, snapshot, self, implementations) {
|
|
1981
|
+
function transitionToHasEffect(transitionTo, context, event, snapshot, self, implementations, sourceId) {
|
|
2457
1982
|
let hasEffect = false;
|
|
2458
1983
|
let res;
|
|
2459
1984
|
try {
|
|
@@ -2464,16 +1989,19 @@ function transitionToHasEffect(transitionTo, context, event, snapshot, self, imp
|
|
|
2464
1989
|
res = transitionTo({
|
|
2465
1990
|
context,
|
|
2466
1991
|
event,
|
|
1992
|
+
output: getEventOutput(event),
|
|
2467
1993
|
self,
|
|
1994
|
+
system: self.system,
|
|
2468
1995
|
value: snapshot.value,
|
|
2469
1996
|
children: snapshot.children,
|
|
2470
1997
|
parent: {
|
|
2471
1998
|
send: triggerEffect
|
|
2472
1999
|
},
|
|
2473
2000
|
actions: implementations.actions,
|
|
2474
|
-
|
|
2001
|
+
actorSources: implementations.actorSources,
|
|
2475
2002
|
guards: implementations.guards,
|
|
2476
|
-
delays: implementations.delays
|
|
2003
|
+
delays: implementations.delays,
|
|
2004
|
+
input: getStateInput(snapshot, sourceId)
|
|
2477
2005
|
}, createEnqueueObject({
|
|
2478
2006
|
emit: triggerEffect,
|
|
2479
2007
|
cancel: triggerEffect,
|
|
@@ -2526,38 +2054,22 @@ function evaluateCandidate(candidate, event, snapshot, stateNode, self) {
|
|
|
2526
2054
|
const guardArgs = {
|
|
2527
2055
|
context: snapshot.context,
|
|
2528
2056
|
event,
|
|
2057
|
+
output: getEventOutput(event),
|
|
2529
2058
|
self,
|
|
2530
2059
|
parent: self._parent,
|
|
2531
2060
|
children: snapshot.children,
|
|
2532
2061
|
actions: stateNode.machine.implementations.actions,
|
|
2533
|
-
|
|
2062
|
+
actorSources: stateNode.machine.implementations.actorSources,
|
|
2534
2063
|
guards: stateNode.machine.implementations.guards,
|
|
2535
2064
|
delays: stateNode.machine.implementations.delays,
|
|
2536
2065
|
_snapshot: snapshot
|
|
2537
2066
|
};
|
|
2538
|
-
|
|
2539
|
-
const guardParams = typeof guardConfig?.params === 'function' ? guardConfig.params({
|
|
2540
|
-
context: snapshot.context,
|
|
2541
|
-
event
|
|
2542
|
-
}) : guardConfig?.params;
|
|
2543
|
-
let guardPassed = true;
|
|
2544
|
-
if (typeof guardConfig === 'function') {
|
|
2545
|
-
guardPassed = guardConfig(guardArgs, guardParams);
|
|
2546
|
-
} else if (typeof guardConfig?.type === 'string') {
|
|
2547
|
-
const guardImpl = stateNode.machine.implementations.guards[guardConfig.type];
|
|
2548
|
-
if (!guardImpl) {
|
|
2549
|
-
// A typo'd guard name must fail loudly — silently passing would
|
|
2550
|
-
// take the guarded transition and corrupt machine behavior.
|
|
2551
|
-
throw new Error(`Guard '${guardConfig.type}' is not implemented in machine '${stateNode.machine.id}'. Available guards: ${Object.keys(stateNode.machine.implementations.guards).map(key => `'${key}'`).join(', ') || '(none)'}.` );
|
|
2552
|
-
}
|
|
2553
|
-
guardPassed = guardImpl(guardArgs, guardParams);
|
|
2554
|
-
}
|
|
2555
|
-
if (!guardPassed) {
|
|
2067
|
+
if (!candidate.guard(guardArgs)) {
|
|
2556
2068
|
return false;
|
|
2557
2069
|
}
|
|
2558
2070
|
}
|
|
2559
2071
|
if (candidate.to) {
|
|
2560
|
-
return transitionToHasEffect(candidate.to, snapshot.context, event, snapshot, self, stateNode.machine.implementations);
|
|
2072
|
+
return transitionToHasEffect(candidate.to, snapshot.context, event, snapshot, self, stateNode.machine.implementations, candidate.source.id);
|
|
2561
2073
|
}
|
|
2562
2074
|
return true;
|
|
2563
2075
|
}
|
|
@@ -2565,29 +2077,6 @@ function evaluateCandidate(candidate, event, snapshot, stateNode, self) {
|
|
|
2565
2077
|
function isMachineSnapshot(value) {
|
|
2566
2078
|
return !!value && typeof value === 'object' && 'machine' in value && 'value' in value;
|
|
2567
2079
|
}
|
|
2568
|
-
let emptyCanActor;
|
|
2569
|
-
let emptyCanActorScope;
|
|
2570
|
-
function getEmptyCanActor() {
|
|
2571
|
-
return emptyCanActor ??= createEmptyActor();
|
|
2572
|
-
}
|
|
2573
|
-
function getEmptyCanActorScope() {
|
|
2574
|
-
if (emptyCanActorScope) {
|
|
2575
|
-
return emptyCanActorScope;
|
|
2576
|
-
}
|
|
2577
|
-
const actor = getEmptyCanActor();
|
|
2578
|
-
emptyCanActorScope = {
|
|
2579
|
-
self: actor,
|
|
2580
|
-
logger: () => {},
|
|
2581
|
-
id: '',
|
|
2582
|
-
sessionId: '',
|
|
2583
|
-
defer: () => {},
|
|
2584
|
-
system: actor.system,
|
|
2585
|
-
stopChild: () => {},
|
|
2586
|
-
emit: () => {},
|
|
2587
|
-
actionExecutor: () => {}
|
|
2588
|
-
};
|
|
2589
|
-
return emptyCanActorScope;
|
|
2590
|
-
}
|
|
2591
2080
|
const machineSnapshotMatches = function matches(testValue) {
|
|
2592
2081
|
return matchesState(testValue, this.value);
|
|
2593
2082
|
};
|
|
@@ -2598,24 +2087,10 @@ const machineSnapshotCan = function can(event) {
|
|
|
2598
2087
|
if (!this.machine) {
|
|
2599
2088
|
console.warn(`state.can(...) used outside of a machine-created State object; this will always return false.`);
|
|
2600
2089
|
}
|
|
2601
|
-
const emptyActor = getEmptyCanActor();
|
|
2602
|
-
const emptyActorScope = getEmptyCanActorScope();
|
|
2603
|
-
const transitionData = this.machine.getTransitionData(this, event, emptyActor);
|
|
2604
|
-
if (!transitionData?.length) {
|
|
2605
|
-
return false;
|
|
2606
|
-
}
|
|
2607
2090
|
|
|
2608
|
-
//
|
|
2609
|
-
|
|
2610
|
-
|
|
2611
|
-
return true;
|
|
2612
|
-
}
|
|
2613
|
-
const res = getTransitionResult(transition, this, event, emptyActorScope);
|
|
2614
|
-
if (res.targets?.length || res.context || hasEffect(transition, this.context, event, this, emptyActor)) {
|
|
2615
|
-
return true;
|
|
2616
|
-
}
|
|
2617
|
-
}
|
|
2618
|
-
return false;
|
|
2091
|
+
// The dry-run logic lives on the machine so that non-machine bundles
|
|
2092
|
+
// (e.g. `createFSM`) don't pay for the transition-resolution machinery.
|
|
2093
|
+
return this.machine?._canTransition(this, event) ?? false;
|
|
2619
2094
|
};
|
|
2620
2095
|
const machineSnapshotToJSON = function toJSON() {
|
|
2621
2096
|
const {
|
|
@@ -2746,7 +2221,7 @@ function getPersistedSnapshot(snapshot, options) {
|
|
|
2746
2221
|
childrenJson[id] = {
|
|
2747
2222
|
snapshot: child.getPersistedSnapshot(options),
|
|
2748
2223
|
src: child.src,
|
|
2749
|
-
|
|
2224
|
+
registryKey: child.registryKey,
|
|
2750
2225
|
syncSnapshot: child._syncSnapshot
|
|
2751
2226
|
};
|
|
2752
2227
|
}
|
|
@@ -2756,6 +2231,9 @@ function getPersistedSnapshot(snapshot, options) {
|
|
|
2756
2231
|
children: childrenJson,
|
|
2757
2232
|
historyValue: serializeHistoryValue(jsonValues.historyValue)
|
|
2758
2233
|
};
|
|
2234
|
+
if (_stateInputs && Object.keys(_stateInputs).length > 0) {
|
|
2235
|
+
persisted.stateInputs = _stateInputs;
|
|
2236
|
+
}
|
|
2759
2237
|
if (machine.version !== undefined) {
|
|
2760
2238
|
persisted.version = machine.version;
|
|
2761
2239
|
}
|
|
@@ -2891,24 +2369,38 @@ function toArray(value) {
|
|
|
2891
2369
|
}
|
|
2892
2370
|
return toArrayStrict(value);
|
|
2893
2371
|
}
|
|
2894
|
-
function resolveOutput(mapper, context, event, self) {
|
|
2372
|
+
function resolveOutput(mapper, context, event, self, input) {
|
|
2895
2373
|
if (typeof mapper === 'function') {
|
|
2896
|
-
|
|
2374
|
+
const outputMapper = mapper;
|
|
2375
|
+
const args = {
|
|
2897
2376
|
context,
|
|
2898
2377
|
event,
|
|
2899
|
-
|
|
2900
|
-
|
|
2378
|
+
output: getEventOutput(event),
|
|
2379
|
+
self,
|
|
2380
|
+
input
|
|
2381
|
+
};
|
|
2382
|
+
return outputMapper(args);
|
|
2901
2383
|
}
|
|
2902
2384
|
if (!!mapper && typeof mapper === 'object' && Object.values(mapper).some(val => typeof val === 'function')) {
|
|
2903
2385
|
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('')}`);
|
|
2904
2386
|
}
|
|
2905
2387
|
return mapper;
|
|
2906
2388
|
}
|
|
2389
|
+
function getEventOutput(event) {
|
|
2390
|
+
if (isDoneEvent(event)) {
|
|
2391
|
+
const doneEvent = event;
|
|
2392
|
+
return doneEvent.output;
|
|
2393
|
+
}
|
|
2394
|
+
return undefined;
|
|
2395
|
+
}
|
|
2396
|
+
function isDoneEvent(event) {
|
|
2397
|
+
return event.type.startsWith('xstate.done.actor.') || event.type.startsWith('xstate.done.state.');
|
|
2398
|
+
}
|
|
2907
2399
|
function isArray(value) {
|
|
2908
2400
|
return Array.isArray(value);
|
|
2909
2401
|
}
|
|
2910
|
-
function
|
|
2911
|
-
return event.type.startsWith('xstate.error.
|
|
2402
|
+
function isErrorEvent(event) {
|
|
2403
|
+
return event.type.startsWith('xstate.error.');
|
|
2912
2404
|
}
|
|
2913
2405
|
function toTransitionConfigArray(configLike) {
|
|
2914
2406
|
return toArrayStrict(configLike).map(transitionLike => {
|
|
@@ -2946,14 +2438,14 @@ function createInvokeId(stateNodeId, index) {
|
|
|
2946
2438
|
function resolveReferencedActor(machine, src) {
|
|
2947
2439
|
const match = src.match(/^xstate\.invoke\.(\d+)\.(.*)/);
|
|
2948
2440
|
if (!match) {
|
|
2949
|
-
return machine.implementations.
|
|
2441
|
+
return machine.implementations.actorSources[src];
|
|
2950
2442
|
}
|
|
2951
2443
|
const [, indexStr, nodeId] = match;
|
|
2952
2444
|
const node = machine.getStateNodeById(nodeId);
|
|
2953
2445
|
const invokeConfig = node.config.invoke;
|
|
2954
2446
|
const configSrc = (Array.isArray(invokeConfig) ? invokeConfig[indexStr] : invokeConfig).src;
|
|
2955
2447
|
// A referenced actor may itself be registered by name.
|
|
2956
|
-
return typeof configSrc === 'string' ? machine.implementations.
|
|
2448
|
+
return typeof configSrc === 'string' ? machine.implementations.actorSources[configSrc] : configSrc;
|
|
2957
2449
|
}
|
|
2958
2450
|
function getAllOwnEventDescriptors(snapshot) {
|
|
2959
2451
|
return [...new Set([...snapshot._nodes.flatMap(sn => sn.ownEvents)])];
|
|
@@ -3006,7 +2498,7 @@ function matchesEventDescriptor(eventType, descriptor) {
|
|
|
3006
2498
|
function createScheduledEventId(actor, id) {
|
|
3007
2499
|
return `${actor.sessionId}.${id}`;
|
|
3008
2500
|
}
|
|
3009
|
-
function
|
|
2501
|
+
function createRuntimeSystem(rootActor, options) {
|
|
3010
2502
|
let idCounter = 0;
|
|
3011
2503
|
const children = new Map();
|
|
3012
2504
|
const keyedActors = new Map();
|
|
@@ -3029,373 +2521,150 @@ function createSystem(rootActor, options) {
|
|
|
3029
2521
|
collected.push({
|
|
3030
2522
|
targetRef: target,
|
|
3031
2523
|
targetId: target.id,
|
|
3032
|
-
event,
|
|
3033
|
-
delay,
|
|
3034
|
-
id
|
|
3035
|
-
});
|
|
3036
|
-
};
|
|
3037
|
-
const scheduler = {
|
|
3038
|
-
schedule: (source, target, event, delay, id = Math.random().toString(36).slice(2)) => {
|
|
3039
|
-
recordSent(source, target, event, delay, id);
|
|
3040
|
-
const scheduledEvent = {
|
|
3041
|
-
source,
|
|
3042
|
-
target,
|
|
3043
|
-
event,
|
|
3044
|
-
delay,
|
|
3045
|
-
id,
|
|
3046
|
-
startedAt: Date.now()
|
|
3047
|
-
};
|
|
3048
|
-
const scheduledEventId = createScheduledEventId(source, id);
|
|
3049
|
-
system._snapshot._scheduledEvents[scheduledEventId] = scheduledEvent;
|
|
3050
|
-
const timeout = clock.setTimeout(() => {
|
|
3051
|
-
delete timerMap[scheduledEventId];
|
|
3052
|
-
delete system._snapshot._scheduledEvents[scheduledEventId];
|
|
3053
|
-
|
|
3054
|
-
// The send was already recorded at schedule time on the sender's
|
|
3055
|
-
// transition, so deliver without re-recording.
|
|
3056
|
-
deliver(source, target, event);
|
|
3057
|
-
}, delay);
|
|
3058
|
-
timerMap[scheduledEventId] = timeout;
|
|
3059
|
-
},
|
|
3060
|
-
cancel: (source, id) => {
|
|
3061
|
-
const scheduledEventId = createScheduledEventId(source, id);
|
|
3062
|
-
const timeout = timerMap[scheduledEventId];
|
|
3063
|
-
delete timerMap[scheduledEventId];
|
|
3064
|
-
delete system._snapshot._scheduledEvents[scheduledEventId];
|
|
3065
|
-
if (timeout !== undefined) {
|
|
3066
|
-
clock.clearTimeout(timeout);
|
|
3067
|
-
}
|
|
3068
|
-
},
|
|
3069
|
-
cancelAll: actor => {
|
|
3070
|
-
for (const scheduledEventId in system._snapshot._scheduledEvents) {
|
|
3071
|
-
const scheduledEvent = system._snapshot._scheduledEvents[scheduledEventId];
|
|
3072
|
-
if (scheduledEvent.source === actor) {
|
|
3073
|
-
scheduler.cancel(actor, scheduledEvent.id);
|
|
3074
|
-
}
|
|
3075
|
-
}
|
|
3076
|
-
}
|
|
3077
|
-
};
|
|
3078
|
-
// Delivers an event to the target actor. Used by both `_relay` (which also
|
|
3079
|
-
// records the send) and the scheduler's timer (which already recorded it).
|
|
3080
|
-
const deliver = (source, target, event) => {
|
|
3081
|
-
const targetMachine = target.logic;
|
|
3082
|
-
const isInternalEvent = typeof targetMachine?.isInternalEventType === 'function' && targetMachine.isInternalEventType(event.type);
|
|
3083
|
-
if (isInternalEvent && source !== target) {
|
|
3084
|
-
throw new Error(`Internal event "${event.type}" cannot be sent to actor "${target.id}" from outside.`);
|
|
3085
|
-
}
|
|
3086
|
-
|
|
3087
|
-
// remember the last source for unified transition inspect event
|
|
3088
|
-
target._lastSourceRef = source;
|
|
3089
|
-
target._send(event);
|
|
3090
|
-
};
|
|
3091
|
-
const sendInspectionEvent = event => {
|
|
3092
|
-
if (!inspectionObservers.size) {
|
|
3093
|
-
return;
|
|
3094
|
-
}
|
|
3095
|
-
const resolvedInspectionEvent = {
|
|
3096
|
-
...event,
|
|
3097
|
-
rootId: rootActor.sessionId
|
|
3098
|
-
};
|
|
3099
|
-
inspectionObservers.forEach(observer => observer.next?.(resolvedInspectionEvent));
|
|
3100
|
-
};
|
|
3101
|
-
const system = {
|
|
3102
|
-
children,
|
|
3103
|
-
reverseKeyedActors,
|
|
3104
|
-
keyedActors,
|
|
3105
|
-
_snapshot: {
|
|
3106
|
-
_scheduledEvents: (options?.snapshot && options.snapshot.scheduler) ?? {}
|
|
3107
|
-
},
|
|
3108
|
-
_bookId: () => `x:${idCounter++}`,
|
|
3109
|
-
_register: (sessionId, actor) => {
|
|
3110
|
-
children.set(sessionId, actor);
|
|
3111
|
-
return sessionId;
|
|
3112
|
-
},
|
|
3113
|
-
_unregister: actor => {
|
|
3114
|
-
children.delete(actor.sessionId);
|
|
3115
|
-
const systemId = reverseKeyedActors.get(actor);
|
|
3116
|
-
if (systemId !== undefined) {
|
|
3117
|
-
keyedActors.delete(systemId);
|
|
3118
|
-
reverseKeyedActors.delete(actor);
|
|
3119
|
-
}
|
|
3120
|
-
},
|
|
3121
|
-
get: systemId => {
|
|
3122
|
-
return keyedActors.get(systemId);
|
|
3123
|
-
},
|
|
3124
|
-
getAll: () => {
|
|
3125
|
-
return Object.fromEntries(keyedActors.entries());
|
|
3126
|
-
},
|
|
3127
|
-
_set: (systemId, actor) => {
|
|
3128
|
-
const existing = keyedActors.get(systemId);
|
|
3129
|
-
if (existing && existing !== actor) {
|
|
3130
|
-
throw new Error(`Actor with system ID '${systemId}' already exists.`);
|
|
3131
|
-
}
|
|
3132
|
-
keyedActors.set(systemId, actor);
|
|
3133
|
-
reverseKeyedActors.set(actor, systemId);
|
|
3134
|
-
},
|
|
3135
|
-
inspect: observerOrFn => {
|
|
3136
|
-
const observer = toObserver(observerOrFn);
|
|
3137
|
-
inspectionObservers.add(observer);
|
|
3138
|
-
return {
|
|
3139
|
-
unsubscribe() {
|
|
3140
|
-
inspectionObservers.delete(observer);
|
|
3141
|
-
}
|
|
3142
|
-
};
|
|
3143
|
-
},
|
|
3144
|
-
_sendInspectionEvent: sendInspectionEvent,
|
|
3145
|
-
_relay: (source, target, event) => {
|
|
3146
|
-
recordSent(source, target, event);
|
|
3147
|
-
deliver(source, target, event);
|
|
3148
|
-
},
|
|
3149
|
-
scheduler,
|
|
3150
|
-
getSnapshot: () => {
|
|
3151
|
-
return {
|
|
3152
|
-
_scheduledEvents: {
|
|
3153
|
-
...system._snapshot._scheduledEvents
|
|
3154
|
-
}
|
|
3155
|
-
};
|
|
3156
|
-
},
|
|
3157
|
-
start: () => {
|
|
3158
|
-
const scheduledEvents = system._snapshot._scheduledEvents;
|
|
3159
|
-
system._snapshot._scheduledEvents = {};
|
|
3160
|
-
for (const scheduledId in scheduledEvents) {
|
|
3161
|
-
const {
|
|
3162
|
-
source,
|
|
3163
|
-
target,
|
|
3164
|
-
event,
|
|
3165
|
-
delay,
|
|
3166
|
-
id
|
|
3167
|
-
} = scheduledEvents[scheduledId];
|
|
3168
|
-
scheduler.schedule(source, target, event, delay, id);
|
|
3169
|
-
}
|
|
3170
|
-
},
|
|
3171
|
-
_clock: clock,
|
|
3172
|
-
_logger: logger,
|
|
3173
|
-
_timerStrategy: options.timers
|
|
3174
|
-
};
|
|
3175
|
-
return system;
|
|
3176
|
-
}
|
|
3177
|
-
|
|
3178
|
-
const effectStates = new WeakMap();
|
|
3179
|
-
const XSTATE_LOGIC_EFFECT_RESOLVE = 'xstate.logic.effect.resolve';
|
|
3180
|
-
const XSTATE_LOGIC_EFFECT_REJECT = 'xstate.logic.effect.reject';
|
|
3181
|
-
const XSTATE_LOGIC_EFFECT_START = 'xstate.logic.effect.start';
|
|
3182
|
-
function getEffectState(self) {
|
|
3183
|
-
let state = effectStates.get(self);
|
|
3184
|
-
if (!state) {
|
|
3185
|
-
state = new Map();
|
|
3186
|
-
effectStates.set(self, state);
|
|
3187
|
-
}
|
|
3188
|
-
return state;
|
|
3189
|
-
}
|
|
3190
|
-
function executeLogicEffects(effects, actorScope) {
|
|
3191
|
-
if (!effects?.length) {
|
|
3192
|
-
return;
|
|
3193
|
-
}
|
|
3194
|
-
for (const effect of effects) {
|
|
3195
|
-
switch (effect.type) {
|
|
3196
|
-
case 'emit':
|
|
3197
|
-
actorScope.emit(effect.event);
|
|
3198
|
-
break;
|
|
3199
|
-
case 'sendBack':
|
|
3200
|
-
{
|
|
3201
|
-
const parent = actorScope.self._parent;
|
|
3202
|
-
if (parent) {
|
|
3203
|
-
actorScope.system._relay(actorScope.self, parent, effect.event);
|
|
3204
|
-
}
|
|
3205
|
-
break;
|
|
3206
|
-
}
|
|
3207
|
-
case 'raise':
|
|
3208
|
-
actorScope.system._relay(actorScope.self, actorScope.self, effect.event);
|
|
3209
|
-
break;
|
|
3210
|
-
case 'effect':
|
|
3211
|
-
{
|
|
3212
|
-
if (!effect.key) {
|
|
3213
|
-
const cleanup = effect.exec();
|
|
3214
|
-
if (typeof cleanup === 'function') {
|
|
3215
|
-
getEffectState(actorScope.self).set(Symbol(), {
|
|
3216
|
-
cleanup
|
|
3217
|
-
});
|
|
3218
|
-
}
|
|
3219
|
-
break;
|
|
3220
|
-
}
|
|
3221
|
-
const state = getEffectState(actorScope.self);
|
|
3222
|
-
if (state.has(effect.key)) {
|
|
3223
|
-
break;
|
|
3224
|
-
}
|
|
3225
|
-
const cleanup = effect.exec();
|
|
3226
|
-
state.set(effect.key, {
|
|
3227
|
-
cleanup: typeof cleanup === 'function' ? cleanup : undefined
|
|
3228
|
-
});
|
|
3229
|
-
break;
|
|
3230
|
-
}
|
|
3231
|
-
case 'cleanupEffects':
|
|
3232
|
-
{
|
|
3233
|
-
const state = effectStates.get(actorScope.self);
|
|
3234
|
-
if (!state) {
|
|
3235
|
-
break;
|
|
3236
|
-
}
|
|
3237
|
-
for (const {
|
|
3238
|
-
cleanup
|
|
3239
|
-
} of state.values()) {
|
|
3240
|
-
cleanup?.();
|
|
3241
|
-
}
|
|
3242
|
-
effectStates.delete(actorScope.self);
|
|
3243
|
-
break;
|
|
3244
|
-
}
|
|
3245
|
-
}
|
|
3246
|
-
}
|
|
3247
|
-
}
|
|
3248
|
-
function resolveContext(context, input) {
|
|
3249
|
-
return typeof context === 'function' ? context({
|
|
3250
|
-
input
|
|
3251
|
-
}) : context;
|
|
3252
|
-
}
|
|
3253
|
-
function createLogic(config) {
|
|
3254
|
-
const transition = (snapshot, event, actorScope) => {
|
|
3255
|
-
if (snapshot.status !== 'active') {
|
|
3256
|
-
return [snapshot, []];
|
|
3257
|
-
}
|
|
3258
|
-
if (event.type === XSTATE_STOP) {
|
|
3259
|
-
return [{
|
|
3260
|
-
...snapshot,
|
|
3261
|
-
status: 'stopped',
|
|
3262
|
-
input: undefined
|
|
3263
|
-
}, [{
|
|
3264
|
-
type: 'cleanupEffects'
|
|
3265
|
-
}]];
|
|
3266
|
-
}
|
|
3267
|
-
if (event.type === XSTATE_LOGIC_EFFECT_START) {
|
|
3268
|
-
return [{
|
|
3269
|
-
...snapshot,
|
|
3270
|
-
effects: {
|
|
3271
|
-
...snapshot.effects,
|
|
3272
|
-
[event.key]: {
|
|
3273
|
-
status: 'active'
|
|
3274
|
-
}
|
|
3275
|
-
}
|
|
3276
|
-
}, []];
|
|
3277
|
-
}
|
|
3278
|
-
if (event.type === XSTATE_LOGIC_EFFECT_RESOLVE) {
|
|
3279
|
-
return [{
|
|
3280
|
-
...snapshot,
|
|
3281
|
-
effects: {
|
|
3282
|
-
...snapshot.effects,
|
|
3283
|
-
[event.key]: {
|
|
3284
|
-
status: 'done',
|
|
3285
|
-
output: event.output
|
|
3286
|
-
}
|
|
3287
|
-
}
|
|
3288
|
-
}, []];
|
|
3289
|
-
}
|
|
3290
|
-
if (event.type === XSTATE_LOGIC_EFFECT_REJECT) {
|
|
3291
|
-
return [{
|
|
3292
|
-
...snapshot,
|
|
3293
|
-
effects: {
|
|
3294
|
-
...snapshot.effects,
|
|
3295
|
-
[event.key]: {
|
|
3296
|
-
status: 'error',
|
|
3297
|
-
error: event.error
|
|
3298
|
-
}
|
|
3299
|
-
}
|
|
3300
|
-
}, []];
|
|
3301
|
-
}
|
|
3302
|
-
const effects = [];
|
|
3303
|
-
const trackedEffects = {};
|
|
3304
|
-
const enqueueEffect = (key, exec) => {
|
|
3305
|
-
if (snapshot.effects?.[key]) {
|
|
3306
|
-
return;
|
|
3307
|
-
}
|
|
3308
|
-
effects.push({
|
|
3309
|
-
type: 'effect',
|
|
3310
|
-
key,
|
|
3311
|
-
exec
|
|
3312
|
-
});
|
|
3313
|
-
trackedEffects[key] = {
|
|
3314
|
-
status: 'active'
|
|
2524
|
+
event,
|
|
2525
|
+
delay,
|
|
2526
|
+
id
|
|
2527
|
+
});
|
|
2528
|
+
};
|
|
2529
|
+
const scheduler = {
|
|
2530
|
+
schedule: (source, target, event, delay, id = Math.random().toString(36).slice(2)) => {
|
|
2531
|
+
recordSent(source, target, event, delay, id);
|
|
2532
|
+
const scheduledEvent = {
|
|
2533
|
+
source,
|
|
2534
|
+
target,
|
|
2535
|
+
event,
|
|
2536
|
+
delay,
|
|
2537
|
+
id,
|
|
2538
|
+
startedAt: Date.now()
|
|
3315
2539
|
};
|
|
3316
|
-
|
|
3317
|
-
|
|
3318
|
-
|
|
3319
|
-
|
|
3320
|
-
|
|
3321
|
-
|
|
3322
|
-
|
|
3323
|
-
|
|
3324
|
-
|
|
3325
|
-
|
|
3326
|
-
|
|
3327
|
-
|
|
3328
|
-
|
|
3329
|
-
|
|
3330
|
-
|
|
3331
|
-
|
|
3332
|
-
|
|
3333
|
-
|
|
3334
|
-
|
|
3335
|
-
}
|
|
3336
|
-
|
|
3337
|
-
|
|
3338
|
-
|
|
3339
|
-
|
|
2540
|
+
const scheduledEventId = createScheduledEventId(source, id);
|
|
2541
|
+
system._snapshot._scheduledEvents[scheduledEventId] = scheduledEvent;
|
|
2542
|
+
const timeout = clock.setTimeout(() => {
|
|
2543
|
+
delete timerMap[scheduledEventId];
|
|
2544
|
+
delete system._snapshot._scheduledEvents[scheduledEventId];
|
|
2545
|
+
|
|
2546
|
+
// The send was already recorded at schedule time on the sender's
|
|
2547
|
+
// transition, so deliver without re-recording.
|
|
2548
|
+
deliver(source, target, event);
|
|
2549
|
+
}, delay);
|
|
2550
|
+
timerMap[scheduledEventId] = timeout;
|
|
2551
|
+
},
|
|
2552
|
+
cancel: (source, id) => {
|
|
2553
|
+
const scheduledEventId = createScheduledEventId(source, id);
|
|
2554
|
+
const timeout = timerMap[scheduledEventId];
|
|
2555
|
+
delete timerMap[scheduledEventId];
|
|
2556
|
+
delete system._snapshot._scheduledEvents[scheduledEventId];
|
|
2557
|
+
if (timeout !== undefined) {
|
|
2558
|
+
clock.clearTimeout(timeout);
|
|
2559
|
+
}
|
|
2560
|
+
},
|
|
2561
|
+
cancelAll: actor => {
|
|
2562
|
+
for (const scheduledEventId in system._snapshot._scheduledEvents) {
|
|
2563
|
+
const scheduledEvent = system._snapshot._scheduledEvents[scheduledEventId];
|
|
2564
|
+
if (scheduledEvent.source === actor) {
|
|
2565
|
+
scheduler.cancel(actor, scheduledEvent.id);
|
|
3340
2566
|
}
|
|
3341
|
-
effects.push({
|
|
3342
|
-
type: 'effect',
|
|
3343
|
-
exec: keyOrExec
|
|
3344
|
-
});
|
|
3345
2567
|
}
|
|
3346
|
-
};
|
|
3347
|
-
const patch = config.run({
|
|
3348
|
-
context: snapshot.context,
|
|
3349
|
-
event,
|
|
3350
|
-
input: snapshot.input,
|
|
3351
|
-
system: actorScope.system,
|
|
3352
|
-
self: actorScope.self,
|
|
3353
|
-
emit: actorScope.emit
|
|
3354
|
-
}, enq);
|
|
3355
|
-
const nextSnapshot = {
|
|
3356
|
-
...snapshot,
|
|
3357
|
-
...(patch || {})
|
|
3358
|
-
};
|
|
3359
|
-
if ('context' in snapshot || patch?.context !== undefined) {
|
|
3360
|
-
nextSnapshot.context = patch?.context ?? snapshot.context;
|
|
3361
2568
|
}
|
|
3362
|
-
|
|
3363
|
-
|
|
3364
|
-
|
|
3365
|
-
|
|
3366
|
-
|
|
3367
|
-
|
|
2569
|
+
};
|
|
2570
|
+
// Delivers an event to the target actor. Used by both `_relay` (which also
|
|
2571
|
+
// records the send) and the scheduler's timer (which already recorded it).
|
|
2572
|
+
const deliver = (source, target, event) => {
|
|
2573
|
+
const targetMachine = target.logic;
|
|
2574
|
+
const isInternalEvent = typeof targetMachine?.isInternalEventType === 'function' && targetMachine.isInternalEventType(event.type);
|
|
2575
|
+
if (isInternalEvent && source !== target) {
|
|
2576
|
+
throw new Error(`Internal event "${event.type}" cannot be sent to actor "${target.id}" from outside.`);
|
|
3368
2577
|
}
|
|
3369
|
-
|
|
2578
|
+
|
|
2579
|
+
// remember the last source for unified transition inspect event
|
|
2580
|
+
target._lastSourceRef = source;
|
|
2581
|
+
target._send(event);
|
|
3370
2582
|
};
|
|
3371
|
-
const
|
|
3372
|
-
|
|
3373
|
-
|
|
3374
|
-
|
|
3375
|
-
|
|
3376
|
-
|
|
3377
|
-
|
|
3378
|
-
|
|
2583
|
+
const sendInspectionEvent = event => {
|
|
2584
|
+
if (!inspectionObservers.size) {
|
|
2585
|
+
return;
|
|
2586
|
+
}
|
|
2587
|
+
const resolvedInspectionEvent = {
|
|
2588
|
+
...event,
|
|
2589
|
+
rootId: rootActor.sessionId
|
|
2590
|
+
};
|
|
2591
|
+
inspectionObservers.forEach(observer => observer.next?.(resolvedInspectionEvent));
|
|
2592
|
+
};
|
|
2593
|
+
const system = {
|
|
2594
|
+
children,
|
|
2595
|
+
reverseKeyedActors,
|
|
2596
|
+
keyedActors,
|
|
2597
|
+
_snapshot: {
|
|
2598
|
+
_scheduledEvents: (options?.snapshot && options.snapshot.scheduler) ?? {}
|
|
3379
2599
|
},
|
|
3380
|
-
|
|
3381
|
-
|
|
3382
|
-
|
|
3383
|
-
|
|
3384
|
-
|
|
3385
|
-
|
|
3386
|
-
|
|
3387
|
-
|
|
3388
|
-
if (
|
|
3389
|
-
|
|
2600
|
+
_bookId: () => `x:${idCounter++}`,
|
|
2601
|
+
_register: (sessionId, actor) => {
|
|
2602
|
+
children.set(sessionId, actor);
|
|
2603
|
+
return sessionId;
|
|
2604
|
+
},
|
|
2605
|
+
_unregister: actor => {
|
|
2606
|
+
children.delete(actor.sessionId);
|
|
2607
|
+
const registryKey = reverseKeyedActors.get(actor);
|
|
2608
|
+
if (registryKey !== undefined) {
|
|
2609
|
+
keyedActors.delete(registryKey);
|
|
2610
|
+
reverseKeyedActors.delete(actor);
|
|
3390
2611
|
}
|
|
2612
|
+
},
|
|
2613
|
+
get: registryKey => {
|
|
2614
|
+
return keyedActors.get(registryKey);
|
|
2615
|
+
},
|
|
2616
|
+
getAll: () => {
|
|
2617
|
+
return Object.fromEntries(keyedActors.entries());
|
|
2618
|
+
},
|
|
2619
|
+
_set: (registryKey, actor) => {
|
|
2620
|
+
const existing = keyedActors.get(registryKey);
|
|
2621
|
+
if (existing && existing !== actor) {
|
|
2622
|
+
throw new Error(`Actor with registry key '${registryKey}' already exists.`);
|
|
2623
|
+
}
|
|
2624
|
+
keyedActors.set(registryKey, actor);
|
|
2625
|
+
reverseKeyedActors.set(actor, registryKey);
|
|
2626
|
+
},
|
|
2627
|
+
inspect: observerOrFn => {
|
|
2628
|
+
const observer = toObserver(observerOrFn);
|
|
2629
|
+
inspectionObservers.add(observer);
|
|
3391
2630
|
return {
|
|
3392
|
-
|
|
2631
|
+
unsubscribe() {
|
|
2632
|
+
inspectionObservers.delete(observer);
|
|
2633
|
+
}
|
|
3393
2634
|
};
|
|
3394
2635
|
},
|
|
3395
|
-
|
|
3396
|
-
|
|
2636
|
+
_sendInspectionEvent: sendInspectionEvent,
|
|
2637
|
+
_relay: (source, target, event) => {
|
|
2638
|
+
recordSent(source, target, event);
|
|
2639
|
+
deliver(source, target, event);
|
|
2640
|
+
},
|
|
2641
|
+
scheduler,
|
|
2642
|
+
getSnapshot: () => {
|
|
2643
|
+
return {
|
|
2644
|
+
_scheduledEvents: {
|
|
2645
|
+
...system._snapshot._scheduledEvents
|
|
2646
|
+
}
|
|
2647
|
+
};
|
|
2648
|
+
},
|
|
2649
|
+
start: () => {
|
|
2650
|
+
const scheduledEvents = system._snapshot._scheduledEvents;
|
|
2651
|
+
system._snapshot._scheduledEvents = {};
|
|
2652
|
+
for (const scheduledId in scheduledEvents) {
|
|
2653
|
+
const {
|
|
2654
|
+
source,
|
|
2655
|
+
target,
|
|
2656
|
+
event,
|
|
2657
|
+
delay,
|
|
2658
|
+
id
|
|
2659
|
+
} = scheduledEvents[scheduledId];
|
|
2660
|
+
scheduler.schedule(source, target, event, delay, id);
|
|
2661
|
+
}
|
|
2662
|
+
},
|
|
2663
|
+
_clock: clock,
|
|
2664
|
+
_logger: logger,
|
|
2665
|
+
_timerStrategy: options.timers
|
|
3397
2666
|
};
|
|
3398
|
-
return
|
|
2667
|
+
return system;
|
|
3399
2668
|
}
|
|
3400
2669
|
|
|
3401
2670
|
// those are needed to make JSDoc `@link` work properly
|
|
@@ -3421,6 +2690,30 @@ const defaultOptions = {
|
|
|
3421
2690
|
},
|
|
3422
2691
|
logger: console.log.bind(console)
|
|
3423
2692
|
};
|
|
2693
|
+
function safeCall(fn, arg) {
|
|
2694
|
+
try {
|
|
2695
|
+
fn?.(arg);
|
|
2696
|
+
} catch (err) {
|
|
2697
|
+
reportUnhandledError(err);
|
|
2698
|
+
}
|
|
2699
|
+
}
|
|
2700
|
+
function isExecutableActionObject(effect) {
|
|
2701
|
+
return typeof effect === 'object' && effect !== null && 'args' in effect && 'exec' in effect;
|
|
2702
|
+
}
|
|
2703
|
+
function executeExecutableEffects(effects, actorScope) {
|
|
2704
|
+
if (!effects?.length) {
|
|
2705
|
+
return [];
|
|
2706
|
+
}
|
|
2707
|
+
const logicEffects = [];
|
|
2708
|
+
for (const effect of effects) {
|
|
2709
|
+
if (isExecutableActionObject(effect)) {
|
|
2710
|
+
actorScope.actionExecutor(effect);
|
|
2711
|
+
} else {
|
|
2712
|
+
logicEffects.push(effect);
|
|
2713
|
+
}
|
|
2714
|
+
}
|
|
2715
|
+
return logicEffects;
|
|
2716
|
+
}
|
|
3424
2717
|
|
|
3425
2718
|
/**
|
|
3426
2719
|
* An Actor is a running process that can receive events, send events and change
|
|
@@ -3459,6 +2752,7 @@ class Actor {
|
|
|
3459
2752
|
this.logger = void 0;
|
|
3460
2753
|
/** @internal */
|
|
3461
2754
|
this._processingStatus = ProcessingStatus.NotStarted;
|
|
2755
|
+
this._forceDeferredActions = false;
|
|
3462
2756
|
// Actor Ref
|
|
3463
2757
|
this._parent = void 0;
|
|
3464
2758
|
/** @internal */
|
|
@@ -3474,7 +2768,8 @@ class Actor {
|
|
|
3474
2768
|
this._collectedActions = [];
|
|
3475
2769
|
/** @internal Events relayed to other actors during the in-flight transition. */
|
|
3476
2770
|
this._collectedSent = [];
|
|
3477
|
-
this.
|
|
2771
|
+
this._initialEffects = void 0;
|
|
2772
|
+
this.registryKey = void 0;
|
|
3478
2773
|
/** The globally unique process ID for this invocation. */
|
|
3479
2774
|
this.sessionId = void 0;
|
|
3480
2775
|
/** The system to which this actor belongs. */
|
|
@@ -3495,14 +2790,17 @@ class Actor {
|
|
|
3495
2790
|
parent,
|
|
3496
2791
|
syncSnapshot,
|
|
3497
2792
|
id,
|
|
3498
|
-
|
|
2793
|
+
registryKey,
|
|
3499
2794
|
inspect
|
|
3500
2795
|
} = resolvedOptions;
|
|
3501
|
-
this.system = parent ? parent.system :
|
|
2796
|
+
this.system = parent ? parent.system : resolvedOptions._systemRef?.current ?? createRuntimeSystem(this, {
|
|
3502
2797
|
clock,
|
|
3503
2798
|
logger,
|
|
3504
2799
|
timers: resolvedOptions.timers
|
|
3505
2800
|
});
|
|
2801
|
+
if (!parent && resolvedOptions._systemRef && !resolvedOptions._systemRef.current) {
|
|
2802
|
+
resolvedOptions._systemRef.current = this.system;
|
|
2803
|
+
}
|
|
3506
2804
|
if (inspect && !parent) {
|
|
3507
2805
|
// Always inspect at the system-level
|
|
3508
2806
|
this.system.inspect(toObserver(inspect));
|
|
@@ -3527,35 +2825,18 @@ class Actor {
|
|
|
3527
2825
|
system: this.system,
|
|
3528
2826
|
stopChild: child => {
|
|
3529
2827
|
if (child._parent !== this) {
|
|
3530
|
-
throw new Error(`Cannot stop child actor ${child.id} of ${this.id} because it is not a child`);
|
|
2828
|
+
throw new Error(`Cannot stop child actor ${child.id} of ${this.id} because it is not a child` );
|
|
3531
2829
|
}
|
|
3532
2830
|
child._stop();
|
|
3533
2831
|
},
|
|
3534
2832
|
emit: emittedEvent => {
|
|
3535
|
-
const listeners
|
|
3536
|
-
|
|
3537
|
-
|
|
3538
|
-
|
|
3539
|
-
}
|
|
3540
|
-
if (listeners) {
|
|
3541
|
-
for (const handler of listeners) {
|
|
3542
|
-
try {
|
|
3543
|
-
handler(emittedEvent);
|
|
3544
|
-
} catch (err) {
|
|
3545
|
-
reportUnhandledError(err);
|
|
2833
|
+
for (const listeners of [this.eventListeners.get(emittedEvent.type), this.eventListeners.get('*')]) {
|
|
2834
|
+
if (listeners) {
|
|
2835
|
+
for (const handler of listeners) {
|
|
2836
|
+
safeCall(handler, emittedEvent);
|
|
3546
2837
|
}
|
|
3547
2838
|
}
|
|
3548
2839
|
}
|
|
3549
|
-
if (!wildcardListener) {
|
|
3550
|
-
return;
|
|
3551
|
-
}
|
|
3552
|
-
for (const handler of wildcardListener) {
|
|
3553
|
-
try {
|
|
3554
|
-
handler(emittedEvent);
|
|
3555
|
-
} catch (err) {
|
|
3556
|
-
reportUnhandledError(err);
|
|
3557
|
-
}
|
|
3558
|
-
}
|
|
3559
2840
|
},
|
|
3560
2841
|
actionExecutor: action => {
|
|
3561
2842
|
const exec = () => {
|
|
@@ -3576,7 +2857,7 @@ class Actor {
|
|
|
3576
2857
|
executingCustomAction = saveExecutingCustomAction;
|
|
3577
2858
|
}
|
|
3578
2859
|
};
|
|
3579
|
-
if (this._processingStatus === ProcessingStatus.Running) {
|
|
2860
|
+
if (this._processingStatus === ProcessingStatus.Running && !this._forceDeferredActions) {
|
|
3580
2861
|
exec();
|
|
3581
2862
|
} else {
|
|
3582
2863
|
this._deferred.push(exec);
|
|
@@ -3597,15 +2878,12 @@ class Actor {
|
|
|
3597
2878
|
};
|
|
3598
2879
|
}
|
|
3599
2880
|
});
|
|
3600
|
-
|
|
3601
|
-
|
|
3602
|
-
|
|
3603
|
-
if (systemId) {
|
|
3604
|
-
this.systemId = systemId;
|
|
3605
|
-
this.system._set(systemId, this);
|
|
2881
|
+
if (registryKey) {
|
|
2882
|
+
this.registryKey = registryKey;
|
|
2883
|
+
this.system._set(registryKey, this);
|
|
3606
2884
|
}
|
|
3607
2885
|
|
|
3608
|
-
// prepare to collect initial microsteps during
|
|
2886
|
+
// prepare to collect initial microsteps during initialTransition
|
|
3609
2887
|
this._collectedMicrosteps = [];
|
|
3610
2888
|
let persistedState = options?.snapshot ?? options?.state;
|
|
3611
2889
|
if (persistedState && typeof persistedState === 'object' && '_pendingEffects' in persistedState) {
|
|
@@ -3619,7 +2897,17 @@ class Actor {
|
|
|
3619
2897
|
persistedState = rest;
|
|
3620
2898
|
}
|
|
3621
2899
|
try {
|
|
3622
|
-
|
|
2900
|
+
if (persistedState) {
|
|
2901
|
+
this._snapshot = this.logic.restoreSnapshot ? this.logic.restoreSnapshot(persistedState, this._actorScope) : persistedState;
|
|
2902
|
+
} else if (options?._inert) {
|
|
2903
|
+
// Inert actors (createInertActorScope) only anchor a scope for pure
|
|
2904
|
+
// transition functions; computing an initial snapshot here would run
|
|
2905
|
+
// init-time side effects (context factories, entry) a second time.
|
|
2906
|
+
} else {
|
|
2907
|
+
const [snapshot, effects] = this.logic.initialTransition(this.options?.input, this._actorScope);
|
|
2908
|
+
this._snapshot = snapshot;
|
|
2909
|
+
this._initialEffects = effects;
|
|
2910
|
+
}
|
|
3623
2911
|
} catch (err) {
|
|
3624
2912
|
// if we get here then it means that we assign a value to this._snapshot that is not of the correct type
|
|
3625
2913
|
// we can't get the true `TSnapshot & { status: 'error'; }`, it's impossible
|
|
@@ -3633,7 +2921,7 @@ class Actor {
|
|
|
3633
2921
|
// computation so they can't run against an inconsistent actor
|
|
3634
2922
|
this._deferred.length = 0;
|
|
3635
2923
|
}
|
|
3636
|
-
if (
|
|
2924
|
+
if (registryKey && this._snapshot.status !== 'active') {
|
|
3637
2925
|
this.system._unregister(this);
|
|
3638
2926
|
}
|
|
3639
2927
|
|
|
@@ -3656,13 +2944,35 @@ class Actor {
|
|
|
3656
2944
|
error: err
|
|
3657
2945
|
};
|
|
3658
2946
|
}
|
|
2947
|
+
|
|
2948
|
+
/** Recover via the logic's error event if possible; otherwise error out. */
|
|
2949
|
+
_recoverOrError(err, snapshot) {
|
|
2950
|
+
if (!this._tryHandleExecutionError(err, snapshot)) {
|
|
2951
|
+
this._setErrorSnapshot(err);
|
|
2952
|
+
this._error(err);
|
|
2953
|
+
}
|
|
2954
|
+
}
|
|
2955
|
+
_tryHandleExecutionError(err, snapshot = this._snapshot) {
|
|
2956
|
+
// Machine logic can recover from execution errors via `onError`
|
|
2957
|
+
// transitions; the logic decides (so the actor stays logic-agnostic).
|
|
2958
|
+
const errorEvent = this.logic.getExecutionErrorEvent?.(snapshot, err);
|
|
2959
|
+
if (!errorEvent) {
|
|
2960
|
+
return false;
|
|
2961
|
+
}
|
|
2962
|
+
try {
|
|
2963
|
+
const [nextSnapshot, effects] = this.logic.transition(snapshot, errorEvent, this._actorScope);
|
|
2964
|
+
this._snapshot = nextSnapshot;
|
|
2965
|
+
const logicEffects = executeExecutableEffects(effects, this._actorScope);
|
|
2966
|
+
this.update(nextSnapshot, errorEvent);
|
|
2967
|
+
this.logic.executeEffects?.(logicEffects, this._actorScope);
|
|
2968
|
+
return true;
|
|
2969
|
+
} catch {
|
|
2970
|
+
return false;
|
|
2971
|
+
}
|
|
2972
|
+
}
|
|
3659
2973
|
_next(snapshot) {
|
|
3660
2974
|
for (const observer of this.observers) {
|
|
3661
|
-
|
|
3662
|
-
observer.next?.(snapshot);
|
|
3663
|
-
} catch (err) {
|
|
3664
|
-
reportUnhandledError(err);
|
|
3665
|
-
}
|
|
2975
|
+
safeCall(observer.next, snapshot);
|
|
3666
2976
|
}
|
|
3667
2977
|
}
|
|
3668
2978
|
update(snapshot, event) {
|
|
@@ -3681,6 +2991,9 @@ class Actor {
|
|
|
3681
2991
|
// no "builtin deferred" should actually throw an error since they are either safe
|
|
3682
2992
|
// or the control flow is passed through the mailbox and errors should be caught by the `_process` used by the mailbox
|
|
3683
2993
|
this._deferred.length = 0;
|
|
2994
|
+
if (this._tryHandleExecutionError(err, snapshot)) {
|
|
2995
|
+
return;
|
|
2996
|
+
}
|
|
3684
2997
|
this._setErrorSnapshot(err, snapshot);
|
|
3685
2998
|
break;
|
|
3686
2999
|
}
|
|
@@ -3728,6 +3041,25 @@ class Actor {
|
|
|
3728
3041
|
this._collectedActions = [];
|
|
3729
3042
|
this._collectedSent = [];
|
|
3730
3043
|
}
|
|
3044
|
+
_flushInitialEffects() {
|
|
3045
|
+
if (!this._initialEffects) {
|
|
3046
|
+
return true;
|
|
3047
|
+
}
|
|
3048
|
+
this._forceDeferredActions = true;
|
|
3049
|
+
try {
|
|
3050
|
+
const logicEffects = executeExecutableEffects(this._initialEffects, this._actorScope);
|
|
3051
|
+
this.logic.executeEffects?.(logicEffects, this._actorScope);
|
|
3052
|
+
this._initialEffects = undefined;
|
|
3053
|
+
return true;
|
|
3054
|
+
} catch (err) {
|
|
3055
|
+
this._initialEffects = undefined;
|
|
3056
|
+
this._deferred.length = 0;
|
|
3057
|
+
this._recoverOrError(err);
|
|
3058
|
+
return this._snapshot.status === 'active';
|
|
3059
|
+
} finally {
|
|
3060
|
+
this._forceDeferredActions = false;
|
|
3061
|
+
}
|
|
3062
|
+
}
|
|
3731
3063
|
|
|
3732
3064
|
/**
|
|
3733
3065
|
* Subscribe an observer to an actor’s snapshot values.
|
|
@@ -3796,11 +3128,7 @@ class Actor {
|
|
|
3796
3128
|
} else {
|
|
3797
3129
|
switch (this._snapshot.status) {
|
|
3798
3130
|
case 'done':
|
|
3799
|
-
|
|
3800
|
-
observer.complete?.();
|
|
3801
|
-
} catch (err) {
|
|
3802
|
-
reportUnhandledError(err);
|
|
3803
|
-
}
|
|
3131
|
+
safeCall(observer.complete);
|
|
3804
3132
|
break;
|
|
3805
3133
|
case 'error':
|
|
3806
3134
|
{
|
|
@@ -3808,11 +3136,7 @@ class Actor {
|
|
|
3808
3136
|
if (!observer.error) {
|
|
3809
3137
|
reportUnhandledError(err);
|
|
3810
3138
|
} else {
|
|
3811
|
-
|
|
3812
|
-
observer.error(err);
|
|
3813
|
-
} catch (err) {
|
|
3814
|
-
reportUnhandledError(err);
|
|
3815
|
-
}
|
|
3139
|
+
safeCall(observer.error, err);
|
|
3816
3140
|
}
|
|
3817
3141
|
break;
|
|
3818
3142
|
}
|
|
@@ -3854,7 +3178,7 @@ class Actor {
|
|
|
3854
3178
|
complete: observer.complete
|
|
3855
3179
|
});
|
|
3856
3180
|
},
|
|
3857
|
-
get: () => selector(this.
|
|
3181
|
+
get: () => selector(this.getSnapshot())
|
|
3858
3182
|
};
|
|
3859
3183
|
}
|
|
3860
3184
|
|
|
@@ -3864,6 +3188,9 @@ class Actor {
|
|
|
3864
3188
|
// Do not restart the service if it is already started
|
|
3865
3189
|
return this;
|
|
3866
3190
|
}
|
|
3191
|
+
if (this._processingStatus === ProcessingStatus.Stopped) {
|
|
3192
|
+
return this;
|
|
3193
|
+
}
|
|
3867
3194
|
if (this._syncSnapshot) {
|
|
3868
3195
|
this.subscribe({
|
|
3869
3196
|
next: snapshot => {
|
|
@@ -3878,8 +3205,8 @@ class Actor {
|
|
|
3878
3205
|
});
|
|
3879
3206
|
}
|
|
3880
3207
|
this.system._register(this.sessionId, this);
|
|
3881
|
-
if (this.
|
|
3882
|
-
this.system._set(this.
|
|
3208
|
+
if (this.registryKey) {
|
|
3209
|
+
this.system._set(this.registryKey, this);
|
|
3883
3210
|
}
|
|
3884
3211
|
this._processingStatus = ProcessingStatus.Running;
|
|
3885
3212
|
|
|
@@ -3892,6 +3219,9 @@ class Actor {
|
|
|
3892
3219
|
case 'done':
|
|
3893
3220
|
// a state machine can be "done" upon initialization (it could reach a final state using initial microsteps)
|
|
3894
3221
|
// we still need to complete observers, flush deferreds etc
|
|
3222
|
+
if (!this._flushInitialEffects()) {
|
|
3223
|
+
return this;
|
|
3224
|
+
}
|
|
3895
3225
|
this.update(this._snapshot, initEvent);
|
|
3896
3226
|
// TODO: rethink cleanup of observers, mailbox, etc
|
|
3897
3227
|
return this;
|
|
@@ -3911,6 +3241,9 @@ class Actor {
|
|
|
3911
3241
|
return this;
|
|
3912
3242
|
}
|
|
3913
3243
|
}
|
|
3244
|
+
if (!this._flushInitialEffects()) {
|
|
3245
|
+
return this;
|
|
3246
|
+
}
|
|
3914
3247
|
|
|
3915
3248
|
// TODO: this notifies all subscribers but usually this is redundant
|
|
3916
3249
|
// there is no real change happening here
|
|
@@ -3942,16 +3275,21 @@ class Actor {
|
|
|
3942
3275
|
};
|
|
3943
3276
|
}
|
|
3944
3277
|
if (caughtError) {
|
|
3945
|
-
|
|
3946
|
-
|
|
3947
|
-
|
|
3948
|
-
|
|
3949
|
-
|
|
3278
|
+
this._recoverOrError(caughtError.err);
|
|
3279
|
+
return;
|
|
3280
|
+
}
|
|
3281
|
+
let snapshot = this._snapshot;
|
|
3282
|
+
try {
|
|
3283
|
+
const [nextSnapshot, effects] = nextState;
|
|
3284
|
+
snapshot = nextSnapshot;
|
|
3285
|
+
this._snapshot = snapshot;
|
|
3286
|
+
const logicEffects = executeExecutableEffects(effects, this._actorScope);
|
|
3287
|
+
this.update(snapshot, event);
|
|
3288
|
+
this.logic.executeEffects?.(logicEffects, this._actorScope);
|
|
3289
|
+
} catch (err) {
|
|
3290
|
+
this._recoverOrError(err, snapshot);
|
|
3950
3291
|
return;
|
|
3951
3292
|
}
|
|
3952
|
-
const [snapshot, effects] = Array.isArray(nextState) ? nextState : [nextState, undefined];
|
|
3953
|
-
this.update(snapshot, event);
|
|
3954
|
-
executeLogicEffects(effects, this._actorScope);
|
|
3955
3293
|
if (event.type === XSTATE_STOP) {
|
|
3956
3294
|
this._stopProcedure();
|
|
3957
3295
|
this._complete();
|
|
@@ -3984,11 +3322,7 @@ class Actor {
|
|
|
3984
3322
|
}
|
|
3985
3323
|
_complete() {
|
|
3986
3324
|
for (const observer of this.observers) {
|
|
3987
|
-
|
|
3988
|
-
observer.complete?.();
|
|
3989
|
-
} catch (err) {
|
|
3990
|
-
reportUnhandledError(err);
|
|
3991
|
-
}
|
|
3325
|
+
safeCall(observer.complete);
|
|
3992
3326
|
}
|
|
3993
3327
|
this.observers.clear();
|
|
3994
3328
|
this.eventListeners.clear();
|
|
@@ -3999,24 +3333,19 @@ class Actor {
|
|
|
3999
3333
|
if (!this._parent) {
|
|
4000
3334
|
reportUnhandledError(err);
|
|
4001
3335
|
}
|
|
4002
|
-
this.eventListeners.clear();
|
|
4003
3336
|
} else {
|
|
4004
3337
|
let reportError = false;
|
|
4005
3338
|
for (const observer of this.observers) {
|
|
4006
3339
|
const errorListener = observer.error;
|
|
4007
3340
|
reportError ||= !errorListener;
|
|
4008
|
-
|
|
4009
|
-
errorListener?.(err);
|
|
4010
|
-
} catch (err2) {
|
|
4011
|
-
reportUnhandledError(err2);
|
|
4012
|
-
}
|
|
3341
|
+
safeCall(errorListener, err);
|
|
4013
3342
|
}
|
|
4014
3343
|
this.observers.clear();
|
|
4015
|
-
this.eventListeners.clear();
|
|
4016
3344
|
if (reportError) {
|
|
4017
3345
|
reportUnhandledError(err);
|
|
4018
3346
|
}
|
|
4019
3347
|
}
|
|
3348
|
+
this.eventListeners.clear();
|
|
4020
3349
|
if (this._parent) {
|
|
4021
3350
|
this.system._relay(this, this._parent, createErrorActorEvent(this.id, err));
|
|
4022
3351
|
}
|
|
@@ -4143,15 +3472,9 @@ class Actor {
|
|
|
4143
3472
|
* @see {@link Actor.getPersistedSnapshot} to persist the internal state of an actor (which is more than just a snapshot).
|
|
4144
3473
|
*/
|
|
4145
3474
|
getSnapshot() {
|
|
4146
|
-
return this.get();
|
|
4147
|
-
}
|
|
4148
|
-
|
|
4149
|
-
/** Read the actor's current snapshot as an atom value. */
|
|
4150
|
-
get() {
|
|
4151
3475
|
if (!this._snapshot) {
|
|
4152
3476
|
throw new Error(`Snapshot can't be read while the actor initializes itself`);
|
|
4153
3477
|
}
|
|
4154
|
-
onActorRead?.(this);
|
|
4155
3478
|
return this._snapshot;
|
|
4156
3479
|
}
|
|
4157
3480
|
}
|
|
@@ -4212,6 +3535,231 @@ function createActor(logic, options) {
|
|
|
4212
3535
|
return new Actor(logic, options);
|
|
4213
3536
|
}
|
|
4214
3537
|
|
|
3538
|
+
const effectStates = new WeakMap();
|
|
3539
|
+
const XSTATE_LOGIC_EFFECT_RESOLVE = 'xstate.logic.effect.resolve';
|
|
3540
|
+
const XSTATE_LOGIC_EFFECT_REJECT = 'xstate.logic.effect.reject';
|
|
3541
|
+
const XSTATE_LOGIC_EFFECT_START = 'xstate.logic.effect.start';
|
|
3542
|
+
function getEffectState(self) {
|
|
3543
|
+
let state = effectStates.get(self);
|
|
3544
|
+
if (!state) {
|
|
3545
|
+
state = new Map();
|
|
3546
|
+
effectStates.set(self, state);
|
|
3547
|
+
}
|
|
3548
|
+
return state;
|
|
3549
|
+
}
|
|
3550
|
+
function executeLogicEffects(effects, actorScope) {
|
|
3551
|
+
if (!effects?.length) {
|
|
3552
|
+
return;
|
|
3553
|
+
}
|
|
3554
|
+
for (const effect of effects) {
|
|
3555
|
+
switch (effect.type) {
|
|
3556
|
+
case 'emit':
|
|
3557
|
+
actorScope.emit(effect.event);
|
|
3558
|
+
break;
|
|
3559
|
+
case 'sendBack':
|
|
3560
|
+
{
|
|
3561
|
+
const parent = actorScope.self._parent;
|
|
3562
|
+
if (parent) {
|
|
3563
|
+
actorScope.system._relay(actorScope.self, parent, effect.event);
|
|
3564
|
+
}
|
|
3565
|
+
break;
|
|
3566
|
+
}
|
|
3567
|
+
case 'raise':
|
|
3568
|
+
actorScope.system._relay(actorScope.self, actorScope.self, effect.event);
|
|
3569
|
+
break;
|
|
3570
|
+
case 'effect':
|
|
3571
|
+
{
|
|
3572
|
+
if (!effect.key) {
|
|
3573
|
+
const cleanup = effect.exec();
|
|
3574
|
+
if (typeof cleanup === 'function') {
|
|
3575
|
+
getEffectState(actorScope.self).set(Symbol(), {
|
|
3576
|
+
cleanup
|
|
3577
|
+
});
|
|
3578
|
+
}
|
|
3579
|
+
break;
|
|
3580
|
+
}
|
|
3581
|
+
const state = getEffectState(actorScope.self);
|
|
3582
|
+
if (state.has(effect.key)) {
|
|
3583
|
+
break;
|
|
3584
|
+
}
|
|
3585
|
+
const cleanup = effect.exec();
|
|
3586
|
+
state.set(effect.key, {
|
|
3587
|
+
cleanup: typeof cleanup === 'function' ? cleanup : undefined
|
|
3588
|
+
});
|
|
3589
|
+
break;
|
|
3590
|
+
}
|
|
3591
|
+
case 'cleanupEffects':
|
|
3592
|
+
{
|
|
3593
|
+
const state = effectStates.get(actorScope.self);
|
|
3594
|
+
if (!state) {
|
|
3595
|
+
break;
|
|
3596
|
+
}
|
|
3597
|
+
for (const {
|
|
3598
|
+
cleanup
|
|
3599
|
+
} of state.values()) {
|
|
3600
|
+
cleanup?.();
|
|
3601
|
+
}
|
|
3602
|
+
effectStates.delete(actorScope.self);
|
|
3603
|
+
break;
|
|
3604
|
+
}
|
|
3605
|
+
}
|
|
3606
|
+
}
|
|
3607
|
+
}
|
|
3608
|
+
function resolveContext(context, input) {
|
|
3609
|
+
return typeof context === 'function' ? context({
|
|
3610
|
+
input
|
|
3611
|
+
}) : context;
|
|
3612
|
+
}
|
|
3613
|
+
function createLogic(config) {
|
|
3614
|
+
const transition = (snapshot, event, actorScope) => {
|
|
3615
|
+
if (snapshot.status !== 'active') {
|
|
3616
|
+
return [snapshot, []];
|
|
3617
|
+
}
|
|
3618
|
+
if (event.type === XSTATE_STOP) {
|
|
3619
|
+
return [{
|
|
3620
|
+
...snapshot,
|
|
3621
|
+
status: 'stopped',
|
|
3622
|
+
input: undefined
|
|
3623
|
+
}, [{
|
|
3624
|
+
type: 'cleanupEffects'
|
|
3625
|
+
}]];
|
|
3626
|
+
}
|
|
3627
|
+
if (event.type === XSTATE_LOGIC_EFFECT_START) {
|
|
3628
|
+
return [{
|
|
3629
|
+
...snapshot,
|
|
3630
|
+
effects: {
|
|
3631
|
+
...snapshot.effects,
|
|
3632
|
+
[event.key]: {
|
|
3633
|
+
status: 'active'
|
|
3634
|
+
}
|
|
3635
|
+
}
|
|
3636
|
+
}, []];
|
|
3637
|
+
}
|
|
3638
|
+
if (event.type === XSTATE_LOGIC_EFFECT_RESOLVE) {
|
|
3639
|
+
return [{
|
|
3640
|
+
...snapshot,
|
|
3641
|
+
effects: {
|
|
3642
|
+
...snapshot.effects,
|
|
3643
|
+
[event.key]: {
|
|
3644
|
+
status: 'done',
|
|
3645
|
+
output: event.output
|
|
3646
|
+
}
|
|
3647
|
+
}
|
|
3648
|
+
}, []];
|
|
3649
|
+
}
|
|
3650
|
+
if (event.type === XSTATE_LOGIC_EFFECT_REJECT) {
|
|
3651
|
+
return [{
|
|
3652
|
+
...snapshot,
|
|
3653
|
+
effects: {
|
|
3654
|
+
...snapshot.effects,
|
|
3655
|
+
[event.key]: {
|
|
3656
|
+
status: 'error',
|
|
3657
|
+
error: event.error
|
|
3658
|
+
}
|
|
3659
|
+
}
|
|
3660
|
+
}, []];
|
|
3661
|
+
}
|
|
3662
|
+
const effects = [];
|
|
3663
|
+
const trackedEffects = {};
|
|
3664
|
+
const enqueueEffect = (key, exec) => {
|
|
3665
|
+
if (snapshot.effects?.[key]) {
|
|
3666
|
+
return;
|
|
3667
|
+
}
|
|
3668
|
+
effects.push({
|
|
3669
|
+
type: 'effect',
|
|
3670
|
+
key,
|
|
3671
|
+
exec
|
|
3672
|
+
});
|
|
3673
|
+
trackedEffects[key] = {
|
|
3674
|
+
status: 'active'
|
|
3675
|
+
};
|
|
3676
|
+
};
|
|
3677
|
+
const enq = {
|
|
3678
|
+
emit: emitted => {
|
|
3679
|
+
effects.push({
|
|
3680
|
+
type: 'emit',
|
|
3681
|
+
event: emitted
|
|
3682
|
+
});
|
|
3683
|
+
},
|
|
3684
|
+
sendBack: sentEvent => {
|
|
3685
|
+
effects.push({
|
|
3686
|
+
type: 'sendBack',
|
|
3687
|
+
event: sentEvent
|
|
3688
|
+
});
|
|
3689
|
+
},
|
|
3690
|
+
raise: raisedEvent => {
|
|
3691
|
+
effects.push({
|
|
3692
|
+
type: 'raise',
|
|
3693
|
+
event: raisedEvent
|
|
3694
|
+
});
|
|
3695
|
+
},
|
|
3696
|
+
effect: (keyOrExec, maybeExec) => {
|
|
3697
|
+
if (typeof keyOrExec === 'string') {
|
|
3698
|
+
enqueueEffect(keyOrExec, maybeExec);
|
|
3699
|
+
return;
|
|
3700
|
+
}
|
|
3701
|
+
effects.push({
|
|
3702
|
+
type: 'effect',
|
|
3703
|
+
exec: keyOrExec
|
|
3704
|
+
});
|
|
3705
|
+
}
|
|
3706
|
+
};
|
|
3707
|
+
const patch = config.run({
|
|
3708
|
+
context: snapshot.context,
|
|
3709
|
+
event,
|
|
3710
|
+
input: snapshot.input,
|
|
3711
|
+
system: actorScope.system,
|
|
3712
|
+
self: actorScope.self,
|
|
3713
|
+
emit: actorScope.emit
|
|
3714
|
+
}, enq);
|
|
3715
|
+
const nextSnapshot = {
|
|
3716
|
+
...snapshot,
|
|
3717
|
+
...(patch || {})
|
|
3718
|
+
};
|
|
3719
|
+
if ('context' in snapshot || patch?.context !== undefined) {
|
|
3720
|
+
nextSnapshot.context = patch?.context ?? snapshot.context;
|
|
3721
|
+
}
|
|
3722
|
+
if (patch?.effects || Object.keys(trackedEffects).length) {
|
|
3723
|
+
nextSnapshot.effects = {
|
|
3724
|
+
...snapshot.effects,
|
|
3725
|
+
...patch?.effects,
|
|
3726
|
+
...trackedEffects
|
|
3727
|
+
};
|
|
3728
|
+
}
|
|
3729
|
+
return [nextSnapshot, effects];
|
|
3730
|
+
};
|
|
3731
|
+
const logic = {
|
|
3732
|
+
id: config.id,
|
|
3733
|
+
config,
|
|
3734
|
+
transition,
|
|
3735
|
+
start: (snapshot, actorScope) => {
|
|
3736
|
+
const [nextSnapshot, effects] = transition(snapshot, createInitEvent(snapshot.input), actorScope);
|
|
3737
|
+
Object.assign(snapshot, nextSnapshot);
|
|
3738
|
+
executeLogicEffects(effects, actorScope);
|
|
3739
|
+
},
|
|
3740
|
+
initialTransition: (input, _) => {
|
|
3741
|
+
const context = resolveContext(config.context, input);
|
|
3742
|
+
const snapshot = {
|
|
3743
|
+
status: 'active',
|
|
3744
|
+
output: undefined,
|
|
3745
|
+
error: undefined,
|
|
3746
|
+
input
|
|
3747
|
+
};
|
|
3748
|
+
if (context !== undefined) {
|
|
3749
|
+
snapshot.context = context;
|
|
3750
|
+
}
|
|
3751
|
+
return [{
|
|
3752
|
+
...snapshot
|
|
3753
|
+
}, []];
|
|
3754
|
+
},
|
|
3755
|
+
getInitialSnapshot: (actorScope, input) => logic.initialTransition(input, actorScope)[0],
|
|
3756
|
+
getPersistedSnapshot: snapshot => snapshot,
|
|
3757
|
+
restoreSnapshot: snapshot => snapshot,
|
|
3758
|
+
executeEffects: executeLogicEffects
|
|
3759
|
+
};
|
|
3760
|
+
return logic;
|
|
3761
|
+
}
|
|
3762
|
+
|
|
4215
3763
|
const instanceStates = /* #__PURE__ */new WeakMap();
|
|
4216
3764
|
|
|
4217
3765
|
/**
|
|
@@ -4590,46 +4138,45 @@ function createEventObservableLogic(lazyObservableOrConfig) {
|
|
|
4590
4138
|
config: lazyObservable,
|
|
4591
4139
|
transition: (state, event) => {
|
|
4592
4140
|
if (state.status !== 'active') {
|
|
4593
|
-
return state;
|
|
4141
|
+
return [state, []];
|
|
4594
4142
|
}
|
|
4595
4143
|
switch (event.type) {
|
|
4596
4144
|
case XSTATE_OBSERVABLE_ERROR:
|
|
4597
|
-
return {
|
|
4145
|
+
return [{
|
|
4598
4146
|
...state,
|
|
4599
4147
|
status: 'error',
|
|
4600
4148
|
error: event.data,
|
|
4601
4149
|
input: undefined,
|
|
4602
4150
|
_subscription: undefined
|
|
4603
|
-
};
|
|
4151
|
+
}, []];
|
|
4604
4152
|
case XSTATE_OBSERVABLE_COMPLETE:
|
|
4605
|
-
return {
|
|
4153
|
+
return [{
|
|
4606
4154
|
...state,
|
|
4607
4155
|
status: 'done',
|
|
4608
4156
|
input: undefined,
|
|
4609
4157
|
_subscription: undefined
|
|
4610
|
-
};
|
|
4158
|
+
}, []];
|
|
4611
4159
|
case XSTATE_STOP:
|
|
4612
4160
|
state._subscription.unsubscribe();
|
|
4613
|
-
return {
|
|
4161
|
+
return [{
|
|
4614
4162
|
...state,
|
|
4615
4163
|
status: 'stopped',
|
|
4616
4164
|
input: undefined,
|
|
4617
4165
|
_subscription: undefined
|
|
4618
|
-
};
|
|
4166
|
+
}, []];
|
|
4619
4167
|
default:
|
|
4620
|
-
return state;
|
|
4168
|
+
return [state, []];
|
|
4621
4169
|
}
|
|
4622
4170
|
},
|
|
4623
|
-
|
|
4624
|
-
|
|
4625
|
-
|
|
4626
|
-
|
|
4627
|
-
|
|
4628
|
-
|
|
4629
|
-
|
|
4630
|
-
|
|
4631
|
-
|
|
4632
|
-
},
|
|
4171
|
+
initialTransition: (input, _) => [{
|
|
4172
|
+
status: 'active',
|
|
4173
|
+
output: undefined,
|
|
4174
|
+
error: undefined,
|
|
4175
|
+
context: undefined,
|
|
4176
|
+
input,
|
|
4177
|
+
_subscription: undefined
|
|
4178
|
+
}, []],
|
|
4179
|
+
getInitialSnapshot: (actorScope, input) => logic.initialTransition(input, actorScope)[0],
|
|
4633
4180
|
start: (state, {
|
|
4634
4181
|
self,
|
|
4635
4182
|
system,
|
|
@@ -4954,14 +4501,15 @@ exports.ProcessingStatus = ProcessingStatus;
|
|
|
4954
4501
|
exports.STATE_DELIMITER = STATE_DELIMITER;
|
|
4955
4502
|
exports.TimeoutError = TimeoutError;
|
|
4956
4503
|
exports.XSTATE_INIT = XSTATE_INIT;
|
|
4504
|
+
exports.XSTATE_STOP = XSTATE_STOP;
|
|
4505
|
+
exports.builtInActions = builtInActions;
|
|
4957
4506
|
exports.checkStateIn = checkStateIn;
|
|
4507
|
+
exports.cloneMachineSnapshot = cloneMachineSnapshot;
|
|
4958
4508
|
exports.createActor = createActor;
|
|
4959
|
-
exports.createAsyncAtom = createAsyncAtom;
|
|
4960
4509
|
exports.createAsyncLogic = createAsyncLogic;
|
|
4961
|
-
exports.createAtom = createAtom;
|
|
4962
|
-
exports.createAtomConfig = createAtomConfig;
|
|
4963
4510
|
exports.createCallbackLogic = createCallbackLogic;
|
|
4964
4511
|
exports.createEmptyActor = createEmptyActor;
|
|
4512
|
+
exports.createErrorPlatformEvent = createErrorPlatformEvent;
|
|
4965
4513
|
exports.createEventObservableLogic = createEventObservableLogic;
|
|
4966
4514
|
exports.createInitEvent = createInitEvent;
|
|
4967
4515
|
exports.createInvokeId = createInvokeId;
|
|
@@ -4970,8 +4518,9 @@ exports.createListenerLogic = createListenerLogic;
|
|
|
4970
4518
|
exports.createLogic = createLogic;
|
|
4971
4519
|
exports.createMachineSnapshot = createMachineSnapshot;
|
|
4972
4520
|
exports.createObservableLogic = createObservableLogic;
|
|
4973
|
-
exports.createReducerAtom = createReducerAtom;
|
|
4974
4521
|
exports.createSubscriptionLogic = createSubscriptionLogic;
|
|
4522
|
+
exports.createTransitionEnqueue = createTransitionEnqueue;
|
|
4523
|
+
exports.deriveDeferredStarts = deriveDeferredStarts;
|
|
4975
4524
|
exports.evaluateCandidate = evaluateCandidate;
|
|
4976
4525
|
exports.formatRouteTransitions = formatRouteTransitions;
|
|
4977
4526
|
exports.formatTransition = formatTransition;
|
|
@@ -4983,9 +4532,11 @@ exports.getPersistedSnapshot = getPersistedSnapshot;
|
|
|
4983
4532
|
exports.getProperAncestors = getProperAncestors;
|
|
4984
4533
|
exports.getStateNodeByPath = getStateNodeByPath;
|
|
4985
4534
|
exports.getStateNodes = getStateNodes;
|
|
4535
|
+
exports.getTransitionResult = getTransitionResult;
|
|
4536
|
+
exports.hasEffect = hasEffect;
|
|
4986
4537
|
exports.initialMicrostep = initialMicrostep;
|
|
4987
|
-
exports.isAtom = isAtom;
|
|
4988
4538
|
exports.isAtomicStateNode = isAtomicStateNode;
|
|
4539
|
+
exports.isBuiltInExecutableAction = isBuiltInExecutableAction;
|
|
4989
4540
|
exports.isInFinalState = isInFinalState;
|
|
4990
4541
|
exports.isMachineSnapshot = isMachineSnapshot;
|
|
4991
4542
|
exports.isStateId = isStateId;
|
|
@@ -4996,7 +4547,7 @@ exports.matchesEventDescriptor = matchesEventDescriptor;
|
|
|
4996
4547
|
exports.matchesState = matchesState;
|
|
4997
4548
|
exports.parseDelayToMilliseconds = parseDelayToMilliseconds;
|
|
4998
4549
|
exports.pathToStateValue = pathToStateValue;
|
|
4999
|
-
exports.
|
|
4550
|
+
exports.resolveActionsWithContext = resolveActionsWithContext;
|
|
5000
4551
|
exports.resolveReferencedActor = resolveReferencedActor;
|
|
5001
4552
|
exports.resolveStateValue = resolveStateValue;
|
|
5002
4553
|
exports.subscriptionLogic = subscriptionLogic;
|