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