@graftland/plugin-remote 0.0.0-stage → 0.3.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/LICENSE +202 -0
- package/README.md +76 -2
- package/README.zh-CN.md +71 -0
- package/README.zh-TW.md +71 -0
- package/dist/Auth-BElTC9_X.js +6 -0
- package/dist/Auth.d.ts +49 -0
- package/dist/Auth.d.ts.map +1 -0
- package/dist/Auth.js +8 -0
- package/dist/Auth.js.map +1 -0
- package/dist/Bootstrap-MCwivGN1.js +9 -0
- package/dist/ConfigDirectory-BCC6q2R4.js +5 -0
- package/dist/Contract-CcpSIJm4.js +121 -0
- package/dist/Contract.d.ts +71 -0
- package/dist/Contract.d.ts.map +1 -0
- package/dist/Contract.js +1 -0
- package/dist/Contract.js.map +1 -0
- package/dist/Errors-oPsTLY7l.js +45 -0
- package/dist/Errors.d.ts +67 -0
- package/dist/Errors.d.ts.map +1 -0
- package/dist/Errors.js +47 -0
- package/dist/Errors.js.map +1 -0
- package/dist/FailureGroup-BcH0XOBi.js +53 -0
- package/dist/Framing-zaYdOJnT.js +61 -0
- package/dist/Graft-BsYq3F-J.js +985 -0
- package/dist/Instance.d.ts +7 -0
- package/dist/Instance.d.ts.map +1 -0
- package/dist/Instance.js +14 -0
- package/dist/Instance.js.map +1 -0
- package/dist/Packages-B1TPk8pa.js +111 -0
- package/dist/Peer-IJgFK6mD.js +342 -0
- package/dist/ProcessObservation-DEGrEMfP.js +38 -0
- package/dist/ServeLifecycle-CJM0gG5B.js +24 -0
- package/dist/Testing.d.ts +26 -0
- package/dist/Testing.d.ts.map +1 -0
- package/dist/Testing.js +69 -0
- package/dist/Testing.js.map +1 -0
- package/dist/Transport-DULzpsgR.js +6 -0
- package/dist/Transport.d.ts +39 -0
- package/dist/Transport.d.ts.map +1 -0
- package/dist/Transport.js +8 -0
- package/dist/Transport.js.map +1 -0
- package/dist/WatchdogProtocol-BZmVxrBP.js +17 -0
- package/dist/_virtual/_rolldown/runtime.js +12 -0
- package/dist/bin.d.ts +1 -0
- package/dist/bin.js +28275 -0
- package/dist/bin.js.map +1 -0
- package/dist/childRuntime-DSqtFN9o.js +188 -0
- package/dist/cleanup-BPWr-5U9.js +13 -0
- package/dist/command-9zrHdbwT.js +7 -0
- package/dist/connectionEnded-DUgcobEd.js +9 -0
- package/dist/directTransport-DrexuPSm.js +5 -0
- package/dist/entry-SYsx9VNb.js +708 -0
- package/dist/host/auth/bearer.d.ts +5 -0
- package/dist/host/auth/bearer.d.ts.map +1 -0
- package/dist/host/auth/bearer.js +4 -0
- package/dist/host/auth/bearer.js.map +1 -0
- package/dist/host/auth/jwt.d.ts +5 -0
- package/dist/host/auth/jwt.d.ts.map +1 -0
- package/dist/host/auth/jwt.js +4 -0
- package/dist/host/auth/jwt.js.map +1 -0
- package/dist/host/auth/mtls.d.ts +8 -0
- package/dist/host/auth/mtls.d.ts.map +1 -0
- package/dist/host/auth/mtls.js +34 -0
- package/dist/host/auth/mtls.js.map +1 -0
- package/dist/host/auth/none.d.ts +3 -0
- package/dist/host/auth/none.d.ts.map +1 -0
- package/dist/host/auth/none.js +13 -0
- package/dist/host/auth/none.js.map +1 -0
- package/dist/host/command.d.ts +22 -0
- package/dist/host/command.d.ts.map +1 -0
- package/dist/host/command.js +61 -0
- package/dist/host/command.js.map +1 -0
- package/dist/host/local.d.ts +4 -0
- package/dist/host/local.d.ts.map +1 -0
- package/dist/host/local.js +52 -0
- package/dist/host/local.js.map +1 -0
- package/dist/host/unix.d.ts +11 -0
- package/dist/host/unix.d.ts.map +1 -0
- package/dist/host/unix.js +39 -0
- package/dist/host/unix.js.map +1 -0
- package/dist/host/websocket.d.ts +13 -0
- package/dist/host/websocket.d.ts.map +1 -0
- package/dist/host/websocket.js +36 -0
- package/dist/host/websocket.js.map +1 -0
- package/dist/host/worker.d.ts +4 -0
- package/dist/host/worker.d.ts.map +1 -0
- package/dist/host/worker.js +52 -0
- package/dist/host/worker.js.map +1 -0
- package/dist/importer-D7zSgqxq.js +347 -0
- package/dist/index.d.ts +29 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +86 -0
- package/dist/index.js.map +1 -0
- package/dist/internal/Certificates.js +182 -0
- package/dist/internal/Certificates.js.map +1 -0
- package/dist/internal/FailureGroup.js +55 -0
- package/dist/internal/FailureGroup.js.map +1 -0
- package/dist/internal/Limits.d.ts +1 -0
- package/dist/internal/Limits.js +17 -0
- package/dist/internal/Limits.js.map +1 -0
- package/dist/internal/Peer.js +6 -0
- package/dist/internal/Peer.js.map +1 -0
- package/dist/internal/ServeLifecycle.js +26 -0
- package/dist/internal/ServeLifecycle.js.map +1 -0
- package/dist/internal/Supervisor.js +7 -0
- package/dist/internal/Supervisor.js.map +1 -0
- package/dist/internal/assembly.d.ts +5 -0
- package/dist/internal/assembly.js +138 -0
- package/dist/internal/assembly.js.map +1 -0
- package/dist/internal/auth/mtls.js +23 -0
- package/dist/internal/auth/mtls.js.map +1 -0
- package/dist/internal/auth/token.js +19 -0
- package/dist/internal/auth/token.js.map +1 -0
- package/dist/internal/auth/tokenCredentials.d.ts +8 -0
- package/dist/internal/auth/tokenCredentials.d.ts.map +1 -0
- package/dist/internal/auth/tokenCredentials.js +33 -0
- package/dist/internal/auth/tokenCredentials.js.map +1 -0
- package/dist/internal/child.js +125 -0
- package/dist/internal/child.js.map +1 -0
- package/dist/internal/childErrors.d.ts +33 -0
- package/dist/internal/childErrors.d.ts.map +1 -0
- package/dist/internal/childErrors.js +29 -0
- package/dist/internal/childErrors.js.map +1 -0
- package/dist/internal/childRuntime.js +59 -0
- package/dist/internal/childRuntime.js.map +1 -0
- package/dist/internal/cleanup.js +15 -0
- package/dist/internal/cleanup.js.map +1 -0
- package/dist/internal/config.js +6 -0
- package/dist/internal/config.js.map +1 -0
- package/dist/internal/control.js +42 -0
- package/dist/internal/control.js.map +1 -0
- package/dist/internal/credentials.d.ts +44 -0
- package/dist/internal/credentials.d.ts.map +1 -0
- package/dist/internal/credentials.js +54 -0
- package/dist/internal/credentials.js.map +1 -0
- package/dist/internal/directTransport.d.ts +3 -0
- package/dist/internal/directTransport.js +7 -0
- package/dist/internal/directTransport.js.map +1 -0
- package/dist/internal/entry.d.ts +9 -0
- package/dist/internal/entry.d.ts.map +1 -0
- package/dist/internal/entry.js +144 -0
- package/dist/internal/entry.js.map +1 -0
- package/dist/internal/entryConfig.d.ts +202 -0
- package/dist/internal/entryConfig.d.ts.map +1 -0
- package/dist/internal/entryConfig.js +176 -0
- package/dist/internal/entryConfig.js.map +1 -0
- package/dist/internal/execution.d.ts +5 -0
- package/dist/internal/execution.js +43 -0
- package/dist/internal/execution.js.map +1 -0
- package/dist/internal/follow.js +11 -0
- package/dist/internal/follow.js.map +1 -0
- package/dist/internal/forkTree.js +7 -0
- package/dist/internal/forkTree.js.map +1 -0
- package/dist/internal/host/local.js +28 -0
- package/dist/internal/host/local.js.map +1 -0
- package/dist/internal/host/runner.js +100 -0
- package/dist/internal/host/runner.js.map +1 -0
- package/dist/internal/host.js +71 -0
- package/dist/internal/host.js.map +1 -0
- package/dist/internal/importer.js +18 -0
- package/dist/internal/importer.js.map +1 -0
- package/dist/internal/listenerStartup.js +17 -0
- package/dist/internal/listenerStartup.js.map +1 -0
- package/dist/internal/loadModule.js +20 -0
- package/dist/internal/loadModule.js.map +1 -0
- package/dist/internal/platform.js +9 -0
- package/dist/internal/platform.js.map +1 -0
- package/dist/internal/port.d.ts +13 -0
- package/dist/internal/port.d.ts.map +1 -0
- package/dist/internal/port.js +275 -0
- package/dist/internal/port.js.map +1 -0
- package/dist/internal/release.js +25 -0
- package/dist/internal/release.js.map +1 -0
- package/dist/internal/runner/Bootstrap.js +11 -0
- package/dist/internal/runner/Bootstrap.js.map +1 -0
- package/dist/internal/runner/Connection.d.ts +11 -0
- package/dist/internal/runner/Connection.js +56 -0
- package/dist/internal/runner/Connection.js.map +1 -0
- package/dist/internal/runner/Execution.js +115 -0
- package/dist/internal/runner/Execution.js.map +1 -0
- package/dist/internal/runner/Framing.d.ts +11 -0
- package/dist/internal/runner/Framing.d.ts.map +1 -0
- package/dist/internal/runner/Framing.js +63 -0
- package/dist/internal/runner/Framing.js.map +1 -0
- package/dist/internal/runner/Local.js +46 -0
- package/dist/internal/runner/Local.js.map +1 -0
- package/dist/internal/runner/Lock.js +25 -0
- package/dist/internal/runner/Lock.js.map +1 -0
- package/dist/internal/runner/Multiplex.d.ts +5 -0
- package/dist/internal/runner/Multiplex.js +49 -0
- package/dist/internal/runner/Multiplex.js.map +1 -0
- package/dist/internal/runner/Packages.d.ts +3 -0
- package/dist/internal/runner/Packages.js +108 -0
- package/dist/internal/runner/Packages.js.map +1 -0
- package/dist/internal/runner/ProcessObservation.js +40 -0
- package/dist/internal/runner/ProcessObservation.js.map +1 -0
- package/dist/internal/runner/Processes.js +83 -0
- package/dist/internal/runner/Processes.js.map +1 -0
- package/dist/internal/runner/Retention.js +13 -0
- package/dist/internal/runner/Retention.js.map +1 -0
- package/dist/internal/runner/Store.js +78 -0
- package/dist/internal/runner/Store.js.map +1 -0
- package/dist/internal/runner/Supervisor.js +375 -0
- package/dist/internal/runner/Supervisor.js.map +1 -0
- package/dist/internal/runner/Watchdog.js +47 -0
- package/dist/internal/runner/Watchdog.js.map +1 -0
- package/dist/internal/runner/WatchdogProtocol.js +19 -0
- package/dist/internal/runner/WatchdogProtocol.js.map +1 -0
- package/dist/internal/runner/Wire.d.ts +5 -0
- package/dist/internal/runner/Wire.js +64 -0
- package/dist/internal/runner/Wire.js.map +1 -0
- package/dist/internal/runner/accept.js +57 -0
- package/dist/internal/runner/accept.js.map +1 -0
- package/dist/internal/runner/command.js +9 -0
- package/dist/internal/runner/command.js.map +1 -0
- package/dist/internal/runner/connectionEnded.js +11 -0
- package/dist/internal/runner/connectionEnded.js.map +1 -0
- package/dist/internal/runner/model.d.ts +17 -0
- package/dist/internal/runner/model.d.ts.map +1 -0
- package/dist/internal/runner/model.js +231 -0
- package/dist/internal/runner/model.js.map +1 -0
- package/dist/internal/serve.js +60 -0
- package/dist/internal/serve.js.map +1 -0
- package/dist/internal/services.d.ts +18 -0
- package/dist/internal/services.d.ts.map +1 -0
- package/dist/internal/services.js +22 -0
- package/dist/internal/services.js.map +1 -0
- package/dist/internal/socketChannel.js +24 -0
- package/dist/internal/socketChannel.js.map +1 -0
- package/dist/internal/stdioChannel.js +23 -0
- package/dist/internal/stdioChannel.js.map +1 -0
- package/dist/internal/supervisorPlugin.js +1 -0
- package/dist/internal/supervisorPlugin.js.map +1 -0
- package/dist/internal/transportOptions.d.ts +18 -0
- package/dist/internal/transportOptions.d.ts.map +1 -0
- package/dist/internal/watchdogMain.d.ts +1 -0
- package/dist/internal/watchdogMain.js +55 -0
- package/dist/internal/watchdogMain.js.map +1 -0
- package/dist/internal/websocket.js +46 -0
- package/dist/internal/websocket.js.map +1 -0
- package/dist/internal/websocketServer.js +61 -0
- package/dist/internal/websocketServer.js.map +1 -0
- package/dist/internal/workerBootstrap.js +9 -0
- package/dist/internal/workerBootstrap.js.map +1 -0
- package/dist/internal/workerChannel.js +24 -0
- package/dist/internal/workerChannel.js.map +1 -0
- package/dist/internal/workerMain.d.ts +1 -0
- package/dist/internal/workerMain.js +18 -0
- package/dist/internal/workerMain.js.map +1 -0
- package/dist/port-Blbh8tms.js +285 -0
- package/dist/release-BY4Mlluy.js +23 -0
- package/dist/rolldown-runtime-DYf_mQCB.js +34 -0
- package/dist/runner/auth/bearer.d.ts +7 -0
- package/dist/runner/auth/bearer.d.ts.map +1 -0
- package/dist/runner/auth/bearer.js +42 -0
- package/dist/runner/auth/bearer.js.map +1 -0
- package/dist/runner/auth/jwt.d.ts +19 -0
- package/dist/runner/auth/jwt.d.ts.map +1 -0
- package/dist/runner/auth/jwt.js +65 -0
- package/dist/runner/auth/jwt.js.map +1 -0
- package/dist/runner/auth/mtls.d.ts +11 -0
- package/dist/runner/auth/mtls.d.ts.map +1 -0
- package/dist/runner/auth/mtls.js +48 -0
- package/dist/runner/auth/mtls.js.map +1 -0
- package/dist/runner/auth/none.d.ts +3 -0
- package/dist/runner/auth/none.d.ts.map +1 -0
- package/dist/runner/auth/none.js +13 -0
- package/dist/runner/auth/none.js.map +1 -0
- package/dist/runner/index.d.ts +3 -0
- package/dist/runner/index.d.ts.map +1 -0
- package/dist/runner/index.js +71 -0
- package/dist/runner/index.js.map +1 -0
- package/dist/runner/stdio.d.ts +7 -0
- package/dist/runner/stdio.d.ts.map +1 -0
- package/dist/runner/stdio.js +39 -0
- package/dist/runner/stdio.js.map +1 -0
- package/dist/runner/unix.d.ts +7 -0
- package/dist/runner/unix.d.ts.map +1 -0
- package/dist/runner/unix.js +42 -0
- package/dist/runner/unix.js.map +1 -0
- package/dist/runner/websocket.d.ts +28 -0
- package/dist/runner/websocket.d.ts.map +1 -0
- package/dist/runner/websocket.js +77 -0
- package/dist/runner/websocket.js.map +1 -0
- package/dist/socketChannel-KmtzF-9G.js +3658 -0
- package/dist/supervisorPlugin-Inb1S5IV.js +737 -0
- package/dist/token-v5ne2I0-.js +17 -0
- package/dist/tokenCredentials-WUJm2AKm.js +31 -0
- package/dist/websocketServer-MXcCJx0v.js +58 -0
- package/dist/workerChannel-P82YdNjh.js +27 -0
- package/package.json +69 -3
- package/src/Auth.ts +62 -0
- package/src/Contract.ts +100 -0
- package/src/Errors.ts +82 -0
- package/src/Instance.ts +12 -0
- package/src/Testing.ts +135 -0
- package/src/Transport.ts +52 -0
- package/src/bin.ts +145 -0
- package/src/host/auth/bearer.ts +10 -0
- package/src/host/auth/jwt.ts +10 -0
- package/src/host/auth/mtls.ts +48 -0
- package/src/host/auth/none.ts +13 -0
- package/src/host/command.ts +131 -0
- package/src/host/local.ts +41 -0
- package/src/host/unix.ts +65 -0
- package/src/host/websocket.ts +66 -0
- package/src/host/worker.ts +90 -0
- package/src/index.ts +115 -0
- package/src/internal/Certificates.ts +330 -0
- package/src/internal/FailureGroup.ts +176 -0
- package/src/internal/Limits.ts +12 -0
- package/src/internal/Peer.ts +4 -0
- package/src/internal/ServeLifecycle.ts +53 -0
- package/src/internal/Supervisor.ts +14 -0
- package/src/internal/assembly.ts +337 -0
- package/src/internal/auth/mtls.ts +40 -0
- package/src/internal/auth/token.ts +26 -0
- package/src/internal/auth/tokenCredentials.ts +65 -0
- package/src/internal/child.ts +323 -0
- package/src/internal/childErrors.ts +44 -0
- package/src/internal/childRuntime.ts +111 -0
- package/src/internal/cleanup.ts +34 -0
- package/src/internal/config.ts +8 -0
- package/src/internal/control.ts +49 -0
- package/src/internal/credentials.ts +112 -0
- package/src/internal/directTransport.ts +38 -0
- package/src/internal/entry.ts +288 -0
- package/src/internal/entryConfig.ts +190 -0
- package/src/internal/execution.ts +121 -0
- package/src/internal/follow.ts +17 -0
- package/src/internal/forkTree.ts +13 -0
- package/src/internal/host/local.ts +53 -0
- package/src/internal/host/runner.ts +187 -0
- package/src/internal/host.ts +209 -0
- package/src/internal/importer.ts +27 -0
- package/src/internal/listenerStartup.ts +55 -0
- package/src/internal/loadModule.ts +23 -0
- package/src/internal/platform.ts +12 -0
- package/src/internal/port.ts +623 -0
- package/src/internal/release.ts +71 -0
- package/src/internal/runner/Bootstrap.ts +9 -0
- package/src/internal/runner/Connection.ts +184 -0
- package/src/internal/runner/Execution.ts +244 -0
- package/src/internal/runner/Framing.ts +122 -0
- package/src/internal/runner/Local.ts +73 -0
- package/src/internal/runner/Lock.ts +29 -0
- package/src/internal/runner/Multiplex.ts +120 -0
- package/src/internal/runner/Packages.ts +178 -0
- package/src/internal/runner/ProcessObservation.ts +63 -0
- package/src/internal/runner/Processes.ts +109 -0
- package/src/internal/runner/Retention.ts +49 -0
- package/src/internal/runner/Store.ts +160 -0
- package/src/internal/runner/Supervisor.ts +901 -0
- package/src/internal/runner/Watchdog.ts +83 -0
- package/src/internal/runner/WatchdogProtocol.ts +12 -0
- package/src/internal/runner/Wire.ts +99 -0
- package/src/internal/runner/accept.ts +103 -0
- package/src/internal/runner/command.ts +15 -0
- package/src/internal/runner/connectionEnded.ts +28 -0
- package/src/internal/runner/model.ts +364 -0
- package/src/internal/serve.ts +117 -0
- package/src/internal/services.ts +74 -0
- package/src/internal/socketChannel.ts +64 -0
- package/src/internal/stdioChannel.ts +25 -0
- package/src/internal/supervisorPlugin.ts +67 -0
- package/src/internal/transportOptions.ts +17 -0
- package/src/internal/watchdogMain.ts +72 -0
- package/src/internal/websocket.ts +81 -0
- package/src/internal/websocketServer.ts +152 -0
- package/src/internal/workerBootstrap.ts +6 -0
- package/src/internal/workerChannel.ts +54 -0
- package/src/internal/workerMain.ts +23 -0
- package/src/runner/auth/bearer.ts +66 -0
- package/src/runner/auth/jwt.ts +114 -0
- package/src/runner/auth/mtls.ts +37 -0
- package/src/runner/auth/none.ts +13 -0
- package/src/runner/index.ts +97 -0
- package/src/runner/stdio.ts +76 -0
- package/src/runner/unix.ts +53 -0
- package/src/runner/websocket.ts +143 -0
|
@@ -0,0 +1,323 @@
|
|
|
1
|
+
import { loadModule } from "./loadModule"
|
|
2
|
+
import { AlreadyLoaded, ActivationFailed } from "./childErrors"
|
|
3
|
+
import { Graft, Plugin, type Fork } from "@graftland/graft"
|
|
4
|
+
import {
|
|
5
|
+
Array,
|
|
6
|
+
Cause,
|
|
7
|
+
Data,
|
|
8
|
+
Deferred,
|
|
9
|
+
Effect,
|
|
10
|
+
Exit,
|
|
11
|
+
Fiber,
|
|
12
|
+
Logger,
|
|
13
|
+
MutableRef,
|
|
14
|
+
Option,
|
|
15
|
+
Queue,
|
|
16
|
+
Ref,
|
|
17
|
+
References,
|
|
18
|
+
Scope,
|
|
19
|
+
Stream,
|
|
20
|
+
SynchronizedRef,
|
|
21
|
+
} from "effect"
|
|
22
|
+
import type { Schema } from "effect"
|
|
23
|
+
import { type RunnerContract } from "../Contract"
|
|
24
|
+
import { Instance, type Identity } from "../Instance"
|
|
25
|
+
import type * as Port from "./port"
|
|
26
|
+
import type { Event, Log, LogEvent } from "./control"
|
|
27
|
+
|
|
28
|
+
type Peer = Port.Port
|
|
29
|
+
type ChildEvent = Data.TaggedEnum<{
|
|
30
|
+
Failed: { readonly cause: Cause.Cause<unknown> }
|
|
31
|
+
Withdrawing: {}
|
|
32
|
+
Stopped: {}
|
|
33
|
+
}>
|
|
34
|
+
const ChildEvent = Data.taggedEnum<ChildEvent>()
|
|
35
|
+
type LogEntry = Omit<typeof Log.Type, "cause"> & {
|
|
36
|
+
readonly cause: Cause.Cause<unknown>
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** Queue data; serialization belongs to the RPC consumer, outside synchronous logger callbacks. */
|
|
40
|
+
export const telemetry = (contract: RunnerContract) =>
|
|
41
|
+
Effect.gen(function* () {
|
|
42
|
+
const events = yield* Queue.bounded<ChildEvent>(2)
|
|
43
|
+
const logs = yield* Queue.dropping<LogEntry, Cause.Done>(128)
|
|
44
|
+
const dropped = MutableRef.make(0)
|
|
45
|
+
const encode = (cause: Cause.Cause<unknown>) =>
|
|
46
|
+
contract.encodeCause(cause).pipe(Effect.orDie)
|
|
47
|
+
const logger = Logger.make((entry) => {
|
|
48
|
+
// Logger callbacks are synchronous; a dropping offer and loss counter cannot suspend.
|
|
49
|
+
const accepted = Queue.offerUnsafe(logs, {
|
|
50
|
+
level: entry.logLevel,
|
|
51
|
+
message: entry.message,
|
|
52
|
+
cause: entry.cause,
|
|
53
|
+
annotations: entry.fiber.getRef(References.CurrentLogAnnotations),
|
|
54
|
+
})
|
|
55
|
+
if (!accepted) MutableRef.increment(dropped)
|
|
56
|
+
})
|
|
57
|
+
return {
|
|
58
|
+
events,
|
|
59
|
+
logs,
|
|
60
|
+
logger,
|
|
61
|
+
eventStream: Stream.fromQueue(events).pipe(
|
|
62
|
+
Stream.mapEffect((event): Effect.Effect<typeof Event.Type> =>
|
|
63
|
+
event._tag === "Failed"
|
|
64
|
+
? encode(event.cause).pipe(
|
|
65
|
+
Effect.map(
|
|
66
|
+
(cause) =>
|
|
67
|
+
({
|
|
68
|
+
_tag: "Failed",
|
|
69
|
+
cause,
|
|
70
|
+
}) satisfies typeof Event.Type,
|
|
71
|
+
),
|
|
72
|
+
)
|
|
73
|
+
: Effect.succeed(event),
|
|
74
|
+
),
|
|
75
|
+
),
|
|
76
|
+
logStream: Stream.fromQueue(logs).pipe(
|
|
77
|
+
Stream.mapEffect((entry) =>
|
|
78
|
+
encode(entry.cause).pipe(
|
|
79
|
+
Effect.map((cause): typeof LogEvent.Type => ({
|
|
80
|
+
_tag: "Log",
|
|
81
|
+
entry: { ...entry, cause },
|
|
82
|
+
dropped: MutableRef.get(dropped),
|
|
83
|
+
})),
|
|
84
|
+
),
|
|
85
|
+
),
|
|
86
|
+
Stream.concat(
|
|
87
|
+
Stream.sync((): typeof LogEvent.Type => ({
|
|
88
|
+
_tag: "LogsEnded",
|
|
89
|
+
dropped: MutableRef.get(dropped),
|
|
90
|
+
})),
|
|
91
|
+
),
|
|
92
|
+
),
|
|
93
|
+
}
|
|
94
|
+
})
|
|
95
|
+
type Telemetry = Effect.Success<ReturnType<typeof telemetry>>
|
|
96
|
+
interface Running {
|
|
97
|
+
readonly fork: Pick<Fork.Fork<never, unknown>, "dispose" | "state">
|
|
98
|
+
readonly bridge: Pick<Fork.Fork<never, never>, "dispose" | "state">
|
|
99
|
+
readonly observation: Fiber.Fiber<void>
|
|
100
|
+
readonly observedFailure: Ref.Ref<Cause.Cause<unknown>>
|
|
101
|
+
}
|
|
102
|
+
type Lifecycle = Data.TaggedEnum<{
|
|
103
|
+
Idle: {}
|
|
104
|
+
Loading: {}
|
|
105
|
+
Running: { readonly running: Running }
|
|
106
|
+
Withdrawing: { readonly running: Option.Option<Running> }
|
|
107
|
+
Disposing: { readonly running: Option.Option<Running> }
|
|
108
|
+
}>
|
|
109
|
+
const Lifecycle = Data.taggedEnum<Lifecycle>()
|
|
110
|
+
const runningOf = (state: Lifecycle): Option.Option<Running> =>
|
|
111
|
+
state._tag === "Running"
|
|
112
|
+
? Option.some(state.running)
|
|
113
|
+
: state._tag === "Withdrawing" || state._tag === "Disposing"
|
|
114
|
+
? state.running
|
|
115
|
+
: Option.none()
|
|
116
|
+
|
|
117
|
+
const cleanup = (running: Running, retired: boolean) =>
|
|
118
|
+
Effect.gen(function* () {
|
|
119
|
+
const pluginExit = yield* Effect.exit(running.fork.dispose)
|
|
120
|
+
const bridgeExit = yield* Effect.exit(running.bridge.dispose)
|
|
121
|
+
yield* Fiber.join(running.observation)
|
|
122
|
+
const failed = yield* Ref.get(running.observedFailure)
|
|
123
|
+
return Array.reduce(
|
|
124
|
+
[bridgeExit, pluginExit],
|
|
125
|
+
retired ? failed : Cause.empty,
|
|
126
|
+
(cause, exit) =>
|
|
127
|
+
Exit.isFailure(exit)
|
|
128
|
+
? Cause.combine(
|
|
129
|
+
cause,
|
|
130
|
+
Cause.fromReasons(
|
|
131
|
+
Array.flatMap(exit.cause.reasons, (reason) =>
|
|
132
|
+
reason._tag === "Fail"
|
|
133
|
+
? reason.error.cause.reasons
|
|
134
|
+
: [reason],
|
|
135
|
+
),
|
|
136
|
+
),
|
|
137
|
+
)
|
|
138
|
+
: cause,
|
|
139
|
+
)
|
|
140
|
+
})
|
|
141
|
+
|
|
142
|
+
export const lifecycle = (
|
|
143
|
+
contract: RunnerContract,
|
|
144
|
+
peer: Peer,
|
|
145
|
+
identity: typeof Identity.Type,
|
|
146
|
+
telemetry: Telemetry,
|
|
147
|
+
) =>
|
|
148
|
+
Effect.gen(function* () {
|
|
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 release = SynchronizedRef.update(state, (current) =>
|
|
155
|
+
Lifecycle.Disposing({ running: runningOf(current) }),
|
|
156
|
+
).pipe(Effect.andThen(Deferred.succeed(releaseExports, undefined)))
|
|
157
|
+
|
|
158
|
+
const exportBridge = Graft.plugin(
|
|
159
|
+
Plugin.make({
|
|
160
|
+
name: `${contract.name}/exports`,
|
|
161
|
+
inject: Array.map(contract.provides, (service) => service.key),
|
|
162
|
+
apply: () =>
|
|
163
|
+
Effect.gen(function* () {
|
|
164
|
+
for (const service of contract.provides)
|
|
165
|
+
yield* service.serve(peer).pipe(
|
|
166
|
+
Effect.tapCause((cause) =>
|
|
167
|
+
Cause.hasInterruptsOnly(cause)
|
|
168
|
+
? Effect.void
|
|
169
|
+
: Queue.offer(
|
|
170
|
+
telemetry.events,
|
|
171
|
+
ChildEvent.Failed({ cause }),
|
|
172
|
+
),
|
|
173
|
+
),
|
|
174
|
+
Effect.forkScoped,
|
|
175
|
+
)
|
|
176
|
+
yield* Effect.addFinalizer(() =>
|
|
177
|
+
Effect.gen(function* () {
|
|
178
|
+
const withdrawing = yield* SynchronizedRef.modify(
|
|
179
|
+
state,
|
|
180
|
+
(current) =>
|
|
181
|
+
current._tag === "Disposing"
|
|
182
|
+
? [false, current]
|
|
183
|
+
: [
|
|
184
|
+
true,
|
|
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
|
+
}),
|
|
194
|
+
)
|
|
195
|
+
}),
|
|
196
|
+
}),
|
|
197
|
+
).pipe(Scope.provide(instanceScope))
|
|
198
|
+
|
|
199
|
+
const start = (input: Schema.Json) =>
|
|
200
|
+
Effect.gen(function* () {
|
|
201
|
+
// Imported clients outlive Load and remain available during plugin cleanup.
|
|
202
|
+
for (const service of contract.inject)
|
|
203
|
+
yield* service.publish(peer).pipe(
|
|
204
|
+
Scope.provide(scope),
|
|
205
|
+
Effect.mapError((error) =>
|
|
206
|
+
ActivationFailed.make({
|
|
207
|
+
detail: "Publishing imported services failed",
|
|
208
|
+
cause: Cause.fail(error),
|
|
209
|
+
}),
|
|
210
|
+
),
|
|
211
|
+
)
|
|
212
|
+
const module = yield* loadModule(contract.module)
|
|
213
|
+
const plugin = Plugin.fromModule(module)
|
|
214
|
+
if (Option.isNone(plugin))
|
|
215
|
+
return yield* ActivationFailed.make({
|
|
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
|
|
238
|
+
})
|
|
239
|
+
const awaitActive = (running: Running) =>
|
|
240
|
+
Effect.gen(function* () {
|
|
241
|
+
const first = yield* running.fork.state.pipe(
|
|
242
|
+
Stream.filter((state) => state._tag !== "Loading"),
|
|
243
|
+
Stream.runHead,
|
|
244
|
+
)
|
|
245
|
+
if (Option.isNone(first))
|
|
246
|
+
return yield* ActivationFailed.make({
|
|
247
|
+
detail: "The remote fork disappeared during startup",
|
|
248
|
+
})
|
|
249
|
+
if (first.value._tag === "Failed")
|
|
250
|
+
return yield* Effect.fail(contract.normalizeCause(first.value.cause))
|
|
251
|
+
if (first.value._tag !== "Active")
|
|
252
|
+
return yield* ActivationFailed.make({
|
|
253
|
+
detail:
|
|
254
|
+
"The remote plugin did not become active; check its declared dependencies",
|
|
255
|
+
})
|
|
256
|
+
yield* running.fork.state.pipe(
|
|
257
|
+
Stream.runForEach((state) =>
|
|
258
|
+
state._tag === "Failed"
|
|
259
|
+
? Queue.offer(
|
|
260
|
+
telemetry.events,
|
|
261
|
+
ChildEvent.Failed({ cause: state.cause }),
|
|
262
|
+
)
|
|
263
|
+
: Effect.void,
|
|
264
|
+
),
|
|
265
|
+
Effect.forkIn(instanceScope),
|
|
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
|
+
})
|
|
285
|
+
})
|
|
286
|
+
const load = (input: Schema.Json) =>
|
|
287
|
+
Effect.gen(function* () {
|
|
288
|
+
yield* SynchronizedRef.updateEffect(state, (current) =>
|
|
289
|
+
current._tag === "Idle"
|
|
290
|
+
? Effect.succeed(Lifecycle.Loading())
|
|
291
|
+
: Effect.fail(AlreadyLoaded.make({})),
|
|
292
|
+
)
|
|
293
|
+
const running = yield* start(input)
|
|
294
|
+
yield* SynchronizedRef.update(state, (current) =>
|
|
295
|
+
current._tag === "Withdrawing"
|
|
296
|
+
? Lifecycle.Withdrawing({ running: Option.some(running) })
|
|
297
|
+
: 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
|
+
),
|
|
308
|
+
)
|
|
309
|
+
const dispose = Effect.gen(function* () {
|
|
310
|
+
const previous = yield* SynchronizedRef.get(state)
|
|
311
|
+
yield* release
|
|
312
|
+
const current = runningOf(previous)
|
|
313
|
+
const retired = previous._tag === "Withdrawing"
|
|
314
|
+
const cause = Option.isSome(current)
|
|
315
|
+
? yield* cleanup(current.value, retired)
|
|
316
|
+
: Cause.empty
|
|
317
|
+
yield* Queue.offer(telemetry.events, ChildEvent.Stopped())
|
|
318
|
+
yield* Queue.end(telemetry.logs)
|
|
319
|
+
if (cause.reasons.length > 0)
|
|
320
|
+
return yield* Effect.fail(contract.normalizeCause(cause))
|
|
321
|
+
})
|
|
322
|
+
return { load, dispose, release }
|
|
323
|
+
})
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { Schema } from "effect"
|
|
2
|
+
|
|
3
|
+
export class AlreadyLoaded extends Schema.TaggedError<
|
|
4
|
+
AlreadyLoaded,
|
|
5
|
+
{ readonly brand: unique symbol }
|
|
6
|
+
>()("AlreadyLoaded", {}) {}
|
|
7
|
+
export class ContractNotFound extends Schema.TaggedError<
|
|
8
|
+
ContractNotFound,
|
|
9
|
+
{ readonly brand: unique symbol }
|
|
10
|
+
>()("ContractNotFound", { module: Schema.String }) {
|
|
11
|
+
override get message() {
|
|
12
|
+
return `No remote contract exported by ${this.module}`
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
export class ModuleImportFailed extends Schema.TaggedError<
|
|
16
|
+
ModuleImportFailed,
|
|
17
|
+
{ readonly brand: unique symbol }
|
|
18
|
+
>()("ModuleImportFailed", {
|
|
19
|
+
module: Schema.String,
|
|
20
|
+
cause: Schema.Defect({ includeStack: true }),
|
|
21
|
+
}) {}
|
|
22
|
+
export class ActivationFailed extends Schema.TaggedError<
|
|
23
|
+
ActivationFailed,
|
|
24
|
+
{ readonly brand: unique symbol }
|
|
25
|
+
>()("ActivationFailed", {
|
|
26
|
+
detail: Schema.String,
|
|
27
|
+
cause: Schema.optionalKey(
|
|
28
|
+
Schema.Cause(
|
|
29
|
+
Schema.Defect({ includeStack: true }),
|
|
30
|
+
Schema.Defect({ includeStack: true }),
|
|
31
|
+
),
|
|
32
|
+
),
|
|
33
|
+
}) {
|
|
34
|
+
override get message() {
|
|
35
|
+
return this.detail
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
export const ChildFailure = Schema.Union([
|
|
39
|
+
AlreadyLoaded,
|
|
40
|
+
ContractNotFound,
|
|
41
|
+
ModuleImportFailed,
|
|
42
|
+
ActivationFailed,
|
|
43
|
+
])
|
|
44
|
+
export type ChildFailure = typeof ChildFailure.Type
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { loadModule } from "./loadModule"
|
|
2
|
+
import { ContractNotFound } from "./childErrors"
|
|
3
|
+
import type { Graft } from "@graftland/graft"
|
|
4
|
+
import { Effect, Logger, Option } from "effect"
|
|
5
|
+
import { RpcServer } from "effect/rpc"
|
|
6
|
+
import { fromModule, type RunnerContract } from "../Contract"
|
|
7
|
+
import * as Port from "./port"
|
|
8
|
+
import * as Services from "./services"
|
|
9
|
+
import * as Child from "./child"
|
|
10
|
+
import * as Multiplex from "./runner/Multiplex"
|
|
11
|
+
import * as Wire from "./runner/Wire"
|
|
12
|
+
import type { Bootstrap } from "./runner/Bootstrap"
|
|
13
|
+
import { ExecutionError } from "./runner/model"
|
|
14
|
+
import { closedNormally } from "./runner/connectionEnded"
|
|
15
|
+
import { endpoint } from "./control"
|
|
16
|
+
|
|
17
|
+
/** Import a contract in the runner installation; importing this module performs no I/O. */
|
|
18
|
+
export const load = (specifier: string) =>
|
|
19
|
+
Effect.gen(function* () {
|
|
20
|
+
const module = yield* loadModule(specifier)
|
|
21
|
+
const contract = fromModule(module)
|
|
22
|
+
if (Option.isNone(contract))
|
|
23
|
+
return yield* ContractNotFound.make({ module: specifier })
|
|
24
|
+
return contract.value
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
const connect = (
|
|
28
|
+
contract: RunnerContract,
|
|
29
|
+
channel: Port.Channel,
|
|
30
|
+
session: string,
|
|
31
|
+
bootstrap: Bootstrap | undefined,
|
|
32
|
+
) =>
|
|
33
|
+
Effect.gen(function* () {
|
|
34
|
+
const mux =
|
|
35
|
+
bootstrap === undefined ? undefined : yield* Multiplex.make(channel)
|
|
36
|
+
return yield* Port.make(
|
|
37
|
+
mux === undefined ? channel : Wire.channel(mux, 1),
|
|
38
|
+
{
|
|
39
|
+
session,
|
|
40
|
+
serve: {
|
|
41
|
+
...Services.endpoints(contract.provides),
|
|
42
|
+
[endpoint]: contract.control,
|
|
43
|
+
},
|
|
44
|
+
call: Services.endpoints(contract.inject),
|
|
45
|
+
...(bootstrap === undefined ? {} : { limits: bootstrap.limits }),
|
|
46
|
+
},
|
|
47
|
+
)
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
/** One execution unit; the executable supplies the platform and Graft services. */
|
|
51
|
+
export const run = (
|
|
52
|
+
contract: RunnerContract,
|
|
53
|
+
channel: Port.Channel,
|
|
54
|
+
session = "local",
|
|
55
|
+
bootstrap?: Bootstrap,
|
|
56
|
+
stop: Effect.Effect<unknown, ExecutionError> = Effect.never,
|
|
57
|
+
): Effect.Effect<void, ExecutionError, Graft.Graft> =>
|
|
58
|
+
Effect.scoped(
|
|
59
|
+
Effect.gen(function* () {
|
|
60
|
+
const telemetry = yield* Child.telemetry(contract)
|
|
61
|
+
const loggers = yield* Logger.CurrentLoggers
|
|
62
|
+
yield* Effect.gen(function* () {
|
|
63
|
+
const peer = yield* connect(contract, channel, session, bootstrap)
|
|
64
|
+
const instance = yield* Child.lifecycle(
|
|
65
|
+
contract,
|
|
66
|
+
peer,
|
|
67
|
+
bootstrap?.identity ?? { id: session, generation: 1 },
|
|
68
|
+
telemetry,
|
|
69
|
+
)
|
|
70
|
+
const handlers = yield* contract.control.toHandlers({
|
|
71
|
+
Load: ({ input }) => instance.load(input),
|
|
72
|
+
Dispose: () => instance.dispose,
|
|
73
|
+
Events: () => telemetry.eventStream,
|
|
74
|
+
Logs: () => telemetry.logStream,
|
|
75
|
+
})
|
|
76
|
+
yield* RpcServer.make(contract.control, {
|
|
77
|
+
disableFatalDefects: true,
|
|
78
|
+
}).pipe(
|
|
79
|
+
Effect.provideContext(handlers),
|
|
80
|
+
Effect.provideService(
|
|
81
|
+
RpcServer.Protocol,
|
|
82
|
+
peer.serverProtocol(endpoint),
|
|
83
|
+
),
|
|
84
|
+
Effect.forkScoped,
|
|
85
|
+
)
|
|
86
|
+
yield* Effect.raceFirst(
|
|
87
|
+
peer.await.pipe(
|
|
88
|
+
Effect.catchIf(
|
|
89
|
+
(error) => closedNormally(error.cause),
|
|
90
|
+
() => Effect.void,
|
|
91
|
+
),
|
|
92
|
+
Effect.mapError((error) =>
|
|
93
|
+
ExecutionError.make({
|
|
94
|
+
detail: "Child transport failed",
|
|
95
|
+
cause: error.cause,
|
|
96
|
+
}),
|
|
97
|
+
),
|
|
98
|
+
),
|
|
99
|
+
stop,
|
|
100
|
+
).pipe(
|
|
101
|
+
// Release exports before closing the instance scope, including on interruption.
|
|
102
|
+
Effect.ensuring(instance.release),
|
|
103
|
+
)
|
|
104
|
+
}).pipe(
|
|
105
|
+
Effect.provideService(
|
|
106
|
+
Logger.CurrentLoggers,
|
|
107
|
+
new Set([...loggers, telemetry.logger]),
|
|
108
|
+
),
|
|
109
|
+
)
|
|
110
|
+
}),
|
|
111
|
+
)
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { Fork } from "@graftland/graft"
|
|
2
|
+
import { Array, Cause, Effect, Schema, Scope } from "effect"
|
|
3
|
+
import * as Release from "./release"
|
|
4
|
+
|
|
5
|
+
/** Finalizers have no typed failure channel. Keep reasons and their annotations flat. */
|
|
6
|
+
export const cleanupCause = (cause: Cause.Cause<unknown>): Cause.Cause<never> =>
|
|
7
|
+
Cause.fromReasons(
|
|
8
|
+
Array.flatMap(cause.reasons, (reason) => {
|
|
9
|
+
const error =
|
|
10
|
+
reason._tag === "Fail"
|
|
11
|
+
? reason.error
|
|
12
|
+
: reason._tag === "Die"
|
|
13
|
+
? reason.defect
|
|
14
|
+
: undefined
|
|
15
|
+
if (Schema.is(Fork.CleanupError)(error))
|
|
16
|
+
return Array.map(cleanupCause(error.cause).reasons, (inner) =>
|
|
17
|
+
inner.annotate(Cause.reasonAnnotations(reason)),
|
|
18
|
+
)
|
|
19
|
+
return [
|
|
20
|
+
reason._tag === "Fail"
|
|
21
|
+
? Cause.makeDieReason(reason.error).annotate(
|
|
22
|
+
Cause.reasonAnnotations(reason),
|
|
23
|
+
)
|
|
24
|
+
: reason,
|
|
25
|
+
]
|
|
26
|
+
}),
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
/** Assembly children close before the entry reports their cleanup as its own result. */
|
|
30
|
+
export const children = Effect.acquireRelease(Scope.make(), (scope, exit) =>
|
|
31
|
+
Release.close(scope, exit).pipe(
|
|
32
|
+
Effect.catchCause((cause) => Effect.failCause(cleanupCause(cause))),
|
|
33
|
+
),
|
|
34
|
+
)
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { Schema } from "effect"
|
|
2
|
+
import { Rpc, RpcGroup } from "effect/rpc"
|
|
3
|
+
|
|
4
|
+
import { Priority } from "./port"
|
|
5
|
+
|
|
6
|
+
export const Log = Schema.Struct({
|
|
7
|
+
level: Schema.Literals([
|
|
8
|
+
"Fatal",
|
|
9
|
+
"Error",
|
|
10
|
+
"Warn",
|
|
11
|
+
"Info",
|
|
12
|
+
"Debug",
|
|
13
|
+
"Trace",
|
|
14
|
+
"All",
|
|
15
|
+
"None",
|
|
16
|
+
]),
|
|
17
|
+
message: Schema.Defect({ includeStack: true }),
|
|
18
|
+
cause: Schema.Json,
|
|
19
|
+
annotations: Schema.Record(
|
|
20
|
+
Schema.String,
|
|
21
|
+
Schema.Defect({ includeStack: true }),
|
|
22
|
+
),
|
|
23
|
+
})
|
|
24
|
+
export const Event = Schema.Union([
|
|
25
|
+
Schema.TaggedStruct("Failed", { cause: Schema.Json }),
|
|
26
|
+
Schema.TaggedStruct("Stopped", {}),
|
|
27
|
+
Schema.TaggedStruct("Withdrawing", {}),
|
|
28
|
+
])
|
|
29
|
+
export const LogEvent = Schema.Union([
|
|
30
|
+
Schema.TaggedStruct("Log", { entry: Log, dropped: Schema.Int }),
|
|
31
|
+
Schema.TaggedStruct("LogsEnded", { dropped: Schema.Int }),
|
|
32
|
+
])
|
|
33
|
+
export const endpoint = "@graftland/plugin-remote/control"
|
|
34
|
+
export const make = <C extends Schema.Top>(cause: C) =>
|
|
35
|
+
RpcGroup.make(
|
|
36
|
+
Rpc.make("Load", {
|
|
37
|
+
payload: { input: Schema.Json },
|
|
38
|
+
error: cause,
|
|
39
|
+
}),
|
|
40
|
+
Rpc.make("Events", { success: Event, stream: true }),
|
|
41
|
+
Rpc.make("Dispose", { error: cause }),
|
|
42
|
+
)
|
|
43
|
+
.annotateRpcs(Priority, "control")
|
|
44
|
+
.add(
|
|
45
|
+
Rpc.make("Logs", { success: LogEvent, stream: true }).annotate(
|
|
46
|
+
Priority,
|
|
47
|
+
"log",
|
|
48
|
+
),
|
|
49
|
+
)
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { Graft, Plugin } from "@graftland/graft"
|
|
2
|
+
import { Cause, Context, Effect, Redacted, Scope } from "effect"
|
|
3
|
+
import { Credentials, type ClientCertificate, type Prepared } from "../Auth"
|
|
4
|
+
import { CredentialsError } from "../Errors"
|
|
5
|
+
import { prepare } from "./auth/token"
|
|
6
|
+
import type * as Configuration from "./entryConfig"
|
|
7
|
+
|
|
8
|
+
export type Source<A, D extends ReadonlyArray<Plugin.Dependency>, E = unknown> =
|
|
9
|
+
| Effect.Effect<A, E, Scope.Scope>
|
|
10
|
+
| {
|
|
11
|
+
readonly inject: D
|
|
12
|
+
readonly read: Effect.Effect<
|
|
13
|
+
A,
|
|
14
|
+
unknown,
|
|
15
|
+
NoInfer<Plugin.Required<D>> | Scope.Scope
|
|
16
|
+
>
|
|
17
|
+
}
|
|
18
|
+
type Token<D extends ReadonlyArray<Plugin.Dependency>> =
|
|
19
|
+
string | { readonly file: string } | Source<Redacted.Redacted<string>, D>
|
|
20
|
+
type Authentication<D extends ReadonlyArray<Plugin.Dependency>> =
|
|
21
|
+
| { readonly name: string; readonly config?: unknown }
|
|
22
|
+
| { readonly none: { readonly identity?: string } }
|
|
23
|
+
| {
|
|
24
|
+
readonly mtls:
|
|
25
|
+
| { readonly cert: string; readonly key: string }
|
|
26
|
+
| Source<ClientCertificate, D>
|
|
27
|
+
}
|
|
28
|
+
| { readonly bearer: { readonly token: Token<D> } }
|
|
29
|
+
| { readonly jwt: { readonly token: Token<D> } }
|
|
30
|
+
type WithCredentials<C, D extends ReadonlyArray<Plugin.Dependency>> = C extends
|
|
31
|
+
| { readonly local: object }
|
|
32
|
+
| { readonly worker: object }
|
|
33
|
+
| { readonly command: ReadonlyArray<string> }
|
|
34
|
+
? C
|
|
35
|
+
: Omit<C, "auth"> & { readonly auth?: Authentication<D> }
|
|
36
|
+
export type Connect<D extends ReadonlyArray<Plugin.Dependency> = readonly []> =
|
|
37
|
+
WithCredentials<typeof Configuration.Connect.Encoded, D>
|
|
38
|
+
|
|
39
|
+
const provider = <A, E, D extends ReadonlyArray<Plugin.Dependency>>(
|
|
40
|
+
source: Source<A, D, E>,
|
|
41
|
+
encode: (
|
|
42
|
+
read: Effect.Effect<A, CredentialsError, Scope.Scope>,
|
|
43
|
+
) => typeof Credentials.Service,
|
|
44
|
+
) => {
|
|
45
|
+
const input = Effect.isEffect(source) ? { inject: [], read: source } : source
|
|
46
|
+
return Plugin.make({
|
|
47
|
+
name: "remote-effect-credentials",
|
|
48
|
+
inject: input.inject,
|
|
49
|
+
apply: Effect.fnUntraced(function* () {
|
|
50
|
+
// Restore the provider's context without replacing each read's owning scope.
|
|
51
|
+
const context = yield* Effect.context<
|
|
52
|
+
Plugin.Required<D> | Scope.Scope
|
|
53
|
+
>().pipe(Effect.map(Context.omit(Scope.Scope)))
|
|
54
|
+
const read = input.read.pipe(
|
|
55
|
+
Effect.provideContext(context),
|
|
56
|
+
Effect.mapError((error) =>
|
|
57
|
+
CredentialsError.make({ cause: Cause.fail(error) }),
|
|
58
|
+
),
|
|
59
|
+
)
|
|
60
|
+
yield* Graft.provide(Credentials, encode(read))
|
|
61
|
+
}),
|
|
62
|
+
})
|
|
63
|
+
}
|
|
64
|
+
export const invocation = <D extends ReadonlyArray<Plugin.Dependency>>(
|
|
65
|
+
connect: Connect<D>,
|
|
66
|
+
) => {
|
|
67
|
+
const auth = connect.auth
|
|
68
|
+
if (auth === undefined) return undefined
|
|
69
|
+
if ("mtls" in auth) {
|
|
70
|
+
const source = auth.mtls
|
|
71
|
+
if (Effect.isEffect(source) || "read" in source)
|
|
72
|
+
return {
|
|
73
|
+
plugin: provider(source, (read) => ({
|
|
74
|
+
kind: "Certificate",
|
|
75
|
+
read: read.pipe(
|
|
76
|
+
Effect.map(
|
|
77
|
+
(value): Extract<Prepared, { readonly _tag: "Certificate" }> => ({
|
|
78
|
+
_tag: "Certificate",
|
|
79
|
+
value,
|
|
80
|
+
}),
|
|
81
|
+
),
|
|
82
|
+
),
|
|
83
|
+
})),
|
|
84
|
+
config: undefined,
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
if ("bearer" in auth || "jwt" in auth) {
|
|
88
|
+
const source = "bearer" in auth ? auth.bearer.token : auth.jwt.token
|
|
89
|
+
if (
|
|
90
|
+
typeof source !== "string" &&
|
|
91
|
+
(Effect.isEffect(source) || "read" in source)
|
|
92
|
+
)
|
|
93
|
+
return {
|
|
94
|
+
plugin: provider(source, (read) => ({
|
|
95
|
+
kind: "Authorization",
|
|
96
|
+
read: read.pipe(
|
|
97
|
+
Effect.flatMap(prepare),
|
|
98
|
+
Effect.map(
|
|
99
|
+
(
|
|
100
|
+
value,
|
|
101
|
+
): Extract<Prepared, { readonly _tag: "Authorization" }> => ({
|
|
102
|
+
_tag: "Authorization",
|
|
103
|
+
value,
|
|
104
|
+
}),
|
|
105
|
+
),
|
|
106
|
+
),
|
|
107
|
+
})),
|
|
108
|
+
config: undefined,
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
return undefined
|
|
112
|
+
}
|