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