xstate 5.0.0-beta.49 → 5.0.0-beta.50

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/actions/dist/xstate-actions.cjs.js +2 -2
  2. package/actions/dist/xstate-actions.development.cjs.js +2 -2
  3. package/actions/dist/xstate-actions.development.esm.js +2 -2
  4. package/actions/dist/xstate-actions.esm.js +2 -2
  5. package/actions/dist/xstate-actions.umd.min.js +1 -1
  6. package/actions/dist/xstate-actions.umd.min.js.map +1 -1
  7. package/actors/dist/xstate-actors.cjs.js +2 -4
  8. package/actors/dist/xstate-actors.development.cjs.js +2 -4
  9. package/actors/dist/xstate-actors.development.esm.js +2 -4
  10. package/actors/dist/xstate-actors.esm.js +2 -4
  11. package/actors/dist/xstate-actors.umd.min.js +1 -1
  12. package/actors/dist/xstate-actors.umd.min.js.map +1 -1
  13. package/dist/declarations/src/actors/callback.d.ts +5 -5
  14. package/dist/declarations/src/actors/observable.d.ts +5 -5
  15. package/dist/declarations/src/actors/promise.d.ts +2 -2
  16. package/dist/declarations/src/actors/transition.d.ts +3 -3
  17. package/dist/declarations/src/interpreter.d.ts +1 -0
  18. package/dist/declarations/src/setup.d.ts +2 -2
  19. package/dist/declarations/src/types.d.ts +5 -3
  20. package/dist/{raise-e4cc6d4f.esm.js → raise-32ec7226.esm.js} +92 -27
  21. package/dist/{raise-1873c645.development.esm.js → raise-6c05c91b.development.esm.js} +92 -27
  22. package/dist/{raise-8f9c4a5a.cjs.js → raise-8176cd35.cjs.js} +91 -27
  23. package/dist/{raise-495f4b9f.development.cjs.js → raise-dc9c2c58.development.cjs.js} +91 -27
  24. package/dist/{send-0a381ca2.development.esm.js → send-2b001f05.development.esm.js} +2 -7
  25. package/dist/{send-8ed5c8b2.cjs.js → send-7f3db830.cjs.js} +2 -7
  26. package/dist/{send-22880315.esm.js → send-88351a33.esm.js} +2 -7
  27. package/dist/{send-8d30b415.development.cjs.js → send-df1c8ef2.development.cjs.js} +2 -7
  28. package/dist/xstate.cjs.js +3 -13
  29. package/dist/xstate.development.cjs.js +3 -13
  30. package/dist/xstate.development.esm.js +5 -15
  31. package/dist/xstate.esm.js +5 -15
  32. package/dist/xstate.umd.min.js +1 -1
  33. package/dist/xstate.umd.min.js.map +1 -1
  34. package/guards/dist/xstate-guards.cjs.js +1 -1
  35. package/guards/dist/xstate-guards.development.cjs.js +1 -1
  36. package/guards/dist/xstate-guards.development.esm.js +1 -1
  37. package/guards/dist/xstate-guards.esm.js +1 -1
  38. package/guards/dist/xstate-guards.umd.min.js.map +1 -1
  39. package/package.json +1 -1
@@ -1,5 +1,5 @@
1
- import { ActorLogic, ActorRefFrom, ActorSystem, AnyActorSystem, Snapshot } from "../types.js";
2
1
  import { XSTATE_STOP } from "../constants.js";
2
+ import { ActorLogic, ActorRefFrom, ActorSystem, AnyActorSystem, NonReducibleUnknown, Snapshot } from "../types.js";
3
3
  export type PromiseSnapshot<TOutput, TInput> = Snapshot<TOutput> & {
4
4
  input: TInput | undefined;
5
5
  };
@@ -64,7 +64,7 @@ export type PromiseActorRef<TOutput> = ActorRefFrom<PromiseActorLogic<TOutput, u
64
64
  * // }
65
65
  * ```
66
66
  */
67
- export declare function fromPromise<TOutput, TInput = unknown>(promiseCreator: ({ input, system }: {
67
+ export declare function fromPromise<TOutput, TInput = NonReducibleUnknown>(promiseCreator: ({ input, system }: {
68
68
  /**
69
69
  * Data that was provided to the promise actor
70
70
  */
@@ -1,8 +1,8 @@
1
- import { ActorLogic, ActorScope, ActorSystem, EventObject, ActorRefFrom, AnyActorSystem, Snapshot } from "../types.js";
1
+ import { ActorLogic, ActorScope, ActorSystem, EventObject, ActorRefFrom, AnyActorSystem, Snapshot, NonReducibleUnknown } from "../types.js";
2
2
  export type TransitionSnapshot<TContext> = Snapshot<undefined> & {
3
3
  context: TContext;
4
4
  };
5
- export type TransitionActorLogic<TContext, TEvent extends EventObject, TInput> = ActorLogic<TransitionSnapshot<TContext>, TEvent, TInput, AnyActorSystem>;
5
+ export type TransitionActorLogic<TContext, TEvent extends EventObject, TInput extends NonReducibleUnknown> = ActorLogic<TransitionSnapshot<TContext>, TEvent, TInput, AnyActorSystem>;
6
6
  export type TransitionActorRef<TContext, TEvent extends EventObject> = ActorRefFrom<TransitionActorLogic<TransitionSnapshot<TContext>, TEvent, unknown>>;
7
7
  /**
8
8
  * Returns actor logic given a transition function and its initial state.
@@ -62,7 +62,7 @@ export type TransitionActorRef<TContext, TEvent extends EventObject> = ActorRefF
62
62
  * // }
63
63
  * ```
64
64
  */
65
- export declare function fromTransition<TContext, TEvent extends EventObject, TSystem extends ActorSystem<any>, TInput>(transition: (state: TContext, event: TEvent, actorScope: ActorScope<TransitionSnapshot<TContext>, TEvent, TSystem>) => TContext, initialContext: TContext | (({ input, self }: {
65
+ export declare function fromTransition<TContext, TEvent extends EventObject, TSystem extends ActorSystem<any>, TInput extends NonReducibleUnknown>(transition: (state: TContext, event: TEvent, actorScope: ActorScope<TransitionSnapshot<TContext>, TEvent, TSystem>) => TContext, initialContext: TContext | (({ input, self }: {
66
66
  input: TInput;
67
67
  self: TransitionActorRef<TContext, TEvent>;
68
68
  }) => TContext)): TransitionActorLogic<TContext, TEvent, TInput>;
@@ -124,6 +124,7 @@ export declare class Actor<TLogic extends AnyActorLogic> implements ActorRef<Eve
124
124
  */
125
125
  stop(): this;
126
126
  private _complete;
127
+ private _reportError;
127
128
  private _error;
128
129
  private _stopProcedure;
129
130
  /**
@@ -1,7 +1,7 @@
1
1
  import { StateMachine } from "./StateMachine.js";
2
2
  import { GuardPredicate } from "./guards.js";
3
3
  import { ResolveTypegenMeta, TypegenDisabled } from "./typegenTypes.js";
4
- import { ActionFunction, AnyActorLogic, AnyActorRef, AnyEventObject, Cast, ConditionalRequired, DelayConfig, Invert, IsNever, MachineConfig, MachineContext, NonReducibleUnknown, ParameterizedObject, SetupTypes, StateSchema, ToChildren, Values } from "./types.js";
4
+ import { ActionFunction, AnyActorLogic, AnyActorRef, AnyEventObject, Cast, ConditionalRequired, DelayConfig, Invert, IsNever, MachineConfig, MachineContext, NonReducibleUnknown, ParameterizedObject, SetupTypes, StateSchema, ToChildren, UnknownActorLogic, Values } from "./types.js";
5
5
  type ToParameterizedObject<TParameterizedMap extends Record<string, ParameterizedObject['params'] | undefined>> = Values<{
6
6
  [K in keyof TParameterizedMap & string]: {
7
7
  type: K;
@@ -33,7 +33,7 @@ type ToStateValue<T extends StateSchema> = T extends {
33
33
  type: 'parallel';
34
34
  } ? true : false> : never) : {};
35
35
  export declare function setup<TContext extends MachineContext, TEvent extends AnyEventObject, // TODO: consider using a stricter `EventObject` here
36
- TActors extends Record<Values<TChildrenMap>, AnyActorLogic>, TActions extends Record<string, ParameterizedObject['params'] | undefined>, TGuards extends Record<string, ParameterizedObject['params'] | undefined>, TDelay extends string, TTag extends string, TInput, TOutput extends NonReducibleUnknown, TChildrenMap extends Record<string, string> = never>({ actors, actions, guards, delays }: {
36
+ TActors extends Record<Values<TChildrenMap>, UnknownActorLogic>, TActions extends Record<string, ParameterizedObject['params'] | undefined>, TGuards extends Record<string, ParameterizedObject['params'] | undefined>, TDelay extends string, TTag extends string, TInput, TOutput extends NonReducibleUnknown, TChildrenMap extends Record<string, string> = never>({ actors, actions, guards, delays }: {
37
37
  types?: SetupTypes<TContext, TEvent, TChildrenMap, TTag, TInput, TOutput>;
38
38
  actors?: TActors;
39
39
  actions?: {
@@ -736,7 +736,7 @@ export interface ActorRef<TEvent extends EventObject, TSnapshot extends Snapshot
736
736
  id: string;
737
737
  sessionId: string;
738
738
  send: (event: TEvent) => void;
739
- start?: () => void;
739
+ start: () => void;
740
740
  getSnapshot: () => TSnapshot;
741
741
  getPersistedState: () => Snapshot<unknown>;
742
742
  stop: () => void;
@@ -800,7 +800,7 @@ export type Snapshot<TOutput> = {
800
800
  * @template TInput - The type of the input.
801
801
  * @template TSystem - The type of the actor system.
802
802
  */
803
- export interface ActorLogic<TSnapshot extends Snapshot<unknown>, TEvent extends EventObject, TInput = unknown, TSystem extends ActorSystem<any> = ActorSystem<any>> {
803
+ export interface ActorLogic<TSnapshot extends Snapshot<unknown>, TEvent extends EventObject, TInput = NonReducibleUnknown, TSystem extends ActorSystem<any> = ActorSystem<any>> {
804
804
  /** The initial setup/configuration used to create the actor logic. */
805
805
  config?: unknown;
806
806
  /**
@@ -811,7 +811,7 @@ export interface ActorLogic<TSnapshot extends Snapshot<unknown>, TEvent extends
811
811
  * @param ctx - The actor scope.
812
812
  * @returns The new state.
813
813
  */
814
- transition: (state: TSnapshot, message: TEvent, ctx: ActorScope<TSnapshot, TEvent, TSystem>) => TSnapshot;
814
+ transition: (snapshot: TSnapshot, message: TEvent, ctx: ActorScope<TSnapshot, TEvent, TSystem>) => TSnapshot;
815
815
  /**
816
816
  * Called to provide the initial state of the actor.
817
817
  * @param actorScope - The actor scope.
@@ -845,6 +845,8 @@ export type AnyActorLogic = ActorLogic<any, // snapshot
845
845
  any, // event
846
846
  any, // input
847
847
  any>;
848
+ export type UnknownActorLogic = ActorLogic<any, // this is invariant and it's hard to figure out a better default than `any`
849
+ EventObject, NonReducibleUnknown, ActorSystem<any>>;
848
850
  export type SnapshotFrom<T> = ReturnTypeOrValue<T> extends infer R ? R extends ActorRef<infer _, infer TSnapshot> ? TSnapshot : R extends Actor<infer TLogic> ? SnapshotFrom<TLogic> : R extends ActorLogic<infer _, infer __, infer ___, infer ____> ? ReturnType<R['transition']> : R extends ActorScope<infer TSnapshot, infer _, infer __> ? TSnapshot : never : never;
849
851
  export type EventFromLogic<TLogic extends ActorLogic<any, any, any, any>> = TLogic extends ActorLogic<infer _, infer TEvent, infer __, infer _____> ? TEvent : never;
850
852
  type ResolveEventType<T> = ReturnTypeOrValue<T> extends infer R ? R extends StateMachine<infer _TContext, infer TEvent, infer _TChildren, infer _TActor, infer _TAction, infer _TGuard, infer _TDelay, infer _TStateValue, infer _TTag, infer _TInput, infer _TOutput, infer _TResolvedTypesMeta> ? TEvent : R extends MachineSnapshot<infer _TContext, infer TEvent, infer _TChildren, infer _TTag, infer _TOutput, infer _TResolvedTypesMeta> ? TEvent : R extends ActorRef<infer TEvent, infer _> ? TEvent : never : never;
@@ -449,7 +449,18 @@ class Actor {
449
449
  }
450
450
  }
451
451
  _initState(persistedState) {
452
- this._state = persistedState ? this.logic.restoreState ? this.logic.restoreState(persistedState, this._actorScope) : persistedState : this.logic.getInitialState(this._actorScope, this.options?.input);
452
+ try {
453
+ this._state = persistedState ? this.logic.restoreState ? this.logic.restoreState(persistedState, this._actorScope) : persistedState : this.logic.getInitialState(this._actorScope, this.options?.input);
454
+ } catch (err) {
455
+ // if we get here then it means that we assign a value to this._state that is not of the correct type
456
+ // we can't get the true `TSnapshot & { status: 'error'; }`, it's impossible
457
+ // so right now this is a lie of sorts
458
+ this._state = {
459
+ status: 'error',
460
+ output: undefined,
461
+ error: err
462
+ };
463
+ }
453
464
  }
454
465
  update(snapshot, event) {
455
466
  // Update state
@@ -458,17 +469,46 @@ class Actor {
458
469
  // Execute deferred effects
459
470
  let deferredFn;
460
471
  while (deferredFn = this._deferred.shift()) {
461
- deferredFn();
462
- }
463
- for (const observer of this.observers) {
464
472
  try {
465
- observer.next?.(snapshot);
473
+ deferredFn();
466
474
  } catch (err) {
467
- reportUnhandledError(err);
475
+ // this error can only be caught when executing *initial* actions
476
+ // it's the only time when we call actions provided by the user through those deferreds
477
+ // when the actor is already running we always execute them synchronously while transitioning
478
+ // no "builtin deferred" should actually throw an error since they are either safe
479
+ // or the control flow is passed through the mailbox and errors should be caught by the `_process` used by the mailbox
480
+ this._deferred.length = 0;
481
+ this._state = {
482
+ ...snapshot,
483
+ status: 'error',
484
+ error: err
485
+ };
468
486
  }
469
487
  }
470
488
  switch (this._state.status) {
489
+ case 'active':
490
+ for (const observer of this.observers) {
491
+ try {
492
+ observer.next?.(snapshot);
493
+ } catch (err) {
494
+ reportUnhandledError(err);
495
+ }
496
+ }
497
+ break;
471
498
  case 'done':
499
+ // next observers are meant to be notified about done snapshots
500
+ // this can be seen as something that is different from how observable work
501
+ // but with observables `complete` callback is called without any arguments
502
+ // it's more ergonomic for XState to treat a done snapshot as a "next" value
503
+ // and the completion event as something that is separate,
504
+ // something that merely follows emitting that done snapshot
505
+ for (const observer of this.observers) {
506
+ try {
507
+ observer.next?.(snapshot);
508
+ } catch (err) {
509
+ reportUnhandledError(err);
510
+ }
511
+ }
472
512
  this._stopProcedure();
473
513
  this._complete();
474
514
  this._doneEvent = createDoneActorEvent(this.id, this._state.output);
@@ -477,11 +517,7 @@ class Actor {
477
517
  }
478
518
  break;
479
519
  case 'error':
480
- this._stopProcedure();
481
520
  this._error(this._state.error);
482
- if (this._parent) {
483
- this.system._relay(this, this._parent, createErrorActorEvent(this.id, this._state.error));
484
- }
485
521
  break;
486
522
  }
487
523
  this.system._sendInspectionEvent({
@@ -571,7 +607,7 @@ class Actor {
571
607
  this.subscribe({
572
608
  next: snapshot => {
573
609
  if (snapshot.status === 'active') {
574
- this._parent.send({
610
+ this.system._relay(this, this._parent, {
575
611
  type: `xstate.snapshot.${this.id}`,
576
612
  snapshot
577
613
  });
@@ -600,18 +636,22 @@ class Actor {
600
636
  // a state machine can be "done" upon initialization (it could reach a final state using initial microsteps)
601
637
  // we still need to complete observers, flush deferreds etc
602
638
  this.update(this._state, initEvent);
603
- // fallthrough
604
- case 'error':
605
639
  // TODO: rethink cleanup of observers, mailbox, etc
606
640
  return this;
641
+ case 'error':
642
+ this._error(this._state.error);
643
+ return this;
607
644
  }
608
645
  if (this.logic.start) {
609
646
  try {
610
647
  this.logic.start(this._state, this._actorScope);
611
648
  } catch (err) {
612
- this._stopProcedure();
649
+ this._state = {
650
+ ...this._state,
651
+ status: 'error',
652
+ error: err
653
+ };
613
654
  this._error(err);
614
- this._parent?.send(createErrorActorEvent(this.id, err));
615
655
  return this;
616
656
  }
617
657
  }
@@ -627,7 +667,6 @@ class Actor {
627
667
  return this;
628
668
  }
629
669
  _process(event) {
630
- // TODO: reexamine what happens when an action (or a guard or smth) throws
631
670
  let nextState;
632
671
  let caughtError;
633
672
  try {
@@ -642,9 +681,12 @@ class Actor {
642
681
  const {
643
682
  err
644
683
  } = caughtError;
645
- this._stopProcedure();
684
+ this._state = {
685
+ ...this._state,
686
+ status: 'error',
687
+ error: err
688
+ };
646
689
  this._error(err);
647
- this._parent?.send(createErrorActorEvent(this.id, err));
648
690
  return;
649
691
  }
650
692
  this.update(nextState, event);
@@ -687,7 +729,7 @@ class Actor {
687
729
  }
688
730
  this.observers.clear();
689
731
  }
690
- _error(err) {
732
+ _reportError(err) {
691
733
  if (!this.observers.size) {
692
734
  if (!this._parent) {
693
735
  reportUnhandledError(err);
@@ -709,6 +751,18 @@ class Actor {
709
751
  reportUnhandledError(err);
710
752
  }
711
753
  }
754
+ _error(err) {
755
+ this._stopProcedure();
756
+ this._reportError(err);
757
+ if (this._parent) {
758
+ this.system._relay(this, this._parent, createErrorActorEvent(this.id, err));
759
+ }
760
+ }
761
+ // TODO: atm children don't belong entirely to the actor so
762
+ // in a way - it's not even super aware of them
763
+ // so we can't stop them from here but we really should!
764
+ // right now, they are being stopped within the machine's transition
765
+ // but that could throw and leave us with "orphaned" active actors
712
766
  _stopProcedure() {
713
767
  if (this._processingStatus !== ProcessingStatus.Running) {
714
768
  // Actor already stopped; do nothing
@@ -954,12 +1008,7 @@ function executeSpawn(actorScope, {
954
1008
  if (actorRef._processingStatus === ProcessingStatus.Stopped) {
955
1009
  return;
956
1010
  }
957
- try {
958
- actorRef.start?.();
959
- } catch (err) {
960
- actorScope.self.send(createErrorActorEvent(id, err));
961
- return;
962
- }
1011
+ actorRef.start();
963
1012
  });
964
1013
  }
965
1014
  function spawnChild(...[src, {
@@ -2014,7 +2063,23 @@ function macrostep(state, event, actorScope, internalQueue = []) {
2014
2063
  // Assume the state is at rest (no raised events)
2015
2064
  // Determine the next state based on the next microstep
2016
2065
  if (nextEvent.type !== XSTATE_INIT) {
2017
- const transitions = selectTransitions(nextEvent, nextState);
2066
+ const currentEvent = nextEvent;
2067
+ const isErr = isErrorActorEvent(currentEvent);
2068
+ const transitions = selectTransitions(currentEvent, nextState);
2069
+ if (isErr && !transitions.length) {
2070
+ // TODO: we should likely only allow transitions selected by very explicit descriptors
2071
+ // `*` shouldn't be matched, likely `xstate.error.*` shouldnt be either
2072
+ // similarly `xstate.error.actor.*` and `xstate.error.actor.todo.*` have to be considered too
2073
+ nextState = cloneMachineSnapshot(state, {
2074
+ status: 'error',
2075
+ error: currentEvent.data
2076
+ });
2077
+ states.push(nextState);
2078
+ return {
2079
+ state: nextState,
2080
+ microstates: states
2081
+ };
2082
+ }
2018
2083
  nextState = microstep(transitions, state, actorScope, nextEvent, false, internalQueue);
2019
2084
  states.push(nextState);
2020
2085
  }
@@ -2254,4 +2319,4 @@ function raise(eventOrExpr, options) {
2254
2319
  return raise;
2255
2320
  }
2256
2321
 
2257
- export { $$ACTOR_TYPE as $, getPersistedState as A, resolveReferencedActor as B, createActor as C, Actor as D, interpret as E, isMachineSnapshot as F, matchesState as G, pathToStateValue as H, toObserver as I, and as J, not as K, or as L, stateIn as M, NULL_EVENT as N, cancel as O, raise as P, stop as Q, stopChild as R, STATE_DELIMITER as S, spawnChild as T, ProcessingStatus as U, createErrorActorEvent as V, XSTATE_ERROR as W, XSTATE_STOP as X, toTransitionConfigArray as a, formatTransition as b, createInvokeId as c, formatInitialTransition as d, evaluateGuard as e, formatTransitions as f, getDelayedTransitions as g, getCandidates as h, getAllStateNodes as i, getStateNodes as j, createMachineSnapshot as k, isInFinalState as l, mapValues as m, isErrorActorEvent as n, getAllOwnEventDescriptors as o, cloneMachineSnapshot as p, macrostep as q, resolveStateValue as r, transitionNode as s, toArray as t, resolveActionsAndContext as u, createInitEvent as v, microstep as w, getInitialStateNodes as x, isStateId as y, getStateNodeByPath as z };
2322
+ export { $$ACTOR_TYPE as $, Actor as A, interpret as B, isMachineSnapshot as C, matchesState as D, pathToStateValue as E, toObserver as F, getAllOwnEventDescriptors as G, and as H, not as I, or as J, stateIn as K, cancel as L, raise as M, NULL_EVENT as N, stop as O, stopChild as P, spawnChild as Q, ProcessingStatus as R, STATE_DELIMITER as S, cloneMachineSnapshot as T, XSTATE_ERROR as U, createErrorActorEvent as V, XSTATE_STOP as X, toTransitionConfigArray as a, formatTransition as b, createInvokeId as c, formatInitialTransition as d, evaluateGuard as e, formatTransitions as f, getDelayedTransitions as g, getCandidates as h, getAllStateNodes as i, getStateNodes as j, createMachineSnapshot as k, isInFinalState as l, mapValues as m, macrostep as n, transitionNode as o, resolveActionsAndContext as p, createInitEvent as q, resolveStateValue as r, microstep as s, toArray as t, getInitialStateNodes as u, isStateId as v, getStateNodeByPath as w, getPersistedState as x, resolveReferencedActor as y, createActor as z };
@@ -452,7 +452,18 @@ class Actor {
452
452
  }
453
453
  }
454
454
  _initState(persistedState) {
455
- this._state = persistedState ? this.logic.restoreState ? this.logic.restoreState(persistedState, this._actorScope) : persistedState : this.logic.getInitialState(this._actorScope, this.options?.input);
455
+ try {
456
+ this._state = persistedState ? this.logic.restoreState ? this.logic.restoreState(persistedState, this._actorScope) : persistedState : this.logic.getInitialState(this._actorScope, this.options?.input);
457
+ } catch (err) {
458
+ // if we get here then it means that we assign a value to this._state that is not of the correct type
459
+ // we can't get the true `TSnapshot & { status: 'error'; }`, it's impossible
460
+ // so right now this is a lie of sorts
461
+ this._state = {
462
+ status: 'error',
463
+ output: undefined,
464
+ error: err
465
+ };
466
+ }
456
467
  }
457
468
  update(snapshot, event) {
458
469
  // Update state
@@ -461,17 +472,46 @@ class Actor {
461
472
  // Execute deferred effects
462
473
  let deferredFn;
463
474
  while (deferredFn = this._deferred.shift()) {
464
- deferredFn();
465
- }
466
- for (const observer of this.observers) {
467
475
  try {
468
- observer.next?.(snapshot);
476
+ deferredFn();
469
477
  } catch (err) {
470
- reportUnhandledError(err);
478
+ // this error can only be caught when executing *initial* actions
479
+ // it's the only time when we call actions provided by the user through those deferreds
480
+ // when the actor is already running we always execute them synchronously while transitioning
481
+ // no "builtin deferred" should actually throw an error since they are either safe
482
+ // or the control flow is passed through the mailbox and errors should be caught by the `_process` used by the mailbox
483
+ this._deferred.length = 0;
484
+ this._state = {
485
+ ...snapshot,
486
+ status: 'error',
487
+ error: err
488
+ };
471
489
  }
472
490
  }
473
491
  switch (this._state.status) {
492
+ case 'active':
493
+ for (const observer of this.observers) {
494
+ try {
495
+ observer.next?.(snapshot);
496
+ } catch (err) {
497
+ reportUnhandledError(err);
498
+ }
499
+ }
500
+ break;
474
501
  case 'done':
502
+ // next observers are meant to be notified about done snapshots
503
+ // this can be seen as something that is different from how observable work
504
+ // but with observables `complete` callback is called without any arguments
505
+ // it's more ergonomic for XState to treat a done snapshot as a "next" value
506
+ // and the completion event as something that is separate,
507
+ // something that merely follows emitting that done snapshot
508
+ for (const observer of this.observers) {
509
+ try {
510
+ observer.next?.(snapshot);
511
+ } catch (err) {
512
+ reportUnhandledError(err);
513
+ }
514
+ }
475
515
  this._stopProcedure();
476
516
  this._complete();
477
517
  this._doneEvent = createDoneActorEvent(this.id, this._state.output);
@@ -480,11 +520,7 @@ class Actor {
480
520
  }
481
521
  break;
482
522
  case 'error':
483
- this._stopProcedure();
484
523
  this._error(this._state.error);
485
- if (this._parent) {
486
- this.system._relay(this, this._parent, createErrorActorEvent(this.id, this._state.error));
487
- }
488
524
  break;
489
525
  }
490
526
  this.system._sendInspectionEvent({
@@ -574,7 +610,7 @@ class Actor {
574
610
  this.subscribe({
575
611
  next: snapshot => {
576
612
  if (snapshot.status === 'active') {
577
- this._parent.send({
613
+ this.system._relay(this, this._parent, {
578
614
  type: `xstate.snapshot.${this.id}`,
579
615
  snapshot
580
616
  });
@@ -603,18 +639,22 @@ class Actor {
603
639
  // a state machine can be "done" upon initialization (it could reach a final state using initial microsteps)
604
640
  // we still need to complete observers, flush deferreds etc
605
641
  this.update(this._state, initEvent);
606
- // fallthrough
607
- case 'error':
608
642
  // TODO: rethink cleanup of observers, mailbox, etc
609
643
  return this;
644
+ case 'error':
645
+ this._error(this._state.error);
646
+ return this;
610
647
  }
611
648
  if (this.logic.start) {
612
649
  try {
613
650
  this.logic.start(this._state, this._actorScope);
614
651
  } catch (err) {
615
- this._stopProcedure();
652
+ this._state = {
653
+ ...this._state,
654
+ status: 'error',
655
+ error: err
656
+ };
616
657
  this._error(err);
617
- this._parent?.send(createErrorActorEvent(this.id, err));
618
658
  return this;
619
659
  }
620
660
  }
@@ -630,7 +670,6 @@ class Actor {
630
670
  return this;
631
671
  }
632
672
  _process(event) {
633
- // TODO: reexamine what happens when an action (or a guard or smth) throws
634
673
  let nextState;
635
674
  let caughtError;
636
675
  try {
@@ -645,9 +684,12 @@ class Actor {
645
684
  const {
646
685
  err
647
686
  } = caughtError;
648
- this._stopProcedure();
687
+ this._state = {
688
+ ...this._state,
689
+ status: 'error',
690
+ error: err
691
+ };
649
692
  this._error(err);
650
- this._parent?.send(createErrorActorEvent(this.id, err));
651
693
  return;
652
694
  }
653
695
  this.update(nextState, event);
@@ -690,7 +732,7 @@ class Actor {
690
732
  }
691
733
  this.observers.clear();
692
734
  }
693
- _error(err) {
735
+ _reportError(err) {
694
736
  if (!this.observers.size) {
695
737
  if (!this._parent) {
696
738
  reportUnhandledError(err);
@@ -712,6 +754,18 @@ class Actor {
712
754
  reportUnhandledError(err);
713
755
  }
714
756
  }
757
+ _error(err) {
758
+ this._stopProcedure();
759
+ this._reportError(err);
760
+ if (this._parent) {
761
+ this.system._relay(this, this._parent, createErrorActorEvent(this.id, err));
762
+ }
763
+ }
764
+ // TODO: atm children don't belong entirely to the actor so
765
+ // in a way - it's not even super aware of them
766
+ // so we can't stop them from here but we really should!
767
+ // right now, they are being stopped within the machine's transition
768
+ // but that could throw and leave us with "orphaned" active actors
715
769
  _stopProcedure() {
716
770
  if (this._processingStatus !== ProcessingStatus.Running) {
717
771
  // Actor already stopped; do nothing
@@ -971,12 +1025,7 @@ function executeSpawn(actorScope, {
971
1025
  if (actorRef._processingStatus === ProcessingStatus.Stopped) {
972
1026
  return;
973
1027
  }
974
- try {
975
- actorRef.start?.();
976
- } catch (err) {
977
- actorScope.self.send(createErrorActorEvent(id, err));
978
- return;
979
- }
1028
+ actorRef.start();
980
1029
  });
981
1030
  }
982
1031
  function spawnChild(...[src, {
@@ -2059,7 +2108,23 @@ function macrostep(state, event, actorScope, internalQueue = []) {
2059
2108
  // Assume the state is at rest (no raised events)
2060
2109
  // Determine the next state based on the next microstep
2061
2110
  if (nextEvent.type !== XSTATE_INIT) {
2062
- const transitions = selectTransitions(nextEvent, nextState);
2111
+ const currentEvent = nextEvent;
2112
+ const isErr = isErrorActorEvent(currentEvent);
2113
+ const transitions = selectTransitions(currentEvent, nextState);
2114
+ if (isErr && !transitions.length) {
2115
+ // TODO: we should likely only allow transitions selected by very explicit descriptors
2116
+ // `*` shouldn't be matched, likely `xstate.error.*` shouldnt be either
2117
+ // similarly `xstate.error.actor.*` and `xstate.error.actor.todo.*` have to be considered too
2118
+ nextState = cloneMachineSnapshot(state, {
2119
+ status: 'error',
2120
+ error: currentEvent.data
2121
+ });
2122
+ states.push(nextState);
2123
+ return {
2124
+ state: nextState,
2125
+ microstates: states
2126
+ };
2127
+ }
2063
2128
  nextState = microstep(transitions, state, actorScope, nextEvent, false, internalQueue);
2064
2129
  states.push(nextState);
2065
2130
  }
@@ -2308,4 +2373,4 @@ function raise(eventOrExpr, options) {
2308
2373
  return raise;
2309
2374
  }
2310
2375
 
2311
- export { $$ACTOR_TYPE as $, getPersistedState as A, resolveReferencedActor as B, createActor as C, Actor as D, interpret as E, isMachineSnapshot as F, matchesState as G, pathToStateValue as H, toObserver as I, and as J, not as K, or as L, stateIn as M, NULL_EVENT as N, cancel as O, raise as P, stop as Q, stopChild as R, STATE_DELIMITER as S, spawnChild as T, ProcessingStatus as U, createErrorActorEvent as V, XSTATE_ERROR as W, XSTATE_STOP as X, toTransitionConfigArray as a, formatTransition as b, createInvokeId as c, formatInitialTransition as d, evaluateGuard as e, formatTransitions as f, getDelayedTransitions as g, getCandidates as h, getAllStateNodes as i, getStateNodes as j, createMachineSnapshot as k, isInFinalState as l, mapValues as m, isErrorActorEvent as n, getAllOwnEventDescriptors as o, cloneMachineSnapshot as p, macrostep as q, resolveStateValue as r, transitionNode as s, toArray as t, resolveActionsAndContext as u, createInitEvent as v, microstep as w, getInitialStateNodes as x, isStateId as y, getStateNodeByPath as z };
2376
+ export { $$ACTOR_TYPE as $, Actor as A, interpret as B, isMachineSnapshot as C, matchesState as D, pathToStateValue as E, toObserver as F, getAllOwnEventDescriptors as G, and as H, not as I, or as J, stateIn as K, cancel as L, raise as M, NULL_EVENT as N, stop as O, stopChild as P, spawnChild as Q, ProcessingStatus as R, STATE_DELIMITER as S, cloneMachineSnapshot as T, XSTATE_ERROR as U, createErrorActorEvent as V, XSTATE_STOP as X, toTransitionConfigArray as a, formatTransition as b, createInvokeId as c, formatInitialTransition as d, evaluateGuard as e, formatTransitions as f, getDelayedTransitions as g, getCandidates as h, getAllStateNodes as i, getStateNodes as j, createMachineSnapshot as k, isInFinalState as l, mapValues as m, macrostep as n, transitionNode as o, resolveActionsAndContext as p, createInitEvent as q, resolveStateValue as r, microstep as s, toArray as t, getInitialStateNodes as u, isStateId as v, getStateNodeByPath as w, getPersistedState as x, resolveReferencedActor as y, createActor as z };