1/**
2 * Process bootstrap core for `AWS.Lambda.MicrovmImage` images. The runtime
3 * (`bun` | `node`) is chosen at deploy time, and each is an optional peer,
4 * so the platform-specific layers are supplied by the thin entry modules
5 * {@link ./MicrovmBun.ts} / {@link ./MicrovmNode.ts} rather than imported
6 * here. The generated entry imports one of those and the user's `main`,
7 * nothing else — see {@link ./Process.ts} for why.
8 */
9import * as Effect from "effect/Effect";
10import * as Layer from "effect/Layer";
11import * as Logger from "effect/Logger";
12import { MinimumLogLevel } from "effect/References";
13import * as FetchHttpClient from "effect/http/FetchHttpClient";
14import {
15 entrypointLayer,
16 resolveProgram,
17 runProcess,
18 stackConstant,
19} from "./Process.ts";
20
21export interface MicrovmBootstrapOptions {
22 /** Port to serve on when the VM does not inject `PORT`. */
23 readonly port: number;
24 /** Stack identity baked in at deploy time. */
25 readonly stack: { readonly name: string; readonly stage: string };
26}
27
28export interface MicrovmRuntime {
29 /** `BunServices.layer` / `NodeServices.layer`. */
30 readonly services: Layer.Layer<any>;
31 /** `BunHttpServer()` / `NodeHttpServer()`; both listen on `PORT`. */
32 readonly httpServer: Layer.Layer<any, any, any>;
33}
34
35/** Serve the bundled in-VM program on the runtime's HTTP server. */
36export const bootstrapMicrovm = (
37 runtime: MicrovmRuntime,
38 entrypoint: unknown,
39 options: MicrovmBootstrapOptions,
40): Promise<void> => {
41 // The HTTP server listens on `PORT`; default it to the image's port when
42 // the VM does not inject one.
43 process.env.PORT ??= String(options.port);
44
45 const platform = Layer.mergeAll(
46 runtime.services,
47 FetchHttpClient.layer,
48 Logger.layer([Logger.consolePretty()]),
49 );
50
51 const program = resolveProgram("default", { telemetry: true }).pipe(
52 Effect.provide(
53 entrypointLayer(entrypoint).pipe(
54 Layer.provideMerge(
55 stackConstant(options.stack.name, options.stack.stage),
56 ),
57 Layer.provideMerge(runtime.httpServer),
58 Layer.provideMerge(platform),
59 Layer.provideMerge(
60 Layer.succeed(MinimumLogLevel, process.env.DEBUG ? "Debug" : "Info"),
61 ),
62 ),
63 ),
64 Effect.scoped,
65 );
66
67 return runProcess(`MicroVM (port ${process.env.PORT})`, program);
68};