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.
- package/actions/dist/xstate-actions.cjs.js +2 -2
- package/actions/dist/xstate-actions.development.cjs.js +2 -2
- package/actions/dist/xstate-actions.development.esm.js +2 -2
- package/actions/dist/xstate-actions.esm.js +2 -2
- package/actions/dist/xstate-actions.umd.min.js.map +1 -1
- package/actors/dist/xstate-actors.cjs.js +7 -8
- package/actors/dist/xstate-actors.development.cjs.js +7 -8
- package/actors/dist/xstate-actors.development.esm.js +7 -8
- package/actors/dist/xstate-actors.esm.js +7 -8
- package/actors/dist/xstate-actors.umd.min.js.map +1 -1
- package/dist/declarations/src/State.d.ts +3 -3
- package/dist/declarations/src/actors/callback.d.ts +5 -6
- package/dist/declarations/src/actors/index.d.ts +1 -1
- package/dist/declarations/src/createActor.d.ts +3 -4
- package/dist/declarations/src/index.d.ts +5 -5
- package/dist/declarations/src/setup.d.ts +1 -17
- package/dist/declarations/src/types.d.ts +20 -0
- package/dist/declarations/src/waitFor.d.ts +2 -0
- package/dist/{log-f7dcaa97.cjs.js → log-098d2ed5.cjs.js} +1 -1
- package/dist/{log-54d038f7.esm.js → log-a2c94240.esm.js} +1 -1
- package/dist/{log-4f8360d3.development.esm.js → log-c92a07bc.development.esm.js} +1 -1
- package/dist/{log-40d606d3.development.cjs.js → log-d26be77d.development.cjs.js} +1 -1
- package/dist/{State-34039d2a.development.esm.js → raise-206d3d29.development.esm.js} +206 -206
- package/dist/{State-cdbc7940.esm.js → raise-c0e3c984.esm.js} +191 -191
- package/dist/xstate.cjs.js +129 -103
- package/dist/xstate.development.cjs.js +129 -103
- package/dist/xstate.development.esm.js +125 -99
- package/dist/xstate.esm.js +125 -99
- package/dist/xstate.umd.min.js +1 -1
- package/dist/xstate.umd.min.js.map +1 -1
- package/guards/dist/xstate-guards.cjs.js +1 -1
- package/guards/dist/xstate-guards.development.cjs.js +1 -1
- package/guards/dist/xstate-guards.development.esm.js +1 -1
- package/guards/dist/xstate-guards.esm.js +1 -1
- package/package.json +1 -1
- package/dist/{State-a2464a1e.development.cjs.js → raise-830a98f7.development.cjs.js} +205 -205
- 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
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
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
|
-
|
|
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
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
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
|
-
|
|
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 => {
|