vinext 0.0.52 → 0.0.54

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 (330) hide show
  1. package/README.md +1 -1
  2. package/dist/build/clean-output.d.ts +14 -0
  3. package/dist/build/clean-output.js +36 -0
  4. package/dist/build/clean-output.js.map +1 -0
  5. package/dist/build/inline-css.d.ts +7 -0
  6. package/dist/build/inline-css.js +50 -0
  7. package/dist/build/inline-css.js.map +1 -0
  8. package/dist/build/prerender.d.ts +6 -2
  9. package/dist/build/prerender.js +51 -12
  10. package/dist/build/prerender.js.map +1 -1
  11. package/dist/build/run-prerender.js +10 -1
  12. package/dist/build/run-prerender.js.map +1 -1
  13. package/dist/build/static-export.d.ts +5 -0
  14. package/dist/build/static-export.js +8 -3
  15. package/dist/build/static-export.js.map +1 -1
  16. package/dist/check.js +4 -0
  17. package/dist/check.js.map +1 -1
  18. package/dist/cli.js +19 -4
  19. package/dist/cli.js.map +1 -1
  20. package/dist/client/instrumentation-client-inject.d.ts +34 -0
  21. package/dist/client/instrumentation-client-inject.js +57 -0
  22. package/dist/client/instrumentation-client-inject.js.map +1 -0
  23. package/dist/client/navigation-runtime.d.ts +16 -2
  24. package/dist/client/navigation-runtime.js +16 -1
  25. package/dist/client/navigation-runtime.js.map +1 -1
  26. package/dist/client/vinext-next-data.d.ts +2 -1
  27. package/dist/client/vinext-next-data.js.map +1 -1
  28. package/dist/client/window-next.d.ts +17 -2
  29. package/dist/client/window-next.js.map +1 -1
  30. package/dist/cloudflare/tpr.js +1 -1
  31. package/dist/cloudflare/tpr.js.map +1 -1
  32. package/dist/config/config-matchers.js +2 -1
  33. package/dist/config/config-matchers.js.map +1 -1
  34. package/dist/config/next-config.d.ts +95 -4
  35. package/dist/config/next-config.js +173 -14
  36. package/dist/config/next-config.js.map +1 -1
  37. package/dist/deploy.js +42 -7
  38. package/dist/deploy.js.map +1 -1
  39. package/dist/entries/app-browser-entry.d.ts +11 -1
  40. package/dist/entries/app-browser-entry.js +16 -6
  41. package/dist/entries/app-browser-entry.js.map +1 -1
  42. package/dist/entries/app-rsc-entry.d.ts +12 -3
  43. package/dist/entries/app-rsc-entry.js +41 -8
  44. package/dist/entries/app-rsc-entry.js.map +1 -1
  45. package/dist/entries/app-rsc-manifest.d.ts +21 -1
  46. package/dist/entries/app-rsc-manifest.js +6 -4
  47. package/dist/entries/app-rsc-manifest.js.map +1 -1
  48. package/dist/entries/pages-client-entry.d.ts +4 -1
  49. package/dist/entries/pages-client-entry.js +40 -3
  50. package/dist/entries/pages-client-entry.js.map +1 -1
  51. package/dist/entries/pages-server-entry.js +292 -34
  52. package/dist/entries/pages-server-entry.js.map +1 -1
  53. package/dist/entries/runtime-entry-module.d.ts +1 -10
  54. package/dist/entries/runtime-entry-module.js +2 -12
  55. package/dist/entries/runtime-entry-module.js.map +1 -1
  56. package/dist/index.js +91 -10
  57. package/dist/index.js.map +1 -1
  58. package/dist/plugins/fonts.js +25 -2
  59. package/dist/plugins/fonts.js.map +1 -1
  60. package/dist/plugins/remove-console.d.ts +16 -0
  61. package/dist/plugins/remove-console.js +176 -0
  62. package/dist/plugins/remove-console.js.map +1 -0
  63. package/dist/routing/app-route-graph.d.ts +24 -1
  64. package/dist/routing/app-route-graph.js +52 -4
  65. package/dist/routing/app-route-graph.js.map +1 -1
  66. package/dist/routing/app-router.d.ts +2 -2
  67. package/dist/routing/app-router.js +2 -2
  68. package/dist/routing/app-router.js.map +1 -1
  69. package/dist/routing/file-matcher.d.ts +21 -1
  70. package/dist/routing/file-matcher.js +39 -1
  71. package/dist/routing/file-matcher.js.map +1 -1
  72. package/dist/routing/pages-router.d.ts +1 -1
  73. package/dist/routing/pages-router.js +10 -3
  74. package/dist/routing/pages-router.js.map +1 -1
  75. package/dist/routing/route-trie.js +13 -18
  76. package/dist/routing/route-trie.js.map +1 -1
  77. package/dist/routing/utils.d.ts +11 -1
  78. package/dist/routing/utils.js +15 -1
  79. package/dist/routing/utils.js.map +1 -1
  80. package/dist/server/api-handler.js +19 -10
  81. package/dist/server/api-handler.js.map +1 -1
  82. package/dist/server/app-browser-action-result.d.ts +16 -1
  83. package/dist/server/app-browser-action-result.js +15 -1
  84. package/dist/server/app-browser-action-result.js.map +1 -1
  85. package/dist/server/app-browser-entry.js +47 -28
  86. package/dist/server/app-browser-entry.js.map +1 -1
  87. package/dist/server/app-browser-navigation-controller.d.ts +2 -0
  88. package/dist/server/app-browser-navigation-controller.js +4 -0
  89. package/dist/server/app-browser-navigation-controller.js.map +1 -1
  90. package/dist/server/app-elements-wire.d.ts +13 -4
  91. package/dist/server/app-elements-wire.js +10 -1
  92. package/dist/server/app-elements-wire.js.map +1 -1
  93. package/dist/server/app-elements.d.ts +2 -2
  94. package/dist/server/app-elements.js +2 -2
  95. package/dist/server/app-elements.js.map +1 -1
  96. package/dist/server/app-fallback-renderer.d.ts +27 -8
  97. package/dist/server/app-fallback-renderer.js +19 -8
  98. package/dist/server/app-fallback-renderer.js.map +1 -1
  99. package/dist/server/app-history-state.js +6 -2
  100. package/dist/server/app-history-state.js.map +1 -1
  101. package/dist/server/app-inline-css-client.d.ts +7 -0
  102. package/dist/server/app-inline-css-client.js +37 -0
  103. package/dist/server/app-inline-css-client.js.map +1 -0
  104. package/dist/server/app-interception-context-header.d.ts +33 -0
  105. package/dist/server/app-interception-context-header.js +44 -0
  106. package/dist/server/app-interception-context-header.js.map +1 -0
  107. package/dist/server/app-mounted-slots-header.d.ts +19 -0
  108. package/dist/server/app-mounted-slots-header.js +40 -1
  109. package/dist/server/app-mounted-slots-header.js.map +1 -1
  110. package/dist/server/app-optimistic-routing.js +26 -18
  111. package/dist/server/app-optimistic-routing.js.map +1 -1
  112. package/dist/server/app-page-boundary-render.d.ts +1 -0
  113. package/dist/server/app-page-boundary-render.js +2 -0
  114. package/dist/server/app-page-boundary-render.js.map +1 -1
  115. package/dist/server/app-page-boundary.d.ts +22 -1
  116. package/dist/server/app-page-boundary.js +30 -3
  117. package/dist/server/app-page-boundary.js.map +1 -1
  118. package/dist/server/app-page-cache.d.ts +9 -3
  119. package/dist/server/app-page-cache.js +14 -8
  120. package/dist/server/app-page-cache.js.map +1 -1
  121. package/dist/server/app-page-dispatch.d.ts +13 -1
  122. package/dist/server/app-page-dispatch.js +136 -82
  123. package/dist/server/app-page-dispatch.js.map +1 -1
  124. package/dist/server/app-page-element-builder.d.ts +2 -1
  125. package/dist/server/app-page-element-builder.js +17 -30
  126. package/dist/server/app-page-element-builder.js.map +1 -1
  127. package/dist/server/app-page-execution.d.ts +1 -0
  128. package/dist/server/app-page-execution.js +2 -0
  129. package/dist/server/app-page-execution.js.map +1 -1
  130. package/dist/server/app-page-head.d.ts +1 -0
  131. package/dist/server/app-page-head.js +8 -0
  132. package/dist/server/app-page-head.js.map +1 -1
  133. package/dist/server/app-page-render-identity.d.ts +22 -0
  134. package/dist/server/app-page-render-identity.js +42 -0
  135. package/dist/server/app-page-render-identity.js.map +1 -0
  136. package/dist/server/app-page-render-observation.js +1 -1
  137. package/dist/server/app-page-render.d.ts +9 -1
  138. package/dist/server/app-page-render.js +8 -2
  139. package/dist/server/app-page-render.js.map +1 -1
  140. package/dist/server/app-page-request.d.ts +6 -3
  141. package/dist/server/app-page-request.js +5 -2
  142. package/dist/server/app-page-request.js.map +1 -1
  143. package/dist/server/app-page-response.d.ts +11 -1
  144. package/dist/server/app-page-response.js +16 -4
  145. package/dist/server/app-page-response.js.map +1 -1
  146. package/dist/server/app-page-route-wiring.d.ts +16 -0
  147. package/dist/server/app-page-route-wiring.js +25 -10
  148. package/dist/server/app-page-route-wiring.js.map +1 -1
  149. package/dist/server/app-page-stream.d.ts +12 -0
  150. package/dist/server/app-page-stream.js +3 -0
  151. package/dist/server/app-page-stream.js.map +1 -1
  152. package/dist/server/app-route-handler-dispatch.d.ts +1 -0
  153. package/dist/server/app-route-handler-dispatch.js +3 -0
  154. package/dist/server/app-route-handler-dispatch.js.map +1 -1
  155. package/dist/server/app-route-handler-execution.d.ts +1 -0
  156. package/dist/server/app-route-handler-execution.js +1 -0
  157. package/dist/server/app-route-handler-execution.js.map +1 -1
  158. package/dist/server/app-route-handler-response.js +38 -6
  159. package/dist/server/app-route-handler-response.js.map +1 -1
  160. package/dist/server/app-rsc-handler.d.ts +16 -3
  161. package/dist/server/app-rsc-handler.js +60 -11
  162. package/dist/server/app-rsc-handler.js.map +1 -1
  163. package/dist/server/app-rsc-request-normalization.d.ts +2 -1
  164. package/dist/server/app-rsc-request-normalization.js +6 -4
  165. package/dist/server/app-rsc-request-normalization.js.map +1 -1
  166. package/dist/server/app-segment-config.d.ts +4 -1
  167. package/dist/server/app-segment-config.js +6 -1
  168. package/dist/server/app-segment-config.js.map +1 -1
  169. package/dist/server/app-server-action-execution.d.ts +22 -3
  170. package/dist/server/app-server-action-execution.js +46 -7
  171. package/dist/server/app-server-action-execution.js.map +1 -1
  172. package/dist/server/app-ssr-entry.d.ts +6 -0
  173. package/dist/server/app-ssr-entry.js +57 -6
  174. package/dist/server/app-ssr-entry.js.map +1 -1
  175. package/dist/server/app-ssr-error-meta.js +3 -3
  176. package/dist/server/app-ssr-error-meta.js.map +1 -1
  177. package/dist/server/app-ssr-stream.d.ts +25 -1
  178. package/dist/server/app-ssr-stream.js +237 -19
  179. package/dist/server/app-ssr-stream.js.map +1 -1
  180. package/dist/server/app-static-generation.d.ts +1 -0
  181. package/dist/server/app-static-generation.js +2 -1
  182. package/dist/server/app-static-generation.js.map +1 -1
  183. package/dist/server/client-trace-metadata.d.ts +31 -0
  184. package/dist/server/client-trace-metadata.js +83 -0
  185. package/dist/server/client-trace-metadata.js.map +1 -0
  186. package/dist/server/cookie-utils.d.ts +13 -0
  187. package/dist/server/cookie-utils.js +20 -0
  188. package/dist/server/cookie-utils.js.map +1 -0
  189. package/dist/server/default-not-found-module.d.ts +20 -0
  190. package/dist/server/default-not-found-module.js +20 -0
  191. package/dist/server/default-not-found-module.js.map +1 -0
  192. package/dist/server/dev-server.d.ts +8 -1
  193. package/dist/server/dev-server.js +56 -11
  194. package/dist/server/dev-server.js.map +1 -1
  195. package/dist/server/headers.d.ts +5 -1
  196. package/dist/server/headers.js +5 -1
  197. package/dist/server/headers.js.map +1 -1
  198. package/dist/server/html.d.ts +2 -1
  199. package/dist/server/html.js +6 -1
  200. package/dist/server/html.js.map +1 -1
  201. package/dist/server/image-optimization.d.ts +13 -4
  202. package/dist/server/image-optimization.js +15 -4
  203. package/dist/server/image-optimization.js.map +1 -1
  204. package/dist/server/isr-cache.d.ts +7 -5
  205. package/dist/server/isr-cache.js +17 -6
  206. package/dist/server/isr-cache.js.map +1 -1
  207. package/dist/server/middleware-runtime.js +1 -2
  208. package/dist/server/middleware-runtime.js.map +1 -1
  209. package/dist/server/middleware.js +1 -1
  210. package/dist/server/middleware.js.map +1 -1
  211. package/dist/server/pages-api-route.d.ts +18 -0
  212. package/dist/server/pages-api-route.js +3 -1
  213. package/dist/server/pages-api-route.js.map +1 -1
  214. package/dist/server/pages-body-parser-config.d.ts +60 -0
  215. package/dist/server/pages-body-parser-config.js +79 -0
  216. package/dist/server/pages-body-parser-config.js.map +1 -0
  217. package/dist/server/pages-data-route.js +1 -0
  218. package/dist/server/pages-data-route.js.map +1 -1
  219. package/dist/server/pages-default-404.d.ts +31 -0
  220. package/dist/server/pages-default-404.js +40 -0
  221. package/dist/server/pages-default-404.js.map +1 -0
  222. package/dist/server/pages-document-initial-props.d.ts +7 -0
  223. package/dist/server/pages-document-initial-props.js +14 -0
  224. package/dist/server/pages-document-initial-props.js.map +1 -0
  225. package/dist/server/pages-node-compat.d.ts +10 -0
  226. package/dist/server/pages-node-compat.js +12 -1
  227. package/dist/server/pages-node-compat.js.map +1 -1
  228. package/dist/server/pages-page-data.d.ts +40 -0
  229. package/dist/server/pages-page-data.js +19 -14
  230. package/dist/server/pages-page-data.js.map +1 -1
  231. package/dist/server/pages-page-method.d.ts +48 -0
  232. package/dist/server/pages-page-method.js +19 -0
  233. package/dist/server/pages-page-method.js.map +1 -0
  234. package/dist/server/pages-page-response.d.ts +8 -0
  235. package/dist/server/pages-page-response.js +21 -11
  236. package/dist/server/pages-page-response.js.map +1 -1
  237. package/dist/server/pages-serializable-props.d.ts +25 -0
  238. package/dist/server/pages-serializable-props.js +69 -0
  239. package/dist/server/pages-serializable-props.js.map +1 -0
  240. package/dist/server/prerender-route-params.d.ts +14 -0
  241. package/dist/server/prerender-route-params.js +94 -0
  242. package/dist/server/prerender-route-params.js.map +1 -0
  243. package/dist/server/prod-server.d.ts +3 -23
  244. package/dist/server/prod-server.js +43 -57
  245. package/dist/server/prod-server.js.map +1 -1
  246. package/dist/server/proxy-trust.d.ts +41 -0
  247. package/dist/server/proxy-trust.js +70 -0
  248. package/dist/server/proxy-trust.js.map +1 -0
  249. package/dist/server/request-pipeline.d.ts +3 -3
  250. package/dist/server/request-pipeline.js +5 -4
  251. package/dist/server/request-pipeline.js.map +1 -1
  252. package/dist/server/seed-cache.js +12 -6
  253. package/dist/server/seed-cache.js.map +1 -1
  254. package/dist/server/server-action-not-found.js +3 -2
  255. package/dist/server/server-action-not-found.js.map +1 -1
  256. package/dist/server/static-file-cache.js +2 -1
  257. package/dist/server/static-file-cache.js.map +1 -1
  258. package/dist/server/streaming-metadata.d.ts +5 -0
  259. package/dist/server/streaming-metadata.js +10 -0
  260. package/dist/server/streaming-metadata.js.map +1 -0
  261. package/dist/shims/app-router-scroll-state.d.ts +14 -0
  262. package/dist/shims/app-router-scroll-state.js +51 -0
  263. package/dist/shims/app-router-scroll-state.js.map +1 -0
  264. package/dist/shims/app-router-scroll.d.ts +28 -0
  265. package/dist/shims/app-router-scroll.js +115 -0
  266. package/dist/shims/app-router-scroll.js.map +1 -0
  267. package/dist/shims/before-interactive-context.d.ts +30 -0
  268. package/dist/shims/before-interactive-context.js +10 -0
  269. package/dist/shims/before-interactive-context.js.map +1 -0
  270. package/dist/shims/cache-runtime.d.ts +1 -1
  271. package/dist/shims/cache-runtime.js +14 -1
  272. package/dist/shims/cache-runtime.js.map +1 -1
  273. package/dist/shims/cache.d.ts +6 -0
  274. package/dist/shims/cache.js +7 -0
  275. package/dist/shims/cache.js.map +1 -1
  276. package/dist/shims/default-not-found.d.ts +12 -0
  277. package/dist/shims/default-not-found.js +61 -0
  278. package/dist/shims/default-not-found.js.map +1 -0
  279. package/dist/shims/error.js +3 -0
  280. package/dist/shims/error.js.map +1 -1
  281. package/dist/shims/font-local.d.ts +5 -0
  282. package/dist/shims/font-local.js +6 -2
  283. package/dist/shims/font-local.js.map +1 -1
  284. package/dist/shims/head.js +4 -4
  285. package/dist/shims/head.js.map +1 -1
  286. package/dist/shims/headers.d.ts +13 -2
  287. package/dist/shims/headers.js +73 -22
  288. package/dist/shims/headers.js.map +1 -1
  289. package/dist/shims/image.d.ts +1 -1
  290. package/dist/shims/image.js +4 -4
  291. package/dist/shims/image.js.map +1 -1
  292. package/dist/shims/internal/app-route-detection.d.ts +37 -0
  293. package/dist/shims/internal/app-route-detection.js +69 -0
  294. package/dist/shims/internal/app-route-detection.js.map +1 -0
  295. package/dist/shims/internal/pages-data-target.d.ts +58 -0
  296. package/dist/shims/internal/pages-data-target.js +91 -0
  297. package/dist/shims/internal/pages-data-target.js.map +1 -0
  298. package/dist/shims/internal/pages-data-url.d.ts +42 -0
  299. package/dist/shims/internal/pages-data-url.js +73 -0
  300. package/dist/shims/internal/pages-data-url.js.map +1 -0
  301. package/dist/shims/link.d.ts +18 -2
  302. package/dist/shims/link.js +129 -15
  303. package/dist/shims/link.js.map +1 -1
  304. package/dist/shims/metadata.d.ts +9 -7
  305. package/dist/shims/metadata.js +70 -7
  306. package/dist/shims/metadata.js.map +1 -1
  307. package/dist/shims/navigation.d.ts +1 -2
  308. package/dist/shims/navigation.js +94 -20
  309. package/dist/shims/navigation.js.map +1 -1
  310. package/dist/shims/router.d.ts +5 -0
  311. package/dist/shims/router.js +389 -80
  312. package/dist/shims/router.js.map +1 -1
  313. package/dist/shims/script.d.ts +11 -1
  314. package/dist/shims/script.js +158 -15
  315. package/dist/shims/script.js.map +1 -1
  316. package/dist/shims/server.js +1 -0
  317. package/dist/shims/server.js.map +1 -1
  318. package/dist/shims/url-utils.d.ts +2 -1
  319. package/dist/shims/url-utils.js +15 -4
  320. package/dist/shims/url-utils.js.map +1 -1
  321. package/dist/utils/html-limited-bots.d.ts +5 -0
  322. package/dist/utils/html-limited-bots.js +15 -0
  323. package/dist/utils/html-limited-bots.js.map +1 -0
  324. package/dist/utils/path.d.ts +13 -0
  325. package/dist/utils/path.js +16 -0
  326. package/dist/utils/path.js.map +1 -0
  327. package/dist/utils/query.d.ts +6 -0
  328. package/dist/utils/query.js +10 -1
  329. package/dist/utils/query.js.map +1 -1
  330. package/package.json +1 -1
@@ -1,6 +1,6 @@
1
1
  import { patternToNextFormat } from "../routing/route-validation.js";
2
2
  import { pagesRouter } from "../routing/pages-router.js";
3
- import { normalizePathSeparators } from "./runtime-entry-module.js";
3
+ import { normalizePathSeparators } from "../utils/path.js";
4
4
  import { findFileWithExts } from "./pages-entry-helpers.js";
5
5
  //#region src/entries/pages-client-entry.ts
6
6
  /**
@@ -14,10 +14,11 @@ import { findFileWithExts } from "./pages-entry-helpers.js";
14
14
  *
15
15
  * Extracted from index.ts.
16
16
  */
17
- async function generateClientEntry(pagesDir, nextConfig, fileMatcher) {
17
+ async function generateClientEntry(pagesDir, nextConfig, fileMatcher, options = {}) {
18
18
  const pageRoutes = await pagesRouter(pagesDir, nextConfig?.pageExtensions, fileMatcher);
19
19
  const appFilePath = findFileWithExts(pagesDir, "_app", fileMatcher);
20
20
  const hasApp = appFilePath !== null;
21
+ const appPrefetchRoutes = options.appPrefetchRoutes ?? [];
21
22
  const loaderEntries = pageRoutes.map((r) => {
22
23
  const absPath = normalizePathSeparators(r.filePath);
23
24
  const nextFormatPattern = patternToNextFormat(r.pattern);
@@ -46,6 +47,42 @@ import { wrapWithRouterContext } from "next/router";
46
47
  const pageLoaders = {
47
48
  ${loaderEntries.join(",\n")}
48
49
  };
50
+ ${hasApp ? `
51
+ const appLoader = () => import(${JSON.stringify(appFileBase)});
52
+ ` : `
53
+ const appLoader = undefined;
54
+ `}
55
+ // Expose the code-split loader manifest on window so client-side
56
+ // _next/data navigations in shims/router.ts can resolve the correct page
57
+ // chunk for any route. Without this, navigateClient() would have to extract
58
+ // the chunk URL from an HTML response — the whole point of switching to the
59
+ // JSON data endpoint is to avoid that round trip.
60
+ //
61
+ // Keys are route patterns in Next.js bracket format (matching __NEXT_DATA__.page
62
+ // and the keys of pageLoaders above). The patterns list is the same as
63
+ // Object.keys(pageLoaders), exposed separately so navigateClient() can iterate
64
+ // it without re-keying the map. Ordering is the insertion order of pageRoutes,
65
+ // which pagesRouter() has already sorted by specificity (static → dynamic →
66
+ // catch-all → optional catch-all) via compareRoutes — so matchPagesPattern()
67
+ // can iterate in order and trust the first match.
68
+ window.__VINEXT_PAGE_LOADERS__ = pageLoaders;
69
+ window.__VINEXT_PAGE_PATTERNS__ = Object.keys(pageLoaders);
70
+ window.__VINEXT_APP_LOADER__ = appLoader;
71
+ // Expose the App Router prefetch manifest so Pages Router \`<Link>\`s and
72
+ // \`Router.prefetch\` can detect when a prefetch target is actually an App
73
+ // Router route, and mark it on \`Router.components[urlPathname]\` with
74
+ // \`{ __appRouter: true }\`. Mirrors Next.js's \`_bfl\`-driven marker write at
75
+ // .nextjs-ref/packages/next/src/shared/lib/router/router.ts:2525, which the
76
+ // Next.js deploy test
77
+ // test/e2e/app-dir/app/index.test.ts → "should successfully detect app
78
+ // route during prefetch"
79
+ // asserts via \`window.next.router.components[<path>]\`. Issue #1526.
80
+ //
81
+ // In a hybrid Pages + App Router build, this entry runs when the user lands
82
+ // on a Pages Router page. The App Router browser entry sets the same global
83
+ // when the user lands on an App Router page (see app-browser-entry.ts) — the
84
+ // two writes do not race because only one entry executes per page load.
85
+ window.__VINEXT_LINK_PREFETCH_ROUTES__ = ${JSON.stringify(appPrefetchRoutes)};
49
86
 
50
87
  async function hydrate() {
51
88
  const nextData = window.__NEXT_DATA__;
@@ -71,7 +108,7 @@ async function hydrate() {
71
108
  let element;
72
109
  ${hasApp ? `
73
110
  try {
74
- const appModule = await import(${JSON.stringify(appFileBase)});
111
+ const appModule = await appLoader();
75
112
  const AppComponent = appModule.default;
76
113
  window.__VINEXT_APP__ = AppComponent;
77
114
  element = React.createElement(AppComponent, { Component: PageComponent, pageProps });
@@ -1 +1 @@
1
- {"version":3,"file":"pages-client-entry.js","names":["pagesPatternToNextFormat"],"sources":["../../src/entries/pages-client-entry.ts"],"sourcesContent":["/**\n * Pages Router client hydration entry generator.\n *\n * Generates the virtual client entry module (`virtual:vinext-client-entry`).\n * This is the entry point for `vite build` (client bundle). It maps route\n * patterns to dynamic imports of page modules so Vite code-splits each page\n * into its own chunk. At runtime it reads __NEXT_DATA__ to determine which\n * page to hydrate.\n *\n * Extracted from index.ts.\n */\nimport {\n pagesRouter,\n patternToNextFormat as pagesPatternToNextFormat,\n type Route,\n} from \"../routing/pages-router.js\";\nimport { createValidFileMatcher } from \"../routing/file-matcher.js\";\nimport { type ResolvedNextConfig } from \"../config/next-config.js\";\nimport { findFileWithExts } from \"./pages-entry-helpers.js\";\nimport { normalizePathSeparators } from \"./runtime-entry-module.js\";\n\nexport async function generateClientEntry(\n pagesDir: string,\n nextConfig: ResolvedNextConfig,\n fileMatcher: ReturnType<typeof createValidFileMatcher>,\n): Promise<string> {\n const pageRoutes = await pagesRouter(pagesDir, nextConfig?.pageExtensions, fileMatcher);\n\n const appFilePath = findFileWithExts(pagesDir, \"_app\", fileMatcher);\n const hasApp = appFilePath !== null;\n\n // Build a map of route pattern -> dynamic import.\n // Keys must use Next.js bracket format (e.g. \"/user/[id]\") to match\n // __NEXT_DATA__.page which is set via patternToNextFormat() during SSR.\n const loaderEntries = pageRoutes.map((r: Route) => {\n const absPath = normalizePathSeparators(r.filePath);\n const nextFormatPattern = pagesPatternToNextFormat(r.pattern);\n // JSON.stringify safely escapes quotes, backslashes, and special chars in\n // both the route pattern and the absolute file path.\n // lgtm[js/bad-code-sanitization]\n return ` ${JSON.stringify(nextFormatPattern)}: () => import(${JSON.stringify(absPath)})`;\n });\n\n const appFileBase = appFilePath ? normalizePathSeparators(appFilePath) : undefined;\n\n return `\nimport \"vinext/instrumentation-client\";\nimport React from \"react\";\nimport { hydrateRoot } from \"react-dom/client\";\nimport { installPagesRouterRuntime } from \"vinext/pages-router-runtime\";\n// Statically import next/router as the very first vinext shim so that\n// (a) installWindowNext runs at top-level — \\`window.next.router\\` is\n// available to test harnesses and third-party scripts BEFORE\n// hydrate() resolves (see .nextjs-ref/packages/next/src/client/next.ts\n// line 13, which also sets window.next as a top-level side effect),\n// and (b) the popstate handler is registered before\n// installPagesRouterRuntime() runs, removing the race window where a\n// popstate event could fire between hydration and runtime install.\n//\n// Mirrors Next.js's bootstrap order: client/next.ts statically imports\n// from './' before calling initialize/hydrate, so window.next is set up\n// before any async work.\nimport { wrapWithRouterContext } from \"next/router\";\n\nconst pageLoaders = {\n${loaderEntries.join(\",\\n\")}\n};\n\nasync function hydrate() {\n const nextData = window.__NEXT_DATA__;\n if (!nextData) {\n console.error(\"[vinext] No __NEXT_DATA__ found\");\n return;\n }\n\n const { pageProps } = nextData.props;\n const loader = pageLoaders[nextData.page];\n if (!loader) {\n console.error(\"[vinext] No page loader for route:\", nextData.page);\n return;\n }\n\n const pageModule = await loader();\n const PageComponent = pageModule.default;\n if (!PageComponent) {\n console.error(\"[vinext] Page module has no default export\");\n return;\n }\n\n let element;\n ${\n hasApp\n ? `\n try {\n const appModule = await import(${JSON.stringify(appFileBase!)});\n const AppComponent = appModule.default;\n window.__VINEXT_APP__ = AppComponent;\n element = React.createElement(AppComponent, { Component: PageComponent, pageProps });\n } catch {\n element = React.createElement(PageComponent, pageProps);\n }\n `\n : `\n element = React.createElement(PageComponent, pageProps);\n `\n }\n\n // Wrap with RouterContext.Provider so next/router and next/compat/router work during hydration.\n element = wrapWithRouterContext(element);\n\n const container = document.getElementById(\"__next\");\n if (!container) {\n console.error(\"[vinext] No #__next element found\");\n return;\n }\n\n const root = hydrateRoot(container, element);\n window.__VINEXT_ROOT__ = root;\n installPagesRouterRuntime();\n const hydratedAt = performance.now();\n window.__VINEXT_HYDRATED_AT = hydratedAt;\n window.__NEXT_HYDRATED = true;\n window.__NEXT_HYDRATED_AT = hydratedAt;\n window.__NEXT_HYDRATED_CB?.();\n}\n\nhydrate();\n`;\n}\n"],"mappings":";;;;;;;;;;;;;;;;AAqBA,eAAsB,oBACpB,UACA,YACA,aACiB;CACjB,MAAM,aAAa,MAAM,YAAY,UAAU,YAAY,gBAAgB,YAAY;CAEvF,MAAM,cAAc,iBAAiB,UAAU,QAAQ,YAAY;CACnE,MAAM,SAAS,gBAAgB;CAK/B,MAAM,gBAAgB,WAAW,KAAK,MAAa;EACjD,MAAM,UAAU,wBAAwB,EAAE,SAAS;EACnD,MAAM,oBAAoBA,oBAAyB,EAAE,QAAQ;EAI7D,OAAO,KAAK,KAAK,UAAU,kBAAkB,CAAC,iBAAiB,KAAK,UAAU,QAAQ,CAAC;GACvF;CAEF,MAAM,cAAc,cAAc,wBAAwB,YAAY,GAAG,KAAA;CAEzE,OAAO;;;;;;;;;;;;;;;;;;;;EAoBP,cAAc,KAAK,MAAM,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;IA0BxB,SACI;;qCAE6B,KAAK,UAAU,YAAa,CAAC;;;;;;;MAQ1D;;IAGL"}
1
+ {"version":3,"file":"pages-client-entry.js","names":["pagesPatternToNextFormat"],"sources":["../../src/entries/pages-client-entry.ts"],"sourcesContent":["/**\n * Pages Router client hydration entry generator.\n *\n * Generates the virtual client entry module (`virtual:vinext-client-entry`).\n * This is the entry point for `vite build` (client bundle). It maps route\n * patterns to dynamic imports of page modules so Vite code-splits each page\n * into its own chunk. At runtime it reads __NEXT_DATA__ to determine which\n * page to hydrate.\n *\n * Extracted from index.ts.\n */\nimport {\n pagesRouter,\n patternToNextFormat as pagesPatternToNextFormat,\n type Route,\n} from \"../routing/pages-router.js\";\nimport { createValidFileMatcher } from \"../routing/file-matcher.js\";\nimport { type ResolvedNextConfig } from \"../config/next-config.js\";\nimport type { VinextLinkPrefetchRoute } from \"../client/vinext-next-data.js\";\nimport { findFileWithExts } from \"./pages-entry-helpers.js\";\nimport { normalizePathSeparators } from \"../utils/path.js\";\n\nexport async function generateClientEntry(\n pagesDir: string,\n nextConfig: ResolvedNextConfig,\n fileMatcher: ReturnType<typeof createValidFileMatcher>,\n options: { appPrefetchRoutes?: readonly VinextLinkPrefetchRoute[] } = {},\n): Promise<string> {\n const pageRoutes = await pagesRouter(pagesDir, nextConfig?.pageExtensions, fileMatcher);\n\n const appFilePath = findFileWithExts(pagesDir, \"_app\", fileMatcher);\n const hasApp = appFilePath !== null;\n const appPrefetchRoutes = options.appPrefetchRoutes ?? [];\n\n // Build a map of route pattern -> dynamic import.\n // Keys must use Next.js bracket format (e.g. \"/user/[id]\") to match\n // __NEXT_DATA__.page which is set via patternToNextFormat() during SSR.\n const loaderEntries = pageRoutes.map((r: Route) => {\n const absPath = normalizePathSeparators(r.filePath);\n const nextFormatPattern = pagesPatternToNextFormat(r.pattern);\n // JSON.stringify safely escapes quotes, backslashes, and special chars in\n // both the route pattern and the absolute file path.\n // lgtm[js/bad-code-sanitization]\n return ` ${JSON.stringify(nextFormatPattern)}: () => import(${JSON.stringify(absPath)})`;\n });\n\n const appFileBase = appFilePath ? normalizePathSeparators(appFilePath) : undefined;\n\n return `\nimport \"vinext/instrumentation-client\";\nimport React from \"react\";\nimport { hydrateRoot } from \"react-dom/client\";\nimport { installPagesRouterRuntime } from \"vinext/pages-router-runtime\";\n// Statically import next/router as the very first vinext shim so that\n// (a) installWindowNext runs at top-level — \\`window.next.router\\` is\n// available to test harnesses and third-party scripts BEFORE\n// hydrate() resolves (see .nextjs-ref/packages/next/src/client/next.ts\n// line 13, which also sets window.next as a top-level side effect),\n// and (b) the popstate handler is registered before\n// installPagesRouterRuntime() runs, removing the race window where a\n// popstate event could fire between hydration and runtime install.\n//\n// Mirrors Next.js's bootstrap order: client/next.ts statically imports\n// from './' before calling initialize/hydrate, so window.next is set up\n// before any async work.\nimport { wrapWithRouterContext } from \"next/router\";\n\nconst pageLoaders = {\n${loaderEntries.join(\",\\n\")}\n};\n${\n hasApp\n ? `\nconst appLoader = () => import(${JSON.stringify(appFileBase!)});\n`\n : `\nconst appLoader = undefined;\n`\n}\n// Expose the code-split loader manifest on window so client-side\n// _next/data navigations in shims/router.ts can resolve the correct page\n// chunk for any route. Without this, navigateClient() would have to extract\n// the chunk URL from an HTML response — the whole point of switching to the\n// JSON data endpoint is to avoid that round trip.\n//\n// Keys are route patterns in Next.js bracket format (matching __NEXT_DATA__.page\n// and the keys of pageLoaders above). The patterns list is the same as\n// Object.keys(pageLoaders), exposed separately so navigateClient() can iterate\n// it without re-keying the map. Ordering is the insertion order of pageRoutes,\n// which pagesRouter() has already sorted by specificity (static → dynamic →\n// catch-all → optional catch-all) via compareRoutes — so matchPagesPattern()\n// can iterate in order and trust the first match.\nwindow.__VINEXT_PAGE_LOADERS__ = pageLoaders;\nwindow.__VINEXT_PAGE_PATTERNS__ = Object.keys(pageLoaders);\nwindow.__VINEXT_APP_LOADER__ = appLoader;\n// Expose the App Router prefetch manifest so Pages Router \\`<Link>\\`s and\n// \\`Router.prefetch\\` can detect when a prefetch target is actually an App\n// Router route, and mark it on \\`Router.components[urlPathname]\\` with\n// \\`{ __appRouter: true }\\`. Mirrors Next.js's \\`_bfl\\`-driven marker write at\n// .nextjs-ref/packages/next/src/shared/lib/router/router.ts:2525, which the\n// Next.js deploy test\n// test/e2e/app-dir/app/index.test.ts → \"should successfully detect app\n// route during prefetch\"\n// asserts via \\`window.next.router.components[<path>]\\`. Issue #1526.\n//\n// In a hybrid Pages + App Router build, this entry runs when the user lands\n// on a Pages Router page. The App Router browser entry sets the same global\n// when the user lands on an App Router page (see app-browser-entry.ts) — the\n// two writes do not race because only one entry executes per page load.\nwindow.__VINEXT_LINK_PREFETCH_ROUTES__ = ${JSON.stringify(appPrefetchRoutes)};\n\nasync function hydrate() {\n const nextData = window.__NEXT_DATA__;\n if (!nextData) {\n console.error(\"[vinext] No __NEXT_DATA__ found\");\n return;\n }\n\n const { pageProps } = nextData.props;\n const loader = pageLoaders[nextData.page];\n if (!loader) {\n console.error(\"[vinext] No page loader for route:\", nextData.page);\n return;\n }\n\n const pageModule = await loader();\n const PageComponent = pageModule.default;\n if (!PageComponent) {\n console.error(\"[vinext] Page module has no default export\");\n return;\n }\n\n let element;\n ${\n hasApp\n ? `\n try {\n const appModule = await appLoader();\n const AppComponent = appModule.default;\n window.__VINEXT_APP__ = AppComponent;\n element = React.createElement(AppComponent, { Component: PageComponent, pageProps });\n } catch {\n element = React.createElement(PageComponent, pageProps);\n }\n `\n : `\n element = React.createElement(PageComponent, pageProps);\n `\n }\n\n // Wrap with RouterContext.Provider so next/router and next/compat/router work during hydration.\n element = wrapWithRouterContext(element);\n\n const container = document.getElementById(\"__next\");\n if (!container) {\n console.error(\"[vinext] No #__next element found\");\n return;\n }\n\n const root = hydrateRoot(container, element);\n window.__VINEXT_ROOT__ = root;\n installPagesRouterRuntime();\n const hydratedAt = performance.now();\n window.__VINEXT_HYDRATED_AT = hydratedAt;\n window.__NEXT_HYDRATED = true;\n window.__NEXT_HYDRATED_AT = hydratedAt;\n window.__NEXT_HYDRATED_CB?.();\n}\n\nhydrate();\n`;\n}\n"],"mappings":";;;;;;;;;;;;;;;;AAsBA,eAAsB,oBACpB,UACA,YACA,aACA,UAAsE,EAAE,EACvD;CACjB,MAAM,aAAa,MAAM,YAAY,UAAU,YAAY,gBAAgB,YAAY;CAEvF,MAAM,cAAc,iBAAiB,UAAU,QAAQ,YAAY;CACnE,MAAM,SAAS,gBAAgB;CAC/B,MAAM,oBAAoB,QAAQ,qBAAqB,EAAE;CAKzD,MAAM,gBAAgB,WAAW,KAAK,MAAa;EACjD,MAAM,UAAU,wBAAwB,EAAE,SAAS;EACnD,MAAM,oBAAoBA,oBAAyB,EAAE,QAAQ;EAI7D,OAAO,KAAK,KAAK,UAAU,kBAAkB,CAAC,iBAAiB,KAAK,UAAU,QAAQ,CAAC;GACvF;CAEF,MAAM,cAAc,cAAc,wBAAwB,YAAY,GAAG,KAAA;CAEzE,OAAO;;;;;;;;;;;;;;;;;;;;EAoBP,cAAc,KAAK,MAAM,CAAC;;EAG1B,SACI;iCAC2B,KAAK,UAAU,YAAa,CAAC;IAExD;;EAGL;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;2CA+B0C,KAAK,UAAU,kBAAkB,CAAC;;;;;;;;;;;;;;;;;;;;;;;;IAyBzE,SACI;;;;;;;;;MAUA;;IAGL"}
@@ -1,5 +1,6 @@
1
1
  import { apiRouter, pagesRouter } from "../routing/pages-router.js";
2
- import { normalizePathSeparators, resolveEntryPath } from "./runtime-entry-module.js";
2
+ import { normalizePathSeparators } from "../utils/path.js";
3
+ import { resolveEntryPath } from "./runtime-entry-module.js";
3
4
  import { isProxyFile } from "../server/middleware.js";
4
5
  import { findFileWithExts } from "./pages-entry-helpers.js";
5
6
  //#region src/entries/pages-server-entry.ts
@@ -18,7 +19,9 @@ const _routeTriePath = resolveEntryPath("../routing/route-trie.js", import.meta.
18
19
  const _pagesI18nPath = resolveEntryPath("../server/pages-i18n.js", import.meta.url);
19
20
  const _pagesPageResponsePath = resolveEntryPath("../server/pages-page-response.js", import.meta.url);
20
21
  const _pagesPageDataPath = resolveEntryPath("../server/pages-page-data.js", import.meta.url);
22
+ const _pagesPageMethodPath = resolveEntryPath("../server/pages-page-method.js", import.meta.url);
21
23
  const _pagesDataRoutePath = resolveEntryPath("../server/pages-data-route.js", import.meta.url);
24
+ const _pagesDefault404Path = resolveEntryPath("../server/pages-default-404.js", import.meta.url);
22
25
  const _pagesNodeCompatPath = resolveEntryPath("../server/pages-node-compat.js", import.meta.url);
23
26
  const _pagesApiRoutePath = resolveEntryPath("../server/pages-api-route.js", import.meta.url);
24
27
  const _isrCachePath = resolveEntryPath("../server/isr-cache.js", import.meta.url);
@@ -46,9 +49,12 @@ async function generateServerEntry(pagesDir, nextConfig, fileMatcher, middleware
46
49
  const apiRouteEntries = apiRoutes.map((r, i) => ` { pattern: ${JSON.stringify(r.pattern)}, patternParts: ${JSON.stringify(r.patternParts)}, isDynamic: ${r.isDynamic}, params: ${JSON.stringify(r.params)}, module: api_${i} }`);
47
50
  const appFilePath = findFileWithExts(pagesDir, "_app", fileMatcher);
48
51
  const docFilePath = findFileWithExts(pagesDir, "_document", fileMatcher);
52
+ const errorFilePath = findFileWithExts(pagesDir, "_error", fileMatcher);
49
53
  const appAssetPathJson = appFilePath !== null ? JSON.stringify(normalizePathSeparators(appFilePath)) : "null";
50
54
  const appImportCode = appFilePath !== null ? `import { default as AppComponent } from ${JSON.stringify(normalizePathSeparators(appFilePath))};` : `const AppComponent = null;`;
51
55
  const docImportCode = docFilePath !== null ? `import { default as DocumentComponent } from ${JSON.stringify(normalizePathSeparators(docFilePath))};` : `const DocumentComponent = null;`;
56
+ const errorAssetPathJson = errorFilePath !== null ? JSON.stringify(normalizePathSeparators(errorFilePath)) : "null";
57
+ const errorImportCode = errorFilePath !== null ? `import * as ErrorPageModule from ${JSON.stringify(normalizePathSeparators(errorFilePath))};` : `const ErrorPageModule = null;`;
52
58
  const i18nConfigJson = nextConfig?.i18n ? JSON.stringify({
53
59
  locales: nextConfig.i18n.locales,
54
60
  defaultLocale: nextConfig.i18n.defaultLocale,
@@ -69,6 +75,8 @@ async function generateServerEntry(pagesDir, nextConfig, fileMatcher, middleware
69
75
  headers: nextConfig?.headers ?? [],
70
76
  expireTime: nextConfig?.expireTime,
71
77
  i18n: nextConfig?.i18n ?? null,
78
+ disableOptimizedLoading: nextConfig?.disableOptimizedLoading === true,
79
+ clientTraceMetadata: nextConfig?.clientTraceMetadata,
72
80
  images: {
73
81
  deviceSizes: nextConfig?.images?.deviceSizes,
74
82
  imageSizes: nextConfig?.images?.imageSizes,
@@ -93,19 +101,44 @@ if (typeof _instrumentation.onRequestError === "function") {
93
101
  const middlewareImportCode = middlewarePath ? `import * as middlewareModule from ${JSON.stringify(normalizePathSeparators(middlewarePath))};` : "";
94
102
  const middlewareExportCode = middlewarePath ? `
95
103
  export async function runMiddleware(request, ctx, options) {
96
- return __runGeneratedMiddleware({
104
+ // Auto-detect /_next/data/<buildId>/<page>.json requests so user-written
105
+ // worker entries don't need to know about the data endpoint protocol.
106
+ // Mismatched buildId → JSON 404 short-circuit. Matched → middleware sees
107
+ // the normalized page path via the request URL, and the worker sees the
108
+ // normalized URL via result.rewriteUrl (if middleware didn't already
109
+ // rewrite to something else).
110
+ const __dataNorm = __normalizePagesDataRequest(request);
111
+ if (__dataNorm.notFoundResponse) {
112
+ return { continue: false, response: __dataNorm.notFoundResponse };
113
+ }
114
+ const __result = await __runGeneratedMiddleware({
97
115
  basePath: vinextConfig.basePath,
98
116
  ctx,
99
117
  i18nConfig,
100
- isDataRequest: options?.isDataRequest === true,
118
+ isDataRequest: options?.isDataRequest === true || __dataNorm.isDataReq,
101
119
  isProxy: ${JSON.stringify(isProxyFile(middlewarePath))},
102
120
  module: middlewareModule,
103
- request,
121
+ request: __dataNorm.request,
104
122
  trailingSlash: vinextConfig.trailingSlash,
105
123
  });
124
+ if (__dataNorm.isDataReq && __result.continue && !__result.rewriteUrl && !__result.redirectUrl) {
125
+ return { ...__result, rewriteUrl: __dataNorm.normalizedPathname + __dataNorm.search };
126
+ }
127
+ return __result;
106
128
  }
107
129
  ` : `
108
- export async function runMiddleware() { return { continue: true }; }
130
+ export async function runMiddleware(request) {
131
+ // Even without user middleware, the data-endpoint URL must be normalized so
132
+ // the worker pipeline sees the page path. Mismatched buildId → JSON 404.
133
+ const __dataNorm = __normalizePagesDataRequest(request);
134
+ if (__dataNorm.notFoundResponse) {
135
+ return { continue: false, response: __dataNorm.notFoundResponse };
136
+ }
137
+ if (__dataNorm.isDataReq) {
138
+ return { continue: true, rewriteUrl: __dataNorm.normalizedPathname + __dataNorm.search };
139
+ }
140
+ return { continue: true };
141
+ }
109
142
  `;
110
143
  return `
111
144
  import ${JSON.stringify(_serverGlobalsPath)};
@@ -142,7 +175,9 @@ import {
142
175
  } from ${JSON.stringify(_isrCachePath)};
143
176
  import { getScriptNonceFromHeaderSources as __getScriptNonceFromHeaderSources } from ${JSON.stringify(_cspPath)};
144
177
  import { resolvePagesPageData as __resolvePagesPageData } from ${JSON.stringify(_pagesPageDataPath)};
145
- import { buildNextDataJsonResponse as __buildNextDataJsonResponse, buildNextDataNotFoundResponse as __buildNextDataNotFoundResponse } from ${JSON.stringify(_pagesDataRoutePath)};
178
+ import { resolvePagesPageMethodResponse as __resolvePagesPageMethodResponse } from ${JSON.stringify(_pagesPageMethodPath)};
179
+ import { buildNextDataJsonResponse as __buildNextDataJsonResponse, buildNextDataNotFoundResponse as __buildNextDataNotFoundResponse, isNextDataPathname as __isNextDataPathname, parseNextDataPathname as __parseNextDataPathname } from ${JSON.stringify(_pagesDataRoutePath)};
180
+ import { buildDefaultPagesNotFoundResponse as __buildDefaultPagesNotFoundResponse } from ${JSON.stringify(_pagesDefault404Path)};
146
181
  import { renderPagesPageResponse as __renderPagesPageResponse } from ${JSON.stringify(_pagesPageResponsePath)};
147
182
  ${instrumentationImportCode}
148
183
  ${middlewareImportCode}
@@ -156,6 +191,7 @@ const i18nConfig = ${i18nConfigJson};
156
191
  // match _next/data requests against the embedded buildId without needing
157
192
  // to load next.config.js at runtime.
158
193
  export const buildId = ${buildIdJson};
194
+ const __hasMiddleware = ${JSON.stringify(Boolean(middlewarePath))};
159
195
 
160
196
  // Full resolved config for production server (embedded at build time)
161
197
  export const vinextConfig = ${vinextConfigJson};
@@ -178,6 +214,40 @@ function isrCacheKey(router, pathname) {
178
214
  return __sharedIsrCacheKey(router, pathname, buildId || undefined);
179
215
  }
180
216
 
217
+ /**
218
+ * Detect and normalize /_next/data/<buildId>/<page>.json requests in one
219
+ * place so user-written worker entries don't need to know about the data
220
+ * endpoint protocol. Returns the normalized request (with the page path as
221
+ * its URL), an isDataReq flag, and notFoundResponse when the buildId in
222
+ * the URL does not match this server's buildId — callers should return that
223
+ * response immediately so a stale client falls back to a hard navigation.
224
+ */
225
+ function __normalizePagesDataRequest(request) {
226
+ const reqUrl = new URL(request.url);
227
+ if (!__isNextDataPathname(reqUrl.pathname)) {
228
+ return { isDataReq: false, request, normalizedPathname: null, search: "", notFoundResponse: null };
229
+ }
230
+ const dataMatch = buildId ? __parseNextDataPathname(reqUrl.pathname, buildId) : null;
231
+ if (!dataMatch) {
232
+ return {
233
+ isDataReq: false,
234
+ request,
235
+ normalizedPathname: null,
236
+ search: "",
237
+ notFoundResponse: __buildNextDataNotFoundResponse(),
238
+ };
239
+ }
240
+ const normalizedUrl = new URL(reqUrl);
241
+ normalizedUrl.pathname = dataMatch.pagePathname;
242
+ return {
243
+ isDataReq: true,
244
+ request: new Request(normalizedUrl, request),
245
+ normalizedPathname: dataMatch.pagePathname,
246
+ search: reqUrl.search,
247
+ notFoundResponse: null,
248
+ };
249
+ }
250
+
181
251
  async function renderToStringAsync(element) {
182
252
  const stream = await renderToReadableStream(element);
183
253
  await stream.allReady;
@@ -204,11 +274,22 @@ ${apiImports.join("\n")}
204
274
 
205
275
  ${appImportCode}
206
276
  ${docImportCode}
277
+ ${errorImportCode}
207
278
 
208
279
  export const pageRoutes = [
209
280
  ${pageRouteEntries.join(",\n")}
210
281
  ];
211
282
  const _pageRouteTrie = _buildRouteTrie(pageRoutes);
283
+ const _errorPageRoute = ErrorPageModule
284
+ ? {
285
+ pattern: "/_error",
286
+ patternParts: ["_error"],
287
+ isDynamic: false,
288
+ params: [],
289
+ module: ErrorPageModule,
290
+ filePath: ${errorAssetPathJson},
291
+ }
292
+ : null;
212
293
 
213
294
  const apiRoutes = [
214
295
  ${apiRouteEntries.join(",\n")}
@@ -240,7 +321,14 @@ export function matchPageRoute(url, request) {
240
321
  }
241
322
 
242
323
  function parseQuery(url) {
243
- const qs = url.split("?")[1];
324
+ // Per RFC 3986 only the first "?" separates path from query, so additional
325
+ // "?" chars belong to the query string (e.g. /linker?href=/about?hello=world
326
+ // has query "href=/about?hello=world"). split("?")[1] would drop everything
327
+ // after the second "?" and strip embedded query strings from values.
328
+ const queryIndex = url.indexOf("?");
329
+ if (queryIndex === -1) return {};
330
+ const hashIndex = url.indexOf("#", queryIndex + 1);
331
+ const qs = hashIndex === -1 ? url.slice(queryIndex + 1) : url.slice(queryIndex + 1, hashIndex);
244
332
  if (!qs) return {};
245
333
  const p = new URLSearchParams(qs);
246
334
  const q = {};
@@ -268,14 +356,39 @@ function patternToNextFormat(pattern) {
268
356
  .replace(/:([^\\/]+?)(?=\\/|$)/g, "[$1]");
269
357
  }
270
358
 
271
- function collectAssetTags(manifest, moduleIds, scriptNonce) {
359
+ function resolveSsrManifest(manifest) {
272
360
  // Fall back to embedded manifest (set by vinext:cloudflare-build for Workers)
273
- const m = (manifest && Object.keys(manifest).length > 0)
361
+ return (manifest && Object.keys(manifest).length > 0)
274
362
  ? manifest
275
363
  : (typeof globalThis !== "undefined" && globalThis.__VINEXT_SSR_MANIFEST__) || null;
364
+ }
365
+
366
+ function getManifestFilesForModule(manifest, moduleId) {
367
+ if (!manifest || !moduleId) return null;
368
+
369
+ var files = manifest[moduleId];
370
+ if (files) return files;
371
+
372
+ for (var key in manifest) {
373
+ if (moduleId.endsWith("/" + key) || moduleId === key) {
374
+ return manifest[key];
375
+ }
376
+ }
377
+ return null;
378
+ }
379
+
380
+ function collectAssetTags(manifest, moduleIds, scriptNonce) {
381
+ const m = resolveSsrManifest(manifest);
276
382
  const tags = [];
277
383
  const seen = new Set();
278
384
  const nonceAttr = __createNonceAttribute(scriptNonce);
385
+ // Mirrors Next.js \`_document\` behaviour: when \`experimental.disableOptimizedLoading\`
386
+ // is false (the default), page scripts are emitted with \`defer\` in <head>. See
387
+ // .nextjs-ref/packages/next/src/pages/_document.tsx getScripts() — \`defer={!disableOptimizedLoading}\`.
388
+ // vinext always emits \`type="module"\` (which already defers implicitly), but
389
+ // upstream tests (e.g. test/e2e/optimized-loading) assert the literal \`defer\`
390
+ // attribute, and adding it preserves parity without changing browser behaviour.
391
+ const deferAttr = vinextConfig.disableOptimizedLoading ? "" : " defer";
279
392
 
280
393
  // Load the set of lazy chunk filenames (only reachable via dynamic imports).
281
394
  // These should NOT get <link rel="modulepreload"> or <script type="module">
@@ -289,7 +402,7 @@ function collectAssetTags(manifest, moduleIds, scriptNonce) {
289
402
  const entry = globalThis.__VINEXT_CLIENT_ENTRY__;
290
403
  seen.add(entry);
291
404
  tags.push('<link rel="modulepreload"' + nonceAttr + ' href="/' + entry + '" />');
292
- tags.push('<script type="module"' + nonceAttr + ' src="/' + entry + '" crossorigin><\/script>');
405
+ tags.push('<script type="module"' + deferAttr + nonceAttr + ' src="/' + entry + '" crossorigin><\/script>');
293
406
  }
294
407
  if (m) {
295
408
  // Always inject shared chunks (framework, vinext runtime, entry) and
@@ -305,18 +418,7 @@ function collectAssetTags(manifest, moduleIds, scriptNonce) {
305
418
  // Collect assets for the requested page modules
306
419
  for (var mi = 0; mi < moduleIds.length; mi++) {
307
420
  var id = moduleIds[mi];
308
- var files = m[id];
309
- if (!files) {
310
- // Absolute path didn't match — try matching by suffix.
311
- // Manifest keys are relative (e.g. "pages/about.tsx") while
312
- // moduleIds may be absolute (e.g. "/home/.../pages/about.tsx").
313
- for (var mk in m) {
314
- if (id.endsWith("/" + mk) || id === mk) {
315
- files = m[mk];
316
- break;
317
- }
318
- }
319
- }
421
+ var files = getManifestFilesForModule(m, id);
320
422
  if (files) {
321
423
  for (var fi = 0; fi < files.length; fi++) allFiles.push(files[fi]);
322
424
  }
@@ -370,20 +472,55 @@ function collectAssetTags(manifest, moduleIds, scriptNonce) {
370
472
  // (React.lazy, next/dynamic) and should only be fetched on demand.
371
473
  if (lazySet && lazySet.has(tf)) continue;
372
474
  tags.push('<link rel="modulepreload"' + nonceAttr + ' href="/' + tf + '" />');
373
- tags.push('<script type="module"' + nonceAttr + ' src="/' + tf + '" crossorigin><\/script>');
475
+ tags.push('<script type="module"' + deferAttr + nonceAttr + ' src="/' + tf + '" crossorigin><\/script>');
374
476
  }
375
477
  }
376
478
  }
377
479
  return tags.join("\\n ");
378
480
  }
379
481
 
482
+ function resolveClientModuleUrl(manifest, moduleId) {
483
+ const files = getManifestFilesForModule(resolveSsrManifest(manifest), moduleId);
484
+ if (!files) return undefined;
485
+ for (var i = 0; i < files.length; i++) {
486
+ var file = files[i];
487
+ if (!file || !file.endsWith(".js")) continue;
488
+ if (file.charAt(0) !== "/") file = "/" + file;
489
+ return file;
490
+ }
491
+ return undefined;
492
+ }
493
+
380
494
  export async function renderPage(request, url, manifest, ctx, middlewareHeaders, options) {
381
495
  if (ctx) return _runWithExecutionContext(ctx, () => _renderPage(request, url, manifest, middlewareHeaders, options));
382
496
  return _renderPage(request, url, manifest, middlewareHeaders, options);
383
497
  }
384
498
 
385
499
  async function _renderPage(request, url, manifest, middlewareHeaders, options) {
386
- const isDataReq = !!(options && options.isDataReq);
500
+ let isDataReq = !!(options && options.isDataReq);
501
+ // Auto-detect /_next/data/... requests by inspecting the incoming request
502
+ // URL. The worker pipeline does not need to know about the data endpoint
503
+ // protocol — when it forwards an unrewritten data URL as the url arg, we
504
+ // normalize it to the page path here.
505
+ if (!isDataReq) {
506
+ const __dataNorm = __normalizePagesDataRequest(request);
507
+ if (__dataNorm.notFoundResponse) return __dataNorm.notFoundResponse;
508
+ if (__dataNorm.isDataReq) {
509
+ isDataReq = true;
510
+ if (url && url.startsWith("/_next/data/")) {
511
+ const __qs = url.includes("?") ? url.slice(url.indexOf("?")) : "";
512
+ url = __dataNorm.normalizedPathname + __qs;
513
+ }
514
+ }
515
+ }
516
+ const statusCode = options && typeof options.statusCode === "number" ? options.statusCode : undefined;
517
+ const asPath = options && typeof options.asPath === "string" ? options.asPath : undefined;
518
+ const renderErrorPageOnMiss = !(options && options.renderErrorPageOnMiss === false);
519
+ // Guard against infinite recursion when the user's custom 500/error page
520
+ // itself throws during render. When this flag is set, the catch block below
521
+ // returns the plain "Internal Server Error" text response instead of trying
522
+ // to render an error page again. Fixes #1458.
523
+ const isInternalErrorRender = !!(options && options.__isInternalErrorRender);
387
524
  const localeInfo = i18nConfig
388
525
  ? resolvePagesI18nRequest(
389
526
  url,
@@ -405,13 +542,35 @@ async function _renderPage(request, url, manifest, middlewareHeaders, options) {
405
542
  return new Response(null, { status: 307, headers: { Location: localeInfo.redirectUrl } });
406
543
  }
407
544
 
408
- const match = matchRoute(routeUrl, pageRoutes);
545
+ // Internal error render path: caller has pinned a specific route (the
546
+ // user's pages/500 or pages/_error) to render in response to an SSR throw.
547
+ // Skip route matching so we don't accidentally double-route, and skip the
548
+ // missing-route 404 fallback. See catch block below. Fixes #1458.
549
+ let match = options && options.__forcedRoute
550
+ ? { route: options.__forcedRoute, params: {} }
551
+ : matchRoute(routeUrl, pageRoutes);
552
+ let renderStatusCodeOverride = statusCode;
553
+ let renderAsPath = asPath;
409
554
  if (!match) {
410
555
  if (isDataReq) {
411
556
  return __buildNextDataNotFoundResponse();
412
557
  }
413
- return new Response("<!DOCTYPE html><html><body><h1>404 - Page not found</h1></body></html>",
414
- { status: 404, headers: { "Content-Type": "text/html" } });
558
+ if (!renderErrorPageOnMiss) {
559
+ return __buildDefaultPagesNotFoundResponse();
560
+ }
561
+ const notFoundMatch = matchRoute("/404", pageRoutes);
562
+ // matchRoute may match a catch-all (e.g. [...slug]); only use the explicit pages/404 route.
563
+ if (notFoundMatch && notFoundMatch.route.pattern === "/404") {
564
+ match = notFoundMatch;
565
+ renderStatusCodeOverride = 404;
566
+ renderAsPath = routeUrl;
567
+ } else if (_errorPageRoute) {
568
+ match = { route: _errorPageRoute, params: {} };
569
+ renderStatusCodeOverride = 404;
570
+ renderAsPath = routeUrl;
571
+ } else {
572
+ return __buildDefaultPagesNotFoundResponse();
573
+ }
415
574
  }
416
575
 
417
576
  const { route, params } = match;
@@ -422,12 +581,13 @@ async function _renderPage(request, url, manifest, middlewareHeaders, options) {
422
581
  ensureFetchPatch();
423
582
  try {
424
583
  const routePattern = patternToNextFormat(route.pattern);
584
+ const renderStatusCode = renderStatusCodeOverride ?? (routePattern === "/404" ? 404 : undefined);
425
585
  const query = mergeRouteParamsIntoQuery(parseQuery(routeUrl), params);
426
586
  if (typeof setSSRContext === "function") {
427
587
  setSSRContext({
428
588
  pathname: routePattern,
429
589
  query,
430
- asPath: routeUrl,
590
+ asPath: renderAsPath || routeUrl,
431
591
  locale: locale,
432
592
  locales: i18nConfig ? i18nConfig.locales : undefined,
433
593
  defaultLocale: currentDefaultLocale,
@@ -450,6 +610,31 @@ async function _renderPage(request, url, manifest, middlewareHeaders, options) {
450
610
  if (!PageComponent) {
451
611
  return new Response("Page has no default export", { status: 500 });
452
612
  }
613
+
614
+ // Refs #1463: reject non-GET/HEAD methods on static (no
615
+ // getServerSideProps) Pages routes with 405 + Allow: GET, HEAD.
616
+ // Skip for error/status pages (/_error, /404, /500), data requests
617
+ // (those go through the JSON envelope path and have their own shape),
618
+ // and renders that are already an error-page miss-render override.
619
+ // Mirrors Next.js's base-server.ts L2277 carve-outs.
620
+ if (
621
+ !isDataReq &&
622
+ routePattern !== "/_error" &&
623
+ routePattern !== "/404" &&
624
+ routePattern !== "/500" &&
625
+ renderStatusCodeOverride === undefined
626
+ ) {
627
+ const methodResponse = __resolvePagesPageMethodResponse({
628
+ hasGetServerSideProps: typeof pageModule.getServerSideProps === "function",
629
+ method: request.method,
630
+ });
631
+ if (methodResponse) {
632
+ return methodResponse;
633
+ }
634
+ }
635
+
636
+ const pageModuleUrl = resolveClientModuleUrl(manifest, route.filePath);
637
+ const appModuleUrl = resolveClientModuleUrl(manifest, _appAssetPath);
453
638
  const scriptNonce = __getScriptNonceFromHeaderSources(request.headers, middlewareHeaders);
454
639
  // Build font Link header early so it's available for ISR cached responses too.
455
640
  // Font preloads are module-level state populated at import time and persist across requests.
@@ -470,7 +655,7 @@ async function _renderPage(request, url, manifest, middlewareHeaders, options) {
470
655
  setSSRContext({
471
656
  pathname: routePattern,
472
657
  query,
473
- asPath: routeUrl,
658
+ asPath: renderAsPath || routeUrl,
474
659
  locale: locale,
475
660
  locales: i18nConfig ? i18nConfig.locales : undefined,
476
661
  defaultLocale: currentDefaultLocale,
@@ -508,6 +693,13 @@ async function _renderPage(request, url, manifest, middlewareHeaders, options) {
508
693
  isrGet,
509
694
  isrSet,
510
695
  expireSeconds: vinextConfig.expireTime,
696
+ // The vinext build phase boots the prod server with VINEXT_PRERENDER=1
697
+ // and fetches every statically-generated page through it. That hit is
698
+ // the "build" prerender for revalidateReason; runtime hits are not.
699
+ // Mirrors Next.js's \`renderOpts.isBuildTimePrerendering\`. See
700
+ // \`.nextjs-ref/packages/next/src/server/render.tsx\` and
701
+ // \`packages/vinext/src/build/prerender.ts\`.
702
+ isBuildTimePrerendering: typeof process !== "undefined" && process.env && process.env.VINEXT_PRERENDER === "1",
511
703
  pageModule,
512
704
  params,
513
705
  query,
@@ -529,12 +721,21 @@ async function _renderPage(request, url, manifest, middlewareHeaders, options) {
529
721
  safeJsonStringify,
530
722
  sanitizeDestination: sanitizeDestinationLocal,
531
723
  scriptNonce,
724
+ statusCode: renderStatusCode,
532
725
  triggerBackgroundRegeneration,
726
+ vinext: {
727
+ pageModuleUrl,
728
+ appModuleUrl,
729
+ hasMiddleware: __hasMiddleware,
730
+ },
533
731
  });
534
732
  if (pageDataResult.kind === "response") {
535
733
  return pageDataResult.response;
536
734
  }
537
735
  let pageProps = pageDataResult.pageProps;
736
+ if (routePattern === "/_error" && typeof renderStatusCode === "number") {
737
+ pageProps = { ...pageProps, statusCode: renderStatusCode };
738
+ }
538
739
  var gsspRes = pageDataResult.gsspRes;
539
740
  let isrRevalidateSeconds = pageDataResult.isrRevalidateSeconds;
540
741
  const isFallbackRender = pageDataResult.isFallback === true;
@@ -547,7 +748,7 @@ async function _renderPage(request, url, manifest, middlewareHeaders, options) {
547
748
  setSSRContext({
548
749
  pathname: routePattern,
549
750
  query,
550
- asPath: routeUrl,
751
+ asPath: renderAsPath || routeUrl,
551
752
  locale: locale,
552
753
  locales: i18nConfig ? i18nConfig.locales : undefined,
553
754
  defaultLocale: currentDefaultLocale,
@@ -564,14 +765,33 @@ async function _renderPage(request, url, manifest, middlewareHeaders, options) {
564
765
  // cookies set on the gsspRes by getServerSideProps are forwarded so
565
766
  // middleware/auth flows work the same as the HTML page.
566
767
  if (isDataReq) {
567
- const init = {};
568
- if (gsspRes && gsspRes.headers) {
569
- init.headers = {};
570
- for (const [k, v] of Object.entries(gsspRes.headers)) {
768
+ const init = { headers: {} };
769
+ if (gsspRes && typeof gsspRes.getHeaders === "function") {
770
+ const gsspHeaders = gsspRes.getHeaders();
771
+ for (const k of Object.keys(gsspHeaders)) {
772
+ const v = gsspHeaders[k];
571
773
  if (v === undefined || v === null) continue;
572
774
  init.headers[k] = Array.isArray(v) ? v.join(", ") : String(v);
573
775
  }
574
776
  }
777
+ if (gsspRes) {
778
+ // Default Cache-Control for gSSP-driven _next/data responses,
779
+ // matching Next.js's pages-handler.ts (revalidate: 0 →
780
+ // getCacheControlHeader). Skip when gSSP already set one via
781
+ // res.setHeader (case-insensitive). Mirrors the HTML branch in
782
+ // pages-page-response.ts and the dev-server branch. Fixes #1461.
783
+ var hasUserCacheControl = false;
784
+ for (const headerKey of Object.keys(init.headers)) {
785
+ if (headerKey.toLowerCase() === "cache-control") {
786
+ hasUserCacheControl = true;
787
+ break;
788
+ }
789
+ }
790
+ if (!hasUserCacheControl) {
791
+ init.headers["Cache-Control"] =
792
+ "private, no-cache, no-store, max-age=0, must-revalidate";
793
+ }
794
+ }
575
795
  return __buildNextDataJsonResponse(pageProps, safeJsonStringify, init);
576
796
  }
577
797
 
@@ -624,6 +844,7 @@ async function _renderPage(request, url, manifest, middlewareHeaders, options) {
624
844
  }
625
845
  },
626
846
  getSSRHeadHTML: typeof getSSRHeadHTML === "function" ? getSSRHeadHTML : undefined,
847
+ clientTraceMetadata: vinextConfig.clientTraceMetadata,
627
848
  gsspRes,
628
849
  isrCacheKey,
629
850
  expireSeconds: vinextConfig.expireTime,
@@ -649,6 +870,12 @@ async function _renderPage(request, url, manifest, middlewareHeaders, options) {
649
870
  routeUrl,
650
871
  safeJsonStringify,
651
872
  scriptNonce,
873
+ statusCode: renderStatusCode,
874
+ vinext: {
875
+ pageModuleUrl,
876
+ appModuleUrl,
877
+ hasMiddleware: __hasMiddleware,
878
+ },
652
879
  });
653
880
  } catch (e) {
654
881
  console.error("[vinext] SSR error:", e);
@@ -657,6 +884,37 @@ async function _renderPage(request, url, manifest, middlewareHeaders, options) {
657
884
  { path: url, method: request.method, headers: Object.fromEntries(request.headers.entries()) },
658
885
  { routerKind: "Pages Router", routePath: route.pattern, routeType: "render" },
659
886
  ).catch(() => { /* ignore reporting errors */ });
887
+ // Data (_next/data) requests can't render HTML, and avoid recursion if
888
+ // we're already in the middle of rendering the error page itself.
889
+ // Mirrors Next.js base-server.ts which renders /500 (or _error) when
890
+ // SSR throws. Fixes #1458.
891
+ if (!isInternalErrorRender && !isDataReq) {
892
+ // Look for an explicit pages/500.tsx first, then fall back to _error.
893
+ // Only match the literal /500 pattern — never a catch-all/dynamic route.
894
+ let errorRoute = null;
895
+ for (var __i = 0; __i < pageRoutes.length; __i++) {
896
+ if (pageRoutes[__i].pattern === "/500") {
897
+ errorRoute = pageRoutes[__i];
898
+ break;
899
+ }
900
+ }
901
+ if (!errorRoute && _errorPageRoute) {
902
+ errorRoute = _errorPageRoute;
903
+ }
904
+ if (errorRoute) {
905
+ try {
906
+ return await _renderPage(request, url, manifest, middlewareHeaders, {
907
+ statusCode: 500,
908
+ asPath: url,
909
+ renderErrorPageOnMiss: false,
910
+ __isInternalErrorRender: true,
911
+ __forcedRoute: errorRoute,
912
+ });
913
+ } catch (errorPageErr) {
914
+ console.error("[vinext] Error page render failed:", errorPageErr);
915
+ }
916
+ }
917
+ }
660
918
  return new Response("Internal Server Error", { status: 500 });
661
919
  }
662
920
  });