@typeonce/effect-machine 0.9.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.
- package/README.md +128 -28
- package/dist/Machine.d.ts +334 -238
- package/dist/Machine.d.ts.map +1 -1
- package/dist/Machine.js +45 -76
- package/dist/Machine.js.map +1 -1
- package/dist/internal/machine/atom.d.ts +6 -2
- package/dist/internal/machine/atom.d.ts.map +1 -1
- package/dist/internal/machine/atom.js +5 -0
- package/dist/internal/machine/atom.js.map +1 -1
- package/dist/internal/machine/cluster.d.ts.map +1 -1
- package/dist/internal/machine/cluster.js +2 -1
- package/dist/internal/machine/cluster.js.map +1 -1
- package/dist/internal/machine/command.js +2 -2
- package/dist/internal/machine/command.js.map +1 -1
- package/dist/internal/machine/commandRuntime.d.ts.map +1 -1
- package/dist/internal/machine/commandRuntime.js +6 -3
- package/dist/internal/machine/commandRuntime.js.map +1 -1
- package/dist/internal/machine/configuration.d.ts +8 -1
- package/dist/internal/machine/configuration.d.ts.map +1 -1
- package/dist/internal/machine/configuration.js +9 -4
- package/dist/internal/machine/configuration.js.map +1 -1
- package/dist/internal/machine/errors.d.ts +1 -1
- package/dist/internal/machine/errors.d.ts.map +1 -1
- package/dist/internal/machine/executionPlan.d.ts +3 -3
- package/dist/internal/machine/executionPlan.d.ts.map +1 -1
- package/dist/internal/machine/executionPlan.js +50 -25
- package/dist/internal/machine/executionPlan.js.map +1 -1
- package/dist/internal/machine/invocation.d.ts.map +1 -1
- package/dist/internal/machine/invocation.js +3 -2
- package/dist/internal/machine/invocation.js.map +1 -1
- package/dist/internal/machine/machine.d.ts +14 -19
- package/dist/internal/machine/machine.d.ts.map +1 -1
- package/dist/internal/machine/machine.js +12 -10
- package/dist/internal/machine/machine.js.map +1 -1
- package/dist/internal/machine/planner.d.ts +2 -1
- package/dist/internal/machine/planner.d.ts.map +1 -1
- package/dist/internal/machine/planner.js +61 -24
- package/dist/internal/machine/planner.js.map +1 -1
- package/dist/internal/machine/process.d.ts.map +1 -1
- package/dist/internal/machine/process.js +8 -8
- package/dist/internal/machine/process.js.map +1 -1
- package/dist/internal/machine/protocol.d.ts +7 -13
- package/dist/internal/machine/protocol.d.ts.map +1 -1
- package/dist/internal/machine/protocol.js +128 -50
- package/dist/internal/machine/protocol.js.map +1 -1
- package/dist/internal/machine/runtime.d.ts +8 -2
- package/dist/internal/machine/runtime.d.ts.map +1 -1
- package/dist/internal/machine/runtime.js +86 -8
- package/dist/internal/machine/runtime.js.map +1 -1
- package/dist/internal/machine/topology.d.ts +7 -0
- package/dist/internal/machine/topology.d.ts.map +1 -1
- package/dist/internal/machine/topology.js +6 -0
- package/dist/internal/machine/topology.js.map +1 -1
- package/dist/internal/testing/machine/finiteModel.js +1 -1
- package/dist/internal/testing/machine/finiteModel.js.map +1 -1
- package/dist/internal/testing/machine/runtime.d.ts.map +1 -1
- package/dist/internal/testing/machine/runtime.js +2 -1
- package/dist/internal/testing/machine/runtime.js.map +1 -1
- package/dist/internal/testing/machine/verification.d.ts.map +1 -1
- package/dist/internal/testing/machine/verification.js +5 -3
- package/dist/internal/testing/machine/verification.js.map +1 -1
- package/dist/testing/MachineTest.d.ts +7 -7
- package/dist/testing/MachineTest.js +7 -7
- package/dist/unstable/cluster/ClusterMachine.d.ts +1 -1
- package/dist/unstable/cluster/ClusterMachine.js +1 -1
- package/dist/unstable/reactivity/AtomMachine.d.ts +25 -8
- package/dist/unstable/reactivity/AtomMachine.d.ts.map +1 -1
- package/dist/unstable/reactivity/AtomMachine.js +18 -3
- package/dist/unstable/reactivity/AtomMachine.js.map +1 -1
- package/docs/agent-guide.md +143 -48
- package/package.json +1 -1
- package/src/Machine.ts +559 -307
- package/src/internal/machine/atom.ts +35 -10
- package/src/internal/machine/cluster.ts +2 -1
- package/src/internal/machine/command.ts +2 -2
- package/src/internal/machine/commandRuntime.ts +9 -4
- package/src/internal/machine/configuration.ts +23 -5
- package/src/internal/machine/errors.ts +1 -1
- package/src/internal/machine/executionPlan.ts +74 -29
- package/src/internal/machine/invocation.ts +3 -2
- package/src/internal/machine/machine.ts +68 -49
- package/src/internal/machine/planner.ts +68 -24
- package/src/internal/machine/process.ts +18 -8
- package/src/internal/machine/protocol.ts +207 -68
- package/src/internal/machine/runtime.ts +135 -9
- package/src/internal/machine/topology.ts +15 -0
- package/src/internal/testing/machine/finiteModel.ts +1 -1
- package/src/internal/testing/machine/runtime.ts +2 -1
- package/src/internal/testing/machine/verification.ts +5 -3
- package/src/testing/MachineTest.ts +7 -7
- package/src/unstable/cluster/ClusterMachine.ts +1 -1
- package/src/unstable/reactivity/AtomMachine.ts +42 -10
|
@@ -18,6 +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
22
|
import { ChildAlreadyExistsError, StoppedError } from "./errors.js"
|
|
22
23
|
|
|
23
24
|
type ChildDescriptor = {
|
|
@@ -284,12 +285,13 @@ export type RuntimeOutcome<State, Error = never, Output = never> =
|
|
|
284
285
|
readonly snapshot: Extract<RuntimeSnapshot<State, Error, Output>, { readonly status: "stopped" }>
|
|
285
286
|
}
|
|
286
287
|
|
|
287
|
-
export interface MachineRef<out State, in Event, out Error = never, out Output = never> {
|
|
288
|
+
export interface MachineRef<out State, in Event, out Error = never, out Output = never, out Emitted = never> {
|
|
288
289
|
readonly id: string
|
|
289
290
|
readonly sessionId: string
|
|
290
291
|
readonly state: Effect.Effect<State>
|
|
291
292
|
readonly snapshot: Effect.Effect<RuntimeSnapshot<State, Error, Output>>
|
|
292
293
|
readonly changes: Stream.Stream<RuntimeSnapshot<State, Error, Output>>
|
|
294
|
+
readonly emissions: Stream.Stream<Emitted>
|
|
293
295
|
readonly join: Effect.Effect<Output, Error | StoppedError>
|
|
294
296
|
readonly stop: Effect.Effect<void>
|
|
295
297
|
readonly send: (event: Event) => Effect.Effect<void, StoppedError>
|
|
@@ -307,12 +309,19 @@ interface ProcessAddress<in Event> {
|
|
|
307
309
|
readonly send: (event: Event) => Effect.Effect<void, StoppedError>
|
|
308
310
|
}
|
|
309
311
|
|
|
312
|
+
const isProcessAddress = (value: unknown): value is ActorRef<unknown> =>
|
|
313
|
+
typeof value === "object" && value !== null && "send" in value && typeof value.send === "function"
|
|
314
|
+
|
|
310
315
|
export interface ProcessScope<Event> {
|
|
311
316
|
readonly self: ProcessAddress<Event>
|
|
312
317
|
readonly parent: ProcessAddress<unknown> | undefined
|
|
313
318
|
readonly spawn: ProcessSpawn
|
|
314
319
|
readonly sendParent: (event: unknown) => Effect.Effect<void, StoppedError>
|
|
315
|
-
readonly
|
|
320
|
+
readonly emit: (event: unknown) => Effect.Effect<void>
|
|
321
|
+
readonly sendTo: {
|
|
322
|
+
<TargetEvent>(target: ActorRef<TargetEvent>, event: TargetEvent): Effect.Effect<void, StoppedError>
|
|
323
|
+
(child: ChildSelector, event: unknown): Effect.Effect<void, StoppedError>
|
|
324
|
+
}
|
|
316
325
|
readonly stopChild: (child: ChildSelector) => Effect.Effect<void>
|
|
317
326
|
/** @internal */
|
|
318
327
|
readonly failCause: (cause: Cause.Cause<unknown>) => Effect.Effect<void>
|
|
@@ -708,6 +717,10 @@ class OwnedChildRuntimeImpl implements OwnedChildRuntime {
|
|
|
708
717
|
ownerPath: options.path,
|
|
709
718
|
ownerActive: true
|
|
710
719
|
})
|
|
720
|
+
const parent: ProcessAddress<unknown> = {
|
|
721
|
+
...this.self,
|
|
722
|
+
send: (event) => options.sendParent(isCurrent, event)
|
|
723
|
+
}
|
|
711
724
|
const startOptions: StartInternalOptions = {
|
|
712
725
|
detached: true,
|
|
713
726
|
id: options.id,
|
|
@@ -722,7 +735,7 @@ class OwnedChildRuntimeImpl implements OwnedChildRuntime {
|
|
|
722
735
|
},
|
|
723
736
|
onStopSync: () => unregisterChild(this.registry, options.id, token),
|
|
724
737
|
skipStoppedOutcome: true,
|
|
725
|
-
parent
|
|
738
|
+
parent,
|
|
726
739
|
runtime: this.runtime
|
|
727
740
|
}
|
|
728
741
|
const execution = logic.execution
|
|
@@ -785,6 +798,9 @@ class OwnedChildRuntimeImpl implements OwnedChildRuntime {
|
|
|
785
798
|
|
|
786
799
|
const noChildChanges = Stream.succeed(Option.none()).pipe(Stream.concat(Stream.never))
|
|
787
800
|
const noParentSend = (_event: unknown): Effect.Effect<void, StoppedError> => Effect.void
|
|
801
|
+
const EmissionsClosed: unique symbol = Symbol("effect/Machine/EmissionsClosed")
|
|
802
|
+
|
|
803
|
+
type LazyEmissions = PubSub.PubSub<unknown> | typeof EmissionsClosed | undefined
|
|
788
804
|
|
|
789
805
|
const childlessRuntime: ChildRuntime = {
|
|
790
806
|
close: () => Effect.void,
|
|
@@ -1091,6 +1107,38 @@ const startGenericInternal: <
|
|
|
1091
1107
|
const sessionId = yield* runtime.nextSessionId
|
|
1092
1108
|
const id = requestedId ?? sessionId
|
|
1093
1109
|
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
|
+
)
|
|
1094
1142
|
const termination = yield* Deferred.make<ProcessTermination>()
|
|
1095
1143
|
const done = yield* Deferred.make<Output, Error | StoppedError>()
|
|
1096
1144
|
const awaitCompletion = Deferred.await(done).pipe(Effect.exit, Effect.asVoid)
|
|
@@ -1161,7 +1209,7 @@ const startGenericInternal: <
|
|
|
1161
1209
|
}
|
|
1162
1210
|
const cleanupStartupFailure = <A, E>(exit: Exit.Exit<A, E>): Effect.Effect<void> =>
|
|
1163
1211
|
Exit.isFailure(exit)
|
|
1164
|
-
? closeChildren(exit)
|
|
1212
|
+
? closeChildren(exit).pipe(Effect.andThen(closeEmissions()))
|
|
1165
1213
|
: Effect.void
|
|
1166
1214
|
const cleanup = onStopSync === undefined ? onStop ?? Effect.void : Effect.sync(onStopSync)
|
|
1167
1215
|
const sendParent = overrideSendParent ?? (parent === undefined ? noParentSend : parent.send)
|
|
@@ -1171,7 +1219,11 @@ const startGenericInternal: <
|
|
|
1171
1219
|
parent,
|
|
1172
1220
|
spawn,
|
|
1173
1221
|
sendParent,
|
|
1174
|
-
|
|
1222
|
+
emit,
|
|
1223
|
+
sendTo: ((target: unknown, event: unknown) =>
|
|
1224
|
+
isProcessAddress(target) ? target.send(event) : sendTo(target as ChildSelector, event)) as ProcessScope<
|
|
1225
|
+
Event
|
|
1226
|
+
>["sendTo"],
|
|
1175
1227
|
stopChild,
|
|
1176
1228
|
failCause: (cause) =>
|
|
1177
1229
|
Deferred.succeed(termination, {
|
|
@@ -1209,7 +1261,9 @@ const startGenericInternal: <
|
|
|
1209
1261
|
const runtimeSnapshot = snapshot.snapshot
|
|
1210
1262
|
return runtimeSnapshot.status !== "active"
|
|
1211
1263
|
? publish
|
|
1212
|
-
: publish.pipe(Effect.tap(() =>
|
|
1264
|
+
: publish.pipe(Effect.tap(() =>
|
|
1265
|
+
notifyActiveSnapshot(onSnapshot, runtimeSnapshot)
|
|
1266
|
+
))
|
|
1213
1267
|
}
|
|
1214
1268
|
|
|
1215
1269
|
const completeChanges = (
|
|
@@ -1348,6 +1402,7 @@ const startGenericInternal: <
|
|
|
1348
1402
|
Effect.andThen(Queue.shutdown(queue)),
|
|
1349
1403
|
Effect.andThen(closeChildren(exit)),
|
|
1350
1404
|
Effect.andThen(setAndPublishSnapshot(snapshot)),
|
|
1405
|
+
Effect.andThen(closeEmissions()),
|
|
1351
1406
|
Effect.andThen(Effect.sync(() => {
|
|
1352
1407
|
if (Exit.isFailure(exit)) {
|
|
1353
1408
|
failAcknowledgedMessage(inFlightMessage, exit.cause)
|
|
@@ -1504,6 +1559,11 @@ const startGenericInternal: <
|
|
|
1504
1559
|
state: SynchronizedRef.get(current).pipe(Effect.map((current) => current.snapshot.state)),
|
|
1505
1560
|
snapshot: SynchronizedRef.get(current).pipe(Effect.map((current) => current.snapshot)),
|
|
1506
1561
|
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>,
|
|
1507
1567
|
join: Deferred.await(done),
|
|
1508
1568
|
stop,
|
|
1509
1569
|
send: self.send,
|
|
@@ -1657,6 +1717,7 @@ class CompiledProcess implements MachineRef<any, any, any, any> {
|
|
|
1657
1717
|
private interruptRequested = false
|
|
1658
1718
|
private offerRevision = 0
|
|
1659
1719
|
private inFlightMessage: ProcessMessage<unknown> | undefined
|
|
1720
|
+
private emissionsPubSub: LazyEmissions
|
|
1660
1721
|
|
|
1661
1722
|
constructor(
|
|
1662
1723
|
private readonly logic: ProcessLogic<any, any, any, any, any, any>,
|
|
@@ -1690,7 +1751,11 @@ class CompiledProcess implements MachineRef<any, any, any, any> {
|
|
|
1690
1751
|
parent,
|
|
1691
1752
|
spawn: this.childRuntime.spawn,
|
|
1692
1753
|
sendParent,
|
|
1693
|
-
|
|
1754
|
+
emit: (event) => this.emitEvent(event),
|
|
1755
|
+
sendTo: ((target: unknown, event: unknown) =>
|
|
1756
|
+
isProcessAddress(target)
|
|
1757
|
+
? target.send(event)
|
|
1758
|
+
: this.childRuntime.sendTo(target as ChildSelector, event)) as ProcessScope<unknown>["sendTo"],
|
|
1694
1759
|
stopChild: this.childRuntime.stop,
|
|
1695
1760
|
failCause: (cause: Cause.Cause<unknown>) => this.failCause(cause)
|
|
1696
1761
|
}
|
|
@@ -1743,7 +1808,11 @@ class CompiledProcess implements MachineRef<any, any, any, any> {
|
|
|
1743
1808
|
parent,
|
|
1744
1809
|
spawn: self.childRuntime.spawn,
|
|
1745
1810
|
sendParent,
|
|
1746
|
-
|
|
1811
|
+
emit: (event) => self.emitEvent(event),
|
|
1812
|
+
sendTo: ((target: unknown, event: unknown) =>
|
|
1813
|
+
isProcessAddress(target)
|
|
1814
|
+
? target.send(event)
|
|
1815
|
+
: self.childRuntime.sendTo(target as ChildSelector, event)) as ProcessScope<unknown>["sendTo"],
|
|
1747
1816
|
stopChild: self.childRuntime.stop,
|
|
1748
1817
|
failCause: (cause: Cause.Cause<unknown>) => self.failCause(cause)
|
|
1749
1818
|
}
|
|
@@ -1838,6 +1907,10 @@ class CompiledProcess implements MachineRef<any, any, any, any> {
|
|
|
1838
1907
|
return this.changesStream()
|
|
1839
1908
|
}
|
|
1840
1909
|
|
|
1910
|
+
get emissions(): Stream.Stream<never> {
|
|
1911
|
+
return this.emissionsStream() as Stream.Stream<never>
|
|
1912
|
+
}
|
|
1913
|
+
|
|
1841
1914
|
get join(): Effect.Effect<unknown, unknown> {
|
|
1842
1915
|
return Effect.suspend(() => {
|
|
1843
1916
|
if (this.lifecycle === "Completed") {
|
|
@@ -2145,6 +2218,7 @@ class CompiledProcess implements MachineRef<any, any, any, any> {
|
|
|
2145
2218
|
}).pipe(
|
|
2146
2219
|
Effect.andThen(this.childRuntime.close(exit)),
|
|
2147
2220
|
Effect.andThen(this.setAndPublishSnapshot(snapshot)),
|
|
2221
|
+
Effect.andThen(this.closeEmissions()),
|
|
2148
2222
|
Effect.andThen(Effect.sync(() => {
|
|
2149
2223
|
if (requested._tag === "Failure") {
|
|
2150
2224
|
failAcknowledgedMessage(this.inFlightMessage, requested.cause)
|
|
@@ -2193,6 +2267,55 @@ class CompiledProcess implements MachineRef<any, any, any, any> {
|
|
|
2193
2267
|
: PubSub.publish(snapshot.changes, Exit.succeed<void>(undefined)).pipe(Effect.asVoid)
|
|
2194
2268
|
}
|
|
2195
2269
|
|
|
2270
|
+
private emitEvent(event: unknown): Effect.Effect<void> {
|
|
2271
|
+
return Effect.suspend(() =>
|
|
2272
|
+
this.emissionsPubSub === undefined || this.emissionsPubSub === EmissionsClosed
|
|
2273
|
+
? Effect.void
|
|
2274
|
+
: PubSub.publish(this.emissionsPubSub, event).pipe(Effect.asVoid)
|
|
2275
|
+
)
|
|
2276
|
+
}
|
|
2277
|
+
|
|
2278
|
+
shutdownEmissions(): Effect.Effect<void> {
|
|
2279
|
+
return this.closeEmissions()
|
|
2280
|
+
}
|
|
2281
|
+
|
|
2282
|
+
private closeEmissions(): Effect.Effect<void> {
|
|
2283
|
+
const observed = this.emissionsPubSub
|
|
2284
|
+
this.emissionsPubSub = EmissionsClosed
|
|
2285
|
+
return observed === undefined || observed === EmissionsClosed ? Effect.void : PubSub.shutdown(observed)
|
|
2286
|
+
}
|
|
2287
|
+
|
|
2288
|
+
private getOrCreateEmissions(): Effect.Effect<PubSub.PubSub<unknown> | undefined> {
|
|
2289
|
+
return Effect.suspend(() => {
|
|
2290
|
+
const observed = this.emissionsPubSub
|
|
2291
|
+
if (observed === EmissionsClosed) return Effect.succeed(undefined)
|
|
2292
|
+
if (observed !== undefined) return Effect.succeed(observed)
|
|
2293
|
+
return PubSub.unbounded<unknown>().pipe(
|
|
2294
|
+
Effect.flatMap((candidate) =>
|
|
2295
|
+
Effect.sync(() => {
|
|
2296
|
+
const latest = this.emissionsPubSub
|
|
2297
|
+
if (latest === EmissionsClosed) return [undefined, true] as const
|
|
2298
|
+
if (latest !== undefined) return [latest, true] as const
|
|
2299
|
+
this.emissionsPubSub = candidate
|
|
2300
|
+
return [candidate, false] as const
|
|
2301
|
+
}).pipe(
|
|
2302
|
+
Effect.flatMap(([selected, discard]) =>
|
|
2303
|
+
discard ? PubSub.shutdown(candidate).pipe(Effect.as(selected)) : Effect.succeed(selected)
|
|
2304
|
+
)
|
|
2305
|
+
)
|
|
2306
|
+
)
|
|
2307
|
+
)
|
|
2308
|
+
})
|
|
2309
|
+
}
|
|
2310
|
+
|
|
2311
|
+
private emissionsStream(): Stream.Stream<unknown> {
|
|
2312
|
+
return Stream.unwrap(
|
|
2313
|
+
this.getOrCreateEmissions().pipe(
|
|
2314
|
+
Effect.map((emissions) => emissions === undefined ? Stream.empty : Stream.fromPubSub(emissions))
|
|
2315
|
+
)
|
|
2316
|
+
)
|
|
2317
|
+
}
|
|
2318
|
+
|
|
2196
2319
|
private setAndPublishSnapshot(snapshot: RuntimeSnapshot<unknown, unknown, unknown>): Effect.Effect<void> {
|
|
2197
2320
|
return Effect.suspend(() => {
|
|
2198
2321
|
this.flushPendingChanges()
|
|
@@ -2407,10 +2530,13 @@ const startCompactCompiledInternal: typeof startGenericInternal = Effect.fnUntra
|
|
|
2407
2530
|
// A compiled initializer is synchronous by construction. Only startup
|
|
2408
2531
|
// callbacks that themselves return Effects need the generic initialization
|
|
2409
2532
|
// program; the compiled drain is still provided the complete service context.
|
|
2410
|
-
|
|
2533
|
+
const initialize = execution.initialSync !== undefined &&
|
|
2411
2534
|
options.onReady === undefined && options.onSnapshot === undefined
|
|
2412
2535
|
? process.initializeCompiledSync()
|
|
2413
2536
|
: process.initialize()
|
|
2537
|
+
return yield* initialize.pipe(
|
|
2538
|
+
Effect.onExit((exit) => Exit.isFailure(exit) ? process.shutdownEmissions() : Effect.void)
|
|
2539
|
+
)
|
|
2414
2540
|
}) as typeof startGenericInternal
|
|
2415
2541
|
|
|
2416
2542
|
const startLogicInternal: typeof startGenericInternal = ((
|
|
@@ -21,6 +21,8 @@ export const HistoryTargetTypeId: unique symbol = Symbol("effect/Machine/History
|
|
|
21
21
|
|
|
22
22
|
export const ChoiceTargetTypeId: unique symbol = Symbol("effect/Machine/ChoiceTarget")
|
|
23
23
|
|
|
24
|
+
export const NoTargetTypeId: unique symbol = Symbol("effect/Machine/NoTarget")
|
|
25
|
+
|
|
24
26
|
interface StateInput {
|
|
25
27
|
readonly [StateInputTypeId]: typeof StateInputTypeId
|
|
26
28
|
readonly input: unknown
|
|
@@ -42,6 +44,19 @@ export interface ChoiceTarget {
|
|
|
42
44
|
readonly values?: Readonly<Record<string, unknown>>
|
|
43
45
|
}
|
|
44
46
|
|
|
47
|
+
/** Internal marker returned by an explicitly targetless transition. */
|
|
48
|
+
export interface NoTarget {
|
|
49
|
+
readonly [NoTargetTypeId]: typeof NoTargetTypeId
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const noTarget = Object.freeze({
|
|
53
|
+
[NoTargetTypeId]: NoTargetTypeId
|
|
54
|
+
}) as NoTarget
|
|
55
|
+
|
|
56
|
+
export const makeNoTarget = (): Machine.NoTarget => noTarget
|
|
57
|
+
|
|
58
|
+
export const isNoTarget = (u: unknown): u is Machine.NoTarget => hasProperty(u, NoTargetTypeId)
|
|
59
|
+
|
|
45
60
|
export const makeHistoryTarget = (path: string, parent: string): HistoryTarget => ({
|
|
46
61
|
[HistoryTargetTypeId]: HistoryTargetTypeId,
|
|
47
62
|
path,
|
|
@@ -1487,7 +1487,7 @@ export const compileModel = (model: FiniteModel): Machine.Machine.Any => {
|
|
|
1487
1487
|
const initial = byPath.get(model.initial)!
|
|
1488
1488
|
const machine = Machine.make({
|
|
1489
1489
|
states: defined.states as any,
|
|
1490
|
-
events: eventSchemas as any,
|
|
1490
|
+
events: Machine.events(...eventSchemas) as any,
|
|
1491
1491
|
initial: () => selectSnapshot(defined.initial as any, initial.path, byPath, [initial.path], 0) as any
|
|
1492
1492
|
})
|
|
1493
1493
|
const transitions = new Map(model.transitions.map((transition) => [
|
|
@@ -15,6 +15,7 @@ import * as Stream from "effect/Stream"
|
|
|
15
15
|
import { FastCheck, TestClock } from "effect/testing"
|
|
16
16
|
import * as Machine from "../../../Machine.js"
|
|
17
17
|
import type { CausalRuntimeEvidence, Probe, ProbeStep, RuntimeInvariant } from "../../../testing/MachineTest.js"
|
|
18
|
+
import * as Protocol from "../../machine/protocol.js"
|
|
18
19
|
import { type SchemaArbitraryReport, toArbitraryWithReport } from "./arbitrary.js"
|
|
19
20
|
import { assertRuntimeInvariants, type RuntimeInvariantError } from "./runtimeInvariant.js"
|
|
20
21
|
|
|
@@ -1523,7 +1524,7 @@ export const runtimeCommands = <M extends AnyMachine>(
|
|
|
1523
1524
|
|
|
1524
1525
|
const reports: Array<SchemaArbitraryReport> = []
|
|
1525
1526
|
const eventArbitraries = options.eventArbitrary === undefined
|
|
1526
|
-
? machine.
|
|
1527
|
+
? Protocol.inputEventSchemas(machine).map((schema) => {
|
|
1527
1528
|
const derived = toArbitraryWithReport(schema)
|
|
1528
1529
|
reports.push(derived.report)
|
|
1529
1530
|
return derived.value as FastCheck.Arbitrary<Machine.Machine.InputEvent<M>>
|
|
@@ -35,6 +35,7 @@ import type {
|
|
|
35
35
|
VerificationViolation,
|
|
36
36
|
VerifyOptions
|
|
37
37
|
} from "../../../testing/MachineTest.js"
|
|
38
|
+
import * as Protocol from "../../machine/protocol.js"
|
|
38
39
|
import { toArbitraryWithReport } from "./arbitrary.js"
|
|
39
40
|
import type { FiniteModel } from "./finiteModel.js"
|
|
40
41
|
import * as ReferenceModel from "./referenceModel.js"
|
|
@@ -167,6 +168,7 @@ export const scenarios = <M extends AnyMachine>(
|
|
|
167
168
|
machine: M,
|
|
168
169
|
options: ScenarioOptions<M> = {} as ScenarioOptions<M>
|
|
169
170
|
): Scenarios<M> => {
|
|
171
|
+
const eventSchemas = Protocol.inputEventSchemas(machine)
|
|
170
172
|
const minEvents = options.minEvents ?? 0
|
|
171
173
|
const maxEvents = options.maxEvents ?? 50
|
|
172
174
|
if (options.eventsArbitrary === undefined) {
|
|
@@ -175,7 +177,7 @@ export const scenarios = <M extends AnyMachine>(
|
|
|
175
177
|
if (minEvents > maxEvents) {
|
|
176
178
|
throw new Error("MachineTest.scenarios expected minEvents to be less than or equal to maxEvents")
|
|
177
179
|
}
|
|
178
|
-
if (
|
|
180
|
+
if (eventSchemas.length === 0 && minEvents > 0) {
|
|
179
181
|
throw new Error(
|
|
180
182
|
"MachineTest.scenarios cannot generate a non-empty event sequence for a machine without public events"
|
|
181
183
|
)
|
|
@@ -184,7 +186,7 @@ export const scenarios = <M extends AnyMachine>(
|
|
|
184
186
|
|
|
185
187
|
const diagnostics: Array<SchemaArbitraryDiagnostic> = []
|
|
186
188
|
const eventArbitraries = options.eventsArbitrary === undefined
|
|
187
|
-
?
|
|
189
|
+
? eventSchemas.map((schema, index) => {
|
|
188
190
|
const derived = toArbitraryWithReport(schema)
|
|
189
191
|
diagnostics.push({
|
|
190
192
|
boundary: "event",
|
|
@@ -524,7 +526,7 @@ const publicEventTags = <M extends AnyMachine>(machine: M): {
|
|
|
524
526
|
} => {
|
|
525
527
|
const tags: Array<Machine.Machine.TagOf<Machine.Machine.InputEvents<M>[number]>> = []
|
|
526
528
|
const diagnostics: Array<{ readonly schemaIndex: number; readonly message: string }> = []
|
|
527
|
-
machine.
|
|
529
|
+
Protocol.inputEventSchemas(machine).forEach((schema, schemaIndex) => {
|
|
528
530
|
const values = finiteTagValues(SchemaAST.toType(schema.ast))
|
|
529
531
|
if (values === undefined) {
|
|
530
532
|
diagnostics.push({
|
|
@@ -230,7 +230,7 @@ export interface Scenarios<M extends AnyMachine> {
|
|
|
230
230
|
* const States = Machine.defineStates({ Idle })
|
|
231
231
|
* const machine = Machine.make({
|
|
232
232
|
* states: States.states,
|
|
233
|
-
* events:
|
|
233
|
+
* events: Machine.events(Reset),
|
|
234
234
|
* initial: () => States.initial.Idle.from()
|
|
235
235
|
* }).handle({ Idle: { on: { Reset: () => States.initial.Idle.from() } } })
|
|
236
236
|
*
|
|
@@ -479,7 +479,7 @@ export { ProbeUnavailableError } from "../internal/testing/machine/verification.
|
|
|
479
479
|
* const States = Machine.defineStates({ Idle })
|
|
480
480
|
* const machine = Machine.make({
|
|
481
481
|
* states: States.states,
|
|
482
|
-
* events:
|
|
482
|
+
* events: Machine.events(),
|
|
483
483
|
* initial: () => States.initial.Idle.from()
|
|
484
484
|
* }).handle({ Idle: {} })
|
|
485
485
|
*
|
|
@@ -1089,7 +1089,7 @@ export const Invariant: {
|
|
|
1089
1089
|
* const States = Machine.defineStates({ Count })
|
|
1090
1090
|
* const machine = Machine.make({
|
|
1091
1091
|
* states: States.states,
|
|
1092
|
-
* events:
|
|
1092
|
+
* events: Machine.events(),
|
|
1093
1093
|
* initial: () => States.initial.Count(new Count({ value: 0 }))
|
|
1094
1094
|
* }).handle({ Count: {} })
|
|
1095
1095
|
*
|
|
@@ -1407,7 +1407,7 @@ export type ExploreOptions<M extends AnyMachine, Key extends ExplorationKey = Ex
|
|
|
1407
1407
|
* const States = Machine.defineStates({ Count })
|
|
1408
1408
|
* const machine = Machine.make({
|
|
1409
1409
|
* states: States.states,
|
|
1410
|
-
* events:
|
|
1410
|
+
* events: Machine.events(Increment),
|
|
1411
1411
|
* initial: () => States.initial.Count(new Count({ value: 0 }))
|
|
1412
1412
|
* }).handle({
|
|
1413
1413
|
* Count: { on: { Increment: ({ state }) =>
|
|
@@ -1591,7 +1591,7 @@ export type RunServices<M extends AnyMachine> = IsAny<
|
|
|
1591
1591
|
* const States = Machine.defineStates({ Idle })
|
|
1592
1592
|
* const machine = Machine.make({
|
|
1593
1593
|
* states: States.states,
|
|
1594
|
-
* events:
|
|
1594
|
+
* events: Machine.events(),
|
|
1595
1595
|
* initial: () => States.initial.Idle.from()
|
|
1596
1596
|
* }).handle({ Idle: {} })
|
|
1597
1597
|
*
|
|
@@ -1818,7 +1818,7 @@ export interface Coverage<M extends AnyMachine> {
|
|
|
1818
1818
|
* const States = Machine.defineStates({ Idle })
|
|
1819
1819
|
* const machine = Machine.make({
|
|
1820
1820
|
* states: States.states,
|
|
1821
|
-
* events:
|
|
1821
|
+
* events: Machine.events(),
|
|
1822
1822
|
* initial: () => States.initial.Idle.from()
|
|
1823
1823
|
* }).handle({ Idle: {} })
|
|
1824
1824
|
*
|
|
@@ -2034,7 +2034,7 @@ export interface VerifyOptions {
|
|
|
2034
2034
|
* const States = Machine.defineStates({ Idle })
|
|
2035
2035
|
* const machine = Machine.make({
|
|
2036
2036
|
* states: States.states,
|
|
2037
|
-
* events:
|
|
2037
|
+
* events: Machine.events(),
|
|
2038
2038
|
* initial: () => States.initial.Idle.from()
|
|
2039
2039
|
* }).handle({ Idle: {} })
|
|
2040
2040
|
*
|
|
@@ -315,7 +315,7 @@ export const layerMemory: Layer.Layer<Storage> = internal.layerMemory
|
|
|
315
315
|
* const States = Machine.defineStates({ Idle })
|
|
316
316
|
* const machine = Machine.make({
|
|
317
317
|
* states: States.states,
|
|
318
|
-
* events:
|
|
318
|
+
* events: Machine.events(),
|
|
319
319
|
* initial: () => States.initial.Idle.from()
|
|
320
320
|
* }).handle({ Idle: {} })
|
|
321
321
|
*
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
import type * as Option from "effect/Option"
|
|
8
8
|
import type * as Schema from "effect/Schema"
|
|
9
9
|
import type * as Scope from "effect/Scope"
|
|
10
|
+
import type * as Stream from "effect/Stream"
|
|
10
11
|
import type { AsyncResult, Atom, AtomRegistry } from "effect/unstable/reactivity"
|
|
11
12
|
import * as internal from "../../internal/machine/atom.js"
|
|
12
13
|
import type { ChildNotActiveError, NotReadyError } from "../../internal/machine/atom.js"
|
|
@@ -89,14 +90,14 @@ type MachineStartError<InitialE, E, InitialR, R, RuntimeError = never> =
|
|
|
89
90
|
* @category models
|
|
90
91
|
* @since 0.4.0
|
|
91
92
|
*/
|
|
92
|
-
export interface MachineAtom<State, Event, Error = never, Output = never, StartError = never> {
|
|
93
|
+
export interface MachineAtom<State, Event, Error = never, Output = never, StartError = never, Emitted = never> {
|
|
93
94
|
/**
|
|
94
95
|
* Atom containing the running machine handle once startup succeeds.
|
|
95
96
|
*
|
|
96
97
|
* @since 0.4.0
|
|
97
98
|
*/
|
|
98
99
|
readonly ref: Atom.Atom<
|
|
99
|
-
AsyncResult.AsyncResult<Machine.MachineRef<State, Event, Error, Output>, StartError>
|
|
100
|
+
AsyncResult.AsyncResult<Machine.MachineRef<State, Event, Error, Output, Emitted>, StartError>
|
|
100
101
|
>
|
|
101
102
|
|
|
102
103
|
/**
|
|
@@ -163,6 +164,32 @@ type RefState<Ref> = Ref extends Machine.MachineRef<infer State, any, any, any>
|
|
|
163
164
|
type RefError<Ref> = Ref extends Machine.MachineRef<any, any, infer Error, any> ? Error : never
|
|
164
165
|
|
|
165
166
|
type RefOutput<Ref> = Ref extends Machine.MachineRef<any, any, any, infer Output> ? Output : never
|
|
167
|
+
type RefEmitted<Ref> = Ref extends Machine.MachineRef<any, any, any, any, infer Emitted> ? Emitted : never
|
|
168
|
+
|
|
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.
|
|
172
|
+
*
|
|
173
|
+
* @category getters
|
|
174
|
+
* @since 0.10.0
|
|
175
|
+
*/
|
|
176
|
+
export const emissions: <State, Event, Error, Output, StartError, Emitted>(
|
|
177
|
+
self: MachineAtom<State, Event, Error, Output, StartError, Emitted>
|
|
178
|
+
) => Stream.Stream<Emitted, StartError, AtomRegistry.AtomRegistry> = internal.emissions
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Observes emissions from each active instance selected by a child bridge.
|
|
182
|
+
*
|
|
183
|
+
* @category getters
|
|
184
|
+
* @since 0.10.0
|
|
185
|
+
*/
|
|
186
|
+
export const childEmissions: <Child extends Machine.ChildMachine.Any, StartError>(
|
|
187
|
+
self: ChildMachineAtom<Child, StartError>
|
|
188
|
+
) => Stream.Stream<
|
|
189
|
+
RefEmitted<Machine.ChildMachine.Ref<Child>>,
|
|
190
|
+
StartError,
|
|
191
|
+
AtomRegistry.AtomRegistry
|
|
192
|
+
> = internal.childEmissions
|
|
166
193
|
|
|
167
194
|
/**
|
|
168
195
|
* Reactive access to one invoked child machine selected by its descriptor.
|
|
@@ -328,7 +355,7 @@ type ChildState<Child extends Machine.ChildMachine.Any> = RefState<Machine.Child
|
|
|
328
355
|
* const States = Machine.defineStates({ Count })
|
|
329
356
|
* const machine = Machine.make({
|
|
330
357
|
* states: States.states,
|
|
331
|
-
* events:
|
|
358
|
+
* events: Machine.events(),
|
|
332
359
|
* initial: () => States.initial.Count(new Count({ value: 0 }))
|
|
333
360
|
* }).handle({ Count: {} })
|
|
334
361
|
* const machineAtom = AtomMachine.make(machine)
|
|
@@ -439,7 +466,7 @@ export const selectSnapshotChild: <
|
|
|
439
466
|
* const States = Machine.defineStates({ Idle })
|
|
440
467
|
* const machine = Machine.make({
|
|
441
468
|
* states: States.states,
|
|
442
|
-
* events:
|
|
469
|
+
* events: Machine.events(),
|
|
443
470
|
* initial: () => States.initial.Idle.from()
|
|
444
471
|
* }).handle({ Idle: {} })
|
|
445
472
|
* const machineAtom = AtomMachine.make(machine)
|
|
@@ -546,7 +573,8 @@ type MachineAtomOf<M extends Machine.Machine.Any, RuntimeError> = MachineAtom<
|
|
|
546
573
|
Machine.Machine.InitialServices<M>,
|
|
547
574
|
Machine.Machine.Services<M>,
|
|
548
575
|
RuntimeError
|
|
549
|
-
|
|
576
|
+
>,
|
|
577
|
+
Machine.Machine.EmittedEvent<M>
|
|
550
578
|
>
|
|
551
579
|
|
|
552
580
|
type ResumedMachineAtomOf<M extends Machine.Machine.Any, RuntimeError> = MachineAtom<
|
|
@@ -554,7 +582,8 @@ type ResumedMachineAtomOf<M extends Machine.Machine.Any, RuntimeError> = Machine
|
|
|
554
582
|
Machine.Machine.EventInput<Machine.Machine.InputEvent<M>>,
|
|
555
583
|
MachineRuntimeError<Machine.Machine.Error<M>, Machine.Machine.Services<M>>,
|
|
556
584
|
Machine.Machine.Output<M>,
|
|
557
|
-
Machine.MachineSchemaDecodeError | RuntimeError
|
|
585
|
+
Machine.MachineSchemaDecodeError | RuntimeError,
|
|
586
|
+
Machine.Machine.EmittedEvent<M>
|
|
558
587
|
>
|
|
559
588
|
|
|
560
589
|
/**
|
|
@@ -607,7 +636,7 @@ export interface Bound<Services, RuntimeError = never> {
|
|
|
607
636
|
* const States = Machine.defineStates({ Idle })
|
|
608
637
|
* const machine = Machine.make({
|
|
609
638
|
* states: States.states,
|
|
610
|
-
* events:
|
|
639
|
+
* events: Machine.events(),
|
|
611
640
|
* initial: () => States.initial.Idle.from()
|
|
612
641
|
* }).handle({ Idle: {} })
|
|
613
642
|
*
|
|
@@ -631,7 +660,8 @@ export const make: {
|
|
|
631
660
|
FinalStates extends Machine.Machine.StateIdentifier<States> = never,
|
|
632
661
|
Output = never,
|
|
633
662
|
OutputStates extends Machine.Machine.StateIdentifier<States> = never,
|
|
634
|
-
InputEvents extends ReadonlyArray<Machine.Machine.TaggedSchema> = Events
|
|
663
|
+
InputEvents extends ReadonlyArray<Machine.Machine.TaggedSchema> = Events,
|
|
664
|
+
ParentEvents extends ReadonlyArray<Machine.Machine.TaggedSchema> = readonly []
|
|
635
665
|
>(
|
|
636
666
|
machine:
|
|
637
667
|
& Machine.Machine<
|
|
@@ -647,7 +677,8 @@ export const make: {
|
|
|
647
677
|
Output,
|
|
648
678
|
Emits,
|
|
649
679
|
OutputStates,
|
|
650
|
-
InputEvents
|
|
680
|
+
InputEvents,
|
|
681
|
+
ParentEvents
|
|
651
682
|
>
|
|
652
683
|
& EnsureNoExternalRequirements<
|
|
653
684
|
MachineRequirements<
|
|
@@ -664,7 +695,8 @@ export const make: {
|
|
|
664
695
|
Machine.Machine.EventInputOf<InputEvents>,
|
|
665
696
|
MachineRuntimeError<E, R>,
|
|
666
697
|
Output,
|
|
667
|
-
MachineStartError<InitialE, E, InitialR, R
|
|
698
|
+
MachineStartError<InitialE, E, InitialR, R>,
|
|
699
|
+
Machine.Machine.EmittedEventOf<Emits>
|
|
668
700
|
>
|
|
669
701
|
} = internal.make
|
|
670
702
|
|