vinext 0.1.2 → 0.1.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 (243) hide show
  1. package/dist/build/client-build-config.d.ts +11 -2
  2. package/dist/build/client-build-config.js +17 -6
  3. package/dist/build/prerender.d.ts +9 -1
  4. package/dist/build/prerender.js +42 -12
  5. package/dist/build/run-prerender.d.ts +10 -2
  6. package/dist/build/run-prerender.js +15 -1
  7. package/dist/client/app-nav-failure-handler.d.ts +8 -0
  8. package/dist/client/app-nav-failure-handler.js +44 -0
  9. package/dist/client/pages-router-link-navigation.d.ts +33 -7
  10. package/dist/client/pages-router-link-navigation.js +32 -2
  11. package/dist/client/vinext-next-data.d.ts +18 -1
  12. package/dist/client/vinext-next-data.js +2 -0
  13. package/dist/client/window-next.d.ts +2 -1
  14. package/dist/client/window-next.js +12 -1
  15. package/dist/cloudflare/src/cache/cdn-adapter.runtime.js +6 -1
  16. package/dist/config/config-matchers.d.ts +11 -1
  17. package/dist/config/config-matchers.js +87 -16
  18. package/dist/config/next-config.d.ts +46 -4
  19. package/dist/config/next-config.js +147 -48
  20. package/dist/config/tsconfig-paths.js +14 -1
  21. package/dist/deploy.d.ts +30 -11
  22. package/dist/deploy.js +200 -112
  23. package/dist/entries/app-browser-entry.d.ts +9 -3
  24. package/dist/entries/app-browser-entry.js +21 -3
  25. package/dist/entries/app-rsc-entry.d.ts +2 -0
  26. package/dist/entries/app-rsc-entry.js +65 -5
  27. package/dist/entries/app-rsc-manifest.js +2 -0
  28. package/dist/entries/app-ssr-entry.js +1 -1
  29. package/dist/entries/pages-client-entry.js +66 -20
  30. package/dist/entries/pages-server-entry.js +47 -31
  31. package/dist/index.js +417 -102
  32. package/dist/plugins/dynamic-preload-metadata.js +2 -4
  33. package/dist/plugins/extensionless-dynamic-import.d.ts +6 -0
  34. package/dist/plugins/extensionless-dynamic-import.js +152 -0
  35. package/dist/plugins/fonts.js +5 -4
  36. package/dist/plugins/optimize-imports.d.ts +2 -1
  37. package/dist/plugins/optimize-imports.js +11 -9
  38. package/dist/plugins/postcss.js +7 -7
  39. package/dist/plugins/strip-server-exports.d.ts +9 -7
  40. package/dist/plugins/strip-server-exports.js +493 -46
  41. package/dist/plugins/typeof-window.d.ts +14 -0
  42. package/dist/plugins/typeof-window.js +150 -0
  43. package/dist/routing/app-route-graph.d.ts +2 -1
  44. package/dist/routing/app-route-graph.js +46 -16
  45. package/dist/routing/file-matcher.d.ts +10 -1
  46. package/dist/routing/file-matcher.js +22 -1
  47. package/dist/routing/pages-router.js +3 -3
  48. package/dist/routing/utils.d.ts +35 -6
  49. package/dist/routing/utils.js +59 -7
  50. package/dist/server/api-handler.d.ts +6 -1
  51. package/dist/server/api-handler.js +21 -15
  52. package/dist/server/app-browser-action-result.d.ts +19 -6
  53. package/dist/server/app-browser-action-result.js +20 -11
  54. package/dist/server/app-browser-entry.js +175 -91
  55. package/dist/server/app-browser-error.d.ts +10 -6
  56. package/dist/server/app-browser-error.js +43 -8
  57. package/dist/server/app-browser-hydration.d.ts +2 -0
  58. package/dist/server/app-browser-hydration.js +1 -0
  59. package/dist/server/app-browser-navigation-controller.d.ts +5 -3
  60. package/dist/server/app-browser-navigation-controller.js +23 -2
  61. package/dist/server/app-browser-server-action-navigation.d.ts +6 -0
  62. package/dist/server/app-browser-server-action-navigation.js +9 -0
  63. package/dist/server/app-browser-state.d.ts +1 -1
  64. package/dist/server/app-browser-state.js +19 -11
  65. package/dist/server/app-browser-stream.js +86 -43
  66. package/dist/server/app-browser-visible-commit.d.ts +1 -1
  67. package/dist/server/app-elements-wire.d.ts +6 -1
  68. package/dist/server/app-elements-wire.js +14 -4
  69. package/dist/server/app-elements.d.ts +2 -2
  70. package/dist/server/app-elements.js +2 -2
  71. package/dist/server/app-fallback-renderer.d.ts +1 -0
  72. package/dist/server/app-fallback-renderer.js +3 -1
  73. package/dist/server/app-optimistic-routing.js +2 -2
  74. package/dist/server/app-page-boundary-render.d.ts +1 -0
  75. package/dist/server/app-page-boundary-render.js +27 -14
  76. package/dist/server/app-page-cache-render.d.ts +53 -0
  77. package/dist/server/app-page-cache-render.js +91 -0
  78. package/dist/server/app-page-cache.d.ts +16 -2
  79. package/dist/server/app-page-cache.js +62 -1
  80. package/dist/server/app-page-dispatch.d.ts +26 -0
  81. package/dist/server/app-page-dispatch.js +149 -92
  82. package/dist/server/app-page-element-builder.d.ts +1 -0
  83. package/dist/server/app-page-element-builder.js +5 -2
  84. package/dist/server/app-page-execution.d.ts +6 -1
  85. package/dist/server/app-page-execution.js +21 -1
  86. package/dist/server/app-page-probe.d.ts +1 -0
  87. package/dist/server/app-page-probe.js +4 -0
  88. package/dist/server/app-page-render-observation.d.ts +3 -1
  89. package/dist/server/app-page-render-observation.js +17 -1
  90. package/dist/server/app-page-render.d.ts +12 -1
  91. package/dist/server/app-page-render.js +42 -4
  92. package/dist/server/app-page-request.d.ts +2 -0
  93. package/dist/server/app-page-request.js +2 -1
  94. package/dist/server/app-page-route-wiring.d.ts +3 -1
  95. package/dist/server/app-page-route-wiring.js +14 -5
  96. package/dist/server/app-page-stream.d.ts +15 -3
  97. package/dist/server/app-page-stream.js +11 -5
  98. package/dist/server/app-pages-bridge.d.ts +23 -1
  99. package/dist/server/app-pages-bridge.js +26 -17
  100. package/dist/server/app-ppr-fallback-shell-render.d.ts +17 -0
  101. package/dist/server/app-ppr-fallback-shell-render.js +26 -0
  102. package/dist/server/app-ppr-fallback-shell.d.ts +13 -1
  103. package/dist/server/app-ppr-fallback-shell.js +8 -1
  104. package/dist/server/app-route-handler-dispatch.js +9 -2
  105. package/dist/server/app-route-handler-policy.d.ts +1 -0
  106. package/dist/server/app-router-entry.js +5 -0
  107. package/dist/server/app-rsc-cache-busting.js +2 -0
  108. package/dist/server/app-rsc-handler.d.ts +28 -0
  109. package/dist/server/app-rsc-handler.js +195 -59
  110. package/dist/server/app-rsc-route-matching.d.ts +3 -0
  111. package/dist/server/app-rsc-route-matching.js +8 -2
  112. package/dist/server/app-segment-config.d.ts +9 -1
  113. package/dist/server/app-segment-config.js +12 -3
  114. package/dist/server/app-server-action-execution.d.ts +1 -0
  115. package/dist/server/app-server-action-execution.js +47 -15
  116. package/dist/server/app-ssr-entry.d.ts +2 -0
  117. package/dist/server/app-ssr-entry.js +84 -39
  118. package/dist/server/before-interactive-head.d.ts +17 -0
  119. package/dist/server/before-interactive-head.js +35 -0
  120. package/dist/server/cache-control.js +4 -0
  121. package/dist/server/csp.js +1 -4
  122. package/dist/server/dev-server.d.ts +2 -2
  123. package/dist/server/dev-server.js +321 -83
  124. package/dist/server/hybrid-route-priority.d.ts +22 -0
  125. package/dist/server/hybrid-route-priority.js +33 -0
  126. package/dist/server/image-optimization.d.ts +18 -9
  127. package/dist/server/image-optimization.js +37 -23
  128. package/dist/server/implicit-tags.d.ts +2 -1
  129. package/dist/server/implicit-tags.js +4 -1
  130. package/dist/server/middleware-matcher.js +12 -3
  131. package/dist/server/middleware-runtime.d.ts +3 -4
  132. package/dist/server/middleware-runtime.js +2 -0
  133. package/dist/server/navigation-planner.d.ts +135 -41
  134. package/dist/server/navigation-planner.js +138 -0
  135. package/dist/server/navigation-trace.d.ts +9 -1
  136. package/dist/server/navigation-trace.js +9 -1
  137. package/dist/server/operation-token.d.ts +40 -0
  138. package/dist/server/operation-token.js +85 -0
  139. package/dist/server/pages-api-route.d.ts +6 -0
  140. package/dist/server/pages-api-route.js +13 -2
  141. package/dist/server/pages-asset-tags.d.ts +2 -1
  142. package/dist/server/pages-asset-tags.js +6 -2
  143. package/dist/server/pages-data-route.d.ts +9 -2
  144. package/dist/server/pages-data-route.js +18 -6
  145. package/dist/server/pages-dev-module-url.d.ts +4 -0
  146. package/dist/server/pages-dev-module-url.js +15 -0
  147. package/dist/server/pages-document-initial-props.d.ts +4 -15
  148. package/dist/server/pages-document-initial-props.js +27 -56
  149. package/dist/server/pages-get-initial-props.d.ts +54 -4
  150. package/dist/server/pages-get-initial-props.js +43 -1
  151. package/dist/server/pages-i18n.js +2 -2
  152. package/dist/server/pages-node-compat.js +2 -2
  153. package/dist/server/pages-page-data.d.ts +11 -2
  154. package/dist/server/pages-page-data.js +207 -34
  155. package/dist/server/pages-page-handler.d.ts +4 -2
  156. package/dist/server/pages-page-handler.js +62 -23
  157. package/dist/server/pages-page-response.d.ts +4 -1
  158. package/dist/server/pages-page-response.js +11 -8
  159. package/dist/server/pages-readiness.js +1 -1
  160. package/dist/server/pages-request-pipeline.d.ts +8 -7
  161. package/dist/server/pages-request-pipeline.js +126 -47
  162. package/dist/server/pregenerated-concrete-paths.d.ts +1 -17
  163. package/dist/server/pregenerated-concrete-paths.js +2 -19
  164. package/dist/server/prerender-manifest.d.ts +33 -0
  165. package/dist/server/prerender-manifest.js +54 -0
  166. package/dist/server/prerender-route-params.d.ts +1 -2
  167. package/dist/server/prod-server.d.ts +3 -1
  168. package/dist/server/prod-server.js +50 -13
  169. package/dist/server/request-pipeline.d.ts +3 -15
  170. package/dist/server/request-pipeline.js +58 -47
  171. package/dist/server/rsc-stream-hints.d.ts +5 -1
  172. package/dist/server/rsc-stream-hints.js +6 -1
  173. package/dist/server/seed-cache.js +10 -18
  174. package/dist/server/static-file-cache.js +16 -4
  175. package/dist/shims/app-router-scroll-state.d.ts +3 -1
  176. package/dist/shims/app-router-scroll-state.js +14 -2
  177. package/dist/shims/app-router-scroll.d.ts +3 -0
  178. package/dist/shims/app-router-scroll.js +28 -18
  179. package/dist/shims/before-interactive-context.d.ts +14 -3
  180. package/dist/shims/cache-runtime.js +3 -2
  181. package/dist/shims/cache.d.ts +1 -0
  182. package/dist/shims/cache.js +1 -1
  183. package/dist/shims/cdn-cache.d.ts +5 -5
  184. package/dist/shims/document.d.ts +15 -20
  185. package/dist/shims/document.js +5 -8
  186. package/dist/shims/dynamic-preload-chunks.js +6 -4
  187. package/dist/shims/error-boundary.d.ts +2 -0
  188. package/dist/shims/error-boundary.js +7 -0
  189. package/dist/shims/error.js +3 -2
  190. package/dist/shims/error.react-server.d.ts +9 -0
  191. package/dist/shims/error.react-server.js +6 -0
  192. package/dist/shims/fetch-cache.d.ts +3 -1
  193. package/dist/shims/fetch-cache.js +45 -20
  194. package/dist/shims/hash-scroll.js +6 -1
  195. package/dist/shims/headers.js +29 -4
  196. package/dist/shims/image.js +9 -2
  197. package/dist/shims/internal/als-registry.js +28 -1
  198. package/dist/shims/internal/app-route-detection.js +8 -17
  199. package/dist/shims/internal/hybrid-client-route-owner.d.ts +31 -0
  200. package/dist/shims/internal/hybrid-client-route-owner.js +143 -0
  201. package/dist/shims/internal/navigation-untracked.d.ts +35 -0
  202. package/dist/shims/internal/navigation-untracked.js +55 -0
  203. package/dist/shims/internal/pages-data-fetch-dedup.d.ts +6 -7
  204. package/dist/shims/internal/pages-data-fetch-dedup.js +67 -14
  205. package/dist/shims/internal/pages-data-target.d.ts +7 -2
  206. package/dist/shims/internal/pages-data-target.js +17 -8
  207. package/dist/shims/internal/pages-router-accessor.d.ts +19 -0
  208. package/dist/shims/internal/pages-router-accessor.js +13 -0
  209. package/dist/shims/internal/router-context.d.ts +2 -1
  210. package/dist/shims/internal/router-context.js +3 -1
  211. package/dist/shims/link.js +47 -19
  212. package/dist/shims/metadata.js +4 -4
  213. package/dist/shims/navigation.d.ts +8 -2
  214. package/dist/shims/navigation.js +63 -31
  215. package/dist/shims/ppr-fallback-shell.d.ts +5 -1
  216. package/dist/shims/ppr-fallback-shell.js +28 -7
  217. package/dist/shims/router.d.ts +18 -3
  218. package/dist/shims/router.js +512 -142
  219. package/dist/shims/script.js +8 -4
  220. package/dist/shims/server.d.ts +16 -1
  221. package/dist/shims/server.js +44 -12
  222. package/dist/shims/unified-request-context.js +1 -0
  223. package/dist/utils/built-asset-url.d.ts +4 -0
  224. package/dist/utils/built-asset-url.js +11 -0
  225. package/dist/utils/commonjs-loader.d.ts +16 -0
  226. package/dist/utils/commonjs-loader.js +100 -0
  227. package/dist/utils/deployment-id.d.ts +8 -0
  228. package/dist/utils/deployment-id.js +22 -0
  229. package/dist/utils/has-trailing-comma.d.ts +24 -0
  230. package/dist/utils/has-trailing-comma.js +62 -0
  231. package/dist/utils/html-limited-bots.d.ts +18 -1
  232. package/dist/utils/html-limited-bots.js +23 -1
  233. package/dist/utils/parse-cookie.d.ts +13 -0
  234. package/dist/utils/parse-cookie.js +52 -0
  235. package/dist/utils/path.d.ts +7 -1
  236. package/dist/utils/path.js +9 -1
  237. package/dist/utils/text-stream.d.ts +1 -1
  238. package/dist/utils/text-stream.js +2 -2
  239. package/dist/utils/vite-version.d.ts +12 -1
  240. package/dist/utils/vite-version.js +9 -1
  241. package/package.json +2 -2
  242. package/dist/shims/internal/parse-cookie-header.d.ts +0 -14
  243. package/dist/shims/internal/parse-cookie-header.js +0 -30
@@ -7,7 +7,7 @@ import { validateCsrfOrigin, validateServerActionPayload } from "./request-pipel
7
7
  import { headersContextFromRequest, setHeadersContext } from "../shims/headers.js";
8
8
  import { getAndClearActionRevalidationKind } from "../shims/cache.js";
9
9
  import { APP_RSC_RENDER_MODE_ACTION_RERENDER_PRESERVE_UI } from "./app-rsc-render-mode.js";
10
- import { setCurrentFetchCacheMode, setCurrentFetchSoftTags } from "../shims/fetch-cache.js";
10
+ import { setCurrentFetchCacheMode, setCurrentFetchSoftTags, setCurrentForceDynamicFetchDefault } from "../shims/fetch-cache.js";
11
11
  import { VINEXT_RSC_CONTENT_TYPE, VINEXT_RSC_VARY_HEADER, applyRscCompatibilityIdHeader } from "./app-rsc-cache-busting.js";
12
12
  import { readStreamAsTextWithLimit } from "../utils/text-stream.js";
13
13
  import { mergeMiddlewareResponseHeaders } from "./middleware-response-headers.js";
@@ -15,9 +15,9 @@ import { applyEdgeRuntimeHeader } from "./app-page-response.js";
15
15
  import { getNextErrorDigest, parseNextHttpErrorDigest, parseNextRedirectDigest } from "./next-error-digest.js";
16
16
  import { createServerActionNotFoundResponse, getServerActionNotFoundMessage, isServerActionNotFoundError } from "./server-action-not-found.js";
17
17
  import { deferUntilStreamConsumed } from "./app-page-stream.js";
18
+ import { buildAppPageTags } from "./implicit-tags.js";
18
19
  import { resolveAppPageNavigationParams } from "./app-page-element-builder.js";
19
20
  import { resolveAppPageActionRerenderTarget } from "./app-page-request.js";
20
- import { buildPageCacheTags } from "./implicit-tags.js";
21
21
  import { getSetCookieName } from "./cookie-utils.js";
22
22
  //#region src/server/app-server-action-execution.ts
23
23
  /**
@@ -46,6 +46,10 @@ function resolveActionRevalidationKind(hasModifiedCookies) {
46
46
  if (hasModifiedCookies) return ACTION_DID_REVALIDATE_STATIC_AND_DYNAMIC;
47
47
  return revalidationKind;
48
48
  }
49
+ function clearRejectedActionSideEffects(getAndClearPendingCookies) {
50
+ getAndClearPendingCookies();
51
+ getAndClearActionRevalidationKind();
52
+ }
49
53
  function cloneActionRedirectHeaders(requestHeaders) {
50
54
  const headers = new Headers(requestHeaders);
51
55
  for (const header of ACTION_REDIRECT_RENDER_STRIPPED_HEADERS) headers.delete(header);
@@ -173,7 +177,7 @@ async function readActionFormDataWithLimit(request, maxBytes) {
173
177
  if (result.done) break;
174
178
  totalSize += result.value.byteLength;
175
179
  if (totalSize > maxBytes) {
176
- await reader.cancel();
180
+ reader.cancel();
177
181
  throw new Error("Request body too large");
178
182
  }
179
183
  chunks.push(result.value);
@@ -233,7 +237,7 @@ function applyActionRedirectBasePath(url, basePath) {
233
237
  return `${addBasePathToPathname(pathname, basePath)}${suffix}`;
234
238
  }
235
239
  function buildServerActionPageTags(route, pathname) {
236
- return buildPageCacheTags(pathname, [], [...route.routeSegments ?? []], "page");
240
+ return buildAppPageTags(pathname, [], route.routeSegments ?? []);
237
241
  }
238
242
  function resolveInternalActionRedirectTarget(redirectUrl, requestUrl, basePath) {
239
243
  if (isExternalUrl(redirectUrl)) {
@@ -331,6 +335,7 @@ async function handleProgressiveServerActionRequest(options) {
331
335
  }
332
336
  const payloadResponse = await validateServerActionPayload(body);
333
337
  if (payloadResponse) {
338
+ clearRejectedActionSideEffects(options.getAndClearPendingCookies);
334
339
  options.clearRequestContext();
335
340
  return payloadResponse;
336
341
  }
@@ -480,8 +485,29 @@ async function handleServerActionRscRequest(options) {
480
485
  if (options.request.method.toUpperCase() !== "POST" || !options.actionId) return null;
481
486
  const csrfResponse = validateCsrfOrigin(options.request, options.allowedOrigins);
482
487
  if (csrfResponse) return csrfResponse;
483
- if (parseInt(options.request.headers.get("content-length") || "0", 10) > options.maxActionBodySize) return renderFetchActionBodyExceededResponse(options);
488
+ if (parseInt(options.request.headers.get("content-length") || "0", 10) > options.maxActionBodySize) {
489
+ if (options.request.body) options.request.body.cancel().catch(() => {});
490
+ return renderFetchActionBodyExceededResponse(options);
491
+ }
484
492
  try {
493
+ let action;
494
+ if (options.contentType.startsWith("multipart/form-data")) {
495
+ let loadedAction;
496
+ try {
497
+ loadedAction = await options.loadServerAction(options.actionId);
498
+ } catch (error) {
499
+ if (isServerActionNotFoundError(error, options.actionId)) return createActionNotFoundResponse(options.actionId, {
500
+ clearRequestContext: options.clearRequestContext,
501
+ getAndClearPendingCookies: options.getAndClearPendingCookies
502
+ });
503
+ throw error;
504
+ }
505
+ if (!isAppServerActionFunction(loadedAction)) return createActionNotFoundResponse(options.actionId, {
506
+ clearRequestContext: options.clearRequestContext,
507
+ getAndClearPendingCookies: options.getAndClearPendingCookies
508
+ });
509
+ action = loadedAction;
510
+ }
485
511
  let body;
486
512
  try {
487
513
  body = options.contentType.startsWith("multipart/form-data") ? await options.readFormDataWithLimit(options.request, options.maxActionBodySize) : await options.readBodyWithLimit(options.request, options.maxActionBodySize);
@@ -491,23 +517,27 @@ async function handleServerActionRscRequest(options) {
491
517
  }
492
518
  const payloadResponse = await validateServerActionPayload(body);
493
519
  if (payloadResponse) {
520
+ clearRejectedActionSideEffects(options.getAndClearPendingCookies);
494
521
  options.clearRequestContext();
495
522
  return payloadResponse;
496
523
  }
497
- let action;
498
- try {
499
- action = await options.loadServerAction(options.actionId);
500
- } catch (error) {
501
- if (isServerActionNotFoundError(error, options.actionId)) return createActionNotFoundResponse(options.actionId, {
524
+ if (action === void 0) {
525
+ let loadedAction;
526
+ try {
527
+ loadedAction = await options.loadServerAction(options.actionId);
528
+ } catch (error) {
529
+ if (isServerActionNotFoundError(error, options.actionId)) return createActionNotFoundResponse(options.actionId, {
530
+ clearRequestContext: options.clearRequestContext,
531
+ getAndClearPendingCookies: options.getAndClearPendingCookies
532
+ });
533
+ throw error;
534
+ }
535
+ if (!isAppServerActionFunction(loadedAction)) return createActionNotFoundResponse(options.actionId, {
502
536
  clearRequestContext: options.clearRequestContext,
503
537
  getAndClearPendingCookies: options.getAndClearPendingCookies
504
538
  });
505
- throw error;
539
+ action = loadedAction;
506
540
  }
507
- if (!isAppServerActionFunction(action)) return createActionNotFoundResponse(options.actionId, {
508
- clearRequestContext: options.clearRequestContext,
509
- getAndClearPendingCookies: options.getAndClearPendingCookies
510
- });
511
541
  const temporaryReferences = options.createTemporaryReferenceSet();
512
542
  const args = await options.decodeReply(body, { temporaryReferences });
513
543
  let returnValue;
@@ -599,6 +629,7 @@ async function handleServerActionRscRequest(options) {
599
629
  params: redirectNavigationParams
600
630
  });
601
631
  setCurrentFetchCacheMode(options.resolveRouteFetchCacheMode?.(targetMatch.route) ?? null);
632
+ setCurrentForceDynamicFetchDefault(options.resolveRouteDynamicConfig?.(targetMatch.route) === "force-dynamic");
602
633
  setCurrentFetchSoftTags(buildServerActionPageTags(targetMatch.route, targetPathname));
603
634
  const element = options.buildPageElement({
604
635
  cleanPathname: targetPathname,
@@ -677,6 +708,7 @@ async function handleServerActionRscRequest(options) {
677
708
  });
678
709
  await options.ensureRouteLoaded?.(actionRerenderTarget.route);
679
710
  setCurrentFetchCacheMode(options.resolveRouteFetchCacheMode?.(actionRerenderTarget.route) ?? null);
711
+ setCurrentForceDynamicFetchDefault(options.resolveRouteDynamicConfig?.(actionRerenderTarget.route) === "force-dynamic");
680
712
  setCurrentFetchSoftTags(buildServerActionPageTags(actionRerenderTarget.route, options.cleanPathname));
681
713
  element = options.buildPageElement({
682
714
  cleanPathname: options.cleanPathname,
@@ -22,6 +22,7 @@ declare function handleSsr(rscStream: ReadableStream<Uint8Array>, navContext: Na
22
22
  capturedRscDataRef?: {
23
23
  value: Promise<ArrayBuffer> | null;
24
24
  };
25
+ pprFallbackShellSignal?: AbortSignal;
25
26
  formState?: ReactFormState | null;
26
27
  basePath?: string;
27
28
  /**
@@ -42,6 +43,7 @@ declare function handleSsr(rscStream: ReadableStream<Uint8Array>, navContext: Na
42
43
  * to resolve before returning the HTML stream. Used for static prerender
43
44
  * and ISR cache writes to avoid caching fallback content. */
44
45
  waitForAllReady?: boolean;
46
+ fallbackToErrorDocumentOnShellError?: boolean;
45
47
  }): Promise<AppSsrRenderResult>;
46
48
  declare const _default: {
47
49
  fetch(request: Request): Promise<Response>;
@@ -1,23 +1,27 @@
1
1
  import "./server-globals.js";
2
2
  import { notFoundResponse } from "./http-error-responses.js";
3
3
  import { isOpenRedirectShaped } from "./request-pipeline.js";
4
+ import { isPprFallbackShellAbortError } from "../shims/ppr-fallback-shell.js";
4
5
  import { AppElementsWire } from "./app-elements-wire.js";
5
6
  import "./app-elements.js";
6
7
  import { AppRouterContext } from "../shims/internal/app-router-context.js";
8
+ import { appendAssetDeploymentIdQuery } from "../utils/deployment-id.js";
7
9
  import { ServerInsertedHTMLContext, appRouterInstance, clearServerInsertedHTML, getBfcacheIdMapContext, renderServerInsertedHTML, setNavigationContext, useServerInsertedHTML } from "../shims/navigation.js";
8
10
  import { runWithNavigationContext } from "../shims/navigation-state.js";
9
11
  import { withScriptNonce } from "../shims/script-nonce-context.js";
10
12
  import { createInlineScriptTag, createNonceAttribute, escapeHtmlAttr, safeJsonStringify } from "./html.js";
11
13
  import { getClientTraceMetadataHTML } from "./client-trace-metadata.js";
14
+ import DefaultGlobalError from "../shims/default-global-error.js";
12
15
  import { BfcacheStateKeyMapContext, ElementsContext, Slot } from "../shims/slot.js";
13
16
  import { createSsrErrorMetaRenderer } from "./app-ssr-error-meta.js";
14
17
  import { createNavigationRuntimeRscMetadataScript, createRscEmbedTransform, createTickBufferedTransform } from "./app-ssr-stream.js";
18
+ import { BeforeInteractiveContext } from "../shims/before-interactive-context.js";
19
+ import { runWithRootParamsScope } from "../shims/root-params.js";
15
20
  import { createBfcacheSegmentStateKeyMap, createInitialBfcacheIdMap } from "./app-browser-state.js";
16
21
  import { RSC_FORM_STATE_GLOBAL } from "./app-browser-hydration.js";
17
22
  import { createClientReferencePreloader } from "./app-client-reference-preloader.js";
18
23
  import { deferUntilStreamConsumed } from "./app-page-stream.js";
19
- import { runWithRootParamsScope } from "../shims/root-params.js";
20
- import { BeforeInteractiveContext } from "../shims/before-interactive-context.js";
24
+ import { renderBeforeInteractiveInlineScripts } from "./before-interactive-head.js";
21
25
  import { createInitialDevServerErrorScript } from "./dev-initial-server-error.js";
22
26
  import { Fragment, createElement, use } from "react";
23
27
  import { renderToReadableStream, renderToStaticMarkup } from "react-dom/server.edge";
@@ -31,6 +35,50 @@ import clientReferences from "virtual:vite-rsc/client-references";
31
35
  * the response-layer combine cap.
32
36
  */
33
37
  const DEFAULT_REACT_MAX_HEADERS_LENGTH = 6e3;
38
+ function isReactDevelopmentRuntime() {
39
+ if (process.env.NODE_ENV === "production") return false;
40
+ if (process.env.NODE_ENV === "development") return true;
41
+ return Function.prototype.toString.call(createElement).includes("getOwner");
42
+ }
43
+ function isStaticPrerenderModule(value) {
44
+ return typeof value === "object" && value !== null && "prerender" in value && typeof value.prerender === "function";
45
+ }
46
+ async function loadStaticPrerender() {
47
+ const staticRenderer = await import("react-dom/static.edge");
48
+ if (isStaticPrerenderModule(staticRenderer)) return staticRenderer.prerender;
49
+ if (isReactDevelopmentRuntime()) try {
50
+ const [{ createRequire }, path] = await Promise.all([import("node:module"), import("node:path")]);
51
+ const reactDomPackageJson = createRequire(import.meta.url).resolve("react-dom/package.json");
52
+ const reactDomDir = path.dirname(reactDomPackageJson);
53
+ const devRenderer = await import(path.join(reactDomDir, "cjs/react-dom-server.edge.development.js"));
54
+ if (isStaticPrerenderModule(devRenderer)) return devRenderer.prerender;
55
+ const devRendererDefault = typeof devRenderer === "object" && devRenderer !== null && "default" in devRenderer && devRenderer.default;
56
+ if (isStaticPrerenderModule(devRendererDefault)) return devRendererDefault.prerender;
57
+ throw new Error("react-dom development renderer did not expose prerender().");
58
+ } catch (error) {
59
+ throw new Error("[vinext] Failed to load React static development renderer.", { cause: error });
60
+ }
61
+ throw new Error("[vinext] react-dom/static.edge did not expose prerender().");
62
+ }
63
+ function createUtf8Stream(html) {
64
+ const encoder = new TextEncoder();
65
+ return new ReadableStream({ start(controller) {
66
+ controller.enqueue(encoder.encode(html));
67
+ controller.close();
68
+ } });
69
+ }
70
+ function buildBootstrapModuleScript(bootstrapModuleUrl, nonce) {
71
+ if (!bootstrapModuleUrl) return "";
72
+ return `<script type="module"${createNonceAttribute(nonce)} src="` + escapeHtmlAttr(bootstrapModuleUrl) + "\" id=\"_R_\" async=\"\"><\/script>";
73
+ }
74
+ function renderSsrErrorDocumentShell(bootstrapModuleUrl, nonce) {
75
+ const html = renderToStaticMarkup(createElement(DefaultGlobalError, { error: null })).replace("<style>", "<style data-vinext-error-shell-style=\"\">");
76
+ const bootstrapScript = buildBootstrapModuleScript(bootstrapModuleUrl, nonce);
77
+ if (!bootstrapScript) return createUtf8Stream(`<!DOCTYPE html>${html}`);
78
+ const documentClose = "</body></html>";
79
+ if (!html.endsWith(documentClose)) return createUtf8Stream(`<!DOCTYPE html>${html}${bootstrapScript}`);
80
+ return createUtf8Stream(`<!DOCTYPE html>${html.slice(0, -14)}${bootstrapScript}${documentClose}`);
81
+ }
34
82
  const clientReferencePreloader = createClientReferencePreloader({
35
83
  getReferences() {
36
84
  return clientReferences;
@@ -60,42 +108,13 @@ function renderInsertedHtml(insertedElements) {
60
108
  } catch {}
61
109
  return insertedHTML;
62
110
  }
63
- /**
64
- * Render captured `<Script strategy="beforeInteractive">` inline scripts to
65
- * HTML, ready to splice immediately after `<head ...>` opens. Each entry has
66
- * already had its inline content escaped via `escapeInlineContent(..., "script")`
67
- * inside the Script shim, so this function only quotes the attributes that
68
- * actually go on the tag (id, nonce, plus the residual passthroughs).
69
- *
70
- * Keeping this function colocated with the rest of the head-injection
71
- * helpers makes it obvious where the boundary is: anything passed through
72
- * here is being concatenated directly into HTML; treat the inputs
73
- * accordingly.
74
- */
75
- const VALID_ATTR_NAME = /^[a-zA-Z][\w.-]*$/;
76
- function renderBeforeInteractiveInlineScripts(scripts) {
77
- if (scripts.length === 0) return "";
78
- let html = "";
79
- for (const script of scripts) {
80
- let attrs = "";
81
- if (script.id) attrs += ` id="${escapeHtmlAttr(script.id)}"`;
82
- attrs += createNonceAttribute(script.nonce);
83
- if (script.attributes) for (const [key, value] of Object.entries(script.attributes)) {
84
- if (!VALID_ATTR_NAME.test(key)) continue;
85
- if (value === true) attrs += ` ${key}`;
86
- else if (typeof value === "string") attrs += ` ${key}="${escapeHtmlAttr(value)}"`;
87
- }
88
- html += `<script${attrs}>${script.innerHTML}<\/script>`;
89
- }
90
- return html;
91
- }
92
111
  function renderFontHtml(fontData, nonce, options = {}) {
93
112
  if (!fontData) return "";
94
113
  let fontHTML = "";
95
114
  const nonceAttr = createNonceAttribute(nonce);
96
115
  const includeStyles = options.includeStyles ?? true;
97
- for (const url of fontData.links ?? []) fontHTML += `<link rel="stylesheet"${nonceAttr} href="${escapeHtmlAttr(url)}" />\n`;
98
- for (const preload of fontData.preloads ?? []) fontHTML += `<link rel="preload"${nonceAttr} href="${escapeHtmlAttr(preload.href)}" as="font" type="${escapeHtmlAttr(preload.type)}" crossorigin />\n`;
116
+ for (const url of fontData.links ?? []) fontHTML += `<link rel="stylesheet"${nonceAttr} href="${escapeHtmlAttr(appendAssetDeploymentIdQuery(url))}" />\n`;
117
+ for (const preload of fontData.preloads ?? []) fontHTML += `<link rel="preload"${nonceAttr} href="${escapeHtmlAttr(appendAssetDeploymentIdQuery(preload.href))}" as="font" type="${escapeHtmlAttr(preload.type)}" crossorigin />\n`;
99
118
  if (includeStyles && fontData.styles && fontData.styles.length > 0) fontHTML += `<style data-vinext-fonts${nonceAttr}>${fontData.styles.join("\n")}</style>\n`;
100
119
  return fontHTML;
101
120
  }
@@ -182,24 +201,50 @@ async function handleSsr(rscStream, navContext, fontData, options) {
182
201
  const ssrRoot = withScriptNonce(createElement(BeforeInteractiveContext.Provider, { value: registerBeforeInteractiveInlineScript }, ssrTree), options?.scriptNonce);
183
202
  const bootstrapModuleUrl = extractBootstrapModuleUrl(await import.meta.viteRsc.loadBootstrapScriptContent("index"));
184
203
  const errorMetaRenderer = createSsrErrorMetaRenderer({ basePath: options?.basePath });
204
+ const pprFallbackShellSignal = options?.pprFallbackShellSignal;
185
205
  let reactLinkHeader = "";
186
206
  const maxHeadersLength = options?.reactMaxHeadersLength ?? DEFAULT_REACT_MAX_HEADERS_LENGTH;
187
207
  const captureHeaders = maxHeadersLength > 0;
188
- const htmlStream = await renderToReadableStream(ssrRoot, {
208
+ const renderOptions = {
209
+ bootstrapModules: bootstrapModuleUrl ? [bootstrapModuleUrl] : void 0,
210
+ formState: options?.formState ?? null,
211
+ nonce: options?.scriptNonce,
189
212
  onHeaders: captureHeaders ? (headers) => {
190
213
  const link = headers.get("Link");
191
214
  if (link) reactLinkHeader = link;
192
215
  } : void 0,
193
216
  maxHeadersLength: captureHeaders ? maxHeadersLength : void 0,
194
- bootstrapModules: bootstrapModuleUrl ? [bootstrapModuleUrl] : void 0,
195
- formState: options?.formState ?? null,
196
- nonce: options?.scriptNonce,
197
217
  onError(error) {
218
+ if (pprFallbackShellSignal && isPprFallbackShellAbortError(error)) return;
198
219
  errorMetaRenderer.capture(error);
199
220
  if (error && typeof error === "object" && "digest" in error) return String(error.digest);
200
221
  if (process.env.NODE_ENV === "production" && error) return ssrErrorDigest(getErrorMessage(error) + (error instanceof Error ? error.stack ?? "" : ""));
201
222
  }
202
- });
223
+ };
224
+ let htmlStream;
225
+ let shellErrorRecovered = false;
226
+ if (pprFallbackShellSignal) {
227
+ const prerender = await loadStaticPrerender();
228
+ const htmlAbortController = new AbortController();
229
+ const pendingHtml = prerender(ssrRoot, {
230
+ ...renderOptions,
231
+ signal: htmlAbortController.signal
232
+ });
233
+ setTimeout(() => htmlAbortController.abort(), 0);
234
+ htmlStream = (await pendingHtml).prelude;
235
+ } else {
236
+ let streamingHtmlStream;
237
+ try {
238
+ streamingHtmlStream = await renderToReadableStream(ssrRoot, { ...renderOptions });
239
+ if (options?.waitForAllReady === true) await streamingHtmlStream.allReady;
240
+ htmlStream = streamingHtmlStream;
241
+ } catch (error) {
242
+ streamingHtmlStream?.cancel().catch(() => {});
243
+ if (options?.fallbackToErrorDocumentOnShellError !== true || options?.waitForAllReady === true || typeof error?.digest === "string") throw error;
244
+ shellErrorRecovered = true;
245
+ htmlStream = renderSsrErrorDocumentShell(bootstrapModuleUrl, options?.scriptNonce);
246
+ }
247
+ }
203
248
  const inlineCssManifest = globalThis.__VINEXT_INLINE_CSS__;
204
249
  const fontStyles = fontData?.styles ?? [];
205
250
  const mergeFontStylesIntoInlineCss = fontStyles.length > 0 && hasInlineCssManifest(inlineCssManifest);
@@ -221,11 +266,11 @@ async function handleSsr(rscStream, navContext, fontData, options) {
221
266
  return buildHeadInjectionHtml(ssrNavigationContext, bootstrapModuleUrl, options?.formState ?? null, insertedHTML + errorMetaHTML + getTraceMetaHTML() + initialDevServerErrorHTML, fontHTML, options?.scriptNonce);
222
267
  };
223
268
  const getBeforeInteractiveHeadHTML = () => renderBeforeInteractiveInlineScripts(beforeInteractiveInlineScripts);
224
- if (options?.waitForAllReady === true) await htmlStream.allReady;
225
269
  return {
226
270
  htmlStream: deferUntilStreamConsumed(htmlStream.pipeThrough(createTickBufferedTransform(rscEmbed, getInsertedHTML, getBeforeInteractiveHeadHTML, inlineCssManifest, inlineCssFontStyles, inlineCssFontStyleFallbackHTML, options?.scriptNonce)), cleanup),
227
271
  metadataReady: Promise.resolve(),
228
272
  capturedRscData: options?.capturedRscDataRef?.value ?? null,
273
+ shellErrorRecovered,
229
274
  linkHeader: reactLinkHeader
230
275
  };
231
276
  } catch (error) {
@@ -0,0 +1,17 @@
1
+ import { BeforeInteractiveInlineScript } from "../shims/before-interactive-context.js";
2
+
3
+ //#region src/server/before-interactive-head.d.ts
4
+ /**
5
+ * Render captured `<Script strategy="beforeInteractive">` scripts to HTML,
6
+ * ready to splice immediately after `<head ...>` opens. Each entry has already
7
+ * had its inline content escaped via `escapeInlineContent(..., "script")`
8
+ * inside the Script shim, so this function only quotes the attributes that
9
+ * actually go on the tag (id, src, nonce, plus the residual passthroughs).
10
+ *
11
+ * Keeping this function in its own module makes the boundary obvious: anything
12
+ * passed through here is being concatenated directly into HTML; treat the
13
+ * inputs accordingly.
14
+ */
15
+ declare function renderBeforeInteractiveInlineScripts(scripts: readonly BeforeInteractiveInlineScript[]): string;
16
+ //#endregion
17
+ export { renderBeforeInteractiveInlineScripts };
@@ -0,0 +1,35 @@
1
+ import { createNonceAttribute, escapeHtmlAttr } from "./html.js";
2
+ //#region src/server/before-interactive-head.ts
3
+ const VALID_ATTR_NAME = /^[a-zA-Z][\w.-]*$/;
4
+ /**
5
+ * Render captured `<Script strategy="beforeInteractive">` scripts to HTML,
6
+ * ready to splice immediately after `<head ...>` opens. Each entry has already
7
+ * had its inline content escaped via `escapeInlineContent(..., "script")`
8
+ * inside the Script shim, so this function only quotes the attributes that
9
+ * actually go on the tag (id, src, nonce, plus the residual passthroughs).
10
+ *
11
+ * Keeping this function in its own module makes the boundary obvious: anything
12
+ * passed through here is being concatenated directly into HTML; treat the
13
+ * inputs accordingly.
14
+ */
15
+ function renderBeforeInteractiveInlineScripts(scripts) {
16
+ if (scripts.length === 0) return "";
17
+ let html = "";
18
+ for (const script of scripts) {
19
+ let attrs = "";
20
+ if (script.id) attrs += ` id="${escapeHtmlAttr(script.id)}"`;
21
+ if (script.src) attrs += ` src="${escapeHtmlAttr(script.src)}"`;
22
+ attrs += createNonceAttribute(script.nonce);
23
+ if (script.attributes) for (const [key, value] of Object.entries(script.attributes)) {
24
+ if (!VALID_ATTR_NAME.test(key)) continue;
25
+ if (key === "data-nscript") continue;
26
+ if (value === true) attrs += ` ${key}`;
27
+ else if (typeof value === "string") attrs += ` ${key}="${escapeHtmlAttr(value)}"`;
28
+ }
29
+ attrs += ` data-nscript="beforeInteractive"`;
30
+ html += `<script${attrs}>${script.innerHTML ?? ""}<\/script>`;
31
+ }
32
+ return html;
33
+ }
34
+ //#endregion
35
+ export { renderBeforeInteractiveInlineScripts };
@@ -19,6 +19,10 @@ function applyCdnResponseHeaders(headers, input) {
19
19
  headers.delete("Cache-Control");
20
20
  const map = getCdnCacheAdapter().buildResponseHeaders(input);
21
21
  for (const [name, value] of Object.entries(map)) {
22
+ if (value === null) {
23
+ headers.delete(name);
24
+ continue;
25
+ }
22
26
  if (value === "") continue;
23
27
  headers.set(name, value);
24
28
  }
@@ -1,8 +1,5 @@
1
1
  //#region src/server/csp.ts
2
2
  const ESCAPE_REGEX = /[&><\u2028\u2029]/;
3
- function matchesDirectiveName(directive, name) {
4
- return directive === name || directive.startsWith(`${name} `);
5
- }
6
3
  function getNodeHeaderValue(headers, key) {
7
4
  const value = headers?.[key];
8
5
  if (Array.isArray(value)) return value.join(", ");
@@ -11,7 +8,7 @@ function getNodeHeaderValue(headers, key) {
11
8
  }
12
9
  function getScriptNonceFromHeader(cspHeaderValue) {
13
10
  const directives = cspHeaderValue.split(";").map((directive) => directive.trim());
14
- const directive = directives.find((value) => matchesDirectiveName(value, "script-src")) ?? directives.find((value) => matchesDirectiveName(value, "default-src"));
11
+ const directive = directives.find((value) => value.startsWith("script-src")) ?? directives.find((value) => value.startsWith("default-src"));
15
12
  if (!directive) return;
16
13
  const nonce = directive.split(" ").slice(1).map((source) => source.trim()).find((source) => source.startsWith("'nonce-") && source.length > 8 && source.endsWith("'"))?.slice(7, -1);
17
14
  if (!nonce) return;
@@ -43,7 +43,7 @@ declare function createSSRHandler(server: ViteDevServer, runner: ModuleImporter,
43
43
  * `next.config`. When undefined or empty, no meta tags are emitted.
44
44
  */
45
45
 
46
- clientTraceMetadata?: readonly string[]): (req: IncomingMessage, res: ServerResponse, url: string, /** Status code override — propagated from middleware rewrite status. */
46
+ clientTraceMetadata?: readonly string[], htmlLimitedBots?: string): (req: IncomingMessage, res: ServerResponse, url: string, /** Status code override — propagated from middleware rewrite status. */
47
47
 
48
48
  statusCode?: number,
49
49
  /**
@@ -53,6 +53,6 @@ statusCode?: number,
53
53
  * client-side navigations in the Pages Router.
54
54
  */
55
55
 
56
- isDataReq?: boolean) => Promise<void>;
56
+ isDataReq?: boolean, originalUrl?: string) => Promise<void>;
57
57
  //#endregion
58
58
  export { createSSRHandler, detectLocaleFromHeaders, extractLocaleFromUrl, parseCookieLocale };