@typeonce/effect-machine 0.8.0 → 0.10.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 +180 -40
- package/dist/Machine.d.ts +659 -481
- package/dist/Machine.d.ts.map +1 -1
- package/dist/Machine.js +86 -222
- package/dist/Machine.js.map +1 -1
- package/dist/internal/machine/activities.d.ts +5 -12
- package/dist/internal/machine/activities.d.ts.map +1 -1
- package/dist/internal/machine/activities.js +47 -17
- package/dist/internal/machine/activities.js.map +1 -1
- package/dist/internal/machine/atom.d.ts +6 -2
- package/dist/internal/machine/atom.d.ts.map +1 -1
- package/dist/internal/machine/atom.js +5 -0
- package/dist/internal/machine/atom.js.map +1 -1
- package/dist/internal/machine/cluster.d.ts.map +1 -1
- package/dist/internal/machine/cluster.js +2 -1
- package/dist/internal/machine/cluster.js.map +1 -1
- package/dist/internal/machine/command.js +2 -2
- package/dist/internal/machine/command.js.map +1 -1
- package/dist/internal/machine/commandRuntime.d.ts.map +1 -1
- package/dist/internal/machine/commandRuntime.js +6 -3
- package/dist/internal/machine/commandRuntime.js.map +1 -1
- package/dist/internal/machine/configuration.d.ts +8 -1
- package/dist/internal/machine/configuration.d.ts.map +1 -1
- package/dist/internal/machine/configuration.js +9 -4
- package/dist/internal/machine/configuration.js.map +1 -1
- package/dist/internal/machine/errors.d.ts +1 -1
- package/dist/internal/machine/errors.d.ts.map +1 -1
- package/dist/internal/machine/executionPlan.d.ts +3 -3
- package/dist/internal/machine/executionPlan.d.ts.map +1 -1
- package/dist/internal/machine/executionPlan.js +74 -24
- package/dist/internal/machine/executionPlan.js.map +1 -1
- package/dist/internal/machine/invocation.d.ts +10 -2
- package/dist/internal/machine/invocation.d.ts.map +1 -1
- package/dist/internal/machine/invocation.js +94 -34
- package/dist/internal/machine/invocation.js.map +1 -1
- package/dist/internal/machine/invocationEvent.d.ts +39 -0
- package/dist/internal/machine/invocationEvent.d.ts.map +1 -0
- package/dist/internal/machine/invocationEvent.js +43 -0
- package/dist/internal/machine/invocationEvent.js.map +1 -0
- package/dist/internal/machine/machine.d.ts +16 -65
- package/dist/internal/machine/machine.d.ts.map +1 -1
- package/dist/internal/machine/machine.js +18 -73
- package/dist/internal/machine/machine.js.map +1 -1
- package/dist/internal/machine/planner.d.ts +4 -2
- package/dist/internal/machine/planner.d.ts.map +1 -1
- package/dist/internal/machine/planner.js +104 -24
- 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 +8 -8
- package/dist/internal/machine/process.js.map +1 -1
- package/dist/internal/machine/protocol.d.ts +8 -5
- package/dist/internal/machine/protocol.d.ts.map +1 -1
- package/dist/internal/machine/protocol.js +207 -15
- package/dist/internal/machine/protocol.js.map +1 -1
- package/dist/internal/machine/runtime.d.ts +8 -2
- package/dist/internal/machine/runtime.d.ts.map +1 -1
- package/dist/internal/machine/runtime.js +86 -8
- 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 +7 -0
- package/dist/internal/machine/topology.d.ts.map +1 -1
- package/dist/internal/machine/topology.js +29 -0
- package/dist/internal/machine/topology.js.map +1 -1
- package/dist/internal/testing/machine/finiteModel.js +1 -1
- package/dist/internal/testing/machine/finiteModel.js.map +1 -1
- package/dist/internal/testing/machine/runtime.d.ts.map +1 -1
- package/dist/internal/testing/machine/runtime.js +2 -1
- package/dist/internal/testing/machine/runtime.js.map +1 -1
- package/dist/internal/testing/machine/verification.d.ts.map +1 -1
- package/dist/internal/testing/machine/verification.js +5 -3
- package/dist/internal/testing/machine/verification.js.map +1 -1
- package/dist/testing/MachineTest.d.ts +7 -7
- package/dist/testing/MachineTest.js +7 -7
- package/dist/unstable/cluster/ClusterMachine.d.ts +1 -1
- package/dist/unstable/cluster/ClusterMachine.js +1 -1
- package/dist/unstable/reactivity/AtomMachine.d.ts +25 -8
- package/dist/unstable/reactivity/AtomMachine.d.ts.map +1 -1
- package/dist/unstable/reactivity/AtomMachine.js +18 -3
- package/dist/unstable/reactivity/AtomMachine.js.map +1 -1
- package/docs/agent-guide.md +242 -122
- package/package.json +2 -2
- package/src/Machine.ts +1453 -812
- package/src/internal/machine/activities.ts +48 -33
- package/src/internal/machine/atom.ts +38 -13
- package/src/internal/machine/cluster.ts +2 -1
- package/src/internal/machine/command.ts +2 -2
- package/src/internal/machine/commandRuntime.ts +9 -4
- package/src/internal/machine/configuration.ts +23 -5
- package/src/internal/machine/errors.ts +1 -1
- package/src/internal/machine/executionPlan.ts +102 -28
- package/src/internal/machine/invocation.ts +182 -50
- package/src/internal/machine/invocationEvent.ts +72 -0
- package/src/internal/machine/machine.ts +80 -387
- package/src/internal/machine/planner.ts +127 -32
- package/src/internal/machine/process.ts +18 -8
- package/src/internal/machine/protocol.ts +313 -25
- package/src/internal/machine/runtime.ts +135 -9
- package/src/internal/machine/symbols.ts +3 -0
- package/src/internal/machine/topology.ts +38 -0
- package/src/internal/testing/machine/finiteModel.ts +1 -1
- package/src/internal/testing/machine/runtime.ts +2 -1
- package/src/internal/testing/machine/verification.ts +5 -3
- package/src/testing/MachineTest.ts +7 -7
- package/src/unstable/cluster/ClusterMachine.ts +1 -1
- package/src/unstable/reactivity/AtomMachine.ts +45 -13
|
@@ -4,8 +4,7 @@
|
|
|
4
4
|
* @since 0.4.0
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
export const ActivityMetadataTypeId: unique symbol = Symbol.for("effect/Machine/ActivityMetadata")
|
|
7
|
+
import * as Duration from "effect/Duration"
|
|
9
8
|
|
|
10
9
|
/** @internal */
|
|
11
10
|
export type StaticActivityMetadata =
|
|
@@ -21,8 +20,7 @@ export type StaticActivityMetadata =
|
|
|
21
20
|
}
|
|
22
21
|
| {
|
|
23
22
|
readonly type: "timer"
|
|
24
|
-
readonly duration: string
|
|
25
|
-
readonly event: string
|
|
23
|
+
readonly duration: string | "dynamic"
|
|
26
24
|
}
|
|
27
25
|
| {
|
|
28
26
|
readonly type: "machine"
|
|
@@ -33,20 +31,10 @@ export type StaticActivityMetadata =
|
|
|
33
31
|
}
|
|
34
32
|
|
|
35
33
|
/** @internal */
|
|
36
|
-
export type ActivityDefinition<Source extends string = string> =
|
|
37
|
-
|
|
38
|
-
readonly source: Source
|
|
39
|
-
readonly id: string
|
|
40
|
-
} & StaticActivityMetadata)
|
|
41
|
-
| {
|
|
42
|
-
readonly source: Source
|
|
43
|
-
readonly type: "dynamic"
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
interface ActivityDescriptor {
|
|
34
|
+
export type ActivityDefinition<Source extends string = string> = {
|
|
35
|
+
readonly source: Source
|
|
47
36
|
readonly id: string
|
|
48
|
-
|
|
49
|
-
}
|
|
37
|
+
} & StaticActivityMetadata
|
|
50
38
|
|
|
51
39
|
interface InspectableMachine {
|
|
52
40
|
readonly handlers: unknown
|
|
@@ -62,31 +50,60 @@ const isObject = (value: unknown): value is object =>
|
|
|
62
50
|
|
|
63
51
|
const getProperty = (value: unknown, key: PropertyKey): unknown => isObject(value) ? Reflect.get(value, key) : undefined
|
|
64
52
|
|
|
65
|
-
const hasActivityMetadata = (value: unknown): value is ActivityDescriptor =>
|
|
66
|
-
isObject(value) && typeof getProperty(value, "id") === "string" &&
|
|
67
|
-
isObject(getProperty(value, ActivityMetadataTypeId))
|
|
68
|
-
|
|
69
53
|
const appendStaticDefinition = (
|
|
70
54
|
definitions: Array<ActivityDefinition>,
|
|
71
55
|
source: string,
|
|
72
56
|
descriptor: unknown
|
|
73
57
|
): void => {
|
|
74
|
-
if (!
|
|
58
|
+
if (!isObject(descriptor)) return
|
|
59
|
+
const child = getProperty(descriptor, "child")
|
|
60
|
+
if (isObject(child)) {
|
|
61
|
+
const id = getProperty(child, "id")
|
|
62
|
+
if (typeof id !== "string") return
|
|
63
|
+
const machine = getProperty(child, "machine")
|
|
64
|
+
const machineId = getProperty(machine, "id")
|
|
65
|
+
definitions.push({
|
|
66
|
+
source,
|
|
67
|
+
id,
|
|
68
|
+
type: "machine",
|
|
69
|
+
child: { id, machineId: typeof machineId === "string" ? machineId : null }
|
|
70
|
+
})
|
|
75
71
|
return
|
|
76
72
|
}
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
73
|
+
const id = getProperty(descriptor, "id")
|
|
74
|
+
if (typeof id !== "string") return
|
|
75
|
+
if (Reflect.has(descriptor, "effect")) {
|
|
76
|
+
definitions.push({
|
|
77
|
+
source,
|
|
78
|
+
id,
|
|
79
|
+
type: "effect",
|
|
80
|
+
outcomes: {
|
|
81
|
+
success: "dynamic",
|
|
82
|
+
failure: Reflect.has(descriptor, "onFailure") ? "dynamic" : "none"
|
|
83
|
+
}
|
|
84
|
+
})
|
|
85
|
+
return
|
|
86
|
+
}
|
|
87
|
+
if (Reflect.has(descriptor, "after")) {
|
|
88
|
+
const after = getProperty(descriptor, "after")
|
|
89
|
+
definitions.push({
|
|
90
|
+
source,
|
|
91
|
+
id,
|
|
92
|
+
type: "timer",
|
|
93
|
+
duration: typeof after === "function" ? "dynamic" : Duration.format(Duration.fromInputUnsafe(after as any))
|
|
94
|
+
})
|
|
95
|
+
return
|
|
96
|
+
}
|
|
97
|
+
if (Reflect.has(descriptor, "logic")) {
|
|
98
|
+
definitions.push({ source, id, type: "process" })
|
|
99
|
+
}
|
|
82
100
|
}
|
|
83
101
|
|
|
84
102
|
/**
|
|
85
103
|
* Collects state-owned activity descriptions without executing user code.
|
|
86
104
|
*
|
|
87
|
-
*
|
|
88
|
-
*
|
|
89
|
-
* definition order and descriptor array order.
|
|
105
|
+
* Source factories are inspected without execution. Static descriptors are
|
|
106
|
+
* returned in state definition order and descriptor array order.
|
|
90
107
|
*
|
|
91
108
|
* @internal
|
|
92
109
|
*/
|
|
@@ -94,9 +111,7 @@ export const activityDefinitions = (machine: InspectableMachine): ReadonlyArray<
|
|
|
94
111
|
const definitions: Array<ActivityDefinition> = []
|
|
95
112
|
for (const node of machine.stateNodes.byPath.values()) {
|
|
96
113
|
const invoke = getProperty(getProperty(machine.handlers, node.path), "invoke")
|
|
97
|
-
if (
|
|
98
|
-
definitions.push({ source: node.path, type: "dynamic" })
|
|
99
|
-
} else if (Array.isArray(invoke)) {
|
|
114
|
+
if (Array.isArray(invoke)) {
|
|
100
115
|
for (const descriptor of invoke) {
|
|
101
116
|
appendStaticDefinition(definitions, node.path, descriptor)
|
|
102
117
|
}
|
|
@@ -72,9 +72,9 @@ type MachineStartError<InitialE, E, InitialR, R, RuntimeError = never> =
|
|
|
72
72
|
| Machine.StoppedError
|
|
73
73
|
| RuntimeError
|
|
74
74
|
|
|
75
|
-
const runMachineAtomEffect = <State, Event, Error, Output, StartError, Requirements>(
|
|
75
|
+
const runMachineAtomEffect = <State, Event, Error, Output, Emitted, StartError, Requirements>(
|
|
76
76
|
get: Atom.AtomContext,
|
|
77
|
-
start: Effect.Effect<Machine.MachineRef<State, Event, Error, Output>, StartError, Requirements>
|
|
77
|
+
start: Effect.Effect<Machine.MachineRef<State, Event, Error, Output, Emitted>, StartError, Requirements>
|
|
78
78
|
): Effect.Effect<never, StartError, Requirements> =>
|
|
79
79
|
Effect.scoped(
|
|
80
80
|
Effect.acquireRelease(start, (ref) => ref.stop).pipe(
|
|
@@ -96,7 +96,8 @@ const startMachineAtomEffect = <
|
|
|
96
96
|
FinalStates extends Machine.Machine.StateIdentifier<States> = never,
|
|
97
97
|
Output = never,
|
|
98
98
|
OutputStates extends Machine.Machine.StateIdentifier<States> = never,
|
|
99
|
-
InputEvents extends ReadonlyArray<Machine.Machine.TaggedSchema> = Events
|
|
99
|
+
InputEvents extends ReadonlyArray<Machine.Machine.TaggedSchema> = Events,
|
|
100
|
+
ParentEvents extends ReadonlyArray<Machine.Machine.TaggedSchema> = readonly []
|
|
100
101
|
>(
|
|
101
102
|
get: Atom.AtomContext,
|
|
102
103
|
machine:
|
|
@@ -113,20 +114,22 @@ const startMachineAtomEffect = <
|
|
|
113
114
|
Output,
|
|
114
115
|
Emits,
|
|
115
116
|
OutputStates,
|
|
116
|
-
InputEvents
|
|
117
|
+
InputEvents,
|
|
118
|
+
ParentEvents
|
|
117
119
|
>
|
|
118
120
|
& EnsureExecutable<States, UnhandledStates, OutputStates>,
|
|
119
121
|
args: [...Machine.Machine.InputArgs<Input>]
|
|
120
122
|
): Effect.Effect<
|
|
121
123
|
Machine.MachineRef<
|
|
122
124
|
Machine.Machine.Snapshot<States>,
|
|
123
|
-
Machine.Machine.
|
|
125
|
+
Machine.Machine.EventInputOf<InputEvents>,
|
|
124
126
|
MachineRuntimeError<E, R>,
|
|
125
|
-
Output
|
|
127
|
+
Output,
|
|
128
|
+
Machine.Machine.EmittedEventOf<Emits>
|
|
126
129
|
>,
|
|
127
130
|
MachineStartError<InitialE, E, InitialR, R>,
|
|
128
131
|
MachineRequirements<InitialR, R, Machine.Machine.EventOf<Events>, Machine.Machine.EmitOf<Emits>>
|
|
129
|
-
> => runMachineAtomEffect(get, internalMachine.start(machine, ...args))
|
|
132
|
+
> => runMachineAtomEffect(get, internalMachine.start(machine as any, ...args) as any)
|
|
130
133
|
|
|
131
134
|
const resumeMachineAtomEffect = (
|
|
132
135
|
get: Atom.AtomContext,
|
|
@@ -137,6 +140,24 @@ const resumeMachineAtomEffect = (
|
|
|
137
140
|
type RefState<Ref> = Ref extends Machine.MachineRef<infer State, any, any, any> ? State : never
|
|
138
141
|
type RefError<Ref> = Ref extends Machine.MachineRef<any, any, infer Error, any> ? Error : never
|
|
139
142
|
type RefOutput<Ref> = Ref extends Machine.MachineRef<any, any, any, infer Output> ? Output : never
|
|
143
|
+
type RefEmitted<Ref> = Ref extends Machine.MachineRef<any, any, any, any, infer Emitted> ? Emitted : never
|
|
144
|
+
|
|
145
|
+
export const emissions = <State, Event, Error, Output, StartError, Emitted>(
|
|
146
|
+
self: MachineAtom<State, Event, Error, Output, StartError, Emitted>
|
|
147
|
+
): Stream.Stream<Emitted, StartError, AtomRegistry.AtomRegistry> =>
|
|
148
|
+
Atom.toStreamResult(self.ref).pipe(Stream.flatMap((ref) => ref.emissions))
|
|
149
|
+
|
|
150
|
+
export const childEmissions = <Child extends Machine.ChildMachine.Any, StartError>(
|
|
151
|
+
self: ChildMachineAtom<Child, StartError>
|
|
152
|
+
): Stream.Stream<RefEmitted<Machine.ChildMachine.Ref<Child>>, StartError, AtomRegistry.AtomRegistry> =>
|
|
153
|
+
Atom.toStreamResult(self.ref).pipe(
|
|
154
|
+
Stream.flatMap(
|
|
155
|
+
Option.match({
|
|
156
|
+
onNone: () => Stream.empty,
|
|
157
|
+
onSome: (ref) => ref.emissions
|
|
158
|
+
})
|
|
159
|
+
)
|
|
160
|
+
)
|
|
140
161
|
|
|
141
162
|
const makeRuntimeResultAtom = <State, Error, Output, StartError>(
|
|
142
163
|
snapshot: Atom.Atom<AsyncResult.AsyncResult<Machine.RuntimeSnapshot<State, Error, Output>, StartError>>
|
|
@@ -585,10 +606,11 @@ type EnsureMachineExecutable<M extends Machine.Machine.Any> = IsAny<Machine.Mach
|
|
|
585
606
|
|
|
586
607
|
type ResumedMachineAtomOf<M extends Machine.Machine.Any, RuntimeError> = MachineAtom<
|
|
587
608
|
Machine.Machine.Snapshot<Machine.Machine.States<M>>,
|
|
588
|
-
Machine.Machine.InputEvent<M
|
|
609
|
+
Machine.Machine.EventInput<Machine.Machine.InputEvent<M>>,
|
|
589
610
|
MachineRuntimeError<Machine.Machine.Error<M>, Machine.Machine.Services<M>>,
|
|
590
611
|
Machine.Machine.Output<M>,
|
|
591
|
-
Machine.MachineSchemaDecodeError | RuntimeError
|
|
612
|
+
Machine.MachineSchemaDecodeError | RuntimeError,
|
|
613
|
+
Machine.Machine.EmittedEvent<M>
|
|
592
614
|
>
|
|
593
615
|
|
|
594
616
|
export const make: {
|
|
@@ -605,7 +627,8 @@ export const make: {
|
|
|
605
627
|
FinalStates extends Machine.Machine.StateIdentifier<States> = never,
|
|
606
628
|
Output = never,
|
|
607
629
|
OutputStates extends Machine.Machine.StateIdentifier<States> = never,
|
|
608
|
-
InputEvents extends ReadonlyArray<Machine.Machine.TaggedSchema> = Events
|
|
630
|
+
InputEvents extends ReadonlyArray<Machine.Machine.TaggedSchema> = Events,
|
|
631
|
+
ParentEvents extends ReadonlyArray<Machine.Machine.TaggedSchema> = readonly []
|
|
609
632
|
>(
|
|
610
633
|
machine:
|
|
611
634
|
& Machine.Machine<
|
|
@@ -621,7 +644,8 @@ export const make: {
|
|
|
621
644
|
Output,
|
|
622
645
|
Emits,
|
|
623
646
|
OutputStates,
|
|
624
|
-
InputEvents
|
|
647
|
+
InputEvents,
|
|
648
|
+
ParentEvents
|
|
625
649
|
>
|
|
626
650
|
& EnsureNoExternalRequirements<
|
|
627
651
|
MachineRequirements<
|
|
@@ -635,10 +659,11 @@ export const make: {
|
|
|
635
659
|
...args: [...Machine.Machine.InputArgs<Input>]
|
|
636
660
|
): MachineAtom<
|
|
637
661
|
Machine.Machine.Snapshot<States>,
|
|
638
|
-
Machine.Machine.
|
|
662
|
+
Machine.Machine.EventInputOf<InputEvents>,
|
|
639
663
|
MachineRuntimeError<E, R>,
|
|
640
664
|
Output,
|
|
641
|
-
MachineStartError<InitialE, E, InitialR, R
|
|
665
|
+
MachineStartError<InitialE, E, InitialR, R>,
|
|
666
|
+
Machine.Machine.EmittedEventOf<Emits>
|
|
642
667
|
>
|
|
643
668
|
} = ((machine: Machine.Machine.Any, ...args: ReadonlyArray<unknown>) => {
|
|
644
669
|
const ref = Atom.make((get) => startMachineAtomEffect(get, machine as any, args as []))
|
|
@@ -14,6 +14,7 @@ import { Rpc } from "effect/unstable/rpc"
|
|
|
14
14
|
import type * as Machine from "../../Machine.js"
|
|
15
15
|
import type { Checkpoint, ClusterMachine, LoadResult } from "../../unstable/cluster/ClusterMachine.js"
|
|
16
16
|
import * as internalMachine from "./machine.js"
|
|
17
|
+
import * as Protocol from "./protocol.js"
|
|
17
18
|
import type { EnsureExecutable } from "./readiness.js"
|
|
18
19
|
|
|
19
20
|
type EntityAddress = EntityAddress.EntityAddress
|
|
@@ -248,7 +249,7 @@ export const make = <
|
|
|
248
249
|
OutputStates,
|
|
249
250
|
InputEvents
|
|
250
251
|
>
|
|
251
|
-
const eventSchema = Schema.Union(machine
|
|
252
|
+
const eventSchema = Schema.Union(Protocol.inputEventSchemas(machine) as MachineEvents<M>)
|
|
252
253
|
const rpc = Rpc.make("send", {
|
|
253
254
|
payload: eventSchema,
|
|
254
255
|
success: SendResult
|
|
@@ -47,8 +47,8 @@ export const makeCollector = <Event>(machine: Machine.Any): Collected<Event> =>
|
|
|
47
47
|
emit: (event) => {
|
|
48
48
|
emittedEvents.push(decodeEmitSync(machine, event))
|
|
49
49
|
},
|
|
50
|
-
sendTo: (
|
|
51
|
-
commands.push({ _tag: "SendTo",
|
|
50
|
+
sendTo: (target: unknown, event: unknown) => {
|
|
51
|
+
commands.push({ _tag: "SendTo", target: target as any, event })
|
|
52
52
|
},
|
|
53
53
|
stop: (child: unknown) => {
|
|
54
54
|
commands.push({ _tag: "Stop", child: child as any })
|
|
@@ -10,6 +10,9 @@ 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
|
+
|
|
13
16
|
export const makeLiveRuntime = <Events, Emits>(
|
|
14
17
|
machine: Machine.Any,
|
|
15
18
|
scope: ProcessScope<Events>
|
|
@@ -18,9 +21,9 @@ export const makeLiveRuntime = <Events, Emits>(
|
|
|
18
21
|
decodeEvent(machine, event).pipe(
|
|
19
22
|
Effect.flatMap((event) => scope.self.send(event as Events))
|
|
20
23
|
),
|
|
21
|
-
|
|
24
|
+
emit: (event) =>
|
|
22
25
|
decodeEmit(machine, event).pipe(
|
|
23
|
-
Effect.flatMap((event) => scope.
|
|
26
|
+
Effect.flatMap((event) => scope.emit(event))
|
|
24
27
|
)
|
|
25
28
|
})
|
|
26
29
|
|
|
@@ -30,7 +33,9 @@ export const runCommands = <Event>(
|
|
|
30
33
|
) =>
|
|
31
34
|
Effect.forEach(commands, (command) =>
|
|
32
35
|
command._tag === "SendTo"
|
|
33
|
-
?
|
|
36
|
+
? isActorRef(command.target)
|
|
37
|
+
? command.target.send(command.event)
|
|
38
|
+
: scope.sendTo(command.target as never, command.event)
|
|
34
39
|
: scope.stopChild(command.child as never), { discard: true })
|
|
35
40
|
|
|
36
41
|
export const runEmittedEvents = <Events, Emits>(
|
|
@@ -38,6 +43,6 @@ export const runEmittedEvents = <Events, Emits>(
|
|
|
38
43
|
runtime: Runtime<Events, Emits>
|
|
39
44
|
) =>
|
|
40
45
|
Effect.all(
|
|
41
|
-
Array.from(events, (event) => runtime.
|
|
46
|
+
Array.from(events, (event) => runtime.emit(event)),
|
|
42
47
|
{ discard: true }
|
|
43
48
|
)
|
|
@@ -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 { Machine } from "../../Machine.js"
|
|
11
|
+
import type { ActorRef, Machine } from "../../Machine.js"
|
|
12
12
|
import { MachineSchemaDecodeError } from "./errors.js"
|
|
13
13
|
import {
|
|
14
14
|
decodeBoundary,
|
|
@@ -252,8 +252,26 @@ export interface ActiveConfiguration {
|
|
|
252
252
|
readonly values: ReadonlyMap<string, unknown>
|
|
253
253
|
readonly outputs: ReadonlyMap<string, unknown>
|
|
254
254
|
readonly history: ReadonlyMap<string, HistoryRecord>
|
|
255
|
+
readonly actorScope?: PlanningActorScope
|
|
255
256
|
}
|
|
256
257
|
|
|
258
|
+
export interface PlanningActorScope {
|
|
259
|
+
readonly self: ActorRef<any>
|
|
260
|
+
readonly parent: ActorRef<any> | undefined
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
export const withActorScope = (
|
|
264
|
+
configuration: ActiveConfiguration,
|
|
265
|
+
actorScope: PlanningActorScope
|
|
266
|
+
): ActiveConfiguration => ({
|
|
267
|
+
...configuration,
|
|
268
|
+
actorScope: { self: actorScope.self, parent: actorScope.parent }
|
|
269
|
+
})
|
|
270
|
+
|
|
271
|
+
export const getActorScope = (
|
|
272
|
+
configuration: ActiveConfiguration
|
|
273
|
+
): PlanningActorScope | undefined => configuration.actorScope
|
|
274
|
+
|
|
257
275
|
export interface FinalCompletion {
|
|
258
276
|
readonly path: string
|
|
259
277
|
readonly output: unknown
|
|
@@ -1178,8 +1196,8 @@ export const resolveFinalOutputEffect: <
|
|
|
1178
1196
|
const node = getNode(machine, path)
|
|
1179
1197
|
const output = getStateConfigByPath(machine, path)?.output?.({
|
|
1180
1198
|
state: configuration.values.get(path),
|
|
1181
|
-
|
|
1182
|
-
|
|
1199
|
+
containingState: getParentValue(machine, configuration, path),
|
|
1200
|
+
ancestors: getParentValues(machine, configuration, path),
|
|
1183
1201
|
event,
|
|
1184
1202
|
outputs
|
|
1185
1203
|
} as any)
|
|
@@ -1331,8 +1349,8 @@ const resolveFinalOutputSync = <const Events extends ReadonlyArray<Machine.Tagge
|
|
|
1331
1349
|
const node = getNode(machine, path)
|
|
1332
1350
|
const output = getStateConfigByPath(machine, path)?.output?.({
|
|
1333
1351
|
state: configuration.values.get(path),
|
|
1334
|
-
|
|
1335
|
-
|
|
1352
|
+
containingState: getParentValue(machine, configuration, path),
|
|
1353
|
+
ancestors: getParentValues(machine, configuration, path),
|
|
1336
1354
|
event,
|
|
1337
1355
|
outputs
|
|
1338
1356
|
} as any)
|
|
@@ -25,7 +25,7 @@ export class MachineSchemaEncodeError extends Data.TaggedError("MachineSchemaEnc
|
|
|
25
25
|
*/
|
|
26
26
|
export class MachineSchemaDecodeError extends Data.TaggedError("MachineSchemaDecodeError")<{
|
|
27
27
|
readonly machineId: string | undefined
|
|
28
|
-
readonly boundary: "input" | "event" | "
|
|
28
|
+
readonly boundary: "input" | "event" | "emission" | "state" | "output" | "history" | "configuration"
|
|
29
29
|
readonly state?: string
|
|
30
30
|
readonly event?: string
|
|
31
31
|
readonly cause: Schema.SchemaError | Cause.Cause<unknown>
|
|
@@ -4,7 +4,8 @@
|
|
|
4
4
|
* @since 0.4.0
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import
|
|
7
|
+
import * as Effect from "effect/Effect"
|
|
8
|
+
import type { ActorRef, Machine } from "../../Machine.js"
|
|
8
9
|
import { getTargetBuilder, type RuntimeCommand } from "./command.js"
|
|
9
10
|
import {
|
|
10
11
|
type ActiveConfiguration,
|
|
@@ -17,10 +18,13 @@ import {
|
|
|
17
18
|
isDescendantOf,
|
|
18
19
|
normalizeConfigurationSync,
|
|
19
20
|
normalizeTargetConfigurationSync,
|
|
21
|
+
type PlanningActorScope,
|
|
20
22
|
snapshotFromConfiguration,
|
|
21
|
-
validateInitialConfiguration
|
|
23
|
+
validateInitialConfiguration,
|
|
24
|
+
withActorScope
|
|
22
25
|
} from "./configuration.js"
|
|
23
|
-
import { InfiniteTransitionError } from "./errors.js"
|
|
26
|
+
import { InfiniteTransitionError, StoppedError } from "./errors.js"
|
|
27
|
+
import * as InvocationEvent from "./invocationEvent.js"
|
|
24
28
|
import {
|
|
25
29
|
broadenTransitionBoundary,
|
|
26
30
|
type EvaluatedTransition,
|
|
@@ -42,7 +46,7 @@ import {
|
|
|
42
46
|
validateDeclaredTransitionTarget
|
|
43
47
|
} from "./planner.js"
|
|
44
48
|
import { decodeEmitSync, decodeEventSync, decodeInputSync, decodeStateValueSync } from "./protocol.js"
|
|
45
|
-
import { isSnapshot, isTarget, TargetSnapshotTypeId } from "./topology.js"
|
|
49
|
+
import { isNoTarget, isSnapshot, isTarget, TargetSnapshotTypeId } from "./topology.js"
|
|
46
50
|
|
|
47
51
|
interface IndexedExecutionDescriptor {
|
|
48
52
|
readonly flat: boolean
|
|
@@ -63,6 +67,29 @@ interface IndexedExecutionDescriptor {
|
|
|
63
67
|
>
|
|
64
68
|
}
|
|
65
69
|
|
|
70
|
+
const planningActorScopes = new WeakMap<Machine.Any, PlanningActorScope>()
|
|
71
|
+
|
|
72
|
+
const getPlanningActorScope = (machine: Machine.Any): PlanningActorScope => {
|
|
73
|
+
const cached = planningActorScopes.get(machine)
|
|
74
|
+
if (cached !== undefined) return cached
|
|
75
|
+
const self: ActorRef<unknown> = {
|
|
76
|
+
id: machine.id ?? "Machine",
|
|
77
|
+
sessionId: "Machine.plan",
|
|
78
|
+
send: () => Effect.fail(new StoppedError())
|
|
79
|
+
}
|
|
80
|
+
const scope = { self, parent: undefined }
|
|
81
|
+
planningActorScopes.set(machine, scope)
|
|
82
|
+
return scope
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const getActorContext = (
|
|
86
|
+
machine: Machine.Any,
|
|
87
|
+
actorScope: PlanningActorScope | undefined
|
|
88
|
+
): PlanningActorScope =>
|
|
89
|
+
actorScope === undefined
|
|
90
|
+
? getPlanningActorScope(machine)
|
|
91
|
+
: { self: actorScope.self, parent: actorScope.parent }
|
|
92
|
+
|
|
66
93
|
const indexedStateConfigKeys: ReadonlySet<PropertyKey> = new Set([
|
|
67
94
|
"initial",
|
|
68
95
|
"invoke",
|
|
@@ -335,18 +362,20 @@ const makeIndexedTransitionContext = (
|
|
|
335
362
|
descriptor: IndexedExecutionDescriptor,
|
|
336
363
|
configuration: OwnedIndexedState,
|
|
337
364
|
sourceIndex: number,
|
|
338
|
-
event: any
|
|
365
|
+
event: any,
|
|
366
|
+
actorScope?: PlanningActorScope
|
|
339
367
|
): any => {
|
|
340
368
|
const source = descriptor.nodes[sourceIndex]!
|
|
341
369
|
const parentIndex = descriptor.parentIndices[sourceIndex]!
|
|
342
|
-
const
|
|
370
|
+
const ancestors: Record<string, unknown> = {}
|
|
343
371
|
for (const ancestorIndex of descriptor.ancestorIndices[sourceIndex]!) {
|
|
344
|
-
|
|
372
|
+
ancestors[descriptor.nodes[ancestorIndex]!.path] = configuration.values[ancestorIndex]
|
|
345
373
|
}
|
|
346
374
|
return {
|
|
375
|
+
...getActorContext(machine, actorScope),
|
|
347
376
|
state: configuration.values[sourceIndex],
|
|
348
|
-
|
|
349
|
-
|
|
377
|
+
containingState: parentIndex < 0 ? undefined : configuration.values[parentIndex],
|
|
378
|
+
ancestors,
|
|
350
379
|
event,
|
|
351
380
|
snapshot: snapshotFromIndexedState(descriptor, configuration),
|
|
352
381
|
target: getTargetBuilder(machine, source.path)
|
|
@@ -398,7 +427,7 @@ const collectIndexedTransition = (
|
|
|
398
427
|
let commands: Array<RuntimeCommand> | undefined
|
|
399
428
|
let raisedEvents: Array<any> | undefined
|
|
400
429
|
let emittedEvents: Array<unknown> | undefined
|
|
401
|
-
const
|
|
430
|
+
const result = transition(context, {
|
|
402
431
|
raise: (event: unknown) => {
|
|
403
432
|
;(raisedEvents ??= []).push(decodeEventSync(machine, event))
|
|
404
433
|
},
|
|
@@ -406,14 +435,14 @@ const collectIndexedTransition = (
|
|
|
406
435
|
;(emittedEvents ??= []).push(decodeEmitSync(machine, event))
|
|
407
436
|
},
|
|
408
437
|
sendTo: (child: unknown, event: unknown) => {
|
|
409
|
-
;(commands ??= []).push({ _tag: "SendTo",
|
|
438
|
+
;(commands ??= []).push({ _tag: "SendTo", target: child as any, event })
|
|
410
439
|
},
|
|
411
440
|
stop: (child: unknown) => {
|
|
412
441
|
;(commands ??= []).push({ _tag: "Stop", child: child as any })
|
|
413
442
|
}
|
|
414
443
|
})
|
|
415
444
|
return {
|
|
416
|
-
state,
|
|
445
|
+
state: isNoTarget(result) ? undefined : result,
|
|
417
446
|
commands: commands ?? emptyExecutionValues,
|
|
418
447
|
raisedEvents: raisedEvents ?? emptyExecutionValues,
|
|
419
448
|
emittedEvents: emittedEvents ?? emptyExecutionValues
|
|
@@ -424,7 +453,8 @@ const selectIndexedEventTransitions = (
|
|
|
424
453
|
machine: Machine.Any,
|
|
425
454
|
descriptor: IndexedExecutionDescriptor,
|
|
426
455
|
configuration: OwnedIndexedState,
|
|
427
|
-
event: any
|
|
456
|
+
event: any,
|
|
457
|
+
actorScope?: PlanningActorScope
|
|
428
458
|
): ReadonlyArray<IndexedSelectedTransition> => {
|
|
429
459
|
const selected: Array<IndexedSelectedTransition> = []
|
|
430
460
|
for (const leafIndex of configuration.activeLeaves) {
|
|
@@ -445,7 +475,8 @@ const selectIndexedEventTransitions = (
|
|
|
445
475
|
descriptor,
|
|
446
476
|
configuration,
|
|
447
477
|
sourceIndex,
|
|
448
|
-
event
|
|
478
|
+
event,
|
|
479
|
+
actorScope
|
|
449
480
|
)
|
|
450
481
|
})
|
|
451
482
|
}
|
|
@@ -628,7 +659,8 @@ const planIndexedFlatState = (
|
|
|
628
659
|
descriptor: IndexedExecutionDescriptor,
|
|
629
660
|
configuration: OwnedIndexedState,
|
|
630
661
|
decoded: { readonly _tag: PropertyKey },
|
|
631
|
-
retainMicrosteps: boolean
|
|
662
|
+
retainMicrosteps: boolean,
|
|
663
|
+
actorScope?: PlanningActorScope
|
|
632
664
|
): ExecutionMacrostep<OwnedIndexedState> => {
|
|
633
665
|
let current = configuration
|
|
634
666
|
let event: any = decoded
|
|
@@ -680,9 +712,10 @@ const planIndexedFlatState = (
|
|
|
680
712
|
machine,
|
|
681
713
|
transition.transition,
|
|
682
714
|
{
|
|
715
|
+
...getActorContext(machine, actorScope),
|
|
683
716
|
state: current.values[sourceIndex],
|
|
684
|
-
|
|
685
|
-
|
|
717
|
+
containingState: undefined,
|
|
718
|
+
ancestors: {},
|
|
686
719
|
event,
|
|
687
720
|
snapshot: snapshotFromIndexedState(descriptor, current),
|
|
688
721
|
target: getTargetBuilder(machine, sourcePath)
|
|
@@ -773,11 +806,42 @@ const planIndexedState = (
|
|
|
773
806
|
descriptor: IndexedExecutionDescriptor,
|
|
774
807
|
configuration: OwnedIndexedState,
|
|
775
808
|
input: unknown,
|
|
776
|
-
retainMicrosteps: boolean
|
|
809
|
+
retainMicrosteps: boolean,
|
|
810
|
+
actorScope?: PlanningActorScope
|
|
777
811
|
): ExecutionMacrostep<OwnedIndexedState> => {
|
|
812
|
+
if (InvocationEvent.isInvocationEvent(input)) {
|
|
813
|
+
// Invocation lifecycle transitions retain the generic planner as their
|
|
814
|
+
// semantic reference. Ordinary machine events and initialization stay on
|
|
815
|
+
// the indexed representation; only the private lifecycle macrostep crosses
|
|
816
|
+
// the representation boundary.
|
|
817
|
+
const planned = planConfiguration(
|
|
818
|
+
machine as any,
|
|
819
|
+
actorScope === undefined
|
|
820
|
+
? activeConfigurationFromIndexedState(descriptor, configuration)
|
|
821
|
+
: withActorScope(activeConfigurationFromIndexedState(descriptor, configuration), actorScope),
|
|
822
|
+
input
|
|
823
|
+
)
|
|
824
|
+
return {
|
|
825
|
+
next: ownedIndexedStateFromActive(descriptor, planned.next),
|
|
826
|
+
commands: planned.commands,
|
|
827
|
+
emittedEvents: planned.emittedEvents,
|
|
828
|
+
microsteps: planned.microsteps.map((step) => ({
|
|
829
|
+
next: ownedIndexedStateFromActive(descriptor, step.next),
|
|
830
|
+
event: step.event,
|
|
831
|
+
commands: step.commands,
|
|
832
|
+
raisedEvents: step.raisedEvents,
|
|
833
|
+
emittedEvents: step.emittedEvents,
|
|
834
|
+
exitPaths: step.exitPaths,
|
|
835
|
+
entryPaths: step.entryPaths,
|
|
836
|
+
changed: step.changed
|
|
837
|
+
})),
|
|
838
|
+
done: planned.done,
|
|
839
|
+
output: planned.output
|
|
840
|
+
}
|
|
841
|
+
}
|
|
778
842
|
const decoded = decodeEventSync(machine, input)
|
|
779
843
|
if (descriptor.flat) {
|
|
780
|
-
return planIndexedFlatState(machine, descriptor, configuration, decoded, retainMicrosteps)
|
|
844
|
+
return planIndexedFlatState(machine, descriptor, configuration, decoded, retainMicrosteps, actorScope)
|
|
781
845
|
}
|
|
782
846
|
if (descriptor.finalIndices.some((index) => configuration.active[index] === 1)) {
|
|
783
847
|
const active = activeConfigurationFromIndexedState(descriptor, configuration)
|
|
@@ -798,7 +862,7 @@ const planIndexedState = (
|
|
|
798
862
|
}
|
|
799
863
|
}
|
|
800
864
|
|
|
801
|
-
const selections = selectIndexedEventTransitions(machine, descriptor, configuration, decoded)
|
|
865
|
+
const selections = selectIndexedEventTransitions(machine, descriptor, configuration, decoded, actorScope)
|
|
802
866
|
if (selections.length === 0) {
|
|
803
867
|
return {
|
|
804
868
|
next: configuration,
|
|
@@ -866,7 +930,7 @@ const planIndexedState = (
|
|
|
866
930
|
}
|
|
867
931
|
raisedIndex += 1
|
|
868
932
|
currentEvent = raised
|
|
869
|
-
const raisedSelections = selectIndexedEventTransitions(machine, descriptor, current, raised)
|
|
933
|
+
const raisedSelections = selectIndexedEventTransitions(machine, descriptor, current, raised, actorScope)
|
|
870
934
|
if (raisedSelections.length === 0) continue
|
|
871
935
|
const step = indexedMicrostep(machine, descriptor, current, raised, raisedSelections)
|
|
872
936
|
current = step.next
|
|
@@ -884,10 +948,12 @@ export interface CompiledExecutionPlan {
|
|
|
884
948
|
readonly plan: (
|
|
885
949
|
state: unknown,
|
|
886
950
|
event: unknown,
|
|
887
|
-
retainMicrosteps?: boolean
|
|
951
|
+
retainMicrosteps?: boolean,
|
|
952
|
+
actorScope?: PlanningActorScope
|
|
888
953
|
) => ExecutionMacrostep
|
|
889
954
|
readonly initial?: (
|
|
890
|
-
args: ReadonlyArray<unknown
|
|
955
|
+
args: ReadonlyArray<unknown>,
|
|
956
|
+
actorScope?: PlanningActorScope
|
|
891
957
|
) => {
|
|
892
958
|
readonly state: Machine.Snapshot<any>
|
|
893
959
|
readonly configuration: unknown
|
|
@@ -904,7 +970,14 @@ const makeActiveExecutionPlan = (machine: Machine.Any): CompiledExecutionPlan =>
|
|
|
904
970
|
fromConfiguration: (configuration) => configuration,
|
|
905
971
|
toConfiguration: (state) => state as ActiveConfiguration,
|
|
906
972
|
snapshot: (state) => snapshotFromConfiguration(machine, state as ActiveConfiguration),
|
|
907
|
-
plan: (state, event
|
|
973
|
+
plan: (state, event, _retainMicrosteps, actorScope) =>
|
|
974
|
+
planConfiguration(
|
|
975
|
+
machine as any,
|
|
976
|
+
actorScope === undefined
|
|
977
|
+
? state as ActiveConfiguration
|
|
978
|
+
: withActorScope(state as ActiveConfiguration, actorScope),
|
|
979
|
+
event as any
|
|
980
|
+
)
|
|
908
981
|
})
|
|
909
982
|
|
|
910
983
|
const makeIndexedExecutionPlan = (
|
|
@@ -914,16 +987,17 @@ const makeIndexedExecutionPlan = (
|
|
|
914
987
|
fromConfiguration: (configuration) => ownedIndexedStateFromActive(indexed, configuration),
|
|
915
988
|
toConfiguration: (state) => activeConfigurationFromIndexedState(indexed, state as OwnedIndexedState),
|
|
916
989
|
snapshot: (state) => snapshotFromIndexedState(indexed, state as OwnedIndexedState),
|
|
917
|
-
plan: (state, event, retainMicrosteps = false) =>
|
|
918
|
-
planIndexedState(machine, indexed, state as OwnedIndexedState, event, retainMicrosteps),
|
|
919
|
-
initial: (args) => {
|
|
990
|
+
plan: (state, event, retainMicrosteps = false, actorScope) =>
|
|
991
|
+
planIndexedState(machine, indexed, state as OwnedIndexedState, event, retainMicrosteps, actorScope),
|
|
992
|
+
initial: (args, actorScope) => {
|
|
920
993
|
const inputArgs = machine.input === undefined
|
|
921
994
|
? args
|
|
922
995
|
: args.length === 0
|
|
923
996
|
? (decodeInputSync(machine, machine.input, undefined), args)
|
|
924
997
|
: [decodeInputSync(machine, machine.input, args[0])]
|
|
925
998
|
const initial = machine.initial(...inputArgs as any)
|
|
926
|
-
const
|
|
999
|
+
const normalized = normalizeConfigurationSync(machine, initial as Machine.Snapshot<any>)
|
|
1000
|
+
const active = actorScope === undefined ? normalized : withActorScope(normalized, actorScope)
|
|
927
1001
|
validateInitialConfiguration(machine, active)
|
|
928
1002
|
const completed = completeConfigurationSync(machine, active, InitialEvent).configuration
|
|
929
1003
|
const configuration = ownedIndexedStateFromActive(indexed, completed)
|