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