@systemfsoftware/xstate 0.0.0-bootstrap.0 → 6.0.0-beta.65
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/CHANGELOG.md +57 -0
- package/LICENSE +22 -0
- package/README.md +640 -2
- package/dist/actors-LxMBvgxO.mjs +472 -0
- package/dist/actors.d.ts +3 -0
- package/dist/actors.mjs +3 -0
- package/dist/base.types-DF8ZPiXj.d.ts +210 -0
- package/dist/createActor-BGHd7i8Q.mjs +4851 -0
- package/dist/durable.d.ts +211 -0
- package/dist/durable.mjs +283 -0
- package/dist/effectDescriptor-EvNuy6U3.d.ts +91 -0
- package/dist/fsm.d.ts +139 -0
- package/dist/fsm.mjs +60 -0
- package/dist/graph.d.ts +160 -0
- package/dist/graph.mjs +667 -0
- package/dist/index-CKUyiMZC.d.ts +1832 -0
- package/dist/index-DDWoKIF5.d.ts +512 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.mjs +7 -0
- package/dist/schema.types-tsIbgQd-.mjs +46 -0
- package/dist/src-zLWug7SF.mjs +2641 -0
- package/dist/transition-ZMXyPI-x.mjs +258 -0
- package/dist/types-B2EAwOk-.d.ts +3511 -0
- package/dist/utils-CXBxxp3Z.mjs +146 -0
- package/dist/validation.d.ts +49 -0
- package/dist/validation.mjs +216 -0
- package/package.json +81 -5
|
@@ -0,0 +1,4851 @@
|
|
|
1
|
+
import { C as XSTATE_TERMINATE, S as XSTATE_STOP, _ as XSTATE_LOGIC_EFFECT_REJECT, a as isActorRefLike, b as XSTATE_SPAWN, c as matchesEventDescriptor, d as resolveReferencedActor, f as toArray, g as XSTATE_INIT, h as defaultWarn, i as getEventOutput, l as normalizeTarget, m as toTransitionConfigArray, o as isErrorEvent, p as toObserver, s as mapValues, t as createInvokeId, u as resolveOutput, v as XSTATE_LOGIC_EFFECT_RESOLVE, w as XSTATE_TIMER, x as XSTATE_START, y as XSTATE_LOGIC_EFFECT_START } from "./utils-CXBxxp3Z.mjs";
|
|
2
|
+
// src/eventUtils.ts
|
|
3
|
+
/**
|
|
4
|
+
* Returns an event that represents an implicit event that is sent after the
|
|
5
|
+
* specified `delay`.
|
|
6
|
+
*
|
|
7
|
+
* @param delayRef The delay in milliseconds
|
|
8
|
+
* @param id The state node ID where this event is handled
|
|
9
|
+
*/
|
|
10
|
+
function createAfterEvent(delayRef, id) {
|
|
11
|
+
return {
|
|
12
|
+
type: "xstate.after",
|
|
13
|
+
delay: delayRef,
|
|
14
|
+
stateId: id
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
function createAfterEventId(delayRef, id) {
|
|
18
|
+
return `xstate.after.${delayRef}.${id}`;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Returns an event that represents an implicit state-level timeout. Fired when
|
|
22
|
+
* a state's `timeout` duration elapses without the state being exited.
|
|
23
|
+
*
|
|
24
|
+
* @param id The state node ID where this timeout is configured
|
|
25
|
+
*/
|
|
26
|
+
function createTimeoutEvent(id) {
|
|
27
|
+
return {
|
|
28
|
+
type: "xstate.timeout",
|
|
29
|
+
stateId: id
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
function createTimeoutEventId(id) {
|
|
33
|
+
return `xstate.timeout.${id}`;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Returns an event that represents an implicit invoke-level timeout. Fired when
|
|
37
|
+
* an invoked actor has not completed within its `timeout` duration.
|
|
38
|
+
*
|
|
39
|
+
* @param invokeId The invoked actor's ID
|
|
40
|
+
*/
|
|
41
|
+
function createInvokeTimeoutEvent(invokeId, sessionId) {
|
|
42
|
+
return {
|
|
43
|
+
type: "xstate.timeout.actor",
|
|
44
|
+
actorId: invokeId,
|
|
45
|
+
sessionId
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
function createInvokeTimeoutEventId(invokeId) {
|
|
49
|
+
return `xstate.timeout.actor.${invokeId}`;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Returns an event that represents that a final state node has been reached in
|
|
53
|
+
* the parent state node.
|
|
54
|
+
*
|
|
55
|
+
* @param id The final state node's parent state node `id`
|
|
56
|
+
* @param output The data to pass into the event
|
|
57
|
+
*/
|
|
58
|
+
function createDoneStateEvent(id, output) {
|
|
59
|
+
return {
|
|
60
|
+
type: "xstate.done.state",
|
|
61
|
+
stateId: id,
|
|
62
|
+
output
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Returns an event that represents that an invoked service has terminated.
|
|
67
|
+
*
|
|
68
|
+
* An invoked service is terminated when it has reached a top-level final state
|
|
69
|
+
* node, but not when it is canceled.
|
|
70
|
+
*
|
|
71
|
+
* @param invokeId The invoked service ID
|
|
72
|
+
* @param output The data to pass into the event
|
|
73
|
+
* @param sessionId The unique session ID of the completed actor
|
|
74
|
+
*/
|
|
75
|
+
function createDoneActorEvent(invokeId, output, sessionId) {
|
|
76
|
+
return {
|
|
77
|
+
type: "xstate.done.actor",
|
|
78
|
+
output,
|
|
79
|
+
actorId: invokeId,
|
|
80
|
+
sessionId
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
function createErrorActorEvent(id, error, sessionId) {
|
|
84
|
+
return {
|
|
85
|
+
type: "xstate.error.actor",
|
|
86
|
+
error,
|
|
87
|
+
actorId: id,
|
|
88
|
+
sessionId
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
function createErrorPlatformEvent(kind, error) {
|
|
92
|
+
return {
|
|
93
|
+
type: `xstate.error.${kind}`,
|
|
94
|
+
error
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
function createInitEvent(input) {
|
|
98
|
+
return {
|
|
99
|
+
type: XSTATE_INIT,
|
|
100
|
+
input
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
// src/Mailbox.ts
|
|
104
|
+
var Mailbox = class {
|
|
105
|
+
_process;
|
|
106
|
+
_active = false;
|
|
107
|
+
_current = null;
|
|
108
|
+
_last = null;
|
|
109
|
+
constructor(_process) {
|
|
110
|
+
this._process = _process;
|
|
111
|
+
}
|
|
112
|
+
start() {
|
|
113
|
+
this._active = true;
|
|
114
|
+
this.flush();
|
|
115
|
+
}
|
|
116
|
+
clear() {
|
|
117
|
+
if (this._current) {
|
|
118
|
+
this._current.next = null;
|
|
119
|
+
this._last = this._current;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
enqueue(event) {
|
|
123
|
+
const enqueued = {
|
|
124
|
+
value: event,
|
|
125
|
+
next: null
|
|
126
|
+
};
|
|
127
|
+
if (this._current) {
|
|
128
|
+
this._last.next = enqueued;
|
|
129
|
+
this._last = enqueued;
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
this._current = enqueued;
|
|
133
|
+
this._last = enqueued;
|
|
134
|
+
if (this._active) this.flush();
|
|
135
|
+
}
|
|
136
|
+
flush() {
|
|
137
|
+
while (this._current) {
|
|
138
|
+
const consumed = this._current;
|
|
139
|
+
this._process(consumed.value);
|
|
140
|
+
this._current = consumed.next;
|
|
141
|
+
}
|
|
142
|
+
this._last = null;
|
|
143
|
+
}
|
|
144
|
+
};
|
|
145
|
+
// src/symbolObservable.ts
|
|
146
|
+
const symbolObservable = (() => typeof Symbol === "function" && Symbol.observable || "@@observable")();
|
|
147
|
+
// src/inspectionAmbient.ts
|
|
148
|
+
/**
|
|
149
|
+
* The ambiently installed system inspector. Set for the duration of a durable
|
|
150
|
+
* execution's transitions (`createDurable(..., { inspect })`), it makes the
|
|
151
|
+
* pure-transition machinery observable: freshly constructed runtime systems
|
|
152
|
+
* attach it, and snapshot systems created while it is set forward inspection
|
|
153
|
+
* to their base system instead of stubbing it out.
|
|
154
|
+
*/
|
|
155
|
+
let ambientInspector;
|
|
156
|
+
/** @internal */
|
|
157
|
+
function hasAmbientInspector() {
|
|
158
|
+
return ambientInspector !== void 0;
|
|
159
|
+
}
|
|
160
|
+
/** @internal */
|
|
161
|
+
function getAmbientInspector() {
|
|
162
|
+
return ambientInspector;
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Runs `fn` with systems it creates wired to `inspect` from construction, so
|
|
166
|
+
* creation-time inspection events (the root actor's own registration, actors
|
|
167
|
+
* spawned during the first transition) are observed too.
|
|
168
|
+
*
|
|
169
|
+
* @internal
|
|
170
|
+
*/
|
|
171
|
+
function withSystemInspector(inspect, fn) {
|
|
172
|
+
if (!inspect) return fn();
|
|
173
|
+
const previous = ambientInspector;
|
|
174
|
+
ambientInspector = inspect;
|
|
175
|
+
try {
|
|
176
|
+
return fn();
|
|
177
|
+
} finally {
|
|
178
|
+
ambientInspector = previous;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
// src/reportUnhandledError.ts
|
|
182
|
+
/**
|
|
183
|
+
* This function makes sure that unhandled errors are thrown in a separate
|
|
184
|
+
* macrotask. It allows those errors to be detected by global error handlers and
|
|
185
|
+
* reported to bug tracking services without interrupting our own stack of
|
|
186
|
+
* execution.
|
|
187
|
+
*
|
|
188
|
+
* @param err Error to be thrown
|
|
189
|
+
*/
|
|
190
|
+
function reportUnhandledError(err) {
|
|
191
|
+
setTimeout(() => {
|
|
192
|
+
throw err;
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
// src/runtimeHelpers.ts
|
|
196
|
+
function getStepEffect(actor, key) {
|
|
197
|
+
const effects = actor.getSnapshot().effects;
|
|
198
|
+
return effects && Object.prototype.hasOwnProperty.call(effects, key) ? effects[key] : void 0;
|
|
199
|
+
}
|
|
200
|
+
const pendingSteps = /* @__PURE__ */ new WeakMap();
|
|
201
|
+
/**
|
|
202
|
+
* Runs one keyed step of an async actor with the built-in journal: the
|
|
203
|
+
* result is memoized in the actor's own snapshot (`effects[key]`), so a
|
|
204
|
+
* restored actor skips completed steps when its logic function re-runs.
|
|
205
|
+
* Custom runtimes that implement `runStep` use this for the default local
|
|
206
|
+
* behavior, the same way `deliverEvent` exposes local delivery.
|
|
207
|
+
*
|
|
208
|
+
* @experimental
|
|
209
|
+
*/
|
|
210
|
+
async function runStep(actor, key, exec, sendSelf = (event) => void actor.system.sendEvent(actor, actor, event)) {
|
|
211
|
+
const effect = getStepEffect(actor, key);
|
|
212
|
+
if (effect?.status === "done") return effect.output;
|
|
213
|
+
if (effect?.status === "error") throw effect.error;
|
|
214
|
+
const running = pendingSteps.get(actor)?.get(key);
|
|
215
|
+
if (running) return running;
|
|
216
|
+
if (actor.getSnapshot().status !== "active") throw new Error(`Actor terminated before step "${key}" completed`);
|
|
217
|
+
let resolve;
|
|
218
|
+
let reject;
|
|
219
|
+
const promise = new Promise((resolvePromise, rejectPromise) => {
|
|
220
|
+
resolve = resolvePromise;
|
|
221
|
+
reject = rejectPromise;
|
|
222
|
+
});
|
|
223
|
+
let steps = pendingSteps.get(actor);
|
|
224
|
+
if (!steps) {
|
|
225
|
+
steps = /* @__PURE__ */ new Map();
|
|
226
|
+
pendingSteps.set(actor, steps);
|
|
227
|
+
}
|
|
228
|
+
steps.set(key, promise);
|
|
229
|
+
let subscription;
|
|
230
|
+
const cleanup = () => {
|
|
231
|
+
subscription?.unsubscribe();
|
|
232
|
+
steps.delete(key);
|
|
233
|
+
if (!steps.size) pendingSteps.delete(actor);
|
|
234
|
+
};
|
|
235
|
+
promise.then(cleanup, cleanup);
|
|
236
|
+
subscription = actor.subscribe({
|
|
237
|
+
error: reject,
|
|
238
|
+
complete: () => reject(/* @__PURE__ */ new Error(`Actor terminated before step "${key}" completed`))
|
|
239
|
+
});
|
|
240
|
+
(async () => {
|
|
241
|
+
try {
|
|
242
|
+
sendSelf({
|
|
243
|
+
type: XSTATE_LOGIC_EFFECT_START,
|
|
244
|
+
key
|
|
245
|
+
});
|
|
246
|
+
const output = await exec();
|
|
247
|
+
if (actor.getSnapshot().status === "active") {
|
|
248
|
+
sendSelf({
|
|
249
|
+
type: XSTATE_LOGIC_EFFECT_RESOLVE,
|
|
250
|
+
key,
|
|
251
|
+
output
|
|
252
|
+
});
|
|
253
|
+
resolve(output);
|
|
254
|
+
}
|
|
255
|
+
} catch (error) {
|
|
256
|
+
if (actor.getSnapshot().status === "active") sendSelf({
|
|
257
|
+
type: XSTATE_LOGIC_EFFECT_REJECT,
|
|
258
|
+
key,
|
|
259
|
+
error
|
|
260
|
+
});
|
|
261
|
+
reject(error);
|
|
262
|
+
}
|
|
263
|
+
})().catch(reject);
|
|
264
|
+
return promise;
|
|
265
|
+
}
|
|
266
|
+
/**
|
|
267
|
+
* Delivers an event directly to a target actor, bypassing any installed host
|
|
268
|
+
* runtime. Custom runtimes use this for the default local delivery behavior
|
|
269
|
+
* inside their own `sendEvent` implementations.
|
|
270
|
+
*
|
|
271
|
+
* @experimental
|
|
272
|
+
*/
|
|
273
|
+
function deliverEvent(source, target, event) {
|
|
274
|
+
if (rejectUndeliverableEvent(source, target, event)) return;
|
|
275
|
+
const runtimeTarget = target;
|
|
276
|
+
runtimeTarget._lastSourceRef = source;
|
|
277
|
+
runtimeTarget._send(event);
|
|
278
|
+
}
|
|
279
|
+
/**
|
|
280
|
+
* Returns the boundary error preventing this delivery, if any: an internal
|
|
281
|
+
* event type cannot cross an actor boundary from an external sender.
|
|
282
|
+
*/
|
|
283
|
+
function getEventBoundaryError(source, target, event) {
|
|
284
|
+
if (source !== target && target.logic?.isInternalEventType?.(event.type)) return /* @__PURE__ */ new Error(`Internal event "${event.type}" cannot be sent to actor "${target.id}" from outside.`);
|
|
285
|
+
}
|
|
286
|
+
/**
|
|
287
|
+
* Rejects an event that must not cross the delivery boundary — an internal
|
|
288
|
+
* event type sent from outside its owning actor — by reporting it as a dead
|
|
289
|
+
* letter. Returns `true` when the event was rejected and must not be
|
|
290
|
+
* delivered. Host runtimes must call this before taking ownership of
|
|
291
|
+
* delivery.
|
|
292
|
+
*/
|
|
293
|
+
function rejectUndeliverableEvent(source, target, event) {
|
|
294
|
+
const error = getEventBoundaryError(source, target, event);
|
|
295
|
+
if (error) {
|
|
296
|
+
target.system.deadLetter(source, target, event, "internalEvent", { error });
|
|
297
|
+
return true;
|
|
298
|
+
}
|
|
299
|
+
return false;
|
|
300
|
+
}
|
|
301
|
+
/**
|
|
302
|
+
* Publishes an actor's terminal result, notifying its parent and observers.
|
|
303
|
+
* Custom runtimes use this for the default local behavior inside their own
|
|
304
|
+
* `terminateActor` implementations.
|
|
305
|
+
*
|
|
306
|
+
* @experimental
|
|
307
|
+
*/
|
|
308
|
+
function terminateActor(actor, termination) {
|
|
309
|
+
actor._terminate(termination);
|
|
310
|
+
}
|
|
311
|
+
/**
|
|
312
|
+
* Stops an actor without producing a completion result. Custom runtimes use
|
|
313
|
+
* this for the default local behavior inside their own `stopActor`
|
|
314
|
+
* implementations.
|
|
315
|
+
*
|
|
316
|
+
* @experimental
|
|
317
|
+
*/
|
|
318
|
+
function stopActor(actor) {
|
|
319
|
+
actor._stop();
|
|
320
|
+
}
|
|
321
|
+
// src/snapshotActorRef.ts
|
|
322
|
+
const snapshotActorRefs = /* @__PURE__ */ new WeakMap();
|
|
323
|
+
const lazySnapshotActorRefs = /* @__PURE__ */ new WeakMap();
|
|
324
|
+
const emptyKeyedActors = /* @__PURE__ */ new Map();
|
|
325
|
+
function copyRegisteredActors(system, state) {
|
|
326
|
+
if (state) {
|
|
327
|
+
const children = new Map(state.children);
|
|
328
|
+
if (state.root?.sessionId) children.set(state.root.sessionId, state.root);
|
|
329
|
+
return children;
|
|
330
|
+
}
|
|
331
|
+
return new Map(system.children);
|
|
332
|
+
}
|
|
333
|
+
function captureRegisteredActors(system) {
|
|
334
|
+
const root = system._getRootActor?.();
|
|
335
|
+
const peekedChildren = system._peekChildren?.();
|
|
336
|
+
if (!root && !peekedChildren) {
|
|
337
|
+
if (system._getRootActor && system._peekChildren) return {};
|
|
338
|
+
const children = new Map(system.children);
|
|
339
|
+
return children.size ? { children } : {};
|
|
340
|
+
}
|
|
341
|
+
if (!peekedChildren?.size) return root ? { root } : {};
|
|
342
|
+
return { children: new Map(system.children) };
|
|
343
|
+
}
|
|
344
|
+
function getKeyedActors(system) {
|
|
345
|
+
const peek = system._peekKeyedActors;
|
|
346
|
+
if (peek) return peek.call(system) ?? emptyKeyedActors;
|
|
347
|
+
return system.keyedActors;
|
|
348
|
+
}
|
|
349
|
+
/** Marks topology or persisted runtime state as changed. @internal */
|
|
350
|
+
function markSystemSnapshotDirty(system) {
|
|
351
|
+
system._snapshotVersion++;
|
|
352
|
+
}
|
|
353
|
+
function getSystemSnapshotVersion(system) {
|
|
354
|
+
return system._snapshotVersion;
|
|
355
|
+
}
|
|
356
|
+
function getSnapshotChildren(snapshot) {
|
|
357
|
+
return "children" in snapshot && snapshot.children ? snapshot.children : {};
|
|
358
|
+
}
|
|
359
|
+
/**
|
|
360
|
+
* Creates the isolated receptionist/id view used by a pure branch. Scheduling,
|
|
361
|
+
* relaying and clocks remain delegated to the snapshot's execution system.
|
|
362
|
+
*
|
|
363
|
+
* @internal
|
|
364
|
+
*/
|
|
365
|
+
function createSnapshotSystem(baseSystem, children, baseState) {
|
|
366
|
+
const forwardInspection = hasAmbientInspector();
|
|
367
|
+
const registeredActors = copyRegisteredActors(baseSystem, baseState);
|
|
368
|
+
const keyedActors = new Map(baseState?.keyedActors ?? getKeyedActors(baseSystem));
|
|
369
|
+
const reverseKeyedActors = /* @__PURE__ */ new WeakMap();
|
|
370
|
+
const system = Object.assign(Object.create(baseSystem), {
|
|
371
|
+
children: registeredActors,
|
|
372
|
+
keyedActors,
|
|
373
|
+
reverseKeyedActors,
|
|
374
|
+
_snapshot: { ...baseState?.snapshot ?? baseSystem._snapshot },
|
|
375
|
+
_snapshotVersion: baseState?.sourceVersion ?? baseSystem._snapshotVersion,
|
|
376
|
+
_register: (sessionId, actor) => {
|
|
377
|
+
registeredActors.set(sessionId, actor);
|
|
378
|
+
markSystemSnapshotDirty(system);
|
|
379
|
+
return sessionId;
|
|
380
|
+
},
|
|
381
|
+
_unregister: (actor) => {
|
|
382
|
+
if (actor.sessionId) registeredActors.delete(actor.sessionId);
|
|
383
|
+
const registryKey = reverseKeyedActors.get(actor);
|
|
384
|
+
if (registryKey !== void 0 && keyedActors.get(registryKey) === actor) keyedActors.delete(registryKey);
|
|
385
|
+
reverseKeyedActors.delete(actor);
|
|
386
|
+
markSystemSnapshotDirty(system);
|
|
387
|
+
},
|
|
388
|
+
_set: (registryKey, actor) => {
|
|
389
|
+
const existing = keyedActors.get(registryKey);
|
|
390
|
+
if (existing && existing !== actor) throw new Error(`Actor with registry key '${String(registryKey)}' already exists.`);
|
|
391
|
+
keyedActors.set(registryKey, actor);
|
|
392
|
+
reverseKeyedActors.set(actor, registryKey);
|
|
393
|
+
markSystemSnapshotDirty(system);
|
|
394
|
+
},
|
|
395
|
+
get: (registryKey) => keyedActors.get(registryKey),
|
|
396
|
+
getAll: () => Object.fromEntries(keyedActors),
|
|
397
|
+
_hasInspectionObservers: forwardInspection ? () => baseSystem._hasInspectionObservers?.() ?? false : () => false,
|
|
398
|
+
_sendInspectionEvent: forwardInspection ? (event) => baseSystem._sendInspectionEvent(event) : () => {}
|
|
399
|
+
});
|
|
400
|
+
for (const [registryKey, actor] of keyedActors) {
|
|
401
|
+
if (!actor) continue;
|
|
402
|
+
reverseKeyedActors.set(actor, registryKey);
|
|
403
|
+
}
|
|
404
|
+
for (const actor of Object.values(children)) {
|
|
405
|
+
if (!actor) continue;
|
|
406
|
+
if (actor.sessionId) registeredActors.set(actor.sessionId, actor);
|
|
407
|
+
const registryKey = actor.registryKey;
|
|
408
|
+
if (registryKey) {
|
|
409
|
+
keyedActors.set(registryKey, actor);
|
|
410
|
+
reverseKeyedActors.set(actor, registryKey);
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
return system;
|
|
414
|
+
}
|
|
415
|
+
/** Returns the actor identity associated with a transition snapshot. @internal */
|
|
416
|
+
function getSnapshotActorRef(snapshot) {
|
|
417
|
+
const existing = snapshotActorRefs.get(snapshot);
|
|
418
|
+
if (existing) return existing;
|
|
419
|
+
const create = lazySnapshotActorRefs.get(snapshot);
|
|
420
|
+
if (!create) return;
|
|
421
|
+
const created = create();
|
|
422
|
+
snapshotActorRefs.set(snapshot, created);
|
|
423
|
+
lazySnapshotActorRefs.delete(snapshot);
|
|
424
|
+
return created;
|
|
425
|
+
}
|
|
426
|
+
/** Reads an already materialized snapshot association without creating one. @internal */
|
|
427
|
+
function peekSnapshotActorRef(snapshot) {
|
|
428
|
+
return snapshotActorRefs.get(snapshot);
|
|
429
|
+
}
|
|
430
|
+
/** Returns the existing identity provider without resolving it. @internal */
|
|
431
|
+
function getSnapshotActorRefProvider(snapshot) {
|
|
432
|
+
const existing = snapshotActorRefs.get(snapshot);
|
|
433
|
+
return existing ? () => existing : lazySnapshotActorRefs.get(snapshot);
|
|
434
|
+
}
|
|
435
|
+
/** Defers actor/system identity allocation until a snapshot capability is used. @internal */
|
|
436
|
+
function setLazySnapshotActorRef(snapshot, create) {
|
|
437
|
+
snapshotActorRefs.delete(snapshot);
|
|
438
|
+
lazySnapshotActorRefs.set(snapshot, create);
|
|
439
|
+
}
|
|
440
|
+
/**
|
|
441
|
+
* Copies an internal actor association without rebuilding its system view.
|
|
442
|
+
*
|
|
443
|
+
* @internal
|
|
444
|
+
*/
|
|
445
|
+
function copySnapshotActorRef(source, target) {
|
|
446
|
+
const ref = snapshotActorRefs.get(source);
|
|
447
|
+
if (ref) {
|
|
448
|
+
snapshotActorRefs.set(target, ref);
|
|
449
|
+
return true;
|
|
450
|
+
}
|
|
451
|
+
const create = lazySnapshotActorRefs.get(source);
|
|
452
|
+
if (create) {
|
|
453
|
+
lazySnapshotActorRefs.set(target, create);
|
|
454
|
+
return true;
|
|
455
|
+
}
|
|
456
|
+
return false;
|
|
457
|
+
}
|
|
458
|
+
/** Refreshes the only topology change made by an otherwise idle root start. */
|
|
459
|
+
function refreshSnapshotActorRefRoot(snapshot, actor, system) {
|
|
460
|
+
const ref = snapshotActorRefs.get(snapshot);
|
|
461
|
+
const sourceVersion = getSystemSnapshotVersion(system);
|
|
462
|
+
if (ref?.actor !== actor || ref.systemState.sourceSystem !== system || ref.systemState.sourceVersion + 1 !== sourceVersion || system._getRootActor?.() !== actor || system._peekChildren?.()) return false;
|
|
463
|
+
ref.systemState.root = actor;
|
|
464
|
+
ref.systemState.sourceVersion = sourceVersion;
|
|
465
|
+
return true;
|
|
466
|
+
}
|
|
467
|
+
/**
|
|
468
|
+
* Associates a transition snapshot with an actor and an immutable system view.
|
|
469
|
+
* The internal association is excluded from enumeration and persistence.
|
|
470
|
+
*
|
|
471
|
+
* @internal
|
|
472
|
+
*/
|
|
473
|
+
function setSnapshotActorRef(snapshot, actor, baseSystem = actor.system, previousSnapshot) {
|
|
474
|
+
lazySnapshotActorRefs.delete(snapshot);
|
|
475
|
+
const sourceVersion = getSystemSnapshotVersion(baseSystem);
|
|
476
|
+
const previousRef = previousSnapshot ? getSnapshotActorRef(previousSnapshot) : void 0;
|
|
477
|
+
const reusableRef = previousRef?.actor === actor ? previousRef : getSnapshotActorRef(snapshot);
|
|
478
|
+
if (reusableRef?.actor === actor && reusableRef.systemState.sourceSystem === baseSystem && reusableRef.systemState.sourceVersion === sourceVersion) {
|
|
479
|
+
snapshotActorRefs.set(snapshot, reusableRef);
|
|
480
|
+
return;
|
|
481
|
+
}
|
|
482
|
+
const capturedActors = captureRegisteredActors(baseSystem);
|
|
483
|
+
let children = capturedActors.children;
|
|
484
|
+
const baseKeyedActors = getKeyedActors(baseSystem);
|
|
485
|
+
let keyedActors = baseKeyedActors.size ? new Map(baseKeyedActors) : emptyKeyedActors;
|
|
486
|
+
for (const child of Object.values(getSnapshotChildren(snapshot))) {
|
|
487
|
+
if (!child) continue;
|
|
488
|
+
if (child.sessionId) (children ??= /* @__PURE__ */ new Map()).set(child.sessionId, child);
|
|
489
|
+
const registryKey = child.registryKey;
|
|
490
|
+
if (registryKey) {
|
|
491
|
+
if (keyedActors === emptyKeyedActors) keyedActors = /* @__PURE__ */ new Map();
|
|
492
|
+
keyedActors.set(registryKey, child);
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
snapshotActorRefs.set(snapshot, {
|
|
496
|
+
actor,
|
|
497
|
+
systemState: {
|
|
498
|
+
...capturedActors,
|
|
499
|
+
children,
|
|
500
|
+
keyedActors,
|
|
501
|
+
snapshot: { ...baseSystem._snapshot },
|
|
502
|
+
sourceSystem: baseSystem,
|
|
503
|
+
sourceVersion
|
|
504
|
+
}
|
|
505
|
+
});
|
|
506
|
+
}
|
|
507
|
+
// src/system.ts
|
|
508
|
+
const defaultWallClock = { now: () => Date.now() };
|
|
509
|
+
let systemIdPrefix;
|
|
510
|
+
let nextSystemId = 0;
|
|
511
|
+
/** @internal */
|
|
512
|
+
const transitionEffectSignal = /* @__PURE__ */ new Error("Transition effect");
|
|
513
|
+
/** @internal */
|
|
514
|
+
const transitionEffectTargets = [];
|
|
515
|
+
function readGlobalCrypto() {
|
|
516
|
+
try {
|
|
517
|
+
return globalThis.crypto;
|
|
518
|
+
} catch {
|
|
519
|
+
return;
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
function createSystemIdPrefix(crypto) {
|
|
523
|
+
if (crypto?.getRandomValues) try {
|
|
524
|
+
const values = crypto.getRandomValues(/* @__PURE__ */ new Uint32Array(4));
|
|
525
|
+
return Array.from(values, (value) => value.toString(36).padStart(7, "0")).join("");
|
|
526
|
+
} catch {}
|
|
527
|
+
if (crypto?.randomUUID) try {
|
|
528
|
+
return crypto.randomUUID().replaceAll("-", "");
|
|
529
|
+
} catch {}
|
|
530
|
+
return `xstate-${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`;
|
|
531
|
+
}
|
|
532
|
+
function createSystemId(crypto) {
|
|
533
|
+
return `${crypto === void 0 ? systemIdPrefix ??= createSystemIdPrefix(readGlobalCrypto()) : createSystemIdPrefix(crypto)}:${(nextSystemId++).toString(36)}`;
|
|
534
|
+
}
|
|
535
|
+
let ambientExecutionIdentity;
|
|
536
|
+
/** @internal Runs `fn` with systems it creates pinned to `identity`. */
|
|
537
|
+
function withExecutionIdentity(identity, fn) {
|
|
538
|
+
if (!identity) return fn();
|
|
539
|
+
const previous = ambientExecutionIdentity;
|
|
540
|
+
ambientExecutionIdentity = identity;
|
|
541
|
+
try {
|
|
542
|
+
return fn();
|
|
543
|
+
} finally {
|
|
544
|
+
ambientExecutionIdentity = previous;
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
/**
|
|
548
|
+
* Derives the deterministic id prefix for a generated actor id from its actor
|
|
549
|
+
* source: the registered source key when the source is a string, the logic's
|
|
550
|
+
* own `id` otherwise, with `x` as the last-resort prefix.
|
|
551
|
+
*
|
|
552
|
+
* @internal
|
|
553
|
+
*/
|
|
554
|
+
function getActorIdPrefix(src) {
|
|
555
|
+
if (typeof src === "string") return src;
|
|
556
|
+
const logicId = src?.id;
|
|
557
|
+
return typeof logicId === "string" && logicId.length && logicId !== "(machine)" ? logicId : "x";
|
|
558
|
+
}
|
|
559
|
+
/**
|
|
560
|
+
* Encodes one actor id as an address segment. `/` separates segments, so it
|
|
561
|
+
* is percent-encoded, and the escape character `%` is escaped first so the
|
|
562
|
+
* encoding stays injective: the ids 'a/b' and 'a%2Fb' must not produce the
|
|
563
|
+
* same address.
|
|
564
|
+
*
|
|
565
|
+
* @internal
|
|
566
|
+
*/
|
|
567
|
+
function encodeAddressSegment(id) {
|
|
568
|
+
return id.includes("/") || id.includes("%") ? id.replaceAll("%", "%25").replaceAll("/", "%2F") : id;
|
|
569
|
+
}
|
|
570
|
+
/**
|
|
571
|
+
* Parses a generated-shaped actor id (`prefix:<n>`). This single definition is
|
|
572
|
+
* the determinism contract for id reservation: live allocation, replay, and
|
|
573
|
+
* restore must all agree on what counts as generated-shaped. Deliberately
|
|
574
|
+
* broader than ids a generated allocation could produce — over-reserving
|
|
575
|
+
* skips numbers, which is harmless, while under-reserving could collide.
|
|
576
|
+
*
|
|
577
|
+
* @internal
|
|
578
|
+
*/
|
|
579
|
+
function parseGeneratedActorId(id) {
|
|
580
|
+
const separator = id.lastIndexOf(":");
|
|
581
|
+
if (separator <= 0 || separator === id.length - 1) return;
|
|
582
|
+
const index = Number(id.slice(separator + 1));
|
|
583
|
+
return Number.isSafeInteger(index) && index >= 0 ? {
|
|
584
|
+
prefix: id.slice(0, separator),
|
|
585
|
+
index
|
|
586
|
+
} : void 0;
|
|
587
|
+
}
|
|
588
|
+
/**
|
|
589
|
+
* The id (and address root segment) of the first parentless actor of a logic:
|
|
590
|
+
* the logic's own name, or `x:0` for anonymous logic.
|
|
591
|
+
*
|
|
592
|
+
* @internal
|
|
593
|
+
*/
|
|
594
|
+
function getRootActorId(src) {
|
|
595
|
+
const prefix = getActorIdPrefix(src);
|
|
596
|
+
return prefix === "x" ? "x:0" : prefix;
|
|
597
|
+
}
|
|
598
|
+
function getActorIdCounterKey(parent, prefix) {
|
|
599
|
+
return `${parent ? parent.address : ""}|${prefix}`;
|
|
600
|
+
}
|
|
601
|
+
function bumpActorIdCounter(system, counterKey, next) {
|
|
602
|
+
const counters = system._snapshot._nextActorIds;
|
|
603
|
+
if ((counters[counterKey] ?? 0) >= next) return;
|
|
604
|
+
system._snapshot._nextActorIds = {
|
|
605
|
+
...counters,
|
|
606
|
+
[counterKey]: next
|
|
607
|
+
};
|
|
608
|
+
markSystemSnapshotDirty(system);
|
|
609
|
+
}
|
|
610
|
+
/** @internal */
|
|
611
|
+
function resolveActorId(system, requestedId, options) {
|
|
612
|
+
if (requestedId !== void 0) {
|
|
613
|
+
const generated = parseGeneratedActorId(requestedId);
|
|
614
|
+
if (generated) bumpActorIdCounter(system, getActorIdCounterKey(options?.parent, generated.prefix), generated.index + 1);
|
|
615
|
+
else if (!options?.parent) bumpActorIdCounter(system, getActorIdCounterKey(void 0, requestedId), 1);
|
|
616
|
+
return requestedId;
|
|
617
|
+
}
|
|
618
|
+
const prefix = getActorIdPrefix(options?.src);
|
|
619
|
+
const counterKey = getActorIdCounterKey(options?.parent, prefix);
|
|
620
|
+
const counter = system._snapshot._nextActorIds[counterKey] ?? 0;
|
|
621
|
+
bumpActorIdCounter(system, counterKey, counter + 1);
|
|
622
|
+
return !options?.parent && prefix !== "x" && counter === 0 ? prefix : `${prefix}:${counter}`;
|
|
623
|
+
}
|
|
624
|
+
/** @internal */
|
|
625
|
+
function bookSessionId(system) {
|
|
626
|
+
return `${system._identity.systemId}:${system._identity.nextSessionId++}`;
|
|
627
|
+
}
|
|
628
|
+
/** @internal Every operation of `ActorSystemRuntime`, for runtime wrappers. */
|
|
629
|
+
const RUNTIME_OPERATIONS = [
|
|
630
|
+
"spawnActor",
|
|
631
|
+
"startActor",
|
|
632
|
+
"stopActor",
|
|
633
|
+
"terminateActor",
|
|
634
|
+
"emitEvent",
|
|
635
|
+
"scheduleTimer",
|
|
636
|
+
"cancelTimer",
|
|
637
|
+
"cancelAllTimers",
|
|
638
|
+
"deadLetter"
|
|
639
|
+
];
|
|
640
|
+
const emptyScheduledTimers = Object.freeze({});
|
|
641
|
+
function createScheduledTimerId(actor, id) {
|
|
642
|
+
return `${actor.sessionId}.${id}`;
|
|
643
|
+
}
|
|
644
|
+
var RuntimeSystem = class {
|
|
645
|
+
_rootActor;
|
|
646
|
+
_identity;
|
|
647
|
+
_snapshot;
|
|
648
|
+
_snapshotVersion = 0;
|
|
649
|
+
scheduler = this;
|
|
650
|
+
_clock;
|
|
651
|
+
_wallClock;
|
|
652
|
+
_logger;
|
|
653
|
+
_reportUnhandledError;
|
|
654
|
+
_warn;
|
|
655
|
+
createActorRef;
|
|
656
|
+
get children() {
|
|
657
|
+
const children = this._children ??= /* @__PURE__ */ new Map();
|
|
658
|
+
const rootActor = this._getRootActor();
|
|
659
|
+
if (rootActor?.sessionId !== void 0) children.set(rootActor.sessionId, rootActor);
|
|
660
|
+
return children;
|
|
661
|
+
}
|
|
662
|
+
set children(children) {
|
|
663
|
+
this._children = children;
|
|
664
|
+
}
|
|
665
|
+
_getRootActor() {
|
|
666
|
+
return this._rootActor._isRunning() ? this._rootActor : void 0;
|
|
667
|
+
}
|
|
668
|
+
_peekChildren() {
|
|
669
|
+
return this._children;
|
|
670
|
+
}
|
|
671
|
+
get keyedActors() {
|
|
672
|
+
return this._keyedActors ??= /* @__PURE__ */ new Map();
|
|
673
|
+
}
|
|
674
|
+
set keyedActors(actors) {
|
|
675
|
+
this._keyedActors = actors;
|
|
676
|
+
}
|
|
677
|
+
get reverseKeyedActors() {
|
|
678
|
+
return this._reverseKeyedActors ??= /* @__PURE__ */ new WeakMap();
|
|
679
|
+
}
|
|
680
|
+
set reverseKeyedActors(actors) {
|
|
681
|
+
this._reverseKeyedActors = actors;
|
|
682
|
+
}
|
|
683
|
+
/** @internal Avoids materializing the receptionist for empty systems. */
|
|
684
|
+
_peekKeyedActors() {
|
|
685
|
+
return this._keyedActors;
|
|
686
|
+
}
|
|
687
|
+
constructor(_rootActor, options) {
|
|
688
|
+
this._rootActor = _rootActor;
|
|
689
|
+
this._identity = ambientExecutionIdentity ?? {
|
|
690
|
+
systemId: createSystemId(options.crypto),
|
|
691
|
+
nextSessionId: 0
|
|
692
|
+
};
|
|
693
|
+
const restoredSnapshot = typeof options.snapshot === "object" && options.snapshot !== null ? options.snapshot : void 0;
|
|
694
|
+
this._clock = options.clock;
|
|
695
|
+
this._wallClock = options.wallClock ?? defaultWallClock;
|
|
696
|
+
this._logger = options.logger;
|
|
697
|
+
this._reportUnhandledError = options.reportUnhandledError ?? reportUnhandledError;
|
|
698
|
+
this._warn = options.warn;
|
|
699
|
+
this.createActorRef = options.createActorRef;
|
|
700
|
+
const ambientInspector = getAmbientInspector();
|
|
701
|
+
if (ambientInspector) this.inspect(ambientInspector);
|
|
702
|
+
this._snapshot = {
|
|
703
|
+
_scheduledTimers: restoredSnapshot?.scheduler ?? emptyScheduledTimers,
|
|
704
|
+
_nextActorIds: {}
|
|
705
|
+
};
|
|
706
|
+
}
|
|
707
|
+
_recordSent(source, target, event, delay, id) {
|
|
708
|
+
if (!this._inspectionObservers?.size || !source) return;
|
|
709
|
+
const inspectionSource = source;
|
|
710
|
+
(inspectionSource._collectedSent ??= []).push({
|
|
711
|
+
targetRef: target,
|
|
712
|
+
targetId: target.id,
|
|
713
|
+
event,
|
|
714
|
+
delay,
|
|
715
|
+
id
|
|
716
|
+
});
|
|
717
|
+
}
|
|
718
|
+
schedule(source, id, delay) {
|
|
719
|
+
const existingId = createScheduledTimerId(source, id);
|
|
720
|
+
if (this._timerMap?.[existingId] !== void 0) this.cancel(source, id);
|
|
721
|
+
const timer = source.getSnapshot()?.timers?.[id];
|
|
722
|
+
if (timer) {
|
|
723
|
+
const target = timer.target === "self" ? source : timer.target;
|
|
724
|
+
this._recordSent(source, target, timer.event, delay, id);
|
|
725
|
+
}
|
|
726
|
+
const scheduledAt = this._clock.now?.() ?? this._wallClock.now();
|
|
727
|
+
const scheduledTimer = {
|
|
728
|
+
source,
|
|
729
|
+
delay,
|
|
730
|
+
id,
|
|
731
|
+
scheduledAt,
|
|
732
|
+
dueAt: scheduledAt + delay
|
|
733
|
+
};
|
|
734
|
+
const scheduledTimerId = createScheduledTimerId(source, id);
|
|
735
|
+
if (this._snapshot._scheduledTimers === emptyScheduledTimers) this._snapshot._scheduledTimers = {};
|
|
736
|
+
this._snapshot._scheduledTimers[scheduledTimerId] = scheduledTimer;
|
|
737
|
+
markSystemSnapshotDirty(this);
|
|
738
|
+
const timeout = this._clock.setTimeout(() => {
|
|
739
|
+
if (this._timerMap) delete this._timerMap[scheduledTimerId];
|
|
740
|
+
delete this._snapshot._scheduledTimers[scheduledTimerId];
|
|
741
|
+
markSystemSnapshotDirty(this);
|
|
742
|
+
this._deliver(source, source, {
|
|
743
|
+
type: XSTATE_TIMER,
|
|
744
|
+
id
|
|
745
|
+
});
|
|
746
|
+
}, delay);
|
|
747
|
+
(this._timerMap ??= {})[scheduledTimerId] = timeout;
|
|
748
|
+
}
|
|
749
|
+
cancel(source, id) {
|
|
750
|
+
const scheduledTimerId = createScheduledTimerId(source, id);
|
|
751
|
+
const timeout = this._timerMap?.[scheduledTimerId];
|
|
752
|
+
if (this._timerMap) delete this._timerMap[scheduledTimerId];
|
|
753
|
+
if (this._snapshot._scheduledTimers !== emptyScheduledTimers) delete this._snapshot._scheduledTimers[scheduledTimerId];
|
|
754
|
+
markSystemSnapshotDirty(this);
|
|
755
|
+
if (timeout !== void 0) this._clock.clearTimeout(timeout);
|
|
756
|
+
}
|
|
757
|
+
cancelAll(actor) {
|
|
758
|
+
for (const scheduledTimerId in this._snapshot._scheduledTimers) {
|
|
759
|
+
const scheduledTimer = this._snapshot._scheduledTimers[scheduledTimerId];
|
|
760
|
+
if (!scheduledTimer) continue;
|
|
761
|
+
if (scheduledTimer.source === actor) this.cancel(actor, scheduledTimer.id);
|
|
762
|
+
}
|
|
763
|
+
}
|
|
764
|
+
_deliver(source, target, event) {
|
|
765
|
+
deliverEvent(source, target, event);
|
|
766
|
+
}
|
|
767
|
+
_register(sessionId, actor) {
|
|
768
|
+
if (actor === this._rootActor) this._children?.set(sessionId, actor);
|
|
769
|
+
else (this._children ??= /* @__PURE__ */ new Map()).set(sessionId, actor);
|
|
770
|
+
markSystemSnapshotDirty(this);
|
|
771
|
+
return sessionId;
|
|
772
|
+
}
|
|
773
|
+
_unregister(actor) {
|
|
774
|
+
let changed;
|
|
775
|
+
if (actor === this._rootActor) {
|
|
776
|
+
changed = this._getRootActor() !== void 0;
|
|
777
|
+
changed = actor.sessionId !== void 0 && (this._children?.delete(actor.sessionId) ?? false) || changed;
|
|
778
|
+
} else changed = actor.sessionId !== void 0 && (this._children?.delete(actor.sessionId) ?? false);
|
|
779
|
+
const registryKey = this._reverseKeyedActors?.get(actor);
|
|
780
|
+
if (registryKey !== void 0) {
|
|
781
|
+
if (this._keyedActors?.get(registryKey) === actor) {
|
|
782
|
+
this._keyedActors.delete(registryKey);
|
|
783
|
+
changed = true;
|
|
784
|
+
}
|
|
785
|
+
this._reverseKeyedActors?.delete(actor);
|
|
786
|
+
}
|
|
787
|
+
if (changed) markSystemSnapshotDirty(this);
|
|
788
|
+
}
|
|
789
|
+
get(registryKey) {
|
|
790
|
+
return this._keyedActors?.get(registryKey);
|
|
791
|
+
}
|
|
792
|
+
getAll() {
|
|
793
|
+
return Object.fromEntries(this._keyedActors?.entries() ?? []);
|
|
794
|
+
}
|
|
795
|
+
_set(registryKey, actor) {
|
|
796
|
+
const existing = this._keyedActors?.get(registryKey);
|
|
797
|
+
if (existing && existing !== actor) throw new Error(`Actor with registry key '${registryKey}' already exists.`);
|
|
798
|
+
(this._keyedActors ??= /* @__PURE__ */ new Map()).set(registryKey, actor);
|
|
799
|
+
(this._reverseKeyedActors ??= /* @__PURE__ */ new WeakMap()).set(actor, registryKey);
|
|
800
|
+
if (existing !== actor) markSystemSnapshotDirty(this);
|
|
801
|
+
}
|
|
802
|
+
inspect(observerOrFn) {
|
|
803
|
+
const observer = toObserver(observerOrFn);
|
|
804
|
+
(this._inspectionObservers ??= /* @__PURE__ */ new Set()).add(observer);
|
|
805
|
+
return { unsubscribe: () => {
|
|
806
|
+
this._inspectionObservers?.delete(observer);
|
|
807
|
+
} };
|
|
808
|
+
}
|
|
809
|
+
onRejectedEvent(listener) {
|
|
810
|
+
const entry = (rejection) => listener(rejection);
|
|
811
|
+
(this._rejectionListeners ??= /* @__PURE__ */ new Set()).add(entry);
|
|
812
|
+
return { unsubscribe: () => {
|
|
813
|
+
this._rejectionListeners?.delete(entry);
|
|
814
|
+
} };
|
|
815
|
+
}
|
|
816
|
+
_hasInspectionObservers() {
|
|
817
|
+
return !!this._inspectionObservers?.size;
|
|
818
|
+
}
|
|
819
|
+
_sendInspectionEvent(event) {
|
|
820
|
+
if (!this._inspectionObservers?.size) return;
|
|
821
|
+
const resolvedInspectionEvent = {
|
|
822
|
+
...event,
|
|
823
|
+
rootId: this._rootActor.sessionId
|
|
824
|
+
};
|
|
825
|
+
this._inspectionObservers.forEach((observer) => observer.next?.(resolvedInspectionEvent));
|
|
826
|
+
}
|
|
827
|
+
spawnActor(source, actor) {
|
|
828
|
+
return this.runtime?.spawnActor?.(source, actor);
|
|
829
|
+
}
|
|
830
|
+
startActor(actor) {
|
|
831
|
+
const override = this.runtime?.startActor;
|
|
832
|
+
if (override) return override(actor);
|
|
833
|
+
actor.start();
|
|
834
|
+
}
|
|
835
|
+
stopActor(actor) {
|
|
836
|
+
const override = this.runtime?.stopActor;
|
|
837
|
+
if (override) return override(actor);
|
|
838
|
+
stopActor(actor);
|
|
839
|
+
}
|
|
840
|
+
terminateActor(actor, termination) {
|
|
841
|
+
const override = this.runtime?.terminateActor;
|
|
842
|
+
if (override) return override(actor, termination);
|
|
843
|
+
terminateActor(actor, termination);
|
|
844
|
+
}
|
|
845
|
+
sendEvent(source, target, event) {
|
|
846
|
+
if (rejectUndeliverableEvent(source, target, event)) return;
|
|
847
|
+
this._recordSent(source, target, event);
|
|
848
|
+
const override = this.runtime?.sendEvent;
|
|
849
|
+
if (override) return override(source, target, event);
|
|
850
|
+
this._deliver(source, target, event);
|
|
851
|
+
}
|
|
852
|
+
emitEvent(source, event) {
|
|
853
|
+
const override = this.runtime?.emitEvent;
|
|
854
|
+
if (override) return override(source, event);
|
|
855
|
+
source._emit(event);
|
|
856
|
+
}
|
|
857
|
+
deadLetter(source, target, event, reason, detail) {
|
|
858
|
+
const listeners = this._rejectionListeners;
|
|
859
|
+
if (listeners?.size) {
|
|
860
|
+
const rejection = {
|
|
861
|
+
event,
|
|
862
|
+
targetRef: target,
|
|
863
|
+
targetId: target?.id ?? detail?.targetId,
|
|
864
|
+
sourceRef: source,
|
|
865
|
+
eventOrigin: source ? "actor" : "external",
|
|
866
|
+
reason,
|
|
867
|
+
issues: detail?.issues,
|
|
868
|
+
error: detail?.error
|
|
869
|
+
};
|
|
870
|
+
for (const listener of [...listeners]) try {
|
|
871
|
+
listener(rejection);
|
|
872
|
+
} catch (err) {
|
|
873
|
+
this._reportUnhandledError(err);
|
|
874
|
+
}
|
|
875
|
+
}
|
|
876
|
+
const override = this.runtime?.deadLetter;
|
|
877
|
+
if (override) return override(source, target, event, reason, detail);
|
|
878
|
+
}
|
|
879
|
+
runStep(actor, key, exec) {
|
|
880
|
+
const override = this.runtime?.runStep;
|
|
881
|
+
if (override) return override(actor, key, exec);
|
|
882
|
+
return runStep(actor, key, exec);
|
|
883
|
+
}
|
|
884
|
+
runLogic(actor, exec) {
|
|
885
|
+
const override = this.runtime?.runLogic;
|
|
886
|
+
if (override) return override(actor, exec);
|
|
887
|
+
return exec();
|
|
888
|
+
}
|
|
889
|
+
scheduleTimer(source, id, delay) {
|
|
890
|
+
const override = this.runtime?.scheduleTimer;
|
|
891
|
+
if (override) {
|
|
892
|
+
const timer = source.getSnapshot()?.timers?.[id];
|
|
893
|
+
if (timer) {
|
|
894
|
+
const target = timer.target === "self" ? source : timer.target;
|
|
895
|
+
this._recordSent(source, target, timer.event, delay, id);
|
|
896
|
+
}
|
|
897
|
+
return override(source, id, delay);
|
|
898
|
+
}
|
|
899
|
+
this.schedule(source, id, delay);
|
|
900
|
+
}
|
|
901
|
+
cancelTimer(source, id) {
|
|
902
|
+
const override = this.runtime?.cancelTimer;
|
|
903
|
+
if (override) return override(source, id);
|
|
904
|
+
this.cancel(source, id);
|
|
905
|
+
}
|
|
906
|
+
cancelAllTimers(source) {
|
|
907
|
+
const override = this.runtime?.cancelAllTimers;
|
|
908
|
+
if (override) return override(source);
|
|
909
|
+
this.cancelAll(source);
|
|
910
|
+
}
|
|
911
|
+
_relay(source, target, event) {
|
|
912
|
+
if (transitionEffectTargets.length && transitionEffectTargets.includes(target)) throw transitionEffectSignal;
|
|
913
|
+
return this.sendEvent(source, target, event);
|
|
914
|
+
}
|
|
915
|
+
getSnapshot() {
|
|
916
|
+
return { _scheduledTimers: { ...this._snapshot._scheduledTimers } };
|
|
917
|
+
}
|
|
918
|
+
start() {
|
|
919
|
+
const scheduledTimers = this._snapshot._scheduledTimers;
|
|
920
|
+
let resetScheduledTimers = true;
|
|
921
|
+
for (const scheduledId in scheduledTimers) {
|
|
922
|
+
if (resetScheduledTimers) {
|
|
923
|
+
this._snapshot._scheduledTimers = {};
|
|
924
|
+
resetScheduledTimers = false;
|
|
925
|
+
}
|
|
926
|
+
const scheduledTimer = scheduledTimers[scheduledId];
|
|
927
|
+
if (!scheduledTimer) continue;
|
|
928
|
+
const { source, dueAt, id } = scheduledTimer;
|
|
929
|
+
this.scheduleTimer(source, id, Math.max(0, dueAt - (this._clock.now?.() ?? this._wallClock.now())));
|
|
930
|
+
}
|
|
931
|
+
}
|
|
932
|
+
};
|
|
933
|
+
/** @internal */
|
|
934
|
+
function createRuntimeSystem(rootActor, options) {
|
|
935
|
+
return new RuntimeSystem(rootActor, options);
|
|
936
|
+
}
|
|
937
|
+
// src/memo.ts
|
|
938
|
+
const cache = /* @__PURE__ */ new WeakMap();
|
|
939
|
+
function memo(object, key, fn) {
|
|
940
|
+
let memoizedData = cache.get(object);
|
|
941
|
+
if (!memoizedData) {
|
|
942
|
+
memoizedData = { [key]: fn() };
|
|
943
|
+
cache.set(object, memoizedData);
|
|
944
|
+
} else if (!(key in memoizedData)) memoizedData[key] = fn();
|
|
945
|
+
return memoizedData[key];
|
|
946
|
+
}
|
|
947
|
+
// src/actions.ts
|
|
948
|
+
function assertSendToEvent(event) {
|
|
949
|
+
if (typeof event === "string") throw new Error(`Only event objects may be used with sendTo`);
|
|
950
|
+
}
|
|
951
|
+
const builtInActions = {
|
|
952
|
+
["@xstate.spawn"]: (actor) => {
|
|
953
|
+
return actor.system.spawnActor(actor._parent, actor);
|
|
954
|
+
},
|
|
955
|
+
["@xstate.start"]: (actor) => {
|
|
956
|
+
return actor.system.startActor(actor);
|
|
957
|
+
},
|
|
958
|
+
["@xstate.raise"]: (actorScope, _event, options) => {
|
|
959
|
+
return actorScope.system.scheduleTimer(actorScope.self, options.id, options?.delay ?? 0);
|
|
960
|
+
},
|
|
961
|
+
["@xstate.sendTo"]: (actorScope, actor, event, options) => {
|
|
962
|
+
assertSendToEvent(event);
|
|
963
|
+
if (options?.delay !== void 0) return actorScope.system.scheduleTimer(actorScope.self, options.id, options?.delay ?? 0);
|
|
964
|
+
else return actorScope.system._relay(actorScope.self, actor, event);
|
|
965
|
+
},
|
|
966
|
+
["@xstate.cancel"]: (actorScope, sendId) => {
|
|
967
|
+
return actorScope.system.cancelTimer(actorScope.self, sendId);
|
|
968
|
+
},
|
|
969
|
+
["@xstate.stop"]: (actorScope, actor) => {
|
|
970
|
+
return actorScope.system.stopActor(actor);
|
|
971
|
+
},
|
|
972
|
+
["@xstate.terminate"]: (actor, termination) => {
|
|
973
|
+
return actor.system.terminateActor(actor, termination);
|
|
974
|
+
}
|
|
975
|
+
};
|
|
976
|
+
// src/actorScope.ts
|
|
977
|
+
/** Marks the internal lazy scope without imposing structure on custom runtimes. @internal */
|
|
978
|
+
const lazyActorScope = Symbol();
|
|
979
|
+
/** Whether actor capabilities are represented by a lazy pure-transition scope. @internal */
|
|
980
|
+
function isLazyActorScope(actorScope) {
|
|
981
|
+
return !!actorScope[lazyActorScope];
|
|
982
|
+
}
|
|
983
|
+
/** Reads parent metadata without otherwise touching actor capabilities. @internal */
|
|
984
|
+
function getActorScopeParent(actorScope) {
|
|
985
|
+
return isLazyActorScope(actorScope) ? actorScope._parent : actorScope.self._parent;
|
|
986
|
+
}
|
|
987
|
+
/** Adds only `self`, preserving callback surfaces that expose no other capabilities. @internal */
|
|
988
|
+
function withActorSelf(args, actorScope) {
|
|
989
|
+
if (!isLazyActorScope(actorScope)) return Object.assign(args, { self: actorScope.self });
|
|
990
|
+
Object.defineProperty(args, "self", {
|
|
991
|
+
enumerable: true,
|
|
992
|
+
get: () => actorScope.self
|
|
993
|
+
});
|
|
994
|
+
return args;
|
|
995
|
+
}
|
|
996
|
+
/** Adds the actor reference and parent without exposing the system. @internal */
|
|
997
|
+
function withActorSelfAndParent(args, actorScope) {
|
|
998
|
+
if (!isLazyActorScope(actorScope)) {
|
|
999
|
+
const self = actorScope.self;
|
|
1000
|
+
return Object.assign(args, {
|
|
1001
|
+
self,
|
|
1002
|
+
parent: self._parent
|
|
1003
|
+
});
|
|
1004
|
+
}
|
|
1005
|
+
Object.defineProperties(args, {
|
|
1006
|
+
self: {
|
|
1007
|
+
enumerable: true,
|
|
1008
|
+
get: () => actorScope.self
|
|
1009
|
+
},
|
|
1010
|
+
parent: {
|
|
1011
|
+
enumerable: true,
|
|
1012
|
+
get: () => getActorScopeParent(actorScope)
|
|
1013
|
+
}
|
|
1014
|
+
});
|
|
1015
|
+
return args;
|
|
1016
|
+
}
|
|
1017
|
+
/** Adds actor capabilities without reading them until the callback does. @internal */
|
|
1018
|
+
function withActorScope(args, actorScope) {
|
|
1019
|
+
if (!isLazyActorScope(actorScope)) {
|
|
1020
|
+
const actorArgs = args;
|
|
1021
|
+
const self = actorScope.self;
|
|
1022
|
+
actorArgs.self = self;
|
|
1023
|
+
actorArgs.system = actorScope.system;
|
|
1024
|
+
actorArgs.parent = self._parent;
|
|
1025
|
+
return actorArgs;
|
|
1026
|
+
}
|
|
1027
|
+
Object.defineProperties(args, {
|
|
1028
|
+
self: {
|
|
1029
|
+
enumerable: true,
|
|
1030
|
+
get: () => actorScope.self
|
|
1031
|
+
},
|
|
1032
|
+
system: {
|
|
1033
|
+
enumerable: true,
|
|
1034
|
+
get: () => actorScope.system
|
|
1035
|
+
},
|
|
1036
|
+
parent: {
|
|
1037
|
+
enumerable: true,
|
|
1038
|
+
get: () => getActorScopeParent(actorScope)
|
|
1039
|
+
}
|
|
1040
|
+
});
|
|
1041
|
+
return args;
|
|
1042
|
+
}
|
|
1043
|
+
// src/delay.ts
|
|
1044
|
+
function parseDurationToMilliseconds(duration) {
|
|
1045
|
+
const normalizedDuration = duration.trim();
|
|
1046
|
+
const millisecondsMatch = normalizedDuration.match(/^(\d+)ms$/i);
|
|
1047
|
+
if (millisecondsMatch) return parseInt(millisecondsMatch[1] ?? "", 10);
|
|
1048
|
+
const secondsMatch = normalizedDuration.match(/^(\d*)(\.?)(\d*)s$/i);
|
|
1049
|
+
if (secondsMatch) {
|
|
1050
|
+
const wholePart = secondsMatch[1] ? parseInt(secondsMatch[1], 10) : 0;
|
|
1051
|
+
const hasDecimal = !!secondsMatch[2];
|
|
1052
|
+
const fracPart = secondsMatch[3] ? parseInt(secondsMatch[3].padEnd(3, "0").slice(0, 3), 10) : 0;
|
|
1053
|
+
return wholePart * 1e3 + (hasDecimal ? fracPart : 0);
|
|
1054
|
+
}
|
|
1055
|
+
const iso8601DurationMatch = normalizedDuration.match(/^P(?:(?<weeks>\d+(?:[.,]\d+)?)W)?(?:(?<days>\d+(?:[.,]\d+)?)D)?(?:T(?:(?<hours>\d+(?:[.,]\d+)?)H)?(?:(?<minutes>\d+(?:[.,]\d+)?)M)?(?:(?<seconds>\d+(?:[.,]\d+)?)S)?)?$/i);
|
|
1056
|
+
if (!iso8601DurationMatch?.groups) return;
|
|
1057
|
+
const { weeks, days, hours, minutes, seconds } = iso8601DurationMatch.groups;
|
|
1058
|
+
if (!weeks && !days && !hours && !minutes && !seconds) return;
|
|
1059
|
+
const toNumber = (value) => value ? Number(value.replace(",", ".")) : 0;
|
|
1060
|
+
return toNumber(weeks) * 7 * 24 * 60 * 60 * 1e3 + toNumber(days) * 24 * 60 * 60 * 1e3 + toNumber(hours) * 60 * 60 * 1e3 + toNumber(minutes) * 60 * 1e3 + toNumber(seconds) * 1e3;
|
|
1061
|
+
}
|
|
1062
|
+
/**
|
|
1063
|
+
* Converts a delay (milliseconds or an ISO 8601 duration string) to
|
|
1064
|
+
* milliseconds. Exported for `@xstate/scxml`; not part of the stable API.
|
|
1065
|
+
*
|
|
1066
|
+
* @experimental
|
|
1067
|
+
*/
|
|
1068
|
+
function parseDelayToMilliseconds(delay) {
|
|
1069
|
+
if (delay === void 0) return;
|
|
1070
|
+
if (typeof delay === "number") return delay;
|
|
1071
|
+
return parseDurationToMilliseconds(delay);
|
|
1072
|
+
}
|
|
1073
|
+
// src/stateNodePredicates.ts
|
|
1074
|
+
const IS_ATOMIC_TYPE = {
|
|
1075
|
+
atomic: true,
|
|
1076
|
+
choice: true,
|
|
1077
|
+
compound: false,
|
|
1078
|
+
final: true,
|
|
1079
|
+
history: false,
|
|
1080
|
+
parallel: false
|
|
1081
|
+
};
|
|
1082
|
+
function isAtomicStateNode(stateNode) {
|
|
1083
|
+
return IS_ATOMIC_TYPE[stateNode.type];
|
|
1084
|
+
}
|
|
1085
|
+
const isOrHasAncestor = (stateNode, ancestor) => stateNode === ancestor || hasAncestor(stateNode.parent, ancestor);
|
|
1086
|
+
const hasAncestor = (stateNode, ancestor) => stateNode === void 0 ? false : isOrHasAncestor(stateNode, ancestor);
|
|
1087
|
+
function isDescendant(args) {
|
|
1088
|
+
return hasAncestor(args.childStateNode.parent, args.parentStateNode);
|
|
1089
|
+
}
|
|
1090
|
+
// src/historyRecall.ts
|
|
1091
|
+
function isHistoryNode(stateNode) {
|
|
1092
|
+
return stateNode.type === "history";
|
|
1093
|
+
}
|
|
1094
|
+
const defaultHistoryTransition = (source, target) => ({
|
|
1095
|
+
target,
|
|
1096
|
+
source,
|
|
1097
|
+
reenter: false,
|
|
1098
|
+
eventType: ""
|
|
1099
|
+
});
|
|
1100
|
+
function recallHistory(args) {
|
|
1101
|
+
const recorded = args.historyValue[args.historyNode.id];
|
|
1102
|
+
return recorded === void 0 ? {
|
|
1103
|
+
type: "default",
|
|
1104
|
+
transition: defaultHistoryTransition(args.historyNode, args.defaultTargets())
|
|
1105
|
+
} : {
|
|
1106
|
+
type: "recorded",
|
|
1107
|
+
nodes: recorded
|
|
1108
|
+
};
|
|
1109
|
+
}
|
|
1110
|
+
function recordHistoryNodes(args) {
|
|
1111
|
+
const { historyNode, exitingNode, currentStateNodes } = args;
|
|
1112
|
+
if (historyNode.history === "deep") return currentStateNodes.filter((stateNode) => isAtomicStateNode(stateNode) && isDescendant({
|
|
1113
|
+
childStateNode: stateNode,
|
|
1114
|
+
parentStateNode: exitingNode
|
|
1115
|
+
}));
|
|
1116
|
+
return currentStateNodes.filter((stateNode) => stateNode.parent === exitingNode);
|
|
1117
|
+
}
|
|
1118
|
+
function restoresSourceViaHistory(args) {
|
|
1119
|
+
return args.targets.some(isHistoryNode) ? args.effectiveTargetStates.some((stateNode) => stateNode === args.source) : false;
|
|
1120
|
+
}
|
|
1121
|
+
// src/remoteActorRef.ts
|
|
1122
|
+
const emptySubscription = { unsubscribe() {} };
|
|
1123
|
+
const restoreHint = "or restore this snapshot with embedded children on the runtime that owns them.";
|
|
1124
|
+
/** @internal */
|
|
1125
|
+
function isRemoteActorRef(actorRef) {
|
|
1126
|
+
return actorRef._remote === true;
|
|
1127
|
+
}
|
|
1128
|
+
/**
|
|
1129
|
+
* The lifecycle-only snapshot a remote handle exposes. `active` is accurate
|
|
1130
|
+
* by construction: a completion removes the child from its parent's
|
|
1131
|
+
* `children`, so a handle only exists while the child is presumed active,
|
|
1132
|
+
* and the terminal result arrives as a completion event instead of a
|
|
1133
|
+
* snapshot. Full snapshots are a co-location capability.
|
|
1134
|
+
*/
|
|
1135
|
+
const remoteSnapshot = Object.freeze({
|
|
1136
|
+
status: "active",
|
|
1137
|
+
output: void 0,
|
|
1138
|
+
error: void 0
|
|
1139
|
+
});
|
|
1140
|
+
/**
|
|
1141
|
+
* Creates a location-transparent handle to an actor whose state lives with
|
|
1142
|
+
* another runtime. The handle is constructed from identity alone — no lookup
|
|
1143
|
+
* — and every send routes through the system's runtime; its state is not
|
|
1144
|
+
* synchronously readable.
|
|
1145
|
+
*
|
|
1146
|
+
* The surface is deliberately minimal: it covers every member the runtime
|
|
1147
|
+
* reaches for a restored child (send/_send, lifecycle via start/_stop and
|
|
1148
|
+
* system.stopActor, persistence, subscribe/on as inert subscriptions).
|
|
1149
|
+
* Members that only make sense for a co-located actor (public stop(),
|
|
1150
|
+
* select(), trigger) throw a descriptive error rather than being absent, so
|
|
1151
|
+
* calling one reports what went wrong instead of a bare TypeError. Observable
|
|
1152
|
+
* interop and _processingStatus are omitted entirely.
|
|
1153
|
+
*
|
|
1154
|
+
* @internal
|
|
1155
|
+
*/
|
|
1156
|
+
function createRemoteActorRef(system, options) {
|
|
1157
|
+
const handle = {
|
|
1158
|
+
_remote: true,
|
|
1159
|
+
ref: void 0,
|
|
1160
|
+
id: options.id,
|
|
1161
|
+
address: options.address,
|
|
1162
|
+
src: options.src,
|
|
1163
|
+
registryKey: options.registryKey,
|
|
1164
|
+
sessionId: options.incarnation,
|
|
1165
|
+
system,
|
|
1166
|
+
_parent: options.parent,
|
|
1167
|
+
_syncSnapshot: options.syncSnapshot,
|
|
1168
|
+
send(event) {
|
|
1169
|
+
system.sendEvent(void 0, ref, event);
|
|
1170
|
+
},
|
|
1171
|
+
_send(event) {
|
|
1172
|
+
throw new Error(`Remote actor '${options.address}' has no local mailbox to receive "${event.type}". Its state lives with another runtime; install a runtime that can reach it (via \`createDurable\`'s adapter runtime operations, or \`system.runtime\`) before sending, ${restoreHint}`);
|
|
1173
|
+
},
|
|
1174
|
+
getSnapshot() {
|
|
1175
|
+
return remoteSnapshot;
|
|
1176
|
+
},
|
|
1177
|
+
getPersistedSnapshot() {
|
|
1178
|
+
throw new Error(`Cannot persist remote actor '${options.address}' from here: its state lives with the runtime that owns it. Persist it there, ${restoreHint}`);
|
|
1179
|
+
},
|
|
1180
|
+
start() {},
|
|
1181
|
+
_stop() {},
|
|
1182
|
+
stop() {
|
|
1183
|
+
throw new Error(`Cannot stop remote actor '${options.address}' directly: stopping is a co-located operation. Stop it through the system runtime that owns it (\`system.stopActor(ref)\`), ${restoreHint}`);
|
|
1184
|
+
},
|
|
1185
|
+
select() {
|
|
1186
|
+
throw new Error(`Cannot select from remote actor '${options.address}': its snapshot is not synchronously readable because its state lives with another runtime. Read it on the runtime that owns it, ${restoreHint}`);
|
|
1187
|
+
},
|
|
1188
|
+
get trigger() {
|
|
1189
|
+
throw new Error(`Remote actor '${options.address}' has no \`trigger\` shorthand: it requires a co-located actor. Use \`send(...)\`, which routes through the system runtime.`);
|
|
1190
|
+
},
|
|
1191
|
+
subscribe() {
|
|
1192
|
+
return emptySubscription;
|
|
1193
|
+
},
|
|
1194
|
+
on() {
|
|
1195
|
+
return emptySubscription;
|
|
1196
|
+
},
|
|
1197
|
+
_isRunning() {
|
|
1198
|
+
return false;
|
|
1199
|
+
},
|
|
1200
|
+
toJSON() {
|
|
1201
|
+
return {
|
|
1202
|
+
xstate$type: ACTOR_REF_TYPE,
|
|
1203
|
+
id: options.id,
|
|
1204
|
+
address: options.address,
|
|
1205
|
+
src: options.src
|
|
1206
|
+
};
|
|
1207
|
+
}
|
|
1208
|
+
};
|
|
1209
|
+
const ref = handle;
|
|
1210
|
+
handle.ref = ref;
|
|
1211
|
+
return ref;
|
|
1212
|
+
}
|
|
1213
|
+
// src/stateMatching.ts
|
|
1214
|
+
const dotAndSegment = /\.(?:\\[\s\S]|[^.\\])*\\?/g;
|
|
1215
|
+
const escapedCharacter = /\\([\s\S])/g;
|
|
1216
|
+
const toStatePath = (stateId) => typeof stateId === "string" ? Array.from(`.${stateId}`.matchAll(dotAndSegment), ([dotted]) => dotted.slice(1).replace(escapedCharacter, "$1")) : [...stateId];
|
|
1217
|
+
/** @public */
|
|
1218
|
+
const pathToStateValue = (statePath) => {
|
|
1219
|
+
const leaf = statePath.at(-1);
|
|
1220
|
+
return leaf === void 0 ? {} : statePath.slice(0, -1).reduceRight((value, key) => ({ [key]: value }), leaf);
|
|
1221
|
+
};
|
|
1222
|
+
const toStateValue = (stateValue) => typeof stateValue === "string" ? pathToStateValue(toStatePath(stateValue)) : stateValue;
|
|
1223
|
+
const presentRegionMatches = (parentRegion, childRegion) => parentRegion !== void 0 && regionsMatch(parentRegion, childRegion);
|
|
1224
|
+
const regionMatches = (parentRegion, childRegion) => childRegion !== void 0 && presentRegionMatches(parentRegion, childRegion);
|
|
1225
|
+
const matchesRegions = (parent, child) => typeof parent === "string" ? parent in child : Object.keys(parent).every((key) => regionMatches(parent[key], child[key]));
|
|
1226
|
+
function regionsMatch(parent, child) {
|
|
1227
|
+
return typeof child === "string" ? parent === child : matchesRegions(parent, child);
|
|
1228
|
+
}
|
|
1229
|
+
const valueMatches = (parentStateValue, childStateValue) => regionsMatch(toStateValue(parentStateValue), toStateValue(childStateValue));
|
|
1230
|
+
function matchesState(...args) {
|
|
1231
|
+
if (args.length === 1) {
|
|
1232
|
+
const [childStateValue] = args;
|
|
1233
|
+
return (parentStateValue) => valueMatches(parentStateValue, childStateValue);
|
|
1234
|
+
}
|
|
1235
|
+
return valueMatches(args[0], args[1]);
|
|
1236
|
+
}
|
|
1237
|
+
// src/timerClock.ts
|
|
1238
|
+
const starts = /* @__PURE__ */ new WeakMap();
|
|
1239
|
+
/** @internal */
|
|
1240
|
+
function getTimerStart(timer) {
|
|
1241
|
+
return starts.get(timer) ?? timer.startedAt;
|
|
1242
|
+
}
|
|
1243
|
+
/** @internal */
|
|
1244
|
+
function recordTimerStart(timer, startedAt) {
|
|
1245
|
+
starts.set(timer, startedAt);
|
|
1246
|
+
}
|
|
1247
|
+
// src/State.ts
|
|
1248
|
+
const emptySnapshotRecord = Object.freeze({});
|
|
1249
|
+
function compactSnapshotRecord(value) {
|
|
1250
|
+
if (value === emptySnapshotRecord) return value;
|
|
1251
|
+
return value && Object.keys(value).length ? value : emptySnapshotRecord;
|
|
1252
|
+
}
|
|
1253
|
+
/** @public */
|
|
1254
|
+
function isMachineSnapshot(value) {
|
|
1255
|
+
return !!value && typeof value === "object" && "machine" in value && "value" in value;
|
|
1256
|
+
}
|
|
1257
|
+
const machineSnapshotMatches = function matches(testValue) {
|
|
1258
|
+
return matchesState(testValue, this.value);
|
|
1259
|
+
};
|
|
1260
|
+
const machineSnapshotHasTag = function hasTag(tag) {
|
|
1261
|
+
return this.tags.has(tag);
|
|
1262
|
+
};
|
|
1263
|
+
const machineSnapshotCan = function can(event) {
|
|
1264
|
+
return this.machine?._canTransition(this, event) ?? false;
|
|
1265
|
+
};
|
|
1266
|
+
const machineSnapshotToJSON = function toJSON() {
|
|
1267
|
+
const { nodes, _stateInputs, tags, machine, getMeta, getInputs, toJSON, can, hasTag, matches, ...jsonValues } = this;
|
|
1268
|
+
return {
|
|
1269
|
+
...jsonValues,
|
|
1270
|
+
tags: Array.from(tags)
|
|
1271
|
+
};
|
|
1272
|
+
};
|
|
1273
|
+
const machineSnapshotGetMeta = function getMeta() {
|
|
1274
|
+
const meta = {};
|
|
1275
|
+
for (const stateNode of this.nodes) if (stateNode.meta !== void 0) meta[stateNode.id] = stateNode.meta;
|
|
1276
|
+
return meta;
|
|
1277
|
+
};
|
|
1278
|
+
const machineSnapshotGetInputs = function getInputs() {
|
|
1279
|
+
return this._stateInputs;
|
|
1280
|
+
};
|
|
1281
|
+
function collectTags(stateNodes) {
|
|
1282
|
+
const tags = /* @__PURE__ */ new Set();
|
|
1283
|
+
for (const stateNode of stateNodes) for (const tag of stateNode.tags) tags.add(tag);
|
|
1284
|
+
return tags;
|
|
1285
|
+
}
|
|
1286
|
+
function createMachineSnapshot(config, machine, actorRef) {
|
|
1287
|
+
const snapshot = {
|
|
1288
|
+
status: config.status,
|
|
1289
|
+
output: config.output,
|
|
1290
|
+
error: config.error,
|
|
1291
|
+
machine,
|
|
1292
|
+
context: config.context,
|
|
1293
|
+
nodes: config._nodes,
|
|
1294
|
+
value: config.value ?? getStateValue(machine.root, config._nodes),
|
|
1295
|
+
tags: collectTags(config._nodes),
|
|
1296
|
+
children: compactSnapshotRecord(config.children),
|
|
1297
|
+
timers: compactSnapshotRecord(config.timers),
|
|
1298
|
+
historyValue: compactSnapshotRecord(config.historyValue),
|
|
1299
|
+
_stateInputs: compactSnapshotRecord(config._stateInputs),
|
|
1300
|
+
_nextTimerId: config._nextTimerId ?? 0,
|
|
1301
|
+
_nextActorIds: config._nextActorIds,
|
|
1302
|
+
matches: machineSnapshotMatches,
|
|
1303
|
+
hasTag: machineSnapshotHasTag,
|
|
1304
|
+
can: machineSnapshotCan,
|
|
1305
|
+
getMeta: machineSnapshotGetMeta,
|
|
1306
|
+
getInputs: machineSnapshotGetInputs,
|
|
1307
|
+
toJSON: machineSnapshotToJSON
|
|
1308
|
+
};
|
|
1309
|
+
if (actorRef) setSnapshotActorRef(snapshot, actorRef);
|
|
1310
|
+
return snapshot;
|
|
1311
|
+
}
|
|
1312
|
+
function cloneMachineSnapshot(snapshot, config = {}) {
|
|
1313
|
+
const configWithSnapshot = {
|
|
1314
|
+
...snapshot,
|
|
1315
|
+
...config
|
|
1316
|
+
};
|
|
1317
|
+
if ((config._nodes ?? snapshot.nodes) === snapshot.nodes) {
|
|
1318
|
+
const clonedSnapshot = {
|
|
1319
|
+
status: configWithSnapshot.status,
|
|
1320
|
+
output: configWithSnapshot.output,
|
|
1321
|
+
error: configWithSnapshot.error,
|
|
1322
|
+
machine: snapshot.machine,
|
|
1323
|
+
context: configWithSnapshot.context,
|
|
1324
|
+
nodes: snapshot.nodes,
|
|
1325
|
+
value: snapshot.value,
|
|
1326
|
+
tags: snapshot.tags,
|
|
1327
|
+
children: compactSnapshotRecord(configWithSnapshot.children),
|
|
1328
|
+
timers: compactSnapshotRecord(configWithSnapshot.timers),
|
|
1329
|
+
historyValue: compactSnapshotRecord(configWithSnapshot.historyValue),
|
|
1330
|
+
_stateInputs: compactSnapshotRecord(configWithSnapshot._stateInputs),
|
|
1331
|
+
_nextTimerId: configWithSnapshot._nextTimerId ?? 0,
|
|
1332
|
+
_nextActorIds: configWithSnapshot._nextActorIds,
|
|
1333
|
+
matches: machineSnapshotMatches,
|
|
1334
|
+
hasTag: machineSnapshotHasTag,
|
|
1335
|
+
can: machineSnapshotCan,
|
|
1336
|
+
getMeta: machineSnapshotGetMeta,
|
|
1337
|
+
getInputs: machineSnapshotGetInputs,
|
|
1338
|
+
toJSON: machineSnapshotToJSON
|
|
1339
|
+
};
|
|
1340
|
+
copySnapshotActorRef(snapshot, clonedSnapshot);
|
|
1341
|
+
return clonedSnapshot;
|
|
1342
|
+
}
|
|
1343
|
+
const clonedSnapshot = createMachineSnapshot({
|
|
1344
|
+
...configWithSnapshot,
|
|
1345
|
+
value: void 0
|
|
1346
|
+
}, snapshot.machine);
|
|
1347
|
+
copySnapshotActorRef(snapshot, clonedSnapshot);
|
|
1348
|
+
return clonedSnapshot;
|
|
1349
|
+
}
|
|
1350
|
+
function serializeHistoryValue(historyValue) {
|
|
1351
|
+
const result = {};
|
|
1352
|
+
for (const key in historyValue) {
|
|
1353
|
+
const value = historyValue[key];
|
|
1354
|
+
if (Array.isArray(value)) result[key] = value.map((item) => ({ id: item.id }));
|
|
1355
|
+
}
|
|
1356
|
+
return result;
|
|
1357
|
+
}
|
|
1358
|
+
function getPersistedSnapshot(snapshot, options, warn = defaultWarn) {
|
|
1359
|
+
const { nodes, _stateInputs, tags, machine, children, timers, context, can, hasTag, matches, getMeta, getInputs, toJSON, ...jsonValues } = snapshot;
|
|
1360
|
+
const childrenJson = {};
|
|
1361
|
+
const timersJson = {};
|
|
1362
|
+
const embedChildren = options?.embedChildren !== false;
|
|
1363
|
+
for (const id in children) {
|
|
1364
|
+
const child = children[id];
|
|
1365
|
+
if (typeof child.src !== "string" && (!options || !("__unsafeAllowInlineActors" in options))) throw new Error("An inline child actor cannot be persisted.");
|
|
1366
|
+
const embed = embedChildren && !isRemoteActorRef(child);
|
|
1367
|
+
if (!embed && typeof child.src !== "string") throw new Error(`Unable to persist child '${id}' by address: it requires a registered source key.`);
|
|
1368
|
+
childrenJson[id] = {
|
|
1369
|
+
address: child.address,
|
|
1370
|
+
...embed ? { snapshot: child.getPersistedSnapshot(options) } : {
|
|
1371
|
+
remote: true,
|
|
1372
|
+
incarnation: isRemoteActorRef(child) ? child.sessionId : void 0
|
|
1373
|
+
},
|
|
1374
|
+
src: child.src,
|
|
1375
|
+
registryKey: child.registryKey,
|
|
1376
|
+
syncSnapshot: child._syncSnapshot
|
|
1377
|
+
};
|
|
1378
|
+
}
|
|
1379
|
+
const snapshotActor = getSnapshotActorRef(snapshot)?.actor;
|
|
1380
|
+
const scheduledTimers = snapshotActor?.system?._clock?.now ? void 0 : snapshotActor?.system?._snapshot?._scheduledTimers;
|
|
1381
|
+
for (const [id, timer] of Object.entries(timers)) {
|
|
1382
|
+
let event = timer.event;
|
|
1383
|
+
if (event.type === "xstate.timeout.actor") {
|
|
1384
|
+
event = { ...event };
|
|
1385
|
+
delete event.sessionId;
|
|
1386
|
+
}
|
|
1387
|
+
let target;
|
|
1388
|
+
if (timer.target === "self") target = "self";
|
|
1389
|
+
else {
|
|
1390
|
+
const childId = Object.entries(children).find(([, child]) => child === timer.target)?.[0];
|
|
1391
|
+
if (childId) target = childId;
|
|
1392
|
+
else if (timer.target === getSnapshotActorRef(snapshot)?.actor._parent) target = { type: "parent" };
|
|
1393
|
+
else throw new Error(`Unable to persist timer '${id}': target actor '${timer.target.id}' is no longer addressable from this snapshot.`);
|
|
1394
|
+
}
|
|
1395
|
+
const scheduled = scheduledTimers?.[`${snapshotActor.sessionId}.${id}`];
|
|
1396
|
+
const startedAt = scheduled ? scheduled.dueAt - timer.delay : getTimerStart(timer);
|
|
1397
|
+
timersJson[id] = {
|
|
1398
|
+
id: timer.id,
|
|
1399
|
+
delay: timer.delay,
|
|
1400
|
+
type: timer.type,
|
|
1401
|
+
event,
|
|
1402
|
+
target,
|
|
1403
|
+
...startedAt !== void 0 && { startedAt }
|
|
1404
|
+
};
|
|
1405
|
+
}
|
|
1406
|
+
const persisted = {
|
|
1407
|
+
...jsonValues,
|
|
1408
|
+
context: persistContext(context, () => getSnapshotActorRef(snapshot)?.actor.id ?? machine.id),
|
|
1409
|
+
children: childrenJson,
|
|
1410
|
+
timers: timersJson,
|
|
1411
|
+
historyValue: serializeHistoryValue(jsonValues.historyValue)
|
|
1412
|
+
};
|
|
1413
|
+
if (_stateInputs && Object.keys(_stateInputs).length > 0) persisted["stateInputs"] = _stateInputs;
|
|
1414
|
+
if (machine.version !== void 0) {
|
|
1415
|
+
persisted["machine"] = {
|
|
1416
|
+
id: machine.id,
|
|
1417
|
+
version: machine.version
|
|
1418
|
+
};
|
|
1419
|
+
persisted["version"] = machine.version;
|
|
1420
|
+
}
|
|
1421
|
+
return persisted;
|
|
1422
|
+
}
|
|
1423
|
+
function persistContext(contextPart, getActorId, path = "context", ancestors = []) {
|
|
1424
|
+
ancestors.push(contextPart);
|
|
1425
|
+
let copy;
|
|
1426
|
+
for (const key in contextPart) {
|
|
1427
|
+
const value = contextPart[key];
|
|
1428
|
+
if (value && typeof value === "object") {
|
|
1429
|
+
if (isActorRefLike(value)) {
|
|
1430
|
+
copy ??= Array.isArray(contextPart) ? contextPart.slice() : { ...contextPart };
|
|
1431
|
+
copy[key] = {
|
|
1432
|
+
xstate$type: ACTOR_REF_TYPE,
|
|
1433
|
+
id: value.id
|
|
1434
|
+
};
|
|
1435
|
+
} else {
|
|
1436
|
+
const valuePath = Array.isArray(contextPart) ? `${path}[${key}]` : `${path}.${key}`;
|
|
1437
|
+
if (ancestors.includes(value)) throw new Error(`Cannot persist actor "${getActorId()}": circular reference at ${valuePath}`);
|
|
1438
|
+
const result = persistContext(value, getActorId, valuePath, ancestors);
|
|
1439
|
+
if (result !== value) {
|
|
1440
|
+
copy ??= Array.isArray(contextPart) ? contextPart.slice() : { ...contextPart };
|
|
1441
|
+
copy[key] = result;
|
|
1442
|
+
}
|
|
1443
|
+
}
|
|
1444
|
+
}
|
|
1445
|
+
}
|
|
1446
|
+
ancestors.pop();
|
|
1447
|
+
return copy ?? contextPart;
|
|
1448
|
+
}
|
|
1449
|
+
// src/inertActorScope.ts
|
|
1450
|
+
/** @internal */
|
|
1451
|
+
function setInertActorScopeSnapshot(actorScope, snapshot, attachActorRef = true) {
|
|
1452
|
+
const lazyState = getLazyInertActorState(actorScope);
|
|
1453
|
+
if (lazyState) {
|
|
1454
|
+
lazyState.snapshot = snapshot;
|
|
1455
|
+
if (lazyState.materialized) lazyState.materialized.self._snapshot = snapshot;
|
|
1456
|
+
} else actorScope.self._snapshot = snapshot;
|
|
1457
|
+
if (attachActorRef && snapshot && typeof snapshot === "object") setSnapshotActorRef(snapshot, actorScope.self);
|
|
1458
|
+
return snapshot;
|
|
1459
|
+
}
|
|
1460
|
+
/** @internal */
|
|
1461
|
+
function isInertActorScope(actorScope) {
|
|
1462
|
+
return !!getLazyInertActorState(actorScope) || !!actorScope.self.options?._inert;
|
|
1463
|
+
}
|
|
1464
|
+
/** @internal */
|
|
1465
|
+
function attachSnapshotActorRef(actorScope, snapshot) {
|
|
1466
|
+
setInertActorScopeSnapshot(actorScope, snapshot, false);
|
|
1467
|
+
const lazyState = getLazyInertActorState(actorScope);
|
|
1468
|
+
snapshotActorScopes.set(snapshot, actorScope);
|
|
1469
|
+
const create = () => {
|
|
1470
|
+
setSnapshotActorRef(snapshot, actorScope.self, actorScope.system);
|
|
1471
|
+
return getSnapshotActorRef(snapshot);
|
|
1472
|
+
};
|
|
1473
|
+
if (lazyState) {
|
|
1474
|
+
if (lazyState.materialized) lazyState.identityProvider = create;
|
|
1475
|
+
else lazyState.identityProvider ??= create;
|
|
1476
|
+
}
|
|
1477
|
+
setLazySnapshotActorRef(snapshot, create);
|
|
1478
|
+
return snapshot;
|
|
1479
|
+
}
|
|
1480
|
+
const lazyInertActorState = Symbol();
|
|
1481
|
+
const snapshotActorScopes = /* @__PURE__ */ new WeakMap();
|
|
1482
|
+
function getLazyInertActorState(actorScope) {
|
|
1483
|
+
return actorScope[lazyInertActorState];
|
|
1484
|
+
}
|
|
1485
|
+
function materializeInertActorScope(actorScope) {
|
|
1486
|
+
return getLazyInertActorState(actorScope).materialize();
|
|
1487
|
+
}
|
|
1488
|
+
const lazyInertActorScopePrototype = {
|
|
1489
|
+
[lazyActorScope]: true,
|
|
1490
|
+
get _parent() {
|
|
1491
|
+
const actorScope = this;
|
|
1492
|
+
const state = getLazyInertActorState(actorScope);
|
|
1493
|
+
return state.parentKnown ? state.parent : materializeInertActorScope(actorScope).self._parent;
|
|
1494
|
+
},
|
|
1495
|
+
get self() {
|
|
1496
|
+
return materializeInertActorScope(this).self;
|
|
1497
|
+
},
|
|
1498
|
+
get defer() {
|
|
1499
|
+
return materializeInertActorScope(this).defer;
|
|
1500
|
+
},
|
|
1501
|
+
get id() {
|
|
1502
|
+
return materializeInertActorScope(this).id;
|
|
1503
|
+
},
|
|
1504
|
+
get logger() {
|
|
1505
|
+
return materializeInertActorScope(this).logger;
|
|
1506
|
+
},
|
|
1507
|
+
get warn() {
|
|
1508
|
+
return materializeInertActorScope(this).warn;
|
|
1509
|
+
},
|
|
1510
|
+
get sessionId() {
|
|
1511
|
+
return materializeInertActorScope(this).sessionId;
|
|
1512
|
+
},
|
|
1513
|
+
get stopChild() {
|
|
1514
|
+
return materializeInertActorScope(this).stopChild;
|
|
1515
|
+
},
|
|
1516
|
+
get system() {
|
|
1517
|
+
return materializeInertActorScope(this).system;
|
|
1518
|
+
},
|
|
1519
|
+
get emit() {
|
|
1520
|
+
return materializeInertActorScope(this).emit;
|
|
1521
|
+
},
|
|
1522
|
+
get actionExecutor() {
|
|
1523
|
+
return materializeInertActorScope(this).actionExecutor;
|
|
1524
|
+
}
|
|
1525
|
+
};
|
|
1526
|
+
function createMaterializedInertActorScope(actorLogic, sourceRef, sourceChildren, currentSnapshot, sourceSelf) {
|
|
1527
|
+
const snapshotRef = sourceRef?.();
|
|
1528
|
+
const previousSelf = sourceSelf ?? snapshotRef?.actor;
|
|
1529
|
+
const baseSystem = previousSelf?.system;
|
|
1530
|
+
const system = previousSelf && baseSystem ? createSnapshotSystem(baseSystem, sourceChildren, sourceSelf ? void 0 : snapshotRef?.systemState) : void 0;
|
|
1531
|
+
const self = createActor(actorLogic, {
|
|
1532
|
+
_inert: true,
|
|
1533
|
+
...previousSelf ? {
|
|
1534
|
+
id: previousSelf.id,
|
|
1535
|
+
_sessionId: previousSelf.sessionId
|
|
1536
|
+
} : {},
|
|
1537
|
+
...system ? { _systemRef: { current: system } } : {}
|
|
1538
|
+
});
|
|
1539
|
+
if (previousSelf?._parent) {
|
|
1540
|
+
self._parent = previousSelf._parent;
|
|
1541
|
+
self._address = void 0;
|
|
1542
|
+
}
|
|
1543
|
+
if (currentSnapshot) self._snapshot = currentSnapshot;
|
|
1544
|
+
return Object.create(self._actorScope, {
|
|
1545
|
+
defer: { value: () => {} },
|
|
1546
|
+
stopChild: { value: (child) => child._stop() },
|
|
1547
|
+
actionExecutor: { value: () => {} }
|
|
1548
|
+
});
|
|
1549
|
+
}
|
|
1550
|
+
/** @internal */
|
|
1551
|
+
function createInertActorScope(actorLogic, snapshot, sourceSelf, sourceActorScope) {
|
|
1552
|
+
const sourceScope = sourceActorScope ?? (snapshot && typeof snapshot === "object" ? snapshotActorScopes.get(snapshot) : void 0);
|
|
1553
|
+
const sourceState = sourceScope ? getLazyInertActorState(sourceScope) : void 0;
|
|
1554
|
+
const eagerSourceRef = snapshot && typeof snapshot === "object" ? peekSnapshotActorRef(snapshot) : void 0;
|
|
1555
|
+
const sourceRef = sourceState?.identityProvider ?? (snapshot && typeof snapshot === "object" ? getSnapshotActorRefProvider(snapshot) : void 0);
|
|
1556
|
+
const state = {
|
|
1557
|
+
snapshot,
|
|
1558
|
+
sourceRef,
|
|
1559
|
+
sourceChildren: isMachineSnapshot(snapshot) ? snapshot.children : {},
|
|
1560
|
+
identityProvider: sourceState?.identityProvider ?? sourceRef,
|
|
1561
|
+
parent: sourceSelf?._parent ?? sourceState?.parent ?? eagerSourceRef?.actor._parent,
|
|
1562
|
+
parentKnown: !!sourceSelf || !!sourceState?.parentKnown || !!eagerSourceRef || !snapshot
|
|
1563
|
+
};
|
|
1564
|
+
state.materialize = () => state.materialized ??= createMaterializedInertActorScope(actorLogic, state.sourceRef, state.sourceChildren, state.snapshot, sourceSelf);
|
|
1565
|
+
if (!state.identityProvider && !snapshot) {
|
|
1566
|
+
const identitySnapshot = {};
|
|
1567
|
+
const identitySourceRef = state.sourceRef;
|
|
1568
|
+
const identitySourceChildren = state.sourceChildren;
|
|
1569
|
+
let identityScope;
|
|
1570
|
+
let identityRef;
|
|
1571
|
+
const getIdentityScope = () => identityScope ??= createMaterializedInertActorScope(actorLogic, identitySourceRef, identitySourceChildren, void 0, sourceSelf);
|
|
1572
|
+
state.materialize = () => {
|
|
1573
|
+
const scope = getIdentityScope();
|
|
1574
|
+
if (state.snapshot !== void 0) scope.self._snapshot = state.snapshot;
|
|
1575
|
+
return state.materialized = scope;
|
|
1576
|
+
};
|
|
1577
|
+
state.identityProvider = () => {
|
|
1578
|
+
if (identityRef) return identityRef;
|
|
1579
|
+
const scope = getIdentityScope();
|
|
1580
|
+
setSnapshotActorRef(identitySnapshot, scope.self, scope.system);
|
|
1581
|
+
return identityRef = getSnapshotActorRef(identitySnapshot);
|
|
1582
|
+
};
|
|
1583
|
+
}
|
|
1584
|
+
const actorScope = Object.create(lazyInertActorScopePrototype);
|
|
1585
|
+
actorScope[lazyInertActorState] = state;
|
|
1586
|
+
return actorScope;
|
|
1587
|
+
}
|
|
1588
|
+
// src/systemLogicMetadata.ts
|
|
1589
|
+
/** Static scheduling metadata on actor definitions, never on live actors. @internal */
|
|
1590
|
+
const systemLogicMetadata = Symbol("xstate.systemLogicMetadata");
|
|
1591
|
+
// src/actors/attached.ts
|
|
1592
|
+
const subscriptions = /* #__PURE__ */ new WeakMap();
|
|
1593
|
+
/**
|
|
1594
|
+
* Internal factory for actor logic that attaches to another actor on start
|
|
1595
|
+
* (`enq.listen()` / `enq.subscribeTo()`) and detaches when stopped.
|
|
1596
|
+
*/
|
|
1597
|
+
function createAttachedLogic(attach, id) {
|
|
1598
|
+
const initialTransition = (input, _) => [{
|
|
1599
|
+
status: "active",
|
|
1600
|
+
output: void 0,
|
|
1601
|
+
error: void 0,
|
|
1602
|
+
input
|
|
1603
|
+
}, []];
|
|
1604
|
+
const logic = {
|
|
1605
|
+
id,
|
|
1606
|
+
start: (state, { self, system }) => {
|
|
1607
|
+
const target = state.input.actor;
|
|
1608
|
+
if (!target || target.getSnapshot().status === "stopped") return;
|
|
1609
|
+
const subscription = attach(state.input, {
|
|
1610
|
+
self,
|
|
1611
|
+
system
|
|
1612
|
+
});
|
|
1613
|
+
if (!subscription) return;
|
|
1614
|
+
let parentSubscription;
|
|
1615
|
+
let torndown = false;
|
|
1616
|
+
const teardown = () => {
|
|
1617
|
+
if (torndown) return;
|
|
1618
|
+
torndown = true;
|
|
1619
|
+
subscription.unsubscribe();
|
|
1620
|
+
parentSubscription?.unsubscribe();
|
|
1621
|
+
};
|
|
1622
|
+
const parent = self._parent;
|
|
1623
|
+
if (parent) parentSubscription = parent.subscribe({
|
|
1624
|
+
complete: teardown,
|
|
1625
|
+
error: teardown
|
|
1626
|
+
});
|
|
1627
|
+
subscriptions.set(self, { unsubscribe: teardown });
|
|
1628
|
+
},
|
|
1629
|
+
transition: (state, event, { self }) => {
|
|
1630
|
+
if (event.type === "@xstate.stop") {
|
|
1631
|
+
subscriptions.get(self)?.unsubscribe();
|
|
1632
|
+
subscriptions.delete(self);
|
|
1633
|
+
return [{
|
|
1634
|
+
...state,
|
|
1635
|
+
status: "stopped",
|
|
1636
|
+
error: void 0
|
|
1637
|
+
}, []];
|
|
1638
|
+
}
|
|
1639
|
+
return [state, []];
|
|
1640
|
+
},
|
|
1641
|
+
initialTransition,
|
|
1642
|
+
getInitialSnapshot: (actorScope, input) => initialTransition(input, actorScope)[0],
|
|
1643
|
+
getPersistedSnapshot: (snapshot) => snapshot,
|
|
1644
|
+
restoreSnapshot: (snapshot) => snapshot
|
|
1645
|
+
};
|
|
1646
|
+
Object.defineProperty(logic, systemLogicMetadata, { value: { kind: id } });
|
|
1647
|
+
return logic;
|
|
1648
|
+
}
|
|
1649
|
+
/**
|
|
1650
|
+
* Relays a mapped event to the attached actor's parent, unless the attached
|
|
1651
|
+
* actor has been stopped (the mapper is not called in that case).
|
|
1652
|
+
*/
|
|
1653
|
+
function relayMappedToParent(self, system, getEvent) {
|
|
1654
|
+
if (self.getSnapshot().status === "stopped" || !self._parent) return;
|
|
1655
|
+
system._relay(self, self._parent, getEvent());
|
|
1656
|
+
}
|
|
1657
|
+
// src/actors/listener.ts
|
|
1658
|
+
/**
|
|
1659
|
+
* Creates actor logic for listening to emitted events from another actor. Used
|
|
1660
|
+
* internally by `enq.listen()`.
|
|
1661
|
+
*
|
|
1662
|
+
* @public
|
|
1663
|
+
*/
|
|
1664
|
+
function createListenerLogic() {
|
|
1665
|
+
return createAttachedLogic(({ actor, eventType, mapper }, { self, system }) => {
|
|
1666
|
+
const isPartialWildcard = eventType !== "*" && eventType.endsWith(".*");
|
|
1667
|
+
return actor.on(isPartialWildcard ? "*" : eventType, (emittedEvent) => {
|
|
1668
|
+
if (isPartialWildcard && !matchesEventDescriptor(emittedEvent.type, eventType, system._warn)) return;
|
|
1669
|
+
relayMappedToParent(self, system, () => mapper(emittedEvent));
|
|
1670
|
+
});
|
|
1671
|
+
}, "xstate.listener");
|
|
1672
|
+
}
|
|
1673
|
+
/** @public */
|
|
1674
|
+
const listenerLogic = /* #__PURE__ */ createListenerLogic();
|
|
1675
|
+
// src/actors/subscription.ts
|
|
1676
|
+
/**
|
|
1677
|
+
* Creates actor logic for subscribing to lifecycle events (done/error/snapshot)
|
|
1678
|
+
* from another actor. Used internally by `enq.subscribeTo()`.
|
|
1679
|
+
*
|
|
1680
|
+
* @public
|
|
1681
|
+
*/
|
|
1682
|
+
function createSubscriptionLogic() {
|
|
1683
|
+
return createAttachedLogic(({ actor, mappers }, { self, system }) => {
|
|
1684
|
+
const { done, error, snapshot: onSnapshot } = mappers;
|
|
1685
|
+
return actor.subscribe({
|
|
1686
|
+
next: (snapshot) => {
|
|
1687
|
+
if (snapshot.status === "done" && done) relayMappedToParent(self, system, () => done(snapshot.output));
|
|
1688
|
+
else if (snapshot.status === "error" && error) relayMappedToParent(self, system, () => error(snapshot.error));
|
|
1689
|
+
else if (snapshot.status === "active" && onSnapshot) relayMappedToParent(self, system, () => onSnapshot(snapshot));
|
|
1690
|
+
},
|
|
1691
|
+
error: (err) => {
|
|
1692
|
+
if (error) relayMappedToParent(self, system, () => error(err));
|
|
1693
|
+
},
|
|
1694
|
+
complete: () => {}
|
|
1695
|
+
});
|
|
1696
|
+
}, "xstate.subscription");
|
|
1697
|
+
}
|
|
1698
|
+
/** @public */
|
|
1699
|
+
const subscriptionLogic = /* #__PURE__ */ createSubscriptionLogic();
|
|
1700
|
+
// src/actorSource.ts
|
|
1701
|
+
const sourceKeyCaches = /* @__PURE__ */ new WeakMap();
|
|
1702
|
+
/** @internal */
|
|
1703
|
+
function resolveRegisteredActorSource(actors, logic) {
|
|
1704
|
+
let cache = sourceKeyCaches.get(actors);
|
|
1705
|
+
if (!cache) {
|
|
1706
|
+
cache = /* @__PURE__ */ new Map();
|
|
1707
|
+
for (const [key, registeredLogic] of Object.entries(actors)) if (!cache.has(registeredLogic)) cache.set(registeredLogic, key);
|
|
1708
|
+
sourceKeyCaches.set(actors, cache);
|
|
1709
|
+
}
|
|
1710
|
+
return cache.get(logic);
|
|
1711
|
+
}
|
|
1712
|
+
// src/transitionActions.ts
|
|
1713
|
+
function execCustomEffect() {
|
|
1714
|
+
return this.action?.(...this.args);
|
|
1715
|
+
}
|
|
1716
|
+
function execEmitEffect(runtime = this.source.system) {
|
|
1717
|
+
return runtime.emitEvent(this.source, this.event);
|
|
1718
|
+
}
|
|
1719
|
+
/** @internal Creates an emitted-event effect. */
|
|
1720
|
+
function createEmitEffect(actorScope, event) {
|
|
1721
|
+
return {
|
|
1722
|
+
kind: "emit",
|
|
1723
|
+
exec: execEmitEffect,
|
|
1724
|
+
type: event.type,
|
|
1725
|
+
source: actorScope.self,
|
|
1726
|
+
event,
|
|
1727
|
+
params: void 0,
|
|
1728
|
+
args: []
|
|
1729
|
+
};
|
|
1730
|
+
}
|
|
1731
|
+
function execDeadLetterEffect(runtime = (this.target ?? this.source).system) {
|
|
1732
|
+
return runtime.deadLetter(this.source, this.target, this.event, this.reason, this.detail);
|
|
1733
|
+
}
|
|
1734
|
+
/** @internal Creates a dead-letter effect for a boundary-rejected event. */
|
|
1735
|
+
function createDeadLetterEffect(actorScope, source, event, reason, detail) {
|
|
1736
|
+
return {
|
|
1737
|
+
kind: "builtin",
|
|
1738
|
+
type: "@xstate.deadLetter",
|
|
1739
|
+
exec: execDeadLetterEffect,
|
|
1740
|
+
source,
|
|
1741
|
+
target: actorScope.self,
|
|
1742
|
+
event,
|
|
1743
|
+
reason,
|
|
1744
|
+
detail,
|
|
1745
|
+
params: void 0,
|
|
1746
|
+
args: []
|
|
1747
|
+
};
|
|
1748
|
+
}
|
|
1749
|
+
/**
|
|
1750
|
+
* @internal Creates a dead-letter effect for an `enq.sendTo` whose target is
|
|
1751
|
+
* an undefined ref, an unknown child id, or the parent of a root actor.
|
|
1752
|
+
*/
|
|
1753
|
+
function createMissingTargetEffect(actorScope, event, targetId) {
|
|
1754
|
+
return {
|
|
1755
|
+
kind: "builtin",
|
|
1756
|
+
type: "@xstate.deadLetter",
|
|
1757
|
+
exec: execDeadLetterEffect,
|
|
1758
|
+
source: actorScope.self,
|
|
1759
|
+
target: void 0,
|
|
1760
|
+
event,
|
|
1761
|
+
reason: "missingTarget",
|
|
1762
|
+
detail: {
|
|
1763
|
+
targetId,
|
|
1764
|
+
error: /* @__PURE__ */ new Error(targetId !== void 0 ? `Unable to send event to unknown child '${targetId}'` : "Unable to send event to an undefined actor")
|
|
1765
|
+
},
|
|
1766
|
+
params: void 0,
|
|
1767
|
+
args: []
|
|
1768
|
+
};
|
|
1769
|
+
}
|
|
1770
|
+
/** @internal Creates a directly executable user effect. */
|
|
1771
|
+
function createCustomEffect(type, action, params) {
|
|
1772
|
+
return {
|
|
1773
|
+
kind: "action",
|
|
1774
|
+
exec: action,
|
|
1775
|
+
type,
|
|
1776
|
+
action,
|
|
1777
|
+
params,
|
|
1778
|
+
args: []
|
|
1779
|
+
};
|
|
1780
|
+
}
|
|
1781
|
+
function execSpawnEffect(runtime = this.actor.system) {
|
|
1782
|
+
return runtime.spawnActor(this.source, this.actor);
|
|
1783
|
+
}
|
|
1784
|
+
function execStartEffect(runtime = this.actor.system) {
|
|
1785
|
+
if (this.actor._processingStatus === 2) return;
|
|
1786
|
+
return runtime.startActor(this.actor);
|
|
1787
|
+
}
|
|
1788
|
+
function execStopEffect(runtime = this.source.system) {
|
|
1789
|
+
return runtime.stopActor(this.actor);
|
|
1790
|
+
}
|
|
1791
|
+
function execTerminateEffect(runtime = this.actor.system) {
|
|
1792
|
+
const termination = this.status === "done" ? {
|
|
1793
|
+
status: "done",
|
|
1794
|
+
output: this.output,
|
|
1795
|
+
error: void 0
|
|
1796
|
+
} : {
|
|
1797
|
+
status: "error",
|
|
1798
|
+
output: void 0,
|
|
1799
|
+
error: this.error
|
|
1800
|
+
};
|
|
1801
|
+
return runtime.terminateActor(this.actor, termination);
|
|
1802
|
+
}
|
|
1803
|
+
function execRaiseEffect(runtime = this.source.system) {
|
|
1804
|
+
return runtime.scheduleTimer(this.source, this.id, this.delay ?? 0);
|
|
1805
|
+
}
|
|
1806
|
+
function execSendToEffect(runtime = this.source.system) {
|
|
1807
|
+
assertSendToEvent(this.event);
|
|
1808
|
+
if (this.delay !== void 0) return runtime.scheduleTimer(this.source, this.id, this.delay);
|
|
1809
|
+
return runtime.sendEvent(this.source, this.target, this.event);
|
|
1810
|
+
}
|
|
1811
|
+
function execCancelEffect(runtime = this.source.system) {
|
|
1812
|
+
return runtime.cancelTimer(this.source, this.id);
|
|
1813
|
+
}
|
|
1814
|
+
function updateLogicalTimers(snapshot, effect, actorScope) {
|
|
1815
|
+
if (!isBuiltInExecutableAction(effect)) return snapshot;
|
|
1816
|
+
if (effect.type === "@xstate.cancel") {
|
|
1817
|
+
if (!snapshot.timers?.[effect.id]) return snapshot;
|
|
1818
|
+
const timers = { ...snapshot.timers };
|
|
1819
|
+
delete timers[effect.id];
|
|
1820
|
+
return {
|
|
1821
|
+
...snapshot,
|
|
1822
|
+
timers
|
|
1823
|
+
};
|
|
1824
|
+
}
|
|
1825
|
+
if (effect.type !== "@xstate.raise" && effect.type !== "@xstate.sendTo" || effect.delay === void 0) return snapshot;
|
|
1826
|
+
let nextTimerId = snapshot._nextTimerId ?? 0;
|
|
1827
|
+
const id = effect.id ?? `xstate.timer.auto.${nextTimerId++}`;
|
|
1828
|
+
effect.id = id;
|
|
1829
|
+
const target = effect.type === "@xstate.raise" || effect.target === actorScope.self ? "self" : effect.target;
|
|
1830
|
+
return {
|
|
1831
|
+
...snapshot,
|
|
1832
|
+
timers: {
|
|
1833
|
+
...snapshot.timers,
|
|
1834
|
+
[id]: {
|
|
1835
|
+
id,
|
|
1836
|
+
delay: effect.delay,
|
|
1837
|
+
type: effect.type,
|
|
1838
|
+
event: effect.event,
|
|
1839
|
+
target
|
|
1840
|
+
}
|
|
1841
|
+
},
|
|
1842
|
+
_nextTimerId: nextTimerId
|
|
1843
|
+
};
|
|
1844
|
+
}
|
|
1845
|
+
function mergeContextPatch(context, patch) {
|
|
1846
|
+
return {
|
|
1847
|
+
...context,
|
|
1848
|
+
...patch
|
|
1849
|
+
};
|
|
1850
|
+
}
|
|
1851
|
+
function pushBuiltInAction(actions, action, ...args) {
|
|
1852
|
+
const actionRecord = {
|
|
1853
|
+
action,
|
|
1854
|
+
args
|
|
1855
|
+
};
|
|
1856
|
+
actions.push(actionRecord);
|
|
1857
|
+
return actionRecord;
|
|
1858
|
+
}
|
|
1859
|
+
/** @internal Max-merges generated-id counter records, copy-on-write. */
|
|
1860
|
+
function mergeActorIdCounters(current, update) {
|
|
1861
|
+
const merged = { ...current };
|
|
1862
|
+
for (const key of Object.keys(update)) {
|
|
1863
|
+
const next = update[key];
|
|
1864
|
+
if (next === void 0) continue;
|
|
1865
|
+
merged[key] = Math.max(merged[key] ?? 0, next);
|
|
1866
|
+
}
|
|
1867
|
+
return merged;
|
|
1868
|
+
}
|
|
1869
|
+
function applyChildUpdate(snapshot, update, actorScope) {
|
|
1870
|
+
if (update.type === "add") return {
|
|
1871
|
+
...snapshot,
|
|
1872
|
+
children: {
|
|
1873
|
+
...snapshot.children,
|
|
1874
|
+
[update.id]: update.actor
|
|
1875
|
+
},
|
|
1876
|
+
...update.counters && { _nextActorIds: mergeActorIdCounters(snapshot._nextActorIds, update.counters) }
|
|
1877
|
+
};
|
|
1878
|
+
const children = { ...snapshot.children };
|
|
1879
|
+
let owned = update.actor._parent === actorScope.self;
|
|
1880
|
+
for (const key of Object.keys(children)) if (children[key] === update.actor) {
|
|
1881
|
+
owned = true;
|
|
1882
|
+
delete children[key];
|
|
1883
|
+
}
|
|
1884
|
+
if (!owned) throw new Error(`Cannot stop non-child actor ${update.actor.id}`);
|
|
1885
|
+
actorScope.system._unregister(update.actor);
|
|
1886
|
+
return {
|
|
1887
|
+
...snapshot,
|
|
1888
|
+
children
|
|
1889
|
+
};
|
|
1890
|
+
}
|
|
1891
|
+
function getTransitionActionRecord(action) {
|
|
1892
|
+
if (typeof action === "object" && action !== null && "action" in action && typeof action.action === "function") return action;
|
|
1893
|
+
}
|
|
1894
|
+
function pushSpawnedChild(actions, actor, id, counters) {
|
|
1895
|
+
const action = pushBuiltInAction(actions, builtInActions["@xstate.spawn"], actor);
|
|
1896
|
+
action.childUpdate = {
|
|
1897
|
+
type: "add",
|
|
1898
|
+
actor,
|
|
1899
|
+
id,
|
|
1900
|
+
counters
|
|
1901
|
+
};
|
|
1902
|
+
}
|
|
1903
|
+
const spawnAllocations = /* @__PURE__ */ new WeakMap();
|
|
1904
|
+
const createSpawnAllocation = () => ({
|
|
1905
|
+
counters: /* @__PURE__ */ new Map(),
|
|
1906
|
+
explicitIds: /* @__PURE__ */ new Set(),
|
|
1907
|
+
stoppedIds: /* @__PURE__ */ new Set()
|
|
1908
|
+
});
|
|
1909
|
+
/**
|
|
1910
|
+
* Starts a fresh spawn-allocation transaction for one logical transition.
|
|
1911
|
+
* Called at every transition boundary so pure replays from the same snapshot
|
|
1912
|
+
* allocate identical ids.
|
|
1913
|
+
*
|
|
1914
|
+
* @internal
|
|
1915
|
+
*/
|
|
1916
|
+
function beginSpawnAllocation(actorScope) {
|
|
1917
|
+
spawnAllocations.set(actorScope, createSpawnAllocation());
|
|
1918
|
+
}
|
|
1919
|
+
function getWorkingSnapshotOf(actorScope) {
|
|
1920
|
+
return actorScope.self._snapshot;
|
|
1921
|
+
}
|
|
1922
|
+
function getRegisteredActors(actorScope) {
|
|
1923
|
+
return actorScope.self.logic?.sources?.actors;
|
|
1924
|
+
}
|
|
1925
|
+
function resolveTransitionSpawnSource(actorScope, source) {
|
|
1926
|
+
const registeredActors = getRegisteredActors(actorScope);
|
|
1927
|
+
if (typeof source === "string") {
|
|
1928
|
+
const logic = registeredActors?.[source];
|
|
1929
|
+
if (!logic) throw new Error(`Actor source '${source}' is not provided`);
|
|
1930
|
+
return {
|
|
1931
|
+
logic,
|
|
1932
|
+
src: source
|
|
1933
|
+
};
|
|
1934
|
+
}
|
|
1935
|
+
if (!registeredActors) return {
|
|
1936
|
+
logic: source,
|
|
1937
|
+
src: void 0
|
|
1938
|
+
};
|
|
1939
|
+
return {
|
|
1940
|
+
logic: source,
|
|
1941
|
+
src: resolveRegisteredActorSource(registeredActors, source)
|
|
1942
|
+
};
|
|
1943
|
+
}
|
|
1944
|
+
/**
|
|
1945
|
+
* The next free index for a prefix. The parent snapshot's persisted counter
|
|
1946
|
+
* is always a floor, so a freed id is never handed out again — not even when
|
|
1947
|
+
* an explicit id reserved a lower one earlier in the transition.
|
|
1948
|
+
*/
|
|
1949
|
+
function nextChildIndex(actorScope, allocation, prefix) {
|
|
1950
|
+
return Math.max(allocation.counters.get(prefix) ?? 0, getWorkingSnapshotOf(actorScope)?._nextActorIds?.[prefix] ?? 0);
|
|
1951
|
+
}
|
|
1952
|
+
/**
|
|
1953
|
+
* Allocates the next generated child id for the transition's allocation
|
|
1954
|
+
* transaction, seeded from the parent snapshot's own persisted counters. This
|
|
1955
|
+
* is the single allocator for snapshot-owned children (`enq.spawn` and
|
|
1956
|
+
* context-factory spawns); a correct allocation cannot collide.
|
|
1957
|
+
*
|
|
1958
|
+
* @internal
|
|
1959
|
+
*/
|
|
1960
|
+
function allocateChildId(actorScope, src, localAllocation) {
|
|
1961
|
+
const allocation = spawnAllocations.get(actorScope) ?? localAllocation ?? createSpawnAllocation();
|
|
1962
|
+
const prefix = getActorIdPrefix(src);
|
|
1963
|
+
const next = nextChildIndex(actorScope, allocation, prefix);
|
|
1964
|
+
allocation.counters.set(prefix, next + 1);
|
|
1965
|
+
return {
|
|
1966
|
+
id: `${prefix}:${next}`,
|
|
1967
|
+
counters: { [prefix]: next + 1 }
|
|
1968
|
+
};
|
|
1969
|
+
}
|
|
1970
|
+
/**
|
|
1971
|
+
* Requires an explicit child id to be unoccupied before a spawn or invoke
|
|
1972
|
+
* claims it: an address must name at most one live actor. An id is occupied
|
|
1973
|
+
* when a child of this parent already holds it and this transition has not
|
|
1974
|
+
* stopped that child, or when an earlier spawn of this transition claimed it.
|
|
1975
|
+
*
|
|
1976
|
+
* @internal
|
|
1977
|
+
*/
|
|
1978
|
+
function assertChildIdFree(actorScope, id, localAllocation) {
|
|
1979
|
+
const allocation = spawnAllocations.get(actorScope) ?? localAllocation ?? createSpawnAllocation();
|
|
1980
|
+
const existing = getWorkingSnapshotOf(actorScope)?.children?.[id];
|
|
1981
|
+
const occupied = existing !== void 0 && !allocation.stoppedIds.has(id) && existing.getSnapshot().status === "active";
|
|
1982
|
+
if (allocation.explicitIds.has(id) || occupied) throw new Error(`Child actor id '${id}' is already in use`);
|
|
1983
|
+
allocation.explicitIds.add(id);
|
|
1984
|
+
}
|
|
1985
|
+
/**
|
|
1986
|
+
* Records that this transition stopped a child, freeing its id for a later
|
|
1987
|
+
* spawn or invoke of the same transition (the invoke restart pattern).
|
|
1988
|
+
*
|
|
1989
|
+
* @internal
|
|
1990
|
+
*/
|
|
1991
|
+
function recordStoppedChild(actorScope, actor) {
|
|
1992
|
+
const allocation = spawnAllocations.get(actorScope);
|
|
1993
|
+
if (allocation) {
|
|
1994
|
+
allocation.stoppedIds.add(actor.id);
|
|
1995
|
+
allocation.explicitIds.delete(actor.id);
|
|
1996
|
+
}
|
|
1997
|
+
}
|
|
1998
|
+
/**
|
|
1999
|
+
* Reserves an explicit generated-shaped id (`worker:5`) in the transaction
|
|
2000
|
+
* and the parent snapshot, so live runs and pure replays allocate the same
|
|
2001
|
+
* later ids even after this child is removed.
|
|
2002
|
+
*
|
|
2003
|
+
* @internal
|
|
2004
|
+
*/
|
|
2005
|
+
function reserveChildId(actorScope, id, localAllocation) {
|
|
2006
|
+
const generated = parseGeneratedActorId(id);
|
|
2007
|
+
if (!generated) return;
|
|
2008
|
+
generated.prefix;
|
|
2009
|
+
const allocation = spawnAllocations.get(actorScope) ?? localAllocation ?? createSpawnAllocation();
|
|
2010
|
+
const next = Math.max(nextChildIndex(actorScope, allocation, generated.prefix), generated.index + 1);
|
|
2011
|
+
allocation.counters.set(generated.prefix, next);
|
|
2012
|
+
return { [generated.prefix]: next };
|
|
2013
|
+
}
|
|
2014
|
+
/**
|
|
2015
|
+
* The counters allocated so far in the transition's transaction, for
|
|
2016
|
+
* committing into a snapshot built outside `applyChildUpdate` (the
|
|
2017
|
+
* pre-initial snapshot with context-factory spawns).
|
|
2018
|
+
*
|
|
2019
|
+
* @internal
|
|
2020
|
+
*/
|
|
2021
|
+
function takeSpawnAllocationCounters(actorScope) {
|
|
2022
|
+
const counters = spawnAllocations.get(actorScope)?.counters;
|
|
2023
|
+
if (!counters?.size) return;
|
|
2024
|
+
return Object.fromEntries(counters);
|
|
2025
|
+
}
|
|
2026
|
+
function createTransitionEnqueue(actorScope, actions, internalEvents, actorSubscriptions = false, createActors = true) {
|
|
2027
|
+
const localAllocation = spawnAllocations.get(actorScope) ?? createSpawnAllocation();
|
|
2028
|
+
const props = {
|
|
2029
|
+
cancel: (id) => {
|
|
2030
|
+
pushBuiltInAction(actions, builtInActions["@xstate.cancel"], actorScope, id);
|
|
2031
|
+
},
|
|
2032
|
+
emit: (emittedEvent) => {
|
|
2033
|
+
actions.push(emittedEvent);
|
|
2034
|
+
},
|
|
2035
|
+
log: (...args) => {
|
|
2036
|
+
pushBuiltInAction(actions, actorScope.logger, ...args);
|
|
2037
|
+
},
|
|
2038
|
+
raise: (raisedEvent, options) => {
|
|
2039
|
+
if (typeof raisedEvent === "string") throw new Error(`Only event objects may be used with raise`);
|
|
2040
|
+
if (options?.delay !== void 0) pushBuiltInAction(actions, builtInActions["@xstate.raise"], actorScope, raisedEvent, options);
|
|
2041
|
+
else internalEvents.push(raisedEvent);
|
|
2042
|
+
},
|
|
2043
|
+
spawn: (source, options) => {
|
|
2044
|
+
const { logic, src } = resolveTransitionSpawnSource(actorScope, source);
|
|
2045
|
+
if (!createActors) return { id: options?.id ?? options?.registryKey ?? src ?? logic.id };
|
|
2046
|
+
let id = options?.id;
|
|
2047
|
+
let counters;
|
|
2048
|
+
if (id === void 0) ({id, counters} = allocateChildId(actorScope, src ?? logic, localAllocation));
|
|
2049
|
+
else {
|
|
2050
|
+
assertChildIdFree(actorScope, id, localAllocation);
|
|
2051
|
+
counters = reserveChildId(actorScope, id, localAllocation);
|
|
2052
|
+
}
|
|
2053
|
+
const actor = actorScope.system.createActorRef(logic, {
|
|
2054
|
+
...options,
|
|
2055
|
+
...src !== void 0 && { src },
|
|
2056
|
+
id,
|
|
2057
|
+
parent: actorScope.self
|
|
2058
|
+
});
|
|
2059
|
+
pushSpawnedChild(actions, actor, id, counters);
|
|
2060
|
+
return actor;
|
|
2061
|
+
},
|
|
2062
|
+
sendTo: (actor, event, options) => {
|
|
2063
|
+
pushBuiltInAction(actions, builtInActions["@xstate.sendTo"], actorScope, actor, event, options);
|
|
2064
|
+
},
|
|
2065
|
+
stop: (actor) => {
|
|
2066
|
+
if (actor) {
|
|
2067
|
+
const actorInstance = actor;
|
|
2068
|
+
const action = pushBuiltInAction(actions, builtInActions["@xstate.stop"], actorScope, actorInstance);
|
|
2069
|
+
action.childUpdate = {
|
|
2070
|
+
type: "remove",
|
|
2071
|
+
actor: actorInstance
|
|
2072
|
+
};
|
|
2073
|
+
recordStoppedChild(actorScope, actorInstance);
|
|
2074
|
+
}
|
|
2075
|
+
}
|
|
2076
|
+
};
|
|
2077
|
+
if (actorSubscriptions) Object.assign(props, {
|
|
2078
|
+
listen: (actor, eventType, mapper) => {
|
|
2079
|
+
if (!createActors) return { id: void 0 };
|
|
2080
|
+
const input = {
|
|
2081
|
+
actor,
|
|
2082
|
+
eventType,
|
|
2083
|
+
mapper
|
|
2084
|
+
};
|
|
2085
|
+
const listenerActor = actorScope.system.createActorRef(listenerLogic, {
|
|
2086
|
+
input,
|
|
2087
|
+
parent: actorScope.self
|
|
2088
|
+
});
|
|
2089
|
+
pushBuiltInAction(actions, builtInActions["@xstate.spawn"], listenerActor);
|
|
2090
|
+
return listenerActor;
|
|
2091
|
+
},
|
|
2092
|
+
subscribeTo: (actor, mappers) => {
|
|
2093
|
+
if (!createActors) return { id: void 0 };
|
|
2094
|
+
const input = {
|
|
2095
|
+
actor,
|
|
2096
|
+
mappers: typeof mappers === "function" ? { snapshot: mappers } : mappers
|
|
2097
|
+
};
|
|
2098
|
+
const subscriptionActor = actorScope.system.createActorRef(subscriptionLogic, {
|
|
2099
|
+
input,
|
|
2100
|
+
parent: actorScope.self
|
|
2101
|
+
});
|
|
2102
|
+
pushBuiltInAction(actions, builtInActions["@xstate.spawn"], subscriptionActor);
|
|
2103
|
+
return subscriptionActor;
|
|
2104
|
+
}
|
|
2105
|
+
});
|
|
2106
|
+
let closed = false;
|
|
2107
|
+
const guard = (fn) => (...args) => closed ? void 0 : fn(...args);
|
|
2108
|
+
for (const key of Object.keys(props)) props[key] = guard(props[key]);
|
|
2109
|
+
const enqueue = createEnqueueObject(props, guard((action, ...args) => {
|
|
2110
|
+
pushBuiltInAction(actions, action, ...args);
|
|
2111
|
+
}));
|
|
2112
|
+
enqueueClosers.set(enqueue, () => {
|
|
2113
|
+
closed = true;
|
|
2114
|
+
});
|
|
2115
|
+
return enqueue;
|
|
2116
|
+
}
|
|
2117
|
+
const enqueueClosers = /* @__PURE__ */ new WeakMap();
|
|
2118
|
+
/**
|
|
2119
|
+
* @internal Invalidates an enqueue handle after the function it was passed
|
|
2120
|
+
* to returned. Later `enq.*` calls throw in development and no-op in
|
|
2121
|
+
* production.
|
|
2122
|
+
*/
|
|
2123
|
+
function closeTransitionEnqueue(enqueue) {
|
|
2124
|
+
enqueueClosers.get(enqueue)?.();
|
|
2125
|
+
}
|
|
2126
|
+
function getBuiltInActionFields(action, args) {
|
|
2127
|
+
switch (action) {
|
|
2128
|
+
case builtInActions["@xstate.spawn"]: {
|
|
2129
|
+
const [actor] = args;
|
|
2130
|
+
return {
|
|
2131
|
+
type: "@xstate.spawn",
|
|
2132
|
+
kind: "builtin",
|
|
2133
|
+
exec: execSpawnEffect,
|
|
2134
|
+
source: actor._parent,
|
|
2135
|
+
actor,
|
|
2136
|
+
id: actor.id,
|
|
2137
|
+
logic: actor.logic,
|
|
2138
|
+
src: actor.src,
|
|
2139
|
+
input: actor.options?.input
|
|
2140
|
+
};
|
|
2141
|
+
}
|
|
2142
|
+
case builtInActions["@xstate.raise"]: {
|
|
2143
|
+
const [, event, options] = args;
|
|
2144
|
+
return {
|
|
2145
|
+
type: "@xstate.raise",
|
|
2146
|
+
kind: "builtin",
|
|
2147
|
+
exec: execRaiseEffect,
|
|
2148
|
+
source: args[0].self,
|
|
2149
|
+
event,
|
|
2150
|
+
id: options?.id,
|
|
2151
|
+
delay: options?.delay
|
|
2152
|
+
};
|
|
2153
|
+
}
|
|
2154
|
+
case builtInActions["@xstate.sendTo"]: {
|
|
2155
|
+
const [, target, event, options] = args;
|
|
2156
|
+
return {
|
|
2157
|
+
type: "@xstate.sendTo",
|
|
2158
|
+
kind: "builtin",
|
|
2159
|
+
exec: execSendToEffect,
|
|
2160
|
+
source: args[0].self,
|
|
2161
|
+
target,
|
|
2162
|
+
event,
|
|
2163
|
+
id: options?.id,
|
|
2164
|
+
delay: options?.delay
|
|
2165
|
+
};
|
|
2166
|
+
}
|
|
2167
|
+
case builtInActions["@xstate.cancel"]: {
|
|
2168
|
+
const [, id] = args;
|
|
2169
|
+
return {
|
|
2170
|
+
type: "@xstate.cancel",
|
|
2171
|
+
kind: "builtin",
|
|
2172
|
+
exec: execCancelEffect,
|
|
2173
|
+
source: args[0].self,
|
|
2174
|
+
id
|
|
2175
|
+
};
|
|
2176
|
+
}
|
|
2177
|
+
case builtInActions["@xstate.stop"]: {
|
|
2178
|
+
const [, actor] = args;
|
|
2179
|
+
return {
|
|
2180
|
+
type: "@xstate.stop",
|
|
2181
|
+
kind: "builtin",
|
|
2182
|
+
exec: execStopEffect,
|
|
2183
|
+
source: args[0].self,
|
|
2184
|
+
actor,
|
|
2185
|
+
id: actor.id
|
|
2186
|
+
};
|
|
2187
|
+
}
|
|
2188
|
+
default: return;
|
|
2189
|
+
}
|
|
2190
|
+
}
|
|
2191
|
+
/** @internal Creates an actor-start effect, including for restored children. */
|
|
2192
|
+
function createStartEffect(actor) {
|
|
2193
|
+
const args = [actor];
|
|
2194
|
+
return {
|
|
2195
|
+
kind: "builtin",
|
|
2196
|
+
exec: execStartEffect,
|
|
2197
|
+
type: XSTATE_START,
|
|
2198
|
+
source: actor._parent,
|
|
2199
|
+
params: void 0,
|
|
2200
|
+
args,
|
|
2201
|
+
actor,
|
|
2202
|
+
id: actor.id
|
|
2203
|
+
};
|
|
2204
|
+
}
|
|
2205
|
+
/** @internal Recreates a pending logical timer without re-entering its state. */
|
|
2206
|
+
function createTimerEffect(actorScope, timer, delay) {
|
|
2207
|
+
const source = actorScope.self;
|
|
2208
|
+
const options = {
|
|
2209
|
+
id: timer.id,
|
|
2210
|
+
delay
|
|
2211
|
+
};
|
|
2212
|
+
if (timer.type === "@xstate.raise") return {
|
|
2213
|
+
kind: "builtin",
|
|
2214
|
+
type: timer.type,
|
|
2215
|
+
source,
|
|
2216
|
+
event: timer.event,
|
|
2217
|
+
id: timer.id,
|
|
2218
|
+
delay,
|
|
2219
|
+
params: void 0,
|
|
2220
|
+
args: [
|
|
2221
|
+
actorScope,
|
|
2222
|
+
timer.event,
|
|
2223
|
+
options
|
|
2224
|
+
],
|
|
2225
|
+
exec: execRaiseEffect
|
|
2226
|
+
};
|
|
2227
|
+
const target = timer.target === "self" ? source : timer.target;
|
|
2228
|
+
return {
|
|
2229
|
+
kind: "builtin",
|
|
2230
|
+
type: timer.type,
|
|
2231
|
+
source,
|
|
2232
|
+
target,
|
|
2233
|
+
event: timer.event,
|
|
2234
|
+
id: timer.id,
|
|
2235
|
+
delay,
|
|
2236
|
+
params: void 0,
|
|
2237
|
+
args: [
|
|
2238
|
+
actorScope,
|
|
2239
|
+
target,
|
|
2240
|
+
timer.event,
|
|
2241
|
+
options
|
|
2242
|
+
],
|
|
2243
|
+
exec: execSendToEffect
|
|
2244
|
+
};
|
|
2245
|
+
}
|
|
2246
|
+
function createSpawnEffect(actor) {
|
|
2247
|
+
const args = [actor];
|
|
2248
|
+
return {
|
|
2249
|
+
kind: "builtin",
|
|
2250
|
+
exec: execSpawnEffect,
|
|
2251
|
+
type: XSTATE_SPAWN,
|
|
2252
|
+
source: actor._parent,
|
|
2253
|
+
params: void 0,
|
|
2254
|
+
args,
|
|
2255
|
+
actor,
|
|
2256
|
+
id: actor.id,
|
|
2257
|
+
logic: actor.logic,
|
|
2258
|
+
src: actor.src,
|
|
2259
|
+
input: actor.options?.input
|
|
2260
|
+
};
|
|
2261
|
+
}
|
|
2262
|
+
/** @internal Creates an immediate actor-to-actor delivery effect. */
|
|
2263
|
+
function createSendToEffect(actorScope, target, event) {
|
|
2264
|
+
const args = [
|
|
2265
|
+
actorScope,
|
|
2266
|
+
target,
|
|
2267
|
+
event,
|
|
2268
|
+
{}
|
|
2269
|
+
];
|
|
2270
|
+
return {
|
|
2271
|
+
kind: "builtin",
|
|
2272
|
+
exec: execSendToEffect,
|
|
2273
|
+
type: "@xstate.sendTo",
|
|
2274
|
+
source: actorScope.self,
|
|
2275
|
+
target,
|
|
2276
|
+
event,
|
|
2277
|
+
id: void 0,
|
|
2278
|
+
delay: void 0,
|
|
2279
|
+
params: void 0,
|
|
2280
|
+
args
|
|
2281
|
+
};
|
|
2282
|
+
}
|
|
2283
|
+
/** @internal Creates the terminal lifecycle effect for an actor. */
|
|
2284
|
+
function createTerminationEffect(actorScope, snapshot) {
|
|
2285
|
+
if (snapshot.status !== "done" && snapshot.status !== "error") throw new Error("Cannot terminate an active or stopped actor");
|
|
2286
|
+
const termination = snapshot.status === "done" ? {
|
|
2287
|
+
status: "done",
|
|
2288
|
+
output: snapshot.output,
|
|
2289
|
+
error: void 0
|
|
2290
|
+
} : {
|
|
2291
|
+
status: "error",
|
|
2292
|
+
output: void 0,
|
|
2293
|
+
error: snapshot.error
|
|
2294
|
+
};
|
|
2295
|
+
const args = [actorScope.self, termination];
|
|
2296
|
+
return {
|
|
2297
|
+
kind: "builtin",
|
|
2298
|
+
exec: execTerminateEffect,
|
|
2299
|
+
type: XSTATE_TERMINATE,
|
|
2300
|
+
source: actorScope.self,
|
|
2301
|
+
actor: actorScope.self,
|
|
2302
|
+
id: actorScope.self.id,
|
|
2303
|
+
...termination,
|
|
2304
|
+
params: void 0,
|
|
2305
|
+
args
|
|
2306
|
+
};
|
|
2307
|
+
}
|
|
2308
|
+
/**
|
|
2309
|
+
* Ensures that a newly terminal snapshot has exactly one ordered actor
|
|
2310
|
+
* termination effect.
|
|
2311
|
+
*
|
|
2312
|
+
* @internal
|
|
2313
|
+
*/
|
|
2314
|
+
function finalizeTransitionResult(actorScope, previousSnapshot, [nextSnapshot, effects]) {
|
|
2315
|
+
const becameTerminal = nextSnapshot.status === "done" || nextSnapshot.status === "error";
|
|
2316
|
+
const wasTerminal = previousSnapshot?.status === "done" || previousSnapshot?.status === "error";
|
|
2317
|
+
const hasTerminationEffect = effects.some((effect) => typeof effect === "object" && effect !== null && "type" in effect && effect.type === "@xstate.terminate");
|
|
2318
|
+
return becameTerminal && !wasTerminal && !hasTerminationEffect ? [nextSnapshot, [...effects, createTerminationEffect(actorScope, nextSnapshot)]] : [nextSnapshot, effects];
|
|
2319
|
+
}
|
|
2320
|
+
/**
|
|
2321
|
+
* Attached (listener/subscription) starts are ordered before child starts so
|
|
2322
|
+
* that a listener/subscription captures events emitted synchronously as its
|
|
2323
|
+
* target actor starts.
|
|
2324
|
+
*/
|
|
2325
|
+
function deriveDeferredStarts(effects) {
|
|
2326
|
+
const attachedStarts = [];
|
|
2327
|
+
const childStarts = [];
|
|
2328
|
+
for (const effect of effects) {
|
|
2329
|
+
if (!isBuiltInExecutableAction(effect) || effect.type !== "@xstate.spawn") continue;
|
|
2330
|
+
const { actor, logic } = effect;
|
|
2331
|
+
const start = createStartEffect(actor);
|
|
2332
|
+
if (logic === listenerLogic || logic === subscriptionLogic) attachedStarts.push(start);
|
|
2333
|
+
else childStarts.push(start);
|
|
2334
|
+
}
|
|
2335
|
+
return [...attachedStarts, ...childStarts];
|
|
2336
|
+
}
|
|
2337
|
+
/** @public */
|
|
2338
|
+
function isBuiltInExecutableAction(action) {
|
|
2339
|
+
return action.kind === "builtin";
|
|
2340
|
+
}
|
|
2341
|
+
/**
|
|
2342
|
+
* Executes transition effects sequentially, awaiting each runtime operation.
|
|
2343
|
+
*
|
|
2344
|
+
* @public
|
|
2345
|
+
*/
|
|
2346
|
+
async function executeEffects(effects, runtime) {
|
|
2347
|
+
for (const effect of effects) await effect.exec(runtime);
|
|
2348
|
+
}
|
|
2349
|
+
function resolveActionsWithContext(currentSnapshot, event, actorScope, actions) {
|
|
2350
|
+
let intermediateSnapshot = currentSnapshot;
|
|
2351
|
+
const executableActions = [];
|
|
2352
|
+
for (const action of actions) {
|
|
2353
|
+
const actionArgs = isLazyActorScope(actorScope) ? withActorScope({
|
|
2354
|
+
context: intermediateSnapshot.context,
|
|
2355
|
+
event,
|
|
2356
|
+
output: getEventOutput(event),
|
|
2357
|
+
children: intermediateSnapshot.children,
|
|
2358
|
+
actions: currentSnapshot.machine.sources.actions,
|
|
2359
|
+
actors: currentSnapshot.machine.sources.actors
|
|
2360
|
+
}, actorScope) : {
|
|
2361
|
+
context: intermediateSnapshot.context,
|
|
2362
|
+
event,
|
|
2363
|
+
output: getEventOutput(event),
|
|
2364
|
+
self: actorScope.self,
|
|
2365
|
+
system: actorScope.system,
|
|
2366
|
+
parent: actorScope.self._parent,
|
|
2367
|
+
children: intermediateSnapshot.children,
|
|
2368
|
+
actions: currentSnapshot.machine.sources.actions,
|
|
2369
|
+
actors: currentSnapshot.machine.sources.actors
|
|
2370
|
+
};
|
|
2371
|
+
const isInline = typeof action === "function";
|
|
2372
|
+
const actionRecord = getTransitionActionRecord(action);
|
|
2373
|
+
let resolvedActionArgs = actionRecord?.args;
|
|
2374
|
+
if (actionRecord?.action === builtInActions["@xstate.sendTo"] && (actionRecord.args[1] === void 0 || typeof actionRecord.args[1] === "string")) {
|
|
2375
|
+
const childId = actionRecord.args[1];
|
|
2376
|
+
const target = childId !== void 0 && Object.hasOwn(intermediateSnapshot.children, childId) ? intermediateSnapshot.children[childId] : void 0;
|
|
2377
|
+
if (!target) {
|
|
2378
|
+
executableActions.push(createMissingTargetEffect(actorScope, actionRecord.args[2], childId));
|
|
2379
|
+
continue;
|
|
2380
|
+
}
|
|
2381
|
+
resolvedActionArgs = [
|
|
2382
|
+
actionRecord.args[0],
|
|
2383
|
+
target,
|
|
2384
|
+
...actionRecord.args.slice(2)
|
|
2385
|
+
];
|
|
2386
|
+
}
|
|
2387
|
+
const resolvedAction = isInline ? action : actionRecord ? actionRecord.action.bind(null, ...resolvedActionArgs) : false;
|
|
2388
|
+
let actionParams = void 0;
|
|
2389
|
+
if (typeof action === "object" && action !== null) {
|
|
2390
|
+
const { type: _, childUpdate: _childUpdate, ...emittedEventParams } = action;
|
|
2391
|
+
actionParams = emittedEventParams;
|
|
2392
|
+
}
|
|
2393
|
+
if (actionRecord?.childUpdate) intermediateSnapshot = applyChildUpdate(intermediateSnapshot, actionRecord.childUpdate, actorScope);
|
|
2394
|
+
if (resolvedAction && "_special" in resolvedAction) {
|
|
2395
|
+
executableActions.push({
|
|
2396
|
+
kind: "action",
|
|
2397
|
+
exec: execCustomEffect,
|
|
2398
|
+
type: typeof action === "object" ? "action" in action && typeof action.action === "function" ? action.action.name ?? "(anonymous)" : action.type ?? "(anonymous)" : action.name || "(anonymous)",
|
|
2399
|
+
params: actionParams,
|
|
2400
|
+
args: [],
|
|
2401
|
+
action: void 0
|
|
2402
|
+
});
|
|
2403
|
+
const res = resolvedAction(actionArgs, emptyEnqueueObject);
|
|
2404
|
+
if (res && ("context" in res || "children" in res)) intermediateSnapshot = {
|
|
2405
|
+
...intermediateSnapshot,
|
|
2406
|
+
...res.context !== void 0 ? { context: mergeContextPatch(intermediateSnapshot.context, res.context) } : {},
|
|
2407
|
+
..."children" in res ? { children: res.children } : {}
|
|
2408
|
+
};
|
|
2409
|
+
continue;
|
|
2410
|
+
}
|
|
2411
|
+
if (!resolvedAction || !("resolve" in resolvedAction)) {
|
|
2412
|
+
const builtInFields = typeof action === "object" && action !== null && "action" in action && typeof action.action === "function" ? getBuiltInActionFields(action.action, resolvedActionArgs) : void 0;
|
|
2413
|
+
const isEmittedEvent = typeof action === "object" && action !== null && !actionRecord;
|
|
2414
|
+
const typedExecutableAction = {
|
|
2415
|
+
kind: builtInFields ? "builtin" : isEmittedEvent ? "emit" : "action",
|
|
2416
|
+
type: typeof action === "object" ? "action" in action && typeof action.action === "function" ? action.action.name ?? "(anonymous)" : action.type : action.name || "(anonymous)",
|
|
2417
|
+
params: builtInFields ? void 0 : actionParams,
|
|
2418
|
+
args: typeof action === "object" && "action" in action ? resolvedActionArgs : [],
|
|
2419
|
+
...builtInFields ? {} : isEmittedEvent ? {
|
|
2420
|
+
source: actorScope.self,
|
|
2421
|
+
event: action
|
|
2422
|
+
} : { action: actionRecord?.action ?? (isInline ? action : void 0) },
|
|
2423
|
+
...!builtInFields ? { exec: isEmittedEvent ? execEmitEffect : execCustomEffect } : {},
|
|
2424
|
+
...builtInFields
|
|
2425
|
+
};
|
|
2426
|
+
intermediateSnapshot = updateLogicalTimers(intermediateSnapshot, typedExecutableAction, actorScope);
|
|
2427
|
+
executableActions.push(typedExecutableAction);
|
|
2428
|
+
continue;
|
|
2429
|
+
}
|
|
2430
|
+
}
|
|
2431
|
+
return [intermediateSnapshot, executableActions];
|
|
2432
|
+
}
|
|
2433
|
+
function createEnqueueObject(props, action) {
|
|
2434
|
+
const enqueueFn = (fn, ...args) => {
|
|
2435
|
+
action(fn, ...args);
|
|
2436
|
+
};
|
|
2437
|
+
Object.assign(enqueueFn, {
|
|
2438
|
+
cancel: noop,
|
|
2439
|
+
emit: noop,
|
|
2440
|
+
log: noop,
|
|
2441
|
+
raise: noop,
|
|
2442
|
+
spawn: emptyActor,
|
|
2443
|
+
sendTo: noop,
|
|
2444
|
+
stop: noop,
|
|
2445
|
+
listen: emptyActor,
|
|
2446
|
+
subscribeTo: emptyActor,
|
|
2447
|
+
...props
|
|
2448
|
+
});
|
|
2449
|
+
return enqueueFn;
|
|
2450
|
+
}
|
|
2451
|
+
const noop = () => {};
|
|
2452
|
+
const emptyActor = () => ({});
|
|
2453
|
+
const emptyEnqueueObject = createEnqueueObject({}, noop);
|
|
2454
|
+
// src/transitionGuards.ts
|
|
2455
|
+
const isStateId = (str) => str.startsWith("#");
|
|
2456
|
+
const isStateIdValue = (stateValue) => typeof stateValue === "string" && isStateId(stateValue);
|
|
2457
|
+
const stateInSnapshot = (snapshot, stateValue) => {
|
|
2458
|
+
if (isStateIdValue(stateValue)) {
|
|
2459
|
+
const target = snapshot.machine.getStateNodeById(stateValue);
|
|
2460
|
+
return snapshot.nodes.some((node) => node === target);
|
|
2461
|
+
}
|
|
2462
|
+
return snapshot.matches(stateValue);
|
|
2463
|
+
};
|
|
2464
|
+
function checkStateIn(...args) {
|
|
2465
|
+
if (args.length === 1) {
|
|
2466
|
+
const [stateValue] = args;
|
|
2467
|
+
return (snapshot) => stateInSnapshot(snapshot, stateValue);
|
|
2468
|
+
}
|
|
2469
|
+
return stateInSnapshot(args[0], args[1]);
|
|
2470
|
+
}
|
|
2471
|
+
const matchesEvent = (event, pattern) => Object.entries(pattern).every(([key, value]) => Object.is(Reflect.get(event, key), value));
|
|
2472
|
+
const selectTransition = (outcome) => outcome.type === "returned" ? selectionOfReturned(outcome.value) : { type: "effect" };
|
|
2473
|
+
const selectionOfReturned = (value) => value === void 0 ? { type: "absent" } : {
|
|
2474
|
+
type: "value",
|
|
2475
|
+
result: value
|
|
2476
|
+
};
|
|
2477
|
+
const admitsByDecisionType = {
|
|
2478
|
+
rejected: false,
|
|
2479
|
+
admitted: true,
|
|
2480
|
+
enabled: true,
|
|
2481
|
+
disabled: false
|
|
2482
|
+
};
|
|
2483
|
+
const isAdmitted = (decision) => admitsByDecisionType[decision.type];
|
|
2484
|
+
const rejectedDecision = { type: "rejected" };
|
|
2485
|
+
const admittedDecision = { type: "admitted" };
|
|
2486
|
+
const patternAccepts = (input) => input.matches === void 0 || matchesEvent(input.event, input.matches);
|
|
2487
|
+
const sessionAccepts = (input) => input.eventMatcher === void 0 || input.eventMatcher(input.event, input.snapshot);
|
|
2488
|
+
const guardAccepts = (input) => input.runGuard === void 0 || input.runGuard();
|
|
2489
|
+
const sessionAndGuardAccept = (input) => sessionAccepts(input) && guardAccepts(input);
|
|
2490
|
+
const checksAccept = (input) => patternAccepts(input) && sessionAndGuardAccept(input);
|
|
2491
|
+
const selectionDecision = (selection) => selection.type === "absent" ? {
|
|
2492
|
+
type: "disabled",
|
|
2493
|
+
selection
|
|
2494
|
+
} : {
|
|
2495
|
+
type: "enabled",
|
|
2496
|
+
selection
|
|
2497
|
+
};
|
|
2498
|
+
const transitionDecision = (input) => input.runTransitionFunction === void 0 ? admittedDecision : selectionDecision(selectTransition(input.runTransitionFunction()));
|
|
2499
|
+
const admitCandidate = (input) => checksAccept(input) ? transitionDecision(input) : rejectedDecision;
|
|
2500
|
+
const admittedOf = (admit, candidate) => {
|
|
2501
|
+
const decision = admit(candidate);
|
|
2502
|
+
return isAdmitted(decision) ? {
|
|
2503
|
+
candidate,
|
|
2504
|
+
decision
|
|
2505
|
+
} : void 0;
|
|
2506
|
+
};
|
|
2507
|
+
const firstAdmittedCandidate = (input) => input.candidates.reduce((admitted, candidate) => admitted ?? admittedOf(input.admit, candidate), void 0);
|
|
2508
|
+
// src/stateUtils.ts
|
|
2509
|
+
function getConfiguredDelayValue(delay, delaySource) {
|
|
2510
|
+
if (typeof delay !== "string") return delay;
|
|
2511
|
+
const referencedDelay = delaySource[delay];
|
|
2512
|
+
if (referencedDelay !== void 0) return referencedDelay;
|
|
2513
|
+
const parsedDelay = parseDurationToMilliseconds(delay);
|
|
2514
|
+
if (parsedDelay === void 0) throw new Error(`Invalid delay "${delay}": not a configured delay name or a valid duration string (e.g. "500ms", "1.5s", "PT1M30S").`);
|
|
2515
|
+
return parsedDelay;
|
|
2516
|
+
}
|
|
2517
|
+
function resolveDelay(delay, delaySource, args) {
|
|
2518
|
+
if (typeof delay === "function") return delay(args);
|
|
2519
|
+
const configuredDelay = getConfiguredDelayValue(delay, delaySource);
|
|
2520
|
+
if (typeof configuredDelay === "function") return configuredDelay(args);
|
|
2521
|
+
return configuredDelay;
|
|
2522
|
+
}
|
|
2523
|
+
function getStateInput(snapshot, stateNodeId) {
|
|
2524
|
+
return snapshot._stateInputs?.[stateNodeId];
|
|
2525
|
+
}
|
|
2526
|
+
function getChildren(stateNode) {
|
|
2527
|
+
return Object.values(stateNode.states).filter((sn) => sn.type !== "history");
|
|
2528
|
+
}
|
|
2529
|
+
function getProperAncestors(stateNode, toStateNode) {
|
|
2530
|
+
const ancestors = [];
|
|
2531
|
+
if (toStateNode === stateNode) return ancestors;
|
|
2532
|
+
let m = stateNode.parent;
|
|
2533
|
+
while (m && m !== toStateNode) {
|
|
2534
|
+
ancestors.push(m);
|
|
2535
|
+
m = m.parent;
|
|
2536
|
+
}
|
|
2537
|
+
return ancestors;
|
|
2538
|
+
}
|
|
2539
|
+
function getAllStateNodes(stateNodes) {
|
|
2540
|
+
const nodeSet = new Set(stateNodes);
|
|
2541
|
+
const activeParentSet = /* @__PURE__ */ new Set();
|
|
2542
|
+
for (const stateNode of nodeSet) if (stateNode.parent) activeParentSet.add(stateNode.parent);
|
|
2543
|
+
for (const s of nodeSet) {
|
|
2544
|
+
if (s.type === "compound" && !activeParentSet.has(s)) {
|
|
2545
|
+
for (const sn of getInitialStateNodes(s)) nodeSet.add(sn);
|
|
2546
|
+
continue;
|
|
2547
|
+
}
|
|
2548
|
+
if (s.type === "parallel") {
|
|
2549
|
+
for (const child of getChildren(s)) if (!nodeSet.has(child)) {
|
|
2550
|
+
const initialStates = getInitialStateNodes(child);
|
|
2551
|
+
for (const initialStateNode of initialStates) nodeSet.add(initialStateNode);
|
|
2552
|
+
}
|
|
2553
|
+
}
|
|
2554
|
+
}
|
|
2555
|
+
for (const s of nodeSet) {
|
|
2556
|
+
let m = s.parent;
|
|
2557
|
+
while (m && !nodeSet.has(m)) {
|
|
2558
|
+
nodeSet.add(m);
|
|
2559
|
+
m = m.parent;
|
|
2560
|
+
}
|
|
2561
|
+
}
|
|
2562
|
+
return nodeSet;
|
|
2563
|
+
}
|
|
2564
|
+
function getStateValue(rootNode, stateNodes) {
|
|
2565
|
+
const config = getAllStateNodes(stateNodes);
|
|
2566
|
+
const adjList = /* @__PURE__ */ new Map();
|
|
2567
|
+
for (const s of config) {
|
|
2568
|
+
if (!adjList.has(s)) adjList.set(s, []);
|
|
2569
|
+
if (!s.parent) continue;
|
|
2570
|
+
if (!adjList.has(s.parent)) adjList.set(s.parent, []);
|
|
2571
|
+
adjList.get(s.parent).push(s);
|
|
2572
|
+
}
|
|
2573
|
+
const getValueFromAdj = (baseNode) => {
|
|
2574
|
+
const childStateNodes = adjList.get(baseNode);
|
|
2575
|
+
if (!childStateNodes) return {};
|
|
2576
|
+
if (baseNode.type === "compound") {
|
|
2577
|
+
const childStateNode = childStateNodes[0];
|
|
2578
|
+
if (!childStateNode) return {};
|
|
2579
|
+
if (isAtomicStateNode(childStateNode)) return childStateNode.key;
|
|
2580
|
+
}
|
|
2581
|
+
const stateValue = {};
|
|
2582
|
+
for (const childStateNode of childStateNodes) stateValue[childStateNode.key] = getValueFromAdj(childStateNode);
|
|
2583
|
+
return stateValue;
|
|
2584
|
+
};
|
|
2585
|
+
return getValueFromAdj(rootNode);
|
|
2586
|
+
}
|
|
2587
|
+
function isInFinalState(stateNodeSet, stateNode) {
|
|
2588
|
+
if (stateNode.type === "compound") return getChildren(stateNode).some((s) => s.type === "final" && stateNodeSet.has(s));
|
|
2589
|
+
if (stateNode.type === "parallel") return getChildren(stateNode).every((sn) => isInFinalState(stateNodeSet, sn));
|
|
2590
|
+
return stateNode.type === "final";
|
|
2591
|
+
}
|
|
2592
|
+
function getLegacyEventType(event) {
|
|
2593
|
+
switch (event.type) {
|
|
2594
|
+
case "xstate.done.actor":
|
|
2595
|
+
case "xstate.error.actor": return `${event.type}.${event.actorId}`;
|
|
2596
|
+
case "xstate.snapshot.actor": return `xstate.snapshot.${event.actorId}`;
|
|
2597
|
+
case "xstate.done.state": return `xstate.done.state.${event.stateId}`;
|
|
2598
|
+
case "xstate.after": return `xstate.after.${event.delay}.${event.stateId}`;
|
|
2599
|
+
case "xstate.timeout": return `xstate.timeout.${event.stateId}`;
|
|
2600
|
+
case "xstate.timeout.actor": return `xstate.timeout.actor.${event.actorId}`;
|
|
2601
|
+
default: return;
|
|
2602
|
+
}
|
|
2603
|
+
}
|
|
2604
|
+
function getEventTypeAliases(event) {
|
|
2605
|
+
const legacyType = getLegacyEventType(event);
|
|
2606
|
+
return legacyType ? [event.type, legacyType] : [event.type];
|
|
2607
|
+
}
|
|
2608
|
+
function getEventDescriptorKey(event) {
|
|
2609
|
+
const legacyType = getLegacyEventType(event);
|
|
2610
|
+
return legacyType ? `${event.type}|${legacyType}` : event.type;
|
|
2611
|
+
}
|
|
2612
|
+
function matchesActorSession(event, snapshot, actorId) {
|
|
2613
|
+
const child = snapshot.children[actorId];
|
|
2614
|
+
if (!child || !("sessionId" in event)) return true;
|
|
2615
|
+
return child.sessionId === void 0 || child.sessionId === event.sessionId;
|
|
2616
|
+
}
|
|
2617
|
+
function normalizeLegacyInternalEvent(event, machine) {
|
|
2618
|
+
if (event.type === "xstate.done.actor" || event.type === "xstate.error.actor" || event.type === "xstate.snapshot.actor" || event.type === "xstate.done.state" || event.type === "xstate.after" || event.type === "xstate.timeout" || event.type === "xstate.timeout.actor") return event;
|
|
2619
|
+
const normalizeWithId = (prefix, type, idKey) => event.type.startsWith(prefix) ? {
|
|
2620
|
+
...event,
|
|
2621
|
+
type,
|
|
2622
|
+
[idKey]: event[idKey] ?? event.type.slice(prefix.length)
|
|
2623
|
+
} : void 0;
|
|
2624
|
+
const normalized = normalizeWithId("xstate.done.actor.", "xstate.done.actor", "actorId") ?? normalizeWithId("xstate.error.actor.", "xstate.error.actor", "actorId") ?? normalizeWithId("xstate.snapshot.", "xstate.snapshot.actor", "actorId") ?? normalizeWithId("xstate.done.state.", "xstate.done.state", "stateId") ?? normalizeWithId("xstate.timeout.actor.", "xstate.timeout.actor", "actorId") ?? normalizeWithId("xstate.timeout.", "xstate.timeout", "stateId");
|
|
2625
|
+
if (normalized) return normalized;
|
|
2626
|
+
if (!event.type.startsWith("xstate.after.")) return event;
|
|
2627
|
+
const stateId = [...machine.idMap.keys()].sort((a, b) => b.length - a.length).find((id) => event.type.endsWith(`.${id}`));
|
|
2628
|
+
if (!stateId) return event;
|
|
2629
|
+
const delayText = event.type.slice(13, -stateId.length - 1);
|
|
2630
|
+
return {
|
|
2631
|
+
...event,
|
|
2632
|
+
type: "xstate.after",
|
|
2633
|
+
delay: Number.isNaN(+delayText) ? delayText : +delayText,
|
|
2634
|
+
stateId
|
|
2635
|
+
};
|
|
2636
|
+
}
|
|
2637
|
+
function getCandidates(stateNode, event, warn) {
|
|
2638
|
+
const eventTypes = getEventTypeAliases(event);
|
|
2639
|
+
const exactMatches = eventTypes.flatMap((eventType) => stateNode.transitions.get(eventType) ?? []);
|
|
2640
|
+
const wildcardCandidates = [...stateNode.transitions.keys()].filter((eventDescriptor) => !eventTypes.includes(eventDescriptor) && eventTypes.some((eventType) => matchesEventDescriptor(eventType, eventDescriptor, warn))).sort((a, b) => b.length - a.length).flatMap((key) => stateNode.transitions.get(key));
|
|
2641
|
+
return exactMatches.length ? [...exactMatches, ...wildcardCandidates] : wildcardCandidates;
|
|
2642
|
+
}
|
|
2643
|
+
function scheduleDelayedEvent(stateNode, timerId, resolveEvent, resolveScheduledDelay) {
|
|
2644
|
+
const oldEntry = stateNode.entry;
|
|
2645
|
+
stateNode.entry = (x, enq) => {
|
|
2646
|
+
const result = typeof oldEntry === "function" ? oldEntry(x, enq) : void 0;
|
|
2647
|
+
const context = result?.context !== void 0 ? mergeContextPatch(x.context, result.context) : x.context;
|
|
2648
|
+
enq.raise(resolveEvent(x), {
|
|
2649
|
+
id: timerId,
|
|
2650
|
+
delay: resolveScheduledDelay({
|
|
2651
|
+
context,
|
|
2652
|
+
event: x.event,
|
|
2653
|
+
delays: x.delays,
|
|
2654
|
+
input: x.input
|
|
2655
|
+
})
|
|
2656
|
+
});
|
|
2657
|
+
return result;
|
|
2658
|
+
};
|
|
2659
|
+
const oldExit = stateNode.exit;
|
|
2660
|
+
stateNode.exit = (_, enq) => {
|
|
2661
|
+
enq.cancel(timerId);
|
|
2662
|
+
return typeof oldExit === "function" ? oldExit(_, enq) : void 0;
|
|
2663
|
+
};
|
|
2664
|
+
}
|
|
2665
|
+
/** All delayed transitions from the config. */
|
|
2666
|
+
function getDelayedTransitions(stateNode) {
|
|
2667
|
+
const afterConfig = stateNode.config.after;
|
|
2668
|
+
const timeoutConfig = stateNode.config.timeout;
|
|
2669
|
+
const onTimeoutConfig = stateNode.config.onTimeout;
|
|
2670
|
+
const invokeDefs = stateNode.invoke.filter((invokeDef) => invokeDef.timeout !== void 0);
|
|
2671
|
+
if (!afterConfig && timeoutConfig === void 0 && invokeDefs.length === 0) return [];
|
|
2672
|
+
const sources = [];
|
|
2673
|
+
if (afterConfig) for (const key of Object.keys(afterConfig)) {
|
|
2674
|
+
const delay = Number.isNaN(+key) ? key : +key;
|
|
2675
|
+
sources.push({
|
|
2676
|
+
event: createAfterEvent(delay, stateNode.id),
|
|
2677
|
+
timerId: createAfterEventId(delay, stateNode.id),
|
|
2678
|
+
delay,
|
|
2679
|
+
transitions: afterConfig[key],
|
|
2680
|
+
fromDelaysMap: true
|
|
2681
|
+
});
|
|
2682
|
+
}
|
|
2683
|
+
if (timeoutConfig !== void 0 && onTimeoutConfig) sources.push({
|
|
2684
|
+
event: createTimeoutEvent(stateNode.id),
|
|
2685
|
+
timerId: createTimeoutEventId(stateNode.id),
|
|
2686
|
+
delay: timeoutConfig,
|
|
2687
|
+
transitions: onTimeoutConfig,
|
|
2688
|
+
fromDelaysMap: true
|
|
2689
|
+
});
|
|
2690
|
+
for (const invokeDef of invokeDefs) sources.push({
|
|
2691
|
+
event: createInvokeTimeoutEvent(invokeDef.id),
|
|
2692
|
+
timerId: createInvokeTimeoutEventId(invokeDef.id),
|
|
2693
|
+
resolveEvent: ({ children }) => createInvokeTimeoutEvent(invokeDef.id, children[invokeDef.id]?.sessionId),
|
|
2694
|
+
eventMatcher: (event, snapshot) => matchesActorSession(event, snapshot, invokeDef.id),
|
|
2695
|
+
delay: invokeDef.timeout,
|
|
2696
|
+
transitions: invokeDef.onTimeout,
|
|
2697
|
+
fromDelaysMap: false
|
|
2698
|
+
});
|
|
2699
|
+
const delayedTransitions = [];
|
|
2700
|
+
for (const { event, timerId, resolveEvent, eventMatcher, delay, transitions, fromDelaysMap } of sources) {
|
|
2701
|
+
scheduleDelayedEvent(stateNode, timerId, resolveEvent ?? (() => event), (x) => resolveDelay(delay, fromDelaysMap ? x.delays : {}, {
|
|
2702
|
+
context: x.context,
|
|
2703
|
+
event: x.event,
|
|
2704
|
+
stateNode,
|
|
2705
|
+
input: x.input
|
|
2706
|
+
}));
|
|
2707
|
+
const { type: eventType, ...eventPattern } = event;
|
|
2708
|
+
for (const transition of toTransitionConfigArray(transitions)) delayedTransitions.push({
|
|
2709
|
+
...transition,
|
|
2710
|
+
matches: {
|
|
2711
|
+
...transition.matches,
|
|
2712
|
+
...Object.fromEntries(Object.entries(eventPattern).filter(([, value]) => value !== void 0))
|
|
2713
|
+
},
|
|
2714
|
+
event: eventType,
|
|
2715
|
+
delay,
|
|
2716
|
+
_eventMatcher: eventMatcher
|
|
2717
|
+
});
|
|
2718
|
+
}
|
|
2719
|
+
return delayedTransitions.map((delayedTransition) => ({
|
|
2720
|
+
...formatTransition(stateNode, delayedTransition.event, delayedTransition),
|
|
2721
|
+
delay: delayedTransition.delay
|
|
2722
|
+
}));
|
|
2723
|
+
}
|
|
2724
|
+
function formatTransition(stateNode, descriptor, transitionConfig) {
|
|
2725
|
+
const normalizedTarget = normalizeTarget(transitionConfig.target);
|
|
2726
|
+
const reenter = transitionConfig.reenter ?? false;
|
|
2727
|
+
const target = resolveTarget(stateNode, normalizedTarget);
|
|
2728
|
+
assertLegalTargetSet(stateNode, target);
|
|
2729
|
+
const transition = {
|
|
2730
|
+
...transitionConfig,
|
|
2731
|
+
target,
|
|
2732
|
+
source: stateNode,
|
|
2733
|
+
reenter,
|
|
2734
|
+
eventType: descriptor,
|
|
2735
|
+
toJSON: () => ({
|
|
2736
|
+
...transition,
|
|
2737
|
+
source: `#${stateNode.id}`,
|
|
2738
|
+
target: target ? target.map((t) => `#${t.id}`) : void 0
|
|
2739
|
+
})
|
|
2740
|
+
};
|
|
2741
|
+
return transition;
|
|
2742
|
+
}
|
|
2743
|
+
function isStateNodeDescendantOf(stateNode, ancestor) {
|
|
2744
|
+
for (let current = stateNode.parent; current; current = current.parent) if (current === ancestor) return true;
|
|
2745
|
+
return false;
|
|
2746
|
+
}
|
|
2747
|
+
function getLeastCommonStateNodeAncestor(left, right) {
|
|
2748
|
+
const leftAncestors = /* @__PURE__ */ new Set();
|
|
2749
|
+
for (let current = left; current; current = current.parent) leftAncestors.add(current);
|
|
2750
|
+
for (let current = right; current; current = current.parent) if (leftAncestors.has(current)) return current;
|
|
2751
|
+
}
|
|
2752
|
+
function assertLegalTargetSet(source, targets) {
|
|
2753
|
+
if (!targets || targets.length < 2) return;
|
|
2754
|
+
for (let leftIndex = 0; leftIndex < targets.length; leftIndex++) for (let rightIndex = leftIndex + 1; rightIndex < targets.length; rightIndex++) {
|
|
2755
|
+
const left = targets[leftIndex];
|
|
2756
|
+
const right = targets[rightIndex];
|
|
2757
|
+
if (!left || !right) continue;
|
|
2758
|
+
const commonAncestor = getLeastCommonStateNodeAncestor(left, right);
|
|
2759
|
+
if (left === right || isStateNodeDescendantOf(left, right) || isStateNodeDescendantOf(right, left) || commonAncestor?.type !== "parallel") throw new Error(`Invalid target set for '${source.id}'`);
|
|
2760
|
+
}
|
|
2761
|
+
}
|
|
2762
|
+
/**
|
|
2763
|
+
* Collects route transitions from all descendants with explicit IDs. Called
|
|
2764
|
+
* once on the root node to avoid O(N²) repeated traversals.
|
|
2765
|
+
*/
|
|
2766
|
+
function formatRouteTransitions(rootStateNode) {
|
|
2767
|
+
const routeTransitions = [];
|
|
2768
|
+
const collectRoutes = (states) => {
|
|
2769
|
+
Object.values(states).forEach((sn) => {
|
|
2770
|
+
if (sn.config.route && sn.config.id) {
|
|
2771
|
+
const routeId = sn.config.id;
|
|
2772
|
+
const routeConfig = sn.config.route;
|
|
2773
|
+
const routeMatches = ({ event }) => event.to === `#${routeId}`;
|
|
2774
|
+
if (typeof routeConfig === "function") {
|
|
2775
|
+
routeTransitions.push(formatTransition(rootStateNode, "xstate.route", {
|
|
2776
|
+
guard: routeMatches,
|
|
2777
|
+
to: (args) => {
|
|
2778
|
+
const result = routeConfig(args);
|
|
2779
|
+
if (!result) return;
|
|
2780
|
+
return {
|
|
2781
|
+
...result === true ? {} : result,
|
|
2782
|
+
target: `#${routeId}`
|
|
2783
|
+
};
|
|
2784
|
+
}
|
|
2785
|
+
}));
|
|
2786
|
+
if (sn.states) collectRoutes(sn.states);
|
|
2787
|
+
return;
|
|
2788
|
+
}
|
|
2789
|
+
const { guard: _guard, ...routeOptions } = routeConfig;
|
|
2790
|
+
const transition = {
|
|
2791
|
+
...routeOptions,
|
|
2792
|
+
guard: routeMatches,
|
|
2793
|
+
target: `#${routeId}`
|
|
2794
|
+
};
|
|
2795
|
+
routeTransitions.push(formatTransition(rootStateNode, "xstate.route", transition));
|
|
2796
|
+
}
|
|
2797
|
+
if (sn.states) collectRoutes(sn.states);
|
|
2798
|
+
});
|
|
2799
|
+
};
|
|
2800
|
+
collectRoutes(rootStateNode.states);
|
|
2801
|
+
if (routeTransitions.length > 0) rootStateNode.transitions.set("xstate.route", routeTransitions);
|
|
2802
|
+
}
|
|
2803
|
+
function resolveTarget(stateNode, targets) {
|
|
2804
|
+
if (targets === void 0) return;
|
|
2805
|
+
return targets.map((target) => {
|
|
2806
|
+
if (typeof target !== "string") return target;
|
|
2807
|
+
if (isStateId(target)) return stateNode.machine.getStateNodeById(target);
|
|
2808
|
+
const isInternalTarget = target[0] === ".";
|
|
2809
|
+
if (isInternalTarget && !stateNode.parent) return getStateNodeByPath(stateNode, target.slice(1));
|
|
2810
|
+
const resolvedTarget = isInternalTarget ? stateNode.key + target : target;
|
|
2811
|
+
if (stateNode.parent) try {
|
|
2812
|
+
return getStateNodeByPath(stateNode.parent, resolvedTarget);
|
|
2813
|
+
} catch (err) {
|
|
2814
|
+
throw new Error(`Invalid transition for '${stateNode.id}': ${err.message}`);
|
|
2815
|
+
}
|
|
2816
|
+
else throw new Error(`Invalid target: "${target}"`);
|
|
2817
|
+
});
|
|
2818
|
+
}
|
|
2819
|
+
function resolveHistoryDefaultTargets(stateNode) {
|
|
2820
|
+
const target = normalizeTarget(stateNode.config.target).map((t) => typeof t === "string" ? getStateNodeByPath(stateNode.parent, t) : t);
|
|
2821
|
+
assertLegalTargetSet(stateNode, target);
|
|
2822
|
+
return target;
|
|
2823
|
+
}
|
|
2824
|
+
function getInitialStateNodes(stateNode) {
|
|
2825
|
+
const set = /* @__PURE__ */ new Set();
|
|
2826
|
+
function iter(descStateNode) {
|
|
2827
|
+
if (set.has(descStateNode)) return;
|
|
2828
|
+
set.add(descStateNode);
|
|
2829
|
+
if (descStateNode.type === "compound") for (const target of descStateNode.initial.target ?? []) iter(target);
|
|
2830
|
+
else if (descStateNode.type === "parallel") for (const child of getChildren(descStateNode)) iter(child);
|
|
2831
|
+
}
|
|
2832
|
+
iter(stateNode);
|
|
2833
|
+
for (const initialState of set) for (const ancestor of getProperAncestors(initialState, stateNode)) set.add(ancestor);
|
|
2834
|
+
return set;
|
|
2835
|
+
}
|
|
2836
|
+
/** Returns the child state node from its relative `stateKey`, or throws. */
|
|
2837
|
+
function getStateNode(stateNode, stateKey) {
|
|
2838
|
+
if (isStateId(stateKey)) return stateNode.machine.getStateNodeById(stateKey);
|
|
2839
|
+
const result = stateNode.states?.[stateKey];
|
|
2840
|
+
if (!result) throw new Error(`Child state '${stateKey}' does not exist on '${stateNode.id}'`);
|
|
2841
|
+
return result;
|
|
2842
|
+
}
|
|
2843
|
+
/**
|
|
2844
|
+
* Returns the relative state node from the given `statePath`, or throws.
|
|
2845
|
+
*
|
|
2846
|
+
* @param statePath The string or string array relative path to the state node.
|
|
2847
|
+
*/
|
|
2848
|
+
function getStateNodeByPath(stateNode, statePath) {
|
|
2849
|
+
if (typeof statePath === "string" && isStateId(statePath)) try {
|
|
2850
|
+
return stateNode.machine.getStateNodeById(statePath);
|
|
2851
|
+
} catch {}
|
|
2852
|
+
const arrayStatePath = toStatePath(statePath).slice();
|
|
2853
|
+
let currentStateNode = stateNode;
|
|
2854
|
+
while (arrayStatePath.length) {
|
|
2855
|
+
const key = arrayStatePath.shift();
|
|
2856
|
+
if (!key.length) break;
|
|
2857
|
+
currentStateNode = getStateNode(currentStateNode, key);
|
|
2858
|
+
}
|
|
2859
|
+
return currentStateNode;
|
|
2860
|
+
}
|
|
2861
|
+
/**
|
|
2862
|
+
* Returns the state nodes represented by the current state value.
|
|
2863
|
+
*
|
|
2864
|
+
* @param stateValue The state value or State instance
|
|
2865
|
+
* @public
|
|
2866
|
+
*/
|
|
2867
|
+
function getStateNodes(stateNode, stateValue) {
|
|
2868
|
+
if (typeof stateValue === "string") {
|
|
2869
|
+
const childStateNode = stateNode.states[stateValue];
|
|
2870
|
+
if (!childStateNode) throw new Error(`State '${stateValue}' does not exist on '${stateNode.id}'`);
|
|
2871
|
+
return [stateNode, childStateNode];
|
|
2872
|
+
}
|
|
2873
|
+
const childStateKeys = Object.keys(stateValue);
|
|
2874
|
+
const childStateNodes = new Array(childStateKeys.length);
|
|
2875
|
+
const allStateNodes = [stateNode.machine.root, stateNode];
|
|
2876
|
+
for (let i = 0; i < childStateKeys.length; i++) {
|
|
2877
|
+
const childKey = childStateKeys[i];
|
|
2878
|
+
if (childKey === void 0) continue;
|
|
2879
|
+
const subStateNode = getStateNode(stateNode, childKey);
|
|
2880
|
+
childStateNodes[i] = subStateNode;
|
|
2881
|
+
allStateNodes.push(subStateNode);
|
|
2882
|
+
}
|
|
2883
|
+
for (let i = 0; i < childStateKeys.length; i++) {
|
|
2884
|
+
const childKey = childStateKeys[i];
|
|
2885
|
+
const childStateNode = childStateNodes[i];
|
|
2886
|
+
const childValue = childKey === void 0 ? void 0 : stateValue[childKey];
|
|
2887
|
+
if (childKey === void 0 || childStateNode === void 0 || childValue === void 0) continue;
|
|
2888
|
+
allStateNodes.push(...getStateNodes(childStateNode, childValue));
|
|
2889
|
+
}
|
|
2890
|
+
return allStateNodes;
|
|
2891
|
+
}
|
|
2892
|
+
const selectionResultOf = (selection) => selection.type === "value" ? {
|
|
2893
|
+
enabled: true,
|
|
2894
|
+
result: selection.result,
|
|
2895
|
+
reusable: true
|
|
2896
|
+
} : selection.type === "effect" ? {
|
|
2897
|
+
enabled: true,
|
|
2898
|
+
result: void 0,
|
|
2899
|
+
reusable: false
|
|
2900
|
+
} : {
|
|
2901
|
+
enabled: false,
|
|
2902
|
+
result: void 0,
|
|
2903
|
+
reusable: true
|
|
2904
|
+
};
|
|
2905
|
+
function transitionNode(stateNode, stateValue, snapshot, event, actorScope, selectionResults) {
|
|
2906
|
+
if (typeof stateValue === "string") {
|
|
2907
|
+
const next = getStateNode(stateNode, stateValue).next(snapshot, event, actorScope, selectionResults);
|
|
2908
|
+
if (!next || !next.length) return stateNode.next(snapshot, event, actorScope, selectionResults);
|
|
2909
|
+
return next;
|
|
2910
|
+
}
|
|
2911
|
+
const subStateKeys = Object.keys(stateValue);
|
|
2912
|
+
const subStateKey = subStateKeys[0];
|
|
2913
|
+
if (subStateKeys.length === 1 && subStateKey !== void 0) {
|
|
2914
|
+
const next = transitionNode(getStateNode(stateNode, subStateKey), stateValue[subStateKey], snapshot, event, actorScope, selectionResults);
|
|
2915
|
+
if (!next || !next.length) return stateNode.next(snapshot, event, actorScope, selectionResults);
|
|
2916
|
+
return next;
|
|
2917
|
+
}
|
|
2918
|
+
const allInnerTransitions = [];
|
|
2919
|
+
for (const subStateKey of subStateKeys) {
|
|
2920
|
+
const subStateValue = stateValue[subStateKey];
|
|
2921
|
+
if (!subStateValue) continue;
|
|
2922
|
+
const innerTransitions = transitionNode(getStateNode(stateNode, subStateKey), subStateValue, snapshot, event, actorScope, selectionResults);
|
|
2923
|
+
if (innerTransitions) allInnerTransitions.push(...innerTransitions);
|
|
2924
|
+
}
|
|
2925
|
+
if (!allInnerTransitions.length) return stateNode.next(snapshot, event, actorScope, selectionResults);
|
|
2926
|
+
return allInnerTransitions;
|
|
2927
|
+
}
|
|
2928
|
+
function hasDescendantState(stateNodes, parentStateNode) {
|
|
2929
|
+
for (const stateNode of stateNodes) if (isDescendant({
|
|
2930
|
+
childStateNode: stateNode,
|
|
2931
|
+
parentStateNode
|
|
2932
|
+
})) return true;
|
|
2933
|
+
return false;
|
|
2934
|
+
}
|
|
2935
|
+
function hasIntersection(s1, s2) {
|
|
2936
|
+
const s1Size = s1 instanceof Set ? s1.size : Array.isArray(s1) ? s1.length : void 0;
|
|
2937
|
+
const s2Size = s2 instanceof Set ? s2.size : Array.isArray(s2) ? s2.length : void 0;
|
|
2938
|
+
if (s1Size !== void 0 && s2Size !== void 0 && s2Size < s1Size) [s1, s2] = [s2, s1];
|
|
2939
|
+
const set1 = s1 instanceof Set ? s1 : new Set(s1);
|
|
2940
|
+
for (const item of s2) if (set1.has(item)) return true;
|
|
2941
|
+
return false;
|
|
2942
|
+
}
|
|
2943
|
+
function removeConflictingTransitions(enabledTransitions, stateNodeSet, snapshot, resolveTransition) {
|
|
2944
|
+
const filteredTransitions = /* @__PURE__ */ new Set();
|
|
2945
|
+
const exitSets = /* @__PURE__ */ new Map();
|
|
2946
|
+
const getExitSet = (transition) => {
|
|
2947
|
+
let exitSet = exitSets.get(transition);
|
|
2948
|
+
if (!exitSet) {
|
|
2949
|
+
exitSet = computeExitSet([transition], stateNodeSet, snapshot, resolveTransition);
|
|
2950
|
+
exitSets.set(transition, exitSet);
|
|
2951
|
+
}
|
|
2952
|
+
return exitSet;
|
|
2953
|
+
};
|
|
2954
|
+
for (const t1 of enabledTransitions) {
|
|
2955
|
+
let t1Preempted = false;
|
|
2956
|
+
const transitionsToRemove = /* @__PURE__ */ new Set();
|
|
2957
|
+
for (const t2 of filteredTransitions) if (hasIntersection(getExitSet(t1), getExitSet(t2))) {
|
|
2958
|
+
if (isDescendant({
|
|
2959
|
+
childStateNode: t1.source,
|
|
2960
|
+
parentStateNode: t2.source
|
|
2961
|
+
})) transitionsToRemove.add(t2);
|
|
2962
|
+
else {
|
|
2963
|
+
t1Preempted = true;
|
|
2964
|
+
break;
|
|
2965
|
+
}
|
|
2966
|
+
}
|
|
2967
|
+
if (!t1Preempted) {
|
|
2968
|
+
for (const t3 of transitionsToRemove) filteredTransitions.delete(t3);
|
|
2969
|
+
filteredTransitions.add(t1);
|
|
2970
|
+
}
|
|
2971
|
+
}
|
|
2972
|
+
return Array.from(filteredTransitions);
|
|
2973
|
+
}
|
|
2974
|
+
function createTransitionResultResolver(snapshot, event, actorScope, resolveActions, selectionResults) {
|
|
2975
|
+
let cache;
|
|
2976
|
+
return (transition) => {
|
|
2977
|
+
if (!transition.to) return getTransitionResult(transition, snapshot, event, actorScope, {
|
|
2978
|
+
resolveActions,
|
|
2979
|
+
selectionResult: selectionResults?.get(transition)
|
|
2980
|
+
});
|
|
2981
|
+
let result = cache?.get(transition);
|
|
2982
|
+
if (!result) {
|
|
2983
|
+
result = getTransitionResult(transition, snapshot, event, actorScope, {
|
|
2984
|
+
resolveActions,
|
|
2985
|
+
selectionResult: selectionResults?.get(transition)
|
|
2986
|
+
});
|
|
2987
|
+
(cache ??= /* @__PURE__ */ new Map()).set(transition, result);
|
|
2988
|
+
}
|
|
2989
|
+
return result;
|
|
2990
|
+
};
|
|
2991
|
+
}
|
|
2992
|
+
function getEffectiveTargetStates(transition, historyValue, resolveTransition) {
|
|
2993
|
+
const { targets } = resolveTransition(transition);
|
|
2994
|
+
if (!targets) return [];
|
|
2995
|
+
const targetSet = /* @__PURE__ */ new Set();
|
|
2996
|
+
for (const targetNode of targets) if (isHistoryNode(targetNode)) {
|
|
2997
|
+
const recall = recallHistory({
|
|
2998
|
+
historyNode: targetNode,
|
|
2999
|
+
historyValue,
|
|
3000
|
+
defaultTargets: () => resolveHistoryDefaultTargets(targetNode)
|
|
3001
|
+
});
|
|
3002
|
+
if (recall.type === "recorded") for (const node of recall.nodes) targetSet.add(node);
|
|
3003
|
+
else for (const node of getEffectiveTargetStates(recall.transition, historyValue, resolveTransition)) targetSet.add(node);
|
|
3004
|
+
} else targetSet.add(targetNode);
|
|
3005
|
+
return [...targetSet];
|
|
3006
|
+
}
|
|
3007
|
+
/**
|
|
3008
|
+
* Narrows a parallel domain to the single child region that contains every
|
|
3009
|
+
* effective target, so that a transition confined to one region does not exit
|
|
3010
|
+
* (and reset) its sibling regions.
|
|
3011
|
+
*/
|
|
3012
|
+
function narrowParallelDomain(domain, targetStates) {
|
|
3013
|
+
let narrowed = domain;
|
|
3014
|
+
while (narrowed.type === "parallel") {
|
|
3015
|
+
const region = getChildren(narrowed).find((child) => targetStates.every((target) => target === child || isDescendant({
|
|
3016
|
+
childStateNode: target,
|
|
3017
|
+
parentStateNode: child
|
|
3018
|
+
})));
|
|
3019
|
+
if (!region) break;
|
|
3020
|
+
narrowed = region;
|
|
3021
|
+
}
|
|
3022
|
+
return narrowed;
|
|
3023
|
+
}
|
|
3024
|
+
function getTransitionDomain(transition, snapshot, resolveTransition) {
|
|
3025
|
+
const targetStates = getEffectiveTargetStates(transition, snapshot.historyValue, resolveTransition);
|
|
3026
|
+
const { targets, reenter } = resolveTransition(transition);
|
|
3027
|
+
if (transition._transitionDomain) {
|
|
3028
|
+
if (transition._transitionDomain === "internal" && transition.source.type === "compound" && targetStates.every((target) => isDescendant({
|
|
3029
|
+
childStateNode: target,
|
|
3030
|
+
parentStateNode: transition.source
|
|
3031
|
+
}))) return transition.source;
|
|
3032
|
+
const [head, ...tail] = targetStates.concat(transition.source);
|
|
3033
|
+
if (head === void 0) return;
|
|
3034
|
+
for (const ancestor of getProperAncestors(head, void 0)) if (ancestor.type === "compound" && tail.every((stateNode) => isDescendant({
|
|
3035
|
+
childStateNode: stateNode,
|
|
3036
|
+
parentStateNode: ancestor
|
|
3037
|
+
}))) return ancestor;
|
|
3038
|
+
return;
|
|
3039
|
+
}
|
|
3040
|
+
if (!restoresSourceViaHistory({
|
|
3041
|
+
targets: targets ?? [],
|
|
3042
|
+
effectiveTargetStates: targetStates,
|
|
3043
|
+
source: transition.source
|
|
3044
|
+
}) && targetStates.every((target) => target === transition.source || isDescendant({
|
|
3045
|
+
childStateNode: target,
|
|
3046
|
+
parentStateNode: transition.source
|
|
3047
|
+
}))) return reenter ? transition.source : narrowParallelDomain(transition.source, targetStates);
|
|
3048
|
+
const [head, ...tail] = targetStates.concat(transition.source);
|
|
3049
|
+
if (head === void 0) return;
|
|
3050
|
+
for (const ancestor of getProperAncestors(head, void 0)) if (tail.every((sn) => isDescendant({
|
|
3051
|
+
childStateNode: sn,
|
|
3052
|
+
parentStateNode: ancestor
|
|
3053
|
+
}))) return reenter ? ancestor : narrowParallelDomain(ancestor, targetStates);
|
|
3054
|
+
if (reenter) return;
|
|
3055
|
+
return narrowParallelDomain(transition.source.machine.root, targetStates);
|
|
3056
|
+
}
|
|
3057
|
+
function computeExitSet(transitions, stateNodeSet, snapshot, resolveTransition) {
|
|
3058
|
+
const statesToExit = /* @__PURE__ */ new Set();
|
|
3059
|
+
for (const transition of transitions) {
|
|
3060
|
+
const { targets, reenter } = resolveTransition(transition);
|
|
3061
|
+
if (targets?.length) {
|
|
3062
|
+
const domain = getTransitionDomain(transition, snapshot, resolveTransition);
|
|
3063
|
+
if (reenter && transition.source === domain) statesToExit.add(domain);
|
|
3064
|
+
for (const stateNode of stateNodeSet) if (isDescendant({
|
|
3065
|
+
childStateNode: stateNode,
|
|
3066
|
+
parentStateNode: domain
|
|
3067
|
+
})) statesToExit.add(stateNode);
|
|
3068
|
+
}
|
|
3069
|
+
}
|
|
3070
|
+
return [...statesToExit];
|
|
3071
|
+
}
|
|
3072
|
+
function initialMicrostep(root, preInitialState, actorScope, initEvent, internalQueue) {
|
|
3073
|
+
root.machine._microstepHooks?.begin(actorScope.self);
|
|
3074
|
+
const initialStateNodes = [...getInitialStateNodes(root)];
|
|
3075
|
+
const statesForDefaultEntry = /* @__PURE__ */ new Set();
|
|
3076
|
+
const collectDefaultEntryStates = (stateNode) => {
|
|
3077
|
+
if (stateNode.type === "compound") {
|
|
3078
|
+
statesForDefaultEntry.add(stateNode);
|
|
3079
|
+
for (const target of stateNode.initial.target ?? []) collectDefaultEntryStates(target);
|
|
3080
|
+
} else if (stateNode.type === "parallel") for (const child of getChildren(stateNode)) collectDefaultEntryStates(child);
|
|
3081
|
+
};
|
|
3082
|
+
collectDefaultEntryStates(root);
|
|
3083
|
+
return microstep([{
|
|
3084
|
+
target: initialStateNodes.filter((stateNode) => !initialStateNodes.some((other) => other !== stateNode && isDescendant({
|
|
3085
|
+
childStateNode: other,
|
|
3086
|
+
parentStateNode: stateNode
|
|
3087
|
+
}))),
|
|
3088
|
+
source: root,
|
|
3089
|
+
reenter: true,
|
|
3090
|
+
eventType: null,
|
|
3091
|
+
toJSON: null,
|
|
3092
|
+
_statesForDefaultEntry: [...statesForDefaultEntry]
|
|
3093
|
+
}], preInitialState, actorScope, initEvent, true, internalQueue);
|
|
3094
|
+
}
|
|
3095
|
+
/** https://www.w3.org/TR/scxml/#microstepProcedure */
|
|
3096
|
+
function microstep(transitions, currentSnapshot, actorScope, event, isInitial, internalQueue, selectionResults) {
|
|
3097
|
+
const executableActions = [];
|
|
3098
|
+
const internalEvents = currentSnapshot.machine._microstepHooks?.drain(actorScope.self);
|
|
3099
|
+
if (internalEvents?.length) internalQueue.push(...internalEvents);
|
|
3100
|
+
if (!transitions.length) return [currentSnapshot, executableActions];
|
|
3101
|
+
{
|
|
3102
|
+
const mutStateNodeSet = new Set(currentSnapshot.nodes);
|
|
3103
|
+
let historyValue = currentSnapshot.historyValue;
|
|
3104
|
+
const originalContext = currentSnapshot.context;
|
|
3105
|
+
const filteredTransitions = transitions.length === 1 ? transitions : removeConflictingTransitions(transitions, mutStateNodeSet, currentSnapshot, createTransitionResultResolver(currentSnapshot, event, actorScope, false, selectionResults));
|
|
3106
|
+
const getCurrentTransitionResult = createTransitionResultResolver(currentSnapshot, event, actorScope, true, selectionResults);
|
|
3107
|
+
const changesState = filteredTransitions.some((transition) => {
|
|
3108
|
+
const { targets, reenter } = getCurrentTransitionResult(transition);
|
|
3109
|
+
return !!targets?.length || !!reenter;
|
|
3110
|
+
});
|
|
3111
|
+
const getStateActionsAndContext = (transitionFn, context, children, input, kind, stateNode) => {
|
|
3112
|
+
if (transitionFn.length === 2) {
|
|
3113
|
+
const actions = [];
|
|
3114
|
+
const internalEvents = [];
|
|
3115
|
+
let updatedContext;
|
|
3116
|
+
const enqueue = createTransitionEnqueue(actorScope, actions, internalEvents, true);
|
|
3117
|
+
const args = isLazyActorScope(actorScope) ? withActorScope({
|
|
3118
|
+
context,
|
|
3119
|
+
event,
|
|
3120
|
+
children,
|
|
3121
|
+
actions: currentSnapshot.machine.sources.actions,
|
|
3122
|
+
actors: currentSnapshot.machine.sources.actors,
|
|
3123
|
+
guards: currentSnapshot.machine.sources.guards,
|
|
3124
|
+
delays: currentSnapshot.machine.sources.delays,
|
|
3125
|
+
input,
|
|
3126
|
+
stateNode
|
|
3127
|
+
}, actorScope) : {
|
|
3128
|
+
context,
|
|
3129
|
+
event,
|
|
3130
|
+
parent: actorScope.self._parent,
|
|
3131
|
+
self: actorScope.self,
|
|
3132
|
+
children,
|
|
3133
|
+
system: actorScope.system,
|
|
3134
|
+
actions: currentSnapshot.machine.sources.actions,
|
|
3135
|
+
actors: currentSnapshot.machine.sources.actors,
|
|
3136
|
+
guards: currentSnapshot.machine.sources.guards,
|
|
3137
|
+
delays: currentSnapshot.machine.sources.delays,
|
|
3138
|
+
input,
|
|
3139
|
+
stateNode
|
|
3140
|
+
};
|
|
3141
|
+
let res;
|
|
3142
|
+
try {
|
|
3143
|
+
res = transitionFn(args, enqueue);
|
|
3144
|
+
} finally {
|
|
3145
|
+
closeTransitionEnqueue(enqueue);
|
|
3146
|
+
}
|
|
3147
|
+
assertSyncTransitionResult(res, event, stateNode.id, kind);
|
|
3148
|
+
if (res?.context !== void 0) updatedContext = mergeContextPatch(context, res.context);
|
|
3149
|
+
return [
|
|
3150
|
+
actions,
|
|
3151
|
+
updatedContext,
|
|
3152
|
+
internalEvents
|
|
3153
|
+
];
|
|
3154
|
+
}
|
|
3155
|
+
return [
|
|
3156
|
+
[Object.assign((args, enqueue) => transitionFn(isLazyActorScope(actorScope) ? withActorScope({
|
|
3157
|
+
context: args.context,
|
|
3158
|
+
event: args.event,
|
|
3159
|
+
output: args.output,
|
|
3160
|
+
children: args.children,
|
|
3161
|
+
actions: args.actions,
|
|
3162
|
+
actors: args.actors,
|
|
3163
|
+
input,
|
|
3164
|
+
stateNode
|
|
3165
|
+
}, actorScope) : {
|
|
3166
|
+
...args,
|
|
3167
|
+
input,
|
|
3168
|
+
stateNode
|
|
3169
|
+
}, enqueue), "_special" in transitionFn ? { _special: true } : {})],
|
|
3170
|
+
void 0,
|
|
3171
|
+
void 0
|
|
3172
|
+
];
|
|
3173
|
+
};
|
|
3174
|
+
let nextState = currentSnapshot;
|
|
3175
|
+
const getInvokeStopActions = (stateNode, children) => {
|
|
3176
|
+
const actions = [];
|
|
3177
|
+
const enqueue = createTransitionEnqueue(actorScope, actions, []);
|
|
3178
|
+
for (const invokeDef of stateNode.invoke) {
|
|
3179
|
+
const child = children[invokeDef.id];
|
|
3180
|
+
if (child) enqueue.stop(child);
|
|
3181
|
+
}
|
|
3182
|
+
return actions;
|
|
3183
|
+
};
|
|
3184
|
+
const exitStates = () => {
|
|
3185
|
+
const statesToExit = computeExitSet(filteredTransitions, mutStateNodeSet, currentSnapshot, getCurrentTransitionResult);
|
|
3186
|
+
statesToExit.sort((a, b) => b.order - a.order);
|
|
3187
|
+
let changedHistory;
|
|
3188
|
+
const currentStateNodes = [...mutStateNodeSet];
|
|
3189
|
+
for (const exitStateNode of statesToExit) for (const historyNode of Object.values(exitStateNode.states)) {
|
|
3190
|
+
if (!isHistoryNode(historyNode)) continue;
|
|
3191
|
+
changedHistory ??= { ...historyValue };
|
|
3192
|
+
changedHistory[historyNode.id] = recordHistoryNodes({
|
|
3193
|
+
historyNode,
|
|
3194
|
+
exitingNode: exitStateNode,
|
|
3195
|
+
currentStateNodes
|
|
3196
|
+
});
|
|
3197
|
+
}
|
|
3198
|
+
for (const exitStateNode of statesToExit) {
|
|
3199
|
+
const stateInput = getStateInput(currentSnapshot, exitStateNode.id);
|
|
3200
|
+
const [exitActions, nextContext, internalEvents] = exitStateNode.exit ? getStateActionsAndContext(exitStateNode.exit, nextState.context, currentSnapshot.children, stateInput, "exit", exitStateNode) : [
|
|
3201
|
+
[],
|
|
3202
|
+
void 0,
|
|
3203
|
+
void 0
|
|
3204
|
+
];
|
|
3205
|
+
if (internalEvents?.length) internalQueue.push(...internalEvents);
|
|
3206
|
+
if (nextContext) nextState = cloneMachineSnapshot(nextState, { context: nextContext });
|
|
3207
|
+
const [resolvedState, resolvedActions] = resolveActionsWithContext(nextState, event, actorScope, exitActions);
|
|
3208
|
+
nextState = resolvedState;
|
|
3209
|
+
executableActions.push(...resolvedActions);
|
|
3210
|
+
const invokeStopActions = getInvokeStopActions(exitStateNode, nextState.children);
|
|
3211
|
+
if (invokeStopActions.length) {
|
|
3212
|
+
const [stoppedState, stopEffects] = resolveActionsWithContext(nextState, event, actorScope, invokeStopActions);
|
|
3213
|
+
nextState = stoppedState;
|
|
3214
|
+
executableActions.push(...stopEffects);
|
|
3215
|
+
}
|
|
3216
|
+
mutStateNodeSet.delete(exitStateNode);
|
|
3217
|
+
}
|
|
3218
|
+
historyValue = changedHistory || historyValue;
|
|
3219
|
+
};
|
|
3220
|
+
if (!isInitial && changesState) exitStates();
|
|
3221
|
+
let context = nextState.context;
|
|
3222
|
+
const transitionActions = [];
|
|
3223
|
+
const internalEvents = [];
|
|
3224
|
+
for (const t of filteredTransitions) {
|
|
3225
|
+
if (t.actions) transitionActions.push(...toArray(t.actions));
|
|
3226
|
+
const res = getCurrentTransitionResult(t);
|
|
3227
|
+
if (res.context !== void 0) context = mergeContextPatch(context, res.context);
|
|
3228
|
+
if (res.actions) transitionActions.push(...res.actions);
|
|
3229
|
+
if (res.internalEvents) internalEvents.push(...res.internalEvents);
|
|
3230
|
+
}
|
|
3231
|
+
if (internalEvents.length) internalQueue.push(...internalEvents);
|
|
3232
|
+
const enterStates = () => {
|
|
3233
|
+
const getMachineOutput = (rootCompletionNode) => {
|
|
3234
|
+
const rootNode = nextState.machine.root;
|
|
3235
|
+
let completionOutput;
|
|
3236
|
+
if (rootCompletionNode.output !== void 0 && rootCompletionNode.parent) {
|
|
3237
|
+
const rootDoneEvent = internalQueue.find((e) => e.type === "xstate.done.state" && e.stateId === rootNode.id);
|
|
3238
|
+
completionOutput = rootDoneEvent && rootCompletionNode.parent === rootNode ? rootDoneEvent.output : resolveOutput(rootCompletionNode.output, nextState.context, event, actorScope.self, actorScope.warn, stateInputMap[rootCompletionNode.id]);
|
|
3239
|
+
} else if (rootCompletionNode.type === "parallel") completionOutput = internalQueue.find((e) => e.type === "xstate.done.state" && e.stateId === rootCompletionNode.id)?.output;
|
|
3240
|
+
if (rootNode.output === void 0) return rootCompletionNode.parent === rootNode ? completionOutput : void 0;
|
|
3241
|
+
return resolveOutput(rootNode.output, nextState.context, createDoneStateEvent(rootCompletionNode.id, completionOutput), actorScope.self, actorScope.warn);
|
|
3242
|
+
};
|
|
3243
|
+
const statesToEnter = /* @__PURE__ */ new Set();
|
|
3244
|
+
const statesForDefaultEntry = /* @__PURE__ */ new Set();
|
|
3245
|
+
const addAncestorStatesToEnter = (ancestors, reentrancyDomain) => {
|
|
3246
|
+
for (const anc of ancestors) {
|
|
3247
|
+
if (!reentrancyDomain || isDescendant({
|
|
3248
|
+
childStateNode: anc,
|
|
3249
|
+
parentStateNode: reentrancyDomain
|
|
3250
|
+
})) statesToEnter.add(anc);
|
|
3251
|
+
if (anc.type === "parallel") {
|
|
3252
|
+
for (const child of getChildren(anc)) if (!hasDescendantState(statesToEnter, child)) {
|
|
3253
|
+
statesToEnter.add(child);
|
|
3254
|
+
addDescendantStatesToEnter(child);
|
|
3255
|
+
}
|
|
3256
|
+
}
|
|
3257
|
+
}
|
|
3258
|
+
};
|
|
3259
|
+
const addDescendantStatesToEnter = (stateNode) => {
|
|
3260
|
+
if (isHistoryNode(stateNode)) {
|
|
3261
|
+
const recall = recallHistory({
|
|
3262
|
+
historyNode: stateNode,
|
|
3263
|
+
historyValue,
|
|
3264
|
+
defaultTargets: () => resolveHistoryDefaultTargets(stateNode)
|
|
3265
|
+
});
|
|
3266
|
+
if (recall.type === "recorded") {
|
|
3267
|
+
for (const s of recall.nodes) {
|
|
3268
|
+
statesToEnter.add(s);
|
|
3269
|
+
addDescendantStatesToEnter(s);
|
|
3270
|
+
}
|
|
3271
|
+
for (const s of recall.nodes) addAncestorStatesToEnter(getProperAncestors(s, stateNode.parent), void 0);
|
|
3272
|
+
} else {
|
|
3273
|
+
statesForDefaultEntry.add(stateNode.parent);
|
|
3274
|
+
const { targets } = getCurrentTransitionResult(recall.transition);
|
|
3275
|
+
for (const s of targets ?? []) {
|
|
3276
|
+
statesToEnter.add(s);
|
|
3277
|
+
addDescendantStatesToEnter(s);
|
|
3278
|
+
}
|
|
3279
|
+
for (const s of targets ?? []) addAncestorStatesToEnter(getProperAncestors(s, stateNode.parent), void 0);
|
|
3280
|
+
}
|
|
3281
|
+
return;
|
|
3282
|
+
}
|
|
3283
|
+
if (stateNode.type === "compound") {
|
|
3284
|
+
const initialStates = getCurrentTransitionResult(stateNode.initial).targets ?? [];
|
|
3285
|
+
for (const initialState of initialStates) if (!isHistoryNode(initialState)) statesToEnter.add(initialState);
|
|
3286
|
+
statesForDefaultEntry.add(stateNode);
|
|
3287
|
+
for (const initialState of initialStates) addDescendantStatesToEnter(initialState);
|
|
3288
|
+
for (const initialState of initialStates) addAncestorStatesToEnter(getProperAncestors(initialState, stateNode), void 0);
|
|
3289
|
+
return;
|
|
3290
|
+
}
|
|
3291
|
+
if (stateNode.type === "parallel") {
|
|
3292
|
+
for (const child of getChildren(stateNode)) if (!hasDescendantState(statesToEnter, child)) {
|
|
3293
|
+
statesToEnter.add(child);
|
|
3294
|
+
statesForDefaultEntry.add(child);
|
|
3295
|
+
addDescendantStatesToEnter(child);
|
|
3296
|
+
}
|
|
3297
|
+
}
|
|
3298
|
+
};
|
|
3299
|
+
const enteredTargetsByTransition = /* @__PURE__ */ new Map();
|
|
3300
|
+
for (const transition of filteredTransitions) {
|
|
3301
|
+
const domain = getTransitionDomain(transition, currentSnapshot, getCurrentTransitionResult);
|
|
3302
|
+
const { targets, reenter } = getCurrentTransitionResult(transition);
|
|
3303
|
+
const enteredTargets = /* @__PURE__ */ new Set();
|
|
3304
|
+
enteredTargetsByTransition.set(transition, enteredTargets);
|
|
3305
|
+
for (const targetNode of targets ?? []) {
|
|
3306
|
+
if (!isHistoryNode(targetNode) && (transition.source !== targetNode || transition.source !== domain || reenter)) {
|
|
3307
|
+
enteredTargets.add(targetNode);
|
|
3308
|
+
statesToEnter.add(targetNode);
|
|
3309
|
+
statesForDefaultEntry.add(targetNode);
|
|
3310
|
+
}
|
|
3311
|
+
addDescendantStatesToEnter(targetNode);
|
|
3312
|
+
}
|
|
3313
|
+
const targetStates = getEffectiveTargetStates(transition, historyValue, getCurrentTransitionResult);
|
|
3314
|
+
for (const s of targetStates) {
|
|
3315
|
+
const ancestors = getProperAncestors(s, domain);
|
|
3316
|
+
if (domain?.type === "parallel") ancestors.push(domain);
|
|
3317
|
+
addAncestorStatesToEnter(ancestors, !transition.source.parent && reenter ? void 0 : domain);
|
|
3318
|
+
}
|
|
3319
|
+
if (reenter && domain === transition.source) statesToEnter.add(transition.source);
|
|
3320
|
+
}
|
|
3321
|
+
if (isInitial) for (const stateNode of filteredTransitions[0]._statesForDefaultEntry ?? [currentSnapshot.machine.root]) statesForDefaultEntry.add(stateNode);
|
|
3322
|
+
const stateInputMap = { ...currentSnapshot._stateInputs };
|
|
3323
|
+
let stateInputsChanged = false;
|
|
3324
|
+
for (const transition of filteredTransitions) {
|
|
3325
|
+
const { targets, input } = getCurrentTransitionResult(transition);
|
|
3326
|
+
if (input && targets) for (const targetNode of targets.filter((targetNode) => enteredTargetsByTransition.get(transition)?.has(targetNode))) {
|
|
3327
|
+
stateInputMap[targetNode.id] = input;
|
|
3328
|
+
stateInputsChanged = true;
|
|
3329
|
+
}
|
|
3330
|
+
}
|
|
3331
|
+
const completedNodes = /* @__PURE__ */ new Set();
|
|
3332
|
+
const children = { ...nextState.children };
|
|
3333
|
+
for (const stateNodeToEnter of [...statesToEnter].sort((a, b) => a.order - b.order)) {
|
|
3334
|
+
mutStateNodeSet.add(stateNodeToEnter);
|
|
3335
|
+
const actions = [];
|
|
3336
|
+
let invoked = false;
|
|
3337
|
+
for (const invokeDef of stateNodeToEnter.type === "final" ? [] : stateNodeToEnter.invoke) {
|
|
3338
|
+
invoked = true;
|
|
3339
|
+
let src = invokeDef.logic;
|
|
3340
|
+
if (typeof src === "function") src = src({
|
|
3341
|
+
actors: currentSnapshot.machine.sources.actors,
|
|
3342
|
+
context: nextState.context,
|
|
3343
|
+
event,
|
|
3344
|
+
self: actorScope.self
|
|
3345
|
+
});
|
|
3346
|
+
const logic = typeof src === "string" ? resolveReferencedActor(currentSnapshot.machine, src) : src;
|
|
3347
|
+
if (!logic) throw new Error(`Actor logic '${typeof src === "string" ? src : "inline"}' not implemented`);
|
|
3348
|
+
const input = typeof invokeDef.input === "function" ? invokeDef.input({
|
|
3349
|
+
self: actorScope.self,
|
|
3350
|
+
context: nextState.context,
|
|
3351
|
+
event,
|
|
3352
|
+
...stateInputMap[stateNodeToEnter.id] !== void 0 && { input: stateInputMap[stateNodeToEnter.id] },
|
|
3353
|
+
output: getEventOutput(event)
|
|
3354
|
+
}) : invokeDef.input;
|
|
3355
|
+
assertChildIdFree(actorScope, invokeDef.id);
|
|
3356
|
+
const actor = actorScope.system.createActorRef(logic, {
|
|
3357
|
+
...invokeDef,
|
|
3358
|
+
input,
|
|
3359
|
+
parent: actorScope.self,
|
|
3360
|
+
syncSnapshot: !!invokeDef.onSnapshot
|
|
3361
|
+
});
|
|
3362
|
+
actions.push({
|
|
3363
|
+
action: builtInActions["@xstate.spawn"],
|
|
3364
|
+
args: [actor]
|
|
3365
|
+
});
|
|
3366
|
+
if (invokeDef.id) children[invokeDef.id] = actor;
|
|
3367
|
+
}
|
|
3368
|
+
if (invoked) nextState = cloneMachineSnapshot(nextState, { children });
|
|
3369
|
+
let context = nextState.context;
|
|
3370
|
+
let contextChangedByEntry = false;
|
|
3371
|
+
const stateInput = stateInputMap[stateNodeToEnter.id];
|
|
3372
|
+
if (stateNodeToEnter.entry) {
|
|
3373
|
+
const [resultActions, nextContext, nextInternalEvents] = getStateActionsAndContext(stateNodeToEnter.entry, context, children, stateInput, "entry", stateNodeToEnter);
|
|
3374
|
+
actions.push(...resultActions);
|
|
3375
|
+
if (nextInternalEvents?.length) internalQueue.push(...nextInternalEvents);
|
|
3376
|
+
if (nextContext) {
|
|
3377
|
+
context = nextContext;
|
|
3378
|
+
contextChangedByEntry = true;
|
|
3379
|
+
}
|
|
3380
|
+
}
|
|
3381
|
+
if (contextChangedByEntry) nextState.context = context;
|
|
3382
|
+
const defaultTransition = stateNodeToEnter.initial;
|
|
3383
|
+
if (defaultTransition && statesForDefaultEntry.has(stateNodeToEnter)) {
|
|
3384
|
+
const { actions: initialActions, context: initialContext, input: initialInput, internalEvents: initialInternalEvents } = getTransitionResult(defaultTransition, nextState, event, actorScope);
|
|
3385
|
+
if (initialActions) actions.push(...initialActions);
|
|
3386
|
+
if (initialInternalEvents?.length) internalQueue.push(...initialInternalEvents);
|
|
3387
|
+
if (initialContext !== void 0) nextState.context = mergeContextPatch(nextState.context, initialContext);
|
|
3388
|
+
if (initialInput && defaultTransition.target) for (const targetNode of defaultTransition.target) {
|
|
3389
|
+
stateInputMap[targetNode.id] = initialInput;
|
|
3390
|
+
stateInputsChanged = true;
|
|
3391
|
+
}
|
|
3392
|
+
}
|
|
3393
|
+
const [resolvedState, resolvedActions] = resolveActionsWithContext(nextState, event, actorScope, actions);
|
|
3394
|
+
nextState = resolvedState;
|
|
3395
|
+
actions.length = 0;
|
|
3396
|
+
executableActions.push(...resolvedActions);
|
|
3397
|
+
if (stateNodeToEnter.type !== "final") continue;
|
|
3398
|
+
const parent = stateNodeToEnter.parent;
|
|
3399
|
+
let ancestorMarker = parent?.type === "parallel" ? parent : parent?.parent;
|
|
3400
|
+
let rootCompletionNode = ancestorMarker || stateNodeToEnter;
|
|
3401
|
+
if (parent?.type === "compound") internalQueue.push(createDoneStateEvent(parent.id, stateNodeToEnter.output !== void 0 ? resolveOutput(stateNodeToEnter.output, nextState.context, event, actorScope.self, actorScope.warn, stateInput) : void 0));
|
|
3402
|
+
while (ancestorMarker?.type === "parallel" && !completedNodes.has(ancestorMarker) && isInFinalState(mutStateNodeSet, ancestorMarker)) {
|
|
3403
|
+
completedNodes.add(ancestorMarker);
|
|
3404
|
+
const regionOutput = {};
|
|
3405
|
+
for (const region of getChildren(ancestorMarker)) {
|
|
3406
|
+
if (region.type === "final") {
|
|
3407
|
+
regionOutput[region.key] = region.output !== void 0 ? resolveOutput(region.output, nextState.context, event, actorScope.self, actorScope.warn, stateInputMap[region.id]) : void 0;
|
|
3408
|
+
continue;
|
|
3409
|
+
}
|
|
3410
|
+
if (region.type === "parallel") {
|
|
3411
|
+
const regionDoneEvent = internalQueue.find((e) => e.type === "xstate.done.state" && e.stateId === region.id);
|
|
3412
|
+
regionOutput[region.key] = regionDoneEvent?.output;
|
|
3413
|
+
continue;
|
|
3414
|
+
}
|
|
3415
|
+
const finalChild = getChildren(region).find((s) => s.type === "final" && mutStateNodeSet.has(s));
|
|
3416
|
+
regionOutput[region.key] = finalChild?.output !== void 0 ? resolveOutput(finalChild.output, nextState.context, event, actorScope.self, actorScope.warn, stateInputMap[finalChild.id]) : void 0;
|
|
3417
|
+
}
|
|
3418
|
+
internalQueue.push(createDoneStateEvent(ancestorMarker.id, regionOutput));
|
|
3419
|
+
rootCompletionNode = ancestorMarker;
|
|
3420
|
+
ancestorMarker = ancestorMarker.parent;
|
|
3421
|
+
}
|
|
3422
|
+
if (ancestorMarker) continue;
|
|
3423
|
+
nextState = cloneMachineSnapshot(nextState, {
|
|
3424
|
+
status: "done",
|
|
3425
|
+
output: getMachineOutput(rootCompletionNode)
|
|
3426
|
+
});
|
|
3427
|
+
}
|
|
3428
|
+
if (stateInputsChanged) nextState = cloneMachineSnapshot(nextState, { _stateInputs: stateInputMap });
|
|
3429
|
+
};
|
|
3430
|
+
const [resolvedTransitionState, transitionExecutableActions] = resolveActionsWithContext(nextState, event, actorScope, transitionActions);
|
|
3431
|
+
nextState = resolvedTransitionState;
|
|
3432
|
+
executableActions.push(...transitionExecutableActions);
|
|
3433
|
+
if (context && context !== currentSnapshot.context) nextState = cloneMachineSnapshot(nextState, { context });
|
|
3434
|
+
if (isInitial || changesState) enterStates();
|
|
3435
|
+
const nextStateNodes = [...mutStateNodeSet];
|
|
3436
|
+
if (nextState.status === "done") {
|
|
3437
|
+
const allExitActions = [];
|
|
3438
|
+
nextStateNodes.sort((a, b) => b.order - a.order).forEach((stateNode) => {
|
|
3439
|
+
if (stateNode.exit) {
|
|
3440
|
+
const stateInput = getStateInput(nextState, stateNode.id);
|
|
3441
|
+
const [exitActions, , nextInternalEvents] = getStateActionsAndContext(stateNode.exit, nextState.context, nextState.children, stateInput, "exit", stateNode);
|
|
3442
|
+
allExitActions.push(...exitActions);
|
|
3443
|
+
if (nextInternalEvents?.length) internalQueue.push(...nextInternalEvents);
|
|
3444
|
+
}
|
|
3445
|
+
});
|
|
3446
|
+
const [resolvedState, resolvedActions] = resolveActionsWithContext(nextState, event, actorScope, allExitActions);
|
|
3447
|
+
nextState = resolvedState;
|
|
3448
|
+
executableActions.push(...resolvedActions);
|
|
3449
|
+
const [stoppedState, stopEffects] = stopChildren(nextState, event, actorScope);
|
|
3450
|
+
nextState = stoppedState;
|
|
3451
|
+
executableActions.push(...stopEffects);
|
|
3452
|
+
const [withoutTimers, cancelEffects] = cancelTimers(nextState, event, actorScope);
|
|
3453
|
+
nextState = withoutTimers;
|
|
3454
|
+
executableActions.push(...cancelEffects);
|
|
3455
|
+
}
|
|
3456
|
+
if (historyValue === currentSnapshot.historyValue && currentSnapshot.nodes.length === mutStateNodeSet.size && currentSnapshot.nodes.every((node) => mutStateNodeSet.has(node))) {
|
|
3457
|
+
if (nextState.context !== originalContext) return [nextState === currentSnapshot ? cloneMachineSnapshot(nextState) : nextState, executableActions];
|
|
3458
|
+
return [nextState, executableActions];
|
|
3459
|
+
}
|
|
3460
|
+
return [cloneMachineSnapshot(nextState, {
|
|
3461
|
+
_nodes: nextStateNodes,
|
|
3462
|
+
historyValue
|
|
3463
|
+
}), executableActions];
|
|
3464
|
+
}
|
|
3465
|
+
}
|
|
3466
|
+
/**
|
|
3467
|
+
* Gets the transition result for a given transition without executing the
|
|
3468
|
+
* transition.
|
|
3469
|
+
*/
|
|
3470
|
+
function getTransitionResult(transition, snapshot, event, actorScope, options) {
|
|
3471
|
+
let transitionArgs;
|
|
3472
|
+
const getTransitionArgs = () => transitionArgs ??= isLazyActorScope(actorScope) ? withActorScope({
|
|
3473
|
+
context: snapshot.context,
|
|
3474
|
+
event,
|
|
3475
|
+
output: getEventOutput(event),
|
|
3476
|
+
value: snapshot.value,
|
|
3477
|
+
children: snapshot.children,
|
|
3478
|
+
actions: snapshot.machine.sources.actions,
|
|
3479
|
+
actors: snapshot.machine.sources.actors,
|
|
3480
|
+
guards: snapshot.machine.sources.guards,
|
|
3481
|
+
delays: snapshot.machine.sources.delays,
|
|
3482
|
+
input: getStateInput(snapshot, transition.source.id)
|
|
3483
|
+
}, actorScope) : {
|
|
3484
|
+
context: snapshot.context,
|
|
3485
|
+
event,
|
|
3486
|
+
output: getEventOutput(event),
|
|
3487
|
+
value: snapshot.value,
|
|
3488
|
+
children: snapshot.children,
|
|
3489
|
+
system: actorScope.system,
|
|
3490
|
+
parent: actorScope.self._parent,
|
|
3491
|
+
self: actorScope.self,
|
|
3492
|
+
actions: snapshot.machine.sources.actions,
|
|
3493
|
+
actors: snapshot.machine.sources.actors,
|
|
3494
|
+
guards: snapshot.machine.sources.guards,
|
|
3495
|
+
delays: snapshot.machine.sources.delays,
|
|
3496
|
+
input: getStateInput(snapshot, transition.source.id)
|
|
3497
|
+
};
|
|
3498
|
+
if (transition.to) {
|
|
3499
|
+
const actions = [];
|
|
3500
|
+
const internalEvents = [];
|
|
3501
|
+
let res;
|
|
3502
|
+
if (options?.selectionResult?.reusable) res = options.selectionResult.result;
|
|
3503
|
+
else {
|
|
3504
|
+
const enqueue = createTransitionEnqueue(actorScope, actions, internalEvents, true, options?.resolveActions ?? true);
|
|
3505
|
+
res = transition.to(getTransitionArgs(), enqueue);
|
|
3506
|
+
closeTransitionEnqueue(enqueue);
|
|
3507
|
+
assertSyncTransitionResult(res, event, transition.source.id);
|
|
3508
|
+
}
|
|
3509
|
+
const targets = res?.target ? resolveTarget(transition.source, toArray(res.target)) : void 0;
|
|
3510
|
+
const resolvedInput = res?.input ?? (typeof transition.input === "function" ? transition.input({
|
|
3511
|
+
context: snapshot.context,
|
|
3512
|
+
event,
|
|
3513
|
+
output: getEventOutput(event)
|
|
3514
|
+
}) : transition.input);
|
|
3515
|
+
return {
|
|
3516
|
+
targets,
|
|
3517
|
+
context: res?.context,
|
|
3518
|
+
reenter: res?.reenter,
|
|
3519
|
+
actions,
|
|
3520
|
+
internalEvents,
|
|
3521
|
+
input: resolvedInput
|
|
3522
|
+
};
|
|
3523
|
+
}
|
|
3524
|
+
const resolvedInput = typeof transition.input === "function" ? transition.input({
|
|
3525
|
+
context: snapshot.context,
|
|
3526
|
+
event,
|
|
3527
|
+
output: getEventOutput(event)
|
|
3528
|
+
}) : transition.input;
|
|
3529
|
+
const resolvedContext = typeof transition.context === "function" ? transition.context(getTransitionArgs()) : transition.context;
|
|
3530
|
+
return {
|
|
3531
|
+
targets: transition.target,
|
|
3532
|
+
context: resolvedContext,
|
|
3533
|
+
reenter: transition.reenter,
|
|
3534
|
+
actions: void 0,
|
|
3535
|
+
internalEvents: void 0,
|
|
3536
|
+
input: resolvedInput
|
|
3537
|
+
};
|
|
3538
|
+
}
|
|
3539
|
+
/** The default microstep bound for one macrostep. */
|
|
3540
|
+
const DEFAULT_MAX_ITERATIONS = 1e3;
|
|
3541
|
+
/**
|
|
3542
|
+
* Thrown when one macrostep takes more microsteps than the machine's
|
|
3543
|
+
* `options.maxIterations` (default `1000`) without reaching a stable state,
|
|
3544
|
+
* usually because eventless transitions or raised events form a cycle.
|
|
3545
|
+
*
|
|
3546
|
+
* @public
|
|
3547
|
+
*/
|
|
3548
|
+
var InfiniteTransitionError = class extends Error {
|
|
3549
|
+
/** The id of the actor that was transitioning, if any. */
|
|
3550
|
+
actorId;
|
|
3551
|
+
/** The event being processed. */
|
|
3552
|
+
event;
|
|
3553
|
+
/** The microstep bound that was exceeded. */
|
|
3554
|
+
maxIterations;
|
|
3555
|
+
/** The last (up to 5) state values visited, oldest first. */
|
|
3556
|
+
states;
|
|
3557
|
+
constructor(actorId, event, maxIterations, states) {
|
|
3558
|
+
super(`Infinite loop detected in actor "${actorId}" processing event "${event.type}": more than ${maxIterations} microsteps without reaching a stable state. Last states: ${states.map((value) => JSON.stringify(value)).join(" -> ")}. Check for a cycle of eventless transitions or raised events, or raise the bound with createMachine({ options: { maxIterations } }).`);
|
|
3559
|
+
this.name = "InfiniteTransitionError";
|
|
3560
|
+
this.actorId = actorId;
|
|
3561
|
+
this.event = event;
|
|
3562
|
+
this.maxIterations = maxIterations;
|
|
3563
|
+
this.states = states.slice();
|
|
3564
|
+
}
|
|
3565
|
+
};
|
|
3566
|
+
function macrostep(snapshot, event, actorScope, internalQueue, initialMicrosteps = []) {
|
|
3567
|
+
let nextSnapshot = snapshot;
|
|
3568
|
+
const microsteps = initialMicrosteps.slice();
|
|
3569
|
+
let handled = false;
|
|
3570
|
+
function removeTerminatedChild(terminalEvent) {
|
|
3571
|
+
if (terminalEvent.type !== "xstate.done.actor" && terminalEvent.type !== "xstate.error.actor") return;
|
|
3572
|
+
const { actorId, sessionId } = terminalEvent;
|
|
3573
|
+
if (!actorId) return;
|
|
3574
|
+
const child = nextSnapshot.children[actorId];
|
|
3575
|
+
if (!child) return;
|
|
3576
|
+
if (child.sessionId !== void 0 && child.sessionId !== sessionId) return;
|
|
3577
|
+
const children = { ...nextSnapshot.children };
|
|
3578
|
+
delete children[actorId];
|
|
3579
|
+
actorScope.system._unregister(child);
|
|
3580
|
+
nextSnapshot = cloneMachineSnapshot(nextSnapshot, { children });
|
|
3581
|
+
if (microsteps.length) {
|
|
3582
|
+
const [, lastEffects, lastTransitions] = microsteps.at(-1);
|
|
3583
|
+
microsteps[microsteps.length - 1] = [
|
|
3584
|
+
nextSnapshot,
|
|
3585
|
+
lastEffects,
|
|
3586
|
+
lastTransitions
|
|
3587
|
+
];
|
|
3588
|
+
} else microsteps.push([
|
|
3589
|
+
nextSnapshot,
|
|
3590
|
+
[],
|
|
3591
|
+
[]
|
|
3592
|
+
]);
|
|
3593
|
+
}
|
|
3594
|
+
function completeMacrostep() {
|
|
3595
|
+
if (handled && nextSnapshot === snapshot) {
|
|
3596
|
+
nextSnapshot = cloneMachineSnapshot(snapshot, {});
|
|
3597
|
+
if (microsteps.at(-1)?.[0] === snapshot) {
|
|
3598
|
+
const [, lastEffects, lastTransitions] = microsteps.at(-1);
|
|
3599
|
+
microsteps[microsteps.length - 1] = [
|
|
3600
|
+
nextSnapshot,
|
|
3601
|
+
lastEffects,
|
|
3602
|
+
lastTransitions
|
|
3603
|
+
];
|
|
3604
|
+
}
|
|
3605
|
+
}
|
|
3606
|
+
const starts = deriveDeferredStarts(microsteps.flatMap(([, actions]) => actions));
|
|
3607
|
+
const shouldTerminate = (snapshot.status === "active" || initialMicrosteps.length > 0) && (nextSnapshot.status === "done" || nextSnapshot.status === "error");
|
|
3608
|
+
if (starts.length || shouldTerminate) {
|
|
3609
|
+
const terminalEffects = shouldTerminate ? [...starts, createTerminationEffect(actorScope, nextSnapshot)] : starts;
|
|
3610
|
+
if (microsteps.length) {
|
|
3611
|
+
const [lastSnapshot, lastEffects, lastTransitions] = microsteps.at(-1);
|
|
3612
|
+
microsteps[microsteps.length - 1] = [
|
|
3613
|
+
lastSnapshot,
|
|
3614
|
+
[...lastEffects, ...terminalEffects],
|
|
3615
|
+
lastTransitions
|
|
3616
|
+
];
|
|
3617
|
+
} else microsteps.push([
|
|
3618
|
+
nextSnapshot,
|
|
3619
|
+
terminalEffects,
|
|
3620
|
+
[]
|
|
3621
|
+
]);
|
|
3622
|
+
}
|
|
3623
|
+
return {
|
|
3624
|
+
snapshot: nextSnapshot,
|
|
3625
|
+
microsteps
|
|
3626
|
+
};
|
|
3627
|
+
}
|
|
3628
|
+
function addMicrostep(step, transitions) {
|
|
3629
|
+
if (!isInertActorScope(actorScope) && (event.type === "@xstate.init" || (actorScope.system._hasInspectionObservers?.() ?? true))) {
|
|
3630
|
+
const collectedMicrosteps = actorScope.self._collectedMicrosteps || [];
|
|
3631
|
+
collectedMicrosteps.push(...transitions);
|
|
3632
|
+
actorScope.self._collectedMicrosteps = collectedMicrosteps;
|
|
3633
|
+
}
|
|
3634
|
+
microsteps.push([
|
|
3635
|
+
step[0],
|
|
3636
|
+
step[1],
|
|
3637
|
+
transitions
|
|
3638
|
+
]);
|
|
3639
|
+
}
|
|
3640
|
+
if (event.type === "@xstate.stop") {
|
|
3641
|
+
const [stoppedChildrenSnapshot, stopEffects] = stopChildren(nextSnapshot, event, actorScope);
|
|
3642
|
+
const [withoutTimers, cancelEffects] = cancelTimers(stoppedChildrenSnapshot, event, actorScope);
|
|
3643
|
+
nextSnapshot = cloneMachineSnapshot(withoutTimers, { status: "stopped" });
|
|
3644
|
+
addMicrostep([nextSnapshot, [...stopEffects, ...cancelEffects]], []);
|
|
3645
|
+
return completeMacrostep();
|
|
3646
|
+
}
|
|
3647
|
+
let nextEvent = normalizeLegacyInternalEvent(event, snapshot.machine);
|
|
3648
|
+
if (event.type === "xstate.timer") {
|
|
3649
|
+
const timer = nextSnapshot.timers[event.id];
|
|
3650
|
+
if (!timer) return completeMacrostep();
|
|
3651
|
+
const timers = { ...nextSnapshot.timers };
|
|
3652
|
+
delete timers[timer.id];
|
|
3653
|
+
nextSnapshot = cloneMachineSnapshot(nextSnapshot, { timers });
|
|
3654
|
+
if (timer.type === "@xstate.raise") {
|
|
3655
|
+
internalQueue.push(timer.event);
|
|
3656
|
+
addMicrostep([nextSnapshot, []], []);
|
|
3657
|
+
} else {
|
|
3658
|
+
const target = timer.target === "self" ? actorScope.self : timer.target;
|
|
3659
|
+
addMicrostep([nextSnapshot, [createSendToEffect(actorScope, target, timer.event)]], []);
|
|
3660
|
+
}
|
|
3661
|
+
}
|
|
3662
|
+
if (nextEvent.type !== "@xstate.init" && nextEvent.type !== "xstate.timer") {
|
|
3663
|
+
const currentEvent = nextEvent;
|
|
3664
|
+
const isErr = isErrorEvent(currentEvent);
|
|
3665
|
+
const selectionResults = /* @__PURE__ */ new Map();
|
|
3666
|
+
const transitions = nextSnapshot.machine.getTransitionData(nextSnapshot, currentEvent, actorScope, selectionResults);
|
|
3667
|
+
if (isErr && !transitions.length) {
|
|
3668
|
+
if (currentEvent.type === "xstate.error.actor" && !matchesActorSession(currentEvent, nextSnapshot, currentEvent["actorId"])) return completeMacrostep();
|
|
3669
|
+
nextSnapshot = cloneMachineSnapshot(snapshot, {
|
|
3670
|
+
status: "error",
|
|
3671
|
+
error: currentEvent.error
|
|
3672
|
+
});
|
|
3673
|
+
addMicrostep([nextSnapshot, []], []);
|
|
3674
|
+
removeTerminatedChild(currentEvent);
|
|
3675
|
+
return completeMacrostep();
|
|
3676
|
+
}
|
|
3677
|
+
handled = transitions.length > 0;
|
|
3678
|
+
const step = microstep(transitions, snapshot, actorScope, nextEvent, false, internalQueue, selectionResults);
|
|
3679
|
+
nextSnapshot = step[0];
|
|
3680
|
+
addMicrostep(step, transitions);
|
|
3681
|
+
removeTerminatedChild(currentEvent);
|
|
3682
|
+
}
|
|
3683
|
+
if (!internalQueue.length && snapshot.machine._hasEventlessTransitions === false) return completeMacrostep();
|
|
3684
|
+
let shouldSelectEventlessTransitions = true;
|
|
3685
|
+
const maxIterations = snapshot.machine.options?.maxIterations ?? DEFAULT_MAX_ITERATIONS;
|
|
3686
|
+
let iterationCount = 0;
|
|
3687
|
+
const recentStates = [];
|
|
3688
|
+
while (nextSnapshot.status === "active") {
|
|
3689
|
+
iterationCount++;
|
|
3690
|
+
if (iterationCount > maxIterations) throw new InfiniteTransitionError(actorScope.self?.id, event, maxIterations, recentStates);
|
|
3691
|
+
recentStates.push(nextSnapshot.value);
|
|
3692
|
+
if (recentStates.length > 5) recentStates.shift();
|
|
3693
|
+
let selectionResults;
|
|
3694
|
+
let enabledTransitions = shouldSelectEventlessTransitions ? selectEventlessTransitions(nextSnapshot, nextEvent, actorScope) : [];
|
|
3695
|
+
const previousState = enabledTransitions.length ? nextSnapshot : void 0;
|
|
3696
|
+
const raisedEvents = nextSnapshot.machine._microstepHooks?.drain(actorScope.self);
|
|
3697
|
+
if (raisedEvents?.length) internalQueue.push(...raisedEvents);
|
|
3698
|
+
if (!enabledTransitions.length) {
|
|
3699
|
+
if (!internalQueue.length) break;
|
|
3700
|
+
nextEvent = internalQueue.shift();
|
|
3701
|
+
selectionResults = /* @__PURE__ */ new Map();
|
|
3702
|
+
enabledTransitions = nextSnapshot.machine.getTransitionData(nextSnapshot, nextEvent, actorScope, selectionResults);
|
|
3703
|
+
}
|
|
3704
|
+
const step = microstep(enabledTransitions, nextSnapshot, actorScope, nextEvent, false, internalQueue, selectionResults);
|
|
3705
|
+
nextSnapshot = step[0];
|
|
3706
|
+
shouldSelectEventlessTransitions = nextSnapshot !== previousState;
|
|
3707
|
+
addMicrostep(step, enabledTransitions);
|
|
3708
|
+
removeTerminatedChild(nextEvent);
|
|
3709
|
+
}
|
|
3710
|
+
return completeMacrostep();
|
|
3711
|
+
}
|
|
3712
|
+
/**
|
|
3713
|
+
* Resolves a partial state value with its full representation in the state
|
|
3714
|
+
* node's machine.
|
|
3715
|
+
*
|
|
3716
|
+
* @param stateValue The partial state value to resolve.
|
|
3717
|
+
*/
|
|
3718
|
+
function resolveStateValue(rootNode, stateValue) {
|
|
3719
|
+
return getStateValue(rootNode, getAllStateNodes(getStateNodes(rootNode, stateValue)));
|
|
3720
|
+
}
|
|
3721
|
+
function hasEffect(transition, context, event, snapshot, actorScope) {
|
|
3722
|
+
if (transition.to) return selectTransition(evaluateTransitionOutcome(transition.to, context, event, snapshot, actorScope, snapshot.machine.sources, transition.source.id)).type !== "absent";
|
|
3723
|
+
return false;
|
|
3724
|
+
}
|
|
3725
|
+
let selectionDepth = 0;
|
|
3726
|
+
const triggerTransitionEffect = () => {
|
|
3727
|
+
if (!selectionDepth) return void 0;
|
|
3728
|
+
throw transitionEffectSignal;
|
|
3729
|
+
};
|
|
3730
|
+
/**
|
|
3731
|
+
* Throws when a transition, entry, or exit function returned a promise. The promise's
|
|
3732
|
+
* rejection is observed so it is never reported as unhandled.
|
|
3733
|
+
*/
|
|
3734
|
+
function assertSyncTransitionResult(res, event, sourceId, kind) {
|
|
3735
|
+
if (res && typeof res.then === "function") {
|
|
3736
|
+
Promise.resolve(res).catch(() => {});
|
|
3737
|
+
throw new Error(kind ? `${kind === "entry" ? "Entry" : "Exit"} functions must be synchronous. The ${kind} function of state "${sourceId}" returned a promise (event "${event.type}"). Move async work into an invoked or spawned actor, or enq.effect.` : `Transition functions must be synchronous. Transition for event "${event.type}" in state "${sourceId}" returned a promise. Move async work into an invoked or spawned actor, or enq.effect.`);
|
|
3738
|
+
}
|
|
3739
|
+
}
|
|
3740
|
+
let transitionEffectEnqueue;
|
|
3741
|
+
function getTransitionEffectEnqueue() {
|
|
3742
|
+
return transitionEffectEnqueue ??= createEnqueueObject({
|
|
3743
|
+
emit: triggerTransitionEffect,
|
|
3744
|
+
cancel: triggerTransitionEffect,
|
|
3745
|
+
log: triggerTransitionEffect,
|
|
3746
|
+
raise: triggerTransitionEffect,
|
|
3747
|
+
spawn: triggerTransitionEffect,
|
|
3748
|
+
sendTo: triggerTransitionEffect,
|
|
3749
|
+
stop: triggerTransitionEffect,
|
|
3750
|
+
listen: triggerTransitionEffect,
|
|
3751
|
+
subscribeTo: triggerTransitionEffect
|
|
3752
|
+
}, triggerTransitionEffect);
|
|
3753
|
+
}
|
|
3754
|
+
function evaluateTransitionOutcome(transitionTo, context, event, snapshot, actorScope, sources, sourceId) {
|
|
3755
|
+
let res;
|
|
3756
|
+
const parent = getActorScopeParent(actorScope);
|
|
3757
|
+
if (parent) transitionEffectTargets.push(parent);
|
|
3758
|
+
selectionDepth++;
|
|
3759
|
+
try {
|
|
3760
|
+
res = transitionTo(withActorScope({
|
|
3761
|
+
context,
|
|
3762
|
+
event,
|
|
3763
|
+
output: getEventOutput(event),
|
|
3764
|
+
value: snapshot.value,
|
|
3765
|
+
children: snapshot.children,
|
|
3766
|
+
actions: sources.actions,
|
|
3767
|
+
actors: sources.actors,
|
|
3768
|
+
guards: sources.guards,
|
|
3769
|
+
delays: sources.delays,
|
|
3770
|
+
input: getStateInput(snapshot, sourceId)
|
|
3771
|
+
}, actorScope), getTransitionEffectEnqueue());
|
|
3772
|
+
} catch (err) {
|
|
3773
|
+
if (err === transitionEffectSignal) return { type: "effectEnqueued" };
|
|
3774
|
+
throw err;
|
|
3775
|
+
} finally {
|
|
3776
|
+
selectionDepth--;
|
|
3777
|
+
if (parent) transitionEffectTargets.pop();
|
|
3778
|
+
}
|
|
3779
|
+
assertSyncTransitionResult(res, event, sourceId);
|
|
3780
|
+
return {
|
|
3781
|
+
type: "returned",
|
|
3782
|
+
value: res
|
|
3783
|
+
};
|
|
3784
|
+
}
|
|
3785
|
+
function stopChildren(snapshot, event, actorScope) {
|
|
3786
|
+
let children;
|
|
3787
|
+
if (!snapshot.children || (children = Object.values(snapshot.children).filter(Boolean)).length === 0) return [snapshot, []];
|
|
3788
|
+
const actions = [];
|
|
3789
|
+
const enqueue = createTransitionEnqueue(actorScope, actions, []);
|
|
3790
|
+
for (const child of children) enqueue.stop(child);
|
|
3791
|
+
return resolveActionsWithContext(snapshot, event, actorScope, actions);
|
|
3792
|
+
}
|
|
3793
|
+
function cancelTimers(snapshot, event, actorScope) {
|
|
3794
|
+
const timerIds = Object.keys(snapshot.timers);
|
|
3795
|
+
if (!timerIds.length) return [snapshot, []];
|
|
3796
|
+
const actions = [];
|
|
3797
|
+
const enqueue = createTransitionEnqueue(actorScope, actions, []);
|
|
3798
|
+
for (const id of timerIds) enqueue.cancel(id);
|
|
3799
|
+
return resolveActionsWithContext(snapshot, event, actorScope, actions);
|
|
3800
|
+
}
|
|
3801
|
+
function selectEventlessTransitions(snapshot, event, actorScope) {
|
|
3802
|
+
snapshot.machine._microstepHooks?.begin(actorScope.self);
|
|
3803
|
+
const enabledTransitionSet = /* @__PURE__ */ new Set();
|
|
3804
|
+
const atomicStates = snapshot.nodes.filter(isAtomicStateNode);
|
|
3805
|
+
for (const atomicStateNode of atomicStates) loop: for (let stateNode = atomicStateNode; stateNode; stateNode = stateNode.parent) {
|
|
3806
|
+
if (!stateNode.always) continue;
|
|
3807
|
+
for (const transition of stateNode.always) if (isAdmitted(admitTransitionCandidate(transition, event, snapshot, stateNode, actorScope))) {
|
|
3808
|
+
enabledTransitionSet.add(transition);
|
|
3809
|
+
break loop;
|
|
3810
|
+
}
|
|
3811
|
+
}
|
|
3812
|
+
return removeConflictingTransitions(Array.from(enabledTransitionSet), new Set(snapshot.nodes), snapshot, createTransitionResultResolver(snapshot, event, actorScope, false));
|
|
3813
|
+
}
|
|
3814
|
+
function admitTransitionCandidate(candidate, event, snapshot, stateNode, actorScope, selectionResults) {
|
|
3815
|
+
const transitionTo = candidate.to;
|
|
3816
|
+
const decision = admitCandidate({
|
|
3817
|
+
event,
|
|
3818
|
+
snapshot,
|
|
3819
|
+
matches: candidate.matches,
|
|
3820
|
+
eventMatcher: candidate._eventMatcher,
|
|
3821
|
+
runGuard: candidate.guard ? () => {
|
|
3822
|
+
const guardArgs = withActorSelfAndParent({
|
|
3823
|
+
context: snapshot.context,
|
|
3824
|
+
event,
|
|
3825
|
+
output: getEventOutput(event),
|
|
3826
|
+
children: snapshot.children,
|
|
3827
|
+
actions: stateNode.machine.sources.actions,
|
|
3828
|
+
actors: stateNode.machine.sources.actors,
|
|
3829
|
+
guards: stateNode.machine.sources.guards,
|
|
3830
|
+
delays: stateNode.machine.sources.delays,
|
|
3831
|
+
_snapshot: snapshot
|
|
3832
|
+
}, actorScope);
|
|
3833
|
+
return !!candidate.guard(guardArgs);
|
|
3834
|
+
} : void 0,
|
|
3835
|
+
runTransitionFunction: transitionTo === void 0 ? void 0 : () => evaluateTransitionOutcome(transitionTo, snapshot.context, event, snapshot, actorScope, stateNode.machine.sources, candidate.source.id)
|
|
3836
|
+
});
|
|
3837
|
+
if (decision.type === "enabled" || decision.type === "disabled") selectionResults?.set(candidate, selectionResultOf(decision.selection));
|
|
3838
|
+
return decision;
|
|
3839
|
+
}
|
|
3840
|
+
// src/StateNode.ts
|
|
3841
|
+
const EMPTY_OBJECT = {};
|
|
3842
|
+
/** @public */
|
|
3843
|
+
var StateNode = class StateNode {
|
|
3844
|
+
config;
|
|
3845
|
+
/**
|
|
3846
|
+
* The relative key of the state node, which represents its location in the
|
|
3847
|
+
* overall state value.
|
|
3848
|
+
*/
|
|
3849
|
+
key;
|
|
3850
|
+
/** The unique ID of the state node. */
|
|
3851
|
+
id;
|
|
3852
|
+
/**
|
|
3853
|
+
* The type of this state node:
|
|
3854
|
+
*
|
|
3855
|
+
* - `'atomic'` - no child state nodes
|
|
3856
|
+
* - `'compound'` - nested child state nodes (XOR)
|
|
3857
|
+
* - `'parallel'` - orthogonal nested child state nodes (AND)
|
|
3858
|
+
* - `'history'` - history state node
|
|
3859
|
+
* - `'choice'` - pure routing decision state node
|
|
3860
|
+
* - `'final'` - final state node
|
|
3861
|
+
*/
|
|
3862
|
+
type;
|
|
3863
|
+
/** The string path from the root machine node to this node. */
|
|
3864
|
+
path;
|
|
3865
|
+
/** The child state nodes. */
|
|
3866
|
+
states;
|
|
3867
|
+
/**
|
|
3868
|
+
* The type of history on this state node. Can be:
|
|
3869
|
+
*
|
|
3870
|
+
* - `'shallow'` - recalls only top-level historical state value
|
|
3871
|
+
* - `'deep'` - recalls historical state value at all levels
|
|
3872
|
+
*/
|
|
3873
|
+
history;
|
|
3874
|
+
/** The action(s) to be executed upon entering the state node. */
|
|
3875
|
+
entry;
|
|
3876
|
+
/** The action(s) to be executed upon exiting the state node. */
|
|
3877
|
+
exit;
|
|
3878
|
+
/** The parent state node. */
|
|
3879
|
+
parent;
|
|
3880
|
+
/** The root machine node. */
|
|
3881
|
+
machine;
|
|
3882
|
+
/**
|
|
3883
|
+
* The meta data associated with this state node, which will be returned in
|
|
3884
|
+
* State instances.
|
|
3885
|
+
*/
|
|
3886
|
+
meta;
|
|
3887
|
+
/**
|
|
3888
|
+
* The output data sent with the `xstate.done.state` event if this is a final
|
|
3889
|
+
* state node.
|
|
3890
|
+
*/
|
|
3891
|
+
output;
|
|
3892
|
+
/**
|
|
3893
|
+
* The order this state node appears. Corresponds to the implicit document
|
|
3894
|
+
* order.
|
|
3895
|
+
*/
|
|
3896
|
+
order = -1;
|
|
3897
|
+
description;
|
|
3898
|
+
schemas;
|
|
3899
|
+
tags = [];
|
|
3900
|
+
/** @internal */
|
|
3901
|
+
_finalStateWarnings;
|
|
3902
|
+
transitions;
|
|
3903
|
+
always;
|
|
3904
|
+
invoke;
|
|
3905
|
+
on;
|
|
3906
|
+
after;
|
|
3907
|
+
events;
|
|
3908
|
+
ownEvents;
|
|
3909
|
+
_candidateCache;
|
|
3910
|
+
constructor(config, options) {
|
|
3911
|
+
this.config = config;
|
|
3912
|
+
this.parent = options._parent;
|
|
3913
|
+
this.key = options._key;
|
|
3914
|
+
this.machine = options._machine;
|
|
3915
|
+
this._finalStateWarnings = options._parent?._finalStateWarnings ?? [];
|
|
3916
|
+
this.path = this.parent ? this.parent.path.concat(this.key) : [];
|
|
3917
|
+
const firstStateKey = this.config.states ? Object.keys(this.config.states)[0] : void 0;
|
|
3918
|
+
this.id = this.config.id || [this.machine.id, ...this.path].join(".");
|
|
3919
|
+
this.type = this.config.type || (firstStateKey !== void 0 ? "compound" : this.config.history ? "history" : "atomic");
|
|
3920
|
+
this.description = this.config.description;
|
|
3921
|
+
this.schemas = this.config.schemas;
|
|
3922
|
+
validateStateNodeConfig(this);
|
|
3923
|
+
this.order = this.machine.idMap.size;
|
|
3924
|
+
this.machine.idMap.set(this.id, this);
|
|
3925
|
+
this.states = this.config.states ? mapValues(this.config.states, (stateConfig, key) => {
|
|
3926
|
+
return new StateNode(stateConfig, {
|
|
3927
|
+
_parent: this,
|
|
3928
|
+
_key: key,
|
|
3929
|
+
_machine: this.machine
|
|
3930
|
+
});
|
|
3931
|
+
}) : EMPTY_OBJECT;
|
|
3932
|
+
if (this.type === "compound" && !this.config.initial) throw new Error(`No initial state specified for compound state node "#${this.id}".`);
|
|
3933
|
+
this.history = this.config.history === true ? "shallow" : this.config.history || false;
|
|
3934
|
+
this.entry = this.config.entry;
|
|
3935
|
+
this.exit = this.config.exit;
|
|
3936
|
+
if (this.entry) this.entry._special = true;
|
|
3937
|
+
if (this.exit) this.exit._special = true;
|
|
3938
|
+
this.meta = this.config.meta;
|
|
3939
|
+
this.output = this.type === "final" || !this.parent ? this.config.output : void 0;
|
|
3940
|
+
this.tags = toArray(config.tags).slice();
|
|
3941
|
+
this.invoke = toArray(this.config.invoke).map((invokeConfig, i) => {
|
|
3942
|
+
const { src, registryKey } = invokeConfig;
|
|
3943
|
+
const invokeId = createInvokeId(this.id, i);
|
|
3944
|
+
const resolvedId = invokeConfig.id ?? invokeId;
|
|
3945
|
+
const sourceName = typeof src === "string" ? src : `xstate.invoke.${invokeId}`;
|
|
3946
|
+
return {
|
|
3947
|
+
...invokeConfig,
|
|
3948
|
+
src: sourceName,
|
|
3949
|
+
logic: src,
|
|
3950
|
+
id: resolvedId,
|
|
3951
|
+
registryKey
|
|
3952
|
+
};
|
|
3953
|
+
});
|
|
3954
|
+
}
|
|
3955
|
+
/** @internal */
|
|
3956
|
+
_initialize() {
|
|
3957
|
+
this.after = getDelayedTransitions(this);
|
|
3958
|
+
this.transitions = formatTransitions(this);
|
|
3959
|
+
if (this.type === "choice") this.always = formatChoiceTransitions(this);
|
|
3960
|
+
else if (this.config.always) this.always = mapTransitionConfigs(this.config.always, (transition) => formatTransition(this, "", transition));
|
|
3961
|
+
for (const childState of Object.values(this.states)) childState._initialize();
|
|
3962
|
+
this._refreshEventMetadata();
|
|
3963
|
+
}
|
|
3964
|
+
/** @internal */
|
|
3965
|
+
_refreshEventMetadata() {
|
|
3966
|
+
const on = {};
|
|
3967
|
+
const ownEvents = [];
|
|
3968
|
+
for (const [descriptor, transitions] of this.transitions) {
|
|
3969
|
+
on[descriptor] = transitions.slice();
|
|
3970
|
+
if (transitions.some((transition) => transition.target || transition.reenter || transition.to)) ownEvents.push(descriptor);
|
|
3971
|
+
}
|
|
3972
|
+
this.on = on;
|
|
3973
|
+
this.ownEvents = ownEvents;
|
|
3974
|
+
const events = new Set(ownEvents);
|
|
3975
|
+
for (const state of Object.values(this.states)) for (const event of state.events) events.add(event);
|
|
3976
|
+
this.events = Array.from(events);
|
|
3977
|
+
}
|
|
3978
|
+
get initial() {
|
|
3979
|
+
return memo(this, "initial", () => formatInitialTransition(this, this.config.initial));
|
|
3980
|
+
}
|
|
3981
|
+
/** @internal */
|
|
3982
|
+
next(snapshot, event, actorScope, selectionResults) {
|
|
3983
|
+
if (this.type === "final") return;
|
|
3984
|
+
const descriptorKey = getEventDescriptorKey(event);
|
|
3985
|
+
let candidates = this._candidateCache?.get(descriptorKey);
|
|
3986
|
+
if (!candidates) {
|
|
3987
|
+
candidates = getCandidates(this, event, (message) => actorScope.warn(message));
|
|
3988
|
+
(this._candidateCache ??= /* @__PURE__ */ new Map()).set(descriptorKey, candidates);
|
|
3989
|
+
}
|
|
3990
|
+
const admitted = firstAdmittedCandidate({
|
|
3991
|
+
candidates,
|
|
3992
|
+
admit: (candidate) => admitTransitionCandidate(candidate, event, snapshot, this, actorScope, selectionResults)
|
|
3993
|
+
});
|
|
3994
|
+
return admitted === void 0 ? void 0 : [admitted.candidate];
|
|
3995
|
+
}
|
|
3996
|
+
};
|
|
3997
|
+
function validateStateNodeConfig(stateNode) {
|
|
3998
|
+
const config = stateNode.config;
|
|
3999
|
+
if (stateNode.type === "history" && !(typeof config.target === "string" && config.target.trim().length > 0 || Array.isArray(config.target) && config.target.length > 0 && config.target.every((target) => typeof target === "string" && target.trim().length > 0))) throw new Error(`Missing history target on "${stateNode.id}"`);
|
|
4000
|
+
if (stateNode.type !== "choice") return;
|
|
4001
|
+
if (typeof config.choice !== "function") throw new Error(`Missing \`choice\` function on "${stateNode.id}"`);
|
|
4002
|
+
}
|
|
4003
|
+
function formatChoiceTransitions(stateNode) {
|
|
4004
|
+
const choice = stateNode.config.choice;
|
|
4005
|
+
const validateChoiceResult = (result) => {
|
|
4006
|
+
if (!result || result.target === void 0) throw new Error(`Choice "${stateNode.id}" has no target`);
|
|
4007
|
+
return result;
|
|
4008
|
+
};
|
|
4009
|
+
return [formatTransition(stateNode, "", { to: (args) => validateChoiceResult(choice(args)) })];
|
|
4010
|
+
}
|
|
4011
|
+
function mapTransitionConfigs(transitionsConfig, mapper) {
|
|
4012
|
+
return toTransitionConfigArray(transitionsConfig).map(mapper);
|
|
4013
|
+
}
|
|
4014
|
+
function formatTransitions(stateNode) {
|
|
4015
|
+
const transitions = /* @__PURE__ */ new Map();
|
|
4016
|
+
const addTransitions = (descriptor, additions) => {
|
|
4017
|
+
transitions.set(descriptor, [...transitions.get(descriptor) ?? [], ...additions]);
|
|
4018
|
+
};
|
|
4019
|
+
if (stateNode.config.on) for (const descriptor of Object.keys(stateNode.config.on)) {
|
|
4020
|
+
if (descriptor === "") throw new Error("Null event transition key");
|
|
4021
|
+
const transitionsConfig = stateNode.config.on[descriptor];
|
|
4022
|
+
transitions.set(descriptor, mapTransitionConfigs(transitionsConfig, (transition) => formatTransition(stateNode, descriptor, transition)));
|
|
4023
|
+
}
|
|
4024
|
+
if (stateNode.config.onDone) {
|
|
4025
|
+
const descriptor = "xstate.done.state";
|
|
4026
|
+
addTransitions(descriptor, mapTransitionConfigs(stateNode.config.onDone, (transition) => formatTransition(stateNode, descriptor, {
|
|
4027
|
+
...transition,
|
|
4028
|
+
matches: {
|
|
4029
|
+
...transition.matches,
|
|
4030
|
+
stateId: stateNode.id
|
|
4031
|
+
}
|
|
4032
|
+
})));
|
|
4033
|
+
}
|
|
4034
|
+
if (stateNode.config.onError) {
|
|
4035
|
+
const descriptor = "xstate.error.*";
|
|
4036
|
+
transitions.set(descriptor, mapTransitionConfigs(stateNode.config.onError, (transition) => formatTransition(stateNode, descriptor, transition)));
|
|
4037
|
+
}
|
|
4038
|
+
const createCancelInvokeTimeoutTransition = (descriptor, timeoutEventId, actorId) => formatTransition(stateNode, descriptor, {
|
|
4039
|
+
matches: { actorId },
|
|
4040
|
+
_eventMatcher: (event, snapshot) => matchesActorSession(event, snapshot, actorId),
|
|
4041
|
+
to: (_args, enq) => {
|
|
4042
|
+
enq.cancel(timeoutEventId);
|
|
4043
|
+
return {};
|
|
4044
|
+
}
|
|
4045
|
+
});
|
|
4046
|
+
const formatInvokeCompletionTransition = (descriptor, transitionConfig, timeoutEventId) => {
|
|
4047
|
+
const { target, to, reenter, ...rest } = transitionConfig;
|
|
4048
|
+
return formatTransition(stateNode, descriptor, {
|
|
4049
|
+
...rest,
|
|
4050
|
+
reenter,
|
|
4051
|
+
to: (args, enq) => {
|
|
4052
|
+
if (to) {
|
|
4053
|
+
let didEnqueue = false;
|
|
4054
|
+
const trackingEnqueue = new Proxy(enq, {
|
|
4055
|
+
apply(target, thisArg, argArray) {
|
|
4056
|
+
didEnqueue = true;
|
|
4057
|
+
return Reflect.apply(target, thisArg, argArray);
|
|
4058
|
+
},
|
|
4059
|
+
get(target, prop, receiver) {
|
|
4060
|
+
const value = Reflect.get(target, prop, receiver);
|
|
4061
|
+
if (typeof value !== "function") return value;
|
|
4062
|
+
return (...argArray) => {
|
|
4063
|
+
didEnqueue = true;
|
|
4064
|
+
return value.apply(target, argArray);
|
|
4065
|
+
};
|
|
4066
|
+
}
|
|
4067
|
+
});
|
|
4068
|
+
const result = to(args, trackingEnqueue);
|
|
4069
|
+
if (result !== void 0 || didEnqueue) enq.cancel(timeoutEventId);
|
|
4070
|
+
return result;
|
|
4071
|
+
}
|
|
4072
|
+
enq.cancel(timeoutEventId);
|
|
4073
|
+
return {
|
|
4074
|
+
target,
|
|
4075
|
+
reenter
|
|
4076
|
+
};
|
|
4077
|
+
}
|
|
4078
|
+
});
|
|
4079
|
+
};
|
|
4080
|
+
for (const invokeDef of stateNode.invoke) {
|
|
4081
|
+
const withInvokeMatch = (transition) => ({
|
|
4082
|
+
...transition,
|
|
4083
|
+
matches: {
|
|
4084
|
+
...transition.matches,
|
|
4085
|
+
actorId: invokeDef.id
|
|
4086
|
+
},
|
|
4087
|
+
_eventMatcher: (event, snapshot) => matchesActorSession(event, snapshot, invokeDef.id)
|
|
4088
|
+
});
|
|
4089
|
+
const invokeTimeoutEventId = invokeDef.timeout !== void 0 ? createInvokeTimeoutEventId(invokeDef.id) : void 0;
|
|
4090
|
+
if (invokeDef.onDone) {
|
|
4091
|
+
const descriptor = "xstate.done.actor";
|
|
4092
|
+
const invokeDoneTransitions = mapTransitionConfigs(invokeDef.onDone, (rawTransition) => {
|
|
4093
|
+
const transition = withInvokeMatch(rawTransition);
|
|
4094
|
+
return invokeTimeoutEventId ? formatInvokeCompletionTransition(descriptor, transition, invokeTimeoutEventId) : formatTransition(stateNode, descriptor, transition);
|
|
4095
|
+
});
|
|
4096
|
+
if (invokeTimeoutEventId) invokeDoneTransitions.push(createCancelInvokeTimeoutTransition(descriptor, invokeTimeoutEventId, invokeDef.id));
|
|
4097
|
+
addTransitions(descriptor, invokeDoneTransitions);
|
|
4098
|
+
} else if (invokeTimeoutEventId) {
|
|
4099
|
+
const descriptor = "xstate.done.actor";
|
|
4100
|
+
addTransitions(descriptor, [createCancelInvokeTimeoutTransition(descriptor, invokeTimeoutEventId, invokeDef.id)]);
|
|
4101
|
+
}
|
|
4102
|
+
if (invokeDef.onError) {
|
|
4103
|
+
const descriptor = "xstate.error.actor";
|
|
4104
|
+
addTransitions(descriptor, mapTransitionConfigs(invokeDef.onError, (rawTransition) => {
|
|
4105
|
+
const transition = withInvokeMatch(rawTransition);
|
|
4106
|
+
return invokeTimeoutEventId ? formatInvokeCompletionTransition(descriptor, transition, invokeTimeoutEventId) : formatTransition(stateNode, descriptor, transition);
|
|
4107
|
+
}));
|
|
4108
|
+
}
|
|
4109
|
+
if (invokeDef.onSnapshot) {
|
|
4110
|
+
const descriptor = "xstate.snapshot.actor";
|
|
4111
|
+
addTransitions(descriptor, mapTransitionConfigs(invokeDef.onSnapshot, (transition) => formatTransition(stateNode, descriptor, withInvokeMatch(transition))));
|
|
4112
|
+
}
|
|
4113
|
+
}
|
|
4114
|
+
for (const delayedTransition of stateNode.after) {
|
|
4115
|
+
let existing = transitions.get(delayedTransition.eventType);
|
|
4116
|
+
if (!existing) {
|
|
4117
|
+
existing = [];
|
|
4118
|
+
transitions.set(delayedTransition.eventType, existing);
|
|
4119
|
+
}
|
|
4120
|
+
existing.push(delayedTransition);
|
|
4121
|
+
}
|
|
4122
|
+
return transitions;
|
|
4123
|
+
}
|
|
4124
|
+
function formatInitialTransition(stateNode, _target) {
|
|
4125
|
+
const targetString = typeof _target === "object" && _target !== null ? _target.target : _target;
|
|
4126
|
+
const input = typeof _target === "object" && _target !== null ? _target.input : void 0;
|
|
4127
|
+
const to = typeof _target === "object" && _target !== null ? _target.to : void 0;
|
|
4128
|
+
const meta = typeof _target === "object" && _target !== null ? _target.meta : void 0;
|
|
4129
|
+
const description = typeof _target === "object" && _target !== null ? _target.description : void 0;
|
|
4130
|
+
const resolvedTargets = (Array.isArray(targetString) ? targetString : targetString ? [targetString] : []).map((target) => target.startsWith("#") ? stateNode.machine.getStateNodeById(target.slice(1)) : stateNode.states[target]);
|
|
4131
|
+
if (resolvedTargets.some((target) => !target)) throw new Error(`Initial state not found on "#${stateNode.id}"`);
|
|
4132
|
+
return {
|
|
4133
|
+
source: stateNode,
|
|
4134
|
+
target: resolvedTargets.length ? resolvedTargets : void 0,
|
|
4135
|
+
input,
|
|
4136
|
+
to,
|
|
4137
|
+
meta,
|
|
4138
|
+
description
|
|
4139
|
+
};
|
|
4140
|
+
}
|
|
4141
|
+
// src/createActor.ts
|
|
4142
|
+
let executingCustomAction = false;
|
|
4143
|
+
/**
|
|
4144
|
+
* Marks a serialized object as an actor reference (`xstate$type` in JSON
|
|
4145
|
+
* output and persisted context).
|
|
4146
|
+
*/
|
|
4147
|
+
const ACTOR_REF_TYPE = "actorRef";
|
|
4148
|
+
const emptyInspectionRecords = Object.freeze([]);
|
|
4149
|
+
const defaultOptions = Object.freeze({
|
|
4150
|
+
clock: {
|
|
4151
|
+
setTimeout: (fn, ms) => {
|
|
4152
|
+
return setTimeout(fn, ms);
|
|
4153
|
+
},
|
|
4154
|
+
clearTimeout: (id) => {
|
|
4155
|
+
return clearTimeout(id);
|
|
4156
|
+
}
|
|
4157
|
+
},
|
|
4158
|
+
logger: console.log.bind(console)
|
|
4159
|
+
});
|
|
4160
|
+
function safeCall(fn, arg, report) {
|
|
4161
|
+
try {
|
|
4162
|
+
fn?.(arg);
|
|
4163
|
+
} catch (err) {
|
|
4164
|
+
report(err);
|
|
4165
|
+
}
|
|
4166
|
+
}
|
|
4167
|
+
function executeExecutableEffects(effects, actorScope) {
|
|
4168
|
+
if (!effects?.length) return;
|
|
4169
|
+
for (const effect of effects) actorScope.actionExecutor(effect);
|
|
4170
|
+
}
|
|
4171
|
+
function createActorRef(logic, options) {
|
|
4172
|
+
return new Actor(logic, options);
|
|
4173
|
+
}
|
|
4174
|
+
/**
|
|
4175
|
+
* An Actor is a running process that can receive events, send events and change
|
|
4176
|
+
* its behavior based on the events it receives, which can cause effects outside
|
|
4177
|
+
* of the actor. When you run a state machine, it becomes an actor.
|
|
4178
|
+
*
|
|
4179
|
+
* An `Actor` is the concrete runtime instance with lifecycle methods and
|
|
4180
|
+
* system-owned internals. It also satisfies the narrower `ActorRef` contract,
|
|
4181
|
+
* so consumer APIs should accept `ActorRef` when they only need to send events
|
|
4182
|
+
* or read snapshots.
|
|
4183
|
+
*
|
|
4184
|
+
* @public
|
|
4185
|
+
*/
|
|
4186
|
+
var Actor = class {
|
|
4187
|
+
logic;
|
|
4188
|
+
/** The current internal state of the actor. */
|
|
4189
|
+
_snapshot;
|
|
4190
|
+
/**
|
|
4191
|
+
* The clock that is responsible for setting and clearing timeouts, such as
|
|
4192
|
+
* delayed events and transitions.
|
|
4193
|
+
*/
|
|
4194
|
+
clock;
|
|
4195
|
+
wallClock;
|
|
4196
|
+
options;
|
|
4197
|
+
/** The unique identifier for this actor relative to its parent. */
|
|
4198
|
+
id;
|
|
4199
|
+
_boundProcess;
|
|
4200
|
+
mailbox;
|
|
4201
|
+
_mailboxStarted = false;
|
|
4202
|
+
observers;
|
|
4203
|
+
/** Whether a consumer subscribed to this actor's error after it errored. */
|
|
4204
|
+
_errorObserved = false;
|
|
4205
|
+
eventListeners;
|
|
4206
|
+
logger;
|
|
4207
|
+
warn;
|
|
4208
|
+
/** @internal */
|
|
4209
|
+
_processingStatus = 0;
|
|
4210
|
+
_forceDeferredActions = false;
|
|
4211
|
+
_parent;
|
|
4212
|
+
/** @internal */
|
|
4213
|
+
_syncSnapshot;
|
|
4214
|
+
ref;
|
|
4215
|
+
_actorScope;
|
|
4216
|
+
/** @internal */
|
|
4217
|
+
_lastSourceRef;
|
|
4218
|
+
/** @internal */
|
|
4219
|
+
_collectedMicrosteps;
|
|
4220
|
+
/** @internal Actions executed during the in-flight transition. */
|
|
4221
|
+
_collectedActions;
|
|
4222
|
+
/** @internal Events relayed to other actors during the in-flight transition. */
|
|
4223
|
+
_collectedSent;
|
|
4224
|
+
_initialEffects;
|
|
4225
|
+
registryKey;
|
|
4226
|
+
/** The globally unique process ID for this invocation. */
|
|
4227
|
+
sessionId;
|
|
4228
|
+
_address;
|
|
4229
|
+
/**
|
|
4230
|
+
* The deterministic logical address of this actor within its system: the
|
|
4231
|
+
* `/`-joined path of actor ids from the root. Stable across persistence and
|
|
4232
|
+
* restore, unlike `sessionId`, which identifies one incarnation.
|
|
4233
|
+
*
|
|
4234
|
+
* `/` separates segments, so an id containing one (a state name with a
|
|
4235
|
+
* slash reaches its invoked child's id) is percent-encoded to keep the
|
|
4236
|
+
* path unambiguous.
|
|
4237
|
+
*/
|
|
4238
|
+
get address() {
|
|
4239
|
+
return this._address ??= this._parent ? `${this._parent.address}/${encodeAddressSegment(this.id)}` : encodeAddressSegment(this.id);
|
|
4240
|
+
}
|
|
4241
|
+
/** The system to which this actor belongs. */
|
|
4242
|
+
system;
|
|
4243
|
+
_trigger;
|
|
4244
|
+
get trigger() {
|
|
4245
|
+
return this._trigger ??= new Proxy({}, { get: (_, eventType) => {
|
|
4246
|
+
return (payload) => {
|
|
4247
|
+
this.send({
|
|
4248
|
+
...payload,
|
|
4249
|
+
type: eventType
|
|
4250
|
+
});
|
|
4251
|
+
};
|
|
4252
|
+
} });
|
|
4253
|
+
}
|
|
4254
|
+
_boundSend;
|
|
4255
|
+
get send() {
|
|
4256
|
+
return this._boundSend ??= this._sendPublic.bind(this);
|
|
4257
|
+
}
|
|
4258
|
+
src;
|
|
4259
|
+
/**
|
|
4260
|
+
* Creates a new actor instance for the given logic with the provided options,
|
|
4261
|
+
* if any.
|
|
4262
|
+
*
|
|
4263
|
+
* @param logic The logic to create an actor from
|
|
4264
|
+
* @param options Actor options
|
|
4265
|
+
*/
|
|
4266
|
+
constructor(logic, options) {
|
|
4267
|
+
this.logic = logic;
|
|
4268
|
+
const resolvedOptions = options ? {
|
|
4269
|
+
...defaultOptions,
|
|
4270
|
+
...options
|
|
4271
|
+
} : defaultOptions;
|
|
4272
|
+
const { clock, wallClock, logger, warn, parent, syncSnapshot, id, registryKey, inspect } = resolvedOptions;
|
|
4273
|
+
this.system = parent ? parent.system : resolvedOptions._systemRef?.current ?? createRuntimeSystem(this, {
|
|
4274
|
+
clock,
|
|
4275
|
+
wallClock,
|
|
4276
|
+
crypto: resolvedOptions.crypto,
|
|
4277
|
+
logger,
|
|
4278
|
+
reportUnhandledError: resolvedOptions.reportUnhandledError,
|
|
4279
|
+
warn: warn ?? defaultWarn,
|
|
4280
|
+
snapshot: resolvedOptions.snapshot,
|
|
4281
|
+
createActorRef
|
|
4282
|
+
});
|
|
4283
|
+
if (!parent && resolvedOptions._systemRef && !resolvedOptions._systemRef.current) resolvedOptions._systemRef.current = this.system;
|
|
4284
|
+
if (inspect && !parent) this.system.inspect(toObserver(inspect));
|
|
4285
|
+
if (resolvedOptions.onRejectedEvent && !parent) this.system.onRejectedEvent(resolvedOptions.onRejectedEvent);
|
|
4286
|
+
this.sessionId = resolvedOptions._sessionId ?? bookSessionId(this.system);
|
|
4287
|
+
this.id = resolveActorId(this.system, id, {
|
|
4288
|
+
parent,
|
|
4289
|
+
src: resolvedOptions.src ?? logic
|
|
4290
|
+
});
|
|
4291
|
+
this.logger = options?.logger ?? this.system._logger;
|
|
4292
|
+
this.warn = options?.warn ?? this.system._warn;
|
|
4293
|
+
this.clock = options?.clock ?? this.system._clock;
|
|
4294
|
+
this.wallClock = options?.wallClock ?? this.system._wallClock;
|
|
4295
|
+
this._parent = parent;
|
|
4296
|
+
this._syncSnapshot = syncSnapshot;
|
|
4297
|
+
this.options = resolvedOptions;
|
|
4298
|
+
this.src = resolvedOptions.src ?? logic;
|
|
4299
|
+
this.ref = this;
|
|
4300
|
+
this._actorScope = this;
|
|
4301
|
+
if (registryKey) {
|
|
4302
|
+
this.registryKey = registryKey;
|
|
4303
|
+
this.system._set(registryKey, this);
|
|
4304
|
+
}
|
|
4305
|
+
this._collectedMicrosteps = void 0;
|
|
4306
|
+
const persistedState = options?.snapshot;
|
|
4307
|
+
this._restored = persistedState !== void 0;
|
|
4308
|
+
try {
|
|
4309
|
+
if (persistedState) this._setSnapshot(this.logic.restoreSnapshot ? this.logic.restoreSnapshot(persistedState, this._actorScope) : persistedState);
|
|
4310
|
+
else if (options?._inert) {} else {
|
|
4311
|
+
const [snapshot, effects] = finalizeTransitionResult(this._actorScope, void 0, this.logic.initialTransition(this.options?.input, this._actorScope));
|
|
4312
|
+
this._setSnapshot(snapshot);
|
|
4313
|
+
this._initialEffects = effects.length ? effects : void 0;
|
|
4314
|
+
}
|
|
4315
|
+
} catch (err) {
|
|
4316
|
+
const restoreErrorSnapshot = persistedState && this.logic._createRestoreErrorSnapshot?.(persistedState, err);
|
|
4317
|
+
this._setSnapshot(restoreErrorSnapshot ?? {
|
|
4318
|
+
status: "error",
|
|
4319
|
+
output: void 0,
|
|
4320
|
+
error: err
|
|
4321
|
+
});
|
|
4322
|
+
if (this._deferred) this._deferred.length = 0;
|
|
4323
|
+
}
|
|
4324
|
+
if (registryKey && this._snapshot.status !== "active") this.system._unregister(this);
|
|
4325
|
+
if (this.system._hasInspectionObservers?.() ?? true) this.system._sendInspectionEvent({
|
|
4326
|
+
type: "@xstate.actor",
|
|
4327
|
+
actorRef: this,
|
|
4328
|
+
parentRef: this._parent,
|
|
4329
|
+
id: this.id,
|
|
4330
|
+
src: this.src,
|
|
4331
|
+
snapshot: this._snapshot
|
|
4332
|
+
});
|
|
4333
|
+
}
|
|
4334
|
+
_deferred;
|
|
4335
|
+
_restored = false;
|
|
4336
|
+
/** @internal */
|
|
4337
|
+
_getLogger() {
|
|
4338
|
+
return this.logger;
|
|
4339
|
+
}
|
|
4340
|
+
get self() {
|
|
4341
|
+
return this;
|
|
4342
|
+
}
|
|
4343
|
+
get defer() {
|
|
4344
|
+
const defer = (fn) => this._defer(fn);
|
|
4345
|
+
Object.defineProperty(this, "defer", { value: defer });
|
|
4346
|
+
return defer;
|
|
4347
|
+
}
|
|
4348
|
+
get stopChild() {
|
|
4349
|
+
const stopChild = (child) => this._stopChild(child);
|
|
4350
|
+
Object.defineProperty(this, "stopChild", { value: stopChild });
|
|
4351
|
+
return stopChild;
|
|
4352
|
+
}
|
|
4353
|
+
get emit() {
|
|
4354
|
+
const emit = (event) => this.system.emitEvent(this, event);
|
|
4355
|
+
Object.defineProperty(this, "emit", { value: emit });
|
|
4356
|
+
return emit;
|
|
4357
|
+
}
|
|
4358
|
+
get actionExecutor() {
|
|
4359
|
+
const actionExecutor = (action) => this._executeAction(action);
|
|
4360
|
+
Object.defineProperty(this, "actionExecutor", { value: actionExecutor });
|
|
4361
|
+
return actionExecutor;
|
|
4362
|
+
}
|
|
4363
|
+
/** @internal */
|
|
4364
|
+
_isRunning() {
|
|
4365
|
+
return this._processingStatus === 1;
|
|
4366
|
+
}
|
|
4367
|
+
/** @internal */
|
|
4368
|
+
_defer(fn) {
|
|
4369
|
+
(this._deferred ??= []).push(fn);
|
|
4370
|
+
}
|
|
4371
|
+
/** @internal */
|
|
4372
|
+
_stopChild(child) {
|
|
4373
|
+
if (child._parent !== this) throw new Error(`Cannot stop non-child actor ${child.id}`);
|
|
4374
|
+
child._stop();
|
|
4375
|
+
}
|
|
4376
|
+
/** @internal */
|
|
4377
|
+
_executeAction(action) {
|
|
4378
|
+
const exec = () => {
|
|
4379
|
+
if (this.system._hasInspectionObservers?.() ?? true) (this._collectedActions ??= []).push({
|
|
4380
|
+
type: action.type,
|
|
4381
|
+
params: action.params
|
|
4382
|
+
});
|
|
4383
|
+
const saveExecutingCustomAction = executingCustomAction;
|
|
4384
|
+
try {
|
|
4385
|
+
executingCustomAction = true;
|
|
4386
|
+
const result = action.exec();
|
|
4387
|
+
if (result && typeof result.then === "function") Promise.resolve(result).catch((err) => {
|
|
4388
|
+
if (this._processingStatus === 2) return;
|
|
4389
|
+
this._recoverOrError(err);
|
|
4390
|
+
});
|
|
4391
|
+
} finally {
|
|
4392
|
+
executingCustomAction = saveExecutingCustomAction;
|
|
4393
|
+
}
|
|
4394
|
+
};
|
|
4395
|
+
if (this._processingStatus === 1 && !this._forceDeferredActions) exec();
|
|
4396
|
+
else (this._deferred ??= []).push(exec);
|
|
4397
|
+
}
|
|
4398
|
+
/** Associates each live snapshot with this actor for later pure transitions. */
|
|
4399
|
+
_setSnapshot(snapshot) {
|
|
4400
|
+
const previousSnapshot = this._snapshot;
|
|
4401
|
+
this._snapshot = snapshot;
|
|
4402
|
+
setSnapshotActorRef(snapshot, this, this.system, previousSnapshot);
|
|
4403
|
+
}
|
|
4404
|
+
_setErrorSnapshot(err, snapshot = this._snapshot) {
|
|
4405
|
+
this._setSnapshot({
|
|
4406
|
+
...snapshot,
|
|
4407
|
+
status: "error",
|
|
4408
|
+
error: err
|
|
4409
|
+
});
|
|
4410
|
+
}
|
|
4411
|
+
/** Recover via the logic's error event if possible; otherwise error out. */
|
|
4412
|
+
_recoverOrError(err, snapshot, previousSnapshot) {
|
|
4413
|
+
if (this._tryHandleExecutionError(err, snapshot)) return true;
|
|
4414
|
+
this._setErrorSnapshot(err);
|
|
4415
|
+
this._error(err, previousSnapshot);
|
|
4416
|
+
return false;
|
|
4417
|
+
}
|
|
4418
|
+
_tryHandleExecutionError(err, snapshot = this._snapshot) {
|
|
4419
|
+
const errorEvent = this.logic.getExecutionErrorEvent?.(snapshot, err);
|
|
4420
|
+
if (!errorEvent) return false;
|
|
4421
|
+
try {
|
|
4422
|
+
const [nextSnapshot, effects] = finalizeTransitionResult(this._actorScope, snapshot, this.logic.transition(snapshot, errorEvent, this._actorScope));
|
|
4423
|
+
this._setSnapshot(nextSnapshot);
|
|
4424
|
+
executeExecutableEffects(effects, this._actorScope);
|
|
4425
|
+
this.update(nextSnapshot, errorEvent);
|
|
4426
|
+
return true;
|
|
4427
|
+
} catch {
|
|
4428
|
+
return false;
|
|
4429
|
+
}
|
|
4430
|
+
}
|
|
4431
|
+
_next(snapshot) {
|
|
4432
|
+
for (const observer of this.observers ?? emptyInspectionRecords) safeCall(observer.next, snapshot, this.system._reportUnhandledError);
|
|
4433
|
+
}
|
|
4434
|
+
update(snapshot, event) {
|
|
4435
|
+
this._setSnapshot(snapshot);
|
|
4436
|
+
const deferred = this._deferred;
|
|
4437
|
+
if (deferred) {
|
|
4438
|
+
for (const deferredFn of deferred) try {
|
|
4439
|
+
deferredFn();
|
|
4440
|
+
} catch (err) {
|
|
4441
|
+
deferred.length = 0;
|
|
4442
|
+
if (this._tryHandleExecutionError(err, snapshot)) return;
|
|
4443
|
+
this._setErrorSnapshot(err, snapshot);
|
|
4444
|
+
this._error(err);
|
|
4445
|
+
break;
|
|
4446
|
+
}
|
|
4447
|
+
deferred.length = 0;
|
|
4448
|
+
}
|
|
4449
|
+
switch (this._snapshot.status) {
|
|
4450
|
+
case "active": this._next(snapshot);
|
|
4451
|
+
}
|
|
4452
|
+
this._inspectTransition(this._snapshot, event);
|
|
4453
|
+
}
|
|
4454
|
+
/** @internal Emits the transition inspection event; used by the pure
|
|
4455
|
+
* `transition()` path for snapshots driven outside a live actor. */
|
|
4456
|
+
_inspectTransition(snapshot, event) {
|
|
4457
|
+
if (this.system._hasInspectionObservers?.() ?? true) this.system._sendInspectionEvent({
|
|
4458
|
+
type: "@xstate.transition",
|
|
4459
|
+
actorRef: this,
|
|
4460
|
+
event,
|
|
4461
|
+
sourceRef: this._lastSourceRef,
|
|
4462
|
+
targetRef: this,
|
|
4463
|
+
snapshot,
|
|
4464
|
+
microsteps: this._collectedMicrosteps ?? emptyInspectionRecords,
|
|
4465
|
+
actions: this._collectedActions ?? emptyInspectionRecords,
|
|
4466
|
+
sent: this._collectedSent ?? emptyInspectionRecords,
|
|
4467
|
+
eventType: event.type
|
|
4468
|
+
});
|
|
4469
|
+
this._collectedMicrosteps = void 0;
|
|
4470
|
+
this._collectedActions = void 0;
|
|
4471
|
+
this._collectedSent = void 0;
|
|
4472
|
+
}
|
|
4473
|
+
_flushInitialEffects() {
|
|
4474
|
+
if (!this._initialEffects?.length) return true;
|
|
4475
|
+
this._forceDeferredActions = true;
|
|
4476
|
+
try {
|
|
4477
|
+
executeExecutableEffects(this._initialEffects, this._actorScope);
|
|
4478
|
+
this._initialEffects = void 0;
|
|
4479
|
+
return true;
|
|
4480
|
+
} catch (err) {
|
|
4481
|
+
this._initialEffects = void 0;
|
|
4482
|
+
if (this._deferred) this._deferred.length = 0;
|
|
4483
|
+
this._recoverOrError(err);
|
|
4484
|
+
return this._snapshot.status === "active";
|
|
4485
|
+
} finally {
|
|
4486
|
+
this._forceDeferredActions = false;
|
|
4487
|
+
}
|
|
4488
|
+
}
|
|
4489
|
+
subscribe(nextListenerOrObserver, errorListener, completeListener) {
|
|
4490
|
+
const observer = toObserver(nextListenerOrObserver, errorListener, completeListener);
|
|
4491
|
+
if (this._processingStatus !== 2) (this.observers ??= /* @__PURE__ */ new Set()).add(observer);
|
|
4492
|
+
else switch (this._snapshot.status) {
|
|
4493
|
+
case "done":
|
|
4494
|
+
safeCall(observer.complete, void 0, this.system._reportUnhandledError);
|
|
4495
|
+
break;
|
|
4496
|
+
case "error": {
|
|
4497
|
+
const err = this._snapshot.error;
|
|
4498
|
+
if (!observer.error) this.system._reportUnhandledError(err);
|
|
4499
|
+
else {
|
|
4500
|
+
if (!observer.passive) this._errorObserved = true;
|
|
4501
|
+
safeCall(observer.error, err, this.system._reportUnhandledError);
|
|
4502
|
+
}
|
|
4503
|
+
break;
|
|
4504
|
+
}
|
|
4505
|
+
}
|
|
4506
|
+
return { unsubscribe: () => {
|
|
4507
|
+
this.observers?.delete(observer);
|
|
4508
|
+
} };
|
|
4509
|
+
}
|
|
4510
|
+
on(type, handler) {
|
|
4511
|
+
let listeners = this.eventListeners?.get(type);
|
|
4512
|
+
if (!listeners) {
|
|
4513
|
+
listeners = /* @__PURE__ */ new Set();
|
|
4514
|
+
(this.eventListeners ??= /* @__PURE__ */ new Map()).set(type, listeners);
|
|
4515
|
+
}
|
|
4516
|
+
listeners.add(handler);
|
|
4517
|
+
return { unsubscribe: () => {
|
|
4518
|
+
listeners.delete(handler);
|
|
4519
|
+
} };
|
|
4520
|
+
}
|
|
4521
|
+
select(selector, equalityFn = Object.is) {
|
|
4522
|
+
return {
|
|
4523
|
+
subscribe: (observerOrFn, errorListener, completeListener) => {
|
|
4524
|
+
const observer = toObserver(observerOrFn, errorListener, completeListener);
|
|
4525
|
+
let selected = selector(this.getSnapshot());
|
|
4526
|
+
return this.subscribe({
|
|
4527
|
+
next: (snapshot) => {
|
|
4528
|
+
const next = selector(snapshot);
|
|
4529
|
+
if (!equalityFn(selected, next)) {
|
|
4530
|
+
selected = next;
|
|
4531
|
+
observer.next?.(next);
|
|
4532
|
+
}
|
|
4533
|
+
},
|
|
4534
|
+
...observer.error !== void 0 && { error: observer.error },
|
|
4535
|
+
...observer.complete !== void 0 && { complete: observer.complete }
|
|
4536
|
+
});
|
|
4537
|
+
},
|
|
4538
|
+
get: () => selector(this.getSnapshot())
|
|
4539
|
+
};
|
|
4540
|
+
}
|
|
4541
|
+
/** Starts the Actor from the initial state */
|
|
4542
|
+
start() {
|
|
4543
|
+
if (this._processingStatus === 1) return this;
|
|
4544
|
+
if (this._processingStatus === 2) {
|
|
4545
|
+
const status = this._snapshot.status;
|
|
4546
|
+
if (status === "done" || status === "error") return this;
|
|
4547
|
+
throw new Error(`Actor ${this.id} was stopped and cannot be restarted. Create a new actor with createActor().`);
|
|
4548
|
+
}
|
|
4549
|
+
if (this._syncSnapshot) this.subscribe({
|
|
4550
|
+
next: (snapshot) => {
|
|
4551
|
+
if (snapshot.status === "active") this.system._relay(this, this._parent, {
|
|
4552
|
+
type: "xstate.snapshot.actor",
|
|
4553
|
+
actorId: this.id,
|
|
4554
|
+
sessionId: this.sessionId,
|
|
4555
|
+
snapshot
|
|
4556
|
+
});
|
|
4557
|
+
},
|
|
4558
|
+
error: () => {}
|
|
4559
|
+
});
|
|
4560
|
+
this.system._register(this.sessionId, this);
|
|
4561
|
+
if (this.registryKey) this.system._set(this.registryKey, this);
|
|
4562
|
+
this._processingStatus = 1;
|
|
4563
|
+
this._lastSourceRef = this._parent;
|
|
4564
|
+
switch (this._snapshot.status) {
|
|
4565
|
+
case "done":
|
|
4566
|
+
if (this._restored) {
|
|
4567
|
+
this._next(this._snapshot);
|
|
4568
|
+
this._stopProcedure();
|
|
4569
|
+
this._complete();
|
|
4570
|
+
return this;
|
|
4571
|
+
}
|
|
4572
|
+
if (!this._flushInitialEffects()) return this;
|
|
4573
|
+
this.update(this._snapshot, createInitEvent(this.options.input));
|
|
4574
|
+
return this;
|
|
4575
|
+
case "error":
|
|
4576
|
+
this._error(this._snapshot.error);
|
|
4577
|
+
return this;
|
|
4578
|
+
case "stopped":
|
|
4579
|
+
this._stopProcedure();
|
|
4580
|
+
this._complete();
|
|
4581
|
+
return this;
|
|
4582
|
+
}
|
|
4583
|
+
if (!this._parent) this.system.start();
|
|
4584
|
+
if (!this._flushInitialEffects()) return this;
|
|
4585
|
+
if (this.logic.start) try {
|
|
4586
|
+
this.logic.start(this._snapshot, this._actorScope, { restored: this._restored });
|
|
4587
|
+
} catch (err) {
|
|
4588
|
+
this._setErrorSnapshot(err);
|
|
4589
|
+
this._error(err);
|
|
4590
|
+
return this;
|
|
4591
|
+
}
|
|
4592
|
+
if (!this._restored && !this._deferred?.length && !this.observers?.size && !(this.system._hasInspectionObservers?.() ?? true)) {
|
|
4593
|
+
if (!refreshSnapshotActorRefRoot(this._snapshot, this, this.system)) this._setSnapshot(this._snapshot);
|
|
4594
|
+
this._collectedMicrosteps = void 0;
|
|
4595
|
+
this._collectedActions = void 0;
|
|
4596
|
+
this._collectedSent = void 0;
|
|
4597
|
+
} else this.update(this._snapshot, createInitEvent(this.options.input));
|
|
4598
|
+
if (this._restored) {
|
|
4599
|
+
const timers = this._snapshot.timers ?? {};
|
|
4600
|
+
const isWallClock = !this.system._clock.now;
|
|
4601
|
+
const now = this.wallClock.now();
|
|
4602
|
+
for (const timer of Object.values(timers)) {
|
|
4603
|
+
const delay = isWallClock && timer.startedAt !== void 0 ? Math.min(timer.delay, Math.max(0, timer.startedAt + timer.delay - now)) : timer.delay;
|
|
4604
|
+
this.system.scheduleTimer(this, timer.id, delay);
|
|
4605
|
+
}
|
|
4606
|
+
this._restored = false;
|
|
4607
|
+
}
|
|
4608
|
+
this._mailboxStarted = true;
|
|
4609
|
+
this.mailbox?.start();
|
|
4610
|
+
return this;
|
|
4611
|
+
}
|
|
4612
|
+
_process(event) {
|
|
4613
|
+
const previousSnapshot = this._snapshot;
|
|
4614
|
+
let nextState;
|
|
4615
|
+
let caughtError;
|
|
4616
|
+
try {
|
|
4617
|
+
nextState = finalizeTransitionResult(this._actorScope, this._snapshot, this.logic.transition(this._snapshot, event, this._actorScope));
|
|
4618
|
+
} catch (err) {
|
|
4619
|
+
caughtError = { err };
|
|
4620
|
+
}
|
|
4621
|
+
if (caughtError) {
|
|
4622
|
+
this._collectedMicrosteps = void 0;
|
|
4623
|
+
this._collectedActions = void 0;
|
|
4624
|
+
this._collectedSent = void 0;
|
|
4625
|
+
if (!this._recoverOrError(caughtError.err)) this._inspectTransition(this._snapshot, event);
|
|
4626
|
+
return;
|
|
4627
|
+
}
|
|
4628
|
+
if (!nextState) return;
|
|
4629
|
+
let snapshot = this._snapshot;
|
|
4630
|
+
try {
|
|
4631
|
+
const [nextSnapshot, effects] = nextState;
|
|
4632
|
+
if (nextSnapshot === previousSnapshot && !effects.length && "machine" in previousSnapshot && !event.type.startsWith("xstate.")) this._reportUnhandledEvent(event);
|
|
4633
|
+
snapshot = nextSnapshot;
|
|
4634
|
+
this._setSnapshot(snapshot);
|
|
4635
|
+
executeExecutableEffects(effects, this._actorScope);
|
|
4636
|
+
this.update(snapshot, event);
|
|
4637
|
+
} catch (err) {
|
|
4638
|
+
if (!this._recoverOrError(err, snapshot, previousSnapshot)) this._inspectTransition(this._snapshot, event);
|
|
4639
|
+
return;
|
|
4640
|
+
}
|
|
4641
|
+
if (event.type === "@xstate.stop") {
|
|
4642
|
+
this._stopProcedure();
|
|
4643
|
+
this._complete();
|
|
4644
|
+
}
|
|
4645
|
+
}
|
|
4646
|
+
_warnedUnhandledTypes;
|
|
4647
|
+
_reportUnhandledEvent(event) {
|
|
4648
|
+
safeCall(() => this.options.onUnhandledEvent?.(event, this._snapshot), void 0, this.system._reportUnhandledError);
|
|
4649
|
+
}
|
|
4650
|
+
/** @internal */
|
|
4651
|
+
_emit(event) {
|
|
4652
|
+
for (const listeners of [this.eventListeners?.get(event.type), this.eventListeners?.get("*")]) if (listeners) for (const handler of listeners) safeCall(handler, event, this.system._reportUnhandledError);
|
|
4653
|
+
}
|
|
4654
|
+
/** @internal */
|
|
4655
|
+
_terminate(termination) {
|
|
4656
|
+
if (termination.status === "done") {
|
|
4657
|
+
this._next(this._snapshot);
|
|
4658
|
+
this._stopProcedure();
|
|
4659
|
+
this._complete();
|
|
4660
|
+
if (this._parent) this.system._relay(this, this._parent, createDoneActorEvent(this.id, termination.output, this.sessionId));
|
|
4661
|
+
return;
|
|
4662
|
+
}
|
|
4663
|
+
if (termination.status === "error") this._error(termination.error);
|
|
4664
|
+
}
|
|
4665
|
+
/** @internal */
|
|
4666
|
+
_stop() {
|
|
4667
|
+
if (this._processingStatus === 2) return this;
|
|
4668
|
+
this.mailbox?.clear();
|
|
4669
|
+
if (this._processingStatus === 0) {
|
|
4670
|
+
this._processingStatus = 2;
|
|
4671
|
+
this.system._unregister(this);
|
|
4672
|
+
return this;
|
|
4673
|
+
}
|
|
4674
|
+
this._send({ type: XSTATE_STOP });
|
|
4675
|
+
this.system._unregister(this);
|
|
4676
|
+
return this;
|
|
4677
|
+
}
|
|
4678
|
+
/** Stops the Actor and unsubscribe all listeners. */
|
|
4679
|
+
stop() {
|
|
4680
|
+
if (this._parent) throw new Error("A non-root actor cannot be stopped directly.");
|
|
4681
|
+
return this._stop();
|
|
4682
|
+
}
|
|
4683
|
+
_complete() {
|
|
4684
|
+
for (const observer of this.observers ?? emptyInspectionRecords) safeCall(observer.complete, void 0, this.system._reportUnhandledError);
|
|
4685
|
+
this.observers?.clear();
|
|
4686
|
+
this.eventListeners?.clear();
|
|
4687
|
+
}
|
|
4688
|
+
/**
|
|
4689
|
+
* Reports an unhandled error unless a consumer observes it before the
|
|
4690
|
+
* report runs. An actor can error before its creator has a chance to
|
|
4691
|
+
* subscribe, so the check waits one macrotask for a subscriber with an
|
|
4692
|
+
* `error` callback.
|
|
4693
|
+
*/
|
|
4694
|
+
_reportUnlessObserved(err) {
|
|
4695
|
+
setTimeout(() => {
|
|
4696
|
+
if (!this._errorObserved) this.system._reportUnhandledError(err);
|
|
4697
|
+
});
|
|
4698
|
+
}
|
|
4699
|
+
_error(err, previousSnapshot) {
|
|
4700
|
+
this._stopProcedure();
|
|
4701
|
+
const children = /* @__PURE__ */ new Set();
|
|
4702
|
+
for (const snapshot of [this._snapshot, previousSnapshot]) {
|
|
4703
|
+
const snapshotChildren = snapshot?.children;
|
|
4704
|
+
for (const child of Object.values(snapshotChildren ?? {})) if (child?._parent === this && typeof child.getSnapshot === "function") children.add(child);
|
|
4705
|
+
}
|
|
4706
|
+
for (const child of this.system._peekChildren?.()?.values() ?? []) if (child._parent === this) children.add(child);
|
|
4707
|
+
for (const child of children) try {
|
|
4708
|
+
if (child.getSnapshot().status !== "active") continue;
|
|
4709
|
+
const result = this.system.stopActor(child);
|
|
4710
|
+
if (result) Promise.resolve(result).catch(this.system._reportUnhandledError);
|
|
4711
|
+
} catch (error) {
|
|
4712
|
+
this.system._reportUnhandledError(error);
|
|
4713
|
+
}
|
|
4714
|
+
if (!this.observers?.size) {
|
|
4715
|
+
if (!this._parent) this._reportUnlessObserved(err);
|
|
4716
|
+
} else {
|
|
4717
|
+
let reportError = false;
|
|
4718
|
+
let handled = false;
|
|
4719
|
+
for (const observer of this.observers) {
|
|
4720
|
+
const errorListener = observer.error;
|
|
4721
|
+
if (!observer.passive) {
|
|
4722
|
+
reportError ||= !errorListener;
|
|
4723
|
+
handled ||= !!errorListener;
|
|
4724
|
+
}
|
|
4725
|
+
safeCall(errorListener, err, this.system._reportUnhandledError);
|
|
4726
|
+
}
|
|
4727
|
+
reportError ||= !handled;
|
|
4728
|
+
this.observers.clear();
|
|
4729
|
+
if (reportError) this._reportUnlessObserved(err);
|
|
4730
|
+
}
|
|
4731
|
+
this.eventListeners?.clear();
|
|
4732
|
+
if (this._parent) this.system._relay(this, this._parent, createErrorActorEvent(this.id, err, this.sessionId));
|
|
4733
|
+
}
|
|
4734
|
+
_stopProcedure() {
|
|
4735
|
+
if (this._processingStatus !== 1) return;
|
|
4736
|
+
this.system.cancelAllTimers(this);
|
|
4737
|
+
this.mailbox?.clear();
|
|
4738
|
+
this.mailbox = void 0;
|
|
4739
|
+
this._boundProcess = void 0;
|
|
4740
|
+
this._mailboxStarted = false;
|
|
4741
|
+
this._processingStatus = 2;
|
|
4742
|
+
this.system._unregister(this);
|
|
4743
|
+
}
|
|
4744
|
+
/** @internal */
|
|
4745
|
+
_send(event) {
|
|
4746
|
+
if (this._processingStatus === 2) {
|
|
4747
|
+
this.system.deadLetter(this._lastSourceRef, this, event, "stopped");
|
|
4748
|
+
return;
|
|
4749
|
+
}
|
|
4750
|
+
let mailbox = this.mailbox;
|
|
4751
|
+
if (!mailbox) {
|
|
4752
|
+
this._boundProcess ??= this._process.bind(this);
|
|
4753
|
+
mailbox = this.mailbox = new Mailbox(this._boundProcess);
|
|
4754
|
+
if (this._mailboxStarted) mailbox.start();
|
|
4755
|
+
}
|
|
4756
|
+
mailbox.enqueue(event);
|
|
4757
|
+
}
|
|
4758
|
+
/**
|
|
4759
|
+
* Sends an event to the running Actor to trigger a transition.
|
|
4760
|
+
*
|
|
4761
|
+
* @param event The event to send
|
|
4762
|
+
*/
|
|
4763
|
+
_sendPublic(event) {
|
|
4764
|
+
this.system._relay(void 0, this, event);
|
|
4765
|
+
}
|
|
4766
|
+
/**
|
|
4767
|
+
* Returns the actor's serializable logical identity: its `id`, `address`,
|
|
4768
|
+
* and registered source key (when the actor was created from one).
|
|
4769
|
+
*/
|
|
4770
|
+
toJSON() {
|
|
4771
|
+
return {
|
|
4772
|
+
xstate$type: ACTOR_REF_TYPE,
|
|
4773
|
+
id: this.id,
|
|
4774
|
+
address: this.address,
|
|
4775
|
+
src: typeof this.src === "string" ? this.src : void 0
|
|
4776
|
+
};
|
|
4777
|
+
}
|
|
4778
|
+
getPersistedSnapshot(options) {
|
|
4779
|
+
return this.logic.getPersistedSnapshot(this._snapshot, options, this.warn);
|
|
4780
|
+
}
|
|
4781
|
+
[symbolObservable]() {
|
|
4782
|
+
return this;
|
|
4783
|
+
}
|
|
4784
|
+
/**
|
|
4785
|
+
* Read an actor’s snapshot synchronously.
|
|
4786
|
+
*
|
|
4787
|
+
* @remarks
|
|
4788
|
+
* The snapshot is the last value the actor published, not a query of its
|
|
4789
|
+
* private state: the actor emits a snapshot when it transitions, and this
|
|
4790
|
+
* returns that cached last emission. The read is coherent within one
|
|
4791
|
+
* synchronous transition turn because event processing runs to completion;
|
|
4792
|
+
* across asynchronous boundaries it may be stale, like any observed value.
|
|
4793
|
+
*
|
|
4794
|
+
* Only co-located actors publish full snapshots. A location-transparent
|
|
4795
|
+
* remote handle exposes lifecycle only (`{ status, output?, error? }`): while
|
|
4796
|
+
* the handle exists among its parent's children the child is presumed
|
|
4797
|
+
* `active`, and its terminal result arrives as a completion event.
|
|
4798
|
+
*
|
|
4799
|
+
* Note that some actors, such as callback actors generated with
|
|
4800
|
+
* `createCallbackLogic`, will not emit snapshots.
|
|
4801
|
+
* @see {@link Actor.(subscribe:1)} to subscribe to an actor’s snapshot values.
|
|
4802
|
+
* @see {@link Actor.getPersistedSnapshot} to persist the internal state of an actor (which is more than just a snapshot).
|
|
4803
|
+
*/
|
|
4804
|
+
getSnapshot() {
|
|
4805
|
+
return this._snapshot;
|
|
4806
|
+
}
|
|
4807
|
+
};
|
|
4808
|
+
/**
|
|
4809
|
+
* Creates a new actor instance for the given actor logic with the provided
|
|
4810
|
+
* options, if any.
|
|
4811
|
+
*
|
|
4812
|
+
* @remarks
|
|
4813
|
+
* When you create an actor from actor logic via `createActor(logic)`, you
|
|
4814
|
+
* implicitly create an actor system where the created actor is the root actor.
|
|
4815
|
+
* Any actors spawned from this root actor and its descendants are part of that
|
|
4816
|
+
* actor system.
|
|
4817
|
+
* @example
|
|
4818
|
+
*
|
|
4819
|
+
* ```ts
|
|
4820
|
+
* import { createActor } from '@systemfsoftware/xstate';
|
|
4821
|
+
* import { someActorLogic } from './someActorLogic.ts';
|
|
4822
|
+
*
|
|
4823
|
+
* // Creating the actor, which implicitly creates an actor system with itself as the root actor
|
|
4824
|
+
* const actor = createActor(someActorLogic);
|
|
4825
|
+
*
|
|
4826
|
+
* actor.subscribe((snapshot) => {
|
|
4827
|
+
* console.log(snapshot);
|
|
4828
|
+
* });
|
|
4829
|
+
*
|
|
4830
|
+
* // Actors must be started by calling `actor.start()`, which will also start the actor system.
|
|
4831
|
+
* actor.start();
|
|
4832
|
+
*
|
|
4833
|
+
* // Actors can receive events
|
|
4834
|
+
* actor.send({ type: 'someEvent' });
|
|
4835
|
+
*
|
|
4836
|
+
* // You can stop root actors by calling `actor.stop()`, which will also stop the actor system and all actors in that system.
|
|
4837
|
+
* actor.stop();
|
|
4838
|
+
* ```
|
|
4839
|
+
*
|
|
4840
|
+
* @param logic - The actor logic to create an actor from. For a state machine
|
|
4841
|
+
* actor logic creator, see {@link (createMachine:1)}. Other actor logic creators
|
|
4842
|
+
* include {@link (createCallbackLogic:1)}, {@link (createEventObservableLogic:1)},
|
|
4843
|
+
* {@link (createObservableLogic:1)}, {@link (createLogic:1)}, and
|
|
4844
|
+
* {@link (createAsyncLogic:1)}.
|
|
4845
|
+
* @param options - Actor options
|
|
4846
|
+
* @public
|
|
4847
|
+
*/
|
|
4848
|
+
function createActor(logic, ...[options]) {
|
|
4849
|
+
return new Actor(logic, options);
|
|
4850
|
+
}
|
|
4851
|
+
export { matchesState as $, executeEffects as A, createListenerLogic as B, createCustomEffect as C, createErrorPlatformEvent as Ct, createSpawnEffect as D, createSendToEffect as E, resolveActionsWithContext as F, isInertActorScope as G, systemLogicMetadata as H, takeSpawnAllocationCounters as I, createMachineSnapshot as J, setInertActorScopeSnapshot as K, resolveRegisteredActorSource as L, isBuiltInExecutableAction as M, mergeActorIdCounters as N, createStartEffect as O, reserveChildId as P, recordTimerStart as Q, createSubscriptionLogic as R, beginSpawnAllocation as S, createErrorActorEvent as St, createEmitEffect as T, createInvokeTimeoutEvent as Tt, attachSnapshotActorRef as U, listenerLogic as V, createInertActorScope as W, isMachineSnapshot as X, getPersistedSnapshot as Y, getTimerStart as Z, transitionNode as _, stopActor as _t, InfiniteTransitionError as a, parseDelayToMilliseconds as at, allocateChildId as b, withSystemInspector as bt, getProperAncestors as c, encodeAddressSegment as ct, getTransitionResult as d, parseGeneratedActorId as dt, pathToStateValue as et, hasEffect as f, withExecutionIdentity as ft, resolveStateValue as g, runStep as gt, macrostep as h, deliverEvent as ht, StateNode as i, isAtomicStateNode as it, finalizeTransitionResult as j, createTimerEffect as k, getStateNodeByPath as l, getActorIdPrefix as lt, isInFinalState as m, setSnapshotActorRef as mt, Actor as n, createRemoteActorRef as nt, formatRouteTransitions as o, withActorSelf as ot, initialMicrostep as p, getSnapshotActorRef as pt, cloneMachineSnapshot as q, createActor as r, isRemoteActorRef as rt, getAllStateNodes as s, RUNTIME_OPERATIONS as st, ACTOR_REF_TYPE as t, toStatePath as tt, getStateNodes as u, getRootActorId as ut, checkStateIn as v, terminateActor as vt, createDeadLetterEffect as w, createInitEvent as wt, assertChildIdFree as x, createDoneActorEvent as xt, isStateId as y, hasAmbientInspector as yt, subscriptionLogic as z };
|