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