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