xstate 6.0.0-alpha.2 → 6.0.0-alpha.21
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 +19 -17
- package/bin/xstate.js +36 -0
- package/dist/{StateMachine-64813f46.cjs.js → StateMachine-1b155670.cjs.js} +182 -40
- package/dist/{StateMachine-2dcdb1f5.esm.js → StateMachine-3c17fe3e.esm.js} +182 -40
- package/dist/{StateMachine-8d22a79c.development.cjs.js → StateMachine-550ff7f2.development.cjs.js} +193 -36
- package/dist/{StateMachine-2ea0a7fa.development.esm.js → StateMachine-e25dd61f.development.esm.js} +193 -36
- package/dist/declarations/src/State.d.ts +13 -2
- package/dist/declarations/src/StateMachine.d.ts +23 -13
- package/dist/declarations/src/actions.d.ts +2 -1
- package/dist/declarations/src/actors/promise.d.ts +4 -2
- package/dist/declarations/src/createActor.d.ts +9 -11
- package/dist/declarations/src/createMachine.d.ts +14 -11
- package/dist/declarations/src/createMachineFromConfig.d.ts +61 -28
- package/dist/declarations/src/fsm.d.ts +74 -0
- package/dist/declarations/src/graph/graph.d.ts +3 -3
- package/dist/declarations/src/graph/shortestPaths.d.ts +2 -2
- package/dist/declarations/src/graph/simplePaths.d.ts +2 -2
- package/dist/declarations/src/index.d.ts +6 -5
- package/dist/declarations/src/serialize.d.ts +10 -18
- package/dist/declarations/src/setup.d.ts +277 -65
- package/dist/declarations/src/spawn.d.ts +3 -3
- package/dist/declarations/src/stateUtils.d.ts +4 -1
- package/dist/declarations/src/system.d.ts +1 -1
- package/dist/declarations/src/transition.d.ts +8 -5
- package/dist/declarations/src/transitionActions.d.ts +12 -0
- package/dist/declarations/src/types.d.ts +237 -102
- package/dist/declarations/src/types.v6.d.ts +132 -58
- package/dist/declarations/src/utils.d.ts +4 -3
- package/dist/{index-93edd3bd.esm.js → index-3f87408c.esm.js} +936 -1389
- package/dist/{index-e61170ba.development.esm.js → index-d39fb138.development.esm.js} +968 -1421
- package/dist/{index-9cb3b3a0.cjs.js → index-e694228c.cjs.js} +945 -1394
- package/dist/{index-f6ef20d1.development.cjs.js → index-ec7c7a2d.development.cjs.js} +977 -1426
- package/dist/xstate-actors.cjs.js +1 -1
- package/dist/xstate-actors.development.cjs.js +1 -1
- package/dist/xstate-actors.development.esm.js +1 -1
- package/dist/xstate-actors.esm.js +1 -1
- package/dist/xstate-actors.umd.min.js +1 -1
- package/dist/xstate-actors.umd.min.js.map +1 -1
- package/dist/xstate-graph.cjs.js +2 -2
- package/dist/xstate-graph.development.cjs.js +2 -2
- package/dist/xstate-graph.development.esm.js +2 -2
- package/dist/xstate-graph.esm.js +2 -2
- package/dist/xstate-graph.umd.min.js +1 -1
- package/dist/xstate-graph.umd.min.js.map +1 -1
- package/dist/xstate.cjs.js +1100 -182
- package/dist/xstate.cjs.mjs +3 -5
- package/dist/xstate.development.cjs.js +1100 -182
- package/dist/xstate.development.cjs.mjs +3 -5
- package/dist/xstate.development.esm.js +1101 -181
- package/dist/xstate.esm.js +1101 -181
- package/dist/xstate.umd.min.js +1 -1
- package/dist/xstate.umd.min.js.map +1 -1
- package/package.json +6 -2
- package/dist/declarations/src/atom.d.ts +0 -81
package/dist/xstate.esm.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { m as matchesEventDescriptor, t as toArray, p as parseDelayToMilliseconds, i as isAtomicStateNode,
|
|
2
|
-
export { A as Actor, T as TimeoutError,
|
|
3
|
-
import { S as StateMachine } from './StateMachine-
|
|
4
|
-
export { S as StateMachine, a as StateNode } from './StateMachine-
|
|
1
|
+
import { m as matchesEventDescriptor, t as toArray$1, b as builtInActions, X as XSTATE_INIT, d as deriveDeferredStarts, c as createTransitionEnqueue, a as XSTATE_STOP, r as resolveActionsWithContext, p as parseDelayToMilliseconds, i as isAtomicStateNode, e as createActor, f as macrostep, g as createInitEvent, h as initialMicrostep, j as getProperAncestors } from './index-3f87408c.esm.js';
|
|
2
|
+
export { A as Actor, T as TimeoutError, n as __unsafe_getAllOwnEventDescriptors, q as checkStateIn, e as createActor, C as createAsyncLogic, x as createCallbackLogic, w as createEmptyActor, z as createEventObservableLogic, D as createListenerLogic, B as createLogic, y as createObservableLogic, F as createSubscriptionLogic, l as getStateNodes, v as isBuiltInExecutableAction, k as isMachineSnapshot, E as listenerLogic, o as matchesState, s as pathToStateValue, G as subscriptionLogic, u as toObserver } from './index-3f87408c.esm.js';
|
|
3
|
+
import { S as StateMachine } from './StateMachine-3c17fe3e.esm.js';
|
|
4
|
+
export { S as StateMachine, a as StateNode } from './StateMachine-3c17fe3e.esm.js';
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* Asserts that the given event object is of the specified type or types. Throws
|
|
@@ -27,7 +27,7 @@ export { S as StateMachine, a as StateNode } from './StateMachine-2dcdb1f5.esm.j
|
|
|
27
27
|
* ```
|
|
28
28
|
*/
|
|
29
29
|
function assertEvent(event, type) {
|
|
30
|
-
const types = toArray(type);
|
|
30
|
+
const types = toArray$1(type);
|
|
31
31
|
const matches = types.some(descriptor => matchesEventDescriptor(event.type, descriptor));
|
|
32
32
|
if (!matches) {
|
|
33
33
|
const typesText = types.length === 1 ? `type matching "${types[0]}"` : `one of types matching "${types.join('", "')}"`;
|
|
@@ -89,11 +89,354 @@ function createStateConfig(config) {
|
|
|
89
89
|
return config;
|
|
90
90
|
}
|
|
91
91
|
|
|
92
|
+
const emptyImplementations = {
|
|
93
|
+
actions: {},
|
|
94
|
+
actorSources: {},
|
|
95
|
+
guards: {},
|
|
96
|
+
delays: {}
|
|
97
|
+
};
|
|
98
|
+
const emptyExecutableActions = [];
|
|
99
|
+
const emptyRawActions = [];
|
|
100
|
+
const builtInActionSet = new Set(Object.values(builtInActions));
|
|
101
|
+
function toArray(value) {
|
|
102
|
+
return value === undefined ? [] : Array.isArray(value) ? value : [value];
|
|
103
|
+
}
|
|
104
|
+
function resolveContext(context, input) {
|
|
105
|
+
return typeof context === 'function' ? context({
|
|
106
|
+
input
|
|
107
|
+
}) : context ?? {};
|
|
108
|
+
}
|
|
109
|
+
function resolveTransitionContext(context, args) {
|
|
110
|
+
return typeof context === 'function' ? context(args) : context;
|
|
111
|
+
}
|
|
112
|
+
function resolveInput(input, context, event) {
|
|
113
|
+
return typeof input === 'function' ? input({
|
|
114
|
+
context,
|
|
115
|
+
event
|
|
116
|
+
}) : input;
|
|
117
|
+
}
|
|
118
|
+
function mergeContextPatch(context, patch) {
|
|
119
|
+
for (const key of Object.keys(patch)) {
|
|
120
|
+
if (!Object.prototype.hasOwnProperty.call(context, key) || !Object.is(context[key], patch[key])) {
|
|
121
|
+
return {
|
|
122
|
+
...context,
|
|
123
|
+
...patch
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
return context;
|
|
128
|
+
}
|
|
129
|
+
function createSnapshot(value, context, input, machine, stateInput) {
|
|
130
|
+
return {
|
|
131
|
+
status: 'active',
|
|
132
|
+
output: undefined,
|
|
133
|
+
error: undefined,
|
|
134
|
+
value,
|
|
135
|
+
context,
|
|
136
|
+
input,
|
|
137
|
+
children: {},
|
|
138
|
+
_stateInput: stateInput,
|
|
139
|
+
machine
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
function cloneSnapshot(snapshot, value, context, stateInput) {
|
|
143
|
+
return {
|
|
144
|
+
status: snapshot.status,
|
|
145
|
+
output: snapshot.output,
|
|
146
|
+
error: snapshot.error,
|
|
147
|
+
value,
|
|
148
|
+
context,
|
|
149
|
+
input: snapshot.input,
|
|
150
|
+
children: snapshot.children,
|
|
151
|
+
_stateInput: stateInput,
|
|
152
|
+
machine: snapshot.machine
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
function stopSnapshot(snapshot) {
|
|
156
|
+
return {
|
|
157
|
+
status: 'stopped',
|
|
158
|
+
output: undefined,
|
|
159
|
+
error: undefined,
|
|
160
|
+
value: snapshot.value,
|
|
161
|
+
context: snapshot.context,
|
|
162
|
+
input: undefined,
|
|
163
|
+
children: snapshot.children,
|
|
164
|
+
_stateInput: snapshot._stateInput,
|
|
165
|
+
machine: snapshot.machine
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
function assertNoStringTransitions(config) {
|
|
169
|
+
for (const [stateKey, stateConfig] of Object.entries(config.states)) {
|
|
170
|
+
for (const [eventType, transitionConfig] of Object.entries(stateConfig.on ?? {})) {
|
|
171
|
+
for (const transition of toArray(transitionConfig)) {
|
|
172
|
+
if (typeof transition === 'string') {
|
|
173
|
+
const target = transition;
|
|
174
|
+
throw new Error(`Invalid transition for "${stateKey}.${eventType}": use { target: "${target}" } instead of a string target.`);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
function resolveSimpleEnqueuedActions(rawActions) {
|
|
181
|
+
const executableActions = [];
|
|
182
|
+
for (const action of rawActions) {
|
|
183
|
+
if (!action || typeof action !== 'object' || !('action' in action) || typeof action.action !== 'function' || builtInActionSet.has(action.action) || '_special' in action.action) {
|
|
184
|
+
return undefined;
|
|
185
|
+
}
|
|
186
|
+
executableActions.push({
|
|
187
|
+
type: action.action.name || '(anonymous)',
|
|
188
|
+
params: undefined,
|
|
189
|
+
args: action.args,
|
|
190
|
+
exec: action.args.length ? action.action.bind(null, ...action.args) : action.action
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
return executableActions;
|
|
194
|
+
}
|
|
195
|
+
function createFSM(config) {
|
|
196
|
+
const machine = {
|
|
197
|
+
id: config.id ?? '(fsm)',
|
|
198
|
+
implementations: emptyImplementations
|
|
199
|
+
};
|
|
200
|
+
assertNoStringTransitions(config);
|
|
201
|
+
const runActions = (snapshot, event, actorScope, rawActions) => {
|
|
202
|
+
if (!rawActions?.length) {
|
|
203
|
+
return [snapshot, emptyExecutableActions];
|
|
204
|
+
}
|
|
205
|
+
const simpleExecutableActions = resolveSimpleEnqueuedActions(rawActions);
|
|
206
|
+
if (simpleExecutableActions) {
|
|
207
|
+
return [snapshot, simpleExecutableActions];
|
|
208
|
+
}
|
|
209
|
+
return resolveActionsWithContext(snapshot, event, actorScope, rawActions);
|
|
210
|
+
};
|
|
211
|
+
const runStateActions = (snapshot, event, actorScope, actionsConfig, stateInput, internalQueue) => {
|
|
212
|
+
if (!actionsConfig) {
|
|
213
|
+
return [snapshot, emptyExecutableActions];
|
|
214
|
+
}
|
|
215
|
+
const actions = [];
|
|
216
|
+
const enq = createTransitionEnqueue(actorScope, actions, internalQueue, true);
|
|
217
|
+
let context;
|
|
218
|
+
const actionCount = Array.isArray(actionsConfig) ? actionsConfig.length : 1;
|
|
219
|
+
for (let i = 0; i < actionCount; i++) {
|
|
220
|
+
const action = Array.isArray(actionsConfig) ? actionsConfig[i] : actionsConfig;
|
|
221
|
+
const result = action({
|
|
222
|
+
context: context ?? snapshot.context,
|
|
223
|
+
event: event,
|
|
224
|
+
input: stateInput,
|
|
225
|
+
value: snapshot.value,
|
|
226
|
+
self: actorScope.self,
|
|
227
|
+
system: actorScope.system,
|
|
228
|
+
parent: actorScope.self._parent,
|
|
229
|
+
children: snapshot.children
|
|
230
|
+
}, enq);
|
|
231
|
+
if (result?.context !== undefined) {
|
|
232
|
+
const currentContext = context ?? snapshot.context;
|
|
233
|
+
const nextContext = mergeContextPatch(currentContext, result.context);
|
|
234
|
+
if (nextContext !== currentContext) {
|
|
235
|
+
context = nextContext;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
const nextSnapshot = context !== undefined ? cloneSnapshot(snapshot, snapshot.value, context, snapshot._stateInput) : snapshot;
|
|
240
|
+
return runActions(nextSnapshot, event, actorScope, actions);
|
|
241
|
+
};
|
|
242
|
+
const selectTransition = (snapshot, event, actorScope, internalQueue) => {
|
|
243
|
+
const state = config.states[snapshot.value];
|
|
244
|
+
const transitionsConfig = state?.on?.[event.type];
|
|
245
|
+
if (!transitionsConfig) {
|
|
246
|
+
return undefined;
|
|
247
|
+
}
|
|
248
|
+
const transitionCount = Array.isArray(transitionsConfig) ? transitionsConfig.length : 1;
|
|
249
|
+
for (let i = 0; i < transitionCount; i++) {
|
|
250
|
+
const transition = Array.isArray(transitionsConfig) ? transitionsConfig[i] : transitionsConfig;
|
|
251
|
+
const args = {
|
|
252
|
+
context: snapshot.context,
|
|
253
|
+
event,
|
|
254
|
+
input: snapshot.input,
|
|
255
|
+
value: snapshot.value,
|
|
256
|
+
self: actorScope.self,
|
|
257
|
+
system: actorScope.system,
|
|
258
|
+
parent: actorScope.self._parent,
|
|
259
|
+
children: snapshot.children
|
|
260
|
+
};
|
|
261
|
+
if (typeof transition === 'function') {
|
|
262
|
+
const actions = [];
|
|
263
|
+
const enq = createTransitionEnqueue(actorScope, actions, internalQueue, true);
|
|
264
|
+
const result = transition(args, enq);
|
|
265
|
+
if (!result) {
|
|
266
|
+
if (actions.length) {
|
|
267
|
+
return {
|
|
268
|
+
actions
|
|
269
|
+
};
|
|
270
|
+
}
|
|
271
|
+
continue;
|
|
272
|
+
}
|
|
273
|
+
return {
|
|
274
|
+
target: result.target,
|
|
275
|
+
context: result.context,
|
|
276
|
+
input: result.input,
|
|
277
|
+
actions
|
|
278
|
+
};
|
|
279
|
+
}
|
|
280
|
+
if ('guard' in transition && transition.guard && !transition.guard(args)) {
|
|
281
|
+
continue;
|
|
282
|
+
}
|
|
283
|
+
return {
|
|
284
|
+
target: transition.target,
|
|
285
|
+
context: resolveTransitionContext(transition.context, args),
|
|
286
|
+
input: transition.input,
|
|
287
|
+
actions: 'actions' in transition && transition.actions ? toArray(transition.actions) : emptyRawActions
|
|
288
|
+
};
|
|
289
|
+
}
|
|
290
|
+
return undefined;
|
|
291
|
+
};
|
|
292
|
+
const transitionCore = (snapshot, event, actorScope) => {
|
|
293
|
+
if (snapshot.status !== 'active') {
|
|
294
|
+
return [snapshot, []];
|
|
295
|
+
}
|
|
296
|
+
if (event.type === XSTATE_STOP) {
|
|
297
|
+
return [stopSnapshot(snapshot), emptyExecutableActions];
|
|
298
|
+
}
|
|
299
|
+
const stateConfig = config.states[snapshot.value];
|
|
300
|
+
const directTransition = stateConfig?.on?.[event.type];
|
|
301
|
+
if (!directTransition) {
|
|
302
|
+
return [snapshot, emptyExecutableActions];
|
|
303
|
+
}
|
|
304
|
+
if (!Array.isArray(directTransition) && typeof directTransition !== 'function' && !('guard' in directTransition) && !('actions' in directTransition && directTransition.actions) && typeof directTransition.input !== 'function') {
|
|
305
|
+
const target = directTransition.target ?? snapshot.value;
|
|
306
|
+
const stateChanged = target !== snapshot.value;
|
|
307
|
+
if (stateChanged && (stateConfig.exit || config.states[target]?.entry)) ; else {
|
|
308
|
+
const hasContext = directTransition.context !== undefined;
|
|
309
|
+
const hasInput = directTransition.input !== undefined;
|
|
310
|
+
const resolvedContext = resolveTransitionContext(directTransition.context, {
|
|
311
|
+
context: snapshot.context,
|
|
312
|
+
event,
|
|
313
|
+
input: snapshot.input,
|
|
314
|
+
value: snapshot.value,
|
|
315
|
+
self: actorScope.self,
|
|
316
|
+
system: actorScope.system,
|
|
317
|
+
parent: actorScope.self._parent,
|
|
318
|
+
children: snapshot.children
|
|
319
|
+
});
|
|
320
|
+
const context = hasContext && resolvedContext ? mergeContextPatch(snapshot.context, resolvedContext) : snapshot.context;
|
|
321
|
+
if (!stateChanged && context === snapshot.context && !hasInput && snapshot._stateInput === undefined) {
|
|
322
|
+
return [snapshot, emptyExecutableActions];
|
|
323
|
+
}
|
|
324
|
+
return [cloneSnapshot(snapshot, target, context, hasInput ? resolveInput(directTransition.input, context, event) : undefined), emptyExecutableActions];
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
if (typeof directTransition === 'function' && directTransition.length < 2 && !stateConfig?.exit) {
|
|
328
|
+
const result = directTransition({
|
|
329
|
+
context: snapshot.context,
|
|
330
|
+
event,
|
|
331
|
+
input: snapshot.input,
|
|
332
|
+
value: snapshot.value,
|
|
333
|
+
self: actorScope.self,
|
|
334
|
+
system: actorScope.system,
|
|
335
|
+
parent: actorScope.self._parent,
|
|
336
|
+
children: snapshot.children
|
|
337
|
+
}, undefined);
|
|
338
|
+
if (result) {
|
|
339
|
+
const target = result.target ?? snapshot.value;
|
|
340
|
+
if (!config.states[target]?.entry) {
|
|
341
|
+
const hasContext = result.context !== undefined;
|
|
342
|
+
const hasInput = result.input !== undefined;
|
|
343
|
+
const context = hasContext && result.context ? mergeContextPatch(snapshot.context, result.context) : snapshot.context;
|
|
344
|
+
if (target === snapshot.value && context === snapshot.context && !hasInput && snapshot._stateInput === undefined) {
|
|
345
|
+
return [snapshot, emptyExecutableActions];
|
|
346
|
+
}
|
|
347
|
+
return [cloneSnapshot(snapshot, target, context, hasInput ? resolveInput(result.input, context, event) : undefined), emptyExecutableActions];
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
let nextSnapshot = snapshot;
|
|
352
|
+
const executableActions = [];
|
|
353
|
+
const internalQueue = [event];
|
|
354
|
+
let iterations = 0;
|
|
355
|
+
while (internalQueue.length) {
|
|
356
|
+
if (++iterations > 1000) {
|
|
357
|
+
throw new Error('FSM microstep count exceeded 1000');
|
|
358
|
+
}
|
|
359
|
+
const nextEvent = internalQueue.shift();
|
|
360
|
+
const selected = selectTransition(nextSnapshot, nextEvent, actorScope, internalQueue);
|
|
361
|
+
if (!selected) {
|
|
362
|
+
continue;
|
|
363
|
+
}
|
|
364
|
+
const nextValue = selected.target ?? nextSnapshot.value;
|
|
365
|
+
const stateChanged = nextValue !== nextSnapshot.value;
|
|
366
|
+
if (stateChanged) {
|
|
367
|
+
const [exited, exitActions] = runStateActions(nextSnapshot, nextEvent, actorScope, config.states[nextSnapshot.value]?.exit, nextSnapshot._stateInput, internalQueue);
|
|
368
|
+
nextSnapshot = exited;
|
|
369
|
+
executableActions.push(...exitActions);
|
|
370
|
+
}
|
|
371
|
+
let context = nextSnapshot.context;
|
|
372
|
+
if (selected.context !== undefined) {
|
|
373
|
+
context = mergeContextPatch(context, selected.context);
|
|
374
|
+
}
|
|
375
|
+
const hasInput = selected.input !== undefined;
|
|
376
|
+
const stateInput = hasInput ? resolveInput(selected.input, context, nextEvent) : undefined;
|
|
377
|
+
if (stateChanged || context !== nextSnapshot.context || hasInput || nextSnapshot._stateInput !== undefined) {
|
|
378
|
+
nextSnapshot = cloneSnapshot(nextSnapshot, nextValue, context, stateInput);
|
|
379
|
+
}
|
|
380
|
+
const [afterTransition, transitionActions] = runActions(nextSnapshot, nextEvent, actorScope, selected.actions);
|
|
381
|
+
nextSnapshot = afterTransition;
|
|
382
|
+
executableActions.push(...transitionActions);
|
|
383
|
+
if (stateChanged) {
|
|
384
|
+
const [entered, entryActions] = runStateActions(nextSnapshot, nextEvent, actorScope, config.states[nextValue]?.entry, stateInput, internalQueue);
|
|
385
|
+
nextSnapshot = entered;
|
|
386
|
+
executableActions.push(...entryActions);
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
return [nextSnapshot, executableActions];
|
|
390
|
+
};
|
|
391
|
+
const transition = (...args) => {
|
|
392
|
+
const [nextSnapshot, effects] = transitionCore(...args);
|
|
393
|
+
return [nextSnapshot, [...effects, ...deriveDeferredStarts(effects)]];
|
|
394
|
+
};
|
|
395
|
+
const logic = {
|
|
396
|
+
id: config.id,
|
|
397
|
+
config,
|
|
398
|
+
transition,
|
|
399
|
+
initialTransition: (input, actorScope) => {
|
|
400
|
+
const context = resolveContext(config.context, input);
|
|
401
|
+
const snapshot = createSnapshot(config.initial, context, input, machine);
|
|
402
|
+
const internalQueue = [];
|
|
403
|
+
let [nextSnapshot, actions] = runStateActions(snapshot, {
|
|
404
|
+
type: XSTATE_INIT
|
|
405
|
+
}, actorScope, config.states[config.initial]?.entry, undefined, internalQueue);
|
|
406
|
+
if (!actions.length) {
|
|
407
|
+
actions = [];
|
|
408
|
+
}
|
|
409
|
+
while (internalQueue.length) {
|
|
410
|
+
const [raisedSnapshot, raisedActions] = transitionCore(nextSnapshot, internalQueue.shift(), actorScope);
|
|
411
|
+
nextSnapshot = raisedSnapshot;
|
|
412
|
+
actions.push(...raisedActions);
|
|
413
|
+
}
|
|
414
|
+
return [nextSnapshot, [...actions, ...deriveDeferredStarts(actions)]];
|
|
415
|
+
},
|
|
416
|
+
getInitialSnapshot: (actorScope, input) => logic.initialTransition(input, actorScope)[0],
|
|
417
|
+
getPersistedSnapshot: ({
|
|
418
|
+
machine: _,
|
|
419
|
+
...snapshot
|
|
420
|
+
}) => snapshot,
|
|
421
|
+
restoreSnapshot: snapshot => ({
|
|
422
|
+
...snapshot,
|
|
423
|
+
machine: machine
|
|
424
|
+
})
|
|
425
|
+
};
|
|
426
|
+
return logic;
|
|
427
|
+
}
|
|
428
|
+
|
|
92
429
|
function delayToMs(delay) {
|
|
93
430
|
const parsedDelay = parseDelayToMilliseconds(delay);
|
|
94
431
|
if (parsedDelay !== undefined) return parsedDelay;
|
|
95
432
|
return typeof delay === 'string' ? parseFloat(delay) || 0 : delay;
|
|
96
433
|
}
|
|
434
|
+
function normalizeScxmlEventType(eventType) {
|
|
435
|
+
return /^error(\.|$)/.test(eventType) ? `xstate.${eventType}` : eventType;
|
|
436
|
+
}
|
|
437
|
+
function toScxmlEventName(eventType) {
|
|
438
|
+
return eventType.startsWith('xstate.error.') ? eventType.slice('xstate.'.length) : eventType;
|
|
439
|
+
}
|
|
97
440
|
|
|
98
441
|
/**
|
|
99
442
|
* Isolated executable-content block. Errors in nested actions stop this block
|
|
@@ -101,48 +444,23 @@ function delayToMs(delay) {
|
|
|
101
444
|
* separate <onentry>/<onexit> blocks: each is its own block per spec.
|
|
102
445
|
*/
|
|
103
446
|
|
|
104
|
-
function
|
|
105
|
-
return !!value && typeof value === 'object' &&
|
|
447
|
+
function isExpression(value) {
|
|
448
|
+
return !!value && typeof value === 'object' && typeof value['@expr'] === 'string';
|
|
106
449
|
}
|
|
107
|
-
function
|
|
108
|
-
|
|
109
|
-
if (value.$unserializable === 'actor' && path.endsWith('.src')) {
|
|
110
|
-
return;
|
|
111
|
-
}
|
|
112
|
-
throw new Error(`Unresolved ${value.$unserializable} at ${path}`);
|
|
113
|
-
}
|
|
114
|
-
if (!value || typeof value !== 'object') {
|
|
115
|
-
return;
|
|
116
|
-
}
|
|
117
|
-
if (Array.isArray(value)) {
|
|
118
|
-
value.forEach((item, index) => assertNoUnresolvedMarkers(item, `${path}[${index}]`));
|
|
119
|
-
return;
|
|
120
|
-
}
|
|
121
|
-
for (const key of Object.keys(value)) {
|
|
122
|
-
const child = value[key];
|
|
123
|
-
const isImplementationMarker = path === '$' && (key === 'actions' || key === 'guards' || key === 'actors' || key === 'delays' || key === 'schemas');
|
|
124
|
-
if (isImplementationMarker) {
|
|
125
|
-
continue;
|
|
126
|
-
}
|
|
127
|
-
assertNoUnresolvedMarkers(child, `${path}.${key}`);
|
|
128
|
-
}
|
|
450
|
+
function isCode(value) {
|
|
451
|
+
return !!value && typeof value === 'object' && typeof value['@code'] === 'string';
|
|
129
452
|
}
|
|
130
|
-
function
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
if (!implementations[kind]?.[key]) {
|
|
142
|
-
throw new Error(`Missing ${kind}.${key}`);
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
}
|
|
453
|
+
function isResolvable(value) {
|
|
454
|
+
return isExpression(value) || isCode(value);
|
|
455
|
+
}
|
|
456
|
+
function isBuiltInActionType(type) {
|
|
457
|
+
return type.startsWith('@xstate.');
|
|
458
|
+
}
|
|
459
|
+
function isScxmlActionType(type) {
|
|
460
|
+
return type.startsWith('scxml.');
|
|
461
|
+
}
|
|
462
|
+
function toPath(parent, key) {
|
|
463
|
+
return typeof key === 'number' ? `${parent}[${key}]` : `${parent}.${key}`;
|
|
146
464
|
}
|
|
147
465
|
function extractSerializableDelays(delays) {
|
|
148
466
|
const result = {};
|
|
@@ -153,6 +471,8 @@ function extractSerializableDelays(delays) {
|
|
|
153
471
|
const value = delays[key];
|
|
154
472
|
if (typeof value === 'number') {
|
|
155
473
|
result[key] = value;
|
|
474
|
+
} else if (value && typeof value === 'object' && typeof value.duration === 'number') {
|
|
475
|
+
result[key] = value.duration;
|
|
156
476
|
}
|
|
157
477
|
}
|
|
158
478
|
return result;
|
|
@@ -161,14 +481,254 @@ function mergeImplementations(json, implementations) {
|
|
|
161
481
|
return {
|
|
162
482
|
actions: implementations.actions ?? {},
|
|
163
483
|
guards: implementations.guards ?? {},
|
|
164
|
-
|
|
484
|
+
actorSources: implementations.actorSources ?? {},
|
|
165
485
|
delays: {
|
|
166
486
|
...extractSerializableDelays(json.delays),
|
|
167
487
|
...(implementations.delays ?? {})
|
|
488
|
+
},
|
|
489
|
+
evaluators: implementations.evaluators ?? {}
|
|
490
|
+
};
|
|
491
|
+
}
|
|
492
|
+
function createExpressionResolver(expressionLanguage, implementations) {
|
|
493
|
+
function getEvaluator(value, path) {
|
|
494
|
+
const lang = value['@lang'] ?? expressionLanguage;
|
|
495
|
+
if (!lang) {
|
|
496
|
+
throw new Error(`Missing @exprLang for expression at ${path}`);
|
|
497
|
+
}
|
|
498
|
+
const evaluator = implementations.evaluators[lang];
|
|
499
|
+
if (!evaluator) {
|
|
500
|
+
throw new Error(`Missing evaluator for @lang '${lang}' at ${path}`);
|
|
501
|
+
}
|
|
502
|
+
return evaluator;
|
|
503
|
+
}
|
|
504
|
+
function evaluateResolvable(value, slot, scope, path) {
|
|
505
|
+
const kind = isExpression(value) ? 'expr' : 'code';
|
|
506
|
+
const source = isExpression(value) ? value['@expr'] : value['@code'];
|
|
507
|
+
return getEvaluator(value, path)({
|
|
508
|
+
source,
|
|
509
|
+
kind,
|
|
510
|
+
slot,
|
|
511
|
+
scope,
|
|
512
|
+
path
|
|
513
|
+
});
|
|
514
|
+
}
|
|
515
|
+
function resolveValue(value, slot, scope, path) {
|
|
516
|
+
if (isResolvable(value)) {
|
|
517
|
+
return evaluateResolvable(value, slot, scope, path);
|
|
518
|
+
}
|
|
519
|
+
if (Array.isArray(value)) {
|
|
520
|
+
return value.map((item, index) => resolveValue(item, slot, scope, toPath(path, index)));
|
|
521
|
+
}
|
|
522
|
+
if (!value || typeof value !== 'object') {
|
|
523
|
+
return value;
|
|
524
|
+
}
|
|
525
|
+
const result = {};
|
|
526
|
+
for (const key of Object.keys(value)) {
|
|
527
|
+
result[key] = resolveValue(value[key], slot, scope, toPath(path, key));
|
|
168
528
|
}
|
|
529
|
+
return result;
|
|
530
|
+
}
|
|
531
|
+
function makeScope(x, extra) {
|
|
532
|
+
return {
|
|
533
|
+
context: x.context,
|
|
534
|
+
event: x.event,
|
|
535
|
+
input: x.input,
|
|
536
|
+
self: x.self,
|
|
537
|
+
children: x.children,
|
|
538
|
+
params: x.params,
|
|
539
|
+
...extra
|
|
540
|
+
};
|
|
541
|
+
}
|
|
542
|
+
function getDurationConfig(value, path) {
|
|
543
|
+
if (!isResolvable(value)) {
|
|
544
|
+
return value;
|
|
545
|
+
}
|
|
546
|
+
return args => delayToMs(resolveValue(value, 'delay', makeScope(args), path));
|
|
547
|
+
}
|
|
548
|
+
function assertResolvable(value, path) {
|
|
549
|
+
if (isResolvable(value)) {
|
|
550
|
+
getEvaluator(value, path);
|
|
551
|
+
return;
|
|
552
|
+
}
|
|
553
|
+
if (Array.isArray(value)) {
|
|
554
|
+
value.forEach((item, index) => assertResolvable(item, toPath(path, index)));
|
|
555
|
+
return;
|
|
556
|
+
}
|
|
557
|
+
if (!value || typeof value !== 'object') {
|
|
558
|
+
return;
|
|
559
|
+
}
|
|
560
|
+
for (const key of Object.keys(value)) {
|
|
561
|
+
assertResolvable(value[key], toPath(path, key));
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
return {
|
|
565
|
+
evaluateResolvable,
|
|
566
|
+
resolveValue,
|
|
567
|
+
makeScope,
|
|
568
|
+
getDurationConfig,
|
|
569
|
+
assertResolvable
|
|
169
570
|
};
|
|
170
571
|
}
|
|
171
|
-
|
|
572
|
+
function toActionArray(actions) {
|
|
573
|
+
return actions === undefined ? [] : Array.isArray(actions) ? actions : [actions];
|
|
574
|
+
}
|
|
575
|
+
function validateChoiceConfig(choice, path) {
|
|
576
|
+
if (!Array.isArray(choice)) {
|
|
577
|
+
return;
|
|
578
|
+
}
|
|
579
|
+
choice.forEach((branch, index) => {
|
|
580
|
+
if (branch.when === undefined && index !== choice.length - 1) {
|
|
581
|
+
throw new Error(`Choice fallback branch at ${path}[${index}] must be last.`);
|
|
582
|
+
}
|
|
583
|
+
});
|
|
584
|
+
}
|
|
585
|
+
function assertMachineJSON(json, resolvedImplementations, expressionResolver) {
|
|
586
|
+
const {
|
|
587
|
+
assertResolvable
|
|
588
|
+
} = expressionResolver;
|
|
589
|
+
function assertCondition(condition, path) {
|
|
590
|
+
if (!condition) {
|
|
591
|
+
return;
|
|
592
|
+
}
|
|
593
|
+
if (isResolvable(condition)) {
|
|
594
|
+
assertResolvable(condition, path);
|
|
595
|
+
return;
|
|
596
|
+
}
|
|
597
|
+
assertResolvable(condition.params, `${path}.params`);
|
|
598
|
+
if (json.guards?.[condition.type]) {
|
|
599
|
+
assertCondition(json.guards[condition.type].when, `$.guards.${condition.type}.when`);
|
|
600
|
+
return;
|
|
601
|
+
}
|
|
602
|
+
if (!resolvedImplementations.guards[condition.type] && !['scxml.cond', 'xstate.stateIn', 'xstate.not'].includes(condition.type)) {
|
|
603
|
+
throw new Error(`Missing guard implementation "${condition.type}"`);
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
function assertAction(action, path, stack = []) {
|
|
607
|
+
if (isResolvable(action)) {
|
|
608
|
+
assertResolvable(action, path);
|
|
609
|
+
return;
|
|
610
|
+
}
|
|
611
|
+
if (!action || typeof action.type !== 'string') {
|
|
612
|
+
throw new Error(`Invalid action at ${path}`);
|
|
613
|
+
}
|
|
614
|
+
assertResolvable(action.params, `${path}.params`);
|
|
615
|
+
if (isBuiltInActionType(action.type) || isScxmlActionType(action.type)) {
|
|
616
|
+
assertResolvable(action, path);
|
|
617
|
+
return;
|
|
618
|
+
}
|
|
619
|
+
const definition = json.actions?.[action.type];
|
|
620
|
+
if (definition) {
|
|
621
|
+
if (stack.includes(action.type)) {
|
|
622
|
+
throw new Error(`Circular action reference: ${stack.concat(action.type).join(' -> ')}`);
|
|
623
|
+
}
|
|
624
|
+
const definitions = Array.isArray(definition) ? definition : [definition];
|
|
625
|
+
definitions.forEach((item, index) => assertAction(item, `${path}.actions.${action.type}${definitions.length > 1 ? `[${index}]` : ''}`, stack.concat(action.type)));
|
|
626
|
+
return;
|
|
627
|
+
}
|
|
628
|
+
if (!resolvedImplementations.actions[action.type]) {
|
|
629
|
+
throw new Error(`Missing action implementation "${action.type}"`);
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
function assertActions(actions, path) {
|
|
633
|
+
toActionArray(actions).forEach((action, index) => assertAction(action, `${path}[${index}]`));
|
|
634
|
+
}
|
|
635
|
+
function assertTransition(transition, path) {
|
|
636
|
+
const transitions = Array.isArray(transition) ? transition : transition ? [transition] : [];
|
|
637
|
+
transitions.forEach((t, index) => {
|
|
638
|
+
const transitionPath = Array.isArray(transition) ? `${path}[${index}]` : path;
|
|
639
|
+
if (isResolvable(t)) {
|
|
640
|
+
assertResolvable(t, transitionPath);
|
|
641
|
+
return;
|
|
642
|
+
}
|
|
643
|
+
assertCondition(t.guard, `${transitionPath}.guard`);
|
|
644
|
+
assertActions(t.actions, `${transitionPath}.actions`);
|
|
645
|
+
assertResolvable(t.context, `${transitionPath}.context`);
|
|
646
|
+
assertResolvable(t.input, `${transitionPath}.input`);
|
|
647
|
+
});
|
|
648
|
+
}
|
|
649
|
+
function assertStateNode(node, path) {
|
|
650
|
+
assertResolvable(node.context, `${path}.context`);
|
|
651
|
+
assertResolvable(node.input, `${path}.input`);
|
|
652
|
+
assertResolvable(node.output, `${path}.output`);
|
|
653
|
+
assertResolvable(node.timeout, `${path}.timeout`);
|
|
654
|
+
assertActions(node.entry, `${path}.entry`);
|
|
655
|
+
assertActions(node.exit, `${path}.exit`);
|
|
656
|
+
if (node.type === 'choice') {
|
|
657
|
+
if (!node.choice) {
|
|
658
|
+
throw new Error(`Choice state at ${path} must declare choice.`);
|
|
659
|
+
}
|
|
660
|
+
if (Array.isArray(node.choice)) {
|
|
661
|
+
validateChoiceConfig(node.choice, `${path}.choice`);
|
|
662
|
+
node.choice.forEach((branch, index) => {
|
|
663
|
+
assertCondition(branch.when, `${path}.choice[${index}].when`);
|
|
664
|
+
assertResolvable(branch.context, `${path}.choice[${index}].context`);
|
|
665
|
+
assertResolvable(branch.input, `${path}.choice[${index}].input`);
|
|
666
|
+
});
|
|
667
|
+
} else {
|
|
668
|
+
assertResolvable(node.choice, `${path}.choice`);
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
if (isResolvable(node.route)) {
|
|
672
|
+
assertResolvable(node.route, `${path}.route`);
|
|
673
|
+
}
|
|
674
|
+
if (node.invoke) {
|
|
675
|
+
const invokes = Array.isArray(node.invoke) ? node.invoke : [node.invoke];
|
|
676
|
+
invokes.forEach((invoke, index) => {
|
|
677
|
+
const invokePath = `${path}.invoke${Array.isArray(node.invoke) ? `[${index}]` : ''}`;
|
|
678
|
+
const extInv = invoke;
|
|
679
|
+
if (!extInv._nestedMachineJSON && !resolvedImplementations.actorSources[invoke.src]) {
|
|
680
|
+
throw new Error(`Missing actorSource implementation "${invoke.src}"`);
|
|
681
|
+
}
|
|
682
|
+
assertResolvable(invoke.input, `${invokePath}.input`);
|
|
683
|
+
assertResolvable(invoke.timeout, `${invokePath}.timeout`);
|
|
684
|
+
assertTransition(invoke.onDone, `${invokePath}.onDone`);
|
|
685
|
+
assertTransition(invoke.onError, `${invokePath}.onError`);
|
|
686
|
+
assertTransition(invoke.onSnapshot, `${invokePath}.onSnapshot`);
|
|
687
|
+
assertTransition(invoke.onTimeout, `${invokePath}.onTimeout`);
|
|
688
|
+
});
|
|
689
|
+
}
|
|
690
|
+
if (node.on) {
|
|
691
|
+
for (const descriptor of Object.keys(node.on)) {
|
|
692
|
+
assertTransition(node.on[descriptor], `${path}.on.${descriptor}`);
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
if (node.after) {
|
|
696
|
+
for (const delay of Object.keys(node.after)) {
|
|
697
|
+
if (Number.isNaN(Number(delay)) && parseDelayToMilliseconds(delay) === undefined && !json.delays?.[delay] && !resolvedImplementations.delays[delay]) {
|
|
698
|
+
throw new Error(`Missing delay implementation "${delay}"`);
|
|
699
|
+
}
|
|
700
|
+
assertTransition(node.after[delay], `${path}.after.${delay}`);
|
|
701
|
+
}
|
|
702
|
+
}
|
|
703
|
+
assertTransition(node.always, `${path}.always`);
|
|
704
|
+
assertTransition(node.onError, `${path}.onError`);
|
|
705
|
+
assertTransition(node.onTimeout, `${path}.onTimeout`);
|
|
706
|
+
if (node.states) {
|
|
707
|
+
for (const key of Object.keys(node.states)) {
|
|
708
|
+
assertStateNode(node.states[key], `${path}.states.${key}`);
|
|
709
|
+
}
|
|
710
|
+
}
|
|
711
|
+
}
|
|
712
|
+
if (json.actions) {
|
|
713
|
+
for (const key of Object.keys(json.actions)) {
|
|
714
|
+
const actions = Array.isArray(json.actions[key]) ? json.actions[key] : [json.actions[key]];
|
|
715
|
+
actions.forEach((action, index) => assertAction(action, `$.actions.${key}${actions.length > 1 ? `[${index}]` : ''}`, [key]));
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
if (json.guards) {
|
|
719
|
+
for (const key of Object.keys(json.guards)) {
|
|
720
|
+
assertCondition(json.guards[key].when, `$.guards.${key}.when`);
|
|
721
|
+
}
|
|
722
|
+
}
|
|
723
|
+
if (json.delays) {
|
|
724
|
+
for (const key of Object.keys(json.delays)) {
|
|
725
|
+
const delay = json.delays[key];
|
|
726
|
+
assertResolvable(delay && typeof delay === 'object' && 'duration' in delay ? delay.duration : delay, `$.delays.${key}`);
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
assertStateNode(json, '$');
|
|
730
|
+
}
|
|
731
|
+
let _scxmlSessionId = 'session_scxml';
|
|
172
732
|
let _scxmlMachineName = '';
|
|
173
733
|
|
|
174
734
|
/** Evaluates an SCXML expression with context variables available via `with`. */
|
|
@@ -193,7 +753,7 @@ with (context) {
|
|
|
193
753
|
}
|
|
194
754
|
return false;
|
|
195
755
|
};
|
|
196
|
-
const SCXML_ORIGIN =
|
|
756
|
+
const SCXML_ORIGIN = `#_scxml_${_scxmlSessionId}`;
|
|
197
757
|
const SCXML_ORIGIN_TYPE = 'http://www.w3.org/TR/scxml/#SCXMLEventProcessor';
|
|
198
758
|
const isExternal = event && event._scxmlExternal;
|
|
199
759
|
const isInitEvent = event && event.type === '@xstate.init';
|
|
@@ -205,7 +765,7 @@ with (context) {
|
|
|
205
765
|
const isDoneEvent = event && typeof event.type === 'string' && event.type.startsWith('xstate.done.');
|
|
206
766
|
const explicitType = event && event._scxmlEventType;
|
|
207
767
|
const scxmlEvent = event && !isInitEvent ? {
|
|
208
|
-
name: event.type,
|
|
768
|
+
name: event._scxmlEventName ?? toScxmlEventName(event.type),
|
|
209
769
|
type: explicitType || (isExternal ? 'external' : 'internal'),
|
|
210
770
|
sendid: event._scxmlSendId,
|
|
211
771
|
origin: isExternal ? SCXML_ORIGIN : undefined,
|
|
@@ -241,9 +801,64 @@ return updates;
|
|
|
241
801
|
return fn(context, updates);
|
|
242
802
|
}
|
|
243
803
|
function createMachineFromConfig(json, implementations = {}) {
|
|
244
|
-
assertRootImplementationMarkers(json, implementations);
|
|
245
|
-
assertNoUnresolvedMarkers(json);
|
|
246
804
|
const resolvedImplementations = mergeImplementations(json, implementations);
|
|
805
|
+
const expressionResolver = createExpressionResolver(json['@exprLang'], resolvedImplementations);
|
|
806
|
+
const {
|
|
807
|
+
evaluateResolvable,
|
|
808
|
+
resolveValue,
|
|
809
|
+
makeScope,
|
|
810
|
+
getDurationConfig
|
|
811
|
+
} = expressionResolver;
|
|
812
|
+
function resolveCondition(condition, slot, path) {
|
|
813
|
+
if (!condition) {
|
|
814
|
+
return undefined;
|
|
815
|
+
}
|
|
816
|
+
if (isResolvable(condition)) {
|
|
817
|
+
return args => !!evaluateResolvable(condition, slot, makeScope(args), path);
|
|
818
|
+
}
|
|
819
|
+
return args => {
|
|
820
|
+
const params = resolveValue(condition.params, slot, makeScope(args), `${path}.params`);
|
|
821
|
+
const declarativeGuard = json.guards?.[condition.type];
|
|
822
|
+
if (declarativeGuard) {
|
|
823
|
+
const guard = resolveCondition(declarativeGuard.when, 'guard', `$.guards.${condition.type}.when`);
|
|
824
|
+
return !!guard?.({
|
|
825
|
+
...args,
|
|
826
|
+
params
|
|
827
|
+
});
|
|
828
|
+
}
|
|
829
|
+
const guardImpl = args.guards?.[condition.type];
|
|
830
|
+
if (!guardImpl) {
|
|
831
|
+
throw new Error(getMissingGuardMessage(condition.type, args.guards ?? {}));
|
|
832
|
+
}
|
|
833
|
+
return guardImpl(args, params);
|
|
834
|
+
};
|
|
835
|
+
}
|
|
836
|
+
function makeChoiceConfig(choice, path) {
|
|
837
|
+
if (!choice) {
|
|
838
|
+
return undefined;
|
|
839
|
+
}
|
|
840
|
+
if (isResolvable(choice)) {
|
|
841
|
+
return args => evaluateResolvable(choice, 'choice', makeScope(args), path);
|
|
842
|
+
}
|
|
843
|
+
validateChoiceConfig(choice, path);
|
|
844
|
+
return args => {
|
|
845
|
+
for (let index = 0; index < choice.length; index++) {
|
|
846
|
+
const branch = choice[index];
|
|
847
|
+
const guard = resolveCondition(branch.when, 'choice', `${path}[${index}].when`);
|
|
848
|
+
if (!guard || guard(args)) {
|
|
849
|
+
return {
|
|
850
|
+
target: branch.target,
|
|
851
|
+
context: branch.context ? resolveValue(branch.context, 'transitionContext', makeScope(args), `${path}[${index}].context`) : undefined,
|
|
852
|
+
input: branch.input !== undefined ? resolveValue(branch.input, 'input', makeScope(args), `${path}[${index}].input`) : undefined,
|
|
853
|
+
description: branch.description,
|
|
854
|
+
reenter: branch.reenter,
|
|
855
|
+
meta: branch.meta
|
|
856
|
+
};
|
|
857
|
+
}
|
|
858
|
+
}
|
|
859
|
+
throw new Error(`Choice state at ${path} did not match any branch.`);
|
|
860
|
+
};
|
|
861
|
+
}
|
|
247
862
|
|
|
248
863
|
// Pending transition actions: set by .to functions, consumed by entry functions.
|
|
249
864
|
// This bridges SCXML's exit→transition→entry action ordering with XState's
|
|
@@ -263,8 +878,30 @@ function createMachineFromConfig(json, implementations = {}) {
|
|
|
263
878
|
// <transition cond>). Drained by the next state's entry into the internal
|
|
264
879
|
// event queue so they're processed before any external events.
|
|
265
880
|
const pendingPlatformErrors = [];
|
|
881
|
+
function getMissingGuardMessage(type, guards) {
|
|
882
|
+
return `Guard '${type}' is not implemented in machine '${json.id ?? '(machine)'}'. Available guards: ${Object.keys(guards).map(key => `'${key}'`).join(', ') || '(none)'}.`;
|
|
883
|
+
}
|
|
884
|
+
function resolveRouteConfig(route, path) {
|
|
885
|
+
if (!route || typeof route === 'function') {
|
|
886
|
+
return route;
|
|
887
|
+
}
|
|
888
|
+
if (isResolvable(route)) {
|
|
889
|
+
return args => evaluateResolvable(route, 'transition', makeScope(args), path);
|
|
890
|
+
}
|
|
891
|
+
const {
|
|
892
|
+
guard,
|
|
893
|
+
...routeConfig
|
|
894
|
+
} = route;
|
|
895
|
+
if (!guard) {
|
|
896
|
+
return routeConfig;
|
|
897
|
+
}
|
|
898
|
+
const resolvedGuard = resolveCondition({
|
|
899
|
+
type: guard
|
|
900
|
+
}, 'guard', path);
|
|
901
|
+
return args => resolvedGuard(args) ? routeConfig : undefined;
|
|
902
|
+
}
|
|
266
903
|
function iterNode(node, nodeKey) {
|
|
267
|
-
const originalEntryActions = node.entry;
|
|
904
|
+
const originalEntryActions = toActionArray(node.entry);
|
|
268
905
|
const stateId = node.id || nodeKey;
|
|
269
906
|
|
|
270
907
|
// Wrap entry to first execute any pending transition actions, then normal entry.
|
|
@@ -278,8 +915,10 @@ function createMachineFromConfig(json, implementations = {}) {
|
|
|
278
915
|
while (pendingPlatformErrors.length) {
|
|
279
916
|
const err = pendingPlatformErrors.shift();
|
|
280
917
|
enq.raise({
|
|
281
|
-
type: 'error.execution',
|
|
918
|
+
type: 'xstate.error.execution',
|
|
919
|
+
error: err,
|
|
282
920
|
_scxmlEventType: 'platform',
|
|
921
|
+
_scxmlEventName: 'error.execution',
|
|
283
922
|
_scxmlEventData: err
|
|
284
923
|
});
|
|
285
924
|
}
|
|
@@ -327,7 +966,7 @@ function createMachineFromConfig(json, implementations = {}) {
|
|
|
327
966
|
}
|
|
328
967
|
|
|
329
968
|
// Execute normal entry actions
|
|
330
|
-
if (originalEntryActions
|
|
969
|
+
if (originalEntryActions.length) {
|
|
331
970
|
const mergedX = context ? {
|
|
332
971
|
...x,
|
|
333
972
|
context
|
|
@@ -350,7 +989,7 @@ function createMachineFromConfig(json, implementations = {}) {
|
|
|
350
989
|
description: node.description,
|
|
351
990
|
tags: node.tags,
|
|
352
991
|
input: node.input,
|
|
353
|
-
timeout: node.timeout,
|
|
992
|
+
timeout: getDurationConfig(node.timeout, `$.states.${nodeKey}.timeout`),
|
|
354
993
|
states: node.states ? Object.entries(node.states).reduce((acc, [key, value]) => {
|
|
355
994
|
acc[key] = iterNode(value, key);
|
|
356
995
|
return acc;
|
|
@@ -360,18 +999,27 @@ function createMachineFromConfig(json, implementations = {}) {
|
|
|
360
999
|
return acc;
|
|
361
1000
|
}, {}) : undefined,
|
|
362
1001
|
always: node.always ? getTransitionConfig(node.always) : undefined,
|
|
363
|
-
|
|
364
|
-
|
|
1002
|
+
onError: node.onError ? getTransitionConfig(node.onError) : undefined,
|
|
1003
|
+
choice: makeChoiceConfig(node.choice, `$.states.${nodeKey}.choice`),
|
|
1004
|
+
route: resolveRouteConfig(node.route, `$.states.${nodeKey}.route`),
|
|
365
1005
|
after: node.after ? Object.entries(node.after).reduce((acc, [key, value]) => {
|
|
366
1006
|
acc[key] = getTransitionConfig(value);
|
|
367
1007
|
return acc;
|
|
368
1008
|
}, {}) : undefined,
|
|
369
1009
|
onTimeout: node.onTimeout ? getTransitionConfig(node.onTimeout) : undefined,
|
|
370
|
-
entry: entryFn,
|
|
371
|
-
exit: node.exit ? iterActions(node.exit) : undefined,
|
|
1010
|
+
entry: node.type === 'choice' ? undefined : entryFn,
|
|
1011
|
+
exit: node.exit ? iterActions(toActionArray(node.exit)) : undefined,
|
|
372
1012
|
invoke: node.invoke ? iterInvokeConfigs(node.invoke) : undefined,
|
|
373
1013
|
meta: node.meta,
|
|
374
|
-
output: node._scxmlDonedata ? makeDonedataOutput(node._scxmlDonedata) : node.output
|
|
1014
|
+
output: node._scxmlDonedata ? makeDonedataOutput(node._scxmlDonedata) : isResolvable(node.output) ? ({
|
|
1015
|
+
context,
|
|
1016
|
+
event,
|
|
1017
|
+
self
|
|
1018
|
+
}) => evaluateResolvable(node.output, 'output', {
|
|
1019
|
+
context,
|
|
1020
|
+
event,
|
|
1021
|
+
self
|
|
1022
|
+
}, `$.states.${nodeKey}.output`) : node.output
|
|
375
1023
|
};
|
|
376
1024
|
return nodeConfig;
|
|
377
1025
|
}
|
|
@@ -424,23 +1072,18 @@ function createMachineFromConfig(json, implementations = {}) {
|
|
|
424
1072
|
let src;
|
|
425
1073
|
if (extInv._nestedMachineJSON) {
|
|
426
1074
|
src = createMachineFromConfig(extInv._nestedMachineJSON, resolvedImplementations);
|
|
427
|
-
} else if (isUnserializableMarker(inv.src)) {
|
|
428
|
-
src = inv.src.id ? resolvedImplementations.actors[inv.src.id] : undefined;
|
|
429
|
-
if (!src) {
|
|
430
|
-
throw new Error(`Missing actors.${inv.src.id ?? ''}`);
|
|
431
|
-
}
|
|
432
1075
|
} else {
|
|
433
|
-
src = inv.src;
|
|
1076
|
+
src = resolvedImplementations.actorSources[inv.src] ?? inv.src;
|
|
434
1077
|
}
|
|
435
1078
|
return {
|
|
436
1079
|
src,
|
|
437
1080
|
id: inv.id,
|
|
438
|
-
|
|
439
|
-
input: inv.input,
|
|
1081
|
+
registryKey: inv.registryKey,
|
|
1082
|
+
input: inv.input !== undefined ? args => resolveValue(inv.input, 'input', makeScope(args), '$.invoke.input') : undefined,
|
|
440
1083
|
onDone: inv.onDone ? getTransitionConfig(inv.onDone) : undefined,
|
|
441
1084
|
onError: inv.onError ? getTransitionConfig(inv.onError) : undefined,
|
|
442
1085
|
onSnapshot: inv.onSnapshot ? getTransitionConfig(inv.onSnapshot) : undefined,
|
|
443
|
-
timeout: inv.timeout,
|
|
1086
|
+
timeout: getDurationConfig(inv.timeout, '$.invoke.timeout'),
|
|
444
1087
|
onTimeout: inv.onTimeout ? getTransitionConfig(inv.onTimeout) : undefined
|
|
445
1088
|
};
|
|
446
1089
|
});
|
|
@@ -454,8 +1097,16 @@ function createMachineFromConfig(json, implementations = {}) {
|
|
|
454
1097
|
function raiseErrorExecution(state, tagname, err) {
|
|
455
1098
|
const message = err instanceof Error ? err.message : typeof err === 'string' ? err : 'unknown error';
|
|
456
1099
|
state.enq.raise({
|
|
457
|
-
type: 'error.execution',
|
|
1100
|
+
type: 'xstate.error.execution',
|
|
1101
|
+
error: {
|
|
1102
|
+
tagname,
|
|
1103
|
+
message,
|
|
1104
|
+
line: NaN,
|
|
1105
|
+
column: NaN,
|
|
1106
|
+
reason: message
|
|
1107
|
+
},
|
|
458
1108
|
_scxmlEventType: 'platform',
|
|
1109
|
+
_scxmlEventName: 'error.execution',
|
|
459
1110
|
_scxmlEventData: {
|
|
460
1111
|
tagname,
|
|
461
1112
|
message,
|
|
@@ -466,8 +1117,31 @@ function createMachineFromConfig(json, implementations = {}) {
|
|
|
466
1117
|
});
|
|
467
1118
|
state.errored = true;
|
|
468
1119
|
}
|
|
1120
|
+
function executeActionDefinition(action, x, enq, params, parentState, stack) {
|
|
1121
|
+
const definition = json.actions?.[action.type];
|
|
1122
|
+
if (!definition) {
|
|
1123
|
+
enq(x.actions[action.type], params);
|
|
1124
|
+
return {
|
|
1125
|
+
context: undefined,
|
|
1126
|
+
errored: parentState.errored
|
|
1127
|
+
};
|
|
1128
|
+
}
|
|
1129
|
+
if (stack.includes(action.type)) {
|
|
1130
|
+
raiseErrorExecution(parentState, 'action', new Error(`Circular action reference: ${stack.concat(action.type).join(' -> ')}`));
|
|
1131
|
+
return {
|
|
1132
|
+
context: undefined,
|
|
1133
|
+
errored: true
|
|
1134
|
+
};
|
|
1135
|
+
}
|
|
1136
|
+
const definitions = Array.isArray(definition) ? definition : [definition];
|
|
1137
|
+
return executeActions(definitions, {
|
|
1138
|
+
...x,
|
|
1139
|
+
params
|
|
1140
|
+
}, enq, parentState, stack.concat(action.type));
|
|
1141
|
+
}
|
|
1142
|
+
|
|
469
1143
|
/** Execute an array of SCXML action JSON descriptors with context and enqueue. */
|
|
470
|
-
function executeActions(actions, x, enq, parentState) {
|
|
1144
|
+
function executeActions(actions, x, enq, parentState, stack = []) {
|
|
471
1145
|
const state = parentState ?? {
|
|
472
1146
|
enq,
|
|
473
1147
|
errored: false
|
|
@@ -475,22 +1149,34 @@ function createMachineFromConfig(json, implementations = {}) {
|
|
|
475
1149
|
let context;
|
|
476
1150
|
for (const action of actions) {
|
|
477
1151
|
if (state.errored) break;
|
|
478
|
-
if (
|
|
1152
|
+
if (isResolvable(action)) {
|
|
1153
|
+
const result = evaluateResolvable(action, 'action', makeScope(x, {
|
|
1154
|
+
params: x.params,
|
|
1155
|
+
enq
|
|
1156
|
+
}), '$.actions');
|
|
1157
|
+
if (result && typeof result === 'object' && 'context' in result && result.context) {
|
|
1158
|
+
context ??= {};
|
|
1159
|
+
Object.assign(context, result.context);
|
|
1160
|
+
}
|
|
1161
|
+
} else if (isBuiltInActionJSON(action)) {
|
|
479
1162
|
switch (action.type) {
|
|
480
1163
|
case '@xstate.raise':
|
|
481
1164
|
{
|
|
482
1165
|
// Tag as external if it has a delay (from <send>, not <raise>).
|
|
483
1166
|
// Attach _scxmlSendId so _event.sendid can be read in expressions.
|
|
484
1167
|
const isExternal = action.delay !== undefined;
|
|
1168
|
+
const resolvedEvent = resolveValue(action.event, 'actionParams', makeScope(x, {
|
|
1169
|
+
params: x.params
|
|
1170
|
+
}), '$.actions.event');
|
|
485
1171
|
const event = isExternal || action.id !== undefined ? {
|
|
486
|
-
...
|
|
1172
|
+
...resolvedEvent,
|
|
487
1173
|
...(isExternal ? {
|
|
488
1174
|
_scxmlExternal: true
|
|
489
1175
|
} : {}),
|
|
490
1176
|
...(action.id !== undefined ? {
|
|
491
1177
|
_scxmlSendId: action.id
|
|
492
1178
|
} : {})
|
|
493
|
-
} :
|
|
1179
|
+
} : resolvedEvent;
|
|
494
1180
|
enq.raise(event, {
|
|
495
1181
|
id: action.id,
|
|
496
1182
|
delay: action.delay
|
|
@@ -504,11 +1190,15 @@ function createMachineFromConfig(json, implementations = {}) {
|
|
|
504
1190
|
enq.log(...action.args);
|
|
505
1191
|
break;
|
|
506
1192
|
case '@xstate.emit':
|
|
507
|
-
enq.emit(action.event
|
|
1193
|
+
enq.emit(resolveValue(action.event, 'actionParams', makeScope(x, {
|
|
1194
|
+
params: x.params
|
|
1195
|
+
}), '$.actions.event'));
|
|
508
1196
|
break;
|
|
509
1197
|
case '@xstate.assign':
|
|
510
1198
|
context ??= {};
|
|
511
|
-
Object.assign(context, action.context
|
|
1199
|
+
Object.assign(context, resolveValue(action.context, 'transitionContext', makeScope(x, {
|
|
1200
|
+
params: x.params
|
|
1201
|
+
}), '$.actions.context'));
|
|
512
1202
|
break;
|
|
513
1203
|
default:
|
|
514
1204
|
throw new Error(`Unknown built-in action: ${action.type}`);
|
|
@@ -547,8 +1237,10 @@ function createMachineFromConfig(json, implementations = {}) {
|
|
|
547
1237
|
let delay;
|
|
548
1238
|
try {
|
|
549
1239
|
eventType = scxmlAction.eventexpr ? evaluateExpr(mergedContext, scxmlAction.eventexpr, x.event, x.self) : scxmlAction.event || 'unknown';
|
|
1240
|
+
eventType = normalizeScxmlEventType(eventType);
|
|
550
1241
|
eventData = {
|
|
551
|
-
type: eventType
|
|
1242
|
+
type: eventType,
|
|
1243
|
+
_scxmlEventName: toScxmlEventName(eventType)
|
|
552
1244
|
};
|
|
553
1245
|
if (scxmlAction.params) {
|
|
554
1246
|
for (const param of scxmlAction.params) {
|
|
@@ -588,25 +1280,50 @@ function createMachineFromConfig(json, implementations = {}) {
|
|
|
588
1280
|
const childRef = x.children?.[childId];
|
|
589
1281
|
if (childRef) {
|
|
590
1282
|
enq.sendTo(childRef, eventData);
|
|
1283
|
+
} else if (childId.startsWith('scxml_') && childId !== `scxml_${_scxmlSessionId}`) {
|
|
1284
|
+
const message = `Unable to dispatch event to target: ${target}`;
|
|
1285
|
+
enq.raise({
|
|
1286
|
+
type: 'xstate.error.communication',
|
|
1287
|
+
error: {
|
|
1288
|
+
tagname: 'send',
|
|
1289
|
+
message,
|
|
1290
|
+
line: NaN,
|
|
1291
|
+
column: NaN,
|
|
1292
|
+
reason: message
|
|
1293
|
+
},
|
|
1294
|
+
_scxmlEventType: 'platform',
|
|
1295
|
+
_scxmlEventName: 'error.communication',
|
|
1296
|
+
_scxmlEventData: {
|
|
1297
|
+
tagname: 'send',
|
|
1298
|
+
message,
|
|
1299
|
+
line: NaN,
|
|
1300
|
+
column: NaN,
|
|
1301
|
+
reason: message
|
|
1302
|
+
}
|
|
1303
|
+
});
|
|
591
1304
|
} else {
|
|
592
|
-
//
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
}
|
|
596
|
-
enq.raise(eventData, {
|
|
1305
|
+
// This SCXML session id uses the external event queue.
|
|
1306
|
+
eventData._scxmlExternal = true;
|
|
1307
|
+
enq.sendTo(x.self, eventData, {
|
|
597
1308
|
id: scxmlAction.id,
|
|
598
1309
|
delay
|
|
599
1310
|
});
|
|
600
1311
|
}
|
|
601
1312
|
} else {
|
|
602
|
-
//
|
|
603
|
-
|
|
1313
|
+
// #_internal uses the internal queue; self-send/no target use the
|
|
1314
|
+
// external event queue.
|
|
1315
|
+
if (isInternalTarget) {
|
|
1316
|
+
enq.raise(eventData, {
|
|
1317
|
+
id: scxmlAction.id,
|
|
1318
|
+
delay
|
|
1319
|
+
});
|
|
1320
|
+
} else {
|
|
604
1321
|
eventData._scxmlExternal = true;
|
|
1322
|
+
enq.sendTo(x.self, eventData, {
|
|
1323
|
+
id: scxmlAction.id,
|
|
1324
|
+
delay
|
|
1325
|
+
});
|
|
605
1326
|
}
|
|
606
|
-
enq.raise(eventData, {
|
|
607
|
-
id: scxmlAction.id,
|
|
608
|
-
delay
|
|
609
|
-
});
|
|
610
1327
|
}
|
|
611
1328
|
} else if (action.type === 'scxml.cancel') {
|
|
612
1329
|
const scxmlAction = action;
|
|
@@ -736,7 +1453,14 @@ function createMachineFromConfig(json, implementations = {}) {
|
|
|
736
1453
|
}
|
|
737
1454
|
}
|
|
738
1455
|
} else {
|
|
739
|
-
|
|
1456
|
+
const params = resolveValue(action.params, 'actionParams', makeScope(x, {
|
|
1457
|
+
params: x.params
|
|
1458
|
+
}), '$.actions.params');
|
|
1459
|
+
const result = executeActionDefinition(action, x, enq, params, state, stack);
|
|
1460
|
+
if (result.context) {
|
|
1461
|
+
context ??= {};
|
|
1462
|
+
Object.assign(context, result.context);
|
|
1463
|
+
}
|
|
740
1464
|
}
|
|
741
1465
|
}
|
|
742
1466
|
return {
|
|
@@ -757,30 +1481,63 @@ function createMachineFromConfig(json, implementations = {}) {
|
|
|
757
1481
|
// a separate XState transition with its own guard and optional .to.
|
|
758
1482
|
// This ensures guards are evaluated by XState's evaluateCandidate (once,
|
|
759
1483
|
// with pre-exit context) and NOT re-evaluated in computeEntrySet.
|
|
760
|
-
return transitions.map(t => {
|
|
1484
|
+
return transitions.map((t, index) => {
|
|
1485
|
+
if (isResolvable(t)) {
|
|
1486
|
+
return (x, enq) => evaluateResolvable(t, 'transition', makeScope(x, {
|
|
1487
|
+
enq
|
|
1488
|
+
}), `$.transition${transitions.length > 1 ? `[${index}]` : ''}`);
|
|
1489
|
+
}
|
|
761
1490
|
const target = Array.isArray(t.target) ? t.target[0] : t.target;
|
|
762
1491
|
const targetConfig = t.target;
|
|
1492
|
+
const guard = resolveCondition(t.guard, 'guard', '$.transition.guard');
|
|
1493
|
+
const resolveTransitionContext = x => t.context ? resolveValue(t.context, 'transitionContext', makeScope(x), '$.transition.context') : undefined;
|
|
1494
|
+
const resolveTransitionInput = x => t.input !== undefined ? resolveValue(t.input, 'input', makeScope(x), '$.transition.input') : undefined;
|
|
763
1495
|
|
|
764
1496
|
// No guard and no actions: simple static config
|
|
765
|
-
if (!
|
|
1497
|
+
if (!guard && !t.actions?.length) {
|
|
1498
|
+
if (t.context) {
|
|
1499
|
+
return {
|
|
1500
|
+
to: x => ({
|
|
1501
|
+
target: targetConfig,
|
|
1502
|
+
context: resolveTransitionContext(x),
|
|
1503
|
+
reenter: t.reenter
|
|
1504
|
+
}),
|
|
1505
|
+
description: t.description,
|
|
1506
|
+
meta: t.meta,
|
|
1507
|
+
input: t.input !== undefined ? resolveTransitionInput : undefined
|
|
1508
|
+
};
|
|
1509
|
+
}
|
|
766
1510
|
return {
|
|
767
1511
|
target: targetConfig,
|
|
768
1512
|
description: t.description,
|
|
769
1513
|
reenter: t.reenter,
|
|
770
1514
|
meta: t.meta,
|
|
771
|
-
input: t.input
|
|
1515
|
+
input: t.input !== undefined ? resolveTransitionInput : undefined
|
|
772
1516
|
};
|
|
773
1517
|
}
|
|
774
1518
|
|
|
775
1519
|
// Guard but no actions: static config with guard
|
|
776
|
-
if (
|
|
1520
|
+
if (guard && !t.actions?.length) {
|
|
1521
|
+
if (t.context) {
|
|
1522
|
+
return {
|
|
1523
|
+
guard,
|
|
1524
|
+
to: x => ({
|
|
1525
|
+
target: targetConfig,
|
|
1526
|
+
context: resolveTransitionContext(x),
|
|
1527
|
+
reenter: t.reenter
|
|
1528
|
+
}),
|
|
1529
|
+
description: t.description,
|
|
1530
|
+
meta: t.meta,
|
|
1531
|
+
input: t.input !== undefined ? resolveTransitionInput : undefined
|
|
1532
|
+
};
|
|
1533
|
+
}
|
|
777
1534
|
return {
|
|
778
1535
|
target: targetConfig,
|
|
779
|
-
guard
|
|
1536
|
+
guard,
|
|
780
1537
|
description: t.description,
|
|
781
1538
|
reenter: t.reenter,
|
|
782
1539
|
meta: t.meta,
|
|
783
|
-
input: t.input
|
|
1540
|
+
input: t.input !== undefined ? resolveTransitionInput : undefined
|
|
784
1541
|
};
|
|
785
1542
|
}
|
|
786
1543
|
|
|
@@ -789,12 +1546,13 @@ function createMachineFromConfig(json, implementations = {}) {
|
|
|
789
1546
|
// For parallel (entries exist), first entry re-executes all in document order.
|
|
790
1547
|
if (!target) {
|
|
791
1548
|
return {
|
|
792
|
-
guard
|
|
1549
|
+
guard,
|
|
793
1550
|
description: t.description,
|
|
794
1551
|
reenter: t.reenter,
|
|
795
1552
|
meta: t.meta,
|
|
796
|
-
input: t.input,
|
|
1553
|
+
input: t.input !== undefined ? resolveTransitionInput : undefined,
|
|
797
1554
|
to: (x, enq) => {
|
|
1555
|
+
const context = resolveTransitionContext(x);
|
|
798
1556
|
if (t.actions?.length) {
|
|
799
1557
|
// Track for parallel re-execution (dedup by reference)
|
|
800
1558
|
if (!allTransitionActions.includes(t.actions)) {
|
|
@@ -806,11 +1564,16 @@ function createMachineFromConfig(json, implementations = {}) {
|
|
|
806
1564
|
const result = executeActions(t.actions, x, enq);
|
|
807
1565
|
if (result.context) {
|
|
808
1566
|
return {
|
|
809
|
-
context:
|
|
1567
|
+
context: {
|
|
1568
|
+
...context,
|
|
1569
|
+
...result.context
|
|
1570
|
+
}
|
|
810
1571
|
};
|
|
811
1572
|
}
|
|
812
1573
|
}
|
|
813
|
-
return {
|
|
1574
|
+
return context ? {
|
|
1575
|
+
context
|
|
1576
|
+
} : {};
|
|
814
1577
|
}
|
|
815
1578
|
};
|
|
816
1579
|
}
|
|
@@ -820,12 +1583,13 @@ function createMachineFromConfig(json, implementations = {}) {
|
|
|
820
1583
|
// already validated it. The .to just stores actions for entry to execute.
|
|
821
1584
|
// Map by target state ID so parallel transitions each get their own actions.
|
|
822
1585
|
return {
|
|
823
|
-
guard
|
|
1586
|
+
guard,
|
|
824
1587
|
description: t.description,
|
|
825
1588
|
reenter: t.reenter,
|
|
826
1589
|
meta: t.meta,
|
|
827
|
-
input: t.input,
|
|
1590
|
+
input: t.input !== undefined ? resolveTransitionInput : undefined,
|
|
828
1591
|
to: (_x, _enq) => {
|
|
1592
|
+
const context = resolveTransitionContext(_x);
|
|
829
1593
|
if (t.actions?.length) {
|
|
830
1594
|
const targetId = target.replace(/^#/, '');
|
|
831
1595
|
pendingTransitionActionsMap[targetId] = t.actions;
|
|
@@ -836,18 +1600,32 @@ function createMachineFromConfig(json, implementations = {}) {
|
|
|
836
1600
|
}
|
|
837
1601
|
return {
|
|
838
1602
|
target: targetConfig,
|
|
1603
|
+
context,
|
|
839
1604
|
reenter: t.reenter
|
|
840
1605
|
};
|
|
841
1606
|
}
|
|
842
1607
|
};
|
|
843
1608
|
});
|
|
844
1609
|
}
|
|
1610
|
+
if (json.delays) {
|
|
1611
|
+
for (const key of Object.keys(json.delays)) {
|
|
1612
|
+
const delay = json.delays[key];
|
|
1613
|
+
if (typeof delay === 'number') {
|
|
1614
|
+
resolvedImplementations.delays[key] = delay;
|
|
1615
|
+
} else if (typeof delay === 'string') {
|
|
1616
|
+
resolvedImplementations.delays[key] = delayToMs(delay);
|
|
1617
|
+
} else if (delay && typeof delay === 'object') {
|
|
1618
|
+
resolvedImplementations.delays[key] = args => delayToMs(resolveValue(delay.duration, 'delay', makeScope(args), `$.delays.${key}.duration`));
|
|
1619
|
+
}
|
|
1620
|
+
}
|
|
1621
|
+
}
|
|
1622
|
+
assertMachineJSON(json, resolvedImplementations, expressionResolver);
|
|
845
1623
|
const rootNodeConfig = iterNode(json);
|
|
846
1624
|
const contextConfig = json.context ? {
|
|
847
|
-
context: json.context
|
|
1625
|
+
context: args => resolveValue(json.context, 'context', args, '$.context')
|
|
848
1626
|
} : {};
|
|
849
1627
|
_scxmlMachineName = json.id || '';
|
|
850
|
-
_scxmlSessionId = '
|
|
1628
|
+
_scxmlSessionId = 'session_scxml';
|
|
851
1629
|
const machine = createMachine({
|
|
852
1630
|
...rootNodeConfig,
|
|
853
1631
|
...contextConfig,
|
|
@@ -892,7 +1670,7 @@ function createMachineFromConfig(json, implementations = {}) {
|
|
|
892
1670
|
},
|
|
893
1671
|
'xstate.not': (args, params) => {
|
|
894
1672
|
const inner = params?.guard;
|
|
895
|
-
const innerImpl = inner &&
|
|
1673
|
+
const innerImpl = inner && args.guards?.[inner.type];
|
|
896
1674
|
if (!innerImpl) {
|
|
897
1675
|
throw new Error(`Guard '${inner?.type}' referenced by 'xstate.not' is not implemented.`);
|
|
898
1676
|
}
|
|
@@ -901,7 +1679,7 @@ function createMachineFromConfig(json, implementations = {}) {
|
|
|
901
1679
|
};
|
|
902
1680
|
const provided = machine.provide({
|
|
903
1681
|
actions: resolvedImplementations.actions,
|
|
904
|
-
|
|
1682
|
+
actorSources: resolvedImplementations.actorSources,
|
|
905
1683
|
guards: {
|
|
906
1684
|
...providedGuards,
|
|
907
1685
|
...resolvedImplementations.guards
|
|
@@ -915,7 +1693,7 @@ function createMachineFromConfig(json, implementations = {}) {
|
|
|
915
1693
|
return provided;
|
|
916
1694
|
}
|
|
917
1695
|
function isBuiltInActionJSON(action) {
|
|
918
|
-
return action.type.startsWith('@xstate.');
|
|
1696
|
+
return !isResolvable(action) && action.type.startsWith('@xstate.');
|
|
919
1697
|
}
|
|
920
1698
|
|
|
921
1699
|
/**
|
|
@@ -923,24 +1701,17 @@ function isBuiltInActionJSON(action) {
|
|
|
923
1701
|
*
|
|
924
1702
|
* `machineConfigToJSON` converts a machine config into a JSON-safe structure
|
|
925
1703
|
* (the `MachineJSON` shape accepted by `createMachineFromConfig`). The boundary
|
|
926
|
-
* between serializable structure and runtime implementations is explicit:
|
|
927
|
-
*
|
|
928
|
-
* runtime schemas
|
|
929
|
-
* rather than silently dropped.
|
|
930
|
-
*
|
|
931
|
-
* A machine definition is fully portable iff its JSON contains no
|
|
932
|
-
* `$unserializable` markers; reviving a definition that has markers requires
|
|
933
|
-
* re-providing those implementations (e.g. via `machine.provide(...)` or the
|
|
934
|
-
* `actions`/`guards`/`actors` maps on `createMachineFromConfig` revival).
|
|
1704
|
+
* between serializable structure and runtime implementations is explicit:
|
|
1705
|
+
* functions are represented as `@code` source objects, while actor logic,
|
|
1706
|
+
* runtime schemas, and other non-data runtime values are omitted.
|
|
935
1707
|
*/
|
|
936
1708
|
|
|
937
1709
|
/**
|
|
938
1710
|
* Returns the JSON-serializable definition of a machine.
|
|
939
1711
|
*
|
|
940
|
-
* Inline functions
|
|
941
|
-
*
|
|
942
|
-
*
|
|
943
|
-
* round-trip):
|
|
1712
|
+
* Inline functions are represented as `{ "@code": string, "@lang": "ts" }`. A
|
|
1713
|
+
* machine created via `createMachineFromConfig` returns its original JSON
|
|
1714
|
+
* config (lossless round-trip):
|
|
944
1715
|
*
|
|
945
1716
|
* ```ts
|
|
946
1717
|
* import { serializeMachine, createMachineFromConfig } from 'xstate';
|
|
@@ -955,48 +1726,57 @@ function serializeMachine(machine) {
|
|
|
955
1726
|
return machine._json ?? machineConfigToJSON(machine.config);
|
|
956
1727
|
}
|
|
957
1728
|
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
function marker(kind, id) {
|
|
964
|
-
const m = {
|
|
965
|
-
$unserializable: kind
|
|
1729
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
|
|
1730
|
+
function codeExpression(fn) {
|
|
1731
|
+
return {
|
|
1732
|
+
'@code': fn.toString(),
|
|
1733
|
+
'@lang': 'ts'
|
|
966
1734
|
};
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
1735
|
+
}
|
|
1736
|
+
function isActorLogic(value) {
|
|
1737
|
+
return !!value && typeof value === 'object' && typeof value.transition === 'function' && typeof value.getInitialSnapshot === 'function';
|
|
1738
|
+
}
|
|
1739
|
+
function isRuntimeSchema(value) {
|
|
1740
|
+
return !!value && typeof value === 'object' && '~standard' in value;
|
|
971
1741
|
}
|
|
972
1742
|
|
|
973
|
-
/** JSON-safe deep copy of a plain value; functions become
|
|
1743
|
+
/** JSON-safe deep copy of a plain value; functions become code expressions. */
|
|
974
1744
|
function valueToJSON(value) {
|
|
975
1745
|
if (typeof value === 'function') {
|
|
976
|
-
return
|
|
1746
|
+
return codeExpression(value);
|
|
977
1747
|
}
|
|
978
1748
|
if (value === null || typeof value !== 'object') {
|
|
979
|
-
return typeof value === 'bigint' || typeof value === 'symbol' ?
|
|
1749
|
+
return typeof value === 'bigint' || typeof value === 'symbol' ? undefined : value;
|
|
1750
|
+
}
|
|
1751
|
+
if (isActorLogic(value)) {
|
|
1752
|
+
return undefined;
|
|
1753
|
+
}
|
|
1754
|
+
if (isRuntimeSchema(value)) {
|
|
1755
|
+
return undefined;
|
|
980
1756
|
}
|
|
981
1757
|
if (Array.isArray(value)) {
|
|
982
|
-
return value.map(valueToJSON);
|
|
1758
|
+
return value.map(valueToJSON).filter(item => item !== undefined);
|
|
983
1759
|
}
|
|
984
1760
|
if (value.constructor !== Object && value.constructor !== undefined) {
|
|
985
1761
|
// Class instances (actor logic, schemas, dates, ...) are not portable.
|
|
986
|
-
return
|
|
1762
|
+
return undefined;
|
|
987
1763
|
}
|
|
988
1764
|
const result = {};
|
|
989
1765
|
for (const key of Object.keys(value)) {
|
|
990
1766
|
const v = value[key];
|
|
991
1767
|
if (v !== undefined) {
|
|
992
|
-
|
|
1768
|
+
const jsonValue = valueToJSON(v);
|
|
1769
|
+
if (jsonValue !== undefined) {
|
|
1770
|
+
result[key] = jsonValue;
|
|
1771
|
+
}
|
|
993
1772
|
}
|
|
994
1773
|
}
|
|
995
1774
|
return result;
|
|
996
1775
|
}
|
|
997
1776
|
function invokeToJSON(invoke) {
|
|
998
1777
|
if (Array.isArray(invoke)) {
|
|
999
|
-
|
|
1778
|
+
const values = invoke.map(invokeToJSON).filter(value => value !== undefined);
|
|
1779
|
+
return values.length ? values : undefined;
|
|
1000
1780
|
}
|
|
1001
1781
|
const def = invoke;
|
|
1002
1782
|
const result = {};
|
|
@@ -1005,37 +1785,47 @@ function invokeToJSON(invoke) {
|
|
|
1005
1785
|
if (value === undefined) {
|
|
1006
1786
|
continue;
|
|
1007
1787
|
}
|
|
1008
|
-
result[key] = key === 'src' && typeof value !== 'string' ?
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1788
|
+
result[key] = key === 'src' && typeof value !== 'string' ? value.id : valueToJSON(value);
|
|
1789
|
+
if (result[key] === undefined) {
|
|
1790
|
+
delete result[key];
|
|
1791
|
+
}
|
|
1012
1792
|
}
|
|
1013
|
-
return result;
|
|
1793
|
+
return result.src === undefined ? undefined : result;
|
|
1014
1794
|
}
|
|
1015
|
-
function implementationsToJSON(map
|
|
1795
|
+
function implementationsToJSON(map) {
|
|
1016
1796
|
if (!map) {
|
|
1017
1797
|
return undefined;
|
|
1018
1798
|
}
|
|
1019
|
-
// Keys are preserved — they are the contract a revived machine must
|
|
1020
|
-
// fulfill via provide() — while values become markers (or stay, for
|
|
1021
|
-
// JSON-safe values like numeric delays).
|
|
1022
1799
|
const result = {};
|
|
1023
1800
|
for (const key of Object.keys(map)) {
|
|
1024
1801
|
const value = map[key];
|
|
1025
|
-
|
|
1802
|
+
if (typeof value === 'function') {
|
|
1803
|
+
result[key] = undefined;
|
|
1804
|
+
} else {
|
|
1805
|
+
result[key] = valueToJSON(value);
|
|
1806
|
+
}
|
|
1807
|
+
if (result[key] === undefined) {
|
|
1808
|
+
delete result[key];
|
|
1809
|
+
}
|
|
1026
1810
|
}
|
|
1027
1811
|
return result;
|
|
1028
1812
|
}
|
|
1029
1813
|
function stateNodeConfigToJSON(config) {
|
|
1030
1814
|
const result = {};
|
|
1031
|
-
for (const key of
|
|
1815
|
+
for (const key of Object.keys(config)) {
|
|
1816
|
+
if (key === 'states' || key === 'invoke') {
|
|
1817
|
+
continue;
|
|
1818
|
+
}
|
|
1032
1819
|
const value = config[key];
|
|
1033
1820
|
if (value !== undefined) {
|
|
1034
1821
|
result[key] = valueToJSON(value);
|
|
1035
1822
|
}
|
|
1036
1823
|
}
|
|
1037
1824
|
if (config.invoke !== undefined) {
|
|
1038
|
-
|
|
1825
|
+
const invoke = invokeToJSON(config.invoke);
|
|
1826
|
+
if (invoke !== undefined) {
|
|
1827
|
+
result.invoke = invoke;
|
|
1828
|
+
}
|
|
1039
1829
|
}
|
|
1040
1830
|
if (config.states) {
|
|
1041
1831
|
const states = {};
|
|
@@ -1049,7 +1839,7 @@ function stateNodeConfigToJSON(config) {
|
|
|
1049
1839
|
|
|
1050
1840
|
/**
|
|
1051
1841
|
* Converts a machine config (as passed to `createMachine`) to its JSON-safe
|
|
1052
|
-
* definition.
|
|
1842
|
+
* definition.
|
|
1053
1843
|
*/
|
|
1054
1844
|
function machineConfigToJSON(config) {
|
|
1055
1845
|
const result = stateNodeConfigToJSON(config);
|
|
@@ -1062,16 +1852,24 @@ function machineConfigToJSON(config) {
|
|
|
1062
1852
|
const value = config.schemas[key];
|
|
1063
1853
|
if (value && typeof value === 'object' && !('~standard' in value)) {
|
|
1064
1854
|
// Map-form schemas (events/emitted): preserve event-type keys.
|
|
1065
|
-
schemas[key] = implementationsToJSON(value
|
|
1855
|
+
schemas[key] = implementationsToJSON(value);
|
|
1066
1856
|
} else {
|
|
1067
|
-
schemas[key] =
|
|
1857
|
+
schemas[key] = valueToJSON(value);
|
|
1858
|
+
}
|
|
1859
|
+
if (schemas[key] === undefined) {
|
|
1860
|
+
delete schemas[key];
|
|
1068
1861
|
}
|
|
1069
1862
|
}
|
|
1070
1863
|
result.schemas = schemas;
|
|
1071
1864
|
}
|
|
1072
|
-
for (const key of ['actions', 'guards', '
|
|
1865
|
+
for (const key of ['actions', 'guards', 'actorSources', 'delays']) {
|
|
1073
1866
|
if (config[key]) {
|
|
1074
|
-
|
|
1867
|
+
const value = implementationsToJSON(config[key]);
|
|
1868
|
+
if (value && Object.keys(value).length) {
|
|
1869
|
+
result[key] = value;
|
|
1870
|
+
} else {
|
|
1871
|
+
delete result[key];
|
|
1872
|
+
}
|
|
1075
1873
|
}
|
|
1076
1874
|
}
|
|
1077
1875
|
return result;
|
|
@@ -1174,6 +1972,28 @@ function isTypeSchema(value) {
|
|
|
1174
1972
|
|
|
1175
1973
|
/** State schema with optional schemas.input and nested states */
|
|
1176
1974
|
|
|
1975
|
+
/**
|
|
1976
|
+
* The sibling state schemas of a dotted path: the children of the path's
|
|
1977
|
+
* parent. A bare transition target resolves relative to the parent, so the
|
|
1978
|
+
* valid targets for a path's config are its siblings. For a dotless (top-level)
|
|
1979
|
+
* path the siblings are the root states.
|
|
1980
|
+
*/
|
|
1981
|
+
|
|
1982
|
+
// dotless path: siblings are the current level's states
|
|
1983
|
+
|
|
1984
|
+
/**
|
|
1985
|
+
* Resolves a dotted state path (e.g. `'parent.child'`) into the leaf
|
|
1986
|
+
* `SetupStateSchema` it addresses, or `never` if any segment is missing.
|
|
1987
|
+
*/
|
|
1988
|
+
|
|
1989
|
+
/** Union of every addressable dotted path into a setup states tree */
|
|
1990
|
+
|
|
1991
|
+
/**
|
|
1992
|
+
* Shared body of both `createStateConfig` overloads: the
|
|
1993
|
+
* `StateNodeConfigWithNestedInput` instantiation whose only varying parts are
|
|
1994
|
+
* the leading sibling-schemas and state-schema arguments.
|
|
1995
|
+
*/
|
|
1996
|
+
|
|
1177
1997
|
/** Extracts input type from a state schema */
|
|
1178
1998
|
|
|
1179
1999
|
/**
|
|
@@ -1229,33 +2049,142 @@ function isTypeSchema(value) {
|
|
|
1229
2049
|
* });
|
|
1230
2050
|
* ```
|
|
1231
2051
|
*/
|
|
2052
|
+
|
|
1232
2053
|
function setup(config = {}) {
|
|
1233
2054
|
const {
|
|
1234
2055
|
states = {},
|
|
1235
|
-
schemas
|
|
2056
|
+
schemas,
|
|
2057
|
+
actions,
|
|
2058
|
+
actorSources,
|
|
2059
|
+
guards,
|
|
2060
|
+
delays
|
|
1236
2061
|
} = config;
|
|
1237
2062
|
return {
|
|
2063
|
+
extend(extension) {
|
|
2064
|
+
return setup(mergeSetupConfigs(config, extension));
|
|
2065
|
+
},
|
|
1238
2066
|
createMachine(machineConfig) {
|
|
1239
2067
|
const configSchemas = machineConfig.schemas;
|
|
1240
|
-
const mergedSchemas =
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
2068
|
+
const mergedSchemas = mergeSchemas(configSchemas, schemas);
|
|
2069
|
+
const mergedActions = mergeMaps(actions, machineConfig.actions);
|
|
2070
|
+
const mergedActorSources = mergeMaps(actorSources, machineConfig.actorSources);
|
|
2071
|
+
const mergedGuards = mergeMaps(guards, machineConfig.guards);
|
|
2072
|
+
const mergedDelays = mergeMaps(delays, machineConfig.delays);
|
|
2073
|
+
return new StateMachine({
|
|
1245
2074
|
...machineConfig,
|
|
1246
|
-
|
|
1247
|
-
|
|
2075
|
+
...(mergedSchemas ? {
|
|
2076
|
+
schemas: mergedSchemas
|
|
2077
|
+
} : undefined),
|
|
2078
|
+
...(mergedActions ? {
|
|
2079
|
+
actions: mergedActions
|
|
2080
|
+
} : undefined),
|
|
2081
|
+
...(mergedActorSources ? {
|
|
2082
|
+
actorSources: mergedActorSources
|
|
2083
|
+
} : undefined),
|
|
2084
|
+
...(mergedGuards ? {
|
|
2085
|
+
guards: mergedGuards
|
|
2086
|
+
} : undefined),
|
|
2087
|
+
...(mergedDelays ? {
|
|
2088
|
+
delays: mergedDelays
|
|
2089
|
+
} : undefined)
|
|
2090
|
+
});
|
|
2091
|
+
},
|
|
2092
|
+
createStateConfig(...args) {
|
|
2093
|
+
return args.length > 1 ? args[1] : args[0];
|
|
2094
|
+
},
|
|
2095
|
+
states,
|
|
2096
|
+
schemas: schemas ?? {}
|
|
2097
|
+
};
|
|
2098
|
+
}
|
|
2099
|
+
function createSystem(_config = {}) {
|
|
2100
|
+
const runtimeRef = {};
|
|
2101
|
+
const pendingObservers = [];
|
|
2102
|
+
const flushObservers = () => {
|
|
2103
|
+
const runtime = runtimeRef.current;
|
|
2104
|
+
if (!runtime) {
|
|
2105
|
+
return;
|
|
2106
|
+
}
|
|
2107
|
+
for (const entry of pendingObservers) {
|
|
2108
|
+
if (entry.active && !entry.subscription) {
|
|
2109
|
+
entry.subscription = runtime.inspect(entry.observer);
|
|
2110
|
+
}
|
|
2111
|
+
}
|
|
2112
|
+
};
|
|
2113
|
+
return {
|
|
2114
|
+
createActor(logic, options) {
|
|
2115
|
+
const actor = createActor(logic, {
|
|
2116
|
+
...options,
|
|
2117
|
+
_systemRef: runtimeRef
|
|
2118
|
+
});
|
|
2119
|
+
flushObservers();
|
|
2120
|
+
return actor;
|
|
2121
|
+
},
|
|
2122
|
+
get(key) {
|
|
2123
|
+
return runtimeRef.current?.get(key);
|
|
1248
2124
|
},
|
|
1249
|
-
|
|
1250
|
-
return
|
|
2125
|
+
getAll() {
|
|
2126
|
+
return runtimeRef.current?.getAll() ?? {};
|
|
2127
|
+
},
|
|
2128
|
+
inspect(observer) {
|
|
2129
|
+
const runtime = runtimeRef.current;
|
|
2130
|
+
if (runtime) {
|
|
2131
|
+
return runtime.inspect(observer);
|
|
2132
|
+
}
|
|
2133
|
+
const entry = {
|
|
2134
|
+
observer: observer,
|
|
2135
|
+
active: true
|
|
2136
|
+
};
|
|
2137
|
+
pendingObservers.push(entry);
|
|
2138
|
+
return {
|
|
2139
|
+
unsubscribe() {
|
|
2140
|
+
entry.active = false;
|
|
2141
|
+
entry.subscription?.unsubscribe();
|
|
2142
|
+
}
|
|
2143
|
+
};
|
|
1251
2144
|
},
|
|
1252
|
-
|
|
2145
|
+
setup(config) {
|
|
2146
|
+
return config ? setup(config) : setup();
|
|
2147
|
+
}
|
|
2148
|
+
};
|
|
2149
|
+
}
|
|
2150
|
+
function mergeMaps(left, right) {
|
|
2151
|
+
return left || right ? {
|
|
2152
|
+
...left,
|
|
2153
|
+
...right
|
|
2154
|
+
} : undefined;
|
|
2155
|
+
}
|
|
2156
|
+
function mergeSchemas(left, right) {
|
|
2157
|
+
if (!left && !right) {
|
|
2158
|
+
return undefined;
|
|
2159
|
+
}
|
|
2160
|
+
return {
|
|
2161
|
+
...left,
|
|
2162
|
+
...right,
|
|
2163
|
+
events: mergeMaps(left?.events, right?.events),
|
|
2164
|
+
actions: mergeMaps(left?.actions, right?.actions),
|
|
2165
|
+
guards: mergeMaps(left?.guards, right?.guards),
|
|
2166
|
+
emitted: mergeMaps(left?.emitted, right?.emitted),
|
|
2167
|
+
children: mergeMaps(left?.children, right?.children)
|
|
2168
|
+
};
|
|
2169
|
+
}
|
|
2170
|
+
function mergeSetupConfigs(base, extension) {
|
|
2171
|
+
return {
|
|
2172
|
+
...base,
|
|
2173
|
+
...extension,
|
|
2174
|
+
schemas: mergeSchemas(base.schemas, extension.schemas),
|
|
2175
|
+
states: mergeMaps(base.states, extension.states),
|
|
2176
|
+
actions: mergeMaps(base.actions, extension.actions),
|
|
2177
|
+
actorSources: mergeMaps(base.actorSources, extension.actorSources),
|
|
2178
|
+
guards: mergeMaps(base.guards, extension.guards),
|
|
2179
|
+
delays: mergeMaps(base.delays, extension.delays)
|
|
1253
2180
|
};
|
|
1254
2181
|
}
|
|
1255
2182
|
|
|
1256
2183
|
/** @internal */
|
|
1257
2184
|
function createInertActorScope(actorLogic) {
|
|
1258
|
-
const self = createActor(actorLogic
|
|
2185
|
+
const self = createActor(actorLogic, {
|
|
2186
|
+
_inert: true
|
|
2187
|
+
});
|
|
1259
2188
|
const inertActorScope = {
|
|
1260
2189
|
self: self,
|
|
1261
2190
|
defer: () => {},
|
|
@@ -1273,7 +2202,7 @@ function createInertActorScope(actorLogic) {
|
|
|
1273
2202
|
/** @deprecated Use `initialTransition(…)` instead. */
|
|
1274
2203
|
function getInitialSnapshot(actorLogic, ...[input]) {
|
|
1275
2204
|
const actorScope = createInertActorScope(actorLogic);
|
|
1276
|
-
return actorLogic.
|
|
2205
|
+
return actorLogic.initialTransition(input, actorScope)[0];
|
|
1277
2206
|
}
|
|
1278
2207
|
|
|
1279
2208
|
/**
|
|
@@ -1563,14 +2492,9 @@ let SpecialTargets = /*#__PURE__*/function (SpecialTargets) {
|
|
|
1563
2492
|
* This is a pure function that does not execute `actions`.
|
|
1564
2493
|
*/
|
|
1565
2494
|
function transition(logic, snapshot, event) {
|
|
1566
|
-
const executableActions = [];
|
|
1567
2495
|
const actorScope = createInertActorScope(logic);
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
};
|
|
1571
|
-
const transitionResult = logic.transition(snapshot, event, actorScope);
|
|
1572
|
-
const [nextSnapshot, effects] = Array.isArray(transitionResult) ? transitionResult : [transitionResult, []];
|
|
1573
|
-
return [nextSnapshot, executableActions.concat(effects)];
|
|
2496
|
+
const [nextSnapshot, effects] = logic.transition(snapshot, event, actorScope);
|
|
2497
|
+
return [nextSnapshot, effects];
|
|
1574
2498
|
}
|
|
1575
2499
|
|
|
1576
2500
|
/**
|
|
@@ -1581,12 +2505,8 @@ function transition(logic, snapshot, event) {
|
|
|
1581
2505
|
* This is a pure function that does not execute `actions`.
|
|
1582
2506
|
*/
|
|
1583
2507
|
function initialTransition(logic, ...[input]) {
|
|
1584
|
-
const executableActions = [];
|
|
1585
2508
|
const actorScope = createInertActorScope(logic);
|
|
1586
|
-
|
|
1587
|
-
executableActions.push(action);
|
|
1588
|
-
};
|
|
1589
|
-
const nextSnapshot = logic.getInitialSnapshot(actorScope, input);
|
|
2509
|
+
const [nextSnapshot, executableActions] = logic.initialTransition(input, actorScope);
|
|
1590
2510
|
return [nextSnapshot, executableActions];
|
|
1591
2511
|
}
|
|
1592
2512
|
|
|
@@ -1780,4 +2700,4 @@ function waitFor(actorRef, predicate, options) {
|
|
|
1780
2700
|
});
|
|
1781
2701
|
}
|
|
1782
2702
|
|
|
1783
|
-
export { SimulatedClock, SpecialTargets, assertEvent, createMachine, createMachineFromConfig, createStateConfig, getInitialMicrosteps, getInitialSnapshot, getMicrosteps, getNextSnapshot, getNextTransitions, initialTransition, isTypeSchema, machineConfigToJSON, mapState, serializeMachine, setup, toPromise, transition, types, waitFor };
|
|
2703
|
+
export { SimulatedClock, SpecialTargets, assertEvent, createFSM, createMachine, createMachineFromConfig, createStateConfig, createSystem, getInitialMicrosteps, getInitialSnapshot, getMicrosteps, getNextSnapshot, getNextTransitions, initialTransition, isTypeSchema, machineConfigToJSON, mapState, serializeMachine, setup, toPromise, transition, types, waitFor };
|