xstate 5.17.4 → 5.18.1

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 (37) 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.map +1 -1
  6. package/actors/dist/xstate-actors.cjs.js +7 -8
  7. package/actors/dist/xstate-actors.development.cjs.js +7 -8
  8. package/actors/dist/xstate-actors.development.esm.js +7 -8
  9. package/actors/dist/xstate-actors.esm.js +7 -8
  10. package/actors/dist/xstate-actors.umd.min.js.map +1 -1
  11. package/dist/declarations/src/State.d.ts +3 -3
  12. package/dist/declarations/src/actors/callback.d.ts +5 -6
  13. package/dist/declarations/src/actors/index.d.ts +1 -1
  14. package/dist/declarations/src/createActor.d.ts +3 -4
  15. package/dist/declarations/src/index.d.ts +5 -5
  16. package/dist/declarations/src/setup.d.ts +1 -17
  17. package/dist/declarations/src/types.d.ts +20 -0
  18. package/dist/declarations/src/waitFor.d.ts +2 -0
  19. package/dist/{log-f7dcaa97.cjs.js → log-098d2ed5.cjs.js} +1 -1
  20. package/dist/{log-54d038f7.esm.js → log-a2c94240.esm.js} +1 -1
  21. package/dist/{log-4f8360d3.development.esm.js → log-c92a07bc.development.esm.js} +1 -1
  22. package/dist/{log-40d606d3.development.cjs.js → log-d26be77d.development.cjs.js} +1 -1
  23. package/dist/{State-34039d2a.development.esm.js → raise-206d3d29.development.esm.js} +206 -206
  24. package/dist/{State-cdbc7940.esm.js → raise-c0e3c984.esm.js} +191 -191
  25. package/dist/xstate.cjs.js +129 -103
  26. package/dist/xstate.development.cjs.js +129 -103
  27. package/dist/xstate.development.esm.js +125 -99
  28. package/dist/xstate.esm.js +125 -99
  29. package/dist/xstate.umd.min.js +1 -1
  30. package/dist/xstate.umd.min.js.map +1 -1
  31. package/guards/dist/xstate-guards.cjs.js +1 -1
  32. package/guards/dist/xstate-guards.development.cjs.js +1 -1
  33. package/guards/dist/xstate-guards.development.esm.js +1 -1
  34. package/guards/dist/xstate-guards.esm.js +1 -1
  35. package/package.json +1 -1
  36. package/dist/{State-a2464a1e.development.cjs.js → raise-830a98f7.development.cjs.js} +205 -205
  37. package/dist/{State-30c95050.cjs.js → raise-cde45f56.cjs.js} +190 -190
@@ -2,74 +2,39 @@
2
2
 
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
- var guards_dist_xstateGuards = require('./State-a2464a1e.development.cjs.js');
6
- var log = require('./log-40d606d3.development.cjs.js');
7
5
  var actors_dist_xstateActors = require('../actors/dist/xstate-actors.development.cjs.js');
6
+ var guards_dist_xstateGuards = require('./raise-830a98f7.development.cjs.js');
7
+ var log = require('./log-d26be77d.development.cjs.js');
8
8
  require('../dev/dist/xstate-dev.development.cjs.js');
9
9
 
10
- class SimulatedClock {
11
- constructor() {
12
- this.timeouts = new Map();
13
- this._now = 0;
14
- this._id = 0;
15
- this._flushing = false;
16
- this._flushingInvalidated = false;
17
- }
18
- now() {
19
- return this._now;
20
- }
21
- getId() {
22
- return this._id++;
23
- }
24
- setTimeout(fn, timeout) {
25
- this._flushingInvalidated = this._flushing;
26
- const id = this.getId();
27
- this.timeouts.set(id, {
28
- start: this.now(),
29
- timeout,
30
- fn
31
- });
32
- return id;
33
- }
34
- clearTimeout(id) {
35
- this._flushingInvalidated = this._flushing;
36
- this.timeouts.delete(id);
37
- }
38
- set(time) {
39
- if (this._now > time) {
40
- throw new Error('Unable to travel back in time');
41
- }
42
- this._now = time;
43
- this.flushTimeouts();
44
- }
45
- flushTimeouts() {
46
- if (this._flushing) {
47
- this._flushingInvalidated = true;
48
- return;
49
- }
50
- this._flushing = true;
51
- const sorted = [...this.timeouts].sort(([_idA, timeoutA], [_idB, timeoutB]) => {
52
- const endA = timeoutA.start + timeoutA.timeout;
53
- const endB = timeoutB.start + timeoutB.timeout;
54
- return endB > endA ? -1 : 1;
55
- });
56
- for (const [id, timeout] of sorted) {
57
- if (this._flushingInvalidated) {
58
- this._flushingInvalidated = false;
59
- this._flushing = false;
60
- this.flushTimeouts();
61
- return;
62
- }
63
- if (this.now() - timeout.start >= timeout.timeout) {
64
- this.timeouts.delete(id);
65
- timeout.fn.call(null);
66
- }
67
- }
68
- this._flushing = false;
69
- }
70
- increment(ms) {
71
- this._now += ms;
72
- this.flushTimeouts();
10
+ /**
11
+ * Asserts that the given event object is of the specified type or types. Throws
12
+ * an error if the event object is not of the specified types.
13
+ *
14
+ * @example
15
+ *
16
+ * ```ts
17
+ * // ...
18
+ * entry: ({ event }) => {
19
+ * assertEvent(event, 'doNothing');
20
+ * // event is { type: 'doNothing' }
21
+ * },
22
+ * // ...
23
+ * exit: ({ event }) => {
24
+ * assertEvent(event, 'greet');
25
+ * // event is { type: 'greet'; message: string }
26
+ *
27
+ * assertEvent(event, ['greet', 'notify']);
28
+ * // event is { type: 'greet'; message: string }
29
+ * // or { type: 'notify'; message: string; level: 'info' | 'error' }
30
+ * },
31
+ * ```
32
+ */
33
+ function assertEvent(event, type) {
34
+ const types = guards_dist_xstateGuards.toArray(type);
35
+ if (!types.includes(event.type)) {
36
+ const typesText = types.length === 1 ? `type "${types[0]}"` : `one of types "${types.join('", "')}"`;
37
+ throw new Error(`Expected event ${JSON.stringify(event)} to have ${typesText}`);
73
38
  }
74
39
  }
75
40
 
@@ -601,37 +566,6 @@ class StateMachine {
601
566
  }
602
567
  }
603
568
 
604
- /**
605
- * Asserts that the given event object is of the specified type or types. Throws
606
- * an error if the event object is not of the specified types.
607
- *
608
- * @example
609
- *
610
- * ```ts
611
- * // ...
612
- * entry: ({ event }) => {
613
- * assertEvent(event, 'doNothing');
614
- * // event is { type: 'doNothing' }
615
- * },
616
- * // ...
617
- * exit: ({ event }) => {
618
- * assertEvent(event, 'greet');
619
- * // event is { type: 'greet'; message: string }
620
- *
621
- * assertEvent(event, ['greet', 'notify']);
622
- * // event is { type: 'greet'; message: string }
623
- * // or { type: 'notify'; message: string; level: 'info' | 'error' }
624
- * },
625
- * ```
626
- */
627
- function assertEvent(event, type) {
628
- const types = guards_dist_xstateGuards.toArray(type);
629
- if (!types.includes(event.type)) {
630
- const typesText = types.length === 1 ? `type "${types[0]}"` : `one of types "${types.join('", "')}"`;
631
- throw new Error(`Expected event ${JSON.stringify(event)} to have ${typesText}`);
632
- }
633
- }
634
-
635
569
  // this is not 100% accurate since we can't make parallel regions required in the result
636
570
  // `TTestValue` doesn't encode this information anyhow for us to be able to do that
637
571
  // this is fine for most practical use cases anyway though
@@ -761,6 +695,72 @@ function setup({
761
695
  };
762
696
  }
763
697
 
698
+ class SimulatedClock {
699
+ constructor() {
700
+ this.timeouts = new Map();
701
+ this._now = 0;
702
+ this._id = 0;
703
+ this._flushing = false;
704
+ this._flushingInvalidated = false;
705
+ }
706
+ now() {
707
+ return this._now;
708
+ }
709
+ getId() {
710
+ return this._id++;
711
+ }
712
+ setTimeout(fn, timeout) {
713
+ this._flushingInvalidated = this._flushing;
714
+ const id = this.getId();
715
+ this.timeouts.set(id, {
716
+ start: this.now(),
717
+ timeout,
718
+ fn
719
+ });
720
+ return id;
721
+ }
722
+ clearTimeout(id) {
723
+ this._flushingInvalidated = this._flushing;
724
+ this.timeouts.delete(id);
725
+ }
726
+ set(time) {
727
+ if (this._now > time) {
728
+ throw new Error('Unable to travel back in time');
729
+ }
730
+ this._now = time;
731
+ this.flushTimeouts();
732
+ }
733
+ flushTimeouts() {
734
+ if (this._flushing) {
735
+ this._flushingInvalidated = true;
736
+ return;
737
+ }
738
+ this._flushing = true;
739
+ const sorted = [...this.timeouts].sort(([_idA, timeoutA], [_idB, timeoutB]) => {
740
+ const endA = timeoutA.start + timeoutA.timeout;
741
+ const endB = timeoutB.start + timeoutB.timeout;
742
+ return endB > endA ? -1 : 1;
743
+ });
744
+ for (const [id, timeout] of sorted) {
745
+ if (this._flushingInvalidated) {
746
+ this._flushingInvalidated = false;
747
+ this._flushing = false;
748
+ this.flushTimeouts();
749
+ return;
750
+ }
751
+ if (this.now() - timeout.start >= timeout.timeout) {
752
+ this.timeouts.delete(id);
753
+ timeout.fn.call(null);
754
+ }
755
+ }
756
+ this._flushing = false;
757
+ }
758
+ increment(ms) {
759
+ this._now += ms;
760
+ this.flushTimeouts();
761
+ }
762
+ }
763
+
764
764
  /**
765
765
  * Returns a promise that resolves to the `output` of the actor when it is done.
766
766
  *
@@ -826,18 +826,28 @@ function waitFor(actorRef, predicate, options) {
826
826
  ...options
827
827
  };
828
828
  return new Promise((res, rej) => {
829
+ const {
830
+ signal
831
+ } = resolvedOptions;
832
+ if (signal?.aborted) {
833
+ rej(signal.reason);
834
+ return;
835
+ }
829
836
  let done = false;
830
837
  if (resolvedOptions.timeout < 0) {
831
838
  console.error('`timeout` passed to `waitFor` is negative and it will reject its internal promise immediately.');
832
839
  }
833
840
  const handle = resolvedOptions.timeout === Infinity ? undefined : setTimeout(() => {
834
- sub.unsubscribe();
841
+ dispose();
835
842
  rej(new Error(`Timeout of ${resolvedOptions.timeout} ms exceeded`));
836
843
  }, resolvedOptions.timeout);
837
844
  const dispose = () => {
838
845
  clearTimeout(handle);
839
846
  done = true;
840
847
  sub?.unsubscribe();
848
+ if (abortListener) {
849
+ signal.removeEventListener('abort', abortListener);
850
+ }
841
851
  };
842
852
  function checkEmitted(emitted) {
843
853
  if (predicate(emitted)) {
@@ -845,6 +855,12 @@ function waitFor(actorRef, predicate, options) {
845
855
  res(emitted);
846
856
  }
847
857
  }
858
+
859
+ /**
860
+ * If the `signal` option is provided, this will be the listener for its
861
+ * `abort` event
862
+ */
863
+ let abortListener;
848
864
  let sub; // avoid TDZ when disposing synchronously
849
865
 
850
866
  // See if the current snapshot already matches the predicate
@@ -852,6 +868,16 @@ function waitFor(actorRef, predicate, options) {
852
868
  if (done) {
853
869
  return;
854
870
  }
871
+
872
+ // only define the `abortListener` if the `signal` option is provided
873
+ if (signal) {
874
+ abortListener = () => {
875
+ dispose();
876
+ // XState does not "own" the signal, so we should reject with its reason (if any)
877
+ rej(signal.reason);
878
+ };
879
+ signal.addEventListener('abort', abortListener);
880
+ }
855
881
  sub = actorRef.subscribe({
856
882
  next: checkEmitted,
857
883
  error: err => {
@@ -869,6 +895,12 @@ function waitFor(actorRef, predicate, options) {
869
895
  });
870
896
  }
871
897
 
898
+ exports.createEmptyActor = actors_dist_xstateActors.createEmptyActor;
899
+ exports.fromCallback = actors_dist_xstateActors.fromCallback;
900
+ exports.fromEventObservable = actors_dist_xstateActors.fromEventObservable;
901
+ exports.fromObservable = actors_dist_xstateActors.fromObservable;
902
+ exports.fromPromise = actors_dist_xstateActors.fromPromise;
903
+ exports.fromTransition = actors_dist_xstateActors.fromTransition;
872
904
  exports.Actor = guards_dist_xstateGuards.Actor;
873
905
  exports.__unsafe_getAllOwnEventDescriptors = guards_dist_xstateGuards.getAllOwnEventDescriptors;
874
906
  exports.and = guards_dist_xstateGuards.and;
@@ -895,12 +927,6 @@ exports.forwardTo = log.forwardTo;
895
927
  exports.log = log.log;
896
928
  exports.sendParent = log.sendParent;
897
929
  exports.sendTo = log.sendTo;
898
- exports.createEmptyActor = actors_dist_xstateActors.createEmptyActor;
899
- exports.fromCallback = actors_dist_xstateActors.fromCallback;
900
- exports.fromEventObservable = actors_dist_xstateActors.fromEventObservable;
901
- exports.fromObservable = actors_dist_xstateActors.fromObservable;
902
- exports.fromPromise = actors_dist_xstateActors.fromPromise;
903
- exports.fromTransition = actors_dist_xstateActors.fromTransition;
904
930
  exports.SimulatedClock = SimulatedClock;
905
931
  exports.StateMachine = StateMachine;
906
932
  exports.StateNode = StateNode;
@@ -1,73 +1,38 @@
1
- import { S as STATE_DELIMITER, m as mapValues, t as toArray, f as formatTransitions, a as toTransitionConfigArray, b as formatTransition, N as NULL_EVENT, e as evaluateGuard, c as createInvokeId, g as getDelayedTransitions, d as formatInitialTransition, h as getCandidates, r as resolveStateValue, i as getAllStateNodes, j as getStateNodes, k as createMachineSnapshot, l as isInFinalState, n as macrostep, o as transitionNode, p as resolveActionsAndContext, q as createInitEvent, s as microstep, u as getInitialStateNodes, v as toStatePath, w as isStateId, x as getStateNodeByPath, y as getPersistedSnapshot, z as resolveReferencedActor, A as createActor, $ as $$ACTOR_TYPE } from './State-34039d2a.development.esm.js';
2
- export { C as Actor, I as __unsafe_getAllOwnEventDescriptors, E as and, M as cancel, A as createActor, j as getStateNodes, D as interpret, B as isMachineSnapshot, J as matchesState, F as not, G as or, K as pathToStateValue, O as raise, P as spawnChild, H as stateIn, Q as stop, R as stopChild, L as toObserver } from './State-34039d2a.development.esm.js';
3
- import { a as assign } from './log-4f8360d3.development.esm.js';
4
- export { S as SpecialTargets, a as assign, e as emit, b as enqueueActions, f as forwardTo, l as log, s as sendParent, c as sendTo } from './log-4f8360d3.development.esm.js';
5
1
  export { createEmptyActor, fromCallback, fromEventObservable, fromObservable, fromPromise, fromTransition } from '../actors/dist/xstate-actors.development.esm.js';
2
+ import { t as toArray, S as STATE_DELIMITER, m as mapValues, f as formatTransitions, a as toTransitionConfigArray, b as formatTransition, N as NULL_EVENT, e as evaluateGuard, c as createInvokeId, g as getDelayedTransitions, d as formatInitialTransition, h as getCandidates, r as resolveStateValue, i as getAllStateNodes, j as getStateNodes, k as createMachineSnapshot, l as isInFinalState, n as macrostep, o as transitionNode, p as resolveActionsAndContext, q as createInitEvent, s as microstep, u as getInitialStateNodes, v as toStatePath, w as isStateId, x as getStateNodeByPath, y as getPersistedSnapshot, z as resolveReferencedActor, A as createActor, $ as $$ACTOR_TYPE } from './raise-206d3d29.development.esm.js';
3
+ export { B as Actor, I as __unsafe_getAllOwnEventDescriptors, D as and, M as cancel, A as createActor, j as getStateNodes, C as interpret, H as isMachineSnapshot, J as matchesState, E as not, F as or, K as pathToStateValue, O as raise, P as spawnChild, G as stateIn, Q as stop, R as stopChild, L as toObserver } from './raise-206d3d29.development.esm.js';
4
+ import { a as assign } from './log-c92a07bc.development.esm.js';
5
+ export { S as SpecialTargets, a as assign, e as emit, b as enqueueActions, f as forwardTo, l as log, s as sendParent, c as sendTo } from './log-c92a07bc.development.esm.js';
6
6
  import '../dev/dist/xstate-dev.development.esm.js';
7
7
 
8
- class SimulatedClock {
9
- constructor() {
10
- this.timeouts = new Map();
11
- this._now = 0;
12
- this._id = 0;
13
- this._flushing = false;
14
- this._flushingInvalidated = false;
15
- }
16
- now() {
17
- return this._now;
18
- }
19
- getId() {
20
- return this._id++;
21
- }
22
- setTimeout(fn, timeout) {
23
- this._flushingInvalidated = this._flushing;
24
- const id = this.getId();
25
- this.timeouts.set(id, {
26
- start: this.now(),
27
- timeout,
28
- fn
29
- });
30
- return id;
31
- }
32
- clearTimeout(id) {
33
- this._flushingInvalidated = this._flushing;
34
- this.timeouts.delete(id);
35
- }
36
- set(time) {
37
- if (this._now > time) {
38
- throw new Error('Unable to travel back in time');
39
- }
40
- this._now = time;
41
- this.flushTimeouts();
42
- }
43
- flushTimeouts() {
44
- if (this._flushing) {
45
- this._flushingInvalidated = true;
46
- return;
47
- }
48
- this._flushing = true;
49
- const sorted = [...this.timeouts].sort(([_idA, timeoutA], [_idB, timeoutB]) => {
50
- const endA = timeoutA.start + timeoutA.timeout;
51
- const endB = timeoutB.start + timeoutB.timeout;
52
- return endB > endA ? -1 : 1;
53
- });
54
- for (const [id, timeout] of sorted) {
55
- if (this._flushingInvalidated) {
56
- this._flushingInvalidated = false;
57
- this._flushing = false;
58
- this.flushTimeouts();
59
- return;
60
- }
61
- if (this.now() - timeout.start >= timeout.timeout) {
62
- this.timeouts.delete(id);
63
- timeout.fn.call(null);
64
- }
65
- }
66
- this._flushing = false;
67
- }
68
- increment(ms) {
69
- this._now += ms;
70
- this.flushTimeouts();
8
+ /**
9
+ * Asserts that the given event object is of the specified type or types. Throws
10
+ * an error if the event object is not of the specified types.
11
+ *
12
+ * @example
13
+ *
14
+ * ```ts
15
+ * // ...
16
+ * entry: ({ event }) => {
17
+ * assertEvent(event, 'doNothing');
18
+ * // event is { type: 'doNothing' }
19
+ * },
20
+ * // ...
21
+ * exit: ({ event }) => {
22
+ * assertEvent(event, 'greet');
23
+ * // event is { type: 'greet'; message: string }
24
+ *
25
+ * assertEvent(event, ['greet', 'notify']);
26
+ * // event is { type: 'greet'; message: string }
27
+ * // or { type: 'notify'; message: string; level: 'info' | 'error' }
28
+ * },
29
+ * ```
30
+ */
31
+ function assertEvent(event, type) {
32
+ const types = toArray(type);
33
+ if (!types.includes(event.type)) {
34
+ const typesText = types.length === 1 ? `type "${types[0]}"` : `one of types "${types.join('", "')}"`;
35
+ throw new Error(`Expected event ${JSON.stringify(event)} to have ${typesText}`);
71
36
  }
72
37
  }
73
38
 
@@ -599,37 +564,6 @@ class StateMachine {
599
564
  }
600
565
  }
601
566
 
602
- /**
603
- * Asserts that the given event object is of the specified type or types. Throws
604
- * an error if the event object is not of the specified types.
605
- *
606
- * @example
607
- *
608
- * ```ts
609
- * // ...
610
- * entry: ({ event }) => {
611
- * assertEvent(event, 'doNothing');
612
- * // event is { type: 'doNothing' }
613
- * },
614
- * // ...
615
- * exit: ({ event }) => {
616
- * assertEvent(event, 'greet');
617
- * // event is { type: 'greet'; message: string }
618
- *
619
- * assertEvent(event, ['greet', 'notify']);
620
- * // event is { type: 'greet'; message: string }
621
- * // or { type: 'notify'; message: string; level: 'info' | 'error' }
622
- * },
623
- * ```
624
- */
625
- function assertEvent(event, type) {
626
- const types = toArray(type);
627
- if (!types.includes(event.type)) {
628
- const typesText = types.length === 1 ? `type "${types[0]}"` : `one of types "${types.join('", "')}"`;
629
- throw new Error(`Expected event ${JSON.stringify(event)} to have ${typesText}`);
630
- }
631
- }
632
-
633
567
  // this is not 100% accurate since we can't make parallel regions required in the result
634
568
  // `TTestValue` doesn't encode this information anyhow for us to be able to do that
635
569
  // this is fine for most practical use cases anyway though
@@ -759,6 +693,72 @@ function setup({
759
693
  };
760
694
  }
761
695
 
696
+ class SimulatedClock {
697
+ constructor() {
698
+ this.timeouts = new Map();
699
+ this._now = 0;
700
+ this._id = 0;
701
+ this._flushing = false;
702
+ this._flushingInvalidated = false;
703
+ }
704
+ now() {
705
+ return this._now;
706
+ }
707
+ getId() {
708
+ return this._id++;
709
+ }
710
+ setTimeout(fn, timeout) {
711
+ this._flushingInvalidated = this._flushing;
712
+ const id = this.getId();
713
+ this.timeouts.set(id, {
714
+ start: this.now(),
715
+ timeout,
716
+ fn
717
+ });
718
+ return id;
719
+ }
720
+ clearTimeout(id) {
721
+ this._flushingInvalidated = this._flushing;
722
+ this.timeouts.delete(id);
723
+ }
724
+ set(time) {
725
+ if (this._now > time) {
726
+ throw new Error('Unable to travel back in time');
727
+ }
728
+ this._now = time;
729
+ this.flushTimeouts();
730
+ }
731
+ flushTimeouts() {
732
+ if (this._flushing) {
733
+ this._flushingInvalidated = true;
734
+ return;
735
+ }
736
+ this._flushing = true;
737
+ const sorted = [...this.timeouts].sort(([_idA, timeoutA], [_idB, timeoutB]) => {
738
+ const endA = timeoutA.start + timeoutA.timeout;
739
+ const endB = timeoutB.start + timeoutB.timeout;
740
+ return endB > endA ? -1 : 1;
741
+ });
742
+ for (const [id, timeout] of sorted) {
743
+ if (this._flushingInvalidated) {
744
+ this._flushingInvalidated = false;
745
+ this._flushing = false;
746
+ this.flushTimeouts();
747
+ return;
748
+ }
749
+ if (this.now() - timeout.start >= timeout.timeout) {
750
+ this.timeouts.delete(id);
751
+ timeout.fn.call(null);
752
+ }
753
+ }
754
+ this._flushing = false;
755
+ }
756
+ increment(ms) {
757
+ this._now += ms;
758
+ this.flushTimeouts();
759
+ }
760
+ }
761
+
762
762
  /**
763
763
  * Returns a promise that resolves to the `output` of the actor when it is done.
764
764
  *
@@ -824,18 +824,28 @@ function waitFor(actorRef, predicate, options) {
824
824
  ...options
825
825
  };
826
826
  return new Promise((res, rej) => {
827
+ const {
828
+ signal
829
+ } = resolvedOptions;
830
+ if (signal?.aborted) {
831
+ rej(signal.reason);
832
+ return;
833
+ }
827
834
  let done = false;
828
835
  if (resolvedOptions.timeout < 0) {
829
836
  console.error('`timeout` passed to `waitFor` is negative and it will reject its internal promise immediately.');
830
837
  }
831
838
  const handle = resolvedOptions.timeout === Infinity ? undefined : setTimeout(() => {
832
- sub.unsubscribe();
839
+ dispose();
833
840
  rej(new Error(`Timeout of ${resolvedOptions.timeout} ms exceeded`));
834
841
  }, resolvedOptions.timeout);
835
842
  const dispose = () => {
836
843
  clearTimeout(handle);
837
844
  done = true;
838
845
  sub?.unsubscribe();
846
+ if (abortListener) {
847
+ signal.removeEventListener('abort', abortListener);
848
+ }
839
849
  };
840
850
  function checkEmitted(emitted) {
841
851
  if (predicate(emitted)) {
@@ -843,6 +853,12 @@ function waitFor(actorRef, predicate, options) {
843
853
  res(emitted);
844
854
  }
845
855
  }
856
+
857
+ /**
858
+ * If the `signal` option is provided, this will be the listener for its
859
+ * `abort` event
860
+ */
861
+ let abortListener;
846
862
  let sub; // avoid TDZ when disposing synchronously
847
863
 
848
864
  // See if the current snapshot already matches the predicate
@@ -850,6 +866,16 @@ function waitFor(actorRef, predicate, options) {
850
866
  if (done) {
851
867
  return;
852
868
  }
869
+
870
+ // only define the `abortListener` if the `signal` option is provided
871
+ if (signal) {
872
+ abortListener = () => {
873
+ dispose();
874
+ // XState does not "own" the signal, so we should reject with its reason (if any)
875
+ rej(signal.reason);
876
+ };
877
+ signal.addEventListener('abort', abortListener);
878
+ }
853
879
  sub = actorRef.subscribe({
854
880
  next: checkEmitted,
855
881
  error: err => {