xstate 5.0.0-beta.26 → 5.0.0-beta.28
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/README.md +9 -7
- package/actions/dist/xstate-actions.cjs.js +14 -18
- package/actions/dist/xstate-actions.cjs.mjs +0 -6
- package/actions/dist/xstate-actions.development.cjs.js +14 -18
- package/actions/dist/xstate-actions.development.cjs.mjs +0 -6
- package/actions/dist/xstate-actions.development.esm.js +3 -1
- package/actions/dist/xstate-actions.esm.js +3 -1
- package/actions/dist/xstate-actions.umd.min.js +1 -1
- package/actions/dist/xstate-actions.umd.min.js.map +1 -1
- package/actors/dist/xstate-actors.cjs.js +439 -14
- package/actors/dist/xstate-actors.cjs.mjs +0 -5
- package/actors/dist/xstate-actors.development.cjs.js +439 -14
- package/actors/dist/xstate-actors.development.cjs.mjs +0 -5
- package/actors/dist/xstate-actors.development.esm.js +435 -1
- package/actors/dist/xstate-actors.esm.js +435 -1
- package/actors/dist/xstate-actors.umd.min.js +1 -1
- package/actors/dist/xstate-actors.umd.min.js.map +1 -1
- package/dist/declarations/src/Machine.d.ts +4 -3
- package/dist/declarations/src/State.d.ts +1 -1
- package/dist/declarations/src/StateNode.d.ts +3 -12
- package/dist/declarations/src/actions/assign.d.ts +11 -11
- package/dist/declarations/src/actions/cancel.d.ts +5 -12
- package/dist/declarations/src/actions/choose.d.ts +9 -7
- package/dist/declarations/src/actions/log.d.ts +5 -20
- package/dist/declarations/src/actions/pure.d.ts +11 -9
- package/dist/declarations/src/actions/raise.d.ts +6 -5
- package/dist/declarations/src/actions/send.d.ts +7 -12
- package/dist/declarations/src/actions/stop.d.ts +5 -12
- package/dist/declarations/src/actions.d.ts +8 -42
- package/dist/declarations/src/actors/index.d.ts +2 -22
- package/dist/declarations/src/actors/promise.d.ts +2 -1
- package/dist/declarations/src/constants.d.ts +8 -0
- package/dist/declarations/src/guards.d.ts +2 -2
- package/dist/declarations/src/index.d.ts +11 -18
- package/dist/declarations/src/spawn.d.ts +25 -0
- package/dist/declarations/src/stateUtils.d.ts +1 -1
- package/dist/declarations/src/typegenTypes.d.ts +5 -5
- package/dist/declarations/src/types.d.ts +81 -108
- package/dist/declarations/src/utils.d.ts +2 -3
- package/dist/interpreter-672794ae.cjs.js +792 -0
- package/dist/interpreter-a1432c7d.development.cjs.js +800 -0
- package/dist/interpreter-a77bb0ec.development.esm.js +765 -0
- package/dist/interpreter-b5203bcb.esm.js +757 -0
- package/dist/raise-436a57a2.cjs.js +1458 -0
- package/dist/raise-74b72ca5.development.cjs.js +1498 -0
- package/dist/raise-a60c9290.development.esm.js +1468 -0
- package/dist/raise-b9c9a234.esm.js +1428 -0
- package/dist/send-35e1a689.cjs.js +349 -0
- package/dist/send-4192e7bc.esm.js +339 -0
- package/dist/send-e63b7b83.development.esm.js +364 -0
- package/dist/send-e8b55d00.development.cjs.js +374 -0
- package/dist/xstate.cjs.js +140 -160
- package/dist/xstate.cjs.mjs +4 -2
- package/dist/xstate.development.cjs.js +140 -160
- package/dist/xstate.development.cjs.mjs +4 -2
- package/dist/xstate.development.esm.js +75 -95
- package/dist/xstate.esm.js +75 -95
- package/dist/xstate.umd.min.js +1 -1
- package/dist/xstate.umd.min.js.map +1 -1
- package/guards/dist/xstate-guards.cjs.js +7 -6
- package/guards/dist/xstate-guards.development.cjs.js +7 -6
- package/guards/dist/xstate-guards.development.esm.js +2 -1
- package/guards/dist/xstate-guards.esm.js +2 -1
- package/guards/dist/xstate-guards.umd.min.js +1 -1
- package/guards/dist/xstate-guards.umd.min.js.map +1 -1
- package/package.json +1 -1
- package/dist/actions-0971b43d.development.cjs.js +0 -3168
- package/dist/actions-319cefe7.cjs.js +0 -3095
- package/dist/actions-5943a9db.esm.js +0 -3018
- package/dist/actions-cf69419d.development.esm.js +0 -3091
- package/dist/declarations/src/constantPrefixes.d.ts +0 -6
|
@@ -1,3091 +0,0 @@
|
|
|
1
|
-
import { devToolsAdapter } from '../dev/dist/xstate-dev.development.esm.js';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* `T | unknown` reduces to `unknown` and that can be problematic when it comes to contextual typing.
|
|
5
|
-
* It especially is a problem when the union has a function member, like here:
|
|
6
|
-
*
|
|
7
|
-
* ```ts
|
|
8
|
-
* declare function test(cbOrVal: ((arg: number) => unknown) | unknown): void;
|
|
9
|
-
* test((arg) => {}) // oops, implicit any
|
|
10
|
-
* ```
|
|
11
|
-
*
|
|
12
|
-
* This type can be used to avoid this problem. This union represents the same value space as `unknown`.
|
|
13
|
-
*/
|
|
14
|
-
|
|
15
|
-
// https://github.com/microsoft/TypeScript/issues/23182#issuecomment-379091887
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* The full definition of an event, with a string `type`.
|
|
19
|
-
*/
|
|
20
|
-
|
|
21
|
-
// TODO: do not accept machines without all implementations
|
|
22
|
-
// we should also accept a raw machine as actor logic here
|
|
23
|
-
// or just make machine actor logic
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* The string or object representing the state value relative to the parent state node.
|
|
27
|
-
*
|
|
28
|
-
* - For a child atomic state node, this is a string, e.g., `"pending"`.
|
|
29
|
-
* - For complex state nodes, this is an object, e.g., `{ success: "someChildState" }`.
|
|
30
|
-
*/
|
|
31
|
-
|
|
32
|
-
// TODO: remove once TS fixes this type-widening issue
|
|
33
|
-
|
|
34
|
-
// TODO: possibly refactor this somehow, use even a simpler type, and maybe even make `machine.options` private or something
|
|
35
|
-
|
|
36
|
-
let ConstantPrefix = /*#__PURE__*/function (ConstantPrefix) {
|
|
37
|
-
ConstantPrefix["After"] = "xstate.after";
|
|
38
|
-
ConstantPrefix["DoneState"] = "done.state";
|
|
39
|
-
ConstantPrefix["DoneInvoke"] = "done.invoke";
|
|
40
|
-
ConstantPrefix["ErrorExecution"] = "error.execution";
|
|
41
|
-
ConstantPrefix["ErrorCommunication"] = "error.communication";
|
|
42
|
-
ConstantPrefix["ErrorPlatform"] = "error.platform";
|
|
43
|
-
ConstantPrefix["ErrorCustom"] = "xstate.error";
|
|
44
|
-
return ConstantPrefix;
|
|
45
|
-
}({});
|
|
46
|
-
let SpecialTargets = /*#__PURE__*/function (SpecialTargets) {
|
|
47
|
-
SpecialTargets["Parent"] = "#_parent";
|
|
48
|
-
SpecialTargets["Internal"] = "#_internal";
|
|
49
|
-
return SpecialTargets;
|
|
50
|
-
}({});
|
|
51
|
-
|
|
52
|
-
const after$1 = ConstantPrefix.After;
|
|
53
|
-
const doneState = ConstantPrefix.DoneState;
|
|
54
|
-
const errorExecution = ConstantPrefix.ErrorExecution;
|
|
55
|
-
const errorPlatform = ConstantPrefix.ErrorPlatform;
|
|
56
|
-
const error$1 = ConstantPrefix.ErrorCustom;
|
|
57
|
-
|
|
58
|
-
var constantPrefixes = /*#__PURE__*/Object.freeze({
|
|
59
|
-
__proto__: null,
|
|
60
|
-
after: after$1,
|
|
61
|
-
doneState: doneState,
|
|
62
|
-
errorExecution: errorExecution,
|
|
63
|
-
errorPlatform: errorPlatform,
|
|
64
|
-
error: error$1
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
const STATE_DELIMITER = '.';
|
|
68
|
-
const TARGETLESS_KEY = '';
|
|
69
|
-
const NULL_EVENT = '';
|
|
70
|
-
const STATE_IDENTIFIER = '#';
|
|
71
|
-
const WILDCARD = '*';
|
|
72
|
-
const INIT_TYPE = 'xstate.init';
|
|
73
|
-
|
|
74
|
-
function resolve$8(actorContext, state, args, {
|
|
75
|
-
to,
|
|
76
|
-
event: eventOrExpr,
|
|
77
|
-
id,
|
|
78
|
-
delay
|
|
79
|
-
}) {
|
|
80
|
-
const delaysMap = state.machine.implementations.delays;
|
|
81
|
-
if (typeof eventOrExpr === 'string') {
|
|
82
|
-
throw new Error(`Only event objects may be used with sendTo; use sendTo({ type: "${eventOrExpr}" }) instead`);
|
|
83
|
-
}
|
|
84
|
-
const resolvedEvent = typeof eventOrExpr === 'function' ? eventOrExpr(args) : eventOrExpr;
|
|
85
|
-
let resolvedDelay;
|
|
86
|
-
if (typeof delay === 'string') {
|
|
87
|
-
const configDelay = delaysMap && delaysMap[delay];
|
|
88
|
-
resolvedDelay = typeof configDelay === 'function' ? configDelay(args) : configDelay;
|
|
89
|
-
} else {
|
|
90
|
-
resolvedDelay = typeof delay === 'function' ? delay(args) : delay;
|
|
91
|
-
}
|
|
92
|
-
const resolvedTarget = typeof to === 'function' ? to(args) : to;
|
|
93
|
-
let targetActorRef;
|
|
94
|
-
if (typeof resolvedTarget === 'string') {
|
|
95
|
-
if (resolvedTarget === SpecialTargets.Parent) {
|
|
96
|
-
targetActorRef = actorContext?.self._parent;
|
|
97
|
-
} else if (resolvedTarget === SpecialTargets.Internal) {
|
|
98
|
-
targetActorRef = actorContext?.self;
|
|
99
|
-
} else if (resolvedTarget.startsWith('#_')) {
|
|
100
|
-
// SCXML compatibility: https://www.w3.org/TR/scxml/#SCXMLEventProcessor
|
|
101
|
-
// #_invokeid. If the target is the special term '#_invokeid', where invokeid is the invokeid of an SCXML session that the sending session has created by <invoke>, the Processor must add the event to the external queue of that session.
|
|
102
|
-
targetActorRef = state.children[resolvedTarget.slice(2)];
|
|
103
|
-
} else {
|
|
104
|
-
targetActorRef = state.children[resolvedTarget];
|
|
105
|
-
}
|
|
106
|
-
if (!targetActorRef) {
|
|
107
|
-
throw new Error(`Unable to send event to actor '${resolvedTarget}' from machine '${state.machine.id}'.`);
|
|
108
|
-
}
|
|
109
|
-
} else {
|
|
110
|
-
targetActorRef = resolvedTarget || actorContext?.self;
|
|
111
|
-
}
|
|
112
|
-
return [state, {
|
|
113
|
-
to: targetActorRef,
|
|
114
|
-
event: resolvedEvent,
|
|
115
|
-
id,
|
|
116
|
-
delay: resolvedDelay
|
|
117
|
-
}];
|
|
118
|
-
}
|
|
119
|
-
function execute$5(actorContext, params) {
|
|
120
|
-
if (typeof params.delay === 'number') {
|
|
121
|
-
actorContext.self.delaySend(params);
|
|
122
|
-
return;
|
|
123
|
-
}
|
|
124
|
-
const {
|
|
125
|
-
to,
|
|
126
|
-
event
|
|
127
|
-
} = params;
|
|
128
|
-
actorContext.defer(() => {
|
|
129
|
-
to.send(event.type === error$1 ? {
|
|
130
|
-
type: `${error(actorContext.self.id)}`,
|
|
131
|
-
data: event.data
|
|
132
|
-
} : event);
|
|
133
|
-
});
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
/**
|
|
137
|
-
* Sends an event to an actor.
|
|
138
|
-
*
|
|
139
|
-
* @param actor The `ActorRef` to send the event to.
|
|
140
|
-
* @param event The event to send, or an expression that evaluates to the event to send
|
|
141
|
-
* @param options Send action options
|
|
142
|
-
* - `id` - The unique send event identifier (used with `cancel()`).
|
|
143
|
-
* - `delay` - The number of milliseconds to delay the sending of the event.
|
|
144
|
-
*/
|
|
145
|
-
function sendTo(to, eventOrExpr, options) {
|
|
146
|
-
function sendTo(_) {
|
|
147
|
-
{
|
|
148
|
-
throw new Error(`This isn't supposed to be called`);
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
sendTo.type = 'xstate.sendTo';
|
|
152
|
-
sendTo.to = to;
|
|
153
|
-
sendTo.event = eventOrExpr;
|
|
154
|
-
sendTo.id = options?.id;
|
|
155
|
-
sendTo.delay = options?.delay;
|
|
156
|
-
sendTo.resolve = resolve$8;
|
|
157
|
-
sendTo.execute = execute$5;
|
|
158
|
-
return sendTo;
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
/**
|
|
162
|
-
* Sends an event to this machine's parent.
|
|
163
|
-
*
|
|
164
|
-
* @param event The event to send to the parent machine.
|
|
165
|
-
* @param options Options to pass into the send event.
|
|
166
|
-
*/
|
|
167
|
-
function sendParent(event, options) {
|
|
168
|
-
return sendTo(SpecialTargets.Parent, event, options);
|
|
169
|
-
}
|
|
170
|
-
/**
|
|
171
|
-
* Forwards (sends) an event to a specified service.
|
|
172
|
-
*
|
|
173
|
-
* @param target The target service to forward the event to.
|
|
174
|
-
* @param options Options to pass into the send action creator.
|
|
175
|
-
*/
|
|
176
|
-
function forwardTo(target, options) {
|
|
177
|
-
if ((!target || typeof target === 'function')) {
|
|
178
|
-
const originalTarget = target;
|
|
179
|
-
target = (...args) => {
|
|
180
|
-
const resolvedTarget = typeof originalTarget === 'function' ? originalTarget(...args) : originalTarget;
|
|
181
|
-
if (!resolvedTarget) {
|
|
182
|
-
throw new Error(`Attempted to forward event to undefined actor. This risks an infinite loop in the sender.`);
|
|
183
|
-
}
|
|
184
|
-
return resolvedTarget;
|
|
185
|
-
};
|
|
186
|
-
}
|
|
187
|
-
return sendTo(target, ({
|
|
188
|
-
event
|
|
189
|
-
}) => event, options);
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
/**
|
|
193
|
-
* Escalates an error by sending it as an event to this machine's parent.
|
|
194
|
-
*
|
|
195
|
-
* @param errorData The error data to send, or the expression function that
|
|
196
|
-
* takes in the `context`, `event`, and `meta`, and returns the error data to send.
|
|
197
|
-
* @param options Options to pass into the send action creator.
|
|
198
|
-
*/
|
|
199
|
-
function escalate(errorData, options) {
|
|
200
|
-
return sendParent(arg => {
|
|
201
|
-
return {
|
|
202
|
-
type: error$1,
|
|
203
|
-
data: typeof errorData === 'function' ? errorData(arg) : errorData
|
|
204
|
-
};
|
|
205
|
-
}, options);
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
const cache = new WeakMap();
|
|
209
|
-
function memo(object, key, fn) {
|
|
210
|
-
let memoizedData = cache.get(object);
|
|
211
|
-
if (!memoizedData) {
|
|
212
|
-
memoizedData = {
|
|
213
|
-
[key]: fn()
|
|
214
|
-
};
|
|
215
|
-
cache.set(object, memoizedData);
|
|
216
|
-
} else if (!(key in memoizedData)) {
|
|
217
|
-
memoizedData[key] = fn();
|
|
218
|
-
}
|
|
219
|
-
return memoizedData[key];
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
function resolve$7(_, state, actionArgs, {
|
|
223
|
-
sendId
|
|
224
|
-
}) {
|
|
225
|
-
const resolvedSendId = typeof sendId === 'function' ? sendId(actionArgs) : sendId;
|
|
226
|
-
return [state, resolvedSendId];
|
|
227
|
-
}
|
|
228
|
-
function execute$4(actorContext, resolvedSendId) {
|
|
229
|
-
actorContext.self.cancel(resolvedSendId);
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
/**
|
|
233
|
-
* Cancels an in-flight `send(...)` action. A canceled sent action will not
|
|
234
|
-
* be executed, nor will its event be sent, unless it has already been sent
|
|
235
|
-
* (e.g., if `cancel(...)` is called after the `send(...)` action's `delay`).
|
|
236
|
-
*
|
|
237
|
-
* @param sendId The `id` of the `send(...)` action to cancel.
|
|
238
|
-
*/
|
|
239
|
-
function cancel(sendId) {
|
|
240
|
-
function cancel(_) {
|
|
241
|
-
{
|
|
242
|
-
throw new Error(`This isn't supposed to be called`);
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
cancel.type = 'xstate.cancel';
|
|
246
|
-
cancel.sendId = sendId;
|
|
247
|
-
cancel.resolve = resolve$7;
|
|
248
|
-
cancel.execute = execute$4;
|
|
249
|
-
return cancel;
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
class Mailbox {
|
|
253
|
-
constructor(_process) {
|
|
254
|
-
this._process = _process;
|
|
255
|
-
this._active = false;
|
|
256
|
-
this._current = null;
|
|
257
|
-
this._last = null;
|
|
258
|
-
}
|
|
259
|
-
start() {
|
|
260
|
-
this._active = true;
|
|
261
|
-
this.flush();
|
|
262
|
-
}
|
|
263
|
-
clear() {
|
|
264
|
-
// we can't set _current to null because we might be currently processing
|
|
265
|
-
// and enqueue following clear shouldnt start processing the enqueued item immediately
|
|
266
|
-
if (this._current) {
|
|
267
|
-
this._current.next = null;
|
|
268
|
-
this._last = this._current;
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
// TODO: rethink this design
|
|
273
|
-
prepend(event) {
|
|
274
|
-
if (!this._current) {
|
|
275
|
-
this.enqueue(event);
|
|
276
|
-
return;
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
// we know that something is already queued up
|
|
280
|
-
// so the mailbox is already flushing or it's inactive
|
|
281
|
-
// therefore the only thing that we need to do is to reassign `this._current`
|
|
282
|
-
this._current = {
|
|
283
|
-
value: event,
|
|
284
|
-
next: this._current
|
|
285
|
-
};
|
|
286
|
-
}
|
|
287
|
-
enqueue(event) {
|
|
288
|
-
const enqueued = {
|
|
289
|
-
value: event,
|
|
290
|
-
next: null
|
|
291
|
-
};
|
|
292
|
-
if (this._current) {
|
|
293
|
-
this._last.next = enqueued;
|
|
294
|
-
this._last = enqueued;
|
|
295
|
-
return;
|
|
296
|
-
}
|
|
297
|
-
this._current = enqueued;
|
|
298
|
-
this._last = enqueued;
|
|
299
|
-
if (this._active) {
|
|
300
|
-
this.flush();
|
|
301
|
-
}
|
|
302
|
-
}
|
|
303
|
-
flush() {
|
|
304
|
-
while (this._current) {
|
|
305
|
-
// atm the given _process is responsible for implementing proper try/catch handling
|
|
306
|
-
// we assume here that this won't throw in a way that can affect this mailbox
|
|
307
|
-
const consumed = this._current;
|
|
308
|
-
this._process(consumed.value);
|
|
309
|
-
// something could have been prepended in the meantime
|
|
310
|
-
// so we need to be defensive here to avoid skipping over a prepended item
|
|
311
|
-
if (consumed === this._current) {
|
|
312
|
-
this._current = this._current.next;
|
|
313
|
-
}
|
|
314
|
-
}
|
|
315
|
-
this._last = null;
|
|
316
|
-
}
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
const symbolObservable = (() => typeof Symbol === 'function' && Symbol.observable || '@@observable')();
|
|
320
|
-
|
|
321
|
-
/**
|
|
322
|
-
* Returns actor logic from a transition function and its initial state.
|
|
323
|
-
*
|
|
324
|
-
* A transition function is a function that takes the current state and an event and returns the next state.
|
|
325
|
-
*
|
|
326
|
-
* @param transition The transition function that returns the next state given the current state and event.
|
|
327
|
-
* @param initialState The initial state of the transition function.
|
|
328
|
-
* @returns Actor logic
|
|
329
|
-
*/
|
|
330
|
-
function fromTransition(transition, initialState) {
|
|
331
|
-
return {
|
|
332
|
-
config: transition,
|
|
333
|
-
transition: (state, event, actorContext) => {
|
|
334
|
-
return transition(state, event, actorContext);
|
|
335
|
-
},
|
|
336
|
-
getInitialState: (_, input) => {
|
|
337
|
-
return typeof initialState === 'function' ? initialState({
|
|
338
|
-
input
|
|
339
|
-
}) : initialState;
|
|
340
|
-
},
|
|
341
|
-
getSnapshot: state => state,
|
|
342
|
-
getPersistedState: state => state,
|
|
343
|
-
restoreState: state => state
|
|
344
|
-
};
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
function matchesState(parentStateId, childStateId) {
|
|
348
|
-
const parentStateValue = toStateValue(parentStateId);
|
|
349
|
-
const childStateValue = toStateValue(childStateId);
|
|
350
|
-
if (typeof childStateValue === 'string') {
|
|
351
|
-
if (typeof parentStateValue === 'string') {
|
|
352
|
-
return childStateValue === parentStateValue;
|
|
353
|
-
}
|
|
354
|
-
|
|
355
|
-
// Parent more specific than child
|
|
356
|
-
return false;
|
|
357
|
-
}
|
|
358
|
-
if (typeof parentStateValue === 'string') {
|
|
359
|
-
return parentStateValue in childStateValue;
|
|
360
|
-
}
|
|
361
|
-
return Object.keys(parentStateValue).every(key => {
|
|
362
|
-
if (!(key in childStateValue)) {
|
|
363
|
-
return false;
|
|
364
|
-
}
|
|
365
|
-
return matchesState(parentStateValue[key], childStateValue[key]);
|
|
366
|
-
});
|
|
367
|
-
}
|
|
368
|
-
function toStatePath(stateId) {
|
|
369
|
-
try {
|
|
370
|
-
if (isArray(stateId)) {
|
|
371
|
-
return stateId;
|
|
372
|
-
}
|
|
373
|
-
return stateId.toString().split(STATE_DELIMITER);
|
|
374
|
-
} catch (e) {
|
|
375
|
-
throw new Error(`'${stateId}' is not a valid state path.`);
|
|
376
|
-
}
|
|
377
|
-
}
|
|
378
|
-
function isStateLike(state) {
|
|
379
|
-
return typeof state === 'object' && 'value' in state && 'context' in state && 'event' in state;
|
|
380
|
-
}
|
|
381
|
-
function toStateValue(stateValue) {
|
|
382
|
-
if (isStateLike(stateValue)) {
|
|
383
|
-
return stateValue.value;
|
|
384
|
-
}
|
|
385
|
-
if (isArray(stateValue)) {
|
|
386
|
-
return pathToStateValue(stateValue);
|
|
387
|
-
}
|
|
388
|
-
if (typeof stateValue !== 'string') {
|
|
389
|
-
return stateValue;
|
|
390
|
-
}
|
|
391
|
-
const statePath = toStatePath(stateValue);
|
|
392
|
-
return pathToStateValue(statePath);
|
|
393
|
-
}
|
|
394
|
-
function pathToStateValue(statePath) {
|
|
395
|
-
if (statePath.length === 1) {
|
|
396
|
-
return statePath[0];
|
|
397
|
-
}
|
|
398
|
-
const value = {};
|
|
399
|
-
let marker = value;
|
|
400
|
-
for (let i = 0; i < statePath.length - 1; i++) {
|
|
401
|
-
if (i === statePath.length - 2) {
|
|
402
|
-
marker[statePath[i]] = statePath[i + 1];
|
|
403
|
-
} else {
|
|
404
|
-
const previous = marker;
|
|
405
|
-
marker = {};
|
|
406
|
-
previous[statePath[i]] = marker;
|
|
407
|
-
}
|
|
408
|
-
}
|
|
409
|
-
return value;
|
|
410
|
-
}
|
|
411
|
-
function mapValues(collection, iteratee) {
|
|
412
|
-
const result = {};
|
|
413
|
-
const collectionKeys = Object.keys(collection);
|
|
414
|
-
for (let i = 0; i < collectionKeys.length; i++) {
|
|
415
|
-
const key = collectionKeys[i];
|
|
416
|
-
result[key] = iteratee(collection[key], key, collection, i);
|
|
417
|
-
}
|
|
418
|
-
return result;
|
|
419
|
-
}
|
|
420
|
-
function flatten(array) {
|
|
421
|
-
return [].concat(...array);
|
|
422
|
-
}
|
|
423
|
-
function toArrayStrict(value) {
|
|
424
|
-
if (isArray(value)) {
|
|
425
|
-
return value;
|
|
426
|
-
}
|
|
427
|
-
return [value];
|
|
428
|
-
}
|
|
429
|
-
function toArray(value) {
|
|
430
|
-
if (value === undefined) {
|
|
431
|
-
return [];
|
|
432
|
-
}
|
|
433
|
-
return toArrayStrict(value);
|
|
434
|
-
}
|
|
435
|
-
function mapContext(mapper, context, event, self) {
|
|
436
|
-
if (typeof mapper === 'function') {
|
|
437
|
-
return mapper({
|
|
438
|
-
context,
|
|
439
|
-
event,
|
|
440
|
-
self
|
|
441
|
-
});
|
|
442
|
-
}
|
|
443
|
-
if (typeof mapper === 'object' && Object.values(mapper).some(val => typeof val === 'function')) {
|
|
444
|
-
console.warn(`Dynamically mapping values to individual properties is deprecated. Use a single function that returns the mapped object instead.\nFound object containing properties whose values are possibly mapping functions: ${Object.entries(mapper).filter(([key, value]) => typeof value === 'function').map(([key, value]) => `\n - ${key}: ${value.toString().replace(/\n\s*/g, '')}`).join('')}`);
|
|
445
|
-
}
|
|
446
|
-
return mapper;
|
|
447
|
-
}
|
|
448
|
-
function isPromiseLike(value) {
|
|
449
|
-
if (value instanceof Promise) {
|
|
450
|
-
return true;
|
|
451
|
-
}
|
|
452
|
-
// Check if shape matches the Promise/A+ specification for a "thenable".
|
|
453
|
-
if (value !== null && (typeof value === 'function' || typeof value === 'object') && typeof value.then === 'function') {
|
|
454
|
-
return true;
|
|
455
|
-
}
|
|
456
|
-
return false;
|
|
457
|
-
}
|
|
458
|
-
function isArray(value) {
|
|
459
|
-
return Array.isArray(value);
|
|
460
|
-
}
|
|
461
|
-
function isErrorEvent(event) {
|
|
462
|
-
return typeof event.type === 'string' && (event.type === errorExecution || event.type.startsWith(errorPlatform));
|
|
463
|
-
}
|
|
464
|
-
function toTransitionConfigArray(configLike) {
|
|
465
|
-
return toArrayStrict(configLike).map(transitionLike => {
|
|
466
|
-
if (typeof transitionLike === 'undefined' || typeof transitionLike === 'string') {
|
|
467
|
-
return {
|
|
468
|
-
target: transitionLike
|
|
469
|
-
};
|
|
470
|
-
}
|
|
471
|
-
return transitionLike;
|
|
472
|
-
});
|
|
473
|
-
}
|
|
474
|
-
function normalizeTarget(target) {
|
|
475
|
-
if (target === undefined || target === TARGETLESS_KEY) {
|
|
476
|
-
return undefined;
|
|
477
|
-
}
|
|
478
|
-
return toArray(target);
|
|
479
|
-
}
|
|
480
|
-
function toInvokeConfig(invocable, id) {
|
|
481
|
-
if (typeof invocable === 'object') {
|
|
482
|
-
if ('src' in invocable) {
|
|
483
|
-
return invocable;
|
|
484
|
-
}
|
|
485
|
-
if ('transition' in invocable) {
|
|
486
|
-
return {
|
|
487
|
-
id,
|
|
488
|
-
src: invocable
|
|
489
|
-
};
|
|
490
|
-
}
|
|
491
|
-
}
|
|
492
|
-
return {
|
|
493
|
-
id,
|
|
494
|
-
src: invocable
|
|
495
|
-
};
|
|
496
|
-
}
|
|
497
|
-
function toObserver(nextHandler, errorHandler, completionHandler) {
|
|
498
|
-
const isObserver = typeof nextHandler === 'object';
|
|
499
|
-
const self = isObserver ? nextHandler : undefined;
|
|
500
|
-
return {
|
|
501
|
-
next: (isObserver ? nextHandler.next : nextHandler)?.bind(self),
|
|
502
|
-
error: (isObserver ? nextHandler.error : errorHandler)?.bind(self),
|
|
503
|
-
complete: (isObserver ? nextHandler.complete : completionHandler)?.bind(self)
|
|
504
|
-
};
|
|
505
|
-
}
|
|
506
|
-
function createInvokeId(stateNodeId, index) {
|
|
507
|
-
return `${stateNodeId}:invocation[${index}]`;
|
|
508
|
-
}
|
|
509
|
-
function resolveReferencedActor(referenced) {
|
|
510
|
-
return referenced ? 'transition' in referenced ? {
|
|
511
|
-
src: referenced,
|
|
512
|
-
input: undefined
|
|
513
|
-
} : referenced : undefined;
|
|
514
|
-
}
|
|
515
|
-
|
|
516
|
-
function fromCallback(invokeCallback) {
|
|
517
|
-
return {
|
|
518
|
-
config: invokeCallback,
|
|
519
|
-
start: (_state, {
|
|
520
|
-
self
|
|
521
|
-
}) => {
|
|
522
|
-
self.send({
|
|
523
|
-
type: startSignalType
|
|
524
|
-
});
|
|
525
|
-
},
|
|
526
|
-
transition: (state, event, {
|
|
527
|
-
self,
|
|
528
|
-
id,
|
|
529
|
-
system
|
|
530
|
-
}) => {
|
|
531
|
-
if (event.type === startSignalType) {
|
|
532
|
-
const sendBack = eventForParent => {
|
|
533
|
-
if (state.canceled) {
|
|
534
|
-
return;
|
|
535
|
-
}
|
|
536
|
-
self._parent?.send(eventForParent);
|
|
537
|
-
};
|
|
538
|
-
const receive = newListener => {
|
|
539
|
-
state.receivers.add(newListener);
|
|
540
|
-
};
|
|
541
|
-
state.dispose = invokeCallback({
|
|
542
|
-
input: state.input,
|
|
543
|
-
system,
|
|
544
|
-
self: self,
|
|
545
|
-
sendBack,
|
|
546
|
-
receive
|
|
547
|
-
});
|
|
548
|
-
if (isPromiseLike(state.dispose)) {
|
|
549
|
-
state.dispose.then(resolved => {
|
|
550
|
-
self._parent?.send(doneInvoke(id, resolved));
|
|
551
|
-
state.canceled = true;
|
|
552
|
-
}, errorData => {
|
|
553
|
-
state.canceled = true;
|
|
554
|
-
self._parent?.send(error(id, errorData));
|
|
555
|
-
});
|
|
556
|
-
}
|
|
557
|
-
return state;
|
|
558
|
-
}
|
|
559
|
-
if (event.type === stopSignalType) {
|
|
560
|
-
state.canceled = true;
|
|
561
|
-
if (typeof state.dispose === 'function') {
|
|
562
|
-
state.dispose();
|
|
563
|
-
}
|
|
564
|
-
return state;
|
|
565
|
-
}
|
|
566
|
-
if (isSignal(event)) {
|
|
567
|
-
// TODO: unrecognized signal
|
|
568
|
-
return state;
|
|
569
|
-
}
|
|
570
|
-
state.receivers.forEach(receiver => receiver(event));
|
|
571
|
-
return state;
|
|
572
|
-
},
|
|
573
|
-
getInitialState: (_, input) => {
|
|
574
|
-
return {
|
|
575
|
-
canceled: false,
|
|
576
|
-
receivers: new Set(),
|
|
577
|
-
dispose: undefined,
|
|
578
|
-
input
|
|
579
|
-
};
|
|
580
|
-
},
|
|
581
|
-
getSnapshot: () => undefined,
|
|
582
|
-
getPersistedState: ({
|
|
583
|
-
input,
|
|
584
|
-
canceled
|
|
585
|
-
}) => ({
|
|
586
|
-
input,
|
|
587
|
-
canceled
|
|
588
|
-
})
|
|
589
|
-
};
|
|
590
|
-
}
|
|
591
|
-
|
|
592
|
-
function fromObservable(observableCreator) {
|
|
593
|
-
const nextEventType = '$$xstate.next';
|
|
594
|
-
const errorEventType = '$$xstate.error';
|
|
595
|
-
const completeEventType = '$$xstate.complete';
|
|
596
|
-
return {
|
|
597
|
-
config: observableCreator,
|
|
598
|
-
transition: (state, event, {
|
|
599
|
-
self,
|
|
600
|
-
id,
|
|
601
|
-
defer
|
|
602
|
-
}) => {
|
|
603
|
-
if (state.status !== 'active') {
|
|
604
|
-
return state;
|
|
605
|
-
}
|
|
606
|
-
switch (event.type) {
|
|
607
|
-
case nextEventType:
|
|
608
|
-
// match the exact timing of events sent by machines
|
|
609
|
-
// send actions are not executed immediately
|
|
610
|
-
defer(() => {
|
|
611
|
-
self._parent?.send({
|
|
612
|
-
type: `xstate.snapshot.${id}`,
|
|
613
|
-
data: event.data
|
|
614
|
-
});
|
|
615
|
-
});
|
|
616
|
-
return {
|
|
617
|
-
...state,
|
|
618
|
-
data: event.data
|
|
619
|
-
};
|
|
620
|
-
case errorEventType:
|
|
621
|
-
return {
|
|
622
|
-
...state,
|
|
623
|
-
status: 'error',
|
|
624
|
-
input: undefined,
|
|
625
|
-
data: event.data,
|
|
626
|
-
// TODO: if we keep this as `data` we should reflect this in the type
|
|
627
|
-
subscription: undefined
|
|
628
|
-
};
|
|
629
|
-
case completeEventType:
|
|
630
|
-
return {
|
|
631
|
-
...state,
|
|
632
|
-
status: 'done',
|
|
633
|
-
input: undefined,
|
|
634
|
-
subscription: undefined
|
|
635
|
-
};
|
|
636
|
-
case stopSignalType:
|
|
637
|
-
state.subscription.unsubscribe();
|
|
638
|
-
return {
|
|
639
|
-
...state,
|
|
640
|
-
status: 'canceled',
|
|
641
|
-
input: undefined,
|
|
642
|
-
subscription: undefined
|
|
643
|
-
};
|
|
644
|
-
default:
|
|
645
|
-
return state;
|
|
646
|
-
}
|
|
647
|
-
},
|
|
648
|
-
getInitialState: (_, input) => {
|
|
649
|
-
return {
|
|
650
|
-
subscription: undefined,
|
|
651
|
-
status: 'active',
|
|
652
|
-
data: undefined,
|
|
653
|
-
input
|
|
654
|
-
};
|
|
655
|
-
},
|
|
656
|
-
start: (state, {
|
|
657
|
-
self,
|
|
658
|
-
system
|
|
659
|
-
}) => {
|
|
660
|
-
if (state.status === 'done') {
|
|
661
|
-
// Do not restart a completed observable
|
|
662
|
-
return;
|
|
663
|
-
}
|
|
664
|
-
state.subscription = observableCreator({
|
|
665
|
-
input: state.input,
|
|
666
|
-
system,
|
|
667
|
-
self
|
|
668
|
-
}).subscribe({
|
|
669
|
-
next: value => {
|
|
670
|
-
self.send({
|
|
671
|
-
type: nextEventType,
|
|
672
|
-
data: value
|
|
673
|
-
});
|
|
674
|
-
},
|
|
675
|
-
error: err => {
|
|
676
|
-
self.send({
|
|
677
|
-
type: errorEventType,
|
|
678
|
-
data: err
|
|
679
|
-
});
|
|
680
|
-
},
|
|
681
|
-
complete: () => {
|
|
682
|
-
self.send({
|
|
683
|
-
type: completeEventType
|
|
684
|
-
});
|
|
685
|
-
}
|
|
686
|
-
});
|
|
687
|
-
},
|
|
688
|
-
getSnapshot: state => state.data,
|
|
689
|
-
getPersistedState: ({
|
|
690
|
-
status,
|
|
691
|
-
data,
|
|
692
|
-
input
|
|
693
|
-
}) => ({
|
|
694
|
-
status,
|
|
695
|
-
data,
|
|
696
|
-
input
|
|
697
|
-
}),
|
|
698
|
-
getStatus: state => state,
|
|
699
|
-
restoreState: state => ({
|
|
700
|
-
...state,
|
|
701
|
-
subscription: undefined
|
|
702
|
-
})
|
|
703
|
-
};
|
|
704
|
-
}
|
|
705
|
-
|
|
706
|
-
/**
|
|
707
|
-
* Creates event observable logic that listens to an observable
|
|
708
|
-
* that delivers event objects.
|
|
709
|
-
*
|
|
710
|
-
*
|
|
711
|
-
* @param lazyObservable A function that creates an observable
|
|
712
|
-
* @returns Event observable logic
|
|
713
|
-
*/
|
|
714
|
-
|
|
715
|
-
function fromEventObservable(lazyObservable) {
|
|
716
|
-
const errorEventType = '$$xstate.error';
|
|
717
|
-
const completeEventType = '$$xstate.complete';
|
|
718
|
-
|
|
719
|
-
// TODO: event types
|
|
720
|
-
return {
|
|
721
|
-
config: lazyObservable,
|
|
722
|
-
transition: (state, event) => {
|
|
723
|
-
if (state.status !== 'active') {
|
|
724
|
-
return state;
|
|
725
|
-
}
|
|
726
|
-
switch (event.type) {
|
|
727
|
-
case errorEventType:
|
|
728
|
-
return {
|
|
729
|
-
...state,
|
|
730
|
-
status: 'error',
|
|
731
|
-
input: undefined,
|
|
732
|
-
data: event.data,
|
|
733
|
-
// TODO: if we keep this as `data` we should reflect this in the type
|
|
734
|
-
subscription: undefined
|
|
735
|
-
};
|
|
736
|
-
case completeEventType:
|
|
737
|
-
return {
|
|
738
|
-
...state,
|
|
739
|
-
status: 'done',
|
|
740
|
-
input: undefined,
|
|
741
|
-
subscription: undefined
|
|
742
|
-
};
|
|
743
|
-
case stopSignalType:
|
|
744
|
-
state.subscription.unsubscribe();
|
|
745
|
-
return {
|
|
746
|
-
...state,
|
|
747
|
-
status: 'canceled',
|
|
748
|
-
input: undefined,
|
|
749
|
-
subscription: undefined
|
|
750
|
-
};
|
|
751
|
-
default:
|
|
752
|
-
return state;
|
|
753
|
-
}
|
|
754
|
-
},
|
|
755
|
-
getInitialState: (_, input) => {
|
|
756
|
-
return {
|
|
757
|
-
subscription: undefined,
|
|
758
|
-
status: 'active',
|
|
759
|
-
data: undefined,
|
|
760
|
-
input
|
|
761
|
-
};
|
|
762
|
-
},
|
|
763
|
-
start: (state, {
|
|
764
|
-
self,
|
|
765
|
-
system
|
|
766
|
-
}) => {
|
|
767
|
-
if (state.status === 'done') {
|
|
768
|
-
// Do not restart a completed observable
|
|
769
|
-
return;
|
|
770
|
-
}
|
|
771
|
-
state.subscription = lazyObservable({
|
|
772
|
-
input: state.input,
|
|
773
|
-
system,
|
|
774
|
-
self
|
|
775
|
-
}).subscribe({
|
|
776
|
-
next: value => {
|
|
777
|
-
self._parent?.send(value);
|
|
778
|
-
},
|
|
779
|
-
error: err => {
|
|
780
|
-
self.send({
|
|
781
|
-
type: errorEventType,
|
|
782
|
-
data: err
|
|
783
|
-
});
|
|
784
|
-
},
|
|
785
|
-
complete: () => {
|
|
786
|
-
self.send({
|
|
787
|
-
type: completeEventType
|
|
788
|
-
});
|
|
789
|
-
}
|
|
790
|
-
});
|
|
791
|
-
},
|
|
792
|
-
getSnapshot: _ => undefined,
|
|
793
|
-
getPersistedState: ({
|
|
794
|
-
status,
|
|
795
|
-
data,
|
|
796
|
-
input
|
|
797
|
-
}) => ({
|
|
798
|
-
status,
|
|
799
|
-
data,
|
|
800
|
-
input
|
|
801
|
-
}),
|
|
802
|
-
getStatus: state => state,
|
|
803
|
-
restoreState: state => ({
|
|
804
|
-
...state,
|
|
805
|
-
subscription: undefined
|
|
806
|
-
})
|
|
807
|
-
};
|
|
808
|
-
}
|
|
809
|
-
|
|
810
|
-
const resolveEventType = '$$xstate.resolve';
|
|
811
|
-
const rejectEventType = '$$xstate.reject';
|
|
812
|
-
function fromPromise(
|
|
813
|
-
// TODO: add types
|
|
814
|
-
promiseCreator) {
|
|
815
|
-
// TODO: add event types
|
|
816
|
-
const logic = {
|
|
817
|
-
config: promiseCreator,
|
|
818
|
-
transition: (state, event) => {
|
|
819
|
-
if (state.status !== 'active') {
|
|
820
|
-
return state;
|
|
821
|
-
}
|
|
822
|
-
switch (event.type) {
|
|
823
|
-
case resolveEventType:
|
|
824
|
-
return {
|
|
825
|
-
...state,
|
|
826
|
-
status: 'done',
|
|
827
|
-
data: event.data,
|
|
828
|
-
input: undefined
|
|
829
|
-
};
|
|
830
|
-
case rejectEventType:
|
|
831
|
-
return {
|
|
832
|
-
...state,
|
|
833
|
-
status: 'error',
|
|
834
|
-
data: event.data,
|
|
835
|
-
// TODO: if we keep this as `data` we should reflect this in the type
|
|
836
|
-
input: undefined
|
|
837
|
-
};
|
|
838
|
-
case stopSignalType:
|
|
839
|
-
return {
|
|
840
|
-
...state,
|
|
841
|
-
status: 'canceled',
|
|
842
|
-
input: undefined
|
|
843
|
-
};
|
|
844
|
-
default:
|
|
845
|
-
return state;
|
|
846
|
-
}
|
|
847
|
-
},
|
|
848
|
-
start: (state, {
|
|
849
|
-
self,
|
|
850
|
-
system
|
|
851
|
-
}) => {
|
|
852
|
-
// TODO: determine how to allow customizing this so that promises
|
|
853
|
-
// can be restarted if necessary
|
|
854
|
-
if (state.status !== 'active') {
|
|
855
|
-
return;
|
|
856
|
-
}
|
|
857
|
-
const resolvedPromise = Promise.resolve(promiseCreator({
|
|
858
|
-
input: state.input,
|
|
859
|
-
system,
|
|
860
|
-
self
|
|
861
|
-
}));
|
|
862
|
-
resolvedPromise.then(response => {
|
|
863
|
-
// TODO: remove this condition once dead letter queue lands
|
|
864
|
-
if (self._state.status !== 'active') {
|
|
865
|
-
return;
|
|
866
|
-
}
|
|
867
|
-
self.send({
|
|
868
|
-
type: resolveEventType,
|
|
869
|
-
data: response
|
|
870
|
-
});
|
|
871
|
-
}, errorData => {
|
|
872
|
-
// TODO: remove this condition once dead letter queue lands
|
|
873
|
-
if (self._state.status !== 'active') {
|
|
874
|
-
return;
|
|
875
|
-
}
|
|
876
|
-
self.send({
|
|
877
|
-
type: rejectEventType,
|
|
878
|
-
data: errorData
|
|
879
|
-
});
|
|
880
|
-
});
|
|
881
|
-
},
|
|
882
|
-
getInitialState: (_, input) => {
|
|
883
|
-
return {
|
|
884
|
-
status: 'active',
|
|
885
|
-
data: undefined,
|
|
886
|
-
input
|
|
887
|
-
};
|
|
888
|
-
},
|
|
889
|
-
getSnapshot: state => state.data,
|
|
890
|
-
getStatus: state => state,
|
|
891
|
-
getPersistedState: state => state,
|
|
892
|
-
restoreState: state => state
|
|
893
|
-
};
|
|
894
|
-
return logic;
|
|
895
|
-
}
|
|
896
|
-
|
|
897
|
-
const startSignalType = 'xstate.init';
|
|
898
|
-
const stopSignalType = 'xstate.stop';
|
|
899
|
-
const startSignal = {
|
|
900
|
-
type: 'xstate.init'
|
|
901
|
-
};
|
|
902
|
-
const stopSignal = {
|
|
903
|
-
type: 'xstate.stop'
|
|
904
|
-
};
|
|
905
|
-
/**
|
|
906
|
-
* An object that expresses the actor logic in reaction to received events,
|
|
907
|
-
* as well as an optionally emitted stream of values.
|
|
908
|
-
*
|
|
909
|
-
* @template TReceived The received event
|
|
910
|
-
* @template TSnapshot The emitted value
|
|
911
|
-
*/
|
|
912
|
-
|
|
913
|
-
function isSignal(event) {
|
|
914
|
-
return event.type === startSignalType || event.type === stopSignalType;
|
|
915
|
-
}
|
|
916
|
-
function isActorRef(item) {
|
|
917
|
-
return !!item && typeof item === 'object' && typeof item.send === 'function';
|
|
918
|
-
}
|
|
919
|
-
|
|
920
|
-
// TODO: refactor the return type, this could be written in a better way
|
|
921
|
-
// but it's best to avoid unneccessary breaking changes now
|
|
922
|
-
// @deprecated use `interpret(actorLogic)` instead
|
|
923
|
-
function toActorRef(actorRefLike) {
|
|
924
|
-
return {
|
|
925
|
-
subscribe: () => ({
|
|
926
|
-
unsubscribe: () => void 0
|
|
927
|
-
}),
|
|
928
|
-
id: 'anonymous',
|
|
929
|
-
sessionId: '',
|
|
930
|
-
getSnapshot: () => undefined,
|
|
931
|
-
// TODO: this isn't safe
|
|
932
|
-
[symbolObservable]: function () {
|
|
933
|
-
return this;
|
|
934
|
-
},
|
|
935
|
-
status: ActorStatus.Running,
|
|
936
|
-
stop: () => void 0,
|
|
937
|
-
...actorRefLike
|
|
938
|
-
};
|
|
939
|
-
}
|
|
940
|
-
const emptyLogic = fromTransition(_ => undefined, undefined);
|
|
941
|
-
function createEmptyActor() {
|
|
942
|
-
return createActor(emptyLogic);
|
|
943
|
-
}
|
|
944
|
-
|
|
945
|
-
/**
|
|
946
|
-
* This function makes sure that unhandled errors are thrown in a separate macrotask.
|
|
947
|
-
* It allows those errors to be detected by global error handlers and reported to bug tracking services
|
|
948
|
-
* without interrupting our own stack of execution.
|
|
949
|
-
*
|
|
950
|
-
* @param err error to be thrown
|
|
951
|
-
*/
|
|
952
|
-
function reportUnhandledError(err) {
|
|
953
|
-
setTimeout(() => {
|
|
954
|
-
throw err;
|
|
955
|
-
});
|
|
956
|
-
}
|
|
957
|
-
|
|
958
|
-
function createSystem() {
|
|
959
|
-
let sessionIdCounter = 0;
|
|
960
|
-
const children = new Map();
|
|
961
|
-
const keyedActors = new Map();
|
|
962
|
-
const reverseKeyedActors = new WeakMap();
|
|
963
|
-
const system = {
|
|
964
|
-
_bookId: () => `x:${sessionIdCounter++}`,
|
|
965
|
-
_register: (sessionId, actorRef) => {
|
|
966
|
-
children.set(sessionId, actorRef);
|
|
967
|
-
return sessionId;
|
|
968
|
-
},
|
|
969
|
-
_unregister: actorRef => {
|
|
970
|
-
children.delete(actorRef.sessionId);
|
|
971
|
-
const systemId = reverseKeyedActors.get(actorRef);
|
|
972
|
-
if (systemId !== undefined) {
|
|
973
|
-
keyedActors.delete(systemId);
|
|
974
|
-
reverseKeyedActors.delete(actorRef);
|
|
975
|
-
}
|
|
976
|
-
},
|
|
977
|
-
get: systemId => {
|
|
978
|
-
return keyedActors.get(systemId);
|
|
979
|
-
},
|
|
980
|
-
_set: (systemId, actorRef) => {
|
|
981
|
-
const existing = keyedActors.get(systemId);
|
|
982
|
-
if (existing && existing !== actorRef) {
|
|
983
|
-
throw new Error(`Actor with system ID '${systemId}' already exists.`);
|
|
984
|
-
}
|
|
985
|
-
keyedActors.set(systemId, actorRef);
|
|
986
|
-
reverseKeyedActors.set(actorRef, systemId);
|
|
987
|
-
}
|
|
988
|
-
};
|
|
989
|
-
return system;
|
|
990
|
-
}
|
|
991
|
-
|
|
992
|
-
let ActorStatus = /*#__PURE__*/function (ActorStatus) {
|
|
993
|
-
ActorStatus[ActorStatus["NotStarted"] = 0] = "NotStarted";
|
|
994
|
-
ActorStatus[ActorStatus["Running"] = 1] = "Running";
|
|
995
|
-
ActorStatus[ActorStatus["Stopped"] = 2] = "Stopped";
|
|
996
|
-
return ActorStatus;
|
|
997
|
-
}({});
|
|
998
|
-
|
|
999
|
-
/**
|
|
1000
|
-
* @deprecated Use `ActorStatus` instead.
|
|
1001
|
-
*/
|
|
1002
|
-
const InterpreterStatus = ActorStatus;
|
|
1003
|
-
const defaultOptions = {
|
|
1004
|
-
deferEvents: true,
|
|
1005
|
-
clock: {
|
|
1006
|
-
setTimeout: (fn, ms) => {
|
|
1007
|
-
return setTimeout(fn, ms);
|
|
1008
|
-
},
|
|
1009
|
-
clearTimeout: id => {
|
|
1010
|
-
return clearTimeout(id);
|
|
1011
|
-
}
|
|
1012
|
-
},
|
|
1013
|
-
logger: console.log.bind(console),
|
|
1014
|
-
devTools: false
|
|
1015
|
-
};
|
|
1016
|
-
class Actor {
|
|
1017
|
-
/**
|
|
1018
|
-
* The current internal state of the actor.
|
|
1019
|
-
*/
|
|
1020
|
-
|
|
1021
|
-
/**
|
|
1022
|
-
* The clock that is responsible for setting and clearing timeouts, such as delayed events and transitions.
|
|
1023
|
-
*/
|
|
1024
|
-
|
|
1025
|
-
/**
|
|
1026
|
-
* The unique identifier for this actor relative to its parent.
|
|
1027
|
-
*/
|
|
1028
|
-
|
|
1029
|
-
/**
|
|
1030
|
-
* Whether the service is started.
|
|
1031
|
-
*/
|
|
1032
|
-
|
|
1033
|
-
// Actor Ref
|
|
1034
|
-
|
|
1035
|
-
// TODO: add typings for system
|
|
1036
|
-
|
|
1037
|
-
/**
|
|
1038
|
-
* The globally unique process ID for this invocation.
|
|
1039
|
-
*/
|
|
1040
|
-
|
|
1041
|
-
/**
|
|
1042
|
-
* Creates a new actor instance for the given logic with the provided options, if any.
|
|
1043
|
-
*
|
|
1044
|
-
* @param logic The logic to create an actor from
|
|
1045
|
-
* @param options Actor options
|
|
1046
|
-
*/
|
|
1047
|
-
constructor(logic, options) {
|
|
1048
|
-
this.logic = logic;
|
|
1049
|
-
this._state = void 0;
|
|
1050
|
-
this.clock = void 0;
|
|
1051
|
-
this.options = void 0;
|
|
1052
|
-
this.id = void 0;
|
|
1053
|
-
this.mailbox = new Mailbox(this._process.bind(this));
|
|
1054
|
-
this.delayedEventsMap = {};
|
|
1055
|
-
this.observers = new Set();
|
|
1056
|
-
this.logger = void 0;
|
|
1057
|
-
this.status = ActorStatus.NotStarted;
|
|
1058
|
-
this._parent = void 0;
|
|
1059
|
-
this.ref = void 0;
|
|
1060
|
-
this._actorContext = void 0;
|
|
1061
|
-
this._systemId = void 0;
|
|
1062
|
-
this.sessionId = void 0;
|
|
1063
|
-
this.system = void 0;
|
|
1064
|
-
this._doneEvent = void 0;
|
|
1065
|
-
this.src = void 0;
|
|
1066
|
-
this._deferred = [];
|
|
1067
|
-
const resolvedOptions = {
|
|
1068
|
-
...defaultOptions,
|
|
1069
|
-
...options
|
|
1070
|
-
};
|
|
1071
|
-
const {
|
|
1072
|
-
clock,
|
|
1073
|
-
logger,
|
|
1074
|
-
parent,
|
|
1075
|
-
id,
|
|
1076
|
-
systemId
|
|
1077
|
-
} = resolvedOptions;
|
|
1078
|
-
const self = this;
|
|
1079
|
-
this.system = parent?.system ?? createSystem();
|
|
1080
|
-
if (systemId) {
|
|
1081
|
-
this._systemId = systemId;
|
|
1082
|
-
this.system._set(systemId, this);
|
|
1083
|
-
}
|
|
1084
|
-
this.sessionId = this.system._bookId();
|
|
1085
|
-
this.id = id ?? this.sessionId;
|
|
1086
|
-
this.logger = logger;
|
|
1087
|
-
this.clock = clock;
|
|
1088
|
-
this._parent = parent;
|
|
1089
|
-
this.options = resolvedOptions;
|
|
1090
|
-
this.src = resolvedOptions.src;
|
|
1091
|
-
this.ref = this;
|
|
1092
|
-
this._actorContext = {
|
|
1093
|
-
self,
|
|
1094
|
-
id: this.id,
|
|
1095
|
-
sessionId: this.sessionId,
|
|
1096
|
-
logger: this.logger,
|
|
1097
|
-
defer: fn => {
|
|
1098
|
-
this._deferred.push(fn);
|
|
1099
|
-
},
|
|
1100
|
-
system: this.system,
|
|
1101
|
-
stopChild: child => {
|
|
1102
|
-
if (child._parent !== this) {
|
|
1103
|
-
throw new Error(`Cannot stop child actor ${child.id} of ${this.id} because it is not a child`);
|
|
1104
|
-
}
|
|
1105
|
-
child._stop();
|
|
1106
|
-
}
|
|
1107
|
-
};
|
|
1108
|
-
|
|
1109
|
-
// Ensure that the send method is bound to this Actor instance
|
|
1110
|
-
// if destructured
|
|
1111
|
-
this.send = this.send.bind(this);
|
|
1112
|
-
this._initState();
|
|
1113
|
-
}
|
|
1114
|
-
_initState() {
|
|
1115
|
-
this._state = this.options.state ? this.logic.restoreState ? this.logic.restoreState(this.options.state, this._actorContext) : this.options.state : this.logic.getInitialState(this._actorContext, this.options?.input);
|
|
1116
|
-
}
|
|
1117
|
-
|
|
1118
|
-
// array of functions to defer
|
|
1119
|
-
|
|
1120
|
-
update(state) {
|
|
1121
|
-
// Update state
|
|
1122
|
-
this._state = state;
|
|
1123
|
-
const snapshot = this.getSnapshot();
|
|
1124
|
-
|
|
1125
|
-
// Execute deferred effects
|
|
1126
|
-
let deferredFn;
|
|
1127
|
-
while (deferredFn = this._deferred.shift()) {
|
|
1128
|
-
deferredFn();
|
|
1129
|
-
}
|
|
1130
|
-
for (const observer of this.observers) {
|
|
1131
|
-
// TODO: should observers be notified in case of the error?
|
|
1132
|
-
try {
|
|
1133
|
-
observer.next?.(snapshot);
|
|
1134
|
-
} catch (err) {
|
|
1135
|
-
reportUnhandledError(err);
|
|
1136
|
-
}
|
|
1137
|
-
}
|
|
1138
|
-
const status = this.logic.getStatus?.(state);
|
|
1139
|
-
switch (status?.status) {
|
|
1140
|
-
case 'done':
|
|
1141
|
-
this._stopProcedure();
|
|
1142
|
-
this._complete();
|
|
1143
|
-
this._doneEvent = doneInvoke(this.id, status.data);
|
|
1144
|
-
this._parent?.send(this._doneEvent);
|
|
1145
|
-
break;
|
|
1146
|
-
case 'error':
|
|
1147
|
-
this._stopProcedure();
|
|
1148
|
-
this._error(status.data);
|
|
1149
|
-
this._parent?.send(error(this.id, status.data));
|
|
1150
|
-
break;
|
|
1151
|
-
}
|
|
1152
|
-
}
|
|
1153
|
-
subscribe(nextListenerOrObserver, errorListener, completeListener) {
|
|
1154
|
-
const observer = toObserver(nextListenerOrObserver, errorListener, completeListener);
|
|
1155
|
-
if (this.status !== ActorStatus.Stopped) {
|
|
1156
|
-
this.observers.add(observer);
|
|
1157
|
-
} else {
|
|
1158
|
-
try {
|
|
1159
|
-
observer.complete?.();
|
|
1160
|
-
} catch (err) {
|
|
1161
|
-
reportUnhandledError(err);
|
|
1162
|
-
}
|
|
1163
|
-
}
|
|
1164
|
-
return {
|
|
1165
|
-
unsubscribe: () => {
|
|
1166
|
-
this.observers.delete(observer);
|
|
1167
|
-
}
|
|
1168
|
-
};
|
|
1169
|
-
}
|
|
1170
|
-
|
|
1171
|
-
/**
|
|
1172
|
-
* Starts the Actor from the initial state
|
|
1173
|
-
*/
|
|
1174
|
-
start() {
|
|
1175
|
-
if (this.status === ActorStatus.Running) {
|
|
1176
|
-
// Do not restart the service if it is already started
|
|
1177
|
-
return this;
|
|
1178
|
-
}
|
|
1179
|
-
this.system._register(this.sessionId, this);
|
|
1180
|
-
if (this._systemId) {
|
|
1181
|
-
this.system._set(this._systemId, this);
|
|
1182
|
-
}
|
|
1183
|
-
this.status = ActorStatus.Running;
|
|
1184
|
-
const status = this.logic.getStatus?.(this._state);
|
|
1185
|
-
switch (status?.status) {
|
|
1186
|
-
case 'done':
|
|
1187
|
-
// a state machine can be "done" upon intialization (it could reach a final state using initial microsteps)
|
|
1188
|
-
// we still need to complete observers, flush deferreds etc
|
|
1189
|
-
this.update(this._state);
|
|
1190
|
-
// fallthrough
|
|
1191
|
-
case 'error':
|
|
1192
|
-
// TODO: rethink cleanup of observers, mailbox, etc
|
|
1193
|
-
return this;
|
|
1194
|
-
}
|
|
1195
|
-
if (this.logic.start) {
|
|
1196
|
-
try {
|
|
1197
|
-
this.logic.start(this._state, this._actorContext);
|
|
1198
|
-
} catch (err) {
|
|
1199
|
-
this._stopProcedure();
|
|
1200
|
-
this._error(err);
|
|
1201
|
-
this._parent?.send(error(this.id, err));
|
|
1202
|
-
return this;
|
|
1203
|
-
}
|
|
1204
|
-
}
|
|
1205
|
-
|
|
1206
|
-
// TODO: this notifies all subscribers but usually this is redundant
|
|
1207
|
-
// there is no real change happening here
|
|
1208
|
-
// we need to rethink if this needs to be refactored
|
|
1209
|
-
this.update(this._state);
|
|
1210
|
-
if (this.options.devTools) {
|
|
1211
|
-
this.attachDevTools();
|
|
1212
|
-
}
|
|
1213
|
-
this.mailbox.start();
|
|
1214
|
-
return this;
|
|
1215
|
-
}
|
|
1216
|
-
_process(event) {
|
|
1217
|
-
// TODO: reexamine what happens when an action (or a guard or smth) throws
|
|
1218
|
-
let nextState;
|
|
1219
|
-
let caughtError;
|
|
1220
|
-
try {
|
|
1221
|
-
nextState = this.logic.transition(this._state, event, this._actorContext);
|
|
1222
|
-
} catch (err) {
|
|
1223
|
-
// we wrap it in a box so we can rethrow it later even if falsy value gets caught here
|
|
1224
|
-
caughtError = {
|
|
1225
|
-
err
|
|
1226
|
-
};
|
|
1227
|
-
}
|
|
1228
|
-
if (caughtError) {
|
|
1229
|
-
const {
|
|
1230
|
-
err
|
|
1231
|
-
} = caughtError;
|
|
1232
|
-
this._stopProcedure();
|
|
1233
|
-
this._error(err);
|
|
1234
|
-
this._parent?.send(error(this.id, err));
|
|
1235
|
-
return;
|
|
1236
|
-
}
|
|
1237
|
-
this.update(nextState);
|
|
1238
|
-
if (event.type === stopSignalType) {
|
|
1239
|
-
this._stopProcedure();
|
|
1240
|
-
this._complete();
|
|
1241
|
-
}
|
|
1242
|
-
}
|
|
1243
|
-
_stop() {
|
|
1244
|
-
if (this.status === ActorStatus.Stopped) {
|
|
1245
|
-
return this;
|
|
1246
|
-
}
|
|
1247
|
-
this.mailbox.clear();
|
|
1248
|
-
if (this.status === ActorStatus.NotStarted) {
|
|
1249
|
-
this.status = ActorStatus.Stopped;
|
|
1250
|
-
return this;
|
|
1251
|
-
}
|
|
1252
|
-
this.mailbox.enqueue({
|
|
1253
|
-
type: stopSignalType
|
|
1254
|
-
});
|
|
1255
|
-
return this;
|
|
1256
|
-
}
|
|
1257
|
-
|
|
1258
|
-
/**
|
|
1259
|
-
* Stops the Actor and unsubscribe all listeners.
|
|
1260
|
-
*/
|
|
1261
|
-
stop() {
|
|
1262
|
-
if (this._parent) {
|
|
1263
|
-
throw new Error('A non-root actor cannot be stopped directly.');
|
|
1264
|
-
}
|
|
1265
|
-
return this._stop();
|
|
1266
|
-
}
|
|
1267
|
-
_complete() {
|
|
1268
|
-
for (const observer of this.observers) {
|
|
1269
|
-
try {
|
|
1270
|
-
observer.complete?.();
|
|
1271
|
-
} catch (err) {
|
|
1272
|
-
reportUnhandledError(err);
|
|
1273
|
-
}
|
|
1274
|
-
}
|
|
1275
|
-
this.observers.clear();
|
|
1276
|
-
}
|
|
1277
|
-
_error(err) {
|
|
1278
|
-
if (!this.observers.size) {
|
|
1279
|
-
if (!this._parent) {
|
|
1280
|
-
reportUnhandledError(err);
|
|
1281
|
-
}
|
|
1282
|
-
return;
|
|
1283
|
-
}
|
|
1284
|
-
let reportError = false;
|
|
1285
|
-
for (const observer of this.observers) {
|
|
1286
|
-
const errorListener = observer.error;
|
|
1287
|
-
reportError ||= !errorListener;
|
|
1288
|
-
try {
|
|
1289
|
-
errorListener?.(err);
|
|
1290
|
-
} catch (err2) {
|
|
1291
|
-
reportUnhandledError(err2);
|
|
1292
|
-
}
|
|
1293
|
-
}
|
|
1294
|
-
this.observers.clear();
|
|
1295
|
-
if (reportError) {
|
|
1296
|
-
reportUnhandledError(err);
|
|
1297
|
-
}
|
|
1298
|
-
}
|
|
1299
|
-
_stopProcedure() {
|
|
1300
|
-
if (this.status !== ActorStatus.Running) {
|
|
1301
|
-
// Actor already stopped; do nothing
|
|
1302
|
-
return this;
|
|
1303
|
-
}
|
|
1304
|
-
|
|
1305
|
-
// Cancel all delayed events
|
|
1306
|
-
for (const key of Object.keys(this.delayedEventsMap)) {
|
|
1307
|
-
this.clock.clearTimeout(this.delayedEventsMap[key]);
|
|
1308
|
-
}
|
|
1309
|
-
|
|
1310
|
-
// TODO: mailbox.reset
|
|
1311
|
-
this.mailbox.clear();
|
|
1312
|
-
// TODO: after `stop` we must prepare ourselves for receiving events again
|
|
1313
|
-
// events sent *after* stop signal must be queued
|
|
1314
|
-
// it seems like this should be the common behavior for all of our consumers
|
|
1315
|
-
// so perhaps this should be unified somehow for all of them
|
|
1316
|
-
this.mailbox = new Mailbox(this._process.bind(this));
|
|
1317
|
-
this.status = ActorStatus.Stopped;
|
|
1318
|
-
this.system._unregister(this);
|
|
1319
|
-
return this;
|
|
1320
|
-
}
|
|
1321
|
-
|
|
1322
|
-
/**
|
|
1323
|
-
* Sends an event to the running Actor to trigger a transition.
|
|
1324
|
-
*
|
|
1325
|
-
* @param event The event to send
|
|
1326
|
-
*/
|
|
1327
|
-
send(event) {
|
|
1328
|
-
if (typeof event === 'string') {
|
|
1329
|
-
throw new Error(`Only event objects may be sent to actors; use .send({ type: "${event}" }) instead`);
|
|
1330
|
-
}
|
|
1331
|
-
if (this.status === ActorStatus.Stopped) {
|
|
1332
|
-
// do nothing
|
|
1333
|
-
{
|
|
1334
|
-
const eventString = JSON.stringify(event);
|
|
1335
|
-
console.warn(`Event "${event.type.toString()}" was sent to stopped actor "${this.id} (${this.sessionId})". This actor has already reached its final state, and will not transition.\nEvent: ${eventString}`);
|
|
1336
|
-
}
|
|
1337
|
-
return;
|
|
1338
|
-
}
|
|
1339
|
-
if (this.status !== ActorStatus.Running && !this.options.deferEvents) {
|
|
1340
|
-
throw new Error(`Event "${event.type}" was sent to uninitialized actor "${this.id
|
|
1341
|
-
// tslint:disable-next-line:max-line-length
|
|
1342
|
-
}". Make sure .start() is called for this actor, or set { deferEvents: true } in the actor options.\nEvent: ${JSON.stringify(event)}`);
|
|
1343
|
-
}
|
|
1344
|
-
this.mailbox.enqueue(event);
|
|
1345
|
-
}
|
|
1346
|
-
|
|
1347
|
-
// TODO: make private (and figure out a way to do this within the machine)
|
|
1348
|
-
delaySend({
|
|
1349
|
-
event,
|
|
1350
|
-
id,
|
|
1351
|
-
delay,
|
|
1352
|
-
to
|
|
1353
|
-
}) {
|
|
1354
|
-
const timerId = this.clock.setTimeout(() => {
|
|
1355
|
-
if (to) {
|
|
1356
|
-
to.send(event);
|
|
1357
|
-
} else {
|
|
1358
|
-
this.send(event);
|
|
1359
|
-
}
|
|
1360
|
-
}, delay);
|
|
1361
|
-
|
|
1362
|
-
// TODO: consider the rehydration story here
|
|
1363
|
-
if (id) {
|
|
1364
|
-
this.delayedEventsMap[id] = timerId;
|
|
1365
|
-
}
|
|
1366
|
-
}
|
|
1367
|
-
|
|
1368
|
-
// TODO: make private (and figure out a way to do this within the machine)
|
|
1369
|
-
cancel(sendId) {
|
|
1370
|
-
this.clock.clearTimeout(this.delayedEventsMap[sendId]);
|
|
1371
|
-
delete this.delayedEventsMap[sendId];
|
|
1372
|
-
}
|
|
1373
|
-
attachDevTools() {
|
|
1374
|
-
const {
|
|
1375
|
-
devTools
|
|
1376
|
-
} = this.options;
|
|
1377
|
-
if (devTools) {
|
|
1378
|
-
const resolvedDevToolsAdapter = typeof devTools === 'function' ? devTools : devToolsAdapter;
|
|
1379
|
-
resolvedDevToolsAdapter(this);
|
|
1380
|
-
}
|
|
1381
|
-
}
|
|
1382
|
-
toJSON() {
|
|
1383
|
-
return {
|
|
1384
|
-
id: this.id
|
|
1385
|
-
};
|
|
1386
|
-
}
|
|
1387
|
-
getPersistedState() {
|
|
1388
|
-
return this.logic.getPersistedState?.(this._state);
|
|
1389
|
-
}
|
|
1390
|
-
[symbolObservable]() {
|
|
1391
|
-
return this;
|
|
1392
|
-
}
|
|
1393
|
-
getSnapshot() {
|
|
1394
|
-
return this.logic.getSnapshot ? this.logic.getSnapshot(this._state) : this._state;
|
|
1395
|
-
}
|
|
1396
|
-
}
|
|
1397
|
-
|
|
1398
|
-
/**
|
|
1399
|
-
* Creates a new `ActorRef` instance for the given machine with the provided options, if any.
|
|
1400
|
-
*
|
|
1401
|
-
* @param machine The machine to create an actor from
|
|
1402
|
-
* @param options `ActorRef` options
|
|
1403
|
-
*/
|
|
1404
|
-
|
|
1405
|
-
function createActor(logic, options) {
|
|
1406
|
-
const interpreter = new Actor(logic, options);
|
|
1407
|
-
return interpreter;
|
|
1408
|
-
}
|
|
1409
|
-
|
|
1410
|
-
/**
|
|
1411
|
-
* Creates a new Interpreter instance for the given machine with the provided options, if any.
|
|
1412
|
-
*
|
|
1413
|
-
* @deprecated Use `createActor` instead
|
|
1414
|
-
*/
|
|
1415
|
-
const interpret = createActor;
|
|
1416
|
-
|
|
1417
|
-
/**
|
|
1418
|
-
* @deprecated Use `Actor` instead.
|
|
1419
|
-
*/
|
|
1420
|
-
|
|
1421
|
-
function resolve$6(actorContext, state, actionArgs, {
|
|
1422
|
-
id,
|
|
1423
|
-
systemId,
|
|
1424
|
-
src,
|
|
1425
|
-
input
|
|
1426
|
-
}) {
|
|
1427
|
-
const referenced = resolveReferencedActor(state.machine.implementations.actors[src]);
|
|
1428
|
-
let actorRef;
|
|
1429
|
-
if (referenced) {
|
|
1430
|
-
// TODO: inline `input: undefined` should win over the referenced one
|
|
1431
|
-
const configuredInput = input || referenced.input;
|
|
1432
|
-
actorRef = createActor(referenced.src, {
|
|
1433
|
-
id,
|
|
1434
|
-
src,
|
|
1435
|
-
parent: actorContext?.self,
|
|
1436
|
-
systemId,
|
|
1437
|
-
input: typeof configuredInput === 'function' ? configuredInput({
|
|
1438
|
-
context: state.context,
|
|
1439
|
-
event: actionArgs.event,
|
|
1440
|
-
self: actorContext?.self
|
|
1441
|
-
}) : configuredInput
|
|
1442
|
-
});
|
|
1443
|
-
}
|
|
1444
|
-
if (!actorRef) {
|
|
1445
|
-
console.warn(`Actor type '${src}' not found in machine '${actorContext.id}'.`);
|
|
1446
|
-
}
|
|
1447
|
-
return [cloneState(state, {
|
|
1448
|
-
children: {
|
|
1449
|
-
...state.children,
|
|
1450
|
-
[id]: actorRef
|
|
1451
|
-
}
|
|
1452
|
-
}), {
|
|
1453
|
-
id,
|
|
1454
|
-
actorRef
|
|
1455
|
-
}];
|
|
1456
|
-
}
|
|
1457
|
-
function execute$3(actorContext, {
|
|
1458
|
-
id,
|
|
1459
|
-
actorRef
|
|
1460
|
-
}) {
|
|
1461
|
-
if (!actorRef) {
|
|
1462
|
-
return;
|
|
1463
|
-
}
|
|
1464
|
-
actorContext.defer(() => {
|
|
1465
|
-
if (actorRef.status === ActorStatus.Stopped) {
|
|
1466
|
-
return;
|
|
1467
|
-
}
|
|
1468
|
-
try {
|
|
1469
|
-
actorRef.start?.();
|
|
1470
|
-
} catch (err) {
|
|
1471
|
-
actorContext.self.send(error(id, err));
|
|
1472
|
-
return;
|
|
1473
|
-
}
|
|
1474
|
-
});
|
|
1475
|
-
}
|
|
1476
|
-
function invoke({
|
|
1477
|
-
id,
|
|
1478
|
-
systemId,
|
|
1479
|
-
src,
|
|
1480
|
-
input
|
|
1481
|
-
}) {
|
|
1482
|
-
function invoke(_) {
|
|
1483
|
-
{
|
|
1484
|
-
throw new Error(`This isn't supposed to be called`);
|
|
1485
|
-
}
|
|
1486
|
-
}
|
|
1487
|
-
invoke.type = 'xstate.invoke';
|
|
1488
|
-
invoke.id = id;
|
|
1489
|
-
invoke.systemId = systemId;
|
|
1490
|
-
invoke.src = src;
|
|
1491
|
-
invoke.input = input;
|
|
1492
|
-
invoke.resolve = resolve$6;
|
|
1493
|
-
invoke.execute = execute$3;
|
|
1494
|
-
return invoke;
|
|
1495
|
-
}
|
|
1496
|
-
|
|
1497
|
-
function checkStateIn(state, _, {
|
|
1498
|
-
stateValue
|
|
1499
|
-
}) {
|
|
1500
|
-
if (typeof stateValue === 'string' && isStateId(stateValue)) {
|
|
1501
|
-
return state.configuration.some(sn => sn.id === stateValue.slice(1));
|
|
1502
|
-
}
|
|
1503
|
-
return state.matches(stateValue);
|
|
1504
|
-
}
|
|
1505
|
-
function stateIn(stateValue) {
|
|
1506
|
-
function stateIn(_) {
|
|
1507
|
-
{
|
|
1508
|
-
throw new Error(`This isn't supposed to be called`);
|
|
1509
|
-
}
|
|
1510
|
-
}
|
|
1511
|
-
stateIn.check = checkStateIn;
|
|
1512
|
-
stateIn.stateValue = stateValue;
|
|
1513
|
-
return stateIn;
|
|
1514
|
-
}
|
|
1515
|
-
function checkNot(state, {
|
|
1516
|
-
context,
|
|
1517
|
-
event
|
|
1518
|
-
}, {
|
|
1519
|
-
guards
|
|
1520
|
-
}) {
|
|
1521
|
-
return !evaluateGuard(guards[0], context, event, state);
|
|
1522
|
-
}
|
|
1523
|
-
function not(guard) {
|
|
1524
|
-
function not(_) {
|
|
1525
|
-
{
|
|
1526
|
-
throw new Error(`This isn't supposed to be called`);
|
|
1527
|
-
}
|
|
1528
|
-
}
|
|
1529
|
-
not.check = checkNot;
|
|
1530
|
-
not.guards = [guard];
|
|
1531
|
-
return not;
|
|
1532
|
-
}
|
|
1533
|
-
function checkAnd(state, {
|
|
1534
|
-
context,
|
|
1535
|
-
event
|
|
1536
|
-
}, {
|
|
1537
|
-
guards
|
|
1538
|
-
}) {
|
|
1539
|
-
return guards.every(guard => evaluateGuard(guard, context, event, state));
|
|
1540
|
-
}
|
|
1541
|
-
function and(guards) {
|
|
1542
|
-
function and(_) {
|
|
1543
|
-
{
|
|
1544
|
-
throw new Error(`This isn't supposed to be called`);
|
|
1545
|
-
}
|
|
1546
|
-
}
|
|
1547
|
-
and.check = checkAnd;
|
|
1548
|
-
and.guards = guards;
|
|
1549
|
-
return and;
|
|
1550
|
-
}
|
|
1551
|
-
function checkOr(state, {
|
|
1552
|
-
context,
|
|
1553
|
-
event
|
|
1554
|
-
}, {
|
|
1555
|
-
guards
|
|
1556
|
-
}) {
|
|
1557
|
-
return guards.some(guard => evaluateGuard(guard, context, event, state));
|
|
1558
|
-
}
|
|
1559
|
-
function or(guards) {
|
|
1560
|
-
function or(_) {
|
|
1561
|
-
{
|
|
1562
|
-
throw new Error(`This isn't supposed to be called`);
|
|
1563
|
-
}
|
|
1564
|
-
}
|
|
1565
|
-
or.check = checkOr;
|
|
1566
|
-
or.guards = guards;
|
|
1567
|
-
return or;
|
|
1568
|
-
}
|
|
1569
|
-
|
|
1570
|
-
// TODO: throw on cycles (depth check should be enough)
|
|
1571
|
-
function evaluateGuard(guard, context, event, state) {
|
|
1572
|
-
const {
|
|
1573
|
-
machine
|
|
1574
|
-
} = state;
|
|
1575
|
-
const isInline = typeof guard === 'function';
|
|
1576
|
-
const resolved = isInline ? guard : machine.implementations.guards[typeof guard === 'string' ? guard : guard.type];
|
|
1577
|
-
if (!isInline && !resolved) {
|
|
1578
|
-
throw new Error(`Guard '${typeof guard === 'string' ? guard : guard.type}' is not implemented.'.`);
|
|
1579
|
-
}
|
|
1580
|
-
if (typeof resolved !== 'function') {
|
|
1581
|
-
return evaluateGuard(resolved, context, event, state);
|
|
1582
|
-
}
|
|
1583
|
-
const guardArgs = {
|
|
1584
|
-
context,
|
|
1585
|
-
event,
|
|
1586
|
-
guard: isInline ? undefined : typeof guard === 'string' ? {
|
|
1587
|
-
type: guard
|
|
1588
|
-
} : guard
|
|
1589
|
-
};
|
|
1590
|
-
if (!('check' in resolved)) {
|
|
1591
|
-
// the existing type of `.guards` assumes non-nullable `TExpressionGuard`
|
|
1592
|
-
// inline guards expect `TExpressionGuard` to be set to `undefined`
|
|
1593
|
-
// it's fine to cast this here, our logic makes sure that we call those 2 "variants" correctly
|
|
1594
|
-
return resolved(guardArgs);
|
|
1595
|
-
}
|
|
1596
|
-
const builtinGuard = resolved;
|
|
1597
|
-
return builtinGuard.check(state, guardArgs, resolved // this holds all params
|
|
1598
|
-
);
|
|
1599
|
-
}
|
|
1600
|
-
|
|
1601
|
-
function getOutput(configuration, context, event, self) {
|
|
1602
|
-
const machine = configuration[0].machine;
|
|
1603
|
-
const finalChildStateNode = configuration.find(stateNode => stateNode.type === 'final' && stateNode.parent === machine.root);
|
|
1604
|
-
return finalChildStateNode && finalChildStateNode.output ? mapContext(finalChildStateNode.output, context, event, self) : undefined;
|
|
1605
|
-
}
|
|
1606
|
-
const isAtomicStateNode = stateNode => stateNode.type === 'atomic' || stateNode.type === 'final';
|
|
1607
|
-
function getChildren(stateNode) {
|
|
1608
|
-
return Object.values(stateNode.states).filter(sn => sn.type !== 'history');
|
|
1609
|
-
}
|
|
1610
|
-
function getProperAncestors(stateNode, toStateNode) {
|
|
1611
|
-
const ancestors = [];
|
|
1612
|
-
|
|
1613
|
-
// add all ancestors
|
|
1614
|
-
let m = stateNode.parent;
|
|
1615
|
-
while (m && m !== toStateNode) {
|
|
1616
|
-
ancestors.push(m);
|
|
1617
|
-
m = m.parent;
|
|
1618
|
-
}
|
|
1619
|
-
return ancestors;
|
|
1620
|
-
}
|
|
1621
|
-
function getConfiguration(stateNodes) {
|
|
1622
|
-
const configuration = new Set(stateNodes);
|
|
1623
|
-
const configurationSet = new Set(stateNodes);
|
|
1624
|
-
const adjList = getAdjList(configurationSet);
|
|
1625
|
-
|
|
1626
|
-
// add descendants
|
|
1627
|
-
for (const s of configuration) {
|
|
1628
|
-
// if previously active, add existing child nodes
|
|
1629
|
-
if (s.type === 'compound' && (!adjList.get(s) || !adjList.get(s).length)) {
|
|
1630
|
-
getInitialStateNodes(s).forEach(sn => configurationSet.add(sn));
|
|
1631
|
-
} else {
|
|
1632
|
-
if (s.type === 'parallel') {
|
|
1633
|
-
for (const child of getChildren(s)) {
|
|
1634
|
-
if (child.type === 'history') {
|
|
1635
|
-
continue;
|
|
1636
|
-
}
|
|
1637
|
-
if (!configurationSet.has(child)) {
|
|
1638
|
-
for (const initialStateNode of getInitialStateNodes(child)) {
|
|
1639
|
-
configurationSet.add(initialStateNode);
|
|
1640
|
-
}
|
|
1641
|
-
}
|
|
1642
|
-
}
|
|
1643
|
-
}
|
|
1644
|
-
}
|
|
1645
|
-
}
|
|
1646
|
-
|
|
1647
|
-
// add all ancestors
|
|
1648
|
-
for (const s of configurationSet) {
|
|
1649
|
-
let m = s.parent;
|
|
1650
|
-
while (m) {
|
|
1651
|
-
configurationSet.add(m);
|
|
1652
|
-
m = m.parent;
|
|
1653
|
-
}
|
|
1654
|
-
}
|
|
1655
|
-
return configurationSet;
|
|
1656
|
-
}
|
|
1657
|
-
function getValueFromAdj(baseNode, adjList) {
|
|
1658
|
-
const childStateNodes = adjList.get(baseNode);
|
|
1659
|
-
if (!childStateNodes) {
|
|
1660
|
-
return {}; // todo: fix?
|
|
1661
|
-
}
|
|
1662
|
-
|
|
1663
|
-
if (baseNode.type === 'compound') {
|
|
1664
|
-
const childStateNode = childStateNodes[0];
|
|
1665
|
-
if (childStateNode) {
|
|
1666
|
-
if (isAtomicStateNode(childStateNode)) {
|
|
1667
|
-
return childStateNode.key;
|
|
1668
|
-
}
|
|
1669
|
-
} else {
|
|
1670
|
-
return {};
|
|
1671
|
-
}
|
|
1672
|
-
}
|
|
1673
|
-
const stateValue = {};
|
|
1674
|
-
for (const childStateNode of childStateNodes) {
|
|
1675
|
-
stateValue[childStateNode.key] = getValueFromAdj(childStateNode, adjList);
|
|
1676
|
-
}
|
|
1677
|
-
return stateValue;
|
|
1678
|
-
}
|
|
1679
|
-
function getAdjList(configuration) {
|
|
1680
|
-
const adjList = new Map();
|
|
1681
|
-
for (const s of configuration) {
|
|
1682
|
-
if (!adjList.has(s)) {
|
|
1683
|
-
adjList.set(s, []);
|
|
1684
|
-
}
|
|
1685
|
-
if (s.parent) {
|
|
1686
|
-
if (!adjList.has(s.parent)) {
|
|
1687
|
-
adjList.set(s.parent, []);
|
|
1688
|
-
}
|
|
1689
|
-
adjList.get(s.parent).push(s);
|
|
1690
|
-
}
|
|
1691
|
-
}
|
|
1692
|
-
return adjList;
|
|
1693
|
-
}
|
|
1694
|
-
function getStateValue(rootNode, configuration) {
|
|
1695
|
-
const config = getConfiguration(configuration);
|
|
1696
|
-
return getValueFromAdj(rootNode, getAdjList(config));
|
|
1697
|
-
}
|
|
1698
|
-
function isInFinalState(configuration, stateNode = configuration[0].machine.root) {
|
|
1699
|
-
if (stateNode.type === 'compound') {
|
|
1700
|
-
return getChildren(stateNode).some(s => s.type === 'final' && configuration.includes(s));
|
|
1701
|
-
}
|
|
1702
|
-
if (stateNode.type === 'parallel') {
|
|
1703
|
-
return getChildren(stateNode).every(sn => isInFinalState(configuration, sn));
|
|
1704
|
-
}
|
|
1705
|
-
return false;
|
|
1706
|
-
}
|
|
1707
|
-
const isStateId = str => str[0] === STATE_IDENTIFIER;
|
|
1708
|
-
function getCandidates(stateNode, receivedEventType) {
|
|
1709
|
-
const candidates = stateNode.transitions.get(receivedEventType) || [...stateNode.transitions.keys()].filter(descriptor => {
|
|
1710
|
-
// check if transition is a wildcard transition,
|
|
1711
|
-
// which matches any non-transient events
|
|
1712
|
-
if (descriptor === WILDCARD) {
|
|
1713
|
-
return true;
|
|
1714
|
-
}
|
|
1715
|
-
if (!descriptor.endsWith('.*')) {
|
|
1716
|
-
return false;
|
|
1717
|
-
}
|
|
1718
|
-
if (/.*\*.+/.test(descriptor)) {
|
|
1719
|
-
console.warn(`Wildcards can only be the last token of an event descriptor (e.g., "event.*") or the entire event descriptor ("*"). Check the "${descriptor}" event.`);
|
|
1720
|
-
}
|
|
1721
|
-
const partialEventTokens = descriptor.split('.');
|
|
1722
|
-
const eventTokens = receivedEventType.split('.');
|
|
1723
|
-
for (let tokenIndex = 0; tokenIndex < partialEventTokens.length; tokenIndex++) {
|
|
1724
|
-
const partialEventToken = partialEventTokens[tokenIndex];
|
|
1725
|
-
const eventToken = eventTokens[tokenIndex];
|
|
1726
|
-
if (partialEventToken === '*') {
|
|
1727
|
-
const isLastToken = tokenIndex === partialEventTokens.length - 1;
|
|
1728
|
-
if (!isLastToken) {
|
|
1729
|
-
console.warn(`Infix wildcards in transition events are not allowed. Check the "${descriptor}" transition.`);
|
|
1730
|
-
}
|
|
1731
|
-
return isLastToken;
|
|
1732
|
-
}
|
|
1733
|
-
if (partialEventToken !== eventToken) {
|
|
1734
|
-
return false;
|
|
1735
|
-
}
|
|
1736
|
-
}
|
|
1737
|
-
return true;
|
|
1738
|
-
}).sort((a, b) => b.length - a.length).flatMap(key => stateNode.transitions.get(key));
|
|
1739
|
-
return candidates;
|
|
1740
|
-
}
|
|
1741
|
-
|
|
1742
|
-
/**
|
|
1743
|
-
* All delayed transitions from the config.
|
|
1744
|
-
*/
|
|
1745
|
-
function getDelayedTransitions(stateNode) {
|
|
1746
|
-
const afterConfig = stateNode.config.after;
|
|
1747
|
-
if (!afterConfig) {
|
|
1748
|
-
return [];
|
|
1749
|
-
}
|
|
1750
|
-
const mutateEntryExit = (delay, i) => {
|
|
1751
|
-
const delayRef = typeof delay === 'function' ? `${stateNode.id}:delay[${i}]` : delay;
|
|
1752
|
-
const eventType = after(delayRef, stateNode.id);
|
|
1753
|
-
stateNode.entry.push(raise({
|
|
1754
|
-
type: eventType
|
|
1755
|
-
}, {
|
|
1756
|
-
id: eventType,
|
|
1757
|
-
delay
|
|
1758
|
-
}));
|
|
1759
|
-
stateNode.exit.push(cancel(eventType));
|
|
1760
|
-
return eventType;
|
|
1761
|
-
};
|
|
1762
|
-
const delayedTransitions = isArray(afterConfig) ? afterConfig.map((transition, i) => {
|
|
1763
|
-
const eventType = mutateEntryExit(transition.delay, i);
|
|
1764
|
-
return {
|
|
1765
|
-
...transition,
|
|
1766
|
-
event: eventType
|
|
1767
|
-
};
|
|
1768
|
-
}) : Object.keys(afterConfig).flatMap((delay, i) => {
|
|
1769
|
-
const configTransition = afterConfig[delay];
|
|
1770
|
-
const resolvedTransition = typeof configTransition === 'string' ? {
|
|
1771
|
-
target: configTransition
|
|
1772
|
-
} : configTransition;
|
|
1773
|
-
const resolvedDelay = !isNaN(+delay) ? +delay : delay;
|
|
1774
|
-
const eventType = mutateEntryExit(resolvedDelay, i);
|
|
1775
|
-
return toArray(resolvedTransition).map(transition => ({
|
|
1776
|
-
...transition,
|
|
1777
|
-
event: eventType,
|
|
1778
|
-
delay: resolvedDelay
|
|
1779
|
-
}));
|
|
1780
|
-
});
|
|
1781
|
-
return delayedTransitions.map(delayedTransition => {
|
|
1782
|
-
const {
|
|
1783
|
-
delay
|
|
1784
|
-
} = delayedTransition;
|
|
1785
|
-
return {
|
|
1786
|
-
...formatTransition(stateNode, delayedTransition.event, delayedTransition),
|
|
1787
|
-
delay
|
|
1788
|
-
};
|
|
1789
|
-
});
|
|
1790
|
-
}
|
|
1791
|
-
function formatTransition(stateNode, descriptor, transitionConfig) {
|
|
1792
|
-
const normalizedTarget = normalizeTarget(transitionConfig.target);
|
|
1793
|
-
const reenter = transitionConfig.reenter ?? false;
|
|
1794
|
-
const target = resolveTarget(stateNode, normalizedTarget);
|
|
1795
|
-
|
|
1796
|
-
// TODO: should this be part of a lint rule instead?
|
|
1797
|
-
if (transitionConfig.cond) {
|
|
1798
|
-
throw new Error(`State "${stateNode.id}" has declared \`cond\` for one of its transitions. This property has been renamed to \`guard\`. Please update your code.`);
|
|
1799
|
-
}
|
|
1800
|
-
const transition = {
|
|
1801
|
-
...transitionConfig,
|
|
1802
|
-
actions: toArray(transitionConfig.actions),
|
|
1803
|
-
guard: transitionConfig.guard,
|
|
1804
|
-
target,
|
|
1805
|
-
source: stateNode,
|
|
1806
|
-
reenter,
|
|
1807
|
-
eventType: descriptor,
|
|
1808
|
-
toJSON: () => ({
|
|
1809
|
-
...transition,
|
|
1810
|
-
source: `#${stateNode.id}`,
|
|
1811
|
-
target: target ? target.map(t => `#${t.id}`) : undefined
|
|
1812
|
-
})
|
|
1813
|
-
};
|
|
1814
|
-
return transition;
|
|
1815
|
-
}
|
|
1816
|
-
function formatTransitions(stateNode) {
|
|
1817
|
-
const transitions = new Map();
|
|
1818
|
-
if (stateNode.config.on) {
|
|
1819
|
-
for (const descriptor of Object.keys(stateNode.config.on)) {
|
|
1820
|
-
if (descriptor === NULL_EVENT) {
|
|
1821
|
-
throw new Error('Null events ("") cannot be specified as a transition key. Use `always: { ... }` instead.');
|
|
1822
|
-
}
|
|
1823
|
-
const transitionsConfig = stateNode.config.on[descriptor];
|
|
1824
|
-
transitions.set(descriptor, toTransitionConfigArray(transitionsConfig).map(t => formatTransition(stateNode, descriptor, t)));
|
|
1825
|
-
}
|
|
1826
|
-
}
|
|
1827
|
-
if (stateNode.config.onDone) {
|
|
1828
|
-
const descriptor = String(done(stateNode.id));
|
|
1829
|
-
transitions.set(descriptor, toTransitionConfigArray(stateNode.config.onDone).map(t => formatTransition(stateNode, descriptor, t)));
|
|
1830
|
-
}
|
|
1831
|
-
for (const invokeDef of stateNode.invoke) {
|
|
1832
|
-
if (invokeDef.onDone) {
|
|
1833
|
-
const descriptor = `done.invoke.${invokeDef.id}`;
|
|
1834
|
-
transitions.set(descriptor, toTransitionConfigArray(invokeDef.onDone).map(t => formatTransition(stateNode, descriptor, t)));
|
|
1835
|
-
}
|
|
1836
|
-
if (invokeDef.onError) {
|
|
1837
|
-
const descriptor = `error.platform.${invokeDef.id}`;
|
|
1838
|
-
transitions.set(descriptor, toTransitionConfigArray(invokeDef.onError).map(t => formatTransition(stateNode, descriptor, t)));
|
|
1839
|
-
}
|
|
1840
|
-
if (invokeDef.onSnapshot) {
|
|
1841
|
-
const descriptor = `xstate.snapshot.${invokeDef.id}`;
|
|
1842
|
-
transitions.set(descriptor, toTransitionConfigArray(invokeDef.onSnapshot).map(t => formatTransition(stateNode, descriptor, t)));
|
|
1843
|
-
}
|
|
1844
|
-
}
|
|
1845
|
-
for (const delayedTransition of stateNode.after) {
|
|
1846
|
-
let existing = transitions.get(delayedTransition.eventType);
|
|
1847
|
-
if (!existing) {
|
|
1848
|
-
existing = [];
|
|
1849
|
-
transitions.set(delayedTransition.eventType, existing);
|
|
1850
|
-
}
|
|
1851
|
-
existing.push(delayedTransition);
|
|
1852
|
-
}
|
|
1853
|
-
return transitions;
|
|
1854
|
-
}
|
|
1855
|
-
function formatInitialTransition(stateNode, _target) {
|
|
1856
|
-
if (typeof _target === 'string' || isArray(_target)) {
|
|
1857
|
-
const targets = toArray(_target).map(t => {
|
|
1858
|
-
// Resolve state string keys (which represent children)
|
|
1859
|
-
// to their state node
|
|
1860
|
-
const descStateNode = typeof t === 'string' ? isStateId(t) ? stateNode.machine.getStateNodeById(t) : stateNode.states[t] : t;
|
|
1861
|
-
if (!descStateNode) {
|
|
1862
|
-
throw new Error(`Initial state node "${t}" not found on parent state node #${stateNode.id}`);
|
|
1863
|
-
}
|
|
1864
|
-
if (!isDescendant(descStateNode, stateNode)) {
|
|
1865
|
-
throw new Error(`Invalid initial target: state node #${descStateNode.id} is not a descendant of #${stateNode.id}`);
|
|
1866
|
-
}
|
|
1867
|
-
return descStateNode;
|
|
1868
|
-
});
|
|
1869
|
-
const resolvedTarget = resolveTarget(stateNode, targets);
|
|
1870
|
-
const transition = {
|
|
1871
|
-
source: stateNode,
|
|
1872
|
-
actions: [],
|
|
1873
|
-
eventType: null,
|
|
1874
|
-
reenter: false,
|
|
1875
|
-
target: resolvedTarget,
|
|
1876
|
-
toJSON: () => ({
|
|
1877
|
-
...transition,
|
|
1878
|
-
source: `#${stateNode.id}`,
|
|
1879
|
-
target: resolvedTarget ? resolvedTarget.map(t => `#${t.id}`) : undefined
|
|
1880
|
-
})
|
|
1881
|
-
};
|
|
1882
|
-
return transition;
|
|
1883
|
-
}
|
|
1884
|
-
return formatTransition(stateNode, '__INITIAL__', {
|
|
1885
|
-
target: toArray(_target.target).map(t => {
|
|
1886
|
-
if (typeof t === 'string') {
|
|
1887
|
-
return isStateId(t) ? t : `${STATE_DELIMITER}${t}`;
|
|
1888
|
-
}
|
|
1889
|
-
return t;
|
|
1890
|
-
}),
|
|
1891
|
-
actions: _target.actions
|
|
1892
|
-
});
|
|
1893
|
-
}
|
|
1894
|
-
function resolveTarget(stateNode, targets) {
|
|
1895
|
-
if (targets === undefined) {
|
|
1896
|
-
// an undefined target signals that the state node should not transition from that state when receiving that event
|
|
1897
|
-
return undefined;
|
|
1898
|
-
}
|
|
1899
|
-
return targets.map(target => {
|
|
1900
|
-
if (typeof target !== 'string') {
|
|
1901
|
-
return target;
|
|
1902
|
-
}
|
|
1903
|
-
if (isStateId(target)) {
|
|
1904
|
-
return stateNode.machine.getStateNodeById(target);
|
|
1905
|
-
}
|
|
1906
|
-
const isInternalTarget = target[0] === STATE_DELIMITER;
|
|
1907
|
-
// If internal target is defined on machine,
|
|
1908
|
-
// do not include machine key on target
|
|
1909
|
-
if (isInternalTarget && !stateNode.parent) {
|
|
1910
|
-
return getStateNodeByPath(stateNode, target.slice(1));
|
|
1911
|
-
}
|
|
1912
|
-
const resolvedTarget = isInternalTarget ? stateNode.key + target : target;
|
|
1913
|
-
if (stateNode.parent) {
|
|
1914
|
-
try {
|
|
1915
|
-
const targetStateNode = getStateNodeByPath(stateNode.parent, resolvedTarget);
|
|
1916
|
-
return targetStateNode;
|
|
1917
|
-
} catch (err) {
|
|
1918
|
-
throw new Error(`Invalid transition definition for state node '${stateNode.id}':\n${err.message}`);
|
|
1919
|
-
}
|
|
1920
|
-
} else {
|
|
1921
|
-
throw new Error(`Invalid target: "${target}" is not a valid target from the root node. Did you mean ".${target}"?`);
|
|
1922
|
-
}
|
|
1923
|
-
});
|
|
1924
|
-
}
|
|
1925
|
-
function resolveHistoryTarget(stateNode) {
|
|
1926
|
-
const normalizedTarget = normalizeTarget(stateNode.target);
|
|
1927
|
-
if (!normalizedTarget) {
|
|
1928
|
-
return stateNode.parent.initial.target;
|
|
1929
|
-
}
|
|
1930
|
-
return normalizedTarget.map(t => typeof t === 'string' ? getStateNodeByPath(stateNode.parent, t) : t);
|
|
1931
|
-
}
|
|
1932
|
-
function isHistoryNode(stateNode) {
|
|
1933
|
-
return stateNode.type === 'history';
|
|
1934
|
-
}
|
|
1935
|
-
function getInitialStateNodes(stateNode) {
|
|
1936
|
-
const set = new Set();
|
|
1937
|
-
function iter(descStateNode) {
|
|
1938
|
-
if (set.has(descStateNode)) {
|
|
1939
|
-
return;
|
|
1940
|
-
}
|
|
1941
|
-
set.add(descStateNode);
|
|
1942
|
-
if (descStateNode.type === 'compound') {
|
|
1943
|
-
for (const targetStateNode of descStateNode.initial.target) {
|
|
1944
|
-
for (const a of getProperAncestors(targetStateNode, stateNode)) {
|
|
1945
|
-
set.add(a);
|
|
1946
|
-
}
|
|
1947
|
-
iter(targetStateNode);
|
|
1948
|
-
}
|
|
1949
|
-
} else if (descStateNode.type === 'parallel') {
|
|
1950
|
-
for (const child of getChildren(descStateNode)) {
|
|
1951
|
-
iter(child);
|
|
1952
|
-
}
|
|
1953
|
-
}
|
|
1954
|
-
}
|
|
1955
|
-
iter(stateNode);
|
|
1956
|
-
return [...set];
|
|
1957
|
-
}
|
|
1958
|
-
/**
|
|
1959
|
-
* Returns the child state node from its relative `stateKey`, or throws.
|
|
1960
|
-
*/
|
|
1961
|
-
function getStateNode(stateNode, stateKey) {
|
|
1962
|
-
if (isStateId(stateKey)) {
|
|
1963
|
-
return stateNode.machine.getStateNodeById(stateKey);
|
|
1964
|
-
}
|
|
1965
|
-
if (!stateNode.states) {
|
|
1966
|
-
throw new Error(`Unable to retrieve child state '${stateKey}' from '${stateNode.id}'; no child states exist.`);
|
|
1967
|
-
}
|
|
1968
|
-
const result = stateNode.states[stateKey];
|
|
1969
|
-
if (!result) {
|
|
1970
|
-
throw new Error(`Child state '${stateKey}' does not exist on '${stateNode.id}'`);
|
|
1971
|
-
}
|
|
1972
|
-
return result;
|
|
1973
|
-
}
|
|
1974
|
-
|
|
1975
|
-
/**
|
|
1976
|
-
* Returns the relative state node from the given `statePath`, or throws.
|
|
1977
|
-
*
|
|
1978
|
-
* @param statePath The string or string array relative path to the state node.
|
|
1979
|
-
*/
|
|
1980
|
-
function getStateNodeByPath(stateNode, statePath) {
|
|
1981
|
-
if (typeof statePath === 'string' && isStateId(statePath)) {
|
|
1982
|
-
try {
|
|
1983
|
-
return stateNode.machine.getStateNodeById(statePath);
|
|
1984
|
-
} catch (e) {
|
|
1985
|
-
// try individual paths
|
|
1986
|
-
// throw e;
|
|
1987
|
-
}
|
|
1988
|
-
}
|
|
1989
|
-
const arrayStatePath = toStatePath(statePath).slice();
|
|
1990
|
-
let currentStateNode = stateNode;
|
|
1991
|
-
while (arrayStatePath.length) {
|
|
1992
|
-
const key = arrayStatePath.shift();
|
|
1993
|
-
if (!key.length) {
|
|
1994
|
-
break;
|
|
1995
|
-
}
|
|
1996
|
-
currentStateNode = getStateNode(currentStateNode, key);
|
|
1997
|
-
}
|
|
1998
|
-
return currentStateNode;
|
|
1999
|
-
}
|
|
2000
|
-
|
|
2001
|
-
/**
|
|
2002
|
-
* Returns the state nodes represented by the current state value.
|
|
2003
|
-
*
|
|
2004
|
-
* @param state The state value or State instance
|
|
2005
|
-
*/
|
|
2006
|
-
function getStateNodes(stateNode, state) {
|
|
2007
|
-
const stateValue = state instanceof State ? state.value : toStateValue(state);
|
|
2008
|
-
if (typeof stateValue === 'string') {
|
|
2009
|
-
return [stateNode, stateNode.states[stateValue]];
|
|
2010
|
-
}
|
|
2011
|
-
const childStateKeys = Object.keys(stateValue);
|
|
2012
|
-
const childStateNodes = childStateKeys.map(subStateKey => getStateNode(stateNode, subStateKey)).filter(Boolean);
|
|
2013
|
-
return [stateNode.machine.root, stateNode].concat(childStateNodes, childStateKeys.reduce((allSubStateNodes, subStateKey) => {
|
|
2014
|
-
const subStateNode = getStateNode(stateNode, subStateKey);
|
|
2015
|
-
if (!subStateNode) {
|
|
2016
|
-
return allSubStateNodes;
|
|
2017
|
-
}
|
|
2018
|
-
const subStateNodes = getStateNodes(subStateNode, stateValue[subStateKey]);
|
|
2019
|
-
return allSubStateNodes.concat(subStateNodes);
|
|
2020
|
-
}, []));
|
|
2021
|
-
}
|
|
2022
|
-
function transitionAtomicNode(stateNode, stateValue, state, event) {
|
|
2023
|
-
const childStateNode = getStateNode(stateNode, stateValue);
|
|
2024
|
-
const next = childStateNode.next(state, event);
|
|
2025
|
-
if (!next || !next.length) {
|
|
2026
|
-
return stateNode.next(state, event);
|
|
2027
|
-
}
|
|
2028
|
-
return next;
|
|
2029
|
-
}
|
|
2030
|
-
function transitionCompoundNode(stateNode, stateValue, state, event) {
|
|
2031
|
-
const subStateKeys = Object.keys(stateValue);
|
|
2032
|
-
const childStateNode = getStateNode(stateNode, subStateKeys[0]);
|
|
2033
|
-
const next = transitionNode(childStateNode, stateValue[subStateKeys[0]], state, event);
|
|
2034
|
-
if (!next || !next.length) {
|
|
2035
|
-
return stateNode.next(state, event);
|
|
2036
|
-
}
|
|
2037
|
-
return next;
|
|
2038
|
-
}
|
|
2039
|
-
function transitionParallelNode(stateNode, stateValue, state, event) {
|
|
2040
|
-
const allInnerTransitions = [];
|
|
2041
|
-
for (const subStateKey of Object.keys(stateValue)) {
|
|
2042
|
-
const subStateValue = stateValue[subStateKey];
|
|
2043
|
-
if (!subStateValue) {
|
|
2044
|
-
continue;
|
|
2045
|
-
}
|
|
2046
|
-
const subStateNode = getStateNode(stateNode, subStateKey);
|
|
2047
|
-
const innerTransitions = transitionNode(subStateNode, subStateValue, state, event);
|
|
2048
|
-
if (innerTransitions) {
|
|
2049
|
-
allInnerTransitions.push(...innerTransitions);
|
|
2050
|
-
}
|
|
2051
|
-
}
|
|
2052
|
-
if (!allInnerTransitions.length) {
|
|
2053
|
-
return stateNode.next(state, event);
|
|
2054
|
-
}
|
|
2055
|
-
return allInnerTransitions;
|
|
2056
|
-
}
|
|
2057
|
-
function transitionNode(stateNode, stateValue, state, event) {
|
|
2058
|
-
// leaf node
|
|
2059
|
-
if (typeof stateValue === 'string') {
|
|
2060
|
-
return transitionAtomicNode(stateNode, stateValue, state, event);
|
|
2061
|
-
}
|
|
2062
|
-
|
|
2063
|
-
// compound node
|
|
2064
|
-
if (Object.keys(stateValue).length === 1) {
|
|
2065
|
-
return transitionCompoundNode(stateNode, stateValue, state, event);
|
|
2066
|
-
}
|
|
2067
|
-
|
|
2068
|
-
// parallel node
|
|
2069
|
-
return transitionParallelNode(stateNode, stateValue, state, event);
|
|
2070
|
-
}
|
|
2071
|
-
function getHistoryNodes(stateNode) {
|
|
2072
|
-
return Object.keys(stateNode.states).map(key => stateNode.states[key]).filter(sn => sn.type === 'history');
|
|
2073
|
-
}
|
|
2074
|
-
function isDescendant(childStateNode, parentStateNode) {
|
|
2075
|
-
let marker = childStateNode;
|
|
2076
|
-
while (marker.parent && marker.parent !== parentStateNode) {
|
|
2077
|
-
marker = marker.parent;
|
|
2078
|
-
}
|
|
2079
|
-
return marker.parent === parentStateNode;
|
|
2080
|
-
}
|
|
2081
|
-
function getPathFromRootToNode(stateNode) {
|
|
2082
|
-
const path = [];
|
|
2083
|
-
let marker = stateNode.parent;
|
|
2084
|
-
while (marker) {
|
|
2085
|
-
path.unshift(marker);
|
|
2086
|
-
marker = marker.parent;
|
|
2087
|
-
}
|
|
2088
|
-
return path;
|
|
2089
|
-
}
|
|
2090
|
-
function hasIntersection(s1, s2) {
|
|
2091
|
-
const set1 = new Set(s1);
|
|
2092
|
-
const set2 = new Set(s2);
|
|
2093
|
-
for (const item of set1) {
|
|
2094
|
-
if (set2.has(item)) {
|
|
2095
|
-
return true;
|
|
2096
|
-
}
|
|
2097
|
-
}
|
|
2098
|
-
for (const item of set2) {
|
|
2099
|
-
if (set1.has(item)) {
|
|
2100
|
-
return true;
|
|
2101
|
-
}
|
|
2102
|
-
}
|
|
2103
|
-
return false;
|
|
2104
|
-
}
|
|
2105
|
-
function removeConflictingTransitions(enabledTransitions, configuration, historyValue) {
|
|
2106
|
-
const filteredTransitions = new Set();
|
|
2107
|
-
for (const t1 of enabledTransitions) {
|
|
2108
|
-
let t1Preempted = false;
|
|
2109
|
-
const transitionsToRemove = new Set();
|
|
2110
|
-
for (const t2 of filteredTransitions) {
|
|
2111
|
-
if (hasIntersection(computeExitSet([t1], configuration, historyValue), computeExitSet([t2], configuration, historyValue))) {
|
|
2112
|
-
if (isDescendant(t1.source, t2.source)) {
|
|
2113
|
-
transitionsToRemove.add(t2);
|
|
2114
|
-
} else {
|
|
2115
|
-
t1Preempted = true;
|
|
2116
|
-
break;
|
|
2117
|
-
}
|
|
2118
|
-
}
|
|
2119
|
-
}
|
|
2120
|
-
if (!t1Preempted) {
|
|
2121
|
-
for (const t3 of transitionsToRemove) {
|
|
2122
|
-
filteredTransitions.delete(t3);
|
|
2123
|
-
}
|
|
2124
|
-
filteredTransitions.add(t1);
|
|
2125
|
-
}
|
|
2126
|
-
}
|
|
2127
|
-
return Array.from(filteredTransitions);
|
|
2128
|
-
}
|
|
2129
|
-
function findLCCA(stateNodes) {
|
|
2130
|
-
const [head] = stateNodes;
|
|
2131
|
-
let current = getPathFromRootToNode(head);
|
|
2132
|
-
let candidates = [];
|
|
2133
|
-
for (const stateNode of stateNodes) {
|
|
2134
|
-
const path = getPathFromRootToNode(stateNode);
|
|
2135
|
-
candidates = current.filter(sn => path.includes(sn));
|
|
2136
|
-
current = candidates;
|
|
2137
|
-
candidates = [];
|
|
2138
|
-
}
|
|
2139
|
-
return current[current.length - 1];
|
|
2140
|
-
}
|
|
2141
|
-
function getEffectiveTargetStates(transition, historyValue) {
|
|
2142
|
-
if (!transition.target) {
|
|
2143
|
-
return [];
|
|
2144
|
-
}
|
|
2145
|
-
const targets = new Set();
|
|
2146
|
-
for (const targetNode of transition.target) {
|
|
2147
|
-
if (isHistoryNode(targetNode)) {
|
|
2148
|
-
if (historyValue[targetNode.id]) {
|
|
2149
|
-
for (const node of historyValue[targetNode.id]) {
|
|
2150
|
-
targets.add(node);
|
|
2151
|
-
}
|
|
2152
|
-
} else {
|
|
2153
|
-
for (const node of getEffectiveTargetStates({
|
|
2154
|
-
target: resolveHistoryTarget(targetNode)
|
|
2155
|
-
}, historyValue)) {
|
|
2156
|
-
targets.add(node);
|
|
2157
|
-
}
|
|
2158
|
-
}
|
|
2159
|
-
} else {
|
|
2160
|
-
targets.add(targetNode);
|
|
2161
|
-
}
|
|
2162
|
-
}
|
|
2163
|
-
return [...targets];
|
|
2164
|
-
}
|
|
2165
|
-
function getTransitionDomain(transition, historyValue) {
|
|
2166
|
-
const targetStates = getEffectiveTargetStates(transition, historyValue);
|
|
2167
|
-
if (!targetStates) {
|
|
2168
|
-
return null;
|
|
2169
|
-
}
|
|
2170
|
-
if (!transition.reenter && transition.source.type !== 'parallel' && targetStates.every(targetStateNode => isDescendant(targetStateNode, transition.source))) {
|
|
2171
|
-
return transition.source;
|
|
2172
|
-
}
|
|
2173
|
-
const lcca = findLCCA(targetStates.concat(transition.source));
|
|
2174
|
-
return lcca;
|
|
2175
|
-
}
|
|
2176
|
-
function computeExitSet(transitions, configuration, historyValue) {
|
|
2177
|
-
const statesToExit = new Set();
|
|
2178
|
-
for (const t of transitions) {
|
|
2179
|
-
if (t.target?.length) {
|
|
2180
|
-
const domain = getTransitionDomain(t, historyValue);
|
|
2181
|
-
for (const stateNode of configuration) {
|
|
2182
|
-
if (isDescendant(stateNode, domain)) {
|
|
2183
|
-
statesToExit.add(stateNode);
|
|
2184
|
-
}
|
|
2185
|
-
}
|
|
2186
|
-
}
|
|
2187
|
-
}
|
|
2188
|
-
return [...statesToExit];
|
|
2189
|
-
}
|
|
2190
|
-
|
|
2191
|
-
/**
|
|
2192
|
-
* https://www.w3.org/TR/scxml/#microstepProcedure
|
|
2193
|
-
*
|
|
2194
|
-
* @private
|
|
2195
|
-
* @param transitions
|
|
2196
|
-
* @param currentState
|
|
2197
|
-
* @param mutConfiguration
|
|
2198
|
-
*/
|
|
2199
|
-
|
|
2200
|
-
function microstep(transitions, currentState, actorCtx, event, isInitial) {
|
|
2201
|
-
const mutConfiguration = new Set(currentState.configuration);
|
|
2202
|
-
if (!transitions.length) {
|
|
2203
|
-
return currentState;
|
|
2204
|
-
}
|
|
2205
|
-
const microstate = microstepProcedure(transitions, currentState, mutConfiguration, event, actorCtx, isInitial);
|
|
2206
|
-
return cloneState(microstate, {
|
|
2207
|
-
value: {} // TODO: make optional
|
|
2208
|
-
});
|
|
2209
|
-
}
|
|
2210
|
-
|
|
2211
|
-
function microstepProcedure(transitions, currentState, mutConfiguration, event, actorCtx, isInitial) {
|
|
2212
|
-
const actions = [];
|
|
2213
|
-
const historyValue = {
|
|
2214
|
-
...currentState.historyValue
|
|
2215
|
-
};
|
|
2216
|
-
const filteredTransitions = removeConflictingTransitions(transitions, mutConfiguration, historyValue);
|
|
2217
|
-
const internalQueue = [...currentState._internalQueue];
|
|
2218
|
-
|
|
2219
|
-
// Exit states
|
|
2220
|
-
if (!isInitial) {
|
|
2221
|
-
exitStates(filteredTransitions, mutConfiguration, historyValue, actions);
|
|
2222
|
-
}
|
|
2223
|
-
|
|
2224
|
-
// Execute transition content
|
|
2225
|
-
actions.push(...filteredTransitions.flatMap(t => t.actions));
|
|
2226
|
-
|
|
2227
|
-
// Enter states
|
|
2228
|
-
enterStates(event, filteredTransitions, mutConfiguration, actions, internalQueue, currentState, historyValue, isInitial, actorCtx);
|
|
2229
|
-
const nextConfiguration = [...mutConfiguration];
|
|
2230
|
-
const done = isInFinalState(nextConfiguration);
|
|
2231
|
-
if (done) {
|
|
2232
|
-
const finalActions = nextConfiguration.sort((a, b) => b.order - a.order).flatMap(state => state.exit);
|
|
2233
|
-
actions.push(...finalActions);
|
|
2234
|
-
}
|
|
2235
|
-
try {
|
|
2236
|
-
const nextState = resolveActionsAndContext(actions, event, currentState, actorCtx);
|
|
2237
|
-
const output = done ? getOutput(nextConfiguration, nextState.context, event, actorCtx.self) : undefined;
|
|
2238
|
-
internalQueue.push(...nextState._internalQueue);
|
|
2239
|
-
return cloneState(currentState, {
|
|
2240
|
-
configuration: nextConfiguration,
|
|
2241
|
-
historyValue,
|
|
2242
|
-
_internalQueue: internalQueue,
|
|
2243
|
-
context: nextState.context,
|
|
2244
|
-
done,
|
|
2245
|
-
output,
|
|
2246
|
-
children: nextState.children
|
|
2247
|
-
});
|
|
2248
|
-
} catch (e) {
|
|
2249
|
-
// TODO: Refactor this once proper error handling is implemented.
|
|
2250
|
-
// See https://github.com/statelyai/rfcs/pull/4
|
|
2251
|
-
throw e;
|
|
2252
|
-
}
|
|
2253
|
-
}
|
|
2254
|
-
function enterStates(event, filteredTransitions, mutConfiguration, actions, internalQueue, currentState, historyValue, isInitial, actorContext) {
|
|
2255
|
-
const statesToEnter = new Set();
|
|
2256
|
-
const statesForDefaultEntry = new Set();
|
|
2257
|
-
computeEntrySet(filteredTransitions, historyValue, statesForDefaultEntry, statesToEnter);
|
|
2258
|
-
|
|
2259
|
-
// In the initial state, the root state node is "entered".
|
|
2260
|
-
if (isInitial) {
|
|
2261
|
-
statesForDefaultEntry.add(currentState.machine.root);
|
|
2262
|
-
}
|
|
2263
|
-
for (const stateNodeToEnter of [...statesToEnter].sort((a, b) => a.order - b.order)) {
|
|
2264
|
-
mutConfiguration.add(stateNodeToEnter);
|
|
2265
|
-
for (const invokeDef of stateNodeToEnter.invoke) {
|
|
2266
|
-
actions.push(invoke(invokeDef));
|
|
2267
|
-
}
|
|
2268
|
-
|
|
2269
|
-
// Add entry actions
|
|
2270
|
-
actions.push(...stateNodeToEnter.entry);
|
|
2271
|
-
if (statesForDefaultEntry.has(stateNodeToEnter)) {
|
|
2272
|
-
for (const stateNode of statesForDefaultEntry) {
|
|
2273
|
-
const initialActions = stateNode.initial.actions;
|
|
2274
|
-
actions.push(...initialActions);
|
|
2275
|
-
}
|
|
2276
|
-
}
|
|
2277
|
-
if (stateNodeToEnter.type === 'final') {
|
|
2278
|
-
const parent = stateNodeToEnter.parent;
|
|
2279
|
-
if (!parent.parent) {
|
|
2280
|
-
continue;
|
|
2281
|
-
}
|
|
2282
|
-
internalQueue.push(done(parent.id, stateNodeToEnter.output ? mapContext(stateNodeToEnter.output, currentState.context, event, actorContext.self) : undefined));
|
|
2283
|
-
if (parent.parent) {
|
|
2284
|
-
const grandparent = parent.parent;
|
|
2285
|
-
if (grandparent.type === 'parallel') {
|
|
2286
|
-
if (getChildren(grandparent).every(parentNode => isInFinalState([...mutConfiguration], parentNode))) {
|
|
2287
|
-
internalQueue.push(done(grandparent.id));
|
|
2288
|
-
}
|
|
2289
|
-
}
|
|
2290
|
-
}
|
|
2291
|
-
}
|
|
2292
|
-
}
|
|
2293
|
-
}
|
|
2294
|
-
function computeEntrySet(transitions, historyValue, statesForDefaultEntry, statesToEnter) {
|
|
2295
|
-
for (const t of transitions) {
|
|
2296
|
-
for (const s of t.target || []) {
|
|
2297
|
-
addDescendantStatesToEnter(s, historyValue, statesForDefaultEntry, statesToEnter);
|
|
2298
|
-
}
|
|
2299
|
-
const ancestor = getTransitionDomain(t, historyValue);
|
|
2300
|
-
const targetStates = getEffectiveTargetStates(t, historyValue);
|
|
2301
|
-
for (const s of targetStates) {
|
|
2302
|
-
addAncestorStatesToEnter(s, ancestor, statesToEnter, historyValue, statesForDefaultEntry);
|
|
2303
|
-
}
|
|
2304
|
-
}
|
|
2305
|
-
}
|
|
2306
|
-
function addDescendantStatesToEnter(stateNode, historyValue, statesForDefaultEntry, statesToEnter) {
|
|
2307
|
-
if (isHistoryNode(stateNode)) {
|
|
2308
|
-
if (historyValue[stateNode.id]) {
|
|
2309
|
-
const historyStateNodes = historyValue[stateNode.id];
|
|
2310
|
-
for (const s of historyStateNodes) {
|
|
2311
|
-
addDescendantStatesToEnter(s, historyValue, statesForDefaultEntry, statesToEnter);
|
|
2312
|
-
}
|
|
2313
|
-
for (const s of historyStateNodes) {
|
|
2314
|
-
addAncestorStatesToEnter(s, stateNode.parent, statesToEnter, historyValue, statesForDefaultEntry);
|
|
2315
|
-
for (const stateForDefaultEntry of statesForDefaultEntry) {
|
|
2316
|
-
statesForDefaultEntry.add(stateForDefaultEntry);
|
|
2317
|
-
}
|
|
2318
|
-
}
|
|
2319
|
-
} else {
|
|
2320
|
-
const targets = resolveHistoryTarget(stateNode);
|
|
2321
|
-
for (const s of targets) {
|
|
2322
|
-
addDescendantStatesToEnter(s, historyValue, statesForDefaultEntry, statesToEnter);
|
|
2323
|
-
}
|
|
2324
|
-
for (const s of targets) {
|
|
2325
|
-
addAncestorStatesToEnter(s, stateNode, statesToEnter, historyValue, statesForDefaultEntry);
|
|
2326
|
-
for (const stateForDefaultEntry of statesForDefaultEntry) {
|
|
2327
|
-
statesForDefaultEntry.add(stateForDefaultEntry);
|
|
2328
|
-
}
|
|
2329
|
-
}
|
|
2330
|
-
}
|
|
2331
|
-
} else {
|
|
2332
|
-
statesToEnter.add(stateNode);
|
|
2333
|
-
if (stateNode.type === 'compound') {
|
|
2334
|
-
statesForDefaultEntry.add(stateNode);
|
|
2335
|
-
const initialStates = stateNode.initial.target;
|
|
2336
|
-
for (const initialState of initialStates) {
|
|
2337
|
-
addDescendantStatesToEnter(initialState, historyValue, statesForDefaultEntry, statesToEnter);
|
|
2338
|
-
}
|
|
2339
|
-
for (const initialState of initialStates) {
|
|
2340
|
-
addAncestorStatesToEnter(initialState, stateNode, statesToEnter, historyValue, statesForDefaultEntry);
|
|
2341
|
-
}
|
|
2342
|
-
} else {
|
|
2343
|
-
if (stateNode.type === 'parallel') {
|
|
2344
|
-
for (const child of getChildren(stateNode).filter(sn => !isHistoryNode(sn))) {
|
|
2345
|
-
if (![...statesToEnter].some(s => isDescendant(s, child))) {
|
|
2346
|
-
addDescendantStatesToEnter(child, historyValue, statesForDefaultEntry, statesToEnter);
|
|
2347
|
-
}
|
|
2348
|
-
}
|
|
2349
|
-
}
|
|
2350
|
-
}
|
|
2351
|
-
}
|
|
2352
|
-
}
|
|
2353
|
-
function addAncestorStatesToEnter(stateNode, toStateNode, statesToEnter, historyValue, statesForDefaultEntry) {
|
|
2354
|
-
const properAncestors = getProperAncestors(stateNode, toStateNode);
|
|
2355
|
-
for (const anc of properAncestors) {
|
|
2356
|
-
statesToEnter.add(anc);
|
|
2357
|
-
if (anc.type === 'parallel') {
|
|
2358
|
-
for (const child of getChildren(anc).filter(sn => !isHistoryNode(sn))) {
|
|
2359
|
-
if (![...statesToEnter].some(s => isDescendant(s, child))) {
|
|
2360
|
-
addDescendantStatesToEnter(child, historyValue, statesForDefaultEntry, statesToEnter);
|
|
2361
|
-
}
|
|
2362
|
-
}
|
|
2363
|
-
}
|
|
2364
|
-
}
|
|
2365
|
-
}
|
|
2366
|
-
function exitStates(transitions, mutConfiguration, historyValue, actions) {
|
|
2367
|
-
const statesToExit = computeExitSet(transitions, mutConfiguration, historyValue);
|
|
2368
|
-
statesToExit.sort((a, b) => b.order - a.order);
|
|
2369
|
-
|
|
2370
|
-
// From SCXML algorithm: https://www.w3.org/TR/scxml/#exitStates
|
|
2371
|
-
for (const exitStateNode of statesToExit) {
|
|
2372
|
-
for (const historyNode of getHistoryNodes(exitStateNode)) {
|
|
2373
|
-
let predicate;
|
|
2374
|
-
if (historyNode.history === 'deep') {
|
|
2375
|
-
predicate = sn => isAtomicStateNode(sn) && isDescendant(sn, exitStateNode);
|
|
2376
|
-
} else {
|
|
2377
|
-
predicate = sn => {
|
|
2378
|
-
return sn.parent === exitStateNode;
|
|
2379
|
-
};
|
|
2380
|
-
}
|
|
2381
|
-
historyValue[historyNode.id] = Array.from(mutConfiguration).filter(predicate);
|
|
2382
|
-
}
|
|
2383
|
-
}
|
|
2384
|
-
for (const s of statesToExit) {
|
|
2385
|
-
actions.push(...s.exit, ...s.invoke.map(def => stop(def.id)));
|
|
2386
|
-
mutConfiguration.delete(s);
|
|
2387
|
-
}
|
|
2388
|
-
}
|
|
2389
|
-
function resolveActionsAndContext(actions, event, currentState, actorCtx) {
|
|
2390
|
-
const {
|
|
2391
|
-
machine
|
|
2392
|
-
} = currentState;
|
|
2393
|
-
// TODO: this `cloneState` is really just a hack to prevent infinite loops
|
|
2394
|
-
// we need to take another look at how internal queue is managed
|
|
2395
|
-
let intermediateState = cloneState(currentState, {
|
|
2396
|
-
_internalQueue: []
|
|
2397
|
-
});
|
|
2398
|
-
for (const action of actions) {
|
|
2399
|
-
const isInline = typeof action === 'function';
|
|
2400
|
-
const resolved = isInline ? action :
|
|
2401
|
-
// the existing type of `.actions` assumes non-nullable `TExpressionAction`
|
|
2402
|
-
// it's fine to cast this here to get a common type and lack of errors in the rest of the code
|
|
2403
|
-
// our logic below makes sure that we call those 2 "variants" correctly
|
|
2404
|
-
machine.implementations.actions[typeof action === 'string' ? action : action.type];
|
|
2405
|
-
if (!resolved) {
|
|
2406
|
-
continue;
|
|
2407
|
-
}
|
|
2408
|
-
const actionArgs = {
|
|
2409
|
-
context: intermediateState.context,
|
|
2410
|
-
event,
|
|
2411
|
-
self: actorCtx?.self,
|
|
2412
|
-
system: actorCtx?.system,
|
|
2413
|
-
action: isInline ? undefined : typeof action === 'string' ? {
|
|
2414
|
-
type: action
|
|
2415
|
-
} : action
|
|
2416
|
-
};
|
|
2417
|
-
if (!('resolve' in resolved)) {
|
|
2418
|
-
if (actorCtx?.self.status === ActorStatus.Running) {
|
|
2419
|
-
resolved(actionArgs);
|
|
2420
|
-
} else {
|
|
2421
|
-
actorCtx?.defer(() => resolved(actionArgs));
|
|
2422
|
-
}
|
|
2423
|
-
continue;
|
|
2424
|
-
}
|
|
2425
|
-
const builtinAction = resolved;
|
|
2426
|
-
const [nextState, params, actions] = builtinAction.resolve(actorCtx, intermediateState, actionArgs, resolved // this holds all params
|
|
2427
|
-
);
|
|
2428
|
-
|
|
2429
|
-
intermediateState = nextState;
|
|
2430
|
-
if ('execute' in resolved) {
|
|
2431
|
-
if (actorCtx?.self.status === ActorStatus.Running) {
|
|
2432
|
-
builtinAction.execute(actorCtx, params);
|
|
2433
|
-
} else {
|
|
2434
|
-
actorCtx?.defer(builtinAction.execute.bind(null, actorCtx, params));
|
|
2435
|
-
}
|
|
2436
|
-
}
|
|
2437
|
-
if (actions) {
|
|
2438
|
-
intermediateState = resolveActionsAndContext(actions, event, intermediateState, actorCtx);
|
|
2439
|
-
}
|
|
2440
|
-
}
|
|
2441
|
-
return intermediateState;
|
|
2442
|
-
}
|
|
2443
|
-
function macrostep(state, event, actorCtx) {
|
|
2444
|
-
if (event.type === WILDCARD) {
|
|
2445
|
-
throw new Error(`An event cannot have the wildcard type ('${WILDCARD}')`);
|
|
2446
|
-
}
|
|
2447
|
-
let nextState = state;
|
|
2448
|
-
const states = [];
|
|
2449
|
-
|
|
2450
|
-
// Handle stop event
|
|
2451
|
-
if (event.type === stopSignalType) {
|
|
2452
|
-
nextState = stopStep(event, nextState, actorCtx);
|
|
2453
|
-
states.push(nextState);
|
|
2454
|
-
return {
|
|
2455
|
-
state: nextState,
|
|
2456
|
-
microstates: states
|
|
2457
|
-
};
|
|
2458
|
-
}
|
|
2459
|
-
let nextEvent = event;
|
|
2460
|
-
|
|
2461
|
-
// Assume the state is at rest (no raised events)
|
|
2462
|
-
// Determine the next state based on the next microstep
|
|
2463
|
-
if (nextEvent.type !== INIT_TYPE) {
|
|
2464
|
-
const transitions = selectTransitions(nextEvent, nextState);
|
|
2465
|
-
nextState = microstep(transitions, state, actorCtx, nextEvent, false);
|
|
2466
|
-
states.push(nextState);
|
|
2467
|
-
}
|
|
2468
|
-
while (!nextState.done) {
|
|
2469
|
-
let enabledTransitions = selectEventlessTransitions(nextState, nextEvent);
|
|
2470
|
-
if (!enabledTransitions.length) {
|
|
2471
|
-
if (!nextState._internalQueue.length) {
|
|
2472
|
-
break;
|
|
2473
|
-
} else {
|
|
2474
|
-
nextEvent = nextState._internalQueue[0];
|
|
2475
|
-
const transitions = selectTransitions(nextEvent, nextState);
|
|
2476
|
-
nextState = microstep(transitions, nextState, actorCtx, nextEvent, false);
|
|
2477
|
-
nextState._internalQueue.shift();
|
|
2478
|
-
states.push(nextState);
|
|
2479
|
-
}
|
|
2480
|
-
} else {
|
|
2481
|
-
nextState = microstep(enabledTransitions, nextState, actorCtx, nextEvent, false);
|
|
2482
|
-
states.push(nextState);
|
|
2483
|
-
}
|
|
2484
|
-
}
|
|
2485
|
-
if (nextState.done) {
|
|
2486
|
-
// Perform the stop step to ensure that child actors are stopped
|
|
2487
|
-
stopStep(nextEvent, nextState, actorCtx);
|
|
2488
|
-
}
|
|
2489
|
-
return {
|
|
2490
|
-
state: nextState,
|
|
2491
|
-
microstates: states
|
|
2492
|
-
};
|
|
2493
|
-
}
|
|
2494
|
-
function stopStep(event, nextState, actorCtx) {
|
|
2495
|
-
const actions = [];
|
|
2496
|
-
for (const stateNode of nextState.configuration.sort((a, b) => b.order - a.order)) {
|
|
2497
|
-
actions.push(...stateNode.exit);
|
|
2498
|
-
}
|
|
2499
|
-
for (const child of Object.values(nextState.children)) {
|
|
2500
|
-
actions.push(stop(child));
|
|
2501
|
-
}
|
|
2502
|
-
return resolveActionsAndContext(actions, event, nextState, actorCtx);
|
|
2503
|
-
}
|
|
2504
|
-
function selectTransitions(event, nextState) {
|
|
2505
|
-
return nextState.machine.getTransitionData(nextState, event);
|
|
2506
|
-
}
|
|
2507
|
-
function selectEventlessTransitions(nextState, event) {
|
|
2508
|
-
const enabledTransitionSet = new Set();
|
|
2509
|
-
const atomicStates = nextState.configuration.filter(isAtomicStateNode);
|
|
2510
|
-
for (const stateNode of atomicStates) {
|
|
2511
|
-
loop: for (const s of [stateNode].concat(getProperAncestors(stateNode, null))) {
|
|
2512
|
-
if (!s.always) {
|
|
2513
|
-
continue;
|
|
2514
|
-
}
|
|
2515
|
-
for (const transition of s.always) {
|
|
2516
|
-
if (transition.guard === undefined || evaluateGuard(transition.guard, nextState.context, event, nextState)) {
|
|
2517
|
-
enabledTransitionSet.add(transition);
|
|
2518
|
-
break loop;
|
|
2519
|
-
}
|
|
2520
|
-
}
|
|
2521
|
-
}
|
|
2522
|
-
}
|
|
2523
|
-
return removeConflictingTransitions(Array.from(enabledTransitionSet), new Set(nextState.configuration), nextState.historyValue);
|
|
2524
|
-
}
|
|
2525
|
-
|
|
2526
|
-
/**
|
|
2527
|
-
* Resolves a partial state value with its full representation in the state node's machine.
|
|
2528
|
-
*
|
|
2529
|
-
* @param stateValue The partial state value to resolve.
|
|
2530
|
-
*/
|
|
2531
|
-
function resolveStateValue(rootNode, stateValue) {
|
|
2532
|
-
const configuration = getConfiguration(getStateNodes(rootNode, stateValue));
|
|
2533
|
-
return getStateValue(rootNode, [...configuration]);
|
|
2534
|
-
}
|
|
2535
|
-
function getInitialConfiguration(rootNode) {
|
|
2536
|
-
const configuration = [];
|
|
2537
|
-
const initialTransition = rootNode.initial;
|
|
2538
|
-
const statesToEnter = new Set();
|
|
2539
|
-
const statesForDefaultEntry = new Set([rootNode]);
|
|
2540
|
-
computeEntrySet([initialTransition], {}, statesForDefaultEntry, statesToEnter);
|
|
2541
|
-
for (const stateNodeToEnter of [...statesToEnter].sort((a, b) => a.order - b.order)) {
|
|
2542
|
-
configuration.push(stateNodeToEnter);
|
|
2543
|
-
}
|
|
2544
|
-
return configuration;
|
|
2545
|
-
}
|
|
2546
|
-
|
|
2547
|
-
class State {
|
|
2548
|
-
/**
|
|
2549
|
-
* Indicates whether the state is a final state.
|
|
2550
|
-
*/
|
|
2551
|
-
|
|
2552
|
-
/**
|
|
2553
|
-
* The output data of the top-level finite state.
|
|
2554
|
-
*/
|
|
2555
|
-
|
|
2556
|
-
/**
|
|
2557
|
-
* The enabled state nodes representative of the state value.
|
|
2558
|
-
*/
|
|
2559
|
-
|
|
2560
|
-
/**
|
|
2561
|
-
* An object mapping actor names to spawned/invoked actors.
|
|
2562
|
-
*/
|
|
2563
|
-
|
|
2564
|
-
/**
|
|
2565
|
-
* Creates a new State instance for the given `stateValue` and `context`.
|
|
2566
|
-
* @param stateValue
|
|
2567
|
-
* @param context
|
|
2568
|
-
*/
|
|
2569
|
-
static from(stateValue, context = {}, machine) {
|
|
2570
|
-
if (stateValue instanceof State) {
|
|
2571
|
-
if (stateValue.context !== context) {
|
|
2572
|
-
return new State({
|
|
2573
|
-
value: stateValue.value,
|
|
2574
|
-
context,
|
|
2575
|
-
meta: {},
|
|
2576
|
-
configuration: [],
|
|
2577
|
-
// TODO: fix,
|
|
2578
|
-
children: {}
|
|
2579
|
-
}, machine);
|
|
2580
|
-
}
|
|
2581
|
-
return stateValue;
|
|
2582
|
-
}
|
|
2583
|
-
const configuration = getConfiguration(getStateNodes(machine.root, stateValue));
|
|
2584
|
-
return new State({
|
|
2585
|
-
value: stateValue,
|
|
2586
|
-
context,
|
|
2587
|
-
meta: undefined,
|
|
2588
|
-
configuration: Array.from(configuration),
|
|
2589
|
-
children: {}
|
|
2590
|
-
}, machine);
|
|
2591
|
-
}
|
|
2592
|
-
|
|
2593
|
-
/**
|
|
2594
|
-
* Creates a new `State` instance that represents the current state of a running machine.
|
|
2595
|
-
*
|
|
2596
|
-
* @param config
|
|
2597
|
-
*/
|
|
2598
|
-
constructor(config, machine) {
|
|
2599
|
-
this.machine = machine;
|
|
2600
|
-
this.tags = void 0;
|
|
2601
|
-
this.value = void 0;
|
|
2602
|
-
this.done = void 0;
|
|
2603
|
-
this.output = void 0;
|
|
2604
|
-
this.error = void 0;
|
|
2605
|
-
this.context = void 0;
|
|
2606
|
-
this.historyValue = {};
|
|
2607
|
-
this._internalQueue = void 0;
|
|
2608
|
-
this.configuration = void 0;
|
|
2609
|
-
this.children = void 0;
|
|
2610
|
-
this.context = config.context;
|
|
2611
|
-
this._internalQueue = config._internalQueue ?? [];
|
|
2612
|
-
this.historyValue = config.historyValue || {};
|
|
2613
|
-
this.matches = this.matches.bind(this);
|
|
2614
|
-
this.toStrings = this.toStrings.bind(this);
|
|
2615
|
-
this.configuration = config.configuration ?? Array.from(getConfiguration(getStateNodes(machine.root, config.value)));
|
|
2616
|
-
this.children = config.children;
|
|
2617
|
-
this.value = getStateValue(machine.root, this.configuration);
|
|
2618
|
-
this.tags = new Set(flatten(this.configuration.map(sn => sn.tags)));
|
|
2619
|
-
this.done = config.done ?? false;
|
|
2620
|
-
this.output = config.output;
|
|
2621
|
-
this.error = config.error;
|
|
2622
|
-
}
|
|
2623
|
-
|
|
2624
|
-
/**
|
|
2625
|
-
* Returns an array of all the string leaf state node paths.
|
|
2626
|
-
* @param stateValue
|
|
2627
|
-
* @param delimiter The character(s) that separate each subpath in the string state node path.
|
|
2628
|
-
*/
|
|
2629
|
-
toStrings(stateValue = this.value) {
|
|
2630
|
-
if (typeof stateValue === 'string') {
|
|
2631
|
-
return [stateValue];
|
|
2632
|
-
}
|
|
2633
|
-
const valueKeys = Object.keys(stateValue);
|
|
2634
|
-
return valueKeys.concat(...valueKeys.map(key => this.toStrings(stateValue[key]).map(s => key + STATE_DELIMITER + s)));
|
|
2635
|
-
}
|
|
2636
|
-
toJSON() {
|
|
2637
|
-
const {
|
|
2638
|
-
configuration,
|
|
2639
|
-
tags,
|
|
2640
|
-
machine,
|
|
2641
|
-
...jsonValues
|
|
2642
|
-
} = this;
|
|
2643
|
-
return {
|
|
2644
|
-
...jsonValues,
|
|
2645
|
-
tags: Array.from(tags),
|
|
2646
|
-
meta: this.meta
|
|
2647
|
-
};
|
|
2648
|
-
}
|
|
2649
|
-
|
|
2650
|
-
/**
|
|
2651
|
-
* Whether the current state value is a subset of the given parent state value.
|
|
2652
|
-
* @param parentStateValue
|
|
2653
|
-
*/
|
|
2654
|
-
matches(parentStateValue) {
|
|
2655
|
-
return matchesState(parentStateValue, this.value);
|
|
2656
|
-
}
|
|
2657
|
-
|
|
2658
|
-
/**
|
|
2659
|
-
* Whether the current state configuration has a state node with the specified `tag`.
|
|
2660
|
-
* @param tag
|
|
2661
|
-
*/
|
|
2662
|
-
hasTag(tag) {
|
|
2663
|
-
return this.tags.has(tag);
|
|
2664
|
-
}
|
|
2665
|
-
|
|
2666
|
-
/**
|
|
2667
|
-
* Determines whether sending the `event` will cause a non-forbidden transition
|
|
2668
|
-
* to be selected, even if the transitions have no actions nor
|
|
2669
|
-
* change the state value.
|
|
2670
|
-
*
|
|
2671
|
-
* @param event The event to test
|
|
2672
|
-
* @returns Whether the event will cause a transition
|
|
2673
|
-
*/
|
|
2674
|
-
can(event) {
|
|
2675
|
-
if (!this.machine) {
|
|
2676
|
-
console.warn(`state.can(...) used outside of a machine-created State object; this will always return false.`);
|
|
2677
|
-
}
|
|
2678
|
-
const transitionData = this.machine.getTransitionData(this, event);
|
|
2679
|
-
return !!transitionData?.length &&
|
|
2680
|
-
// Check that at least one transition is not forbidden
|
|
2681
|
-
transitionData.some(t => t.target !== undefined || t.actions.length);
|
|
2682
|
-
}
|
|
2683
|
-
|
|
2684
|
-
/**
|
|
2685
|
-
* The next events that will cause a transition from the current state.
|
|
2686
|
-
*/
|
|
2687
|
-
get nextEvents() {
|
|
2688
|
-
return memo(this, 'nextEvents', () => {
|
|
2689
|
-
return [...new Set(flatten([...this.configuration.map(sn => sn.ownEvents)]))];
|
|
2690
|
-
});
|
|
2691
|
-
}
|
|
2692
|
-
get meta() {
|
|
2693
|
-
return this.configuration.reduce((acc, stateNode) => {
|
|
2694
|
-
if (stateNode.meta !== undefined) {
|
|
2695
|
-
acc[stateNode.id] = stateNode.meta;
|
|
2696
|
-
}
|
|
2697
|
-
return acc;
|
|
2698
|
-
}, {});
|
|
2699
|
-
}
|
|
2700
|
-
}
|
|
2701
|
-
function cloneState(state, config = {}) {
|
|
2702
|
-
return new State({
|
|
2703
|
-
...state,
|
|
2704
|
-
...config
|
|
2705
|
-
}, state.machine);
|
|
2706
|
-
}
|
|
2707
|
-
function getPersistedState(state) {
|
|
2708
|
-
const {
|
|
2709
|
-
configuration,
|
|
2710
|
-
tags,
|
|
2711
|
-
machine,
|
|
2712
|
-
children,
|
|
2713
|
-
...jsonValues
|
|
2714
|
-
} = state;
|
|
2715
|
-
const childrenJson = {};
|
|
2716
|
-
for (const id in children) {
|
|
2717
|
-
childrenJson[id] = {
|
|
2718
|
-
state: children[id].getPersistedState?.(),
|
|
2719
|
-
src: children[id].src
|
|
2720
|
-
};
|
|
2721
|
-
}
|
|
2722
|
-
return {
|
|
2723
|
-
...jsonValues,
|
|
2724
|
-
children: childrenJson
|
|
2725
|
-
};
|
|
2726
|
-
}
|
|
2727
|
-
|
|
2728
|
-
function resolve$5(_, state, args, {
|
|
2729
|
-
actorRef
|
|
2730
|
-
}) {
|
|
2731
|
-
const actorRefOrString = typeof actorRef === 'function' ? actorRef(args) : actorRef;
|
|
2732
|
-
const resolvedActorRef = typeof actorRefOrString === 'string' ? state.children[actorRefOrString] : actorRefOrString;
|
|
2733
|
-
let children = state.children;
|
|
2734
|
-
if (resolvedActorRef) {
|
|
2735
|
-
children = {
|
|
2736
|
-
...children
|
|
2737
|
-
};
|
|
2738
|
-
delete children[resolvedActorRef.id];
|
|
2739
|
-
}
|
|
2740
|
-
return [cloneState(state, {
|
|
2741
|
-
children
|
|
2742
|
-
}), resolvedActorRef];
|
|
2743
|
-
}
|
|
2744
|
-
function execute$2(actorContext, actorRef) {
|
|
2745
|
-
if (!actorRef) {
|
|
2746
|
-
return;
|
|
2747
|
-
}
|
|
2748
|
-
if (actorRef.status !== ActorStatus.Running) {
|
|
2749
|
-
actorContext.stopChild(actorRef);
|
|
2750
|
-
return;
|
|
2751
|
-
}
|
|
2752
|
-
// TODO: recheck why this one has to be deferred
|
|
2753
|
-
actorContext.defer(() => {
|
|
2754
|
-
actorContext.stopChild(actorRef);
|
|
2755
|
-
});
|
|
2756
|
-
}
|
|
2757
|
-
|
|
2758
|
-
/**
|
|
2759
|
-
* Stops an actor.
|
|
2760
|
-
*
|
|
2761
|
-
* @param actorRef The actor to stop.
|
|
2762
|
-
*/
|
|
2763
|
-
|
|
2764
|
-
function stop(actorRef) {
|
|
2765
|
-
function stop(_) {
|
|
2766
|
-
{
|
|
2767
|
-
throw new Error(`This isn't supposed to be called`);
|
|
2768
|
-
}
|
|
2769
|
-
}
|
|
2770
|
-
stop.type = 'xstate.stop';
|
|
2771
|
-
stop.actorRef = actorRef;
|
|
2772
|
-
stop.resolve = resolve$5;
|
|
2773
|
-
stop.execute = execute$2;
|
|
2774
|
-
return stop;
|
|
2775
|
-
}
|
|
2776
|
-
|
|
2777
|
-
function resolve$4(_, state, actionArgs, {
|
|
2778
|
-
value,
|
|
2779
|
-
label
|
|
2780
|
-
}) {
|
|
2781
|
-
return [state, {
|
|
2782
|
-
value: typeof value === 'function' ? value(actionArgs) : value,
|
|
2783
|
-
label
|
|
2784
|
-
}];
|
|
2785
|
-
}
|
|
2786
|
-
function execute$1({
|
|
2787
|
-
logger
|
|
2788
|
-
}, {
|
|
2789
|
-
value,
|
|
2790
|
-
label
|
|
2791
|
-
}) {
|
|
2792
|
-
if (label) {
|
|
2793
|
-
logger(label, value);
|
|
2794
|
-
} else {
|
|
2795
|
-
logger(value);
|
|
2796
|
-
}
|
|
2797
|
-
}
|
|
2798
|
-
|
|
2799
|
-
/**
|
|
2800
|
-
*
|
|
2801
|
-
* @param expr The expression function to evaluate which will be logged.
|
|
2802
|
-
* Takes in 2 arguments:
|
|
2803
|
-
* - `ctx` - the current state context
|
|
2804
|
-
* - `event` - the event that caused this action to be executed.
|
|
2805
|
-
* @param label The label to give to the logged expression.
|
|
2806
|
-
*/
|
|
2807
|
-
function log(value = ({
|
|
2808
|
-
context,
|
|
2809
|
-
event
|
|
2810
|
-
}) => ({
|
|
2811
|
-
context,
|
|
2812
|
-
event
|
|
2813
|
-
}), label) {
|
|
2814
|
-
function log(_) {
|
|
2815
|
-
{
|
|
2816
|
-
throw new Error(`This isn't supposed to be called`);
|
|
2817
|
-
}
|
|
2818
|
-
}
|
|
2819
|
-
log.type = 'xstate.log';
|
|
2820
|
-
log.value = value;
|
|
2821
|
-
log.label = label;
|
|
2822
|
-
log.resolve = resolve$4;
|
|
2823
|
-
log.execute = execute$1;
|
|
2824
|
-
return log;
|
|
2825
|
-
}
|
|
2826
|
-
|
|
2827
|
-
function createSpawner(actorContext, {
|
|
2828
|
-
machine,
|
|
2829
|
-
context
|
|
2830
|
-
}, event, spawnedChildren) {
|
|
2831
|
-
const spawn = (src, options = {}) => {
|
|
2832
|
-
const {
|
|
2833
|
-
systemId
|
|
2834
|
-
} = options;
|
|
2835
|
-
if (typeof src === 'string') {
|
|
2836
|
-
const referenced = resolveReferencedActor(machine.implementations.actors[src]);
|
|
2837
|
-
if (!referenced) {
|
|
2838
|
-
throw new Error(`Actor logic '${src}' not implemented in machine '${machine.id}'`);
|
|
2839
|
-
}
|
|
2840
|
-
const input = 'input' in options ? options.input : referenced.input;
|
|
2841
|
-
|
|
2842
|
-
// TODO: this should also receive `src`
|
|
2843
|
-
const actor = createActor(referenced.src, {
|
|
2844
|
-
id: options.id,
|
|
2845
|
-
parent: actorContext.self,
|
|
2846
|
-
input: typeof input === 'function' ? input({
|
|
2847
|
-
context,
|
|
2848
|
-
event,
|
|
2849
|
-
self: actorContext.self
|
|
2850
|
-
}) : input,
|
|
2851
|
-
systemId
|
|
2852
|
-
});
|
|
2853
|
-
spawnedChildren[actor.id] = actor;
|
|
2854
|
-
return actor;
|
|
2855
|
-
} else {
|
|
2856
|
-
// TODO: this should also receive `src`
|
|
2857
|
-
return createActor(src, {
|
|
2858
|
-
id: options.id,
|
|
2859
|
-
parent: actorContext.self,
|
|
2860
|
-
input: options.input,
|
|
2861
|
-
systemId
|
|
2862
|
-
});
|
|
2863
|
-
}
|
|
2864
|
-
};
|
|
2865
|
-
return (src, options) => {
|
|
2866
|
-
const actorRef = spawn(src, options); // TODO: fix types
|
|
2867
|
-
spawnedChildren[actorRef.id] = actorRef;
|
|
2868
|
-
actorContext.defer(() => {
|
|
2869
|
-
if (actorRef.status === ActorStatus.Stopped) {
|
|
2870
|
-
return;
|
|
2871
|
-
}
|
|
2872
|
-
try {
|
|
2873
|
-
actorRef.start?.();
|
|
2874
|
-
} catch (err) {
|
|
2875
|
-
actorContext.self.send(error(actorRef.id, err));
|
|
2876
|
-
return;
|
|
2877
|
-
}
|
|
2878
|
-
});
|
|
2879
|
-
return actorRef;
|
|
2880
|
-
};
|
|
2881
|
-
}
|
|
2882
|
-
|
|
2883
|
-
function resolve$3(actorContext, state, actionArgs, {
|
|
2884
|
-
assignment
|
|
2885
|
-
}) {
|
|
2886
|
-
if (!state.context) {
|
|
2887
|
-
throw new Error('Cannot assign to undefined `context`. Ensure that `context` is defined in the machine config.');
|
|
2888
|
-
}
|
|
2889
|
-
const spawnedChildren = {};
|
|
2890
|
-
const assignArgs = {
|
|
2891
|
-
context: state.context,
|
|
2892
|
-
event: actionArgs.event,
|
|
2893
|
-
action: actionArgs.action,
|
|
2894
|
-
spawn: createSpawner(actorContext, state, actionArgs.event, spawnedChildren),
|
|
2895
|
-
self: actorContext?.self,
|
|
2896
|
-
system: actorContext?.system
|
|
2897
|
-
};
|
|
2898
|
-
let partialUpdate = {};
|
|
2899
|
-
if (typeof assignment === 'function') {
|
|
2900
|
-
partialUpdate = assignment(assignArgs);
|
|
2901
|
-
} else {
|
|
2902
|
-
for (const key of Object.keys(assignment)) {
|
|
2903
|
-
const propAssignment = assignment[key];
|
|
2904
|
-
partialUpdate[key] = typeof propAssignment === 'function' ? propAssignment(assignArgs) : propAssignment;
|
|
2905
|
-
}
|
|
2906
|
-
}
|
|
2907
|
-
const updatedContext = Object.assign({}, state.context, partialUpdate);
|
|
2908
|
-
return [cloneState(state, {
|
|
2909
|
-
context: updatedContext,
|
|
2910
|
-
children: Object.keys(spawnedChildren).length ? {
|
|
2911
|
-
...state.children,
|
|
2912
|
-
...spawnedChildren
|
|
2913
|
-
} : state.children
|
|
2914
|
-
})];
|
|
2915
|
-
}
|
|
2916
|
-
|
|
2917
|
-
/**
|
|
2918
|
-
* Updates the current context of the machine.
|
|
2919
|
-
*
|
|
2920
|
-
* @param assignment An object that represents the partial context to update.
|
|
2921
|
-
*/
|
|
2922
|
-
function assign(assignment) {
|
|
2923
|
-
function assign(_) {
|
|
2924
|
-
{
|
|
2925
|
-
throw new Error(`This isn't supposed to be called`);
|
|
2926
|
-
}
|
|
2927
|
-
}
|
|
2928
|
-
assign.type = 'xstate.assign';
|
|
2929
|
-
assign.assignment = assignment;
|
|
2930
|
-
assign.resolve = resolve$3;
|
|
2931
|
-
return assign;
|
|
2932
|
-
}
|
|
2933
|
-
|
|
2934
|
-
function resolve$2(_, state, args, {
|
|
2935
|
-
event: eventOrExpr,
|
|
2936
|
-
id,
|
|
2937
|
-
delay
|
|
2938
|
-
}) {
|
|
2939
|
-
const delaysMap = state.machine.implementations.delays;
|
|
2940
|
-
if (typeof eventOrExpr === 'string') {
|
|
2941
|
-
throw new Error(`Only event objects may be used with raise; use raise({ type: "${eventOrExpr}" }) instead`);
|
|
2942
|
-
}
|
|
2943
|
-
const resolvedEvent = typeof eventOrExpr === 'function' ? eventOrExpr(args) : eventOrExpr;
|
|
2944
|
-
let resolvedDelay;
|
|
2945
|
-
if (typeof delay === 'string') {
|
|
2946
|
-
const configDelay = delaysMap && delaysMap[delay];
|
|
2947
|
-
resolvedDelay = typeof configDelay === 'function' ? configDelay(args) : configDelay;
|
|
2948
|
-
} else {
|
|
2949
|
-
resolvedDelay = typeof delay === 'function' ? delay(args) : delay;
|
|
2950
|
-
}
|
|
2951
|
-
return [typeof resolvedDelay !== 'number' ? cloneState(state, {
|
|
2952
|
-
_internalQueue: state._internalQueue.concat(resolvedEvent)
|
|
2953
|
-
}) : state, {
|
|
2954
|
-
event: resolvedEvent,
|
|
2955
|
-
id,
|
|
2956
|
-
delay: resolvedDelay
|
|
2957
|
-
}];
|
|
2958
|
-
}
|
|
2959
|
-
function execute(actorContext, params) {
|
|
2960
|
-
if (typeof params.delay === 'number') {
|
|
2961
|
-
actorContext.self.delaySend(params);
|
|
2962
|
-
return;
|
|
2963
|
-
}
|
|
2964
|
-
}
|
|
2965
|
-
|
|
2966
|
-
/**
|
|
2967
|
-
* Raises an event. This places the event in the internal event queue, so that
|
|
2968
|
-
* the event is immediately consumed by the machine in the current step.
|
|
2969
|
-
*
|
|
2970
|
-
* @param eventType The event to raise.
|
|
2971
|
-
*/
|
|
2972
|
-
|
|
2973
|
-
function raise(eventOrExpr, options) {
|
|
2974
|
-
function raise(_) {
|
|
2975
|
-
{
|
|
2976
|
-
throw new Error(`This isn't supposed to be called`);
|
|
2977
|
-
}
|
|
2978
|
-
}
|
|
2979
|
-
raise.type = 'xstate.raise';
|
|
2980
|
-
raise.event = eventOrExpr;
|
|
2981
|
-
raise.id = options?.id;
|
|
2982
|
-
raise.delay = options?.delay;
|
|
2983
|
-
raise.resolve = resolve$2;
|
|
2984
|
-
raise.execute = execute;
|
|
2985
|
-
return raise;
|
|
2986
|
-
}
|
|
2987
|
-
|
|
2988
|
-
function resolve$1(_, state, actionArgs, {
|
|
2989
|
-
branches
|
|
2990
|
-
}) {
|
|
2991
|
-
const matchedActions = branches.find(condition => {
|
|
2992
|
-
return !condition.guard || evaluateGuard(condition.guard, state.context, actionArgs.event, state);
|
|
2993
|
-
})?.actions;
|
|
2994
|
-
return [state, undefined, toArray(matchedActions)];
|
|
2995
|
-
}
|
|
2996
|
-
function choose(branches) {
|
|
2997
|
-
function choose(_) {
|
|
2998
|
-
{
|
|
2999
|
-
throw new Error(`This isn't supposed to be called`);
|
|
3000
|
-
}
|
|
3001
|
-
}
|
|
3002
|
-
choose.type = 'xstate.choose';
|
|
3003
|
-
choose.branches = branches;
|
|
3004
|
-
choose.resolve = resolve$1;
|
|
3005
|
-
return choose;
|
|
3006
|
-
}
|
|
3007
|
-
|
|
3008
|
-
function resolve(_, state, args, {
|
|
3009
|
-
get
|
|
3010
|
-
}) {
|
|
3011
|
-
return [state, undefined, toArray(get({
|
|
3012
|
-
context: args.context,
|
|
3013
|
-
event: args.event
|
|
3014
|
-
}))];
|
|
3015
|
-
}
|
|
3016
|
-
function pure(getActions) {
|
|
3017
|
-
function pure(_) {
|
|
3018
|
-
{
|
|
3019
|
-
throw new Error(`This isn't supposed to be called`);
|
|
3020
|
-
}
|
|
3021
|
-
}
|
|
3022
|
-
pure.type = 'xstate.pure';
|
|
3023
|
-
pure.get = getActions;
|
|
3024
|
-
pure.resolve = resolve;
|
|
3025
|
-
return pure;
|
|
3026
|
-
}
|
|
3027
|
-
|
|
3028
|
-
/**
|
|
3029
|
-
* Returns an event type that represents an implicit event that
|
|
3030
|
-
* is sent after the specified `delay`.
|
|
3031
|
-
*
|
|
3032
|
-
* @param delayRef The delay in milliseconds
|
|
3033
|
-
* @param id The state node ID where this event is handled
|
|
3034
|
-
*/
|
|
3035
|
-
function after(delayRef, id) {
|
|
3036
|
-
const idSuffix = id ? `#${id}` : '';
|
|
3037
|
-
return `${ConstantPrefix.After}(${delayRef})${idSuffix}`;
|
|
3038
|
-
}
|
|
3039
|
-
|
|
3040
|
-
/**
|
|
3041
|
-
* Returns an event that represents that a final state node
|
|
3042
|
-
* has been reached in the parent state node.
|
|
3043
|
-
*
|
|
3044
|
-
* @param id The final state node's parent state node `id`
|
|
3045
|
-
* @param output The data to pass into the event
|
|
3046
|
-
*/
|
|
3047
|
-
function done(id, output) {
|
|
3048
|
-
const type = `${ConstantPrefix.DoneState}.${id}`;
|
|
3049
|
-
const eventObject = {
|
|
3050
|
-
type,
|
|
3051
|
-
output
|
|
3052
|
-
};
|
|
3053
|
-
eventObject.toString = () => type;
|
|
3054
|
-
return eventObject;
|
|
3055
|
-
}
|
|
3056
|
-
|
|
3057
|
-
/**
|
|
3058
|
-
* Returns an event that represents that an invoked service has terminated.
|
|
3059
|
-
*
|
|
3060
|
-
* An invoked service is terminated when it has reached a top-level final state node,
|
|
3061
|
-
* but not when it is canceled.
|
|
3062
|
-
*
|
|
3063
|
-
* @param invokeId The invoked service ID
|
|
3064
|
-
* @param output The data to pass into the event
|
|
3065
|
-
*/
|
|
3066
|
-
function doneInvoke(invokeId, output) {
|
|
3067
|
-
const type = `${ConstantPrefix.DoneInvoke}.${invokeId}`;
|
|
3068
|
-
const eventObject = {
|
|
3069
|
-
type,
|
|
3070
|
-
output
|
|
3071
|
-
};
|
|
3072
|
-
eventObject.toString = () => type;
|
|
3073
|
-
return eventObject;
|
|
3074
|
-
}
|
|
3075
|
-
function error(id, data) {
|
|
3076
|
-
const type = `${ConstantPrefix.ErrorPlatform}.${id}`;
|
|
3077
|
-
const eventObject = {
|
|
3078
|
-
type,
|
|
3079
|
-
data
|
|
3080
|
-
};
|
|
3081
|
-
eventObject.toString = () => type;
|
|
3082
|
-
return eventObject;
|
|
3083
|
-
}
|
|
3084
|
-
function createInitEvent(input) {
|
|
3085
|
-
return {
|
|
3086
|
-
type: INIT_TYPE,
|
|
3087
|
-
input
|
|
3088
|
-
};
|
|
3089
|
-
}
|
|
3090
|
-
|
|
3091
|
-
export { fromObservable as $, microstep as A, isAtomicStateNode as B, isStateId as C, getStateNodeByPath as D, getPersistedState as E, resolveReferencedActor as F, createActor as G, matchesState as H, sendTo as I, sendParent as J, forwardTo as K, interpret as L, Actor as M, NULL_EVENT as N, ActorStatus as O, InterpreterStatus as P, doneInvoke as Q, cancel as R, STATE_DELIMITER as S, choose as T, log as U, pure as V, raise as W, stop as X, pathToStateValue as Y, toObserver as Z, fromPromise as _, toTransitionConfigArray as a, fromCallback as a0, fromEventObservable as a1, fromTransition as a2, stateIn as a3, not as a4, and as a5, or as a6, ConstantPrefix as a7, SpecialTargets as a8, startSignalType as a9, stopSignalType as aa, startSignal as ab, stopSignal as ac, isSignal as ad, isActorRef as ae, toActorRef as af, createEmptyActor as ag, constantPrefixes as ah, after as ai, done as aj, error as ak, escalate as al, formatTransition as b, memo as c, flatten as d, evaluateGuard as e, formatTransitions as f, createInvokeId as g, getDelayedTransitions as h, formatInitialTransition as i, getCandidates as j, toInvokeConfig as k, getConfiguration as l, mapValues as m, getStateNodes as n, isInFinalState as o, State as p, isErrorEvent as q, resolveStateValue as r, cloneState as s, toArray as t, macrostep as u, transitionNode as v, getInitialConfiguration as w, resolveActionsAndContext as x, assign as y, createInitEvent as z };
|