Skip to content

fix(node): scope FastResponse subclass instanceof and avoid nesting as global Response - #330

Merged
pi0 merged 2 commits into
mainfrom
fix/global-fast-res
Oct 23, 2026
Merged

pi0 merged 2 commits into
mainfrom
fix/global-fast-res

Conversation

@pi0x

@pi0x pi0x commented Oct 23, 2026 •

Copy link
Copy Markdown
Contributor

Resolves #329

When FastResponse is installed as globalThis.Response (Nitro on Node, nitrojs/nitro#4910):

  • Subclass instanceof: static [Symbol.hasInstance] was inherited by subclasses, so with class MyRes extends Response {} every native or fast response was instanceof MyRes. The widened check now only applies when this === NodeResponse; subclasses fall back to a prototype-chain check (same approach as patchGlobalRequest()).
  • Nesting on re-evaluation: NativeResponse was captured from globalThis.Response at module eval, so a re-evaluated srvx (Vite module runner reload, second srvx copy) wrapped the previous FastResponse, stacking layers per reload. Added getNativeResponse(), mirroring getNativeRequest(): caches under Symbol.for("srvx.nativeResponse") and unwraps classes marked static _srvx = true.

Tests in test/node-global-response.test.ts cover both (re-evaluation via vi.resetModules(), three generations).

🤖 Generated with AI assistant

Summary by CodeRabbit

  • Bug Fixes
    • Improved compatibility when using the response wrapper as the global Response, including more reliable subclass instanceof checks and response text handling across repeated module loads.

@pi0x
pi0x requested a review from pi0 as a code owner October 23, 2026 05:36
@vercel

vercel Bot commented Oct 23, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
srvx Ready Ready Preview Oct 23, 2026 5:39am UTC

Request Review

@coderabbitai

coderabbitai Bot commented Oct 23, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 37d6987c-815a-42cb-8c1f-0ebb9f9ba006
📥 Commits

Reviewing files that changed from the base of the PR and between cc446f5 and 4c492d9.

📒 Files selected for processing: 2
  • src/adapters/_node/response.ts
  • test/node-global-response.test.ts

Included review availability: This review used your included allowance. Your plan provides up to 4 included reviews per hour; 3 remain after this review.

📝 Walkthrough

NodeResponse now resolves the native Response constructor through a shared global cache and unwraps previously installed srvx response classes. Its Symbol.hasInstance behavior distinguishes checks for NodeResponse from checks for subclasses. Tests cover both behaviors.

Changes: NodeResponse behavior

Layer / File(s) Summary
Resolve and reuse the native Response constructor
src/adapters/_node/response.ts, test/node-global-response.test.ts
getNativeResponse() reads a shared cache or unwraps constructors marked _srvx before caching the native constructor. Tests cover repeated module evaluation and response text.
Scope subclass instance checks
src/adapters/_node/response.ts, test/node-global-response.test.ts
NodeResponse uses native instanceof for its own checks. Subclasses use a prototype-chain check. Tests cover native, FastResponse, and MyRes instances.

Priority: ⚪ Pending latest changes

Estimated code review effort: 2 (Simple) | ~10 minutes

Change: Bug fix

Merge Risk: ⚪ Minimal · up to 4c492

The reported test cleanup concern does not block the intended Response behavior. No actionable merge-blocking risk remains after normal checks.

Pre-merge checks: Passed 4 · Failed 1

Failed Failed checks: 1 warning

Check name Status Explanation Resolution
Docstring Coverage Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 2 functions across 2 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
Passed Passed checks: 4 passed
Check name Status Explanation
Description Check Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check Passed The title names the two changes in the PR: scoping the FastResponse subclass instanceof check and avoiding nested wrapping when FastResponse is installed as global Response. The raw summary and object…
Linked Issues check Passed Issue #329 has two coding objectives. (1) Subclass instanceof must not widen to all responses. In src/adapters/_node/response.ts, static [Symbol.hasInstance] now applies the widened check only w…
Out of Scope Changes check Passed The whole-PR diff against the merge base changes two files. src/adapters/_node/response.ts adds the kNativeResponse symbol, static _srvx = true, the scoped Symbol.hasInstance branch, and `getN…
Full details: Title check

Explanation

The title names the two changes in the PR: scoping the FastResponse subclass instanceof check and avoiding nested wrapping when FastResponse is installed as global Response. The raw summary and objectives confirm both changes in src/adapters/_node/response.ts and the new test file. The title is specific and matches the main change, so it passes.

Full details: Linked Issues check

Explanation

Issue #329 has two coding objectives. (1) Subclass instanceof must not widen to all responses. In src/adapters/_node/response.ts, static [Symbol.hasInstance] now applies the widened check only when this === NodeResponse. For any other this, it uses Object.prototype.isPrototypeOf.call(this.prototype, val). Tracing class MyRes extends Response {} at head: new Native("x") instanceof MyRes is false, new MyRes("x") instanceof MyRes is true, and new MyRes("x") instanceof FastResponse is true. These match the issue's expected results. (2) Re-evaluation must not wrap a previous FastResponse. The new getNativeResponse() reads the shared cache under Symbol.for("srvx.nativeResponse"), falls back to globalThis.Response, and unwraps classes with _srvx by walking Object.getPrototypeOf. It caches the result with ??=. NodeResponse sets static _srvx = true, and the symbol name matches the kNativeRequest pattern in src/adapters/_node/request.ts. test/node-global-response.test.ts covers both objectives. It checks subclass instanceof results and runs three generations via vi.resetModules(), asserting Object.getPrototypeOf(F3) is the native Response. Without the fix, the second and third generations would wrap the earlier class, so the test distinguishes the behavior. I did not read the full body of getNativeRequest(), so the symmetry claim rests on the matching symbol name only.

Full details: Out of Scope Changes check

Explanation

The whole-PR diff against the merge base changes two files. src/adapters/_node/response.ts adds the kNativeResponse symbol, static _srvx = true, the scoped Symbol.hasInstance branch, and getNativeResponse(). Each change maps directly to an objective in #329. test/node-global-response.test.ts is new and only tests those behaviors. The NodeResponse body, the _response getter, _toNodeResponse(), and the header and status logic are not changed in this diff. No unrelated behavior change appears in the reviewed hunks.

✨ Finishing Touches: 💡 1
📝 Generate docstrings: 💡
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

A rabbit checks each response class,
The native source stays clear.
Subclasses match their prototype paths,
No extra wrapper layers appear.
Fresh tests watch the text come through,
Then hop along with ears held high.

Comment @coderabbitai help to get the list of available commands.

@codecov

codecov Bot commented Oct 23, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@pkg-pr-new

pkg-pr-new Bot commented Oct 23, 2026 •

Copy link
Copy Markdown

Open in StackBlitz

npm i https://pkg.pr.new/srvx@330

commit: 37085df

@pi0
pi0 merged commit 008a954 into main Oct 23, 2026
17 checks passed
@pi0
pi0 deleted the fix/global-fast-res branch October 23, 2026 05:41

This branch was successfully deployed

1 active deployment
Preview — 37085df0 Deployed Oct 23, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Node: FastResponse breaks subclass instanceof and nests when used as global Response

2 participants