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