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