import { fileURLToPath } from "node:url"; import { access } from "node:fs/promises"; import { expect, it, vi } from "vitest"; import { SourceMapDecoder } from "../../../src/javascript/sourceMaps/SourceMapDecoder.js"; import { SOURCE_MAP_CODEC_MEMORY_LIMIT_BYTES, type SourceMapDecoderDependencies, } from "../../../src/javascript/sourceMaps/SourceMapCommand.js"; import { PrivateRuntimeRoot } from "../../../src/process/PrivateRuntimeRoot.js"; import { spawnOwnedProviderProcess } from "../../../src/process/ProviderProcess.js"; import { waitForProviderProcessReady } from "../../fixtures/providerProcess.js"; import { webSourceLocationArgs, webSourceLocationFixture, } from "../../fixtures/webSourceLocation.js"; const realDecoder = (dependencies: SourceMapDecoderDependencies = {}) => new SourceMapDecoder({ ...dependencies, launcher: dependencies.launcher ?? ((options) => spawnOwnedProviderProcess({ ...options, arguments: options.arguments.map((argument) => argument.endsWith("/SourceMapCodecProcess.js") || argument.endsWith("\\SourceMapCodecProcess.js") ? fileURLToPath( new URL( "../../../dist/javascript/sourceMaps/SourceMapCodecProcess.js", import.meta.url, ), ) : argument, ), })), }); const fixture = webSourceLocationFixture(); const input = { text: fixture.sourceMap.text, url: fixture.sourceMap.url, path: fixture.sourceMap.file.path, position: webSourceLocationArgs.generated_position, }; it("starts the codec deadline after cold ownership preparation", async () => { vi.useFakeTimers(); try { let prepared = false; const responsePromise = realDecoder({ prepareOwnership: async (signal) => { await vi.advanceTimersByTimeAsync(20_001); expect(signal?.aborted).not.toBe(true); prepared = true; }, launcher: (options) => { expect(prepared).toBe(true); expect(options.signal?.aborted).not.toBe(true); return Promise.reject(new Error("observed launch after preparation")); }, }).trace(input); const response = await responsePromise; if (response.ok) throw new Error("expected launch failure"); expect(response.error).toMatchObject({ diagnostics: { error_message: "observed launch after preparation" }, }); } finally { vi.useRealTimers(); } }); it("runs the real pinned codec with an independent heap and removes its private request directory", async () => { let path: string | undefined; const environment = { ...process.env, NODE_OPTIONS: "--require=/rea-file-that-does-not-exist", }; const decoder = realDecoder({ environment, createRuntime: async () => { const root = await PrivateRuntimeRoot.create(); path = root.path; return root; }, }); const response = await decoder.trace(input); if (!response.ok) throw response.error; expect(response.value).toMatchObject({ engine: { id: "jridgewell-trace-mapping", version: "0.3.31" }, matches: [{ content: { text: "original" } }], }); if (process.versions.bun === undefined) { expect(response.value.runtime).toMatchObject({ id: "node-v8", version: process.version, }); expect(response.value.runtime.v8_heap_limit_bytes).toBeLessThanOrEqual( 256 * 1024 * 1024, ); } else expect(response.value.runtime).toEqual({ id: "bun", version: process.versions.bun, node_compatibility_version: process.versions.node, v8_heap_limit_bytes: null, // Windows enforces a Job Object commit limit in place of the heap flags. process_memory_limit_bytes: SOURCE_MAP_CODEC_MEMORY_LIMIT_BYTES, }); expect(environment.NODE_OPTIONS).toBe( "--require=/rea-file-that-does-not-exist", ); if (path === undefined) throw new Error("runtime not acquired"); await expect(access(path)).rejects.toMatchObject({ code: "ENOENT" }); }); it.each([ { text: "{", tag: "AnalysisInputError" }, { text: JSON.stringify({ version: 3, sections: [ { offset: { line: 0, column: 0 }, url: "https://app.test/external.map", }, ], }), tag: "AnalysisCapabilityUnavailableError", }, { text: JSON.stringify({ version: 3, sections: [{ offset: { line: 1000000000, column: 0 }, map: {} }], }), tag: "ArtifactOperationError", }, ])("preserves actual producer failures: $tag", async ({ text, tag }) => { const response = await realDecoder().trace({ ...input, text }); if (response.ok) throw new Error("expected failed map"); expect(response.error._tag).toBe(tag); }); it("reports cleanup failure with the actual owning operation", async () => { const response = await realDecoder({ createRuntime: async () => { const root = await PrivateRuntimeRoot.create(); return { path: root.path, close: async () => { await root.close(); throw new Error("observed cleanup failure"); }, }; }, }).trace(input); if (response.ok) throw new Error("expected cleanup failure"); expect(response.error).toMatchObject({ cleanupIncomplete: true, operation: "trace_web_source_location", diagnostics: { cleanup_failures: ["observed cleanup failure"] }, }); }); it("cancels a real owned process and removes the runtime request", async () => { const controller = new AbortController(); let path: string | undefined; const response = await realDecoder({ createRuntime: async () => { const root = await PrivateRuntimeRoot.create(); path = root.path; return root; }, launcher: async (options) => { const child = await spawnOwnedProviderProcess({ ...options, arguments: [ "-e", 'process.stdout.write("ready\\n"); setInterval(() => undefined, 1000)', ], }); await waitForProviderProcessReady(child.process); setImmediate(() => controller.abort()); return child; }, }).trace(input, { signal: controller.signal }); if (response.ok) throw new Error("expected cancellation"); expect(response.error._tag).toBe("AnalysisCancelledError"); if (path === undefined) throw new Error("runtime not acquired"); await expect(access(path)).rejects.toMatchObject({ code: "ENOENT" }); }); it("retains final diagnostic bytes for an actual failed codec process", async () => { const response = await realDecoder({ launcher: (options) => spawnOwnedProviderProcess({ ...options, arguments: [ "-e", 'process.stderr.write("final diagnostic"); process.exitCode = 7', ], }), }).trace(input); if (response.ok) throw new Error("expected exit failure"); expect(response.error).toMatchObject({ diagnostics: { exit_code: 7, stderr: "final diagnostic" }, }); }); it("reports complete evidence amplification as a resource failure before serializing an oversized reply", async () => { const text = JSON.stringify({ version: 3, sources: ["large.ts"], sourcesContent: ["a".repeat(600000)], names: [], mappings: Array.from({ length: 60 }, () => "AAAA").join(","), }); const response = await realDecoder().trace({ ...input, text }); if (response.ok) throw new Error("expected complete-evidence budget failure"); expect(response.error).toMatchObject({ _tag: "ArtifactOperationError", reason: "limit", detail: expect.stringContaining("no partial"), }); }); it("rejects a malformed actual subprocess reply", async () => { const response = await realDecoder({ launcher: (options) => spawnOwnedProviderProcess({ ...options, arguments: ["-e", 'process.stdout.write("not JSON")'], }), }).trace(input); if (response.ok) throw new Error("expected malformed reply"); expect(response.error._tag).toBe("AnalysisOutputError"); }); it("rejects a codec report from a runtime other than the launching one", async () => { const actual = await realDecoder().trace(input); if (!actual.ok) throw actual.error; const replay = (report: unknown) => realDecoder({ launcher: (options) => spawnOwnedProviderProcess({ ...options, arguments: [ "-e", `process.stdout.write(${JSON.stringify(JSON.stringify({ state: "success", report }))})`, ], }), }).trace(input); const unchanged = await replay(actual.value); if (!unchanged.ok) throw unchanged.error; expect(unchanged.value).toEqual(actual.value); const response = await replay({ ...actual.value, runtime: actual.value.runtime.id === "bun" ? { id: "node-v8", version: "v24.18.0", v8_heap_limit_bytes: 1 } : { id: "bun", version: "1.4.2", node_compatibility_version: "24.18.0", v8_heap_limit_bytes: null, process_memory_limit_bytes: null, }, }); if (response.ok) throw new Error("expected runtime mismatch"); expect(response.error._tag).toBe("AnalysisOutputError"); if (actual.value.runtime.id !== "bun") return; // A Bun codec must report exactly the process limit its launcher enforced. const limit = await replay({ ...actual.value, runtime: { ...actual.value.runtime, process_memory_limit_bytes: SOURCE_MAP_CODEC_MEMORY_LIMIT_BYTES === null ? 4096 : null, }, }); if (limit.ok) throw new Error("expected process limit mismatch"); expect(limit.error.message).toContain("process memory limit"); }); it.each([true, false])( "classifies a codec exit by the job memory notification (reached: %s)", async (reached) => { const response = await realDecoder({ launcher: async ({ memoryLimitBytes, ...options }) => { expect(memoryLimitBytes).toBe( SOURCE_MAP_CODEC_MEMORY_LIMIT_BYTES ?? undefined, ); // Without a job limit any artifact runs the child; its handle reports // the notification an owned Windows job records before exit. const spawned = await spawnOwnedProviderProcess({ ...options, arguments: [ "-e", 'process.stderr.write("panic: out of memory"); process.exit(3)', ], }); let exited = false; spawned.process.once("exit", () => { exited = true; }); Object.defineProperty(spawned.process, "memoryLimitReached", { get: () => reached && exited, }); return spawned; }, }).trace(input); if (response.ok) throw new Error("expected codec failure"); expect(response.error).toMatchObject( reached ? { _tag: "AnalysisResourceConstraintError", resource: "memory", reason: expect.stringContaining("process memory limit"), reportedLimits: { codec_process_memory_limit_bytes: SOURCE_MAP_CODEC_MEMORY_LIMIT_BYTES, }, capturedOutput: { stderr: "panic: out of memory" }, } : { diagnostics: { exit_code: 3, stderr: "panic: out of memory" } }, ); }, ); it("removes the private request when launching the codec fails", async () => { let path: string | undefined; const response = await realDecoder({ createRuntime: async () => { const root = await PrivateRuntimeRoot.create(); path = root.path; return root; }, launcher: () => Promise.reject(new Error("observed launch failure")), }).trace(input); if (response.ok) throw new Error("expected launch failure"); expect(response.error).toMatchObject({ diagnostics: { error_message: "observed launch failure" }, }); if (path === undefined) throw new Error("runtime not acquired"); await expect(access(path)).rejects.toMatchObject({ code: "ENOENT" }); });