@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,17 @@
|
|
|
1
|
+
import { t as AuthenticationFailed } from "./Errors-oPsTLY7l.js";
|
|
2
|
+
import { Effect, Option, Redacted } from "effect";
|
|
3
|
+
// packages/remote/src/internal/auth/token.ts
|
|
4
|
+
const invalid = () => AuthenticationFailed.make({
|
|
5
|
+
reason: "invalid",
|
|
6
|
+
challenge: "Bearer error=\"invalid_token\""
|
|
7
|
+
});
|
|
8
|
+
const read = (authorization) => {
|
|
9
|
+
if (Option.isNone(authorization)) return Effect.fail(AuthenticationFailed.make({
|
|
10
|
+
reason: "missing",
|
|
11
|
+
challenge: "Bearer"
|
|
12
|
+
}));
|
|
13
|
+
const token = /^Bearer +([^ ]+)$/i.exec(Redacted.value(authorization.value))?.[1];
|
|
14
|
+
return token === void 0 ? Effect.fail(invalid()) : Effect.succeed(Redacted.make(token));
|
|
15
|
+
};
|
|
16
|
+
const prepare = (token) => Effect.succeed(Redacted.make("Bearer " + Redacted.value(token)));
|
|
17
|
+
export { prepare as n, read as r, invalid as t };
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { a as provide, l as make } from "./Graft-BsYq3F-J.js";
|
|
2
|
+
import { t as ConfigDirectory } from "./ConfigDirectory-BCC6q2R4.js";
|
|
3
|
+
import { n as Credentials } from "./Auth-BElTC9_X.js";
|
|
4
|
+
import { n as CredentialsError } from "./Errors-oPsTLY7l.js";
|
|
5
|
+
import { n as prepare } from "./token-v5ne2I0-.js";
|
|
6
|
+
import { Cause, Effect, FileSystem, Option, Path, Redacted, Schema } from "effect";
|
|
7
|
+
// packages/remote/src/internal/auth/tokenCredentials.ts
|
|
8
|
+
const Config$1 = Schema.Struct({ token: Schema.Union([Schema.RedactedFromValue(Schema.String), Schema.Struct({ file: Schema.String })]) });
|
|
9
|
+
const plugin = (name) => make({
|
|
10
|
+
name,
|
|
11
|
+
config: Config$1,
|
|
12
|
+
inject: [FileSystem.FileSystem, Path.Path],
|
|
13
|
+
apply: Effect.fnUntraced(function* (config) {
|
|
14
|
+
const fs = yield* FileSystem.FileSystem;
|
|
15
|
+
const path = yield* Path.Path;
|
|
16
|
+
const directory = yield* ConfigDirectory;
|
|
17
|
+
const token = config.token;
|
|
18
|
+
const read = Redacted.isRedacted(token) ? Effect.succeed(token) : fs.readFileString(path.resolve(Option.getOrElse(directory, () => "."), token.file)).pipe(Effect.map(Redacted.make));
|
|
19
|
+
yield* provide(Credentials, {
|
|
20
|
+
kind: "Authorization",
|
|
21
|
+
read: read.pipe(Effect.flatMap(prepare), Effect.mapBoth({
|
|
22
|
+
onSuccess: (value) => ({
|
|
23
|
+
_tag: "Authorization",
|
|
24
|
+
value
|
|
25
|
+
}),
|
|
26
|
+
onFailure: (error) => CredentialsError.make({ cause: Cause.fail(error) })
|
|
27
|
+
}))
|
|
28
|
+
});
|
|
29
|
+
})
|
|
30
|
+
});
|
|
31
|
+
export { plugin as n, Config$1 as t };
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { c as TransportError } from "./Errors-oPsTLY7l.js";
|
|
2
|
+
import { a as payloadLimit, s as protocol, t as fromSocket } from "./socketChannel-KmtzF-9G.js";
|
|
3
|
+
import { Array, Cause, Deferred, Effect, Option, Redacted, Scope, String } from "effect";
|
|
4
|
+
import { NodeHttpServer, NodeHttpServerRequest } from "@effect/platform-node";
|
|
5
|
+
import { HttpServerRequest, HttpServerResponse } from "effect/http";
|
|
6
|
+
import { TLSSocket } from "node:tls";
|
|
7
|
+
// packages/remote/src/internal/websocketServer.ts
|
|
8
|
+
const rejection = (error) => Effect.logWarning("Remote host authentication failed", { reason: error.reason }, ...error.cause === void 0 ? [] : [error.cause]).pipe(Effect.as(HttpServerResponse.empty({
|
|
9
|
+
status: 401,
|
|
10
|
+
headers: error.challenge === void 0 ? {} : { "www-authenticate": error.challenge }
|
|
11
|
+
})));
|
|
12
|
+
const handshake = Effect.gen(function* () {
|
|
13
|
+
const request = yield* HttpServerRequest.HttpServerRequest;
|
|
14
|
+
const { socket } = NodeHttpServerRequest.toIncomingMessage(request);
|
|
15
|
+
const peer = socket instanceof TLSSocket && socket.authorized ? Option.some(socket.getPeerCertificate()) : Option.none();
|
|
16
|
+
return {
|
|
17
|
+
authorization: Option.map(Option.fromUndefinedOr(request.headers["authorization"]), Redacted.make),
|
|
18
|
+
certificate: Option.map(peer, (certificate) => ({
|
|
19
|
+
der: certificate.raw,
|
|
20
|
+
subjectAltName: Option.fromUndefinedOr(certificate.subjectaltname)
|
|
21
|
+
}))
|
|
22
|
+
};
|
|
23
|
+
});
|
|
24
|
+
const open = Effect.fnUntraced(function* (createServer, options) {
|
|
25
|
+
const resources = yield* Effect.acquireRelease(Scope.make(), Scope.close);
|
|
26
|
+
const ready = yield* Deferred.make();
|
|
27
|
+
const server = yield* NodeHttpServer.make(createServer, {
|
|
28
|
+
...options,
|
|
29
|
+
disablePreemptiveShutdown: true,
|
|
30
|
+
websocket: {
|
|
31
|
+
perMessageDeflate: false,
|
|
32
|
+
maxPayload: payloadLimit,
|
|
33
|
+
handleProtocols: (protocols) => protocols.has("graft-remote.v1") ? protocol : false
|
|
34
|
+
}
|
|
35
|
+
}).pipe(Scope.provide(resources));
|
|
36
|
+
return {
|
|
37
|
+
address: server.address,
|
|
38
|
+
ready: Deferred.await(ready),
|
|
39
|
+
run: (accept) => server.serve(Effect.gen(function* () {
|
|
40
|
+
const request = yield* HttpServerRequest.HttpServerRequest;
|
|
41
|
+
if (request.url !== "/graft") return HttpServerResponse.empty({ status: 404 });
|
|
42
|
+
if (String.toLowerCase(request.headers["upgrade"] ?? "") !== "websocket" || !Array.some(String.split(request.headers["sec-websocket-protocol"] ?? "", ","), (value) => String.trim(value) === "graft-remote.v1")) return HttpServerResponse.empty({ status: 400 });
|
|
43
|
+
const session = yield* Effect.result(accept);
|
|
44
|
+
if (session._tag === "Failure") return yield* rejection(session.failure);
|
|
45
|
+
yield* request.upgrade.pipe(Effect.flatMap(session.success), Effect.interruptible, Effect.scoped);
|
|
46
|
+
return HttpServerResponse.empty();
|
|
47
|
+
})).pipe(Effect.andThen(Deferred.succeed(ready, void 0)), Effect.andThen(Effect.never))
|
|
48
|
+
};
|
|
49
|
+
});
|
|
50
|
+
const bind = Effect.fnUntraced(function* (createServer, options) {
|
|
51
|
+
const server = yield* open(createServer, options);
|
|
52
|
+
return {
|
|
53
|
+
address: server.address,
|
|
54
|
+
ready: server.ready,
|
|
55
|
+
serve: (accept) => server.run(handshake.pipe(Effect.flatMap(accept), Effect.map((session) => (socket) => fromSocket(socket).pipe(Effect.flatMap(session))))).pipe(Effect.mapError((error) => TransportError.make({ cause: Cause.fail(error) })))
|
|
56
|
+
};
|
|
57
|
+
});
|
|
58
|
+
export { bind as t };
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { c as TransportError } from "./Errors-oPsTLY7l.js";
|
|
2
|
+
import { Cause, Effect, FiberSet, Queue, Schema, Sink, Stream } from "effect";
|
|
3
|
+
// packages/remote/src/internal/workerBootstrap.ts
|
|
4
|
+
const Bootstrap = Schema.Struct({
|
|
5
|
+
contractModule: Schema.String,
|
|
6
|
+
session: Schema.String
|
|
7
|
+
});
|
|
8
|
+
// packages/remote/src/internal/workerChannel.ts
|
|
9
|
+
const make = Effect.fnUntraced(function* (endpoint) {
|
|
10
|
+
const input = yield* Queue.unbounded();
|
|
11
|
+
const callbacks = yield* FiberSet.make();
|
|
12
|
+
const run = yield* FiberSet.runtime(callbacks)();
|
|
13
|
+
const receive = (value) => run(value === null ? Queue.end(input) : Queue.offer(input, new Uint8Array(value)));
|
|
14
|
+
yield* Effect.acquireRelease(Effect.sync(() => endpoint.on("message", receive)), () => Effect.sync(() => endpoint.off("message", receive)).pipe(Effect.andThen(Queue.shutdown(input))));
|
|
15
|
+
const send = (bytes) => Effect.try({
|
|
16
|
+
try: () => {
|
|
17
|
+
const buffer = bytes === null ? null : new Uint8Array(bytes).buffer;
|
|
18
|
+
endpoint.postMessage(buffer, buffer === null ? [] : [buffer]);
|
|
19
|
+
},
|
|
20
|
+
catch: (cause) => TransportError.make({ cause: Cause.die(cause) })
|
|
21
|
+
});
|
|
22
|
+
return {
|
|
23
|
+
input: Stream.fromQueue(input),
|
|
24
|
+
output: Sink.forEach((bytes) => send(bytes)).pipe(Sink.ensuring(send(null)))
|
|
25
|
+
};
|
|
26
|
+
});
|
|
27
|
+
export { Bootstrap as n, make as t };
|
package/package.json
CHANGED
|
@@ -1,6 +1,72 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@graftland/plugin-remote",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"description": "
|
|
3
|
+
"version": "0.3.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "Local and authenticated network execution for Graft plugins",
|
|
6
|
+
"license": "Apache-2.0",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/graftland/graft.git",
|
|
10
|
+
"directory": "packages/remote"
|
|
11
|
+
},
|
|
12
|
+
"files": [
|
|
13
|
+
"dist",
|
|
14
|
+
"src",
|
|
15
|
+
"README.zh-CN.md",
|
|
16
|
+
"README.zh-TW.md"
|
|
17
|
+
],
|
|
18
|
+
"sideEffects": false,
|
|
19
|
+
"exports": {
|
|
20
|
+
".": "./dist/index.js",
|
|
21
|
+
"./Auth": "./dist/Auth.js",
|
|
22
|
+
"./Contract": "./dist/Contract.js",
|
|
23
|
+
"./Errors": "./dist/Errors.js",
|
|
24
|
+
"./host/auth/bearer": "./dist/host/auth/bearer.js",
|
|
25
|
+
"./host/auth/jwt": "./dist/host/auth/jwt.js",
|
|
26
|
+
"./host/auth/mtls": "./dist/host/auth/mtls.js",
|
|
27
|
+
"./host/auth/none": "./dist/host/auth/none.js",
|
|
28
|
+
"./host/command": "./dist/host/command.js",
|
|
29
|
+
"./host/local": "./dist/host/local.js",
|
|
30
|
+
"./host/unix": "./dist/host/unix.js",
|
|
31
|
+
"./host/websocket": "./dist/host/websocket.js",
|
|
32
|
+
"./host/worker": "./dist/host/worker.js",
|
|
33
|
+
"./runner": "./dist/runner/index.js",
|
|
34
|
+
"./runner/auth/bearer": "./dist/runner/auth/bearer.js",
|
|
35
|
+
"./runner/auth/jwt": "./dist/runner/auth/jwt.js",
|
|
36
|
+
"./runner/auth/mtls": "./dist/runner/auth/mtls.js",
|
|
37
|
+
"./runner/auth/none": "./dist/runner/auth/none.js",
|
|
38
|
+
"./runner/stdio": "./dist/runner/stdio.js",
|
|
39
|
+
"./runner/unix": "./dist/runner/unix.js",
|
|
40
|
+
"./runner/websocket": "./dist/runner/websocket.js",
|
|
41
|
+
"./Testing": "./dist/Testing.js",
|
|
42
|
+
"./Transport": "./dist/Transport.js",
|
|
43
|
+
"./package.json": "./package.json",
|
|
44
|
+
"./internal/bin": "./dist/bin.js",
|
|
45
|
+
"./internal/watchdog": "./dist/internal/watchdogMain.js",
|
|
46
|
+
"./internal/worker": "./dist/internal/workerMain.js"
|
|
47
|
+
},
|
|
48
|
+
"peerDependencies": {
|
|
49
|
+
"effect": "4.1.*"
|
|
50
|
+
},
|
|
51
|
+
"dependencies": {
|
|
52
|
+
"@graftland/graft": "0.3.0",
|
|
53
|
+
"@effect/platform-node": "4.1.2",
|
|
54
|
+
"@graftland/loader": "0.3.0",
|
|
55
|
+
"pkijs": "4.0.1",
|
|
56
|
+
"asn1js": "4.0.0",
|
|
57
|
+
"ws": "8.22.1"
|
|
58
|
+
},
|
|
59
|
+
"devDependencies": {
|
|
60
|
+
"@effect/vitest": "4.1.2",
|
|
61
|
+
"effect": "4.1.2",
|
|
62
|
+
"vitest": "5.1.0",
|
|
63
|
+
"@types/ws": "8.18.2"
|
|
64
|
+
},
|
|
65
|
+
"bin": {
|
|
66
|
+
"graft-remote": "./dist/bin.js"
|
|
67
|
+
},
|
|
68
|
+
"scripts": {
|
|
69
|
+
"build": "tsdown",
|
|
70
|
+
"test": "vitest run"
|
|
71
|
+
}
|
|
6
72
|
}
|
package/src/Auth.ts
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/** Credentials and authenticated identities shared by transport plugins. */
|
|
2
|
+
import { Context } from "effect"
|
|
3
|
+
import type { Effect, Redacted, Option, Scope } from "effect"
|
|
4
|
+
import type { AuthenticationFailed, CredentialsError } from "./Errors"
|
|
5
|
+
|
|
6
|
+
export interface ClientCertificate {
|
|
7
|
+
readonly cert: string
|
|
8
|
+
readonly key: Redacted.Redacted<string>
|
|
9
|
+
}
|
|
10
|
+
export interface VerifiedCertificate {
|
|
11
|
+
readonly der: Uint8Array
|
|
12
|
+
readonly subjectAltName: Option.Option<string>
|
|
13
|
+
}
|
|
14
|
+
export interface TlsPolicy {
|
|
15
|
+
readonly ca: ReadonlyArray<string>
|
|
16
|
+
readonly minVersion: "TLSv1.3"
|
|
17
|
+
readonly requestCert: true
|
|
18
|
+
readonly rejectUnauthorized: true
|
|
19
|
+
}
|
|
20
|
+
export type Prepared =
|
|
21
|
+
| {
|
|
22
|
+
readonly _tag: "Authorization"
|
|
23
|
+
readonly value: Redacted.Redacted<string>
|
|
24
|
+
}
|
|
25
|
+
| { readonly _tag: "Certificate"; readonly value: ClientCertificate }
|
|
26
|
+
| { readonly _tag: "None" }
|
|
27
|
+
|
|
28
|
+
export type CredentialsApi = {
|
|
29
|
+
[K in Prepared["_tag"]]: {
|
|
30
|
+
readonly kind: K
|
|
31
|
+
readonly read: Effect.Effect<
|
|
32
|
+
Extract<Prepared, { readonly _tag: K }>,
|
|
33
|
+
CredentialsError,
|
|
34
|
+
Scope.Scope
|
|
35
|
+
>
|
|
36
|
+
}
|
|
37
|
+
}[Prepared["_tag"]]
|
|
38
|
+
|
|
39
|
+
export type AuthenticatorApi =
|
|
40
|
+
| {
|
|
41
|
+
readonly _tag: "Header"
|
|
42
|
+
readonly issuer?: string
|
|
43
|
+
readonly authenticate: (
|
|
44
|
+
header: Option.Option<Redacted.Redacted<string>>,
|
|
45
|
+
) => Effect.Effect<string, AuthenticationFailed>
|
|
46
|
+
}
|
|
47
|
+
| {
|
|
48
|
+
readonly _tag: "Mtls"
|
|
49
|
+
readonly tls: TlsPolicy
|
|
50
|
+
readonly authenticate: (
|
|
51
|
+
peer: VerifiedCertificate,
|
|
52
|
+
) => Effect.Effect<string, AuthenticationFailed>
|
|
53
|
+
}
|
|
54
|
+
| { readonly _tag: "None"; readonly identity: string }
|
|
55
|
+
|
|
56
|
+
export class Credentials extends Context.Service<Credentials, CredentialsApi>()(
|
|
57
|
+
"@graftland/plugin-remote/Auth/Credentials",
|
|
58
|
+
) {}
|
|
59
|
+
export class Authenticator extends Context.Service<
|
|
60
|
+
Authenticator,
|
|
61
|
+
AuthenticatorApi
|
|
62
|
+
>()("@graftland/plugin-remote/Auth/Authenticator") {}
|
package/src/Contract.ts
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { ChildFailure } from "./internal/childErrors"
|
|
2
|
+
export { ChildFailure }
|
|
3
|
+
import { Array, Cause, Predicate, Schema } from "effect"
|
|
4
|
+
|
|
5
|
+
import type { Service } from "./internal/services"
|
|
6
|
+
import * as Control from "./internal/control"
|
|
7
|
+
export { service } from "./internal/services"
|
|
8
|
+
|
|
9
|
+
const TypeId = "~@graftland/plugin-remote/Contract"
|
|
10
|
+
|
|
11
|
+
/** An undeclared failure still crosses the boundary, with its diagnostic value. */
|
|
12
|
+
export class UndeclaredFailure extends Schema.TaggedError<
|
|
13
|
+
UndeclaredFailure,
|
|
14
|
+
{ readonly brand: unique symbol }
|
|
15
|
+
>()("UndeclaredFailure", { original: Schema.Defect({ includeStack: true }) }) {}
|
|
16
|
+
|
|
17
|
+
/** Pure metadata shared by the host and the executable runner bootstrap. */
|
|
18
|
+
export const make = <
|
|
19
|
+
S extends Schema.Top & {
|
|
20
|
+
readonly Encoded: Schema.Json
|
|
21
|
+
readonly DecodingServices: never
|
|
22
|
+
readonly EncodingServices: never
|
|
23
|
+
},
|
|
24
|
+
E extends Schema.Top & {
|
|
25
|
+
readonly DecodingServices: never
|
|
26
|
+
readonly EncodingServices: never
|
|
27
|
+
},
|
|
28
|
+
>(options: {
|
|
29
|
+
readonly name: string
|
|
30
|
+
readonly config: S
|
|
31
|
+
readonly error: E
|
|
32
|
+
readonly module: string
|
|
33
|
+
readonly inject?: ReadonlyArray<Service>
|
|
34
|
+
readonly provides?: ReadonlyArray<Service>
|
|
35
|
+
}) => {
|
|
36
|
+
// Keys are runtime strings; tuple types cannot distinguish independently created keys.
|
|
37
|
+
const keys = Array.map(
|
|
38
|
+
[...(options.inject ?? []), ...(options.provides ?? [])],
|
|
39
|
+
(service) => service.key.key,
|
|
40
|
+
)
|
|
41
|
+
if (
|
|
42
|
+
Array.dedupe(keys).length !== keys.length ||
|
|
43
|
+
keys.includes(Control.endpoint)
|
|
44
|
+
)
|
|
45
|
+
throw new Error(
|
|
46
|
+
"Service keys must be unique within a remote contract and must not use the control endpoint",
|
|
47
|
+
)
|
|
48
|
+
const failure = Schema.Union([options.error, UndeclaredFailure, ChildFailure])
|
|
49
|
+
const codec = Schema.toCodecJson(
|
|
50
|
+
Schema.Cause(failure, Schema.Defect({ includeStack: true })),
|
|
51
|
+
)
|
|
52
|
+
const declared = Schema.is(failure)
|
|
53
|
+
const normalizeCause = (cause: Cause.Cause<unknown>) =>
|
|
54
|
+
Cause.map(cause, (error) =>
|
|
55
|
+
declared(error) ? error : UndeclaredFailure.make({ original: error }),
|
|
56
|
+
)
|
|
57
|
+
return {
|
|
58
|
+
...options,
|
|
59
|
+
control: Control.make(codec),
|
|
60
|
+
normalizeCause,
|
|
61
|
+
inject: options.inject ?? [],
|
|
62
|
+
provides: options.provides ?? [],
|
|
63
|
+
[TypeId]: TypeId,
|
|
64
|
+
encodeCause: (cause: Cause.Cause<unknown>) =>
|
|
65
|
+
normalizeCause(cause).pipe(Schema.encodeEffect(codec)),
|
|
66
|
+
decodeCause: Schema.decodeUnknownEffect(codec),
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/** The shared config, error codecs, and service declarations for one remote plugin. */
|
|
71
|
+
export type Contract<
|
|
72
|
+
S extends Schema.Top & {
|
|
73
|
+
readonly Encoded: Schema.Json
|
|
74
|
+
readonly DecodingServices: never
|
|
75
|
+
readonly EncodingServices: never
|
|
76
|
+
},
|
|
77
|
+
E extends Schema.Top & {
|
|
78
|
+
readonly DecodingServices: never
|
|
79
|
+
readonly EncodingServices: never
|
|
80
|
+
},
|
|
81
|
+
> = ReturnType<typeof make<S, E>>
|
|
82
|
+
|
|
83
|
+
/** Dynamic imports retain the control server and cause normalization; hosts retain the declared error type. */
|
|
84
|
+
export type RunnerContract = Contract<
|
|
85
|
+
Schema.Codec<unknown, Schema.Json>,
|
|
86
|
+
Schema.Codec<unknown>
|
|
87
|
+
>
|
|
88
|
+
|
|
89
|
+
export const isContract = (value: unknown): value is RunnerContract =>
|
|
90
|
+
Predicate.hasProperty(value, TypeId) && value[TypeId] === TypeId
|
|
91
|
+
|
|
92
|
+
/** A contract module exports either default or a named contract. */
|
|
93
|
+
export const fromModule = (module: unknown) =>
|
|
94
|
+
Array.findFirst(
|
|
95
|
+
[
|
|
96
|
+
Predicate.hasProperty(module, "default") ? module.default : undefined,
|
|
97
|
+
Predicate.hasProperty(module, "contract") ? module.contract : undefined,
|
|
98
|
+
],
|
|
99
|
+
isContract,
|
|
100
|
+
)
|
package/src/Errors.ts
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { Option, Schema } from "effect"
|
|
2
|
+
|
|
3
|
+
export class ProcessExited extends Schema.TaggedError<
|
|
4
|
+
ProcessExited,
|
|
5
|
+
{ readonly brand: unique symbol }
|
|
6
|
+
>()("ProcessExited", {
|
|
7
|
+
pid: Schema.Option(Schema.Int),
|
|
8
|
+
threadId: Schema.optionalKey(Schema.Int),
|
|
9
|
+
exitCode: Schema.NullOr(Schema.Int),
|
|
10
|
+
stderr: Schema.String,
|
|
11
|
+
statusCause: Schema.Cause(Schema.Unknown, Schema.Unknown),
|
|
12
|
+
}) {
|
|
13
|
+
override get message() {
|
|
14
|
+
return this.threadId !== undefined
|
|
15
|
+
? `Remote worker ${this.threadId} exited unexpectedly (${this.exitCode})`
|
|
16
|
+
: `Remote process${Option.isSome(this.pid) ? ` ${this.pid.value}` : ""} exited unexpectedly (${this.exitCode ?? "signal"})`
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
export class TransportError extends Schema.TaggedError<
|
|
20
|
+
TransportError,
|
|
21
|
+
{ readonly brand: unique symbol }
|
|
22
|
+
>()("TransportError", {
|
|
23
|
+
cause: Schema.Cause(Schema.Unknown, Schema.Unknown),
|
|
24
|
+
}) {}
|
|
25
|
+
|
|
26
|
+
/** The remote export lease was withdrawn; host consumers must drain before Dispose. */
|
|
27
|
+
export class RemoteWithdrawn extends Schema.TaggedError<
|
|
28
|
+
RemoteWithdrawn,
|
|
29
|
+
{ readonly brand: unique symbol }
|
|
30
|
+
>()("RemoteWithdrawn", {}) {
|
|
31
|
+
override get message() {
|
|
32
|
+
return "Remote services were withdrawn"
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** Infrastructure failure at the plugin boundary; the complete underlying cause remains inspectable. */
|
|
37
|
+
export class RemoteError extends Schema.TaggedError<
|
|
38
|
+
RemoteError,
|
|
39
|
+
{ readonly brand: unique symbol }
|
|
40
|
+
>()("RemoteError", { cause: Schema.Cause(Schema.Unknown, Schema.Unknown) }) {}
|
|
41
|
+
|
|
42
|
+
/** A network execution needs a stable logical identity across host restarts. */
|
|
43
|
+
export class MissingInstance extends Schema.TaggedError<
|
|
44
|
+
MissingInstance,
|
|
45
|
+
{ readonly brand: unique symbol }
|
|
46
|
+
>()("MissingInstance", {}) {
|
|
47
|
+
override get message() {
|
|
48
|
+
return "A runner connection requires an instance or a loader entry id; set connect.instance when loading without Loader."
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** Dispose completed, but Node still owns live handles; process-scope cleanup must terminate it. */
|
|
53
|
+
export class LocalTerminationTimeout extends Schema.TaggedError<
|
|
54
|
+
LocalTerminationTimeout,
|
|
55
|
+
{ readonly brand: unique symbol }
|
|
56
|
+
>()("LocalTerminationTimeout", {
|
|
57
|
+
pid: Schema.optionalKey(Schema.Int),
|
|
58
|
+
threadId: Schema.optionalKey(Schema.Int),
|
|
59
|
+
}) {
|
|
60
|
+
override get message() {
|
|
61
|
+
return this.threadId !== undefined
|
|
62
|
+
? `Remote worker ${this.threadId} did not exit within one second after Dispose; terminating it`
|
|
63
|
+
: `Remote process ${this.pid} did not exit within one second after Dispose; terminating it`
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/** A credential source failed; the original failure remains available. */
|
|
68
|
+
export class CredentialsError extends Schema.TaggedError<
|
|
69
|
+
CredentialsError,
|
|
70
|
+
{ readonly brand: unique symbol }
|
|
71
|
+
>()("CredentialsError", {
|
|
72
|
+
cause: Schema.Cause(Schema.Unknown, Schema.Unknown),
|
|
73
|
+
}) {}
|
|
74
|
+
|
|
75
|
+
export class AuthenticationFailed extends Schema.TaggedError<
|
|
76
|
+
AuthenticationFailed,
|
|
77
|
+
{ readonly brand: unique symbol }
|
|
78
|
+
>()("AuthenticationFailed", {
|
|
79
|
+
reason: Schema.Literals(["missing", "invalid"]),
|
|
80
|
+
challenge: Schema.optionalKey(Schema.String),
|
|
81
|
+
cause: Schema.optionalKey(Schema.Cause(Schema.Unknown, Schema.Unknown)),
|
|
82
|
+
}) {}
|
package/src/Instance.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { Context, Schema } from "effect"
|
|
2
|
+
export const Identity = Schema.Struct({
|
|
3
|
+
id: Schema.String,
|
|
4
|
+
generation: Schema.Int,
|
|
5
|
+
})
|
|
6
|
+
/** Outside a remote activation the identity is local/0. Local activations use a fresh UUID/1; runners allocate persistent generations. */
|
|
7
|
+
export const Instance = Context.Reference<typeof Identity.Type>(
|
|
8
|
+
"@graftland/plugin-remote/Instance/Instance",
|
|
9
|
+
{
|
|
10
|
+
defaultValue: () => ({ id: "local", generation: 0 }),
|
|
11
|
+
},
|
|
12
|
+
)
|
package/src/Testing.ts
ADDED
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import type { Plugin } from "@graftland/graft"
|
|
2
|
+
import {
|
|
3
|
+
Array,
|
|
4
|
+
Equivalence,
|
|
5
|
+
Deferred,
|
|
6
|
+
Effect,
|
|
7
|
+
Fiber,
|
|
8
|
+
Stream,
|
|
9
|
+
String,
|
|
10
|
+
Schema,
|
|
11
|
+
} from "effect"
|
|
12
|
+
import type { Channel } from "./Transport"
|
|
13
|
+
import * as Framing from "./internal/runner/Framing"
|
|
14
|
+
|
|
15
|
+
export class ConformanceError extends Schema.TaggedError<
|
|
16
|
+
ConformanceError,
|
|
17
|
+
{ readonly brand: unique symbol }
|
|
18
|
+
>()("ConformanceError", { message: Schema.String }) {}
|
|
19
|
+
const check = (condition: boolean, message: string) =>
|
|
20
|
+
condition ? Effect.void : Effect.fail(ConformanceError.make({ message }))
|
|
21
|
+
export interface Capabilities {
|
|
22
|
+
readonly connector: Plugin.Plugin
|
|
23
|
+
readonly listener: Plugin.Plugin
|
|
24
|
+
readonly credentials: Plugin.Plugin
|
|
25
|
+
readonly authenticator: Plugin.Plugin
|
|
26
|
+
}
|
|
27
|
+
/** The fixture supplies addresses/credentials and an echo session; no full runner is required. */
|
|
28
|
+
export const transport = <E, R>(
|
|
29
|
+
plugins: Capabilities,
|
|
30
|
+
fixture: (plugins: Capabilities) => Effect.Effect<Channel, E, R>,
|
|
31
|
+
) => {
|
|
32
|
+
const connect = fixture(plugins)
|
|
33
|
+
return {
|
|
34
|
+
chunks: Effect.gen(function* () {
|
|
35
|
+
const channel = yield* connect
|
|
36
|
+
const first = yield* Framing.encode({
|
|
37
|
+
channel: 0,
|
|
38
|
+
payload: new Uint8Array([1, 2, 3]),
|
|
39
|
+
})
|
|
40
|
+
const second = yield* Framing.encode({
|
|
41
|
+
channel: 1,
|
|
42
|
+
payload: new Uint8Array([4, 5]),
|
|
43
|
+
})
|
|
44
|
+
const combined = new Uint8Array(first.length + second.length)
|
|
45
|
+
combined.set(first)
|
|
46
|
+
combined.set(second, first.length)
|
|
47
|
+
// Split the header, then put the remainder and the next frame in one chunk.
|
|
48
|
+
const done = yield* Deferred.make<void>()
|
|
49
|
+
const sending = yield* Stream.make(
|
|
50
|
+
combined.subarray(0, 2),
|
|
51
|
+
combined.subarray(2),
|
|
52
|
+
).pipe(
|
|
53
|
+
Stream.concat(
|
|
54
|
+
Stream.fromEffect(Deferred.await(done)).pipe(Stream.drain),
|
|
55
|
+
),
|
|
56
|
+
Stream.run(channel.output),
|
|
57
|
+
Effect.forkScoped,
|
|
58
|
+
)
|
|
59
|
+
const frames = Framing.decoder()
|
|
60
|
+
const reply = yield* channel.input.pipe(
|
|
61
|
+
Stream.mapEffect((chunk) => Effect.fromResult(frames.decode(chunk))),
|
|
62
|
+
Stream.flattenIterable,
|
|
63
|
+
Stream.zipWithIndex,
|
|
64
|
+
Stream.tap(([, index]) =>
|
|
65
|
+
index === 1 ? Deferred.succeed(done, undefined) : Effect.void,
|
|
66
|
+
),
|
|
67
|
+
Stream.map(([frame]) => frame),
|
|
68
|
+
Stream.runCollect,
|
|
69
|
+
)
|
|
70
|
+
yield* frames.finish
|
|
71
|
+
yield* Deferred.succeed(done, undefined)
|
|
72
|
+
yield* Fiber.join(sending)
|
|
73
|
+
yield* check(
|
|
74
|
+
Array.makeEquivalence(
|
|
75
|
+
Equivalence.Struct({
|
|
76
|
+
channel: Equivalence.Number,
|
|
77
|
+
payload: Array.makeEquivalence(Equivalence.Number),
|
|
78
|
+
}),
|
|
79
|
+
)(
|
|
80
|
+
Array.map(reply, (frame) => ({
|
|
81
|
+
channel: frame.channel,
|
|
82
|
+
payload: Array.fromIterable(frame.payload),
|
|
83
|
+
})),
|
|
84
|
+
[
|
|
85
|
+
{ channel: 0, payload: [1, 2, 3] },
|
|
86
|
+
{ channel: 1, payload: [4, 5] },
|
|
87
|
+
],
|
|
88
|
+
),
|
|
89
|
+
"The channel changed byte order across split or coalesced chunks",
|
|
90
|
+
)
|
|
91
|
+
}),
|
|
92
|
+
eof: Effect.gen(function* () {
|
|
93
|
+
const channel = yield* connect
|
|
94
|
+
yield* Stream.empty.pipe(Stream.run(channel.output))
|
|
95
|
+
const remaining = yield* Stream.runCollect(channel.input)
|
|
96
|
+
yield* check(
|
|
97
|
+
remaining.length === 0,
|
|
98
|
+
"The channel produced bytes after EOF",
|
|
99
|
+
)
|
|
100
|
+
}),
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/** HTTP implementations reject before accepting a session, preserving the challenge. */
|
|
105
|
+
export const rejection = <E, R>(
|
|
106
|
+
response: Effect.Effect<string, E, R>,
|
|
107
|
+
challenge?: string,
|
|
108
|
+
) =>
|
|
109
|
+
response.pipe(
|
|
110
|
+
Effect.tap((response) =>
|
|
111
|
+
Effect.all(
|
|
112
|
+
[
|
|
113
|
+
check(String.includes("HTTP/1.1 401")(response), "Expected HTTP 401"),
|
|
114
|
+
check(
|
|
115
|
+
!String.includes("101 Switching Protocols")(response),
|
|
116
|
+
"Rejected credentials opened a WebSocket",
|
|
117
|
+
),
|
|
118
|
+
check(
|
|
119
|
+
!String.includes("sec-websocket-accept:")(
|
|
120
|
+
String.toLowerCase(response),
|
|
121
|
+
),
|
|
122
|
+
"Rejected credentials received a WebSocket accept header",
|
|
123
|
+
),
|
|
124
|
+
check(
|
|
125
|
+
challenge === undefined ||
|
|
126
|
+
String.includes(
|
|
127
|
+
"www-authenticate: " + String.toLowerCase(challenge),
|
|
128
|
+
)(String.toLowerCase(response)),
|
|
129
|
+
"The authentication challenge was lost",
|
|
130
|
+
),
|
|
131
|
+
],
|
|
132
|
+
{ discard: true },
|
|
133
|
+
),
|
|
134
|
+
),
|
|
135
|
+
)
|
package/src/Transport.ts
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/** Execution and byte-channel capabilities; implementations live in host plugins. */
|
|
2
|
+
import { Context } from "effect"
|
|
3
|
+
import type { Effect, Option, Scope, Stream, Sink, Redacted } from "effect"
|
|
4
|
+
import type { AuthenticationFailed, TransportError } from "./Errors"
|
|
5
|
+
import type { Prepared, VerifiedCertificate, TlsPolicy } from "./Auth"
|
|
6
|
+
/** Ordered reliable bytes. Chunk boundaries have no meaning; EOF is not exit confirmation. */
|
|
7
|
+
export interface Channel {
|
|
8
|
+
readonly input: Stream.Stream<Uint8Array, TransportError>
|
|
9
|
+
readonly output: Sink.Sink<unknown, Uint8Array, unknown, TransportError>
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface Properties {
|
|
13
|
+
readonly protocol: string
|
|
14
|
+
readonly encrypted: boolean
|
|
15
|
+
readonly network: boolean
|
|
16
|
+
readonly carries: ReadonlyArray<"header" | "certificate">
|
|
17
|
+
}
|
|
18
|
+
export interface Handshake {
|
|
19
|
+
readonly authorization: Option.Option<Redacted.Redacted<string>>
|
|
20
|
+
readonly certificate: Option.Option<VerifiedCertificate>
|
|
21
|
+
}
|
|
22
|
+
export type Session = (
|
|
23
|
+
channel: Channel,
|
|
24
|
+
) => Effect.Effect<void, TransportError, Scope.Scope>
|
|
25
|
+
export type Accept = (
|
|
26
|
+
handshake: Handshake,
|
|
27
|
+
) => Effect.Effect<Session, AuthenticationFailed>
|
|
28
|
+
export interface Serving {
|
|
29
|
+
/** Completes after serve has registered its accept loop, not merely after bind. */
|
|
30
|
+
readonly ready: Effect.Effect<void>
|
|
31
|
+
readonly serve: (
|
|
32
|
+
accept: Accept,
|
|
33
|
+
) => Effect.Effect<void, TransportError, Scope.Scope>
|
|
34
|
+
}
|
|
35
|
+
export interface ConnectorApi {
|
|
36
|
+
readonly properties: Properties
|
|
37
|
+
readonly connect: (
|
|
38
|
+
credentials: Prepared,
|
|
39
|
+
) => Effect.Effect<Channel, TransportError, Scope.Scope>
|
|
40
|
+
}
|
|
41
|
+
export interface ListenerApi {
|
|
42
|
+
readonly properties: Properties
|
|
43
|
+
readonly bind: (
|
|
44
|
+
tls: Option.Option<TlsPolicy>,
|
|
45
|
+
) => Effect.Effect<Serving, TransportError, Scope.Scope>
|
|
46
|
+
}
|
|
47
|
+
export class Connector extends Context.Service<Connector, ConnectorApi>()(
|
|
48
|
+
"@graftland/plugin-remote/Transport/Connector",
|
|
49
|
+
) {}
|
|
50
|
+
export class Listener extends Context.Service<Listener, ListenerApi>()(
|
|
51
|
+
"@graftland/plugin-remote/Transport/Listener",
|
|
52
|
+
) {}
|