@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
|
@@ -7,13 +7,7 @@
|
|
|
7
7
|
import * as Cause from "effect/Cause"
|
|
8
8
|
import * as Effect from "effect/Effect"
|
|
9
9
|
import type * as Schema from "effect/Schema"
|
|
10
|
-
import type {
|
|
11
|
-
Enqueue,
|
|
12
|
-
InitialEvent as MachineInitialEvent,
|
|
13
|
-
Machine,
|
|
14
|
-
MachineReferences,
|
|
15
|
-
MachineTarget
|
|
16
|
-
} from "../../Machine.js"
|
|
10
|
+
import type { Enqueue, InitialEvent as MachineInitialEvent, Machine, MachineTarget } from "../../Machine.js"
|
|
17
11
|
import { getTargetBuilder, makeCollector, type RuntimeCommand } from "./command.js"
|
|
18
12
|
import {
|
|
19
13
|
type ActiveConfiguration,
|
|
@@ -53,6 +47,7 @@ import {
|
|
|
53
47
|
getNode,
|
|
54
48
|
type InitialTarget as InitialTargetInstruction,
|
|
55
49
|
isChoiceTarget,
|
|
50
|
+
isDeclined,
|
|
56
51
|
isHistoryTarget,
|
|
57
52
|
isInitialTarget,
|
|
58
53
|
isNoTarget,
|
|
@@ -71,6 +66,7 @@ export type MicrostepPlan<State, Event, E, R> = {
|
|
|
71
66
|
readonly trigger: Machine.TransitionTrigger
|
|
72
67
|
readonly reenter: boolean
|
|
73
68
|
readonly branchIndex: number
|
|
69
|
+
readonly branchKey: string | undefined
|
|
74
70
|
readonly target: string | undefined
|
|
75
71
|
readonly resolvedTarget: string | undefined
|
|
76
72
|
}>
|
|
@@ -103,11 +99,12 @@ export type MacrostepPlan<State, Event, E, R, Output> =
|
|
|
103
99
|
export type TransitionHandler<States extends Machine.StateSchemas, E, R, Context> = (
|
|
104
100
|
context: Context,
|
|
105
101
|
enqueue: Enqueue<any, any>
|
|
106
|
-
) => Machine.HandlerResult<States, E, R>
|
|
102
|
+
) => Machine.HandlerResult<States, E, R> | Machine.Declined
|
|
107
103
|
|
|
108
104
|
type TransitionEvaluation<States extends Machine.StateSchemas, E, R> = {
|
|
109
|
-
readonly result: Machine.HandlerResult<States, E, R>
|
|
105
|
+
readonly result: Machine.HandlerResult<States, E, R> | Machine.Declined
|
|
110
106
|
readonly branchIndex: number
|
|
107
|
+
readonly branchKey: string | undefined
|
|
111
108
|
}
|
|
112
109
|
|
|
113
110
|
type TransitionEvaluator<States extends Machine.StateSchemas, E, R, Context> = (
|
|
@@ -115,24 +112,42 @@ type TransitionEvaluator<States extends Machine.StateSchemas, E, R, Context> = (
|
|
|
115
112
|
enqueue: Enqueue<any, any>
|
|
116
113
|
) => TransitionEvaluation<States, E, R>
|
|
117
114
|
|
|
118
|
-
|
|
119
|
-
|
|
115
|
+
type PlanningMachineReferences = {
|
|
116
|
+
readonly self: MachineTarget<any>
|
|
117
|
+
readonly parent: MachineTarget<any> | undefined
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
type BehaviorMachineReferences = {
|
|
121
|
+
readonly self: MachineTarget<any>
|
|
122
|
+
readonly parent?: MachineTarget<any> | undefined
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const rootMachineReferences = new WeakMap<Machine.Any, BehaviorMachineReferences>()
|
|
126
|
+
const scopedMachineReferences = new WeakMap<Machine.Any, BehaviorMachineReferences>()
|
|
127
|
+
|
|
128
|
+
const exposeMachineReferences = (
|
|
129
|
+
machine: Machine.Any,
|
|
130
|
+
machineReferences: PlanningMachineReferences
|
|
131
|
+
): BehaviorMachineReferences =>
|
|
132
|
+
machine.parent === undefined
|
|
133
|
+
? { self: machineReferences.self }
|
|
134
|
+
: { self: machineReferences.self, parent: machineReferences.parent }
|
|
120
135
|
|
|
121
136
|
export const withMachineReferences = (
|
|
122
137
|
machine: Machine.Any,
|
|
123
|
-
machineReferences:
|
|
138
|
+
machineReferences: PlanningMachineReferences
|
|
124
139
|
): Machine.Any => {
|
|
125
140
|
const scoped = Object.create(machine) as Machine.Any
|
|
126
|
-
scopedMachineReferences.set(scoped,
|
|
141
|
+
scopedMachineReferences.set(scoped, exposeMachineReferences(machine, machineReferences))
|
|
127
142
|
return scoped
|
|
128
143
|
}
|
|
129
144
|
|
|
130
145
|
const resolveMachineReferences = (
|
|
131
146
|
machine: Machine.Any,
|
|
132
147
|
configuration: ActiveConfiguration
|
|
133
|
-
):
|
|
148
|
+
): BehaviorMachineReferences => {
|
|
134
149
|
const scope = configuration.machineReferences
|
|
135
|
-
if (scope !== undefined) return scope
|
|
150
|
+
if (scope !== undefined) return exposeMachineReferences(machine, scope)
|
|
136
151
|
const machineScope = scopedMachineReferences.get(machine)
|
|
137
152
|
if (machineScope !== undefined) return machineScope
|
|
138
153
|
const cached = rootMachineReferences.get(machine)
|
|
@@ -142,7 +157,7 @@ const resolveMachineReferences = (
|
|
|
142
157
|
sessionId: "Machine.plan",
|
|
143
158
|
send: () => Effect.fail(new StoppedError())
|
|
144
159
|
}
|
|
145
|
-
const root = { self, parent: undefined }
|
|
160
|
+
const root = exposeMachineReferences(machine, { self, parent: undefined })
|
|
146
161
|
rootMachineReferences.set(machine, root)
|
|
147
162
|
return root
|
|
148
163
|
}
|
|
@@ -151,6 +166,7 @@ type EventTransition<States extends Machine.StateSchemas, E, R, Context> =
|
|
|
151
166
|
| TransitionHandler<States, E, R, Context>
|
|
152
167
|
| {
|
|
153
168
|
readonly reenter?: boolean
|
|
169
|
+
readonly declinable?: boolean
|
|
154
170
|
readonly targets?: ReadonlyArray<string>
|
|
155
171
|
readonly transition: TransitionHandler<States, E, R, Context>
|
|
156
172
|
readonly evaluate?: TransitionEvaluator<States, E, R, Context>
|
|
@@ -158,6 +174,7 @@ type EventTransition<States extends Machine.StateSchemas, E, R, Context> =
|
|
|
158
174
|
|
|
159
175
|
export type MicrostepTransition<States extends Machine.StateSchemas, E, R, Context> = {
|
|
160
176
|
readonly reenter: boolean
|
|
177
|
+
readonly declinable: boolean
|
|
161
178
|
readonly targets: ReadonlyArray<string> | undefined
|
|
162
179
|
readonly transition: TransitionHandler<States, E, R, Context>
|
|
163
180
|
readonly evaluate: TransitionEvaluator<States, E, R, Context> | undefined
|
|
@@ -170,9 +187,10 @@ export const normalizeTransition = <States extends Machine.StateSchemas, E, R, C
|
|
|
170
187
|
return undefined
|
|
171
188
|
}
|
|
172
189
|
return typeof transition === "function"
|
|
173
|
-
? { reenter: false, targets: undefined, transition, evaluate: undefined }
|
|
190
|
+
? { reenter: false, declinable: false, targets: undefined, transition, evaluate: undefined }
|
|
174
191
|
: {
|
|
175
192
|
reenter: transition.reenter === true,
|
|
193
|
+
declinable: transition.declinable === true,
|
|
176
194
|
targets: transition.targets,
|
|
177
195
|
transition: transition.transition,
|
|
178
196
|
evaluate: transition.evaluate
|
|
@@ -214,11 +232,16 @@ const collectTransition = <
|
|
|
214
232
|
) => {
|
|
215
233
|
const collected = makeCollector<Event>(machine)
|
|
216
234
|
const evaluated = evaluate === undefined
|
|
217
|
-
? { result: transition(context, collected.enqueue), branchIndex: 0 }
|
|
235
|
+
? { result: transition(context, collected.enqueue), branchIndex: 0, branchKey: undefined }
|
|
218
236
|
: evaluate(context, collected.enqueue)
|
|
237
|
+
if (isDeclined(evaluated.result)) {
|
|
238
|
+
return { declined: true as const }
|
|
239
|
+
}
|
|
219
240
|
return {
|
|
241
|
+
declined: false as const,
|
|
220
242
|
state: isNoTarget(evaluated.result) ? undefined : evaluated.result,
|
|
221
243
|
branchIndex: evaluated.branchIndex,
|
|
244
|
+
branchKey: evaluated.branchKey,
|
|
222
245
|
commands: collected.commands,
|
|
223
246
|
raisedEvents: collected.raisedEvents,
|
|
224
247
|
emittedEvents: collected.emittedEvents
|
|
@@ -518,11 +541,35 @@ export type SelectedTransition<States extends Machine.StateSchemas, E, R, Contex
|
|
|
518
541
|
readonly trigger: Machine.TransitionTrigger
|
|
519
542
|
readonly transition: MicrostepTransition<States, E, R, Context>
|
|
520
543
|
readonly context: Context
|
|
544
|
+
readonly collected?: {
|
|
545
|
+
readonly declined: false
|
|
546
|
+
readonly state: unknown
|
|
547
|
+
readonly branchIndex: number
|
|
548
|
+
readonly branchKey: string | undefined
|
|
549
|
+
readonly commands: ReadonlyArray<RuntimeCommand>
|
|
550
|
+
readonly raisedEvents: ReadonlyArray<unknown>
|
|
551
|
+
readonly emittedEvents: ReadonlyArray<unknown>
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
const resolveDeclinableCandidate = <States extends Machine.StateSchemas, E, R, Context>(
|
|
556
|
+
machine: Machine.Any,
|
|
557
|
+
selection: SelectedTransition<States, E, R, Context>
|
|
558
|
+
): SelectedTransition<States, E, R, Context> | undefined => {
|
|
559
|
+
if (!selection.transition.declinable) return selection
|
|
560
|
+
const collected = collectTransition(
|
|
561
|
+
machine,
|
|
562
|
+
selection.transition.transition,
|
|
563
|
+
selection.context,
|
|
564
|
+
selection.transition.evaluate
|
|
565
|
+
)
|
|
566
|
+
return collected.declined ? undefined : { ...selection, collected }
|
|
521
567
|
}
|
|
522
568
|
|
|
523
569
|
export type EvaluatedTransition<States extends Machine.StateSchemas, Event, E, R, Context> = {
|
|
524
570
|
readonly selection: SelectedTransition<States, E, R, Context>
|
|
525
571
|
readonly branchIndex: number
|
|
572
|
+
readonly branchKey: string | undefined
|
|
526
573
|
readonly unresolvedTarget:
|
|
527
574
|
| Machine.Snapshot<States>
|
|
528
575
|
| Machine.Target<States, Machine.StateIdentifier<States>>
|
|
@@ -544,6 +591,7 @@ export type EvaluatedTransition<States extends Machine.StateSchemas, Event, E, R
|
|
|
544
591
|
readonly trigger: Machine.TransitionTrigger
|
|
545
592
|
readonly reenter: false
|
|
546
593
|
readonly branchIndex: number
|
|
594
|
+
readonly branchKey: string | undefined
|
|
547
595
|
readonly target: string
|
|
548
596
|
readonly resolvedTarget: string
|
|
549
597
|
}>
|
|
@@ -754,15 +802,16 @@ const selectAlwaysTransitions = <
|
|
|
754
802
|
>
|
|
755
803
|
> = []
|
|
756
804
|
const selectedSources = new Set<string>()
|
|
805
|
+
const evaluatedSources = new Map<string, SelectedTransition<States, E, R, any> | undefined>()
|
|
757
806
|
let snapshot: Machine.Snapshot<States> | undefined
|
|
758
807
|
const capturedSnapshot = () => snapshot ??= snapshotFromConfiguration<States>(machine, configuration)
|
|
759
808
|
for (const leaf of getActiveLeafPaths(machine, configuration)) {
|
|
760
809
|
for (const path of getLeafCandidatePaths(machine, leaf)) {
|
|
761
810
|
const always = normalizeTransition(machine.handlers[path]?.always)
|
|
762
811
|
if (always !== undefined) {
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
812
|
+
let candidate = evaluatedSources.get(path)
|
|
813
|
+
if (!evaluatedSources.has(path)) {
|
|
814
|
+
candidate = resolveDeclinableCandidate(machine, {
|
|
766
815
|
sourcePath: path,
|
|
767
816
|
leafPath: leaf,
|
|
768
817
|
trigger: { type: "always" },
|
|
@@ -791,6 +840,12 @@ const selectAlwaysTransitions = <
|
|
|
791
840
|
target: getTargetBuilder(machine, path)
|
|
792
841
|
}
|
|
793
842
|
})
|
|
843
|
+
evaluatedSources.set(path, candidate)
|
|
844
|
+
}
|
|
845
|
+
if (candidate === undefined) continue
|
|
846
|
+
if (!selectedSources.has(path)) {
|
|
847
|
+
selectedSources.add(path)
|
|
848
|
+
selected.push(candidate as (typeof selected)[number])
|
|
794
849
|
}
|
|
795
850
|
break
|
|
796
851
|
}
|
|
@@ -833,7 +888,7 @@ const selectDoneTransitions = <
|
|
|
833
888
|
const onDone = normalizeTransition(machine.handlers[completion.path]?.onDone)
|
|
834
889
|
if (onDone !== undefined && !selectedSources.has(completion.path)) {
|
|
835
890
|
selectedSources.add(completion.path)
|
|
836
|
-
|
|
891
|
+
const candidate = resolveDeclinableCandidate(machine, {
|
|
837
892
|
sourcePath: completion.path,
|
|
838
893
|
leafPath: getActiveLeafPathFrom(machine, configuration, completion.path),
|
|
839
894
|
trigger: { type: "done" },
|
|
@@ -852,6 +907,7 @@ const selectDoneTransitions = <
|
|
|
852
907
|
capturedSnapshot()
|
|
853
908
|
)
|
|
854
909
|
})
|
|
910
|
+
if (candidate !== undefined) selected.push(candidate)
|
|
855
911
|
}
|
|
856
912
|
}
|
|
857
913
|
return selected
|
|
@@ -892,15 +948,16 @@ const selectEventTransitions = <
|
|
|
892
948
|
>
|
|
893
949
|
> = []
|
|
894
950
|
const selectedSources = new Set<string>()
|
|
951
|
+
const evaluatedSources = new Map<string, SelectedTransition<States, E, R, any> | undefined>()
|
|
895
952
|
let snapshot: Machine.Snapshot<States> | undefined
|
|
896
953
|
const capturedSnapshot = () => snapshot ??= snapshotFromConfiguration<States>(machine, configuration)
|
|
897
954
|
for (const leaf of getActiveLeafPaths(machine, configuration)) {
|
|
898
955
|
for (const path of getLeafCandidatePaths(machine, leaf)) {
|
|
899
956
|
const transition = normalizeTransition(machine.handlers[path]?.on?.[event._tag])
|
|
900
957
|
if (transition !== undefined) {
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
958
|
+
let candidate = evaluatedSources.get(path)
|
|
959
|
+
if (!evaluatedSources.has(path)) {
|
|
960
|
+
candidate = resolveDeclinableCandidate(machine, {
|
|
904
961
|
sourcePath: path,
|
|
905
962
|
leafPath: leaf,
|
|
906
963
|
trigger: { type: "event", event: event._tag },
|
|
@@ -926,6 +983,12 @@ const selectEventTransitions = <
|
|
|
926
983
|
Machine.TagOf<Events[number]>
|
|
927
984
|
>(machine as any, configuration, path, event, capturedSnapshot())
|
|
928
985
|
})
|
|
986
|
+
evaluatedSources.set(path, candidate)
|
|
987
|
+
}
|
|
988
|
+
if (candidate === undefined) continue
|
|
989
|
+
if (!selectedSources.has(path)) {
|
|
990
|
+
selectedSources.add(path)
|
|
991
|
+
selected.push(candidate as (typeof selected)[number])
|
|
929
992
|
}
|
|
930
993
|
break
|
|
931
994
|
}
|
|
@@ -952,7 +1015,9 @@ const selectInvocationTransition = <
|
|
|
952
1015
|
return String(id) === event.id
|
|
953
1016
|
})
|
|
954
1017
|
if (invoke === undefined) return []
|
|
955
|
-
const handler = event.type === "
|
|
1018
|
+
const handler = event.type === "element"
|
|
1019
|
+
? invoke.onElement
|
|
1020
|
+
: event.type === "done"
|
|
956
1021
|
? invoke.onDone
|
|
957
1022
|
: event.type === "failure"
|
|
958
1023
|
? invoke.onFailure
|
|
@@ -968,19 +1033,22 @@ const selectInvocationTransition = <
|
|
|
968
1033
|
snapshot,
|
|
969
1034
|
target: getTargetBuilder(machine, event.path),
|
|
970
1035
|
id: event.id,
|
|
971
|
-
...(event.type === "
|
|
1036
|
+
...(event.type === "element"
|
|
1037
|
+
? { element: event.element }
|
|
1038
|
+
: event.type === "done"
|
|
972
1039
|
? { output: event.output }
|
|
973
1040
|
: event.type === "failure"
|
|
974
1041
|
? { error: event.error }
|
|
975
1042
|
: { snapshot: event.snapshot })
|
|
976
1043
|
}
|
|
977
|
-
|
|
1044
|
+
const candidate = resolveDeclinableCandidate(machine, {
|
|
978
1045
|
sourcePath: event.path,
|
|
979
1046
|
leafPath: getActiveLeafPathFrom(machine, configuration, event.path),
|
|
980
1047
|
trigger: { type: "invoke", id: event.id, outcome: event.type },
|
|
981
1048
|
transition: transition as unknown as MicrostepTransition<States, E, R, any>,
|
|
982
1049
|
context
|
|
983
|
-
}
|
|
1050
|
+
})
|
|
1051
|
+
return candidate === undefined ? [] : [candidate]
|
|
984
1052
|
}
|
|
985
1053
|
|
|
986
1054
|
export const getTargetNodePath = <const States extends Machine.StateSchemas>(
|
|
@@ -1150,6 +1218,7 @@ interface ResolvedChoiceTransition {
|
|
|
1150
1218
|
readonly trigger: Machine.TransitionTrigger
|
|
1151
1219
|
readonly reenter: false
|
|
1152
1220
|
readonly branchIndex: number
|
|
1221
|
+
readonly branchKey: string | undefined
|
|
1153
1222
|
readonly target: string
|
|
1154
1223
|
readonly resolvedTarget: string
|
|
1155
1224
|
}
|
|
@@ -1228,6 +1297,7 @@ function resolveChoiceTarget(
|
|
|
1228
1297
|
trigger: { type: "choice" },
|
|
1229
1298
|
reenter: false,
|
|
1230
1299
|
branchIndex: collected.branchIndex,
|
|
1300
|
+
branchKey: collected.branchKey,
|
|
1231
1301
|
target: returnedPath,
|
|
1232
1302
|
resolvedTarget: nested?.target.path ?? returnedPath
|
|
1233
1303
|
})
|
|
@@ -1264,12 +1334,15 @@ const collectEvaluatedTransition = <
|
|
|
1264
1334
|
selection: SelectedTransition<States, E, R, Context>
|
|
1265
1335
|
) => {
|
|
1266
1336
|
const stateIdentifier = selection.leafPath
|
|
1267
|
-
const transitionResult = collectTransition<States, Event, E, R, Context>(
|
|
1337
|
+
const transitionResult = selection.collected ?? collectTransition<States, Event, E, R, Context>(
|
|
1268
1338
|
machine,
|
|
1269
1339
|
selection.transition.transition,
|
|
1270
1340
|
selection.context,
|
|
1271
1341
|
selection.transition.evaluate
|
|
1272
1342
|
)
|
|
1343
|
+
if (transitionResult.declined) {
|
|
1344
|
+
throw new Error("Machine transition returned decline without declaring declinable: true")
|
|
1345
|
+
}
|
|
1273
1346
|
const unresolvedTarget = transitionResult.state === undefined
|
|
1274
1347
|
? undefined
|
|
1275
1348
|
: transitionResult.state as
|
|
@@ -1398,6 +1471,7 @@ const collectEvaluatedTransition = <
|
|
|
1398
1471
|
return {
|
|
1399
1472
|
selection,
|
|
1400
1473
|
branchIndex: transitionResult.branchIndex,
|
|
1474
|
+
branchKey: transitionResult.branchKey,
|
|
1401
1475
|
unresolvedTarget,
|
|
1402
1476
|
target,
|
|
1403
1477
|
commands: [
|
|
@@ -1444,6 +1518,7 @@ const collectEvaluatedTransition = <
|
|
|
1444
1518
|
return {
|
|
1445
1519
|
selection,
|
|
1446
1520
|
branchIndex: transitionResult.branchIndex,
|
|
1521
|
+
branchKey: transitionResult.branchKey,
|
|
1447
1522
|
unresolvedTarget,
|
|
1448
1523
|
target,
|
|
1449
1524
|
commands: [
|
|
@@ -1799,6 +1874,7 @@ const microstep = <
|
|
|
1799
1874
|
trigger: transition.selection.trigger,
|
|
1800
1875
|
reenter: transition.selection.transition.reenter,
|
|
1801
1876
|
branchIndex: transition.branchIndex,
|
|
1877
|
+
branchKey: transition.branchKey,
|
|
1802
1878
|
target: transition.unresolvedTarget === undefined ? undefined : getTargetNodePath(transition.unresolvedTarget),
|
|
1803
1879
|
resolvedTarget: transition.target === undefined ? undefined : getTargetNodePath(transition.target)
|
|
1804
1880
|
},
|
|
@@ -355,6 +355,9 @@ interface ProcessAddress<in Event> {
|
|
|
355
355
|
source: Inspection.Subject | undefined,
|
|
356
356
|
causedBy: Inspection.Causation | undefined
|
|
357
357
|
) => Effect.Effect<void, StoppedError>
|
|
358
|
+
readonly [acknowledgedSend]?: (
|
|
359
|
+
event: Event
|
|
360
|
+
) => Effect.Effect<AcknowledgedDelivery<unknown>, unknown | StoppedError>
|
|
358
361
|
}
|
|
359
362
|
|
|
360
363
|
const isMachineTarget = (value: unknown): value is MachineTarget<unknown> =>
|
|
@@ -843,7 +846,10 @@ class OwnedChildRuntimeImpl implements OwnedChildRuntime {
|
|
|
843
846
|
private readonly registry: ChildRegistry,
|
|
844
847
|
private readonly self: ProcessAddress<any>,
|
|
845
848
|
private readonly runtime: ProcessRuntime,
|
|
846
|
-
private readonly services?: Context.Context<any
|
|
849
|
+
private readonly services?: Context.Context<any>,
|
|
850
|
+
private readonly sendAcknowledged?: (
|
|
851
|
+
event: unknown
|
|
852
|
+
) => Effect.Effect<AcknowledgedDelivery<unknown>, unknown | StoppedError>
|
|
847
853
|
) {}
|
|
848
854
|
|
|
849
855
|
private has(key: string): boolean {
|
|
@@ -886,7 +892,15 @@ class OwnedChildRuntimeImpl implements OwnedChildRuntime {
|
|
|
886
892
|
...this.self,
|
|
887
893
|
send: (event) => options.sendParent(isCurrent, event),
|
|
888
894
|
sendInspected: (event, source, causedBy) =>
|
|
889
|
-
isCurrent() ? sendMachineTarget(this.self, event, source, causedBy) : Effect.void
|
|
895
|
+
isCurrent() ? sendMachineTarget(this.self, event, source, causedBy) : Effect.void,
|
|
896
|
+
...(this.sendAcknowledged === undefined
|
|
897
|
+
? undefined
|
|
898
|
+
: {
|
|
899
|
+
[acknowledgedSend]: (event: unknown) =>
|
|
900
|
+
isCurrent()
|
|
901
|
+
? this.sendAcknowledged!(event)
|
|
902
|
+
: Effect.interrupt
|
|
903
|
+
})
|
|
890
904
|
}
|
|
891
905
|
const startOptions: StartInternalOptions = {
|
|
892
906
|
detached: true,
|
|
@@ -1048,7 +1062,10 @@ const childlessRuntime: ChildRuntime = {
|
|
|
1048
1062
|
const makeChildRuntimeSync = (
|
|
1049
1063
|
self: ProcessAddress<any>,
|
|
1050
1064
|
runtime: ProcessRuntime,
|
|
1051
|
-
services?: Context.Context<any
|
|
1065
|
+
services?: Context.Context<any>,
|
|
1066
|
+
sendAcknowledged?: (
|
|
1067
|
+
event: unknown
|
|
1068
|
+
) => Effect.Effect<AcknowledgedDelivery<unknown>, unknown | StoppedError>
|
|
1052
1069
|
): ChildRuntime => {
|
|
1053
1070
|
// Child-registry decisions are synchronous and every access below runs in
|
|
1054
1071
|
// one Effect.sync / Effect.suspend step. Keep the unobserved representation
|
|
@@ -1290,15 +1307,18 @@ const makeChildRuntimeSync = (
|
|
|
1290
1307
|
changes,
|
|
1291
1308
|
sendTo,
|
|
1292
1309
|
stop,
|
|
1293
|
-
owned: new OwnedChildRuntimeImpl(registry, self, runtime, services)
|
|
1310
|
+
owned: new OwnedChildRuntimeImpl(registry, self, runtime, services, sendAcknowledged)
|
|
1294
1311
|
}
|
|
1295
1312
|
}
|
|
1296
1313
|
|
|
1297
1314
|
const makeChildRuntime = (
|
|
1298
1315
|
self: ProcessAddress<any>,
|
|
1299
1316
|
runtime: ProcessRuntime,
|
|
1300
|
-
services?: Context.Context<any
|
|
1301
|
-
|
|
1317
|
+
services?: Context.Context<any>,
|
|
1318
|
+
sendAcknowledged?: (
|
|
1319
|
+
event: unknown
|
|
1320
|
+
) => Effect.Effect<AcknowledgedDelivery<unknown>, unknown | StoppedError>
|
|
1321
|
+
): Effect.Effect<ChildRuntime> => Effect.sync(() => makeChildRuntimeSync(self, runtime, services, sendAcknowledged))
|
|
1302
1322
|
|
|
1303
1323
|
// `Machine.logic` permits an arbitrary Effect program, including programs that
|
|
1304
1324
|
// suspend or supervise their own fibers. Keep its two-fiber worker/supervisor
|
|
@@ -1376,6 +1396,23 @@ const startGenericInternal: <
|
|
|
1376
1396
|
Effect.tap(() => Effect.sync(() => publishInspectedSent(inspector, subject!, message)))
|
|
1377
1397
|
)
|
|
1378
1398
|
})
|
|
1399
|
+
const sendAcknowledged:
|
|
1400
|
+
| ((event: Event) => Effect.Effect<AcknowledgedDelivery<State>, Error | StoppedError>)
|
|
1401
|
+
| undefined = logic.execution?._tag !== "Compiled"
|
|
1402
|
+
? undefined
|
|
1403
|
+
: (event) =>
|
|
1404
|
+
Effect.uninterruptibleMask((restore) =>
|
|
1405
|
+
Deferred.make<AcknowledgedDelivery<unknown>, unknown>().pipe(
|
|
1406
|
+
Effect.flatMap((deferred) => {
|
|
1407
|
+
const offered = inspector === undefined
|
|
1408
|
+
? offerDirect({ [AcknowledgedMessageTypeId]: true as const, event, deferred })
|
|
1409
|
+
: offerInspected!(event, undefined, undefined, deferred)
|
|
1410
|
+
return offered.pipe(Effect.andThen(restore(Deferred.await(deferred))))
|
|
1411
|
+
}),
|
|
1412
|
+
Effect.map((delivery) => delivery as AcknowledgedDelivery<State>)
|
|
1413
|
+
)
|
|
1414
|
+
) as Effect.Effect<AcknowledgedDelivery<State>, Error | StoppedError>
|
|
1415
|
+
|
|
1379
1416
|
const self: ProcessAddress<Event> = {
|
|
1380
1417
|
id,
|
|
1381
1418
|
sessionId,
|
|
@@ -1396,22 +1433,6 @@ const startGenericInternal: <
|
|
|
1396
1433
|
? undefined
|
|
1397
1434
|
: { inspectionSubject: subject!, sendInspected: offerInspected! })
|
|
1398
1435
|
}
|
|
1399
|
-
const sendAcknowledged:
|
|
1400
|
-
| ((event: Event) => Effect.Effect<AcknowledgedDelivery<State>, Error | StoppedError>)
|
|
1401
|
-
| undefined = logic.execution?._tag !== "Compiled"
|
|
1402
|
-
? undefined
|
|
1403
|
-
: (event) =>
|
|
1404
|
-
Effect.uninterruptibleMask((restore) =>
|
|
1405
|
-
Deferred.make<AcknowledgedDelivery<unknown>, unknown>().pipe(
|
|
1406
|
-
Effect.flatMap((deferred) => {
|
|
1407
|
-
const offered = inspector === undefined
|
|
1408
|
-
? offerDirect({ [AcknowledgedMessageTypeId]: true as const, event, deferred })
|
|
1409
|
-
: offerInspected!(event, undefined, undefined, deferred)
|
|
1410
|
-
return offered.pipe(Effect.andThen(restore(Deferred.await(deferred))))
|
|
1411
|
-
}),
|
|
1412
|
-
Effect.map((delivery) => delivery as AcknowledgedDelivery<State>)
|
|
1413
|
-
)
|
|
1414
|
-
) as Effect.Effect<AcknowledgedDelivery<State>, Error | StoppedError>
|
|
1415
1436
|
|
|
1416
1437
|
let {
|
|
1417
1438
|
changes: childChanges,
|
|
@@ -1431,7 +1452,12 @@ const startGenericInternal: <
|
|
|
1431
1452
|
sendTo,
|
|
1432
1453
|
spawn,
|
|
1433
1454
|
stop: stopChild
|
|
1434
|
-
} = yield* makeChildRuntime(
|
|
1455
|
+
} = yield* makeChildRuntime(
|
|
1456
|
+
self,
|
|
1457
|
+
runtime,
|
|
1458
|
+
undefined,
|
|
1459
|
+
sendAcknowledged === undefined ? undefined : (event) => sendAcknowledged(event as Event)
|
|
1460
|
+
))
|
|
1435
1461
|
}
|
|
1436
1462
|
const cleanupStartupFailure = <A, E>(exit: Exit.Exit<A, E>): Effect.Effect<void> => {
|
|
1437
1463
|
if (Exit.isSuccess(exit)) return Effect.void
|
|
@@ -2227,7 +2253,12 @@ class CompiledProcess implements MachineRef<any, any, any, any> {
|
|
|
2227
2253
|
initializeCompiledSync(): Effect.Effect<MachineRef<any, any, any, any>, unknown> {
|
|
2228
2254
|
this.publishCreated()
|
|
2229
2255
|
if (!this.execution.childless) {
|
|
2230
|
-
this.childRuntime = makeChildRuntimeSync(
|
|
2256
|
+
this.childRuntime = makeChildRuntimeSync(
|
|
2257
|
+
this.address,
|
|
2258
|
+
this.options.runtime,
|
|
2259
|
+
this.services,
|
|
2260
|
+
(event) => this.sendAcknowledgedEffect(event)
|
|
2261
|
+
)
|
|
2231
2262
|
}
|
|
2232
2263
|
const parent = this.options.parent
|
|
2233
2264
|
const sendParent = this.options.sendParent ?? (parent === undefined
|
|
@@ -2304,7 +2335,12 @@ class CompiledProcess implements MachineRef<any, any, any, any> {
|
|
|
2304
2335
|
return Effect.gen(function*() {
|
|
2305
2336
|
self.publishCreated()
|
|
2306
2337
|
if (!self.execution.childless) {
|
|
2307
|
-
self.childRuntime = yield* makeChildRuntime(
|
|
2338
|
+
self.childRuntime = yield* makeChildRuntime(
|
|
2339
|
+
self.address,
|
|
2340
|
+
self.options.runtime,
|
|
2341
|
+
self.services,
|
|
2342
|
+
(event) => self.sendAcknowledgedEffect(event)
|
|
2343
|
+
)
|
|
2308
2344
|
}
|
|
2309
2345
|
const parent = self.options.parent
|
|
2310
2346
|
const sendParent = self.options.sendParent ?? (parent === undefined
|
|
@@ -138,6 +138,22 @@ const assertPlainRecord: (
|
|
|
138
138
|
}
|
|
139
139
|
}
|
|
140
140
|
|
|
141
|
+
const validateTargetSelectorChildKey = (
|
|
142
|
+
boundary: StateDefinitionBoundary,
|
|
143
|
+
path: string,
|
|
144
|
+
states: unknown,
|
|
145
|
+
key: "initial" | "with",
|
|
146
|
+
message: string
|
|
147
|
+
): void => {
|
|
148
|
+
if (typeof states !== "object" || states === null || Array.isArray(states) || !hasOwn(states, key)) return
|
|
149
|
+
const child = (states as Readonly<Record<string, unknown>>)[key]
|
|
150
|
+
if (
|
|
151
|
+
typeof child === "object" && child !== null && !Schema.isSchema(child) &&
|
|
152
|
+
(child as Readonly<Record<string, unknown>>).type === "history"
|
|
153
|
+
) return
|
|
154
|
+
fail(boundary, `${path}.${key}`, message)
|
|
155
|
+
}
|
|
156
|
+
|
|
141
157
|
const assertAllowedProperties = (
|
|
142
158
|
boundary: StateDefinitionBoundary,
|
|
143
159
|
path: string,
|
|
@@ -252,6 +268,22 @@ const validateStateTree = (
|
|
|
252
268
|
if (typeof node.initial !== "string") {
|
|
253
269
|
fail(boundary, `${path}.initial`, "compound states must declare an initial child key")
|
|
254
270
|
}
|
|
271
|
+
validateTargetSelectorChildKey(
|
|
272
|
+
boundary,
|
|
273
|
+
path,
|
|
274
|
+
node.states,
|
|
275
|
+
"initial",
|
|
276
|
+
"active and choice child states cannot use the reserved target selector key \"initial\""
|
|
277
|
+
)
|
|
278
|
+
if (valued) {
|
|
279
|
+
validateTargetSelectorChildKey(
|
|
280
|
+
boundary,
|
|
281
|
+
path,
|
|
282
|
+
node.states,
|
|
283
|
+
"with",
|
|
284
|
+
"schema-backed compound child states cannot use the reserved local target selector key \"with\""
|
|
285
|
+
)
|
|
286
|
+
}
|
|
255
287
|
const initialKey = node.initial as string
|
|
256
288
|
validateStateTree(boundary, node.states, path, true)
|
|
257
289
|
if (!hasOwn(node.states as object, initialKey)) {
|
|
@@ -270,6 +302,13 @@ const validateStateTree = (
|
|
|
270
302
|
if (!hasOwn(node, "states")) {
|
|
271
303
|
fail(boundary, `${path}.states`, "parallel states must declare child regions")
|
|
272
304
|
}
|
|
305
|
+
validateTargetSelectorChildKey(
|
|
306
|
+
boundary,
|
|
307
|
+
path,
|
|
308
|
+
node.states,
|
|
309
|
+
"initial",
|
|
310
|
+
"active and choice child states cannot use the reserved target selector key \"initial\""
|
|
311
|
+
)
|
|
273
312
|
validateStateTree(boundary, node.states, path, true)
|
|
274
313
|
}
|
|
275
314
|
}
|