Skip to content

fix(nitro): stop the v3 path importing the optional h3 peer#373

Merged
HugoRCD merged 2 commits into
HugoRCD:mainfrom
jmcgoldrick:fix/v3-h3-optional-peer-leak
Jun 11, 2026
Merged

fix(nitro): stop the v3 path importing the optional h3 peer#373
HugoRCD merged 2 commits into
HugoRCD:mainfrom
jmcgoldrick:fix/v3-h3-optional-peer-leak

Conversation

@jmcgoldrick

@jmcgoldrick jmcgoldrick commented Jun 11, 2026

Copy link
Copy Markdown
Contributor

🔗 Linked issue

Closes #372

📚 Description

The v3 plugin imports extendDeferredDrain from nitro/enrich-drain, whose top-level import { getHeaders } from 'h3' is for the v2 runtime — so the v3 bundle referenced the optional h3 peer even though v3 never uses it (it reads event.req.headers). Consumers without a direct h3 dep then failed to build (#372).

Moved the h3-free extendDeferredDrain into its own nitro/deferred-drain module; both the v3 plugin and v2 enrich-drain import it from there. The v2 path keeps its h3 import; the v3 bundle no longer references h3.

Added a regression test that walks the built v3 plugin's import graph and asserts it never reaches h3. No peer ranges changed; patch changeset included.

📝 Checklist

  • I have linked an issue or discussion.
  • I have updated the documentation accordingly.

Summary by CodeRabbit

  • Bug Fixes

    • Fixed an issue where the v3 bundle could pull in the optional h3 peer, preventing build failures for projects without h3 installed.
    • Improved background drain handling so drains no longer block responses; background failures are logged without impacting runtime.
  • Tests

    • Added a regression test to ensure the v3 bundle remains isolated from h3.

Copilot AI review requested due to automatic review settings June 11, 2026 02:52
@vercel

vercel Bot commented Jun 11, 2026

Copy link
Copy Markdown

@jmcgoldrick is attempting to deploy a commit to the HRCD Projects Team on Vercel.

A member of the Team first needs to authorize it.

@github-actions github-actions Bot added the bug Something isn't working label Jun 11, 2026
@github-actions

github-actions Bot commented Jun 11, 2026

Copy link
Copy Markdown
Contributor

Thank you for following the naming conventions! 🙏

@coderabbitai

coderabbitai Bot commented Jun 11, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Caution

Review failed

Pull request was closed or merged during review

📝 Walkthrough

Walkthrough

This PR prevents the v3 Nitro plugin from pulling the optional h3 peer by extracting extendDeferredDrain to an h3-free module, updating the v3 plugin import to use it, adding a regression test that inspects built v3 chunks for h3 imports, and recording the change in a changeset.

Changes

h3 Optional Peer Dependency Isolation

Layer / File(s) Summary
Extract h3-free deferred-drain helper
packages/evlog/src/nitro/deferred-drain.ts, packages/evlog/src/nitro/enrich-drain.ts
New deferred-drain.ts module exports extendDeferredDrain without importing h3; enrich-drain.ts imports it instead of defining it locally.
Route v3 plugin to h3-free deferred-drain
packages/evlog/src/nitro-v3/plugin.ts
Update the import source of extendDeferredDrain from ../nitro/enrich-drain to ../nitro/deferred-drain so the v3 bundle no longer pulls h3 transitively.
Add h3 peer isolation regression test
packages/evlog/test/nitro-v3/h3-peer-isolation.test.ts
New Vitest that resolves the built plugin.mjs, traverses relative imports to collect reachable chunks, and asserts none contain bare h3 or h3/* imports; test is skipped with a warning if the dist entry is missing.
Changeset documentation
.changeset/fix-v3-h3-optional-peer-leak.md
Patch release entry documenting that evlog/nitro/v3 no longer references the optional h3 peer.

🎯 2 (Simple) | ⏱️ ~12 minutes

Suggested labels

bug

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 40.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and specifically summarizes the main change: preventing the v3 Nitro plugin from importing the optional h3 peer dependency.
Description check ✅ Passed The description covers the linked issue, explains the problem, details the solution, and confirms the checklist items, following the template structure adequately.
Linked Issues check ✅ Passed All code changes directly address issue #372: moving extendDeferredDrain to a new h3-free module, updating imports in v3 plugin, and adding a regression test to prevent v3 from referencing h3.
Out of Scope Changes check ✅ Passed All changes are directly scoped to fixing the h3 peer leak in the v3 plugin; no unrelated modifications were introduced.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint install timed out. The project may have too many dependencies for the sandbox.


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

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

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Note

Copilot was unable to run its full agentic suite in this review.

This PR fixes an evlog/nitro/v3 bundling regression where the v3 plugin could accidentally pull in the optional h3 peer by reusing a helper from the v2 module, and adds a regression test to prevent reintroducing the issue.

Changes:

  • Extracted extendDeferredDrain into an h3-free module so v3 imports don’t traverse the v2 h3 dependency.
  • Updated the v3 Nitro plugin to import the helper from the new module.
  • Added a built-output regression test ensuring the v3 plugin’s import graph never reaches h3, plus a patch changeset.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
packages/evlog/test/nitro-v3/h3-peer-isolation.test.ts Adds a dist-based regression test that asserts h3 isn’t imported from the built v3 plugin graph.
packages/evlog/src/nitro/enrich-drain.ts Stops exporting the helper inline and imports it from the new module.
packages/evlog/src/nitro/deferred-drain.ts New h3-free module hosting extendDeferredDrain.
packages/evlog/src/nitro-v3/plugin.ts Switches v3 plugin to import the helper from the new module (avoids h3).
.changeset/fix-v3-h3-optional-peer-leak.md Documents the fix and bumps evlog as a patch.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread packages/evlog/test/nitro-v3/h3-peer-isolation.test.ts Outdated
Comment thread packages/evlog/test/nitro-v3/h3-peer-isolation.test.ts Outdated
Comment thread packages/evlog/test/nitro-v3/h3-peer-isolation.test.ts
6dc352d pointed the v3 plugin at extendDeferredDrain in nitro/enrich-drain,
which imports h3 (getHeaders) for v2 — leaking the optional h3 peer into the
v3 bundle. Split the helper into an h3-free nitro/deferred-drain module.
@jmcgoldrick jmcgoldrick force-pushed the fix/v3-h3-optional-peer-leak branch from ab3ad23 to d78a3e7 Compare June 11, 2026 02:59

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/evlog/test/nitro-v3/h3-peer-isolation.test.ts`:
- Line 17: The global regex relativeRe is created with the /g flag and reused
across recursive calls to walk(), causing exec()'s lastIndex to carry over and
skip matches; fix by ensuring a fresh regex or resetting state before each
use—either recreate the pattern inside the walk() function (e.g., new RegExp or
re-declare relativeRe) or set relativeRe.lastIndex = 0 immediately before the
while (relativeRe.exec(...)) loop; update the code around the walk() function
and the import-matching while loop so nested recursive calls don't inherit a
stale lastIndex.
- Line 31: The resolver call inside the import-walking logic (the
walk(resolve(dirname(file), match[1] ?? match[2])) invocation) doesn't add an
extension for relative imports, so change the resolution to detect if the
matched path has no extension (use path.extname on match[1] ?? match[2]) and, if
missing, append ".mjs" before calling walk; update the same computed resolved
path used for readFileSync to ensure files built as .mjs are found (keep using
dirname(file) and the match variable names to locate the change).
- Around line 51-63: The current test builds a forbidden array and uses
expect(src, file).not.toContain(...) which misses h3 subpath imports like from
"h3/utils" or import("h3/router"); update the test around the forbidden array
and the loop that reads chunks (symbols: forbidden, chunks, readFileSync,
expect(...).not.toContain) to either expand forbidden to include subpath
patterns or—preferably—replace the string containment check with a regex match
that rejects any import/use of h3 or h3/* (e.g. test src against a regex for
from|import followed by 'h3' optionally followed by a slash) and assert that the
regex does not match.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: ce7ff6dd-2dac-4489-a462-ad00ef4dad43

📥 Commits

Reviewing files that changed from the base of the PR and between ab3ad23 and d78a3e7.

📒 Files selected for processing (5)
  • .changeset/fix-v3-h3-optional-peer-leak.md
  • packages/evlog/src/nitro-v3/plugin.ts
  • packages/evlog/src/nitro/deferred-drain.ts
  • packages/evlog/src/nitro/enrich-drain.ts
  • packages/evlog/test/nitro-v3/h3-peer-isolation.test.ts

Comment thread packages/evlog/test/nitro-v3/h3-peer-isolation.test.ts
}
let match: RegExpExecArray | null
while ((match = relativeRe.exec(source))) {
walk(resolve(dirname(file), match[1] ?? match[2]))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Add .mjs extension when resolving relative imports.

Built chunks use the .mjs extension, but line 31 resolves bare paths like ./foo without appending .mjs. The subsequent readFileSync on line 24 will fail (silently caught on line 26), causing the walk to miss reachable chunks that might import h3.

🐛 Proposed fix: append `.mjs` if no extension present
     let match: RegExpExecArray | null
     while ((match = relativeRe.exec(source))) {
-      walk(resolve(dirname(file), match[1] ?? match[2]))
+      const relPath = match[1] ?? match[2]
+      const absPath = resolve(dirname(file), relPath)
+      walk(absPath.endsWith('.mjs') ? absPath : `${absPath}.mjs`)
     }
   }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/evlog/test/nitro-v3/h3-peer-isolation.test.ts` at line 31, The
resolver call inside the import-walking logic (the walk(resolve(dirname(file),
match[1] ?? match[2])) invocation) doesn't add an extension for relative
imports, so change the resolution to detect if the matched path has no extension
(use path.extname on match[1] ?? match[2]) and, if missing, append ".mjs" before
calling walk; update the same computed resolved path used for readFileSync to
ensure files built as .mjs are found (keep using dirname(file) and the match
variable names to locate the change).

Comment thread packages/evlog/test/nitro-v3/h3-peer-isolation.test.ts
@jmcgoldrick

Copy link
Copy Markdown
Contributor Author

thanks copilot and code rabbit, good finds! Resolved and test made more consistent with others while I was at it

@HugoRCD HugoRCD merged commit 4c51970 into HugoRCD:main Jun 11, 2026
13 of 16 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[bug] evlog/nitro/v3 build fails without h3 dep

3 participants