vinext 1.0.0-beta.2 → 1.0.0-beta.4

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 (174) hide show
  1. package/README.md +6 -9
  2. package/dist/build/client-build-config.js +7 -1
  3. package/dist/build/precompress.js +1 -1
  4. package/dist/build/prerender.d.ts +2 -1
  5. package/dist/build/prerender.js +12 -2
  6. package/dist/check.js +4 -0
  7. package/dist/cli-args.d.ts +2 -1
  8. package/dist/cli-args.js +10 -0
  9. package/dist/cli.js +14 -8
  10. package/dist/client/navigation-runtime.d.ts +6 -0
  11. package/dist/client/navigation-runtime.js +1 -1
  12. package/dist/config/config-matchers.js +4 -1
  13. package/dist/config/dotenv.d.ts +3 -4
  14. package/dist/config/dotenv.js +30 -22
  15. package/dist/config/next-config.d.ts +16 -5
  16. package/dist/config/next-config.js +7 -0
  17. package/dist/entries/app-browser-entry.d.ts +4 -10
  18. package/dist/entries/app-browser-entry.js +20 -4
  19. package/dist/entries/app-rsc-entry.js +21 -3
  20. package/dist/entries/app-rsc-manifest.js +19 -0
  21. package/dist/entries/pages-client-entry.js +0 -1
  22. package/dist/entries/pages-server-entry.js +13 -3
  23. package/dist/index.js +122 -49
  24. package/dist/init-cloudflare.d.ts +3 -1
  25. package/dist/init-cloudflare.js +59 -5
  26. package/dist/plugins/client-reference-dedup.js +7 -5
  27. package/dist/plugins/extensionless-dynamic-import.js +23 -18
  28. package/dist/plugins/ignore-dynamic-requests.js +3 -1
  29. package/dist/plugins/import-meta-url.js +17 -6
  30. package/dist/plugins/require-context.js +20 -15
  31. package/dist/plugins/sass.js +3 -4
  32. package/dist/plugins/transform-cache.d.ts +19 -0
  33. package/dist/plugins/transform-cache.js +36 -0
  34. package/dist/routing/app-route-graph.d.ts +14 -6
  35. package/dist/routing/app-route-graph.js +106 -16
  36. package/dist/server/api-handler.d.ts +2 -0
  37. package/dist/server/api-handler.js +7 -6
  38. package/dist/server/app-bfcache-identity.js +28 -2
  39. package/dist/server/app-browser-entry.js +175 -75
  40. package/dist/server/app-browser-navigation-controller.d.ts +23 -21
  41. package/dist/server/app-browser-navigation-controller.js +30 -5
  42. package/dist/server/app-browser-prefetch-response.d.ts +9 -0
  43. package/dist/server/app-browser-prefetch-response.js +32 -0
  44. package/dist/server/app-browser-state.d.ts +8 -3
  45. package/dist/server/app-browser-state.js +9 -3
  46. package/dist/server/app-page-cache-finalizer.js +1 -1
  47. package/dist/server/app-page-cache.d.ts +1 -1
  48. package/dist/server/app-page-cache.js +16 -2
  49. package/dist/server/app-page-dispatch.d.ts +9 -1
  50. package/dist/server/app-page-dispatch.js +37 -8
  51. package/dist/server/app-page-element-builder.d.ts +4 -1
  52. package/dist/server/app-page-element-builder.js +34 -7
  53. package/dist/server/app-page-head.d.ts +3 -3
  54. package/dist/server/app-page-head.js +58 -13
  55. package/dist/server/app-page-http-access-fallback-metadata.d.ts +2 -2
  56. package/dist/server/app-page-http-access-fallback-metadata.js +9 -1
  57. package/dist/server/app-page-probe.d.ts +4 -0
  58. package/dist/server/app-page-probe.js +6 -2
  59. package/dist/server/app-page-render.d.ts +1 -1
  60. package/dist/server/app-page-render.js +13 -3
  61. package/dist/server/app-page-request.d.ts +3 -1
  62. package/dist/server/app-page-request.js +4 -13
  63. package/dist/server/app-page-response.d.ts +2 -0
  64. package/dist/server/app-page-response.js +7 -2
  65. package/dist/server/app-page-route-wiring.d.ts +10 -2
  66. package/dist/server/app-page-route-wiring.js +269 -59
  67. package/dist/server/app-pages-bridge.d.ts +2 -1
  68. package/dist/server/app-pages-bridge.js +3 -1
  69. package/dist/server/app-route-handler-cache.js +4 -0
  70. package/dist/server/app-route-handler-dispatch.js +9 -3
  71. package/dist/server/app-route-handler-response.js +1 -1
  72. package/dist/server/app-route-module-loader.d.ts +6 -0
  73. package/dist/server/app-route-module-loader.js +3 -0
  74. package/dist/server/app-router-entry.js +6 -3
  75. package/dist/server/app-rsc-cache-busting.d.ts +7 -0
  76. package/dist/server/app-rsc-cache-busting.js +6 -0
  77. package/dist/server/app-rsc-errors.js +10 -0
  78. package/dist/server/app-rsc-handler.d.ts +2 -0
  79. package/dist/server/app-rsc-handler.js +30 -13
  80. package/dist/server/app-rsc-render-mode.d.ts +3 -2
  81. package/dist/server/app-rsc-render-mode.js +4 -1
  82. package/dist/server/app-rsc-request-normalization.js +65 -4
  83. package/dist/server/app-rsc-route-matching.d.ts +9 -0
  84. package/dist/server/app-rsc-route-matching.js +6 -0
  85. package/dist/server/app-server-action-execution.d.ts +1 -1
  86. package/dist/server/app-ssr-entry.js +4 -4
  87. package/dist/server/app-visited-response-cache.d.ts +6 -1
  88. package/dist/server/app-visited-response-cache.js +49 -1
  89. package/dist/server/cache-control.d.ts +2 -2
  90. package/dist/server/cache-control.js +2 -1
  91. package/dist/server/dev-server.d.ts +3 -1
  92. package/dist/server/dev-server.js +204 -348
  93. package/dist/server/edge-api-runtime.d.ts +10 -1
  94. package/dist/server/edge-api-runtime.js +29 -1
  95. package/dist/server/headers.d.ts +4 -2
  96. package/dist/server/headers.js +6 -3
  97. package/dist/server/isr-cache.d.ts +10 -33
  98. package/dist/server/isr-cache.js +33 -43
  99. package/dist/server/isr-decision.d.ts +2 -2
  100. package/dist/server/pages-api-route.d.ts +4 -0
  101. package/dist/server/pages-api-route.js +6 -3
  102. package/dist/server/pages-asset-tags.d.ts +1 -0
  103. package/dist/server/pages-asset-tags.js +6 -4
  104. package/dist/server/pages-dev-hydration.js +0 -1
  105. package/dist/server/pages-document-asset-props.d.ts +26 -0
  106. package/dist/server/pages-document-asset-props.js +69 -0
  107. package/dist/server/pages-get-initial-props.d.ts +1 -1
  108. package/dist/server/pages-get-initial-props.js +1 -4
  109. package/dist/server/pages-i18n.js +64 -16
  110. package/dist/server/pages-node-compat.d.ts +2 -0
  111. package/dist/server/pages-node-compat.js +10 -6
  112. package/dist/server/pages-page-data.d.ts +61 -9
  113. package/dist/server/pages-page-data.js +334 -74
  114. package/dist/server/pages-page-handler.d.ts +6 -1
  115. package/dist/server/pages-page-handler.js +119 -20
  116. package/dist/server/pages-page-response.d.ts +7 -3
  117. package/dist/server/pages-page-response.js +41 -33
  118. package/dist/server/pages-request-pipeline.d.ts +7 -0
  119. package/dist/server/pages-request-pipeline.js +5 -1
  120. package/dist/server/pages-revalidate.d.ts +1 -1
  121. package/dist/server/pages-revalidate.js +54 -8
  122. package/dist/server/pages-router-entry.d.ts +1 -0
  123. package/dist/server/pages-router-entry.js +10 -4
  124. package/dist/server/prerender-manifest.d.ts +1 -0
  125. package/dist/server/prod-server.d.ts +2 -1
  126. package/dist/server/prod-server.js +38 -13
  127. package/dist/server/revalidation-host.d.ts +11 -0
  128. package/dist/server/revalidation-host.js +19 -0
  129. package/dist/server/seed-cache.js +1 -1
  130. package/dist/server/worker-revalidation-context.d.ts +13 -0
  131. package/dist/server/worker-revalidation-context.js +30 -0
  132. package/dist/shims/app-router-scroll-state.d.ts +2 -1
  133. package/dist/shims/app-router-scroll-state.js +7 -2
  134. package/dist/shims/cache-handler.d.ts +1 -1
  135. package/dist/shims/cache-handler.js +10 -7
  136. package/dist/shims/cache-runtime.d.ts +6 -0
  137. package/dist/shims/cache-runtime.js +4 -1
  138. package/dist/shims/cache.js +1 -1
  139. package/dist/shims/document.js +15 -2
  140. package/dist/shims/error.js +1 -1
  141. package/dist/shims/fetch-cache.js +1 -1
  142. package/dist/shims/font-google-base.js +17 -8
  143. package/dist/shims/font-local.js +41 -10
  144. package/dist/shims/hash-scroll.d.ts +1 -1
  145. package/dist/shims/hash-scroll.js +2 -1
  146. package/dist/shims/headers.d.ts +10 -13
  147. package/dist/shims/headers.js +85 -36
  148. package/dist/shims/internal/cookie-serialize.d.ts +10 -9
  149. package/dist/shims/internal/cookie-serialize.js +9 -8
  150. package/dist/shims/internal/interpolate-as.d.ts +15 -1
  151. package/dist/shims/internal/interpolate-as.js +27 -2
  152. package/dist/shims/link.d.ts +2 -0
  153. package/dist/shims/link.js +77 -24
  154. package/dist/shims/metadata.d.ts +18 -34
  155. package/dist/shims/metadata.js +98 -56
  156. package/dist/shims/navigation.d.ts +18 -2
  157. package/dist/shims/navigation.js +70 -21
  158. package/dist/shims/request-context.d.ts +6 -2
  159. package/dist/shims/router.js +18 -7
  160. package/dist/shims/script.js +1 -1
  161. package/dist/shims/server.d.ts +7 -6
  162. package/dist/shims/server.js +156 -57
  163. package/dist/shims/slot.d.ts +2 -1
  164. package/dist/shims/slot.js +6 -2
  165. package/dist/shims/streamed-icons.d.ts +9 -0
  166. package/dist/shims/streamed-icons.js +33 -0
  167. package/dist/shims/unified-request-context.d.ts +21 -2
  168. package/dist/shims/unified-request-context.js +82 -1
  169. package/dist/utils/cache-control-metadata.d.ts +2 -1
  170. package/dist/utils/cache-control-metadata.js +5 -1
  171. package/dist/utils/html-limited-bots.js +1 -1
  172. package/dist/utils/protocol-headers.d.ts +7 -1
  173. package/dist/utils/protocol-headers.js +7 -1
  174. package/package.json +1 -1
@@ -1,4 +1,13 @@
1
1
  //#region src/server/edge-api-runtime.d.ts
2
2
  declare function isEdgeApiRuntime(runtime: string | undefined): boolean;
3
+ type EdgeApiExecutionRuntime = "node" | "worker";
4
+ /**
5
+ * Remove wire-level body headers after Node's fetch implementation has decoded
6
+ * an Edge API response. Next.js applies the same boundary in its edge sandbox.
7
+ *
8
+ * Workers keep the original response untouched: workerd preserves the encoded
9
+ * body/header pair and can stream it directly to the client.
10
+ */
11
+ declare function finalizeEdgeApiResponse(response: Response, runtime: EdgeApiExecutionRuntime): Response;
3
12
  //#endregion
4
- export { isEdgeApiRuntime };
13
+ export { EdgeApiExecutionRuntime, finalizeEdgeApiResponse, isEdgeApiRuntime };
@@ -2,5 +2,33 @@
2
2
  function isEdgeApiRuntime(runtime) {
3
3
  return runtime === "edge" || runtime === "experimental-edge";
4
4
  }
5
+ const NODE_EDGE_RESPONSE_FORBIDDEN_HEADERS = [
6
+ "content-length",
7
+ "content-encoding",
8
+ "transfer-encoding"
9
+ ];
10
+ /**
11
+ * Remove wire-level body headers after Node's fetch implementation has decoded
12
+ * an Edge API response. Next.js applies the same boundary in its edge sandbox.
13
+ *
14
+ * Workers keep the original response untouched: workerd preserves the encoded
15
+ * body/header pair and can stream it directly to the client.
16
+ */
17
+ function finalizeEdgeApiResponse(response, runtime) {
18
+ if (runtime === "worker") return response;
19
+ const headers = new Headers(response.headers);
20
+ let changed = false;
21
+ for (const name of NODE_EDGE_RESPONSE_FORBIDDEN_HEADERS) {
22
+ if (!headers.has(name)) continue;
23
+ headers.delete(name);
24
+ changed = true;
25
+ }
26
+ if (!changed) return response;
27
+ return new Response(response.body, {
28
+ headers,
29
+ status: response.status,
30
+ statusText: response.statusText
31
+ });
32
+ }
5
33
  //#endregion
6
- export { isEdgeApiRuntime };
34
+ export { finalizeEdgeApiResponse, isEdgeApiRuntime };
@@ -1,4 +1,4 @@
1
- import { MIDDLEWARE_HEADER_PREFIX, MIDDLEWARE_SET_COOKIE_HEADER, MIDDLEWARE_SKIP_HEADER, VINEXT_MW_CTX_HEADER, VINEXT_PRERENDER_ROUTE_PARAMS_HEADER, VINEXT_PRERENDER_SECRET_HEADER, VINEXT_PRERENDER_SPECULATIVE_HEADER } from "../utils/protocol-headers.js";
1
+ import { MIDDLEWARE_HEADER_PREFIX, MIDDLEWARE_SET_COOKIE_HEADER, MIDDLEWARE_SKIP_HEADER, VINEXT_MW_CTX_HEADER, VINEXT_PRERENDER_ROUTE_PARAMS_HEADER, VINEXT_PRERENDER_SECRET_HEADER, VINEXT_PRERENDER_SPECULATIVE_HEADER, VINEXT_REVALIDATE_HOST_HEADER } from "../utils/protocol-headers.js";
2
2
 
3
3
  //#region src/server/headers.d.ts
4
4
  /**
@@ -15,6 +15,8 @@ import { MIDDLEWARE_HEADER_PREFIX, MIDDLEWARE_SET_COOKIE_HEADER, MIDDLEWARE_SKIP
15
15
  declare const VINEXT_CACHE_HEADER = "X-Vinext-Cache";
16
16
  /** Next.js public ISR / page cache state indicator. */
17
17
  declare const NEXTJS_CACHE_HEADER = "x-nextjs-cache";
18
+ /** Next.js cache-tag metadata carried by prerendered App Router responses. */
19
+ declare const NEXT_CACHE_TAGS_HEADER = "x-next-cache-tags";
18
20
  /** Static file signal — value is URL-encoded pathname. */
19
21
  declare const VINEXT_STATIC_FILE_HEADER = "x-vinext-static-file";
20
22
  /** Timing metrics: `handlerStart,compileMs,renderMs`. */
@@ -107,4 +109,4 @@ declare const INTERNAL_HEADERS: string[];
107
109
  /** Vinext-only internal headers stripped alongside Next.js protocol internals. */
108
110
  declare const VINEXT_INTERNAL_HEADERS: string[];
109
111
  //#endregion
110
- export { ACTION_FORWARDED_HEADER, ACTION_REDIRECT_HEADER, ACTION_REDIRECT_STATUS_HEADER, ACTION_REDIRECT_TYPE_HEADER, ACTION_REVALIDATED_HEADER, FLIGHT_HEADERS, INTERNAL_HEADERS, MIDDLEWARE_HEADER_PREFIX, MIDDLEWARE_NEXT_HEADER, MIDDLEWARE_REWRITE_HEADER, MIDDLEWARE_SET_COOKIE_HEADER, MIDDLEWARE_SKIP_HEADER, NEXTJS_ACTION_NOT_FOUND_HEADER, NEXTJS_CACHE_HEADER, NEXTJS_DEPLOYMENT_ID_HEADER, NEXT_ACTION_HEADER, NEXT_HTML_REQUEST_ID_HEADER, NEXT_REQUEST_ID_HEADER, NEXT_ROUTER_PREFETCH_HEADER, NEXT_ROUTER_SEGMENT_PREFETCH_HEADER, NEXT_ROUTER_STATE_TREE_HEADER, NEXT_URL_HEADER, RSC_ACTION_HEADER, RSC_HEADER, VINEXT_CACHE_HEADER, VINEXT_CLIENT_REUSE_MANIFEST_HEADER, VINEXT_DYNAMIC_STALE_TIME_HEADER, VINEXT_INTERCEPTION_CONTEXT_HEADER, VINEXT_INTERNAL_HEADERS, VINEXT_MOUNTED_SLOTS_HEADER, VINEXT_MW_CTX_HEADER, VINEXT_PARAMS_HEADER, VINEXT_PRERENDER_CACHE_LIFE_HEADER, VINEXT_PRERENDER_PAGES_STATIC_PATHS_PATH, VINEXT_PRERENDER_ROUTE_PARAMS_HEADER, VINEXT_PRERENDER_SECRET_HEADER, VINEXT_PRERENDER_SPECULATIVE_HEADER, VINEXT_PRERENDER_STATIC_PARAMS_PATH, VINEXT_RENDERED_PATH_AND_SEARCH_HEADER, VINEXT_REVALIDATE_HEADER, VINEXT_RSC_MARKER_HEADER, VINEXT_RSC_REDIRECT_HEADER, VINEXT_RSC_REDIRECT_TYPE_HEADER, VINEXT_RSC_RENDER_MODE_HEADER, VINEXT_STATIC_FILE_HEADER, VINEXT_TIMING_HEADER };
112
+ export { ACTION_FORWARDED_HEADER, ACTION_REDIRECT_HEADER, ACTION_REDIRECT_STATUS_HEADER, ACTION_REDIRECT_TYPE_HEADER, ACTION_REVALIDATED_HEADER, FLIGHT_HEADERS, INTERNAL_HEADERS, MIDDLEWARE_HEADER_PREFIX, MIDDLEWARE_NEXT_HEADER, MIDDLEWARE_REWRITE_HEADER, MIDDLEWARE_SET_COOKIE_HEADER, MIDDLEWARE_SKIP_HEADER, NEXTJS_ACTION_NOT_FOUND_HEADER, NEXTJS_CACHE_HEADER, NEXTJS_DEPLOYMENT_ID_HEADER, NEXT_ACTION_HEADER, NEXT_CACHE_TAGS_HEADER, NEXT_HTML_REQUEST_ID_HEADER, NEXT_REQUEST_ID_HEADER, NEXT_ROUTER_PREFETCH_HEADER, NEXT_ROUTER_SEGMENT_PREFETCH_HEADER, NEXT_ROUTER_STATE_TREE_HEADER, NEXT_URL_HEADER, RSC_ACTION_HEADER, RSC_HEADER, VINEXT_CACHE_HEADER, VINEXT_CLIENT_REUSE_MANIFEST_HEADER, VINEXT_DYNAMIC_STALE_TIME_HEADER, VINEXT_INTERCEPTION_CONTEXT_HEADER, VINEXT_INTERNAL_HEADERS, VINEXT_MOUNTED_SLOTS_HEADER, VINEXT_MW_CTX_HEADER, VINEXT_PARAMS_HEADER, VINEXT_PRERENDER_CACHE_LIFE_HEADER, VINEXT_PRERENDER_PAGES_STATIC_PATHS_PATH, VINEXT_PRERENDER_ROUTE_PARAMS_HEADER, VINEXT_PRERENDER_SECRET_HEADER, VINEXT_PRERENDER_SPECULATIVE_HEADER, VINEXT_PRERENDER_STATIC_PARAMS_PATH, VINEXT_RENDERED_PATH_AND_SEARCH_HEADER, VINEXT_REVALIDATE_HEADER, VINEXT_REVALIDATE_HOST_HEADER, VINEXT_RSC_MARKER_HEADER, VINEXT_RSC_REDIRECT_HEADER, VINEXT_RSC_REDIRECT_TYPE_HEADER, VINEXT_RSC_RENDER_MODE_HEADER, VINEXT_STATIC_FILE_HEADER, VINEXT_TIMING_HEADER };
@@ -1,4 +1,4 @@
1
- import { MIDDLEWARE_HEADER_PREFIX, MIDDLEWARE_OVERRIDE_HEADERS, MIDDLEWARE_SET_COOKIE_HEADER, MIDDLEWARE_SKIP_HEADER, VINEXT_MW_CTX_HEADER, VINEXT_PRERENDER_ROUTE_PARAMS_HEADER, VINEXT_PRERENDER_SECRET_HEADER, VINEXT_PRERENDER_SPECULATIVE_HEADER } from "../utils/protocol-headers.js";
1
+ import { MIDDLEWARE_HEADER_PREFIX, MIDDLEWARE_OVERRIDE_HEADERS, MIDDLEWARE_SET_COOKIE_HEADER, MIDDLEWARE_SKIP_HEADER, VINEXT_MW_CTX_HEADER, VINEXT_PRERENDER_ROUTE_PARAMS_HEADER, VINEXT_PRERENDER_SECRET_HEADER, VINEXT_PRERENDER_SPECULATIVE_HEADER, VINEXT_REVALIDATE_HOST_HEADER } from "../utils/protocol-headers.js";
2
2
  //#region src/server/headers.ts
3
3
  /**
4
4
  * Internal HTTP header name constants used throughout vinext.
@@ -14,6 +14,8 @@ import { MIDDLEWARE_HEADER_PREFIX, MIDDLEWARE_OVERRIDE_HEADERS, MIDDLEWARE_SET_C
14
14
  const VINEXT_CACHE_HEADER = "X-Vinext-Cache";
15
15
  /** Next.js public ISR / page cache state indicator. */
16
16
  const NEXTJS_CACHE_HEADER = "x-nextjs-cache";
17
+ /** Next.js cache-tag metadata carried by prerendered App Router responses. */
18
+ const NEXT_CACHE_TAGS_HEADER = "x-next-cache-tags";
17
19
  /** Static file signal — value is URL-encoded pathname. */
18
20
  const VINEXT_STATIC_FILE_HEADER = "x-vinext-static-file";
19
21
  /** Timing metrics: `handlerStart,compileMs,renderMs`. */
@@ -127,7 +129,8 @@ const INTERNAL_HEADERS = [
127
129
  const VINEXT_INTERNAL_HEADERS = [
128
130
  VINEXT_PRERENDER_ROUTE_PARAMS_HEADER,
129
131
  VINEXT_PRERENDER_SPECULATIVE_HEADER,
130
- VINEXT_PRERENDER_CACHE_LIFE_HEADER
132
+ VINEXT_PRERENDER_CACHE_LIFE_HEADER,
133
+ VINEXT_REVALIDATE_HOST_HEADER
131
134
  ];
132
135
  //#endregion
133
- export { ACTION_FORWARDED_HEADER, ACTION_REDIRECT_HEADER, ACTION_REDIRECT_STATUS_HEADER, ACTION_REDIRECT_TYPE_HEADER, ACTION_REVALIDATED_HEADER, FLIGHT_HEADERS, INTERNAL_HEADERS, MIDDLEWARE_HEADER_PREFIX, MIDDLEWARE_NEXT_HEADER, MIDDLEWARE_REWRITE_HEADER, MIDDLEWARE_SET_COOKIE_HEADER, MIDDLEWARE_SKIP_HEADER, NEXTJS_ACTION_NOT_FOUND_HEADER, NEXTJS_CACHE_HEADER, NEXTJS_DEPLOYMENT_ID_HEADER, NEXT_ACTION_HEADER, NEXT_HTML_REQUEST_ID_HEADER, NEXT_REQUEST_ID_HEADER, NEXT_ROUTER_PREFETCH_HEADER, NEXT_ROUTER_SEGMENT_PREFETCH_HEADER, NEXT_ROUTER_STATE_TREE_HEADER, NEXT_URL_HEADER, RSC_ACTION_HEADER, RSC_HEADER, VINEXT_CACHE_HEADER, VINEXT_CLIENT_REUSE_MANIFEST_HEADER, VINEXT_DYNAMIC_STALE_TIME_HEADER, VINEXT_INTERCEPTION_CONTEXT_HEADER, VINEXT_INTERNAL_HEADERS, VINEXT_MOUNTED_SLOTS_HEADER, VINEXT_MW_CTX_HEADER, VINEXT_PARAMS_HEADER, VINEXT_PRERENDER_CACHE_LIFE_HEADER, VINEXT_PRERENDER_PAGES_STATIC_PATHS_PATH, VINEXT_PRERENDER_ROUTE_PARAMS_HEADER, VINEXT_PRERENDER_SECRET_HEADER, VINEXT_PRERENDER_SPECULATIVE_HEADER, VINEXT_PRERENDER_STATIC_PARAMS_PATH, VINEXT_RENDERED_PATH_AND_SEARCH_HEADER, VINEXT_REVALIDATE_HEADER, VINEXT_RSC_MARKER_HEADER, VINEXT_RSC_REDIRECT_HEADER, VINEXT_RSC_REDIRECT_TYPE_HEADER, VINEXT_RSC_RENDER_MODE_HEADER, VINEXT_STATIC_FILE_HEADER, VINEXT_TIMING_HEADER };
136
+ export { ACTION_FORWARDED_HEADER, ACTION_REDIRECT_HEADER, ACTION_REDIRECT_STATUS_HEADER, ACTION_REDIRECT_TYPE_HEADER, ACTION_REVALIDATED_HEADER, FLIGHT_HEADERS, INTERNAL_HEADERS, MIDDLEWARE_HEADER_PREFIX, MIDDLEWARE_NEXT_HEADER, MIDDLEWARE_REWRITE_HEADER, MIDDLEWARE_SET_COOKIE_HEADER, MIDDLEWARE_SKIP_HEADER, NEXTJS_ACTION_NOT_FOUND_HEADER, NEXTJS_CACHE_HEADER, NEXTJS_DEPLOYMENT_ID_HEADER, NEXT_ACTION_HEADER, NEXT_CACHE_TAGS_HEADER, NEXT_HTML_REQUEST_ID_HEADER, NEXT_REQUEST_ID_HEADER, NEXT_ROUTER_PREFETCH_HEADER, NEXT_ROUTER_SEGMENT_PREFETCH_HEADER, NEXT_ROUTER_STATE_TREE_HEADER, NEXT_URL_HEADER, RSC_ACTION_HEADER, RSC_HEADER, VINEXT_CACHE_HEADER, VINEXT_CLIENT_REUSE_MANIFEST_HEADER, VINEXT_DYNAMIC_STALE_TIME_HEADER, VINEXT_INTERCEPTION_CONTEXT_HEADER, VINEXT_INTERNAL_HEADERS, VINEXT_MOUNTED_SLOTS_HEADER, VINEXT_MW_CTX_HEADER, VINEXT_PARAMS_HEADER, VINEXT_PRERENDER_CACHE_LIFE_HEADER, VINEXT_PRERENDER_PAGES_STATIC_PATHS_PATH, VINEXT_PRERENDER_ROUTE_PARAMS_HEADER, VINEXT_PRERENDER_SECRET_HEADER, VINEXT_PRERENDER_SPECULATIVE_HEADER, VINEXT_PRERENDER_STATIC_PARAMS_PATH, VINEXT_RENDERED_PATH_AND_SEARCH_HEADER, VINEXT_REVALIDATE_HEADER, VINEXT_REVALIDATE_HOST_HEADER, VINEXT_RSC_MARKER_HEADER, VINEXT_RSC_REDIRECT_HEADER, VINEXT_RSC_REDIRECT_TYPE_HEADER, VINEXT_RSC_RENDER_MODE_HEADER, VINEXT_STATIC_FILE_HEADER, VINEXT_TIMING_HEADER };
@@ -1,38 +1,11 @@
1
1
  import { OnRequestErrorContext } from "./instrumentation.js";
2
2
  import { RenderObservation } from "./cache-proof.js";
3
3
  import { AppRscRenderMode } from "./app-rsc-render-mode.js";
4
+ import { PRERENDER_REVALIDATE_HEADER, PRERENDER_REVALIDATE_ONLY_GENERATED_HEADER } from "../utils/protocol-headers.js";
4
5
  import { normalizeMountedSlotsHeader } from "./app-mounted-slots-header.js";
5
6
  import { CacheHandlerValue, CachedAppPageValue, CachedPagesValue, IncrementalCacheValue } from "../shims/cache-handler.js";
6
7
 
7
8
  //#region src/server/isr-cache.d.ts
8
- /**
9
- * Header set on the internal request that `res.revalidate()` issues to
10
- * trigger on-demand ISR regeneration of a Pages Router route. Mirrors Next.js's
11
- * `PRERENDER_REVALIDATE_HEADER` (`x-prerender-revalidate`) — see
12
- * `.nextjs-ref/packages/next/src/lib/constants.ts`.
13
- *
14
- * SECURITY: in Next.js this header is NOT a presence flag — it carries the
15
- * secret `previewModeId`, and `checkIsOnDemandRevalidate`
16
- * (`.nextjs-ref/packages/next/src/server/api-utils/index.ts`) only treats a
17
- * request as on-demand revalidation when the value *equals* that secret. If we
18
- * gated on presence alone, any external client could send
19
- * `x-prerender-revalidate: <anything>` to force synchronous regeneration of any
20
- * ISR page, bypassing the fresh/stale cache short-circuits — a
21
- * cache-stampede/DoS vector. We therefore validate the value against
22
- * {@link getRevalidateSecret} (a build-time secret shared across all Workers
23
- * isolates) with a constant-time comparison, and only the matching value (sent
24
- * by our own `res.revalidate()`) is honored.
25
- */
26
- declare const PRERENDER_REVALIDATE_HEADER = "x-prerender-revalidate";
27
- /**
28
- * Companion header to {@link PRERENDER_REVALIDATE_HEADER}. When set,
29
- * `res.revalidate(path, { unstable_onlyGenerated: true })` only revalidates the
30
- * path if it was already generated, and a 404 response counts as a successful
31
- * no-op. Mirrors Next.js's `PRERENDER_REVALIDATE_ONLY_GENERATED_HEADER`
32
- * (`x-prerender-revalidate-if-generated`) — see
33
- * `.nextjs-ref/packages/next/src/lib/constants.ts`.
34
- */
35
- declare const PRERENDER_REVALIDATE_ONLY_GENERATED_HEADER = "x-prerender-revalidate-if-generated";
36
9
  declare function getRevalidateSecret(): string;
37
10
  declare function isRevalidateSecret(value: string | null | undefined): boolean;
38
11
  /**
@@ -44,20 +17,22 @@ declare function isRevalidateSecret(value: string | null | undefined): boolean;
44
17
  declare function isOnDemandRevalidateRequest(headerValue: string | string[] | null | undefined): boolean;
45
18
  type ISRCacheEntry = {
46
19
  value: CacheHandlerValue;
47
- isStale: boolean;
20
+ isStale: boolean; /** The entry crossed its hard expire boundary and must not be served. */
21
+ isExpired?: boolean;
48
22
  };
49
23
  /**
50
24
  * Get a cache entry with staleness information.
51
25
  *
52
26
  * Returns { value, isStale: false } for fresh entries,
53
- * { value, isStale: true } for expired-but-usable entries,
54
- * or null for cache misses.
27
+ * { value, isStale: true } for stale-but-usable entries,
28
+ * { value, isStale: true, isExpired: true } for entries that must be retained
29
+ * as regeneration input but not served, or null for cache misses.
55
30
  */
56
31
  declare function isrGet(key: string): Promise<ISRCacheEntry | null>;
57
32
  /**
58
33
  * Store a value in the ISR cache with a revalidation period.
59
34
  */
60
- declare function isrSet(key: string, data: IncrementalCacheValue, revalidateSeconds: number, tags?: string[], expireSeconds?: number): Promise<void>;
35
+ declare function isrSet(key: string, data: IncrementalCacheValue | null, revalidateSeconds: number | false, tags?: string[], expireSeconds?: number): Promise<void>;
61
36
  declare function isrSetPrerenderedAppPage(key: string, data: CachedAppPageValue, metadata: {
62
37
  expireSeconds?: number;
63
38
  revalidateSeconds?: number;
@@ -70,6 +45,8 @@ declare function isrSetPrerenderedAppPage(key: string, data: CachedAppPageValue,
70
45
  */
71
46
  tags?: string[];
72
47
  }): Promise<void>;
48
+ /** Coalesce same-key synchronous on-demand revalidations. */
49
+ declare function coalesceOnDemandRevalidation<T>(key: string, renderFn: () => Promise<T>): Promise<T>;
73
50
  /**
74
51
  * Trigger a background regeneration for a cache key.
75
52
  *
@@ -133,4 +110,4 @@ declare function setRevalidateDuration(key: string, seconds: number): void;
133
110
  */
134
111
  declare function getRevalidateDuration(key: string): number | undefined;
135
112
  //#endregion
136
- export { ISRCacheEntry, PRERENDER_REVALIDATE_HEADER, PRERENDER_REVALIDATE_ONLY_GENERATED_HEADER, appIsrCacheKey, appIsrHtmlKey, appIsrRouteKey, appIsrRscKey, buildAppPageCacheValue, buildPagesCacheValue, getRevalidateDuration, getRevalidateSecret, isOnDemandRevalidateRequest, isRevalidateSecret, isrCacheKey, isrGet, isrSet, isrSetPrerenderedAppPage, normalizeMountedSlotsHeader, setRevalidateDuration, triggerBackgroundRegeneration };
113
+ export { ISRCacheEntry, PRERENDER_REVALIDATE_HEADER, PRERENDER_REVALIDATE_ONLY_GENERATED_HEADER, appIsrCacheKey, appIsrHtmlKey, appIsrRouteKey, appIsrRscKey, buildAppPageCacheValue, buildPagesCacheValue, coalesceOnDemandRevalidation, getRevalidateDuration, getRevalidateSecret, isOnDemandRevalidateRequest, isRevalidateSecret, isrCacheKey, isrGet, isrSet, isrSetPrerenderedAppPage, normalizeMountedSlotsHeader, setRevalidateDuration, triggerBackgroundRegeneration };
@@ -1,4 +1,5 @@
1
1
  import { getRequestExecutionContext } from "../shims/request-context.js";
2
+ import { PRERENDER_REVALIDATE_HEADER, PRERENDER_REVALIDATE_ONLY_GENERATED_HEADER } from "../utils/protocol-headers.js";
2
3
  import { reportRequestError } from "./instrumentation.js";
3
4
  import { getCdnCacheAdapter } from "../shims/cdn-cache.js";
4
5
  import { fnv1a64 } from "../utils/hash.js";
@@ -7,34 +8,6 @@ import { APP_RSC_RENDER_MODE_NAVIGATION, getRscRenderModeCacheVariant } from "./
7
8
  import { normalizeAppPageInterceptionProofPathname } from "./app-page-render-identity.js";
8
9
  //#region src/server/isr-cache.ts
9
10
  /**
10
- * Header set on the internal request that `res.revalidate()` issues to
11
- * trigger on-demand ISR regeneration of a Pages Router route. Mirrors Next.js's
12
- * `PRERENDER_REVALIDATE_HEADER` (`x-prerender-revalidate`) — see
13
- * `.nextjs-ref/packages/next/src/lib/constants.ts`.
14
- *
15
- * SECURITY: in Next.js this header is NOT a presence flag — it carries the
16
- * secret `previewModeId`, and `checkIsOnDemandRevalidate`
17
- * (`.nextjs-ref/packages/next/src/server/api-utils/index.ts`) only treats a
18
- * request as on-demand revalidation when the value *equals* that secret. If we
19
- * gated on presence alone, any external client could send
20
- * `x-prerender-revalidate: <anything>` to force synchronous regeneration of any
21
- * ISR page, bypassing the fresh/stale cache short-circuits — a
22
- * cache-stampede/DoS vector. We therefore validate the value against
23
- * {@link getRevalidateSecret} (a build-time secret shared across all Workers
24
- * isolates) with a constant-time comparison, and only the matching value (sent
25
- * by our own `res.revalidate()`) is honored.
26
- */
27
- const PRERENDER_REVALIDATE_HEADER = "x-prerender-revalidate";
28
- /**
29
- * Companion header to {@link PRERENDER_REVALIDATE_HEADER}. When set,
30
- * `res.revalidate(path, { unstable_onlyGenerated: true })` only revalidates the
31
- * path if it was already generated, and a 404 response counts as a successful
32
- * no-op. Mirrors Next.js's `PRERENDER_REVALIDATE_ONLY_GENERATED_HEADER`
33
- * (`x-prerender-revalidate-if-generated`) — see
34
- * `.nextjs-ref/packages/next/src/lib/constants.ts`.
35
- */
36
- const PRERENDER_REVALIDATE_ONLY_GENERATED_HEADER = "x-prerender-revalidate-if-generated";
37
- /**
38
11
  * Build-time secret that authenticates on-demand revalidation requests, the
39
12
  * vinext analog of Next.js's prerender-manifest `previewModeId`.
40
13
  *
@@ -54,19 +27,22 @@ const PRERENDER_REVALIDATE_ONLY_GENERATED_HEADER = "x-prerender-revalidate-if-ge
54
27
  *
55
28
  * When the build-time define is absent — dev mode, and any path that doesn't
56
29
  * run through `vinext build` — we fall back to a lazily-generated random secret.
57
- * Those paths are single-process, so a module-scoped value is shared by sender
58
- * and receiver there no regression.
30
+ * Those paths are single-process, but Vite can evaluate this module separately
31
+ * in its RSC and SSR module graphs. Store the fallback on `globalThis` under a
32
+ * registry symbol so every module copy in the process reads the same value.
59
33
  */
60
- let devRevalidateSecret;
34
+ const _DEV_REVALIDATE_SECRET_KEY = Symbol.for("vinext.isrCache.devRevalidateSecret");
61
35
  function getRevalidateSecret() {
62
36
  const baked = process.env.__VINEXT_REVALIDATE_SECRET;
63
37
  if (baked) return baked;
64
- if (devRevalidateSecret === void 0) {
65
- const bytes = /* @__PURE__ */ new Uint8Array(32);
66
- crypto.getRandomValues(bytes);
67
- devRevalidateSecret = Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
68
- }
69
- return devRevalidateSecret;
38
+ const globals = globalThis;
39
+ const existing = globals[_DEV_REVALIDATE_SECRET_KEY];
40
+ if (typeof existing === "string") return existing;
41
+ const bytes = /* @__PURE__ */ new Uint8Array(32);
42
+ crypto.getRandomValues(bytes);
43
+ const secret = Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
44
+ globals[_DEV_REVALIDATE_SECRET_KEY] = secret;
45
+ return secret;
70
46
  }
71
47
  /**
72
48
  * Constant-time string equality. Avoids leaking secret length / prefix via
@@ -98,16 +74,18 @@ function isOnDemandRevalidateRequest(headerValue) {
98
74
  * Get a cache entry with staleness information.
99
75
  *
100
76
  * Returns { value, isStale: false } for fresh entries,
101
- * { value, isStale: true } for expired-but-usable entries,
102
- * or null for cache misses.
77
+ * { value, isStale: true } for stale-but-usable entries,
78
+ * { value, isStale: true, isExpired: true } for entries that must be retained
79
+ * as regeneration input but not served, or null for cache misses.
103
80
  */
104
81
  async function isrGet(key) {
105
82
  const result = await getCdnCacheAdapter().get(key);
106
- if (!result || !result.value) return null;
107
- if (result.cacheState === "expired") return null;
83
+ if (!result) return null;
84
+ const isExpired = result.cacheState === "expired";
108
85
  return {
109
86
  value: result,
110
- isStale: result.cacheState === "stale"
87
+ isStale: isExpired || result.cacheState === "stale",
88
+ ...isExpired ? { isExpired: true } : {}
111
89
  };
112
90
  }
113
91
  /**
@@ -142,6 +120,18 @@ async function isrSetPrerenderedAppPage(key, data, metadata) {
142
120
  const _PENDING_REGEN_KEY = Symbol.for("vinext.isrCache.pendingRegenerations");
143
121
  const _g = globalThis;
144
122
  const pendingRegenerations = _g[_PENDING_REGEN_KEY] ??= /* @__PURE__ */ new Map();
123
+ const _PENDING_ON_DEMAND_REGEN_KEY = Symbol.for("vinext.isrCache.pendingOnDemandRegenerations");
124
+ const pendingOnDemandRegenerations = _g[_PENDING_ON_DEMAND_REGEN_KEY] ??= /* @__PURE__ */ new Map();
125
+ /** Coalesce same-key synchronous on-demand revalidations. */
126
+ function coalesceOnDemandRevalidation(key, renderFn) {
127
+ const pending = pendingOnDemandRegenerations.get(key);
128
+ if (pending) return pending;
129
+ const promise = Promise.resolve().then(renderFn).finally(() => {
130
+ if (pendingOnDemandRegenerations.get(key) === promise) pendingOnDemandRegenerations.delete(key);
131
+ });
132
+ pendingOnDemandRegenerations.set(key, promise);
133
+ return promise;
134
+ }
145
135
  /**
146
136
  * Trigger a background regeneration for a cache key.
147
137
  *
@@ -283,4 +273,4 @@ function getRevalidateDuration(key) {
283
273
  return revalidateDurations.get(key);
284
274
  }
285
275
  //#endregion
286
- export { PRERENDER_REVALIDATE_HEADER, PRERENDER_REVALIDATE_ONLY_GENERATED_HEADER, appIsrCacheKey, appIsrHtmlKey, appIsrRouteKey, appIsrRscKey, buildAppPageCacheValue, buildPagesCacheValue, getRevalidateDuration, getRevalidateSecret, isOnDemandRevalidateRequest, isRevalidateSecret, isrCacheKey, isrGet, isrSet, isrSetPrerenderedAppPage, normalizeMountedSlotsHeader, setRevalidateDuration, triggerBackgroundRegeneration };
276
+ export { PRERENDER_REVALIDATE_HEADER, PRERENDER_REVALIDATE_ONLY_GENERATED_HEADER, appIsrCacheKey, appIsrHtmlKey, appIsrRouteKey, appIsrRscKey, buildAppPageCacheValue, buildPagesCacheValue, coalesceOnDemandRevalidation, getRevalidateDuration, getRevalidateSecret, isOnDemandRevalidateRequest, isRevalidateSecret, isrCacheKey, isrGet, isrSet, isrSetPrerenderedAppPage, normalizeMountedSlotsHeader, setRevalidateDuration, triggerBackgroundRegeneration };
@@ -33,7 +33,7 @@ type DecideIsrOptions = {
33
33
  * For `"dev"` call sites this is the only source of the revalidate value —
34
34
  * dev never has metadata attached to a cache entry.
35
35
  */
36
- revalidateSeconds: number;
36
+ revalidateSeconds: number | false;
37
37
  /**
38
38
  * The expire ceiling (seconds from epoch) read from the route config.
39
39
  * Absent when the route pre-dates expire metadata support.
@@ -63,7 +63,7 @@ declare function decideIsr(options: DecideIsrOptions): IsrDecision;
63
63
  * therefore never has `cacheControlMeta`. `expireSeconds` here is the route
64
64
  * config ceiling passed directly from the caller (not a per-entry fallback).
65
65
  */
66
- declare function buildMissIsrCacheControl(revalidateSeconds: number, expireSeconds?: number): string;
66
+ declare function buildMissIsrCacheControl(revalidateSeconds: number | false, expireSeconds?: number): string;
67
67
  /**
68
68
  * Build the `Cache-Control` string for a fresh (MISS) app-route response.
69
69
  *
@@ -1,6 +1,7 @@
1
1
  import { NextI18nConfig } from "../config/next-config.js";
2
2
  import { Route } from "../routing/pages-router.js";
3
3
  import { ExecutionContextLike } from "../shims/request-context.js";
4
+ import { EdgeApiExecutionRuntime } from "./edge-api-runtime.js";
4
5
  import { PagesReqResRequest, PagesReqResResponse, PagesRequestQuery } from "./pages-node-compat.js";
5
6
 
6
7
  //#region src/server/pages-api-route.d.ts
@@ -68,15 +69,18 @@ type HandlePagesApiRouteOptions = {
68
69
  * response. Omit on Node.js dev where no Workers lifecycle exists.
69
70
  */
70
71
  ctx?: ExecutionContextLike;
72
+ edgeRuntime?: EdgeApiExecutionRuntime;
71
73
  match: PagesApiRouteMatch | null;
72
74
  reportRequestError?: (error: Error, routePattern: string) => void | Promise<void>;
73
75
  request: Request;
74
76
  url: string;
75
77
  nextConfig?: {
76
78
  basePath?: string;
79
+ allowedRevalidateHeaderKeys?: readonly string[];
77
80
  i18n?: NextI18nConfig | null;
78
81
  trailingSlash?: boolean;
79
82
  };
83
+ trustedRevalidateOrigin?: string;
80
84
  };
81
85
  declare function handlePagesApiRoute(options: HandlePagesApiRouteOptions): Promise<Response>;
82
86
  //#endregion
@@ -7,7 +7,7 @@ import { mergeRouteParamsIntoQuery, parseQueryString, urlQueryToSearchParams } f
7
7
  import { resolveBodyParserConfig } from "./pages-body-parser-config.js";
8
8
  import { PagesBodyParseError } from "./pages-media-type.js";
9
9
  import { createPagesReqRes, parsePagesApiBody } from "./pages-node-compat.js";
10
- import { isEdgeApiRuntime } from "./edge-api-runtime.js";
10
+ import { finalizeEdgeApiResponse, isEdgeApiRuntime } from "./edge-api-runtime.js";
11
11
  //#region src/server/pages-api-route.ts
12
12
  function resolveModuleRuntime(module) {
13
13
  return module.runtime ?? module.config?.runtime;
@@ -42,16 +42,19 @@ async function _handlePagesApiRoute(options) {
42
42
  trailingSlash: options.nextConfig.trailingSlash
43
43
  } } : void 0);
44
44
  const response = await route.module.default(nextRequest);
45
- if (response instanceof Response) return response;
45
+ if (response instanceof Response) return finalizeEdgeApiResponse(response, options.edgeRuntime ?? "worker");
46
46
  throw new Error("Edge API route did not return a Response");
47
47
  }
48
48
  if (!isNodeApiRouteModule(route.module)) return new Response("API route does not export a default function", { status: 500 });
49
49
  const query = buildPagesApiQuery(options.url, params);
50
50
  const bodyParserConfig = resolveBodyParserConfig(route.module.config);
51
+ const body = bodyParserConfig.enabled ? await parsePagesApiBody(options.request, bodyParserConfig.sizeLimit) : void 0;
51
52
  const { req, res, responsePromise } = createPagesReqRes({
52
- body: bodyParserConfig.enabled ? await parsePagesApiBody(options.request, bodyParserConfig.sizeLimit) : void 0,
53
+ allowedRevalidateHeaderKeys: options.nextConfig?.allowedRevalidateHeaderKeys,
54
+ body,
53
55
  query,
54
56
  request: options.request,
57
+ trustedRevalidateOrigin: options.trustedRevalidateOrigin,
55
58
  url: options.url
56
59
  });
57
60
  let resWasPiped = false;
@@ -57,6 +57,7 @@ type CollectAssetTagsOptions = {
57
57
  basePath?: string;
58
58
  assetPrefix?: string;
59
59
  deploymentId?: string;
60
+ crossOrigin?: string;
60
61
  };
61
62
  /**
62
63
  * Build the HTML `<link>` and `<script>` tag string for the SSR response.
@@ -70,6 +70,8 @@ function collectAssetTags(options) {
70
70
  const seen = /* @__PURE__ */ new Set();
71
71
  const nonceAttr = createNonceAttribute(options.scriptNonce);
72
72
  const deferAttr = options.disableOptimizedLoading ? "" : " defer";
73
+ const scriptCrossOriginAttr = options.crossOrigin ? ` crossorigin="${options.crossOrigin}"` : " crossorigin";
74
+ const preloadCrossOriginAttr = options.crossOrigin ? ` crossorigin="${options.crossOrigin}"` : "";
73
75
  const basePath = options.basePath ?? "";
74
76
  const assetPrefix = options.assetPrefix ?? "";
75
77
  const href = (value) => {
@@ -82,8 +84,8 @@ function collectAssetTags(options) {
82
84
  const clientEntry = runtimeAssets.clientEntry;
83
85
  if (clientEntry) {
84
86
  seen.add(clientEntry);
85
- tags.push("<link rel=\"modulepreload\"" + nonceAttr + " href=\"" + href(clientEntry) + "\" />");
86
- tags.push("<script type=\"module\"" + deferAttr + nonceAttr + " src=\"" + href(clientEntry) + "\" crossorigin><\/script>");
87
+ tags.push("<link rel=\"modulepreload\"" + nonceAttr + " href=\"" + href(clientEntry) + "\"" + preloadCrossOriginAttr + " />");
88
+ tags.push("<script type=\"module\"" + deferAttr + nonceAttr + " src=\"" + href(clientEntry) + "\"" + scriptCrossOriginAttr + "><\/script>");
87
89
  }
88
90
  if (m) {
89
91
  const allFiles = [];
@@ -115,8 +117,8 @@ function collectAssetTags(options) {
115
117
  if (tf.endsWith(".css")) tags.push("<link rel=\"stylesheet\"" + nonceAttr + " href=\"" + href(tf) + "\" />");
116
118
  else if (tf.endsWith(".js")) {
117
119
  if (lazySet && lazySet.has(tf)) continue;
118
- tags.push("<link rel=\"modulepreload\"" + nonceAttr + " href=\"" + href(tf) + "\" />");
119
- tags.push("<script type=\"module\"" + deferAttr + nonceAttr + " src=\"" + href(tf) + "\" crossorigin><\/script>");
120
+ tags.push("<link rel=\"modulepreload\"" + nonceAttr + " href=\"" + href(tf) + "\"" + preloadCrossOriginAttr + " />");
121
+ tags.push("<script type=\"module\"" + deferAttr + nonceAttr + " src=\"" + href(tf) + "\"" + scriptCrossOriginAttr + "><\/script>");
120
122
  }
121
123
  }
122
124
  }
@@ -17,7 +17,6 @@ function createPagesDevHydrationScript(options) {
17
17
  element = React.createElement(AppComponent, {
18
18
  ...props,
19
19
  Component: PageComponent,
20
- pageProps: rawPageProps,
21
20
  router: appRouter,
22
21
  });
23
22
  ` : `
@@ -0,0 +1,26 @@
1
+ //#region src/server/pages-document-asset-props.d.ts
2
+ type DocumentAssetProps = {
3
+ headNonce?: string;
4
+ headCrossOrigin?: string;
5
+ scriptNonce?: string;
6
+ scriptCrossOrigin?: string;
7
+ };
8
+ type ApplyDocumentAssetPropsOptions = {
9
+ configuredCrossOrigin?: string;
10
+ protectedAssetMarker?: string;
11
+ scriptOwner?: "head" | "next-script";
12
+ };
13
+ declare function extractDocumentAssetProps(html: string): {
14
+ html: string;
15
+ props: DocumentAssetProps;
16
+ };
17
+ /**
18
+ * Protect asset tags whose attributes already have a more specific owner while
19
+ * Vite runs its HTML transforms. Tags injected by Vite after this marker pass
20
+ * remain unmarked, so Document/config props can still be applied to them.
21
+ */
22
+ declare function markDocumentAssetPropsProtectedTags(html: string, markerAttribute: string): string;
23
+ declare function stripDocumentAssetPropsProtectionMarkers(html: string, markerAttribute: string): string;
24
+ declare function applyDocumentAssetProps(html: string, props: DocumentAssetProps, options?: ApplyDocumentAssetPropsOptions): string;
25
+ //#endregion
26
+ export { DocumentAssetProps, applyDocumentAssetProps, extractDocumentAssetProps, markDocumentAssetPropsProtectedTags, stripDocumentAssetPropsProtectionMarkers };
@@ -0,0 +1,69 @@
1
+ import { escapeHtmlAttr } from "./html.js";
2
+ //#region src/server/pages-document-asset-props.ts
3
+ const HEAD_NONCE_ATTR = "data-vinext-head-nonce";
4
+ const HEAD_CROSS_ORIGIN_ATTR = "data-vinext-head-cross-origin";
5
+ const SCRIPT_NONCE_ATTR = "data-vinext-script-nonce";
6
+ const SCRIPT_CROSS_ORIGIN_ATTR = "data-vinext-script-cross-origin";
7
+ function readAttribute(tag, name) {
8
+ if (!tag) return void 0;
9
+ return tag.match(new RegExp(`\\s${name}="([^"]*)"`))?.[1];
10
+ }
11
+ function removeAttributes(tag, names) {
12
+ return tag.replace(new RegExp(`\\s(?:${names.join("|")})="[^"]*"`, "g"), "");
13
+ }
14
+ function extractDocumentAssetProps(html) {
15
+ const headTag = html.match(/<head\b[^>]*>/i)?.[0];
16
+ const nextScriptTag = html.match(/<span\b[^>]*>(?=<!-- __NEXT_SCRIPTS__ -->)/i)?.[0];
17
+ const props = {
18
+ headNonce: readAttribute(headTag, HEAD_NONCE_ATTR),
19
+ headCrossOrigin: readAttribute(headTag, HEAD_CROSS_ORIGIN_ATTR),
20
+ scriptNonce: readAttribute(nextScriptTag, SCRIPT_NONCE_ATTR),
21
+ scriptCrossOrigin: readAttribute(nextScriptTag, SCRIPT_CROSS_ORIGIN_ATTR)
22
+ };
23
+ return {
24
+ html: html.replace(/<head\b[^>]*>/i, (tag) => removeAttributes(tag, [HEAD_NONCE_ATTR, HEAD_CROSS_ORIGIN_ATTR])).replace(/<span\b[^>]*>(?=<!-- __NEXT_SCRIPTS__ -->)/i, (tag) => removeAttributes(tag, [SCRIPT_NONCE_ATTR, SCRIPT_CROSS_ORIGIN_ATTR])),
25
+ props
26
+ };
27
+ }
28
+ function addAttribute(tag, name, value, replaceExisting = false) {
29
+ if (value === void 0) return tag;
30
+ const attributePattern = new RegExp(`\\s${name}(?:="[^"]*")?`, "i");
31
+ if (attributePattern.test(tag)) return replaceExisting ? tag.replace(attributePattern, ` ${name}="${escapeHtmlAttr(value)}"`) : tag;
32
+ if (!tag.endsWith(">")) return tag;
33
+ const selfClosing = tag.endsWith("/>");
34
+ const closingStart = selfClosing ? tag.length - 2 : tag.length - 1;
35
+ const opening = tag.slice(0, closingStart).trimEnd();
36
+ const closing = selfClosing ? " />" : ">";
37
+ return `${opening} ${name}="${escapeHtmlAttr(value)}"${closing}`;
38
+ }
39
+ function hasAttribute(tag, name) {
40
+ return name !== void 0 && new RegExp(`\\s${name}(?:="[^"]*")?`, "i").test(tag);
41
+ }
42
+ /**
43
+ * Protect asset tags whose attributes already have a more specific owner while
44
+ * Vite runs its HTML transforms. Tags injected by Vite after this marker pass
45
+ * remain unmarked, so Document/config props can still be applied to them.
46
+ */
47
+ function markDocumentAssetPropsProtectedTags(html, markerAttribute) {
48
+ return html.replace(/<script\b[^>]*>/gi, (tag) => addAttribute(tag, markerAttribute, "")).replace(/<link\b[^>]*\brel="(?:preload|modulepreload)"[^>]*>/gi, (tag) => addAttribute(tag, markerAttribute, ""));
49
+ }
50
+ function stripDocumentAssetPropsProtectionMarkers(html, markerAttribute) {
51
+ const stripMarker = (tag) => removeAttributes(tag, [markerAttribute]);
52
+ return html.replace(/<script\b[^>]*>/gi, stripMarker).replace(/<link\b[^>]*\brel="(?:preload|modulepreload)"[^>]*>/gi, stripMarker);
53
+ }
54
+ function applyDocumentAssetProps(html, props, options = {}) {
55
+ const scriptOwner = options.scriptOwner ?? "next-script";
56
+ const scriptNonce = scriptOwner === "head" ? props.headNonce : props.scriptNonce;
57
+ const preloadNonce = props.headNonce;
58
+ const scriptCrossOrigin = (scriptOwner === "head" ? props.headCrossOrigin : props.scriptCrossOrigin) ?? options.configuredCrossOrigin;
59
+ const preloadCrossOrigin = props.headCrossOrigin ?? options.configuredCrossOrigin;
60
+ return html.replace(/<script\b[^>]*>/gi, (tag) => {
61
+ if (hasAttribute(tag, options.protectedAssetMarker)) return tag;
62
+ return addAttribute(addAttribute(tag, "nonce", scriptNonce, true), "crossorigin", scriptCrossOrigin, true);
63
+ }).replace(/<link\b[^>]*\brel="(?:preload|modulepreload)"[^>]*>/gi, (tag) => {
64
+ if (hasAttribute(tag, options.protectedAssetMarker)) return tag;
65
+ return addAttribute(addAttribute(tag, "nonce", preloadNonce, true), "crossorigin", preloadCrossOrigin, true);
66
+ });
67
+ }
68
+ //#endregion
69
+ export { applyDocumentAssetProps, extractDocumentAssetProps, markDocumentAssetPropsProtectedTags, stripDocumentAssetPropsProtectionMarkers };
@@ -43,7 +43,7 @@ type DevAppInitialPropsResult = {
43
43
  kind: "render";
44
44
  pageProps: Record<string, unknown>;
45
45
  renderProps: Record<string, unknown> & {
46
- pageProps: unknown;
46
+ pageProps?: unknown;
47
47
  };
48
48
  };
49
49
  type DevAppInitialPropsContext = {
@@ -92,10 +92,7 @@ async function loadDevAppInitialProps(ctx) {
92
92
  return {
93
93
  kind: "render",
94
94
  pageProps: isPropsObject(initialPageProps) ? initialPageProps : {},
95
- renderProps: isPropsObject(initialProps) ? {
96
- ...initialProps,
97
- pageProps: initialPageProps
98
- } : { pageProps: initialPageProps }
95
+ renderProps: isPropsObject(initialProps) ? initialProps : {}
99
96
  };
100
97
  }
101
98
  //#endregion