"use client"; // SSMS saves .sqlplan as UTF-16LE with a BOM; other tools write UTF-8 (sometimes with a BOM), and a few write // UTF-16BE or BOM-less UTF-16. File.text() assumes UTF-8, which turns a UTF-16 plan into NUL-filled junk that // fails to parse with a message blaming the user's export. BOM first, then a NUL-density guess. export function decodePlan(buf: Uint8Array): string { const enc = utfEncoding(buf); return new TextDecoder(enc).decode(buf).replace(/^\uFEFF/, ""); } function utfEncoding(buf: Uint8Array): "utf-8" | "utf-16le" | "utf-16be" | "utf-32le" | "utf-32be" { const b = (i: number) => buf[i] ?? 0; if (b(0) === 0xff && b(1) === 0xfe && b(2) === 0 && b(3) === 0) return "utf-32le"; if (b(0) === 0 && b(1) === 0 && b(2) === 0xfe && b(3) === 0xff) return "utf-32be"; if (b(0) === 0xff && b(1) === 0xfe) return "utf-16le"; if (b(0) === 0xfe && b(1) === 0xff) return "utf-16be"; // BOM-less UTF-16 of ASCII XML: in the first 200 bytes roughly every other byte is 0x00. const n = Math.min(buf.length - (buf.length % 2), 200); if (n < 40) return "utf-8"; let even = 0, odd = 0; for (let i = 0; i < n; i += 2) { if (b(i) === 0) even++; if (b(i + 1) === 0) odd++; } if (odd > n / 6) return "utf-16le"; if (even > n / 6) return "utf-16be"; return "utf-8"; }