{"version":3,"file":"fixtures.d.ts","sourceRoot":"","sources":["../../../src/core/evaluation/fixtures.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AAIxD,MAAM,WAAW,mBAAmB;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,OAAO,CAAC;CAClB;AAiCD,wBAAsB,kBAAkB,CACvC,IAAI,EAAE,qBAAqB,EAC3B,OAAO,GAAE;IAAE,eAAe,CAAC,EAAE,OAAO,CAAA;CAAO,GACzC,OAAO,CAAC,mBAAmB,CAAC,CAuC9B;AAED,wBAAsB,cAAc,CAAC,OAAO,EAAE,mBAAmB,GAAG,OAAO,CAAC,IAAI,CAAC,CAEhF","sourcesContent":["import { execFile } from \"node:child_process\";\nimport { mkdir, mkdtemp, readdir, readFile, rm, stat, writeFile } from \"node:fs/promises\";\nimport { tmpdir } from \"node:os\";\nimport { dirname, isAbsolute, join, relative, resolve, sep } from \"node:path\";\nimport { promisify } from \"node:util\";\nimport { hashJson, sha256 } from \"./identity.js\";\nimport type { EvaluationFixtureSpec } from \"./types.js\";\n\nconst execFileAsync = promisify(execFile);\n\nexport interface MaterializedFixture {\n\troot: string;\n\tfixtureHash: string;\n\tretained: boolean;\n}\n\nfunction assertRelativePath(root: string, candidate: string): string {\n\tif (isAbsolute(candidate)) throw new Error(`fixture path must be relative: ${candidate}`);\n\tconst target = resolve(root, candidate);\n\tconst rootWithSeparator = `${resolve(root)}${sep}`;\n\tif (target !== resolve(root) && !target.startsWith(rootWithSeparator))\n\t\tthrow new Error(`fixture path escapes root: ${candidate}`);\n\treturn target;\n}\n\nasync function copyTree(source: string, destination: string, sourceRoot: string): Promise<Record<string, string>> {\n\tconst hashes: Record<string, string> = {};\n\tconst entries = await readdir(source, { withFileTypes: true });\n\tfor (const entry of entries.sort((left, right) => left.name.localeCompare(right.name))) {\n\t\tconst sourcePath = join(source, entry.name);\n\t\tconst relativePath = relative(sourceRoot, sourcePath);\n\t\tconst destinationPath = assertRelativePath(destination, relativePath);\n\t\tif (entry.isSymbolicLink()) throw new Error(`fixture symlinks are not permitted: ${relativePath}`);\n\t\tif (entry.isDirectory()) {\n\t\t\tawait mkdir(destinationPath, { recursive: true });\n\t\t\tObject.assign(hashes, await copyTree(sourcePath, destination, sourceRoot));\n\t\t\tcontinue;\n\t\t}\n\t\tif (!entry.isFile()) throw new Error(`unsupported fixture entry: ${relativePath}`);\n\t\tconst content = await readFile(sourcePath);\n\t\tawait mkdir(dirname(destinationPath), { recursive: true });\n\t\tawait writeFile(destinationPath, content, { flag: \"wx\" });\n\t\thashes[relativePath] = sha256(content);\n\t}\n\treturn hashes;\n}\n\nexport async function materializeFixture(\n\tspec: EvaluationFixtureSpec,\n\toptions: { retainOnFailure?: boolean } = {},\n): Promise<MaterializedFixture> {\n\tconst root = await mkdtemp(join(tmpdir(), \"jensen-eval-\"));\n\ttry {\n\t\tlet hashes: Record<string, string> = {};\n\t\tif (spec.kind === \"inline\" || spec.kind === \"generated\" || spec.kind === \"provider_trace\") {\n\t\t\tfor (const [filePath, contents] of Object.entries(spec.files ?? {}).sort(([left], [right]) =>\n\t\t\t\tleft.localeCompare(right),\n\t\t\t)) {\n\t\t\t\tconst destination = assertRelativePath(root, filePath);\n\t\t\t\tawait mkdir(dirname(destination), { recursive: true });\n\t\t\t\tawait writeFile(destination, contents, { flag: \"wx\" });\n\t\t\t\thashes[filePath] = sha256(contents);\n\t\t\t}\n\t\t} else {\n\t\t\tif (!spec.root) throw new Error(\"local fixture root is required\");\n\t\t\tconst source = resolve(spec.root);\n\t\t\tconst sourceStat = await stat(source);\n\t\t\tif (!sourceStat.isDirectory()) throw new Error(\"local fixture root must be a directory\");\n\t\t\thashes = await copyTree(source, root, source);\n\t\t}\n\t\tfor (const [filePath, expectedHash] of Object.entries(spec.fileHashes ?? {})) {\n\t\t\tif (hashes[filePath] !== expectedHash) throw new Error(`fixture hash mismatch for ${filePath}`);\n\t\t}\n\t\tif (spec.git?.initialize) {\n\t\t\tawait execFileAsync(\"git\", [\"init\", \"--quiet\", \"--initial-branch\", spec.git.branch ?? \"main\"], { cwd: root });\n\t\t\tawait execFileAsync(\"git\", [\"config\", \"user.email\", \"evaluation@localhost\"], { cwd: root });\n\t\t\tawait execFileAsync(\"git\", [\"config\", \"user.name\", \"Jensen Evaluation\"], { cwd: root });\n\t\t\tawait execFileAsync(\"git\", [\"add\", \"--all\"], { cwd: root });\n\t\t\tawait execFileAsync(\"git\", [\"commit\", \"--quiet\", \"--no-gpg-sign\", \"-m\", \"fixture\"], { cwd: root });\n\t\t}\n\t\treturn {\n\t\t\troot,\n\t\t\tfixtureHash: hashJson({ files: hashes, git: spec.git }),\n\t\t\tretained: options.retainOnFailure === true,\n\t\t};\n\t} catch (error) {\n\t\tif (!options.retainOnFailure) await rm(root, { recursive: true, force: true });\n\t\tthrow error;\n\t}\n}\n\nexport async function cleanupFixture(fixture: MaterializedFixture): Promise<void> {\n\tif (!fixture.retained) await rm(fixture.root, { recursive: true, force: true });\n}\n"]}