SIGN IN SIGN UP

fix(js-exec): honor encoding arg in Buffer.from / toString / write / byteLength (#256)

* fix(js-exec): honor encoding arg in Buffer.from / toString / write / byteLength

Add pure-JS encoders/decoders for base64, base64url, hex, latin1/binary,
ascii, and utf16le inside BUFFER_MODULE_SOURCE. Dispatch on a normalized
encoding string in Buffer.from, Buffer.prototype.toString,
Buffer.prototype.write, and Buffer.byteLength so the QuickJS shim matches
real Node.js Buffer semantics for every encoding Node supports.

- _normEnc: normalizes encoding aliases (binary→latin1, ucs2→utf16le, etc.)
- _hexEncode/_hexDecode: lenient hex (stops at first invalid char)
- _latin1Encode/_latin1Decode, _asciiEncode/_asciiDecode
- _utf16leEncode/_utf16leDecode
- _b64Encode/_b64UrlEncode/_b64Decode: forgiving base64 (ignores whitespace,
  tolerates missing padding, supports base64url alphabet)

Adds js-exec.buffer-encoding.test.ts with 29 cases covering encode-only,
decode-only, round-trips, binary bytes (0xff/0xfe/0x00), alias names, and
forgiving inputs. Adds a guardrail comment above _normEnc explaining why
round-trip tests alone would mask a broken encoding implementation.

* perf(js-exec): compute base64 byteLength in O(1) without decoding

Replace _b64Decode(value).length with a length-based formula
(strip non-alphabet + padding, then floor(len * 3 / 4)). Matches
Node's Buffer.byteLength semantics and avoids allocating an array
just to count bytes.

* refactor(js-exec): consolidate ascii/latin1 encode and fix Buffer shim correctness

- Replace _latin1Encode/_asciiEncode with shared _rawEncode (both truncate to low byte; encode semantics are identical)
- Fix Buffer.from(ArrayBuffer, byteOffset, length) to respect offset and length args
- Fix Buffer.byteLength to throw TypeError for non-string/non-Buffer input instead of returning 0
- Fix Buffer.byteLength base64 regex to correctly handle mid-string '=' as terminator
- Fix Buffer.prototype.toString to clamp negative start to 0, matching Node behavior
- Fix Buffer.prototype.write to throw RangeError for negative/out-of-range offset

* chore: bump patch version for changeset

* fix(js-exec): Buffer shim ArrayBuffer sharing, write length validation, toString negative end

- Buffer.from(ArrayBuffer, offset, length) now shares the backing store as Node does
  (previously the inner Uint8Array was copied through the Buffer constructor).
- Buffer.prototype.write throws RangeError for negative length or length > remaining,
  matching Node's ERR_OUT_OF_RANGE rather than silently clamping.
- Buffer.prototype.toString clamps negative end to 0 (Node behavior), so
  Buffer.from("abc").toString("utf8", 0, -1) returns "" instead of "ab".

Adds edge-case tests covering all three behaviors.

* fix(js-exec): validate Buffer.write length against total size, clamp writes to remaining

Per vercel bot review: Node validates the `length` arg against the buffer's
total size, not the remaining space after `offset`, and clamps actual writes
to the remaining space. The previous fix validated against remaining, which
incorrectly threw for cases like `Buffer.alloc(5).write('abcde', 3, 5)` that
Node accepts (writing 2 bytes).

Adds a regression test for the offset+length clamping case.
H
Harry Nguyen committed
75d8dfd3a322786250e3b0f81b1500c87610acb7
Parent: 4ece258
Committed by GitHub <noreply@github.com> on 6/4/2026, 5:30:16 PM