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