@typeonce/effect-machine 0.10.0 → 0.12.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 (58) hide show
  1. package/README.md +76 -13
  2. package/dist/Machine.d.ts +202 -90
  3. package/dist/Machine.d.ts.map +1 -1
  4. package/dist/Machine.js +38 -9
  5. package/dist/Machine.js.map +1 -1
  6. package/dist/internal/machine/atom.d.ts +1 -1
  7. package/dist/internal/machine/atom.d.ts.map +1 -1
  8. package/dist/internal/machine/atom.js +38 -7
  9. package/dist/internal/machine/atom.js.map +1 -1
  10. package/dist/internal/machine/cluster.js +2 -2
  11. package/dist/internal/machine/cluster.js.map +1 -1
  12. package/dist/internal/machine/commandRuntime.d.ts.map +1 -1
  13. package/dist/internal/machine/commandRuntime.js +2 -2
  14. package/dist/internal/machine/commandRuntime.js.map +1 -1
  15. package/dist/internal/machine/configuration.d.ts +7 -7
  16. package/dist/internal/machine/configuration.d.ts.map +1 -1
  17. package/dist/internal/machine/configuration.js +3 -3
  18. package/dist/internal/machine/configuration.js.map +1 -1
  19. package/dist/internal/machine/executionPlan.d.ts +3 -3
  20. package/dist/internal/machine/executionPlan.d.ts.map +1 -1
  21. package/dist/internal/machine/executionPlan.js +25 -25
  22. package/dist/internal/machine/executionPlan.js.map +1 -1
  23. package/dist/internal/machine/invocation.js +1 -1
  24. package/dist/internal/machine/invocation.js.map +1 -1
  25. package/dist/internal/machine/machine.d.ts +1 -0
  26. package/dist/internal/machine/machine.d.ts.map +1 -1
  27. package/dist/internal/machine/machine.js +4 -0
  28. package/dist/internal/machine/machine.js.map +1 -1
  29. package/dist/internal/machine/planner.d.ts +2 -2
  30. package/dist/internal/machine/planner.d.ts.map +1 -1
  31. package/dist/internal/machine/planner.js +20 -20
  32. package/dist/internal/machine/planner.js.map +1 -1
  33. package/dist/internal/machine/process.d.ts +3 -0
  34. package/dist/internal/machine/process.d.ts.map +1 -1
  35. package/dist/internal/machine/process.js +6 -3
  36. package/dist/internal/machine/process.js.map +1 -1
  37. package/dist/internal/machine/runtime.d.ts +17 -3
  38. package/dist/internal/machine/runtime.d.ts.map +1 -1
  39. package/dist/internal/machine/runtime.js +82 -32
  40. package/dist/internal/machine/runtime.js.map +1 -1
  41. package/dist/unstable/reactivity/AtomMachine.d.ts +4 -2
  42. package/dist/unstable/reactivity/AtomMachine.d.ts.map +1 -1
  43. package/dist/unstable/reactivity/AtomMachine.js +4 -2
  44. package/dist/unstable/reactivity/AtomMachine.js.map +1 -1
  45. package/docs/agent-guide.md +78 -19
  46. package/package.json +1 -1
  47. package/src/Machine.ts +704 -96
  48. package/src/internal/machine/atom.ts +50 -52
  49. package/src/internal/machine/cluster.ts +2 -2
  50. package/src/internal/machine/commandRuntime.ts +4 -2
  51. package/src/internal/machine/configuration.ts +10 -10
  52. package/src/internal/machine/executionPlan.ts +35 -35
  53. package/src/internal/machine/invocation.ts +1 -1
  54. package/src/internal/machine/machine.ts +6 -0
  55. package/src/internal/machine/planner.ts +30 -24
  56. package/src/internal/machine/process.ts +60 -3
  57. package/src/internal/machine/runtime.ts +176 -47
  58. package/src/unstable/reactivity/AtomMachine.ts +4 -2
@@ -459,7 +459,7 @@ const makeProcessLogic: <
459
459
  ) =>
460
460
  compiledInitial === undefined
461
461
  ? internalRuntime.provideMachineRuntime(
462
- internalPlanner.planInitial(internalPlanner.withActorScope(machine, scope), ...initialArgs).pipe(
462
+ internalPlanner.planInitial(internalPlanner.withMachineReferences(machine, scope), ...initialArgs).pipe(
463
463
  Effect.flatMap((planned) => {
464
464
  const commands = planned.commands.length === 0
465
465
  ? undefined
@@ -540,7 +540,7 @@ const makeProcessLogic: <
540
540
  try {
541
541
  planned = internalPlanner.planConfiguration(
542
542
  machine,
543
- Configuration.withActorScope(
543
+ Configuration.withMachineReferences(
544
544
  configuration ?? Configuration.normalizeConfigurationSync(machine, current),
545
545
  context
546
546
  ),
@@ -651,7 +651,7 @@ const makeProcessLogic: <
651
651
  try {
652
652
  planned = internalPlanner.planConfiguration(
653
653
  machine,
654
- Configuration.withActorScope(
654
+ Configuration.withMachineReferences(
655
655
  configuration ?? Configuration.normalizeConfigurationSync(machine, current),
656
656
  context
657
657
  ),
@@ -825,6 +825,18 @@ export const startWithRuntimeStrategyForTesting = (
825
825
  machine.id === undefined ? undefined : { id: machine.id }
826
826
  )
827
827
 
828
+ /** @internal Test-only prepared startup strategy selection for a fresh machine. */
829
+ export const prepareWithRuntimeStrategyForTesting = (
830
+ machine: Machine.Any,
831
+ strategy: internalRuntime.ProcessRuntimeStrategy,
832
+ ...args: ReadonlyArray<unknown>
833
+ ): Effect.Effect<internalRuntime.PreparedProcess<any, any, any, any, any, any, any>, any, any> =>
834
+ internalRuntime.prepareProcessWithStrategyForTesting(
835
+ (toProcessLogic as any)(machine, ...args),
836
+ strategy,
837
+ machine.id === undefined ? undefined : { id: machine.id }
838
+ )
839
+
828
840
  /** @internal Test-only runtime strategy selection for a resumed machine. */
829
841
  export const resumeWithRuntimeStrategyForTesting = (
830
842
  machine: Machine.Any,
@@ -881,6 +893,51 @@ export const start: <
881
893
  machine.id === undefined ? undefined : { id: machine.id }
882
894
  ) as any
883
895
 
896
+ export const prepare: <
897
+ const States extends Machine.StateSchemas,
898
+ const Events extends ReadonlyArray<Machine.TaggedSchema>,
899
+ const Emits extends ReadonlyArray<Machine.TaggedSchema> = readonly [],
900
+ const Input extends Schema.Top = typeof Schema.Void,
901
+ UnhandledStates extends Machine.StateIdentifier<States> = Machine.StateIdentifier<States>,
902
+ E = never,
903
+ R = never,
904
+ InitialE = never,
905
+ InitialR = never,
906
+ FinalStates extends Machine.StateIdentifier<States> = never,
907
+ Output = never
908
+ >(
909
+ machine: Machine<States, Events, Input, UnhandledStates, E, R, InitialE, InitialR, FinalStates, Output, Emits>,
910
+ ...args: [...Machine.InputArgs<Input>]
911
+ ) => Effect.Effect<
912
+ internalRuntime.PreparedProcess<
913
+ Machine.Snapshot<States>,
914
+ Machine.EventOf<Events>,
915
+ | E
916
+ | ActionError<R>
917
+ | InfiniteTransitionError
918
+ | MachineSchemaDecodeError
919
+ | StoppedError,
920
+ Output,
921
+ Machine.EmittedEventOf<Emits>,
922
+ | InitialE
923
+ | E
924
+ | ActionError<InitialR | R>
925
+ | InfiniteTransitionError
926
+ | MachineSchemaDecodeError
927
+ | StartupError
928
+ | StoppedError,
929
+ ExcludeCompatibleRuntime<
930
+ Exclude<ExecutionServices<InitialR | R>, internalRuntime.MachineRuntime>,
931
+ Machine.EventOf<Events>,
932
+ Machine.EmitOf<Emits>
933
+ >
934
+ >
935
+ > = (machine, ...args) =>
936
+ internalRuntime.prepareProcess(
937
+ toProcessLogic(machine, ...args),
938
+ machine.id === undefined ? undefined : { id: machine.id }
939
+ ) as any
940
+
884
941
  export const resume: <
885
942
  const States extends Machine.StateSchemas,
886
943
  const Events extends ReadonlyArray<Machine.TaggedSchema>,
@@ -18,7 +18,7 @@ import * as Scope from "effect/Scope"
18
18
  import * as Stream from "effect/Stream"
19
19
  import * as SynchronizedRef from "effect/SynchronizedRef"
20
20
  import type * as Take from "effect/Take"
21
- import type { ActorRef } from "../../Machine.js"
21
+ import type { MachineTarget } from "../../Machine.js"
22
22
  import { ChildAlreadyExistsError, StoppedError } from "./errors.js"
23
23
 
24
24
  type ChildDescriptor = {
@@ -302,6 +302,22 @@ export interface MachineRef<out State, in Event, out Error = never, out Output =
302
302
  readonly childChanges: (child: any) => Stream.Stream<Option.Option<any>>
303
303
  }
304
304
 
305
+ export interface PreparedProcess<
306
+ out State,
307
+ in Event,
308
+ out Error,
309
+ out Output,
310
+ out Emitted,
311
+ out StartError,
312
+ StartRequirements
313
+ > {
314
+ readonly id: string
315
+ readonly sessionId: string
316
+ readonly changes: Stream.Stream<RuntimeSnapshot<State, Error, Output>, StartError>
317
+ readonly emissions: Stream.Stream<Emitted>
318
+ readonly start: Effect.Effect<MachineRef<State, Event, Error, Output, Emitted>, StartError, StartRequirements>
319
+ }
320
+
305
321
  interface ProcessAddress<in Event> {
306
322
  readonly id: string
307
323
  readonly sessionId: string
@@ -309,7 +325,7 @@ interface ProcessAddress<in Event> {
309
325
  readonly send: (event: Event) => Effect.Effect<void, StoppedError>
310
326
  }
311
327
 
312
- const isProcessAddress = (value: unknown): value is ActorRef<unknown> =>
328
+ const isProcessAddress = (value: unknown): value is MachineTarget<unknown> =>
313
329
  typeof value === "object" && value !== null && "send" in value && typeof value.send === "function"
314
330
 
315
331
  export interface ProcessScope<Event> {
@@ -319,7 +335,7 @@ export interface ProcessScope<Event> {
319
335
  readonly sendParent: (event: unknown) => Effect.Effect<void, StoppedError>
320
336
  readonly emit: (event: unknown) => Effect.Effect<void>
321
337
  readonly sendTo: {
322
- <TargetEvent>(target: ActorRef<TargetEvent>, event: TargetEvent): Effect.Effect<void, StoppedError>
338
+ <TargetEvent>(target: MachineTarget<TargetEvent>, event: TargetEvent): Effect.Effect<void, StoppedError>
323
339
  (child: ChildSelector, event: unknown): Effect.Effect<void, StoppedError>
324
340
  }
325
341
  readonly stopChild: (child: ChildSelector) => Effect.Effect<void>
@@ -351,7 +367,7 @@ export interface ProcessContext<State, Event> extends ProcessScope<Event> {
351
367
  *
352
368
  * Unlike `ProcessContext`, synchronous mailbox and state operations do not
353
369
  * introduce an Effect boundary. The compiled drain still returns an Effect so
354
- * actor commands, invokes, observation callbacks, interruption, and the Effect
370
+ * machine commands, invokes, observation callbacks, interruption, and the Effect
355
371
  * scheduler remain explicit at their actual boundaries.
356
372
  *
357
373
  * @internal
@@ -609,6 +625,8 @@ const makeProcessRuntime: Effect.Effect<ProcessRuntime> = Effect.sync(() => {
609
625
  interface StartInternalOptions {
610
626
  readonly detached?: boolean
611
627
  readonly id?: string
628
+ readonly sessionId?: string
629
+ readonly emissions?: EmissionRuntime
612
630
  readonly onOutcome?: (outcome: RuntimeOutcome<any, any, any>) => Effect.Effect<void>
613
631
  readonly onSnapshot?: (
614
632
  snapshot: Extract<RuntimeSnapshot<any, any, any>, { readonly status: "active" }>
@@ -802,6 +820,54 @@ const EmissionsClosed: unique symbol = Symbol("effect/Machine/EmissionsClosed")
802
820
 
803
821
  type LazyEmissions = PubSub.PubSub<unknown> | typeof EmissionsClosed | undefined
804
822
 
823
+ interface EmissionRuntime {
824
+ readonly emit: (event: unknown) => Effect.Effect<void>
825
+ readonly close: () => Effect.Effect<void>
826
+ readonly stream: Stream.Stream<unknown>
827
+ }
828
+
829
+ const makeEmissionRuntime = (): EmissionRuntime => {
830
+ let emissions: LazyEmissions
831
+ const getOrCreate: Effect.Effect<PubSub.PubSub<unknown> | undefined> = Effect.suspend(() => {
832
+ const observed = emissions
833
+ if (observed === EmissionsClosed) return Effect.succeed(undefined)
834
+ if (observed !== undefined) return Effect.succeed(observed)
835
+ return PubSub.unbounded<unknown>().pipe(
836
+ Effect.flatMap((candidate) =>
837
+ Effect.sync(() => {
838
+ const latest = emissions
839
+ if (latest === EmissionsClosed) return [undefined, true] as const
840
+ if (latest !== undefined) return [latest, true] as const
841
+ emissions = candidate
842
+ return [candidate, false] as const
843
+ }).pipe(
844
+ Effect.flatMap(([selected, discard]) =>
845
+ discard ? PubSub.shutdown(candidate).pipe(Effect.as(selected)) : Effect.succeed(selected)
846
+ )
847
+ )
848
+ )
849
+ )
850
+ })
851
+ return {
852
+ emit: (event) =>
853
+ Effect.suspend(() =>
854
+ emissions === undefined || emissions === EmissionsClosed
855
+ ? Effect.void
856
+ : PubSub.publish(emissions, event).pipe(Effect.asVoid)
857
+ ),
858
+ close: () => {
859
+ const observed = emissions
860
+ emissions = EmissionsClosed
861
+ return observed === undefined || observed === EmissionsClosed ? Effect.void : PubSub.shutdown(observed)
862
+ },
863
+ stream: Stream.unwrap(
864
+ getOrCreate.pipe(
865
+ Effect.map((emissions) => emissions === undefined ? Stream.empty : Stream.fromPubSub(emissions))
866
+ )
867
+ )
868
+ }
869
+ }
870
+
805
871
  const childlessRuntime: ChildRuntime = {
806
872
  close: () => Effect.void,
807
873
  spawn: (() => Effect.die(new Error("Childless machine logic cannot spawn a process"))) as ProcessSpawn,
@@ -1104,41 +1170,10 @@ const startGenericInternal: <
1104
1170
  | { readonly _tag: "Done"; readonly output: Output }
1105
1171
  | { readonly _tag: "Failure"; readonly cause: Cause.Cause<Error> }
1106
1172
 
1107
- const sessionId = yield* runtime.nextSessionId
1173
+ const sessionId = options.sessionId ?? (yield* runtime.nextSessionId)
1108
1174
  const id = requestedId ?? sessionId
1109
1175
  const queue = yield* Queue.unbounded<ProcessMessage<Event>>()
1110
- let emissions: LazyEmissions
1111
- const getOrCreateEmissions: Effect.Effect<PubSub.PubSub<unknown> | undefined> = Effect.suspend(() => {
1112
- const observed = emissions
1113
- if (observed === EmissionsClosed) return Effect.succeed(undefined)
1114
- if (observed !== undefined) return Effect.succeed(observed)
1115
- return PubSub.unbounded<unknown>().pipe(
1116
- Effect.flatMap((candidate) =>
1117
- Effect.sync(() => {
1118
- const latest = emissions
1119
- if (latest === EmissionsClosed) return [undefined, true] as const
1120
- if (latest !== undefined) return [latest, true] as const
1121
- emissions = candidate
1122
- return [candidate, false] as const
1123
- }).pipe(
1124
- Effect.flatMap(([selected, discard]) =>
1125
- discard ? PubSub.shutdown(candidate).pipe(Effect.as(selected)) : Effect.succeed(selected)
1126
- )
1127
- )
1128
- )
1129
- )
1130
- })
1131
- const closeEmissions = (): Effect.Effect<void> => {
1132
- const observed = emissions
1133
- emissions = EmissionsClosed
1134
- return observed === undefined || observed === EmissionsClosed ? Effect.void : PubSub.shutdown(observed)
1135
- }
1136
- const emit = (event: unknown): Effect.Effect<void> =>
1137
- Effect.suspend(() =>
1138
- emissions === undefined || emissions === EmissionsClosed
1139
- ? Effect.void
1140
- : PubSub.publish(emissions, event).pipe(Effect.asVoid)
1141
- )
1176
+ const emissions = options.emissions ?? makeEmissionRuntime()
1142
1177
  const termination = yield* Deferred.make<ProcessTermination>()
1143
1178
  const done = yield* Deferred.make<Output, Error | StoppedError>()
1144
1179
  const awaitCompletion = Deferred.await(done).pipe(Effect.exit, Effect.asVoid)
@@ -1209,7 +1244,7 @@ const startGenericInternal: <
1209
1244
  }
1210
1245
  const cleanupStartupFailure = <A, E>(exit: Exit.Exit<A, E>): Effect.Effect<void> =>
1211
1246
  Exit.isFailure(exit)
1212
- ? closeChildren(exit).pipe(Effect.andThen(closeEmissions()))
1247
+ ? closeChildren(exit).pipe(Effect.andThen(emissions.close()))
1213
1248
  : Effect.void
1214
1249
  const cleanup = onStopSync === undefined ? onStop ?? Effect.void : Effect.sync(onStopSync)
1215
1250
  const sendParent = overrideSendParent ?? (parent === undefined ? noParentSend : parent.send)
@@ -1219,7 +1254,7 @@ const startGenericInternal: <
1219
1254
  parent,
1220
1255
  spawn,
1221
1256
  sendParent,
1222
- emit,
1257
+ emit: emissions.emit,
1223
1258
  sendTo: ((target: unknown, event: unknown) =>
1224
1259
  isProcessAddress(target) ? target.send(event) : sendTo(target as ChildSelector, event)) as ProcessScope<
1225
1260
  Event
@@ -1402,7 +1437,7 @@ const startGenericInternal: <
1402
1437
  Effect.andThen(Queue.shutdown(queue)),
1403
1438
  Effect.andThen(closeChildren(exit)),
1404
1439
  Effect.andThen(setAndPublishSnapshot(snapshot)),
1405
- Effect.andThen(closeEmissions()),
1440
+ Effect.andThen(emissions.close()),
1406
1441
  Effect.andThen(Effect.sync(() => {
1407
1442
  if (Exit.isFailure(exit)) {
1408
1443
  failAcknowledgedMessage(inFlightMessage, exit.cause)
@@ -1559,11 +1594,7 @@ const startGenericInternal: <
1559
1594
  state: SynchronizedRef.get(current).pipe(Effect.map((current) => current.snapshot.state)),
1560
1595
  snapshot: SynchronizedRef.get(current).pipe(Effect.map((current) => current.snapshot)),
1561
1596
  changes: changesStream,
1562
- emissions: Stream.unwrap(
1563
- getOrCreateEmissions.pipe(
1564
- Effect.map((emissions) => emissions === undefined ? Stream.empty : Stream.fromPubSub(emissions))
1565
- )
1566
- ) as Stream.Stream<never>,
1597
+ emissions: emissions.stream as Stream.Stream<never>,
1567
1598
  join: Deferred.await(done),
1568
1599
  stop,
1569
1600
  send: self.send,
@@ -1717,6 +1748,7 @@ class CompiledProcess implements MachineRef<any, any, any, any> {
1717
1748
  private interruptRequested = false
1718
1749
  private offerRevision = 0
1719
1750
  private inFlightMessage: ProcessMessage<unknown> | undefined
1751
+ private readonly externalEmissions: EmissionRuntime | undefined
1720
1752
  private emissionsPubSub: LazyEmissions
1721
1753
 
1722
1754
  constructor(
@@ -1727,6 +1759,7 @@ class CompiledProcess implements MachineRef<any, any, any, any> {
1727
1759
  ) {
1728
1760
  this.sessionId = sessionId
1729
1761
  this.id = options.id ?? sessionId
1762
+ this.externalEmissions = options.emissions
1730
1763
  this.send = (event) => this.offerMessage(event)
1731
1764
  this.address = {
1732
1765
  id: this.id,
@@ -1908,7 +1941,7 @@ class CompiledProcess implements MachineRef<any, any, any, any> {
1908
1941
  }
1909
1942
 
1910
1943
  get emissions(): Stream.Stream<never> {
1911
- return this.emissionsStream() as Stream.Stream<never>
1944
+ return (this.externalEmissions?.stream ?? this.emissionsStream()) as Stream.Stream<never>
1912
1945
  }
1913
1946
 
1914
1947
  get join(): Effect.Effect<unknown, unknown> {
@@ -2268,6 +2301,7 @@ class CompiledProcess implements MachineRef<any, any, any, any> {
2268
2301
  }
2269
2302
 
2270
2303
  private emitEvent(event: unknown): Effect.Effect<void> {
2304
+ if (this.externalEmissions !== undefined) return this.externalEmissions.emit(event)
2271
2305
  return Effect.suspend(() =>
2272
2306
  this.emissionsPubSub === undefined || this.emissionsPubSub === EmissionsClosed
2273
2307
  ? Effect.void
@@ -2280,6 +2314,7 @@ class CompiledProcess implements MachineRef<any, any, any, any> {
2280
2314
  }
2281
2315
 
2282
2316
  private closeEmissions(): Effect.Effect<void> {
2317
+ if (this.externalEmissions !== undefined) return this.externalEmissions.close()
2283
2318
  const observed = this.emissionsPubSub
2284
2319
  this.emissionsPubSub = EmissionsClosed
2285
2320
  return observed === undefined || observed === EmissionsClosed ? Effect.void : PubSub.shutdown(observed)
@@ -2523,7 +2558,7 @@ const startCompactCompiledInternal: typeof startGenericInternal = Effect.fnUntra
2523
2558
  logic: ProcessLogic<any, any, any, any, any, any>,
2524
2559
  options: StartInternalOptions
2525
2560
  ) {
2526
- const sessionId = yield* options.runtime.nextSessionId
2561
+ const sessionId = options.sessionId ?? (yield* options.runtime.nextSessionId)
2527
2562
  const services = yield* Effect.context<any>()
2528
2563
  const execution = logic.execution as CompiledProcessExecution<any, any, any, any, any, any>
2529
2564
  const process = new CompiledProcess(logic, options, services, sessionId)
@@ -2636,3 +2671,97 @@ export const startProcess: <
2636
2671
  }
2637
2672
  )
2638
2673
  })
2674
+
2675
+ const prepareProcessWithStrategy = Effect.fnUntraced(function*<
2676
+ State,
2677
+ Event,
2678
+ Error,
2679
+ Requirements,
2680
+ Output,
2681
+ InitialError,
2682
+ Emitted
2683
+ >(
2684
+ logic: ProcessLogic<State, Event, Error, Requirements, Output, InitialError>,
2685
+ strategy: ProcessRuntimeStrategy,
2686
+ options?: {
2687
+ readonly id?: string
2688
+ }
2689
+ ) {
2690
+ const runtime = yield* makeProcessRuntime
2691
+ const sessionId = yield* runtime.nextSessionId
2692
+ const emissions = makeEmissionRuntime()
2693
+ const started = yield* Deferred.make<MachineRef<State, Event, Error, Output, Emitted>, InitialError>()
2694
+ const internalOptions: StartInternalOptions = options === undefined
2695
+ ? {
2696
+ detached: true,
2697
+ emissions,
2698
+ runtime,
2699
+ sessionId
2700
+ }
2701
+ : {
2702
+ ...options,
2703
+ detached: true,
2704
+ emissions,
2705
+ runtime,
2706
+ sessionId
2707
+ }
2708
+ const initialize = strategy === "generic"
2709
+ ? startGenericInternal(logic, internalOptions)
2710
+ : strategy === "compiled"
2711
+ ? logic.execution?._tag === "Compiled"
2712
+ ? startCompactCompiledInternal(logic, internalOptions)
2713
+ : Effect.die(new Error("Machine cannot force the compiled runtime for generic process logic"))
2714
+ : startLogicInternal(logic, internalOptions)
2715
+ const start = yield* Effect.cached(
2716
+ initialize.pipe(
2717
+ Effect.onExit((exit) => Deferred.done(started, exit))
2718
+ ) as Effect.Effect<MachineRef<State, Event, Error, Output, Emitted>, InitialError, Requirements>
2719
+ )
2720
+ return {
2721
+ id: options?.id ?? sessionId,
2722
+ sessionId,
2723
+ changes: Stream.unwrap(
2724
+ Deferred.await(started).pipe(Effect.map((ref) => ref.changes))
2725
+ ),
2726
+ emissions: emissions.stream as Stream.Stream<Emitted>,
2727
+ start
2728
+ }
2729
+ })
2730
+
2731
+ export const prepareProcess: <
2732
+ State,
2733
+ Event,
2734
+ Error = never,
2735
+ Requirements = never,
2736
+ Output = never,
2737
+ InitialError = never,
2738
+ Emitted = never
2739
+ >(
2740
+ logic: ProcessLogic<State, Event, Error, Requirements, Output, InitialError>,
2741
+ options?: {
2742
+ readonly id?: string
2743
+ }
2744
+ ) => Effect.Effect<
2745
+ PreparedProcess<State, Event, Error, Output, Emitted, InitialError, Requirements>
2746
+ > =
2747
+ ((logic: ProcessLogic<any, any, any, any, any, any>, options?: { readonly id?: string }) =>
2748
+ prepareProcessWithStrategy(logic, "auto", options)) as any
2749
+
2750
+ /** @internal Test-only prepared startup strategy selection. */
2751
+ export const prepareProcessWithStrategyForTesting = <
2752
+ State,
2753
+ Event,
2754
+ Error = never,
2755
+ Requirements = never,
2756
+ Output = never,
2757
+ InitialError = never,
2758
+ Emitted = never
2759
+ >(
2760
+ logic: ProcessLogic<State, Event, Error, Requirements, Output, InitialError>,
2761
+ strategy: ProcessRuntimeStrategy,
2762
+ options?: {
2763
+ readonly id?: string
2764
+ }
2765
+ ): Effect.Effect<
2766
+ PreparedProcess<State, Event, Error, Output, Emitted, InitialError, Requirements>
2767
+ > => prepareProcessWithStrategy(logic, strategy, options) as any
@@ -167,8 +167,10 @@ type RefOutput<Ref> = Ref extends Machine.MachineRef<any, any, any, infer Output
167
167
  type RefEmitted<Ref> = Ref extends Machine.MachineRef<any, any, any, any, infer Emitted> ? Emitted : never
168
168
 
169
169
  /**
170
- * Observes ephemeral notifications from the actor owned by a machine atom.
171
- * The stream requires an `AtomRegistry` and does not replay earlier emissions.
170
+ * Observes ephemeral notifications from the running machine owned by a machine
171
+ * atom. When this stream activates a fresh bridge, it subscribes before machine
172
+ * initialization and observes initial-entry emissions. It never replays
173
+ * emissions from a machine that was already running.
172
174
  *
173
175
  * @category getters
174
176
  * @since 0.10.0