import { createHash } from "node:crypto"; import { mkdir, readFile, writeFile } from "node:fs/promises"; import path from "node:path"; import { ensureWithinProject } from "./native-water-build-utils.mjs"; const sha256 = (bytes) => createHash("sha256").update(bytes).digest("hex"); async function writeOrVerify(file, bytes, expectedHash) { await mkdir(path.dirname(file), { recursive: true }); try { await writeFile(file, bytes, { flag: "wx" }); } catch (error) { if (error.code !== "EEXIST") throw error; } const persisted = await readFile(file); const actualHash = sha256(persisted); if (actualHash !== expectedHash || !persisted.equals(bytes)) { throw new Error(`Refusing to replace non-identical native water audit-cache alias: ${file}`); } return { sha256: actualHash, bytes: persisted.length }; } /** Write exact source and prepared-frame aliases for the environment-quality audit. */ export async function writeNativeWaterAuditCacheAliases({ projectRoot, auditCacheRoot, variantName, ddsBytes, pngBytes, }) { if (!auditCacheRoot) return null; if (!/^[a-z\d][a-z\d_-]*$/i.test(variantName)) { throw new Error(`Native water audit alias must be a safe texture basename: ${variantName}`); } ensureWithinProject(projectRoot, auditCacheRoot); const root = path.resolve(auditCacheRoot); const ddsHash = sha256(ddsBytes); const pngHash = sha256(pngBytes); const ddsFile = path.join(root, "raw", "textures", `${variantName}.dds`); const pngFile = path.join(root, "prepared", "textures", `${variantName}.png`); const [dds, png] = await Promise.all([ writeOrVerify(ddsFile, ddsBytes, ddsHash), writeOrVerify(pngFile, pngBytes, pngHash), ]); const relative = (file) => path.relative(projectRoot, file).replaceAll("\\", "/"); return { textureBasename: variantName, rawDds: { path: relative(ddsFile), ...dds }, preparedPng: { path: relative(pngFile), ...png }, }; }