[PR #393] [MERGED] fix: enforce segment boundaries for basePath stripping and redirect prefixing #537

Closed
opened 2026-05-06 13:08:38 +02:00 by BreizhHardware · 0 comments

📋 Pull Request Information

Original PR: https://github.com/cloudflare/vinext/pull/393
Author: @JaredStowell
Created: 3/10/2026
Status: Merged
Merged: 3/10/2026
Merged by: @james-elicx

Base: mainHead: jstowell/fix-basepath-segment-boundary


📝 Commits (4)

  • a76caa8 Update deploy basePath stripping
  • b2aa9d3 Fix redirect basePath handling
  • 3885f97 Document basePath segment fix
  • 36a0fdd Fix external redirect basePath

📊 Changes

13 files changed (+194 additions, -56 deletions)

View changed files

📝 packages/vinext/src/deploy.ts (+19 -5)
📝 packages/vinext/src/entries/app-rsc-entry.ts (+4 -2)
📝 packages/vinext/src/index.ts (+14 -2)
📝 packages/vinext/src/server/prod-server.ts (+12 -8)
📝 packages/vinext/src/server/request-pipeline.ts (+3 -6)
📝 packages/vinext/src/shims/navigation.ts (+3 -9)
📝 packages/vinext/src/shims/router.ts (+3 -9)
packages/vinext/src/utils/base-path.ts (+24 -0)
📝 tests/__snapshots__/entry-templates.test.ts.snap (+24 -12)
📝 tests/app-router.test.ts (+2 -1)
📝 tests/deploy.test.ts (+11 -2)
📝 tests/features.test.ts (+49 -0)
📝 tests/request-pipeline.test.ts (+26 -0)

📄 Description

This fixes incorrect basePath handling when a path only shares a string prefix with the configured base path, instead of matching on a path-segment boundary.

Before this change, vinext treated any pathname beginning with the basePath string as being inside the base path. With basePath: "/app", that meant:

  • /app/about correctly stripped to /about
  • /app correctly stripped to /
  • but /application/about incorrectly stripped as well
  • and /app2 incorrectly stripped as well

That was not limited to request-path stripping. Redirect destination handling had the same bug: a destination like /application/about was incorrectly treated as already being under /app, so vinext would emit /application/about instead of /app/application/about.

In the server flow, the stripped value was fed back into URL/request handling, so /application/about could normalize to "/lication/about" and potentially collide with a real in-app route. In other words, a request outside the configured base path could be misrouted into an unrelated page if the suffix happened to exist.

The fix centralizes basePath handling into shared helpers:

  • hasBasePath() only matches when the pathname is exactly equal to the base path, or starts with basePath + "/"
  • stripBasePath() only strips in those cases

Implementation details:

  • added shared basePath helpers
  • updated shared request pipeline exports
  • updated Pages Router production request handling
  • updated Pages Router dev redirect handling
  • updated client-side App Router pathname stripping
  • updated client-side Pages Router pathname stripping
  • updated App Router redirect generation
  • updated generated Cloudflare Pages Router worker template redirect handling

Test coverage added:

  • unit regression coverage for exact-match, descendant-match, and shared-prefix non-matches
  • nested basePath coverage such as /docs/v2 vs /docs/v20
  • template/codegen assertions for the worker entry
  • App Router generator assertions for redirect prefixing
  • HTTP integration regressions covering:
    • shared-prefix collision protection, proving /application/about must not resolve to /app/lication/about
    • redirect destination prefixing, proving /app/redir correctly redirects to /app/application/about

Validation run:

  • pnpm test tests/features.test.ts -t "basePath HTTP routing"
  • pnpm test tests/request-pipeline.test.ts tests/deploy.test.ts tests/app-router.test.ts -t "basePath"
  • pnpm test tests/entry-templates.test.ts -u
  • pnpm run typecheck

🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/cloudflare/vinext/pull/393 **Author:** [@JaredStowell](https://github.com/JaredStowell) **Created:** 3/10/2026 **Status:** ✅ Merged **Merged:** 3/10/2026 **Merged by:** [@james-elicx](https://github.com/james-elicx) **Base:** `main` ← **Head:** `jstowell/fix-basepath-segment-boundary` --- ### 📝 Commits (4) - [`a76caa8`](https://github.com/cloudflare/vinext/commit/a76caa8fa486616893d403771e09c44d0ecd899b) Update deploy basePath stripping - [`b2aa9d3`](https://github.com/cloudflare/vinext/commit/b2aa9d330194d95ee4945938571146d5729b4170) Fix redirect basePath handling - [`3885f97`](https://github.com/cloudflare/vinext/commit/3885f97e86daaaba0cf3a3c95a742752fef1cc6a) Document basePath segment fix - [`36a0fdd`](https://github.com/cloudflare/vinext/commit/36a0fdd35b436d741e045fc8f3120598e7b1a977) Fix external redirect basePath ### 📊 Changes **13 files changed** (+194 additions, -56 deletions) <details> <summary>View changed files</summary> 📝 `packages/vinext/src/deploy.ts` (+19 -5) 📝 `packages/vinext/src/entries/app-rsc-entry.ts` (+4 -2) 📝 `packages/vinext/src/index.ts` (+14 -2) 📝 `packages/vinext/src/server/prod-server.ts` (+12 -8) 📝 `packages/vinext/src/server/request-pipeline.ts` (+3 -6) 📝 `packages/vinext/src/shims/navigation.ts` (+3 -9) 📝 `packages/vinext/src/shims/router.ts` (+3 -9) ➕ `packages/vinext/src/utils/base-path.ts` (+24 -0) 📝 `tests/__snapshots__/entry-templates.test.ts.snap` (+24 -12) 📝 `tests/app-router.test.ts` (+2 -1) 📝 `tests/deploy.test.ts` (+11 -2) 📝 `tests/features.test.ts` (+49 -0) 📝 `tests/request-pipeline.test.ts` (+26 -0) </details> ### 📄 Description This fixes incorrect `basePath` handling when a path only shares a string prefix with the configured base path, instead of matching on a path-segment boundary. Before this change, vinext treated any pathname beginning with the `basePath` string as being inside the base path. With `basePath: "/app"`, that meant: - `/app/about` correctly stripped to `/about` - `/app` correctly stripped to `/` - but `/application/about` incorrectly stripped as well - and `/app2` incorrectly stripped as well That was not limited to request-path stripping. Redirect destination handling had the same bug: a destination like `/application/about` was incorrectly treated as already being under `/app`, so vinext would emit `/application/about` instead of `/app/application/about`. In the server flow, the stripped value was fed back into URL/request handling, so `/application/about` could normalize to `"/lication/about"` and potentially collide with a real in-app route. In other words, a request outside the configured base path could be misrouted into an unrelated page if the suffix happened to exist. The fix centralizes basePath handling into shared helpers: - `hasBasePath()` only matches when the pathname is exactly equal to the base path, or starts with `basePath + "/"` - `stripBasePath()` only strips in those cases Implementation details: - added shared basePath helpers - updated shared request pipeline exports - updated Pages Router production request handling - updated Pages Router dev redirect handling - updated client-side App Router pathname stripping - updated client-side Pages Router pathname stripping - updated App Router redirect generation - updated generated Cloudflare Pages Router worker template redirect handling Test coverage added: - unit regression coverage for exact-match, descendant-match, and shared-prefix non-matches - nested `basePath` coverage such as `/docs/v2` vs `/docs/v20` - template/codegen assertions for the worker entry - App Router generator assertions for redirect prefixing - HTTP integration regressions covering: - shared-prefix collision protection, proving `/application/about` must not resolve to `/app/lication/about` - redirect destination prefixing, proving `/app/redir` correctly redirects to `/app/application/about` Validation run: - `pnpm test tests/features.test.ts -t "basePath HTTP routing"` - `pnpm test tests/request-pipeline.test.ts tests/deploy.test.ts tests/app-router.test.ts -t "basePath"` - `pnpm test tests/entry-templates.test.ts -u` - `pnpm run typecheck` --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
BreizhHardware 2026-05-06 13:08:38 +02:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
starred/vinext#537
No description provided.