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