@wular/pnext 0.0.4 → 0.0.7

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 (395) hide show
  1. package/README.md +22 -16
  2. package/bin/pnext +6 -1
  3. package/config/lint/base.js +7 -7
  4. package/config/ts/base.json +2 -4
  5. package/config/ts/react.json +2 -6
  6. package/package.json +23 -2
  7. package/reference/compat.md +87 -22
  8. package/reference/config.md +40 -93
  9. package/reference/css.md +42 -33
  10. package/reference/dev.md +26 -33
  11. package/reference/env.md +18 -20
  12. package/reference/getting-started.md +132 -0
  13. package/reference/metadata.md +31 -46
  14. package/reference/navigation.md +65 -105
  15. package/reference/performance.md +21 -142
  16. package/reference/rendering.md +78 -75
  17. package/reference/routing.md +97 -107
  18. package/reference/typegen.md +27 -29
  19. package/src/api/cache.ts +36 -37
  20. package/src/api/client-cache.ts +2 -2
  21. package/src/api/client-navigation.ts +121 -114
  22. package/src/api/dynamic.tsx +58 -55
  23. package/src/api/link.tsx +52 -53
  24. package/src/api/navigation.ts +59 -58
  25. package/src/api/server.ts +152 -144
  26. package/src/api/suspense.ts +4 -4
  27. package/src/cli/adapters/vercel-warm.ts +126 -121
  28. package/src/cli/adapters/vercel.ts +437 -443
  29. package/src/cli/analyze.ts +333 -144
  30. package/src/cli/{named-bin.ts → boot/named-bin.ts} +37 -37
  31. package/src/cli/{boot-trace.ts → boot/trace.ts} +10 -10
  32. package/src/cli/build.ts +976 -978
  33. package/src/cli/create.ts +72 -64
  34. package/src/cli/dev.ts +116 -116
  35. package/src/cli/index.ts +94 -85
  36. package/src/cli/migrate/package-json.ts +73 -71
  37. package/src/cli/migrate/report.ts +32 -33
  38. package/src/cli/migrate/{index.ts → run.ts} +48 -49
  39. package/src/cli/migrate/scan.ts +46 -46
  40. package/src/cli/migrate/spinner.ts +9 -9
  41. package/src/cli/migrate/tsconfig.ts +35 -35
  42. package/src/cli/{server-entry.ts → serve/entry.ts} +57 -57
  43. package/src/cli/{request-pipeline.ts → serve/pipeline.ts} +386 -394
  44. package/src/cli/{serve-ui.ts → serve/ui.ts} +47 -47
  45. package/src/cli/start.ts +65 -66
  46. package/src/{typegen.ts → cli/typegen.ts} +59 -59
  47. package/src/client/build.ts +802 -768
  48. package/src/client/chunk-fold.ts +245 -240
  49. package/src/client/{paths.ts → chunk-name.ts} +6 -6
  50. package/src/client/entry.ts +162 -147
  51. package/src/client/prebuilt.ts +231 -223
  52. package/src/client/profile.ts +29 -29
  53. package/src/client/react-compiler.ts +20 -15
  54. package/src/client/{compat-surface.ts → react-tier.ts} +64 -64
  55. package/src/client/reference-stub.ts +51 -51
  56. package/src/client/reference.ts +19 -19
  57. package/src/{api → client}/router/events.ts +32 -17
  58. package/src/{api → client}/router/history.ts +16 -16
  59. package/src/{api → client}/router/hub.ts +52 -53
  60. package/src/{api/router.ts → client/router/index.ts} +61 -60
  61. package/src/{api → client}/router/policies.ts +24 -24
  62. package/src/{api → client}/router/runtime.ts +2022 -1995
  63. package/src/{api → client}/router/types.ts +98 -98
  64. package/src/compat/actions/client-plugin.ts +42 -43
  65. package/src/compat/actions/client-stub.ts +14 -14
  66. package/src/compat/actions/{action-client.ts → client.ts} +194 -193
  67. package/src/compat/actions/config.ts +63 -64
  68. package/src/compat/actions/detect.ts +68 -68
  69. package/src/compat/actions/discovery.ts +105 -105
  70. package/src/compat/actions/{action-dispatch.ts → dispatch.ts} +277 -274
  71. package/src/compat/actions/early-submit.ts +1 -1
  72. package/src/compat/actions/endpoint.ts +198 -198
  73. package/src/compat/actions/extensions.ts +811 -0
  74. package/src/compat/actions/flight.ts +23 -23
  75. package/src/compat/actions/form-state.ts +34 -34
  76. package/src/compat/actions/hoist.ts +382 -236
  77. package/src/compat/actions/ids.ts +7 -12
  78. package/src/compat/actions/index.ts +8 -8
  79. package/src/compat/actions/instances.ts +46 -46
  80. package/src/compat/actions/origin.ts +47 -48
  81. package/src/compat/actions/protocol.ts +22 -22
  82. package/src/compat/actions/registry.ts +19 -19
  83. package/src/compat/{misc/action-return.ts → actions/return.ts} +60 -57
  84. package/src/compat/actions/rewrite.ts +125 -125
  85. package/src/compat/actions/{action-router.ts → router.ts} +10 -10
  86. package/src/compat/actions/serve.ts +132 -136
  87. package/src/compat/actions/server-tag.ts +4 -4
  88. package/src/compat/actions/{action-shared.ts → shared.ts} +34 -34
  89. package/src/compat/actions/unrecognized-error.ts +4 -4
  90. package/src/compat/{index.ts → aliases.ts} +112 -109
  91. package/src/compat/bundler/bun-externals.ts +18 -18
  92. package/src/compat/bundler/cjs-exports.ts +271 -223
  93. package/src/compat/bundler/config.ts +116 -111
  94. package/src/compat/bundler/externals.ts +12 -12
  95. package/src/compat/bundler/import-meta-url.ts +19 -19
  96. package/src/compat/bundler/modularize-imports.ts +36 -33
  97. package/src/compat/bundler/new-url-asset.ts +22 -24
  98. package/src/compat/bundler/optimize-package-imports.ts +111 -107
  99. package/src/compat/bundler/polyfill.ts +28 -28
  100. package/src/compat/bundler/react-compiler.ts +35 -29
  101. package/src/compat/bundler/react-profiler.tsx +11 -11
  102. package/src/compat/bundler/relay-transform.ts +48 -47
  103. package/src/compat/bundler/require-context.ts +119 -113
  104. package/src/compat/bundler/resolve-extensions.ts +19 -19
  105. package/src/compat/bundler/source-cache.ts +25 -25
  106. package/src/compat/bundler/static-imports.ts +7 -7
  107. package/src/compat/bundler/symlink-imports.ts +46 -46
  108. package/src/compat/bundler/tsconfig-paths.ts +13 -15
  109. package/src/compat/bundler/wasm.ts +58 -60
  110. package/src/compat/bundler/webpack-loaders.ts +254 -241
  111. package/src/compat/bundler/worker.ts +101 -104
  112. package/src/compat/cache/build-flags.ts +29 -29
  113. package/src/compat/cache/build-prerender-errors.ts +40 -44
  114. package/src/compat/cache/custom-handler.ts +60 -53
  115. package/src/compat/cache/fetch-patch.ts +240 -240
  116. package/src/compat/cache/handler.ts +27 -31
  117. package/src/compat/cache/modern-handler.ts +148 -126
  118. package/src/compat/cache/resume-data-cache.ts +47 -45
  119. package/src/compat/cache/revalidate.ts +233 -232
  120. package/src/compat/cache/runtime-error.ts +35 -35
  121. package/src/compat/cache/use-cache-transform.ts +442 -417
  122. package/src/compat/cache/use-cache.ts +650 -614
  123. package/src/compat/cache-control.ts +140 -142
  124. package/src/compat/client/base-path.ts +21 -20
  125. package/src/compat/client/css-order.ts +18 -18
  126. package/src/compat/client/errors/bare-boundary.ts +11 -11
  127. package/src/compat/client/errors/control-flow.ts +23 -23
  128. package/src/compat/client/errors/error-boundary.ts +61 -61
  129. package/src/compat/client/errors/global-error.ts +101 -91
  130. package/src/compat/client/errors/install.ts +71 -72
  131. package/src/compat/client/errors/lazy.ts +23 -23
  132. package/src/compat/client/errors/primitive-throw.ts +47 -45
  133. package/src/compat/client/errors/soft-refresh.ts +4 -4
  134. package/src/compat/client/link-status.ts +33 -33
  135. package/src/compat/client/{nav-compat-runtime.ts → nav-runtime.ts} +20 -20
  136. package/src/compat/client/{nav-compat.ts → nav.ts} +12 -13
  137. package/src/compat/client/navigation-scroll.ts +63 -63
  138. package/src/compat/client/optimistic-routing.ts +93 -88
  139. package/src/compat/client/prefetch-cache.ts +23 -24
  140. package/src/compat/client/route-announcer.ts +35 -35
  141. package/src/compat/client/segment-cache-policy.ts +20 -20
  142. package/src/compat/client/segment-cache.ts +309 -315
  143. package/src/compat/client/segment-prefetch.ts +127 -132
  144. package/src/compat/client/trailing-slash.ts +5 -4
  145. package/src/compat/css/chunking.ts +113 -116
  146. package/src/compat/css/inline-css.ts +21 -21
  147. package/src/compat/css/lightningcss.ts +37 -38
  148. package/src/compat/css/modules.ts +161 -175
  149. package/src/compat/css/nonce.ts +7 -7
  150. package/src/compat/css/sass-plugin.ts +18 -21
  151. package/src/compat/css/sass.ts +150 -152
  152. package/src/compat/css/styled-jsx-runtime.ts +27 -27
  153. package/src/compat/css/styled-jsx.ts +21 -21
  154. package/src/compat/edge-runtime.ts +27 -27
  155. package/src/compat/{adapter → export}/build-complete.ts +72 -75
  156. package/src/compat/export/client.ts +29 -31
  157. package/src/compat/export/{index.ts → emit.ts} +111 -110
  158. package/src/compat/export/standalone.ts +62 -54
  159. package/src/compat/image-optimizer/cache.ts +51 -49
  160. package/src/compat/image-optimizer/detect.ts +52 -52
  161. package/src/compat/image-optimizer/{index.ts → optimize.ts} +189 -194
  162. package/src/compat/image-optimizer/source.ts +77 -80
  163. package/src/compat/lifecycle/after-scope.ts +26 -26
  164. package/src/compat/lifecycle/after.ts +48 -45
  165. package/src/compat/lifecycle/error-funnel.ts +98 -102
  166. package/src/compat/lifecycle/error-serialize.ts +30 -26
  167. package/src/compat/lifecycle/error-ui.ts +67 -32
  168. package/src/compat/lifecycle/instrumentation-client.ts +36 -38
  169. package/src/compat/lifecycle/instrumentation.ts +85 -85
  170. package/src/compat/lifecycle/node-console.ts +11 -11
  171. package/src/compat/lifecycle/testmode.ts +160 -132
  172. package/src/compat/mdx/compile.ts +60 -57
  173. package/src/compat/mdx/plugin.ts +11 -11
  174. package/src/compat/mdx/{next-mdx-stub.ts → stub.ts} +5 -5
  175. package/src/compat/{metadata-route-artifacts.ts → metadata-artifacts.ts} +195 -191
  176. package/src/compat/metadata.ts +75 -81
  177. package/src/compat/next/cache.ts +68 -69
  178. package/src/compat/next/canonical-url.ts +9 -9
  179. package/src/compat/next/client-cache.ts +19 -19
  180. package/src/compat/next/client-navigation.ts +112 -116
  181. package/src/compat/next/client-only.ts +1 -1
  182. package/src/compat/next/client-script.tsx +99 -93
  183. package/src/compat/next/client-server.ts +10 -10
  184. package/src/compat/next/config-loader.ts +176 -173
  185. package/src/compat/next/config.ts +7 -7
  186. package/src/compat/next/constants.cjs +6 -6
  187. package/src/compat/next/constants.ts +6 -6
  188. package/src/compat/next/custom-server.ts +26 -24
  189. package/src/compat/next/dist/client/components/app-router-headers.ts +21 -21
  190. package/src/compat/next/dist/server/app-render/work-unit-async-storage.external.cjs +3 -4
  191. package/src/compat/next/dist/server/web/spec-extension/revalidate.ts +1 -1
  192. package/src/compat/next/dist/server/web/spec-extension/unstable-cache.ts +1 -1
  193. package/src/compat/next/dist/server/web/spec-extension/unstable-no-store.ts +1 -1
  194. package/src/compat/next/dynamic.tsx +21 -18
  195. package/src/compat/next/error.tsx +52 -52
  196. package/src/compat/next/font/cache.ts +74 -74
  197. package/src/compat/next/font/google.ts +2 -2
  198. package/src/compat/next/font/index.ts +1 -1
  199. package/src/compat/next/font/local.ts +3 -3
  200. package/src/compat/next/font/runtime-client.ts +17 -15
  201. package/src/compat/next/font/runtime.ts +443 -408
  202. package/src/compat/next/font/shared.ts +120 -108
  203. package/src/compat/next/form.tsx +63 -63
  204. package/src/compat/next/head.tsx +2 -2
  205. package/src/compat/next/headers.ts +103 -95
  206. package/src/compat/next/image/client.tsx +220 -0
  207. package/src/compat/next/image/config.ts +56 -58
  208. package/src/compat/next/image/optimizer.ts +40 -36
  209. package/src/compat/next/image/patterns.ts +37 -40
  210. package/src/compat/next/{image-props.ts → image/props.ts} +208 -202
  211. package/src/compat/next/image/shared.ts +65 -57
  212. package/src/compat/next/image/static-metadata.ts +98 -107
  213. package/src/compat/next/image/validate.ts +79 -88
  214. package/src/compat/next/image.tsx +19 -23
  215. package/src/compat/next/index.ts +1 -1
  216. package/src/compat/next/legacy-image.tsx +59 -60
  217. package/src/compat/next/{link-validation-transform.ts → link-transform.ts} +95 -96
  218. package/src/compat/next/link.tsx +158 -158
  219. package/src/compat/next/navigation.cjs +12 -3
  220. package/src/compat/next/navigation.ts +48 -50
  221. package/src/compat/next/offline.ts +27 -27
  222. package/src/compat/next/og.ts +121 -124
  223. package/src/compat/next/preferred-region.ts +13 -14
  224. package/src/compat/next/redirects.ts +58 -56
  225. package/src/compat/next/resource-hints.ts +73 -76
  226. package/src/compat/next/rewrites.ts +130 -133
  227. package/src/compat/next/root-params.ts +45 -45
  228. package/src/compat/next/{optimistic-route-state.ts → route-state.ts} +52 -52
  229. package/src/compat/next/router.cjs +4 -2
  230. package/src/compat/next/router.ts +58 -61
  231. package/src/compat/next/script.tsx +108 -108
  232. package/src/compat/next/server-only.ts +1 -1
  233. package/src/compat/next/server.ts +19 -19
  234. package/src/compat/next/svgr.ts +18 -17
  235. package/src/compat/next/telemetry.ts +24 -24
  236. package/src/compat/next/{image-usage.ts → usage.ts} +70 -39
  237. package/src/compat/next/user-agent.ts +53 -49
  238. package/src/compat/next/web-vitals.ts +22 -24
  239. package/src/compat/otel/api.ts +41 -41
  240. package/src/compat/otel/client-trace-metadata.ts +25 -27
  241. package/src/compat/otel/fetch-span.ts +29 -29
  242. package/src/compat/otel/tracer.ts +331 -331
  243. package/src/compat/pages/api.ts +456 -0
  244. package/src/compat/pages/client-plugin.ts +36 -36
  245. package/src/compat/pages/router-state.ts +34 -34
  246. package/src/compat/pages/{index.ts → router.ts} +130 -135
  247. package/src/compat/ppr/io.ts +12 -12
  248. package/src/compat/ppr/missing-root-params.ts +34 -38
  249. package/src/compat/ppr/root-params-scan.ts +66 -66
  250. package/src/compat/ppr/root-params-transform.ts +24 -26
  251. package/src/compat/ppr/root-params.ts +30 -30
  252. package/src/compat/ppr/segment-config-incompat.ts +6 -7
  253. package/src/compat/protocol.ts +71 -70
  254. package/src/compat/react/action-state.ts +47 -48
  255. package/src/compat/react/client-lite.ts +15 -15
  256. package/src/compat/react/client.ts +4 -4
  257. package/src/compat/react/compiler-runtime.ts +11 -11
  258. package/src/compat/react/dom-client.ts +44 -44
  259. package/src/compat/react/dom-react-server.ts +10 -16
  260. package/src/compat/react/dom-server.ts +10 -10
  261. package/src/compat/react/dom.ts +52 -52
  262. package/src/compat/react/hooks-extra.ts +34 -35
  263. package/src/compat/react/parity.ts +64 -61
  264. package/src/compat/react/preact.ts +81 -82
  265. package/src/compat/react/react-server.ts +28 -28
  266. package/src/compat/react/router-shim.ts +1 -1
  267. package/src/compat/react/server-component-use.ts +8 -8
  268. package/src/compat/react/server-inserted-html.ts +30 -31
  269. package/src/compat/react/server.ts +53 -55
  270. package/src/compat/react/use.ts +32 -32
  271. package/src/compat/react/view-transition.ts +20 -20
  272. package/src/compat/register/actions.ts +35 -824
  273. package/src/compat/register/boot.ts +41 -41
  274. package/src/compat/register/build-tier.ts +5 -5
  275. package/src/compat/register/build.ts +74 -70
  276. package/src/compat/register/bundler.ts +161 -156
  277. package/src/compat/register/cache.ts +20 -20
  278. package/src/compat/register/client-errors.ts +3 -3
  279. package/src/compat/register/config.ts +6 -6
  280. package/src/compat/register/css-extras.ts +30 -34
  281. package/src/compat/register/edge-runtime.ts +3 -3
  282. package/src/compat/register/errors.ts +10 -12
  283. package/src/compat/register/export.ts +16 -16
  284. package/src/compat/register/font.ts +11 -11
  285. package/src/compat/register/hooks.ts +2 -2
  286. package/src/compat/register/image.ts +40 -40
  287. package/src/compat/register/index.ts +59 -62
  288. package/src/compat/register/instrumentation-client.ts +10 -10
  289. package/src/compat/register/lifecycle.ts +25 -28
  290. package/src/compat/register/mdx.ts +10 -10
  291. package/src/compat/register/middleware.ts +226 -16
  292. package/src/compat/register/otel.ts +80 -88
  293. package/src/compat/register/pages-api.ts +10 -463
  294. package/src/compat/register/ppr.ts +16 -16
  295. package/src/compat/register/protocol.ts +17 -18
  296. package/src/compat/register/proxy.ts +49 -51
  297. package/src/compat/register/render.ts +79 -76
  298. package/src/compat/register/routing.ts +162 -159
  299. package/src/compat/register/segment.ts +24 -1897
  300. package/src/compat/register/static-image.ts +3 -3
  301. package/src/compat/register/{misc.ts → taint.ts} +9 -9
  302. package/src/compat/register/typed-routes.ts +14 -14
  303. package/src/compat/register/{usecache.ts → use-cache.ts} +39 -39
  304. package/src/compat/register/validation.ts +18 -18
  305. package/src/compat/segment/loading-boundary.ts +43 -45
  306. package/src/compat/segment/page-slot.ts +69 -69
  307. package/src/compat/segment/serve.ts +1884 -0
  308. package/src/compat/segment/tree.ts +113 -112
  309. package/src/compat/segment/vary-key.ts +38 -38
  310. package/src/compat/segment/vary-params.ts +138 -142
  311. package/src/compat/static-params.ts +14 -12
  312. package/src/compat/tsconfig-defaults.ts +87 -91
  313. package/src/compat/typecheck/{index.ts → check.ts} +234 -212
  314. package/src/compat/typecheck/worker.ts +15 -12
  315. package/src/compat/typed-routes/{index.ts → generate.ts} +36 -36
  316. package/src/compat/typed-routes/manifest.ts +174 -170
  317. package/src/compat/typed-routes/typegen.ts +127 -110
  318. package/src/compat/validation/errors.ts +16 -19
  319. package/src/compat/validation/prerender-diagnostics.ts +521 -504
  320. package/src/compat/validation/{index.ts → validate.ts} +647 -648
  321. package/src/compat-bootstrap.ts +16 -16
  322. package/src/config.ts +86 -69
  323. package/src/css/build.ts +230 -227
  324. package/src/css/postcss.ts +79 -80
  325. package/src/css/worker.ts +14 -15
  326. package/src/dev/client-actions.ts +10 -10
  327. package/src/dev/client-chunk-store.ts +27 -27
  328. package/src/dev/{client-key-cache.ts → restart/client-key.ts} +76 -76
  329. package/src/dev/{restart-cache.ts → restart/enabled.ts} +1 -1
  330. package/src/dev/{global-css-cache.ts → restart/global-css.ts} +91 -83
  331. package/src/dev/{node-module-bundle-cache.ts → restart/node-modules.ts} +24 -24
  332. package/src/dev/{route-bundle-key-cache.ts → restart/route-bundle-key.ts} +53 -53
  333. package/src/dev/{route-facts-cache.ts → restart/route-facts.ts} +82 -82
  334. package/src/dev/server.ts +804 -820
  335. package/src/env.ts +46 -43
  336. package/src/extensions.ts +491 -478
  337. package/src/index.ts +8 -8
  338. package/src/internal.ts +20 -23
  339. package/src/{islands → render}/boundary-error.ts +3 -3
  340. package/src/render/hooks.ts +71 -71
  341. package/src/render/island-context.ts +14 -14
  342. package/src/render/metadata.ts +310 -310
  343. package/src/{ppr-postpone.ts → render/postpone.ts} +5 -5
  344. package/src/{ppr.ts → render/ppr.ts} +244 -245
  345. package/src/render/renderer.ts +2087 -2082
  346. package/src/render/resource-hints.ts +16 -17
  347. package/src/render/slots.tsx +224 -235
  348. package/src/{islands → render}/static-children.ts +9 -12
  349. package/src/{islands → render}/static-slots.ts +37 -37
  350. package/src/{cache/context.ts → request/cache.ts} +20 -20
  351. package/src/request/context.ts +107 -107
  352. package/src/{dynamic/source.ts → resolve/dynamic.ts} +139 -139
  353. package/src/resolve/engine.ts +90 -77
  354. package/src/resolve/imports.ts +475 -463
  355. package/src/resolve/scan-facts.ts +318 -296
  356. package/src/resolve/source-text.ts +37 -37
  357. package/src/{dynamic → resolve}/tree-shake.ts +132 -128
  358. package/src/routing/forwarded.ts +19 -19
  359. package/src/routing/handler.ts +84 -91
  360. package/src/routing/href.ts +87 -70
  361. package/src/routing/{metadata.ts → metadata-files.ts} +403 -401
  362. package/src/{proxy.ts → routing/proxy.ts} +306 -312
  363. package/src/routing/{request-runtime.ts → request-environment.ts} +10 -10
  364. package/src/routing/routes.ts +827 -815
  365. package/src/routing/slots.ts +164 -160
  366. package/src/runtime/loader.ts +954 -0
  367. package/src/{dev → runtime}/module-cache.ts +309 -287
  368. package/src/{dev → runtime}/module-generations.ts +9 -9
  369. package/src/{dev → runtime}/module-transform.ts +81 -72
  370. package/src/{dev/imports.ts → runtime/modules.ts} +934 -847
  371. package/src/runtime/{server.ts → vendor-build.ts} +848 -1696
  372. package/src/runtime/vendor.ts +428 -406
  373. package/src/styles.d.ts +9 -0
  374. package/src/types.ts +324 -335
  375. package/src/utils/ansi.ts +5 -5
  376. package/src/utils/{source.ts → code.ts} +15 -12
  377. package/src/utils/content-type.ts +3 -3
  378. package/src/utils/decode.ts +2 -2
  379. package/src/utils/dev-profile.ts +13 -13
  380. package/src/utils/error-log.ts +6 -6
  381. package/src/utils/esbuild.ts +18 -18
  382. package/src/utils/fs-cache.ts +13 -13
  383. package/src/utils/fs.ts +57 -49
  384. package/src/utils/html.ts +20 -24
  385. package/src/utils/native-require.ts +8 -8
  386. package/src/utils/serialize.ts +139 -146
  387. package/src/utils/verbose.ts +18 -18
  388. package/reference/overview.md +0 -35
  389. package/src/cli/analyze-print.ts +0 -181
  390. package/src/compat/middleware/manifest.ts +0 -210
  391. package/src/compat/next/image-client.tsx +0 -215
  392. package/src/compat/next/link-usage.ts +0 -29
  393. package/src/css/index.ts +0 -2
  394. package/src/render/index.ts +0 -1
  395. package/src/style-modules.d.ts +0 -9
@@ -4,37 +4,37 @@
4
4
  * routing, runtime — ~700 KB of the bundle) is dynamically imported after the
5
5
  * port is bound and the ready banner is printed.
6
6
  */
7
- import { existsSync } from 'node:fs';
8
- import { mkdir, readFile, writeFile } from 'node:fs/promises';
9
- import { stat } from 'node:fs/promises';
10
- import path from 'node:path';
11
- import { constants as zlibConstants, createGzip, gzipSync } from 'node:zlib';
12
- import { Readable } from 'node:stream';
13
- import { loadConfig, pathToFileHref } from '../config';
14
- import type { ResolvedConfig } from '../config';
15
- import { bootstrapCompat } from '../compat-bootstrap';
16
- import { registerServerRuntime, serverBundleTargetForRuntime } from '../runtime/server';
17
- import { applyProxyResponse, createProxyRunner } from '../proxy';
7
+ import { existsSync } from 'node:fs'
8
+ import { mkdir, readFile, writeFile } from 'node:fs/promises'
9
+ import { stat } from 'node:fs/promises'
10
+ import path from 'node:path'
11
+ import { constants as zlibConstants, createGzip, gzipSync } from 'node:zlib'
12
+ import { Readable } from 'node:stream'
13
+ import { loadConfig, pathToFileHref } from '../../config'
14
+ import type { ResolvedConfig } from '../../config'
15
+ import { bootstrapCompat } from '../../compat-bootstrap'
16
+ import { registerServerRuntime, serverBundleTargetForRuntime } from '../../runtime/loader'
17
+ import { applyProxyResponse, createProxyRunner } from '../../routing/proxy'
18
18
  import {
19
19
  isPprShellUpgradeEligible,
20
20
  renderGlobalNotFoundResponse,
21
21
  renderPageResponse,
22
22
  renderPageWithStatus,
23
- } from '../render';
23
+ } from '../../render/renderer'
24
24
  import {
25
25
  handleRouteModule,
26
26
  routeParamsFromPath,
27
27
  type RouteHandlerModule,
28
- } from '../routing/handler';
28
+ } from '../../routing/handler'
29
29
  import {
30
30
  canonicalTrailingSlashPath,
31
31
  malformedUrlResponse,
32
32
  trailingSlashRedirect,
33
- } from '../routing/href';
34
- import { withForwardedHeaders } from '../routing/forwarded';
35
- import { normalizePathname, parseNavState, selectRouteForRequest } from '../routing/routes';
36
- import { runWithCacheScope } from '../cache/context';
37
- import { metadataRouteHandlerModule } from '../routing/metadata';
33
+ } from '../../routing/href'
34
+ import { withForwardedHeaders } from '../../routing/forwarded'
35
+ import { normalizePathname, parseNavState, selectRouteForRequest } from '../../routing/routes'
36
+ import { runWithCacheScope } from '../../request/cache'
37
+ import { metadataRouteHandlerModule } from '../../routing/metadata-files'
38
38
  import {
39
39
  finalizeResponse,
40
40
  getAssetExtensions,
@@ -45,7 +45,7 @@ import {
45
45
  runInitHooks,
46
46
  setRequestExtensions,
47
47
  withRouteRuntime,
48
- } from '../extensions';
48
+ } from '../../extensions'
49
49
  import {
50
50
  flushWorkUnit,
51
51
  flushWorkUnitOnClose,
@@ -53,28 +53,28 @@ import {
53
53
  runWithWorkUnit,
54
54
  setPhase,
55
55
  setWorkUnitRoute,
56
- } from '../request/context';
57
- import { setRequestRuntime } from '../routing/request-runtime';
58
- import { getRenderSpanExtensions } from '../render/hooks';
59
- import { contentType } from '../utils/content-type';
60
- import { stopEsbuildService } from '../utils/esbuild';
61
- import { markErrorLogged } from '../utils/error-log';
56
+ } from '../../request/context'
57
+ import { setRequestRuntime } from '../../routing/request-environment'
58
+ import { getRenderSpanExtensions } from '../../render/hooks'
59
+ import { contentType } from '../../utils/content-type'
60
+ import { stopEsbuildService } from '../../utils/esbuild'
61
+ import { markErrorLogged } from '../../utils/error-log'
62
62
  import type {
63
63
  BuildManifest,
64
64
  RouteManifestEntry,
65
65
  RouteParamValue,
66
66
  StaticFileMetadata,
67
- } from '../types';
67
+ } from '../../types'
68
68
 
69
69
  /** Compat module-mode route hrefs are the only prod use of the dev import layer; keep it off the start graph. */
70
70
  async function moduleHrefForRoute(
71
71
  config: ResolvedConfig,
72
72
  route: RouteManifestEntry,
73
73
  ): Promise<string> {
74
- const { devServerModuleHref } = await import('../dev/imports');
74
+ const { devServerModuleHref } = await import('../../runtime/modules')
75
75
  return devServerModuleHref(config, route.file, 'build', {
76
76
  conditionTarget: serverBundleTargetForRuntime(route.segmentConfig?.runtime),
77
- });
77
+ })
78
78
  }
79
79
 
80
80
  /**
@@ -88,35 +88,35 @@ export async function createRequestHandler(
88
88
  // `start` already resolved (and bootstrapped) the config for its output-mode
89
89
  // guard; reusing it skips a second loadConfig — env load, pnext.config +
90
90
  // next.config imports, app-path resolution — before the ready banner.
91
- const config = options.config ?? (await loadConfig(options.root));
91
+ const config = options.config ?? (await loadConfig(options.root))
92
92
  // Compat plugin loader: the single gated seam that populates the core
93
93
  // extension registries when compat is enabled (no-op for pure-core apps).
94
94
  // A prod server answers the very next request, so it takes both tiers here.
95
- await bootstrapCompat(config);
96
- const manifestPath = path.join(config.outPath, 'manifest.json');
95
+ await bootstrapCompat(config)
96
+ const manifestPath = path.join(config.outPath, 'manifest.json')
97
97
  // `start` reads the manifest before binding the port (a missing build must
98
98
  // still fail there, not on the first request) and hands it over.
99
99
  const manifest =
100
- options.manifest ?? (JSON.parse(await readFile(manifestPath, 'utf8')) as BuildManifest);
101
- const persistManifest = () => writeFile(manifestPath, `${JSON.stringify(manifest, null, 2)}\n`);
100
+ options.manifest ?? (JSON.parse(await readFile(manifestPath, 'utf8')) as BuildManifest)
101
+ const persistManifest = () => writeFile(manifestPath, `${JSON.stringify(manifest, null, 2)}\n`)
102
102
  // Compat action-registry arming + serverActions.bodySizeLimit resolution +
103
103
  // fetch-cache install now run through the extension registry: the action
104
104
  // dispatch interceptor rebuilds the registry from manifest.actions on the
105
105
  // first action request, and runInitHooks installs the Next fetch-cache patch
106
106
  // so runtime renders observe force-cache / revalidate TTLs / tags exactly
107
107
  // like build prerenders. No-op for pure-core apps.
108
- runInitHooks(config);
108
+ runInitHooks(config)
109
109
  // Publish the live routing state the compat request interceptors (action
110
110
  // dispatch, rewrites) read; prod loads the route table once.
111
- setRequestRuntime({ config, routes: manifest.routes, dev: false });
111
+ setRequestRuntime({ config, routes: manifest.routes, dev: false })
112
112
  const proxyRunner = createProxyRunner(config, {
113
113
  compiledModuleHref: manifest.proxyModule
114
114
  ? pathToFileHref(path.resolve(config.outPath, manifest.proxyModule))
115
115
  : undefined,
116
- });
117
- const routesById = new Map(manifest.routes.map(route => [route.id, route]));
118
- const nextCompat = Boolean(config.compat?.next);
119
- const regenerating = new Map<string, Promise<void>>();
116
+ })
117
+ const routesById = new Map(manifest.routes.map(route => [route.id, route]))
118
+ const nextCompat = Boolean(config.compat?.next)
119
+ const regenerating = new Map<string, Promise<void>>()
120
120
 
121
121
  /**
122
122
  * ISR background regeneration (stale-while-revalidate): re-render the route
@@ -131,22 +131,22 @@ export async function createRequestHandler(
131
131
  metadata: StaticFileMetadata,
132
132
  reason: 'stale' | 'on-demand',
133
133
  ) {
134
- if (!metadata.routeId) return;
135
- const route = routesById.get(metadata.routeId);
136
- if (!route) return;
134
+ if (!metadata.routeId) return
135
+ const route = routesById.get(metadata.routeId)
136
+ if (!route) return
137
137
  await runWithWorkUnit('render', async () => {
138
- const url = new URL(`http://pnext.local${pathname}`);
139
- const params = routeParamsFromPath(route, pathname);
140
- const fetchCache = route.segmentConfig?.fetchCache;
138
+ const url = new URL(`http://pnext.local${pathname}`)
139
+ const params = routeParamsFromPath(route, pathname)
140
+ const fetchCache = route.segmentConfig?.fetchCache
141
141
  try {
142
142
  if (route.kind === 'handler') {
143
- setWorkUnitRoute('route-handler', 'isr', { revalidateReason: reason });
144
- setPhase('handler');
145
- registerServerRuntime(config, route.sourceFiles);
143
+ setWorkUnitRoute('route-handler', 'isr', { revalidateReason: reason })
144
+ setPhase('handler')
145
+ registerServerRuntime(config, route.sourceFiles)
146
146
  const href = compatModuleMode(config)
147
147
  ? await moduleHrefForRoute(config, route)
148
- : pathToFileHref(route.file);
149
- const module = (await import(href)) as Parameters<typeof handleRouteModule>[0];
148
+ : pathToFileHref(route.file)
149
+ const module = (await import(href)) as Parameters<typeof handleRouteModule>[0]
150
150
  const rendered = await getRenderExtensions().collectRenderMeta(
151
151
  () =>
152
152
  withRouteRuntime(route.segmentConfig?.runtime, () =>
@@ -155,18 +155,18 @@ export async function createRequestHandler(
155
155
  ),
156
156
  ),
157
157
  { fetchCache, blockingStaleFetches: true, route: pathname, handler: true },
158
- );
159
- if (rendered.value.status >= 500) return;
160
- await writeFile(file, new Uint8Array(await rendered.value.arrayBuffer()));
158
+ )
159
+ if (rendered.value.status >= 500) return
160
+ await writeFile(file, new Uint8Array(await rendered.value.arrayBuffer()))
161
161
  manifest.staticFiles![relative] = {
162
162
  ...metadata,
163
163
  status: rendered.value.status,
164
164
  headers: [...rendered.value.headers.entries()],
165
165
  ...(rendered.tags.length > 0 ? { tags: rendered.tags } : {}),
166
- };
166
+ }
167
167
  } else {
168
- setWorkUnitRoute('html', 'isr', { revalidateReason: reason });
169
- setPhase('render');
168
+ setWorkUnitRoute('html', 'isr', { revalidateReason: reason })
169
+ setPhase('render')
170
170
  const rendered = await getRenderExtensions().collectRenderMeta(
171
171
  () =>
172
172
  withRouteRuntime(route.segmentConfig?.runtime, () =>
@@ -182,20 +182,20 @@ export async function createRequestHandler(
182
182
  }),
183
183
  ),
184
184
  { fetchCache, blockingStaleFetches: true, route: pathname },
185
- );
185
+ )
186
186
  // A failed regeneration (render threw → 5xx) must not overwrite the
187
187
  // good prebuilt copy: Next discards the error and keeps serving the
188
188
  // stale page (the error is still logged via the renderer's funnel).
189
- if (rendered.value.status >= 500) return;
190
- await writeFile(file, rendered.value.html);
189
+ if (rendered.value.status >= 500) return
190
+ await writeFile(file, rendered.value.html)
191
191
  manifest.staticFiles![relative] = {
192
192
  ...metadata,
193
193
  status: rendered.value.status,
194
194
  ...(rendered.tags.length > 0 ? { tags: rendered.tags } : {}),
195
- };
195
+ }
196
196
  }
197
197
  } catch (error) {
198
- const unit = getWorkUnit();
198
+ const unit = getWorkUnit()
199
199
  await reportRequestError(
200
200
  error,
201
201
  { method: 'GET', url: url.href, headers: new Headers() },
@@ -204,10 +204,10 @@ export async function createRequestHandler(
204
204
  routeKind: unit?.routeKind,
205
205
  ...(unit?.responseHints ?? {}),
206
206
  },
207
- );
208
- throw error;
207
+ )
208
+ throw error
209
209
  }
210
- });
210
+ })
211
211
  }
212
212
 
213
213
  function runRegeneration(
@@ -217,17 +217,17 @@ export async function createRequestHandler(
217
217
  metadata: StaticFileMetadata,
218
218
  reason: 'stale' | 'on-demand',
219
219
  ) {
220
- const existing = regenerating.get(file);
221
- if (existing) return existing;
220
+ const existing = regenerating.get(file)
221
+ if (existing) return existing
222
222
  const task = regenerateStaticFile(pathname, file, relative, metadata, reason)
223
223
  .catch(error => {
224
- console.warn(`pnext start: background revalidate failed for ${pathname}:`, error);
224
+ console.warn(`pnext start: background revalidate failed for ${pathname}:`, error)
225
225
  })
226
226
  .finally(() => {
227
- regenerating.delete(file);
228
- });
229
- regenerating.set(file, task);
230
- return task;
227
+ regenerating.delete(file)
228
+ })
229
+ regenerating.set(file, task)
230
+ return task
231
231
  }
232
232
 
233
233
  function scheduleRegen(
@@ -236,16 +236,16 @@ export async function createRequestHandler(
236
236
  relative: string,
237
237
  metadata: StaticFileMetadata,
238
238
  ) {
239
- void runRegeneration(pathname, file, relative, metadata, 'stale');
239
+ void runRegeneration(pathname, file, relative, metadata, 'stale')
240
240
  }
241
241
 
242
242
  setRequestExtensions({
243
243
  onDemandRevalidatePath: async pathname => {
244
- const built = await builtFileInfo(config.outPath, pathname, manifest.staticFiles);
245
- if (!built?.metadata) return;
246
- await runRegeneration(pathname, built.file, built.relative, built.metadata, 'on-demand');
244
+ const built = await builtFileInfo(config.outPath, pathname, manifest.staticFiles)
245
+ if (!built?.metadata) return
246
+ await runRegeneration(pathname, built.file, built.relative, built.metadata, 'on-demand')
247
247
  },
248
- });
248
+ })
249
249
 
250
250
  async function renderNotFound(request: Request): Promise<Response> {
251
251
  return compressResponse(
@@ -258,20 +258,20 @@ export async function createRequestHandler(
258
258
  staticRouteMetadata: manifest.staticRouteMetadata,
259
259
  }),
260
260
  request,
261
- );
261
+ )
262
262
  }
263
263
 
264
264
  // The boot config compile is the only build work a prod server does; drop the
265
265
  // resident esbuild service child (~10+ MB RSS) — it respawns if ever needed.
266
- stopEsbuildService();
266
+ stopEsbuildService()
267
267
 
268
268
  return function handleRequest(request: Request): Promise<Response> {
269
269
  // One work unit spans the whole request; its after-queue flushes once the
270
270
  // response fully closes (stream end, redirect, notFound, error, abort).
271
271
  return runWithWorkUnit('render', async () => {
272
- const unit = getWorkUnit();
272
+ const unit = getWorkUnit()
273
273
  try {
274
- const raw = await handle(request);
274
+ const raw = await handle(request)
275
275
  const finalized = await finalizeResponse(
276
276
  raw,
277
277
  { method: request.method, url: new URL(request.url), headers: request.headers },
@@ -280,9 +280,9 @@ export async function createRequestHandler(
280
280
  routeMode: unit?.routeMode,
281
281
  hints: unit?.responseHints,
282
282
  },
283
- );
284
- const response = maybeCloseNodeFetchConnection(finalized, request);
285
- return flushWorkUnitOnClose(response, unit, request.signal);
283
+ )
284
+ const response = maybeCloseNodeFetchConnection(finalized, request)
285
+ return flushWorkUnitOnClose(response, unit, request.signal)
286
286
  } catch (error) {
287
287
  // A dropped promise here would close the socket without a response
288
288
  // (client sees "socket hang up"); surface a 500 instead. The error
@@ -291,74 +291,74 @@ export async function createRequestHandler(
291
291
  error,
292
292
  { method: request.method, url: request.url, headers: request.headers },
293
293
  { phase: unit?.phase, routeKind: unit?.routeKind },
294
- );
294
+ )
295
295
  // Dedupe: the renderer may have already logged this SSR error inline
296
296
  // before it propagated here. Log once per error object across log sites.
297
297
  if (markErrorLogged(error)) {
298
- console.error(`pnext start: request failed for ${request.url}:`, error);
298
+ console.error(`pnext start: request failed for ${request.url}:`, error)
299
299
  }
300
- flushWorkUnit(unit);
301
- return new Response('Internal Server Error', { status: 500 });
300
+ flushWorkUnit(unit)
301
+ return new Response('Internal Server Error', { status: 500 })
302
302
  }
303
- });
304
- };
303
+ })
304
+ }
305
305
 
306
306
  async function handle(request: Request): Promise<Response> {
307
- const badRequest = malformedUrlResponse(request);
308
- if (badRequest) return badRequest;
309
- request = withForwardedHeaders(request);
310
- const requestedUrl = new URL(request.url);
307
+ const badRequest = malformedUrlResponse(request)
308
+ if (badRequest) return badRequest
309
+ request = withForwardedHeaders(request)
310
+ const requestedUrl = new URL(request.url)
311
311
  // assetPrefix is independent from basePath. Strip a path-style asset
312
312
  // prefix first so `/cdn/_next/static/*` remains servable even when the app
313
313
  // itself lives under a different basePath.
314
- const assetStripped = stripAssetPrefix(request, config.assetPrefix);
315
- if (assetStripped) request = assetStripped;
314
+ const assetStripped = stripAssetPrefix(request, config.assetPrefix)
315
+ if (assetStripped) request = assetStripped
316
316
  // basePath: strip the configured prefix so all downstream routing, static
317
317
  // lookup, and handler context see the app-relative path (Next serves the
318
318
  // app under basePath; `usePathname`/`req.nextUrl.pathname` exclude it). A
319
319
  // request that does not carry the prefix is outside the app → 404.
320
320
  if (config.basePath && !assetStripped) {
321
- const stripped = stripBasePath(request, config.basePath);
321
+ const stripped = stripBasePath(request, config.basePath)
322
322
  if (!stripped) {
323
323
  // Outside the basePath: only a rule that opted out of it (next.config's
324
324
  // `basePath: false` rewrites/redirects) may answer. If one rewrites the
325
325
  // request back into the app, re-apply the strip; otherwise this is a 404
326
326
  // exactly as before.
327
- const answered = await runOutsideBasePathInterceptors(request, config);
328
- if (answered instanceof Response) return answered;
329
- const rewritten = answered && stripBasePath(answered.request, config.basePath);
330
- if (!rewritten) return await renderNotFound(request);
331
- request = rewritten;
327
+ const answered = await runOutsideBasePathInterceptors(request, config)
328
+ if (answered instanceof Response) return answered
329
+ const rewritten = answered && stripBasePath(answered.request, config.basePath)
330
+ if (!rewritten) return await renderNotFound(request)
331
+ request = rewritten
332
332
  } else {
333
- request = stripped;
333
+ request = stripped
334
334
  }
335
335
  }
336
336
  // `/_next/data/<buildId>/<page>.json` is the pages-router data protocol:
337
337
  // Next serves it as a request for the PAGE path (trailing slash applied),
338
338
  // normalized BEFORE middleware so the handler sees the page URL — or, with
339
339
  // skipMiddlewareUrlNormalize, after it (middleware sees the raw data URL).
340
- const skipProxyNormalize = getProxyExtensions().skipUrlNormalize();
340
+ const skipProxyNormalize = getProxyExtensions().skipUrlNormalize()
341
341
  if (nextCompat && !skipProxyNormalize) {
342
- request = normalizeDataRequest(request, config) ?? request;
342
+ request = normalizeDataRequest(request, config) ?? request
343
343
  }
344
344
  // A request authorized by the preview-mode id is Next's on-demand
345
345
  // revalidation: middleware never runs for it and the route re-renders
346
346
  // fresh below (the static fast path is skipped).
347
- const bypassToken = getRequestExtensions().revalidateBypassToken();
347
+ const bypassToken = getRequestExtensions().revalidateBypassToken()
348
348
  const revalidateBypass = Boolean(
349
349
  bypassToken && request.headers.get('x-prerender-revalidate') === bypassToken,
350
- );
351
- const canonicalUrl = new URL(request.url);
352
- const canonicalHeaders = request.headers;
353
- let proxyResponse: Response | undefined;
354
- const proxyResult = revalidateBypass ? undefined : await proxyRunner(request);
355
- if (proxyResult instanceof Response) return proxyResult;
350
+ )
351
+ const canonicalUrl = new URL(request.url)
352
+ const canonicalHeaders = request.headers
353
+ let proxyResponse: Response | undefined
354
+ const proxyResult = revalidateBypass ? undefined : await proxyRunner(request)
355
+ if (proxyResult instanceof Response) return proxyResult
356
356
  if (proxyResult) {
357
- request = proxyResult.request;
358
- proxyResponse = proxyResult.response;
357
+ request = proxyResult.request
358
+ proxyResponse = proxyResult.response
359
359
  }
360
360
  if (nextCompat && skipProxyNormalize) {
361
- request = normalizeDataRequest(request, config) ?? request;
361
+ request = normalizeDataRequest(request, config) ?? request
362
362
  }
363
363
 
364
364
  // The compat request interceptors run after the proxy, before route
@@ -370,38 +370,38 @@ export async function createRequestHandler(
370
370
  // use the (possibly rewritten) url. Pure-core apps register no
371
371
  // interceptors (a no-op).
372
372
  for (const interceptor of getRequestExtensions().interceptors) {
373
- const result = await interceptor(request, { config });
373
+ const result = await interceptor(request, { config })
374
374
  // Interceptor responses (segment/flight payloads, action results) sit in
375
375
  // front of Next's compression middleware too — run them through the same
376
376
  // gzip/Vary negotiation as rendered pages.
377
377
  if (result instanceof Response)
378
- return applyProxyResponse(compressResponse(result, request), proxyResponse);
379
- if (result) request = result.request;
378
+ return applyProxyResponse(compressResponse(result, request), proxyResponse)
379
+ if (result) request = result.request
380
380
  }
381
- let url = new URL(request.url);
382
- const rewritten = url.href !== canonicalUrl.href;
381
+ let url = new URL(request.url)
382
+ const rewritten = url.href !== canonicalUrl.href
383
383
  // Unlike pathname, Next DOES thread a rewrite's destination query into the page's `searchParams`
384
384
  // prop - only the address bar and usePathname() stay on the as-requested path - for both
385
385
  // middleware and next.config rewrites. So the canonical request keeps the original pathname but
386
386
  // picks up whatever search ended up on the fully-resolved request.
387
- const canonicalRequestUrl = new URL(canonicalUrl);
388
- canonicalRequestUrl.search = url.search;
387
+ const canonicalRequestUrl = new URL(canonicalUrl)
388
+ canonicalRequestUrl.search = url.search
389
389
  const canonicalRequest =
390
390
  canonicalRequestUrl.href === request.url
391
391
  ? request
392
- : new Request(canonicalRequestUrl, { headers: canonicalHeaders });
392
+ : new Request(canonicalRequestUrl, { headers: canonicalHeaders })
393
393
 
394
- const method = request.method.toUpperCase();
394
+ const method = request.method.toUpperCase()
395
395
  // Trailing-slash normalization applies to the browser-visible (as-requested)
396
396
  // URL, never to an internal rewrite destination. A middleware/next.config
397
397
  // rewrite to `/en/` must render internally, not 308 the visitor to `/en`.
398
- const canonicalRedirect = trailingSlashRedirect(config, requestedUrl, method);
399
- if (canonicalRedirect) return applyProxyResponse(canonicalRedirect, proxyResponse);
398
+ const canonicalRedirect = trailingSlashRedirect(config, requestedUrl, method)
399
+ if (canonicalRedirect) return applyProxyResponse(canonicalRedirect, proxyResponse)
400
400
  // Soft navigations carry the client's parallel-route state; when the
401
401
  // target involves slots, interception, or a host render, the response is
402
402
  // state-dependent and must render dynamically (never from prebuilt html).
403
- const nav = parseNavState(request);
404
- const selection = selectRouteForRequest(manifest.routes, url.pathname, nav);
403
+ const nav = parseNavState(request)
404
+ const selection = selectRouteForRequest(manifest.routes, url.pathname, nav)
405
405
  const softDynamic = Boolean(
406
406
  nav &&
407
407
  selection?.route.kind === 'page' &&
@@ -409,30 +409,25 @@ export async function createRequestHandler(
409
409
  selection.route.synthetic ||
410
410
  selection.route.interception ||
411
411
  selection.childrenPath !== normalizePathname(url.pathname)),
412
- );
412
+ )
413
413
  // PPR shells carry an RDC sidecar and must resume through the renderer.
414
414
  // Their persisted HTML is only the shell; serving it directly skips the
415
415
  // dynamic continuation (and its RDC seed). Non-PPR static pages retain the
416
416
  // normal static-file fast path.
417
- const pprDocumentResume = selection?.route.kind === 'page' && selection.route.ppr;
417
+ const pprDocumentResume = selection?.route.kind === 'page' && selection.route.ppr
418
418
  // Draft mode: a __prerender_bypass cookie skips prebuilt page html so the
419
419
  // request falls through to a fresh dynamic render below; non-page assets
420
420
  // still serve statically and the stale-page write-back is disabled so a
421
421
  // draft render never overwrites the prerendered copy.
422
- const draftBypass = hasDraftBypassCookie(request.headers);
422
+ const draftBypass = hasDraftBypassCookie(request.headers)
423
423
  // revalidatePath()/revalidateTag() mark prebuilt output stale: skip the static copy (even on a
424
424
  // matching etag - the bytes are stale) and fall through to a fresh blocking render. TTL expiry is
425
425
  // served stale-while-revalidate instead: the stale copy goes out while a background regeneration
426
426
  // rewrites it.
427
427
  const built =
428
428
  method === 'GET' || method === 'HEAD'
429
- ? await builtFileInfo(
430
- config.outPath,
431
- url.pathname,
432
- manifest.staticFiles,
433
- nextCompat,
434
- )
435
- : null;
429
+ ? await builtFileInfo(config.outPath, url.pathname, manifest.staticFiles, nextCompat)
430
+ : null
436
431
  const hardStaticStale =
437
432
  built !== null &&
438
433
  // Content-hashed build assets are immutable and are never route outputs, so a
@@ -445,17 +440,16 @@ export async function createRequestHandler(
445
440
  url.pathname,
446
441
  built.mtimeMs,
447
442
  built.metadata?.tags ?? [],
448
- );
449
- const staleReason =
450
- built
451
- ? (getRequestExtensions().staticStalenessReason(
452
- url.pathname,
453
- built.mtimeMs,
454
- built.metadata?.tags ?? [],
455
- ) ?? 'on-demand')
456
- : undefined;
457
- const softStale = staleReason === 'soft';
458
- const staleOnDemand = hardStaticStale && !softStale;
443
+ )
444
+ const staleReason = built
445
+ ? (getRequestExtensions().staticStalenessReason(
446
+ url.pathname,
447
+ built.mtimeMs,
448
+ built.metadata?.tags ?? [],
449
+ ) ?? 'on-demand')
450
+ : undefined
451
+ const softStale = staleReason === 'soft'
452
+ const staleOnDemand = hardStaticStale && !softStale
459
453
  // A `use cache` hard-expiry (cacheLife expire) elapsed: the stale copy is no
460
454
  // longer servable — fall through to a fresh blocking render (unlike ISR/SWR,
461
455
  // which serves stale while regenerating). Only page HTML is regenerated
@@ -463,12 +457,12 @@ export async function createRequestHandler(
463
457
  const hardExpired =
464
458
  built?.metadata?.expireSeconds !== undefined &&
465
459
  isHtmlPageFile(built.file) &&
466
- Date.now() - built.mtimeMs >= built.metadata.expireSeconds * 1000;
460
+ Date.now() - built.mtimeMs >= built.metadata.expireSeconds * 1000
467
461
  // Route-shape bypasses (a PPR page resumes through the renderer; a soft navigation with
468
462
  // parallel-slot state re-renders) only apply when the built file IS the page's HTML. A plain ASSET
469
463
  // whose path merely matches a page route pattern must always serve statically - rendering the
470
464
  // route for it hands the browser HTML for a module script.
471
- const builtIsPageHtml = built !== null && isHtmlPageFile(built.file);
465
+ const builtIsPageHtml = built !== null && isHtmlPageFile(built.file)
472
466
  if (
473
467
  built &&
474
468
  !staleOnDemand &&
@@ -477,14 +471,14 @@ export async function createRequestHandler(
477
471
  !hardExpired &&
478
472
  !revalidateBypass
479
473
  ) {
480
- const servedBuilt = built;
481
- const metadata = built.metadata;
482
- const route = metadata?.routeId ? routesById.get(metadata.routeId) : undefined;
474
+ const servedBuilt = built
475
+ const metadata = built.metadata
476
+ const route = metadata?.routeId ? routesById.get(metadata.routeId) : undefined
483
477
  if (
484
478
  route?.segmentConfig?.runtime === 'edge' ||
485
479
  route?.segmentConfig?.runtime === 'experimental-edge'
486
480
  ) {
487
- withRouteRuntime(route.segmentConfig.runtime, () => undefined);
481
+ withRouteRuntime(route.segmentConfig.runtime, () => undefined)
488
482
  }
489
483
  // A stale static handler body keeps stale-while-revalidate semantics: the generic serving path
490
484
  // below serves the stale bytes immediately with an x-nextjs-cache: STALE marker and schedules
@@ -497,51 +491,50 @@ export async function createRequestHandler(
497
491
  request.method,
498
492
  request.headers,
499
493
  nextCompat,
500
- );
494
+ )
501
495
  if (staticFile && !(draftBypass && isHtmlResponse(staticFile))) {
502
- const staticMode = servedBuilt.metadata?.revalidateSeconds !== undefined ? 'isr' : 'static';
496
+ const staticMode = servedBuilt.metadata?.revalidateSeconds !== undefined ? 'isr' : 'static'
503
497
  setWorkUnitRoute(
504
498
  isHtmlResponse(staticFile) ? 'html' : 'static-asset',
505
499
  staticMode,
506
500
  servedBuilt.metadata?.revalidateSeconds !== undefined
507
501
  ? { revalidateSeconds: servedBuilt.metadata.revalidateSeconds }
508
502
  : undefined,
509
- );
503
+ )
510
504
  if (servedBuilt.metadata) {
511
- const servedTtl = servedBuilt.metadata.revalidateSeconds;
505
+ const servedTtl = servedBuilt.metadata.revalidateSeconds
512
506
  const expired =
513
507
  softStale ||
514
- (servedTtl !== undefined &&
515
- Date.now() - servedBuilt.mtimeMs >= servedTtl * 1000);
508
+ (servedTtl !== undefined && Date.now() - servedBuilt.mtimeMs >= servedTtl * 1000)
516
509
  if (expired) {
517
510
  scheduleRegen(
518
511
  url.pathname,
519
512
  servedBuilt.file,
520
513
  servedBuilt.relative,
521
514
  servedBuilt.metadata,
522
- );
523
- staticFile.headers.set('x-nextjs-cache', 'STALE');
515
+ )
516
+ staticFile.headers.set('x-nextjs-cache', 'STALE')
524
517
  } else {
525
- staticFile.headers.set('x-nextjs-cache', 'HIT');
518
+ staticFile.headers.set('x-nextjs-cache', 'HIT')
526
519
  }
527
520
  }
528
- return applyProxyResponse(compressResponse(staticFile, request), proxyResponse);
521
+ return applyProxyResponse(compressResponse(staticFile, request), proxyResponse)
529
522
  }
530
523
  }
531
524
  if (url.pathname.startsWith('/_next/static/') && !built) {
532
- setWorkUnitRoute('static-asset', 'static');
525
+ setWorkUnitRoute('static-asset', 'static')
533
526
  return applyProxyResponse(
534
527
  new Response('Not Found', {
535
528
  status: 404,
536
529
  headers: { 'content-type': 'text/plain' },
537
530
  }),
538
531
  proxyResponse,
539
- );
532
+ )
540
533
  }
541
534
  const stalePage =
542
- built && (staleOnDemand || hardExpired) && isHtmlPageFile(built.file) ? built.file : null;
535
+ built && (staleOnDemand || hardExpired) && isHtmlPageFile(built.file) ? built.file : null
543
536
 
544
- const matched = selection;
537
+ const matched = selection
545
538
  if (!matched) {
546
539
  return applyProxyResponse(
547
540
  compressResponse(
@@ -556,15 +549,15 @@ export async function createRequestHandler(
556
549
  request,
557
550
  ),
558
551
  proxyResponse,
559
- );
552
+ )
560
553
  }
561
554
  if (matched.route.kind === 'handler') {
562
555
  setWorkUnitRoute(
563
556
  'route-handler',
564
557
  staleOnDemand ? 'isr' : routeModeOf(matched.route),
565
558
  responseHintsFor(matched.route, staleReason),
566
- );
567
- setPhase('handler');
559
+ )
560
+ setPhase('handler')
568
561
  // On-demand revalidated renders refetch their data caches (Next's
569
562
  // isOnDemandRevalidate semantics) and persist the fresh bytes so
570
563
  // subsequent requests serve the regenerated static copy again.
@@ -576,8 +569,8 @@ export async function createRequestHandler(
576
569
  route: url.pathname,
577
570
  handler: true,
578
571
  },
579
- );
580
- const handlerResponse = renderedHandler.value;
572
+ )
573
+ const handlerResponse = renderedHandler.value
581
574
  if (
582
575
  built &&
583
576
  staleOnDemand &&
@@ -585,8 +578,8 @@ export async function createRequestHandler(
585
578
  method === 'GET' &&
586
579
  handlerResponse.status === 200
587
580
  ) {
588
- await writeFile(built.file, new Uint8Array(await handlerResponse.clone().arrayBuffer()));
589
- handlerResponse.headers.set('x-nextjs-cache', 'MISS');
581
+ await writeFile(built.file, new Uint8Array(await handlerResponse.clone().arrayBuffer()))
582
+ handlerResponse.headers.set('x-nextjs-cache', 'MISS')
590
583
  }
591
584
  if (
592
585
  !built &&
@@ -599,34 +592,34 @@ export async function createRequestHandler(
599
592
  method === 'GET' &&
600
593
  handlerResponse.status < 500
601
594
  ) {
602
- const file = lazyStaticHandlerPath(config.outPath, url.pathname);
595
+ const file = lazyStaticHandlerPath(config.outPath, url.pathname)
603
596
  if (file) {
604
- await mkdir(path.dirname(file), { recursive: true });
605
- await writeFile(file, new Uint8Array(await handlerResponse.clone().arrayBuffer()));
597
+ await mkdir(path.dirname(file), { recursive: true })
598
+ await writeFile(file, new Uint8Array(await handlerResponse.clone().arrayBuffer()))
606
599
  const relative = path
607
600
  .relative(path.join(config.outPath, 'public'), file)
608
601
  .split(path.sep)
609
- .join('/');
610
- const routeRevalidate = matched.route.segmentConfig?.revalidate;
602
+ .join('/')
603
+ const routeRevalidate = matched.route.segmentConfig?.revalidate
611
604
  const revalidateSeconds =
612
605
  typeof routeRevalidate === 'number' && routeRevalidate > 0
613
606
  ? renderedHandler.revalidateSeconds === undefined
614
607
  ? routeRevalidate
615
608
  : Math.min(routeRevalidate, renderedHandler.revalidateSeconds)
616
- : renderedHandler.revalidateSeconds;
617
- manifest.staticFiles ??= {};
609
+ : renderedHandler.revalidateSeconds
610
+ manifest.staticFiles ??= {}
618
611
  manifest.staticFiles[relative] = {
619
612
  status: handlerResponse.status,
620
613
  headers: [...handlerResponse.headers.entries()],
621
614
  routeId: matched.route.id,
622
615
  ...(revalidateSeconds !== undefined ? { revalidateSeconds } : {}),
623
616
  ...(renderedHandler.tags.length > 0 ? { tags: renderedHandler.tags } : {}),
624
- };
625
- await persistManifest();
626
- handlerResponse.headers.set('x-nextjs-cache', 'MISS');
617
+ }
618
+ await persistManifest()
619
+ handlerResponse.headers.set('x-nextjs-cache', 'MISS')
627
620
  }
628
621
  }
629
- return applyProxyResponse(handlerResponse, proxyResponse);
622
+ return applyProxyResponse(handlerResponse, proxyResponse)
630
623
  }
631
624
 
632
625
  // dynamic = 'error': request-data access is a hard error instead of a
@@ -638,7 +631,7 @@ export async function createRequestHandler(
638
631
  { status: 500 },
639
632
  ),
640
633
  proxyResponse,
641
- );
634
+ )
642
635
  }
643
636
 
644
637
  // dynamicParams=false: dynamic segments governed by it must resolve to a
@@ -658,15 +651,15 @@ export async function createRequestHandler(
658
651
  request,
659
652
  ),
660
653
  proxyResponse,
661
- );
654
+ )
662
655
  }
663
656
 
664
657
  // force-static routes render with an empty request and no search params
665
658
  // even when served dynamically (Next returns empty cookies/headers/params).
666
- const forceStaticRoute = matched.route.segmentConfig?.dynamic === 'force-static';
659
+ const forceStaticRoute = matched.route.segmentConfig?.dynamic === 'force-static'
667
660
  const renderUrl = forceStaticRoute
668
661
  ? new URL(canonicalUrl.pathname, canonicalUrl.origin)
669
- : canonicalRequestUrl;
662
+ : canonicalRequestUrl
670
663
  // Non-action POSTs to a page render the page like Next's MPA fallback (a
671
664
  // plain <form method="POST"> submit, or a browser following a 307/308 from
672
665
  // a route handler) instead of a 405.
@@ -674,13 +667,13 @@ export async function createRequestHandler(
674
667
  ? new Request(renderUrl)
675
668
  : method === 'POST'
676
669
  ? new Request(canonicalRequest.url, { headers: canonicalRequest.headers })
677
- : canonicalRequest;
678
- const pageRevalidateReason = stalePage ? staleReason : hardExpired ? 'on-demand' : undefined;
670
+ : canonicalRequest
671
+ const pageRevalidateReason = stalePage ? staleReason : hardExpired ? 'on-demand' : undefined
679
672
  setWorkUnitRoute(
680
673
  'html',
681
674
  pageRevalidateReason ? 'isr' : routeModeOf(matched.route),
682
675
  responseHintsFor(matched.route, pageRevalidateReason),
683
- );
676
+ )
684
677
  const renderMatchedPage = () =>
685
678
  withRouteRuntime(matched.route.segmentConfig?.runtime, () =>
686
679
  renderPageResponse({
@@ -702,7 +695,7 @@ export async function createRequestHandler(
702
695
  }
703
696
  : {}),
704
697
  }),
705
- );
698
+ )
706
699
  // Every render carries a cache-meta scope: the route's fetchCache config
707
700
  // drives fetch cache-mode defaults; on-demand revalidated renders also
708
701
  // refetch their data caches (Next's isOnDemandRevalidate semantics).
@@ -710,12 +703,12 @@ export async function createRequestHandler(
710
703
  fetchCache: matched.route.segmentConfig?.fetchCache,
711
704
  refreshFetches: Boolean(stalePage),
712
705
  route: url.pathname,
713
- });
706
+ })
714
707
  const renderRevalidateSeconds =
715
708
  renderedPage.revalidateSeconds ??
716
709
  (typeof matched.route.segmentConfig?.revalidate === 'number'
717
710
  ? matched.route.segmentConfig.revalidate
718
- : undefined);
711
+ : undefined)
719
712
  // Only a POSITIVE revalidate window is ISR: `revalidate = 0` is Next's fully-dynamic opt-out
720
713
  // (never cached, dynamic staleTime). Promoting a 0-second window to 'isr' handed the route the
721
714
  // STATIC client staleTime via the segment finalizer, so a soft push to an already-visited dynamic
@@ -727,32 +720,32 @@ export async function createRequestHandler(
727
720
  // so after a server action revalidated the path the browser replayed the pre-revalidation payload.
728
721
  // A `segmentConfig.revalidate > 0` route is already classified 'isr' by routeModeOf above.
729
722
  const renderWasDynamic =
730
- matched.route.mode === 'dynamic' || matched.route.usesRequest || renderedPage.noStore === true;
723
+ matched.route.mode === 'dynamic' || matched.route.usesRequest || renderedPage.noStore === true
731
724
  if (renderRevalidateSeconds !== undefined && renderRevalidateSeconds > 0 && !renderWasDynamic) {
732
725
  setWorkUnitRoute('html', 'isr', {
733
726
  ...(getWorkUnit()?.responseHints ?? {}),
734
727
  revalidateSeconds: renderRevalidateSeconds,
735
- });
728
+ })
736
729
  }
737
- const pageResponse = renderedPage.value;
730
+ const pageResponse = renderedPage.value
738
731
 
739
732
  // A PPR resume replays the prebuilt shell without re-running the
740
733
  // components whose react-dom preload()/font hints produced the prerender's
741
734
  // `Link` header — re-emit the build-captured value (Next serves the
742
735
  // prerender's stored headers the same way).
743
736
  if (matched.route.linkHeader && !pageResponse.headers.has('link')) {
744
- pageResponse.headers.set('link', matched.route.linkHeader);
737
+ pageResponse.headers.set('link', matched.route.linkHeader)
745
738
  }
746
739
 
747
740
  // A prebuilt `use cache` page never re-runs its cache scopes, so the render
748
741
  // response finalizer can't stamp its SWR cache-control / x-nextjs-stale-time
749
742
  // (for PPR routes the cacheLife is stashed on the shell-resume work unit, not
750
743
  // this one). Re-emit them from the route's build-captured cacheLife.
751
- const cacheLifeHeaders = cacheLifeResponseHeaders(matched.route.cacheLife);
744
+ const cacheLifeHeaders = cacheLifeResponseHeaders(matched.route.cacheLife)
752
745
  if (cacheLifeHeaders.length > 0 && matched.route.kind === 'page') {
753
746
  for (const [key, value] of cacheLifeHeaders) {
754
- if (key === 'cache-control' && pageResponse.headers.has('cache-control')) continue;
755
- pageResponse.headers.set(key, value);
747
+ if (key === 'cache-control' && pageResponse.headers.has('cache-control')) continue
748
+ pageResponse.headers.set(key, value)
756
749
  }
757
750
  }
758
751
 
@@ -772,7 +765,7 @@ export async function createRequestHandler(
772
765
  // excluded from build prerendering too — see build.ts); their data
773
766
  // stability comes from the fetch cache alone.
774
767
  matched.route.segmentConfig?.runtime !== 'edge' &&
775
- (matched.route.hasStaticParams || matched.route.mode === 'static')));
768
+ (matched.route.hasStaticParams || matched.route.mode === 'static')))
776
769
  if (
777
770
  !stalePage &&
778
771
  !built &&
@@ -786,15 +779,15 @@ export async function createRequestHandler(
786
779
  (pageResponse.headers.get('content-type') ?? '').includes('text/html')
787
780
  ) {
788
781
  try {
789
- const file = lazyStaticHtmlPath(config.outPath, url.pathname);
790
- if (!file) throw new Error('unsafe path');
791
- await mkdir(path.dirname(file), { recursive: true });
792
- await writeFile(file, await pageResponse.clone().text());
782
+ const file = lazyStaticHtmlPath(config.outPath, url.pathname)
783
+ if (!file) throw new Error('unsafe path')
784
+ await mkdir(path.dirname(file), { recursive: true })
785
+ await writeFile(file, await pageResponse.clone().text())
793
786
  const relative = path
794
787
  .relative(path.join(config.outPath, 'public'), file)
795
788
  .split(path.sep)
796
- .join('/');
797
- manifest.staticFiles ??= {};
789
+ .join('/')
790
+ manifest.staticFiles ??= {}
798
791
  manifest.staticFiles[relative] = {
799
792
  status: 200,
800
793
  // Persist the cacheLife SWR headers (and the route's prerendered
@@ -818,8 +811,8 @@ export async function createRequestHandler(
818
811
  ? { staleSeconds: matched.route.cacheLife.staleSeconds }
819
812
  : {}),
820
813
  ...(renderedPage.tags.length > 0 ? { tags: renderedPage.tags } : {}),
821
- };
822
- pageResponse.headers.set('x-nextjs-cache', 'MISS');
814
+ }
815
+ pageResponse.headers.set('x-nextjs-cache', 'MISS')
823
816
  } catch {
824
817
  // Lazy caching is best-effort; the response is still served.
825
818
  }
@@ -838,8 +831,8 @@ export async function createRequestHandler(
838
831
  pageResponse.status === 200 &&
839
832
  (pageResponse.headers.get('content-type') ?? '').includes('text/html')
840
833
  ) {
841
- await writeFile(stalePage, await pageResponse.clone().text());
842
- pageResponse.headers.set('x-nextjs-cache', 'MISS');
834
+ await writeFile(stalePage, await pageResponse.clone().text())
835
+ pageResponse.headers.set('x-nextjs-cache', 'MISS')
843
836
  }
844
837
  // On-demand revalidation (valid x-prerender-revalidate): the fresh render
845
838
  // replaces any prebuilt copy and is reported as REVALIDATED (Next's
@@ -851,11 +844,11 @@ export async function createRequestHandler(
851
844
  pageResponse.status === 200 &&
852
845
  (pageResponse.headers.get('content-type') ?? '').includes('text/html')
853
846
  ) {
854
- await writeFile(built.file, await pageResponse.clone().text());
847
+ await writeFile(built.file, await pageResponse.clone().text())
855
848
  }
856
- pageResponse.headers.set('x-nextjs-cache', 'REVALIDATED');
849
+ pageResponse.headers.set('x-nextjs-cache', 'REVALIDATED')
857
850
  }
858
- return applyProxyResponse(compressResponse(pageResponse, request), proxyResponse);
851
+ return applyProxyResponse(compressResponse(pageResponse, request), proxyResponse)
859
852
  }
860
853
  }
861
854
 
@@ -869,16 +862,16 @@ function normalizeDataRequest(
869
862
  request: Request,
870
863
  config: { trailingSlash?: boolean },
871
864
  ): Request | null {
872
- const method = request.method.toUpperCase();
873
- if (method !== 'GET' && method !== 'HEAD') return null;
874
- const url = new URL(request.url);
875
- const match = /^\/_next\/data\/[^/]+(\/.+)\.json$/.exec(url.pathname);
876
- if (!match) return null;
877
- const page = match[1] === '/index' ? '/' : match[1]!;
878
- url.pathname = canonicalTrailingSlashPath(page, Boolean(config.trailingSlash));
879
- const headers = new Headers(request.headers);
880
- headers.set('x-nextjs-data', '1');
881
- return new Request(url, { method, headers });
865
+ const method = request.method.toUpperCase()
866
+ if (method !== 'GET' && method !== 'HEAD') return null
867
+ const url = new URL(request.url)
868
+ const match = /^\/_next\/data\/[^/]+(\/.+)\.json$/.exec(url.pathname)
869
+ if (!match) return null
870
+ const page = match[1] === '/index' ? '/' : match[1]!
871
+ url.pathname = canonicalTrailingSlashPath(page, Boolean(config.trailingSlash))
872
+ const headers = new Headers(request.headers)
873
+ headers.set('x-nextjs-data', '1')
874
+ return new Request(url, { method, headers })
882
875
  }
883
876
 
884
877
  // Give the basePath-independent interceptors (compat's `basePath: false`
@@ -890,10 +883,10 @@ async function runOutsideBasePathInterceptors(
890
883
  config: Awaited<ReturnType<typeof loadConfig>>,
891
884
  ): Promise<Response | { request: Request } | undefined> {
892
885
  for (const interceptor of getRequestExtensions().outsideBasePathInterceptors) {
893
- const result = await interceptor(request, { config, outsideBasePath: true });
894
- if (result) return result;
886
+ const result = await interceptor(request, { config, outsideBasePath: true })
887
+ if (result) return result
895
888
  }
896
- return undefined;
889
+ return undefined
897
890
  }
898
891
 
899
892
  // Remove the configured basePath prefix from the request URL, returning a
@@ -901,11 +894,11 @@ async function runOutsideBasePathInterceptors(
901
894
  // null when the path is outside basePath (Next 404s those). The root of the
902
895
  // app (`/basePath`) maps to `/`.
903
896
  function stripBasePath(request: Request, basePath: string): Request | null {
904
- const url = new URL(request.url);
905
- const { pathname } = url;
906
- if (pathname !== basePath && !pathname.startsWith(`${basePath}/`)) return null;
907
- url.pathname = pathname.slice(basePath.length) || '/';
908
- return new Request(url, request);
897
+ const url = new URL(request.url)
898
+ const { pathname } = url
899
+ if (pathname !== basePath && !pathname.startsWith(`${basePath}/`)) return null
900
+ url.pathname = pathname.slice(basePath.length) || '/'
901
+ return new Request(url, request)
909
902
  }
910
903
 
911
904
  // Remove a configured PATH-style assetPrefix from an asset request URL so the
@@ -915,38 +908,38 @@ function stripBasePath(request: Request, basePath: string): Request | null {
915
908
  // absolute prefix, only its pathname is matched so local production starts can
916
909
  // serve the same generated asset URLs.
917
910
  function stripAssetPrefix(request: Request, assetPrefix: string | undefined): Request | null {
918
- if (!assetPrefix) return null;
919
- let prefixValue = assetPrefix;
911
+ if (!assetPrefix) return null
912
+ let prefixValue = assetPrefix
920
913
  if (/^[a-z][a-z0-9+.-]*:\/\//i.test(assetPrefix)) {
921
- prefixValue = new URL(assetPrefix).pathname;
914
+ prefixValue = new URL(assetPrefix).pathname
922
915
  }
923
- const prefix = `/${prefixValue.replace(/^\/+|\/+$/g, '')}`;
924
- if (prefix === '/') return null;
925
- const url = new URL(request.url);
926
- const { pathname } = url;
916
+ const prefix = `/${prefixValue.replace(/^\/+|\/+$/g, '')}`
917
+ if (prefix === '/') return null
918
+ const url = new URL(request.url)
919
+ const { pathname } = url
927
920
  if (
928
921
  !pathname.startsWith(`${prefix}/assets/`) &&
929
922
  !pathname.startsWith(`${prefix}/_next/static/`)
930
923
  ) {
931
- return null;
924
+ return null
932
925
  }
933
- url.pathname = pathname.slice(prefix.length);
934
- return new Request(url, request);
926
+ url.pathname = pathname.slice(prefix.length)
927
+ return new Request(url, request)
935
928
  }
936
929
 
937
930
  function maybeCloseNodeFetchConnection(response: Response, request: Request) {
938
- const userAgent = request.headers.get('user-agent') ?? '';
939
- if (!userAgent.includes('node-fetch')) return response;
940
- response.headers.set('connection', 'close');
941
- return response;
931
+ const userAgent = request.headers.get('user-agent') ?? ''
932
+ if (!userAgent.includes('node-fetch')) return response
933
+ response.headers.set('connection', 'close')
934
+ return response
942
935
  }
943
936
 
944
937
  // Compat mode gate (a pure read of config.compat, no compat import): under
945
938
  // compat the compiled module is loaded so bare next/* / react aliases resolve
946
- // to the compat layer. Mirrors compat/index.ts reactCompatEnabled — kept inline
939
+ // to the compat layer. Mirrors compat/aliases.ts reactCompatEnabled — kept inline
947
940
  // so start.ts carries no static edge into compat.
948
941
  function compatModuleMode(config: Awaited<ReturnType<typeof loadConfig>>): boolean {
949
- return Boolean(config.compat?.next || config.compat?.react || config.compat?.reactCompiler);
942
+ return Boolean(config.compat?.next || config.compat?.react || config.compat?.reactCompiler)
950
943
  }
951
944
 
952
945
  async function handleRoute(
@@ -956,11 +949,11 @@ async function handleRoute(
956
949
  params: Record<string, RouteParamValue>,
957
950
  ) {
958
951
  return withRouteRuntime(route.segmentConfig?.runtime, async () => {
959
- registerServerRuntime(config, route.sourceFiles);
952
+ registerServerRuntime(config, route.sourceFiles)
960
953
  // Module resolution and handler invocation run through the render-span seam
961
954
  // (compat/otel emits `resolve page components` + `executing api route`;
962
955
  // pass-through for pure-core apps).
963
- const spans = getRenderSpanExtensions();
956
+ const spans = getRenderSpanExtensions()
964
957
  const module = await spans.withFindPageComponentsSpan(route.route, async () => {
965
958
  // Compat mode loads the compiled module (aliases baked in): a raw import
966
959
  // would resolve bare next/* specifiers (next/headers etc.) against whatever
@@ -968,37 +961,37 @@ async function handleRoute(
968
961
  // onResolve plugin cannot alias bare imports. Same pattern as pageRender.
969
962
  const href = compatModuleMode(config)
970
963
  ? await moduleHrefForRoute(config, route)
971
- : new URL(`file://${route.file}`).href;
964
+ : new URL(`file://${route.file}`).href
972
965
  const imported = (await import(href)) as RouteHandlerModule &
973
- Parameters<typeof metadataRouteHandlerModule>[0];
974
- return (metadataRouteHandlerModule(imported, route) ?? imported) as RouteHandlerModule;
975
- });
966
+ Parameters<typeof metadataRouteHandlerModule>[0]
967
+ return (metadataRouteHandlerModule(imported, route) ?? imported) as RouteHandlerModule
968
+ })
976
969
  return spans.withRouteHandlerSpan(route.route, () =>
977
970
  runWithCacheScope(() =>
978
971
  handleRouteModule(module, request, params, { routeFile: route.file }),
979
972
  ),
980
- );
981
- });
973
+ )
974
+ })
982
975
  }
983
976
 
984
977
  // Draft mode (draftMode().enable() from next/headers) marks the browser with
985
978
  // a bypass cookie; requests carrying it must not see prerendered page html.
986
979
  function hasDraftBypassCookie(headers: Headers) {
987
- return /(?:^|;\s*)__prerender_bypass=/.test(headers.get('cookie') ?? '');
980
+ return /(?:^|;\s*)__prerender_bypass=/.test(headers.get('cookie') ?? '')
988
981
  }
989
982
 
990
983
  function isHtmlResponse(response: Response) {
991
- return (response.headers.get('content-type') ?? '').includes('text/html');
984
+ return (response.headers.get('content-type') ?? '').includes('text/html')
992
985
  }
993
986
 
994
987
  // Core's coarse route caching disposition for the work unit / finalizers. ISR
995
988
  // (a static route with a revalidate TTL) is reported as 'isr'; compat can refine
996
989
  // the exact cache-control strings in its response finalizers.
997
990
  function routeModeOf(route: RouteManifestEntry): 'static' | 'isr' | 'dynamic' {
998
- if (route.segmentConfig?.dynamic === 'force-dynamic') return 'dynamic';
999
- const revalidate = route.segmentConfig?.revalidate;
1000
- if (typeof revalidate === 'number' && revalidate > 0) return 'isr';
1001
- return route.mode === 'static' ? 'static' : 'dynamic';
991
+ if (route.segmentConfig?.dynamic === 'force-dynamic') return 'dynamic'
992
+ const revalidate = route.segmentConfig?.revalidate
993
+ if (typeof revalidate === 'number' && revalidate > 0) return 'isr'
994
+ return route.mode === 'static' ? 'static' : 'dynamic'
1002
995
  }
1003
996
 
1004
997
  // Compat classification hints carried on the work unit for response finalizers:
@@ -1008,23 +1001,21 @@ function responseHintsFor(
1008
1001
  route: RouteManifestEntry,
1009
1002
  revalidateReason: string | undefined,
1010
1003
  ): Record<string, unknown> | undefined {
1011
- const runtime = route.segmentConfig?.runtime;
1012
- const revalidate = route.segmentConfig?.revalidate;
1013
- if (!revalidateReason && !runtime && typeof revalidate !== 'number') return undefined;
1004
+ const runtime = route.segmentConfig?.runtime
1005
+ const revalidate = route.segmentConfig?.revalidate
1006
+ if (!revalidateReason && !runtime && typeof revalidate !== 'number') return undefined
1014
1007
  return {
1015
1008
  ...(revalidateReason ? { revalidateReason } : {}),
1016
1009
  ...(runtime ? { runtime } : {}),
1017
- ...(typeof revalidate === 'number' && revalidate > 0
1018
- ? { revalidateSeconds: revalidate }
1019
- : {}),
1020
- };
1010
+ ...(typeof revalidate === 'number' && revalidate > 0 ? { revalidateSeconds: revalidate } : {}),
1011
+ }
1021
1012
  }
1022
1013
 
1023
1014
  interface BuiltFileInfo {
1024
- file: string;
1025
- relative: string;
1026
- mtimeMs: number;
1027
- metadata?: StaticFileMetadata;
1015
+ file: string
1016
+ relative: string
1017
+ mtimeMs: number
1018
+ metadata?: StaticFileMetadata
1028
1019
  }
1029
1020
 
1030
1021
  // The prebuilt output file a GET for `pathname` would serve (page html or a
@@ -1038,71 +1029,75 @@ async function builtFileInfo(
1038
1029
  const file = await firstFile(
1039
1030
  path.join(outPath, 'public'),
1040
1031
  builtFileCandidates(outPath, pathname, nextStaticFallback),
1041
- );
1042
- if (!file) return null;
1043
- const publicPath = path.join(outPath, 'public');
1044
- const fileStat = await stat(file);
1045
- const relative = path.relative(publicPath, file).split(path.sep).join('/');
1046
- return { file, relative, mtimeMs: fileStat.mtimeMs, metadata: staticFiles[relative] };
1032
+ )
1033
+ if (!file) return null
1034
+ const publicPath = path.join(outPath, 'public')
1035
+ const fileStat = await stat(file)
1036
+ const relative = path.relative(publicPath, file).split(path.sep).join('/')
1037
+ return { file, relative, mtimeMs: fileStat.mtimeMs, metadata: staticFiles[relative] }
1047
1038
  }
1048
1039
 
1049
1040
  // The prebuilt files a GET for `pathname` could resolve to, most-specific
1050
1041
  // first: the dir layout (`/a/index.html`), a raw static asset (`/a`), and the
1051
1042
  // flat export layout (`/a.html`, output:'export' + trailingSlash:false).
1052
- function builtFileCandidates(outPath: string, pathname: string, nextStaticFallback = false): string[] {
1053
- const publicPath = path.join(outPath, 'public');
1054
- const trimmed = pathname.replace(/^\/+/, '');
1055
- if (pathname === '/') return [path.join(publicPath, 'index.html')];
1043
+ function builtFileCandidates(
1044
+ outPath: string,
1045
+ pathname: string,
1046
+ nextStaticFallback = false,
1047
+ ): string[] {
1048
+ const publicPath = path.join(outPath, 'public')
1049
+ const trimmed = pathname.replace(/^\/+/, '')
1050
+ if (pathname === '/') return [path.join(publicPath, 'index.html')]
1056
1051
  const candidates = [
1057
1052
  path.join(publicPath, trimmed, 'index.html'),
1058
1053
  path.join(publicPath, trimmed),
1059
1054
  path.join(publicPath, `${trimmed.replace(/\/+$/, '')}.html`),
1060
- ];
1055
+ ]
1061
1056
  // Prerendered pages for generateStaticParams values with special characters
1062
1057
  // live under the DECODED segment (`sticks & stones/`), while the request
1063
1058
  // pathname arrives percent-encoded (prerender-encoding suite).
1064
1059
  try {
1065
- const decoded = decodeURIComponent(trimmed);
1060
+ const decoded = decodeURIComponent(trimmed)
1066
1061
  if (decoded !== trimmed && !decoded.split('/').some(seg => seg === '..' || seg === '')) {
1067
1062
  candidates.push(
1068
1063
  path.join(publicPath, decoded, 'index.html'),
1069
1064
  path.join(publicPath, `${decoded.replace(/\/+$/, '')}.html`),
1070
- );
1065
+ )
1071
1066
  }
1072
1067
  } catch {
1073
1068
  // malformed escape — encoded candidates only
1074
1069
  }
1075
1070
  if (nextStaticFallback && trimmed.startsWith('_next/static/')) {
1076
- const asset = trimmed.slice('_next/static/'.length);
1077
- candidates.push(path.join(publicPath, 'assets', asset));
1071
+ const asset = trimmed.slice('_next/static/'.length)
1072
+ candidates.push(path.join(publicPath, 'assets', asset))
1078
1073
  }
1079
- return candidates;
1074
+ return candidates
1080
1075
  }
1081
1076
 
1082
1077
  // Where a lazily generated page for `pathname` is persisted (mirrors the
1083
1078
  // build's staticHtmlPath); null when the path would escape public/.
1084
1079
  function lazyStaticHtmlPath(outPath: string, pathname: string): string | null {
1085
- const publicPath = path.join(outPath, 'public');
1080
+ const publicPath = path.join(outPath, 'public')
1086
1081
  const file =
1087
1082
  pathname === '/'
1088
1083
  ? path.join(publicPath, 'index.html')
1089
- : path.join(publicPath, pathname.replace(/^\/+|\/+$/g, ''), 'index.html');
1090
- const relative = path.relative(publicPath, file);
1091
- if (relative.startsWith('..') || path.isAbsolute(relative)) return null;
1092
- return file;
1084
+ : path.join(publicPath, pathname.replace(/^\/+|\/+$/g, ''), 'index.html')
1085
+ const relative = path.relative(publicPath, file)
1086
+ if (relative.startsWith('..') || path.isAbsolute(relative)) return null
1087
+ return file
1093
1088
  }
1094
1089
 
1095
1090
  function lazyStaticHandlerPath(outPath: string, pathname: string): string | null {
1096
- if (pathname === '/') return null;
1097
- const publicPath = path.join(outPath, 'public');
1098
- const file = path.join(publicPath, pathname.replace(/^\/+|\/+$/g, ''));
1099
- const relative = path.relative(publicPath, file);
1100
- if (relative.startsWith('..') || path.isAbsolute(relative)) return null;
1101
- return file;
1091
+ if (pathname === '/') return null
1092
+ const publicPath = path.join(outPath, 'public')
1093
+ const file = path.join(publicPath, pathname.replace(/^\/+|\/+$/g, ''))
1094
+ const relative = path.relative(publicPath, file)
1095
+ if (relative.startsWith('..') || path.isAbsolute(relative)) return null
1096
+ return file
1102
1097
  }
1103
1098
 
1104
1099
  function isHtmlPageFile(file: string) {
1105
- return file.endsWith('.html');
1100
+ return file.endsWith('.html')
1106
1101
  }
1107
1102
 
1108
1103
  /**
@@ -1110,18 +1105,16 @@ function isHtmlPageFile(file: string) {
1110
1105
  * cacheLife implies. A pure static HIT never re-runs the render header finalizer, so start.ts
1111
1106
  * re-emits these from the persisted cacheLife. Returns [] when none applies.
1112
1107
  */
1113
- function cacheLifeResponseHeaders(
1114
- life: RouteManifestEntry['cacheLife'],
1115
- ): [string, string][] {
1116
- if (!life) return [];
1117
- const headers: [string, string][] = [];
1118
- const { revalidateSeconds, expireSeconds, staleSeconds } = life;
1108
+ function cacheLifeResponseHeaders(life: RouteManifestEntry['cacheLife']): [string, string][] {
1109
+ if (!life) return []
1110
+ const headers: [string, string][] = []
1111
+ const { revalidateSeconds, expireSeconds, staleSeconds } = life
1119
1112
  if (revalidateSeconds !== undefined && expireSeconds !== undefined) {
1120
- const swr = Math.max(0, expireSeconds - revalidateSeconds);
1121
- headers.push(['cache-control', `s-maxage=${revalidateSeconds}, stale-while-revalidate=${swr}`]);
1113
+ const swr = Math.max(0, expireSeconds - revalidateSeconds)
1114
+ headers.push(['cache-control', `s-maxage=${revalidateSeconds}, stale-while-revalidate=${swr}`])
1122
1115
  }
1123
- if (staleSeconds !== undefined) headers.push(['x-nextjs-stale-time', String(staleSeconds)]);
1124
- return headers;
1116
+ if (staleSeconds !== undefined) headers.push(['x-nextjs-stale-time', String(staleSeconds)])
1117
+ return headers
1125
1118
  }
1126
1119
 
1127
1120
  // A content-hashed, immutable build asset (client runtime chunks under
@@ -1130,7 +1123,7 @@ function cacheLifeResponseHeaders(
1130
1123
  // revalidatePath/revalidateTag staleness, unlike prebuilt page HTML and
1131
1124
  // route-handler bodies. `relative` uses forward slashes (see builtFileInfo).
1132
1125
  function isImmutableAssetFile(relative: string) {
1133
- return relative.startsWith('assets/') || relative.startsWith('_next/static/');
1126
+ return relative.startsWith('assets/') || relative.startsWith('_next/static/')
1134
1127
  }
1135
1128
 
1136
1129
  // `dynamicParams = false` (route segment config): params governed by the
@@ -1138,19 +1131,19 @@ function isImmutableAssetFile(relative: string) {
1138
1131
  // the page has its own static params export, the route behaves like Next's
1139
1132
  // fallback: false — the full param tuple must match a prerendered path.
1140
1133
  function dynamicParamsAllowed(route: RouteManifestEntry, params: Record<string, RouteParamValue>) {
1141
- const governed = route.segmentConfig?.dynamicParamsFalse;
1142
- if (!governed?.length) return true;
1143
- const allowed = route.prerenderedParams ?? [];
1134
+ const governed = route.segmentConfig?.dynamicParamsFalse
1135
+ if (!governed?.length) return true
1136
+ const allowed = route.prerenderedParams ?? []
1144
1137
  const required = route.segmentConfig?.strictDynamicParams
1145
1138
  ? [...route.params, ...(route.catchAll ? [route.catchAll] : [])]
1146
- : governed;
1147
- return allowed.some(set => required.every(param => paramValueEqual(set[param], params[param])));
1139
+ : governed
1140
+ return allowed.some(set => required.every(param => paramValueEqual(set[param], params[param])))
1148
1141
  }
1149
1142
 
1150
1143
  function paramValueEqual(a: RouteParamValue | undefined, b: RouteParamValue | undefined) {
1151
- if (a === undefined || b === undefined) return false;
1152
- const join = (value: RouteParamValue) => (Array.isArray(value) ? value.join('/') : value);
1153
- return join(a) === join(b);
1144
+ if (a === undefined || b === undefined) return false
1145
+ const join = (value: RouteParamValue) => (Array.isArray(value) ? value.join('/') : value)
1146
+ return join(a) === join(b)
1154
1147
  }
1155
1148
 
1156
1149
  export async function maybeBuiltFile(
@@ -1161,55 +1154,55 @@ export async function maybeBuiltFile(
1161
1154
  requestHeaders?: Headers,
1162
1155
  nextStaticFallback = false,
1163
1156
  ) {
1164
- const normalizedMethod = method.toUpperCase();
1165
- if (normalizedMethod !== 'GET' && normalizedMethod !== 'HEAD') return null;
1157
+ const normalizedMethod = method.toUpperCase()
1158
+ if (normalizedMethod !== 'GET' && normalizedMethod !== 'HEAD') return null
1166
1159
 
1167
- const publicPath = path.join(outPath, 'public');
1160
+ const publicPath = path.join(outPath, 'public')
1168
1161
  const file = await firstFile(
1169
1162
  publicPath,
1170
1163
  builtFileCandidates(outPath, pathname, nextStaticFallback),
1171
- );
1172
- if (!file) return null;
1173
- const relative = path.relative(publicPath, file).split(path.sep).join('/');
1174
- const metadata = staticFiles[relative];
1175
- const headers = new Headers(metadata?.headers);
1176
- if (!headers.has('content-type')) headers.set('content-type', contentType(file));
1164
+ )
1165
+ if (!file) return null
1166
+ const relative = path.relative(publicPath, file).split(path.sep).join('/')
1167
+ const metadata = staticFiles[relative]
1168
+ const headers = new Headers(metadata?.headers)
1169
+ if (!headers.has('content-type')) headers.set('content-type', contentType(file))
1177
1170
 
1178
- const fileStat = await stat(file);
1171
+ const fileStat = await stat(file)
1179
1172
  // Vary discriminator: a router-originated fetch gets its Content-Type rewritten downstream (compat
1180
1173
  // swaps it to text/x-component for soft-nav requests) without this file or its etag changing. The
1181
1174
  // response's `Vary` header already lists `rsc` as differentiating, so the validator must differ too
1182
1175
  // - otherwise a browser that cached one representation gets a 304 on a later request for the OTHER
1183
1176
  // and silently reuses the wrong body and content-type.
1184
- const variant = requestHeaders?.get('rsc') === '1' ? '-rsc' : '';
1185
- const etag = `W/"${fileStat.size.toString(16)}-${Math.trunc(fileStat.mtimeMs).toString(16)}${variant}"`;
1177
+ const variant = requestHeaders?.get('rsc') === '1' ? '-rsc' : ''
1178
+ const etag = `W/"${fileStat.size.toString(16)}-${Math.trunc(fileStat.mtimeMs).toString(16)}${variant}"`
1186
1179
  if (!headers.has('cache-control')) {
1187
- headers.set('cache-control', immutableAssetPath(relative) ? immutableCacheControl : 'no-cache');
1188
- headers.set('etag', etag);
1180
+ headers.set('cache-control', immutableAssetPath(relative) ? immutableCacheControl : 'no-cache')
1181
+ headers.set('etag', etag)
1189
1182
  if (requestHeaders?.get('if-none-match') === etag) {
1190
- return new Response(null, { status: 304, headers });
1183
+ return new Response(null, { status: 304, headers })
1191
1184
  }
1192
1185
  }
1193
1186
 
1194
- const raw = await readFile(file);
1195
- let body: BodyInit = raw;
1187
+ const raw = await readFile(file)
1188
+ let body: BodyInit = raw
1196
1189
  if (
1197
1190
  compressibleContentType(headers.get('content-type') ?? '') &&
1198
1191
  raw.length > 1024 &&
1199
1192
  acceptsGzip(requestHeaders)
1200
1193
  ) {
1201
- const zipped = cachedGzip(`${file}:${etag}`, raw);
1194
+ const zipped = cachedGzip(`${file}:${etag}`, raw)
1202
1195
  if (zipped.length < raw.length) {
1203
- body = zipped as Uint8Array<ArrayBuffer>;
1204
- headers.set('content-encoding', 'gzip');
1205
- headers.append('vary', 'accept-encoding');
1196
+ body = zipped as Uint8Array<ArrayBuffer>
1197
+ headers.set('content-encoding', 'gzip')
1198
+ headers.append('vary', 'accept-encoding')
1206
1199
  }
1207
1200
  }
1208
1201
 
1209
1202
  return new Response(normalizedMethod === 'HEAD' ? null : body, {
1210
1203
  status: metadata?.status ?? 200,
1211
1204
  headers,
1212
- });
1205
+ })
1213
1206
  }
1214
1207
 
1215
1208
  // Wrap dynamic responses (rendered pages, not-found) in streaming gzip.
@@ -1217,42 +1210,42 @@ export async function maybeBuiltFile(
1217
1210
  // encoded or non-text passes through untouched.
1218
1211
  // Append a token to the Vary header without duplicating existing entries.
1219
1212
  function appendVaryToken(headers: Headers, token: string): void {
1220
- const existing = headers.get('vary');
1213
+ const existing = headers.get('vary')
1221
1214
  if (!existing) {
1222
- headers.set('vary', token);
1223
- return;
1215
+ headers.set('vary', token)
1216
+ return
1224
1217
  }
1225
- const tokens = existing.split(',').map(part => part.trim().toLowerCase());
1226
- if (tokens.includes(token.toLowerCase()) || tokens.includes('*')) return;
1227
- headers.set('vary', `${existing}, ${token}`);
1218
+ const tokens = existing.split(',').map(part => part.trim().toLowerCase())
1219
+ if (tokens.includes(token.toLowerCase()) || tokens.includes('*')) return
1220
+ headers.set('vary', `${existing}, ${token}`)
1228
1221
  }
1229
1222
 
1230
1223
  export function compressResponse(response: Response, request: Request) {
1231
- if (!response.body || response.status === 304 || response.status === 204) return response;
1232
- if (response.headers.get('content-encoding')) return response;
1233
- if (!compressibleContentType(response.headers.get('content-type') ?? '')) return response;
1224
+ if (!response.body || response.status === 304 || response.status === 204) return response
1225
+ if (response.headers.get('content-encoding')) return response
1226
+ if (!compressibleContentType(response.headers.get('content-type') ?? '')) return response
1234
1227
 
1235
- const headers = new Headers(response.headers);
1228
+ const headers = new Headers(response.headers)
1236
1229
  // `compress: true` advertises encoding negotiability on every compressible
1237
1230
  // response — even when the client didn't send Accept-Encoding — so caches key
1238
1231
  // on it (matches Next's compression middleware, which sets Vary before it
1239
1232
  // decides whether to gzip). The flight-request vary assertion expects this.
1240
- appendVaryToken(headers, 'accept-encoding');
1233
+ appendVaryToken(headers, 'accept-encoding')
1241
1234
  if (!acceptsGzip(request.headers)) {
1242
- return new Response(response.body, { status: response.status, headers });
1235
+ return new Response(response.body, { status: response.status, headers })
1243
1236
  }
1244
1237
  // Match Next's compression middleware threshold: bodies with a known
1245
1238
  // length at or below 1KB are served identity-encoded (Content-Length kept).
1246
- const knownLength = Number(headers.get('content-length'));
1239
+ const knownLength = Number(headers.get('content-length'))
1247
1240
  if (Number.isFinite(knownLength) && knownLength <= 1024) {
1248
- return new Response(response.body, { status: response.status, headers });
1241
+ return new Response(response.body, { status: response.status, headers })
1249
1242
  }
1250
- headers.set('content-encoding', 'gzip');
1251
- headers.delete('content-length');
1243
+ headers.set('content-encoding', 'gzip')
1244
+ headers.delete('content-length')
1252
1245
  return new Response(streamingGzip(response.body), {
1253
1246
  status: response.status,
1254
1247
  headers,
1255
- });
1248
+ })
1256
1249
  }
1257
1250
 
1258
1251
  // Gzip a streaming response, flushing the compressor after every source chunk
@@ -1264,52 +1257,52 @@ export function compressResponse(response: Response, request: Request) {
1264
1257
  // general). A per-chunk sync flush keeps the stream observable at the cost of a
1265
1258
  // slightly worse ratio on tiny early chunks.
1266
1259
  function streamingGzip(body: ReadableStream<Uint8Array>): ReadableStream<Uint8Array> {
1267
- const gzip = createGzip();
1268
- const reader = body.getReader();
1260
+ const gzip = createGzip()
1261
+ const reader = body.getReader()
1269
1262
  void (async () => {
1270
1263
  try {
1271
1264
  for (;;) {
1272
- const { done, value } = await reader.read();
1273
- if (done) break;
1274
- gzip.write(value);
1265
+ const { done, value } = await reader.read()
1266
+ if (done) break
1267
+ gzip.write(value)
1275
1268
  // Emit the bytes buffered so far as a flushable block.
1276
- await new Promise<void>(resolve => gzip.flush(zlibConstants.Z_SYNC_FLUSH, resolve));
1269
+ await new Promise<void>(resolve => gzip.flush(zlibConstants.Z_SYNC_FLUSH, resolve))
1277
1270
  }
1278
- gzip.end();
1271
+ gzip.end()
1279
1272
  } catch (error) {
1280
- gzip.destroy(error as Error);
1281
- reader.cancel(error).catch(() => undefined);
1273
+ gzip.destroy(error as Error)
1274
+ reader.cancel(error).catch(() => undefined)
1282
1275
  }
1283
- })();
1284
- return Readable.toWeb(gzip) as unknown as ReadableStream<Uint8Array>;
1276
+ })()
1277
+ return Readable.toWeb(gzip) as unknown as ReadableStream<Uint8Array>
1285
1278
  }
1286
1279
 
1287
1280
  function compressibleContentType(type: string) {
1288
- return /^(text\/|application\/(javascript|json|manifest|xml)|image\/svg)/.test(type);
1281
+ return /^(text\/|application\/(javascript|json|manifest|xml)|image\/svg)/.test(type)
1289
1282
  }
1290
1283
 
1291
1284
  function acceptsGzip(requestHeaders?: Headers) {
1292
- return requestHeaders?.get('accept-encoding')?.includes('gzip') ?? false;
1285
+ return requestHeaders?.get('accept-encoding')?.includes('gzip') ?? false
1293
1286
  }
1294
1287
 
1295
1288
  // Bounded by the build's compressible assets; entries are keyed on content
1296
1289
  // identity (path + etag), so a rebuild naturally replaces stale ones.
1297
- const gzipCache = new Map<string, Buffer>();
1298
- const gzipCacheLimit = 512;
1290
+ const gzipCache = new Map<string, Buffer>()
1291
+ const gzipCacheLimit = 512
1299
1292
 
1300
1293
  function cachedGzip(key: string, body: Buffer) {
1301
- const cached = gzipCache.get(key);
1302
- if (cached) return cached;
1303
- const zipped = gzipSync(body, { level: 6 });
1294
+ const cached = gzipCache.get(key)
1295
+ if (cached) return cached
1296
+ const zipped = gzipSync(body, { level: 6 })
1304
1297
  if (gzipCache.size >= gzipCacheLimit) {
1305
- const oldest = gzipCache.keys().next().value;
1306
- if (oldest !== undefined) gzipCache.delete(oldest);
1298
+ const oldest = gzipCache.keys().next().value
1299
+ if (oldest !== undefined) gzipCache.delete(oldest)
1307
1300
  }
1308
- gzipCache.set(key, zipped);
1309
- return zipped;
1301
+ gzipCache.set(key, zipped)
1302
+ return zipped
1310
1303
  }
1311
1304
 
1312
- export const immutableCacheControl = 'public, max-age=31536000, immutable';
1305
+ export const immutableCacheControl = 'public, max-age=31536000, immutable'
1313
1306
 
1314
1307
  // Chunk and font filenames are content-hashed, so their bytes can never change under a given URL;
1315
1308
  // entries, css and html keep stable names and must revalidate (no-cache still allows storing, so
@@ -1317,24 +1310,23 @@ export const immutableCacheControl = 'public, max-age=31536000, immutable';
1317
1310
  // way, so they qualify too.
1318
1311
  export function immutableAssetPath(relativePath: string) {
1319
1312
  if (relativePath.startsWith('assets/chunks/') || relativePath.startsWith('assets/fonts/')) {
1320
- return true;
1313
+ return true
1321
1314
  }
1322
1315
  return getAssetExtensions()
1323
1316
  .staticAssetPublicPrefixes()
1324
- .some(prefix => relativePath.startsWith(prefix.replace(/^\/+/, '')));
1317
+ .some(prefix => relativePath.startsWith(prefix.replace(/^\/+/, '')))
1325
1318
  }
1326
1319
 
1327
1320
  async function firstFile(root: string, files: string[]) {
1328
1321
  for (const file of files) {
1329
- if (!isInside(root, file)) continue;
1330
- if (!existsSync(file)) continue;
1331
- if ((await stat(file)).isFile()) return file;
1322
+ if (!isInside(root, file)) continue
1323
+ if (!existsSync(file)) continue
1324
+ if ((await stat(file)).isFile()) return file
1332
1325
  }
1333
- return null;
1326
+ return null
1334
1327
  }
1335
1328
 
1336
1329
  function isInside(root: string, file: string) {
1337
- const relative = path.relative(root, file);
1338
- return relative === '' || (!relative.startsWith('..') && !path.isAbsolute(relative));
1330
+ const relative = path.relative(root, file)
1331
+ return relative === '' || (!relative.startsWith('..') && !path.isAbsolute(relative))
1339
1332
  }
1340
-