vinext 0.0.30 → 0.0.31

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 (435) hide show
  1. package/README.md +12 -6
  2. package/dist/build/prerender.d.ts +188 -0
  3. package/dist/build/prerender.js +675 -0
  4. package/dist/build/prerender.js.map +1 -0
  5. package/dist/build/report.d.ts +45 -46
  6. package/dist/build/report.js +247 -276
  7. package/dist/build/report.js.map +1 -1
  8. package/dist/build/run-prerender.d.ts +62 -0
  9. package/dist/build/run-prerender.js +183 -0
  10. package/dist/build/run-prerender.js.map +1 -0
  11. package/dist/build/server-manifest.d.ts +19 -0
  12. package/dist/build/server-manifest.js +29 -0
  13. package/dist/build/server-manifest.js.map +1 -0
  14. package/dist/build/static-export.d.ts +51 -66
  15. package/dist/build/static-export.js +51 -545
  16. package/dist/build/static-export.js.map +1 -1
  17. package/dist/check.d.ts +26 -24
  18. package/dist/check.js +591 -571
  19. package/dist/check.js.map +1 -1
  20. package/dist/cli.d.ts +1 -15
  21. package/dist/cli.js +430 -491
  22. package/dist/cli.js.map +1 -1
  23. package/dist/client/entry.d.ts +1 -2
  24. package/dist/client/entry.js +49 -62
  25. package/dist/client/entry.js.map +1 -1
  26. package/dist/client/validate-module-path.d.ts +4 -1
  27. package/dist/client/validate-module-path.js +23 -28
  28. package/dist/client/validate-module-path.js.map +1 -1
  29. package/dist/client/vinext-next-data.d.ts +15 -20
  30. package/dist/client/vinext-next-data.js +0 -1
  31. package/dist/cloudflare/index.d.ts +3 -8
  32. package/dist/cloudflare/index.js +3 -8
  33. package/dist/cloudflare/kv-cache-handler.d.ts +95 -105
  34. package/dist/cloudflare/kv-cache-handler.js +354 -380
  35. package/dist/cloudflare/kv-cache-handler.js.map +1 -1
  36. package/dist/cloudflare/tpr.d.ts +36 -34
  37. package/dist/cloudflare/tpr.js +460 -603
  38. package/dist/cloudflare/tpr.js.map +1 -1
  39. package/dist/config/config-matchers.d.ts +31 -40
  40. package/dist/config/config-matchers.js +727 -936
  41. package/dist/config/config-matchers.js.map +1 -1
  42. package/dist/config/dotenv.d.ts +18 -11
  43. package/dist/config/dotenv.js +79 -84
  44. package/dist/config/dotenv.js.map +1 -1
  45. package/dist/config/next-config.d.ts +156 -146
  46. package/dist/config/next-config.js +374 -464
  47. package/dist/config/next-config.js.map +1 -1
  48. package/dist/deploy.d.ts +87 -96
  49. package/dist/deploy.js +490 -628
  50. package/dist/deploy.js.map +1 -1
  51. package/dist/entries/app-browser-entry.d.ts +4 -1
  52. package/dist/entries/app-browser-entry.js +12 -8
  53. package/dist/entries/app-browser-entry.js.map +1 -1
  54. package/dist/entries/app-rsc-entry.d.ts +33 -20
  55. package/dist/entries/app-rsc-entry.js +442 -211
  56. package/dist/entries/app-rsc-entry.js.map +1 -1
  57. package/dist/entries/app-ssr-entry.d.ts +9 -1
  58. package/dist/entries/app-ssr-entry.js +61 -28
  59. package/dist/entries/app-ssr-entry.js.map +1 -1
  60. package/dist/entries/pages-client-entry.d.ts +6 -2
  61. package/dist/entries/pages-client-entry.js +30 -33
  62. package/dist/entries/pages-client-entry.js.map +1 -1
  63. package/dist/entries/pages-entry-helpers.d.ts +5 -1
  64. package/dist/entries/pages-entry-helpers.js +17 -14
  65. package/dist/entries/pages-entry-helpers.js.map +1 -1
  66. package/dist/entries/pages-server-entry.d.ts +6 -2
  67. package/dist/entries/pages-server-entry.js +84 -113
  68. package/dist/entries/pages-server-entry.js.map +1 -1
  69. package/dist/index.d.ts +82 -62
  70. package/dist/index.js +2172 -3133
  71. package/dist/index.js.map +1 -1
  72. package/dist/init.d.ts +40 -37
  73. package/dist/init.js +201 -258
  74. package/dist/init.js.map +1 -1
  75. package/dist/plugins/async-hooks-stub.d.ts +7 -3
  76. package/dist/plugins/async-hooks-stub.js +39 -42
  77. package/dist/plugins/async-hooks-stub.js.map +1 -1
  78. package/dist/plugins/client-reference-dedup.d.ts +7 -3
  79. package/dist/plugins/client-reference-dedup.js +63 -88
  80. package/dist/plugins/client-reference-dedup.js.map +1 -1
  81. package/dist/routing/app-router.d.ts +100 -96
  82. package/dist/routing/app-router.js +560 -670
  83. package/dist/routing/app-router.js.map +1 -1
  84. package/dist/routing/file-matcher.d.ts +18 -15
  85. package/dist/routing/file-matcher.js +65 -65
  86. package/dist/routing/file-matcher.js.map +1 -1
  87. package/dist/routing/pages-router.d.ts +23 -24
  88. package/dist/routing/pages-router.js +147 -172
  89. package/dist/routing/pages-router.js.map +1 -1
  90. package/dist/routing/route-trie.d.ts +23 -20
  91. package/dist/routing/route-trie.js +131 -151
  92. package/dist/routing/route-trie.js.map +1 -1
  93. package/dist/routing/route-validation.d.ts +5 -2
  94. package/dist/routing/route-validation.js +98 -130
  95. package/dist/routing/route-validation.js.map +1 -1
  96. package/dist/routing/utils.d.ts +10 -7
  97. package/dist/routing/utils.js +75 -111
  98. package/dist/routing/utils.js.map +1 -1
  99. package/dist/server/api-handler.d.ts +8 -13
  100. package/dist/server/api-handler.js +161 -193
  101. package/dist/server/api-handler.js.map +1 -1
  102. package/dist/server/app-router-entry.d.ts +6 -16
  103. package/dist/server/app-router-entry.js +26 -54
  104. package/dist/server/app-router-entry.js.map +1 -1
  105. package/dist/server/dev-module-runner.d.ts +11 -64
  106. package/dist/server/dev-module-runner.js +89 -101
  107. package/dist/server/dev-module-runner.js.map +1 -1
  108. package/dist/server/dev-origin-check.d.ts +12 -10
  109. package/dist/server/dev-origin-check.js +98 -108
  110. package/dist/server/dev-origin-check.js.map +1 -1
  111. package/dist/server/dev-server.d.ts +17 -14
  112. package/dist/server/dev-server.js +542 -869
  113. package/dist/server/dev-server.js.map +1 -1
  114. package/dist/server/html.d.ts +4 -1
  115. package/dist/server/html.js +25 -26
  116. package/dist/server/html.js.map +1 -1
  117. package/dist/server/image-optimization.d.ts +31 -28
  118. package/dist/server/image-optimization.js +181 -210
  119. package/dist/server/image-optimization.js.map +1 -1
  120. package/dist/server/instrumentation.d.ts +25 -22
  121. package/dist/server/instrumentation.js +110 -122
  122. package/dist/server/instrumentation.js.map +1 -1
  123. package/dist/server/isr-cache.d.ts +16 -26
  124. package/dist/server/isr-cache.js +106 -128
  125. package/dist/server/isr-cache.js.map +1 -1
  126. package/dist/server/metadata-routes.d.ts +85 -88
  127. package/dist/server/metadata-routes.js +270 -317
  128. package/dist/server/metadata-routes.js.map +1 -1
  129. package/dist/server/middleware-codegen.d.ts +7 -4
  130. package/dist/server/middleware-codegen.js +61 -61
  131. package/dist/server/middleware-codegen.js.map +1 -1
  132. package/dist/server/middleware-request-headers.d.ts +8 -6
  133. package/dist/server/middleware-request-headers.js +47 -65
  134. package/dist/server/middleware-request-headers.js.map +1 -1
  135. package/dist/server/middleware.d.ts +31 -47
  136. package/dist/server/middleware.js +273 -404
  137. package/dist/server/middleware.js.map +1 -1
  138. package/dist/server/normalize-path.d.ts +4 -1
  139. package/dist/server/normalize-path.js +33 -47
  140. package/dist/server/normalize-path.js.map +1 -1
  141. package/dist/server/pages-i18n.d.ts +38 -30
  142. package/dist/server/pages-i18n.js +112 -139
  143. package/dist/server/pages-i18n.js.map +1 -1
  144. package/dist/server/prod-server.d.ts +19 -31
  145. package/dist/server/prod-server.js +714 -945
  146. package/dist/server/prod-server.js.map +1 -1
  147. package/dist/server/request-log.d.ts +18 -12
  148. package/dist/server/request-log.js +45 -52
  149. package/dist/server/request-log.js.map +1 -1
  150. package/dist/server/request-pipeline.d.ts +9 -17
  151. package/dist/server/request-pipeline.js +133 -184
  152. package/dist/server/request-pipeline.js.map +1 -1
  153. package/dist/server/worker-utils.d.ts +4 -1
  154. package/dist/server/worker-utils.js +31 -37
  155. package/dist/server/worker-utils.js.map +1 -1
  156. package/dist/shims/amp.d.ts +5 -2
  157. package/dist/shims/amp.js +19 -15
  158. package/dist/shims/amp.js.map +1 -1
  159. package/dist/shims/app.d.ts +8 -10
  160. package/dist/shims/app.js +0 -1
  161. package/dist/shims/cache-runtime.d.ts +20 -45
  162. package/dist/shims/cache-runtime.js +271 -422
  163. package/dist/shims/cache-runtime.js.map +1 -1
  164. package/dist/shims/cache.d.ts +130 -121
  165. package/dist/shims/cache.js +339 -427
  166. package/dist/shims/cache.js.map +1 -1
  167. package/dist/shims/client-only.d.ts +1 -18
  168. package/dist/shims/client-only.js +0 -17
  169. package/dist/shims/compat-router.d.ts +4 -1
  170. package/dist/shims/compat-router.js +23 -19
  171. package/dist/shims/compat-router.js.map +1 -1
  172. package/dist/shims/config.d.ts +7 -5
  173. package/dist/shims/config.js +16 -23
  174. package/dist/shims/config.js.map +1 -1
  175. package/dist/shims/constants.d.ts +119 -118
  176. package/dist/shims/constants.js +159 -164
  177. package/dist/shims/constants.js.map +1 -1
  178. package/dist/shims/document.d.ts +20 -16
  179. package/dist/shims/document.js +41 -22
  180. package/dist/shims/document.js.map +1 -1
  181. package/dist/shims/dynamic.d.ts +13 -22
  182. package/dist/shims/dynamic.js +122 -136
  183. package/dist/shims/dynamic.js.map +1 -1
  184. package/dist/shims/error-boundary.d.ts +22 -15
  185. package/dist/shims/error-boundary.js +81 -79
  186. package/dist/shims/error-boundary.js.map +1 -1
  187. package/dist/shims/error.d.ts +11 -12
  188. package/dist/shims/error.js +35 -39
  189. package/dist/shims/error.js.map +1 -1
  190. package/dist/shims/fetch-cache.d.ts +16 -14
  191. package/dist/shims/fetch-cache.js +437 -645
  192. package/dist/shims/fetch-cache.js.map +1 -1
  193. package/dist/shims/font-google-base.d.ts +28 -26
  194. package/dist/shims/font-google-base.js +238 -325
  195. package/dist/shims/font-google-base.js.map +1 -1
  196. package/dist/shims/font-google.d.ts +3 -3
  197. package/dist/shims/font-google.generated.d.ts +1928 -1924
  198. package/dist/shims/font-google.generated.js +1928 -2133
  199. package/dist/shims/font-google.generated.js.map +1 -1
  200. package/dist/shims/font-google.js +3 -3
  201. package/dist/shims/font-local.d.ts +28 -26
  202. package/dist/shims/font-local.js +204 -260
  203. package/dist/shims/font-local.js.map +1 -1
  204. package/dist/shims/form.d.ts +13 -27
  205. package/dist/shims/form.js +128 -180
  206. package/dist/shims/form.js.map +1 -1
  207. package/dist/shims/head-state.d.ts +8 -13
  208. package/dist/shims/head-state.js +25 -42
  209. package/dist/shims/head-state.js.map +1 -1
  210. package/dist/shims/head.d.ts +16 -20
  211. package/dist/shims/head.js +172 -250
  212. package/dist/shims/head.js.map +1 -1
  213. package/dist/shims/headers.d.ts +84 -78
  214. package/dist/shims/headers.js +447 -575
  215. package/dist/shims/headers.js.map +1 -1
  216. package/dist/shims/i18n-context.d.ts +16 -20
  217. package/dist/shims/i18n-context.js +35 -48
  218. package/dist/shims/i18n-context.js.map +1 -1
  219. package/dist/shims/i18n-state.d.ts +8 -14
  220. package/dist/shims/i18n-state.js +34 -42
  221. package/dist/shims/i18n-state.js.map +1 -1
  222. package/dist/shims/image-config.d.ts +11 -8
  223. package/dist/shims/image-config.js +50 -83
  224. package/dist/shims/image-config.js.map +1 -1
  225. package/dist/shims/image.d.ts +37 -46
  226. package/dist/shims/image.js +283 -308
  227. package/dist/shims/image.js.map +1 -1
  228. package/dist/shims/internal/api-utils.d.ts +7 -4
  229. package/dist/shims/internal/api-utils.js +0 -6
  230. package/dist/shims/internal/app-router-context.d.ts +22 -17
  231. package/dist/shims/internal/app-router-context.js +17 -13
  232. package/dist/shims/internal/app-router-context.js.map +1 -1
  233. package/dist/shims/internal/cookies.d.ts +2 -9
  234. package/dist/shims/internal/cookies.js +2 -9
  235. package/dist/shims/internal/parse-cookie-header.d.ts +4 -1
  236. package/dist/shims/internal/parse-cookie-header.js +29 -29
  237. package/dist/shims/internal/parse-cookie-header.js.map +1 -1
  238. package/dist/shims/internal/router-context.d.ts +6 -1
  239. package/dist/shims/internal/router-context.js +11 -7
  240. package/dist/shims/internal/router-context.js.map +1 -1
  241. package/dist/shims/internal/utils.d.ts +40 -37
  242. package/dist/shims/internal/utils.js +24 -30
  243. package/dist/shims/internal/utils.js.map +1 -1
  244. package/dist/shims/internal/work-unit-async-storage.d.ts +6 -10
  245. package/dist/shims/internal/work-unit-async-storage.js +14 -11
  246. package/dist/shims/internal/work-unit-async-storage.js.map +1 -1
  247. package/dist/shims/layout-segment-context.d.ts +11 -14
  248. package/dist/shims/layout-segment-context.js +24 -23
  249. package/dist/shims/layout-segment-context.js.map +1 -1
  250. package/dist/shims/legacy-image.d.ts +39 -46
  251. package/dist/shims/legacy-image.js +47 -42
  252. package/dist/shims/legacy-image.js.map +1 -1
  253. package/dist/shims/link.d.ts +32 -36
  254. package/dist/shims/link.js +255 -391
  255. package/dist/shims/link.js.map +1 -1
  256. package/dist/shims/metadata.d.ts +210 -202
  257. package/dist/shims/metadata.js +545 -546
  258. package/dist/shims/metadata.js.map +1 -1
  259. package/dist/shims/navigation-state.d.ts +10 -18
  260. package/dist/shims/navigation-state.js +66 -74
  261. package/dist/shims/navigation-state.js.map +1 -1
  262. package/dist/shims/navigation.d.ts +59 -63
  263. package/dist/shims/navigation.js +505 -704
  264. package/dist/shims/navigation.js.map +1 -1
  265. package/dist/shims/og.d.ts +2 -20
  266. package/dist/shims/og.js +2 -19
  267. package/dist/shims/readonly-url-search-params.d.ts +8 -5
  268. package/dist/shims/readonly-url-search-params.js +26 -22
  269. package/dist/shims/readonly-url-search-params.js.map +1 -1
  270. package/dist/shims/request-context.d.ts +8 -5
  271. package/dist/shims/request-context.js +50 -60
  272. package/dist/shims/request-context.js.map +1 -1
  273. package/dist/shims/request-state-types.d.ts +11 -11
  274. package/dist/shims/request-state-types.js +0 -1
  275. package/dist/shims/router-state.d.ts +13 -10
  276. package/dist/shims/router-state.js +34 -43
  277. package/dist/shims/router-state.js.map +1 -1
  278. package/dist/shims/router.d.ts +81 -85
  279. package/dist/shims/router.js +506 -628
  280. package/dist/shims/router.js.map +1 -1
  281. package/dist/shims/script.d.ts +39 -48
  282. package/dist/shims/script.js +107 -160
  283. package/dist/shims/script.js.map +1 -1
  284. package/dist/shims/server-only.d.ts +1 -19
  285. package/dist/shims/server-only.js +0 -18
  286. package/dist/shims/server.d.ts +175 -164
  287. package/dist/shims/server.js +462 -478
  288. package/dist/shims/server.js.map +1 -1
  289. package/dist/shims/unified-request-context.d.ts +20 -20
  290. package/dist/shims/unified-request-context.js +81 -99
  291. package/dist/shims/unified-request-context.js.map +1 -1
  292. package/dist/shims/url-safety.d.ts +4 -1
  293. package/dist/shims/url-safety.js +15 -11
  294. package/dist/shims/url-safety.js.map +1 -1
  295. package/dist/shims/url-utils.d.ts +8 -5
  296. package/dist/shims/url-utils.js +62 -93
  297. package/dist/shims/url-utils.js.map +1 -1
  298. package/dist/shims/web-vitals.d.ts +10 -8
  299. package/dist/shims/web-vitals.js +9 -15
  300. package/dist/shims/web-vitals.js.map +1 -1
  301. package/dist/utils/base-path.d.ts +5 -2
  302. package/dist/utils/base-path.js +21 -19
  303. package/dist/utils/base-path.js.map +1 -1
  304. package/dist/utils/domain-locale.d.ts +17 -9
  305. package/dist/utils/domain-locale.js +36 -56
  306. package/dist/utils/domain-locale.js.map +1 -1
  307. package/dist/utils/hash.d.ts +4 -1
  308. package/dist/utils/hash.js +19 -17
  309. package/dist/utils/hash.js.map +1 -1
  310. package/dist/utils/manifest-paths.d.ts +6 -3
  311. package/dist/utils/manifest-paths.js +15 -16
  312. package/dist/utils/manifest-paths.js.map +1 -1
  313. package/dist/utils/project.d.ts +13 -11
  314. package/dist/utils/project.js +169 -216
  315. package/dist/utils/project.js.map +1 -1
  316. package/dist/utils/query.d.ts +8 -6
  317. package/dist/utils/query.js +57 -67
  318. package/dist/utils/query.js.map +1 -1
  319. package/package.json +10 -9
  320. package/dist/build/report.d.ts.map +0 -1
  321. package/dist/build/static-export.d.ts.map +0 -1
  322. package/dist/check.d.ts.map +0 -1
  323. package/dist/cli.d.ts.map +0 -1
  324. package/dist/client/entry.d.ts.map +0 -1
  325. package/dist/client/validate-module-path.d.ts.map +0 -1
  326. package/dist/client/vinext-next-data.d.ts.map +0 -1
  327. package/dist/client/vinext-next-data.js.map +0 -1
  328. package/dist/cloudflare/index.d.ts.map +0 -1
  329. package/dist/cloudflare/index.js.map +0 -1
  330. package/dist/cloudflare/kv-cache-handler.d.ts.map +0 -1
  331. package/dist/cloudflare/tpr.d.ts.map +0 -1
  332. package/dist/config/config-matchers.d.ts.map +0 -1
  333. package/dist/config/dotenv.d.ts.map +0 -1
  334. package/dist/config/next-config.d.ts.map +0 -1
  335. package/dist/deploy.d.ts.map +0 -1
  336. package/dist/entries/app-browser-entry.d.ts.map +0 -1
  337. package/dist/entries/app-rsc-entry.d.ts.map +0 -1
  338. package/dist/entries/app-ssr-entry.d.ts.map +0 -1
  339. package/dist/entries/pages-client-entry.d.ts.map +0 -1
  340. package/dist/entries/pages-entry-helpers.d.ts.map +0 -1
  341. package/dist/entries/pages-server-entry.d.ts.map +0 -1
  342. package/dist/index.d.ts.map +0 -1
  343. package/dist/init.d.ts.map +0 -1
  344. package/dist/plugins/async-hooks-stub.d.ts.map +0 -1
  345. package/dist/plugins/client-reference-dedup.d.ts.map +0 -1
  346. package/dist/routing/app-router.d.ts.map +0 -1
  347. package/dist/routing/file-matcher.d.ts.map +0 -1
  348. package/dist/routing/pages-router.d.ts.map +0 -1
  349. package/dist/routing/route-trie.d.ts.map +0 -1
  350. package/dist/routing/route-validation.d.ts.map +0 -1
  351. package/dist/routing/utils.d.ts.map +0 -1
  352. package/dist/server/api-handler.d.ts.map +0 -1
  353. package/dist/server/app-router-entry.d.ts.map +0 -1
  354. package/dist/server/dev-module-runner.d.ts.map +0 -1
  355. package/dist/server/dev-origin-check.d.ts.map +0 -1
  356. package/dist/server/dev-server.d.ts.map +0 -1
  357. package/dist/server/html.d.ts.map +0 -1
  358. package/dist/server/image-optimization.d.ts.map +0 -1
  359. package/dist/server/instrumentation.d.ts.map +0 -1
  360. package/dist/server/isr-cache.d.ts.map +0 -1
  361. package/dist/server/metadata-routes.d.ts.map +0 -1
  362. package/dist/server/middleware-codegen.d.ts.map +0 -1
  363. package/dist/server/middleware-request-headers.d.ts.map +0 -1
  364. package/dist/server/middleware.d.ts.map +0 -1
  365. package/dist/server/normalize-path.d.ts.map +0 -1
  366. package/dist/server/pages-i18n.d.ts.map +0 -1
  367. package/dist/server/prod-server.d.ts.map +0 -1
  368. package/dist/server/request-log.d.ts.map +0 -1
  369. package/dist/server/request-pipeline.d.ts.map +0 -1
  370. package/dist/server/worker-utils.d.ts.map +0 -1
  371. package/dist/shims/amp.d.ts.map +0 -1
  372. package/dist/shims/app.d.ts.map +0 -1
  373. package/dist/shims/app.js.map +0 -1
  374. package/dist/shims/cache-runtime.d.ts.map +0 -1
  375. package/dist/shims/cache.d.ts.map +0 -1
  376. package/dist/shims/client-only.d.ts.map +0 -1
  377. package/dist/shims/client-only.js.map +0 -1
  378. package/dist/shims/compat-router.d.ts.map +0 -1
  379. package/dist/shims/config.d.ts.map +0 -1
  380. package/dist/shims/constants.d.ts.map +0 -1
  381. package/dist/shims/document.d.ts.map +0 -1
  382. package/dist/shims/dynamic.d.ts.map +0 -1
  383. package/dist/shims/error-boundary.d.ts.map +0 -1
  384. package/dist/shims/error.d.ts.map +0 -1
  385. package/dist/shims/fetch-cache.d.ts.map +0 -1
  386. package/dist/shims/font-google-base.d.ts.map +0 -1
  387. package/dist/shims/font-google.d.ts.map +0 -1
  388. package/dist/shims/font-google.generated.d.ts.map +0 -1
  389. package/dist/shims/font-google.js.map +0 -1
  390. package/dist/shims/font-local.d.ts.map +0 -1
  391. package/dist/shims/form.d.ts.map +0 -1
  392. package/dist/shims/head-state.d.ts.map +0 -1
  393. package/dist/shims/head.d.ts.map +0 -1
  394. package/dist/shims/headers.d.ts.map +0 -1
  395. package/dist/shims/i18n-context.d.ts.map +0 -1
  396. package/dist/shims/i18n-state.d.ts.map +0 -1
  397. package/dist/shims/image-config.d.ts.map +0 -1
  398. package/dist/shims/image.d.ts.map +0 -1
  399. package/dist/shims/internal/api-utils.d.ts.map +0 -1
  400. package/dist/shims/internal/api-utils.js.map +0 -1
  401. package/dist/shims/internal/app-router-context.d.ts.map +0 -1
  402. package/dist/shims/internal/cookies.d.ts.map +0 -1
  403. package/dist/shims/internal/cookies.js.map +0 -1
  404. package/dist/shims/internal/parse-cookie-header.d.ts.map +0 -1
  405. package/dist/shims/internal/router-context.d.ts.map +0 -1
  406. package/dist/shims/internal/utils.d.ts.map +0 -1
  407. package/dist/shims/internal/work-unit-async-storage.d.ts.map +0 -1
  408. package/dist/shims/layout-segment-context.d.ts.map +0 -1
  409. package/dist/shims/legacy-image.d.ts.map +0 -1
  410. package/dist/shims/link.d.ts.map +0 -1
  411. package/dist/shims/metadata.d.ts.map +0 -1
  412. package/dist/shims/navigation-state.d.ts.map +0 -1
  413. package/dist/shims/navigation.d.ts.map +0 -1
  414. package/dist/shims/og.d.ts.map +0 -1
  415. package/dist/shims/og.js.map +0 -1
  416. package/dist/shims/readonly-url-search-params.d.ts.map +0 -1
  417. package/dist/shims/request-context.d.ts.map +0 -1
  418. package/dist/shims/request-state-types.d.ts.map +0 -1
  419. package/dist/shims/request-state-types.js.map +0 -1
  420. package/dist/shims/router-state.d.ts.map +0 -1
  421. package/dist/shims/router.d.ts.map +0 -1
  422. package/dist/shims/script.d.ts.map +0 -1
  423. package/dist/shims/server-only.d.ts.map +0 -1
  424. package/dist/shims/server-only.js.map +0 -1
  425. package/dist/shims/server.d.ts.map +0 -1
  426. package/dist/shims/unified-request-context.d.ts.map +0 -1
  427. package/dist/shims/url-safety.d.ts.map +0 -1
  428. package/dist/shims/url-utils.d.ts.map +0 -1
  429. package/dist/shims/web-vitals.d.ts.map +0 -1
  430. package/dist/utils/base-path.d.ts.map +0 -1
  431. package/dist/utils/domain-locale.d.ts.map +0 -1
  432. package/dist/utils/hash.d.ts.map +0 -1
  433. package/dist/utils/manifest-paths.d.ts.map +0 -1
  434. package/dist/utils/project.d.ts.map +0 -1
  435. package/dist/utils/query.d.ts.map +0 -1
@@ -1,87 +1,67 @@
1
- import { matchRoute, patternToNextFormat } from "../routing/pages-router.js";
2
- import { isrGet, isrSet, isrCacheKey, buildPagesCacheValue, triggerBackgroundRegeneration, setRevalidateDuration, getRevalidateDuration, } from "./isr-cache.js";
1
+ import { createValidFileMatcher } from "../routing/file-matcher.js";
2
+ import { patternToNextFormat } from "../routing/route-validation.js";
3
+ import { matchRoute } from "../routing/pages-router.js";
4
+ import { createRequestContext, runWithRequestContext } from "../shims/unified-request-context.js";
3
5
  import { _runWithCacheState } from "../shims/cache.js";
6
+ import { buildPagesCacheValue, getRevalidateDuration, isrCacheKey, isrGet, isrSet, setRevalidateDuration, triggerBackgroundRegeneration } from "./isr-cache.js";
4
7
  import { runWithPrivateCache } from "../shims/cache-runtime.js";
5
8
  import { ensureFetchPatch, runWithFetchCache } from "../shims/fetch-cache.js";
6
- import { createRequestContext, runWithRequestContext } from "../shims/unified-request-context.js";
7
- // Import server-only state modules to register ALS-backed accessors.
8
- // These modules must be imported before any rendering occurs.
9
+ import { parseQueryString } from "../utils/query.js";
9
10
  import "../shims/router-state.js";
10
11
  import { runWithHeadState } from "../shims/head-state.js";
11
12
  import { runWithServerInsertedHTMLState } from "../shims/navigation-state.js";
12
13
  import { reportRequestError } from "./instrumentation.js";
13
14
  import { safeJsonStringify } from "./html.js";
14
- import { parseQueryString as parseQuery } from "../utils/query.js";
15
- import path from "node:path";
15
+ import { logRequest, now } from "./request-log.js";
16
+ import { detectLocaleFromAcceptLanguage, extractLocaleFromUrl as extractLocaleFromUrl$1, parseCookieLocaleFromHeader, resolvePagesI18nRequest } from "./pages-i18n.js";
16
17
  import fs from "node:fs";
18
+ import path from "node:path";
17
19
  import React from "react";
18
20
  import { renderToReadableStream } from "react-dom/server.edge";
19
- import { logRequest, now } from "./request-log.js";
20
- import { createValidFileMatcher } from "../routing/file-matcher.js";
21
- import { extractLocaleFromUrl as extractLocaleFromUrlShared, detectLocaleFromAcceptLanguage, parseCookieLocaleFromHeader, resolvePagesI18nRequest, } from "./pages-i18n.js";
21
+ //#region src/server/dev-server.ts
22
22
  /**
23
- * Render a React element to a string using renderToReadableStream.
24
- *
25
- * Uses the edge-compatible Web Streams API. Waits for all Suspense
26
- * boundaries to resolve via stream.allReady before collecting output.
27
- * Used for _document rendering and error pages (small, non-streaming).
28
- */
23
+ * Render a React element to a string using renderToReadableStream.
24
+ *
25
+ * Uses the edge-compatible Web Streams API. Waits for all Suspense
26
+ * boundaries to resolve via stream.allReady before collecting output.
27
+ * Used for _document rendering and error pages (small, non-streaming).
28
+ */
29
29
  async function renderToStringAsync(element) {
30
- const stream = await renderToReadableStream(element);
31
- await stream.allReady;
32
- return new Response(stream).text();
30
+ const stream = await renderToReadableStream(element);
31
+ await stream.allReady;
32
+ return new Response(stream).text();
33
33
  }
34
34
  async function renderIsrPassToStringAsync(element) {
35
- // The cache-fill render is a second render pass for the same request.
36
- // Reset render-scoped state so it cannot leak from the streamed response
37
- // render or affect async work that is still draining from that stream.
38
- // Keep request identity state (pathname/query/locale/executionContext)
39
- // intact: this second pass still belongs to the same request.
40
- return await runWithServerInsertedHTMLState(() => runWithHeadState(() => _runWithCacheState(() => runWithPrivateCache(() => runWithFetchCache(async () => renderToStringAsync(element))))));
35
+ return await runWithServerInsertedHTMLState(() => runWithHeadState(() => _runWithCacheState(() => runWithPrivateCache(() => runWithFetchCache(async () => renderToStringAsync(element))))));
41
36
  }
42
37
  /** Body placeholder used to split the document shell for streaming. */
43
38
  const STREAM_BODY_MARKER = "<!--VINEXT_STREAM_BODY-->";
44
39
  /**
45
- * Stream a Pages Router page response using progressive SSR.
46
- *
47
- * Sends the HTML shell (head, layout, Suspense fallbacks) immediately
48
- * when the React shell is ready, then streams Suspense content as it
49
- * resolves. This gives the browser content to render while slow data
50
- * loads are still in flight.
51
- *
52
- * `__NEXT_DATA__` and the hydration script are appended after the body
53
- * stream completes (the data is known before rendering starts, but
54
- * deferring them reduces TTFB and lets the browser start parsing the
55
- * shell sooner).
56
- */
40
+ * Stream a Pages Router page response using progressive SSR.
41
+ *
42
+ * Sends the HTML shell (head, layout, Suspense fallbacks) immediately
43
+ * when the React shell is ready, then streams Suspense content as it
44
+ * resolves. This gives the browser content to render while slow data
45
+ * loads are still in flight.
46
+ *
47
+ * `__NEXT_DATA__` and the hydration script are appended after the body
48
+ * stream completes (the data is known before rendering starts, but
49
+ * deferring them reduces TTFB and lets the browser start parsing the
50
+ * shell sooner).
51
+ */
57
52
  async function streamPageToResponse(res, element, options) {
58
- const { url, server, fontHeadHTML, scripts, DocumentComponent, statusCode = 200, extraHeaders, getHeadHTML, } = options;
59
- // Start the React body stream FIRST — the promise resolves when the
60
- // shell is ready (synchronous content outside Suspense boundaries).
61
- // This triggers the render which populates <Head> tags.
62
- const bodyStream = await renderToReadableStream(element);
63
- // Now that the shell has rendered, collect head HTML
64
- const headHTML = getHeadHTML();
65
- // Build the document shell with a placeholder for the body
66
- let shellTemplate;
67
- if (DocumentComponent) {
68
- const docElement = React.createElement(DocumentComponent);
69
- let docHtml = await renderToStringAsync(docElement);
70
- // Replace __NEXT_MAIN__ with our stream marker
71
- docHtml = docHtml.replace("__NEXT_MAIN__", STREAM_BODY_MARKER);
72
- // Inject head tags
73
- if (headHTML || fontHeadHTML) {
74
- docHtml = docHtml.replace("</head>", ` ${fontHeadHTML}${headHTML}\n</head>`);
75
- }
76
- // Inject scripts: replace placeholder or append before </body>
77
- docHtml = docHtml.replace("<!-- __NEXT_SCRIPTS__ -->", scripts);
78
- if (!docHtml.includes("__NEXT_DATA__")) {
79
- docHtml = docHtml.replace("</body>", ` ${scripts}\n</body>`);
80
- }
81
- shellTemplate = docHtml;
82
- }
83
- else {
84
- shellTemplate = `<!DOCTYPE html>
53
+ const { url, server, fontHeadHTML, scripts, DocumentComponent, statusCode = 200, extraHeaders, getHeadHTML } = options;
54
+ const bodyStream = await renderToReadableStream(element);
55
+ const headHTML = getHeadHTML();
56
+ let shellTemplate;
57
+ if (DocumentComponent) {
58
+ let docHtml = await renderToStringAsync(React.createElement(DocumentComponent));
59
+ docHtml = docHtml.replace("__NEXT_MAIN__", STREAM_BODY_MARKER);
60
+ if (headHTML || fontHeadHTML) docHtml = docHtml.replace("</head>", ` ${fontHeadHTML}${headHTML}\n</head>`);
61
+ docHtml = docHtml.replace("<!-- __NEXT_SCRIPTS__ -->", scripts);
62
+ if (!docHtml.includes("__NEXT_DATA__")) docHtml = docHtml.replace("</body>", ` ${scripts}\n</body>`);
63
+ shellTemplate = docHtml;
64
+ } else shellTemplate = `<!DOCTYPE html>
85
65
  <html>
86
66
  <head>
87
67
  <meta charset="utf-8" />
@@ -93,596 +73,384 @@ async function streamPageToResponse(res, element, options) {
93
73
  ${scripts}
94
74
  </body>
95
75
  </html>`;
96
- }
97
- // Apply Vite's HTML transforms (injects HMR client, etc.) on the full
98
- // shell template, then split at the body marker.
99
- const transformedShell = await server.transformIndexHtml(url, shellTemplate);
100
- const markerIdx = transformedShell.indexOf(STREAM_BODY_MARKER);
101
- const prefix = transformedShell.slice(0, markerIdx);
102
- const suffix = transformedShell.slice(markerIdx + STREAM_BODY_MARKER.length);
103
- // Send headers and start streaming.
104
- // Set array-valued headers (e.g. Set-Cookie from gSSP) via setHeader()
105
- // before writeHead(), since writeHead()'s headers object doesn't handle
106
- // arrays portably. Then writeHead() merges with any setHeader() calls.
107
- const headers = {
108
- "Content-Type": "text/html",
109
- "Transfer-Encoding": "chunked",
110
- };
111
- if (extraHeaders) {
112
- for (const [key, val] of Object.entries(extraHeaders)) {
113
- if (Array.isArray(val)) {
114
- res.setHeader(key, val);
115
- }
116
- else {
117
- headers[key] = val;
118
- }
119
- }
120
- }
121
- res.writeHead(statusCode, headers);
122
- // Write the document prefix (head, opening body)
123
- res.write(prefix);
124
- // Pipe the React body stream through (Suspense content streams progressively)
125
- const reader = bodyStream.getReader();
126
- try {
127
- for (;;) {
128
- const { done, value } = await reader.read();
129
- if (done)
130
- break;
131
- res.write(value);
132
- }
133
- }
134
- finally {
135
- reader.releaseLock();
136
- }
137
- // Write the document suffix (closing tags, scripts)
138
- res.end(suffix);
76
+ const transformedShell = await server.transformIndexHtml(url, shellTemplate);
77
+ const markerIdx = transformedShell.indexOf(STREAM_BODY_MARKER);
78
+ const prefix = transformedShell.slice(0, markerIdx);
79
+ const suffix = transformedShell.slice(markerIdx + 25);
80
+ const headers = {
81
+ "Content-Type": "text/html",
82
+ "Transfer-Encoding": "chunked"
83
+ };
84
+ if (extraHeaders) for (const [key, val] of Object.entries(extraHeaders)) if (Array.isArray(val)) res.setHeader(key, val);
85
+ else headers[key] = val;
86
+ res.writeHead(statusCode, headers);
87
+ res.write(prefix);
88
+ const reader = bodyStream.getReader();
89
+ try {
90
+ for (;;) {
91
+ const { done, value } = await reader.read();
92
+ if (done) break;
93
+ res.write(value);
94
+ }
95
+ } finally {
96
+ reader.releaseLock();
97
+ }
98
+ res.end(suffix);
139
99
  }
140
100
  /** Check if a file exists with any configured page extension. */
141
101
  function findFileWithExtensions(basePath, matcher) {
142
- return matcher.dottedExtensions.some((ext) => fs.existsSync(basePath + ext));
102
+ return matcher.dottedExtensions.some((ext) => fs.existsSync(basePath + ext));
143
103
  }
144
104
  /**
145
- * Extract locale prefix from a URL path.
146
- * e.g. /fr/about -> { locale: "fr", url: "/about", hadPrefix: true }
147
- * /about -> { locale: "en", url: "/about", hadPrefix: false } (defaultLocale)
148
- */
149
- export function extractLocaleFromUrl(url, i18nConfig) {
150
- return extractLocaleFromUrlShared(url, i18nConfig);
105
+ * Extract locale prefix from a URL path.
106
+ * e.g. /fr/about -> { locale: "fr", url: "/about", hadPrefix: true }
107
+ * /about -> { locale: "en", url: "/about", hadPrefix: false } (defaultLocale)
108
+ */
109
+ function extractLocaleFromUrl(url, i18nConfig) {
110
+ return extractLocaleFromUrl$1(url, i18nConfig);
151
111
  }
152
112
  /**
153
- * Detect the preferred locale from the Accept-Language header.
154
- * Returns the best matching locale or null.
155
- */
156
- export function detectLocaleFromHeaders(req, i18nConfig) {
157
- return detectLocaleFromAcceptLanguage(req.headers["accept-language"], i18nConfig);
113
+ * Detect the preferred locale from the Accept-Language header.
114
+ * Returns the best matching locale or null.
115
+ */
116
+ function detectLocaleFromHeaders(req, i18nConfig) {
117
+ return detectLocaleFromAcceptLanguage(req.headers["accept-language"], i18nConfig);
158
118
  }
159
119
  /**
160
- * Parse the NEXT_LOCALE cookie from a request.
161
- * Returns the cookie value if it matches a configured locale, otherwise null.
162
- */
163
- export function parseCookieLocale(req, i18nConfig) {
164
- return parseCookieLocaleFromHeader(req.headers.cookie, i18nConfig);
120
+ * Parse the NEXT_LOCALE cookie from a request.
121
+ * Returns the cookie value if it matches a configured locale, otherwise null.
122
+ */
123
+ function parseCookieLocale(req, i18nConfig) {
124
+ return parseCookieLocaleFromHeader(req.headers.cookie, i18nConfig);
165
125
  }
166
126
  /**
167
- * Create an SSR request handler for the Pages Router.
168
- *
169
- * For each request:
170
- * 1. Match the URL against discovered routes
171
- * 2. Load the page module via Vite's SSR module loader
172
- * 3. Call getServerSideProps/getStaticProps if present
173
- * 4. Render the component to HTML
174
- * 5. Wrap in _document shell and send response
175
- */
176
- export function createSSRHandler(server, routes, pagesDir, i18nConfig, fileMatcher, basePath = "", trailingSlash = false) {
177
- const matcher = fileMatcher ?? createValidFileMatcher();
178
- return async (req, res, url,
179
- /** Status code override — propagated from middleware rewrite status. */
180
- statusCode) => {
181
- const _reqStart = now();
182
- let _compileEnd;
183
- let _renderEnd;
184
- res.on("finish", () => {
185
- const totalMs = now() - _reqStart;
186
- const compileMs = _compileEnd !== undefined ? Math.round(_compileEnd - _reqStart) : undefined;
187
- // renderMs = time from end of compile to end of stream.
188
- // _renderEnd is set just after streamPageToResponse resolves.
189
- const renderMs = _renderEnd !== undefined && _compileEnd !== undefined
190
- ? Math.round(_renderEnd - _compileEnd)
191
- : undefined;
192
- logRequest({
193
- method: req.method ?? "GET",
194
- url,
195
- status: res.statusCode,
196
- totalMs,
197
- compileMs,
198
- renderMs,
199
- });
200
- });
201
- // --- i18n: extract locale from URL prefix ---
202
- let locale;
203
- let localeStrippedUrl = url;
204
- let currentDefaultLocale;
205
- const domainLocales = i18nConfig?.domains;
206
- if (i18nConfig) {
207
- const resolved = resolvePagesI18nRequest(url, i18nConfig, req.headers, req.headers.host, basePath, trailingSlash);
208
- locale = resolved.locale;
209
- localeStrippedUrl = resolved.url;
210
- currentDefaultLocale = resolved.domainLocale?.defaultLocale ?? i18nConfig.defaultLocale;
211
- if (resolved.redirectUrl) {
212
- res.writeHead(307, { Location: resolved.redirectUrl });
213
- res.end();
214
- return;
215
- }
216
- }
217
- const match = matchRoute(localeStrippedUrl, routes);
218
- if (!match) {
219
- // No route matched — try to render custom 404 page
220
- await renderErrorPage(server, req, res, url, pagesDir, 404, undefined, matcher);
221
- return;
222
- }
223
- const { route, params } = match;
224
- // Wrap the entire request in a single unified AsyncLocalStorage scope.
225
- const requestContext = createRequestContext();
226
- return runWithRequestContext(requestContext, async () => {
227
- ensureFetchPatch();
228
- try {
229
- // Set SSR context for the router shim so useRouter() returns
230
- // the correct URL and params during server-side rendering.
231
- const routerShim = await server.ssrLoadModule("next/router");
232
- if (typeof routerShim.setSSRContext === "function") {
233
- routerShim.setSSRContext({
234
- pathname: patternToNextFormat(route.pattern),
235
- query: { ...params, ...parseQuery(url) },
236
- asPath: url,
237
- locale: locale ?? currentDefaultLocale,
238
- locales: i18nConfig?.locales,
239
- defaultLocale: currentDefaultLocale,
240
- domainLocales,
241
- });
242
- }
243
- // Set per-request i18n context for Link component locale
244
- // prop support during SSR. Use ssrLoadModule to set it on
245
- // the SSR environment's module instance (same pattern as
246
- // setSSRContext above).
247
- if (i18nConfig) {
248
- // Register ALS-backed i18n accessors in the SSR module graph so
249
- // next/link and other SSR imports read from the unified store.
250
- await server.ssrLoadModule("vinext/i18n-state");
251
- const i18nCtx = await server.ssrLoadModule("vinext/i18n-context");
252
- if (typeof i18nCtx.setI18nContext === "function") {
253
- i18nCtx.setI18nContext({
254
- locale: locale ?? currentDefaultLocale,
255
- locales: i18nConfig.locales,
256
- defaultLocale: currentDefaultLocale,
257
- domainLocales,
258
- hostname: req.headers.host?.split(":", 1)[0],
259
- });
260
- }
261
- }
262
- // Load the page module through Vite's SSR pipeline
263
- // This gives us HMR and transform support for free
264
- const pageModule = await server.ssrLoadModule(route.filePath);
265
- // Mark end of compile phase: everything from here is rendering.
266
- _compileEnd = now();
267
- // Get the page component (default export)
268
- const PageComponent = pageModule.default;
269
- if (!PageComponent) {
270
- console.error(`[vinext] Page ${route.filePath} has no default export`);
271
- res.statusCode = 500;
272
- res.end("Page has no default export");
273
- return;
274
- }
275
- // Collect page props via data fetching methods
276
- let pageProps = {};
277
- let isrRevalidateSeconds = null;
278
- // Handle getStaticPaths for dynamic routes: validate the path
279
- // and respect fallback: false (return 404 for unlisted paths).
280
- if (typeof pageModule.getStaticPaths === "function" && route.isDynamic) {
281
- const pathsResult = await pageModule.getStaticPaths({
282
- locales: i18nConfig?.locales ?? [],
283
- defaultLocale: currentDefaultLocale ?? "",
284
- });
285
- const fallback = pathsResult?.fallback ?? false;
286
- if (fallback === false) {
287
- // Only allow paths explicitly listed in getStaticPaths
288
- const paths = pathsResult?.paths ?? [];
289
- const isValidPath = paths.some((p) => {
290
- return Object.entries(p.params).every(([key, val]) => {
291
- const actual = params[key];
292
- if (Array.isArray(val)) {
293
- return Array.isArray(actual) && val.join("/") === actual.join("/");
294
- }
295
- return String(val) === String(actual);
296
- });
297
- });
298
- if (!isValidPath) {
299
- await renderErrorPage(server, req, res, url, pagesDir, 404, routerShim.wrapWithRouterContext, matcher);
300
- return;
301
- }
302
- }
303
- // fallback: true or "blocking" — always SSR on-demand.
304
- // In dev mode, Next.js does the same (no fallback shell).
305
- // In production, both modes SSR on-demand with caching.
306
- // The difference is that fallback:true could serve a shell first,
307
- // but since we always have data available via SSR, we render fully.
308
- }
309
- // Headers set by getServerSideProps for explicit forwarding to
310
- // streamPageToResponse. Without this, they survive only through
311
- // Node.js writeHead() implicitly merging setHeader() calls, which
312
- // would silently break if streamPageToResponse is refactored.
313
- const gsspExtraHeaders = {};
314
- if (typeof pageModule.getServerSideProps === "function") {
315
- // Snapshot existing headers so we can detect what gSSP adds.
316
- const headersBeforeGSSP = new Set(Object.keys(res.getHeaders()));
317
- const context = {
318
- params,
319
- req,
320
- res,
321
- query: parseQuery(url),
322
- resolvedUrl: localeStrippedUrl,
323
- locale: locale ?? currentDefaultLocale,
324
- locales: i18nConfig?.locales,
325
- defaultLocale: currentDefaultLocale,
326
- };
327
- const result = await pageModule.getServerSideProps(context);
328
- // If gSSP called res.end() directly (short-circuit pattern),
329
- // the response is already sent. Do not continue rendering.
330
- // Note: middleware headers are already on `res` (middleware runs
331
- // before this handler in the connect chain), so they are included
332
- // in the short-circuited response. The prod path achieves the same
333
- // result via the worker entry merging middleware headers after
334
- // renderPage() returns.
335
- if (res.writableEnded) {
336
- return;
337
- }
338
- if (result && "props" in result) {
339
- pageProps = result.props;
340
- }
341
- if (result && "redirect" in result) {
342
- const { redirect } = result;
343
- const status = redirect.statusCode ?? (redirect.permanent ? 308 : 307);
344
- // Sanitize destination to prevent open redirect via protocol-relative URLs.
345
- // Also normalize backslashes — browsers treat \ as / in URL contexts.
346
- let dest = redirect.destination;
347
- if (!dest.startsWith("http://") && !dest.startsWith("https://")) {
348
- dest = dest.replace(/^[\\/]+/, "/");
349
- }
350
- res.writeHead(status, {
351
- Location: dest,
352
- });
353
- res.end();
354
- return;
355
- }
356
- if (result && "notFound" in result && result.notFound) {
357
- await renderErrorPage(server, req, res, url, pagesDir, 404, routerShim.wrapWithRouterContext);
358
- return;
359
- }
360
- // Preserve any status code set by gSSP (e.g. res.statusCode = 201).
361
- // This takes precedence over the default 200 but not over middleware status.
362
- if (!statusCode && res.statusCode !== 200) {
363
- statusCode = res.statusCode;
364
- }
365
- // Capture headers newly set by gSSP and forward them explicitly.
366
- // Remove from `res` to prevent duplication when writeHead() merges.
367
- const headersAfterGSSP = res.getHeaders();
368
- for (const [key, val] of Object.entries(headersAfterGSSP)) {
369
- if (headersBeforeGSSP.has(key) || val == null)
370
- continue;
371
- res.removeHeader(key);
372
- if (Array.isArray(val)) {
373
- gsspExtraHeaders[key] = val.map(String);
374
- }
375
- else {
376
- gsspExtraHeaders[key] = String(val);
377
- }
378
- }
379
- }
380
- // Collect font preloads early so ISR cached responses can include
381
- // the Link header (font preloads are module-level state that persists
382
- // across requests after the font modules are first loaded).
383
- let earlyFontLinkHeader = "";
384
- try {
385
- const earlyPreloads = [];
386
- const fontGoogleEarly = await server.ssrLoadModule("next/font/google");
387
- if (typeof fontGoogleEarly.getSSRFontPreloads === "function") {
388
- earlyPreloads.push(...fontGoogleEarly.getSSRFontPreloads());
389
- }
390
- const fontLocalEarly = await server.ssrLoadModule("next/font/local");
391
- if (typeof fontLocalEarly.getSSRFontPreloads === "function") {
392
- earlyPreloads.push(...fontLocalEarly.getSSRFontPreloads());
393
- }
394
- if (earlyPreloads.length > 0) {
395
- earlyFontLinkHeader = earlyPreloads
396
- .map((p) => `<${p.href}>; rel=preload; as=font; type=${p.type}; crossorigin`)
397
- .join(", ");
398
- }
399
- }
400
- catch {
401
- // Font modules not loaded yet — skip
402
- }
403
- if (typeof pageModule.getStaticProps === "function") {
404
- // Check ISR cache before calling getStaticProps
405
- const cacheKey = isrCacheKey("pages", url.split("?")[0],
406
- // __VINEXT_BUILD_ID is a compile-time define undefined in dev,
407
- // which is fine: dev doesn't need cross-deploy cache isolation.
408
- process.env.__VINEXT_BUILD_ID);
409
- const cached = await isrGet(cacheKey);
410
- if (cached && !cached.isStale && cached.value.value?.kind === "PAGES") {
411
- // Fresh cache hit — serve directly
412
- const cachedPage = cached.value.value;
413
- const cachedHtml = cachedPage.html;
414
- const transformedHtml = await server.transformIndexHtml(url, cachedHtml);
415
- const revalidateSecs = getRevalidateDuration(cacheKey) ?? 60;
416
- const hitHeaders = {
417
- "Content-Type": "text/html",
418
- "X-Vinext-Cache": "HIT",
419
- "Cache-Control": `s-maxage=${revalidateSecs}, stale-while-revalidate`,
420
- };
421
- if (earlyFontLinkHeader)
422
- hitHeaders["Link"] = earlyFontLinkHeader;
423
- res.writeHead(200, hitHeaders);
424
- res.end(transformedHtml);
425
- return;
426
- }
427
- if (cached && cached.isStale && cached.value.value?.kind === "PAGES") {
428
- // Stale hit — serve stale immediately, trigger background regen
429
- const cachedPage = cached.value.value;
430
- const cachedHtml = cachedPage.html;
431
- const transformedHtml = await server.transformIndexHtml(url, cachedHtml);
432
- // Trigger background regeneration: re-run getStaticProps,
433
- // re-render the page, and cache the fresh HTML.
434
- triggerBackgroundRegeneration(cacheKey, async () => {
435
- const regenContext = createRequestContext({
436
- // Dev never has a Workers ExecutionContext. Set it
437
- // explicitly so background regeneration cannot inherit
438
- // a standalone execution-context scope from the caller.
439
- executionContext: null,
440
- });
441
- return runWithRequestContext(regenContext, async () => {
442
- ensureFetchPatch();
443
- const freshResult = await pageModule.getStaticProps({
444
- params,
445
- locale: locale ?? currentDefaultLocale,
446
- locales: i18nConfig?.locales,
447
- defaultLocale: currentDefaultLocale,
448
- });
449
- if (freshResult && "props" in freshResult) {
450
- const revalidate = typeof freshResult.revalidate === "number" ? freshResult.revalidate : 0;
451
- if (revalidate > 0) {
452
- const freshProps = freshResult.props;
453
- if (typeof routerShim.setSSRContext === "function") {
454
- routerShim.setSSRContext({
455
- pathname: patternToNextFormat(route.pattern),
456
- query: { ...params, ...parseQuery(url) },
457
- asPath: url,
458
- locale: locale ?? currentDefaultLocale,
459
- locales: i18nConfig?.locales,
460
- defaultLocale: currentDefaultLocale,
461
- domainLocales,
462
- });
463
- }
464
- if (i18nConfig) {
465
- await server.ssrLoadModule("vinext/i18n-state");
466
- const i18nCtx = await server.ssrLoadModule("vinext/i18n-context");
467
- if (typeof i18nCtx.setI18nContext === "function") {
468
- i18nCtx.setI18nContext({
469
- locale: locale ?? currentDefaultLocale,
470
- locales: i18nConfig.locales,
471
- defaultLocale: currentDefaultLocale,
472
- domainLocales,
473
- hostname: req.headers.host?.split(":", 1)[0],
474
- });
475
- }
476
- }
477
- // Re-render the page with fresh props inside fresh
478
- // render sub-scopes so head/cache state cannot leak.
479
- let RegenApp = null;
480
- const appPath = path.join(pagesDir, "_app");
481
- if (findFileWithExtensions(appPath, matcher)) {
482
- try {
483
- const appMod = await server.ssrLoadModule(appPath);
484
- RegenApp = appMod.default ?? null;
485
- }
486
- catch {
487
- // _app failed to load
488
- }
489
- }
490
- let el = RegenApp
491
- ? React.createElement(RegenApp, {
492
- Component: pageModule.default,
493
- pageProps: freshProps,
494
- })
495
- : React.createElement(pageModule.default, freshProps);
496
- if (routerShim.wrapWithRouterContext) {
497
- el = routerShim.wrapWithRouterContext(el);
498
- }
499
- const freshBody = await renderIsrPassToStringAsync(el);
500
- // Rebuild __NEXT_DATA__ with fresh props. The hydration
501
- // script (module URLs) is stable across regenerations —
502
- // extract it from the cached HTML to avoid duplication.
503
- const viteRoot = server.config?.root;
504
- const regenPageUrl = viteRoot
505
- ? "/" + path.relative(viteRoot, route.filePath)
506
- : route.filePath;
507
- const regenAppUrl = RegenApp
508
- ? viteRoot
509
- ? "/" + path.relative(viteRoot, path.join(pagesDir, "_app"))
510
- : path.join(pagesDir, "_app")
511
- : null;
512
- const freshNextData = `<script>window.__NEXT_DATA__ = ${safeJsonStringify({
513
- props: { pageProps: freshProps },
514
- page: patternToNextFormat(route.pattern),
515
- query: params,
516
- buildId: process.env.__VINEXT_BUILD_ID,
517
- isFallback: false,
518
- locale: locale ?? currentDefaultLocale,
519
- locales: i18nConfig?.locales,
520
- defaultLocale: currentDefaultLocale,
521
- domainLocales,
522
- __vinext: {
523
- pageModuleUrl: regenPageUrl,
524
- appModuleUrl: regenAppUrl,
525
- },
526
- })}${i18nConfig ? `;window.__VINEXT_LOCALE__=${safeJsonStringify(locale ?? currentDefaultLocale)};window.__VINEXT_LOCALES__=${safeJsonStringify(i18nConfig.locales)};window.__VINEXT_DEFAULT_LOCALE__=${safeJsonStringify(currentDefaultLocale)}` : ""}</script>`;
527
- const hydrationMatch = cachedHtml.match(/<script type="module">[\s\S]*?<\/script>/);
528
- const hydrationScript = hydrationMatch?.[0] ?? "";
529
- const freshHtml = `<!DOCTYPE html><html><head></head><body><div id="__next">${freshBody}</div>${freshNextData}\n ${hydrationScript}</body></html>`;
530
- await isrSet(cacheKey, buildPagesCacheValue(freshHtml, freshProps), revalidate);
531
- setRevalidateDuration(cacheKey, revalidate);
532
- }
533
- }
534
- });
535
- });
536
- const revalidateSecs = getRevalidateDuration(cacheKey) ?? 60;
537
- const staleHeaders = {
538
- "Content-Type": "text/html",
539
- "X-Vinext-Cache": "STALE",
540
- "Cache-Control": `s-maxage=${revalidateSecs}, stale-while-revalidate`,
541
- };
542
- if (earlyFontLinkHeader)
543
- staleHeaders["Link"] = earlyFontLinkHeader;
544
- res.writeHead(200, staleHeaders);
545
- res.end(transformedHtml);
546
- return;
547
- }
548
- // Cache miss — call getStaticProps normally
549
- const context = {
550
- params,
551
- locale: locale ?? currentDefaultLocale,
552
- locales: i18nConfig?.locales,
553
- defaultLocale: currentDefaultLocale,
554
- };
555
- const result = await pageModule.getStaticProps(context);
556
- if (result && "props" in result) {
557
- pageProps = result.props;
558
- }
559
- if (result && "redirect" in result) {
560
- const { redirect } = result;
561
- const status = redirect.statusCode ?? (redirect.permanent ? 308 : 307);
562
- // Sanitize destination to prevent open redirect via protocol-relative URLs.
563
- // Also normalize backslashes — browsers treat \ as / in URL contexts.
564
- let dest = redirect.destination;
565
- if (!dest.startsWith("http://") && !dest.startsWith("https://")) {
566
- dest = dest.replace(/^[\\/]+/, "/");
567
- }
568
- res.writeHead(status, {
569
- Location: dest,
570
- });
571
- res.end();
572
- return;
573
- }
574
- if (result && "notFound" in result && result.notFound) {
575
- await renderErrorPage(server, req, res, url, pagesDir, 404, routerShim.wrapWithRouterContext);
576
- return;
577
- }
578
- // Extract revalidate period for ISR caching after render
579
- if (typeof result?.revalidate === "number" && result.revalidate > 0) {
580
- isrRevalidateSeconds = result.revalidate;
581
- }
582
- }
583
- // Try to load _app.tsx if it exists
584
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
585
- let AppComponent = null;
586
- const appPath = path.join(pagesDir, "_app");
587
- if (findFileWithExtensions(appPath, matcher)) {
588
- try {
589
- const appModule = await server.ssrLoadModule(appPath);
590
- AppComponent = appModule.default ?? null;
591
- }
592
- catch {
593
- // _app exists but failed to load
594
- }
595
- }
596
- // React and ReactDOMServer are imported at the top level as native Node
597
- // modules. They must NOT go through Vite's SSR module runner because
598
- // React is CJS and the ESModulesEvaluator doesn't define `module`.
599
- const createElement = React.createElement;
600
- let element;
601
- // wrapWithRouterContext wraps the element in RouterContext.Provider so that
602
- // next/compat/router's useRouter() returns the real router.
603
- const wrapWithRouterContext = routerShim.wrapWithRouterContext;
604
- if (AppComponent) {
605
- element = createElement(AppComponent, {
606
- Component: PageComponent,
607
- pageProps,
608
- });
609
- }
610
- else {
611
- element = createElement(PageComponent, pageProps);
612
- }
613
- if (wrapWithRouterContext) {
614
- element = wrapWithRouterContext(element);
615
- }
616
- // Reset SSR head collector before rendering so <Head> tags are captured
617
- const headShim = await server.ssrLoadModule("next/head");
618
- if (typeof headShim.resetSSRHead === "function") {
619
- headShim.resetSSRHead();
620
- }
621
- // Flush any pending dynamic() preloads so components are ready
622
- const dynamicShim = await server.ssrLoadModule("next/dynamic");
623
- if (typeof dynamicShim.flushPreloads === "function") {
624
- await dynamicShim.flushPreloads();
625
- }
626
- // Collect any <Head> tags that were rendered during data fetching
627
- // (shell head tags — Suspense children's head tags arrive late,
628
- // matching Next.js behavior)
629
- // Collect SSR font links (Google Fonts <link> tags) and font class styles
630
- let fontHeadHTML = "";
631
- const allFontStyles = [];
632
- const allFontPreloads = [];
633
- try {
634
- const fontGoogle = await server.ssrLoadModule("next/font/google");
635
- if (typeof fontGoogle.getSSRFontLinks === "function") {
636
- const fontUrls = fontGoogle.getSSRFontLinks();
637
- for (const fontUrl of fontUrls) {
638
- const safeFontUrl = fontUrl.replace(/&/g, "&amp;").replace(/"/g, "&quot;");
639
- fontHeadHTML += `<link rel="stylesheet" href="${safeFontUrl}" />\n `;
640
- }
641
- }
642
- if (typeof fontGoogle.getSSRFontStyles === "function") {
643
- allFontStyles.push(...fontGoogle.getSSRFontStyles());
644
- }
645
- // Collect preloads from self-hosted Google fonts
646
- if (typeof fontGoogle.getSSRFontPreloads === "function") {
647
- allFontPreloads.push(...fontGoogle.getSSRFontPreloads());
648
- }
649
- }
650
- catch {
651
- // next/font/google not used — skip
652
- }
653
- try {
654
- const fontLocal = await server.ssrLoadModule("next/font/local");
655
- if (typeof fontLocal.getSSRFontStyles === "function") {
656
- allFontStyles.push(...fontLocal.getSSRFontStyles());
657
- }
658
- // Collect preloads from local font files
659
- if (typeof fontLocal.getSSRFontPreloads === "function") {
660
- allFontPreloads.push(...fontLocal.getSSRFontPreloads());
661
- }
662
- }
663
- catch {
664
- // next/font/local not used — skip
665
- }
666
- // Emit <link rel="preload"> for all collected font files (Google + local)
667
- for (const { href, type } of allFontPreloads) {
668
- // Escape href/type to prevent HTML attribute injection (defense-in-depth;
669
- // Vite-resolved asset paths should never contain special chars).
670
- const safeHref = href.replace(/&/g, "&amp;").replace(/"/g, "&quot;");
671
- const safeType = type.replace(/&/g, "&amp;").replace(/"/g, "&quot;");
672
- fontHeadHTML += `<link rel="preload" href="${safeHref}" as="font" type="${safeType}" crossorigin />\n `;
673
- }
674
- if (allFontStyles.length > 0) {
675
- fontHeadHTML += `<style data-vinext-fonts>${allFontStyles.join("\n")}</style>\n `;
676
- }
677
- // Convert absolute file paths to Vite-servable URLs (relative to root)
678
- const viteRoot = server.config.root;
679
- const pageModuleUrl = "/" + path.relative(viteRoot, route.filePath);
680
- const appModuleUrl = AppComponent
681
- ? "/" + path.relative(viteRoot, path.join(pagesDir, "_app"))
682
- : null;
683
- // Hydration entry: inline script that imports the page and hydrates.
684
- // Stores the React root and page loader for client-side navigation.
685
- const hydrationScript = `
127
+ * Create an SSR request handler for the Pages Router.
128
+ *
129
+ * For each request:
130
+ * 1. Match the URL against discovered routes
131
+ * 2. Load the page module via Vite's SSR module loader
132
+ * 3. Call getServerSideProps/getStaticProps if present
133
+ * 4. Render the component to HTML
134
+ * 5. Wrap in _document shell and send response
135
+ */
136
+ function createSSRHandler(server, routes, pagesDir, i18nConfig, fileMatcher, basePath = "", trailingSlash = false) {
137
+ const matcher = fileMatcher ?? createValidFileMatcher();
138
+ const _alsRegistration = Promise.all([server.ssrLoadModule("vinext/head-state"), server.ssrLoadModule("vinext/router-state")]);
139
+ _alsRegistration.catch(() => {});
140
+ return async (req, res, url, statusCode) => {
141
+ const _reqStart = now();
142
+ let _compileEnd;
143
+ let _renderEnd;
144
+ res.on("finish", () => {
145
+ const totalMs = now() - _reqStart;
146
+ const compileMs = _compileEnd !== void 0 ? Math.round(_compileEnd - _reqStart) : void 0;
147
+ const renderMs = _renderEnd !== void 0 && _compileEnd !== void 0 ? Math.round(_renderEnd - _compileEnd) : void 0;
148
+ logRequest({
149
+ method: req.method ?? "GET",
150
+ url,
151
+ status: res.statusCode,
152
+ totalMs,
153
+ compileMs,
154
+ renderMs
155
+ });
156
+ });
157
+ let locale;
158
+ let localeStrippedUrl = url;
159
+ let currentDefaultLocale;
160
+ const domainLocales = i18nConfig?.domains;
161
+ if (i18nConfig) {
162
+ const resolved = resolvePagesI18nRequest(url, i18nConfig, req.headers, req.headers.host, basePath, trailingSlash);
163
+ locale = resolved.locale;
164
+ localeStrippedUrl = resolved.url;
165
+ currentDefaultLocale = resolved.domainLocale?.defaultLocale ?? i18nConfig.defaultLocale;
166
+ if (resolved.redirectUrl) {
167
+ res.writeHead(307, { Location: resolved.redirectUrl });
168
+ res.end();
169
+ return;
170
+ }
171
+ }
172
+ const match = matchRoute(localeStrippedUrl, routes);
173
+ if (!match) {
174
+ await renderErrorPage(server, req, res, url, pagesDir, 404, void 0, matcher);
175
+ return;
176
+ }
177
+ const { route, params } = match;
178
+ return runWithRequestContext(createRequestContext(), async () => {
179
+ ensureFetchPatch();
180
+ try {
181
+ await _alsRegistration;
182
+ const routerShim = await server.ssrLoadModule("next/router");
183
+ if (typeof routerShim.setSSRContext === "function") routerShim.setSSRContext({
184
+ pathname: patternToNextFormat(route.pattern),
185
+ query: {
186
+ ...params,
187
+ ...parseQueryString(url)
188
+ },
189
+ asPath: url,
190
+ locale: locale ?? currentDefaultLocale,
191
+ locales: i18nConfig?.locales,
192
+ defaultLocale: currentDefaultLocale,
193
+ domainLocales
194
+ });
195
+ if (i18nConfig) {
196
+ await server.ssrLoadModule("vinext/i18n-state");
197
+ const i18nCtx = await server.ssrLoadModule("vinext/i18n-context");
198
+ if (typeof i18nCtx.setI18nContext === "function") i18nCtx.setI18nContext({
199
+ locale: locale ?? currentDefaultLocale,
200
+ locales: i18nConfig.locales,
201
+ defaultLocale: currentDefaultLocale,
202
+ domainLocales,
203
+ hostname: req.headers.host?.split(":", 1)[0]
204
+ });
205
+ }
206
+ const pageModule = await server.ssrLoadModule(route.filePath);
207
+ _compileEnd = now();
208
+ const PageComponent = pageModule.default;
209
+ if (!PageComponent) {
210
+ console.error(`[vinext] Page ${route.filePath} has no default export`);
211
+ res.statusCode = 500;
212
+ res.end("Page has no default export");
213
+ return;
214
+ }
215
+ let pageProps = {};
216
+ let isrRevalidateSeconds = null;
217
+ if (typeof pageModule.getStaticPaths === "function" && route.isDynamic) {
218
+ const pathsResult = await pageModule.getStaticPaths({
219
+ locales: i18nConfig?.locales ?? [],
220
+ defaultLocale: currentDefaultLocale ?? ""
221
+ });
222
+ if ((pathsResult?.fallback ?? false) === false) {
223
+ if (!(pathsResult?.paths ?? []).some((p) => {
224
+ return Object.entries(p.params).every(([key, val]) => {
225
+ const actual = params[key];
226
+ if (Array.isArray(val)) return Array.isArray(actual) && val.join("/") === actual.join("/");
227
+ return String(val) === String(actual);
228
+ });
229
+ })) {
230
+ await renderErrorPage(server, req, res, url, pagesDir, 404, routerShim.wrapWithRouterContext, matcher);
231
+ return;
232
+ }
233
+ }
234
+ }
235
+ const gsspExtraHeaders = {};
236
+ if (typeof pageModule.getServerSideProps === "function") {
237
+ const headersBeforeGSSP = new Set(Object.keys(res.getHeaders()));
238
+ const context = {
239
+ params,
240
+ req,
241
+ res,
242
+ query: parseQueryString(url),
243
+ resolvedUrl: localeStrippedUrl,
244
+ locale: locale ?? currentDefaultLocale,
245
+ locales: i18nConfig?.locales,
246
+ defaultLocale: currentDefaultLocale
247
+ };
248
+ const result = await pageModule.getServerSideProps(context);
249
+ if (res.writableEnded) return;
250
+ if (result && "props" in result) pageProps = result.props;
251
+ if (result && "redirect" in result) {
252
+ const { redirect } = result;
253
+ const status = redirect.statusCode ?? (redirect.permanent ? 308 : 307);
254
+ let dest = redirect.destination;
255
+ if (!dest.startsWith("http://") && !dest.startsWith("https://")) dest = dest.replace(/^[\\/]+/, "/");
256
+ res.writeHead(status, { Location: dest });
257
+ res.end();
258
+ return;
259
+ }
260
+ if (result && "notFound" in result && result.notFound) {
261
+ await renderErrorPage(server, req, res, url, pagesDir, 404, routerShim.wrapWithRouterContext);
262
+ return;
263
+ }
264
+ if (!statusCode && res.statusCode !== 200) statusCode = res.statusCode;
265
+ const headersAfterGSSP = res.getHeaders();
266
+ for (const [key, val] of Object.entries(headersAfterGSSP)) {
267
+ if (headersBeforeGSSP.has(key) || val == null) continue;
268
+ res.removeHeader(key);
269
+ if (Array.isArray(val)) gsspExtraHeaders[key] = val.map(String);
270
+ else gsspExtraHeaders[key] = String(val);
271
+ }
272
+ }
273
+ let earlyFontLinkHeader = "";
274
+ try {
275
+ const earlyPreloads = [];
276
+ const fontGoogleEarly = await server.ssrLoadModule("next/font/google");
277
+ if (typeof fontGoogleEarly.getSSRFontPreloads === "function") earlyPreloads.push(...fontGoogleEarly.getSSRFontPreloads());
278
+ const fontLocalEarly = await server.ssrLoadModule("next/font/local");
279
+ if (typeof fontLocalEarly.getSSRFontPreloads === "function") earlyPreloads.push(...fontLocalEarly.getSSRFontPreloads());
280
+ if (earlyPreloads.length > 0) earlyFontLinkHeader = earlyPreloads.map((p) => `<${p.href}>; rel=preload; as=font; type=${p.type}; crossorigin`).join(", ");
281
+ } catch {}
282
+ if (typeof pageModule.getStaticProps === "function") {
283
+ const cacheKey = isrCacheKey("pages", url.split("?")[0], process.env.__VINEXT_BUILD_ID);
284
+ const cached = await isrGet(cacheKey);
285
+ if (cached && !cached.isStale && cached.value.value?.kind === "PAGES") {
286
+ const cachedHtml = cached.value.value.html;
287
+ const transformedHtml = await server.transformIndexHtml(url, cachedHtml);
288
+ const revalidateSecs = getRevalidateDuration(cacheKey) ?? 60;
289
+ const hitHeaders = {
290
+ "Content-Type": "text/html",
291
+ "X-Vinext-Cache": "HIT",
292
+ "Cache-Control": `s-maxage=${revalidateSecs}, stale-while-revalidate`
293
+ };
294
+ if (earlyFontLinkHeader) hitHeaders["Link"] = earlyFontLinkHeader;
295
+ res.writeHead(200, hitHeaders);
296
+ res.end(transformedHtml);
297
+ return;
298
+ }
299
+ if (cached && cached.isStale && cached.value.value?.kind === "PAGES") {
300
+ const cachedHtml = cached.value.value.html;
301
+ const transformedHtml = await server.transformIndexHtml(url, cachedHtml);
302
+ triggerBackgroundRegeneration(cacheKey, async () => {
303
+ return runWithRequestContext(createRequestContext({ executionContext: null }), async () => {
304
+ ensureFetchPatch();
305
+ const freshResult = await pageModule.getStaticProps({
306
+ params,
307
+ locale: locale ?? currentDefaultLocale,
308
+ locales: i18nConfig?.locales,
309
+ defaultLocale: currentDefaultLocale
310
+ });
311
+ if (freshResult && "props" in freshResult) {
312
+ const revalidate = typeof freshResult.revalidate === "number" ? freshResult.revalidate : 0;
313
+ if (revalidate > 0) {
314
+ const freshProps = freshResult.props;
315
+ if (typeof routerShim.setSSRContext === "function") routerShim.setSSRContext({
316
+ pathname: patternToNextFormat(route.pattern),
317
+ query: {
318
+ ...params,
319
+ ...parseQueryString(url)
320
+ },
321
+ asPath: url,
322
+ locale: locale ?? currentDefaultLocale,
323
+ locales: i18nConfig?.locales,
324
+ defaultLocale: currentDefaultLocale,
325
+ domainLocales
326
+ });
327
+ if (i18nConfig) {
328
+ await server.ssrLoadModule("vinext/i18n-state");
329
+ const i18nCtx = await server.ssrLoadModule("vinext/i18n-context");
330
+ if (typeof i18nCtx.setI18nContext === "function") i18nCtx.setI18nContext({
331
+ locale: locale ?? currentDefaultLocale,
332
+ locales: i18nConfig.locales,
333
+ defaultLocale: currentDefaultLocale,
334
+ domainLocales,
335
+ hostname: req.headers.host?.split(":", 1)[0]
336
+ });
337
+ }
338
+ let RegenApp = null;
339
+ const appPath = path.join(pagesDir, "_app");
340
+ if (findFileWithExtensions(appPath, matcher)) try {
341
+ RegenApp = (await server.ssrLoadModule(appPath)).default ?? null;
342
+ } catch {}
343
+ let el = RegenApp ? React.createElement(RegenApp, {
344
+ Component: pageModule.default,
345
+ pageProps: freshProps
346
+ }) : React.createElement(pageModule.default, freshProps);
347
+ if (routerShim.wrapWithRouterContext) el = routerShim.wrapWithRouterContext(el);
348
+ const freshBody = await renderIsrPassToStringAsync(el);
349
+ const viteRoot = server.config?.root;
350
+ const regenPageUrl = viteRoot ? "/" + path.relative(viteRoot, route.filePath) : route.filePath;
351
+ const regenAppUrl = RegenApp ? viteRoot ? "/" + path.relative(viteRoot, path.join(pagesDir, "_app")) : path.join(pagesDir, "_app") : null;
352
+ await isrSet(cacheKey, buildPagesCacheValue(`<!DOCTYPE html><html><head></head><body><div id="__next">${freshBody}</div>${`<script>window.__NEXT_DATA__ = ${safeJsonStringify({
353
+ props: { pageProps: freshProps },
354
+ page: patternToNextFormat(route.pattern),
355
+ query: params,
356
+ buildId: process.env.__VINEXT_BUILD_ID,
357
+ isFallback: false,
358
+ locale: locale ?? currentDefaultLocale,
359
+ locales: i18nConfig?.locales,
360
+ defaultLocale: currentDefaultLocale,
361
+ domainLocales,
362
+ __vinext: {
363
+ pageModuleUrl: regenPageUrl,
364
+ appModuleUrl: regenAppUrl
365
+ }
366
+ })}${i18nConfig ? `;window.__VINEXT_LOCALE__=${safeJsonStringify(locale ?? currentDefaultLocale)};window.__VINEXT_LOCALES__=${safeJsonStringify(i18nConfig.locales)};window.__VINEXT_DEFAULT_LOCALE__=${safeJsonStringify(currentDefaultLocale)}` : ""}<\/script>`}\n ${cachedHtml.match(/<script type="module">[\s\S]*?<\/script>/)?.[0] ?? ""}</body></html>`, freshProps), revalidate);
367
+ setRevalidateDuration(cacheKey, revalidate);
368
+ }
369
+ }
370
+ });
371
+ });
372
+ const revalidateSecs = getRevalidateDuration(cacheKey) ?? 60;
373
+ const staleHeaders = {
374
+ "Content-Type": "text/html",
375
+ "X-Vinext-Cache": "STALE",
376
+ "Cache-Control": `s-maxage=${revalidateSecs}, stale-while-revalidate`
377
+ };
378
+ if (earlyFontLinkHeader) staleHeaders["Link"] = earlyFontLinkHeader;
379
+ res.writeHead(200, staleHeaders);
380
+ res.end(transformedHtml);
381
+ return;
382
+ }
383
+ const context = {
384
+ params,
385
+ locale: locale ?? currentDefaultLocale,
386
+ locales: i18nConfig?.locales,
387
+ defaultLocale: currentDefaultLocale
388
+ };
389
+ const result = await pageModule.getStaticProps(context);
390
+ if (result && "props" in result) pageProps = result.props;
391
+ if (result && "redirect" in result) {
392
+ const { redirect } = result;
393
+ const status = redirect.statusCode ?? (redirect.permanent ? 308 : 307);
394
+ let dest = redirect.destination;
395
+ if (!dest.startsWith("http://") && !dest.startsWith("https://")) dest = dest.replace(/^[\\/]+/, "/");
396
+ res.writeHead(status, { Location: dest });
397
+ res.end();
398
+ return;
399
+ }
400
+ if (result && "notFound" in result && result.notFound) {
401
+ await renderErrorPage(server, req, res, url, pagesDir, 404, routerShim.wrapWithRouterContext);
402
+ return;
403
+ }
404
+ if (typeof result?.revalidate === "number" && result.revalidate > 0) isrRevalidateSeconds = result.revalidate;
405
+ }
406
+ let AppComponent = null;
407
+ const appPath = path.join(pagesDir, "_app");
408
+ if (findFileWithExtensions(appPath, matcher)) try {
409
+ AppComponent = (await server.ssrLoadModule(appPath)).default ?? null;
410
+ } catch {}
411
+ const createElement = React.createElement;
412
+ let element;
413
+ const wrapWithRouterContext = routerShim.wrapWithRouterContext;
414
+ if (AppComponent) element = createElement(AppComponent, {
415
+ Component: PageComponent,
416
+ pageProps
417
+ });
418
+ else element = createElement(PageComponent, pageProps);
419
+ if (wrapWithRouterContext) element = wrapWithRouterContext(element);
420
+ const headShim = await server.ssrLoadModule("next/head");
421
+ if (typeof headShim.resetSSRHead === "function") headShim.resetSSRHead();
422
+ const dynamicShim = await server.ssrLoadModule("next/dynamic");
423
+ if (typeof dynamicShim.flushPreloads === "function") await dynamicShim.flushPreloads();
424
+ let fontHeadHTML = "";
425
+ const allFontStyles = [];
426
+ const allFontPreloads = [];
427
+ try {
428
+ const fontGoogle = await server.ssrLoadModule("next/font/google");
429
+ if (typeof fontGoogle.getSSRFontLinks === "function") {
430
+ const fontUrls = fontGoogle.getSSRFontLinks();
431
+ for (const fontUrl of fontUrls) {
432
+ const safeFontUrl = fontUrl.replace(/&/g, "&amp;").replace(/"/g, "&quot;");
433
+ fontHeadHTML += `<link rel="stylesheet" href="${safeFontUrl}" />\n `;
434
+ }
435
+ }
436
+ if (typeof fontGoogle.getSSRFontStyles === "function") allFontStyles.push(...fontGoogle.getSSRFontStyles());
437
+ if (typeof fontGoogle.getSSRFontPreloads === "function") allFontPreloads.push(...fontGoogle.getSSRFontPreloads());
438
+ } catch {}
439
+ try {
440
+ const fontLocal = await server.ssrLoadModule("next/font/local");
441
+ if (typeof fontLocal.getSSRFontStyles === "function") allFontStyles.push(...fontLocal.getSSRFontStyles());
442
+ if (typeof fontLocal.getSSRFontPreloads === "function") allFontPreloads.push(...fontLocal.getSSRFontPreloads());
443
+ } catch {}
444
+ for (const { href, type } of allFontPreloads) {
445
+ const safeHref = href.replace(/&/g, "&amp;").replace(/"/g, "&quot;");
446
+ const safeType = type.replace(/&/g, "&amp;").replace(/"/g, "&quot;");
447
+ fontHeadHTML += `<link rel="preload" href="${safeHref}" as="font" type="${safeType}" crossorigin />\n `;
448
+ }
449
+ if (allFontStyles.length > 0) fontHeadHTML += `<style data-vinext-fonts>${allFontStyles.join("\n")}</style>\n `;
450
+ const viteRoot = server.config.root;
451
+ const pageModuleUrl = "/" + path.relative(viteRoot, route.filePath);
452
+ const appModuleUrl = AppComponent ? "/" + path.relative(viteRoot, path.join(pagesDir, "_app")) : null;
453
+ const hydrationScript = `
686
454
  <script type="module">
687
455
  import React from "react";
688
456
  import { hydrateRoot } from "react-dom/client";
@@ -695,14 +463,12 @@ async function hydrate() {
695
463
  const pageModule = await import("${pageModuleUrl}");
696
464
  const PageComponent = pageModule.default;
697
465
  let element;
698
- ${appModuleUrl
699
- ? `
466
+ ${appModuleUrl ? `
700
467
  const appModule = await import("${appModuleUrl}");
701
468
  const AppComponent = appModule.default;
702
469
  window.__VINEXT_APP__ = AppComponent;
703
470
  element = React.createElement(AppComponent, { Component: PageComponent, pageProps });
704
- `
705
- : `
471
+ ` : `
706
472
  element = React.createElement(PageComponent, pageProps);
707
473
  `}
708
474
  element = wrapWithRouterContext(element);
@@ -710,217 +476,126 @@ async function hydrate() {
710
476
  window.__VINEXT_ROOT__ = root;
711
477
  }
712
478
  hydrate();
713
- </script>`;
714
- const nextDataScript = `<script>window.__NEXT_DATA__ = ${safeJsonStringify({
715
- props: { pageProps },
716
- page: patternToNextFormat(route.pattern),
717
- query: params,
718
- buildId: process.env.__VINEXT_BUILD_ID,
719
- isFallback: false,
720
- locale: locale ?? currentDefaultLocale,
721
- locales: i18nConfig?.locales,
722
- defaultLocale: currentDefaultLocale,
723
- domainLocales,
724
- // Include module URLs so client navigation can import pages directly
725
- __vinext: {
726
- pageModuleUrl,
727
- appModuleUrl,
728
- },
729
- })}${i18nConfig ? `;window.__VINEXT_LOCALE__=${safeJsonStringify(locale ?? currentDefaultLocale)};window.__VINEXT_LOCALES__=${safeJsonStringify(i18nConfig.locales)};window.__VINEXT_DEFAULT_LOCALE__=${safeJsonStringify(currentDefaultLocale)}` : ""}</script>`;
730
- // Try to load custom _document.tsx
731
- const docPath = path.join(pagesDir, "_document");
732
- let DocumentComponent = null;
733
- if (findFileWithExtensions(docPath, matcher)) {
734
- try {
735
- const docModule = await server.ssrLoadModule(docPath);
736
- DocumentComponent = docModule.default ?? null;
737
- }
738
- catch {
739
- // _document exists but failed to load
740
- }
741
- }
742
- const allScripts = `${nextDataScript}\n ${hydrationScript}`;
743
- // Build response headers: start with gSSP headers, then layer on
744
- // ISR and font preload headers (which take precedence).
745
- const extraHeaders = {
746
- ...gsspExtraHeaders,
747
- };
748
- if (isrRevalidateSeconds) {
749
- extraHeaders["Cache-Control"] =
750
- `s-maxage=${isrRevalidateSeconds}, stale-while-revalidate`;
751
- extraHeaders["X-Vinext-Cache"] = "MISS";
752
- }
753
- // Set HTTP Link header for font preloading.
754
- // This lets the browser (and CDN) start fetching font files before parsing HTML.
755
- if (allFontPreloads.length > 0) {
756
- extraHeaders["Link"] = allFontPreloads
757
- .map((p) => `<${p.href}>; rel=preload; as=font; type=${p.type}; crossorigin`)
758
- .join(", ");
759
- }
760
- // Stream the page using progressive SSR.
761
- // The shell (layouts, non-suspended content) arrives immediately.
762
- // Suspense content streams in as it resolves.
763
- await streamPageToResponse(res, element, {
764
- url,
765
- server,
766
- fontHeadHTML,
767
- scripts: allScripts,
768
- DocumentComponent,
769
- statusCode,
770
- extraHeaders,
771
- // Collect head HTML AFTER the shell renders (inside streamPageToResponse,
772
- // after renderToReadableStream resolves). Head tags from Suspense
773
- // children arrive late — this matches Next.js behavior.
774
- getHeadHTML: () => typeof headShim.getSSRHeadHTML === "function" ? headShim.getSSRHeadHTML() : "",
775
- });
776
- _renderEnd = now();
777
- // Clear SSR context after rendering
778
- if (typeof routerShim.setSSRContext === "function") {
779
- routerShim.setSSRContext(null);
780
- }
781
- // If ISR is enabled, we need the full HTML for caching.
782
- // For ISR, re-render synchronously to get the complete HTML string.
783
- // This runs after the stream is already sent, so it doesn't affect TTFB.
784
- if (isrRevalidateSeconds !== null && isrRevalidateSeconds > 0) {
785
- let isrElement = AppComponent
786
- ? createElement(AppComponent, {
787
- Component: pageModule.default,
788
- pageProps,
789
- })
790
- : createElement(pageModule.default, pageProps);
791
- if (wrapWithRouterContext) {
792
- isrElement = wrapWithRouterContext(isrElement);
793
- }
794
- const isrBodyHtml = await renderIsrPassToStringAsync(isrElement);
795
- const isrHtml = `<!DOCTYPE html><html><head></head><body><div id="__next">${isrBodyHtml}</div>${allScripts}</body></html>`;
796
- const cacheKey = isrCacheKey("pages", url.split("?")[0],
797
- // __VINEXT_BUILD_ID is a compile-time define — undefined in dev,
798
- // which is fine: dev doesn't need cross-deploy cache isolation.
799
- process.env.__VINEXT_BUILD_ID);
800
- await isrSet(cacheKey, buildPagesCacheValue(isrHtml, pageProps), isrRevalidateSeconds);
801
- setRevalidateDuration(cacheKey, isrRevalidateSeconds);
802
- }
803
- }
804
- catch (e) {
805
- // Let Vite fix the stack trace for better dev experience
806
- server.ssrFixStacktrace(e);
807
- console.error(e);
808
- // Report error via instrumentation hook if registered
809
- reportRequestError(e instanceof Error ? e : new Error(String(e)), {
810
- path: url,
811
- method: req.method ?? "GET",
812
- headers: Object.fromEntries(Object.entries(req.headers).map(([k, v]) => [
813
- k,
814
- Array.isArray(v) ? v.join(", ") : String(v ?? ""),
815
- ])),
816
- }, {
817
- routerKind: "Pages Router",
818
- routePath: route.pattern,
819
- routeType: "render",
820
- }).catch(() => {
821
- /* ignore reporting errors */
822
- });
823
- // Try to render custom 500 error page
824
- try {
825
- await renderErrorPage(server, req, res, url, pagesDir, 500, undefined, matcher);
826
- }
827
- catch (fallbackErr) {
828
- // If error page itself fails, fall back to plain text.
829
- // This is a dev-only code path (prod uses prod-server.ts), so
830
- // include the error message for debugging.
831
- res.statusCode = 500;
832
- res.end(`Internal Server Error: ${fallbackErr.message}`);
833
- }
834
- }
835
- finally {
836
- // Cleanup is handled by unified ALS scope unwinding.
837
- }
838
- });
839
- };
479
+ <\/script>`;
480
+ const nextDataScript = `<script>window.__NEXT_DATA__ = ${safeJsonStringify({
481
+ props: { pageProps },
482
+ page: patternToNextFormat(route.pattern),
483
+ query: params,
484
+ buildId: process.env.__VINEXT_BUILD_ID,
485
+ isFallback: false,
486
+ locale: locale ?? currentDefaultLocale,
487
+ locales: i18nConfig?.locales,
488
+ defaultLocale: currentDefaultLocale,
489
+ domainLocales,
490
+ __vinext: {
491
+ pageModuleUrl,
492
+ appModuleUrl
493
+ }
494
+ })}${i18nConfig ? `;window.__VINEXT_LOCALE__=${safeJsonStringify(locale ?? currentDefaultLocale)};window.__VINEXT_LOCALES__=${safeJsonStringify(i18nConfig.locales)};window.__VINEXT_DEFAULT_LOCALE__=${safeJsonStringify(currentDefaultLocale)}` : ""}<\/script>`;
495
+ const docPath = path.join(pagesDir, "_document");
496
+ let DocumentComponent = null;
497
+ if (findFileWithExtensions(docPath, matcher)) try {
498
+ DocumentComponent = (await server.ssrLoadModule(docPath)).default ?? null;
499
+ } catch {}
500
+ const allScripts = `${nextDataScript}\n ${hydrationScript}`;
501
+ const extraHeaders = { ...gsspExtraHeaders };
502
+ if (isrRevalidateSeconds) {
503
+ extraHeaders["Cache-Control"] = `s-maxage=${isrRevalidateSeconds}, stale-while-revalidate`;
504
+ extraHeaders["X-Vinext-Cache"] = "MISS";
505
+ }
506
+ if (allFontPreloads.length > 0) extraHeaders["Link"] = allFontPreloads.map((p) => `<${p.href}>; rel=preload; as=font; type=${p.type}; crossorigin`).join(", ");
507
+ await streamPageToResponse(res, element, {
508
+ url,
509
+ server,
510
+ fontHeadHTML,
511
+ scripts: allScripts,
512
+ DocumentComponent,
513
+ statusCode,
514
+ extraHeaders,
515
+ getHeadHTML: () => typeof headShim.getSSRHeadHTML === "function" ? headShim.getSSRHeadHTML() : ""
516
+ });
517
+ _renderEnd = now();
518
+ if (typeof routerShim.setSSRContext === "function") routerShim.setSSRContext(null);
519
+ if (isrRevalidateSeconds !== null && isrRevalidateSeconds > 0) {
520
+ let isrElement = AppComponent ? createElement(AppComponent, {
521
+ Component: pageModule.default,
522
+ pageProps
523
+ }) : createElement(pageModule.default, pageProps);
524
+ if (wrapWithRouterContext) isrElement = wrapWithRouterContext(isrElement);
525
+ const isrHtml = `<!DOCTYPE html><html><head></head><body><div id="__next">${await renderIsrPassToStringAsync(isrElement)}</div>${allScripts}</body></html>`;
526
+ const cacheKey = isrCacheKey("pages", url.split("?")[0], process.env.__VINEXT_BUILD_ID);
527
+ await isrSet(cacheKey, buildPagesCacheValue(isrHtml, pageProps), isrRevalidateSeconds);
528
+ setRevalidateDuration(cacheKey, isrRevalidateSeconds);
529
+ }
530
+ } catch (e) {
531
+ server.ssrFixStacktrace?.(e);
532
+ console.error(e);
533
+ reportRequestError(e instanceof Error ? e : new Error(String(e)), {
534
+ path: url,
535
+ method: req.method ?? "GET",
536
+ headers: Object.fromEntries(Object.entries(req.headers).map(([k, v]) => [k, Array.isArray(v) ? v.join(", ") : String(v ?? "")]))
537
+ }, {
538
+ routerKind: "Pages Router",
539
+ routePath: route.pattern,
540
+ routeType: "render"
541
+ }).catch(() => {});
542
+ try {
543
+ await renderErrorPage(server, req, res, url, pagesDir, 500, void 0, matcher);
544
+ } catch (fallbackErr) {
545
+ res.statusCode = 500;
546
+ res.end(`Internal Server Error: ${fallbackErr.message}`);
547
+ }
548
+ }
549
+ });
550
+ };
840
551
  }
841
552
  /**
842
- * Render a custom error page (404.tsx, 500.tsx, or _error.tsx).
843
- *
844
- * Next.js resolution order:
845
- * - 404: pages/404.tsx -> pages/_error.tsx -> default
846
- * - 500: pages/500.tsx -> pages/_error.tsx -> default
847
- * - other: pages/_error.tsx -> default
848
- */
553
+ * Render a custom error page (404.tsx, 500.tsx, or _error.tsx).
554
+ *
555
+ * Next.js resolution order:
556
+ * - 404: pages/404.tsx -> pages/_error.tsx -> default
557
+ * - 500: pages/500.tsx -> pages/_error.tsx -> default
558
+ * - other: pages/_error.tsx -> default
559
+ */
849
560
  async function renderErrorPage(server, _req, res, url, pagesDir, statusCode, wrapWithRouterContext, fileMatcher) {
850
- const matcher = fileMatcher ?? createValidFileMatcher();
851
- // Try specific status page first, then _error, then fallback
852
- const candidates = statusCode === 404 ? ["404", "_error"] : statusCode === 500 ? ["500", "_error"] : ["_error"];
853
- for (const candidate of candidates) {
854
- try {
855
- const candidatePath = path.join(pagesDir, candidate);
856
- if (!findFileWithExtensions(candidatePath, matcher))
857
- continue;
858
- const errorModule = await server.ssrLoadModule(candidatePath);
859
- const ErrorComponent = errorModule.default;
860
- if (!ErrorComponent)
861
- continue;
862
- // Try to load _app.tsx to wrap the error page
863
- let AppComponent = null;
864
- const appPathErr = path.join(pagesDir, "_app");
865
- if (findFileWithExtensions(appPathErr, matcher)) {
866
- try {
867
- const appModule = await server.ssrLoadModule(appPathErr);
868
- AppComponent = appModule.default ?? null;
869
- }
870
- catch {
871
- // _app exists but failed to load
872
- }
873
- }
874
- const createElement = React.createElement;
875
- const errorProps = { statusCode };
876
- // If the caller didn't supply wrapWithRouterContext, load it now.
877
- // ssrLoadModule caches internally so the cost is negligible.
878
- let wrapFn = wrapWithRouterContext;
879
- if (!wrapFn) {
880
- try {
881
- const errRouterShim = await server.ssrLoadModule("next/router");
882
- wrapFn = errRouterShim.wrapWithRouterContext;
883
- }
884
- catch {
885
- // router shim not available — continue without it
886
- }
887
- }
888
- let element;
889
- if (AppComponent) {
890
- element = createElement(AppComponent, {
891
- Component: ErrorComponent,
892
- pageProps: errorProps,
893
- });
894
- }
895
- else {
896
- element = createElement(ErrorComponent, errorProps);
897
- }
898
- if (wrapFn) {
899
- element = wrapFn(element);
900
- }
901
- const bodyHtml = await renderToStringAsync(element);
902
- // Try custom _document
903
- let html;
904
- let DocumentComponent = null;
905
- const docPathErr = path.join(pagesDir, "_document");
906
- if (findFileWithExtensions(docPathErr, matcher)) {
907
- try {
908
- const docModule = await server.ssrLoadModule(docPathErr);
909
- DocumentComponent = docModule.default ?? null;
910
- }
911
- catch {
912
- // _document exists but failed to load
913
- }
914
- }
915
- if (DocumentComponent) {
916
- const docElement = createElement(DocumentComponent);
917
- let docHtml = await renderToStringAsync(docElement);
918
- docHtml = docHtml.replace("__NEXT_MAIN__", bodyHtml);
919
- docHtml = docHtml.replace("<!-- __NEXT_SCRIPTS__ -->", "");
920
- html = docHtml;
921
- }
922
- else {
923
- html = `<!DOCTYPE html>
561
+ const matcher = fileMatcher ?? createValidFileMatcher();
562
+ const candidates = statusCode === 404 ? ["404", "_error"] : statusCode === 500 ? ["500", "_error"] : ["_error"];
563
+ for (const candidate of candidates) try {
564
+ const candidatePath = path.join(pagesDir, candidate);
565
+ if (!findFileWithExtensions(candidatePath, matcher)) continue;
566
+ const ErrorComponent = (await server.ssrLoadModule(candidatePath)).default;
567
+ if (!ErrorComponent) continue;
568
+ let AppComponent = null;
569
+ const appPathErr = path.join(pagesDir, "_app");
570
+ if (findFileWithExtensions(appPathErr, matcher)) try {
571
+ AppComponent = (await server.ssrLoadModule(appPathErr)).default ?? null;
572
+ } catch {}
573
+ const createElement = React.createElement;
574
+ const errorProps = { statusCode };
575
+ let wrapFn = wrapWithRouterContext;
576
+ if (!wrapFn) try {
577
+ wrapFn = (await server.ssrLoadModule("next/router")).wrapWithRouterContext;
578
+ } catch {}
579
+ let element;
580
+ if (AppComponent) element = createElement(AppComponent, {
581
+ Component: ErrorComponent,
582
+ pageProps: errorProps
583
+ });
584
+ else element = createElement(ErrorComponent, errorProps);
585
+ if (wrapFn) element = wrapFn(element);
586
+ const bodyHtml = await renderToStringAsync(element);
587
+ let html;
588
+ let DocumentComponent = null;
589
+ const docPathErr = path.join(pagesDir, "_document");
590
+ if (findFileWithExtensions(docPathErr, matcher)) try {
591
+ DocumentComponent = (await server.ssrLoadModule(docPathErr)).default ?? null;
592
+ } catch {}
593
+ if (DocumentComponent) {
594
+ let docHtml = await renderToStringAsync(createElement(DocumentComponent));
595
+ docHtml = docHtml.replace("__NEXT_MAIN__", bodyHtml);
596
+ docHtml = docHtml.replace("<!-- __NEXT_SCRIPTS__ -->", "");
597
+ html = docHtml;
598
+ } else html = `<!DOCTYPE html>
924
599
  <html>
925
600
  <head>
926
601
  <meta charset="utf-8" />
@@ -930,19 +605,17 @@ async function renderErrorPage(server, _req, res, url, pagesDir, statusCode, wra
930
605
  <div id="__next">${bodyHtml}</div>
931
606
  </body>
932
607
  </html>`;
933
- }
934
- const transformedHtml = await server.transformIndexHtml(url, html);
935
- res.writeHead(statusCode, { "Content-Type": "text/html" });
936
- res.end(transformedHtml);
937
- return;
938
- }
939
- catch {
940
- // This candidate doesn't exist, try next
941
- continue;
942
- }
943
- }
944
- // No custom error page found — use plain text fallback
945
- res.writeHead(statusCode, { "Content-Type": "text/plain" });
946
- res.end(`${statusCode} - ${statusCode === 404 ? "Page not found" : "Internal Server Error"}`);
608
+ const transformedHtml = await server.transformIndexHtml(url, html);
609
+ res.writeHead(statusCode, { "Content-Type": "text/html" });
610
+ res.end(transformedHtml);
611
+ return;
612
+ } catch {
613
+ continue;
614
+ }
615
+ res.writeHead(statusCode, { "Content-Type": "text/plain" });
616
+ res.end(`${statusCode} - ${statusCode === 404 ? "Page not found" : "Internal Server Error"}`);
947
617
  }
618
+ //#endregion
619
+ export { createSSRHandler, detectLocaleFromHeaders, extractLocaleFromUrl, parseCookieLocale };
620
+
948
621
  //# sourceMappingURL=dev-server.js.map