xstate 5.0.0-beta.43 → 5.0.0-beta.45
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 -3
- package/actions/dist/xstate-actions.development.cjs.js +2 -3
- package/actions/dist/xstate-actions.development.esm.js +2 -3
- package/actions/dist/xstate-actions.esm.js +2 -3
- package/actions/dist/xstate-actions.umd.min.js +1 -1
- package/actions/dist/xstate-actions.umd.min.js.map +1 -1
- package/actors/dist/xstate-actors.cjs.js +98 -10
- package/actors/dist/xstate-actors.development.cjs.js +98 -10
- package/actors/dist/xstate-actors.development.esm.js +93 -5
- package/actors/dist/xstate-actors.esm.js +93 -5
- package/actors/dist/xstate-actors.umd.min.js +1 -1
- package/actors/dist/xstate-actors.umd.min.js.map +1 -1
- package/dist/declarations/src/State.d.ts +14 -18
- package/dist/declarations/src/StateMachine.d.ts +1 -1
- package/dist/declarations/src/actions/choose.d.ts +3 -3
- package/dist/declarations/src/actions/pure.d.ts +4 -4
- package/dist/declarations/src/actions/spawn.d.ts +11 -16
- package/dist/declarations/src/actors/observable.d.ts +39 -0
- package/dist/declarations/src/actors/transition.d.ts +53 -4
- package/dist/declarations/src/{Machine.d.ts → createMachine.d.ts} +1 -1
- package/dist/declarations/src/guards.d.ts +27 -5
- package/dist/declarations/src/index.d.ts +3 -2
- package/dist/declarations/src/interpreter.d.ts +1 -0
- package/dist/declarations/src/setup.d.ts +32 -0
- package/dist/declarations/src/spawn.d.ts +9 -13
- package/dist/declarations/src/stateUtils.d.ts +11 -11
- package/dist/declarations/src/types.d.ts +31 -29
- package/dist/declarations/src/utils.d.ts +1 -3
- package/dist/{raise-8dc8e1aa.esm.js → raise-2b5a4e4c.esm.js} +934 -103
- package/dist/{raise-f4ad5a87.development.esm.js → raise-90139fbc.development.esm.js} +945 -103
- package/dist/{raise-23dea0d7.development.cjs.js → raise-b3fb3c65.development.cjs.js} +999 -137
- package/dist/{raise-e0fe5c2d.cjs.js → raise-fabffc3d.cjs.js} +986 -135
- package/dist/{send-5d129d95.development.esm.js → send-24cc8018.development.esm.js} +4 -30
- package/dist/{send-84e2e742.esm.js → send-8e7e41e7.esm.js} +4 -30
- package/dist/{send-87bbaaab.cjs.js → send-c124176f.cjs.js} +13 -39
- package/dist/{send-0174c155.development.cjs.js → send-d0bc7eed.development.cjs.js} +13 -39
- package/dist/xstate.cjs.js +67 -35
- package/dist/xstate.cjs.mjs +2 -0
- package/dist/xstate.development.cjs.js +67 -35
- package/dist/xstate.development.cjs.mjs +2 -0
- package/dist/xstate.development.esm.js +42 -13
- package/dist/xstate.esm.js +42 -13
- 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 -2
- package/guards/dist/xstate-guards.development.cjs.js +1 -2
- package/guards/dist/xstate-guards.development.esm.js +1 -2
- package/guards/dist/xstate-guards.esm.js +1 -2
- package/guards/dist/xstate-guards.umd.min.js +1 -1
- package/guards/dist/xstate-guards.umd.min.js.map +1 -1
- package/package.json +1 -1
- package/dist/interpreter-36d5556e.cjs.js +0 -887
- package/dist/interpreter-4e8e2a0d.development.cjs.js +0 -898
- package/dist/interpreter-63c80754.esm.js +0 -857
- package/dist/interpreter-80eb3bec.development.esm.js +0 -868
|
@@ -1,21 +1,874 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
3
|
+
var dev_dist_xstateDev = require('../dev/dist/xstate-dev.cjs.js');
|
|
4
|
+
|
|
5
|
+
class Mailbox {
|
|
6
|
+
constructor(_process) {
|
|
7
|
+
this._process = _process;
|
|
8
|
+
this._active = false;
|
|
9
|
+
this._current = null;
|
|
10
|
+
this._last = null;
|
|
11
|
+
}
|
|
12
|
+
start() {
|
|
13
|
+
this._active = true;
|
|
14
|
+
this.flush();
|
|
15
|
+
}
|
|
16
|
+
clear() {
|
|
17
|
+
// we can't set _current to null because we might be currently processing
|
|
18
|
+
// and enqueue following clear shouldnt start processing the enqueued item immediately
|
|
19
|
+
if (this._current) {
|
|
20
|
+
this._current.next = null;
|
|
21
|
+
this._last = this._current;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
enqueue(event) {
|
|
25
|
+
const enqueued = {
|
|
26
|
+
value: event,
|
|
27
|
+
next: null
|
|
28
|
+
};
|
|
29
|
+
if (this._current) {
|
|
30
|
+
this._last.next = enqueued;
|
|
31
|
+
this._last = enqueued;
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
this._current = enqueued;
|
|
35
|
+
this._last = enqueued;
|
|
36
|
+
if (this._active) {
|
|
37
|
+
this.flush();
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
flush() {
|
|
41
|
+
while (this._current) {
|
|
42
|
+
// atm the given _process is responsible for implementing proper try/catch handling
|
|
43
|
+
// we assume here that this won't throw in a way that can affect this mailbox
|
|
44
|
+
const consumed = this._current;
|
|
45
|
+
this._process(consumed.value);
|
|
46
|
+
this._current = consumed.next;
|
|
47
|
+
}
|
|
48
|
+
this._last = null;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const STATE_DELIMITER = '.';
|
|
53
|
+
const TARGETLESS_KEY = '';
|
|
54
|
+
const NULL_EVENT = '';
|
|
55
|
+
const STATE_IDENTIFIER = '#';
|
|
56
|
+
const WILDCARD = '*';
|
|
57
|
+
const XSTATE_INIT = 'xstate.init';
|
|
58
|
+
const XSTATE_ERROR = 'xstate.error';
|
|
59
|
+
const XSTATE_STOP = 'xstate.stop';
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Returns an event that represents an implicit event that
|
|
63
|
+
* is sent after the specified `delay`.
|
|
64
|
+
*
|
|
65
|
+
* @param delayRef The delay in milliseconds
|
|
66
|
+
* @param id The state node ID where this event is handled
|
|
67
|
+
*/
|
|
68
|
+
function createAfterEvent(delayRef, id) {
|
|
69
|
+
const idSuffix = id ? `#${id}` : '';
|
|
70
|
+
return {
|
|
71
|
+
type: `xstate.after(${delayRef})${idSuffix}`
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Returns an event that represents that a final state node
|
|
77
|
+
* has been reached in the parent state node.
|
|
78
|
+
*
|
|
79
|
+
* @param id The final state node's parent state node `id`
|
|
80
|
+
* @param output The data to pass into the event
|
|
81
|
+
*/
|
|
82
|
+
function createDoneStateEvent(id, output) {
|
|
83
|
+
return {
|
|
84
|
+
type: `xstate.done.state.${id}`,
|
|
85
|
+
output
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Returns an event that represents that an invoked service has terminated.
|
|
91
|
+
*
|
|
92
|
+
* An invoked service is terminated when it has reached a top-level final state node,
|
|
93
|
+
* but not when it is canceled.
|
|
94
|
+
*
|
|
95
|
+
* @param invokeId The invoked service ID
|
|
96
|
+
* @param output The data to pass into the event
|
|
97
|
+
*/
|
|
98
|
+
function createDoneActorEvent(invokeId, output) {
|
|
99
|
+
return {
|
|
100
|
+
type: `xstate.done.actor.${invokeId}`,
|
|
101
|
+
output
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
function createErrorActorEvent(id, data) {
|
|
105
|
+
return {
|
|
106
|
+
type: `xstate.error.actor.${id}`,
|
|
107
|
+
data
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
function createInitEvent(input) {
|
|
111
|
+
return {
|
|
112
|
+
type: XSTATE_INIT,
|
|
113
|
+
input
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* This function makes sure that unhandled errors are thrown in a separate macrotask.
|
|
119
|
+
* It allows those errors to be detected by global error handlers and reported to bug tracking services
|
|
120
|
+
* without interrupting our own stack of execution.
|
|
121
|
+
*
|
|
122
|
+
* @param err error to be thrown
|
|
123
|
+
*/
|
|
124
|
+
function reportUnhandledError(err) {
|
|
125
|
+
setTimeout(() => {
|
|
126
|
+
throw err;
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const symbolObservable = (() => typeof Symbol === 'function' && Symbol.observable || '@@observable')();
|
|
131
|
+
|
|
132
|
+
let idCounter = 0;
|
|
133
|
+
function createSystem(rootActor) {
|
|
134
|
+
const children = new Map();
|
|
135
|
+
const keyedActors = new Map();
|
|
136
|
+
const reverseKeyedActors = new WeakMap();
|
|
137
|
+
const observers = new Set();
|
|
138
|
+
const system = {
|
|
139
|
+
_bookId: () => `x:${idCounter++}`,
|
|
140
|
+
_register: (sessionId, actorRef) => {
|
|
141
|
+
children.set(sessionId, actorRef);
|
|
142
|
+
return sessionId;
|
|
143
|
+
},
|
|
144
|
+
_unregister: actorRef => {
|
|
145
|
+
children.delete(actorRef.sessionId);
|
|
146
|
+
const systemId = reverseKeyedActors.get(actorRef);
|
|
147
|
+
if (systemId !== undefined) {
|
|
148
|
+
keyedActors.delete(systemId);
|
|
149
|
+
reverseKeyedActors.delete(actorRef);
|
|
150
|
+
}
|
|
151
|
+
},
|
|
152
|
+
get: systemId => {
|
|
153
|
+
return keyedActors.get(systemId);
|
|
154
|
+
},
|
|
155
|
+
_set: (systemId, actorRef) => {
|
|
156
|
+
const existing = keyedActors.get(systemId);
|
|
157
|
+
if (existing && existing !== actorRef) {
|
|
158
|
+
throw new Error(`Actor with system ID '${systemId}' already exists.`);
|
|
159
|
+
}
|
|
160
|
+
keyedActors.set(systemId, actorRef);
|
|
161
|
+
reverseKeyedActors.set(actorRef, systemId);
|
|
162
|
+
},
|
|
163
|
+
inspect: observer => {
|
|
164
|
+
observers.add(observer);
|
|
165
|
+
},
|
|
166
|
+
_sendInspectionEvent: event => {
|
|
167
|
+
const resolvedInspectionEvent = {
|
|
168
|
+
...event,
|
|
169
|
+
rootId: rootActor.sessionId
|
|
170
|
+
};
|
|
171
|
+
observers.forEach(observer => observer.next?.(resolvedInspectionEvent));
|
|
172
|
+
},
|
|
173
|
+
_relay: (source, target, event) => {
|
|
174
|
+
system._sendInspectionEvent({
|
|
175
|
+
type: '@xstate.event',
|
|
176
|
+
sourceRef: source,
|
|
177
|
+
actorRef: target,
|
|
178
|
+
event
|
|
179
|
+
});
|
|
180
|
+
target._send(event);
|
|
181
|
+
}
|
|
182
|
+
};
|
|
183
|
+
return system;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
function matchesState(parentStateId, childStateId) {
|
|
187
|
+
const parentStateValue = toStateValue(parentStateId);
|
|
188
|
+
const childStateValue = toStateValue(childStateId);
|
|
189
|
+
if (typeof childStateValue === 'string') {
|
|
190
|
+
if (typeof parentStateValue === 'string') {
|
|
191
|
+
return childStateValue === parentStateValue;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// Parent more specific than child
|
|
195
|
+
return false;
|
|
196
|
+
}
|
|
197
|
+
if (typeof parentStateValue === 'string') {
|
|
198
|
+
return parentStateValue in childStateValue;
|
|
199
|
+
}
|
|
200
|
+
return Object.keys(parentStateValue).every(key => {
|
|
201
|
+
if (!(key in childStateValue)) {
|
|
202
|
+
return false;
|
|
203
|
+
}
|
|
204
|
+
return matchesState(parentStateValue[key], childStateValue[key]);
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
function toStatePath(stateId) {
|
|
208
|
+
try {
|
|
209
|
+
if (isArray(stateId)) {
|
|
210
|
+
return stateId;
|
|
211
|
+
}
|
|
212
|
+
return stateId.split(STATE_DELIMITER);
|
|
213
|
+
} catch (e) {
|
|
214
|
+
throw new Error(`'${stateId}' is not a valid state path.`);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
function toStateValue(stateValue) {
|
|
218
|
+
if (isMachineSnapshot(stateValue)) {
|
|
219
|
+
return stateValue.value;
|
|
220
|
+
}
|
|
221
|
+
if (typeof stateValue !== 'string') {
|
|
222
|
+
return stateValue;
|
|
223
|
+
}
|
|
224
|
+
const statePath = toStatePath(stateValue);
|
|
225
|
+
return pathToStateValue(statePath);
|
|
226
|
+
}
|
|
227
|
+
function pathToStateValue(statePath) {
|
|
228
|
+
if (statePath.length === 1) {
|
|
229
|
+
return statePath[0];
|
|
230
|
+
}
|
|
231
|
+
const value = {};
|
|
232
|
+
let marker = value;
|
|
233
|
+
for (let i = 0; i < statePath.length - 1; i++) {
|
|
234
|
+
if (i === statePath.length - 2) {
|
|
235
|
+
marker[statePath[i]] = statePath[i + 1];
|
|
236
|
+
} else {
|
|
237
|
+
const previous = marker;
|
|
238
|
+
marker = {};
|
|
239
|
+
previous[statePath[i]] = marker;
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
return value;
|
|
243
|
+
}
|
|
244
|
+
function mapValues(collection, iteratee) {
|
|
245
|
+
const result = {};
|
|
246
|
+
const collectionKeys = Object.keys(collection);
|
|
247
|
+
for (let i = 0; i < collectionKeys.length; i++) {
|
|
248
|
+
const key = collectionKeys[i];
|
|
249
|
+
result[key] = iteratee(collection[key], key, collection, i);
|
|
250
|
+
}
|
|
251
|
+
return result;
|
|
252
|
+
}
|
|
253
|
+
function flatten(array) {
|
|
254
|
+
return [].concat(...array);
|
|
255
|
+
}
|
|
256
|
+
function toArrayStrict(value) {
|
|
257
|
+
if (isArray(value)) {
|
|
258
|
+
return value;
|
|
259
|
+
}
|
|
260
|
+
return [value];
|
|
261
|
+
}
|
|
262
|
+
function toArray(value) {
|
|
263
|
+
if (value === undefined) {
|
|
264
|
+
return [];
|
|
265
|
+
}
|
|
266
|
+
return toArrayStrict(value);
|
|
267
|
+
}
|
|
268
|
+
function resolveOutput(mapper, context, event, self) {
|
|
269
|
+
if (typeof mapper === 'function') {
|
|
270
|
+
return mapper({
|
|
271
|
+
context,
|
|
272
|
+
event,
|
|
273
|
+
self
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
return mapper;
|
|
277
|
+
}
|
|
278
|
+
function isArray(value) {
|
|
279
|
+
return Array.isArray(value);
|
|
280
|
+
}
|
|
281
|
+
function isErrorActorEvent(event) {
|
|
282
|
+
return event.type.startsWith('xstate.error.actor');
|
|
283
|
+
}
|
|
284
|
+
function toTransitionConfigArray(configLike) {
|
|
285
|
+
return toArrayStrict(configLike).map(transitionLike => {
|
|
286
|
+
if (typeof transitionLike === 'undefined' || typeof transitionLike === 'string') {
|
|
287
|
+
return {
|
|
288
|
+
target: transitionLike
|
|
289
|
+
};
|
|
290
|
+
}
|
|
291
|
+
return transitionLike;
|
|
292
|
+
});
|
|
293
|
+
}
|
|
294
|
+
function normalizeTarget(target) {
|
|
295
|
+
if (target === undefined || target === TARGETLESS_KEY) {
|
|
296
|
+
return undefined;
|
|
297
|
+
}
|
|
298
|
+
return toArray(target);
|
|
299
|
+
}
|
|
300
|
+
function toObserver(nextHandler, errorHandler, completionHandler) {
|
|
301
|
+
const isObserver = typeof nextHandler === 'object';
|
|
302
|
+
const self = isObserver ? nextHandler : undefined;
|
|
303
|
+
return {
|
|
304
|
+
next: (isObserver ? nextHandler.next : nextHandler)?.bind(self),
|
|
305
|
+
error: (isObserver ? nextHandler.error : errorHandler)?.bind(self),
|
|
306
|
+
complete: (isObserver ? nextHandler.complete : completionHandler)?.bind(self)
|
|
307
|
+
};
|
|
308
|
+
}
|
|
309
|
+
function createInvokeId(stateNodeId, index) {
|
|
310
|
+
return `${stateNodeId}[${index}]`;
|
|
311
|
+
}
|
|
312
|
+
function resolveReferencedActor(machine, src) {
|
|
313
|
+
if (src.startsWith('xstate#')) {
|
|
314
|
+
const [, indexStr] = src.match(/\[(\d+)\]$/);
|
|
315
|
+
const node = machine.getStateNodeById(src.slice(7, -(indexStr.length + 2)));
|
|
316
|
+
const invokeConfig = node.config.invoke;
|
|
317
|
+
return (Array.isArray(invokeConfig) ? invokeConfig[indexStr] : invokeConfig).src;
|
|
318
|
+
}
|
|
319
|
+
return machine.implementations.actors[src];
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
const $$ACTOR_TYPE = 1;
|
|
323
|
+
// those values are currently used by @xstate/react directly so it's important to keep the assigned values in sync
|
|
324
|
+
let ProcessingStatus = /*#__PURE__*/function (ProcessingStatus) {
|
|
325
|
+
ProcessingStatus[ProcessingStatus["NotStarted"] = 0] = "NotStarted";
|
|
326
|
+
ProcessingStatus[ProcessingStatus["Running"] = 1] = "Running";
|
|
327
|
+
ProcessingStatus[ProcessingStatus["Stopped"] = 2] = "Stopped";
|
|
328
|
+
return ProcessingStatus;
|
|
329
|
+
}({});
|
|
330
|
+
const defaultOptions = {
|
|
331
|
+
clock: {
|
|
332
|
+
setTimeout: (fn, ms) => {
|
|
333
|
+
return setTimeout(fn, ms);
|
|
334
|
+
},
|
|
335
|
+
clearTimeout: id => {
|
|
336
|
+
return clearTimeout(id);
|
|
337
|
+
}
|
|
338
|
+
},
|
|
339
|
+
logger: console.log.bind(console),
|
|
340
|
+
devTools: false
|
|
341
|
+
};
|
|
342
|
+
|
|
343
|
+
/**
|
|
344
|
+
* An Actor is a running process that can receive events, send events and change its behavior based on the events it receives, which can cause effects outside of the actor. When you run a state machine, it becomes an actor.
|
|
345
|
+
*/
|
|
346
|
+
class Actor {
|
|
347
|
+
/**
|
|
348
|
+
* The current internal state of the actor.
|
|
349
|
+
*/
|
|
350
|
+
|
|
351
|
+
/**
|
|
352
|
+
* The clock that is responsible for setting and clearing timeouts, such as delayed events and transitions.
|
|
353
|
+
*/
|
|
354
|
+
|
|
355
|
+
/**
|
|
356
|
+
* The unique identifier for this actor relative to its parent.
|
|
357
|
+
*/
|
|
358
|
+
|
|
359
|
+
/** @internal */
|
|
360
|
+
|
|
361
|
+
// Actor Ref
|
|
362
|
+
|
|
363
|
+
// TODO: add typings for system
|
|
364
|
+
|
|
365
|
+
/**
|
|
366
|
+
* The globally unique process ID for this invocation.
|
|
367
|
+
*/
|
|
368
|
+
|
|
369
|
+
/**
|
|
370
|
+
* The system to which this actor belongs.
|
|
371
|
+
*/
|
|
372
|
+
|
|
373
|
+
/**
|
|
374
|
+
* Creates a new actor instance for the given logic with the provided options, if any.
|
|
375
|
+
*
|
|
376
|
+
* @param logic The logic to create an actor from
|
|
377
|
+
* @param options Actor options
|
|
378
|
+
*/
|
|
379
|
+
constructor(logic, options) {
|
|
380
|
+
this.logic = logic;
|
|
381
|
+
this._state = void 0;
|
|
382
|
+
this.clock = void 0;
|
|
383
|
+
this.options = void 0;
|
|
384
|
+
this.id = void 0;
|
|
385
|
+
this.mailbox = new Mailbox(this._process.bind(this));
|
|
386
|
+
this.delayedEventsMap = {};
|
|
387
|
+
this.observers = new Set();
|
|
388
|
+
this.logger = void 0;
|
|
389
|
+
this._processingStatus = ProcessingStatus.NotStarted;
|
|
390
|
+
this._parent = void 0;
|
|
391
|
+
this._syncSnapshot = void 0;
|
|
392
|
+
this.ref = void 0;
|
|
393
|
+
this._actorScope = void 0;
|
|
394
|
+
this._systemId = void 0;
|
|
395
|
+
this.sessionId = void 0;
|
|
396
|
+
this.system = void 0;
|
|
397
|
+
this._doneEvent = void 0;
|
|
398
|
+
this.src = void 0;
|
|
399
|
+
this._deferred = [];
|
|
400
|
+
const resolvedOptions = {
|
|
401
|
+
...defaultOptions,
|
|
402
|
+
...options
|
|
403
|
+
};
|
|
404
|
+
const {
|
|
405
|
+
clock,
|
|
406
|
+
logger,
|
|
407
|
+
parent,
|
|
408
|
+
syncSnapshot,
|
|
409
|
+
id,
|
|
410
|
+
systemId,
|
|
411
|
+
inspect
|
|
412
|
+
} = resolvedOptions;
|
|
413
|
+
this.system = parent?.system ?? createSystem(this);
|
|
414
|
+
if (inspect && !parent) {
|
|
415
|
+
// Always inspect at the system-level
|
|
416
|
+
this.system.inspect(toObserver(inspect));
|
|
417
|
+
}
|
|
418
|
+
this.sessionId = this.system._bookId();
|
|
419
|
+
this.id = id ?? this.sessionId;
|
|
420
|
+
this.logger = logger;
|
|
421
|
+
this.clock = clock;
|
|
422
|
+
this._parent = parent;
|
|
423
|
+
this._syncSnapshot = syncSnapshot;
|
|
424
|
+
this.options = resolvedOptions;
|
|
425
|
+
this.src = resolvedOptions.src ?? logic;
|
|
426
|
+
this.ref = this;
|
|
427
|
+
this._actorScope = {
|
|
428
|
+
self: this,
|
|
429
|
+
id: this.id,
|
|
430
|
+
sessionId: this.sessionId,
|
|
431
|
+
logger: this.logger,
|
|
432
|
+
defer: fn => {
|
|
433
|
+
this._deferred.push(fn);
|
|
434
|
+
},
|
|
435
|
+
system: this.system,
|
|
436
|
+
stopChild: child => {
|
|
437
|
+
if (child._parent !== this) {
|
|
438
|
+
throw new Error(`Cannot stop child actor ${child.id} of ${this.id} because it is not a child`);
|
|
439
|
+
}
|
|
440
|
+
child._stop();
|
|
441
|
+
}
|
|
442
|
+
};
|
|
443
|
+
|
|
444
|
+
// Ensure that the send method is bound to this Actor instance
|
|
445
|
+
// if destructured
|
|
446
|
+
this.send = this.send.bind(this);
|
|
447
|
+
this.system._sendInspectionEvent({
|
|
448
|
+
type: '@xstate.actor',
|
|
449
|
+
actorRef: this
|
|
450
|
+
});
|
|
451
|
+
if (systemId) {
|
|
452
|
+
this._systemId = systemId;
|
|
453
|
+
this.system._set(systemId, this);
|
|
454
|
+
}
|
|
455
|
+
this._initState(options?.state);
|
|
456
|
+
if (systemId && this._state.status !== 'active') {
|
|
457
|
+
this.system._unregister(this);
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
_initState(persistedState) {
|
|
461
|
+
this._state = persistedState ? this.logic.restoreState ? this.logic.restoreState(persistedState, this._actorScope) : persistedState : this.logic.getInitialState(this._actorScope, this.options?.input);
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
// array of functions to defer
|
|
465
|
+
|
|
466
|
+
update(snapshot, event) {
|
|
467
|
+
// Update state
|
|
468
|
+
this._state = snapshot;
|
|
469
|
+
|
|
470
|
+
// Execute deferred effects
|
|
471
|
+
let deferredFn;
|
|
472
|
+
while (deferredFn = this._deferred.shift()) {
|
|
473
|
+
deferredFn();
|
|
474
|
+
}
|
|
475
|
+
for (const observer of this.observers) {
|
|
476
|
+
try {
|
|
477
|
+
observer.next?.(snapshot);
|
|
478
|
+
} catch (err) {
|
|
479
|
+
reportUnhandledError(err);
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
switch (this._state.status) {
|
|
483
|
+
case 'done':
|
|
484
|
+
this._stopProcedure();
|
|
485
|
+
this._complete();
|
|
486
|
+
this._doneEvent = createDoneActorEvent(this.id, this._state.output);
|
|
487
|
+
if (this._parent) {
|
|
488
|
+
this.system._relay(this, this._parent, this._doneEvent);
|
|
489
|
+
}
|
|
490
|
+
break;
|
|
491
|
+
case 'error':
|
|
492
|
+
this._stopProcedure();
|
|
493
|
+
this._error(this._state.error);
|
|
494
|
+
if (this._parent) {
|
|
495
|
+
this.system._relay(this, this._parent, createErrorActorEvent(this.id, this._state.error));
|
|
496
|
+
}
|
|
497
|
+
break;
|
|
498
|
+
}
|
|
499
|
+
this.system._sendInspectionEvent({
|
|
500
|
+
type: '@xstate.snapshot',
|
|
501
|
+
actorRef: this,
|
|
502
|
+
event,
|
|
503
|
+
snapshot
|
|
504
|
+
});
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
/**
|
|
508
|
+
* Subscribe an observer to an actor’s snapshot values.
|
|
509
|
+
*
|
|
510
|
+
* @remarks
|
|
511
|
+
* The observer will receive the actor’s snapshot value when it is emitted. The observer can be:
|
|
512
|
+
* - A plain function that receives the latest snapshot, or
|
|
513
|
+
* - An observer object whose `.next(snapshot)` method receives the latest snapshot
|
|
514
|
+
*
|
|
515
|
+
* @example
|
|
516
|
+
* ```ts
|
|
517
|
+
* // Observer as a plain function
|
|
518
|
+
* const subscription = actor.subscribe((snapshot) => {
|
|
519
|
+
* console.log(snapshot);
|
|
520
|
+
* });
|
|
521
|
+
* ```
|
|
522
|
+
*
|
|
523
|
+
* @example
|
|
524
|
+
* ```ts
|
|
525
|
+
* // Observer as an object
|
|
526
|
+
* const subscription = actor.subscribe({
|
|
527
|
+
* next(snapshot) {
|
|
528
|
+
* console.log(snapshot);
|
|
529
|
+
* },
|
|
530
|
+
* error(err) {
|
|
531
|
+
* // ...
|
|
532
|
+
* },
|
|
533
|
+
* complete() {
|
|
534
|
+
* // ...
|
|
535
|
+
* },
|
|
536
|
+
* });
|
|
537
|
+
* ```
|
|
538
|
+
*
|
|
539
|
+
* The return value of `actor.subscribe(observer)` is a subscription object that has an `.unsubscribe()` method. You can call `subscription.unsubscribe()` to unsubscribe the observer:
|
|
540
|
+
*
|
|
541
|
+
* @example
|
|
542
|
+
* ```ts
|
|
543
|
+
* const subscription = actor.subscribe((snapshot) => {
|
|
544
|
+
* // ...
|
|
545
|
+
* });
|
|
546
|
+
*
|
|
547
|
+
* // Unsubscribe the observer
|
|
548
|
+
* subscription.unsubscribe();
|
|
549
|
+
* ```
|
|
550
|
+
*
|
|
551
|
+
* When the actor is stopped, all of its observers will automatically be unsubscribed.
|
|
552
|
+
*
|
|
553
|
+
* @param observer - Either a plain function that receives the latest snapshot, or an observer object whose `.next(snapshot)` method receives the latest snapshot
|
|
554
|
+
*/
|
|
555
|
+
|
|
556
|
+
subscribe(nextListenerOrObserver, errorListener, completeListener) {
|
|
557
|
+
const observer = toObserver(nextListenerOrObserver, errorListener, completeListener);
|
|
558
|
+
if (this._processingStatus !== ProcessingStatus.Stopped) {
|
|
559
|
+
this.observers.add(observer);
|
|
560
|
+
} else {
|
|
561
|
+
try {
|
|
562
|
+
observer.complete?.();
|
|
563
|
+
} catch (err) {
|
|
564
|
+
reportUnhandledError(err);
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
return {
|
|
568
|
+
unsubscribe: () => {
|
|
569
|
+
this.observers.delete(observer);
|
|
570
|
+
}
|
|
11
571
|
};
|
|
12
|
-
cache.set(object, memoizedData);
|
|
13
|
-
} else if (!(key in memoizedData)) {
|
|
14
|
-
memoizedData[key] = fn();
|
|
15
572
|
}
|
|
16
|
-
|
|
573
|
+
|
|
574
|
+
/**
|
|
575
|
+
* Starts the Actor from the initial state
|
|
576
|
+
*/
|
|
577
|
+
start() {
|
|
578
|
+
if (this._processingStatus === ProcessingStatus.Running) {
|
|
579
|
+
// Do not restart the service if it is already started
|
|
580
|
+
return this;
|
|
581
|
+
}
|
|
582
|
+
if (this._syncSnapshot) {
|
|
583
|
+
this.subscribe({
|
|
584
|
+
next: snapshot => {
|
|
585
|
+
if (snapshot.status === 'active') {
|
|
586
|
+
this._parent.send({
|
|
587
|
+
type: `xstate.snapshot.${this.id}`,
|
|
588
|
+
snapshot
|
|
589
|
+
});
|
|
590
|
+
}
|
|
591
|
+
},
|
|
592
|
+
error: () => {}
|
|
593
|
+
});
|
|
594
|
+
}
|
|
595
|
+
this.system._register(this.sessionId, this);
|
|
596
|
+
if (this._systemId) {
|
|
597
|
+
this.system._set(this._systemId, this);
|
|
598
|
+
}
|
|
599
|
+
this._processingStatus = ProcessingStatus.Running;
|
|
600
|
+
|
|
601
|
+
// TODO: this isn't correct when rehydrating
|
|
602
|
+
const initEvent = createInitEvent(this.options.input);
|
|
603
|
+
this.system._sendInspectionEvent({
|
|
604
|
+
type: '@xstate.event',
|
|
605
|
+
sourceRef: this._parent,
|
|
606
|
+
actorRef: this,
|
|
607
|
+
event: initEvent
|
|
608
|
+
});
|
|
609
|
+
const status = this._state.status;
|
|
610
|
+
switch (status) {
|
|
611
|
+
case 'done':
|
|
612
|
+
// a state machine can be "done" upon initialization (it could reach a final state using initial microsteps)
|
|
613
|
+
// we still need to complete observers, flush deferreds etc
|
|
614
|
+
this.update(this._state, initEvent);
|
|
615
|
+
// fallthrough
|
|
616
|
+
case 'error':
|
|
617
|
+
// TODO: rethink cleanup of observers, mailbox, etc
|
|
618
|
+
return this;
|
|
619
|
+
}
|
|
620
|
+
if (this.logic.start) {
|
|
621
|
+
try {
|
|
622
|
+
this.logic.start(this._state, this._actorScope);
|
|
623
|
+
} catch (err) {
|
|
624
|
+
this._stopProcedure();
|
|
625
|
+
this._error(err);
|
|
626
|
+
this._parent?.send(createErrorActorEvent(this.id, err));
|
|
627
|
+
return this;
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
// TODO: this notifies all subscribers but usually this is redundant
|
|
632
|
+
// there is no real change happening here
|
|
633
|
+
// we need to rethink if this needs to be refactored
|
|
634
|
+
this.update(this._state, initEvent);
|
|
635
|
+
if (this.options.devTools) {
|
|
636
|
+
this.attachDevTools();
|
|
637
|
+
}
|
|
638
|
+
this.mailbox.start();
|
|
639
|
+
return this;
|
|
640
|
+
}
|
|
641
|
+
_process(event) {
|
|
642
|
+
// TODO: reexamine what happens when an action (or a guard or smth) throws
|
|
643
|
+
let nextState;
|
|
644
|
+
let caughtError;
|
|
645
|
+
try {
|
|
646
|
+
nextState = this.logic.transition(this._state, event, this._actorScope);
|
|
647
|
+
} catch (err) {
|
|
648
|
+
// we wrap it in a box so we can rethrow it later even if falsy value gets caught here
|
|
649
|
+
caughtError = {
|
|
650
|
+
err
|
|
651
|
+
};
|
|
652
|
+
}
|
|
653
|
+
if (caughtError) {
|
|
654
|
+
const {
|
|
655
|
+
err
|
|
656
|
+
} = caughtError;
|
|
657
|
+
this._stopProcedure();
|
|
658
|
+
this._error(err);
|
|
659
|
+
this._parent?.send(createErrorActorEvent(this.id, err));
|
|
660
|
+
return;
|
|
661
|
+
}
|
|
662
|
+
this.update(nextState, event);
|
|
663
|
+
if (event.type === XSTATE_STOP) {
|
|
664
|
+
this._stopProcedure();
|
|
665
|
+
this._complete();
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
_stop() {
|
|
669
|
+
if (this._processingStatus === ProcessingStatus.Stopped) {
|
|
670
|
+
return this;
|
|
671
|
+
}
|
|
672
|
+
this.mailbox.clear();
|
|
673
|
+
if (this._processingStatus === ProcessingStatus.NotStarted) {
|
|
674
|
+
this._processingStatus = ProcessingStatus.Stopped;
|
|
675
|
+
return this;
|
|
676
|
+
}
|
|
677
|
+
this.mailbox.enqueue({
|
|
678
|
+
type: XSTATE_STOP
|
|
679
|
+
});
|
|
680
|
+
return this;
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
/**
|
|
684
|
+
* Stops the Actor and unsubscribe all listeners.
|
|
685
|
+
*/
|
|
686
|
+
stop() {
|
|
687
|
+
if (this._parent) {
|
|
688
|
+
throw new Error('A non-root actor cannot be stopped directly.');
|
|
689
|
+
}
|
|
690
|
+
return this._stop();
|
|
691
|
+
}
|
|
692
|
+
_complete() {
|
|
693
|
+
for (const observer of this.observers) {
|
|
694
|
+
try {
|
|
695
|
+
observer.complete?.();
|
|
696
|
+
} catch (err) {
|
|
697
|
+
reportUnhandledError(err);
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
this.observers.clear();
|
|
701
|
+
}
|
|
702
|
+
_error(err) {
|
|
703
|
+
if (!this.observers.size) {
|
|
704
|
+
if (!this._parent) {
|
|
705
|
+
reportUnhandledError(err);
|
|
706
|
+
}
|
|
707
|
+
return;
|
|
708
|
+
}
|
|
709
|
+
let reportError = false;
|
|
710
|
+
for (const observer of this.observers) {
|
|
711
|
+
const errorListener = observer.error;
|
|
712
|
+
reportError ||= !errorListener;
|
|
713
|
+
try {
|
|
714
|
+
errorListener?.(err);
|
|
715
|
+
} catch (err2) {
|
|
716
|
+
reportUnhandledError(err2);
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
this.observers.clear();
|
|
720
|
+
if (reportError) {
|
|
721
|
+
reportUnhandledError(err);
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
_stopProcedure() {
|
|
725
|
+
if (this._processingStatus !== ProcessingStatus.Running) {
|
|
726
|
+
// Actor already stopped; do nothing
|
|
727
|
+
return this;
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
// Cancel all delayed events
|
|
731
|
+
for (const key of Object.keys(this.delayedEventsMap)) {
|
|
732
|
+
this.clock.clearTimeout(this.delayedEventsMap[key]);
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
// TODO: mailbox.reset
|
|
736
|
+
this.mailbox.clear();
|
|
737
|
+
// TODO: after `stop` we must prepare ourselves for receiving events again
|
|
738
|
+
// events sent *after* stop signal must be queued
|
|
739
|
+
// it seems like this should be the common behavior for all of our consumers
|
|
740
|
+
// so perhaps this should be unified somehow for all of them
|
|
741
|
+
this.mailbox = new Mailbox(this._process.bind(this));
|
|
742
|
+
this._processingStatus = ProcessingStatus.Stopped;
|
|
743
|
+
this.system._unregister(this);
|
|
744
|
+
return this;
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
/**
|
|
748
|
+
* @internal
|
|
749
|
+
*/
|
|
750
|
+
_send(event) {
|
|
751
|
+
if (this._processingStatus === ProcessingStatus.Stopped) {
|
|
752
|
+
return;
|
|
753
|
+
}
|
|
754
|
+
this.mailbox.enqueue(event);
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
/**
|
|
758
|
+
* Sends an event to the running Actor to trigger a transition.
|
|
759
|
+
*
|
|
760
|
+
* @param event The event to send
|
|
761
|
+
*/
|
|
762
|
+
send(event) {
|
|
763
|
+
this.system._relay(undefined, this, event);
|
|
764
|
+
}
|
|
765
|
+
|
|
766
|
+
/**
|
|
767
|
+
* TODO: figure out a way to do this within the machine
|
|
768
|
+
* @internal
|
|
769
|
+
*/
|
|
770
|
+
delaySend(params) {
|
|
771
|
+
const {
|
|
772
|
+
event,
|
|
773
|
+
id,
|
|
774
|
+
delay
|
|
775
|
+
} = params;
|
|
776
|
+
const timerId = this.clock.setTimeout(() => {
|
|
777
|
+
this.system._relay(this, params.to ?? this, event);
|
|
778
|
+
}, delay);
|
|
779
|
+
|
|
780
|
+
// TODO: consider the rehydration story here
|
|
781
|
+
if (id) {
|
|
782
|
+
this.delayedEventsMap[id] = timerId;
|
|
783
|
+
}
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
/**
|
|
787
|
+
* TODO: figure out a way to do this within the machine
|
|
788
|
+
* @internal
|
|
789
|
+
*/
|
|
790
|
+
cancel(sendId) {
|
|
791
|
+
this.clock.clearTimeout(this.delayedEventsMap[sendId]);
|
|
792
|
+
delete this.delayedEventsMap[sendId];
|
|
793
|
+
}
|
|
794
|
+
attachDevTools() {
|
|
795
|
+
const {
|
|
796
|
+
devTools
|
|
797
|
+
} = this.options;
|
|
798
|
+
if (devTools) {
|
|
799
|
+
const resolvedDevToolsAdapter = typeof devTools === 'function' ? devTools : dev_dist_xstateDev.devToolsAdapter;
|
|
800
|
+
resolvedDevToolsAdapter(this);
|
|
801
|
+
}
|
|
802
|
+
}
|
|
803
|
+
toJSON() {
|
|
804
|
+
return {
|
|
805
|
+
xstate$$type: $$ACTOR_TYPE,
|
|
806
|
+
id: this.id
|
|
807
|
+
};
|
|
808
|
+
}
|
|
809
|
+
|
|
810
|
+
/**
|
|
811
|
+
* Obtain the internal state of the actor, which can be persisted.
|
|
812
|
+
*
|
|
813
|
+
* @remarks
|
|
814
|
+
* The internal state can be persisted from any actor, not only machines.
|
|
815
|
+
*
|
|
816
|
+
* Note that the persisted state is not the same as the snapshot from {@link Actor.getSnapshot}. Persisted state represents the internal state of the actor, while snapshots represent the actor's last emitted value.
|
|
817
|
+
*
|
|
818
|
+
* Can be restored with {@link ActorOptions.state}
|
|
819
|
+
*
|
|
820
|
+
* @see https://stately.ai/docs/persistence
|
|
821
|
+
*/
|
|
822
|
+
|
|
823
|
+
getPersistedState(options) {
|
|
824
|
+
return this.logic.getPersistedState(this._state, options);
|
|
825
|
+
}
|
|
826
|
+
[symbolObservable]() {
|
|
827
|
+
return this;
|
|
828
|
+
}
|
|
829
|
+
|
|
830
|
+
/**
|
|
831
|
+
* Read an actor’s snapshot synchronously.
|
|
832
|
+
*
|
|
833
|
+
* @remarks
|
|
834
|
+
* The snapshot represent an actor's last emitted value.
|
|
835
|
+
*
|
|
836
|
+
* When an actor receives an event, its internal state may change.
|
|
837
|
+
* An actor may emit a snapshot when a state transition occurs.
|
|
838
|
+
*
|
|
839
|
+
* Note that some actors, such as callback actors generated with `fromCallback`, will not emit snapshots.
|
|
840
|
+
*
|
|
841
|
+
* @see {@link Actor.subscribe} to subscribe to an actor’s snapshot values.
|
|
842
|
+
* @see {@link Actor.getPersistedState} to persist the internal state of an actor (which is more than just a snapshot).
|
|
843
|
+
*/
|
|
844
|
+
getSnapshot() {
|
|
845
|
+
return this._state;
|
|
846
|
+
}
|
|
17
847
|
}
|
|
18
848
|
|
|
849
|
+
/**
|
|
850
|
+
* Creates a new `ActorRef` instance for the given machine with the provided options, if any.
|
|
851
|
+
*
|
|
852
|
+
* @param machine The machine to create an actor from
|
|
853
|
+
* @param options `ActorRef` options
|
|
854
|
+
*/
|
|
855
|
+
|
|
856
|
+
function createActor(logic, options) {
|
|
857
|
+
const interpreter = new Actor(logic, options);
|
|
858
|
+
return interpreter;
|
|
859
|
+
}
|
|
860
|
+
|
|
861
|
+
/**
|
|
862
|
+
* Creates a new Interpreter instance for the given machine with the provided options, if any.
|
|
863
|
+
*
|
|
864
|
+
* @deprecated Use `createActor` instead
|
|
865
|
+
*/
|
|
866
|
+
const interpret = createActor;
|
|
867
|
+
|
|
868
|
+
/**
|
|
869
|
+
* @deprecated Use `Actor` instead.
|
|
870
|
+
*/
|
|
871
|
+
|
|
19
872
|
function resolveCancel(_, state, actionArgs, actionParams, {
|
|
20
873
|
sendId
|
|
21
874
|
}) {
|
|
@@ -49,14 +902,15 @@ function resolveSpawn(actorScope, state, actionArgs, _actionParams, {
|
|
|
49
902
|
input,
|
|
50
903
|
syncSnapshot
|
|
51
904
|
}) {
|
|
52
|
-
const logic = typeof src === 'string' ?
|
|
905
|
+
const logic = typeof src === 'string' ? resolveReferencedActor(state.machine, src) : src;
|
|
53
906
|
const resolvedId = typeof id === 'function' ? id(actionArgs) : id;
|
|
54
907
|
let actorRef;
|
|
55
908
|
if (logic) {
|
|
56
|
-
actorRef =
|
|
909
|
+
actorRef = createActor(logic, {
|
|
57
910
|
id: resolvedId,
|
|
58
911
|
src,
|
|
59
912
|
parent: actorScope?.self,
|
|
913
|
+
syncSnapshot,
|
|
60
914
|
systemId,
|
|
61
915
|
input: typeof input === 'function' ? input({
|
|
62
916
|
context: state.context,
|
|
@@ -64,19 +918,6 @@ function resolveSpawn(actorScope, state, actionArgs, _actionParams, {
|
|
|
64
918
|
self: actorScope?.self
|
|
65
919
|
}) : input
|
|
66
920
|
});
|
|
67
|
-
if (syncSnapshot) {
|
|
68
|
-
actorRef.subscribe({
|
|
69
|
-
next: snapshot => {
|
|
70
|
-
if (snapshot.status === 'active') {
|
|
71
|
-
actorScope.self.send({
|
|
72
|
-
type: `xstate.snapshot.${id}`,
|
|
73
|
-
snapshot
|
|
74
|
-
});
|
|
75
|
-
}
|
|
76
|
-
},
|
|
77
|
-
error: () => {}
|
|
78
|
-
});
|
|
79
|
-
}
|
|
80
921
|
}
|
|
81
922
|
return [cloneMachineSnapshot(state, {
|
|
82
923
|
children: {
|
|
@@ -96,13 +937,13 @@ function executeSpawn(actorScope, {
|
|
|
96
937
|
return;
|
|
97
938
|
}
|
|
98
939
|
actorScope.defer(() => {
|
|
99
|
-
if (actorRef._processingStatus ===
|
|
940
|
+
if (actorRef._processingStatus === ProcessingStatus.Stopped) {
|
|
100
941
|
return;
|
|
101
942
|
}
|
|
102
943
|
try {
|
|
103
944
|
actorRef.start?.();
|
|
104
945
|
} catch (err) {
|
|
105
|
-
actorScope.self.send(
|
|
946
|
+
actorScope.self.send(createErrorActorEvent(id, err));
|
|
106
947
|
return;
|
|
107
948
|
}
|
|
108
949
|
});
|
|
@@ -154,7 +995,7 @@ function executeStop(actorScope, actorRef) {
|
|
|
154
995
|
|
|
155
996
|
// this allows us to prevent an actor from being started if it gets stopped within the same macrostep
|
|
156
997
|
// this can happen, for example, when the invoking state is being exited immediately by an always transition
|
|
157
|
-
if (actorRef._processingStatus !==
|
|
998
|
+
if (actorRef._processingStatus !== ProcessingStatus.Running) {
|
|
158
999
|
actorScope.stopChild(actorRef);
|
|
159
1000
|
return;
|
|
160
1001
|
}
|
|
@@ -186,7 +1027,7 @@ function checkStateIn(state, _, {
|
|
|
186
1027
|
}) {
|
|
187
1028
|
if (typeof stateValue === 'string' && isStateId(stateValue)) {
|
|
188
1029
|
const target = state.machine.getStateNodeById(stateValue);
|
|
189
|
-
return state.
|
|
1030
|
+
return state._nodes.some(sn => sn === target);
|
|
190
1031
|
}
|
|
191
1032
|
return state.matches(stateValue);
|
|
192
1033
|
}
|
|
@@ -297,26 +1138,25 @@ function getProperAncestors(stateNode, toStateNode) {
|
|
|
297
1138
|
}
|
|
298
1139
|
return ancestors;
|
|
299
1140
|
}
|
|
300
|
-
function
|
|
301
|
-
const
|
|
302
|
-
const
|
|
303
|
-
const adjList = getAdjList(configurationSet);
|
|
1141
|
+
function getAllStateNodes(stateNodes) {
|
|
1142
|
+
const nodeSet = new Set(stateNodes);
|
|
1143
|
+
const adjList = getAdjList(nodeSet);
|
|
304
1144
|
|
|
305
1145
|
// add descendants
|
|
306
|
-
for (const s of
|
|
1146
|
+
for (const s of nodeSet) {
|
|
307
1147
|
// if previously active, add existing child nodes
|
|
308
1148
|
if (s.type === 'compound' && (!adjList.get(s) || !adjList.get(s).length)) {
|
|
309
|
-
getInitialStateNodesWithTheirAncestors(s).forEach(sn =>
|
|
1149
|
+
getInitialStateNodesWithTheirAncestors(s).forEach(sn => nodeSet.add(sn));
|
|
310
1150
|
} else {
|
|
311
1151
|
if (s.type === 'parallel') {
|
|
312
1152
|
for (const child of getChildren(s)) {
|
|
313
1153
|
if (child.type === 'history') {
|
|
314
1154
|
continue;
|
|
315
1155
|
}
|
|
316
|
-
if (!
|
|
1156
|
+
if (!nodeSet.has(child)) {
|
|
317
1157
|
const initialStates = getInitialStateNodesWithTheirAncestors(child);
|
|
318
1158
|
for (const initialStateNode of initialStates) {
|
|
319
|
-
|
|
1159
|
+
nodeSet.add(initialStateNode);
|
|
320
1160
|
}
|
|
321
1161
|
}
|
|
322
1162
|
}
|
|
@@ -325,14 +1165,14 @@ function getConfiguration(stateNodes) {
|
|
|
325
1165
|
}
|
|
326
1166
|
|
|
327
1167
|
// add all ancestors
|
|
328
|
-
for (const s of
|
|
1168
|
+
for (const s of nodeSet) {
|
|
329
1169
|
let m = s.parent;
|
|
330
1170
|
while (m) {
|
|
331
|
-
|
|
1171
|
+
nodeSet.add(m);
|
|
332
1172
|
m = m.parent;
|
|
333
1173
|
}
|
|
334
1174
|
}
|
|
335
|
-
return
|
|
1175
|
+
return nodeSet;
|
|
336
1176
|
}
|
|
337
1177
|
function getValueFromAdj(baseNode, adjList) {
|
|
338
1178
|
const childStateNodes = adjList.get(baseNode);
|
|
@@ -356,9 +1196,9 @@ function getValueFromAdj(baseNode, adjList) {
|
|
|
356
1196
|
}
|
|
357
1197
|
return stateValue;
|
|
358
1198
|
}
|
|
359
|
-
function getAdjList(
|
|
1199
|
+
function getAdjList(stateNodes) {
|
|
360
1200
|
const adjList = new Map();
|
|
361
|
-
for (const s of
|
|
1201
|
+
for (const s of stateNodes) {
|
|
362
1202
|
if (!adjList.has(s)) {
|
|
363
1203
|
adjList.set(s, []);
|
|
364
1204
|
}
|
|
@@ -371,25 +1211,25 @@ function getAdjList(configuration) {
|
|
|
371
1211
|
}
|
|
372
1212
|
return adjList;
|
|
373
1213
|
}
|
|
374
|
-
function getStateValue(rootNode,
|
|
375
|
-
const config =
|
|
1214
|
+
function getStateValue(rootNode, stateNodes) {
|
|
1215
|
+
const config = getAllStateNodes(stateNodes);
|
|
376
1216
|
return getValueFromAdj(rootNode, getAdjList(config));
|
|
377
1217
|
}
|
|
378
|
-
function isInFinalState(
|
|
1218
|
+
function isInFinalState(stateNodeSet, stateNode) {
|
|
379
1219
|
if (stateNode.type === 'compound') {
|
|
380
|
-
return getChildren(stateNode).some(s => s.type === 'final' &&
|
|
1220
|
+
return getChildren(stateNode).some(s => s.type === 'final' && stateNodeSet.has(s));
|
|
381
1221
|
}
|
|
382
1222
|
if (stateNode.type === 'parallel') {
|
|
383
|
-
return getChildren(stateNode).every(sn => isInFinalState(
|
|
1223
|
+
return getChildren(stateNode).every(sn => isInFinalState(stateNodeSet, sn));
|
|
384
1224
|
}
|
|
385
1225
|
return stateNode.type === 'final';
|
|
386
1226
|
}
|
|
387
|
-
const isStateId = str => str[0] ===
|
|
1227
|
+
const isStateId = str => str[0] === STATE_IDENTIFIER;
|
|
388
1228
|
function getCandidates(stateNode, receivedEventType) {
|
|
389
1229
|
const candidates = stateNode.transitions.get(receivedEventType) || [...stateNode.transitions.keys()].filter(descriptor => {
|
|
390
1230
|
// check if transition is a wildcard transition,
|
|
391
1231
|
// which matches any non-transient events
|
|
392
|
-
if (descriptor ===
|
|
1232
|
+
if (descriptor === WILDCARD) {
|
|
393
1233
|
return true;
|
|
394
1234
|
}
|
|
395
1235
|
if (!descriptor.endsWith('.*')) {
|
|
@@ -423,7 +1263,7 @@ function getDelayedTransitions(stateNode) {
|
|
|
423
1263
|
}
|
|
424
1264
|
const mutateEntryExit = (delay, i) => {
|
|
425
1265
|
const delayRef = typeof delay === 'function' ? `${stateNode.id}:delay[${i}]` : delay;
|
|
426
|
-
const afterEvent =
|
|
1266
|
+
const afterEvent = createAfterEvent(delayRef, stateNode.id);
|
|
427
1267
|
const eventType = afterEvent.type;
|
|
428
1268
|
stateNode.entry.push(raise(afterEvent, {
|
|
429
1269
|
id: eventType,
|
|
@@ -439,7 +1279,7 @@ function getDelayedTransitions(stateNode) {
|
|
|
439
1279
|
} : configTransition;
|
|
440
1280
|
const resolvedDelay = !isNaN(+delay) ? +delay : delay;
|
|
441
1281
|
const eventType = mutateEntryExit(resolvedDelay, i);
|
|
442
|
-
return
|
|
1282
|
+
return toArray(resolvedTransition).map(transition => ({
|
|
443
1283
|
...transition,
|
|
444
1284
|
event: eventType,
|
|
445
1285
|
delay: resolvedDelay
|
|
@@ -456,12 +1296,12 @@ function getDelayedTransitions(stateNode) {
|
|
|
456
1296
|
});
|
|
457
1297
|
}
|
|
458
1298
|
function formatTransition(stateNode, descriptor, transitionConfig) {
|
|
459
|
-
const normalizedTarget =
|
|
1299
|
+
const normalizedTarget = normalizeTarget(transitionConfig.target);
|
|
460
1300
|
const reenter = transitionConfig.reenter ?? false;
|
|
461
1301
|
const target = resolveTarget(stateNode, normalizedTarget);
|
|
462
1302
|
const transition = {
|
|
463
1303
|
...transitionConfig,
|
|
464
|
-
actions:
|
|
1304
|
+
actions: toArray(transitionConfig.actions),
|
|
465
1305
|
guard: transitionConfig.guard,
|
|
466
1306
|
target,
|
|
467
1307
|
source: stateNode,
|
|
@@ -479,29 +1319,29 @@ function formatTransitions(stateNode) {
|
|
|
479
1319
|
const transitions = new Map();
|
|
480
1320
|
if (stateNode.config.on) {
|
|
481
1321
|
for (const descriptor of Object.keys(stateNode.config.on)) {
|
|
482
|
-
if (descriptor ===
|
|
1322
|
+
if (descriptor === NULL_EVENT) {
|
|
483
1323
|
throw new Error('Null events ("") cannot be specified as a transition key. Use `always: { ... }` instead.');
|
|
484
1324
|
}
|
|
485
1325
|
const transitionsConfig = stateNode.config.on[descriptor];
|
|
486
|
-
transitions.set(descriptor,
|
|
1326
|
+
transitions.set(descriptor, toTransitionConfigArray(transitionsConfig).map(t => formatTransition(stateNode, descriptor, t)));
|
|
487
1327
|
}
|
|
488
1328
|
}
|
|
489
1329
|
if (stateNode.config.onDone) {
|
|
490
1330
|
const descriptor = `xstate.done.state.${stateNode.id}`;
|
|
491
|
-
transitions.set(descriptor,
|
|
1331
|
+
transitions.set(descriptor, toTransitionConfigArray(stateNode.config.onDone).map(t => formatTransition(stateNode, descriptor, t)));
|
|
492
1332
|
}
|
|
493
1333
|
for (const invokeDef of stateNode.invoke) {
|
|
494
1334
|
if (invokeDef.onDone) {
|
|
495
1335
|
const descriptor = `xstate.done.actor.${invokeDef.id}`;
|
|
496
|
-
transitions.set(descriptor,
|
|
1336
|
+
transitions.set(descriptor, toTransitionConfigArray(invokeDef.onDone).map(t => formatTransition(stateNode, descriptor, t)));
|
|
497
1337
|
}
|
|
498
1338
|
if (invokeDef.onError) {
|
|
499
1339
|
const descriptor = `xstate.error.actor.${invokeDef.id}`;
|
|
500
|
-
transitions.set(descriptor,
|
|
1340
|
+
transitions.set(descriptor, toTransitionConfigArray(invokeDef.onError).map(t => formatTransition(stateNode, descriptor, t)));
|
|
501
1341
|
}
|
|
502
1342
|
if (invokeDef.onSnapshot) {
|
|
503
1343
|
const descriptor = `xstate.snapshot.${invokeDef.id}`;
|
|
504
|
-
transitions.set(descriptor,
|
|
1344
|
+
transitions.set(descriptor, toTransitionConfigArray(invokeDef.onSnapshot).map(t => formatTransition(stateNode, descriptor, t)));
|
|
505
1345
|
}
|
|
506
1346
|
}
|
|
507
1347
|
for (const delayedTransition of stateNode.after) {
|
|
@@ -521,7 +1361,7 @@ function formatInitialTransition(stateNode, _target) {
|
|
|
521
1361
|
}
|
|
522
1362
|
const transition = {
|
|
523
1363
|
source: stateNode,
|
|
524
|
-
actions: !_target || typeof _target === 'string' ? [] :
|
|
1364
|
+
actions: !_target || typeof _target === 'string' ? [] : toArray(_target.actions),
|
|
525
1365
|
eventType: null,
|
|
526
1366
|
reenter: false,
|
|
527
1367
|
target: resolvedTarget ? [resolvedTarget] : [],
|
|
@@ -545,7 +1385,7 @@ function resolveTarget(stateNode, targets) {
|
|
|
545
1385
|
if (isStateId(target)) {
|
|
546
1386
|
return stateNode.machine.getStateNodeById(target);
|
|
547
1387
|
}
|
|
548
|
-
const isInternalTarget = target[0] ===
|
|
1388
|
+
const isInternalTarget = target[0] === STATE_DELIMITER;
|
|
549
1389
|
// If internal target is defined on machine,
|
|
550
1390
|
// do not include machine key on target
|
|
551
1391
|
if (isInternalTarget && !stateNode.parent) {
|
|
@@ -565,7 +1405,7 @@ function resolveTarget(stateNode, targets) {
|
|
|
565
1405
|
});
|
|
566
1406
|
}
|
|
567
1407
|
function resolveHistoryDefaultTransition(stateNode) {
|
|
568
|
-
const normalizedTarget =
|
|
1408
|
+
const normalizedTarget = normalizeTarget(stateNode.config.target);
|
|
569
1409
|
if (!normalizedTarget) {
|
|
570
1410
|
return stateNode.parent.initial;
|
|
571
1411
|
}
|
|
@@ -634,7 +1474,7 @@ function getStateNodeByPath(stateNode, statePath) {
|
|
|
634
1474
|
// throw e;
|
|
635
1475
|
}
|
|
636
1476
|
}
|
|
637
|
-
const arrayStatePath =
|
|
1477
|
+
const arrayStatePath = toStatePath(statePath).slice();
|
|
638
1478
|
let currentStateNode = stateNode;
|
|
639
1479
|
while (arrayStatePath.length) {
|
|
640
1480
|
const key = arrayStatePath.shift();
|
|
@@ -740,13 +1580,13 @@ function hasIntersection(s1, s2) {
|
|
|
740
1580
|
}
|
|
741
1581
|
return false;
|
|
742
1582
|
}
|
|
743
|
-
function removeConflictingTransitions(enabledTransitions,
|
|
1583
|
+
function removeConflictingTransitions(enabledTransitions, stateNodeSet, historyValue) {
|
|
744
1584
|
const filteredTransitions = new Set();
|
|
745
1585
|
for (const t1 of enabledTransitions) {
|
|
746
1586
|
let t1Preempted = false;
|
|
747
1587
|
const transitionsToRemove = new Set();
|
|
748
1588
|
for (const t2 of filteredTransitions) {
|
|
749
|
-
if (hasIntersection(computeExitSet([t1],
|
|
1589
|
+
if (hasIntersection(computeExitSet([t1], stateNodeSet, historyValue), computeExitSet([t2], stateNodeSet, historyValue))) {
|
|
750
1590
|
if (isDescendant(t1.source, t2.source)) {
|
|
751
1591
|
transitionsToRemove.add(t2);
|
|
752
1592
|
} else {
|
|
@@ -813,7 +1653,7 @@ function getTransitionDomain(transition, historyValue) {
|
|
|
813
1653
|
}
|
|
814
1654
|
return transition.source.machine.root;
|
|
815
1655
|
}
|
|
816
|
-
function computeExitSet(transitions,
|
|
1656
|
+
function computeExitSet(transitions, stateNodeSet, historyValue) {
|
|
817
1657
|
const statesToExit = new Set();
|
|
818
1658
|
for (const t of transitions) {
|
|
819
1659
|
if (t.target?.length) {
|
|
@@ -821,7 +1661,7 @@ function computeExitSet(transitions, configuration, historyValue) {
|
|
|
821
1661
|
if (t.reenter && t.source === domain) {
|
|
822
1662
|
statesToExit.add(domain);
|
|
823
1663
|
}
|
|
824
|
-
for (const stateNode of
|
|
1664
|
+
for (const stateNode of stateNodeSet) {
|
|
825
1665
|
if (isDescendant(stateNode, domain)) {
|
|
826
1666
|
statesToExit.add(stateNode);
|
|
827
1667
|
}
|
|
@@ -830,12 +1670,12 @@ function computeExitSet(transitions, configuration, historyValue) {
|
|
|
830
1670
|
}
|
|
831
1671
|
return [...statesToExit];
|
|
832
1672
|
}
|
|
833
|
-
function
|
|
834
|
-
if (
|
|
1673
|
+
function areStateNodeCollectionsEqual(prevStateNodes, nextStateNodeSet) {
|
|
1674
|
+
if (prevStateNodes.length !== nextStateNodeSet.size) {
|
|
835
1675
|
return false;
|
|
836
1676
|
}
|
|
837
|
-
for (const node of
|
|
838
|
-
if (!
|
|
1677
|
+
for (const node of prevStateNodes) {
|
|
1678
|
+
if (!nextStateNodeSet.has(node)) {
|
|
839
1679
|
return false;
|
|
840
1680
|
}
|
|
841
1681
|
}
|
|
@@ -849,31 +1689,31 @@ function microstep(transitions, currentState, actorScope, event, isInitial, inte
|
|
|
849
1689
|
if (!transitions.length) {
|
|
850
1690
|
return currentState;
|
|
851
1691
|
}
|
|
852
|
-
const
|
|
1692
|
+
const mutStateNodeSet = new Set(currentState._nodes);
|
|
853
1693
|
let historyValue = currentState.historyValue;
|
|
854
|
-
const filteredTransitions = removeConflictingTransitions(transitions,
|
|
1694
|
+
const filteredTransitions = removeConflictingTransitions(transitions, mutStateNodeSet, historyValue);
|
|
855
1695
|
let nextState = currentState;
|
|
856
1696
|
|
|
857
1697
|
// Exit states
|
|
858
1698
|
if (!isInitial) {
|
|
859
|
-
[nextState, historyValue] = exitStates(nextState, event, actorScope, filteredTransitions,
|
|
1699
|
+
[nextState, historyValue] = exitStates(nextState, event, actorScope, filteredTransitions, mutStateNodeSet, historyValue, internalQueue);
|
|
860
1700
|
}
|
|
861
1701
|
|
|
862
1702
|
// Execute transition content
|
|
863
1703
|
nextState = resolveActionsAndContext(nextState, event, actorScope, filteredTransitions.flatMap(t => t.actions), internalQueue);
|
|
864
1704
|
|
|
865
1705
|
// Enter states
|
|
866
|
-
nextState = enterStates(nextState, event, actorScope, filteredTransitions,
|
|
867
|
-
const
|
|
1706
|
+
nextState = enterStates(nextState, event, actorScope, filteredTransitions, mutStateNodeSet, internalQueue, historyValue, isInitial);
|
|
1707
|
+
const nextStateNodes = [...mutStateNodeSet];
|
|
868
1708
|
if (nextState.status === 'done') {
|
|
869
|
-
nextState = resolveActionsAndContext(nextState, event, actorScope,
|
|
1709
|
+
nextState = resolveActionsAndContext(nextState, event, actorScope, nextStateNodes.sort((a, b) => b.order - a.order).flatMap(state => state.exit), internalQueue);
|
|
870
1710
|
}
|
|
871
1711
|
try {
|
|
872
|
-
if (historyValue === currentState.historyValue &&
|
|
1712
|
+
if (historyValue === currentState.historyValue && areStateNodeCollectionsEqual(currentState._nodes, mutStateNodeSet)) {
|
|
873
1713
|
return nextState;
|
|
874
1714
|
}
|
|
875
1715
|
return cloneMachineSnapshot(nextState, {
|
|
876
|
-
|
|
1716
|
+
_nodes: nextStateNodes,
|
|
877
1717
|
historyValue
|
|
878
1718
|
});
|
|
879
1719
|
} catch (e) {
|
|
@@ -886,10 +1726,10 @@ function getMachineOutput(state, event, actorScope, rootNode, rootCompletionNode
|
|
|
886
1726
|
if (!rootNode.output) {
|
|
887
1727
|
return;
|
|
888
1728
|
}
|
|
889
|
-
const doneStateEvent =
|
|
890
|
-
return
|
|
1729
|
+
const doneStateEvent = createDoneStateEvent(rootCompletionNode.id, rootCompletionNode.output && rootCompletionNode.parent ? resolveOutput(rootCompletionNode.output, state.context, event, actorScope.self) : undefined);
|
|
1730
|
+
return resolveOutput(rootNode.output, state.context, doneStateEvent, actorScope.self);
|
|
891
1731
|
}
|
|
892
|
-
function enterStates(currentState, event, actorScope, filteredTransitions,
|
|
1732
|
+
function enterStates(currentState, event, actorScope, filteredTransitions, mutStateNodeSet, internalQueue, historyValue, isInitial) {
|
|
893
1733
|
let nextState = currentState;
|
|
894
1734
|
const statesToEnter = new Set();
|
|
895
1735
|
// those are states that were directly targeted or indirectly targeted by the explicit target
|
|
@@ -904,7 +1744,7 @@ function enterStates(currentState, event, actorScope, filteredTransitions, mutCo
|
|
|
904
1744
|
}
|
|
905
1745
|
const completedNodes = new Set();
|
|
906
1746
|
for (const stateNodeToEnter of [...statesToEnter].sort((a, b) => a.order - b.order)) {
|
|
907
|
-
|
|
1747
|
+
mutStateNodeSet.add(stateNodeToEnter);
|
|
908
1748
|
const actions = [];
|
|
909
1749
|
|
|
910
1750
|
// Add entry actions
|
|
@@ -925,11 +1765,11 @@ function enterStates(currentState, event, actorScope, filteredTransitions, mutCo
|
|
|
925
1765
|
let ancestorMarker = parent?.type === 'parallel' ? parent : parent?.parent;
|
|
926
1766
|
let rootCompletionNode = ancestorMarker || stateNodeToEnter;
|
|
927
1767
|
if (parent?.type === 'compound') {
|
|
928
|
-
internalQueue.push(
|
|
1768
|
+
internalQueue.push(createDoneStateEvent(parent.id, stateNodeToEnter.output ? resolveOutput(stateNodeToEnter.output, nextState.context, event, actorScope.self) : undefined));
|
|
929
1769
|
}
|
|
930
|
-
while (ancestorMarker?.type === 'parallel' && !completedNodes.has(ancestorMarker) && isInFinalState(
|
|
1770
|
+
while (ancestorMarker?.type === 'parallel' && !completedNodes.has(ancestorMarker) && isInFinalState(mutStateNodeSet, ancestorMarker)) {
|
|
931
1771
|
completedNodes.add(ancestorMarker);
|
|
932
|
-
internalQueue.push(
|
|
1772
|
+
internalQueue.push(createDoneStateEvent(ancestorMarker.id));
|
|
933
1773
|
rootCompletionNode = ancestorMarker;
|
|
934
1774
|
ancestorMarker = ancestorMarker.parent;
|
|
935
1775
|
}
|
|
@@ -1037,9 +1877,9 @@ function addAncestorStatesToEnter(statesToEnter, historyValue, statesForDefaultE
|
|
|
1037
1877
|
function addProperAncestorStatesToEnter(stateNode, toStateNode, statesToEnter, historyValue, statesForDefaultEntry) {
|
|
1038
1878
|
addAncestorStatesToEnter(statesToEnter, historyValue, statesForDefaultEntry, getProperAncestors(stateNode, toStateNode));
|
|
1039
1879
|
}
|
|
1040
|
-
function exitStates(currentState, event, actorScope, transitions,
|
|
1880
|
+
function exitStates(currentState, event, actorScope, transitions, mutStateNodeSet, historyValue, internalQueue) {
|
|
1041
1881
|
let nextState = currentState;
|
|
1042
|
-
const statesToExit = computeExitSet(transitions,
|
|
1882
|
+
const statesToExit = computeExitSet(transitions, mutStateNodeSet, historyValue);
|
|
1043
1883
|
statesToExit.sort((a, b) => b.order - a.order);
|
|
1044
1884
|
let changedHistory;
|
|
1045
1885
|
|
|
@@ -1057,12 +1897,12 @@ function exitStates(currentState, event, actorScope, transitions, mutConfigurati
|
|
|
1057
1897
|
changedHistory ??= {
|
|
1058
1898
|
...historyValue
|
|
1059
1899
|
};
|
|
1060
|
-
changedHistory[historyNode.id] = Array.from(
|
|
1900
|
+
changedHistory[historyNode.id] = Array.from(mutStateNodeSet).filter(predicate);
|
|
1061
1901
|
}
|
|
1062
1902
|
}
|
|
1063
1903
|
for (const s of statesToExit) {
|
|
1064
1904
|
nextState = resolveActionsAndContext(nextState, event, actorScope, [...s.exit, ...s.invoke.map(def => stop(def.id))], internalQueue);
|
|
1065
|
-
|
|
1905
|
+
mutStateNodeSet.delete(s);
|
|
1066
1906
|
}
|
|
1067
1907
|
return [nextState, changedHistory || historyValue];
|
|
1068
1908
|
}
|
|
@@ -1092,7 +1932,7 @@ function resolveActionsAndContextWorker(currentState, event, actorScope, actions
|
|
|
1092
1932
|
event
|
|
1093
1933
|
}) : action.params : undefined;
|
|
1094
1934
|
if (!('resolve' in resolvedAction)) {
|
|
1095
|
-
if (actorScope?.self._processingStatus ===
|
|
1935
|
+
if (actorScope?.self._processingStatus === ProcessingStatus.Running) {
|
|
1096
1936
|
resolvedAction(actionArgs, actionParams);
|
|
1097
1937
|
} else {
|
|
1098
1938
|
actorScope?.defer(() => {
|
|
@@ -1110,7 +1950,7 @@ function resolveActionsAndContextWorker(currentState, event, actorScope, actions
|
|
|
1110
1950
|
retries?.push([builtinAction, params]);
|
|
1111
1951
|
}
|
|
1112
1952
|
if ('execute' in builtinAction) {
|
|
1113
|
-
if (actorScope?.self._processingStatus ===
|
|
1953
|
+
if (actorScope?.self._processingStatus === ProcessingStatus.Running) {
|
|
1114
1954
|
builtinAction.execute(actorScope, params);
|
|
1115
1955
|
} else {
|
|
1116
1956
|
actorScope?.defer(builtinAction.execute.bind(null, actorScope, params));
|
|
@@ -1138,7 +1978,7 @@ function macrostep(state, event, actorScope, internalQueue = []) {
|
|
|
1138
1978
|
const states = [];
|
|
1139
1979
|
|
|
1140
1980
|
// Handle stop event
|
|
1141
|
-
if (event.type ===
|
|
1981
|
+
if (event.type === XSTATE_STOP) {
|
|
1142
1982
|
nextState = cloneMachineSnapshot(stopChildren(nextState, event, actorScope), {
|
|
1143
1983
|
status: 'stopped'
|
|
1144
1984
|
});
|
|
@@ -1152,7 +1992,7 @@ function macrostep(state, event, actorScope, internalQueue = []) {
|
|
|
1152
1992
|
|
|
1153
1993
|
// Assume the state is at rest (no raised events)
|
|
1154
1994
|
// Determine the next state based on the next microstep
|
|
1155
|
-
if (nextEvent.type !==
|
|
1995
|
+
if (nextEvent.type !== XSTATE_INIT) {
|
|
1156
1996
|
const transitions = selectTransitions(nextEvent, nextState);
|
|
1157
1997
|
nextState = microstep(transitions, state, actorScope, nextEvent, false, internalQueue);
|
|
1158
1998
|
states.push(nextState);
|
|
@@ -1191,7 +2031,7 @@ function selectTransitions(event, nextState) {
|
|
|
1191
2031
|
}
|
|
1192
2032
|
function selectEventlessTransitions(nextState, event) {
|
|
1193
2033
|
const enabledTransitionSet = new Set();
|
|
1194
|
-
const atomicStates = nextState.
|
|
2034
|
+
const atomicStates = nextState._nodes.filter(isAtomicStateNode);
|
|
1195
2035
|
for (const stateNode of atomicStates) {
|
|
1196
2036
|
loop: for (const s of [stateNode].concat(getProperAncestors(stateNode, undefined))) {
|
|
1197
2037
|
if (!s.always) {
|
|
@@ -1205,7 +2045,7 @@ function selectEventlessTransitions(nextState, event) {
|
|
|
1205
2045
|
}
|
|
1206
2046
|
}
|
|
1207
2047
|
}
|
|
1208
|
-
return removeConflictingTransitions(Array.from(enabledTransitionSet), new Set(nextState.
|
|
2048
|
+
return removeConflictingTransitions(Array.from(enabledTransitionSet), new Set(nextState._nodes), nextState.historyValue);
|
|
1209
2049
|
}
|
|
1210
2050
|
|
|
1211
2051
|
/**
|
|
@@ -1214,12 +2054,15 @@ function selectEventlessTransitions(nextState, event) {
|
|
|
1214
2054
|
* @param stateValue The partial state value to resolve.
|
|
1215
2055
|
*/
|
|
1216
2056
|
function resolveStateValue(rootNode, stateValue) {
|
|
1217
|
-
const
|
|
1218
|
-
return getStateValue(rootNode, [...
|
|
2057
|
+
const allStateNodes = getAllStateNodes(getStateNodes(rootNode, stateValue));
|
|
2058
|
+
return getStateValue(rootNode, [...allStateNodes]);
|
|
1219
2059
|
}
|
|
1220
2060
|
|
|
2061
|
+
function isMachineSnapshot(value) {
|
|
2062
|
+
return !!value && typeof value === 'object' && 'machine' in value && 'value' in value;
|
|
2063
|
+
}
|
|
1221
2064
|
const machineSnapshotMatches = function matches(testValue) {
|
|
1222
|
-
return
|
|
2065
|
+
return matchesState(testValue, this.value);
|
|
1223
2066
|
};
|
|
1224
2067
|
const machineSnapshotHasTag = function hasTag(tag) {
|
|
1225
2068
|
return this.tags.has(tag);
|
|
@@ -1232,10 +2075,11 @@ const machineSnapshotCan = function can(event) {
|
|
|
1232
2075
|
};
|
|
1233
2076
|
const machineSnapshotToJSON = function toJSON() {
|
|
1234
2077
|
const {
|
|
1235
|
-
|
|
2078
|
+
_nodes: nodes,
|
|
1236
2079
|
tags,
|
|
1237
2080
|
machine,
|
|
1238
|
-
|
|
2081
|
+
getNextEvents,
|
|
2082
|
+
getMeta,
|
|
1239
2083
|
toJSON,
|
|
1240
2084
|
can,
|
|
1241
2085
|
hasTag,
|
|
@@ -1247,13 +2091,11 @@ const machineSnapshotToJSON = function toJSON() {
|
|
|
1247
2091
|
tags: Array.from(tags)
|
|
1248
2092
|
};
|
|
1249
2093
|
};
|
|
1250
|
-
const
|
|
1251
|
-
return
|
|
1252
|
-
return [...new Set(interpreter.flatten([...this.configuration.map(sn => sn.ownEvents)]))];
|
|
1253
|
-
});
|
|
2094
|
+
const machineSnapshotGetNextEvents = function getNextEvents() {
|
|
2095
|
+
return [...new Set(flatten([...this._nodes.map(sn => sn.ownEvents)]))];
|
|
1254
2096
|
};
|
|
1255
|
-
const
|
|
1256
|
-
return this.
|
|
2097
|
+
const machineSnapshotGetMeta = function getMeta() {
|
|
2098
|
+
return this._nodes.reduce((acc, stateNode) => {
|
|
1257
2099
|
if (stateNode.meta !== undefined) {
|
|
1258
2100
|
acc[stateNode.id] = stateNode.meta;
|
|
1259
2101
|
}
|
|
@@ -1261,48 +2103,35 @@ const machineSnapshotMeta = function nextEvents() {
|
|
|
1261
2103
|
}, {});
|
|
1262
2104
|
};
|
|
1263
2105
|
function createMachineSnapshot(config, machine) {
|
|
1264
|
-
|
|
2106
|
+
return {
|
|
1265
2107
|
status: config.status,
|
|
1266
2108
|
output: config.output,
|
|
1267
2109
|
error: config.error,
|
|
1268
2110
|
machine,
|
|
1269
2111
|
context: config.context,
|
|
1270
|
-
|
|
1271
|
-
value: getStateValue(machine.root, config.
|
|
1272
|
-
tags: new Set(
|
|
2112
|
+
_nodes: config._nodes,
|
|
2113
|
+
value: getStateValue(machine.root, config._nodes),
|
|
2114
|
+
tags: new Set(flatten(config._nodes.map(sn => sn.tags))),
|
|
1273
2115
|
children: config.children,
|
|
1274
2116
|
historyValue: config.historyValue || {},
|
|
1275
2117
|
// this one is generic in the target and it's hard to create a matching non-generic source signature
|
|
1276
2118
|
matches: machineSnapshotMatches,
|
|
1277
2119
|
hasTag: machineSnapshotHasTag,
|
|
1278
2120
|
can: machineSnapshotCan,
|
|
2121
|
+
getNextEvents: machineSnapshotGetNextEvents,
|
|
2122
|
+
getMeta: machineSnapshotGetMeta,
|
|
1279
2123
|
toJSON: machineSnapshotToJSON
|
|
1280
2124
|
};
|
|
1281
|
-
Object.defineProperties(snapshot, {
|
|
1282
|
-
nextEvents: {
|
|
1283
|
-
get: machineSnapshotNextEvents,
|
|
1284
|
-
configurable: true,
|
|
1285
|
-
enumerable: true
|
|
1286
|
-
},
|
|
1287
|
-
meta: {
|
|
1288
|
-
get: machineSnapshotMeta,
|
|
1289
|
-
configurable: true,
|
|
1290
|
-
enumerable: true
|
|
1291
|
-
}
|
|
1292
|
-
});
|
|
1293
|
-
return snapshot;
|
|
1294
2125
|
}
|
|
1295
2126
|
function cloneMachineSnapshot(state, config = {}) {
|
|
1296
|
-
return createMachineSnapshot(
|
|
1297
|
-
// TODO: it's wasteful that this spread triggers getters
|
|
1298
|
-
{
|
|
2127
|
+
return createMachineSnapshot({
|
|
1299
2128
|
...state,
|
|
1300
2129
|
...config
|
|
1301
2130
|
}, state.machine);
|
|
1302
2131
|
}
|
|
1303
2132
|
function getPersistedState(state, options) {
|
|
1304
2133
|
const {
|
|
1305
|
-
|
|
2134
|
+
_nodes: nodes,
|
|
1306
2135
|
tags,
|
|
1307
2136
|
machine,
|
|
1308
2137
|
children,
|
|
@@ -1310,8 +2139,9 @@ function getPersistedState(state, options) {
|
|
|
1310
2139
|
can,
|
|
1311
2140
|
hasTag,
|
|
1312
2141
|
matches,
|
|
2142
|
+
getNextEvents,
|
|
2143
|
+
getMeta,
|
|
1313
2144
|
toJSON,
|
|
1314
|
-
nextEvents,
|
|
1315
2145
|
...jsonValues
|
|
1316
2146
|
} = state;
|
|
1317
2147
|
const childrenJson = {};
|
|
@@ -1320,7 +2150,8 @@ function getPersistedState(state, options) {
|
|
|
1320
2150
|
childrenJson[id] = {
|
|
1321
2151
|
state: child.getPersistedState(options),
|
|
1322
2152
|
src: child.src,
|
|
1323
|
-
systemId: child._systemId
|
|
2153
|
+
systemId: child._systemId,
|
|
2154
|
+
syncSnapshot: child._syncSnapshot
|
|
1324
2155
|
};
|
|
1325
2156
|
}
|
|
1326
2157
|
const persisted = {
|
|
@@ -1340,7 +2171,7 @@ function persistContext(contextPart) {
|
|
|
1340
2171
|
...contextPart
|
|
1341
2172
|
};
|
|
1342
2173
|
copy[key] = {
|
|
1343
|
-
xstate$$type:
|
|
2174
|
+
xstate$$type: $$ACTOR_TYPE,
|
|
1344
2175
|
id: value.id
|
|
1345
2176
|
};
|
|
1346
2177
|
} else {
|
|
@@ -1409,32 +2240,52 @@ function raise(eventOrExpr, options) {
|
|
|
1409
2240
|
return raise;
|
|
1410
2241
|
}
|
|
1411
2242
|
|
|
2243
|
+
exports.$$ACTOR_TYPE = $$ACTOR_TYPE;
|
|
2244
|
+
exports.Actor = Actor;
|
|
2245
|
+
exports.NULL_EVENT = NULL_EVENT;
|
|
2246
|
+
exports.ProcessingStatus = ProcessingStatus;
|
|
2247
|
+
exports.STATE_DELIMITER = STATE_DELIMITER;
|
|
2248
|
+
exports.XSTATE_ERROR = XSTATE_ERROR;
|
|
2249
|
+
exports.XSTATE_STOP = XSTATE_STOP;
|
|
1412
2250
|
exports.and = and;
|
|
1413
2251
|
exports.cancel = cancel;
|
|
1414
2252
|
exports.cloneMachineSnapshot = cloneMachineSnapshot;
|
|
2253
|
+
exports.createActor = createActor;
|
|
2254
|
+
exports.createErrorActorEvent = createErrorActorEvent;
|
|
2255
|
+
exports.createInitEvent = createInitEvent;
|
|
2256
|
+
exports.createInvokeId = createInvokeId;
|
|
1415
2257
|
exports.createMachineSnapshot = createMachineSnapshot;
|
|
1416
2258
|
exports.evaluateGuard = evaluateGuard;
|
|
1417
2259
|
exports.formatInitialTransition = formatInitialTransition;
|
|
1418
2260
|
exports.formatTransition = formatTransition;
|
|
1419
2261
|
exports.formatTransitions = formatTransitions;
|
|
2262
|
+
exports.getAllStateNodes = getAllStateNodes;
|
|
1420
2263
|
exports.getCandidates = getCandidates;
|
|
1421
|
-
exports.getConfiguration = getConfiguration;
|
|
1422
2264
|
exports.getDelayedTransitions = getDelayedTransitions;
|
|
1423
2265
|
exports.getInitialStateNodes = getInitialStateNodes;
|
|
1424
2266
|
exports.getPersistedState = getPersistedState;
|
|
1425
2267
|
exports.getStateNodeByPath = getStateNodeByPath;
|
|
1426
2268
|
exports.getStateNodes = getStateNodes;
|
|
2269
|
+
exports.interpret = interpret;
|
|
2270
|
+
exports.isErrorActorEvent = isErrorActorEvent;
|
|
1427
2271
|
exports.isInFinalState = isInFinalState;
|
|
2272
|
+
exports.isMachineSnapshot = isMachineSnapshot;
|
|
1428
2273
|
exports.isStateId = isStateId;
|
|
1429
2274
|
exports.macrostep = macrostep;
|
|
1430
|
-
exports.
|
|
2275
|
+
exports.mapValues = mapValues;
|
|
2276
|
+
exports.matchesState = matchesState;
|
|
1431
2277
|
exports.microstep = microstep;
|
|
1432
2278
|
exports.not = not;
|
|
1433
2279
|
exports.or = or;
|
|
2280
|
+
exports.pathToStateValue = pathToStateValue;
|
|
1434
2281
|
exports.raise = raise;
|
|
1435
2282
|
exports.resolveActionsAndContext = resolveActionsAndContext;
|
|
2283
|
+
exports.resolveReferencedActor = resolveReferencedActor;
|
|
1436
2284
|
exports.resolveStateValue = resolveStateValue;
|
|
1437
2285
|
exports.spawn = spawn;
|
|
1438
2286
|
exports.stateIn = stateIn;
|
|
1439
2287
|
exports.stop = stop;
|
|
2288
|
+
exports.toArray = toArray;
|
|
2289
|
+
exports.toObserver = toObserver;
|
|
2290
|
+
exports.toTransitionConfigArray = toTransitionConfigArray;
|
|
1440
2291
|
exports.transitionNode = transitionNode;
|