* refactor(prerender): extract shared static-paths normalization
PR #1227 added the same getStaticPaths-entry normalization logic to
three files: packages/vinext/src/build/prerender.ts,
packages/vinext/src/server/pages-page-data.ts, and
packages/vinext/src/server/dev-server.ts. Each site handled the
string-vs-object branch, the missing-params guard, and the query-string
+ trailing-slash trimming on its own.
Hoist the shared logic into packages/vinext/src/routing/route-pattern.ts
alongside the existing matchRoutePattern helper. Two new exports:
- normalizeStaticPathname(pathname): the shared query-strip and
trailing-slash-strip used by every call site for path comparison.
- normalizeStaticPathsEntry(entry, routePattern): the build-time helper,
returning { params } | { error }; reused by prerenderPages.
Both server-side matchers (matchesPagesStaticPath in pages-page-data.ts
and the dev-server's request handling) now share the normalizeStaticPathname
helper and the StaticPathsEntry type. Their runtime matching behaviour is
preserved exactly, including the missing-params -> false (404) guard.
Pure refactor: no behaviour changes. Existing tests in
tests/prerender.test.ts (the safety net from #1227) still pass; new
focused unit tests added to tests/route-pattern.test.ts cover the helper
shape, string and object entries, catch-all patterns, percent-encoded
segments, and every error path.
* fix: drop unused NormalizedStaticPathsEntry export
knip flagged the type as an unused exported type. It's only referenced
inside route-pattern.ts as the return-type alias of normalizeStaticPathsEntry,
so make it module-local.
* fix(routing): decode matched params with decodeURIComponent
Vinext's segment-safe normalization preserves encoded delimiters (%2F) for
correct route matching, but the captured param values were left undecoded.
This caused dynamic params to return "a%2Fb" instead of "a/b", diverging
from Next.js which decodes at the route-matcher level.
Add decodeURIComponent pass in trieMatch and matchRoutePattern — the two
route matching entry points — so all downstream consumers (pages, route
handlers, metadata, useParams) receive decoded param values.
Mirrors Next.js: packages/next/src/shared/lib/router/utils/route-matcher.ts L25-27
* test(app-rsc-route-matching): update param decode assertion to match new behavior
* refactor(metadata): share route pattern helpers
Metadata route handling repeated the same dynamic segment normalization and pattern matching in several places. That made the file-convention metadata path harder to review and kept generic routing behavior inside metadata-specific modules.
Move route pattern parsing, matching, and dynamic segment filling into a normal routing module. Reuse it from metadata route build data, file-based metadata, metadata route response matching, and app RSC intercept matching, then consolidate duplicated icon descriptor normalization in the metadata server and shim code.
Add focused route-pattern tests and keep the existing metadata and shim tests covering consumer behavior.
* fix(routing): preserve prototype-named route params
The shared route pattern matcher used a normal object for matched params, unlike the previous metadata and App RSC matchers. A route parameter named __proto__ could mutate the params object's prototype instead of being stored as an own value.
Restore a null-prototype params map and add regression coverage for single and catch-all prototype-named route params.
* fix(routing): preserve literal intercept target segments