the-moby-effect
Version:
Moby/Docker API client built using effect-ts
115 lines • 4.84 kB
JavaScript
import * as Effect from "effect/Effect";
import * as Function from "effect/Function";
import * as Layer from "effect/Layer";
import * as Socket from "effect/unstable/socket/Socket";
import * as internalAgnostic from "./agnostic.js";
import * as internalConnection from "./connection.js";
/** @internal */
export const makeDispatcher = /*#__PURE__*/Effect.fnUntraced(function* (agentOptions) {
const undiciLazy = yield* Effect.promise(() => import("undici"));
const acquire = Effect.sync(() => new undiciLazy.Agent(agentOptions));
const release = agent => Effect.promise(() => agent.destroy());
const resource = Effect.acquireRelease(acquire, release);
return yield* resource;
});
/** @internal */
export const makeSshDispatcher = /*#__PURE__*/Effect.fnUntraced(function* (connectionOptions) {
const ssh2Lazy = yield* Effect.promise(() => import("ssh2"));
const undiciLazy = yield* Effect.promise(() => import("undici"));
const sshClient = new ssh2Lazy.Client();
let sshConnection = "unopened";
let openFailedError = null;
const connector = (
// oxlint-disable-next-line only-used-in-recursion
_options, callback) => {
const onError = error => callback(error, null);
// No connection yet, start connecting
if (sshConnection === "unopened") {
sshConnection = "connecting";
const unableToOpen = error => {
sshConnection = "open-failed";
openFailedError = error;
onError(error);
};
sshClient.once("ready", () => {
sshConnection = "ready";
sshClient.off("error", unableToOpen);
connector(_options, callback);
}).once("error", unableToOpen).connect(connectionOptions);
}
// Already tried to connect but failed
else if (sshConnection === "open-failed") {
callback(openFailedError, null);
}
// Another connection attempt is already in progress, wait for it
else if (sshConnection === "connecting") {
sshClient.once("ready", () => {
sshClient.off("error", onError);
connector(_options, callback);
}).once("error", onError);
}
// We are connected to the ssh server, now forward our stream local
else if (sshConnection === "ready") {
sshClient.openssh_forwardOutStreamLocal(connectionOptions.remoteSocketPath, (error, stream) => {
sshClient.off("error", onError);
if (error) return callback(error, null);else return callback(null, stream);
}).once("error", onError);
}
// Should never happen
else {
return Function.absurd(sshConnection);
}
};
const acquire = Effect.sync(() => new undiciLazy.Agent({
connect: connector
}));
const release = _agent => Effect.promise(async () => {
sshClient.end();
sshClient.destroy();
// await agent.close();
// await agent.destroy();
});
const resource = Effect.acquireRelease(acquire, release);
return yield* resource;
});
/** @internal */
export const getUndiciDispatcher = /*#__PURE__*/internalConnection.MobyConnectionOptions.$match({
ssh: options => makeSshDispatcher(options),
socket: options => makeDispatcher({
connect: {
socketPath: options.socketPath
}
}),
http: options => makeDispatcher({
connect: {
host: options.host,
port: options.port,
path: options.path
}
}),
https: options => makeDispatcher({
connect: {
ca: options.ca,
key: options.key,
cert: options.cert,
passphrase: options.passphrase,
host: options.host,
port: options.port,
path: options.path
}
})
});
/** @internal */
export const getUndiciWebsocketConstructor = connectionOptions => Function.pipe(Effect.promise(() => import("ws")), Effect.map(ws => {
const prependedUrl = internalAgnostic.makeWebsocketRequestUrl(connectionOptions);
return (url, protocols) => new ws.WebSocket(`${prependedUrl}${url}`, protocols);
}));
/** @internal */
export const makeUndiciHttpClientLayer = connectionOptions => {
const dispatcherLive = Function.pipe(Effect.promise(() => import("@effect/platform-node/NodeHttpClient")), Effect.map(nodeHttpClientLazy => Layer.effect(nodeHttpClientLazy.Dispatcher, getUndiciDispatcher(connectionOptions))), Layer.unwrap);
const websocketConstructorLive = Layer.effect(Socket.WebSocketConstructor, getUndiciWebsocketConstructor(connectionOptions));
const undiciHttpClientLive = Function.pipe(Effect.promise(() => import("@effect/platform-node/NodeHttpClient")), Effect.map(nodeHttpClientLazy => nodeHttpClientLazy.layerUndiciNoDispatcher), Layer.unwrap);
const agnosticHttpClientLayer = internalAgnostic.makeAgnosticHttpClientLayer(connectionOptions);
return agnosticHttpClientLayer.pipe(Layer.merge(websocketConstructorLive), Layer.provide(undiciHttpClientLive), Layer.provide(dispatcherLive));
};
//# sourceMappingURL=undici.js.map