@typeonce/effect-machine 0.16.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.
Files changed (61) hide show
  1. package/README.md +98 -106
  2. package/dist/Machine.d.ts +324 -367
  3. package/dist/Machine.d.ts.map +1 -1
  4. package/dist/Machine.js +61 -96
  5. package/dist/Machine.js.map +1 -1
  6. package/dist/internal/machine/atom.d.ts +7 -7
  7. package/dist/internal/machine/atom.d.ts.map +1 -1
  8. package/dist/internal/machine/atom.js.map +1 -1
  9. package/dist/internal/machine/cluster.d.ts +2 -2
  10. package/dist/internal/machine/cluster.d.ts.map +1 -1
  11. package/dist/internal/machine/cluster.js +6 -5
  12. package/dist/internal/machine/cluster.js.map +1 -1
  13. package/dist/internal/machine/executionPlan.d.ts.map +1 -1
  14. package/dist/internal/machine/executionPlan.js +10 -3
  15. package/dist/internal/machine/executionPlan.js.map +1 -1
  16. package/dist/internal/machine/machine.d.ts +8 -6
  17. package/dist/internal/machine/machine.d.ts.map +1 -1
  18. package/dist/internal/machine/machine.js +191 -25
  19. package/dist/internal/machine/machine.js.map +1 -1
  20. package/dist/internal/machine/planner.d.ts +19 -4
  21. package/dist/internal/machine/planner.d.ts.map +1 -1
  22. package/dist/internal/machine/planner.js +56 -20
  23. package/dist/internal/machine/planner.js.map +1 -1
  24. package/dist/internal/machine/stateDefinition.d.ts.map +1 -1
  25. package/dist/internal/machine/stateDefinition.js +14 -0
  26. package/dist/internal/machine/stateDefinition.js.map +1 -1
  27. package/dist/internal/machine/topology.d.ts +9 -0
  28. package/dist/internal/machine/topology.d.ts.map +1 -1
  29. package/dist/internal/machine/topology.js +16 -0
  30. package/dist/internal/machine/topology.js.map +1 -1
  31. package/dist/internal/testing/machine/finiteModel.d.ts.map +1 -1
  32. package/dist/internal/testing/machine/finiteModel.js +15 -20
  33. package/dist/internal/testing/machine/finiteModel.js.map +1 -1
  34. package/dist/internal/testing/machine/transitionCoverage.d.ts.map +1 -1
  35. package/dist/internal/testing/machine/transitionCoverage.js +2 -0
  36. package/dist/internal/testing/machine/transitionCoverage.js.map +1 -1
  37. package/dist/testing/MachineTest.d.ts +11 -11
  38. package/dist/testing/MachineTest.d.ts.map +1 -1
  39. package/dist/testing/MachineTest.js +5 -8
  40. package/dist/testing/MachineTest.js.map +1 -1
  41. package/dist/unstable/cluster/ClusterMachine.d.ts +2 -2
  42. package/dist/unstable/cluster/ClusterMachine.d.ts.map +1 -1
  43. package/dist/unstable/cluster/ClusterMachine.js.map +1 -1
  44. package/dist/unstable/reactivity/AtomMachine.d.ts +12 -12
  45. package/dist/unstable/reactivity/AtomMachine.d.ts.map +1 -1
  46. package/dist/unstable/reactivity/AtomMachine.js.map +1 -1
  47. package/docs/agent-guide.md +144 -147
  48. package/package.json +1 -1
  49. package/src/Machine.ts +1118 -1178
  50. package/src/internal/machine/atom.ts +20 -15
  51. package/src/internal/machine/cluster.ts +14 -9
  52. package/src/internal/machine/executionPlan.ts +8 -3
  53. package/src/internal/machine/machine.ts +301 -40
  54. package/src/internal/machine/planner.ts +89 -27
  55. package/src/internal/machine/stateDefinition.ts +39 -0
  56. package/src/internal/machine/topology.ts +28 -0
  57. package/src/internal/testing/machine/finiteModel.ts +18 -21
  58. package/src/internal/testing/machine/transitionCoverage.ts +2 -0
  59. package/src/testing/MachineTest.ts +14 -11
  60. package/src/unstable/cluster/ClusterMachine.ts +8 -4
  61. 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,
@@ -104,10 +99,10 @@ export type MacrostepPlan<State, Event, E, R, Output> =
104
99
  export type TransitionHandler<States extends Machine.StateSchemas, E, R, Context> = (
105
100
  context: Context,
106
101
  enqueue: Enqueue<any, any>
107
- ) => Machine.HandlerResult<States, E, R>
102
+ ) => Machine.HandlerResult<States, E, R> | Machine.Declined
108
103
 
109
104
  type TransitionEvaluation<States extends Machine.StateSchemas, E, R> = {
110
- readonly result: Machine.HandlerResult<States, E, R>
105
+ readonly result: Machine.HandlerResult<States, E, R> | Machine.Declined
111
106
  readonly branchIndex: number
112
107
  readonly branchKey: string | undefined
113
108
  }
@@ -117,24 +112,42 @@ type TransitionEvaluator<States extends Machine.StateSchemas, E, R, Context> = (
117
112
  enqueue: Enqueue<any, any>
118
113
  ) => TransitionEvaluation<States, E, R>
119
114
 
120
- const rootMachineReferences = new WeakMap<Machine.Any, MachineReferences<any, any>>()
121
- const scopedMachineReferences = new WeakMap<Machine.Any, MachineReferences<any, any>>()
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 }
122
135
 
123
136
  export const withMachineReferences = (
124
137
  machine: Machine.Any,
125
- machineReferences: MachineReferences<any, any>
138
+ machineReferences: PlanningMachineReferences
126
139
  ): Machine.Any => {
127
140
  const scoped = Object.create(machine) as Machine.Any
128
- scopedMachineReferences.set(scoped, { self: machineReferences.self, parent: machineReferences.parent })
141
+ scopedMachineReferences.set(scoped, exposeMachineReferences(machine, machineReferences))
129
142
  return scoped
130
143
  }
131
144
 
132
145
  const resolveMachineReferences = (
133
146
  machine: Machine.Any,
134
147
  configuration: ActiveConfiguration
135
- ): MachineReferences<any, any> => {
148
+ ): BehaviorMachineReferences => {
136
149
  const scope = configuration.machineReferences
137
- if (scope !== undefined) return scope
150
+ if (scope !== undefined) return exposeMachineReferences(machine, scope)
138
151
  const machineScope = scopedMachineReferences.get(machine)
139
152
  if (machineScope !== undefined) return machineScope
140
153
  const cached = rootMachineReferences.get(machine)
@@ -144,7 +157,7 @@ const resolveMachineReferences = (
144
157
  sessionId: "Machine.plan",
145
158
  send: () => Effect.fail(new StoppedError())
146
159
  }
147
- const root = { self, parent: undefined }
160
+ const root = exposeMachineReferences(machine, { self, parent: undefined })
148
161
  rootMachineReferences.set(machine, root)
149
162
  return root
150
163
  }
@@ -153,6 +166,7 @@ type EventTransition<States extends Machine.StateSchemas, E, R, Context> =
153
166
  | TransitionHandler<States, E, R, Context>
154
167
  | {
155
168
  readonly reenter?: boolean
169
+ readonly declinable?: boolean
156
170
  readonly targets?: ReadonlyArray<string>
157
171
  readonly transition: TransitionHandler<States, E, R, Context>
158
172
  readonly evaluate?: TransitionEvaluator<States, E, R, Context>
@@ -160,6 +174,7 @@ type EventTransition<States extends Machine.StateSchemas, E, R, Context> =
160
174
 
161
175
  export type MicrostepTransition<States extends Machine.StateSchemas, E, R, Context> = {
162
176
  readonly reenter: boolean
177
+ readonly declinable: boolean
163
178
  readonly targets: ReadonlyArray<string> | undefined
164
179
  readonly transition: TransitionHandler<States, E, R, Context>
165
180
  readonly evaluate: TransitionEvaluator<States, E, R, Context> | undefined
@@ -172,9 +187,10 @@ export const normalizeTransition = <States extends Machine.StateSchemas, E, R, C
172
187
  return undefined
173
188
  }
174
189
  return typeof transition === "function"
175
- ? { reenter: false, targets: undefined, transition, evaluate: undefined }
190
+ ? { reenter: false, declinable: false, targets: undefined, transition, evaluate: undefined }
176
191
  : {
177
192
  reenter: transition.reenter === true,
193
+ declinable: transition.declinable === true,
178
194
  targets: transition.targets,
179
195
  transition: transition.transition,
180
196
  evaluate: transition.evaluate
@@ -218,7 +234,11 @@ const collectTransition = <
218
234
  const evaluated = evaluate === undefined
219
235
  ? { result: transition(context, collected.enqueue), branchIndex: 0, branchKey: undefined }
220
236
  : evaluate(context, collected.enqueue)
237
+ if (isDeclined(evaluated.result)) {
238
+ return { declined: true as const }
239
+ }
221
240
  return {
241
+ declined: false as const,
222
242
  state: isNoTarget(evaluated.result) ? undefined : evaluated.result,
223
243
  branchIndex: evaluated.branchIndex,
224
244
  branchKey: evaluated.branchKey,
@@ -521,6 +541,29 @@ export type SelectedTransition<States extends Machine.StateSchemas, E, R, Contex
521
541
  readonly trigger: Machine.TransitionTrigger
522
542
  readonly transition: MicrostepTransition<States, E, R, Context>
523
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 }
524
567
  }
525
568
 
526
569
  export type EvaluatedTransition<States extends Machine.StateSchemas, Event, E, R, Context> = {
@@ -759,15 +802,16 @@ const selectAlwaysTransitions = <
759
802
  >
760
803
  > = []
761
804
  const selectedSources = new Set<string>()
805
+ const evaluatedSources = new Map<string, SelectedTransition<States, E, R, any> | undefined>()
762
806
  let snapshot: Machine.Snapshot<States> | undefined
763
807
  const capturedSnapshot = () => snapshot ??= snapshotFromConfiguration<States>(machine, configuration)
764
808
  for (const leaf of getActiveLeafPaths(machine, configuration)) {
765
809
  for (const path of getLeafCandidatePaths(machine, leaf)) {
766
810
  const always = normalizeTransition(machine.handlers[path]?.always)
767
811
  if (always !== undefined) {
768
- if (!selectedSources.has(path)) {
769
- selectedSources.add(path)
770
- selected.push({
812
+ let candidate = evaluatedSources.get(path)
813
+ if (!evaluatedSources.has(path)) {
814
+ candidate = resolveDeclinableCandidate(machine, {
771
815
  sourcePath: path,
772
816
  leafPath: leaf,
773
817
  trigger: { type: "always" },
@@ -796,6 +840,12 @@ const selectAlwaysTransitions = <
796
840
  target: getTargetBuilder(machine, path)
797
841
  }
798
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])
799
849
  }
800
850
  break
801
851
  }
@@ -838,7 +888,7 @@ const selectDoneTransitions = <
838
888
  const onDone = normalizeTransition(machine.handlers[completion.path]?.onDone)
839
889
  if (onDone !== undefined && !selectedSources.has(completion.path)) {
840
890
  selectedSources.add(completion.path)
841
- selected.push({
891
+ const candidate = resolveDeclinableCandidate(machine, {
842
892
  sourcePath: completion.path,
843
893
  leafPath: getActiveLeafPathFrom(machine, configuration, completion.path),
844
894
  trigger: { type: "done" },
@@ -857,6 +907,7 @@ const selectDoneTransitions = <
857
907
  capturedSnapshot()
858
908
  )
859
909
  })
910
+ if (candidate !== undefined) selected.push(candidate)
860
911
  }
861
912
  }
862
913
  return selected
@@ -897,15 +948,16 @@ const selectEventTransitions = <
897
948
  >
898
949
  > = []
899
950
  const selectedSources = new Set<string>()
951
+ const evaluatedSources = new Map<string, SelectedTransition<States, E, R, any> | undefined>()
900
952
  let snapshot: Machine.Snapshot<States> | undefined
901
953
  const capturedSnapshot = () => snapshot ??= snapshotFromConfiguration<States>(machine, configuration)
902
954
  for (const leaf of getActiveLeafPaths(machine, configuration)) {
903
955
  for (const path of getLeafCandidatePaths(machine, leaf)) {
904
956
  const transition = normalizeTransition(machine.handlers[path]?.on?.[event._tag])
905
957
  if (transition !== undefined) {
906
- if (!selectedSources.has(path)) {
907
- selectedSources.add(path)
908
- selected.push({
958
+ let candidate = evaluatedSources.get(path)
959
+ if (!evaluatedSources.has(path)) {
960
+ candidate = resolveDeclinableCandidate(machine, {
909
961
  sourcePath: path,
910
962
  leafPath: leaf,
911
963
  trigger: { type: "event", event: event._tag },
@@ -931,6 +983,12 @@ const selectEventTransitions = <
931
983
  Machine.TagOf<Events[number]>
932
984
  >(machine as any, configuration, path, event, capturedSnapshot())
933
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])
934
992
  }
935
993
  break
936
994
  }
@@ -983,13 +1041,14 @@ const selectInvocationTransition = <
983
1041
  ? { error: event.error }
984
1042
  : { snapshot: event.snapshot })
985
1043
  }
986
- return [{
1044
+ const candidate = resolveDeclinableCandidate(machine, {
987
1045
  sourcePath: event.path,
988
1046
  leafPath: getActiveLeafPathFrom(machine, configuration, event.path),
989
1047
  trigger: { type: "invoke", id: event.id, outcome: event.type },
990
1048
  transition: transition as unknown as MicrostepTransition<States, E, R, any>,
991
1049
  context
992
- }]
1050
+ })
1051
+ return candidate === undefined ? [] : [candidate]
993
1052
  }
994
1053
 
995
1054
  export const getTargetNodePath = <const States extends Machine.StateSchemas>(
@@ -1275,12 +1334,15 @@ const collectEvaluatedTransition = <
1275
1334
  selection: SelectedTransition<States, E, R, Context>
1276
1335
  ) => {
1277
1336
  const stateIdentifier = selection.leafPath
1278
- const transitionResult = collectTransition<States, Event, E, R, Context>(
1337
+ const transitionResult = selection.collected ?? collectTransition<States, Event, E, R, Context>(
1279
1338
  machine,
1280
1339
  selection.transition.transition,
1281
1340
  selection.context,
1282
1341
  selection.transition.evaluate
1283
1342
  )
1343
+ if (transitionResult.declined) {
1344
+ throw new Error("Machine transition returned decline without declaring declinable: true")
1345
+ }
1284
1346
  const unresolvedTarget = transitionResult.state === undefined
1285
1347
  ? undefined
1286
1348
  : transitionResult.state as
@@ -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
  }
@@ -25,6 +25,8 @@ export const ChoiceTargetTypeId: unique symbol = Symbol("effect/Machine/ChoiceTa
25
25
 
26
26
  export const NoTargetTypeId: unique symbol = Symbol("effect/Machine/NoTarget")
27
27
 
28
+ export const DeclinedTypeId: unique symbol = Symbol("effect/Machine/Declined")
29
+
28
30
  export const TargetSelectionTypeId: unique symbol = Symbol("effect/Machine/TargetSelection")
29
31
 
30
32
  export const SelectedBranchTypeId: unique symbol = Symbol("effect/Machine/SelectedBranch")
@@ -66,6 +68,11 @@ export interface NoTarget {
66
68
  readonly [NoTargetTypeId]: typeof NoTargetTypeId
67
69
  }
68
70
 
71
+ /** Internal marker returned when a declinable transition is not enabled. */
72
+ export interface Declined {
73
+ readonly [DeclinedTypeId]: typeof DeclinedTypeId
74
+ }
75
+
69
76
  export type TargetSelectionKind = "state" | "initial" | "history" | "choice" | "none"
70
77
 
71
78
  export type TargetSelectionScope = "local" | "branch" | "full" | "initial"
@@ -99,6 +106,9 @@ export const makeTargetSelection = (
99
106
  path
100
107
  })
101
108
 
109
+ /** Source-independent definition-time selection for an explicitly targetless transition. */
110
+ export const noneTargetSelection: TargetSelection = makeTargetSelection("none", undefined, "local")
111
+
102
112
  export const isTargetSelection = (u: unknown): u is TargetSelection => hasProperty(u, TargetSelectionTypeId)
103
113
 
104
114
  export const makeSelectedBranch = (
@@ -125,6 +135,14 @@ export const makeNoTarget = (): Machine.NoTarget => noTarget
125
135
 
126
136
  export const isNoTarget = (u: unknown): u is Machine.NoTarget => hasProperty(u, NoTargetTypeId)
127
137
 
138
+ const declined = Object.freeze({
139
+ [DeclinedTypeId]: DeclinedTypeId
140
+ }) as Declined
141
+
142
+ export const makeDeclined = (): Machine.Declined => declined
143
+
144
+ export const isDeclined = (u: unknown): u is Machine.Declined => hasProperty(u, DeclinedTypeId)
145
+
128
146
  export const makeHistoryTarget = (path: string, parent: string): HistoryTarget => ({
129
147
  [HistoryTargetTypeId]: HistoryTargetTypeId,
130
148
  path,
@@ -441,6 +459,11 @@ const transitionBranches = (handler: unknown): ReadonlyArray<Machine.TransitionB
441
459
  ? Array.from(handler.branches as ReadonlyArray<Machine.TransitionBranch>)
442
460
  : []
443
461
 
462
+ const transitionAcceptance = (handler: unknown): Machine.TransitionAcceptance =>
463
+ typeof handler === "object" && handler !== null && "declinable" in handler && handler.declinable === true
464
+ ? "declinable"
465
+ : "required"
466
+
444
467
  export const transitionDefinitions = (
445
468
  machine: Machine.Any
446
469
  ): ReadonlyArray<Machine.TransitionDefinition> => {
@@ -457,6 +480,7 @@ export const transitionDefinitions = (
457
480
  source: node.path,
458
481
  trigger: { type: "choice" },
459
482
  reenter: false,
483
+ acceptance: "required",
460
484
  branches: transitionBranches(choice)
461
485
  })
462
486
  }
@@ -468,6 +492,7 @@ export const transitionDefinitions = (
468
492
  source: node.path,
469
493
  trigger: { type: "event", event },
470
494
  reenter: hasProperty(handler, "reenter") && handler.reenter === true,
495
+ acceptance: transitionAcceptance(handler),
471
496
  branches: transitionBranches(handler)
472
497
  })
473
498
  }
@@ -476,6 +501,7 @@ export const transitionDefinitions = (
476
501
  source: node.path,
477
502
  trigger: { type: "always" },
478
503
  reenter: false,
504
+ acceptance: transitionAcceptance(config.always),
479
505
  branches: transitionBranches(config.always)
480
506
  })
481
507
  }
@@ -484,6 +510,7 @@ export const transitionDefinitions = (
484
510
  source: node.path,
485
511
  trigger: { type: "done" },
486
512
  reenter: false,
513
+ acceptance: transitionAcceptance(config.onDone),
487
514
  branches: transitionBranches(config.onDone)
488
515
  })
489
516
  }
@@ -507,6 +534,7 @@ export const transitionDefinitions = (
507
534
  source: node.path,
508
535
  trigger: { type: "invoke", id, outcome },
509
536
  reenter: typeof handler === "object" && handler !== null && handler.reenter === true,
537
+ acceptance: transitionAcceptance(handler),
510
538
  branches: transitionBranches(handler)
511
539
  })
512
540
  }
@@ -1316,7 +1316,7 @@ const selectHistoryTarget = (builder: Record<string, any>, path: string): unknow
1316
1316
  const parts = path.split(".")
1317
1317
  let current: any = builder
1318
1318
  for (let index = 0; index < parts.length - 1; index++) current = current[parts[index]!]
1319
- return current[parts[parts.length - 1]!]()
1319
+ return current[parts[parts.length - 1]!]
1320
1320
  }
1321
1321
 
1322
1322
  const selectableDefinitionTarget = (
@@ -1356,7 +1356,7 @@ const selectDefinitionTarget = (
1356
1356
  const parts = selectable.split(".")
1357
1357
  for (const part of parts) current = current[part]
1358
1358
  if (typeof current === "function") return current()
1359
- return current.initial()
1359
+ return current.initial
1360
1360
  }
1361
1361
 
1362
1362
  const resolveDefinitionTarget = (
@@ -1415,11 +1415,10 @@ const makeHandlers = (
1415
1415
  if (node._tag === "History") continue
1416
1416
  if (node._tag === "Choice") {
1417
1417
  handlers[node.key] = {
1418
- choice: (Machine.transition as any)({
1419
- target: (to: Record<string, any>) => selectDefinitionTarget(to, path, node.selected, byPath) as any,
1420
- resolve: ({ target }: { readonly target: any }) =>
1421
- resolveDefinitionTarget(target, path, node.selected, byPath)
1422
- } as any)
1418
+ choice: (to: Record<string, any>) =>
1419
+ (selectDefinitionTarget(to, path, node.selected, byPath) as any).resolve(
1420
+ ({ target }: { readonly target: any }) => resolveDefinitionTarget(target, path, node.selected, byPath)
1421
+ )
1423
1422
  }
1424
1423
  continue
1425
1424
  }
@@ -1432,17 +1431,16 @@ const makeHandlers = (
1432
1431
  let onDone: unknown
1433
1432
  for (const transition of transitions.values()) {
1434
1433
  if (transition.source !== path) continue
1435
- const config = (Machine.transition as any)({
1436
- ...("reenter" in transition ? { reenter: transition.reenter } : {}),
1437
- target: (to: Record<string, any>) =>
1438
- transition.target === undefined
1439
- ? to.none()
1440
- : selectDefinitionTarget(to, path, transition.target, byPath),
1441
- resolve: transition.target === undefined
1434
+ const config = (to: Record<string, any>) => {
1435
+ const selected = transition.target === undefined
1436
+ ? to.none
1437
+ : selectDefinitionTarget(to, path, transition.target, byPath)
1438
+ const resolve = transition.target === undefined
1442
1439
  ? () => undefined
1443
1440
  : ({ target }: { readonly target: any }) =>
1444
1441
  resolveDefinitionTarget(target, path, transition.target!, byPath, transition.targetValue)
1445
- } as any)
1442
+ return selected.resolve(resolve, "reenter" in transition ? { reenter: transition.reenter } : undefined)
1443
+ }
1446
1444
  if (transition.trigger.type === "event") on[transition.trigger.event] = config
1447
1445
  else if (transition.trigger.type === "always") always = config
1448
1446
  else onDone = config
@@ -1515,13 +1513,12 @@ export const compileModel = (model: FiniteModel): Machine.Machine.Any => {
1515
1513
  const machine = Machine.make({
1516
1514
  states: defined.states as any,
1517
1515
  events: Machine.events(...eventSchemas) as any,
1518
- initial: {
1519
- target: (to: Record<string, any>) => {
1520
- const selected = to[initial.path]
1521
- return typeof selected === "function" ? selected() : selected.initial()
1522
- },
1523
- resolve: ({ target }: { readonly target: any }) =>
1516
+ initial: (to: Record<string, any>) => {
1517
+ const selected = to[initial.path]
1518
+ const selection = typeof selected === "function" ? selected() : selected.initial
1519
+ return selection.resolve(({ target }: { readonly target: any }) =>
1524
1520
  selectSnapshot({ [initial.path]: target }, initial.path, byPath, [initial.path], 0) as any
1521
+ )
1525
1522
  }
1526
1523
  } as any)
1527
1524
  const transitions = new Map(model.transitions.map((transition) => [
@@ -66,6 +66,7 @@ export const makeTransitionCoverageCollector = <M extends AnyMachine>(
66
66
  source: definition.source,
67
67
  trigger: definition.trigger,
68
68
  reenter: definition.reenter,
69
+ acceptance: definition.acceptance,
69
70
  branches: definition.branches
70
71
  })
71
72
  )
@@ -85,6 +86,7 @@ export const makeTransitionCoverageCollector = <M extends AnyMachine>(
85
86
  source: definition.source,
86
87
  trigger: definition.trigger,
87
88
  reenter: definition.reenter,
89
+ acceptance: definition.acceptance,
88
90
  branch
89
91
  })
90
92
  })
@@ -138,6 +138,10 @@ type ReadyMachine<M extends AnyMachine> =
138
138
  Machine.Machine.OutputStates<M>
139
139
  >
140
140
 
141
+ type RootReadyMachine<M extends AnyMachine> =
142
+ & ReadyMachine<M>
143
+ & Machine.Machine.RootCompatible<Machine.Machine.ParentEvents<M>>
144
+
141
145
  /**
142
146
  * A generated public-input scenario for a machine.
143
147
  *
@@ -238,10 +242,8 @@ export interface Scenarios<M extends AnyMachine> {
238
242
  * }).handle({
239
243
  * Idle: {
240
244
  * on: {
241
- * Reset: Machine.transition({
242
- * target: (to) => to.full.Idle(),
243
- * resolve: ({ target }) => target.from()
244
- * })
245
+ * Reset: (to) =>
246
+ * to.full.Idle().resolve(({ target }) => target.from())
245
247
  * }
246
248
  * }
247
249
  * })
@@ -881,7 +883,7 @@ export { PlannerRuntimeAgreementError } from "../internal/testing/machine/verifi
881
883
  * @since 0.4.0
882
884
  */
883
885
  export const assertPlannerRuntimeAgreement: <M extends AnyMachine, Error, Output>(
884
- machine: ReadyMachine<M>,
886
+ machine: RootReadyMachine<M>,
885
887
  transcript: CausalRuntimeEvidence<M, Error, Output>
886
888
  ) => Effect.Effect<void, internal.PlannerRuntimeAgreementError<M>, RunServices<M>> =
887
889
  internal.assertPlannerRuntimeAgreement
@@ -1447,10 +1449,9 @@ export type ExploreOptions<M extends AnyMachine, Key extends ExplorationKey = Ex
1447
1449
  * }).handle({
1448
1450
  * Count: {
1449
1451
  * on: {
1450
- * Increment: Machine.transition({
1451
- * target: (to) => to.full.Count(),
1452
- * resolve: ({ state, target }) => target(new Count({ value: state.value + 1 }))
1453
- * })
1452
+ * Increment: (to) =>
1453
+ * to.full.Count().resolve(({ state, target }) =>
1454
+ * target(new Count({ value: state.value + 1 })))
1454
1455
  * }
1455
1456
  * }
1456
1457
  * })
@@ -1465,7 +1466,7 @@ export type ExploreOptions<M extends AnyMachine, Key extends ExplorationKey = Ex
1465
1466
  * @since 0.4.0
1466
1467
  */
1467
1468
  export const explore: <M extends AnyMachine, Key extends ExplorationKey>(
1468
- machine: ReadyMachine<M>,
1469
+ machine: RootReadyMachine<M>,
1469
1470
  options: ExploreOptions<M, Key>
1470
1471
  ) => Effect.Effect<
1471
1472
  Exploration<M, Key>,
@@ -1646,7 +1647,7 @@ export type RunServices<M extends AnyMachine> = IsAny<
1646
1647
  * @since 0.4.0
1647
1648
  */
1648
1649
  export const run: <M extends AnyMachine>(
1649
- machine: ReadyMachine<M>,
1650
+ machine: RootReadyMachine<M>,
1650
1651
  scenario: Scenario<M>
1651
1652
  ) => Effect.Effect<Trace<M>, RunFailure<RunError<M>, M>, RunServices<M>> = internal.run
1652
1653
 
@@ -1703,6 +1704,7 @@ export interface TransitionDefinitionCoverageItem<
1703
1704
  readonly source: SourcePath
1704
1705
  readonly trigger: Machine.Machine.TransitionTrigger<EventTag>
1705
1706
  readonly reenter: boolean
1707
+ readonly acceptance: Machine.Machine.TransitionAcceptance
1706
1708
  readonly branches: ReadonlyArray<Machine.Machine.TransitionBranch<TargetPath>>
1707
1709
  }
1708
1710
 
@@ -1725,6 +1727,7 @@ export interface TransitionBranchCoverageItem<
1725
1727
  readonly source: SourcePath
1726
1728
  readonly trigger: Machine.Machine.TransitionTrigger<EventTag>
1727
1729
  readonly reenter: boolean
1730
+ readonly acceptance: Machine.Machine.TransitionAcceptance
1728
1731
  readonly branch: Machine.Machine.TransitionBranch<TargetPath>
1729
1732
  }
1730
1733
 
@@ -342,7 +342,8 @@ export const make: <
342
342
  Output,
343
343
  Emits extends ReadonlyArray<Machine.Machine.TaggedSchema>,
344
344
  OutputStates extends Machine.Machine.StateIdentifier<States>,
345
- InputEvents extends ReadonlyArray<Machine.Machine.TaggedSchema> = Events
345
+ InputEvents extends ReadonlyArray<Machine.Machine.TaggedSchema> = Events,
346
+ ParentEvents extends ReadonlyArray<Machine.Machine.TaggedSchema> = readonly []
346
347
  >(
347
348
  type: Type,
348
349
  machine:
@@ -359,9 +360,11 @@ export const make: <
359
360
  Output,
360
361
  Emits,
361
362
  OutputStates,
362
- InputEvents
363
+ InputEvents,
364
+ ParentEvents
363
365
  >
364
- & EnsureExecutable<States, UnhandledStates, OutputStates>,
366
+ & EnsureExecutable<States, UnhandledStates, OutputStates>
367
+ & Machine.Machine.RootCompatible<ParentEvents>,
365
368
  options: {
366
369
  readonly version: string
367
370
  },
@@ -381,7 +384,8 @@ export const make: <
381
384
  Output,
382
385
  Emits,
383
386
  OutputStates,
384
- InputEvents
387
+ InputEvents,
388
+ ParentEvents
385
389
  >,
386
390
  | ExcludeCompatibleRuntime<
387
391
  Machine.ExecutionServices<R | InitialR>,