Skip to content

feat: export isCanonicalPath predicate for resolveDotSegments#1460

Merged
pi0 merged 2 commits into
perf/resolve-dot-segments-fast-pathfrom
feat/is-canonical-path
Jul 14, 2026
Merged

feat: export isCanonicalPath predicate for resolveDotSegments#1460
pi0 merged 2 commits into
perf/resolve-dot-segments-fast-pathfrom
feat/is-canonical-path

Conversation

@pi0x

@pi0x pi0x commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

Stacked on #1458 (retarget to main after it merges).

What

Exports the resolver's fast-path guard as a public predicate:

isCanonicalPath(path, opts?: ResolveDotSegmentsOptions): boolean

Exact in both directions: true iff resolveDotSegments(path, opts) === path.

Why

Callers that canonicalize on a hot path (per-request scope checks, route-rule matching) want to skip the resolve and the work derived from it — h3-rules matches both the empty-preserving and slash-merged readings on every request, and can collapse to a single match when the path is already canonical in the strictest mode.

They can't safely answer "is this canonical?" themselves. A caller-side prescan duplicates knowledge of what the resolver decodes, and a stale copy silently skips a canonicalization step — for these consumers that's a scope-check bypass, not a perf bug. The alternative, resolveDotSegments(p, o) === p, only works because the fast path happens to return the same string reference, which is an implementation detail and not part of the contract.

Exporting the resolver's own guard gives callers the check without either dependency.

Pinning

  • 200k-assertion seeded-fuzz equivalence test across all four option modes: isCanonicalPath(p, o) === (resolveDotSegments(p, o) === p). Any future widening of what the resolver decodes must widen the guard in the same commit, or the property breaks.
  • Directed cases for per-mode triggers (%2f opaque by default vs decoded with decodeSlashes, // with mergeSlashes) and fast-path retention (/.well-known/…, /a%2eb, /a/...).

🤖 Generated with Claude Code

Expose the resolver's fast-path guard as a public predicate: exact in
both directions (`isCanonicalPath(path, opts)` iff
`resolveDotSegments(path, opts) === path`), so a caller that
canonicalizes on a hot path (per-request scope or rule matching) can
skip the call without duplicating knowledge of what the resolver
decodes.

Pinned by a 200k-assertion seeded-fuzz equivalence test across all four
option modes — any future widening of what the resolver decodes must
widen the guard in the same commit or the property breaks.

Co-Authored-By: Claude Fable 5 <[email protected]>
@pi0x
pi0x requested a review from pi0 as a code owner July 14, 2026 09:05
@coderabbitai

coderabbitai Bot commented Jul 14, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 80fb2948-4eea-4f78-8102-e90d4c923092

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/is-canonical-path

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.

The JSDoc claimed the predicate "never inspects a query/hash", which reads
as "a query is safely ignored". The resolver has no notion of one and scans
it as path, so `/a?next=/../b` resolves to `/b` — the opposite of ignored.
State that, with the example.

Also drop the duplicated fast-path guard in `resolveDotSegments`: after the
leading-run normalization the path starts with a single `/`, so the
predicate's leading-slash checks are already satisfied and it can just call
`isCanonicalPath`. Behavior and fast-path cost are unchanged; the guard now
lives in exactly one place.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
@pi0 pi0 changed the title feat(resolveDotSegments): export isCanonicalPath predicate feat: export isCanonicalPath predicate for resolveDotSegments Jul 14, 2026
@pi0
pi0 merged commit d2daf7b into perf/resolve-dot-segments-fast-path Jul 14, 2026
6 checks passed
@pi0
pi0 deleted the feat/is-canonical-path branch July 14, 2026 10:54
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