Issue 243 Text Encoder Dependency Removal Implementation Plan

For Claude: REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.

Goal: Remove @sinonjs/text-encoding from fake XHR response handling by replacing its lone TextEncoder usage with a small dependency-free UTF-8 helper, while preserving current arraybuffer and blob behavior.

Architecture: Keep the change local to the fake XHR response conversion path in lib/fake-xhr/index.js. String response bodies should always be encoded as UTF-8, ArrayBuffer inputs should still pass through unchanged, and tests should assert exact byte output without depending on the deprecated polyfill.

Tech Stack: Node.js, Mocha, referee, browserify, nise fake XHR implementation


Task 1: Reproduce and pin the current string-to-buffer behavior

Files: - Modify: lib/fake-xhr/index.test.js - Test: lib/fake-xhr/index.test.js

Step 1: Add a focused failing test for UTF-8 arraybuffer conversion

Add a test near the existing responseType === "arraybuffer" coverage that asserts exact UTF-8 bytes for a non-ASCII string without using TextEncoder.

it("encodes string responses as utf-8 arraybuffers", function () {
    this.xhr.responseType = "arraybuffer";
    this.xhr.open("GET", "/");
    this.xhr.send();

    this.xhr.respond(
        200,
        { "Content-Type": "application/octet-stream" },
        "\xFF",
    );

    assertArrayBufferMatches(
        this.xhr.response,
        new Uint8Array([0xc3, 0xbf]).buffer,
    );
});

Step 2: Run the focused test to verify it passes before refactoring

Run: npm test -- --grep "encodes string responses as utf-8 arraybuffers" Expected: PASS on the current branch, proving the existing behavior is already UTF-8.

Step 3: Add a narrow regression test for ASCII strings if the current suite is not explicit enough

it("encodes ascii string responses as identical byte values", function () {
    this.xhr.responseType = "arraybuffer";
    this.xhr.open("GET", "/");
    this.xhr.send();

    this.xhr.respond(
        200,
        { "Content-Type": "application/octet-stream" },
        "a test buffer",
    );

    assertArrayBufferMatches(
        this.xhr.response,
        new Uint8Array([
            97, 32, 116, 101, 115, 116, 32, 98, 117, 102, 102, 101, 114,
        ]).buffer,
    );
});

Step 4: Run the focused ASCII test

Run: npm test -- --grep "encodes ascii string responses as identical byte values" Expected: PASS

Step 5: Commit

git add lib/fake-xhr/index.test.js
git commit -m "test: pin utf8 arraybuffer response behavior"

Task 2: Replace the deprecated TextEncoder dependency with a local helper

Files: - Modify: lib/fake-xhr/index.js - Test: lib/fake-xhr/index.test.js

Step 1: Write a failing unit-level regression around the old dependency seam

If Task 1 does not already give enough protection, add a test that exercises a code path which previously used GlobalTextEncoder indirectly through respond(..., "string body").

it("does not require an external text-encoding polyfill for string bodies", function () {
    this.xhr.responseType = "arraybuffer";
    this.xhr.open("GET", "/");
    this.xhr.send();

    this.xhr.respond(200, {}, "hola");

    assertArrayBufferMatches(
        this.xhr.response,
        new Uint8Array([104, 111, 108, 97]).buffer,
    );
});

Step 2: Implement a narrow UTF-8 helper in lib/fake-xhr/index.js

Remove the top-level GlobalTextEncoder fallback and replace it with a local helper. Keep it intentionally UTF-8-only.

Example direction:

function stringToUtf8ArrayBuffer(input) {
    return Uint8Array.from(Buffer.from(input, "utf8")).buffer;
}

If Buffer is not acceptable for browser-facing code paths, use a small pure-JS UTF-8 encoder instead. In either case, the helper should not accept an encoding parameter.

Step 3: Simplify convertToArrayBuffer

Change it from:

function convertToArrayBuffer(body, encoding) {
    if (body instanceof ArrayBuffer) {
        return body;
    }

    return new GlobalTextEncoder(encoding || "utf-8").encode(body).buffer;
}

to logic equivalent to:

function convertToArrayBuffer(body) {
    if (body instanceof ArrayBuffer) {
        return body;
    }

    return stringToUtf8ArrayBuffer(body);
}

Do not preserve the unused encoding parameter.

Step 4: Run the focused arraybuffer tests

Run: npm test -- --grep "arraybuffer|utf-8|utf8" Expected: PASS

Step 5: Commit

git add lib/fake-xhr/index.js lib/fake-xhr/index.test.js
git commit -m "fix: replace deprecated text encoder dependency"

Task 3: Remove the package dependency and update generated artifacts

Files: - Modify: package.json - Modify: package-lock.json - Modify: nise.js

Step 1: Remove @sinonjs/text-encoding from runtime dependencies

Delete the dependency entry from package.json.

Step 2: Refresh the lockfile

Run: npm install Expected: package-lock.json no longer contains @sinonjs/text-encoding.

Step 3: Rebuild the browser bundle

Run: npm run bundle Expected: nise.js rebuilds successfully without references to the removed dependency.

Step 4: Verify the dependency is fully gone

Run: rg -n "@sinonjs/text-encoding|GlobalTextEncoder|new TextEncoder\\(" . Expected: no remaining production references; only historical mention may remain in the plan docs.

Step 5: Commit

git add package.json package-lock.json nise.js
git commit -m "build: remove text-encoding dependency"

Task 4: Verify blob and binary regressions

Files: - Test: lib/fake-xhr/index.test.js

Step 1: Run the full fake XHR test file

Run: npm test -- lib/fake-xhr/index.test.js Expected: PASS

Step 2: Run the full test suite

Run: npm test Expected: PASS

Step 3: Run headless browser tests because the removed dependency affected bundled code

Run: npm run test-headless Expected: PASS

Step 4: Run lint if helper code changed control flow or globals

Run: npm run lint Expected: PASS

Step 5: Inspect the final diff

Run: git diff --stat Expected: changes limited to lib/fake-xhr/index.js, lib/fake-xhr/index.test.js, package.json, package-lock.json, nise.js, and the plan docs.

Task 5: Final review and merge readiness

Files: - Modify: docs/plans/2026-03-03-issue-243-text-encoder-design.md - Modify: docs/plans/2026-03-03-issue-243-text-encoder.md

Step 1: Review the implementation against issue #243 comments

Confirm the finished patch satisfies the useful parts of the issue discussion:

  • deprecated dependency removed;
  • no new runtime dependency introduced;
  • UTF-8-only behavior made explicit;
  • no unnecessary TextDecoder work added.

Step 2: Summarize any intentional non-goals in the PR description

Include a short note that the fix does not add non-UTF-8 support because the old implementation never truly supported it either.

Step 3: Prepare a concise verification summary

Capture the exact commands and results:

npm test
npm run test-headless
npm run lint
npm run bundle

Step 4: Commit the plan docs if they changed during execution notes

git add docs/plans/2026-03-03-issue-243-text-encoder-design.md docs/plans/2026-03-03-issue-243-text-encoder.md
git commit -m "docs: add issue 243 implementation plan"