xstate 5.0.0-beta.14 → 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 (46) 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-444a17c3.esm.js → actions-0386b622.esm.js} +934 -997
  15. package/dist/{actions-73b8d456.development.cjs.js → actions-0f903c0d.development.cjs.js} +863 -928
  16. package/dist/{actions-17c3bcfa.cjs.js → actions-6b9073db.cjs.js} +934 -999
  17. package/dist/{actions-60622c0c.development.esm.js → actions-6f7fbc84.development.esm.js} +863 -926
  18. package/dist/declarations/src/State.d.ts +1 -11
  19. package/dist/declarations/src/StateMachine.d.ts +4 -11
  20. package/dist/declarations/src/actionTypes.d.ts +1 -1
  21. package/dist/declarations/src/actions/assign.d.ts +2 -2
  22. package/dist/declarations/src/actions/cancel.d.ts +2 -2
  23. package/dist/declarations/src/actions/send.d.ts +12 -22
  24. package/dist/declarations/src/actions/stop.d.ts +2 -2
  25. package/dist/declarations/src/actions.d.ts +1 -4
  26. package/dist/declarations/src/actors/callback.d.ts +3 -3
  27. package/dist/declarations/src/actors/index.d.ts +1 -1
  28. package/dist/declarations/src/actors/promise.d.ts +13 -3
  29. package/dist/declarations/src/guards.d.ts +1 -1
  30. package/dist/declarations/src/interpreter.d.ts +2 -2
  31. package/dist/declarations/src/stateUtils.d.ts +3 -2
  32. package/dist/declarations/src/types.d.ts +44 -71
  33. package/dist/declarations/src/utils.d.ts +3 -3
  34. package/dist/xstate.cjs.js +44 -59
  35. package/dist/xstate.development.cjs.js +44 -59
  36. package/dist/xstate.development.esm.js +45 -60
  37. package/dist/xstate.esm.js +45 -60
  38. package/dist/xstate.umd.min.js +1 -1
  39. package/dist/xstate.umd.min.js.map +1 -1
  40. package/guards/dist/xstate-guards.cjs.js +1 -1
  41. package/guards/dist/xstate-guards.development.cjs.js +1 -1
  42. package/guards/dist/xstate-guards.development.esm.js +1 -1
  43. package/guards/dist/xstate-guards.esm.js +1 -1
  44. package/guards/dist/xstate-guards.umd.min.js +1 -1
  45. package/guards/dist/xstate-guards.umd.min.js.map +1 -1
  46. package/package.json +2 -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,
@@ -100,9 +100,9 @@ const NULL_EVENT = '';
100
100
  const STATE_IDENTIFIER = '#';
101
101
  const WILDCARD = '*';
102
102
 
103
- function matchesState(parentStateId, childStateId, delimiter = STATE_DELIMITER) {
104
- const parentStateValue = toStateValue(parentStateId, delimiter);
105
- const childStateValue = toStateValue(childStateId, delimiter);
103
+ function matchesState(parentStateId, childStateId) {
104
+ const parentStateValue = toStateValue(parentStateId);
105
+ const childStateValue = toStateValue(childStateId);
106
106
  if (isString(childStateValue)) {
107
107
  if (isString(parentStateValue)) {
108
108
  return childStateValue === parentStateValue;
@@ -121,12 +121,12 @@ function matchesState(parentStateId, childStateId, delimiter = STATE_DELIMITER)
121
121
  return matchesState(parentStateValue[key], childStateValue[key]);
122
122
  });
123
123
  }
124
- function toStatePath(stateId, delimiter) {
124
+ function toStatePath(stateId) {
125
125
  try {
126
126
  if (isArray(stateId)) {
127
127
  return stateId;
128
128
  }
129
- return stateId.toString().split(delimiter);
129
+ return stateId.toString().split(STATE_DELIMITER);
130
130
  } catch (e) {
131
131
  throw new Error(`'${stateId}' is not a valid state path.`);
132
132
  }
@@ -134,7 +134,7 @@ function toStatePath(stateId, delimiter) {
134
134
  function isStateLike(state) {
135
135
  return typeof state === 'object' && 'value' in state && 'context' in state && 'event' in state;
136
136
  }
137
- function toStateValue(stateValue, delimiter) {
137
+ function toStateValue(stateValue) {
138
138
  if (isStateLike(stateValue)) {
139
139
  return stateValue.value;
140
140
  }
@@ -144,7 +144,7 @@ function toStateValue(stateValue, delimiter) {
144
144
  if (typeof stateValue !== 'string') {
145
145
  return stateValue;
146
146
  }
147
- const statePath = toStatePath(stateValue, delimiter);
147
+ const statePath = toStatePath(stateValue);
148
148
  return pathToStateValue(statePath);
149
149
  }
150
150
  function pathToStateValue(statePath) {
@@ -157,8 +157,9 @@ function pathToStateValue(statePath) {
157
157
  if (i === statePath.length - 2) {
158
158
  marker[statePath[i]] = statePath[i + 1];
159
159
  } else {
160
- marker[statePath[i]] = {};
161
- marker = marker[statePath[i]];
160
+ const previous = marker;
161
+ marker = {};
162
+ previous[statePath[i]] = marker;
162
163
  }
163
164
  }
164
165
  return value;
@@ -303,22 +304,19 @@ function isDynamicAction(action) {
303
304
  }
304
305
 
305
306
  /**
306
- * Sends an event. This returns an action that will be read by an interpreter to
307
- * send the event in the next step, after the current step is finished executing.
308
- *
309
- * @deprecated Use the `sendTo(...)` action creator instead.
307
+ * Sends an event to an actor.
310
308
  *
311
- * @param eventOrExpr The event to send.
312
- * @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
313
312
  * - `id` - The unique send event identifier (used with `cancel()`).
314
313
  * - `delay` - The number of milliseconds to delay the sending of the event.
315
- * - `to` - The target of this event (by default, the machine the event was sent from).
316
314
  */
317
- function send(eventOrExpr, options) {
315
+ function sendTo(actor, eventOrExpr, options) {
318
316
  return createDynamicAction({
319
- type: send$1,
317
+ type: sendTo$1,
320
318
  params: {
321
- to: options ? options.to : undefined,
319
+ to: actor,
322
320
  delay: options ? options.delay : undefined,
323
321
  event: eventOrExpr,
324
322
  id: options && options.id !== undefined ? options.id : isFunction(eventOrExpr) ? eventOrExpr.name : eventOrExpr.type
@@ -328,7 +326,7 @@ function send(eventOrExpr, options) {
328
326
  state
329
327
  }) => {
330
328
  const params = {
331
- to: options ? options.to : undefined,
329
+ to: actor,
332
330
  delay: options ? options.delay : undefined,
333
331
  event: eventOrExpr,
334
332
  // TODO: don't auto-generate IDs here like that
@@ -341,7 +339,7 @@ function send(eventOrExpr, options) {
341
339
  self: actorContext?.self ?? null,
342
340
  system: actorContext?.system
343
341
  };
344
- const delaysMap = state.machine.options.delays;
342
+ const delaysMap = state.machine.implementations.delays;
345
343
 
346
344
  // TODO: helper function for resolving Expr
347
345
  if (typeof eventOrExpr === 'string') {
@@ -376,13 +374,12 @@ function send(eventOrExpr, options) {
376
374
  targetActorRef = resolvedTarget || actorContext?.self;
377
375
  }
378
376
  const resolvedAction = {
379
- type: send$1,
377
+ type: sendTo$1,
380
378
  params: {
381
379
  ...params,
382
380
  to: targetActorRef,
383
381
  event: resolvedEvent,
384
- delay: resolvedDelay,
385
- internal: resolvedTarget === SpecialTargets.Internal
382
+ delay: resolvedDelay
386
383
  },
387
384
  execute: actorCtx => {
388
385
  const sendAction = resolvedAction;
@@ -412,12 +409,8 @@ function send(eventOrExpr, options) {
412
409
  * @param options Options to pass into the send event.
413
410
  */
414
411
  function sendParent(event, options) {
415
- return send(event, {
416
- ...options,
417
- to: SpecialTargets.Parent
418
- });
412
+ return sendTo(SpecialTargets.Parent, event, options);
419
413
  }
420
-
421
414
  /**
422
415
  * Forwards (sends) an event to a specified service.
423
416
  *
@@ -425,12 +418,9 @@ function sendParent(event, options) {
425
418
  * @param options Options to pass into the send action creator.
426
419
  */
427
420
  function forwardTo(target, options) {
428
- return send(({
421
+ return sendTo(target, ({
429
422
  event
430
- }) => event, {
431
- ...options,
432
- to: target
433
- });
423
+ }) => event, options);
434
424
  }
435
425
 
436
426
  /**
@@ -446,25 +436,7 @@ function escalate(errorData, options) {
446
436
  type: error$1,
447
437
  data: isFunction(errorData) ? errorData(arg) : errorData
448
438
  };
449
- }, {
450
- ...options,
451
- to: SpecialTargets.Parent
452
- });
453
- }
454
-
455
- /**
456
- * Sends an event to an actor.
457
- *
458
- * @param actor The `ActorRef` to send the event to.
459
- * @param event The event to send, or an expression that evaluates to the event to send
460
- * @param options Send action options
461
- * @returns An XState send action object
462
- */
463
- function sendTo(actor, event, options) {
464
- return send(event, {
465
- ...options,
466
- to: actor
467
- });
439
+ }, options);
468
440
  }
469
441
 
470
442
  const cache = new WeakMap();
@@ -518,8 +490,6 @@ function cancel(sendId) {
518
490
  });
519
491
  }
520
492
 
521
- const symbolObservable = (() => typeof Symbol === 'function' && Symbol.observable || '@@observable')();
522
-
523
493
  class Mailbox {
524
494
  constructor(_process) {
525
495
  this._process = _process;
@@ -587,833 +557,832 @@ class Mailbox {
587
557
  }
588
558
  }
589
559
 
590
- function createSystem() {
591
- let sessionIdCounter = 0;
592
- const children = new Map();
593
- const keyedActors = new Map();
594
- const reverseKeyedActors = new WeakMap();
595
- const system = {
596
- _bookId: () => `x:${sessionIdCounter++}`,
597
- _register: (sessionId, actorRef) => {
598
- children.set(sessionId, actorRef);
599
- return sessionId;
600
- },
601
- _unregister: actorRef => {
602
- children.delete(actorRef.sessionId);
603
- const systemId = reverseKeyedActors.get(actorRef);
604
- if (systemId !== undefined) {
605
- keyedActors.delete(systemId);
606
- reverseKeyedActors.delete(actorRef);
607
- }
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);
608
576
  },
609
- get: systemId => {
610
- return keyedActors.get(systemId);
577
+ getInitialState: (_, input) => {
578
+ return typeof initialState === 'function' ? initialState({
579
+ input
580
+ }) : initialState;
611
581
  },
612
- _set: (systemId, actorRef) => {
613
- const existing = keyedActors.get(systemId);
614
- if (existing && existing !== actorRef) {
615
- throw new Error(`Actor with system ID '${systemId}' already exists.`);
616
- }
617
- keyedActors.set(systemId, actorRef);
618
- reverseKeyedActors.set(actorRef, systemId);
619
- }
582
+ getSnapshot: state => state,
583
+ getPersistedState: state => state,
584
+ restoreState: state => state
620
585
  };
621
- return system;
586
+ return logic;
622
587
  }
623
588
 
624
- let ActorStatus = /*#__PURE__*/function (ActorStatus) {
625
- ActorStatus[ActorStatus["NotStarted"] = 0] = "NotStarted";
626
- ActorStatus[ActorStatus["Running"] = 1] = "Running";
627
- ActorStatus[ActorStatus["Stopped"] = 2] = "Stopped";
628
- return ActorStatus;
629
- }({});
630
- const defaultOptions = {
631
- deferEvents: true,
632
- clock: {
633
- setTimeout: (fn, ms) => {
634
- return setTimeout(fn, ms);
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
+ }
635
625
  },
636
- clearTimeout: id => {
637
- return clearTimeout(id);
638
- }
639
- },
640
- logger: console.log.bind(console),
641
- devTools: false
642
- };
643
- class Interpreter {
644
- /**
645
- * The current state of the interpreted logic.
646
- */
647
-
648
- /**
649
- * The clock that is responsible for setting and clearing timeouts, such as delayed events and transitions.
650
- */
651
-
652
- /**
653
- * The unique identifier for this actor relative to its parent.
654
- */
655
-
656
- /**
657
- * Whether the service is started.
658
- */
659
-
660
- // Actor Ref
661
-
662
- // 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
+ }
663
673
 
664
- /**
665
- * The globally unique process ID for this invocation.
666
- */
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';
667
679
 
668
- /**
669
- * Creates a new Interpreter instance (i.e., service) for the given logic with the provided options, if any.
670
- *
671
- * @param logic The logic to be interpreted
672
- * @param options Interpreter options
673
- */
674
- constructor(logic, options) {
675
- this.logic = logic;
676
- this._state = void 0;
677
- this.clock = void 0;
678
- this.options = void 0;
679
- this.id = void 0;
680
- this.mailbox = new Mailbox(this._process.bind(this));
681
- this.delayedEventsMap = {};
682
- this.observers = new Set();
683
- this.logger = void 0;
684
- this.status = ActorStatus.NotStarted;
685
- this._parent = void 0;
686
- this.ref = void 0;
687
- this._actorContext = void 0;
688
- this._systemId = void 0;
689
- this.sessionId = void 0;
690
- this.system = void 0;
691
- this._doneEvent = void 0;
692
- this.src = void 0;
693
- this._deferred = [];
694
- const resolvedOptions = {
695
- ...defaultOptions,
696
- ...options
697
- };
698
- const {
699
- clock,
700
- logger,
701
- parent,
680
+ // TODO: add event types
681
+ const logic = {
682
+ config: observableCreator,
683
+ transition: (state, event, {
684
+ self,
702
685
  id,
703
- systemId
704
- } = resolvedOptions;
705
- const self = this;
706
- this.system = parent?.system ?? createSystem();
707
- if (systemId) {
708
- this._systemId = systemId;
709
- this.system._set(systemId, this);
710
- }
711
- this.sessionId = this.system._bookId();
712
- this.id = id ?? this.sessionId;
713
- this.logger = logger;
714
- this.clock = clock;
715
- this._parent = parent;
716
- this.options = resolvedOptions;
717
- this.src = resolvedOptions.src;
718
- this.ref = this;
719
- this._actorContext = {
686
+ defer
687
+ }) => {
688
+ if (state.status !== 'active') {
689
+ return state;
690
+ }
691
+ switch (event.type) {
692
+ case nextEventType:
693
+ // match the exact timing of events sent by machines
694
+ // send actions are not executed immediately
695
+ defer(() => {
696
+ self._parent?.send({
697
+ type: `xstate.snapshot.${id}`,
698
+ data: event.data
699
+ });
700
+ });
701
+ return {
702
+ ...state,
703
+ data: event.data
704
+ };
705
+ case errorEventType:
706
+ return {
707
+ ...state,
708
+ status: 'error',
709
+ input: undefined,
710
+ data: event.data,
711
+ subscription: undefined
712
+ };
713
+ case completeEventType:
714
+ return {
715
+ ...state,
716
+ status: 'done',
717
+ input: undefined,
718
+ subscription: undefined
719
+ };
720
+ case stopSignalType:
721
+ state.subscription.unsubscribe();
722
+ return {
723
+ ...state,
724
+ status: 'canceled',
725
+ input: undefined,
726
+ subscription: undefined
727
+ };
728
+ default:
729
+ return state;
730
+ }
731
+ },
732
+ getInitialState: (_, input) => {
733
+ return {
734
+ subscription: undefined,
735
+ status: 'active',
736
+ data: undefined,
737
+ input
738
+ };
739
+ },
740
+ start: (state, {
720
741
  self,
721
- id: this.id,
722
- sessionId: this.sessionId,
723
- logger: this.logger,
724
- defer: fn => {
725
- this._deferred.push(fn);
726
- },
727
- system: this.system,
728
- stopChild: child => {
729
- if (child._parent !== this) {
730
- throw new Error(`Cannot stop child actor ${child.id} of ${this.id} because it is not a child`);
731
- }
732
- child._stop();
742
+ system
743
+ }) => {
744
+ if (state.status === 'done') {
745
+ // Do not restart a completed observable
746
+ return;
733
747
  }
734
- };
735
-
736
- // Ensure that the send method is bound to this interpreter instance
737
- // if destructured
738
- this.send = this.send.bind(this);
739
- this._initState();
740
- }
741
- _initState() {
742
- this._state = this.options.state ? this.logic.restoreState ? this.logic.restoreState(this.options.state, this._actorContext) : this.options.state : this.logic.getInitialState(this._actorContext, this.options?.input);
743
- }
744
-
745
- // array of functions to defer
746
-
747
- update(state) {
748
- // Update state
749
- this._state = state;
750
- const snapshot = this.getSnapshot();
748
+ state.subscription = observableCreator({
749
+ input: state.input,
750
+ system
751
+ }).subscribe({
752
+ next: value => {
753
+ self.send({
754
+ type: nextEventType,
755
+ data: value
756
+ });
757
+ },
758
+ error: err => {
759
+ self.send({
760
+ type: errorEventType,
761
+ data: err
762
+ });
763
+ },
764
+ complete: () => {
765
+ self.send({
766
+ type: completeEventType
767
+ });
768
+ }
769
+ });
770
+ },
771
+ getSnapshot: state => state.data,
772
+ getPersistedState: ({
773
+ status,
774
+ data,
775
+ input
776
+ }) => ({
777
+ status,
778
+ data,
779
+ input
780
+ }),
781
+ getStatus: state => state,
782
+ restoreState: state => ({
783
+ ...state,
784
+ subscription: undefined
785
+ })
786
+ };
787
+ return logic;
788
+ }
751
789
 
752
- // Execute deferred effects
753
- let deferredFn;
754
- while (deferredFn = this._deferred.shift()) {
755
- deferredFn();
756
- }
757
- for (const observer of this.observers) {
758
- observer.next?.(snapshot);
759
- }
760
- const status = this.logic.getStatus?.(state);
761
- switch (status?.status) {
762
- case 'done':
763
- this._stopProcedure();
764
- this._doneEvent = doneInvoke(this.id, status.data);
765
- this._parent?.send(this._doneEvent);
766
- this._complete();
767
- break;
768
- case 'error':
769
- this._stopProcedure();
770
- this._parent?.send(error(this.id, status.data));
771
- this._error(status.data);
772
- break;
773
- }
774
- }
775
- subscribe(nextListenerOrObserver, errorListener, completeListener) {
776
- const observer = toObserver(nextListenerOrObserver, errorListener, completeListener);
777
- this.observers.add(observer);
778
- if (this.status === ActorStatus.Stopped) {
779
- observer.complete?.();
780
- this.observers.delete(observer);
781
- }
782
- return {
783
- unsubscribe: () => {
784
- this.observers.delete(observer);
785
- }
786
- };
787
- }
790
+ /**
791
+ * Creates event observable logic that listens to an observable
792
+ * that delivers event objects.
793
+ *
794
+ *
795
+ * @param lazyObservable A function that creates an observable
796
+ * @returns Event observable logic
797
+ */
788
798
 
789
- /**
790
- * Starts the interpreter from the initial state
791
- */
792
- start() {
793
- if (this.status === ActorStatus.Running) {
794
- // Do not restart the service if it is already started
795
- return this;
796
- }
797
- this.system._register(this.sessionId, this);
798
- if (this._systemId) {
799
- this.system._set(this._systemId, this);
800
- }
801
- this.status = ActorStatus.Running;
802
- if (this.logic.start) {
803
- this.logic.start(this._state, this._actorContext);
804
- }
799
+ function fromEventObservable(lazyObservable) {
800
+ const errorEventType = '$$xstate.error';
801
+ const completeEventType = '$$xstate.complete';
805
802
 
806
- // TODO: this notifies all subscribers but usually this is redundant
807
- // there is no real change happening here
808
- // we need to rethink if this needs to be refactored
809
- this.update(this._state);
810
- if (this.options.devTools) {
811
- this.attachDevTools();
812
- }
813
- this.mailbox.start();
814
- return this;
815
- }
816
- _process(event) {
817
- try {
818
- const nextState = this.logic.transition(this._state, event, this._actorContext);
819
- this.update(nextState);
820
- if (event.type === stopSignalType) {
821
- this._stopProcedure();
822
- this._complete();
803
+ // TODO: event types
804
+ const logic = {
805
+ config: lazyObservable,
806
+ transition: (state, event) => {
807
+ if (state.status !== 'active') {
808
+ return state;
823
809
  }
824
- } catch (err) {
825
- // TODO: properly handle errors
826
- if (this.observers.size > 0) {
827
- this.observers.forEach(observer => {
828
- observer.error?.(err);
829
- });
830
- this.stop();
831
- } else {
832
- throw err;
810
+ switch (event.type) {
811
+ case errorEventType:
812
+ return {
813
+ ...state,
814
+ status: 'error',
815
+ input: undefined,
816
+ data: event.data,
817
+ subscription: undefined
818
+ };
819
+ case completeEventType:
820
+ return {
821
+ ...state,
822
+ status: 'done',
823
+ input: undefined,
824
+ subscription: undefined
825
+ };
826
+ case stopSignalType:
827
+ state.subscription.unsubscribe();
828
+ return {
829
+ ...state,
830
+ status: 'canceled',
831
+ input: undefined,
832
+ subscription: undefined
833
+ };
834
+ default:
835
+ return state;
833
836
  }
834
- }
835
- }
836
- _stop() {
837
- if (this.status === ActorStatus.Stopped) {
838
- return this;
839
- }
840
- this.mailbox.clear();
841
- if (this.status === ActorStatus.NotStarted) {
842
- this.status = ActorStatus.Stopped;
843
- return this;
844
- }
845
- this.mailbox.enqueue({
846
- type: stopSignalType
847
- });
848
- return this;
849
- }
850
-
851
- /**
852
- * Stops the interpreter and unsubscribe all listeners.
853
- */
854
- stop() {
855
- if (this._parent) {
856
- throw new Error('A non-root actor cannot be stopped directly.');
857
- }
858
- return this._stop();
859
- }
860
- _complete() {
861
- for (const observer of this.observers) {
862
- observer.complete?.();
863
- }
864
- this.observers.clear();
865
- }
866
- _error(data) {
867
- for (const observer of this.observers) {
868
- observer.error?.(data);
869
- }
870
- this.observers.clear();
871
- }
872
- _stopProcedure() {
873
- if (this.status !== ActorStatus.Running) {
874
- // Interpreter already stopped; do nothing
875
- return this;
876
- }
877
-
878
- // Cancel all delayed events
879
- for (const key of Object.keys(this.delayedEventsMap)) {
880
- this.clock.clearTimeout(this.delayedEventsMap[key]);
881
- }
882
-
883
- // TODO: mailbox.reset
884
- this.mailbox.clear();
885
- // TODO: after `stop` we must prepare ourselves for receiving events again
886
- // events sent *after* stop signal must be queued
887
- // it seems like this should be the common behavior for all of our consumers
888
- // so perhaps this should be unified somehow for all of them
889
- this.mailbox = new Mailbox(this._process.bind(this));
890
- this.status = ActorStatus.Stopped;
891
- this.system._unregister(this);
892
- return this;
893
- }
894
-
895
- /**
896
- * Sends an event to the running interpreter to trigger a transition.
897
- *
898
- * @param event The event to send
899
- */
900
- send(event) {
901
- if (typeof event === 'string') {
902
- throw new Error(`Only event objects may be sent to actors; use .send({ type: "${event}" }) instead`);
903
- }
904
- if (this.status === ActorStatus.Stopped) {
905
- return;
906
- }
907
- if (this.status !== ActorStatus.Running && !this.options.deferEvents) {
908
- throw new Error(`Event "${event.type}" was sent to uninitialized actor "${this.id
909
- // tslint:disable-next-line:max-line-length
910
- }". Make sure .start() is called for this actor, or set { deferEvents: true } in the actor options.\nEvent: ${JSON.stringify(event)}`);
911
- }
912
- this.mailbox.enqueue(event);
913
- }
914
-
915
- // TODO: make private (and figure out a way to do this within the machine)
916
- delaySend(sendAction) {
917
- this.delayedEventsMap[sendAction.params.id] = this.clock.setTimeout(() => {
918
- if ('to' in sendAction.params && sendAction.params.to) {
919
- sendAction.params.to.send(sendAction.params.event);
920
- } else {
921
- this.send(sendAction.params.event);
922
- }
923
- }, sendAction.params.delay);
924
- }
925
-
926
- // TODO: make private (and figure out a way to do this within the machine)
927
- cancel(sendId) {
928
- this.clock.clearTimeout(this.delayedEventsMap[sendId]);
929
- delete this.delayedEventsMap[sendId];
930
- }
931
- attachDevTools() {
932
- const {
933
- devTools
934
- } = this.options;
935
- if (devTools) {
936
- const resolvedDevToolsAdapter = typeof devTools === 'function' ? devTools : devToolsAdapter;
937
- resolvedDevToolsAdapter(this);
938
- }
939
- }
940
- toJSON() {
941
- return {
942
- id: this.id
943
- };
944
- }
945
- getPersistedState() {
946
- return this.logic.getPersistedState?.(this._state);
947
- }
948
- [symbolObservable]() {
949
- return this;
950
- }
951
- getSnapshot() {
952
- return this.logic.getSnapshot ? this.logic.getSnapshot(this._state) : this._state;
953
- }
954
- }
955
-
956
- /**
957
- * Creates a new Interpreter instance for the given machine with the provided options, if any.
958
- *
959
- * @param machine The machine to interpret
960
- * @param options Interpreter options
961
- */
962
-
963
- function interpret(logic, options) {
964
- const interpreter = new Interpreter(logic, options);
965
- return interpreter;
966
- }
967
-
968
- /**
969
- * Returns actor logic from a transition function and its initial state.
970
- *
971
- * A transition function is a function that takes the current state and an event and returns the next state.
972
- *
973
- * @param transition The transition function that returns the next state given the current state and event.
974
- * @param initialState The initial state of the transition function.
975
- * @returns Actor logic
976
- */
977
- function fromTransition(transition, initialState) {
978
- const logic = {
979
- config: transition,
980
- transition: (state, event, actorContext) => {
981
- return transition(state, event, actorContext);
982
837
  },
983
838
  getInitialState: (_, input) => {
984
- return typeof initialState === 'function' ? initialState({
839
+ return {
840
+ subscription: undefined,
841
+ status: 'active',
842
+ data: undefined,
985
843
  input
986
- }) : initialState;
987
- },
988
- getSnapshot: state => state,
989
- getPersistedState: state => state,
990
- restoreState: state => state
991
- };
992
- return logic;
993
- }
994
-
995
- function fromPromise(
996
- // TODO: add types
997
- promiseCreator) {
998
- const resolveEventType = '$$xstate.resolve';
999
- const rejectEventType = '$$xstate.reject';
1000
-
1001
- // TODO: add event types
1002
- const logic = {
1003
- config: promiseCreator,
1004
- transition: (state, event) => {
1005
- if (state.status !== 'active') {
1006
- return state;
1007
- }
1008
- switch (event.type) {
1009
- case resolveEventType:
1010
- return {
1011
- ...state,
1012
- status: 'done',
1013
- data: event.data,
1014
- input: undefined
1015
- };
1016
- case rejectEventType:
1017
- return {
1018
- ...state,
1019
- status: 'error',
1020
- data: event.data,
1021
- input: undefined
1022
- };
1023
- case stopSignalType:
1024
- return {
1025
- ...state,
1026
- status: 'canceled',
1027
- input: undefined
1028
- };
1029
- default:
1030
- return state;
1031
- }
844
+ };
1032
845
  },
1033
846
  start: (state, {
1034
847
  self,
1035
848
  system
1036
849
  }) => {
1037
- // TODO: determine how to allow customizing this so that promises
1038
- // can be restarted if necessary
1039
- if (state.status !== 'active') {
850
+ if (state.status === 'done') {
851
+ // Do not restart a completed observable
1040
852
  return;
1041
853
  }
1042
- const resolvedPromise = Promise.resolve(promiseCreator({
854
+ state.subscription = lazyObservable({
1043
855
  input: state.input,
1044
856
  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;
857
+ }).subscribe({
858
+ next: value => {
859
+ self._parent?.send(value);
860
+ },
861
+ error: err => {
862
+ self.send({
863
+ type: errorEventType,
864
+ data: err
865
+ });
866
+ },
867
+ complete: () => {
868
+ self.send({
869
+ type: completeEventType
870
+ });
1059
871
  }
1060
- self.send({
1061
- type: rejectEventType,
1062
- data: errorData
1063
- });
1064
872
  });
1065
873
  },
1066
- getInitialState: (_, input) => {
1067
- return {
1068
- status: 'active',
1069
- data: undefined,
1070
- input
1071
- };
1072
- },
1073
- getSnapshot: state => state.data,
874
+ getSnapshot: _ => undefined,
875
+ getPersistedState: ({
876
+ status,
877
+ data,
878
+ input
879
+ }) => ({
880
+ status,
881
+ data,
882
+ input
883
+ }),
1074
884
  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';
885
+ restoreState: state => ({
886
+ ...state,
887
+ subscription: undefined
888
+ })
889
+ };
890
+ return logic;
891
+ }
1086
892
 
1087
- // TODO: add event types
893
+ function fromCallback(invokeCallback) {
1088
894
  const logic = {
1089
- config: observableCreator,
895
+ config: invokeCallback,
896
+ start: (_state, {
897
+ self
898
+ }) => {
899
+ self.send({
900
+ type: startSignalType
901
+ });
902
+ },
1090
903
  transition: (state, event, {
1091
904
  self,
1092
905
  id,
1093
- defer
906
+ system
1094
907
  }) => {
1095
- if (state.status !== 'active') {
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
+ }
1096
931
  return state;
1097
932
  }
1098
- switch (event.type) {
1099
- case nextEventType:
1100
- // match the exact timing of events sent by machines
1101
- // send actions are not executed immediately
1102
- defer(() => {
1103
- self._parent?.send({
1104
- type: `xstate.snapshot.${id}`,
1105
- data: event.data
1106
- });
1107
- });
1108
- return {
1109
- ...state,
1110
- data: event.data
1111
- };
1112
- case errorEventType:
1113
- return {
1114
- ...state,
1115
- status: 'error',
1116
- input: undefined,
1117
- data: event.data,
1118
- subscription: undefined
1119
- };
1120
- case completeEventType:
1121
- return {
1122
- ...state,
1123
- status: 'done',
1124
- input: undefined,
1125
- subscription: undefined
1126
- };
1127
- case stopSignalType:
1128
- state.subscription.unsubscribe();
1129
- return {
1130
- ...state,
1131
- status: 'canceled',
1132
- input: undefined,
1133
- subscription: undefined
1134
- };
1135
- default:
1136
- return state;
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;
1137
943
  }
944
+ state.receivers.forEach(receiver => receiver(event));
945
+ return state;
1138
946
  },
1139
947
  getInitialState: (_, input) => {
1140
948
  return {
1141
- subscription: undefined,
1142
- status: 'active',
1143
- data: undefined,
949
+ canceled: false,
950
+ receivers: new Set(),
951
+ dispose: undefined,
1144
952
  input
1145
953
  };
1146
954
  },
1147
- start: (state, {
1148
- self,
1149
- system
1150
- }) => {
1151
- if (state.status === 'done') {
1152
- // Do not restart a completed observable
1153
- return;
1154
- }
1155
- state.subscription = observableCreator({
1156
- input: state.input,
1157
- system
1158
- }).subscribe({
1159
- next: value => {
1160
- self.send({
1161
- type: nextEventType,
1162
- data: value
1163
- });
1164
- },
1165
- error: err => {
1166
- self.send({
1167
- type: errorEventType,
1168
- data: err
1169
- });
1170
- },
1171
- complete: () => {
1172
- self.send({
1173
- type: completeEventType
1174
- });
1175
- }
1176
- });
1177
- },
1178
- getSnapshot: state => state.data,
955
+ getSnapshot: () => undefined,
1179
956
  getPersistedState: ({
1180
- status,
1181
- data,
1182
- input
1183
- }) => ({
1184
- status,
1185
- data,
1186
957
  input
1187
- }),
1188
- getStatus: state => state,
1189
- restoreState: state => ({
1190
- ...state,
1191
- subscription: undefined
1192
- })
958
+ }) => input
1193
959
  };
1194
960
  return logic;
1195
961
  }
1196
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
+ };
1197
971
  /**
1198
- * Creates event observable logic that listens to an observable
1199
- * that delivers event objects.
1200
- *
972
+ * An object that expresses the actor logic in reaction to received events,
973
+ * as well as an optionally emitted stream of values.
1201
974
  *
1202
- * @param lazyObservable A function that creates an observable
1203
- * @returns Event observable logic
975
+ * @template TReceived The received event
976
+ * @template TSnapshot The emitted value
1204
977
  */
1205
978
 
1206
- function fromEventObservable(lazyObservable) {
1207
- const errorEventType = '$$xstate.error';
1208
- const completeEventType = '$$xstate.complete';
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
+ }
1209
985
 
1210
- // TODO: event types
1211
- const logic = {
1212
- config: lazyObservable,
1213
- transition: (state, event) => {
1214
- if (state.status !== 'active') {
1215
- return state;
1216
- }
1217
- switch (event.type) {
1218
- case errorEventType:
1219
- return {
1220
- ...state,
1221
- status: 'error',
1222
- input: undefined,
1223
- data: event.data,
1224
- subscription: undefined
1225
- };
1226
- case completeEventType:
1227
- return {
1228
- ...state,
1229
- status: 'done',
1230
- input: undefined,
1231
- subscription: undefined
1232
- };
1233
- case stopSignalType:
1234
- state.subscription.unsubscribe();
1235
- return {
1236
- ...state,
1237
- status: 'canceled',
1238
- input: undefined,
1239
- subscription: undefined
1240
- };
1241
- default:
1242
- return state;
1243
- }
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;
1244
999
  },
1245
- getInitialState: (_, input) => {
1246
- return {
1247
- subscription: undefined,
1248
- status: 'active',
1249
- data: undefined,
1250
- input
1251
- };
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;
1252
1020
  },
1253
- start: (state, {
1254
- self,
1255
- system
1256
- }) => {
1257
- if (state.status === 'done') {
1258
- // Do not restart a completed observable
1259
- return;
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);
1260
1027
  }
1261
- state.subscription = lazyObservable({
1262
- input: state.input,
1263
- system
1264
- }).subscribe({
1265
- next: value => {
1266
- self._parent?.send(value);
1267
- },
1268
- error: err => {
1269
- self.send({
1270
- type: errorEventType,
1271
- data: err
1272
- });
1273
- },
1274
- complete: () => {
1275
- self.send({
1276
- type: completeEventType
1277
- });
1278
- }
1279
- });
1280
1028
  },
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
- })
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
- });
1309
- },
1310
- transition: (state, event, {
1311
- self,
1312
- id,
1313
- system
1314
- }) => {
1315
- if (event.type === startSignalType) {
1316
- const sender = eventForParent => {
1317
- if (state.canceled) {
1318
- return;
1319
- }
1320
- self._parent?.send(eventForParent);
1321
- };
1322
- const receiver = newListener => {
1323
- state.receivers.add(newListener);
1324
- };
1325
- state.dispose = invokeCallback(sender, receiver, {
1326
- input: state.input,
1327
- system
1328
- });
1329
- if (isPromiseLike(state.dispose)) {
1330
- state.dispose.then(resolved => {
1331
- self._parent?.send(doneInvoke(id, resolved));
1332
- state.canceled = true;
1333
- }, errorData => {
1334
- state.canceled = true;
1335
- self._parent?.send(error(id, errorData));
1336
- });
1337
- }
1338
- return state;
1339
- }
1340
- if (event.type === stopSignalType) {
1341
- state.canceled = true;
1342
- if (isFunction(state.dispose)) {
1343
- state.dispose();
1344
- }
1345
- return state;
1346
- }
1347
- if (isSignal(event.type)) {
1348
- // TODO: unrecognized signal
1349
- return state;
1350
- }
1351
- if (!isSignal(event.type)) {
1352
- state.receivers.forEach(receiver => receiver(event));
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);
1055
+ },
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,
1122
+ id,
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`);
1151
+ }
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);
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);
1240
+ if (event.type === stopSignalType) {
1241
+ this._stopProcedure();
1242
+ this._complete();
1243
+ }
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;
1253
+ }
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);
1353
1342
  }
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;
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
+ }
1370
1374
  }
1371
1375
 
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
1376
  /**
1381
- * An object that expresses the actor logic in reaction to received events,
1382
- * 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.
1383
1378
  *
1384
- * @template TReceived The received event
1385
- * @template TSnapshot The emitted value
1379
+ * @param machine The machine to interpret
1380
+ * @param options Interpreter options
1386
1381
  */
1387
1382
 
1388
- function isSignal(eventType) {
1389
- return eventType === startSignalType || eventType === stopSignalType;
1390
- }
1391
- function isActorRef(item) {
1392
- return !!item && typeof item === 'object' && typeof item.send === 'function';
1393
- }
1394
-
1395
- // TODO: refactor the return type, this could be written in a better way
1396
- // but it's best to avoid unneccessary breaking changes now
1397
- // @deprecated use `interpret(actorLogic)` instead
1398
- function toActorRef(actorRefLike) {
1399
- return {
1400
- subscribe: () => ({
1401
- unsubscribe: () => void 0
1402
- }),
1403
- id: 'anonymous',
1404
- sessionId: '',
1405
- getSnapshot: () => undefined,
1406
- [symbolObservable]: function () {
1407
- return this;
1408
- },
1409
- status: ActorStatus.Running,
1410
- stop: () => void 0,
1411
- ...actorRefLike
1412
- };
1413
- }
1414
- const emptyLogic = fromTransition(_ => undefined, undefined);
1415
- function createEmptyActor() {
1416
- return interpret(emptyLogic);
1383
+ function interpret(logic, options) {
1384
+ const interpreter = new Interpreter(logic, options);
1385
+ return interpreter;
1417
1386
  }
1418
1387
 
1419
1388
  function invoke(invokeDef) {
@@ -1430,42 +1399,32 @@ function invoke(invokeDef) {
1430
1399
  src
1431
1400
  } = invokeDef;
1432
1401
  let resolvedInvokeAction;
1433
- 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
+ });
1434
1421
  resolvedInvokeAction = {
1435
1422
  type,
1436
1423
  params: {
1437
1424
  ...invokeDef,
1438
- ref: src
1425
+ ref
1439
1426
  }
1440
1427
  };
1441
- } else {
1442
- const referenced = resolveReferencedActor(state.machine.options.actors[src]);
1443
- if (!referenced) {
1444
- resolvedInvokeAction = {
1445
- type,
1446
- params: invokeDef
1447
- };
1448
- } else {
1449
- const input = 'input' in invokeDef ? invokeDef.input : referenced.input;
1450
- const ref = interpret(referenced.src, {
1451
- id,
1452
- src,
1453
- parent: actorContext?.self,
1454
- systemId: invokeDef.systemId,
1455
- input: typeof input === 'function' ? input({
1456
- context: state.context,
1457
- event,
1458
- self: actorContext?.self
1459
- }) : input
1460
- });
1461
- resolvedInvokeAction = {
1462
- type,
1463
- params: {
1464
- ...invokeDef,
1465
- ref
1466
- }
1467
- };
1468
- }
1469
1428
  }
1470
1429
  const actorRef = resolvedInvokeAction.params.ref;
1471
1430
  const invokedState = cloneState(state, {
@@ -1577,7 +1536,7 @@ function evaluateGuard(guard, context, event, state) {
1577
1536
  const {
1578
1537
  machine
1579
1538
  } = state;
1580
- const predicate = machine?.options?.guards?.[guard.type] ?? guard.predicate;
1539
+ const predicate = machine?.implementations?.guards?.[guard.type] ?? guard.predicate;
1581
1540
  if (!predicate) {
1582
1541
  throw new Error(`Guard '${guard.type}' is not implemented.'.`);
1583
1542
  }
@@ -1590,14 +1549,28 @@ function evaluateGuard(guard, context, event, state) {
1590
1549
  });
1591
1550
  }
1592
1551
  function toGuardDefinition(guardConfig, getPredicate) {
1552
+ // TODO: check for cycles and consider a refactor to more lazily evaluated guards
1553
+ // TODO: resolve this more recursively: https://github.com/statelyai/xstate/pull/4064#discussion_r1229915724
1593
1554
  if (isString(guardConfig)) {
1594
- return {
1595
- type: guardConfig,
1596
- predicate: getPredicate?.(guardConfig) || undefined,
1597
- params: {
1598
- type: guardConfig
1599
- }
1600
- };
1555
+ const predicateOrDef = getPredicate?.(guardConfig);
1556
+ if (isFunction(predicateOrDef)) {
1557
+ return {
1558
+ type: guardConfig,
1559
+ predicate: predicateOrDef,
1560
+ params: {
1561
+ type: guardConfig
1562
+ }
1563
+ };
1564
+ } else if (predicateOrDef) {
1565
+ return predicateOrDef;
1566
+ } else {
1567
+ return {
1568
+ type: guardConfig,
1569
+ params: {
1570
+ type: guardConfig
1571
+ }
1572
+ };
1573
+ }
1601
1574
  }
1602
1575
  if (isFunction(guardConfig)) {
1603
1576
  return {
@@ -1609,12 +1582,24 @@ function toGuardDefinition(guardConfig, getPredicate) {
1609
1582
  }
1610
1583
  };
1611
1584
  }
1612
- return {
1613
- type: guardConfig.type,
1614
- params: guardConfig.params || guardConfig,
1615
- children: guardConfig.children?.map(childGuard => toGuardDefinition(childGuard, getPredicate)),
1616
- predicate: getPredicate?.(guardConfig.type) || guardConfig.predicate
1617
- };
1585
+ const predicateOrDef = getPredicate?.(guardConfig.type);
1586
+ if (isFunction(predicateOrDef)) {
1587
+ return {
1588
+ type: guardConfig.type,
1589
+ params: guardConfig.params || guardConfig,
1590
+ children: guardConfig.children?.map(childGuard => toGuardDefinition(childGuard, getPredicate)),
1591
+ predicate: getPredicate?.(guardConfig.type) || guardConfig.predicate
1592
+ };
1593
+ } else if (predicateOrDef) {
1594
+ return predicateOrDef;
1595
+ } else {
1596
+ return {
1597
+ type: guardConfig.type,
1598
+ params: guardConfig.params || guardConfig,
1599
+ children: guardConfig.children?.map(childGuard => toGuardDefinition(childGuard, getPredicate)),
1600
+ predicate: guardConfig.predicate
1601
+ };
1602
+ }
1618
1603
  }
1619
1604
 
1620
1605
  function getOutput(configuration, context, event) {
@@ -1813,7 +1798,7 @@ function formatTransition(stateNode, transitionConfig) {
1813
1798
  const reenter = transitionConfig.reenter ?? false;
1814
1799
  const {
1815
1800
  guards
1816
- } = stateNode.machine.options;
1801
+ } = stateNode.machine.implementations;
1817
1802
  const target = resolveTarget(stateNode, normalizedTarget);
1818
1803
  const transition = {
1819
1804
  ...transitionConfig,
@@ -1904,7 +1889,7 @@ function formatInitialTransition(stateNode, _target) {
1904
1889
  return formatTransition(stateNode, {
1905
1890
  target: toArray(_target.target).map(t => {
1906
1891
  if (isString(t)) {
1907
- return isStateId(t) ? t : `${stateNode.machine.delimiter}${t}`;
1892
+ return isStateId(t) ? t : `${STATE_DELIMITER}${t}`;
1908
1893
  }
1909
1894
  return t;
1910
1895
  }),
@@ -1924,7 +1909,7 @@ function resolveTarget(stateNode, targets) {
1924
1909
  if (isStateId(target)) {
1925
1910
  return stateNode.machine.getStateNodeById(target);
1926
1911
  }
1927
- const isInternalTarget = target[0] === stateNode.machine.delimiter;
1912
+ const isInternalTarget = target[0] === STATE_DELIMITER;
1928
1913
  // If internal target is defined on machine,
1929
1914
  // do not include machine key on target
1930
1915
  if (isInternalTarget && !stateNode.parent) {
@@ -2007,7 +1992,7 @@ function getStateNodeByPath(stateNode, statePath) {
2007
1992
  // throw e;
2008
1993
  }
2009
1994
  }
2010
- const arrayStatePath = toStatePath(statePath, stateNode.machine.delimiter).slice();
1995
+ const arrayStatePath = toStatePath(statePath).slice();
2011
1996
  let currentStateNode = stateNode;
2012
1997
  while (arrayStatePath.length) {
2013
1998
  const key = arrayStatePath.shift();
@@ -2025,7 +2010,7 @@ function getStateNodeByPath(stateNode, statePath) {
2025
2010
  * @param state The state value or State instance
2026
2011
  */
2027
2012
  function getStateNodes(stateNode, state) {
2028
- const stateValue = state instanceof State ? state.value : toStateValue(state, stateNode.machine.delimiter);
2013
+ const stateValue = state instanceof State ? state.value : toStateValue(state);
2029
2014
  if (isString(stateValue)) {
2030
2015
  return [stateNode, stateNode.states[stateValue]];
2031
2016
  }
@@ -2218,40 +2203,18 @@ function computeExitSet(transitions, configuration, historyValue) {
2218
2203
  * @param mutConfiguration
2219
2204
  */
2220
2205
 
2221
- function microstep(transitions, currentState, actorCtx, event) {
2222
- const {
2223
- machine
2224
- } = currentState;
2225
- // Transition will "apply" if:
2226
- // - the state node is the initial state (there is no current state)
2227
- // - OR there are transitions
2228
- const willTransition = currentState._initial || transitions.length > 0;
2206
+ function microstep(transitions, currentState, actorCtx, event, isInitial) {
2229
2207
  const mutConfiguration = new Set(currentState.configuration);
2230
- if (!currentState._initial && !willTransition) {
2231
- const inertState = cloneState(currentState, {});
2232
- inertState.changed = false;
2233
- return inertState;
2234
- }
2235
- const [microstate, actions] = microstepProcedure(currentState._initial ? [{
2236
- target: [...currentState.configuration].filter(isAtomicStateNode),
2237
- source: machine.root,
2238
- reenter: true,
2239
- actions: [],
2240
- eventType: null,
2241
- toJSON: null // TODO: fix
2242
- }] : transitions, currentState, mutConfiguration, event, actorCtx);
2243
- const {
2244
- context
2245
- } = microstate;
2246
- const nextState = cloneState(microstate, {
2247
- value: {},
2248
- // TODO: make optional
2249
- transitions
2208
+ if (!transitions.length) {
2209
+ return currentState;
2210
+ }
2211
+ const microstate = microstepProcedure(transitions, currentState, mutConfiguration, event, actorCtx, isInitial);
2212
+ return cloneState(microstate, {
2213
+ value: {} // TODO: make optional
2250
2214
  });
2251
- nextState.changed = currentState._initial ? undefined : !stateValuesEqual(nextState.value, currentState.value) || actions.length > 0 || context !== currentState.context;
2252
- return nextState;
2253
2215
  }
2254
- function microstepProcedure(transitions, currentState, mutConfiguration, event, actorCtx) {
2216
+
2217
+ function microstepProcedure(transitions, currentState, mutConfiguration, event, actorCtx, isInitial) {
2255
2218
  const actions = [];
2256
2219
  const historyValue = {
2257
2220
  ...currentState.historyValue
@@ -2260,7 +2223,7 @@ function microstepProcedure(transitions, currentState, mutConfiguration, event,
2260
2223
  const internalQueue = [...currentState._internalQueue];
2261
2224
 
2262
2225
  // Exit states
2263
- if (!currentState._initial) {
2226
+ if (!isInitial) {
2264
2227
  exitStates(filteredTransitions, mutConfiguration, historyValue, actions);
2265
2228
  }
2266
2229
 
@@ -2268,7 +2231,7 @@ function microstepProcedure(transitions, currentState, mutConfiguration, event,
2268
2231
  actions.push(...filteredTransitions.flatMap(t => t.actions));
2269
2232
 
2270
2233
  // Enter states
2271
- enterStates(event, filteredTransitions, mutConfiguration, actions, internalQueue, currentState, historyValue);
2234
+ enterStates(event, filteredTransitions, mutConfiguration, actions, internalQueue, currentState, historyValue, isInitial);
2272
2235
  const nextConfiguration = [...mutConfiguration];
2273
2236
  const done = isInFinalState(nextConfiguration);
2274
2237
  if (done) {
@@ -2276,10 +2239,10 @@ function microstepProcedure(transitions, currentState, mutConfiguration, event,
2276
2239
  actions.push(...finalActions);
2277
2240
  }
2278
2241
  try {
2279
- const [nextState, resolvedActions] = resolveActionsAndContext(actions, event, currentState, actorCtx);
2242
+ const nextState = resolveActionsAndContext(actions, event, currentState, actorCtx);
2280
2243
  const output = done ? getOutput(nextConfiguration, nextState.context, event) : undefined;
2281
2244
  internalQueue.push(...nextState._internalQueue);
2282
- return [cloneState(currentState, {
2245
+ return cloneState(currentState, {
2283
2246
  configuration: nextConfiguration,
2284
2247
  historyValue,
2285
2248
  _internalQueue: internalQueue,
@@ -2287,20 +2250,20 @@ function microstepProcedure(transitions, currentState, mutConfiguration, event,
2287
2250
  done,
2288
2251
  output,
2289
2252
  children: nextState.children
2290
- }), resolvedActions];
2253
+ });
2291
2254
  } catch (e) {
2292
2255
  // TODO: Refactor this once proper error handling is implemented.
2293
2256
  // See https://github.com/statelyai/rfcs/pull/4
2294
2257
  throw e;
2295
2258
  }
2296
2259
  }
2297
- function enterStates(event, filteredTransitions, mutConfiguration, actions, internalQueue, currentState, historyValue) {
2260
+ function enterStates(event, filteredTransitions, mutConfiguration, actions, internalQueue, currentState, historyValue, isInitial) {
2298
2261
  const statesToEnter = new Set();
2299
2262
  const statesForDefaultEntry = new Set();
2300
2263
  computeEntrySet(filteredTransitions, historyValue, statesForDefaultEntry, statesToEnter);
2301
2264
 
2302
2265
  // In the initial state, the root state node is "entered".
2303
- if (currentState._initial) {
2266
+ if (isInitial) {
2304
2267
  statesForDefaultEntry.add(currentState.machine.root);
2305
2268
  }
2306
2269
  for (const stateNodeToEnter of [...statesToEnter].sort((a, b) => a.order - b.order)) {
@@ -2425,7 +2388,7 @@ function exitStates(transitions, mutConfiguration, historyValue, actions) {
2425
2388
  }
2426
2389
  }
2427
2390
  for (const s of statesToExit) {
2428
- actions.push(...s.exit.flat(), ...s.invoke.map(def => stop(def.id)));
2391
+ actions.push(...s.exit, ...s.invoke.map(def => stop(def.id)));
2429
2392
  mutConfiguration.delete(s);
2430
2393
  }
2431
2394
  }
@@ -2433,11 +2396,9 @@ function resolveActionsAndContext(actions, event, currentState, actorCtx) {
2433
2396
  const {
2434
2397
  machine
2435
2398
  } = currentState;
2436
- const resolvedActions = [];
2437
2399
  const raiseActions = [];
2438
2400
  let intermediateState = currentState;
2439
2401
  function handleAction(action) {
2440
- resolvedActions.push(action);
2441
2402
  if (actorCtx?.self.status === ActorStatus.Running) {
2442
2403
  action.execute?.(actorCtx);
2443
2404
  } else {
@@ -2445,7 +2406,7 @@ function resolveActionsAndContext(actions, event, currentState, actorCtx) {
2445
2406
  }
2446
2407
  }
2447
2408
  function resolveAction(actionObject) {
2448
- const executableActionObject = resolveActionObject(actionObject, machine.options.actions);
2409
+ const executableActionObject = resolveActionObject(actionObject, machine.implementations.actions);
2449
2410
  if (isDynamicAction(executableActionObject)) {
2450
2411
  const [nextState, resolvedAction] = executableActionObject.resolve(event, {
2451
2412
  state: intermediateState,
@@ -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
 
@@ -2470,9 +2431,9 @@ function resolveActionsAndContext(actions, event, currentState, actorCtx) {
2470
2431
  for (const actionObject of actions) {
2471
2432
  resolveAction(actionObject);
2472
2433
  }
2473
- return [cloneState(intermediateState, {
2434
+ return cloneState(intermediateState, {
2474
2435
  _internalQueue: raiseActions.map(a => a.params.event)
2475
- }), resolvedActions];
2436
+ });
2476
2437
  }
2477
2438
  function macrostep(state, event, actorCtx) {
2478
2439
  let nextState = state;
@@ -2480,7 +2441,7 @@ function macrostep(state, event, actorCtx) {
2480
2441
 
2481
2442
  // Handle stop event
2482
2443
  if (event.type === stopSignalType) {
2483
- nextState = stopStep(event, nextState, actorCtx)[0];
2444
+ nextState = stopStep(event, nextState, actorCtx);
2484
2445
  states.push(nextState);
2485
2446
  return {
2486
2447
  state: nextState,
@@ -2493,7 +2454,7 @@ function macrostep(state, event, actorCtx) {
2493
2454
  // Determine the next state based on the next microstep
2494
2455
  if (nextEvent.type !== init) {
2495
2456
  const transitions = selectTransitions(nextEvent, nextState);
2496
- nextState = microstep(transitions, state, actorCtx, nextEvent);
2457
+ nextState = microstep(transitions, state, actorCtx, nextEvent, false);
2497
2458
  states.push(nextState);
2498
2459
  }
2499
2460
  while (!nextState.done) {
@@ -2504,12 +2465,12 @@ function macrostep(state, event, actorCtx) {
2504
2465
  } else {
2505
2466
  nextEvent = nextState._internalQueue[0];
2506
2467
  const transitions = selectTransitions(nextEvent, nextState);
2507
- nextState = microstep(transitions, nextState, actorCtx, nextEvent);
2468
+ nextState = microstep(transitions, nextState, actorCtx, nextEvent, false);
2508
2469
  nextState._internalQueue.shift();
2509
2470
  states.push(nextState);
2510
2471
  }
2511
2472
  } else {
2512
- nextState = microstep(enabledTransitions, nextState, actorCtx, nextEvent);
2473
+ nextState = microstep(enabledTransitions, nextState, actorCtx, nextEvent, false);
2513
2474
  states.push(nextState);
2514
2475
  }
2515
2476
  }
@@ -2563,20 +2524,6 @@ function resolveStateValue(rootNode, stateValue) {
2563
2524
  const configuration = getConfiguration(getStateNodes(rootNode, stateValue));
2564
2525
  return getStateValue(rootNode, [...configuration]);
2565
2526
  }
2566
- function stateValuesEqual(a, b) {
2567
- if (a === b) {
2568
- return true;
2569
- }
2570
- if (a === undefined || b === undefined) {
2571
- return false;
2572
- }
2573
- if (isString(a) || isString(b)) {
2574
- return a === b;
2575
- }
2576
- const aKeys = Object.keys(a);
2577
- const bKeys = Object.keys(b);
2578
- return aKeys.length === bKeys.length && aKeys.every(key => stateValuesEqual(a[key], b[key]));
2579
- }
2580
2527
  function getInitialConfiguration(rootNode) {
2581
2528
  const configuration = [];
2582
2529
  const initialTransition = rootNode.initial;
@@ -2599,15 +2546,6 @@ class State {
2599
2546
  */
2600
2547
  // TODO: add an explicit type for `output`
2601
2548
 
2602
- /**
2603
- * Indicates whether the state has changed from the previous state. A state is considered "changed" if:
2604
- *
2605
- * - Its value is not equal to its previous value, or:
2606
- * - It has any new actions (side-effects) to execute.
2607
- *
2608
- * An initial state (with no history) will return `undefined`.
2609
- */
2610
-
2611
2549
  /**
2612
2550
  * The enabled state nodes representative of the state value.
2613
2551
  */
@@ -2630,7 +2568,6 @@ class State {
2630
2568
  meta: {},
2631
2569
  configuration: [],
2632
2570
  // TODO: fix,
2633
- transitions: [],
2634
2571
  children: {}
2635
2572
  }, machine);
2636
2573
  }
@@ -2660,8 +2597,6 @@ class State {
2660
2597
  this.context = void 0;
2661
2598
  this.historyValue = {};
2662
2599
  this._internalQueue = void 0;
2663
- this._initial = false;
2664
- this.changed = void 0;
2665
2600
  this.configuration = void 0;
2666
2601
  this.children = void 0;
2667
2602
  this.context = config.context;
@@ -2682,12 +2617,12 @@ class State {
2682
2617
  * @param stateValue
2683
2618
  * @param delimiter The character(s) that separate each subpath in the string state node path.
2684
2619
  */
2685
- toStrings(stateValue = this.value, delimiter = '.') {
2620
+ toStrings(stateValue = this.value) {
2686
2621
  if (isString(stateValue)) {
2687
2622
  return [stateValue];
2688
2623
  }
2689
2624
  const valueKeys = Object.keys(stateValue);
2690
- return valueKeys.concat(...valueKeys.map(key => this.toStrings(stateValue[key], delimiter).map(s => key + delimiter + s)));
2625
+ return valueKeys.concat(...valueKeys.map(key => this.toStrings(stateValue[key]).map(s => key + STATE_DELIMITER + s)));
2691
2626
  }
2692
2627
  toJSON() {
2693
2628
  const {
@@ -2792,11 +2727,14 @@ function stop(actorRef) {
2792
2727
  actor
2793
2728
  }
2794
2729
  }, (event, {
2795
- state
2730
+ state,
2731
+ actorContext
2796
2732
  }) => {
2797
2733
  const actorRefOrString = isFunction(actor) ? actor({
2798
2734
  context: state.context,
2799
- event
2735
+ event,
2736
+ self: actorContext?.self ?? {},
2737
+ system: actorContext?.system
2800
2738
  }) : actor;
2801
2739
  const actorRef = typeof actorRefOrString === 'string' ? state.children[actorRefOrString] : actorRefOrString;
2802
2740
  let children = state.children;
@@ -2880,61 +2818,60 @@ function log(expr = defaultLogExpr, label) {
2880
2818
  });
2881
2819
  }
2882
2820
 
2883
- function createSpawner(self, machine, context, event, mutCapturedActions) {
2884
- return (src, options = {}) => {
2821
+ function createSpawner(actorContext, {
2822
+ machine,
2823
+ context
2824
+ }, event, spawnedChildren) {
2825
+ const spawn = (src, options = {}) => {
2885
2826
  const {
2886
2827
  systemId
2887
2828
  } = options;
2888
- if (isString(src)) {
2889
- const referenced = resolveReferencedActor(machine.options.actors[src]);
2890
- if (referenced) {
2891
- const resolvedName = options.id ?? 'anon'; // TODO: better name
2892
- const input = 'input' in options ? options.input : referenced.input;
2893
-
2894
- // TODO: this should also receive `src`
2895
- const actorRef = interpret(referenced.src, {
2896
- id: resolvedName,
2897
- parent: self,
2898
- input: typeof input === 'function' ? input({
2899
- context,
2900
- event,
2901
- self
2902
- }) : input
2903
- });
2904
- mutCapturedActions.push(invoke({
2905
- id: actorRef.id,
2906
- // @ts-ignore TODO: fix types
2907
- src: actorRef,
2908
- // TODO
2909
- ref: actorRef,
2910
- meta: undefined,
2911
- input,
2912
- systemId
2913
- }));
2914
- return actorRef; // TODO: fix types
2915
- }
2916
-
2917
- throw new Error(`Actor logic '${src}' not implemented in machine '${machine.id}'`);
2829
+ if (typeof src === 'string') {
2830
+ const referenced = resolveReferencedActor(machine.implementations.actors[src]);
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;
2918
2849
  } else {
2919
2850
  // TODO: this should also receive `src`
2920
- // TODO: instead of anonymous, it should be a unique stable ID
2921
- const actorRef = interpret(src, {
2922
- id: options.id || 'anonymous',
2923
- parent: self,
2851
+ return interpret(src, {
2852
+ id: options.id,
2853
+ parent: actorContext.self,
2924
2854
  input: options.input,
2925
2855
  systemId
2926
2856
  });
2927
- mutCapturedActions.push(invoke({
2928
- // @ts-ignore TODO: fix types
2929
- src: actorRef,
2930
- ref: actorRef,
2931
- id: actorRef.id,
2932
- meta: undefined,
2933
- input: options.input
2934
- }));
2935
- return actorRef; // TODO: fix types
2936
2857
  }
2937
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
+ };
2938
2875
  }
2939
2876
 
2940
2877
  /**
@@ -2953,15 +2890,15 @@ function assign(assignment) {
2953
2890
  action,
2954
2891
  actorContext
2955
2892
  }) => {
2956
- const capturedActions = [];
2957
2893
  if (!state.context) {
2958
2894
  throw new Error('Cannot assign to undefined `context`. Ensure that `context` is defined in the machine config.');
2959
2895
  }
2896
+ const spawnedChildren = {};
2960
2897
  const args = {
2961
2898
  context: state.context,
2962
2899
  event,
2963
2900
  action,
2964
- spawn: createSpawner(actorContext?.self, state.machine, state.context, event, capturedActions),
2901
+ spawn: createSpawner(actorContext, state, event, spawnedChildren),
2965
2902
  self: actorContext?.self ?? {},
2966
2903
  system: actorContext?.system
2967
2904
  };
@@ -2976,12 +2913,15 @@ function assign(assignment) {
2976
2913
  }
2977
2914
  const updatedContext = Object.assign({}, state.context, partialUpdate);
2978
2915
  return [cloneState(state, {
2979
- context: updatedContext
2916
+ context: updatedContext,
2917
+ children: Object.keys(spawnedChildren).length ? {
2918
+ ...state.children,
2919
+ ...spawnedChildren
2920
+ } : state.children
2980
2921
  }), {
2981
2922
  type: assign$1,
2982
2923
  params: {
2983
- context: updatedContext,
2984
- actions: capturedActions
2924
+ context: updatedContext
2985
2925
  }
2986
2926
  }];
2987
2927
  });
@@ -3017,7 +2957,7 @@ function raise(eventOrExpr, options) {
3017
2957
  self: actorContext?.self ?? {},
3018
2958
  system: actorContext?.system
3019
2959
  };
3020
- const delaysMap = state.machine.options.delays;
2960
+ const delaysMap = state.machine.implementations.delays;
3021
2961
 
3022
2962
  // TODO: helper function for resolving Expr
3023
2963
  if (typeof eventOrExpr === 'string') {
@@ -3059,7 +2999,7 @@ function choose(guards) {
3059
2999
  state
3060
3000
  }) => {
3061
3001
  const matchedActions = guards.find(condition => {
3062
- const guard = condition.guard && toGuardDefinition(condition.guard, guardType => state.machine.options.guards[guardType]);
3002
+ const guard = condition.guard && toGuardDefinition(condition.guard, guardType => state.machine.implementations.guards[guardType]);
3063
3003
  return !guard || evaluateGuard(guard, state.context, event, state);
3064
3004
  })?.actions;
3065
3005
  return [state, {
@@ -3092,9 +3032,6 @@ function pure(getActions) {
3092
3032
  });
3093
3033
  }
3094
3034
 
3095
- const initEvent = {
3096
- type: init
3097
- };
3098
3035
  function resolveActionObject(actionObject, actionFunctionMap) {
3099
3036
  if (isDynamicAction(actionObject)) {
3100
3037
  return actionObject;
@@ -3239,4 +3176,4 @@ function createInitEvent(input) {
3239
3176
  };
3240
3177
  }
3241
3178
 
3242
- export { toObserver as $, resolveActionsAndContext as A, microstep as B, error as C, isStateId as D, getStateNodeByPath as E, getPersistedState as F, resolveReferencedActor as G, interpret as H, createInitEvent as I, initEvent as J, matchesState as K, sendTo as L, sendParent as M, NULL_EVENT as N, forwardTo as O, Interpreter as P, ActorStatus as Q, doneInvoke as R, STATE_DELIMITER as S, assign as T, cancel as U, choose as V, log as W, pure as X, raise as Y, stop as Z, pathToStateValue as _, toArray as a, fromPromise as a0, fromObservable as a1, fromCallback as a2, fromEventObservable as a3, fromTransition as a4, stateIn as a5, not as a6, and as a7, or as a8, ActionTypes as a9, SpecialTargets as aa, startSignalType as ab, stopSignalType as ac, startSignal as ad, stopSignal as ae, isSignal as af, isActorRef as ag, toActorRef as ah, createEmptyActor as ai, toGuardDefinition as aj, actionTypes as ak, resolveActionObject as al, toActionObject as am, after as an, done as ao, send as ap, escalate as aq, toTransitionConfigArray as b, formatTransition as c, memo as d, evaluateGuard as e, formatTransitions as f, flatten as g, createInvokeId as h, isString as i, invoke$1 as j, getDelayedTransitions as k, formatInitialTransition as l, mapValues as m, getCandidates as n, toInvokeConfig as o, createSpawner as p, getConfiguration as q, getStateNodes as r, resolveStateValue as s, toActionObjects as t, isInFinalState as u, State as v, isErrorEvent as w, macrostep as x, transitionNode as y, getInitialConfiguration as z };
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 };