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,883 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
1
|
+
import { devToolsAdapter } from '../dev/dist/xstate-dev.development.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
|
+
if (!!mapper && typeof mapper === 'object' && Object.values(mapper).some(val => typeof val === 'function')) {
|
|
275
|
+
console.warn(`Dynamically mapping values to individual properties is deprecated. Use a single function that returns the mapped object instead.\nFound object containing properties whose values are possibly mapping functions: ${Object.entries(mapper).filter(([key, value]) => typeof value === 'function').map(([key, value]) => `\n - ${key}: ${value.toString().replace(/\n\s*/g, '')}`).join('')}`);
|
|
276
|
+
}
|
|
277
|
+
return mapper;
|
|
278
|
+
}
|
|
279
|
+
function isArray(value) {
|
|
280
|
+
return Array.isArray(value);
|
|
281
|
+
}
|
|
282
|
+
function isErrorActorEvent(event) {
|
|
283
|
+
return event.type.startsWith('xstate.error.actor');
|
|
284
|
+
}
|
|
285
|
+
function toTransitionConfigArray(configLike) {
|
|
286
|
+
return toArrayStrict(configLike).map(transitionLike => {
|
|
287
|
+
if (typeof transitionLike === 'undefined' || typeof transitionLike === 'string') {
|
|
288
|
+
return {
|
|
289
|
+
target: transitionLike
|
|
290
|
+
};
|
|
291
|
+
}
|
|
292
|
+
return transitionLike;
|
|
293
|
+
});
|
|
294
|
+
}
|
|
295
|
+
function normalizeTarget(target) {
|
|
296
|
+
if (target === undefined || target === TARGETLESS_KEY) {
|
|
297
|
+
return undefined;
|
|
298
|
+
}
|
|
299
|
+
return toArray(target);
|
|
300
|
+
}
|
|
301
|
+
function toObserver(nextHandler, errorHandler, completionHandler) {
|
|
302
|
+
const isObserver = typeof nextHandler === 'object';
|
|
303
|
+
const self = isObserver ? nextHandler : undefined;
|
|
304
|
+
return {
|
|
305
|
+
next: (isObserver ? nextHandler.next : nextHandler)?.bind(self),
|
|
306
|
+
error: (isObserver ? nextHandler.error : errorHandler)?.bind(self),
|
|
307
|
+
complete: (isObserver ? nextHandler.complete : completionHandler)?.bind(self)
|
|
308
|
+
};
|
|
309
|
+
}
|
|
310
|
+
function createInvokeId(stateNodeId, index) {
|
|
311
|
+
return `${stateNodeId}[${index}]`;
|
|
312
|
+
}
|
|
313
|
+
function resolveReferencedActor(machine, src) {
|
|
314
|
+
if (src.startsWith('xstate#')) {
|
|
315
|
+
const [, indexStr] = src.match(/\[(\d+)\]$/);
|
|
316
|
+
const node = machine.getStateNodeById(src.slice(7, -(indexStr.length + 2)));
|
|
317
|
+
const invokeConfig = node.config.invoke;
|
|
318
|
+
return (Array.isArray(invokeConfig) ? invokeConfig[indexStr] : invokeConfig).src;
|
|
319
|
+
}
|
|
320
|
+
return machine.implementations.actors[src];
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
const $$ACTOR_TYPE = 1;
|
|
324
|
+
// those values are currently used by @xstate/react directly so it's important to keep the assigned values in sync
|
|
325
|
+
let ProcessingStatus = /*#__PURE__*/function (ProcessingStatus) {
|
|
326
|
+
ProcessingStatus[ProcessingStatus["NotStarted"] = 0] = "NotStarted";
|
|
327
|
+
ProcessingStatus[ProcessingStatus["Running"] = 1] = "Running";
|
|
328
|
+
ProcessingStatus[ProcessingStatus["Stopped"] = 2] = "Stopped";
|
|
329
|
+
return ProcessingStatus;
|
|
330
|
+
}({});
|
|
331
|
+
const defaultOptions = {
|
|
332
|
+
clock: {
|
|
333
|
+
setTimeout: (fn, ms) => {
|
|
334
|
+
return setTimeout(fn, ms);
|
|
335
|
+
},
|
|
336
|
+
clearTimeout: id => {
|
|
337
|
+
return clearTimeout(id);
|
|
338
|
+
}
|
|
339
|
+
},
|
|
340
|
+
logger: console.log.bind(console),
|
|
341
|
+
devTools: false
|
|
342
|
+
};
|
|
343
|
+
|
|
344
|
+
/**
|
|
345
|
+
* 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.
|
|
346
|
+
*/
|
|
347
|
+
class Actor {
|
|
348
|
+
/**
|
|
349
|
+
* The current internal state of the actor.
|
|
350
|
+
*/
|
|
351
|
+
|
|
352
|
+
/**
|
|
353
|
+
* The clock that is responsible for setting and clearing timeouts, such as delayed events and transitions.
|
|
354
|
+
*/
|
|
355
|
+
|
|
356
|
+
/**
|
|
357
|
+
* The unique identifier for this actor relative to its parent.
|
|
358
|
+
*/
|
|
359
|
+
|
|
360
|
+
/** @internal */
|
|
361
|
+
|
|
362
|
+
// Actor Ref
|
|
363
|
+
|
|
364
|
+
// TODO: add typings for system
|
|
365
|
+
|
|
366
|
+
/**
|
|
367
|
+
* The globally unique process ID for this invocation.
|
|
368
|
+
*/
|
|
369
|
+
|
|
370
|
+
/**
|
|
371
|
+
* The system to which this actor belongs.
|
|
372
|
+
*/
|
|
373
|
+
|
|
374
|
+
/**
|
|
375
|
+
* Creates a new actor instance for the given logic with the provided options, if any.
|
|
376
|
+
*
|
|
377
|
+
* @param logic The logic to create an actor from
|
|
378
|
+
* @param options Actor options
|
|
379
|
+
*/
|
|
380
|
+
constructor(logic, options) {
|
|
381
|
+
this.logic = logic;
|
|
382
|
+
this._state = void 0;
|
|
383
|
+
this.clock = void 0;
|
|
384
|
+
this.options = void 0;
|
|
385
|
+
this.id = void 0;
|
|
386
|
+
this.mailbox = new Mailbox(this._process.bind(this));
|
|
387
|
+
this.delayedEventsMap = {};
|
|
388
|
+
this.observers = new Set();
|
|
389
|
+
this.logger = void 0;
|
|
390
|
+
this._processingStatus = ProcessingStatus.NotStarted;
|
|
391
|
+
this._parent = void 0;
|
|
392
|
+
this._syncSnapshot = void 0;
|
|
393
|
+
this.ref = void 0;
|
|
394
|
+
this._actorScope = void 0;
|
|
395
|
+
this._systemId = void 0;
|
|
396
|
+
this.sessionId = void 0;
|
|
397
|
+
this.system = void 0;
|
|
398
|
+
this._doneEvent = void 0;
|
|
399
|
+
this.src = void 0;
|
|
400
|
+
this._deferred = [];
|
|
401
|
+
const resolvedOptions = {
|
|
402
|
+
...defaultOptions,
|
|
403
|
+
...options
|
|
404
|
+
};
|
|
405
|
+
const {
|
|
406
|
+
clock,
|
|
407
|
+
logger,
|
|
408
|
+
parent,
|
|
409
|
+
syncSnapshot,
|
|
410
|
+
id,
|
|
411
|
+
systemId,
|
|
412
|
+
inspect
|
|
413
|
+
} = resolvedOptions;
|
|
414
|
+
this.system = parent?.system ?? createSystem(this);
|
|
415
|
+
if (inspect && !parent) {
|
|
416
|
+
// Always inspect at the system-level
|
|
417
|
+
this.system.inspect(toObserver(inspect));
|
|
418
|
+
}
|
|
419
|
+
this.sessionId = this.system._bookId();
|
|
420
|
+
this.id = id ?? this.sessionId;
|
|
421
|
+
this.logger = logger;
|
|
422
|
+
this.clock = clock;
|
|
423
|
+
this._parent = parent;
|
|
424
|
+
this._syncSnapshot = syncSnapshot;
|
|
425
|
+
this.options = resolvedOptions;
|
|
426
|
+
this.src = resolvedOptions.src ?? logic;
|
|
427
|
+
this.ref = this;
|
|
428
|
+
this._actorScope = {
|
|
429
|
+
self: this,
|
|
430
|
+
id: this.id,
|
|
431
|
+
sessionId: this.sessionId,
|
|
432
|
+
logger: this.logger,
|
|
433
|
+
defer: fn => {
|
|
434
|
+
this._deferred.push(fn);
|
|
435
|
+
},
|
|
436
|
+
system: this.system,
|
|
437
|
+
stopChild: child => {
|
|
438
|
+
if (child._parent !== this) {
|
|
439
|
+
throw new Error(`Cannot stop child actor ${child.id} of ${this.id} because it is not a child`);
|
|
440
|
+
}
|
|
441
|
+
child._stop();
|
|
442
|
+
}
|
|
443
|
+
};
|
|
444
|
+
|
|
445
|
+
// Ensure that the send method is bound to this Actor instance
|
|
446
|
+
// if destructured
|
|
447
|
+
this.send = this.send.bind(this);
|
|
448
|
+
this.system._sendInspectionEvent({
|
|
449
|
+
type: '@xstate.actor',
|
|
450
|
+
actorRef: this
|
|
451
|
+
});
|
|
452
|
+
if (systemId) {
|
|
453
|
+
this._systemId = systemId;
|
|
454
|
+
this.system._set(systemId, this);
|
|
455
|
+
}
|
|
456
|
+
this._initState(options?.state);
|
|
457
|
+
if (systemId && this._state.status !== 'active') {
|
|
458
|
+
this.system._unregister(this);
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
_initState(persistedState) {
|
|
462
|
+
this._state = persistedState ? this.logic.restoreState ? this.logic.restoreState(persistedState, this._actorScope) : persistedState : this.logic.getInitialState(this._actorScope, this.options?.input);
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
// array of functions to defer
|
|
466
|
+
|
|
467
|
+
update(snapshot, event) {
|
|
468
|
+
// Update state
|
|
469
|
+
this._state = snapshot;
|
|
470
|
+
|
|
471
|
+
// Execute deferred effects
|
|
472
|
+
let deferredFn;
|
|
473
|
+
while (deferredFn = this._deferred.shift()) {
|
|
474
|
+
deferredFn();
|
|
475
|
+
}
|
|
476
|
+
for (const observer of this.observers) {
|
|
477
|
+
try {
|
|
478
|
+
observer.next?.(snapshot);
|
|
479
|
+
} catch (err) {
|
|
480
|
+
reportUnhandledError(err);
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
switch (this._state.status) {
|
|
484
|
+
case 'done':
|
|
485
|
+
this._stopProcedure();
|
|
486
|
+
this._complete();
|
|
487
|
+
this._doneEvent = createDoneActorEvent(this.id, this._state.output);
|
|
488
|
+
if (this._parent) {
|
|
489
|
+
this.system._relay(this, this._parent, this._doneEvent);
|
|
490
|
+
}
|
|
491
|
+
break;
|
|
492
|
+
case 'error':
|
|
493
|
+
this._stopProcedure();
|
|
494
|
+
this._error(this._state.error);
|
|
495
|
+
if (this._parent) {
|
|
496
|
+
this.system._relay(this, this._parent, createErrorActorEvent(this.id, this._state.error));
|
|
497
|
+
}
|
|
498
|
+
break;
|
|
499
|
+
}
|
|
500
|
+
this.system._sendInspectionEvent({
|
|
501
|
+
type: '@xstate.snapshot',
|
|
502
|
+
actorRef: this,
|
|
503
|
+
event,
|
|
504
|
+
snapshot
|
|
505
|
+
});
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
/**
|
|
509
|
+
* Subscribe an observer to an actor’s snapshot values.
|
|
510
|
+
*
|
|
511
|
+
* @remarks
|
|
512
|
+
* The observer will receive the actor’s snapshot value when it is emitted. The observer can be:
|
|
513
|
+
* - A plain function that receives the latest snapshot, or
|
|
514
|
+
* - An observer object whose `.next(snapshot)` method receives the latest snapshot
|
|
515
|
+
*
|
|
516
|
+
* @example
|
|
517
|
+
* ```ts
|
|
518
|
+
* // Observer as a plain function
|
|
519
|
+
* const subscription = actor.subscribe((snapshot) => {
|
|
520
|
+
* console.log(snapshot);
|
|
521
|
+
* });
|
|
522
|
+
* ```
|
|
523
|
+
*
|
|
524
|
+
* @example
|
|
525
|
+
* ```ts
|
|
526
|
+
* // Observer as an object
|
|
527
|
+
* const subscription = actor.subscribe({
|
|
528
|
+
* next(snapshot) {
|
|
529
|
+
* console.log(snapshot);
|
|
530
|
+
* },
|
|
531
|
+
* error(err) {
|
|
532
|
+
* // ...
|
|
533
|
+
* },
|
|
534
|
+
* complete() {
|
|
535
|
+
* // ...
|
|
536
|
+
* },
|
|
537
|
+
* });
|
|
538
|
+
* ```
|
|
539
|
+
*
|
|
540
|
+
* 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:
|
|
541
|
+
*
|
|
542
|
+
* @example
|
|
543
|
+
* ```ts
|
|
544
|
+
* const subscription = actor.subscribe((snapshot) => {
|
|
545
|
+
* // ...
|
|
546
|
+
* });
|
|
547
|
+
*
|
|
548
|
+
* // Unsubscribe the observer
|
|
549
|
+
* subscription.unsubscribe();
|
|
550
|
+
* ```
|
|
551
|
+
*
|
|
552
|
+
* When the actor is stopped, all of its observers will automatically be unsubscribed.
|
|
553
|
+
*
|
|
554
|
+
* @param observer - Either a plain function that receives the latest snapshot, or an observer object whose `.next(snapshot)` method receives the latest snapshot
|
|
555
|
+
*/
|
|
556
|
+
|
|
557
|
+
subscribe(nextListenerOrObserver, errorListener, completeListener) {
|
|
558
|
+
const observer = toObserver(nextListenerOrObserver, errorListener, completeListener);
|
|
559
|
+
if (this._processingStatus !== ProcessingStatus.Stopped) {
|
|
560
|
+
this.observers.add(observer);
|
|
561
|
+
} else {
|
|
562
|
+
try {
|
|
563
|
+
observer.complete?.();
|
|
564
|
+
} catch (err) {
|
|
565
|
+
reportUnhandledError(err);
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
return {
|
|
569
|
+
unsubscribe: () => {
|
|
570
|
+
this.observers.delete(observer);
|
|
571
|
+
}
|
|
9
572
|
};
|
|
10
|
-
cache.set(object, memoizedData);
|
|
11
|
-
} else if (!(key in memoizedData)) {
|
|
12
|
-
memoizedData[key] = fn();
|
|
13
573
|
}
|
|
14
|
-
|
|
574
|
+
|
|
575
|
+
/**
|
|
576
|
+
* Starts the Actor from the initial state
|
|
577
|
+
*/
|
|
578
|
+
start() {
|
|
579
|
+
if (this._processingStatus === ProcessingStatus.Running) {
|
|
580
|
+
// Do not restart the service if it is already started
|
|
581
|
+
return this;
|
|
582
|
+
}
|
|
583
|
+
if (this._syncSnapshot) {
|
|
584
|
+
this.subscribe({
|
|
585
|
+
next: snapshot => {
|
|
586
|
+
if (snapshot.status === 'active') {
|
|
587
|
+
this._parent.send({
|
|
588
|
+
type: `xstate.snapshot.${this.id}`,
|
|
589
|
+
snapshot
|
|
590
|
+
});
|
|
591
|
+
}
|
|
592
|
+
},
|
|
593
|
+
error: () => {}
|
|
594
|
+
});
|
|
595
|
+
}
|
|
596
|
+
this.system._register(this.sessionId, this);
|
|
597
|
+
if (this._systemId) {
|
|
598
|
+
this.system._set(this._systemId, this);
|
|
599
|
+
}
|
|
600
|
+
this._processingStatus = ProcessingStatus.Running;
|
|
601
|
+
|
|
602
|
+
// TODO: this isn't correct when rehydrating
|
|
603
|
+
const initEvent = createInitEvent(this.options.input);
|
|
604
|
+
this.system._sendInspectionEvent({
|
|
605
|
+
type: '@xstate.event',
|
|
606
|
+
sourceRef: this._parent,
|
|
607
|
+
actorRef: this,
|
|
608
|
+
event: initEvent
|
|
609
|
+
});
|
|
610
|
+
const status = this._state.status;
|
|
611
|
+
switch (status) {
|
|
612
|
+
case 'done':
|
|
613
|
+
// a state machine can be "done" upon initialization (it could reach a final state using initial microsteps)
|
|
614
|
+
// we still need to complete observers, flush deferreds etc
|
|
615
|
+
this.update(this._state, initEvent);
|
|
616
|
+
// fallthrough
|
|
617
|
+
case 'error':
|
|
618
|
+
// TODO: rethink cleanup of observers, mailbox, etc
|
|
619
|
+
return this;
|
|
620
|
+
}
|
|
621
|
+
if (this.logic.start) {
|
|
622
|
+
try {
|
|
623
|
+
this.logic.start(this._state, this._actorScope);
|
|
624
|
+
} catch (err) {
|
|
625
|
+
this._stopProcedure();
|
|
626
|
+
this._error(err);
|
|
627
|
+
this._parent?.send(createErrorActorEvent(this.id, err));
|
|
628
|
+
return this;
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
// TODO: this notifies all subscribers but usually this is redundant
|
|
633
|
+
// there is no real change happening here
|
|
634
|
+
// we need to rethink if this needs to be refactored
|
|
635
|
+
this.update(this._state, initEvent);
|
|
636
|
+
if (this.options.devTools) {
|
|
637
|
+
this.attachDevTools();
|
|
638
|
+
}
|
|
639
|
+
this.mailbox.start();
|
|
640
|
+
return this;
|
|
641
|
+
}
|
|
642
|
+
_process(event) {
|
|
643
|
+
// TODO: reexamine what happens when an action (or a guard or smth) throws
|
|
644
|
+
let nextState;
|
|
645
|
+
let caughtError;
|
|
646
|
+
try {
|
|
647
|
+
nextState = this.logic.transition(this._state, event, this._actorScope);
|
|
648
|
+
} catch (err) {
|
|
649
|
+
// we wrap it in a box so we can rethrow it later even if falsy value gets caught here
|
|
650
|
+
caughtError = {
|
|
651
|
+
err
|
|
652
|
+
};
|
|
653
|
+
}
|
|
654
|
+
if (caughtError) {
|
|
655
|
+
const {
|
|
656
|
+
err
|
|
657
|
+
} = caughtError;
|
|
658
|
+
this._stopProcedure();
|
|
659
|
+
this._error(err);
|
|
660
|
+
this._parent?.send(createErrorActorEvent(this.id, err));
|
|
661
|
+
return;
|
|
662
|
+
}
|
|
663
|
+
this.update(nextState, event);
|
|
664
|
+
if (event.type === XSTATE_STOP) {
|
|
665
|
+
this._stopProcedure();
|
|
666
|
+
this._complete();
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
_stop() {
|
|
670
|
+
if (this._processingStatus === ProcessingStatus.Stopped) {
|
|
671
|
+
return this;
|
|
672
|
+
}
|
|
673
|
+
this.mailbox.clear();
|
|
674
|
+
if (this._processingStatus === ProcessingStatus.NotStarted) {
|
|
675
|
+
this._processingStatus = ProcessingStatus.Stopped;
|
|
676
|
+
return this;
|
|
677
|
+
}
|
|
678
|
+
this.mailbox.enqueue({
|
|
679
|
+
type: XSTATE_STOP
|
|
680
|
+
});
|
|
681
|
+
return this;
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
/**
|
|
685
|
+
* Stops the Actor and unsubscribe all listeners.
|
|
686
|
+
*/
|
|
687
|
+
stop() {
|
|
688
|
+
if (this._parent) {
|
|
689
|
+
throw new Error('A non-root actor cannot be stopped directly.');
|
|
690
|
+
}
|
|
691
|
+
return this._stop();
|
|
692
|
+
}
|
|
693
|
+
_complete() {
|
|
694
|
+
for (const observer of this.observers) {
|
|
695
|
+
try {
|
|
696
|
+
observer.complete?.();
|
|
697
|
+
} catch (err) {
|
|
698
|
+
reportUnhandledError(err);
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
this.observers.clear();
|
|
702
|
+
}
|
|
703
|
+
_error(err) {
|
|
704
|
+
if (!this.observers.size) {
|
|
705
|
+
if (!this._parent) {
|
|
706
|
+
reportUnhandledError(err);
|
|
707
|
+
}
|
|
708
|
+
return;
|
|
709
|
+
}
|
|
710
|
+
let reportError = false;
|
|
711
|
+
for (const observer of this.observers) {
|
|
712
|
+
const errorListener = observer.error;
|
|
713
|
+
reportError ||= !errorListener;
|
|
714
|
+
try {
|
|
715
|
+
errorListener?.(err);
|
|
716
|
+
} catch (err2) {
|
|
717
|
+
reportUnhandledError(err2);
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
this.observers.clear();
|
|
721
|
+
if (reportError) {
|
|
722
|
+
reportUnhandledError(err);
|
|
723
|
+
}
|
|
724
|
+
}
|
|
725
|
+
_stopProcedure() {
|
|
726
|
+
if (this._processingStatus !== ProcessingStatus.Running) {
|
|
727
|
+
// Actor already stopped; do nothing
|
|
728
|
+
return this;
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
// Cancel all delayed events
|
|
732
|
+
for (const key of Object.keys(this.delayedEventsMap)) {
|
|
733
|
+
this.clock.clearTimeout(this.delayedEventsMap[key]);
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
// TODO: mailbox.reset
|
|
737
|
+
this.mailbox.clear();
|
|
738
|
+
// TODO: after `stop` we must prepare ourselves for receiving events again
|
|
739
|
+
// events sent *after* stop signal must be queued
|
|
740
|
+
// it seems like this should be the common behavior for all of our consumers
|
|
741
|
+
// so perhaps this should be unified somehow for all of them
|
|
742
|
+
this.mailbox = new Mailbox(this._process.bind(this));
|
|
743
|
+
this._processingStatus = ProcessingStatus.Stopped;
|
|
744
|
+
this.system._unregister(this);
|
|
745
|
+
return this;
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
/**
|
|
749
|
+
* @internal
|
|
750
|
+
*/
|
|
751
|
+
_send(event) {
|
|
752
|
+
if (this._processingStatus === ProcessingStatus.Stopped) {
|
|
753
|
+
// do nothing
|
|
754
|
+
{
|
|
755
|
+
const eventString = JSON.stringify(event);
|
|
756
|
+
console.warn(`Event "${event.type}" was sent to stopped actor "${this.id} (${this.sessionId})". This actor has already reached its final state, and will not transition.\nEvent: ${eventString}`);
|
|
757
|
+
}
|
|
758
|
+
return;
|
|
759
|
+
}
|
|
760
|
+
this.mailbox.enqueue(event);
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
/**
|
|
764
|
+
* Sends an event to the running Actor to trigger a transition.
|
|
765
|
+
*
|
|
766
|
+
* @param event The event to send
|
|
767
|
+
*/
|
|
768
|
+
send(event) {
|
|
769
|
+
if (typeof event === 'string') {
|
|
770
|
+
throw new Error(`Only event objects may be sent to actors; use .send({ type: "${event}" }) instead`);
|
|
771
|
+
}
|
|
772
|
+
this.system._relay(undefined, this, event);
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
/**
|
|
776
|
+
* TODO: figure out a way to do this within the machine
|
|
777
|
+
* @internal
|
|
778
|
+
*/
|
|
779
|
+
delaySend(params) {
|
|
780
|
+
const {
|
|
781
|
+
event,
|
|
782
|
+
id,
|
|
783
|
+
delay
|
|
784
|
+
} = params;
|
|
785
|
+
const timerId = this.clock.setTimeout(() => {
|
|
786
|
+
this.system._relay(this, params.to ?? this, event);
|
|
787
|
+
}, delay);
|
|
788
|
+
|
|
789
|
+
// TODO: consider the rehydration story here
|
|
790
|
+
if (id) {
|
|
791
|
+
this.delayedEventsMap[id] = timerId;
|
|
792
|
+
}
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
/**
|
|
796
|
+
* TODO: figure out a way to do this within the machine
|
|
797
|
+
* @internal
|
|
798
|
+
*/
|
|
799
|
+
cancel(sendId) {
|
|
800
|
+
this.clock.clearTimeout(this.delayedEventsMap[sendId]);
|
|
801
|
+
delete this.delayedEventsMap[sendId];
|
|
802
|
+
}
|
|
803
|
+
attachDevTools() {
|
|
804
|
+
const {
|
|
805
|
+
devTools
|
|
806
|
+
} = this.options;
|
|
807
|
+
if (devTools) {
|
|
808
|
+
const resolvedDevToolsAdapter = typeof devTools === 'function' ? devTools : devToolsAdapter;
|
|
809
|
+
resolvedDevToolsAdapter(this);
|
|
810
|
+
}
|
|
811
|
+
}
|
|
812
|
+
toJSON() {
|
|
813
|
+
return {
|
|
814
|
+
xstate$$type: $$ACTOR_TYPE,
|
|
815
|
+
id: this.id
|
|
816
|
+
};
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
/**
|
|
820
|
+
* Obtain the internal state of the actor, which can be persisted.
|
|
821
|
+
*
|
|
822
|
+
* @remarks
|
|
823
|
+
* The internal state can be persisted from any actor, not only machines.
|
|
824
|
+
*
|
|
825
|
+
* 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.
|
|
826
|
+
*
|
|
827
|
+
* Can be restored with {@link ActorOptions.state}
|
|
828
|
+
*
|
|
829
|
+
* @see https://stately.ai/docs/persistence
|
|
830
|
+
*/
|
|
831
|
+
|
|
832
|
+
getPersistedState(options) {
|
|
833
|
+
return this.logic.getPersistedState(this._state, options);
|
|
834
|
+
}
|
|
835
|
+
[symbolObservable]() {
|
|
836
|
+
return this;
|
|
837
|
+
}
|
|
838
|
+
|
|
839
|
+
/**
|
|
840
|
+
* Read an actor’s snapshot synchronously.
|
|
841
|
+
*
|
|
842
|
+
* @remarks
|
|
843
|
+
* The snapshot represent an actor's last emitted value.
|
|
844
|
+
*
|
|
845
|
+
* When an actor receives an event, its internal state may change.
|
|
846
|
+
* An actor may emit a snapshot when a state transition occurs.
|
|
847
|
+
*
|
|
848
|
+
* Note that some actors, such as callback actors generated with `fromCallback`, will not emit snapshots.
|
|
849
|
+
*
|
|
850
|
+
* @see {@link Actor.subscribe} to subscribe to an actor’s snapshot values.
|
|
851
|
+
* @see {@link Actor.getPersistedState} to persist the internal state of an actor (which is more than just a snapshot).
|
|
852
|
+
*/
|
|
853
|
+
getSnapshot() {
|
|
854
|
+
return this._state;
|
|
855
|
+
}
|
|
15
856
|
}
|
|
16
857
|
|
|
858
|
+
/**
|
|
859
|
+
* Creates a new `ActorRef` instance for the given machine with the provided options, if any.
|
|
860
|
+
*
|
|
861
|
+
* @param machine The machine to create an actor from
|
|
862
|
+
* @param options `ActorRef` options
|
|
863
|
+
*/
|
|
864
|
+
|
|
865
|
+
function createActor(logic, options) {
|
|
866
|
+
const interpreter = new Actor(logic, options);
|
|
867
|
+
return interpreter;
|
|
868
|
+
}
|
|
869
|
+
|
|
870
|
+
/**
|
|
871
|
+
* Creates a new Interpreter instance for the given machine with the provided options, if any.
|
|
872
|
+
*
|
|
873
|
+
* @deprecated Use `createActor` instead
|
|
874
|
+
*/
|
|
875
|
+
const interpret = createActor;
|
|
876
|
+
|
|
877
|
+
/**
|
|
878
|
+
* @deprecated Use `Actor` instead.
|
|
879
|
+
*/
|
|
880
|
+
|
|
17
881
|
function resolveCancel(_, state, actionArgs, actionParams, {
|
|
18
882
|
sendId
|
|
19
883
|
}) {
|
|
@@ -58,6 +922,7 @@ function resolveSpawn(actorScope, state, actionArgs, _actionParams, {
|
|
|
58
922
|
id: resolvedId,
|
|
59
923
|
src,
|
|
60
924
|
parent: actorScope?.self,
|
|
925
|
+
syncSnapshot,
|
|
61
926
|
systemId,
|
|
62
927
|
input: typeof input === 'function' ? input({
|
|
63
928
|
context: state.context,
|
|
@@ -65,19 +930,6 @@ function resolveSpawn(actorScope, state, actionArgs, _actionParams, {
|
|
|
65
930
|
self: actorScope?.self
|
|
66
931
|
}) : input
|
|
67
932
|
});
|
|
68
|
-
if (syncSnapshot) {
|
|
69
|
-
actorRef.subscribe({
|
|
70
|
-
next: snapshot => {
|
|
71
|
-
if (snapshot.status === 'active') {
|
|
72
|
-
actorScope.self.send({
|
|
73
|
-
type: `xstate.snapshot.${id}`,
|
|
74
|
-
snapshot
|
|
75
|
-
});
|
|
76
|
-
}
|
|
77
|
-
},
|
|
78
|
-
error: () => {}
|
|
79
|
-
});
|
|
80
|
-
}
|
|
81
933
|
}
|
|
82
934
|
if (!actorRef) {
|
|
83
935
|
console.warn(`Actor type '${src}' not found in machine '${actorScope.id}'.`);
|
|
@@ -196,7 +1048,7 @@ function checkStateIn(state, _, {
|
|
|
196
1048
|
}) {
|
|
197
1049
|
if (typeof stateValue === 'string' && isStateId(stateValue)) {
|
|
198
1050
|
const target = state.machine.getStateNodeById(stateValue);
|
|
199
|
-
return state.
|
|
1051
|
+
return state._nodes.some(sn => sn === target);
|
|
200
1052
|
}
|
|
201
1053
|
return state.matches(stateValue);
|
|
202
1054
|
}
|
|
@@ -315,26 +1167,25 @@ function getProperAncestors(stateNode, toStateNode) {
|
|
|
315
1167
|
}
|
|
316
1168
|
return ancestors;
|
|
317
1169
|
}
|
|
318
|
-
function
|
|
319
|
-
const
|
|
320
|
-
const
|
|
321
|
-
const adjList = getAdjList(configurationSet);
|
|
1170
|
+
function getAllStateNodes(stateNodes) {
|
|
1171
|
+
const nodeSet = new Set(stateNodes);
|
|
1172
|
+
const adjList = getAdjList(nodeSet);
|
|
322
1173
|
|
|
323
1174
|
// add descendants
|
|
324
|
-
for (const s of
|
|
1175
|
+
for (const s of nodeSet) {
|
|
325
1176
|
// if previously active, add existing child nodes
|
|
326
1177
|
if (s.type === 'compound' && (!adjList.get(s) || !adjList.get(s).length)) {
|
|
327
|
-
getInitialStateNodesWithTheirAncestors(s).forEach(sn =>
|
|
1178
|
+
getInitialStateNodesWithTheirAncestors(s).forEach(sn => nodeSet.add(sn));
|
|
328
1179
|
} else {
|
|
329
1180
|
if (s.type === 'parallel') {
|
|
330
1181
|
for (const child of getChildren(s)) {
|
|
331
1182
|
if (child.type === 'history') {
|
|
332
1183
|
continue;
|
|
333
1184
|
}
|
|
334
|
-
if (!
|
|
1185
|
+
if (!nodeSet.has(child)) {
|
|
335
1186
|
const initialStates = getInitialStateNodesWithTheirAncestors(child);
|
|
336
1187
|
for (const initialStateNode of initialStates) {
|
|
337
|
-
|
|
1188
|
+
nodeSet.add(initialStateNode);
|
|
338
1189
|
}
|
|
339
1190
|
}
|
|
340
1191
|
}
|
|
@@ -343,14 +1194,14 @@ function getConfiguration(stateNodes) {
|
|
|
343
1194
|
}
|
|
344
1195
|
|
|
345
1196
|
// add all ancestors
|
|
346
|
-
for (const s of
|
|
1197
|
+
for (const s of nodeSet) {
|
|
347
1198
|
let m = s.parent;
|
|
348
1199
|
while (m) {
|
|
349
|
-
|
|
1200
|
+
nodeSet.add(m);
|
|
350
1201
|
m = m.parent;
|
|
351
1202
|
}
|
|
352
1203
|
}
|
|
353
|
-
return
|
|
1204
|
+
return nodeSet;
|
|
354
1205
|
}
|
|
355
1206
|
function getValueFromAdj(baseNode, adjList) {
|
|
356
1207
|
const childStateNodes = adjList.get(baseNode);
|
|
@@ -374,9 +1225,9 @@ function getValueFromAdj(baseNode, adjList) {
|
|
|
374
1225
|
}
|
|
375
1226
|
return stateValue;
|
|
376
1227
|
}
|
|
377
|
-
function getAdjList(
|
|
1228
|
+
function getAdjList(stateNodes) {
|
|
378
1229
|
const adjList = new Map();
|
|
379
|
-
for (const s of
|
|
1230
|
+
for (const s of stateNodes) {
|
|
380
1231
|
if (!adjList.has(s)) {
|
|
381
1232
|
adjList.set(s, []);
|
|
382
1233
|
}
|
|
@@ -389,16 +1240,16 @@ function getAdjList(configuration) {
|
|
|
389
1240
|
}
|
|
390
1241
|
return adjList;
|
|
391
1242
|
}
|
|
392
|
-
function getStateValue(rootNode,
|
|
393
|
-
const config =
|
|
1243
|
+
function getStateValue(rootNode, stateNodes) {
|
|
1244
|
+
const config = getAllStateNodes(stateNodes);
|
|
394
1245
|
return getValueFromAdj(rootNode, getAdjList(config));
|
|
395
1246
|
}
|
|
396
|
-
function isInFinalState(
|
|
1247
|
+
function isInFinalState(stateNodeSet, stateNode) {
|
|
397
1248
|
if (stateNode.type === 'compound') {
|
|
398
|
-
return getChildren(stateNode).some(s => s.type === 'final' &&
|
|
1249
|
+
return getChildren(stateNode).some(s => s.type === 'final' && stateNodeSet.has(s));
|
|
399
1250
|
}
|
|
400
1251
|
if (stateNode.type === 'parallel') {
|
|
401
|
-
return getChildren(stateNode).every(sn => isInFinalState(
|
|
1252
|
+
return getChildren(stateNode).every(sn => isInFinalState(stateNodeSet, sn));
|
|
402
1253
|
}
|
|
403
1254
|
return stateNode.type === 'final';
|
|
404
1255
|
}
|
|
@@ -769,13 +1620,13 @@ function hasIntersection(s1, s2) {
|
|
|
769
1620
|
}
|
|
770
1621
|
return false;
|
|
771
1622
|
}
|
|
772
|
-
function removeConflictingTransitions(enabledTransitions,
|
|
1623
|
+
function removeConflictingTransitions(enabledTransitions, stateNodeSet, historyValue) {
|
|
773
1624
|
const filteredTransitions = new Set();
|
|
774
1625
|
for (const t1 of enabledTransitions) {
|
|
775
1626
|
let t1Preempted = false;
|
|
776
1627
|
const transitionsToRemove = new Set();
|
|
777
1628
|
for (const t2 of filteredTransitions) {
|
|
778
|
-
if (hasIntersection(computeExitSet([t1],
|
|
1629
|
+
if (hasIntersection(computeExitSet([t1], stateNodeSet, historyValue), computeExitSet([t2], stateNodeSet, historyValue))) {
|
|
779
1630
|
if (isDescendant(t1.source, t2.source)) {
|
|
780
1631
|
transitionsToRemove.add(t2);
|
|
781
1632
|
} else {
|
|
@@ -842,7 +1693,7 @@ function getTransitionDomain(transition, historyValue) {
|
|
|
842
1693
|
}
|
|
843
1694
|
return transition.source.machine.root;
|
|
844
1695
|
}
|
|
845
|
-
function computeExitSet(transitions,
|
|
1696
|
+
function computeExitSet(transitions, stateNodeSet, historyValue) {
|
|
846
1697
|
const statesToExit = new Set();
|
|
847
1698
|
for (const t of transitions) {
|
|
848
1699
|
if (t.target?.length) {
|
|
@@ -850,7 +1701,7 @@ function computeExitSet(transitions, configuration, historyValue) {
|
|
|
850
1701
|
if (t.reenter && t.source === domain) {
|
|
851
1702
|
statesToExit.add(domain);
|
|
852
1703
|
}
|
|
853
|
-
for (const stateNode of
|
|
1704
|
+
for (const stateNode of stateNodeSet) {
|
|
854
1705
|
if (isDescendant(stateNode, domain)) {
|
|
855
1706
|
statesToExit.add(stateNode);
|
|
856
1707
|
}
|
|
@@ -859,12 +1710,12 @@ function computeExitSet(transitions, configuration, historyValue) {
|
|
|
859
1710
|
}
|
|
860
1711
|
return [...statesToExit];
|
|
861
1712
|
}
|
|
862
|
-
function
|
|
863
|
-
if (
|
|
1713
|
+
function areStateNodeCollectionsEqual(prevStateNodes, nextStateNodeSet) {
|
|
1714
|
+
if (prevStateNodes.length !== nextStateNodeSet.size) {
|
|
864
1715
|
return false;
|
|
865
1716
|
}
|
|
866
|
-
for (const node of
|
|
867
|
-
if (!
|
|
1717
|
+
for (const node of prevStateNodes) {
|
|
1718
|
+
if (!nextStateNodeSet.has(node)) {
|
|
868
1719
|
return false;
|
|
869
1720
|
}
|
|
870
1721
|
}
|
|
@@ -878,31 +1729,31 @@ function microstep(transitions, currentState, actorScope, event, isInitial, inte
|
|
|
878
1729
|
if (!transitions.length) {
|
|
879
1730
|
return currentState;
|
|
880
1731
|
}
|
|
881
|
-
const
|
|
1732
|
+
const mutStateNodeSet = new Set(currentState._nodes);
|
|
882
1733
|
let historyValue = currentState.historyValue;
|
|
883
|
-
const filteredTransitions = removeConflictingTransitions(transitions,
|
|
1734
|
+
const filteredTransitions = removeConflictingTransitions(transitions, mutStateNodeSet, historyValue);
|
|
884
1735
|
let nextState = currentState;
|
|
885
1736
|
|
|
886
1737
|
// Exit states
|
|
887
1738
|
if (!isInitial) {
|
|
888
|
-
[nextState, historyValue] = exitStates(nextState, event, actorScope, filteredTransitions,
|
|
1739
|
+
[nextState, historyValue] = exitStates(nextState, event, actorScope, filteredTransitions, mutStateNodeSet, historyValue, internalQueue);
|
|
889
1740
|
}
|
|
890
1741
|
|
|
891
1742
|
// Execute transition content
|
|
892
1743
|
nextState = resolveActionsAndContext(nextState, event, actorScope, filteredTransitions.flatMap(t => t.actions), internalQueue);
|
|
893
1744
|
|
|
894
1745
|
// Enter states
|
|
895
|
-
nextState = enterStates(nextState, event, actorScope, filteredTransitions,
|
|
896
|
-
const
|
|
1746
|
+
nextState = enterStates(nextState, event, actorScope, filteredTransitions, mutStateNodeSet, internalQueue, historyValue, isInitial);
|
|
1747
|
+
const nextStateNodes = [...mutStateNodeSet];
|
|
897
1748
|
if (nextState.status === 'done') {
|
|
898
|
-
nextState = resolveActionsAndContext(nextState, event, actorScope,
|
|
1749
|
+
nextState = resolveActionsAndContext(nextState, event, actorScope, nextStateNodes.sort((a, b) => b.order - a.order).flatMap(state => state.exit), internalQueue);
|
|
899
1750
|
}
|
|
900
1751
|
try {
|
|
901
|
-
if (historyValue === currentState.historyValue &&
|
|
1752
|
+
if (historyValue === currentState.historyValue && areStateNodeCollectionsEqual(currentState._nodes, mutStateNodeSet)) {
|
|
902
1753
|
return nextState;
|
|
903
1754
|
}
|
|
904
1755
|
return cloneMachineSnapshot(nextState, {
|
|
905
|
-
|
|
1756
|
+
_nodes: nextStateNodes,
|
|
906
1757
|
historyValue
|
|
907
1758
|
});
|
|
908
1759
|
} catch (e) {
|
|
@@ -918,7 +1769,7 @@ function getMachineOutput(state, event, actorScope, rootNode, rootCompletionNode
|
|
|
918
1769
|
const doneStateEvent = createDoneStateEvent(rootCompletionNode.id, rootCompletionNode.output && rootCompletionNode.parent ? resolveOutput(rootCompletionNode.output, state.context, event, actorScope.self) : undefined);
|
|
919
1770
|
return resolveOutput(rootNode.output, state.context, doneStateEvent, actorScope.self);
|
|
920
1771
|
}
|
|
921
|
-
function enterStates(currentState, event, actorScope, filteredTransitions,
|
|
1772
|
+
function enterStates(currentState, event, actorScope, filteredTransitions, mutStateNodeSet, internalQueue, historyValue, isInitial) {
|
|
922
1773
|
let nextState = currentState;
|
|
923
1774
|
const statesToEnter = new Set();
|
|
924
1775
|
// those are states that were directly targeted or indirectly targeted by the explicit target
|
|
@@ -933,7 +1784,7 @@ function enterStates(currentState, event, actorScope, filteredTransitions, mutCo
|
|
|
933
1784
|
}
|
|
934
1785
|
const completedNodes = new Set();
|
|
935
1786
|
for (const stateNodeToEnter of [...statesToEnter].sort((a, b) => a.order - b.order)) {
|
|
936
|
-
|
|
1787
|
+
mutStateNodeSet.add(stateNodeToEnter);
|
|
937
1788
|
const actions = [];
|
|
938
1789
|
|
|
939
1790
|
// Add entry actions
|
|
@@ -956,7 +1807,7 @@ function enterStates(currentState, event, actorScope, filteredTransitions, mutCo
|
|
|
956
1807
|
if (parent?.type === 'compound') {
|
|
957
1808
|
internalQueue.push(createDoneStateEvent(parent.id, stateNodeToEnter.output ? resolveOutput(stateNodeToEnter.output, nextState.context, event, actorScope.self) : undefined));
|
|
958
1809
|
}
|
|
959
|
-
while (ancestorMarker?.type === 'parallel' && !completedNodes.has(ancestorMarker) && isInFinalState(
|
|
1810
|
+
while (ancestorMarker?.type === 'parallel' && !completedNodes.has(ancestorMarker) && isInFinalState(mutStateNodeSet, ancestorMarker)) {
|
|
960
1811
|
completedNodes.add(ancestorMarker);
|
|
961
1812
|
internalQueue.push(createDoneStateEvent(ancestorMarker.id));
|
|
962
1813
|
rootCompletionNode = ancestorMarker;
|
|
@@ -1066,9 +1917,9 @@ function addAncestorStatesToEnter(statesToEnter, historyValue, statesForDefaultE
|
|
|
1066
1917
|
function addProperAncestorStatesToEnter(stateNode, toStateNode, statesToEnter, historyValue, statesForDefaultEntry) {
|
|
1067
1918
|
addAncestorStatesToEnter(statesToEnter, historyValue, statesForDefaultEntry, getProperAncestors(stateNode, toStateNode));
|
|
1068
1919
|
}
|
|
1069
|
-
function exitStates(currentState, event, actorScope, transitions,
|
|
1920
|
+
function exitStates(currentState, event, actorScope, transitions, mutStateNodeSet, historyValue, internalQueue) {
|
|
1070
1921
|
let nextState = currentState;
|
|
1071
|
-
const statesToExit = computeExitSet(transitions,
|
|
1922
|
+
const statesToExit = computeExitSet(transitions, mutStateNodeSet, historyValue);
|
|
1072
1923
|
statesToExit.sort((a, b) => b.order - a.order);
|
|
1073
1924
|
let changedHistory;
|
|
1074
1925
|
|
|
@@ -1086,12 +1937,12 @@ function exitStates(currentState, event, actorScope, transitions, mutConfigurati
|
|
|
1086
1937
|
changedHistory ??= {
|
|
1087
1938
|
...historyValue
|
|
1088
1939
|
};
|
|
1089
|
-
changedHistory[historyNode.id] = Array.from(
|
|
1940
|
+
changedHistory[historyNode.id] = Array.from(mutStateNodeSet).filter(predicate);
|
|
1090
1941
|
}
|
|
1091
1942
|
}
|
|
1092
1943
|
for (const s of statesToExit) {
|
|
1093
1944
|
nextState = resolveActionsAndContext(nextState, event, actorScope, [...s.exit, ...s.invoke.map(def => stop(def.id))], internalQueue);
|
|
1094
|
-
|
|
1945
|
+
mutStateNodeSet.delete(s);
|
|
1095
1946
|
}
|
|
1096
1947
|
return [nextState, changedHistory || historyValue];
|
|
1097
1948
|
}
|
|
@@ -1223,7 +2074,7 @@ function selectTransitions(event, nextState) {
|
|
|
1223
2074
|
}
|
|
1224
2075
|
function selectEventlessTransitions(nextState, event) {
|
|
1225
2076
|
const enabledTransitionSet = new Set();
|
|
1226
|
-
const atomicStates = nextState.
|
|
2077
|
+
const atomicStates = nextState._nodes.filter(isAtomicStateNode);
|
|
1227
2078
|
for (const stateNode of atomicStates) {
|
|
1228
2079
|
loop: for (const s of [stateNode].concat(getProperAncestors(stateNode, undefined))) {
|
|
1229
2080
|
if (!s.always) {
|
|
@@ -1237,7 +2088,7 @@ function selectEventlessTransitions(nextState, event) {
|
|
|
1237
2088
|
}
|
|
1238
2089
|
}
|
|
1239
2090
|
}
|
|
1240
|
-
return removeConflictingTransitions(Array.from(enabledTransitionSet), new Set(nextState.
|
|
2091
|
+
return removeConflictingTransitions(Array.from(enabledTransitionSet), new Set(nextState._nodes), nextState.historyValue);
|
|
1241
2092
|
}
|
|
1242
2093
|
|
|
1243
2094
|
/**
|
|
@@ -1246,10 +2097,13 @@ function selectEventlessTransitions(nextState, event) {
|
|
|
1246
2097
|
* @param stateValue The partial state value to resolve.
|
|
1247
2098
|
*/
|
|
1248
2099
|
function resolveStateValue(rootNode, stateValue) {
|
|
1249
|
-
const
|
|
1250
|
-
return getStateValue(rootNode, [...
|
|
2100
|
+
const allStateNodes = getAllStateNodes(getStateNodes(rootNode, stateValue));
|
|
2101
|
+
return getStateValue(rootNode, [...allStateNodes]);
|
|
1251
2102
|
}
|
|
1252
2103
|
|
|
2104
|
+
function isMachineSnapshot(value) {
|
|
2105
|
+
return !!value && typeof value === 'object' && 'machine' in value && 'value' in value;
|
|
2106
|
+
}
|
|
1253
2107
|
const machineSnapshotMatches = function matches(testValue) {
|
|
1254
2108
|
return matchesState(testValue, this.value);
|
|
1255
2109
|
};
|
|
@@ -1267,10 +2121,11 @@ const machineSnapshotCan = function can(event) {
|
|
|
1267
2121
|
};
|
|
1268
2122
|
const machineSnapshotToJSON = function toJSON() {
|
|
1269
2123
|
const {
|
|
1270
|
-
|
|
2124
|
+
_nodes: nodes,
|
|
1271
2125
|
tags,
|
|
1272
2126
|
machine,
|
|
1273
|
-
|
|
2127
|
+
getNextEvents,
|
|
2128
|
+
getMeta,
|
|
1274
2129
|
toJSON,
|
|
1275
2130
|
can,
|
|
1276
2131
|
hasTag,
|
|
@@ -1282,13 +2137,11 @@ const machineSnapshotToJSON = function toJSON() {
|
|
|
1282
2137
|
tags: Array.from(tags)
|
|
1283
2138
|
};
|
|
1284
2139
|
};
|
|
1285
|
-
const
|
|
1286
|
-
return
|
|
1287
|
-
return [...new Set(flatten([...this.configuration.map(sn => sn.ownEvents)]))];
|
|
1288
|
-
});
|
|
2140
|
+
const machineSnapshotGetNextEvents = function getNextEvents() {
|
|
2141
|
+
return [...new Set(flatten([...this._nodes.map(sn => sn.ownEvents)]))];
|
|
1289
2142
|
};
|
|
1290
|
-
const
|
|
1291
|
-
return this.
|
|
2143
|
+
const machineSnapshotGetMeta = function getMeta() {
|
|
2144
|
+
return this._nodes.reduce((acc, stateNode) => {
|
|
1292
2145
|
if (stateNode.meta !== undefined) {
|
|
1293
2146
|
acc[stateNode.id] = stateNode.meta;
|
|
1294
2147
|
}
|
|
@@ -1296,48 +2149,35 @@ const machineSnapshotMeta = function nextEvents() {
|
|
|
1296
2149
|
}, {});
|
|
1297
2150
|
};
|
|
1298
2151
|
function createMachineSnapshot(config, machine) {
|
|
1299
|
-
|
|
2152
|
+
return {
|
|
1300
2153
|
status: config.status,
|
|
1301
2154
|
output: config.output,
|
|
1302
2155
|
error: config.error,
|
|
1303
2156
|
machine,
|
|
1304
2157
|
context: config.context,
|
|
1305
|
-
|
|
1306
|
-
value: getStateValue(machine.root, config.
|
|
1307
|
-
tags: new Set(flatten(config.
|
|
2158
|
+
_nodes: config._nodes,
|
|
2159
|
+
value: getStateValue(machine.root, config._nodes),
|
|
2160
|
+
tags: new Set(flatten(config._nodes.map(sn => sn.tags))),
|
|
1308
2161
|
children: config.children,
|
|
1309
2162
|
historyValue: config.historyValue || {},
|
|
1310
2163
|
// this one is generic in the target and it's hard to create a matching non-generic source signature
|
|
1311
2164
|
matches: machineSnapshotMatches,
|
|
1312
2165
|
hasTag: machineSnapshotHasTag,
|
|
1313
2166
|
can: machineSnapshotCan,
|
|
2167
|
+
getNextEvents: machineSnapshotGetNextEvents,
|
|
2168
|
+
getMeta: machineSnapshotGetMeta,
|
|
1314
2169
|
toJSON: machineSnapshotToJSON
|
|
1315
2170
|
};
|
|
1316
|
-
Object.defineProperties(snapshot, {
|
|
1317
|
-
nextEvents: {
|
|
1318
|
-
get: machineSnapshotNextEvents,
|
|
1319
|
-
configurable: true,
|
|
1320
|
-
enumerable: true
|
|
1321
|
-
},
|
|
1322
|
-
meta: {
|
|
1323
|
-
get: machineSnapshotMeta,
|
|
1324
|
-
configurable: true,
|
|
1325
|
-
enumerable: true
|
|
1326
|
-
}
|
|
1327
|
-
});
|
|
1328
|
-
return snapshot;
|
|
1329
2171
|
}
|
|
1330
2172
|
function cloneMachineSnapshot(state, config = {}) {
|
|
1331
|
-
return createMachineSnapshot(
|
|
1332
|
-
// TODO: it's wasteful that this spread triggers getters
|
|
1333
|
-
{
|
|
2173
|
+
return createMachineSnapshot({
|
|
1334
2174
|
...state,
|
|
1335
2175
|
...config
|
|
1336
2176
|
}, state.machine);
|
|
1337
2177
|
}
|
|
1338
2178
|
function getPersistedState(state, options) {
|
|
1339
2179
|
const {
|
|
1340
|
-
|
|
2180
|
+
_nodes: nodes,
|
|
1341
2181
|
tags,
|
|
1342
2182
|
machine,
|
|
1343
2183
|
children,
|
|
@@ -1345,8 +2185,9 @@ function getPersistedState(state, options) {
|
|
|
1345
2185
|
can,
|
|
1346
2186
|
hasTag,
|
|
1347
2187
|
matches,
|
|
2188
|
+
getNextEvents,
|
|
2189
|
+
getMeta,
|
|
1348
2190
|
toJSON,
|
|
1349
|
-
nextEvents,
|
|
1350
2191
|
...jsonValues
|
|
1351
2192
|
} = state;
|
|
1352
2193
|
const childrenJson = {};
|
|
@@ -1358,7 +2199,8 @@ function getPersistedState(state, options) {
|
|
|
1358
2199
|
childrenJson[id] = {
|
|
1359
2200
|
state: child.getPersistedState(options),
|
|
1360
2201
|
src: child.src,
|
|
1361
|
-
systemId: child._systemId
|
|
2202
|
+
systemId: child._systemId,
|
|
2203
|
+
syncSnapshot: child._syncSnapshot
|
|
1362
2204
|
};
|
|
1363
2205
|
}
|
|
1364
2206
|
const persisted = {
|
|
@@ -1450,4 +2292,4 @@ function raise(eventOrExpr, options) {
|
|
|
1450
2292
|
return raise;
|
|
1451
2293
|
}
|
|
1452
2294
|
|
|
1453
|
-
export {
|
|
2295
|
+
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 };
|