xstate 5.0.0-beta.43 → 5.0.0-beta.45
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/actions/dist/xstate-actions.cjs.js +2 -3
- package/actions/dist/xstate-actions.development.cjs.js +2 -3
- package/actions/dist/xstate-actions.development.esm.js +2 -3
- package/actions/dist/xstate-actions.esm.js +2 -3
- package/actions/dist/xstate-actions.umd.min.js +1 -1
- package/actions/dist/xstate-actions.umd.min.js.map +1 -1
- package/actors/dist/xstate-actors.cjs.js +98 -10
- package/actors/dist/xstate-actors.development.cjs.js +98 -10
- package/actors/dist/xstate-actors.development.esm.js +93 -5
- package/actors/dist/xstate-actors.esm.js +93 -5
- package/actors/dist/xstate-actors.umd.min.js +1 -1
- package/actors/dist/xstate-actors.umd.min.js.map +1 -1
- package/dist/declarations/src/State.d.ts +14 -18
- package/dist/declarations/src/StateMachine.d.ts +1 -1
- package/dist/declarations/src/actions/choose.d.ts +3 -3
- package/dist/declarations/src/actions/pure.d.ts +4 -4
- package/dist/declarations/src/actions/spawn.d.ts +11 -16
- package/dist/declarations/src/actors/observable.d.ts +39 -0
- package/dist/declarations/src/actors/transition.d.ts +53 -4
- package/dist/declarations/src/{Machine.d.ts → createMachine.d.ts} +1 -1
- package/dist/declarations/src/guards.d.ts +27 -5
- package/dist/declarations/src/index.d.ts +3 -2
- package/dist/declarations/src/interpreter.d.ts +1 -0
- package/dist/declarations/src/setup.d.ts +32 -0
- package/dist/declarations/src/spawn.d.ts +9 -13
- package/dist/declarations/src/stateUtils.d.ts +11 -11
- package/dist/declarations/src/types.d.ts +31 -29
- package/dist/declarations/src/utils.d.ts +1 -3
- package/dist/{raise-8dc8e1aa.esm.js → raise-2b5a4e4c.esm.js} +934 -103
- package/dist/{raise-f4ad5a87.development.esm.js → raise-90139fbc.development.esm.js} +945 -103
- package/dist/{raise-23dea0d7.development.cjs.js → raise-b3fb3c65.development.cjs.js} +999 -137
- package/dist/{raise-e0fe5c2d.cjs.js → raise-fabffc3d.cjs.js} +986 -135
- package/dist/{send-5d129d95.development.esm.js → send-24cc8018.development.esm.js} +4 -30
- package/dist/{send-84e2e742.esm.js → send-8e7e41e7.esm.js} +4 -30
- package/dist/{send-87bbaaab.cjs.js → send-c124176f.cjs.js} +13 -39
- package/dist/{send-0174c155.development.cjs.js → send-d0bc7eed.development.cjs.js} +13 -39
- package/dist/xstate.cjs.js +67 -35
- package/dist/xstate.cjs.mjs +2 -0
- package/dist/xstate.development.cjs.js +67 -35
- package/dist/xstate.development.cjs.mjs +2 -0
- package/dist/xstate.development.esm.js +42 -13
- package/dist/xstate.esm.js +42 -13
- package/dist/xstate.umd.min.js +1 -1
- package/dist/xstate.umd.min.js.map +1 -1
- package/guards/dist/xstate-guards.cjs.js +1 -2
- package/guards/dist/xstate-guards.development.cjs.js +1 -2
- package/guards/dist/xstate-guards.development.esm.js +1 -2
- package/guards/dist/xstate-guards.esm.js +1 -2
- package/guards/dist/xstate-guards.umd.min.js +1 -1
- package/guards/dist/xstate-guards.umd.min.js.map +1 -1
- package/package.json +1 -1
- package/dist/interpreter-36d5556e.cjs.js +0 -887
- package/dist/interpreter-4e8e2a0d.development.cjs.js +0 -898
- package/dist/interpreter-63c80754.esm.js +0 -857
- package/dist/interpreter-80eb3bec.development.esm.js +0 -868
|
@@ -1,21 +1,885 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
3
|
+
var dev_dist_xstateDev = require('../dev/dist/xstate-dev.development.cjs.js');
|
|
4
|
+
|
|
5
|
+
class Mailbox {
|
|
6
|
+
constructor(_process) {
|
|
7
|
+
this._process = _process;
|
|
8
|
+
this._active = false;
|
|
9
|
+
this._current = null;
|
|
10
|
+
this._last = null;
|
|
11
|
+
}
|
|
12
|
+
start() {
|
|
13
|
+
this._active = true;
|
|
14
|
+
this.flush();
|
|
15
|
+
}
|
|
16
|
+
clear() {
|
|
17
|
+
// we can't set _current to null because we might be currently processing
|
|
18
|
+
// and enqueue following clear shouldnt start processing the enqueued item immediately
|
|
19
|
+
if (this._current) {
|
|
20
|
+
this._current.next = null;
|
|
21
|
+
this._last = this._current;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
enqueue(event) {
|
|
25
|
+
const enqueued = {
|
|
26
|
+
value: event,
|
|
27
|
+
next: null
|
|
28
|
+
};
|
|
29
|
+
if (this._current) {
|
|
30
|
+
this._last.next = enqueued;
|
|
31
|
+
this._last = enqueued;
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
this._current = enqueued;
|
|
35
|
+
this._last = enqueued;
|
|
36
|
+
if (this._active) {
|
|
37
|
+
this.flush();
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
flush() {
|
|
41
|
+
while (this._current) {
|
|
42
|
+
// atm the given _process is responsible for implementing proper try/catch handling
|
|
43
|
+
// we assume here that this won't throw in a way that can affect this mailbox
|
|
44
|
+
const consumed = this._current;
|
|
45
|
+
this._process(consumed.value);
|
|
46
|
+
this._current = consumed.next;
|
|
47
|
+
}
|
|
48
|
+
this._last = null;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const STATE_DELIMITER = '.';
|
|
53
|
+
const TARGETLESS_KEY = '';
|
|
54
|
+
const NULL_EVENT = '';
|
|
55
|
+
const STATE_IDENTIFIER = '#';
|
|
56
|
+
const WILDCARD = '*';
|
|
57
|
+
const XSTATE_INIT = 'xstate.init';
|
|
58
|
+
const XSTATE_ERROR = 'xstate.error';
|
|
59
|
+
const XSTATE_STOP = 'xstate.stop';
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Returns an event that represents an implicit event that
|
|
63
|
+
* is sent after the specified `delay`.
|
|
64
|
+
*
|
|
65
|
+
* @param delayRef The delay in milliseconds
|
|
66
|
+
* @param id The state node ID where this event is handled
|
|
67
|
+
*/
|
|
68
|
+
function createAfterEvent(delayRef, id) {
|
|
69
|
+
const idSuffix = id ? `#${id}` : '';
|
|
70
|
+
return {
|
|
71
|
+
type: `xstate.after(${delayRef})${idSuffix}`
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Returns an event that represents that a final state node
|
|
77
|
+
* has been reached in the parent state node.
|
|
78
|
+
*
|
|
79
|
+
* @param id The final state node's parent state node `id`
|
|
80
|
+
* @param output The data to pass into the event
|
|
81
|
+
*/
|
|
82
|
+
function createDoneStateEvent(id, output) {
|
|
83
|
+
return {
|
|
84
|
+
type: `xstate.done.state.${id}`,
|
|
85
|
+
output
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Returns an event that represents that an invoked service has terminated.
|
|
91
|
+
*
|
|
92
|
+
* An invoked service is terminated when it has reached a top-level final state node,
|
|
93
|
+
* but not when it is canceled.
|
|
94
|
+
*
|
|
95
|
+
* @param invokeId The invoked service ID
|
|
96
|
+
* @param output The data to pass into the event
|
|
97
|
+
*/
|
|
98
|
+
function createDoneActorEvent(invokeId, output) {
|
|
99
|
+
return {
|
|
100
|
+
type: `xstate.done.actor.${invokeId}`,
|
|
101
|
+
output
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
function createErrorActorEvent(id, data) {
|
|
105
|
+
return {
|
|
106
|
+
type: `xstate.error.actor.${id}`,
|
|
107
|
+
data
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
function createInitEvent(input) {
|
|
111
|
+
return {
|
|
112
|
+
type: XSTATE_INIT,
|
|
113
|
+
input
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* This function makes sure that unhandled errors are thrown in a separate macrotask.
|
|
119
|
+
* It allows those errors to be detected by global error handlers and reported to bug tracking services
|
|
120
|
+
* without interrupting our own stack of execution.
|
|
121
|
+
*
|
|
122
|
+
* @param err error to be thrown
|
|
123
|
+
*/
|
|
124
|
+
function reportUnhandledError(err) {
|
|
125
|
+
setTimeout(() => {
|
|
126
|
+
throw err;
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const symbolObservable = (() => typeof Symbol === 'function' && Symbol.observable || '@@observable')();
|
|
131
|
+
|
|
132
|
+
let idCounter = 0;
|
|
133
|
+
function createSystem(rootActor) {
|
|
134
|
+
const children = new Map();
|
|
135
|
+
const keyedActors = new Map();
|
|
136
|
+
const reverseKeyedActors = new WeakMap();
|
|
137
|
+
const observers = new Set();
|
|
138
|
+
const system = {
|
|
139
|
+
_bookId: () => `x:${idCounter++}`,
|
|
140
|
+
_register: (sessionId, actorRef) => {
|
|
141
|
+
children.set(sessionId, actorRef);
|
|
142
|
+
return sessionId;
|
|
143
|
+
},
|
|
144
|
+
_unregister: actorRef => {
|
|
145
|
+
children.delete(actorRef.sessionId);
|
|
146
|
+
const systemId = reverseKeyedActors.get(actorRef);
|
|
147
|
+
if (systemId !== undefined) {
|
|
148
|
+
keyedActors.delete(systemId);
|
|
149
|
+
reverseKeyedActors.delete(actorRef);
|
|
150
|
+
}
|
|
151
|
+
},
|
|
152
|
+
get: systemId => {
|
|
153
|
+
return keyedActors.get(systemId);
|
|
154
|
+
},
|
|
155
|
+
_set: (systemId, actorRef) => {
|
|
156
|
+
const existing = keyedActors.get(systemId);
|
|
157
|
+
if (existing && existing !== actorRef) {
|
|
158
|
+
throw new Error(`Actor with system ID '${systemId}' already exists.`);
|
|
159
|
+
}
|
|
160
|
+
keyedActors.set(systemId, actorRef);
|
|
161
|
+
reverseKeyedActors.set(actorRef, systemId);
|
|
162
|
+
},
|
|
163
|
+
inspect: observer => {
|
|
164
|
+
observers.add(observer);
|
|
165
|
+
},
|
|
166
|
+
_sendInspectionEvent: event => {
|
|
167
|
+
const resolvedInspectionEvent = {
|
|
168
|
+
...event,
|
|
169
|
+
rootId: rootActor.sessionId
|
|
170
|
+
};
|
|
171
|
+
observers.forEach(observer => observer.next?.(resolvedInspectionEvent));
|
|
172
|
+
},
|
|
173
|
+
_relay: (source, target, event) => {
|
|
174
|
+
system._sendInspectionEvent({
|
|
175
|
+
type: '@xstate.event',
|
|
176
|
+
sourceRef: source,
|
|
177
|
+
actorRef: target,
|
|
178
|
+
event
|
|
179
|
+
});
|
|
180
|
+
target._send(event);
|
|
181
|
+
}
|
|
182
|
+
};
|
|
183
|
+
return system;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
function matchesState(parentStateId, childStateId) {
|
|
187
|
+
const parentStateValue = toStateValue(parentStateId);
|
|
188
|
+
const childStateValue = toStateValue(childStateId);
|
|
189
|
+
if (typeof childStateValue === 'string') {
|
|
190
|
+
if (typeof parentStateValue === 'string') {
|
|
191
|
+
return childStateValue === parentStateValue;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// Parent more specific than child
|
|
195
|
+
return false;
|
|
196
|
+
}
|
|
197
|
+
if (typeof parentStateValue === 'string') {
|
|
198
|
+
return parentStateValue in childStateValue;
|
|
199
|
+
}
|
|
200
|
+
return Object.keys(parentStateValue).every(key => {
|
|
201
|
+
if (!(key in childStateValue)) {
|
|
202
|
+
return false;
|
|
203
|
+
}
|
|
204
|
+
return matchesState(parentStateValue[key], childStateValue[key]);
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
function toStatePath(stateId) {
|
|
208
|
+
try {
|
|
209
|
+
if (isArray(stateId)) {
|
|
210
|
+
return stateId;
|
|
211
|
+
}
|
|
212
|
+
return stateId.split(STATE_DELIMITER);
|
|
213
|
+
} catch (e) {
|
|
214
|
+
throw new Error(`'${stateId}' is not a valid state path.`);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
function toStateValue(stateValue) {
|
|
218
|
+
if (isMachineSnapshot(stateValue)) {
|
|
219
|
+
return stateValue.value;
|
|
220
|
+
}
|
|
221
|
+
if (typeof stateValue !== 'string') {
|
|
222
|
+
return stateValue;
|
|
223
|
+
}
|
|
224
|
+
const statePath = toStatePath(stateValue);
|
|
225
|
+
return pathToStateValue(statePath);
|
|
226
|
+
}
|
|
227
|
+
function pathToStateValue(statePath) {
|
|
228
|
+
if (statePath.length === 1) {
|
|
229
|
+
return statePath[0];
|
|
230
|
+
}
|
|
231
|
+
const value = {};
|
|
232
|
+
let marker = value;
|
|
233
|
+
for (let i = 0; i < statePath.length - 1; i++) {
|
|
234
|
+
if (i === statePath.length - 2) {
|
|
235
|
+
marker[statePath[i]] = statePath[i + 1];
|
|
236
|
+
} else {
|
|
237
|
+
const previous = marker;
|
|
238
|
+
marker = {};
|
|
239
|
+
previous[statePath[i]] = marker;
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
return value;
|
|
243
|
+
}
|
|
244
|
+
function mapValues(collection, iteratee) {
|
|
245
|
+
const result = {};
|
|
246
|
+
const collectionKeys = Object.keys(collection);
|
|
247
|
+
for (let i = 0; i < collectionKeys.length; i++) {
|
|
248
|
+
const key = collectionKeys[i];
|
|
249
|
+
result[key] = iteratee(collection[key], key, collection, i);
|
|
250
|
+
}
|
|
251
|
+
return result;
|
|
252
|
+
}
|
|
253
|
+
function flatten(array) {
|
|
254
|
+
return [].concat(...array);
|
|
255
|
+
}
|
|
256
|
+
function toArrayStrict(value) {
|
|
257
|
+
if (isArray(value)) {
|
|
258
|
+
return value;
|
|
259
|
+
}
|
|
260
|
+
return [value];
|
|
261
|
+
}
|
|
262
|
+
function toArray(value) {
|
|
263
|
+
if (value === undefined) {
|
|
264
|
+
return [];
|
|
265
|
+
}
|
|
266
|
+
return toArrayStrict(value);
|
|
267
|
+
}
|
|
268
|
+
function resolveOutput(mapper, context, event, self) {
|
|
269
|
+
if (typeof mapper === 'function') {
|
|
270
|
+
return mapper({
|
|
271
|
+
context,
|
|
272
|
+
event,
|
|
273
|
+
self
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
if (!!mapper && typeof mapper === 'object' && Object.values(mapper).some(val => typeof val === 'function')) {
|
|
277
|
+
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('')}`);
|
|
278
|
+
}
|
|
279
|
+
return mapper;
|
|
280
|
+
}
|
|
281
|
+
function isArray(value) {
|
|
282
|
+
return Array.isArray(value);
|
|
283
|
+
}
|
|
284
|
+
function isErrorActorEvent(event) {
|
|
285
|
+
return event.type.startsWith('xstate.error.actor');
|
|
286
|
+
}
|
|
287
|
+
function toTransitionConfigArray(configLike) {
|
|
288
|
+
return toArrayStrict(configLike).map(transitionLike => {
|
|
289
|
+
if (typeof transitionLike === 'undefined' || typeof transitionLike === 'string') {
|
|
290
|
+
return {
|
|
291
|
+
target: transitionLike
|
|
292
|
+
};
|
|
293
|
+
}
|
|
294
|
+
return transitionLike;
|
|
295
|
+
});
|
|
296
|
+
}
|
|
297
|
+
function normalizeTarget(target) {
|
|
298
|
+
if (target === undefined || target === TARGETLESS_KEY) {
|
|
299
|
+
return undefined;
|
|
300
|
+
}
|
|
301
|
+
return toArray(target);
|
|
302
|
+
}
|
|
303
|
+
function toObserver(nextHandler, errorHandler, completionHandler) {
|
|
304
|
+
const isObserver = typeof nextHandler === 'object';
|
|
305
|
+
const self = isObserver ? nextHandler : undefined;
|
|
306
|
+
return {
|
|
307
|
+
next: (isObserver ? nextHandler.next : nextHandler)?.bind(self),
|
|
308
|
+
error: (isObserver ? nextHandler.error : errorHandler)?.bind(self),
|
|
309
|
+
complete: (isObserver ? nextHandler.complete : completionHandler)?.bind(self)
|
|
310
|
+
};
|
|
311
|
+
}
|
|
312
|
+
function createInvokeId(stateNodeId, index) {
|
|
313
|
+
return `${stateNodeId}[${index}]`;
|
|
314
|
+
}
|
|
315
|
+
function resolveReferencedActor(machine, src) {
|
|
316
|
+
if (src.startsWith('xstate#')) {
|
|
317
|
+
const [, indexStr] = src.match(/\[(\d+)\]$/);
|
|
318
|
+
const node = machine.getStateNodeById(src.slice(7, -(indexStr.length + 2)));
|
|
319
|
+
const invokeConfig = node.config.invoke;
|
|
320
|
+
return (Array.isArray(invokeConfig) ? invokeConfig[indexStr] : invokeConfig).src;
|
|
321
|
+
}
|
|
322
|
+
return machine.implementations.actors[src];
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
const $$ACTOR_TYPE = 1;
|
|
326
|
+
// those values are currently used by @xstate/react directly so it's important to keep the assigned values in sync
|
|
327
|
+
let ProcessingStatus = /*#__PURE__*/function (ProcessingStatus) {
|
|
328
|
+
ProcessingStatus[ProcessingStatus["NotStarted"] = 0] = "NotStarted";
|
|
329
|
+
ProcessingStatus[ProcessingStatus["Running"] = 1] = "Running";
|
|
330
|
+
ProcessingStatus[ProcessingStatus["Stopped"] = 2] = "Stopped";
|
|
331
|
+
return ProcessingStatus;
|
|
332
|
+
}({});
|
|
333
|
+
const defaultOptions = {
|
|
334
|
+
clock: {
|
|
335
|
+
setTimeout: (fn, ms) => {
|
|
336
|
+
return setTimeout(fn, ms);
|
|
337
|
+
},
|
|
338
|
+
clearTimeout: id => {
|
|
339
|
+
return clearTimeout(id);
|
|
340
|
+
}
|
|
341
|
+
},
|
|
342
|
+
logger: console.log.bind(console),
|
|
343
|
+
devTools: false
|
|
344
|
+
};
|
|
345
|
+
|
|
346
|
+
/**
|
|
347
|
+
* 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.
|
|
348
|
+
*/
|
|
349
|
+
class Actor {
|
|
350
|
+
/**
|
|
351
|
+
* The current internal state of the actor.
|
|
352
|
+
*/
|
|
353
|
+
|
|
354
|
+
/**
|
|
355
|
+
* The clock that is responsible for setting and clearing timeouts, such as delayed events and transitions.
|
|
356
|
+
*/
|
|
357
|
+
|
|
358
|
+
/**
|
|
359
|
+
* The unique identifier for this actor relative to its parent.
|
|
360
|
+
*/
|
|
361
|
+
|
|
362
|
+
/** @internal */
|
|
363
|
+
|
|
364
|
+
// Actor Ref
|
|
365
|
+
|
|
366
|
+
// TODO: add typings for system
|
|
367
|
+
|
|
368
|
+
/**
|
|
369
|
+
* The globally unique process ID for this invocation.
|
|
370
|
+
*/
|
|
371
|
+
|
|
372
|
+
/**
|
|
373
|
+
* The system to which this actor belongs.
|
|
374
|
+
*/
|
|
375
|
+
|
|
376
|
+
/**
|
|
377
|
+
* Creates a new actor instance for the given logic with the provided options, if any.
|
|
378
|
+
*
|
|
379
|
+
* @param logic The logic to create an actor from
|
|
380
|
+
* @param options Actor options
|
|
381
|
+
*/
|
|
382
|
+
constructor(logic, options) {
|
|
383
|
+
this.logic = logic;
|
|
384
|
+
this._state = void 0;
|
|
385
|
+
this.clock = void 0;
|
|
386
|
+
this.options = void 0;
|
|
387
|
+
this.id = void 0;
|
|
388
|
+
this.mailbox = new Mailbox(this._process.bind(this));
|
|
389
|
+
this.delayedEventsMap = {};
|
|
390
|
+
this.observers = new Set();
|
|
391
|
+
this.logger = void 0;
|
|
392
|
+
this._processingStatus = ProcessingStatus.NotStarted;
|
|
393
|
+
this._parent = void 0;
|
|
394
|
+
this._syncSnapshot = void 0;
|
|
395
|
+
this.ref = void 0;
|
|
396
|
+
this._actorScope = void 0;
|
|
397
|
+
this._systemId = void 0;
|
|
398
|
+
this.sessionId = void 0;
|
|
399
|
+
this.system = void 0;
|
|
400
|
+
this._doneEvent = void 0;
|
|
401
|
+
this.src = void 0;
|
|
402
|
+
this._deferred = [];
|
|
403
|
+
const resolvedOptions = {
|
|
404
|
+
...defaultOptions,
|
|
405
|
+
...options
|
|
406
|
+
};
|
|
407
|
+
const {
|
|
408
|
+
clock,
|
|
409
|
+
logger,
|
|
410
|
+
parent,
|
|
411
|
+
syncSnapshot,
|
|
412
|
+
id,
|
|
413
|
+
systemId,
|
|
414
|
+
inspect
|
|
415
|
+
} = resolvedOptions;
|
|
416
|
+
this.system = parent?.system ?? createSystem(this);
|
|
417
|
+
if (inspect && !parent) {
|
|
418
|
+
// Always inspect at the system-level
|
|
419
|
+
this.system.inspect(toObserver(inspect));
|
|
420
|
+
}
|
|
421
|
+
this.sessionId = this.system._bookId();
|
|
422
|
+
this.id = id ?? this.sessionId;
|
|
423
|
+
this.logger = logger;
|
|
424
|
+
this.clock = clock;
|
|
425
|
+
this._parent = parent;
|
|
426
|
+
this._syncSnapshot = syncSnapshot;
|
|
427
|
+
this.options = resolvedOptions;
|
|
428
|
+
this.src = resolvedOptions.src ?? logic;
|
|
429
|
+
this.ref = this;
|
|
430
|
+
this._actorScope = {
|
|
431
|
+
self: this,
|
|
432
|
+
id: this.id,
|
|
433
|
+
sessionId: this.sessionId,
|
|
434
|
+
logger: this.logger,
|
|
435
|
+
defer: fn => {
|
|
436
|
+
this._deferred.push(fn);
|
|
437
|
+
},
|
|
438
|
+
system: this.system,
|
|
439
|
+
stopChild: child => {
|
|
440
|
+
if (child._parent !== this) {
|
|
441
|
+
throw new Error(`Cannot stop child actor ${child.id} of ${this.id} because it is not a child`);
|
|
442
|
+
}
|
|
443
|
+
child._stop();
|
|
444
|
+
}
|
|
445
|
+
};
|
|
446
|
+
|
|
447
|
+
// Ensure that the send method is bound to this Actor instance
|
|
448
|
+
// if destructured
|
|
449
|
+
this.send = this.send.bind(this);
|
|
450
|
+
this.system._sendInspectionEvent({
|
|
451
|
+
type: '@xstate.actor',
|
|
452
|
+
actorRef: this
|
|
453
|
+
});
|
|
454
|
+
if (systemId) {
|
|
455
|
+
this._systemId = systemId;
|
|
456
|
+
this.system._set(systemId, this);
|
|
457
|
+
}
|
|
458
|
+
this._initState(options?.state);
|
|
459
|
+
if (systemId && this._state.status !== 'active') {
|
|
460
|
+
this.system._unregister(this);
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
_initState(persistedState) {
|
|
464
|
+
this._state = persistedState ? this.logic.restoreState ? this.logic.restoreState(persistedState, this._actorScope) : persistedState : this.logic.getInitialState(this._actorScope, this.options?.input);
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
// array of functions to defer
|
|
468
|
+
|
|
469
|
+
update(snapshot, event) {
|
|
470
|
+
// Update state
|
|
471
|
+
this._state = snapshot;
|
|
472
|
+
|
|
473
|
+
// Execute deferred effects
|
|
474
|
+
let deferredFn;
|
|
475
|
+
while (deferredFn = this._deferred.shift()) {
|
|
476
|
+
deferredFn();
|
|
477
|
+
}
|
|
478
|
+
for (const observer of this.observers) {
|
|
479
|
+
try {
|
|
480
|
+
observer.next?.(snapshot);
|
|
481
|
+
} catch (err) {
|
|
482
|
+
reportUnhandledError(err);
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
switch (this._state.status) {
|
|
486
|
+
case 'done':
|
|
487
|
+
this._stopProcedure();
|
|
488
|
+
this._complete();
|
|
489
|
+
this._doneEvent = createDoneActorEvent(this.id, this._state.output);
|
|
490
|
+
if (this._parent) {
|
|
491
|
+
this.system._relay(this, this._parent, this._doneEvent);
|
|
492
|
+
}
|
|
493
|
+
break;
|
|
494
|
+
case 'error':
|
|
495
|
+
this._stopProcedure();
|
|
496
|
+
this._error(this._state.error);
|
|
497
|
+
if (this._parent) {
|
|
498
|
+
this.system._relay(this, this._parent, createErrorActorEvent(this.id, this._state.error));
|
|
499
|
+
}
|
|
500
|
+
break;
|
|
501
|
+
}
|
|
502
|
+
this.system._sendInspectionEvent({
|
|
503
|
+
type: '@xstate.snapshot',
|
|
504
|
+
actorRef: this,
|
|
505
|
+
event,
|
|
506
|
+
snapshot
|
|
507
|
+
});
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
/**
|
|
511
|
+
* Subscribe an observer to an actor’s snapshot values.
|
|
512
|
+
*
|
|
513
|
+
* @remarks
|
|
514
|
+
* The observer will receive the actor’s snapshot value when it is emitted. The observer can be:
|
|
515
|
+
* - A plain function that receives the latest snapshot, or
|
|
516
|
+
* - An observer object whose `.next(snapshot)` method receives the latest snapshot
|
|
517
|
+
*
|
|
518
|
+
* @example
|
|
519
|
+
* ```ts
|
|
520
|
+
* // Observer as a plain function
|
|
521
|
+
* const subscription = actor.subscribe((snapshot) => {
|
|
522
|
+
* console.log(snapshot);
|
|
523
|
+
* });
|
|
524
|
+
* ```
|
|
525
|
+
*
|
|
526
|
+
* @example
|
|
527
|
+
* ```ts
|
|
528
|
+
* // Observer as an object
|
|
529
|
+
* const subscription = actor.subscribe({
|
|
530
|
+
* next(snapshot) {
|
|
531
|
+
* console.log(snapshot);
|
|
532
|
+
* },
|
|
533
|
+
* error(err) {
|
|
534
|
+
* // ...
|
|
535
|
+
* },
|
|
536
|
+
* complete() {
|
|
537
|
+
* // ...
|
|
538
|
+
* },
|
|
539
|
+
* });
|
|
540
|
+
* ```
|
|
541
|
+
*
|
|
542
|
+
* 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:
|
|
543
|
+
*
|
|
544
|
+
* @example
|
|
545
|
+
* ```ts
|
|
546
|
+
* const subscription = actor.subscribe((snapshot) => {
|
|
547
|
+
* // ...
|
|
548
|
+
* });
|
|
549
|
+
*
|
|
550
|
+
* // Unsubscribe the observer
|
|
551
|
+
* subscription.unsubscribe();
|
|
552
|
+
* ```
|
|
553
|
+
*
|
|
554
|
+
* When the actor is stopped, all of its observers will automatically be unsubscribed.
|
|
555
|
+
*
|
|
556
|
+
* @param observer - Either a plain function that receives the latest snapshot, or an observer object whose `.next(snapshot)` method receives the latest snapshot
|
|
557
|
+
*/
|
|
558
|
+
|
|
559
|
+
subscribe(nextListenerOrObserver, errorListener, completeListener) {
|
|
560
|
+
const observer = toObserver(nextListenerOrObserver, errorListener, completeListener);
|
|
561
|
+
if (this._processingStatus !== ProcessingStatus.Stopped) {
|
|
562
|
+
this.observers.add(observer);
|
|
563
|
+
} else {
|
|
564
|
+
try {
|
|
565
|
+
observer.complete?.();
|
|
566
|
+
} catch (err) {
|
|
567
|
+
reportUnhandledError(err);
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
return {
|
|
571
|
+
unsubscribe: () => {
|
|
572
|
+
this.observers.delete(observer);
|
|
573
|
+
}
|
|
11
574
|
};
|
|
12
|
-
cache.set(object, memoizedData);
|
|
13
|
-
} else if (!(key in memoizedData)) {
|
|
14
|
-
memoizedData[key] = fn();
|
|
15
575
|
}
|
|
16
|
-
|
|
576
|
+
|
|
577
|
+
/**
|
|
578
|
+
* Starts the Actor from the initial state
|
|
579
|
+
*/
|
|
580
|
+
start() {
|
|
581
|
+
if (this._processingStatus === ProcessingStatus.Running) {
|
|
582
|
+
// Do not restart the service if it is already started
|
|
583
|
+
return this;
|
|
584
|
+
}
|
|
585
|
+
if (this._syncSnapshot) {
|
|
586
|
+
this.subscribe({
|
|
587
|
+
next: snapshot => {
|
|
588
|
+
if (snapshot.status === 'active') {
|
|
589
|
+
this._parent.send({
|
|
590
|
+
type: `xstate.snapshot.${this.id}`,
|
|
591
|
+
snapshot
|
|
592
|
+
});
|
|
593
|
+
}
|
|
594
|
+
},
|
|
595
|
+
error: () => {}
|
|
596
|
+
});
|
|
597
|
+
}
|
|
598
|
+
this.system._register(this.sessionId, this);
|
|
599
|
+
if (this._systemId) {
|
|
600
|
+
this.system._set(this._systemId, this);
|
|
601
|
+
}
|
|
602
|
+
this._processingStatus = ProcessingStatus.Running;
|
|
603
|
+
|
|
604
|
+
// TODO: this isn't correct when rehydrating
|
|
605
|
+
const initEvent = createInitEvent(this.options.input);
|
|
606
|
+
this.system._sendInspectionEvent({
|
|
607
|
+
type: '@xstate.event',
|
|
608
|
+
sourceRef: this._parent,
|
|
609
|
+
actorRef: this,
|
|
610
|
+
event: initEvent
|
|
611
|
+
});
|
|
612
|
+
const status = this._state.status;
|
|
613
|
+
switch (status) {
|
|
614
|
+
case 'done':
|
|
615
|
+
// a state machine can be "done" upon initialization (it could reach a final state using initial microsteps)
|
|
616
|
+
// we still need to complete observers, flush deferreds etc
|
|
617
|
+
this.update(this._state, initEvent);
|
|
618
|
+
// fallthrough
|
|
619
|
+
case 'error':
|
|
620
|
+
// TODO: rethink cleanup of observers, mailbox, etc
|
|
621
|
+
return this;
|
|
622
|
+
}
|
|
623
|
+
if (this.logic.start) {
|
|
624
|
+
try {
|
|
625
|
+
this.logic.start(this._state, this._actorScope);
|
|
626
|
+
} catch (err) {
|
|
627
|
+
this._stopProcedure();
|
|
628
|
+
this._error(err);
|
|
629
|
+
this._parent?.send(createErrorActorEvent(this.id, err));
|
|
630
|
+
return this;
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
// TODO: this notifies all subscribers but usually this is redundant
|
|
635
|
+
// there is no real change happening here
|
|
636
|
+
// we need to rethink if this needs to be refactored
|
|
637
|
+
this.update(this._state, initEvent);
|
|
638
|
+
if (this.options.devTools) {
|
|
639
|
+
this.attachDevTools();
|
|
640
|
+
}
|
|
641
|
+
this.mailbox.start();
|
|
642
|
+
return this;
|
|
643
|
+
}
|
|
644
|
+
_process(event) {
|
|
645
|
+
// TODO: reexamine what happens when an action (or a guard or smth) throws
|
|
646
|
+
let nextState;
|
|
647
|
+
let caughtError;
|
|
648
|
+
try {
|
|
649
|
+
nextState = this.logic.transition(this._state, event, this._actorScope);
|
|
650
|
+
} catch (err) {
|
|
651
|
+
// we wrap it in a box so we can rethrow it later even if falsy value gets caught here
|
|
652
|
+
caughtError = {
|
|
653
|
+
err
|
|
654
|
+
};
|
|
655
|
+
}
|
|
656
|
+
if (caughtError) {
|
|
657
|
+
const {
|
|
658
|
+
err
|
|
659
|
+
} = caughtError;
|
|
660
|
+
this._stopProcedure();
|
|
661
|
+
this._error(err);
|
|
662
|
+
this._parent?.send(createErrorActorEvent(this.id, err));
|
|
663
|
+
return;
|
|
664
|
+
}
|
|
665
|
+
this.update(nextState, event);
|
|
666
|
+
if (event.type === XSTATE_STOP) {
|
|
667
|
+
this._stopProcedure();
|
|
668
|
+
this._complete();
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
_stop() {
|
|
672
|
+
if (this._processingStatus === ProcessingStatus.Stopped) {
|
|
673
|
+
return this;
|
|
674
|
+
}
|
|
675
|
+
this.mailbox.clear();
|
|
676
|
+
if (this._processingStatus === ProcessingStatus.NotStarted) {
|
|
677
|
+
this._processingStatus = ProcessingStatus.Stopped;
|
|
678
|
+
return this;
|
|
679
|
+
}
|
|
680
|
+
this.mailbox.enqueue({
|
|
681
|
+
type: XSTATE_STOP
|
|
682
|
+
});
|
|
683
|
+
return this;
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
/**
|
|
687
|
+
* Stops the Actor and unsubscribe all listeners.
|
|
688
|
+
*/
|
|
689
|
+
stop() {
|
|
690
|
+
if (this._parent) {
|
|
691
|
+
throw new Error('A non-root actor cannot be stopped directly.');
|
|
692
|
+
}
|
|
693
|
+
return this._stop();
|
|
694
|
+
}
|
|
695
|
+
_complete() {
|
|
696
|
+
for (const observer of this.observers) {
|
|
697
|
+
try {
|
|
698
|
+
observer.complete?.();
|
|
699
|
+
} catch (err) {
|
|
700
|
+
reportUnhandledError(err);
|
|
701
|
+
}
|
|
702
|
+
}
|
|
703
|
+
this.observers.clear();
|
|
704
|
+
}
|
|
705
|
+
_error(err) {
|
|
706
|
+
if (!this.observers.size) {
|
|
707
|
+
if (!this._parent) {
|
|
708
|
+
reportUnhandledError(err);
|
|
709
|
+
}
|
|
710
|
+
return;
|
|
711
|
+
}
|
|
712
|
+
let reportError = false;
|
|
713
|
+
for (const observer of this.observers) {
|
|
714
|
+
const errorListener = observer.error;
|
|
715
|
+
reportError ||= !errorListener;
|
|
716
|
+
try {
|
|
717
|
+
errorListener?.(err);
|
|
718
|
+
} catch (err2) {
|
|
719
|
+
reportUnhandledError(err2);
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
this.observers.clear();
|
|
723
|
+
if (reportError) {
|
|
724
|
+
reportUnhandledError(err);
|
|
725
|
+
}
|
|
726
|
+
}
|
|
727
|
+
_stopProcedure() {
|
|
728
|
+
if (this._processingStatus !== ProcessingStatus.Running) {
|
|
729
|
+
// Actor already stopped; do nothing
|
|
730
|
+
return this;
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
// Cancel all delayed events
|
|
734
|
+
for (const key of Object.keys(this.delayedEventsMap)) {
|
|
735
|
+
this.clock.clearTimeout(this.delayedEventsMap[key]);
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
// TODO: mailbox.reset
|
|
739
|
+
this.mailbox.clear();
|
|
740
|
+
// TODO: after `stop` we must prepare ourselves for receiving events again
|
|
741
|
+
// events sent *after* stop signal must be queued
|
|
742
|
+
// it seems like this should be the common behavior for all of our consumers
|
|
743
|
+
// so perhaps this should be unified somehow for all of them
|
|
744
|
+
this.mailbox = new Mailbox(this._process.bind(this));
|
|
745
|
+
this._processingStatus = ProcessingStatus.Stopped;
|
|
746
|
+
this.system._unregister(this);
|
|
747
|
+
return this;
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
/**
|
|
751
|
+
* @internal
|
|
752
|
+
*/
|
|
753
|
+
_send(event) {
|
|
754
|
+
if (this._processingStatus === ProcessingStatus.Stopped) {
|
|
755
|
+
// do nothing
|
|
756
|
+
{
|
|
757
|
+
const eventString = JSON.stringify(event);
|
|
758
|
+
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}`);
|
|
759
|
+
}
|
|
760
|
+
return;
|
|
761
|
+
}
|
|
762
|
+
this.mailbox.enqueue(event);
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
/**
|
|
766
|
+
* Sends an event to the running Actor to trigger a transition.
|
|
767
|
+
*
|
|
768
|
+
* @param event The event to send
|
|
769
|
+
*/
|
|
770
|
+
send(event) {
|
|
771
|
+
if (typeof event === 'string') {
|
|
772
|
+
throw new Error(`Only event objects may be sent to actors; use .send({ type: "${event}" }) instead`);
|
|
773
|
+
}
|
|
774
|
+
this.system._relay(undefined, this, event);
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
/**
|
|
778
|
+
* TODO: figure out a way to do this within the machine
|
|
779
|
+
* @internal
|
|
780
|
+
*/
|
|
781
|
+
delaySend(params) {
|
|
782
|
+
const {
|
|
783
|
+
event,
|
|
784
|
+
id,
|
|
785
|
+
delay
|
|
786
|
+
} = params;
|
|
787
|
+
const timerId = this.clock.setTimeout(() => {
|
|
788
|
+
this.system._relay(this, params.to ?? this, event);
|
|
789
|
+
}, delay);
|
|
790
|
+
|
|
791
|
+
// TODO: consider the rehydration story here
|
|
792
|
+
if (id) {
|
|
793
|
+
this.delayedEventsMap[id] = timerId;
|
|
794
|
+
}
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
/**
|
|
798
|
+
* TODO: figure out a way to do this within the machine
|
|
799
|
+
* @internal
|
|
800
|
+
*/
|
|
801
|
+
cancel(sendId) {
|
|
802
|
+
this.clock.clearTimeout(this.delayedEventsMap[sendId]);
|
|
803
|
+
delete this.delayedEventsMap[sendId];
|
|
804
|
+
}
|
|
805
|
+
attachDevTools() {
|
|
806
|
+
const {
|
|
807
|
+
devTools
|
|
808
|
+
} = this.options;
|
|
809
|
+
if (devTools) {
|
|
810
|
+
const resolvedDevToolsAdapter = typeof devTools === 'function' ? devTools : dev_dist_xstateDev.devToolsAdapter;
|
|
811
|
+
resolvedDevToolsAdapter(this);
|
|
812
|
+
}
|
|
813
|
+
}
|
|
814
|
+
toJSON() {
|
|
815
|
+
return {
|
|
816
|
+
xstate$$type: $$ACTOR_TYPE,
|
|
817
|
+
id: this.id
|
|
818
|
+
};
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
/**
|
|
822
|
+
* Obtain the internal state of the actor, which can be persisted.
|
|
823
|
+
*
|
|
824
|
+
* @remarks
|
|
825
|
+
* The internal state can be persisted from any actor, not only machines.
|
|
826
|
+
*
|
|
827
|
+
* 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.
|
|
828
|
+
*
|
|
829
|
+
* Can be restored with {@link ActorOptions.state}
|
|
830
|
+
*
|
|
831
|
+
* @see https://stately.ai/docs/persistence
|
|
832
|
+
*/
|
|
833
|
+
|
|
834
|
+
getPersistedState(options) {
|
|
835
|
+
return this.logic.getPersistedState(this._state, options);
|
|
836
|
+
}
|
|
837
|
+
[symbolObservable]() {
|
|
838
|
+
return this;
|
|
839
|
+
}
|
|
840
|
+
|
|
841
|
+
/**
|
|
842
|
+
* Read an actor’s snapshot synchronously.
|
|
843
|
+
*
|
|
844
|
+
* @remarks
|
|
845
|
+
* The snapshot represent an actor's last emitted value.
|
|
846
|
+
*
|
|
847
|
+
* When an actor receives an event, its internal state may change.
|
|
848
|
+
* An actor may emit a snapshot when a state transition occurs.
|
|
849
|
+
*
|
|
850
|
+
* Note that some actors, such as callback actors generated with `fromCallback`, will not emit snapshots.
|
|
851
|
+
*
|
|
852
|
+
* @see {@link Actor.subscribe} to subscribe to an actor’s snapshot values.
|
|
853
|
+
* @see {@link Actor.getPersistedState} to persist the internal state of an actor (which is more than just a snapshot).
|
|
854
|
+
*/
|
|
855
|
+
getSnapshot() {
|
|
856
|
+
return this._state;
|
|
857
|
+
}
|
|
17
858
|
}
|
|
18
859
|
|
|
860
|
+
/**
|
|
861
|
+
* Creates a new `ActorRef` instance for the given machine with the provided options, if any.
|
|
862
|
+
*
|
|
863
|
+
* @param machine The machine to create an actor from
|
|
864
|
+
* @param options `ActorRef` options
|
|
865
|
+
*/
|
|
866
|
+
|
|
867
|
+
function createActor(logic, options) {
|
|
868
|
+
const interpreter = new Actor(logic, options);
|
|
869
|
+
return interpreter;
|
|
870
|
+
}
|
|
871
|
+
|
|
872
|
+
/**
|
|
873
|
+
* Creates a new Interpreter instance for the given machine with the provided options, if any.
|
|
874
|
+
*
|
|
875
|
+
* @deprecated Use `createActor` instead
|
|
876
|
+
*/
|
|
877
|
+
const interpret = createActor;
|
|
878
|
+
|
|
879
|
+
/**
|
|
880
|
+
* @deprecated Use `Actor` instead.
|
|
881
|
+
*/
|
|
882
|
+
|
|
19
883
|
function resolveCancel(_, state, actionArgs, actionParams, {
|
|
20
884
|
sendId
|
|
21
885
|
}) {
|
|
@@ -52,14 +916,15 @@ function resolveSpawn(actorScope, state, actionArgs, _actionParams, {
|
|
|
52
916
|
input,
|
|
53
917
|
syncSnapshot
|
|
54
918
|
}) {
|
|
55
|
-
const logic = typeof src === 'string' ?
|
|
919
|
+
const logic = typeof src === 'string' ? resolveReferencedActor(state.machine, src) : src;
|
|
56
920
|
const resolvedId = typeof id === 'function' ? id(actionArgs) : id;
|
|
57
921
|
let actorRef;
|
|
58
922
|
if (logic) {
|
|
59
|
-
actorRef =
|
|
923
|
+
actorRef = createActor(logic, {
|
|
60
924
|
id: resolvedId,
|
|
61
925
|
src,
|
|
62
926
|
parent: actorScope?.self,
|
|
927
|
+
syncSnapshot,
|
|
63
928
|
systemId,
|
|
64
929
|
input: typeof input === 'function' ? input({
|
|
65
930
|
context: state.context,
|
|
@@ -67,19 +932,6 @@ function resolveSpawn(actorScope, state, actionArgs, _actionParams, {
|
|
|
67
932
|
self: actorScope?.self
|
|
68
933
|
}) : input
|
|
69
934
|
});
|
|
70
|
-
if (syncSnapshot) {
|
|
71
|
-
actorRef.subscribe({
|
|
72
|
-
next: snapshot => {
|
|
73
|
-
if (snapshot.status === 'active') {
|
|
74
|
-
actorScope.self.send({
|
|
75
|
-
type: `xstate.snapshot.${id}`,
|
|
76
|
-
snapshot
|
|
77
|
-
});
|
|
78
|
-
}
|
|
79
|
-
},
|
|
80
|
-
error: () => {}
|
|
81
|
-
});
|
|
82
|
-
}
|
|
83
935
|
}
|
|
84
936
|
if (!actorRef) {
|
|
85
937
|
console.warn(`Actor type '${src}' not found in machine '${actorScope.id}'.`);
|
|
@@ -102,13 +954,13 @@ function executeSpawn(actorScope, {
|
|
|
102
954
|
return;
|
|
103
955
|
}
|
|
104
956
|
actorScope.defer(() => {
|
|
105
|
-
if (actorRef._processingStatus ===
|
|
957
|
+
if (actorRef._processingStatus === ProcessingStatus.Stopped) {
|
|
106
958
|
return;
|
|
107
959
|
}
|
|
108
960
|
try {
|
|
109
961
|
actorRef.start?.();
|
|
110
962
|
} catch (err) {
|
|
111
|
-
actorScope.self.send(
|
|
963
|
+
actorScope.self.send(createErrorActorEvent(id, err));
|
|
112
964
|
return;
|
|
113
965
|
}
|
|
114
966
|
});
|
|
@@ -163,7 +1015,7 @@ function executeStop(actorScope, actorRef) {
|
|
|
163
1015
|
|
|
164
1016
|
// this allows us to prevent an actor from being started if it gets stopped within the same macrostep
|
|
165
1017
|
// this can happen, for example, when the invoking state is being exited immediately by an always transition
|
|
166
|
-
if (actorRef._processingStatus !==
|
|
1018
|
+
if (actorRef._processingStatus !== ProcessingStatus.Running) {
|
|
167
1019
|
actorScope.stopChild(actorRef);
|
|
168
1020
|
return;
|
|
169
1021
|
}
|
|
@@ -198,7 +1050,7 @@ function checkStateIn(state, _, {
|
|
|
198
1050
|
}) {
|
|
199
1051
|
if (typeof stateValue === 'string' && isStateId(stateValue)) {
|
|
200
1052
|
const target = state.machine.getStateNodeById(stateValue);
|
|
201
|
-
return state.
|
|
1053
|
+
return state._nodes.some(sn => sn === target);
|
|
202
1054
|
}
|
|
203
1055
|
return state.matches(stateValue);
|
|
204
1056
|
}
|
|
@@ -317,26 +1169,25 @@ function getProperAncestors(stateNode, toStateNode) {
|
|
|
317
1169
|
}
|
|
318
1170
|
return ancestors;
|
|
319
1171
|
}
|
|
320
|
-
function
|
|
321
|
-
const
|
|
322
|
-
const
|
|
323
|
-
const adjList = getAdjList(configurationSet);
|
|
1172
|
+
function getAllStateNodes(stateNodes) {
|
|
1173
|
+
const nodeSet = new Set(stateNodes);
|
|
1174
|
+
const adjList = getAdjList(nodeSet);
|
|
324
1175
|
|
|
325
1176
|
// add descendants
|
|
326
|
-
for (const s of
|
|
1177
|
+
for (const s of nodeSet) {
|
|
327
1178
|
// if previously active, add existing child nodes
|
|
328
1179
|
if (s.type === 'compound' && (!adjList.get(s) || !adjList.get(s).length)) {
|
|
329
|
-
getInitialStateNodesWithTheirAncestors(s).forEach(sn =>
|
|
1180
|
+
getInitialStateNodesWithTheirAncestors(s).forEach(sn => nodeSet.add(sn));
|
|
330
1181
|
} else {
|
|
331
1182
|
if (s.type === 'parallel') {
|
|
332
1183
|
for (const child of getChildren(s)) {
|
|
333
1184
|
if (child.type === 'history') {
|
|
334
1185
|
continue;
|
|
335
1186
|
}
|
|
336
|
-
if (!
|
|
1187
|
+
if (!nodeSet.has(child)) {
|
|
337
1188
|
const initialStates = getInitialStateNodesWithTheirAncestors(child);
|
|
338
1189
|
for (const initialStateNode of initialStates) {
|
|
339
|
-
|
|
1190
|
+
nodeSet.add(initialStateNode);
|
|
340
1191
|
}
|
|
341
1192
|
}
|
|
342
1193
|
}
|
|
@@ -345,14 +1196,14 @@ function getConfiguration(stateNodes) {
|
|
|
345
1196
|
}
|
|
346
1197
|
|
|
347
1198
|
// add all ancestors
|
|
348
|
-
for (const s of
|
|
1199
|
+
for (const s of nodeSet) {
|
|
349
1200
|
let m = s.parent;
|
|
350
1201
|
while (m) {
|
|
351
|
-
|
|
1202
|
+
nodeSet.add(m);
|
|
352
1203
|
m = m.parent;
|
|
353
1204
|
}
|
|
354
1205
|
}
|
|
355
|
-
return
|
|
1206
|
+
return nodeSet;
|
|
356
1207
|
}
|
|
357
1208
|
function getValueFromAdj(baseNode, adjList) {
|
|
358
1209
|
const childStateNodes = adjList.get(baseNode);
|
|
@@ -376,9 +1227,9 @@ function getValueFromAdj(baseNode, adjList) {
|
|
|
376
1227
|
}
|
|
377
1228
|
return stateValue;
|
|
378
1229
|
}
|
|
379
|
-
function getAdjList(
|
|
1230
|
+
function getAdjList(stateNodes) {
|
|
380
1231
|
const adjList = new Map();
|
|
381
|
-
for (const s of
|
|
1232
|
+
for (const s of stateNodes) {
|
|
382
1233
|
if (!adjList.has(s)) {
|
|
383
1234
|
adjList.set(s, []);
|
|
384
1235
|
}
|
|
@@ -391,25 +1242,25 @@ function getAdjList(configuration) {
|
|
|
391
1242
|
}
|
|
392
1243
|
return adjList;
|
|
393
1244
|
}
|
|
394
|
-
function getStateValue(rootNode,
|
|
395
|
-
const config =
|
|
1245
|
+
function getStateValue(rootNode, stateNodes) {
|
|
1246
|
+
const config = getAllStateNodes(stateNodes);
|
|
396
1247
|
return getValueFromAdj(rootNode, getAdjList(config));
|
|
397
1248
|
}
|
|
398
|
-
function isInFinalState(
|
|
1249
|
+
function isInFinalState(stateNodeSet, stateNode) {
|
|
399
1250
|
if (stateNode.type === 'compound') {
|
|
400
|
-
return getChildren(stateNode).some(s => s.type === 'final' &&
|
|
1251
|
+
return getChildren(stateNode).some(s => s.type === 'final' && stateNodeSet.has(s));
|
|
401
1252
|
}
|
|
402
1253
|
if (stateNode.type === 'parallel') {
|
|
403
|
-
return getChildren(stateNode).every(sn => isInFinalState(
|
|
1254
|
+
return getChildren(stateNode).every(sn => isInFinalState(stateNodeSet, sn));
|
|
404
1255
|
}
|
|
405
1256
|
return stateNode.type === 'final';
|
|
406
1257
|
}
|
|
407
|
-
const isStateId = str => str[0] ===
|
|
1258
|
+
const isStateId = str => str[0] === STATE_IDENTIFIER;
|
|
408
1259
|
function getCandidates(stateNode, receivedEventType) {
|
|
409
1260
|
const candidates = stateNode.transitions.get(receivedEventType) || [...stateNode.transitions.keys()].filter(descriptor => {
|
|
410
1261
|
// check if transition is a wildcard transition,
|
|
411
1262
|
// which matches any non-transient events
|
|
412
|
-
if (descriptor ===
|
|
1263
|
+
if (descriptor === WILDCARD) {
|
|
413
1264
|
return true;
|
|
414
1265
|
}
|
|
415
1266
|
if (!descriptor.endsWith('.*')) {
|
|
@@ -449,7 +1300,7 @@ function getDelayedTransitions(stateNode) {
|
|
|
449
1300
|
}
|
|
450
1301
|
const mutateEntryExit = (delay, i) => {
|
|
451
1302
|
const delayRef = typeof delay === 'function' ? `${stateNode.id}:delay[${i}]` : delay;
|
|
452
|
-
const afterEvent =
|
|
1303
|
+
const afterEvent = createAfterEvent(delayRef, stateNode.id);
|
|
453
1304
|
const eventType = afterEvent.type;
|
|
454
1305
|
stateNode.entry.push(raise(afterEvent, {
|
|
455
1306
|
id: eventType,
|
|
@@ -465,7 +1316,7 @@ function getDelayedTransitions(stateNode) {
|
|
|
465
1316
|
} : configTransition;
|
|
466
1317
|
const resolvedDelay = !isNaN(+delay) ? +delay : delay;
|
|
467
1318
|
const eventType = mutateEntryExit(resolvedDelay, i);
|
|
468
|
-
return
|
|
1319
|
+
return toArray(resolvedTransition).map(transition => ({
|
|
469
1320
|
...transition,
|
|
470
1321
|
event: eventType,
|
|
471
1322
|
delay: resolvedDelay
|
|
@@ -482,7 +1333,7 @@ function getDelayedTransitions(stateNode) {
|
|
|
482
1333
|
});
|
|
483
1334
|
}
|
|
484
1335
|
function formatTransition(stateNode, descriptor, transitionConfig) {
|
|
485
|
-
const normalizedTarget =
|
|
1336
|
+
const normalizedTarget = normalizeTarget(transitionConfig.target);
|
|
486
1337
|
const reenter = transitionConfig.reenter ?? false;
|
|
487
1338
|
const target = resolveTarget(stateNode, normalizedTarget);
|
|
488
1339
|
|
|
@@ -492,7 +1343,7 @@ function formatTransition(stateNode, descriptor, transitionConfig) {
|
|
|
492
1343
|
}
|
|
493
1344
|
const transition = {
|
|
494
1345
|
...transitionConfig,
|
|
495
|
-
actions:
|
|
1346
|
+
actions: toArray(transitionConfig.actions),
|
|
496
1347
|
guard: transitionConfig.guard,
|
|
497
1348
|
target,
|
|
498
1349
|
source: stateNode,
|
|
@@ -510,29 +1361,29 @@ function formatTransitions(stateNode) {
|
|
|
510
1361
|
const transitions = new Map();
|
|
511
1362
|
if (stateNode.config.on) {
|
|
512
1363
|
for (const descriptor of Object.keys(stateNode.config.on)) {
|
|
513
|
-
if (descriptor ===
|
|
1364
|
+
if (descriptor === NULL_EVENT) {
|
|
514
1365
|
throw new Error('Null events ("") cannot be specified as a transition key. Use `always: { ... }` instead.');
|
|
515
1366
|
}
|
|
516
1367
|
const transitionsConfig = stateNode.config.on[descriptor];
|
|
517
|
-
transitions.set(descriptor,
|
|
1368
|
+
transitions.set(descriptor, toTransitionConfigArray(transitionsConfig).map(t => formatTransition(stateNode, descriptor, t)));
|
|
518
1369
|
}
|
|
519
1370
|
}
|
|
520
1371
|
if (stateNode.config.onDone) {
|
|
521
1372
|
const descriptor = `xstate.done.state.${stateNode.id}`;
|
|
522
|
-
transitions.set(descriptor,
|
|
1373
|
+
transitions.set(descriptor, toTransitionConfigArray(stateNode.config.onDone).map(t => formatTransition(stateNode, descriptor, t)));
|
|
523
1374
|
}
|
|
524
1375
|
for (const invokeDef of stateNode.invoke) {
|
|
525
1376
|
if (invokeDef.onDone) {
|
|
526
1377
|
const descriptor = `xstate.done.actor.${invokeDef.id}`;
|
|
527
|
-
transitions.set(descriptor,
|
|
1378
|
+
transitions.set(descriptor, toTransitionConfigArray(invokeDef.onDone).map(t => formatTransition(stateNode, descriptor, t)));
|
|
528
1379
|
}
|
|
529
1380
|
if (invokeDef.onError) {
|
|
530
1381
|
const descriptor = `xstate.error.actor.${invokeDef.id}`;
|
|
531
|
-
transitions.set(descriptor,
|
|
1382
|
+
transitions.set(descriptor, toTransitionConfigArray(invokeDef.onError).map(t => formatTransition(stateNode, descriptor, t)));
|
|
532
1383
|
}
|
|
533
1384
|
if (invokeDef.onSnapshot) {
|
|
534
1385
|
const descriptor = `xstate.snapshot.${invokeDef.id}`;
|
|
535
|
-
transitions.set(descriptor,
|
|
1386
|
+
transitions.set(descriptor, toTransitionConfigArray(invokeDef.onSnapshot).map(t => formatTransition(stateNode, descriptor, t)));
|
|
536
1387
|
}
|
|
537
1388
|
}
|
|
538
1389
|
for (const delayedTransition of stateNode.after) {
|
|
@@ -552,7 +1403,7 @@ function formatInitialTransition(stateNode, _target) {
|
|
|
552
1403
|
}
|
|
553
1404
|
const transition = {
|
|
554
1405
|
source: stateNode,
|
|
555
|
-
actions: !_target || typeof _target === 'string' ? [] :
|
|
1406
|
+
actions: !_target || typeof _target === 'string' ? [] : toArray(_target.actions),
|
|
556
1407
|
eventType: null,
|
|
557
1408
|
reenter: false,
|
|
558
1409
|
target: resolvedTarget ? [resolvedTarget] : [],
|
|
@@ -576,7 +1427,7 @@ function resolveTarget(stateNode, targets) {
|
|
|
576
1427
|
if (isStateId(target)) {
|
|
577
1428
|
return stateNode.machine.getStateNodeById(target);
|
|
578
1429
|
}
|
|
579
|
-
const isInternalTarget = target[0] ===
|
|
1430
|
+
const isInternalTarget = target[0] === STATE_DELIMITER;
|
|
580
1431
|
// If internal target is defined on machine,
|
|
581
1432
|
// do not include machine key on target
|
|
582
1433
|
if (isInternalTarget && !stateNode.parent) {
|
|
@@ -596,7 +1447,7 @@ function resolveTarget(stateNode, targets) {
|
|
|
596
1447
|
});
|
|
597
1448
|
}
|
|
598
1449
|
function resolveHistoryDefaultTransition(stateNode) {
|
|
599
|
-
const normalizedTarget =
|
|
1450
|
+
const normalizedTarget = normalizeTarget(stateNode.config.target);
|
|
600
1451
|
if (!normalizedTarget) {
|
|
601
1452
|
return stateNode.parent.initial;
|
|
602
1453
|
}
|
|
@@ -665,7 +1516,7 @@ function getStateNodeByPath(stateNode, statePath) {
|
|
|
665
1516
|
// throw e;
|
|
666
1517
|
}
|
|
667
1518
|
}
|
|
668
|
-
const arrayStatePath =
|
|
1519
|
+
const arrayStatePath = toStatePath(statePath).slice();
|
|
669
1520
|
let currentStateNode = stateNode;
|
|
670
1521
|
while (arrayStatePath.length) {
|
|
671
1522
|
const key = arrayStatePath.shift();
|
|
@@ -771,13 +1622,13 @@ function hasIntersection(s1, s2) {
|
|
|
771
1622
|
}
|
|
772
1623
|
return false;
|
|
773
1624
|
}
|
|
774
|
-
function removeConflictingTransitions(enabledTransitions,
|
|
1625
|
+
function removeConflictingTransitions(enabledTransitions, stateNodeSet, historyValue) {
|
|
775
1626
|
const filteredTransitions = new Set();
|
|
776
1627
|
for (const t1 of enabledTransitions) {
|
|
777
1628
|
let t1Preempted = false;
|
|
778
1629
|
const transitionsToRemove = new Set();
|
|
779
1630
|
for (const t2 of filteredTransitions) {
|
|
780
|
-
if (hasIntersection(computeExitSet([t1],
|
|
1631
|
+
if (hasIntersection(computeExitSet([t1], stateNodeSet, historyValue), computeExitSet([t2], stateNodeSet, historyValue))) {
|
|
781
1632
|
if (isDescendant(t1.source, t2.source)) {
|
|
782
1633
|
transitionsToRemove.add(t2);
|
|
783
1634
|
} else {
|
|
@@ -844,7 +1695,7 @@ function getTransitionDomain(transition, historyValue) {
|
|
|
844
1695
|
}
|
|
845
1696
|
return transition.source.machine.root;
|
|
846
1697
|
}
|
|
847
|
-
function computeExitSet(transitions,
|
|
1698
|
+
function computeExitSet(transitions, stateNodeSet, historyValue) {
|
|
848
1699
|
const statesToExit = new Set();
|
|
849
1700
|
for (const t of transitions) {
|
|
850
1701
|
if (t.target?.length) {
|
|
@@ -852,7 +1703,7 @@ function computeExitSet(transitions, configuration, historyValue) {
|
|
|
852
1703
|
if (t.reenter && t.source === domain) {
|
|
853
1704
|
statesToExit.add(domain);
|
|
854
1705
|
}
|
|
855
|
-
for (const stateNode of
|
|
1706
|
+
for (const stateNode of stateNodeSet) {
|
|
856
1707
|
if (isDescendant(stateNode, domain)) {
|
|
857
1708
|
statesToExit.add(stateNode);
|
|
858
1709
|
}
|
|
@@ -861,12 +1712,12 @@ function computeExitSet(transitions, configuration, historyValue) {
|
|
|
861
1712
|
}
|
|
862
1713
|
return [...statesToExit];
|
|
863
1714
|
}
|
|
864
|
-
function
|
|
865
|
-
if (
|
|
1715
|
+
function areStateNodeCollectionsEqual(prevStateNodes, nextStateNodeSet) {
|
|
1716
|
+
if (prevStateNodes.length !== nextStateNodeSet.size) {
|
|
866
1717
|
return false;
|
|
867
1718
|
}
|
|
868
|
-
for (const node of
|
|
869
|
-
if (!
|
|
1719
|
+
for (const node of prevStateNodes) {
|
|
1720
|
+
if (!nextStateNodeSet.has(node)) {
|
|
870
1721
|
return false;
|
|
871
1722
|
}
|
|
872
1723
|
}
|
|
@@ -880,31 +1731,31 @@ function microstep(transitions, currentState, actorScope, event, isInitial, inte
|
|
|
880
1731
|
if (!transitions.length) {
|
|
881
1732
|
return currentState;
|
|
882
1733
|
}
|
|
883
|
-
const
|
|
1734
|
+
const mutStateNodeSet = new Set(currentState._nodes);
|
|
884
1735
|
let historyValue = currentState.historyValue;
|
|
885
|
-
const filteredTransitions = removeConflictingTransitions(transitions,
|
|
1736
|
+
const filteredTransitions = removeConflictingTransitions(transitions, mutStateNodeSet, historyValue);
|
|
886
1737
|
let nextState = currentState;
|
|
887
1738
|
|
|
888
1739
|
// Exit states
|
|
889
1740
|
if (!isInitial) {
|
|
890
|
-
[nextState, historyValue] = exitStates(nextState, event, actorScope, filteredTransitions,
|
|
1741
|
+
[nextState, historyValue] = exitStates(nextState, event, actorScope, filteredTransitions, mutStateNodeSet, historyValue, internalQueue);
|
|
891
1742
|
}
|
|
892
1743
|
|
|
893
1744
|
// Execute transition content
|
|
894
1745
|
nextState = resolveActionsAndContext(nextState, event, actorScope, filteredTransitions.flatMap(t => t.actions), internalQueue);
|
|
895
1746
|
|
|
896
1747
|
// Enter states
|
|
897
|
-
nextState = enterStates(nextState, event, actorScope, filteredTransitions,
|
|
898
|
-
const
|
|
1748
|
+
nextState = enterStates(nextState, event, actorScope, filteredTransitions, mutStateNodeSet, internalQueue, historyValue, isInitial);
|
|
1749
|
+
const nextStateNodes = [...mutStateNodeSet];
|
|
899
1750
|
if (nextState.status === 'done') {
|
|
900
|
-
nextState = resolveActionsAndContext(nextState, event, actorScope,
|
|
1751
|
+
nextState = resolveActionsAndContext(nextState, event, actorScope, nextStateNodes.sort((a, b) => b.order - a.order).flatMap(state => state.exit), internalQueue);
|
|
901
1752
|
}
|
|
902
1753
|
try {
|
|
903
|
-
if (historyValue === currentState.historyValue &&
|
|
1754
|
+
if (historyValue === currentState.historyValue && areStateNodeCollectionsEqual(currentState._nodes, mutStateNodeSet)) {
|
|
904
1755
|
return nextState;
|
|
905
1756
|
}
|
|
906
1757
|
return cloneMachineSnapshot(nextState, {
|
|
907
|
-
|
|
1758
|
+
_nodes: nextStateNodes,
|
|
908
1759
|
historyValue
|
|
909
1760
|
});
|
|
910
1761
|
} catch (e) {
|
|
@@ -917,10 +1768,10 @@ function getMachineOutput(state, event, actorScope, rootNode, rootCompletionNode
|
|
|
917
1768
|
if (!rootNode.output) {
|
|
918
1769
|
return;
|
|
919
1770
|
}
|
|
920
|
-
const doneStateEvent =
|
|
921
|
-
return
|
|
1771
|
+
const doneStateEvent = createDoneStateEvent(rootCompletionNode.id, rootCompletionNode.output && rootCompletionNode.parent ? resolveOutput(rootCompletionNode.output, state.context, event, actorScope.self) : undefined);
|
|
1772
|
+
return resolveOutput(rootNode.output, state.context, doneStateEvent, actorScope.self);
|
|
922
1773
|
}
|
|
923
|
-
function enterStates(currentState, event, actorScope, filteredTransitions,
|
|
1774
|
+
function enterStates(currentState, event, actorScope, filteredTransitions, mutStateNodeSet, internalQueue, historyValue, isInitial) {
|
|
924
1775
|
let nextState = currentState;
|
|
925
1776
|
const statesToEnter = new Set();
|
|
926
1777
|
// those are states that were directly targeted or indirectly targeted by the explicit target
|
|
@@ -935,7 +1786,7 @@ function enterStates(currentState, event, actorScope, filteredTransitions, mutCo
|
|
|
935
1786
|
}
|
|
936
1787
|
const completedNodes = new Set();
|
|
937
1788
|
for (const stateNodeToEnter of [...statesToEnter].sort((a, b) => a.order - b.order)) {
|
|
938
|
-
|
|
1789
|
+
mutStateNodeSet.add(stateNodeToEnter);
|
|
939
1790
|
const actions = [];
|
|
940
1791
|
|
|
941
1792
|
// Add entry actions
|
|
@@ -956,11 +1807,11 @@ function enterStates(currentState, event, actorScope, filteredTransitions, mutCo
|
|
|
956
1807
|
let ancestorMarker = parent?.type === 'parallel' ? parent : parent?.parent;
|
|
957
1808
|
let rootCompletionNode = ancestorMarker || stateNodeToEnter;
|
|
958
1809
|
if (parent?.type === 'compound') {
|
|
959
|
-
internalQueue.push(
|
|
1810
|
+
internalQueue.push(createDoneStateEvent(parent.id, stateNodeToEnter.output ? resolveOutput(stateNodeToEnter.output, nextState.context, event, actorScope.self) : undefined));
|
|
960
1811
|
}
|
|
961
|
-
while (ancestorMarker?.type === 'parallel' && !completedNodes.has(ancestorMarker) && isInFinalState(
|
|
1812
|
+
while (ancestorMarker?.type === 'parallel' && !completedNodes.has(ancestorMarker) && isInFinalState(mutStateNodeSet, ancestorMarker)) {
|
|
962
1813
|
completedNodes.add(ancestorMarker);
|
|
963
|
-
internalQueue.push(
|
|
1814
|
+
internalQueue.push(createDoneStateEvent(ancestorMarker.id));
|
|
964
1815
|
rootCompletionNode = ancestorMarker;
|
|
965
1816
|
ancestorMarker = ancestorMarker.parent;
|
|
966
1817
|
}
|
|
@@ -1068,9 +1919,9 @@ function addAncestorStatesToEnter(statesToEnter, historyValue, statesForDefaultE
|
|
|
1068
1919
|
function addProperAncestorStatesToEnter(stateNode, toStateNode, statesToEnter, historyValue, statesForDefaultEntry) {
|
|
1069
1920
|
addAncestorStatesToEnter(statesToEnter, historyValue, statesForDefaultEntry, getProperAncestors(stateNode, toStateNode));
|
|
1070
1921
|
}
|
|
1071
|
-
function exitStates(currentState, event, actorScope, transitions,
|
|
1922
|
+
function exitStates(currentState, event, actorScope, transitions, mutStateNodeSet, historyValue, internalQueue) {
|
|
1072
1923
|
let nextState = currentState;
|
|
1073
|
-
const statesToExit = computeExitSet(transitions,
|
|
1924
|
+
const statesToExit = computeExitSet(transitions, mutStateNodeSet, historyValue);
|
|
1074
1925
|
statesToExit.sort((a, b) => b.order - a.order);
|
|
1075
1926
|
let changedHistory;
|
|
1076
1927
|
|
|
@@ -1088,12 +1939,12 @@ function exitStates(currentState, event, actorScope, transitions, mutConfigurati
|
|
|
1088
1939
|
changedHistory ??= {
|
|
1089
1940
|
...historyValue
|
|
1090
1941
|
};
|
|
1091
|
-
changedHistory[historyNode.id] = Array.from(
|
|
1942
|
+
changedHistory[historyNode.id] = Array.from(mutStateNodeSet).filter(predicate);
|
|
1092
1943
|
}
|
|
1093
1944
|
}
|
|
1094
1945
|
for (const s of statesToExit) {
|
|
1095
1946
|
nextState = resolveActionsAndContext(nextState, event, actorScope, [...s.exit, ...s.invoke.map(def => stop(def.id))], internalQueue);
|
|
1096
|
-
|
|
1947
|
+
mutStateNodeSet.delete(s);
|
|
1097
1948
|
}
|
|
1098
1949
|
return [nextState, changedHistory || historyValue];
|
|
1099
1950
|
}
|
|
@@ -1123,7 +1974,7 @@ function resolveActionsAndContextWorker(currentState, event, actorScope, actions
|
|
|
1123
1974
|
event
|
|
1124
1975
|
}) : action.params : undefined;
|
|
1125
1976
|
if (!('resolve' in resolvedAction)) {
|
|
1126
|
-
if (actorScope?.self._processingStatus ===
|
|
1977
|
+
if (actorScope?.self._processingStatus === ProcessingStatus.Running) {
|
|
1127
1978
|
resolvedAction(actionArgs, actionParams);
|
|
1128
1979
|
} else {
|
|
1129
1980
|
actorScope?.defer(() => {
|
|
@@ -1141,7 +1992,7 @@ function resolveActionsAndContextWorker(currentState, event, actorScope, actions
|
|
|
1141
1992
|
retries?.push([builtinAction, params]);
|
|
1142
1993
|
}
|
|
1143
1994
|
if ('execute' in builtinAction) {
|
|
1144
|
-
if (actorScope?.self._processingStatus ===
|
|
1995
|
+
if (actorScope?.self._processingStatus === ProcessingStatus.Running) {
|
|
1145
1996
|
builtinAction.execute(actorScope, params);
|
|
1146
1997
|
} else {
|
|
1147
1998
|
actorScope?.defer(builtinAction.execute.bind(null, actorScope, params));
|
|
@@ -1165,14 +2016,14 @@ function resolveActionsAndContext(currentState, event, actorScope, actions, inte
|
|
|
1165
2016
|
return nextState;
|
|
1166
2017
|
}
|
|
1167
2018
|
function macrostep(state, event, actorScope, internalQueue = []) {
|
|
1168
|
-
if (event.type ===
|
|
1169
|
-
throw new Error(`An event cannot have the wildcard type ('${
|
|
2019
|
+
if (event.type === WILDCARD) {
|
|
2020
|
+
throw new Error(`An event cannot have the wildcard type ('${WILDCARD}')`);
|
|
1170
2021
|
}
|
|
1171
2022
|
let nextState = state;
|
|
1172
2023
|
const states = [];
|
|
1173
2024
|
|
|
1174
2025
|
// Handle stop event
|
|
1175
|
-
if (event.type ===
|
|
2026
|
+
if (event.type === XSTATE_STOP) {
|
|
1176
2027
|
nextState = cloneMachineSnapshot(stopChildren(nextState, event, actorScope), {
|
|
1177
2028
|
status: 'stopped'
|
|
1178
2029
|
});
|
|
@@ -1186,7 +2037,7 @@ function macrostep(state, event, actorScope, internalQueue = []) {
|
|
|
1186
2037
|
|
|
1187
2038
|
// Assume the state is at rest (no raised events)
|
|
1188
2039
|
// Determine the next state based on the next microstep
|
|
1189
|
-
if (nextEvent.type !==
|
|
2040
|
+
if (nextEvent.type !== XSTATE_INIT) {
|
|
1190
2041
|
const transitions = selectTransitions(nextEvent, nextState);
|
|
1191
2042
|
nextState = microstep(transitions, state, actorScope, nextEvent, false, internalQueue);
|
|
1192
2043
|
states.push(nextState);
|
|
@@ -1225,7 +2076,7 @@ function selectTransitions(event, nextState) {
|
|
|
1225
2076
|
}
|
|
1226
2077
|
function selectEventlessTransitions(nextState, event) {
|
|
1227
2078
|
const enabledTransitionSet = new Set();
|
|
1228
|
-
const atomicStates = nextState.
|
|
2079
|
+
const atomicStates = nextState._nodes.filter(isAtomicStateNode);
|
|
1229
2080
|
for (const stateNode of atomicStates) {
|
|
1230
2081
|
loop: for (const s of [stateNode].concat(getProperAncestors(stateNode, undefined))) {
|
|
1231
2082
|
if (!s.always) {
|
|
@@ -1239,7 +2090,7 @@ function selectEventlessTransitions(nextState, event) {
|
|
|
1239
2090
|
}
|
|
1240
2091
|
}
|
|
1241
2092
|
}
|
|
1242
|
-
return removeConflictingTransitions(Array.from(enabledTransitionSet), new Set(nextState.
|
|
2093
|
+
return removeConflictingTransitions(Array.from(enabledTransitionSet), new Set(nextState._nodes), nextState.historyValue);
|
|
1243
2094
|
}
|
|
1244
2095
|
|
|
1245
2096
|
/**
|
|
@@ -1248,12 +2099,15 @@ function selectEventlessTransitions(nextState, event) {
|
|
|
1248
2099
|
* @param stateValue The partial state value to resolve.
|
|
1249
2100
|
*/
|
|
1250
2101
|
function resolveStateValue(rootNode, stateValue) {
|
|
1251
|
-
const
|
|
1252
|
-
return getStateValue(rootNode, [...
|
|
2102
|
+
const allStateNodes = getAllStateNodes(getStateNodes(rootNode, stateValue));
|
|
2103
|
+
return getStateValue(rootNode, [...allStateNodes]);
|
|
1253
2104
|
}
|
|
1254
2105
|
|
|
2106
|
+
function isMachineSnapshot(value) {
|
|
2107
|
+
return !!value && typeof value === 'object' && 'machine' in value && 'value' in value;
|
|
2108
|
+
}
|
|
1255
2109
|
const machineSnapshotMatches = function matches(testValue) {
|
|
1256
|
-
return
|
|
2110
|
+
return matchesState(testValue, this.value);
|
|
1257
2111
|
};
|
|
1258
2112
|
const machineSnapshotHasTag = function hasTag(tag) {
|
|
1259
2113
|
return this.tags.has(tag);
|
|
@@ -1269,10 +2123,11 @@ const machineSnapshotCan = function can(event) {
|
|
|
1269
2123
|
};
|
|
1270
2124
|
const machineSnapshotToJSON = function toJSON() {
|
|
1271
2125
|
const {
|
|
1272
|
-
|
|
2126
|
+
_nodes: nodes,
|
|
1273
2127
|
tags,
|
|
1274
2128
|
machine,
|
|
1275
|
-
|
|
2129
|
+
getNextEvents,
|
|
2130
|
+
getMeta,
|
|
1276
2131
|
toJSON,
|
|
1277
2132
|
can,
|
|
1278
2133
|
hasTag,
|
|
@@ -1284,13 +2139,11 @@ const machineSnapshotToJSON = function toJSON() {
|
|
|
1284
2139
|
tags: Array.from(tags)
|
|
1285
2140
|
};
|
|
1286
2141
|
};
|
|
1287
|
-
const
|
|
1288
|
-
return
|
|
1289
|
-
return [...new Set(interpreter.flatten([...this.configuration.map(sn => sn.ownEvents)]))];
|
|
1290
|
-
});
|
|
2142
|
+
const machineSnapshotGetNextEvents = function getNextEvents() {
|
|
2143
|
+
return [...new Set(flatten([...this._nodes.map(sn => sn.ownEvents)]))];
|
|
1291
2144
|
};
|
|
1292
|
-
const
|
|
1293
|
-
return this.
|
|
2145
|
+
const machineSnapshotGetMeta = function getMeta() {
|
|
2146
|
+
return this._nodes.reduce((acc, stateNode) => {
|
|
1294
2147
|
if (stateNode.meta !== undefined) {
|
|
1295
2148
|
acc[stateNode.id] = stateNode.meta;
|
|
1296
2149
|
}
|
|
@@ -1298,48 +2151,35 @@ const machineSnapshotMeta = function nextEvents() {
|
|
|
1298
2151
|
}, {});
|
|
1299
2152
|
};
|
|
1300
2153
|
function createMachineSnapshot(config, machine) {
|
|
1301
|
-
|
|
2154
|
+
return {
|
|
1302
2155
|
status: config.status,
|
|
1303
2156
|
output: config.output,
|
|
1304
2157
|
error: config.error,
|
|
1305
2158
|
machine,
|
|
1306
2159
|
context: config.context,
|
|
1307
|
-
|
|
1308
|
-
value: getStateValue(machine.root, config.
|
|
1309
|
-
tags: new Set(
|
|
2160
|
+
_nodes: config._nodes,
|
|
2161
|
+
value: getStateValue(machine.root, config._nodes),
|
|
2162
|
+
tags: new Set(flatten(config._nodes.map(sn => sn.tags))),
|
|
1310
2163
|
children: config.children,
|
|
1311
2164
|
historyValue: config.historyValue || {},
|
|
1312
2165
|
// this one is generic in the target and it's hard to create a matching non-generic source signature
|
|
1313
2166
|
matches: machineSnapshotMatches,
|
|
1314
2167
|
hasTag: machineSnapshotHasTag,
|
|
1315
2168
|
can: machineSnapshotCan,
|
|
2169
|
+
getNextEvents: machineSnapshotGetNextEvents,
|
|
2170
|
+
getMeta: machineSnapshotGetMeta,
|
|
1316
2171
|
toJSON: machineSnapshotToJSON
|
|
1317
2172
|
};
|
|
1318
|
-
Object.defineProperties(snapshot, {
|
|
1319
|
-
nextEvents: {
|
|
1320
|
-
get: machineSnapshotNextEvents,
|
|
1321
|
-
configurable: true,
|
|
1322
|
-
enumerable: true
|
|
1323
|
-
},
|
|
1324
|
-
meta: {
|
|
1325
|
-
get: machineSnapshotMeta,
|
|
1326
|
-
configurable: true,
|
|
1327
|
-
enumerable: true
|
|
1328
|
-
}
|
|
1329
|
-
});
|
|
1330
|
-
return snapshot;
|
|
1331
2173
|
}
|
|
1332
2174
|
function cloneMachineSnapshot(state, config = {}) {
|
|
1333
|
-
return createMachineSnapshot(
|
|
1334
|
-
// TODO: it's wasteful that this spread triggers getters
|
|
1335
|
-
{
|
|
2175
|
+
return createMachineSnapshot({
|
|
1336
2176
|
...state,
|
|
1337
2177
|
...config
|
|
1338
2178
|
}, state.machine);
|
|
1339
2179
|
}
|
|
1340
2180
|
function getPersistedState(state, options) {
|
|
1341
2181
|
const {
|
|
1342
|
-
|
|
2182
|
+
_nodes: nodes,
|
|
1343
2183
|
tags,
|
|
1344
2184
|
machine,
|
|
1345
2185
|
children,
|
|
@@ -1347,8 +2187,9 @@ function getPersistedState(state, options) {
|
|
|
1347
2187
|
can,
|
|
1348
2188
|
hasTag,
|
|
1349
2189
|
matches,
|
|
2190
|
+
getNextEvents,
|
|
2191
|
+
getMeta,
|
|
1350
2192
|
toJSON,
|
|
1351
|
-
nextEvents,
|
|
1352
2193
|
...jsonValues
|
|
1353
2194
|
} = state;
|
|
1354
2195
|
const childrenJson = {};
|
|
@@ -1360,7 +2201,8 @@ function getPersistedState(state, options) {
|
|
|
1360
2201
|
childrenJson[id] = {
|
|
1361
2202
|
state: child.getPersistedState(options),
|
|
1362
2203
|
src: child.src,
|
|
1363
|
-
systemId: child._systemId
|
|
2204
|
+
systemId: child._systemId,
|
|
2205
|
+
syncSnapshot: child._syncSnapshot
|
|
1364
2206
|
};
|
|
1365
2207
|
}
|
|
1366
2208
|
const persisted = {
|
|
@@ -1380,7 +2222,7 @@ function persistContext(contextPart) {
|
|
|
1380
2222
|
...contextPart
|
|
1381
2223
|
};
|
|
1382
2224
|
copy[key] = {
|
|
1383
|
-
xstate$$type:
|
|
2225
|
+
xstate$$type: $$ACTOR_TYPE,
|
|
1384
2226
|
id: value.id
|
|
1385
2227
|
};
|
|
1386
2228
|
} else {
|
|
@@ -1452,32 +2294,52 @@ function raise(eventOrExpr, options) {
|
|
|
1452
2294
|
return raise;
|
|
1453
2295
|
}
|
|
1454
2296
|
|
|
2297
|
+
exports.$$ACTOR_TYPE = $$ACTOR_TYPE;
|
|
2298
|
+
exports.Actor = Actor;
|
|
2299
|
+
exports.NULL_EVENT = NULL_EVENT;
|
|
2300
|
+
exports.ProcessingStatus = ProcessingStatus;
|
|
2301
|
+
exports.STATE_DELIMITER = STATE_DELIMITER;
|
|
2302
|
+
exports.XSTATE_ERROR = XSTATE_ERROR;
|
|
2303
|
+
exports.XSTATE_STOP = XSTATE_STOP;
|
|
1455
2304
|
exports.and = and;
|
|
1456
2305
|
exports.cancel = cancel;
|
|
1457
2306
|
exports.cloneMachineSnapshot = cloneMachineSnapshot;
|
|
2307
|
+
exports.createActor = createActor;
|
|
2308
|
+
exports.createErrorActorEvent = createErrorActorEvent;
|
|
2309
|
+
exports.createInitEvent = createInitEvent;
|
|
2310
|
+
exports.createInvokeId = createInvokeId;
|
|
1458
2311
|
exports.createMachineSnapshot = createMachineSnapshot;
|
|
1459
2312
|
exports.evaluateGuard = evaluateGuard;
|
|
1460
2313
|
exports.formatInitialTransition = formatInitialTransition;
|
|
1461
2314
|
exports.formatTransition = formatTransition;
|
|
1462
2315
|
exports.formatTransitions = formatTransitions;
|
|
2316
|
+
exports.getAllStateNodes = getAllStateNodes;
|
|
1463
2317
|
exports.getCandidates = getCandidates;
|
|
1464
|
-
exports.getConfiguration = getConfiguration;
|
|
1465
2318
|
exports.getDelayedTransitions = getDelayedTransitions;
|
|
1466
2319
|
exports.getInitialStateNodes = getInitialStateNodes;
|
|
1467
2320
|
exports.getPersistedState = getPersistedState;
|
|
1468
2321
|
exports.getStateNodeByPath = getStateNodeByPath;
|
|
1469
2322
|
exports.getStateNodes = getStateNodes;
|
|
2323
|
+
exports.interpret = interpret;
|
|
2324
|
+
exports.isErrorActorEvent = isErrorActorEvent;
|
|
1470
2325
|
exports.isInFinalState = isInFinalState;
|
|
2326
|
+
exports.isMachineSnapshot = isMachineSnapshot;
|
|
1471
2327
|
exports.isStateId = isStateId;
|
|
1472
2328
|
exports.macrostep = macrostep;
|
|
1473
|
-
exports.
|
|
2329
|
+
exports.mapValues = mapValues;
|
|
2330
|
+
exports.matchesState = matchesState;
|
|
1474
2331
|
exports.microstep = microstep;
|
|
1475
2332
|
exports.not = not;
|
|
1476
2333
|
exports.or = or;
|
|
2334
|
+
exports.pathToStateValue = pathToStateValue;
|
|
1477
2335
|
exports.raise = raise;
|
|
1478
2336
|
exports.resolveActionsAndContext = resolveActionsAndContext;
|
|
2337
|
+
exports.resolveReferencedActor = resolveReferencedActor;
|
|
1479
2338
|
exports.resolveStateValue = resolveStateValue;
|
|
1480
2339
|
exports.spawn = spawn;
|
|
1481
2340
|
exports.stateIn = stateIn;
|
|
1482
2341
|
exports.stop = stop;
|
|
2342
|
+
exports.toArray = toArray;
|
|
2343
|
+
exports.toObserver = toObserver;
|
|
2344
|
+
exports.toTransitionConfigArray = toTransitionConfigArray;
|
|
1483
2345
|
exports.transitionNode = transitionNode;
|