vinext 0.0.51 → 0.0.52

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 (307) hide show
  1. package/dist/build/precompress.d.ts +7 -7
  2. package/dist/build/precompress.js +18 -17
  3. package/dist/build/precompress.js.map +1 -1
  4. package/dist/build/prerender.d.ts +3 -14
  5. package/dist/build/prerender.js +40 -40
  6. package/dist/build/prerender.js.map +1 -1
  7. package/dist/check.js +4 -0
  8. package/dist/check.js.map +1 -1
  9. package/dist/cli-args.d.ts +1 -0
  10. package/dist/cli-args.js +5 -0
  11. package/dist/cli-args.js.map +1 -1
  12. package/dist/cli.js +39 -0
  13. package/dist/cli.js.map +1 -1
  14. package/dist/client/navigation-runtime.d.ts +47 -0
  15. package/dist/client/navigation-runtime.js +156 -0
  16. package/dist/client/navigation-runtime.js.map +1 -0
  17. package/dist/client/pages-router-link-navigation.d.ts +26 -0
  18. package/dist/client/pages-router-link-navigation.js +14 -0
  19. package/dist/client/pages-router-link-navigation.js.map +1 -0
  20. package/dist/client/vinext-next-data.d.ts +12 -2
  21. package/dist/client/vinext-next-data.js +50 -1
  22. package/dist/client/vinext-next-data.js.map +1 -0
  23. package/dist/cloudflare/kv-cache-handler.js +2 -1
  24. package/dist/cloudflare/kv-cache-handler.js.map +1 -1
  25. package/dist/config/config-matchers.d.ts +63 -16
  26. package/dist/config/config-matchers.js +143 -8
  27. package/dist/config/config-matchers.js.map +1 -1
  28. package/dist/config/next-config.d.ts +20 -2
  29. package/dist/config/next-config.js +11 -1
  30. package/dist/config/next-config.js.map +1 -1
  31. package/dist/deploy.js +101 -39
  32. package/dist/deploy.js.map +1 -1
  33. package/dist/entries/app-browser-entry.js +9 -3
  34. package/dist/entries/app-browser-entry.js.map +1 -1
  35. package/dist/entries/app-rsc-entry.js +53 -13
  36. package/dist/entries/app-rsc-entry.js.map +1 -1
  37. package/dist/entries/app-rsc-manifest.d.ts +1 -0
  38. package/dist/entries/app-rsc-manifest.js +53 -6
  39. package/dist/entries/app-rsc-manifest.js.map +1 -1
  40. package/dist/entries/app-ssr-entry.d.ts +3 -3
  41. package/dist/entries/app-ssr-entry.js +4 -4
  42. package/dist/entries/app-ssr-entry.js.map +1 -1
  43. package/dist/entries/pages-client-entry.js +18 -2
  44. package/dist/entries/pages-client-entry.js.map +1 -1
  45. package/dist/entries/pages-server-entry.js +58 -8
  46. package/dist/entries/pages-server-entry.js.map +1 -1
  47. package/dist/entries/runtime-entry-module.d.ts +2 -1
  48. package/dist/entries/runtime-entry-module.js +9 -3
  49. package/dist/entries/runtime-entry-module.js.map +1 -1
  50. package/dist/index.js +132 -40
  51. package/dist/index.js.map +1 -1
  52. package/dist/plugins/css-data-url.d.ts +7 -0
  53. package/dist/plugins/css-data-url.js +81 -0
  54. package/dist/plugins/css-data-url.js.map +1 -0
  55. package/dist/plugins/fonts.js +5 -3
  56. package/dist/plugins/fonts.js.map +1 -1
  57. package/dist/plugins/middleware-server-only.d.ts +54 -0
  58. package/dist/plugins/middleware-server-only.js +91 -0
  59. package/dist/plugins/middleware-server-only.js.map +1 -0
  60. package/dist/plugins/optimize-imports.js +4 -4
  61. package/dist/plugins/optimize-imports.js.map +1 -1
  62. package/dist/plugins/strip-server-exports.js +5 -8
  63. package/dist/plugins/strip-server-exports.js.map +1 -1
  64. package/dist/routing/app-route-graph.d.ts +20 -1
  65. package/dist/routing/app-route-graph.js +58 -6
  66. package/dist/routing/app-route-graph.js.map +1 -1
  67. package/dist/routing/app-router.d.ts +2 -2
  68. package/dist/routing/app-router.js +2 -2
  69. package/dist/routing/app-router.js.map +1 -1
  70. package/dist/routing/utils.d.ts +2 -1
  71. package/dist/routing/utils.js +4 -1
  72. package/dist/routing/utils.js.map +1 -1
  73. package/dist/server/api-handler.js +139 -37
  74. package/dist/server/api-handler.js.map +1 -1
  75. package/dist/server/app-browser-entry.js +293 -149
  76. package/dist/server/app-browser-entry.js.map +1 -1
  77. package/dist/server/app-browser-interception-context.d.ts +24 -0
  78. package/dist/server/app-browser-interception-context.js +32 -0
  79. package/dist/server/app-browser-interception-context.js.map +1 -0
  80. package/dist/server/app-browser-navigation-controller.d.ts +3 -1
  81. package/dist/server/app-browser-navigation-controller.js +5 -1
  82. package/dist/server/app-browser-navigation-controller.js.map +1 -1
  83. package/dist/server/app-browser-rsc-redirect.d.ts +2 -1
  84. package/dist/server/app-browser-rsc-redirect.js +2 -2
  85. package/dist/server/app-browser-rsc-redirect.js.map +1 -1
  86. package/dist/server/app-browser-state.d.ts +18 -1
  87. package/dist/server/app-browser-state.js +19 -1
  88. package/dist/server/app-browser-state.js.map +1 -1
  89. package/dist/server/app-browser-stream.d.ts +5 -14
  90. package/dist/server/app-browser-stream.js +13 -7
  91. package/dist/server/app-browser-stream.js.map +1 -1
  92. package/dist/server/app-browser-visible-commit.d.ts +2 -1
  93. package/dist/server/app-browser-visible-commit.js +1 -0
  94. package/dist/server/app-browser-visible-commit.js.map +1 -1
  95. package/dist/server/app-elements-wire.d.ts +10 -5
  96. package/dist/server/app-elements-wire.js +84 -2
  97. package/dist/server/app-elements-wire.js.map +1 -1
  98. package/dist/server/app-elements.d.ts +3 -2
  99. package/dist/server/app-elements.js +3 -2
  100. package/dist/server/app-elements.js.map +1 -1
  101. package/dist/server/app-fallback-renderer.js +5 -3
  102. package/dist/server/app-fallback-renderer.js.map +1 -1
  103. package/dist/server/app-middleware.d.ts +13 -0
  104. package/dist/server/app-middleware.js +3 -1
  105. package/dist/server/app-middleware.js.map +1 -1
  106. package/dist/server/app-optimistic-routing.d.ts +54 -0
  107. package/dist/server/app-optimistic-routing.js +200 -0
  108. package/dist/server/app-optimistic-routing.js.map +1 -0
  109. package/dist/server/app-page-cache.d.ts +13 -1
  110. package/dist/server/app-page-cache.js +61 -6
  111. package/dist/server/app-page-cache.js.map +1 -1
  112. package/dist/server/app-page-dispatch.d.ts +2 -0
  113. package/dist/server/app-page-dispatch.js +28 -1
  114. package/dist/server/app-page-dispatch.js.map +1 -1
  115. package/dist/server/app-page-element-builder.js +2 -1
  116. package/dist/server/app-page-element-builder.js.map +1 -1
  117. package/dist/server/app-page-execution.d.ts +28 -1
  118. package/dist/server/app-page-execution.js +89 -4
  119. package/dist/server/app-page-execution.js.map +1 -1
  120. package/dist/server/app-page-head.js +21 -2
  121. package/dist/server/app-page-head.js.map +1 -1
  122. package/dist/server/app-page-probe.js +1 -1
  123. package/dist/server/app-page-render.d.ts +2 -0
  124. package/dist/server/app-page-render.js +2 -1
  125. package/dist/server/app-page-render.js.map +1 -1
  126. package/dist/server/app-page-response.js +4 -3
  127. package/dist/server/app-page-response.js.map +1 -1
  128. package/dist/server/app-page-route-wiring.js +17 -10
  129. package/dist/server/app-page-route-wiring.js.map +1 -1
  130. package/dist/server/app-page-stream.d.ts +3 -0
  131. package/dist/server/app-page-stream.js +1 -0
  132. package/dist/server/app-page-stream.js.map +1 -1
  133. package/dist/server/app-prerender-static-params.d.ts +2 -1
  134. package/dist/server/app-prerender-static-params.js +44 -8
  135. package/dist/server/app-prerender-static-params.js.map +1 -1
  136. package/dist/server/app-route-handler-cache.d.ts +2 -2
  137. package/dist/server/app-route-handler-cache.js +3 -2
  138. package/dist/server/app-route-handler-cache.js.map +1 -1
  139. package/dist/server/app-route-handler-dispatch.d.ts +6 -1
  140. package/dist/server/app-route-handler-dispatch.js +1 -1
  141. package/dist/server/app-route-handler-dispatch.js.map +1 -1
  142. package/dist/server/app-route-handler-execution.d.ts +17 -2
  143. package/dist/server/app-route-handler-execution.js.map +1 -1
  144. package/dist/server/app-route-handler-response.js +5 -4
  145. package/dist/server/app-route-handler-response.js.map +1 -1
  146. package/dist/server/app-router-entry.js +6 -2
  147. package/dist/server/app-router-entry.js.map +1 -1
  148. package/dist/server/app-rsc-handler.d.ts +9 -1
  149. package/dist/server/app-rsc-handler.js +32 -14
  150. package/dist/server/app-rsc-handler.js.map +1 -1
  151. package/dist/server/app-rsc-render-mode.d.ts +4 -3
  152. package/dist/server/app-rsc-render-mode.js +7 -1
  153. package/dist/server/app-rsc-render-mode.js.map +1 -1
  154. package/dist/server/app-rsc-request-normalization.d.ts +4 -1
  155. package/dist/server/app-rsc-request-normalization.js +4 -1
  156. package/dist/server/app-rsc-request-normalization.js.map +1 -1
  157. package/dist/server/app-rsc-response-finalizer.d.ts +8 -1
  158. package/dist/server/app-rsc-response-finalizer.js +10 -3
  159. package/dist/server/app-rsc-response-finalizer.js.map +1 -1
  160. package/dist/server/app-rsc-route-matching.js +2 -2
  161. package/dist/server/app-rsc-route-matching.js.map +1 -1
  162. package/dist/server/app-server-action-execution.js +1 -1
  163. package/dist/server/app-ssr-entry.d.ts +2 -0
  164. package/dist/server/app-ssr-entry.js +56 -55
  165. package/dist/server/app-ssr-entry.js.map +1 -1
  166. package/dist/server/app-ssr-stream.d.ts +6 -1
  167. package/dist/server/app-ssr-stream.js +17 -3
  168. package/dist/server/app-ssr-stream.js.map +1 -1
  169. package/dist/server/artifact-compatibility.d.ts +1 -1
  170. package/dist/server/artifact-compatibility.js.map +1 -1
  171. package/dist/server/cache-headers.d.ts +7 -0
  172. package/dist/server/cache-headers.js +19 -0
  173. package/dist/server/cache-headers.js.map +1 -0
  174. package/dist/server/cache-proof.d.ts +49 -3
  175. package/dist/server/cache-proof.js +78 -22
  176. package/dist/server/cache-proof.js.map +1 -1
  177. package/dist/server/client-reuse-manifest.d.ts +99 -0
  178. package/dist/server/client-reuse-manifest.js +212 -0
  179. package/dist/server/client-reuse-manifest.js.map +1 -0
  180. package/dist/server/default-global-error-module.d.ts +20 -0
  181. package/dist/server/default-global-error-module.js +20 -0
  182. package/dist/server/default-global-error-module.js.map +1 -0
  183. package/dist/server/dev-server.d.ts +9 -1
  184. package/dist/server/dev-server.js +76 -29
  185. package/dist/server/dev-server.js.map +1 -1
  186. package/dist/server/edge-api-runtime.d.ts +5 -0
  187. package/dist/server/edge-api-runtime.js +8 -0
  188. package/dist/server/edge-api-runtime.js.map +1 -0
  189. package/dist/server/headers.d.ts +18 -1
  190. package/dist/server/headers.js +18 -1
  191. package/dist/server/headers.js.map +1 -1
  192. package/dist/server/http-error-responses.d.ts +16 -1
  193. package/dist/server/http-error-responses.js +21 -1
  194. package/dist/server/http-error-responses.js.map +1 -1
  195. package/dist/server/isr-cache.d.ts +6 -2
  196. package/dist/server/isr-cache.js +20 -4
  197. package/dist/server/isr-cache.js.map +1 -1
  198. package/dist/server/middleware-runtime.d.ts +15 -0
  199. package/dist/server/middleware-runtime.js +59 -7
  200. package/dist/server/middleware-runtime.js.map +1 -1
  201. package/dist/server/middleware.d.ts +1 -1
  202. package/dist/server/middleware.js +4 -2
  203. package/dist/server/middleware.js.map +1 -1
  204. package/dist/server/navigation-planner.d.ts +9 -3
  205. package/dist/server/navigation-planner.js +98 -25
  206. package/dist/server/navigation-planner.js.map +1 -1
  207. package/dist/server/navigation-trace.d.ts +2 -1
  208. package/dist/server/navigation-trace.js +1 -0
  209. package/dist/server/navigation-trace.js.map +1 -1
  210. package/dist/server/pages-api-route.d.ts +27 -1
  211. package/dist/server/pages-api-route.js +24 -3
  212. package/dist/server/pages-api-route.js.map +1 -1
  213. package/dist/server/pages-data-route.d.ts +77 -0
  214. package/dist/server/pages-data-route.js +97 -0
  215. package/dist/server/pages-data-route.js.map +1 -0
  216. package/dist/server/pages-i18n.d.ts +51 -1
  217. package/dist/server/pages-i18n.js +61 -1
  218. package/dist/server/pages-i18n.js.map +1 -1
  219. package/dist/server/pages-page-data.d.ts +29 -2
  220. package/dist/server/pages-page-data.js +31 -17
  221. package/dist/server/pages-page-data.js.map +1 -1
  222. package/dist/server/pages-page-response.d.ts +11 -1
  223. package/dist/server/pages-page-response.js +5 -3
  224. package/dist/server/pages-page-response.js.map +1 -1
  225. package/dist/server/prod-server.d.ts +13 -15
  226. package/dist/server/prod-server.js +109 -56
  227. package/dist/server/prod-server.js.map +1 -1
  228. package/dist/server/request-pipeline.d.ts +11 -2
  229. package/dist/server/request-pipeline.js +28 -11
  230. package/dist/server/request-pipeline.js.map +1 -1
  231. package/dist/server/seed-cache.d.ts +12 -31
  232. package/dist/server/seed-cache.js +22 -35
  233. package/dist/server/seed-cache.js.map +1 -1
  234. package/dist/server/server-action-not-found.js +8 -3
  235. package/dist/server/server-action-not-found.js.map +1 -1
  236. package/dist/server/skip-cache-proof.d.ts +41 -0
  237. package/dist/server/skip-cache-proof.js +101 -0
  238. package/dist/server/skip-cache-proof.js.map +1 -0
  239. package/dist/server/static-file-cache.d.ts +1 -1
  240. package/dist/server/static-file-cache.js +7 -6
  241. package/dist/server/static-file-cache.js.map +1 -1
  242. package/dist/shims/client-locale.d.ts +15 -0
  243. package/dist/shims/client-locale.js +13 -0
  244. package/dist/shims/client-locale.js.map +1 -0
  245. package/dist/shims/default-global-error.d.ts +32 -0
  246. package/dist/shims/default-global-error.js +181 -0
  247. package/dist/shims/default-global-error.js.map +1 -0
  248. package/dist/shims/document.d.ts +59 -3
  249. package/dist/shims/document.js +36 -5
  250. package/dist/shims/document.js.map +1 -1
  251. package/dist/shims/error-boundary.d.ts +2 -2
  252. package/dist/shims/form.js +13 -6
  253. package/dist/shims/form.js.map +1 -1
  254. package/dist/shims/link.d.ts +21 -3
  255. package/dist/shims/link.js +131 -22
  256. package/dist/shims/link.js.map +1 -1
  257. package/dist/shims/metadata.js +4 -4
  258. package/dist/shims/metadata.js.map +1 -1
  259. package/dist/shims/navigation.d.ts +8 -2
  260. package/dist/shims/navigation.js +36 -15
  261. package/dist/shims/navigation.js.map +1 -1
  262. package/dist/shims/og.d.ts +18 -2
  263. package/dist/shims/og.js +49 -1
  264. package/dist/shims/og.js.map +1 -0
  265. package/dist/shims/request-state-types.d.ts +1 -1
  266. package/dist/shims/root-params.d.ts +3 -1
  267. package/dist/shims/root-params.js +11 -3
  268. package/dist/shims/root-params.js.map +1 -1
  269. package/dist/shims/router-state.d.ts +1 -0
  270. package/dist/shims/router-state.js.map +1 -1
  271. package/dist/shims/router.d.ts +12 -5
  272. package/dist/shims/router.js +172 -22
  273. package/dist/shims/router.js.map +1 -1
  274. package/dist/shims/server.d.ts +21 -4
  275. package/dist/shims/server.js +29 -9
  276. package/dist/shims/server.js.map +1 -1
  277. package/dist/shims/slot.js +5 -1
  278. package/dist/shims/slot.js.map +1 -1
  279. package/dist/shims/unified-request-context.d.ts +1 -1
  280. package/dist/shims/url-safety.d.ts +23 -1
  281. package/dist/shims/url-safety.js +29 -2
  282. package/dist/shims/url-safety.js.map +1 -1
  283. package/dist/typegen.d.ts +10 -0
  284. package/dist/typegen.js +242 -0
  285. package/dist/typegen.js.map +1 -0
  286. package/dist/utils/asset-prefix.d.ts +33 -5
  287. package/dist/utils/asset-prefix.js +39 -6
  288. package/dist/utils/asset-prefix.js.map +1 -1
  289. package/dist/utils/cache-control-metadata.d.ts +2 -1
  290. package/dist/utils/cache-control-metadata.js +1 -3
  291. package/dist/utils/cache-control-metadata.js.map +1 -1
  292. package/dist/utils/domain-locale.d.ts +2 -1
  293. package/dist/utils/domain-locale.js +9 -1
  294. package/dist/utils/domain-locale.js.map +1 -1
  295. package/dist/utils/lazy-chunks.d.ts +1 -1
  296. package/dist/utils/lazy-chunks.js +1 -1
  297. package/dist/utils/lazy-chunks.js.map +1 -1
  298. package/dist/utils/prerender-output-paths.d.ts +15 -0
  299. package/dist/utils/prerender-output-paths.js +24 -0
  300. package/dist/utils/prerender-output-paths.js.map +1 -0
  301. package/dist/utils/query.d.ts +17 -1
  302. package/dist/utils/query.js +36 -1
  303. package/dist/utils/query.js.map +1 -1
  304. package/dist/utils/record.d.ts +5 -0
  305. package/dist/utils/record.js +8 -0
  306. package/dist/utils/record.js.map +1 -0
  307. package/package.json +11 -3
@@ -1 +1 @@
1
- {"version":3,"file":"router.js","names":[],"sources":["../../src/shims/router.ts"],"sourcesContent":["/**\n * next/router shim\n *\n * Provides useRouter() hook and Router singleton for Pages Router.\n * Backed by the browser History API. Supports client-side navigation\n * by fetching new page data and re-rendering the React root.\n */\nimport {\n useState,\n useEffect,\n useMemo,\n useContext,\n createElement,\n type ReactElement,\n type ReactNode,\n type ComponentType,\n} from \"react\";\nimport { RouterContext } from \"./internal/router-context.js\";\nimport type { VinextNextData } from \"../client/vinext-next-data.js\";\nimport { isValidModulePath } from \"../client/validate-module-path.js\";\nimport { installWindowNext, type PagesRouterPublicInstance } from \"../client/window-next.js\";\nimport {\n isAbsoluteOrProtocolRelativeUrl,\n isHashOnlyBrowserUrlChange,\n toBrowserNavigationHref,\n toSameOriginAppPath,\n} from \"./url-utils.js\";\nimport { stripBasePath } from \"../utils/base-path.js\";\nimport { addLocalePrefix, getDomainLocaleUrl, type DomainLocale } from \"../utils/domain-locale.js\";\nimport {\n addQueryParam,\n appendSearchParamsToUrl,\n type UrlQuery,\n urlQueryToSearchParams,\n} from \"../utils/query.js\";\nimport { matchRoutePattern, routePatternParts } from \"../routing/route-pattern.js\";\nimport { scrollToHashTarget } from \"./hash-scroll.js\";\nimport { setPagesRouterPopStateHandler } from \"./pages-router-runtime.js\";\n\n/** basePath from next.config.js, injected by the plugin at build time */\nconst __basePath: string = process.env.__NEXT_ROUTER_BASEPATH ?? \"\";\n\ntype BeforePopStateCallback = (state: {\n url: string;\n as: string;\n options: { shallow: boolean };\n}) => boolean;\n\nexport type NextRouter = {\n /** Current pathname */\n pathname: string;\n /** Current route pattern (e.g., \"/posts/[id]\") */\n route: string;\n /** Query parameters */\n query: Record<string, string | string[]>;\n /** Full URL including query string */\n asPath: string;\n /** Base path */\n basePath: string;\n /** Current locale */\n locale?: string;\n /** Available locales */\n locales?: string[];\n /** Default locale */\n defaultLocale?: string;\n /** Configured domain locales */\n domainLocales?: VinextNextData[\"domainLocales\"];\n /** Whether the router is ready */\n isReady: boolean;\n /** Whether this is a preview */\n isPreview: boolean;\n /** Whether this is a fallback page */\n isFallback: boolean;\n\n /** Navigate to a new URL */\n push(url: string | UrlObject, as?: string, options?: TransitionOptions): Promise<boolean>;\n /** Replace current URL */\n replace(url: string | UrlObject, as?: string, options?: TransitionOptions): Promise<boolean>;\n /** Go back */\n back(): void;\n /** Reload the page */\n reload(): void;\n /** Prefetch a page (injects <link rel=\"prefetch\">) */\n prefetch(url: string): Promise<void>;\n /** Register a callback to run before popstate navigation */\n beforePopState(cb: BeforePopStateCallback): void;\n /** Listen for route changes */\n events: RouterEvents;\n};\n\ntype UrlObject = {\n pathname?: string;\n query?: UrlQuery;\n};\n\ntype TransitionOptions = {\n shallow?: boolean;\n scroll?: boolean;\n locale?: string;\n};\n\ntype RouterEvents = {\n on(event: string, handler: (...args: unknown[]) => void): void;\n off(event: string, handler: (...args: unknown[]) => void): void;\n emit(event: string, ...args: unknown[]): void;\n};\n\nfunction createRouterEvents(): RouterEvents {\n const listeners = new Map<string, Set<(...args: unknown[]) => void>>();\n\n return {\n on(event: string, handler: (...args: unknown[]) => void) {\n if (!listeners.has(event)) listeners.set(event, new Set());\n (listeners.get(event) as Set<(...args: unknown[]) => void>).add(handler);\n },\n off(event: string, handler: (...args: unknown[]) => void) {\n listeners.get(event)?.delete(handler);\n },\n emit(event: string, ...args: unknown[]) {\n listeners.get(event)?.forEach((handler) => handler(...args));\n },\n };\n}\n\n// Singleton events instance\nconst routerEvents = createRouterEvents();\n\nfunction resolveUrl(url: string | UrlObject): string {\n if (typeof url === \"string\") return url;\n let result = url.pathname ?? \"/\";\n if (url.query) {\n const params = urlQueryToSearchParams(url.query);\n result = appendSearchParamsToUrl(result, params);\n }\n return result;\n}\n\n/**\n * When `as` is provided, use it as the navigation target. This is a\n * simplification: Next.js keeps `url` and `as` as separate values (url for\n * data fetching, as for the browser URL). We collapse them because vinext's\n * navigateClient() fetches HTML from the target URL, so `as` must be a\n * server-resolvable path. Purely decorative `as` values are not supported.\n */\nfunction resolveNavigationTarget(\n url: string | UrlObject,\n as: string | undefined,\n locale: string | undefined,\n): string {\n return applyNavigationLocale(as ?? resolveUrl(url), locale);\n}\n\nfunction getDomainLocales(): readonly DomainLocale[] | undefined {\n return (window.__NEXT_DATA__ as VinextNextData | undefined)?.domainLocales;\n}\n\nfunction getCurrentHostname(): string | undefined {\n return window.location?.hostname;\n}\n\nfunction getDomainLocalePath(url: string, locale: string): string | undefined {\n return getDomainLocaleUrl(url, locale, {\n basePath: __basePath,\n currentHostname: getCurrentHostname(),\n domainItems: getDomainLocales(),\n });\n}\n\n/**\n * Apply locale prefix to a URL for client-side navigation.\n * Same logic as Link's applyLocaleToHref but reads from window globals.\n */\nexport function applyNavigationLocale(url: string, locale?: string): string {\n if (!locale || typeof window === \"undefined\") return url;\n // Absolute and protocol-relative URLs must not be prefixed — locale\n // only applies to local paths.\n if (isAbsoluteOrProtocolRelativeUrl(url)) {\n return url;\n }\n\n const domainLocalePath = getDomainLocalePath(url, locale);\n if (domainLocalePath) return domainLocalePath;\n\n return addLocalePrefix(url, locale, window.__VINEXT_DEFAULT_LOCALE__ ?? \"\");\n}\n\n/** Check if a URL is external (any URL scheme per RFC 3986, or protocol-relative) */\nexport function isExternalUrl(url: string): boolean {\n return isAbsoluteOrProtocolRelativeUrl(url);\n}\n\n/** Resolve a hash URL to a basePath-stripped app URL for event payloads */\nfunction resolveHashUrl(url: string): string {\n if (typeof window === \"undefined\") return url;\n if (url.startsWith(\"#\"))\n return stripBasePath(window.location.pathname, __basePath) + window.location.search + url;\n // Full-path hash URL — strip basePath for consistency with other events\n try {\n const parsed = new URL(url, window.location.href);\n return stripBasePath(parsed.pathname, __basePath) + parsed.search + parsed.hash;\n } catch {\n return url;\n }\n}\n\n/** Check if a href is only a hash change relative to the current URL */\nexport function isHashOnlyChange(href: string): boolean {\n if (href.startsWith(\"#\")) return true;\n if (typeof window === \"undefined\") return false;\n return isHashOnlyBrowserUrlChange(href, window.location.href, __basePath);\n}\n\n/** Save current scroll position into history state for back/forward restoration */\nfunction saveScrollPosition(): void {\n const state = window.history.state ?? {};\n window.history.replaceState(\n { ...state, __vinext_scrollX: window.scrollX, __vinext_scrollY: window.scrollY },\n \"\",\n );\n}\n\n/** Restore scroll position from history state */\nfunction restoreScrollPosition(state: unknown): void {\n if (state && typeof state === \"object\" && \"__vinext_scrollY\" in state) {\n const { __vinext_scrollX: x, __vinext_scrollY: y } = state as {\n __vinext_scrollX: number;\n __vinext_scrollY: number;\n };\n requestAnimationFrame(() => window.scrollTo(x, y));\n }\n}\n\n/**\n * SSR context - set by the dev server before rendering each page.\n */\ntype SSRContext = {\n pathname: string;\n query: Record<string, string | string[]>;\n asPath: string;\n locale?: string;\n locales?: string[];\n defaultLocale?: string;\n domainLocales?: VinextNextData[\"domainLocales\"];\n};\n\n// ---------------------------------------------------------------------------\n// Server-side SSR state uses a registration pattern so this module can be\n// bundled for the browser. The ALS-backed implementation lives in\n// router-state.ts (server-only) and registers itself on import.\n// ---------------------------------------------------------------------------\n\nlet _ssrContext: SSRContext | null = null;\n\nlet _getSSRContext = (): SSRContext | null => _ssrContext;\nlet _setSSRContextImpl = (ctx: SSRContext | null): void => {\n _ssrContext = ctx;\n};\n\n/**\n * Register ALS-backed state accessors. Called by router-state.ts on import.\n * @internal\n */\nexport function _registerRouterStateAccessors(accessors: {\n getSSRContext: () => SSRContext | null;\n setSSRContext: (ctx: SSRContext | null) => void;\n}): void {\n _getSSRContext = accessors.getSSRContext;\n _setSSRContextImpl = accessors.setSSRContext;\n}\n\nexport function setSSRContext(ctx: SSRContext | null): void {\n _setSSRContextImpl(ctx);\n}\n\ntype PagesNavigationContextShape = {\n pathname: string;\n searchParams: URLSearchParams;\n params: Record<string, string | string[]>;\n};\n\n// Client-only cache for snapshot stability. useSyncExternalStore compares\n// snapshots with Object.is, so returning a fresh object on every render would\n// trigger re-render loops. We use a module-level cache keyed by URL inputs;\n// this is safe in the browser because there is exactly one request at a time\n// but it must NOT be used on the server (concurrent ALS-scoped requests).\nlet _cachedClientPagesNavCtx: PagesNavigationContextShape | null = null;\nlet _cachedClientPagesNavCtxKey: string | null = null;\n\nfunction _buildClientPagesNavigationContext(\n routePattern: string,\n resolvedPath: string,\n searchString: string,\n): PagesNavigationContextShape {\n const cacheKey = `${routePattern}|${resolvedPath}|${searchString}`;\n if (_cachedClientPagesNavCtxKey === cacheKey && _cachedClientPagesNavCtx) {\n return _cachedClientPagesNavCtx;\n }\n const searchParams = new URLSearchParams(searchString);\n const params = routePattern ? (extractRouteParamsFromPath(routePattern, resolvedPath) ?? {}) : {};\n const ctx: PagesNavigationContextShape = { pathname: resolvedPath, searchParams, params };\n _cachedClientPagesNavCtx = ctx;\n _cachedClientPagesNavCtxKey = cacheKey;\n return ctx;\n}\n\n/**\n * Cross-router compat shim source for `next/navigation` hooks.\n *\n * Returns the current Pages Router state shaped as a navigation context so\n * the App Router hooks (useParams/useSearchParams/usePathname) can act as\n * compat shims when invoked inside a Pages Router render. Mirrors Next.js's\n * `adaptForPathParams` and `adaptForSearchParams` in\n * .nextjs-ref/packages/next/src/shared/lib/router/adapters.tsx, which Next.js\n * uses to populate SearchParamsContext / PathParamsContext for the Pages\n * Router (see packages/next/src/server/render.tsx and\n * packages/next/src/client/index.tsx).\n *\n * Returns `null` when there is no Pages Router state available — e.g. App\n * Router pages, RSC-only renders, or pre-router renders. Callers should\n * treat null as \"App Router context, use normal app-router state\".\n */\nexport function getPagesNavigationContext(): PagesNavigationContextShape | null {\n if (typeof window === \"undefined\") {\n const ssrCtx = _getSSRContext();\n if (!ssrCtx) return null;\n // ssrCtx.pathname is the route pattern (e.g. \"/blog/[slug]\").\n // ssrCtx.asPath is the resolved URL with query string. For useSearchParams\n // we want only the URL search string; for useParams we want only the\n // dynamic route params. Build a fresh object each call — server scope is\n // request-isolated via ALS but module state must not be cached across\n // concurrent requests.\n let searchParams: URLSearchParams;\n let resolvedPath: string;\n try {\n const url = new URL(ssrCtx.asPath, \"http://_\");\n searchParams = url.searchParams;\n resolvedPath = url.pathname;\n } catch {\n searchParams = new URLSearchParams();\n resolvedPath = ssrCtx.pathname;\n }\n const params = extractRouteParamsFromPath(ssrCtx.pathname, resolvedPath) ?? {};\n return { pathname: resolvedPath, searchParams, params };\n }\n\n // Client: derive from window.location + __NEXT_DATA__. __NEXT_DATA__.page\n // is the route pattern that was matched; navigateClient() keeps it in sync\n // with the visible URL on every client-side navigation. Cached so\n // useSyncExternalStore sees a stable snapshot between renders.\n const resolvedPath = stripBasePath(window.location.pathname, __basePath);\n const pattern = window.__NEXT_DATA__?.page ?? \"\";\n return _buildClientPagesNavigationContext(pattern, resolvedPath, window.location.search);\n}\n\n/**\n * Extract param names from a Next.js route pattern.\n * E.g., \"/posts/[id]\" → [\"id\"], \"/docs/[...slug]\" → [\"slug\"],\n * \"/shop/[[...path]]\" → [\"path\"], \"/blog/[year]/[month]\" → [\"year\", \"month\"]\n * Also handles internal format: \"/posts/:id\" → [\"id\"], \"/docs/:slug+\" → [\"slug\"]\n */\nfunction extractRouteParamNames(pattern: string): string[] {\n const names: string[] = [];\n // Match Next.js bracket format: [id], [...slug], [[...slug]]\n // Accepts any non-] characters inside brackets (Next.js PARAMETER_PATTERN parity).\n const bracketMatches = pattern.matchAll(/\\[{1,2}(?:\\.\\.\\.)?([^\\]]+)\\]{1,2}/g);\n for (const m of bracketMatches) {\n names.push(m[1]);\n }\n if (names.length > 0) return names;\n // Fallback: match internal :param format (any chars except /, +, *)\n const colonMatches = pattern.matchAll(/:([^/+*]+)[+*]?/g);\n for (const m of colonMatches) {\n names.push(m[1]);\n }\n return names;\n}\n\ntype RouteQueryNextData = {\n page?: string;\n query?: Record<string, string | string[] | undefined>;\n};\n\nfunction splitPathSegments(pathname: string): string[] {\n return pathname.split(\"/\").filter(Boolean);\n}\n\nfunction extractRouteParamsFromPath(\n pattern: string,\n pathname: string,\n): Record<string, string | string[]> | null {\n return matchRoutePattern(splitPathSegments(pathname), routePatternParts(pattern));\n}\n\nfunction getRouteQueryFromNextData(\n nextData: RouteQueryNextData | undefined,\n resolvedPath: string,\n): Record<string, string | string[]> {\n const routeQuery: Record<string, string | string[]> = {};\n if (!nextData?.query || !nextData.page) return routeQuery;\n\n const routeParamNames = extractRouteParamNames(nextData.page);\n if (routeParamNames.length === 0) return routeQuery;\n\n const currentRouteParams = extractRouteParamsFromPath(nextData.page, resolvedPath);\n if (currentRouteParams) return currentRouteParams;\n\n for (const key of routeParamNames) {\n const value = nextData.query[key];\n if (typeof value === \"string\") {\n routeQuery[key] = value;\n } else if (Array.isArray(value)) {\n routeQuery[key] = [...value];\n }\n }\n return routeQuery;\n}\n\nfunction getPathnameAndQuery(): {\n pathname: string;\n query: Record<string, string | string[]>;\n asPath: string;\n} {\n if (typeof window === \"undefined\") {\n const _ssrCtx = _getSSRContext();\n if (_ssrCtx) {\n const query: Record<string, string | string[]> = {};\n for (const [key, value] of Object.entries(_ssrCtx.query)) {\n query[key] = Array.isArray(value) ? [...value] : value;\n }\n return { pathname: _ssrCtx.pathname, query, asPath: _ssrCtx.asPath };\n }\n return { pathname: \"/\", query: {}, asPath: \"/\" };\n }\n const resolvedPath = stripBasePath(window.location.pathname, __basePath);\n // In Next.js, router.pathname is the route pattern (e.g., \"/posts/[id]\"),\n // not the resolved path (\"/posts/42\"). __NEXT_DATA__.page holds the route\n // pattern and is updated by navigateClient() on every client-side navigation.\n const pathname = window.__NEXT_DATA__?.page ?? resolvedPath;\n const nextData = window.__NEXT_DATA__;\n const routeQuery = getRouteQueryFromNextData(nextData, resolvedPath);\n // URL search params always reflect the current URL\n const searchQuery: Record<string, string | string[]> = {};\n const params = new URLSearchParams(window.location.search);\n for (const [key, value] of params) {\n addQueryParam(searchQuery, key, value);\n }\n const query = { ...searchQuery, ...routeQuery };\n // asPath uses the resolved browser path, not the route pattern\n const asPath = resolvedPath + window.location.search + window.location.hash;\n return { pathname, query, asPath };\n}\n\n/**\n * Error thrown when a navigation is superseded by a newer one.\n * Matches Next.js's convention of an Error with `.cancelled = true`.\n */\nclass NavigationCancelledError extends Error {\n cancelled = true;\n constructor(route: string) {\n super(`Abort fetching component for route: \"${route}\"`);\n this.name = \"NavigationCancelledError\";\n }\n}\n\n/**\n * Error thrown after queueing a hard navigation fallback for a known failure\n * mode. Callers can use this to avoid scheduling the same hard navigation twice.\n */\nclass HardNavigationScheduledError extends Error {\n hardNavigationScheduled = true;\n constructor(message: string) {\n super(message);\n this.name = \"HardNavigationScheduledError\";\n }\n}\n\n/**\n * Monotonically increasing ID for tracking the current navigation.\n * Each call to navigateClient() increments this and captures the value.\n * After each async boundary, the navigation checks whether it is still\n * the active one. If a newer navigation has started, the stale one\n * throws NavigationCancelledError so the caller can emit routeChangeError\n * and skip routeChangeComplete.\n *\n * Replaces the old boolean `_navInProgress` guard which silently dropped\n * the second navigation, causing URL/content mismatch.\n */\nlet _navigationId = 0;\n\n/** AbortController for the in-flight fetch, so superseded navigations abort network I/O. */\nlet _activeAbortController: AbortController | null = null;\n\nfunction scheduleHardNavigationAndThrow(url: string, message: string): never {\n if (typeof window === \"undefined\") {\n throw new HardNavigationScheduledError(message);\n }\n window.location.href = url;\n throw new HardNavigationScheduledError(message);\n}\n\n/**\n * Perform client-side navigation: fetch the target page's HTML,\n * extract __NEXT_DATA__, and re-render the React root.\n *\n * Throws NavigationCancelledError if a newer navigation supersedes this one.\n * Throws on hard-navigation failures (non-OK response, missing data) so the\n * caller can distinguish success from failure for event emission.\n */\nasync function navigateClient(url: string): Promise<void> {\n if (typeof window === \"undefined\") return;\n\n const root = window.__VINEXT_ROOT__;\n if (!root) {\n // No React root yet — fall back to hard navigation\n window.location.href = url;\n return;\n }\n\n // Cancel any in-flight navigation (abort its fetch, mark it stale)\n _activeAbortController?.abort();\n const controller = new AbortController();\n _activeAbortController = controller;\n\n const navId = ++_navigationId;\n\n /** Check if this navigation is still the active one. If not, throw. */\n function assertStillCurrent(): void {\n if (navId !== _navigationId) {\n throw new NavigationCancelledError(url);\n }\n }\n\n try {\n // Fetch the target page's SSR HTML\n let res: Response;\n try {\n res = await fetch(url, {\n headers: { Accept: \"text/html\" },\n signal: controller.signal,\n });\n } catch (err: unknown) {\n // AbortError means a newer navigation cancelled this fetch\n if (err instanceof DOMException && err.name === \"AbortError\") {\n throw new NavigationCancelledError(url);\n }\n throw err;\n }\n assertStillCurrent();\n\n if (!res.ok) {\n // Set window.location.href first so the browser navigates to the correct\n // page even if the caller suppresses the error. The assignment schedules\n // the navigation asynchronously (as a task), so synchronous routeChangeError\n // listeners still run — and observe the error — before the page unloads.\n // Contract: routeChangeError listeners MUST be synchronous; async listeners\n // will not fire before the navigation completes. Callers (runNavigateClient)\n // must NOT schedule a second hard navigation — this assignment already queues\n // the browser fallback, and the helper-level HardNavigationScheduledError\n // makes that contract explicit to callers.\n scheduleHardNavigationAndThrow(url, `Navigation failed: ${res.status} ${res.statusText}`);\n }\n\n const html = await res.text();\n assertStillCurrent();\n\n // Extract __NEXT_DATA__ from the HTML\n const match = html.match(/<script>window\\.__NEXT_DATA__\\s*=\\s*(.*?)<\\/script>/);\n if (!match) {\n scheduleHardNavigationAndThrow(url, \"Navigation failed: missing __NEXT_DATA__ in response\");\n }\n\n const nextData = JSON.parse(match[1]);\n const { pageProps } = nextData.props;\n // Defer writing window.__NEXT_DATA__ until just before root.render() —\n // writing it here would let a stale navigation briefly pollute the global\n // between this assertStillCurrent() and the next one after await import().\n\n // Get the page module URL from __NEXT_DATA__.__vinext (preferred),\n // or fall back to parsing the hydration script\n let pageModuleUrl: string | undefined = nextData.__vinext?.pageModuleUrl;\n\n if (!pageModuleUrl) {\n // Legacy fallback: try to find the module URL in the inline script\n const moduleMatch = html.match(/import\\(\"([^\"]+)\"\\);\\s*\\n\\s*const PageComponent/);\n const altMatch = html.match(/await import\\(\"([^\"]+pages\\/[^\"]+)\"\\)/);\n pageModuleUrl = moduleMatch?.[1] ?? altMatch?.[1] ?? undefined;\n }\n\n if (!pageModuleUrl) {\n scheduleHardNavigationAndThrow(url, \"Navigation failed: no page module URL found\");\n }\n\n // Validate the module URL before importing — defense-in-depth against\n // unexpected __NEXT_DATA__ or malformed HTML responses\n if (!isValidModulePath(pageModuleUrl)) {\n console.error(\"[vinext] Blocked import of invalid page module path:\", pageModuleUrl);\n scheduleHardNavigationAndThrow(url, \"Navigation failed: invalid page module path\");\n }\n\n // Dynamically import the new page module\n const pageModule = await import(/* @vite-ignore */ pageModuleUrl);\n assertStillCurrent();\n\n const PageComponent = pageModule.default;\n\n if (!PageComponent) {\n scheduleHardNavigationAndThrow(url, \"Navigation failed: page module has no default export\");\n }\n\n // Import React for createElement\n const React = (await import(\"react\")).default;\n assertStillCurrent();\n\n // Re-render with the new page, loading _app if needed\n let AppComponent = window.__VINEXT_APP__;\n const appModuleUrl: string | undefined = nextData.__vinext?.appModuleUrl;\n\n if (!AppComponent && appModuleUrl) {\n if (!isValidModulePath(appModuleUrl)) {\n console.error(\"[vinext] Blocked import of invalid app module path:\", appModuleUrl);\n } else {\n try {\n const appModule = await import(/* @vite-ignore */ appModuleUrl);\n AppComponent = appModule.default;\n window.__VINEXT_APP__ = AppComponent;\n } catch {\n // _app not available — continue without it\n }\n }\n }\n assertStillCurrent();\n\n let element;\n if (AppComponent) {\n element = React.createElement(AppComponent, {\n Component: PageComponent,\n pageProps,\n });\n } else {\n element = React.createElement(PageComponent, pageProps);\n }\n\n // Wrap with RouterContext.Provider so next/router and next/compat/router work.\n element = wrapWithRouterContext(element);\n\n // Commit __NEXT_DATA__ only after all assertStillCurrent() checks have passed,\n // so a stale navigation can never pollute the global.\n // INVARIANT: Everything after the final assertStillCurrent() above (the\n // checkpoint immediately after the optional _app import) through\n // root.render() is synchronous. If any step here ever becomes async, add\n // another assertStillCurrent() before writing __NEXT_DATA__.\n window.__NEXT_DATA__ = nextData;\n root.render(element);\n } finally {\n // Clean up the abort controller if this navigation is still the active one\n if (navId === _navigationId) {\n _activeAbortController = null;\n }\n }\n}\n\n/**\n * Run navigateClient and handle errors: emit routeChangeError on failure,\n * and fall back to a hard navigation for non-cancel errors so the browser\n * recovers to a consistent state.\n *\n * Returns:\n * - \"completed\" — navigation finished, caller should emit routeChangeComplete\n * - \"cancelled\" — superseded by a newer navigation, caller should return true\n * without emitting routeChangeComplete (matches Next.js behaviour)\n * - \"failed\" — genuine error, caller should return false (hard nav is already\n * scheduled as recovery)\n */\nasync function runNavigateClient(\n fullUrl: string,\n resolvedUrl: string,\n): Promise<\"completed\" | \"cancelled\" | \"failed\"> {\n try {\n await navigateClient(fullUrl);\n return \"completed\";\n } catch (err: unknown) {\n routerEvents.emit(\"routeChangeError\", err, resolvedUrl, { shallow: false });\n if (err instanceof NavigationCancelledError) {\n return \"cancelled\";\n }\n // Genuine error (network, parse, import failure): fall back to a hard\n // navigation so the browser lands on the correct page. Known failure modes\n // throw HardNavigationScheduledError, and this guard skips those; only\n // unexpected failures (parse, import, render) need recovery here.\n if (typeof window !== \"undefined\" && !(err instanceof HardNavigationScheduledError)) {\n window.location.href = fullUrl;\n }\n return \"failed\";\n }\n}\n\n/**\n * Build the full router value object from the current pathname, query, asPath,\n * and a set of navigation methods. Shared by the Pages Router context provider\n * and tests so the public router shape stays in sync.\n */\nfunction buildRouterValue(\n pathname: string,\n query: Record<string, string | string[]>,\n asPath: string,\n methods: {\n push: NextRouter[\"push\"];\n replace: NextRouter[\"replace\"];\n back: NextRouter[\"back\"];\n reload: NextRouter[\"reload\"];\n prefetch: NextRouter[\"prefetch\"];\n beforePopState: NextRouter[\"beforePopState\"];\n },\n): NextRouter {\n const _ssrState = _getSSRContext();\n const nextData =\n typeof window !== \"undefined\"\n ? (window.__NEXT_DATA__ as VinextNextData | undefined)\n : undefined;\n const locale = typeof window === \"undefined\" ? _ssrState?.locale : window.__VINEXT_LOCALE__;\n const locales = typeof window === \"undefined\" ? _ssrState?.locales : window.__VINEXT_LOCALES__;\n const defaultLocale =\n typeof window === \"undefined\" ? _ssrState?.defaultLocale : window.__VINEXT_DEFAULT_LOCALE__;\n const domainLocales =\n typeof window === \"undefined\" ? _ssrState?.domainLocales : nextData?.domainLocales;\n\n const route = typeof window !== \"undefined\" ? (nextData?.page ?? pathname) : pathname;\n\n return {\n pathname,\n route,\n query,\n asPath,\n basePath: __basePath,\n locale,\n locales,\n defaultLocale,\n domainLocales,\n isReady: true,\n isPreview: false,\n isFallback: typeof window !== \"undefined\" && nextData?.isFallback === true,\n ...methods,\n events: routerEvents,\n };\n}\n\n/** Extract the hash fragment from a URL, including the leading `#`. */\nfunction extractHash(url: string): string {\n const i = url.indexOf(\"#\");\n return i === -1 ? \"\" : url.slice(i);\n}\n\n/** Notify in-page listeners (e.g. useRouter hooks) that navigation occurred. */\nfunction dispatchNavigateEvent(): void {\n window.dispatchEvent(new CustomEvent(\"vinext:navigate\"));\n}\n\n/**\n * Update history with the new URL and refresh the hash-only-detection tracker.\n * Centralises the `pushState`/`replaceState` branch so callers don't repeat it.\n */\nfunction updateHistory(mode: \"push\" | \"replace\", url: string): void {\n if (mode === \"push\") window.history.pushState({}, \"\", url);\n else window.history.replaceState({}, \"\", url);\n _lastPathnameAndSearch = window.location.pathname + window.location.search;\n}\n\n/**\n * Shared client-side navigation flow used by both `useRouter()` and the\n * `Router` singleton. The only differences between push/replace are the\n * history method (`pushState` vs `replaceState`), the external-URL fallback\n * (`assign` vs `replace`), and the fact that push saves scroll position for\n * back/forward restoration while replace does not.\n *\n * `onStateUpdate` lets the hook trigger a `setState` re-render at the same\n * point that hashChangeComplete/routeChangeComplete fires; the singleton\n * passes no callback.\n */\nasync function performNavigation(\n url: string | UrlObject,\n as: string | undefined,\n options: TransitionOptions | undefined,\n mode: \"push\" | \"replace\",\n onStateUpdate?: () => void,\n): Promise<boolean> {\n let resolved = resolveNavigationTarget(url, as, options?.locale);\n\n // External URLs — delegate to browser (unless same-origin)\n if (isExternalUrl(resolved)) {\n const localPath = toSameOriginAppPath(resolved, __basePath);\n if (localPath == null) {\n if (mode === \"push\") window.location.assign(resolved);\n else window.location.replace(resolved);\n return true;\n }\n resolved = localPath;\n }\n\n const full = toBrowserNavigationHref(resolved, window.location.href, __basePath);\n const shallow = options?.shallow ?? false;\n const doScroll = options?.scroll !== false;\n\n // Hash-only change — no page fetch needed\n if (isHashOnlyChange(full)) {\n const eventUrl = resolveHashUrl(full);\n routerEvents.emit(\"hashChangeStart\", eventUrl, { shallow });\n updateHistory(mode, resolved.startsWith(\"#\") ? resolved : full);\n if (doScroll) scrollToHashTarget(extractHash(resolved));\n onStateUpdate?.();\n routerEvents.emit(\"hashChangeComplete\", eventUrl, { shallow });\n dispatchNavigateEvent();\n return true;\n }\n\n if (mode === \"push\") saveScrollPosition();\n routerEvents.emit(\"routeChangeStart\", resolved, { shallow });\n routerEvents.emit(\"beforeHistoryChange\", resolved, { shallow });\n updateHistory(mode, full);\n if (!shallow) {\n const result = await runNavigateClient(full, resolved);\n if (result === \"cancelled\") return true;\n if (result === \"failed\") return false;\n }\n onStateUpdate?.();\n routerEvents.emit(\"routeChangeComplete\", resolved, { shallow });\n\n const hash = extractHash(resolved);\n if (doScroll) {\n if (hash) scrollToHashTarget(hash);\n else window.scrollTo(0, 0);\n }\n dispatchNavigateEvent();\n return true;\n}\n\n/** Inject a `<link rel=\"prefetch\">` for the target page. */\nasync function prefetchUrl(url: string): Promise<void> {\n if (typeof document !== \"undefined\") {\n const link = document.createElement(\"link\");\n link.rel = \"prefetch\";\n link.href = url;\n link.as = \"document\";\n document.head.appendChild(link);\n }\n}\n\n/**\n * useRouter hook - Pages Router compatible.\n *\n * Ported from Next.js: packages/next/src/client/router.ts\n * https://github.com/vercel/next.js/blob/canary/packages/next/src/client/router.ts\n */\nexport function useRouter(): NextRouter {\n const router = useContext(RouterContext);\n if (!router) {\n throw new Error(\n \"NextRouter was not mounted. https://nextjs.org/docs/messages/next-router-not-mounted\",\n );\n }\n\n return router;\n}\n\nfunction PagesRouterProvider({ children }: { children: ReactNode }): ReactElement {\n const [{ pathname, query, asPath }, setState] = useState(getPathnameAndQuery);\n\n // Popstate is handled by the Pages Router client entry via\n // installPagesRouterRuntime() so beforePopState() is consistently enforced\n // regardless of hook consumers. Keep URL snapshot subscriptions at the\n // provider boundary so many useRouter() calls share one router state and one\n // vinext:navigate listener.\n useEffect(() => {\n const onNavigate = ((_e: CustomEvent) => {\n setState(getPathnameAndQuery());\n }) as EventListener;\n window.addEventListener(\"vinext:navigate\", onNavigate);\n return () => window.removeEventListener(\"vinext:navigate\", onNavigate);\n }, []);\n\n const router = useMemo(\n (): NextRouter =>\n buildRouterValue(pathname, query, asPath, {\n push: Router.push,\n replace: Router.replace,\n back: Router.back,\n reload: Router.reload,\n prefetch: Router.prefetch,\n beforePopState: Router.beforePopState,\n }),\n [pathname, query, asPath],\n );\n\n return createElement(RouterContext.Provider, { value: router }, children);\n}\n\n// beforePopState callback: called before handling browser back/forward.\n// If it returns false, the navigation is cancelled.\nlet _beforePopStateCb: BeforePopStateCallback | undefined;\n\n// Track pathname+search for detecting hash-only back/forward in the popstate\n// handler. Updated after every pushState/replaceState so that popstate can\n// compare the previous value with the (already-changed) window.location.\nlet _lastPathnameAndSearch =\n typeof window !== \"undefined\" ? window.location.pathname + window.location.search : \"\";\n\nfunction handlePagesRouterPopState(e: PopStateEvent): void {\n const browserUrl = window.location.pathname + window.location.search;\n const appUrl = stripBasePath(window.location.pathname, __basePath) + window.location.search;\n\n // Detect hash-only back/forward: pathname+search unchanged, only hash differs.\n const isHashOnly = browserUrl === _lastPathnameAndSearch;\n\n // Check beforePopState callback\n if (_beforePopStateCb !== undefined) {\n const shouldContinue = _beforePopStateCb({\n url: appUrl,\n as: appUrl,\n options: { shallow: false },\n });\n if (!shouldContinue) return;\n }\n\n // Update tracker only after beforePopState confirms navigation proceeds.\n // If beforePopState cancels, the tracker must retain the previous value\n // so the next popstate compares against the correct baseline.\n _lastPathnameAndSearch = browserUrl;\n\n if (isHashOnly) {\n // Hash-only back/forward — no page fetch needed\n const hashUrl = appUrl + window.location.hash;\n routerEvents.emit(\"hashChangeStart\", hashUrl, { shallow: false });\n scrollToHashTarget(window.location.hash);\n routerEvents.emit(\"hashChangeComplete\", hashUrl, { shallow: false });\n dispatchNavigateEvent();\n return;\n }\n\n const fullAppUrl = appUrl + window.location.hash;\n routerEvents.emit(\"routeChangeStart\", fullAppUrl, { shallow: false });\n // Note: The browser has already updated window.location by the time popstate\n // fires, so this is not truly \"before\" the URL change. In Next.js the popstate\n // handler calls replaceState to store history metadata — beforeHistoryChange\n // precedes that call, not the URL change itself. We emit it here for API\n // compatibility.\n routerEvents.emit(\"beforeHistoryChange\", fullAppUrl, { shallow: false });\n void (async () => {\n const result = await runNavigateClient(browserUrl, fullAppUrl);\n if (result === \"completed\") {\n routerEvents.emit(\"routeChangeComplete\", fullAppUrl, { shallow: false });\n restoreScrollPosition(e.state);\n dispatchNavigateEvent();\n }\n // \"cancelled\": superseded by a newer navigation, so this popstate no longer wins.\n // \"failed\": runNavigateClient already scheduled the hard-navigation fallback.\n })();\n}\n\nsetPagesRouterPopStateHandler(handlePagesRouterPopState);\n\n/**\n * Wrap a React element in a RouterContext.Provider so that\n * next/compat/router's useRouter() returns the real Pages Router value.\n *\n * The provider owns the reactive Pages Router snapshot so next/router and\n * next/compat/router consumers share one context value instead of each hook\n * installing its own global URL-change listener.\n */\nexport function wrapWithRouterContext(element: ReactElement): ReactElement {\n return createElement(PagesRouterProvider, null, element);\n}\n\n/**\n * Props injected by `withRouter` into the wrapped component.\n *\n * Ported from Next.js: packages/next/src/client/with-router.tsx\n * https://github.com/vercel/next.js/blob/canary/packages/next/src/client/with-router.tsx\n */\nexport type WithRouterProps = {\n router: NextRouter;\n};\n\n/**\n * Pick<P, Exclude<keyof P, keyof WithRouterProps>> — the props of the\n * composed component minus the `router` prop that `withRouter` injects.\n *\n * Ported from Next.js: packages/next/src/client/with-router.tsx\n */\nexport type ExcludeRouterProps<P> = Pick<P, Exclude<keyof P, keyof WithRouterProps>>;\n\n/**\n * Higher-order component that injects the Pages Router `router` instance as\n * a `router` prop into a wrapped component. Primarily used by class\n * components (which cannot call hooks) to access the router. The wrapped\n * component receives the same props as the original, minus `router`, which\n * is filled in by the HOC.\n *\n * Ported from Next.js: packages/next/src/client/with-router.tsx\n * https://github.com/vercel/next.js/blob/canary/packages/next/src/client/with-router.tsx\n *\n * Differences from Next.js:\n * - We type the composed component as `ComponentType<P>` instead of\n * `NextComponentType<C, any, P>` because vinext does not expose\n * `NextComponentType` from this shim. The runtime shape (and the props\n * the wrapper forwards) is identical.\n * - We forward `getInitialProps` and `origGetInitialProps` from the\n * composed component so `_app` parity holds for class components that\n * define `getInitialProps`.\n */\nexport function withRouter<P extends WithRouterProps>(\n ComposedComponent: ComponentType<P>,\n): ComponentType<ExcludeRouterProps<P>> {\n function WithRouterWrapper(props: ExcludeRouterProps<P>): ReactElement {\n const router = useRouter();\n // Match Next.js spread order:\n // `<ComposedComponent router={useRouter()} {...props} />`\n // The injected `router` is placed first, and `{...props}` is spread\n // after, so a user-passed `router` prop overrides the HOC-injected\n // one (last-spread wins). Mirrors\n // packages/next/src/client/with-router.tsx. At the type level\n // `props: ExcludeRouterProps<P>` has no `router` key, but TS still\n // sees `P` as `WithRouterProps`-extending when checking the literal,\n // so we widen to a `Record` for the final prop bag.\n const merged: Record<string, unknown> = { router, ...(props as Record<string, unknown>) };\n return createElement(ComposedComponent, merged as unknown as P);\n }\n\n // Forward getInitialProps so class-component pages that define it keep\n // working when wrapped. Mirrors Next.js's with-router.tsx.\n const composed = ComposedComponent as ComponentType<P> & {\n getInitialProps?: unknown;\n origGetInitialProps?: unknown;\n };\n (WithRouterWrapper as unknown as { getInitialProps?: unknown }).getInitialProps =\n composed.getInitialProps;\n (WithRouterWrapper as unknown as { origGetInitialProps?: unknown }).origGetInitialProps =\n composed.origGetInitialProps;\n\n if (process.env.NODE_ENV !== \"production\") {\n const name = composed.displayName || composed.name || \"Unknown\";\n WithRouterWrapper.displayName = `withRouter(${name})`;\n }\n\n return WithRouterWrapper;\n}\n\n// Note: `withRouter` is exposed only as a named export from `next/router`.\n// The default export of that module is the Router singleton declared below.\n\n// Also export a default Router singleton for `import Router from 'next/router'`\nconst Router = {\n push: (url: string | UrlObject, as?: string, options?: TransitionOptions) =>\n performNavigation(url, as, options, \"push\"),\n replace: (url: string | UrlObject, as?: string, options?: TransitionOptions) =>\n performNavigation(url, as, options, \"replace\"),\n back: () => window.history.back(),\n reload: () => window.location.reload(),\n prefetch: prefetchUrl,\n beforePopState: (cb: BeforePopStateCallback) => {\n _beforePopStateCb = cb;\n },\n events: routerEvents,\n};\n\n// Expose `window.next.router` for Next.js parity. Pages Router test suites,\n// userland scripts, and third-party libraries reach for this global directly\n// (e.g. `window.next.router.push(...)`, `window.next.router.events.on(...)`).\n// Without this assignment, those callers crash with\n// `TypeError: Cannot read properties of undefined (reading 'router')`.\n//\n// Ported from Next.js: `packages/next/src/client/next.ts` (line 13). We do\n// NOT use a live-binding getter like Next.js does because vinext's Router\n// singleton is constructed synchronously here, so by the time this module\n// finishes loading the value is final.\nif (typeof window !== \"undefined\") {\n // Cast: `NextRouter.push`/`replace` are typed with narrow parameters\n // (UrlObject | string) while `PagesRouterPublicInstance` accepts unknown\n // args. The two are structurally compatible at runtime; TypeScript flags\n // the narrowing of contravariant function params, which is benign here\n // because callers reading off `window.next.router` are tests/userland\n // and treat the surface as opaque.\n installWindowNext({ router: Router as unknown as PagesRouterPublicInstance });\n}\n\n// Register the Pages Router compat shim source for `next/navigation` hooks\n// (useParams / useSearchParams / usePathname). The accessor is exposed\n// through a well-known Symbol.for so navigation.ts can read it without\n// importing this module — that avoids triggering navigation.ts's\n// `window.history.pushState` patch in tests that only need next/router.\n//\n// Mirrors Next.js's behavior where the pages-router server (render.tsx) and\n// client (client/index.tsx) wrap pages with SearchParamsContext /\n// PathParamsContext / PathnameContext providers populated from the router.\nconst _PAGES_NAVIGATION_ACCESSOR_KEY = Symbol.for(\n \"vinext.navigation.pagesNavigationContextAccessor\",\n);\n(globalThis as Record<PropertyKey, unknown>)[_PAGES_NAVIGATION_ACCESSOR_KEY] =\n getPagesNavigationContext;\n\nexport default Router;\n"],"mappings":"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"}
1
+ {"version":3,"file":"router.js","names":[],"sources":["../../src/shims/router.ts"],"sourcesContent":["/**\n * next/router shim\n *\n * Provides useRouter() hook and Router singleton for Pages Router.\n * Backed by the browser History API. Supports client-side navigation\n * by fetching new page data and re-rendering the React root.\n */\nimport {\n useState,\n useEffect,\n useMemo,\n useContext,\n createElement,\n type ReactElement,\n type ReactNode,\n type ComponentType,\n} from \"react\";\nimport { RouterContext } from \"./internal/router-context.js\";\nimport {\n applyVinextLocaleGlobals,\n extractVinextNextDataJson,\n parseVinextNextDataJson,\n type VinextNextData,\n} from \"../client/vinext-next-data.js\";\nimport { isValidModulePath } from \"../client/validate-module-path.js\";\nimport { installWindowNext, type PagesRouterPublicInstance } from \"../client/window-next.js\";\nimport {\n isAbsoluteOrProtocolRelativeUrl,\n isHashOnlyBrowserUrlChange,\n normalizePathTrailingSlash,\n toBrowserNavigationHref,\n toSameOriginAppPath,\n} from \"./url-utils.js\";\nimport { stripBasePath } from \"../utils/base-path.js\";\nimport {\n addLocalePrefix,\n getDomainLocaleUrl,\n getLocalePathPrefix,\n type DomainLocale,\n} from \"../utils/domain-locale.js\";\nimport {\n addQueryParam,\n appendSearchParamsToUrl,\n type UrlQuery,\n urlQueryToSearchParams,\n} from \"../utils/query.js\";\nimport { matchRoutePattern, routePatternParts } from \"../routing/route-pattern.js\";\nimport { scrollToHashTarget } from \"./hash-scroll.js\";\nimport { setPagesRouterPopStateHandler } from \"./pages-router-runtime.js\";\nimport { assertSafeNavigationUrl } from \"./url-safety.js\";\nimport { getCurrentBrowserLocale } from \"./client-locale.js\";\n\n/** basePath from next.config.js, injected by the plugin at build time */\nconst __basePath: string = process.env.__NEXT_ROUTER_BASEPATH ?? \"\";\n/** trailingSlash from next.config.js, injected by the plugin at build time */\nconst __trailingSlash: boolean = process.env.__VINEXT_TRAILING_SLASH === \"true\";\n\ntype BeforePopStateCallback = (state: {\n url: string;\n as: string;\n options: { shallow: boolean };\n}) => boolean;\n\nexport type NextRouter = {\n /** Current pathname */\n pathname: string;\n /** Current route pattern (e.g., \"/posts/[id]\") */\n route: string;\n /** Query parameters */\n query: Record<string, string | string[]>;\n /** Full URL including query string */\n asPath: string;\n /** Base path */\n basePath: string;\n /** Current locale */\n locale?: string;\n /** Available locales */\n locales?: string[];\n /** Default locale */\n defaultLocale?: string;\n /** Configured domain locales */\n domainLocales?: VinextNextData[\"domainLocales\"];\n /** Whether the router is ready */\n isReady: boolean;\n /** Whether this is a preview */\n isPreview: boolean;\n /** Whether this is a fallback page */\n isFallback: boolean;\n\n /** Navigate to a new URL */\n push(url: string | UrlObject, as?: string, options?: TransitionOptions): Promise<boolean>;\n /** Replace current URL */\n replace(url: string | UrlObject, as?: string, options?: TransitionOptions): Promise<boolean>;\n /** Go back */\n back(): void;\n /** Reload the page */\n reload(): void;\n /** Prefetch a page (injects <link rel=\"prefetch\">) */\n prefetch(url: string): Promise<void>;\n /** Register a callback to run before popstate navigation */\n beforePopState(cb: BeforePopStateCallback): void;\n /** Listen for route changes */\n events: RouterEvents;\n};\n\ntype UrlObject = {\n pathname?: string;\n query?: UrlQuery;\n};\n\ntype TransitionOptions = {\n shallow?: boolean;\n scroll?: boolean;\n locale?: string | false;\n};\n\ntype RouterEvents = {\n on(event: string, handler: (...args: unknown[]) => void): void;\n off(event: string, handler: (...args: unknown[]) => void): void;\n emit(event: string, ...args: unknown[]): void;\n};\n\nfunction createRouterEvents(): RouterEvents {\n const listeners = new Map<string, Set<(...args: unknown[]) => void>>();\n\n return {\n on(event: string, handler: (...args: unknown[]) => void) {\n if (!listeners.has(event)) listeners.set(event, new Set());\n (listeners.get(event) as Set<(...args: unknown[]) => void>).add(handler);\n },\n off(event: string, handler: (...args: unknown[]) => void) {\n listeners.get(event)?.delete(handler);\n },\n emit(event: string, ...args: unknown[]) {\n listeners.get(event)?.forEach((handler) => handler(...args));\n },\n };\n}\n\n// Singleton events instance\nconst routerEvents = createRouterEvents();\n\nfunction resolveUrl(url: string | UrlObject): string {\n if (typeof url === \"string\") return url;\n let result = url.pathname ?? \"/\";\n if (url.query) {\n const params = urlQueryToSearchParams(url.query);\n result = appendSearchParamsToUrl(result, params);\n }\n return result;\n}\n\n/**\n * When `as` is provided, use it as the navigation target. This is a\n * simplification: Next.js keeps `url` and `as` as separate values (url for\n * data fetching, as for the browser URL). We collapse them because vinext's\n * navigateClient() fetches HTML from the target URL, so `as` must be a\n * server-resolvable path. Purely decorative `as` values are not supported.\n */\nfunction resolveNavigationTarget(\n url: string | UrlObject,\n as: string | undefined,\n locale: string | undefined,\n): string {\n return applyNavigationLocale(as ?? resolveUrl(url), locale);\n}\n\nfunction getCurrentUrlLocale(): string | undefined {\n return getCurrentBrowserLocale({\n basePath: __basePath,\n domainLocales: getDomainLocales(),\n hostname: getCurrentHostname(),\n });\n}\n\nfunction resolveTransitionLocale(locale: TransitionOptions[\"locale\"]): string | undefined {\n if (typeof window === \"undefined\") return undefined;\n if (locale === false) return window.__VINEXT_DEFAULT_LOCALE__;\n return locale ?? getCurrentUrlLocale();\n}\n\nfunction getDomainLocales(): readonly DomainLocale[] | undefined {\n return (window.__NEXT_DATA__ as VinextNextData | undefined)?.domainLocales;\n}\n\nfunction getCurrentHostname(): string | undefined {\n return window.location?.hostname;\n}\n\nfunction getDomainLocalePath(url: string, locale: string): string | undefined {\n return getDomainLocaleUrl(url, locale, {\n basePath: __basePath,\n currentHostname: getCurrentHostname(),\n domainItems: getDomainLocales(),\n });\n}\n\n/**\n * Apply locale prefix to a URL for client-side navigation.\n * Same logic as Link's applyLocaleToHref but reads from window globals.\n */\nexport function applyNavigationLocale(url: string, locale?: string): string {\n if (!locale || typeof window === \"undefined\") return url;\n // Absolute and protocol-relative URLs must not be prefixed — locale\n // only applies to local paths.\n if (isAbsoluteOrProtocolRelativeUrl(url)) {\n return url;\n }\n if (getLocalePathPrefix(url, window.__VINEXT_LOCALES__)) {\n return url;\n }\n\n const domainLocalePath = getDomainLocalePath(url, locale);\n if (domainLocalePath) return domainLocalePath;\n\n return addLocalePrefix(url, locale, window.__VINEXT_DEFAULT_LOCALE__ ?? \"\");\n}\n\nfunction isDefaultLocaleRootNavigation(url: string, locale: string | undefined): boolean {\n if (typeof window === \"undefined\") return false;\n if (!locale || locale !== window.__VINEXT_DEFAULT_LOCALE__) return false;\n\n let parsed: URL;\n try {\n parsed = new URL(url, window.location.href);\n } catch {\n return false;\n }\n\n return stripBasePath(parsed.pathname, __basePath) === \"/\";\n}\n\nfunction getPagesHtmlFetchUrl(browserUrl: string, locale: string | undefined): string {\n if (!isDefaultLocaleRootNavigation(browserUrl, locale)) return browserUrl;\n\n // Browser URL stays unprefixed for the default locale, but the internal\n // HTML fetch must bypass root Accept-Language detection.\n const parsed = new URL(browserUrl, window.location.href);\n const localeRoot = normalizePathTrailingSlash(`/${locale}`, __trailingSlash);\n // Base path joining can change slash shape, then the final URL must still\n // conform to the app's trailingSlash setting.\n return normalizePathTrailingSlash(\n toBrowserNavigationHref(\n `${localeRoot}${parsed.search}${parsed.hash}`,\n window.location.href,\n __basePath,\n ),\n __trailingSlash,\n );\n}\n\n/** Check if a URL is external (any URL scheme per RFC 3986, or protocol-relative) */\nexport function isExternalUrl(url: string): boolean {\n return isAbsoluteOrProtocolRelativeUrl(url);\n}\n\n/** Resolve a hash URL to a basePath-stripped app URL for event payloads */\nfunction resolveHashUrl(url: string): string {\n if (typeof window === \"undefined\") return url;\n if (url.startsWith(\"#\"))\n return stripBasePath(window.location.pathname, __basePath) + window.location.search + url;\n // Full-path hash URL — strip basePath for consistency with other events\n try {\n const parsed = new URL(url, window.location.href);\n return stripBasePath(parsed.pathname, __basePath) + parsed.search + parsed.hash;\n } catch {\n return url;\n }\n}\n\n/** Check if a href is only a hash change relative to the current URL */\nexport function isHashOnlyChange(href: string): boolean {\n if (href.startsWith(\"#\")) return true;\n if (typeof window === \"undefined\") return false;\n return isHashOnlyBrowserUrlChange(href, window.location.href, __basePath);\n}\n\n/** Save current scroll position into history state for back/forward restoration */\nfunction saveScrollPosition(): void {\n const state = window.history.state ?? {};\n window.history.replaceState(\n { ...state, __vinext_scrollX: window.scrollX, __vinext_scrollY: window.scrollY },\n \"\",\n );\n}\n\n/** Restore scroll position from history state */\nfunction restoreScrollPosition(state: unknown): void {\n if (state && typeof state === \"object\" && \"__vinext_scrollY\" in state) {\n const { __vinext_scrollX: x, __vinext_scrollY: y } = state as {\n __vinext_scrollX: number;\n __vinext_scrollY: number;\n };\n requestAnimationFrame(() => window.scrollTo(x, y));\n }\n}\n\n/**\n * SSR context - set by the dev server before rendering each page.\n */\ntype SSRContext = {\n pathname: string;\n query: Record<string, string | string[]>;\n asPath: string;\n locale?: string;\n locales?: string[];\n defaultLocale?: string;\n domainLocales?: VinextNextData[\"domainLocales\"];\n /**\n * True when rendering a `getStaticPaths` fallback shell for a path that\n * hasn't been pre-rendered yet (`fallback: true` + unlisted path). Mirrors\n * `renderContext.isFallback` in Next.js's `render.tsx`: `getStaticProps`\n * is skipped, the page renders with empty props, and `useRouter().isFallback`\n * returns `true` so user code can show a loading state.\n */\n isFallback?: boolean;\n};\n\n// ---------------------------------------------------------------------------\n// Server-side SSR state uses a registration pattern so this module can be\n// bundled for the browser. The ALS-backed implementation lives in\n// router-state.ts (server-only) and registers itself on import.\n// ---------------------------------------------------------------------------\n\nlet _ssrContext: SSRContext | null = null;\n\nlet _getSSRContext = (): SSRContext | null => _ssrContext;\nlet _setSSRContextImpl = (ctx: SSRContext | null): void => {\n _ssrContext = ctx;\n};\n\n/**\n * Register ALS-backed state accessors. Called by router-state.ts on import.\n * @internal\n */\nexport function _registerRouterStateAccessors(accessors: {\n getSSRContext: () => SSRContext | null;\n setSSRContext: (ctx: SSRContext | null) => void;\n}): void {\n _getSSRContext = accessors.getSSRContext;\n _setSSRContextImpl = accessors.setSSRContext;\n}\n\nexport function setSSRContext(ctx: SSRContext | null): void {\n _setSSRContextImpl(ctx);\n}\n\ntype PagesNavigationContextShape = {\n pathname: string;\n searchParams: URLSearchParams;\n params: Record<string, string | string[]>;\n};\n\n// Client-only cache for snapshot stability. useSyncExternalStore compares\n// snapshots with Object.is, so returning a fresh object on every render would\n// trigger re-render loops. We use a module-level cache keyed by URL inputs;\n// this is safe in the browser because there is exactly one request at a time\n// but it must NOT be used on the server (concurrent ALS-scoped requests).\nlet _cachedClientPagesNavCtx: PagesNavigationContextShape | null = null;\nlet _cachedClientPagesNavCtxKey: string | null = null;\n\nfunction _buildClientPagesNavigationContext(\n routePattern: string,\n resolvedPath: string,\n searchString: string,\n): PagesNavigationContextShape {\n const cacheKey = `${routePattern}|${resolvedPath}|${searchString}`;\n if (_cachedClientPagesNavCtxKey === cacheKey && _cachedClientPagesNavCtx) {\n return _cachedClientPagesNavCtx;\n }\n const searchParams = new URLSearchParams(searchString);\n const params = routePattern ? (extractRouteParamsFromPath(routePattern, resolvedPath) ?? {}) : {};\n const ctx: PagesNavigationContextShape = { pathname: resolvedPath, searchParams, params };\n _cachedClientPagesNavCtx = ctx;\n _cachedClientPagesNavCtxKey = cacheKey;\n return ctx;\n}\n\n/**\n * Cross-router compat shim source for `next/navigation` hooks.\n *\n * Returns the current Pages Router state shaped as a navigation context so\n * the App Router hooks (useParams/useSearchParams/usePathname) can act as\n * compat shims when invoked inside a Pages Router render. Mirrors Next.js's\n * `adaptForPathParams` and `adaptForSearchParams` in\n * .nextjs-ref/packages/next/src/shared/lib/router/adapters.tsx, which Next.js\n * uses to populate SearchParamsContext / PathParamsContext for the Pages\n * Router (see packages/next/src/server/render.tsx and\n * packages/next/src/client/index.tsx).\n *\n * Returns `null` when there is no Pages Router state available — e.g. App\n * Router pages, RSC-only renders, or pre-router renders. Callers should\n * treat null as \"App Router context, use normal app-router state\".\n */\nexport function getPagesNavigationContext(): PagesNavigationContextShape | null {\n if (typeof window === \"undefined\") {\n const ssrCtx = _getSSRContext();\n if (!ssrCtx) return null;\n // ssrCtx.pathname is the route pattern (e.g. \"/blog/[slug]\").\n // ssrCtx.asPath is the resolved URL with query string. For useSearchParams\n // we want only the URL search string; for useParams we want only the\n // dynamic route params. Build a fresh object each call — server scope is\n // request-isolated via ALS but module state must not be cached across\n // concurrent requests.\n let searchParams: URLSearchParams;\n let resolvedPath: string;\n try {\n const url = new URL(ssrCtx.asPath, \"http://_\");\n searchParams = url.searchParams;\n resolvedPath = url.pathname;\n } catch {\n searchParams = new URLSearchParams();\n resolvedPath = ssrCtx.pathname;\n }\n const params = extractRouteParamsFromPath(ssrCtx.pathname, resolvedPath) ?? {};\n return { pathname: resolvedPath, searchParams, params };\n }\n\n // Client: derive from window.location + __NEXT_DATA__. __NEXT_DATA__.page\n // is the route pattern that was matched; navigateClient() keeps it in sync\n // with the visible URL on every client-side navigation. Cached so\n // useSyncExternalStore sees a stable snapshot between renders.\n const resolvedPath = stripBasePath(window.location.pathname, __basePath);\n const pattern = window.__NEXT_DATA__?.page ?? \"\";\n return _buildClientPagesNavigationContext(pattern, resolvedPath, window.location.search);\n}\n\n/**\n * Extract param names from a Next.js route pattern.\n * E.g., \"/posts/[id]\" → [\"id\"], \"/docs/[...slug]\" → [\"slug\"],\n * \"/shop/[[...path]]\" → [\"path\"], \"/blog/[year]/[month]\" → [\"year\", \"month\"]\n * Also handles internal format: \"/posts/:id\" → [\"id\"], \"/docs/:slug+\" → [\"slug\"]\n */\nfunction extractRouteParamNames(pattern: string): string[] {\n const names: string[] = [];\n // Match Next.js bracket format: [id], [...slug], [[...slug]]\n // Accepts any non-] characters inside brackets (Next.js PARAMETER_PATTERN parity).\n const bracketMatches = pattern.matchAll(/\\[{1,2}(?:\\.\\.\\.)?([^\\]]+)\\]{1,2}/g);\n for (const m of bracketMatches) {\n names.push(m[1]);\n }\n if (names.length > 0) return names;\n // Fallback: match internal :param format (any chars except /, +, *)\n const colonMatches = pattern.matchAll(/:([^/+*]+)[+*]?/g);\n for (const m of colonMatches) {\n names.push(m[1]);\n }\n return names;\n}\n\ntype RouteQueryNextData = {\n page?: string;\n query?: Record<string, string | string[] | undefined>;\n};\n\nfunction splitPathSegments(pathname: string): string[] {\n return pathname.split(\"/\").filter(Boolean);\n}\n\nfunction extractRouteParamsFromPath(\n pattern: string,\n pathname: string,\n): Record<string, string | string[]> | null {\n return matchRoutePattern(splitPathSegments(pathname), routePatternParts(pattern));\n}\n\nfunction getRouteQueryFromNextData(\n nextData: RouteQueryNextData | undefined,\n resolvedPath: string,\n): Record<string, string | string[]> {\n const routeQuery: Record<string, string | string[]> = {};\n if (!nextData?.query || !nextData.page) return routeQuery;\n\n const routeParamNames = extractRouteParamNames(nextData.page);\n if (routeParamNames.length === 0) return routeQuery;\n\n const currentRouteParams = extractRouteParamsFromPath(nextData.page, resolvedPath);\n if (currentRouteParams) return currentRouteParams;\n\n for (const key of routeParamNames) {\n const value = nextData.query[key];\n if (typeof value === \"string\") {\n routeQuery[key] = value;\n } else if (Array.isArray(value)) {\n routeQuery[key] = [...value];\n }\n }\n return routeQuery;\n}\n\nfunction getPathnameAndQuery(): {\n pathname: string;\n query: Record<string, string | string[]>;\n asPath: string;\n} {\n if (typeof window === \"undefined\") {\n const _ssrCtx = _getSSRContext();\n if (_ssrCtx) {\n const query: Record<string, string | string[]> = {};\n for (const [key, value] of Object.entries(_ssrCtx.query)) {\n query[key] = Array.isArray(value) ? [...value] : value;\n }\n return { pathname: _ssrCtx.pathname, query, asPath: _ssrCtx.asPath };\n }\n return { pathname: \"/\", query: {}, asPath: \"/\" };\n }\n const resolvedPath = stripBasePath(window.location.pathname, __basePath);\n // In Next.js, router.pathname is the route pattern (e.g., \"/posts/[id]\"),\n // not the resolved path (\"/posts/42\"). __NEXT_DATA__.page holds the route\n // pattern and is updated by navigateClient() on every client-side navigation.\n const pathname = window.__NEXT_DATA__?.page ?? resolvedPath;\n const nextData = window.__NEXT_DATA__;\n const routeQuery = getRouteQueryFromNextData(nextData, resolvedPath);\n // URL search params always reflect the current URL\n const searchQuery: Record<string, string | string[]> = {};\n const params = new URLSearchParams(window.location.search);\n for (const [key, value] of params) {\n addQueryParam(searchQuery, key, value);\n }\n const query = { ...searchQuery, ...routeQuery };\n // asPath uses the resolved browser path, not the route pattern\n const asPath = resolvedPath + window.location.search + window.location.hash;\n return { pathname, query, asPath };\n}\n\n/**\n * Error thrown when a navigation is superseded by a newer one.\n * Matches Next.js's convention of an Error with `.cancelled = true`.\n */\nclass NavigationCancelledError extends Error {\n cancelled = true;\n constructor(route: string) {\n super(`Abort fetching component for route: \"${route}\"`);\n this.name = \"NavigationCancelledError\";\n }\n}\n\n/**\n * Error thrown after queueing a hard navigation fallback for a known failure\n * mode. Callers can use this to avoid scheduling the same hard navigation twice.\n */\nclass HardNavigationScheduledError extends Error {\n hardNavigationScheduled = true;\n constructor(message: string) {\n super(message);\n this.name = \"HardNavigationScheduledError\";\n }\n}\n\n/**\n * Monotonically increasing ID for tracking the current navigation.\n * Each call to navigateClient() increments this and captures the value.\n * After each async boundary, the navigation checks whether it is still\n * the active one. If a newer navigation has started, the stale one\n * throws NavigationCancelledError so the caller can emit routeChangeError\n * and skip routeChangeComplete.\n *\n * Replaces the old boolean `_navInProgress` guard which silently dropped\n * the second navigation, causing URL/content mismatch.\n */\nlet _navigationId = 0;\n\n/** AbortController for the in-flight fetch, so superseded navigations abort network I/O. */\nlet _activeAbortController: AbortController | null = null;\n\nfunction scheduleHardNavigationAndThrow(url: string, message: string): never {\n if (typeof window === \"undefined\") {\n throw new HardNavigationScheduledError(message);\n }\n window.location.href = url;\n throw new HardNavigationScheduledError(message);\n}\n\n/**\n * Perform client-side navigation: fetch the target page's HTML,\n * extract __NEXT_DATA__, and re-render the React root.\n *\n * Throws NavigationCancelledError if a newer navigation supersedes this one.\n * Throws on hard-navigation failures (non-OK response, missing data) so the\n * caller can distinguish success from failure for event emission.\n */\nasync function navigateClient(url: string, fetchUrl = url): Promise<void> {\n if (typeof window === \"undefined\") return;\n\n const root = window.__VINEXT_ROOT__;\n if (!root) {\n // No React root yet — fall back to hard navigation\n window.location.href = url;\n return;\n }\n\n // Cancel any in-flight navigation (abort its fetch, mark it stale)\n _activeAbortController?.abort();\n const controller = new AbortController();\n _activeAbortController = controller;\n\n const navId = ++_navigationId;\n\n /** Check if this navigation is still the active one. If not, throw. */\n function assertStillCurrent(): void {\n if (navId !== _navigationId) {\n throw new NavigationCancelledError(url);\n }\n }\n\n try {\n // Fetch the target page's SSR HTML\n let res: Response;\n try {\n res = await fetch(fetchUrl, {\n headers: { Accept: \"text/html\" },\n signal: controller.signal,\n });\n } catch (err: unknown) {\n // AbortError means a newer navigation cancelled this fetch\n if (err instanceof DOMException && err.name === \"AbortError\") {\n throw new NavigationCancelledError(url);\n }\n throw err;\n }\n assertStillCurrent();\n\n if (!res.ok) {\n // Set window.location.href first so the browser navigates to the correct\n // page even if the caller suppresses the error. The assignment schedules\n // the navigation asynchronously (as a task), so synchronous routeChangeError\n // listeners still run — and observe the error — before the page unloads.\n // Contract: routeChangeError listeners MUST be synchronous; async listeners\n // will not fire before the navigation completes. Callers (runNavigateClient)\n // must NOT schedule a second hard navigation — this assignment already queues\n // the browser fallback, and the helper-level HardNavigationScheduledError\n // makes that contract explicit to callers.\n scheduleHardNavigationAndThrow(url, `Navigation failed: ${res.status} ${res.statusText}`);\n }\n\n const html = await res.text();\n assertStillCurrent();\n\n // Extract __NEXT_DATA__ from the HTML\n const nextDataJson = extractVinextNextDataJson(html);\n if (!nextDataJson) {\n scheduleHardNavigationAndThrow(url, \"Navigation failed: missing __NEXT_DATA__ in response\");\n }\n\n const nextData = parseVinextNextDataJson(nextDataJson);\n const { pageProps } = nextData.props;\n // Defer writing window.__NEXT_DATA__ until just before root.render() —\n // writing it here would let a stale navigation briefly pollute the global\n // between this assertStillCurrent() and the next one after await import().\n\n // Get the page module URL from __NEXT_DATA__.__vinext (preferred),\n // or fall back to parsing the hydration script\n let pageModuleUrl: string | undefined = nextData.__vinext?.pageModuleUrl;\n\n if (!pageModuleUrl) {\n // Legacy fallback: try to find the module URL in the inline script\n const moduleMatch = html.match(/import\\(\"([^\"]+)\"\\);\\s*\\n\\s*const PageComponent/);\n const altMatch = html.match(/await import\\(\"([^\"]+pages\\/[^\"]+)\"\\)/);\n pageModuleUrl = moduleMatch?.[1] ?? altMatch?.[1] ?? undefined;\n }\n\n if (!pageModuleUrl) {\n scheduleHardNavigationAndThrow(url, \"Navigation failed: no page module URL found\");\n }\n\n // Validate the module URL before importing — defense-in-depth against\n // unexpected __NEXT_DATA__ or malformed HTML responses\n if (!isValidModulePath(pageModuleUrl)) {\n console.error(\"[vinext] Blocked import of invalid page module path:\", pageModuleUrl);\n scheduleHardNavigationAndThrow(url, \"Navigation failed: invalid page module path\");\n }\n\n // Dynamically import the new page module\n const pageModule = await import(/* @vite-ignore */ pageModuleUrl);\n assertStillCurrent();\n\n const PageComponent = pageModule.default;\n\n if (!PageComponent) {\n scheduleHardNavigationAndThrow(url, \"Navigation failed: page module has no default export\");\n }\n\n // Import React for createElement\n const React = (await import(\"react\")).default;\n assertStillCurrent();\n\n // Re-render with the new page, loading _app if needed\n let AppComponent = window.__VINEXT_APP__;\n const appModuleUrl: string | undefined = nextData.__vinext?.appModuleUrl;\n\n if (!AppComponent && appModuleUrl) {\n if (!isValidModulePath(appModuleUrl)) {\n console.error(\"[vinext] Blocked import of invalid app module path:\", appModuleUrl);\n } else {\n try {\n const appModule = await import(/* @vite-ignore */ appModuleUrl);\n AppComponent = appModule.default;\n window.__VINEXT_APP__ = AppComponent;\n } catch {\n // _app not available — continue without it\n }\n }\n }\n assertStillCurrent();\n\n let element;\n if (AppComponent) {\n element = React.createElement(AppComponent, {\n Component: PageComponent,\n pageProps,\n });\n } else {\n element = React.createElement(PageComponent, pageProps);\n }\n\n // Wrap with RouterContext.Provider so next/router and next/compat/router work.\n element = wrapWithRouterContext(element);\n\n // Commit __NEXT_DATA__ only after all assertStillCurrent() checks have passed,\n // so a stale navigation can never pollute the global.\n // INVARIANT: Everything after the final assertStillCurrent() above (the\n // checkpoint immediately after the optional _app import) through\n // root.render() is synchronous. If any step here ever becomes async, add\n // another assertStillCurrent() before writing __NEXT_DATA__.\n window.__NEXT_DATA__ = nextData;\n applyVinextLocaleGlobals(window, nextData);\n root.render(element);\n } finally {\n // Clean up the abort controller if this navigation is still the active one\n if (navId === _navigationId) {\n _activeAbortController = null;\n }\n }\n}\n\n/**\n * Run navigateClient and handle errors: emit routeChangeError on failure,\n * and fall back to a hard navigation for non-cancel errors so the browser\n * recovers to a consistent state.\n *\n * Returns:\n * - \"completed\" — navigation finished, caller should emit routeChangeComplete\n * - \"cancelled\" — superseded by a newer navigation, caller should return true\n * without emitting routeChangeComplete (matches Next.js behaviour)\n * - \"failed\" — genuine error, caller should return false (hard nav is already\n * scheduled as recovery)\n */\nasync function runNavigateClient(\n fullUrl: string,\n resolvedUrl: string,\n fetchUrl = fullUrl,\n): Promise<\"completed\" | \"cancelled\" | \"failed\"> {\n try {\n await navigateClient(fullUrl, fetchUrl);\n return \"completed\";\n } catch (err: unknown) {\n routerEvents.emit(\"routeChangeError\", err, resolvedUrl, { shallow: false });\n if (err instanceof NavigationCancelledError) {\n return \"cancelled\";\n }\n // Genuine error (network, parse, import failure): fall back to a hard\n // navigation so the browser lands on the correct page. Known failure modes\n // throw HardNavigationScheduledError, and this guard skips those; only\n // unexpected failures (parse, import, render) need recovery here.\n if (typeof window !== \"undefined\" && !(err instanceof HardNavigationScheduledError)) {\n window.location.href = fullUrl;\n }\n return \"failed\";\n }\n}\n\n/**\n * Build the full router value object from the current pathname, query, asPath,\n * and a set of navigation methods. Shared by the Pages Router context provider\n * and tests so the public router shape stays in sync.\n */\nfunction buildRouterValue(\n pathname: string,\n query: Record<string, string | string[]>,\n asPath: string,\n methods: {\n push: NextRouter[\"push\"];\n replace: NextRouter[\"replace\"];\n back: NextRouter[\"back\"];\n reload: NextRouter[\"reload\"];\n prefetch: NextRouter[\"prefetch\"];\n beforePopState: NextRouter[\"beforePopState\"];\n },\n): NextRouter {\n const _ssrState = _getSSRContext();\n const nextData =\n typeof window !== \"undefined\"\n ? (window.__NEXT_DATA__ as VinextNextData | undefined)\n : undefined;\n const locale = typeof window === \"undefined\" ? _ssrState?.locale : window.__VINEXT_LOCALE__;\n const locales = typeof window === \"undefined\" ? _ssrState?.locales : window.__VINEXT_LOCALES__;\n const defaultLocale =\n typeof window === \"undefined\" ? _ssrState?.defaultLocale : window.__VINEXT_DEFAULT_LOCALE__;\n const domainLocales =\n typeof window === \"undefined\" ? _ssrState?.domainLocales : nextData?.domainLocales;\n\n const route = typeof window !== \"undefined\" ? (nextData?.page ?? pathname) : pathname;\n\n return {\n pathname,\n route,\n query,\n asPath,\n basePath: __basePath,\n locale,\n locales,\n defaultLocale,\n domainLocales,\n isReady: true,\n isPreview: false,\n isFallback:\n typeof window !== \"undefined\"\n ? nextData?.isFallback === true\n : _ssrState?.isFallback === true,\n ...methods,\n events: routerEvents,\n };\n}\n\n/** Extract the hash fragment from a URL, including the leading `#`. */\nfunction extractHash(url: string): string {\n const i = url.indexOf(\"#\");\n return i === -1 ? \"\" : url.slice(i);\n}\n\n/** Notify in-page listeners (e.g. useRouter hooks) that navigation occurred. */\nfunction dispatchNavigateEvent(): void {\n window.dispatchEvent(new CustomEvent(\"vinext:navigate\"));\n}\n\n/**\n * Update history with the new URL and refresh the hash-only-detection tracker.\n * Centralises the `pushState`/`replaceState` branch so callers don't repeat it.\n */\nfunction updateHistory(mode: \"push\" | \"replace\", url: string): void {\n if (mode === \"push\") window.history.pushState({}, \"\", url);\n else window.history.replaceState({}, \"\", url);\n _lastPathnameAndSearch = window.location.pathname + window.location.search;\n}\n\n/**\n * Throw the canonical \"no router instance\" error used when a Pages Router\n * navigation method (push/replace/back/reload/prefetch/beforePopState) is\n * invoked during SSR or prerendering.\n *\n * Mirrors Next.js's `ServerRouter.push`/`replace`/etc. which all call\n * `noRouter()` in `packages/next/src/server/render.tsx`. The error message\n * matches Next.js verbatim so userland error handling and docs links work\n * unchanged.\n *\n * Ported from Next.js: packages/next/src/server/render.tsx\n * https://github.com/vercel/next.js/blob/canary/packages/next/src/server/render.tsx\n */\nfunction throwNoRouterInstance(): never {\n throw new Error(\n 'No router instance found. you should only use \"next/router\" inside the client side of your app. https://nextjs.org/docs/messages/no-router-instance',\n );\n}\n\n/**\n * Shared client-side navigation flow used by both `useRouter()` and the\n * `Router` singleton. The only differences between push/replace are the\n * history method (`pushState` vs `replaceState`), the external-URL fallback\n * (`assign` vs `replace`), and the fact that push saves scroll position for\n * back/forward restoration while replace does not.\n *\n * `onStateUpdate` lets the hook trigger a `setState` re-render at the same\n * point that hashChangeComplete/routeChangeComplete fires; the singleton\n * passes no callback.\n */\nasync function performNavigation(\n url: string | UrlObject,\n as: string | undefined,\n options: TransitionOptions | undefined,\n mode: \"push\" | \"replace\",\n onStateUpdate?: () => void,\n): Promise<boolean> {\n // SSR / prerender guard. Calling Router.push or Router.replace from a\n // Pages Router component during server rendering would otherwise crash\n // with `ReferenceError: window is not defined` (window.location is\n // accessed unconditionally below). Match Next.js's `ServerRouter.push`\n // behaviour and throw the documented \"no router instance\" error so the\n // failure surfaces as a normal render error instead of a ReferenceError\n // that takes down the request pipeline.\n if (typeof window === \"undefined\") {\n throwNoRouterInstance();\n }\n\n // Block dangerous URI schemes (javascript:, data:, vbscript:) before any\n // navigation work happens. Mirrors Next.js's Pages Router guard at\n // packages/next/src/shared/lib/router/router.ts:1020-1028,1052-1060, which\n // throws and (via React's event-handler runtime) surfaces a console.error\n // that the `test/e2e/app-dir/javascript-urls/javascript-urls.test.ts` suite\n // asserts on. `assertSafeNavigationUrl` emits the matching console.error\n // before throwing so the same observable behaviour holds when the throw is\n // swallowed by an async event handler (e.g. Link's click delegation).\n assertSafeNavigationUrl(resolveUrl(url));\n if (as !== undefined) {\n assertSafeNavigationUrl(String(as));\n }\n\n const navigationLocale = resolveTransitionLocale(options?.locale);\n let resolved = resolveNavigationTarget(url, as, navigationLocale);\n\n // External URLs — delegate to browser (unless same-origin)\n if (isExternalUrl(resolved)) {\n const localPath = toSameOriginAppPath(resolved, __basePath);\n if (localPath == null) {\n if (mode === \"push\") window.location.assign(resolved);\n else window.location.replace(resolved);\n return true;\n }\n resolved = localPath;\n }\n\n resolved = normalizePathTrailingSlash(resolved, __trailingSlash);\n const full = normalizePathTrailingSlash(\n toBrowserNavigationHref(resolved, window.location.href, __basePath),\n __trailingSlash,\n );\n const htmlFetchUrl = getPagesHtmlFetchUrl(full, navigationLocale);\n const shallow = options?.shallow ?? false;\n const doScroll = options?.scroll !== false;\n\n // Hash-only change — no page fetch needed\n if (isHashOnlyChange(full)) {\n const eventUrl = resolveHashUrl(full);\n routerEvents.emit(\"hashChangeStart\", eventUrl, { shallow });\n updateHistory(mode, resolved.startsWith(\"#\") ? resolved : full);\n if (doScroll) scrollToHashTarget(extractHash(resolved));\n onStateUpdate?.();\n routerEvents.emit(\"hashChangeComplete\", eventUrl, { shallow });\n dispatchNavigateEvent();\n return true;\n }\n\n if (mode === \"push\") saveScrollPosition();\n routerEvents.emit(\"routeChangeStart\", resolved, { shallow });\n routerEvents.emit(\"beforeHistoryChange\", resolved, { shallow });\n updateHistory(mode, full);\n if (!shallow) {\n const result = await runNavigateClient(full, resolved, htmlFetchUrl);\n if (result === \"cancelled\") return true;\n if (result === \"failed\") return false;\n }\n onStateUpdate?.();\n routerEvents.emit(\"routeChangeComplete\", resolved, { shallow });\n\n const hash = extractHash(resolved);\n if (doScroll) {\n if (hash) scrollToHashTarget(hash);\n else window.scrollTo(0, 0);\n }\n dispatchNavigateEvent();\n return true;\n}\n\n/** Inject a `<link rel=\"prefetch\">` for the target page. */\nasync function prefetchUrl(url: string): Promise<void> {\n if (typeof document !== \"undefined\") {\n const link = document.createElement(\"link\");\n link.rel = \"prefetch\";\n link.href = url;\n link.as = \"document\";\n document.head.appendChild(link);\n }\n}\n\n/**\n * useRouter hook - Pages Router compatible.\n *\n * Ported from Next.js: packages/next/src/client/router.ts\n * https://github.com/vercel/next.js/blob/canary/packages/next/src/client/router.ts\n */\nexport function useRouter(): NextRouter {\n const router = useContext(RouterContext);\n if (!router) {\n throw new Error(\n \"NextRouter was not mounted. https://nextjs.org/docs/messages/next-router-not-mounted\",\n );\n }\n\n return router;\n}\n\nfunction PagesRouterProvider({ children }: { children: ReactNode }): ReactElement {\n const [{ pathname, query, asPath }, setState] = useState(getPathnameAndQuery);\n\n // Popstate is handled by the Pages Router client entry via\n // installPagesRouterRuntime() so beforePopState() is consistently enforced\n // regardless of hook consumers. Keep URL snapshot subscriptions at the\n // provider boundary so many useRouter() calls share one router state and one\n // vinext:navigate listener.\n useEffect(() => {\n const onNavigate = ((_e: CustomEvent) => {\n setState(getPathnameAndQuery());\n }) as EventListener;\n window.addEventListener(\"vinext:navigate\", onNavigate);\n return () => window.removeEventListener(\"vinext:navigate\", onNavigate);\n }, []);\n\n const router = useMemo(\n (): NextRouter =>\n buildRouterValue(pathname, query, asPath, {\n push: Router.push,\n replace: Router.replace,\n back: Router.back,\n reload: Router.reload,\n prefetch: Router.prefetch,\n beforePopState: Router.beforePopState,\n }),\n [pathname, query, asPath],\n );\n\n return createElement(RouterContext.Provider, { value: router }, children);\n}\n\n// beforePopState callback: called before handling browser back/forward.\n// If it returns false, the navigation is cancelled.\nlet _beforePopStateCb: BeforePopStateCallback | undefined;\n\n// Track pathname+search for detecting hash-only back/forward in the popstate\n// handler. Updated after every pushState/replaceState so that popstate can\n// compare the previous value with the (already-changed) window.location.\nlet _lastPathnameAndSearch =\n typeof window !== \"undefined\" ? window.location.pathname + window.location.search : \"\";\n\nfunction handlePagesRouterPopState(e: PopStateEvent): void {\n const browserUrl = window.location.pathname + window.location.search;\n const appUrl = stripBasePath(window.location.pathname, __basePath) + window.location.search;\n\n // Detect hash-only back/forward: pathname+search unchanged, only hash differs.\n const isHashOnly = browserUrl === _lastPathnameAndSearch;\n\n // Check beforePopState callback\n if (_beforePopStateCb !== undefined) {\n const shouldContinue = _beforePopStateCb({\n url: appUrl,\n as: appUrl,\n options: { shallow: false },\n });\n if (!shouldContinue) return;\n }\n\n // Update tracker only after beforePopState confirms navigation proceeds.\n // If beforePopState cancels, the tracker must retain the previous value\n // so the next popstate compares against the correct baseline.\n _lastPathnameAndSearch = browserUrl;\n\n if (isHashOnly) {\n // Hash-only back/forward — no page fetch needed\n const hashUrl = appUrl + window.location.hash;\n routerEvents.emit(\"hashChangeStart\", hashUrl, { shallow: false });\n scrollToHashTarget(window.location.hash);\n routerEvents.emit(\"hashChangeComplete\", hashUrl, { shallow: false });\n dispatchNavigateEvent();\n return;\n }\n\n const fullAppUrl = appUrl + window.location.hash;\n routerEvents.emit(\"routeChangeStart\", fullAppUrl, { shallow: false });\n // Note: The browser has already updated window.location by the time popstate\n // fires, so this is not truly \"before\" the URL change. In Next.js the popstate\n // handler calls replaceState to store history metadata — beforeHistoryChange\n // precedes that call, not the URL change itself. We emit it here for API\n // compatibility.\n routerEvents.emit(\"beforeHistoryChange\", fullAppUrl, { shallow: false });\n void (async () => {\n const result = await runNavigateClient(\n browserUrl,\n fullAppUrl,\n getPagesHtmlFetchUrl(browserUrl, window.__VINEXT_LOCALE__),\n );\n if (result === \"completed\") {\n routerEvents.emit(\"routeChangeComplete\", fullAppUrl, { shallow: false });\n restoreScrollPosition(e.state);\n dispatchNavigateEvent();\n }\n // \"cancelled\": superseded by a newer navigation, so this popstate no longer wins.\n // \"failed\": runNavigateClient already scheduled the hard-navigation fallback.\n })();\n}\n\nsetPagesRouterPopStateHandler(handlePagesRouterPopState);\n\n/**\n * Wrap a React element in a RouterContext.Provider so that\n * next/compat/router's useRouter() returns the real Pages Router value.\n *\n * The provider owns the reactive Pages Router snapshot so next/router and\n * next/compat/router consumers share one context value instead of each hook\n * installing its own global URL-change listener.\n */\nexport function wrapWithRouterContext(element: ReactElement): ReactElement {\n return createElement(PagesRouterProvider, null, element);\n}\n\n/**\n * Props injected by `withRouter` into the wrapped component.\n *\n * Ported from Next.js: packages/next/src/client/with-router.tsx\n * https://github.com/vercel/next.js/blob/canary/packages/next/src/client/with-router.tsx\n */\nexport type WithRouterProps = {\n router: NextRouter;\n};\n\n/**\n * Pick<P, Exclude<keyof P, keyof WithRouterProps>> — the props of the\n * composed component minus the `router` prop that `withRouter` injects.\n *\n * Ported from Next.js: packages/next/src/client/with-router.tsx\n */\nexport type ExcludeRouterProps<P> = Pick<P, Exclude<keyof P, keyof WithRouterProps>>;\n\n/**\n * Higher-order component that injects the Pages Router `router` instance as\n * a `router` prop into a wrapped component. Primarily used by class\n * components (which cannot call hooks) to access the router. The wrapped\n * component receives the same props as the original, minus `router`, which\n * is filled in by the HOC.\n *\n * Ported from Next.js: packages/next/src/client/with-router.tsx\n * https://github.com/vercel/next.js/blob/canary/packages/next/src/client/with-router.tsx\n *\n * Differences from Next.js:\n * - We type the composed component as `ComponentType<P>` instead of\n * `NextComponentType<C, any, P>` because vinext does not expose\n * `NextComponentType` from this shim. The runtime shape (and the props\n * the wrapper forwards) is identical.\n * - We forward `getInitialProps` and `origGetInitialProps` from the\n * composed component so `_app` parity holds for class components that\n * define `getInitialProps`.\n */\nexport function withRouter<P extends WithRouterProps>(\n ComposedComponent: ComponentType<P>,\n): ComponentType<ExcludeRouterProps<P>> {\n function WithRouterWrapper(props: ExcludeRouterProps<P>): ReactElement {\n const router = useRouter();\n // Match Next.js spread order:\n // `<ComposedComponent router={useRouter()} {...props} />`\n // The injected `router` is placed first, and `{...props}` is spread\n // after, so a user-passed `router` prop overrides the HOC-injected\n // one (last-spread wins). Mirrors\n // packages/next/src/client/with-router.tsx. At the type level\n // `props: ExcludeRouterProps<P>` has no `router` key, but TS still\n // sees `P` as `WithRouterProps`-extending when checking the literal,\n // so we widen to a `Record` for the final prop bag.\n const merged: Record<string, unknown> = { router, ...(props as Record<string, unknown>) };\n return createElement(ComposedComponent, merged as unknown as P);\n }\n\n // Forward getInitialProps so class-component pages that define it keep\n // working when wrapped. Mirrors Next.js's with-router.tsx.\n const composed = ComposedComponent as ComponentType<P> & {\n getInitialProps?: unknown;\n origGetInitialProps?: unknown;\n };\n (WithRouterWrapper as unknown as { getInitialProps?: unknown }).getInitialProps =\n composed.getInitialProps;\n (WithRouterWrapper as unknown as { origGetInitialProps?: unknown }).origGetInitialProps =\n composed.origGetInitialProps;\n\n if (process.env.NODE_ENV !== \"production\") {\n const name = composed.displayName || composed.name || \"Unknown\";\n WithRouterWrapper.displayName = `withRouter(${name})`;\n }\n\n return WithRouterWrapper;\n}\n\n// Note: `withRouter` is exposed only as a named export from `next/router`.\n// The default export of that module is the Router singleton declared below.\n\n// Also export a default Router singleton for `import Router from 'next/router'`.\n//\n// State fields (`pathname`, `route`, `query`, `asPath`, etc.) are exposed as\n// live getters so `window.next.router.pathname` reflects the current URL\n// without callers needing to know about React render cycles. Mirrors\n// Next.js's `singletonRouter` shape from\n// .nextjs-ref/packages/next/src/client/router.ts (lines 32–47), which uses\n// `Object.defineProperty` to forward `urlPropertyFields` to the active\n// router instance. The Next.js deploy test suite drives navigations through\n// `browser.eval('next.router.push(...)')` and then reads\n// `browser.eval('next.router.pathname')` to assert success, so the fields\n// must be readable, not just the methods.\n//\n// Every navigation method is also guarded against SSR/prerender execution.\n// Matches Next.js's `ServerRouter` (packages/next/src/server/render.tsx) which\n// throws `noRouter()` from push/replace/back/reload/prefetch/beforePopState so\n// that invoking them during server rendering surfaces as a documented render\n// error rather than a `ReferenceError: window is not defined`. The throws are\n// synchronous (not via the returned Promise) so render-time callers see the\n// error inline — matching Next.js behaviour and avoiding unhandled rejections.\nconst RouterMethods = {\n push: (url: string | UrlObject, as?: string, options?: TransitionOptions) => {\n if (typeof window === \"undefined\") throwNoRouterInstance();\n return performNavigation(url, as, options, \"push\");\n },\n replace: (url: string | UrlObject, as?: string, options?: TransitionOptions) => {\n if (typeof window === \"undefined\") throwNoRouterInstance();\n return performNavigation(url, as, options, \"replace\");\n },\n back: () => {\n if (typeof window === \"undefined\") throwNoRouterInstance();\n window.history.back();\n },\n reload: () => {\n if (typeof window === \"undefined\") throwNoRouterInstance();\n window.location.reload();\n },\n prefetch: (url: string) => {\n if (typeof window === \"undefined\") throwNoRouterInstance();\n return prefetchUrl(url);\n },\n beforePopState: (cb: BeforePopStateCallback) => {\n if (typeof window === \"undefined\") throwNoRouterInstance();\n _beforePopStateCb = cb;\n },\n events: routerEvents,\n};\n\nconst Router: typeof RouterMethods & Omit<NextRouter, keyof typeof RouterMethods> =\n Object.defineProperties(RouterMethods, {\n pathname: {\n enumerable: true,\n get(): string {\n return getPathnameAndQuery().pathname;\n },\n },\n route: {\n enumerable: true,\n get(): string {\n const { pathname } = getPathnameAndQuery();\n if (typeof window === \"undefined\") return pathname;\n const nextData = window.__NEXT_DATA__ as VinextNextData | undefined;\n return nextData?.page ?? pathname;\n },\n },\n query: {\n enumerable: true,\n get(): Record<string, string | string[]> {\n return getPathnameAndQuery().query;\n },\n },\n asPath: {\n enumerable: true,\n get(): string {\n return getPathnameAndQuery().asPath;\n },\n },\n basePath: { enumerable: true, value: __basePath, writable: false },\n locale: {\n enumerable: true,\n get(): string | undefined {\n if (typeof window === \"undefined\") return _getSSRContext()?.locale;\n return window.__VINEXT_LOCALE__;\n },\n },\n locales: {\n enumerable: true,\n get(): string[] | undefined {\n if (typeof window === \"undefined\") return _getSSRContext()?.locales;\n return window.__VINEXT_LOCALES__;\n },\n },\n defaultLocale: {\n enumerable: true,\n get(): string | undefined {\n if (typeof window === \"undefined\") return _getSSRContext()?.defaultLocale;\n return window.__VINEXT_DEFAULT_LOCALE__;\n },\n },\n domainLocales: {\n enumerable: true,\n get(): VinextNextData[\"domainLocales\"] | undefined {\n if (typeof window === \"undefined\") return _getSSRContext()?.domainLocales;\n return (window.__NEXT_DATA__ as VinextNextData | undefined)?.domainLocales;\n },\n },\n isReady: { enumerable: true, value: true, writable: false },\n isPreview: { enumerable: true, value: false, writable: false },\n isFallback: {\n enumerable: true,\n get(): boolean {\n if (typeof window === \"undefined\") return _getSSRContext()?.isFallback === true;\n return (window.__NEXT_DATA__ as VinextNextData | undefined)?.isFallback === true;\n },\n },\n }) as typeof RouterMethods & Omit<NextRouter, keyof typeof RouterMethods>;\n\n// Expose `window.next.router` for Next.js parity. Pages Router test suites,\n// userland scripts, and third-party libraries reach for this global directly\n// (e.g. `window.next.router.push(...)`, `window.next.router.events.on(...)`,\n// `window.next.router.pathname`).\n// Without this assignment, those callers crash with\n// `TypeError: Cannot read properties of undefined (reading 'router')`.\n//\n// Ported from Next.js: `packages/next/src/client/next.ts` (line 13). We do\n// NOT use a live-binding getter like Next.js does because vinext's Router\n// singleton is constructed synchronously here, so by the time this module\n// finishes loading the value is final.\nif (typeof window !== \"undefined\") {\n // Cast: `NextRouter.push`/`replace` are typed with narrow parameters\n // (UrlObject | string) while `PagesRouterPublicInstance` accepts unknown\n // args. The two are structurally compatible at runtime; TypeScript flags\n // the narrowing of contravariant function params, which is benign here\n // because callers reading off `window.next.router` are tests/userland\n // and treat the surface as opaque.\n installWindowNext({ router: Router as unknown as PagesRouterPublicInstance });\n}\n\n// Register the Pages Router compat shim source for `next/navigation` hooks\n// (useParams / useSearchParams / usePathname). The accessor is exposed\n// through a well-known Symbol.for so navigation.ts can read it without\n// importing this module — that avoids triggering navigation.ts's\n// `window.history.pushState` patch in tests that only need next/router.\n//\n// Mirrors Next.js's behavior where the pages-router server (render.tsx) and\n// client (client/index.tsx) wrap pages with SearchParamsContext /\n// PathParamsContext / PathnameContext providers populated from the router.\nconst _PAGES_NAVIGATION_ACCESSOR_KEY = Symbol.for(\n \"vinext.navigation.pagesNavigationContextAccessor\",\n);\n(globalThis as Record<PropertyKey, unknown>)[_PAGES_NAVIGATION_ACCESSOR_KEY] =\n getPagesNavigationContext;\n\nexport default Router;\n"],"mappings":"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"}
@@ -20,6 +20,7 @@ declare class NextRequest extends Request {
20
20
  locales: string[];
21
21
  defaultLocale: string;
22
22
  };
23
+ trailingSlash?: boolean;
23
24
  };
24
25
  });
25
26
  get nextUrl(): NextURL;
@@ -59,12 +60,12 @@ declare class NextResponse<_Body = unknown> extends Response {
59
60
  /**
60
61
  * Create a redirect response.
61
62
  */
62
- static redirect(url: string | URL, init?: number | ResponseInit): NextResponse;
63
+ static redirect(url: string | URL | NextURL, init?: number | ResponseInit): NextResponse;
63
64
  /**
64
65
  * Create a rewrite response (middleware pattern).
65
66
  * Sets the x-middleware-rewrite header.
66
67
  */
67
- static rewrite(destination: string | URL, init?: MiddlewareResponseInit): NextResponse;
68
+ static rewrite(destination: string | URL | NextURL, init?: MiddlewareResponseInit): NextResponse;
68
69
  /**
69
70
  * Continue to the next handler (middleware pattern).
70
71
  * Sets the x-middleware-next header.
@@ -78,12 +79,20 @@ type NextURLConfig = {
78
79
  locales: string[];
79
80
  defaultLocale: string;
80
81
  };
82
+ /**
83
+ * When true, `href`/`toString()` formats non-root, non-file-like pathnames
84
+ * with a trailing slash. Matches Next.js's `formatNextPathnameInfo` so that
85
+ * `NextResponse.redirect(request.nextUrl)` and `NextResponse.rewrite(url)`
86
+ * honour the user's `trailingSlash` config.
87
+ */
88
+ trailingSlash?: boolean;
81
89
  };
82
90
  };
83
91
  declare class NextURL {
84
92
  /** Internal URL stores the pathname WITHOUT basePath or locale prefix. */
85
93
  private _url;
86
94
  private _basePath;
95
+ private _trailingSlash;
87
96
  private _locale;
88
97
  private _defaultLocale;
89
98
  private _locales;
@@ -93,10 +102,18 @@ declare class NextURL {
93
102
  /** Extract locale from pathname, stripping it from the internal URL. */
94
103
  private _analyzeLocale;
95
104
  /**
96
- * Reconstruct the full pathname with basePath + locale prefix.
97
- * Mirrors Next.js's internal formatPathname().
105
+ * Reconstruct the full pathname with basePath + locale prefix and apply
106
+ * the configured trailingSlash policy.
107
+ * Mirrors Next.js's internal formatNextPathnameInfo().
98
108
  */
99
109
  private _formatPathname;
110
+ /**
111
+ * Apply the configured trailingSlash policy to a composed pathname. Matches
112
+ * Next.js's `formatNextPathnameInfo`: when `trailingSlash` is true, add a
113
+ * trailing slash unless the path is empty/root; when false, strip a trailing
114
+ * slash unless the path is empty/root.
115
+ */
116
+ private _applyTrailingSlash;
100
117
  get href(): string;
101
118
  set href(value: string);
102
119
  get origin(): string;
@@ -54,7 +54,10 @@ var NextRequest = class extends Request {
54
54
  const url = typeof input === "string" ? new URL(input, "http://localhost") : input instanceof URL ? input : new URL(input.url, "http://localhost");
55
55
  const urlConfig = _nextConfig ? {
56
56
  basePath: _nextConfig.basePath,
57
- nextConfig: { i18n: _nextConfig.i18n }
57
+ nextConfig: {
58
+ i18n: _nextConfig.i18n,
59
+ trailingSlash: _nextConfig.trailingSlash
60
+ }
58
61
  } : void 0;
59
62
  this._nextUrl = new NextURL(url, void 0, urlConfig);
60
63
  this._url = process.env.__NEXT_NO_MIDDLEWARE_URL_NORMALIZE ? url.toString() : this._nextUrl.toString();
@@ -180,12 +183,14 @@ var NextURL = class NextURL {
180
183
  /** Internal URL stores the pathname WITHOUT basePath or locale prefix. */
181
184
  _url;
182
185
  _basePath;
186
+ _trailingSlash;
183
187
  _locale;
184
188
  _defaultLocale;
185
189
  _locales;
186
190
  constructor(input, base, config) {
187
191
  this._url = new URL(input.toString(), base);
188
192
  this._basePath = config?.basePath ?? "";
193
+ this._trailingSlash = config?.nextConfig?.trailingSlash ?? false;
189
194
  this._stripBasePath();
190
195
  const i18n = config?.nextConfig?.i18n;
191
196
  if (i18n) {
@@ -210,15 +215,27 @@ var NextURL = class NextURL {
210
215
  } else this._locale = this._defaultLocale;
211
216
  }
212
217
  /**
213
- * Reconstruct the full pathname with basePath + locale prefix.
214
- * Mirrors Next.js's internal formatPathname().
218
+ * Reconstruct the full pathname with basePath + locale prefix and apply
219
+ * the configured trailingSlash policy.
220
+ * Mirrors Next.js's internal formatNextPathnameInfo().
215
221
  */
216
222
  _formatPathname() {
217
223
  let prefix = this._basePath;
218
224
  if (this._locale && this._locale !== this._defaultLocale) prefix += "/" + this._locale;
219
- if (!prefix) return this._url.pathname;
220
225
  const inner = this._url.pathname;
221
- return inner === "/" ? prefix : prefix + inner;
226
+ const composed = !prefix ? inner : inner === "/" ? prefix : prefix + inner;
227
+ return this._applyTrailingSlash(composed);
228
+ }
229
+ /**
230
+ * Apply the configured trailingSlash policy to a composed pathname. Matches
231
+ * Next.js's `formatNextPathnameInfo`: when `trailingSlash` is true, add a
232
+ * trailing slash unless the path is empty/root; when false, strip a trailing
233
+ * slash unless the path is empty/root.
234
+ */
235
+ _applyTrailingSlash(pathname) {
236
+ if (pathname === "" || pathname === "/") return pathname;
237
+ if (this._trailingSlash) return pathname.endsWith("/") ? pathname : pathname + "/";
238
+ return pathname.endsWith("/") ? pathname.slice(0, -1) : pathname;
222
239
  }
223
240
  get href() {
224
241
  const formatted = this._formatPathname();
@@ -319,12 +336,15 @@ var NextURL = class NextURL {
319
336
  return this._locales ? [...this._locales] : void 0;
320
337
  }
321
338
  clone() {
339
+ const nextConfig = {};
340
+ if (this._locales) nextConfig.i18n = {
341
+ locales: [...this._locales],
342
+ defaultLocale: this._defaultLocale
343
+ };
344
+ if (this._trailingSlash) nextConfig.trailingSlash = true;
322
345
  const config = {
323
346
  basePath: this._basePath,
324
- nextConfig: this._locales ? { i18n: {
325
- locales: [...this._locales],
326
- defaultLocale: this._defaultLocale
327
- } } : void 0
347
+ nextConfig: Object.keys(nextConfig).length > 0 ? nextConfig : void 0
328
348
  };
329
349
  return new NextURL(this.href, void 0, config);
330
350
  }