1import * as Effect from "effect/Effect";
2import * as Redacted from "effect/Redacted";
3import * as HttpClient from "effect/http/HttpClient";
4import * as HttpClientRequest from "effect/http/HttpClientRequest";
5import { makeFetchRpcStub } from "../../Rpc.ts";
6
7/**
8 * A running MicroVM's connection details, as returned by the {@link RunMicrovm}
9 * and {@link CreateAuthToken} bindings.
10 */
11export interface MicrovmConnection {
12 /**
13 * The MicroVM endpoint hostname (no scheme), e.g.
14 * `<id>.lambda-microvm.<region>.on.aws` — from `RunMicrovm`'s response.
15 */
16 endpoint: string;
17 /**
18 * The auth token from `CreateAuthToken` — a map of header name → value that
19 * authorizes requests to the MicroVM endpoint (the AWS proxy validates them).
20 * Values may be {@link Redacted.Redacted}.
21 */
22 authToken: Record<string, string | Redacted.Redacted<string> | undefined>;
23}
24
25/**
26 * Flatten a MicroVM `authToken` (from {@link CreateAuthToken}) into a plain
27 * header map, unwrapping any {@link Redacted.Redacted} values. Send these as
28 * request headers when calling the MicroVM endpoint.
29 */
30export const microvmAuthHeaders = (
31 authToken: MicrovmConnection["authToken"],
32): Record<string, string> => {
33 const headers: Record<string, string> = {};
34 for (const [key, value] of Object.entries(authToken)) {
35 if (value === undefined) continue;
36 headers[key] = Redacted.isRedacted(value) ? Redacted.value(value) : value;
37 }
38 return headers;
39};
40
41/**
42 * Connect to a running MicroVM's in-VM RPC server and return a typed client
43 * stub. The stub mirrors the image's tagged RPC `Shape`: value methods
44 * `yield*` as `Effect`s, streaming methods pipe as `Stream`s. Each call is a
45 * `POST https://<endpoint>/__rpc__/<method>` with the auth-token entries set as
46 * request headers — the mirror of {@link serveRpc} running inside the MicroVM.
47 *
48 * @example
49 * ```typescript
50 * const vm = yield* runMicrovm({});
51 * const { authToken } = yield* createAuthToken({
52 * microvmIdentifier: vm.microvmId,
53 * expirationInMinutes: 5,
54 * allowedPorts: [{ port: 8080 }],
55 * });
56 * const sandbox = yield* AWS.Lambda.connectMicrovm(Sandbox, {
57 * endpoint: vm.endpoint,
58 * authToken,
59 * });
60 * const reply = yield* sandbox.hello("world");
61 * ```
62 */
63export const connectMicrovm = <S>(
64 _image: abstract new (_: never) => S,
65 connection: MicrovmConnection,
66): Effect.Effect<S, never, HttpClient.HttpClient> =>
67 Effect.gen(function* () {
68 const client = yield* HttpClient.HttpClient;
69 const headers = microvmAuthHeaders(connection.authToken);
70 return makeFetchRpcStub<S>({
71 baseUrl: `https://${connection.endpoint}`,
72 fetch: (request) =>
73 client.execute(request.pipe(HttpClientRequest.setHeaders(headers))),
74 });
75 });