xstate 5.0.0-beta.43 → 5.0.0-beta.44
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 +6 -6
- package/actors/dist/xstate-actors.development.cjs.js +6 -6
- package/actors/dist/xstate-actors.development.esm.js +1 -1
- package/actors/dist/xstate-actors.esm.js +1 -1
- 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 +3 -7
- package/dist/declarations/src/actions/spawn.d.ts +11 -16
- package/dist/declarations/src/guards.d.ts +2 -2
- package/dist/declarations/src/index.d.ts +1 -1
- package/dist/declarations/src/spawn.d.ts +9 -13
- package/dist/declarations/src/stateUtils.d.ts +4 -4
- package/dist/declarations/src/types.d.ts +15 -16
- package/dist/declarations/src/utils.d.ts +1 -3
- package/dist/{raise-e0fe5c2d.cjs.js → raise-348cc74e.development.esm.js} +946 -68
- package/dist/{raise-f4ad5a87.development.esm.js → raise-5854eaca.esm.js} +860 -49
- package/dist/{raise-23dea0d7.development.cjs.js → raise-ed700d14.development.cjs.js} +922 -36
- package/dist/{raise-8dc8e1aa.esm.js → raise-fb6f017b.cjs.js} +907 -2
- package/dist/{send-0174c155.development.cjs.js → send-00466e37.development.cjs.js} +11 -12
- package/dist/{send-87bbaaab.cjs.js → send-53e5693c.cjs.js} +11 -12
- package/dist/{send-5d129d95.development.esm.js → send-a0193bdb.development.esm.js} +2 -3
- package/dist/{send-84e2e742.esm.js → send-b7b4befa.esm.js} +2 -3
- package/dist/xstate.cjs.js +25 -25
- package/dist/xstate.cjs.mjs +1 -0
- package/dist/xstate.development.cjs.js +25 -25
- package/dist/xstate.development.cjs.mjs +1 -0
- package/dist/xstate.development.esm.js +4 -6
- package/dist/xstate.esm.js +4 -6
- 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.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,6 +1,868 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
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.ref = void 0;
|
|
395
|
+
this._actorScope = void 0;
|
|
396
|
+
this._systemId = void 0;
|
|
397
|
+
this.sessionId = void 0;
|
|
398
|
+
this.system = void 0;
|
|
399
|
+
this._doneEvent = void 0;
|
|
400
|
+
this.src = void 0;
|
|
401
|
+
this._deferred = [];
|
|
402
|
+
const resolvedOptions = {
|
|
403
|
+
...defaultOptions,
|
|
404
|
+
...options
|
|
405
|
+
};
|
|
406
|
+
const {
|
|
407
|
+
clock,
|
|
408
|
+
logger,
|
|
409
|
+
parent,
|
|
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.options = resolvedOptions;
|
|
425
|
+
this.src = resolvedOptions.src ?? logic;
|
|
426
|
+
this.ref = this;
|
|
427
|
+
this._actorScope = {
|
|
428
|
+
self: this,
|
|
429
|
+
id: this.id,
|
|
430
|
+
sessionId: this.sessionId,
|
|
431
|
+
logger: this.logger,
|
|
432
|
+
defer: fn => {
|
|
433
|
+
this._deferred.push(fn);
|
|
434
|
+
},
|
|
435
|
+
system: this.system,
|
|
436
|
+
stopChild: child => {
|
|
437
|
+
if (child._parent !== this) {
|
|
438
|
+
throw new Error(`Cannot stop child actor ${child.id} of ${this.id} because it is not a child`);
|
|
439
|
+
}
|
|
440
|
+
child._stop();
|
|
441
|
+
}
|
|
442
|
+
};
|
|
443
|
+
|
|
444
|
+
// Ensure that the send method is bound to this Actor instance
|
|
445
|
+
// if destructured
|
|
446
|
+
this.send = this.send.bind(this);
|
|
447
|
+
this.system._sendInspectionEvent({
|
|
448
|
+
type: '@xstate.actor',
|
|
449
|
+
actorRef: this
|
|
450
|
+
});
|
|
451
|
+
if (systemId) {
|
|
452
|
+
this._systemId = systemId;
|
|
453
|
+
this.system._set(systemId, this);
|
|
454
|
+
}
|
|
455
|
+
this._initState(options?.state);
|
|
456
|
+
if (systemId && this._state.status !== 'active') {
|
|
457
|
+
this.system._unregister(this);
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
_initState(persistedState) {
|
|
461
|
+
this._state = persistedState ? this.logic.restoreState ? this.logic.restoreState(persistedState, this._actorScope) : persistedState : this.logic.getInitialState(this._actorScope, this.options?.input);
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
// array of functions to defer
|
|
465
|
+
|
|
466
|
+
update(snapshot, event) {
|
|
467
|
+
// Update state
|
|
468
|
+
this._state = snapshot;
|
|
469
|
+
|
|
470
|
+
// Execute deferred effects
|
|
471
|
+
let deferredFn;
|
|
472
|
+
while (deferredFn = this._deferred.shift()) {
|
|
473
|
+
deferredFn();
|
|
474
|
+
}
|
|
475
|
+
for (const observer of this.observers) {
|
|
476
|
+
try {
|
|
477
|
+
observer.next?.(snapshot);
|
|
478
|
+
} catch (err) {
|
|
479
|
+
reportUnhandledError(err);
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
switch (this._state.status) {
|
|
483
|
+
case 'done':
|
|
484
|
+
this._stopProcedure();
|
|
485
|
+
this._complete();
|
|
486
|
+
this._doneEvent = createDoneActorEvent(this.id, this._state.output);
|
|
487
|
+
if (this._parent) {
|
|
488
|
+
this.system._relay(this, this._parent, this._doneEvent);
|
|
489
|
+
}
|
|
490
|
+
break;
|
|
491
|
+
case 'error':
|
|
492
|
+
this._stopProcedure();
|
|
493
|
+
this._error(this._state.error);
|
|
494
|
+
if (this._parent) {
|
|
495
|
+
this.system._relay(this, this._parent, createErrorActorEvent(this.id, this._state.error));
|
|
496
|
+
}
|
|
497
|
+
break;
|
|
498
|
+
}
|
|
499
|
+
this.system._sendInspectionEvent({
|
|
500
|
+
type: '@xstate.snapshot',
|
|
501
|
+
actorRef: this,
|
|
502
|
+
event,
|
|
503
|
+
snapshot
|
|
504
|
+
});
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
/**
|
|
508
|
+
* Subscribe an observer to an actor’s snapshot values.
|
|
509
|
+
*
|
|
510
|
+
* @remarks
|
|
511
|
+
* The observer will receive the actor’s snapshot value when it is emitted. The observer can be:
|
|
512
|
+
* - A plain function that receives the latest snapshot, or
|
|
513
|
+
* - An observer object whose `.next(snapshot)` method receives the latest snapshot
|
|
514
|
+
*
|
|
515
|
+
* @example
|
|
516
|
+
* ```ts
|
|
517
|
+
* // Observer as a plain function
|
|
518
|
+
* const subscription = actor.subscribe((snapshot) => {
|
|
519
|
+
* console.log(snapshot);
|
|
520
|
+
* });
|
|
521
|
+
* ```
|
|
522
|
+
*
|
|
523
|
+
* @example
|
|
524
|
+
* ```ts
|
|
525
|
+
* // Observer as an object
|
|
526
|
+
* const subscription = actor.subscribe({
|
|
527
|
+
* next(snapshot) {
|
|
528
|
+
* console.log(snapshot);
|
|
529
|
+
* },
|
|
530
|
+
* error(err) {
|
|
531
|
+
* // ...
|
|
532
|
+
* },
|
|
533
|
+
* complete() {
|
|
534
|
+
* // ...
|
|
535
|
+
* },
|
|
536
|
+
* });
|
|
537
|
+
* ```
|
|
538
|
+
*
|
|
539
|
+
* The return value of `actor.subscribe(observer)` is a subscription object that has an `.unsubscribe()` method. You can call `subscription.unsubscribe()` to unsubscribe the observer:
|
|
540
|
+
*
|
|
541
|
+
* @example
|
|
542
|
+
* ```ts
|
|
543
|
+
* const subscription = actor.subscribe((snapshot) => {
|
|
544
|
+
* // ...
|
|
545
|
+
* });
|
|
546
|
+
*
|
|
547
|
+
* // Unsubscribe the observer
|
|
548
|
+
* subscription.unsubscribe();
|
|
549
|
+
* ```
|
|
550
|
+
*
|
|
551
|
+
* When the actor is stopped, all of its observers will automatically be unsubscribed.
|
|
552
|
+
*
|
|
553
|
+
* @param observer - Either a plain function that receives the latest snapshot, or an observer object whose `.next(snapshot)` method receives the latest snapshot
|
|
554
|
+
*/
|
|
555
|
+
|
|
556
|
+
subscribe(nextListenerOrObserver, errorListener, completeListener) {
|
|
557
|
+
const observer = toObserver(nextListenerOrObserver, errorListener, completeListener);
|
|
558
|
+
if (this._processingStatus !== ProcessingStatus.Stopped) {
|
|
559
|
+
this.observers.add(observer);
|
|
560
|
+
} else {
|
|
561
|
+
try {
|
|
562
|
+
observer.complete?.();
|
|
563
|
+
} catch (err) {
|
|
564
|
+
reportUnhandledError(err);
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
return {
|
|
568
|
+
unsubscribe: () => {
|
|
569
|
+
this.observers.delete(observer);
|
|
570
|
+
}
|
|
571
|
+
};
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
/**
|
|
575
|
+
* Starts the Actor from the initial state
|
|
576
|
+
*/
|
|
577
|
+
start() {
|
|
578
|
+
if (this._processingStatus === ProcessingStatus.Running) {
|
|
579
|
+
// Do not restart the service if it is already started
|
|
580
|
+
return this;
|
|
581
|
+
}
|
|
582
|
+
this.system._register(this.sessionId, this);
|
|
583
|
+
if (this._systemId) {
|
|
584
|
+
this.system._set(this._systemId, this);
|
|
585
|
+
}
|
|
586
|
+
this._processingStatus = ProcessingStatus.Running;
|
|
587
|
+
|
|
588
|
+
// TODO: this isn't correct when rehydrating
|
|
589
|
+
const initEvent = createInitEvent(this.options.input);
|
|
590
|
+
this.system._sendInspectionEvent({
|
|
591
|
+
type: '@xstate.event',
|
|
592
|
+
sourceRef: this._parent,
|
|
593
|
+
actorRef: this,
|
|
594
|
+
event: initEvent
|
|
595
|
+
});
|
|
596
|
+
const status = this._state.status;
|
|
597
|
+
switch (status) {
|
|
598
|
+
case 'done':
|
|
599
|
+
// a state machine can be "done" upon initialization (it could reach a final state using initial microsteps)
|
|
600
|
+
// we still need to complete observers, flush deferreds etc
|
|
601
|
+
this.update(this._state, initEvent);
|
|
602
|
+
// fallthrough
|
|
603
|
+
case 'error':
|
|
604
|
+
// TODO: rethink cleanup of observers, mailbox, etc
|
|
605
|
+
return this;
|
|
606
|
+
}
|
|
607
|
+
if (this.logic.start) {
|
|
608
|
+
try {
|
|
609
|
+
this.logic.start(this._state, this._actorScope);
|
|
610
|
+
} catch (err) {
|
|
611
|
+
this._stopProcedure();
|
|
612
|
+
this._error(err);
|
|
613
|
+
this._parent?.send(createErrorActorEvent(this.id, err));
|
|
614
|
+
return this;
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
// TODO: this notifies all subscribers but usually this is redundant
|
|
619
|
+
// there is no real change happening here
|
|
620
|
+
// we need to rethink if this needs to be refactored
|
|
621
|
+
this.update(this._state, initEvent);
|
|
622
|
+
if (this.options.devTools) {
|
|
623
|
+
this.attachDevTools();
|
|
624
|
+
}
|
|
625
|
+
this.mailbox.start();
|
|
626
|
+
return this;
|
|
627
|
+
}
|
|
628
|
+
_process(event) {
|
|
629
|
+
// TODO: reexamine what happens when an action (or a guard or smth) throws
|
|
630
|
+
let nextState;
|
|
631
|
+
let caughtError;
|
|
632
|
+
try {
|
|
633
|
+
nextState = this.logic.transition(this._state, event, this._actorScope);
|
|
634
|
+
} catch (err) {
|
|
635
|
+
// we wrap it in a box so we can rethrow it later even if falsy value gets caught here
|
|
636
|
+
caughtError = {
|
|
637
|
+
err
|
|
638
|
+
};
|
|
639
|
+
}
|
|
640
|
+
if (caughtError) {
|
|
641
|
+
const {
|
|
642
|
+
err
|
|
643
|
+
} = caughtError;
|
|
644
|
+
this._stopProcedure();
|
|
645
|
+
this._error(err);
|
|
646
|
+
this._parent?.send(createErrorActorEvent(this.id, err));
|
|
647
|
+
return;
|
|
648
|
+
}
|
|
649
|
+
this.update(nextState, event);
|
|
650
|
+
if (event.type === XSTATE_STOP) {
|
|
651
|
+
this._stopProcedure();
|
|
652
|
+
this._complete();
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
_stop() {
|
|
656
|
+
if (this._processingStatus === ProcessingStatus.Stopped) {
|
|
657
|
+
return this;
|
|
658
|
+
}
|
|
659
|
+
this.mailbox.clear();
|
|
660
|
+
if (this._processingStatus === ProcessingStatus.NotStarted) {
|
|
661
|
+
this._processingStatus = ProcessingStatus.Stopped;
|
|
662
|
+
return this;
|
|
663
|
+
}
|
|
664
|
+
this.mailbox.enqueue({
|
|
665
|
+
type: XSTATE_STOP
|
|
666
|
+
});
|
|
667
|
+
return this;
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
/**
|
|
671
|
+
* Stops the Actor and unsubscribe all listeners.
|
|
672
|
+
*/
|
|
673
|
+
stop() {
|
|
674
|
+
if (this._parent) {
|
|
675
|
+
throw new Error('A non-root actor cannot be stopped directly.');
|
|
676
|
+
}
|
|
677
|
+
return this._stop();
|
|
678
|
+
}
|
|
679
|
+
_complete() {
|
|
680
|
+
for (const observer of this.observers) {
|
|
681
|
+
try {
|
|
682
|
+
observer.complete?.();
|
|
683
|
+
} catch (err) {
|
|
684
|
+
reportUnhandledError(err);
|
|
685
|
+
}
|
|
686
|
+
}
|
|
687
|
+
this.observers.clear();
|
|
688
|
+
}
|
|
689
|
+
_error(err) {
|
|
690
|
+
if (!this.observers.size) {
|
|
691
|
+
if (!this._parent) {
|
|
692
|
+
reportUnhandledError(err);
|
|
693
|
+
}
|
|
694
|
+
return;
|
|
695
|
+
}
|
|
696
|
+
let reportError = false;
|
|
697
|
+
for (const observer of this.observers) {
|
|
698
|
+
const errorListener = observer.error;
|
|
699
|
+
reportError ||= !errorListener;
|
|
700
|
+
try {
|
|
701
|
+
errorListener?.(err);
|
|
702
|
+
} catch (err2) {
|
|
703
|
+
reportUnhandledError(err2);
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
this.observers.clear();
|
|
707
|
+
if (reportError) {
|
|
708
|
+
reportUnhandledError(err);
|
|
709
|
+
}
|
|
710
|
+
}
|
|
711
|
+
_stopProcedure() {
|
|
712
|
+
if (this._processingStatus !== ProcessingStatus.Running) {
|
|
713
|
+
// Actor already stopped; do nothing
|
|
714
|
+
return this;
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
// Cancel all delayed events
|
|
718
|
+
for (const key of Object.keys(this.delayedEventsMap)) {
|
|
719
|
+
this.clock.clearTimeout(this.delayedEventsMap[key]);
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
// TODO: mailbox.reset
|
|
723
|
+
this.mailbox.clear();
|
|
724
|
+
// TODO: after `stop` we must prepare ourselves for receiving events again
|
|
725
|
+
// events sent *after* stop signal must be queued
|
|
726
|
+
// it seems like this should be the common behavior for all of our consumers
|
|
727
|
+
// so perhaps this should be unified somehow for all of them
|
|
728
|
+
this.mailbox = new Mailbox(this._process.bind(this));
|
|
729
|
+
this._processingStatus = ProcessingStatus.Stopped;
|
|
730
|
+
this.system._unregister(this);
|
|
731
|
+
return this;
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
/**
|
|
735
|
+
* @internal
|
|
736
|
+
*/
|
|
737
|
+
_send(event) {
|
|
738
|
+
if (this._processingStatus === ProcessingStatus.Stopped) {
|
|
739
|
+
// do nothing
|
|
740
|
+
{
|
|
741
|
+
const eventString = JSON.stringify(event);
|
|
742
|
+
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}`);
|
|
743
|
+
}
|
|
744
|
+
return;
|
|
745
|
+
}
|
|
746
|
+
this.mailbox.enqueue(event);
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
/**
|
|
750
|
+
* Sends an event to the running Actor to trigger a transition.
|
|
751
|
+
*
|
|
752
|
+
* @param event The event to send
|
|
753
|
+
*/
|
|
754
|
+
send(event) {
|
|
755
|
+
if (typeof event === 'string') {
|
|
756
|
+
throw new Error(`Only event objects may be sent to actors; use .send({ type: "${event}" }) instead`);
|
|
757
|
+
}
|
|
758
|
+
this.system._relay(undefined, this, event);
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
/**
|
|
762
|
+
* TODO: figure out a way to do this within the machine
|
|
763
|
+
* @internal
|
|
764
|
+
*/
|
|
765
|
+
delaySend(params) {
|
|
766
|
+
const {
|
|
767
|
+
event,
|
|
768
|
+
id,
|
|
769
|
+
delay
|
|
770
|
+
} = params;
|
|
771
|
+
const timerId = this.clock.setTimeout(() => {
|
|
772
|
+
this.system._relay(this, params.to ?? this, event);
|
|
773
|
+
}, delay);
|
|
774
|
+
|
|
775
|
+
// TODO: consider the rehydration story here
|
|
776
|
+
if (id) {
|
|
777
|
+
this.delayedEventsMap[id] = timerId;
|
|
778
|
+
}
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
/**
|
|
782
|
+
* TODO: figure out a way to do this within the machine
|
|
783
|
+
* @internal
|
|
784
|
+
*/
|
|
785
|
+
cancel(sendId) {
|
|
786
|
+
this.clock.clearTimeout(this.delayedEventsMap[sendId]);
|
|
787
|
+
delete this.delayedEventsMap[sendId];
|
|
788
|
+
}
|
|
789
|
+
attachDevTools() {
|
|
790
|
+
const {
|
|
791
|
+
devTools
|
|
792
|
+
} = this.options;
|
|
793
|
+
if (devTools) {
|
|
794
|
+
const resolvedDevToolsAdapter = typeof devTools === 'function' ? devTools : dev_dist_xstateDev.devToolsAdapter;
|
|
795
|
+
resolvedDevToolsAdapter(this);
|
|
796
|
+
}
|
|
797
|
+
}
|
|
798
|
+
toJSON() {
|
|
799
|
+
return {
|
|
800
|
+
xstate$$type: $$ACTOR_TYPE,
|
|
801
|
+
id: this.id
|
|
802
|
+
};
|
|
803
|
+
}
|
|
804
|
+
|
|
805
|
+
/**
|
|
806
|
+
* Obtain the internal state of the actor, which can be persisted.
|
|
807
|
+
*
|
|
808
|
+
* @remarks
|
|
809
|
+
* The internal state can be persisted from any actor, not only machines.
|
|
810
|
+
*
|
|
811
|
+
* 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.
|
|
812
|
+
*
|
|
813
|
+
* Can be restored with {@link ActorOptions.state}
|
|
814
|
+
*
|
|
815
|
+
* @see https://stately.ai/docs/persistence
|
|
816
|
+
*/
|
|
817
|
+
|
|
818
|
+
getPersistedState(options) {
|
|
819
|
+
return this.logic.getPersistedState(this._state, options);
|
|
820
|
+
}
|
|
821
|
+
[symbolObservable]() {
|
|
822
|
+
return this;
|
|
823
|
+
}
|
|
824
|
+
|
|
825
|
+
/**
|
|
826
|
+
* Read an actor’s snapshot synchronously.
|
|
827
|
+
*
|
|
828
|
+
* @remarks
|
|
829
|
+
* The snapshot represent an actor's last emitted value.
|
|
830
|
+
*
|
|
831
|
+
* When an actor receives an event, its internal state may change.
|
|
832
|
+
* An actor may emit a snapshot when a state transition occurs.
|
|
833
|
+
*
|
|
834
|
+
* Note that some actors, such as callback actors generated with `fromCallback`, will not emit snapshots.
|
|
835
|
+
*
|
|
836
|
+
* @see {@link Actor.subscribe} to subscribe to an actor’s snapshot values.
|
|
837
|
+
* @see {@link Actor.getPersistedState} to persist the internal state of an actor (which is more than just a snapshot).
|
|
838
|
+
*/
|
|
839
|
+
getSnapshot() {
|
|
840
|
+
return this._state;
|
|
841
|
+
}
|
|
842
|
+
}
|
|
843
|
+
|
|
844
|
+
/**
|
|
845
|
+
* Creates a new `ActorRef` instance for the given machine with the provided options, if any.
|
|
846
|
+
*
|
|
847
|
+
* @param machine The machine to create an actor from
|
|
848
|
+
* @param options `ActorRef` options
|
|
849
|
+
*/
|
|
850
|
+
|
|
851
|
+
function createActor(logic, options) {
|
|
852
|
+
const interpreter = new Actor(logic, options);
|
|
853
|
+
return interpreter;
|
|
854
|
+
}
|
|
855
|
+
|
|
856
|
+
/**
|
|
857
|
+
* Creates a new Interpreter instance for the given machine with the provided options, if any.
|
|
858
|
+
*
|
|
859
|
+
* @deprecated Use `createActor` instead
|
|
860
|
+
*/
|
|
861
|
+
const interpret = createActor;
|
|
862
|
+
|
|
863
|
+
/**
|
|
864
|
+
* @deprecated Use `Actor` instead.
|
|
865
|
+
*/
|
|
4
866
|
|
|
5
867
|
const cache = new WeakMap();
|
|
6
868
|
function memo(object, key, fn) {
|
|
@@ -52,11 +914,11 @@ function resolveSpawn(actorScope, state, actionArgs, _actionParams, {
|
|
|
52
914
|
input,
|
|
53
915
|
syncSnapshot
|
|
54
916
|
}) {
|
|
55
|
-
const logic = typeof src === 'string' ?
|
|
917
|
+
const logic = typeof src === 'string' ? resolveReferencedActor(state.machine, src) : src;
|
|
56
918
|
const resolvedId = typeof id === 'function' ? id(actionArgs) : id;
|
|
57
919
|
let actorRef;
|
|
58
920
|
if (logic) {
|
|
59
|
-
actorRef =
|
|
921
|
+
actorRef = createActor(logic, {
|
|
60
922
|
id: resolvedId,
|
|
61
923
|
src,
|
|
62
924
|
parent: actorScope?.self,
|
|
@@ -102,13 +964,13 @@ function executeSpawn(actorScope, {
|
|
|
102
964
|
return;
|
|
103
965
|
}
|
|
104
966
|
actorScope.defer(() => {
|
|
105
|
-
if (actorRef._processingStatus ===
|
|
967
|
+
if (actorRef._processingStatus === ProcessingStatus.Stopped) {
|
|
106
968
|
return;
|
|
107
969
|
}
|
|
108
970
|
try {
|
|
109
971
|
actorRef.start?.();
|
|
110
972
|
} catch (err) {
|
|
111
|
-
actorScope.self.send(
|
|
973
|
+
actorScope.self.send(createErrorActorEvent(id, err));
|
|
112
974
|
return;
|
|
113
975
|
}
|
|
114
976
|
});
|
|
@@ -163,7 +1025,7 @@ function executeStop(actorScope, actorRef) {
|
|
|
163
1025
|
|
|
164
1026
|
// this allows us to prevent an actor from being started if it gets stopped within the same macrostep
|
|
165
1027
|
// this can happen, for example, when the invoking state is being exited immediately by an always transition
|
|
166
|
-
if (actorRef._processingStatus !==
|
|
1028
|
+
if (actorRef._processingStatus !== ProcessingStatus.Running) {
|
|
167
1029
|
actorScope.stopChild(actorRef);
|
|
168
1030
|
return;
|
|
169
1031
|
}
|
|
@@ -404,12 +1266,12 @@ function isInFinalState(configuration, stateNode) {
|
|
|
404
1266
|
}
|
|
405
1267
|
return stateNode.type === 'final';
|
|
406
1268
|
}
|
|
407
|
-
const isStateId = str => str[0] ===
|
|
1269
|
+
const isStateId = str => str[0] === STATE_IDENTIFIER;
|
|
408
1270
|
function getCandidates(stateNode, receivedEventType) {
|
|
409
1271
|
const candidates = stateNode.transitions.get(receivedEventType) || [...stateNode.transitions.keys()].filter(descriptor => {
|
|
410
1272
|
// check if transition is a wildcard transition,
|
|
411
1273
|
// which matches any non-transient events
|
|
412
|
-
if (descriptor ===
|
|
1274
|
+
if (descriptor === WILDCARD) {
|
|
413
1275
|
return true;
|
|
414
1276
|
}
|
|
415
1277
|
if (!descriptor.endsWith('.*')) {
|
|
@@ -449,7 +1311,7 @@ function getDelayedTransitions(stateNode) {
|
|
|
449
1311
|
}
|
|
450
1312
|
const mutateEntryExit = (delay, i) => {
|
|
451
1313
|
const delayRef = typeof delay === 'function' ? `${stateNode.id}:delay[${i}]` : delay;
|
|
452
|
-
const afterEvent =
|
|
1314
|
+
const afterEvent = createAfterEvent(delayRef, stateNode.id);
|
|
453
1315
|
const eventType = afterEvent.type;
|
|
454
1316
|
stateNode.entry.push(raise(afterEvent, {
|
|
455
1317
|
id: eventType,
|
|
@@ -465,7 +1327,7 @@ function getDelayedTransitions(stateNode) {
|
|
|
465
1327
|
} : configTransition;
|
|
466
1328
|
const resolvedDelay = !isNaN(+delay) ? +delay : delay;
|
|
467
1329
|
const eventType = mutateEntryExit(resolvedDelay, i);
|
|
468
|
-
return
|
|
1330
|
+
return toArray(resolvedTransition).map(transition => ({
|
|
469
1331
|
...transition,
|
|
470
1332
|
event: eventType,
|
|
471
1333
|
delay: resolvedDelay
|
|
@@ -482,7 +1344,7 @@ function getDelayedTransitions(stateNode) {
|
|
|
482
1344
|
});
|
|
483
1345
|
}
|
|
484
1346
|
function formatTransition(stateNode, descriptor, transitionConfig) {
|
|
485
|
-
const normalizedTarget =
|
|
1347
|
+
const normalizedTarget = normalizeTarget(transitionConfig.target);
|
|
486
1348
|
const reenter = transitionConfig.reenter ?? false;
|
|
487
1349
|
const target = resolveTarget(stateNode, normalizedTarget);
|
|
488
1350
|
|
|
@@ -492,7 +1354,7 @@ function formatTransition(stateNode, descriptor, transitionConfig) {
|
|
|
492
1354
|
}
|
|
493
1355
|
const transition = {
|
|
494
1356
|
...transitionConfig,
|
|
495
|
-
actions:
|
|
1357
|
+
actions: toArray(transitionConfig.actions),
|
|
496
1358
|
guard: transitionConfig.guard,
|
|
497
1359
|
target,
|
|
498
1360
|
source: stateNode,
|
|
@@ -510,29 +1372,29 @@ function formatTransitions(stateNode) {
|
|
|
510
1372
|
const transitions = new Map();
|
|
511
1373
|
if (stateNode.config.on) {
|
|
512
1374
|
for (const descriptor of Object.keys(stateNode.config.on)) {
|
|
513
|
-
if (descriptor ===
|
|
1375
|
+
if (descriptor === NULL_EVENT) {
|
|
514
1376
|
throw new Error('Null events ("") cannot be specified as a transition key. Use `always: { ... }` instead.');
|
|
515
1377
|
}
|
|
516
1378
|
const transitionsConfig = stateNode.config.on[descriptor];
|
|
517
|
-
transitions.set(descriptor,
|
|
1379
|
+
transitions.set(descriptor, toTransitionConfigArray(transitionsConfig).map(t => formatTransition(stateNode, descriptor, t)));
|
|
518
1380
|
}
|
|
519
1381
|
}
|
|
520
1382
|
if (stateNode.config.onDone) {
|
|
521
1383
|
const descriptor = `xstate.done.state.${stateNode.id}`;
|
|
522
|
-
transitions.set(descriptor,
|
|
1384
|
+
transitions.set(descriptor, toTransitionConfigArray(stateNode.config.onDone).map(t => formatTransition(stateNode, descriptor, t)));
|
|
523
1385
|
}
|
|
524
1386
|
for (const invokeDef of stateNode.invoke) {
|
|
525
1387
|
if (invokeDef.onDone) {
|
|
526
1388
|
const descriptor = `xstate.done.actor.${invokeDef.id}`;
|
|
527
|
-
transitions.set(descriptor,
|
|
1389
|
+
transitions.set(descriptor, toTransitionConfigArray(invokeDef.onDone).map(t => formatTransition(stateNode, descriptor, t)));
|
|
528
1390
|
}
|
|
529
1391
|
if (invokeDef.onError) {
|
|
530
1392
|
const descriptor = `xstate.error.actor.${invokeDef.id}`;
|
|
531
|
-
transitions.set(descriptor,
|
|
1393
|
+
transitions.set(descriptor, toTransitionConfigArray(invokeDef.onError).map(t => formatTransition(stateNode, descriptor, t)));
|
|
532
1394
|
}
|
|
533
1395
|
if (invokeDef.onSnapshot) {
|
|
534
1396
|
const descriptor = `xstate.snapshot.${invokeDef.id}`;
|
|
535
|
-
transitions.set(descriptor,
|
|
1397
|
+
transitions.set(descriptor, toTransitionConfigArray(invokeDef.onSnapshot).map(t => formatTransition(stateNode, descriptor, t)));
|
|
536
1398
|
}
|
|
537
1399
|
}
|
|
538
1400
|
for (const delayedTransition of stateNode.after) {
|
|
@@ -552,7 +1414,7 @@ function formatInitialTransition(stateNode, _target) {
|
|
|
552
1414
|
}
|
|
553
1415
|
const transition = {
|
|
554
1416
|
source: stateNode,
|
|
555
|
-
actions: !_target || typeof _target === 'string' ? [] :
|
|
1417
|
+
actions: !_target || typeof _target === 'string' ? [] : toArray(_target.actions),
|
|
556
1418
|
eventType: null,
|
|
557
1419
|
reenter: false,
|
|
558
1420
|
target: resolvedTarget ? [resolvedTarget] : [],
|
|
@@ -576,7 +1438,7 @@ function resolveTarget(stateNode, targets) {
|
|
|
576
1438
|
if (isStateId(target)) {
|
|
577
1439
|
return stateNode.machine.getStateNodeById(target);
|
|
578
1440
|
}
|
|
579
|
-
const isInternalTarget = target[0] ===
|
|
1441
|
+
const isInternalTarget = target[0] === STATE_DELIMITER;
|
|
580
1442
|
// If internal target is defined on machine,
|
|
581
1443
|
// do not include machine key on target
|
|
582
1444
|
if (isInternalTarget && !stateNode.parent) {
|
|
@@ -596,7 +1458,7 @@ function resolveTarget(stateNode, targets) {
|
|
|
596
1458
|
});
|
|
597
1459
|
}
|
|
598
1460
|
function resolveHistoryDefaultTransition(stateNode) {
|
|
599
|
-
const normalizedTarget =
|
|
1461
|
+
const normalizedTarget = normalizeTarget(stateNode.config.target);
|
|
600
1462
|
if (!normalizedTarget) {
|
|
601
1463
|
return stateNode.parent.initial;
|
|
602
1464
|
}
|
|
@@ -665,7 +1527,7 @@ function getStateNodeByPath(stateNode, statePath) {
|
|
|
665
1527
|
// throw e;
|
|
666
1528
|
}
|
|
667
1529
|
}
|
|
668
|
-
const arrayStatePath =
|
|
1530
|
+
const arrayStatePath = toStatePath(statePath).slice();
|
|
669
1531
|
let currentStateNode = stateNode;
|
|
670
1532
|
while (arrayStatePath.length) {
|
|
671
1533
|
const key = arrayStatePath.shift();
|
|
@@ -917,8 +1779,8 @@ function getMachineOutput(state, event, actorScope, rootNode, rootCompletionNode
|
|
|
917
1779
|
if (!rootNode.output) {
|
|
918
1780
|
return;
|
|
919
1781
|
}
|
|
920
|
-
const doneStateEvent =
|
|
921
|
-
return
|
|
1782
|
+
const doneStateEvent = createDoneStateEvent(rootCompletionNode.id, rootCompletionNode.output && rootCompletionNode.parent ? resolveOutput(rootCompletionNode.output, state.context, event, actorScope.self) : undefined);
|
|
1783
|
+
return resolveOutput(rootNode.output, state.context, doneStateEvent, actorScope.self);
|
|
922
1784
|
}
|
|
923
1785
|
function enterStates(currentState, event, actorScope, filteredTransitions, mutConfiguration, internalQueue, historyValue, isInitial) {
|
|
924
1786
|
let nextState = currentState;
|
|
@@ -956,11 +1818,11 @@ function enterStates(currentState, event, actorScope, filteredTransitions, mutCo
|
|
|
956
1818
|
let ancestorMarker = parent?.type === 'parallel' ? parent : parent?.parent;
|
|
957
1819
|
let rootCompletionNode = ancestorMarker || stateNodeToEnter;
|
|
958
1820
|
if (parent?.type === 'compound') {
|
|
959
|
-
internalQueue.push(
|
|
1821
|
+
internalQueue.push(createDoneStateEvent(parent.id, stateNodeToEnter.output ? resolveOutput(stateNodeToEnter.output, nextState.context, event, actorScope.self) : undefined));
|
|
960
1822
|
}
|
|
961
1823
|
while (ancestorMarker?.type === 'parallel' && !completedNodes.has(ancestorMarker) && isInFinalState(mutConfiguration, ancestorMarker)) {
|
|
962
1824
|
completedNodes.add(ancestorMarker);
|
|
963
|
-
internalQueue.push(
|
|
1825
|
+
internalQueue.push(createDoneStateEvent(ancestorMarker.id));
|
|
964
1826
|
rootCompletionNode = ancestorMarker;
|
|
965
1827
|
ancestorMarker = ancestorMarker.parent;
|
|
966
1828
|
}
|
|
@@ -1123,7 +1985,7 @@ function resolveActionsAndContextWorker(currentState, event, actorScope, actions
|
|
|
1123
1985
|
event
|
|
1124
1986
|
}) : action.params : undefined;
|
|
1125
1987
|
if (!('resolve' in resolvedAction)) {
|
|
1126
|
-
if (actorScope?.self._processingStatus ===
|
|
1988
|
+
if (actorScope?.self._processingStatus === ProcessingStatus.Running) {
|
|
1127
1989
|
resolvedAction(actionArgs, actionParams);
|
|
1128
1990
|
} else {
|
|
1129
1991
|
actorScope?.defer(() => {
|
|
@@ -1141,7 +2003,7 @@ function resolveActionsAndContextWorker(currentState, event, actorScope, actions
|
|
|
1141
2003
|
retries?.push([builtinAction, params]);
|
|
1142
2004
|
}
|
|
1143
2005
|
if ('execute' in builtinAction) {
|
|
1144
|
-
if (actorScope?.self._processingStatus ===
|
|
2006
|
+
if (actorScope?.self._processingStatus === ProcessingStatus.Running) {
|
|
1145
2007
|
builtinAction.execute(actorScope, params);
|
|
1146
2008
|
} else {
|
|
1147
2009
|
actorScope?.defer(builtinAction.execute.bind(null, actorScope, params));
|
|
@@ -1165,14 +2027,14 @@ function resolveActionsAndContext(currentState, event, actorScope, actions, inte
|
|
|
1165
2027
|
return nextState;
|
|
1166
2028
|
}
|
|
1167
2029
|
function macrostep(state, event, actorScope, internalQueue = []) {
|
|
1168
|
-
if (event.type ===
|
|
1169
|
-
throw new Error(`An event cannot have the wildcard type ('${
|
|
2030
|
+
if (event.type === WILDCARD) {
|
|
2031
|
+
throw new Error(`An event cannot have the wildcard type ('${WILDCARD}')`);
|
|
1170
2032
|
}
|
|
1171
2033
|
let nextState = state;
|
|
1172
2034
|
const states = [];
|
|
1173
2035
|
|
|
1174
2036
|
// Handle stop event
|
|
1175
|
-
if (event.type ===
|
|
2037
|
+
if (event.type === XSTATE_STOP) {
|
|
1176
2038
|
nextState = cloneMachineSnapshot(stopChildren(nextState, event, actorScope), {
|
|
1177
2039
|
status: 'stopped'
|
|
1178
2040
|
});
|
|
@@ -1186,7 +2048,7 @@ function macrostep(state, event, actorScope, internalQueue = []) {
|
|
|
1186
2048
|
|
|
1187
2049
|
// Assume the state is at rest (no raised events)
|
|
1188
2050
|
// Determine the next state based on the next microstep
|
|
1189
|
-
if (nextEvent.type !==
|
|
2051
|
+
if (nextEvent.type !== XSTATE_INIT) {
|
|
1190
2052
|
const transitions = selectTransitions(nextEvent, nextState);
|
|
1191
2053
|
nextState = microstep(transitions, state, actorScope, nextEvent, false, internalQueue);
|
|
1192
2054
|
states.push(nextState);
|
|
@@ -1252,8 +2114,11 @@ function resolveStateValue(rootNode, stateValue) {
|
|
|
1252
2114
|
return getStateValue(rootNode, [...configuration]);
|
|
1253
2115
|
}
|
|
1254
2116
|
|
|
2117
|
+
function isMachineSnapshot(value) {
|
|
2118
|
+
return !!value && typeof value === 'object' && 'machine' in value && 'value' in value;
|
|
2119
|
+
}
|
|
1255
2120
|
const machineSnapshotMatches = function matches(testValue) {
|
|
1256
|
-
return
|
|
2121
|
+
return matchesState(testValue, this.value);
|
|
1257
2122
|
};
|
|
1258
2123
|
const machineSnapshotHasTag = function hasTag(tag) {
|
|
1259
2124
|
return this.tags.has(tag);
|
|
@@ -1286,7 +2151,7 @@ const machineSnapshotToJSON = function toJSON() {
|
|
|
1286
2151
|
};
|
|
1287
2152
|
const machineSnapshotNextEvents = function nextEvents() {
|
|
1288
2153
|
return memo(this, 'nextEvents', () => {
|
|
1289
|
-
return [...new Set(
|
|
2154
|
+
return [...new Set(flatten([...this.configuration.map(sn => sn.ownEvents)]))];
|
|
1290
2155
|
});
|
|
1291
2156
|
};
|
|
1292
2157
|
const machineSnapshotMeta = function nextEvents() {
|
|
@@ -1306,7 +2171,7 @@ function createMachineSnapshot(config, machine) {
|
|
|
1306
2171
|
context: config.context,
|
|
1307
2172
|
configuration: config.configuration,
|
|
1308
2173
|
value: getStateValue(machine.root, config.configuration),
|
|
1309
|
-
tags: new Set(
|
|
2174
|
+
tags: new Set(flatten(config.configuration.map(sn => sn.tags))),
|
|
1310
2175
|
children: config.children,
|
|
1311
2176
|
historyValue: config.historyValue || {},
|
|
1312
2177
|
// this one is generic in the target and it's hard to create a matching non-generic source signature
|
|
@@ -1380,7 +2245,7 @@ function persistContext(contextPart) {
|
|
|
1380
2245
|
...contextPart
|
|
1381
2246
|
};
|
|
1382
2247
|
copy[key] = {
|
|
1383
|
-
xstate$$type:
|
|
2248
|
+
xstate$$type: $$ACTOR_TYPE,
|
|
1384
2249
|
id: value.id
|
|
1385
2250
|
};
|
|
1386
2251
|
} else {
|
|
@@ -1452,9 +2317,20 @@ function raise(eventOrExpr, options) {
|
|
|
1452
2317
|
return raise;
|
|
1453
2318
|
}
|
|
1454
2319
|
|
|
2320
|
+
exports.$$ACTOR_TYPE = $$ACTOR_TYPE;
|
|
2321
|
+
exports.Actor = Actor;
|
|
2322
|
+
exports.NULL_EVENT = NULL_EVENT;
|
|
2323
|
+
exports.ProcessingStatus = ProcessingStatus;
|
|
2324
|
+
exports.STATE_DELIMITER = STATE_DELIMITER;
|
|
2325
|
+
exports.XSTATE_ERROR = XSTATE_ERROR;
|
|
2326
|
+
exports.XSTATE_STOP = XSTATE_STOP;
|
|
1455
2327
|
exports.and = and;
|
|
1456
2328
|
exports.cancel = cancel;
|
|
1457
2329
|
exports.cloneMachineSnapshot = cloneMachineSnapshot;
|
|
2330
|
+
exports.createActor = createActor;
|
|
2331
|
+
exports.createErrorActorEvent = createErrorActorEvent;
|
|
2332
|
+
exports.createInitEvent = createInitEvent;
|
|
2333
|
+
exports.createInvokeId = createInvokeId;
|
|
1458
2334
|
exports.createMachineSnapshot = createMachineSnapshot;
|
|
1459
2335
|
exports.evaluateGuard = evaluateGuard;
|
|
1460
2336
|
exports.formatInitialTransition = formatInitialTransition;
|
|
@@ -1467,17 +2343,27 @@ exports.getInitialStateNodes = getInitialStateNodes;
|
|
|
1467
2343
|
exports.getPersistedState = getPersistedState;
|
|
1468
2344
|
exports.getStateNodeByPath = getStateNodeByPath;
|
|
1469
2345
|
exports.getStateNodes = getStateNodes;
|
|
2346
|
+
exports.interpret = interpret;
|
|
2347
|
+
exports.isErrorActorEvent = isErrorActorEvent;
|
|
1470
2348
|
exports.isInFinalState = isInFinalState;
|
|
2349
|
+
exports.isMachineSnapshot = isMachineSnapshot;
|
|
1471
2350
|
exports.isStateId = isStateId;
|
|
1472
2351
|
exports.macrostep = macrostep;
|
|
2352
|
+
exports.mapValues = mapValues;
|
|
2353
|
+
exports.matchesState = matchesState;
|
|
1473
2354
|
exports.memo = memo;
|
|
1474
2355
|
exports.microstep = microstep;
|
|
1475
2356
|
exports.not = not;
|
|
1476
2357
|
exports.or = or;
|
|
2358
|
+
exports.pathToStateValue = pathToStateValue;
|
|
1477
2359
|
exports.raise = raise;
|
|
1478
2360
|
exports.resolveActionsAndContext = resolveActionsAndContext;
|
|
2361
|
+
exports.resolveReferencedActor = resolveReferencedActor;
|
|
1479
2362
|
exports.resolveStateValue = resolveStateValue;
|
|
1480
2363
|
exports.spawn = spawn;
|
|
1481
2364
|
exports.stateIn = stateIn;
|
|
1482
2365
|
exports.stop = stop;
|
|
2366
|
+
exports.toArray = toArray;
|
|
2367
|
+
exports.toObserver = toObserver;
|
|
2368
|
+
exports.toTransitionConfigArray = toTransitionConfigArray;
|
|
1483
2369
|
exports.transitionNode = transitionNode;
|