@typeonce/effect-machine 0.16.0 → 0.18.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/README.md +157 -150
- package/dist/Machine.d.ts +416 -592
- package/dist/Machine.d.ts.map +1 -1
- package/dist/Machine.js +55 -136
- package/dist/Machine.js.map +1 -1
- package/dist/internal/machine/atom.d.ts +7 -7
- package/dist/internal/machine/atom.d.ts.map +1 -1
- package/dist/internal/machine/atom.js.map +1 -1
- package/dist/internal/machine/cluster.d.ts +2 -2
- package/dist/internal/machine/cluster.d.ts.map +1 -1
- package/dist/internal/machine/cluster.js +6 -5
- package/dist/internal/machine/cluster.js.map +1 -1
- package/dist/internal/machine/executionPlan.d.ts.map +1 -1
- package/dist/internal/machine/executionPlan.js +10 -3
- package/dist/internal/machine/executionPlan.js.map +1 -1
- package/dist/internal/machine/invocation.d.ts.map +1 -1
- package/dist/internal/machine/invocation.js +1 -1
- package/dist/internal/machine/invocation.js.map +1 -1
- package/dist/internal/machine/machine.d.ts +8 -6
- package/dist/internal/machine/machine.d.ts.map +1 -1
- package/dist/internal/machine/machine.js +239 -35
- package/dist/internal/machine/machine.js.map +1 -1
- package/dist/internal/machine/planner.d.ts +19 -4
- package/dist/internal/machine/planner.d.ts.map +1 -1
- package/dist/internal/machine/planner.js +56 -20
- package/dist/internal/machine/planner.js.map +1 -1
- package/dist/internal/machine/stateDefinition.d.ts.map +1 -1
- package/dist/internal/machine/stateDefinition.js +14 -0
- package/dist/internal/machine/stateDefinition.js.map +1 -1
- package/dist/internal/machine/topology.d.ts +9 -0
- package/dist/internal/machine/topology.d.ts.map +1 -1
- package/dist/internal/machine/topology.js +16 -0
- package/dist/internal/machine/topology.js.map +1 -1
- package/dist/internal/testing/machine/finiteModel.d.ts.map +1 -1
- package/dist/internal/testing/machine/finiteModel.js +15 -20
- package/dist/internal/testing/machine/finiteModel.js.map +1 -1
- package/dist/internal/testing/machine/transitionCoverage.d.ts.map +1 -1
- package/dist/internal/testing/machine/transitionCoverage.js +2 -0
- package/dist/internal/testing/machine/transitionCoverage.js.map +1 -1
- package/dist/testing/MachineTest.d.ts +15 -15
- package/dist/testing/MachineTest.d.ts.map +1 -1
- package/dist/testing/MachineTest.js +5 -8
- package/dist/testing/MachineTest.js.map +1 -1
- package/dist/unstable/cluster/ClusterMachine.d.ts +2 -2
- package/dist/unstable/cluster/ClusterMachine.d.ts.map +1 -1
- package/dist/unstable/cluster/ClusterMachine.js.map +1 -1
- package/dist/unstable/reactivity/AtomMachine.d.ts +13 -13
- package/dist/unstable/reactivity/AtomMachine.d.ts.map +1 -1
- package/dist/unstable/reactivity/AtomMachine.js.map +1 -1
- package/docs/agent-guide.md +229 -193
- package/package.json +1 -1
- package/src/Machine.ts +1686 -2234
- package/src/internal/machine/atom.ts +20 -15
- package/src/internal/machine/cluster.ts +14 -9
- package/src/internal/machine/executionPlan.ts +8 -3
- package/src/internal/machine/invocation.ts +1 -1
- package/src/internal/machine/machine.ts +365 -48
- package/src/internal/machine/planner.ts +89 -27
- package/src/internal/machine/stateDefinition.ts +39 -0
- package/src/internal/machine/topology.ts +28 -0
- package/src/internal/testing/machine/exploration.ts +1 -1
- package/src/internal/testing/machine/finiteModel.ts +18 -21
- package/src/internal/testing/machine/trace.ts +1 -1
- package/src/internal/testing/machine/transitionCoverage.ts +2 -0
- package/src/internal/testing/machine/verification.ts +1 -1
- package/src/testing/MachineTest.ts +18 -15
- package/src/unstable/cluster/ClusterMachine.ts +8 -4
- package/src/unstable/reactivity/AtomMachine.ts +20 -13
|
@@ -16,8 +16,10 @@ import * as Topology from "./topology.js";
|
|
|
16
16
|
export { ChildAlreadyExistsError, InfiniteTransitionError, MachineSchemaDecodeError, MachineSchemaEncodeError, ProcessLocalError, StartupError, StoppedError } from "./errors.js";
|
|
17
17
|
export { ChildMachineLogicTypeId, InitialEventTypeId, SnapshotBuilderStateTypeId } from "./symbols.js";
|
|
18
18
|
const TypeId = "~effect/Machine";
|
|
19
|
+
const ParentTypeId = "~effect/Machine/Parent";
|
|
19
20
|
export const InvokeTypeId = Symbol.for("effect/Machine/Invoke");
|
|
20
21
|
export const TransitionTypeId = Symbol.for("effect/Machine/Transition");
|
|
22
|
+
const InvokeBuilderDescriptorTypeId = Symbol("effect/Machine/InvokeBuilderDescriptor");
|
|
21
23
|
const ChildMachineTypeId = "~effect/Machine/ChildMachine";
|
|
22
24
|
const Proto = {
|
|
23
25
|
...Inspectable.BaseProto,
|
|
@@ -29,14 +31,13 @@ const Proto = {
|
|
|
29
31
|
};
|
|
30
32
|
}
|
|
31
33
|
};
|
|
32
|
-
const makeBoundInvoke = (config) => config;
|
|
33
34
|
const makeWithHandlers = (self, handlers) => {
|
|
34
35
|
const machine = Object.create(Proto);
|
|
35
36
|
machine.states = self.states;
|
|
36
37
|
machine.events = self.events;
|
|
37
38
|
machine.internalEvents = self.internalEvents;
|
|
38
39
|
machine.emittedEvents = self.emittedEvents;
|
|
39
|
-
machine.
|
|
40
|
+
machine.parent = self.parent;
|
|
40
41
|
machine.input = self.input;
|
|
41
42
|
machine.id = self.id;
|
|
42
43
|
machine.initial = self.initial;
|
|
@@ -44,16 +45,134 @@ const makeWithHandlers = (self, handlers) => {
|
|
|
44
45
|
machine.stateNodes = self.stateNodes;
|
|
45
46
|
machine.makeTargetBuilder = self.makeTargetBuilder;
|
|
46
47
|
machine.handlers = handlers;
|
|
47
|
-
machine.invoke = makeBoundInvoke;
|
|
48
48
|
Protocol.copyProtocol(self, machine);
|
|
49
49
|
return machine;
|
|
50
50
|
};
|
|
51
|
+
const TransitionBuilderDescriptorTypeId = Symbol("effect/Machine/TransitionBuilderDescriptor");
|
|
52
|
+
const InitialBuilderDescriptorTypeId = Symbol("effect/Machine/InitialBuilderDescriptor");
|
|
53
|
+
const plainTargetSelection = (selection) => selection.kind === "none"
|
|
54
|
+
? Topology.noneTargetSelection
|
|
55
|
+
: Topology.makeTargetSelection(selection.kind, selection.path, selection.scope);
|
|
56
|
+
const transitionOptions = (options) => {
|
|
57
|
+
const configuration = typeof options === "object" && options !== null
|
|
58
|
+
? options
|
|
59
|
+
: {};
|
|
60
|
+
return {
|
|
61
|
+
reenter: configuration.reenter === true,
|
|
62
|
+
declinable: configuration.declinable === true
|
|
63
|
+
};
|
|
64
|
+
};
|
|
65
|
+
const makeDirectTransitionDescriptor = (selection, resolve, options) => {
|
|
66
|
+
const shared = {
|
|
67
|
+
[TransitionBuilderDescriptorTypeId]: TransitionBuilderDescriptorTypeId,
|
|
68
|
+
type: "direct",
|
|
69
|
+
selection: plainTargetSelection(selection),
|
|
70
|
+
...transitionOptions(options)
|
|
71
|
+
};
|
|
72
|
+
return resolve === undefined
|
|
73
|
+
? Object.freeze(shared)
|
|
74
|
+
: Object.freeze({ ...shared, resolve });
|
|
75
|
+
};
|
|
76
|
+
const decorateTransitionSelection = (selection) => Object.freeze({
|
|
77
|
+
...selection,
|
|
78
|
+
resolve: (resolve, options) => makeDirectTransitionDescriptor(selection, resolve, options),
|
|
79
|
+
reenter: () => makeDirectTransitionDescriptor(selection, undefined, { reenter: true })
|
|
80
|
+
});
|
|
81
|
+
const noneTransitionSelection = decorateTransitionSelection(Topology.noneTargetSelection);
|
|
82
|
+
const makeInitialBuilderDescriptor = (selection, resolve) => Object.freeze({
|
|
83
|
+
[InitialBuilderDescriptorTypeId]: InitialBuilderDescriptorTypeId,
|
|
84
|
+
selection: plainTargetSelection(selection),
|
|
85
|
+
resolve
|
|
86
|
+
});
|
|
87
|
+
const decorateInitialSelection = (selection) => Object.freeze({
|
|
88
|
+
...selection,
|
|
89
|
+
resolve: (resolve) => makeInitialBuilderDescriptor(selection, resolve)
|
|
90
|
+
});
|
|
91
|
+
const decorateInitialSelectorNode = (node) => {
|
|
92
|
+
if (Topology.isTargetSelection(node))
|
|
93
|
+
return decorateInitialSelection(node);
|
|
94
|
+
if (typeof node === "function") {
|
|
95
|
+
const wrapped = ((...args) => decorateInitialSelection(node(...args)));
|
|
96
|
+
for (const key of Object.keys(node)) {
|
|
97
|
+
wrapped[key] = decorateInitialSelectorNode(node[key]);
|
|
98
|
+
}
|
|
99
|
+
return Object.freeze(wrapped);
|
|
100
|
+
}
|
|
101
|
+
if (typeof node === "object" && node !== null) {
|
|
102
|
+
const wrapped = {};
|
|
103
|
+
for (const key of Object.keys(node)) {
|
|
104
|
+
wrapped[key] = decorateInitialSelectorNode(node[key]);
|
|
105
|
+
}
|
|
106
|
+
return Object.freeze(wrapped);
|
|
107
|
+
}
|
|
108
|
+
return node;
|
|
109
|
+
};
|
|
110
|
+
const decorateTransitionSelectorNode = (node) => {
|
|
111
|
+
if (Topology.isTargetSelection(node)) {
|
|
112
|
+
return node === Topology.noneTargetSelection ? noneTransitionSelection : decorateTransitionSelection(node);
|
|
113
|
+
}
|
|
114
|
+
if (typeof node === "function") {
|
|
115
|
+
const wrapped = ((...args) => decorateTransitionSelection(node(...args)));
|
|
116
|
+
for (const key of Object.keys(node)) {
|
|
117
|
+
wrapped[key] = decorateTransitionSelectorNode(node[key]);
|
|
118
|
+
}
|
|
119
|
+
return Object.freeze(wrapped);
|
|
120
|
+
}
|
|
121
|
+
if (typeof node === "object" && node !== null) {
|
|
122
|
+
const wrapped = {};
|
|
123
|
+
for (const key of Object.keys(node)) {
|
|
124
|
+
wrapped[key] = decorateTransitionSelectorNode(node[key]);
|
|
125
|
+
}
|
|
126
|
+
return Object.freeze(wrapped);
|
|
127
|
+
}
|
|
128
|
+
return node;
|
|
129
|
+
};
|
|
130
|
+
const makeTransitionSelector = (stateNodes, source) => {
|
|
131
|
+
const selector = {
|
|
132
|
+
...decorateTransitionSelectorNode(makeTargetSelector(stateNodes, source))
|
|
133
|
+
};
|
|
134
|
+
selector.branches = (declarations) => Object.freeze({
|
|
135
|
+
resolve: (resolve, options) => Object.freeze({
|
|
136
|
+
[TransitionBuilderDescriptorTypeId]: TransitionBuilderDescriptorTypeId,
|
|
137
|
+
type: "branches",
|
|
138
|
+
declarations,
|
|
139
|
+
resolve,
|
|
140
|
+
...transitionOptions(options)
|
|
141
|
+
})
|
|
142
|
+
});
|
|
143
|
+
return Object.freeze(selector);
|
|
144
|
+
};
|
|
145
|
+
const normalizeTransitionBuilder = (transition, stateNodes, path) => {
|
|
146
|
+
const result = transition(makeTransitionSelector(stateNodes, path));
|
|
147
|
+
if (Topology.isTargetSelection(result)) {
|
|
148
|
+
const selection = plainTargetSelection(result);
|
|
149
|
+
return { target: () => selection };
|
|
150
|
+
}
|
|
151
|
+
if (!hasProperty(result, TransitionBuilderDescriptorTypeId))
|
|
152
|
+
return result;
|
|
153
|
+
const descriptor = result;
|
|
154
|
+
if (descriptor.type === "branches") {
|
|
155
|
+
return {
|
|
156
|
+
branches: () => descriptor.declarations,
|
|
157
|
+
resolve: descriptor.resolve,
|
|
158
|
+
reenter: descriptor.reenter,
|
|
159
|
+
declinable: descriptor.declinable
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
return {
|
|
163
|
+
target: () => descriptor.selection,
|
|
164
|
+
resolve: descriptor.resolve,
|
|
165
|
+
reenter: descriptor.reenter,
|
|
166
|
+
declinable: descriptor.declinable
|
|
167
|
+
};
|
|
168
|
+
};
|
|
51
169
|
const transitionTargetSelection = (selection) => Object.freeze({
|
|
52
170
|
path: selection.path,
|
|
53
171
|
kind: selection.kind,
|
|
54
172
|
scope: selection.scope
|
|
55
173
|
});
|
|
56
174
|
const makeSelectionMethod = (kind, path, scope) => () => Topology.makeTargetSelection(kind, path, scope);
|
|
175
|
+
const makeSelectionValue = (kind, path, scope) => Topology.makeTargetSelection(kind, path, scope);
|
|
57
176
|
const addSelectionChildren = (builder, stateNodes, parent, scope) => {
|
|
58
177
|
for (const node of stateNodes.byPath.values()) {
|
|
59
178
|
if (node.parent !== parent || node.type === "history")
|
|
@@ -67,7 +186,7 @@ const makeSelectionNode = (stateNodes, path, scope) => {
|
|
|
67
186
|
const method = makeSelectionMethod(kind, path, scope);
|
|
68
187
|
if (node.type !== "atomic" && node.type !== "final" && node.type !== "choice" && node.type !== "history") {
|
|
69
188
|
Object.defineProperty(method, "initial", {
|
|
70
|
-
value:
|
|
189
|
+
value: makeSelectionValue("initial", path, scope),
|
|
71
190
|
enumerable: true
|
|
72
191
|
});
|
|
73
192
|
if (scope === "local" || scope === "branch") {
|
|
@@ -82,7 +201,7 @@ const makeHistorySelectionTree = (stateNodes, parent) => {
|
|
|
82
201
|
if (node.parent !== parent)
|
|
83
202
|
continue;
|
|
84
203
|
if (node.type === "history") {
|
|
85
|
-
builder[node.key] =
|
|
204
|
+
builder[node.key] = makeSelectionValue("history", node.path, "full");
|
|
86
205
|
}
|
|
87
206
|
else if (node.type !== "choice") {
|
|
88
207
|
const children = makeHistorySelectionTree(stateNodes, node.path);
|
|
@@ -107,12 +226,12 @@ const makeTargetSelector = (stateNodes, source) => {
|
|
|
107
226
|
if (localScope !== undefined) {
|
|
108
227
|
const localScopeNode = getTargetBuilderNode(stateNodes, localScope);
|
|
109
228
|
if (localScopeNode.schema !== undefined) {
|
|
110
|
-
local.with =
|
|
229
|
+
local.with = makeSelectionValue("state", localScope, "local");
|
|
111
230
|
}
|
|
112
231
|
addSelectionChildren(local, stateNodes, localScope, "local");
|
|
113
232
|
}
|
|
114
233
|
return {
|
|
115
|
-
none:
|
|
234
|
+
none: Topology.noneTargetSelection,
|
|
116
235
|
local,
|
|
117
236
|
branch,
|
|
118
237
|
full,
|
|
@@ -167,7 +286,7 @@ const getSelectionBuilder = (target, selection, stateNodes, source) => {
|
|
|
167
286
|
}
|
|
168
287
|
return builder;
|
|
169
288
|
};
|
|
170
|
-
const constructSelectedTarget = (builder) => typeof builder === "function" ? builder() : builder
|
|
289
|
+
const constructSelectedTarget = (builder) => typeof builder?.from === "function" ? builder.from() : builder();
|
|
171
290
|
const validateResolvedSelection = (result, selection, stateNodes) => {
|
|
172
291
|
if (selection.kind === "none") {
|
|
173
292
|
if (result !== undefined) {
|
|
@@ -198,7 +317,15 @@ const runCapturedBranch = (branch, context, enqueue, stateNodes, source) => {
|
|
|
198
317
|
delete resolverContext.target;
|
|
199
318
|
else
|
|
200
319
|
resolverContext.target = selectedTarget;
|
|
320
|
+
if (branch.declinable === true)
|
|
321
|
+
resolverContext.decline = Topology.makeDeclined;
|
|
201
322
|
const resolved = branch.resolve(resolverContext, enqueue);
|
|
323
|
+
if (Topology.isDeclined(resolved)) {
|
|
324
|
+
if (branch.declinable !== true) {
|
|
325
|
+
throw new Error(`Machine transition for state "${source}" returned decline without declaring declinable: true`);
|
|
326
|
+
}
|
|
327
|
+
return resolved;
|
|
328
|
+
}
|
|
202
329
|
validateResolvedSelection(resolved, branch.selection, stateNodes);
|
|
203
330
|
return resolved === undefined ? constructSelectedTarget(selectedTarget) : resolved;
|
|
204
331
|
};
|
|
@@ -232,7 +359,7 @@ const captureNamedBranches = (declarations, path, trigger) => {
|
|
|
232
359
|
if (title !== undefined && (typeof title !== "string" || title.length === 0)) {
|
|
233
360
|
throw new Error(`Machine transition branch "${key}" title must be a non-empty string`);
|
|
234
361
|
}
|
|
235
|
-
return Object.freeze({ key, title: title ?? key, selection: target });
|
|
362
|
+
return Object.freeze({ key, title: title ?? key, selection: plainTargetSelection(target) });
|
|
236
363
|
}));
|
|
237
364
|
};
|
|
238
365
|
const wrapSelectedBranchBuilder = (builder, owner, branchIndex, branchKey) => {
|
|
@@ -287,13 +414,18 @@ const validateSelectedBranchResult = (result, selection, stateNodes) => {
|
|
|
287
414
|
}
|
|
288
415
|
validateResolvedSelection(result, selection, stateNodes);
|
|
289
416
|
};
|
|
290
|
-
const captureTransition = (
|
|
417
|
+
const captureTransition = (rawTransition, stateNodes, path, trigger) => {
|
|
418
|
+
if (typeof rawTransition !== "function") {
|
|
419
|
+
throw new Error(`Machine transition for state "${path}" on "${String(trigger)}" must be a target-first callback`);
|
|
420
|
+
}
|
|
421
|
+
const transition = normalizeTransitionBuilder(rawTransition, stateNodes, path);
|
|
291
422
|
if (typeof transition !== "object" || transition === null) {
|
|
292
423
|
throw new Error(`Machine transition for state "${path}" on "${String(trigger)}" must be an object`);
|
|
293
424
|
}
|
|
294
425
|
const definition = transition;
|
|
295
426
|
const selector = makeTargetSelector(stateNodes, path);
|
|
296
427
|
const reenter = definition.reenter === true;
|
|
428
|
+
const declinable = definition.declinable === true;
|
|
297
429
|
if (hasProperty(definition, "branches")) {
|
|
298
430
|
const branching = definition;
|
|
299
431
|
if (typeof branching.branches !== "function" || typeof branching.resolve !== "function") {
|
|
@@ -306,7 +438,15 @@ const captureTransition = (transition, stateNodes, path, trigger) => {
|
|
|
306
438
|
const resolverContext = { ...context };
|
|
307
439
|
delete resolverContext.target;
|
|
308
440
|
resolverContext.select = makeBranchSelectors(context, branches, owner, stateNodes, path);
|
|
441
|
+
if (declinable)
|
|
442
|
+
resolverContext.decline = Topology.makeDeclined;
|
|
309
443
|
const selected = resolve(resolverContext, enqueue);
|
|
444
|
+
if (Topology.isDeclined(selected)) {
|
|
445
|
+
if (!declinable) {
|
|
446
|
+
throw new Error(`Machine branching transition for state "${path}" on "${String(trigger)}" returned decline without declaring declinable: true`);
|
|
447
|
+
}
|
|
448
|
+
return { result: selected, branchIndex: -1, branchKey: undefined };
|
|
449
|
+
}
|
|
310
450
|
if (!Topology.isSelectedBranch(selected) || selected.owner !== owner) {
|
|
311
451
|
throw new Error(`Machine branching transition for state "${path}" on "${String(trigger)}" must select one declared branch`);
|
|
312
452
|
}
|
|
@@ -323,6 +463,7 @@ const captureTransition = (transition, stateNodes, path, trigger) => {
|
|
|
323
463
|
};
|
|
324
464
|
return {
|
|
325
465
|
reenter,
|
|
466
|
+
declinable,
|
|
326
467
|
targets: [
|
|
327
468
|
...new Set(branches.flatMap((branch) => branch.selection.path === undefined ? [] : [branch.selection.path]))
|
|
328
469
|
],
|
|
@@ -345,6 +486,7 @@ const captureTransition = (transition, stateNodes, path, trigger) => {
|
|
|
345
486
|
});
|
|
346
487
|
return {
|
|
347
488
|
reenter,
|
|
489
|
+
declinable,
|
|
348
490
|
targets: branch.selection.path === undefined ? [] : [branch.selection.path],
|
|
349
491
|
branches: [{
|
|
350
492
|
type: "direct",
|
|
@@ -365,18 +507,55 @@ const captureEventHandlers = (on, stateNodes, path) => {
|
|
|
365
507
|
}
|
|
366
508
|
return captured;
|
|
367
509
|
};
|
|
368
|
-
const
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
const
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
captured[key] = captureTransition(captured[key], stateNodes, path, key);
|
|
510
|
+
const makeInvokeBuilder = (config, channels) => {
|
|
511
|
+
const builder = {
|
|
512
|
+
[InvokeBuilderDescriptorTypeId]: InvokeBuilderDescriptorTypeId,
|
|
513
|
+
config
|
|
514
|
+
};
|
|
515
|
+
for (const channel of channels) {
|
|
516
|
+
if (!hasProperty(config, channel)) {
|
|
517
|
+
builder[channel] = (handler) => makeInvokeBuilder({ ...config, [channel]: handler }, channels);
|
|
377
518
|
}
|
|
378
519
|
}
|
|
379
|
-
return
|
|
520
|
+
return Object.freeze(builder);
|
|
521
|
+
};
|
|
522
|
+
const invokeSelector = Object.freeze({
|
|
523
|
+
effect: (id, effect) => makeInvokeBuilder({ id, effect }, ["onDone", "onFailure"]),
|
|
524
|
+
stream: (id, stream) => makeInvokeBuilder({ id, stream }, ["onElement", "onDone", "onFailure"]),
|
|
525
|
+
timer: (id, after) => makeInvokeBuilder({ id, after }, ["onDone"]),
|
|
526
|
+
logic: (id, options) => makeInvokeBuilder({ id, ...options }, ["onSnapshot", "onDone", "onFailure"]),
|
|
527
|
+
child: (child, options) => makeInvokeBuilder(options === undefined ? { child } : { child, ...options }, [
|
|
528
|
+
"onSnapshot",
|
|
529
|
+
"onDone",
|
|
530
|
+
"onFailure"
|
|
531
|
+
])
|
|
532
|
+
});
|
|
533
|
+
const invokeBuilderConfig = (value, path) => {
|
|
534
|
+
if (typeof value !== "object" || value === null ||
|
|
535
|
+
!hasProperty(value, InvokeBuilderDescriptorTypeId) ||
|
|
536
|
+
value[InvokeBuilderDescriptorTypeId] !== InvokeBuilderDescriptorTypeId) {
|
|
537
|
+
throw new Error(`Machine invocation for state "${path}" must be constructed from its source selector`);
|
|
538
|
+
}
|
|
539
|
+
return value.config;
|
|
540
|
+
};
|
|
541
|
+
const captureInvokeDefinition = (invoke, stateNodes, path) => {
|
|
542
|
+
if (typeof invoke !== "function") {
|
|
543
|
+
throw new Error(`Machine invocation for state "${path}" must be a source-first callback`);
|
|
544
|
+
}
|
|
545
|
+
const authored = invoke(invokeSelector);
|
|
546
|
+
const definitions = Array.isArray(authored) ? authored : [authored];
|
|
547
|
+
const capturedDefinitions = definitions.map((definition) => {
|
|
548
|
+
const captured = { ...invokeBuilderConfig(definition, path) };
|
|
549
|
+
for (const key of ["onElement", "onDone", "onFailure", "onSnapshot"]) {
|
|
550
|
+
if (captured[key] !== undefined) {
|
|
551
|
+
captured[key] = captureTransition(captured[key], stateNodes, path, key);
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
return captured;
|
|
555
|
+
});
|
|
556
|
+
if (Array.isArray(authored))
|
|
557
|
+
return capturedDefinitions;
|
|
558
|
+
return capturedDefinitions[0];
|
|
380
559
|
};
|
|
381
560
|
const flattenHandlers = (handlers, stateNodes, states, prefix, config) => {
|
|
382
561
|
for (const key of Object.keys(config)) {
|
|
@@ -750,10 +929,12 @@ const makeInitialSelector = (stateNodes) => {
|
|
|
750
929
|
const selector = {};
|
|
751
930
|
for (const node of stateNodes.byPath.values()) {
|
|
752
931
|
if (node.parent === undefined && node.type !== "history" && node.type !== "choice") {
|
|
753
|
-
selector[node.key] =
|
|
932
|
+
selector[node.key] = node.type === "atomic" || node.type === "final"
|
|
933
|
+
? makeSelectionMethod("state", node.path, "initial")
|
|
934
|
+
: Object.freeze({ initial: makeSelectionValue("initial", node.path, "initial") });
|
|
754
935
|
}
|
|
755
936
|
}
|
|
756
|
-
return selector;
|
|
937
|
+
return Object.freeze(selector);
|
|
757
938
|
};
|
|
758
939
|
const getInitialSelectionBuilder = (initialBuilder, selection) => {
|
|
759
940
|
const path = selection.path;
|
|
@@ -766,12 +947,32 @@ const getInitialSelectionBuilder = (initialBuilder, selection) => {
|
|
|
766
947
|
}
|
|
767
948
|
return builder;
|
|
768
949
|
};
|
|
769
|
-
const captureInitialBranch = (
|
|
770
|
-
|
|
771
|
-
|
|
950
|
+
const captureInitialBranch = (definition, stateNodes, initialBuilder) => {
|
|
951
|
+
if (typeof definition !== "function") {
|
|
952
|
+
throw new Error("Machine initial definition must be a target-first callback");
|
|
953
|
+
}
|
|
954
|
+
const result = definition(decorateInitialSelectorNode(makeInitialSelector(stateNodes)));
|
|
955
|
+
let selection;
|
|
956
|
+
let resolve;
|
|
957
|
+
if (Topology.isTargetSelection(result)) {
|
|
958
|
+
selection = plainTargetSelection(result);
|
|
959
|
+
}
|
|
960
|
+
else if (hasProperty(result, InitialBuilderDescriptorTypeId)) {
|
|
961
|
+
const descriptor = result;
|
|
962
|
+
selection = descriptor.selection;
|
|
963
|
+
resolve = descriptor.resolve;
|
|
964
|
+
}
|
|
965
|
+
else {
|
|
966
|
+
throw new Error("Machine initial definition must select exactly one target");
|
|
967
|
+
}
|
|
968
|
+
if (selection.kind !== "state" && selection.kind !== "initial") {
|
|
772
969
|
throw new Error("Machine initial target must select a top-level state or its declared initial entry");
|
|
773
970
|
}
|
|
774
|
-
|
|
971
|
+
const captured = {
|
|
972
|
+
selection,
|
|
973
|
+
builder: getInitialSelectionBuilder(initialBuilder, selection)
|
|
974
|
+
};
|
|
975
|
+
return resolve === undefined ? Object.freeze(captured) : Object.freeze({ ...captured, resolve });
|
|
775
976
|
};
|
|
776
977
|
const validateInitialSelection = (result, selection) => {
|
|
777
978
|
if (typeof result !== "object" || result === null || !hasProperty(result, "path") || result.path !== selection.path) {
|
|
@@ -782,17 +983,13 @@ const validateInitialSelection = (result, selection) => {
|
|
|
782
983
|
}
|
|
783
984
|
};
|
|
784
985
|
const compileInitial = (definition, states, stateNodes) => {
|
|
785
|
-
if (typeof definition !== "object" || definition === null) {
|
|
786
|
-
throw new Error("Machine initial definition must be an object");
|
|
787
|
-
}
|
|
788
|
-
const selector = makeInitialSelector(stateNodes);
|
|
789
986
|
const initialBuilder = makeSnapshotBuilder(states, { mode: "initial", prefix: "" });
|
|
790
|
-
const branch = captureInitialBranch(definition,
|
|
987
|
+
const branch = captureInitialBranch(definition, stateNodes, initialBuilder);
|
|
791
988
|
return {
|
|
792
989
|
initial: (input) => {
|
|
793
990
|
const result = branch.resolve === undefined
|
|
794
|
-
? branch.builder
|
|
795
|
-
: branch.resolve({ input, target: branch.builder }
|
|
991
|
+
? constructSelectedTarget(branch.builder)
|
|
992
|
+
: branch.resolve({ input, target: branch.builder });
|
|
796
993
|
validateInitialSelection(result, branch.selection);
|
|
797
994
|
return result;
|
|
798
995
|
},
|
|
@@ -828,7 +1025,7 @@ export const make = ((config) => {
|
|
|
828
1025
|
self.events = config.events;
|
|
829
1026
|
self.internalEvents = config.internalEvents ?? Protocol.makeEventProtocol("internal", []);
|
|
830
1027
|
self.emittedEvents = config.emittedEvents ?? Protocol.makeEventProtocol("emitted", []);
|
|
831
|
-
self.
|
|
1028
|
+
self.parent = config.parent;
|
|
832
1029
|
self.input = config.input;
|
|
833
1030
|
self.id = config.id;
|
|
834
1031
|
self.stateNodes = Topology.compileStateNodes(config.states);
|
|
@@ -838,7 +1035,6 @@ export const make = ((config) => {
|
|
|
838
1035
|
self.makeTargetBuilder = makeTargetBuilder(config.states, self.stateNodes);
|
|
839
1036
|
self.handlers = Object.create(null);
|
|
840
1037
|
self.handle = makeHandle(self);
|
|
841
|
-
self.invoke = makeBoundInvoke;
|
|
842
1038
|
Protocol.setProtocol(self);
|
|
843
1039
|
return self;
|
|
844
1040
|
});
|
|
@@ -846,6 +1042,14 @@ const flattenEventProtocolInputs = (kind, inputs) => inputs.flatMap((input) => P
|
|
|
846
1042
|
? Protocol.eventProtocolSchemas(input)
|
|
847
1043
|
: [input]);
|
|
848
1044
|
export const events = (...inputs) => Protocol.makeEventProtocol("public", flattenEventProtocolInputs("public", inputs));
|
|
1045
|
+
const makeParent = (mode, events) => {
|
|
1046
|
+
if (!Protocol.isEventProtocol(events, "public")) {
|
|
1047
|
+
throw new Error("Machine parent declarations require a protocol created with Machine.events");
|
|
1048
|
+
}
|
|
1049
|
+
return Object.freeze({ [ParentTypeId]: ParentTypeId, mode, events });
|
|
1050
|
+
};
|
|
1051
|
+
export const parent = (events) => makeParent("required", events);
|
|
1052
|
+
export const optionalParent = (events) => makeParent("optional", events);
|
|
849
1053
|
export const internalEvents = (...inputs) => Protocol.makeEventProtocol("internal", flattenEventProtocolInputs("internal", inputs));
|
|
850
1054
|
export const emittedEvents = (...inputs) => Protocol.makeEventProtocol("emitted", flattenEventProtocolInputs("emitted", inputs));
|
|
851
1055
|
export const encodeSnapshot = Serialization.encodeSnapshot;
|