xstate 6.0.0-alpha.2 → 6.0.0-alpha.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +19 -17
- package/bin/xstate.js +36 -0
- package/dist/{StateMachine-64813f46.cjs.js → StateMachine-1b155670.cjs.js} +182 -40
- package/dist/{StateMachine-2dcdb1f5.esm.js → StateMachine-3c17fe3e.esm.js} +182 -40
- package/dist/{StateMachine-8d22a79c.development.cjs.js → StateMachine-550ff7f2.development.cjs.js} +193 -36
- package/dist/{StateMachine-2ea0a7fa.development.esm.js → StateMachine-e25dd61f.development.esm.js} +193 -36
- package/dist/declarations/src/State.d.ts +13 -2
- package/dist/declarations/src/StateMachine.d.ts +23 -13
- package/dist/declarations/src/actions.d.ts +2 -1
- package/dist/declarations/src/actors/promise.d.ts +4 -2
- package/dist/declarations/src/createActor.d.ts +9 -11
- package/dist/declarations/src/createMachine.d.ts +14 -11
- package/dist/declarations/src/createMachineFromConfig.d.ts +61 -28
- package/dist/declarations/src/fsm.d.ts +74 -0
- package/dist/declarations/src/graph/graph.d.ts +3 -3
- package/dist/declarations/src/graph/shortestPaths.d.ts +2 -2
- package/dist/declarations/src/graph/simplePaths.d.ts +2 -2
- package/dist/declarations/src/index.d.ts +6 -5
- package/dist/declarations/src/serialize.d.ts +10 -18
- package/dist/declarations/src/setup.d.ts +277 -65
- package/dist/declarations/src/spawn.d.ts +3 -3
- package/dist/declarations/src/stateUtils.d.ts +4 -1
- package/dist/declarations/src/system.d.ts +1 -1
- package/dist/declarations/src/transition.d.ts +8 -5
- package/dist/declarations/src/transitionActions.d.ts +12 -0
- package/dist/declarations/src/types.d.ts +237 -102
- package/dist/declarations/src/types.v6.d.ts +132 -58
- package/dist/declarations/src/utils.d.ts +4 -3
- package/dist/{index-93edd3bd.esm.js → index-3f87408c.esm.js} +936 -1389
- package/dist/{index-e61170ba.development.esm.js → index-d39fb138.development.esm.js} +968 -1421
- package/dist/{index-9cb3b3a0.cjs.js → index-e694228c.cjs.js} +945 -1394
- package/dist/{index-f6ef20d1.development.cjs.js → index-ec7c7a2d.development.cjs.js} +977 -1426
- package/dist/xstate-actors.cjs.js +1 -1
- package/dist/xstate-actors.development.cjs.js +1 -1
- package/dist/xstate-actors.development.esm.js +1 -1
- package/dist/xstate-actors.esm.js +1 -1
- package/dist/xstate-actors.umd.min.js +1 -1
- package/dist/xstate-actors.umd.min.js.map +1 -1
- package/dist/xstate-graph.cjs.js +2 -2
- package/dist/xstate-graph.development.cjs.js +2 -2
- package/dist/xstate-graph.development.esm.js +2 -2
- package/dist/xstate-graph.esm.js +2 -2
- package/dist/xstate-graph.umd.min.js +1 -1
- package/dist/xstate-graph.umd.min.js.map +1 -1
- package/dist/xstate.cjs.js +1100 -182
- package/dist/xstate.cjs.mjs +3 -5
- package/dist/xstate.development.cjs.js +1100 -182
- package/dist/xstate.development.cjs.mjs +3 -5
- package/dist/xstate.development.esm.js +1101 -181
- package/dist/xstate.esm.js +1101 -181
- package/dist/xstate.umd.min.js +1 -1
- package/dist/xstate.umd.min.js.map +1 -1
- package/package.json +6 -2
- package/dist/declarations/src/atom.d.ts +0 -81
|
@@ -1,18 +1,5 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
/**
|
|
4
|
-
* Reactivity interop between actors and atoms.
|
|
5
|
-
*
|
|
6
|
-
* `Actor#get()` calls this hook (when installed) so that computed atoms can
|
|
7
|
-
* track actor reads as dependencies. The hook is installed by `atom.ts` on
|
|
8
|
-
* module evaluation, which only happens when atoms are actually used — apps
|
|
9
|
-
* that never import atoms don't bundle the reactive system.
|
|
10
|
-
*/
|
|
11
|
-
let onActorRead;
|
|
12
|
-
function installActorReadHook(hook) {
|
|
13
|
-
onActorRead = hook;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
3
|
class Mailbox {
|
|
17
4
|
constructor(_process) {
|
|
18
5
|
this._process = _process;
|
|
@@ -66,6 +53,8 @@ const NULL_EVENT = '';
|
|
|
66
53
|
const STATE_IDENTIFIER = '#';
|
|
67
54
|
const WILDCARD = '*';
|
|
68
55
|
const XSTATE_INIT = '@xstate.init';
|
|
56
|
+
const XSTATE_START = '@xstate.start';
|
|
57
|
+
const XSTATE_SPAWN = '@xstate.spawn';
|
|
69
58
|
const XSTATE_STOP = '@xstate.stop';
|
|
70
59
|
|
|
71
60
|
/**
|
|
@@ -142,6 +131,12 @@ function createErrorActorEvent(id, error) {
|
|
|
142
131
|
actorId: id
|
|
143
132
|
};
|
|
144
133
|
}
|
|
134
|
+
function createErrorPlatformEvent(kind, error) {
|
|
135
|
+
return {
|
|
136
|
+
type: `xstate.error.${kind}`,
|
|
137
|
+
error
|
|
138
|
+
};
|
|
139
|
+
}
|
|
145
140
|
function createInitEvent(input) {
|
|
146
141
|
return {
|
|
147
142
|
type: XSTATE_INIT,
|
|
@@ -166,6 +161,9 @@ function reportUnhandledError(err) {
|
|
|
166
161
|
const symbolObservable = (() => typeof Symbol === 'function' && Symbol.observable || '@@observable')();
|
|
167
162
|
|
|
168
163
|
const builtInActions = {
|
|
164
|
+
// No-op: creation/registration are eager and pure; the start is a separate
|
|
165
|
+
// deferred `@xstate.start` effect (see `deriveDeferredStarts`).
|
|
166
|
+
['@xstate.spawn']: _actor => {},
|
|
169
167
|
['@xstate.start']: actor => {
|
|
170
168
|
actor.start();
|
|
171
169
|
},
|
|
@@ -187,7 +185,7 @@ const builtInActions = {
|
|
|
187
185
|
['@xstate.cancel']: (actorScope, sendId) => {
|
|
188
186
|
actorScope.system.scheduler.cancel(actorScope.self, sendId);
|
|
189
187
|
},
|
|
190
|
-
['@xstate.
|
|
188
|
+
['@xstate.stop']: (actorScope, actor) => {
|
|
191
189
|
actorScope.stopChild(actor);
|
|
192
190
|
}
|
|
193
191
|
};
|
|
@@ -199,15 +197,20 @@ const subscriptions = /* #__PURE__ */new WeakMap();
|
|
|
199
197
|
* (`enq.listen()` / `enq.subscribeTo()`) and detaches when stopped.
|
|
200
198
|
*/
|
|
201
199
|
function createAttachedLogic(attach) {
|
|
200
|
+
const initialTransition = (input, _) => [{
|
|
201
|
+
status: 'active',
|
|
202
|
+
output: undefined,
|
|
203
|
+
error: undefined,
|
|
204
|
+
input
|
|
205
|
+
}, []];
|
|
202
206
|
return {
|
|
203
207
|
start: (state, {
|
|
204
208
|
self,
|
|
205
209
|
system
|
|
206
210
|
}) => {
|
|
207
|
-
// Don't attach if the target doesn't exist
|
|
208
|
-
// Atoms have no `getSnapshot` / lifecycle, so they always attach.
|
|
211
|
+
// Don't attach if the target doesn't exist or is stopped.
|
|
209
212
|
const target = state.input.actor;
|
|
210
|
-
if (!target ||
|
|
213
|
+
if (!target || target.getSnapshot().status === 'stopped') {
|
|
211
214
|
return;
|
|
212
215
|
}
|
|
213
216
|
const subscription = attach(state.input, {
|
|
@@ -250,20 +253,16 @@ function createAttachedLogic(attach) {
|
|
|
250
253
|
if (event.type === XSTATE_STOP) {
|
|
251
254
|
subscriptions.get(self)?.unsubscribe();
|
|
252
255
|
subscriptions.delete(self);
|
|
253
|
-
return {
|
|
256
|
+
return [{
|
|
254
257
|
...state,
|
|
255
258
|
status: 'stopped',
|
|
256
259
|
error: undefined
|
|
257
|
-
};
|
|
260
|
+
}, []];
|
|
258
261
|
}
|
|
259
|
-
return state;
|
|
262
|
+
return [state, []];
|
|
260
263
|
},
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
output: undefined,
|
|
264
|
-
error: undefined,
|
|
265
|
-
input
|
|
266
|
-
}),
|
|
264
|
+
initialTransition,
|
|
265
|
+
getInitialSnapshot: (actorScope, input) => initialTransition(input)[0],
|
|
267
266
|
getPersistedSnapshot: snapshot => snapshot,
|
|
268
267
|
restoreSnapshot: snapshot => snapshot
|
|
269
268
|
};
|
|
@@ -309,620 +308,6 @@ function createListenerLogic() {
|
|
|
309
308
|
// Singleton logic instance
|
|
310
309
|
const listenerLogic = /* #__PURE__ */createListenerLogic();
|
|
311
310
|
|
|
312
|
-
/* eslint-disable */
|
|
313
|
-
// Adapted from Alien Signals
|
|
314
|
-
// https://github.com/stackblitz/alien-signals/
|
|
315
|
-
|
|
316
|
-
let ReactiveFlags = /*#__PURE__*/function (ReactiveFlags) {
|
|
317
|
-
ReactiveFlags[ReactiveFlags["None"] = 0] = "None";
|
|
318
|
-
ReactiveFlags[ReactiveFlags["Mutable"] = 1] = "Mutable";
|
|
319
|
-
ReactiveFlags[ReactiveFlags["Watching"] = 2] = "Watching";
|
|
320
|
-
ReactiveFlags[ReactiveFlags["RecursedCheck"] = 4] = "RecursedCheck";
|
|
321
|
-
ReactiveFlags[ReactiveFlags["Recursed"] = 8] = "Recursed";
|
|
322
|
-
ReactiveFlags[ReactiveFlags["Dirty"] = 16] = "Dirty";
|
|
323
|
-
ReactiveFlags[ReactiveFlags["Pending"] = 32] = "Pending";
|
|
324
|
-
return ReactiveFlags;
|
|
325
|
-
}({});
|
|
326
|
-
function createReactiveSystem({
|
|
327
|
-
update,
|
|
328
|
-
notify,
|
|
329
|
-
unwatched
|
|
330
|
-
}) {
|
|
331
|
-
return {
|
|
332
|
-
link,
|
|
333
|
-
unlink,
|
|
334
|
-
propagate,
|
|
335
|
-
checkDirty,
|
|
336
|
-
shallowPropagate
|
|
337
|
-
};
|
|
338
|
-
function link(dep, sub, version) {
|
|
339
|
-
const prevDep = sub.depsTail;
|
|
340
|
-
if (prevDep !== undefined && prevDep.dep === dep) {
|
|
341
|
-
return;
|
|
342
|
-
}
|
|
343
|
-
const nextDep = prevDep !== undefined ? prevDep.nextDep : sub.deps;
|
|
344
|
-
if (nextDep !== undefined && nextDep.dep === dep) {
|
|
345
|
-
nextDep.version = version;
|
|
346
|
-
sub.depsTail = nextDep;
|
|
347
|
-
return;
|
|
348
|
-
}
|
|
349
|
-
const prevSub = dep.subsTail;
|
|
350
|
-
if (prevSub !== undefined && prevSub.version === version && prevSub.sub === sub) {
|
|
351
|
-
return;
|
|
352
|
-
}
|
|
353
|
-
const newLink = sub.depsTail = dep.subsTail = {
|
|
354
|
-
version,
|
|
355
|
-
dep,
|
|
356
|
-
sub,
|
|
357
|
-
prevDep,
|
|
358
|
-
nextDep,
|
|
359
|
-
prevSub,
|
|
360
|
-
nextSub: undefined
|
|
361
|
-
};
|
|
362
|
-
if (nextDep !== undefined) {
|
|
363
|
-
nextDep.prevDep = newLink;
|
|
364
|
-
}
|
|
365
|
-
if (prevDep !== undefined) {
|
|
366
|
-
prevDep.nextDep = newLink;
|
|
367
|
-
} else {
|
|
368
|
-
sub.deps = newLink;
|
|
369
|
-
}
|
|
370
|
-
if (prevSub !== undefined) {
|
|
371
|
-
prevSub.nextSub = newLink;
|
|
372
|
-
} else {
|
|
373
|
-
dep.subs = newLink;
|
|
374
|
-
}
|
|
375
|
-
}
|
|
376
|
-
function unlink(link, sub = link.sub) {
|
|
377
|
-
const dep = link.dep;
|
|
378
|
-
const prevDep = link.prevDep;
|
|
379
|
-
const nextDep = link.nextDep;
|
|
380
|
-
const nextSub = link.nextSub;
|
|
381
|
-
const prevSub = link.prevSub;
|
|
382
|
-
if (nextDep !== undefined) {
|
|
383
|
-
nextDep.prevDep = prevDep;
|
|
384
|
-
} else {
|
|
385
|
-
sub.depsTail = prevDep;
|
|
386
|
-
}
|
|
387
|
-
if (prevDep !== undefined) {
|
|
388
|
-
prevDep.nextDep = nextDep;
|
|
389
|
-
} else {
|
|
390
|
-
sub.deps = nextDep;
|
|
391
|
-
}
|
|
392
|
-
if (nextSub !== undefined) {
|
|
393
|
-
nextSub.prevSub = prevSub;
|
|
394
|
-
} else {
|
|
395
|
-
dep.subsTail = prevSub;
|
|
396
|
-
}
|
|
397
|
-
if (prevSub !== undefined) {
|
|
398
|
-
prevSub.nextSub = nextSub;
|
|
399
|
-
} else if ((dep.subs = nextSub) === undefined) {
|
|
400
|
-
unwatched(dep);
|
|
401
|
-
}
|
|
402
|
-
return nextDep;
|
|
403
|
-
}
|
|
404
|
-
function propagate(link) {
|
|
405
|
-
let next = link.nextSub;
|
|
406
|
-
let stack;
|
|
407
|
-
top: do {
|
|
408
|
-
const sub = link.sub;
|
|
409
|
-
let flags = sub.flags;
|
|
410
|
-
if (!(flags & (ReactiveFlags.RecursedCheck | ReactiveFlags.Recursed | ReactiveFlags.Dirty | ReactiveFlags.Pending))) {
|
|
411
|
-
sub.flags = flags | ReactiveFlags.Pending;
|
|
412
|
-
} else if (!(flags & (ReactiveFlags.RecursedCheck | ReactiveFlags.Recursed))) {
|
|
413
|
-
flags = ReactiveFlags.None;
|
|
414
|
-
} else if (!(flags & ReactiveFlags.RecursedCheck)) {
|
|
415
|
-
sub.flags = flags & ~ReactiveFlags.Recursed | ReactiveFlags.Pending;
|
|
416
|
-
} else if (!(flags & (ReactiveFlags.Dirty | ReactiveFlags.Pending)) && isValidLink(link, sub)) {
|
|
417
|
-
sub.flags = flags | (ReactiveFlags.Recursed | ReactiveFlags.Pending);
|
|
418
|
-
flags &= ReactiveFlags.Mutable;
|
|
419
|
-
} else {
|
|
420
|
-
flags = ReactiveFlags.None;
|
|
421
|
-
}
|
|
422
|
-
if (flags & ReactiveFlags.Watching) {
|
|
423
|
-
notify(sub);
|
|
424
|
-
}
|
|
425
|
-
if (flags & ReactiveFlags.Mutable) {
|
|
426
|
-
const subSubs = sub.subs;
|
|
427
|
-
if (subSubs !== undefined) {
|
|
428
|
-
const nextSub = (link = subSubs).nextSub;
|
|
429
|
-
if (nextSub !== undefined) {
|
|
430
|
-
stack = {
|
|
431
|
-
value: next,
|
|
432
|
-
prev: stack
|
|
433
|
-
};
|
|
434
|
-
next = nextSub;
|
|
435
|
-
}
|
|
436
|
-
continue;
|
|
437
|
-
}
|
|
438
|
-
}
|
|
439
|
-
if ((link = next) !== undefined) {
|
|
440
|
-
next = link.nextSub;
|
|
441
|
-
continue;
|
|
442
|
-
}
|
|
443
|
-
while (stack !== undefined) {
|
|
444
|
-
link = stack.value;
|
|
445
|
-
stack = stack.prev;
|
|
446
|
-
if (link !== undefined) {
|
|
447
|
-
next = link.nextSub;
|
|
448
|
-
continue top;
|
|
449
|
-
}
|
|
450
|
-
}
|
|
451
|
-
break;
|
|
452
|
-
} while (true);
|
|
453
|
-
}
|
|
454
|
-
function checkDirty(link, sub) {
|
|
455
|
-
let stack;
|
|
456
|
-
let checkDepth = 0;
|
|
457
|
-
let dirty = false;
|
|
458
|
-
top: do {
|
|
459
|
-
const dep = link.dep;
|
|
460
|
-
const flags = dep.flags;
|
|
461
|
-
if (sub.flags & ReactiveFlags.Dirty) {
|
|
462
|
-
dirty = true;
|
|
463
|
-
} else if ((flags & (ReactiveFlags.Mutable | ReactiveFlags.Dirty)) === (ReactiveFlags.Mutable | ReactiveFlags.Dirty)) {
|
|
464
|
-
if (update(dep)) {
|
|
465
|
-
const subs = dep.subs;
|
|
466
|
-
if (subs.nextSub !== undefined) {
|
|
467
|
-
shallowPropagate(subs);
|
|
468
|
-
}
|
|
469
|
-
dirty = true;
|
|
470
|
-
}
|
|
471
|
-
} else if ((flags & (ReactiveFlags.Mutable | ReactiveFlags.Pending)) === (ReactiveFlags.Mutable | ReactiveFlags.Pending)) {
|
|
472
|
-
if (link.nextSub !== undefined || link.prevSub !== undefined) {
|
|
473
|
-
stack = {
|
|
474
|
-
value: link,
|
|
475
|
-
prev: stack
|
|
476
|
-
};
|
|
477
|
-
}
|
|
478
|
-
link = dep.deps;
|
|
479
|
-
sub = dep;
|
|
480
|
-
++checkDepth;
|
|
481
|
-
continue;
|
|
482
|
-
}
|
|
483
|
-
if (!dirty) {
|
|
484
|
-
const nextDep = link.nextDep;
|
|
485
|
-
if (nextDep !== undefined) {
|
|
486
|
-
link = nextDep;
|
|
487
|
-
continue;
|
|
488
|
-
}
|
|
489
|
-
}
|
|
490
|
-
while (checkDepth--) {
|
|
491
|
-
const firstSub = sub.subs;
|
|
492
|
-
const hasMultipleSubs = firstSub.nextSub !== undefined;
|
|
493
|
-
if (hasMultipleSubs) {
|
|
494
|
-
link = stack.value;
|
|
495
|
-
stack = stack.prev;
|
|
496
|
-
} else {
|
|
497
|
-
link = firstSub;
|
|
498
|
-
}
|
|
499
|
-
if (dirty) {
|
|
500
|
-
if (update(sub)) {
|
|
501
|
-
if (hasMultipleSubs) {
|
|
502
|
-
shallowPropagate(firstSub);
|
|
503
|
-
}
|
|
504
|
-
sub = link.sub;
|
|
505
|
-
continue;
|
|
506
|
-
}
|
|
507
|
-
dirty = false;
|
|
508
|
-
} else {
|
|
509
|
-
sub.flags &= ~ReactiveFlags.Pending;
|
|
510
|
-
}
|
|
511
|
-
sub = link.sub;
|
|
512
|
-
const nextDep = link.nextDep;
|
|
513
|
-
if (nextDep !== undefined) {
|
|
514
|
-
link = nextDep;
|
|
515
|
-
continue top;
|
|
516
|
-
}
|
|
517
|
-
}
|
|
518
|
-
return dirty;
|
|
519
|
-
} while (true);
|
|
520
|
-
}
|
|
521
|
-
function shallowPropagate(link) {
|
|
522
|
-
do {
|
|
523
|
-
const sub = link.sub;
|
|
524
|
-
const flags = sub.flags;
|
|
525
|
-
if ((flags & (ReactiveFlags.Pending | ReactiveFlags.Dirty)) === ReactiveFlags.Pending) {
|
|
526
|
-
sub.flags = flags | ReactiveFlags.Dirty;
|
|
527
|
-
if ((flags & (ReactiveFlags.Watching | ReactiveFlags.RecursedCheck)) === ReactiveFlags.Watching) {
|
|
528
|
-
notify(sub);
|
|
529
|
-
}
|
|
530
|
-
}
|
|
531
|
-
} while ((link = link.nextSub) !== undefined);
|
|
532
|
-
}
|
|
533
|
-
function isValidLink(checkLink, sub) {
|
|
534
|
-
let link = sub.depsTail;
|
|
535
|
-
while (link !== undefined) {
|
|
536
|
-
if (link === checkLink) {
|
|
537
|
-
return true;
|
|
538
|
-
}
|
|
539
|
-
link = link.prevDep;
|
|
540
|
-
}
|
|
541
|
-
return false;
|
|
542
|
-
}
|
|
543
|
-
}
|
|
544
|
-
|
|
545
|
-
/**
|
|
546
|
-
* Nominal brand (phantom, compile-time only) marking a value as an XState atom.
|
|
547
|
-
* Real atoms get the runtime brand ({@link $$atom}) in `createAtom`; this type
|
|
548
|
-
* brand keeps atom-only APIs (e.g. `enq.subscribeTo`) from structurally
|
|
549
|
-
* accepting plain readables like `actor.select(...)` that would be dispatched
|
|
550
|
-
* down the non-atom path at runtime.
|
|
551
|
-
*/
|
|
552
|
-
|
|
553
|
-
/** Brand marking an object as an atom, so it can be detected structurally. */
|
|
554
|
-
const $$atom = /* #__PURE__ */Symbol.for('xstate.atom');
|
|
555
|
-
|
|
556
|
-
/**
|
|
557
|
-
* Returns `true` if the value is an atom (writable, computed, async, or
|
|
558
|
-
* reducer). Lets actor-consuming APIs (e.g. `enq.subscribeTo`) accept atoms
|
|
559
|
-
* directly.
|
|
560
|
-
*/
|
|
561
|
-
function isAtom(value) {
|
|
562
|
-
return !!value && typeof value === 'object' && $$atom in value;
|
|
563
|
-
}
|
|
564
|
-
const queuedEffects = [];
|
|
565
|
-
let cycle = 0;
|
|
566
|
-
// Initialized lazily on first atom creation (instead of at module evaluation)
|
|
567
|
-
// so the reactive system stays tree-shakeable for apps that never use atoms —
|
|
568
|
-
|
|
569
|
-
// would be retained by consumer bundlers even when atoms are unused.
|
|
570
|
-
let link;
|
|
571
|
-
let unlink;
|
|
572
|
-
let propagate;
|
|
573
|
-
let checkDirty;
|
|
574
|
-
let shallowPropagate;
|
|
575
|
-
let reactiveSystemInstalled = false;
|
|
576
|
-
function ensureReactiveSystem() {
|
|
577
|
-
if (reactiveSystemInstalled) {
|
|
578
|
-
return;
|
|
579
|
-
}
|
|
580
|
-
reactiveSystemInstalled = true;
|
|
581
|
-
({
|
|
582
|
-
link,
|
|
583
|
-
unlink,
|
|
584
|
-
propagate,
|
|
585
|
-
checkDirty,
|
|
586
|
-
shallowPropagate
|
|
587
|
-
} = createReactiveSystem({
|
|
588
|
-
update(atom) {
|
|
589
|
-
return atom._update();
|
|
590
|
-
},
|
|
591
|
-
notify(effect) {
|
|
592
|
-
queuedEffects[queuedEffectsLength++] = effect;
|
|
593
|
-
effect.flags &= ~ReactiveFlags.Watching;
|
|
594
|
-
},
|
|
595
|
-
unwatched(atom) {
|
|
596
|
-
if (atom.depsTail !== undefined) {
|
|
597
|
-
atom.depsTail = undefined;
|
|
598
|
-
atom.flags = ReactiveFlags.Mutable | ReactiveFlags.Dirty;
|
|
599
|
-
purgeDeps(atom);
|
|
600
|
-
}
|
|
601
|
-
}
|
|
602
|
-
}));
|
|
603
|
-
}
|
|
604
|
-
let notifyIndex = 0;
|
|
605
|
-
let queuedEffectsLength = 0;
|
|
606
|
-
let activeSub;
|
|
607
|
-
function purgeDeps(sub) {
|
|
608
|
-
const depsTail = sub.depsTail;
|
|
609
|
-
let dep = depsTail !== undefined ? depsTail.nextDep : sub.deps;
|
|
610
|
-
while (dep !== undefined) {
|
|
611
|
-
dep = unlink(dep, sub);
|
|
612
|
-
}
|
|
613
|
-
}
|
|
614
|
-
function flush() {
|
|
615
|
-
while (notifyIndex < queuedEffectsLength) {
|
|
616
|
-
const effect = queuedEffects[notifyIndex];
|
|
617
|
-
queuedEffects[notifyIndex++] = undefined;
|
|
618
|
-
effect.notify();
|
|
619
|
-
}
|
|
620
|
-
notifyIndex = 0;
|
|
621
|
-
queuedEffectsLength = 0;
|
|
622
|
-
}
|
|
623
|
-
|
|
624
|
-
/** The current state of an async atom. */
|
|
625
|
-
|
|
626
|
-
/** Options passed to an async atom getter. */
|
|
627
|
-
|
|
628
|
-
function compareAsyncAtomState(a, b) {
|
|
629
|
-
if (a.status !== b.status) {
|
|
630
|
-
return false;
|
|
631
|
-
}
|
|
632
|
-
if (a.status === 'done' && b.status === 'done') {
|
|
633
|
-
return Object.is(a.data, b.data);
|
|
634
|
-
}
|
|
635
|
-
if (a.status === 'error' && b.status === 'error') {
|
|
636
|
-
return Object.is(a.error, b.error);
|
|
637
|
-
}
|
|
638
|
-
// both 'pending'
|
|
639
|
-
return true;
|
|
640
|
-
}
|
|
641
|
-
function updateAsyncAtom(atom, nextValue) {
|
|
642
|
-
if (atom._update(nextValue)) {
|
|
643
|
-
const subs = atom.subs;
|
|
644
|
-
if (subs !== undefined) {
|
|
645
|
-
propagate(subs);
|
|
646
|
-
shallowPropagate(subs);
|
|
647
|
-
flush();
|
|
648
|
-
}
|
|
649
|
-
}
|
|
650
|
-
}
|
|
651
|
-
|
|
652
|
-
/**
|
|
653
|
-
* Creates a read-only atom whose value is loaded from an async getter.
|
|
654
|
-
*
|
|
655
|
-
* The getter receives an `AbortSignal`; when the async atom recomputes, the
|
|
656
|
-
* previous signal is aborted and stale resolutions are ignored.
|
|
657
|
-
*/
|
|
658
|
-
function createAsyncAtom(getValue, options) {
|
|
659
|
-
const ref = {};
|
|
660
|
-
let currentController;
|
|
661
|
-
let currentRunId = 0;
|
|
662
|
-
const atom = createAtom(() => {
|
|
663
|
-
currentController?.abort();
|
|
664
|
-
const controller = new AbortController();
|
|
665
|
-
const runId = ++currentRunId;
|
|
666
|
-
currentController = controller;
|
|
667
|
-
getValue({
|
|
668
|
-
signal: controller.signal
|
|
669
|
-
}).then(data => {
|
|
670
|
-
if (runId !== currentRunId || controller.signal.aborted) {
|
|
671
|
-
return;
|
|
672
|
-
}
|
|
673
|
-
updateAsyncAtom(ref.current, {
|
|
674
|
-
status: 'done',
|
|
675
|
-
data
|
|
676
|
-
});
|
|
677
|
-
}, error => {
|
|
678
|
-
if (runId !== currentRunId || controller.signal.aborted) {
|
|
679
|
-
return;
|
|
680
|
-
}
|
|
681
|
-
updateAsyncAtom(ref.current, {
|
|
682
|
-
status: 'error',
|
|
683
|
-
error
|
|
684
|
-
});
|
|
685
|
-
});
|
|
686
|
-
return {
|
|
687
|
-
status: 'pending'
|
|
688
|
-
};
|
|
689
|
-
}, {
|
|
690
|
-
compare: compareAsyncAtomState,
|
|
691
|
-
...options
|
|
692
|
-
});
|
|
693
|
-
ref.current = atom;
|
|
694
|
-
return atom;
|
|
695
|
-
}
|
|
696
|
-
|
|
697
|
-
/**
|
|
698
|
-
* Mirror atoms that bump a version whenever a tracked actor emits a snapshot,
|
|
699
|
-
* letting computed atoms depend on `actor.get()` reads.
|
|
700
|
-
*
|
|
701
|
-
* The hook is installed lazily on first atom creation (not at module
|
|
702
|
-
* evaluation) so that it remains tree-shakeable for apps that never use atoms —
|
|
703
|
-
* any tracked read necessarily happens inside an atom computation, so first-use
|
|
704
|
-
* installation is always early enough.
|
|
705
|
-
*/
|
|
706
|
-
let actorInteropInstalled = false;
|
|
707
|
-
function ensureActorInterop() {
|
|
708
|
-
if (actorInteropInstalled) {
|
|
709
|
-
return;
|
|
710
|
-
}
|
|
711
|
-
actorInteropInstalled = true;
|
|
712
|
-
const actorVersions = new WeakMap();
|
|
713
|
-
installActorReadHook(actorRef => {
|
|
714
|
-
if (activeSub === undefined) {
|
|
715
|
-
return;
|
|
716
|
-
}
|
|
717
|
-
let version = actorVersions.get(actorRef);
|
|
718
|
-
if (version === undefined) {
|
|
719
|
-
version = createAtom(0);
|
|
720
|
-
actorVersions.set(actorRef, version);
|
|
721
|
-
actorRef.subscribe({
|
|
722
|
-
next: () => version.set(v => v + 1),
|
|
723
|
-
error: () => {}
|
|
724
|
-
});
|
|
725
|
-
}
|
|
726
|
-
version.get();
|
|
727
|
-
});
|
|
728
|
-
}
|
|
729
|
-
|
|
730
|
-
/**
|
|
731
|
-
* Creates an atom.
|
|
732
|
-
*
|
|
733
|
-
* Pass a value for a writable atom or a getter for a computed read-only atom.
|
|
734
|
-
*/
|
|
735
|
-
|
|
736
|
-
function createAtom(valueOrFn, optionsOrInput) {
|
|
737
|
-
ensureReactiveSystem();
|
|
738
|
-
ensureActorInterop();
|
|
739
|
-
const isComputed = typeof valueOrFn === 'function';
|
|
740
|
-
const getter = valueOrFn;
|
|
741
|
-
|
|
742
|
-
// Create plain object atom
|
|
743
|
-
const atom = {
|
|
744
|
-
_snapshot: isComputed ? undefined : valueOrFn,
|
|
745
|
-
subs: undefined,
|
|
746
|
-
subsTail: undefined,
|
|
747
|
-
deps: undefined,
|
|
748
|
-
depsTail: undefined,
|
|
749
|
-
flags: isComputed ? ReactiveFlags.None : ReactiveFlags.Mutable,
|
|
750
|
-
get() {
|
|
751
|
-
if (activeSub !== undefined) {
|
|
752
|
-
link(atom, activeSub, cycle);
|
|
753
|
-
}
|
|
754
|
-
return atom._snapshot;
|
|
755
|
-
},
|
|
756
|
-
subscribe(observerOrFn) {
|
|
757
|
-
const observer = typeof observerOrFn === 'function' ? {
|
|
758
|
-
next: observerOrFn
|
|
759
|
-
} : observerOrFn;
|
|
760
|
-
const observed = {
|
|
761
|
-
current: false
|
|
762
|
-
};
|
|
763
|
-
const e = effect(() => {
|
|
764
|
-
atom.get();
|
|
765
|
-
if (!observed.current) {
|
|
766
|
-
observed.current = true;
|
|
767
|
-
} else {
|
|
768
|
-
const prevSub = activeSub;
|
|
769
|
-
activeSub = undefined;
|
|
770
|
-
try {
|
|
771
|
-
observer.next?.(atom._snapshot);
|
|
772
|
-
} catch (err) {
|
|
773
|
-
reportUnhandledError(err);
|
|
774
|
-
} finally {
|
|
775
|
-
activeSub = prevSub;
|
|
776
|
-
}
|
|
777
|
-
|
|
778
|
-
// If the observer synchronously updates any of our deps we'll be
|
|
779
|
-
// marked as dirty preventing this effect from re-running. Request
|
|
780
|
-
// the value again to reconcile any dirty deps.
|
|
781
|
-
atom.get();
|
|
782
|
-
}
|
|
783
|
-
});
|
|
784
|
-
return {
|
|
785
|
-
unsubscribe: () => {
|
|
786
|
-
e.stop();
|
|
787
|
-
}
|
|
788
|
-
};
|
|
789
|
-
},
|
|
790
|
-
_update(getValue) {
|
|
791
|
-
const prevSub = activeSub;
|
|
792
|
-
const compare = optionsOrInput?.compare ?? Object.is;
|
|
793
|
-
activeSub = atom;
|
|
794
|
-
++cycle;
|
|
795
|
-
atom.depsTail = undefined;
|
|
796
|
-
if (isComputed) {
|
|
797
|
-
atom.flags = ReactiveFlags.Mutable | ReactiveFlags.RecursedCheck;
|
|
798
|
-
}
|
|
799
|
-
try {
|
|
800
|
-
const oldValue = atom._snapshot;
|
|
801
|
-
const newValue = typeof getValue === 'function' ? getValue(oldValue) : getValue === undefined && isComputed ? getter(oldValue) : getValue;
|
|
802
|
-
if (oldValue === undefined || !compare(oldValue, newValue)) {
|
|
803
|
-
atom._snapshot = newValue;
|
|
804
|
-
return true;
|
|
805
|
-
}
|
|
806
|
-
return false;
|
|
807
|
-
} finally {
|
|
808
|
-
activeSub = prevSub;
|
|
809
|
-
if (isComputed) {
|
|
810
|
-
atom.flags &= ~ReactiveFlags.RecursedCheck;
|
|
811
|
-
}
|
|
812
|
-
purgeDeps(atom);
|
|
813
|
-
}
|
|
814
|
-
}
|
|
815
|
-
};
|
|
816
|
-
if (isComputed) {
|
|
817
|
-
atom.flags = ReactiveFlags.Mutable | ReactiveFlags.Dirty;
|
|
818
|
-
atom.get = function () {
|
|
819
|
-
const flags = atom.flags;
|
|
820
|
-
if (flags & ReactiveFlags.Dirty || flags & ReactiveFlags.Pending && checkDirty(atom.deps, atom)) {
|
|
821
|
-
if (atom._update()) {
|
|
822
|
-
const subs = atom.subs;
|
|
823
|
-
if (subs !== undefined) {
|
|
824
|
-
shallowPropagate(subs);
|
|
825
|
-
}
|
|
826
|
-
}
|
|
827
|
-
} else if (flags & ReactiveFlags.Pending) {
|
|
828
|
-
atom.flags = flags & ~ReactiveFlags.Pending;
|
|
829
|
-
}
|
|
830
|
-
if (activeSub !== undefined) {
|
|
831
|
-
link(atom, activeSub, cycle);
|
|
832
|
-
}
|
|
833
|
-
return atom._snapshot;
|
|
834
|
-
};
|
|
835
|
-
} else {
|
|
836
|
-
atom.set = function (valueOrFn) {
|
|
837
|
-
if (atom._update(valueOrFn)) {
|
|
838
|
-
const subs = atom.subs;
|
|
839
|
-
if (subs !== undefined) {
|
|
840
|
-
propagate(subs);
|
|
841
|
-
shallowPropagate(subs);
|
|
842
|
-
flush();
|
|
843
|
-
}
|
|
844
|
-
}
|
|
845
|
-
};
|
|
846
|
-
}
|
|
847
|
-
atom[$$atom] = true;
|
|
848
|
-
return atom;
|
|
849
|
-
}
|
|
850
|
-
|
|
851
|
-
/**
|
|
852
|
-
* Creates an inert atom config that can be instantiated later.
|
|
853
|
-
*
|
|
854
|
-
* Useful for framework hooks that need to create a stable local atom from
|
|
855
|
-
* component input.
|
|
856
|
-
*/
|
|
857
|
-
|
|
858
|
-
function createAtomConfig(initialValueOrFn, options) {
|
|
859
|
-
return {
|
|
860
|
-
createAtom(input) {
|
|
861
|
-
const initialValue = typeof initialValueOrFn === 'function' ? initialValueOrFn(input) : initialValueOrFn;
|
|
862
|
-
return createAtom(initialValue, options);
|
|
863
|
-
}
|
|
864
|
-
};
|
|
865
|
-
}
|
|
866
|
-
|
|
867
|
-
/** Creates an atom whose updates are handled by a reducer function. */
|
|
868
|
-
function createReducerAtom(initialValue, reducer, options) {
|
|
869
|
-
const atom = createAtom(initialValue, options);
|
|
870
|
-
return {
|
|
871
|
-
[$$atom]: true,
|
|
872
|
-
get: atom.get.bind(atom),
|
|
873
|
-
subscribe: atom.subscribe.bind(atom),
|
|
874
|
-
send(event) {
|
|
875
|
-
const prevSub = activeSub;
|
|
876
|
-
activeSub = undefined;
|
|
877
|
-
let nextState;
|
|
878
|
-
try {
|
|
879
|
-
nextState = reducer(atom.get(), event);
|
|
880
|
-
} finally {
|
|
881
|
-
activeSub = prevSub;
|
|
882
|
-
}
|
|
883
|
-
atom.set(nextState);
|
|
884
|
-
}
|
|
885
|
-
};
|
|
886
|
-
}
|
|
887
|
-
function effect(fn) {
|
|
888
|
-
const run = () => {
|
|
889
|
-
const prevSub = activeSub;
|
|
890
|
-
activeSub = effectObj;
|
|
891
|
-
++cycle;
|
|
892
|
-
effectObj.depsTail = undefined;
|
|
893
|
-
effectObj.flags = ReactiveFlags.Watching | ReactiveFlags.RecursedCheck;
|
|
894
|
-
try {
|
|
895
|
-
return fn();
|
|
896
|
-
} finally {
|
|
897
|
-
activeSub = prevSub;
|
|
898
|
-
effectObj.flags &= ~ReactiveFlags.RecursedCheck;
|
|
899
|
-
purgeDeps(effectObj);
|
|
900
|
-
}
|
|
901
|
-
};
|
|
902
|
-
const effectObj = {
|
|
903
|
-
deps: undefined,
|
|
904
|
-
depsTail: undefined,
|
|
905
|
-
subs: undefined,
|
|
906
|
-
subsTail: undefined,
|
|
907
|
-
flags: ReactiveFlags.Watching | ReactiveFlags.RecursedCheck,
|
|
908
|
-
notify() {
|
|
909
|
-
const flags = this.flags;
|
|
910
|
-
if (flags & ReactiveFlags.Dirty || flags & ReactiveFlags.Pending && checkDirty(this.deps, this)) {
|
|
911
|
-
run();
|
|
912
|
-
} else {
|
|
913
|
-
this.flags = ReactiveFlags.Watching;
|
|
914
|
-
}
|
|
915
|
-
},
|
|
916
|
-
stop() {
|
|
917
|
-
this.flags = ReactiveFlags.None;
|
|
918
|
-
this.depsTail = undefined;
|
|
919
|
-
purgeDeps(this);
|
|
920
|
-
}
|
|
921
|
-
};
|
|
922
|
-
run();
|
|
923
|
-
return effectObj;
|
|
924
|
-
}
|
|
925
|
-
|
|
926
311
|
/**
|
|
927
312
|
* Creates actor logic for subscribing to lifecycle events (done/error/snapshot)
|
|
928
313
|
* from another actor. Used internally by `enq.subscribeTo()`.
|
|
@@ -940,15 +325,6 @@ function createSubscriptionLogic() {
|
|
|
940
325
|
error,
|
|
941
326
|
snapshot: onSnapshot
|
|
942
327
|
} = mappers;
|
|
943
|
-
|
|
944
|
-
// Atoms emit a raw value (no `status`) and have no done/error lifecycle —
|
|
945
|
-
// pass the value straight to the `snapshot` mapper.
|
|
946
|
-
if (isAtom(actor)) {
|
|
947
|
-
if (!onSnapshot) {
|
|
948
|
-
return;
|
|
949
|
-
}
|
|
950
|
-
return actor.subscribe(value => relayMappedToParent(self, system, () => onSnapshot(value)));
|
|
951
|
-
}
|
|
952
328
|
return actor.subscribe({
|
|
953
329
|
next: snapshot => {
|
|
954
330
|
if (snapshot.status === 'done' && done) {
|
|
@@ -974,6 +350,19 @@ function createSubscriptionLogic() {
|
|
|
974
350
|
// Singleton logic instance
|
|
975
351
|
const subscriptionLogic = /* #__PURE__ */createSubscriptionLogic();
|
|
976
352
|
|
|
353
|
+
const builtInExecutableAction = Symbol('xstate.builtInExecutableAction');
|
|
354
|
+
function brandBuiltInExecutableAction(action) {
|
|
355
|
+
Object.defineProperty(action, builtInExecutableAction, {
|
|
356
|
+
value: true
|
|
357
|
+
});
|
|
358
|
+
return action;
|
|
359
|
+
}
|
|
360
|
+
function mergeContextPatch(context, patch) {
|
|
361
|
+
return {
|
|
362
|
+
...context,
|
|
363
|
+
...patch
|
|
364
|
+
};
|
|
365
|
+
}
|
|
977
366
|
function pushBuiltInAction(actions, action, ...args) {
|
|
978
367
|
actions.push({
|
|
979
368
|
action,
|
|
@@ -1009,11 +398,11 @@ function unregisterChild(actor) {
|
|
|
1009
398
|
_special: true
|
|
1010
399
|
});
|
|
1011
400
|
}
|
|
1012
|
-
function
|
|
1013
|
-
pushBuiltInAction(actions, builtInActions['@xstate.
|
|
401
|
+
function pushSpawnedChild(actions, actor, id) {
|
|
402
|
+
pushBuiltInAction(actions, builtInActions['@xstate.spawn'], actor);
|
|
1014
403
|
actions.push(registerSpawnedChild(actor, id ?? actor.id));
|
|
1015
404
|
}
|
|
1016
|
-
function createTransitionEnqueue(actorScope, actions, internalEvents, actorSubscriptions = false) {
|
|
405
|
+
function createTransitionEnqueue(actorScope, actions, internalEvents, actorSubscriptions = false, createActors = true) {
|
|
1017
406
|
const props = {
|
|
1018
407
|
cancel: id => {
|
|
1019
408
|
pushBuiltInAction(actions, builtInActions['@xstate.cancel'], actorScope, id);
|
|
@@ -1035,21 +424,32 @@ function createTransitionEnqueue(actorScope, actions, internalEvents, actorSubsc
|
|
|
1035
424
|
}
|
|
1036
425
|
},
|
|
1037
426
|
spawn: (logic, options) => {
|
|
427
|
+
if (!createActors) {
|
|
428
|
+
// TODO: replace this speculative placeholder with a typed inert actor ref.
|
|
429
|
+
return {
|
|
430
|
+
id: options?.id ?? options?.registryKey ?? logic.id
|
|
431
|
+
};
|
|
432
|
+
}
|
|
1038
433
|
const actor = createActor(logic, {
|
|
1039
434
|
...options,
|
|
1040
435
|
parent: actorScope.self
|
|
1041
436
|
});
|
|
1042
|
-
|
|
437
|
+
pushSpawnedChild(actions, actor, options?.id);
|
|
1043
438
|
return actor;
|
|
1044
439
|
},
|
|
1045
440
|
sendTo: (actor, event, options) => {
|
|
1046
|
-
if (actor) {
|
|
1047
|
-
|
|
441
|
+
if (!actor) {
|
|
442
|
+
internalEvents.push(createErrorPlatformEvent('communication', {
|
|
443
|
+
message: 'Unable to send event to an undefined actor',
|
|
444
|
+
event
|
|
445
|
+
}));
|
|
446
|
+
return;
|
|
1048
447
|
}
|
|
448
|
+
pushBuiltInAction(actions, builtInActions['@xstate.sendTo'], actorScope, actor, event, options);
|
|
1049
449
|
},
|
|
1050
450
|
stop: actor => {
|
|
1051
451
|
if (actor) {
|
|
1052
|
-
pushBuiltInAction(actions, builtInActions['@xstate.
|
|
452
|
+
pushBuiltInAction(actions, builtInActions['@xstate.stop'], actorScope, actor);
|
|
1053
453
|
actions.push(unregisterChild(actor));
|
|
1054
454
|
}
|
|
1055
455
|
}
|
|
@@ -1066,7 +466,7 @@ function createTransitionEnqueue(actorScope, actions, internalEvents, actorSubsc
|
|
|
1066
466
|
input,
|
|
1067
467
|
parent: actorScope.self
|
|
1068
468
|
});
|
|
1069
|
-
pushBuiltInAction(actions, builtInActions['@xstate.
|
|
469
|
+
pushBuiltInAction(actions, builtInActions['@xstate.spawn'], listenerActor);
|
|
1070
470
|
return listenerActor;
|
|
1071
471
|
},
|
|
1072
472
|
subscribeTo: (actor, mappers) => {
|
|
@@ -1081,34 +481,125 @@ function createTransitionEnqueue(actorScope, actions, internalEvents, actorSubsc
|
|
|
1081
481
|
input,
|
|
1082
482
|
parent: actorScope.self
|
|
1083
483
|
});
|
|
1084
|
-
pushBuiltInAction(actions, builtInActions['@xstate.
|
|
484
|
+
pushBuiltInAction(actions, builtInActions['@xstate.spawn'], subscriptionActor);
|
|
1085
485
|
return subscriptionActor;
|
|
1086
486
|
}
|
|
1087
487
|
});
|
|
1088
488
|
}
|
|
1089
|
-
return createEnqueueObject
|
|
489
|
+
return createEnqueueObject(props, (action, ...args) => {
|
|
1090
490
|
pushBuiltInAction(actions, action, ...args);
|
|
1091
491
|
});
|
|
1092
492
|
}
|
|
1093
|
-
function
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
493
|
+
function getBuiltInActionFields(action, args) {
|
|
494
|
+
switch (action) {
|
|
495
|
+
case builtInActions['@xstate.spawn']:
|
|
496
|
+
{
|
|
497
|
+
const [actor] = args;
|
|
498
|
+
return {
|
|
499
|
+
actor,
|
|
500
|
+
id: actor.id,
|
|
501
|
+
logic: actor.logic,
|
|
502
|
+
src: actor.src,
|
|
503
|
+
input: actor.options?.input
|
|
504
|
+
};
|
|
505
|
+
}
|
|
506
|
+
case builtInActions['@xstate.raise']:
|
|
507
|
+
{
|
|
508
|
+
const [, event, options] = args;
|
|
509
|
+
return {
|
|
510
|
+
event,
|
|
511
|
+
id: options?.id,
|
|
512
|
+
delay: options?.delay
|
|
513
|
+
};
|
|
514
|
+
}
|
|
515
|
+
case builtInActions['@xstate.sendTo']:
|
|
516
|
+
{
|
|
517
|
+
const [, target, event, options] = args;
|
|
518
|
+
return {
|
|
519
|
+
target,
|
|
520
|
+
event,
|
|
521
|
+
id: options?.id,
|
|
522
|
+
delay: options?.delay
|
|
523
|
+
};
|
|
524
|
+
}
|
|
525
|
+
case builtInActions['@xstate.cancel']:
|
|
526
|
+
{
|
|
527
|
+
const [, id] = args;
|
|
528
|
+
return {
|
|
529
|
+
id
|
|
530
|
+
};
|
|
531
|
+
}
|
|
532
|
+
case builtInActions['@xstate.stop']:
|
|
533
|
+
{
|
|
534
|
+
const [, actor] = args;
|
|
535
|
+
return {
|
|
536
|
+
actor
|
|
537
|
+
};
|
|
538
|
+
}
|
|
539
|
+
default:
|
|
540
|
+
return undefined;
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
function createStartEffect(actor) {
|
|
544
|
+
const action = builtInActions['@xstate.start'];
|
|
545
|
+
const args = [actor];
|
|
546
|
+
return brandBuiltInExecutableAction({
|
|
547
|
+
type: XSTATE_START,
|
|
548
|
+
params: {
|
|
549
|
+
action,
|
|
550
|
+
args
|
|
551
|
+
},
|
|
552
|
+
args,
|
|
553
|
+
exec: action.bind(null, actor),
|
|
554
|
+
actor,
|
|
555
|
+
id: actor.id
|
|
556
|
+
});
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
/**
|
|
560
|
+
* Attached (listener/subscription) starts are ordered before child starts so
|
|
561
|
+
* that a listener/subscription captures events emitted synchronously as its
|
|
562
|
+
* target actor starts.
|
|
563
|
+
*/
|
|
564
|
+
function deriveDeferredStarts(effects) {
|
|
565
|
+
const attachedStarts = [];
|
|
566
|
+
const childStarts = [];
|
|
567
|
+
for (const effect of effects) {
|
|
568
|
+
if (!isBuiltInExecutableAction(effect) || effect.type !== XSTATE_SPAWN) {
|
|
569
|
+
continue;
|
|
1102
570
|
}
|
|
571
|
+
const {
|
|
572
|
+
actor,
|
|
573
|
+
logic
|
|
574
|
+
} = effect;
|
|
575
|
+
const start = createStartEffect(actor);
|
|
576
|
+
if (logic === listenerLogic || logic === subscriptionLogic) {
|
|
577
|
+
attachedStarts.push(start);
|
|
578
|
+
} else {
|
|
579
|
+
childStarts.push(start);
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
return [...attachedStarts, ...childStarts];
|
|
583
|
+
}
|
|
584
|
+
function isBuiltInExecutableAction(action) {
|
|
585
|
+
return action[builtInExecutableAction] === true;
|
|
586
|
+
}
|
|
587
|
+
function resolveActionsWithContext(currentSnapshot, event, actorScope, actions) {
|
|
588
|
+
let intermediateSnapshot = currentSnapshot;
|
|
589
|
+
const executableActions = [];
|
|
590
|
+
for (const action of actions) {
|
|
591
|
+
const isInline = typeof action === 'function';
|
|
592
|
+
const resolvedAction = isInline ? action : typeof action === 'object' && 'action' in action && typeof action.action === 'function' ? action.action.bind(null, ...action.args) : false;
|
|
1103
593
|
const actionArgs = {
|
|
1104
594
|
context: intermediateSnapshot.context,
|
|
1105
595
|
event,
|
|
596
|
+
output: getEventOutput(event),
|
|
1106
597
|
self: actorScope.self,
|
|
1107
598
|
system: actorScope.system,
|
|
1108
599
|
children: intermediateSnapshot.children,
|
|
1109
600
|
parent: actorScope.self._parent,
|
|
1110
601
|
actions: currentSnapshot.machine.implementations.actions,
|
|
1111
|
-
|
|
602
|
+
actorSources: currentSnapshot.machine.implementations.actorSources
|
|
1112
603
|
};
|
|
1113
604
|
let actionParams = undefined;
|
|
1114
605
|
if (typeof action === 'object' && action !== null) {
|
|
@@ -1119,7 +610,7 @@ function resolveAndExecuteActionsWithContext(currentSnapshot, event, actorScope,
|
|
|
1119
610
|
actionParams = emittedEventParams;
|
|
1120
611
|
}
|
|
1121
612
|
if (resolvedAction && '_special' in resolvedAction) {
|
|
1122
|
-
|
|
613
|
+
executableActions.push({
|
|
1123
614
|
type: typeof action === 'object' ? 'action' in action && typeof action.action === 'function' ? action.action.name ?? '(anonymous)' : action.type ?? '(anonymous)' : action.name || '(anonymous)',
|
|
1124
615
|
params: actionParams,
|
|
1125
616
|
args: [],
|
|
@@ -1128,30 +619,37 @@ function resolveAndExecuteActionsWithContext(currentSnapshot, event, actorScope,
|
|
|
1128
619
|
const specialAction = resolvedAction;
|
|
1129
620
|
const res = specialAction(actionArgs, emptyEnqueueObject);
|
|
1130
621
|
if (res && ('context' in res || 'children' in res)) {
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
622
|
+
// Special-action patches never change `_nodes`, so a shallow clone is
|
|
623
|
+
// equivalent to `cloneMachineSnapshot` — and keeps this module (and
|
|
624
|
+
// non-machine logic like `createFSM`) independent of State.ts.
|
|
625
|
+
intermediateSnapshot = {
|
|
626
|
+
...intermediateSnapshot,
|
|
627
|
+
...(res.context !== undefined ? {
|
|
628
|
+
context: mergeContextPatch(intermediateSnapshot.context, res.context)
|
|
1134
629
|
} : {}),
|
|
1135
630
|
...('children' in res ? {
|
|
1136
631
|
children: res.children
|
|
1137
632
|
} : {})
|
|
1138
|
-
}
|
|
633
|
+
};
|
|
1139
634
|
}
|
|
1140
635
|
continue;
|
|
1141
636
|
}
|
|
1142
637
|
if (!resolvedAction || !('resolve' in resolvedAction)) {
|
|
1143
|
-
|
|
638
|
+
const builtInFields = typeof action === 'object' && action !== null && 'action' in action && typeof action.action === 'function' ? getBuiltInActionFields(action.action, action.args) : undefined;
|
|
639
|
+
const executableAction = {
|
|
1144
640
|
type: typeof action === 'object' ? 'action' in action && typeof action.action === 'function' ? action.action.name ?? '(anonymous)' : action.type : action.name || '(anonymous)',
|
|
1145
641
|
params: actionParams,
|
|
1146
642
|
args: typeof action === 'object' && 'action' in action ? action.args : [],
|
|
1147
|
-
exec: resolvedAction
|
|
1148
|
-
|
|
643
|
+
exec: resolvedAction || (typeof action === 'object' && action !== null ? () => actorScope.defer(() => actorScope.emit(action)) : undefined),
|
|
644
|
+
...builtInFields
|
|
645
|
+
};
|
|
646
|
+
executableActions.push(builtInFields ? brandBuiltInExecutableAction(executableAction) : executableAction);
|
|
1149
647
|
continue;
|
|
1150
648
|
}
|
|
1151
649
|
}
|
|
1152
|
-
return intermediateSnapshot;
|
|
650
|
+
return [intermediateSnapshot, executableActions];
|
|
1153
651
|
}
|
|
1154
|
-
function createEnqueueObject
|
|
652
|
+
function createEnqueueObject(props, action) {
|
|
1155
653
|
const enqueueFn = (fn, ...args) => {
|
|
1156
654
|
action(fn, ...args);
|
|
1157
655
|
};
|
|
@@ -1169,7 +667,7 @@ function createEnqueueObject$1(props, action) {
|
|
|
1169
667
|
});
|
|
1170
668
|
return enqueueFn;
|
|
1171
669
|
}
|
|
1172
|
-
const emptyEnqueueObject = createEnqueueObject
|
|
670
|
+
const emptyEnqueueObject = createEnqueueObject({}, () => {});
|
|
1173
671
|
|
|
1174
672
|
function parseDurationToMilliseconds(duration) {
|
|
1175
673
|
const normalizedDuration = duration.trim();
|
|
@@ -1231,6 +729,9 @@ function resolveDelay(delay, delaySource, args) {
|
|
|
1231
729
|
}
|
|
1232
730
|
return configuredDelay;
|
|
1233
731
|
}
|
|
732
|
+
function getStateInput(snapshot, stateNodeId) {
|
|
733
|
+
return snapshot._stateInputs?.[stateNodeId];
|
|
734
|
+
}
|
|
1234
735
|
function isAtomicStateNode(stateNode) {
|
|
1235
736
|
return stateNode.type === 'atomic' || stateNode.type === 'final' || stateNode.type === 'choice';
|
|
1236
737
|
}
|
|
@@ -1343,22 +844,9 @@ function isInFinalState(stateNodeSet, stateNode) {
|
|
|
1343
844
|
}
|
|
1344
845
|
const isStateId = str => str[0] === STATE_IDENTIFIER;
|
|
1345
846
|
function getCandidates(stateNode, receivedEventType) {
|
|
1346
|
-
const
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
}
|
|
1350
|
-
const matchingDescriptors = [];
|
|
1351
|
-
for (const eventDescriptor of stateNode.transitions.keys()) {
|
|
1352
|
-
if (matchesEventDescriptor(receivedEventType, eventDescriptor)) {
|
|
1353
|
-
matchingDescriptors.push(eventDescriptor);
|
|
1354
|
-
}
|
|
1355
|
-
}
|
|
1356
|
-
matchingDescriptors.sort((a, b) => b.length - a.length);
|
|
1357
|
-
const wildcardCandidates = [];
|
|
1358
|
-
for (const descriptor of matchingDescriptors) {
|
|
1359
|
-
wildcardCandidates.push(...stateNode.transitions.get(descriptor));
|
|
1360
|
-
}
|
|
1361
|
-
return wildcardCandidates;
|
|
847
|
+
const exactMatch = stateNode.transitions.get(receivedEventType);
|
|
848
|
+
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));
|
|
849
|
+
return exactMatch ? [...exactMatch, ...wildcardCandidates] : wildcardCandidates;
|
|
1362
850
|
}
|
|
1363
851
|
function scheduleDelayedEvent(stateNode, event, resolveScheduledDelay) {
|
|
1364
852
|
const eventType = event.type;
|
|
@@ -1387,61 +875,69 @@ function getDelayedTransitions(stateNode) {
|
|
|
1387
875
|
if (!afterConfig && timeoutConfig === undefined && invokeDefs.length === 0) {
|
|
1388
876
|
return [];
|
|
1389
877
|
}
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
...transition,
|
|
1403
|
-
event,
|
|
1404
|
-
delay
|
|
1405
|
-
});
|
|
1406
|
-
}
|
|
1407
|
-
};
|
|
878
|
+
|
|
879
|
+
// Every delayed transition — `after`, state-level `timeout`/`onTimeout`, and
|
|
880
|
+
// invoke-level `timeout`/`onTimeout` — has the same shape: an event raised on
|
|
881
|
+
// entry (and cancelled on exit) plus a transition that catches it. They
|
|
882
|
+
// differ only in the event raised, the transition(s) caught, and whether the
|
|
883
|
+
// delay resolves against the machine's `delays` map (invoke timeouts do not).
|
|
884
|
+
//
|
|
885
|
+
// `xstate.timeout.<id>` is a dedicated event so a state-level `timeout` cannot
|
|
886
|
+
// collide with an explicit `after` entry on the same state. Invoke timeouts
|
|
887
|
+
// are scheduled on the enclosing state; completion transitions cancel the
|
|
888
|
+
// timer separately, so it clears even when the parent state stays active.
|
|
889
|
+
const sources = [];
|
|
1408
890
|
if (afterConfig) {
|
|
1409
|
-
for (const
|
|
1410
|
-
const
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
891
|
+
for (const key of Object.keys(afterConfig)) {
|
|
892
|
+
const delay = Number.isNaN(+key) ? key : +key;
|
|
893
|
+
sources.push({
|
|
894
|
+
event: createAfterEvent(delay, stateNode.id),
|
|
895
|
+
delay,
|
|
896
|
+
transitions: afterConfig[key],
|
|
897
|
+
fromDelaysMap: true
|
|
898
|
+
});
|
|
1415
899
|
}
|
|
1416
900
|
}
|
|
1417
|
-
|
|
1418
|
-
// Desugar state-level `timeout` + `onTimeout` into a delayed transition.
|
|
1419
|
-
// Uses a dedicated `xstate.timeout.<id>` event so it cannot collide with
|
|
1420
|
-
// explicit `after` entries on the same state.
|
|
1421
901
|
if (timeoutConfig !== undefined && onTimeoutConfig) {
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
})
|
|
1428
|
-
const resolvedDelay = typeof timeoutConfig === 'function' ? timeoutConfig : getConfiguredDelayValue(timeoutConfig, stateNode.machine.implementations.delays);
|
|
1429
|
-
addDelayedTransitions(onTimeoutConfig, timeoutEventType, resolvedDelay);
|
|
902
|
+
sources.push({
|
|
903
|
+
event: createTimeoutEvent(stateNode.id),
|
|
904
|
+
delay: timeoutConfig,
|
|
905
|
+
transitions: onTimeoutConfig,
|
|
906
|
+
fromDelaysMap: true
|
|
907
|
+
});
|
|
1430
908
|
}
|
|
1431
|
-
|
|
1432
|
-
// Desugar invoke-level `timeout` + `onTimeout` into a delayed transition on
|
|
1433
|
-
// the enclosing state. Completion transitions cancel this timer separately,
|
|
1434
|
-
// so the timeout is cleared even when the parent state stays active.
|
|
1435
909
|
for (const invokeDef of invokeDefs) {
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
910
|
+
sources.push({
|
|
911
|
+
event: createInvokeTimeoutEvent(invokeDef.id),
|
|
912
|
+
delay: invokeDef.timeout,
|
|
913
|
+
transitions: invokeDef.onTimeout,
|
|
914
|
+
fromDelaysMap: false
|
|
915
|
+
});
|
|
916
|
+
}
|
|
917
|
+
|
|
918
|
+
// `delay` here is the raw config value, retained only as metadata on the
|
|
919
|
+
// transition definition — the live delay is resolved at runtime in the
|
|
920
|
+
// scheduled entry action, so no eager resolution is needed.
|
|
921
|
+
const delayedTransitions = [];
|
|
922
|
+
for (const {
|
|
923
|
+
event,
|
|
924
|
+
delay,
|
|
925
|
+
transitions,
|
|
926
|
+
fromDelaysMap
|
|
927
|
+
} of sources) {
|
|
928
|
+
const eventType = scheduleDelayedEvent(stateNode, event, x => resolveDelay(delay, fromDelaysMap ? x.delays : {}, {
|
|
1439
929
|
context: x.context,
|
|
1440
930
|
event: x.event,
|
|
1441
|
-
stateNode
|
|
931
|
+
stateNode,
|
|
932
|
+
input: x.input
|
|
1442
933
|
}));
|
|
1443
|
-
const
|
|
1444
|
-
|
|
934
|
+
for (const transition of toTransitionConfigArray(transitions)) {
|
|
935
|
+
delayedTransitions.push({
|
|
936
|
+
...transition,
|
|
937
|
+
event: eventType,
|
|
938
|
+
delay
|
|
939
|
+
});
|
|
940
|
+
}
|
|
1445
941
|
}
|
|
1446
942
|
return delayedTransitions.map(delayedTransition => ({
|
|
1447
943
|
...formatTransition(stateNode, delayedTransition.event, delayedTransition),
|
|
@@ -1503,30 +999,13 @@ function formatRouteTransitions(rootStateNode) {
|
|
|
1503
999
|
}
|
|
1504
1000
|
return;
|
|
1505
1001
|
}
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
// Object-form route. A `guard` (string or function) on the object is
|
|
1511
|
-
// only produced by the JSON layer (createMachineFromConfig) —
|
|
1512
|
-
// authoring uses the function form above.
|
|
1513
|
-
const userGuard = routeConfig.guard;
|
|
1002
|
+
const {
|
|
1003
|
+
guard: _guard,
|
|
1004
|
+
...routeOptions
|
|
1005
|
+
} = routeConfig;
|
|
1514
1006
|
const transition = {
|
|
1515
|
-
...
|
|
1516
|
-
guard:
|
|
1517
|
-
if (!routeMatches(args)) return false;
|
|
1518
|
-
if (typeof userGuard === 'function') return userGuard(args);
|
|
1519
|
-
if (typeof userGuard === 'string') {
|
|
1520
|
-
const guardImpl = args.guards?.[userGuard];
|
|
1521
|
-
if (!guardImpl) {
|
|
1522
|
-
// A typo'd guard name must fail loudly — silently passing
|
|
1523
|
-
// would allow the route and corrupt machine behavior.
|
|
1524
|
-
throw new Error(`Guard '${userGuard}' is not implemented in machine '${rootStateNode.machine.id}'.`);
|
|
1525
|
-
}
|
|
1526
|
-
return guardImpl(args);
|
|
1527
|
-
}
|
|
1528
|
-
return true;
|
|
1529
|
-
} : routeMatches,
|
|
1007
|
+
...routeOptions,
|
|
1008
|
+
guard: routeMatches,
|
|
1530
1009
|
target: `#${routeId}`
|
|
1531
1010
|
};
|
|
1532
1011
|
routeTransitions.push(formatTransition(rootStateNode, 'xstate.route', transition));
|
|
@@ -1565,7 +1044,7 @@ function resolveTarget(stateNode, targets) {
|
|
|
1565
1044
|
const targetStateNode = getStateNodeByPath(stateNode.parent, resolvedTarget);
|
|
1566
1045
|
return targetStateNode;
|
|
1567
1046
|
} catch (err) {
|
|
1568
|
-
throw new Error(`Invalid transition
|
|
1047
|
+
throw new Error(`Invalid transition for '${stateNode.id}': ${err.message}`);
|
|
1569
1048
|
}
|
|
1570
1049
|
} else {
|
|
1571
1050
|
throw new Error(`Invalid target: "${target}"`);
|
|
@@ -1575,6 +1054,14 @@ function resolveTarget(stateNode, targets) {
|
|
|
1575
1054
|
function resolveHistoryDefaultTransition(stateNode) {
|
|
1576
1055
|
const normalizedTarget = normalizeTarget(stateNode.config.target);
|
|
1577
1056
|
if (!normalizedTarget) {
|
|
1057
|
+
if (stateNode.parent.type === 'parallel') {
|
|
1058
|
+
return {
|
|
1059
|
+
target: [stateNode.parent],
|
|
1060
|
+
source: stateNode,
|
|
1061
|
+
reenter: false,
|
|
1062
|
+
eventType: ''
|
|
1063
|
+
};
|
|
1064
|
+
}
|
|
1578
1065
|
return stateNode.parent.initial;
|
|
1579
1066
|
}
|
|
1580
1067
|
return {
|
|
@@ -1616,10 +1103,7 @@ function getStateNode(stateNode, stateKey) {
|
|
|
1616
1103
|
if (isStateId(stateKey)) {
|
|
1617
1104
|
return stateNode.machine.getStateNodeById(stateKey);
|
|
1618
1105
|
}
|
|
1619
|
-
|
|
1620
|
-
throw new Error(`Unable to retrieve child state '${stateKey}' from '${stateNode.id}'; no child states exist.`);
|
|
1621
|
-
}
|
|
1622
|
-
const result = stateNode.states[stateKey];
|
|
1106
|
+
const result = stateNode.states?.[stateKey];
|
|
1623
1107
|
if (!result) {
|
|
1624
1108
|
throw new Error(`Child state '${stateKey}' does not exist on '${stateNode.id}'`);
|
|
1625
1109
|
}
|
|
@@ -1762,6 +1246,11 @@ function removeConflictingTransitions(enabledTransitions, stateNodeSet, snapshot
|
|
|
1762
1246
|
if (hasIntersection(getExitSet(t1), getExitSet(t2))) {
|
|
1763
1247
|
if (isDescendant(t1.source, t2.source)) {
|
|
1764
1248
|
transitionsToRemove.add(t2);
|
|
1249
|
+
} else if (t2.source.type === 'final' && t1.source.type !== 'final') {
|
|
1250
|
+
// A transition sourced in a final state yields to a conflicting
|
|
1251
|
+
// transition from a live state, so a done region doesn't keep
|
|
1252
|
+
// consuming events its siblings can still handle.
|
|
1253
|
+
transitionsToRemove.add(t2);
|
|
1765
1254
|
} else {
|
|
1766
1255
|
t1Preempted = true;
|
|
1767
1256
|
break;
|
|
@@ -1781,7 +1270,9 @@ function getEffectiveTargetStates(transition, snapshot, event, actorScope) {
|
|
|
1781
1270
|
const historyValue = snapshot.historyValue;
|
|
1782
1271
|
const {
|
|
1783
1272
|
targets
|
|
1784
|
-
} = getTransitionResult(transition, snapshot, event, actorScope
|
|
1273
|
+
} = getTransitionResult(transition, snapshot, event, actorScope, {
|
|
1274
|
+
resolveActions: false
|
|
1275
|
+
});
|
|
1785
1276
|
if (!targets) {
|
|
1786
1277
|
return [];
|
|
1787
1278
|
}
|
|
@@ -1803,19 +1294,44 @@ function getEffectiveTargetStates(transition, snapshot, event, actorScope) {
|
|
|
1803
1294
|
}
|
|
1804
1295
|
return [...targetSet];
|
|
1805
1296
|
}
|
|
1297
|
+
|
|
1298
|
+
/**
|
|
1299
|
+
* Narrows a parallel domain to the single child region that contains every
|
|
1300
|
+
* effective target, so that a transition confined to one region does not exit
|
|
1301
|
+
* (and reset) its sibling regions.
|
|
1302
|
+
*/
|
|
1303
|
+
function narrowParallelDomain(domain, targetStates) {
|
|
1304
|
+
let narrowed = domain;
|
|
1305
|
+
while (narrowed.type === 'parallel') {
|
|
1306
|
+
const region = getChildren(narrowed).find(child => targetStates.every(target => target === child || isDescendant(target, child)));
|
|
1307
|
+
if (!region) {
|
|
1308
|
+
break;
|
|
1309
|
+
}
|
|
1310
|
+
narrowed = region;
|
|
1311
|
+
}
|
|
1312
|
+
return narrowed;
|
|
1313
|
+
}
|
|
1806
1314
|
function getTransitionDomain(transition, snapshot, event, actorScope) {
|
|
1807
1315
|
const targetStates = getEffectiveTargetStates(transition, snapshot, event, actorScope);
|
|
1808
1316
|
const {
|
|
1809
1317
|
reenter
|
|
1810
|
-
} = getTransitionResult(transition, snapshot, event, actorScope
|
|
1811
|
-
|
|
1812
|
-
|
|
1318
|
+
} = getTransitionResult(transition, snapshot, event, actorScope, {
|
|
1319
|
+
resolveActions: false
|
|
1320
|
+
});
|
|
1321
|
+
if (targetStates.every(target => target === transition.source || isDescendant(target, transition.source))) {
|
|
1322
|
+
// Targets are contained within the source. A reentering transition
|
|
1323
|
+
// exits and reenters the source itself; otherwise the domain narrows
|
|
1324
|
+
// past parallel nodes so sibling regions are left untouched.
|
|
1325
|
+
return reenter ? transition.source : narrowParallelDomain(transition.source, targetStates);
|
|
1813
1326
|
}
|
|
1814
1327
|
const [head, ...tail] = targetStates.concat(transition.source);
|
|
1815
1328
|
// Find the least common ancestor (LCA) of the source and effective targets.
|
|
1816
1329
|
for (const ancestor of getProperAncestors(head, undefined)) {
|
|
1817
1330
|
if (tail.every(sn => isDescendant(sn, ancestor))) {
|
|
1818
|
-
|
|
1331
|
+
// A cross-region transition (source in one parallel region, targets in
|
|
1332
|
+
// another) only exits the region containing its targets; the source
|
|
1333
|
+
// region and other sibling regions stay put unless it reenters.
|
|
1334
|
+
return reenter ? ancestor : narrowParallelDomain(ancestor, targetStates);
|
|
1819
1335
|
}
|
|
1820
1336
|
}
|
|
1821
1337
|
|
|
@@ -1823,17 +1339,20 @@ function getTransitionDomain(transition, snapshot, event, actorScope) {
|
|
|
1823
1339
|
if (reenter) {
|
|
1824
1340
|
return;
|
|
1825
1341
|
}
|
|
1826
|
-
return transition.source.machine.root;
|
|
1342
|
+
return narrowParallelDomain(transition.source.machine.root, targetStates);
|
|
1827
1343
|
}
|
|
1828
1344
|
function computeExitSet(transitions, stateNodeSet, snapshot, event, actorScope) {
|
|
1829
1345
|
const statesToExit = new Set();
|
|
1830
1346
|
for (const transition of transitions) {
|
|
1831
1347
|
const {
|
|
1832
|
-
targets
|
|
1833
|
-
|
|
1348
|
+
targets,
|
|
1349
|
+
reenter
|
|
1350
|
+
} = getTransitionResult(transition, snapshot, event, actorScope, {
|
|
1351
|
+
resolveActions: false
|
|
1352
|
+
});
|
|
1834
1353
|
if (targets?.length) {
|
|
1835
1354
|
const domain = getTransitionDomain(transition, snapshot, event, actorScope);
|
|
1836
|
-
if (
|
|
1355
|
+
if (reenter && transition.source === domain) {
|
|
1837
1356
|
statesToExit.add(domain);
|
|
1838
1357
|
}
|
|
1839
1358
|
for (const stateNode of stateNodeSet) {
|
|
@@ -1857,16 +1376,11 @@ function initialMicrostep(root, preInitialState, actorScope, initEvent, internal
|
|
|
1857
1376
|
|
|
1858
1377
|
/** https://www.w3.org/TR/scxml/#microstepProcedure */
|
|
1859
1378
|
function microstep(transitions, currentSnapshot, actorScope, event, isInitial, internalQueue) {
|
|
1860
|
-
const
|
|
1379
|
+
const executableActions = [];
|
|
1861
1380
|
if (!transitions.length) {
|
|
1862
|
-
return [currentSnapshot,
|
|
1381
|
+
return [currentSnapshot, executableActions];
|
|
1863
1382
|
}
|
|
1864
|
-
|
|
1865
|
-
actorScope.actionExecutor = action => {
|
|
1866
|
-
actions.push(action);
|
|
1867
|
-
originalExecutor(action);
|
|
1868
|
-
};
|
|
1869
|
-
try {
|
|
1383
|
+
{
|
|
1870
1384
|
const mutStateNodeSet = new Set(currentSnapshot._nodes);
|
|
1871
1385
|
let historyValue = currentSnapshot.historyValue;
|
|
1872
1386
|
const originalContext = currentSnapshot.context;
|
|
@@ -1887,13 +1401,13 @@ function microstep(transitions, currentSnapshot, actorScope, event, isInitial, i
|
|
|
1887
1401
|
children,
|
|
1888
1402
|
system: actorScope.system,
|
|
1889
1403
|
actions: currentSnapshot.machine.implementations.actions,
|
|
1890
|
-
|
|
1404
|
+
actorSources: currentSnapshot.machine.implementations.actorSources,
|
|
1891
1405
|
guards: currentSnapshot.machine.implementations.guards,
|
|
1892
1406
|
delays: currentSnapshot.machine.implementations.delays,
|
|
1893
1407
|
input
|
|
1894
1408
|
}, enqueue);
|
|
1895
|
-
if (res?.context) {
|
|
1896
|
-
updatedContext = res.context;
|
|
1409
|
+
if (res?.context !== undefined) {
|
|
1410
|
+
updatedContext = mergeContextPatch(context, res.context);
|
|
1897
1411
|
}
|
|
1898
1412
|
return [actions, updatedContext, internalEvents];
|
|
1899
1413
|
}
|
|
@@ -1929,7 +1443,7 @@ function microstep(transitions, currentSnapshot, actorScope, event, isInitial, i
|
|
|
1929
1443
|
}
|
|
1930
1444
|
}
|
|
1931
1445
|
for (const exitStateNode of statesToExit) {
|
|
1932
|
-
const stateInput = currentSnapshot
|
|
1446
|
+
const stateInput = getStateInput(currentSnapshot, exitStateNode.id);
|
|
1933
1447
|
const [exitActions, nextContext, internalEvents] = exitStateNode.exit ? getStateActionsAndContext(exitStateNode.exit, nextState.context, currentSnapshot.children, stateInput) : [[]];
|
|
1934
1448
|
if (internalEvents?.length) {
|
|
1935
1449
|
internalQueue.push(...internalEvents);
|
|
@@ -1939,7 +1453,9 @@ function microstep(transitions, currentSnapshot, actorScope, event, isInitial, i
|
|
|
1939
1453
|
context: nextContext
|
|
1940
1454
|
});
|
|
1941
1455
|
}
|
|
1942
|
-
|
|
1456
|
+
const [resolvedState, resolvedActions] = resolveActionsWithContext(nextState, event, actorScope, exitActions);
|
|
1457
|
+
nextState = resolvedState;
|
|
1458
|
+
executableActions.push(...resolvedActions);
|
|
1943
1459
|
for (const def of exitStateNode.invoke) {
|
|
1944
1460
|
const childActor = nextState.children[def.id];
|
|
1945
1461
|
if (childActor && !childActor._isExternal) {
|
|
@@ -1964,8 +1480,8 @@ function microstep(transitions, currentSnapshot, actorScope, event, isInitial, i
|
|
|
1964
1480
|
transitionActions.push(...toArray(t.actions));
|
|
1965
1481
|
}
|
|
1966
1482
|
const res = getCurrentTransitionResult(t);
|
|
1967
|
-
if (res.context) {
|
|
1968
|
-
context = res.context;
|
|
1483
|
+
if (res.context !== undefined) {
|
|
1484
|
+
context = mergeContextPatch(context, res.context);
|
|
1969
1485
|
}
|
|
1970
1486
|
if (res.actions) {
|
|
1971
1487
|
transitionActions.push(...res.actions);
|
|
@@ -1980,17 +1496,18 @@ function microstep(transitions, currentSnapshot, actorScope, event, isInitial, i
|
|
|
1980
1496
|
const enterStates = () => {
|
|
1981
1497
|
const getMachineOutput = rootCompletionNode => {
|
|
1982
1498
|
const rootNode = nextState.machine.root;
|
|
1983
|
-
if (rootNode.output === undefined) {
|
|
1984
|
-
return;
|
|
1985
|
-
}
|
|
1986
1499
|
let completionOutput;
|
|
1987
1500
|
if (rootCompletionNode.output !== undefined && rootCompletionNode.parent) {
|
|
1988
|
-
|
|
1501
|
+
const rootDoneEvent = internalQueue.find(e => e.type === `xstate.done.state.${rootNode.id}`);
|
|
1502
|
+
completionOutput = rootDoneEvent && rootCompletionNode.parent === rootNode ? rootDoneEvent.output : resolveOutput(rootCompletionNode.output, nextState.context, event, actorScope.self, stateInputMap[rootCompletionNode.id]);
|
|
1989
1503
|
} else if (rootCompletionNode.type === 'parallel') {
|
|
1990
1504
|
const parallelDoneType = `xstate.done.state.${rootCompletionNode.id}`;
|
|
1991
1505
|
const parallelDoneEvent = internalQueue.find(e => e.type === parallelDoneType);
|
|
1992
1506
|
completionOutput = parallelDoneEvent?.output;
|
|
1993
1507
|
}
|
|
1508
|
+
if (rootNode.output === undefined) {
|
|
1509
|
+
return rootCompletionNode.parent === rootNode ? completionOutput : undefined;
|
|
1510
|
+
}
|
|
1994
1511
|
return resolveOutput(rootNode.output, nextState.context, createDoneStateEvent(rootCompletionNode.id, completionOutput), actorScope.self);
|
|
1995
1512
|
};
|
|
1996
1513
|
const statesToEnter = new Set();
|
|
@@ -2067,7 +1584,9 @@ function microstep(transitions, currentSnapshot, actorScope, event, isInitial, i
|
|
|
2067
1584
|
const {
|
|
2068
1585
|
targets,
|
|
2069
1586
|
reenter
|
|
2070
|
-
} =
|
|
1587
|
+
} = getTransitionResult(transition, currentSnapshot, event, actorScope, {
|
|
1588
|
+
resolveActions: false
|
|
1589
|
+
});
|
|
2071
1590
|
for (const targetNode of targets ?? []) {
|
|
2072
1591
|
if (!isHistoryNode(targetNode) && (transition.source !== targetNode || transition.source !== domain || reenter)) {
|
|
2073
1592
|
statesToEnter.add(targetNode);
|
|
@@ -2083,6 +1602,12 @@ function microstep(transitions, currentSnapshot, actorScope, event, isInitial, i
|
|
|
2083
1602
|
}
|
|
2084
1603
|
addAncestorStatesToEnter(ancestors, !transition.source.parent && reenter ? undefined : domain);
|
|
2085
1604
|
}
|
|
1605
|
+
if (reenter && domain === transition.source) {
|
|
1606
|
+
// A reentering transition whose domain is its own source exits and
|
|
1607
|
+
// reenters the source, so the source must be entered explicitly —
|
|
1608
|
+
// it has no ancestor inside the domain to add it.
|
|
1609
|
+
statesToEnter.add(transition.source);
|
|
1610
|
+
}
|
|
2086
1611
|
}
|
|
2087
1612
|
if (isInitial) {
|
|
2088
1613
|
statesForDefaultEntry.add(currentSnapshot.machine.root);
|
|
@@ -2090,14 +1615,18 @@ function microstep(transitions, currentSnapshot, actorScope, event, isInitial, i
|
|
|
2090
1615
|
const stateInputMap = {
|
|
2091
1616
|
...currentSnapshot._stateInputs
|
|
2092
1617
|
};
|
|
1618
|
+
let stateInputsChanged = false;
|
|
2093
1619
|
for (const transition of filteredTransitions) {
|
|
2094
1620
|
const {
|
|
2095
1621
|
targets,
|
|
2096
1622
|
input
|
|
2097
|
-
} =
|
|
1623
|
+
} = getTransitionResult(transition, currentSnapshot, event, actorScope, {
|
|
1624
|
+
resolveActions: false
|
|
1625
|
+
});
|
|
2098
1626
|
if (input && targets) {
|
|
2099
1627
|
for (const targetNode of targets) {
|
|
2100
1628
|
stateInputMap[targetNode.id] = input;
|
|
1629
|
+
stateInputsChanged = true;
|
|
2101
1630
|
}
|
|
2102
1631
|
}
|
|
2103
1632
|
}
|
|
@@ -2114,7 +1643,7 @@ function microstep(transitions, currentSnapshot, actorScope, event, isInitial, i
|
|
|
2114
1643
|
let src = invokeDef.logic;
|
|
2115
1644
|
if (typeof src === 'function') {
|
|
2116
1645
|
src = src({
|
|
2117
|
-
|
|
1646
|
+
actorSources: currentSnapshot.machine.implementations.actorSources,
|
|
2118
1647
|
context: nextState.context,
|
|
2119
1648
|
event,
|
|
2120
1649
|
self: actorScope.self
|
|
@@ -2122,12 +1651,13 @@ function microstep(transitions, currentSnapshot, actorScope, event, isInitial, i
|
|
|
2122
1651
|
}
|
|
2123
1652
|
const logic = typeof src === 'string' ? resolveReferencedActor(currentSnapshot.machine, src) : src;
|
|
2124
1653
|
if (!logic) {
|
|
2125
|
-
throw new Error(`Actor logic '${typeof src === 'string' ? src : 'inline'}' not implemented
|
|
1654
|
+
throw new Error(`Actor logic '${typeof src === 'string' ? src : 'inline'}' not implemented`);
|
|
2126
1655
|
}
|
|
2127
1656
|
const input = typeof invokeDef.input === 'function' ? invokeDef.input({
|
|
2128
1657
|
self: actorScope.self,
|
|
2129
1658
|
context: nextState.context,
|
|
2130
|
-
event
|
|
1659
|
+
event,
|
|
1660
|
+
output: getEventOutput(event)
|
|
2131
1661
|
}) : invokeDef.input;
|
|
2132
1662
|
const actor = createActor(logic, {
|
|
2133
1663
|
...invokeDef,
|
|
@@ -2136,7 +1666,7 @@ function microstep(transitions, currentSnapshot, actorScope, event, isInitial, i
|
|
|
2136
1666
|
syncSnapshot: !!invokeDef.onSnapshot
|
|
2137
1667
|
});
|
|
2138
1668
|
actions.push({
|
|
2139
|
-
action: builtInActions['@xstate.
|
|
1669
|
+
action: builtInActions['@xstate.spawn'],
|
|
2140
1670
|
args: [actor]
|
|
2141
1671
|
});
|
|
2142
1672
|
if (invokeDef.id) {
|
|
@@ -2148,18 +1678,23 @@ function microstep(transitions, currentSnapshot, actorScope, event, isInitial, i
|
|
|
2148
1678
|
children
|
|
2149
1679
|
});
|
|
2150
1680
|
}
|
|
2151
|
-
let context;
|
|
1681
|
+
let context = nextState.context;
|
|
1682
|
+
let contextChangedByEntry = false;
|
|
2152
1683
|
const stateInput = stateInputMap[stateNodeToEnter.id];
|
|
2153
1684
|
if (stateNodeToEnter.entry) {
|
|
2154
|
-
const [resultActions, nextContext, nextInternalEvents] = getStateActionsAndContext(stateNodeToEnter.entry,
|
|
1685
|
+
const [resultActions, nextContext, nextInternalEvents] = getStateActionsAndContext(stateNodeToEnter.entry, context, children, stateInput);
|
|
2155
1686
|
actions.push(...resultActions);
|
|
2156
1687
|
if (nextInternalEvents?.length) {
|
|
2157
1688
|
internalQueue.push(...nextInternalEvents);
|
|
2158
1689
|
}
|
|
2159
1690
|
if (nextContext) {
|
|
2160
1691
|
context = nextContext;
|
|
1692
|
+
contextChangedByEntry = true;
|
|
2161
1693
|
}
|
|
2162
1694
|
}
|
|
1695
|
+
if (contextChangedByEntry) {
|
|
1696
|
+
nextState.context = context;
|
|
1697
|
+
}
|
|
2163
1698
|
if (statesForDefaultEntry.has(stateNodeToEnter)) {
|
|
2164
1699
|
const {
|
|
2165
1700
|
actions: initialActions,
|
|
@@ -2171,13 +1706,14 @@ function microstep(transitions, currentSnapshot, actorScope, event, isInitial, i
|
|
|
2171
1706
|
if (initialInput && stateNodeToEnter.initial?.target) {
|
|
2172
1707
|
for (const targetNode of stateNodeToEnter.initial.target) {
|
|
2173
1708
|
stateInputMap[targetNode.id] = initialInput;
|
|
1709
|
+
stateInputsChanged = true;
|
|
2174
1710
|
}
|
|
2175
1711
|
}
|
|
2176
1712
|
}
|
|
2177
|
-
|
|
2178
|
-
|
|
2179
|
-
|
|
2180
|
-
|
|
1713
|
+
const [resolvedState, resolvedActions] = resolveActionsWithContext(nextState, event, actorScope, actions);
|
|
1714
|
+
nextState = resolvedState;
|
|
1715
|
+
actions.length = 0;
|
|
1716
|
+
executableActions.push(...resolvedActions);
|
|
2181
1717
|
if (stateNodeToEnter.type !== 'final') {
|
|
2182
1718
|
continue;
|
|
2183
1719
|
}
|
|
@@ -2185,14 +1721,14 @@ function microstep(transitions, currentSnapshot, actorScope, event, isInitial, i
|
|
|
2185
1721
|
let ancestorMarker = parent?.type === 'parallel' ? parent : parent?.parent;
|
|
2186
1722
|
let rootCompletionNode = ancestorMarker || stateNodeToEnter;
|
|
2187
1723
|
if (parent?.type === 'compound') {
|
|
2188
|
-
internalQueue.push(createDoneStateEvent(parent.id, stateNodeToEnter.output !== undefined ? resolveOutput(stateNodeToEnter.output, nextState.context, event, actorScope.self) : undefined));
|
|
1724
|
+
internalQueue.push(createDoneStateEvent(parent.id, stateNodeToEnter.output !== undefined ? resolveOutput(stateNodeToEnter.output, nextState.context, event, actorScope.self, stateInput) : undefined));
|
|
2189
1725
|
}
|
|
2190
1726
|
while (ancestorMarker?.type === 'parallel' && !completedNodes.has(ancestorMarker) && isInFinalState(mutStateNodeSet, ancestorMarker)) {
|
|
2191
1727
|
completedNodes.add(ancestorMarker);
|
|
2192
1728
|
const regionOutput = {};
|
|
2193
1729
|
for (const region of getChildren(ancestorMarker)) {
|
|
2194
1730
|
if (region.type === 'final') {
|
|
2195
|
-
regionOutput[region.key] = region.output !== undefined ? resolveOutput(region.output, nextState.context, event, actorScope.self) : undefined;
|
|
1731
|
+
regionOutput[region.key] = region.output !== undefined ? resolveOutput(region.output, nextState.context, event, actorScope.self, stateInputMap[region.id]) : undefined;
|
|
2196
1732
|
continue;
|
|
2197
1733
|
}
|
|
2198
1734
|
if (region.type === 'parallel') {
|
|
@@ -2202,7 +1738,7 @@ function microstep(transitions, currentSnapshot, actorScope, event, isInitial, i
|
|
|
2202
1738
|
continue;
|
|
2203
1739
|
}
|
|
2204
1740
|
const finalChild = getChildren(region).find(s => s.type === 'final' && mutStateNodeSet.has(s));
|
|
2205
|
-
regionOutput[region.key] = finalChild?.output !== undefined ? resolveOutput(finalChild.output, nextState.context, event, actorScope.self) : undefined;
|
|
1741
|
+
regionOutput[region.key] = finalChild?.output !== undefined ? resolveOutput(finalChild.output, nextState.context, event, actorScope.self, stateInputMap[finalChild.id]) : undefined;
|
|
2206
1742
|
}
|
|
2207
1743
|
internalQueue.push(createDoneStateEvent(ancestorMarker.id, regionOutput));
|
|
2208
1744
|
rootCompletionNode = ancestorMarker;
|
|
@@ -2216,8 +1752,7 @@ function microstep(transitions, currentSnapshot, actorScope, event, isInitial, i
|
|
|
2216
1752
|
output: getMachineOutput(rootCompletionNode)
|
|
2217
1753
|
});
|
|
2218
1754
|
}
|
|
2219
|
-
|
|
2220
|
-
if (inputChanged) {
|
|
1755
|
+
if (stateInputsChanged) {
|
|
2221
1756
|
nextState = cloneMachineSnapshot(nextState, {
|
|
2222
1757
|
_stateInputs: stateInputMap
|
|
2223
1758
|
});
|
|
@@ -2225,7 +1760,9 @@ function microstep(transitions, currentSnapshot, actorScope, event, isInitial, i
|
|
|
2225
1760
|
};
|
|
2226
1761
|
|
|
2227
1762
|
// Execute transition content
|
|
2228
|
-
|
|
1763
|
+
const [resolvedTransitionState, transitionExecutableActions] = resolveActionsWithContext(nextState, event, actorScope, transitionActions);
|
|
1764
|
+
nextState = resolvedTransitionState;
|
|
1765
|
+
executableActions.push(...transitionExecutableActions);
|
|
2229
1766
|
if (context && context !== currentSnapshot.context) {
|
|
2230
1767
|
nextState = cloneMachineSnapshot(nextState, {
|
|
2231
1768
|
context
|
|
@@ -2240,7 +1777,7 @@ function microstep(transitions, currentSnapshot, actorScope, event, isInitial, i
|
|
|
2240
1777
|
const nextStateNodesToExit = nextStateNodes.sort((a, b) => b.order - a.order);
|
|
2241
1778
|
nextStateNodesToExit.forEach(stateNode => {
|
|
2242
1779
|
if (stateNode.exit) {
|
|
2243
|
-
const stateInput = nextState
|
|
1780
|
+
const stateInput = getStateInput(nextState, stateNode.id);
|
|
2244
1781
|
const [exitActions,, nextInternalEvents] = getStateActionsAndContext(stateNode.exit, nextState.context, nextState.children, stateInput);
|
|
2245
1782
|
allExitActions.push(...exitActions);
|
|
2246
1783
|
if (nextInternalEvents?.length) {
|
|
@@ -2248,22 +1785,22 @@ function microstep(transitions, currentSnapshot, actorScope, event, isInitial, i
|
|
|
2248
1785
|
}
|
|
2249
1786
|
}
|
|
2250
1787
|
});
|
|
2251
|
-
|
|
1788
|
+
const [resolvedState, resolvedActions] = resolveActionsWithContext(nextState, event, actorScope, allExitActions);
|
|
1789
|
+
nextState = resolvedState;
|
|
1790
|
+
executableActions.push(...resolvedActions);
|
|
2252
1791
|
}
|
|
2253
1792
|
if (historyValue === currentSnapshot.historyValue && currentSnapshot._nodes.length === mutStateNodeSet.size && currentSnapshot._nodes.every(node => mutStateNodeSet.has(node))) {
|
|
2254
1793
|
// If context was changed (e.g. by entry actions during self-transition),
|
|
2255
1794
|
// clone to ensure reference inequality for eventless transition re-evaluation
|
|
2256
1795
|
if (nextState.context !== originalContext) {
|
|
2257
|
-
return [cloneMachineSnapshot(nextState),
|
|
1796
|
+
return [cloneMachineSnapshot(nextState), executableActions];
|
|
2258
1797
|
}
|
|
2259
|
-
return [nextState,
|
|
1798
|
+
return [nextState, executableActions];
|
|
2260
1799
|
}
|
|
2261
1800
|
return [cloneMachineSnapshot(nextState, {
|
|
2262
1801
|
_nodes: nextStateNodes,
|
|
2263
1802
|
historyValue
|
|
2264
|
-
}),
|
|
2265
|
-
} finally {
|
|
2266
|
-
actorScope.actionExecutor = originalExecutor;
|
|
1803
|
+
}), executableActions];
|
|
2267
1804
|
}
|
|
2268
1805
|
}
|
|
2269
1806
|
|
|
@@ -2271,29 +1808,33 @@ function microstep(transitions, currentSnapshot, actorScope, event, isInitial, i
|
|
|
2271
1808
|
* Gets the transition result for a given transition without executing the
|
|
2272
1809
|
* transition.
|
|
2273
1810
|
*/
|
|
2274
|
-
function getTransitionResult(transition, snapshot, event, actorScope) {
|
|
1811
|
+
function getTransitionResult(transition, snapshot, event, actorScope, options) {
|
|
1812
|
+
const transitionArgs = {
|
|
1813
|
+
context: snapshot.context,
|
|
1814
|
+
event,
|
|
1815
|
+
output: getEventOutput(event),
|
|
1816
|
+
value: snapshot.value,
|
|
1817
|
+
children: snapshot.children,
|
|
1818
|
+
system: actorScope.system,
|
|
1819
|
+
parent: actorScope.self._parent,
|
|
1820
|
+
self: actorScope.self,
|
|
1821
|
+
actions: snapshot.machine.implementations.actions,
|
|
1822
|
+
actorSources: snapshot.machine.implementations.actorSources,
|
|
1823
|
+
guards: snapshot.machine.implementations.guards,
|
|
1824
|
+
delays: snapshot.machine.implementations.delays,
|
|
1825
|
+
input: getStateInput(snapshot, transition.source.id)
|
|
1826
|
+
};
|
|
2275
1827
|
if (transition.to) {
|
|
2276
1828
|
const actions = [];
|
|
2277
1829
|
const internalEvents = [];
|
|
2278
|
-
const enqueue = createTransitionEnqueue(actorScope, actions, internalEvents);
|
|
2279
|
-
const res = transition.to(
|
|
2280
|
-
context: snapshot.context,
|
|
2281
|
-
event,
|
|
2282
|
-
value: snapshot.value,
|
|
2283
|
-
children: snapshot.children,
|
|
2284
|
-
parent: actorScope.self._parent,
|
|
2285
|
-
self: actorScope.self,
|
|
2286
|
-
actions: snapshot.machine.implementations.actions,
|
|
2287
|
-
actors: snapshot.machine.implementations.actors,
|
|
2288
|
-
guards: snapshot.machine.implementations.guards,
|
|
2289
|
-
delays: snapshot.machine.implementations.delays
|
|
2290
|
-
}, enqueue);
|
|
1830
|
+
const enqueue = createTransitionEnqueue(actorScope, actions, internalEvents, false, options?.resolveActions ?? true);
|
|
1831
|
+
const res = transition.to(transitionArgs, enqueue);
|
|
2291
1832
|
const targets = res?.target ? resolveTarget(transition.source, toArray(res.target)) : undefined;
|
|
2292
|
-
|
|
2293
|
-
const resolvedInput = typeof transition.input === 'function' ? transition.input({
|
|
1833
|
+
const resolvedInput = res?.input ?? (typeof transition.input === 'function' ? transition.input({
|
|
2294
1834
|
context: snapshot.context,
|
|
2295
|
-
event
|
|
2296
|
-
|
|
1835
|
+
event,
|
|
1836
|
+
output: getEventOutput(event)
|
|
1837
|
+
}) : transition.input);
|
|
2297
1838
|
return {
|
|
2298
1839
|
targets: targets,
|
|
2299
1840
|
context: res?.context,
|
|
@@ -2307,11 +1848,13 @@ function getTransitionResult(transition, snapshot, event, actorScope) {
|
|
|
2307
1848
|
// Resolve input for regular transitions
|
|
2308
1849
|
const resolvedInput = typeof transition.input === 'function' ? transition.input({
|
|
2309
1850
|
context: snapshot.context,
|
|
2310
|
-
event
|
|
1851
|
+
event,
|
|
1852
|
+
output: getEventOutput(event)
|
|
2311
1853
|
}) : transition.input;
|
|
1854
|
+
const resolvedContext = typeof transition.context === 'function' ? transition.context(transitionArgs) : transition.context;
|
|
2312
1855
|
return {
|
|
2313
1856
|
targets: transition.target,
|
|
2314
|
-
context:
|
|
1857
|
+
context: resolvedContext,
|
|
2315
1858
|
reenter: transition.reenter,
|
|
2316
1859
|
actions: undefined,
|
|
2317
1860
|
internalEvents: undefined,
|
|
@@ -2347,7 +1890,7 @@ function macrostep(snapshot, event, actorScope, internalQueue) {
|
|
|
2347
1890
|
// Determine the next state based on the next microstep
|
|
2348
1891
|
if (nextEvent.type !== XSTATE_INIT) {
|
|
2349
1892
|
const currentEvent = nextEvent;
|
|
2350
|
-
const isErr =
|
|
1893
|
+
const isErr = isErrorEvent(currentEvent);
|
|
2351
1894
|
const transitions = nextSnapshot.machine.getTransitionData(nextSnapshot, currentEvent, actorScope.self);
|
|
2352
1895
|
if (isErr && !transitions.length) {
|
|
2353
1896
|
// TODO: we should likely only allow transitions selected by very explicit descriptors
|
|
@@ -2418,31 +1961,13 @@ function resolveStateValue(rootNode, stateValue) {
|
|
|
2418
1961
|
const allStateNodes = getAllStateNodes(getStateNodes(rootNode, stateValue));
|
|
2419
1962
|
return getStateValue(rootNode, allStateNodes);
|
|
2420
1963
|
}
|
|
2421
|
-
function createEnqueueObject(props, action) {
|
|
2422
|
-
const enqueueFn = (fn, ...args) => {
|
|
2423
|
-
action(fn, ...args);
|
|
2424
|
-
};
|
|
2425
|
-
Object.assign(enqueueFn, {
|
|
2426
|
-
cancel: () => {},
|
|
2427
|
-
emit: () => {},
|
|
2428
|
-
log: () => {},
|
|
2429
|
-
raise: () => {},
|
|
2430
|
-
spawn: () => ({}),
|
|
2431
|
-
sendTo: () => {},
|
|
2432
|
-
stop: () => {},
|
|
2433
|
-
listen: () => ({}),
|
|
2434
|
-
subscribeTo: () => ({}),
|
|
2435
|
-
...props
|
|
2436
|
-
});
|
|
2437
|
-
return enqueueFn;
|
|
2438
|
-
}
|
|
2439
1964
|
function hasEffect(transition, context, event, snapshot, self) {
|
|
2440
1965
|
if (transition.to) {
|
|
2441
|
-
return transitionToHasEffect(transition.to, context, event, snapshot, self, snapshot.machine.implementations);
|
|
1966
|
+
return transitionToHasEffect(transition.to, context, event, snapshot, self, snapshot.machine.implementations, transition.source.id);
|
|
2442
1967
|
}
|
|
2443
1968
|
return false;
|
|
2444
1969
|
}
|
|
2445
|
-
function transitionToHasEffect(transitionTo, context, event, snapshot, self, implementations) {
|
|
1970
|
+
function transitionToHasEffect(transitionTo, context, event, snapshot, self, implementations, sourceId) {
|
|
2446
1971
|
let hasEffect = false;
|
|
2447
1972
|
let res;
|
|
2448
1973
|
try {
|
|
@@ -2453,16 +1978,19 @@ function transitionToHasEffect(transitionTo, context, event, snapshot, self, imp
|
|
|
2453
1978
|
res = transitionTo({
|
|
2454
1979
|
context,
|
|
2455
1980
|
event,
|
|
1981
|
+
output: getEventOutput(event),
|
|
2456
1982
|
self,
|
|
1983
|
+
system: self.system,
|
|
2457
1984
|
value: snapshot.value,
|
|
2458
1985
|
children: snapshot.children,
|
|
2459
1986
|
parent: {
|
|
2460
1987
|
send: triggerEffect
|
|
2461
1988
|
},
|
|
2462
1989
|
actions: implementations.actions,
|
|
2463
|
-
|
|
1990
|
+
actorSources: implementations.actorSources,
|
|
2464
1991
|
guards: implementations.guards,
|
|
2465
|
-
delays: implementations.delays
|
|
1992
|
+
delays: implementations.delays,
|
|
1993
|
+
input: getStateInput(snapshot, sourceId)
|
|
2466
1994
|
}, createEnqueueObject({
|
|
2467
1995
|
emit: triggerEffect,
|
|
2468
1996
|
cancel: triggerEffect,
|
|
@@ -2515,38 +2043,22 @@ function evaluateCandidate(candidate, event, snapshot, stateNode, self) {
|
|
|
2515
2043
|
const guardArgs = {
|
|
2516
2044
|
context: snapshot.context,
|
|
2517
2045
|
event,
|
|
2046
|
+
output: getEventOutput(event),
|
|
2518
2047
|
self,
|
|
2519
2048
|
parent: self._parent,
|
|
2520
2049
|
children: snapshot.children,
|
|
2521
2050
|
actions: stateNode.machine.implementations.actions,
|
|
2522
|
-
|
|
2051
|
+
actorSources: stateNode.machine.implementations.actorSources,
|
|
2523
2052
|
guards: stateNode.machine.implementations.guards,
|
|
2524
2053
|
delays: stateNode.machine.implementations.delays,
|
|
2525
2054
|
_snapshot: snapshot
|
|
2526
2055
|
};
|
|
2527
|
-
|
|
2528
|
-
const guardParams = typeof guardConfig?.params === 'function' ? guardConfig.params({
|
|
2529
|
-
context: snapshot.context,
|
|
2530
|
-
event
|
|
2531
|
-
}) : guardConfig?.params;
|
|
2532
|
-
let guardPassed = true;
|
|
2533
|
-
if (typeof guardConfig === 'function') {
|
|
2534
|
-
guardPassed = guardConfig(guardArgs, guardParams);
|
|
2535
|
-
} else if (typeof guardConfig?.type === 'string') {
|
|
2536
|
-
const guardImpl = stateNode.machine.implementations.guards[guardConfig.type];
|
|
2537
|
-
if (!guardImpl) {
|
|
2538
|
-
// A typo'd guard name must fail loudly — silently passing would
|
|
2539
|
-
// take the guarded transition and corrupt machine behavior.
|
|
2540
|
-
throw new Error(`Guard '${guardConfig.type}' is not implemented in machine '${stateNode.machine.id}'.`);
|
|
2541
|
-
}
|
|
2542
|
-
guardPassed = guardImpl(guardArgs, guardParams);
|
|
2543
|
-
}
|
|
2544
|
-
if (!guardPassed) {
|
|
2056
|
+
if (!candidate.guard(guardArgs)) {
|
|
2545
2057
|
return false;
|
|
2546
2058
|
}
|
|
2547
2059
|
}
|
|
2548
2060
|
if (candidate.to) {
|
|
2549
|
-
return transitionToHasEffect(candidate.to, snapshot.context, event, snapshot, self, stateNode.machine.implementations);
|
|
2061
|
+
return transitionToHasEffect(candidate.to, snapshot.context, event, snapshot, self, stateNode.machine.implementations, candidate.source.id);
|
|
2550
2062
|
}
|
|
2551
2063
|
return true;
|
|
2552
2064
|
}
|
|
@@ -2554,29 +2066,6 @@ function evaluateCandidate(candidate, event, snapshot, stateNode, self) {
|
|
|
2554
2066
|
function isMachineSnapshot(value) {
|
|
2555
2067
|
return !!value && typeof value === 'object' && 'machine' in value && 'value' in value;
|
|
2556
2068
|
}
|
|
2557
|
-
let emptyCanActor;
|
|
2558
|
-
let emptyCanActorScope;
|
|
2559
|
-
function getEmptyCanActor() {
|
|
2560
|
-
return emptyCanActor ??= createEmptyActor();
|
|
2561
|
-
}
|
|
2562
|
-
function getEmptyCanActorScope() {
|
|
2563
|
-
if (emptyCanActorScope) {
|
|
2564
|
-
return emptyCanActorScope;
|
|
2565
|
-
}
|
|
2566
|
-
const actor = getEmptyCanActor();
|
|
2567
|
-
emptyCanActorScope = {
|
|
2568
|
-
self: actor,
|
|
2569
|
-
logger: () => {},
|
|
2570
|
-
id: '',
|
|
2571
|
-
sessionId: '',
|
|
2572
|
-
defer: () => {},
|
|
2573
|
-
system: actor.system,
|
|
2574
|
-
stopChild: () => {},
|
|
2575
|
-
emit: () => {},
|
|
2576
|
-
actionExecutor: () => {}
|
|
2577
|
-
};
|
|
2578
|
-
return emptyCanActorScope;
|
|
2579
|
-
}
|
|
2580
2069
|
const machineSnapshotMatches = function matches(testValue) {
|
|
2581
2070
|
return matchesState(testValue, this.value);
|
|
2582
2071
|
};
|
|
@@ -2584,24 +2073,10 @@ const machineSnapshotHasTag = function hasTag(tag) {
|
|
|
2584
2073
|
return this.tags.has(tag);
|
|
2585
2074
|
};
|
|
2586
2075
|
const machineSnapshotCan = function can(event) {
|
|
2587
|
-
const emptyActor = getEmptyCanActor();
|
|
2588
|
-
const emptyActorScope = getEmptyCanActorScope();
|
|
2589
|
-
const transitionData = this.machine.getTransitionData(this, event, emptyActor);
|
|
2590
|
-
if (!transitionData?.length) {
|
|
2591
|
-
return false;
|
|
2592
|
-
}
|
|
2593
2076
|
|
|
2594
|
-
//
|
|
2595
|
-
|
|
2596
|
-
|
|
2597
|
-
return true;
|
|
2598
|
-
}
|
|
2599
|
-
const res = getTransitionResult(transition, this, event, emptyActorScope);
|
|
2600
|
-
if (res.targets?.length || res.context || hasEffect(transition, this.context, event, this, emptyActor)) {
|
|
2601
|
-
return true;
|
|
2602
|
-
}
|
|
2603
|
-
}
|
|
2604
|
-
return false;
|
|
2077
|
+
// The dry-run logic lives on the machine so that non-machine bundles
|
|
2078
|
+
// (e.g. `createFSM`) don't pay for the transition-resolution machinery.
|
|
2079
|
+
return this.machine?._canTransition(this, event) ?? false;
|
|
2605
2080
|
};
|
|
2606
2081
|
const machineSnapshotToJSON = function toJSON() {
|
|
2607
2082
|
const {
|
|
@@ -2729,7 +2204,7 @@ function getPersistedSnapshot(snapshot, options) {
|
|
|
2729
2204
|
childrenJson[id] = {
|
|
2730
2205
|
snapshot: child.getPersistedSnapshot(options),
|
|
2731
2206
|
src: child.src,
|
|
2732
|
-
|
|
2207
|
+
registryKey: child.registryKey,
|
|
2733
2208
|
syncSnapshot: child._syncSnapshot
|
|
2734
2209
|
};
|
|
2735
2210
|
}
|
|
@@ -2739,6 +2214,9 @@ function getPersistedSnapshot(snapshot, options) {
|
|
|
2739
2214
|
children: childrenJson,
|
|
2740
2215
|
historyValue: serializeHistoryValue(jsonValues.historyValue)
|
|
2741
2216
|
};
|
|
2217
|
+
if (_stateInputs && Object.keys(_stateInputs).length > 0) {
|
|
2218
|
+
persisted.stateInputs = _stateInputs;
|
|
2219
|
+
}
|
|
2742
2220
|
if (machine.version !== undefined) {
|
|
2743
2221
|
persisted.version = machine.version;
|
|
2744
2222
|
}
|
|
@@ -2874,21 +2352,35 @@ function toArray(value) {
|
|
|
2874
2352
|
}
|
|
2875
2353
|
return toArrayStrict(value);
|
|
2876
2354
|
}
|
|
2877
|
-
function resolveOutput(mapper, context, event, self) {
|
|
2355
|
+
function resolveOutput(mapper, context, event, self, input) {
|
|
2878
2356
|
if (typeof mapper === 'function') {
|
|
2879
|
-
|
|
2357
|
+
const outputMapper = mapper;
|
|
2358
|
+
const args = {
|
|
2880
2359
|
context,
|
|
2881
2360
|
event,
|
|
2882
|
-
|
|
2883
|
-
|
|
2361
|
+
output: getEventOutput(event),
|
|
2362
|
+
self,
|
|
2363
|
+
input
|
|
2364
|
+
};
|
|
2365
|
+
return outputMapper(args);
|
|
2884
2366
|
}
|
|
2885
2367
|
return mapper;
|
|
2886
2368
|
}
|
|
2369
|
+
function getEventOutput(event) {
|
|
2370
|
+
if (isDoneEvent(event)) {
|
|
2371
|
+
const doneEvent = event;
|
|
2372
|
+
return doneEvent.output;
|
|
2373
|
+
}
|
|
2374
|
+
return undefined;
|
|
2375
|
+
}
|
|
2376
|
+
function isDoneEvent(event) {
|
|
2377
|
+
return event.type.startsWith('xstate.done.actor.') || event.type.startsWith('xstate.done.state.');
|
|
2378
|
+
}
|
|
2887
2379
|
function isArray(value) {
|
|
2888
2380
|
return Array.isArray(value);
|
|
2889
2381
|
}
|
|
2890
|
-
function
|
|
2891
|
-
return event.type.startsWith('xstate.error.
|
|
2382
|
+
function isErrorEvent(event) {
|
|
2383
|
+
return event.type.startsWith('xstate.error.');
|
|
2892
2384
|
}
|
|
2893
2385
|
function toTransitionConfigArray(configLike) {
|
|
2894
2386
|
return toArrayStrict(configLike).map(transitionLike => {
|
|
@@ -2926,14 +2418,14 @@ function createInvokeId(stateNodeId, index) {
|
|
|
2926
2418
|
function resolveReferencedActor(machine, src) {
|
|
2927
2419
|
const match = src.match(/^xstate\.invoke\.(\d+)\.(.*)/);
|
|
2928
2420
|
if (!match) {
|
|
2929
|
-
return machine.implementations.
|
|
2421
|
+
return machine.implementations.actorSources[src];
|
|
2930
2422
|
}
|
|
2931
2423
|
const [, indexStr, nodeId] = match;
|
|
2932
2424
|
const node = machine.getStateNodeById(nodeId);
|
|
2933
2425
|
const invokeConfig = node.config.invoke;
|
|
2934
2426
|
const configSrc = (Array.isArray(invokeConfig) ? invokeConfig[indexStr] : invokeConfig).src;
|
|
2935
2427
|
// A referenced actor may itself be registered by name.
|
|
2936
|
-
return typeof configSrc === 'string' ? machine.implementations.
|
|
2428
|
+
return typeof configSrc === 'string' ? machine.implementations.actorSources[configSrc] : configSrc;
|
|
2937
2429
|
}
|
|
2938
2430
|
function getAllOwnEventDescriptors(snapshot) {
|
|
2939
2431
|
return [...new Set([...snapshot._nodes.flatMap(sn => sn.ownEvents)])];
|
|
@@ -2980,7 +2472,7 @@ function matchesEventDescriptor(eventType, descriptor) {
|
|
|
2980
2472
|
function createScheduledEventId(actor, id) {
|
|
2981
2473
|
return `${actor.sessionId}.${id}`;
|
|
2982
2474
|
}
|
|
2983
|
-
function
|
|
2475
|
+
function createRuntimeSystem(rootActor, options) {
|
|
2984
2476
|
let idCounter = 0;
|
|
2985
2477
|
const children = new Map();
|
|
2986
2478
|
const keyedActors = new Map();
|
|
@@ -3003,373 +2495,150 @@ function createSystem(rootActor, options) {
|
|
|
3003
2495
|
collected.push({
|
|
3004
2496
|
targetRef: target,
|
|
3005
2497
|
targetId: target.id,
|
|
3006
|
-
event,
|
|
3007
|
-
delay,
|
|
3008
|
-
id
|
|
3009
|
-
});
|
|
3010
|
-
};
|
|
3011
|
-
const scheduler = {
|
|
3012
|
-
schedule: (source, target, event, delay, id = Math.random().toString(36).slice(2)) => {
|
|
3013
|
-
recordSent(source, target, event, delay, id);
|
|
3014
|
-
const scheduledEvent = {
|
|
3015
|
-
source,
|
|
3016
|
-
target,
|
|
3017
|
-
event,
|
|
3018
|
-
delay,
|
|
3019
|
-
id,
|
|
3020
|
-
startedAt: Date.now()
|
|
3021
|
-
};
|
|
3022
|
-
const scheduledEventId = createScheduledEventId(source, id);
|
|
3023
|
-
system._snapshot._scheduledEvents[scheduledEventId] = scheduledEvent;
|
|
3024
|
-
const timeout = clock.setTimeout(() => {
|
|
3025
|
-
delete timerMap[scheduledEventId];
|
|
3026
|
-
delete system._snapshot._scheduledEvents[scheduledEventId];
|
|
3027
|
-
|
|
3028
|
-
// The send was already recorded at schedule time on the sender's
|
|
3029
|
-
// transition, so deliver without re-recording.
|
|
3030
|
-
deliver(source, target, event);
|
|
3031
|
-
}, delay);
|
|
3032
|
-
timerMap[scheduledEventId] = timeout;
|
|
3033
|
-
},
|
|
3034
|
-
cancel: (source, id) => {
|
|
3035
|
-
const scheduledEventId = createScheduledEventId(source, id);
|
|
3036
|
-
const timeout = timerMap[scheduledEventId];
|
|
3037
|
-
delete timerMap[scheduledEventId];
|
|
3038
|
-
delete system._snapshot._scheduledEvents[scheduledEventId];
|
|
3039
|
-
if (timeout !== undefined) {
|
|
3040
|
-
clock.clearTimeout(timeout);
|
|
3041
|
-
}
|
|
3042
|
-
},
|
|
3043
|
-
cancelAll: actor => {
|
|
3044
|
-
for (const scheduledEventId in system._snapshot._scheduledEvents) {
|
|
3045
|
-
const scheduledEvent = system._snapshot._scheduledEvents[scheduledEventId];
|
|
3046
|
-
if (scheduledEvent.source === actor) {
|
|
3047
|
-
scheduler.cancel(actor, scheduledEvent.id);
|
|
3048
|
-
}
|
|
3049
|
-
}
|
|
3050
|
-
}
|
|
3051
|
-
};
|
|
3052
|
-
// Delivers an event to the target actor. Used by both `_relay` (which also
|
|
3053
|
-
// records the send) and the scheduler's timer (which already recorded it).
|
|
3054
|
-
const deliver = (source, target, event) => {
|
|
3055
|
-
const targetMachine = target.logic;
|
|
3056
|
-
const isInternalEvent = typeof targetMachine?.isInternalEventType === 'function' && targetMachine.isInternalEventType(event.type);
|
|
3057
|
-
if (isInternalEvent && source !== target) {
|
|
3058
|
-
throw new Error(`Internal event "${event.type}" cannot be sent to actor "${target.id}" from outside.`);
|
|
3059
|
-
}
|
|
3060
|
-
|
|
3061
|
-
// remember the last source for unified transition inspect event
|
|
3062
|
-
target._lastSourceRef = source;
|
|
3063
|
-
target._send(event);
|
|
3064
|
-
};
|
|
3065
|
-
const sendInspectionEvent = event => {
|
|
3066
|
-
if (!inspectionObservers.size) {
|
|
3067
|
-
return;
|
|
3068
|
-
}
|
|
3069
|
-
const resolvedInspectionEvent = {
|
|
3070
|
-
...event,
|
|
3071
|
-
rootId: rootActor.sessionId
|
|
3072
|
-
};
|
|
3073
|
-
inspectionObservers.forEach(observer => observer.next?.(resolvedInspectionEvent));
|
|
3074
|
-
};
|
|
3075
|
-
const system = {
|
|
3076
|
-
children,
|
|
3077
|
-
reverseKeyedActors,
|
|
3078
|
-
keyedActors,
|
|
3079
|
-
_snapshot: {
|
|
3080
|
-
_scheduledEvents: (options?.snapshot && options.snapshot.scheduler) ?? {}
|
|
3081
|
-
},
|
|
3082
|
-
_bookId: () => `x:${idCounter++}`,
|
|
3083
|
-
_register: (sessionId, actor) => {
|
|
3084
|
-
children.set(sessionId, actor);
|
|
3085
|
-
return sessionId;
|
|
3086
|
-
},
|
|
3087
|
-
_unregister: actor => {
|
|
3088
|
-
children.delete(actor.sessionId);
|
|
3089
|
-
const systemId = reverseKeyedActors.get(actor);
|
|
3090
|
-
if (systemId !== undefined) {
|
|
3091
|
-
keyedActors.delete(systemId);
|
|
3092
|
-
reverseKeyedActors.delete(actor);
|
|
3093
|
-
}
|
|
3094
|
-
},
|
|
3095
|
-
get: systemId => {
|
|
3096
|
-
return keyedActors.get(systemId);
|
|
3097
|
-
},
|
|
3098
|
-
getAll: () => {
|
|
3099
|
-
return Object.fromEntries(keyedActors.entries());
|
|
3100
|
-
},
|
|
3101
|
-
_set: (systemId, actor) => {
|
|
3102
|
-
const existing = keyedActors.get(systemId);
|
|
3103
|
-
if (existing && existing !== actor) {
|
|
3104
|
-
throw new Error(`Actor with system ID '${systemId}' already exists.`);
|
|
3105
|
-
}
|
|
3106
|
-
keyedActors.set(systemId, actor);
|
|
3107
|
-
reverseKeyedActors.set(actor, systemId);
|
|
3108
|
-
},
|
|
3109
|
-
inspect: observerOrFn => {
|
|
3110
|
-
const observer = toObserver(observerOrFn);
|
|
3111
|
-
inspectionObservers.add(observer);
|
|
3112
|
-
return {
|
|
3113
|
-
unsubscribe() {
|
|
3114
|
-
inspectionObservers.delete(observer);
|
|
3115
|
-
}
|
|
3116
|
-
};
|
|
3117
|
-
},
|
|
3118
|
-
_sendInspectionEvent: sendInspectionEvent,
|
|
3119
|
-
_relay: (source, target, event) => {
|
|
3120
|
-
recordSent(source, target, event);
|
|
3121
|
-
deliver(source, target, event);
|
|
3122
|
-
},
|
|
3123
|
-
scheduler,
|
|
3124
|
-
getSnapshot: () => {
|
|
3125
|
-
return {
|
|
3126
|
-
_scheduledEvents: {
|
|
3127
|
-
...system._snapshot._scheduledEvents
|
|
3128
|
-
}
|
|
3129
|
-
};
|
|
3130
|
-
},
|
|
3131
|
-
start: () => {
|
|
3132
|
-
const scheduledEvents = system._snapshot._scheduledEvents;
|
|
3133
|
-
system._snapshot._scheduledEvents = {};
|
|
3134
|
-
for (const scheduledId in scheduledEvents) {
|
|
3135
|
-
const {
|
|
3136
|
-
source,
|
|
3137
|
-
target,
|
|
3138
|
-
event,
|
|
3139
|
-
delay,
|
|
3140
|
-
id
|
|
3141
|
-
} = scheduledEvents[scheduledId];
|
|
3142
|
-
scheduler.schedule(source, target, event, delay, id);
|
|
3143
|
-
}
|
|
3144
|
-
},
|
|
3145
|
-
_clock: clock,
|
|
3146
|
-
_logger: logger,
|
|
3147
|
-
_timerStrategy: options.timers
|
|
3148
|
-
};
|
|
3149
|
-
return system;
|
|
3150
|
-
}
|
|
3151
|
-
|
|
3152
|
-
const effectStates = new WeakMap();
|
|
3153
|
-
const XSTATE_LOGIC_EFFECT_RESOLVE = 'xstate.logic.effect.resolve';
|
|
3154
|
-
const XSTATE_LOGIC_EFFECT_REJECT = 'xstate.logic.effect.reject';
|
|
3155
|
-
const XSTATE_LOGIC_EFFECT_START = 'xstate.logic.effect.start';
|
|
3156
|
-
function getEffectState(self) {
|
|
3157
|
-
let state = effectStates.get(self);
|
|
3158
|
-
if (!state) {
|
|
3159
|
-
state = new Map();
|
|
3160
|
-
effectStates.set(self, state);
|
|
3161
|
-
}
|
|
3162
|
-
return state;
|
|
3163
|
-
}
|
|
3164
|
-
function executeLogicEffects(effects, actorScope) {
|
|
3165
|
-
if (!effects?.length) {
|
|
3166
|
-
return;
|
|
3167
|
-
}
|
|
3168
|
-
for (const effect of effects) {
|
|
3169
|
-
switch (effect.type) {
|
|
3170
|
-
case 'emit':
|
|
3171
|
-
actorScope.emit(effect.event);
|
|
3172
|
-
break;
|
|
3173
|
-
case 'sendBack':
|
|
3174
|
-
{
|
|
3175
|
-
const parent = actorScope.self._parent;
|
|
3176
|
-
if (parent) {
|
|
3177
|
-
actorScope.system._relay(actorScope.self, parent, effect.event);
|
|
3178
|
-
}
|
|
3179
|
-
break;
|
|
3180
|
-
}
|
|
3181
|
-
case 'raise':
|
|
3182
|
-
actorScope.system._relay(actorScope.self, actorScope.self, effect.event);
|
|
3183
|
-
break;
|
|
3184
|
-
case 'effect':
|
|
3185
|
-
{
|
|
3186
|
-
if (!effect.key) {
|
|
3187
|
-
const cleanup = effect.exec();
|
|
3188
|
-
if (typeof cleanup === 'function') {
|
|
3189
|
-
getEffectState(actorScope.self).set(Symbol(), {
|
|
3190
|
-
cleanup
|
|
3191
|
-
});
|
|
3192
|
-
}
|
|
3193
|
-
break;
|
|
3194
|
-
}
|
|
3195
|
-
const state = getEffectState(actorScope.self);
|
|
3196
|
-
if (state.has(effect.key)) {
|
|
3197
|
-
break;
|
|
3198
|
-
}
|
|
3199
|
-
const cleanup = effect.exec();
|
|
3200
|
-
state.set(effect.key, {
|
|
3201
|
-
cleanup: typeof cleanup === 'function' ? cleanup : undefined
|
|
3202
|
-
});
|
|
3203
|
-
break;
|
|
3204
|
-
}
|
|
3205
|
-
case 'cleanupEffects':
|
|
3206
|
-
{
|
|
3207
|
-
const state = effectStates.get(actorScope.self);
|
|
3208
|
-
if (!state) {
|
|
3209
|
-
break;
|
|
3210
|
-
}
|
|
3211
|
-
for (const {
|
|
3212
|
-
cleanup
|
|
3213
|
-
} of state.values()) {
|
|
3214
|
-
cleanup?.();
|
|
3215
|
-
}
|
|
3216
|
-
effectStates.delete(actorScope.self);
|
|
3217
|
-
break;
|
|
3218
|
-
}
|
|
3219
|
-
}
|
|
3220
|
-
}
|
|
3221
|
-
}
|
|
3222
|
-
function resolveContext(context, input) {
|
|
3223
|
-
return typeof context === 'function' ? context({
|
|
3224
|
-
input
|
|
3225
|
-
}) : context;
|
|
3226
|
-
}
|
|
3227
|
-
function createLogic(config) {
|
|
3228
|
-
const transition = (snapshot, event, actorScope) => {
|
|
3229
|
-
if (snapshot.status !== 'active') {
|
|
3230
|
-
return [snapshot, []];
|
|
3231
|
-
}
|
|
3232
|
-
if (event.type === XSTATE_STOP) {
|
|
3233
|
-
return [{
|
|
3234
|
-
...snapshot,
|
|
3235
|
-
status: 'stopped',
|
|
3236
|
-
input: undefined
|
|
3237
|
-
}, [{
|
|
3238
|
-
type: 'cleanupEffects'
|
|
3239
|
-
}]];
|
|
3240
|
-
}
|
|
3241
|
-
if (event.type === XSTATE_LOGIC_EFFECT_START) {
|
|
3242
|
-
return [{
|
|
3243
|
-
...snapshot,
|
|
3244
|
-
effects: {
|
|
3245
|
-
...snapshot.effects,
|
|
3246
|
-
[event.key]: {
|
|
3247
|
-
status: 'active'
|
|
3248
|
-
}
|
|
3249
|
-
}
|
|
3250
|
-
}, []];
|
|
3251
|
-
}
|
|
3252
|
-
if (event.type === XSTATE_LOGIC_EFFECT_RESOLVE) {
|
|
3253
|
-
return [{
|
|
3254
|
-
...snapshot,
|
|
3255
|
-
effects: {
|
|
3256
|
-
...snapshot.effects,
|
|
3257
|
-
[event.key]: {
|
|
3258
|
-
status: 'done',
|
|
3259
|
-
output: event.output
|
|
3260
|
-
}
|
|
3261
|
-
}
|
|
3262
|
-
}, []];
|
|
3263
|
-
}
|
|
3264
|
-
if (event.type === XSTATE_LOGIC_EFFECT_REJECT) {
|
|
3265
|
-
return [{
|
|
3266
|
-
...snapshot,
|
|
3267
|
-
effects: {
|
|
3268
|
-
...snapshot.effects,
|
|
3269
|
-
[event.key]: {
|
|
3270
|
-
status: 'error',
|
|
3271
|
-
error: event.error
|
|
3272
|
-
}
|
|
3273
|
-
}
|
|
3274
|
-
}, []];
|
|
3275
|
-
}
|
|
3276
|
-
const effects = [];
|
|
3277
|
-
const trackedEffects = {};
|
|
3278
|
-
const enqueueEffect = (key, exec) => {
|
|
3279
|
-
if (snapshot.effects?.[key]) {
|
|
3280
|
-
return;
|
|
3281
|
-
}
|
|
3282
|
-
effects.push({
|
|
3283
|
-
type: 'effect',
|
|
3284
|
-
key,
|
|
3285
|
-
exec
|
|
3286
|
-
});
|
|
3287
|
-
trackedEffects[key] = {
|
|
3288
|
-
status: 'active'
|
|
2498
|
+
event,
|
|
2499
|
+
delay,
|
|
2500
|
+
id
|
|
2501
|
+
});
|
|
2502
|
+
};
|
|
2503
|
+
const scheduler = {
|
|
2504
|
+
schedule: (source, target, event, delay, id = Math.random().toString(36).slice(2)) => {
|
|
2505
|
+
recordSent(source, target, event, delay, id);
|
|
2506
|
+
const scheduledEvent = {
|
|
2507
|
+
source,
|
|
2508
|
+
target,
|
|
2509
|
+
event,
|
|
2510
|
+
delay,
|
|
2511
|
+
id,
|
|
2512
|
+
startedAt: Date.now()
|
|
3289
2513
|
};
|
|
3290
|
-
|
|
3291
|
-
|
|
3292
|
-
|
|
3293
|
-
|
|
3294
|
-
|
|
3295
|
-
|
|
3296
|
-
|
|
3297
|
-
|
|
3298
|
-
|
|
3299
|
-
|
|
3300
|
-
|
|
3301
|
-
|
|
3302
|
-
|
|
3303
|
-
|
|
3304
|
-
|
|
3305
|
-
|
|
3306
|
-
|
|
3307
|
-
|
|
3308
|
-
|
|
3309
|
-
}
|
|
3310
|
-
|
|
3311
|
-
|
|
3312
|
-
|
|
3313
|
-
|
|
2514
|
+
const scheduledEventId = createScheduledEventId(source, id);
|
|
2515
|
+
system._snapshot._scheduledEvents[scheduledEventId] = scheduledEvent;
|
|
2516
|
+
const timeout = clock.setTimeout(() => {
|
|
2517
|
+
delete timerMap[scheduledEventId];
|
|
2518
|
+
delete system._snapshot._scheduledEvents[scheduledEventId];
|
|
2519
|
+
|
|
2520
|
+
// The send was already recorded at schedule time on the sender's
|
|
2521
|
+
// transition, so deliver without re-recording.
|
|
2522
|
+
deliver(source, target, event);
|
|
2523
|
+
}, delay);
|
|
2524
|
+
timerMap[scheduledEventId] = timeout;
|
|
2525
|
+
},
|
|
2526
|
+
cancel: (source, id) => {
|
|
2527
|
+
const scheduledEventId = createScheduledEventId(source, id);
|
|
2528
|
+
const timeout = timerMap[scheduledEventId];
|
|
2529
|
+
delete timerMap[scheduledEventId];
|
|
2530
|
+
delete system._snapshot._scheduledEvents[scheduledEventId];
|
|
2531
|
+
if (timeout !== undefined) {
|
|
2532
|
+
clock.clearTimeout(timeout);
|
|
2533
|
+
}
|
|
2534
|
+
},
|
|
2535
|
+
cancelAll: actor => {
|
|
2536
|
+
for (const scheduledEventId in system._snapshot._scheduledEvents) {
|
|
2537
|
+
const scheduledEvent = system._snapshot._scheduledEvents[scheduledEventId];
|
|
2538
|
+
if (scheduledEvent.source === actor) {
|
|
2539
|
+
scheduler.cancel(actor, scheduledEvent.id);
|
|
3314
2540
|
}
|
|
3315
|
-
effects.push({
|
|
3316
|
-
type: 'effect',
|
|
3317
|
-
exec: keyOrExec
|
|
3318
|
-
});
|
|
3319
2541
|
}
|
|
3320
|
-
};
|
|
3321
|
-
const patch = config.run({
|
|
3322
|
-
context: snapshot.context,
|
|
3323
|
-
event,
|
|
3324
|
-
input: snapshot.input,
|
|
3325
|
-
system: actorScope.system,
|
|
3326
|
-
self: actorScope.self,
|
|
3327
|
-
emit: actorScope.emit
|
|
3328
|
-
}, enq);
|
|
3329
|
-
const nextSnapshot = {
|
|
3330
|
-
...snapshot,
|
|
3331
|
-
...(patch || {})
|
|
3332
|
-
};
|
|
3333
|
-
if ('context' in snapshot || patch?.context !== undefined) {
|
|
3334
|
-
nextSnapshot.context = patch?.context ?? snapshot.context;
|
|
3335
2542
|
}
|
|
3336
|
-
|
|
3337
|
-
|
|
3338
|
-
|
|
3339
|
-
|
|
3340
|
-
|
|
3341
|
-
|
|
2543
|
+
};
|
|
2544
|
+
// Delivers an event to the target actor. Used by both `_relay` (which also
|
|
2545
|
+
// records the send) and the scheduler's timer (which already recorded it).
|
|
2546
|
+
const deliver = (source, target, event) => {
|
|
2547
|
+
const targetMachine = target.logic;
|
|
2548
|
+
const isInternalEvent = typeof targetMachine?.isInternalEventType === 'function' && targetMachine.isInternalEventType(event.type);
|
|
2549
|
+
if (isInternalEvent && source !== target) {
|
|
2550
|
+
throw new Error(`Internal event "${event.type}" cannot be sent to actor "${target.id}" from outside.`);
|
|
3342
2551
|
}
|
|
3343
|
-
|
|
2552
|
+
|
|
2553
|
+
// remember the last source for unified transition inspect event
|
|
2554
|
+
target._lastSourceRef = source;
|
|
2555
|
+
target._send(event);
|
|
3344
2556
|
};
|
|
3345
|
-
const
|
|
3346
|
-
|
|
3347
|
-
|
|
3348
|
-
|
|
3349
|
-
|
|
3350
|
-
|
|
3351
|
-
|
|
3352
|
-
|
|
2557
|
+
const sendInspectionEvent = event => {
|
|
2558
|
+
if (!inspectionObservers.size) {
|
|
2559
|
+
return;
|
|
2560
|
+
}
|
|
2561
|
+
const resolvedInspectionEvent = {
|
|
2562
|
+
...event,
|
|
2563
|
+
rootId: rootActor.sessionId
|
|
2564
|
+
};
|
|
2565
|
+
inspectionObservers.forEach(observer => observer.next?.(resolvedInspectionEvent));
|
|
2566
|
+
};
|
|
2567
|
+
const system = {
|
|
2568
|
+
children,
|
|
2569
|
+
reverseKeyedActors,
|
|
2570
|
+
keyedActors,
|
|
2571
|
+
_snapshot: {
|
|
2572
|
+
_scheduledEvents: (options?.snapshot && options.snapshot.scheduler) ?? {}
|
|
3353
2573
|
},
|
|
3354
|
-
|
|
3355
|
-
|
|
3356
|
-
|
|
3357
|
-
|
|
3358
|
-
|
|
3359
|
-
|
|
3360
|
-
|
|
3361
|
-
|
|
3362
|
-
if (
|
|
3363
|
-
|
|
2574
|
+
_bookId: () => `x:${idCounter++}`,
|
|
2575
|
+
_register: (sessionId, actor) => {
|
|
2576
|
+
children.set(sessionId, actor);
|
|
2577
|
+
return sessionId;
|
|
2578
|
+
},
|
|
2579
|
+
_unregister: actor => {
|
|
2580
|
+
children.delete(actor.sessionId);
|
|
2581
|
+
const registryKey = reverseKeyedActors.get(actor);
|
|
2582
|
+
if (registryKey !== undefined) {
|
|
2583
|
+
keyedActors.delete(registryKey);
|
|
2584
|
+
reverseKeyedActors.delete(actor);
|
|
3364
2585
|
}
|
|
2586
|
+
},
|
|
2587
|
+
get: registryKey => {
|
|
2588
|
+
return keyedActors.get(registryKey);
|
|
2589
|
+
},
|
|
2590
|
+
getAll: () => {
|
|
2591
|
+
return Object.fromEntries(keyedActors.entries());
|
|
2592
|
+
},
|
|
2593
|
+
_set: (registryKey, actor) => {
|
|
2594
|
+
const existing = keyedActors.get(registryKey);
|
|
2595
|
+
if (existing && existing !== actor) {
|
|
2596
|
+
throw new Error(`Actor with registry key '${registryKey}' already exists.`);
|
|
2597
|
+
}
|
|
2598
|
+
keyedActors.set(registryKey, actor);
|
|
2599
|
+
reverseKeyedActors.set(actor, registryKey);
|
|
2600
|
+
},
|
|
2601
|
+
inspect: observerOrFn => {
|
|
2602
|
+
const observer = toObserver(observerOrFn);
|
|
2603
|
+
inspectionObservers.add(observer);
|
|
3365
2604
|
return {
|
|
3366
|
-
|
|
2605
|
+
unsubscribe() {
|
|
2606
|
+
inspectionObservers.delete(observer);
|
|
2607
|
+
}
|
|
3367
2608
|
};
|
|
3368
2609
|
},
|
|
3369
|
-
|
|
3370
|
-
|
|
2610
|
+
_sendInspectionEvent: sendInspectionEvent,
|
|
2611
|
+
_relay: (source, target, event) => {
|
|
2612
|
+
recordSent(source, target, event);
|
|
2613
|
+
deliver(source, target, event);
|
|
2614
|
+
},
|
|
2615
|
+
scheduler,
|
|
2616
|
+
getSnapshot: () => {
|
|
2617
|
+
return {
|
|
2618
|
+
_scheduledEvents: {
|
|
2619
|
+
...system._snapshot._scheduledEvents
|
|
2620
|
+
}
|
|
2621
|
+
};
|
|
2622
|
+
},
|
|
2623
|
+
start: () => {
|
|
2624
|
+
const scheduledEvents = system._snapshot._scheduledEvents;
|
|
2625
|
+
system._snapshot._scheduledEvents = {};
|
|
2626
|
+
for (const scheduledId in scheduledEvents) {
|
|
2627
|
+
const {
|
|
2628
|
+
source,
|
|
2629
|
+
target,
|
|
2630
|
+
event,
|
|
2631
|
+
delay,
|
|
2632
|
+
id
|
|
2633
|
+
} = scheduledEvents[scheduledId];
|
|
2634
|
+
scheduler.schedule(source, target, event, delay, id);
|
|
2635
|
+
}
|
|
2636
|
+
},
|
|
2637
|
+
_clock: clock,
|
|
2638
|
+
_logger: logger,
|
|
2639
|
+
_timerStrategy: options.timers
|
|
3371
2640
|
};
|
|
3372
|
-
return
|
|
2641
|
+
return system;
|
|
3373
2642
|
}
|
|
3374
2643
|
|
|
3375
2644
|
// those are needed to make JSDoc `@link` work properly
|
|
@@ -3395,6 +2664,30 @@ const defaultOptions = {
|
|
|
3395
2664
|
},
|
|
3396
2665
|
logger: console.log.bind(console)
|
|
3397
2666
|
};
|
|
2667
|
+
function safeCall(fn, arg) {
|
|
2668
|
+
try {
|
|
2669
|
+
fn?.(arg);
|
|
2670
|
+
} catch (err) {
|
|
2671
|
+
reportUnhandledError(err);
|
|
2672
|
+
}
|
|
2673
|
+
}
|
|
2674
|
+
function isExecutableActionObject(effect) {
|
|
2675
|
+
return typeof effect === 'object' && effect !== null && 'args' in effect && 'exec' in effect;
|
|
2676
|
+
}
|
|
2677
|
+
function executeExecutableEffects(effects, actorScope) {
|
|
2678
|
+
if (!effects?.length) {
|
|
2679
|
+
return [];
|
|
2680
|
+
}
|
|
2681
|
+
const logicEffects = [];
|
|
2682
|
+
for (const effect of effects) {
|
|
2683
|
+
if (isExecutableActionObject(effect)) {
|
|
2684
|
+
actorScope.actionExecutor(effect);
|
|
2685
|
+
} else {
|
|
2686
|
+
logicEffects.push(effect);
|
|
2687
|
+
}
|
|
2688
|
+
}
|
|
2689
|
+
return logicEffects;
|
|
2690
|
+
}
|
|
3398
2691
|
|
|
3399
2692
|
/**
|
|
3400
2693
|
* An Actor is a running process that can receive events, send events and change
|
|
@@ -3433,6 +2726,7 @@ class Actor {
|
|
|
3433
2726
|
this.logger = void 0;
|
|
3434
2727
|
/** @internal */
|
|
3435
2728
|
this._processingStatus = ProcessingStatus.NotStarted;
|
|
2729
|
+
this._forceDeferredActions = false;
|
|
3436
2730
|
// Actor Ref
|
|
3437
2731
|
this._parent = void 0;
|
|
3438
2732
|
/** @internal */
|
|
@@ -3448,7 +2742,8 @@ class Actor {
|
|
|
3448
2742
|
this._collectedActions = [];
|
|
3449
2743
|
/** @internal Events relayed to other actors during the in-flight transition. */
|
|
3450
2744
|
this._collectedSent = [];
|
|
3451
|
-
this.
|
|
2745
|
+
this._initialEffects = void 0;
|
|
2746
|
+
this.registryKey = void 0;
|
|
3452
2747
|
/** The globally unique process ID for this invocation. */
|
|
3453
2748
|
this.sessionId = void 0;
|
|
3454
2749
|
/** The system to which this actor belongs. */
|
|
@@ -3469,14 +2764,17 @@ class Actor {
|
|
|
3469
2764
|
parent,
|
|
3470
2765
|
syncSnapshot,
|
|
3471
2766
|
id,
|
|
3472
|
-
|
|
2767
|
+
registryKey,
|
|
3473
2768
|
inspect
|
|
3474
2769
|
} = resolvedOptions;
|
|
3475
|
-
this.system = parent ? parent.system :
|
|
2770
|
+
this.system = parent ? parent.system : resolvedOptions._systemRef?.current ?? createRuntimeSystem(this, {
|
|
3476
2771
|
clock,
|
|
3477
2772
|
logger,
|
|
3478
2773
|
timers: resolvedOptions.timers
|
|
3479
2774
|
});
|
|
2775
|
+
if (!parent && resolvedOptions._systemRef && !resolvedOptions._systemRef.current) {
|
|
2776
|
+
resolvedOptions._systemRef.current = this.system;
|
|
2777
|
+
}
|
|
3480
2778
|
if (inspect && !parent) {
|
|
3481
2779
|
// Always inspect at the system-level
|
|
3482
2780
|
this.system.inspect(toObserver(inspect));
|
|
@@ -3501,35 +2799,18 @@ class Actor {
|
|
|
3501
2799
|
system: this.system,
|
|
3502
2800
|
stopChild: child => {
|
|
3503
2801
|
if (child._parent !== this) {
|
|
3504
|
-
throw new Error(`Cannot stop child actor ${child.id}
|
|
2802
|
+
throw new Error(`Cannot stop non-child actor ${child.id}`);
|
|
3505
2803
|
}
|
|
3506
2804
|
child._stop();
|
|
3507
2805
|
},
|
|
3508
2806
|
emit: emittedEvent => {
|
|
3509
|
-
const listeners
|
|
3510
|
-
|
|
3511
|
-
|
|
3512
|
-
|
|
3513
|
-
}
|
|
3514
|
-
if (listeners) {
|
|
3515
|
-
for (const handler of listeners) {
|
|
3516
|
-
try {
|
|
3517
|
-
handler(emittedEvent);
|
|
3518
|
-
} catch (err) {
|
|
3519
|
-
reportUnhandledError(err);
|
|
2807
|
+
for (const listeners of [this.eventListeners.get(emittedEvent.type), this.eventListeners.get('*')]) {
|
|
2808
|
+
if (listeners) {
|
|
2809
|
+
for (const handler of listeners) {
|
|
2810
|
+
safeCall(handler, emittedEvent);
|
|
3520
2811
|
}
|
|
3521
2812
|
}
|
|
3522
2813
|
}
|
|
3523
|
-
if (!wildcardListener) {
|
|
3524
|
-
return;
|
|
3525
|
-
}
|
|
3526
|
-
for (const handler of wildcardListener) {
|
|
3527
|
-
try {
|
|
3528
|
-
handler(emittedEvent);
|
|
3529
|
-
} catch (err) {
|
|
3530
|
-
reportUnhandledError(err);
|
|
3531
|
-
}
|
|
3532
|
-
}
|
|
3533
2814
|
},
|
|
3534
2815
|
actionExecutor: action => {
|
|
3535
2816
|
const exec = () => {
|
|
@@ -3550,7 +2831,7 @@ class Actor {
|
|
|
3550
2831
|
executingCustomAction = saveExecutingCustomAction;
|
|
3551
2832
|
}
|
|
3552
2833
|
};
|
|
3553
|
-
if (this._processingStatus === ProcessingStatus.Running) {
|
|
2834
|
+
if (this._processingStatus === ProcessingStatus.Running && !this._forceDeferredActions) {
|
|
3554
2835
|
exec();
|
|
3555
2836
|
} else {
|
|
3556
2837
|
this._deferred.push(exec);
|
|
@@ -3571,15 +2852,12 @@ class Actor {
|
|
|
3571
2852
|
};
|
|
3572
2853
|
}
|
|
3573
2854
|
});
|
|
3574
|
-
|
|
3575
|
-
|
|
3576
|
-
|
|
3577
|
-
if (systemId) {
|
|
3578
|
-
this.systemId = systemId;
|
|
3579
|
-
this.system._set(systemId, this);
|
|
2855
|
+
if (registryKey) {
|
|
2856
|
+
this.registryKey = registryKey;
|
|
2857
|
+
this.system._set(registryKey, this);
|
|
3580
2858
|
}
|
|
3581
2859
|
|
|
3582
|
-
// prepare to collect initial microsteps during
|
|
2860
|
+
// prepare to collect initial microsteps during initialTransition
|
|
3583
2861
|
this._collectedMicrosteps = [];
|
|
3584
2862
|
let persistedState = options?.snapshot ?? options?.state;
|
|
3585
2863
|
if (persistedState && typeof persistedState === 'object' && '_pendingEffects' in persistedState) {
|
|
@@ -3593,7 +2871,17 @@ class Actor {
|
|
|
3593
2871
|
persistedState = rest;
|
|
3594
2872
|
}
|
|
3595
2873
|
try {
|
|
3596
|
-
|
|
2874
|
+
if (persistedState) {
|
|
2875
|
+
this._snapshot = this.logic.restoreSnapshot ? this.logic.restoreSnapshot(persistedState, this._actorScope) : persistedState;
|
|
2876
|
+
} else if (options?._inert) {
|
|
2877
|
+
// Inert actors (createInertActorScope) only anchor a scope for pure
|
|
2878
|
+
// transition functions; computing an initial snapshot here would run
|
|
2879
|
+
// init-time side effects (context factories, entry) a second time.
|
|
2880
|
+
} else {
|
|
2881
|
+
const [snapshot, effects] = this.logic.initialTransition(this.options?.input, this._actorScope);
|
|
2882
|
+
this._snapshot = snapshot;
|
|
2883
|
+
this._initialEffects = effects;
|
|
2884
|
+
}
|
|
3597
2885
|
} catch (err) {
|
|
3598
2886
|
// if we get here then it means that we assign a value to this._snapshot that is not of the correct type
|
|
3599
2887
|
// we can't get the true `TSnapshot & { status: 'error'; }`, it's impossible
|
|
@@ -3607,7 +2895,7 @@ class Actor {
|
|
|
3607
2895
|
// computation so they can't run against an inconsistent actor
|
|
3608
2896
|
this._deferred.length = 0;
|
|
3609
2897
|
}
|
|
3610
|
-
if (
|
|
2898
|
+
if (registryKey && this._snapshot.status !== 'active') {
|
|
3611
2899
|
this.system._unregister(this);
|
|
3612
2900
|
}
|
|
3613
2901
|
|
|
@@ -3630,13 +2918,35 @@ class Actor {
|
|
|
3630
2918
|
error: err
|
|
3631
2919
|
};
|
|
3632
2920
|
}
|
|
2921
|
+
|
|
2922
|
+
/** Recover via the logic's error event if possible; otherwise error out. */
|
|
2923
|
+
_recoverOrError(err, snapshot) {
|
|
2924
|
+
if (!this._tryHandleExecutionError(err, snapshot)) {
|
|
2925
|
+
this._setErrorSnapshot(err);
|
|
2926
|
+
this._error(err);
|
|
2927
|
+
}
|
|
2928
|
+
}
|
|
2929
|
+
_tryHandleExecutionError(err, snapshot = this._snapshot) {
|
|
2930
|
+
// Machine logic can recover from execution errors via `onError`
|
|
2931
|
+
// transitions; the logic decides (so the actor stays logic-agnostic).
|
|
2932
|
+
const errorEvent = this.logic.getExecutionErrorEvent?.(snapshot, err);
|
|
2933
|
+
if (!errorEvent) {
|
|
2934
|
+
return false;
|
|
2935
|
+
}
|
|
2936
|
+
try {
|
|
2937
|
+
const [nextSnapshot, effects] = this.logic.transition(snapshot, errorEvent, this._actorScope);
|
|
2938
|
+
this._snapshot = nextSnapshot;
|
|
2939
|
+
const logicEffects = executeExecutableEffects(effects, this._actorScope);
|
|
2940
|
+
this.update(nextSnapshot, errorEvent);
|
|
2941
|
+
this.logic.executeEffects?.(logicEffects, this._actorScope);
|
|
2942
|
+
return true;
|
|
2943
|
+
} catch {
|
|
2944
|
+
return false;
|
|
2945
|
+
}
|
|
2946
|
+
}
|
|
3633
2947
|
_next(snapshot) {
|
|
3634
2948
|
for (const observer of this.observers) {
|
|
3635
|
-
|
|
3636
|
-
observer.next?.(snapshot);
|
|
3637
|
-
} catch (err) {
|
|
3638
|
-
reportUnhandledError(err);
|
|
3639
|
-
}
|
|
2949
|
+
safeCall(observer.next, snapshot);
|
|
3640
2950
|
}
|
|
3641
2951
|
}
|
|
3642
2952
|
update(snapshot, event) {
|
|
@@ -3655,6 +2965,9 @@ class Actor {
|
|
|
3655
2965
|
// no "builtin deferred" should actually throw an error since they are either safe
|
|
3656
2966
|
// or the control flow is passed through the mailbox and errors should be caught by the `_process` used by the mailbox
|
|
3657
2967
|
this._deferred.length = 0;
|
|
2968
|
+
if (this._tryHandleExecutionError(err, snapshot)) {
|
|
2969
|
+
return;
|
|
2970
|
+
}
|
|
3658
2971
|
this._setErrorSnapshot(err, snapshot);
|
|
3659
2972
|
break;
|
|
3660
2973
|
}
|
|
@@ -3702,6 +3015,25 @@ class Actor {
|
|
|
3702
3015
|
this._collectedActions = [];
|
|
3703
3016
|
this._collectedSent = [];
|
|
3704
3017
|
}
|
|
3018
|
+
_flushInitialEffects() {
|
|
3019
|
+
if (!this._initialEffects) {
|
|
3020
|
+
return true;
|
|
3021
|
+
}
|
|
3022
|
+
this._forceDeferredActions = true;
|
|
3023
|
+
try {
|
|
3024
|
+
const logicEffects = executeExecutableEffects(this._initialEffects, this._actorScope);
|
|
3025
|
+
this.logic.executeEffects?.(logicEffects, this._actorScope);
|
|
3026
|
+
this._initialEffects = undefined;
|
|
3027
|
+
return true;
|
|
3028
|
+
} catch (err) {
|
|
3029
|
+
this._initialEffects = undefined;
|
|
3030
|
+
this._deferred.length = 0;
|
|
3031
|
+
this._recoverOrError(err);
|
|
3032
|
+
return this._snapshot.status === 'active';
|
|
3033
|
+
} finally {
|
|
3034
|
+
this._forceDeferredActions = false;
|
|
3035
|
+
}
|
|
3036
|
+
}
|
|
3705
3037
|
|
|
3706
3038
|
/**
|
|
3707
3039
|
* Subscribe an observer to an actor’s snapshot values.
|
|
@@ -3770,11 +3102,7 @@ class Actor {
|
|
|
3770
3102
|
} else {
|
|
3771
3103
|
switch (this._snapshot.status) {
|
|
3772
3104
|
case 'done':
|
|
3773
|
-
|
|
3774
|
-
observer.complete?.();
|
|
3775
|
-
} catch (err) {
|
|
3776
|
-
reportUnhandledError(err);
|
|
3777
|
-
}
|
|
3105
|
+
safeCall(observer.complete);
|
|
3778
3106
|
break;
|
|
3779
3107
|
case 'error':
|
|
3780
3108
|
{
|
|
@@ -3782,11 +3110,7 @@ class Actor {
|
|
|
3782
3110
|
if (!observer.error) {
|
|
3783
3111
|
reportUnhandledError(err);
|
|
3784
3112
|
} else {
|
|
3785
|
-
|
|
3786
|
-
observer.error(err);
|
|
3787
|
-
} catch (err) {
|
|
3788
|
-
reportUnhandledError(err);
|
|
3789
|
-
}
|
|
3113
|
+
safeCall(observer.error, err);
|
|
3790
3114
|
}
|
|
3791
3115
|
break;
|
|
3792
3116
|
}
|
|
@@ -3828,7 +3152,7 @@ class Actor {
|
|
|
3828
3152
|
complete: observer.complete
|
|
3829
3153
|
});
|
|
3830
3154
|
},
|
|
3831
|
-
get: () => selector(this.
|
|
3155
|
+
get: () => selector(this.getSnapshot())
|
|
3832
3156
|
};
|
|
3833
3157
|
}
|
|
3834
3158
|
|
|
@@ -3838,6 +3162,9 @@ class Actor {
|
|
|
3838
3162
|
// Do not restart the service if it is already started
|
|
3839
3163
|
return this;
|
|
3840
3164
|
}
|
|
3165
|
+
if (this._processingStatus === ProcessingStatus.Stopped) {
|
|
3166
|
+
return this;
|
|
3167
|
+
}
|
|
3841
3168
|
if (this._syncSnapshot) {
|
|
3842
3169
|
this.subscribe({
|
|
3843
3170
|
next: snapshot => {
|
|
@@ -3852,8 +3179,8 @@ class Actor {
|
|
|
3852
3179
|
});
|
|
3853
3180
|
}
|
|
3854
3181
|
this.system._register(this.sessionId, this);
|
|
3855
|
-
if (this.
|
|
3856
|
-
this.system._set(this.
|
|
3182
|
+
if (this.registryKey) {
|
|
3183
|
+
this.system._set(this.registryKey, this);
|
|
3857
3184
|
}
|
|
3858
3185
|
this._processingStatus = ProcessingStatus.Running;
|
|
3859
3186
|
|
|
@@ -3866,6 +3193,9 @@ class Actor {
|
|
|
3866
3193
|
case 'done':
|
|
3867
3194
|
// a state machine can be "done" upon initialization (it could reach a final state using initial microsteps)
|
|
3868
3195
|
// we still need to complete observers, flush deferreds etc
|
|
3196
|
+
if (!this._flushInitialEffects()) {
|
|
3197
|
+
return this;
|
|
3198
|
+
}
|
|
3869
3199
|
this.update(this._snapshot, initEvent);
|
|
3870
3200
|
// TODO: rethink cleanup of observers, mailbox, etc
|
|
3871
3201
|
return this;
|
|
@@ -3885,6 +3215,9 @@ class Actor {
|
|
|
3885
3215
|
return this;
|
|
3886
3216
|
}
|
|
3887
3217
|
}
|
|
3218
|
+
if (!this._flushInitialEffects()) {
|
|
3219
|
+
return this;
|
|
3220
|
+
}
|
|
3888
3221
|
|
|
3889
3222
|
// TODO: this notifies all subscribers but usually this is redundant
|
|
3890
3223
|
// there is no real change happening here
|
|
@@ -3916,16 +3249,21 @@ class Actor {
|
|
|
3916
3249
|
};
|
|
3917
3250
|
}
|
|
3918
3251
|
if (caughtError) {
|
|
3919
|
-
|
|
3920
|
-
|
|
3921
|
-
|
|
3922
|
-
|
|
3923
|
-
|
|
3252
|
+
this._recoverOrError(caughtError.err);
|
|
3253
|
+
return;
|
|
3254
|
+
}
|
|
3255
|
+
let snapshot = this._snapshot;
|
|
3256
|
+
try {
|
|
3257
|
+
const [nextSnapshot, effects] = nextState;
|
|
3258
|
+
snapshot = nextSnapshot;
|
|
3259
|
+
this._snapshot = snapshot;
|
|
3260
|
+
const logicEffects = executeExecutableEffects(effects, this._actorScope);
|
|
3261
|
+
this.update(snapshot, event);
|
|
3262
|
+
this.logic.executeEffects?.(logicEffects, this._actorScope);
|
|
3263
|
+
} catch (err) {
|
|
3264
|
+
this._recoverOrError(err, snapshot);
|
|
3924
3265
|
return;
|
|
3925
3266
|
}
|
|
3926
|
-
const [snapshot, effects] = Array.isArray(nextState) ? nextState : [nextState, undefined];
|
|
3927
|
-
this.update(snapshot, event);
|
|
3928
|
-
executeLogicEffects(effects, this._actorScope);
|
|
3929
3267
|
if (event.type === XSTATE_STOP) {
|
|
3930
3268
|
this._stopProcedure();
|
|
3931
3269
|
this._complete();
|
|
@@ -3958,11 +3296,7 @@ class Actor {
|
|
|
3958
3296
|
}
|
|
3959
3297
|
_complete() {
|
|
3960
3298
|
for (const observer of this.observers) {
|
|
3961
|
-
|
|
3962
|
-
observer.complete?.();
|
|
3963
|
-
} catch (err) {
|
|
3964
|
-
reportUnhandledError(err);
|
|
3965
|
-
}
|
|
3299
|
+
safeCall(observer.complete);
|
|
3966
3300
|
}
|
|
3967
3301
|
this.observers.clear();
|
|
3968
3302
|
this.eventListeners.clear();
|
|
@@ -3973,24 +3307,19 @@ class Actor {
|
|
|
3973
3307
|
if (!this._parent) {
|
|
3974
3308
|
reportUnhandledError(err);
|
|
3975
3309
|
}
|
|
3976
|
-
this.eventListeners.clear();
|
|
3977
3310
|
} else {
|
|
3978
3311
|
let reportError = false;
|
|
3979
3312
|
for (const observer of this.observers) {
|
|
3980
3313
|
const errorListener = observer.error;
|
|
3981
3314
|
reportError ||= !errorListener;
|
|
3982
|
-
|
|
3983
|
-
errorListener?.(err);
|
|
3984
|
-
} catch (err2) {
|
|
3985
|
-
reportUnhandledError(err2);
|
|
3986
|
-
}
|
|
3315
|
+
safeCall(errorListener, err);
|
|
3987
3316
|
}
|
|
3988
3317
|
this.observers.clear();
|
|
3989
|
-
this.eventListeners.clear();
|
|
3990
3318
|
if (reportError) {
|
|
3991
3319
|
reportUnhandledError(err);
|
|
3992
3320
|
}
|
|
3993
3321
|
}
|
|
3322
|
+
this.eventListeners.clear();
|
|
3994
3323
|
if (this._parent) {
|
|
3995
3324
|
this.system._relay(this, this._parent, createErrorActorEvent(this.id, err));
|
|
3996
3325
|
}
|
|
@@ -4107,12 +3436,6 @@ class Actor {
|
|
|
4107
3436
|
* @see {@link Actor.getPersistedSnapshot} to persist the internal state of an actor (which is more than just a snapshot).
|
|
4108
3437
|
*/
|
|
4109
3438
|
getSnapshot() {
|
|
4110
|
-
return this.get();
|
|
4111
|
-
}
|
|
4112
|
-
|
|
4113
|
-
/** Read the actor's current snapshot as an atom value. */
|
|
4114
|
-
get() {
|
|
4115
|
-
onActorRead?.(this);
|
|
4116
3439
|
return this._snapshot;
|
|
4117
3440
|
}
|
|
4118
3441
|
}
|
|
@@ -4173,6 +3496,231 @@ function createActor(logic, options) {
|
|
|
4173
3496
|
return new Actor(logic, options);
|
|
4174
3497
|
}
|
|
4175
3498
|
|
|
3499
|
+
const effectStates = new WeakMap();
|
|
3500
|
+
const XSTATE_LOGIC_EFFECT_RESOLVE = 'xstate.logic.effect.resolve';
|
|
3501
|
+
const XSTATE_LOGIC_EFFECT_REJECT = 'xstate.logic.effect.reject';
|
|
3502
|
+
const XSTATE_LOGIC_EFFECT_START = 'xstate.logic.effect.start';
|
|
3503
|
+
function getEffectState(self) {
|
|
3504
|
+
let state = effectStates.get(self);
|
|
3505
|
+
if (!state) {
|
|
3506
|
+
state = new Map();
|
|
3507
|
+
effectStates.set(self, state);
|
|
3508
|
+
}
|
|
3509
|
+
return state;
|
|
3510
|
+
}
|
|
3511
|
+
function executeLogicEffects(effects, actorScope) {
|
|
3512
|
+
if (!effects?.length) {
|
|
3513
|
+
return;
|
|
3514
|
+
}
|
|
3515
|
+
for (const effect of effects) {
|
|
3516
|
+
switch (effect.type) {
|
|
3517
|
+
case 'emit':
|
|
3518
|
+
actorScope.emit(effect.event);
|
|
3519
|
+
break;
|
|
3520
|
+
case 'sendBack':
|
|
3521
|
+
{
|
|
3522
|
+
const parent = actorScope.self._parent;
|
|
3523
|
+
if (parent) {
|
|
3524
|
+
actorScope.system._relay(actorScope.self, parent, effect.event);
|
|
3525
|
+
}
|
|
3526
|
+
break;
|
|
3527
|
+
}
|
|
3528
|
+
case 'raise':
|
|
3529
|
+
actorScope.system._relay(actorScope.self, actorScope.self, effect.event);
|
|
3530
|
+
break;
|
|
3531
|
+
case 'effect':
|
|
3532
|
+
{
|
|
3533
|
+
if (!effect.key) {
|
|
3534
|
+
const cleanup = effect.exec();
|
|
3535
|
+
if (typeof cleanup === 'function') {
|
|
3536
|
+
getEffectState(actorScope.self).set(Symbol(), {
|
|
3537
|
+
cleanup
|
|
3538
|
+
});
|
|
3539
|
+
}
|
|
3540
|
+
break;
|
|
3541
|
+
}
|
|
3542
|
+
const state = getEffectState(actorScope.self);
|
|
3543
|
+
if (state.has(effect.key)) {
|
|
3544
|
+
break;
|
|
3545
|
+
}
|
|
3546
|
+
const cleanup = effect.exec();
|
|
3547
|
+
state.set(effect.key, {
|
|
3548
|
+
cleanup: typeof cleanup === 'function' ? cleanup : undefined
|
|
3549
|
+
});
|
|
3550
|
+
break;
|
|
3551
|
+
}
|
|
3552
|
+
case 'cleanupEffects':
|
|
3553
|
+
{
|
|
3554
|
+
const state = effectStates.get(actorScope.self);
|
|
3555
|
+
if (!state) {
|
|
3556
|
+
break;
|
|
3557
|
+
}
|
|
3558
|
+
for (const {
|
|
3559
|
+
cleanup
|
|
3560
|
+
} of state.values()) {
|
|
3561
|
+
cleanup?.();
|
|
3562
|
+
}
|
|
3563
|
+
effectStates.delete(actorScope.self);
|
|
3564
|
+
break;
|
|
3565
|
+
}
|
|
3566
|
+
}
|
|
3567
|
+
}
|
|
3568
|
+
}
|
|
3569
|
+
function resolveContext(context, input) {
|
|
3570
|
+
return typeof context === 'function' ? context({
|
|
3571
|
+
input
|
|
3572
|
+
}) : context;
|
|
3573
|
+
}
|
|
3574
|
+
function createLogic(config) {
|
|
3575
|
+
const transition = (snapshot, event, actorScope) => {
|
|
3576
|
+
if (snapshot.status !== 'active') {
|
|
3577
|
+
return [snapshot, []];
|
|
3578
|
+
}
|
|
3579
|
+
if (event.type === XSTATE_STOP) {
|
|
3580
|
+
return [{
|
|
3581
|
+
...snapshot,
|
|
3582
|
+
status: 'stopped',
|
|
3583
|
+
input: undefined
|
|
3584
|
+
}, [{
|
|
3585
|
+
type: 'cleanupEffects'
|
|
3586
|
+
}]];
|
|
3587
|
+
}
|
|
3588
|
+
if (event.type === XSTATE_LOGIC_EFFECT_START) {
|
|
3589
|
+
return [{
|
|
3590
|
+
...snapshot,
|
|
3591
|
+
effects: {
|
|
3592
|
+
...snapshot.effects,
|
|
3593
|
+
[event.key]: {
|
|
3594
|
+
status: 'active'
|
|
3595
|
+
}
|
|
3596
|
+
}
|
|
3597
|
+
}, []];
|
|
3598
|
+
}
|
|
3599
|
+
if (event.type === XSTATE_LOGIC_EFFECT_RESOLVE) {
|
|
3600
|
+
return [{
|
|
3601
|
+
...snapshot,
|
|
3602
|
+
effects: {
|
|
3603
|
+
...snapshot.effects,
|
|
3604
|
+
[event.key]: {
|
|
3605
|
+
status: 'done',
|
|
3606
|
+
output: event.output
|
|
3607
|
+
}
|
|
3608
|
+
}
|
|
3609
|
+
}, []];
|
|
3610
|
+
}
|
|
3611
|
+
if (event.type === XSTATE_LOGIC_EFFECT_REJECT) {
|
|
3612
|
+
return [{
|
|
3613
|
+
...snapshot,
|
|
3614
|
+
effects: {
|
|
3615
|
+
...snapshot.effects,
|
|
3616
|
+
[event.key]: {
|
|
3617
|
+
status: 'error',
|
|
3618
|
+
error: event.error
|
|
3619
|
+
}
|
|
3620
|
+
}
|
|
3621
|
+
}, []];
|
|
3622
|
+
}
|
|
3623
|
+
const effects = [];
|
|
3624
|
+
const trackedEffects = {};
|
|
3625
|
+
const enqueueEffect = (key, exec) => {
|
|
3626
|
+
if (snapshot.effects?.[key]) {
|
|
3627
|
+
return;
|
|
3628
|
+
}
|
|
3629
|
+
effects.push({
|
|
3630
|
+
type: 'effect',
|
|
3631
|
+
key,
|
|
3632
|
+
exec
|
|
3633
|
+
});
|
|
3634
|
+
trackedEffects[key] = {
|
|
3635
|
+
status: 'active'
|
|
3636
|
+
};
|
|
3637
|
+
};
|
|
3638
|
+
const enq = {
|
|
3639
|
+
emit: emitted => {
|
|
3640
|
+
effects.push({
|
|
3641
|
+
type: 'emit',
|
|
3642
|
+
event: emitted
|
|
3643
|
+
});
|
|
3644
|
+
},
|
|
3645
|
+
sendBack: sentEvent => {
|
|
3646
|
+
effects.push({
|
|
3647
|
+
type: 'sendBack',
|
|
3648
|
+
event: sentEvent
|
|
3649
|
+
});
|
|
3650
|
+
},
|
|
3651
|
+
raise: raisedEvent => {
|
|
3652
|
+
effects.push({
|
|
3653
|
+
type: 'raise',
|
|
3654
|
+
event: raisedEvent
|
|
3655
|
+
});
|
|
3656
|
+
},
|
|
3657
|
+
effect: (keyOrExec, maybeExec) => {
|
|
3658
|
+
if (typeof keyOrExec === 'string') {
|
|
3659
|
+
enqueueEffect(keyOrExec, maybeExec);
|
|
3660
|
+
return;
|
|
3661
|
+
}
|
|
3662
|
+
effects.push({
|
|
3663
|
+
type: 'effect',
|
|
3664
|
+
exec: keyOrExec
|
|
3665
|
+
});
|
|
3666
|
+
}
|
|
3667
|
+
};
|
|
3668
|
+
const patch = config.run({
|
|
3669
|
+
context: snapshot.context,
|
|
3670
|
+
event,
|
|
3671
|
+
input: snapshot.input,
|
|
3672
|
+
system: actorScope.system,
|
|
3673
|
+
self: actorScope.self,
|
|
3674
|
+
emit: actorScope.emit
|
|
3675
|
+
}, enq);
|
|
3676
|
+
const nextSnapshot = {
|
|
3677
|
+
...snapshot,
|
|
3678
|
+
...(patch || {})
|
|
3679
|
+
};
|
|
3680
|
+
if ('context' in snapshot || patch?.context !== undefined) {
|
|
3681
|
+
nextSnapshot.context = patch?.context ?? snapshot.context;
|
|
3682
|
+
}
|
|
3683
|
+
if (patch?.effects || Object.keys(trackedEffects).length) {
|
|
3684
|
+
nextSnapshot.effects = {
|
|
3685
|
+
...snapshot.effects,
|
|
3686
|
+
...patch?.effects,
|
|
3687
|
+
...trackedEffects
|
|
3688
|
+
};
|
|
3689
|
+
}
|
|
3690
|
+
return [nextSnapshot, effects];
|
|
3691
|
+
};
|
|
3692
|
+
const logic = {
|
|
3693
|
+
id: config.id,
|
|
3694
|
+
config,
|
|
3695
|
+
transition,
|
|
3696
|
+
start: (snapshot, actorScope) => {
|
|
3697
|
+
const [nextSnapshot, effects] = transition(snapshot, createInitEvent(snapshot.input), actorScope);
|
|
3698
|
+
Object.assign(snapshot, nextSnapshot);
|
|
3699
|
+
executeLogicEffects(effects, actorScope);
|
|
3700
|
+
},
|
|
3701
|
+
initialTransition: (input, _) => {
|
|
3702
|
+
const context = resolveContext(config.context, input);
|
|
3703
|
+
const snapshot = {
|
|
3704
|
+
status: 'active',
|
|
3705
|
+
output: undefined,
|
|
3706
|
+
error: undefined,
|
|
3707
|
+
input
|
|
3708
|
+
};
|
|
3709
|
+
if (context !== undefined) {
|
|
3710
|
+
snapshot.context = context;
|
|
3711
|
+
}
|
|
3712
|
+
return [{
|
|
3713
|
+
...snapshot
|
|
3714
|
+
}, []];
|
|
3715
|
+
},
|
|
3716
|
+
getInitialSnapshot: (actorScope, input) => logic.initialTransition(input, actorScope)[0],
|
|
3717
|
+
getPersistedSnapshot: snapshot => snapshot,
|
|
3718
|
+
restoreSnapshot: snapshot => snapshot,
|
|
3719
|
+
executeEffects: executeLogicEffects
|
|
3720
|
+
};
|
|
3721
|
+
return logic;
|
|
3722
|
+
}
|
|
3723
|
+
|
|
4176
3724
|
const instanceStates = /* #__PURE__ */new WeakMap();
|
|
4177
3725
|
|
|
4178
3726
|
/**
|
|
@@ -4551,46 +4099,45 @@ function createEventObservableLogic(lazyObservableOrConfig) {
|
|
|
4551
4099
|
config: lazyObservable,
|
|
4552
4100
|
transition: (state, event) => {
|
|
4553
4101
|
if (state.status !== 'active') {
|
|
4554
|
-
return state;
|
|
4102
|
+
return [state, []];
|
|
4555
4103
|
}
|
|
4556
4104
|
switch (event.type) {
|
|
4557
4105
|
case XSTATE_OBSERVABLE_ERROR:
|
|
4558
|
-
return {
|
|
4106
|
+
return [{
|
|
4559
4107
|
...state,
|
|
4560
4108
|
status: 'error',
|
|
4561
4109
|
error: event.data,
|
|
4562
4110
|
input: undefined,
|
|
4563
4111
|
_subscription: undefined
|
|
4564
|
-
};
|
|
4112
|
+
}, []];
|
|
4565
4113
|
case XSTATE_OBSERVABLE_COMPLETE:
|
|
4566
|
-
return {
|
|
4114
|
+
return [{
|
|
4567
4115
|
...state,
|
|
4568
4116
|
status: 'done',
|
|
4569
4117
|
input: undefined,
|
|
4570
4118
|
_subscription: undefined
|
|
4571
|
-
};
|
|
4119
|
+
}, []];
|
|
4572
4120
|
case XSTATE_STOP:
|
|
4573
4121
|
state._subscription.unsubscribe();
|
|
4574
|
-
return {
|
|
4122
|
+
return [{
|
|
4575
4123
|
...state,
|
|
4576
4124
|
status: 'stopped',
|
|
4577
4125
|
input: undefined,
|
|
4578
4126
|
_subscription: undefined
|
|
4579
|
-
};
|
|
4127
|
+
}, []];
|
|
4580
4128
|
default:
|
|
4581
|
-
return state;
|
|
4129
|
+
return [state, []];
|
|
4582
4130
|
}
|
|
4583
4131
|
},
|
|
4584
|
-
|
|
4585
|
-
|
|
4586
|
-
|
|
4587
|
-
|
|
4588
|
-
|
|
4589
|
-
|
|
4590
|
-
|
|
4591
|
-
|
|
4592
|
-
|
|
4593
|
-
},
|
|
4132
|
+
initialTransition: (input, _) => [{
|
|
4133
|
+
status: 'active',
|
|
4134
|
+
output: undefined,
|
|
4135
|
+
error: undefined,
|
|
4136
|
+
context: undefined,
|
|
4137
|
+
input,
|
|
4138
|
+
_subscription: undefined
|
|
4139
|
+
}, []],
|
|
4140
|
+
getInitialSnapshot: (actorScope, input) => logic.initialTransition(input, actorScope)[0],
|
|
4594
4141
|
start: (state, {
|
|
4595
4142
|
self,
|
|
4596
4143
|
system,
|
|
@@ -4915,14 +4462,15 @@ exports.ProcessingStatus = ProcessingStatus;
|
|
|
4915
4462
|
exports.STATE_DELIMITER = STATE_DELIMITER;
|
|
4916
4463
|
exports.TimeoutError = TimeoutError;
|
|
4917
4464
|
exports.XSTATE_INIT = XSTATE_INIT;
|
|
4465
|
+
exports.XSTATE_STOP = XSTATE_STOP;
|
|
4466
|
+
exports.builtInActions = builtInActions;
|
|
4918
4467
|
exports.checkStateIn = checkStateIn;
|
|
4468
|
+
exports.cloneMachineSnapshot = cloneMachineSnapshot;
|
|
4919
4469
|
exports.createActor = createActor;
|
|
4920
|
-
exports.createAsyncAtom = createAsyncAtom;
|
|
4921
4470
|
exports.createAsyncLogic = createAsyncLogic;
|
|
4922
|
-
exports.createAtom = createAtom;
|
|
4923
|
-
exports.createAtomConfig = createAtomConfig;
|
|
4924
4471
|
exports.createCallbackLogic = createCallbackLogic;
|
|
4925
4472
|
exports.createEmptyActor = createEmptyActor;
|
|
4473
|
+
exports.createErrorPlatformEvent = createErrorPlatformEvent;
|
|
4926
4474
|
exports.createEventObservableLogic = createEventObservableLogic;
|
|
4927
4475
|
exports.createInitEvent = createInitEvent;
|
|
4928
4476
|
exports.createInvokeId = createInvokeId;
|
|
@@ -4931,8 +4479,9 @@ exports.createListenerLogic = createListenerLogic;
|
|
|
4931
4479
|
exports.createLogic = createLogic;
|
|
4932
4480
|
exports.createMachineSnapshot = createMachineSnapshot;
|
|
4933
4481
|
exports.createObservableLogic = createObservableLogic;
|
|
4934
|
-
exports.createReducerAtom = createReducerAtom;
|
|
4935
4482
|
exports.createSubscriptionLogic = createSubscriptionLogic;
|
|
4483
|
+
exports.createTransitionEnqueue = createTransitionEnqueue;
|
|
4484
|
+
exports.deriveDeferredStarts = deriveDeferredStarts;
|
|
4936
4485
|
exports.evaluateCandidate = evaluateCandidate;
|
|
4937
4486
|
exports.formatRouteTransitions = formatRouteTransitions;
|
|
4938
4487
|
exports.formatTransition = formatTransition;
|
|
@@ -4944,9 +4493,11 @@ exports.getPersistedSnapshot = getPersistedSnapshot;
|
|
|
4944
4493
|
exports.getProperAncestors = getProperAncestors;
|
|
4945
4494
|
exports.getStateNodeByPath = getStateNodeByPath;
|
|
4946
4495
|
exports.getStateNodes = getStateNodes;
|
|
4496
|
+
exports.getTransitionResult = getTransitionResult;
|
|
4497
|
+
exports.hasEffect = hasEffect;
|
|
4947
4498
|
exports.initialMicrostep = initialMicrostep;
|
|
4948
|
-
exports.isAtom = isAtom;
|
|
4949
4499
|
exports.isAtomicStateNode = isAtomicStateNode;
|
|
4500
|
+
exports.isBuiltInExecutableAction = isBuiltInExecutableAction;
|
|
4950
4501
|
exports.isInFinalState = isInFinalState;
|
|
4951
4502
|
exports.isMachineSnapshot = isMachineSnapshot;
|
|
4952
4503
|
exports.isStateId = isStateId;
|
|
@@ -4957,7 +4508,7 @@ exports.matchesEventDescriptor = matchesEventDescriptor;
|
|
|
4957
4508
|
exports.matchesState = matchesState;
|
|
4958
4509
|
exports.parseDelayToMilliseconds = parseDelayToMilliseconds;
|
|
4959
4510
|
exports.pathToStateValue = pathToStateValue;
|
|
4960
|
-
exports.
|
|
4511
|
+
exports.resolveActionsWithContext = resolveActionsWithContext;
|
|
4961
4512
|
exports.resolveReferencedActor = resolveReferencedActor;
|
|
4962
4513
|
exports.resolveStateValue = resolveStateValue;
|
|
4963
4514
|
exports.subscriptionLogic = subscriptionLogic;
|