xstate 6.0.0-alpha.21 → 6.0.0-alpha.22
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/dist/{StateMachine-550ff7f2.development.cjs.js → StateMachine-1888d034.development.cjs.js} +178 -27
- package/dist/{StateMachine-1b155670.cjs.js → StateMachine-610fe929.cjs.js} +178 -27
- package/dist/{StateMachine-e25dd61f.development.esm.js → StateMachine-9fad7a2b.development.esm.js} +174 -28
- package/dist/{StateMachine-3c17fe3e.esm.js → StateMachine-ddb5d007.esm.js} +174 -28
- package/dist/declarations/src/State.d.ts +4 -2
- package/dist/declarations/src/StateMachine.d.ts +5 -4
- package/dist/declarations/src/actions.d.ts +10 -8
- package/dist/declarations/src/actors/logic.d.ts +5 -21
- package/dist/declarations/src/createActor.d.ts +3 -1
- package/dist/declarations/src/fsm.d.ts +3 -1
- package/dist/declarations/src/index.d.ts +2 -2
- package/dist/declarations/src/stateUtils.d.ts +1 -1
- package/dist/declarations/src/system.d.ts +34 -9
- package/dist/declarations/src/transitionActions.d.ts +5 -1
- package/dist/declarations/src/types.d.ts +70 -51
- package/dist/{index-3f87408c.esm.js → index-2fe11873.esm.js} +782 -389
- package/dist/{index-ec7c7a2d.development.cjs.js → index-41c0375c.development.cjs.js} +790 -390
- package/dist/{index-e694228c.cjs.js → index-434fac23.cjs.js} +789 -389
- package/dist/{index-d39fb138.development.esm.js → index-44d8c2d1.development.esm.js} +783 -390
- 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 +82 -101
- package/dist/xstate.cjs.mjs +1 -0
- package/dist/xstate.development.cjs.js +82 -101
- package/dist/xstate.development.cjs.mjs +1 -0
- package/dist/xstate.development.esm.js +80 -100
- package/dist/xstate.esm.js +80 -100
- package/dist/xstate.umd.min.js +1 -1
- package/dist/xstate.umd.min.js.map +1 -1
- package/package.json +1 -1
|
@@ -54,6 +54,8 @@ const XSTATE_INIT = '@xstate.init';
|
|
|
54
54
|
const XSTATE_START = '@xstate.start';
|
|
55
55
|
const XSTATE_SPAWN = '@xstate.spawn';
|
|
56
56
|
const XSTATE_STOP = '@xstate.stop';
|
|
57
|
+
const XSTATE_TERMINATE = '@xstate.terminate';
|
|
58
|
+
const XSTATE_TIMER = 'xstate.timer';
|
|
57
59
|
|
|
58
60
|
/**
|
|
59
61
|
* Returns an event that represents an implicit event that is sent after the
|
|
@@ -158,34 +160,40 @@ function reportUnhandledError(err) {
|
|
|
158
160
|
|
|
159
161
|
const symbolObservable = (() => typeof Symbol === 'function' && Symbol.observable || '@@observable')();
|
|
160
162
|
|
|
163
|
+
function assertSendToEvent(event) {
|
|
164
|
+
if (typeof event === 'string') {
|
|
165
|
+
throw new Error(// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
|
|
166
|
+
`Only event objects may be used with sendTo; use sendTo({ type: "${event}" }) instead` );
|
|
167
|
+
}
|
|
168
|
+
}
|
|
161
169
|
const builtInActions = {
|
|
162
|
-
//
|
|
163
|
-
// deferred
|
|
164
|
-
['@xstate.spawn']:
|
|
170
|
+
// Actor refs are created while resolving the next snapshot. Starting is a
|
|
171
|
+
// separate deferred effect so the returned list remains execution-complete.
|
|
172
|
+
['@xstate.spawn']: actor => {
|
|
173
|
+
return actor.system.spawnActor(actor._parent, actor);
|
|
174
|
+
},
|
|
165
175
|
['@xstate.start']: actor => {
|
|
166
|
-
actor.
|
|
176
|
+
return actor.system.startActor(actor);
|
|
167
177
|
},
|
|
168
|
-
['@xstate.raise']: (actorScope,
|
|
169
|
-
actorScope.system.
|
|
178
|
+
['@xstate.raise']: (actorScope, _event, options) => {
|
|
179
|
+
return actorScope.system.scheduleTimer(actorScope.self, options.id, options?.delay ?? 0);
|
|
170
180
|
},
|
|
171
181
|
['@xstate.sendTo']: (actorScope, actor, event, options) => {
|
|
172
|
-
|
|
173
|
-
throw new Error(// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
|
|
174
|
-
`Only event objects may be used with sendTo; use sendTo({ type: "${event}" }) instead` );
|
|
175
|
-
}
|
|
182
|
+
assertSendToEvent(event);
|
|
176
183
|
if (options?.delay !== undefined) {
|
|
177
|
-
actorScope.system.
|
|
184
|
+
return actorScope.system.scheduleTimer(actorScope.self, options.id, options?.delay ?? 0);
|
|
178
185
|
} else {
|
|
179
|
-
actorScope.
|
|
180
|
-
actorScope.system._relay(actorScope.self, actor, event);
|
|
181
|
-
});
|
|
186
|
+
return actorScope.system._relay(actorScope.self, actor, event);
|
|
182
187
|
}
|
|
183
188
|
},
|
|
184
189
|
['@xstate.cancel']: (actorScope, sendId) => {
|
|
185
|
-
actorScope.system.
|
|
190
|
+
return actorScope.system.cancelTimer(actorScope.self, sendId);
|
|
186
191
|
},
|
|
187
192
|
['@xstate.stop']: (actorScope, actor) => {
|
|
188
|
-
actorScope.
|
|
193
|
+
return actorScope.system.stopActor(actor);
|
|
194
|
+
},
|
|
195
|
+
['@xstate.terminate']: (actor, termination) => {
|
|
196
|
+
return actor.system.terminateActor(actor, termination);
|
|
189
197
|
}
|
|
190
198
|
};
|
|
191
199
|
|
|
@@ -349,12 +357,109 @@ function createSubscriptionLogic() {
|
|
|
349
357
|
// Singleton logic instance
|
|
350
358
|
const subscriptionLogic = /* #__PURE__ */createSubscriptionLogic();
|
|
351
359
|
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
360
|
+
function execCustomEffect() {
|
|
361
|
+
return this.action?.(...this.args);
|
|
362
|
+
}
|
|
363
|
+
function execEmitEffect(runtime = this.source.system) {
|
|
364
|
+
return runtime.emitEvent(this.source, this.event);
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
/** @internal Creates an emitted-event effect. */
|
|
368
|
+
function createEmitEffect(actorScope, event) {
|
|
369
|
+
return {
|
|
370
|
+
kind: 'emit',
|
|
371
|
+
exec: execEmitEffect,
|
|
372
|
+
type: event.type,
|
|
373
|
+
source: actorScope.self,
|
|
374
|
+
event,
|
|
375
|
+
params: undefined,
|
|
376
|
+
args: []
|
|
377
|
+
};
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
/** @internal Creates a directly executable user effect. */
|
|
381
|
+
function createCustomEffect(type, action, params) {
|
|
382
|
+
return {
|
|
383
|
+
kind: 'action',
|
|
384
|
+
exec: action,
|
|
385
|
+
type,
|
|
386
|
+
action: action,
|
|
387
|
+
params,
|
|
388
|
+
args: []
|
|
389
|
+
};
|
|
390
|
+
}
|
|
391
|
+
function execSpawnEffect(runtime = this.actor.system) {
|
|
392
|
+
return runtime.spawnActor(this.source, this.actor);
|
|
393
|
+
}
|
|
394
|
+
function execStartEffect(runtime = this.actor.system) {
|
|
395
|
+
return runtime.startActor(this.actor);
|
|
396
|
+
}
|
|
397
|
+
function execStopEffect(runtime = this.source.system) {
|
|
398
|
+
return runtime.stopActor(this.actor);
|
|
399
|
+
}
|
|
400
|
+
function execTerminateEffect(runtime = this.actor.system) {
|
|
401
|
+
const termination = this.status === 'done' ? {
|
|
402
|
+
status: 'done',
|
|
403
|
+
output: this.output,
|
|
404
|
+
error: undefined
|
|
405
|
+
} : {
|
|
406
|
+
status: 'error',
|
|
407
|
+
output: undefined,
|
|
408
|
+
error: this.error
|
|
409
|
+
};
|
|
410
|
+
return runtime.terminateActor(this.actor, termination);
|
|
411
|
+
}
|
|
412
|
+
function execRaiseEffect(runtime = this.source.system) {
|
|
413
|
+
return runtime.scheduleTimer(this.source, this.id, this.delay ?? 0);
|
|
414
|
+
}
|
|
415
|
+
function execSendToEffect(runtime = this.source.system) {
|
|
416
|
+
assertSendToEvent(this.event);
|
|
417
|
+
if (this.delay !== undefined) {
|
|
418
|
+
return runtime.scheduleTimer(this.source, this.id, this.delay);
|
|
419
|
+
}
|
|
420
|
+
return runtime.sendEvent(this.source, this.target, this.event);
|
|
421
|
+
}
|
|
422
|
+
function execCancelEffect(runtime = this.source.system) {
|
|
423
|
+
return runtime.cancelTimer(this.source, this.id);
|
|
424
|
+
}
|
|
425
|
+
function updateLogicalTimers(snapshot, effect, actorScope) {
|
|
426
|
+
if (!isBuiltInExecutableAction(effect)) {
|
|
427
|
+
return snapshot;
|
|
428
|
+
}
|
|
429
|
+
if (effect.type === '@xstate.cancel') {
|
|
430
|
+
if (!snapshot.timers?.[effect.id]) {
|
|
431
|
+
return snapshot;
|
|
432
|
+
}
|
|
433
|
+
const timers = {
|
|
434
|
+
...snapshot.timers
|
|
435
|
+
};
|
|
436
|
+
delete timers[effect.id];
|
|
437
|
+
return {
|
|
438
|
+
...snapshot,
|
|
439
|
+
timers
|
|
440
|
+
};
|
|
441
|
+
}
|
|
442
|
+
if (effect.type !== '@xstate.raise' && effect.type !== '@xstate.sendTo' || effect.delay === undefined) {
|
|
443
|
+
return snapshot;
|
|
444
|
+
}
|
|
445
|
+
let nextTimerId = snapshot._nextTimerId ?? 0;
|
|
446
|
+
const id = effect.id ?? `xstate.timer.auto.${nextTimerId++}`;
|
|
447
|
+
effect.id = id;
|
|
448
|
+
const target = effect.type === '@xstate.raise' || effect.target === actorScope.self ? 'self' : effect.target;
|
|
449
|
+
return {
|
|
450
|
+
...snapshot,
|
|
451
|
+
timers: {
|
|
452
|
+
...snapshot.timers,
|
|
453
|
+
[id]: {
|
|
454
|
+
id,
|
|
455
|
+
delay: effect.delay,
|
|
456
|
+
type: effect.type,
|
|
457
|
+
event: effect.event,
|
|
458
|
+
target
|
|
459
|
+
}
|
|
460
|
+
},
|
|
461
|
+
_nextTimerId: nextTimerId
|
|
462
|
+
};
|
|
358
463
|
}
|
|
359
464
|
function mergeContextPatch(context, patch) {
|
|
360
465
|
return {
|
|
@@ -363,43 +468,55 @@ function mergeContextPatch(context, patch) {
|
|
|
363
468
|
};
|
|
364
469
|
}
|
|
365
470
|
function pushBuiltInAction(actions, action, ...args) {
|
|
366
|
-
|
|
471
|
+
const actionRecord = {
|
|
367
472
|
action,
|
|
368
473
|
args
|
|
369
|
-
}
|
|
474
|
+
};
|
|
475
|
+
actions.push(actionRecord);
|
|
476
|
+
return actionRecord;
|
|
370
477
|
}
|
|
371
|
-
function
|
|
372
|
-
|
|
478
|
+
function applyChildUpdate(snapshot, update, actorScope) {
|
|
479
|
+
if (update.type === 'add') {
|
|
373
480
|
return {
|
|
481
|
+
...snapshot,
|
|
374
482
|
children: {
|
|
375
|
-
...
|
|
376
|
-
[id]: actor
|
|
483
|
+
...snapshot.children,
|
|
484
|
+
[update.id]: update.actor
|
|
377
485
|
}
|
|
378
486
|
};
|
|
379
|
-
}
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
}
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
for (const key of Object.keys(children)) {
|
|
389
|
-
if (children[key] === actor) {
|
|
390
|
-
delete children[key];
|
|
391
|
-
}
|
|
487
|
+
}
|
|
488
|
+
const children = {
|
|
489
|
+
...snapshot.children
|
|
490
|
+
};
|
|
491
|
+
let owned = update.actor._parent === actorScope.self;
|
|
492
|
+
for (const key of Object.keys(children)) {
|
|
493
|
+
if (children[key] === update.actor) {
|
|
494
|
+
owned = true;
|
|
495
|
+
delete children[key];
|
|
392
496
|
}
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
};
|
|
396
|
-
}
|
|
397
|
-
|
|
398
|
-
|
|
497
|
+
}
|
|
498
|
+
if (!owned) {
|
|
499
|
+
throw new Error(`Cannot stop child actor ${update.actor.id} of ${actorScope.self.id} because it is not a child` );
|
|
500
|
+
}
|
|
501
|
+
actorScope.system._unregister(update.actor);
|
|
502
|
+
return {
|
|
503
|
+
...snapshot,
|
|
504
|
+
children
|
|
505
|
+
};
|
|
506
|
+
}
|
|
507
|
+
function getTransitionActionRecord(action) {
|
|
508
|
+
if (typeof action === 'object' && action !== null && 'action' in action && typeof action.action === 'function') {
|
|
509
|
+
return action;
|
|
510
|
+
}
|
|
511
|
+
return undefined;
|
|
399
512
|
}
|
|
400
513
|
function pushSpawnedChild(actions, actor, id) {
|
|
401
|
-
pushBuiltInAction(actions, builtInActions['@xstate.spawn'], actor);
|
|
402
|
-
|
|
514
|
+
const action = pushBuiltInAction(actions, builtInActions['@xstate.spawn'], actor);
|
|
515
|
+
action.childUpdate = {
|
|
516
|
+
type: 'add',
|
|
517
|
+
actor,
|
|
518
|
+
id: id ?? actor.id
|
|
519
|
+
};
|
|
403
520
|
}
|
|
404
521
|
function createTransitionEnqueue(actorScope, actions, internalEvents, actorSubscriptions = false, createActors = true) {
|
|
405
522
|
const props = {
|
|
@@ -429,7 +546,7 @@ function createTransitionEnqueue(actorScope, actions, internalEvents, actorSubsc
|
|
|
429
546
|
id: options?.id ?? options?.registryKey ?? logic.id
|
|
430
547
|
};
|
|
431
548
|
}
|
|
432
|
-
const actor =
|
|
549
|
+
const actor = actorScope.system.createActorRef(logic, {
|
|
433
550
|
...options,
|
|
434
551
|
parent: actorScope.self
|
|
435
552
|
});
|
|
@@ -448,8 +565,11 @@ function createTransitionEnqueue(actorScope, actions, internalEvents, actorSubsc
|
|
|
448
565
|
},
|
|
449
566
|
stop: actor => {
|
|
450
567
|
if (actor) {
|
|
451
|
-
pushBuiltInAction(actions, builtInActions['@xstate.stop'], actorScope, actor);
|
|
452
|
-
|
|
568
|
+
const action = pushBuiltInAction(actions, builtInActions['@xstate.stop'], actorScope, actor);
|
|
569
|
+
action.childUpdate = {
|
|
570
|
+
type: 'remove',
|
|
571
|
+
actor
|
|
572
|
+
};
|
|
453
573
|
}
|
|
454
574
|
}
|
|
455
575
|
};
|
|
@@ -461,7 +581,7 @@ function createTransitionEnqueue(actorScope, actions, internalEvents, actorSubsc
|
|
|
461
581
|
eventType,
|
|
462
582
|
mapper
|
|
463
583
|
};
|
|
464
|
-
const listenerActor =
|
|
584
|
+
const listenerActor = actorScope.system.createActorRef(listenerLogic, {
|
|
465
585
|
input,
|
|
466
586
|
parent: actorScope.self
|
|
467
587
|
});
|
|
@@ -476,7 +596,7 @@ function createTransitionEnqueue(actorScope, actions, internalEvents, actorSubsc
|
|
|
476
596
|
actor,
|
|
477
597
|
mappers: normalizedMappers
|
|
478
598
|
};
|
|
479
|
-
const subscriptionActor =
|
|
599
|
+
const subscriptionActor = actorScope.system.createActorRef(subscriptionLogic, {
|
|
480
600
|
input,
|
|
481
601
|
parent: actorScope.self
|
|
482
602
|
});
|
|
@@ -495,6 +615,9 @@ function getBuiltInActionFields(action, args) {
|
|
|
495
615
|
{
|
|
496
616
|
const [actor] = args;
|
|
497
617
|
return {
|
|
618
|
+
kind: 'builtin',
|
|
619
|
+
exec: execSpawnEffect,
|
|
620
|
+
source: actor._parent,
|
|
498
621
|
actor,
|
|
499
622
|
id: actor.id,
|
|
500
623
|
logic: actor.logic,
|
|
@@ -506,6 +629,9 @@ function getBuiltInActionFields(action, args) {
|
|
|
506
629
|
{
|
|
507
630
|
const [, event, options] = args;
|
|
508
631
|
return {
|
|
632
|
+
kind: 'builtin',
|
|
633
|
+
exec: execRaiseEffect,
|
|
634
|
+
source: args[0].self,
|
|
509
635
|
event,
|
|
510
636
|
id: options?.id,
|
|
511
637
|
delay: options?.delay
|
|
@@ -515,6 +641,9 @@ function getBuiltInActionFields(action, args) {
|
|
|
515
641
|
{
|
|
516
642
|
const [, target, event, options] = args;
|
|
517
643
|
return {
|
|
644
|
+
kind: 'builtin',
|
|
645
|
+
exec: execSendToEffect,
|
|
646
|
+
source: args[0].self,
|
|
518
647
|
target,
|
|
519
648
|
event,
|
|
520
649
|
id: options?.id,
|
|
@@ -525,6 +654,9 @@ function getBuiltInActionFields(action, args) {
|
|
|
525
654
|
{
|
|
526
655
|
const [, id] = args;
|
|
527
656
|
return {
|
|
657
|
+
kind: 'builtin',
|
|
658
|
+
exec: execCancelEffect,
|
|
659
|
+
source: args[0].self,
|
|
528
660
|
id
|
|
529
661
|
};
|
|
530
662
|
}
|
|
@@ -532,7 +664,11 @@ function getBuiltInActionFields(action, args) {
|
|
|
532
664
|
{
|
|
533
665
|
const [, actor] = args;
|
|
534
666
|
return {
|
|
535
|
-
|
|
667
|
+
kind: 'builtin',
|
|
668
|
+
exec: execStopEffect,
|
|
669
|
+
source: args[0].self,
|
|
670
|
+
actor,
|
|
671
|
+
id: actor.id
|
|
536
672
|
};
|
|
537
673
|
}
|
|
538
674
|
default:
|
|
@@ -540,19 +676,91 @@ function getBuiltInActionFields(action, args) {
|
|
|
540
676
|
}
|
|
541
677
|
}
|
|
542
678
|
function createStartEffect(actor) {
|
|
543
|
-
const action = builtInActions['@xstate.start'];
|
|
544
679
|
const args = [actor];
|
|
545
|
-
return
|
|
680
|
+
return {
|
|
681
|
+
kind: 'builtin',
|
|
682
|
+
exec: execStartEffect,
|
|
546
683
|
type: XSTATE_START,
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
args
|
|
550
|
-
},
|
|
684
|
+
source: actor._parent,
|
|
685
|
+
params: undefined,
|
|
551
686
|
args,
|
|
552
|
-
exec: action.bind(null, actor),
|
|
553
687
|
actor,
|
|
554
688
|
id: actor.id
|
|
555
|
-
}
|
|
689
|
+
};
|
|
690
|
+
}
|
|
691
|
+
function createSpawnEffect(actor) {
|
|
692
|
+
const args = [actor];
|
|
693
|
+
return {
|
|
694
|
+
kind: 'builtin',
|
|
695
|
+
exec: execSpawnEffect,
|
|
696
|
+
type: XSTATE_SPAWN,
|
|
697
|
+
source: actor._parent,
|
|
698
|
+
params: undefined,
|
|
699
|
+
args,
|
|
700
|
+
actor,
|
|
701
|
+
id: actor.id,
|
|
702
|
+
logic: actor.logic,
|
|
703
|
+
src: actor.src,
|
|
704
|
+
input: actor.options?.input
|
|
705
|
+
};
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
/** @internal Creates an immediate actor-to-actor delivery effect. */
|
|
709
|
+
function createSendToEffect(actorScope, target, event) {
|
|
710
|
+
const args = [actorScope, target, event, {}];
|
|
711
|
+
return {
|
|
712
|
+
kind: 'builtin',
|
|
713
|
+
exec: execSendToEffect,
|
|
714
|
+
type: '@xstate.sendTo',
|
|
715
|
+
source: actorScope.self,
|
|
716
|
+
target,
|
|
717
|
+
event,
|
|
718
|
+
id: undefined,
|
|
719
|
+
delay: undefined,
|
|
720
|
+
params: undefined,
|
|
721
|
+
args
|
|
722
|
+
};
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
/** @internal Creates the terminal lifecycle effect for an actor. */
|
|
726
|
+
function createTerminationEffect(actorScope, snapshot) {
|
|
727
|
+
if (snapshot.status !== 'done' && snapshot.status !== 'error') {
|
|
728
|
+
throw new Error('Cannot terminate an active or stopped actor');
|
|
729
|
+
}
|
|
730
|
+
const termination = snapshot.status === 'done' ? {
|
|
731
|
+
status: 'done',
|
|
732
|
+
output: snapshot.output,
|
|
733
|
+
error: undefined
|
|
734
|
+
} : {
|
|
735
|
+
status: 'error',
|
|
736
|
+
output: undefined,
|
|
737
|
+
error: snapshot.error
|
|
738
|
+
};
|
|
739
|
+
const args = [actorScope.self, termination];
|
|
740
|
+
return {
|
|
741
|
+
kind: 'builtin',
|
|
742
|
+
exec: execTerminateEffect,
|
|
743
|
+
type: XSTATE_TERMINATE,
|
|
744
|
+
source: actorScope.self,
|
|
745
|
+
actor: actorScope.self,
|
|
746
|
+
id: actorScope.self.id,
|
|
747
|
+
...termination,
|
|
748
|
+
params: undefined,
|
|
749
|
+
args
|
|
750
|
+
};
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
/**
|
|
754
|
+
* Ensures that a newly terminal snapshot has exactly one ordered actor
|
|
755
|
+
* termination effect.
|
|
756
|
+
*
|
|
757
|
+
* @internal
|
|
758
|
+
*/
|
|
759
|
+
function finalizeTransitionResult(actorScope, previousSnapshot, [nextSnapshot, effects]) {
|
|
760
|
+
const becameTerminal = nextSnapshot.status === 'done' || nextSnapshot.status === 'error';
|
|
761
|
+
const wasTerminal = previousSnapshot?.status === 'done' || previousSnapshot?.status === 'error';
|
|
762
|
+
const hasTerminationEffect = effects.some(effect => typeof effect === 'object' && effect !== null && 'type' in effect && effect.type === XSTATE_TERMINATE);
|
|
763
|
+
return becameTerminal && !wasTerminal && !hasTerminationEffect ? [nextSnapshot, [...effects, createTerminationEffect(actorScope, nextSnapshot)]] : [nextSnapshot, effects];
|
|
556
764
|
}
|
|
557
765
|
|
|
558
766
|
/**
|
|
@@ -581,14 +789,19 @@ function deriveDeferredStarts(effects) {
|
|
|
581
789
|
return [...attachedStarts, ...childStarts];
|
|
582
790
|
}
|
|
583
791
|
function isBuiltInExecutableAction(action) {
|
|
584
|
-
return action
|
|
792
|
+
return action.kind === 'builtin';
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
/** Executes transition effects sequentially, awaiting each runtime operation. */
|
|
796
|
+
async function executeEffects(effects, runtime) {
|
|
797
|
+
for (const effect of effects) {
|
|
798
|
+
await effect.exec(runtime);
|
|
799
|
+
}
|
|
585
800
|
}
|
|
586
801
|
function resolveActionsWithContext(currentSnapshot, event, actorScope, actions) {
|
|
587
802
|
let intermediateSnapshot = currentSnapshot;
|
|
588
803
|
const executableActions = [];
|
|
589
804
|
for (const action of actions) {
|
|
590
|
-
const isInline = typeof action === 'function';
|
|
591
|
-
const resolvedAction = isInline ? action : typeof action === 'object' && 'action' in action && typeof action.action === 'function' ? action.action.bind(null, ...action.args) : false;
|
|
592
805
|
const actionArgs = {
|
|
593
806
|
context: intermediateSnapshot.context,
|
|
594
807
|
event,
|
|
@@ -600,20 +813,29 @@ function resolveActionsWithContext(currentSnapshot, event, actorScope, actions)
|
|
|
600
813
|
actions: currentSnapshot.machine.implementations.actions,
|
|
601
814
|
actorSources: currentSnapshot.machine.implementations.actorSources
|
|
602
815
|
};
|
|
816
|
+
const isInline = typeof action === 'function';
|
|
817
|
+
const actionRecord = getTransitionActionRecord(action);
|
|
818
|
+
const resolvedAction = isInline ? action : actionRecord ? actionRecord.action.bind(null, ...actionRecord.args) : false;
|
|
603
819
|
let actionParams = undefined;
|
|
604
820
|
if (typeof action === 'object' && action !== null) {
|
|
605
821
|
const {
|
|
606
822
|
type: _,
|
|
823
|
+
childUpdate: _childUpdate,
|
|
607
824
|
...emittedEventParams
|
|
608
825
|
} = action;
|
|
609
826
|
actionParams = emittedEventParams;
|
|
610
827
|
}
|
|
828
|
+
if (actionRecord?.childUpdate) {
|
|
829
|
+
intermediateSnapshot = applyChildUpdate(intermediateSnapshot, actionRecord.childUpdate, actorScope);
|
|
830
|
+
}
|
|
611
831
|
if (resolvedAction && '_special' in resolvedAction) {
|
|
612
832
|
executableActions.push({
|
|
833
|
+
kind: 'action',
|
|
834
|
+
exec: execCustomEffect,
|
|
613
835
|
type: typeof action === 'object' ? 'action' in action && typeof action.action === 'function' ? action.action.name ?? '(anonymous)' : action.type ?? '(anonymous)' : action.name || '(anonymous)',
|
|
614
836
|
params: actionParams,
|
|
615
837
|
args: [],
|
|
616
|
-
|
|
838
|
+
action: undefined
|
|
617
839
|
});
|
|
618
840
|
const specialAction = resolvedAction;
|
|
619
841
|
const res = specialAction(actionArgs, emptyEnqueueObject);
|
|
@@ -635,14 +857,26 @@ function resolveActionsWithContext(currentSnapshot, event, actorScope, actions)
|
|
|
635
857
|
}
|
|
636
858
|
if (!resolvedAction || !('resolve' in resolvedAction)) {
|
|
637
859
|
const builtInFields = typeof action === 'object' && action !== null && 'action' in action && typeof action.action === 'function' ? getBuiltInActionFields(action.action, action.args) : undefined;
|
|
860
|
+
const isEmittedEvent = typeof action === 'object' && action !== null && !actionRecord;
|
|
638
861
|
const executableAction = {
|
|
862
|
+
kind: builtInFields ? 'builtin' : isEmittedEvent ? 'emit' : 'action',
|
|
639
863
|
type: typeof action === 'object' ? 'action' in action && typeof action.action === 'function' ? action.action.name ?? '(anonymous)' : action.type : action.name || '(anonymous)',
|
|
640
|
-
params: actionParams,
|
|
864
|
+
params: builtInFields ? undefined : actionParams,
|
|
641
865
|
args: typeof action === 'object' && 'action' in action ? action.args : [],
|
|
642
|
-
|
|
866
|
+
...(builtInFields ? {} : isEmittedEvent ? {
|
|
867
|
+
source: actorScope.self,
|
|
868
|
+
event: action
|
|
869
|
+
} : {
|
|
870
|
+
action: actionRecord?.action ?? (isInline ? action : undefined)
|
|
871
|
+
}),
|
|
872
|
+
...(!builtInFields ? {
|
|
873
|
+
exec: isEmittedEvent ? execEmitEffect : execCustomEffect
|
|
874
|
+
} : {}),
|
|
643
875
|
...builtInFields
|
|
644
876
|
};
|
|
645
|
-
|
|
877
|
+
const typedExecutableAction = executableAction;
|
|
878
|
+
intermediateSnapshot = updateLogicalTimers(intermediateSnapshot, typedExecutableAction, actorScope);
|
|
879
|
+
executableActions.push(typedExecutableAction);
|
|
646
880
|
continue;
|
|
647
881
|
}
|
|
648
882
|
}
|
|
@@ -1432,6 +1666,17 @@ function microstep(transitions, currentSnapshot, actorScope, event, isInitial, i
|
|
|
1432
1666
|
return [[wrappedAction], undefined, undefined];
|
|
1433
1667
|
};
|
|
1434
1668
|
let nextState = currentSnapshot;
|
|
1669
|
+
const getInvokeStopActions = (stateNode, children) => {
|
|
1670
|
+
const actions = [];
|
|
1671
|
+
const enqueue = createTransitionEnqueue(actorScope, actions, []);
|
|
1672
|
+
for (const invokeDef of stateNode.invoke) {
|
|
1673
|
+
const child = children[invokeDef.id];
|
|
1674
|
+
if (child) {
|
|
1675
|
+
enqueue.stop(child);
|
|
1676
|
+
}
|
|
1677
|
+
}
|
|
1678
|
+
return actions;
|
|
1679
|
+
};
|
|
1435
1680
|
const exitStates = () => {
|
|
1436
1681
|
const statesToExit = computeExitSet(filteredTransitions, mutStateNodeSet, currentSnapshot, event, actorScope);
|
|
1437
1682
|
statesToExit.sort((a, b) => b.order - a.order);
|
|
@@ -1453,7 +1698,7 @@ function microstep(transitions, currentSnapshot, actorScope, event, isInitial, i
|
|
|
1453
1698
|
}
|
|
1454
1699
|
for (const exitStateNode of statesToExit) {
|
|
1455
1700
|
const stateInput = getStateInput(currentSnapshot, exitStateNode.id);
|
|
1456
|
-
const [exitActions, nextContext, internalEvents] = exitStateNode.exit ? getStateActionsAndContext(exitStateNode.exit, nextState.context, currentSnapshot.children, stateInput) : [[]];
|
|
1701
|
+
const [exitActions, nextContext, internalEvents] = exitStateNode.exit ? getStateActionsAndContext(exitStateNode.exit, nextState.context, currentSnapshot.children, stateInput) : [[], undefined, undefined];
|
|
1457
1702
|
if (internalEvents?.length) {
|
|
1458
1703
|
internalQueue.push(...internalEvents);
|
|
1459
1704
|
}
|
|
@@ -1465,12 +1710,11 @@ function microstep(transitions, currentSnapshot, actorScope, event, isInitial, i
|
|
|
1465
1710
|
const [resolvedState, resolvedActions] = resolveActionsWithContext(nextState, event, actorScope, exitActions);
|
|
1466
1711
|
nextState = resolvedState;
|
|
1467
1712
|
executableActions.push(...resolvedActions);
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
delete nextState.children[def.id];
|
|
1713
|
+
const invokeStopActions = getInvokeStopActions(exitStateNode, nextState.children);
|
|
1714
|
+
if (invokeStopActions.length) {
|
|
1715
|
+
const [stoppedState, stopEffects] = resolveActionsWithContext(nextState, event, actorScope, invokeStopActions);
|
|
1716
|
+
nextState = stoppedState;
|
|
1717
|
+
executableActions.push(...stopEffects);
|
|
1474
1718
|
}
|
|
1475
1719
|
mutStateNodeSet.delete(exitStateNode);
|
|
1476
1720
|
}
|
|
@@ -1641,7 +1885,7 @@ function microstep(transitions, currentSnapshot, actorScope, event, isInitial, i
|
|
|
1641
1885
|
}
|
|
1642
1886
|
const completedNodes = new Set();
|
|
1643
1887
|
const children = {
|
|
1644
|
-
...
|
|
1888
|
+
...nextState.children
|
|
1645
1889
|
};
|
|
1646
1890
|
for (const stateNodeToEnter of [...statesToEnter].sort((a, b) => a.order - b.order)) {
|
|
1647
1891
|
mutStateNodeSet.add(stateNodeToEnter);
|
|
@@ -1668,7 +1912,7 @@ function microstep(transitions, currentSnapshot, actorScope, event, isInitial, i
|
|
|
1668
1912
|
event,
|
|
1669
1913
|
output: getEventOutput(event)
|
|
1670
1914
|
}) : invokeDef.input;
|
|
1671
|
-
const actor =
|
|
1915
|
+
const actor = actorScope.system.createActorRef(logic, {
|
|
1672
1916
|
...invokeDef,
|
|
1673
1917
|
input,
|
|
1674
1918
|
parent: actorScope.self,
|
|
@@ -1797,6 +2041,12 @@ function microstep(transitions, currentSnapshot, actorScope, event, isInitial, i
|
|
|
1797
2041
|
const [resolvedState, resolvedActions] = resolveActionsWithContext(nextState, event, actorScope, allExitActions);
|
|
1798
2042
|
nextState = resolvedState;
|
|
1799
2043
|
executableActions.push(...resolvedActions);
|
|
2044
|
+
const [stoppedState, stopEffects] = stopChildren(nextState, event, actorScope);
|
|
2045
|
+
nextState = stoppedState;
|
|
2046
|
+
executableActions.push(...stopEffects);
|
|
2047
|
+
const [withoutTimers, cancelEffects] = cancelTimers(nextState, event, actorScope);
|
|
2048
|
+
nextState = withoutTimers;
|
|
2049
|
+
executableActions.push(...cancelEffects);
|
|
1800
2050
|
}
|
|
1801
2051
|
if (historyValue === currentSnapshot.historyValue && currentSnapshot._nodes.length === mutStateNodeSet.size && currentSnapshot._nodes.every(node => mutStateNodeSet.has(node))) {
|
|
1802
2052
|
// If context was changed (e.g. by entry actions during self-transition),
|
|
@@ -1870,9 +2120,27 @@ function getTransitionResult(transition, snapshot, event, actorScope, options) {
|
|
|
1870
2120
|
input: resolvedInput
|
|
1871
2121
|
};
|
|
1872
2122
|
}
|
|
1873
|
-
function macrostep(snapshot, event, actorScope, internalQueue) {
|
|
2123
|
+
function macrostep(snapshot, event, actorScope, internalQueue, initialMicrosteps = []) {
|
|
1874
2124
|
let nextSnapshot = snapshot;
|
|
1875
|
-
const microsteps =
|
|
2125
|
+
const microsteps = initialMicrosteps.slice();
|
|
2126
|
+
function completeMacrostep() {
|
|
2127
|
+
const effects = microsteps.flatMap(([, actions]) => actions);
|
|
2128
|
+
const starts = deriveDeferredStarts(effects);
|
|
2129
|
+
const shouldTerminate = (snapshot.status === 'active' || initialMicrosteps.length > 0) && (nextSnapshot.status === 'done' || nextSnapshot.status === 'error');
|
|
2130
|
+
if (starts.length || shouldTerminate) {
|
|
2131
|
+
const terminalEffects = shouldTerminate ? [...starts, createTerminationEffect(actorScope, nextSnapshot)] : starts;
|
|
2132
|
+
if (microsteps.length) {
|
|
2133
|
+
const [lastSnapshot, lastEffects] = microsteps.at(-1);
|
|
2134
|
+
microsteps[microsteps.length - 1] = [lastSnapshot, [...lastEffects, ...terminalEffects]];
|
|
2135
|
+
} else {
|
|
2136
|
+
microsteps.push([nextSnapshot, terminalEffects]);
|
|
2137
|
+
}
|
|
2138
|
+
}
|
|
2139
|
+
return {
|
|
2140
|
+
snapshot: nextSnapshot,
|
|
2141
|
+
microsteps
|
|
2142
|
+
};
|
|
2143
|
+
}
|
|
1876
2144
|
function addMicrostep(step, transitions) {
|
|
1877
2145
|
// collect microsteps; surfaced on the enclosing '@xstate.transition' event
|
|
1878
2146
|
// via its `microsteps[]` facet (there is no standalone microstep event)
|
|
@@ -1884,20 +2152,39 @@ function macrostep(snapshot, event, actorScope, internalQueue) {
|
|
|
1884
2152
|
|
|
1885
2153
|
// Handle stop event
|
|
1886
2154
|
if (event.type === XSTATE_STOP) {
|
|
1887
|
-
|
|
2155
|
+
const [stoppedChildrenSnapshot, stopEffects] = stopChildren(nextSnapshot, event, actorScope);
|
|
2156
|
+
const [withoutTimers, cancelEffects] = cancelTimers(stoppedChildrenSnapshot, event, actorScope);
|
|
2157
|
+
nextSnapshot = cloneMachineSnapshot(withoutTimers, {
|
|
1888
2158
|
status: 'stopped'
|
|
1889
2159
|
});
|
|
1890
|
-
addMicrostep([nextSnapshot, []], []);
|
|
1891
|
-
return
|
|
1892
|
-
snapshot: nextSnapshot,
|
|
1893
|
-
microsteps
|
|
1894
|
-
};
|
|
2160
|
+
addMicrostep([nextSnapshot, [...stopEffects, ...cancelEffects]], []);
|
|
2161
|
+
return completeMacrostep();
|
|
1895
2162
|
}
|
|
1896
2163
|
let nextEvent = event;
|
|
2164
|
+
if (event.type === XSTATE_TIMER) {
|
|
2165
|
+
const timer = nextSnapshot.timers[event.id];
|
|
2166
|
+
if (!timer) {
|
|
2167
|
+
return completeMacrostep();
|
|
2168
|
+
}
|
|
2169
|
+
const timers = {
|
|
2170
|
+
...nextSnapshot.timers
|
|
2171
|
+
};
|
|
2172
|
+
delete timers[timer.id];
|
|
2173
|
+
nextSnapshot = cloneMachineSnapshot(nextSnapshot, {
|
|
2174
|
+
timers
|
|
2175
|
+
});
|
|
2176
|
+
if (timer.type === '@xstate.raise') {
|
|
2177
|
+
internalQueue.push(timer.event);
|
|
2178
|
+
addMicrostep([nextSnapshot, []], []);
|
|
2179
|
+
} else {
|
|
2180
|
+
const target = timer.target === 'self' ? actorScope.self : timer.target;
|
|
2181
|
+
addMicrostep([nextSnapshot, [createSendToEffect(actorScope, target, timer.event)]], []);
|
|
2182
|
+
}
|
|
2183
|
+
}
|
|
1897
2184
|
|
|
1898
2185
|
// Assume the state is at rest (no raised events)
|
|
1899
2186
|
// Determine the next state based on the next microstep
|
|
1900
|
-
if (nextEvent.type !== XSTATE_INIT) {
|
|
2187
|
+
if (nextEvent.type !== XSTATE_INIT && nextEvent.type !== XSTATE_TIMER) {
|
|
1901
2188
|
const currentEvent = nextEvent;
|
|
1902
2189
|
const isErr = isErrorEvent(currentEvent);
|
|
1903
2190
|
const transitions = nextSnapshot.machine.getTransitionData(nextSnapshot, currentEvent, actorScope.self);
|
|
@@ -1910,10 +2197,7 @@ function macrostep(snapshot, event, actorScope, internalQueue) {
|
|
|
1910
2197
|
error: currentEvent.error
|
|
1911
2198
|
});
|
|
1912
2199
|
addMicrostep([nextSnapshot, []], []);
|
|
1913
|
-
return
|
|
1914
|
-
snapshot: nextSnapshot,
|
|
1915
|
-
microsteps
|
|
1916
|
-
};
|
|
2200
|
+
return completeMacrostep();
|
|
1917
2201
|
}
|
|
1918
2202
|
const step = microstep(transitions, snapshot, actorScope, nextEvent, false,
|
|
1919
2203
|
// isInitial
|
|
@@ -1951,13 +2235,7 @@ function macrostep(snapshot, event, actorScope, internalQueue) {
|
|
|
1951
2235
|
shouldSelectEventlessTransitions = nextSnapshot !== previousState;
|
|
1952
2236
|
addMicrostep(step, enabledTransitions);
|
|
1953
2237
|
}
|
|
1954
|
-
|
|
1955
|
-
stopChildren(nextSnapshot, actorScope);
|
|
1956
|
-
}
|
|
1957
|
-
return {
|
|
1958
|
-
snapshot: nextSnapshot,
|
|
1959
|
-
microsteps
|
|
1960
|
-
};
|
|
2238
|
+
return completeMacrostep();
|
|
1961
2239
|
}
|
|
1962
2240
|
|
|
1963
2241
|
/**
|
|
@@ -2017,17 +2295,29 @@ function transitionToHasEffect(transitionTo, context, event, snapshot, self, imp
|
|
|
2017
2295
|
}
|
|
2018
2296
|
return res !== undefined;
|
|
2019
2297
|
}
|
|
2020
|
-
function stopChildren(snapshot, actorScope) {
|
|
2298
|
+
function stopChildren(snapshot, event, actorScope) {
|
|
2021
2299
|
let children;
|
|
2022
2300
|
if (!snapshot.children || (children = Object.values(snapshot.children).filter(Boolean)).length === 0) {
|
|
2023
|
-
return snapshot;
|
|
2301
|
+
return [snapshot, []];
|
|
2024
2302
|
}
|
|
2303
|
+
const actions = [];
|
|
2304
|
+
const enqueue = createTransitionEnqueue(actorScope, actions, []);
|
|
2025
2305
|
for (const child of children) {
|
|
2026
|
-
|
|
2306
|
+
enqueue.stop(child);
|
|
2027
2307
|
}
|
|
2028
|
-
return
|
|
2029
|
-
|
|
2030
|
-
|
|
2308
|
+
return resolveActionsWithContext(snapshot, event, actorScope, actions);
|
|
2309
|
+
}
|
|
2310
|
+
function cancelTimers(snapshot, event, actorScope) {
|
|
2311
|
+
const timerIds = Object.keys(snapshot.timers);
|
|
2312
|
+
if (!timerIds.length) {
|
|
2313
|
+
return [snapshot, []];
|
|
2314
|
+
}
|
|
2315
|
+
const actions = [];
|
|
2316
|
+
const enqueue = createTransitionEnqueue(actorScope, actions, []);
|
|
2317
|
+
for (const id of timerIds) {
|
|
2318
|
+
enqueue.cancel(id);
|
|
2319
|
+
}
|
|
2320
|
+
return resolveActionsWithContext(snapshot, event, actorScope, actions);
|
|
2031
2321
|
}
|
|
2032
2322
|
function selectEventlessTransitions(snapshot, event, actorScope) {
|
|
2033
2323
|
const enabledTransitionSet = new Set();
|
|
@@ -2072,6 +2362,129 @@ function evaluateCandidate(candidate, event, snapshot, stateNode, self) {
|
|
|
2072
2362
|
return true;
|
|
2073
2363
|
}
|
|
2074
2364
|
|
|
2365
|
+
/** @internal */
|
|
2366
|
+
const snapshotActorRef = Symbol('xstate.snapshotActorRef');
|
|
2367
|
+
|
|
2368
|
+
/** Snapshot-scoped actor identity and system view. @internal */
|
|
2369
|
+
|
|
2370
|
+
function getSnapshotChildren(snapshot) {
|
|
2371
|
+
return 'children' in snapshot && snapshot.children ? snapshot.children : {};
|
|
2372
|
+
}
|
|
2373
|
+
|
|
2374
|
+
/**
|
|
2375
|
+
* Creates the isolated receptionist/id view used by a pure branch. Scheduling,
|
|
2376
|
+
* relaying and clocks remain delegated to the snapshot's execution system.
|
|
2377
|
+
*
|
|
2378
|
+
* @internal
|
|
2379
|
+
*/
|
|
2380
|
+
function createSnapshotSystem(baseSystem, self, children, baseState) {
|
|
2381
|
+
const registeredActors = new Map(baseState?.children ?? baseSystem.children);
|
|
2382
|
+
const keyedActors = new Map(baseState?.keyedActors ?? Object.entries(baseSystem.getAll()).filter(entry => !!entry[1]));
|
|
2383
|
+
const reverseKeyedActors = new WeakMap();
|
|
2384
|
+
let booksSelf = true;
|
|
2385
|
+
const system = {
|
|
2386
|
+
...baseSystem,
|
|
2387
|
+
children: registeredActors,
|
|
2388
|
+
keyedActors,
|
|
2389
|
+
reverseKeyedActors,
|
|
2390
|
+
_snapshot: {
|
|
2391
|
+
...(baseState?.snapshot ?? baseSystem._snapshot)
|
|
2392
|
+
},
|
|
2393
|
+
_bookId: () => {
|
|
2394
|
+
if (booksSelf) {
|
|
2395
|
+
booksSelf = false;
|
|
2396
|
+
return self.sessionId ?? self.id;
|
|
2397
|
+
}
|
|
2398
|
+
return `x:${system._snapshot._nextActorId++}`;
|
|
2399
|
+
},
|
|
2400
|
+
_register: (sessionId, actor) => {
|
|
2401
|
+
registeredActors.set(sessionId, actor);
|
|
2402
|
+
return sessionId;
|
|
2403
|
+
},
|
|
2404
|
+
_unregister: actor => {
|
|
2405
|
+
if (actor.sessionId) {
|
|
2406
|
+
registeredActors.delete(actor.sessionId);
|
|
2407
|
+
}
|
|
2408
|
+
const registryKey = reverseKeyedActors.get(actor);
|
|
2409
|
+
if (registryKey !== undefined && keyedActors.get(registryKey) === actor) {
|
|
2410
|
+
keyedActors.delete(registryKey);
|
|
2411
|
+
}
|
|
2412
|
+
reverseKeyedActors.delete(actor);
|
|
2413
|
+
},
|
|
2414
|
+
_set: (registryKey, actor) => {
|
|
2415
|
+
const existing = keyedActors.get(registryKey);
|
|
2416
|
+
if (existing && existing !== actor) {
|
|
2417
|
+
throw new Error(`Actor with registry key '${String(registryKey)}' already exists.`);
|
|
2418
|
+
}
|
|
2419
|
+
keyedActors.set(registryKey, actor);
|
|
2420
|
+
reverseKeyedActors.set(actor, registryKey);
|
|
2421
|
+
},
|
|
2422
|
+
get: registryKey => keyedActors.get(registryKey),
|
|
2423
|
+
getAll: () => Object.fromEntries(keyedActors),
|
|
2424
|
+
// Pure transition resolution must not leak topology inspection events into
|
|
2425
|
+
// a live runtime system.
|
|
2426
|
+
_sendInspectionEvent: () => {}
|
|
2427
|
+
};
|
|
2428
|
+
for (const [registryKey, actor] of keyedActors) {
|
|
2429
|
+
reverseKeyedActors.set(actor, registryKey);
|
|
2430
|
+
}
|
|
2431
|
+
for (const actor of Object.values(children)) {
|
|
2432
|
+
if (!actor) {
|
|
2433
|
+
continue;
|
|
2434
|
+
}
|
|
2435
|
+
if (actor.sessionId) {
|
|
2436
|
+
registeredActors.set(actor.sessionId, actor);
|
|
2437
|
+
}
|
|
2438
|
+
const registryKey = actor.registryKey;
|
|
2439
|
+
if (registryKey) {
|
|
2440
|
+
keyedActors.set(registryKey, actor);
|
|
2441
|
+
reverseKeyedActors.set(actor, registryKey);
|
|
2442
|
+
}
|
|
2443
|
+
}
|
|
2444
|
+
return system;
|
|
2445
|
+
}
|
|
2446
|
+
|
|
2447
|
+
/** Returns the actor identity associated with a transition snapshot. @internal */
|
|
2448
|
+
function getSnapshotActorRef(snapshot) {
|
|
2449
|
+
return snapshot[snapshotActorRef];
|
|
2450
|
+
}
|
|
2451
|
+
|
|
2452
|
+
/**
|
|
2453
|
+
* Copies an internal actor association without rebuilding its system view.
|
|
2454
|
+
*
|
|
2455
|
+
* @internal
|
|
2456
|
+
*/
|
|
2457
|
+
function copySnapshotActorRef(source, target) {
|
|
2458
|
+
const ref = getSnapshotActorRef(source);
|
|
2459
|
+
if (ref) {
|
|
2460
|
+
Object.defineProperty(target, snapshotActorRef, {
|
|
2461
|
+
configurable: true,
|
|
2462
|
+
value: ref
|
|
2463
|
+
});
|
|
2464
|
+
}
|
|
2465
|
+
}
|
|
2466
|
+
|
|
2467
|
+
/**
|
|
2468
|
+
* Associates a transition snapshot with an actor and an immutable system view.
|
|
2469
|
+
* The internal association is excluded from enumeration and persistence.
|
|
2470
|
+
*
|
|
2471
|
+
* @internal
|
|
2472
|
+
*/
|
|
2473
|
+
function setSnapshotActorRef(snapshot, actor, baseSystem = actor.system) {
|
|
2474
|
+
const system = createSnapshotSystem(baseSystem, actor, getSnapshotChildren(snapshot));
|
|
2475
|
+
Object.defineProperty(snapshot, snapshotActorRef, {
|
|
2476
|
+
configurable: true,
|
|
2477
|
+
value: {
|
|
2478
|
+
actor,
|
|
2479
|
+
systemState: {
|
|
2480
|
+
children: system.children,
|
|
2481
|
+
keyedActors: system.keyedActors,
|
|
2482
|
+
snapshot: system._snapshot
|
|
2483
|
+
}
|
|
2484
|
+
}
|
|
2485
|
+
});
|
|
2486
|
+
}
|
|
2487
|
+
|
|
2075
2488
|
function isMachineSnapshot(value) {
|
|
2076
2489
|
return !!value && typeof value === 'object' && 'machine' in value && 'value' in value;
|
|
2077
2490
|
}
|
|
@@ -2094,6 +2507,7 @@ const machineSnapshotToJSON = function toJSON() {
|
|
|
2094
2507
|
const {
|
|
2095
2508
|
_nodes: nodes,
|
|
2096
2509
|
_stateInputs,
|
|
2510
|
+
[snapshotActorRef]: _actorRef,
|
|
2097
2511
|
tags,
|
|
2098
2512
|
machine,
|
|
2099
2513
|
getMeta,
|
|
@@ -2130,8 +2544,8 @@ function collectTags(stateNodes) {
|
|
|
2130
2544
|
}
|
|
2131
2545
|
return tags;
|
|
2132
2546
|
}
|
|
2133
|
-
function createMachineSnapshot(config, machine) {
|
|
2134
|
-
|
|
2547
|
+
function createMachineSnapshot(config, machine, actorRef) {
|
|
2548
|
+
const snapshot = {
|
|
2135
2549
|
status: config.status,
|
|
2136
2550
|
output: config.output,
|
|
2137
2551
|
error: config.error,
|
|
@@ -2141,8 +2555,10 @@ function createMachineSnapshot(config, machine) {
|
|
|
2141
2555
|
value: config.value ?? getStateValue(machine.root, config._nodes),
|
|
2142
2556
|
tags: collectTags(config._nodes),
|
|
2143
2557
|
children: config.children,
|
|
2558
|
+
timers: config.timers ?? {},
|
|
2144
2559
|
historyValue: config.historyValue || {},
|
|
2145
2560
|
_stateInputs: config._stateInputs || {},
|
|
2561
|
+
_nextTimerId: config._nextTimerId ?? 0,
|
|
2146
2562
|
matches: machineSnapshotMatches,
|
|
2147
2563
|
hasTag: machineSnapshotHasTag,
|
|
2148
2564
|
can: machineSnapshotCan,
|
|
@@ -2150,6 +2566,10 @@ function createMachineSnapshot(config, machine) {
|
|
|
2150
2566
|
getInputs: machineSnapshotGetInputs,
|
|
2151
2567
|
toJSON: machineSnapshotToJSON
|
|
2152
2568
|
};
|
|
2569
|
+
if (actorRef) {
|
|
2570
|
+
setSnapshotActorRef(snapshot, actorRef);
|
|
2571
|
+
}
|
|
2572
|
+
return snapshot;
|
|
2153
2573
|
}
|
|
2154
2574
|
function cloneMachineSnapshot(snapshot, config = {}) {
|
|
2155
2575
|
const configWithSnapshot = {
|
|
@@ -2157,7 +2577,7 @@ function cloneMachineSnapshot(snapshot, config = {}) {
|
|
|
2157
2577
|
...config
|
|
2158
2578
|
};
|
|
2159
2579
|
if ((config._nodes ?? snapshot._nodes) === snapshot._nodes) {
|
|
2160
|
-
|
|
2580
|
+
const clonedSnapshot = {
|
|
2161
2581
|
status: configWithSnapshot.status,
|
|
2162
2582
|
output: configWithSnapshot.output,
|
|
2163
2583
|
error: configWithSnapshot.error,
|
|
@@ -2167,8 +2587,10 @@ function cloneMachineSnapshot(snapshot, config = {}) {
|
|
|
2167
2587
|
value: snapshot.value,
|
|
2168
2588
|
tags: snapshot.tags,
|
|
2169
2589
|
children: configWithSnapshot.children,
|
|
2590
|
+
timers: configWithSnapshot.timers ?? {},
|
|
2170
2591
|
historyValue: configWithSnapshot.historyValue || {},
|
|
2171
2592
|
_stateInputs: configWithSnapshot._stateInputs || {},
|
|
2593
|
+
_nextTimerId: configWithSnapshot._nextTimerId ?? 0,
|
|
2172
2594
|
matches: machineSnapshotMatches,
|
|
2173
2595
|
hasTag: machineSnapshotHasTag,
|
|
2174
2596
|
can: machineSnapshotCan,
|
|
@@ -2176,11 +2598,15 @@ function cloneMachineSnapshot(snapshot, config = {}) {
|
|
|
2176
2598
|
getInputs: machineSnapshotGetInputs,
|
|
2177
2599
|
toJSON: machineSnapshotToJSON
|
|
2178
2600
|
};
|
|
2601
|
+
copySnapshotActorRef(snapshot, clonedSnapshot);
|
|
2602
|
+
return clonedSnapshot;
|
|
2179
2603
|
}
|
|
2180
|
-
|
|
2604
|
+
const clonedSnapshot = createMachineSnapshot({
|
|
2181
2605
|
...configWithSnapshot,
|
|
2182
2606
|
value: undefined
|
|
2183
2607
|
}, snapshot.machine);
|
|
2608
|
+
copySnapshotActorRef(snapshot, clonedSnapshot);
|
|
2609
|
+
return clonedSnapshot;
|
|
2184
2610
|
}
|
|
2185
2611
|
function serializeHistoryValue(historyValue) {
|
|
2186
2612
|
const result = {};
|
|
@@ -2201,6 +2627,7 @@ function getPersistedSnapshot(snapshot, options) {
|
|
|
2201
2627
|
tags,
|
|
2202
2628
|
machine,
|
|
2203
2629
|
children,
|
|
2630
|
+
timers,
|
|
2204
2631
|
context,
|
|
2205
2632
|
can,
|
|
2206
2633
|
hasTag,
|
|
@@ -2211,6 +2638,7 @@ function getPersistedSnapshot(snapshot, options) {
|
|
|
2211
2638
|
...jsonValues
|
|
2212
2639
|
} = snapshot;
|
|
2213
2640
|
const childrenJson = {};
|
|
2641
|
+
const timersJson = {};
|
|
2214
2642
|
for (const id in children) {
|
|
2215
2643
|
const child = children[id];
|
|
2216
2644
|
if (typeof child.src !== 'string' && (!options || !('__unsafeAllowInlineActors' in options))) {
|
|
@@ -2223,10 +2651,36 @@ function getPersistedSnapshot(snapshot, options) {
|
|
|
2223
2651
|
syncSnapshot: child._syncSnapshot
|
|
2224
2652
|
};
|
|
2225
2653
|
}
|
|
2654
|
+
for (const id in timers) {
|
|
2655
|
+
const timer = timers[id];
|
|
2656
|
+
let target;
|
|
2657
|
+
if (timer.target === 'self') {
|
|
2658
|
+
target = 'self';
|
|
2659
|
+
} else {
|
|
2660
|
+
const childId = Object.entries(children).find(([, child]) => child === timer.target)?.[0];
|
|
2661
|
+
if (childId) {
|
|
2662
|
+
target = childId;
|
|
2663
|
+
} else if (timer.target === getSnapshotActorRef(snapshot)?.actor._parent) {
|
|
2664
|
+
target = {
|
|
2665
|
+
type: 'parent'
|
|
2666
|
+
};
|
|
2667
|
+
} else {
|
|
2668
|
+
throw new Error(`Unable to persist timer '${id}': target actor '${timer.target.id}' is no longer addressable from this snapshot.`);
|
|
2669
|
+
}
|
|
2670
|
+
}
|
|
2671
|
+
timersJson[id] = {
|
|
2672
|
+
id: timer.id,
|
|
2673
|
+
delay: timer.delay,
|
|
2674
|
+
type: timer.type,
|
|
2675
|
+
event: timer.event,
|
|
2676
|
+
target
|
|
2677
|
+
};
|
|
2678
|
+
}
|
|
2226
2679
|
const persisted = {
|
|
2227
2680
|
...jsonValues,
|
|
2228
2681
|
context: persistContext(context),
|
|
2229
2682
|
children: childrenJson,
|
|
2683
|
+
timers: timersJson,
|
|
2230
2684
|
historyValue: serializeHistoryValue(jsonValues.historyValue)
|
|
2231
2685
|
};
|
|
2232
2686
|
if (_stateInputs && Object.keys(_stateInputs).length > 0) {
|
|
@@ -2493,11 +2947,17 @@ function matchesEventDescriptor(eventType, descriptor) {
|
|
|
2493
2947
|
return true;
|
|
2494
2948
|
}
|
|
2495
2949
|
|
|
2496
|
-
|
|
2950
|
+
/**
|
|
2951
|
+
* Runtime operations used to execute effects.
|
|
2952
|
+
*
|
|
2953
|
+
* An external interpreter can override these operations while the default actor
|
|
2954
|
+
* system provides the local in-memory implementation.
|
|
2955
|
+
*/
|
|
2956
|
+
|
|
2957
|
+
function createScheduledTimerId(actor, id) {
|
|
2497
2958
|
return `${actor.sessionId}.${id}`;
|
|
2498
2959
|
}
|
|
2499
2960
|
function createRuntimeSystem(rootActor, options) {
|
|
2500
|
-
let idCounter = 0;
|
|
2501
2961
|
const children = new Map();
|
|
2502
2962
|
const keyedActors = new Map();
|
|
2503
2963
|
const reverseKeyedActors = new WeakMap();
|
|
@@ -2525,42 +2985,50 @@ function createRuntimeSystem(rootActor, options) {
|
|
|
2525
2985
|
});
|
|
2526
2986
|
};
|
|
2527
2987
|
const scheduler = {
|
|
2528
|
-
schedule: (source,
|
|
2529
|
-
|
|
2530
|
-
|
|
2988
|
+
schedule: (source, id, delay) => {
|
|
2989
|
+
const existingId = createScheduledTimerId(source, id);
|
|
2990
|
+
if (timerMap[existingId] !== undefined) {
|
|
2991
|
+
scheduler.cancel(source, id);
|
|
2992
|
+
}
|
|
2993
|
+
const timer = source.getSnapshot()?.timers?.[id];
|
|
2994
|
+
if (timer) {
|
|
2995
|
+
const target = timer.target === 'self' ? source : timer.target;
|
|
2996
|
+
recordSent(source, target, timer.event, delay, id);
|
|
2997
|
+
}
|
|
2998
|
+
const scheduledAt = Date.now();
|
|
2999
|
+
const scheduledTimer = {
|
|
2531
3000
|
source,
|
|
2532
|
-
target,
|
|
2533
|
-
event,
|
|
2534
3001
|
delay,
|
|
2535
3002
|
id,
|
|
2536
|
-
|
|
3003
|
+
scheduledAt,
|
|
3004
|
+
dueAt: scheduledAt + delay
|
|
2537
3005
|
};
|
|
2538
|
-
const
|
|
2539
|
-
system._snapshot.
|
|
3006
|
+
const scheduledTimerId = createScheduledTimerId(source, id);
|
|
3007
|
+
system._snapshot._scheduledTimers[scheduledTimerId] = scheduledTimer;
|
|
2540
3008
|
const timeout = clock.setTimeout(() => {
|
|
2541
|
-
delete timerMap[
|
|
2542
|
-
delete system._snapshot.
|
|
2543
|
-
|
|
2544
|
-
|
|
2545
|
-
|
|
2546
|
-
|
|
3009
|
+
delete timerMap[scheduledTimerId];
|
|
3010
|
+
delete system._snapshot._scheduledTimers[scheduledTimerId];
|
|
3011
|
+
deliver(source, source, {
|
|
3012
|
+
type: XSTATE_TIMER,
|
|
3013
|
+
id
|
|
3014
|
+
});
|
|
2547
3015
|
}, delay);
|
|
2548
|
-
timerMap[
|
|
3016
|
+
timerMap[scheduledTimerId] = timeout;
|
|
2549
3017
|
},
|
|
2550
3018
|
cancel: (source, id) => {
|
|
2551
|
-
const
|
|
2552
|
-
const timeout = timerMap[
|
|
2553
|
-
delete timerMap[
|
|
2554
|
-
delete system._snapshot.
|
|
3019
|
+
const scheduledTimerId = createScheduledTimerId(source, id);
|
|
3020
|
+
const timeout = timerMap[scheduledTimerId];
|
|
3021
|
+
delete timerMap[scheduledTimerId];
|
|
3022
|
+
delete system._snapshot._scheduledTimers[scheduledTimerId];
|
|
2555
3023
|
if (timeout !== undefined) {
|
|
2556
3024
|
clock.clearTimeout(timeout);
|
|
2557
3025
|
}
|
|
2558
3026
|
},
|
|
2559
3027
|
cancelAll: actor => {
|
|
2560
|
-
for (const
|
|
2561
|
-
const
|
|
2562
|
-
if (
|
|
2563
|
-
scheduler.cancel(actor,
|
|
3028
|
+
for (const scheduledTimerId in system._snapshot._scheduledTimers) {
|
|
3029
|
+
const scheduledTimer = system._snapshot._scheduledTimers[scheduledTimerId];
|
|
3030
|
+
if (scheduledTimer.source === actor) {
|
|
3031
|
+
scheduler.cancel(actor, scheduledTimer.id);
|
|
2564
3032
|
}
|
|
2565
3033
|
}
|
|
2566
3034
|
}
|
|
@@ -2593,9 +3061,10 @@ function createRuntimeSystem(rootActor, options) {
|
|
|
2593
3061
|
reverseKeyedActors,
|
|
2594
3062
|
keyedActors,
|
|
2595
3063
|
_snapshot: {
|
|
2596
|
-
|
|
3064
|
+
_scheduledTimers: (options?.snapshot && options.snapshot.scheduler) ?? {},
|
|
3065
|
+
_nextActorId: 0
|
|
2597
3066
|
},
|
|
2598
|
-
_bookId: () => `x:${
|
|
3067
|
+
_bookId: () => `x:${system._snapshot._nextActorId++}`,
|
|
2599
3068
|
_register: (sessionId, actor) => {
|
|
2600
3069
|
children.set(sessionId, actor);
|
|
2601
3070
|
return sessionId;
|
|
@@ -2604,7 +3073,9 @@ function createRuntimeSystem(rootActor, options) {
|
|
|
2604
3073
|
children.delete(actor.sessionId);
|
|
2605
3074
|
const registryKey = reverseKeyedActors.get(actor);
|
|
2606
3075
|
if (registryKey !== undefined) {
|
|
2607
|
-
keyedActors.
|
|
3076
|
+
if (keyedActors.get(registryKey) === actor) {
|
|
3077
|
+
keyedActors.delete(registryKey);
|
|
3078
|
+
}
|
|
2608
3079
|
reverseKeyedActors.delete(actor);
|
|
2609
3080
|
}
|
|
2610
3081
|
},
|
|
@@ -2632,35 +3103,48 @@ function createRuntimeSystem(rootActor, options) {
|
|
|
2632
3103
|
};
|
|
2633
3104
|
},
|
|
2634
3105
|
_sendInspectionEvent: sendInspectionEvent,
|
|
2635
|
-
|
|
3106
|
+
createActorRef: options.createActorRef,
|
|
3107
|
+
spawnActor: () => {},
|
|
3108
|
+
startActor: actor => {
|
|
3109
|
+
actor.start();
|
|
3110
|
+
},
|
|
3111
|
+
stopActor: actor => {
|
|
3112
|
+
actor._stop();
|
|
3113
|
+
},
|
|
3114
|
+
terminateActor: (actor, termination) => {
|
|
3115
|
+
actor._terminate(termination);
|
|
3116
|
+
},
|
|
3117
|
+
sendEvent: (source, target, event) => {
|
|
2636
3118
|
recordSent(source, target, event);
|
|
2637
3119
|
deliver(source, target, event);
|
|
2638
3120
|
},
|
|
3121
|
+
emitEvent: (source, event) => source._emit(event),
|
|
3122
|
+
scheduleTimer: (source, id, delay) => scheduler.schedule(source, id, delay),
|
|
3123
|
+
cancelTimer: (source, id) => scheduler.cancel(source, id),
|
|
3124
|
+
cancelAllTimers: source => scheduler.cancelAll(source),
|
|
3125
|
+
_relay: (source, target, event) => system.sendEvent(source, target, event),
|
|
2639
3126
|
scheduler,
|
|
2640
3127
|
getSnapshot: () => {
|
|
2641
3128
|
return {
|
|
2642
|
-
|
|
2643
|
-
...system._snapshot.
|
|
3129
|
+
_scheduledTimers: {
|
|
3130
|
+
...system._snapshot._scheduledTimers
|
|
2644
3131
|
}
|
|
2645
3132
|
};
|
|
2646
3133
|
},
|
|
2647
3134
|
start: () => {
|
|
2648
|
-
const
|
|
2649
|
-
system._snapshot.
|
|
2650
|
-
for (const scheduledId in
|
|
3135
|
+
const scheduledTimers = system._snapshot._scheduledTimers;
|
|
3136
|
+
system._snapshot._scheduledTimers = {};
|
|
3137
|
+
for (const scheduledId in scheduledTimers) {
|
|
2651
3138
|
const {
|
|
2652
3139
|
source,
|
|
2653
|
-
|
|
2654
|
-
event,
|
|
2655
|
-
delay,
|
|
3140
|
+
dueAt,
|
|
2656
3141
|
id
|
|
2657
|
-
} =
|
|
2658
|
-
|
|
3142
|
+
} = scheduledTimers[scheduledId];
|
|
3143
|
+
system.scheduleTimer(source, id, Math.max(0, dueAt - Date.now()));
|
|
2659
3144
|
}
|
|
2660
3145
|
},
|
|
2661
3146
|
_clock: clock,
|
|
2662
|
-
_logger: logger
|
|
2663
|
-
_timerStrategy: options.timers
|
|
3147
|
+
_logger: logger
|
|
2664
3148
|
};
|
|
2665
3149
|
return system;
|
|
2666
3150
|
}
|
|
@@ -2695,22 +3179,13 @@ function safeCall(fn, arg) {
|
|
|
2695
3179
|
reportUnhandledError(err);
|
|
2696
3180
|
}
|
|
2697
3181
|
}
|
|
2698
|
-
function isExecutableActionObject(effect) {
|
|
2699
|
-
return typeof effect === 'object' && effect !== null && 'args' in effect && 'exec' in effect;
|
|
2700
|
-
}
|
|
2701
3182
|
function executeExecutableEffects(effects, actorScope) {
|
|
2702
3183
|
if (!effects?.length) {
|
|
2703
|
-
return
|
|
3184
|
+
return;
|
|
2704
3185
|
}
|
|
2705
|
-
const logicEffects = [];
|
|
2706
3186
|
for (const effect of effects) {
|
|
2707
|
-
|
|
2708
|
-
actorScope.actionExecutor(effect);
|
|
2709
|
-
} else {
|
|
2710
|
-
logicEffects.push(effect);
|
|
2711
|
-
}
|
|
3187
|
+
actorScope.actionExecutor(effect);
|
|
2712
3188
|
}
|
|
2713
|
-
return logicEffects;
|
|
2714
3189
|
}
|
|
2715
3190
|
|
|
2716
3191
|
/**
|
|
@@ -2776,8 +3251,7 @@ class Actor {
|
|
|
2776
3251
|
this.src = void 0;
|
|
2777
3252
|
// array of functions to defer
|
|
2778
3253
|
this._deferred = [];
|
|
2779
|
-
|
|
2780
|
-
this._pendingEffects = void 0;
|
|
3254
|
+
this._restored = false;
|
|
2781
3255
|
const resolvedOptions = {
|
|
2782
3256
|
...defaultOptions,
|
|
2783
3257
|
...options
|
|
@@ -2794,7 +3268,7 @@ class Actor {
|
|
|
2794
3268
|
this.system = parent ? parent.system : resolvedOptions._systemRef?.current ?? createRuntimeSystem(this, {
|
|
2795
3269
|
clock,
|
|
2796
3270
|
logger,
|
|
2797
|
-
|
|
3271
|
+
createActorRef: (childLogic, childOptions) => createActor(childLogic, childOptions)
|
|
2798
3272
|
});
|
|
2799
3273
|
if (!parent && resolvedOptions._systemRef && !resolvedOptions._systemRef.current) {
|
|
2800
3274
|
resolvedOptions._systemRef.current = this.system;
|
|
@@ -2827,15 +3301,7 @@ class Actor {
|
|
|
2827
3301
|
}
|
|
2828
3302
|
child._stop();
|
|
2829
3303
|
},
|
|
2830
|
-
emit: emittedEvent =>
|
|
2831
|
-
for (const listeners of [this.eventListeners.get(emittedEvent.type), this.eventListeners.get('*')]) {
|
|
2832
|
-
if (listeners) {
|
|
2833
|
-
for (const handler of listeners) {
|
|
2834
|
-
safeCall(handler, emittedEvent);
|
|
2835
|
-
}
|
|
2836
|
-
}
|
|
2837
|
-
}
|
|
2838
|
-
},
|
|
3304
|
+
emit: emittedEvent => this.system.emitEvent(this, emittedEvent),
|
|
2839
3305
|
actionExecutor: action => {
|
|
2840
3306
|
const exec = () => {
|
|
2841
3307
|
// Record every executed action for the '@xstate.transition' inspection
|
|
@@ -2844,13 +3310,10 @@ class Actor {
|
|
|
2844
3310
|
type: action.type,
|
|
2845
3311
|
params: action.params
|
|
2846
3312
|
});
|
|
2847
|
-
if (!action.exec) {
|
|
2848
|
-
return;
|
|
2849
|
-
}
|
|
2850
3313
|
const saveExecutingCustomAction = executingCustomAction;
|
|
2851
3314
|
try {
|
|
2852
3315
|
executingCustomAction = true;
|
|
2853
|
-
action.exec();
|
|
3316
|
+
void action.exec();
|
|
2854
3317
|
} finally {
|
|
2855
3318
|
executingCustomAction = saveExecutingCustomAction;
|
|
2856
3319
|
}
|
|
@@ -2883,38 +3346,29 @@ class Actor {
|
|
|
2883
3346
|
|
|
2884
3347
|
// prepare to collect initial microsteps during initialTransition
|
|
2885
3348
|
this._collectedMicrosteps = [];
|
|
2886
|
-
|
|
2887
|
-
|
|
2888
|
-
// Pending effects are restored by this actor on `start()`; the logic
|
|
2889
|
-
// only sees the rest of the persisted snapshot.
|
|
2890
|
-
const {
|
|
2891
|
-
_pendingEffects,
|
|
2892
|
-
...rest
|
|
2893
|
-
} = persistedState;
|
|
2894
|
-
this._pendingEffects = _pendingEffects;
|
|
2895
|
-
persistedState = rest;
|
|
2896
|
-
}
|
|
3349
|
+
const persistedState = options?.snapshot ?? options?.state;
|
|
3350
|
+
this._restored = persistedState !== undefined;
|
|
2897
3351
|
try {
|
|
2898
3352
|
if (persistedState) {
|
|
2899
|
-
this.
|
|
3353
|
+
this._setSnapshot(this.logic.restoreSnapshot ? this.logic.restoreSnapshot(persistedState, this._actorScope) : persistedState);
|
|
2900
3354
|
} else if (options?._inert) {
|
|
2901
3355
|
// Inert actors (createInertActorScope) only anchor a scope for pure
|
|
2902
3356
|
// transition functions; computing an initial snapshot here would run
|
|
2903
3357
|
// init-time side effects (context factories, entry) a second time.
|
|
2904
3358
|
} else {
|
|
2905
|
-
const [snapshot, effects] = this.logic.initialTransition(this.options?.input, this._actorScope);
|
|
2906
|
-
this.
|
|
3359
|
+
const [snapshot, effects] = finalizeTransitionResult(this._actorScope, undefined, this.logic.initialTransition(this.options?.input, this._actorScope));
|
|
3360
|
+
this._setSnapshot(snapshot);
|
|
2907
3361
|
this._initialEffects = effects;
|
|
2908
3362
|
}
|
|
2909
3363
|
} catch (err) {
|
|
2910
3364
|
// if we get here then it means that we assign a value to this._snapshot that is not of the correct type
|
|
2911
3365
|
// we can't get the true `TSnapshot & { status: 'error'; }`, it's impossible
|
|
2912
3366
|
// so right now this is a lie of sorts
|
|
2913
|
-
this.
|
|
3367
|
+
this._setSnapshot({
|
|
2914
3368
|
status: 'error',
|
|
2915
3369
|
output: undefined,
|
|
2916
3370
|
error: err
|
|
2917
|
-
};
|
|
3371
|
+
});
|
|
2918
3372
|
// discard any functions deferred during the failed initial snapshot
|
|
2919
3373
|
// computation so they can't run against an inconsistent actor
|
|
2920
3374
|
this._deferred.length = 0;
|
|
@@ -2935,12 +3389,17 @@ class Actor {
|
|
|
2935
3389
|
snapshot: this._snapshot
|
|
2936
3390
|
});
|
|
2937
3391
|
}
|
|
3392
|
+
/** Associates each live snapshot with this actor for later pure transitions. */
|
|
3393
|
+
_setSnapshot(snapshot) {
|
|
3394
|
+
this._snapshot = snapshot;
|
|
3395
|
+
setSnapshotActorRef(snapshot, this);
|
|
3396
|
+
}
|
|
2938
3397
|
_setErrorSnapshot(err, snapshot = this._snapshot) {
|
|
2939
|
-
this.
|
|
3398
|
+
this._setSnapshot({
|
|
2940
3399
|
...snapshot,
|
|
2941
3400
|
status: 'error',
|
|
2942
3401
|
error: err
|
|
2943
|
-
};
|
|
3402
|
+
});
|
|
2944
3403
|
}
|
|
2945
3404
|
|
|
2946
3405
|
/** Recover via the logic's error event if possible; otherwise error out. */
|
|
@@ -2958,11 +3417,10 @@ class Actor {
|
|
|
2958
3417
|
return false;
|
|
2959
3418
|
}
|
|
2960
3419
|
try {
|
|
2961
|
-
const [nextSnapshot, effects] = this.logic.transition(snapshot, errorEvent, this._actorScope);
|
|
2962
|
-
this.
|
|
2963
|
-
|
|
3420
|
+
const [nextSnapshot, effects] = finalizeTransitionResult(this._actorScope, snapshot, this.logic.transition(snapshot, errorEvent, this._actorScope));
|
|
3421
|
+
this._setSnapshot(nextSnapshot);
|
|
3422
|
+
executeExecutableEffects(effects, this._actorScope);
|
|
2964
3423
|
this.update(nextSnapshot, errorEvent);
|
|
2965
|
-
this.logic.executeEffects?.(logicEffects, this._actorScope);
|
|
2966
3424
|
return true;
|
|
2967
3425
|
} catch {
|
|
2968
3426
|
return false;
|
|
@@ -2975,7 +3433,7 @@ class Actor {
|
|
|
2975
3433
|
}
|
|
2976
3434
|
update(snapshot, event) {
|
|
2977
3435
|
// Update state
|
|
2978
|
-
this.
|
|
3436
|
+
this._setSnapshot(snapshot);
|
|
2979
3437
|
|
|
2980
3438
|
// Execute deferred effects
|
|
2981
3439
|
for (let i = 0; i < this._deferred.length; i++) {
|
|
@@ -2993,6 +3451,7 @@ class Actor {
|
|
|
2993
3451
|
return;
|
|
2994
3452
|
}
|
|
2995
3453
|
this._setErrorSnapshot(err, snapshot);
|
|
3454
|
+
this._error(err);
|
|
2996
3455
|
break;
|
|
2997
3456
|
}
|
|
2998
3457
|
}
|
|
@@ -3001,26 +3460,6 @@ class Actor {
|
|
|
3001
3460
|
case 'active':
|
|
3002
3461
|
this._next(snapshot);
|
|
3003
3462
|
break;
|
|
3004
|
-
case 'done':
|
|
3005
|
-
{
|
|
3006
|
-
// next observers are meant to be notified about done snapshots
|
|
3007
|
-
// this can be seen as something that is different from how observable work
|
|
3008
|
-
// but with observables `complete` callback is called without any arguments
|
|
3009
|
-
// it's more ergonomic for XState to treat a done snapshot as a "next" value
|
|
3010
|
-
// and the completion event as something that is separate,
|
|
3011
|
-
// something that merely follows emitting that done snapshot
|
|
3012
|
-
this._next(snapshot);
|
|
3013
|
-
this._stopProcedure();
|
|
3014
|
-
this._complete();
|
|
3015
|
-
const doneEvent = createDoneActorEvent(this.id, this._snapshot.output);
|
|
3016
|
-
if (this._parent) {
|
|
3017
|
-
this.system._relay(this, this._parent, doneEvent);
|
|
3018
|
-
}
|
|
3019
|
-
break;
|
|
3020
|
-
}
|
|
3021
|
-
case 'error':
|
|
3022
|
-
this._error(this._snapshot.error);
|
|
3023
|
-
break;
|
|
3024
3463
|
}
|
|
3025
3464
|
this.system._sendInspectionEvent({
|
|
3026
3465
|
type: '@xstate.transition',
|
|
@@ -3045,8 +3484,7 @@ class Actor {
|
|
|
3045
3484
|
}
|
|
3046
3485
|
this._forceDeferredActions = true;
|
|
3047
3486
|
try {
|
|
3048
|
-
|
|
3049
|
-
this.logic.executeEffects?.(logicEffects, this._actorScope);
|
|
3487
|
+
executeExecutableEffects(this._initialEffects, this._actorScope);
|
|
3050
3488
|
this._initialEffects = undefined;
|
|
3051
3489
|
return true;
|
|
3052
3490
|
} catch (err) {
|
|
@@ -3217,6 +3655,14 @@ class Actor {
|
|
|
3217
3655
|
case 'done':
|
|
3218
3656
|
// a state machine can be "done" upon initialization (it could reach a final state using initial microsteps)
|
|
3219
3657
|
// we still need to complete observers, flush deferreds etc
|
|
3658
|
+
if (this._restored) {
|
|
3659
|
+
// Restoration does not replay the transition that originally
|
|
3660
|
+
// terminated the actor, and must not notify the parent again.
|
|
3661
|
+
this._next(this._snapshot);
|
|
3662
|
+
this._stopProcedure();
|
|
3663
|
+
this._complete();
|
|
3664
|
+
return this;
|
|
3665
|
+
}
|
|
3220
3666
|
if (!this._flushInitialEffects()) {
|
|
3221
3667
|
return this;
|
|
3222
3668
|
}
|
|
@@ -3230,33 +3676,31 @@ class Actor {
|
|
|
3230
3676
|
if (!this._parent) {
|
|
3231
3677
|
this.system.start();
|
|
3232
3678
|
}
|
|
3679
|
+
if (!this._flushInitialEffects()) {
|
|
3680
|
+
return this;
|
|
3681
|
+
}
|
|
3233
3682
|
if (this.logic.start) {
|
|
3234
3683
|
try {
|
|
3235
|
-
this.logic.start(this._snapshot, this._actorScope
|
|
3684
|
+
this.logic.start(this._snapshot, this._actorScope, {
|
|
3685
|
+
restored: this._restored
|
|
3686
|
+
});
|
|
3236
3687
|
} catch (err) {
|
|
3237
3688
|
this._setErrorSnapshot(err);
|
|
3238
3689
|
this._error(err);
|
|
3239
3690
|
return this;
|
|
3240
3691
|
}
|
|
3241
3692
|
}
|
|
3242
|
-
if (!this._flushInitialEffects()) {
|
|
3243
|
-
return this;
|
|
3244
|
-
}
|
|
3245
3693
|
|
|
3246
3694
|
// TODO: this notifies all subscribers but usually this is redundant
|
|
3247
3695
|
// there is no real change happening here
|
|
3248
3696
|
// we need to rethink if this needs to be refactored
|
|
3249
3697
|
this.update(this._snapshot, initEvent);
|
|
3250
|
-
if (this.
|
|
3251
|
-
const
|
|
3252
|
-
for (const
|
|
3253
|
-
|
|
3254
|
-
// self-targeted raises exist today; other effect types are ignored.
|
|
3255
|
-
if (effect.type === '@xstate.raise') {
|
|
3256
|
-
this.system.scheduler.schedule(this, this, effect.event, resolveRestoredTimerDelay(strategy, effect), effect.id);
|
|
3257
|
-
}
|
|
3698
|
+
if (this._restored) {
|
|
3699
|
+
const timers = this._snapshot.timers ?? {};
|
|
3700
|
+
for (const timer of Object.values(timers)) {
|
|
3701
|
+
this.system.scheduleTimer(this, timer.id, timer.delay);
|
|
3258
3702
|
}
|
|
3259
|
-
this.
|
|
3703
|
+
this._restored = false;
|
|
3260
3704
|
}
|
|
3261
3705
|
this.mailbox.start();
|
|
3262
3706
|
return this;
|
|
@@ -3265,7 +3709,7 @@ class Actor {
|
|
|
3265
3709
|
let nextState;
|
|
3266
3710
|
let caughtError;
|
|
3267
3711
|
try {
|
|
3268
|
-
nextState = this.logic.transition(this._snapshot, event, this._actorScope);
|
|
3712
|
+
nextState = finalizeTransitionResult(this._actorScope, this._snapshot, this.logic.transition(this._snapshot, event, this._actorScope));
|
|
3269
3713
|
} catch (err) {
|
|
3270
3714
|
// we wrap it in a box so we can rethrow it later even if falsy value gets caught here
|
|
3271
3715
|
caughtError = {
|
|
@@ -3280,10 +3724,9 @@ class Actor {
|
|
|
3280
3724
|
try {
|
|
3281
3725
|
const [nextSnapshot, effects] = nextState;
|
|
3282
3726
|
snapshot = nextSnapshot;
|
|
3283
|
-
this.
|
|
3284
|
-
|
|
3727
|
+
this._setSnapshot(snapshot);
|
|
3728
|
+
executeExecutableEffects(effects, this._actorScope);
|
|
3285
3729
|
this.update(snapshot, event);
|
|
3286
|
-
this.logic.executeEffects?.(logicEffects, this._actorScope);
|
|
3287
3730
|
} catch (err) {
|
|
3288
3731
|
this._recoverOrError(err, snapshot);
|
|
3289
3732
|
return;
|
|
@@ -3294,6 +3737,35 @@ class Actor {
|
|
|
3294
3737
|
}
|
|
3295
3738
|
}
|
|
3296
3739
|
|
|
3740
|
+
/** @internal */
|
|
3741
|
+
_emit(event) {
|
|
3742
|
+
for (const listeners of [this.eventListeners.get(event.type), this.eventListeners.get('*')]) {
|
|
3743
|
+
if (listeners) {
|
|
3744
|
+
for (const handler of listeners) {
|
|
3745
|
+
safeCall(handler, event);
|
|
3746
|
+
}
|
|
3747
|
+
}
|
|
3748
|
+
}
|
|
3749
|
+
}
|
|
3750
|
+
|
|
3751
|
+
/** @internal */
|
|
3752
|
+
_terminate(termination) {
|
|
3753
|
+
if (termination.status === 'done') {
|
|
3754
|
+
// The terminal snapshot is published before observer completion and the
|
|
3755
|
+
// parent notification, matching actor lifecycle order.
|
|
3756
|
+
this._next(this._snapshot);
|
|
3757
|
+
this._stopProcedure();
|
|
3758
|
+
this._complete();
|
|
3759
|
+
if (this._parent) {
|
|
3760
|
+
this.system._relay(this, this._parent, createDoneActorEvent(this.id, termination.output));
|
|
3761
|
+
}
|
|
3762
|
+
return;
|
|
3763
|
+
}
|
|
3764
|
+
if (termination.status === 'error') {
|
|
3765
|
+
this._error(termination.error);
|
|
3766
|
+
}
|
|
3767
|
+
}
|
|
3768
|
+
|
|
3297
3769
|
/** @internal */
|
|
3298
3770
|
_stop() {
|
|
3299
3771
|
if (this._processingStatus === ProcessingStatus.Stopped) {
|
|
@@ -3302,6 +3774,7 @@ class Actor {
|
|
|
3302
3774
|
this.mailbox.clear();
|
|
3303
3775
|
if (this._processingStatus === ProcessingStatus.NotStarted) {
|
|
3304
3776
|
this._processingStatus = ProcessingStatus.Stopped;
|
|
3777
|
+
this.system._unregister(this);
|
|
3305
3778
|
return this;
|
|
3306
3779
|
}
|
|
3307
3780
|
this.mailbox.enqueue({
|
|
@@ -3360,7 +3833,7 @@ class Actor {
|
|
|
3360
3833
|
}
|
|
3361
3834
|
|
|
3362
3835
|
// Cancel all delayed events
|
|
3363
|
-
this.system.
|
|
3836
|
+
this.system.cancelAllTimers(this);
|
|
3364
3837
|
|
|
3365
3838
|
// TODO: mailbox.reset
|
|
3366
3839
|
this.mailbox.clear();
|
|
@@ -3421,35 +3894,7 @@ class Actor {
|
|
|
3421
3894
|
*/
|
|
3422
3895
|
|
|
3423
3896
|
getPersistedSnapshot(options) {
|
|
3424
|
-
|
|
3425
|
-
|
|
3426
|
-
// Capture this actor's pending self-targeted effects (delayed
|
|
3427
|
-
// events/transitions) as serialized action descriptors plus their
|
|
3428
|
-
// runtime progress, so they can be restored on `start()`.
|
|
3429
|
-
const scheduledEvents = this.system._snapshot._scheduledEvents;
|
|
3430
|
-
let pendingEffects;
|
|
3431
|
-
const now = Date.now();
|
|
3432
|
-
for (const key in scheduledEvents) {
|
|
3433
|
-
const scheduled = scheduledEvents[key];
|
|
3434
|
-
if (scheduled.source === this && scheduled.target === this) {
|
|
3435
|
-
(pendingEffects ??= []).push({
|
|
3436
|
-
type: '@xstate.raise',
|
|
3437
|
-
event: scheduled.event,
|
|
3438
|
-
id: scheduled.id,
|
|
3439
|
-
delay: scheduled.delay,
|
|
3440
|
-
startedAt: scheduled.startedAt,
|
|
3441
|
-
elapsed: Math.max(0, now - scheduled.startedAt)
|
|
3442
|
-
});
|
|
3443
|
-
}
|
|
3444
|
-
}
|
|
3445
|
-
|
|
3446
|
-
// If this actor was restored but not yet started, its pending effects
|
|
3447
|
-
// are not in the scheduler yet — carry them through as-is.
|
|
3448
|
-
pendingEffects ??= this._pendingEffects;
|
|
3449
|
-
return pendingEffects ? {
|
|
3450
|
-
...persisted,
|
|
3451
|
-
_pendingEffects: pendingEffects
|
|
3452
|
-
} : persisted;
|
|
3897
|
+
return this.logic.getPersistedSnapshot(this._snapshot, options);
|
|
3453
3898
|
}
|
|
3454
3899
|
[symbolObservable]() {
|
|
3455
3900
|
return this;
|
|
@@ -3476,20 +3921,6 @@ class Actor {
|
|
|
3476
3921
|
return this._snapshot;
|
|
3477
3922
|
}
|
|
3478
3923
|
}
|
|
3479
|
-
function resolveRestoredTimerDelay(strategy, effect) {
|
|
3480
|
-
if (typeof strategy === 'function') {
|
|
3481
|
-
return strategy(effect);
|
|
3482
|
-
}
|
|
3483
|
-
switch (strategy) {
|
|
3484
|
-
case 'restart':
|
|
3485
|
-
return effect.delay;
|
|
3486
|
-
case 'absolute':
|
|
3487
|
-
return Math.max(0, effect.startedAt + effect.delay - Date.now());
|
|
3488
|
-
case 'resume':
|
|
3489
|
-
default:
|
|
3490
|
-
return Math.max(0, effect.delay - effect.elapsed);
|
|
3491
|
-
}
|
|
3492
|
-
}
|
|
3493
3924
|
/**
|
|
3494
3925
|
* Creates a new actor instance for the given actor logic with the provided
|
|
3495
3926
|
* options, if any.
|
|
@@ -3545,63 +3976,27 @@ function getEffectState(self) {
|
|
|
3545
3976
|
}
|
|
3546
3977
|
return state;
|
|
3547
3978
|
}
|
|
3548
|
-
function
|
|
3549
|
-
|
|
3979
|
+
function executeLogicEffect(self, key, effect) {
|
|
3980
|
+
const state = getEffectState(self);
|
|
3981
|
+
if (key !== undefined && state.has(key)) {
|
|
3550
3982
|
return;
|
|
3551
3983
|
}
|
|
3552
|
-
|
|
3553
|
-
|
|
3554
|
-
|
|
3555
|
-
|
|
3556
|
-
|
|
3557
|
-
|
|
3558
|
-
|
|
3559
|
-
|
|
3560
|
-
|
|
3561
|
-
|
|
3562
|
-
|
|
3563
|
-
|
|
3564
|
-
|
|
3565
|
-
|
|
3566
|
-
actorScope.system._relay(actorScope.self, actorScope.self, effect.event);
|
|
3567
|
-
break;
|
|
3568
|
-
case 'effect':
|
|
3569
|
-
{
|
|
3570
|
-
if (!effect.key) {
|
|
3571
|
-
const cleanup = effect.exec();
|
|
3572
|
-
if (typeof cleanup === 'function') {
|
|
3573
|
-
getEffectState(actorScope.self).set(Symbol(), {
|
|
3574
|
-
cleanup
|
|
3575
|
-
});
|
|
3576
|
-
}
|
|
3577
|
-
break;
|
|
3578
|
-
}
|
|
3579
|
-
const state = getEffectState(actorScope.self);
|
|
3580
|
-
if (state.has(effect.key)) {
|
|
3581
|
-
break;
|
|
3582
|
-
}
|
|
3583
|
-
const cleanup = effect.exec();
|
|
3584
|
-
state.set(effect.key, {
|
|
3585
|
-
cleanup: typeof cleanup === 'function' ? cleanup : undefined
|
|
3586
|
-
});
|
|
3587
|
-
break;
|
|
3588
|
-
}
|
|
3589
|
-
case 'cleanupEffects':
|
|
3590
|
-
{
|
|
3591
|
-
const state = effectStates.get(actorScope.self);
|
|
3592
|
-
if (!state) {
|
|
3593
|
-
break;
|
|
3594
|
-
}
|
|
3595
|
-
for (const {
|
|
3596
|
-
cleanup
|
|
3597
|
-
} of state.values()) {
|
|
3598
|
-
cleanup?.();
|
|
3599
|
-
}
|
|
3600
|
-
effectStates.delete(actorScope.self);
|
|
3601
|
-
break;
|
|
3602
|
-
}
|
|
3603
|
-
}
|
|
3984
|
+
const cleanup = effect();
|
|
3985
|
+
state.set(key ?? Symbol(), {
|
|
3986
|
+
cleanup: typeof cleanup === 'function' ? cleanup : undefined
|
|
3987
|
+
});
|
|
3988
|
+
}
|
|
3989
|
+
function cleanupLogicEffects(self) {
|
|
3990
|
+
const state = effectStates.get(self);
|
|
3991
|
+
if (!state) {
|
|
3992
|
+
return;
|
|
3993
|
+
}
|
|
3994
|
+
for (const {
|
|
3995
|
+
cleanup
|
|
3996
|
+
} of state.values()) {
|
|
3997
|
+
cleanup?.();
|
|
3604
3998
|
}
|
|
3999
|
+
effectStates.delete(self);
|
|
3605
4000
|
}
|
|
3606
4001
|
function resolveContext(context, input) {
|
|
3607
4002
|
return typeof context === 'function' ? context({
|
|
@@ -3618,9 +4013,7 @@ function createLogic(config) {
|
|
|
3618
4013
|
...snapshot,
|
|
3619
4014
|
status: 'stopped',
|
|
3620
4015
|
input: undefined
|
|
3621
|
-
}, [
|
|
3622
|
-
type: 'cleanupEffects'
|
|
3623
|
-
}]];
|
|
4016
|
+
}, [createCustomEffect('xstate.logic.cleanup', () => cleanupLogicEffects(actorScope.self))]];
|
|
3624
4017
|
}
|
|
3625
4018
|
if (event.type === XSTATE_LOGIC_EFFECT_START) {
|
|
3626
4019
|
return [{
|
|
@@ -3663,43 +4056,33 @@ function createLogic(config) {
|
|
|
3663
4056
|
if (snapshot.effects?.[key]) {
|
|
3664
4057
|
return;
|
|
3665
4058
|
}
|
|
3666
|
-
effects.push({
|
|
3667
|
-
|
|
3668
|
-
|
|
3669
|
-
exec
|
|
3670
|
-
});
|
|
4059
|
+
effects.push(createCustomEffect('xstate.logic.effect', (runtime = actorScope.self.system) => executeLogicEffect(actorScope.self, key, () => exec(runtime)), {
|
|
4060
|
+
key
|
|
4061
|
+
}));
|
|
3671
4062
|
trackedEffects[key] = {
|
|
3672
4063
|
status: 'active'
|
|
3673
4064
|
};
|
|
3674
4065
|
};
|
|
3675
4066
|
const enq = {
|
|
3676
4067
|
emit: emitted => {
|
|
3677
|
-
effects.push(
|
|
3678
|
-
type: 'emit',
|
|
3679
|
-
event: emitted
|
|
3680
|
-
});
|
|
4068
|
+
effects.push(createEmitEffect(actorScope, emitted));
|
|
3681
4069
|
},
|
|
3682
4070
|
sendBack: sentEvent => {
|
|
3683
|
-
|
|
3684
|
-
|
|
3685
|
-
|
|
3686
|
-
}
|
|
4071
|
+
const parent = actorScope.self._parent;
|
|
4072
|
+
if (parent) {
|
|
4073
|
+
effects.push(createSendToEffect(actorScope, parent, sentEvent));
|
|
4074
|
+
}
|
|
3687
4075
|
},
|
|
3688
4076
|
raise: raisedEvent => {
|
|
3689
|
-
effects.push(
|
|
3690
|
-
type: 'raise',
|
|
3691
|
-
event: raisedEvent
|
|
3692
|
-
});
|
|
4077
|
+
effects.push(createSendToEffect(actorScope, actorScope.self, raisedEvent));
|
|
3693
4078
|
},
|
|
3694
4079
|
effect: (keyOrExec, maybeExec) => {
|
|
3695
4080
|
if (typeof keyOrExec === 'string') {
|
|
3696
4081
|
enqueueEffect(keyOrExec, maybeExec);
|
|
3697
4082
|
return;
|
|
3698
4083
|
}
|
|
3699
|
-
|
|
3700
|
-
|
|
3701
|
-
exec: keyOrExec
|
|
3702
|
-
});
|
|
4084
|
+
const exec = keyOrExec;
|
|
4085
|
+
effects.push(createCustomEffect('xstate.logic.effect', (runtime = actorScope.self.system) => executeLogicEffect(actorScope.self, undefined, () => exec(runtime))));
|
|
3703
4086
|
}
|
|
3704
4087
|
};
|
|
3705
4088
|
const patch = config.run({
|
|
@@ -3724,18 +4107,23 @@ function createLogic(config) {
|
|
|
3724
4107
|
...trackedEffects
|
|
3725
4108
|
};
|
|
3726
4109
|
}
|
|
3727
|
-
return [nextSnapshot, effects];
|
|
4110
|
+
return finalizeTransitionResult(actorScope, snapshot, [nextSnapshot, effects]);
|
|
3728
4111
|
};
|
|
3729
4112
|
const logic = {
|
|
3730
4113
|
id: config.id,
|
|
3731
4114
|
config,
|
|
3732
4115
|
transition,
|
|
3733
|
-
start: (snapshot, actorScope) => {
|
|
4116
|
+
start: (snapshot, actorScope, options) => {
|
|
4117
|
+
if (!options?.restored) {
|
|
4118
|
+
return;
|
|
4119
|
+
}
|
|
3734
4120
|
const [nextSnapshot, effects] = transition(snapshot, createInitEvent(snapshot.input), actorScope);
|
|
3735
4121
|
Object.assign(snapshot, nextSnapshot);
|
|
3736
|
-
|
|
4122
|
+
for (const effect of effects) {
|
|
4123
|
+
actorScope.actionExecutor(effect);
|
|
4124
|
+
}
|
|
3737
4125
|
},
|
|
3738
|
-
initialTransition: (input,
|
|
4126
|
+
initialTransition: (input, actorScope) => {
|
|
3739
4127
|
const context = resolveContext(config.context, input);
|
|
3740
4128
|
const snapshot = {
|
|
3741
4129
|
status: 'active',
|
|
@@ -3746,14 +4134,13 @@ function createLogic(config) {
|
|
|
3746
4134
|
if (context !== undefined) {
|
|
3747
4135
|
snapshot.context = context;
|
|
3748
4136
|
}
|
|
3749
|
-
return
|
|
4137
|
+
return transition({
|
|
3750
4138
|
...snapshot
|
|
3751
|
-
},
|
|
4139
|
+
}, createInitEvent(input), actorScope);
|
|
3752
4140
|
},
|
|
3753
4141
|
getInitialSnapshot: (actorScope, input) => logic.initialTransition(input, actorScope)[0],
|
|
3754
4142
|
getPersistedSnapshot: snapshot => snapshot,
|
|
3755
|
-
restoreSnapshot: snapshot => snapshot
|
|
3756
|
-
executeEffects: executeLogicEffects
|
|
4143
|
+
restoreSnapshot: snapshot => snapshot
|
|
3757
4144
|
};
|
|
3758
4145
|
return logic;
|
|
3759
4146
|
}
|
|
@@ -4013,7 +4400,6 @@ function createObservableLogic(observableCreatorOrConfig) {
|
|
|
4013
4400
|
self,
|
|
4014
4401
|
system
|
|
4015
4402
|
} = args;
|
|
4016
|
-
const emit = args.emit;
|
|
4017
4403
|
switch (event.type) {
|
|
4018
4404
|
case XSTATE_OBSERVABLE_NEXT:
|
|
4019
4405
|
{
|
|
@@ -4044,28 +4430,32 @@ function createObservableLogic(observableCreatorOrConfig) {
|
|
|
4044
4430
|
}
|
|
4045
4431
|
};
|
|
4046
4432
|
}
|
|
4047
|
-
|
|
4433
|
+
if (event.type !== XSTATE_INIT) {
|
|
4434
|
+
return;
|
|
4435
|
+
}
|
|
4436
|
+
enq.effect((runtime = system) => {
|
|
4048
4437
|
const actorSelf = self;
|
|
4438
|
+
const sendSelf = event => void runtime.sendEvent(actorSelf, actorSelf, event);
|
|
4049
4439
|
const subscription = observableCreator({
|
|
4050
4440
|
input,
|
|
4051
4441
|
system,
|
|
4052
4442
|
self: self,
|
|
4053
|
-
emit:
|
|
4443
|
+
emit: event => void runtime.emitEvent(actorSelf, event)
|
|
4054
4444
|
}).subscribe({
|
|
4055
4445
|
next: value => {
|
|
4056
|
-
|
|
4446
|
+
sendSelf({
|
|
4057
4447
|
type: XSTATE_OBSERVABLE_NEXT,
|
|
4058
4448
|
data: value
|
|
4059
4449
|
});
|
|
4060
4450
|
},
|
|
4061
4451
|
error: err => {
|
|
4062
|
-
|
|
4452
|
+
sendSelf({
|
|
4063
4453
|
type: XSTATE_OBSERVABLE_ERROR,
|
|
4064
4454
|
data: err
|
|
4065
4455
|
});
|
|
4066
4456
|
},
|
|
4067
4457
|
complete: () => {
|
|
4068
|
-
|
|
4458
|
+
sendSelf({
|
|
4069
4459
|
type: XSTATE_OBSERVABLE_COMPLETE
|
|
4070
4460
|
});
|
|
4071
4461
|
}
|
|
@@ -4134,26 +4524,26 @@ function createEventObservableLogic(lazyObservableOrConfig) {
|
|
|
4134
4524
|
// TODO: event types
|
|
4135
4525
|
const logic = {
|
|
4136
4526
|
config: lazyObservable,
|
|
4137
|
-
transition: (state, event) => {
|
|
4527
|
+
transition: (state, event, actorScope) => {
|
|
4138
4528
|
if (state.status !== 'active') {
|
|
4139
4529
|
return [state, []];
|
|
4140
4530
|
}
|
|
4141
4531
|
switch (event.type) {
|
|
4142
4532
|
case XSTATE_OBSERVABLE_ERROR:
|
|
4143
|
-
return [{
|
|
4533
|
+
return finalizeTransitionResult(actorScope, state, [{
|
|
4144
4534
|
...state,
|
|
4145
4535
|
status: 'error',
|
|
4146
4536
|
error: event.data,
|
|
4147
4537
|
input: undefined,
|
|
4148
4538
|
_subscription: undefined
|
|
4149
|
-
}, []];
|
|
4539
|
+
}, []]);
|
|
4150
4540
|
case XSTATE_OBSERVABLE_COMPLETE:
|
|
4151
|
-
return [{
|
|
4541
|
+
return finalizeTransitionResult(actorScope, state, [{
|
|
4152
4542
|
...state,
|
|
4153
4543
|
status: 'done',
|
|
4154
4544
|
input: undefined,
|
|
4155
4545
|
_subscription: undefined
|
|
4156
|
-
}, []];
|
|
4546
|
+
}, []]);
|
|
4157
4547
|
case XSTATE_STOP:
|
|
4158
4548
|
state._subscription.unsubscribe();
|
|
4159
4549
|
return [{
|
|
@@ -4188,7 +4578,9 @@ function createEventObservableLogic(lazyObservableOrConfig) {
|
|
|
4188
4578
|
input: state.input,
|
|
4189
4579
|
system,
|
|
4190
4580
|
self,
|
|
4191
|
-
emit
|
|
4581
|
+
emit: event => {
|
|
4582
|
+
void emit(event);
|
|
4583
|
+
}
|
|
4192
4584
|
}).subscribe({
|
|
4193
4585
|
next: value => {
|
|
4194
4586
|
if (self._parent) {
|
|
@@ -4389,8 +4781,9 @@ function createAsyncLogic(asyncLogic) {
|
|
|
4389
4781
|
if (event.type !== XSTATE_INIT) {
|
|
4390
4782
|
return;
|
|
4391
4783
|
}
|
|
4392
|
-
enq.effect(() => {
|
|
4784
|
+
enq.effect((runtime = system) => {
|
|
4393
4785
|
const actorSelf = self;
|
|
4786
|
+
const sendSelf = event => void runtime.sendEvent(actorSelf, actorSelf, event);
|
|
4394
4787
|
const controller = new AbortController();
|
|
4395
4788
|
const timeout = config.timeout;
|
|
4396
4789
|
const timeoutMs = parseDelayToMilliseconds(timeout);
|
|
@@ -4399,7 +4792,7 @@ function createAsyncLogic(asyncLogic) {
|
|
|
4399
4792
|
return;
|
|
4400
4793
|
}
|
|
4401
4794
|
controller.abort();
|
|
4402
|
-
|
|
4795
|
+
sendSelf({
|
|
4403
4796
|
type: XSTATE_ASYNC_REJECT,
|
|
4404
4797
|
data: new TimeoutError(timeout)
|
|
4405
4798
|
});
|
|
@@ -4415,7 +4808,7 @@ function createAsyncLogic(asyncLogic) {
|
|
|
4415
4808
|
self: self,
|
|
4416
4809
|
signal: controller.signal
|
|
4417
4810
|
}, {
|
|
4418
|
-
emit:
|
|
4811
|
+
emit: event => void runtime.emitEvent(actorSelf, event),
|
|
4419
4812
|
step: async (key, exec) => {
|
|
4420
4813
|
const effect = self.getSnapshot().effects?.[key];
|
|
4421
4814
|
if (effect?.status === 'done') {
|
|
@@ -4427,20 +4820,20 @@ function createAsyncLogic(asyncLogic) {
|
|
|
4427
4820
|
if (effect?.status === 'active') {
|
|
4428
4821
|
return waitForEffect(self, key);
|
|
4429
4822
|
}
|
|
4430
|
-
|
|
4823
|
+
sendSelf({
|
|
4431
4824
|
type: XSTATE_LOGIC_EFFECT_START,
|
|
4432
4825
|
key
|
|
4433
4826
|
});
|
|
4434
4827
|
try {
|
|
4435
4828
|
const output = await exec();
|
|
4436
|
-
|
|
4829
|
+
sendSelf({
|
|
4437
4830
|
type: XSTATE_LOGIC_EFFECT_RESOLVE,
|
|
4438
4831
|
key,
|
|
4439
4832
|
output
|
|
4440
4833
|
});
|
|
4441
4834
|
return output;
|
|
4442
4835
|
} catch (error) {
|
|
4443
|
-
|
|
4836
|
+
sendSelf({
|
|
4444
4837
|
type: XSTATE_LOGIC_EFFECT_REJECT,
|
|
4445
4838
|
key,
|
|
4446
4839
|
error
|
|
@@ -4454,7 +4847,7 @@ function createAsyncLogic(asyncLogic) {
|
|
|
4454
4847
|
if (self.getSnapshot().status !== 'active') {
|
|
4455
4848
|
return;
|
|
4456
4849
|
}
|
|
4457
|
-
|
|
4850
|
+
sendSelf({
|
|
4458
4851
|
type: XSTATE_ASYNC_RESOLVE,
|
|
4459
4852
|
data: response
|
|
4460
4853
|
});
|
|
@@ -4463,7 +4856,7 @@ function createAsyncLogic(asyncLogic) {
|
|
|
4463
4856
|
if (self.getSnapshot().status !== 'active') {
|
|
4464
4857
|
return;
|
|
4465
4858
|
}
|
|
4466
|
-
|
|
4859
|
+
sendSelf({
|
|
4467
4860
|
type: XSTATE_ASYNC_REJECT,
|
|
4468
4861
|
data: errorData
|
|
4469
4862
|
});
|
|
@@ -4492,4 +4885,4 @@ function createEmptyActor() {
|
|
|
4492
4885
|
return createActor(emptyLogic);
|
|
4493
4886
|
}
|
|
4494
4887
|
|
|
4495
|
-
export {
|
|
4888
|
+
export { getCandidates as $, Actor as A, isBuiltInExecutableAction as B, createEmptyActor as C, createCallbackLogic as D, createObservableLogic as E, createEventObservableLogic as F, createLogic as G, createAsyncLogic as H, createListenerLogic as I, listenerLogic as J, createSubscriptionLogic as K, subscriptionLogic as L, resolveReferencedActor as M, setSnapshotActorRef as N, getSnapshotActorRef as O, createSnapshotSystem as P, mapValues as Q, createInvokeId as R, STATE_DELIMITER as S, TimeoutError as T, getDelayedTransitions as U, evaluateCandidate as V, formatTransition as W, XSTATE_INIT as X, NULL_EVENT as Y, toTransitionConfigArray as Z, createInvokeTimeoutEvent as _, XSTATE_STOP as a, formatRouteTransitions as a0, resolveStateValue as a1, getAllStateNodes as a2, createMachineSnapshot as a3, isInFinalState as a4, cloneMachineSnapshot as a5, transitionNode as a6, getTransitionResult as a7, hasEffect as a8, createErrorPlatformEvent as a9, toStatePath as aa, isStateId as ab, getStateNodeByPath as ac, getPersistedSnapshot as ad, $$ACTOR_TYPE as ae, builtInActions as b, createTransitionEnqueue as c, deriveDeferredStarts as d, XSTATE_TIMER as e, finalizeTransitionResult as f, createSendToEffect as g, createActor as h, isAtomicStateNode as i, macrostep as j, createInitEvent as k, createSpawnEffect as l, matchesEventDescriptor as m, initialMicrostep as n, getProperAncestors as o, parseDelayToMilliseconds as p, isMachineSnapshot as q, resolveActionsWithContext as r, getStateNodes as s, toArray as t, getAllOwnEventDescriptors as u, matchesState as v, checkStateIn as w, pathToStateValue as x, toObserver as y, executeEffects as z };
|