vinext 1.1.0 → 1.2.0

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 (234) hide show
  1. package/README.md +21 -9
  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/pages-client-assets-module.d.ts +8 -0
  5. package/dist/build/pages-client-assets-module.js +21 -1
  6. package/dist/build/prerender-paths.js +5 -3
  7. package/dist/build/run-prerender.js +1 -1
  8. package/dist/build/ssr-manifest.d.ts +2 -0
  9. package/dist/build/ssr-manifest.js +6 -2
  10. package/dist/check.js +7 -6
  11. package/dist/cli.js +2 -1
  12. package/dist/client/client-redirect-matcher.d.ts +14 -0
  13. package/dist/client/client-redirect-matcher.js +37 -0
  14. package/dist/client/client-redirects.d.ts +27 -0
  15. package/dist/client/client-redirects.js +28 -0
  16. package/dist/client/client-rewrites.d.ts +2 -1
  17. package/dist/client/client-rewrites.js +1 -1
  18. package/dist/config/config-matchers.d.ts +26 -11
  19. package/dist/config/config-matchers.js +78 -81
  20. package/dist/config/destination-params.d.ts +47 -0
  21. package/dist/config/destination-params.js +116 -0
  22. package/dist/config/next-config.d.ts +7 -0
  23. package/dist/config/next-config.js +3 -0
  24. package/dist/entries/app-rsc-entry.d.ts +4 -0
  25. package/dist/entries/app-rsc-entry.js +20 -6
  26. package/dist/entries/pages-client-entry.js +3 -1
  27. package/dist/entries/pages-server-entry.js +13 -37
  28. package/dist/index.d.ts +3 -1
  29. package/dist/index.js +134 -43
  30. package/dist/init-cloudflare.d.ts +9 -0
  31. package/dist/init-cloudflare.js +107 -31
  32. package/dist/init-platform.d.ts +4 -0
  33. package/dist/init-platform.js +27 -2
  34. package/dist/init.d.ts +1 -0
  35. package/dist/init.js +67 -3
  36. package/dist/plugins/ast-scope.d.ts +7 -0
  37. package/dist/plugins/ast-scope.js +59 -3
  38. package/dist/plugins/cache-flight-codec.d.ts +1 -1
  39. package/dist/plugins/dynamic-preload-metadata.d.ts +1 -1
  40. package/dist/plugins/dynamic-preload-metadata.js +9 -8
  41. package/dist/plugins/ignore-dynamic-requests.d.ts +1 -1
  42. package/dist/plugins/ignore-dynamic-requests.js +3 -4
  43. package/dist/plugins/middleware-server-only.d.ts +5 -4
  44. package/dist/plugins/middleware-server-only.js +8 -5
  45. package/dist/plugins/pages-node-externals.js +1 -1
  46. package/dist/plugins/rsc-scan-glob-imports.d.ts +19 -0
  47. package/dist/plugins/rsc-scan-glob-imports.js +50 -0
  48. package/dist/plugins/strip-server-exports.js +262 -98
  49. package/dist/plugins/transform-result.d.ts +9 -5
  50. package/dist/plugins/transitive-externals.js +1 -1
  51. package/dist/plugins/typeof-window.d.ts +2 -2
  52. package/dist/plugins/use-cache-callable.js +8 -2
  53. package/dist/plugins/worker-server-only-imports.d.ts +20 -0
  54. package/dist/plugins/worker-server-only-imports.js +31 -0
  55. package/dist/plugins/worker-use-server.d.ts +25 -0
  56. package/dist/plugins/worker-use-server.js +129 -0
  57. package/dist/server/api-handler.d.ts +1 -0
  58. package/dist/server/api-handler.js +2 -1
  59. package/dist/server/app-browser-action-result.d.ts +1 -1
  60. package/dist/server/app-browser-action-result.js +9 -2
  61. package/dist/server/app-browser-navigation-controller.js +1 -1
  62. package/dist/server/app-browser-server-action-client.js +1 -1
  63. package/dist/server/app-middleware.d.ts +11 -0
  64. package/dist/server/app-middleware.js +17 -2
  65. package/dist/server/app-page-cache.d.ts +1 -1
  66. package/dist/server/app-page-cache.js +1 -1
  67. package/dist/server/app-page-dispatch.d.ts +3 -1
  68. package/dist/server/app-page-dispatch.js +4 -3
  69. package/dist/server/app-page-ppr-runtime.js +1 -1
  70. package/dist/server/app-page-render-observation.js +1 -1
  71. package/dist/server/app-page-request.js +53 -15
  72. package/dist/server/app-page-route-wiring.js +2 -2
  73. package/dist/server/app-pages-bridge.d.ts +1 -1
  74. package/dist/server/app-pages-bridge.js +2 -3
  75. package/dist/server/app-prerender-endpoints.js +9 -6
  76. package/dist/server/app-request-stage-dispatch.js +6 -2
  77. package/dist/server/app-request-stage-independent-entry.js +25 -5
  78. package/dist/server/app-response-stage-entry.js +10 -2
  79. package/dist/server/app-route-handler-cache.d.ts +1 -0
  80. package/dist/server/app-route-handler-cache.js +1 -0
  81. package/dist/server/app-route-handler-dispatch.d.ts +1 -0
  82. package/dist/server/app-route-handler-dispatch.js +5 -2
  83. package/dist/server/app-route-handler-execution.d.ts +1 -0
  84. package/dist/server/app-route-handler-execution.js +1 -0
  85. package/dist/server/app-route-handler-runtime.d.ts +4 -1
  86. package/dist/server/app-route-handler-runtime.js +45 -24
  87. package/dist/server/app-router-entry.js +24 -5
  88. package/dist/server/app-rsc-cache-busting.js +1 -1
  89. package/dist/server/app-rsc-errors.js +20 -16
  90. package/dist/server/app-rsc-handler.d.ts +7 -0
  91. package/dist/server/app-rsc-handler.js +85 -35
  92. package/dist/server/app-rsc-response-stage.js +4 -2
  93. package/dist/server/app-segment-config.d.ts +2 -1
  94. package/dist/server/app-server-action-execution.d.ts +2 -1
  95. package/dist/server/app-server-action-execution.js +61 -22
  96. package/dist/server/app-ssr-entry.js +2 -1
  97. package/dist/server/app-worker-stages.d.ts +5 -0
  98. package/dist/server/app-worker-stages.js +1 -1
  99. package/dist/server/cacheability-request.js +2 -2
  100. package/dist/server/client-build-files.d.ts +16 -0
  101. package/dist/server/client-build-files.js +36 -0
  102. package/dist/server/client-reuse-manifest.js +1 -1
  103. package/dist/server/dev-server.js +38 -27
  104. package/dist/server/dev-stack-sourcemap.js +18 -23
  105. package/dist/server/document-initial-head.d.ts +0 -11
  106. package/dist/server/document-initial-head.js +1 -1
  107. package/dist/server/forwarded-middleware-context.d.ts +5 -0
  108. package/dist/server/forwarded-middleware-context.js +22 -0
  109. package/dist/server/headers.d.ts +4 -1
  110. package/dist/server/headers.js +4 -1
  111. package/dist/server/http-conditional.d.ts +11 -0
  112. package/dist/server/http-conditional.js +10 -1
  113. package/dist/server/http-error-responses.d.ts +1 -1
  114. package/dist/server/image-optimization.d.ts +38 -0
  115. package/dist/server/image-optimization.js +93 -4
  116. package/dist/server/isr-cache.d.ts +10 -0
  117. package/dist/server/isr-cache.js +41 -6
  118. package/dist/server/metadata-route-response.js +9 -8
  119. package/dist/server/middleware-matcher.d.ts +2 -0
  120. package/dist/server/middleware-matcher.js +23 -2
  121. package/dist/server/middleware-runtime.d.ts +13 -0
  122. package/dist/server/middleware-runtime.js +33 -20
  123. package/dist/server/middleware.d.ts +1 -1
  124. package/dist/server/middleware.js +3 -1
  125. package/dist/server/multi-stage.d.ts +11 -0
  126. package/dist/server/normalize-path.d.ts +0 -13
  127. package/dist/server/normalize-path.js +1 -34
  128. package/dist/server/pages-api-route.d.ts +3 -2
  129. package/dist/server/pages-api-route.js +4 -3
  130. package/dist/server/pages-cache-tag.d.ts +12 -0
  131. package/dist/server/pages-cache-tag.js +19 -0
  132. package/dist/server/pages-client-assets.d.ts +2 -0
  133. package/dist/server/pages-data-route.d.ts +10 -0
  134. package/dist/server/pages-data-route.js +19 -2
  135. package/dist/server/pages-document-asset-props.d.ts +5 -1
  136. package/dist/server/pages-document-asset-props.js +13 -5
  137. package/dist/server/pages-document-splice.d.ts +19 -0
  138. package/dist/server/pages-document-splice.js +55 -0
  139. package/dist/server/pages-i18n.d.ts +12 -1
  140. package/dist/server/pages-i18n.js +18 -4
  141. package/dist/server/pages-node-compat.d.ts +16 -5
  142. package/dist/server/pages-node-compat.js +20 -12
  143. package/dist/server/pages-page-data.d.ts +32 -24
  144. package/dist/server/pages-page-data.js +50 -120
  145. package/dist/server/pages-page-handler.d.ts +0 -6
  146. package/dist/server/pages-page-handler.js +188 -126
  147. package/dist/server/pages-page-response.d.ts +12 -6
  148. package/dist/server/pages-page-response.js +41 -34
  149. package/dist/server/pages-request-pipeline.d.ts +10 -2
  150. package/dist/server/pages-request-pipeline.js +37 -18
  151. package/dist/server/pages-request-stage-entry.d.ts +6 -0
  152. package/dist/server/pages-request-stage-entry.js +71 -12
  153. package/dist/server/pages-response-stage-entry.js +4 -0
  154. package/dist/server/pages-revalidate.js +1 -1
  155. package/dist/server/pages-router-entry.js +4 -3
  156. package/dist/server/prerender-manifest.d.ts +3 -3
  157. package/dist/server/prerender-manifest.js +4 -3
  158. package/dist/server/prerender-work-unit-setup.js +4 -0
  159. package/dist/server/prod-server.d.ts +12 -0
  160. package/dist/server/prod-server.js +199 -69
  161. package/dist/server/proper-error.d.ts +4 -0
  162. package/dist/server/proper-error.js +31 -0
  163. package/dist/server/request-pipeline.d.ts +4 -1
  164. package/dist/server/request-pipeline.js +3 -2
  165. package/dist/server/revalidation-request.d.ts +1 -0
  166. package/dist/server/revalidation-request.js +1 -1
  167. package/dist/server/rsc-stream-hints.js +31 -2
  168. package/dist/server/seed-cache.d.ts +1 -0
  169. package/dist/server/seed-cache.js +5 -4
  170. package/dist/server/server-reference-loader.d.ts +4 -0
  171. package/dist/server/server-reference-loader.js +29 -0
  172. package/dist/server/socket-error-backstop.d.ts +9 -1
  173. package/dist/server/socket-error-backstop.js +46 -17
  174. package/dist/server/static-file-signal.d.ts +15 -4
  175. package/dist/server/static-file-signal.js +46 -12
  176. package/dist/server/worker-image-optimization.d.ts +34 -0
  177. package/dist/server/worker-image-optimization.js +86 -0
  178. package/dist/server/worker-stages.d.ts +1 -1
  179. package/dist/server/worker-stages.js +2 -2
  180. package/dist/server/worker-utils.d.ts +8 -3
  181. package/dist/server/worker-utils.js +12 -7
  182. package/dist/shims/app-router-scroll.js +4 -1
  183. package/dist/shims/app-router-tree-context.d.ts +5 -0
  184. package/dist/shims/app-router-tree-context.js +27 -0
  185. package/dist/shims/cache-callable-runtime.d.ts +4 -2
  186. package/dist/shims/cache-callable-runtime.js +21 -12
  187. package/dist/shims/cache-handler.js +1 -9
  188. package/dist/shims/cache-runtime.d.ts +6 -1
  189. package/dist/shims/cache-runtime.js +2 -1
  190. package/dist/shims/cache.d.ts +2 -2
  191. package/dist/shims/cache.js +40 -13
  192. package/dist/shims/cdn-cache.d.ts +8 -0
  193. package/dist/shims/cdn-cache.js +17 -1
  194. package/dist/shims/disable-smooth-scroll.d.ts +3 -0
  195. package/dist/shims/disable-smooth-scroll.js +36 -0
  196. package/dist/shims/document-placeholders.d.ts +19 -0
  197. package/dist/shims/document-placeholders.js +45 -0
  198. package/dist/shims/document.js +21 -5
  199. package/dist/shims/dynamic-preload-chunks.d.ts +1 -0
  200. package/dist/shims/dynamic-preload-chunks.js +13 -9
  201. package/dist/shims/dynamic.d.ts +1 -1
  202. package/dist/shims/dynamic.js +60 -8
  203. package/dist/shims/fetch-cache-state.d.ts +66 -0
  204. package/dist/shims/fetch-cache-state.js +92 -0
  205. package/dist/shims/fetch-cache.d.ts +3 -67
  206. package/dist/shims/fetch-cache.js +160 -125
  207. package/dist/shims/head.d.ts +10 -6
  208. package/dist/shims/head.js +15 -13
  209. package/dist/shims/image.js +2 -1
  210. package/dist/shims/internal/work-store-scope.d.ts +5 -0
  211. package/dist/shims/internal/work-store-scope.js +24 -0
  212. package/dist/shims/navigation.js +31 -5
  213. package/dist/shims/request-context.d.ts +2 -0
  214. package/dist/shims/request-state-types.d.ts +2 -1
  215. package/dist/shims/router.js +12 -20
  216. package/dist/shims/server.d.ts +16 -0
  217. package/dist/shims/server.js +24 -4
  218. package/dist/shims/unified-request-context.d.ts +1 -1
  219. package/dist/shims/url-utils.d.ts +2 -0
  220. package/dist/shims/url-utils.js +3 -1
  221. package/dist/utils/client-runtime-metadata.js +1 -1
  222. package/dist/utils/hash.d.ts +7 -0
  223. package/dist/utils/hash.js +11 -1
  224. package/dist/utils/package-name.d.ts +6 -0
  225. package/dist/utils/package-name.js +12 -1
  226. package/dist/utils/plugin-rsc-version.d.ts +4 -0
  227. package/dist/utils/plugin-rsc-version.js +15 -0
  228. package/dist/utils/prerender-output-paths.js +15 -4
  229. package/dist/utils/public-file-path.d.ts +10 -2
  230. package/dist/utils/public-file-path.js +13 -9
  231. package/dist/utils/public-routes.js +2 -2
  232. package/dist/utils/react-version.d.ts +3 -0
  233. package/dist/utils/react-version.js +1 -1
  234. package/package.json +7 -3
@@ -53,6 +53,10 @@ async function renderPagesResponse(request, env, platformCtx, props, stagedHeade
53
53
  ...ctx,
54
54
  isPrerenderPathDiscovery: true
55
55
  };
56
+ else if (props.kind === "pages-page" && dispatchOptions.regeneratesStoredEntry === true) ctx = {
57
+ ...ctx,
58
+ isStoredEntryRegeneration: true
59
+ };
56
60
  const render = async (cacheabilityContext) => {
57
61
  if (props.kind === "pages-prerender-discovery") {
58
62
  const readinessResponse = createWorkerPrerenderReadinessResponse(cacheabilityContext, request);
@@ -29,7 +29,7 @@ async function performOnDemandRevalidate(source, urlPath, opts = {}, trustedOrig
29
29
  const res = await fetchRevalidateTarget(target, headers, dispatchRevalidate);
30
30
  if (!((res.headers.get("x-vercel-cache") ?? res.headers.get("x-nextjs-cache"))?.toUpperCase() === "REVALIDATED" || res.status === 200 || res.status === 404 && opts.unstable_onlyGenerated === true)) throw new Error(`Failed to revalidate ${urlPath}: ${res.status}`);
31
31
  const regeneratedTag = res.headers.get(VINEXT_REVALIDATED_CACHE_TAG_HEADER);
32
- if (regeneratedTag) await getCdnCacheAdapter().revalidateTag(regeneratedTag);
32
+ if (regeneratedTag) await getCdnCacheAdapter().revalidateTag(decodeURIComponent(regeneratedTag));
33
33
  }
34
34
  function readSourceHeader(source, key) {
35
35
  if (isWebHeaders(source)) return source.get(key) ?? void 0;
@@ -5,7 +5,7 @@ import { getCdnCacheAdapter } from "../shims/cdn-cache.js";
5
5
  import { validateCdnRequest } from "./cache-control.js";
6
6
  import { createWorkerRevalidationContext } from "./worker-revalidation-context.js";
7
7
  import { createWorkerPrerenderDiscoveryContext, createWorkerPrerenderReadinessResponse } from "./worker-prerender-discovery.js";
8
- import { handleRequestStageLocally, pagesRequestStageBuildId, pagesRequestStagePrerenderSecret } from "./pages-request-stage-entry.js";
8
+ import { handleRequestStageLocally, isPagesClientBuildFileRequest, pagesRequestStageBuildId, pagesRequestStagePrerenderSecret } from "./pages-request-stage-entry.js";
9
9
  import { renderPagesResponse } from "./pages-response-stage-entry.js";
10
10
  import { registerConfiguredCacheAdapters } from "virtual:vinext-cache-adapters";
11
11
  import { __ensureInstrumentation } from "virtual:vinext-pages-request-entry";
@@ -23,8 +23,9 @@ async function handleSingleStageRequest(request, env, platformCtx) {
23
23
  if (readinessResponse.status === 204) await Promise.all([__ensureInstrumentation(), __ensureInstrumentation$1()]);
24
24
  return await validateCdnRequest(request) ?? readinessResponse;
25
25
  }
26
+ const isClientBuildFile = isPagesClientBuildFileRequest(request, env, ctx);
26
27
  let finalize;
27
- if (request.headers.has("X-Vinext-Cacheability-Probe")) {
28
+ if (!isClientBuildFile && request.headers.has("X-Vinext-Cacheability-Probe")) {
28
29
  const cacheability = await import("./cacheability-request.js");
29
30
  const probeContext = cacheability.createWorkerCacheabilityContext(ctx, request, pagesRequestStagePrerenderSecret, adapter.responseVary);
30
31
  if (probeContext !== ctx) {
@@ -39,7 +40,7 @@ async function handleSingleStageRequest(request, env, platformCtx) {
39
40
  }
40
41
  }
41
42
  const requiresCompletedResponseAdmission = adapter.requiresCompletedResponseAdmission === true;
42
- if (!finalize && (__cacheabilityManifest || requiresCompletedResponseAdmission)) {
43
+ if (!finalize && !isClientBuildFile && (__cacheabilityManifest || requiresCompletedResponseAdmission)) {
43
44
  const cacheability = await import("./cacheability-request.js");
44
45
  const admissionContext = cacheability.createWorkerCacheabilityAdmissionContext(ctx, request, __cacheabilityManifest, pagesRequestStageBuildId, requiresCompletedResponseAdmission, adapter.responseVary);
45
46
  if (admissionContext !== ctx) {
@@ -32,7 +32,6 @@ export type PrerenderManifest = {
32
32
  pregeneratedConcretePaths?: Array<[string, string[]]>;
33
33
  };
34
34
  export type PrerenderedPathSelectionOptions = {
35
- includeFallbackShells?: boolean;
36
35
  includeErrorDocuments?: boolean;
37
36
  };
38
37
  export declare function readPrerenderManifest(manifestPath: string): PrerenderManifest | null;
@@ -58,8 +57,9 @@ export declare function buildPregeneratedConcretePathTable(manifest: PrerenderMa
58
57
  * This intentionally includes both App Router and Pages Router entries because
59
58
  * deploy-time cache warmup should exercise the same URLs the prerender phase
60
59
  * proved are statically renderable. PPR fallback-shell placeholder artifacts
61
- * and known error documents are excluded by default so warmup does not request
62
- * synthetic bracket paths or treat a healthy 404 response as a failed warmup.
60
+ * are always excluded, and known error documents by default, so warmup does not
61
+ * request synthetic bracket paths or treat a healthy 404 response as a failed
62
+ * warmup.
63
63
  */
64
64
  export declare function getPrerenderedConcretePaths(manifest: PrerenderManifest, options?: PrerenderedPathSelectionOptions): string[];
65
65
  //#endregion
@@ -65,8 +65,9 @@ function buildPregeneratedConcretePathTable(manifest) {
65
65
  * This intentionally includes both App Router and Pages Router entries because
66
66
  * deploy-time cache warmup should exercise the same URLs the prerender phase
67
67
  * proved are statically renderable. PPR fallback-shell placeholder artifacts
68
- * and known error documents are excluded by default so warmup does not request
69
- * synthetic bracket paths or treat a healthy 404 response as a failed warmup.
68
+ * are always excluded, and known error documents by default, so warmup does not
69
+ * request synthetic bracket paths or treat a healthy 404 response as a failed
70
+ * warmup.
70
71
  */
71
72
  function getPrerenderedConcretePaths(manifest, options) {
72
73
  const routes = manifest.routes;
@@ -76,7 +77,7 @@ function getPrerenderedConcretePaths(manifest, options) {
76
77
  for (const route of routes) {
77
78
  if (route.status !== "rendered" || route.notFound) continue;
78
79
  const pathname = route.path ?? route.route;
79
- if (!options?.includeFallbackShells && isFallbackShellArtifactPath(pathname, route)) continue;
80
+ if (isFallbackShellArtifactPath(pathname, route)) continue;
80
81
  if (!options?.includeErrorDocuments && isErrorDocumentRoute(pathname, route)) continue;
81
82
  if (seen.has(pathname)) continue;
82
83
  seen.add(pathname);
@@ -1,3 +1,4 @@
1
+ import { runInWorkStoreScope } from "../shims/internal/work-store-scope.js";
1
2
  import { workUnitAsyncStorage } from "../shims/internal/work-unit-async-storage.js";
2
3
  import { isRouteCacheabilityProbe } from "../shims/cacheability-classification.js";
3
4
  import { NO_STORE_CACHE_CONTROL } from "./cache-control.js";
@@ -15,6 +16,9 @@ import { NO_STORE_CACHE_CONTROL } from "./cache-control.js";
15
16
  *
16
17
  */
17
18
  function runWithPrerenderWorkUnit(fn, options) {
19
+ return runInWorkStoreScope(() => runWithWorkUnit(fn, options));
20
+ }
21
+ function runWithWorkUnit(fn, options) {
18
22
  if (process.env.VINEXT_PRERENDER === "1" || isRouteCacheabilityProbe()) {
19
23
  if (options?.cacheComponents !== true) return workUnitAsyncStorage.run({ type: "prerender-legacy" }, fn);
20
24
  return runWithPrerenderWorkUnitOwner(fn, options);
@@ -155,5 +155,17 @@ export declare function resolveAppRouterPrerenderSeeder(entryModule: unknown): A
155
155
  * route through; the on-disk path is just `_next/static/...`.
156
156
  */
157
157
  export declare function resolveAppRouterAssetPath(pathname: string, assetPathPrefix: string, assetPrefix: string): string | null;
158
+ /**
159
+ * Create the Pages Router request handler for `vite preview`. Vite's preview
160
+ * middleware (proxy, compression, `base`, static files) runs first, so this
161
+ * handles only the requests Vite doesn't serve and leaves compression to Vite.
162
+ * Resolves to null when the build has no Pages Router server entry.
163
+ */
164
+ export declare function createPagesPreviewHandler(options: {
165
+ clientDir: string;
166
+ serverDir: string;
167
+ host: string;
168
+ port: number;
169
+ }): Promise<((req: IncomingMessage, res: ServerResponse) => void) | null>;
158
170
  //#endregion
159
171
  export { COMPRESS_THRESHOLD, isCompressibleContentType, mergeResponseHeaders, mergeWebResponse, negotiateEncoding, nodeToWebRequest, resolveRequestHost as resolveHost, sendCompressed, sendWebResponse, trustProxy, trustedHosts, tryServeStatic, waitForNodeResponseCompletion };
@@ -8,17 +8,17 @@ import { hasBasePath, stripBasePath } from "../utils/base-path.js";
8
8
  import { VINEXT_PRERENDER_RENDER_ERROR_HEADER, VINEXT_PRERENDER_SPECIAL_ERROR_HEADER } from "./headers.js";
9
9
  import { normalizePath } from "./normalize-path.js";
10
10
  import { notFoundResponse } from "./http-error-responses.js";
11
- import { isOpenRedirectShaped, sendRepeatedSlashRedirect } from "./open-redirect.js";
11
+ import { getRepeatedSlashRedirect, isOpenRedirectShaped, sendRepeatedSlashRedirect } from "./open-redirect.js";
12
12
  import { readStaticFileSignal } from "./static-file-signal.js";
13
13
  import { canonicalizeRequestPathname, filterInternalHeaders } from "./request-pipeline.js";
14
+ import { buildNextDataNotFoundResponse, encodeUrlParserIgnoredCharacters, isNextDataPathname, normalizeNextDataPagePathname, parseNextDataPathname, shouldAddTrailingSlashToPagesDataPath, urlParserCreatesPagesDataPath } from "./pages-data-route.js";
14
15
  import { buildCacheStateHeaders } from "./cache-headers.js";
15
16
  import { parseHttpDate } from "./http-date.js";
16
17
  import { evaluateStaticPreconditions } from "./http-conditional.js";
17
- import { buildNextDataNotFoundResponse, encodeUrlParserIgnoredCharacters, isNextDataPathname, normalizeNextDataPagePathname, parseNextDataPathname, shouldAddTrailingSlashToPagesDataPath, urlParserCreatesPagesDataPath } from "./pages-data-route.js";
18
18
  import { setPagesClientAssets } from "./pages-client-assets.js";
19
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 };