1/**
2 * Process bootstrap for `Railway.Service` (a Node process serving the
3 * bundled program). The generated entry imports this module and the user's
4 * `main`, nothing else — see {@link ./Process.ts} for why.
5 *
6 * Railway canvas Functions stay on bun (`functionRuntime(bun)` + the
7 * inline canvas wrapper). This module is the Docker/Service path only.
8 */
9import { NodeServices } from "@effect/platform-node";
10import * as ConfigProvider from "effect/ConfigProvider";
11import * as Effect from "effect/Effect";
12import * as Layer from "effect/Layer";
13import * as Logger from "effect/Logger";
14import * as FetchHttpClient from "effect/http/FetchHttpClient";
15import { NodeHttpServer } from "../../Http.ts";
16import { reifyBoundConfigProvider } from "../../Runtime.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 program = resolveProgram("program").pipe(
38 Effect.provide(
39 entrypointLayer(entrypoint).pipe(
40 Layer.provideMerge(stackFromEnv),
41 Layer.provideMerge(NodeHttpServer({ hostname: "0.0.0.0" })),
42 Layer.provideMerge(platform),
43 Layer.provideMerge(
44 Layer.succeed(
45 ConfigProvider.ConfigProvider,
46 reifyBoundConfigProvider(ConfigProvider.fromEnv(), process.env),
47 ),
48 ),
49 ),
50 ),
51 Effect.scoped,
52 );
53
54 return runProcess("Railway service", program);
55};