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