1import * as Effect from "effect/Effect";
2import * as FileSystem from "effect/FileSystem";
3import * as Path from "effect/Path";
4import * as Schema from "effect/Schema";
5import * as ChildProcess from "effect/process/ChildProcess";
6
7export interface TraceInput {
8 readonly seeds: string[];
9 readonly base: string;
10 readonly root: string;
11 readonly next: boolean;
12}
13
14/** Keep dependency-parser heaps outside the long-lived deployment process. */
15export const traceWebsiteFiles = Effect.fn(function* (input: TraceInput) {
16 const fs = yield* FileSystem.FileSystem;
17 const path = yield* Path.Path;
18 const directory = yield* fs.makeTempDirectoryScoped({
19 prefix: "alchemy-neon-trace-",
20 });
21 const inputPath = path.join(directory, "input.json");
22 const outputPath = path.join(directory, "output.json");
23 yield* fs.writeFileString(inputPath, JSON.stringify(input));
24 const runner = yield* path.fromFileUrl(
25 new URL(
26 import.meta.url.endsWith(".ts") ? "./TraceRunner.ts" : "./TraceRunner.js",
27 import.meta.url,
28 ),
29 );
30 const child = yield* ChildProcess.make(
31 "node",
32 ["--max-old-space-size=1536", runner, inputPath, outputPath],
33 { stdin: "ignore", stdout: "ignore", stderr: "inherit" },
34 );
35 const code = yield* child.exitCode;
36 if (code !== 0)
37 return yield* Effect.fail(
38 new Error(`Website dependency trace exited ${code}`),
39 );
40 return yield* fs
41 .readFileString(outputPath)
42 .pipe(
43 Effect.flatMap(
44 Schema.decodeUnknownEffect(
45 Schema.fromJsonString(Schema.Array(Schema.String)),
46 ),
47 ),
48 );
49}, Effect.scoped);