small-state 0.0.0-stage → 0.3.0
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/LICENSE +21 -0
- package/README.ja.md +564 -0
- package/README.md +566 -2
- package/dist/index.cjs +1754 -0
- package/dist/index.d.cts +369 -0
- package/dist/index.d.mts +369 -0
- package/dist/index.mjs +1748 -0
- package/package.json +47 -4
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,1754 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
let arktype = require("arktype");
|
|
3
|
+
//#endregion
|
|
4
|
+
//#region src/shared/utils.ts
|
|
5
|
+
function ensureArray(v) {
|
|
6
|
+
return Array.isArray(v) ? v : !v ? [] : [v];
|
|
7
|
+
}
|
|
8
|
+
function assertOrThrow(validCondition, msg) {
|
|
9
|
+
if (!validCondition) throw new Error(msg);
|
|
10
|
+
return true;
|
|
11
|
+
}
|
|
12
|
+
function reportUnhandledError(err) {
|
|
13
|
+
setTimeout(() => {
|
|
14
|
+
throw err;
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
function deepFreeze(obj) {
|
|
18
|
+
if (obj === null || typeof obj !== "object" && typeof obj !== "function") return obj;
|
|
19
|
+
try {
|
|
20
|
+
if (Array.isArray(obj)) obj.forEach((el) => deepFreeze(el));
|
|
21
|
+
else Object.keys(obj).forEach((key) => {
|
|
22
|
+
const prop = obj[key];
|
|
23
|
+
if (!Object.isFrozen(prop)) deepFreeze(prop);
|
|
24
|
+
});
|
|
25
|
+
return Object.freeze(obj);
|
|
26
|
+
} catch (error) {
|
|
27
|
+
console.error("Error occurred in deepFreeze:", error);
|
|
28
|
+
return obj;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
function defineFixedProperty(obj, key, value, option) {
|
|
32
|
+
Object.defineProperty(obj, key, {
|
|
33
|
+
value,
|
|
34
|
+
...option
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
function isPlainObject(obj) {
|
|
38
|
+
if (obj === null || typeof obj !== "object") return false;
|
|
39
|
+
const proto = Object.getPrototypeOf(obj);
|
|
40
|
+
return proto === Object.prototype || proto === null;
|
|
41
|
+
}
|
|
42
|
+
function parentPathOf(statePath) {
|
|
43
|
+
return statePath.slice(0, statePath.lastIndexOf("/"));
|
|
44
|
+
}
|
|
45
|
+
function splitStPath(statePath) {
|
|
46
|
+
if (statePath === "") return [];
|
|
47
|
+
if (statePath[0] !== "/") throw new Error(`path must start with '/': "${statePath}"`);
|
|
48
|
+
return statePath.slice(1).split("/");
|
|
49
|
+
}
|
|
50
|
+
//#endregion
|
|
51
|
+
//#region src/machine/arktype/arktype.internal.ts
|
|
52
|
+
const AnyFn = (0, arktype.type)("Function").narrow((f) => true);
|
|
53
|
+
const FunctionRecord = (0, arktype.type)({ "[string]": AnyFn });
|
|
54
|
+
const SingleRecord = (0, arktype.type)({ "[string]": "unknown" }).narrow((value) => Object.keys(value).length === 1);
|
|
55
|
+
const ActionElement = arktype.type.or(AnyFn, "string", SingleRecord);
|
|
56
|
+
//#endregion
|
|
57
|
+
//#region src/machine/arktype/ark_m_config.ts
|
|
58
|
+
const o_Types = (0, arktype.type)({
|
|
59
|
+
"+": "reject",
|
|
60
|
+
"context?": "unknown",
|
|
61
|
+
"events?": "unknown",
|
|
62
|
+
"emittable?": "unknown",
|
|
63
|
+
"input?": "unknown",
|
|
64
|
+
"output?": "unknown"
|
|
65
|
+
});
|
|
66
|
+
const MachineConfig = (0, arktype.type)({
|
|
67
|
+
"+": "reject",
|
|
68
|
+
"types?": o_Types,
|
|
69
|
+
"name?": "string",
|
|
70
|
+
"initialState?": "string",
|
|
71
|
+
"initContext?": AnyFn,
|
|
72
|
+
"output?": AnyFn,
|
|
73
|
+
"guards?": FunctionRecord,
|
|
74
|
+
"actions?": FunctionRecord,
|
|
75
|
+
"services?": "Record<string, unknown>"
|
|
76
|
+
});
|
|
77
|
+
//#endregion
|
|
78
|
+
//#region src/machine/m.types.ts
|
|
79
|
+
const EntrySymbol = Symbol("Entry");
|
|
80
|
+
const ExitSymbol = Symbol("Exit");
|
|
81
|
+
const SubStatesSymbol = Symbol("SubStates");
|
|
82
|
+
const ParallelSymbol = Symbol("Parallel");
|
|
83
|
+
const InvokeSymbol = Symbol("Invoke");
|
|
84
|
+
const SystemEventSymbol = Symbol("SystemEvent");
|
|
85
|
+
const MachineSymbol = Symbol("Machine");
|
|
86
|
+
//#endregion
|
|
87
|
+
//#region src/machine/arktype/ark_normalized.ts
|
|
88
|
+
const NormalizedActions = ActionElement.array();
|
|
89
|
+
const x_NormalizedTransitions = (0, arktype.type)({
|
|
90
|
+
"to?": "string",
|
|
91
|
+
"fullTo?": "string",
|
|
92
|
+
actions: NormalizedActions,
|
|
93
|
+
guards: NormalizedActions
|
|
94
|
+
}).array();
|
|
95
|
+
const x_InternalEvent = (0, arktype.type)({
|
|
96
|
+
"cond?": AnyFn,
|
|
97
|
+
transitions: x_NormalizedTransitions
|
|
98
|
+
});
|
|
99
|
+
const ChildMachine = (0, arktype.type)({
|
|
100
|
+
name: "string",
|
|
101
|
+
provide: AnyFn,
|
|
102
|
+
internal: {
|
|
103
|
+
[MachineSymbol]: "true",
|
|
104
|
+
initContext: AnyFn.or("undefined"),
|
|
105
|
+
output: AnyFn.or("undefined"),
|
|
106
|
+
guards: FunctionRecord,
|
|
107
|
+
actions: FunctionRecord,
|
|
108
|
+
services: FunctionRecord,
|
|
109
|
+
definition: "unknown",
|
|
110
|
+
topLvDefItem: {
|
|
111
|
+
events: (0, arktype.type)({ "[string]": x_NormalizedTransitions }),
|
|
112
|
+
internalEvents: (0, arktype.type)({ "[string]": x_InternalEvent })
|
|
113
|
+
},
|
|
114
|
+
stateCatalog: arktype.type.instanceOf(Map).as()
|
|
115
|
+
}
|
|
116
|
+
});
|
|
117
|
+
const o_NormalizedInvocations = (0, arktype.type)({
|
|
118
|
+
"+": "reject",
|
|
119
|
+
mainKey: "string",
|
|
120
|
+
"subKey?": "string|number",
|
|
121
|
+
"input?": AnyFn,
|
|
122
|
+
emittableETypesToListen: "string[]",
|
|
123
|
+
src: arktype.type.or({
|
|
124
|
+
type: "\"function\"",
|
|
125
|
+
value: AnyFn
|
|
126
|
+
}, {
|
|
127
|
+
type: "\"string\"",
|
|
128
|
+
value: "string"
|
|
129
|
+
}, {
|
|
130
|
+
type: "\"machine\"",
|
|
131
|
+
value: ChildMachine
|
|
132
|
+
})
|
|
133
|
+
}).array();
|
|
134
|
+
const NStateItemDef = (0, arktype.type)({
|
|
135
|
+
"+": "reject",
|
|
136
|
+
pPath: "string",
|
|
137
|
+
final: AnyFn.or("boolean"),
|
|
138
|
+
entryHooks: NormalizedActions.or("null"),
|
|
139
|
+
exitHooks: NormalizedActions.or("null"),
|
|
140
|
+
invocations: o_NormalizedInvocations,
|
|
141
|
+
internalEvents: { "[string]": x_InternalEvent },
|
|
142
|
+
events: { "[string]": x_NormalizedTransitions }
|
|
143
|
+
});
|
|
144
|
+
const SubStNormalizedDefinition = (0, arktype.type)([
|
|
145
|
+
{
|
|
146
|
+
"dType?": "\"normal\"",
|
|
147
|
+
initial: "string | undefined",
|
|
148
|
+
stateOf: { "[string]": {
|
|
149
|
+
def: NStateItemDef,
|
|
150
|
+
sub: "null | this"
|
|
151
|
+
} }
|
|
152
|
+
},
|
|
153
|
+
"|",
|
|
154
|
+
{
|
|
155
|
+
dType: "\"regions\"",
|
|
156
|
+
stateOf: { "[string]": {
|
|
157
|
+
def: NStateItemDef,
|
|
158
|
+
sub: "null | this"
|
|
159
|
+
} }
|
|
160
|
+
}
|
|
161
|
+
]);
|
|
162
|
+
//#endregion
|
|
163
|
+
//#region src/machine/arktype/ark_stdef.ts
|
|
164
|
+
const x_ActionEffects = arktype.type.or(ActionElement, ActionElement.array());
|
|
165
|
+
const Transition = (0, arktype.type)({
|
|
166
|
+
"+": "reject",
|
|
167
|
+
"to?": "string",
|
|
168
|
+
"guard?": x_ActionEffects,
|
|
169
|
+
"actions?": x_ActionEffects
|
|
170
|
+
});
|
|
171
|
+
const OnFnReturn = (0, arktype.type)([
|
|
172
|
+
"string",
|
|
173
|
+
AnyFn,
|
|
174
|
+
arktype.type.or(Transition, Transition.array())
|
|
175
|
+
]).readonly();
|
|
176
|
+
const EntryHookReturn = (0, arktype.type)([
|
|
177
|
+
arktype.type.unit(EntrySymbol),
|
|
178
|
+
AnyFn,
|
|
179
|
+
"null",
|
|
180
|
+
x_ActionEffects
|
|
181
|
+
]).readonly();
|
|
182
|
+
const ExitHookReturn = (0, arktype.type)([
|
|
183
|
+
arktype.type.unit(ExitSymbol),
|
|
184
|
+
AnyFn,
|
|
185
|
+
"null",
|
|
186
|
+
x_ActionEffects
|
|
187
|
+
]).readonly();
|
|
188
|
+
const o_StDefMetaInfo = (0, arktype.type)({
|
|
189
|
+
"+": "reject",
|
|
190
|
+
"final?": AnyFn.or("boolean")
|
|
191
|
+
});
|
|
192
|
+
const o_SubStatesNormalized = (0, arktype.type)([AnyFn, SubStNormalizedDefinition]).readonly();
|
|
193
|
+
const o_Invocation = (0, arktype.type)([
|
|
194
|
+
arktype.type.unit(InvokeSymbol),
|
|
195
|
+
AnyFn,
|
|
196
|
+
"null",
|
|
197
|
+
arktype.type.or(AnyFn, "string", ChildMachine),
|
|
198
|
+
{
|
|
199
|
+
"+": "reject",
|
|
200
|
+
"subKey?": "string | number",
|
|
201
|
+
"input?": AnyFn
|
|
202
|
+
},
|
|
203
|
+
OnFnReturn.array()
|
|
204
|
+
]).readonly();
|
|
205
|
+
const StateDefinition = arktype.type.or(OnFnReturn, EntryHookReturn, ExitHookReturn, o_StDefMetaInfo, o_SubStatesNormalized, o_Invocation).array().or("null");
|
|
206
|
+
//#endregion
|
|
207
|
+
//#region src/machine/machine.ts
|
|
208
|
+
function entryHook(...actions) {
|
|
209
|
+
return [
|
|
210
|
+
EntrySymbol,
|
|
211
|
+
(() => null),
|
|
212
|
+
null,
|
|
213
|
+
actions
|
|
214
|
+
];
|
|
215
|
+
}
|
|
216
|
+
function exitHook(...actions) {
|
|
217
|
+
return [
|
|
218
|
+
ExitSymbol,
|
|
219
|
+
(() => null),
|
|
220
|
+
null,
|
|
221
|
+
actions
|
|
222
|
+
];
|
|
223
|
+
}
|
|
224
|
+
function on(eventType, config) {
|
|
225
|
+
return [
|
|
226
|
+
eventType,
|
|
227
|
+
(() => null),
|
|
228
|
+
config
|
|
229
|
+
];
|
|
230
|
+
}
|
|
231
|
+
function subStates(statesDefinition, initialState) {
|
|
232
|
+
return [(() => SubStatesSymbol), transformNormalizedDefinition(statesDefinition, initialState)];
|
|
233
|
+
}
|
|
234
|
+
function parallel(parallelDefinition) {
|
|
235
|
+
return [(() => ParallelSymbol), transformParallelNormalizedDefinition()(parallelDefinition)];
|
|
236
|
+
}
|
|
237
|
+
function invoke(service, ...args) {
|
|
238
|
+
const [config, events] = (() => {
|
|
239
|
+
if (!args.length) return [{}, []];
|
|
240
|
+
if (Array.isArray(args[0])) return [{}, args];
|
|
241
|
+
const [first, ...rest] = args;
|
|
242
|
+
return [first, rest];
|
|
243
|
+
})();
|
|
244
|
+
return [
|
|
245
|
+
InvokeSymbol,
|
|
246
|
+
(() => null),
|
|
247
|
+
null,
|
|
248
|
+
service,
|
|
249
|
+
config,
|
|
250
|
+
events
|
|
251
|
+
];
|
|
252
|
+
}
|
|
253
|
+
const o_getNormalizedStDefTemplate = () => ({
|
|
254
|
+
def: {
|
|
255
|
+
pPath: "",
|
|
256
|
+
final: false,
|
|
257
|
+
entryHooks: null,
|
|
258
|
+
exitHooks: null,
|
|
259
|
+
invocations: [],
|
|
260
|
+
internalEvents: {},
|
|
261
|
+
events: {}
|
|
262
|
+
},
|
|
263
|
+
sub: null
|
|
264
|
+
});
|
|
265
|
+
function o_normalizeTransitions(transitions) {
|
|
266
|
+
return transitions.map(({ actions, guard, ...rest }) => ({
|
|
267
|
+
actions: ensureArray(actions),
|
|
268
|
+
guards: ensureArray(guard),
|
|
269
|
+
...rest
|
|
270
|
+
}));
|
|
271
|
+
}
|
|
272
|
+
function x_parseStDefItems(items, stateName) {
|
|
273
|
+
let { def, sub } = o_getNormalizedStDefTemplate();
|
|
274
|
+
if (!items?.length) {
|
|
275
|
+
def.final = !items;
|
|
276
|
+
return {
|
|
277
|
+
def,
|
|
278
|
+
sub
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
const _getPath = () => `${def.pPath}/${stateName}`;
|
|
282
|
+
for (const [i, item] of items.entries()) {
|
|
283
|
+
if (!Array.isArray(item)) {
|
|
284
|
+
if ("final" in item) {
|
|
285
|
+
const { final } = item;
|
|
286
|
+
def.final = typeof final === "function" ? final : !!final;
|
|
287
|
+
}
|
|
288
|
+
continue;
|
|
289
|
+
}
|
|
290
|
+
switch (typeof item?.[0]) {
|
|
291
|
+
case "string": {
|
|
292
|
+
const [eType, _, transitions] = item;
|
|
293
|
+
if (Object.hasOwn(def.events, eType) || Object.hasOwn(def.internalEvents, eType)) throw new Error(`createMachine: Duplicate event definition for event "${eType}", at "${stateName}"[${i}]`);
|
|
294
|
+
const mid = ensureArray(transitions);
|
|
295
|
+
if (!mid.length || mid.some((t) => !Object.keys(t).length)) throw new Error(`createMachine: on(eventType, config): Empty config is not allowed: on("${eType}", ${JSON.stringify(transitions)}), at "${stateName}"[${i}]`);
|
|
296
|
+
const nTransitions = o_normalizeTransitions(mid);
|
|
297
|
+
switch (eType) {
|
|
298
|
+
case ":always:":
|
|
299
|
+
def.internalEvents[":always:"] = { transitions: nTransitions };
|
|
300
|
+
break;
|
|
301
|
+
case ":childDone:":
|
|
302
|
+
def.internalEvents[":childDone:"] = {
|
|
303
|
+
cond: ({ path }) => path === _getPath(),
|
|
304
|
+
transitions: nTransitions
|
|
305
|
+
};
|
|
306
|
+
break;
|
|
307
|
+
default: def.events[eType] = nTransitions;
|
|
308
|
+
}
|
|
309
|
+
break;
|
|
310
|
+
}
|
|
311
|
+
case "symbol":
|
|
312
|
+
switch (item[0]) {
|
|
313
|
+
case InvokeSymbol: {
|
|
314
|
+
const rawSrc = item[3];
|
|
315
|
+
const src = (() => {
|
|
316
|
+
switch (typeof rawSrc) {
|
|
317
|
+
case "object": return {
|
|
318
|
+
type: "machine",
|
|
319
|
+
value: rawSrc
|
|
320
|
+
};
|
|
321
|
+
case "function": return {
|
|
322
|
+
type: "function",
|
|
323
|
+
value: rawSrc
|
|
324
|
+
};
|
|
325
|
+
default: return {
|
|
326
|
+
type: "string",
|
|
327
|
+
value: rawSrc
|
|
328
|
+
};
|
|
329
|
+
}
|
|
330
|
+
})();
|
|
331
|
+
const { invocations, internalEvents } = def;
|
|
332
|
+
const emittableETypesToListen = [];
|
|
333
|
+
invocations.push({
|
|
334
|
+
...item[4],
|
|
335
|
+
src,
|
|
336
|
+
emittableETypesToListen,
|
|
337
|
+
mainKey: ""
|
|
338
|
+
});
|
|
339
|
+
const suffixNum = invocations.length - 1;
|
|
340
|
+
let hasErrObserver = false;
|
|
341
|
+
for (const [j, [ivkEType, _, ivkTransitions]] of item[5].entries()) {
|
|
342
|
+
const transitions = ensureArray(ivkTransitions);
|
|
343
|
+
if (!transitions.length || transitions.some((t) => !Object.keys(t).length)) throw new Error(`createMachine: on(eventType, config): The 'config' argument is required, at state:"${stateName}"[${i}] > invoke events[${j}] event:"${ivkEType}"`);
|
|
344
|
+
const eType = `invoke/${ivkEType}[${suffixNum}]`;
|
|
345
|
+
if (Object.hasOwn(internalEvents, eType)) throw new Error(`createMachine: Duplicate event definition for event "${ivkEType}", at state:"${stateName}"[${i}] > invoke events[${j}]`);
|
|
346
|
+
internalEvents[eType] = {
|
|
347
|
+
cond: ({ path }) => path === _getPath(),
|
|
348
|
+
transitions: o_normalizeTransitions(transitions)
|
|
349
|
+
};
|
|
350
|
+
if (ivkEType === ":error:") hasErrObserver = true;
|
|
351
|
+
else if (ivkEType !== ":done:") emittableETypesToListen.push(ivkEType);
|
|
352
|
+
}
|
|
353
|
+
if (!hasErrObserver) internalEvents[`invoke/:error:[${suffixNum}]`] = {
|
|
354
|
+
cond: ({ path }) => path === _getPath(),
|
|
355
|
+
transitions: [{
|
|
356
|
+
guards: [],
|
|
357
|
+
actions: [({ e }) => {
|
|
358
|
+
throw e.error;
|
|
359
|
+
}]
|
|
360
|
+
}]
|
|
361
|
+
};
|
|
362
|
+
break;
|
|
363
|
+
}
|
|
364
|
+
case EntrySymbol:
|
|
365
|
+
def.entryHooks = ensureArray(item[3]);
|
|
366
|
+
break;
|
|
367
|
+
case ExitSymbol: def.exitHooks = ensureArray(item[3]);
|
|
368
|
+
}
|
|
369
|
+
break;
|
|
370
|
+
case "function": {
|
|
371
|
+
const subStDict = item[1];
|
|
372
|
+
if (sub) throw new Error(`createMachine: Duplicated subStates: at "${stateName}"[${i}] { ${Object.keys("stateOf" in subStDict ? subStDict.stateOf : {}).map((el) => `${el}:...`).join(", ")} }`);
|
|
373
|
+
sub = subStDict;
|
|
374
|
+
break;
|
|
375
|
+
}
|
|
376
|
+
default: throw new Error(`createMachine: Unexpected array[0] type: ${JSON.stringify(item?.[0])}`);
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
return {
|
|
380
|
+
def,
|
|
381
|
+
sub
|
|
382
|
+
};
|
|
383
|
+
}
|
|
384
|
+
function o_normalizeStates(stateDefOf) {
|
|
385
|
+
return Object.entries(stateDefOf).reduce((acc, [stKey, stDefs], i) => {
|
|
386
|
+
if (stKey.includes("/")) throw new Error(`createMachine: State names cannot contain the character "/": "${stKey}"`);
|
|
387
|
+
else if (Array.isArray(stDefs) || stDefs == null) try {
|
|
388
|
+
const { def, sub } = x_parseStDefItems(StateDefinition.assert(stDefs), stKey);
|
|
389
|
+
acc[stKey] = {
|
|
390
|
+
def,
|
|
391
|
+
sub
|
|
392
|
+
};
|
|
393
|
+
} catch (err) {
|
|
394
|
+
if (err instanceof Error) throw new Error(`createMachine:(at state "${stKey}") ${err.name}: ${err.message}`);
|
|
395
|
+
}
|
|
396
|
+
else throw new Error(`createMachine: Invalid state definition for state "${String(stKey)}". Expected an array but got ${typeof stDefs}.`);
|
|
397
|
+
return acc;
|
|
398
|
+
}, {});
|
|
399
|
+
}
|
|
400
|
+
function transformNormalizedDefinition(stateDefOf, initialState) {
|
|
401
|
+
const firstState = Object.keys(stateDefOf)?.[0];
|
|
402
|
+
return {
|
|
403
|
+
initial: !firstState ? void 0 : initialState && assertOrThrow(Object.hasOwn(stateDefOf, initialState), `createMachine: Invalid initialState: "${initialState}"`) ? initialState : firstState,
|
|
404
|
+
stateOf: o_normalizeStates(stateDefOf)
|
|
405
|
+
};
|
|
406
|
+
}
|
|
407
|
+
function transformParallelNormalizedDefinition() {
|
|
408
|
+
return (parallelDefinition) => {
|
|
409
|
+
return {
|
|
410
|
+
dType: "regions",
|
|
411
|
+
stateOf: Object.fromEntries(Object.entries(parallelDefinition).map(([regionName, rawDefinition]) => {
|
|
412
|
+
if (regionName.includes("/")) throw new Error(`createMachine: Parallel region state names cannot contain the character "/": "${regionName}"`);
|
|
413
|
+
const len = rawDefinition.length;
|
|
414
|
+
const leafDefOf = rawDefinition[len - 1] || {};
|
|
415
|
+
const { def } = x_parseStDefItems(rawDefinition[len - 2] || [], regionName);
|
|
416
|
+
return [regionName, {
|
|
417
|
+
def,
|
|
418
|
+
sub: transformNormalizedDefinition(leafDefOf, void 0)
|
|
419
|
+
}];
|
|
420
|
+
}))
|
|
421
|
+
};
|
|
422
|
+
};
|
|
423
|
+
}
|
|
424
|
+
function o_setPPathAndInvocationMainKeyThenAddCatalog(nStItemRecord, parent, addToCatalog, isRegion, addRegionPath) {
|
|
425
|
+
for (const [key, nState] of Object.entries(nStItemRecord)) {
|
|
426
|
+
const { def, sub } = nState;
|
|
427
|
+
def.pPath = parent;
|
|
428
|
+
const stPath = `${parent}/${key}`;
|
|
429
|
+
for (const [i, e] of def.invocations.entries()) e.mainKey = e.src.type === "string" ? e.src.value : `${stPath}[${i}]`;
|
|
430
|
+
addToCatalog(stPath, nState);
|
|
431
|
+
if (isRegion) addRegionPath(stPath);
|
|
432
|
+
if (sub) {
|
|
433
|
+
const isParallel = "dType" in sub && sub.dType === "regions";
|
|
434
|
+
o_setPPathAndInvocationMainKeyThenAddCatalog(sub.stateOf, stPath, addToCatalog, isParallel, addRegionPath);
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
function o_getTransitionFullPath(to, crrPath, pPath, stateCatalog) {
|
|
439
|
+
const pth = to[0] === "/" ? to : to.startsWith("./") ? `${crrPath}/${to.slice(2)}` : to.startsWith("../") ? `${pPath}/${to.slice(3)}` : "";
|
|
440
|
+
if (pth) return stateCatalog.has(pth) ? pth : "";
|
|
441
|
+
for (const pthFn of [() => `${crrPath}/${to}`, () => `${pPath}/${to}`]) {
|
|
442
|
+
const pth = pthFn();
|
|
443
|
+
if (stateCatalog.has(pth)) return pth;
|
|
444
|
+
}
|
|
445
|
+
return "";
|
|
446
|
+
}
|
|
447
|
+
function x_readyToValidateTransitionAndSetFullTo(stateCatalog, statePath, pPath, regionPaths) {
|
|
448
|
+
const ancestorRegion = regionPaths.find((regionPath) => pPath.startsWith(regionPath));
|
|
449
|
+
const parallelRoot = !ancestorRegion ? null : parentPathOf(ancestorRegion);
|
|
450
|
+
return (transitions, eType) => {
|
|
451
|
+
for (const transition of transitions) {
|
|
452
|
+
const { to } = transition;
|
|
453
|
+
if (!to) continue;
|
|
454
|
+
const tFullPath = o_getTransitionFullPath(to, statePath, pPath, stateCatalog);
|
|
455
|
+
if (!tFullPath) throw new Error(`createMachine: Invalid transition "${to}" in state "${statePath || "/"}" (on:"${eType}").`);
|
|
456
|
+
if (ancestorRegion && !tFullPath.startsWith(ancestorRegion) && tFullPath.startsWith(parallelRoot)) throw new Error(`createMachine: Transitions in a parallel state cannot target a different region: on('${eType}', ['${to}', ...]) in state "${statePath || "/"}".`);
|
|
457
|
+
transition.fullTo = tFullPath;
|
|
458
|
+
}
|
|
459
|
+
};
|
|
460
|
+
}
|
|
461
|
+
function o_validateTransitionAndSetFullTo(stateCatalog, regionPaths) {
|
|
462
|
+
stateCatalog.forEach(({ def }, statePath) => {
|
|
463
|
+
const { events, internalEvents, pPath } = def;
|
|
464
|
+
const _validateTransitionAndSetFullTo = x_readyToValidateTransitionAndSetFullTo(stateCatalog, statePath, pPath, regionPaths);
|
|
465
|
+
Object.entries(events).forEach(([eType, transitions]) => {
|
|
466
|
+
if (!transitions) return;
|
|
467
|
+
_validateTransitionAndSetFullTo(transitions, eType);
|
|
468
|
+
});
|
|
469
|
+
Object.entries(internalEvents).forEach(([eType, internalEvent]) => _validateTransitionAndSetFullTo(internalEvent.transitions, eType));
|
|
470
|
+
});
|
|
471
|
+
}
|
|
472
|
+
function getDefinitionAndCatalog(config, defineStates) {
|
|
473
|
+
{
|
|
474
|
+
const configOut = MachineConfig(config);
|
|
475
|
+
if (configOut instanceof arktype.type.errors) throw new Error(`createMachine: Invalid machine config: ${configOut.summary}`);
|
|
476
|
+
}
|
|
477
|
+
const rawDefinition = defineStates({
|
|
478
|
+
on,
|
|
479
|
+
entryHook,
|
|
480
|
+
exitHook,
|
|
481
|
+
subStates,
|
|
482
|
+
parallel,
|
|
483
|
+
invoke
|
|
484
|
+
});
|
|
485
|
+
const [stateDefDict, globalEvents] = Array.isArray(rawDefinition) ? [rawDefinition[rawDefinition.length - 1], rawDefinition[rawDefinition.length - 2]] : [rawDefinition, []];
|
|
486
|
+
{
|
|
487
|
+
const dType = stateDefDict === null ? "null" : typeof stateDefDict;
|
|
488
|
+
if (dType !== "object") throw new Error(`createMachine: Invalid state definition: Expected an object but got ${dType}.`);
|
|
489
|
+
if (Object.keys(stateDefDict).length === 0) throw new Error(`createMachine: No states in machine definition.`);
|
|
490
|
+
}
|
|
491
|
+
const { initial, stateOf } = transformNormalizedDefinition(stateDefDict, config.initialState);
|
|
492
|
+
const stateCatalog = /* @__PURE__ */ new Map();
|
|
493
|
+
const regionPaths = [];
|
|
494
|
+
o_setPPathAndInvocationMainKeyThenAddCatalog(stateOf, "", (stPath, state) => stateCatalog.set(stPath, state), false, (regionPath) => regionPaths.push(regionPath));
|
|
495
|
+
regionPaths.sort((a, b) => b.length - a.length);
|
|
496
|
+
o_validateTransitionAndSetFullTo(stateCatalog, regionPaths);
|
|
497
|
+
const { def: { events, internalEvents } } = x_parseStDefItems(globalEvents || [], "(root)");
|
|
498
|
+
{
|
|
499
|
+
const _validateTransitionAndSetFullTo = x_readyToValidateTransitionAndSetFullTo(stateCatalog, "", "", regionPaths);
|
|
500
|
+
Object.entries(events).forEach(([eType, transitions]) => {
|
|
501
|
+
if (!transitions) return;
|
|
502
|
+
_validateTransitionAndSetFullTo(transitions, eType);
|
|
503
|
+
});
|
|
504
|
+
Object.entries(internalEvents).forEach(([eType, internalEvent]) => _validateTransitionAndSetFullTo(internalEvent.transitions, eType));
|
|
505
|
+
}
|
|
506
|
+
return {
|
|
507
|
+
definition: {
|
|
508
|
+
initial,
|
|
509
|
+
stateOf
|
|
510
|
+
},
|
|
511
|
+
topLvDefItem: {
|
|
512
|
+
events,
|
|
513
|
+
internalEvents
|
|
514
|
+
},
|
|
515
|
+
stateCatalog,
|
|
516
|
+
regionPaths
|
|
517
|
+
};
|
|
518
|
+
}
|
|
519
|
+
function defServiceFn(fn) {
|
|
520
|
+
return fn;
|
|
521
|
+
}
|
|
522
|
+
function isMachine(v) {
|
|
523
|
+
if (!(v && typeof v === "object" && "internal" in v)) return false;
|
|
524
|
+
const { internal } = v;
|
|
525
|
+
return !!(internal && typeof internal === "object" && MachineSymbol in internal);
|
|
526
|
+
}
|
|
527
|
+
function makeMachine(config, defineStates) {
|
|
528
|
+
const { definition, topLvDefItem, stateCatalog } = getDefinitionAndCatalog(config, defineStates);
|
|
529
|
+
const { name, initContext, output, guards, actions, services } = config;
|
|
530
|
+
return deepFreeze({
|
|
531
|
+
name: name || "(machine)",
|
|
532
|
+
provide,
|
|
533
|
+
internal: {
|
|
534
|
+
[MachineSymbol]: true,
|
|
535
|
+
initContext,
|
|
536
|
+
output,
|
|
537
|
+
guards: guards || {},
|
|
538
|
+
actions: actions || {},
|
|
539
|
+
services: services || {},
|
|
540
|
+
definition,
|
|
541
|
+
topLvDefItem,
|
|
542
|
+
stateCatalog
|
|
543
|
+
}
|
|
544
|
+
});
|
|
545
|
+
}
|
|
546
|
+
function provide(implementations) {
|
|
547
|
+
const { internal } = this;
|
|
548
|
+
const { guards, actions, services } = internal;
|
|
549
|
+
return deepFreeze({
|
|
550
|
+
...this,
|
|
551
|
+
internal: {
|
|
552
|
+
...internal,
|
|
553
|
+
guards: {
|
|
554
|
+
...guards,
|
|
555
|
+
...implementations.guards
|
|
556
|
+
},
|
|
557
|
+
actions: {
|
|
558
|
+
...actions,
|
|
559
|
+
...implementations.actions
|
|
560
|
+
},
|
|
561
|
+
services: {
|
|
562
|
+
...services,
|
|
563
|
+
...implementations.services
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
});
|
|
567
|
+
}
|
|
568
|
+
//#endregion
|
|
569
|
+
//#region src/runtime/MailBox.ts
|
|
570
|
+
var MailBox = class {
|
|
571
|
+
#isActive = false;
|
|
572
|
+
#current = null;
|
|
573
|
+
#last = null;
|
|
574
|
+
#process;
|
|
575
|
+
#flush;
|
|
576
|
+
constructor(process, isAsync = false) {
|
|
577
|
+
this.#process = process;
|
|
578
|
+
this.#flush = isAsync ? this.#_flushAsync : this.#_flush;
|
|
579
|
+
}
|
|
580
|
+
start() {
|
|
581
|
+
this.#isActive = true;
|
|
582
|
+
this.#flush();
|
|
583
|
+
}
|
|
584
|
+
clear() {
|
|
585
|
+
if (this.#current) {
|
|
586
|
+
this.#current.next = null;
|
|
587
|
+
this.#last = this.#current;
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
enqueue(value, payload) {
|
|
591
|
+
const newElement = {
|
|
592
|
+
value,
|
|
593
|
+
payload,
|
|
594
|
+
next: null
|
|
595
|
+
};
|
|
596
|
+
if (this.#current) {
|
|
597
|
+
this.#last.next = newElement;
|
|
598
|
+
this.#last = newElement;
|
|
599
|
+
return;
|
|
600
|
+
}
|
|
601
|
+
this.#current = newElement;
|
|
602
|
+
this.#last = newElement;
|
|
603
|
+
if (this.#isActive) this.#flush();
|
|
604
|
+
}
|
|
605
|
+
#_flush() {
|
|
606
|
+
while (this.#current) {
|
|
607
|
+
const { value, payload } = this.#current;
|
|
608
|
+
this.#process(value, payload);
|
|
609
|
+
this.#current = this.#current.next;
|
|
610
|
+
}
|
|
611
|
+
this.#last = null;
|
|
612
|
+
}
|
|
613
|
+
async #_flushAsync() {
|
|
614
|
+
while (this.#current) {
|
|
615
|
+
const { value, payload } = this.#current;
|
|
616
|
+
await this.#process(value, payload);
|
|
617
|
+
this.#current = this.#current.next;
|
|
618
|
+
}
|
|
619
|
+
this.#last = null;
|
|
620
|
+
}
|
|
621
|
+
};
|
|
622
|
+
//#endregion
|
|
623
|
+
//#region src/runtime/runtime_utils.ts
|
|
624
|
+
function x_resolveActionElement(kind, type, implRecord, keyParams = void 0) {
|
|
625
|
+
switch (typeof type) {
|
|
626
|
+
case "function": return [type];
|
|
627
|
+
case "string":
|
|
628
|
+
if (!Object.hasOwn(implRecord, type)) throw new Error(`unknown ${kind} type: "${type}"`);
|
|
629
|
+
return [implRecord[type], keyParams];
|
|
630
|
+
case "object": {
|
|
631
|
+
const entries = Object.entries(type);
|
|
632
|
+
if (!(entries.length === 1 && isPlainObject(type))) throw new Error(`invalid ${kind} type: "${JSON.stringify(type)}"`);
|
|
633
|
+
const [key, val] = entries[0];
|
|
634
|
+
if (!Object.hasOwn(implRecord, key)) throw new Error(`unknown ${kind} type: "${key}"`);
|
|
635
|
+
return [implRecord[key], val];
|
|
636
|
+
}
|
|
637
|
+
default: throw new Error(`invalid ${kind} type: "${JSON.stringify(type)}"`);
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
function resolveGuardElement(type, machineGuards, keyParams = void 0) {
|
|
641
|
+
return x_resolveActionElement("Guard", type, machineGuards, keyParams);
|
|
642
|
+
}
|
|
643
|
+
function resolveActionElement(type, machineActions, keyParams = void 0) {
|
|
644
|
+
return x_resolveActionElement("Action", type, machineActions, keyParams);
|
|
645
|
+
}
|
|
646
|
+
const runtimeUtils = { makeFnBaseParam(context, event, input) {
|
|
647
|
+
return {
|
|
648
|
+
context,
|
|
649
|
+
c: context,
|
|
650
|
+
event,
|
|
651
|
+
e: event,
|
|
652
|
+
input
|
|
653
|
+
};
|
|
654
|
+
} };
|
|
655
|
+
function readyToEvalGuard(fnBaseParam, machineGuards) {
|
|
656
|
+
return (guard) => {
|
|
657
|
+
const [fn, params] = resolveGuardElement(guard, machineGuards);
|
|
658
|
+
return fn(fnBaseParam, typeof params !== "function" ? params : params(fnBaseParam));
|
|
659
|
+
};
|
|
660
|
+
}
|
|
661
|
+
function traverseStateRouteMatch(stateRoute, stPathSplitted) {
|
|
662
|
+
let focusedRoute = stateRoute;
|
|
663
|
+
let routeIdx = 0;
|
|
664
|
+
const len = stPathSplitted.length;
|
|
665
|
+
let i = 0;
|
|
666
|
+
for (; i < len; i++, routeIdx++) {
|
|
667
|
+
const sName = stPathSplitted[i];
|
|
668
|
+
const routeEl = focusedRoute[routeIdx];
|
|
669
|
+
if (!routeEl) return {
|
|
670
|
+
isExhausted: false,
|
|
671
|
+
endIdx: i,
|
|
672
|
+
remaining: [focusedRoute, routeIdx]
|
|
673
|
+
};
|
|
674
|
+
else if (typeof routeEl === "string") {
|
|
675
|
+
if (sName !== routeEl.valueOf()) return {
|
|
676
|
+
isExhausted: false,
|
|
677
|
+
endIdx: i,
|
|
678
|
+
remaining: [focusedRoute, routeIdx]
|
|
679
|
+
};
|
|
680
|
+
continue;
|
|
681
|
+
}
|
|
682
|
+
const { parallel, regions } = routeEl;
|
|
683
|
+
if (sName !== parallel) return {
|
|
684
|
+
isExhausted: false,
|
|
685
|
+
endIdx: i
|
|
686
|
+
};
|
|
687
|
+
i += 1;
|
|
688
|
+
if (i >= len) return {
|
|
689
|
+
isExhausted: true,
|
|
690
|
+
endIdx: i,
|
|
691
|
+
remaining: [
|
|
692
|
+
focusedRoute,
|
|
693
|
+
routeIdx,
|
|
694
|
+
true
|
|
695
|
+
]
|
|
696
|
+
};
|
|
697
|
+
const regionRoute = regions[stPathSplitted[i]];
|
|
698
|
+
if (!regionRoute) return {
|
|
699
|
+
isExhausted: false,
|
|
700
|
+
endIdx: i
|
|
701
|
+
};
|
|
702
|
+
focusedRoute = regionRoute;
|
|
703
|
+
routeIdx = -1;
|
|
704
|
+
}
|
|
705
|
+
return {
|
|
706
|
+
isExhausted: true,
|
|
707
|
+
endIdx: i,
|
|
708
|
+
remaining: [focusedRoute, routeIdx]
|
|
709
|
+
};
|
|
710
|
+
}
|
|
711
|
+
//#endregion
|
|
712
|
+
//#region src/runtime/runtime_step.core.ts
|
|
713
|
+
function buildTransitionCandidate(transition, tIndex, firedIn, leaves) {
|
|
714
|
+
return {
|
|
715
|
+
transition,
|
|
716
|
+
tIndex,
|
|
717
|
+
firedIn,
|
|
718
|
+
leaves
|
|
719
|
+
};
|
|
720
|
+
}
|
|
721
|
+
const x_getStItemDef = (stPath, stateCatalog) => {
|
|
722
|
+
const stateItem = stateCatalog.get(stPath);
|
|
723
|
+
if (!stateItem) throw new Error(`Unknown state path "${stPath}"`);
|
|
724
|
+
return stateItem.def;
|
|
725
|
+
};
|
|
726
|
+
function o_deriveLowestCommonAncestor(model, leaves) {
|
|
727
|
+
let crrLeaves = leaves;
|
|
728
|
+
let bgn = 1;
|
|
729
|
+
let end = model.indexOf("/", bgn) + 1;
|
|
730
|
+
while (end) {
|
|
731
|
+
const needle = model.slice(bgn, end);
|
|
732
|
+
crrLeaves = crrLeaves.filter((pth) => pth.startsWith(needle, bgn));
|
|
733
|
+
if (!crrLeaves.length) break;
|
|
734
|
+
bgn = end;
|
|
735
|
+
end = model.indexOf("/", bgn) + 1;
|
|
736
|
+
}
|
|
737
|
+
return model.slice(0, bgn - 1);
|
|
738
|
+
}
|
|
739
|
+
function x_removeConflictedTransitions(tCandidates, activeParallels) {
|
|
740
|
+
if (tCandidates.length < 2 || !activeParallels.length) return tCandidates.map(({ transition, tIndex, leaves }) => {
|
|
741
|
+
const { fullTo } = transition;
|
|
742
|
+
return {
|
|
743
|
+
tIndex,
|
|
744
|
+
transition,
|
|
745
|
+
lca: !fullTo ? void 0 : o_deriveLowestCommonAncestor(fullTo, leaves)
|
|
746
|
+
};
|
|
747
|
+
});
|
|
748
|
+
const stateStayingTExecs = [];
|
|
749
|
+
const nonPExitedTExecs = [];
|
|
750
|
+
const accStChangingTExecDict = Object.create(null);
|
|
751
|
+
for (const tCand of tCandidates) {
|
|
752
|
+
const { transition, tIndex, leaves } = tCand;
|
|
753
|
+
const { fullTo } = transition;
|
|
754
|
+
if (!fullTo) {
|
|
755
|
+
stateStayingTExecs.push({
|
|
756
|
+
transition,
|
|
757
|
+
tIndex
|
|
758
|
+
});
|
|
759
|
+
continue;
|
|
760
|
+
}
|
|
761
|
+
const lca = o_deriveLowestCommonAncestor(fullTo, leaves);
|
|
762
|
+
const exitedParallel = (() => {
|
|
763
|
+
const idx = fullTo.indexOf("/", lca.length + 1);
|
|
764
|
+
const needle = idx == -1 ? fullTo : fullTo.slice(0, idx + 1);
|
|
765
|
+
const candidates = activeParallels.filter((p) => !p.startsWith(needle) && p.startsWith(lca));
|
|
766
|
+
return !candidates.length ? "" : candidates.reduce((a, b) => a.length <= b.length ? a : b);
|
|
767
|
+
})();
|
|
768
|
+
if (!exitedParallel) nonPExitedTExecs.push({
|
|
769
|
+
transition,
|
|
770
|
+
tIndex,
|
|
771
|
+
lca
|
|
772
|
+
});
|
|
773
|
+
else if (!(exitedParallel in accStChangingTExecDict)) accStChangingTExecDict[exitedParallel] = {
|
|
774
|
+
transition,
|
|
775
|
+
tIndex,
|
|
776
|
+
lca
|
|
777
|
+
};
|
|
778
|
+
}
|
|
779
|
+
const [tmp1, tmp2] = (() => {
|
|
780
|
+
const _adoptedParallels = [];
|
|
781
|
+
return [Object.entries(accStChangingTExecDict).sort(([pathA], [pathB]) => pathA > pathB ? 1 : -1).reduce((acc, [pth, tExec]) => {
|
|
782
|
+
if (!_adoptedParallels.some((parPath) => pth.startsWith(parPath))) {
|
|
783
|
+
_adoptedParallels.push(pth);
|
|
784
|
+
acc.push(tExec);
|
|
785
|
+
}
|
|
786
|
+
return acc;
|
|
787
|
+
}, []), nonPExitedTExecs.filter(({ lca }) => !_adoptedParallels.some((pth) => lca?.startsWith(pth)))];
|
|
788
|
+
})();
|
|
789
|
+
return [
|
|
790
|
+
...stateStayingTExecs,
|
|
791
|
+
...tmp1,
|
|
792
|
+
...tmp2
|
|
793
|
+
];
|
|
794
|
+
}
|
|
795
|
+
function o_findChildOrSelfTransition(stPath, stateRoute, childIdx, activeParallelSet, stateCatalog, findTransition, eType) {
|
|
796
|
+
const [childCandidates, cLeaves] = x_findDeepestTransitions(stateRoute, childIdx, stPath, activeParallelSet, stateCatalog, findTransition, eType);
|
|
797
|
+
if (childCandidates.length) return [childCandidates, []];
|
|
798
|
+
const result = findTransition(x_getStItemDef(stPath, stateCatalog), eType);
|
|
799
|
+
const leaves = cLeaves.length ? cLeaves : [stPath];
|
|
800
|
+
if (!result) return [[], leaves];
|
|
801
|
+
const { transition, tIndex } = result;
|
|
802
|
+
return [[buildTransitionCandidate(transition, tIndex, stPath, leaves)], []];
|
|
803
|
+
}
|
|
804
|
+
function x_findDeepestTransitions(stateRoute, idx, parentPath, activeParallelSet, stateCatalog, findTransition, eType = "") {
|
|
805
|
+
if (idx >= stateRoute.length) return [[], []];
|
|
806
|
+
const stateNameOrParallel = stateRoute[idx];
|
|
807
|
+
if (typeof stateNameOrParallel === "string") return o_findChildOrSelfTransition(`${parentPath}/${stateNameOrParallel}`, stateRoute, idx + 1, activeParallelSet, stateCatalog, findTransition, eType);
|
|
808
|
+
const { parallel, regions } = stateNameOrParallel;
|
|
809
|
+
const parallelRoot = `${parentPath}/${parallel}`;
|
|
810
|
+
activeParallelSet.add(parallelRoot);
|
|
811
|
+
const accTCandidates = [];
|
|
812
|
+
const accLeaves = [];
|
|
813
|
+
for (const key in regions) {
|
|
814
|
+
const [resultCandidates, resultLeaves] = o_findChildOrSelfTransition(`${parentPath}/${parallel}/${key}`, regions[key], 0, activeParallelSet, stateCatalog, findTransition, eType);
|
|
815
|
+
if (resultCandidates.length) accTCandidates.push(...resultCandidates);
|
|
816
|
+
if (resultLeaves.length) accLeaves.push(...resultLeaves);
|
|
817
|
+
}
|
|
818
|
+
if (accTCandidates.length) return [accTCandidates, []];
|
|
819
|
+
const result = findTransition(x_getStItemDef(parallelRoot, stateCatalog), eType);
|
|
820
|
+
if (!result) return [[], accLeaves];
|
|
821
|
+
const { transition, tIndex } = result;
|
|
822
|
+
return [[buildTransitionCandidate(transition, tIndex, parallelRoot, accLeaves)], []];
|
|
823
|
+
}
|
|
824
|
+
function o_unwrapSystemEvent(event) {
|
|
825
|
+
return !(SystemEventSymbol in event && event.payload) ? event : event.payload;
|
|
826
|
+
}
|
|
827
|
+
function o_evalActionParams(param, context, event, input) {
|
|
828
|
+
return typeof param !== "function" ? param : param(runtimeUtils.makeFnBaseParam(context, event, input));
|
|
829
|
+
}
|
|
830
|
+
function readyAction(origContext, rawEvent, input, machineGuards, machineActions, rtOutbound) {
|
|
831
|
+
const event = o_unwrapSystemEvent(rawEvent);
|
|
832
|
+
const { emit } = rtOutbound;
|
|
833
|
+
let context = deepFreeze(origContext);
|
|
834
|
+
let updateApplications = [];
|
|
835
|
+
let enqActions = [];
|
|
836
|
+
const update = (updates) => {
|
|
837
|
+
if (!updates || typeof updates !== "object") throw new Error(`RT"${rtOutbound.id}"> Invalid updates argument: ${JSON.stringify(updates)}`);
|
|
838
|
+
updateApplications.push(updates);
|
|
839
|
+
};
|
|
840
|
+
function applyUpdate() {
|
|
841
|
+
for (const updates of updateApplications) {
|
|
842
|
+
const partial = Object.entries(updates).reduce((acc, [key, val]) => {
|
|
843
|
+
acc[key] = typeof val === "function" ? val() : val;
|
|
844
|
+
return acc;
|
|
845
|
+
}, {});
|
|
846
|
+
context = {
|
|
847
|
+
...context,
|
|
848
|
+
...partial
|
|
849
|
+
};
|
|
850
|
+
}
|
|
851
|
+
if (!updateApplications.length) return;
|
|
852
|
+
context = deepFreeze(context);
|
|
853
|
+
updateApplications = [];
|
|
854
|
+
}
|
|
855
|
+
function check(type, ...args) {
|
|
856
|
+
const [fn, params] = resolveGuardElement(type, machineGuards, args?.[0]);
|
|
857
|
+
const fnBaseParam = runtimeUtils.makeFnBaseParam(context, event, input);
|
|
858
|
+
return fn(fnBaseParam, typeof params !== "function" ? params : params(fnBaseParam));
|
|
859
|
+
}
|
|
860
|
+
function sendTo(serviceName, ...args) {
|
|
861
|
+
const [subKey, sEvent] = args.length == 2 ? args : ["", args[0]];
|
|
862
|
+
const childKey = subKey || typeof subKey === "number" ? `${serviceName}:${subKey}` : serviceName;
|
|
863
|
+
if (!Object.hasOwn(rtOutbound.children, childKey)) throw new Error(`RT"${rtOutbound.id}"> Cannot send event to "${childKey}": child not found.`);
|
|
864
|
+
rtOutbound.children[childKey]?.send(sEvent);
|
|
865
|
+
}
|
|
866
|
+
let isProcessingQueue = false;
|
|
867
|
+
function enqueue(type, ...args) {
|
|
868
|
+
if (isProcessingQueue) throw new Error(`RT"${rtOutbound.id}"> Nested enqueue operations are not supported.`);
|
|
869
|
+
const [fn, params] = resolveActionElement(type, machineActions, args?.[0]);
|
|
870
|
+
enqActions.push({
|
|
871
|
+
fn,
|
|
872
|
+
params
|
|
873
|
+
});
|
|
874
|
+
}
|
|
875
|
+
let saveContext = origContext;
|
|
876
|
+
let saveFnBaseParam;
|
|
877
|
+
return {
|
|
878
|
+
makeFnBaseParam() {
|
|
879
|
+
if (!(context === saveContext && saveFnBaseParam)) {
|
|
880
|
+
saveContext = context;
|
|
881
|
+
saveFnBaseParam = runtimeUtils.makeFnBaseParam(context, event, input);
|
|
882
|
+
}
|
|
883
|
+
return saveFnBaseParam;
|
|
884
|
+
},
|
|
885
|
+
execAction(transitionAction, eventOverride) {
|
|
886
|
+
const bodies = resolveActionElement(transitionAction, machineActions);
|
|
887
|
+
const qEvent = !eventOverride ? event : o_unwrapSystemEvent(eventOverride);
|
|
888
|
+
if (bodies.length === 1) bodies[0]({
|
|
889
|
+
context,
|
|
890
|
+
c: context,
|
|
891
|
+
event: qEvent,
|
|
892
|
+
e: qEvent,
|
|
893
|
+
input,
|
|
894
|
+
update,
|
|
895
|
+
sendTo,
|
|
896
|
+
emit,
|
|
897
|
+
check,
|
|
898
|
+
enqueue
|
|
899
|
+
});
|
|
900
|
+
else {
|
|
901
|
+
const [fn, params] = bodies;
|
|
902
|
+
fn({
|
|
903
|
+
context,
|
|
904
|
+
c: context,
|
|
905
|
+
event: qEvent,
|
|
906
|
+
e: qEvent,
|
|
907
|
+
input,
|
|
908
|
+
update,
|
|
909
|
+
emit
|
|
910
|
+
}, o_evalActionParams(params, context, qEvent, input));
|
|
911
|
+
}
|
|
912
|
+
applyUpdate();
|
|
913
|
+
},
|
|
914
|
+
processQueue() {
|
|
915
|
+
isProcessingQueue = true;
|
|
916
|
+
try {
|
|
917
|
+
for (const { fn, params } of enqActions) {
|
|
918
|
+
fn({
|
|
919
|
+
context,
|
|
920
|
+
c: context,
|
|
921
|
+
event,
|
|
922
|
+
e: event,
|
|
923
|
+
input,
|
|
924
|
+
update,
|
|
925
|
+
emit
|
|
926
|
+
}, o_evalActionParams(params, context, event, input));
|
|
927
|
+
applyUpdate();
|
|
928
|
+
}
|
|
929
|
+
} finally {
|
|
930
|
+
enqActions = [];
|
|
931
|
+
isProcessingQueue = false;
|
|
932
|
+
}
|
|
933
|
+
},
|
|
934
|
+
getResult() {
|
|
935
|
+
return { context };
|
|
936
|
+
}
|
|
937
|
+
};
|
|
938
|
+
}
|
|
939
|
+
function o_readyToFindInternalEventTransition(fnBaseParam, machineGuards, internalEPayload) {
|
|
940
|
+
const _evalGuard = readyToEvalGuard(fnBaseParam, machineGuards);
|
|
941
|
+
return ({ internalEvents }, eType) => {
|
|
942
|
+
const internalEType = eType;
|
|
943
|
+
if (Object.hasOwn(internalEvents, internalEType)) {
|
|
944
|
+
const cond = internalEvents[internalEType].cond;
|
|
945
|
+
if (!cond || cond(internalEPayload)) {
|
|
946
|
+
const { transitions } = internalEvents[internalEType];
|
|
947
|
+
const tIndex = transitions.findIndex(({ guards }) => !guards.length || guards.every((e) => _evalGuard(e)));
|
|
948
|
+
if (tIndex != -1) return {
|
|
949
|
+
tIndex,
|
|
950
|
+
transition: transitions[tIndex]
|
|
951
|
+
};
|
|
952
|
+
}
|
|
953
|
+
}
|
|
954
|
+
};
|
|
955
|
+
}
|
|
956
|
+
function o_readyToFindTransitionByEvent(fnBaseParam, machineGuards) {
|
|
957
|
+
const _evalGuard = readyToEvalGuard(fnBaseParam, machineGuards);
|
|
958
|
+
return ({ events }, eType) => {
|
|
959
|
+
if (Object.hasOwn(events, eType)) {
|
|
960
|
+
const transitions = events[eType];
|
|
961
|
+
const tIndex = transitions.findIndex(({ guards }) => !guards.length || guards.every((e) => _evalGuard(e)));
|
|
962
|
+
if (tIndex != -1) return {
|
|
963
|
+
tIndex,
|
|
964
|
+
transition: transitions[tIndex]
|
|
965
|
+
};
|
|
966
|
+
}
|
|
967
|
+
};
|
|
968
|
+
}
|
|
969
|
+
function deriveEventTransition(eType, internalEPayload, fnBaseParam, crrStates, machineGuards, stateCatalog, topLvDefItem) {
|
|
970
|
+
const findTransition = internalEPayload ? o_readyToFindInternalEventTransition(fnBaseParam, machineGuards, internalEPayload) : o_readyToFindTransitionByEvent(fnBaseParam, machineGuards);
|
|
971
|
+
const activeParallelSet = /* @__PURE__ */ new Set();
|
|
972
|
+
const [tCandidates, cLeaves] = x_findDeepestTransitions(crrStates, 0, "", activeParallelSet, stateCatalog, findTransition, eType);
|
|
973
|
+
if (!tCandidates.length) {
|
|
974
|
+
const result = findTransition(topLvDefItem, eType);
|
|
975
|
+
if (result) {
|
|
976
|
+
const { transition, tIndex } = result;
|
|
977
|
+
tCandidates.push(buildTransitionCandidate(transition, tIndex, "/", cLeaves));
|
|
978
|
+
}
|
|
979
|
+
}
|
|
980
|
+
return x_removeConflictedTransitions(tCandidates, [...activeParallelSet].sort((a, b) => a > b ? -1 : 1));
|
|
981
|
+
}
|
|
982
|
+
function* o_genExitPaths(parent, stateRoute, idx, asRootpath = false) {
|
|
983
|
+
if (idx >= stateRoute.length) return;
|
|
984
|
+
const el = stateRoute[idx];
|
|
985
|
+
if (typeof el === "string") {
|
|
986
|
+
const pth = `${parent}/${el}`;
|
|
987
|
+
yield* o_genExitPaths(pth, stateRoute, idx + 1);
|
|
988
|
+
yield pth;
|
|
989
|
+
return;
|
|
990
|
+
}
|
|
991
|
+
const { parallel, regions } = el;
|
|
992
|
+
const rootPath = asRootpath ? parent : `${parent}/${parallel}`;
|
|
993
|
+
for (const regionName in regions) {
|
|
994
|
+
const pth = `${rootPath}/${regionName}`;
|
|
995
|
+
yield* o_genExitPaths(pth, regions[regionName], 0);
|
|
996
|
+
yield pth;
|
|
997
|
+
}
|
|
998
|
+
if (!asRootpath) yield rootPath;
|
|
999
|
+
}
|
|
1000
|
+
function* genExitPathsAndDeleteFromCrrStates(crrStates, lca, fullTo = ":DUMMY:") {
|
|
1001
|
+
const { remaining, isExhausted } = traverseStateRouteMatch(crrStates, splitStPath(lca));
|
|
1002
|
+
if (!isExhausted) throw new Error(`During transition to "${fullTo}", state "${lca}" is not found in ${JSON.stringify(crrStates)}`);
|
|
1003
|
+
const [stateRoute, startIdx, asRootpath] = remaining;
|
|
1004
|
+
yield* o_genExitPaths(lca, stateRoute, startIdx, asRootpath);
|
|
1005
|
+
if (!asRootpath) stateRoute.splice(startIdx);
|
|
1006
|
+
else if (typeof stateRoute[startIdx] === "object") stateRoute[startIdx].regions = {};
|
|
1007
|
+
else throw new Error(`Internal error: unexpected stateRoute: ${JSON.stringify(stateRoute)}, lca:${lca}`);
|
|
1008
|
+
}
|
|
1009
|
+
function oo_addSubStatesToDest(destinations, { initial, stateOf }) {
|
|
1010
|
+
if (!initial || initial in destinations) return destinations;
|
|
1011
|
+
destinations[initial] = Object.create(null);
|
|
1012
|
+
const { sub } = stateOf[initial];
|
|
1013
|
+
if (!sub) return destinations;
|
|
1014
|
+
else if (sub.dType === "regions") oo_addParallelToDestTree(destinations[initial], sub.stateOf);
|
|
1015
|
+
else oo_addSubStatesToDest(destinations[initial], sub);
|
|
1016
|
+
return destinations;
|
|
1017
|
+
}
|
|
1018
|
+
function oo_addParallelToDestTree(destinations, regionStItemOf, routeRegions) {
|
|
1019
|
+
for (const key in regionStItemOf) {
|
|
1020
|
+
if (key in destinations || routeRegions?.[key]?.length) continue;
|
|
1021
|
+
destinations[key] = Object.create(null);
|
|
1022
|
+
oo_addSubStatesToDest(destinations[key], regionStItemOf[key].sub);
|
|
1023
|
+
}
|
|
1024
|
+
}
|
|
1025
|
+
function* o_genStateEntryData(transitionDest, target, rIdx, stateDefinition) {
|
|
1026
|
+
const { stateOf } = stateDefinition;
|
|
1027
|
+
let isLeaf = true;
|
|
1028
|
+
for (const dKey in transitionDest) {
|
|
1029
|
+
isLeaf = false;
|
|
1030
|
+
const stDef = stateOf?.[dKey];
|
|
1031
|
+
if (!stDef) throw new Error(`Unknown state "${dKey}"`);
|
|
1032
|
+
const { def, sub } = stDef;
|
|
1033
|
+
const isParallel = sub?.dType === "regions";
|
|
1034
|
+
const childTree = transitionDest[dKey];
|
|
1035
|
+
if (rIdx >= 0) {
|
|
1036
|
+
if (isParallel) {
|
|
1037
|
+
const { route } = target;
|
|
1038
|
+
const el = route[rIdx];
|
|
1039
|
+
if (typeof el === "object" && "parallel" in el && el.parallel === dKey) {
|
|
1040
|
+
oo_addParallelToDestTree(childTree, sub.stateOf, el.regions);
|
|
1041
|
+
yield* o_genStateEntryData(childTree, {
|
|
1042
|
+
isRegions: true,
|
|
1043
|
+
regions: el.regions
|
|
1044
|
+
}, rIdx, sub);
|
|
1045
|
+
continue;
|
|
1046
|
+
}
|
|
1047
|
+
} else if (target.isRegions) {
|
|
1048
|
+
const { regions } = target;
|
|
1049
|
+
if (dKey in regions) {
|
|
1050
|
+
if (sub) yield* o_genStateEntryData(childTree, {
|
|
1051
|
+
isRegions: false,
|
|
1052
|
+
route: regions[dKey],
|
|
1053
|
+
isRegionChild: true
|
|
1054
|
+
}, 0, sub);
|
|
1055
|
+
continue;
|
|
1056
|
+
}
|
|
1057
|
+
} else {
|
|
1058
|
+
const { route } = target;
|
|
1059
|
+
if (route[rIdx]?.valueOf() === dKey) {
|
|
1060
|
+
if (sub) yield* o_genStateEntryData(childTree, {
|
|
1061
|
+
isRegions: false,
|
|
1062
|
+
route,
|
|
1063
|
+
isRegionChild: false
|
|
1064
|
+
}, rIdx + 1, sub);
|
|
1065
|
+
continue;
|
|
1066
|
+
}
|
|
1067
|
+
}
|
|
1068
|
+
}
|
|
1069
|
+
if (isParallel) {
|
|
1070
|
+
const regions = Object.create(null);
|
|
1071
|
+
const el = {
|
|
1072
|
+
parallel: dKey,
|
|
1073
|
+
regions
|
|
1074
|
+
};
|
|
1075
|
+
const { route, isRegionChild } = target;
|
|
1076
|
+
route.push(el);
|
|
1077
|
+
yield [def, {
|
|
1078
|
+
stateName: dKey,
|
|
1079
|
+
isRegionChild
|
|
1080
|
+
}];
|
|
1081
|
+
oo_addParallelToDestTree(childTree, sub.stateOf);
|
|
1082
|
+
yield* o_genStateEntryData(childTree, {
|
|
1083
|
+
isRegions: true,
|
|
1084
|
+
regions
|
|
1085
|
+
}, -1, sub);
|
|
1086
|
+
} else if (target.isRegions) {
|
|
1087
|
+
const route = [];
|
|
1088
|
+
target.regions[dKey] = route;
|
|
1089
|
+
yield [def, { stateName: dKey }];
|
|
1090
|
+
if (sub) yield* o_genStateEntryData(childTree, {
|
|
1091
|
+
isRegions: false,
|
|
1092
|
+
route,
|
|
1093
|
+
isRegionChild: true
|
|
1094
|
+
}, -1, sub);
|
|
1095
|
+
} else {
|
|
1096
|
+
const { route, isRegionChild } = target;
|
|
1097
|
+
route.push(dKey);
|
|
1098
|
+
yield [def, {
|
|
1099
|
+
stateName: dKey,
|
|
1100
|
+
isRegionChild
|
|
1101
|
+
}];
|
|
1102
|
+
if (sub) yield* o_genStateEntryData(childTree, {
|
|
1103
|
+
isRegions: false,
|
|
1104
|
+
route,
|
|
1105
|
+
isRegionChild: false
|
|
1106
|
+
}, -1, sub);
|
|
1107
|
+
}
|
|
1108
|
+
}
|
|
1109
|
+
if (isLeaf && "initial" in stateDefinition && stateDefinition.initial) yield* o_genStateEntryData(oo_addSubStatesToDest(Object.create(null), stateDefinition), target, -1, stateDefinition);
|
|
1110
|
+
}
|
|
1111
|
+
function* updateStateRouteForEntry(fullToSplittees, crrStates, stateDefinition) {
|
|
1112
|
+
yield* o_genStateEntryData((() => {
|
|
1113
|
+
const root = Object.create(null);
|
|
1114
|
+
for (const stateNames of fullToSplittees) {
|
|
1115
|
+
let focused = root;
|
|
1116
|
+
for (const stName of stateNames) {
|
|
1117
|
+
focused[stName] = focused[stName] || Object.create(null);
|
|
1118
|
+
focused = focused[stName];
|
|
1119
|
+
}
|
|
1120
|
+
}
|
|
1121
|
+
return root;
|
|
1122
|
+
})(), {
|
|
1123
|
+
isRegions: false,
|
|
1124
|
+
route: crrStates,
|
|
1125
|
+
isRegionChild: false
|
|
1126
|
+
}, 0, stateDefinition);
|
|
1127
|
+
}
|
|
1128
|
+
function launchInvocations(invocations, path, makeFnBaseParam, machineServices, rtOutbound) {
|
|
1129
|
+
const { enqueueEvent } = rtOutbound;
|
|
1130
|
+
const fnBaseParam = makeFnBaseParam();
|
|
1131
|
+
for (const [i, { input, mainKey, subKey, src, emittableETypesToListen }] of invocations.entries()) {
|
|
1132
|
+
const [srcType, srcValue] = (() => {
|
|
1133
|
+
const { type, value } = src;
|
|
1134
|
+
if (type !== "string") return [type, value];
|
|
1135
|
+
const service = machineServices[value];
|
|
1136
|
+
if (!service) throw new Error(`RT"${rtOutbound.id}"> invoke: Unknown src: "${value}"`);
|
|
1137
|
+
return [typeof service === "function" ? "function" : "machine", service];
|
|
1138
|
+
})();
|
|
1139
|
+
const childKey = subKey ? `${mainKey}:${subKey}` : mainKey;
|
|
1140
|
+
if (rtOutbound.children[childKey]) throw new Error(`RT"${rtOutbound.id}"> Service with ID '${childKey}' is already running.`);
|
|
1141
|
+
const onDone = (output) => enqueueEvent({
|
|
1142
|
+
type: `invoke/:done:[${i}]`,
|
|
1143
|
+
payload: {
|
|
1144
|
+
type: ":done:",
|
|
1145
|
+
output
|
|
1146
|
+
},
|
|
1147
|
+
[SystemEventSymbol]: true
|
|
1148
|
+
}, { path });
|
|
1149
|
+
const onError = (error) => {
|
|
1150
|
+
enqueueEvent({
|
|
1151
|
+
type: `invoke/:error:[${i}]`,
|
|
1152
|
+
payload: {
|
|
1153
|
+
type: ":error:",
|
|
1154
|
+
error
|
|
1155
|
+
},
|
|
1156
|
+
[SystemEventSymbol]: true
|
|
1157
|
+
}, { path });
|
|
1158
|
+
};
|
|
1159
|
+
const inputValue = input && typeof input === "function" ? input(fnBaseParam) : void 0;
|
|
1160
|
+
switch (srcType) {
|
|
1161
|
+
case "machine": {
|
|
1162
|
+
const childRt = makeRuntime(srcValue, {
|
|
1163
|
+
id: `${rtOutbound.id}/${childKey}`,
|
|
1164
|
+
input: () => inputValue
|
|
1165
|
+
});
|
|
1166
|
+
rtOutbound.children = {
|
|
1167
|
+
...rtOutbound.children,
|
|
1168
|
+
[childKey]: childRt
|
|
1169
|
+
};
|
|
1170
|
+
childRt.subscribe(({ runtimeStatus, output }) => {
|
|
1171
|
+
if (runtimeStatus === "done") onDone(output);
|
|
1172
|
+
}, onError);
|
|
1173
|
+
for (const eType of emittableETypesToListen) childRt.onEmitted(eType, (emittedEvent) => {
|
|
1174
|
+
enqueueEvent({
|
|
1175
|
+
type: `invoke/${eType}[${i}]`,
|
|
1176
|
+
payload: emittedEvent,
|
|
1177
|
+
[SystemEventSymbol]: true
|
|
1178
|
+
}, { path });
|
|
1179
|
+
});
|
|
1180
|
+
childRt.start();
|
|
1181
|
+
break;
|
|
1182
|
+
}
|
|
1183
|
+
case "function": {
|
|
1184
|
+
const abortController = new AbortController();
|
|
1185
|
+
const { signal } = abortController;
|
|
1186
|
+
const emit = (emittedEvent) => {
|
|
1187
|
+
if (!(emittedEvent && typeof emittedEvent === "object" && emittableETypesToListen.includes(emittedEvent?.type))) throw new Error(`RT"${rtOutbound.id}"> Invalid emittedEvent: ${JSON.stringify(emittedEvent)}`);
|
|
1188
|
+
enqueueEvent({
|
|
1189
|
+
type: `invoke/${emittedEvent?.type}[${i}]`,
|
|
1190
|
+
payload: emittedEvent,
|
|
1191
|
+
[SystemEventSymbol]: true
|
|
1192
|
+
}, { path });
|
|
1193
|
+
};
|
|
1194
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
1195
|
+
const listen = (listener) => {
|
|
1196
|
+
listeners.add(listener);
|
|
1197
|
+
};
|
|
1198
|
+
const child = {
|
|
1199
|
+
send(event) {
|
|
1200
|
+
listeners.forEach((listener) => listener(event));
|
|
1201
|
+
},
|
|
1202
|
+
stop() {
|
|
1203
|
+
abortController.abort();
|
|
1204
|
+
listeners.clear();
|
|
1205
|
+
}
|
|
1206
|
+
};
|
|
1207
|
+
rtOutbound.children = {
|
|
1208
|
+
...rtOutbound.children,
|
|
1209
|
+
[childKey]: child
|
|
1210
|
+
};
|
|
1211
|
+
const result = (() => {
|
|
1212
|
+
try {
|
|
1213
|
+
return srcValue(inputValue, emit, listen, signal);
|
|
1214
|
+
} catch (error) {
|
|
1215
|
+
if (!signal.aborted) onError(error);
|
|
1216
|
+
}
|
|
1217
|
+
})();
|
|
1218
|
+
const _handleOutput = (output) => {
|
|
1219
|
+
if (typeof output === "function") {
|
|
1220
|
+
if (signal.aborted) output();
|
|
1221
|
+
else child.stop = () => {
|
|
1222
|
+
abortController.abort();
|
|
1223
|
+
listeners.clear();
|
|
1224
|
+
output();
|
|
1225
|
+
};
|
|
1226
|
+
} else if (!signal.aborted) onDone(output);
|
|
1227
|
+
};
|
|
1228
|
+
if (!(result instanceof Promise)) {
|
|
1229
|
+
_handleOutput(result);
|
|
1230
|
+
return;
|
|
1231
|
+
}
|
|
1232
|
+
result.then((output) => _handleOutput(output)).catch((error) => {
|
|
1233
|
+
if (!signal.aborted) onError(error);
|
|
1234
|
+
});
|
|
1235
|
+
break;
|
|
1236
|
+
}
|
|
1237
|
+
}
|
|
1238
|
+
}
|
|
1239
|
+
}
|
|
1240
|
+
function stopInvocations(invocations, rtOutbound) {
|
|
1241
|
+
const newChildren = { ...rtOutbound.children };
|
|
1242
|
+
for (const { mainKey, subKey } of invocations) {
|
|
1243
|
+
const key = subKey ? `${mainKey}:${subKey}` : mainKey;
|
|
1244
|
+
newChildren[key].stop();
|
|
1245
|
+
delete newChildren[key];
|
|
1246
|
+
}
|
|
1247
|
+
rtOutbound.children = newChildren;
|
|
1248
|
+
}
|
|
1249
|
+
function o_gatherAllChildrenOutputs(regions, parent, fnBaseParams, stateCatalog) {
|
|
1250
|
+
const output = {};
|
|
1251
|
+
for (const key in regions) {
|
|
1252
|
+
const regionChild = regions[key][0];
|
|
1253
|
+
if (!regionChild) continue;
|
|
1254
|
+
else if (typeof regionChild === "object") {
|
|
1255
|
+
const result = regionChild.output;
|
|
1256
|
+
if (!result) return null;
|
|
1257
|
+
output[key] = result;
|
|
1258
|
+
continue;
|
|
1259
|
+
}
|
|
1260
|
+
const { final } = x_getStItemDef(`${parent}/${key}/${regionChild}`, stateCatalog);
|
|
1261
|
+
if (!final) return null;
|
|
1262
|
+
if (typeof final === "function") output[key] = final(fnBaseParams);
|
|
1263
|
+
}
|
|
1264
|
+
return output;
|
|
1265
|
+
}
|
|
1266
|
+
function* genChildDoneEvents(reachedFinalStateData = [], fnBaseParams, crrStates, stateCatalog) {
|
|
1267
|
+
const accParallelRoots = /* @__PURE__ */ new Set();
|
|
1268
|
+
for (const [pPath, isRegionChild, final] of reachedFinalStateData) {
|
|
1269
|
+
if (!pPath) continue;
|
|
1270
|
+
if (isRegionChild) accParallelRoots.add(parentPathOf(pPath));
|
|
1271
|
+
else yield [{
|
|
1272
|
+
type: ":childDone:",
|
|
1273
|
+
output: typeof final === "function" ? final(fnBaseParams) : void 0
|
|
1274
|
+
}, { path: pPath }];
|
|
1275
|
+
}
|
|
1276
|
+
{
|
|
1277
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1278
|
+
for (const parallelRoot of accParallelRoots) {
|
|
1279
|
+
let focusedPath = parentPathOf(parallelRoot);
|
|
1280
|
+
while (focusedPath) {
|
|
1281
|
+
if (seen.has(focusedPath)) break;
|
|
1282
|
+
seen.add(focusedPath);
|
|
1283
|
+
const item = stateCatalog.get(focusedPath);
|
|
1284
|
+
const pPath = item.def.pPath;
|
|
1285
|
+
if (item.sub?.dType === "regions") {
|
|
1286
|
+
accParallelRoots.add(focusedPath);
|
|
1287
|
+
focusedPath = pPath;
|
|
1288
|
+
continue;
|
|
1289
|
+
} else if (!pPath) break;
|
|
1290
|
+
const pItem = stateCatalog.get(pPath);
|
|
1291
|
+
if (pItem.sub?.dType !== "regions") break;
|
|
1292
|
+
accParallelRoots.add(pPath);
|
|
1293
|
+
focusedPath = pItem.def.pPath;
|
|
1294
|
+
}
|
|
1295
|
+
}
|
|
1296
|
+
}
|
|
1297
|
+
const parallelRoots = [...accParallelRoots].sort((a, b) => a > b ? -1 : 1);
|
|
1298
|
+
const last = parallelRoots.length - 1;
|
|
1299
|
+
for (const [i, parallelRoot] of parallelRoots.entries()) {
|
|
1300
|
+
const def = stateCatalog.get(parallelRoot)?.def;
|
|
1301
|
+
if (i >= last && !def?.internalEvents[":childDone:"]?.transitions.length) break;
|
|
1302
|
+
const { remaining, isExhausted } = traverseStateRouteMatch(crrStates, splitStPath(parallelRoot));
|
|
1303
|
+
if (!isExhausted) throw new Error(`Internal error: state "${parallelRoot}" is not found in current states.`);
|
|
1304
|
+
const [route, idx] = remaining;
|
|
1305
|
+
const parallelRouteEl = route[idx];
|
|
1306
|
+
const output = o_gatherAllChildrenOutputs(parallelRouteEl.regions, parallelRoot, fnBaseParams, stateCatalog);
|
|
1307
|
+
if (output) {
|
|
1308
|
+
parallelRouteEl.output = output;
|
|
1309
|
+
yield [{
|
|
1310
|
+
type: ":childDone:",
|
|
1311
|
+
output
|
|
1312
|
+
}, { path: parallelRoot }];
|
|
1313
|
+
}
|
|
1314
|
+
}
|
|
1315
|
+
}
|
|
1316
|
+
function o_readyToFindAlwaysTransition(fnBaseParam, machineGuards) {
|
|
1317
|
+
const _evalGuard = readyToEvalGuard(fnBaseParam, machineGuards);
|
|
1318
|
+
return ({ internalEvents }) => {
|
|
1319
|
+
const transitions = internalEvents[":always:"]?.transitions;
|
|
1320
|
+
if (transitions) {
|
|
1321
|
+
const tIndex = transitions.findIndex(({ guards }) => !guards.length || guards.every((e) => _evalGuard(e)));
|
|
1322
|
+
if (tIndex != -1) return {
|
|
1323
|
+
tIndex,
|
|
1324
|
+
transition: transitions[tIndex]
|
|
1325
|
+
};
|
|
1326
|
+
}
|
|
1327
|
+
};
|
|
1328
|
+
}
|
|
1329
|
+
function evalAlwaysEvent(fnBaseParam, crrStates, machineGuards, stateCatalog) {
|
|
1330
|
+
const findTransition = o_readyToFindAlwaysTransition(fnBaseParam, machineGuards);
|
|
1331
|
+
const activeParallelSet = /* @__PURE__ */ new Set();
|
|
1332
|
+
const [tCandidates] = x_findDeepestTransitions(crrStates, 0, "", activeParallelSet, stateCatalog, findTransition);
|
|
1333
|
+
return x_removeConflictedTransitions(tCandidates, [...activeParallelSet].sort((a, b) => a > b ? -1 : 1));
|
|
1334
|
+
}
|
|
1335
|
+
//#endregion
|
|
1336
|
+
//#region src/runtime/runtime_step.ts
|
|
1337
|
+
function execTExecActions(tExecs, actionRunner, errLocation, internalQueueEvent) {
|
|
1338
|
+
const { execAction, processQueue } = actionRunner;
|
|
1339
|
+
let i, tExec, action;
|
|
1340
|
+
try {
|
|
1341
|
+
for (tExec of tExecs) for ([i, action] of tExec.transition.actions.entries()) {
|
|
1342
|
+
execAction(action, internalQueueEvent);
|
|
1343
|
+
processQueue();
|
|
1344
|
+
}
|
|
1345
|
+
} catch (err) {
|
|
1346
|
+
const { lca, tIndex } = tExec;
|
|
1347
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
1348
|
+
console.error(`small-state: ${msg}`, {
|
|
1349
|
+
state: lca,
|
|
1350
|
+
location: `${errLocation}[${tIndex}], actions[${i}]`
|
|
1351
|
+
});
|
|
1352
|
+
throw err;
|
|
1353
|
+
}
|
|
1354
|
+
}
|
|
1355
|
+
function initRuntime(realContext, input, crrStates, machineInternal, rtOutbound, event = { type: ":init:" }) {
|
|
1356
|
+
const { definition, guards, actions } = machineInternal;
|
|
1357
|
+
const { initial } = definition;
|
|
1358
|
+
const actionRunner = readyAction(realContext, event, input, guards, actions, rtOutbound);
|
|
1359
|
+
const [hasTransitioned, doneEvent] = runMicrosteps([{
|
|
1360
|
+
transition: {
|
|
1361
|
+
to: initial,
|
|
1362
|
+
fullTo: `/${initial}`,
|
|
1363
|
+
guards: [],
|
|
1364
|
+
actions: []
|
|
1365
|
+
},
|
|
1366
|
+
tIndex: -1
|
|
1367
|
+
}], crrStates, actionRunner, machineInternal, rtOutbound);
|
|
1368
|
+
{
|
|
1369
|
+
const { context } = actionRunner.getResult();
|
|
1370
|
+
return {
|
|
1371
|
+
context,
|
|
1372
|
+
crrStates,
|
|
1373
|
+
hasTransitioned,
|
|
1374
|
+
doneEvent
|
|
1375
|
+
};
|
|
1376
|
+
}
|
|
1377
|
+
}
|
|
1378
|
+
function macrostep(eType, internalEPayload, crrStates, actionRunner, machineInternal, rtOutbound) {
|
|
1379
|
+
const { guards, stateCatalog, topLvDefItem } = machineInternal;
|
|
1380
|
+
const tExecs = deriveEventTransition(eType, internalEPayload, actionRunner.makeFnBaseParam(), crrStates, guards, stateCatalog, topLvDefItem);
|
|
1381
|
+
execTExecActions(tExecs, actionRunner, "transition");
|
|
1382
|
+
const [hasTransitioned, doneEvent] = runMicrosteps(tExecs, crrStates, actionRunner, machineInternal, rtOutbound);
|
|
1383
|
+
{
|
|
1384
|
+
const { context } = actionRunner.getResult();
|
|
1385
|
+
return {
|
|
1386
|
+
context,
|
|
1387
|
+
crrStates,
|
|
1388
|
+
hasTransitioned,
|
|
1389
|
+
doneEvent
|
|
1390
|
+
};
|
|
1391
|
+
}
|
|
1392
|
+
}
|
|
1393
|
+
function runMicrosteps(tExecs, crrStates, actionRunner, machineInternal, rtOutbound) {
|
|
1394
|
+
const internalQueue = [];
|
|
1395
|
+
let [hasTransitioned, doneEvent] = runExitAndEntryThenAlways(tExecs, crrStates, actionRunner, machineInternal, rtOutbound, internalQueue);
|
|
1396
|
+
const { guards, stateCatalog, topLvDefItem } = machineInternal;
|
|
1397
|
+
const { makeFnBaseParam } = actionRunner;
|
|
1398
|
+
while (internalQueue.length) {
|
|
1399
|
+
const [event, payload] = internalQueue.shift();
|
|
1400
|
+
const internalTExecs = deriveEventTransition(event.type, payload, makeFnBaseParam(), crrStates, guards, stateCatalog, topLvDefItem);
|
|
1401
|
+
execTExecActions(internalTExecs, actionRunner, event.type, event);
|
|
1402
|
+
[hasTransitioned, doneEvent] = runExitAndEntryThenAlways(internalTExecs, crrStates, actionRunner, machineInternal, rtOutbound, internalQueue) || hasTransitioned;
|
|
1403
|
+
}
|
|
1404
|
+
return [hasTransitioned, doneEvent];
|
|
1405
|
+
}
|
|
1406
|
+
function runExitAndEntryThenAlways(tExecs, crrStates, actionRunner, machineInternal, rtOutbound, internalQueue) {
|
|
1407
|
+
let [alwaysTExecs, hasTransitioned, doneEvent] = exitAndEntry(tExecs, crrStates, actionRunner, machineInternal, rtOutbound, internalQueue);
|
|
1408
|
+
let i = 0;
|
|
1409
|
+
while (alwaysTExecs.length) {
|
|
1410
|
+
let tmp;
|
|
1411
|
+
[alwaysTExecs, tmp, doneEvent] = exitAndEntry(alwaysTExecs, crrStates, actionRunner, machineInternal, rtOutbound, internalQueue);
|
|
1412
|
+
hasTransitioned ||= tmp;
|
|
1413
|
+
if (++i > 1e3) throw new Error(`RT"${rtOutbound.id}"> Maximum number of microsteps exceeded.`);
|
|
1414
|
+
}
|
|
1415
|
+
return [hasTransitioned, doneEvent];
|
|
1416
|
+
}
|
|
1417
|
+
function exitAndEntry(tExecs, crrStates, actionRunner, { guards, services, stateCatalog, definition }, rtOutbound, internalQueue) {
|
|
1418
|
+
let doneEvent;
|
|
1419
|
+
const { makeFnBaseParam, execAction, processQueue } = actionRunner;
|
|
1420
|
+
for (const { lca, transition } of tExecs) {
|
|
1421
|
+
if (lca === void 0) continue;
|
|
1422
|
+
{
|
|
1423
|
+
const exitPaths = genExitPathsAndDeleteFromCrrStates(crrStates, lca, transition.fullTo);
|
|
1424
|
+
let i, action;
|
|
1425
|
+
try {
|
|
1426
|
+
for (const pth of exitPaths) {
|
|
1427
|
+
const { def } = stateCatalog.get(pth);
|
|
1428
|
+
const { invocations, exitHooks } = def;
|
|
1429
|
+
if (exitHooks) for ([i, action] of exitHooks.entries()) {
|
|
1430
|
+
execAction(action);
|
|
1431
|
+
processQueue();
|
|
1432
|
+
}
|
|
1433
|
+
if (invocations.length) stopInvocations(invocations, rtOutbound);
|
|
1434
|
+
}
|
|
1435
|
+
} catch (err) {
|
|
1436
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
1437
|
+
console.error(`small-state: ${msg}`, {
|
|
1438
|
+
runtimeId: rtOutbound.id,
|
|
1439
|
+
state: lca,
|
|
1440
|
+
location: `exitHooks[${i}]`
|
|
1441
|
+
});
|
|
1442
|
+
throw err;
|
|
1443
|
+
}
|
|
1444
|
+
}
|
|
1445
|
+
}
|
|
1446
|
+
const stChangingTExecs = tExecs.filter(({ transition: { fullTo } }) => fullTo);
|
|
1447
|
+
if (stChangingTExecs.length) {
|
|
1448
|
+
const accReachedFinal = [];
|
|
1449
|
+
const fullToSplittees = stChangingTExecs.map(({ transition }) => splitStPath(transition.fullTo));
|
|
1450
|
+
const enteredStItemDefs = updateStateRouteForEntry(fullToSplittees, crrStates, definition);
|
|
1451
|
+
let i = 0, j, action;
|
|
1452
|
+
try {
|
|
1453
|
+
for (const [def, additional] of enteredStItemDefs) {
|
|
1454
|
+
const { invocations, entryHooks, final, pPath } = def;
|
|
1455
|
+
if (entryHooks) for ([j, action] of entryHooks.entries()) {
|
|
1456
|
+
execAction(action);
|
|
1457
|
+
processQueue();
|
|
1458
|
+
}
|
|
1459
|
+
if (invocations.length) launchInvocations(invocations, `${pPath}/${additional.stateName}`, makeFnBaseParam, services, rtOutbound);
|
|
1460
|
+
if (final) {
|
|
1461
|
+
if (!pPath) doneEvent = {
|
|
1462
|
+
type: ":done:",
|
|
1463
|
+
output: typeof final === "function" ? final(makeFnBaseParam()) : void 0
|
|
1464
|
+
};
|
|
1465
|
+
else accReachedFinal.push([
|
|
1466
|
+
pPath,
|
|
1467
|
+
additional.isRegionChild,
|
|
1468
|
+
final
|
|
1469
|
+
]);
|
|
1470
|
+
}
|
|
1471
|
+
i += 1;
|
|
1472
|
+
}
|
|
1473
|
+
} catch (err) {
|
|
1474
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
1475
|
+
const pth = "/" + fullToSplittees[i]?.join("/");
|
|
1476
|
+
console.error(`small-state: ${msg}`, {
|
|
1477
|
+
runtimeId: rtOutbound.id,
|
|
1478
|
+
state: pth,
|
|
1479
|
+
location: `entryHooks[${j}]`
|
|
1480
|
+
});
|
|
1481
|
+
throw err;
|
|
1482
|
+
}
|
|
1483
|
+
internalQueue.push(...genChildDoneEvents(accReachedFinal, makeFnBaseParam(), crrStates, stateCatalog));
|
|
1484
|
+
}
|
|
1485
|
+
const alwaysTExecs = evalAlwaysEvent(makeFnBaseParam(), crrStates, guards, stateCatalog);
|
|
1486
|
+
execTExecActions(alwaysTExecs, actionRunner, ":always:");
|
|
1487
|
+
return [
|
|
1488
|
+
alwaysTExecs,
|
|
1489
|
+
!!stChangingTExecs.length,
|
|
1490
|
+
doneEvent
|
|
1491
|
+
];
|
|
1492
|
+
}
|
|
1493
|
+
//#endregion
|
|
1494
|
+
//#region src/runtime/Runtime.ts
|
|
1495
|
+
function isRuntime(v) {
|
|
1496
|
+
return v instanceof Runtime;
|
|
1497
|
+
}
|
|
1498
|
+
function makeRuntime(machine, options) {
|
|
1499
|
+
return new Runtime(machine, options);
|
|
1500
|
+
}
|
|
1501
|
+
const STOP_EVENT = { type: "system/stop" };
|
|
1502
|
+
let idCounter = 0;
|
|
1503
|
+
var Runtime = class {
|
|
1504
|
+
id;
|
|
1505
|
+
machine;
|
|
1506
|
+
#machineInternal;
|
|
1507
|
+
#input;
|
|
1508
|
+
#realContext;
|
|
1509
|
+
#crrStates = [];
|
|
1510
|
+
#sentQueue = new MailBox(this.#processSentEvent.bind(this));
|
|
1511
|
+
#enqueueEvent = this.#sentQueue.enqueue.bind(this.#sentQueue);
|
|
1512
|
+
#outbound = {
|
|
1513
|
+
id: "",
|
|
1514
|
+
enqueueEvent: this.#enqueueEvent,
|
|
1515
|
+
emit: this.#_emit.bind(this),
|
|
1516
|
+
children: {}
|
|
1517
|
+
};
|
|
1518
|
+
#subscribers = /* @__PURE__ */ new Set();
|
|
1519
|
+
#emittedListeners = /* @__PURE__ */ new Map();
|
|
1520
|
+
#snapshot = {};
|
|
1521
|
+
get states() {
|
|
1522
|
+
return this.#snapshot.states;
|
|
1523
|
+
}
|
|
1524
|
+
get stateValue() {
|
|
1525
|
+
return this.#snapshot.stateValue;
|
|
1526
|
+
}
|
|
1527
|
+
get context() {
|
|
1528
|
+
return this.#snapshot.context;
|
|
1529
|
+
}
|
|
1530
|
+
get runtimeStatus() {
|
|
1531
|
+
return this.#snapshot.runtimeStatus;
|
|
1532
|
+
}
|
|
1533
|
+
get output() {
|
|
1534
|
+
return this.#snapshot.output;
|
|
1535
|
+
}
|
|
1536
|
+
getSnapshot() {
|
|
1537
|
+
return this.#snapshot;
|
|
1538
|
+
}
|
|
1539
|
+
constructor(machine, { id, input } = {}) {
|
|
1540
|
+
defineFixedProperty(this, "id", id || `${machine.name}:${idCounter++}`, { enumerable: true });
|
|
1541
|
+
defineFixedProperty(this, "machine", machine, { enumerable: true });
|
|
1542
|
+
this.#machineInternal = machine.internal;
|
|
1543
|
+
this.#input = input?.();
|
|
1544
|
+
this.#outbound.id = this.id;
|
|
1545
|
+
const { initContext } = machine.internal;
|
|
1546
|
+
const context = initContext ? initContext(this.#input) : null;
|
|
1547
|
+
this.#buildAndSetSnapshot(context, true, "notStarted")();
|
|
1548
|
+
}
|
|
1549
|
+
subscribe(onSnapshot, onError) {
|
|
1550
|
+
const subscriber = {
|
|
1551
|
+
onSnapshot,
|
|
1552
|
+
onError
|
|
1553
|
+
};
|
|
1554
|
+
this.#subscribers.add(subscriber);
|
|
1555
|
+
return () => {
|
|
1556
|
+
this.#subscribers.delete(subscriber);
|
|
1557
|
+
};
|
|
1558
|
+
}
|
|
1559
|
+
start() {
|
|
1560
|
+
if (this.runtimeStatus !== "notStarted") return this;
|
|
1561
|
+
const result = (() => {
|
|
1562
|
+
try {
|
|
1563
|
+
return initRuntime(this.#realContext, this.#input, this.#crrStates, this.#machineInternal, this.#outbound);
|
|
1564
|
+
} catch (err) {
|
|
1565
|
+
this.#handleError(err);
|
|
1566
|
+
}
|
|
1567
|
+
})();
|
|
1568
|
+
if (!result) return this;
|
|
1569
|
+
const { context, hasTransitioned, doneEvent } = result;
|
|
1570
|
+
this.#buildAndSetSnapshot(context, hasTransitioned, "active")(doneEvent);
|
|
1571
|
+
this.#sentQueue.start();
|
|
1572
|
+
return this;
|
|
1573
|
+
}
|
|
1574
|
+
send(event) {
|
|
1575
|
+
const eventObj = typeof event === "string" ? { type: event } : event;
|
|
1576
|
+
if (this.#snapshot.runtimeStatus !== "active") {
|
|
1577
|
+
console.warn(`RT"${this.id}"> Event "${eventObj.type}" was sent to non-active runtime: ${this.id}`);
|
|
1578
|
+
return this;
|
|
1579
|
+
}
|
|
1580
|
+
this.#enqueueEvent(eventObj);
|
|
1581
|
+
return this;
|
|
1582
|
+
}
|
|
1583
|
+
stop() {
|
|
1584
|
+
if (this.#snapshot.runtimeStatus !== "active") return this;
|
|
1585
|
+
this.#sentQueue.clear();
|
|
1586
|
+
this.#enqueueEvent(STOP_EVENT, {});
|
|
1587
|
+
return this;
|
|
1588
|
+
}
|
|
1589
|
+
onEmitted(emittedEventType, handler) {
|
|
1590
|
+
let listeners = this.#emittedListeners.get(emittedEventType);
|
|
1591
|
+
if (!listeners) {
|
|
1592
|
+
listeners = /* @__PURE__ */ new Set();
|
|
1593
|
+
this.#emittedListeners.set(emittedEventType, listeners);
|
|
1594
|
+
}
|
|
1595
|
+
const handlerWrapper = (emittedEvent) => handler(emittedEvent);
|
|
1596
|
+
listeners.add(handlerWrapper);
|
|
1597
|
+
return () => {
|
|
1598
|
+
listeners.delete(handlerWrapper);
|
|
1599
|
+
};
|
|
1600
|
+
}
|
|
1601
|
+
#processSentEvent(event, internalEPayload) {
|
|
1602
|
+
if (event === STOP_EVENT) {
|
|
1603
|
+
this.#dispose();
|
|
1604
|
+
return this.#buildAndSetSnapshot(this.#realContext, false, "stopped")();
|
|
1605
|
+
}
|
|
1606
|
+
const result = (() => {
|
|
1607
|
+
try {
|
|
1608
|
+
const { guards, actions } = this.#machineInternal;
|
|
1609
|
+
const actionRunner = readyAction(this.#realContext, event, this.#input, guards, actions, this.#outbound);
|
|
1610
|
+
return macrostep(event.type, internalEPayload, this.#crrStates, actionRunner, this.#machineInternal, this.#outbound);
|
|
1611
|
+
} catch (err) {
|
|
1612
|
+
this.#handleError(err);
|
|
1613
|
+
}
|
|
1614
|
+
})();
|
|
1615
|
+
if (!result) return;
|
|
1616
|
+
const { context, crrStates, hasTransitioned, doneEvent } = result;
|
|
1617
|
+
this.#crrStates = crrStates;
|
|
1618
|
+
this.#buildAndSetSnapshot(context, hasTransitioned)(doneEvent);
|
|
1619
|
+
}
|
|
1620
|
+
#dispose() {
|
|
1621
|
+
{
|
|
1622
|
+
const children = { ...this.#outbound.children };
|
|
1623
|
+
for (const id in children) {
|
|
1624
|
+
children[id].stop();
|
|
1625
|
+
delete children[id];
|
|
1626
|
+
}
|
|
1627
|
+
this.#outbound.children = children;
|
|
1628
|
+
}
|
|
1629
|
+
this.#sentQueue.clear();
|
|
1630
|
+
this.#subscribers.clear();
|
|
1631
|
+
this.#emittedListeners.clear();
|
|
1632
|
+
}
|
|
1633
|
+
#_emit(emittedEvent) {
|
|
1634
|
+
const listeners = this.#emittedListeners.get(emittedEvent.type);
|
|
1635
|
+
const wildcardListener = this.#emittedListeners.get("*");
|
|
1636
|
+
if (!(listeners || wildcardListener)) return;
|
|
1637
|
+
for (const handler of [...listeners || [], ...wildcardListener || []]) try {
|
|
1638
|
+
handler(emittedEvent);
|
|
1639
|
+
} catch (err) {
|
|
1640
|
+
reportUnhandledError(err);
|
|
1641
|
+
}
|
|
1642
|
+
}
|
|
1643
|
+
#buildAndSetSnapshot(newRealContext, hasTransitioned, runtimeStatus) {
|
|
1644
|
+
const oldSnapshot = this.#snapshot;
|
|
1645
|
+
const crrStates = this.#crrStates;
|
|
1646
|
+
const states = !hasTransitioned ? oldSnapshot.states : !crrStates.length ? [] : makeStateList(this.#crrStates);
|
|
1647
|
+
let { context } = oldSnapshot;
|
|
1648
|
+
const contextUpdated = newRealContext !== this.#realContext;
|
|
1649
|
+
if (contextUpdated) {
|
|
1650
|
+
this.#realContext = newRealContext;
|
|
1651
|
+
context = !newRealContext ? newRealContext : Object.entries(newRealContext).reduce((acc, [k, v]) => {
|
|
1652
|
+
if (!k.startsWith("_")) acc[k] = v;
|
|
1653
|
+
return acc;
|
|
1654
|
+
}, {});
|
|
1655
|
+
}
|
|
1656
|
+
return (doneEvent) => {
|
|
1657
|
+
if (!(hasTransitioned || contextUpdated || runtimeStatus)) return;
|
|
1658
|
+
const { stateCatalog, output } = this.#machineInternal;
|
|
1659
|
+
const isCompleted = !!(doneEvent || states.length && states.every((stPath) => stateCatalog.get(stPath)?.def.final));
|
|
1660
|
+
this.#snapshot = {
|
|
1661
|
+
runtimeId: this.id,
|
|
1662
|
+
states,
|
|
1663
|
+
get stateValue() {
|
|
1664
|
+
return states.length <= 1 ? states[0] : states;
|
|
1665
|
+
},
|
|
1666
|
+
context,
|
|
1667
|
+
c: context,
|
|
1668
|
+
runtimeStatus: isCompleted ? "done" : runtimeStatus || oldSnapshot.runtimeStatus,
|
|
1669
|
+
output: !(isCompleted && output) ? void 0 : output(runtimeUtils.makeFnBaseParam(newRealContext, doneEvent || {
|
|
1670
|
+
type: ":done:",
|
|
1671
|
+
output: void 0
|
|
1672
|
+
}, this.#input)),
|
|
1673
|
+
error: void 0,
|
|
1674
|
+
_children: this.#outbound.children,
|
|
1675
|
+
_runtime: this
|
|
1676
|
+
};
|
|
1677
|
+
Object.defineProperty(this.#snapshot, "_runtime", { enumerable: false });
|
|
1678
|
+
{
|
|
1679
|
+
const snapshot = this.#snapshot;
|
|
1680
|
+
for (const { onSnapshot } of this.#subscribers) {
|
|
1681
|
+
if (!onSnapshot) continue;
|
|
1682
|
+
try {
|
|
1683
|
+
onSnapshot(snapshot, oldSnapshot);
|
|
1684
|
+
} catch (err) {
|
|
1685
|
+
reportUnhandledError(err);
|
|
1686
|
+
}
|
|
1687
|
+
}
|
|
1688
|
+
}
|
|
1689
|
+
if (isCompleted) this.#dispose();
|
|
1690
|
+
};
|
|
1691
|
+
}
|
|
1692
|
+
#handleError(err) {
|
|
1693
|
+
this.#snapshot = {
|
|
1694
|
+
...this.#snapshot,
|
|
1695
|
+
runtimeStatus: "error",
|
|
1696
|
+
error: err
|
|
1697
|
+
};
|
|
1698
|
+
let shouldReport = !this.#subscribers.size;
|
|
1699
|
+
for (const { onError } of this.#subscribers) {
|
|
1700
|
+
shouldReport ||= !onError;
|
|
1701
|
+
try {
|
|
1702
|
+
onError?.(err);
|
|
1703
|
+
} catch (err2) {
|
|
1704
|
+
reportUnhandledError(err2);
|
|
1705
|
+
}
|
|
1706
|
+
}
|
|
1707
|
+
this.#dispose();
|
|
1708
|
+
if (shouldReport) reportUnhandledError(err);
|
|
1709
|
+
}
|
|
1710
|
+
};
|
|
1711
|
+
function makeStateList(stateRoute, idx = 0, parent = "") {
|
|
1712
|
+
if (idx >= stateRoute.length) return [parent];
|
|
1713
|
+
const el = stateRoute[idx];
|
|
1714
|
+
if (typeof el === "string") return makeStateList(stateRoute, idx + 1, `${parent}/${el}`);
|
|
1715
|
+
const { parallel, regions } = el;
|
|
1716
|
+
const ret = [];
|
|
1717
|
+
for (const key in regions) ret.push(...makeStateList(regions[key], 0, `${parent}/${parallel}/${key}`));
|
|
1718
|
+
return ret;
|
|
1719
|
+
}
|
|
1720
|
+
//#endregion
|
|
1721
|
+
//#region src/tools/waitFor.ts
|
|
1722
|
+
async function waitFor(runtime, cond, options) {
|
|
1723
|
+
if (runtime.runtimeStatus === "notStarted") throw new Error(`RT"${runtime.id}" is not started.`);
|
|
1724
|
+
return new Promise((resolve, reject) => {
|
|
1725
|
+
let done = false;
|
|
1726
|
+
let unsubscribe;
|
|
1727
|
+
const dispose = () => {
|
|
1728
|
+
clearTimeout(timeoutId);
|
|
1729
|
+
done = true;
|
|
1730
|
+
unsubscribe?.();
|
|
1731
|
+
};
|
|
1732
|
+
const check = (snapshot) => {
|
|
1733
|
+
if (!cond(snapshot)) return;
|
|
1734
|
+
dispose();
|
|
1735
|
+
resolve(snapshot);
|
|
1736
|
+
};
|
|
1737
|
+
const timeoutId = !options?.timeout ? void 0 : setTimeout(() => {
|
|
1738
|
+
dispose();
|
|
1739
|
+
reject(`waitFor: timeout of ${options.timeout} ms exceeded`);
|
|
1740
|
+
}, options.timeout);
|
|
1741
|
+
check(runtime.getSnapshot());
|
|
1742
|
+
if (done) return;
|
|
1743
|
+
unsubscribe = runtime.subscribe((snapshot) => {
|
|
1744
|
+
check(snapshot);
|
|
1745
|
+
});
|
|
1746
|
+
});
|
|
1747
|
+
}
|
|
1748
|
+
//#endregion
|
|
1749
|
+
exports.defServiceFn = defServiceFn;
|
|
1750
|
+
exports.isMachine = isMachine;
|
|
1751
|
+
exports.isRuntime = isRuntime;
|
|
1752
|
+
exports.makeMachine = makeMachine;
|
|
1753
|
+
exports.makeRuntime = makeRuntime;
|
|
1754
|
+
exports.waitFor = waitFor;
|