@typeonce/effect-machine 0.10.0 → 0.11.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 +43 -4
- package/dist/Machine.d.ts +159 -47
- package/dist/Machine.d.ts.map +1 -1
- package/dist/Machine.js +29 -0
- package/dist/Machine.js.map +1 -1
- package/dist/internal/machine/atom.d.ts +1 -1
- package/dist/internal/machine/atom.d.ts.map +1 -1
- package/dist/internal/machine/atom.js +38 -7
- package/dist/internal/machine/atom.js.map +1 -1
- package/dist/internal/machine/machine.d.ts +1 -0
- package/dist/internal/machine/machine.d.ts.map +1 -1
- package/dist/internal/machine/machine.js +4 -0
- package/dist/internal/machine/machine.js.map +1 -1
- package/dist/internal/machine/process.d.ts +3 -0
- package/dist/internal/machine/process.d.ts.map +1 -1
- package/dist/internal/machine/process.js +3 -0
- package/dist/internal/machine/process.js.map +1 -1
- package/dist/internal/machine/runtime.d.ts +14 -0
- package/dist/internal/machine/runtime.d.ts.map +1 -1
- package/dist/internal/machine/runtime.js +82 -32
- package/dist/internal/machine/runtime.js.map +1 -1
- package/dist/unstable/reactivity/AtomMachine.d.ts +4 -2
- package/dist/unstable/reactivity/AtomMachine.d.ts.map +1 -1
- package/dist/unstable/reactivity/AtomMachine.js +4 -2
- package/dist/unstable/reactivity/AtomMachine.js.map +1 -1
- package/docs/agent-guide.md +50 -8
- package/package.json +1 -1
- package/src/Machine.ts +661 -53
- package/src/internal/machine/atom.ts +50 -52
- package/src/internal/machine/machine.ts +6 -0
- package/src/internal/machine/process.ts +57 -0
- package/src/internal/machine/runtime.ts +172 -43
- package/src/unstable/reactivity/AtomMachine.ts +4 -2
|
@@ -7,11 +7,12 @@
|
|
|
7
7
|
import * as Data from "effect/Data"
|
|
8
8
|
import * as Effect from "effect/Effect"
|
|
9
9
|
import * as Equal from "effect/Equal"
|
|
10
|
+
import * as Fiber from "effect/Fiber"
|
|
10
11
|
import * as Option from "effect/Option"
|
|
11
12
|
import type * as Schema from "effect/Schema"
|
|
12
13
|
import type * as Scope from "effect/Scope"
|
|
13
14
|
import * as Stream from "effect/Stream"
|
|
14
|
-
import { AsyncResult, Atom,
|
|
15
|
+
import { AsyncResult, Atom, AtomRegistry } from "effect/unstable/reactivity"
|
|
15
16
|
import type * as Machine from "../../Machine.js"
|
|
16
17
|
import type { Bound, ChildMachineAtom, MachineAtom } from "../../unstable/reactivity/AtomMachine.js"
|
|
17
18
|
import * as internalMachine from "./machine.js"
|
|
@@ -72,6 +73,11 @@ type MachineStartError<InitialE, E, InitialR, R, RuntimeError = never> =
|
|
|
72
73
|
| Machine.StoppedError
|
|
73
74
|
| RuntimeError
|
|
74
75
|
|
|
76
|
+
const preparedByMachineAtom = new WeakMap<
|
|
77
|
+
object,
|
|
78
|
+
Atom.Atom<AsyncResult.AsyncResult<Machine.Prepared<any, any, any, any, any, any, any>, any>>
|
|
79
|
+
>()
|
|
80
|
+
|
|
75
81
|
const runMachineAtomEffect = <State, Event, Error, Output, Emitted, StartError, Requirements>(
|
|
76
82
|
get: Atom.AtomContext,
|
|
77
83
|
start: Effect.Effect<Machine.MachineRef<State, Event, Error, Output, Emitted>, StartError, Requirements>
|
|
@@ -83,53 +89,16 @@ const runMachineAtomEffect = <State, Event, Error, Output, Emitted, StartError,
|
|
|
83
89
|
)
|
|
84
90
|
)
|
|
85
91
|
|
|
86
|
-
const
|
|
87
|
-
const States extends Machine.Machine.StateSchemas,
|
|
88
|
-
const Events extends ReadonlyArray<Machine.Machine.TaggedSchema>,
|
|
89
|
-
const Emits extends ReadonlyArray<Machine.Machine.TaggedSchema> = any,
|
|
90
|
-
const Input extends Schema.Top = typeof Schema.Void,
|
|
91
|
-
UnhandledStates extends Machine.Machine.StateIdentifier<States> = Machine.Machine.StateIdentifier<States>,
|
|
92
|
-
E = never,
|
|
93
|
-
R = never,
|
|
94
|
-
InitialE = never,
|
|
95
|
-
InitialR = never,
|
|
96
|
-
FinalStates extends Machine.Machine.StateIdentifier<States> = never,
|
|
97
|
-
Output = never,
|
|
98
|
-
OutputStates extends Machine.Machine.StateIdentifier<States> = never,
|
|
99
|
-
InputEvents extends ReadonlyArray<Machine.Machine.TaggedSchema> = Events,
|
|
100
|
-
ParentEvents extends ReadonlyArray<Machine.Machine.TaggedSchema> = readonly []
|
|
101
|
-
>(
|
|
92
|
+
const startPreparedMachineAtomEffect = <State, Event, Error, Output, Emitted, StartError, Requirements>(
|
|
102
93
|
get: Atom.AtomContext,
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
Input,
|
|
108
|
-
UnhandledStates,
|
|
109
|
-
E,
|
|
110
|
-
R,
|
|
111
|
-
InitialE,
|
|
112
|
-
InitialR,
|
|
113
|
-
FinalStates,
|
|
114
|
-
Output,
|
|
115
|
-
Emits,
|
|
116
|
-
OutputStates,
|
|
117
|
-
InputEvents,
|
|
118
|
-
ParentEvents
|
|
94
|
+
prepared: Atom.Atom<
|
|
95
|
+
AsyncResult.AsyncResult<
|
|
96
|
+
Machine.Prepared<State, Event, Error, Output, Emitted, StartError, Requirements>,
|
|
97
|
+
never
|
|
119
98
|
>
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
)
|
|
123
|
-
Machine.MachineRef<
|
|
124
|
-
Machine.Machine.Snapshot<States>,
|
|
125
|
-
Machine.Machine.EventInputOf<InputEvents>,
|
|
126
|
-
MachineRuntimeError<E, R>,
|
|
127
|
-
Output,
|
|
128
|
-
Machine.Machine.EmittedEventOf<Emits>
|
|
129
|
-
>,
|
|
130
|
-
MachineStartError<InitialE, E, InitialR, R>,
|
|
131
|
-
MachineRequirements<InitialR, R, Machine.Machine.EventOf<Events>, Machine.Machine.EmitOf<Emits>>
|
|
132
|
-
> => runMachineAtomEffect(get, internalMachine.start(machine as any, ...args) as any)
|
|
99
|
+
>
|
|
100
|
+
): Effect.Effect<never, StartError, Requirements> =>
|
|
101
|
+
runMachineAtomEffect(get, get.result(prepared).pipe(Effect.flatMap((prepared) => prepared.start)))
|
|
133
102
|
|
|
134
103
|
const resumeMachineAtomEffect = (
|
|
135
104
|
get: Atom.AtomContext,
|
|
@@ -144,8 +113,31 @@ type RefEmitted<Ref> = Ref extends Machine.MachineRef<any, any, any, any, infer
|
|
|
144
113
|
|
|
145
114
|
export const emissions = <State, Event, Error, Output, StartError, Emitted>(
|
|
146
115
|
self: MachineAtom<State, Event, Error, Output, StartError, Emitted>
|
|
147
|
-
): Stream.Stream<Emitted, StartError, AtomRegistry.AtomRegistry> =>
|
|
148
|
-
|
|
116
|
+
): Stream.Stream<Emitted, StartError, AtomRegistry.AtomRegistry> => {
|
|
117
|
+
const prepared = preparedByMachineAtom.get(self as object)
|
|
118
|
+
if (prepared === undefined) {
|
|
119
|
+
return Atom.toStreamResult(self.ref).pipe(Stream.flatMap((ref) => ref.emissions))
|
|
120
|
+
}
|
|
121
|
+
return Stream.unwrap(
|
|
122
|
+
Effect.gen(function*() {
|
|
123
|
+
const registry = yield* AtomRegistry.AtomRegistry
|
|
124
|
+
const releasePrepared = yield* Effect.sync(() => registry.mount(prepared))
|
|
125
|
+
yield* Effect.addFinalizer(() => Effect.sync(releasePrepared))
|
|
126
|
+
const machine = yield* Atom.getResult(prepared)
|
|
127
|
+
const pull = yield* Stream.toPull(machine.emissions as Stream.Stream<Emitted>)
|
|
128
|
+
const firstPull = yield* pull.pipe(Effect.forkScoped({ startImmediately: true }))
|
|
129
|
+
const releaseRef = yield* Effect.sync(() => registry.mount(self.ref))
|
|
130
|
+
yield* Effect.addFinalizer(() => Effect.sync(releaseRef))
|
|
131
|
+
yield* Atom.getResult(self.ref)
|
|
132
|
+
let first = true
|
|
133
|
+
return Stream.fromPull(Effect.succeed(Effect.suspend(() => {
|
|
134
|
+
if (!first) return pull
|
|
135
|
+
first = false
|
|
136
|
+
return Fiber.join(firstPull)
|
|
137
|
+
})))
|
|
138
|
+
})
|
|
139
|
+
)
|
|
140
|
+
}
|
|
149
141
|
|
|
150
142
|
export const childEmissions = <Child extends Machine.ChildMachine.Any, StartError>(
|
|
151
143
|
self: ChildMachineAtom<Child, StartError>
|
|
@@ -666,8 +658,11 @@ export const make: {
|
|
|
666
658
|
Machine.Machine.EmittedEventOf<Emits>
|
|
667
659
|
>
|
|
668
660
|
} = ((machine: Machine.Machine.Any, ...args: ReadonlyArray<unknown>) => {
|
|
669
|
-
const
|
|
670
|
-
|
|
661
|
+
const prepared = Atom.make(() => internalMachine.prepare(machine as any, ...(args as [])))
|
|
662
|
+
const ref = Atom.make((get) => startPreparedMachineAtomEffect(get, prepared as any))
|
|
663
|
+
const result = makeFromRefAtom(ref as any)
|
|
664
|
+
preparedByMachineAtom.set(result, prepared as any)
|
|
665
|
+
return result
|
|
671
666
|
}) as any
|
|
672
667
|
|
|
673
668
|
export const resume: {
|
|
@@ -688,8 +683,11 @@ const makeWithRuntime = (
|
|
|
688
683
|
machine: Machine.Machine.Any,
|
|
689
684
|
args: ReadonlyArray<unknown>
|
|
690
685
|
): MachineAtom<any, any, any, any, any> => {
|
|
691
|
-
const
|
|
692
|
-
|
|
686
|
+
const prepared = runtime.atom(() => internalMachine.prepare(machine as any, ...(args as [])))
|
|
687
|
+
const ref = runtime.atom((get) => startPreparedMachineAtomEffect(get, prepared as any))
|
|
688
|
+
const result = makeFromRefAtom(ref as any)
|
|
689
|
+
preparedByMachineAtom.set(result, prepared as any)
|
|
690
|
+
return result
|
|
693
691
|
}
|
|
694
692
|
|
|
695
693
|
const resumeWithRuntime = (
|
|
@@ -104,6 +104,8 @@ const Proto = {
|
|
|
104
104
|
}
|
|
105
105
|
}
|
|
106
106
|
|
|
107
|
+
const makeBoundInvoke = (config: unknown): unknown => config
|
|
108
|
+
|
|
107
109
|
const cloneWithHandlers = (
|
|
108
110
|
self: Machine.Any,
|
|
109
111
|
handlers: Machine.StateConfigs<any, any, any, any, any, any, any>
|
|
@@ -121,6 +123,7 @@ const cloneWithHandlers = (
|
|
|
121
123
|
machine.makeTargetBuilder = self.makeTargetBuilder
|
|
122
124
|
machine.handlers = handlers
|
|
123
125
|
machine.handle = makeHandle(machine)
|
|
126
|
+
machine.invoke = makeBoundInvoke
|
|
124
127
|
Protocol.copyProtocol(self, machine)
|
|
125
128
|
return machine
|
|
126
129
|
}
|
|
@@ -879,6 +882,7 @@ export const make: Make = (<
|
|
|
879
882
|
self.makeTargetBuilder = makeTargetBuilder(config.states, self.stateNodes)
|
|
880
883
|
self.handlers = Object.create(null)
|
|
881
884
|
self.handle = makeHandle(self)
|
|
885
|
+
self.invoke = makeBoundInvoke
|
|
882
886
|
Protocol.setProtocol(self)
|
|
883
887
|
return self
|
|
884
888
|
}) as Make
|
|
@@ -1358,6 +1362,8 @@ export const watch = <State, Event, Error = never, Output = never>(
|
|
|
1358
1362
|
ref: MachineRef<State, Event, Error, Output>
|
|
1359
1363
|
): Stream.Stream<RuntimeOutcome<State, Error, Output>> => internalRuntime.watch(ref)
|
|
1360
1364
|
|
|
1365
|
+
export const prepare = internalProcess.prepare
|
|
1366
|
+
|
|
1361
1367
|
export const start: <
|
|
1362
1368
|
const States extends Machine.StateSchemas,
|
|
1363
1369
|
const Events extends ReadonlyArray<Machine.TaggedSchema>,
|
|
@@ -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>,
|
|
@@ -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
|
|
@@ -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
|
-
|
|
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(
|
|
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(
|
|
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.
|
|
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
|
|
171
|
-
*
|
|
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
|