export async function sha256Hex(input: string): Promise { const bytes = new TextEncoder().encode(input); const digest = await crypto.subtle.digest("SHA-256", bytes); return [...new Uint8Array(digest)] .map((b) => b.toString(16).padStart(2, "0")) .join(""); } export async function shortHash(input: string): Promise { return (await sha256Hex(input)).slice(0, 16); } /** Deterministic JSON with sorted keys, so hashing is stable regardless of key order. */ export function stableStringify(value: unknown): string { if (value === null || typeof value !== "object") return JSON.stringify(value) ?? "null"; if (Array.isArray(value)) return `[${value.map(stableStringify).join(",")}]`; const obj = value as Record; return `{${Object.keys(obj) .sort() .map((k) => `${JSON.stringify(k)}:${stableStringify(obj[k])}`) .join(",")}}`; }