xstate 5.0.0-beta.13 → 5.0.0-beta.14
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 -1
- package/actions/dist/xstate-actions.development.cjs.js +1 -1
- 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-e4c704f3.cjs.js → actions-17c3bcfa.cjs.js} +803 -853
- package/dist/{actions-b34f6ce7.esm.js → actions-444a17c3.esm.js} +804 -854
- package/dist/{actions-c8b9504d.development.esm.js → actions-60622c0c.development.esm.js} +829 -879
- package/dist/{actions-d9c19f35.development.cjs.js → actions-73b8d456.development.cjs.js} +828 -878
- package/dist/declarations/src/State.d.ts +2 -8
- package/dist/declarations/src/StateMachine.d.ts +5 -10
- package/dist/declarations/src/StateNode.d.ts +1 -1
- package/dist/declarations/src/actors/callback.d.ts +2 -2
- package/dist/declarations/src/actors/index.d.ts +1 -1
- package/dist/declarations/src/actors/observable.d.ts +8 -6
- package/dist/declarations/src/actors/promise.d.ts +4 -3
- package/dist/declarations/src/actors/transition.d.ts +4 -4
- package/dist/declarations/src/interpreter.d.ts +15 -15
- package/dist/declarations/src/stateUtils.d.ts +4 -6
- package/dist/declarations/src/types.d.ts +23 -33
- package/dist/declarations/src/utils.d.ts +7 -7
- package/dist/xstate.cjs.js +13 -37
- package/dist/xstate.development.cjs.js +13 -37
- package/dist/xstate.development.esm.js +14 -38
- package/dist/xstate.esm.js +14 -38
- 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
|
@@ -12,10 +12,10 @@ var dev_dist_xstateDev = require('../dev/dist/xstate-dev.development.cjs.js');
|
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
14
|
// TODO: do not accept machines without all implementations
|
|
15
|
-
// we should also accept a raw machine as
|
|
16
|
-
// or just make machine
|
|
15
|
+
// we should also accept a raw machine as actor logic here
|
|
16
|
+
// or just make machine actor logic
|
|
17
17
|
|
|
18
|
-
// TODO: narrow this to
|
|
18
|
+
// TODO: narrow this to logic from machine
|
|
19
19
|
|
|
20
20
|
// TODO: fix last param
|
|
21
21
|
|
|
@@ -479,6 +479,59 @@ function sendTo(actor, event, options) {
|
|
|
479
479
|
});
|
|
480
480
|
}
|
|
481
481
|
|
|
482
|
+
const cache = new WeakMap();
|
|
483
|
+
function memo(object, key, fn) {
|
|
484
|
+
let memoizedData = cache.get(object);
|
|
485
|
+
if (!memoizedData) {
|
|
486
|
+
memoizedData = {
|
|
487
|
+
[key]: fn()
|
|
488
|
+
};
|
|
489
|
+
cache.set(object, memoizedData);
|
|
490
|
+
} else if (!(key in memoizedData)) {
|
|
491
|
+
memoizedData[key] = fn();
|
|
492
|
+
}
|
|
493
|
+
return memoizedData[key];
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
/**
|
|
497
|
+
* Cancels an in-flight `send(...)` action. A canceled sent action will not
|
|
498
|
+
* be executed, nor will its event be sent, unless it has already been sent
|
|
499
|
+
* (e.g., if `cancel(...)` is called after the `send(...)` action's `delay`).
|
|
500
|
+
*
|
|
501
|
+
* @param sendId The `id` of the `send(...)` action to cancel.
|
|
502
|
+
*/
|
|
503
|
+
|
|
504
|
+
function cancel(sendId) {
|
|
505
|
+
return createDynamicAction({
|
|
506
|
+
type: cancel$1,
|
|
507
|
+
params: {
|
|
508
|
+
sendId
|
|
509
|
+
}
|
|
510
|
+
}, (event, {
|
|
511
|
+
state,
|
|
512
|
+
actorContext
|
|
513
|
+
}) => {
|
|
514
|
+
const resolvedSendId = isFunction(sendId) ? sendId({
|
|
515
|
+
context: state.context,
|
|
516
|
+
event,
|
|
517
|
+
self: actorContext?.self ?? {},
|
|
518
|
+
system: actorContext?.system
|
|
519
|
+
}) : sendId;
|
|
520
|
+
return [state, {
|
|
521
|
+
type: 'xstate.cancel',
|
|
522
|
+
params: {
|
|
523
|
+
sendId: resolvedSendId
|
|
524
|
+
},
|
|
525
|
+
execute: actorCtx => {
|
|
526
|
+
const interpreter = actorCtx.self;
|
|
527
|
+
interpreter.cancel(resolvedSendId);
|
|
528
|
+
}
|
|
529
|
+
}];
|
|
530
|
+
});
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
const symbolObservable = (() => typeof Symbol === 'function' && Symbol.observable || '@@observable')();
|
|
534
|
+
|
|
482
535
|
class Mailbox {
|
|
483
536
|
constructor(_process) {
|
|
484
537
|
this._process = _process;
|
|
@@ -546,537 +599,92 @@ class Mailbox {
|
|
|
546
599
|
}
|
|
547
600
|
}
|
|
548
601
|
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
*/
|
|
560
|
-
function fromTransition(transition, initialState) {
|
|
561
|
-
const behavior = {
|
|
562
|
-
config: transition,
|
|
563
|
-
transition: (state, event, actorContext) => {
|
|
564
|
-
return transition(state, event, actorContext);
|
|
565
|
-
},
|
|
566
|
-
getInitialState: (_, input) => {
|
|
567
|
-
return typeof initialState === 'function' ? initialState({
|
|
568
|
-
input
|
|
569
|
-
}) : initialState;
|
|
570
|
-
},
|
|
571
|
-
getSnapshot: state => state,
|
|
572
|
-
getPersistedState: state => state,
|
|
573
|
-
restoreState: state => state
|
|
574
|
-
};
|
|
575
|
-
return behavior;
|
|
576
|
-
}
|
|
577
|
-
|
|
578
|
-
function fromPromise(
|
|
579
|
-
// TODO: add types
|
|
580
|
-
promiseCreator) {
|
|
581
|
-
const resolveEventType = '$$xstate.resolve';
|
|
582
|
-
const rejectEventType = '$$xstate.reject';
|
|
583
|
-
|
|
584
|
-
// TODO: add event types
|
|
585
|
-
const behavior = {
|
|
586
|
-
config: promiseCreator,
|
|
587
|
-
transition: (state, event) => {
|
|
588
|
-
if (state.status !== 'active') {
|
|
589
|
-
return state;
|
|
590
|
-
}
|
|
591
|
-
switch (event.type) {
|
|
592
|
-
case resolveEventType:
|
|
593
|
-
return {
|
|
594
|
-
...state,
|
|
595
|
-
status: 'done',
|
|
596
|
-
data: event.data,
|
|
597
|
-
input: undefined
|
|
598
|
-
};
|
|
599
|
-
case rejectEventType:
|
|
600
|
-
return {
|
|
601
|
-
...state,
|
|
602
|
-
status: 'error',
|
|
603
|
-
data: event.data,
|
|
604
|
-
input: undefined
|
|
605
|
-
};
|
|
606
|
-
case stopSignalType:
|
|
607
|
-
return {
|
|
608
|
-
...state,
|
|
609
|
-
status: 'canceled',
|
|
610
|
-
input: undefined
|
|
611
|
-
};
|
|
612
|
-
default:
|
|
613
|
-
return state;
|
|
614
|
-
}
|
|
602
|
+
function createSystem() {
|
|
603
|
+
let sessionIdCounter = 0;
|
|
604
|
+
const children = new Map();
|
|
605
|
+
const keyedActors = new Map();
|
|
606
|
+
const reverseKeyedActors = new WeakMap();
|
|
607
|
+
const system = {
|
|
608
|
+
_bookId: () => `x:${sessionIdCounter++}`,
|
|
609
|
+
_register: (sessionId, actorRef) => {
|
|
610
|
+
children.set(sessionId, actorRef);
|
|
611
|
+
return sessionId;
|
|
615
612
|
},
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
return;
|
|
613
|
+
_unregister: actorRef => {
|
|
614
|
+
children.delete(actorRef.sessionId);
|
|
615
|
+
const systemId = reverseKeyedActors.get(actorRef);
|
|
616
|
+
if (systemId !== undefined) {
|
|
617
|
+
keyedActors.delete(systemId);
|
|
618
|
+
reverseKeyedActors.delete(actorRef);
|
|
623
619
|
}
|
|
624
|
-
const resolvedPromise = Promise.resolve(promiseCreator({
|
|
625
|
-
input: state.input
|
|
626
|
-
}));
|
|
627
|
-
resolvedPromise.then(response => {
|
|
628
|
-
// TODO: remove this condition once dead letter queue lands
|
|
629
|
-
if (self._state.status !== 'active') {
|
|
630
|
-
return;
|
|
631
|
-
}
|
|
632
|
-
self.send({
|
|
633
|
-
type: resolveEventType,
|
|
634
|
-
data: response
|
|
635
|
-
});
|
|
636
|
-
}, errorData => {
|
|
637
|
-
// TODO: remove this condition once dead letter queue lands
|
|
638
|
-
if (self._state.status !== 'active') {
|
|
639
|
-
return;
|
|
640
|
-
}
|
|
641
|
-
self.send({
|
|
642
|
-
type: rejectEventType,
|
|
643
|
-
data: errorData
|
|
644
|
-
});
|
|
645
|
-
});
|
|
646
620
|
},
|
|
647
|
-
|
|
648
|
-
return
|
|
649
|
-
status: 'active',
|
|
650
|
-
data: undefined,
|
|
651
|
-
input
|
|
652
|
-
};
|
|
621
|
+
get: systemId => {
|
|
622
|
+
return keyedActors.get(systemId);
|
|
653
623
|
},
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
624
|
+
_set: (systemId, actorRef) => {
|
|
625
|
+
const existing = keyedActors.get(systemId);
|
|
626
|
+
if (existing && existing !== actorRef) {
|
|
627
|
+
throw new Error(`Actor with system ID '${systemId}' already exists.`);
|
|
628
|
+
}
|
|
629
|
+
keyedActors.set(systemId, actorRef);
|
|
630
|
+
reverseKeyedActors.set(actorRef, systemId);
|
|
631
|
+
}
|
|
658
632
|
};
|
|
659
|
-
return
|
|
633
|
+
return system;
|
|
660
634
|
}
|
|
661
635
|
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
id,
|
|
674
|
-
defer
|
|
675
|
-
}) => {
|
|
676
|
-
if (state.status !== 'active') {
|
|
677
|
-
return state;
|
|
678
|
-
}
|
|
679
|
-
switch (event.type) {
|
|
680
|
-
case nextEventType:
|
|
681
|
-
// match the exact timing of events sent by machines
|
|
682
|
-
// send actions are not executed immediately
|
|
683
|
-
defer(() => {
|
|
684
|
-
self._parent?.send({
|
|
685
|
-
type: `xstate.snapshot.${id}`,
|
|
686
|
-
data: event.data
|
|
687
|
-
});
|
|
688
|
-
});
|
|
689
|
-
return {
|
|
690
|
-
...state,
|
|
691
|
-
data: event.data
|
|
692
|
-
};
|
|
693
|
-
case errorEventType:
|
|
694
|
-
return {
|
|
695
|
-
...state,
|
|
696
|
-
status: 'error',
|
|
697
|
-
input: undefined,
|
|
698
|
-
data: event.data,
|
|
699
|
-
subscription: undefined
|
|
700
|
-
};
|
|
701
|
-
case completeEventType:
|
|
702
|
-
return {
|
|
703
|
-
...state,
|
|
704
|
-
status: 'done',
|
|
705
|
-
input: undefined,
|
|
706
|
-
subscription: undefined
|
|
707
|
-
};
|
|
708
|
-
case stopSignalType:
|
|
709
|
-
state.subscription.unsubscribe();
|
|
710
|
-
return {
|
|
711
|
-
...state,
|
|
712
|
-
status: 'canceled',
|
|
713
|
-
input: undefined,
|
|
714
|
-
subscription: undefined
|
|
715
|
-
};
|
|
716
|
-
default:
|
|
717
|
-
return state;
|
|
718
|
-
}
|
|
719
|
-
},
|
|
720
|
-
getInitialState: (_, input) => {
|
|
721
|
-
return {
|
|
722
|
-
subscription: undefined,
|
|
723
|
-
status: 'active',
|
|
724
|
-
data: undefined,
|
|
725
|
-
input
|
|
726
|
-
};
|
|
636
|
+
let ActorStatus = /*#__PURE__*/function (ActorStatus) {
|
|
637
|
+
ActorStatus[ActorStatus["NotStarted"] = 0] = "NotStarted";
|
|
638
|
+
ActorStatus[ActorStatus["Running"] = 1] = "Running";
|
|
639
|
+
ActorStatus[ActorStatus["Stopped"] = 2] = "Stopped";
|
|
640
|
+
return ActorStatus;
|
|
641
|
+
}({});
|
|
642
|
+
const defaultOptions = {
|
|
643
|
+
deferEvents: true,
|
|
644
|
+
clock: {
|
|
645
|
+
setTimeout: (fn, ms) => {
|
|
646
|
+
return setTimeout(fn, ms);
|
|
727
647
|
},
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
}
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
self.send({
|
|
740
|
-
type: nextEventType,
|
|
741
|
-
data: value
|
|
742
|
-
});
|
|
743
|
-
},
|
|
744
|
-
error: err => {
|
|
745
|
-
self.send({
|
|
746
|
-
type: errorEventType,
|
|
747
|
-
data: err
|
|
748
|
-
});
|
|
749
|
-
},
|
|
750
|
-
complete: () => {
|
|
751
|
-
self.send({
|
|
752
|
-
type: completeEventType
|
|
753
|
-
});
|
|
754
|
-
}
|
|
755
|
-
});
|
|
756
|
-
},
|
|
757
|
-
getSnapshot: state => state.data,
|
|
758
|
-
getPersistedState: ({
|
|
759
|
-
status,
|
|
760
|
-
data,
|
|
761
|
-
input
|
|
762
|
-
}) => ({
|
|
763
|
-
status,
|
|
764
|
-
data,
|
|
765
|
-
input
|
|
766
|
-
}),
|
|
767
|
-
getStatus: state => state,
|
|
768
|
-
restoreState: state => ({
|
|
769
|
-
...state,
|
|
770
|
-
subscription: undefined
|
|
771
|
-
})
|
|
772
|
-
};
|
|
773
|
-
return behavior;
|
|
774
|
-
}
|
|
648
|
+
clearTimeout: id => {
|
|
649
|
+
return clearTimeout(id);
|
|
650
|
+
}
|
|
651
|
+
},
|
|
652
|
+
logger: console.log.bind(console),
|
|
653
|
+
devTools: false
|
|
654
|
+
};
|
|
655
|
+
class Interpreter {
|
|
656
|
+
/**
|
|
657
|
+
* The current state of the interpreted logic.
|
|
658
|
+
*/
|
|
775
659
|
|
|
776
|
-
/**
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
*
|
|
780
|
-
*
|
|
781
|
-
* @param lazyObservable A function that creates an observable
|
|
782
|
-
* @returns An event observable behavior
|
|
783
|
-
*/
|
|
660
|
+
/**
|
|
661
|
+
* The clock that is responsible for setting and clearing timeouts, such as delayed events and transitions.
|
|
662
|
+
*/
|
|
784
663
|
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
664
|
+
/**
|
|
665
|
+
* The unique identifier for this actor relative to its parent.
|
|
666
|
+
*/
|
|
788
667
|
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
transition: (state, event) => {
|
|
793
|
-
if (state.status !== 'active') {
|
|
794
|
-
return state;
|
|
795
|
-
}
|
|
796
|
-
switch (event.type) {
|
|
797
|
-
case errorEventType:
|
|
798
|
-
return {
|
|
799
|
-
...state,
|
|
800
|
-
status: 'error',
|
|
801
|
-
input: undefined,
|
|
802
|
-
data: event.data,
|
|
803
|
-
subscription: undefined
|
|
804
|
-
};
|
|
805
|
-
case completeEventType:
|
|
806
|
-
return {
|
|
807
|
-
...state,
|
|
808
|
-
status: 'done',
|
|
809
|
-
input: undefined,
|
|
810
|
-
subscription: undefined
|
|
811
|
-
};
|
|
812
|
-
case stopSignalType:
|
|
813
|
-
state.subscription.unsubscribe();
|
|
814
|
-
return {
|
|
815
|
-
...state,
|
|
816
|
-
status: 'canceled',
|
|
817
|
-
input: undefined,
|
|
818
|
-
subscription: undefined
|
|
819
|
-
};
|
|
820
|
-
default:
|
|
821
|
-
return state;
|
|
822
|
-
}
|
|
823
|
-
},
|
|
824
|
-
getInitialState: (_, input) => {
|
|
825
|
-
return {
|
|
826
|
-
subscription: undefined,
|
|
827
|
-
status: 'active',
|
|
828
|
-
data: undefined,
|
|
829
|
-
input
|
|
830
|
-
};
|
|
831
|
-
},
|
|
832
|
-
start: (state, {
|
|
833
|
-
self
|
|
834
|
-
}) => {
|
|
835
|
-
if (state.status === 'done') {
|
|
836
|
-
// Do not restart a completed observable
|
|
837
|
-
return;
|
|
838
|
-
}
|
|
839
|
-
state.subscription = lazyObservable({
|
|
840
|
-
input: state.input
|
|
841
|
-
}).subscribe({
|
|
842
|
-
next: value => {
|
|
843
|
-
self._parent?.send(value);
|
|
844
|
-
},
|
|
845
|
-
error: err => {
|
|
846
|
-
self.send({
|
|
847
|
-
type: errorEventType,
|
|
848
|
-
data: err
|
|
849
|
-
});
|
|
850
|
-
},
|
|
851
|
-
complete: () => {
|
|
852
|
-
self.send({
|
|
853
|
-
type: completeEventType
|
|
854
|
-
});
|
|
855
|
-
}
|
|
856
|
-
});
|
|
857
|
-
},
|
|
858
|
-
getSnapshot: _ => undefined,
|
|
859
|
-
getPersistedState: ({
|
|
860
|
-
status,
|
|
861
|
-
data,
|
|
862
|
-
input
|
|
863
|
-
}) => ({
|
|
864
|
-
status,
|
|
865
|
-
data,
|
|
866
|
-
input
|
|
867
|
-
}),
|
|
868
|
-
getStatus: state => state,
|
|
869
|
-
restoreState: state => ({
|
|
870
|
-
...state,
|
|
871
|
-
subscription: undefined
|
|
872
|
-
})
|
|
873
|
-
};
|
|
874
|
-
return behavior;
|
|
875
|
-
}
|
|
668
|
+
/**
|
|
669
|
+
* Whether the service is started.
|
|
670
|
+
*/
|
|
876
671
|
|
|
877
|
-
|
|
878
|
-
const behavior = {
|
|
879
|
-
config: invokeCallback,
|
|
880
|
-
start: (_state, {
|
|
881
|
-
self
|
|
882
|
-
}) => {
|
|
883
|
-
self.send({
|
|
884
|
-
type: startSignalType
|
|
885
|
-
});
|
|
886
|
-
},
|
|
887
|
-
transition: (state, event, {
|
|
888
|
-
self,
|
|
889
|
-
id
|
|
890
|
-
}) => {
|
|
891
|
-
if (event.type === startSignalType) {
|
|
892
|
-
const sender = eventForParent => {
|
|
893
|
-
if (state.canceled) {
|
|
894
|
-
return;
|
|
895
|
-
}
|
|
896
|
-
self._parent?.send(eventForParent);
|
|
897
|
-
};
|
|
898
|
-
const receiver = newListener => {
|
|
899
|
-
state.receivers.add(newListener);
|
|
900
|
-
};
|
|
901
|
-
state.dispose = invokeCallback(sender, receiver, {
|
|
902
|
-
input: state.input
|
|
903
|
-
});
|
|
904
|
-
if (isPromiseLike(state.dispose)) {
|
|
905
|
-
state.dispose.then(resolved => {
|
|
906
|
-
self._parent?.send(doneInvoke(id, resolved));
|
|
907
|
-
state.canceled = true;
|
|
908
|
-
}, errorData => {
|
|
909
|
-
state.canceled = true;
|
|
910
|
-
self._parent?.send(error(id, errorData));
|
|
911
|
-
});
|
|
912
|
-
}
|
|
913
|
-
return state;
|
|
914
|
-
}
|
|
915
|
-
if (event.type === stopSignalType) {
|
|
916
|
-
state.canceled = true;
|
|
917
|
-
if (isFunction(state.dispose)) {
|
|
918
|
-
state.dispose();
|
|
919
|
-
}
|
|
920
|
-
return state;
|
|
921
|
-
}
|
|
922
|
-
if (isSignal(event.type)) {
|
|
923
|
-
// TODO: unrecognized signal
|
|
924
|
-
return state;
|
|
925
|
-
}
|
|
926
|
-
if (!isSignal(event.type)) {
|
|
927
|
-
state.receivers.forEach(receiver => receiver(event));
|
|
928
|
-
}
|
|
929
|
-
return state;
|
|
930
|
-
},
|
|
931
|
-
getInitialState: (_, input) => {
|
|
932
|
-
return {
|
|
933
|
-
canceled: false,
|
|
934
|
-
receivers: new Set(),
|
|
935
|
-
dispose: undefined,
|
|
936
|
-
input
|
|
937
|
-
};
|
|
938
|
-
},
|
|
939
|
-
getSnapshot: () => undefined,
|
|
940
|
-
getPersistedState: ({
|
|
941
|
-
input
|
|
942
|
-
}) => input
|
|
943
|
-
};
|
|
944
|
-
return behavior;
|
|
945
|
-
}
|
|
672
|
+
// Actor Ref
|
|
946
673
|
|
|
947
|
-
|
|
948
|
-
const stopSignalType = 'xstate.stop';
|
|
949
|
-
const startSignal = {
|
|
950
|
-
type: 'xstate.init'
|
|
951
|
-
};
|
|
952
|
-
const stopSignal = {
|
|
953
|
-
type: 'xstate.stop'
|
|
954
|
-
};
|
|
955
|
-
/**
|
|
956
|
-
* An object that expresses the behavior of an actor in reaction to received events,
|
|
957
|
-
* as well as an optionally emitted stream of values.
|
|
958
|
-
*
|
|
959
|
-
* @template TReceived The received event
|
|
960
|
-
* @template TSnapshot The emitted value
|
|
961
|
-
*/
|
|
674
|
+
// TODO: add typings for system
|
|
962
675
|
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
function isActorRef(item) {
|
|
967
|
-
return !!item && typeof item === 'object' && typeof item.send === 'function';
|
|
968
|
-
}
|
|
969
|
-
|
|
970
|
-
// TODO: refactor the return type, this could be written in a better way
|
|
971
|
-
// but it's best to avoid unneccessary breaking changes now
|
|
972
|
-
// @deprecated use `interpret(behavior)` instead
|
|
973
|
-
function toActorRef(actorRefLike) {
|
|
974
|
-
return {
|
|
975
|
-
subscribe: () => ({
|
|
976
|
-
unsubscribe: () => void 0
|
|
977
|
-
}),
|
|
978
|
-
id: 'anonymous',
|
|
979
|
-
sessionId: '',
|
|
980
|
-
getSnapshot: () => undefined,
|
|
981
|
-
[symbolObservable]: function () {
|
|
982
|
-
return this;
|
|
983
|
-
},
|
|
984
|
-
status: ActorStatus.Running,
|
|
985
|
-
stop: () => void 0,
|
|
986
|
-
...actorRefLike
|
|
987
|
-
};
|
|
988
|
-
}
|
|
989
|
-
const emptyBehavior = fromTransition(_ => undefined, undefined);
|
|
990
|
-
function createEmptyActor() {
|
|
991
|
-
return interpret(emptyBehavior);
|
|
992
|
-
}
|
|
993
|
-
|
|
994
|
-
function createSystem() {
|
|
995
|
-
let sessionIdCounter = 0;
|
|
996
|
-
const children = new Map();
|
|
997
|
-
const keyedActors = new Map();
|
|
998
|
-
const reverseKeyedActors = new WeakMap();
|
|
999
|
-
const system = {
|
|
1000
|
-
_bookId: () => `x:${sessionIdCounter++}`,
|
|
1001
|
-
_register: (sessionId, actorRef) => {
|
|
1002
|
-
children.set(sessionId, actorRef);
|
|
1003
|
-
return sessionId;
|
|
1004
|
-
},
|
|
1005
|
-
_unregister: actorRef => {
|
|
1006
|
-
children.delete(actorRef.sessionId);
|
|
1007
|
-
const systemId = reverseKeyedActors.get(actorRef);
|
|
1008
|
-
if (systemId !== undefined) {
|
|
1009
|
-
keyedActors.delete(systemId);
|
|
1010
|
-
reverseKeyedActors.delete(actorRef);
|
|
1011
|
-
}
|
|
1012
|
-
},
|
|
1013
|
-
get: systemId => {
|
|
1014
|
-
return keyedActors.get(systemId);
|
|
1015
|
-
},
|
|
1016
|
-
_set: (systemId, actorRef) => {
|
|
1017
|
-
const existing = keyedActors.get(systemId);
|
|
1018
|
-
if (existing && existing !== actorRef) {
|
|
1019
|
-
throw new Error(`Actor with system ID '${systemId}' already exists.`);
|
|
1020
|
-
}
|
|
1021
|
-
keyedActors.set(systemId, actorRef);
|
|
1022
|
-
reverseKeyedActors.set(actorRef, systemId);
|
|
1023
|
-
}
|
|
1024
|
-
};
|
|
1025
|
-
return system;
|
|
1026
|
-
}
|
|
1027
|
-
|
|
1028
|
-
let ActorStatus = /*#__PURE__*/function (ActorStatus) {
|
|
1029
|
-
ActorStatus[ActorStatus["NotStarted"] = 0] = "NotStarted";
|
|
1030
|
-
ActorStatus[ActorStatus["Running"] = 1] = "Running";
|
|
1031
|
-
ActorStatus[ActorStatus["Stopped"] = 2] = "Stopped";
|
|
1032
|
-
return ActorStatus;
|
|
1033
|
-
}({});
|
|
1034
|
-
const defaultOptions = {
|
|
1035
|
-
deferEvents: true,
|
|
1036
|
-
clock: {
|
|
1037
|
-
setTimeout: (fn, ms) => {
|
|
1038
|
-
return setTimeout(fn, ms);
|
|
1039
|
-
},
|
|
1040
|
-
clearTimeout: id => {
|
|
1041
|
-
return clearTimeout(id);
|
|
1042
|
-
}
|
|
1043
|
-
},
|
|
1044
|
-
logger: console.log.bind(console),
|
|
1045
|
-
devTools: false
|
|
1046
|
-
};
|
|
1047
|
-
class Interpreter {
|
|
1048
|
-
/**
|
|
1049
|
-
* The current state of the interpreted behavior.
|
|
1050
|
-
*/
|
|
1051
|
-
|
|
1052
|
-
/**
|
|
1053
|
-
* The clock that is responsible for setting and clearing timeouts, such as delayed events and transitions.
|
|
1054
|
-
*/
|
|
1055
|
-
|
|
1056
|
-
/**
|
|
1057
|
-
* The unique identifier for this actor relative to its parent.
|
|
1058
|
-
*/
|
|
676
|
+
/**
|
|
677
|
+
* The globally unique process ID for this invocation.
|
|
678
|
+
*/
|
|
1059
679
|
|
|
1060
680
|
/**
|
|
1061
|
-
*
|
|
1062
|
-
*/
|
|
1063
|
-
|
|
1064
|
-
// Actor Ref
|
|
1065
|
-
|
|
1066
|
-
// TODO: add typings for system
|
|
1067
|
-
|
|
1068
|
-
/**
|
|
1069
|
-
* The globally unique process ID for this invocation.
|
|
1070
|
-
*/
|
|
1071
|
-
|
|
1072
|
-
/**
|
|
1073
|
-
* Creates a new Interpreter instance (i.e., service) for the given behavior with the provided options, if any.
|
|
681
|
+
* Creates a new Interpreter instance (i.e., service) for the given logic with the provided options, if any.
|
|
1074
682
|
*
|
|
1075
|
-
* @param
|
|
683
|
+
* @param logic The logic to be interpreted
|
|
1076
684
|
* @param options Interpreter options
|
|
1077
685
|
*/
|
|
1078
|
-
constructor(
|
|
1079
|
-
this.
|
|
686
|
+
constructor(logic, options) {
|
|
687
|
+
this.logic = logic;
|
|
1080
688
|
this._state = void 0;
|
|
1081
689
|
this.clock = void 0;
|
|
1082
690
|
this.options = void 0;
|
|
@@ -1143,7 +751,7 @@ class Interpreter {
|
|
|
1143
751
|
this._initState();
|
|
1144
752
|
}
|
|
1145
753
|
_initState() {
|
|
1146
|
-
this._state = this.options.state ? this.
|
|
754
|
+
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);
|
|
1147
755
|
}
|
|
1148
756
|
|
|
1149
757
|
// array of functions to defer
|
|
@@ -1156,12 +764,12 @@ class Interpreter {
|
|
|
1156
764
|
// Execute deferred effects
|
|
1157
765
|
let deferredFn;
|
|
1158
766
|
while (deferredFn = this._deferred.shift()) {
|
|
1159
|
-
deferredFn(
|
|
767
|
+
deferredFn();
|
|
1160
768
|
}
|
|
1161
769
|
for (const observer of this.observers) {
|
|
1162
770
|
observer.next?.(snapshot);
|
|
1163
771
|
}
|
|
1164
|
-
const status = this.
|
|
772
|
+
const status = this.logic.getStatus?.(state);
|
|
1165
773
|
switch (status?.status) {
|
|
1166
774
|
case 'done':
|
|
1167
775
|
this._stopProcedure();
|
|
@@ -1203,8 +811,8 @@ class Interpreter {
|
|
|
1203
811
|
this.system._set(this._systemId, this);
|
|
1204
812
|
}
|
|
1205
813
|
this.status = ActorStatus.Running;
|
|
1206
|
-
if (this.
|
|
1207
|
-
this.
|
|
814
|
+
if (this.logic.start) {
|
|
815
|
+
this.logic.start(this._state, this._actorContext);
|
|
1208
816
|
}
|
|
1209
817
|
|
|
1210
818
|
// TODO: this notifies all subscribers but usually this is redundant
|
|
@@ -1219,7 +827,7 @@ class Interpreter {
|
|
|
1219
827
|
}
|
|
1220
828
|
_process(event) {
|
|
1221
829
|
try {
|
|
1222
|
-
const nextState = this.
|
|
830
|
+
const nextState = this.logic.transition(this._state, event, this._actorContext);
|
|
1223
831
|
this.update(nextState);
|
|
1224
832
|
if (event.type === stopSignalType) {
|
|
1225
833
|
this._stopProcedure();
|
|
@@ -1311,211 +919,604 @@ class Interpreter {
|
|
|
1311
919
|
const eventString = JSON.stringify(event);
|
|
1312
920
|
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}`);
|
|
1313
921
|
}
|
|
1314
|
-
return;
|
|
1315
|
-
}
|
|
1316
|
-
if (this.status !== ActorStatus.Running && !this.options.deferEvents) {
|
|
1317
|
-
throw new Error(`Event "${event.type}" was sent to uninitialized actor "${this.id
|
|
1318
|
-
// tslint:disable-next-line:max-line-length
|
|
1319
|
-
}". Make sure .start() is called for this actor, or set { deferEvents: true } in the actor options.\nEvent: ${JSON.stringify(event)}`);
|
|
1320
|
-
}
|
|
1321
|
-
this.mailbox.enqueue(event);
|
|
1322
|
-
}
|
|
1323
|
-
|
|
1324
|
-
// TODO: make private (and figure out a way to do this within the machine)
|
|
1325
|
-
delaySend(sendAction) {
|
|
1326
|
-
this.delayedEventsMap[sendAction.params.id] = this.clock.setTimeout(() => {
|
|
1327
|
-
if ('to' in sendAction.params && sendAction.params.to) {
|
|
1328
|
-
sendAction.params.to.send(sendAction.params.event);
|
|
1329
|
-
} else {
|
|
1330
|
-
this.send(sendAction.params.event);
|
|
922
|
+
return;
|
|
923
|
+
}
|
|
924
|
+
if (this.status !== ActorStatus.Running && !this.options.deferEvents) {
|
|
925
|
+
throw new Error(`Event "${event.type}" was sent to uninitialized actor "${this.id
|
|
926
|
+
// tslint:disable-next-line:max-line-length
|
|
927
|
+
}". Make sure .start() is called for this actor, or set { deferEvents: true } in the actor options.\nEvent: ${JSON.stringify(event)}`);
|
|
928
|
+
}
|
|
929
|
+
this.mailbox.enqueue(event);
|
|
930
|
+
}
|
|
931
|
+
|
|
932
|
+
// TODO: make private (and figure out a way to do this within the machine)
|
|
933
|
+
delaySend(sendAction) {
|
|
934
|
+
this.delayedEventsMap[sendAction.params.id] = this.clock.setTimeout(() => {
|
|
935
|
+
if ('to' in sendAction.params && sendAction.params.to) {
|
|
936
|
+
sendAction.params.to.send(sendAction.params.event);
|
|
937
|
+
} else {
|
|
938
|
+
this.send(sendAction.params.event);
|
|
939
|
+
}
|
|
940
|
+
}, sendAction.params.delay);
|
|
941
|
+
}
|
|
942
|
+
|
|
943
|
+
// TODO: make private (and figure out a way to do this within the machine)
|
|
944
|
+
cancel(sendId) {
|
|
945
|
+
this.clock.clearTimeout(this.delayedEventsMap[sendId]);
|
|
946
|
+
delete this.delayedEventsMap[sendId];
|
|
947
|
+
}
|
|
948
|
+
attachDevTools() {
|
|
949
|
+
const {
|
|
950
|
+
devTools
|
|
951
|
+
} = this.options;
|
|
952
|
+
if (devTools) {
|
|
953
|
+
const resolvedDevToolsAdapter = typeof devTools === 'function' ? devTools : dev_dist_xstateDev.devToolsAdapter;
|
|
954
|
+
resolvedDevToolsAdapter(this);
|
|
955
|
+
}
|
|
956
|
+
}
|
|
957
|
+
toJSON() {
|
|
958
|
+
return {
|
|
959
|
+
id: this.id
|
|
960
|
+
};
|
|
961
|
+
}
|
|
962
|
+
getPersistedState() {
|
|
963
|
+
return this.logic.getPersistedState?.(this._state);
|
|
964
|
+
}
|
|
965
|
+
[symbolObservable]() {
|
|
966
|
+
return this;
|
|
967
|
+
}
|
|
968
|
+
getSnapshot() {
|
|
969
|
+
return this.logic.getSnapshot ? this.logic.getSnapshot(this._state) : this._state;
|
|
970
|
+
}
|
|
971
|
+
}
|
|
972
|
+
|
|
973
|
+
/**
|
|
974
|
+
* Creates a new Interpreter instance for the given machine with the provided options, if any.
|
|
975
|
+
*
|
|
976
|
+
* @param machine The machine to interpret
|
|
977
|
+
* @param options Interpreter options
|
|
978
|
+
*/
|
|
979
|
+
|
|
980
|
+
function interpret(logic, options) {
|
|
981
|
+
const interpreter = new Interpreter(logic, options);
|
|
982
|
+
return interpreter;
|
|
983
|
+
}
|
|
984
|
+
|
|
985
|
+
/**
|
|
986
|
+
* Returns actor logic from a transition function and its initial state.
|
|
987
|
+
*
|
|
988
|
+
* A transition function is a function that takes the current state and an event and returns the next state.
|
|
989
|
+
*
|
|
990
|
+
* @param transition The transition function that returns the next state given the current state and event.
|
|
991
|
+
* @param initialState The initial state of the transition function.
|
|
992
|
+
* @returns Actor logic
|
|
993
|
+
*/
|
|
994
|
+
function fromTransition(transition, initialState) {
|
|
995
|
+
const logic = {
|
|
996
|
+
config: transition,
|
|
997
|
+
transition: (state, event, actorContext) => {
|
|
998
|
+
return transition(state, event, actorContext);
|
|
999
|
+
},
|
|
1000
|
+
getInitialState: (_, input) => {
|
|
1001
|
+
return typeof initialState === 'function' ? initialState({
|
|
1002
|
+
input
|
|
1003
|
+
}) : initialState;
|
|
1004
|
+
},
|
|
1005
|
+
getSnapshot: state => state,
|
|
1006
|
+
getPersistedState: state => state,
|
|
1007
|
+
restoreState: state => state
|
|
1008
|
+
};
|
|
1009
|
+
return logic;
|
|
1010
|
+
}
|
|
1011
|
+
|
|
1012
|
+
function fromPromise(
|
|
1013
|
+
// TODO: add types
|
|
1014
|
+
promiseCreator) {
|
|
1015
|
+
const resolveEventType = '$$xstate.resolve';
|
|
1016
|
+
const rejectEventType = '$$xstate.reject';
|
|
1017
|
+
|
|
1018
|
+
// TODO: add event types
|
|
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
|
+
}
|
|
1049
|
+
},
|
|
1050
|
+
start: (state, {
|
|
1051
|
+
self,
|
|
1052
|
+
system
|
|
1053
|
+
}) => {
|
|
1054
|
+
// TODO: determine how to allow customizing this so that promises
|
|
1055
|
+
// can be restarted if necessary
|
|
1056
|
+
if (state.status !== 'active') {
|
|
1057
|
+
return;
|
|
1058
|
+
}
|
|
1059
|
+
const resolvedPromise = Promise.resolve(promiseCreator({
|
|
1060
|
+
input: state.input,
|
|
1061
|
+
system
|
|
1062
|
+
}));
|
|
1063
|
+
resolvedPromise.then(response => {
|
|
1064
|
+
// TODO: remove this condition once dead letter queue lands
|
|
1065
|
+
if (self._state.status !== 'active') {
|
|
1066
|
+
return;
|
|
1067
|
+
}
|
|
1068
|
+
self.send({
|
|
1069
|
+
type: resolveEventType,
|
|
1070
|
+
data: response
|
|
1071
|
+
});
|
|
1072
|
+
}, errorData => {
|
|
1073
|
+
// TODO: remove this condition once dead letter queue lands
|
|
1074
|
+
if (self._state.status !== 'active') {
|
|
1075
|
+
return;
|
|
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
|
+
});
|
|
1295
|
+
}
|
|
1296
|
+
});
|
|
1297
|
+
},
|
|
1298
|
+
getSnapshot: _ => undefined,
|
|
1299
|
+
getPersistedState: ({
|
|
1300
|
+
status,
|
|
1301
|
+
data,
|
|
1302
|
+
input
|
|
1303
|
+
}) => ({
|
|
1304
|
+
status,
|
|
1305
|
+
data,
|
|
1306
|
+
input
|
|
1307
|
+
}),
|
|
1308
|
+
getStatus: state => state,
|
|
1309
|
+
restoreState: state => ({
|
|
1310
|
+
...state,
|
|
1311
|
+
subscription: undefined
|
|
1312
|
+
})
|
|
1313
|
+
};
|
|
1314
|
+
return logic;
|
|
1315
|
+
}
|
|
1316
|
+
|
|
1317
|
+
function fromCallback(invokeCallback) {
|
|
1318
|
+
const logic = {
|
|
1319
|
+
config: invokeCallback,
|
|
1320
|
+
start: (_state, {
|
|
1321
|
+
self
|
|
1322
|
+
}) => {
|
|
1323
|
+
self.send({
|
|
1324
|
+
type: startSignalType
|
|
1325
|
+
});
|
|
1326
|
+
},
|
|
1327
|
+
transition: (state, event, {
|
|
1328
|
+
self,
|
|
1329
|
+
id,
|
|
1330
|
+
system
|
|
1331
|
+
}) => {
|
|
1332
|
+
if (event.type === startSignalType) {
|
|
1333
|
+
const sender = eventForParent => {
|
|
1334
|
+
if (state.canceled) {
|
|
1335
|
+
return;
|
|
1336
|
+
}
|
|
1337
|
+
self._parent?.send(eventForParent);
|
|
1338
|
+
};
|
|
1339
|
+
const receiver = newListener => {
|
|
1340
|
+
state.receivers.add(newListener);
|
|
1341
|
+
};
|
|
1342
|
+
state.dispose = invokeCallback(sender, receiver, {
|
|
1343
|
+
input: state.input,
|
|
1344
|
+
system
|
|
1345
|
+
});
|
|
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;
|
|
1356
|
+
}
|
|
1357
|
+
if (event.type === stopSignalType) {
|
|
1358
|
+
state.canceled = true;
|
|
1359
|
+
if (isFunction(state.dispose)) {
|
|
1360
|
+
state.dispose();
|
|
1361
|
+
}
|
|
1362
|
+
return state;
|
|
1331
1363
|
}
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
}
|
|
1353
|
-
}
|
|
1354
|
-
|
|
1355
|
-
return this.behavior.getPersistedState?.(this._state);
|
|
1356
|
-
}
|
|
1357
|
-
[symbolObservable]() {
|
|
1358
|
-
return this;
|
|
1359
|
-
}
|
|
1360
|
-
getSnapshot() {
|
|
1361
|
-
return this.behavior.getSnapshot ? this.behavior.getSnapshot(this._state) : this._state;
|
|
1362
|
-
}
|
|
1364
|
+
if (isSignal(event.type)) {
|
|
1365
|
+
// TODO: unrecognized signal
|
|
1366
|
+
return state;
|
|
1367
|
+
}
|
|
1368
|
+
if (!isSignal(event.type)) {
|
|
1369
|
+
state.receivers.forEach(receiver => receiver(event));
|
|
1370
|
+
}
|
|
1371
|
+
return state;
|
|
1372
|
+
},
|
|
1373
|
+
getInitialState: (_, input) => {
|
|
1374
|
+
return {
|
|
1375
|
+
canceled: false,
|
|
1376
|
+
receivers: new Set(),
|
|
1377
|
+
dispose: undefined,
|
|
1378
|
+
input
|
|
1379
|
+
};
|
|
1380
|
+
},
|
|
1381
|
+
getSnapshot: () => undefined,
|
|
1382
|
+
getPersistedState: ({
|
|
1383
|
+
input
|
|
1384
|
+
}) => input
|
|
1385
|
+
};
|
|
1386
|
+
return logic;
|
|
1363
1387
|
}
|
|
1364
1388
|
|
|
1389
|
+
const startSignalType = 'xstate.init';
|
|
1390
|
+
const stopSignalType = 'xstate.stop';
|
|
1391
|
+
const startSignal = {
|
|
1392
|
+
type: 'xstate.init'
|
|
1393
|
+
};
|
|
1394
|
+
const stopSignal = {
|
|
1395
|
+
type: 'xstate.stop'
|
|
1396
|
+
};
|
|
1365
1397
|
/**
|
|
1366
|
-
*
|
|
1398
|
+
* An object that expresses the actor logic in reaction to received events,
|
|
1399
|
+
* as well as an optionally emitted stream of values.
|
|
1367
1400
|
*
|
|
1368
|
-
* @
|
|
1369
|
-
* @
|
|
1401
|
+
* @template TReceived The received event
|
|
1402
|
+
* @template TSnapshot The emitted value
|
|
1370
1403
|
*/
|
|
1371
1404
|
|
|
1372
|
-
function
|
|
1373
|
-
|
|
1374
|
-
return interpreter;
|
|
1405
|
+
function isSignal(eventType) {
|
|
1406
|
+
return eventType === startSignalType || eventType === stopSignalType;
|
|
1375
1407
|
}
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
* Stops an actor.
|
|
1379
|
-
*
|
|
1380
|
-
* @param actorRef The actor to stop.
|
|
1381
|
-
*/
|
|
1382
|
-
|
|
1383
|
-
function stop(actorRef) {
|
|
1384
|
-
const actor = actorRef;
|
|
1385
|
-
return createDynamicAction({
|
|
1386
|
-
type: stop$1,
|
|
1387
|
-
params: {
|
|
1388
|
-
actor
|
|
1389
|
-
}
|
|
1390
|
-
}, (event, {
|
|
1391
|
-
state
|
|
1392
|
-
}) => {
|
|
1393
|
-
const actorRefOrString = isFunction(actor) ? actor({
|
|
1394
|
-
context: state.context,
|
|
1395
|
-
event
|
|
1396
|
-
}) : actor;
|
|
1397
|
-
const actorRef = typeof actorRefOrString === 'string' ? state.children[actorRefOrString] : actorRefOrString;
|
|
1398
|
-
return [state, {
|
|
1399
|
-
type: 'xstate.stop',
|
|
1400
|
-
params: {
|
|
1401
|
-
actor: actorRef
|
|
1402
|
-
},
|
|
1403
|
-
execute: actorCtx => {
|
|
1404
|
-
if (!actorRef) {
|
|
1405
|
-
return;
|
|
1406
|
-
}
|
|
1407
|
-
if (actorRef.status !== ActorStatus.Running) {
|
|
1408
|
-
actorCtx.stopChild(actorRef);
|
|
1409
|
-
return;
|
|
1410
|
-
}
|
|
1411
|
-
actorCtx.defer(() => {
|
|
1412
|
-
actorCtx.stopChild(actorRef);
|
|
1413
|
-
});
|
|
1414
|
-
}
|
|
1415
|
-
}];
|
|
1416
|
-
});
|
|
1408
|
+
function isActorRef(item) {
|
|
1409
|
+
return !!item && typeof item === 'object' && typeof item.send === 'function';
|
|
1417
1410
|
}
|
|
1418
1411
|
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1412
|
+
// TODO: refactor the return type, this could be written in a better way
|
|
1413
|
+
// but it's best to avoid unneccessary breaking changes now
|
|
1414
|
+
// @deprecated use `interpret(actorLogic)` instead
|
|
1415
|
+
function toActorRef(actorRefLike) {
|
|
1416
|
+
return {
|
|
1417
|
+
subscribe: () => ({
|
|
1418
|
+
unsubscribe: () => void 0
|
|
1419
|
+
}),
|
|
1420
|
+
id: 'anonymous',
|
|
1421
|
+
sessionId: '',
|
|
1422
|
+
getSnapshot: () => undefined,
|
|
1423
|
+
[symbolObservable]: function () {
|
|
1424
|
+
return this;
|
|
1425
|
+
},
|
|
1426
|
+
status: ActorStatus.Running,
|
|
1427
|
+
stop: () => void 0,
|
|
1428
|
+
...actorRefLike
|
|
1429
|
+
};
|
|
1430
|
+
}
|
|
1431
|
+
const emptyLogic = fromTransition(_ => undefined, undefined);
|
|
1432
|
+
function createEmptyActor() {
|
|
1433
|
+
return interpret(emptyLogic);
|
|
1434
|
+
}
|
|
1435
1435
|
|
|
1436
|
-
function
|
|
1436
|
+
function invoke(invokeDef) {
|
|
1437
1437
|
return createDynamicAction({
|
|
1438
|
-
type:
|
|
1439
|
-
params:
|
|
1440
|
-
label,
|
|
1441
|
-
expr
|
|
1442
|
-
}
|
|
1438
|
+
type: invoke$1,
|
|
1439
|
+
params: invokeDef
|
|
1443
1440
|
}, (event, {
|
|
1444
1441
|
state,
|
|
1445
1442
|
actorContext
|
|
1446
1443
|
}) => {
|
|
1447
|
-
const
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
execute: actorCtx => {
|
|
1460
|
-
if (label) {
|
|
1461
|
-
actorCtx.logger?.(label, resolvedValue);
|
|
1462
|
-
} else {
|
|
1463
|
-
actorCtx.logger?.(resolvedValue);
|
|
1444
|
+
const type = invoke$1;
|
|
1445
|
+
const {
|
|
1446
|
+
id,
|
|
1447
|
+
src
|
|
1448
|
+
} = invokeDef;
|
|
1449
|
+
let resolvedInvokeAction;
|
|
1450
|
+
if (isActorRef(src)) {
|
|
1451
|
+
resolvedInvokeAction = {
|
|
1452
|
+
type,
|
|
1453
|
+
params: {
|
|
1454
|
+
...invokeDef,
|
|
1455
|
+
ref: src
|
|
1464
1456
|
}
|
|
1465
|
-
}
|
|
1466
|
-
}
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1457
|
+
};
|
|
1458
|
+
} else {
|
|
1459
|
+
const referenced = resolveReferencedActor(state.machine.options.actors[src]);
|
|
1460
|
+
if (!referenced) {
|
|
1461
|
+
resolvedInvokeAction = {
|
|
1462
|
+
type,
|
|
1463
|
+
params: invokeDef
|
|
1464
|
+
};
|
|
1465
|
+
} else {
|
|
1466
|
+
const input = 'input' in invokeDef ? invokeDef.input : referenced.input;
|
|
1467
|
+
const ref = interpret(referenced.src, {
|
|
1468
|
+
id,
|
|
1469
|
+
src,
|
|
1470
|
+
parent: actorContext?.self,
|
|
1471
|
+
systemId: invokeDef.systemId,
|
|
1472
|
+
input: typeof input === 'function' ? input({
|
|
1473
|
+
context: state.context,
|
|
1474
|
+
event,
|
|
1475
|
+
self: actorContext?.self
|
|
1476
|
+
}) : input
|
|
1477
|
+
});
|
|
1478
|
+
resolvedInvokeAction = {
|
|
1479
|
+
type,
|
|
1480
|
+
params: {
|
|
1481
|
+
...invokeDef,
|
|
1482
|
+
ref
|
|
1483
|
+
}
|
|
1484
|
+
};
|
|
1485
|
+
}
|
|
1483
1486
|
}
|
|
1484
|
-
|
|
1485
|
-
state,
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
context: state.context,
|
|
1490
|
-
event,
|
|
1491
|
-
self: actorContext?.self ?? {},
|
|
1492
|
-
system: actorContext?.system
|
|
1493
|
-
}) : sendId;
|
|
1494
|
-
return [state, {
|
|
1495
|
-
type: 'xstate.cancel',
|
|
1496
|
-
params: {
|
|
1497
|
-
sendId: resolvedSendId
|
|
1498
|
-
},
|
|
1499
|
-
execute: actorCtx => {
|
|
1500
|
-
const interpreter = actorCtx.self;
|
|
1501
|
-
interpreter.cancel(resolvedSendId);
|
|
1487
|
+
const actorRef = resolvedInvokeAction.params.ref;
|
|
1488
|
+
const invokedState = cloneState(state, {
|
|
1489
|
+
children: {
|
|
1490
|
+
...state.children,
|
|
1491
|
+
[id]: actorRef
|
|
1502
1492
|
}
|
|
1503
|
-
}
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1493
|
+
});
|
|
1494
|
+
resolvedInvokeAction.execute = actorCtx => {
|
|
1495
|
+
const parent = actorCtx.self;
|
|
1496
|
+
const {
|
|
1497
|
+
id,
|
|
1498
|
+
ref
|
|
1499
|
+
} = resolvedInvokeAction.params;
|
|
1500
|
+
if (!ref) {
|
|
1501
|
+
{
|
|
1502
|
+
console.warn(`Actor type '${resolvedInvokeAction.params.src}' not found in machine '${actorCtx.id}'.`);
|
|
1503
|
+
}
|
|
1504
|
+
return;
|
|
1505
|
+
}
|
|
1506
|
+
actorCtx.defer(() => {
|
|
1507
|
+
if (actorRef.status === ActorStatus.Stopped) {
|
|
1508
|
+
return;
|
|
1509
|
+
}
|
|
1510
|
+
try {
|
|
1511
|
+
actorRef.start?.();
|
|
1512
|
+
} catch (err) {
|
|
1513
|
+
parent.send(error(id, err));
|
|
1514
|
+
return;
|
|
1515
|
+
}
|
|
1516
|
+
});
|
|
1513
1517
|
};
|
|
1514
|
-
|
|
1515
|
-
}
|
|
1516
|
-
memoizedData[key] = fn();
|
|
1517
|
-
}
|
|
1518
|
-
return memoizedData[key];
|
|
1518
|
+
return [invokedState, resolvedInvokeAction];
|
|
1519
|
+
});
|
|
1519
1520
|
}
|
|
1520
1521
|
|
|
1521
1522
|
function stateIn(stateValue) {
|
|
@@ -2258,15 +2259,11 @@ function microstep(transitions, currentState, actorCtx, event) {
|
|
|
2258
2259
|
const willTransition = currentState._initial || transitions.length > 0;
|
|
2259
2260
|
const mutConfiguration = new Set(currentState.configuration);
|
|
2260
2261
|
if (!currentState._initial && !willTransition) {
|
|
2261
|
-
const inertState = cloneState(currentState, {
|
|
2262
|
-
event,
|
|
2263
|
-
actions: [],
|
|
2264
|
-
transitions: []
|
|
2265
|
-
});
|
|
2262
|
+
const inertState = cloneState(currentState, {});
|
|
2266
2263
|
inertState.changed = false;
|
|
2267
2264
|
return inertState;
|
|
2268
2265
|
}
|
|
2269
|
-
const microstate = microstepProcedure(currentState._initial ? [{
|
|
2266
|
+
const [microstate, actions] = microstepProcedure(currentState._initial ? [{
|
|
2270
2267
|
target: [...currentState.configuration].filter(isAtomicStateNode),
|
|
2271
2268
|
source: machine.root,
|
|
2272
2269
|
reenter: true,
|
|
@@ -2275,38 +2272,16 @@ function microstep(transitions, currentState, actorCtx, event) {
|
|
|
2275
2272
|
toJSON: null // TODO: fix
|
|
2276
2273
|
}] : transitions, currentState, mutConfiguration, event, actorCtx);
|
|
2277
2274
|
const {
|
|
2278
|
-
context
|
|
2279
|
-
actions: nonRaisedActions
|
|
2275
|
+
context
|
|
2280
2276
|
} = microstate;
|
|
2281
|
-
const children = setChildren(currentState, nonRaisedActions);
|
|
2282
2277
|
const nextState = cloneState(microstate, {
|
|
2283
2278
|
value: {},
|
|
2284
2279
|
// TODO: make optional
|
|
2285
|
-
transitions
|
|
2286
|
-
children
|
|
2280
|
+
transitions
|
|
2287
2281
|
});
|
|
2288
|
-
nextState.changed = currentState._initial ? undefined : !stateValuesEqual(nextState.value, currentState.value) ||
|
|
2282
|
+
nextState.changed = currentState._initial ? undefined : !stateValuesEqual(nextState.value, currentState.value) || actions.length > 0 || context !== currentState.context;
|
|
2289
2283
|
return nextState;
|
|
2290
2284
|
}
|
|
2291
|
-
function setChildren(currentState, nonRaisedActions) {
|
|
2292
|
-
const children = {
|
|
2293
|
-
...currentState.children
|
|
2294
|
-
};
|
|
2295
|
-
for (const action of nonRaisedActions) {
|
|
2296
|
-
if (action.type === invoke$1 && action.params.ref) {
|
|
2297
|
-
const ref = action.params.ref;
|
|
2298
|
-
if (ref) {
|
|
2299
|
-
children[ref.id] = ref;
|
|
2300
|
-
}
|
|
2301
|
-
} else if (action.type === stop$1) {
|
|
2302
|
-
const ref = action.params.actor;
|
|
2303
|
-
if (ref) {
|
|
2304
|
-
delete children[ref.id];
|
|
2305
|
-
}
|
|
2306
|
-
}
|
|
2307
|
-
}
|
|
2308
|
-
return children;
|
|
2309
|
-
}
|
|
2310
2285
|
function microstepProcedure(transitions, currentState, mutConfiguration, event, actorCtx) {
|
|
2311
2286
|
const actions = [];
|
|
2312
2287
|
const historyValue = {
|
|
@@ -2324,7 +2299,7 @@ function microstepProcedure(transitions, currentState, mutConfiguration, event,
|
|
|
2324
2299
|
actions.push(...filteredTransitions.flatMap(t => t.actions));
|
|
2325
2300
|
|
|
2326
2301
|
// Enter states
|
|
2327
|
-
enterStates(filteredTransitions, mutConfiguration, actions, internalQueue, currentState, historyValue);
|
|
2302
|
+
enterStates(event, filteredTransitions, mutConfiguration, actions, internalQueue, currentState, historyValue);
|
|
2328
2303
|
const nextConfiguration = [...mutConfiguration];
|
|
2329
2304
|
const done = isInFinalState(nextConfiguration);
|
|
2330
2305
|
if (done) {
|
|
@@ -2332,29 +2307,25 @@ function microstepProcedure(transitions, currentState, mutConfiguration, event,
|
|
|
2332
2307
|
actions.push(...finalActions);
|
|
2333
2308
|
}
|
|
2334
2309
|
try {
|
|
2335
|
-
const
|
|
2336
|
-
nextState
|
|
2337
|
-
} = resolveActionsAndContext(actions, event, currentState, actorCtx);
|
|
2310
|
+
const [nextState, resolvedActions] = resolveActionsAndContext(actions, event, currentState, actorCtx);
|
|
2338
2311
|
const output = done ? getOutput(nextConfiguration, nextState.context, event) : undefined;
|
|
2339
2312
|
internalQueue.push(...nextState._internalQueue);
|
|
2340
|
-
return cloneState(currentState, {
|
|
2341
|
-
actions: nextState.actions,
|
|
2313
|
+
return [cloneState(currentState, {
|
|
2342
2314
|
configuration: nextConfiguration,
|
|
2343
2315
|
historyValue,
|
|
2344
2316
|
_internalQueue: internalQueue,
|
|
2345
2317
|
context: nextState.context,
|
|
2346
|
-
event,
|
|
2347
2318
|
done,
|
|
2348
2319
|
output,
|
|
2349
2320
|
children: nextState.children
|
|
2350
|
-
});
|
|
2321
|
+
}), resolvedActions];
|
|
2351
2322
|
} catch (e) {
|
|
2352
2323
|
// TODO: Refactor this once proper error handling is implemented.
|
|
2353
2324
|
// See https://github.com/statelyai/rfcs/pull/4
|
|
2354
2325
|
throw e;
|
|
2355
2326
|
}
|
|
2356
2327
|
}
|
|
2357
|
-
function enterStates(filteredTransitions, mutConfiguration, actions, internalQueue, currentState, historyValue) {
|
|
2328
|
+
function enterStates(event, filteredTransitions, mutConfiguration, actions, internalQueue, currentState, historyValue) {
|
|
2358
2329
|
const statesToEnter = new Set();
|
|
2359
2330
|
const statesForDefaultEntry = new Set();
|
|
2360
2331
|
computeEntrySet(filteredTransitions, historyValue, statesForDefaultEntry, statesToEnter);
|
|
@@ -2382,7 +2353,7 @@ function enterStates(filteredTransitions, mutConfiguration, actions, internalQue
|
|
|
2382
2353
|
if (!parent.parent) {
|
|
2383
2354
|
continue;
|
|
2384
2355
|
}
|
|
2385
|
-
internalQueue.push(done(parent.id, stateNodeToEnter.output ? mapContext(stateNodeToEnter.output, currentState.context,
|
|
2356
|
+
internalQueue.push(done(parent.id, stateNodeToEnter.output ? mapContext(stateNodeToEnter.output, currentState.context, event) : undefined));
|
|
2386
2357
|
if (parent.parent) {
|
|
2387
2358
|
const grandparent = parent.parent;
|
|
2388
2359
|
if (grandparent.type === 'parallel') {
|
|
@@ -2500,8 +2471,8 @@ function resolveActionsAndContext(actions, event, currentState, actorCtx) {
|
|
|
2500
2471
|
resolvedActions.push(action);
|
|
2501
2472
|
if (actorCtx?.self.status === ActorStatus.Running) {
|
|
2502
2473
|
action.execute?.(actorCtx);
|
|
2503
|
-
|
|
2504
|
-
|
|
2474
|
+
} else {
|
|
2475
|
+
actorCtx?.defer(() => action.execute?.(actorCtx));
|
|
2505
2476
|
}
|
|
2506
2477
|
}
|
|
2507
2478
|
function resolveAction(actionObject) {
|
|
@@ -2530,12 +2501,9 @@ function resolveActionsAndContext(actions, event, currentState, actorCtx) {
|
|
|
2530
2501
|
for (const actionObject of actions) {
|
|
2531
2502
|
resolveAction(actionObject);
|
|
2532
2503
|
}
|
|
2533
|
-
return {
|
|
2534
|
-
|
|
2535
|
-
|
|
2536
|
-
_internalQueue: raiseActions.map(a => a.params.event)
|
|
2537
|
-
})
|
|
2538
|
-
};
|
|
2504
|
+
return [cloneState(intermediateState, {
|
|
2505
|
+
_internalQueue: raiseActions.map(a => a.params.event)
|
|
2506
|
+
}), resolvedActions];
|
|
2539
2507
|
}
|
|
2540
2508
|
function macrostep(state, event, actorCtx) {
|
|
2541
2509
|
if (event.type === WILDCARD) {
|
|
@@ -2546,54 +2514,42 @@ function macrostep(state, event, actorCtx) {
|
|
|
2546
2514
|
|
|
2547
2515
|
// Handle stop event
|
|
2548
2516
|
if (event.type === stopSignalType) {
|
|
2549
|
-
nextState = stopStep(event, nextState, actorCtx);
|
|
2517
|
+
nextState = stopStep(event, nextState, actorCtx)[0];
|
|
2550
2518
|
states.push(nextState);
|
|
2551
2519
|
return {
|
|
2552
2520
|
state: nextState,
|
|
2553
2521
|
microstates: states
|
|
2554
2522
|
};
|
|
2555
2523
|
}
|
|
2524
|
+
let nextEvent = event;
|
|
2556
2525
|
|
|
2557
2526
|
// Assume the state is at rest (no raised events)
|
|
2558
2527
|
// Determine the next state based on the next microstep
|
|
2559
|
-
if (
|
|
2560
|
-
const transitions = selectTransitions(
|
|
2561
|
-
nextState = microstep(transitions, state, actorCtx,
|
|
2528
|
+
if (nextEvent.type !== init) {
|
|
2529
|
+
const transitions = selectTransitions(nextEvent, nextState);
|
|
2530
|
+
nextState = microstep(transitions, state, actorCtx, nextEvent);
|
|
2562
2531
|
states.push(nextState);
|
|
2563
2532
|
}
|
|
2564
2533
|
while (!nextState.done) {
|
|
2565
|
-
let enabledTransitions = selectEventlessTransitions(nextState);
|
|
2566
|
-
if (enabledTransitions.length
|
|
2567
|
-
// TODO: this is a bit of a hack, we need to review this
|
|
2568
|
-
// this matches the behavior from v4 for eventless transitions
|
|
2569
|
-
// where for `hasAlwaysTransitions` we were always trying to resolve with a NULL event
|
|
2570
|
-
// and if a transition was not selected the `state.transitions` stayed empty
|
|
2571
|
-
// without this we get into an infinite loop in the dieHard test in `@xstate/test` for the `simplePathsTo`
|
|
2572
|
-
if (nextState.configuration.some(state => state.always)) {
|
|
2573
|
-
nextState.transitions = [];
|
|
2574
|
-
}
|
|
2534
|
+
let enabledTransitions = selectEventlessTransitions(nextState, nextEvent);
|
|
2535
|
+
if (!enabledTransitions.length) {
|
|
2575
2536
|
if (!nextState._internalQueue.length) {
|
|
2576
2537
|
break;
|
|
2577
2538
|
} else {
|
|
2578
|
-
|
|
2579
|
-
const nextEvent = nextState._internalQueue[0];
|
|
2539
|
+
nextEvent = nextState._internalQueue[0];
|
|
2580
2540
|
const transitions = selectTransitions(nextEvent, nextState);
|
|
2581
2541
|
nextState = microstep(transitions, nextState, actorCtx, nextEvent);
|
|
2582
2542
|
nextState._internalQueue.shift();
|
|
2583
|
-
nextState.actions.unshift(...currentActions);
|
|
2584
2543
|
states.push(nextState);
|
|
2585
2544
|
}
|
|
2586
|
-
}
|
|
2587
|
-
|
|
2588
|
-
const currentActions = nextState.actions;
|
|
2589
|
-
nextState = microstep(enabledTransitions, nextState, actorCtx, nextState.event);
|
|
2590
|
-
nextState.actions.unshift(...currentActions);
|
|
2545
|
+
} else {
|
|
2546
|
+
nextState = microstep(enabledTransitions, nextState, actorCtx, nextEvent);
|
|
2591
2547
|
states.push(nextState);
|
|
2592
2548
|
}
|
|
2593
2549
|
}
|
|
2594
2550
|
if (nextState.done) {
|
|
2595
2551
|
// Perform the stop step to ensure that child actors are stopped
|
|
2596
|
-
stopStep(
|
|
2552
|
+
stopStep(nextEvent, nextState, actorCtx);
|
|
2597
2553
|
}
|
|
2598
2554
|
return {
|
|
2599
2555
|
state: nextState,
|
|
@@ -2608,15 +2564,12 @@ function stopStep(event, nextState, actorCtx) {
|
|
|
2608
2564
|
for (const child of Object.values(nextState.children)) {
|
|
2609
2565
|
actions.push(stop(child));
|
|
2610
2566
|
}
|
|
2611
|
-
|
|
2612
|
-
nextState: stoppedState
|
|
2613
|
-
} = resolveActionsAndContext(actions, event, nextState, actorCtx);
|
|
2614
|
-
return stoppedState;
|
|
2567
|
+
return resolveActionsAndContext(actions, event, nextState, actorCtx);
|
|
2615
2568
|
}
|
|
2616
2569
|
function selectTransitions(event, nextState) {
|
|
2617
2570
|
return nextState.machine.getTransitionData(nextState, event);
|
|
2618
2571
|
}
|
|
2619
|
-
function selectEventlessTransitions(nextState) {
|
|
2572
|
+
function selectEventlessTransitions(nextState, event) {
|
|
2620
2573
|
const enabledTransitionSet = new Set();
|
|
2621
2574
|
const atomicStates = nextState.configuration.filter(isAtomicStateNode);
|
|
2622
2575
|
for (const stateNode of atomicStates) {
|
|
@@ -2625,7 +2578,7 @@ function selectEventlessTransitions(nextState) {
|
|
|
2625
2578
|
continue;
|
|
2626
2579
|
}
|
|
2627
2580
|
for (const transition of s.always) {
|
|
2628
|
-
if (transition.guard === undefined || evaluateGuard(transition.guard, nextState.context,
|
|
2581
|
+
if (transition.guard === undefined || evaluateGuard(transition.guard, nextState.context, event, nextState)) {
|
|
2629
2582
|
enabledTransitionSet.add(transition);
|
|
2630
2583
|
break loop;
|
|
2631
2584
|
}
|
|
@@ -2693,10 +2646,6 @@ class State {
|
|
|
2693
2646
|
* The enabled state nodes representative of the state value.
|
|
2694
2647
|
*/
|
|
2695
2648
|
|
|
2696
|
-
/**
|
|
2697
|
-
* The transition definitions that resulted in this state.
|
|
2698
|
-
*/
|
|
2699
|
-
|
|
2700
2649
|
/**
|
|
2701
2650
|
* An object mapping actor names to spawned/invoked actors.
|
|
2702
2651
|
*/
|
|
@@ -2712,8 +2661,6 @@ class State {
|
|
|
2712
2661
|
return new State({
|
|
2713
2662
|
value: stateValue.value,
|
|
2714
2663
|
context,
|
|
2715
|
-
event: stateValue.event,
|
|
2716
|
-
actions: [],
|
|
2717
2664
|
meta: {},
|
|
2718
2665
|
configuration: [],
|
|
2719
2666
|
// TODO: fix,
|
|
@@ -2723,17 +2670,12 @@ class State {
|
|
|
2723
2670
|
}
|
|
2724
2671
|
return stateValue;
|
|
2725
2672
|
}
|
|
2726
|
-
const event = createInitEvent({}); // TODO: fix
|
|
2727
|
-
|
|
2728
2673
|
const configuration = getConfiguration(getStateNodes(machine.root, stateValue));
|
|
2729
2674
|
return new State({
|
|
2730
2675
|
value: stateValue,
|
|
2731
2676
|
context,
|
|
2732
|
-
event,
|
|
2733
|
-
actions: [],
|
|
2734
2677
|
meta: undefined,
|
|
2735
2678
|
configuration: Array.from(configuration),
|
|
2736
|
-
transitions: [],
|
|
2737
2679
|
children: {}
|
|
2738
2680
|
}, machine);
|
|
2739
2681
|
}
|
|
@@ -2751,23 +2693,17 @@ class State {
|
|
|
2751
2693
|
this.output = void 0;
|
|
2752
2694
|
this.context = void 0;
|
|
2753
2695
|
this.historyValue = {};
|
|
2754
|
-
this.actions = [];
|
|
2755
|
-
this.event = void 0;
|
|
2756
2696
|
this._internalQueue = void 0;
|
|
2757
2697
|
this._initial = false;
|
|
2758
2698
|
this.changed = void 0;
|
|
2759
2699
|
this.configuration = void 0;
|
|
2760
|
-
this.transitions = void 0;
|
|
2761
2700
|
this.children = void 0;
|
|
2762
2701
|
this.context = config.context;
|
|
2763
2702
|
this._internalQueue = config._internalQueue ?? [];
|
|
2764
|
-
this.event = config.event;
|
|
2765
2703
|
this.historyValue = config.historyValue || {};
|
|
2766
|
-
this.actions = config.actions ?? [];
|
|
2767
2704
|
this.matches = this.matches.bind(this);
|
|
2768
2705
|
this.toStrings = this.toStrings.bind(this);
|
|
2769
2706
|
this.configuration = config.configuration ?? Array.from(getConfiguration(getStateNodes(machine.root, config.value)));
|
|
2770
|
-
this.transitions = config.transitions;
|
|
2771
2707
|
this.children = config.children;
|
|
2772
2708
|
this.value = getStateValue(machine.root, this.configuration);
|
|
2773
2709
|
this.tags = new Set(flatten(this.configuration.map(sn => sn.tags)));
|
|
@@ -2790,7 +2726,6 @@ class State {
|
|
|
2790
2726
|
toJSON() {
|
|
2791
2727
|
const {
|
|
2792
2728
|
configuration,
|
|
2793
|
-
transitions,
|
|
2794
2729
|
tags,
|
|
2795
2730
|
machine,
|
|
2796
2731
|
...jsonValues
|
|
@@ -2862,7 +2797,6 @@ function cloneState(state, config = {}) {
|
|
|
2862
2797
|
function getPersistedState(state) {
|
|
2863
2798
|
const {
|
|
2864
2799
|
configuration,
|
|
2865
|
-
transitions,
|
|
2866
2800
|
tags,
|
|
2867
2801
|
machine,
|
|
2868
2802
|
children,
|
|
@@ -2881,89 +2815,105 @@ function getPersistedState(state) {
|
|
|
2881
2815
|
};
|
|
2882
2816
|
}
|
|
2883
2817
|
|
|
2884
|
-
|
|
2818
|
+
/**
|
|
2819
|
+
* Stops an actor.
|
|
2820
|
+
*
|
|
2821
|
+
* @param actorRef The actor to stop.
|
|
2822
|
+
*/
|
|
2823
|
+
|
|
2824
|
+
function stop(actorRef) {
|
|
2825
|
+
const actor = actorRef;
|
|
2885
2826
|
return createDynamicAction({
|
|
2886
|
-
type:
|
|
2887
|
-
params:
|
|
2827
|
+
type: stop$1,
|
|
2828
|
+
params: {
|
|
2829
|
+
actor
|
|
2830
|
+
}
|
|
2888
2831
|
}, (event, {
|
|
2889
|
-
state
|
|
2890
|
-
actorContext
|
|
2832
|
+
state
|
|
2891
2833
|
}) => {
|
|
2892
|
-
const
|
|
2893
|
-
|
|
2894
|
-
|
|
2895
|
-
|
|
2896
|
-
|
|
2897
|
-
let
|
|
2898
|
-
if (
|
|
2899
|
-
|
|
2900
|
-
|
|
2901
|
-
params: {
|
|
2902
|
-
...invokeDef,
|
|
2903
|
-
ref: src
|
|
2904
|
-
}
|
|
2834
|
+
const actorRefOrString = isFunction(actor) ? actor({
|
|
2835
|
+
context: state.context,
|
|
2836
|
+
event
|
|
2837
|
+
}) : actor;
|
|
2838
|
+
const actorRef = typeof actorRefOrString === 'string' ? state.children[actorRefOrString] : actorRefOrString;
|
|
2839
|
+
let children = state.children;
|
|
2840
|
+
if (actorRef) {
|
|
2841
|
+
children = {
|
|
2842
|
+
...children
|
|
2905
2843
|
};
|
|
2906
|
-
|
|
2907
|
-
const referenced = resolveReferencedActor(state.machine.options.actors[src]);
|
|
2908
|
-
if (!referenced) {
|
|
2909
|
-
resolvedInvokeAction = {
|
|
2910
|
-
type,
|
|
2911
|
-
params: invokeDef
|
|
2912
|
-
};
|
|
2913
|
-
} else {
|
|
2914
|
-
const input = 'input' in invokeDef ? invokeDef.input : referenced.input;
|
|
2915
|
-
const ref = interpret(referenced.src, {
|
|
2916
|
-
id,
|
|
2917
|
-
src,
|
|
2918
|
-
parent: actorContext?.self,
|
|
2919
|
-
systemId: invokeDef.systemId,
|
|
2920
|
-
input: typeof input === 'function' ? input({
|
|
2921
|
-
context: state.context,
|
|
2922
|
-
event,
|
|
2923
|
-
self: actorContext?.self
|
|
2924
|
-
}) : input
|
|
2925
|
-
});
|
|
2926
|
-
resolvedInvokeAction = {
|
|
2927
|
-
type,
|
|
2928
|
-
params: {
|
|
2929
|
-
...invokeDef,
|
|
2930
|
-
ref
|
|
2931
|
-
}
|
|
2932
|
-
};
|
|
2933
|
-
}
|
|
2844
|
+
delete children[actorRef.id];
|
|
2934
2845
|
}
|
|
2935
|
-
|
|
2936
|
-
|
|
2937
|
-
|
|
2938
|
-
|
|
2939
|
-
|
|
2940
|
-
|
|
2941
|
-
|
|
2942
|
-
|
|
2943
|
-
|
|
2944
|
-
const {
|
|
2945
|
-
id,
|
|
2946
|
-
ref
|
|
2947
|
-
} = resolvedInvokeAction.params;
|
|
2948
|
-
if (!ref) {
|
|
2949
|
-
{
|
|
2950
|
-
console.warn(`Actor type '${resolvedInvokeAction.params.src}' not found in machine '${actorCtx.id}'.`);
|
|
2951
|
-
}
|
|
2952
|
-
return;
|
|
2953
|
-
}
|
|
2954
|
-
actorCtx.defer(() => {
|
|
2955
|
-
if (actorRef.status === ActorStatus.Stopped) {
|
|
2846
|
+
return [cloneState(state, {
|
|
2847
|
+
children
|
|
2848
|
+
}), {
|
|
2849
|
+
type: 'xstate.stop',
|
|
2850
|
+
params: {
|
|
2851
|
+
actor: actorRef
|
|
2852
|
+
},
|
|
2853
|
+
execute: actorCtx => {
|
|
2854
|
+
if (!actorRef) {
|
|
2956
2855
|
return;
|
|
2957
2856
|
}
|
|
2958
|
-
|
|
2959
|
-
|
|
2960
|
-
} catch (err) {
|
|
2961
|
-
parent.send(error(id, err));
|
|
2857
|
+
if (actorRef.status !== ActorStatus.Running) {
|
|
2858
|
+
actorCtx.stopChild(actorRef);
|
|
2962
2859
|
return;
|
|
2963
2860
|
}
|
|
2964
|
-
|
|
2965
|
-
|
|
2966
|
-
|
|
2861
|
+
actorCtx.defer(() => {
|
|
2862
|
+
actorCtx.stopChild(actorRef);
|
|
2863
|
+
});
|
|
2864
|
+
}
|
|
2865
|
+
}];
|
|
2866
|
+
});
|
|
2867
|
+
}
|
|
2868
|
+
|
|
2869
|
+
const defaultLogExpr = ({
|
|
2870
|
+
context,
|
|
2871
|
+
event
|
|
2872
|
+
}) => ({
|
|
2873
|
+
context,
|
|
2874
|
+
event
|
|
2875
|
+
});
|
|
2876
|
+
|
|
2877
|
+
/**
|
|
2878
|
+
*
|
|
2879
|
+
* @param expr The expression function to evaluate which will be logged.
|
|
2880
|
+
* Takes in 2 arguments:
|
|
2881
|
+
* - `ctx` - the current state context
|
|
2882
|
+
* - `event` - the event that caused this action to be executed.
|
|
2883
|
+
* @param label The label to give to the logged expression.
|
|
2884
|
+
*/
|
|
2885
|
+
|
|
2886
|
+
function log(expr = defaultLogExpr, label) {
|
|
2887
|
+
return createDynamicAction({
|
|
2888
|
+
type: log$1,
|
|
2889
|
+
params: {
|
|
2890
|
+
label,
|
|
2891
|
+
expr
|
|
2892
|
+
}
|
|
2893
|
+
}, (event, {
|
|
2894
|
+
state,
|
|
2895
|
+
actorContext
|
|
2896
|
+
}) => {
|
|
2897
|
+
const resolvedValue = typeof expr === 'function' ? expr({
|
|
2898
|
+
context: state.context,
|
|
2899
|
+
event,
|
|
2900
|
+
self: actorContext?.self ?? {},
|
|
2901
|
+
system: actorContext?.system
|
|
2902
|
+
}) : expr;
|
|
2903
|
+
return [state, {
|
|
2904
|
+
type: 'xstate.log',
|
|
2905
|
+
params: {
|
|
2906
|
+
label,
|
|
2907
|
+
value: resolvedValue
|
|
2908
|
+
},
|
|
2909
|
+
execute: actorCtx => {
|
|
2910
|
+
if (label) {
|
|
2911
|
+
actorCtx.logger?.(label, resolvedValue);
|
|
2912
|
+
} else {
|
|
2913
|
+
actorCtx.logger?.(resolvedValue);
|
|
2914
|
+
}
|
|
2915
|
+
}
|
|
2916
|
+
}];
|
|
2967
2917
|
});
|
|
2968
2918
|
}
|
|
2969
2919
|
|
|
@@ -3001,7 +2951,7 @@ function createSpawner(self, machine, context, event, mutCapturedActions) {
|
|
|
3001
2951
|
return actorRef; // TODO: fix types
|
|
3002
2952
|
}
|
|
3003
2953
|
|
|
3004
|
-
throw new Error(`
|
|
2954
|
+
throw new Error(`Actor logic '${src}' not implemented in machine '${machine.id}'`);
|
|
3005
2955
|
} else {
|
|
3006
2956
|
// TODO: this should also receive `src`
|
|
3007
2957
|
// TODO: instead of anonymous, it should be a unique stable ID
|