@graftland/plugin-remote 0.3.1 → 0.3.2
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/dist/Auth.d.ts +2 -2
- package/dist/Auth.d.ts.map +1 -1
- package/dist/Auth.js.map +1 -1
- package/dist/{Bootstrap-Dr6sX7el.js → Bootstrap-H9gDsgsC.js} +2 -2
- package/dist/{Contract-DL2mHvaT.js → Contract-DsJtisvL.js} +2 -2
- package/dist/Contract.js +1 -1
- package/dist/{Errors-BMKmu5Y7.js → Errors-DYhsGZcZ.js} +3 -1
- package/dist/Errors.d.ts +7 -0
- package/dist/Errors.d.ts.map +1 -1
- package/dist/Errors.js +3 -1
- package/dist/Errors.js.map +1 -1
- package/dist/{FailureGroup-BcH0XOBi.js → FailureGroup-BgT73AaR.js} +14 -4
- package/dist/{Framing-zaYdOJnT.js → Framing-G9eSaeLb.js} +1 -1
- package/dist/{Graft-CVyueNOf.js → Graft-B71CPA2Y.js} +20 -16
- package/dist/{Packages-JNL34JpS.js → Packages-AgoCGotL.js} +23 -26
- package/dist/{Peer-BT7eAGmh.js → Peer-DXeVrGBh.js} +5 -5
- package/dist/{ProcessObservation-DEGrEMfP.js → ProcessObservation-gLgGirYv.js} +1 -1
- package/dist/{ServeLifecycle-CJM0gG5B.js → ServeLifecycle-Bd1FPJ6e.js} +1 -1
- package/dist/Transport.d.ts +2 -2
- package/dist/Transport.d.ts.map +1 -1
- package/dist/Transport.js.map +1 -1
- package/dist/bin.js +48 -47
- package/dist/bin.js.map +1 -1
- package/dist/{childRuntime-An0HsIzl.js → childRuntime-Bm3iSg_N.js} +29 -26
- package/dist/{connectionEnded-DCpFCjMX.js → connectionEnded-BwcxQ7W8.js} +4 -4
- package/dist/{entry-B89sepMU.js → entry-psOpX3Bj.js} +72 -35
- package/dist/host/auth/bearer.js +1 -1
- package/dist/host/auth/jwt.js +1 -1
- package/dist/host/auth/mtls.js +2 -2
- package/dist/host/auth/none.js +1 -1
- package/dist/host/command.js +4 -4
- package/dist/host/local.js +3 -3
- package/dist/host/unix.js +3 -3
- package/dist/host/websocket.js +3 -3
- package/dist/host/worker.js +4 -5
- package/dist/{importer-Bk6U_sn4.js → importer-Cvf88eAY.js} +5 -8
- package/dist/index.js +9 -9
- package/dist/index.js.map +1 -1
- package/dist/internal/Certificates.js +4 -4
- package/dist/internal/Certificates.js.map +1 -1
- package/dist/internal/FailureGroup.js +3 -2
- package/dist/internal/FailureGroup.js.map +1 -1
- package/dist/internal/assembly.js +9 -2
- package/dist/internal/assembly.js.map +1 -1
- package/dist/internal/auth/mtls.js +1 -1
- package/dist/internal/auth/mtls.js.map +1 -1
- package/dist/internal/child.js +20 -17
- package/dist/internal/child.js.map +1 -1
- package/dist/internal/childRuntime.js +4 -4
- package/dist/internal/childRuntime.js.map +1 -1
- package/dist/internal/directTransport.d.ts +1 -1
- package/dist/internal/entry.d.ts.map +1 -1
- package/dist/internal/entry.js +1 -1
- package/dist/internal/entry.js.map +1 -1
- package/dist/internal/execution.js +40 -5
- package/dist/internal/execution.js.map +1 -1
- package/dist/internal/host/runner.js +7 -6
- package/dist/internal/host/runner.js.map +1 -1
- package/dist/internal/host.js +5 -5
- package/dist/internal/host.js.map +1 -1
- package/dist/internal/port.js +5 -5
- package/dist/internal/port.js.map +1 -1
- package/dist/internal/release.js +1 -1
- package/dist/internal/release.js.map +1 -1
- package/dist/internal/runner/Connection.d.ts +1 -1
- package/dist/internal/runner/Connection.js +4 -4
- package/dist/internal/runner/Connection.js.map +1 -1
- package/dist/internal/runner/Execution.js +3 -3
- package/dist/internal/runner/Execution.js.map +1 -1
- package/dist/internal/runner/Framing.js +1 -1
- package/dist/internal/runner/Framing.js.map +1 -1
- package/dist/internal/runner/Local.js +1 -1
- package/dist/internal/runner/Local.js.map +1 -1
- package/dist/internal/runner/Lock.js +1 -1
- package/dist/internal/runner/Lock.js.map +1 -1
- package/dist/internal/runner/Packages.d.ts +1 -1
- package/dist/internal/runner/Packages.js +22 -25
- package/dist/internal/runner/Packages.js.map +1 -1
- package/dist/internal/runner/Processes.js +7 -9
- package/dist/internal/runner/Processes.js.map +1 -1
- package/dist/internal/runner/Store.js +3 -3
- package/dist/internal/runner/Store.js.map +1 -1
- package/dist/internal/runner/Supervisor.js +46 -47
- package/dist/internal/runner/Supervisor.js.map +1 -1
- package/dist/internal/runner/accept.js +3 -3
- package/dist/internal/runner/accept.js.map +1 -1
- package/dist/internal/runner/model.js +1 -1
- package/dist/internal/runner/model.js.map +1 -1
- package/dist/internal/serve.js +1 -1
- package/dist/internal/serve.js.map +1 -1
- package/dist/internal/supervisorPlugin.js +1 -1
- package/dist/internal/watchdogMain.js +5 -5
- package/dist/internal/watchdogMain.js.map +1 -1
- package/dist/internal/websocketServer.js +3 -1
- package/dist/internal/websocketServer.js.map +1 -1
- package/dist/internal/workerMain.js +3 -3
- package/dist/{port-DSVUY8Bq.js → port--3x2zJ4D.js} +8 -8
- package/dist/{release-BY4Mlluy.js → release-DfZPuIcK.js} +1 -1
- package/dist/runner/auth/bearer.js +2 -2
- package/dist/runner/auth/jwt.js +4 -4
- package/dist/runner/auth/jwt.js.map +1 -1
- package/dist/runner/auth/mtls.js +3 -3
- package/dist/runner/auth/none.js +1 -1
- package/dist/runner/index.js +5 -5
- package/dist/runner/stdio.js +3 -3
- package/dist/runner/unix.js +3 -3
- package/dist/runner/websocket.js +4 -4
- package/dist/{socketChannel-C_ppdSzz.js → socketChannel-CJvL2DiB.js} +2 -2
- package/dist/{supervisorPlugin-CivhM7x5.js → supervisorPlugin-t6Z6PrhD.js} +72 -76
- package/dist/{token-Cc_1CTbE.js → token-CYBNNlhS.js} +1 -1
- package/dist/{tokenCredentials-CI6mMRRd.js → tokenCredentials-CbCuk-Rt.js} +3 -3
- package/dist/{websocketServer-D2Hk7wsA.js → websocketServer-NQhODlEt.js} +5 -3
- package/dist/{workerChannel-cmLc13vm.js → workerChannel-C8hhBJzA.js} +1 -1
- package/package.json +3 -3
- package/src/Auth.ts +9 -2
- package/src/Errors.ts +8 -0
- package/src/Transport.ts +6 -2
- package/src/bin.ts +28 -30
- package/src/index.ts +5 -3
- package/src/internal/Certificates.ts +10 -13
- package/src/internal/FailureGroup.ts +128 -121
- package/src/internal/assembly.ts +19 -10
- package/src/internal/auth/mtls.ts +19 -20
- package/src/internal/child.ts +248 -239
- package/src/internal/childRuntime.ts +81 -80
- package/src/internal/entry.ts +10 -11
- package/src/internal/execution.ts +69 -12
- package/src/internal/host/runner.ts +153 -153
- package/src/internal/host.ts +116 -120
- package/src/internal/port.ts +100 -103
- package/src/internal/release.ts +3 -2
- package/src/internal/runner/Connection.ts +52 -60
- package/src/internal/runner/Execution.ts +163 -164
- package/src/internal/runner/Framing.ts +9 -10
- package/src/internal/runner/Local.ts +26 -27
- package/src/internal/runner/Lock.ts +23 -25
- package/src/internal/runner/Packages.ts +67 -70
- package/src/internal/runner/Processes.ts +89 -92
- package/src/internal/runner/Store.ts +110 -114
- package/src/internal/runner/Supervisor.ts +392 -405
- package/src/internal/runner/accept.ts +76 -84
- package/src/internal/runner/model.ts +20 -21
- package/src/internal/serve.ts +15 -13
- package/src/internal/watchdogMain.ts +16 -11
- package/src/internal/websocketServer.ts +18 -3
- package/src/runner/auth/jwt.ts +10 -6
- package/dist/cleanup-CXLa1Wd9.js +0 -13
package/src/internal/child.ts
CHANGED
|
@@ -37,61 +37,62 @@ type LogEntry = Omit<typeof Log.Type, "cause"> & {
|
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
/** Queue data; serialization belongs to the RPC consumer, outside synchronous logger callbacks. */
|
|
40
|
-
export const telemetry = (
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
if (!accepted) MutableRef.increment(dropped)
|
|
40
|
+
export const telemetry = Effect.fnUntraced(function* (
|
|
41
|
+
contract: RunnerContract,
|
|
42
|
+
) {
|
|
43
|
+
const events = yield* Queue.bounded<ChildEvent>(2)
|
|
44
|
+
const logs = yield* Queue.dropping<LogEntry, Cause.Done>(128)
|
|
45
|
+
const dropped = MutableRef.make(0)
|
|
46
|
+
const encode = (cause: Cause.Cause<unknown>) =>
|
|
47
|
+
contract.encodeCause(cause).pipe(Effect.orDie)
|
|
48
|
+
const logger = Logger.make((entry) => {
|
|
49
|
+
// Logger callbacks are synchronous; a dropping offer and loss counter cannot suspend.
|
|
50
|
+
const accepted = Queue.offerUnsafe(logs, {
|
|
51
|
+
level: entry.logLevel,
|
|
52
|
+
message: entry.message,
|
|
53
|
+
cause: entry.cause,
|
|
54
|
+
annotations: entry.fiber.getRef(References.CurrentLogAnnotations),
|
|
56
55
|
})
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
56
|
+
if (!accepted) MutableRef.increment(dropped)
|
|
57
|
+
})
|
|
58
|
+
return {
|
|
59
|
+
events,
|
|
60
|
+
logs,
|
|
61
|
+
logger,
|
|
62
|
+
eventStream: Stream.fromQueue(events).pipe(
|
|
63
|
+
Stream.mapEffect((event): Effect.Effect<typeof Event.Type> =>
|
|
64
|
+
event._tag === "Failed"
|
|
65
|
+
? encode(event.cause).pipe(
|
|
66
|
+
Effect.map(
|
|
67
|
+
(cause) =>
|
|
68
|
+
({
|
|
69
|
+
_tag: "Failed",
|
|
70
|
+
cause,
|
|
71
|
+
}) satisfies typeof Event.Type,
|
|
72
|
+
),
|
|
73
|
+
)
|
|
74
|
+
: Effect.succeed(event),
|
|
75
75
|
),
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
})),
|
|
84
|
-
),
|
|
85
|
-
),
|
|
86
|
-
Stream.concat(
|
|
87
|
-
Stream.sync((): typeof LogEvent.Type => ({
|
|
88
|
-
_tag: "LogsEnded",
|
|
76
|
+
),
|
|
77
|
+
logStream: Stream.fromQueue(logs).pipe(
|
|
78
|
+
Stream.mapEffect((entry) =>
|
|
79
|
+
encode(entry.cause).pipe(
|
|
80
|
+
Effect.map((cause): typeof LogEvent.Type => ({
|
|
81
|
+
_tag: "Log",
|
|
82
|
+
entry: { ...entry, cause },
|
|
89
83
|
dropped: MutableRef.get(dropped),
|
|
90
84
|
})),
|
|
91
85
|
),
|
|
92
86
|
),
|
|
93
|
-
|
|
94
|
-
|
|
87
|
+
Stream.concat(
|
|
88
|
+
Stream.sync((): typeof LogEvent.Type => ({
|
|
89
|
+
_tag: "LogsEnded",
|
|
90
|
+
dropped: MutableRef.get(dropped),
|
|
91
|
+
})),
|
|
92
|
+
),
|
|
93
|
+
),
|
|
94
|
+
}
|
|
95
|
+
})
|
|
95
96
|
type Telemetry = Effect.Success<ReturnType<typeof telemetry>>
|
|
96
97
|
interface Running {
|
|
97
98
|
readonly fork: Pick<Fork.Fork<never, unknown>, "dispose" | "state">
|
|
@@ -114,210 +115,218 @@ const runningOf = (state: Lifecycle): Option.Option<Running> =>
|
|
|
114
115
|
? state.running
|
|
115
116
|
: Option.none()
|
|
116
117
|
|
|
117
|
-
const cleanup = (
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
),
|
|
118
|
+
const cleanup = Effect.fnUntraced(function* (
|
|
119
|
+
running: Running,
|
|
120
|
+
retired: boolean,
|
|
121
|
+
) {
|
|
122
|
+
const pluginExit = yield* Effect.exit(running.fork.dispose)
|
|
123
|
+
const bridgeExit = yield* Effect.exit(running.bridge.dispose)
|
|
124
|
+
yield* Fiber.join(running.observation)
|
|
125
|
+
const failed = yield* Ref.get(running.observedFailure)
|
|
126
|
+
return Array.reduce(
|
|
127
|
+
[bridgeExit, pluginExit],
|
|
128
|
+
retired ? failed : Cause.empty,
|
|
129
|
+
(cause, exit) =>
|
|
130
|
+
Exit.isFailure(exit)
|
|
131
|
+
? Cause.combine(
|
|
132
|
+
cause,
|
|
133
|
+
Cause.fromReasons(
|
|
134
|
+
Array.flatMap(exit.cause.reasons, (reason) =>
|
|
135
|
+
reason._tag === "Fail" ? reason.error.cause.reasons : [reason],
|
|
136
136
|
),
|
|
137
|
-
)
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
137
|
+
),
|
|
138
|
+
)
|
|
139
|
+
: cause,
|
|
140
|
+
)
|
|
141
|
+
})
|
|
141
142
|
|
|
142
|
-
export const lifecycle = (
|
|
143
|
+
export const lifecycle = Effect.fnUntraced(function* (
|
|
143
144
|
contract: RunnerContract,
|
|
144
145
|
peer: Peer,
|
|
145
146
|
identity: typeof Identity.Type,
|
|
146
147
|
telemetry: Telemetry,
|
|
147
|
-
)
|
|
148
|
-
Effect.
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
148
|
+
) {
|
|
149
|
+
const scope = yield* Effect.scope
|
|
150
|
+
const instanceScope = yield* Scope.fork(scope)
|
|
151
|
+
const releaseExports = yield* Deferred.make<void>()
|
|
152
|
+
const state = yield* SynchronizedRef.make<Lifecycle>(Lifecycle.Idle())
|
|
153
|
+
// This gate represents host consumers draining, not a second lifecycle state.
|
|
154
|
+
const disposing = (current: Lifecycle) =>
|
|
155
|
+
Lifecycle.Disposing({ running: runningOf(current) })
|
|
156
|
+
const release = SynchronizedRef.update(state, disposing).pipe(
|
|
157
|
+
Effect.andThen(Deferred.succeed(releaseExports, undefined)),
|
|
158
|
+
)
|
|
157
159
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
Lifecycle.Withdrawing({
|
|
186
|
-
running: runningOf(current),
|
|
187
|
-
}),
|
|
188
|
-
],
|
|
189
|
-
)
|
|
190
|
-
if (withdrawing)
|
|
191
|
-
yield* Queue.offer(telemetry.events, ChildEvent.Withdrawing())
|
|
192
|
-
yield* Deferred.await(releaseExports)
|
|
193
|
-
}),
|
|
160
|
+
const exportBridge = Graft.plugin(
|
|
161
|
+
Plugin.make({
|
|
162
|
+
name: `${contract.name}/exports`,
|
|
163
|
+
inject: Array.map(contract.provides, (service) => service.key),
|
|
164
|
+
apply: Effect.fnUntraced(function* () {
|
|
165
|
+
for (const service of contract.provides)
|
|
166
|
+
yield* service.serve(peer).pipe(
|
|
167
|
+
Effect.tapCause((cause) =>
|
|
168
|
+
Cause.hasInterruptsOnly(cause)
|
|
169
|
+
? Effect.void
|
|
170
|
+
: Queue.offer(telemetry.events, ChildEvent.Failed({ cause })),
|
|
171
|
+
),
|
|
172
|
+
Effect.forkScoped,
|
|
173
|
+
)
|
|
174
|
+
yield* Effect.addFinalizer(
|
|
175
|
+
Effect.fnUntraced(function* () {
|
|
176
|
+
const withdrawing = yield* SynchronizedRef.modify(
|
|
177
|
+
state,
|
|
178
|
+
(current) =>
|
|
179
|
+
current._tag === "Disposing"
|
|
180
|
+
? [false, current]
|
|
181
|
+
: [
|
|
182
|
+
true,
|
|
183
|
+
Lifecycle.Withdrawing({
|
|
184
|
+
running: runningOf(current),
|
|
185
|
+
}),
|
|
186
|
+
],
|
|
194
187
|
)
|
|
188
|
+
if (withdrawing)
|
|
189
|
+
yield* Queue.offer(telemetry.events, ChildEvent.Withdrawing())
|
|
190
|
+
yield* Deferred.await(releaseExports)
|
|
195
191
|
}),
|
|
192
|
+
)
|
|
196
193
|
}),
|
|
197
|
-
)
|
|
194
|
+
}),
|
|
195
|
+
).pipe(Scope.provide(instanceScope))
|
|
198
196
|
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
detail: "The implementation module exports no plugin",
|
|
217
|
-
})
|
|
218
|
-
const fork = yield* Graft.plugin(
|
|
219
|
-
{ ...plugin.value, retry: undefined },
|
|
220
|
-
input,
|
|
221
|
-
).pipe(
|
|
222
|
-
Effect.provideService(Instance, identity),
|
|
223
|
-
Scope.provide(instanceScope),
|
|
224
|
-
)
|
|
225
|
-
const observedFailure = yield* Ref.make<Cause.Cause<unknown>>(
|
|
226
|
-
Cause.empty,
|
|
227
|
-
)
|
|
228
|
-
const observation = yield* fork.state.pipe(
|
|
229
|
-
Stream.runForEach((state) =>
|
|
230
|
-
state._tag === "Failed"
|
|
231
|
-
? Ref.set(observedFailure, state.cause)
|
|
232
|
-
: Effect.void,
|
|
233
|
-
),
|
|
234
|
-
Effect.forkIn(instanceScope),
|
|
235
|
-
)
|
|
236
|
-
const bridge = yield* exportBridge
|
|
237
|
-
return { fork, bridge, observedFailure, observation } satisfies Running
|
|
197
|
+
const start = Effect.fnUntraced(function* (input: Schema.Json) {
|
|
198
|
+
// Imported clients outlive Load and remain available during plugin cleanup.
|
|
199
|
+
for (const service of contract.inject)
|
|
200
|
+
yield* service.publish(peer).pipe(
|
|
201
|
+
Scope.provide(scope),
|
|
202
|
+
Effect.mapError((error) =>
|
|
203
|
+
ActivationFailed.make({
|
|
204
|
+
detail: "Publishing imported services failed",
|
|
205
|
+
cause: Cause.fail(error),
|
|
206
|
+
}),
|
|
207
|
+
),
|
|
208
|
+
)
|
|
209
|
+
const module = yield* loadModule(contract.module)
|
|
210
|
+
const plugin = Plugin.fromModule(module)
|
|
211
|
+
if (Option.isNone(plugin))
|
|
212
|
+
return yield* ActivationFailed.make({
|
|
213
|
+
detail: "The implementation module exports no plugin",
|
|
238
214
|
})
|
|
239
|
-
const
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
)
|
|
267
|
-
const exported = yield* running.bridge.state.pipe(
|
|
268
|
-
Stream.filter(
|
|
269
|
-
(state) =>
|
|
270
|
-
state._tag === "Active" ||
|
|
271
|
-
state._tag === "Failed" ||
|
|
272
|
-
state._tag === "Disposed",
|
|
273
|
-
),
|
|
274
|
-
Stream.runHead,
|
|
275
|
-
)
|
|
276
|
-
if (Option.isSome(exported) && exported.value._tag === "Failed")
|
|
277
|
-
return yield* ActivationFailed.make({
|
|
278
|
-
detail: "The remote export bridge failed during startup",
|
|
279
|
-
cause: exported.value.cause,
|
|
280
|
-
})
|
|
281
|
-
if (Option.isNone(exported) || exported.value._tag !== "Active")
|
|
282
|
-
return yield* ActivationFailed.make({
|
|
283
|
-
detail: "The remote export bridge stopped during startup",
|
|
284
|
-
})
|
|
215
|
+
const fork = yield* Graft.plugin(
|
|
216
|
+
{ ...plugin.value, retry: undefined },
|
|
217
|
+
input,
|
|
218
|
+
).pipe(
|
|
219
|
+
Effect.provideService(Instance, identity),
|
|
220
|
+
Scope.provide(instanceScope),
|
|
221
|
+
)
|
|
222
|
+
const observedFailure = yield* Ref.make<Cause.Cause<unknown>>(Cause.empty)
|
|
223
|
+
const observation = yield* fork.state.pipe(
|
|
224
|
+
Stream.runForEach((state) =>
|
|
225
|
+
state._tag === "Failed"
|
|
226
|
+
? Ref.set(observedFailure, state.cause)
|
|
227
|
+
: Effect.void,
|
|
228
|
+
),
|
|
229
|
+
Effect.forkIn(instanceScope),
|
|
230
|
+
)
|
|
231
|
+
const bridge = yield* exportBridge
|
|
232
|
+
return { fork, bridge, observedFailure, observation } satisfies Running
|
|
233
|
+
})
|
|
234
|
+
const awaitActive = Effect.fnUntraced(function* (running: Running) {
|
|
235
|
+
const first = yield* running.fork.state.pipe(
|
|
236
|
+
Stream.filter((state) => state._tag !== "Loading"),
|
|
237
|
+
Stream.runHead,
|
|
238
|
+
)
|
|
239
|
+
if (Option.isNone(first))
|
|
240
|
+
return yield* ActivationFailed.make({
|
|
241
|
+
detail: "The remote fork disappeared during startup",
|
|
285
242
|
})
|
|
286
|
-
|
|
287
|
-
Effect.
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
243
|
+
if (first.value._tag === "Failed")
|
|
244
|
+
return yield* Effect.fail(contract.normalizeCause(first.value.cause))
|
|
245
|
+
if (first.value._tag !== "Active")
|
|
246
|
+
return yield* ActivationFailed.make({
|
|
247
|
+
detail:
|
|
248
|
+
"The remote plugin did not become active; check its declared dependencies",
|
|
249
|
+
})
|
|
250
|
+
yield* running.fork.state.pipe(
|
|
251
|
+
Stream.runForEach((state) =>
|
|
252
|
+
state._tag === "Failed"
|
|
253
|
+
? Queue.offer(
|
|
254
|
+
telemetry.events,
|
|
255
|
+
ChildEvent.Failed({ cause: state.cause }),
|
|
256
|
+
)
|
|
257
|
+
: Effect.void,
|
|
258
|
+
),
|
|
259
|
+
Effect.forkIn(instanceScope),
|
|
260
|
+
)
|
|
261
|
+
const exported = yield* running.bridge.state.pipe(
|
|
262
|
+
Stream.filter(
|
|
263
|
+
(state) =>
|
|
264
|
+
state._tag === "Active" ||
|
|
265
|
+
state._tag === "Failed" ||
|
|
266
|
+
state._tag === "Disposed",
|
|
267
|
+
),
|
|
268
|
+
Stream.runHead,
|
|
269
|
+
)
|
|
270
|
+
if (Option.isSome(exported) && exported.value._tag === "Failed")
|
|
271
|
+
return yield* ActivationFailed.make({
|
|
272
|
+
detail: "The remote export bridge failed during startup",
|
|
273
|
+
cause: exported.value.cause,
|
|
274
|
+
})
|
|
275
|
+
if (Option.isNone(exported) || exported.value._tag !== "Active")
|
|
276
|
+
return yield* ActivationFailed.make({
|
|
277
|
+
detail: "The remote export bridge stopped during startup",
|
|
278
|
+
})
|
|
279
|
+
})
|
|
280
|
+
const load = Effect.fnUntraced(
|
|
281
|
+
function* (input: Schema.Json) {
|
|
282
|
+
yield* SynchronizedRef.updateEffect(state, (current) =>
|
|
283
|
+
current._tag === "Idle"
|
|
284
|
+
? Effect.succeed(Lifecycle.Loading())
|
|
285
|
+
: Effect.fail(AlreadyLoaded.make({})),
|
|
286
|
+
)
|
|
287
|
+
const running = yield* start(input)
|
|
288
|
+
const disposed = yield* SynchronizedRef.modify(
|
|
289
|
+
state,
|
|
290
|
+
(current): readonly [boolean, Lifecycle] =>
|
|
295
291
|
current._tag === "Withdrawing"
|
|
296
|
-
? Lifecycle.Withdrawing({ running: Option.some(running) })
|
|
292
|
+
? [false, Lifecycle.Withdrawing({ running: Option.some(running) })]
|
|
297
293
|
: current._tag === "Disposing"
|
|
298
|
-
? Lifecycle.Disposing({ running: Option.some(running) })
|
|
299
|
-
: Lifecycle.Running({ running }),
|
|
300
|
-
)
|
|
301
|
-
yield* awaitActive(running)
|
|
302
|
-
}).pipe(
|
|
303
|
-
Effect.mapError((error) =>
|
|
304
|
-
Cause.isCause(error)
|
|
305
|
-
? error
|
|
306
|
-
: contract.normalizeCause(Cause.fail(error)),
|
|
307
|
-
),
|
|
294
|
+
? [true, Lifecycle.Disposing({ running: Option.some(running) })]
|
|
295
|
+
: [false, Lifecycle.Running({ running })],
|
|
308
296
|
)
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
297
|
+
// Dispose saw Loading and found nothing to stop; this load owns the cleanup.
|
|
298
|
+
if (disposed) {
|
|
299
|
+
const cause = yield* cleanup(running, false)
|
|
300
|
+
return yield* Effect.fail(
|
|
301
|
+
Cause.combine(
|
|
302
|
+
Cause.fail(
|
|
303
|
+
ActivationFailed.make({
|
|
304
|
+
detail: "The execution was disposed during load",
|
|
305
|
+
}),
|
|
306
|
+
),
|
|
307
|
+
cause,
|
|
308
|
+
),
|
|
309
|
+
)
|
|
310
|
+
}
|
|
311
|
+
yield* awaitActive(running)
|
|
312
|
+
},
|
|
313
|
+
Effect.mapError((error) =>
|
|
314
|
+
contract.normalizeCause(Cause.isCause(error) ? error : Cause.fail(error)),
|
|
315
|
+
),
|
|
316
|
+
)
|
|
317
|
+
const dispose = Effect.gen(function* () {
|
|
318
|
+
// One transition, so a concurrent Load or export withdrawal cannot slip between read and write.
|
|
319
|
+
const previous = yield* SynchronizedRef.getAndUpdate(state, disposing)
|
|
320
|
+
yield* Deferred.succeed(releaseExports, undefined)
|
|
321
|
+
const current = runningOf(previous)
|
|
322
|
+
const retired = previous._tag === "Withdrawing"
|
|
323
|
+
const cause = Option.isSome(current)
|
|
324
|
+
? yield* cleanup(current.value, retired)
|
|
325
|
+
: Cause.empty
|
|
326
|
+
yield* Queue.offer(telemetry.events, ChildEvent.Stopped())
|
|
327
|
+
yield* Queue.end(telemetry.logs)
|
|
328
|
+
if (cause.reasons.length > 0)
|
|
329
|
+
return yield* Effect.fail(contract.normalizeCause(cause))
|
|
323
330
|
})
|
|
331
|
+
return { load, dispose, release }
|
|
332
|
+
})
|