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.development.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
  /**
@@ -420,27 +432,18 @@ function toArray(value) {
420
432
  }
421
433
  return toArrayStrict(value);
422
434
  }
423
- function mapContext(mapper, context, event) {
435
+ function mapContext(mapper, context, event, self) {
424
436
  if (typeof mapper === 'function') {
425
437
  return mapper({
426
438
  context,
427
- event
439
+ event,
440
+ self
428
441
  });
429
442
  }
430
- const result = {};
431
- const args = {
432
- context,
433
- event
434
- };
435
- for (const key of Object.keys(mapper)) {
436
- const subMapper = mapper[key];
437
- if (typeof subMapper === 'function') {
438
- result[key] = subMapper(args);
439
- } else {
440
- result[key] = subMapper;
441
- }
443
+ if (typeof mapper === 'object' && Object.values(mapper).some(val => typeof val === 'function')) {
444
+ console.warn(`Dynamically mapping values to individual properties is deprecated. Use a single function that returns the mapped object instead.\nFound object containing properties whose values are possibly mapping functions: ${Object.entries(mapper).filter(([key, value]) => typeof value === 'function').map(([key, value]) => `\n - ${key}: ${value.toString().replace(/\n\s*/g, '')}`).join('')}`);
442
445
  }
443
- return result;
446
+ return mapper;
444
447
  }
445
448
  function isPromiseLike(value) {
446
449
  if (value instanceof Promise) {
@@ -492,13 +495,12 @@ function toInvokeConfig(invocable, id) {
492
495
  };
493
496
  }
494
497
  function toObserver(nextHandler, errorHandler, completionHandler) {
495
- const noop = () => {};
496
498
  const isObserver = typeof nextHandler === 'object';
497
- const self = isObserver ? nextHandler : null;
499
+ const self = isObserver ? nextHandler : undefined;
498
500
  return {
499
- next: ((isObserver ? nextHandler.next : nextHandler) || noop).bind(self),
500
- error: ((isObserver ? nextHandler.error : errorHandler) || noop).bind(self),
501
- complete: ((isObserver ? nextHandler.complete : completionHandler) || noop).bind(self)
501
+ next: (isObserver ? nextHandler.next : nextHandler)?.bind(self),
502
+ error: (isObserver ? nextHandler.error : errorHandler)?.bind(self),
503
+ complete: (isObserver ? nextHandler.complete : completionHandler)?.bind(self)
502
504
  };
503
505
  }
504
506
  function createInvokeId(stateNodeId, index) {
@@ -512,7 +514,7 @@ function resolveReferencedActor(referenced) {
512
514
  }
513
515
 
514
516
  function fromCallback(invokeCallback) {
515
- const logic = {
517
+ return {
516
518
  config: invokeCallback,
517
519
  start: (_state, {
518
520
  self
@@ -578,19 +580,20 @@ function fromCallback(invokeCallback) {
578
580
  },
579
581
  getSnapshot: () => undefined,
580
582
  getPersistedState: ({
581
- input
582
- }) => input
583
+ input,
584
+ canceled
585
+ }) => ({
586
+ input,
587
+ canceled
588
+ })
583
589
  };
584
- return logic;
585
590
  }
586
591
 
587
592
  function fromObservable(observableCreator) {
588
593
  const nextEventType = '$$xstate.next';
589
594
  const errorEventType = '$$xstate.error';
590
595
  const completeEventType = '$$xstate.complete';
591
-
592
- // TODO: add event types
593
- const logic = {
596
+ return {
594
597
  config: observableCreator,
595
598
  transition: (state, event, {
596
599
  self,
@@ -620,6 +623,7 @@ function fromObservable(observableCreator) {
620
623
  status: 'error',
621
624
  input: undefined,
622
625
  data: event.data,
626
+ // TODO: if we keep this as `data` we should reflect this in the type
623
627
  subscription: undefined
624
628
  };
625
629
  case completeEventType:
@@ -697,7 +701,6 @@ function fromObservable(observableCreator) {
697
701
  subscription: undefined
698
702
  })
699
703
  };
700
- return logic;
701
704
  }
702
705
 
703
706
  /**
@@ -714,7 +717,7 @@ function fromEventObservable(lazyObservable) {
714
717
  const completeEventType = '$$xstate.complete';
715
718
 
716
719
  // TODO: event types
717
- const logic = {
720
+ return {
718
721
  config: lazyObservable,
719
722
  transition: (state, event) => {
720
723
  if (state.status !== 'active') {
@@ -727,6 +730,7 @@ function fromEventObservable(lazyObservable) {
727
730
  status: 'error',
728
731
  input: undefined,
729
732
  data: event.data,
733
+ // TODO: if we keep this as `data` we should reflect this in the type
730
734
  subscription: undefined
731
735
  };
732
736
  case completeEventType:
@@ -801,7 +805,6 @@ function fromEventObservable(lazyObservable) {
801
805
  subscription: undefined
802
806
  })
803
807
  };
804
- return logic;
805
808
  }
806
809
 
807
810
  const resolveEventType = '$$xstate.resolve';
@@ -829,6 +832,7 @@ promiseCreator) {
829
832
  ...state,
830
833
  status: 'error',
831
834
  data: event.data,
835
+ // TODO: if we keep this as `data` we should reflect this in the type
832
836
  input: undefined
833
837
  };
834
838
  case stopSignalType:
@@ -938,6 +942,19 @@ function createEmptyActor() {
938
942
  return interpret(emptyLogic);
939
943
  }
940
944
 
945
+ /**
946
+ * This function makes sure that unhandled errors are thrown in a separate macrotask.
947
+ * It allows those errors to be detected by global error handlers and reported to bug tracking services
948
+ * without interrupting our own stack of execution.
949
+ *
950
+ * @param err error to be thrown
951
+ */
952
+ function reportUnhandledError(err) {
953
+ setTimeout(() => {
954
+ throw err;
955
+ });
956
+ }
957
+
941
958
  function createSystem() {
942
959
  let sessionIdCounter = 0;
943
960
  const children = new Map();
@@ -1106,29 +1123,38 @@ class Interpreter {
1106
1123
  deferredFn();
1107
1124
  }
1108
1125
  for (const observer of this.observers) {
1109
- observer.next?.(snapshot);
1126
+ // TODO: should observers be notified in case of the error?
1127
+ try {
1128
+ observer.next?.(snapshot);
1129
+ } catch (err) {
1130
+ reportUnhandledError(err);
1131
+ }
1110
1132
  }
1111
1133
  const status = this.logic.getStatus?.(state);
1112
1134
  switch (status?.status) {
1113
1135
  case 'done':
1114
1136
  this._stopProcedure();
1137
+ this._complete();
1115
1138
  this._doneEvent = doneInvoke(this.id, status.data);
1116
1139
  this._parent?.send(this._doneEvent);
1117
- this._complete();
1118
1140
  break;
1119
1141
  case 'error':
1120
1142
  this._stopProcedure();
1121
- this._parent?.send(error(this.id, status.data));
1122
1143
  this._error(status.data);
1144
+ this._parent?.send(error(this.id, status.data));
1123
1145
  break;
1124
1146
  }
1125
1147
  }
1126
1148
  subscribe(nextListenerOrObserver, errorListener, completeListener) {
1127
1149
  const observer = toObserver(nextListenerOrObserver, errorListener, completeListener);
1128
- this.observers.add(observer);
1129
- if (this.status === ActorStatus.Stopped) {
1130
- observer.complete?.();
1131
- this.observers.delete(observer);
1150
+ if (this.status !== ActorStatus.Stopped) {
1151
+ this.observers.add(observer);
1152
+ } else {
1153
+ try {
1154
+ observer.complete?.();
1155
+ } catch (err) {
1156
+ reportUnhandledError(err);
1157
+ }
1132
1158
  }
1133
1159
  return {
1134
1160
  unsubscribe: () => {
@@ -1150,8 +1176,26 @@ class Interpreter {
1150
1176
  this.system._set(this._systemId, this);
1151
1177
  }
1152
1178
  this.status = ActorStatus.Running;
1179
+ const status = this.logic.getStatus?.(this._state);
1180
+ switch (status?.status) {
1181
+ case 'done':
1182
+ // a state machine can be "done" upon intialization (it could reach a final state using initial microsteps)
1183
+ // we still need to complete observers, flush deferreds etc
1184
+ this.update(this._state);
1185
+ // fallthrough
1186
+ case 'error':
1187
+ // TODO: rethink cleanup of observers, mailbox, etc
1188
+ return this;
1189
+ }
1153
1190
  if (this.logic.start) {
1154
- this.logic.start(this._state, this._actorContext);
1191
+ try {
1192
+ this.logic.start(this._state, this._actorContext);
1193
+ } catch (err) {
1194
+ this._stopProcedure();
1195
+ this._error(err);
1196
+ this._parent?.send(error(this.id, err));
1197
+ return this;
1198
+ }
1155
1199
  }
1156
1200
 
1157
1201
  // TODO: this notifies all subscribers but usually this is redundant
@@ -1165,23 +1209,30 @@ class Interpreter {
1165
1209
  return this;
1166
1210
  }
1167
1211
  _process(event) {
1212
+ // TODO: reexamine what happens when an action (or a guard or smth) throws
1213
+ let nextState;
1214
+ let caughtError;
1168
1215
  try {
1169
- const nextState = this.logic.transition(this._state, event, this._actorContext);
1170
- this.update(nextState);
1171
- if (event.type === stopSignalType) {
1172
- this._stopProcedure();
1173
- this._complete();
1174
- }
1216
+ nextState = this.logic.transition(this._state, event, this._actorContext);
1175
1217
  } catch (err) {
1176
- // TODO: properly handle errors
1177
- if (this.observers.size > 0) {
1178
- this.observers.forEach(observer => {
1179
- observer.error?.(err);
1180
- });
1181
- this.stop();
1182
- } else {
1183
- throw err;
1184
- }
1218
+ // we wrap it in a box so we can rethrow it later even if falsy value gets caught here
1219
+ caughtError = {
1220
+ err
1221
+ };
1222
+ }
1223
+ if (caughtError) {
1224
+ const {
1225
+ err
1226
+ } = caughtError;
1227
+ this._stopProcedure();
1228
+ this._error(err);
1229
+ this._parent?.send(error(this.id, err));
1230
+ return;
1231
+ }
1232
+ this.update(nextState);
1233
+ if (event.type === stopSignalType) {
1234
+ this._stopProcedure();
1235
+ this._complete();
1185
1236
  }
1186
1237
  }
1187
1238
  _stop() {
@@ -1210,15 +1261,35 @@ class Interpreter {
1210
1261
  }
1211
1262
  _complete() {
1212
1263
  for (const observer of this.observers) {
1213
- observer.complete?.();
1264
+ try {
1265
+ observer.complete?.();
1266
+ } catch (err) {
1267
+ reportUnhandledError(err);
1268
+ }
1214
1269
  }
1215
1270
  this.observers.clear();
1216
1271
  }
1217
- _error(data) {
1272
+ _error(err) {
1273
+ if (!this.observers.size) {
1274
+ if (!this._parent) {
1275
+ reportUnhandledError(err);
1276
+ }
1277
+ return;
1278
+ }
1279
+ let reportError = false;
1218
1280
  for (const observer of this.observers) {
1219
- observer.error?.(data);
1281
+ const errorListener = observer.error;
1282
+ reportError ||= !errorListener;
1283
+ try {
1284
+ errorListener?.(err);
1285
+ } catch (err2) {
1286
+ reportUnhandledError(err2);
1287
+ }
1220
1288
  }
1221
1289
  this.observers.clear();
1290
+ if (reportError) {
1291
+ reportUnhandledError(err);
1292
+ }
1222
1293
  }
1223
1294
  _stopProcedure() {
1224
1295
  if (this.status !== ActorStatus.Running) {
@@ -1551,10 +1622,10 @@ function toGuardDefinition(guardConfig, getPredicate) {
1551
1622
  }
1552
1623
  }
1553
1624
 
1554
- function getOutput(configuration, context, event) {
1625
+ function getOutput(configuration, context, event, self) {
1555
1626
  const machine = configuration[0].machine;
1556
1627
  const finalChildStateNode = configuration.find(stateNode => stateNode.type === 'final' && stateNode.parent === machine.root);
1557
- return finalChildStateNode && finalChildStateNode.output ? mapContext(finalChildStateNode.output, context, event) : undefined;
1628
+ return finalChildStateNode && finalChildStateNode.output ? mapContext(finalChildStateNode.output, context, event, self) : undefined;
1558
1629
  }
1559
1630
  const isAtomicStateNode = stateNode => stateNode.type === 'atomic' || stateNode.type === 'final';
1560
1631
  function getChildren(stateNode) {
@@ -2181,7 +2252,7 @@ function microstepProcedure(transitions, currentState, mutConfiguration, event,
2181
2252
  actions.push(...filteredTransitions.flatMap(t => t.actions));
2182
2253
 
2183
2254
  // Enter states
2184
- enterStates(event, filteredTransitions, mutConfiguration, actions, internalQueue, currentState, historyValue, isInitial);
2255
+ enterStates(event, filteredTransitions, mutConfiguration, actions, internalQueue, currentState, historyValue, isInitial, actorCtx);
2185
2256
  const nextConfiguration = [...mutConfiguration];
2186
2257
  const done = isInFinalState(nextConfiguration);
2187
2258
  if (done) {
@@ -2190,7 +2261,7 @@ function microstepProcedure(transitions, currentState, mutConfiguration, event,
2190
2261
  }
2191
2262
  try {
2192
2263
  const nextState = resolveActionsAndContext(actions, event, currentState, actorCtx);
2193
- const output = done ? getOutput(nextConfiguration, nextState.context, event) : undefined;
2264
+ const output = done ? getOutput(nextConfiguration, nextState.context, event, actorCtx.self) : undefined;
2194
2265
  internalQueue.push(...nextState._internalQueue);
2195
2266
  return cloneState(currentState, {
2196
2267
  configuration: nextConfiguration,
@@ -2207,7 +2278,7 @@ function microstepProcedure(transitions, currentState, mutConfiguration, event,
2207
2278
  throw e;
2208
2279
  }
2209
2280
  }
2210
- function enterStates(event, filteredTransitions, mutConfiguration, actions, internalQueue, currentState, historyValue, isInitial) {
2281
+ function enterStates(event, filteredTransitions, mutConfiguration, actions, internalQueue, currentState, historyValue, isInitial, actorContext) {
2211
2282
  const statesToEnter = new Set();
2212
2283
  const statesForDefaultEntry = new Set();
2213
2284
  computeEntrySet(filteredTransitions, historyValue, statesForDefaultEntry, statesToEnter);
@@ -2235,7 +2306,7 @@ function enterStates(event, filteredTransitions, mutConfiguration, actions, inte
2235
2306
  if (!parent.parent) {
2236
2307
  continue;
2237
2308
  }
2238
- internalQueue.push(done(parent.id, stateNodeToEnter.output ? mapContext(stateNodeToEnter.output, currentState.context, event) : undefined));
2309
+ internalQueue.push(done(parent.id, stateNodeToEnter.output ? mapContext(stateNodeToEnter.output, currentState.context, event, actorContext.self) : undefined));
2239
2310
  if (parent.parent) {
2240
2311
  const grandparent = parent.parent;
2241
2312
  if (grandparent.type === 'parallel') {
@@ -2558,6 +2629,7 @@ class State {
2558
2629
  this.value = void 0;
2559
2630
  this.done = void 0;
2560
2631
  this.output = void 0;
2632
+ this.error = void 0;
2561
2633
  this.context = void 0;
2562
2634
  this.historyValue = {};
2563
2635
  this._internalQueue = void 0;
@@ -2574,6 +2646,7 @@ class State {
2574
2646
  this.tags = new Set(flatten(this.configuration.map(sn => sn.tags)));
2575
2647
  this.done = config.done ?? false;
2576
2648
  this.output = config.output;
2649
+ this.error = config.error;
2577
2650
  }
2578
2651
 
2579
2652
  /**
@@ -3044,4 +3117,4 @@ function createInitEvent(input) {
3044
3117
  };
3045
3118
  }
3046
3119
 
3047
- 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 };
3120
+ 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 };