@typeonce/effect-machine 0.11.0 → 0.13.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 +89 -13
- package/dist/Machine.d.ts +396 -127
- package/dist/Machine.d.ts.map +1 -1
- package/dist/Machine.js +21 -19
- package/dist/Machine.js.map +1 -1
- package/dist/internal/machine/atom.d.ts +1 -0
- package/dist/internal/machine/atom.d.ts.map +1 -1
- package/dist/internal/machine/atom.js +23 -0
- package/dist/internal/machine/atom.js.map +1 -1
- package/dist/internal/machine/cluster.js +2 -2
- package/dist/internal/machine/cluster.js.map +1 -1
- package/dist/internal/machine/commandRuntime.d.ts.map +1 -1
- package/dist/internal/machine/commandRuntime.js +2 -5
- package/dist/internal/machine/commandRuntime.js.map +1 -1
- package/dist/internal/machine/configuration.d.ts +12 -7
- package/dist/internal/machine/configuration.d.ts.map +1 -1
- package/dist/internal/machine/configuration.js +9 -5
- package/dist/internal/machine/configuration.js.map +1 -1
- package/dist/internal/machine/executionPlan.d.ts +4 -3
- package/dist/internal/machine/executionPlan.d.ts.map +1 -1
- package/dist/internal/machine/executionPlan.js +68 -43
- package/dist/internal/machine/executionPlan.js.map +1 -1
- package/dist/internal/machine/initialization.d.ts +5 -0
- package/dist/internal/machine/initialization.d.ts.map +1 -0
- package/dist/internal/machine/initialization.js +61 -0
- package/dist/internal/machine/initialization.js.map +1 -0
- package/dist/internal/machine/inspectionRuntime.d.ts +25 -0
- package/dist/internal/machine/inspectionRuntime.d.ts.map +1 -0
- package/dist/internal/machine/inspectionRuntime.js +79 -0
- package/dist/internal/machine/inspectionRuntime.js.map +1 -0
- package/dist/internal/machine/invocation.d.ts +2 -1
- package/dist/internal/machine/invocation.d.ts.map +1 -1
- package/dist/internal/machine/invocation.js +18 -11
- package/dist/internal/machine/invocation.js.map +1 -1
- package/dist/internal/machine/machine.d.ts +1 -2
- package/dist/internal/machine/machine.d.ts.map +1 -1
- package/dist/internal/machine/machine.js +21 -6
- package/dist/internal/machine/machine.js.map +1 -1
- package/dist/internal/machine/planner.d.ts +17 -2
- package/dist/internal/machine/planner.d.ts.map +1 -1
- package/dist/internal/machine/planner.js +137 -64
- package/dist/internal/machine/planner.js.map +1 -1
- package/dist/internal/machine/process.d.ts.map +1 -1
- package/dist/internal/machine/process.js +7 -5
- package/dist/internal/machine/process.js.map +1 -1
- package/dist/internal/machine/runtime.d.ts +29 -5
- package/dist/internal/machine/runtime.d.ts.map +1 -1
- package/dist/internal/machine/runtime.js +440 -64
- package/dist/internal/machine/runtime.js.map +1 -1
- package/dist/internal/machine/symbols.d.ts +2 -0
- package/dist/internal/machine/symbols.d.ts.map +1 -1
- package/dist/internal/machine/symbols.js +2 -0
- package/dist/internal/machine/symbols.js.map +1 -1
- package/dist/internal/machine/topology.d.ts +13 -0
- package/dist/internal/machine/topology.d.ts.map +1 -1
- package/dist/internal/machine/topology.js +9 -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 +3 -3
- package/dist/internal/testing/machine/finiteModel.js.map +1 -1
- package/dist/unstable/reactivity/AtomMachine.d.ts +9 -0
- package/dist/unstable/reactivity/AtomMachine.d.ts.map +1 -1
- package/dist/unstable/reactivity/AtomMachine.js +9 -0
- package/dist/unstable/reactivity/AtomMachine.js.map +1 -1
- package/docs/agent-guide.md +130 -20
- package/package.json +5 -5
- package/src/Machine.ts +548 -217
- package/src/internal/machine/atom.ts +26 -0
- package/src/internal/machine/cluster.ts +2 -2
- package/src/internal/machine/commandRuntime.ts +2 -7
- package/src/internal/machine/configuration.ts +38 -13
- package/src/internal/machine/executionPlan.ts +85 -52
- package/src/internal/machine/initialization.ts +73 -0
- package/src/internal/machine/inspectionRuntime.ts +102 -0
- package/src/internal/machine/invocation.ts +38 -13
- package/src/internal/machine/machine.ts +31 -6
- package/src/internal/machine/planner.ts +166 -66
- package/src/internal/machine/process.ts +7 -5
- package/src/internal/machine/runtime.ts +663 -91
- package/src/internal/machine/symbols.ts +3 -0
- package/src/internal/machine/topology.ts +27 -0
- package/src/internal/testing/machine/finiteModel.ts +8 -7
- package/src/unstable/reactivity/AtomMachine.ts +12 -0
|
@@ -139,6 +139,32 @@ export const emissions = <State, Event, Error, Output, StartError, Emitted>(
|
|
|
139
139
|
)
|
|
140
140
|
}
|
|
141
141
|
|
|
142
|
+
export const inspection = <State, Event, Error, Output, StartError, Emitted>(
|
|
143
|
+
self: MachineAtom<State, Event, Error, Output, StartError, Emitted>
|
|
144
|
+
): Stream.Stream<Machine.Inspection.Event, StartError, AtomRegistry.AtomRegistry> => {
|
|
145
|
+
const prepared = preparedByMachineAtom.get(self as object)
|
|
146
|
+
if (prepared === undefined) return Stream.empty
|
|
147
|
+
return Stream.unwrap(
|
|
148
|
+
Effect.gen(function*() {
|
|
149
|
+
const registry = yield* AtomRegistry.AtomRegistry
|
|
150
|
+
const releasePrepared = yield* Effect.sync(() => registry.mount(prepared))
|
|
151
|
+
yield* Effect.addFinalizer(() => Effect.sync(releasePrepared))
|
|
152
|
+
const machine = yield* Atom.getResult(prepared)
|
|
153
|
+
const pull = yield* Stream.toPull(machine.inspection)
|
|
154
|
+
const firstPull = yield* pull.pipe(Effect.forkScoped({ startImmediately: true }))
|
|
155
|
+
const releaseRef = yield* Effect.sync(() => registry.mount(self.ref))
|
|
156
|
+
yield* Effect.addFinalizer(() => Effect.sync(releaseRef))
|
|
157
|
+
yield* Atom.getResult(self.ref)
|
|
158
|
+
let first = true
|
|
159
|
+
return Stream.fromPull(Effect.succeed(Effect.suspend(() => {
|
|
160
|
+
if (!first) return pull
|
|
161
|
+
first = false
|
|
162
|
+
return Fiber.join(firstPull)
|
|
163
|
+
})))
|
|
164
|
+
})
|
|
165
|
+
)
|
|
166
|
+
}
|
|
167
|
+
|
|
142
168
|
export const childEmissions = <Child extends Machine.ChildMachine.Any, StartError>(
|
|
143
169
|
self: ChildMachineAtom<Child, StartError>
|
|
144
170
|
): Stream.Stream<RefEmitted<Machine.ChildMachine.Ref<Child>>, StartError, AtomRegistry.AtomRegistry> =>
|
|
@@ -317,7 +317,7 @@ export const make = <
|
|
|
317
317
|
if (initial.commands.length > 0) {
|
|
318
318
|
return yield* fail(
|
|
319
319
|
"UnsupportedProcessLocal",
|
|
320
|
-
"Machine
|
|
320
|
+
"Machine commands require a managed local process"
|
|
321
321
|
)
|
|
322
322
|
}
|
|
323
323
|
current = initial.state
|
|
@@ -329,7 +329,7 @@ export const make = <
|
|
|
329
329
|
if (planned.commands.length > 0) {
|
|
330
330
|
return yield* fail(
|
|
331
331
|
"UnsupportedProcessLocal",
|
|
332
|
-
"Machine
|
|
332
|
+
"Machine commands require a managed local process"
|
|
333
333
|
)
|
|
334
334
|
}
|
|
335
335
|
current = planned.next
|
|
@@ -10,16 +10,13 @@ import type { RuntimeCommand } from "./command.js"
|
|
|
10
10
|
import { decodeEmit, decodeEvent } from "./protocol.js"
|
|
11
11
|
import type { ProcessScope } from "./runtime.js"
|
|
12
12
|
|
|
13
|
-
const isActorRef = (target: unknown): target is { readonly send: (event: unknown) => Effect.Effect<void, unknown> } =>
|
|
14
|
-
typeof target === "object" && target !== null && "send" in target && typeof target.send === "function"
|
|
15
|
-
|
|
16
13
|
export const makeLiveRuntime = <Events, Emits>(
|
|
17
14
|
machine: Machine.Any,
|
|
18
15
|
scope: ProcessScope<Events>
|
|
19
16
|
): Runtime<Events, Emits> => ({
|
|
20
17
|
raise: (event) =>
|
|
21
18
|
decodeEvent(machine, event).pipe(
|
|
22
|
-
Effect.flatMap((event) => scope.self
|
|
19
|
+
Effect.flatMap((event) => scope.sendTo(scope.self, event as Events))
|
|
23
20
|
),
|
|
24
21
|
emit: (event) =>
|
|
25
22
|
decodeEmit(machine, event).pipe(
|
|
@@ -33,9 +30,7 @@ export const runCommands = <Event>(
|
|
|
33
30
|
) =>
|
|
34
31
|
Effect.forEach(commands, (command) =>
|
|
35
32
|
command._tag === "SendTo"
|
|
36
|
-
?
|
|
37
|
-
? command.target.send(command.event)
|
|
38
|
-
: scope.sendTo(command.target as never, command.event)
|
|
33
|
+
? scope.sendTo(command.target as never, command.event)
|
|
39
34
|
: scope.stopChild(command.child as never), { discard: true })
|
|
40
35
|
|
|
41
36
|
export const runEmittedEvents = <Events, Emits>(
|
|
@@ -8,7 +8,7 @@ import * as Cause from "effect/Cause"
|
|
|
8
8
|
import * as Effect from "effect/Effect"
|
|
9
9
|
import * as Option from "effect/Option"
|
|
10
10
|
import { hasProperty } from "effect/Predicate"
|
|
11
|
-
import type {
|
|
11
|
+
import type { Machine, MachineTarget } from "../../Machine.js"
|
|
12
12
|
import { MachineSchemaDecodeError } from "./errors.js"
|
|
13
13
|
import {
|
|
14
14
|
decodeBoundary,
|
|
@@ -17,7 +17,14 @@ import {
|
|
|
17
17
|
decodeStateValue,
|
|
18
18
|
decodeStateValueSync
|
|
19
19
|
} from "./protocol.js"
|
|
20
|
-
import {
|
|
20
|
+
import {
|
|
21
|
+
getNode,
|
|
22
|
+
getStateNodeSchema,
|
|
23
|
+
type InitialTarget as InitialTargetInstruction,
|
|
24
|
+
isSnapshot,
|
|
25
|
+
isTarget,
|
|
26
|
+
TargetSnapshotTypeId
|
|
27
|
+
} from "./topology.js"
|
|
21
28
|
|
|
22
29
|
export interface HistoryRecord {
|
|
23
30
|
readonly mode: "shallow" | "deep"
|
|
@@ -252,25 +259,25 @@ export interface ActiveConfiguration {
|
|
|
252
259
|
readonly values: ReadonlyMap<string, unknown>
|
|
253
260
|
readonly outputs: ReadonlyMap<string, unknown>
|
|
254
261
|
readonly history: ReadonlyMap<string, HistoryRecord>
|
|
255
|
-
readonly
|
|
262
|
+
readonly machineReferences?: PlanningMachineReferences
|
|
256
263
|
}
|
|
257
264
|
|
|
258
|
-
export interface
|
|
259
|
-
readonly self:
|
|
260
|
-
readonly parent:
|
|
265
|
+
export interface PlanningMachineReferences {
|
|
266
|
+
readonly self: MachineTarget<any>
|
|
267
|
+
readonly parent: MachineTarget<any> | undefined
|
|
261
268
|
}
|
|
262
269
|
|
|
263
|
-
export const
|
|
270
|
+
export const withMachineReferences = (
|
|
264
271
|
configuration: ActiveConfiguration,
|
|
265
|
-
|
|
272
|
+
machineReferences: PlanningMachineReferences
|
|
266
273
|
): ActiveConfiguration => ({
|
|
267
274
|
...configuration,
|
|
268
|
-
|
|
275
|
+
machineReferences: { self: machineReferences.self, parent: machineReferences.parent }
|
|
269
276
|
})
|
|
270
277
|
|
|
271
|
-
export const
|
|
278
|
+
export const getMachineReferences = (
|
|
272
279
|
configuration: ActiveConfiguration
|
|
273
|
-
):
|
|
280
|
+
): PlanningMachineReferences | undefined => configuration.machineReferences
|
|
274
281
|
|
|
275
282
|
export interface FinalCompletion {
|
|
276
283
|
readonly path: string
|
|
@@ -952,7 +959,8 @@ const configurationFromTargetPathSync = (
|
|
|
952
959
|
current: ActiveConfiguration,
|
|
953
960
|
path: string,
|
|
954
961
|
value: unknown,
|
|
955
|
-
providedValues: Readonly<Record<string, unknown>> | undefined
|
|
962
|
+
providedValues: Readonly<Record<string, unknown>> | undefined,
|
|
963
|
+
allowIncomplete = false
|
|
956
964
|
): ActiveConfiguration => {
|
|
957
965
|
const node = getNode(machine, path)
|
|
958
966
|
const active = new Set<string>()
|
|
@@ -998,12 +1006,29 @@ const configurationFromTargetPathSync = (
|
|
|
998
1006
|
}
|
|
999
1007
|
}
|
|
1000
1008
|
|
|
1001
|
-
if (node.type === "compound" || node.type === "parallel") {
|
|
1009
|
+
if (!allowIncomplete && (node.type === "compound" || node.type === "parallel")) {
|
|
1002
1010
|
throw new Error(`Machine target "${node.path}" must include an active child state`)
|
|
1003
1011
|
}
|
|
1004
1012
|
return { active, values, outputs, history: current.history }
|
|
1005
1013
|
}
|
|
1006
1014
|
|
|
1015
|
+
/** Builds the selected path and its ancestors without requiring nested active
|
|
1016
|
+
* children. The planner completes those children through declared initial
|
|
1017
|
+
* topology and `initialize` handlers before normalization continues. */
|
|
1018
|
+
export const configurationFromInitialTargetSync = (
|
|
1019
|
+
machine: Machine.Any,
|
|
1020
|
+
current: ActiveConfiguration,
|
|
1021
|
+
target: InitialTargetInstruction
|
|
1022
|
+
): ActiveConfiguration =>
|
|
1023
|
+
configurationFromTargetPathSync(
|
|
1024
|
+
machine,
|
|
1025
|
+
current,
|
|
1026
|
+
target.path,
|
|
1027
|
+
target.value,
|
|
1028
|
+
target.values as Readonly<Record<string, unknown>> | undefined,
|
|
1029
|
+
true
|
|
1030
|
+
)
|
|
1031
|
+
|
|
1007
1032
|
const configurationFromTargetSnapshotSync = (
|
|
1008
1033
|
machine: Machine.Any,
|
|
1009
1034
|
current: ActiveConfiguration,
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import * as Effect from "effect/Effect"
|
|
8
|
-
import type {
|
|
8
|
+
import type { Machine, MachineTarget } from "../../Machine.js"
|
|
9
9
|
import { getTargetBuilder, type RuntimeCommand } from "./command.js"
|
|
10
10
|
import {
|
|
11
11
|
type ActiveConfiguration,
|
|
@@ -18,10 +18,10 @@ import {
|
|
|
18
18
|
isDescendantOf,
|
|
19
19
|
normalizeConfigurationSync,
|
|
20
20
|
normalizeTargetConfigurationSync,
|
|
21
|
-
type
|
|
21
|
+
type PlanningMachineReferences,
|
|
22
22
|
snapshotFromConfiguration,
|
|
23
23
|
validateInitialConfiguration,
|
|
24
|
-
|
|
24
|
+
withMachineReferences
|
|
25
25
|
} from "./configuration.js"
|
|
26
26
|
import { InfiniteTransitionError, StoppedError } from "./errors.js"
|
|
27
27
|
import * as InvocationEvent from "./invocationEvent.js"
|
|
@@ -38,6 +38,7 @@ import {
|
|
|
38
38
|
normalizeTransition,
|
|
39
39
|
planConfiguration,
|
|
40
40
|
removeConflictingTransitions,
|
|
41
|
+
resolveInitialTarget,
|
|
41
42
|
type SelectedTransition,
|
|
42
43
|
sortEntryPaths,
|
|
43
44
|
sortEvaluatedTransitions,
|
|
@@ -46,7 +47,7 @@ import {
|
|
|
46
47
|
validateDeclaredTransitionTarget
|
|
47
48
|
} from "./planner.js"
|
|
48
49
|
import { decodeEmitSync, decodeEventSync, decodeInputSync, decodeStateValueSync } from "./protocol.js"
|
|
49
|
-
import { isNoTarget, isSnapshot, isTarget, TargetSnapshotTypeId } from "./topology.js"
|
|
50
|
+
import { isInitialTarget, isNoTarget, isSnapshot, isTarget, TargetSnapshotTypeId } from "./topology.js"
|
|
50
51
|
|
|
51
52
|
interface IndexedExecutionDescriptor {
|
|
52
53
|
readonly flat: boolean
|
|
@@ -67,31 +68,31 @@ interface IndexedExecutionDescriptor {
|
|
|
67
68
|
>
|
|
68
69
|
}
|
|
69
70
|
|
|
70
|
-
const
|
|
71
|
+
const planningMachineReferences = new WeakMap<Machine.Any, PlanningMachineReferences>()
|
|
71
72
|
|
|
72
|
-
const
|
|
73
|
-
const cached =
|
|
73
|
+
const getPlanningMachineReferences = (machine: Machine.Any): PlanningMachineReferences => {
|
|
74
|
+
const cached = planningMachineReferences.get(machine)
|
|
74
75
|
if (cached !== undefined) return cached
|
|
75
|
-
const self:
|
|
76
|
+
const self: MachineTarget<unknown> = {
|
|
76
77
|
id: machine.id ?? "Machine",
|
|
77
78
|
sessionId: "Machine.plan",
|
|
78
79
|
send: () => Effect.fail(new StoppedError())
|
|
79
80
|
}
|
|
80
81
|
const scope = { self, parent: undefined }
|
|
81
|
-
|
|
82
|
+
planningMachineReferences.set(machine, scope)
|
|
82
83
|
return scope
|
|
83
84
|
}
|
|
84
85
|
|
|
85
|
-
const
|
|
86
|
+
const resolveMachineReferences = (
|
|
86
87
|
machine: Machine.Any,
|
|
87
|
-
|
|
88
|
-
):
|
|
89
|
-
|
|
90
|
-
?
|
|
91
|
-
: { self:
|
|
88
|
+
machineReferences: PlanningMachineReferences | undefined
|
|
89
|
+
): PlanningMachineReferences =>
|
|
90
|
+
machineReferences === undefined
|
|
91
|
+
? getPlanningMachineReferences(machine)
|
|
92
|
+
: { self: machineReferences.self, parent: machineReferences.parent }
|
|
92
93
|
|
|
93
94
|
const indexedStateConfigKeys: ReadonlySet<PropertyKey> = new Set([
|
|
94
|
-
"
|
|
95
|
+
"initialize",
|
|
95
96
|
"invoke",
|
|
96
97
|
"on",
|
|
97
98
|
"output"
|
|
@@ -363,7 +364,7 @@ const makeIndexedTransitionContext = (
|
|
|
363
364
|
configuration: OwnedIndexedState,
|
|
364
365
|
sourceIndex: number,
|
|
365
366
|
event: any,
|
|
366
|
-
|
|
367
|
+
machineReferences?: PlanningMachineReferences
|
|
367
368
|
): any => {
|
|
368
369
|
const source = descriptor.nodes[sourceIndex]!
|
|
369
370
|
const parentIndex = descriptor.parentIndices[sourceIndex]!
|
|
@@ -372,7 +373,7 @@ const makeIndexedTransitionContext = (
|
|
|
372
373
|
ancestors[descriptor.nodes[ancestorIndex]!.path] = configuration.values[ancestorIndex]
|
|
373
374
|
}
|
|
374
375
|
return {
|
|
375
|
-
...
|
|
376
|
+
...resolveMachineReferences(machine, machineReferences),
|
|
376
377
|
state: configuration.values[sourceIndex],
|
|
377
378
|
containingState: parentIndex < 0 ? undefined : configuration.values[parentIndex],
|
|
378
379
|
ancestors,
|
|
@@ -402,6 +403,7 @@ const emptyExecutionValues: ReadonlyArray<never> = Object.freeze([])
|
|
|
402
403
|
export interface ExecutionMicrostep<State = unknown> {
|
|
403
404
|
readonly next: State
|
|
404
405
|
readonly event: unknown
|
|
406
|
+
readonly transitions?: ReadonlyArray<Machine.RetainedTransition>
|
|
405
407
|
readonly commands: ReadonlyArray<RuntimeCommand>
|
|
406
408
|
readonly raisedEvents: ReadonlyArray<unknown>
|
|
407
409
|
readonly emittedEvents: ReadonlyArray<unknown>
|
|
@@ -454,7 +456,7 @@ const selectIndexedEventTransitions = (
|
|
|
454
456
|
descriptor: IndexedExecutionDescriptor,
|
|
455
457
|
configuration: OwnedIndexedState,
|
|
456
458
|
event: any,
|
|
457
|
-
|
|
459
|
+
machineReferences?: PlanningMachineReferences
|
|
458
460
|
): ReadonlyArray<IndexedSelectedTransition> => {
|
|
459
461
|
const selected: Array<IndexedSelectedTransition> = []
|
|
460
462
|
for (const leafIndex of configuration.activeLeaves) {
|
|
@@ -476,7 +478,7 @@ const selectIndexedEventTransitions = (
|
|
|
476
478
|
configuration,
|
|
477
479
|
sourceIndex,
|
|
478
480
|
event,
|
|
479
|
-
|
|
481
|
+
machineReferences
|
|
480
482
|
)
|
|
481
483
|
})
|
|
482
484
|
}
|
|
@@ -530,13 +532,22 @@ const collectIndexedEvaluatedTransition = (
|
|
|
530
532
|
selection: IndexedSelectedTransition
|
|
531
533
|
): IndexedEvaluatedTransition => {
|
|
532
534
|
const transitionResult = collectIndexedTransition(machine, selection.transition.transition, selection.context)
|
|
533
|
-
const
|
|
535
|
+
const unresolvedTarget = transitionResult.state
|
|
534
536
|
validateDeclaredTransitionTarget(
|
|
535
537
|
selection.sourcePath,
|
|
536
538
|
selection.trigger,
|
|
537
539
|
selection.transition.targets,
|
|
538
|
-
|
|
540
|
+
unresolvedTarget
|
|
539
541
|
)
|
|
542
|
+
const initialResolution = unresolvedTarget !== undefined && isInitialTarget(unresolvedTarget)
|
|
543
|
+
? resolveInitialTarget(
|
|
544
|
+
machine,
|
|
545
|
+
activeConfigurationFromIndexedState(descriptor, state),
|
|
546
|
+
unresolvedTarget,
|
|
547
|
+
(selection.context as any).event
|
|
548
|
+
)
|
|
549
|
+
: undefined
|
|
550
|
+
const target = initialResolution?.target ?? unresolvedTarget
|
|
540
551
|
if (target !== undefined && !isTarget(target) && !isSnapshot(target)) {
|
|
541
552
|
throw new Error("Machine expected indexed transition target to be a snapshot or target builder result")
|
|
542
553
|
}
|
|
@@ -547,16 +558,16 @@ const collectIndexedEvaluatedTransition = (
|
|
|
547
558
|
if (!changed) {
|
|
548
559
|
return {
|
|
549
560
|
selection,
|
|
550
|
-
unresolvedTarget:
|
|
561
|
+
unresolvedTarget: unresolvedTarget as any,
|
|
551
562
|
target: target as any,
|
|
552
563
|
next,
|
|
553
|
-
commands: transitionResult.commands,
|
|
554
|
-
raisedEvents: transitionResult.raisedEvents,
|
|
555
|
-
emittedEvents: transitionResult.emittedEvents,
|
|
564
|
+
commands: [...transitionResult.commands, ...(initialResolution?.commands ?? [])],
|
|
565
|
+
raisedEvents: [...transitionResult.raisedEvents, ...(initialResolution?.raisedEvents ?? [])],
|
|
566
|
+
emittedEvents: [...transitionResult.emittedEvents, ...(initialResolution?.emittedEvents ?? [])],
|
|
556
567
|
changed: false,
|
|
557
568
|
exitPaths: [],
|
|
558
569
|
entryPaths: [],
|
|
559
|
-
choiceTransitions: []
|
|
570
|
+
choiceTransitions: initialResolution?.transitions ?? []
|
|
560
571
|
}
|
|
561
572
|
}
|
|
562
573
|
|
|
@@ -570,16 +581,16 @@ const collectIndexedEvaluatedTransition = (
|
|
|
570
581
|
: naturalBoundary
|
|
571
582
|
return {
|
|
572
583
|
selection,
|
|
573
|
-
unresolvedTarget:
|
|
584
|
+
unresolvedTarget: unresolvedTarget as any,
|
|
574
585
|
target: target as any,
|
|
575
586
|
next,
|
|
576
|
-
commands: transitionResult.commands,
|
|
577
|
-
raisedEvents: transitionResult.raisedEvents,
|
|
578
|
-
emittedEvents: transitionResult.emittedEvents,
|
|
587
|
+
commands: [...transitionResult.commands, ...(initialResolution?.commands ?? [])],
|
|
588
|
+
raisedEvents: [...transitionResult.raisedEvents, ...(initialResolution?.raisedEvents ?? [])],
|
|
589
|
+
emittedEvents: [...transitionResult.emittedEvents, ...(initialResolution?.emittedEvents ?? [])],
|
|
579
590
|
changed: true,
|
|
580
591
|
exitPaths: getExitPaths(machine, activeConfigurationFromIndexedState(descriptor, state), boundary),
|
|
581
592
|
entryPaths: getEntryPaths(machine, activeConfigurationFromIndexedState(descriptor, next), boundary),
|
|
582
|
-
choiceTransitions: []
|
|
593
|
+
choiceTransitions: initialResolution?.transitions ?? []
|
|
583
594
|
}
|
|
584
595
|
}
|
|
585
596
|
|
|
@@ -588,13 +599,22 @@ const indexedMicrostep = (
|
|
|
588
599
|
descriptor: IndexedExecutionDescriptor,
|
|
589
600
|
state: OwnedIndexedState,
|
|
590
601
|
event: any,
|
|
591
|
-
selections: ReadonlyArray<IndexedSelectedTransition
|
|
602
|
+
selections: ReadonlyArray<IndexedSelectedTransition>,
|
|
603
|
+
retainTransitions: boolean
|
|
592
604
|
): ExecutionMicrostep<OwnedIndexedState> => {
|
|
605
|
+
const retain = (transition: IndexedEvaluatedTransition): Machine.RetainedTransition => ({
|
|
606
|
+
source: transition.selection.sourcePath,
|
|
607
|
+
trigger: transition.selection.trigger,
|
|
608
|
+
reenter: transition.selection.transition.reenter,
|
|
609
|
+
target: transition.unresolvedTarget === undefined ? undefined : getTargetNodePath(transition.unresolvedTarget),
|
|
610
|
+
resolvedTarget: transition.target === undefined ? undefined : getTargetNodePath(transition.target)
|
|
611
|
+
})
|
|
593
612
|
if (selections.length === 1) {
|
|
594
613
|
const transition = collectIndexedEvaluatedTransition(machine, descriptor, state, selections[0]!)
|
|
595
614
|
return {
|
|
596
615
|
next: transition.next,
|
|
597
616
|
event,
|
|
617
|
+
...(retainTransitions ? { transitions: [retain(transition)] } : undefined),
|
|
598
618
|
commands: transition.commands,
|
|
599
619
|
raisedEvents: transition.raisedEvents,
|
|
600
620
|
emittedEvents: transition.emittedEvents,
|
|
@@ -645,6 +665,7 @@ const indexedMicrostep = (
|
|
|
645
665
|
return {
|
|
646
666
|
next,
|
|
647
667
|
event,
|
|
668
|
+
...(retainTransitions ? { transitions: transitions.map(retain) } : undefined),
|
|
648
669
|
commands,
|
|
649
670
|
raisedEvents,
|
|
650
671
|
emittedEvents,
|
|
@@ -660,7 +681,7 @@ const planIndexedFlatState = (
|
|
|
660
681
|
configuration: OwnedIndexedState,
|
|
661
682
|
decoded: { readonly _tag: PropertyKey },
|
|
662
683
|
retainMicrosteps: boolean,
|
|
663
|
-
|
|
684
|
+
machineReferences?: PlanningMachineReferences
|
|
664
685
|
): ExecutionMacrostep<OwnedIndexedState> => {
|
|
665
686
|
let current = configuration
|
|
666
687
|
let event: any = decoded
|
|
@@ -712,7 +733,7 @@ const planIndexedFlatState = (
|
|
|
712
733
|
machine,
|
|
713
734
|
transition.transition,
|
|
714
735
|
{
|
|
715
|
-
...
|
|
736
|
+
...resolveMachineReferences(machine, machineReferences),
|
|
716
737
|
state: current.values[sourceIndex],
|
|
717
738
|
containingState: undefined,
|
|
718
739
|
ancestors: {},
|
|
@@ -762,6 +783,17 @@ const planIndexedFlatState = (
|
|
|
762
783
|
const step: ExecutionMicrostep<OwnedIndexedState> = {
|
|
763
784
|
next,
|
|
764
785
|
event,
|
|
786
|
+
...(retainMicrosteps
|
|
787
|
+
? {
|
|
788
|
+
transitions: [{
|
|
789
|
+
source: sourcePath,
|
|
790
|
+
trigger: { type: "event", event: event._tag },
|
|
791
|
+
reenter: transition.reenter,
|
|
792
|
+
target: target === undefined ? undefined : getTargetNodePath(target as any),
|
|
793
|
+
resolvedTarget: target === undefined ? undefined : getTargetNodePath(target as any)
|
|
794
|
+
}]
|
|
795
|
+
}
|
|
796
|
+
: undefined),
|
|
765
797
|
commands: transitionResult.commands,
|
|
766
798
|
raisedEvents: transitionResult.raisedEvents,
|
|
767
799
|
emittedEvents: transitionResult.emittedEvents,
|
|
@@ -807,7 +839,7 @@ const planIndexedState = (
|
|
|
807
839
|
configuration: OwnedIndexedState,
|
|
808
840
|
input: unknown,
|
|
809
841
|
retainMicrosteps: boolean,
|
|
810
|
-
|
|
842
|
+
machineReferences?: PlanningMachineReferences
|
|
811
843
|
): ExecutionMacrostep<OwnedIndexedState> => {
|
|
812
844
|
if (InvocationEvent.isInvocationEvent(input)) {
|
|
813
845
|
// Invocation lifecycle transitions retain the generic planner as their
|
|
@@ -816,9 +848,9 @@ const planIndexedState = (
|
|
|
816
848
|
// the representation boundary.
|
|
817
849
|
const planned = planConfiguration(
|
|
818
850
|
machine as any,
|
|
819
|
-
|
|
851
|
+
machineReferences === undefined
|
|
820
852
|
? activeConfigurationFromIndexedState(descriptor, configuration)
|
|
821
|
-
:
|
|
853
|
+
: withMachineReferences(activeConfigurationFromIndexedState(descriptor, configuration), machineReferences),
|
|
822
854
|
input
|
|
823
855
|
)
|
|
824
856
|
return {
|
|
@@ -828,6 +860,7 @@ const planIndexedState = (
|
|
|
828
860
|
microsteps: planned.microsteps.map((step) => ({
|
|
829
861
|
next: ownedIndexedStateFromActive(descriptor, step.next),
|
|
830
862
|
event: step.event,
|
|
863
|
+
transitions: step.transitions,
|
|
831
864
|
commands: step.commands,
|
|
832
865
|
raisedEvents: step.raisedEvents,
|
|
833
866
|
emittedEvents: step.emittedEvents,
|
|
@@ -841,7 +874,7 @@ const planIndexedState = (
|
|
|
841
874
|
}
|
|
842
875
|
const decoded = decodeEventSync(machine, input)
|
|
843
876
|
if (descriptor.flat) {
|
|
844
|
-
return planIndexedFlatState(machine, descriptor, configuration, decoded, retainMicrosteps,
|
|
877
|
+
return planIndexedFlatState(machine, descriptor, configuration, decoded, retainMicrosteps, machineReferences)
|
|
845
878
|
}
|
|
846
879
|
if (descriptor.finalIndices.some((index) => configuration.active[index] === 1)) {
|
|
847
880
|
const active = activeConfigurationFromIndexedState(descriptor, configuration)
|
|
@@ -862,7 +895,7 @@ const planIndexedState = (
|
|
|
862
895
|
}
|
|
863
896
|
}
|
|
864
897
|
|
|
865
|
-
const selections = selectIndexedEventTransitions(machine, descriptor, configuration, decoded,
|
|
898
|
+
const selections = selectIndexedEventTransitions(machine, descriptor, configuration, decoded, machineReferences)
|
|
866
899
|
if (selections.length === 0) {
|
|
867
900
|
return {
|
|
868
901
|
next: configuration,
|
|
@@ -874,7 +907,7 @@ const planIndexedState = (
|
|
|
874
907
|
}
|
|
875
908
|
}
|
|
876
909
|
|
|
877
|
-
const first = indexedMicrostep(machine, descriptor, configuration, decoded, selections)
|
|
910
|
+
const first = indexedMicrostep(machine, descriptor, configuration, decoded, selections, retainMicrosteps)
|
|
878
911
|
let current = first.next
|
|
879
912
|
let currentEvent: any = decoded
|
|
880
913
|
const commands = [...first.commands]
|
|
@@ -930,9 +963,9 @@ const planIndexedState = (
|
|
|
930
963
|
}
|
|
931
964
|
raisedIndex += 1
|
|
932
965
|
currentEvent = raised
|
|
933
|
-
const raisedSelections = selectIndexedEventTransitions(machine, descriptor, current, raised,
|
|
966
|
+
const raisedSelections = selectIndexedEventTransitions(machine, descriptor, current, raised, machineReferences)
|
|
934
967
|
if (raisedSelections.length === 0) continue
|
|
935
|
-
const step = indexedMicrostep(machine, descriptor, current, raised, raisedSelections)
|
|
968
|
+
const step = indexedMicrostep(machine, descriptor, current, raised, raisedSelections, retainMicrosteps)
|
|
936
969
|
current = step.next
|
|
937
970
|
commands.push(...step.commands)
|
|
938
971
|
raisedEvents.push(...step.raisedEvents)
|
|
@@ -949,11 +982,11 @@ export interface CompiledExecutionPlan {
|
|
|
949
982
|
state: unknown,
|
|
950
983
|
event: unknown,
|
|
951
984
|
retainMicrosteps?: boolean,
|
|
952
|
-
|
|
985
|
+
machineReferences?: PlanningMachineReferences
|
|
953
986
|
) => ExecutionMacrostep
|
|
954
987
|
readonly initial?: (
|
|
955
988
|
args: ReadonlyArray<unknown>,
|
|
956
|
-
|
|
989
|
+
machineReferences?: PlanningMachineReferences
|
|
957
990
|
) => {
|
|
958
991
|
readonly state: Machine.Snapshot<any>
|
|
959
992
|
readonly configuration: unknown
|
|
@@ -970,12 +1003,12 @@ const makeActiveExecutionPlan = (machine: Machine.Any): CompiledExecutionPlan =>
|
|
|
970
1003
|
fromConfiguration: (configuration) => configuration,
|
|
971
1004
|
toConfiguration: (state) => state as ActiveConfiguration,
|
|
972
1005
|
snapshot: (state) => snapshotFromConfiguration(machine, state as ActiveConfiguration),
|
|
973
|
-
plan: (state, event, _retainMicrosteps,
|
|
1006
|
+
plan: (state, event, _retainMicrosteps, machineReferences) =>
|
|
974
1007
|
planConfiguration(
|
|
975
1008
|
machine as any,
|
|
976
|
-
|
|
1009
|
+
machineReferences === undefined
|
|
977
1010
|
? state as ActiveConfiguration
|
|
978
|
-
:
|
|
1011
|
+
: withMachineReferences(state as ActiveConfiguration, machineReferences),
|
|
979
1012
|
event as any
|
|
980
1013
|
)
|
|
981
1014
|
})
|
|
@@ -987,9 +1020,9 @@ const makeIndexedExecutionPlan = (
|
|
|
987
1020
|
fromConfiguration: (configuration) => ownedIndexedStateFromActive(indexed, configuration),
|
|
988
1021
|
toConfiguration: (state) => activeConfigurationFromIndexedState(indexed, state as OwnedIndexedState),
|
|
989
1022
|
snapshot: (state) => snapshotFromIndexedState(indexed, state as OwnedIndexedState),
|
|
990
|
-
plan: (state, event, retainMicrosteps = false,
|
|
991
|
-
planIndexedState(machine, indexed, state as OwnedIndexedState, event, retainMicrosteps,
|
|
992
|
-
initial: (args,
|
|
1023
|
+
plan: (state, event, retainMicrosteps = false, machineReferences) =>
|
|
1024
|
+
planIndexedState(machine, indexed, state as OwnedIndexedState, event, retainMicrosteps, machineReferences),
|
|
1025
|
+
initial: (args, machineReferences) => {
|
|
993
1026
|
const inputArgs = machine.input === undefined
|
|
994
1027
|
? args
|
|
995
1028
|
: args.length === 0
|
|
@@ -997,7 +1030,7 @@ const makeIndexedExecutionPlan = (
|
|
|
997
1030
|
: [decodeInputSync(machine, machine.input, args[0])]
|
|
998
1031
|
const initial = machine.initial(...inputArgs as any)
|
|
999
1032
|
const normalized = normalizeConfigurationSync(machine, initial as Machine.Snapshot<any>)
|
|
1000
|
-
const active =
|
|
1033
|
+
const active = machineReferences === undefined ? normalized : withMachineReferences(normalized, machineReferences)
|
|
1001
1034
|
validateInitialConfiguration(machine, active)
|
|
1002
1035
|
const completed = completeConfigurationSync(machine, active, InitialEvent).configuration
|
|
1003
1036
|
const configuration = ownedIndexedStateFromActive(indexed, completed)
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/** Internal builders for schema-valued default state entry. */
|
|
2
|
+
|
|
3
|
+
import { hasProperty } from "effect/Predicate"
|
|
4
|
+
import type { Machine } from "../../Machine.js"
|
|
5
|
+
import { SnapshotBuilderStateTypeId } from "./symbols.js"
|
|
6
|
+
import * as Topology from "./topology.js"
|
|
7
|
+
|
|
8
|
+
const getNode = (machine: Machine.Any, path: string): Machine.StateNode => {
|
|
9
|
+
const node = machine.stateNodes.byPath.get(path)
|
|
10
|
+
if (node === undefined) {
|
|
11
|
+
throw new Error(`Machine expected state path "${path}" to exist`)
|
|
12
|
+
}
|
|
13
|
+
return node
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const withFrom = <Method extends (value: unknown) => unknown>(method: Method) => {
|
|
17
|
+
Object.defineProperty(method, "from", {
|
|
18
|
+
value: (...args: ReadonlyArray<unknown>) => method(Topology.makeStateInput(args.length === 0 ? {} : args[0])),
|
|
19
|
+
enumerable: false
|
|
20
|
+
})
|
|
21
|
+
return method as Method & { readonly from: (...args: ReadonlyArray<unknown>) => unknown }
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const makeCompletion = (values: Readonly<Record<string, unknown>>): object => {
|
|
25
|
+
const completion = {}
|
|
26
|
+
Object.defineProperty(completion, SnapshotBuilderStateTypeId, {
|
|
27
|
+
value: values,
|
|
28
|
+
enumerable: false
|
|
29
|
+
})
|
|
30
|
+
return completion
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const makeParallelBuilder = (
|
|
34
|
+
machine: Machine.Any,
|
|
35
|
+
node: Machine.StateNode,
|
|
36
|
+
values: Readonly<Record<string, unknown>>
|
|
37
|
+
): object => {
|
|
38
|
+
const builder = makeCompletion(values) as Record<string | symbol, unknown>
|
|
39
|
+
for (const childPath of node.children) {
|
|
40
|
+
const child = getNode(machine, childPath)
|
|
41
|
+
if (child.schema === undefined || hasProperty(values, child.key)) continue
|
|
42
|
+
builder[child.key] = withFrom((value: unknown) => {
|
|
43
|
+
const nextValues: Record<string, unknown> = Object.assign({}, values)
|
|
44
|
+
nextValues[child.key] = value
|
|
45
|
+
return makeParallelBuilder(machine, node, nextValues)
|
|
46
|
+
})
|
|
47
|
+
}
|
|
48
|
+
return builder
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export const makeStateInitializeBuilder = (machine: Machine.Any, path: string): object => {
|
|
52
|
+
const node = getNode(machine, path)
|
|
53
|
+
if (node.type === "parallel") {
|
|
54
|
+
return makeParallelBuilder(machine, node, {})
|
|
55
|
+
}
|
|
56
|
+
if (node.type !== "compound") {
|
|
57
|
+
throw new Error(`Machine state "${path}" cannot initialize child states`)
|
|
58
|
+
}
|
|
59
|
+
const child = getNode(machine, node.initial)
|
|
60
|
+
if (child.schema === undefined) {
|
|
61
|
+
throw new Error(`Machine state "${path}" has no schema-valued initial child`)
|
|
62
|
+
}
|
|
63
|
+
return withFrom((value: unknown) => makeCompletion({ [child.key]: value }))
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export const getStateInitializeValues = (path: string, result: unknown): Readonly<Record<string, unknown>> => {
|
|
67
|
+
if (typeof result !== "object" || result === null || !hasProperty(result, SnapshotBuilderStateTypeId)) {
|
|
68
|
+
throw new Error(`Machine initialize handler for "${path}" must return its builder result`)
|
|
69
|
+
}
|
|
70
|
+
return (result as { readonly [SnapshotBuilderStateTypeId]: Readonly<Record<string, unknown>> })[
|
|
71
|
+
SnapshotBuilderStateTypeId
|
|
72
|
+
]
|
|
73
|
+
}
|