xstate 5.0.0-beta.20 → 5.0.0-beta.21

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 (40) hide show
  1. package/actions/dist/xstate-actions.cjs.js +1 -1
  2. package/actions/dist/xstate-actions.development.cjs.js +1 -1
  3. package/actions/dist/xstate-actions.development.esm.js +1 -1
  4. package/actions/dist/xstate-actions.esm.js +1 -1
  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 +1 -1
  8. package/actors/dist/xstate-actors.development.cjs.js +1 -1
  9. package/actors/dist/xstate-actors.development.esm.js +1 -1
  10. package/actors/dist/xstate-actors.esm.js +1 -1
  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/{actions-b299d008.development.esm.js → actions-49f0501e.development.esm.js} +136 -63
  14. package/dist/{actions-a8a9433c.esm.js → actions-5039c951.esm.js} +134 -64
  15. package/dist/{actions-d1c41ed3.development.cjs.js → actions-a95d2e66.development.cjs.js} +136 -62
  16. package/dist/{actions-069d9805.cjs.js → actions-c619a105.cjs.js} +134 -63
  17. package/dist/declarations/src/Machine.d.ts +2 -2
  18. package/dist/declarations/src/State.d.ts +4 -7
  19. package/dist/declarations/src/StateMachine.d.ts +7 -6
  20. package/dist/declarations/src/StateNode.d.ts +3 -3
  21. package/dist/declarations/src/actions/send.d.ts +1 -1
  22. package/dist/declarations/src/actions/stop.d.ts +1 -1
  23. package/dist/declarations/src/actions.d.ts +2 -2
  24. package/dist/declarations/src/actors/callback.d.ts +4 -4
  25. package/dist/declarations/src/actors/observable.d.ts +7 -4
  26. package/dist/declarations/src/actors/promise.d.ts +4 -4
  27. package/dist/declarations/src/types.d.ts +41 -29
  28. package/dist/declarations/src/utils.d.ts +2 -2
  29. package/dist/xstate.cjs.js +11 -10
  30. package/dist/xstate.development.cjs.js +11 -10
  31. package/dist/xstate.development.esm.js +12 -11
  32. package/dist/xstate.esm.js +12 -11
  33. package/dist/xstate.umd.min.js +1 -1
  34. package/dist/xstate.umd.min.js.map +1 -1
  35. package/guards/dist/xstate-guards.cjs.js +1 -1
  36. package/guards/dist/xstate-guards.development.cjs.js +1 -1
  37. package/guards/dist/xstate-guards.development.esm.js +1 -1
  38. package/guards/dist/xstate-guards.esm.js +1 -1
  39. package/guards/dist/xstate-guards.umd.min.js.map +1 -1
  40. package/package.json +1 -1
@@ -1,5 +1,17 @@
1
1
  import { devToolsAdapter } from '../dev/dist/xstate-dev.esm.js';
2
2
 
3
+ /**
4
+ * `T | unknown` reduces to `unknown` and that can be problematic when it comes to contextual typing.
5
+ * It especially is a problem when the union has a function member, like here:
6
+ *
7
+ * ```ts
8
+ * declare function test(cbOrVal: ((arg: number) => unknown) | unknown): void;
9
+ * test((arg) => {}) // oops, implicit any
10
+ * ```
11
+ *
12
+ * This type can be used to avoid this problem. This union represents the same value space as `unknown`.
13
+ */
14
+
3
15
  // https://github.com/microsoft/TypeScript/issues/23182#issuecomment-379091887
4
16
 
5
17
  /**
@@ -404,27 +416,15 @@ function toArray(value) {
404
416
  }
405
417
  return toArrayStrict(value);
406
418
  }
407
- function mapContext(mapper, context, event) {
419
+ function mapContext(mapper, context, event, self) {
408
420
  if (typeof mapper === 'function') {
409
421
  return mapper({
410
422
  context,
411
- event
423
+ event,
424
+ self
412
425
  });
413
426
  }
414
- const result = {};
415
- const args = {
416
- context,
417
- event
418
- };
419
- for (const key of Object.keys(mapper)) {
420
- const subMapper = mapper[key];
421
- if (typeof subMapper === 'function') {
422
- result[key] = subMapper(args);
423
- } else {
424
- result[key] = subMapper;
425
- }
426
- }
427
- return result;
427
+ return mapper;
428
428
  }
429
429
  function isPromiseLike(value) {
430
430
  if (value instanceof Promise) {
@@ -476,13 +476,12 @@ function toInvokeConfig(invocable, id) {
476
476
  };
477
477
  }
478
478
  function toObserver(nextHandler, errorHandler, completionHandler) {
479
- const noop = () => {};
480
479
  const isObserver = typeof nextHandler === 'object';
481
- const self = isObserver ? nextHandler : null;
480
+ const self = isObserver ? nextHandler : undefined;
482
481
  return {
483
- next: ((isObserver ? nextHandler.next : nextHandler) || noop).bind(self),
484
- error: ((isObserver ? nextHandler.error : errorHandler) || noop).bind(self),
485
- complete: ((isObserver ? nextHandler.complete : completionHandler) || noop).bind(self)
482
+ next: (isObserver ? nextHandler.next : nextHandler)?.bind(self),
483
+ error: (isObserver ? nextHandler.error : errorHandler)?.bind(self),
484
+ complete: (isObserver ? nextHandler.complete : completionHandler)?.bind(self)
486
485
  };
487
486
  }
488
487
  function createInvokeId(stateNodeId, index) {
@@ -496,7 +495,7 @@ function resolveReferencedActor(referenced) {
496
495
  }
497
496
 
498
497
  function fromCallback(invokeCallback) {
499
- const logic = {
498
+ return {
500
499
  config: invokeCallback,
501
500
  start: (_state, {
502
501
  self
@@ -562,19 +561,20 @@ function fromCallback(invokeCallback) {
562
561
  },
563
562
  getSnapshot: () => undefined,
564
563
  getPersistedState: ({
565
- input
566
- }) => input
564
+ input,
565
+ canceled
566
+ }) => ({
567
+ input,
568
+ canceled
569
+ })
567
570
  };
568
- return logic;
569
571
  }
570
572
 
571
573
  function fromObservable(observableCreator) {
572
574
  const nextEventType = '$$xstate.next';
573
575
  const errorEventType = '$$xstate.error';
574
576
  const completeEventType = '$$xstate.complete';
575
-
576
- // TODO: add event types
577
- const logic = {
577
+ return {
578
578
  config: observableCreator,
579
579
  transition: (state, event, {
580
580
  self,
@@ -604,6 +604,7 @@ function fromObservable(observableCreator) {
604
604
  status: 'error',
605
605
  input: undefined,
606
606
  data: event.data,
607
+ // TODO: if we keep this as `data` we should reflect this in the type
607
608
  subscription: undefined
608
609
  };
609
610
  case completeEventType:
@@ -681,7 +682,6 @@ function fromObservable(observableCreator) {
681
682
  subscription: undefined
682
683
  })
683
684
  };
684
- return logic;
685
685
  }
686
686
 
687
687
  /**
@@ -698,7 +698,7 @@ function fromEventObservable(lazyObservable) {
698
698
  const completeEventType = '$$xstate.complete';
699
699
 
700
700
  // TODO: event types
701
- const logic = {
701
+ return {
702
702
  config: lazyObservable,
703
703
  transition: (state, event) => {
704
704
  if (state.status !== 'active') {
@@ -711,6 +711,7 @@ function fromEventObservable(lazyObservable) {
711
711
  status: 'error',
712
712
  input: undefined,
713
713
  data: event.data,
714
+ // TODO: if we keep this as `data` we should reflect this in the type
714
715
  subscription: undefined
715
716
  };
716
717
  case completeEventType:
@@ -785,7 +786,6 @@ function fromEventObservable(lazyObservable) {
785
786
  subscription: undefined
786
787
  })
787
788
  };
788
- return logic;
789
789
  }
790
790
 
791
791
  const resolveEventType = '$$xstate.resolve';
@@ -813,6 +813,7 @@ promiseCreator) {
813
813
  ...state,
814
814
  status: 'error',
815
815
  data: event.data,
816
+ // TODO: if we keep this as `data` we should reflect this in the type
816
817
  input: undefined
817
818
  };
818
819
  case stopSignalType:
@@ -922,6 +923,19 @@ function createEmptyActor() {
922
923
  return interpret(emptyLogic);
923
924
  }
924
925
 
926
+ /**
927
+ * This function makes sure that unhandled errors are thrown in a separate macrotask.
928
+ * It allows those errors to be detected by global error handlers and reported to bug tracking services
929
+ * without interrupting our own stack of execution.
930
+ *
931
+ * @param err error to be thrown
932
+ */
933
+ function reportUnhandledError(err) {
934
+ setTimeout(() => {
935
+ throw err;
936
+ });
937
+ }
938
+
925
939
  function createSystem() {
926
940
  let sessionIdCounter = 0;
927
941
  const children = new Map();
@@ -1090,29 +1104,38 @@ class Interpreter {
1090
1104
  deferredFn();
1091
1105
  }
1092
1106
  for (const observer of this.observers) {
1093
- observer.next?.(snapshot);
1107
+ // TODO: should observers be notified in case of the error?
1108
+ try {
1109
+ observer.next?.(snapshot);
1110
+ } catch (err) {
1111
+ reportUnhandledError(err);
1112
+ }
1094
1113
  }
1095
1114
  const status = this.logic.getStatus?.(state);
1096
1115
  switch (status?.status) {
1097
1116
  case 'done':
1098
1117
  this._stopProcedure();
1118
+ this._complete();
1099
1119
  this._doneEvent = doneInvoke(this.id, status.data);
1100
1120
  this._parent?.send(this._doneEvent);
1101
- this._complete();
1102
1121
  break;
1103
1122
  case 'error':
1104
1123
  this._stopProcedure();
1105
- this._parent?.send(error(this.id, status.data));
1106
1124
  this._error(status.data);
1125
+ this._parent?.send(error(this.id, status.data));
1107
1126
  break;
1108
1127
  }
1109
1128
  }
1110
1129
  subscribe(nextListenerOrObserver, errorListener, completeListener) {
1111
1130
  const observer = toObserver(nextListenerOrObserver, errorListener, completeListener);
1112
- this.observers.add(observer);
1113
- if (this.status === ActorStatus.Stopped) {
1114
- observer.complete?.();
1115
- this.observers.delete(observer);
1131
+ if (this.status !== ActorStatus.Stopped) {
1132
+ this.observers.add(observer);
1133
+ } else {
1134
+ try {
1135
+ observer.complete?.();
1136
+ } catch (err) {
1137
+ reportUnhandledError(err);
1138
+ }
1116
1139
  }
1117
1140
  return {
1118
1141
  unsubscribe: () => {
@@ -1134,8 +1157,26 @@ class Interpreter {
1134
1157
  this.system._set(this._systemId, this);
1135
1158
  }
1136
1159
  this.status = ActorStatus.Running;
1160
+ const status = this.logic.getStatus?.(this._state);
1161
+ switch (status?.status) {
1162
+ case 'done':
1163
+ // a state machine can be "done" upon intialization (it could reach a final state using initial microsteps)
1164
+ // we still need to complete observers, flush deferreds etc
1165
+ this.update(this._state);
1166
+ // fallthrough
1167
+ case 'error':
1168
+ // TODO: rethink cleanup of observers, mailbox, etc
1169
+ return this;
1170
+ }
1137
1171
  if (this.logic.start) {
1138
- this.logic.start(this._state, this._actorContext);
1172
+ try {
1173
+ this.logic.start(this._state, this._actorContext);
1174
+ } catch (err) {
1175
+ this._stopProcedure();
1176
+ this._error(err);
1177
+ this._parent?.send(error(this.id, err));
1178
+ return this;
1179
+ }
1139
1180
  }
1140
1181
 
1141
1182
  // TODO: this notifies all subscribers but usually this is redundant
@@ -1149,23 +1190,30 @@ class Interpreter {
1149
1190
  return this;
1150
1191
  }
1151
1192
  _process(event) {
1193
+ // TODO: reexamine what happens when an action (or a guard or smth) throws
1194
+ let nextState;
1195
+ let caughtError;
1152
1196
  try {
1153
- const nextState = this.logic.transition(this._state, event, this._actorContext);
1154
- this.update(nextState);
1155
- if (event.type === stopSignalType) {
1156
- this._stopProcedure();
1157
- this._complete();
1158
- }
1197
+ nextState = this.logic.transition(this._state, event, this._actorContext);
1159
1198
  } catch (err) {
1160
- // TODO: properly handle errors
1161
- if (this.observers.size > 0) {
1162
- this.observers.forEach(observer => {
1163
- observer.error?.(err);
1164
- });
1165
- this.stop();
1166
- } else {
1167
- throw err;
1168
- }
1199
+ // we wrap it in a box so we can rethrow it later even if falsy value gets caught here
1200
+ caughtError = {
1201
+ err
1202
+ };
1203
+ }
1204
+ if (caughtError) {
1205
+ const {
1206
+ err
1207
+ } = caughtError;
1208
+ this._stopProcedure();
1209
+ this._error(err);
1210
+ this._parent?.send(error(this.id, err));
1211
+ return;
1212
+ }
1213
+ this.update(nextState);
1214
+ if (event.type === stopSignalType) {
1215
+ this._stopProcedure();
1216
+ this._complete();
1169
1217
  }
1170
1218
  }
1171
1219
  _stop() {
@@ -1194,15 +1242,35 @@ class Interpreter {
1194
1242
  }
1195
1243
  _complete() {
1196
1244
  for (const observer of this.observers) {
1197
- observer.complete?.();
1245
+ try {
1246
+ observer.complete?.();
1247
+ } catch (err) {
1248
+ reportUnhandledError(err);
1249
+ }
1198
1250
  }
1199
1251
  this.observers.clear();
1200
1252
  }
1201
- _error(data) {
1253
+ _error(err) {
1254
+ if (!this.observers.size) {
1255
+ if (!this._parent) {
1256
+ reportUnhandledError(err);
1257
+ }
1258
+ return;
1259
+ }
1260
+ let reportError = false;
1202
1261
  for (const observer of this.observers) {
1203
- observer.error?.(data);
1262
+ const errorListener = observer.error;
1263
+ reportError ||= !errorListener;
1264
+ try {
1265
+ errorListener?.(err);
1266
+ } catch (err2) {
1267
+ reportUnhandledError(err2);
1268
+ }
1204
1269
  }
1205
1270
  this.observers.clear();
1271
+ if (reportError) {
1272
+ reportUnhandledError(err);
1273
+ }
1206
1274
  }
1207
1275
  _stopProcedure() {
1208
1276
  if (this.status !== ActorStatus.Running) {
@@ -1524,10 +1592,10 @@ function toGuardDefinition(guardConfig, getPredicate) {
1524
1592
  }
1525
1593
  }
1526
1594
 
1527
- function getOutput(configuration, context, event) {
1595
+ function getOutput(configuration, context, event, self) {
1528
1596
  const machine = configuration[0].machine;
1529
1597
  const finalChildStateNode = configuration.find(stateNode => stateNode.type === 'final' && stateNode.parent === machine.root);
1530
- return finalChildStateNode && finalChildStateNode.output ? mapContext(finalChildStateNode.output, context, event) : undefined;
1598
+ return finalChildStateNode && finalChildStateNode.output ? mapContext(finalChildStateNode.output, context, event, self) : undefined;
1531
1599
  }
1532
1600
  const isAtomicStateNode = stateNode => stateNode.type === 'atomic' || stateNode.type === 'final';
1533
1601
  function getChildren(stateNode) {
@@ -2143,7 +2211,7 @@ function microstepProcedure(transitions, currentState, mutConfiguration, event,
2143
2211
  actions.push(...filteredTransitions.flatMap(t => t.actions));
2144
2212
 
2145
2213
  // Enter states
2146
- enterStates(event, filteredTransitions, mutConfiguration, actions, internalQueue, currentState, historyValue, isInitial);
2214
+ enterStates(event, filteredTransitions, mutConfiguration, actions, internalQueue, currentState, historyValue, isInitial, actorCtx);
2147
2215
  const nextConfiguration = [...mutConfiguration];
2148
2216
  const done = isInFinalState(nextConfiguration);
2149
2217
  if (done) {
@@ -2152,7 +2220,7 @@ function microstepProcedure(transitions, currentState, mutConfiguration, event,
2152
2220
  }
2153
2221
  try {
2154
2222
  const nextState = resolveActionsAndContext(actions, event, currentState, actorCtx);
2155
- const output = done ? getOutput(nextConfiguration, nextState.context, event) : undefined;
2223
+ const output = done ? getOutput(nextConfiguration, nextState.context, event, actorCtx.self) : undefined;
2156
2224
  internalQueue.push(...nextState._internalQueue);
2157
2225
  return cloneState(currentState, {
2158
2226
  configuration: nextConfiguration,
@@ -2169,7 +2237,7 @@ function microstepProcedure(transitions, currentState, mutConfiguration, event,
2169
2237
  throw e;
2170
2238
  }
2171
2239
  }
2172
- function enterStates(event, filteredTransitions, mutConfiguration, actions, internalQueue, currentState, historyValue, isInitial) {
2240
+ function enterStates(event, filteredTransitions, mutConfiguration, actions, internalQueue, currentState, historyValue, isInitial, actorContext) {
2173
2241
  const statesToEnter = new Set();
2174
2242
  const statesForDefaultEntry = new Set();
2175
2243
  computeEntrySet(filteredTransitions, historyValue, statesForDefaultEntry, statesToEnter);
@@ -2197,7 +2265,7 @@ function enterStates(event, filteredTransitions, mutConfiguration, actions, inte
2197
2265
  if (!parent.parent) {
2198
2266
  continue;
2199
2267
  }
2200
- internalQueue.push(done(parent.id, stateNodeToEnter.output ? mapContext(stateNodeToEnter.output, currentState.context, event) : undefined));
2268
+ internalQueue.push(done(parent.id, stateNodeToEnter.output ? mapContext(stateNodeToEnter.output, currentState.context, event, actorContext.self) : undefined));
2201
2269
  if (parent.parent) {
2202
2270
  const grandparent = parent.parent;
2203
2271
  if (grandparent.type === 'parallel') {
@@ -2517,6 +2585,7 @@ class State {
2517
2585
  this.value = void 0;
2518
2586
  this.done = void 0;
2519
2587
  this.output = void 0;
2588
+ this.error = void 0;
2520
2589
  this.context = void 0;
2521
2590
  this.historyValue = {};
2522
2591
  this._internalQueue = void 0;
@@ -2533,6 +2602,7 @@ class State {
2533
2602
  this.tags = new Set(flatten(this.configuration.map(sn => sn.tags)));
2534
2603
  this.done = config.done ?? false;
2535
2604
  this.output = config.output;
2605
+ this.error = config.error;
2536
2606
  }
2537
2607
 
2538
2608
  /**
@@ -2982,4 +3052,4 @@ function createInitEvent(input) {
2982
3052
  };
2983
3053
  }
2984
3054
 
2985
- export { fromEventObservable as $, isAtomicStateNode as A, error as B, isStateId as C, getStateNodeByPath as D, getPersistedState as E, resolveReferencedActor as F, interpret as G, matchesState as H, sendTo as I, sendParent as J, forwardTo as K, Interpreter as L, ActorStatus as M, NULL_EVENT as N, doneInvoke as O, cancel as P, choose as Q, log as R, STATE_DELIMITER as S, pure as T, raise as U, stop as V, pathToStateValue as W, toObserver as X, fromPromise as Y, fromObservable as Z, fromCallback as _, toTransitionConfigArray as a, fromTransition as a0, stateIn as a1, not as a2, and as a3, or as a4, ConstantPrefix as a5, SpecialTargets as a6, startSignalType as a7, stopSignalType as a8, startSignal as a9, stopSignal as aa, isSignal as ab, isActorRef as ac, toActorRef as ad, createEmptyActor as ae, toGuardDefinition as af, constantPrefixes as ag, after as ah, done as ai, escalate as aj, formatTransition as b, memo as c, flatten as d, evaluateGuard as e, formatTransitions as f, createInvokeId as g, getDelayedTransitions as h, formatInitialTransition as i, getCandidates as j, toInvokeConfig as k, getConfiguration as l, mapValues as m, getStateNodes as n, isInFinalState as o, State as p, isErrorEvent as q, resolveStateValue as r, macrostep as s, toArray as t, transitionNode as u, getInitialConfiguration as v, resolveActionsAndContext as w, assign as x, createInitEvent as y, microstep as z };
3055
+ export { fromEventObservable as $, microstep as A, isAtomicStateNode as B, isStateId as C, getStateNodeByPath as D, getPersistedState as E, resolveReferencedActor as F, interpret as G, matchesState as H, sendTo as I, sendParent as J, forwardTo as K, Interpreter as L, ActorStatus as M, NULL_EVENT as N, doneInvoke as O, cancel as P, choose as Q, log as R, STATE_DELIMITER as S, pure as T, raise as U, stop as V, pathToStateValue as W, toObserver as X, fromPromise as Y, fromObservable as Z, fromCallback as _, toTransitionConfigArray as a, fromTransition as a0, stateIn as a1, not as a2, and as a3, or as a4, ConstantPrefix as a5, SpecialTargets as a6, startSignalType as a7, stopSignalType as a8, startSignal as a9, stopSignal as aa, isSignal as ab, isActorRef as ac, toActorRef as ad, createEmptyActor as ae, toGuardDefinition as af, constantPrefixes as ag, after as ah, done as ai, error as aj, escalate as ak, formatTransition as b, memo as c, flatten as d, evaluateGuard as e, formatTransitions as f, createInvokeId as g, getDelayedTransitions as h, formatInitialTransition as i, getCandidates as j, toInvokeConfig as k, getConfiguration as l, mapValues as m, getStateNodes as n, isInFinalState as o, State as p, isErrorEvent as q, resolveStateValue as r, cloneState as s, toArray as t, macrostep as u, transitionNode as v, getInitialConfiguration as w, resolveActionsAndContext as x, assign as y, createInitEvent as z };