/*
RFC-0656: Stable-mode dispatcher — selects the correct stable normalizer by file extension. Returns null for unhandled extensions (caller falls back to byte hash).
- Do not implement individual normalizers — each lives in its own file.
- Do not handle semantic-mode normalization — that lives in normalizers/index.ts.
- RFC-0656: initial stable-mode dispatcher.
*/
import path from "node:path";
import { byteHash } from "@warpgogol/werkstatt-shared/fingerprint";
import { normalizePdf } from "./pdf.ts";
import { normalizeSourceMap } from "./sourcemap.ts";
import { normalizeJsonStable } from "./json-stable.ts";
export interface StableNormalizeResult {
normalizer: string;
hash: string;
}
const SOURCE_MAP_EXTENSIONS = new Set([".map"]);
export async function normalizeFileStable(
absPath: string,
bytes: Uint8Array,
distRoot?: string,
): Promise {
const ext = path.extname(absPath).toLowerCase();
const lowerPath = absPath.toLowerCase();
if (ext === ".pdf") {
const normalized = await normalizePdf(bytes);
return { normalizer: "pdf", hash: byteHash(normalized) };
}
if (SOURCE_MAP_EXTENSIONS.has(ext)) {
const content = new TextDecoder("utf-8", { fatal: false }).decode(bytes);
return { normalizer: "sourcemap", hash: normalizeSourceMap(content, distRoot) };
}
if (ext === ".json" || lowerPath.endsWith(".well-known/build-identity.json")) {
const content = new TextDecoder("utf-8", { fatal: false }).decode(bytes);
return { normalizer: "json-stable", hash: normalizeJsonStable(content) };
}
return null;
}