import { type ChildProcess } from "node:child_process"; import { type CollectedPair } from "./build-snapshot.js"; import { type BuildSample } from "./build-verdict.js"; import { type ProcessTableSample } from "./process-tree.js"; import type { TrendResult } from "./trend.js"; export type WorkerSeries = { pid: number; samples: BuildSample[]; }; export type BuildCapture = { pid: number; files: CollectedPair; baselineRssBytes: number; afterRssBytes: number; /** * Peak resident memory of *this* worker, not of the build. The share of * growth the pair covers is quoted against the curve the findings came * from, and on a multi-worker build the highest peak can belong to a worker * nothing was captured from. */ peakRssBytes: number; }; export type BuildRunResult = { appDir: string; /** * `measured` carries a verdict. `build-failed` means the build broke for a * reason that is not memory, and makes no memory claim. `nothing-to-measure` * means no static-generation worker ever ran. `cannot-sample` means the * process table could not be read, which is not the same as finding nothing * in it. */ status: "measured" | "build-failed" | "nothing-to-measure" | "cannot-sample"; /** Why sampling stopped, when it did. */ samplingFailure: string | null; verdict: TrendResult["verdict"] | null; trend: TrendResult | null; /** Segmented levels the verdict was read from, in bytes. */ levels: number[]; workers: WorkerSeries[]; /** The build's own process, reported but never judged: it sheds while workers climb. */ parentSamples: BuildSample[]; peakWorkerRssBytes: number; netGrowthBytes: number; pagesGenerated: number | null; retentionPerPageBytes: number | null; /** True when a worker hit the V8 heap limit — the finding, not a failure. */ heapExhausted: boolean; /** * The snapshot pair captured from a worker, when one was. Attribution is an * addition to this report, never a precondition for it: every field above is * produced whether or not capture worked. */ capture: BuildCapture | null; strippedCapWarning: string | null; exitCode: number | null; output: string; }; export type BuildRunOptions = { appDir: string; /** Where a captured snapshot pair is moved to. Capture is skipped without it. */ workDir?: string; signal?: AbortSignal; onProgress?: (message: string) => void; /** Overrides `process.env` for the build. */ env?: NodeJS.ProcessEnv; }; export type BuildRunDeps = { spawnBuild: (appDir: string, env: NodeJS.ProcessEnv) => ChildProcess; signalWorker: (pid: number) => void; sampleTable: () => Promise; now: () => number; sleep: (ms: number) => Promise; }; /** * Measures the memory of a `next build`'s static-generation workers. * * The build runs unmodified — nothing is injected into it. Workers are child * processes with their own resident memory, so the whole measurement is made * from outside by watching the process tree. */ export declare function runBuildMeasurement(options: BuildRunOptions, deps?: BuildRunDeps): Promise;