Skip to content

feat(routing): migrate route rules to h3-rules - #4411

Merged
pi0 merged 16 commits into
mainfrom
feat/h3-rules
Jul 11, 2026
Merged

feat(routing): migrate route rules to h3-rules#4411
pi0 merged 16 commits into
mainfrom
feat/h3-rules

Conversation

@pi0x

@pi0x pi0x commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

This PR migrates Nitro route rules to use h3-rules

pi0 and others added 4 commits July 8, 2026 23:53
Route-rule types are now owned by the extracted `h3-rules` package. Re-export
`RouteRuleConfig`/`RouteRules`/`MatchedRouteRule(s)` from it, keep
`NitroRouteConfig`/`NitroRouteRules` as deprecated aliases, and augment
`h3-rules` with Nitro's `isr`/`prerender`/`static` rules.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Delegate `normalizeRouteRules` to `h3-rules` (the `/**` scope `base` field
replaces the internal `_redirectStripBase`/`_proxyStripBase` flags). Narrow the
netlify/cloudflare/vercel preset generators for the now-explicit
`redirect`/`proxy: false` reset; generated platform configs are unchanged.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Compile the `findRouteRules` lookup with `h3-rules/compiler` (tree-shaking to
only the rule handlers the app uses) and resolve rules at runtime with the
h3-rules matcher, replacing the hand-rolled dual-path merge in `getRouteRules`.
Add a `route-rule-handlers` module that re-exports the h3-rules handlers plus a
`cache` handler bound to Nitro's cache runtime (stable `nitro/route-rules` keys),
and delete the in-tree `route-rules` runtime. The matcher is lazily instantiated
so apps without route rules tree-shake h3-rules out entirely.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Point the normalization unit test at the h3-rules-backed resolver (the
`canonicalPath`/`isPathInScope` suites now live upstream in h3-rules; the GHSA
dual-path behavior stays covered by the `/rules/*` fixture e2e), adjust the
virtual-routing template stub, and note in the docs that route-rule semantics
are defined in h3-rules.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@vercel

vercel Bot commented Jul 8, 2026

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
nitro.build Ready Ready Preview, Comment Jul 11, 2026 10:00pm

Request Review

@coderabbitai

coderabbitai Bot commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Nitro delegates route-rule normalization, matching, and handler implementations to h3-rules. Local route-rule types, manual matching logic, and the legacy handler module are replaced, while Nitro-specific extensions and integrations are retained.

Changes

h3-rules migration

Layer / File(s) Summary
Route rule types re-exported from h3-rules
src/types/route-rules.ts
Local route-rule interfaces are replaced with h3-rules re-exports, deprecated aliases, and Nitro-specific module augmentation.
Config normalization delegated to h3-rules
src/config/resolvers/route-rules.ts, test/unit/route-rules.test.ts
Route-rule normalization delegates to h3-rules, including updated SWR and cache reset semantics.
Runtime rule handlers and legacy module removal
src/runtime/internal/route-rule-handlers.ts, src/runtime/internal/route-rules.ts
Nitro’s cache runtime is bound to the h3-rules cache handler, and the legacy local handler module is deleted.
Runtime matcher integration
src/runtime/internal/app.ts, src/runtime/virtual/routing.ts, test/unit/virtual-routing.test.ts
getRouteRules uses a memoized h3-rules matcher, and findRouteRules returns RouteRuleLayer[].
Build-time route-rule compilation
src/build/virtual/routing.ts, package.json, pnpm-workspace.yaml
Generated routing code uses h3-rules compilation, removing the local serializer and handler allowlist while adding dependency configuration.
Preset redirect and proxy updates
src/presets/cloudflare/utils.ts, src/presets/netlify/utils.ts, src/presets/vercel/utils.ts
Redirect access is explicitly guarded, while Vercel proxy rewrite inputs use type narrowing and missing-proxy checks.
Validation and documentation
test/presets/*, test/tests.ts, .agents/architecture.md, docs/1.docs/5.routing.md
Snapshots, CORS assertions, and route-rule documentation reflect h3-rules behavior.

Estimated code review effort: 4 (Complex) | ~55 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 18.18% 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
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title follows conventional commits and clearly describes the route-rules migration.
Description check ✅ Passed The description is directly related and accurately summarizes the h3-rules migration.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/h3-rules

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.

@pkg-pr-new

pkg-pr-new Bot commented Jul 8, 2026

Copy link
Copy Markdown

Open in StackBlitz

npm i https://pkg.pr.new/nitro@4411

commit: 46f91ff

Mark the injected `cache` rule handler `/* @__PURE__ */` so an app using only
non-cache route rules (e.g. `headers`) no longer pulls `ocache`/`ohash`/
`unstorage` into the bundle. Importing `headers` from `route-rule-handlers`
previously evaluated the top-level `createCacheRuleHandler(...)` call, dragging
in the whole cache runtime.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@pi0
pi0 marked this pull request as ready for review July 9, 2026 06:18
@pi0
pi0 self-requested a review as a code owner July 9, 2026 06:18

@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: 1

🧹 Nitpick comments (1)
.agents/architecture.md (1)

91-91: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Add cors to the h3-rules built-ins list for consistency with routing docs.

docs/1.docs/5.routing.md lists cors among the h3-rules-provided handlers, but this line omits it from the built-ins enumeration. Add cors to (headers, redirect, proxy, basicAuth, cors) if it is indeed an upstream handler.

📝 Suggested update
-- `route-rule-handlers.ts` — Rule handlers for the compiled matcher: h3-rules built-ins (headers, redirect, proxy, basicAuth) plus a `cache` handler bound to Nitro's cache runtime. Rule matching/normalization live in the [`h3-rules`](https://github.com/h3js/h3-rules) package.
+- `route-rule-handlers.ts` — Rule handlers for the compiled matcher: h3-rules built-ins (headers, redirect, proxy, basicAuth, cors) plus a `cache` handler bound to Nitro's cache runtime. Rule matching/normalization live in the [`h3-rules`](https://github.com/h3js/h3-rules) package.
🤖 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 @.agents/architecture.md at line 91, The h3-rules built-ins list in the
architecture notes is missing `cors`, which makes it inconsistent with the
routing documentation. Update the `route-rule-handlers.ts` description to
include `cors` alongside the existing built-ins (`headers`, `redirect`, `proxy`,
`basicAuth`), keeping the `cache` handler note unchanged if it remains
Nitro-specific. Verify the wording still matches the upstream `h3-rules` handler
set and the routing docs.
🤖 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 `@docs/1.docs/5.routing.md`:
- Around line 440-441: The routing docs sentence incorrectly attributes the
`cache` handler to `h3-rules`; update the wording in the routing section to
separate the `headers` / `redirect` / `proxy` / `basicAuth` / `cors` handlers
that come from `h3-rules` from the Nitro-specific `cache` handler. Use the
existing routing documentation text around the `Route rule matching` description
and the `cache` mention to make the ownership clear without changing the meaning
of the other handlers.

---

Nitpick comments:
In @.agents/architecture.md:
- Line 91: The h3-rules built-ins list in the architecture notes is missing
`cors`, which makes it inconsistent with the routing documentation. Update the
`route-rule-handlers.ts` description to include `cors` alongside the existing
built-ins (`headers`, `redirect`, `proxy`, `basicAuth`), keeping the `cache`
handler note unchanged if it remains Nitro-specific. Verify the wording still
matches the upstream `h3-rules` handler set and the routing docs.
🪄 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: CHILL

Plan: Pro

Run ID: 21f923d5-1892-4534-ae40-915eb9726b9b

📥 Commits

Reviewing files that changed from the base of the PR and between eee0abd and b0191b7.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (16)
  • .agents/architecture.md
  • docs/1.docs/5.routing.md
  • package.json
  • pnpm-workspace.yaml
  • src/build/virtual/routing.ts
  • src/config/resolvers/route-rules.ts
  • src/presets/cloudflare/utils.ts
  • src/presets/netlify/utils.ts
  • src/presets/vercel/utils.ts
  • src/runtime/internal/app.ts
  • src/runtime/internal/route-rule-handlers.ts
  • src/runtime/internal/route-rules.ts
  • src/runtime/virtual/routing.ts
  • src/types/route-rules.ts
  • test/unit/route-rules.test.ts
  • test/unit/virtual-routing.test.ts
💤 Files with no reviewable changes (1)
  • src/runtime/internal/route-rules.ts

Comment thread docs/1.docs/5.routing.md Outdated
…sult

The cache handler's memo map lives in route-rule-handlers module scope, not
in the matcher; the lazy init exists only to keep createMatcherFromFind out
of module scope for tree-shaking. Also drop the stale optional marker on
getRouteRules().routeRules — the h3-rules matcher always returns a rule map.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Adopt the new compiler API (`compileRouteRules` + `runtimeRules`), so
built-in handlers import straight from `h3-rules`/`h3-rules/proxy` and
only the `cache` rule is overridden to Nitro's cache runtime.

Picks up 0.1.0 behavior deltas: real h3 `handleCors` for `cors: true`
(so preset generators no longer bake CORS headers into static config),
`swr: false` as a cache reset marker, and a closed `RouteRuleConfig`
(config typos type-error again). Tests/snapshots updated accordingly.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
`cors` is now applied at runtime by h3's `handleCors` (not baked into
static headers), accepts a `CorsOptions` object, and won't cover assets
served straight from the CDN edge. Also note `swr: false` as a cache
reset marker in the rules reference table.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Compile with `preMerge: true` so each pattern's subsumption chain is
  collapsed at build time and per-request resolution takes only the
  most-specific matched layer. Fail-safe: a non-chain-clean rule set
  warns and falls back to plain compilation.
- Wrap the runtime matcher with `memoizeRouteRulesMatcher` (bounded LRU,
  cap 1024, oldest-evicted) so hot paths skip the dual-path resolve.

Both operate around the dual-path (raw+canonical) union in
`createMatcherFromFind`, so the encoded-separator hardening
(GHSA-5w89-w975-hf9q) is unaffected — verified that the compiled
matcher resolves the encoded-separator scope cases identically with and
without preMerge. Full suite green on both builders (937 passed).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
pi0 and others added 2 commits July 11, 2026 21:36
h3-rules parses `"METHOD /path"` keys and the compiled matcher dispatches
on method at runtime (method-scoped rules merge on top of method-agnostic
ones for the same path). Add a fixture rule + node e2e proving a
`POST /rules/method-scoped/**` basicAuth gate applies to POST only, and
document the `METHOD ` key prefix in the routing guide.

Uses a runtime-only `basicAuth` rule so the method-prefixed key does not
leak into platform-native static config (the netlify/cloudflare/vercel
header/redirect generators still treat keys as plain paths) — noted as a
caveat in the docs.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@pi0 pi0 changed the title refactor(routing): migrate route rules to h3-rules feat(routing): migrate route rules to h3-rules Jul 11, 2026
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.

2 participants