@graftland/plugin-remote 0.0.0-stage → 0.2.0-beta.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/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
package/src/index.ts
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { Plugin } from "@graftland/graft"
|
|
2
|
+
import { ModuleResolver } from "@graftland/loader"
|
|
3
|
+
import { Array, Cause, Effect, Path, Schema } from "effect"
|
|
4
|
+
import type { Duration, Schedule } from "effect"
|
|
5
|
+
import { SecureRandom } from "effect/crypto"
|
|
6
|
+
import { ChildProcessSpawner } from "effect/process"
|
|
7
|
+
import { launch } from "./internal/entry"
|
|
8
|
+
import { follow } from "./internal/follow"
|
|
9
|
+
import { CapabilityFailed } from "./internal/assembly"
|
|
10
|
+
import { publicError } from "./internal/execution"
|
|
11
|
+
import * as Configuration from "./internal/entryConfig"
|
|
12
|
+
import * as Credential from "./internal/credentials"
|
|
13
|
+
import { RemoteWithdrawn, RemoteError, MissingInstance } from "./Errors"
|
|
14
|
+
import * as Contract from "./Contract"
|
|
15
|
+
export { Contract }
|
|
16
|
+
export { Instance } from "./Instance"
|
|
17
|
+
export { RemoteTerminationUnconfirmed } from "./internal/runner/model"
|
|
18
|
+
export { RemoteError, RemoteWithdrawn, MissingInstance } from "./Errors"
|
|
19
|
+
export { UndeclaredFailure, ChildFailure } from "./Contract"
|
|
20
|
+
export type {
|
|
21
|
+
Connect,
|
|
22
|
+
Source as CredentialSource,
|
|
23
|
+
} from "./internal/credentials"
|
|
24
|
+
|
|
25
|
+
export interface Options<
|
|
26
|
+
D extends ReadonlyArray<Plugin.Dependency> = readonly [],
|
|
27
|
+
> {
|
|
28
|
+
readonly connect: Credential.Connect<D>
|
|
29
|
+
/** Contract module resolved independently in each execution environment. */
|
|
30
|
+
readonly contractModule: string
|
|
31
|
+
readonly config?: Schema.Json
|
|
32
|
+
readonly retry?: Schedule.Schedule<unknown, unknown> | undefined
|
|
33
|
+
readonly shutdownTimeout?: Duration.Input | undefined
|
|
34
|
+
}
|
|
35
|
+
const publicCause = <E extends Schema.Top>(
|
|
36
|
+
declared: E,
|
|
37
|
+
cause: Cause.Cause<unknown>,
|
|
38
|
+
): Cause.Cause<
|
|
39
|
+
| E["Type"]
|
|
40
|
+
| Contract.UndeclaredFailure
|
|
41
|
+
| RemoteWithdrawn
|
|
42
|
+
| RemoteError
|
|
43
|
+
| MissingInstance
|
|
44
|
+
> =>
|
|
45
|
+
Cause.fromReasons(
|
|
46
|
+
Array.flatMap(cause.reasons, (reason) => {
|
|
47
|
+
if (reason._tag !== "Fail") return [reason]
|
|
48
|
+
return Schema.is(CapabilityFailed)(reason.error)
|
|
49
|
+
? Array.map(
|
|
50
|
+
publicCause(declared, reason.error.cause).reasons,
|
|
51
|
+
(inner) => inner.annotate(Cause.reasonAnnotations(reason)),
|
|
52
|
+
)
|
|
53
|
+
: [
|
|
54
|
+
Cause.makeFailReason(publicError(declared, reason.error)).annotate(
|
|
55
|
+
Cause.reasonAnnotations(reason),
|
|
56
|
+
),
|
|
57
|
+
]
|
|
58
|
+
}),
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
export const plugin = <
|
|
62
|
+
S extends Schema.Top & {
|
|
63
|
+
readonly Encoded: Schema.Json
|
|
64
|
+
readonly DecodingServices: never
|
|
65
|
+
readonly EncodingServices: never
|
|
66
|
+
},
|
|
67
|
+
E extends Schema.Top & {
|
|
68
|
+
readonly DecodingServices: never
|
|
69
|
+
readonly EncodingServices: never
|
|
70
|
+
},
|
|
71
|
+
const D extends ReadonlyArray<Plugin.Dependency> = readonly [],
|
|
72
|
+
>(
|
|
73
|
+
contract: Contract.Contract<S, E>,
|
|
74
|
+
options: Options<D> & { readonly config?: S["Encoded"] },
|
|
75
|
+
): Plugin.Plugin<
|
|
76
|
+
void,
|
|
77
|
+
void,
|
|
78
|
+
| E["Type"]
|
|
79
|
+
| Contract.UndeclaredFailure
|
|
80
|
+
| RemoteError
|
|
81
|
+
| RemoteWithdrawn
|
|
82
|
+
| MissingInstance
|
|
83
|
+
> =>
|
|
84
|
+
Plugin.make({
|
|
85
|
+
name: contract.name,
|
|
86
|
+
inject: [
|
|
87
|
+
Plugin.optional(ModuleResolver.ModuleResolver),
|
|
88
|
+
Path.Path,
|
|
89
|
+
SecureRandom.SecureRandom,
|
|
90
|
+
ChildProcessSpawner.ChildProcessSpawner,
|
|
91
|
+
],
|
|
92
|
+
retry: options.retry,
|
|
93
|
+
apply: () =>
|
|
94
|
+
Effect.gen(function* () {
|
|
95
|
+
const input = yield* Schema.decodeUnknownEffect(Configuration.Host)({
|
|
96
|
+
contract: options.contractModule,
|
|
97
|
+
connect: options.connect,
|
|
98
|
+
config: options.config === undefined ? {} : options.config,
|
|
99
|
+
})
|
|
100
|
+
yield* Schema.decodeEffect(contract.config)(input.config)
|
|
101
|
+
const child = yield* launch(
|
|
102
|
+
contract,
|
|
103
|
+
input,
|
|
104
|
+
{
|
|
105
|
+
contractModule: options.contractModule,
|
|
106
|
+
shutdownTimeout: options.shutdownTimeout,
|
|
107
|
+
},
|
|
108
|
+
Credential.invocation(options.connect),
|
|
109
|
+
)
|
|
110
|
+
yield* follow(child, (cause) =>
|
|
111
|
+
Effect.failCause(publicCause(contract.error, cause)),
|
|
112
|
+
)
|
|
113
|
+
}).pipe(Effect.mapError((error) => publicError(contract.error, error))),
|
|
114
|
+
})
|
|
115
|
+
export { host as default } from "./internal/entry"
|
|
@@ -0,0 +1,330 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Array,
|
|
3
|
+
DateTime,
|
|
4
|
+
Effect,
|
|
5
|
+
FileSystem,
|
|
6
|
+
Path,
|
|
7
|
+
Redacted,
|
|
8
|
+
Result,
|
|
9
|
+
Schema,
|
|
10
|
+
String,
|
|
11
|
+
} from "effect"
|
|
12
|
+
import { SecureRandom, WebCrypto } from "effect/crypto"
|
|
13
|
+
import { Base64 } from "effect/encoding"
|
|
14
|
+
import { NetAddress } from "effect/net"
|
|
15
|
+
import * as asn1js from "asn1js"
|
|
16
|
+
import * as pki from "pkijs"
|
|
17
|
+
|
|
18
|
+
export class CertificateError extends Schema.TaggedError<
|
|
19
|
+
CertificateError,
|
|
20
|
+
{ readonly brand: unique symbol }
|
|
21
|
+
>()("CertificateError", {
|
|
22
|
+
detail: Schema.String,
|
|
23
|
+
cause: Schema.optional(Schema.Defect()),
|
|
24
|
+
}) {
|
|
25
|
+
override get message() {
|
|
26
|
+
return this.detail
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/** A requested output already exists and replacing it was not asked for. */
|
|
31
|
+
export class CertificateExists extends Schema.TaggedError<
|
|
32
|
+
CertificateExists,
|
|
33
|
+
{ readonly brand: unique symbol }
|
|
34
|
+
>()("CertificateExists", { file: Schema.String }) {
|
|
35
|
+
override get message() {
|
|
36
|
+
return `${this.file} already exists; use --force to replace the requested certificates`
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** Only PEM leaves this boundary; PKI.js objects stay local to each operation. */
|
|
41
|
+
export interface Pem {
|
|
42
|
+
readonly certificate: string
|
|
43
|
+
readonly privateKey: Redacted.Redacted<string>
|
|
44
|
+
}
|
|
45
|
+
export interface IssueOptions {
|
|
46
|
+
readonly name: string
|
|
47
|
+
readonly days: number
|
|
48
|
+
readonly usage: "ca" | "server" | "client"
|
|
49
|
+
readonly hosts?: ReadonlyArray<string>
|
|
50
|
+
readonly identities?: ReadonlyArray<string>
|
|
51
|
+
readonly issuer?: Pem
|
|
52
|
+
}
|
|
53
|
+
const pem = (label: string, bytes: ArrayBuffer) =>
|
|
54
|
+
`-----BEGIN ${label}-----\n${String.replace(/.{64}(?=.)/g, "$&\n")(Base64.encode(new Uint8Array(bytes)))}\n-----END ${label}-----\n`
|
|
55
|
+
// WebCrypto and PKI.js take ArrayBuffer-backed bytes, so the decoded view is copied into one.
|
|
56
|
+
const der = (value: string) =>
|
|
57
|
+
Effect.fromResult(
|
|
58
|
+
Base64.decode(String.replace(/-----[^-]+-----|\s/g, "")(value)),
|
|
59
|
+
).pipe(Effect.map((bytes) => new Uint8Array(bytes)))
|
|
60
|
+
const subject = (name: string) =>
|
|
61
|
+
new pki.RelativeDistinguishedNames({
|
|
62
|
+
typesAndValues: [
|
|
63
|
+
new pki.AttributeTypeAndValue({
|
|
64
|
+
type: "2.5.4.3",
|
|
65
|
+
value: new asn1js.Utf8String({ value: name }),
|
|
66
|
+
}),
|
|
67
|
+
],
|
|
68
|
+
})
|
|
69
|
+
const hostName = (host: string) =>
|
|
70
|
+
Result.match(NetAddress.ipFromString(host), {
|
|
71
|
+
onFailure: () => new pki.GeneralName({ type: 2, value: host }),
|
|
72
|
+
onSuccess: (ip) =>
|
|
73
|
+
new pki.GeneralName({
|
|
74
|
+
type: 7,
|
|
75
|
+
value: new asn1js.OctetString({
|
|
76
|
+
valueHex: new Uint8Array(
|
|
77
|
+
ip._tag === "Ipv4Address"
|
|
78
|
+
? NetAddress.ipv4ToOctets(ip)
|
|
79
|
+
: NetAddress.ipv6ToOctets(ip),
|
|
80
|
+
),
|
|
81
|
+
}),
|
|
82
|
+
}),
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
export const issue = Effect.fn("Certificates.issue")(function* (
|
|
86
|
+
options: IssueOptions,
|
|
87
|
+
) {
|
|
88
|
+
const random = yield* SecureRandom.SecureRandom
|
|
89
|
+
const serial = yield* random.randomBytes(19)
|
|
90
|
+
const now = yield* DateTime.now
|
|
91
|
+
const webcrypto = yield* WebCrypto.WebCrypto
|
|
92
|
+
const engine = new pki.CryptoEngine({
|
|
93
|
+
name: "Node WebCrypto",
|
|
94
|
+
crypto: webcrypto,
|
|
95
|
+
})
|
|
96
|
+
const failure = (cause: unknown) =>
|
|
97
|
+
CertificateError.make({
|
|
98
|
+
detail: `Could not issue the certificate for ${options.name}`,
|
|
99
|
+
cause,
|
|
100
|
+
})
|
|
101
|
+
const keys = yield* Effect.tryPromise({
|
|
102
|
+
try: () =>
|
|
103
|
+
webcrypto.subtle.generateKey(
|
|
104
|
+
{ name: "ECDSA", namedCurve: "P-256" },
|
|
105
|
+
true,
|
|
106
|
+
["sign", "verify"],
|
|
107
|
+
),
|
|
108
|
+
catch: failure,
|
|
109
|
+
})
|
|
110
|
+
// Reused CA keys have the same P-256 / PKCS#8 format as the ones this command creates.
|
|
111
|
+
const authority =
|
|
112
|
+
options.issuer === undefined
|
|
113
|
+
? undefined
|
|
114
|
+
: {
|
|
115
|
+
certificate: yield* Effect.mapError(
|
|
116
|
+
der(options.issuer.certificate),
|
|
117
|
+
failure,
|
|
118
|
+
),
|
|
119
|
+
privateKey: yield* Effect.mapError(
|
|
120
|
+
der(Redacted.value(options.issuer.privateKey)),
|
|
121
|
+
failure,
|
|
122
|
+
),
|
|
123
|
+
}
|
|
124
|
+
const signingKey =
|
|
125
|
+
authority === undefined
|
|
126
|
+
? keys.privateKey
|
|
127
|
+
: yield* Effect.tryPromise({
|
|
128
|
+
try: () =>
|
|
129
|
+
webcrypto.subtle.importKey(
|
|
130
|
+
"pkcs8",
|
|
131
|
+
authority.privateKey,
|
|
132
|
+
{ name: "ECDSA", namedCurve: "P-256" },
|
|
133
|
+
false,
|
|
134
|
+
["sign"],
|
|
135
|
+
),
|
|
136
|
+
catch: failure,
|
|
137
|
+
})
|
|
138
|
+
const certificate = yield* Effect.try({
|
|
139
|
+
try: () => {
|
|
140
|
+
const issuer =
|
|
141
|
+
authority === undefined
|
|
142
|
+
? undefined
|
|
143
|
+
: pki.Certificate.fromBER(authority.certificate)
|
|
144
|
+
const names = [
|
|
145
|
+
...Array.map(options.hosts ?? [], hostName),
|
|
146
|
+
...Array.map(
|
|
147
|
+
options.identities ?? [],
|
|
148
|
+
(identity) =>
|
|
149
|
+
new pki.GeneralName({ type: 6, value: `graft://${identity}` }),
|
|
150
|
+
),
|
|
151
|
+
]
|
|
152
|
+
return new pki.Certificate({
|
|
153
|
+
version: 2,
|
|
154
|
+
// A leading nonzero byte keeps the serial positive without losing random bits.
|
|
155
|
+
serialNumber: new asn1js.Integer({
|
|
156
|
+
valueHex: new Uint8Array([1, ...serial]),
|
|
157
|
+
}),
|
|
158
|
+
subject: subject(options.name),
|
|
159
|
+
issuer: issuer?.subject ?? subject(options.name),
|
|
160
|
+
notBefore: new pki.Time({ value: DateTime.toDateUtc(now) }),
|
|
161
|
+
notAfter: new pki.Time({
|
|
162
|
+
value: DateTime.toDateUtc(DateTime.add(now, { days: options.days })),
|
|
163
|
+
}),
|
|
164
|
+
extensions: [
|
|
165
|
+
new pki.Extension({
|
|
166
|
+
extnID: "2.5.29.19",
|
|
167
|
+
critical: true,
|
|
168
|
+
extnValue: new pki.BasicConstraints({ cA: options.usage === "ca" })
|
|
169
|
+
.toSchema()
|
|
170
|
+
.toBER(),
|
|
171
|
+
}),
|
|
172
|
+
new pki.Extension({
|
|
173
|
+
extnID: "2.5.29.15",
|
|
174
|
+
critical: true,
|
|
175
|
+
extnValue: new asn1js.BitString({
|
|
176
|
+
valueHex: new Uint8Array([options.usage === "ca" ? 0x06 : 0x80]),
|
|
177
|
+
}).toBER(),
|
|
178
|
+
}),
|
|
179
|
+
...(options.usage === "ca"
|
|
180
|
+
? []
|
|
181
|
+
: [
|
|
182
|
+
new pki.Extension({
|
|
183
|
+
extnID: "2.5.29.37",
|
|
184
|
+
extnValue: new pki.ExtKeyUsage({
|
|
185
|
+
keyPurposes: [
|
|
186
|
+
options.usage === "server"
|
|
187
|
+
? "1.3.6.1.5.5.7.3.1"
|
|
188
|
+
: "1.3.6.1.5.5.7.3.2",
|
|
189
|
+
],
|
|
190
|
+
})
|
|
191
|
+
.toSchema()
|
|
192
|
+
.toBER(),
|
|
193
|
+
}),
|
|
194
|
+
]),
|
|
195
|
+
...(names.length === 0
|
|
196
|
+
? []
|
|
197
|
+
: [
|
|
198
|
+
new pki.Extension({
|
|
199
|
+
extnID: "2.5.29.17",
|
|
200
|
+
extnValue: new pki.AltName({ altNames: names })
|
|
201
|
+
.toSchema()
|
|
202
|
+
.toBER(),
|
|
203
|
+
}),
|
|
204
|
+
]),
|
|
205
|
+
],
|
|
206
|
+
})
|
|
207
|
+
},
|
|
208
|
+
catch: failure,
|
|
209
|
+
})
|
|
210
|
+
yield* Effect.tryPromise({
|
|
211
|
+
try: () =>
|
|
212
|
+
certificate.subjectPublicKeyInfo.importKey(keys.publicKey, engine),
|
|
213
|
+
catch: failure,
|
|
214
|
+
})
|
|
215
|
+
yield* Effect.tryPromise({
|
|
216
|
+
try: () => certificate.sign(signingKey, "SHA-256", engine),
|
|
217
|
+
catch: failure,
|
|
218
|
+
})
|
|
219
|
+
const privateKey = yield* Effect.tryPromise({
|
|
220
|
+
try: () => webcrypto.subtle.exportKey("pkcs8", keys.privateKey),
|
|
221
|
+
catch: failure,
|
|
222
|
+
})
|
|
223
|
+
return yield* Effect.try({
|
|
224
|
+
try: () => ({
|
|
225
|
+
certificate: pem("CERTIFICATE", certificate.toSchema().toBER()),
|
|
226
|
+
privateKey: Redacted.make(pem("PRIVATE KEY", privateKey)),
|
|
227
|
+
}),
|
|
228
|
+
catch: failure,
|
|
229
|
+
})
|
|
230
|
+
})
|
|
231
|
+
|
|
232
|
+
export const read = Effect.fn("Certificates.read")(function* (base: string) {
|
|
233
|
+
const fs = yield* FileSystem.FileSystem
|
|
234
|
+
const certificate = yield* fs.readFileString(base + ".crt")
|
|
235
|
+
const privateKey = yield* fs.readFileString(base + ".key")
|
|
236
|
+
return { certificate, privateKey: Redacted.make(privateKey) }
|
|
237
|
+
})
|
|
238
|
+
export const write = Effect.fn("Certificates.write")(function* (
|
|
239
|
+
base: string,
|
|
240
|
+
pair: Pem,
|
|
241
|
+
force: boolean = false,
|
|
242
|
+
) {
|
|
243
|
+
const fs = yield* FileSystem.FileSystem
|
|
244
|
+
yield* fs
|
|
245
|
+
.writeFileString(base + ".key", Redacted.value(pair.privateKey), {
|
|
246
|
+
flag: force ? "w" : "wx",
|
|
247
|
+
mode: 0o600,
|
|
248
|
+
})
|
|
249
|
+
.pipe(Effect.uninterruptible)
|
|
250
|
+
// Opening an existing file does not change its permissions when renewing it.
|
|
251
|
+
yield* fs.chmod(base + ".key", 0o600).pipe(Effect.uninterruptible)
|
|
252
|
+
yield* fs
|
|
253
|
+
.writeFileString(base + ".crt", pair.certificate, {
|
|
254
|
+
flag: force ? "w" : "wx",
|
|
255
|
+
})
|
|
256
|
+
.pipe(Effect.uninterruptible)
|
|
257
|
+
})
|
|
258
|
+
|
|
259
|
+
export interface Options {
|
|
260
|
+
readonly out: string
|
|
261
|
+
readonly server: ReadonlyArray<string>
|
|
262
|
+
readonly identity: ReadonlyArray<string>
|
|
263
|
+
readonly days: number
|
|
264
|
+
readonly force: boolean
|
|
265
|
+
}
|
|
266
|
+
/** URI-encode names so each identity has a distinct filename, including names with slashes. */
|
|
267
|
+
export const clientName = (identity: string) =>
|
|
268
|
+
`client-${encodeURIComponent(identity)}`
|
|
269
|
+
export const generate = Effect.fn("Certificates.generate")(function* (
|
|
270
|
+
options: Options,
|
|
271
|
+
) {
|
|
272
|
+
const fs = yield* FileSystem.FileSystem
|
|
273
|
+
const path = yield* Path.Path
|
|
274
|
+
const outputs = [
|
|
275
|
+
...(options.server.length === 0 ? [] : ["server"]),
|
|
276
|
+
...Array.map(Array.dedupe(options.identity), clientName),
|
|
277
|
+
]
|
|
278
|
+
if (!options.force) {
|
|
279
|
+
const files = Array.flatMap(outputs, (name) =>
|
|
280
|
+
Array.map([".crt", ".key"], (extension) =>
|
|
281
|
+
path.join(options.out, name + extension),
|
|
282
|
+
),
|
|
283
|
+
)
|
|
284
|
+
const taken = yield* Effect.filter(files, fs.exists)
|
|
285
|
+
if (Array.isReadonlyArrayNonEmpty(taken))
|
|
286
|
+
return yield* CertificateExists.make({ file: taken[0] })
|
|
287
|
+
}
|
|
288
|
+
yield* fs
|
|
289
|
+
.makeDirectory(options.out, { recursive: true })
|
|
290
|
+
.pipe(Effect.uninterruptible)
|
|
291
|
+
const ca = path.join(options.out, "ca")
|
|
292
|
+
const existing =
|
|
293
|
+
(yield* fs.exists(ca + ".crt")) || (yield* fs.exists(ca + ".key"))
|
|
294
|
+
// An incomplete CA pair fails on read. Even --force never replaces the signing authority.
|
|
295
|
+
const issuer = existing
|
|
296
|
+
? yield* read(ca)
|
|
297
|
+
: yield* issue({ name: "Graft CA", usage: "ca", days: options.days }).pipe(
|
|
298
|
+
Effect.tap((pair) => write(ca, pair)),
|
|
299
|
+
)
|
|
300
|
+
if (options.server.length > 0) {
|
|
301
|
+
const pair = yield* issue({
|
|
302
|
+
name: "Graft runner",
|
|
303
|
+
usage: "server",
|
|
304
|
+
hosts: options.server,
|
|
305
|
+
days: options.days,
|
|
306
|
+
issuer,
|
|
307
|
+
})
|
|
308
|
+
yield* write(path.join(options.out, "server"), pair, options.force)
|
|
309
|
+
}
|
|
310
|
+
yield* Effect.forEach(
|
|
311
|
+
Array.dedupe(options.identity),
|
|
312
|
+
(identity) =>
|
|
313
|
+
issue({
|
|
314
|
+
name: identity,
|
|
315
|
+
usage: "client",
|
|
316
|
+
identities: [identity],
|
|
317
|
+
days: options.days,
|
|
318
|
+
issuer,
|
|
319
|
+
}).pipe(
|
|
320
|
+
Effect.flatMap((pair) =>
|
|
321
|
+
write(
|
|
322
|
+
path.join(options.out, clientName(identity)),
|
|
323
|
+
pair,
|
|
324
|
+
options.force,
|
|
325
|
+
),
|
|
326
|
+
),
|
|
327
|
+
),
|
|
328
|
+
{ discard: true },
|
|
329
|
+
)
|
|
330
|
+
})
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Array,
|
|
3
|
+
Cause,
|
|
4
|
+
Fiber,
|
|
5
|
+
Data,
|
|
6
|
+
Deferred,
|
|
7
|
+
Effect,
|
|
8
|
+
Exit,
|
|
9
|
+
FiberSet,
|
|
10
|
+
Ref,
|
|
11
|
+
Scope,
|
|
12
|
+
Schema,
|
|
13
|
+
TxRef,
|
|
14
|
+
} from "effect"
|
|
15
|
+
import * as Release from "./release"
|
|
16
|
+
|
|
17
|
+
type State<E> = Data.TaggedEnum<{
|
|
18
|
+
Open: {}
|
|
19
|
+
Failed: { readonly cause: Cause.Cause<E> }
|
|
20
|
+
Closed: {}
|
|
21
|
+
}>
|
|
22
|
+
interface StateDefinition extends Data.TaggedEnum.WithGenerics<1> {
|
|
23
|
+
readonly taggedEnum: State<this["A"]>
|
|
24
|
+
}
|
|
25
|
+
const State = Data.taggedEnum<StateDefinition>()
|
|
26
|
+
export interface FailureGroup<E> {
|
|
27
|
+
readonly await: Effect.Effect<never, E>
|
|
28
|
+
readonly check: Effect.Effect<void, E>
|
|
29
|
+
readonly cause: Effect.Effect<Cause.Cause<E>>
|
|
30
|
+
readonly run: <A, R>(
|
|
31
|
+
effect: Effect.Effect<A, E, R>,
|
|
32
|
+
) => Effect.Effect<Fiber.Fiber<A, E>, never, R>
|
|
33
|
+
readonly fail: (error: E) => Effect.Effect<void>
|
|
34
|
+
readonly failCause: (cause: Cause.Cause<E>) => Effect.Effect<void>
|
|
35
|
+
readonly race: <A, E2, R>(
|
|
36
|
+
effect: Effect.Effect<A, E2, R>,
|
|
37
|
+
) => Effect.Effect<A, E | E2, R>
|
|
38
|
+
readonly runtime: <R>() => Effect.Effect<
|
|
39
|
+
(effect: Effect.Effect<unknown, E, R>) => Fiber.Fiber<unknown, E>,
|
|
40
|
+
never,
|
|
41
|
+
R
|
|
42
|
+
>
|
|
43
|
+
readonly close: Effect.Effect<void, E>
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** One owner for first failure, transaction wakeups, and interruption of sibling work. */
|
|
47
|
+
export const make = <E>(): Effect.Effect<FailureGroup<E>, never, Scope.Scope> =>
|
|
48
|
+
Effect.gen(function* () {
|
|
49
|
+
const fibers = yield* FiberSet.make<unknown, E>()
|
|
50
|
+
const state = yield* TxRef.make<State<E>>(State.Open())
|
|
51
|
+
const failures = yield* Ref.make<Cause.Cause<E>>(Cause.empty)
|
|
52
|
+
const cleanup = yield* Ref.make<Cause.Cause<E>>(Cause.empty)
|
|
53
|
+
const check = TxRef.get(state).pipe(
|
|
54
|
+
Effect.flatMap((s) =>
|
|
55
|
+
s._tag === "Failed"
|
|
56
|
+
? Effect.failCause(s.cause)
|
|
57
|
+
: s._tag === "Closed"
|
|
58
|
+
? Effect.interrupt
|
|
59
|
+
: Effect.void,
|
|
60
|
+
),
|
|
61
|
+
)
|
|
62
|
+
const publish = (cause: Cause.Cause<E>) =>
|
|
63
|
+
TxRef.update(state, (s): State<E> =>
|
|
64
|
+
s._tag === "Open" ? State.Failed({ cause }) : s,
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
const observe = <A, R>(effect: Effect.Effect<A, E, R>) =>
|
|
68
|
+
Effect.uninterruptibleMask((restore) =>
|
|
69
|
+
Effect.gen(function* () {
|
|
70
|
+
const scope = yield* Scope.make()
|
|
71
|
+
// The owned child makes cancellation explicit, even when a stream closes an internal scope.
|
|
72
|
+
const ownerId = yield* Effect.withFiber((self) =>
|
|
73
|
+
Effect.succeed(self.id),
|
|
74
|
+
)
|
|
75
|
+
const worker = yield* Effect.forkIn(
|
|
76
|
+
restore(Scope.provide(scope)(effect)),
|
|
77
|
+
scope,
|
|
78
|
+
)
|
|
79
|
+
const waiting = yield* Effect.exit(restore(Fiber.await(worker)))
|
|
80
|
+
if (Exit.isFailure(waiting)) yield* Fiber.interrupt(worker)
|
|
81
|
+
const body = yield* Fiber.await(worker)
|
|
82
|
+
const closed = yield* Effect.exit(Release.close(scope, body))
|
|
83
|
+
// Forwarding this exact cancellation from a finalizer is indistinguishable from acknowledging it.
|
|
84
|
+
const expected =
|
|
85
|
+
Exit.isFailure(waiting) &&
|
|
86
|
+
Exit.isFailure(body) &&
|
|
87
|
+
Array.every(
|
|
88
|
+
body.cause.reasons,
|
|
89
|
+
(reason) =>
|
|
90
|
+
reason._tag === "Interrupt" && reason.fiberId === ownerId,
|
|
91
|
+
)
|
|
92
|
+
const bodyCause =
|
|
93
|
+
Exit.isFailure(body) && !expected ? body.cause : Cause.empty
|
|
94
|
+
// Release.close marks independent interruptions; an unmarked one is pending cancellation.
|
|
95
|
+
const releaseCause =
|
|
96
|
+
Exit.isFailure(closed) && !Cause.hasInterruptsOnly(closed.cause)
|
|
97
|
+
? closed.cause
|
|
98
|
+
: Cause.empty
|
|
99
|
+
const failure = Cause.combine(bodyCause, releaseCause)
|
|
100
|
+
// Body cleanup may carry either an interruption or a release defect from an inner group.
|
|
101
|
+
const closing = Cause.combine(
|
|
102
|
+
Cause.hasInterrupts(bodyCause) ||
|
|
103
|
+
Array.some(
|
|
104
|
+
bodyCause.reasons,
|
|
105
|
+
(reason) =>
|
|
106
|
+
reason._tag === "Die" &&
|
|
107
|
+
Schema.is(Release.ReleaseInterrupted)(reason.defect),
|
|
108
|
+
)
|
|
109
|
+
? bodyCause
|
|
110
|
+
: Cause.empty,
|
|
111
|
+
releaseCause,
|
|
112
|
+
)
|
|
113
|
+
yield* Ref.update(cleanup, (cause) => Cause.combine(cause, closing))
|
|
114
|
+
if (failure.reasons.length > 0) {
|
|
115
|
+
yield* Ref.update(failures, (cause) =>
|
|
116
|
+
Cause.combine(cause, failure),
|
|
117
|
+
)
|
|
118
|
+
yield* publish(failure)
|
|
119
|
+
yield* Deferred.failCause(fibers.deferred, failure)
|
|
120
|
+
}
|
|
121
|
+
if (Exit.isFailure(closed))
|
|
122
|
+
return yield* Effect.failCause(
|
|
123
|
+
Exit.isFailure(body)
|
|
124
|
+
? Cause.combine(body.cause, closed.cause)
|
|
125
|
+
: closed.cause,
|
|
126
|
+
)
|
|
127
|
+
if (Exit.isFailure(waiting) && Exit.isSuccess(body))
|
|
128
|
+
return yield* Effect.failCause(waiting.cause)
|
|
129
|
+
return yield* body
|
|
130
|
+
}),
|
|
131
|
+
)
|
|
132
|
+
yield* FiberSet.join(fibers).pipe(
|
|
133
|
+
Effect.catchCause((cause) =>
|
|
134
|
+
publish(cause).pipe(
|
|
135
|
+
Effect.andThen(FiberSet.clear(fibers)),
|
|
136
|
+
Effect.uninterruptible,
|
|
137
|
+
),
|
|
138
|
+
),
|
|
139
|
+
Effect.forkScoped,
|
|
140
|
+
)
|
|
141
|
+
const failure = Effect.tx(check.pipe(Effect.andThen(Effect.txRetry)))
|
|
142
|
+
const failCause = (cause: Cause.Cause<E>) =>
|
|
143
|
+
// This is an explicit failure report, not cancellation of an owned worker.
|
|
144
|
+
FiberSet.run(fibers, observe(Effect.failCause(cause))).pipe(Effect.asVoid)
|
|
145
|
+
const close = TxRef.update(state, (s) =>
|
|
146
|
+
s._tag === "Open" ? State.Closed() : s,
|
|
147
|
+
).pipe(
|
|
148
|
+
Effect.andThen(FiberSet.clear(fibers)),
|
|
149
|
+
Effect.andThen(Ref.get(cleanup)),
|
|
150
|
+
Effect.flatMap((cause) =>
|
|
151
|
+
cause.reasons.length > 0 ? Effect.failCause(cause) : Effect.void,
|
|
152
|
+
),
|
|
153
|
+
Effect.uninterruptible,
|
|
154
|
+
)
|
|
155
|
+
// Operational failure is delivered by await; release must also deliver sibling cleanup failures.
|
|
156
|
+
yield* Effect.addFinalizer(() => close.pipe(Effect.orDie))
|
|
157
|
+
return {
|
|
158
|
+
await: failure,
|
|
159
|
+
check,
|
|
160
|
+
cause: Ref.get(failures),
|
|
161
|
+
runtime: <R>() =>
|
|
162
|
+
FiberSet.runtime(fibers)<R>().pipe(
|
|
163
|
+
Effect.map(
|
|
164
|
+
(run) => (effect: Effect.Effect<unknown, E, R>) =>
|
|
165
|
+
run(check.pipe(Effect.andThen(observe(effect)))),
|
|
166
|
+
),
|
|
167
|
+
),
|
|
168
|
+
run: (effect) =>
|
|
169
|
+
FiberSet.run(fibers, check.pipe(Effect.andThen(observe(effect)))),
|
|
170
|
+
fail: (error) => failCause(Cause.fail(error)),
|
|
171
|
+
failCause,
|
|
172
|
+
race: (effect) =>
|
|
173
|
+
check.pipe(Effect.andThen(Effect.raceFirst(effect, failure))),
|
|
174
|
+
close,
|
|
175
|
+
}
|
|
176
|
+
})
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { Schema } from "effect"
|
|
2
|
+
|
|
3
|
+
export const PositiveInt = Schema.Int.check(Schema.isGreaterThan(0))
|
|
4
|
+
export const PositiveFinite = Schema.Finite.check(Schema.isGreaterThan(0))
|
|
5
|
+
export const Limits = Schema.Struct({
|
|
6
|
+
control: PositiveInt,
|
|
7
|
+
data: PositiveInt,
|
|
8
|
+
log: PositiveInt,
|
|
9
|
+
})
|
|
10
|
+
export type Limits = typeof Limits.Type
|
|
11
|
+
export type Priority = keyof Limits
|
|
12
|
+
export const defaults: Limits = { control: 8, data: 16, log: 2 }
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { Context, Deferred, Effect, Exit, Option, Schema, Scope } from "effect"
|
|
2
|
+
import * as Release from "./release"
|
|
3
|
+
import type { TransportError } from "../Errors"
|
|
4
|
+
|
|
5
|
+
export class StartupError extends Schema.TaggedError<
|
|
6
|
+
StartupError,
|
|
7
|
+
{ readonly brand: unique symbol }
|
|
8
|
+
>()("RunnerStartupError", {
|
|
9
|
+
detail: Schema.String,
|
|
10
|
+
cause: Schema.optionalKey(Schema.Cause(Schema.Unknown, Schema.Defect())),
|
|
11
|
+
}) {
|
|
12
|
+
override get message() {
|
|
13
|
+
return this.detail
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export class ServeLifecycle extends Context.Service<
|
|
18
|
+
ServeLifecycle,
|
|
19
|
+
{
|
|
20
|
+
readonly ended: Deferred.Deferred<void, TransportError>
|
|
21
|
+
readonly released: Deferred.Deferred<void, StartupError>
|
|
22
|
+
}
|
|
23
|
+
>()("@graftland/plugin-remote/internal/ServeLifecycle") {}
|
|
24
|
+
|
|
25
|
+
export const make = Effect.all({
|
|
26
|
+
ended: Deferred.make<void, TransportError>(),
|
|
27
|
+
released: Deferred.make<void, StartupError>(),
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
/** Observe shutdown independently of the loader, which logs child cleanup failures. */
|
|
31
|
+
export const resources = Effect.fnUntraced(function* (
|
|
32
|
+
lifecycle: Option.Option<typeof ServeLifecycle.Service>,
|
|
33
|
+
) {
|
|
34
|
+
return yield* Effect.acquireRelease(Scope.make(), (scope, exit) =>
|
|
35
|
+
Release.close(scope, exit).pipe(
|
|
36
|
+
Effect.onExit((released) =>
|
|
37
|
+
Option.isNone(lifecycle)
|
|
38
|
+
? Effect.void
|
|
39
|
+
: Deferred.done(
|
|
40
|
+
lifecycle.value.released,
|
|
41
|
+
Exit.isSuccess(released)
|
|
42
|
+
? Exit.void
|
|
43
|
+
: Exit.fail(
|
|
44
|
+
StartupError.make({
|
|
45
|
+
detail: "Supervisor shutdown failed",
|
|
46
|
+
cause: released.cause,
|
|
47
|
+
}),
|
|
48
|
+
),
|
|
49
|
+
).pipe(Effect.asVoid),
|
|
50
|
+
),
|
|
51
|
+
),
|
|
52
|
+
)
|
|
53
|
+
})
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/** A listener hands an authenticated byte channel to the supervisor. */
|
|
2
|
+
import { Context } from "effect"
|
|
3
|
+
import type { Effect, Scope } from "effect"
|
|
4
|
+
import type { Peer } from "./Peer"
|
|
5
|
+
import type { Channel } from "../Transport"
|
|
6
|
+
import type { TransportError } from "../Errors"
|
|
7
|
+
export class Supervisor extends Context.Service<
|
|
8
|
+
Supervisor,
|
|
9
|
+
{
|
|
10
|
+
readonly accept: (
|
|
11
|
+
channel: Channel,
|
|
12
|
+
) => Effect.Effect<void, TransportError, Peer | Scope.Scope>
|
|
13
|
+
}
|
|
14
|
+
>()("@graftland/plugin-remote/internal/Supervisor") {}
|