1import * as NodeRuntime from "@effect/platform-node/NodeRuntime";
2import * as NodeServices from "@effect/platform-node/NodeServices";
3import * as ConfigProvider from "effect/ConfigProvider";
4import * as Effect from "effect/Effect";
5import * as Layer from "effect/Layer";
6import * as Scope from "effect/Scope";
7import * as Framework from "./Framework.ts";
8import * as Server from "./Server.ts";
9
10export { Cwd } from "./Cwd.ts";
11
12export const layer = Server.layer.pipe(
13 Layer.provideMerge(Framework.layer),
14 Layer.provideMerge(
15 ConfigProvider.layer(
16 ConfigProvider.fromDotEnv().pipe(
17 Effect.orElseSucceed(() => ConfigProvider.fromEnv()),
18 ),
19 ),
20 ),
21 Layer.provideMerge(NodeServices.layer),
22);
23
24export const runMain = <A, E>(effect: Effect.Effect<A, E, Scope.Scope>) => {
25 const scope = Scope.makeUnsafe();
26 return NodeRuntime.runMain(effect.pipe(Scope.provide(scope)), {
27 teardown: (exit, onExit) => {
28 Effect.runPromise(Scope.close(scope, exit)).then(() => {
29 const code = exit._tag === "Success" ? 0 : 1;
30 onExit(code);
31 // `NodeRuntime.runMain` only force-exits on failure/signal; on success
32 // it waits for the event loop to drain. The runtime layers can leave
33 // lingering handles behind (e.g. `cloudflare-runtime`'s docker proxy
34 // listener is started on a detached fiber with no finalizer, keeping
35 // the loop alive forever after a successful `e2e build`). A CLI must
36 // exit deterministically once its teardown has run.
37 process.exit(code);
38 });
39 },
40 });
41};