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