@typeonce/effect-machine 0.15.0 → 0.17.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 +115 -95
- package/dist/Machine.d.ts +464 -337
- package/dist/Machine.d.ts.map +1 -1
- package/dist/Machine.js +72 -107
- package/dist/Machine.js.map +1 -1
- package/dist/internal/machine/activities.d.ts +2 -0
- package/dist/internal/machine/activities.d.ts.map +1 -1
- package/dist/internal/machine/activities.js +4 -0
- package/dist/internal/machine/activities.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 +16 -4
- package/dist/internal/machine/executionPlan.js.map +1 -1
- package/dist/internal/machine/invocation.d.ts +1 -1
- package/dist/internal/machine/invocation.d.ts.map +1 -1
- package/dist/internal/machine/invocation.js +25 -3
- package/dist/internal/machine/invocation.js.map +1 -1
- package/dist/internal/machine/invocationEvent.d.ts +8 -0
- package/dist/internal/machine/invocationEvent.d.ts.map +1 -1
- package/dist/internal/machine/invocationEvent.js +8 -0
- package/dist/internal/machine/invocationEvent.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 +308 -71
- package/dist/internal/machine/machine.js.map +1 -1
- package/dist/internal/machine/planner.d.ts +26 -4
- package/dist/internal/machine/planner.d.ts.map +1 -1
- package/dist/internal/machine/planner.js +76 -31
- package/dist/internal/machine/planner.js.map +1 -1
- package/dist/internal/machine/runtime.d.ts +1 -0
- package/dist/internal/machine/runtime.d.ts.map +1 -1
- package/dist/internal/machine/runtime.js +25 -16
- package/dist/internal/machine/runtime.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 +20 -0
- package/dist/internal/machine/topology.d.ts.map +1 -1
- package/dist/internal/machine/topology.js +33 -6
- 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 +8 -2
- package/dist/internal/testing/machine/transitionCoverage.js.map +1 -1
- package/dist/internal/testing/machine/verification.d.ts.map +1 -1
- package/dist/internal/testing/machine/verification.js +6 -0
- package/dist/internal/testing/machine/verification.js.map +1 -1
- package/dist/testing/MachineTest.d.ts +13 -12
- 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 +12 -12
- package/dist/unstable/reactivity/AtomMachine.d.ts.map +1 -1
- package/dist/unstable/reactivity/AtomMachine.js.map +1 -1
- package/docs/agent-guide.md +182 -159
- package/package.json +1 -1
- package/src/Machine.ts +1073 -700
- package/src/internal/machine/activities.ts +7 -0
- package/src/internal/machine/atom.ts +20 -15
- package/src/internal/machine/cluster.ts +14 -9
- package/src/internal/machine/executionPlan.ts +14 -4
- package/src/internal/machine/invocation.ts +39 -4
- package/src/internal/machine/invocationEvent.ts +16 -0
- package/src/internal/machine/machine.ts +460 -86
- package/src/internal/machine/planner.ts +106 -30
- package/src/internal/machine/runtime.ts +61 -25
- package/src/internal/machine/stateDefinition.ts +39 -0
- package/src/internal/machine/topology.ts +59 -2
- package/src/internal/testing/machine/finiteModel.ts +18 -21
- package/src/internal/testing/machine/transitionCoverage.ts +8 -2
- package/src/internal/testing/machine/verification.ts +11 -0
- package/src/testing/MachineTest.ts +16 -11
- package/src/unstable/cluster/ClusterMachine.ts +8 -4
- package/src/unstable/reactivity/AtomMachine.ts +19 -12
|
@@ -19,6 +19,8 @@ import type {
|
|
|
19
19
|
MachineRef,
|
|
20
20
|
MachineSchemaDecodeError,
|
|
21
21
|
MachineSchemaEncodeError,
|
|
22
|
+
Parent,
|
|
23
|
+
ParentMode,
|
|
22
24
|
Runtime,
|
|
23
25
|
RuntimeOutcome,
|
|
24
26
|
SpawnOptions,
|
|
@@ -49,6 +51,7 @@ export {
|
|
|
49
51
|
export { ChildMachineLogicTypeId, InitialEventTypeId, SnapshotBuilderStateTypeId } from "./symbols.js"
|
|
50
52
|
|
|
51
53
|
const TypeId = "~effect/Machine"
|
|
54
|
+
const ParentTypeId = "~effect/Machine/Parent"
|
|
52
55
|
export const InvokeTypeId: unique symbol = Symbol.for("effect/Machine/Invoke")
|
|
53
56
|
export const TransitionTypeId: unique symbol = Symbol.for("effect/Machine/Transition")
|
|
54
57
|
const ChildMachineTypeId = "~effect/Machine/ChildMachine"
|
|
@@ -112,8 +115,6 @@ const Proto = {
|
|
|
112
115
|
}
|
|
113
116
|
}
|
|
114
117
|
|
|
115
|
-
const makeBoundInvoke = (config: unknown): unknown => config
|
|
116
|
-
|
|
117
118
|
const makeWithHandlers = (
|
|
118
119
|
self: Definition.Any,
|
|
119
120
|
handlers: Machine.StateConfigs<any, any, any, any, any, any, any>
|
|
@@ -123,7 +124,7 @@ const makeWithHandlers = (
|
|
|
123
124
|
machine.events = self.events
|
|
124
125
|
machine.internalEvents = self.internalEvents
|
|
125
126
|
machine.emittedEvents = self.emittedEvents
|
|
126
|
-
machine.
|
|
127
|
+
machine.parent = self.parent
|
|
127
128
|
machine.input = self.input
|
|
128
129
|
machine.id = self.id
|
|
129
130
|
machine.initial = self.initial
|
|
@@ -131,22 +132,214 @@ const makeWithHandlers = (
|
|
|
131
132
|
machine.stateNodes = self.stateNodes
|
|
132
133
|
machine.makeTargetBuilder = self.makeTargetBuilder
|
|
133
134
|
machine.handlers = handlers
|
|
134
|
-
machine.invoke = makeBoundInvoke
|
|
135
135
|
Protocol.copyProtocol(self, machine)
|
|
136
136
|
return machine
|
|
137
137
|
}
|
|
138
138
|
|
|
139
139
|
type DefinitionBranch = {
|
|
140
|
-
readonly title?: string
|
|
141
|
-
readonly when?: (context: any) => Option.Option<unknown>
|
|
142
140
|
readonly target: (selector: unknown) => unknown
|
|
143
141
|
readonly resolve?: (context: any, enqueue: unknown) => unknown
|
|
142
|
+
readonly declinable?: boolean
|
|
144
143
|
}
|
|
145
144
|
|
|
146
145
|
type CapturedBranch = DefinitionBranch & {
|
|
147
146
|
readonly selection: Topology.TargetSelection
|
|
148
147
|
}
|
|
149
148
|
|
|
149
|
+
type BranchDeclaration = {
|
|
150
|
+
readonly title?: string
|
|
151
|
+
readonly target: unknown
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
type CapturedNamedBranch = {
|
|
155
|
+
readonly key: string
|
|
156
|
+
readonly title: string
|
|
157
|
+
readonly selection: Topology.TargetSelection
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
const TransitionBuilderDescriptorTypeId: unique symbol = Symbol("effect/Machine/TransitionBuilderDescriptor")
|
|
161
|
+
|
|
162
|
+
type DirectTransitionDescriptor = {
|
|
163
|
+
readonly [TransitionBuilderDescriptorTypeId]: typeof TransitionBuilderDescriptorTypeId
|
|
164
|
+
readonly type: "direct"
|
|
165
|
+
readonly selection: Topology.TargetSelection
|
|
166
|
+
readonly resolve?: (context: any, enqueue: unknown) => unknown
|
|
167
|
+
readonly reenter: boolean
|
|
168
|
+
readonly declinable: boolean
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
type BranchesTransitionDescriptor = {
|
|
172
|
+
readonly [TransitionBuilderDescriptorTypeId]: typeof TransitionBuilderDescriptorTypeId
|
|
173
|
+
readonly type: "branches"
|
|
174
|
+
readonly declarations: unknown
|
|
175
|
+
readonly resolve: (context: any, enqueue: unknown) => unknown
|
|
176
|
+
readonly reenter: boolean
|
|
177
|
+
readonly declinable: boolean
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
type TransitionBuilderDescriptor = DirectTransitionDescriptor | BranchesTransitionDescriptor
|
|
181
|
+
|
|
182
|
+
const InitialBuilderDescriptorTypeId: unique symbol = Symbol("effect/Machine/InitialBuilderDescriptor")
|
|
183
|
+
|
|
184
|
+
type InitialBuilderDescriptor = {
|
|
185
|
+
readonly [InitialBuilderDescriptorTypeId]: typeof InitialBuilderDescriptorTypeId
|
|
186
|
+
readonly selection: Topology.TargetSelection
|
|
187
|
+
readonly resolve: (context: any) => unknown
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
const plainTargetSelection = (selection: Topology.TargetSelection): Topology.TargetSelection =>
|
|
191
|
+
selection.kind === "none"
|
|
192
|
+
? Topology.noneTargetSelection
|
|
193
|
+
: Topology.makeTargetSelection(selection.kind, selection.path, selection.scope)
|
|
194
|
+
|
|
195
|
+
const transitionOptions = (options: unknown): { readonly reenter: boolean; readonly declinable: boolean } => {
|
|
196
|
+
const configuration = typeof options === "object" && options !== null
|
|
197
|
+
? options as { readonly reenter?: unknown; readonly declinable?: unknown }
|
|
198
|
+
: {}
|
|
199
|
+
return {
|
|
200
|
+
reenter: configuration.reenter === true,
|
|
201
|
+
declinable: configuration.declinable === true
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
const makeDirectTransitionDescriptor = (
|
|
206
|
+
selection: Topology.TargetSelection,
|
|
207
|
+
resolve: ((context: any, enqueue: unknown) => unknown) | undefined,
|
|
208
|
+
options: unknown
|
|
209
|
+
): DirectTransitionDescriptor => {
|
|
210
|
+
const shared: Omit<DirectTransitionDescriptor, "resolve"> = {
|
|
211
|
+
[TransitionBuilderDescriptorTypeId]: TransitionBuilderDescriptorTypeId,
|
|
212
|
+
type: "direct",
|
|
213
|
+
selection: plainTargetSelection(selection),
|
|
214
|
+
...transitionOptions(options)
|
|
215
|
+
}
|
|
216
|
+
return resolve === undefined
|
|
217
|
+
? Object.freeze(shared)
|
|
218
|
+
: Object.freeze({ ...shared, resolve })
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
const decorateTransitionSelection = (selection: Topology.TargetSelection): Topology.TargetSelection =>
|
|
222
|
+
Object.freeze({
|
|
223
|
+
...selection,
|
|
224
|
+
resolve: (resolve: (context: any, enqueue: unknown) => unknown, options?: unknown) =>
|
|
225
|
+
makeDirectTransitionDescriptor(selection, resolve, options),
|
|
226
|
+
reenter: () => makeDirectTransitionDescriptor(selection, undefined, { reenter: true })
|
|
227
|
+
})
|
|
228
|
+
|
|
229
|
+
const noneTransitionSelection = decorateTransitionSelection(Topology.noneTargetSelection)
|
|
230
|
+
|
|
231
|
+
const makeInitialBuilderDescriptor = (
|
|
232
|
+
selection: Topology.TargetSelection,
|
|
233
|
+
resolve: (context: any) => unknown
|
|
234
|
+
): InitialBuilderDescriptor =>
|
|
235
|
+
Object.freeze({
|
|
236
|
+
[InitialBuilderDescriptorTypeId]: InitialBuilderDescriptorTypeId,
|
|
237
|
+
selection: plainTargetSelection(selection),
|
|
238
|
+
resolve
|
|
239
|
+
})
|
|
240
|
+
|
|
241
|
+
const decorateInitialSelection = (selection: Topology.TargetSelection): Topology.TargetSelection =>
|
|
242
|
+
Object.freeze({
|
|
243
|
+
...selection,
|
|
244
|
+
resolve: (resolve: (context: any) => unknown) => makeInitialBuilderDescriptor(selection, resolve)
|
|
245
|
+
})
|
|
246
|
+
|
|
247
|
+
const decorateInitialSelectorNode = (node: unknown): unknown => {
|
|
248
|
+
if (Topology.isTargetSelection(node)) return decorateInitialSelection(node)
|
|
249
|
+
if (typeof node === "function") {
|
|
250
|
+
const wrapped = ((...args: ReadonlyArray<unknown>) => decorateInitialSelection(node(...args))) as
|
|
251
|
+
& ((...args: ReadonlyArray<unknown>) => unknown)
|
|
252
|
+
& Record<string, unknown>
|
|
253
|
+
for (const key of Object.keys(node)) {
|
|
254
|
+
wrapped[key] = decorateInitialSelectorNode((node as unknown as Record<string, unknown>)[key])
|
|
255
|
+
}
|
|
256
|
+
return Object.freeze(wrapped)
|
|
257
|
+
}
|
|
258
|
+
if (typeof node === "object" && node !== null) {
|
|
259
|
+
const wrapped: Record<string, unknown> = {}
|
|
260
|
+
for (const key of Object.keys(node)) {
|
|
261
|
+
wrapped[key] = decorateInitialSelectorNode((node as Record<string, unknown>)[key])
|
|
262
|
+
}
|
|
263
|
+
return Object.freeze(wrapped)
|
|
264
|
+
}
|
|
265
|
+
return node
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
const decorateTransitionSelectorNode = (node: unknown): unknown => {
|
|
269
|
+
if (Topology.isTargetSelection(node)) {
|
|
270
|
+
return node === Topology.noneTargetSelection ? noneTransitionSelection : decorateTransitionSelection(node)
|
|
271
|
+
}
|
|
272
|
+
if (typeof node === "function") {
|
|
273
|
+
const wrapped = ((...args: ReadonlyArray<unknown>) => decorateTransitionSelection(node(...args))) as
|
|
274
|
+
& ((...args: ReadonlyArray<unknown>) => unknown)
|
|
275
|
+
& Record<string, unknown>
|
|
276
|
+
for (const key of Object.keys(node)) {
|
|
277
|
+
wrapped[key] = decorateTransitionSelectorNode((node as unknown as Record<string, unknown>)[key])
|
|
278
|
+
}
|
|
279
|
+
return Object.freeze(wrapped)
|
|
280
|
+
}
|
|
281
|
+
if (typeof node === "object" && node !== null) {
|
|
282
|
+
const wrapped: Record<string, unknown> = {}
|
|
283
|
+
for (const key of Object.keys(node)) {
|
|
284
|
+
wrapped[key] = decorateTransitionSelectorNode((node as Record<string, unknown>)[key])
|
|
285
|
+
}
|
|
286
|
+
return Object.freeze(wrapped)
|
|
287
|
+
}
|
|
288
|
+
return node
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
const makeTransitionSelector = (
|
|
292
|
+
stateNodes: Machine.StateNodes,
|
|
293
|
+
source: string
|
|
294
|
+
): unknown => {
|
|
295
|
+
const selector = {
|
|
296
|
+
...decorateTransitionSelectorNode(makeTargetSelector(stateNodes, source)) as Record<string, unknown>
|
|
297
|
+
}
|
|
298
|
+
selector.branches = (declarations: unknown) =>
|
|
299
|
+
Object.freeze({
|
|
300
|
+
resolve: (
|
|
301
|
+
resolve: (context: any, enqueue: unknown) => unknown,
|
|
302
|
+
options?: unknown
|
|
303
|
+
): BranchesTransitionDescriptor =>
|
|
304
|
+
Object.freeze({
|
|
305
|
+
[TransitionBuilderDescriptorTypeId]: TransitionBuilderDescriptorTypeId,
|
|
306
|
+
type: "branches",
|
|
307
|
+
declarations,
|
|
308
|
+
resolve,
|
|
309
|
+
...transitionOptions(options)
|
|
310
|
+
})
|
|
311
|
+
})
|
|
312
|
+
return Object.freeze(selector)
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
const normalizeTransitionBuilder = (
|
|
316
|
+
transition: (selector: unknown) => unknown,
|
|
317
|
+
stateNodes: Machine.StateNodes,
|
|
318
|
+
path: string
|
|
319
|
+
): unknown => {
|
|
320
|
+
const result = transition(makeTransitionSelector(stateNodes, path))
|
|
321
|
+
if (Topology.isTargetSelection(result)) {
|
|
322
|
+
const selection = plainTargetSelection(result)
|
|
323
|
+
return { target: () => selection }
|
|
324
|
+
}
|
|
325
|
+
if (!hasProperty(result, TransitionBuilderDescriptorTypeId)) return result
|
|
326
|
+
const descriptor = result as TransitionBuilderDescriptor
|
|
327
|
+
if (descriptor.type === "branches") {
|
|
328
|
+
return {
|
|
329
|
+
branches: () => descriptor.declarations,
|
|
330
|
+
resolve: descriptor.resolve,
|
|
331
|
+
reenter: descriptor.reenter,
|
|
332
|
+
declinable: descriptor.declinable
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
return {
|
|
336
|
+
target: () => descriptor.selection,
|
|
337
|
+
resolve: descriptor.resolve,
|
|
338
|
+
reenter: descriptor.reenter,
|
|
339
|
+
declinable: descriptor.declinable
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
|
|
150
343
|
const transitionTargetSelection = (
|
|
151
344
|
selection: Topology.TargetSelection
|
|
152
345
|
): Machine.TransitionTargetSelection =>
|
|
@@ -163,6 +356,12 @@ const makeSelectionMethod = (
|
|
|
163
356
|
): () => Topology.TargetSelection =>
|
|
164
357
|
() => Topology.makeTargetSelection(kind, path, scope)
|
|
165
358
|
|
|
359
|
+
const makeSelectionValue = (
|
|
360
|
+
kind: Topology.TargetSelectionKind,
|
|
361
|
+
path: string | undefined,
|
|
362
|
+
scope: Topology.TargetSelectionScope
|
|
363
|
+
): Topology.TargetSelection => Topology.makeTargetSelection(kind, path, scope)
|
|
364
|
+
|
|
166
365
|
const addSelectionChildren = (
|
|
167
366
|
builder: Record<string, unknown>,
|
|
168
367
|
stateNodes: Machine.StateNodes,
|
|
@@ -185,7 +384,7 @@ const makeSelectionNode = (
|
|
|
185
384
|
const method = makeSelectionMethod(kind, path, scope) as unknown as Record<string, unknown>
|
|
186
385
|
if (node.type !== "atomic" && node.type !== "final" && node.type !== "choice" && node.type !== "history") {
|
|
187
386
|
Object.defineProperty(method, "initial", {
|
|
188
|
-
value:
|
|
387
|
+
value: makeSelectionValue("initial", path, scope),
|
|
189
388
|
enumerable: true
|
|
190
389
|
})
|
|
191
390
|
if (scope === "local" || scope === "branch") {
|
|
@@ -203,7 +402,7 @@ const makeHistorySelectionTree = (
|
|
|
203
402
|
for (const node of stateNodes.byPath.values()) {
|
|
204
403
|
if (node.parent !== parent) continue
|
|
205
404
|
if (node.type === "history") {
|
|
206
|
-
builder[node.key] =
|
|
405
|
+
builder[node.key] = makeSelectionValue("history", node.path, "full")
|
|
207
406
|
} else if (node.type !== "choice") {
|
|
208
407
|
const children = makeHistorySelectionTree(stateNodes, node.path)
|
|
209
408
|
if (Object.keys(children).length > 0) builder[node.key] = children
|
|
@@ -230,12 +429,12 @@ const makeTargetSelector = (
|
|
|
230
429
|
if (localScope !== undefined) {
|
|
231
430
|
const localScopeNode = getTargetBuilderNode(stateNodes, localScope)
|
|
232
431
|
if (localScopeNode.schema !== undefined) {
|
|
233
|
-
local.with =
|
|
432
|
+
local.with = makeSelectionValue("state", localScope, "local")
|
|
234
433
|
}
|
|
235
434
|
addSelectionChildren(local, stateNodes, localScope, "local")
|
|
236
435
|
}
|
|
237
436
|
return {
|
|
238
|
-
none:
|
|
437
|
+
none: Topology.noneTargetSelection,
|
|
239
438
|
local,
|
|
240
439
|
branch,
|
|
241
440
|
full,
|
|
@@ -300,7 +499,8 @@ const getSelectionBuilder = (
|
|
|
300
499
|
return builder
|
|
301
500
|
}
|
|
302
501
|
|
|
303
|
-
const constructSelectedTarget = (builder: any): unknown =>
|
|
502
|
+
const constructSelectedTarget = (builder: any): unknown =>
|
|
503
|
+
typeof builder?.from === "function" ? builder.from() : builder()
|
|
304
504
|
|
|
305
505
|
const validateResolvedSelection = (
|
|
306
506
|
result: unknown,
|
|
@@ -336,90 +536,219 @@ const runCapturedBranch = (
|
|
|
336
536
|
context: Record<string, any>,
|
|
337
537
|
enqueue: unknown,
|
|
338
538
|
stateNodes: Machine.StateNodes,
|
|
339
|
-
source: string
|
|
340
|
-
match?: { readonly value: unknown }
|
|
539
|
+
source: string
|
|
341
540
|
): unknown => {
|
|
342
541
|
const selectedTarget = getSelectionBuilder(context.target, branch.selection, stateNodes, source)
|
|
343
542
|
if (branch.resolve === undefined) return constructSelectedTarget(selectedTarget)
|
|
344
543
|
const resolverContext = { ...context }
|
|
345
544
|
if (branch.selection.kind === "none") delete resolverContext.target
|
|
346
545
|
else resolverContext.target = selectedTarget
|
|
347
|
-
if (
|
|
546
|
+
if (branch.declinable === true) resolverContext.decline = Topology.makeDeclined
|
|
348
547
|
const resolved = branch.resolve(resolverContext, enqueue)
|
|
548
|
+
if (Topology.isDeclined(resolved)) {
|
|
549
|
+
if (branch.declinable !== true) {
|
|
550
|
+
throw new Error(`Machine transition for state "${source}" returned decline without declaring declinable: true`)
|
|
551
|
+
}
|
|
552
|
+
return resolved
|
|
553
|
+
}
|
|
349
554
|
validateResolvedSelection(resolved, branch.selection, stateNodes)
|
|
350
555
|
return resolved === undefined ? constructSelectedTarget(selectedTarget) : resolved
|
|
351
556
|
}
|
|
352
557
|
|
|
558
|
+
const isArrayIndexKey = (key: string): boolean => {
|
|
559
|
+
const index = Number(key)
|
|
560
|
+
return Number.isInteger(index) && index >= 0 && index < 0xffff_ffff && String(index) === key
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
const captureNamedBranches = (
|
|
564
|
+
declarations: unknown,
|
|
565
|
+
path: string,
|
|
566
|
+
trigger: PropertyKey
|
|
567
|
+
): ReadonlyArray<CapturedNamedBranch> => {
|
|
568
|
+
if (typeof declarations !== "object" || declarations === null || Array.isArray(declarations)) {
|
|
569
|
+
throw new Error(`Machine branching transition for state "${path}" on "${String(trigger)}" requires a branch record`)
|
|
570
|
+
}
|
|
571
|
+
if (Object.getOwnPropertySymbols(declarations).length > 0) {
|
|
572
|
+
throw new Error(`Machine branching transition for state "${path}" on "${String(trigger)}" cannot use symbol keys`)
|
|
573
|
+
}
|
|
574
|
+
const keys = Object.keys(declarations)
|
|
575
|
+
if (keys.length === 0) {
|
|
576
|
+
throw new Error(`Machine branching transition for state "${path}" on "${String(trigger)}" requires a branch`)
|
|
577
|
+
}
|
|
578
|
+
return Object.freeze(keys.map((key) => {
|
|
579
|
+
if (key.length === 0 || isArrayIndexKey(key)) {
|
|
580
|
+
throw new Error(
|
|
581
|
+
`Machine branching transition for state "${path}" on "${String(trigger)}" requires non-index string branch keys`
|
|
582
|
+
)
|
|
583
|
+
}
|
|
584
|
+
const declaration = (declarations as Record<string, unknown>)[key]
|
|
585
|
+
if (typeof declaration !== "object" || declaration === null || !hasProperty(declaration, "target")) {
|
|
586
|
+
throw new Error(`Machine transition branch "${key}" requires a target selection`)
|
|
587
|
+
}
|
|
588
|
+
const { target, title } = declaration as BranchDeclaration
|
|
589
|
+
if (!Topology.isTargetSelection(target)) {
|
|
590
|
+
throw new Error(`Machine transition branch "${key}" must select exactly one target`)
|
|
591
|
+
}
|
|
592
|
+
if (title !== undefined && (typeof title !== "string" || title.length === 0)) {
|
|
593
|
+
throw new Error(`Machine transition branch "${key}" title must be a non-empty string`)
|
|
594
|
+
}
|
|
595
|
+
return Object.freeze({ key, title: title ?? key, selection: plainTargetSelection(target) })
|
|
596
|
+
}))
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
const wrapSelectedBranchBuilder = (
|
|
600
|
+
builder: unknown,
|
|
601
|
+
owner: object,
|
|
602
|
+
branchIndex: number,
|
|
603
|
+
branchKey: string
|
|
604
|
+
): unknown => {
|
|
605
|
+
if (typeof builder === "function") {
|
|
606
|
+
const wrapped = (...args: ReadonlyArray<unknown>) =>
|
|
607
|
+
Topology.makeSelectedBranch(owner, branchIndex, branchKey, builder(...args))
|
|
608
|
+
for (const property of Reflect.ownKeys(builder)) {
|
|
609
|
+
if (
|
|
610
|
+
property === "length" || property === "name" || property === "prototype" || property === "caller" ||
|
|
611
|
+
property === "arguments"
|
|
612
|
+
) continue
|
|
613
|
+
const descriptor = Object.getOwnPropertyDescriptor(builder, property)
|
|
614
|
+
if (descriptor === undefined) continue
|
|
615
|
+
if ("value" in descriptor && typeof descriptor.value === "function") {
|
|
616
|
+
descriptor.value = wrapSelectedBranchBuilder(descriptor.value, owner, branchIndex, branchKey)
|
|
617
|
+
}
|
|
618
|
+
Object.defineProperty(wrapped, property, descriptor)
|
|
619
|
+
}
|
|
620
|
+
return wrapped
|
|
621
|
+
}
|
|
622
|
+
if (typeof builder === "object" && builder !== null) {
|
|
623
|
+
const wrapped: Record<PropertyKey, unknown> = {}
|
|
624
|
+
for (const property of Reflect.ownKeys(builder)) {
|
|
625
|
+
const descriptor = Object.getOwnPropertyDescriptor(builder, property)
|
|
626
|
+
if (descriptor === undefined) continue
|
|
627
|
+
if ("value" in descriptor && typeof descriptor.value === "function") {
|
|
628
|
+
descriptor.value = wrapSelectedBranchBuilder(descriptor.value, owner, branchIndex, branchKey)
|
|
629
|
+
}
|
|
630
|
+
Object.defineProperty(wrapped, property, descriptor)
|
|
631
|
+
}
|
|
632
|
+
return wrapped
|
|
633
|
+
}
|
|
634
|
+
throw new Error(`Machine could not construct transition branch "${branchKey}"`)
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
const makeBranchSelectors = (
|
|
638
|
+
context: Record<string, any>,
|
|
639
|
+
branches: ReadonlyArray<CapturedNamedBranch>,
|
|
640
|
+
owner: object,
|
|
641
|
+
stateNodes: Machine.StateNodes,
|
|
642
|
+
source: string
|
|
643
|
+
): Readonly<Record<string, unknown>> => {
|
|
644
|
+
const select: Record<string, unknown> = Object.create(null)
|
|
645
|
+
for (let branchIndex = 0; branchIndex < branches.length; branchIndex++) {
|
|
646
|
+
const branch = branches[branchIndex]!
|
|
647
|
+
select[branch.key] = wrapSelectedBranchBuilder(
|
|
648
|
+
getSelectionBuilder(context.target, branch.selection, stateNodes, source),
|
|
649
|
+
owner,
|
|
650
|
+
branchIndex,
|
|
651
|
+
branch.key
|
|
652
|
+
)
|
|
653
|
+
}
|
|
654
|
+
return Object.freeze(select)
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
const validateSelectedBranchResult = (
|
|
658
|
+
result: unknown,
|
|
659
|
+
selection: Topology.TargetSelection,
|
|
660
|
+
stateNodes: Machine.StateNodes
|
|
661
|
+
): void => {
|
|
662
|
+
if (selection.kind === "none") {
|
|
663
|
+
if (!Topology.isNoTarget(result)) {
|
|
664
|
+
throw new Error("Machine targetless branch must return its selected targetless builder")
|
|
665
|
+
}
|
|
666
|
+
return
|
|
667
|
+
}
|
|
668
|
+
if (result === undefined) {
|
|
669
|
+
throw new Error(`Machine transition branch selected "${selection.path}" without constructing its target`)
|
|
670
|
+
}
|
|
671
|
+
validateResolvedSelection(result, selection, stateNodes)
|
|
672
|
+
}
|
|
673
|
+
|
|
353
674
|
const captureTransition = (
|
|
354
|
-
|
|
675
|
+
rawTransition: unknown,
|
|
355
676
|
stateNodes: Machine.StateNodes,
|
|
356
677
|
path: string,
|
|
357
678
|
trigger: PropertyKey
|
|
358
679
|
): unknown => {
|
|
680
|
+
if (typeof rawTransition !== "function") {
|
|
681
|
+
throw new Error(
|
|
682
|
+
`Machine transition for state "${path}" on "${String(trigger)}" must be a target-first callback`
|
|
683
|
+
)
|
|
684
|
+
}
|
|
685
|
+
const transition = normalizeTransitionBuilder(rawTransition as (selector: unknown) => unknown, stateNodes, path)
|
|
359
686
|
if (typeof transition !== "object" || transition === null) {
|
|
360
687
|
throw new Error(`Machine transition for state "${path}" on "${String(trigger)}" must be an object`)
|
|
361
688
|
}
|
|
362
689
|
const definition = transition as Record<PropertyKey, unknown>
|
|
363
690
|
const selector = makeTargetSelector(stateNodes, path)
|
|
364
691
|
const reenter = definition.reenter === true
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
692
|
+
const declinable = definition.declinable === true
|
|
693
|
+
if (hasProperty(definition, "branches")) {
|
|
694
|
+
const branching = definition as { readonly branches: unknown; readonly resolve?: unknown }
|
|
695
|
+
if (typeof branching.branches !== "function" || typeof branching.resolve !== "function") {
|
|
368
696
|
throw new Error(
|
|
369
|
-
`Machine
|
|
697
|
+
`Machine branching transition for state "${path}" on "${String(trigger)}" requires branches and resolve`
|
|
370
698
|
)
|
|
371
699
|
}
|
|
372
|
-
const
|
|
373
|
-
|
|
374
|
-
|
|
700
|
+
const resolve = branching.resolve
|
|
701
|
+
const branches = captureNamedBranches(branching.branches(selector), path, trigger)
|
|
702
|
+
const owner = Object.freeze({})
|
|
703
|
+
const evaluate = (context: Record<string, any>, enqueue: unknown) => {
|
|
704
|
+
const resolverContext = { ...context }
|
|
705
|
+
delete resolverContext.target
|
|
706
|
+
resolverContext.select = makeBranchSelectors(context, branches, owner, stateNodes, path)
|
|
707
|
+
if (declinable) resolverContext.decline = Topology.makeDeclined
|
|
708
|
+
const selected = resolve(resolverContext, enqueue)
|
|
709
|
+
if (Topology.isDeclined(selected)) {
|
|
710
|
+
if (!declinable) {
|
|
711
|
+
throw new Error(
|
|
712
|
+
`Machine branching transition for state "${path}" on "${
|
|
713
|
+
String(trigger)
|
|
714
|
+
}" returned decline without declaring declinable: true`
|
|
715
|
+
)
|
|
716
|
+
}
|
|
717
|
+
return { result: selected, branchIndex: -1, branchKey: undefined }
|
|
718
|
+
}
|
|
719
|
+
if (!Topology.isSelectedBranch(selected) || selected.owner !== owner) {
|
|
375
720
|
throw new Error(
|
|
376
|
-
`Machine
|
|
721
|
+
`Machine branching transition for state "${path}" on "${String(trigger)}" must select one declared branch`
|
|
377
722
|
)
|
|
378
723
|
}
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
const evaluate = (context: Record<string, any>, enqueue: unknown) => {
|
|
383
|
-
const predicateContext = { ...context }
|
|
384
|
-
delete predicateContext.target
|
|
385
|
-
for (let branchIndex = 0; branchIndex < cases.length; branchIndex++) {
|
|
386
|
-
const branch = cases[branchIndex]!
|
|
387
|
-
const result = branch.when!(predicateContext)
|
|
388
|
-
if (!Option.isOption(result)) {
|
|
389
|
-
throw new Error(`Machine conditional transition case "${branch.title}" must return Option`)
|
|
390
|
-
}
|
|
391
|
-
if (Option.isSome(result)) {
|
|
392
|
-
return {
|
|
393
|
-
result: runCapturedBranch(branch, context, enqueue, stateNodes, path, { value: result.value }),
|
|
394
|
-
branchIndex
|
|
395
|
-
}
|
|
396
|
-
}
|
|
724
|
+
const branch = branches[selected.branchIndex]
|
|
725
|
+
if (branch === undefined || selected.branchKey !== branch.key) {
|
|
726
|
+
throw new Error(`Machine branching transition returned invalid branch evidence`)
|
|
397
727
|
}
|
|
728
|
+
validateSelectedBranchResult(selected.result, branch.selection, stateNodes)
|
|
398
729
|
return {
|
|
399
|
-
result:
|
|
400
|
-
branchIndex:
|
|
730
|
+
result: selected.result,
|
|
731
|
+
branchIndex: selected.branchIndex,
|
|
732
|
+
branchKey: selected.branchKey
|
|
401
733
|
}
|
|
402
734
|
}
|
|
403
735
|
return {
|
|
404
736
|
reenter,
|
|
737
|
+
declinable,
|
|
405
738
|
targets: [
|
|
406
739
|
...new Set(
|
|
407
|
-
|
|
740
|
+
branches.flatMap((branch) => branch.selection.path === undefined ? [] : [branch.selection.path])
|
|
408
741
|
)
|
|
409
742
|
],
|
|
410
|
-
branches:
|
|
411
|
-
|
|
412
|
-
type: "
|
|
413
|
-
|
|
743
|
+
branches: branches.map((branch) =>
|
|
744
|
+
Object.freeze({
|
|
745
|
+
type: "branch" as const,
|
|
746
|
+
key: branch.key,
|
|
747
|
+
title: branch.title,
|
|
414
748
|
target: branch.selection.path,
|
|
415
749
|
selection: transitionTargetSelection(branch.selection)
|
|
416
|
-
})
|
|
417
|
-
|
|
418
|
-
type: "otherwise" as const,
|
|
419
|
-
target: otherwise.selection.path,
|
|
420
|
-
selection: transitionTargetSelection(otherwise.selection)
|
|
421
|
-
}
|
|
422
|
-
],
|
|
750
|
+
})
|
|
751
|
+
),
|
|
423
752
|
evaluate,
|
|
424
753
|
transition: (context: Record<string, any>, enqueue: unknown) => evaluate(context, enqueue).result
|
|
425
754
|
}
|
|
@@ -427,10 +756,12 @@ const captureTransition = (
|
|
|
427
756
|
const branch = captureDefinitionBranch(transition, selector, path, trigger)
|
|
428
757
|
const evaluate = (context: Record<string, any>, enqueue: unknown) => ({
|
|
429
758
|
result: runCapturedBranch(branch, context, enqueue, stateNodes, path),
|
|
430
|
-
branchIndex: 0
|
|
759
|
+
branchIndex: 0,
|
|
760
|
+
branchKey: undefined
|
|
431
761
|
})
|
|
432
762
|
return {
|
|
433
763
|
reenter,
|
|
764
|
+
declinable,
|
|
434
765
|
targets: branch.selection.path === undefined ? [] : [branch.selection.path],
|
|
435
766
|
branches: [{
|
|
436
767
|
type: "direct" as const,
|
|
@@ -465,7 +796,7 @@ const captureInvokeDefinition = (
|
|
|
465
796
|
if (Array.isArray(invoke)) return invoke.map((item) => captureInvokeDefinition(item, stateNodes, path))
|
|
466
797
|
if (typeof invoke !== "object" || invoke === null) return invoke
|
|
467
798
|
const captured = { ...(invoke as Record<PropertyKey, unknown>) }
|
|
468
|
-
for (const key of ["onDone", "onFailure", "onSnapshot"] as const) {
|
|
799
|
+
for (const key of ["onElement", "onDone", "onFailure", "onSnapshot"] as const) {
|
|
469
800
|
if (captured[key] !== undefined) {
|
|
470
801
|
captured[key] = captureTransition(captured[key], stateNodes, path, key)
|
|
471
802
|
}
|
|
@@ -1072,10 +1403,12 @@ const makeInitialSelector = (stateNodes: Machine.StateNodes): unknown => {
|
|
|
1072
1403
|
const selector: Record<string, unknown> = {}
|
|
1073
1404
|
for (const node of stateNodes.byPath.values()) {
|
|
1074
1405
|
if (node.parent === undefined && node.type !== "history" && node.type !== "choice") {
|
|
1075
|
-
selector[node.key] =
|
|
1406
|
+
selector[node.key] = node.type === "atomic" || node.type === "final"
|
|
1407
|
+
? makeSelectionMethod("state", node.path, "initial")
|
|
1408
|
+
: Object.freeze({ initial: makeSelectionValue("initial", node.path, "initial") })
|
|
1076
1409
|
}
|
|
1077
1410
|
}
|
|
1078
|
-
return selector
|
|
1411
|
+
return Object.freeze(selector)
|
|
1079
1412
|
}
|
|
1080
1413
|
|
|
1081
1414
|
const getInitialSelectionBuilder = (
|
|
@@ -1094,15 +1427,37 @@ const getInitialSelectionBuilder = (
|
|
|
1094
1427
|
}
|
|
1095
1428
|
|
|
1096
1429
|
const captureInitialBranch = (
|
|
1097
|
-
|
|
1098
|
-
|
|
1430
|
+
definition: unknown,
|
|
1431
|
+
stateNodes: Machine.StateNodes,
|
|
1099
1432
|
initialBuilder: Record<string, any>
|
|
1100
|
-
):
|
|
1101
|
-
|
|
1102
|
-
|
|
1433
|
+
): {
|
|
1434
|
+
readonly selection: Topology.TargetSelection
|
|
1435
|
+
readonly resolve?: (context: any) => unknown
|
|
1436
|
+
readonly builder: (...args: ReadonlyArray<any>) => unknown
|
|
1437
|
+
} => {
|
|
1438
|
+
if (typeof definition !== "function") {
|
|
1439
|
+
throw new Error("Machine initial definition must be a target-first callback")
|
|
1440
|
+
}
|
|
1441
|
+
const result = definition(decorateInitialSelectorNode(makeInitialSelector(stateNodes)))
|
|
1442
|
+
let selection: Topology.TargetSelection
|
|
1443
|
+
let resolve: ((context: any) => unknown) | undefined
|
|
1444
|
+
if (Topology.isTargetSelection(result)) {
|
|
1445
|
+
selection = plainTargetSelection(result)
|
|
1446
|
+
} else if (hasProperty(result, InitialBuilderDescriptorTypeId)) {
|
|
1447
|
+
const descriptor = result as InitialBuilderDescriptor
|
|
1448
|
+
selection = descriptor.selection
|
|
1449
|
+
resolve = descriptor.resolve
|
|
1450
|
+
} else {
|
|
1451
|
+
throw new Error("Machine initial definition must select exactly one target")
|
|
1452
|
+
}
|
|
1453
|
+
if (selection.kind !== "state" && selection.kind !== "initial") {
|
|
1103
1454
|
throw new Error("Machine initial target must select a top-level state or its declared initial entry")
|
|
1104
1455
|
}
|
|
1105
|
-
|
|
1456
|
+
const captured = {
|
|
1457
|
+
selection,
|
|
1458
|
+
builder: getInitialSelectionBuilder(initialBuilder, selection)
|
|
1459
|
+
}
|
|
1460
|
+
return resolve === undefined ? Object.freeze(captured) : Object.freeze({ ...captured, resolve })
|
|
1106
1461
|
}
|
|
1107
1462
|
|
|
1108
1463
|
const validateInitialSelection = (result: unknown, selection: Topology.TargetSelection): void => {
|
|
@@ -1124,17 +1479,13 @@ const compileInitial = (
|
|
|
1124
1479
|
readonly initial: (input?: unknown) => unknown
|
|
1125
1480
|
readonly definition: Machine.InitialDefinition
|
|
1126
1481
|
} => {
|
|
1127
|
-
if (typeof definition !== "object" || definition === null) {
|
|
1128
|
-
throw new Error("Machine initial definition must be an object")
|
|
1129
|
-
}
|
|
1130
|
-
const selector = makeInitialSelector(stateNodes)
|
|
1131
1482
|
const initialBuilder = makeSnapshotBuilder(states, { mode: "initial", prefix: "" }) as Record<string, any>
|
|
1132
|
-
const branch = captureInitialBranch(definition,
|
|
1483
|
+
const branch = captureInitialBranch(definition, stateNodes, initialBuilder)
|
|
1133
1484
|
return {
|
|
1134
1485
|
initial: (input?: unknown) => {
|
|
1135
1486
|
const result = branch.resolve === undefined
|
|
1136
|
-
? branch.builder
|
|
1137
|
-
: branch.resolve({ input, target: branch.builder }
|
|
1487
|
+
? constructSelectedTarget(branch.builder)
|
|
1488
|
+
: branch.resolve({ input, target: branch.builder })
|
|
1138
1489
|
validateInitialSelection(result, branch.selection)
|
|
1139
1490
|
return result
|
|
1140
1491
|
},
|
|
@@ -1184,7 +1535,7 @@ type MakeConfig<
|
|
|
1184
1535
|
InitialE,
|
|
1185
1536
|
InitialR,
|
|
1186
1537
|
InternalEvents extends ReadonlyArray<Machine.TaggedSchema>,
|
|
1187
|
-
|
|
1538
|
+
ParentDeclaration extends Parent.Any | undefined
|
|
1188
1539
|
> = {
|
|
1189
1540
|
readonly id?: string
|
|
1190
1541
|
readonly states: States & DefineStateTreeInput<NoInfer<States>>
|
|
@@ -1198,7 +1549,7 @@ type MakeConfig<
|
|
|
1198
1549
|
NoInfer<InternalEvents>
|
|
1199
1550
|
>
|
|
1200
1551
|
readonly emittedEvents?: Machine.EventProtocol<"emitted", Emits>
|
|
1201
|
-
readonly
|
|
1552
|
+
readonly parent?: ParentDeclaration
|
|
1202
1553
|
readonly input?: Input
|
|
1203
1554
|
readonly initial: unknown
|
|
1204
1555
|
}
|
|
@@ -1211,7 +1562,7 @@ type MakeResult<
|
|
|
1211
1562
|
InitialE,
|
|
1212
1563
|
InitialR,
|
|
1213
1564
|
InternalEvents extends ReadonlyArray<Machine.TaggedSchema>,
|
|
1214
|
-
|
|
1565
|
+
ParentDeclaration extends Parent.Any | undefined
|
|
1215
1566
|
> = Definition<
|
|
1216
1567
|
States,
|
|
1217
1568
|
readonly [...InputEvents, ...InternalEvents],
|
|
@@ -1222,7 +1573,7 @@ type MakeResult<
|
|
|
1222
1573
|
Machine.TerminalOutput<States>,
|
|
1223
1574
|
Emits,
|
|
1224
1575
|
InputEvents,
|
|
1225
|
-
|
|
1576
|
+
Machine.ParentEventsOf<ParentDeclaration>
|
|
1226
1577
|
>
|
|
1227
1578
|
|
|
1228
1579
|
interface Make {
|
|
@@ -1234,11 +1585,11 @@ interface Make {
|
|
|
1234
1585
|
InitialE = never,
|
|
1235
1586
|
InitialR = never,
|
|
1236
1587
|
const InternalEvents extends ReadonlyArray<Machine.TaggedSchema> = readonly [],
|
|
1237
|
-
const
|
|
1588
|
+
const ParentDeclaration extends Parent.Any | undefined = undefined
|
|
1238
1589
|
>(
|
|
1239
|
-
config: MakeConfig<States, InputEvents, Emits, Input, InitialE, InitialR, InternalEvents,
|
|
1590
|
+
config: MakeConfig<States, InputEvents, Emits, Input, InitialE, InitialR, InternalEvents, ParentDeclaration>,
|
|
1240
1591
|
..._validation: ValidateDefinedStates<NoInfer<States>>
|
|
1241
|
-
): MakeResult<States, InputEvents, Emits, Input, InitialE, InitialR, InternalEvents,
|
|
1592
|
+
): MakeResult<States, InputEvents, Emits, Input, InitialE, InitialR, InternalEvents, ParentDeclaration>
|
|
1242
1593
|
<
|
|
1243
1594
|
const States extends Machine.StateSchemas,
|
|
1244
1595
|
const InputEvents extends ReadonlyArray<Machine.TaggedSchema>,
|
|
@@ -1247,10 +1598,13 @@ interface Make {
|
|
|
1247
1598
|
InitialE = never,
|
|
1248
1599
|
InitialR = never,
|
|
1249
1600
|
const InternalEvents extends ReadonlyArray<Machine.TaggedSchema> = readonly [],
|
|
1250
|
-
const
|
|
1601
|
+
const ParentDeclaration extends Parent.Any | undefined = undefined
|
|
1251
1602
|
>(
|
|
1252
1603
|
config:
|
|
1253
|
-
& Omit<
|
|
1604
|
+
& Omit<
|
|
1605
|
+
MakeConfig<States, InputEvents, Emits, Input, InitialE, InitialR, InternalEvents, ParentDeclaration>,
|
|
1606
|
+
"states"
|
|
1607
|
+
>
|
|
1254
1608
|
& { readonly states: InvalidDefinedStateTreeInput<States> }
|
|
1255
1609
|
): never
|
|
1256
1610
|
}
|
|
@@ -1263,7 +1617,7 @@ export const make: Make = (<
|
|
|
1263
1617
|
InitialE = never,
|
|
1264
1618
|
InitialR = never,
|
|
1265
1619
|
const InternalEvents extends ReadonlyArray<Machine.TaggedSchema> = readonly [],
|
|
1266
|
-
const
|
|
1620
|
+
const ParentDeclaration extends Parent.Any | undefined = undefined
|
|
1267
1621
|
>(
|
|
1268
1622
|
config: {
|
|
1269
1623
|
readonly id?: string
|
|
@@ -1271,18 +1625,18 @@ export const make: Make = (<
|
|
|
1271
1625
|
readonly events: Machine.EventProtocol<"public", InputEvents>
|
|
1272
1626
|
readonly internalEvents?: Machine.EventProtocol<"internal", InternalEvents>
|
|
1273
1627
|
readonly emittedEvents?: Machine.EventProtocol<"emitted", Emits>
|
|
1274
|
-
readonly
|
|
1628
|
+
readonly parent?: ParentDeclaration
|
|
1275
1629
|
readonly input?: Input
|
|
1276
1630
|
readonly initial: unknown
|
|
1277
1631
|
}
|
|
1278
|
-
): MakeResult<States, InputEvents, Emits, Input, InitialE, InitialR, InternalEvents,
|
|
1632
|
+
): MakeResult<States, InputEvents, Emits, Input, InitialE, InitialR, InternalEvents, ParentDeclaration> => {
|
|
1279
1633
|
StateDefinition.validateStateDefinitions(config.states, "Machine.make")
|
|
1280
1634
|
const self = Object.create(Proto)
|
|
1281
1635
|
self.states = config.states
|
|
1282
1636
|
self.events = config.events
|
|
1283
1637
|
self.internalEvents = config.internalEvents ?? Protocol.makeEventProtocol("internal", [] as const)
|
|
1284
1638
|
self.emittedEvents = config.emittedEvents ?? Protocol.makeEventProtocol("emitted", [] as const)
|
|
1285
|
-
self.
|
|
1639
|
+
self.parent = config.parent
|
|
1286
1640
|
self.input = config.input
|
|
1287
1641
|
self.id = config.id
|
|
1288
1642
|
self.stateNodes = Topology.compileStateNodes(config.states)
|
|
@@ -1292,7 +1646,6 @@ export const make: Make = (<
|
|
|
1292
1646
|
self.makeTargetBuilder = makeTargetBuilder(config.states, self.stateNodes)
|
|
1293
1647
|
self.handlers = Object.create(null)
|
|
1294
1648
|
self.handle = makeHandle(self)
|
|
1295
|
-
self.invoke = makeBoundInvoke
|
|
1296
1649
|
Protocol.setProtocol(self)
|
|
1297
1650
|
return self
|
|
1298
1651
|
}) as Make
|
|
@@ -1315,6 +1668,27 @@ export const events = <const Inputs extends ReadonlyArray<Machine.EventProtocolI
|
|
|
1315
1668
|
flattenEventProtocolInputs("public", inputs)
|
|
1316
1669
|
) as Machine.EventProtocol<"public", Machine.EventProtocolInputSchemasOf<"public", Inputs>>
|
|
1317
1670
|
|
|
1671
|
+
const makeParent = <
|
|
1672
|
+
const Mode extends ParentMode,
|
|
1673
|
+
const Events extends ReadonlyArray<Machine.TaggedSchema>
|
|
1674
|
+
>(
|
|
1675
|
+
mode: Mode,
|
|
1676
|
+
events: Machine.EventProtocol<"public", Events>
|
|
1677
|
+
): Parent<Mode, Events> => {
|
|
1678
|
+
if (!Protocol.isEventProtocol(events, "public")) {
|
|
1679
|
+
throw new Error("Machine parent declarations require a protocol created with Machine.events")
|
|
1680
|
+
}
|
|
1681
|
+
return Object.freeze({ [ParentTypeId]: ParentTypeId, mode, events })
|
|
1682
|
+
}
|
|
1683
|
+
|
|
1684
|
+
export const parent = <const Events extends ReadonlyArray<Machine.TaggedSchema>>(
|
|
1685
|
+
events: Machine.EventProtocol<"public", Events>
|
|
1686
|
+
): Parent<"required", Events> => makeParent("required", events)
|
|
1687
|
+
|
|
1688
|
+
export const optionalParent = <const Events extends ReadonlyArray<Machine.TaggedSchema>>(
|
|
1689
|
+
events: Machine.EventProtocol<"public", Events>
|
|
1690
|
+
): Parent<"optional", Events> => makeParent("optional", events)
|
|
1691
|
+
|
|
1318
1692
|
export const internalEvents = <const Inputs extends ReadonlyArray<Machine.EventProtocolInput<"internal">>>(
|
|
1319
1693
|
...inputs: Inputs
|
|
1320
1694
|
): Machine.EventProtocol<"internal", Machine.EventProtocolInputSchemasOf<"internal", Inputs>> =>
|