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,33 +162,39 @@ 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(`Only event objects may be used with sendTo`);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
163
170
|
const builtInActions = {
|
|
164
|
-
//
|
|
165
|
-
// deferred
|
|
166
|
-
['@xstate.spawn']:
|
|
171
|
+
// Actor refs are created while resolving the next snapshot. Starting is a
|
|
172
|
+
// separate deferred effect so the returned list remains execution-complete.
|
|
173
|
+
['@xstate.spawn']: actor => {
|
|
174
|
+
return actor.system.spawnActor(actor._parent, actor);
|
|
175
|
+
},
|
|
167
176
|
['@xstate.start']: actor => {
|
|
168
|
-
actor.
|
|
177
|
+
return actor.system.startActor(actor);
|
|
169
178
|
},
|
|
170
|
-
['@xstate.raise']: (actorScope,
|
|
171
|
-
actorScope.system.
|
|
179
|
+
['@xstate.raise']: (actorScope, _event, options) => {
|
|
180
|
+
return actorScope.system.scheduleTimer(actorScope.self, options.id, options?.delay ?? 0);
|
|
172
181
|
},
|
|
173
182
|
['@xstate.sendTo']: (actorScope, actor, event, options) => {
|
|
174
|
-
|
|
175
|
-
throw new Error(`Only event objects may be used with sendTo`);
|
|
176
|
-
}
|
|
183
|
+
assertSendToEvent(event);
|
|
177
184
|
if (options?.delay !== undefined) {
|
|
178
|
-
actorScope.system.
|
|
185
|
+
return actorScope.system.scheduleTimer(actorScope.self, options.id, options?.delay ?? 0);
|
|
179
186
|
} else {
|
|
180
|
-
actorScope.
|
|
181
|
-
actorScope.system._relay(actorScope.self, actor, event);
|
|
182
|
-
});
|
|
187
|
+
return actorScope.system._relay(actorScope.self, actor, event);
|
|
183
188
|
}
|
|
184
189
|
},
|
|
185
190
|
['@xstate.cancel']: (actorScope, sendId) => {
|
|
186
|
-
actorScope.system.
|
|
191
|
+
return actorScope.system.cancelTimer(actorScope.self, sendId);
|
|
187
192
|
},
|
|
188
193
|
['@xstate.stop']: (actorScope, actor) => {
|
|
189
|
-
actorScope.
|
|
194
|
+
return actorScope.system.stopActor(actor);
|
|
195
|
+
},
|
|
196
|
+
['@xstate.terminate']: (actor, termination) => {
|
|
197
|
+
return actor.system.terminateActor(actor, termination);
|
|
190
198
|
}
|
|
191
199
|
};
|
|
192
200
|
|
|
@@ -350,12 +358,109 @@ function createSubscriptionLogic() {
|
|
|
350
358
|
// Singleton logic instance
|
|
351
359
|
const subscriptionLogic = /* #__PURE__ */createSubscriptionLogic();
|
|
352
360
|
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
361
|
+
function execCustomEffect() {
|
|
362
|
+
return this.action?.(...this.args);
|
|
363
|
+
}
|
|
364
|
+
function execEmitEffect(runtime = this.source.system) {
|
|
365
|
+
return runtime.emitEvent(this.source, this.event);
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
/** @internal Creates an emitted-event effect. */
|
|
369
|
+
function createEmitEffect(actorScope, event) {
|
|
370
|
+
return {
|
|
371
|
+
kind: 'emit',
|
|
372
|
+
exec: execEmitEffect,
|
|
373
|
+
type: event.type,
|
|
374
|
+
source: actorScope.self,
|
|
375
|
+
event,
|
|
376
|
+
params: undefined,
|
|
377
|
+
args: []
|
|
378
|
+
};
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
/** @internal Creates a directly executable user effect. */
|
|
382
|
+
function createCustomEffect(type, action, params) {
|
|
383
|
+
return {
|
|
384
|
+
kind: 'action',
|
|
385
|
+
exec: action,
|
|
386
|
+
type,
|
|
387
|
+
action: action,
|
|
388
|
+
params,
|
|
389
|
+
args: []
|
|
390
|
+
};
|
|
391
|
+
}
|
|
392
|
+
function execSpawnEffect(runtime = this.actor.system) {
|
|
393
|
+
return runtime.spawnActor(this.source, this.actor);
|
|
394
|
+
}
|
|
395
|
+
function execStartEffect(runtime = this.actor.system) {
|
|
396
|
+
return runtime.startActor(this.actor);
|
|
397
|
+
}
|
|
398
|
+
function execStopEffect(runtime = this.source.system) {
|
|
399
|
+
return runtime.stopActor(this.actor);
|
|
400
|
+
}
|
|
401
|
+
function execTerminateEffect(runtime = this.actor.system) {
|
|
402
|
+
const termination = this.status === 'done' ? {
|
|
403
|
+
status: 'done',
|
|
404
|
+
output: this.output,
|
|
405
|
+
error: undefined
|
|
406
|
+
} : {
|
|
407
|
+
status: 'error',
|
|
408
|
+
output: undefined,
|
|
409
|
+
error: this.error
|
|
410
|
+
};
|
|
411
|
+
return runtime.terminateActor(this.actor, termination);
|
|
412
|
+
}
|
|
413
|
+
function execRaiseEffect(runtime = this.source.system) {
|
|
414
|
+
return runtime.scheduleTimer(this.source, this.id, this.delay ?? 0);
|
|
415
|
+
}
|
|
416
|
+
function execSendToEffect(runtime = this.source.system) {
|
|
417
|
+
assertSendToEvent(this.event);
|
|
418
|
+
if (this.delay !== undefined) {
|
|
419
|
+
return runtime.scheduleTimer(this.source, this.id, this.delay);
|
|
420
|
+
}
|
|
421
|
+
return runtime.sendEvent(this.source, this.target, this.event);
|
|
422
|
+
}
|
|
423
|
+
function execCancelEffect(runtime = this.source.system) {
|
|
424
|
+
return runtime.cancelTimer(this.source, this.id);
|
|
425
|
+
}
|
|
426
|
+
function updateLogicalTimers(snapshot, effect, actorScope) {
|
|
427
|
+
if (!isBuiltInExecutableAction(effect)) {
|
|
428
|
+
return snapshot;
|
|
429
|
+
}
|
|
430
|
+
if (effect.type === '@xstate.cancel') {
|
|
431
|
+
if (!snapshot.timers?.[effect.id]) {
|
|
432
|
+
return snapshot;
|
|
433
|
+
}
|
|
434
|
+
const timers = {
|
|
435
|
+
...snapshot.timers
|
|
436
|
+
};
|
|
437
|
+
delete timers[effect.id];
|
|
438
|
+
return {
|
|
439
|
+
...snapshot,
|
|
440
|
+
timers
|
|
441
|
+
};
|
|
442
|
+
}
|
|
443
|
+
if (effect.type !== '@xstate.raise' && effect.type !== '@xstate.sendTo' || effect.delay === undefined) {
|
|
444
|
+
return snapshot;
|
|
445
|
+
}
|
|
446
|
+
let nextTimerId = snapshot._nextTimerId ?? 0;
|
|
447
|
+
const id = effect.id ?? `xstate.timer.auto.${nextTimerId++}`;
|
|
448
|
+
effect.id = id;
|
|
449
|
+
const target = effect.type === '@xstate.raise' || effect.target === actorScope.self ? 'self' : effect.target;
|
|
450
|
+
return {
|
|
451
|
+
...snapshot,
|
|
452
|
+
timers: {
|
|
453
|
+
...snapshot.timers,
|
|
454
|
+
[id]: {
|
|
455
|
+
id,
|
|
456
|
+
delay: effect.delay,
|
|
457
|
+
type: effect.type,
|
|
458
|
+
event: effect.event,
|
|
459
|
+
target
|
|
460
|
+
}
|
|
461
|
+
},
|
|
462
|
+
_nextTimerId: nextTimerId
|
|
463
|
+
};
|
|
359
464
|
}
|
|
360
465
|
function mergeContextPatch(context, patch) {
|
|
361
466
|
return {
|
|
@@ -364,43 +469,55 @@ function mergeContextPatch(context, patch) {
|
|
|
364
469
|
};
|
|
365
470
|
}
|
|
366
471
|
function pushBuiltInAction(actions, action, ...args) {
|
|
367
|
-
|
|
472
|
+
const actionRecord = {
|
|
368
473
|
action,
|
|
369
474
|
args
|
|
370
|
-
}
|
|
475
|
+
};
|
|
476
|
+
actions.push(actionRecord);
|
|
477
|
+
return actionRecord;
|
|
371
478
|
}
|
|
372
|
-
function
|
|
373
|
-
|
|
479
|
+
function applyChildUpdate(snapshot, update, actorScope) {
|
|
480
|
+
if (update.type === 'add') {
|
|
374
481
|
return {
|
|
482
|
+
...snapshot,
|
|
375
483
|
children: {
|
|
376
|
-
...
|
|
377
|
-
[id]: actor
|
|
484
|
+
...snapshot.children,
|
|
485
|
+
[update.id]: update.actor
|
|
378
486
|
}
|
|
379
487
|
};
|
|
380
|
-
}
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
}
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
for (const key of Object.keys(children)) {
|
|
390
|
-
if (children[key] === actor) {
|
|
391
|
-
delete children[key];
|
|
392
|
-
}
|
|
488
|
+
}
|
|
489
|
+
const children = {
|
|
490
|
+
...snapshot.children
|
|
491
|
+
};
|
|
492
|
+
let owned = update.actor._parent === actorScope.self;
|
|
493
|
+
for (const key of Object.keys(children)) {
|
|
494
|
+
if (children[key] === update.actor) {
|
|
495
|
+
owned = true;
|
|
496
|
+
delete children[key];
|
|
393
497
|
}
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
};
|
|
397
|
-
}
|
|
398
|
-
|
|
399
|
-
|
|
498
|
+
}
|
|
499
|
+
if (!owned) {
|
|
500
|
+
throw new Error(`Cannot stop non-child actor ${update.actor.id}`);
|
|
501
|
+
}
|
|
502
|
+
actorScope.system._unregister(update.actor);
|
|
503
|
+
return {
|
|
504
|
+
...snapshot,
|
|
505
|
+
children
|
|
506
|
+
};
|
|
507
|
+
}
|
|
508
|
+
function getTransitionActionRecord(action) {
|
|
509
|
+
if (typeof action === 'object' && action !== null && 'action' in action && typeof action.action === 'function') {
|
|
510
|
+
return action;
|
|
511
|
+
}
|
|
512
|
+
return undefined;
|
|
400
513
|
}
|
|
401
514
|
function pushSpawnedChild(actions, actor, id) {
|
|
402
|
-
pushBuiltInAction(actions, builtInActions['@xstate.spawn'], actor);
|
|
403
|
-
|
|
515
|
+
const action = pushBuiltInAction(actions, builtInActions['@xstate.spawn'], actor);
|
|
516
|
+
action.childUpdate = {
|
|
517
|
+
type: 'add',
|
|
518
|
+
actor,
|
|
519
|
+
id: id ?? actor.id
|
|
520
|
+
};
|
|
404
521
|
}
|
|
405
522
|
function createTransitionEnqueue(actorScope, actions, internalEvents, actorSubscriptions = false, createActors = true) {
|
|
406
523
|
const props = {
|
|
@@ -430,7 +547,7 @@ function createTransitionEnqueue(actorScope, actions, internalEvents, actorSubsc
|
|
|
430
547
|
id: options?.id ?? options?.registryKey ?? logic.id
|
|
431
548
|
};
|
|
432
549
|
}
|
|
433
|
-
const actor =
|
|
550
|
+
const actor = actorScope.system.createActorRef(logic, {
|
|
434
551
|
...options,
|
|
435
552
|
parent: actorScope.self
|
|
436
553
|
});
|
|
@@ -449,8 +566,11 @@ function createTransitionEnqueue(actorScope, actions, internalEvents, actorSubsc
|
|
|
449
566
|
},
|
|
450
567
|
stop: actor => {
|
|
451
568
|
if (actor) {
|
|
452
|
-
pushBuiltInAction(actions, builtInActions['@xstate.stop'], actorScope, actor);
|
|
453
|
-
|
|
569
|
+
const action = pushBuiltInAction(actions, builtInActions['@xstate.stop'], actorScope, actor);
|
|
570
|
+
action.childUpdate = {
|
|
571
|
+
type: 'remove',
|
|
572
|
+
actor
|
|
573
|
+
};
|
|
454
574
|
}
|
|
455
575
|
}
|
|
456
576
|
};
|
|
@@ -462,7 +582,7 @@ function createTransitionEnqueue(actorScope, actions, internalEvents, actorSubsc
|
|
|
462
582
|
eventType,
|
|
463
583
|
mapper
|
|
464
584
|
};
|
|
465
|
-
const listenerActor =
|
|
585
|
+
const listenerActor = actorScope.system.createActorRef(listenerLogic, {
|
|
466
586
|
input,
|
|
467
587
|
parent: actorScope.self
|
|
468
588
|
});
|
|
@@ -477,7 +597,7 @@ function createTransitionEnqueue(actorScope, actions, internalEvents, actorSubsc
|
|
|
477
597
|
actor,
|
|
478
598
|
mappers: normalizedMappers
|
|
479
599
|
};
|
|
480
|
-
const subscriptionActor =
|
|
600
|
+
const subscriptionActor = actorScope.system.createActorRef(subscriptionLogic, {
|
|
481
601
|
input,
|
|
482
602
|
parent: actorScope.self
|
|
483
603
|
});
|
|
@@ -496,6 +616,9 @@ function getBuiltInActionFields(action, args) {
|
|
|
496
616
|
{
|
|
497
617
|
const [actor] = args;
|
|
498
618
|
return {
|
|
619
|
+
kind: 'builtin',
|
|
620
|
+
exec: execSpawnEffect,
|
|
621
|
+
source: actor._parent,
|
|
499
622
|
actor,
|
|
500
623
|
id: actor.id,
|
|
501
624
|
logic: actor.logic,
|
|
@@ -507,6 +630,9 @@ function getBuiltInActionFields(action, args) {
|
|
|
507
630
|
{
|
|
508
631
|
const [, event, options] = args;
|
|
509
632
|
return {
|
|
633
|
+
kind: 'builtin',
|
|
634
|
+
exec: execRaiseEffect,
|
|
635
|
+
source: args[0].self,
|
|
510
636
|
event,
|
|
511
637
|
id: options?.id,
|
|
512
638
|
delay: options?.delay
|
|
@@ -516,6 +642,9 @@ function getBuiltInActionFields(action, args) {
|
|
|
516
642
|
{
|
|
517
643
|
const [, target, event, options] = args;
|
|
518
644
|
return {
|
|
645
|
+
kind: 'builtin',
|
|
646
|
+
exec: execSendToEffect,
|
|
647
|
+
source: args[0].self,
|
|
519
648
|
target,
|
|
520
649
|
event,
|
|
521
650
|
id: options?.id,
|
|
@@ -526,6 +655,9 @@ function getBuiltInActionFields(action, args) {
|
|
|
526
655
|
{
|
|
527
656
|
const [, id] = args;
|
|
528
657
|
return {
|
|
658
|
+
kind: 'builtin',
|
|
659
|
+
exec: execCancelEffect,
|
|
660
|
+
source: args[0].self,
|
|
529
661
|
id
|
|
530
662
|
};
|
|
531
663
|
}
|
|
@@ -533,7 +665,11 @@ function getBuiltInActionFields(action, args) {
|
|
|
533
665
|
{
|
|
534
666
|
const [, actor] = args;
|
|
535
667
|
return {
|
|
536
|
-
|
|
668
|
+
kind: 'builtin',
|
|
669
|
+
exec: execStopEffect,
|
|
670
|
+
source: args[0].self,
|
|
671
|
+
actor,
|
|
672
|
+
id: actor.id
|
|
537
673
|
};
|
|
538
674
|
}
|
|
539
675
|
default:
|
|
@@ -541,19 +677,91 @@ function getBuiltInActionFields(action, args) {
|
|
|
541
677
|
}
|
|
542
678
|
}
|
|
543
679
|
function createStartEffect(actor) {
|
|
544
|
-
const action = builtInActions['@xstate.start'];
|
|
545
680
|
const args = [actor];
|
|
546
|
-
return
|
|
681
|
+
return {
|
|
682
|
+
kind: 'builtin',
|
|
683
|
+
exec: execStartEffect,
|
|
547
684
|
type: XSTATE_START,
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
args
|
|
551
|
-
},
|
|
685
|
+
source: actor._parent,
|
|
686
|
+
params: undefined,
|
|
552
687
|
args,
|
|
553
|
-
exec: action.bind(null, actor),
|
|
554
688
|
actor,
|
|
555
689
|
id: actor.id
|
|
556
|
-
}
|
|
690
|
+
};
|
|
691
|
+
}
|
|
692
|
+
function createSpawnEffect(actor) {
|
|
693
|
+
const args = [actor];
|
|
694
|
+
return {
|
|
695
|
+
kind: 'builtin',
|
|
696
|
+
exec: execSpawnEffect,
|
|
697
|
+
type: XSTATE_SPAWN,
|
|
698
|
+
source: actor._parent,
|
|
699
|
+
params: undefined,
|
|
700
|
+
args,
|
|
701
|
+
actor,
|
|
702
|
+
id: actor.id,
|
|
703
|
+
logic: actor.logic,
|
|
704
|
+
src: actor.src,
|
|
705
|
+
input: actor.options?.input
|
|
706
|
+
};
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
/** @internal Creates an immediate actor-to-actor delivery effect. */
|
|
710
|
+
function createSendToEffect(actorScope, target, event) {
|
|
711
|
+
const args = [actorScope, target, event, {}];
|
|
712
|
+
return {
|
|
713
|
+
kind: 'builtin',
|
|
714
|
+
exec: execSendToEffect,
|
|
715
|
+
type: '@xstate.sendTo',
|
|
716
|
+
source: actorScope.self,
|
|
717
|
+
target,
|
|
718
|
+
event,
|
|
719
|
+
id: undefined,
|
|
720
|
+
delay: undefined,
|
|
721
|
+
params: undefined,
|
|
722
|
+
args
|
|
723
|
+
};
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
/** @internal Creates the terminal lifecycle effect for an actor. */
|
|
727
|
+
function createTerminationEffect(actorScope, snapshot) {
|
|
728
|
+
if (snapshot.status !== 'done' && snapshot.status !== 'error') {
|
|
729
|
+
throw new Error('Cannot terminate an active or stopped actor');
|
|
730
|
+
}
|
|
731
|
+
const termination = snapshot.status === 'done' ? {
|
|
732
|
+
status: 'done',
|
|
733
|
+
output: snapshot.output,
|
|
734
|
+
error: undefined
|
|
735
|
+
} : {
|
|
736
|
+
status: 'error',
|
|
737
|
+
output: undefined,
|
|
738
|
+
error: snapshot.error
|
|
739
|
+
};
|
|
740
|
+
const args = [actorScope.self, termination];
|
|
741
|
+
return {
|
|
742
|
+
kind: 'builtin',
|
|
743
|
+
exec: execTerminateEffect,
|
|
744
|
+
type: XSTATE_TERMINATE,
|
|
745
|
+
source: actorScope.self,
|
|
746
|
+
actor: actorScope.self,
|
|
747
|
+
id: actorScope.self.id,
|
|
748
|
+
...termination,
|
|
749
|
+
params: undefined,
|
|
750
|
+
args
|
|
751
|
+
};
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
/**
|
|
755
|
+
* Ensures that a newly terminal snapshot has exactly one ordered actor
|
|
756
|
+
* termination effect.
|
|
757
|
+
*
|
|
758
|
+
* @internal
|
|
759
|
+
*/
|
|
760
|
+
function finalizeTransitionResult(actorScope, previousSnapshot, [nextSnapshot, effects]) {
|
|
761
|
+
const becameTerminal = nextSnapshot.status === 'done' || nextSnapshot.status === 'error';
|
|
762
|
+
const wasTerminal = previousSnapshot?.status === 'done' || previousSnapshot?.status === 'error';
|
|
763
|
+
const hasTerminationEffect = effects.some(effect => typeof effect === 'object' && effect !== null && 'type' in effect && effect.type === XSTATE_TERMINATE);
|
|
764
|
+
return becameTerminal && !wasTerminal && !hasTerminationEffect ? [nextSnapshot, [...effects, createTerminationEffect(actorScope, nextSnapshot)]] : [nextSnapshot, effects];
|
|
557
765
|
}
|
|
558
766
|
|
|
559
767
|
/**
|
|
@@ -582,14 +790,19 @@ function deriveDeferredStarts(effects) {
|
|
|
582
790
|
return [...attachedStarts, ...childStarts];
|
|
583
791
|
}
|
|
584
792
|
function isBuiltInExecutableAction(action) {
|
|
585
|
-
return action
|
|
793
|
+
return action.kind === 'builtin';
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
/** Executes transition effects sequentially, awaiting each runtime operation. */
|
|
797
|
+
async function executeEffects(effects, runtime) {
|
|
798
|
+
for (const effect of effects) {
|
|
799
|
+
await effect.exec(runtime);
|
|
800
|
+
}
|
|
586
801
|
}
|
|
587
802
|
function resolveActionsWithContext(currentSnapshot, event, actorScope, actions) {
|
|
588
803
|
let intermediateSnapshot = currentSnapshot;
|
|
589
804
|
const executableActions = [];
|
|
590
805
|
for (const action of actions) {
|
|
591
|
-
const isInline = typeof action === 'function';
|
|
592
|
-
const resolvedAction = isInline ? action : typeof action === 'object' && 'action' in action && typeof action.action === 'function' ? action.action.bind(null, ...action.args) : false;
|
|
593
806
|
const actionArgs = {
|
|
594
807
|
context: intermediateSnapshot.context,
|
|
595
808
|
event,
|
|
@@ -601,20 +814,29 @@ function resolveActionsWithContext(currentSnapshot, event, actorScope, actions)
|
|
|
601
814
|
actions: currentSnapshot.machine.implementations.actions,
|
|
602
815
|
actorSources: currentSnapshot.machine.implementations.actorSources
|
|
603
816
|
};
|
|
817
|
+
const isInline = typeof action === 'function';
|
|
818
|
+
const actionRecord = getTransitionActionRecord(action);
|
|
819
|
+
const resolvedAction = isInline ? action : actionRecord ? actionRecord.action.bind(null, ...actionRecord.args) : false;
|
|
604
820
|
let actionParams = undefined;
|
|
605
821
|
if (typeof action === 'object' && action !== null) {
|
|
606
822
|
const {
|
|
607
823
|
type: _,
|
|
824
|
+
childUpdate: _childUpdate,
|
|
608
825
|
...emittedEventParams
|
|
609
826
|
} = action;
|
|
610
827
|
actionParams = emittedEventParams;
|
|
611
828
|
}
|
|
829
|
+
if (actionRecord?.childUpdate) {
|
|
830
|
+
intermediateSnapshot = applyChildUpdate(intermediateSnapshot, actionRecord.childUpdate, actorScope);
|
|
831
|
+
}
|
|
612
832
|
if (resolvedAction && '_special' in resolvedAction) {
|
|
613
833
|
executableActions.push({
|
|
834
|
+
kind: 'action',
|
|
835
|
+
exec: execCustomEffect,
|
|
614
836
|
type: typeof action === 'object' ? 'action' in action && typeof action.action === 'function' ? action.action.name ?? '(anonymous)' : action.type ?? '(anonymous)' : action.name || '(anonymous)',
|
|
615
837
|
params: actionParams,
|
|
616
838
|
args: [],
|
|
617
|
-
|
|
839
|
+
action: undefined
|
|
618
840
|
});
|
|
619
841
|
const specialAction = resolvedAction;
|
|
620
842
|
const res = specialAction(actionArgs, emptyEnqueueObject);
|
|
@@ -636,14 +858,26 @@ function resolveActionsWithContext(currentSnapshot, event, actorScope, actions)
|
|
|
636
858
|
}
|
|
637
859
|
if (!resolvedAction || !('resolve' in resolvedAction)) {
|
|
638
860
|
const builtInFields = typeof action === 'object' && action !== null && 'action' in action && typeof action.action === 'function' ? getBuiltInActionFields(action.action, action.args) : undefined;
|
|
861
|
+
const isEmittedEvent = typeof action === 'object' && action !== null && !actionRecord;
|
|
639
862
|
const executableAction = {
|
|
863
|
+
kind: builtInFields ? 'builtin' : isEmittedEvent ? 'emit' : 'action',
|
|
640
864
|
type: typeof action === 'object' ? 'action' in action && typeof action.action === 'function' ? action.action.name ?? '(anonymous)' : action.type : action.name || '(anonymous)',
|
|
641
|
-
params: actionParams,
|
|
865
|
+
params: builtInFields ? undefined : actionParams,
|
|
642
866
|
args: typeof action === 'object' && 'action' in action ? action.args : [],
|
|
643
|
-
|
|
867
|
+
...(builtInFields ? {} : isEmittedEvent ? {
|
|
868
|
+
source: actorScope.self,
|
|
869
|
+
event: action
|
|
870
|
+
} : {
|
|
871
|
+
action: actionRecord?.action ?? (isInline ? action : undefined)
|
|
872
|
+
}),
|
|
873
|
+
...(!builtInFields ? {
|
|
874
|
+
exec: isEmittedEvent ? execEmitEffect : execCustomEffect
|
|
875
|
+
} : {}),
|
|
644
876
|
...builtInFields
|
|
645
877
|
};
|
|
646
|
-
|
|
878
|
+
const typedExecutableAction = executableAction;
|
|
879
|
+
intermediateSnapshot = updateLogicalTimers(intermediateSnapshot, typedExecutableAction, actorScope);
|
|
880
|
+
executableActions.push(typedExecutableAction);
|
|
647
881
|
continue;
|
|
648
882
|
}
|
|
649
883
|
}
|
|
@@ -1423,6 +1657,17 @@ function microstep(transitions, currentSnapshot, actorScope, event, isInitial, i
|
|
|
1423
1657
|
return [[wrappedAction], undefined, undefined];
|
|
1424
1658
|
};
|
|
1425
1659
|
let nextState = currentSnapshot;
|
|
1660
|
+
const getInvokeStopActions = (stateNode, children) => {
|
|
1661
|
+
const actions = [];
|
|
1662
|
+
const enqueue = createTransitionEnqueue(actorScope, actions, []);
|
|
1663
|
+
for (const invokeDef of stateNode.invoke) {
|
|
1664
|
+
const child = children[invokeDef.id];
|
|
1665
|
+
if (child) {
|
|
1666
|
+
enqueue.stop(child);
|
|
1667
|
+
}
|
|
1668
|
+
}
|
|
1669
|
+
return actions;
|
|
1670
|
+
};
|
|
1426
1671
|
const exitStates = () => {
|
|
1427
1672
|
const statesToExit = computeExitSet(filteredTransitions, mutStateNodeSet, currentSnapshot, event, actorScope);
|
|
1428
1673
|
statesToExit.sort((a, b) => b.order - a.order);
|
|
@@ -1444,7 +1689,7 @@ function microstep(transitions, currentSnapshot, actorScope, event, isInitial, i
|
|
|
1444
1689
|
}
|
|
1445
1690
|
for (const exitStateNode of statesToExit) {
|
|
1446
1691
|
const stateInput = getStateInput(currentSnapshot, exitStateNode.id);
|
|
1447
|
-
const [exitActions, nextContext, internalEvents] = exitStateNode.exit ? getStateActionsAndContext(exitStateNode.exit, nextState.context, currentSnapshot.children, stateInput) : [[]];
|
|
1692
|
+
const [exitActions, nextContext, internalEvents] = exitStateNode.exit ? getStateActionsAndContext(exitStateNode.exit, nextState.context, currentSnapshot.children, stateInput) : [[], undefined, undefined];
|
|
1448
1693
|
if (internalEvents?.length) {
|
|
1449
1694
|
internalQueue.push(...internalEvents);
|
|
1450
1695
|
}
|
|
@@ -1456,12 +1701,11 @@ function microstep(transitions, currentSnapshot, actorScope, event, isInitial, i
|
|
|
1456
1701
|
const [resolvedState, resolvedActions] = resolveActionsWithContext(nextState, event, actorScope, exitActions);
|
|
1457
1702
|
nextState = resolvedState;
|
|
1458
1703
|
executableActions.push(...resolvedActions);
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
delete nextState.children[def.id];
|
|
1704
|
+
const invokeStopActions = getInvokeStopActions(exitStateNode, nextState.children);
|
|
1705
|
+
if (invokeStopActions.length) {
|
|
1706
|
+
const [stoppedState, stopEffects] = resolveActionsWithContext(nextState, event, actorScope, invokeStopActions);
|
|
1707
|
+
nextState = stoppedState;
|
|
1708
|
+
executableActions.push(...stopEffects);
|
|
1465
1709
|
}
|
|
1466
1710
|
mutStateNodeSet.delete(exitStateNode);
|
|
1467
1711
|
}
|
|
@@ -1632,7 +1876,7 @@ function microstep(transitions, currentSnapshot, actorScope, event, isInitial, i
|
|
|
1632
1876
|
}
|
|
1633
1877
|
const completedNodes = new Set();
|
|
1634
1878
|
const children = {
|
|
1635
|
-
...
|
|
1879
|
+
...nextState.children
|
|
1636
1880
|
};
|
|
1637
1881
|
for (const stateNodeToEnter of [...statesToEnter].sort((a, b) => a.order - b.order)) {
|
|
1638
1882
|
mutStateNodeSet.add(stateNodeToEnter);
|
|
@@ -1659,7 +1903,7 @@ function microstep(transitions, currentSnapshot, actorScope, event, isInitial, i
|
|
|
1659
1903
|
event,
|
|
1660
1904
|
output: getEventOutput(event)
|
|
1661
1905
|
}) : invokeDef.input;
|
|
1662
|
-
const actor =
|
|
1906
|
+
const actor = actorScope.system.createActorRef(logic, {
|
|
1663
1907
|
...invokeDef,
|
|
1664
1908
|
input,
|
|
1665
1909
|
parent: actorScope.self,
|
|
@@ -1788,6 +2032,12 @@ function microstep(transitions, currentSnapshot, actorScope, event, isInitial, i
|
|
|
1788
2032
|
const [resolvedState, resolvedActions] = resolveActionsWithContext(nextState, event, actorScope, allExitActions);
|
|
1789
2033
|
nextState = resolvedState;
|
|
1790
2034
|
executableActions.push(...resolvedActions);
|
|
2035
|
+
const [stoppedState, stopEffects] = stopChildren(nextState, event, actorScope);
|
|
2036
|
+
nextState = stoppedState;
|
|
2037
|
+
executableActions.push(...stopEffects);
|
|
2038
|
+
const [withoutTimers, cancelEffects] = cancelTimers(nextState, event, actorScope);
|
|
2039
|
+
nextState = withoutTimers;
|
|
2040
|
+
executableActions.push(...cancelEffects);
|
|
1791
2041
|
}
|
|
1792
2042
|
if (historyValue === currentSnapshot.historyValue && currentSnapshot._nodes.length === mutStateNodeSet.size && currentSnapshot._nodes.every(node => mutStateNodeSet.has(node))) {
|
|
1793
2043
|
// If context was changed (e.g. by entry actions during self-transition),
|
|
@@ -1861,9 +2111,27 @@ function getTransitionResult(transition, snapshot, event, actorScope, options) {
|
|
|
1861
2111
|
input: resolvedInput
|
|
1862
2112
|
};
|
|
1863
2113
|
}
|
|
1864
|
-
function macrostep(snapshot, event, actorScope, internalQueue) {
|
|
2114
|
+
function macrostep(snapshot, event, actorScope, internalQueue, initialMicrosteps = []) {
|
|
1865
2115
|
let nextSnapshot = snapshot;
|
|
1866
|
-
const microsteps =
|
|
2116
|
+
const microsteps = initialMicrosteps.slice();
|
|
2117
|
+
function completeMacrostep() {
|
|
2118
|
+
const effects = microsteps.flatMap(([, actions]) => actions);
|
|
2119
|
+
const starts = deriveDeferredStarts(effects);
|
|
2120
|
+
const shouldTerminate = (snapshot.status === 'active' || initialMicrosteps.length > 0) && (nextSnapshot.status === 'done' || nextSnapshot.status === 'error');
|
|
2121
|
+
if (starts.length || shouldTerminate) {
|
|
2122
|
+
const terminalEffects = shouldTerminate ? [...starts, createTerminationEffect(actorScope, nextSnapshot)] : starts;
|
|
2123
|
+
if (microsteps.length) {
|
|
2124
|
+
const [lastSnapshot, lastEffects] = microsteps.at(-1);
|
|
2125
|
+
microsteps[microsteps.length - 1] = [lastSnapshot, [...lastEffects, ...terminalEffects]];
|
|
2126
|
+
} else {
|
|
2127
|
+
microsteps.push([nextSnapshot, terminalEffects]);
|
|
2128
|
+
}
|
|
2129
|
+
}
|
|
2130
|
+
return {
|
|
2131
|
+
snapshot: nextSnapshot,
|
|
2132
|
+
microsteps
|
|
2133
|
+
};
|
|
2134
|
+
}
|
|
1867
2135
|
function addMicrostep(step, transitions) {
|
|
1868
2136
|
// collect microsteps; surfaced on the enclosing '@xstate.transition' event
|
|
1869
2137
|
// via its `microsteps[]` facet (there is no standalone microstep event)
|
|
@@ -1875,20 +2143,39 @@ function macrostep(snapshot, event, actorScope, internalQueue) {
|
|
|
1875
2143
|
|
|
1876
2144
|
// Handle stop event
|
|
1877
2145
|
if (event.type === XSTATE_STOP) {
|
|
1878
|
-
|
|
2146
|
+
const [stoppedChildrenSnapshot, stopEffects] = stopChildren(nextSnapshot, event, actorScope);
|
|
2147
|
+
const [withoutTimers, cancelEffects] = cancelTimers(stoppedChildrenSnapshot, event, actorScope);
|
|
2148
|
+
nextSnapshot = cloneMachineSnapshot(withoutTimers, {
|
|
1879
2149
|
status: 'stopped'
|
|
1880
2150
|
});
|
|
1881
|
-
addMicrostep([nextSnapshot, []], []);
|
|
1882
|
-
return
|
|
1883
|
-
snapshot: nextSnapshot,
|
|
1884
|
-
microsteps
|
|
1885
|
-
};
|
|
2151
|
+
addMicrostep([nextSnapshot, [...stopEffects, ...cancelEffects]], []);
|
|
2152
|
+
return completeMacrostep();
|
|
1886
2153
|
}
|
|
1887
2154
|
let nextEvent = event;
|
|
2155
|
+
if (event.type === XSTATE_TIMER) {
|
|
2156
|
+
const timer = nextSnapshot.timers[event.id];
|
|
2157
|
+
if (!timer) {
|
|
2158
|
+
return completeMacrostep();
|
|
2159
|
+
}
|
|
2160
|
+
const timers = {
|
|
2161
|
+
...nextSnapshot.timers
|
|
2162
|
+
};
|
|
2163
|
+
delete timers[timer.id];
|
|
2164
|
+
nextSnapshot = cloneMachineSnapshot(nextSnapshot, {
|
|
2165
|
+
timers
|
|
2166
|
+
});
|
|
2167
|
+
if (timer.type === '@xstate.raise') {
|
|
2168
|
+
internalQueue.push(timer.event);
|
|
2169
|
+
addMicrostep([nextSnapshot, []], []);
|
|
2170
|
+
} else {
|
|
2171
|
+
const target = timer.target === 'self' ? actorScope.self : timer.target;
|
|
2172
|
+
addMicrostep([nextSnapshot, [createSendToEffect(actorScope, target, timer.event)]], []);
|
|
2173
|
+
}
|
|
2174
|
+
}
|
|
1888
2175
|
|
|
1889
2176
|
// Assume the state is at rest (no raised events)
|
|
1890
2177
|
// Determine the next state based on the next microstep
|
|
1891
|
-
if (nextEvent.type !== XSTATE_INIT) {
|
|
2178
|
+
if (nextEvent.type !== XSTATE_INIT && nextEvent.type !== XSTATE_TIMER) {
|
|
1892
2179
|
const currentEvent = nextEvent;
|
|
1893
2180
|
const isErr = isErrorEvent(currentEvent);
|
|
1894
2181
|
const transitions = nextSnapshot.machine.getTransitionData(nextSnapshot, currentEvent, actorScope.self);
|
|
@@ -1901,10 +2188,7 @@ function macrostep(snapshot, event, actorScope, internalQueue) {
|
|
|
1901
2188
|
error: currentEvent.error
|
|
1902
2189
|
});
|
|
1903
2190
|
addMicrostep([nextSnapshot, []], []);
|
|
1904
|
-
return
|
|
1905
|
-
snapshot: nextSnapshot,
|
|
1906
|
-
microsteps
|
|
1907
|
-
};
|
|
2191
|
+
return completeMacrostep();
|
|
1908
2192
|
}
|
|
1909
2193
|
const step = microstep(transitions, snapshot, actorScope, nextEvent, false,
|
|
1910
2194
|
// isInitial
|
|
@@ -1942,13 +2226,7 @@ function macrostep(snapshot, event, actorScope, internalQueue) {
|
|
|
1942
2226
|
shouldSelectEventlessTransitions = nextSnapshot !== previousState;
|
|
1943
2227
|
addMicrostep(step, enabledTransitions);
|
|
1944
2228
|
}
|
|
1945
|
-
|
|
1946
|
-
stopChildren(nextSnapshot, actorScope);
|
|
1947
|
-
}
|
|
1948
|
-
return {
|
|
1949
|
-
snapshot: nextSnapshot,
|
|
1950
|
-
microsteps
|
|
1951
|
-
};
|
|
2229
|
+
return completeMacrostep();
|
|
1952
2230
|
}
|
|
1953
2231
|
|
|
1954
2232
|
/**
|
|
@@ -2008,17 +2286,29 @@ function transitionToHasEffect(transitionTo, context, event, snapshot, self, imp
|
|
|
2008
2286
|
}
|
|
2009
2287
|
return res !== undefined;
|
|
2010
2288
|
}
|
|
2011
|
-
function stopChildren(snapshot, actorScope) {
|
|
2289
|
+
function stopChildren(snapshot, event, actorScope) {
|
|
2012
2290
|
let children;
|
|
2013
2291
|
if (!snapshot.children || (children = Object.values(snapshot.children).filter(Boolean)).length === 0) {
|
|
2014
|
-
return snapshot;
|
|
2292
|
+
return [snapshot, []];
|
|
2015
2293
|
}
|
|
2294
|
+
const actions = [];
|
|
2295
|
+
const enqueue = createTransitionEnqueue(actorScope, actions, []);
|
|
2016
2296
|
for (const child of children) {
|
|
2017
|
-
|
|
2297
|
+
enqueue.stop(child);
|
|
2018
2298
|
}
|
|
2019
|
-
return
|
|
2020
|
-
|
|
2021
|
-
|
|
2299
|
+
return resolveActionsWithContext(snapshot, event, actorScope, actions);
|
|
2300
|
+
}
|
|
2301
|
+
function cancelTimers(snapshot, event, actorScope) {
|
|
2302
|
+
const timerIds = Object.keys(snapshot.timers);
|
|
2303
|
+
if (!timerIds.length) {
|
|
2304
|
+
return [snapshot, []];
|
|
2305
|
+
}
|
|
2306
|
+
const actions = [];
|
|
2307
|
+
const enqueue = createTransitionEnqueue(actorScope, actions, []);
|
|
2308
|
+
for (const id of timerIds) {
|
|
2309
|
+
enqueue.cancel(id);
|
|
2310
|
+
}
|
|
2311
|
+
return resolveActionsWithContext(snapshot, event, actorScope, actions);
|
|
2022
2312
|
}
|
|
2023
2313
|
function selectEventlessTransitions(snapshot, event, actorScope) {
|
|
2024
2314
|
const enabledTransitionSet = new Set();
|
|
@@ -2063,6 +2353,129 @@ function evaluateCandidate(candidate, event, snapshot, stateNode, self) {
|
|
|
2063
2353
|
return true;
|
|
2064
2354
|
}
|
|
2065
2355
|
|
|
2356
|
+
/** @internal */
|
|
2357
|
+
const snapshotActorRef = Symbol('xstate.snapshotActorRef');
|
|
2358
|
+
|
|
2359
|
+
/** Snapshot-scoped actor identity and system view. @internal */
|
|
2360
|
+
|
|
2361
|
+
function getSnapshotChildren(snapshot) {
|
|
2362
|
+
return 'children' in snapshot && snapshot.children ? snapshot.children : {};
|
|
2363
|
+
}
|
|
2364
|
+
|
|
2365
|
+
/**
|
|
2366
|
+
* Creates the isolated receptionist/id view used by a pure branch. Scheduling,
|
|
2367
|
+
* relaying and clocks remain delegated to the snapshot's execution system.
|
|
2368
|
+
*
|
|
2369
|
+
* @internal
|
|
2370
|
+
*/
|
|
2371
|
+
function createSnapshotSystem(baseSystem, self, children, baseState) {
|
|
2372
|
+
const registeredActors = new Map(baseState?.children ?? baseSystem.children);
|
|
2373
|
+
const keyedActors = new Map(baseState?.keyedActors ?? Object.entries(baseSystem.getAll()).filter(entry => !!entry[1]));
|
|
2374
|
+
const reverseKeyedActors = new WeakMap();
|
|
2375
|
+
let booksSelf = true;
|
|
2376
|
+
const system = {
|
|
2377
|
+
...baseSystem,
|
|
2378
|
+
children: registeredActors,
|
|
2379
|
+
keyedActors,
|
|
2380
|
+
reverseKeyedActors,
|
|
2381
|
+
_snapshot: {
|
|
2382
|
+
...(baseState?.snapshot ?? baseSystem._snapshot)
|
|
2383
|
+
},
|
|
2384
|
+
_bookId: () => {
|
|
2385
|
+
if (booksSelf) {
|
|
2386
|
+
booksSelf = false;
|
|
2387
|
+
return self.sessionId ?? self.id;
|
|
2388
|
+
}
|
|
2389
|
+
return `x:${system._snapshot._nextActorId++}`;
|
|
2390
|
+
},
|
|
2391
|
+
_register: (sessionId, actor) => {
|
|
2392
|
+
registeredActors.set(sessionId, actor);
|
|
2393
|
+
return sessionId;
|
|
2394
|
+
},
|
|
2395
|
+
_unregister: actor => {
|
|
2396
|
+
if (actor.sessionId) {
|
|
2397
|
+
registeredActors.delete(actor.sessionId);
|
|
2398
|
+
}
|
|
2399
|
+
const registryKey = reverseKeyedActors.get(actor);
|
|
2400
|
+
if (registryKey !== undefined && keyedActors.get(registryKey) === actor) {
|
|
2401
|
+
keyedActors.delete(registryKey);
|
|
2402
|
+
}
|
|
2403
|
+
reverseKeyedActors.delete(actor);
|
|
2404
|
+
},
|
|
2405
|
+
_set: (registryKey, actor) => {
|
|
2406
|
+
const existing = keyedActors.get(registryKey);
|
|
2407
|
+
if (existing && existing !== actor) {
|
|
2408
|
+
throw new Error(`Actor with registry key '${String(registryKey)}' already exists.`);
|
|
2409
|
+
}
|
|
2410
|
+
keyedActors.set(registryKey, actor);
|
|
2411
|
+
reverseKeyedActors.set(actor, registryKey);
|
|
2412
|
+
},
|
|
2413
|
+
get: registryKey => keyedActors.get(registryKey),
|
|
2414
|
+
getAll: () => Object.fromEntries(keyedActors),
|
|
2415
|
+
// Pure transition resolution must not leak topology inspection events into
|
|
2416
|
+
// a live runtime system.
|
|
2417
|
+
_sendInspectionEvent: () => {}
|
|
2418
|
+
};
|
|
2419
|
+
for (const [registryKey, actor] of keyedActors) {
|
|
2420
|
+
reverseKeyedActors.set(actor, registryKey);
|
|
2421
|
+
}
|
|
2422
|
+
for (const actor of Object.values(children)) {
|
|
2423
|
+
if (!actor) {
|
|
2424
|
+
continue;
|
|
2425
|
+
}
|
|
2426
|
+
if (actor.sessionId) {
|
|
2427
|
+
registeredActors.set(actor.sessionId, actor);
|
|
2428
|
+
}
|
|
2429
|
+
const registryKey = actor.registryKey;
|
|
2430
|
+
if (registryKey) {
|
|
2431
|
+
keyedActors.set(registryKey, actor);
|
|
2432
|
+
reverseKeyedActors.set(actor, registryKey);
|
|
2433
|
+
}
|
|
2434
|
+
}
|
|
2435
|
+
return system;
|
|
2436
|
+
}
|
|
2437
|
+
|
|
2438
|
+
/** Returns the actor identity associated with a transition snapshot. @internal */
|
|
2439
|
+
function getSnapshotActorRef(snapshot) {
|
|
2440
|
+
return snapshot[snapshotActorRef];
|
|
2441
|
+
}
|
|
2442
|
+
|
|
2443
|
+
/**
|
|
2444
|
+
* Copies an internal actor association without rebuilding its system view.
|
|
2445
|
+
*
|
|
2446
|
+
* @internal
|
|
2447
|
+
*/
|
|
2448
|
+
function copySnapshotActorRef(source, target) {
|
|
2449
|
+
const ref = getSnapshotActorRef(source);
|
|
2450
|
+
if (ref) {
|
|
2451
|
+
Object.defineProperty(target, snapshotActorRef, {
|
|
2452
|
+
configurable: true,
|
|
2453
|
+
value: ref
|
|
2454
|
+
});
|
|
2455
|
+
}
|
|
2456
|
+
}
|
|
2457
|
+
|
|
2458
|
+
/**
|
|
2459
|
+
* Associates a transition snapshot with an actor and an immutable system view.
|
|
2460
|
+
* The internal association is excluded from enumeration and persistence.
|
|
2461
|
+
*
|
|
2462
|
+
* @internal
|
|
2463
|
+
*/
|
|
2464
|
+
function setSnapshotActorRef(snapshot, actor, baseSystem = actor.system) {
|
|
2465
|
+
const system = createSnapshotSystem(baseSystem, actor, getSnapshotChildren(snapshot));
|
|
2466
|
+
Object.defineProperty(snapshot, snapshotActorRef, {
|
|
2467
|
+
configurable: true,
|
|
2468
|
+
value: {
|
|
2469
|
+
actor,
|
|
2470
|
+
systemState: {
|
|
2471
|
+
children: system.children,
|
|
2472
|
+
keyedActors: system.keyedActors,
|
|
2473
|
+
snapshot: system._snapshot
|
|
2474
|
+
}
|
|
2475
|
+
}
|
|
2476
|
+
});
|
|
2477
|
+
}
|
|
2478
|
+
|
|
2066
2479
|
function isMachineSnapshot(value) {
|
|
2067
2480
|
return !!value && typeof value === 'object' && 'machine' in value && 'value' in value;
|
|
2068
2481
|
}
|
|
@@ -2082,6 +2495,7 @@ const machineSnapshotToJSON = function toJSON() {
|
|
|
2082
2495
|
const {
|
|
2083
2496
|
_nodes: nodes,
|
|
2084
2497
|
_stateInputs,
|
|
2498
|
+
[snapshotActorRef]: _actorRef,
|
|
2085
2499
|
tags,
|
|
2086
2500
|
machine,
|
|
2087
2501
|
getMeta,
|
|
@@ -2118,8 +2532,8 @@ function collectTags(stateNodes) {
|
|
|
2118
2532
|
}
|
|
2119
2533
|
return tags;
|
|
2120
2534
|
}
|
|
2121
|
-
function createMachineSnapshot(config, machine) {
|
|
2122
|
-
|
|
2535
|
+
function createMachineSnapshot(config, machine, actorRef) {
|
|
2536
|
+
const snapshot = {
|
|
2123
2537
|
status: config.status,
|
|
2124
2538
|
output: config.output,
|
|
2125
2539
|
error: config.error,
|
|
@@ -2129,8 +2543,10 @@ function createMachineSnapshot(config, machine) {
|
|
|
2129
2543
|
value: config.value ?? getStateValue(machine.root, config._nodes),
|
|
2130
2544
|
tags: collectTags(config._nodes),
|
|
2131
2545
|
children: config.children,
|
|
2546
|
+
timers: config.timers ?? {},
|
|
2132
2547
|
historyValue: config.historyValue || {},
|
|
2133
2548
|
_stateInputs: config._stateInputs || {},
|
|
2549
|
+
_nextTimerId: config._nextTimerId ?? 0,
|
|
2134
2550
|
matches: machineSnapshotMatches,
|
|
2135
2551
|
hasTag: machineSnapshotHasTag,
|
|
2136
2552
|
can: machineSnapshotCan,
|
|
@@ -2138,6 +2554,10 @@ function createMachineSnapshot(config, machine) {
|
|
|
2138
2554
|
getInputs: machineSnapshotGetInputs,
|
|
2139
2555
|
toJSON: machineSnapshotToJSON
|
|
2140
2556
|
};
|
|
2557
|
+
if (actorRef) {
|
|
2558
|
+
setSnapshotActorRef(snapshot, actorRef);
|
|
2559
|
+
}
|
|
2560
|
+
return snapshot;
|
|
2141
2561
|
}
|
|
2142
2562
|
function cloneMachineSnapshot(snapshot, config = {}) {
|
|
2143
2563
|
const configWithSnapshot = {
|
|
@@ -2145,7 +2565,7 @@ function cloneMachineSnapshot(snapshot, config = {}) {
|
|
|
2145
2565
|
...config
|
|
2146
2566
|
};
|
|
2147
2567
|
if ((config._nodes ?? snapshot._nodes) === snapshot._nodes) {
|
|
2148
|
-
|
|
2568
|
+
const clonedSnapshot = {
|
|
2149
2569
|
status: configWithSnapshot.status,
|
|
2150
2570
|
output: configWithSnapshot.output,
|
|
2151
2571
|
error: configWithSnapshot.error,
|
|
@@ -2155,8 +2575,10 @@ function cloneMachineSnapshot(snapshot, config = {}) {
|
|
|
2155
2575
|
value: snapshot.value,
|
|
2156
2576
|
tags: snapshot.tags,
|
|
2157
2577
|
children: configWithSnapshot.children,
|
|
2578
|
+
timers: configWithSnapshot.timers ?? {},
|
|
2158
2579
|
historyValue: configWithSnapshot.historyValue || {},
|
|
2159
2580
|
_stateInputs: configWithSnapshot._stateInputs || {},
|
|
2581
|
+
_nextTimerId: configWithSnapshot._nextTimerId ?? 0,
|
|
2160
2582
|
matches: machineSnapshotMatches,
|
|
2161
2583
|
hasTag: machineSnapshotHasTag,
|
|
2162
2584
|
can: machineSnapshotCan,
|
|
@@ -2164,11 +2586,15 @@ function cloneMachineSnapshot(snapshot, config = {}) {
|
|
|
2164
2586
|
getInputs: machineSnapshotGetInputs,
|
|
2165
2587
|
toJSON: machineSnapshotToJSON
|
|
2166
2588
|
};
|
|
2589
|
+
copySnapshotActorRef(snapshot, clonedSnapshot);
|
|
2590
|
+
return clonedSnapshot;
|
|
2167
2591
|
}
|
|
2168
|
-
|
|
2592
|
+
const clonedSnapshot = createMachineSnapshot({
|
|
2169
2593
|
...configWithSnapshot,
|
|
2170
2594
|
value: undefined
|
|
2171
2595
|
}, snapshot.machine);
|
|
2596
|
+
copySnapshotActorRef(snapshot, clonedSnapshot);
|
|
2597
|
+
return clonedSnapshot;
|
|
2172
2598
|
}
|
|
2173
2599
|
function serializeHistoryValue(historyValue) {
|
|
2174
2600
|
const result = {};
|
|
@@ -2189,6 +2615,7 @@ function getPersistedSnapshot(snapshot, options) {
|
|
|
2189
2615
|
tags,
|
|
2190
2616
|
machine,
|
|
2191
2617
|
children,
|
|
2618
|
+
timers,
|
|
2192
2619
|
context,
|
|
2193
2620
|
can,
|
|
2194
2621
|
hasTag,
|
|
@@ -2199,6 +2626,7 @@ function getPersistedSnapshot(snapshot, options) {
|
|
|
2199
2626
|
...jsonValues
|
|
2200
2627
|
} = snapshot;
|
|
2201
2628
|
const childrenJson = {};
|
|
2629
|
+
const timersJson = {};
|
|
2202
2630
|
for (const id in children) {
|
|
2203
2631
|
const child = children[id];
|
|
2204
2632
|
childrenJson[id] = {
|
|
@@ -2208,10 +2636,36 @@ function getPersistedSnapshot(snapshot, options) {
|
|
|
2208
2636
|
syncSnapshot: child._syncSnapshot
|
|
2209
2637
|
};
|
|
2210
2638
|
}
|
|
2639
|
+
for (const id in timers) {
|
|
2640
|
+
const timer = timers[id];
|
|
2641
|
+
let target;
|
|
2642
|
+
if (timer.target === 'self') {
|
|
2643
|
+
target = 'self';
|
|
2644
|
+
} else {
|
|
2645
|
+
const childId = Object.entries(children).find(([, child]) => child === timer.target)?.[0];
|
|
2646
|
+
if (childId) {
|
|
2647
|
+
target = childId;
|
|
2648
|
+
} else if (timer.target === getSnapshotActorRef(snapshot)?.actor._parent) {
|
|
2649
|
+
target = {
|
|
2650
|
+
type: 'parent'
|
|
2651
|
+
};
|
|
2652
|
+
} else {
|
|
2653
|
+
throw new Error(`Unable to persist timer '${id}': target actor '${timer.target.id}' is no longer addressable from this snapshot.`);
|
|
2654
|
+
}
|
|
2655
|
+
}
|
|
2656
|
+
timersJson[id] = {
|
|
2657
|
+
id: timer.id,
|
|
2658
|
+
delay: timer.delay,
|
|
2659
|
+
type: timer.type,
|
|
2660
|
+
event: timer.event,
|
|
2661
|
+
target
|
|
2662
|
+
};
|
|
2663
|
+
}
|
|
2211
2664
|
const persisted = {
|
|
2212
2665
|
...jsonValues,
|
|
2213
2666
|
context: persistContext(context),
|
|
2214
2667
|
children: childrenJson,
|
|
2668
|
+
timers: timersJson,
|
|
2215
2669
|
historyValue: serializeHistoryValue(jsonValues.historyValue)
|
|
2216
2670
|
};
|
|
2217
2671
|
if (_stateInputs && Object.keys(_stateInputs).length > 0) {
|
|
@@ -2469,11 +2923,17 @@ function matchesEventDescriptor(eventType, descriptor) {
|
|
|
2469
2923
|
return true;
|
|
2470
2924
|
}
|
|
2471
2925
|
|
|
2472
|
-
|
|
2926
|
+
/**
|
|
2927
|
+
* Runtime operations used to execute effects.
|
|
2928
|
+
*
|
|
2929
|
+
* An external interpreter can override these operations while the default actor
|
|
2930
|
+
* system provides the local in-memory implementation.
|
|
2931
|
+
*/
|
|
2932
|
+
|
|
2933
|
+
function createScheduledTimerId(actor, id) {
|
|
2473
2934
|
return `${actor.sessionId}.${id}`;
|
|
2474
2935
|
}
|
|
2475
2936
|
function createRuntimeSystem(rootActor, options) {
|
|
2476
|
-
let idCounter = 0;
|
|
2477
2937
|
const children = new Map();
|
|
2478
2938
|
const keyedActors = new Map();
|
|
2479
2939
|
const reverseKeyedActors = new WeakMap();
|
|
@@ -2501,42 +2961,50 @@ function createRuntimeSystem(rootActor, options) {
|
|
|
2501
2961
|
});
|
|
2502
2962
|
};
|
|
2503
2963
|
const scheduler = {
|
|
2504
|
-
schedule: (source,
|
|
2505
|
-
|
|
2506
|
-
|
|
2964
|
+
schedule: (source, id, delay) => {
|
|
2965
|
+
const existingId = createScheduledTimerId(source, id);
|
|
2966
|
+
if (timerMap[existingId] !== undefined) {
|
|
2967
|
+
scheduler.cancel(source, id);
|
|
2968
|
+
}
|
|
2969
|
+
const timer = source.getSnapshot()?.timers?.[id];
|
|
2970
|
+
if (timer) {
|
|
2971
|
+
const target = timer.target === 'self' ? source : timer.target;
|
|
2972
|
+
recordSent(source, target, timer.event, delay, id);
|
|
2973
|
+
}
|
|
2974
|
+
const scheduledAt = Date.now();
|
|
2975
|
+
const scheduledTimer = {
|
|
2507
2976
|
source,
|
|
2508
|
-
target,
|
|
2509
|
-
event,
|
|
2510
2977
|
delay,
|
|
2511
2978
|
id,
|
|
2512
|
-
|
|
2979
|
+
scheduledAt,
|
|
2980
|
+
dueAt: scheduledAt + delay
|
|
2513
2981
|
};
|
|
2514
|
-
const
|
|
2515
|
-
system._snapshot.
|
|
2982
|
+
const scheduledTimerId = createScheduledTimerId(source, id);
|
|
2983
|
+
system._snapshot._scheduledTimers[scheduledTimerId] = scheduledTimer;
|
|
2516
2984
|
const timeout = clock.setTimeout(() => {
|
|
2517
|
-
delete timerMap[
|
|
2518
|
-
delete system._snapshot.
|
|
2519
|
-
|
|
2520
|
-
|
|
2521
|
-
|
|
2522
|
-
|
|
2985
|
+
delete timerMap[scheduledTimerId];
|
|
2986
|
+
delete system._snapshot._scheduledTimers[scheduledTimerId];
|
|
2987
|
+
deliver(source, source, {
|
|
2988
|
+
type: XSTATE_TIMER,
|
|
2989
|
+
id
|
|
2990
|
+
});
|
|
2523
2991
|
}, delay);
|
|
2524
|
-
timerMap[
|
|
2992
|
+
timerMap[scheduledTimerId] = timeout;
|
|
2525
2993
|
},
|
|
2526
2994
|
cancel: (source, id) => {
|
|
2527
|
-
const
|
|
2528
|
-
const timeout = timerMap[
|
|
2529
|
-
delete timerMap[
|
|
2530
|
-
delete system._snapshot.
|
|
2995
|
+
const scheduledTimerId = createScheduledTimerId(source, id);
|
|
2996
|
+
const timeout = timerMap[scheduledTimerId];
|
|
2997
|
+
delete timerMap[scheduledTimerId];
|
|
2998
|
+
delete system._snapshot._scheduledTimers[scheduledTimerId];
|
|
2531
2999
|
if (timeout !== undefined) {
|
|
2532
3000
|
clock.clearTimeout(timeout);
|
|
2533
3001
|
}
|
|
2534
3002
|
},
|
|
2535
3003
|
cancelAll: actor => {
|
|
2536
|
-
for (const
|
|
2537
|
-
const
|
|
2538
|
-
if (
|
|
2539
|
-
scheduler.cancel(actor,
|
|
3004
|
+
for (const scheduledTimerId in system._snapshot._scheduledTimers) {
|
|
3005
|
+
const scheduledTimer = system._snapshot._scheduledTimers[scheduledTimerId];
|
|
3006
|
+
if (scheduledTimer.source === actor) {
|
|
3007
|
+
scheduler.cancel(actor, scheduledTimer.id);
|
|
2540
3008
|
}
|
|
2541
3009
|
}
|
|
2542
3010
|
}
|
|
@@ -2569,9 +3037,10 @@ function createRuntimeSystem(rootActor, options) {
|
|
|
2569
3037
|
reverseKeyedActors,
|
|
2570
3038
|
keyedActors,
|
|
2571
3039
|
_snapshot: {
|
|
2572
|
-
|
|
3040
|
+
_scheduledTimers: (options?.snapshot && options.snapshot.scheduler) ?? {},
|
|
3041
|
+
_nextActorId: 0
|
|
2573
3042
|
},
|
|
2574
|
-
_bookId: () => `x:${
|
|
3043
|
+
_bookId: () => `x:${system._snapshot._nextActorId++}`,
|
|
2575
3044
|
_register: (sessionId, actor) => {
|
|
2576
3045
|
children.set(sessionId, actor);
|
|
2577
3046
|
return sessionId;
|
|
@@ -2580,7 +3049,9 @@ function createRuntimeSystem(rootActor, options) {
|
|
|
2580
3049
|
children.delete(actor.sessionId);
|
|
2581
3050
|
const registryKey = reverseKeyedActors.get(actor);
|
|
2582
3051
|
if (registryKey !== undefined) {
|
|
2583
|
-
keyedActors.
|
|
3052
|
+
if (keyedActors.get(registryKey) === actor) {
|
|
3053
|
+
keyedActors.delete(registryKey);
|
|
3054
|
+
}
|
|
2584
3055
|
reverseKeyedActors.delete(actor);
|
|
2585
3056
|
}
|
|
2586
3057
|
},
|
|
@@ -2608,35 +3079,48 @@ function createRuntimeSystem(rootActor, options) {
|
|
|
2608
3079
|
};
|
|
2609
3080
|
},
|
|
2610
3081
|
_sendInspectionEvent: sendInspectionEvent,
|
|
2611
|
-
|
|
3082
|
+
createActorRef: options.createActorRef,
|
|
3083
|
+
spawnActor: () => {},
|
|
3084
|
+
startActor: actor => {
|
|
3085
|
+
actor.start();
|
|
3086
|
+
},
|
|
3087
|
+
stopActor: actor => {
|
|
3088
|
+
actor._stop();
|
|
3089
|
+
},
|
|
3090
|
+
terminateActor: (actor, termination) => {
|
|
3091
|
+
actor._terminate(termination);
|
|
3092
|
+
},
|
|
3093
|
+
sendEvent: (source, target, event) => {
|
|
2612
3094
|
recordSent(source, target, event);
|
|
2613
3095
|
deliver(source, target, event);
|
|
2614
3096
|
},
|
|
3097
|
+
emitEvent: (source, event) => source._emit(event),
|
|
3098
|
+
scheduleTimer: (source, id, delay) => scheduler.schedule(source, id, delay),
|
|
3099
|
+
cancelTimer: (source, id) => scheduler.cancel(source, id),
|
|
3100
|
+
cancelAllTimers: source => scheduler.cancelAll(source),
|
|
3101
|
+
_relay: (source, target, event) => system.sendEvent(source, target, event),
|
|
2615
3102
|
scheduler,
|
|
2616
3103
|
getSnapshot: () => {
|
|
2617
3104
|
return {
|
|
2618
|
-
|
|
2619
|
-
...system._snapshot.
|
|
3105
|
+
_scheduledTimers: {
|
|
3106
|
+
...system._snapshot._scheduledTimers
|
|
2620
3107
|
}
|
|
2621
3108
|
};
|
|
2622
3109
|
},
|
|
2623
3110
|
start: () => {
|
|
2624
|
-
const
|
|
2625
|
-
system._snapshot.
|
|
2626
|
-
for (const scheduledId in
|
|
3111
|
+
const scheduledTimers = system._snapshot._scheduledTimers;
|
|
3112
|
+
system._snapshot._scheduledTimers = {};
|
|
3113
|
+
for (const scheduledId in scheduledTimers) {
|
|
2627
3114
|
const {
|
|
2628
3115
|
source,
|
|
2629
|
-
|
|
2630
|
-
event,
|
|
2631
|
-
delay,
|
|
3116
|
+
dueAt,
|
|
2632
3117
|
id
|
|
2633
|
-
} =
|
|
2634
|
-
|
|
3118
|
+
} = scheduledTimers[scheduledId];
|
|
3119
|
+
system.scheduleTimer(source, id, Math.max(0, dueAt - Date.now()));
|
|
2635
3120
|
}
|
|
2636
3121
|
},
|
|
2637
3122
|
_clock: clock,
|
|
2638
|
-
_logger: logger
|
|
2639
|
-
_timerStrategy: options.timers
|
|
3123
|
+
_logger: logger
|
|
2640
3124
|
};
|
|
2641
3125
|
return system;
|
|
2642
3126
|
}
|
|
@@ -2671,22 +3155,13 @@ function safeCall(fn, arg) {
|
|
|
2671
3155
|
reportUnhandledError(err);
|
|
2672
3156
|
}
|
|
2673
3157
|
}
|
|
2674
|
-
function isExecutableActionObject(effect) {
|
|
2675
|
-
return typeof effect === 'object' && effect !== null && 'args' in effect && 'exec' in effect;
|
|
2676
|
-
}
|
|
2677
3158
|
function executeExecutableEffects(effects, actorScope) {
|
|
2678
3159
|
if (!effects?.length) {
|
|
2679
|
-
return
|
|
3160
|
+
return;
|
|
2680
3161
|
}
|
|
2681
|
-
const logicEffects = [];
|
|
2682
3162
|
for (const effect of effects) {
|
|
2683
|
-
|
|
2684
|
-
actorScope.actionExecutor(effect);
|
|
2685
|
-
} else {
|
|
2686
|
-
logicEffects.push(effect);
|
|
2687
|
-
}
|
|
3163
|
+
actorScope.actionExecutor(effect);
|
|
2688
3164
|
}
|
|
2689
|
-
return logicEffects;
|
|
2690
3165
|
}
|
|
2691
3166
|
|
|
2692
3167
|
/**
|
|
@@ -2752,8 +3227,7 @@ class Actor {
|
|
|
2752
3227
|
this.src = void 0;
|
|
2753
3228
|
// array of functions to defer
|
|
2754
3229
|
this._deferred = [];
|
|
2755
|
-
|
|
2756
|
-
this._pendingEffects = void 0;
|
|
3230
|
+
this._restored = false;
|
|
2757
3231
|
const resolvedOptions = {
|
|
2758
3232
|
...defaultOptions,
|
|
2759
3233
|
...options
|
|
@@ -2770,7 +3244,7 @@ class Actor {
|
|
|
2770
3244
|
this.system = parent ? parent.system : resolvedOptions._systemRef?.current ?? createRuntimeSystem(this, {
|
|
2771
3245
|
clock,
|
|
2772
3246
|
logger,
|
|
2773
|
-
|
|
3247
|
+
createActorRef: (childLogic, childOptions) => createActor(childLogic, childOptions)
|
|
2774
3248
|
});
|
|
2775
3249
|
if (!parent && resolvedOptions._systemRef && !resolvedOptions._systemRef.current) {
|
|
2776
3250
|
resolvedOptions._systemRef.current = this.system;
|
|
@@ -2803,15 +3277,7 @@ class Actor {
|
|
|
2803
3277
|
}
|
|
2804
3278
|
child._stop();
|
|
2805
3279
|
},
|
|
2806
|
-
emit: emittedEvent =>
|
|
2807
|
-
for (const listeners of [this.eventListeners.get(emittedEvent.type), this.eventListeners.get('*')]) {
|
|
2808
|
-
if (listeners) {
|
|
2809
|
-
for (const handler of listeners) {
|
|
2810
|
-
safeCall(handler, emittedEvent);
|
|
2811
|
-
}
|
|
2812
|
-
}
|
|
2813
|
-
}
|
|
2814
|
-
},
|
|
3280
|
+
emit: emittedEvent => this.system.emitEvent(this, emittedEvent),
|
|
2815
3281
|
actionExecutor: action => {
|
|
2816
3282
|
const exec = () => {
|
|
2817
3283
|
// Record every executed action for the '@xstate.transition' inspection
|
|
@@ -2820,13 +3286,10 @@ class Actor {
|
|
|
2820
3286
|
type: action.type,
|
|
2821
3287
|
params: action.params
|
|
2822
3288
|
});
|
|
2823
|
-
if (!action.exec) {
|
|
2824
|
-
return;
|
|
2825
|
-
}
|
|
2826
3289
|
const saveExecutingCustomAction = executingCustomAction;
|
|
2827
3290
|
try {
|
|
2828
3291
|
executingCustomAction = true;
|
|
2829
|
-
action.exec();
|
|
3292
|
+
void action.exec();
|
|
2830
3293
|
} finally {
|
|
2831
3294
|
executingCustomAction = saveExecutingCustomAction;
|
|
2832
3295
|
}
|
|
@@ -2859,38 +3322,29 @@ class Actor {
|
|
|
2859
3322
|
|
|
2860
3323
|
// prepare to collect initial microsteps during initialTransition
|
|
2861
3324
|
this._collectedMicrosteps = [];
|
|
2862
|
-
|
|
2863
|
-
|
|
2864
|
-
// Pending effects are restored by this actor on `start()`; the logic
|
|
2865
|
-
// only sees the rest of the persisted snapshot.
|
|
2866
|
-
const {
|
|
2867
|
-
_pendingEffects,
|
|
2868
|
-
...rest
|
|
2869
|
-
} = persistedState;
|
|
2870
|
-
this._pendingEffects = _pendingEffects;
|
|
2871
|
-
persistedState = rest;
|
|
2872
|
-
}
|
|
3325
|
+
const persistedState = options?.snapshot ?? options?.state;
|
|
3326
|
+
this._restored = persistedState !== undefined;
|
|
2873
3327
|
try {
|
|
2874
3328
|
if (persistedState) {
|
|
2875
|
-
this.
|
|
3329
|
+
this._setSnapshot(this.logic.restoreSnapshot ? this.logic.restoreSnapshot(persistedState, this._actorScope) : persistedState);
|
|
2876
3330
|
} else if (options?._inert) {
|
|
2877
3331
|
// Inert actors (createInertActorScope) only anchor a scope for pure
|
|
2878
3332
|
// transition functions; computing an initial snapshot here would run
|
|
2879
3333
|
// init-time side effects (context factories, entry) a second time.
|
|
2880
3334
|
} else {
|
|
2881
|
-
const [snapshot, effects] = this.logic.initialTransition(this.options?.input, this._actorScope);
|
|
2882
|
-
this.
|
|
3335
|
+
const [snapshot, effects] = finalizeTransitionResult(this._actorScope, undefined, this.logic.initialTransition(this.options?.input, this._actorScope));
|
|
3336
|
+
this._setSnapshot(snapshot);
|
|
2883
3337
|
this._initialEffects = effects;
|
|
2884
3338
|
}
|
|
2885
3339
|
} catch (err) {
|
|
2886
3340
|
// if we get here then it means that we assign a value to this._snapshot that is not of the correct type
|
|
2887
3341
|
// we can't get the true `TSnapshot & { status: 'error'; }`, it's impossible
|
|
2888
3342
|
// so right now this is a lie of sorts
|
|
2889
|
-
this.
|
|
3343
|
+
this._setSnapshot({
|
|
2890
3344
|
status: 'error',
|
|
2891
3345
|
output: undefined,
|
|
2892
3346
|
error: err
|
|
2893
|
-
};
|
|
3347
|
+
});
|
|
2894
3348
|
// discard any functions deferred during the failed initial snapshot
|
|
2895
3349
|
// computation so they can't run against an inconsistent actor
|
|
2896
3350
|
this._deferred.length = 0;
|
|
@@ -2911,12 +3365,17 @@ class Actor {
|
|
|
2911
3365
|
snapshot: this._snapshot
|
|
2912
3366
|
});
|
|
2913
3367
|
}
|
|
3368
|
+
/** Associates each live snapshot with this actor for later pure transitions. */
|
|
3369
|
+
_setSnapshot(snapshot) {
|
|
3370
|
+
this._snapshot = snapshot;
|
|
3371
|
+
setSnapshotActorRef(snapshot, this);
|
|
3372
|
+
}
|
|
2914
3373
|
_setErrorSnapshot(err, snapshot = this._snapshot) {
|
|
2915
|
-
this.
|
|
3374
|
+
this._setSnapshot({
|
|
2916
3375
|
...snapshot,
|
|
2917
3376
|
status: 'error',
|
|
2918
3377
|
error: err
|
|
2919
|
-
};
|
|
3378
|
+
});
|
|
2920
3379
|
}
|
|
2921
3380
|
|
|
2922
3381
|
/** Recover via the logic's error event if possible; otherwise error out. */
|
|
@@ -2934,11 +3393,10 @@ class Actor {
|
|
|
2934
3393
|
return false;
|
|
2935
3394
|
}
|
|
2936
3395
|
try {
|
|
2937
|
-
const [nextSnapshot, effects] = this.logic.transition(snapshot, errorEvent, this._actorScope);
|
|
2938
|
-
this.
|
|
2939
|
-
|
|
3396
|
+
const [nextSnapshot, effects] = finalizeTransitionResult(this._actorScope, snapshot, this.logic.transition(snapshot, errorEvent, this._actorScope));
|
|
3397
|
+
this._setSnapshot(nextSnapshot);
|
|
3398
|
+
executeExecutableEffects(effects, this._actorScope);
|
|
2940
3399
|
this.update(nextSnapshot, errorEvent);
|
|
2941
|
-
this.logic.executeEffects?.(logicEffects, this._actorScope);
|
|
2942
3400
|
return true;
|
|
2943
3401
|
} catch {
|
|
2944
3402
|
return false;
|
|
@@ -2951,7 +3409,7 @@ class Actor {
|
|
|
2951
3409
|
}
|
|
2952
3410
|
update(snapshot, event) {
|
|
2953
3411
|
// Update state
|
|
2954
|
-
this.
|
|
3412
|
+
this._setSnapshot(snapshot);
|
|
2955
3413
|
|
|
2956
3414
|
// Execute deferred effects
|
|
2957
3415
|
for (let i = 0; i < this._deferred.length; i++) {
|
|
@@ -2969,6 +3427,7 @@ class Actor {
|
|
|
2969
3427
|
return;
|
|
2970
3428
|
}
|
|
2971
3429
|
this._setErrorSnapshot(err, snapshot);
|
|
3430
|
+
this._error(err);
|
|
2972
3431
|
break;
|
|
2973
3432
|
}
|
|
2974
3433
|
}
|
|
@@ -2977,26 +3436,6 @@ class Actor {
|
|
|
2977
3436
|
case 'active':
|
|
2978
3437
|
this._next(snapshot);
|
|
2979
3438
|
break;
|
|
2980
|
-
case 'done':
|
|
2981
|
-
{
|
|
2982
|
-
// next observers are meant to be notified about done snapshots
|
|
2983
|
-
// this can be seen as something that is different from how observable work
|
|
2984
|
-
// but with observables `complete` callback is called without any arguments
|
|
2985
|
-
// it's more ergonomic for XState to treat a done snapshot as a "next" value
|
|
2986
|
-
// and the completion event as something that is separate,
|
|
2987
|
-
// something that merely follows emitting that done snapshot
|
|
2988
|
-
this._next(snapshot);
|
|
2989
|
-
this._stopProcedure();
|
|
2990
|
-
this._complete();
|
|
2991
|
-
const doneEvent = createDoneActorEvent(this.id, this._snapshot.output);
|
|
2992
|
-
if (this._parent) {
|
|
2993
|
-
this.system._relay(this, this._parent, doneEvent);
|
|
2994
|
-
}
|
|
2995
|
-
break;
|
|
2996
|
-
}
|
|
2997
|
-
case 'error':
|
|
2998
|
-
this._error(this._snapshot.error);
|
|
2999
|
-
break;
|
|
3000
3439
|
}
|
|
3001
3440
|
this.system._sendInspectionEvent({
|
|
3002
3441
|
type: '@xstate.transition',
|
|
@@ -3021,8 +3460,7 @@ class Actor {
|
|
|
3021
3460
|
}
|
|
3022
3461
|
this._forceDeferredActions = true;
|
|
3023
3462
|
try {
|
|
3024
|
-
|
|
3025
|
-
this.logic.executeEffects?.(logicEffects, this._actorScope);
|
|
3463
|
+
executeExecutableEffects(this._initialEffects, this._actorScope);
|
|
3026
3464
|
this._initialEffects = undefined;
|
|
3027
3465
|
return true;
|
|
3028
3466
|
} catch (err) {
|
|
@@ -3193,6 +3631,14 @@ class Actor {
|
|
|
3193
3631
|
case 'done':
|
|
3194
3632
|
// a state machine can be "done" upon initialization (it could reach a final state using initial microsteps)
|
|
3195
3633
|
// we still need to complete observers, flush deferreds etc
|
|
3634
|
+
if (this._restored) {
|
|
3635
|
+
// Restoration does not replay the transition that originally
|
|
3636
|
+
// terminated the actor, and must not notify the parent again.
|
|
3637
|
+
this._next(this._snapshot);
|
|
3638
|
+
this._stopProcedure();
|
|
3639
|
+
this._complete();
|
|
3640
|
+
return this;
|
|
3641
|
+
}
|
|
3196
3642
|
if (!this._flushInitialEffects()) {
|
|
3197
3643
|
return this;
|
|
3198
3644
|
}
|
|
@@ -3206,33 +3652,31 @@ class Actor {
|
|
|
3206
3652
|
if (!this._parent) {
|
|
3207
3653
|
this.system.start();
|
|
3208
3654
|
}
|
|
3655
|
+
if (!this._flushInitialEffects()) {
|
|
3656
|
+
return this;
|
|
3657
|
+
}
|
|
3209
3658
|
if (this.logic.start) {
|
|
3210
3659
|
try {
|
|
3211
|
-
this.logic.start(this._snapshot, this._actorScope
|
|
3660
|
+
this.logic.start(this._snapshot, this._actorScope, {
|
|
3661
|
+
restored: this._restored
|
|
3662
|
+
});
|
|
3212
3663
|
} catch (err) {
|
|
3213
3664
|
this._setErrorSnapshot(err);
|
|
3214
3665
|
this._error(err);
|
|
3215
3666
|
return this;
|
|
3216
3667
|
}
|
|
3217
3668
|
}
|
|
3218
|
-
if (!this._flushInitialEffects()) {
|
|
3219
|
-
return this;
|
|
3220
|
-
}
|
|
3221
3669
|
|
|
3222
3670
|
// TODO: this notifies all subscribers but usually this is redundant
|
|
3223
3671
|
// there is no real change happening here
|
|
3224
3672
|
// we need to rethink if this needs to be refactored
|
|
3225
3673
|
this.update(this._snapshot, initEvent);
|
|
3226
|
-
if (this.
|
|
3227
|
-
const
|
|
3228
|
-
for (const
|
|
3229
|
-
|
|
3230
|
-
// self-targeted raises exist today; other effect types are ignored.
|
|
3231
|
-
if (effect.type === '@xstate.raise') {
|
|
3232
|
-
this.system.scheduler.schedule(this, this, effect.event, resolveRestoredTimerDelay(strategy, effect), effect.id);
|
|
3233
|
-
}
|
|
3674
|
+
if (this._restored) {
|
|
3675
|
+
const timers = this._snapshot.timers ?? {};
|
|
3676
|
+
for (const timer of Object.values(timers)) {
|
|
3677
|
+
this.system.scheduleTimer(this, timer.id, timer.delay);
|
|
3234
3678
|
}
|
|
3235
|
-
this.
|
|
3679
|
+
this._restored = false;
|
|
3236
3680
|
}
|
|
3237
3681
|
this.mailbox.start();
|
|
3238
3682
|
return this;
|
|
@@ -3241,7 +3685,7 @@ class Actor {
|
|
|
3241
3685
|
let nextState;
|
|
3242
3686
|
let caughtError;
|
|
3243
3687
|
try {
|
|
3244
|
-
nextState = this.logic.transition(this._snapshot, event, this._actorScope);
|
|
3688
|
+
nextState = finalizeTransitionResult(this._actorScope, this._snapshot, this.logic.transition(this._snapshot, event, this._actorScope));
|
|
3245
3689
|
} catch (err) {
|
|
3246
3690
|
// we wrap it in a box so we can rethrow it later even if falsy value gets caught here
|
|
3247
3691
|
caughtError = {
|
|
@@ -3256,10 +3700,9 @@ class Actor {
|
|
|
3256
3700
|
try {
|
|
3257
3701
|
const [nextSnapshot, effects] = nextState;
|
|
3258
3702
|
snapshot = nextSnapshot;
|
|
3259
|
-
this.
|
|
3260
|
-
|
|
3703
|
+
this._setSnapshot(snapshot);
|
|
3704
|
+
executeExecutableEffects(effects, this._actorScope);
|
|
3261
3705
|
this.update(snapshot, event);
|
|
3262
|
-
this.logic.executeEffects?.(logicEffects, this._actorScope);
|
|
3263
3706
|
} catch (err) {
|
|
3264
3707
|
this._recoverOrError(err, snapshot);
|
|
3265
3708
|
return;
|
|
@@ -3270,6 +3713,35 @@ class Actor {
|
|
|
3270
3713
|
}
|
|
3271
3714
|
}
|
|
3272
3715
|
|
|
3716
|
+
/** @internal */
|
|
3717
|
+
_emit(event) {
|
|
3718
|
+
for (const listeners of [this.eventListeners.get(event.type), this.eventListeners.get('*')]) {
|
|
3719
|
+
if (listeners) {
|
|
3720
|
+
for (const handler of listeners) {
|
|
3721
|
+
safeCall(handler, event);
|
|
3722
|
+
}
|
|
3723
|
+
}
|
|
3724
|
+
}
|
|
3725
|
+
}
|
|
3726
|
+
|
|
3727
|
+
/** @internal */
|
|
3728
|
+
_terminate(termination) {
|
|
3729
|
+
if (termination.status === 'done') {
|
|
3730
|
+
// The terminal snapshot is published before observer completion and the
|
|
3731
|
+
// parent notification, matching actor lifecycle order.
|
|
3732
|
+
this._next(this._snapshot);
|
|
3733
|
+
this._stopProcedure();
|
|
3734
|
+
this._complete();
|
|
3735
|
+
if (this._parent) {
|
|
3736
|
+
this.system._relay(this, this._parent, createDoneActorEvent(this.id, termination.output));
|
|
3737
|
+
}
|
|
3738
|
+
return;
|
|
3739
|
+
}
|
|
3740
|
+
if (termination.status === 'error') {
|
|
3741
|
+
this._error(termination.error);
|
|
3742
|
+
}
|
|
3743
|
+
}
|
|
3744
|
+
|
|
3273
3745
|
/** @internal */
|
|
3274
3746
|
_stop() {
|
|
3275
3747
|
if (this._processingStatus === ProcessingStatus.Stopped) {
|
|
@@ -3278,6 +3750,7 @@ class Actor {
|
|
|
3278
3750
|
this.mailbox.clear();
|
|
3279
3751
|
if (this._processingStatus === ProcessingStatus.NotStarted) {
|
|
3280
3752
|
this._processingStatus = ProcessingStatus.Stopped;
|
|
3753
|
+
this.system._unregister(this);
|
|
3281
3754
|
return this;
|
|
3282
3755
|
}
|
|
3283
3756
|
this.mailbox.enqueue({
|
|
@@ -3336,7 +3809,7 @@ class Actor {
|
|
|
3336
3809
|
}
|
|
3337
3810
|
|
|
3338
3811
|
// Cancel all delayed events
|
|
3339
|
-
this.system.
|
|
3812
|
+
this.system.cancelAllTimers(this);
|
|
3340
3813
|
|
|
3341
3814
|
// TODO: mailbox.reset
|
|
3342
3815
|
this.mailbox.clear();
|
|
@@ -3387,35 +3860,7 @@ class Actor {
|
|
|
3387
3860
|
*/
|
|
3388
3861
|
|
|
3389
3862
|
getPersistedSnapshot(options) {
|
|
3390
|
-
|
|
3391
|
-
|
|
3392
|
-
// Capture this actor's pending self-targeted effects (delayed
|
|
3393
|
-
// events/transitions) as serialized action descriptors plus their
|
|
3394
|
-
// runtime progress, so they can be restored on `start()`.
|
|
3395
|
-
const scheduledEvents = this.system._snapshot._scheduledEvents;
|
|
3396
|
-
let pendingEffects;
|
|
3397
|
-
const now = Date.now();
|
|
3398
|
-
for (const key in scheduledEvents) {
|
|
3399
|
-
const scheduled = scheduledEvents[key];
|
|
3400
|
-
if (scheduled.source === this && scheduled.target === this) {
|
|
3401
|
-
(pendingEffects ??= []).push({
|
|
3402
|
-
type: '@xstate.raise',
|
|
3403
|
-
event: scheduled.event,
|
|
3404
|
-
id: scheduled.id,
|
|
3405
|
-
delay: scheduled.delay,
|
|
3406
|
-
startedAt: scheduled.startedAt,
|
|
3407
|
-
elapsed: Math.max(0, now - scheduled.startedAt)
|
|
3408
|
-
});
|
|
3409
|
-
}
|
|
3410
|
-
}
|
|
3411
|
-
|
|
3412
|
-
// If this actor was restored but not yet started, its pending effects
|
|
3413
|
-
// are not in the scheduler yet — carry them through as-is.
|
|
3414
|
-
pendingEffects ??= this._pendingEffects;
|
|
3415
|
-
return pendingEffects ? {
|
|
3416
|
-
...persisted,
|
|
3417
|
-
_pendingEffects: pendingEffects
|
|
3418
|
-
} : persisted;
|
|
3863
|
+
return this.logic.getPersistedSnapshot(this._snapshot, options);
|
|
3419
3864
|
}
|
|
3420
3865
|
[symbolObservable]() {
|
|
3421
3866
|
return this;
|
|
@@ -3439,20 +3884,6 @@ class Actor {
|
|
|
3439
3884
|
return this._snapshot;
|
|
3440
3885
|
}
|
|
3441
3886
|
}
|
|
3442
|
-
function resolveRestoredTimerDelay(strategy, effect) {
|
|
3443
|
-
if (typeof strategy === 'function') {
|
|
3444
|
-
return strategy(effect);
|
|
3445
|
-
}
|
|
3446
|
-
switch (strategy) {
|
|
3447
|
-
case 'restart':
|
|
3448
|
-
return effect.delay;
|
|
3449
|
-
case 'absolute':
|
|
3450
|
-
return Math.max(0, effect.startedAt + effect.delay - Date.now());
|
|
3451
|
-
case 'resume':
|
|
3452
|
-
default:
|
|
3453
|
-
return Math.max(0, effect.delay - effect.elapsed);
|
|
3454
|
-
}
|
|
3455
|
-
}
|
|
3456
3887
|
/**
|
|
3457
3888
|
* Creates a new actor instance for the given actor logic with the provided
|
|
3458
3889
|
* options, if any.
|
|
@@ -3508,63 +3939,27 @@ function getEffectState(self) {
|
|
|
3508
3939
|
}
|
|
3509
3940
|
return state;
|
|
3510
3941
|
}
|
|
3511
|
-
function
|
|
3512
|
-
|
|
3942
|
+
function executeLogicEffect(self, key, effect) {
|
|
3943
|
+
const state = getEffectState(self);
|
|
3944
|
+
if (key !== undefined && state.has(key)) {
|
|
3513
3945
|
return;
|
|
3514
3946
|
}
|
|
3515
|
-
|
|
3516
|
-
|
|
3517
|
-
|
|
3518
|
-
|
|
3519
|
-
|
|
3520
|
-
|
|
3521
|
-
|
|
3522
|
-
|
|
3523
|
-
|
|
3524
|
-
|
|
3525
|
-
|
|
3526
|
-
|
|
3527
|
-
|
|
3528
|
-
|
|
3529
|
-
actorScope.system._relay(actorScope.self, actorScope.self, effect.event);
|
|
3530
|
-
break;
|
|
3531
|
-
case 'effect':
|
|
3532
|
-
{
|
|
3533
|
-
if (!effect.key) {
|
|
3534
|
-
const cleanup = effect.exec();
|
|
3535
|
-
if (typeof cleanup === 'function') {
|
|
3536
|
-
getEffectState(actorScope.self).set(Symbol(), {
|
|
3537
|
-
cleanup
|
|
3538
|
-
});
|
|
3539
|
-
}
|
|
3540
|
-
break;
|
|
3541
|
-
}
|
|
3542
|
-
const state = getEffectState(actorScope.self);
|
|
3543
|
-
if (state.has(effect.key)) {
|
|
3544
|
-
break;
|
|
3545
|
-
}
|
|
3546
|
-
const cleanup = effect.exec();
|
|
3547
|
-
state.set(effect.key, {
|
|
3548
|
-
cleanup: typeof cleanup === 'function' ? cleanup : undefined
|
|
3549
|
-
});
|
|
3550
|
-
break;
|
|
3551
|
-
}
|
|
3552
|
-
case 'cleanupEffects':
|
|
3553
|
-
{
|
|
3554
|
-
const state = effectStates.get(actorScope.self);
|
|
3555
|
-
if (!state) {
|
|
3556
|
-
break;
|
|
3557
|
-
}
|
|
3558
|
-
for (const {
|
|
3559
|
-
cleanup
|
|
3560
|
-
} of state.values()) {
|
|
3561
|
-
cleanup?.();
|
|
3562
|
-
}
|
|
3563
|
-
effectStates.delete(actorScope.self);
|
|
3564
|
-
break;
|
|
3565
|
-
}
|
|
3566
|
-
}
|
|
3947
|
+
const cleanup = effect();
|
|
3948
|
+
state.set(key ?? Symbol(), {
|
|
3949
|
+
cleanup: typeof cleanup === 'function' ? cleanup : undefined
|
|
3950
|
+
});
|
|
3951
|
+
}
|
|
3952
|
+
function cleanupLogicEffects(self) {
|
|
3953
|
+
const state = effectStates.get(self);
|
|
3954
|
+
if (!state) {
|
|
3955
|
+
return;
|
|
3956
|
+
}
|
|
3957
|
+
for (const {
|
|
3958
|
+
cleanup
|
|
3959
|
+
} of state.values()) {
|
|
3960
|
+
cleanup?.();
|
|
3567
3961
|
}
|
|
3962
|
+
effectStates.delete(self);
|
|
3568
3963
|
}
|
|
3569
3964
|
function resolveContext(context, input) {
|
|
3570
3965
|
return typeof context === 'function' ? context({
|
|
@@ -3581,9 +3976,7 @@ function createLogic(config) {
|
|
|
3581
3976
|
...snapshot,
|
|
3582
3977
|
status: 'stopped',
|
|
3583
3978
|
input: undefined
|
|
3584
|
-
}, [
|
|
3585
|
-
type: 'cleanupEffects'
|
|
3586
|
-
}]];
|
|
3979
|
+
}, [createCustomEffect('xstate.logic.cleanup', () => cleanupLogicEffects(actorScope.self))]];
|
|
3587
3980
|
}
|
|
3588
3981
|
if (event.type === XSTATE_LOGIC_EFFECT_START) {
|
|
3589
3982
|
return [{
|
|
@@ -3626,43 +4019,33 @@ function createLogic(config) {
|
|
|
3626
4019
|
if (snapshot.effects?.[key]) {
|
|
3627
4020
|
return;
|
|
3628
4021
|
}
|
|
3629
|
-
effects.push({
|
|
3630
|
-
|
|
3631
|
-
|
|
3632
|
-
exec
|
|
3633
|
-
});
|
|
4022
|
+
effects.push(createCustomEffect('xstate.logic.effect', (runtime = actorScope.self.system) => executeLogicEffect(actorScope.self, key, () => exec(runtime)), {
|
|
4023
|
+
key
|
|
4024
|
+
}));
|
|
3634
4025
|
trackedEffects[key] = {
|
|
3635
4026
|
status: 'active'
|
|
3636
4027
|
};
|
|
3637
4028
|
};
|
|
3638
4029
|
const enq = {
|
|
3639
4030
|
emit: emitted => {
|
|
3640
|
-
effects.push(
|
|
3641
|
-
type: 'emit',
|
|
3642
|
-
event: emitted
|
|
3643
|
-
});
|
|
4031
|
+
effects.push(createEmitEffect(actorScope, emitted));
|
|
3644
4032
|
},
|
|
3645
4033
|
sendBack: sentEvent => {
|
|
3646
|
-
|
|
3647
|
-
|
|
3648
|
-
|
|
3649
|
-
}
|
|
4034
|
+
const parent = actorScope.self._parent;
|
|
4035
|
+
if (parent) {
|
|
4036
|
+
effects.push(createSendToEffect(actorScope, parent, sentEvent));
|
|
4037
|
+
}
|
|
3650
4038
|
},
|
|
3651
4039
|
raise: raisedEvent => {
|
|
3652
|
-
effects.push(
|
|
3653
|
-
type: 'raise',
|
|
3654
|
-
event: raisedEvent
|
|
3655
|
-
});
|
|
4040
|
+
effects.push(createSendToEffect(actorScope, actorScope.self, raisedEvent));
|
|
3656
4041
|
},
|
|
3657
4042
|
effect: (keyOrExec, maybeExec) => {
|
|
3658
4043
|
if (typeof keyOrExec === 'string') {
|
|
3659
4044
|
enqueueEffect(keyOrExec, maybeExec);
|
|
3660
4045
|
return;
|
|
3661
4046
|
}
|
|
3662
|
-
|
|
3663
|
-
|
|
3664
|
-
exec: keyOrExec
|
|
3665
|
-
});
|
|
4047
|
+
const exec = keyOrExec;
|
|
4048
|
+
effects.push(createCustomEffect('xstate.logic.effect', (runtime = actorScope.self.system) => executeLogicEffect(actorScope.self, undefined, () => exec(runtime))));
|
|
3666
4049
|
}
|
|
3667
4050
|
};
|
|
3668
4051
|
const patch = config.run({
|
|
@@ -3687,18 +4070,23 @@ function createLogic(config) {
|
|
|
3687
4070
|
...trackedEffects
|
|
3688
4071
|
};
|
|
3689
4072
|
}
|
|
3690
|
-
return [nextSnapshot, effects];
|
|
4073
|
+
return finalizeTransitionResult(actorScope, snapshot, [nextSnapshot, effects]);
|
|
3691
4074
|
};
|
|
3692
4075
|
const logic = {
|
|
3693
4076
|
id: config.id,
|
|
3694
4077
|
config,
|
|
3695
4078
|
transition,
|
|
3696
|
-
start: (snapshot, actorScope) => {
|
|
4079
|
+
start: (snapshot, actorScope, options) => {
|
|
4080
|
+
if (!options?.restored) {
|
|
4081
|
+
return;
|
|
4082
|
+
}
|
|
3697
4083
|
const [nextSnapshot, effects] = transition(snapshot, createInitEvent(snapshot.input), actorScope);
|
|
3698
4084
|
Object.assign(snapshot, nextSnapshot);
|
|
3699
|
-
|
|
4085
|
+
for (const effect of effects) {
|
|
4086
|
+
actorScope.actionExecutor(effect);
|
|
4087
|
+
}
|
|
3700
4088
|
},
|
|
3701
|
-
initialTransition: (input,
|
|
4089
|
+
initialTransition: (input, actorScope) => {
|
|
3702
4090
|
const context = resolveContext(config.context, input);
|
|
3703
4091
|
const snapshot = {
|
|
3704
4092
|
status: 'active',
|
|
@@ -3709,14 +4097,13 @@ function createLogic(config) {
|
|
|
3709
4097
|
if (context !== undefined) {
|
|
3710
4098
|
snapshot.context = context;
|
|
3711
4099
|
}
|
|
3712
|
-
return
|
|
4100
|
+
return transition({
|
|
3713
4101
|
...snapshot
|
|
3714
|
-
},
|
|
4102
|
+
}, createInitEvent(input), actorScope);
|
|
3715
4103
|
},
|
|
3716
4104
|
getInitialSnapshot: (actorScope, input) => logic.initialTransition(input, actorScope)[0],
|
|
3717
4105
|
getPersistedSnapshot: snapshot => snapshot,
|
|
3718
|
-
restoreSnapshot: snapshot => snapshot
|
|
3719
|
-
executeEffects: executeLogicEffects
|
|
4106
|
+
restoreSnapshot: snapshot => snapshot
|
|
3720
4107
|
};
|
|
3721
4108
|
return logic;
|
|
3722
4109
|
}
|
|
@@ -3976,7 +4363,6 @@ function createObservableLogic(observableCreatorOrConfig) {
|
|
|
3976
4363
|
self,
|
|
3977
4364
|
system
|
|
3978
4365
|
} = args;
|
|
3979
|
-
const emit = args.emit;
|
|
3980
4366
|
switch (event.type) {
|
|
3981
4367
|
case XSTATE_OBSERVABLE_NEXT:
|
|
3982
4368
|
{
|
|
@@ -4007,28 +4393,32 @@ function createObservableLogic(observableCreatorOrConfig) {
|
|
|
4007
4393
|
}
|
|
4008
4394
|
};
|
|
4009
4395
|
}
|
|
4010
|
-
|
|
4396
|
+
if (event.type !== XSTATE_INIT) {
|
|
4397
|
+
return;
|
|
4398
|
+
}
|
|
4399
|
+
enq.effect((runtime = system) => {
|
|
4011
4400
|
const actorSelf = self;
|
|
4401
|
+
const sendSelf = event => void runtime.sendEvent(actorSelf, actorSelf, event);
|
|
4012
4402
|
const subscription = observableCreator({
|
|
4013
4403
|
input,
|
|
4014
4404
|
system,
|
|
4015
4405
|
self: self,
|
|
4016
|
-
emit:
|
|
4406
|
+
emit: event => void runtime.emitEvent(actorSelf, event)
|
|
4017
4407
|
}).subscribe({
|
|
4018
4408
|
next: value => {
|
|
4019
|
-
|
|
4409
|
+
sendSelf({
|
|
4020
4410
|
type: XSTATE_OBSERVABLE_NEXT,
|
|
4021
4411
|
data: value
|
|
4022
4412
|
});
|
|
4023
4413
|
},
|
|
4024
4414
|
error: err => {
|
|
4025
|
-
|
|
4415
|
+
sendSelf({
|
|
4026
4416
|
type: XSTATE_OBSERVABLE_ERROR,
|
|
4027
4417
|
data: err
|
|
4028
4418
|
});
|
|
4029
4419
|
},
|
|
4030
4420
|
complete: () => {
|
|
4031
|
-
|
|
4421
|
+
sendSelf({
|
|
4032
4422
|
type: XSTATE_OBSERVABLE_COMPLETE
|
|
4033
4423
|
});
|
|
4034
4424
|
}
|
|
@@ -4097,26 +4487,26 @@ function createEventObservableLogic(lazyObservableOrConfig) {
|
|
|
4097
4487
|
// TODO: event types
|
|
4098
4488
|
const logic = {
|
|
4099
4489
|
config: lazyObservable,
|
|
4100
|
-
transition: (state, event) => {
|
|
4490
|
+
transition: (state, event, actorScope) => {
|
|
4101
4491
|
if (state.status !== 'active') {
|
|
4102
4492
|
return [state, []];
|
|
4103
4493
|
}
|
|
4104
4494
|
switch (event.type) {
|
|
4105
4495
|
case XSTATE_OBSERVABLE_ERROR:
|
|
4106
|
-
return [{
|
|
4496
|
+
return finalizeTransitionResult(actorScope, state, [{
|
|
4107
4497
|
...state,
|
|
4108
4498
|
status: 'error',
|
|
4109
4499
|
error: event.data,
|
|
4110
4500
|
input: undefined,
|
|
4111
4501
|
_subscription: undefined
|
|
4112
|
-
}, []];
|
|
4502
|
+
}, []]);
|
|
4113
4503
|
case XSTATE_OBSERVABLE_COMPLETE:
|
|
4114
|
-
return [{
|
|
4504
|
+
return finalizeTransitionResult(actorScope, state, [{
|
|
4115
4505
|
...state,
|
|
4116
4506
|
status: 'done',
|
|
4117
4507
|
input: undefined,
|
|
4118
4508
|
_subscription: undefined
|
|
4119
|
-
}, []];
|
|
4509
|
+
}, []]);
|
|
4120
4510
|
case XSTATE_STOP:
|
|
4121
4511
|
state._subscription.unsubscribe();
|
|
4122
4512
|
return [{
|
|
@@ -4151,7 +4541,9 @@ function createEventObservableLogic(lazyObservableOrConfig) {
|
|
|
4151
4541
|
input: state.input,
|
|
4152
4542
|
system,
|
|
4153
4543
|
self,
|
|
4154
|
-
emit
|
|
4544
|
+
emit: event => {
|
|
4545
|
+
void emit(event);
|
|
4546
|
+
}
|
|
4155
4547
|
}).subscribe({
|
|
4156
4548
|
next: value => {
|
|
4157
4549
|
if (self._parent) {
|
|
@@ -4352,8 +4744,9 @@ function createAsyncLogic(asyncLogic) {
|
|
|
4352
4744
|
if (event.type !== XSTATE_INIT) {
|
|
4353
4745
|
return;
|
|
4354
4746
|
}
|
|
4355
|
-
enq.effect(() => {
|
|
4747
|
+
enq.effect((runtime = system) => {
|
|
4356
4748
|
const actorSelf = self;
|
|
4749
|
+
const sendSelf = event => void runtime.sendEvent(actorSelf, actorSelf, event);
|
|
4357
4750
|
const controller = new AbortController();
|
|
4358
4751
|
const timeout = config.timeout;
|
|
4359
4752
|
const timeoutMs = parseDelayToMilliseconds(timeout);
|
|
@@ -4362,7 +4755,7 @@ function createAsyncLogic(asyncLogic) {
|
|
|
4362
4755
|
return;
|
|
4363
4756
|
}
|
|
4364
4757
|
controller.abort();
|
|
4365
|
-
|
|
4758
|
+
sendSelf({
|
|
4366
4759
|
type: XSTATE_ASYNC_REJECT,
|
|
4367
4760
|
data: new TimeoutError(timeout)
|
|
4368
4761
|
});
|
|
@@ -4378,7 +4771,7 @@ function createAsyncLogic(asyncLogic) {
|
|
|
4378
4771
|
self: self,
|
|
4379
4772
|
signal: controller.signal
|
|
4380
4773
|
}, {
|
|
4381
|
-
emit:
|
|
4774
|
+
emit: event => void runtime.emitEvent(actorSelf, event),
|
|
4382
4775
|
step: async (key, exec) => {
|
|
4383
4776
|
const effect = self.getSnapshot().effects?.[key];
|
|
4384
4777
|
if (effect?.status === 'done') {
|
|
@@ -4390,20 +4783,20 @@ function createAsyncLogic(asyncLogic) {
|
|
|
4390
4783
|
if (effect?.status === 'active') {
|
|
4391
4784
|
return waitForEffect(self, key);
|
|
4392
4785
|
}
|
|
4393
|
-
|
|
4786
|
+
sendSelf({
|
|
4394
4787
|
type: XSTATE_LOGIC_EFFECT_START,
|
|
4395
4788
|
key
|
|
4396
4789
|
});
|
|
4397
4790
|
try {
|
|
4398
4791
|
const output = await exec();
|
|
4399
|
-
|
|
4792
|
+
sendSelf({
|
|
4400
4793
|
type: XSTATE_LOGIC_EFFECT_RESOLVE,
|
|
4401
4794
|
key,
|
|
4402
4795
|
output
|
|
4403
4796
|
});
|
|
4404
4797
|
return output;
|
|
4405
4798
|
} catch (error) {
|
|
4406
|
-
|
|
4799
|
+
sendSelf({
|
|
4407
4800
|
type: XSTATE_LOGIC_EFFECT_REJECT,
|
|
4408
4801
|
key,
|
|
4409
4802
|
error
|
|
@@ -4417,7 +4810,7 @@ function createAsyncLogic(asyncLogic) {
|
|
|
4417
4810
|
if (self.getSnapshot().status !== 'active') {
|
|
4418
4811
|
return;
|
|
4419
4812
|
}
|
|
4420
|
-
|
|
4813
|
+
sendSelf({
|
|
4421
4814
|
type: XSTATE_ASYNC_RESOLVE,
|
|
4422
4815
|
data: response
|
|
4423
4816
|
});
|
|
@@ -4426,7 +4819,7 @@ function createAsyncLogic(asyncLogic) {
|
|
|
4426
4819
|
if (self.getSnapshot().status !== 'active') {
|
|
4427
4820
|
return;
|
|
4428
4821
|
}
|
|
4429
|
-
|
|
4822
|
+
sendSelf({
|
|
4430
4823
|
type: XSTATE_ASYNC_REJECT,
|
|
4431
4824
|
data: errorData
|
|
4432
4825
|
});
|
|
@@ -4458,11 +4851,11 @@ function createEmptyActor() {
|
|
|
4458
4851
|
exports.$$ACTOR_TYPE = $$ACTOR_TYPE;
|
|
4459
4852
|
exports.Actor = Actor;
|
|
4460
4853
|
exports.NULL_EVENT = NULL_EVENT;
|
|
4461
|
-
exports.ProcessingStatus = ProcessingStatus;
|
|
4462
4854
|
exports.STATE_DELIMITER = STATE_DELIMITER;
|
|
4463
4855
|
exports.TimeoutError = TimeoutError;
|
|
4464
4856
|
exports.XSTATE_INIT = XSTATE_INIT;
|
|
4465
4857
|
exports.XSTATE_STOP = XSTATE_STOP;
|
|
4858
|
+
exports.XSTATE_TIMER = XSTATE_TIMER;
|
|
4466
4859
|
exports.builtInActions = builtInActions;
|
|
4467
4860
|
exports.checkStateIn = checkStateIn;
|
|
4468
4861
|
exports.cloneMachineSnapshot = cloneMachineSnapshot;
|
|
@@ -4479,10 +4872,15 @@ exports.createListenerLogic = createListenerLogic;
|
|
|
4479
4872
|
exports.createLogic = createLogic;
|
|
4480
4873
|
exports.createMachineSnapshot = createMachineSnapshot;
|
|
4481
4874
|
exports.createObservableLogic = createObservableLogic;
|
|
4875
|
+
exports.createSendToEffect = createSendToEffect;
|
|
4876
|
+
exports.createSnapshotSystem = createSnapshotSystem;
|
|
4877
|
+
exports.createSpawnEffect = createSpawnEffect;
|
|
4482
4878
|
exports.createSubscriptionLogic = createSubscriptionLogic;
|
|
4483
4879
|
exports.createTransitionEnqueue = createTransitionEnqueue;
|
|
4484
4880
|
exports.deriveDeferredStarts = deriveDeferredStarts;
|
|
4485
4881
|
exports.evaluateCandidate = evaluateCandidate;
|
|
4882
|
+
exports.executeEffects = executeEffects;
|
|
4883
|
+
exports.finalizeTransitionResult = finalizeTransitionResult;
|
|
4486
4884
|
exports.formatRouteTransitions = formatRouteTransitions;
|
|
4487
4885
|
exports.formatTransition = formatTransition;
|
|
4488
4886
|
exports.getAllOwnEventDescriptors = getAllOwnEventDescriptors;
|
|
@@ -4491,6 +4889,7 @@ exports.getCandidates = getCandidates;
|
|
|
4491
4889
|
exports.getDelayedTransitions = getDelayedTransitions;
|
|
4492
4890
|
exports.getPersistedSnapshot = getPersistedSnapshot;
|
|
4493
4891
|
exports.getProperAncestors = getProperAncestors;
|
|
4892
|
+
exports.getSnapshotActorRef = getSnapshotActorRef;
|
|
4494
4893
|
exports.getStateNodeByPath = getStateNodeByPath;
|
|
4495
4894
|
exports.getStateNodes = getStateNodes;
|
|
4496
4895
|
exports.getTransitionResult = getTransitionResult;
|
|
@@ -4511,6 +4910,7 @@ exports.pathToStateValue = pathToStateValue;
|
|
|
4511
4910
|
exports.resolveActionsWithContext = resolveActionsWithContext;
|
|
4512
4911
|
exports.resolveReferencedActor = resolveReferencedActor;
|
|
4513
4912
|
exports.resolveStateValue = resolveStateValue;
|
|
4913
|
+
exports.setSnapshotActorRef = setSnapshotActorRef;
|
|
4514
4914
|
exports.subscriptionLogic = subscriptionLogic;
|
|
4515
4915
|
exports.toArray = toArray;
|
|
4516
4916
|
exports.toObserver = toObserver;
|