xstate 5.0.0-beta.15 → 5.0.0-beta.16
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/actions/dist/xstate-actions.cjs.js +1 -3
- package/actions/dist/xstate-actions.cjs.mjs +0 -2
- package/actions/dist/xstate-actions.development.cjs.js +1 -3
- package/actions/dist/xstate-actions.development.esm.js +1 -1
- package/actions/dist/xstate-actions.esm.js +1 -1
- package/actions/dist/xstate-actions.umd.min.js +1 -1
- package/actions/dist/xstate-actions.umd.min.js.map +1 -1
- package/actors/dist/xstate-actors.cjs.js +1 -1
- package/actors/dist/xstate-actors.development.cjs.js +1 -1
- package/actors/dist/xstate-actors.development.esm.js +1 -1
- package/actors/dist/xstate-actors.esm.js +1 -1
- package/actors/dist/xstate-actors.umd.min.js +1 -1
- package/actors/dist/xstate-actors.umd.min.js.map +1 -1
- package/dist/{actions-6884fae8.esm.js → actions-0386b622.esm.js} +707 -742
- package/dist/{actions-d71ac253.development.cjs.js → actions-0f903c0d.development.cjs.js} +836 -874
- package/dist/{actions-81cc7f2b.cjs.js → actions-6b9073db.cjs.js} +706 -744
- package/dist/{actions-98f362b9.development.esm.js → actions-6f7fbc84.development.esm.js} +837 -872
- package/dist/declarations/src/StateMachine.d.ts +2 -4
- package/dist/declarations/src/actionTypes.d.ts +1 -1
- package/dist/declarations/src/actions/assign.d.ts +2 -2
- package/dist/declarations/src/actions/cancel.d.ts +2 -2
- package/dist/declarations/src/actions/send.d.ts +12 -22
- package/dist/declarations/src/actions/stop.d.ts +2 -2
- package/dist/declarations/src/actions.d.ts +1 -4
- package/dist/declarations/src/interpreter.d.ts +2 -2
- package/dist/declarations/src/stateUtils.d.ts +1 -1
- package/dist/declarations/src/types.d.ts +29 -49
- package/dist/xstate.cjs.js +20 -34
- package/dist/xstate.development.cjs.js +20 -34
- package/dist/xstate.development.esm.js +21 -35
- package/dist/xstate.esm.js +21 -35
- package/dist/xstate.umd.min.js +1 -1
- package/dist/xstate.umd.min.js.map +1 -1
- package/guards/dist/xstate-guards.cjs.js +1 -1
- package/guards/dist/xstate-guards.development.cjs.js +1 -1
- package/guards/dist/xstate-guards.development.esm.js +1 -1
- package/guards/dist/xstate-guards.esm.js +1 -1
- package/guards/dist/xstate-guards.umd.min.js.map +1 -1
- package/package.json +1 -1
|
@@ -35,7 +35,7 @@ import { devToolsAdapter } from '../dev/dist/xstate-dev.development.esm.js';
|
|
|
35
35
|
let ActionTypes = /*#__PURE__*/function (ActionTypes) {
|
|
36
36
|
ActionTypes["Stop"] = "xstate.stop";
|
|
37
37
|
ActionTypes["Raise"] = "xstate.raise";
|
|
38
|
-
ActionTypes["
|
|
38
|
+
ActionTypes["SendTo"] = "xstate.sendTo";
|
|
39
39
|
ActionTypes["Cancel"] = "xstate.cancel";
|
|
40
40
|
ActionTypes["Assign"] = "xstate.assign";
|
|
41
41
|
ActionTypes["After"] = "xstate.after";
|
|
@@ -61,7 +61,7 @@ let SpecialTargets = /*#__PURE__*/function (SpecialTargets) {
|
|
|
61
61
|
// xstate-specific action types
|
|
62
62
|
const stop$1 = ActionTypes.Stop;
|
|
63
63
|
const raise$1 = ActionTypes.Raise;
|
|
64
|
-
const
|
|
64
|
+
const sendTo$1 = ActionTypes.SendTo;
|
|
65
65
|
const cancel$1 = ActionTypes.Cancel;
|
|
66
66
|
const assign$1 = ActionTypes.Assign;
|
|
67
67
|
const after$1 = ActionTypes.After;
|
|
@@ -79,7 +79,7 @@ var actionTypes = /*#__PURE__*/Object.freeze({
|
|
|
79
79
|
__proto__: null,
|
|
80
80
|
stop: stop$1,
|
|
81
81
|
raise: raise$1,
|
|
82
|
-
|
|
82
|
+
sendTo: sendTo$1,
|
|
83
83
|
cancel: cancel$1,
|
|
84
84
|
assign: assign$1,
|
|
85
85
|
after: after$1,
|
|
@@ -304,22 +304,19 @@ function isDynamicAction(action) {
|
|
|
304
304
|
}
|
|
305
305
|
|
|
306
306
|
/**
|
|
307
|
-
* Sends an event
|
|
308
|
-
* send the event in the next step, after the current step is finished executing.
|
|
309
|
-
*
|
|
310
|
-
* @deprecated Use the `sendTo(...)` action creator instead.
|
|
307
|
+
* Sends an event to an actor.
|
|
311
308
|
*
|
|
312
|
-
* @param
|
|
313
|
-
* @param
|
|
309
|
+
* @param actor The `ActorRef` to send the event to.
|
|
310
|
+
* @param event The event to send, or an expression that evaluates to the event to send
|
|
311
|
+
* @param options Send action options
|
|
314
312
|
* - `id` - The unique send event identifier (used with `cancel()`).
|
|
315
313
|
* - `delay` - The number of milliseconds to delay the sending of the event.
|
|
316
|
-
* - `to` - The target of this event (by default, the machine the event was sent from).
|
|
317
314
|
*/
|
|
318
|
-
function
|
|
315
|
+
function sendTo(actor, eventOrExpr, options) {
|
|
319
316
|
return createDynamicAction({
|
|
320
|
-
type:
|
|
317
|
+
type: sendTo$1,
|
|
321
318
|
params: {
|
|
322
|
-
to:
|
|
319
|
+
to: actor,
|
|
323
320
|
delay: options ? options.delay : undefined,
|
|
324
321
|
event: eventOrExpr,
|
|
325
322
|
id: options && options.id !== undefined ? options.id : isFunction(eventOrExpr) ? eventOrExpr.name : eventOrExpr.type
|
|
@@ -329,7 +326,7 @@ function send(eventOrExpr, options) {
|
|
|
329
326
|
state
|
|
330
327
|
}) => {
|
|
331
328
|
const params = {
|
|
332
|
-
to:
|
|
329
|
+
to: actor,
|
|
333
330
|
delay: options ? options.delay : undefined,
|
|
334
331
|
event: eventOrExpr,
|
|
335
332
|
// TODO: don't auto-generate IDs here like that
|
|
@@ -377,13 +374,12 @@ function send(eventOrExpr, options) {
|
|
|
377
374
|
targetActorRef = resolvedTarget || actorContext?.self;
|
|
378
375
|
}
|
|
379
376
|
const resolvedAction = {
|
|
380
|
-
type:
|
|
377
|
+
type: sendTo$1,
|
|
381
378
|
params: {
|
|
382
379
|
...params,
|
|
383
380
|
to: targetActorRef,
|
|
384
381
|
event: resolvedEvent,
|
|
385
|
-
delay: resolvedDelay
|
|
386
|
-
internal: resolvedTarget === SpecialTargets.Internal
|
|
382
|
+
delay: resolvedDelay
|
|
387
383
|
},
|
|
388
384
|
execute: actorCtx => {
|
|
389
385
|
const sendAction = resolvedAction;
|
|
@@ -413,12 +409,8 @@ function send(eventOrExpr, options) {
|
|
|
413
409
|
* @param options Options to pass into the send event.
|
|
414
410
|
*/
|
|
415
411
|
function sendParent(event, options) {
|
|
416
|
-
return
|
|
417
|
-
...options,
|
|
418
|
-
to: SpecialTargets.Parent
|
|
419
|
-
});
|
|
412
|
+
return sendTo(SpecialTargets.Parent, event, options);
|
|
420
413
|
}
|
|
421
|
-
|
|
422
414
|
/**
|
|
423
415
|
* Forwards (sends) an event to a specified service.
|
|
424
416
|
*
|
|
@@ -436,12 +428,9 @@ function forwardTo(target, options) {
|
|
|
436
428
|
return resolvedTarget;
|
|
437
429
|
};
|
|
438
430
|
}
|
|
439
|
-
return
|
|
431
|
+
return sendTo(target, ({
|
|
440
432
|
event
|
|
441
|
-
}) => event,
|
|
442
|
-
...options,
|
|
443
|
-
to: target
|
|
444
|
-
});
|
|
433
|
+
}) => event, options);
|
|
445
434
|
}
|
|
446
435
|
|
|
447
436
|
/**
|
|
@@ -457,25 +446,7 @@ function escalate(errorData, options) {
|
|
|
457
446
|
type: error$1,
|
|
458
447
|
data: isFunction(errorData) ? errorData(arg) : errorData
|
|
459
448
|
};
|
|
460
|
-
},
|
|
461
|
-
...options,
|
|
462
|
-
to: SpecialTargets.Parent
|
|
463
|
-
});
|
|
464
|
-
}
|
|
465
|
-
|
|
466
|
-
/**
|
|
467
|
-
* Sends an event to an actor.
|
|
468
|
-
*
|
|
469
|
-
* @param actor The `ActorRef` to send the event to.
|
|
470
|
-
* @param event The event to send, or an expression that evaluates to the event to send
|
|
471
|
-
* @param options Send action options
|
|
472
|
-
* @returns An XState send action object
|
|
473
|
-
*/
|
|
474
|
-
function sendTo(actor, event, options) {
|
|
475
|
-
return send(event, {
|
|
476
|
-
...options,
|
|
477
|
-
to: actor
|
|
478
|
-
});
|
|
449
|
+
}, options);
|
|
479
450
|
}
|
|
480
451
|
|
|
481
452
|
const cache = new WeakMap();
|
|
@@ -529,8 +500,6 @@ function cancel(sendId) {
|
|
|
529
500
|
});
|
|
530
501
|
}
|
|
531
502
|
|
|
532
|
-
const symbolObservable = (() => typeof Symbol === 'function' && Symbol.observable || '@@observable')();
|
|
533
|
-
|
|
534
503
|
class Mailbox {
|
|
535
504
|
constructor(_process) {
|
|
536
505
|
this._process = _process;
|
|
@@ -598,698 +567,317 @@ class Mailbox {
|
|
|
598
567
|
}
|
|
599
568
|
}
|
|
600
569
|
|
|
601
|
-
function
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
reverseKeyedActors.delete(actorRef);
|
|
618
|
-
}
|
|
570
|
+
const symbolObservable = (() => typeof Symbol === 'function' && Symbol.observable || '@@observable')();
|
|
571
|
+
|
|
572
|
+
/**
|
|
573
|
+
* Returns actor logic from a transition function and its initial state.
|
|
574
|
+
*
|
|
575
|
+
* A transition function is a function that takes the current state and an event and returns the next state.
|
|
576
|
+
*
|
|
577
|
+
* @param transition The transition function that returns the next state given the current state and event.
|
|
578
|
+
* @param initialState The initial state of the transition function.
|
|
579
|
+
* @returns Actor logic
|
|
580
|
+
*/
|
|
581
|
+
function fromTransition(transition, initialState) {
|
|
582
|
+
const logic = {
|
|
583
|
+
config: transition,
|
|
584
|
+
transition: (state, event, actorContext) => {
|
|
585
|
+
return transition(state, event, actorContext);
|
|
619
586
|
},
|
|
620
|
-
|
|
621
|
-
return
|
|
587
|
+
getInitialState: (_, input) => {
|
|
588
|
+
return typeof initialState === 'function' ? initialState({
|
|
589
|
+
input
|
|
590
|
+
}) : initialState;
|
|
622
591
|
},
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
throw new Error(`Actor with system ID '${systemId}' already exists.`);
|
|
627
|
-
}
|
|
628
|
-
keyedActors.set(systemId, actorRef);
|
|
629
|
-
reverseKeyedActors.set(actorRef, systemId);
|
|
630
|
-
}
|
|
592
|
+
getSnapshot: state => state,
|
|
593
|
+
getPersistedState: state => state,
|
|
594
|
+
restoreState: state => state
|
|
631
595
|
};
|
|
632
|
-
return
|
|
596
|
+
return logic;
|
|
633
597
|
}
|
|
634
598
|
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
const
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
599
|
+
const resolveEventType = '$$xstate.resolve';
|
|
600
|
+
const rejectEventType = '$$xstate.reject';
|
|
601
|
+
function fromPromise(
|
|
602
|
+
// TODO: add types
|
|
603
|
+
promiseCreator) {
|
|
604
|
+
// TODO: add event types, consider making the `PromiseEvent` a private type or smth alike
|
|
605
|
+
const logic = {
|
|
606
|
+
config: promiseCreator,
|
|
607
|
+
transition: (state, event) => {
|
|
608
|
+
if (state.status !== 'active') {
|
|
609
|
+
return state;
|
|
610
|
+
}
|
|
611
|
+
switch (event.type) {
|
|
612
|
+
case resolveEventType:
|
|
613
|
+
return {
|
|
614
|
+
...state,
|
|
615
|
+
status: 'done',
|
|
616
|
+
data: event.data,
|
|
617
|
+
input: undefined
|
|
618
|
+
};
|
|
619
|
+
case rejectEventType:
|
|
620
|
+
return {
|
|
621
|
+
...state,
|
|
622
|
+
status: 'error',
|
|
623
|
+
data: event.data,
|
|
624
|
+
input: undefined
|
|
625
|
+
};
|
|
626
|
+
case stopSignalType:
|
|
627
|
+
return {
|
|
628
|
+
...state,
|
|
629
|
+
status: 'canceled',
|
|
630
|
+
input: undefined
|
|
631
|
+
};
|
|
632
|
+
default:
|
|
633
|
+
return state;
|
|
634
|
+
}
|
|
646
635
|
},
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
636
|
+
start: (state, {
|
|
637
|
+
self,
|
|
638
|
+
system
|
|
639
|
+
}) => {
|
|
640
|
+
// TODO: determine how to allow customizing this so that promises
|
|
641
|
+
// can be restarted if necessary
|
|
642
|
+
if (state.status !== 'active') {
|
|
643
|
+
return;
|
|
644
|
+
}
|
|
645
|
+
const resolvedPromise = Promise.resolve(promiseCreator({
|
|
646
|
+
input: state.input,
|
|
647
|
+
system
|
|
648
|
+
}));
|
|
649
|
+
resolvedPromise.then(response => {
|
|
650
|
+
// TODO: remove this condition once dead letter queue lands
|
|
651
|
+
if (self._state.status !== 'active') {
|
|
652
|
+
return;
|
|
653
|
+
}
|
|
654
|
+
self.send({
|
|
655
|
+
type: resolveEventType,
|
|
656
|
+
data: response
|
|
657
|
+
});
|
|
658
|
+
}, errorData => {
|
|
659
|
+
// TODO: remove this condition once dead letter queue lands
|
|
660
|
+
if (self._state.status !== 'active') {
|
|
661
|
+
return;
|
|
662
|
+
}
|
|
663
|
+
self.send({
|
|
664
|
+
type: rejectEventType,
|
|
665
|
+
data: errorData
|
|
666
|
+
});
|
|
667
|
+
});
|
|
668
|
+
},
|
|
669
|
+
getInitialState: (_, input) => {
|
|
670
|
+
return {
|
|
671
|
+
status: 'active',
|
|
672
|
+
data: undefined,
|
|
673
|
+
input
|
|
674
|
+
};
|
|
675
|
+
},
|
|
676
|
+
getSnapshot: state => state.data,
|
|
677
|
+
getStatus: state => state,
|
|
678
|
+
getPersistedState: state => state,
|
|
679
|
+
restoreState: state => state
|
|
680
|
+
};
|
|
681
|
+
return logic;
|
|
682
|
+
}
|
|
674
683
|
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
684
|
+
// TODO: this likely shouldn't accept TEvent, observable actor doesn't accept external events
|
|
685
|
+
function fromObservable(observableCreator) {
|
|
686
|
+
const nextEventType = '$$xstate.next';
|
|
687
|
+
const errorEventType = '$$xstate.error';
|
|
688
|
+
const completeEventType = '$$xstate.complete';
|
|
678
689
|
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
*/
|
|
685
|
-
constructor(logic, options) {
|
|
686
|
-
this.logic = logic;
|
|
687
|
-
this._state = void 0;
|
|
688
|
-
this.clock = void 0;
|
|
689
|
-
this.options = void 0;
|
|
690
|
-
this.id = void 0;
|
|
691
|
-
this.mailbox = new Mailbox(this._process.bind(this));
|
|
692
|
-
this.delayedEventsMap = {};
|
|
693
|
-
this.observers = new Set();
|
|
694
|
-
this.logger = void 0;
|
|
695
|
-
this.status = ActorStatus.NotStarted;
|
|
696
|
-
this._parent = void 0;
|
|
697
|
-
this.ref = void 0;
|
|
698
|
-
this._actorContext = void 0;
|
|
699
|
-
this._systemId = void 0;
|
|
700
|
-
this.sessionId = void 0;
|
|
701
|
-
this.system = void 0;
|
|
702
|
-
this._doneEvent = void 0;
|
|
703
|
-
this.src = void 0;
|
|
704
|
-
this._deferred = [];
|
|
705
|
-
const resolvedOptions = {
|
|
706
|
-
...defaultOptions,
|
|
707
|
-
...options
|
|
708
|
-
};
|
|
709
|
-
const {
|
|
710
|
-
clock,
|
|
711
|
-
logger,
|
|
712
|
-
parent,
|
|
690
|
+
// TODO: add event types
|
|
691
|
+
const logic = {
|
|
692
|
+
config: observableCreator,
|
|
693
|
+
transition: (state, event, {
|
|
694
|
+
self,
|
|
713
695
|
id,
|
|
714
|
-
|
|
715
|
-
}
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
696
|
+
defer
|
|
697
|
+
}) => {
|
|
698
|
+
if (state.status !== 'active') {
|
|
699
|
+
return state;
|
|
700
|
+
}
|
|
701
|
+
switch (event.type) {
|
|
702
|
+
case nextEventType:
|
|
703
|
+
// match the exact timing of events sent by machines
|
|
704
|
+
// send actions are not executed immediately
|
|
705
|
+
defer(() => {
|
|
706
|
+
self._parent?.send({
|
|
707
|
+
type: `xstate.snapshot.${id}`,
|
|
708
|
+
data: event.data
|
|
709
|
+
});
|
|
710
|
+
});
|
|
711
|
+
return {
|
|
712
|
+
...state,
|
|
713
|
+
data: event.data
|
|
714
|
+
};
|
|
715
|
+
case errorEventType:
|
|
716
|
+
return {
|
|
717
|
+
...state,
|
|
718
|
+
status: 'error',
|
|
719
|
+
input: undefined,
|
|
720
|
+
data: event.data,
|
|
721
|
+
subscription: undefined
|
|
722
|
+
};
|
|
723
|
+
case completeEventType:
|
|
724
|
+
return {
|
|
725
|
+
...state,
|
|
726
|
+
status: 'done',
|
|
727
|
+
input: undefined,
|
|
728
|
+
subscription: undefined
|
|
729
|
+
};
|
|
730
|
+
case stopSignalType:
|
|
731
|
+
state.subscription.unsubscribe();
|
|
732
|
+
return {
|
|
733
|
+
...state,
|
|
734
|
+
status: 'canceled',
|
|
735
|
+
input: undefined,
|
|
736
|
+
subscription: undefined
|
|
737
|
+
};
|
|
738
|
+
default:
|
|
739
|
+
return state;
|
|
740
|
+
}
|
|
741
|
+
},
|
|
742
|
+
getInitialState: (_, input) => {
|
|
743
|
+
return {
|
|
744
|
+
subscription: undefined,
|
|
745
|
+
status: 'active',
|
|
746
|
+
data: undefined,
|
|
747
|
+
input
|
|
748
|
+
};
|
|
749
|
+
},
|
|
750
|
+
start: (state, {
|
|
731
751
|
self,
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
},
|
|
738
|
-
system: this.system,
|
|
739
|
-
stopChild: child => {
|
|
740
|
-
if (child._parent !== this) {
|
|
741
|
-
throw new Error(`Cannot stop child actor ${child.id} of ${this.id} because it is not a child`);
|
|
742
|
-
}
|
|
743
|
-
child._stop();
|
|
752
|
+
system
|
|
753
|
+
}) => {
|
|
754
|
+
if (state.status === 'done') {
|
|
755
|
+
// Do not restart a completed observable
|
|
756
|
+
return;
|
|
744
757
|
}
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
758
|
+
state.subscription = observableCreator({
|
|
759
|
+
input: state.input,
|
|
760
|
+
system
|
|
761
|
+
}).subscribe({
|
|
762
|
+
next: value => {
|
|
763
|
+
self.send({
|
|
764
|
+
type: nextEventType,
|
|
765
|
+
data: value
|
|
766
|
+
});
|
|
767
|
+
},
|
|
768
|
+
error: err => {
|
|
769
|
+
self.send({
|
|
770
|
+
type: errorEventType,
|
|
771
|
+
data: err
|
|
772
|
+
});
|
|
773
|
+
},
|
|
774
|
+
complete: () => {
|
|
775
|
+
self.send({
|
|
776
|
+
type: completeEventType
|
|
777
|
+
});
|
|
778
|
+
}
|
|
779
|
+
});
|
|
780
|
+
},
|
|
781
|
+
getSnapshot: state => state.data,
|
|
782
|
+
getPersistedState: ({
|
|
783
|
+
status,
|
|
784
|
+
data,
|
|
785
|
+
input
|
|
786
|
+
}) => ({
|
|
787
|
+
status,
|
|
788
|
+
data,
|
|
789
|
+
input
|
|
790
|
+
}),
|
|
791
|
+
getStatus: state => state,
|
|
792
|
+
restoreState: state => ({
|
|
793
|
+
...state,
|
|
794
|
+
subscription: undefined
|
|
795
|
+
})
|
|
796
|
+
};
|
|
797
|
+
return logic;
|
|
798
|
+
}
|
|
762
799
|
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
const status = this.logic.getStatus?.(state);
|
|
772
|
-
switch (status?.status) {
|
|
773
|
-
case 'done':
|
|
774
|
-
this._stopProcedure();
|
|
775
|
-
this._doneEvent = doneInvoke(this.id, status.data);
|
|
776
|
-
this._parent?.send(this._doneEvent);
|
|
777
|
-
this._complete();
|
|
778
|
-
break;
|
|
779
|
-
case 'error':
|
|
780
|
-
this._stopProcedure();
|
|
781
|
-
this._parent?.send(error(this.id, status.data));
|
|
782
|
-
this._error(status.data);
|
|
783
|
-
break;
|
|
784
|
-
}
|
|
785
|
-
}
|
|
786
|
-
subscribe(nextListenerOrObserver, errorListener, completeListener) {
|
|
787
|
-
const observer = toObserver(nextListenerOrObserver, errorListener, completeListener);
|
|
788
|
-
this.observers.add(observer);
|
|
789
|
-
if (this.status === ActorStatus.Stopped) {
|
|
790
|
-
observer.complete?.();
|
|
791
|
-
this.observers.delete(observer);
|
|
792
|
-
}
|
|
793
|
-
return {
|
|
794
|
-
unsubscribe: () => {
|
|
795
|
-
this.observers.delete(observer);
|
|
796
|
-
}
|
|
797
|
-
};
|
|
798
|
-
}
|
|
800
|
+
/**
|
|
801
|
+
* Creates event observable logic that listens to an observable
|
|
802
|
+
* that delivers event objects.
|
|
803
|
+
*
|
|
804
|
+
*
|
|
805
|
+
* @param lazyObservable A function that creates an observable
|
|
806
|
+
* @returns Event observable logic
|
|
807
|
+
*/
|
|
799
808
|
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
start() {
|
|
804
|
-
if (this.status === ActorStatus.Running) {
|
|
805
|
-
// Do not restart the service if it is already started
|
|
806
|
-
return this;
|
|
807
|
-
}
|
|
808
|
-
this.system._register(this.sessionId, this);
|
|
809
|
-
if (this._systemId) {
|
|
810
|
-
this.system._set(this._systemId, this);
|
|
811
|
-
}
|
|
812
|
-
this.status = ActorStatus.Running;
|
|
813
|
-
if (this.logic.start) {
|
|
814
|
-
this.logic.start(this._state, this._actorContext);
|
|
815
|
-
}
|
|
809
|
+
function fromEventObservable(lazyObservable) {
|
|
810
|
+
const errorEventType = '$$xstate.error';
|
|
811
|
+
const completeEventType = '$$xstate.complete';
|
|
816
812
|
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
}
|
|
824
|
-
this.mailbox.start();
|
|
825
|
-
return this;
|
|
826
|
-
}
|
|
827
|
-
_process(event) {
|
|
828
|
-
try {
|
|
829
|
-
const nextState = this.logic.transition(this._state, event, this._actorContext);
|
|
830
|
-
this.update(nextState);
|
|
831
|
-
if (event.type === stopSignalType) {
|
|
832
|
-
this._stopProcedure();
|
|
833
|
-
this._complete();
|
|
813
|
+
// TODO: event types
|
|
814
|
+
const logic = {
|
|
815
|
+
config: lazyObservable,
|
|
816
|
+
transition: (state, event) => {
|
|
817
|
+
if (state.status !== 'active') {
|
|
818
|
+
return state;
|
|
834
819
|
}
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
820
|
+
switch (event.type) {
|
|
821
|
+
case errorEventType:
|
|
822
|
+
return {
|
|
823
|
+
...state,
|
|
824
|
+
status: 'error',
|
|
825
|
+
input: undefined,
|
|
826
|
+
data: event.data,
|
|
827
|
+
subscription: undefined
|
|
828
|
+
};
|
|
829
|
+
case completeEventType:
|
|
830
|
+
return {
|
|
831
|
+
...state,
|
|
832
|
+
status: 'done',
|
|
833
|
+
input: undefined,
|
|
834
|
+
subscription: undefined
|
|
835
|
+
};
|
|
836
|
+
case stopSignalType:
|
|
837
|
+
state.subscription.unsubscribe();
|
|
838
|
+
return {
|
|
839
|
+
...state,
|
|
840
|
+
status: 'canceled',
|
|
841
|
+
input: undefined,
|
|
842
|
+
subscription: undefined
|
|
843
|
+
};
|
|
844
|
+
default:
|
|
845
|
+
return state;
|
|
844
846
|
}
|
|
845
|
-
}
|
|
846
|
-
}
|
|
847
|
-
_stop() {
|
|
848
|
-
if (this.status === ActorStatus.Stopped) {
|
|
849
|
-
return this;
|
|
850
|
-
}
|
|
851
|
-
this.mailbox.clear();
|
|
852
|
-
if (this.status === ActorStatus.NotStarted) {
|
|
853
|
-
this.status = ActorStatus.Stopped;
|
|
854
|
-
return this;
|
|
855
|
-
}
|
|
856
|
-
this.mailbox.enqueue({
|
|
857
|
-
type: stopSignalType
|
|
858
|
-
});
|
|
859
|
-
return this;
|
|
860
|
-
}
|
|
861
|
-
|
|
862
|
-
/**
|
|
863
|
-
* Stops the interpreter and unsubscribe all listeners.
|
|
864
|
-
*/
|
|
865
|
-
stop() {
|
|
866
|
-
if (this._parent) {
|
|
867
|
-
throw new Error('A non-root actor cannot be stopped directly.');
|
|
868
|
-
}
|
|
869
|
-
return this._stop();
|
|
870
|
-
}
|
|
871
|
-
_complete() {
|
|
872
|
-
for (const observer of this.observers) {
|
|
873
|
-
observer.complete?.();
|
|
874
|
-
}
|
|
875
|
-
this.observers.clear();
|
|
876
|
-
}
|
|
877
|
-
_error(data) {
|
|
878
|
-
for (const observer of this.observers) {
|
|
879
|
-
observer.error?.(data);
|
|
880
|
-
}
|
|
881
|
-
this.observers.clear();
|
|
882
|
-
}
|
|
883
|
-
_stopProcedure() {
|
|
884
|
-
if (this.status !== ActorStatus.Running) {
|
|
885
|
-
// Interpreter already stopped; do nothing
|
|
886
|
-
return this;
|
|
887
|
-
}
|
|
888
|
-
|
|
889
|
-
// Cancel all delayed events
|
|
890
|
-
for (const key of Object.keys(this.delayedEventsMap)) {
|
|
891
|
-
this.clock.clearTimeout(this.delayedEventsMap[key]);
|
|
892
|
-
}
|
|
893
|
-
|
|
894
|
-
// TODO: mailbox.reset
|
|
895
|
-
this.mailbox.clear();
|
|
896
|
-
// TODO: after `stop` we must prepare ourselves for receiving events again
|
|
897
|
-
// events sent *after* stop signal must be queued
|
|
898
|
-
// it seems like this should be the common behavior for all of our consumers
|
|
899
|
-
// so perhaps this should be unified somehow for all of them
|
|
900
|
-
this.mailbox = new Mailbox(this._process.bind(this));
|
|
901
|
-
this.status = ActorStatus.Stopped;
|
|
902
|
-
this.system._unregister(this);
|
|
903
|
-
return this;
|
|
904
|
-
}
|
|
905
|
-
|
|
906
|
-
/**
|
|
907
|
-
* Sends an event to the running interpreter to trigger a transition.
|
|
908
|
-
*
|
|
909
|
-
* @param event The event to send
|
|
910
|
-
*/
|
|
911
|
-
send(event) {
|
|
912
|
-
if (typeof event === 'string') {
|
|
913
|
-
throw new Error(`Only event objects may be sent to actors; use .send({ type: "${event}" }) instead`);
|
|
914
|
-
}
|
|
915
|
-
if (this.status === ActorStatus.Stopped) {
|
|
916
|
-
// do nothing
|
|
917
|
-
{
|
|
918
|
-
const eventString = JSON.stringify(event);
|
|
919
|
-
console.warn(`Event "${event.type.toString()}" was sent to stopped actor "${this.id} (${this.sessionId})". This actor has already reached its final state, and will not transition.\nEvent: ${eventString}`);
|
|
920
|
-
}
|
|
921
|
-
return;
|
|
922
|
-
}
|
|
923
|
-
if (this.status !== ActorStatus.Running && !this.options.deferEvents) {
|
|
924
|
-
throw new Error(`Event "${event.type}" was sent to uninitialized actor "${this.id
|
|
925
|
-
// tslint:disable-next-line:max-line-length
|
|
926
|
-
}". Make sure .start() is called for this actor, or set { deferEvents: true } in the actor options.\nEvent: ${JSON.stringify(event)}`);
|
|
927
|
-
}
|
|
928
|
-
this.mailbox.enqueue(event);
|
|
929
|
-
}
|
|
930
|
-
|
|
931
|
-
// TODO: make private (and figure out a way to do this within the machine)
|
|
932
|
-
delaySend(sendAction) {
|
|
933
|
-
this.delayedEventsMap[sendAction.params.id] = this.clock.setTimeout(() => {
|
|
934
|
-
if ('to' in sendAction.params && sendAction.params.to) {
|
|
935
|
-
sendAction.params.to.send(sendAction.params.event);
|
|
936
|
-
} else {
|
|
937
|
-
this.send(sendAction.params.event);
|
|
938
|
-
}
|
|
939
|
-
}, sendAction.params.delay);
|
|
940
|
-
}
|
|
941
|
-
|
|
942
|
-
// TODO: make private (and figure out a way to do this within the machine)
|
|
943
|
-
cancel(sendId) {
|
|
944
|
-
this.clock.clearTimeout(this.delayedEventsMap[sendId]);
|
|
945
|
-
delete this.delayedEventsMap[sendId];
|
|
946
|
-
}
|
|
947
|
-
attachDevTools() {
|
|
948
|
-
const {
|
|
949
|
-
devTools
|
|
950
|
-
} = this.options;
|
|
951
|
-
if (devTools) {
|
|
952
|
-
const resolvedDevToolsAdapter = typeof devTools === 'function' ? devTools : devToolsAdapter;
|
|
953
|
-
resolvedDevToolsAdapter(this);
|
|
954
|
-
}
|
|
955
|
-
}
|
|
956
|
-
toJSON() {
|
|
957
|
-
return {
|
|
958
|
-
id: this.id
|
|
959
|
-
};
|
|
960
|
-
}
|
|
961
|
-
getPersistedState() {
|
|
962
|
-
return this.logic.getPersistedState?.(this._state);
|
|
963
|
-
}
|
|
964
|
-
[symbolObservable]() {
|
|
965
|
-
return this;
|
|
966
|
-
}
|
|
967
|
-
getSnapshot() {
|
|
968
|
-
return this.logic.getSnapshot ? this.logic.getSnapshot(this._state) : this._state;
|
|
969
|
-
}
|
|
970
|
-
}
|
|
971
|
-
|
|
972
|
-
/**
|
|
973
|
-
* Creates a new Interpreter instance for the given machine with the provided options, if any.
|
|
974
|
-
*
|
|
975
|
-
* @param machine The machine to interpret
|
|
976
|
-
* @param options Interpreter options
|
|
977
|
-
*/
|
|
978
|
-
|
|
979
|
-
function interpret(logic, options) {
|
|
980
|
-
const interpreter = new Interpreter(logic, options);
|
|
981
|
-
return interpreter;
|
|
982
|
-
}
|
|
983
|
-
|
|
984
|
-
/**
|
|
985
|
-
* Returns actor logic from a transition function and its initial state.
|
|
986
|
-
*
|
|
987
|
-
* A transition function is a function that takes the current state and an event and returns the next state.
|
|
988
|
-
*
|
|
989
|
-
* @param transition The transition function that returns the next state given the current state and event.
|
|
990
|
-
* @param initialState The initial state of the transition function.
|
|
991
|
-
* @returns Actor logic
|
|
992
|
-
*/
|
|
993
|
-
function fromTransition(transition, initialState) {
|
|
994
|
-
const logic = {
|
|
995
|
-
config: transition,
|
|
996
|
-
transition: (state, event, actorContext) => {
|
|
997
|
-
return transition(state, event, actorContext);
|
|
998
847
|
},
|
|
999
848
|
getInitialState: (_, input) => {
|
|
1000
|
-
return
|
|
849
|
+
return {
|
|
850
|
+
subscription: undefined,
|
|
851
|
+
status: 'active',
|
|
852
|
+
data: undefined,
|
|
1001
853
|
input
|
|
1002
|
-
}
|
|
1003
|
-
},
|
|
1004
|
-
getSnapshot: state => state,
|
|
1005
|
-
getPersistedState: state => state,
|
|
1006
|
-
restoreState: state => state
|
|
1007
|
-
};
|
|
1008
|
-
return logic;
|
|
1009
|
-
}
|
|
1010
|
-
|
|
1011
|
-
const resolveEventType = '$$xstate.resolve';
|
|
1012
|
-
const rejectEventType = '$$xstate.reject';
|
|
1013
|
-
function fromPromise(
|
|
1014
|
-
// TODO: add types
|
|
1015
|
-
promiseCreator) {
|
|
1016
|
-
// TODO: add event types, consider making the `PromiseEvent` a private type or smth alike
|
|
1017
|
-
const logic = {
|
|
1018
|
-
config: promiseCreator,
|
|
1019
|
-
transition: (state, event) => {
|
|
1020
|
-
if (state.status !== 'active') {
|
|
1021
|
-
return state;
|
|
1022
|
-
}
|
|
1023
|
-
switch (event.type) {
|
|
1024
|
-
case resolveEventType:
|
|
1025
|
-
return {
|
|
1026
|
-
...state,
|
|
1027
|
-
status: 'done',
|
|
1028
|
-
data: event.data,
|
|
1029
|
-
input: undefined
|
|
1030
|
-
};
|
|
1031
|
-
case rejectEventType:
|
|
1032
|
-
return {
|
|
1033
|
-
...state,
|
|
1034
|
-
status: 'error',
|
|
1035
|
-
data: event.data,
|
|
1036
|
-
input: undefined
|
|
1037
|
-
};
|
|
1038
|
-
case stopSignalType:
|
|
1039
|
-
return {
|
|
1040
|
-
...state,
|
|
1041
|
-
status: 'canceled',
|
|
1042
|
-
input: undefined
|
|
1043
|
-
};
|
|
1044
|
-
default:
|
|
1045
|
-
return state;
|
|
1046
|
-
}
|
|
854
|
+
};
|
|
1047
855
|
},
|
|
1048
856
|
start: (state, {
|
|
1049
857
|
self,
|
|
1050
858
|
system
|
|
1051
859
|
}) => {
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
if (state.status !== 'active') {
|
|
860
|
+
if (state.status === 'done') {
|
|
861
|
+
// Do not restart a completed observable
|
|
1055
862
|
return;
|
|
1056
863
|
}
|
|
1057
|
-
|
|
864
|
+
state.subscription = lazyObservable({
|
|
1058
865
|
input: state.input,
|
|
1059
866
|
system
|
|
1060
|
-
})
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
}
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
}
|
|
1075
|
-
self.send({
|
|
1076
|
-
type: rejectEventType,
|
|
1077
|
-
data: errorData
|
|
1078
|
-
});
|
|
1079
|
-
});
|
|
1080
|
-
},
|
|
1081
|
-
getInitialState: (_, input) => {
|
|
1082
|
-
return {
|
|
1083
|
-
status: 'active',
|
|
1084
|
-
data: undefined,
|
|
1085
|
-
input
|
|
1086
|
-
};
|
|
1087
|
-
},
|
|
1088
|
-
getSnapshot: state => state.data,
|
|
1089
|
-
getStatus: state => state,
|
|
1090
|
-
getPersistedState: state => state,
|
|
1091
|
-
restoreState: state => state
|
|
1092
|
-
};
|
|
1093
|
-
return logic;
|
|
1094
|
-
}
|
|
1095
|
-
|
|
1096
|
-
// TODO: this likely shouldn't accept TEvent, observable actor doesn't accept external events
|
|
1097
|
-
function fromObservable(observableCreator) {
|
|
1098
|
-
const nextEventType = '$$xstate.next';
|
|
1099
|
-
const errorEventType = '$$xstate.error';
|
|
1100
|
-
const completeEventType = '$$xstate.complete';
|
|
1101
|
-
|
|
1102
|
-
// TODO: add event types
|
|
1103
|
-
const logic = {
|
|
1104
|
-
config: observableCreator,
|
|
1105
|
-
transition: (state, event, {
|
|
1106
|
-
self,
|
|
1107
|
-
id,
|
|
1108
|
-
defer
|
|
1109
|
-
}) => {
|
|
1110
|
-
if (state.status !== 'active') {
|
|
1111
|
-
return state;
|
|
1112
|
-
}
|
|
1113
|
-
switch (event.type) {
|
|
1114
|
-
case nextEventType:
|
|
1115
|
-
// match the exact timing of events sent by machines
|
|
1116
|
-
// send actions are not executed immediately
|
|
1117
|
-
defer(() => {
|
|
1118
|
-
self._parent?.send({
|
|
1119
|
-
type: `xstate.snapshot.${id}`,
|
|
1120
|
-
data: event.data
|
|
1121
|
-
});
|
|
1122
|
-
});
|
|
1123
|
-
return {
|
|
1124
|
-
...state,
|
|
1125
|
-
data: event.data
|
|
1126
|
-
};
|
|
1127
|
-
case errorEventType:
|
|
1128
|
-
return {
|
|
1129
|
-
...state,
|
|
1130
|
-
status: 'error',
|
|
1131
|
-
input: undefined,
|
|
1132
|
-
data: event.data,
|
|
1133
|
-
subscription: undefined
|
|
1134
|
-
};
|
|
1135
|
-
case completeEventType:
|
|
1136
|
-
return {
|
|
1137
|
-
...state,
|
|
1138
|
-
status: 'done',
|
|
1139
|
-
input: undefined,
|
|
1140
|
-
subscription: undefined
|
|
1141
|
-
};
|
|
1142
|
-
case stopSignalType:
|
|
1143
|
-
state.subscription.unsubscribe();
|
|
1144
|
-
return {
|
|
1145
|
-
...state,
|
|
1146
|
-
status: 'canceled',
|
|
1147
|
-
input: undefined,
|
|
1148
|
-
subscription: undefined
|
|
1149
|
-
};
|
|
1150
|
-
default:
|
|
1151
|
-
return state;
|
|
1152
|
-
}
|
|
1153
|
-
},
|
|
1154
|
-
getInitialState: (_, input) => {
|
|
1155
|
-
return {
|
|
1156
|
-
subscription: undefined,
|
|
1157
|
-
status: 'active',
|
|
1158
|
-
data: undefined,
|
|
1159
|
-
input
|
|
1160
|
-
};
|
|
1161
|
-
},
|
|
1162
|
-
start: (state, {
|
|
1163
|
-
self,
|
|
1164
|
-
system
|
|
1165
|
-
}) => {
|
|
1166
|
-
if (state.status === 'done') {
|
|
1167
|
-
// Do not restart a completed observable
|
|
1168
|
-
return;
|
|
1169
|
-
}
|
|
1170
|
-
state.subscription = observableCreator({
|
|
1171
|
-
input: state.input,
|
|
1172
|
-
system
|
|
1173
|
-
}).subscribe({
|
|
1174
|
-
next: value => {
|
|
1175
|
-
self.send({
|
|
1176
|
-
type: nextEventType,
|
|
1177
|
-
data: value
|
|
1178
|
-
});
|
|
1179
|
-
},
|
|
1180
|
-
error: err => {
|
|
1181
|
-
self.send({
|
|
1182
|
-
type: errorEventType,
|
|
1183
|
-
data: err
|
|
1184
|
-
});
|
|
1185
|
-
},
|
|
1186
|
-
complete: () => {
|
|
1187
|
-
self.send({
|
|
1188
|
-
type: completeEventType
|
|
1189
|
-
});
|
|
1190
|
-
}
|
|
1191
|
-
});
|
|
1192
|
-
},
|
|
1193
|
-
getSnapshot: state => state.data,
|
|
1194
|
-
getPersistedState: ({
|
|
1195
|
-
status,
|
|
1196
|
-
data,
|
|
1197
|
-
input
|
|
1198
|
-
}) => ({
|
|
1199
|
-
status,
|
|
1200
|
-
data,
|
|
1201
|
-
input
|
|
1202
|
-
}),
|
|
1203
|
-
getStatus: state => state,
|
|
1204
|
-
restoreState: state => ({
|
|
1205
|
-
...state,
|
|
1206
|
-
subscription: undefined
|
|
1207
|
-
})
|
|
1208
|
-
};
|
|
1209
|
-
return logic;
|
|
1210
|
-
}
|
|
1211
|
-
|
|
1212
|
-
/**
|
|
1213
|
-
* Creates event observable logic that listens to an observable
|
|
1214
|
-
* that delivers event objects.
|
|
1215
|
-
*
|
|
1216
|
-
*
|
|
1217
|
-
* @param lazyObservable A function that creates an observable
|
|
1218
|
-
* @returns Event observable logic
|
|
1219
|
-
*/
|
|
1220
|
-
|
|
1221
|
-
function fromEventObservable(lazyObservable) {
|
|
1222
|
-
const errorEventType = '$$xstate.error';
|
|
1223
|
-
const completeEventType = '$$xstate.complete';
|
|
1224
|
-
|
|
1225
|
-
// TODO: event types
|
|
1226
|
-
const logic = {
|
|
1227
|
-
config: lazyObservable,
|
|
1228
|
-
transition: (state, event) => {
|
|
1229
|
-
if (state.status !== 'active') {
|
|
1230
|
-
return state;
|
|
1231
|
-
}
|
|
1232
|
-
switch (event.type) {
|
|
1233
|
-
case errorEventType:
|
|
1234
|
-
return {
|
|
1235
|
-
...state,
|
|
1236
|
-
status: 'error',
|
|
1237
|
-
input: undefined,
|
|
1238
|
-
data: event.data,
|
|
1239
|
-
subscription: undefined
|
|
1240
|
-
};
|
|
1241
|
-
case completeEventType:
|
|
1242
|
-
return {
|
|
1243
|
-
...state,
|
|
1244
|
-
status: 'done',
|
|
1245
|
-
input: undefined,
|
|
1246
|
-
subscription: undefined
|
|
1247
|
-
};
|
|
1248
|
-
case stopSignalType:
|
|
1249
|
-
state.subscription.unsubscribe();
|
|
1250
|
-
return {
|
|
1251
|
-
...state,
|
|
1252
|
-
status: 'canceled',
|
|
1253
|
-
input: undefined,
|
|
1254
|
-
subscription: undefined
|
|
1255
|
-
};
|
|
1256
|
-
default:
|
|
1257
|
-
return state;
|
|
1258
|
-
}
|
|
1259
|
-
},
|
|
1260
|
-
getInitialState: (_, input) => {
|
|
1261
|
-
return {
|
|
1262
|
-
subscription: undefined,
|
|
1263
|
-
status: 'active',
|
|
1264
|
-
data: undefined,
|
|
1265
|
-
input
|
|
1266
|
-
};
|
|
1267
|
-
},
|
|
1268
|
-
start: (state, {
|
|
1269
|
-
self,
|
|
1270
|
-
system
|
|
1271
|
-
}) => {
|
|
1272
|
-
if (state.status === 'done') {
|
|
1273
|
-
// Do not restart a completed observable
|
|
1274
|
-
return;
|
|
1275
|
-
}
|
|
1276
|
-
state.subscription = lazyObservable({
|
|
1277
|
-
input: state.input,
|
|
1278
|
-
system
|
|
1279
|
-
}).subscribe({
|
|
1280
|
-
next: value => {
|
|
1281
|
-
self._parent?.send(value);
|
|
1282
|
-
},
|
|
1283
|
-
error: err => {
|
|
1284
|
-
self.send({
|
|
1285
|
-
type: errorEventType,
|
|
1286
|
-
data: err
|
|
1287
|
-
});
|
|
1288
|
-
},
|
|
1289
|
-
complete: () => {
|
|
1290
|
-
self.send({
|
|
1291
|
-
type: completeEventType
|
|
1292
|
-
});
|
|
867
|
+
}).subscribe({
|
|
868
|
+
next: value => {
|
|
869
|
+
self._parent?.send(value);
|
|
870
|
+
},
|
|
871
|
+
error: err => {
|
|
872
|
+
self.send({
|
|
873
|
+
type: errorEventType,
|
|
874
|
+
data: err
|
|
875
|
+
});
|
|
876
|
+
},
|
|
877
|
+
complete: () => {
|
|
878
|
+
self.send({
|
|
879
|
+
type: completeEventType
|
|
880
|
+
});
|
|
1293
881
|
}
|
|
1294
882
|
});
|
|
1295
883
|
},
|
|
@@ -1341,92 +929,475 @@ function fromCallback(invokeCallback) {
|
|
|
1341
929
|
input: state.input,
|
|
1342
930
|
system
|
|
1343
931
|
});
|
|
1344
|
-
if (isPromiseLike(state.dispose)) {
|
|
1345
|
-
state.dispose.then(resolved => {
|
|
1346
|
-
self._parent?.send(doneInvoke(id, resolved));
|
|
1347
|
-
state.canceled = true;
|
|
1348
|
-
}, errorData => {
|
|
1349
|
-
state.canceled = true;
|
|
1350
|
-
self._parent?.send(error(id, errorData));
|
|
1351
|
-
});
|
|
1352
|
-
}
|
|
1353
|
-
return state;
|
|
932
|
+
if (isPromiseLike(state.dispose)) {
|
|
933
|
+
state.dispose.then(resolved => {
|
|
934
|
+
self._parent?.send(doneInvoke(id, resolved));
|
|
935
|
+
state.canceled = true;
|
|
936
|
+
}, errorData => {
|
|
937
|
+
state.canceled = true;
|
|
938
|
+
self._parent?.send(error(id, errorData));
|
|
939
|
+
});
|
|
940
|
+
}
|
|
941
|
+
return state;
|
|
942
|
+
}
|
|
943
|
+
if (event.type === stopSignalType) {
|
|
944
|
+
state.canceled = true;
|
|
945
|
+
if (isFunction(state.dispose)) {
|
|
946
|
+
state.dispose();
|
|
947
|
+
}
|
|
948
|
+
return state;
|
|
949
|
+
}
|
|
950
|
+
if (isSignal(event)) {
|
|
951
|
+
// TODO: unrecognized signal
|
|
952
|
+
return state;
|
|
953
|
+
}
|
|
954
|
+
state.receivers.forEach(receiver => receiver(event));
|
|
955
|
+
return state;
|
|
956
|
+
},
|
|
957
|
+
getInitialState: (_, input) => {
|
|
958
|
+
return {
|
|
959
|
+
canceled: false,
|
|
960
|
+
receivers: new Set(),
|
|
961
|
+
dispose: undefined,
|
|
962
|
+
input
|
|
963
|
+
};
|
|
964
|
+
},
|
|
965
|
+
getSnapshot: () => undefined,
|
|
966
|
+
getPersistedState: ({
|
|
967
|
+
input
|
|
968
|
+
}) => input
|
|
969
|
+
};
|
|
970
|
+
return logic;
|
|
971
|
+
}
|
|
972
|
+
|
|
973
|
+
const startSignalType = 'xstate.init';
|
|
974
|
+
const stopSignalType = 'xstate.stop';
|
|
975
|
+
const startSignal = {
|
|
976
|
+
type: 'xstate.init'
|
|
977
|
+
};
|
|
978
|
+
const stopSignal = {
|
|
979
|
+
type: 'xstate.stop'
|
|
980
|
+
};
|
|
981
|
+
/**
|
|
982
|
+
* An object that expresses the actor logic in reaction to received events,
|
|
983
|
+
* as well as an optionally emitted stream of values.
|
|
984
|
+
*
|
|
985
|
+
* @template TReceived The received event
|
|
986
|
+
* @template TSnapshot The emitted value
|
|
987
|
+
*/
|
|
988
|
+
|
|
989
|
+
function isSignal(event) {
|
|
990
|
+
return event.type === startSignalType || event.type === stopSignalType;
|
|
991
|
+
}
|
|
992
|
+
function isActorRef(item) {
|
|
993
|
+
return !!item && typeof item === 'object' && typeof item.send === 'function';
|
|
994
|
+
}
|
|
995
|
+
|
|
996
|
+
// TODO: refactor the return type, this could be written in a better way
|
|
997
|
+
// but it's best to avoid unneccessary breaking changes now
|
|
998
|
+
// @deprecated use `interpret(actorLogic)` instead
|
|
999
|
+
function toActorRef(actorRefLike) {
|
|
1000
|
+
return {
|
|
1001
|
+
subscribe: () => ({
|
|
1002
|
+
unsubscribe: () => void 0
|
|
1003
|
+
}),
|
|
1004
|
+
id: 'anonymous',
|
|
1005
|
+
sessionId: '',
|
|
1006
|
+
getSnapshot: () => undefined,
|
|
1007
|
+
[symbolObservable]: function () {
|
|
1008
|
+
return this;
|
|
1009
|
+
},
|
|
1010
|
+
status: ActorStatus.Running,
|
|
1011
|
+
stop: () => void 0,
|
|
1012
|
+
...actorRefLike
|
|
1013
|
+
};
|
|
1014
|
+
}
|
|
1015
|
+
const emptyLogic = fromTransition(_ => undefined, undefined);
|
|
1016
|
+
function createEmptyActor() {
|
|
1017
|
+
return interpret(emptyLogic);
|
|
1018
|
+
}
|
|
1019
|
+
|
|
1020
|
+
function createSystem() {
|
|
1021
|
+
let sessionIdCounter = 0;
|
|
1022
|
+
const children = new Map();
|
|
1023
|
+
const keyedActors = new Map();
|
|
1024
|
+
const reverseKeyedActors = new WeakMap();
|
|
1025
|
+
const system = {
|
|
1026
|
+
_bookId: () => `x:${sessionIdCounter++}`,
|
|
1027
|
+
_register: (sessionId, actorRef) => {
|
|
1028
|
+
children.set(sessionId, actorRef);
|
|
1029
|
+
return sessionId;
|
|
1030
|
+
},
|
|
1031
|
+
_unregister: actorRef => {
|
|
1032
|
+
children.delete(actorRef.sessionId);
|
|
1033
|
+
const systemId = reverseKeyedActors.get(actorRef);
|
|
1034
|
+
if (systemId !== undefined) {
|
|
1035
|
+
keyedActors.delete(systemId);
|
|
1036
|
+
reverseKeyedActors.delete(actorRef);
|
|
1037
|
+
}
|
|
1038
|
+
},
|
|
1039
|
+
get: systemId => {
|
|
1040
|
+
return keyedActors.get(systemId);
|
|
1041
|
+
},
|
|
1042
|
+
_set: (systemId, actorRef) => {
|
|
1043
|
+
const existing = keyedActors.get(systemId);
|
|
1044
|
+
if (existing && existing !== actorRef) {
|
|
1045
|
+
throw new Error(`Actor with system ID '${systemId}' already exists.`);
|
|
1046
|
+
}
|
|
1047
|
+
keyedActors.set(systemId, actorRef);
|
|
1048
|
+
reverseKeyedActors.set(actorRef, systemId);
|
|
1049
|
+
}
|
|
1050
|
+
};
|
|
1051
|
+
return system;
|
|
1052
|
+
}
|
|
1053
|
+
|
|
1054
|
+
let ActorStatus = /*#__PURE__*/function (ActorStatus) {
|
|
1055
|
+
ActorStatus[ActorStatus["NotStarted"] = 0] = "NotStarted";
|
|
1056
|
+
ActorStatus[ActorStatus["Running"] = 1] = "Running";
|
|
1057
|
+
ActorStatus[ActorStatus["Stopped"] = 2] = "Stopped";
|
|
1058
|
+
return ActorStatus;
|
|
1059
|
+
}({});
|
|
1060
|
+
const defaultOptions = {
|
|
1061
|
+
deferEvents: true,
|
|
1062
|
+
clock: {
|
|
1063
|
+
setTimeout: (fn, ms) => {
|
|
1064
|
+
return setTimeout(fn, ms);
|
|
1065
|
+
},
|
|
1066
|
+
clearTimeout: id => {
|
|
1067
|
+
return clearTimeout(id);
|
|
1068
|
+
}
|
|
1069
|
+
},
|
|
1070
|
+
logger: console.log.bind(console),
|
|
1071
|
+
devTools: false
|
|
1072
|
+
};
|
|
1073
|
+
class Interpreter {
|
|
1074
|
+
/**
|
|
1075
|
+
* The current state of the interpreted logic.
|
|
1076
|
+
*/
|
|
1077
|
+
|
|
1078
|
+
/**
|
|
1079
|
+
* The clock that is responsible for setting and clearing timeouts, such as delayed events and transitions.
|
|
1080
|
+
*/
|
|
1081
|
+
|
|
1082
|
+
/**
|
|
1083
|
+
* The unique identifier for this actor relative to its parent.
|
|
1084
|
+
*/
|
|
1085
|
+
|
|
1086
|
+
/**
|
|
1087
|
+
* Whether the service is started.
|
|
1088
|
+
*/
|
|
1089
|
+
|
|
1090
|
+
// Actor Ref
|
|
1091
|
+
|
|
1092
|
+
// TODO: add typings for system
|
|
1093
|
+
|
|
1094
|
+
/**
|
|
1095
|
+
* The globally unique process ID for this invocation.
|
|
1096
|
+
*/
|
|
1097
|
+
|
|
1098
|
+
/**
|
|
1099
|
+
* Creates a new Interpreter instance (i.e., service) for the given logic with the provided options, if any.
|
|
1100
|
+
*
|
|
1101
|
+
* @param logic The logic to be interpreted
|
|
1102
|
+
* @param options Interpreter options
|
|
1103
|
+
*/
|
|
1104
|
+
constructor(logic, options) {
|
|
1105
|
+
this.logic = logic;
|
|
1106
|
+
this._state = void 0;
|
|
1107
|
+
this.clock = void 0;
|
|
1108
|
+
this.options = void 0;
|
|
1109
|
+
this.id = void 0;
|
|
1110
|
+
this.mailbox = new Mailbox(this._process.bind(this));
|
|
1111
|
+
this.delayedEventsMap = {};
|
|
1112
|
+
this.observers = new Set();
|
|
1113
|
+
this.logger = void 0;
|
|
1114
|
+
this.status = ActorStatus.NotStarted;
|
|
1115
|
+
this._parent = void 0;
|
|
1116
|
+
this.ref = void 0;
|
|
1117
|
+
this._actorContext = void 0;
|
|
1118
|
+
this._systemId = void 0;
|
|
1119
|
+
this.sessionId = void 0;
|
|
1120
|
+
this.system = void 0;
|
|
1121
|
+
this._doneEvent = void 0;
|
|
1122
|
+
this.src = void 0;
|
|
1123
|
+
this._deferred = [];
|
|
1124
|
+
const resolvedOptions = {
|
|
1125
|
+
...defaultOptions,
|
|
1126
|
+
...options
|
|
1127
|
+
};
|
|
1128
|
+
const {
|
|
1129
|
+
clock,
|
|
1130
|
+
logger,
|
|
1131
|
+
parent,
|
|
1132
|
+
id,
|
|
1133
|
+
systemId
|
|
1134
|
+
} = resolvedOptions;
|
|
1135
|
+
const self = this;
|
|
1136
|
+
this.system = parent?.system ?? createSystem();
|
|
1137
|
+
if (systemId) {
|
|
1138
|
+
this._systemId = systemId;
|
|
1139
|
+
this.system._set(systemId, this);
|
|
1140
|
+
}
|
|
1141
|
+
this.sessionId = this.system._bookId();
|
|
1142
|
+
this.id = id ?? this.sessionId;
|
|
1143
|
+
this.logger = logger;
|
|
1144
|
+
this.clock = clock;
|
|
1145
|
+
this._parent = parent;
|
|
1146
|
+
this.options = resolvedOptions;
|
|
1147
|
+
this.src = resolvedOptions.src;
|
|
1148
|
+
this.ref = this;
|
|
1149
|
+
this._actorContext = {
|
|
1150
|
+
self,
|
|
1151
|
+
id: this.id,
|
|
1152
|
+
sessionId: this.sessionId,
|
|
1153
|
+
logger: this.logger,
|
|
1154
|
+
defer: fn => {
|
|
1155
|
+
this._deferred.push(fn);
|
|
1156
|
+
},
|
|
1157
|
+
system: this.system,
|
|
1158
|
+
stopChild: child => {
|
|
1159
|
+
if (child._parent !== this) {
|
|
1160
|
+
throw new Error(`Cannot stop child actor ${child.id} of ${this.id} because it is not a child`);
|
|
1161
|
+
}
|
|
1162
|
+
child._stop();
|
|
1163
|
+
}
|
|
1164
|
+
};
|
|
1165
|
+
|
|
1166
|
+
// Ensure that the send method is bound to this interpreter instance
|
|
1167
|
+
// if destructured
|
|
1168
|
+
this.send = this.send.bind(this);
|
|
1169
|
+
this._initState();
|
|
1170
|
+
}
|
|
1171
|
+
_initState() {
|
|
1172
|
+
this._state = this.options.state ? this.logic.restoreState ? this.logic.restoreState(this.options.state, this._actorContext) : this.options.state : this.logic.getInitialState(this._actorContext, this.options?.input);
|
|
1173
|
+
}
|
|
1174
|
+
|
|
1175
|
+
// array of functions to defer
|
|
1176
|
+
|
|
1177
|
+
update(state) {
|
|
1178
|
+
// Update state
|
|
1179
|
+
this._state = state;
|
|
1180
|
+
const snapshot = this.getSnapshot();
|
|
1181
|
+
|
|
1182
|
+
// Execute deferred effects
|
|
1183
|
+
let deferredFn;
|
|
1184
|
+
while (deferredFn = this._deferred.shift()) {
|
|
1185
|
+
deferredFn();
|
|
1186
|
+
}
|
|
1187
|
+
for (const observer of this.observers) {
|
|
1188
|
+
observer.next?.(snapshot);
|
|
1189
|
+
}
|
|
1190
|
+
const status = this.logic.getStatus?.(state);
|
|
1191
|
+
switch (status?.status) {
|
|
1192
|
+
case 'done':
|
|
1193
|
+
this._stopProcedure();
|
|
1194
|
+
this._doneEvent = doneInvoke(this.id, status.data);
|
|
1195
|
+
this._parent?.send(this._doneEvent);
|
|
1196
|
+
this._complete();
|
|
1197
|
+
break;
|
|
1198
|
+
case 'error':
|
|
1199
|
+
this._stopProcedure();
|
|
1200
|
+
this._parent?.send(error(this.id, status.data));
|
|
1201
|
+
this._error(status.data);
|
|
1202
|
+
break;
|
|
1203
|
+
}
|
|
1204
|
+
}
|
|
1205
|
+
subscribe(nextListenerOrObserver, errorListener, completeListener) {
|
|
1206
|
+
const observer = toObserver(nextListenerOrObserver, errorListener, completeListener);
|
|
1207
|
+
this.observers.add(observer);
|
|
1208
|
+
if (this.status === ActorStatus.Stopped) {
|
|
1209
|
+
observer.complete?.();
|
|
1210
|
+
this.observers.delete(observer);
|
|
1211
|
+
}
|
|
1212
|
+
return {
|
|
1213
|
+
unsubscribe: () => {
|
|
1214
|
+
this.observers.delete(observer);
|
|
1215
|
+
}
|
|
1216
|
+
};
|
|
1217
|
+
}
|
|
1218
|
+
|
|
1219
|
+
/**
|
|
1220
|
+
* Starts the interpreter from the initial state
|
|
1221
|
+
*/
|
|
1222
|
+
start() {
|
|
1223
|
+
if (this.status === ActorStatus.Running) {
|
|
1224
|
+
// Do not restart the service if it is already started
|
|
1225
|
+
return this;
|
|
1226
|
+
}
|
|
1227
|
+
this.system._register(this.sessionId, this);
|
|
1228
|
+
if (this._systemId) {
|
|
1229
|
+
this.system._set(this._systemId, this);
|
|
1230
|
+
}
|
|
1231
|
+
this.status = ActorStatus.Running;
|
|
1232
|
+
if (this.logic.start) {
|
|
1233
|
+
this.logic.start(this._state, this._actorContext);
|
|
1234
|
+
}
|
|
1235
|
+
|
|
1236
|
+
// TODO: this notifies all subscribers but usually this is redundant
|
|
1237
|
+
// there is no real change happening here
|
|
1238
|
+
// we need to rethink if this needs to be refactored
|
|
1239
|
+
this.update(this._state);
|
|
1240
|
+
if (this.options.devTools) {
|
|
1241
|
+
this.attachDevTools();
|
|
1242
|
+
}
|
|
1243
|
+
this.mailbox.start();
|
|
1244
|
+
return this;
|
|
1245
|
+
}
|
|
1246
|
+
_process(event) {
|
|
1247
|
+
try {
|
|
1248
|
+
const nextState = this.logic.transition(this._state, event, this._actorContext);
|
|
1249
|
+
this.update(nextState);
|
|
1250
|
+
if (event.type === stopSignalType) {
|
|
1251
|
+
this._stopProcedure();
|
|
1252
|
+
this._complete();
|
|
1253
|
+
}
|
|
1254
|
+
} catch (err) {
|
|
1255
|
+
// TODO: properly handle errors
|
|
1256
|
+
if (this.observers.size > 0) {
|
|
1257
|
+
this.observers.forEach(observer => {
|
|
1258
|
+
observer.error?.(err);
|
|
1259
|
+
});
|
|
1260
|
+
this.stop();
|
|
1261
|
+
} else {
|
|
1262
|
+
throw err;
|
|
1354
1263
|
}
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1264
|
+
}
|
|
1265
|
+
}
|
|
1266
|
+
_stop() {
|
|
1267
|
+
if (this.status === ActorStatus.Stopped) {
|
|
1268
|
+
return this;
|
|
1269
|
+
}
|
|
1270
|
+
this.mailbox.clear();
|
|
1271
|
+
if (this.status === ActorStatus.NotStarted) {
|
|
1272
|
+
this.status = ActorStatus.Stopped;
|
|
1273
|
+
return this;
|
|
1274
|
+
}
|
|
1275
|
+
this.mailbox.enqueue({
|
|
1276
|
+
type: stopSignalType
|
|
1277
|
+
});
|
|
1278
|
+
return this;
|
|
1279
|
+
}
|
|
1280
|
+
|
|
1281
|
+
/**
|
|
1282
|
+
* Stops the interpreter and unsubscribe all listeners.
|
|
1283
|
+
*/
|
|
1284
|
+
stop() {
|
|
1285
|
+
if (this._parent) {
|
|
1286
|
+
throw new Error('A non-root actor cannot be stopped directly.');
|
|
1287
|
+
}
|
|
1288
|
+
return this._stop();
|
|
1289
|
+
}
|
|
1290
|
+
_complete() {
|
|
1291
|
+
for (const observer of this.observers) {
|
|
1292
|
+
observer.complete?.();
|
|
1293
|
+
}
|
|
1294
|
+
this.observers.clear();
|
|
1295
|
+
}
|
|
1296
|
+
_error(data) {
|
|
1297
|
+
for (const observer of this.observers) {
|
|
1298
|
+
observer.error?.(data);
|
|
1299
|
+
}
|
|
1300
|
+
this.observers.clear();
|
|
1301
|
+
}
|
|
1302
|
+
_stopProcedure() {
|
|
1303
|
+
if (this.status !== ActorStatus.Running) {
|
|
1304
|
+
// Interpreter already stopped; do nothing
|
|
1305
|
+
return this;
|
|
1306
|
+
}
|
|
1307
|
+
|
|
1308
|
+
// Cancel all delayed events
|
|
1309
|
+
for (const key of Object.keys(this.delayedEventsMap)) {
|
|
1310
|
+
this.clock.clearTimeout(this.delayedEventsMap[key]);
|
|
1311
|
+
}
|
|
1312
|
+
|
|
1313
|
+
// TODO: mailbox.reset
|
|
1314
|
+
this.mailbox.clear();
|
|
1315
|
+
// TODO: after `stop` we must prepare ourselves for receiving events again
|
|
1316
|
+
// events sent *after* stop signal must be queued
|
|
1317
|
+
// it seems like this should be the common behavior for all of our consumers
|
|
1318
|
+
// so perhaps this should be unified somehow for all of them
|
|
1319
|
+
this.mailbox = new Mailbox(this._process.bind(this));
|
|
1320
|
+
this.status = ActorStatus.Stopped;
|
|
1321
|
+
this.system._unregister(this);
|
|
1322
|
+
return this;
|
|
1323
|
+
}
|
|
1324
|
+
|
|
1325
|
+
/**
|
|
1326
|
+
* Sends an event to the running interpreter to trigger a transition.
|
|
1327
|
+
*
|
|
1328
|
+
* @param event The event to send
|
|
1329
|
+
*/
|
|
1330
|
+
send(event) {
|
|
1331
|
+
if (typeof event === 'string') {
|
|
1332
|
+
throw new Error(`Only event objects may be sent to actors; use .send({ type: "${event}" }) instead`);
|
|
1333
|
+
}
|
|
1334
|
+
if (this.status === ActorStatus.Stopped) {
|
|
1335
|
+
// do nothing
|
|
1336
|
+
{
|
|
1337
|
+
const eventString = JSON.stringify(event);
|
|
1338
|
+
console.warn(`Event "${event.type.toString()}" was sent to stopped actor "${this.id} (${this.sessionId})". This actor has already reached its final state, and will not transition.\nEvent: ${eventString}`);
|
|
1361
1339
|
}
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1340
|
+
return;
|
|
1341
|
+
}
|
|
1342
|
+
if (this.status !== ActorStatus.Running && !this.options.deferEvents) {
|
|
1343
|
+
throw new Error(`Event "${event.type}" was sent to uninitialized actor "${this.id
|
|
1344
|
+
// tslint:disable-next-line:max-line-length
|
|
1345
|
+
}". Make sure .start() is called for this actor, or set { deferEvents: true } in the actor options.\nEvent: ${JSON.stringify(event)}`);
|
|
1346
|
+
}
|
|
1347
|
+
this.mailbox.enqueue(event);
|
|
1348
|
+
}
|
|
1349
|
+
|
|
1350
|
+
// TODO: make private (and figure out a way to do this within the machine)
|
|
1351
|
+
delaySend(sendAction) {
|
|
1352
|
+
this.delayedEventsMap[sendAction.params.id] = this.clock.setTimeout(() => {
|
|
1353
|
+
if ('to' in sendAction.params && sendAction.params.to) {
|
|
1354
|
+
sendAction.params.to.send(sendAction.params.event);
|
|
1355
|
+
} else {
|
|
1356
|
+
this.send(sendAction.params.event);
|
|
1365
1357
|
}
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1358
|
+
}, sendAction.params.delay);
|
|
1359
|
+
}
|
|
1360
|
+
|
|
1361
|
+
// TODO: make private (and figure out a way to do this within the machine)
|
|
1362
|
+
cancel(sendId) {
|
|
1363
|
+
this.clock.clearTimeout(this.delayedEventsMap[sendId]);
|
|
1364
|
+
delete this.delayedEventsMap[sendId];
|
|
1365
|
+
}
|
|
1366
|
+
attachDevTools() {
|
|
1367
|
+
const {
|
|
1368
|
+
devTools
|
|
1369
|
+
} = this.options;
|
|
1370
|
+
if (devTools) {
|
|
1371
|
+
const resolvedDevToolsAdapter = typeof devTools === 'function' ? devTools : devToolsAdapter;
|
|
1372
|
+
resolvedDevToolsAdapter(this);
|
|
1373
|
+
}
|
|
1374
|
+
}
|
|
1375
|
+
toJSON() {
|
|
1376
|
+
return {
|
|
1377
|
+
id: this.id
|
|
1378
|
+
};
|
|
1379
|
+
}
|
|
1380
|
+
getPersistedState() {
|
|
1381
|
+
return this.logic.getPersistedState?.(this._state);
|
|
1382
|
+
}
|
|
1383
|
+
[symbolObservable]() {
|
|
1384
|
+
return this;
|
|
1385
|
+
}
|
|
1386
|
+
getSnapshot() {
|
|
1387
|
+
return this.logic.getSnapshot ? this.logic.getSnapshot(this._state) : this._state;
|
|
1388
|
+
}
|
|
1383
1389
|
}
|
|
1384
1390
|
|
|
1385
|
-
const startSignalType = 'xstate.init';
|
|
1386
|
-
const stopSignalType = 'xstate.stop';
|
|
1387
|
-
const startSignal = {
|
|
1388
|
-
type: 'xstate.init'
|
|
1389
|
-
};
|
|
1390
|
-
const stopSignal = {
|
|
1391
|
-
type: 'xstate.stop'
|
|
1392
|
-
};
|
|
1393
1391
|
/**
|
|
1394
|
-
*
|
|
1395
|
-
* as well as an optionally emitted stream of values.
|
|
1392
|
+
* Creates a new Interpreter instance for the given machine with the provided options, if any.
|
|
1396
1393
|
*
|
|
1397
|
-
* @
|
|
1398
|
-
* @
|
|
1394
|
+
* @param machine The machine to interpret
|
|
1395
|
+
* @param options Interpreter options
|
|
1399
1396
|
*/
|
|
1400
1397
|
|
|
1401
|
-
function
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
function isActorRef(item) {
|
|
1405
|
-
return !!item && typeof item === 'object' && typeof item.send === 'function';
|
|
1406
|
-
}
|
|
1407
|
-
|
|
1408
|
-
// TODO: refactor the return type, this could be written in a better way
|
|
1409
|
-
// but it's best to avoid unneccessary breaking changes now
|
|
1410
|
-
// @deprecated use `interpret(actorLogic)` instead
|
|
1411
|
-
function toActorRef(actorRefLike) {
|
|
1412
|
-
return {
|
|
1413
|
-
subscribe: () => ({
|
|
1414
|
-
unsubscribe: () => void 0
|
|
1415
|
-
}),
|
|
1416
|
-
id: 'anonymous',
|
|
1417
|
-
sessionId: '',
|
|
1418
|
-
getSnapshot: () => undefined,
|
|
1419
|
-
[symbolObservable]: function () {
|
|
1420
|
-
return this;
|
|
1421
|
-
},
|
|
1422
|
-
status: ActorStatus.Running,
|
|
1423
|
-
stop: () => void 0,
|
|
1424
|
-
...actorRefLike
|
|
1425
|
-
};
|
|
1426
|
-
}
|
|
1427
|
-
const emptyLogic = fromTransition(_ => undefined, undefined);
|
|
1428
|
-
function createEmptyActor() {
|
|
1429
|
-
return interpret(emptyLogic);
|
|
1398
|
+
function interpret(logic, options) {
|
|
1399
|
+
const interpreter = new Interpreter(logic, options);
|
|
1400
|
+
return interpreter;
|
|
1430
1401
|
}
|
|
1431
1402
|
|
|
1432
1403
|
function invoke(invokeDef) {
|
|
@@ -1443,42 +1414,32 @@ function invoke(invokeDef) {
|
|
|
1443
1414
|
src
|
|
1444
1415
|
} = invokeDef;
|
|
1445
1416
|
let resolvedInvokeAction;
|
|
1446
|
-
|
|
1417
|
+
const referenced = resolveReferencedActor(state.machine.implementations.actors[src]);
|
|
1418
|
+
if (!referenced) {
|
|
1419
|
+
resolvedInvokeAction = {
|
|
1420
|
+
type,
|
|
1421
|
+
params: invokeDef
|
|
1422
|
+
};
|
|
1423
|
+
} else {
|
|
1424
|
+
const input = 'input' in invokeDef ? invokeDef.input : referenced.input;
|
|
1425
|
+
const ref = interpret(referenced.src, {
|
|
1426
|
+
id,
|
|
1427
|
+
src,
|
|
1428
|
+
parent: actorContext?.self,
|
|
1429
|
+
systemId: invokeDef.systemId,
|
|
1430
|
+
input: typeof input === 'function' ? input({
|
|
1431
|
+
context: state.context,
|
|
1432
|
+
event,
|
|
1433
|
+
self: actorContext?.self
|
|
1434
|
+
}) : input
|
|
1435
|
+
});
|
|
1447
1436
|
resolvedInvokeAction = {
|
|
1448
1437
|
type,
|
|
1449
1438
|
params: {
|
|
1450
1439
|
...invokeDef,
|
|
1451
|
-
ref
|
|
1440
|
+
ref
|
|
1452
1441
|
}
|
|
1453
1442
|
};
|
|
1454
|
-
} else {
|
|
1455
|
-
const referenced = resolveReferencedActor(state.machine.implementations.actors[src]);
|
|
1456
|
-
if (!referenced) {
|
|
1457
|
-
resolvedInvokeAction = {
|
|
1458
|
-
type,
|
|
1459
|
-
params: invokeDef
|
|
1460
|
-
};
|
|
1461
|
-
} else {
|
|
1462
|
-
const input = 'input' in invokeDef ? invokeDef.input : referenced.input;
|
|
1463
|
-
const ref = interpret(referenced.src, {
|
|
1464
|
-
id,
|
|
1465
|
-
src,
|
|
1466
|
-
parent: actorContext?.self,
|
|
1467
|
-
systemId: invokeDef.systemId,
|
|
1468
|
-
input: typeof input === 'function' ? input({
|
|
1469
|
-
context: state.context,
|
|
1470
|
-
event,
|
|
1471
|
-
self: actorContext?.self
|
|
1472
|
-
}) : input
|
|
1473
|
-
});
|
|
1474
|
-
resolvedInvokeAction = {
|
|
1475
|
-
type,
|
|
1476
|
-
params: {
|
|
1477
|
-
...invokeDef,
|
|
1478
|
-
ref
|
|
1479
|
-
}
|
|
1480
|
-
};
|
|
1481
|
-
}
|
|
1482
1443
|
}
|
|
1483
1444
|
const actorRef = resolvedInvokeAction.params.ref;
|
|
1484
1445
|
const invokedState = cloneState(state, {
|
|
@@ -2483,7 +2444,7 @@ function resolveActionsAndContext(actions, event, currentState, actorCtx) {
|
|
|
2483
2444
|
});
|
|
2484
2445
|
const matchedActions = resolvedAction.params?.actions;
|
|
2485
2446
|
intermediateState = nextState;
|
|
2486
|
-
if (
|
|
2447
|
+
if (resolvedAction.type === raise$1 && typeof resolvedAction.params.delay !== 'number') {
|
|
2487
2448
|
raiseActions.push(resolvedAction);
|
|
2488
2449
|
}
|
|
2489
2450
|
|
|
@@ -2801,11 +2762,14 @@ function stop(actorRef) {
|
|
|
2801
2762
|
actor
|
|
2802
2763
|
}
|
|
2803
2764
|
}, (event, {
|
|
2804
|
-
state
|
|
2765
|
+
state,
|
|
2766
|
+
actorContext
|
|
2805
2767
|
}) => {
|
|
2806
2768
|
const actorRefOrString = isFunction(actor) ? actor({
|
|
2807
2769
|
context: state.context,
|
|
2808
|
-
event
|
|
2770
|
+
event,
|
|
2771
|
+
self: actorContext?.self ?? {},
|
|
2772
|
+
system: actorContext?.system
|
|
2809
2773
|
}) : actor;
|
|
2810
2774
|
const actorRef = typeof actorRefOrString === 'string' ? state.children[actorRefOrString] : actorRefOrString;
|
|
2811
2775
|
let children = state.children;
|
|
@@ -2889,59 +2853,60 @@ function log(expr = defaultLogExpr, label) {
|
|
|
2889
2853
|
});
|
|
2890
2854
|
}
|
|
2891
2855
|
|
|
2892
|
-
function createSpawner(
|
|
2893
|
-
|
|
2856
|
+
function createSpawner(actorContext, {
|
|
2857
|
+
machine,
|
|
2858
|
+
context
|
|
2859
|
+
}, event, spawnedChildren) {
|
|
2860
|
+
const spawn = (src, options = {}) => {
|
|
2894
2861
|
const {
|
|
2895
2862
|
systemId
|
|
2896
2863
|
} = options;
|
|
2897
|
-
if (
|
|
2864
|
+
if (typeof src === 'string') {
|
|
2898
2865
|
const referenced = resolveReferencedActor(machine.implementations.actors[src]);
|
|
2899
|
-
if (referenced) {
|
|
2900
|
-
|
|
2901
|
-
|
|
2902
|
-
|
|
2903
|
-
|
|
2904
|
-
|
|
2905
|
-
|
|
2906
|
-
|
|
2907
|
-
|
|
2908
|
-
|
|
2909
|
-
|
|
2910
|
-
|
|
2911
|
-
|
|
2912
|
-
|
|
2913
|
-
|
|
2914
|
-
|
|
2915
|
-
|
|
2916
|
-
|
|
2917
|
-
ref: actorRef,
|
|
2918
|
-
meta: undefined,
|
|
2919
|
-
input,
|
|
2920
|
-
systemId
|
|
2921
|
-
}));
|
|
2922
|
-
return actorRef; // TODO: fix types
|
|
2923
|
-
}
|
|
2924
|
-
|
|
2925
|
-
throw new Error(`Actor logic '${src}' not implemented in machine '${machine.id}'`);
|
|
2866
|
+
if (!referenced) {
|
|
2867
|
+
throw new Error(`Actor logic '${src}' not implemented in machine '${machine.id}'`);
|
|
2868
|
+
}
|
|
2869
|
+
const input = 'input' in options ? options.input : referenced.input;
|
|
2870
|
+
|
|
2871
|
+
// TODO: this should also receive `src`
|
|
2872
|
+
const actor = interpret(referenced.src, {
|
|
2873
|
+
id: options.id,
|
|
2874
|
+
parent: actorContext.self,
|
|
2875
|
+
input: typeof input === 'function' ? input({
|
|
2876
|
+
context,
|
|
2877
|
+
event,
|
|
2878
|
+
self: actorContext.self
|
|
2879
|
+
}) : input,
|
|
2880
|
+
systemId
|
|
2881
|
+
});
|
|
2882
|
+
spawnedChildren[actor.id] = actor;
|
|
2883
|
+
return actor;
|
|
2926
2884
|
} else {
|
|
2927
2885
|
// TODO: this should also receive `src`
|
|
2928
|
-
|
|
2886
|
+
return interpret(src, {
|
|
2929
2887
|
id: options.id,
|
|
2930
|
-
parent: self,
|
|
2888
|
+
parent: actorContext.self,
|
|
2931
2889
|
input: options.input,
|
|
2932
2890
|
systemId
|
|
2933
2891
|
});
|
|
2934
|
-
mutCapturedActions.push(invoke({
|
|
2935
|
-
// @ts-ignore TODO: fix types
|
|
2936
|
-
src: actorRef,
|
|
2937
|
-
ref: actorRef,
|
|
2938
|
-
id: actorRef.id,
|
|
2939
|
-
meta: undefined,
|
|
2940
|
-
input: options.input
|
|
2941
|
-
}));
|
|
2942
|
-
return actorRef; // TODO: fix types
|
|
2943
2892
|
}
|
|
2944
2893
|
};
|
|
2894
|
+
return (src, options) => {
|
|
2895
|
+
const actorRef = spawn(src, options);
|
|
2896
|
+
spawnedChildren[actorRef.id] = actorRef;
|
|
2897
|
+
actorContext.defer(() => {
|
|
2898
|
+
if (actorRef.status === ActorStatus.Stopped) {
|
|
2899
|
+
return;
|
|
2900
|
+
}
|
|
2901
|
+
try {
|
|
2902
|
+
actorRef.start?.();
|
|
2903
|
+
} catch (err) {
|
|
2904
|
+
actorContext.self.send(error(actorRef.id, err));
|
|
2905
|
+
return;
|
|
2906
|
+
}
|
|
2907
|
+
});
|
|
2908
|
+
return actorRef;
|
|
2909
|
+
};
|
|
2945
2910
|
}
|
|
2946
2911
|
|
|
2947
2912
|
/**
|
|
@@ -2960,15 +2925,15 @@ function assign(assignment) {
|
|
|
2960
2925
|
action,
|
|
2961
2926
|
actorContext
|
|
2962
2927
|
}) => {
|
|
2963
|
-
const capturedActions = [];
|
|
2964
2928
|
if (!state.context) {
|
|
2965
2929
|
throw new Error('Cannot assign to undefined `context`. Ensure that `context` is defined in the machine config.');
|
|
2966
2930
|
}
|
|
2931
|
+
const spawnedChildren = {};
|
|
2967
2932
|
const args = {
|
|
2968
2933
|
context: state.context,
|
|
2969
2934
|
event,
|
|
2970
2935
|
action,
|
|
2971
|
-
spawn: createSpawner(actorContext
|
|
2936
|
+
spawn: createSpawner(actorContext, state, event, spawnedChildren),
|
|
2972
2937
|
self: actorContext?.self ?? {},
|
|
2973
2938
|
system: actorContext?.system
|
|
2974
2939
|
};
|
|
@@ -2983,12 +2948,15 @@ function assign(assignment) {
|
|
|
2983
2948
|
}
|
|
2984
2949
|
const updatedContext = Object.assign({}, state.context, partialUpdate);
|
|
2985
2950
|
return [cloneState(state, {
|
|
2986
|
-
context: updatedContext
|
|
2951
|
+
context: updatedContext,
|
|
2952
|
+
children: Object.keys(spawnedChildren).length ? {
|
|
2953
|
+
...state.children,
|
|
2954
|
+
...spawnedChildren
|
|
2955
|
+
} : state.children
|
|
2987
2956
|
}), {
|
|
2988
2957
|
type: assign$1,
|
|
2989
2958
|
params: {
|
|
2990
|
-
context: updatedContext
|
|
2991
|
-
actions: capturedActions
|
|
2959
|
+
context: updatedContext
|
|
2992
2960
|
}
|
|
2993
2961
|
}];
|
|
2994
2962
|
});
|
|
@@ -3099,9 +3067,6 @@ function pure(getActions) {
|
|
|
3099
3067
|
});
|
|
3100
3068
|
}
|
|
3101
3069
|
|
|
3102
|
-
const initEvent = {
|
|
3103
|
-
type: init
|
|
3104
|
-
};
|
|
3105
3070
|
function resolveActionObject(actionObject, actionFunctionMap) {
|
|
3106
3071
|
if (isDynamicAction(actionObject)) {
|
|
3107
3072
|
return actionObject;
|
|
@@ -3246,4 +3211,4 @@ function createInitEvent(input) {
|
|
|
3246
3211
|
};
|
|
3247
3212
|
}
|
|
3248
3213
|
|
|
3249
|
-
export {
|
|
3214
|
+
export { fromPromise as $, assign as A, microstep as B, isAtomicStateNode as C, error as D, isStateId as E, getStateNodeByPath as F, getPersistedState as G, resolveReferencedActor as H, interpret as I, createInitEvent as J, matchesState as K, sendTo as L, sendParent as M, NULL_EVENT as N, forwardTo as O, Interpreter as P, ActorStatus as Q, doneInvoke as R, STATE_DELIMITER as S, cancel as T, choose as U, log as V, pure as W, raise as X, stop as Y, pathToStateValue as Z, toObserver as _, toArray as a, fromObservable as a0, fromCallback as a1, fromEventObservable as a2, fromTransition as a3, stateIn as a4, not as a5, and as a6, or as a7, ActionTypes as a8, SpecialTargets as a9, startSignalType as aa, stopSignalType as ab, startSignal as ac, stopSignal as ad, isSignal as ae, isActorRef as af, toActorRef as ag, createEmptyActor as ah, toGuardDefinition as ai, actionTypes as aj, resolveActionObject as ak, toActionObject as al, after as am, done as an, escalate as ao, toTransitionConfigArray as b, formatTransition as c, memo as d, evaluateGuard as e, formatTransitions as f, flatten as g, createInvokeId as h, isString as i, invoke$1 as j, getDelayedTransitions as k, formatInitialTransition as l, mapValues as m, getCandidates as n, toInvokeConfig as o, getConfiguration as p, getStateNodes as q, resolveStateValue as r, isInFinalState as s, toActionObjects as t, State as u, isErrorEvent as v, macrostep as w, transitionNode as x, getInitialConfiguration as y, resolveActionsAndContext as z };
|