vinext 1.0.0-beta.12 → 1.0.0-beta.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (132) hide show
  1. package/README.md +39 -12
  2. package/dist/build/action-owner-manifest.js +23 -5
  3. package/dist/build/lifecycle.d.ts +42 -0
  4. package/dist/build/lifecycle.js +246 -0
  5. package/dist/build/nitro-route-rules.js +1 -1
  6. package/dist/build/pages-client-assets-module.d.ts +0 -3
  7. package/dist/build/pages-client-assets-module.js +1 -13
  8. package/dist/build/precompress.js +1 -1
  9. package/dist/build/prerender-paths.d.ts +1 -1
  10. package/dist/build/prerender-paths.js +6 -6
  11. package/dist/build/prerender-server-pool.js +1 -1
  12. package/dist/build/prerender.d.ts +1 -1
  13. package/dist/build/prerender.js +5 -5
  14. package/dist/build/preview-credentials.d.ts +1 -1
  15. package/dist/build/preview-credentials.js +2 -2
  16. package/dist/build/report.js +1 -1
  17. package/dist/build/run-prerender.d.ts +2 -2
  18. package/dist/build/run-prerender.js +5 -5
  19. package/dist/build/standalone.js +2 -2
  20. package/dist/cli-args.d.ts +5 -13
  21. package/dist/cli-args.js +2 -45
  22. package/dist/cli-dev-config.d.ts +6 -2
  23. package/dist/cli-dev-config.js +160 -3
  24. package/dist/cli.js +104 -439
  25. package/dist/config/config-matchers.js +1 -1
  26. package/dist/config/next-config.d.ts +2 -2
  27. package/dist/config/next-config.js +5 -5
  28. package/dist/config/prerender.d.ts +3 -0
  29. package/dist/config/prerender.js +8 -2
  30. package/dist/entries/app-browser-entry.js +1 -1
  31. package/dist/entries/app-rsc-entry.js +1 -1
  32. package/dist/entries/pages-client-entry.d.ts +1 -0
  33. package/dist/entries/pages-client-entry.js +17 -2
  34. package/dist/index.d.ts +6 -8
  35. package/dist/index.js +262 -101
  36. package/dist/init-cloudflare.d.ts +3 -2
  37. package/dist/init-cloudflare.js +117 -5
  38. package/dist/init-platform.d.ts +2 -0
  39. package/dist/init-platform.js +9 -3
  40. package/dist/init.d.ts +7 -5
  41. package/dist/init.js +41 -22
  42. package/dist/plugins/dynamic-preload-metadata.js +1 -1
  43. package/dist/plugins/extensionless-dynamic-import.js +2 -2
  44. package/dist/plugins/fonts.js +1 -1
  45. package/dist/plugins/ignore-dynamic-requests.js +1 -1
  46. package/dist/plugins/import-meta-url.js +1 -1
  47. package/dist/plugins/instrumentation-client.js +1 -1
  48. package/dist/plugins/og-assets.js +32 -13
  49. package/dist/plugins/og-harfbuzz.d.ts +24 -0
  50. package/dist/plugins/og-harfbuzz.js +260 -0
  51. package/dist/plugins/optimize-imports.js +1 -1
  52. package/dist/plugins/require-condition-resolution.js +2 -2
  53. package/dist/plugins/require-context.js +2 -2
  54. package/dist/plugins/typeof-window.js +1 -1
  55. package/dist/plugins/use-cache-callable.d.ts +0 -2
  56. package/dist/plugins/use-cache-callable.js +8 -16
  57. package/dist/plugins/worker-image-imports.js +1 -1
  58. package/dist/routing/app-route-graph.js +1 -1
  59. package/dist/routing/app-router.js +1 -1
  60. package/dist/routing/pages-router.js +1 -1
  61. package/dist/server/app-action-forwarding.js +1 -1
  62. package/dist/server/app-browser-error.js +1 -1
  63. package/dist/server/app-elements-wire.js +2 -2
  64. package/dist/server/app-middleware.js +1 -1
  65. package/dist/server/app-optimistic-routing.js +1 -1
  66. package/dist/server/app-page-dispatch.js +1 -1
  67. package/dist/server/app-page-element-builder.js +3 -2
  68. package/dist/server/app-page-probe.js +3 -3
  69. package/dist/server/app-request-stage-independent-entry.js +2 -2
  70. package/dist/server/app-route-handler-dispatch.js +1 -1
  71. package/dist/server/app-route-handler-runtime.js +1 -1
  72. package/dist/server/app-router-entry.js +2 -2
  73. package/dist/server/app-rsc-handler.d.ts +1 -1
  74. package/dist/server/app-rsc-handler.js +4 -4
  75. package/dist/server/app-rsc-response-stage.js +1 -1
  76. package/dist/server/app-rsc-route-matching.js +1 -1
  77. package/dist/server/app-server-action-execution.js +4 -4
  78. package/dist/server/artifact-compatibility.js +1 -1
  79. package/dist/server/client-reuse-manifest.js +1 -1
  80. package/dist/server/dev-lockfile.d.ts +1 -1
  81. package/dist/server/dev-lockfile.js +4 -1
  82. package/dist/server/dev-server.d.ts +1 -1
  83. package/dist/server/dev-server.js +28 -6
  84. package/dist/server/dev-stack-sourcemap.js +1 -1
  85. package/dist/server/isr-cache.js +1 -1
  86. package/dist/server/metadata-route-response.js +8 -0
  87. package/dist/server/middleware-runtime.js +3 -3
  88. package/dist/server/pages-data-route.js +1 -1
  89. package/dist/server/pages-dev-hydration.d.ts +6 -0
  90. package/dist/server/pages-dev-hydration.js +19 -7
  91. package/dist/server/pages-page-data.js +2 -2
  92. package/dist/server/pages-page-handler.js +1 -1
  93. package/dist/server/pages-page-response.js +1 -1
  94. package/dist/server/pages-request-pipeline.js +3 -3
  95. package/dist/server/pages-request-stage-entry.js +3 -3
  96. package/dist/server/pages-revalidate.js +1 -1
  97. package/dist/server/prerender-route-params.js +1 -1
  98. package/dist/server/prod-server.js +7 -7
  99. package/dist/server/request-pipeline.js +2 -2
  100. package/dist/server/socket-error-backstop.js +1 -1
  101. package/dist/shims/cache-callable-runtime.d.ts +38 -0
  102. package/dist/shims/cache-callable-runtime.js +162 -11
  103. package/dist/shims/cache-runtime.d.ts +6 -8
  104. package/dist/shims/cache-runtime.js +82 -22
  105. package/dist/shims/font-local.js +1 -1
  106. package/dist/shims/form.js +1 -1
  107. package/dist/shims/headers.js +2 -2
  108. package/dist/shims/internal/app-page-props-cache-key.d.ts +29 -0
  109. package/dist/shims/internal/app-page-props-cache-key.js +53 -1
  110. package/dist/shims/internal/format-url-object.d.ts +7 -0
  111. package/dist/shims/internal/format-url-object.js +53 -0
  112. package/dist/shims/internal/normalize-router-href.d.ts +15 -0
  113. package/dist/shims/internal/normalize-router-href.js +41 -0
  114. package/dist/shims/internal/pages-data-target.d.ts +4 -1
  115. package/dist/shims/internal/pages-data-target.js +28 -35
  116. package/dist/shims/internal/route-pattern-for-warning.d.ts +2 -2
  117. package/dist/shims/internal/route-pattern-for-warning.js +3 -3
  118. package/dist/shims/link.js +31 -92
  119. package/dist/shims/metadata.js +5 -4
  120. package/dist/shims/navigation.js +2 -2
  121. package/dist/shims/router-state.js +1 -1
  122. package/dist/shims/router.d.ts +8 -7
  123. package/dist/shims/router.js +312 -148
  124. package/dist/shims/server.js +3 -3
  125. package/dist/utils/built-asset-url.js +1 -1
  126. package/dist/utils/project.d.ts +1 -11
  127. package/dist/utils/project.js +19 -28
  128. package/dist/utils/react-version.d.ts +3 -1
  129. package/dist/utils/react-version.js +19 -1
  130. package/dist/utils/vite-cli-invocation.d.ts +21 -0
  131. package/dist/utils/vite-cli-invocation.js +144 -0
  132. package/package.json +10 -2
@@ -1,27 +1,30 @@
1
- import { splitPathSegments } from "../routing/utils.js";
2
- import { hasBasePath, removeTrailingSlash, stripBasePath } from "../utils/base-path.js";
1
+ import { isUnknownRecord } from "../utils/record.js";
3
2
  import { parseCookieHeader } from "../utils/parse-cookie.js";
4
- import { assertSafeNavigationUrl } from "./url-safety.js";
5
3
  import { isExternalUrl } from "../utils/external-url.js";
4
+ import { sortRoutes, splitPathSegments } from "../routing/utils.js";
5
+ import { hasBasePath, removeTrailingSlash, stripBasePath } from "../utils/base-path.js";
6
+ import { assertSafeNavigationUrl } from "./url-safety.js";
6
7
  import { addLocalePrefix, getDomainLocaleUrl, getLocalePathPrefix } from "../utils/domain-locale.js";
7
- import { isUnknownRecord } from "../utils/record.js";
8
8
  import { AppRouterContext } from "./internal/app-router-context.js";
9
9
  import { RouterContext } from "./internal/router-context.js";
10
10
  import { applyVinextLocaleGlobals, extractVinextNextDataJson, parseVinextNextDataJson } from "../client/vinext-next-data.js";
11
11
  import { isValidModulePath } from "../client/validate-module-path.js";
12
- import { fillRoutePatternSegments, matchRoutePattern, routePatternParts } from "../routing/route-pattern.js";
12
+ import { matchRoutePattern, routePattern, routePatternParts } from "../routing/route-pattern.js";
13
13
  import { NEXT_DEPLOYMENT_ID_HEADER, getDeploymentId } from "../utils/deployment-id.js";
14
14
  import { dedupedPagesDataFetch, evictPagesDataCache, fetchCachedPagesData, fetchStaticPagesData, getPagesStaticDataCache } from "./internal/pages-data-fetch-dedup.js";
15
- import { getPagesMiddlewareDataHref, prefetchPagesData, resolvePagesDataNavigationTarget } from "./internal/pages-data-target.js";
15
+ import { getPagesMiddlewareDataHref, matchesPagesMiddlewarePath, prefetchPagesData, resolvePagesDataNavigationTarget } from "./internal/pages-data-target.js";
16
16
  import { getPagesRouterComponentsMap } from "./internal/pages-router-components.js";
17
17
  import { markAppRouteDetectedOnPrefetch } from "./internal/app-route-detection.js";
18
18
  import { resolveDirectHybridClientRouteOwner } from "./internal/hybrid-client-route-owner-direct.js";
19
19
  import { installWindowNext } from "../client/window-next.js";
20
- import { getWindowOrigin, isAbsoluteOrProtocolRelativeUrl, isHashOnlyBrowserUrlChange, normalizePathTrailingSlash, toBrowserNavigationHref, toSameOriginAppPath, withBasePath } from "./url-utils.js";
21
- import { addQueryParam, appendSearchParamsToUrl, mergeRewriteQuery, mergeRouteParamsIntoQuery, parseQueryString, urlQueryToSearchParams } from "../utils/query.js";
20
+ import { getWindowOrigin, isAbsoluteOrProtocolRelativeUrl, isAbsoluteUrl, isHashOnlyBrowserUrlChange, normalizePathTrailingSlash, toBrowserNavigationHref, toSameOriginAppPath, toSameOriginPath, withBasePath } from "./url-utils.js";
21
+ import { addQueryParam, mergeRewriteQuery, mergeRouteParamsIntoQuery, parseQueryString } from "../utils/query.js";
22
22
  import { scrollToHashTarget } from "./hash-scroll.js";
23
23
  import { installPagesRouterRuntime, setPagesRouterPopStateHandler, setStampInitialHistoryState } from "./pages-router-runtime.js";
24
- import { interpolateDynamicRouteHref } from "./internal/interpolate-as.js";
24
+ import { interpolateDynamicRouteHref, resolveDynamicRouteHref } from "./internal/interpolate-as.js";
25
+ import { formatUrlObject, formatUrlObjectWithValidation } from "./internal/format-url-object.js";
26
+ import { normalizeRouterHref, resolvePagesRouterHref } from "./internal/normalize-router-href.js";
27
+ import { getCurrentRoutePathnameForWarning } from "./internal/route-pattern-for-warning.js";
25
28
  import { getCurrentBrowserLocale } from "./client-locale.js";
26
29
  import { Component, Fragment, StrictMode, createElement, useContext, useEffect, useLayoutEffect, useMemo, useState } from "react";
27
30
  //#region src/shims/router.ts
@@ -217,36 +220,69 @@ function getPagesRouterRuntimeComponents() {
217
220
  routerRuntimeState.components = components;
218
221
  return components;
219
222
  }
220
- function resolveUrl(url) {
223
+ function resolvePagesUrlPath(formatted) {
224
+ if (typeof window === "undefined") return formatted;
225
+ const routePathname = window.__NEXT_DATA__?.page ?? stripBasePath(window.location.pathname, __basePath);
226
+ const visibleAsPath = stripBasePath(window.location.pathname, __basePath) + window.location.search;
227
+ return resolvePagesRouterHref(formatted, {
228
+ pathname: routePathname,
229
+ asPath: formatted.startsWith("#") ? visibleAsPath : routePathname
230
+ }, __basePath);
231
+ }
232
+ function hasUrlObjectAuthority(url) {
233
+ return Boolean(url.host || url.hostname || url.slashes);
234
+ }
235
+ function resolveUrl(url, resolveQueryFromRoute = false) {
221
236
  if (typeof url === "string") return url;
222
- const query = url.query && typeof url.query === "object" ? url.query : void 0;
223
- const hasQuery = query !== void 0 && Object.keys(query).length > 0;
224
- const hasSearch = typeof url.search === "string" && url.search.length > 0;
225
- const hasHash = typeof url.hash === "string" && url.hash.length > 0;
226
- const inheritsVisiblePath = url.pathname === void 0 && (hasQuery || hasSearch || hasHash);
227
- let result = url.pathname ?? (typeof window !== "undefined" ? inheritsVisiblePath ? stripBasePath(window.location.pathname, __basePath) : window.__NEXT_DATA__?.page ?? stripBasePath(window.location.pathname, __basePath) : "/");
228
- if (hasSearch) {
229
- const search = url.search.startsWith("?") ? url.search : `?${url.search}`;
230
- const hashIndex = search.indexOf("#");
231
- result += hashIndex === -1 ? search : `${search.slice(0, hashIndex)}%23${search.slice(hashIndex + 1)}`;
232
- } else if (hasQuery) {
233
- const params = urlQueryToSearchParams(query);
234
- result = appendSearchParamsToUrl(result, params);
235
- } else if (hasHash && typeof window !== "undefined") result += window.location.search;
236
- if (hasHash) result += url.hash.startsWith("#") ? url.hash : `#${url.hash}`;
237
- return result;
237
+ const formatted = formatUrlObject(url);
238
+ if (url.protocol || hasUrlObjectAuthority(url) || url.pathname) return resolvePagesUrlPath(formatted);
239
+ const visiblePathname = typeof window === "undefined" ? "/" : stripBasePath(window.location.pathname, __basePath);
240
+ const routePathname = typeof window === "undefined" ? void 0 : window.__NEXT_DATA__?.page;
241
+ const matchingDynamicRoute = resolveQueryFromRoute && formatted.startsWith("?") && routePathname && extractRouteParamNames(routePathname).length > 0 && extractRouteParamsFromPath(routePathname, removeNavigationLocalePrefix(visiblePathname)) !== null ? routePathname : void 0;
242
+ return resolvePagesUrlPath((typeof window === "undefined" ? "/" : matchingDynamicRoute ?? (formatted ? visiblePathname : routePathname ?? visiblePathname)) + (formatted.startsWith("#") && typeof window !== "undefined" ? window.location.search : "") + formatted);
243
+ }
244
+ function toPreparedSameOriginPath(url) {
245
+ const appPath = toSameOriginAppPath(url, __basePath);
246
+ if (appPath !== null) return appPath;
247
+ if (!isAbsoluteUrl(url)) return null;
248
+ const localPath = toSameOriginPath(url);
249
+ if (localPath === null) return null;
250
+ const parsed = new URL(url);
251
+ return parsed.username || parsed.password ? null : localPath;
252
+ }
253
+ /** Next.js only strips a same-origin URL when the origin is a literal prefix. */
254
+ function credentialedSameOriginHref(url) {
255
+ if (!url) return void 0;
256
+ const href = resolveUrl(url);
257
+ try {
258
+ const parsed = new URL(href, window.location.href);
259
+ if (!parsed.username && !parsed.password) return void 0;
260
+ } catch {
261
+ return;
262
+ }
263
+ return toSameOriginPath(href) !== null ? href : void 0;
238
264
  }
239
- /**
240
- * When `as` is provided, use it as the navigation target. This is a
241
- * simplification: Next.js keeps `url` and `as` as separate values (url for
242
- * data fetching, as for the browser URL). We collapse them because vinext's
243
- * navigateClient() fetches HTML from the target URL, so `as` must be a
244
- * server-resolvable path. Purely decorative `as` values are not supported.
245
- * Pages error routes are handled as a narrow exception below because Next.js
246
- * treats their href as the component route while preserving `as` in history.
247
- */
248
- function resolveNavigationTarget(url, as, locale, replaceExistingLocale = false) {
249
- return applyNavigationLocale(as ?? resolveUrl(url), locale, replaceExistingLocale);
265
+ function hasUrlObjectQuery(url) {
266
+ return typeof url.query === "string" ? url.query.length > 0 : url.query !== null && typeof url.query === "object" && Object.keys(url.query).length > 0;
267
+ }
268
+ function inheritsVisiblePath(url) {
269
+ return !url.pathname && (hasUrlObjectQuery(url) || typeof url.search === "string" && url.search.length > 0 || typeof url.hash === "string" && url.hash.length > 0);
270
+ }
271
+ function prepareUrl(url) {
272
+ const routePathname = getCurrentRoutePathnameForWarning();
273
+ if (typeof url === "string") {
274
+ const normalized = normalizeRouterHref(url, routePathname);
275
+ return normalized === url ? url : resolvePagesUrlPath(normalized);
276
+ }
277
+ const formatted = formatUrlObjectWithValidation(url);
278
+ const normalized = normalizeRouterHref(formatted, routePathname);
279
+ if (normalized === formatted) return url;
280
+ if (!hasUrlObjectAuthority(url) && (url.protocol || url.pathname)) return resolvePagesUrlPath(normalized);
281
+ if (!url.pathname && normalized.startsWith("#")) return {
282
+ ...url,
283
+ hash: normalized
284
+ };
285
+ return normalized;
250
286
  }
251
287
  /**
252
288
  * Next.js's internal `_h` replacement receives browser-visible URLs, which may
@@ -266,35 +302,43 @@ function normalizeHydrationNavigationUrl(url) {
266
302
  }
267
303
  }
268
304
  var HrefInterpolationError = class extends Error {};
269
- function interpolateCurrentDynamicRoute(resolved) {
270
- if (typeof window === "undefined") return resolved;
305
+ function getHrefInterpolationError(href, routePattern, query) {
306
+ const missingParams = routePatternParts(routePattern).filter((part) => part.startsWith(":") && !part.endsWith("*")).map((part) => part.slice(1, part.endsWith("+") ? -1 : void 0)).filter((paramName) => {
307
+ const value = query[paramName];
308
+ return value === void 0 || value === "" || Array.isArray(value) && value.length === 0;
309
+ });
310
+ return missingParams.length > 0 ? new HrefInterpolationError(`The provided \`href\` (${href}) value is missing query values (${missingParams.join(", ")}) to be interpolated properly. Read more: https://nextjs.org/docs/messages/href-interpolation-failed`) : null;
311
+ }
312
+ function resolveCurrentDynamicRoute(resolved, displayHref) {
313
+ if (typeof window === "undefined") return null;
271
314
  const routePattern = window.__NEXT_DATA__?.page;
272
- if (!routePattern || extractRouteParamNames(routePattern).length === 0) return resolved;
315
+ if (!routePattern || extractRouteParamNames(routePattern).length === 0) return null;
273
316
  try {
274
317
  const target = new URL(resolved, "http://vinext.local");
275
318
  const currentOrigin = getWindowOrigin();
276
- if (currentOrigin && target.origin !== "http://vinext.local" && target.origin !== currentOrigin) return resolved;
319
+ if (currentOrigin && target.origin !== "http://vinext.local" && target.origin !== currentOrigin) return null;
277
320
  const visiblePath = stripBasePath(window.location.pathname, __basePath);
278
321
  const visibleLocale = getLocalePathPrefix(visiblePath, window.__VINEXT_LOCALES__);
279
- if (extractRouteParamsFromPath(routePattern, visibleLocale ? visiblePath.slice(visibleLocale.length + 1) || "/" : visiblePath) === null) return resolved;
280
- const query = parseQueryString(target.search);
281
- const missingParams = routePatternParts(routePattern).filter((part) => part.startsWith(":") && !part.endsWith("*")).map((part) => part.slice(1, part.endsWith("+") ? -1 : void 0)).filter((paramName) => {
282
- const value = query[paramName];
283
- return value === void 0 || value === "" || Array.isArray(value) && value.length === 0;
284
- });
285
- if (missingParams.length > 0) throw new HrefInterpolationError(`The provided \`href\` (${`${routePattern}${target.search}${target.hash}`}) value is missing query values (${missingParams.join(", ")}) to be interpolated properly. Read more: https://nextjs.org/docs/messages/href-interpolation-failed`);
286
- const routeParams = getRouteParamsFromQuery(routePattern, query);
287
- if (!routeParams) return resolved;
288
- const encodedRouteParams = Object.fromEntries(Object.entries(routeParams).map(([key, value]) => [key, Array.isArray(value) ? value.map(encodeURIComponent) : encodeURIComponent(value)]));
289
- const pathname = fillRoutePatternSegments(routePattern, encodedRouteParams);
290
- if (!pathname) return resolved;
322
+ if (extractRouteParamsFromPath(routePattern, visibleLocale ? visiblePath.slice(visibleLocale.length + 1) || "/" : visiblePath) === null) return null;
323
+ const href = routePattern + target.search + target.hash;
324
+ if (displayHref !== void 0) return {
325
+ href,
326
+ as: interpolateDynamicRouteHref(href, displayHref)?.href || null
327
+ };
328
+ const dynamicRoute = resolveDynamicRouteHref(href);
329
+ if (!dynamicRoute) {
330
+ const error = getHrefInterpolationError(href, routePattern, parseQueryString(target.search));
331
+ if (error) throw error;
332
+ return null;
333
+ }
291
334
  const targetLocale = getLocalePathPrefix(target.pathname, window.__VINEXT_LOCALES__);
292
- target.pathname = targetLocale ? `/${targetLocale}${pathname}` : pathname;
293
- for (const paramName of extractRouteParamNames(routePattern)) target.searchParams.delete(paramName);
294
- return target.href.slice(target.origin.length);
335
+ return {
336
+ href,
337
+ as: targetLocale ? `/${targetLocale}${dynamicRoute.as}` : dynamicRoute.as
338
+ };
295
339
  } catch (error) {
296
340
  if (error instanceof HrefInterpolationError) throw error;
297
- return resolved;
341
+ return null;
298
342
  }
299
343
  }
300
344
  function getCurrentUrlLocale() {
@@ -363,7 +407,11 @@ function getDomainLocalePath(url, locale) {
363
407
  */
364
408
  function applyNavigationLocale(url, locale, replaceExistingLocale = false) {
365
409
  if (!locale || typeof window === "undefined") return url;
366
- if (isAbsoluteOrProtocolRelativeUrl(url)) return url;
410
+ if (isAbsoluteOrProtocolRelativeUrl(url)) {
411
+ const localPath = window.location ? toSameOriginAppPath(url, __basePath) : null;
412
+ if (localPath == null) return url;
413
+ url = localPath;
414
+ }
367
415
  if (!replaceExistingLocale && getLocalePathPrefix(url, window.__VINEXT_LOCALES__)) return url;
368
416
  const normalizedUrl = replaceExistingLocale ? removeNavigationLocalePrefix(url) : url;
369
417
  const domainLocalePath = getDomainLocalePath(normalizedUrl, locale);
@@ -416,6 +464,15 @@ function isHashOnlyChange(href) {
416
464
  if (typeof window === "undefined") return false;
417
465
  return isHashOnlyBrowserUrlChange(href, window.location.href, __basePath);
418
466
  }
467
+ function toHistoryAppPath(url) {
468
+ const localUrl = isAbsoluteUrl(url) ? toSameOriginPath(url) ?? url : url;
469
+ if (!localUrl.startsWith("/") || localUrl.startsWith("//")) return url;
470
+ if (!__basePath) return localUrl;
471
+ const suffixIndex = localUrl.search(/[?#]/);
472
+ const pathname = suffixIndex === -1 ? localUrl : localUrl.slice(0, suffixIndex);
473
+ if (!hasBasePath(pathname, __basePath)) return localUrl;
474
+ return stripBasePath(pathname, __basePath) + (suffixIndex === -1 ? "" : localUrl.slice(suffixIndex));
475
+ }
419
476
  /**
420
477
  * Build router-shaped state for the initial document entry. Captures the
421
478
  * active locale (from `window.__VINEXT_LOCALE__`) so a back-navigation
@@ -424,12 +481,18 @@ function isHashOnlyChange(href) {
424
481
  * navigates back.
425
482
  */
426
483
  function buildInitialRouterState() {
427
- const appPath = stripBasePath(window.location.pathname, __basePath) + window.location.search;
484
+ const browserPath = window.location.pathname + window.location.search;
485
+ const nextData = window.__NEXT_DATA__;
486
+ const middlewareMatches = nextData?.__vinext?.hasMiddleware && matchesPagesMiddlewarePath(stripBasePath(window.location.pathname, __basePath));
487
+ const routeUrl = normalizePathTrailingSlash(nextData?.page && !middlewareMatches ? withBasePath(formatUrlObject({
488
+ pathname: nextData.page,
489
+ query: mergeRouteParamsIntoQuery(parseQueryString(browserPath), nextData.query ?? {})
490
+ }), __basePath) : browserPath, __trailingSlash);
428
491
  const options = {};
429
492
  if (window.__VINEXT_LOCALE__ !== void 0) options.locale = window.__VINEXT_LOCALE__;
430
493
  return {
431
- url: appPath,
432
- as: appPath,
494
+ url: routeUrl,
495
+ as: browserPath,
433
496
  options,
434
497
  __N: true,
435
498
  key: createHistoryKey()
@@ -437,8 +500,8 @@ function buildInitialRouterState() {
437
500
  }
438
501
  /**
439
502
  * Stamp the initial document entry with router-shaped state (only if no
440
- * state is present). Called once at runtime install so the entry has a
441
- * locale stamped before any push could overwrite the active locale global.
503
+ * state is present). An early next/router import may precede the generated
504
+ * entry's __NEXT_DATA__ and matcher setup, so the ready transition retries it.
442
505
  */
443
506
  function stampInitialHistoryState() {
444
507
  installManualScrollRestoration();
@@ -448,6 +511,8 @@ function stampInitialHistoryState() {
448
511
  routerRuntimeState.currentHistoryKey = getRouterStateKey(existingState) ?? routerRuntimeState.currentHistoryKey;
449
512
  return;
450
513
  }
514
+ const nextData = window.__NEXT_DATA__;
515
+ if (!nextData || nextData.__vinext?.hasMiddleware && !Object.hasOwn(window, "__VINEXT_MIDDLEWARE_MATCHER__")) return;
451
516
  const initialState = buildInitialRouterState();
452
517
  routerRuntimeState.currentHistoryKey = initialState.key;
453
518
  window.history.replaceState(initialState, "");
@@ -742,12 +807,13 @@ function getCurrentHistoryAsPath() {
742
807
  try {
743
808
  const browserUrl = new URL(window.location.href);
744
809
  const stateLocale = state.options.locale === false ? void 0 : state.options.locale;
745
- const localizedStateAs = applyNavigationLocale(state.as, stateLocale);
810
+ const stateAs = toHistoryAppPath(state.as);
811
+ const localizedStateAs = applyNavigationLocale(stateAs, stateLocale);
746
812
  const stateUrl = new URL(toBrowserNavigationHref(localizedStateAs, window.location.href, __basePath), window.location.href);
747
813
  if (stateUrl.pathname !== browserUrl.pathname || stateUrl.search !== browserUrl.search) return null;
748
- const stateAs = removeNavigationLocalePrefix(stripHash(state.as));
814
+ const appAs = removeNavigationLocalePrefix(stripHash(stateAs));
749
815
  const visibleAs = `${removeNavigationLocalePrefix(stripBasePath(window.location.pathname, __basePath))}${window.location.search}`;
750
- return `${stateAs || visibleAs}${window.location.hash}`;
816
+ return `${appAs || visibleAs}${window.location.hash}`;
751
817
  } catch {
752
818
  return null;
753
819
  }
@@ -783,6 +849,7 @@ function markPagesRouterReady() {
783
849
  }
784
850
  function initializePagesRouterReadyFromNextData(nextData, forceReady = false) {
785
851
  if (typeof window === "undefined") return;
852
+ stampInitialHistoryState();
786
853
  routerRuntimeState.pagesRouterReady = forceReady || getPagesNavigationIsReadyFromSerializedState(nextData.page, window.location.search, nextData);
787
854
  }
788
855
  function markPagesRouterHydrated() {
@@ -1098,8 +1165,8 @@ async function resolveClientConfigRewrite(href) {
1098
1165
  kind: "rewrite"
1099
1166
  } : null;
1100
1167
  }
1101
- function getMiddlewarePagesDataFetchUrl(browserUrl, dataTarget) {
1102
- const middlewareDataHref = getPagesMiddlewareDataHref(browserUrl, __basePath, dataTarget?.prefetchLocale ? { locale: dataTarget.prefetchLocale } : void 0);
1168
+ function getMiddlewarePagesDataFetchUrl(browserUrl, dataTarget, routeUrl = browserUrl) {
1169
+ const middlewareDataHref = getPagesMiddlewareDataHref(browserUrl, __basePath, dataTarget?.prefetchLocale ? { locale: dataTarget.prefetchLocale } : void 0, routeUrl);
1103
1170
  if (!middlewareDataHref) return null;
1104
1171
  if (dataTarget?.dataKind === "static" && dataTarget.middlewareDataHref === middlewareDataHref && dataTarget.prefetchDataHref) return dataTarget.prefetchDataHref;
1105
1172
  return middlewareDataHref;
@@ -1114,8 +1181,8 @@ function getPagesDataCacheHref(dataHref) {
1114
1181
  function shouldEvictMiddlewareDataCache(middlewareEffect, dataTarget) {
1115
1182
  return middlewareEffect?.redirectLocation != null || dataTarget?.dataKind !== "static";
1116
1183
  }
1117
- async function resolveMiddlewareDataEffect(browserUrl, signal, dataTarget) {
1118
- const dataUrl = getMiddlewarePagesDataFetchUrl(browserUrl, dataTarget);
1184
+ async function resolveMiddlewareDataEffect(browserUrl, signal, dataTarget, routeUrl = browserUrl) {
1185
+ const dataUrl = getMiddlewarePagesDataFetchUrl(browserUrl, dataTarget, routeUrl);
1119
1186
  if (!dataUrl) return null;
1120
1187
  if (signal.aborted) throw new DOMException("Aborted", "AbortError");
1121
1188
  try {
@@ -1413,6 +1480,7 @@ async function navigateClientHtml(url, fetchUrl, controller, navId, assertStillC
1413
1480
  scheduleHardNavigationAndThrow(url, "Navigation failed: missing __NEXT_DATA__ in response");
1414
1481
  }
1415
1482
  const nextData = parseVinextNextDataJson(nextDataJson);
1483
+ if (options.routePattern && nextData.page !== options.routePattern) scheduleHardNavigationAndThrow(browserUrl, "Navigation fallback resolved a different page");
1416
1484
  let nextDataRouteState = options.nextDataRouteState;
1417
1485
  let isDevelopmentNotFound = false;
1418
1486
  if (res.status === 404 && options.allowNotFoundResponse !== true && (nextData.page === "/404" || nextData.page === "/_error") && typeof nextData.notFoundSrcPage === "string") {
@@ -1459,6 +1527,19 @@ async function navigateClientHtml(url, fetchUrl, controller, navId, assertStillC
1459
1527
  assertStillCurrent();
1460
1528
  const PageComponent = pageModule.default;
1461
1529
  if (!isPageComponent(PageComponent)) scheduleHardNavigationAndThrow(browserUrl, "Navigation failed: page module default export is not a component");
1530
+ const pageLoaders = window.__VINEXT_PAGE_LOADERS__ ??= {};
1531
+ pageLoaders[nextData.page] ??= () => Promise.resolve(pageModule);
1532
+ const pagePatterns = window.__VINEXT_PAGE_PATTERNS__ ??= [];
1533
+ if (!pagePatterns.includes(nextData.page)) {
1534
+ pagePatterns.push(nextData.page);
1535
+ const sortedPatterns = sortRoutes(pagePatterns.map((pagePattern) => ({
1536
+ pagePattern,
1537
+ pattern: routePattern(pagePattern)
1538
+ })));
1539
+ pagePatterns.splice(0, pagePatterns.length, ...sortedPatterns.map(({ pagePattern }) => pagePattern));
1540
+ _cachedPagesRoutePatternKey = null;
1541
+ _cachedPagesRoutePattern = void 0;
1542
+ }
1462
1543
  const React = (await import("react")).default;
1463
1544
  assertStillCurrent();
1464
1545
  let AppComponent = window.__VINEXT_APP__;
@@ -1524,6 +1605,7 @@ async function navigateClient(url, fetchUrl = url, options = {}, routeUrl = url)
1524
1605
  else {
1525
1606
  let browserUrl = url;
1526
1607
  let htmlFetchUrl = fetchUrl;
1608
+ const routeMasked = options.routeMasked ?? routeUrl !== url;
1527
1609
  const configRedirect = hasClientRedirectRules() && clientConfigRedirectCouldMatch(browserUrl) ? await resolveClientConfigRedirect(browserUrl) : null;
1528
1610
  if (configRedirect) {
1529
1611
  const redirectedUrl = resolveLocalRedirectUrl(configRedirect);
@@ -1531,30 +1613,37 @@ async function navigateClient(url, fetchUrl = url, options = {}, routeUrl = url)
1531
1613
  throw new InternalNavigationRedirect(redirectedUrl);
1532
1614
  }
1533
1615
  let routeLookupUrl = routeUrl;
1534
- if (routeUrl === url && hasClientRewriteRules()) {
1616
+ let routePattern = options.routePattern;
1617
+ if (!routeMasked && hasClientRewriteRules()) {
1535
1618
  const syncConfigRewrite = hasClientAppRouteManifest() ? void 0 : resolveClientConfigRewriteSync(browserUrl);
1536
1619
  const configRewrite = syncConfigRewrite === void 0 ? await resolveClientConfigRewrite(browserUrl) : syncConfigRewrite;
1537
1620
  if (configRewrite?.kind === "document") scheduleHardNavigationAndThrow(browserUrl, "Navigation rewritten to a document route");
1538
1621
  else if (configRewrite?.kind === "rewrite") {
1539
1622
  routeLookupUrl = configRewrite.href;
1540
1623
  htmlFetchUrl = configRewrite.href;
1624
+ routePattern = void 0;
1541
1625
  }
1542
1626
  }
1543
- const pagesDataTargetOptions = { locale: options.locale };
1627
+ const pagesDataTargetOptions = {
1628
+ locale: options.locale,
1629
+ routePattern
1630
+ };
1631
+ const middlewareTargetOptions = { locale: options.locale };
1544
1632
  let dataTarget = resolvePagesDataNavigationTarget(routeLookupUrl, __basePath, pagesDataTargetOptions);
1545
1633
  let middlewareDataResponse;
1546
1634
  let middlewareEffect = null;
1547
1635
  let middlewareRewrittenTarget;
1548
- const middlewareProbeDataHref = getMiddlewarePagesDataFetchUrl(browserUrl, dataTarget);
1636
+ const middlewareUrl = routeMasked ? browserUrl : routeUrl;
1637
+ const middlewareProbeDataHref = getMiddlewarePagesDataFetchUrl(middlewareUrl, dataTarget, routeUrl);
1549
1638
  if (middlewareProbeDataHref !== null) {
1550
1639
  middlewareDataCacheEvictHref = getPagesDataCacheHref(middlewareProbeDataHref);
1551
1640
  try {
1552
- middlewareEffect = await resolveMiddlewareDataEffect(browserUrl, controller.signal, dataTarget);
1641
+ middlewareEffect = await resolveMiddlewareDataEffect(middlewareUrl, controller.signal, dataTarget, routeUrl);
1553
1642
  } catch (err) {
1554
1643
  if (err instanceof DOMException && err.name === "AbortError") throw new NavigationCancelledError(browserUrl);
1555
1644
  throw err;
1556
1645
  }
1557
- if (middlewareEffect?.rewriteTarget) middlewareRewrittenTarget = resolvePagesDataNavigationTarget(middlewareEffect.rewriteTarget, __basePath, pagesDataTargetOptions);
1646
+ if (middlewareEffect?.rewriteTarget) middlewareRewrittenTarget = resolvePagesDataNavigationTarget(middlewareEffect.rewriteTarget, __basePath, middlewareTargetOptions);
1558
1647
  if (middlewareEffect) {
1559
1648
  if (shouldEvictMiddlewareDataCache(middlewareEffect, middlewareRewrittenTarget !== void 0 ? middlewareRewrittenTarget : dataTarget)) middlewareDataCacheEvictHref = middlewareEffect.dataHref;
1560
1649
  else middlewareDataCacheEvictHref = null;
@@ -1567,14 +1656,14 @@ async function navigateClient(url, fetchUrl = url, options = {}, routeUrl = url)
1567
1656
  if (!redirectedUrl) scheduleHardNavigationAndThrow(redirectLocation, "Navigation redirected externally");
1568
1657
  throw new InternalNavigationRedirect(redirectedUrl);
1569
1658
  } else if (middlewareEffect) {
1570
- if (middlewareEffect.rewriteTarget || routeUrl === url) middlewareDataResponse = middlewareEffect.response;
1659
+ if (middlewareEffect.rewriteTarget || !routeMasked) middlewareDataResponse = middlewareEffect.response;
1571
1660
  if (middlewareEffect.rewriteTarget) {
1572
1661
  const rewrittenOwner = resolveDirectHybridClientRouteOwner(middlewareEffect.rewriteTarget, __basePath);
1573
1662
  if (rewrittenOwner === "app" || rewrittenOwner === "document") {
1574
1663
  options.beforeHistoryChange?.();
1575
1664
  scheduleHardNavigationAndThrow(browserUrl, "Navigation rewritten to a non-Pages route");
1576
1665
  }
1577
- const rewrittenTarget = middlewareRewrittenTarget ?? resolvePagesDataNavigationTarget(middlewareEffect.rewriteTarget, __basePath, pagesDataTargetOptions);
1666
+ const rewrittenTarget = middlewareRewrittenTarget ?? resolvePagesDataNavigationTarget(middlewareEffect.rewriteTarget, __basePath, middlewareTargetOptions);
1578
1667
  if (!rewrittenTarget) {
1579
1668
  options.beforeHistoryChange?.();
1580
1669
  scheduleHardNavigationAndThrow(browserUrl, "Navigation rewritten to a non-Pages route");
@@ -1584,9 +1673,13 @@ async function navigateClient(url, fetchUrl = url, options = {}, routeUrl = url)
1584
1673
  }
1585
1674
  if (middlewareEffect && shouldEvictMiddlewareDataCache(middlewareEffect, dataTarget)) middlewareDataCacheEvictHref = middlewareEffect.dataHref;
1586
1675
  else if (middlewareEffect) middlewareDataCacheEvictHref = null;
1676
+ if (middlewareEffect?.rewriteTarget) routePattern = void 0;
1587
1677
  if (dataTarget?.dataKind === "static" || dataTarget?.dataKind === "server") await navigateClientData(browserUrl, dataTarget, controller, navId, assertStillCurrent, options, middlewareDataResponse);
1588
1678
  else if (dataTarget) await navigateClientNoData(browserUrl, dataTarget, controller, assertStillCurrent, options);
1589
- else await navigateClientHtml(browserUrl, htmlFetchUrl, controller, navId, assertStillCurrent, options);
1679
+ else await navigateClientHtml(browserUrl, htmlFetchUrl, controller, navId, assertStillCurrent, {
1680
+ ...options,
1681
+ routePattern
1682
+ });
1590
1683
  }
1591
1684
  } finally {
1592
1685
  if (navId === routerRuntimeState.navigationId) routerRuntimeState.activeAbortController = null;
@@ -1677,7 +1770,7 @@ function dispatchNavigateEvent() {
1677
1770
  * @param fullUrl absolute URL committed to the browser (with basePath)
1678
1771
  * @param navState router-level metadata (`url`, `as`, `options`) the popstate
1679
1772
  * handler needs to honour stickiness — most importantly the active
1680
- * locale and the canonical app-relative `as` path.
1773
+ * locale and the Next-compatible basePath-prefixed `as` path.
1681
1774
  */
1682
1775
  function updateHistory(mode, fullUrl, navState) {
1683
1776
  const previousKey = getRouterStateKey(window.history.state);
@@ -1719,9 +1812,8 @@ function throwNoRouterInstance() {
1719
1812
  /**
1720
1813
  * Shared client-side navigation flow used by both `useRouter()` and the
1721
1814
  * `Router` singleton. The only differences between push/replace are the
1722
- * history method (`pushState` vs `replaceState`), the external-URL fallback
1723
- * (`assign` vs `replace`), and the fact that push saves scroll position for
1724
- * back/forward restoration while replace does not.
1815
+ * history method (`pushState` vs `replaceState`) and the fact that push saves
1816
+ * scroll position for back/forward restoration while replace does not.
1725
1817
  *
1726
1818
  * `onStateUpdate` lets the hook trigger a `setState` re-render at the same
1727
1819
  * point that hashChangeComplete/routeChangeComplete fires; the singleton
@@ -1729,36 +1821,69 @@ function throwNoRouterInstance() {
1729
1821
  */
1730
1822
  async function performNavigation(url, as, options, mode, onStateUpdate, redirectBrowserHref) {
1731
1823
  if (typeof window === "undefined") throwNoRouterInstance();
1824
+ const inputHref = resolveUrl(url);
1825
+ const sameOriginPath = isAbsoluteUrl(inputHref) ? toSameOriginPath(inputHref) : null;
1826
+ const origin = getWindowOrigin();
1827
+ const absoluteHistoryUrl = sameOriginPath === null ? void 0 : origin && inputHref.startsWith(origin) ? inputHref.slice(origin.length) : sameOriginPath;
1828
+ const currentRoute = window.__NEXT_DATA__?.page ?? stripBasePath(window.location.pathname, __basePath);
1829
+ const currentAs = stripBasePath(window.location.pathname, __basePath) + window.location.search;
1830
+ const preparedHistoryHref = (isAbsoluteUrl(inputHref) ? toSameOriginAppPath(inputHref, __basePath) : null) ?? absoluteHistoryUrl ?? resolvePagesRouterHref(inputHref, {
1831
+ pathname: currentRoute,
1832
+ asPath: currentAs
1833
+ }, __basePath);
1834
+ const isQueryOnlyString = typeof url === "string" && url.startsWith("?");
1835
+ const urlInheritsCurrentPath = typeof url === "string" && (isQueryOnlyString || options?._vinextInterpolateDynamicRoute === true) || typeof url !== "string" && !url.pathname && (hasUrlObjectQuery(url) || typeof url.search === "string" && url.search.length > 0);
1836
+ const replaceInheritedLocale = options?.locale !== void 0 && (!as ? urlInheritsCurrentPath || typeof url !== "string" && inheritsVisiblePath(url) : typeof as !== "string" && inheritsVisiblePath(as));
1837
+ const credentialedHistoryHref = credentialedSameOriginHref(url);
1838
+ const credentialedDisplayHref = credentialedSameOriginHref(as) ?? (!as ? credentialedHistoryHref : void 0);
1839
+ as = as ? resolveUrl(as, true) : void 0;
1732
1840
  assertSafeNavigationUrl(resolveUrl(url));
1733
- if (as !== void 0) assertSafeNavigationUrl(String(as));
1841
+ if (as !== void 0) assertSafeNavigationUrl(as);
1734
1842
  const isHydrationQueryUpdate = options?._h === 1;
1735
1843
  const navigationLocale = resolveTransitionLocale(options?.locale);
1736
- const replaceInheritedLocale = as === void 0 && options?.locale !== void 0 && typeof url !== "string" && url.pathname === void 0 && (url.query !== null && typeof url.query === "object" && Object.keys(url.query).length > 0 || typeof url.search === "string" && url.search.length > 0 || typeof url.hash === "string" && url.hash.length > 0);
1737
- let resolved = isHydrationQueryUpdate ? normalizeHydrationNavigationUrl(as ?? resolveUrl(url)) : resolveNavigationTarget(url, as, navigationLocale, replaceInheritedLocale);
1738
- let resolvedRoute = isHydrationQueryUpdate ? normalizeHydrationNavigationUrl(resolveUrl(url)) : applyNavigationLocale(resolveUrl(url), navigationLocale, replaceInheritedLocale);
1739
- if (as === void 0 && (typeof url === "string" && options?._vinextInterpolateDynamicRoute === true || typeof url !== "string" && url.pathname === void 0 && (url.query !== null && typeof url.query === "object" && Object.keys(url.query).length > 0 || typeof url.search === "string" && url.search.length > 0))) {
1740
- resolved = interpolateCurrentDynamicRoute(resolved);
1741
- resolvedRoute = interpolateCurrentDynamicRoute(resolvedRoute);
1844
+ const unlocalizedRouteHref = resolveUrl(isQueryOnlyString ? `${stripBasePath(window.location.pathname, __basePath)}${url}` : url);
1845
+ const localRouteHref = isExternalUrl(unlocalizedRouteHref) ? toSameOriginAppPath(unlocalizedRouteHref, __basePath) : unlocalizedRouteHref;
1846
+ const implicitDynamicRoute = !isHydrationQueryUpdate && as === void 0 && localRouteHref !== null ? resolveDynamicRouteHref(localRouteHref) : null;
1847
+ const routeForNavigation = implicitDynamicRoute?.as ?? unlocalizedRouteHref;
1848
+ let resolved = isHydrationQueryUpdate ? normalizeHydrationNavigationUrl(as ?? resolveUrl(url)) : applyNavigationLocale(as ?? resolveUrl(routeForNavigation), navigationLocale, replaceInheritedLocale);
1849
+ let resolvedRoute = isHydrationQueryUpdate ? normalizeHydrationNavigationUrl(resolveUrl(url)) : applyNavigationLocale(routeForNavigation, navigationLocale, replaceInheritedLocale);
1850
+ let routeIdentityHref = implicitDynamicRoute ? removeNavigationLocalePrefix(implicitDynamicRoute.href) : void 0;
1851
+ if (urlInheritsCurrentPath) {
1852
+ const internalDynamicRoute = resolveCurrentDynamicRoute(unlocalizedRouteHref, as);
1853
+ if (internalDynamicRoute) {
1854
+ if (internalDynamicRoute.as) {
1855
+ const localizedRoute = applyNavigationLocale(internalDynamicRoute.as, navigationLocale, replaceInheritedLocale);
1856
+ if (as === void 0) resolved = localizedRoute;
1857
+ resolvedRoute = localizedRoute;
1858
+ } else resolvedRoute = internalDynamicRoute.href;
1859
+ routeIdentityHref = internalDynamicRoute.href;
1860
+ }
1861
+ }
1862
+ if (isExternalUrl(resolvedRoute)) {
1863
+ const localPath = toPreparedSameOriginPath(resolvedRoute);
1864
+ if (localPath == null) {
1865
+ window.location.href = resolvedRoute;
1866
+ return false;
1867
+ }
1868
+ resolvedRoute = localPath;
1742
1869
  }
1743
1870
  if (isExternalUrl(resolved)) {
1744
- const localPath = toSameOriginAppPath(resolved, __basePath);
1871
+ const localPath = toPreparedSameOriginPath(resolved);
1745
1872
  if (localPath == null) {
1746
- if (mode === "push") window.location.assign(resolved);
1747
- else window.location.replace(resolved);
1748
- return true;
1873
+ if (process.env.NODE_ENV !== "production") throw new Error(`Invalid href: "${toBrowserNavigationHref(resolvedRoute, window.location.href, __basePath)}" and as: "${resolved}", received relative href and external as
1874
+ See more info: https://nextjs.org/docs/messages/invalid-relative-url-external-as`);
1875
+ window.location.href = resolved;
1876
+ return false;
1749
1877
  }
1750
1878
  resolved = localPath;
1751
1879
  }
1752
- if (isExternalUrl(resolvedRoute)) {
1753
- const localPath = toSameOriginAppPath(resolvedRoute, __basePath);
1754
- if (localPath != null) resolvedRoute = localPath;
1755
- }
1756
1880
  resolved = normalizePathTrailingSlash(resolved, __trailingSlash);
1757
1881
  resolvedRoute = normalizePathTrailingSlash(resolvedRoute, __trailingSlash);
1758
1882
  let interpolatedRoute = resolvedRoute;
1759
1883
  if (resolvedRoute.includes("[")) {
1760
- const projection = interpolateDynamicRouteHref(resolvedRoute, resolved, typeof url === "string" || !url.query || typeof url.query === "string" ? void 0 : url.query);
1884
+ const projection = interpolateDynamicRouteHref(resolvedRoute, resolved, typeof url === "string" || url.search || !url.query || typeof url.query === "string" ? void 0 : url.query);
1761
1885
  if (projection?.href) {
1886
+ routeIdentityHref ??= removeNavigationLocalePrefix(resolvedRoute);
1762
1887
  interpolatedRoute = projection.href;
1763
1888
  if (as === void 0 && stripHash(resolved).split("?", 1)[0] === projection.routePathname) {
1764
1889
  const remaining = new URLSearchParams();
@@ -1774,16 +1899,14 @@ async function performNavigation(url, as, options, mode, onStateUpdate, redirect
1774
1899
  resolved = normalizePathTrailingSlash(`${projection.href.split(/[?#]/, 1)[0]}${searchStr ? `?${searchStr}` : ""}${hashStr}`, __trailingSlash);
1775
1900
  }
1776
1901
  } else {
1777
- const missingParams = projection ? routePatternParts(projection.routePathname).filter((part) => part.startsWith(":") && !part.endsWith("*")).map((part) => part.slice(1, part.endsWith("+") ? -1 : void 0)).filter((paramName) => {
1778
- const value = projection.query[paramName];
1779
- return value === void 0 || value === "" || Array.isArray(value) && value.length === 0;
1780
- }) : [];
1781
- const hasExplicitHrefPathname = typeof url === "string" || url.pathname !== void 0;
1902
+ const hrefInterpolationError = projection ? getHrefInterpolationError(resolvedRoute, projection.routePathname, projection.query) : null;
1903
+ const hasExplicitHrefPathname = typeof url === "string" || !urlInheritsCurrentPath;
1782
1904
  const isMiddlewareMatch = options?.shallow !== true && getPagesMiddlewareDataHref(resolved, __basePath, { locale: navigationLocale }) !== null;
1783
- if (missingParams.length > 0 && hasExplicitHrefPathname && !isMiddlewareMatch) {
1905
+ if (hrefInterpolationError && hasExplicitHrefPathname && !isMiddlewareMatch) {
1784
1906
  const asPathname = stripHash(resolved).split("?", 1)[0];
1785
1907
  const routePathname = projection?.routePathname ?? stripHash(resolvedRoute).split("?", 1)[0];
1786
- throw new HrefInterpolationError(asPathname === routePathname ? `The provided \`href\` (${resolvedRoute}) value is missing query values (${missingParams.join(", ")}) to be interpolated properly. Read more: https://nextjs.org/docs/messages/href-interpolation-failed` : `The provided \`as\` value (${asPathname}) is incompatible with the \`href\` value (${routePathname}). Read more: https://nextjs.org/docs/messages/incompatible-href-as`);
1908
+ if (asPathname === routePathname) throw hrefInterpolationError;
1909
+ throw new HrefInterpolationError(`The provided \`as\` value (${asPathname}) is incompatible with the \`href\` value (${routePathname}). Read more: https://nextjs.org/docs/messages/incompatible-href-as`);
1787
1910
  }
1788
1911
  interpolatedRoute = resolved;
1789
1912
  }
@@ -1795,50 +1918,59 @@ async function performNavigation(url, as, options, mode, onStateUpdate, redirect
1795
1918
  if (parsed.origin === getWindowOrigin()) browserEventUrl = parsed.pathname + parsed.search + parsed.hash;
1796
1919
  } catch {}
1797
1920
  const fullRouteUrl = interpolatedRoute !== resolved ? normalizePathTrailingSlash(toBrowserNavigationHref(interpolatedRoute, window.location.href, __basePath), __trailingSlash) : full;
1921
+ if (routeIdentityHref && !routeIdentityHref.startsWith("/") && !isAbsoluteUrl(routeIdentityHref)) routeIdentityHref = resolvePagesRouterHref(routeIdentityHref, {
1922
+ pathname: currentRoute,
1923
+ asPath: currentAs
1924
+ }, __basePath);
1925
+ const routeFetchUrl = routeIdentityHref ? replaceUrlPathname(routeIdentityHref, new URL(fullRouteUrl, window.location.href).pathname) : void 0;
1798
1926
  const errorRouteHtmlFetchUrl = resolvePagesErrorHtmlFetchUrl(url, navigationLocale);
1799
- const htmlFetchUrl = errorRouteHtmlFetchUrl ?? getPagesHtmlFetchUrl(fullRouteUrl, navigationLocale);
1927
+ const htmlFetchUrl = errorRouteHtmlFetchUrl ?? getPagesHtmlFetchUrl(routeFetchUrl ?? fullRouteUrl, navigationLocale);
1800
1928
  const shallow = options?.shallow ?? false;
1801
1929
  const doScroll = options?.scroll !== false;
1802
1930
  const hash = extractHash(resolved);
1803
- const scrollTarget = doScroll ? {
1804
- x: 0,
1805
- y: 0
1806
- } : null;
1807
- const navigateOptions = errorRouteHtmlFetchUrl ? {
1808
- allowNotFoundResponse: true,
1809
- locale: navigationLocale,
1810
- mode,
1811
- scroll: scrollTarget,
1812
- isHydrationQueryUpdate: options?._h === 1
1813
- } : {
1931
+ const navigateOptions = {
1932
+ allowNotFoundResponse: errorRouteHtmlFetchUrl !== null,
1814
1933
  locale: navigationLocale,
1815
1934
  mode,
1816
- scroll: scrollTarget,
1935
+ scroll: doScroll ? {
1936
+ x: 0,
1937
+ y: 0
1938
+ } : null,
1817
1939
  isHydrationQueryUpdate: options?._h === 1
1818
1940
  };
1819
1941
  if (redirectBrowserHref !== void 0) navigateOptions.redirectBrowserHref = redirectBrowserHref;
1820
- const currentLocale = getCurrentUrlLocale();
1821
- if (mode === "push" && interpolatedRoute !== resolved && stripHash(full) === routerRuntimeState.lastPathnameAndSearch && navigationLocale === currentLocale) mode = "replace";
1942
+ navigateOptions.routeMasked = as !== void 0 && stripHash(interpolatedRoute).split("?", 1)[0] !== stripHash(resolved).split("?", 1)[0];
1943
+ if (routeIdentityHref) navigateOptions.routePattern = removeTrailingSlash(stripHash(routeIdentityHref).split("?", 1)[0]);
1944
+ const isHashOnlyNavigation = options?._h !== 1 && isHashOnlyChange(full);
1822
1945
  const navStateOptions = { shallow };
1823
1946
  if (navigationLocale !== void 0) navStateOptions.locale = navigationLocale;
1824
- const resolvedNoHash = stripHash(resolved);
1947
+ const currentAppUrl = stripBasePath(window.location.pathname, __basePath) + window.location.search;
1948
+ const historyRouteUrl = routeIdentityHref ?? (redirectBrowserHref === void 0 ? preparedHistoryHref : interpolatedRoute);
1949
+ const absoluteInputHistoryUrl = redirectBrowserHref === void 0 && absoluteHistoryUrl !== void 0 && (__basePath === "" || !hasBasePath(new URL(absoluteHistoryUrl, window.location.href).pathname, __basePath)) ? absoluteHistoryUrl : void 0;
1950
+ const historyUrl = historyRouteUrl.startsWith("#") ? currentAppUrl + historyRouteUrl : historyRouteUrl;
1951
+ const preparedHistoryAs = resolved.startsWith("#") ? currentAppUrl + resolved : redirectBrowserHref !== void 0 || resolved.startsWith("//") ? resolved : resolvePagesRouterHref(resolved, {
1952
+ pathname: currentRoute,
1953
+ asPath: currentAs
1954
+ }, __basePath);
1825
1955
  const navState = {
1826
- url: stripHash(interpolatedRoute),
1827
- as: resolvedNoHash,
1956
+ url: credentialedHistoryHref ?? normalizePathTrailingSlash(absoluteInputHistoryUrl === void 0 ? withBasePath(historyUrl, __basePath) : historyUrl, __trailingSlash),
1957
+ as: credentialedDisplayHref ?? normalizePathTrailingSlash(withBasePath(preparedHistoryAs, __basePath), __trailingSlash),
1828
1958
  options: navStateOptions
1829
1959
  };
1830
1960
  if (options?._h !== 1) cancelActiveNavigationEvent({ shallow });
1831
- if (options?._h !== 1 && interpolatedRoute === resolved && isHashOnlyChange(full)) {
1961
+ if (isHashOnlyNavigation) {
1832
1962
  if (mode === "push") saveScrollPosition();
1833
1963
  const eventUrl = resolveHashUrl(browserEventUrl);
1834
1964
  routerEvents.emit("hashChangeStart", eventUrl, { shallow });
1835
- updateHistory(mode, resolved.startsWith("#") ? resolved : redirectBrowserHref ?? full, navState);
1965
+ updateHistory(mode, resolved.startsWith("#") ? resolved : redirectBrowserHref ?? credentialedDisplayHref ?? full, navState);
1836
1966
  if (doScroll) scrollToHashTarget(extractHash(resolved));
1837
1967
  onStateUpdate?.();
1838
1968
  routerEvents.emit("hashChangeComplete", eventUrl, { shallow });
1839
1969
  dispatchNavigateEvent();
1840
1970
  return true;
1841
1971
  }
1972
+ const currentLocale = getCurrentUrlLocale();
1973
+ if (mode === "push" && interpolatedRoute !== resolved && stripHash(full) === routerRuntimeState.lastPathnameAndSearch && navigationLocale === currentLocale) mode = "replace";
1842
1974
  const appPath = getLocalPathname(resolved);
1843
1975
  const appPathNorm = appPath !== null ? removeTrailingSlash(appPath) : null;
1844
1976
  const appPathEntry = appPathNorm !== null ? getPagesRouterComponentsMap()[appPathNorm] : void 0;
@@ -1867,12 +1999,12 @@ async function performNavigation(url, as, options, mode, onStateUpdate, redirect
1867
1999
  if (beforeHistoryChangeEmitted) return;
1868
2000
  beforeHistoryChangeEmitted = true;
1869
2001
  routerEvents.emit("beforeHistoryChange", browserEventUrl, { shallow });
1870
- updateHistory(mode, redirectBrowserHref ?? full, navState);
2002
+ updateHistory(mode, redirectBrowserHref ?? credentialedDisplayHref ?? full, navState);
1871
2003
  };
1872
2004
  if (!shallow) {
1873
2005
  if (reusesMountedConfigRewritePage(full)) emitBeforeHistoryChange();
1874
2006
  navigateOptions.beforeHistoryChange = emitBeforeHistoryChange;
1875
- const result = await runNavigateClient(full, browserEventUrl, redirectBrowserHref ?? htmlFetchUrl, navigateOptions, fullRouteUrl, eventContext);
2007
+ const result = await runNavigateClient(full, browserEventUrl, redirectBrowserHref ?? htmlFetchUrl, navigateOptions, routeFetchUrl ?? fullRouteUrl, eventContext);
1876
2008
  if (typeof result === "object") {
1877
2009
  if (eventContext) clearActiveNavigationEvent(eventContext);
1878
2010
  return performNavigation(result.redirectDestination, void 0, {
@@ -1921,7 +2053,7 @@ async function prefetchUrl(url, as) {
1921
2053
  const displayUrl = as ?? url;
1922
2054
  const dataTarget = resolvePagesDataNavigationTarget(url, __basePath);
1923
2055
  if (dataTarget) {
1924
- const middlewareDataHref = displayUrl === url ? dataTarget.middlewareDataHref : getPagesMiddlewareDataHref(displayUrl, __basePath) ?? void 0;
2056
+ const middlewareDataHref = displayUrl === url ? dataTarget.middlewareDataHref : getPagesMiddlewareDataHref(displayUrl, __basePath, {}, url) ?? void 0;
1925
2057
  prefetchPagesData({
1926
2058
  ...dataTarget,
1927
2059
  middlewareDataHref
@@ -2027,7 +2159,8 @@ function handlePagesRouterPopState(e) {
2027
2159
  if (state !== null && state !== void 0 && !isNextRouterState(state)) return;
2028
2160
  if (wasFirst && !routerRuntimeState.routerDidNavigate && isNextRouterState(state)) {
2029
2161
  const currentLocale = window.__VINEXT_LOCALE__;
2030
- if (state.options?.locale === currentLocale && typeof state.as === "string" && state.as === getTrackedPagesRouterAsPath()) return;
2162
+ const stateAs = typeof state.as === "string" ? removeNavigationLocalePrefix(stripHash(toHistoryAppPath(state.as))) : void 0;
2163
+ if (state.options?.locale === currentLocale && stateAs === getTrackedPagesRouterAsPath()) return;
2031
2164
  }
2032
2165
  const currentHash = window.location.hash;
2033
2166
  const isHashOnly = browserUrl === routerRuntimeState.lastPathnameAndSearch && (currentHash !== routerRuntimeState.lastHash || currentHash !== "");
@@ -2085,14 +2218,28 @@ function handlePagesRouterPopState(e) {
2085
2218
  x: 0,
2086
2219
  y: 0
2087
2220
  } : readScrollPosition(state);
2088
- const stateRouteUrl = (() => {
2089
- if (isNextRouterState(state) && typeof state.url === "string" && typeof state.as === "string" && state.url !== state.as) return normalizePathTrailingSlash(withBasePath(state.url, __basePath), __trailingSlash);
2090
- return browserUrl;
2091
- })();
2092
- const result = await runNavigateClient(browserUrl, fullEventUrl, getPagesHtmlFetchUrl(stateRouteUrl, effectiveLocale), {
2221
+ let stateFetchUrl = browserUrl;
2222
+ let routePattern;
2223
+ let routeMasked = false;
2224
+ if (isNextRouterState(state) && typeof state.url === "string" && typeof state.as === "string" && stripHash(state.url) !== stripHash(state.as)) {
2225
+ const stateUrl = toHistoryAppPath(state.url);
2226
+ const stateAs = toHistoryAppPath(state.as);
2227
+ const dynamicRoute = interpolateDynamicRouteHref(stateUrl, removeNavigationLocalePrefix(stateAs));
2228
+ const concreteRouteUrl = dynamicRoute?.href || stateUrl;
2229
+ const fetchRouteUrl = applyNavigationLocale(concreteRouteUrl, effectiveLocale);
2230
+ stateFetchUrl = normalizePathTrailingSlash(withBasePath(fetchRouteUrl, __basePath), __trailingSlash);
2231
+ if (dynamicRoute?.href) routePattern = removeTrailingSlash(stripHash(removeNavigationLocalePrefix(stateUrl)).split("?", 1)[0]);
2232
+ const routePathname = getLocalPathname(concreteRouteUrl);
2233
+ const displayPathname = getLocalPathname(stateAs);
2234
+ routeMasked = routePathname === null || displayPathname === null || removeNavigationLocalePrefix(routePathname) !== removeNavigationLocalePrefix(displayPathname);
2235
+ }
2236
+ const result = await runNavigateClient(browserUrl, fullEventUrl, getPagesHtmlFetchUrl(stateFetchUrl, effectiveLocale), {
2237
+ locale: effectiveLocale,
2238
+ routePattern,
2239
+ routeMasked,
2093
2240
  scroll: scrollTarget,
2094
2241
  beforeHistoryChange: emitBeforeHistoryChange
2095
- }, stateRouteUrl, eventContext);
2242
+ }, stateFetchUrl, eventContext);
2096
2243
  if (typeof result === "object") {
2097
2244
  clearActiveNavigationEvent(eventContext);
2098
2245
  await performNavigation(result.redirectDestination, void 0, { locale: false }, "replace", void 0, isAbsoluteOrProtocolRelativeUrl(result.redirectDestination) ? result.redirectDestination : void 0);
@@ -2165,6 +2312,33 @@ function withRouter(ComposedComponent) {
2165
2312
  if (process.env.NODE_ENV !== "production") WithRouterWrapper.displayName = `withRouter(${composed.displayName || composed.name || "Unknown"})`;
2166
2313
  return WithRouterWrapper;
2167
2314
  }
2315
+ /**
2316
+ * Pages Router `components` map exposed on the singleton.
2317
+ *
2318
+ * In Next.js, `Router.components` doubles as (a) the cached `PrivateRouteInfo`
2319
+ * keyed by route pattern after a real page render, and (b) a marker store
2320
+ * (`{ __appRouter: true }`) for App Router routes detected via prefetch (see
2321
+ * `packages/next/src/shared/lib/router/router.ts:2525`). The Next.js deploy
2322
+ * test suite asserts the latter through `window.next.router.components`.
2323
+ *
2324
+ * vinext only writes the marker variant — the cached-page-info side is handled
2325
+ * by Vite's module graph + our own loader manifest — but the property must be
2326
+ * present and mutable so the deploy assertion can find it. The map lives
2327
+ * behind a `Symbol.for` global so the Link shim's Pages-mode prefetch branch
2328
+ * writes to the same instance even when Vite resolves the router shim and
2329
+ * the link shim through different module IDs.
2330
+ *
2331
+ * Issue: https://github.com/cloudflare/vinext/issues/1526
2332
+ */
2333
+ const _components = getPagesRouterComponentsMap();
2334
+ function navigatePagesRouter(url, as, options, mode) {
2335
+ if (typeof window === "undefined") throwNoRouterInstance();
2336
+ const preparedUrl = prepareUrl(url);
2337
+ const preparedAs = as ? prepareUrl(as) : void 0;
2338
+ assertSafeNavigationUrl(resolveUrl(preparedUrl));
2339
+ if (preparedAs) assertSafeNavigationUrl(resolveUrl(preparedAs));
2340
+ return performNavigation(preparedUrl, preparedAs, options, mode);
2341
+ }
2168
2342
  const RouterMethods = {
2169
2343
  router: null,
2170
2344
  readyCallbacks: [],
@@ -2172,20 +2346,10 @@ const RouterMethods = {
2172
2346
  callback();
2173
2347
  },
2174
2348
  /** See `_components` comment above for the dual role this map plays. */
2175
- components: getPagesRouterComponentsMap(),
2349
+ components: _components,
2176
2350
  sdc: getPagesStaticDataCache(),
2177
- push: (url, as, options) => {
2178
- if (typeof window === "undefined") throwNoRouterInstance();
2179
- assertSafeNavigationUrl(resolveUrl(url));
2180
- if (as !== void 0) assertSafeNavigationUrl(String(as));
2181
- return performNavigation(url, as, options, "push");
2182
- },
2183
- replace: (url, as, options) => {
2184
- if (typeof window === "undefined") throwNoRouterInstance();
2185
- assertSafeNavigationUrl(resolveUrl(url));
2186
- if (as !== void 0) assertSafeNavigationUrl(String(as));
2187
- return performNavigation(url, as, options, "replace");
2188
- },
2351
+ push: (url, as, options) => navigatePagesRouter(url, as, options, "push"),
2352
+ replace: (url, as, options) => navigatePagesRouter(url, as, options, "replace"),
2189
2353
  back: () => {
2190
2354
  if (typeof window === "undefined") throwNoRouterInstance();
2191
2355
  window.history.back();