// The raw-bytes fast path (src/index.js's pushBytes) must agree with the string path at every // possible byte split, including splits that land inside a multi-byte or astral character, so // the held-back-tail logic in incompleteTailLen is exercised at every offset. Also covers a // leading BOM, split at every offset, and invalid UTF-8 (which must fall back, not misbehave). // Usage: node test/byte-chunks.mjs import { transformSync } from "../src/index.js"; const encoder = new TextEncoder(); const sources = [ "const x: number = 1;", "const s = '日本語 string with 漢字 and emoji 🎉🚀 inside';", "const u = '😀 full astral pair, repeated 😀😀';", "// café, naïve, Zürich — latin-1-ish accents\nconst a: string = 'café';", "const bom = 1;", // BOM is prepended to the bytes below for this one "let a: {[" , // unterminated pattern, from chunk-offsets.mjs ]; function run(mode, parts) { const errors = []; const onError = (e) => errors.push(e.start); if (parts.length === 1) { try { return { out: transformSync(parts[0], { mode, onError }), errors }; } catch (e) { return { threw: e.message }; } } try { let out = ""; for (const piece of transformSync(parts, { mode, onError })) out += piece; return { out, errors }; } catch (e) { return { threw: e.message }; } } const same = (a, b) => a.threw === b.threw && a.out === b.out && a.errors?.join() === b.errors?.join(); let checked = 0, splits = 0; const failures = []; for (const src of sources) { const bom = src === "const bom = 1;"; const bytes = encoder.encode((bom ? "" : "") + src); for (const mode of ["erase", "downlevel"]) { const whole = run(mode, [src]); checked++; for (let k = 0; k <= bytes.length; k++) { splits++; const split = run(mode, [bytes.subarray(0, k), bytes.subarray(k)]); if (!same(whole, split)) { failures.push(`${JSON.stringify(src.slice(0, 30))} (${mode}) byte-split at ${k}/${bytes.length}: got ${JSON.stringify(split)} want ${JSON.stringify(whole)}`); break; } } } } // Invalid UTF-8 mid-stream: must fall back to the decode path, not throw or corrupt output. { const good = encoder.encode("const x: number = 1; // "); const bad = new Uint8Array([0xff, 0xfe, 0x80, 0x80]); // never valid UTF-8 const tail = encoder.encode(" done();"); const expected = run("erase", ["const x: number = 1; // ���� done();"]); const got = run("erase", [good, bad, tail]); checked++; if (!same(expected, got)) failures.push(`invalid UTF-8 fallback: got ${JSON.stringify(got)} want ${JSON.stringify(expected)}`); } for (const f of failures.slice(0, 20)) console.log("FAIL " + f); console.log(`byte-chunks: ${checked} input/mode pairs, ${splits} splits, ${failures.length} fail`); process.exitCode = failures.length ? 1 : 0;