xstate 6.0.0-alpha.11 → 6.0.0-alpha.13
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/dist/{StateMachine-0aa98f54.development.esm.js → StateMachine-193c2d4d.development.esm.js} +43 -2
- package/dist/{StateMachine-aef700df.cjs.js → StateMachine-1b26c5de.cjs.js} +43 -2
- package/dist/{StateMachine-da872dde.development.cjs.js → StateMachine-3120a7ff.development.cjs.js} +43 -2
- package/dist/{StateMachine-d4931336.esm.js → StateMachine-614c0f36.esm.js} +43 -2
- package/dist/declarations/src/StateMachine.d.ts +1 -0
- package/dist/declarations/src/actors/promise.d.ts +4 -2
- package/dist/declarations/src/createActor.d.ts +2 -8
- package/dist/declarations/src/createMachineFromConfig.d.ts +56 -25
- package/dist/declarations/src/fsm.d.ts +74 -0
- package/dist/declarations/src/index.d.ts +2 -1
- package/dist/declarations/src/serialize.d.ts +8 -22
- package/dist/declarations/src/setup.d.ts +54 -23
- package/dist/declarations/src/stateUtils.d.ts +3 -1
- package/dist/declarations/src/transitionActions.d.ts +1 -1
- package/dist/declarations/src/types.d.ts +17 -7
- package/dist/declarations/src/types.v6.d.ts +25 -6
- package/dist/{index-219cb621.development.cjs.js → index-08d86676.development.cjs.js} +48 -27
- package/dist/{index-559ceab3.cjs.js → index-32631949.cjs.js} +48 -27
- package/dist/{index-8828376f.esm.js → index-603c1cda.esm.js} +45 -28
- package/dist/{index-2f2fbd9b.development.esm.js → index-7081e0c9.development.esm.js} +45 -28
- 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.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 +835 -144
- package/dist/xstate.cjs.mjs +1 -0
- package/dist/xstate.development.cjs.js +835 -144
- package/dist/xstate.development.cjs.mjs +1 -0
- package/dist/xstate.development.esm.js +838 -148
- package/dist/xstate.esm.js +838 -148
- package/dist/xstate.umd.min.js +1 -1
- package/dist/xstate.umd.min.js.map +1 -1
- package/package.json +1 -1
package/dist/xstate.cjs.js
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
4
|
|
|
5
|
-
var dist_xstateActors = require('./index-
|
|
6
|
-
var StateMachine = require('./StateMachine-
|
|
5
|
+
var dist_xstateActors = require('./index-32631949.cjs.js');
|
|
6
|
+
var StateMachine = require('./StateMachine-1b26c5de.cjs.js');
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
* Asserts that the given event object is of the specified type or types. Throws
|
|
@@ -91,6 +91,339 @@ function createStateConfig(config) {
|
|
|
91
91
|
return config;
|
|
92
92
|
}
|
|
93
93
|
|
|
94
|
+
const emptyImplementations = {
|
|
95
|
+
actions: {},
|
|
96
|
+
actorSources: {},
|
|
97
|
+
guards: {},
|
|
98
|
+
delays: {}
|
|
99
|
+
};
|
|
100
|
+
const emptyExecutableActions = [];
|
|
101
|
+
const emptyRawActions = [];
|
|
102
|
+
const builtInActionSet = new Set(Object.values(dist_xstateActors.builtInActions));
|
|
103
|
+
function toArray(value) {
|
|
104
|
+
return value === undefined ? [] : Array.isArray(value) ? value : [value];
|
|
105
|
+
}
|
|
106
|
+
function resolveContext(context, input) {
|
|
107
|
+
return typeof context === 'function' ? context({
|
|
108
|
+
input
|
|
109
|
+
}) : context ?? {};
|
|
110
|
+
}
|
|
111
|
+
function resolveTransitionContext(context, args) {
|
|
112
|
+
return typeof context === 'function' ? context(args) : context;
|
|
113
|
+
}
|
|
114
|
+
function resolveInput(input, context, event) {
|
|
115
|
+
return typeof input === 'function' ? input({
|
|
116
|
+
context,
|
|
117
|
+
event
|
|
118
|
+
}) : input;
|
|
119
|
+
}
|
|
120
|
+
function mergeContextPatch(context, patch) {
|
|
121
|
+
for (const key of Object.keys(patch)) {
|
|
122
|
+
if (!Object.prototype.hasOwnProperty.call(context, key) || !Object.is(context[key], patch[key])) {
|
|
123
|
+
return {
|
|
124
|
+
...context,
|
|
125
|
+
...patch
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
return context;
|
|
130
|
+
}
|
|
131
|
+
function createSnapshot(value, context, input, machine, stateInput) {
|
|
132
|
+
return {
|
|
133
|
+
status: 'active',
|
|
134
|
+
output: undefined,
|
|
135
|
+
error: undefined,
|
|
136
|
+
value,
|
|
137
|
+
context,
|
|
138
|
+
input,
|
|
139
|
+
children: {},
|
|
140
|
+
_stateInput: stateInput,
|
|
141
|
+
machine
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
function cloneSnapshot(snapshot, value, context, stateInput) {
|
|
145
|
+
return {
|
|
146
|
+
status: snapshot.status,
|
|
147
|
+
output: snapshot.output,
|
|
148
|
+
error: snapshot.error,
|
|
149
|
+
value,
|
|
150
|
+
context,
|
|
151
|
+
input: snapshot.input,
|
|
152
|
+
children: snapshot.children,
|
|
153
|
+
_stateInput: stateInput,
|
|
154
|
+
machine: snapshot.machine
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
function stopSnapshot(snapshot) {
|
|
158
|
+
return {
|
|
159
|
+
status: 'stopped',
|
|
160
|
+
output: undefined,
|
|
161
|
+
error: undefined,
|
|
162
|
+
value: snapshot.value,
|
|
163
|
+
context: snapshot.context,
|
|
164
|
+
input: undefined,
|
|
165
|
+
children: snapshot.children,
|
|
166
|
+
_stateInput: snapshot._stateInput,
|
|
167
|
+
machine: snapshot.machine
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
function assertNoStringTransitions(config) {
|
|
171
|
+
for (const [stateKey, stateConfig] of Object.entries(config.states)) {
|
|
172
|
+
for (const [eventType, transitionConfig] of Object.entries(stateConfig.on ?? {})) {
|
|
173
|
+
for (const transition of toArray(transitionConfig)) {
|
|
174
|
+
if (typeof transition === 'string') {
|
|
175
|
+
const target = transition;
|
|
176
|
+
throw new Error(`Invalid transition for "${stateKey}.${eventType}": use { target: "${target}" } instead of a string target.`);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
function resolveSimpleEnqueuedActions(rawActions) {
|
|
183
|
+
const executableActions = [];
|
|
184
|
+
for (const action of rawActions) {
|
|
185
|
+
if (!action || typeof action !== 'object' || !('action' in action) || typeof action.action !== 'function' || builtInActionSet.has(action.action) || '_special' in action.action) {
|
|
186
|
+
return undefined;
|
|
187
|
+
}
|
|
188
|
+
executableActions.push({
|
|
189
|
+
type: action.action.name || '(anonymous)',
|
|
190
|
+
params: undefined,
|
|
191
|
+
args: action.args,
|
|
192
|
+
exec: action.args.length ? action.action.bind(null, ...action.args) : action.action
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
return executableActions;
|
|
196
|
+
}
|
|
197
|
+
function createFSM(config) {
|
|
198
|
+
const machine = {
|
|
199
|
+
id: config.id ?? '(fsm)',
|
|
200
|
+
implementations: emptyImplementations
|
|
201
|
+
};
|
|
202
|
+
assertNoStringTransitions(config);
|
|
203
|
+
const runActions = (snapshot, event, actorScope, rawActions) => {
|
|
204
|
+
if (!rawActions?.length) {
|
|
205
|
+
return [snapshot, emptyExecutableActions];
|
|
206
|
+
}
|
|
207
|
+
const simpleExecutableActions = resolveSimpleEnqueuedActions(rawActions);
|
|
208
|
+
if (simpleExecutableActions) {
|
|
209
|
+
return [snapshot, simpleExecutableActions];
|
|
210
|
+
}
|
|
211
|
+
return dist_xstateActors.resolveActionsWithContext(snapshot, event, actorScope, rawActions);
|
|
212
|
+
};
|
|
213
|
+
const runStateActions = (snapshot, event, actorScope, actionsConfig, stateInput, internalQueue) => {
|
|
214
|
+
if (!actionsConfig) {
|
|
215
|
+
return [snapshot, emptyExecutableActions];
|
|
216
|
+
}
|
|
217
|
+
const actions = [];
|
|
218
|
+
const enq = dist_xstateActors.createTransitionEnqueue(actorScope, actions, internalQueue, true);
|
|
219
|
+
let context;
|
|
220
|
+
const actionCount = Array.isArray(actionsConfig) ? actionsConfig.length : 1;
|
|
221
|
+
for (let i = 0; i < actionCount; i++) {
|
|
222
|
+
const action = Array.isArray(actionsConfig) ? actionsConfig[i] : actionsConfig;
|
|
223
|
+
const result = action({
|
|
224
|
+
context: context ?? snapshot.context,
|
|
225
|
+
event: event,
|
|
226
|
+
input: stateInput,
|
|
227
|
+
value: snapshot.value,
|
|
228
|
+
self: actorScope.self,
|
|
229
|
+
system: actorScope.system,
|
|
230
|
+
parent: actorScope.self._parent,
|
|
231
|
+
children: snapshot.children
|
|
232
|
+
}, enq);
|
|
233
|
+
if (result?.context !== undefined) {
|
|
234
|
+
const currentContext = context ?? snapshot.context;
|
|
235
|
+
const nextContext = mergeContextPatch(currentContext, result.context);
|
|
236
|
+
if (nextContext !== currentContext) {
|
|
237
|
+
context = nextContext;
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
const nextSnapshot = context !== undefined ? cloneSnapshot(snapshot, snapshot.value, context, snapshot._stateInput) : snapshot;
|
|
242
|
+
return runActions(nextSnapshot, event, actorScope, actions);
|
|
243
|
+
};
|
|
244
|
+
const selectTransition = (snapshot, event, actorScope, internalQueue) => {
|
|
245
|
+
const state = config.states[snapshot.value];
|
|
246
|
+
const transitionsConfig = state?.on?.[event.type];
|
|
247
|
+
if (!transitionsConfig) {
|
|
248
|
+
return undefined;
|
|
249
|
+
}
|
|
250
|
+
const transitionCount = Array.isArray(transitionsConfig) ? transitionsConfig.length : 1;
|
|
251
|
+
for (let i = 0; i < transitionCount; i++) {
|
|
252
|
+
const transition = Array.isArray(transitionsConfig) ? transitionsConfig[i] : transitionsConfig;
|
|
253
|
+
const args = {
|
|
254
|
+
context: snapshot.context,
|
|
255
|
+
event,
|
|
256
|
+
input: snapshot.input,
|
|
257
|
+
value: snapshot.value,
|
|
258
|
+
self: actorScope.self,
|
|
259
|
+
system: actorScope.system,
|
|
260
|
+
parent: actorScope.self._parent,
|
|
261
|
+
children: snapshot.children
|
|
262
|
+
};
|
|
263
|
+
if (typeof transition === 'function') {
|
|
264
|
+
const actions = [];
|
|
265
|
+
const enq = dist_xstateActors.createTransitionEnqueue(actorScope, actions, internalQueue, true);
|
|
266
|
+
const result = transition(args, enq);
|
|
267
|
+
if (!result) {
|
|
268
|
+
if (actions.length) {
|
|
269
|
+
return {
|
|
270
|
+
actions
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
continue;
|
|
274
|
+
}
|
|
275
|
+
return {
|
|
276
|
+
target: result.target,
|
|
277
|
+
context: result.context,
|
|
278
|
+
input: result.input,
|
|
279
|
+
actions
|
|
280
|
+
};
|
|
281
|
+
}
|
|
282
|
+
if ('guard' in transition && transition.guard && !transition.guard(args)) {
|
|
283
|
+
continue;
|
|
284
|
+
}
|
|
285
|
+
return {
|
|
286
|
+
target: transition.target,
|
|
287
|
+
context: resolveTransitionContext(transition.context, args),
|
|
288
|
+
input: transition.input,
|
|
289
|
+
actions: 'actions' in transition && transition.actions ? toArray(transition.actions) : emptyRawActions
|
|
290
|
+
};
|
|
291
|
+
}
|
|
292
|
+
return undefined;
|
|
293
|
+
};
|
|
294
|
+
const transition = (snapshot, event, actorScope) => {
|
|
295
|
+
if (snapshot.status !== 'active') {
|
|
296
|
+
return [snapshot, []];
|
|
297
|
+
}
|
|
298
|
+
if (event.type === dist_xstateActors.XSTATE_STOP) {
|
|
299
|
+
return [stopSnapshot(snapshot), emptyExecutableActions];
|
|
300
|
+
}
|
|
301
|
+
const stateConfig = config.states[snapshot.value];
|
|
302
|
+
const directTransition = stateConfig?.on?.[event.type];
|
|
303
|
+
if (!directTransition) {
|
|
304
|
+
return [snapshot, emptyExecutableActions];
|
|
305
|
+
}
|
|
306
|
+
if (!Array.isArray(directTransition) && typeof directTransition !== 'function' && !('guard' in directTransition) && !('actions' in directTransition && directTransition.actions) && typeof directTransition.input !== 'function') {
|
|
307
|
+
const target = directTransition.target ?? snapshot.value;
|
|
308
|
+
const stateChanged = target !== snapshot.value;
|
|
309
|
+
if (stateChanged && (stateConfig.exit || config.states[target]?.entry)) ; else {
|
|
310
|
+
const hasContext = directTransition.context !== undefined;
|
|
311
|
+
const hasInput = directTransition.input !== undefined;
|
|
312
|
+
const resolvedContext = resolveTransitionContext(directTransition.context, {
|
|
313
|
+
context: snapshot.context,
|
|
314
|
+
event,
|
|
315
|
+
input: snapshot.input,
|
|
316
|
+
value: snapshot.value,
|
|
317
|
+
self: actorScope.self,
|
|
318
|
+
system: actorScope.system,
|
|
319
|
+
parent: actorScope.self._parent,
|
|
320
|
+
children: snapshot.children
|
|
321
|
+
});
|
|
322
|
+
const context = hasContext && resolvedContext ? mergeContextPatch(snapshot.context, resolvedContext) : snapshot.context;
|
|
323
|
+
if (!stateChanged && context === snapshot.context && !hasInput && snapshot._stateInput === undefined) {
|
|
324
|
+
return [snapshot, emptyExecutableActions];
|
|
325
|
+
}
|
|
326
|
+
return [cloneSnapshot(snapshot, target, context, hasInput ? resolveInput(directTransition.input, context, event) : undefined), emptyExecutableActions];
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
if (typeof directTransition === 'function' && directTransition.length < 2 && !stateConfig?.exit) {
|
|
330
|
+
const result = directTransition({
|
|
331
|
+
context: snapshot.context,
|
|
332
|
+
event,
|
|
333
|
+
input: snapshot.input,
|
|
334
|
+
value: snapshot.value,
|
|
335
|
+
self: actorScope.self,
|
|
336
|
+
system: actorScope.system,
|
|
337
|
+
parent: actorScope.self._parent,
|
|
338
|
+
children: snapshot.children
|
|
339
|
+
}, undefined);
|
|
340
|
+
if (result) {
|
|
341
|
+
const target = result.target ?? snapshot.value;
|
|
342
|
+
if (!config.states[target]?.entry) {
|
|
343
|
+
const hasContext = result.context !== undefined;
|
|
344
|
+
const hasInput = result.input !== undefined;
|
|
345
|
+
const context = hasContext && result.context ? mergeContextPatch(snapshot.context, result.context) : snapshot.context;
|
|
346
|
+
if (target === snapshot.value && context === snapshot.context && !hasInput && snapshot._stateInput === undefined) {
|
|
347
|
+
return [snapshot, emptyExecutableActions];
|
|
348
|
+
}
|
|
349
|
+
return [cloneSnapshot(snapshot, target, context, hasInput ? resolveInput(result.input, context, event) : undefined), emptyExecutableActions];
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
let nextSnapshot = snapshot;
|
|
354
|
+
const executableActions = [];
|
|
355
|
+
const internalQueue = [event];
|
|
356
|
+
let iterations = 0;
|
|
357
|
+
while (internalQueue.length) {
|
|
358
|
+
if (++iterations > 1000) {
|
|
359
|
+
throw new Error('FSM microstep count exceeded 1000');
|
|
360
|
+
}
|
|
361
|
+
const nextEvent = internalQueue.shift();
|
|
362
|
+
const selected = selectTransition(nextSnapshot, nextEvent, actorScope, internalQueue);
|
|
363
|
+
if (!selected) {
|
|
364
|
+
continue;
|
|
365
|
+
}
|
|
366
|
+
const nextValue = selected.target ?? nextSnapshot.value;
|
|
367
|
+
const stateChanged = nextValue !== nextSnapshot.value;
|
|
368
|
+
if (stateChanged) {
|
|
369
|
+
const [exited, exitActions] = runStateActions(nextSnapshot, nextEvent, actorScope, config.states[nextSnapshot.value]?.exit, nextSnapshot._stateInput, internalQueue);
|
|
370
|
+
nextSnapshot = exited;
|
|
371
|
+
executableActions.push(...exitActions);
|
|
372
|
+
}
|
|
373
|
+
let context = nextSnapshot.context;
|
|
374
|
+
if (selected.context !== undefined) {
|
|
375
|
+
context = mergeContextPatch(context, selected.context);
|
|
376
|
+
}
|
|
377
|
+
const hasInput = selected.input !== undefined;
|
|
378
|
+
const stateInput = hasInput ? resolveInput(selected.input, context, nextEvent) : undefined;
|
|
379
|
+
if (stateChanged || context !== nextSnapshot.context || hasInput || nextSnapshot._stateInput !== undefined) {
|
|
380
|
+
nextSnapshot = cloneSnapshot(nextSnapshot, nextValue, context, stateInput);
|
|
381
|
+
}
|
|
382
|
+
const [afterTransition, transitionActions] = runActions(nextSnapshot, nextEvent, actorScope, selected.actions);
|
|
383
|
+
nextSnapshot = afterTransition;
|
|
384
|
+
executableActions.push(...transitionActions);
|
|
385
|
+
if (stateChanged) {
|
|
386
|
+
const [entered, entryActions] = runStateActions(nextSnapshot, nextEvent, actorScope, config.states[nextValue]?.entry, stateInput, internalQueue);
|
|
387
|
+
nextSnapshot = entered;
|
|
388
|
+
executableActions.push(...entryActions);
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
return [nextSnapshot, executableActions];
|
|
392
|
+
};
|
|
393
|
+
const logic = {
|
|
394
|
+
id: config.id,
|
|
395
|
+
config,
|
|
396
|
+
transition,
|
|
397
|
+
initialTransition: (input, actorScope) => {
|
|
398
|
+
const context = resolveContext(config.context, input);
|
|
399
|
+
const snapshot = createSnapshot(config.initial, context, input, machine);
|
|
400
|
+
const internalQueue = [];
|
|
401
|
+
let [nextSnapshot, actions] = runStateActions(snapshot, {
|
|
402
|
+
type: dist_xstateActors.XSTATE_INIT
|
|
403
|
+
}, actorScope, config.states[config.initial]?.entry, undefined, internalQueue);
|
|
404
|
+
if (!actions.length) {
|
|
405
|
+
actions = [];
|
|
406
|
+
}
|
|
407
|
+
while (internalQueue.length) {
|
|
408
|
+
const [raisedSnapshot, raisedActions] = transition(nextSnapshot, internalQueue.shift(), actorScope);
|
|
409
|
+
nextSnapshot = raisedSnapshot;
|
|
410
|
+
actions.push(...raisedActions);
|
|
411
|
+
}
|
|
412
|
+
return [nextSnapshot, actions];
|
|
413
|
+
},
|
|
414
|
+
getInitialSnapshot: (actorScope, input) => logic.initialTransition(input, actorScope)[0],
|
|
415
|
+
getPersistedSnapshot: ({
|
|
416
|
+
machine: _,
|
|
417
|
+
...snapshot
|
|
418
|
+
}) => snapshot,
|
|
419
|
+
restoreSnapshot: snapshot => ({
|
|
420
|
+
...snapshot,
|
|
421
|
+
machine: machine
|
|
422
|
+
})
|
|
423
|
+
};
|
|
424
|
+
return logic;
|
|
425
|
+
}
|
|
426
|
+
|
|
94
427
|
function delayToMs(delay) {
|
|
95
428
|
const parsedDelay = dist_xstateActors.parseDelayToMilliseconds(delay);
|
|
96
429
|
if (parsedDelay !== undefined) return parsedDelay;
|
|
@@ -103,48 +436,23 @@ function delayToMs(delay) {
|
|
|
103
436
|
* separate <onentry>/<onexit> blocks: each is its own block per spec.
|
|
104
437
|
*/
|
|
105
438
|
|
|
106
|
-
function
|
|
107
|
-
return !!value && typeof value === 'object' &&
|
|
439
|
+
function isExpression(value) {
|
|
440
|
+
return !!value && typeof value === 'object' && typeof value['@expr'] === 'string';
|
|
108
441
|
}
|
|
109
|
-
function
|
|
110
|
-
|
|
111
|
-
if (value.$unserializable === 'actor' && path.endsWith('.src')) {
|
|
112
|
-
return;
|
|
113
|
-
}
|
|
114
|
-
throw new Error(`Unresolved ${value.$unserializable} at ${path}`);
|
|
115
|
-
}
|
|
116
|
-
if (!value || typeof value !== 'object') {
|
|
117
|
-
return;
|
|
118
|
-
}
|
|
119
|
-
if (Array.isArray(value)) {
|
|
120
|
-
value.forEach((item, index) => assertNoUnresolvedMarkers(item, `${path}[${index}]`));
|
|
121
|
-
return;
|
|
122
|
-
}
|
|
123
|
-
for (const key of Object.keys(value)) {
|
|
124
|
-
const child = value[key];
|
|
125
|
-
const isImplementationMarker = path === '$' && (key === 'actions' || key === 'guards' || key === 'actorSources' || key === 'delays' || key === 'schemas');
|
|
126
|
-
if (isImplementationMarker) {
|
|
127
|
-
continue;
|
|
128
|
-
}
|
|
129
|
-
assertNoUnresolvedMarkers(child, `${path}.${key}`);
|
|
130
|
-
}
|
|
442
|
+
function isCode(value) {
|
|
443
|
+
return !!value && typeof value === 'object' && typeof value['@code'] === 'string';
|
|
131
444
|
}
|
|
132
|
-
function
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
if (!implementations[kind]?.[key]) {
|
|
144
|
-
throw new Error(`Missing ${kind}.${key}`);
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
}
|
|
445
|
+
function isResolvable(value) {
|
|
446
|
+
return isExpression(value) || isCode(value);
|
|
447
|
+
}
|
|
448
|
+
function isBuiltInActionType(type) {
|
|
449
|
+
return type.startsWith('@xstate.');
|
|
450
|
+
}
|
|
451
|
+
function isScxmlActionType(type) {
|
|
452
|
+
return type.startsWith('scxml.');
|
|
453
|
+
}
|
|
454
|
+
function toPath(parent, key) {
|
|
455
|
+
return typeof key === 'number' ? `${parent}[${key}]` : `${parent}.${key}`;
|
|
148
456
|
}
|
|
149
457
|
function extractSerializableDelays(delays) {
|
|
150
458
|
const result = {};
|
|
@@ -155,6 +463,8 @@ function extractSerializableDelays(delays) {
|
|
|
155
463
|
const value = delays[key];
|
|
156
464
|
if (typeof value === 'number') {
|
|
157
465
|
result[key] = value;
|
|
466
|
+
} else if (value && typeof value === 'object' && typeof value.duration === 'number') {
|
|
467
|
+
result[key] = value.duration;
|
|
158
468
|
}
|
|
159
469
|
}
|
|
160
470
|
return result;
|
|
@@ -167,9 +477,248 @@ function mergeImplementations(json, implementations) {
|
|
|
167
477
|
delays: {
|
|
168
478
|
...extractSerializableDelays(json.delays),
|
|
169
479
|
...(implementations.delays ?? {})
|
|
480
|
+
},
|
|
481
|
+
evaluators: implementations.evaluators ?? {}
|
|
482
|
+
};
|
|
483
|
+
}
|
|
484
|
+
function createExpressionResolver(expressionLanguage, implementations) {
|
|
485
|
+
function getEvaluator(value, path) {
|
|
486
|
+
const lang = value['@lang'] ?? expressionLanguage;
|
|
487
|
+
if (!lang) {
|
|
488
|
+
throw new Error(`Missing @exprLang for expression at ${path}`);
|
|
489
|
+
}
|
|
490
|
+
const evaluator = implementations.evaluators[lang];
|
|
491
|
+
if (!evaluator) {
|
|
492
|
+
throw new Error(`Missing evaluator for @lang '${lang}' at ${path}`);
|
|
493
|
+
}
|
|
494
|
+
return evaluator;
|
|
495
|
+
}
|
|
496
|
+
function evaluateResolvable(value, slot, scope, path) {
|
|
497
|
+
const kind = isExpression(value) ? 'expr' : 'code';
|
|
498
|
+
const source = isExpression(value) ? value['@expr'] : value['@code'];
|
|
499
|
+
return getEvaluator(value, path)({
|
|
500
|
+
source,
|
|
501
|
+
kind,
|
|
502
|
+
slot,
|
|
503
|
+
scope,
|
|
504
|
+
path
|
|
505
|
+
});
|
|
506
|
+
}
|
|
507
|
+
function resolveValue(value, slot, scope, path) {
|
|
508
|
+
if (isResolvable(value)) {
|
|
509
|
+
return evaluateResolvable(value, slot, scope, path);
|
|
510
|
+
}
|
|
511
|
+
if (Array.isArray(value)) {
|
|
512
|
+
return value.map((item, index) => resolveValue(item, slot, scope, toPath(path, index)));
|
|
513
|
+
}
|
|
514
|
+
if (!value || typeof value !== 'object') {
|
|
515
|
+
return value;
|
|
516
|
+
}
|
|
517
|
+
const result = {};
|
|
518
|
+
for (const key of Object.keys(value)) {
|
|
519
|
+
result[key] = resolveValue(value[key], slot, scope, toPath(path, key));
|
|
520
|
+
}
|
|
521
|
+
return result;
|
|
522
|
+
}
|
|
523
|
+
function makeScope(x, extra) {
|
|
524
|
+
return {
|
|
525
|
+
context: x.context,
|
|
526
|
+
event: x.event,
|
|
527
|
+
input: x.input,
|
|
528
|
+
self: x.self,
|
|
529
|
+
children: x.children,
|
|
530
|
+
params: x.params,
|
|
531
|
+
...extra
|
|
532
|
+
};
|
|
533
|
+
}
|
|
534
|
+
function getDurationConfig(value, path) {
|
|
535
|
+
if (!isResolvable(value)) {
|
|
536
|
+
return value;
|
|
537
|
+
}
|
|
538
|
+
return args => delayToMs(resolveValue(value, 'delay', makeScope(args), path));
|
|
539
|
+
}
|
|
540
|
+
function assertResolvable(value, path) {
|
|
541
|
+
if (isResolvable(value)) {
|
|
542
|
+
getEvaluator(value, path);
|
|
543
|
+
return;
|
|
544
|
+
}
|
|
545
|
+
if (Array.isArray(value)) {
|
|
546
|
+
value.forEach((item, index) => assertResolvable(item, toPath(path, index)));
|
|
547
|
+
return;
|
|
548
|
+
}
|
|
549
|
+
if (!value || typeof value !== 'object') {
|
|
550
|
+
return;
|
|
551
|
+
}
|
|
552
|
+
for (const key of Object.keys(value)) {
|
|
553
|
+
assertResolvable(value[key], toPath(path, key));
|
|
170
554
|
}
|
|
555
|
+
}
|
|
556
|
+
return {
|
|
557
|
+
evaluateResolvable,
|
|
558
|
+
resolveValue,
|
|
559
|
+
makeScope,
|
|
560
|
+
getDurationConfig,
|
|
561
|
+
assertResolvable
|
|
171
562
|
};
|
|
172
563
|
}
|
|
564
|
+
function toActionArray(actions) {
|
|
565
|
+
return actions === undefined ? [] : Array.isArray(actions) ? actions : [actions];
|
|
566
|
+
}
|
|
567
|
+
function validateChoiceConfig(choice, path) {
|
|
568
|
+
if (!Array.isArray(choice)) {
|
|
569
|
+
return;
|
|
570
|
+
}
|
|
571
|
+
choice.forEach((branch, index) => {
|
|
572
|
+
if (branch.when === undefined && index !== choice.length - 1) {
|
|
573
|
+
throw new Error(`Choice fallback branch at ${path}[${index}] must be last.`);
|
|
574
|
+
}
|
|
575
|
+
});
|
|
576
|
+
}
|
|
577
|
+
function assertMachineJSON(json, resolvedImplementations, expressionResolver) {
|
|
578
|
+
const {
|
|
579
|
+
assertResolvable
|
|
580
|
+
} = expressionResolver;
|
|
581
|
+
function assertCondition(condition, path) {
|
|
582
|
+
if (!condition) {
|
|
583
|
+
return;
|
|
584
|
+
}
|
|
585
|
+
if (isResolvable(condition)) {
|
|
586
|
+
assertResolvable(condition, path);
|
|
587
|
+
return;
|
|
588
|
+
}
|
|
589
|
+
assertResolvable(condition.params, `${path}.params`);
|
|
590
|
+
if (json.guards?.[condition.type]) {
|
|
591
|
+
assertCondition(json.guards[condition.type].when, `$.guards.${condition.type}.when`);
|
|
592
|
+
return;
|
|
593
|
+
}
|
|
594
|
+
if (!resolvedImplementations.guards[condition.type] && !['scxml.cond', 'xstate.stateIn', 'xstate.not'].includes(condition.type)) {
|
|
595
|
+
throw new Error(`Missing guard implementation "${condition.type}"`);
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
function assertAction(action, path, stack = []) {
|
|
599
|
+
if (isResolvable(action)) {
|
|
600
|
+
assertResolvable(action, path);
|
|
601
|
+
return;
|
|
602
|
+
}
|
|
603
|
+
if (!action || typeof action.type !== 'string') {
|
|
604
|
+
throw new Error(`Invalid action at ${path}`);
|
|
605
|
+
}
|
|
606
|
+
assertResolvable(action.params, `${path}.params`);
|
|
607
|
+
if (isBuiltInActionType(action.type) || isScxmlActionType(action.type)) {
|
|
608
|
+
assertResolvable(action, path);
|
|
609
|
+
return;
|
|
610
|
+
}
|
|
611
|
+
const definition = json.actions?.[action.type];
|
|
612
|
+
if (definition) {
|
|
613
|
+
if (stack.includes(action.type)) {
|
|
614
|
+
throw new Error(`Circular action reference: ${stack.concat(action.type).join(' -> ')}`);
|
|
615
|
+
}
|
|
616
|
+
const definitions = Array.isArray(definition) ? definition : [definition];
|
|
617
|
+
definitions.forEach((item, index) => assertAction(item, `${path}.actions.${action.type}${definitions.length > 1 ? `[${index}]` : ''}`, stack.concat(action.type)));
|
|
618
|
+
return;
|
|
619
|
+
}
|
|
620
|
+
if (!resolvedImplementations.actions[action.type]) {
|
|
621
|
+
throw new Error(`Missing action implementation "${action.type}"`);
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
function assertActions(actions, path) {
|
|
625
|
+
toActionArray(actions).forEach((action, index) => assertAction(action, `${path}[${index}]`));
|
|
626
|
+
}
|
|
627
|
+
function assertTransition(transition, path) {
|
|
628
|
+
const transitions = Array.isArray(transition) ? transition : transition ? [transition] : [];
|
|
629
|
+
transitions.forEach((t, index) => {
|
|
630
|
+
const transitionPath = Array.isArray(transition) ? `${path}[${index}]` : path;
|
|
631
|
+
if (isResolvable(t)) {
|
|
632
|
+
assertResolvable(t, transitionPath);
|
|
633
|
+
return;
|
|
634
|
+
}
|
|
635
|
+
assertCondition(t.guard, `${transitionPath}.guard`);
|
|
636
|
+
assertActions(t.actions, `${transitionPath}.actions`);
|
|
637
|
+
assertResolvable(t.context, `${transitionPath}.context`);
|
|
638
|
+
assertResolvable(t.input, `${transitionPath}.input`);
|
|
639
|
+
});
|
|
640
|
+
}
|
|
641
|
+
function assertStateNode(node, path) {
|
|
642
|
+
assertResolvable(node.context, `${path}.context`);
|
|
643
|
+
assertResolvable(node.input, `${path}.input`);
|
|
644
|
+
assertResolvable(node.output, `${path}.output`);
|
|
645
|
+
assertResolvable(node.timeout, `${path}.timeout`);
|
|
646
|
+
assertActions(node.entry, `${path}.entry`);
|
|
647
|
+
assertActions(node.exit, `${path}.exit`);
|
|
648
|
+
if (node.type === 'choice') {
|
|
649
|
+
if (!node.choice) {
|
|
650
|
+
throw new Error(`Choice state at ${path} must declare choice.`);
|
|
651
|
+
}
|
|
652
|
+
if (Array.isArray(node.choice)) {
|
|
653
|
+
validateChoiceConfig(node.choice, `${path}.choice`);
|
|
654
|
+
node.choice.forEach((branch, index) => {
|
|
655
|
+
assertCondition(branch.when, `${path}.choice[${index}].when`);
|
|
656
|
+
assertResolvable(branch.context, `${path}.choice[${index}].context`);
|
|
657
|
+
assertResolvable(branch.input, `${path}.choice[${index}].input`);
|
|
658
|
+
});
|
|
659
|
+
} else {
|
|
660
|
+
assertResolvable(node.choice, `${path}.choice`);
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
if (isResolvable(node.route)) {
|
|
664
|
+
assertResolvable(node.route, `${path}.route`);
|
|
665
|
+
}
|
|
666
|
+
if (node.invoke) {
|
|
667
|
+
const invokes = Array.isArray(node.invoke) ? node.invoke : [node.invoke];
|
|
668
|
+
invokes.forEach((invoke, index) => {
|
|
669
|
+
const invokePath = `${path}.invoke${Array.isArray(node.invoke) ? `[${index}]` : ''}`;
|
|
670
|
+
const extInv = invoke;
|
|
671
|
+
if (!extInv._nestedMachineJSON && !resolvedImplementations.actorSources[invoke.src]) {
|
|
672
|
+
throw new Error(`Missing actorSource implementation "${invoke.src}"`);
|
|
673
|
+
}
|
|
674
|
+
assertResolvable(invoke.input, `${invokePath}.input`);
|
|
675
|
+
assertResolvable(invoke.timeout, `${invokePath}.timeout`);
|
|
676
|
+
assertTransition(invoke.onDone, `${invokePath}.onDone`);
|
|
677
|
+
assertTransition(invoke.onError, `${invokePath}.onError`);
|
|
678
|
+
assertTransition(invoke.onSnapshot, `${invokePath}.onSnapshot`);
|
|
679
|
+
assertTransition(invoke.onTimeout, `${invokePath}.onTimeout`);
|
|
680
|
+
});
|
|
681
|
+
}
|
|
682
|
+
if (node.on) {
|
|
683
|
+
for (const descriptor of Object.keys(node.on)) {
|
|
684
|
+
assertTransition(node.on[descriptor], `${path}.on.${descriptor}`);
|
|
685
|
+
}
|
|
686
|
+
}
|
|
687
|
+
if (node.after) {
|
|
688
|
+
for (const delay of Object.keys(node.after)) {
|
|
689
|
+
if (Number.isNaN(Number(delay)) && dist_xstateActors.parseDelayToMilliseconds(delay) === undefined && !json.delays?.[delay] && !resolvedImplementations.delays[delay]) {
|
|
690
|
+
throw new Error(`Missing delay implementation "${delay}"`);
|
|
691
|
+
}
|
|
692
|
+
assertTransition(node.after[delay], `${path}.after.${delay}`);
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
assertTransition(node.always, `${path}.always`);
|
|
696
|
+
assertTransition(node.onTimeout, `${path}.onTimeout`);
|
|
697
|
+
if (node.states) {
|
|
698
|
+
for (const key of Object.keys(node.states)) {
|
|
699
|
+
assertStateNode(node.states[key], `${path}.states.${key}`);
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
}
|
|
703
|
+
if (json.actions) {
|
|
704
|
+
for (const key of Object.keys(json.actions)) {
|
|
705
|
+
const actions = Array.isArray(json.actions[key]) ? json.actions[key] : [json.actions[key]];
|
|
706
|
+
actions.forEach((action, index) => assertAction(action, `$.actions.${key}${actions.length > 1 ? `[${index}]` : ''}`, [key]));
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
if (json.guards) {
|
|
710
|
+
for (const key of Object.keys(json.guards)) {
|
|
711
|
+
assertCondition(json.guards[key].when, `$.guards.${key}.when`);
|
|
712
|
+
}
|
|
713
|
+
}
|
|
714
|
+
if (json.delays) {
|
|
715
|
+
for (const key of Object.keys(json.delays)) {
|
|
716
|
+
const delay = json.delays[key];
|
|
717
|
+
assertResolvable(delay && typeof delay === 'object' && 'duration' in delay ? delay.duration : delay, `$.delays.${key}`);
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
assertStateNode(json, '$');
|
|
721
|
+
}
|
|
173
722
|
let _scxmlSessionId = 'session_' + Math.random().toString(36).slice(2);
|
|
174
723
|
let _scxmlMachineName = '';
|
|
175
724
|
|
|
@@ -243,9 +792,64 @@ return updates;
|
|
|
243
792
|
return fn(context, updates);
|
|
244
793
|
}
|
|
245
794
|
function createMachineFromConfig(json, implementations = {}) {
|
|
246
|
-
assertRootImplementationMarkers(json, implementations);
|
|
247
|
-
assertNoUnresolvedMarkers(json);
|
|
248
795
|
const resolvedImplementations = mergeImplementations(json, implementations);
|
|
796
|
+
const expressionResolver = createExpressionResolver(json['@exprLang'], resolvedImplementations);
|
|
797
|
+
const {
|
|
798
|
+
evaluateResolvable,
|
|
799
|
+
resolveValue,
|
|
800
|
+
makeScope,
|
|
801
|
+
getDurationConfig
|
|
802
|
+
} = expressionResolver;
|
|
803
|
+
function resolveCondition(condition, slot, path) {
|
|
804
|
+
if (!condition) {
|
|
805
|
+
return undefined;
|
|
806
|
+
}
|
|
807
|
+
if (isResolvable(condition)) {
|
|
808
|
+
return args => !!evaluateResolvable(condition, slot, makeScope(args), path);
|
|
809
|
+
}
|
|
810
|
+
return args => {
|
|
811
|
+
const params = resolveValue(condition.params, slot, makeScope(args), `${path}.params`);
|
|
812
|
+
const declarativeGuard = json.guards?.[condition.type];
|
|
813
|
+
if (declarativeGuard) {
|
|
814
|
+
const guard = resolveCondition(declarativeGuard.when, 'guard', `$.guards.${condition.type}.when`);
|
|
815
|
+
return !!guard?.({
|
|
816
|
+
...args,
|
|
817
|
+
params
|
|
818
|
+
});
|
|
819
|
+
}
|
|
820
|
+
const guardImpl = args.guards?.[condition.type];
|
|
821
|
+
if (!guardImpl) {
|
|
822
|
+
throw new Error(getMissingGuardMessage(condition.type, args.guards ?? {}));
|
|
823
|
+
}
|
|
824
|
+
return guardImpl(args, params);
|
|
825
|
+
};
|
|
826
|
+
}
|
|
827
|
+
function makeChoiceConfig(choice, path) {
|
|
828
|
+
if (!choice) {
|
|
829
|
+
return undefined;
|
|
830
|
+
}
|
|
831
|
+
if (isResolvable(choice)) {
|
|
832
|
+
return args => evaluateResolvable(choice, 'choice', makeScope(args), path);
|
|
833
|
+
}
|
|
834
|
+
validateChoiceConfig(choice, path);
|
|
835
|
+
return args => {
|
|
836
|
+
for (let index = 0; index < choice.length; index++) {
|
|
837
|
+
const branch = choice[index];
|
|
838
|
+
const guard = resolveCondition(branch.when, 'choice', `${path}[${index}].when`);
|
|
839
|
+
if (!guard || guard(args)) {
|
|
840
|
+
return {
|
|
841
|
+
target: branch.target,
|
|
842
|
+
context: branch.context ? resolveValue(branch.context, 'transitionContext', makeScope(args), `${path}[${index}].context`) : undefined,
|
|
843
|
+
input: branch.input !== undefined ? resolveValue(branch.input, 'input', makeScope(args), `${path}[${index}].input`) : undefined,
|
|
844
|
+
description: branch.description,
|
|
845
|
+
reenter: branch.reenter,
|
|
846
|
+
meta: branch.meta
|
|
847
|
+
};
|
|
848
|
+
}
|
|
849
|
+
}
|
|
850
|
+
throw new Error(`Choice state at ${path} did not match any branch.`);
|
|
851
|
+
};
|
|
852
|
+
}
|
|
249
853
|
|
|
250
854
|
// Pending transition actions: set by .to functions, consumed by entry functions.
|
|
251
855
|
// This bridges SCXML's exit→transition→entry action ordering with XState's
|
|
@@ -268,22 +872,13 @@ function createMachineFromConfig(json, implementations = {}) {
|
|
|
268
872
|
function getMissingGuardMessage(type, guards) {
|
|
269
873
|
return `Guard '${type}' is not implemented in machine '${json.id ?? '(machine)'}'. Available guards: ${Object.keys(guards).map(key => `'${key}'`).join(', ') || '(none)'}.`;
|
|
270
874
|
}
|
|
271
|
-
function
|
|
272
|
-
if (!guard) {
|
|
273
|
-
return undefined;
|
|
274
|
-
}
|
|
275
|
-
return args => {
|
|
276
|
-
const guardImpl = args.guards?.[guard.type];
|
|
277
|
-
if (!guardImpl) {
|
|
278
|
-
throw new Error(getMissingGuardMessage(guard.type, args.guards ?? {}));
|
|
279
|
-
}
|
|
280
|
-
return guardImpl(args, guard.params);
|
|
281
|
-
};
|
|
282
|
-
}
|
|
283
|
-
function resolveRouteConfig(route) {
|
|
875
|
+
function resolveRouteConfig(route, path) {
|
|
284
876
|
if (!route || typeof route === 'function') {
|
|
285
877
|
return route;
|
|
286
878
|
}
|
|
879
|
+
if (isResolvable(route)) {
|
|
880
|
+
return args => evaluateResolvable(route, 'transition', makeScope(args), path);
|
|
881
|
+
}
|
|
287
882
|
const {
|
|
288
883
|
guard,
|
|
289
884
|
...routeConfig
|
|
@@ -291,13 +886,13 @@ function createMachineFromConfig(json, implementations = {}) {
|
|
|
291
886
|
if (!guard) {
|
|
292
887
|
return routeConfig;
|
|
293
888
|
}
|
|
294
|
-
const resolvedGuard =
|
|
889
|
+
const resolvedGuard = resolveCondition({
|
|
295
890
|
type: guard
|
|
296
|
-
});
|
|
891
|
+
}, 'guard', path);
|
|
297
892
|
return args => resolvedGuard(args) ? routeConfig : undefined;
|
|
298
893
|
}
|
|
299
894
|
function iterNode(node, nodeKey) {
|
|
300
|
-
const originalEntryActions = node.entry;
|
|
895
|
+
const originalEntryActions = toActionArray(node.entry);
|
|
301
896
|
const stateId = node.id || nodeKey;
|
|
302
897
|
|
|
303
898
|
// Wrap entry to first execute any pending transition actions, then normal entry.
|
|
@@ -360,7 +955,7 @@ function createMachineFromConfig(json, implementations = {}) {
|
|
|
360
955
|
}
|
|
361
956
|
|
|
362
957
|
// Execute normal entry actions
|
|
363
|
-
if (originalEntryActions
|
|
958
|
+
if (originalEntryActions.length) {
|
|
364
959
|
const mergedX = context ? {
|
|
365
960
|
...x,
|
|
366
961
|
context
|
|
@@ -383,7 +978,7 @@ function createMachineFromConfig(json, implementations = {}) {
|
|
|
383
978
|
description: node.description,
|
|
384
979
|
tags: node.tags,
|
|
385
980
|
input: node.input,
|
|
386
|
-
timeout: node.timeout,
|
|
981
|
+
timeout: getDurationConfig(node.timeout, `$.states.${nodeKey}.timeout`),
|
|
387
982
|
states: node.states ? Object.entries(node.states).reduce((acc, [key, value]) => {
|
|
388
983
|
acc[key] = iterNode(value, key);
|
|
389
984
|
return acc;
|
|
@@ -393,18 +988,26 @@ function createMachineFromConfig(json, implementations = {}) {
|
|
|
393
988
|
return acc;
|
|
394
989
|
}, {}) : undefined,
|
|
395
990
|
always: node.always ? getTransitionConfig(node.always) : undefined,
|
|
396
|
-
choice: node.choice
|
|
397
|
-
route: resolveRouteConfig(node.route),
|
|
991
|
+
choice: makeChoiceConfig(node.choice, `$.states.${nodeKey}.choice`),
|
|
992
|
+
route: resolveRouteConfig(node.route, `$.states.${nodeKey}.route`),
|
|
398
993
|
after: node.after ? Object.entries(node.after).reduce((acc, [key, value]) => {
|
|
399
994
|
acc[key] = getTransitionConfig(value);
|
|
400
995
|
return acc;
|
|
401
996
|
}, {}) : undefined,
|
|
402
997
|
onTimeout: node.onTimeout ? getTransitionConfig(node.onTimeout) : undefined,
|
|
403
|
-
entry: entryFn,
|
|
404
|
-
exit: node.exit ? iterActions(node.exit) : undefined,
|
|
998
|
+
entry: node.type === 'choice' ? undefined : entryFn,
|
|
999
|
+
exit: node.exit ? iterActions(toActionArray(node.exit)) : undefined,
|
|
405
1000
|
invoke: node.invoke ? iterInvokeConfigs(node.invoke) : undefined,
|
|
406
1001
|
meta: node.meta,
|
|
407
|
-
output: node._scxmlDonedata ? makeDonedataOutput(node._scxmlDonedata) : node.output
|
|
1002
|
+
output: node._scxmlDonedata ? makeDonedataOutput(node._scxmlDonedata) : isResolvable(node.output) ? ({
|
|
1003
|
+
context,
|
|
1004
|
+
event,
|
|
1005
|
+
self
|
|
1006
|
+
}) => evaluateResolvable(node.output, 'output', {
|
|
1007
|
+
context,
|
|
1008
|
+
event,
|
|
1009
|
+
self
|
|
1010
|
+
}, `$.states.${nodeKey}.output`) : node.output
|
|
408
1011
|
};
|
|
409
1012
|
return nodeConfig;
|
|
410
1013
|
}
|
|
@@ -457,23 +1060,18 @@ function createMachineFromConfig(json, implementations = {}) {
|
|
|
457
1060
|
let src;
|
|
458
1061
|
if (extInv._nestedMachineJSON) {
|
|
459
1062
|
src = createMachineFromConfig(extInv._nestedMachineJSON, resolvedImplementations);
|
|
460
|
-
} else if (isUnserializableMarker(inv.src)) {
|
|
461
|
-
src = inv.src.id ? resolvedImplementations.actorSources[inv.src.id] : undefined;
|
|
462
|
-
if (!src) {
|
|
463
|
-
throw new Error(`Missing actorSources.${inv.src.id ?? ''}`);
|
|
464
|
-
}
|
|
465
1063
|
} else {
|
|
466
|
-
src = inv.src;
|
|
1064
|
+
src = resolvedImplementations.actorSources[inv.src] ?? inv.src;
|
|
467
1065
|
}
|
|
468
1066
|
return {
|
|
469
1067
|
src,
|
|
470
1068
|
id: inv.id,
|
|
471
1069
|
registryKey: inv.registryKey,
|
|
472
|
-
input: inv.input,
|
|
1070
|
+
input: inv.input !== undefined ? args => resolveValue(inv.input, 'input', makeScope(args), '$.invoke.input') : undefined,
|
|
473
1071
|
onDone: inv.onDone ? getTransitionConfig(inv.onDone) : undefined,
|
|
474
1072
|
onError: inv.onError ? getTransitionConfig(inv.onError) : undefined,
|
|
475
1073
|
onSnapshot: inv.onSnapshot ? getTransitionConfig(inv.onSnapshot) : undefined,
|
|
476
|
-
timeout: inv.timeout,
|
|
1074
|
+
timeout: getDurationConfig(inv.timeout, '$.invoke.timeout'),
|
|
477
1075
|
onTimeout: inv.onTimeout ? getTransitionConfig(inv.onTimeout) : undefined
|
|
478
1076
|
};
|
|
479
1077
|
});
|
|
@@ -499,8 +1097,31 @@ function createMachineFromConfig(json, implementations = {}) {
|
|
|
499
1097
|
});
|
|
500
1098
|
state.errored = true;
|
|
501
1099
|
}
|
|
1100
|
+
function executeActionDefinition(action, x, enq, params, parentState, stack) {
|
|
1101
|
+
const definition = json.actions?.[action.type];
|
|
1102
|
+
if (!definition) {
|
|
1103
|
+
enq(x.actions[action.type], params);
|
|
1104
|
+
return {
|
|
1105
|
+
context: undefined,
|
|
1106
|
+
errored: parentState.errored
|
|
1107
|
+
};
|
|
1108
|
+
}
|
|
1109
|
+
if (stack.includes(action.type)) {
|
|
1110
|
+
raiseErrorExecution(parentState, 'action', new Error(`Circular action reference: ${stack.concat(action.type).join(' -> ')}`));
|
|
1111
|
+
return {
|
|
1112
|
+
context: undefined,
|
|
1113
|
+
errored: true
|
|
1114
|
+
};
|
|
1115
|
+
}
|
|
1116
|
+
const definitions = Array.isArray(definition) ? definition : [definition];
|
|
1117
|
+
return executeActions(definitions, {
|
|
1118
|
+
...x,
|
|
1119
|
+
params
|
|
1120
|
+
}, enq, parentState, stack.concat(action.type));
|
|
1121
|
+
}
|
|
1122
|
+
|
|
502
1123
|
/** Execute an array of SCXML action JSON descriptors with context and enqueue. */
|
|
503
|
-
function executeActions(actions, x, enq, parentState) {
|
|
1124
|
+
function executeActions(actions, x, enq, parentState, stack = []) {
|
|
504
1125
|
const state = parentState ?? {
|
|
505
1126
|
enq,
|
|
506
1127
|
errored: false
|
|
@@ -508,22 +1129,34 @@ function createMachineFromConfig(json, implementations = {}) {
|
|
|
508
1129
|
let context;
|
|
509
1130
|
for (const action of actions) {
|
|
510
1131
|
if (state.errored) break;
|
|
511
|
-
if (
|
|
1132
|
+
if (isResolvable(action)) {
|
|
1133
|
+
const result = evaluateResolvable(action, 'action', makeScope(x, {
|
|
1134
|
+
params: x.params,
|
|
1135
|
+
enq
|
|
1136
|
+
}), '$.actions');
|
|
1137
|
+
if (result && typeof result === 'object' && 'context' in result && result.context) {
|
|
1138
|
+
context ??= {};
|
|
1139
|
+
Object.assign(context, result.context);
|
|
1140
|
+
}
|
|
1141
|
+
} else if (isBuiltInActionJSON(action)) {
|
|
512
1142
|
switch (action.type) {
|
|
513
1143
|
case '@xstate.raise':
|
|
514
1144
|
{
|
|
515
1145
|
// Tag as external if it has a delay (from <send>, not <raise>).
|
|
516
1146
|
// Attach _scxmlSendId so _event.sendid can be read in expressions.
|
|
517
1147
|
const isExternal = action.delay !== undefined;
|
|
1148
|
+
const resolvedEvent = resolveValue(action.event, 'actionParams', makeScope(x, {
|
|
1149
|
+
params: x.params
|
|
1150
|
+
}), '$.actions.event');
|
|
518
1151
|
const event = isExternal || action.id !== undefined ? {
|
|
519
|
-
...
|
|
1152
|
+
...resolvedEvent,
|
|
520
1153
|
...(isExternal ? {
|
|
521
1154
|
_scxmlExternal: true
|
|
522
1155
|
} : {}),
|
|
523
1156
|
...(action.id !== undefined ? {
|
|
524
1157
|
_scxmlSendId: action.id
|
|
525
1158
|
} : {})
|
|
526
|
-
} :
|
|
1159
|
+
} : resolvedEvent;
|
|
527
1160
|
enq.raise(event, {
|
|
528
1161
|
id: action.id,
|
|
529
1162
|
delay: action.delay
|
|
@@ -537,11 +1170,15 @@ function createMachineFromConfig(json, implementations = {}) {
|
|
|
537
1170
|
enq.log(...action.args);
|
|
538
1171
|
break;
|
|
539
1172
|
case '@xstate.emit':
|
|
540
|
-
enq.emit(action.event
|
|
1173
|
+
enq.emit(resolveValue(action.event, 'actionParams', makeScope(x, {
|
|
1174
|
+
params: x.params
|
|
1175
|
+
}), '$.actions.event'));
|
|
541
1176
|
break;
|
|
542
1177
|
case '@xstate.assign':
|
|
543
1178
|
context ??= {};
|
|
544
|
-
Object.assign(context, action.context
|
|
1179
|
+
Object.assign(context, resolveValue(action.context, 'transitionContext', makeScope(x, {
|
|
1180
|
+
params: x.params
|
|
1181
|
+
}), '$.actions.context'));
|
|
545
1182
|
break;
|
|
546
1183
|
default:
|
|
547
1184
|
throw new Error(`Unknown built-in action: ${action.type}`);
|
|
@@ -769,7 +1406,14 @@ function createMachineFromConfig(json, implementations = {}) {
|
|
|
769
1406
|
}
|
|
770
1407
|
}
|
|
771
1408
|
} else {
|
|
772
|
-
|
|
1409
|
+
const params = resolveValue(action.params, 'actionParams', makeScope(x, {
|
|
1410
|
+
params: x.params
|
|
1411
|
+
}), '$.actions.params');
|
|
1412
|
+
const result = executeActionDefinition(action, x, enq, params, state, stack);
|
|
1413
|
+
if (result.context) {
|
|
1414
|
+
context ??= {};
|
|
1415
|
+
Object.assign(context, result.context);
|
|
1416
|
+
}
|
|
773
1417
|
}
|
|
774
1418
|
}
|
|
775
1419
|
return {
|
|
@@ -790,31 +1434,63 @@ function createMachineFromConfig(json, implementations = {}) {
|
|
|
790
1434
|
// a separate XState transition with its own guard and optional .to.
|
|
791
1435
|
// This ensures guards are evaluated by XState's evaluateCandidate (once,
|
|
792
1436
|
// with pre-exit context) and NOT re-evaluated in computeEntrySet.
|
|
793
|
-
return transitions.map(t => {
|
|
1437
|
+
return transitions.map((t, index) => {
|
|
1438
|
+
if (isResolvable(t)) {
|
|
1439
|
+
return (x, enq) => evaluateResolvable(t, 'transition', makeScope(x, {
|
|
1440
|
+
enq
|
|
1441
|
+
}), `$.transition${transitions.length > 1 ? `[${index}]` : ''}`);
|
|
1442
|
+
}
|
|
794
1443
|
const target = Array.isArray(t.target) ? t.target[0] : t.target;
|
|
795
1444
|
const targetConfig = t.target;
|
|
796
|
-
const guard =
|
|
1445
|
+
const guard = resolveCondition(t.guard, 'guard', '$.transition.guard');
|
|
1446
|
+
const resolveTransitionContext = x => t.context ? resolveValue(t.context, 'transitionContext', makeScope(x), '$.transition.context') : undefined;
|
|
1447
|
+
const resolveTransitionInput = x => t.input !== undefined ? resolveValue(t.input, 'input', makeScope(x), '$.transition.input') : undefined;
|
|
797
1448
|
|
|
798
1449
|
// No guard and no actions: simple static config
|
|
799
1450
|
if (!guard && !t.actions?.length) {
|
|
1451
|
+
if (t.context) {
|
|
1452
|
+
return {
|
|
1453
|
+
to: x => ({
|
|
1454
|
+
target: targetConfig,
|
|
1455
|
+
context: resolveTransitionContext(x),
|
|
1456
|
+
reenter: t.reenter
|
|
1457
|
+
}),
|
|
1458
|
+
description: t.description,
|
|
1459
|
+
meta: t.meta,
|
|
1460
|
+
input: t.input !== undefined ? resolveTransitionInput : undefined
|
|
1461
|
+
};
|
|
1462
|
+
}
|
|
800
1463
|
return {
|
|
801
1464
|
target: targetConfig,
|
|
802
1465
|
description: t.description,
|
|
803
1466
|
reenter: t.reenter,
|
|
804
1467
|
meta: t.meta,
|
|
805
|
-
input: t.input
|
|
1468
|
+
input: t.input !== undefined ? resolveTransitionInput : undefined
|
|
806
1469
|
};
|
|
807
1470
|
}
|
|
808
1471
|
|
|
809
1472
|
// Guard but no actions: static config with guard
|
|
810
1473
|
if (guard && !t.actions?.length) {
|
|
1474
|
+
if (t.context) {
|
|
1475
|
+
return {
|
|
1476
|
+
guard,
|
|
1477
|
+
to: x => ({
|
|
1478
|
+
target: targetConfig,
|
|
1479
|
+
context: resolveTransitionContext(x),
|
|
1480
|
+
reenter: t.reenter
|
|
1481
|
+
}),
|
|
1482
|
+
description: t.description,
|
|
1483
|
+
meta: t.meta,
|
|
1484
|
+
input: t.input !== undefined ? resolveTransitionInput : undefined
|
|
1485
|
+
};
|
|
1486
|
+
}
|
|
811
1487
|
return {
|
|
812
1488
|
target: targetConfig,
|
|
813
1489
|
guard,
|
|
814
1490
|
description: t.description,
|
|
815
1491
|
reenter: t.reenter,
|
|
816
1492
|
meta: t.meta,
|
|
817
|
-
input: t.input
|
|
1493
|
+
input: t.input !== undefined ? resolveTransitionInput : undefined
|
|
818
1494
|
};
|
|
819
1495
|
}
|
|
820
1496
|
|
|
@@ -827,8 +1503,9 @@ function createMachineFromConfig(json, implementations = {}) {
|
|
|
827
1503
|
description: t.description,
|
|
828
1504
|
reenter: t.reenter,
|
|
829
1505
|
meta: t.meta,
|
|
830
|
-
input: t.input,
|
|
1506
|
+
input: t.input !== undefined ? resolveTransitionInput : undefined,
|
|
831
1507
|
to: (x, enq) => {
|
|
1508
|
+
const context = resolveTransitionContext(x);
|
|
832
1509
|
if (t.actions?.length) {
|
|
833
1510
|
// Track for parallel re-execution (dedup by reference)
|
|
834
1511
|
if (!allTransitionActions.includes(t.actions)) {
|
|
@@ -840,11 +1517,16 @@ function createMachineFromConfig(json, implementations = {}) {
|
|
|
840
1517
|
const result = executeActions(t.actions, x, enq);
|
|
841
1518
|
if (result.context) {
|
|
842
1519
|
return {
|
|
843
|
-
context:
|
|
1520
|
+
context: {
|
|
1521
|
+
...context,
|
|
1522
|
+
...result.context
|
|
1523
|
+
}
|
|
844
1524
|
};
|
|
845
1525
|
}
|
|
846
1526
|
}
|
|
847
|
-
return {
|
|
1527
|
+
return context ? {
|
|
1528
|
+
context
|
|
1529
|
+
} : {};
|
|
848
1530
|
}
|
|
849
1531
|
};
|
|
850
1532
|
}
|
|
@@ -858,8 +1540,9 @@ function createMachineFromConfig(json, implementations = {}) {
|
|
|
858
1540
|
description: t.description,
|
|
859
1541
|
reenter: t.reenter,
|
|
860
1542
|
meta: t.meta,
|
|
861
|
-
input: t.input,
|
|
1543
|
+
input: t.input !== undefined ? resolveTransitionInput : undefined,
|
|
862
1544
|
to: (_x, _enq) => {
|
|
1545
|
+
const context = resolveTransitionContext(_x);
|
|
863
1546
|
if (t.actions?.length) {
|
|
864
1547
|
const targetId = target.replace(/^#/, '');
|
|
865
1548
|
pendingTransitionActionsMap[targetId] = t.actions;
|
|
@@ -870,15 +1553,29 @@ function createMachineFromConfig(json, implementations = {}) {
|
|
|
870
1553
|
}
|
|
871
1554
|
return {
|
|
872
1555
|
target: targetConfig,
|
|
1556
|
+
context,
|
|
873
1557
|
reenter: t.reenter
|
|
874
1558
|
};
|
|
875
1559
|
}
|
|
876
1560
|
};
|
|
877
1561
|
});
|
|
878
1562
|
}
|
|
1563
|
+
if (json.delays) {
|
|
1564
|
+
for (const key of Object.keys(json.delays)) {
|
|
1565
|
+
const delay = json.delays[key];
|
|
1566
|
+
if (typeof delay === 'number') {
|
|
1567
|
+
resolvedImplementations.delays[key] = delay;
|
|
1568
|
+
} else if (typeof delay === 'string') {
|
|
1569
|
+
resolvedImplementations.delays[key] = delayToMs(delay);
|
|
1570
|
+
} else if (delay && typeof delay === 'object') {
|
|
1571
|
+
resolvedImplementations.delays[key] = args => delayToMs(resolveValue(delay.duration, 'delay', makeScope(args), `$.delays.${key}.duration`));
|
|
1572
|
+
}
|
|
1573
|
+
}
|
|
1574
|
+
}
|
|
1575
|
+
assertMachineJSON(json, resolvedImplementations, expressionResolver);
|
|
879
1576
|
const rootNodeConfig = iterNode(json);
|
|
880
1577
|
const contextConfig = json.context ? {
|
|
881
|
-
context: json.context
|
|
1578
|
+
context: args => resolveValue(json.context, 'context', args, '$.context')
|
|
882
1579
|
} : {};
|
|
883
1580
|
_scxmlMachineName = json.id || '';
|
|
884
1581
|
_scxmlSessionId = 'session_' + Math.random().toString(36).slice(2);
|
|
@@ -949,7 +1646,7 @@ function createMachineFromConfig(json, implementations = {}) {
|
|
|
949
1646
|
return provided;
|
|
950
1647
|
}
|
|
951
1648
|
function isBuiltInActionJSON(action) {
|
|
952
|
-
return action.type.startsWith('@xstate.');
|
|
1649
|
+
return !isResolvable(action) && action.type.startsWith('@xstate.');
|
|
953
1650
|
}
|
|
954
1651
|
|
|
955
1652
|
/**
|
|
@@ -958,24 +1655,16 @@ function isBuiltInActionJSON(action) {
|
|
|
958
1655
|
* `machineConfigToJSON` converts a machine config into a JSON-safe structure
|
|
959
1656
|
* (the `MachineJSON` shape accepted by `createMachineFromConfig`). The boundary
|
|
960
1657
|
* between serializable structure and runtime implementations is explicit:
|
|
961
|
-
* functions are represented as code
|
|
962
|
-
* schemas, and other non-
|
|
963
|
-
* "$unserializable": <kind> }` marker rather than silently dropped.
|
|
964
|
-
*
|
|
965
|
-
* A machine definition is fully portable iff its JSON contains no
|
|
966
|
-
* `$unserializable` markers; reviving a definition that has markers requires
|
|
967
|
-
* re-providing those implementations (e.g. via `machine.provide(...)` or the
|
|
968
|
-
* `actions`/`guards`/`actorSources` maps on `createMachineFromConfig`
|
|
969
|
-
* revival).
|
|
1658
|
+
* functions are represented as `@code` source objects, while actor logic,
|
|
1659
|
+
* runtime schemas, and other non-data runtime values are omitted.
|
|
970
1660
|
*/
|
|
971
1661
|
|
|
972
1662
|
/**
|
|
973
1663
|
* Returns the JSON-serializable definition of a machine.
|
|
974
1664
|
*
|
|
975
|
-
* Inline functions are represented as `{ "@
|
|
976
|
-
*
|
|
977
|
-
*
|
|
978
|
-
* JSON config (lossless round-trip):
|
|
1665
|
+
* Inline functions are represented as `{ "@code": string, "@lang": "ts" }`. A
|
|
1666
|
+
* machine created via `createMachineFromConfig` returns its original JSON
|
|
1667
|
+
* config (lossless round-trip):
|
|
979
1668
|
*
|
|
980
1669
|
* ```ts
|
|
981
1670
|
* import { serializeMachine, createMachineFromConfig } from 'xstate';
|
|
@@ -989,22 +1678,12 @@ function isBuiltInActionJSON(action) {
|
|
|
989
1678
|
function serializeMachine(machine) {
|
|
990
1679
|
return machine._json ?? machineConfigToJSON(machine.config);
|
|
991
1680
|
}
|
|
992
|
-
function marker(kind, id) {
|
|
993
|
-
const m = {
|
|
994
|
-
$unserializable: kind
|
|
995
|
-
};
|
|
996
|
-
if (id) {
|
|
997
|
-
m.id = id;
|
|
998
|
-
}
|
|
999
|
-
return m;
|
|
1000
|
-
}
|
|
1001
1681
|
|
|
1002
1682
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
|
|
1003
1683
|
function codeExpression(fn) {
|
|
1004
1684
|
return {
|
|
1005
|
-
'@
|
|
1006
|
-
lang: 'ts'
|
|
1007
|
-
expr: fn.toString()
|
|
1685
|
+
'@code': fn.toString(),
|
|
1686
|
+
'@lang': 'ts'
|
|
1008
1687
|
};
|
|
1009
1688
|
}
|
|
1010
1689
|
function isActorLogic(value) {
|
|
@@ -1020,33 +1699,37 @@ function valueToJSON(value) {
|
|
|
1020
1699
|
return codeExpression(value);
|
|
1021
1700
|
}
|
|
1022
1701
|
if (value === null || typeof value !== 'object') {
|
|
1023
|
-
return typeof value === 'bigint' || typeof value === 'symbol' ?
|
|
1702
|
+
return typeof value === 'bigint' || typeof value === 'symbol' ? undefined : value;
|
|
1024
1703
|
}
|
|
1025
1704
|
if (isActorLogic(value)) {
|
|
1026
|
-
return
|
|
1705
|
+
return undefined;
|
|
1027
1706
|
}
|
|
1028
1707
|
if (isRuntimeSchema(value)) {
|
|
1029
|
-
return
|
|
1708
|
+
return undefined;
|
|
1030
1709
|
}
|
|
1031
1710
|
if (Array.isArray(value)) {
|
|
1032
|
-
return value.map(valueToJSON);
|
|
1711
|
+
return value.map(valueToJSON).filter(item => item !== undefined);
|
|
1033
1712
|
}
|
|
1034
1713
|
if (value.constructor !== Object && value.constructor !== undefined) {
|
|
1035
1714
|
// Class instances (actor logic, schemas, dates, ...) are not portable.
|
|
1036
|
-
return
|
|
1715
|
+
return undefined;
|
|
1037
1716
|
}
|
|
1038
1717
|
const result = {};
|
|
1039
1718
|
for (const key of Object.keys(value)) {
|
|
1040
1719
|
const v = value[key];
|
|
1041
1720
|
if (v !== undefined) {
|
|
1042
|
-
|
|
1721
|
+
const jsonValue = valueToJSON(v);
|
|
1722
|
+
if (jsonValue !== undefined) {
|
|
1723
|
+
result[key] = jsonValue;
|
|
1724
|
+
}
|
|
1043
1725
|
}
|
|
1044
1726
|
}
|
|
1045
1727
|
return result;
|
|
1046
1728
|
}
|
|
1047
1729
|
function invokeToJSON(invoke) {
|
|
1048
1730
|
if (Array.isArray(invoke)) {
|
|
1049
|
-
|
|
1731
|
+
const values = invoke.map(invokeToJSON).filter(value => value !== undefined);
|
|
1732
|
+
return values.length ? values : undefined;
|
|
1050
1733
|
}
|
|
1051
1734
|
const def = invoke;
|
|
1052
1735
|
const result = {};
|
|
@@ -1055,32 +1738,28 @@ function invokeToJSON(invoke) {
|
|
|
1055
1738
|
if (value === undefined) {
|
|
1056
1739
|
continue;
|
|
1057
1740
|
}
|
|
1058
|
-
result[key] = key === 'src' && typeof value !== 'string' ?
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1741
|
+
result[key] = key === 'src' && typeof value !== 'string' ? value.id : valueToJSON(value);
|
|
1742
|
+
if (result[key] === undefined) {
|
|
1743
|
+
delete result[key];
|
|
1744
|
+
}
|
|
1062
1745
|
}
|
|
1063
|
-
return result;
|
|
1746
|
+
return result.src === undefined ? undefined : result;
|
|
1064
1747
|
}
|
|
1065
|
-
function implementationsToJSON(map
|
|
1748
|
+
function implementationsToJSON(map) {
|
|
1066
1749
|
if (!map) {
|
|
1067
1750
|
return undefined;
|
|
1068
1751
|
}
|
|
1069
|
-
// Keys are preserved — they are the contract a revived machine must
|
|
1070
|
-
// fulfill via provide() — while values become markers (or stay, for
|
|
1071
|
-
// JSON-safe values like numeric delays).
|
|
1072
1752
|
const result = {};
|
|
1073
1753
|
for (const key of Object.keys(map)) {
|
|
1074
1754
|
const value = map[key];
|
|
1075
|
-
if (
|
|
1076
|
-
result[key] =
|
|
1077
|
-
} else if (typeof value === 'function') {
|
|
1078
|
-
result[key] = codeExpression(value);
|
|
1079
|
-
} else if (kind === 'actor' && value && typeof value === 'object') {
|
|
1080
|
-
result[key] = marker('actor', key);
|
|
1755
|
+
if (typeof value === 'function') {
|
|
1756
|
+
result[key] = undefined;
|
|
1081
1757
|
} else {
|
|
1082
1758
|
result[key] = valueToJSON(value);
|
|
1083
1759
|
}
|
|
1760
|
+
if (result[key] === undefined) {
|
|
1761
|
+
delete result[key];
|
|
1762
|
+
}
|
|
1084
1763
|
}
|
|
1085
1764
|
return result;
|
|
1086
1765
|
}
|
|
@@ -1096,7 +1775,10 @@ function stateNodeConfigToJSON(config) {
|
|
|
1096
1775
|
}
|
|
1097
1776
|
}
|
|
1098
1777
|
if (config.invoke !== undefined) {
|
|
1099
|
-
|
|
1778
|
+
const invoke = invokeToJSON(config.invoke);
|
|
1779
|
+
if (invoke !== undefined) {
|
|
1780
|
+
result.invoke = invoke;
|
|
1781
|
+
}
|
|
1100
1782
|
}
|
|
1101
1783
|
if (config.states) {
|
|
1102
1784
|
const states = {};
|
|
@@ -1110,7 +1792,7 @@ function stateNodeConfigToJSON(config) {
|
|
|
1110
1792
|
|
|
1111
1793
|
/**
|
|
1112
1794
|
* Converts a machine config (as passed to `createMachine`) to its JSON-safe
|
|
1113
|
-
* definition.
|
|
1795
|
+
* definition.
|
|
1114
1796
|
*/
|
|
1115
1797
|
function machineConfigToJSON(config) {
|
|
1116
1798
|
const result = stateNodeConfigToJSON(config);
|
|
@@ -1123,16 +1805,24 @@ function machineConfigToJSON(config) {
|
|
|
1123
1805
|
const value = config.schemas[key];
|
|
1124
1806
|
if (value && typeof value === 'object' && !('~standard' in value)) {
|
|
1125
1807
|
// Map-form schemas (events/emitted): preserve event-type keys.
|
|
1126
|
-
schemas[key] = implementationsToJSON(value
|
|
1808
|
+
schemas[key] = implementationsToJSON(value);
|
|
1127
1809
|
} else {
|
|
1128
|
-
schemas[key] =
|
|
1810
|
+
schemas[key] = valueToJSON(value);
|
|
1811
|
+
}
|
|
1812
|
+
if (schemas[key] === undefined) {
|
|
1813
|
+
delete schemas[key];
|
|
1129
1814
|
}
|
|
1130
1815
|
}
|
|
1131
1816
|
result.schemas = schemas;
|
|
1132
1817
|
}
|
|
1133
1818
|
for (const key of ['actions', 'guards', 'actorSources', 'delays']) {
|
|
1134
1819
|
if (config[key]) {
|
|
1135
|
-
|
|
1820
|
+
const value = implementationsToJSON(config[key]);
|
|
1821
|
+
if (value && Object.keys(value).length) {
|
|
1822
|
+
result[key] = value;
|
|
1823
|
+
} else {
|
|
1824
|
+
delete result[key];
|
|
1825
|
+
}
|
|
1136
1826
|
}
|
|
1137
1827
|
}
|
|
1138
1828
|
return result;
|
|
@@ -1962,6 +2652,7 @@ exports.StateNode = StateMachine.StateNode;
|
|
|
1962
2652
|
exports.SimulatedClock = SimulatedClock;
|
|
1963
2653
|
exports.SpecialTargets = SpecialTargets;
|
|
1964
2654
|
exports.assertEvent = assertEvent;
|
|
2655
|
+
exports.createFSM = createFSM;
|
|
1965
2656
|
exports.createMachine = createMachine;
|
|
1966
2657
|
exports.createMachineFromConfig = createMachineFromConfig;
|
|
1967
2658
|
exports.createStateConfig = createStateConfig;
|