vinext 1.1.0 → 1.2.1

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 (288) hide show
  1. package/README.md +22 -10
  2. package/dist/build/client-build-config.d.ts +10 -0
  3. package/dist/build/client-build-config.js +18 -2
  4. package/dist/build/output-root-externals.d.ts +21 -0
  5. package/dist/build/output-root-externals.js +40 -0
  6. package/dist/build/pages-client-assets-module.d.ts +8 -0
  7. package/dist/build/pages-client-assets-module.js +21 -1
  8. package/dist/build/prerender-paths.js +6 -4
  9. package/dist/build/prerender.js +1 -1
  10. package/dist/build/run-prerender.js +1 -1
  11. package/dist/build/ssr-manifest.d.ts +2 -0
  12. package/dist/build/ssr-manifest.js +6 -2
  13. package/dist/check.js +12 -12
  14. package/dist/cli-dev-config.js +3 -3
  15. package/dist/cli.js +3 -2
  16. package/dist/client/client-redirect-matcher.d.ts +14 -0
  17. package/dist/client/client-redirect-matcher.js +37 -0
  18. package/dist/client/client-redirects.d.ts +27 -0
  19. package/dist/client/client-redirects.js +28 -0
  20. package/dist/client/client-rewrites.d.ts +2 -1
  21. package/dist/client/client-rewrites.js +1 -1
  22. package/dist/config/config-matchers.d.ts +26 -11
  23. package/dist/config/config-matchers.js +90 -88
  24. package/dist/config/destination-params.d.ts +47 -0
  25. package/dist/config/destination-params.js +116 -0
  26. package/dist/config/next-config.d.ts +7 -0
  27. package/dist/config/next-config.js +3 -0
  28. package/dist/entries/app-rsc-entry.d.ts +4 -0
  29. package/dist/entries/app-rsc-entry.js +20 -6
  30. package/dist/entries/pages-client-entry.js +3 -1
  31. package/dist/entries/pages-server-entry.js +13 -37
  32. package/dist/index.d.ts +3 -1
  33. package/dist/index.js +246 -136
  34. package/dist/init-cloudflare.d.ts +9 -0
  35. package/dist/init-cloudflare.js +107 -31
  36. package/dist/init-platform.d.ts +4 -0
  37. package/dist/init-platform.js +27 -2
  38. package/dist/init.d.ts +2 -1
  39. package/dist/init.js +153 -9
  40. package/dist/plugins/ast-scope.d.ts +7 -0
  41. package/dist/plugins/ast-scope.js +59 -3
  42. package/dist/plugins/ast-utils.d.ts +53 -2
  43. package/dist/plugins/ast-utils.js +90 -2
  44. package/dist/plugins/cloudflare-dev-module-urls.d.ts +41 -0
  45. package/dist/plugins/cloudflare-dev-module-urls.js +271 -0
  46. package/dist/plugins/commonjs-esm-facade.js +2 -2
  47. package/dist/plugins/css-module-imports.js +13 -20
  48. package/dist/plugins/dynamic-preload-metadata.d.ts +1 -1
  49. package/dist/plugins/dynamic-preload-metadata.js +11 -11
  50. package/dist/plugins/extensionless-dynamic-import.js +2 -3
  51. package/dist/plugins/fonts.d.ts +1 -1
  52. package/dist/plugins/fonts.js +3 -3
  53. package/dist/plugins/ignore-dynamic-requests.d.ts +1 -1
  54. package/dist/plugins/ignore-dynamic-requests.js +5 -7
  55. package/dist/plugins/import-meta-url.js +3 -4
  56. package/dist/plugins/instrumentation-client.js +3 -2
  57. package/dist/plugins/middleware-export-validation.js +2 -3
  58. package/dist/plugins/middleware-server-only.d.ts +5 -4
  59. package/dist/plugins/middleware-server-only.js +8 -5
  60. package/dist/plugins/og-harfbuzz.js +2 -3
  61. package/dist/plugins/optimize-imports.js +3 -4
  62. package/dist/plugins/pages-node-externals.js +3 -4
  63. package/dist/plugins/remove-console.js +2 -18
  64. package/dist/plugins/require-condition-resolution.js +3 -3
  65. package/dist/plugins/require-context.js +2 -3
  66. package/dist/plugins/rsc-scan-glob-imports.d.ts +19 -0
  67. package/dist/plugins/rsc-scan-glob-imports.js +50 -0
  68. package/dist/plugins/strip-server-exports.js +265 -102
  69. package/dist/plugins/styled-jsx.js +2 -3
  70. package/dist/plugins/transform-result.d.ts +9 -5
  71. package/dist/plugins/transitive-externals.js +1 -1
  72. package/dist/plugins/typeof-window.d.ts +2 -2
  73. package/dist/plugins/use-cache-callable.d.ts +21 -2
  74. package/dist/plugins/use-cache-callable.js +84 -35
  75. package/dist/plugins/worker-image-imports.js +30 -33
  76. package/dist/plugins/worker-server-only-imports.d.ts +20 -0
  77. package/dist/plugins/worker-server-only-imports.js +31 -0
  78. package/dist/plugins/worker-use-server.d.ts +25 -0
  79. package/dist/plugins/worker-use-server.js +153 -0
  80. package/dist/server/api-handler.d.ts +1 -0
  81. package/dist/server/api-handler.js +3 -2
  82. package/dist/server/app-action-forwarding.js +2 -2
  83. package/dist/server/app-browser-action-result.d.ts +1 -1
  84. package/dist/server/app-browser-action-result.js +9 -2
  85. package/dist/server/app-browser-entry.js +3 -3
  86. package/dist/server/app-browser-interception-context.js +1 -1
  87. package/dist/server/app-browser-navigation-controller.js +1 -1
  88. package/dist/server/app-browser-prefetch-response.js +2 -2
  89. package/dist/server/app-browser-server-action-client.js +1 -1
  90. package/dist/server/app-browser-state.js +1 -1
  91. package/dist/server/app-browser-supplemental-refresh.js +1 -1
  92. package/dist/server/app-middleware.d.ts +11 -0
  93. package/dist/server/app-middleware.js +17 -2
  94. package/dist/server/app-optimistic-routing.js +1 -1
  95. package/dist/server/app-page-cache.d.ts +1 -1
  96. package/dist/server/app-page-cache.js +1 -1
  97. package/dist/server/app-page-dispatch.d.ts +3 -1
  98. package/dist/server/app-page-dispatch.js +5 -4
  99. package/dist/server/app-page-ppr-runtime.js +1 -1
  100. package/dist/server/app-page-render-observation.js +1 -1
  101. package/dist/server/app-page-render.js +1 -1
  102. package/dist/server/app-page-request.js +53 -15
  103. package/dist/server/app-page-route-wiring.js +2 -2
  104. package/dist/server/app-pages-bridge.d.ts +1 -1
  105. package/dist/server/app-pages-bridge.js +3 -4
  106. package/dist/server/app-prerender-endpoints.js +9 -6
  107. package/dist/server/app-render-abort-error.d.ts +19 -0
  108. package/dist/server/app-render-abort-error.js +31 -1
  109. package/dist/server/app-request-stage-dispatch.js +6 -2
  110. package/dist/server/app-request-stage-independent-entry.js +29 -9
  111. package/dist/server/app-response-stage-entry.js +11 -3
  112. package/dist/server/app-route-handler-cache.d.ts +1 -0
  113. package/dist/server/app-route-handler-cache.js +1 -0
  114. package/dist/server/app-route-handler-dispatch.d.ts +1 -0
  115. package/dist/server/app-route-handler-dispatch.js +5 -2
  116. package/dist/server/app-route-handler-execution.d.ts +1 -0
  117. package/dist/server/app-route-handler-execution.js +1 -0
  118. package/dist/server/app-route-handler-runtime.d.ts +4 -1
  119. package/dist/server/app-route-handler-runtime.js +46 -25
  120. package/dist/server/app-router-entry.js +29 -10
  121. package/dist/server/app-rsc-cache-busting.d.ts +16 -0
  122. package/dist/server/app-rsc-cache-busting.js +30 -13
  123. package/dist/server/app-rsc-errors.js +20 -16
  124. package/dist/server/app-rsc-handler.d.ts +7 -0
  125. package/dist/server/app-rsc-handler.js +89 -39
  126. package/dist/server/app-rsc-request-normalization.js +3 -3
  127. package/dist/server/app-rsc-response-stage.js +5 -3
  128. package/dist/server/app-segment-config.d.ts +2 -1
  129. package/dist/server/app-server-action-execution.d.ts +2 -1
  130. package/dist/server/app-server-action-execution.js +62 -23
  131. package/dist/server/app-ssr-entry.js +7 -5
  132. package/dist/server/app-visited-response-cache.js +2 -2
  133. package/dist/server/app-worker-stages.d.ts +5 -0
  134. package/dist/server/app-worker-stages.js +1 -1
  135. package/dist/server/cacheability-request.js +2 -2
  136. package/dist/server/client-build-files.d.ts +16 -0
  137. package/dist/server/client-build-files.js +36 -0
  138. package/dist/server/client-reuse-manifest.js +1 -1
  139. package/dist/server/config-headers.js +1 -1
  140. package/dist/server/defer-until-stream-consumed.d.ts +2 -1
  141. package/dist/server/defer-until-stream-consumed.js +3 -1
  142. package/dist/server/dev-server.js +39 -28
  143. package/dist/server/dev-stack-sourcemap.js +18 -23
  144. package/dist/server/document-initial-head.d.ts +0 -11
  145. package/dist/server/document-initial-head.js +1 -1
  146. package/dist/server/forwarded-middleware-context.d.ts +5 -0
  147. package/dist/server/forwarded-middleware-context.js +22 -0
  148. package/dist/server/headers.d.ts +4 -1
  149. package/dist/server/headers.js +4 -1
  150. package/dist/server/http-conditional.d.ts +11 -0
  151. package/dist/server/http-conditional.js +10 -1
  152. package/dist/server/http-error-responses.d.ts +1 -1
  153. package/dist/server/image-optimization.d.ts +38 -0
  154. package/dist/server/image-optimization.js +94 -4
  155. package/dist/server/isr-cache.d.ts +10 -0
  156. package/dist/server/isr-cache.js +41 -6
  157. package/dist/server/metadata-route-response.js +11 -10
  158. package/dist/server/middleware-matcher.d.ts +2 -0
  159. package/dist/server/middleware-matcher.js +23 -2
  160. package/dist/server/middleware-path-to-regexp.js +1 -1
  161. package/dist/server/middleware-runtime.d.ts +13 -0
  162. package/dist/server/middleware-runtime.js +38 -25
  163. package/dist/server/middleware.d.ts +1 -1
  164. package/dist/server/middleware.js +3 -1
  165. package/dist/server/multi-stage.d.ts +11 -0
  166. package/dist/server/navigation-planner.js +1 -1
  167. package/dist/server/normalize-path.d.ts +0 -13
  168. package/dist/server/normalize-path.js +1 -34
  169. package/dist/server/open-redirect.d.ts +11 -4
  170. package/dist/server/open-redirect.js +11 -4
  171. package/dist/server/pages-api-route.d.ts +3 -2
  172. package/dist/server/pages-api-route.js +6 -5
  173. package/dist/server/pages-cache-tag.d.ts +12 -0
  174. package/dist/server/pages-cache-tag.js +19 -0
  175. package/dist/server/pages-client-assets.d.ts +2 -0
  176. package/dist/server/pages-data-route.d.ts +10 -0
  177. package/dist/server/pages-data-route.js +21 -3
  178. package/dist/server/pages-document-asset-props.d.ts +5 -1
  179. package/dist/server/pages-document-asset-props.js +13 -5
  180. package/dist/server/pages-document-splice.d.ts +19 -0
  181. package/dist/server/pages-document-splice.js +55 -0
  182. package/dist/server/pages-i18n.d.ts +12 -1
  183. package/dist/server/pages-i18n.js +18 -4
  184. package/dist/server/pages-node-compat.d.ts +16 -5
  185. package/dist/server/pages-node-compat.js +20 -12
  186. package/dist/server/pages-page-data.d.ts +32 -24
  187. package/dist/server/pages-page-data.js +51 -121
  188. package/dist/server/pages-page-handler.d.ts +0 -6
  189. package/dist/server/pages-page-handler.js +189 -127
  190. package/dist/server/pages-page-response.d.ts +12 -6
  191. package/dist/server/pages-page-response.js +42 -35
  192. package/dist/server/pages-request-pipeline.d.ts +10 -2
  193. package/dist/server/pages-request-pipeline.js +41 -22
  194. package/dist/server/pages-request-stage-entry.d.ts +6 -0
  195. package/dist/server/pages-request-stage-entry.js +75 -16
  196. package/dist/server/pages-response-stage-entry.js +4 -0
  197. package/dist/server/pages-revalidate.js +3 -3
  198. package/dist/server/pages-router-entry.js +5 -4
  199. package/dist/server/prerender-manifest.d.ts +3 -3
  200. package/dist/server/prerender-manifest.js +4 -3
  201. package/dist/server/prerender-work-unit-setup.js +4 -0
  202. package/dist/server/prod-server.d.ts +12 -0
  203. package/dist/server/prod-server.js +204 -74
  204. package/dist/server/proper-error.d.ts +4 -0
  205. package/dist/server/proper-error.js +31 -0
  206. package/dist/server/request-pipeline.d.ts +4 -1
  207. package/dist/server/request-pipeline.js +3 -2
  208. package/dist/server/revalidation-request.d.ts +1 -0
  209. package/dist/server/revalidation-request.js +1 -1
  210. package/dist/server/rsc-stream-hints.d.ts +1 -1
  211. package/dist/server/rsc-stream-hints.js +52 -5
  212. package/dist/server/seed-cache.d.ts +1 -0
  213. package/dist/server/seed-cache.js +5 -4
  214. package/dist/server/server-reference-loader.d.ts +4 -0
  215. package/dist/server/server-reference-loader.js +29 -0
  216. package/dist/server/socket-error-backstop.d.ts +9 -1
  217. package/dist/server/socket-error-backstop.js +46 -17
  218. package/dist/server/static-file-signal.d.ts +15 -4
  219. package/dist/server/static-file-signal.js +46 -12
  220. package/dist/server/worker-image-optimization.d.ts +34 -0
  221. package/dist/server/worker-image-optimization.js +99 -0
  222. package/dist/server/worker-stages.d.ts +1 -1
  223. package/dist/server/worker-stages.js +2 -2
  224. package/dist/server/worker-utils.d.ts +8 -3
  225. package/dist/server/worker-utils.js +12 -7
  226. package/dist/shims/app-router-scroll.js +4 -1
  227. package/dist/shims/app-router-tree-context.d.ts +5 -0
  228. package/dist/shims/app-router-tree-context.js +27 -0
  229. package/dist/shims/cache-callable-runtime.d.ts +4 -2
  230. package/dist/shims/cache-callable-runtime.js +21 -12
  231. package/dist/shims/cache-handler.js +1 -9
  232. package/dist/shims/cache-runtime.d.ts +6 -1
  233. package/dist/shims/cache-runtime.js +2 -1
  234. package/dist/shims/cache.d.ts +2 -2
  235. package/dist/shims/cache.js +40 -13
  236. package/dist/shims/cdn-cache.d.ts +8 -0
  237. package/dist/shims/cdn-cache.js +17 -1
  238. package/dist/shims/disable-smooth-scroll.d.ts +3 -0
  239. package/dist/shims/disable-smooth-scroll.js +36 -0
  240. package/dist/shims/document-placeholders.d.ts +19 -0
  241. package/dist/shims/document-placeholders.js +45 -0
  242. package/dist/shims/document.js +21 -5
  243. package/dist/shims/dynamic-preload-chunks.d.ts +1 -0
  244. package/dist/shims/dynamic-preload-chunks.js +13 -9
  245. package/dist/shims/dynamic.d.ts +1 -1
  246. package/dist/shims/dynamic.js +60 -8
  247. package/dist/shims/fetch-cache-state.d.ts +66 -0
  248. package/dist/shims/fetch-cache-state.js +92 -0
  249. package/dist/shims/fetch-cache.d.ts +3 -67
  250. package/dist/shims/fetch-cache.js +160 -125
  251. package/dist/shims/head.d.ts +10 -6
  252. package/dist/shims/head.js +16 -14
  253. package/dist/shims/headers.js +2 -2
  254. package/dist/shims/image.js +2 -1
  255. package/dist/shims/internal/hybrid-client-route-owner-direct.js +1 -1
  256. package/dist/shims/internal/hybrid-client-route-owner.js +1 -0
  257. package/dist/shims/internal/work-store-scope.d.ts +5 -0
  258. package/dist/shims/internal/work-store-scope.js +24 -0
  259. package/dist/shims/link-prefetch.js +5 -4
  260. package/dist/shims/link.js +2 -2
  261. package/dist/shims/navigation.js +34 -9
  262. package/dist/shims/request-context.d.ts +2 -0
  263. package/dist/shims/request-state-types.d.ts +2 -1
  264. package/dist/shims/router.js +21 -32
  265. package/dist/shims/server.d.ts +27 -4
  266. package/dist/shims/server.js +65 -9
  267. package/dist/shims/unified-request-context.d.ts +1 -1
  268. package/dist/shims/url-utils.d.ts +9 -0
  269. package/dist/shims/url-utils.js +15 -4
  270. package/dist/utils/client-runtime-metadata.js +1 -1
  271. package/dist/utils/domain-locale.js +1 -1
  272. package/dist/utils/hash.d.ts +7 -0
  273. package/dist/utils/hash.js +11 -1
  274. package/dist/utils/package-name.d.ts +6 -0
  275. package/dist/utils/package-name.js +12 -1
  276. package/dist/utils/plugin-rsc-version.d.ts +6 -0
  277. package/dist/utils/plugin-rsc-version.js +41 -0
  278. package/dist/utils/prerender-output-paths.js +15 -4
  279. package/dist/utils/public-file-path.d.ts +10 -2
  280. package/dist/utils/public-file-path.js +13 -9
  281. package/dist/utils/public-routes.js +2 -2
  282. package/dist/utils/react-version.d.ts +10 -0
  283. package/dist/utils/react-version.js +39 -16
  284. package/package.json +11 -7
  285. package/dist/build/nitro-route-rules.d.ts +0 -47
  286. package/dist/build/nitro-route-rules.js +0 -79
  287. package/dist/plugins/cache-flight-codec.d.ts +0 -19
  288. package/dist/plugins/cache-flight-codec.js +0 -68
@@ -1,24 +1,24 @@
1
1
  import path from "../deps/.pnpm/pathslash@0.1.0/deps/pathslash/dist/index.js";
2
2
  import { isUnknownRecord } from "../utils/record.js";
3
3
  import { VINEXT_PRERENDER_ROUTE_PARAMS_HEADER, VINEXT_PRERENDER_SECRET_HEADER, VINEXT_PRERENDER_SPECULATIVE_HEADER } from "../utils/protocol-headers.js";
4
- import { ASSET_PREFIX_URL_DIR, assetPrefixPathname, isAbsoluteAssetPrefix } from "../utils/asset-prefix.js";
5
- import { StaticFileCache, contentTypeForPath, etagFromFilenameHash } from "./static-file-cache.js";
6
- import { normalizePathnameForRouteMatchStrict } from "../routing/utils.js";
7
4
  import { hasBasePath, stripBasePath } from "../utils/base-path.js";
8
5
  import { VINEXT_PRERENDER_RENDER_ERROR_HEADER, VINEXT_PRERENDER_SPECIAL_ERROR_HEADER } from "./headers.js";
9
- import { normalizePath } from "./normalize-path.js";
10
6
  import { notFoundResponse } from "./http-error-responses.js";
11
- import { isOpenRedirectShaped, sendRepeatedSlashRedirect } from "./open-redirect.js";
7
+ import { getRepeatedSlashRedirect, isOpenRedirectShaped, sendRepeatedSlashRedirect } from "./open-redirect.js";
12
8
  import { readStaticFileSignal } from "./static-file-signal.js";
13
9
  import { canonicalizeRequestPathname, filterInternalHeaders } from "./request-pipeline.js";
10
+ import { ASSET_PREFIX_URL_DIR, assetPrefixPathname, isAbsoluteAssetPrefix } from "../utils/asset-prefix.js";
11
+ import { StaticFileCache, contentTypeForPath, etagFromFilenameHash } from "./static-file-cache.js";
12
+ import { normalizePathnameForRouteMatchStrict } from "../routing/utils.js";
13
+ import { resolveRequestHost, resolveRequestProtocol, trustProxy, trustedHosts } from "./proxy-trust.js";
14
+ import { normalizePath } from "./normalize-path.js";
15
+ import { buildNextDataNotFoundResponse, encodeUrlParserIgnoredCharacters, isNextDataPathname, normalizeNextDataPagePathname, parseNextDataPathname, shouldAddTrailingSlashToPagesDataPath, urlParserCreatesPagesDataPath } from "./pages-data-route.js";
14
16
  import { buildCacheStateHeaders } from "./cache-headers.js";
15
17
  import { parseHttpDate } from "./http-date.js";
16
18
  import { evaluateStaticPreconditions } from "./http-conditional.js";
17
- import { buildNextDataNotFoundResponse, encodeUrlParserIgnoredCharacters, isNextDataPathname, normalizeNextDataPagePathname, parseNextDataPathname, shouldAddTrailingSlashToPagesDataPath, urlParserCreatesPagesDataPath } from "./pages-data-route.js";
18
19
  import { setPagesClientAssets } from "./pages-client-assets.js";
19
- import { resolveRequestHost, resolveRequestProtocol, trustProxy, trustedHosts } from "./proxy-trust.js";
20
20
  import { signalFromNodeResponse } from "./node-response-signal.js";
21
- import { DEFAULT_DEVICE_SIZES, DEFAULT_IMAGE_SIZES, IMAGE_RESPONSE_CACHE_STATE, isImageOptimizationPath, isSafeImageContentType, parseImageParams } from "./image-optimization.js";
21
+ import { DEFAULT_DEVICE_SIZES, DEFAULT_IMAGE_SIZES, IMAGE_RESPONSE_CACHE_STATE, INVALID_IMAGE_SOURCE_MESSAGE, isImageOptimizationPath, isImageSourceBodyStatus, isSafeImageContentType, parseImageParams, readImageOptimizationSignal } from "./image-optimization.js";
22
22
  import { installSocketErrorBackstop } from "./socket-error-backstop.js";
23
23
  import { collectInlineCssManifest } from "../build/inline-css.js";
24
24
  import { readTrustedRevalidationHostname } from "./revalidation-host.js";
@@ -28,6 +28,7 @@ import { runPagesRequest, wrapMiddlewareWithBasePath } from "./pages-request-pip
28
28
  import { computeClientRuntimeMetadata } from "../utils/client-runtime-metadata.js";
29
29
  import { readPrerenderSecret, readServerCompress } from "../build/server-manifest.js";
30
30
  import { readTrustedPrerenderRouteParamsFromHeaders, serializePrerenderRouteParamsHeader } from "./prerender-route-params.js";
31
+ import { resolveClientBuildFile } from "./client-build-files.js";
31
32
  import { registerPrerenderCloudflareLoader } from "../build/prerender-cloudflare-loader.js";
32
33
  import { seedMemoryCacheFromPrerender } from "./seed-cache.js";
33
34
  import { negotiateEncoding, parseAcceptedEncodings, selectContentEncoding } from "./accept-encoding.js";
@@ -335,10 +336,15 @@ function installClientBuildManifestGlobals(clientDir, assetBase, assetPrefix, cr
335
336
  function isNoBodyResponseStatus(status) {
336
337
  return NO_BODY_RESPONSE_STATUSES.has(status);
337
338
  }
338
- function cancelResponseBody(response) {
339
+ /**
340
+ * Discard a response body nobody will read. When the client is gone, pass the
341
+ * request signal's reason: React reports a reasonless cancel of a pending
342
+ * render as a server error, but recognizes `ResponseAborted` as a disconnect.
343
+ */
344
+ function cancelResponseBody(response, reason) {
339
345
  const body = response.body;
340
346
  if (!body || body.locked) return;
341
- body.cancel().catch(() => {});
347
+ body.cancel(reason).catch(() => {});
342
348
  }
343
349
  /**
344
350
  * Buffer a response body, cancelling it if the client disconnects first so a
@@ -433,6 +439,21 @@ function sendCompressed(req, res, body, contentType, statusCode, extraHeaders =
433
439
  res.end(buf);
434
440
  }
435
441
  }
442
+ /** The StaticFileCache key for a request pathname, or null when it is never served. */
443
+ function staticCacheLookupPath(pathname) {
444
+ let lookupPath = pathname;
445
+ if (pathname.includes("%")) try {
446
+ lookupPath = decodeURIComponent(pathname);
447
+ } catch {
448
+ return null;
449
+ }
450
+ if (lookupPath.startsWith("/.vite/") || lookupPath === "/.vite") return null;
451
+ return lookupPath;
452
+ }
453
+ /** Pathnames the Pages Router serves from routes rather than the client directory. */
454
+ function isPagesFilesystemExcludedPath(pathname, phase) {
455
+ return pathname === "/" || pathname === "/api" || pathname.startsWith("/api/") || phase === "direct" && pathname.startsWith(`/_next/static/`);
456
+ }
436
457
  /**
437
458
  * Try to serve a static file from the client build directory.
438
459
  *
@@ -456,18 +477,8 @@ async function tryServeStatic(req, res, clientDir, pathname, compress, cache, ex
456
477
  const rawIfRange = req.headers["if-range"];
457
478
  const ifRange = typeof rawIfRange === "string" ? rawIfRange : void 0;
458
479
  if (cache) {
459
- let lookupPath;
460
- if (pathname.includes("%")) {
461
- try {
462
- lookupPath = decodeURIComponent(pathname);
463
- } catch {
464
- return false;
465
- }
466
- if (lookupPath.startsWith("/.vite/") || lookupPath === "/.vite") return false;
467
- } else {
468
- if (pathname.startsWith("/.vite/") || pathname === "/.vite") return false;
469
- lookupPath = pathname;
470
- }
480
+ const lookupPath = staticCacheLookupPath(pathname);
481
+ if (lookupPath === null) return false;
471
482
  const entry = cache.lookup(lookupPath);
472
483
  if (!entry) return false;
473
484
  if (req.method !== "GET" && req.method !== "HEAD") {
@@ -878,7 +889,7 @@ function waitForNodeResponseCompletion(res) {
878
889
  * Pages Router (dist/server/entry.js) and configures the appropriate handler.
879
890
  */
880
891
  async function startProdServer(options = {}) {
881
- installSocketErrorBackstop();
892
+ installSocketErrorBackstop({ runningServer: options.purpose !== "prerender" });
882
893
  const { port = process.env.PORT ? parseInt(process.env.PORT) : 3e3, host = "0.0.0.0", outDir = path.resolve("dist"), rscEntryPath: explicitRscEntryPath, serverDir: explicitServerDir, serverEntryPath: explicitServerEntryPath, noCompression = false, purpose, silent = false } = options;
883
894
  if (purpose === "prerender") registerPrerenderCloudflareLoader();
884
895
  const compress = !noCompression;
@@ -1110,6 +1121,59 @@ async function startAppRouterServer(options) {
1110
1121
  const seededRoutes = await runWithServerEntryRequire(rscEntryRequire, () => seedPrerenderedRoutes(serverDir));
1111
1122
  if (seededRoutes > 0) console.log(`[vinext] Seeded ${seededRoutes} pre-rendered route${seededRoutes !== 1 ? "s" : ""} into memory cache`);
1112
1123
  const staticCache = await StaticFileCache.create(clientDir);
1124
+ const serveImageOptimization = async (req, res, request, signal) => {
1125
+ const stagedHeaders = omitHeadersCaseInsensitive(mergeResponseHeaders({}, new Response(null, { headers: signal.middlewareHeaders })), OMIT_STATIC_RESPONSE_HEADERS);
1126
+ for (const [name, value] of Object.entries(stagedHeaders)) res.setHeader(name, value);
1127
+ const parsedUrl = new URL(`/${signal.search}`, "http://localhost");
1128
+ const params = parseImageParams(parsedUrl, appImageAllowedWidths, imageConfig?.qualities);
1129
+ if (!params) {
1130
+ res.writeHead(400);
1131
+ res.end("Bad Request");
1132
+ return;
1133
+ }
1134
+ const sendUnsafeContentType = () => {
1135
+ res.writeHead(400);
1136
+ res.end("The requested resource is not an allowed image type");
1137
+ };
1138
+ const isSafeSourcePath = (sourcePath) => isSafeImageContentType(contentTypeForPath(sourcePath), imageConfig?.dangerouslyAllowSVG);
1139
+ const imageSecurityHeaders = {
1140
+ "Content-Security-Policy": imageConfig?.contentSecurityPolicy ?? "script-src 'none'; frame-src 'none'; sandbox;",
1141
+ "X-Content-Type-Options": "nosniff",
1142
+ "Content-Disposition": imageConfig?.contentDispositionType === "attachment" ? "attachment" : "inline"
1143
+ };
1144
+ const serveSource = (sourcePath) => tryServeStatic(req, res, clientDir, sourcePath, false, staticCache, imageSecurityHeaders, void 0, IMAGE_REPRESENTATION_HEADERS);
1145
+ const sourceUrl = new URL(params.imageUrl, request.url);
1146
+ let sourcePathname = null;
1147
+ try {
1148
+ sourcePathname = normalizePath(normalizePathnameForRouteMatchStrict(sourceUrl.pathname));
1149
+ } catch {}
1150
+ if (!isSafeSourcePath(sourcePathname ?? params.imageUrl)) {
1151
+ sendUnsafeContentType();
1152
+ return;
1153
+ }
1154
+ const assetLookupPath = sourcePathname !== null ? resolveAppRouterAssetPath(sourcePathname, appAssetPathPrefix, appRouterAssetPrefix) : null;
1155
+ if (assetLookupPath && await serveSource(assetLookupPath)) return;
1156
+ if (sourcePathname !== null) {
1157
+ const sourceResponse = await rscHandler(new Request(sourceUrl, { signal: request.signal }), createNodeExecutionContext(resolveTrustedNodeRevalidateOrigin(req, host, port)));
1158
+ const sourceSignal = isImageSourceBodyStatus(sourceResponse.status) ? readStaticFileSignal(sourceResponse) : null;
1159
+ cancelResponseBody(sourceResponse, request.signal.reason);
1160
+ if (request.signal.aborted) return;
1161
+ if (sourceSignal !== null) {
1162
+ let sourcePath = sourceSignal;
1163
+ try {
1164
+ sourcePath = decodeURIComponent(sourceSignal);
1165
+ } catch {}
1166
+ if (!isSafeSourcePath(sourcePath)) {
1167
+ sendUnsafeContentType();
1168
+ return;
1169
+ }
1170
+ if (await serveSource(sourcePath)) return;
1171
+ }
1172
+ }
1173
+ res.writeHead(400);
1174
+ res.end(INVALID_IMAGE_SOURCE_MESSAGE);
1175
+ };
1176
+ const clientBuildFiles = new Set(Array.isArray(rscModule.__clientBuildFiles) ? rscModule.__clientBuildFiles : []);
1113
1177
  const handleRequestImpl = async (req, res) => {
1114
1178
  const rawUrl = req.url ?? "/";
1115
1179
  const rawPathname = rawUrl.split("?")[0];
@@ -1142,29 +1206,9 @@ async function startAppRouterServer(options) {
1142
1206
  if (await tryServeStatic(req, res, clientDir, assetLookupPath, compress, staticCache)) return;
1143
1207
  }
1144
1208
  }
1145
- if (isImageOptimizationPath(pathname)) {
1146
- const parsedUrl = new URL(rawUrl, "http://localhost");
1147
- const params = parseImageParams(parsedUrl, appImageAllowedWidths, imageConfig?.qualities);
1148
- if (!params) {
1149
- res.writeHead(400);
1150
- res.end("Bad Request");
1151
- return;
1152
- }
1153
- const ct = contentTypeForPath(params.imageUrl);
1154
- if (!isSafeImageContentType(ct, imageConfig?.dangerouslyAllowSVG)) {
1155
- res.writeHead(400);
1156
- res.end("The requested resource is not an allowed image type");
1157
- return;
1158
- }
1159
- const imageSecurityHeaders = {
1160
- "Content-Security-Policy": imageConfig?.contentSecurityPolicy ?? "script-src 'none'; frame-src 'none'; sandbox;",
1161
- "X-Content-Type-Options": "nosniff",
1162
- "Content-Disposition": imageConfig?.contentDispositionType === "attachment" ? "attachment" : "inline"
1163
- };
1164
- if (await tryServeStatic(req, res, clientDir, params.imageUrl, false, staticCache, imageSecurityHeaders, void 0, IMAGE_REPRESENTATION_HEADERS)) return;
1165
- res.writeHead(404);
1166
- res.end("Image not found");
1167
- return;
1209
+ if (req.method === "GET" || req.method === "HEAD") {
1210
+ const clientBuildFile = resolveClientBuildFile(normalizedRawPathname, appRouterBasePath, clientBuildFiles);
1211
+ if (clientBuildFile && await tryServeStatic(req, res, clientDir, clientBuildFile, compress, staticCache)) return;
1168
1212
  }
1169
1213
  try {
1170
1214
  const request = nodeToWebRequest(req, rawUrl, prerenderSecret, appRouterI18nConfig, appRouterAuthorizeOnDemandRevalidate, signalFromNodeResponse(res));
@@ -1175,7 +1219,13 @@ async function startAppRouterServer(options) {
1175
1219
  };
1176
1220
  const response = await rscHandler(request, ctx);
1177
1221
  if (request.signal.aborted) {
1222
+ cancelResponseBody(response, request.signal.reason);
1223
+ return;
1224
+ }
1225
+ const imageSignal = readImageOptimizationSignal(response);
1226
+ if (imageSignal) {
1178
1227
  cancelResponseBody(response);
1228
+ await serveImageOptimization(req, res, request, imageSignal);
1179
1229
  return;
1180
1230
  }
1181
1231
  const staticFileSignal = readStaticFileSignal(response);
@@ -1270,7 +1320,45 @@ function readPagesServerEntryPageRoutes(value) {
1270
1320
  * - vinextConfig — embedded next.config.js settings
1271
1321
  */
1272
1322
  async function startPagesRouterServer(options) {
1273
- const { port, host, clientDir, serverDir, serverEntryPath, compress: compressOption, purpose, silent } = options;
1323
+ const { port, host, purpose, silent } = options;
1324
+ const handler = await createPagesRouterRequestHandler(options);
1325
+ const server = createServer(handler);
1326
+ await new Promise((resolve) => {
1327
+ server.listen(port, host, () => {
1328
+ const addr = server.address();
1329
+ const actualPort = typeof addr === "object" && addr ? addr.port : port;
1330
+ if (!silent) logProdServerStarted(host, actualPort, purpose);
1331
+ resolve();
1332
+ });
1333
+ });
1334
+ const addr = server.address();
1335
+ return {
1336
+ server,
1337
+ port: typeof addr === "object" && addr ? addr.port : port
1338
+ };
1339
+ }
1340
+ /**
1341
+ * Create the Pages Router request handler for `vite preview`. Vite's preview
1342
+ * middleware (proxy, compression, `base`, static files) runs first, so this
1343
+ * handles only the requests Vite doesn't serve and leaves compression to Vite.
1344
+ * Resolves to null when the build has no Pages Router server entry.
1345
+ */
1346
+ async function createPagesPreviewHandler(options) {
1347
+ const clientDir = path.resolve(options.clientDir);
1348
+ const serverDir = path.resolve(options.serverDir);
1349
+ const serverEntryPath = path.join(serverDir, "entry.js");
1350
+ if (!fs.existsSync(serverEntryPath)) return null;
1351
+ return createPagesRouterRequestHandler({
1352
+ port: options.port,
1353
+ host: options.host,
1354
+ clientDir,
1355
+ serverDir,
1356
+ serverEntryPath,
1357
+ compress: false
1358
+ });
1359
+ }
1360
+ async function createPagesRouterRequestHandler(options) {
1361
+ const { port, host, clientDir, serverDir, serverEntryPath, compress: compressOption, purpose } = options;
1274
1362
  const serverEntry = await importServerEntryModule(serverEntryPath);
1275
1363
  const serverEntryRequire = createServerEntryRequire(serverEntryPath);
1276
1364
  const { renderPage, handleApiRoute: handleApi, authorizeOnDemandRevalidate, runMiddleware, vinextConfig, buildId: pagesBuildId } = serverEntry;
@@ -1286,6 +1374,7 @@ async function startPagesRouterServer(options) {
1286
1374
  const pagesAssetPathPrefix = assetPrefixPathname(assetPrefix);
1287
1375
  const assetBase = basePath ? `${basePath}/` : "/";
1288
1376
  const trailingSlash = vinextConfig?.trailingSlash ?? false;
1377
+ const skipTrailingSlashRedirect = vinextConfig?.skipTrailingSlashRedirect ?? false;
1289
1378
  const skipProxyUrlNormalize = vinextConfig?.skipProxyUrlNormalize ?? false;
1290
1379
  const i18nConfig = vinextConfig?.i18n ?? null;
1291
1380
  const configRedirects = vinextConfig?.redirects ?? [];
@@ -1445,7 +1534,8 @@ async function startPagesRouterServer(options) {
1445
1534
  const method = req.method ?? "GET";
1446
1535
  const signal = signalFromNodeResponse(res);
1447
1536
  const hasBody = method !== "GET" && method !== "HEAD" && !signal.aborted;
1448
- const webRequest = new Request(`${protocol}://${revalidationHostname ?? hostHeader}${url}`, {
1537
+ const requestOrigin = `${protocol}://${revalidationHostname ?? hostHeader}`;
1538
+ const webRequest = new Request(new URL(url, requestOrigin), {
1449
1539
  method,
1450
1540
  headers: reqHeaders,
1451
1541
  signal,
@@ -1453,10 +1543,44 @@ async function startPagesRouterServer(options) {
1453
1543
  duplex: hasBody ? "half" : void 0
1454
1544
  });
1455
1545
  const middlewareRequest = isDataReq && skipProxyUrlNormalize ? new Request(new URL(originalRenderUrl, webRequest.url), webRequest) : void 0;
1546
+ const resolvePublicImageSource = async (sourceUrl) => {
1547
+ const rawSourcePathname = sourceUrl.pathname;
1548
+ if (getRepeatedSlashRedirect(rawSourcePathname) || isOpenRedirectShaped(rawSourcePathname)) return null;
1549
+ const sourceRequestPathname = normalizePath(rawSourcePathname);
1550
+ const sourceHadBasePath = !basePath || hasBasePath(sourceRequestPathname, basePath);
1551
+ const strippedSourcePathname = stripBasePath(sourceRequestPathname, basePath);
1552
+ if (isNextDataPathname(strippedSourcePathname)) return null;
1553
+ let sourcePath = null;
1554
+ const result = await runPagesRequest(new Request(new URL(strippedSourcePathname + sourceUrl.search, sourceUrl), { signal: webRequest.signal }), {
1555
+ ...deps,
1556
+ hadBasePath: sourceHadBasePath,
1557
+ isDataReq: false,
1558
+ isDataRequest: false,
1559
+ middlewareRequest: void 0,
1560
+ dataNotFoundResponse: null,
1561
+ recordCacheability: false,
1562
+ rawSearch: sourceUrl.search,
1563
+ configMatchPathname: strippedSourcePathname,
1564
+ runMiddleware: typeof runMiddleware === "function" ? wrapMiddlewareWithBasePath(runMiddleware, basePath, sourceHadBasePath) : null,
1565
+ renderPage: null,
1566
+ handleApi: null,
1567
+ serveFilesystemRoute: async (candidate, _stagedHeaders, phase) => {
1568
+ if (isImageOptimizationPath(candidate) || isPagesFilesystemExcludedPath(candidate, phase)) return false;
1569
+ const lookupPath = staticCacheLookupPath(candidate);
1570
+ if (lookupPath === null || lookupPath === "/" || !staticCache.lookup(lookupPath)) return false;
1571
+ sourcePath = candidate;
1572
+ return new Response(null);
1573
+ }
1574
+ });
1575
+ if (result.type !== "response") return null;
1576
+ cancelResponseBody(result.response);
1577
+ return isImageSourceBodyStatus(result.response.status) ? sourcePath : null;
1578
+ };
1456
1579
  const deps = {
1457
1580
  assetPrefix,
1458
1581
  basePath,
1459
1582
  trailingSlash,
1583
+ skipTrailingSlashRedirect,
1460
1584
  i18nConfig,
1461
1585
  configRedirects,
1462
1586
  configRewrites,
@@ -1479,8 +1603,8 @@ async function startPagesRouterServer(options) {
1479
1603
  originalUrl: originalRenderUrl
1480
1604
  }, stagedHeaders?.has("Cache-Control") ? new Headers({ "Cache-Control": stagedHeaders.get("Cache-Control") }) : void 0) : null,
1481
1605
  handleApi: typeof handleApi === "function" ? (request, apiUrl) => handleApi(request, apiUrl, createNodeExecutionContext(), resolveTrustedNodeRevalidateOrigin(req, host, port), "node") : null,
1482
- serveFilesystemRoute: async (requestPathname, stagedHeaders, phase, resolvedUrl) => {
1483
- if (isImageOptimizationPath(requestPathname)) {
1606
+ serveFilesystemRoute: async (requestPathname, stagedHeaders, phase, resolvedUrl, _request, resolvedPathnameHasBasePath) => {
1607
+ if (resolvedPathnameHasBasePath && isImageOptimizationPath(requestPathname)) {
1484
1608
  for (const [name, value] of Object.entries(stagedHeaders)) res.setHeader(name, value);
1485
1609
  const parsedUrl = new URL(resolvedUrl, "http://localhost");
1486
1610
  const params = parseImageParams(parsedUrl, allowedImageWidths, pagesImageConfig?.qualities);
@@ -1489,23 +1613,42 @@ async function startPagesRouterServer(options) {
1489
1613
  res.end("Bad Request");
1490
1614
  return true;
1491
1615
  }
1492
- const ct = contentTypeForPath(params.imageUrl);
1493
- if (!isSafeImageContentType(ct, pagesImageConfig?.dangerouslyAllowSVG)) {
1616
+ const sendUnsafeContentType = () => {
1494
1617
  res.writeHead(400);
1495
1618
  res.end("The requested resource is not an allowed image type");
1496
- return true;
1497
- }
1619
+ };
1620
+ const isSafeSourcePath = (sourcePath) => isSafeImageContentType(contentTypeForPath(sourcePath), pagesImageConfig?.dangerouslyAllowSVG);
1498
1621
  const imageSecurityHeaders = {
1499
1622
  "Content-Security-Policy": pagesImageConfig?.contentSecurityPolicy ?? "script-src 'none'; frame-src 'none'; sandbox;",
1500
1623
  "X-Content-Type-Options": "nosniff",
1501
1624
  "Content-Disposition": pagesImageConfig?.contentDispositionType === "attachment" ? "attachment" : "inline"
1502
1625
  };
1503
- if (await tryServeStatic(req, res, clientDir, params.imageUrl, false, staticCache, imageSecurityHeaders, void 0, IMAGE_REPRESENTATION_HEADERS)) return true;
1504
- res.writeHead(404);
1505
- res.end("Image not found");
1626
+ const serveSource = (sourcePath) => tryServeStatic(req, res, clientDir, sourcePath, false, staticCache, imageSecurityHeaders, void 0, IMAGE_REPRESENTATION_HEADERS);
1627
+ const sourceUrl = new URL(params.imageUrl, webRequest.url);
1628
+ let sourcePathname = null;
1629
+ try {
1630
+ sourcePathname = normalizePath(normalizePathnameForRouteMatchStrict(sourceUrl.pathname));
1631
+ } catch {}
1632
+ if (!isSafeSourcePath(sourcePathname ?? params.imageUrl)) {
1633
+ sendUnsafeContentType();
1634
+ return true;
1635
+ }
1636
+ const assetLookupPath = sourcePathname !== null ? resolveAppRouterAssetPath(sourcePathname, pagesAssetPathPrefix, assetPrefix) : null;
1637
+ if (assetLookupPath && await serveSource(assetLookupPath)) return true;
1638
+ const sourcePath = sourcePathname !== null ? await resolvePublicImageSource(sourceUrl) : null;
1639
+ if (webRequest.signal.aborted) return true;
1640
+ if (sourcePath !== null) {
1641
+ if (!isSafeSourcePath(staticCacheLookupPath(sourcePath) ?? sourcePath)) {
1642
+ sendUnsafeContentType();
1643
+ return true;
1644
+ }
1645
+ if (await serveSource(sourcePath)) return true;
1646
+ }
1647
+ res.writeHead(400);
1648
+ res.end(INVALID_IMAGE_SOURCE_MESSAGE);
1506
1649
  return true;
1507
1650
  }
1508
- if (requestPathname === "/" || requestPathname === "/api" || requestPathname.startsWith("/api/") || phase === "direct" && requestPathname.startsWith(`/_next/static/`)) return false;
1651
+ if (isPagesFilesystemExcludedPath(requestPathname, phase)) return false;
1509
1652
  return tryServeStatic(req, res, clientDir, requestPathname, compress, staticCache, stagedHeaders);
1510
1653
  }
1511
1654
  };
@@ -1514,7 +1657,7 @@ async function startPagesRouterServer(options) {
1514
1657
  if (result.type === "response") {
1515
1658
  const { response } = result;
1516
1659
  if (webRequest.signal.aborted) {
1517
- cancelResponseBody(response);
1660
+ cancelResponseBody(response, webRequest.signal.reason);
1518
1661
  return;
1519
1662
  }
1520
1663
  const streamedApi = isVinextStreamedApiResponse(response);
@@ -1548,22 +1691,9 @@ async function startPagesRouterServer(options) {
1548
1691
  }
1549
1692
  }
1550
1693
  };
1551
- const server = createServer((req, res) => {
1694
+ return (req, res) => {
1552
1695
  runWithServerEntryRequire(serverEntryRequire, () => handleRequest(req, res));
1553
- });
1554
- await new Promise((resolve) => {
1555
- server.listen(port, host, () => {
1556
- const addr = server.address();
1557
- const actualPort = typeof addr === "object" && addr ? addr.port : port;
1558
- if (!silent) logProdServerStarted(host, actualPort, purpose);
1559
- resolve();
1560
- });
1561
- });
1562
- const addr = server.address();
1563
- return {
1564
- server,
1565
- port: typeof addr === "object" && addr ? addr.port : port
1566
1696
  };
1567
1697
  }
1568
1698
  //#endregion
1569
- export { COMPRESS_THRESHOLD, importServerEntryModule, isCompressibleContentType, mergeResponseHeaders, mergeWebResponse, negotiateEncoding, nodeToWebRequest, readNodeStream, rememberCurrentServerEntryImportMtime, resolveAppRouterAssetPath, resolveAppRouterPrerenderSeeder, resolveRequestHost as resolveHost, resolveServerEntryImportUrl, sendCompressed, sendWebResponse, startProdServer, trustProxy, trustedHosts, tryServeStatic, waitForNodeResponseCompletion };
1699
+ export { COMPRESS_THRESHOLD, createPagesPreviewHandler, importServerEntryModule, isCompressibleContentType, mergeResponseHeaders, mergeWebResponse, negotiateEncoding, nodeToWebRequest, readNodeStream, rememberCurrentServerEntryImportMtime, resolveAppRouterAssetPath, resolveAppRouterPrerenderSeeder, resolveRequestHost as resolveHost, resolveServerEntryImportUrl, sendCompressed, sendWebResponse, startProdServer, trustProxy, trustedHosts, tryServeStatic, waitForNodeResponseCompletion };
@@ -0,0 +1,4 @@
1
+ //#region src/server/proper-error.d.ts
2
+ /** Turn a thrown value into an Error, as Next.js does before reporting it. */
3
+ export declare function getProperError(error: unknown): Error;
4
+ //#endregion
@@ -0,0 +1,31 @@
1
+ //#region src/server/proper-error.ts
2
+ function isPlainObject(value) {
3
+ if (Object.prototype.toString.call(value) !== "[object Object]") return false;
4
+ const prototype = Object.getPrototypeOf(value);
5
+ return prototype === null || Object.prototype.hasOwnProperty.call(prototype, "isPrototypeOf");
6
+ }
7
+ /** JSON.stringify that replaces circular references with "[Circular]". */
8
+ function safeStringifyLite(value) {
9
+ const seen = /* @__PURE__ */ new WeakSet();
10
+ return JSON.stringify(value, (_key, nested) => {
11
+ if (typeof nested === "object" && nested !== null) {
12
+ if (seen.has(nested)) return "[Circular]";
13
+ seen.add(nested);
14
+ }
15
+ return nested;
16
+ });
17
+ }
18
+ function isError(value) {
19
+ return typeof value === "object" && value !== null && "name" in value && "message" in value;
20
+ }
21
+ /** Turn a thrown value into an Error, as Next.js does before reporting it. */
22
+ function getProperError(error) {
23
+ if (isError(error)) return error;
24
+ if (process.env.NODE_ENV === "development") {
25
+ if (typeof error === "undefined") return /* @__PURE__ */ new Error("An undefined error was thrown, see here for more info: https://nextjs.org/docs/messages/threw-undefined");
26
+ if (error === null) return /* @__PURE__ */ new Error("A null error was thrown, see here for more info: https://nextjs.org/docs/messages/threw-undefined");
27
+ }
28
+ return new Error(isPlainObject(error) ? safeStringifyLite(error) : String(error));
29
+ }
30
+ //#endregion
31
+ export { getProperError };
@@ -69,7 +69,10 @@ export declare function guardProtocolRelativeUrl(rawPathname: string, search?: s
69
69
  export type HeaderRecord = Record<string, string | string[]>;
70
70
  type ResolvePublicFileRouteOptions = {
71
71
  cleanPathname: string;
72
- middlewareContext: StaticFileSignalContext;
72
+ /** `requestHeaders` holds middleware's request-header override instructions. */
73
+ middlewareContext: StaticFileSignalContext & {
74
+ requestHeaders?: Headers | null;
75
+ };
73
76
  pathname: string;
74
77
  publicFiles: ReadonlySet<string>;
75
78
  request: Request;
@@ -1,5 +1,5 @@
1
1
  import "../utils/protocol-headers.js";
2
- import { getUnconsumedMiddlewareRequestHeaders } from "../utils/middleware-request-headers.js";
2
+ import { buildRequestHeadersFromMiddlewareResponse, getUnconsumedMiddlewareRequestHeaders } from "../utils/middleware-request-headers.js";
3
3
  import { hasBasePath, removeTrailingSlash, stripBasePath } from "../utils/base-path.js";
4
4
  import { INTERNAL_HEADERS, VINEXT_INTERNAL_HEADERS } from "./headers.js";
5
5
  import { forbiddenResponse, methodNotAllowedResponse, notFoundResponse } from "./http-error-responses.js";
@@ -100,7 +100,8 @@ function resolvePublicFileRoute(options) {
100
100
  if (options.pathname.endsWith(".rsc")) return null;
101
101
  if (!options.publicFiles.has(options.cleanPathname)) return null;
102
102
  if (options.request.method !== "GET" && options.request.method !== "HEAD") return methodNotAllowedResponse("GET, HEAD", { headers: options.middlewareContext.headers ?? void 0 });
103
- return createStaticFileSignal(options.cleanPathname, options.middlewareContext);
103
+ const { requestHeaders } = options.middlewareContext;
104
+ return createStaticFileSignal(options.cleanPathname, options.middlewareContext, requestHeaders ? buildRequestHeadersFromMiddlewareResponse(options.request.headers, requestHeaders) : null);
104
105
  }
105
106
  function normalizeTrailingSlashPathname(pathname, trailingSlash) {
106
107
  if (pathname === "/" || pathname === "/api" || pathname.startsWith("/api/")) return null;
@@ -1,6 +1,7 @@
1
1
  import { PRERENDER_REVALIDATE_HEADER } from "../utils/protocol-headers.js";
2
2
  //#region src/server/revalidation-request.d.ts
3
3
  export declare function getRevalidateSecret(): string;
4
+ export declare function safeEqual(first: string, second: string): boolean;
4
5
  export declare function isRevalidateSecret(value: string | null | undefined): boolean;
5
6
  /** Match Next.js: the header value must equal the preview/revalidation secret. */
6
7
  export declare function isOnDemandRevalidateRequest(headerValue: string | string[] | null | undefined): boolean;
@@ -34,4 +34,4 @@ function isOnDemandRevalidateRequest(headerValue) {
34
34
  return typeof headerValue === "string" && isRevalidateSecret(headerValue);
35
35
  }
36
36
  //#endregion
37
- export { PRERENDER_REVALIDATE_HEADER, getRevalidateSecret, isOnDemandRevalidateRequest, isRevalidateSecret };
37
+ export { PRERENDER_REVALIDATE_HEADER, getRevalidateSecret, isOnDemandRevalidateRequest, isRevalidateSecret, safeEqual };
@@ -11,7 +11,7 @@
11
11
  * tag is `HL` are decoded and inspected. Length-prefixed bodies remain separate
12
12
  * so binary bytes do not force adjacent text through the embed's base64 path.
13
13
  */
14
- export declare function normalizeReactFlightPreloadHints(stream: ReadableStream<Uint8Array>): ReadableStream<Uint8Array>;
14
+ export declare function normalizeReactFlightPreloadHints(stream: ReadableStream<Uint8Array>, onCancel?: (reason: unknown) => void): ReadableStream<Uint8Array>;
15
15
  export type RscRawRenderer = (model: unknown, options?: unknown) => ReadableStream<Uint8Array>;
16
16
  export type RscRawPrerenderer = (model: unknown, options?: unknown) => Promise<{
17
17
  prelude: ReadableStream<Uint8Array>;
@@ -1,3 +1,4 @@
1
+ import { createRenderStreamCancellation } from "./app-render-abort-error.js";
1
2
  //#region src/server/rsc-stream-hints.ts
2
3
  const REACT_FLIGHT_STYLESHEET_PRELOAD_HINT = /^([0-9a-f]*:HL\[.*?),"stylesheet"(\]|,)/;
3
4
  const STYLESHEET_TO_STYLE_JSON_PADDING = " ".repeat(5);
@@ -55,6 +56,15 @@ function indexOfByte(bytes, byte, from) {
55
56
  for (let index = from; index < bytes.byteLength; index++) if (bytes[index] === byte) return index;
56
57
  return -1;
57
58
  }
59
+ function utf8CompleteEnd(bytes, from) {
60
+ const length = bytes.byteLength;
61
+ for (let index = length - 1; index >= from && index >= length - 3; index--) {
62
+ const byte = bytes[index];
63
+ if ((byte & 192) === 128) continue;
64
+ return index + (byte >= 240 ? 4 : byte >= 224 ? 3 : byte >= 192 ? 2 : 1) > length ? index : length;
65
+ }
66
+ return length;
67
+ }
58
68
  function parseHexBytes(bytes, start, end) {
59
69
  if (start === end) return null;
60
70
  let value = 0;
@@ -97,20 +107,34 @@ function isTagInTable(table, byte) {
97
107
  * tag is `HL` are decoded and inspected. Length-prefixed bodies remain separate
98
108
  * so binary bytes do not force adjacent text through the embed's base64 path.
99
109
  */
100
- function normalizeReactFlightPreloadHints(stream) {
110
+ function normalizeReactFlightPreloadHints(stream, onCancel) {
101
111
  let carry = null;
102
112
  let rawBytesRemaining = 0;
103
113
  let passThrough = false;
114
+ let inPlainRow = false;
104
115
  return stream.pipeThrough(new TransformStream({
105
116
  transform(chunk, controller) {
106
117
  if (passThrough) {
107
118
  controller.enqueue(chunk);
108
119
  return;
109
120
  }
121
+ let start = 0;
122
+ if (inPlainRow) {
123
+ const end = indexOfByte(chunk, NEWLINE_BYTE, 0);
124
+ if (end === -1) {
125
+ const bytes = carry === null ? chunk : concatBytes(carry, chunk);
126
+ const complete = utf8CompleteEnd(bytes, 0);
127
+ carry = complete === bytes.byteLength ? null : bytes.slice(complete);
128
+ if (complete > 0) controller.enqueue(complete === bytes.byteLength ? bytes : bytes.subarray(0, complete));
129
+ return;
130
+ }
131
+ inPlainRow = false;
132
+ start = (carry === null ? 0 : carry.byteLength) + end + 1;
133
+ }
110
134
  const bytes = carry === null ? chunk : concatBytes(carry, chunk);
111
135
  carry = null;
112
136
  const byteLength = bytes.byteLength;
113
- let offset = 0;
137
+ let offset = start;
114
138
  let emittedThrough = 0;
115
139
  let output = bytes;
116
140
  let ownsOutput = bytes !== chunk;
@@ -146,7 +170,13 @@ function normalizeReactFlightPreloadHints(stream) {
146
170
  break;
147
171
  }
148
172
  const newline = indexOfByte(bytes, NEWLINE_BYTE, offset);
149
- if (newline === -1) break;
173
+ if (newline === -1) {
174
+ if (!(tagByte === HINT_TAG_BYTE && (colon + 2 === byteLength || bytes[colon + 2] === LINK_HINT_CODE_BYTE))) {
175
+ inPlainRow = true;
176
+ offset = utf8CompleteEnd(bytes, offset);
177
+ }
178
+ break;
179
+ }
150
180
  if (tagByte === HINT_TAG_BYTE && bytes[colon + 2] === LINK_HINT_CODE_BYTE) {
151
181
  const line = bytes.subarray(offset, newline + 1);
152
182
  const normalized = normalizeReactFlightHintLine(line);
@@ -165,11 +195,28 @@ function normalizeReactFlightPreloadHints(stream) {
165
195
  },
166
196
  flush(controller) {
167
197
  if (carry !== null && carry.byteLength > 0) controller.enqueue(rawBytesRemaining > 0 ? carry : normalizeReactFlightHintLine(carry));
168
- }
198
+ },
199
+ cancel: onCancel
169
200
  }));
170
201
  }
171
202
  function createRscRenderer(render) {
172
- return (model, options) => normalizeReactFlightPreloadHints(render(model, options));
203
+ return (model, options) => {
204
+ const cancellation = createRenderStreamCancellation();
205
+ return normalizeReactFlightPreloadHints(render(model, ignoreCancellationErrors(options, cancellation)), cancellation.record);
206
+ };
207
+ }
208
+ /** A consumer cancelling the payload is not a render failure. */
209
+ function ignoreCancellationErrors(options, cancellation) {
210
+ if (!options || typeof options !== "object") return options;
211
+ const onError = Reflect.get(options, "onError");
212
+ if (typeof onError !== "function") return options;
213
+ return {
214
+ ...options,
215
+ onError(error, ...rest) {
216
+ if (cancellation.isReason(error)) return void 0;
217
+ return Reflect.apply(onError, options, [error, ...rest]);
218
+ }
219
+ };
173
220
  }
174
221
  function createRscPrerenderer(prerender) {
175
222
  return async (model, options) => {
@@ -14,6 +14,7 @@ type PrerenderCacheSeedMetadata = {
14
14
  };
15
15
  type PrerenderCacheSeedOptions = {
16
16
  buildAppPageHtmlKey?: (pathname: string) => string;
17
+ buildAppPageFallbackShellKey?: (pathname: string, suffix: "html" | "rsc") => string;
17
18
  buildAppPageRscKey?: (pathname: string) => string;
18
19
  buildAppRouteKey?: (pathname: string) => string;
19
20
  writeAppPageEntry?: (key: string, data: CachedAppPageValue, metadata: PrerenderCacheSeedMetadata) => Promise<void>;
@@ -1,5 +1,5 @@
1
1
  import path from "../deps/.pnpm/pathslash@0.1.0/deps/pathslash/dist/index.js";
2
- import { appIsrCacheKey, isrSet, isrSetPrerenderedAppPage } from "./isr-cache.js";
2
+ import { appIsrCacheKey, appIsrFallbackShellKey, isrSet, isrSetPrerenderedAppPage } from "./isr-cache.js";
3
3
  import { addPregeneratedConcretePath, addPregeneratedRoute, clearPregeneratedConcretePaths, normalizePregeneratedPathname } from "./pregenerated-concrete-paths.js";
4
4
  import { getRenderedAppRoutes, getRenderedMetadataRoutes, isFallbackShellArtifactPath, readPrerenderManifest } from "./prerender-manifest.js";
5
5
  import { buildAppPageCacheTags, buildAppRouteCacheTags } from "./app-page-cache.js";
@@ -61,11 +61,12 @@ async function seedMemoryCacheFromPrerender(serverDir, options) {
61
61
  const appRoutes = getRenderedAppRoutes(routes);
62
62
  for (const route of appRoutes) {
63
63
  const concretePathname = route.path ?? route.route;
64
- if (!isFallbackShellArtifactPath(concretePathname, route)) addPregeneratedConcretePath(route.route, concretePathname);
64
+ const isFallbackShell = isFallbackShellArtifactPath(concretePathname, route);
65
+ if (!isFallbackShell) addPregeneratedConcretePath(route.route, concretePathname);
65
66
  const artifactPathname = route.path ?? route.route;
66
67
  const cachePathname = normalizePregeneratedPathname(artifactPathname);
67
- const htmlKey = options?.buildAppPageHtmlKey?.(cachePathname) ?? appIsrCacheKey(cachePathname, "html", buildId);
68
- const rscKey = options?.buildAppPageRscKey?.(cachePathname) ?? appIsrCacheKey(cachePathname, "rsc", buildId);
68
+ const htmlKey = isFallbackShell ? options?.buildAppPageFallbackShellKey?.(cachePathname, "html") ?? appIsrFallbackShellKey(cachePathname, "html", buildId) : options?.buildAppPageHtmlKey?.(cachePathname) ?? appIsrCacheKey(cachePathname, "html", buildId);
69
+ const rscKey = isFallbackShell ? options?.buildAppPageFallbackShellKey?.(cachePathname, "rsc") ?? appIsrFallbackShellKey(cachePathname, "rsc", buildId) : options?.buildAppPageRscKey?.(cachePathname) ?? appIsrCacheKey(cachePathname, "rsc", buildId);
69
70
  const revalidateSeconds = typeof route.revalidate === "number" ? route.revalidate : void 0;
70
71
  const expireSeconds = typeof route.expire === "number" ? route.expire : void 0;
71
72
  const staleSeconds = typeof route.stale === "number" && route.stale >= 0 ? route.stale : void 0;