xstate 5.0.0-beta.15 → 5.0.0-beta.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/actions/dist/xstate-actions.cjs.js +1 -3
  2. package/actions/dist/xstate-actions.cjs.mjs +0 -2
  3. package/actions/dist/xstate-actions.development.cjs.js +1 -3
  4. package/actions/dist/xstate-actions.development.esm.js +1 -1
  5. package/actions/dist/xstate-actions.esm.js +1 -1
  6. package/actions/dist/xstate-actions.umd.min.js +1 -1
  7. package/actions/dist/xstate-actions.umd.min.js.map +1 -1
  8. package/actors/dist/xstate-actors.cjs.js +1 -1
  9. package/actors/dist/xstate-actors.development.cjs.js +1 -1
  10. package/actors/dist/xstate-actors.development.esm.js +1 -1
  11. package/actors/dist/xstate-actors.esm.js +1 -1
  12. package/actors/dist/xstate-actors.umd.min.js +1 -1
  13. package/actors/dist/xstate-actors.umd.min.js.map +1 -1
  14. package/dist/{actions-6884fae8.esm.js → actions-0386b622.esm.js} +707 -742
  15. package/dist/{actions-d71ac253.development.cjs.js → actions-0f903c0d.development.cjs.js} +836 -874
  16. package/dist/{actions-81cc7f2b.cjs.js → actions-6b9073db.cjs.js} +706 -744
  17. package/dist/{actions-98f362b9.development.esm.js → actions-6f7fbc84.development.esm.js} +837 -872
  18. package/dist/declarations/src/StateMachine.d.ts +2 -4
  19. package/dist/declarations/src/actionTypes.d.ts +1 -1
  20. package/dist/declarations/src/actions/assign.d.ts +2 -2
  21. package/dist/declarations/src/actions/cancel.d.ts +2 -2
  22. package/dist/declarations/src/actions/send.d.ts +12 -22
  23. package/dist/declarations/src/actions/stop.d.ts +2 -2
  24. package/dist/declarations/src/actions.d.ts +1 -4
  25. package/dist/declarations/src/interpreter.d.ts +2 -2
  26. package/dist/declarations/src/stateUtils.d.ts +1 -1
  27. package/dist/declarations/src/types.d.ts +29 -49
  28. package/dist/xstate.cjs.js +20 -34
  29. package/dist/xstate.development.cjs.js +20 -34
  30. package/dist/xstate.development.esm.js +21 -35
  31. package/dist/xstate.esm.js +21 -35
  32. package/dist/xstate.umd.min.js +1 -1
  33. package/dist/xstate.umd.min.js.map +1 -1
  34. package/guards/dist/xstate-guards.cjs.js +1 -1
  35. package/guards/dist/xstate-guards.development.cjs.js +1 -1
  36. package/guards/dist/xstate-guards.development.esm.js +1 -1
  37. package/guards/dist/xstate-guards.esm.js +1 -1
  38. package/guards/dist/xstate-guards.umd.min.js.map +1 -1
  39. package/package.json +1 -1
@@ -35,7 +35,7 @@ import { devToolsAdapter } from '../dev/dist/xstate-dev.esm.js';
35
35
  let ActionTypes = /*#__PURE__*/function (ActionTypes) {
36
36
  ActionTypes["Stop"] = "xstate.stop";
37
37
  ActionTypes["Raise"] = "xstate.raise";
38
- ActionTypes["Send"] = "xstate.send";
38
+ ActionTypes["SendTo"] = "xstate.sendTo";
39
39
  ActionTypes["Cancel"] = "xstate.cancel";
40
40
  ActionTypes["Assign"] = "xstate.assign";
41
41
  ActionTypes["After"] = "xstate.after";
@@ -61,7 +61,7 @@ let SpecialTargets = /*#__PURE__*/function (SpecialTargets) {
61
61
  // xstate-specific action types
62
62
  const stop$1 = ActionTypes.Stop;
63
63
  const raise$1 = ActionTypes.Raise;
64
- const send$1 = ActionTypes.Send;
64
+ const sendTo$1 = ActionTypes.SendTo;
65
65
  const cancel$1 = ActionTypes.Cancel;
66
66
  const assign$1 = ActionTypes.Assign;
67
67
  const after$1 = ActionTypes.After;
@@ -79,7 +79,7 @@ var actionTypes = /*#__PURE__*/Object.freeze({
79
79
  __proto__: null,
80
80
  stop: stop$1,
81
81
  raise: raise$1,
82
- send: send$1,
82
+ sendTo: sendTo$1,
83
83
  cancel: cancel$1,
84
84
  assign: assign$1,
85
85
  after: after$1,
@@ -304,22 +304,19 @@ function isDynamicAction(action) {
304
304
  }
305
305
 
306
306
  /**
307
- * Sends an event. This returns an action that will be read by an interpreter to
308
- * send the event in the next step, after the current step is finished executing.
309
- *
310
- * @deprecated Use the `sendTo(...)` action creator instead.
307
+ * Sends an event to an actor.
311
308
  *
312
- * @param eventOrExpr The event to send.
313
- * @param options Options to pass into the send event:
309
+ * @param actor The `ActorRef` to send the event to.
310
+ * @param event The event to send, or an expression that evaluates to the event to send
311
+ * @param options Send action options
314
312
  * - `id` - The unique send event identifier (used with `cancel()`).
315
313
  * - `delay` - The number of milliseconds to delay the sending of the event.
316
- * - `to` - The target of this event (by default, the machine the event was sent from).
317
314
  */
318
- function send(eventOrExpr, options) {
315
+ function sendTo(actor, eventOrExpr, options) {
319
316
  return createDynamicAction({
320
- type: send$1,
317
+ type: sendTo$1,
321
318
  params: {
322
- to: options ? options.to : undefined,
319
+ to: actor,
323
320
  delay: options ? options.delay : undefined,
324
321
  event: eventOrExpr,
325
322
  id: options && options.id !== undefined ? options.id : isFunction(eventOrExpr) ? eventOrExpr.name : eventOrExpr.type
@@ -329,7 +326,7 @@ function send(eventOrExpr, options) {
329
326
  state
330
327
  }) => {
331
328
  const params = {
332
- to: options ? options.to : undefined,
329
+ to: actor,
333
330
  delay: options ? options.delay : undefined,
334
331
  event: eventOrExpr,
335
332
  // TODO: don't auto-generate IDs here like that
@@ -377,13 +374,12 @@ function send(eventOrExpr, options) {
377
374
  targetActorRef = resolvedTarget || actorContext?.self;
378
375
  }
379
376
  const resolvedAction = {
380
- type: send$1,
377
+ type: sendTo$1,
381
378
  params: {
382
379
  ...params,
383
380
  to: targetActorRef,
384
381
  event: resolvedEvent,
385
- delay: resolvedDelay,
386
- internal: resolvedTarget === SpecialTargets.Internal
382
+ delay: resolvedDelay
387
383
  },
388
384
  execute: actorCtx => {
389
385
  const sendAction = resolvedAction;
@@ -413,12 +409,8 @@ function send(eventOrExpr, options) {
413
409
  * @param options Options to pass into the send event.
414
410
  */
415
411
  function sendParent(event, options) {
416
- return send(event, {
417
- ...options,
418
- to: SpecialTargets.Parent
419
- });
412
+ return sendTo(SpecialTargets.Parent, event, options);
420
413
  }
421
-
422
414
  /**
423
415
  * Forwards (sends) an event to a specified service.
424
416
  *
@@ -426,12 +418,9 @@ function sendParent(event, options) {
426
418
  * @param options Options to pass into the send action creator.
427
419
  */
428
420
  function forwardTo(target, options) {
429
- return send(({
421
+ return sendTo(target, ({
430
422
  event
431
- }) => event, {
432
- ...options,
433
- to: target
434
- });
423
+ }) => event, options);
435
424
  }
436
425
 
437
426
  /**
@@ -447,25 +436,7 @@ function escalate(errorData, options) {
447
436
  type: error$1,
448
437
  data: isFunction(errorData) ? errorData(arg) : errorData
449
438
  };
450
- }, {
451
- ...options,
452
- to: SpecialTargets.Parent
453
- });
454
- }
455
-
456
- /**
457
- * Sends an event to an actor.
458
- *
459
- * @param actor The `ActorRef` to send the event to.
460
- * @param event The event to send, or an expression that evaluates to the event to send
461
- * @param options Send action options
462
- * @returns An XState send action object
463
- */
464
- function sendTo(actor, event, options) {
465
- return send(event, {
466
- ...options,
467
- to: actor
468
- });
439
+ }, options);
469
440
  }
470
441
 
471
442
  const cache = new WeakMap();
@@ -519,8 +490,6 @@ function cancel(sendId) {
519
490
  });
520
491
  }
521
492
 
522
- const symbolObservable = (() => typeof Symbol === 'function' && Symbol.observable || '@@observable')();
523
-
524
493
  class Mailbox {
525
494
  constructor(_process) {
526
495
  this._process = _process;
@@ -588,507 +557,131 @@ class Mailbox {
588
557
  }
589
558
  }
590
559
 
591
- function createSystem() {
592
- let sessionIdCounter = 0;
593
- const children = new Map();
594
- const keyedActors = new Map();
595
- const reverseKeyedActors = new WeakMap();
596
- const system = {
597
- _bookId: () => `x:${sessionIdCounter++}`,
598
- _register: (sessionId, actorRef) => {
599
- children.set(sessionId, actorRef);
600
- return sessionId;
601
- },
602
- _unregister: actorRef => {
603
- children.delete(actorRef.sessionId);
604
- const systemId = reverseKeyedActors.get(actorRef);
605
- if (systemId !== undefined) {
606
- keyedActors.delete(systemId);
607
- reverseKeyedActors.delete(actorRef);
608
- }
560
+ const symbolObservable = (() => typeof Symbol === 'function' && Symbol.observable || '@@observable')();
561
+
562
+ /**
563
+ * Returns actor logic from a transition function and its initial state.
564
+ *
565
+ * A transition function is a function that takes the current state and an event and returns the next state.
566
+ *
567
+ * @param transition The transition function that returns the next state given the current state and event.
568
+ * @param initialState The initial state of the transition function.
569
+ * @returns Actor logic
570
+ */
571
+ function fromTransition(transition, initialState) {
572
+ const logic = {
573
+ config: transition,
574
+ transition: (state, event, actorContext) => {
575
+ return transition(state, event, actorContext);
609
576
  },
610
- get: systemId => {
611
- return keyedActors.get(systemId);
577
+ getInitialState: (_, input) => {
578
+ return typeof initialState === 'function' ? initialState({
579
+ input
580
+ }) : initialState;
612
581
  },
613
- _set: (systemId, actorRef) => {
614
- const existing = keyedActors.get(systemId);
615
- if (existing && existing !== actorRef) {
616
- throw new Error(`Actor with system ID '${systemId}' already exists.`);
617
- }
618
- keyedActors.set(systemId, actorRef);
619
- reverseKeyedActors.set(actorRef, systemId);
620
- }
582
+ getSnapshot: state => state,
583
+ getPersistedState: state => state,
584
+ restoreState: state => state
621
585
  };
622
- return system;
586
+ return logic;
623
587
  }
624
588
 
625
- let ActorStatus = /*#__PURE__*/function (ActorStatus) {
626
- ActorStatus[ActorStatus["NotStarted"] = 0] = "NotStarted";
627
- ActorStatus[ActorStatus["Running"] = 1] = "Running";
628
- ActorStatus[ActorStatus["Stopped"] = 2] = "Stopped";
629
- return ActorStatus;
630
- }({});
631
- const defaultOptions = {
632
- deferEvents: true,
633
- clock: {
634
- setTimeout: (fn, ms) => {
635
- return setTimeout(fn, ms);
589
+ const resolveEventType = '$$xstate.resolve';
590
+ const rejectEventType = '$$xstate.reject';
591
+ function fromPromise(
592
+ // TODO: add types
593
+ promiseCreator) {
594
+ // TODO: add event types, consider making the `PromiseEvent` a private type or smth alike
595
+ const logic = {
596
+ config: promiseCreator,
597
+ transition: (state, event) => {
598
+ if (state.status !== 'active') {
599
+ return state;
600
+ }
601
+ switch (event.type) {
602
+ case resolveEventType:
603
+ return {
604
+ ...state,
605
+ status: 'done',
606
+ data: event.data,
607
+ input: undefined
608
+ };
609
+ case rejectEventType:
610
+ return {
611
+ ...state,
612
+ status: 'error',
613
+ data: event.data,
614
+ input: undefined
615
+ };
616
+ case stopSignalType:
617
+ return {
618
+ ...state,
619
+ status: 'canceled',
620
+ input: undefined
621
+ };
622
+ default:
623
+ return state;
624
+ }
636
625
  },
637
- clearTimeout: id => {
638
- return clearTimeout(id);
639
- }
640
- },
641
- logger: console.log.bind(console),
642
- devTools: false
643
- };
644
- class Interpreter {
645
- /**
646
- * The current state of the interpreted logic.
647
- */
648
-
649
- /**
650
- * The clock that is responsible for setting and clearing timeouts, such as delayed events and transitions.
651
- */
652
-
653
- /**
654
- * The unique identifier for this actor relative to its parent.
655
- */
656
-
657
- /**
658
- * Whether the service is started.
659
- */
660
-
661
- // Actor Ref
662
-
663
- // TODO: add typings for system
626
+ start: (state, {
627
+ self,
628
+ system
629
+ }) => {
630
+ // TODO: determine how to allow customizing this so that promises
631
+ // can be restarted if necessary
632
+ if (state.status !== 'active') {
633
+ return;
634
+ }
635
+ const resolvedPromise = Promise.resolve(promiseCreator({
636
+ input: state.input,
637
+ system
638
+ }));
639
+ resolvedPromise.then(response => {
640
+ // TODO: remove this condition once dead letter queue lands
641
+ if (self._state.status !== 'active') {
642
+ return;
643
+ }
644
+ self.send({
645
+ type: resolveEventType,
646
+ data: response
647
+ });
648
+ }, errorData => {
649
+ // TODO: remove this condition once dead letter queue lands
650
+ if (self._state.status !== 'active') {
651
+ return;
652
+ }
653
+ self.send({
654
+ type: rejectEventType,
655
+ data: errorData
656
+ });
657
+ });
658
+ },
659
+ getInitialState: (_, input) => {
660
+ return {
661
+ status: 'active',
662
+ data: undefined,
663
+ input
664
+ };
665
+ },
666
+ getSnapshot: state => state.data,
667
+ getStatus: state => state,
668
+ getPersistedState: state => state,
669
+ restoreState: state => state
670
+ };
671
+ return logic;
672
+ }
664
673
 
665
- /**
666
- * The globally unique process ID for this invocation.
667
- */
674
+ // TODO: this likely shouldn't accept TEvent, observable actor doesn't accept external events
675
+ function fromObservable(observableCreator) {
676
+ const nextEventType = '$$xstate.next';
677
+ const errorEventType = '$$xstate.error';
678
+ const completeEventType = '$$xstate.complete';
668
679
 
669
- /**
670
- * Creates a new Interpreter instance (i.e., service) for the given logic with the provided options, if any.
671
- *
672
- * @param logic The logic to be interpreted
673
- * @param options Interpreter options
674
- */
675
- constructor(logic, options) {
676
- this.logic = logic;
677
- this._state = void 0;
678
- this.clock = void 0;
679
- this.options = void 0;
680
- this.id = void 0;
681
- this.mailbox = new Mailbox(this._process.bind(this));
682
- this.delayedEventsMap = {};
683
- this.observers = new Set();
684
- this.logger = void 0;
685
- this.status = ActorStatus.NotStarted;
686
- this._parent = void 0;
687
- this.ref = void 0;
688
- this._actorContext = void 0;
689
- this._systemId = void 0;
690
- this.sessionId = void 0;
691
- this.system = void 0;
692
- this._doneEvent = void 0;
693
- this.src = void 0;
694
- this._deferred = [];
695
- const resolvedOptions = {
696
- ...defaultOptions,
697
- ...options
698
- };
699
- const {
700
- clock,
701
- logger,
702
- parent,
703
- id,
704
- systemId
705
- } = resolvedOptions;
706
- const self = this;
707
- this.system = parent?.system ?? createSystem();
708
- if (systemId) {
709
- this._systemId = systemId;
710
- this.system._set(systemId, this);
711
- }
712
- this.sessionId = this.system._bookId();
713
- this.id = id ?? this.sessionId;
714
- this.logger = logger;
715
- this.clock = clock;
716
- this._parent = parent;
717
- this.options = resolvedOptions;
718
- this.src = resolvedOptions.src;
719
- this.ref = this;
720
- this._actorContext = {
721
- self,
722
- id: this.id,
723
- sessionId: this.sessionId,
724
- logger: this.logger,
725
- defer: fn => {
726
- this._deferred.push(fn);
727
- },
728
- system: this.system,
729
- stopChild: child => {
730
- if (child._parent !== this) {
731
- throw new Error(`Cannot stop child actor ${child.id} of ${this.id} because it is not a child`);
732
- }
733
- child._stop();
734
- }
735
- };
736
-
737
- // Ensure that the send method is bound to this interpreter instance
738
- // if destructured
739
- this.send = this.send.bind(this);
740
- this._initState();
741
- }
742
- _initState() {
743
- 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);
744
- }
745
-
746
- // array of functions to defer
747
-
748
- update(state) {
749
- // Update state
750
- this._state = state;
751
- const snapshot = this.getSnapshot();
752
-
753
- // Execute deferred effects
754
- let deferredFn;
755
- while (deferredFn = this._deferred.shift()) {
756
- deferredFn();
757
- }
758
- for (const observer of this.observers) {
759
- observer.next?.(snapshot);
760
- }
761
- const status = this.logic.getStatus?.(state);
762
- switch (status?.status) {
763
- case 'done':
764
- this._stopProcedure();
765
- this._doneEvent = doneInvoke(this.id, status.data);
766
- this._parent?.send(this._doneEvent);
767
- this._complete();
768
- break;
769
- case 'error':
770
- this._stopProcedure();
771
- this._parent?.send(error(this.id, status.data));
772
- this._error(status.data);
773
- break;
774
- }
775
- }
776
- subscribe(nextListenerOrObserver, errorListener, completeListener) {
777
- const observer = toObserver(nextListenerOrObserver, errorListener, completeListener);
778
- this.observers.add(observer);
779
- if (this.status === ActorStatus.Stopped) {
780
- observer.complete?.();
781
- this.observers.delete(observer);
782
- }
783
- return {
784
- unsubscribe: () => {
785
- this.observers.delete(observer);
786
- }
787
- };
788
- }
789
-
790
- /**
791
- * Starts the interpreter from the initial state
792
- */
793
- start() {
794
- if (this.status === ActorStatus.Running) {
795
- // Do not restart the service if it is already started
796
- return this;
797
- }
798
- this.system._register(this.sessionId, this);
799
- if (this._systemId) {
800
- this.system._set(this._systemId, this);
801
- }
802
- this.status = ActorStatus.Running;
803
- if (this.logic.start) {
804
- this.logic.start(this._state, this._actorContext);
805
- }
806
-
807
- // TODO: this notifies all subscribers but usually this is redundant
808
- // there is no real change happening here
809
- // we need to rethink if this needs to be refactored
810
- this.update(this._state);
811
- if (this.options.devTools) {
812
- this.attachDevTools();
813
- }
814
- this.mailbox.start();
815
- return this;
816
- }
817
- _process(event) {
818
- try {
819
- const nextState = this.logic.transition(this._state, event, this._actorContext);
820
- this.update(nextState);
821
- if (event.type === stopSignalType) {
822
- this._stopProcedure();
823
- this._complete();
824
- }
825
- } catch (err) {
826
- // TODO: properly handle errors
827
- if (this.observers.size > 0) {
828
- this.observers.forEach(observer => {
829
- observer.error?.(err);
830
- });
831
- this.stop();
832
- } else {
833
- throw err;
834
- }
835
- }
836
- }
837
- _stop() {
838
- if (this.status === ActorStatus.Stopped) {
839
- return this;
840
- }
841
- this.mailbox.clear();
842
- if (this.status === ActorStatus.NotStarted) {
843
- this.status = ActorStatus.Stopped;
844
- return this;
845
- }
846
- this.mailbox.enqueue({
847
- type: stopSignalType
848
- });
849
- return this;
850
- }
851
-
852
- /**
853
- * Stops the interpreter and unsubscribe all listeners.
854
- */
855
- stop() {
856
- if (this._parent) {
857
- throw new Error('A non-root actor cannot be stopped directly.');
858
- }
859
- return this._stop();
860
- }
861
- _complete() {
862
- for (const observer of this.observers) {
863
- observer.complete?.();
864
- }
865
- this.observers.clear();
866
- }
867
- _error(data) {
868
- for (const observer of this.observers) {
869
- observer.error?.(data);
870
- }
871
- this.observers.clear();
872
- }
873
- _stopProcedure() {
874
- if (this.status !== ActorStatus.Running) {
875
- // Interpreter already stopped; do nothing
876
- return this;
877
- }
878
-
879
- // Cancel all delayed events
880
- for (const key of Object.keys(this.delayedEventsMap)) {
881
- this.clock.clearTimeout(this.delayedEventsMap[key]);
882
- }
883
-
884
- // TODO: mailbox.reset
885
- this.mailbox.clear();
886
- // TODO: after `stop` we must prepare ourselves for receiving events again
887
- // events sent *after* stop signal must be queued
888
- // it seems like this should be the common behavior for all of our consumers
889
- // so perhaps this should be unified somehow for all of them
890
- this.mailbox = new Mailbox(this._process.bind(this));
891
- this.status = ActorStatus.Stopped;
892
- this.system._unregister(this);
893
- return this;
894
- }
895
-
896
- /**
897
- * Sends an event to the running interpreter to trigger a transition.
898
- *
899
- * @param event The event to send
900
- */
901
- send(event) {
902
- if (typeof event === 'string') {
903
- throw new Error(`Only event objects may be sent to actors; use .send({ type: "${event}" }) instead`);
904
- }
905
- if (this.status === ActorStatus.Stopped) {
906
- return;
907
- }
908
- if (this.status !== ActorStatus.Running && !this.options.deferEvents) {
909
- throw new Error(`Event "${event.type}" was sent to uninitialized actor "${this.id
910
- // tslint:disable-next-line:max-line-length
911
- }". Make sure .start() is called for this actor, or set { deferEvents: true } in the actor options.\nEvent: ${JSON.stringify(event)}`);
912
- }
913
- this.mailbox.enqueue(event);
914
- }
915
-
916
- // TODO: make private (and figure out a way to do this within the machine)
917
- delaySend(sendAction) {
918
- this.delayedEventsMap[sendAction.params.id] = this.clock.setTimeout(() => {
919
- if ('to' in sendAction.params && sendAction.params.to) {
920
- sendAction.params.to.send(sendAction.params.event);
921
- } else {
922
- this.send(sendAction.params.event);
923
- }
924
- }, sendAction.params.delay);
925
- }
926
-
927
- // TODO: make private (and figure out a way to do this within the machine)
928
- cancel(sendId) {
929
- this.clock.clearTimeout(this.delayedEventsMap[sendId]);
930
- delete this.delayedEventsMap[sendId];
931
- }
932
- attachDevTools() {
933
- const {
934
- devTools
935
- } = this.options;
936
- if (devTools) {
937
- const resolvedDevToolsAdapter = typeof devTools === 'function' ? devTools : devToolsAdapter;
938
- resolvedDevToolsAdapter(this);
939
- }
940
- }
941
- toJSON() {
942
- return {
943
- id: this.id
944
- };
945
- }
946
- getPersistedState() {
947
- return this.logic.getPersistedState?.(this._state);
948
- }
949
- [symbolObservable]() {
950
- return this;
951
- }
952
- getSnapshot() {
953
- return this.logic.getSnapshot ? this.logic.getSnapshot(this._state) : this._state;
954
- }
955
- }
956
-
957
- /**
958
- * Creates a new Interpreter instance for the given machine with the provided options, if any.
959
- *
960
- * @param machine The machine to interpret
961
- * @param options Interpreter options
962
- */
963
-
964
- function interpret(logic, options) {
965
- const interpreter = new Interpreter(logic, options);
966
- return interpreter;
967
- }
968
-
969
- /**
970
- * Returns actor logic from a transition function and its initial state.
971
- *
972
- * A transition function is a function that takes the current state and an event and returns the next state.
973
- *
974
- * @param transition The transition function that returns the next state given the current state and event.
975
- * @param initialState The initial state of the transition function.
976
- * @returns Actor logic
977
- */
978
- function fromTransition(transition, initialState) {
979
- const logic = {
980
- config: transition,
981
- transition: (state, event, actorContext) => {
982
- return transition(state, event, actorContext);
983
- },
984
- getInitialState: (_, input) => {
985
- return typeof initialState === 'function' ? initialState({
986
- input
987
- }) : initialState;
988
- },
989
- getSnapshot: state => state,
990
- getPersistedState: state => state,
991
- restoreState: state => state
992
- };
993
- return logic;
994
- }
995
-
996
- const resolveEventType = '$$xstate.resolve';
997
- const rejectEventType = '$$xstate.reject';
998
- function fromPromise(
999
- // TODO: add types
1000
- promiseCreator) {
1001
- // TODO: add event types, consider making the `PromiseEvent` a private type or smth alike
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';
1086
-
1087
- // TODO: add event types
1088
- const logic = {
1089
- config: observableCreator,
1090
- transition: (state, event, {
1091
- self,
680
+ // TODO: add event types
681
+ const logic = {
682
+ config: observableCreator,
683
+ transition: (state, event, {
684
+ self,
1092
685
  id,
1093
686
  defer
1094
687
  }) => {
@@ -1278,140 +871,518 @@ function fromEventObservable(lazyObservable) {
1278
871
  }
1279
872
  });
1280
873
  },
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
- })
874
+ getSnapshot: _ => undefined,
875
+ getPersistedState: ({
876
+ status,
877
+ data,
878
+ input
879
+ }) => ({
880
+ status,
881
+ data,
882
+ input
883
+ }),
884
+ getStatus: state => state,
885
+ restoreState: state => ({
886
+ ...state,
887
+ subscription: undefined
888
+ })
889
+ };
890
+ return logic;
891
+ }
892
+
893
+ function fromCallback(invokeCallback) {
894
+ const logic = {
895
+ config: invokeCallback,
896
+ start: (_state, {
897
+ self
898
+ }) => {
899
+ self.send({
900
+ type: startSignalType
901
+ });
902
+ },
903
+ transition: (state, event, {
904
+ self,
905
+ id,
906
+ system
907
+ }) => {
908
+ if (event.type === startSignalType) {
909
+ const sender = eventForParent => {
910
+ if (state.canceled) {
911
+ return;
912
+ }
913
+ self._parent?.send(eventForParent);
914
+ };
915
+ const receiver = newListener => {
916
+ state.receivers.add(newListener);
917
+ };
918
+ state.dispose = invokeCallback(sender, receiver, {
919
+ input: state.input,
920
+ system
921
+ });
922
+ if (isPromiseLike(state.dispose)) {
923
+ state.dispose.then(resolved => {
924
+ self._parent?.send(doneInvoke(id, resolved));
925
+ state.canceled = true;
926
+ }, errorData => {
927
+ state.canceled = true;
928
+ self._parent?.send(error(id, errorData));
929
+ });
930
+ }
931
+ return state;
932
+ }
933
+ if (event.type === stopSignalType) {
934
+ state.canceled = true;
935
+ if (isFunction(state.dispose)) {
936
+ state.dispose();
937
+ }
938
+ return state;
939
+ }
940
+ if (isSignal(event)) {
941
+ // TODO: unrecognized signal
942
+ return state;
943
+ }
944
+ state.receivers.forEach(receiver => receiver(event));
945
+ return state;
946
+ },
947
+ getInitialState: (_, input) => {
948
+ return {
949
+ canceled: false,
950
+ receivers: new Set(),
951
+ dispose: undefined,
952
+ input
953
+ };
954
+ },
955
+ getSnapshot: () => undefined,
956
+ getPersistedState: ({
957
+ input
958
+ }) => input
959
+ };
960
+ return logic;
961
+ }
962
+
963
+ const startSignalType = 'xstate.init';
964
+ const stopSignalType = 'xstate.stop';
965
+ const startSignal = {
966
+ type: 'xstate.init'
967
+ };
968
+ const stopSignal = {
969
+ type: 'xstate.stop'
970
+ };
971
+ /**
972
+ * An object that expresses the actor logic in reaction to received events,
973
+ * as well as an optionally emitted stream of values.
974
+ *
975
+ * @template TReceived The received event
976
+ * @template TSnapshot The emitted value
977
+ */
978
+
979
+ function isSignal(event) {
980
+ return event.type === startSignalType || event.type === stopSignalType;
981
+ }
982
+ function isActorRef(item) {
983
+ return !!item && typeof item === 'object' && typeof item.send === 'function';
984
+ }
985
+
986
+ // TODO: refactor the return type, this could be written in a better way
987
+ // but it's best to avoid unneccessary breaking changes now
988
+ // @deprecated use `interpret(actorLogic)` instead
989
+ function toActorRef(actorRefLike) {
990
+ return {
991
+ subscribe: () => ({
992
+ unsubscribe: () => void 0
993
+ }),
994
+ id: 'anonymous',
995
+ sessionId: '',
996
+ getSnapshot: () => undefined,
997
+ [symbolObservable]: function () {
998
+ return this;
999
+ },
1000
+ status: ActorStatus.Running,
1001
+ stop: () => void 0,
1002
+ ...actorRefLike
1003
+ };
1004
+ }
1005
+ const emptyLogic = fromTransition(_ => undefined, undefined);
1006
+ function createEmptyActor() {
1007
+ return interpret(emptyLogic);
1008
+ }
1009
+
1010
+ function createSystem() {
1011
+ let sessionIdCounter = 0;
1012
+ const children = new Map();
1013
+ const keyedActors = new Map();
1014
+ const reverseKeyedActors = new WeakMap();
1015
+ const system = {
1016
+ _bookId: () => `x:${sessionIdCounter++}`,
1017
+ _register: (sessionId, actorRef) => {
1018
+ children.set(sessionId, actorRef);
1019
+ return sessionId;
1020
+ },
1021
+ _unregister: actorRef => {
1022
+ children.delete(actorRef.sessionId);
1023
+ const systemId = reverseKeyedActors.get(actorRef);
1024
+ if (systemId !== undefined) {
1025
+ keyedActors.delete(systemId);
1026
+ reverseKeyedActors.delete(actorRef);
1027
+ }
1028
+ },
1029
+ get: systemId => {
1030
+ return keyedActors.get(systemId);
1031
+ },
1032
+ _set: (systemId, actorRef) => {
1033
+ const existing = keyedActors.get(systemId);
1034
+ if (existing && existing !== actorRef) {
1035
+ throw new Error(`Actor with system ID '${systemId}' already exists.`);
1036
+ }
1037
+ keyedActors.set(systemId, actorRef);
1038
+ reverseKeyedActors.set(actorRef, systemId);
1039
+ }
1296
1040
  };
1297
- return logic;
1041
+ return system;
1298
1042
  }
1299
1043
 
1300
- function fromCallback(invokeCallback) {
1301
- const logic = {
1302
- config: invokeCallback,
1303
- start: (_state, {
1304
- self
1305
- }) => {
1306
- self.send({
1307
- type: startSignalType
1308
- });
1044
+ let ActorStatus = /*#__PURE__*/function (ActorStatus) {
1045
+ ActorStatus[ActorStatus["NotStarted"] = 0] = "NotStarted";
1046
+ ActorStatus[ActorStatus["Running"] = 1] = "Running";
1047
+ ActorStatus[ActorStatus["Stopped"] = 2] = "Stopped";
1048
+ return ActorStatus;
1049
+ }({});
1050
+ const defaultOptions = {
1051
+ deferEvents: true,
1052
+ clock: {
1053
+ setTimeout: (fn, ms) => {
1054
+ return setTimeout(fn, ms);
1309
1055
  },
1310
- transition: (state, event, {
1311
- self,
1056
+ clearTimeout: id => {
1057
+ return clearTimeout(id);
1058
+ }
1059
+ },
1060
+ logger: console.log.bind(console),
1061
+ devTools: false
1062
+ };
1063
+ class Interpreter {
1064
+ /**
1065
+ * The current state of the interpreted logic.
1066
+ */
1067
+
1068
+ /**
1069
+ * The clock that is responsible for setting and clearing timeouts, such as delayed events and transitions.
1070
+ */
1071
+
1072
+ /**
1073
+ * The unique identifier for this actor relative to its parent.
1074
+ */
1075
+
1076
+ /**
1077
+ * Whether the service is started.
1078
+ */
1079
+
1080
+ // Actor Ref
1081
+
1082
+ // TODO: add typings for system
1083
+
1084
+ /**
1085
+ * The globally unique process ID for this invocation.
1086
+ */
1087
+
1088
+ /**
1089
+ * Creates a new Interpreter instance (i.e., service) for the given logic with the provided options, if any.
1090
+ *
1091
+ * @param logic The logic to be interpreted
1092
+ * @param options Interpreter options
1093
+ */
1094
+ constructor(logic, options) {
1095
+ this.logic = logic;
1096
+ this._state = void 0;
1097
+ this.clock = void 0;
1098
+ this.options = void 0;
1099
+ this.id = void 0;
1100
+ this.mailbox = new Mailbox(this._process.bind(this));
1101
+ this.delayedEventsMap = {};
1102
+ this.observers = new Set();
1103
+ this.logger = void 0;
1104
+ this.status = ActorStatus.NotStarted;
1105
+ this._parent = void 0;
1106
+ this.ref = void 0;
1107
+ this._actorContext = void 0;
1108
+ this._systemId = void 0;
1109
+ this.sessionId = void 0;
1110
+ this.system = void 0;
1111
+ this._doneEvent = void 0;
1112
+ this.src = void 0;
1113
+ this._deferred = [];
1114
+ const resolvedOptions = {
1115
+ ...defaultOptions,
1116
+ ...options
1117
+ };
1118
+ const {
1119
+ clock,
1120
+ logger,
1121
+ parent,
1312
1122
  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
- });
1123
+ systemId
1124
+ } = resolvedOptions;
1125
+ const self = this;
1126
+ this.system = parent?.system ?? createSystem();
1127
+ if (systemId) {
1128
+ this._systemId = systemId;
1129
+ this.system._set(systemId, this);
1130
+ }
1131
+ this.sessionId = this.system._bookId();
1132
+ this.id = id ?? this.sessionId;
1133
+ this.logger = logger;
1134
+ this.clock = clock;
1135
+ this._parent = parent;
1136
+ this.options = resolvedOptions;
1137
+ this.src = resolvedOptions.src;
1138
+ this.ref = this;
1139
+ this._actorContext = {
1140
+ self,
1141
+ id: this.id,
1142
+ sessionId: this.sessionId,
1143
+ logger: this.logger,
1144
+ defer: fn => {
1145
+ this._deferred.push(fn);
1146
+ },
1147
+ system: this.system,
1148
+ stopChild: child => {
1149
+ if (child._parent !== this) {
1150
+ throw new Error(`Cannot stop child actor ${child.id} of ${this.id} because it is not a child`);
1337
1151
  }
1338
- return state;
1152
+ child._stop();
1153
+ }
1154
+ };
1155
+
1156
+ // Ensure that the send method is bound to this interpreter instance
1157
+ // if destructured
1158
+ this.send = this.send.bind(this);
1159
+ this._initState();
1160
+ }
1161
+ _initState() {
1162
+ 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);
1163
+ }
1164
+
1165
+ // array of functions to defer
1166
+
1167
+ update(state) {
1168
+ // Update state
1169
+ this._state = state;
1170
+ const snapshot = this.getSnapshot();
1171
+
1172
+ // Execute deferred effects
1173
+ let deferredFn;
1174
+ while (deferredFn = this._deferred.shift()) {
1175
+ deferredFn();
1176
+ }
1177
+ for (const observer of this.observers) {
1178
+ observer.next?.(snapshot);
1179
+ }
1180
+ const status = this.logic.getStatus?.(state);
1181
+ switch (status?.status) {
1182
+ case 'done':
1183
+ this._stopProcedure();
1184
+ this._doneEvent = doneInvoke(this.id, status.data);
1185
+ this._parent?.send(this._doneEvent);
1186
+ this._complete();
1187
+ break;
1188
+ case 'error':
1189
+ this._stopProcedure();
1190
+ this._parent?.send(error(this.id, status.data));
1191
+ this._error(status.data);
1192
+ break;
1193
+ }
1194
+ }
1195
+ subscribe(nextListenerOrObserver, errorListener, completeListener) {
1196
+ const observer = toObserver(nextListenerOrObserver, errorListener, completeListener);
1197
+ this.observers.add(observer);
1198
+ if (this.status === ActorStatus.Stopped) {
1199
+ observer.complete?.();
1200
+ this.observers.delete(observer);
1201
+ }
1202
+ return {
1203
+ unsubscribe: () => {
1204
+ this.observers.delete(observer);
1339
1205
  }
1206
+ };
1207
+ }
1208
+
1209
+ /**
1210
+ * Starts the interpreter from the initial state
1211
+ */
1212
+ start() {
1213
+ if (this.status === ActorStatus.Running) {
1214
+ // Do not restart the service if it is already started
1215
+ return this;
1216
+ }
1217
+ this.system._register(this.sessionId, this);
1218
+ if (this._systemId) {
1219
+ this.system._set(this._systemId, this);
1220
+ }
1221
+ this.status = ActorStatus.Running;
1222
+ if (this.logic.start) {
1223
+ this.logic.start(this._state, this._actorContext);
1224
+ }
1225
+
1226
+ // TODO: this notifies all subscribers but usually this is redundant
1227
+ // there is no real change happening here
1228
+ // we need to rethink if this needs to be refactored
1229
+ this.update(this._state);
1230
+ if (this.options.devTools) {
1231
+ this.attachDevTools();
1232
+ }
1233
+ this.mailbox.start();
1234
+ return this;
1235
+ }
1236
+ _process(event) {
1237
+ try {
1238
+ const nextState = this.logic.transition(this._state, event, this._actorContext);
1239
+ this.update(nextState);
1340
1240
  if (event.type === stopSignalType) {
1341
- state.canceled = true;
1342
- if (isFunction(state.dispose)) {
1343
- state.dispose();
1344
- }
1345
- return state;
1241
+ this._stopProcedure();
1242
+ this._complete();
1346
1243
  }
1347
- if (isSignal(event)) {
1348
- // TODO: unrecognized signal
1349
- return state;
1244
+ } catch (err) {
1245
+ // TODO: properly handle errors
1246
+ if (this.observers.size > 0) {
1247
+ this.observers.forEach(observer => {
1248
+ observer.error?.(err);
1249
+ });
1250
+ this.stop();
1251
+ } else {
1252
+ throw err;
1350
1253
  }
1351
- state.receivers.forEach(receiver => receiver(event));
1352
- return state;
1353
- },
1354
- getInitialState: (_, input) => {
1355
- return {
1356
- canceled: false,
1357
- receivers: new Set(),
1358
- dispose: undefined,
1359
- input
1360
- };
1361
- },
1362
- getSnapshot: () => undefined,
1363
- getPersistedState: ({
1364
- input
1365
- }) => input
1366
- };
1367
- return logic;
1254
+ }
1255
+ }
1256
+ _stop() {
1257
+ if (this.status === ActorStatus.Stopped) {
1258
+ return this;
1259
+ }
1260
+ this.mailbox.clear();
1261
+ if (this.status === ActorStatus.NotStarted) {
1262
+ this.status = ActorStatus.Stopped;
1263
+ return this;
1264
+ }
1265
+ this.mailbox.enqueue({
1266
+ type: stopSignalType
1267
+ });
1268
+ return this;
1269
+ }
1270
+
1271
+ /**
1272
+ * Stops the interpreter and unsubscribe all listeners.
1273
+ */
1274
+ stop() {
1275
+ if (this._parent) {
1276
+ throw new Error('A non-root actor cannot be stopped directly.');
1277
+ }
1278
+ return this._stop();
1279
+ }
1280
+ _complete() {
1281
+ for (const observer of this.observers) {
1282
+ observer.complete?.();
1283
+ }
1284
+ this.observers.clear();
1285
+ }
1286
+ _error(data) {
1287
+ for (const observer of this.observers) {
1288
+ observer.error?.(data);
1289
+ }
1290
+ this.observers.clear();
1291
+ }
1292
+ _stopProcedure() {
1293
+ if (this.status !== ActorStatus.Running) {
1294
+ // Interpreter already stopped; do nothing
1295
+ return this;
1296
+ }
1297
+
1298
+ // Cancel all delayed events
1299
+ for (const key of Object.keys(this.delayedEventsMap)) {
1300
+ this.clock.clearTimeout(this.delayedEventsMap[key]);
1301
+ }
1302
+
1303
+ // TODO: mailbox.reset
1304
+ this.mailbox.clear();
1305
+ // TODO: after `stop` we must prepare ourselves for receiving events again
1306
+ // events sent *after* stop signal must be queued
1307
+ // it seems like this should be the common behavior for all of our consumers
1308
+ // so perhaps this should be unified somehow for all of them
1309
+ this.mailbox = new Mailbox(this._process.bind(this));
1310
+ this.status = ActorStatus.Stopped;
1311
+ this.system._unregister(this);
1312
+ return this;
1313
+ }
1314
+
1315
+ /**
1316
+ * Sends an event to the running interpreter to trigger a transition.
1317
+ *
1318
+ * @param event The event to send
1319
+ */
1320
+ send(event) {
1321
+ if (typeof event === 'string') {
1322
+ throw new Error(`Only event objects may be sent to actors; use .send({ type: "${event}" }) instead`);
1323
+ }
1324
+ if (this.status === ActorStatus.Stopped) {
1325
+ return;
1326
+ }
1327
+ if (this.status !== ActorStatus.Running && !this.options.deferEvents) {
1328
+ throw new Error(`Event "${event.type}" was sent to uninitialized actor "${this.id
1329
+ // tslint:disable-next-line:max-line-length
1330
+ }". Make sure .start() is called for this actor, or set { deferEvents: true } in the actor options.\nEvent: ${JSON.stringify(event)}`);
1331
+ }
1332
+ this.mailbox.enqueue(event);
1333
+ }
1334
+
1335
+ // TODO: make private (and figure out a way to do this within the machine)
1336
+ delaySend(sendAction) {
1337
+ this.delayedEventsMap[sendAction.params.id] = this.clock.setTimeout(() => {
1338
+ if ('to' in sendAction.params && sendAction.params.to) {
1339
+ sendAction.params.to.send(sendAction.params.event);
1340
+ } else {
1341
+ this.send(sendAction.params.event);
1342
+ }
1343
+ }, sendAction.params.delay);
1344
+ }
1345
+
1346
+ // TODO: make private (and figure out a way to do this within the machine)
1347
+ cancel(sendId) {
1348
+ this.clock.clearTimeout(this.delayedEventsMap[sendId]);
1349
+ delete this.delayedEventsMap[sendId];
1350
+ }
1351
+ attachDevTools() {
1352
+ const {
1353
+ devTools
1354
+ } = this.options;
1355
+ if (devTools) {
1356
+ const resolvedDevToolsAdapter = typeof devTools === 'function' ? devTools : devToolsAdapter;
1357
+ resolvedDevToolsAdapter(this);
1358
+ }
1359
+ }
1360
+ toJSON() {
1361
+ return {
1362
+ id: this.id
1363
+ };
1364
+ }
1365
+ getPersistedState() {
1366
+ return this.logic.getPersistedState?.(this._state);
1367
+ }
1368
+ [symbolObservable]() {
1369
+ return this;
1370
+ }
1371
+ getSnapshot() {
1372
+ return this.logic.getSnapshot ? this.logic.getSnapshot(this._state) : this._state;
1373
+ }
1368
1374
  }
1369
1375
 
1370
- const startSignalType = 'xstate.init';
1371
- const stopSignalType = 'xstate.stop';
1372
- const startSignal = {
1373
- type: 'xstate.init'
1374
- };
1375
- const stopSignal = {
1376
- type: 'xstate.stop'
1377
- };
1378
1376
  /**
1379
- * An object that expresses the actor logic in reaction to received events,
1380
- * as well as an optionally emitted stream of values.
1377
+ * Creates a new Interpreter instance for the given machine with the provided options, if any.
1381
1378
  *
1382
- * @template TReceived The received event
1383
- * @template TSnapshot The emitted value
1379
+ * @param machine The machine to interpret
1380
+ * @param options Interpreter options
1384
1381
  */
1385
1382
 
1386
- function isSignal(event) {
1387
- return event.type === startSignalType || event.type === stopSignalType;
1388
- }
1389
- function isActorRef(item) {
1390
- return !!item && typeof item === 'object' && typeof item.send === 'function';
1391
- }
1392
-
1393
- // TODO: refactor the return type, this could be written in a better way
1394
- // but it's best to avoid unneccessary breaking changes now
1395
- // @deprecated use `interpret(actorLogic)` instead
1396
- function toActorRef(actorRefLike) {
1397
- return {
1398
- subscribe: () => ({
1399
- unsubscribe: () => void 0
1400
- }),
1401
- id: 'anonymous',
1402
- sessionId: '',
1403
- getSnapshot: () => undefined,
1404
- [symbolObservable]: function () {
1405
- return this;
1406
- },
1407
- status: ActorStatus.Running,
1408
- stop: () => void 0,
1409
- ...actorRefLike
1410
- };
1411
- }
1412
- const emptyLogic = fromTransition(_ => undefined, undefined);
1413
- function createEmptyActor() {
1414
- return interpret(emptyLogic);
1383
+ function interpret(logic, options) {
1384
+ const interpreter = new Interpreter(logic, options);
1385
+ return interpreter;
1415
1386
  }
1416
1387
 
1417
1388
  function invoke(invokeDef) {
@@ -1428,42 +1399,32 @@ function invoke(invokeDef) {
1428
1399
  src
1429
1400
  } = invokeDef;
1430
1401
  let resolvedInvokeAction;
1431
- if (isActorRef(src)) {
1402
+ const referenced = resolveReferencedActor(state.machine.implementations.actors[src]);
1403
+ if (!referenced) {
1404
+ resolvedInvokeAction = {
1405
+ type,
1406
+ params: invokeDef
1407
+ };
1408
+ } else {
1409
+ const input = 'input' in invokeDef ? invokeDef.input : referenced.input;
1410
+ const ref = interpret(referenced.src, {
1411
+ id,
1412
+ src,
1413
+ parent: actorContext?.self,
1414
+ systemId: invokeDef.systemId,
1415
+ input: typeof input === 'function' ? input({
1416
+ context: state.context,
1417
+ event,
1418
+ self: actorContext?.self
1419
+ }) : input
1420
+ });
1432
1421
  resolvedInvokeAction = {
1433
1422
  type,
1434
1423
  params: {
1435
1424
  ...invokeDef,
1436
- ref: src
1425
+ ref
1437
1426
  }
1438
1427
  };
1439
- } else {
1440
- const referenced = resolveReferencedActor(state.machine.implementations.actors[src]);
1441
- if (!referenced) {
1442
- resolvedInvokeAction = {
1443
- type,
1444
- params: invokeDef
1445
- };
1446
- } else {
1447
- const input = 'input' in invokeDef ? invokeDef.input : referenced.input;
1448
- const ref = interpret(referenced.src, {
1449
- id,
1450
- src,
1451
- parent: actorContext?.self,
1452
- systemId: invokeDef.systemId,
1453
- input: typeof input === 'function' ? input({
1454
- context: state.context,
1455
- event,
1456
- self: actorContext?.self
1457
- }) : input
1458
- });
1459
- resolvedInvokeAction = {
1460
- type,
1461
- params: {
1462
- ...invokeDef,
1463
- ref
1464
- }
1465
- };
1466
- }
1467
1428
  }
1468
1429
  const actorRef = resolvedInvokeAction.params.ref;
1469
1430
  const invokedState = cloneState(state, {
@@ -2454,7 +2415,7 @@ function resolveActionsAndContext(actions, event, currentState, actorCtx) {
2454
2415
  });
2455
2416
  const matchedActions = resolvedAction.params?.actions;
2456
2417
  intermediateState = nextState;
2457
- if ((resolvedAction.type === raise$1 || resolvedAction.type === send$1 && resolvedAction.params.internal) && typeof resolvedAction.params.delay !== 'number') {
2418
+ if (resolvedAction.type === raise$1 && typeof resolvedAction.params.delay !== 'number') {
2458
2419
  raiseActions.push(resolvedAction);
2459
2420
  }
2460
2421
 
@@ -2766,11 +2727,14 @@ function stop(actorRef) {
2766
2727
  actor
2767
2728
  }
2768
2729
  }, (event, {
2769
- state
2730
+ state,
2731
+ actorContext
2770
2732
  }) => {
2771
2733
  const actorRefOrString = isFunction(actor) ? actor({
2772
2734
  context: state.context,
2773
- event
2735
+ event,
2736
+ self: actorContext?.self ?? {},
2737
+ system: actorContext?.system
2774
2738
  }) : actor;
2775
2739
  const actorRef = typeof actorRefOrString === 'string' ? state.children[actorRefOrString] : actorRefOrString;
2776
2740
  let children = state.children;
@@ -2854,59 +2818,60 @@ function log(expr = defaultLogExpr, label) {
2854
2818
  });
2855
2819
  }
2856
2820
 
2857
- function createSpawner(self, machine, context, event, mutCapturedActions) {
2858
- return (src, options = {}) => {
2821
+ function createSpawner(actorContext, {
2822
+ machine,
2823
+ context
2824
+ }, event, spawnedChildren) {
2825
+ const spawn = (src, options = {}) => {
2859
2826
  const {
2860
2827
  systemId
2861
2828
  } = options;
2862
- if (isString(src)) {
2829
+ if (typeof src === 'string') {
2863
2830
  const referenced = resolveReferencedActor(machine.implementations.actors[src]);
2864
- if (referenced) {
2865
- const input = 'input' in options ? options.input : referenced.input;
2866
-
2867
- // TODO: this should also receive `src`
2868
- const actorRef = interpret(referenced.src, {
2869
- id: options.id,
2870
- parent: self,
2871
- input: typeof input === 'function' ? input({
2872
- context,
2873
- event,
2874
- self
2875
- }) : input
2876
- });
2877
- mutCapturedActions.push(invoke({
2878
- id: actorRef.id,
2879
- // @ts-ignore TODO: fix types
2880
- src: actorRef,
2881
- // TODO
2882
- ref: actorRef,
2883
- meta: undefined,
2884
- input,
2885
- systemId
2886
- }));
2887
- return actorRef; // TODO: fix types
2888
- }
2889
-
2890
- throw new Error(`Actor logic '${src}' not implemented in machine '${machine.id}'`);
2831
+ if (!referenced) {
2832
+ throw new Error(`Actor logic '${src}' not implemented in machine '${machine.id}'`);
2833
+ }
2834
+ const input = 'input' in options ? options.input : referenced.input;
2835
+
2836
+ // TODO: this should also receive `src`
2837
+ const actor = interpret(referenced.src, {
2838
+ id: options.id,
2839
+ parent: actorContext.self,
2840
+ input: typeof input === 'function' ? input({
2841
+ context,
2842
+ event,
2843
+ self: actorContext.self
2844
+ }) : input,
2845
+ systemId
2846
+ });
2847
+ spawnedChildren[actor.id] = actor;
2848
+ return actor;
2891
2849
  } else {
2892
2850
  // TODO: this should also receive `src`
2893
- const actorRef = interpret(src, {
2851
+ return interpret(src, {
2894
2852
  id: options.id,
2895
- parent: self,
2853
+ parent: actorContext.self,
2896
2854
  input: options.input,
2897
2855
  systemId
2898
2856
  });
2899
- mutCapturedActions.push(invoke({
2900
- // @ts-ignore TODO: fix types
2901
- src: actorRef,
2902
- ref: actorRef,
2903
- id: actorRef.id,
2904
- meta: undefined,
2905
- input: options.input
2906
- }));
2907
- return actorRef; // TODO: fix types
2908
2857
  }
2909
2858
  };
2859
+ return (src, options) => {
2860
+ const actorRef = spawn(src, options);
2861
+ spawnedChildren[actorRef.id] = actorRef;
2862
+ actorContext.defer(() => {
2863
+ if (actorRef.status === ActorStatus.Stopped) {
2864
+ return;
2865
+ }
2866
+ try {
2867
+ actorRef.start?.();
2868
+ } catch (err) {
2869
+ actorContext.self.send(error(actorRef.id, err));
2870
+ return;
2871
+ }
2872
+ });
2873
+ return actorRef;
2874
+ };
2910
2875
  }
2911
2876
 
2912
2877
  /**
@@ -2925,15 +2890,15 @@ function assign(assignment) {
2925
2890
  action,
2926
2891
  actorContext
2927
2892
  }) => {
2928
- const capturedActions = [];
2929
2893
  if (!state.context) {
2930
2894
  throw new Error('Cannot assign to undefined `context`. Ensure that `context` is defined in the machine config.');
2931
2895
  }
2896
+ const spawnedChildren = {};
2932
2897
  const args = {
2933
2898
  context: state.context,
2934
2899
  event,
2935
2900
  action,
2936
- spawn: createSpawner(actorContext?.self, state.machine, state.context, event, capturedActions),
2901
+ spawn: createSpawner(actorContext, state, event, spawnedChildren),
2937
2902
  self: actorContext?.self ?? {},
2938
2903
  system: actorContext?.system
2939
2904
  };
@@ -2948,12 +2913,15 @@ function assign(assignment) {
2948
2913
  }
2949
2914
  const updatedContext = Object.assign({}, state.context, partialUpdate);
2950
2915
  return [cloneState(state, {
2951
- context: updatedContext
2916
+ context: updatedContext,
2917
+ children: Object.keys(spawnedChildren).length ? {
2918
+ ...state.children,
2919
+ ...spawnedChildren
2920
+ } : state.children
2952
2921
  }), {
2953
2922
  type: assign$1,
2954
2923
  params: {
2955
- context: updatedContext,
2956
- actions: capturedActions
2924
+ context: updatedContext
2957
2925
  }
2958
2926
  }];
2959
2927
  });
@@ -3064,9 +3032,6 @@ function pure(getActions) {
3064
3032
  });
3065
3033
  }
3066
3034
 
3067
- const initEvent = {
3068
- type: init
3069
- };
3070
3035
  function resolveActionObject(actionObject, actionFunctionMap) {
3071
3036
  if (isDynamicAction(actionObject)) {
3072
3037
  return actionObject;
@@ -3211,4 +3176,4 @@ function createInitEvent(input) {
3211
3176
  };
3212
3177
  }
3213
3178
 
3214
- export { pathToStateValue as $, resolveActionsAndContext as A, microstep as B, isAtomicStateNode as C, error as D, isStateId as E, getStateNodeByPath as F, getPersistedState as G, resolveReferencedActor as H, interpret as I, createInitEvent as J, initEvent as K, matchesState as L, sendTo as M, NULL_EVENT as N, sendParent as O, forwardTo as P, Interpreter as Q, ActorStatus as R, STATE_DELIMITER as S, doneInvoke as T, assign as U, cancel as V, choose as W, log as X, pure as Y, raise as Z, stop as _, toArray as a, toObserver as a0, fromPromise as a1, fromObservable as a2, fromCallback as a3, fromEventObservable as a4, fromTransition as a5, stateIn as a6, not as a7, and as a8, or as a9, ActionTypes as aa, SpecialTargets as ab, startSignalType as ac, stopSignalType as ad, startSignal as ae, stopSignal as af, isSignal as ag, isActorRef as ah, toActorRef as ai, createEmptyActor as aj, toGuardDefinition as ak, actionTypes as al, resolveActionObject as am, toActionObject as an, after as ao, done as ap, send as aq, escalate as ar, 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 };
3179
+ export { fromPromise as $, assign as A, microstep as B, isAtomicStateNode as C, error as D, isStateId as E, getStateNodeByPath as F, getPersistedState as G, resolveReferencedActor as H, interpret as I, createInitEvent 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, cancel as T, choose as U, log as V, pure as W, raise as X, stop as Y, pathToStateValue as Z, toObserver as _, toArray as a, fromObservable as a0, fromCallback as a1, fromEventObservable as a2, fromTransition as a3, stateIn as a4, not as a5, and as a6, or as a7, ActionTypes as a8, SpecialTargets as a9, startSignalType as aa, stopSignalType as ab, startSignal as ac, stopSignal as ad, isSignal as ae, isActorRef as af, toActorRef as ag, createEmptyActor as ah, toGuardDefinition as ai, actionTypes as aj, resolveActionObject as ak, toActionObject as al, after as am, done as an, escalate as ao, 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, getConfiguration as p, getStateNodes as q, resolveStateValue as r, isInFinalState as s, toActionObjects as t, State as u, isErrorEvent as v, macrostep as w, transitionNode as x, getInitialConfiguration as y, resolveActionsAndContext as z };