@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.
Files changed (108) hide show
  1. package/README.md +180 -40
  2. package/dist/Machine.d.ts +659 -481
  3. package/dist/Machine.d.ts.map +1 -1
  4. package/dist/Machine.js +86 -222
  5. package/dist/Machine.js.map +1 -1
  6. package/dist/internal/machine/activities.d.ts +5 -12
  7. package/dist/internal/machine/activities.d.ts.map +1 -1
  8. package/dist/internal/machine/activities.js +47 -17
  9. package/dist/internal/machine/activities.js.map +1 -1
  10. package/dist/internal/machine/atom.d.ts +6 -2
  11. package/dist/internal/machine/atom.d.ts.map +1 -1
  12. package/dist/internal/machine/atom.js +5 -0
  13. package/dist/internal/machine/atom.js.map +1 -1
  14. package/dist/internal/machine/cluster.d.ts.map +1 -1
  15. package/dist/internal/machine/cluster.js +2 -1
  16. package/dist/internal/machine/cluster.js.map +1 -1
  17. package/dist/internal/machine/command.js +2 -2
  18. package/dist/internal/machine/command.js.map +1 -1
  19. package/dist/internal/machine/commandRuntime.d.ts.map +1 -1
  20. package/dist/internal/machine/commandRuntime.js +6 -3
  21. package/dist/internal/machine/commandRuntime.js.map +1 -1
  22. package/dist/internal/machine/configuration.d.ts +8 -1
  23. package/dist/internal/machine/configuration.d.ts.map +1 -1
  24. package/dist/internal/machine/configuration.js +9 -4
  25. package/dist/internal/machine/configuration.js.map +1 -1
  26. package/dist/internal/machine/errors.d.ts +1 -1
  27. package/dist/internal/machine/errors.d.ts.map +1 -1
  28. package/dist/internal/machine/executionPlan.d.ts +3 -3
  29. package/dist/internal/machine/executionPlan.d.ts.map +1 -1
  30. package/dist/internal/machine/executionPlan.js +74 -24
  31. package/dist/internal/machine/executionPlan.js.map +1 -1
  32. package/dist/internal/machine/invocation.d.ts +10 -2
  33. package/dist/internal/machine/invocation.d.ts.map +1 -1
  34. package/dist/internal/machine/invocation.js +94 -34
  35. package/dist/internal/machine/invocation.js.map +1 -1
  36. package/dist/internal/machine/invocationEvent.d.ts +39 -0
  37. package/dist/internal/machine/invocationEvent.d.ts.map +1 -0
  38. package/dist/internal/machine/invocationEvent.js +43 -0
  39. package/dist/internal/machine/invocationEvent.js.map +1 -0
  40. package/dist/internal/machine/machine.d.ts +16 -65
  41. package/dist/internal/machine/machine.d.ts.map +1 -1
  42. package/dist/internal/machine/machine.js +18 -73
  43. package/dist/internal/machine/machine.js.map +1 -1
  44. package/dist/internal/machine/planner.d.ts +4 -2
  45. package/dist/internal/machine/planner.d.ts.map +1 -1
  46. package/dist/internal/machine/planner.js +104 -24
  47. package/dist/internal/machine/planner.js.map +1 -1
  48. package/dist/internal/machine/process.d.ts.map +1 -1
  49. package/dist/internal/machine/process.js +8 -8
  50. package/dist/internal/machine/process.js.map +1 -1
  51. package/dist/internal/machine/protocol.d.ts +8 -5
  52. package/dist/internal/machine/protocol.d.ts.map +1 -1
  53. package/dist/internal/machine/protocol.js +207 -15
  54. package/dist/internal/machine/protocol.js.map +1 -1
  55. package/dist/internal/machine/runtime.d.ts +8 -2
  56. package/dist/internal/machine/runtime.d.ts.map +1 -1
  57. package/dist/internal/machine/runtime.js +86 -8
  58. package/dist/internal/machine/runtime.js.map +1 -1
  59. package/dist/internal/machine/symbols.d.ts +2 -0
  60. package/dist/internal/machine/symbols.d.ts.map +1 -1
  61. package/dist/internal/machine/symbols.js +2 -0
  62. package/dist/internal/machine/symbols.js.map +1 -1
  63. package/dist/internal/machine/topology.d.ts +7 -0
  64. package/dist/internal/machine/topology.d.ts.map +1 -1
  65. package/dist/internal/machine/topology.js +29 -0
  66. package/dist/internal/machine/topology.js.map +1 -1
  67. package/dist/internal/testing/machine/finiteModel.js +1 -1
  68. package/dist/internal/testing/machine/finiteModel.js.map +1 -1
  69. package/dist/internal/testing/machine/runtime.d.ts.map +1 -1
  70. package/dist/internal/testing/machine/runtime.js +2 -1
  71. package/dist/internal/testing/machine/runtime.js.map +1 -1
  72. package/dist/internal/testing/machine/verification.d.ts.map +1 -1
  73. package/dist/internal/testing/machine/verification.js +5 -3
  74. package/dist/internal/testing/machine/verification.js.map +1 -1
  75. package/dist/testing/MachineTest.d.ts +7 -7
  76. package/dist/testing/MachineTest.js +7 -7
  77. package/dist/unstable/cluster/ClusterMachine.d.ts +1 -1
  78. package/dist/unstable/cluster/ClusterMachine.js +1 -1
  79. package/dist/unstable/reactivity/AtomMachine.d.ts +25 -8
  80. package/dist/unstable/reactivity/AtomMachine.d.ts.map +1 -1
  81. package/dist/unstable/reactivity/AtomMachine.js +18 -3
  82. package/dist/unstable/reactivity/AtomMachine.js.map +1 -1
  83. package/docs/agent-guide.md +242 -122
  84. package/package.json +2 -2
  85. package/src/Machine.ts +1453 -812
  86. package/src/internal/machine/activities.ts +48 -33
  87. package/src/internal/machine/atom.ts +38 -13
  88. package/src/internal/machine/cluster.ts +2 -1
  89. package/src/internal/machine/command.ts +2 -2
  90. package/src/internal/machine/commandRuntime.ts +9 -4
  91. package/src/internal/machine/configuration.ts +23 -5
  92. package/src/internal/machine/errors.ts +1 -1
  93. package/src/internal/machine/executionPlan.ts +102 -28
  94. package/src/internal/machine/invocation.ts +182 -50
  95. package/src/internal/machine/invocationEvent.ts +72 -0
  96. package/src/internal/machine/machine.ts +80 -387
  97. package/src/internal/machine/planner.ts +127 -32
  98. package/src/internal/machine/process.ts +18 -8
  99. package/src/internal/machine/protocol.ts +313 -25
  100. package/src/internal/machine/runtime.ts +135 -9
  101. package/src/internal/machine/symbols.ts +3 -0
  102. package/src/internal/machine/topology.ts +38 -0
  103. package/src/internal/testing/machine/finiteModel.ts +1 -1
  104. package/src/internal/testing/machine/runtime.ts +2 -1
  105. package/src/internal/testing/machine/verification.ts +5 -3
  106. package/src/testing/MachineTest.ts +7 -7
  107. package/src/unstable/cluster/ClusterMachine.ts +1 -1
  108. package/src/unstable/reactivity/AtomMachine.ts +45 -13
@@ -4,8 +4,7 @@
4
4
  * @since 0.4.0
5
5
  */
6
6
 
7
- /** @internal */
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
- readonly [ActivityMetadataTypeId]: StaticActivityMetadata
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 (!hasActivityMetadata(descriptor)) {
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
- definitions.push({
78
- source,
79
- id: descriptor.id,
80
- ...descriptor[ActivityMetadataTypeId]
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
- * Function-valued invoke definitions depend on an entry context and are
88
- * therefore represented as dynamic. Static descriptors are returned in state
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 (typeof invoke === "function") {
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.EventOf<InputEvents>,
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.EventOf<InputEvents>,
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.events as MachineEvents<M>)
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: (child: unknown, event: unknown) => {
51
- commands.push({ _tag: "SendTo", child: child as any, event })
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
- sendParent: (event) =>
24
+ emit: (event) =>
22
25
  decodeEmit(machine, event).pipe(
23
- Effect.flatMap((event) => scope.sendParent(event))
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
- ? scope.sendTo(command.child as never, command.event)
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.sendParent(event)),
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
- parent: getParentValue(machine, configuration, path),
1182
- parents: getParentValues(machine, configuration, path),
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
- parent: getParentValue(machine, configuration, path),
1335
- parents: getParentValues(machine, configuration, path),
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" | "emit" | "state" | "output" | "history" | "configuration"
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 type { Machine } from "../../Machine.js"
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 parents: Record<string, unknown> = {}
370
+ const ancestors: Record<string, unknown> = {}
343
371
  for (const ancestorIndex of descriptor.ancestorIndices[sourceIndex]!) {
344
- parents[descriptor.nodes[ancestorIndex]!.path] = configuration.values[ancestorIndex]
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
- parent: parentIndex < 0 ? undefined : configuration.values[parentIndex],
349
- parents,
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 state = transition(context, {
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", child: child as any, event })
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
- parent: undefined,
685
- parents: {},
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) => planConfiguration(machine as any, state as ActiveConfiguration, event as any)
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 active = normalizeConfigurationSync(machine, initial as Machine.Snapshot<any>)
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)