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