1/**
2 * Process bootstrap for `Fly.Service` and `Fly.Sprite` (a Node process
3 * serving the bundled program). The generated entry imports this module
4 * and the user's `main`, nothing else — see {@link ./Process.ts} for why.
5 */
6import { NodeServices } from "@effect/platform-node";
7import * as ConfigProvider from "effect/ConfigProvider";
8import * as Effect from "effect/Effect";
9import * as Layer from "effect/Layer";
10import * as Logger from "effect/Logger";
11import * as Option from "effect/Option";
12import * as Scope from "effect/Scope";
13import * as FetchHttpClient from "effect/http/FetchHttpClient";
14import { NodeHttpServer } from "../../Http.ts";
15import { reifyBoundConfigProvider } from "../../Runtime.ts";
16import { ManagedHttpShutdown } from "./ManagedHttpShutdown.ts";
17import {
18 entrypointLayer,
19 resolveProgram,
20 runProcess,
21 stackFromEnv,
22} from "./Process.ts";
23
24/**
25 * Resolve the bundled program (the runners registered via `host.run` /
26 * serve) and run it with a Node HTTP server bound to `PORT`, so the
27 * returned `{ fetch }` handler is actually served and `host.run` loops
28 * stay alive.
29 */
30export const bootstrap = (entrypoint: unknown): Promise<void> => {
31 const platform = Layer.mergeAll(
32 NodeServices.layer,
33 FetchHttpClient.layer,
34 Logger.layer([Logger.consolePretty()]),
35 );
36
37 const services = entrypointLayer(entrypoint).pipe(
38 Layer.provideMerge(stackFromEnv),
39 Layer.provideMerge(NodeHttpServer({ hostname: "0.0.0.0" })),
40 Layer.provideMerge(platform),
41 Layer.provideMerge(
42 Layer.succeed(
43 ConfigProvider.ConfigProvider,
44 reifyBoundConfigProvider(ConfigProvider.fromEnv(), process.env),
45 ),
46 ),
47 );
48 const program = Effect.gen(function* () {
49 const managed = yield* Effect.serviceOption(ManagedHttpShutdown);
50 if (Option.isNone(managed)) {
51 return yield* resolveProgram("program").pipe(
52 Effect.provide(services),
53 Effect.scoped,
54 );
55 }
56 // Initializer dependencies outlive both run finalizers and HTTP responses.
57 const context = yield* Layer.buildWithScope(
58 services,
59 managed.value.dependencies,
60 );
61 return yield* resolveProgram("program").pipe(
62 Effect.provideContext(context),
63 Scope.provide(managed.value.dependencies),
64 );
65 });
66
67 const timeout = process.env.ALCHEMY_FLY_SHUTDOWN_TIMEOUT_MS;
68 return runProcess("Fly service", program, {
69 managedHttpShutdownTimeoutMs:
70 timeout === undefined
71 ? undefined
72 : /^\d+$/.test(timeout)
73 ? Number(timeout)
74 : NaN,
75 });
76};