vinext 1.0.0-beta.7 → 1.0.0-beta.9

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 (137) hide show
  1. package/README.md +43 -0
  2. package/dist/build/prerender-paths.d.ts +50 -1
  3. package/dist/build/prerender-paths.js +627 -88
  4. package/dist/build/prerender.d.ts +4 -2
  5. package/dist/build/prerender.js +40 -23
  6. package/dist/build/report.d.ts +16 -1
  7. package/dist/build/report.js +64 -3
  8. package/dist/build/static-export.d.ts +1 -1
  9. package/dist/cache/cache-adapters-virtual.d.ts +47 -1
  10. package/dist/cache/cache-adapters-virtual.js +10 -1
  11. package/dist/cli.js +7 -0
  12. package/dist/client/vinext-next-data.d.ts +2 -0
  13. package/dist/config/config-matchers.d.ts +3 -3
  14. package/dist/config/config-matchers.js +8 -3
  15. package/dist/config/prerender.d.ts +2 -2
  16. package/dist/config/prerender.js +2 -2
  17. package/dist/entries/app-browser-entry.d.ts +1 -1
  18. package/dist/entries/app-browser-entry.js +4 -1
  19. package/dist/entries/app-rsc-entry.d.ts +2 -0
  20. package/dist/entries/app-rsc-entry.js +28 -4
  21. package/dist/entries/app-rsc-manifest.js +1 -0
  22. package/dist/entries/pages-server-entry.d.ts +1 -1
  23. package/dist/entries/pages-server-entry.js +4 -1
  24. package/dist/index.d.ts +3 -0
  25. package/dist/index.js +146 -28
  26. package/dist/init-cloudflare.d.ts +5 -3
  27. package/dist/init-cloudflare.js +58 -25
  28. package/dist/plugins/import-meta-url.js +7 -6
  29. package/dist/routing/app-router.d.ts +3 -1
  30. package/dist/routing/app-router.js +5 -1
  31. package/dist/server/app-browser-entry.js +41 -13
  32. package/dist/server/app-browser-navigation-controller.js +1 -1
  33. package/dist/server/app-browser-state.js +1 -1
  34. package/dist/server/app-browser-visible-commit.js +1 -1
  35. package/dist/server/app-middleware.d.ts +8 -0
  36. package/dist/server/app-middleware.js +28 -2
  37. package/dist/server/app-optimistic-routing.d.ts +6 -1
  38. package/dist/server/app-optimistic-routing.js +13 -1
  39. package/dist/server/app-page-boundary.js +2 -1
  40. package/dist/server/app-page-cache-finalizer.d.ts +16 -1
  41. package/dist/server/app-page-cache-finalizer.js +73 -4
  42. package/dist/server/app-page-cache.js +2 -2
  43. package/dist/server/app-page-dispatch.d.ts +2 -0
  44. package/dist/server/app-page-dispatch.js +12 -4
  45. package/dist/server/app-page-execution.js +1 -1
  46. package/dist/server/app-page-render.d.ts +1 -0
  47. package/dist/server/app-page-render.js +27 -9
  48. package/dist/server/app-page-response.js +2 -2
  49. package/dist/server/app-page-stream.js +2 -1
  50. package/dist/server/app-pages-bridge.js +2 -0
  51. package/dist/server/app-prerender-endpoints.js +23 -11
  52. package/dist/server/app-prerender-static-params.js +12 -5
  53. package/dist/server/app-route-handler-dispatch.js +13 -9
  54. package/dist/server/app-route-handler-execution.d.ts +1 -0
  55. package/dist/server/app-route-handler-execution.js +127 -14
  56. package/dist/server/app-route-handler-policy.d.ts +15 -3
  57. package/dist/server/app-route-handler-policy.js +19 -4
  58. package/dist/server/app-route-tree-prefetch.js +2 -2
  59. package/dist/server/app-router-entry.js +52 -8
  60. package/dist/server/app-rsc-cache-busting.d.ts +22 -4
  61. package/dist/server/app-rsc-cache-busting.js +68 -18
  62. package/dist/server/app-rsc-handler.d.ts +2 -0
  63. package/dist/server/app-rsc-handler.js +95 -30
  64. package/dist/server/app-rsc-response-finalizer.js +24 -7
  65. package/dist/server/app-rsc-route-matching.d.ts +1 -0
  66. package/dist/server/app-rsc-route-matching.js +1 -0
  67. package/dist/server/app-segment-config.d.ts +3 -1
  68. package/dist/server/app-segment-config.js +23 -11
  69. package/dist/server/app-server-action-execution.js +4 -4
  70. package/dist/server/app-ssr-entry.js +4 -4
  71. package/dist/server/cache-control.d.ts +8 -2
  72. package/dist/server/cache-control.js +35 -4
  73. package/dist/server/cacheability-limits.d.ts +9 -0
  74. package/dist/server/cacheability-limits.js +9 -0
  75. package/dist/server/cacheability-manifest.d.ts +42 -0
  76. package/dist/server/cacheability-manifest.js +191 -0
  77. package/dist/server/cacheability-request.d.ts +20 -0
  78. package/dist/server/cacheability-request.js +457 -0
  79. package/dist/server/config-headers.js +23 -3
  80. package/dist/server/headers.d.ts +16 -3
  81. package/dist/server/headers.js +29 -3
  82. package/dist/server/middleware-matcher.d.ts +9 -1
  83. package/dist/server/middleware-matcher.js +22 -1
  84. package/dist/server/middleware-runtime.d.ts +6 -0
  85. package/dist/server/middleware-runtime.js +18 -3
  86. package/dist/server/normalize-path.js +1 -1
  87. package/dist/server/pages-api-route.js +1 -1
  88. package/dist/server/pages-i18n.js +3 -2
  89. package/dist/server/pages-page-handler.js +22 -9
  90. package/dist/server/pages-request-pipeline.d.ts +2 -0
  91. package/dist/server/pages-request-pipeline.js +17 -6
  92. package/dist/server/pages-router-entry.js +56 -9
  93. package/dist/server/prerender-route-params.js +1 -0
  94. package/dist/server/prerender-work-unit-setup.d.ts +3 -2
  95. package/dist/server/prerender-work-unit-setup.js +34 -11
  96. package/dist/server/prod-server.js +5 -5
  97. package/dist/server/request-pipeline.d.ts +1 -5
  98. package/dist/server/request-pipeline.js +2 -9
  99. package/dist/server/revalidation-host.js +1 -0
  100. package/dist/server/server-globals.js +13 -0
  101. package/dist/server/static-file-signal.d.ts +13 -0
  102. package/dist/server/static-file-signal.js +33 -0
  103. package/dist/server/worker-prerender-discovery.d.ts +19 -0
  104. package/dist/server/worker-prerender-discovery.js +55 -0
  105. package/dist/server/worker-utils.js +2 -3
  106. package/dist/shims/cache-runtime.js +140 -102
  107. package/dist/shims/cache.js +11 -1
  108. package/dist/shims/cacheability-classification.d.ts +85 -0
  109. package/dist/shims/cacheability-classification.js +146 -0
  110. package/dist/shims/cdn-cache.d.ts +30 -1
  111. package/dist/shims/cdn-cache.js +25 -1
  112. package/dist/shims/error.js +1 -1
  113. package/dist/shims/fetch-cache.js +8 -3
  114. package/dist/shims/headers.d.ts +3 -1
  115. package/dist/shims/headers.js +9 -5
  116. package/dist/shims/internal/app-prefetch-rsc-request.d.ts +25 -0
  117. package/dist/shims/internal/app-prefetch-rsc-request.js +21 -0
  118. package/dist/shims/internal/app-route-prefetch-policy.d.ts +2 -0
  119. package/dist/shims/internal/app-route-prefetch-policy.js +2 -0
  120. package/dist/shims/internal/hybrid-client-route-owner-direct.d.ts +2 -2
  121. package/dist/shims/internal/hybrid-client-route-owner-direct.js +29 -11
  122. package/dist/shims/internal/hybrid-client-route-owner.js +2 -2
  123. package/dist/shims/internal/make-hanging-promise.d.ts +7 -1
  124. package/dist/shims/internal/make-hanging-promise.js +13 -2
  125. package/dist/shims/internal/work-unit-async-storage.d.ts +2 -0
  126. package/dist/shims/link.js +25 -14
  127. package/dist/shims/navigation.js +31 -11
  128. package/dist/shims/navigation.react-server.js +1 -1
  129. package/dist/shims/request-context.d.ts +2 -0
  130. package/dist/shims/server.js +1 -1
  131. package/dist/shims/url-utils.d.ts +8 -1
  132. package/dist/shims/url-utils.js +10 -1
  133. package/dist/utils/prerender-output-paths.d.ts +7 -7
  134. package/dist/utils/prerender-output-paths.js +16 -9
  135. package/dist/utils/react-compiler-support.d.ts +20 -0
  136. package/dist/utils/react-compiler-support.js +29 -0
  137. package/package.json +10 -2
@@ -1,15 +1,77 @@
1
- import { NEVER_CACHE_CONTROL, applyCdnResponseHeaders } from "./cache-control.js";
1
+ import { NEVER_CACHE_CONTROL, applyCdnResponseHeaders, hasExplicitNonCacheableResponsePolicy } from "./cache-control.js";
2
2
  import { isrCacheControl } from "./isr-cache.js";
3
3
  import { isDraftModeRequest, setHeadersContext } from "../shims/headers.js";
4
4
  import { _drainPendingRevalidations } from "../shims/cache-request-state.js";
5
5
  import { copyLinkHeaderProvenance } from "./app-response-header-provenance.js";
6
+ import { CACHEABILITY_POLICY_HEADERS, getRouteCacheabilityCaptureOptions, getRouteCacheabilityDynamicReason, isRouteCacheabilityEvaluation, markRouteCacheabilityExplicitResponsePolicy, markRouteCacheabilityResponseBodyComplete } from "../shims/cacheability-classification.js";
6
7
  import { applyRouteHandlerMiddlewareContext, applyRouteHandlerRevalidateHeader, assertSupportedAppRouteHandlerResponse, buildAppRouteCacheValue, finalizeRouteHandlerResponse, markRouteHandlerCacheMiss } from "./app-route-handler-response.js";
7
- import { runWithRootParamsUsage } from "../shims/root-params.js";
8
8
  import { isPossibleAppRouteActionRequest } from "./app-action-request.js";
9
+ import { runWithRootParamsUsage } from "../shims/root-params.js";
9
10
  import { createTrackedAppRouteRequest, markKnownDynamicAppRoute } from "./app-route-handler-runtime.js";
10
11
  import { createStaticGenerationHeadersContext, getAppRouteStaticGenerationErrorMessage } from "./app-static-generation.js";
11
- import { resolveAppRouteHandlerSpecialError, shouldApplyAppRouteHandlerRevalidateHeader, shouldWriteAppRouteHandlerCache } from "./app-route-handler-policy.js";
12
+ import { resolveAppRouteHandlerSpecialError, shouldApplyAppRouteHandlerRevalidateHeader, shouldCompleteAppRouteHandlerResponse, shouldWriteAppRouteHandlerCache } from "./app-route-handler-policy.js";
13
+ import { CACHEABILITY_ADMISSION_RESPONSE_BODY_LIMIT } from "./cacheability-limits.js";
12
14
  //#region src/server/app-route-handler-execution.ts
15
+ function hasExplicitCacheableResponsePolicy(headers) {
16
+ return !hasExplicitNonCacheableResponsePolicy(headers) && CACHEABILITY_POLICY_HEADERS.some((name) => headers.has(name));
17
+ }
18
+ async function completeAppRouteHandlerResponse(response) {
19
+ const { captureCacheabilityAdmissionBody } = await import("./cacheability-request.js");
20
+ const captureOptions = getRouteCacheabilityCaptureOptions();
21
+ const captured = await captureCacheabilityAdmissionBody(response.body, captureOptions?.captureDeadlineAt ?? Date.now() + 2e4, CACHEABILITY_ADMISSION_RESPONSE_BODY_LIMIT, captureOptions?.captureBudget);
22
+ const completed = new Response(captured.body, {
23
+ headers: response.headers,
24
+ status: response.status,
25
+ statusText: response.statusText
26
+ });
27
+ copyLinkHeaderProvenance(response.headers, completed.headers);
28
+ return {
29
+ completed: captured.kind === "captured",
30
+ response: completed
31
+ };
32
+ }
33
+ function deferAppRouteHandlerCleanup(response, cleanup) {
34
+ if (!response.body) {
35
+ cleanup();
36
+ return response;
37
+ }
38
+ const reader = response.body.getReader();
39
+ let cleaned = false;
40
+ const cleanOnce = async () => {
41
+ if (cleaned) return;
42
+ cleaned = true;
43
+ reader.releaseLock();
44
+ await cleanup();
45
+ };
46
+ const body = new ReadableStream({
47
+ async pull(controller) {
48
+ try {
49
+ const result = await reader.read();
50
+ if (result.done) {
51
+ await cleanOnce();
52
+ controller.close();
53
+ } else controller.enqueue(result.value);
54
+ } catch (error) {
55
+ await cleanOnce();
56
+ controller.error(error);
57
+ }
58
+ },
59
+ async cancel(reason) {
60
+ try {
61
+ await reader.cancel(reason);
62
+ } finally {
63
+ await cleanOnce();
64
+ }
65
+ }
66
+ }, { highWaterMark: 0 });
67
+ const deferred = new Response(body, {
68
+ headers: response.headers,
69
+ status: response.status,
70
+ statusText: response.statusText
71
+ });
72
+ copyLinkHeaderProvenance(response.headers, deferred.headers);
73
+ return deferred;
74
+ }
13
75
  function applyDraftModeCachePolicy(response, isDraftMode) {
14
76
  if (!isDraftMode) return response;
15
77
  const headers = new Headers(response.headers);
@@ -54,13 +116,16 @@ async function runAppRouteHandler(options) {
54
116
  kind: "route-handler",
55
117
  routePattern: options.routePattern ?? new URL(options.request.url).pathname
56
118
  }, () => options.handlerFn(trackedRequest.request, { params: options.params }));
119
+ const dynamicUsedInContext = options.consumeDynamicUsage();
57
120
  return {
58
- dynamicUsedInHandler: options.consumeDynamicUsage(),
121
+ didAccessDynamicRequest: () => trackedRequest.didAccessDynamicRequest(),
122
+ dynamicUsedInHandler: trackedRequest.didAccessDynamicRequest() || dynamicUsedInContext,
59
123
  response
60
124
  };
61
125
  }
62
126
  async function executeAppRouteHandler(options) {
63
127
  const previousHeadersPhase = options.setHeadersAccessPhase("route-handler");
128
+ let cleanupDeferredToBody = false;
64
129
  const middlewareMergeOptions = { appendResponseLink: options.handler.runtime === "edge" || options.handler.runtime === "experimental-edge" };
65
130
  try {
66
131
  let handlerResult;
@@ -72,19 +137,44 @@ async function executeAppRouteHandler(options) {
72
137
  } finally {
73
138
  await _drainPendingRevalidations();
74
139
  }
75
- const { dynamicUsedInHandler, response } = handlerResult;
140
+ let { dynamicUsedInHandler, response } = handlerResult;
76
141
  assertSupportedAppRouteHandlerResponse(response);
77
- const handlerSetCacheControl = response.headers.has("cache-control");
142
+ const handlerSetCachePolicy = CACHEABILITY_POLICY_HEADERS.some((name) => response.headers.has(name));
143
+ const hasExplicitCacheablePolicy = hasExplicitCacheableResponsePolicy(response.headers);
144
+ if (hasExplicitCacheablePolicy) markRouteCacheabilityExplicitResponsePolicy();
145
+ const draftModeBeforeCompletion = options.getActiveDraftModeState?.() ?? options.isDraftMode === true;
146
+ const handlerDraftCookieBeforeCompletion = options.getDraftModeCookieHeader() ?? options.initialDraftModeCookie;
147
+ if (shouldCompleteAppRouteHandlerResponse({
148
+ dynamicConfig: options.handler.dynamic,
149
+ dynamicUsedInHandler,
150
+ hasExplicitCacheablePolicy,
151
+ handlerSetCachePolicy,
152
+ isAutoHead: options.isAutoHead,
153
+ isDraftMode: draftModeBeforeCompletion || handlerDraftCookieBeforeCompletion != null,
154
+ isProduction: options.isProduction,
155
+ method: options.method,
156
+ revalidateSeconds: options.revalidateSeconds,
157
+ requiresCompletedResponseAdmission: isRouteCacheabilityEvaluation()
158
+ })) {
159
+ const completed = await completeAppRouteHandlerResponse(response);
160
+ response = completed.response;
161
+ cleanupDeferredToBody = !completed.completed;
162
+ if (completed.completed) markRouteCacheabilityResponseBodyComplete();
163
+ const dynamicUsedDuringCompletion = options.consumeDynamicUsage();
164
+ dynamicUsedInHandler = handlerResult.didAccessDynamicRequest() || dynamicUsedDuringCompletion || dynamicUsedInHandler;
165
+ }
166
+ const requestCacheabilityVeto = getRouteCacheabilityDynamicReason();
167
+ const responseMustStayPrivate = Boolean(options.handler.dynamic === "force-dynamic" || dynamicUsedInHandler || requestCacheabilityVeto || cleanupDeferredToBody);
78
168
  if (dynamicUsedInHandler) markKnownDynamicAppRoute(options.routePattern);
79
169
  const pendingCookies = options.getAndClearPendingCookies();
80
- const handlerDraftCookie = options.getDraftModeCookieHeader();
170
+ const handlerDraftCookie = options.getDraftModeCookieHeader() ?? handlerDraftCookieBeforeCompletion;
81
171
  const draftCookie = handlerDraftCookie ?? options.initialDraftModeCookie;
82
172
  const shouldApplyDraftPolicy = (options.getActiveDraftModeState?.() ?? options.isDraftMode === true) || draftCookie != null;
83
173
  if (handlerDraftCookie != null) markKnownDynamicAppRoute(options.routePattern);
84
174
  const routeTags = options.buildPageCacheTags(options.cleanPathname, options.getCollectedFetchTags());
85
175
  if (shouldApplyAppRouteHandlerRevalidateHeader({
86
- dynamicUsedInHandler,
87
- handlerSetCacheControl,
176
+ dynamicUsedInHandler: responseMustStayPrivate,
177
+ handlerSetCachePolicy,
88
178
  isAutoHead: options.isAutoHead,
89
179
  isDraftMode: shouldApplyDraftPolicy,
90
180
  method: options.method,
@@ -96,8 +186,8 @@ async function executeAppRouteHandler(options) {
96
186
  }
97
187
  if (shouldWriteAppRouteHandlerCache({
98
188
  dynamicConfig: options.handler.dynamic,
99
- dynamicUsedInHandler,
100
- handlerSetCacheControl,
189
+ dynamicUsedInHandler: responseMustStayPrivate,
190
+ handlerSetCachePolicy,
101
191
  isAutoHead: options.isAutoHead,
102
192
  isDraftMode: shouldApplyDraftPolicy,
103
193
  isProduction: options.isProduction,
@@ -123,12 +213,35 @@ async function executeAppRouteHandler(options) {
123
213
  })();
124
214
  options.executionContext?.waitUntil(routeWritePromise);
125
215
  }
126
- options.clearRequestContext();
127
- return applyDraftModeCachePolicy(applyRouteHandlerMiddlewareContext(finalizeRouteHandlerResponse(response, {
216
+ let finalized = applyDraftModeCachePolicy(applyRouteHandlerMiddlewareContext(finalizeRouteHandlerResponse(response, {
128
217
  pendingCookies,
129
218
  draftCookie,
130
219
  isHead: options.isAutoHead
131
220
  }), options.middlewareContext, middlewareMergeOptions), shouldApplyDraftPolicy);
221
+ const preserveHandlerPolicy = isRouteCacheabilityEvaluation() ? hasExplicitCacheablePolicy : handlerSetCachePolicy;
222
+ if (responseMustStayPrivate && !preserveHandlerPolicy) {
223
+ const headers = new Headers(finalized.headers);
224
+ applyCdnResponseHeaders(headers, { cacheControl: NEVER_CACHE_CONTROL });
225
+ finalized = new Response(finalized.body, {
226
+ headers,
227
+ status: finalized.status,
228
+ statusText: finalized.statusText
229
+ });
230
+ copyLinkHeaderProvenance(response.headers, finalized.headers);
231
+ }
232
+ if (!cleanupDeferredToBody) {
233
+ options.clearRequestContext();
234
+ return finalized;
235
+ }
236
+ return deferAppRouteHandlerCleanup(finalized, async () => {
237
+ try {
238
+ await _drainPendingRevalidations();
239
+ options.consumeDynamicUsage();
240
+ } finally {
241
+ options.clearRequestContext();
242
+ options.setHeadersAccessPhase(previousHeadersPhase);
243
+ }
244
+ });
132
245
  } catch (error) {
133
246
  const pendingCookies = options.getAndClearPendingCookies();
134
247
  const handlerDraftCookie = options.getDraftModeCookieHeader();
@@ -160,7 +273,7 @@ async function executeAppRouteHandler(options) {
160
273
  });
161
274
  return applyDraftModeCachePolicy(applyRouteHandlerMiddlewareContext(new Response(null, { status: 500 }), options.middlewareContext, middlewareMergeOptions), shouldApplyDraftPolicy);
162
275
  } finally {
163
- options.setHeadersAccessPhase(previousHeadersPhase);
276
+ if (!cleanupDeferredToBody) options.setHeadersAccessPhase(previousHeadersPhase);
164
277
  }
165
278
  }
166
279
  //#endregion
@@ -4,6 +4,7 @@ import { RouteHandlerHttpMethod, RouteHandlerModule } from "./app-route-handler-
4
4
  type AppRouteHandlerModule = {
5
5
  dynamic?: string;
6
6
  fetchCache?: unknown;
7
+ generateStaticParams?: unknown;
7
8
  revalidate?: unknown;
8
9
  runtime?: string;
9
10
  } & RouteHandlerModule;
@@ -28,12 +29,14 @@ type AppRouteHandlerCacheReadOptions = {
28
29
  type AppRouteHandlerResponseCacheOptions = {
29
30
  dynamicConfig?: string;
30
31
  dynamicUsedInHandler: boolean;
31
- handlerSetCacheControl: boolean;
32
+ hasExplicitCacheablePolicy?: boolean;
33
+ handlerSetCachePolicy: boolean;
32
34
  isAutoHead: boolean;
33
35
  isDraftMode?: boolean;
34
36
  isProduction: boolean;
35
37
  method: string;
36
38
  revalidateSeconds: number | null;
39
+ requiresCompletedResponseAdmission?: boolean;
37
40
  };
38
41
  type AppRouteHandlerSpecialError = {
39
42
  kind: "redirect";
@@ -46,12 +49,21 @@ type AppRouteHandlerSpecialError = {
46
49
  type AppRouteHandlerSpecialErrorOptions = {
47
50
  isAction: boolean;
48
51
  };
49
- declare function getAppRouteHandlerRevalidateSeconds(handler: Pick<AppRouteHandlerModule, "revalidate">): number | null;
52
+ declare function getAppRouteHandlerRevalidateSeconds(handler: Pick<AppRouteHandlerModule, "dynamic" | "generateStaticParams" | "revalidate">): number | null;
50
53
  declare function hasAppRouteHandlerDefaultExport(handler: RouteHandlerModule): boolean;
54
+ /** Match Next.js methods that force an App Route out of static generation. */
55
+ declare function hasNonStaticAppRouteHandlerMethods(handler: RouteHandlerModule): boolean;
51
56
  declare function resolveAppRouteHandlerMethod(handler: AppRouteHandlerModule, method: string): ResolvedAppRouteHandlerMethod;
52
57
  declare function shouldReadAppRouteHandlerCache(options: AppRouteHandlerCacheReadOptions): boolean;
53
58
  declare function shouldApplyAppRouteHandlerRevalidateHeader(options: Omit<AppRouteHandlerResponseCacheOptions, "dynamicConfig" | "isProduction">): boolean;
59
+ /**
60
+ * Next.js consumes the full body before completing static generation for an
61
+ * App Route. Keep that completion boundary for every response that can still
62
+ * receive a public framework cache policy, including
63
+ * `revalidate = false` (represented by Infinity).
64
+ */
65
+ declare function shouldCompleteAppRouteHandlerResponse(options: AppRouteHandlerResponseCacheOptions): boolean;
54
66
  declare function shouldWriteAppRouteHandlerCache(options: AppRouteHandlerResponseCacheOptions): boolean;
55
67
  declare function resolveAppRouteHandlerSpecialError(error: unknown, requestUrl: string, options?: AppRouteHandlerSpecialErrorOptions): AppRouteHandlerSpecialError | null;
56
68
  //#endregion
57
- export { AppRouteHandlerModule, getAppRouteHandlerRevalidateSeconds, hasAppRouteHandlerDefaultExport, isPossibleAppRouteActionRequest, resolveAppRouteHandlerMethod, resolveAppRouteHandlerSpecialError, shouldApplyAppRouteHandlerRevalidateHeader, shouldReadAppRouteHandlerCache, shouldWriteAppRouteHandlerCache };
69
+ export { AppRouteHandlerModule, getAppRouteHandlerRevalidateSeconds, hasAppRouteHandlerDefaultExport, hasNonStaticAppRouteHandlerMethods, isPossibleAppRouteActionRequest, resolveAppRouteHandlerMethod, resolveAppRouteHandlerSpecialError, shouldApplyAppRouteHandlerRevalidateHeader, shouldCompleteAppRouteHandlerResponse, shouldReadAppRouteHandlerCache, shouldWriteAppRouteHandlerCache };
@@ -5,12 +5,18 @@ import { buildRouteHandlerAllowHeader, collectRouteHandlerMethods } from "./app-
5
5
  function getAppRouteHandlerRevalidateSeconds(handler) {
6
6
  const { revalidate } = handler;
7
7
  if (revalidate === false) return Infinity;
8
- if (typeof revalidate !== "number" || !Number.isFinite(revalidate) || revalidate < 0) return null;
9
- return revalidate;
8
+ if (typeof revalidate === "number" && Number.isFinite(revalidate) && revalidate >= 0) return revalidate;
9
+ if (revalidate !== void 0) return null;
10
+ if (handler.dynamic === "force-static" || handler.dynamic === "error" || typeof handler.generateStaticParams === "function") return Infinity;
11
+ return null;
10
12
  }
11
13
  function hasAppRouteHandlerDefaultExport(handler) {
12
14
  return typeof handler.default === "function";
13
15
  }
16
+ /** Match Next.js methods that force an App Route out of static generation. */
17
+ function hasNonStaticAppRouteHandlerMethods(handler) {
18
+ return Boolean(handler.POST || handler.PUT || handler.DELETE || handler.PATCH || handler.OPTIONS);
19
+ }
14
20
  function resolveAppRouteHandlerMethod(handler, method) {
15
21
  const exportedMethods = collectRouteHandlerMethods(handler);
16
22
  const allowHeaderForOptions = buildRouteHandlerAllowHeader(exportedMethods);
@@ -33,7 +39,16 @@ function shouldReadAppRouteHandlerCache(options) {
33
39
  return options.isProduction && options.revalidateSeconds !== null && options.revalidateSeconds > 0 && options.revalidateSeconds !== Infinity && options.dynamicConfig !== "force-dynamic" && !options.isDraftMode && !options.isKnownDynamic && (options.method === "GET" || options.isAutoHead) && typeof options.handlerFn === "function";
34
40
  }
35
41
  function shouldApplyAppRouteHandlerRevalidateHeader(options) {
36
- return options.revalidateSeconds !== null && !options.isDraftMode && !options.dynamicUsedInHandler && (options.method === "GET" || options.isAutoHead) && !options.handlerSetCacheControl;
42
+ return options.revalidateSeconds !== null && !options.isDraftMode && !options.dynamicUsedInHandler && (options.method === "GET" || options.isAutoHead) && !options.handlerSetCachePolicy;
43
+ }
44
+ /**
45
+ * Next.js consumes the full body before completing static generation for an
46
+ * App Route. Keep that completion boundary for every response that can still
47
+ * receive a public framework cache policy, including
48
+ * `revalidate = false` (represented by Infinity).
49
+ */
50
+ function shouldCompleteAppRouteHandlerResponse(options) {
51
+ return options.isProduction && (options.revalidateSeconds !== null && options.revalidateSeconds > 0 || options.requiresCompletedResponseAdmission === true || options.hasExplicitCacheablePolicy === true) && options.dynamicConfig !== "force-dynamic" && !options.isDraftMode && !options.dynamicUsedInHandler && (options.method === "GET" || options.isAutoHead);
37
52
  }
38
53
  function shouldWriteAppRouteHandlerCache(options) {
39
54
  return options.isProduction && options.revalidateSeconds !== null && options.revalidateSeconds > 0 && options.revalidateSeconds !== Infinity && options.dynamicConfig !== "force-dynamic" && !options.isDraftMode && shouldApplyAppRouteHandlerRevalidateHeader(options);
@@ -55,4 +70,4 @@ function resolveAppRouteHandlerSpecialError(error, requestUrl, options) {
55
70
  return null;
56
71
  }
57
72
  //#endregion
58
- export { getAppRouteHandlerRevalidateSeconds, hasAppRouteHandlerDefaultExport, isPossibleAppRouteActionRequest, resolveAppRouteHandlerMethod, resolveAppRouteHandlerSpecialError, shouldApplyAppRouteHandlerRevalidateHeader, shouldReadAppRouteHandlerCache, shouldWriteAppRouteHandlerCache };
73
+ export { getAppRouteHandlerRevalidateSeconds, hasAppRouteHandlerDefaultExport, hasNonStaticAppRouteHandlerMethods, isPossibleAppRouteActionRequest, resolveAppRouteHandlerMethod, resolveAppRouteHandlerSpecialError, shouldApplyAppRouteHandlerRevalidateHeader, shouldCompleteAppRouteHandlerResponse, shouldReadAppRouteHandlerCache, shouldWriteAppRouteHandlerCache };
@@ -1,6 +1,6 @@
1
- import { NEXTJS_DEPLOYMENT_ID_HEADER } from "./headers.js";
1
+ import { NEXTJS_DEPLOYMENT_ID_HEADER, VINEXT_RSC_VARY_HEADER } from "./headers.js";
2
2
  import { getDeploymentId } from "../utils/deployment-id.js";
3
- import { VINEXT_RSC_CONTENT_TYPE, VINEXT_RSC_VARY_HEADER, applyRscCompatibilityIdHeader } from "./app-rsc-cache-busting.js";
3
+ import { VINEXT_RSC_CONTENT_TYPE, applyRscCompatibilityIdHeader } from "./app-rsc-cache-busting.js";
4
4
  //#region src/server/app-route-tree-prefetch.ts
5
5
  const PARENT_INLINED_INTO_SELF = 32;
6
6
  const INLINED_INTO_CHILD = 64;
@@ -1,27 +1,65 @@
1
- import "./server-globals.js";
2
1
  import { runWithExecutionContext } from "../shims/request-context.js";
3
- import { VINEXT_PRERENDER_ROUTE_PARAMS_HEADER, VINEXT_REVALIDATE_HOST_HEADER } from "../utils/protocol-headers.js";
2
+ import "./server-globals.js";
3
+ import { VINEXT_PRERENDER_ROUTE_PARAMS_HEADER, VINEXT_PRERENDER_SECRET_HEADER, VINEXT_REVALIDATE_HOST_HEADER } from "../utils/protocol-headers.js";
4
+ import { VINEXT_CACHEABILITY_PROBE_QUERY_PARAM } from "./headers.js";
4
5
  import { badRequestResponse, notFoundResponse, notFoundStaticAssetResponse } from "./http-error-responses.js";
5
6
  import { isOpenRedirectShaped } from "./open-redirect.js";
6
7
  import { cloneRequestWithHeaders, filterInternalHeaders } from "./request-pipeline.js";
8
+ import { getCdnCacheAdapter } from "../shims/cdn-cache.js";
9
+ import { applyCdnResponseIdentityHeaders, validateCdnRequest } from "./cache-control.js";
7
10
  import { assetPrefixPathname, isNextStaticPath } from "../utils/asset-prefix.js";
8
11
  import { getImageOptimizer, handleConfiguredImageOptimization, isImageOptimizationPath } from "./image-optimization.js";
9
12
  import { createStaticAssetRequest, finalizeMissingStaticAssetResponse, resolveStaticAssetSignal } from "./worker-utils.js";
10
13
  import { readTrustedPrerenderRouteParams, serializePrerenderRouteParamsHeader } from "./prerender-route-params.js";
11
14
  import { createWorkerRevalidationContext } from "./worker-revalidation-context.js";
12
- import rscHandler, { __assetPrefix, __basePath, __imageAllowedWidths, __imageConfig } from "virtual:vinext-rsc-entry";
15
+ import { createWorkerPrerenderDiscoveryContext, createWorkerPrerenderReadinessResponse } from "./worker-prerender-discovery.js";
16
+ import rscHandler, { __assetPrefix, __basePath, __cacheabilityManifest, __imageAllowedWidths, __imageConfig, __prerenderSecret } from "virtual:vinext-rsc-entry";
13
17
  import { registerConfiguredCacheAdapters } from "virtual:vinext-cache-adapters";
14
18
  import { registerConfiguredImageOptimizer } from "virtual:vinext-image-adapters";
15
19
  //#region src/server/app-router-entry.ts
16
20
  const __workerBasePath = typeof __basePath === "string" ? __basePath : "";
17
21
  const __workerAssetPathPrefix = assetPrefixPathname(typeof __assetPrefix === "string" ? __assetPrefix : "");
22
+ function isPotentialCompletedAdmissionRequest(request) {
23
+ if (request.method !== "GET" && request.method !== "HEAD") return false;
24
+ if (request.headers.has("next-action") || request.headers.has("x-rsc-action")) return false;
25
+ return true;
26
+ }
18
27
  var app_router_entry_default = { async fetch(request, env, ctx) {
19
- return handleRequest(request, env, ctx);
28
+ return applyCdnResponseIdentityHeaders(await handleRequest(request, env, ctx), request);
20
29
  } };
21
30
  async function handleRequest(request, env, platformCtx) {
22
- const ctx = platformCtx?.trustedRevalidateOrigin ? platformCtx : createWorkerRevalidationContext(platformCtx, (internalRequest, internalCtx) => handleRequest(internalRequest, env, internalCtx));
31
+ const requestCtx = platformCtx?.trustedRevalidateOrigin ? platformCtx : createWorkerRevalidationContext(platformCtx, (internalRequest, internalCtx) => handleRequest(internalRequest, env, internalCtx));
23
32
  registerConfiguredCacheAdapters(env);
33
+ const cdnCacheAdapter = getCdnCacheAdapter();
34
+ let ctx = createWorkerPrerenderDiscoveryContext(requestCtx, request, __prerenderSecret);
35
+ const readinessResponse = createWorkerPrerenderReadinessResponse(ctx, request);
36
+ if (readinessResponse) return await validateCdnRequest(request) ?? readinessResponse;
37
+ let finalizeCacheabilityResponse;
38
+ if (request.headers.has("X-Vinext-Cacheability-Probe")) {
39
+ const cacheability = await import("./cacheability-request.js");
40
+ const probeContext = cacheability.createWorkerCacheabilityContext(ctx, request, __prerenderSecret, cdnCacheAdapter.responseVary);
41
+ if (probeContext !== ctx) {
42
+ ctx = probeContext;
43
+ finalizeCacheabilityResponse = cacheability.finalizeWorkerCacheabilityResponse;
44
+ const probeUrl = new URL(request.url);
45
+ if (probeUrl.searchParams.has("__vinext_cacheability_probe")) {
46
+ probeUrl.searchParams.delete(VINEXT_CACHEABILITY_PROBE_QUERY_PARAM);
47
+ request = new Request(probeUrl, request);
48
+ }
49
+ }
50
+ }
51
+ const requiresCompletedResponseAdmission = cdnCacheAdapter.requiresCompletedResponseAdmission === true;
52
+ if (!finalizeCacheabilityResponse && (__cacheabilityManifest || requiresCompletedResponseAdmission) && isPotentialCompletedAdmissionRequest(request)) {
53
+ const cacheability = await import("./cacheability-request.js");
54
+ const admissionContext = cacheability.createWorkerCacheabilityAdmissionContext(ctx, request, __cacheabilityManifest, process.env.__VINEXT_BUILD_ID, requiresCompletedResponseAdmission, cdnCacheAdapter.responseVary);
55
+ if (admissionContext !== ctx) {
56
+ ctx = admissionContext;
57
+ finalizeCacheabilityResponse = cacheability.finalizeWorkerCacheabilityResponse;
58
+ }
59
+ }
24
60
  registerConfiguredImageOptimizer(env);
61
+ const cdnValidationResponse = await validateCdnRequest(request);
62
+ if (cdnValidationResponse) return cdnValidationResponse;
25
63
  const url = new URL(request.url);
26
64
  if (isImageOptimizationPath(url.pathname) && env?.ASSETS && getImageOptimizer()) {
27
65
  const assetFetcher = env.ASSETS;
@@ -37,6 +75,7 @@ async function handleRequest(request, env, platformCtx) {
37
75
  {
38
76
  const trustedPrerenderRouteParams = ctx.hostRuntime === "node" ? readTrustedPrerenderRouteParams(request) : null;
39
77
  const filteredHeaders = ctx.isInternalPagesRevalidation ? new Headers(request.headers) : filterInternalHeaders(request.headers);
78
+ filteredHeaders.delete(VINEXT_PRERENDER_SECRET_HEADER);
40
79
  filteredHeaders.delete(VINEXT_REVALIDATE_HOST_HEADER);
41
80
  const prerenderRouteParamsHeader = serializePrerenderRouteParamsHeader(trustedPrerenderRouteParams);
42
81
  if (prerenderRouteParamsHeader !== null) filteredHeaders.set(VINEXT_PRERENDER_ROUTE_PARAMS_HEADER, prerenderRouteParamsHeader);
@@ -51,10 +90,15 @@ async function handleRequest(request, env, platformCtx) {
51
90
  const assetResponse = await resolveStaticAssetSignal(response, { fetchAsset: (path) => Promise.resolve(assetFetcher.fetch(createStaticAssetRequest(path, request))) });
52
91
  if (assetResponse) response = assetResponse;
53
92
  }
54
- return finalizeMissingStaticAssetResponse(response, missingBuildAsset);
93
+ response = finalizeMissingStaticAssetResponse(response, missingBuildAsset);
94
+ return finalizeCacheabilityResponse ? finalizeCacheabilityResponse(response, ctx) : response;
95
+ }
96
+ if (result === null || result === void 0) {
97
+ const response = missingBuildAsset ? notFoundStaticAssetResponse() : notFoundResponse();
98
+ return finalizeCacheabilityResponse ? finalizeCacheabilityResponse(response, ctx) : response;
55
99
  }
56
- if (result === null || result === void 0) return missingBuildAsset ? notFoundStaticAssetResponse() : notFoundResponse();
57
- return new Response(String(result), { status: 200 });
100
+ const response = new Response(String(result), { status: 200 });
101
+ return finalizeCacheabilityResponse ? finalizeCacheabilityResponse(response, ctx) : response;
58
102
  }
59
103
  //#endregion
60
104
  export { app_router_entry_default as default };
@@ -1,4 +1,4 @@
1
- import { VINEXT_RSC_RENDER_MODE_HEADER } from "./headers.js";
1
+ import { VINEXT_RSC_RENDER_MODE_HEADER, VINEXT_RSC_VARY_HEADER } from "./headers.js";
2
2
  import { AppRscRenderMode } from "./app-rsc-render-mode.js";
3
3
  import { AppRscStateFingerprintInput } from "./app-rsc-state-fingerprint.js";
4
4
  //#region src/server/app-rsc-cache-busting.d.ts
@@ -9,9 +9,9 @@ import { AppRscStateFingerprintInput } from "./app-rsc-state-fingerprint.js";
9
9
  * repeated canonicalization redirects.
10
10
  */
11
11
  declare const VINEXT_RSC_CACHE_BUSTING_SEARCH_PARAM = "_rsc";
12
+ declare const VINEXT_RSC_BUILD_ID_HEADER = "X-Vinext-RSC-Build-Id";
12
13
  declare const VINEXT_RSC_COMPATIBILITY_ID_HEADER = "X-Vinext-RSC-Compatibility-Id";
13
14
  declare const VINEXT_RSC_CONTENT_TYPE = "text/x-component";
14
- declare const VINEXT_RSC_VARY_HEADER: string;
15
15
  type CreateRscRequestHeadersOptions = {
16
16
  clientReuseManifestHeader?: string | null;
17
17
  interceptionContext?: string | null;
@@ -26,13 +26,14 @@ type CreateRscRequestHeadersOptions = {
26
26
  routeId: string;
27
27
  } | null;
28
28
  routerState?: AppRscStateFingerprintInput | null;
29
+ deploymentId?: string | null;
29
30
  };
30
31
  type ResolveInvalidRscCacheBustingRequestOptions = {
31
32
  isRscRequest: boolean;
32
33
  request: Request;
33
34
  };
34
35
  declare function getVinextRscCompatibilityId(): string | null;
35
- declare function applyRscCompatibilityIdHeader(headers: Headers, compatibilityId?: string | null | undefined): void;
36
+ declare function applyRscCompatibilityIdHeader(headers: Headers, compatibilityId?: string | null | undefined, buildId?: string | null | undefined): void;
36
37
  declare function applyRscDeploymentIdHeader(headers: Headers): void;
37
38
  declare function isRscCompatibilityIdCompatible(responseCompatibilityId: string | null | undefined, clientCompatibilityId?: string | null | undefined): boolean;
38
39
  type RscCompatibilityNavigationDecision = {
@@ -65,10 +66,27 @@ declare function stripRscCacheBustingSearchParam(url: URL): void;
65
66
  * unchanged when the suffix is absent.
66
67
  */
67
68
  declare function stripRscSuffix(pathname: string): string;
69
+ declare function createCanonicalRscRequestHeaders(deploymentId?: string | null | undefined): Headers;
70
+ /** Headers for the deterministic loading-boundary prefetch representation. */
71
+ declare function createCanonicalLoadingShellRscRequestHeaders(deploymentId?: string | null | undefined): Headers;
68
72
  declare function createRscRequestHeaders(options?: CreateRscRequestHeadersOptions): Headers;
73
+ /**
74
+ * Convert a full, non-contextual navigation request to the definitive ISR RSC
75
+ * variant. Partial/intercepted/mounted-slot payloads remain contextual because
76
+ * their bytes can legitimately differ from the destination's full route.
77
+ */
78
+ declare function canonicalizePrewarmableRscRequestHeaders(headers: Headers): boolean;
79
+ /**
80
+ * Convert an ordinary loading-boundary prefetch to the shared loading-shell
81
+ * variant. The payload is selected by the three retained mode headers; visible
82
+ * router state is transport context and must not fragment the shared response.
83
+ */
84
+ declare function canonicalizeLoadingShellRscRequestHeaders(headers: Headers): boolean;
69
85
  declare function createRscRequestUrl(href: string, headers: Headers): Promise<string>;
86
+ /** Build the definitive full-route RSC URL shared by prefetch and navigation. */
87
+ declare function createCanonicalRscRequestUrl(href: string): string;
70
88
  declare function createServerActionRequestUrl(href: string): string;
71
89
  declare function createRscRedirectLocation(location: string, request: Request): Promise<string>;
72
90
  declare function resolveInvalidRscCacheBustingRequest(options: ResolveInvalidRscCacheBustingRequestOptions): Promise<Response | null>;
73
91
  //#endregion
74
- export { VINEXT_RSC_CACHE_BUSTING_SEARCH_PARAM, VINEXT_RSC_COMPATIBILITY_ID_HEADER, VINEXT_RSC_CONTENT_TYPE, VINEXT_RSC_RENDER_MODE_HEADER, VINEXT_RSC_VARY_HEADER, applyRscCompatibilityIdHeader, applyRscDeploymentIdHeader, computeRscCacheBustingSearchParam, createRscRedirectLocation, createRscRequestHeaders, createRscRequestUrl, createServerActionRequestUrl, getVinextRscCompatibilityId, hasRscCacheBustingSearchParam, isRscCompatibilityIdCompatible, resolveHardNavigationTargetFromRscResponse, resolveInvalidRscCacheBustingRequest, resolveRscCompatibilityNavigationDecision, setRscCacheBustingSearchParam, stripRscCacheBustingSearchParam, stripRscSuffix };
92
+ export { VINEXT_RSC_BUILD_ID_HEADER, VINEXT_RSC_CACHE_BUSTING_SEARCH_PARAM, VINEXT_RSC_COMPATIBILITY_ID_HEADER, VINEXT_RSC_CONTENT_TYPE, VINEXT_RSC_RENDER_MODE_HEADER, VINEXT_RSC_VARY_HEADER, applyRscCompatibilityIdHeader, applyRscDeploymentIdHeader, canonicalizeLoadingShellRscRequestHeaders, canonicalizePrewarmableRscRequestHeaders, computeRscCacheBustingSearchParam, createCanonicalLoadingShellRscRequestHeaders, createCanonicalRscRequestHeaders, createCanonicalRscRequestUrl, createRscRedirectLocation, createRscRequestHeaders, createRscRequestUrl, createServerActionRequestUrl, getVinextRscCompatibilityId, hasRscCacheBustingSearchParam, isRscCompatibilityIdCompatible, resolveHardNavigationTargetFromRscResponse, resolveInvalidRscCacheBustingRequest, resolveRscCompatibilityNavigationDecision, setRscCacheBustingSearchParam, stripRscCacheBustingSearchParam, stripRscSuffix };
@@ -1,6 +1,6 @@
1
- import { NEXTJS_DEPLOYMENT_ID_HEADER, NEXT_ROUTER_PREFETCH_HEADER, NEXT_ROUTER_SEGMENT_PREFETCH_HEADER, NEXT_ROUTER_STATE_TREE_HEADER, NEXT_URL_HEADER, VINEXT_CLIENT_REUSE_MANIFEST_HEADER, VINEXT_INTERCEPTION_CONTEXT_HEADER, VINEXT_INTERCEPTION_ID_HEADER, VINEXT_MOUNTED_SLOTS_HEADER, VINEXT_RSC_RENDER_MODE_HEADER, VINEXT_RSC_STATE_FINGERPRINT_HEADER } from "./headers.js";
1
+ import { NEXTJS_DEPLOYMENT_ID_HEADER, NEXT_ROUTER_PREFETCH_HEADER, NEXT_ROUTER_SEGMENT_PREFETCH_HEADER, NEXT_ROUTER_STATE_TREE_HEADER, NEXT_URL_HEADER, VINEXT_CLIENT_REUSE_MANIFEST_HEADER, VINEXT_INTERCEPTION_CONTEXT_HEADER, VINEXT_INTERCEPTION_ID_HEADER, VINEXT_MOUNTED_SLOTS_HEADER, VINEXT_RSC_RENDER_MODE_HEADER, VINEXT_RSC_STATE_FINGERPRINT_HEADER, VINEXT_RSC_VARY_HEADER } from "./headers.js";
2
2
  import { fnv1a64 } from "../utils/hash.js";
3
- import { parseAppRscRenderMode } from "./app-rsc-render-mode.js";
3
+ import { APP_RSC_RENDER_MODE_PREFETCH_LOADING_SHELL, parseAppRscRenderMode } from "./app-rsc-render-mode.js";
4
4
  import { applyDeploymentIdHeader, getDeploymentId } from "../utils/deployment-id.js";
5
5
  import { createAppRscStateFingerprint } from "./app-rsc-state-fingerprint.js";
6
6
  //#region src/server/app-rsc-cache-busting.ts
@@ -11,20 +11,9 @@ import { createAppRscStateFingerprint } from "./app-rsc-state-fingerprint.js";
11
11
  * repeated canonicalization redirects.
12
12
  */
13
13
  const VINEXT_RSC_CACHE_BUSTING_SEARCH_PARAM = "_rsc";
14
+ const VINEXT_RSC_BUILD_ID_HEADER = "X-Vinext-RSC-Build-Id";
14
15
  const VINEXT_RSC_COMPATIBILITY_ID_HEADER = "X-Vinext-RSC-Compatibility-Id";
15
16
  const VINEXT_RSC_CONTENT_TYPE = "text/x-component";
16
- const VINEXT_RSC_VARY_HEADER = [
17
- "RSC",
18
- NEXT_ROUTER_STATE_TREE_HEADER,
19
- NEXT_ROUTER_PREFETCH_HEADER,
20
- NEXT_ROUTER_SEGMENT_PREFETCH_HEADER,
21
- NEXT_URL_HEADER,
22
- VINEXT_INTERCEPTION_CONTEXT_HEADER,
23
- VINEXT_INTERCEPTION_ID_HEADER,
24
- VINEXT_MOUNTED_SLOTS_HEADER,
25
- VINEXT_RSC_RENDER_MODE_HEADER,
26
- VINEXT_RSC_STATE_FINGERPRINT_HEADER
27
- ].join(", ");
28
17
  const CACHE_BUSTING_DIGEST_BYTES = 12;
29
18
  const textEncoder = new TextEncoder();
30
19
  function encodeBase64Url(bytes) {
@@ -41,10 +30,16 @@ function normalizeCompatibilityId(value) {
41
30
  function getVinextRscCompatibilityId() {
42
31
  return normalizeCompatibilityId(process.env.__VINEXT_RSC_COMPATIBILITY_ID);
43
32
  }
44
- function applyRscCompatibilityIdHeader(headers, compatibilityId = getVinextRscCompatibilityId()) {
33
+ function getVinextRscBuildId() {
34
+ return normalizeCompatibilityId(process.env.__VINEXT_RSC_BUILD_IDENTITY);
35
+ }
36
+ function applyRscCompatibilityIdHeader(headers, compatibilityId = getVinextRscCompatibilityId(), buildId = getVinextRscBuildId()) {
45
37
  const normalized = normalizeCompatibilityId(compatibilityId);
46
38
  if (normalized) headers.set(VINEXT_RSC_COMPATIBILITY_ID_HEADER, normalized);
47
39
  else headers.delete(VINEXT_RSC_COMPATIBILITY_ID_HEADER);
40
+ const normalizedBuildId = normalizeCompatibilityId(buildId);
41
+ if (normalizedBuildId) headers.set(VINEXT_RSC_BUILD_ID_HEADER, normalizedBuildId);
42
+ else headers.delete(VINEXT_RSC_BUILD_ID_HEADER);
48
43
  }
49
44
  function applyRscDeploymentIdHeader(headers) {
50
45
  const deploymentId = getDeploymentId();
@@ -147,12 +142,24 @@ function stripRscCacheBustingSearchParam(url) {
147
142
  function stripRscSuffix(pathname) {
148
143
  return pathname.endsWith(".rsc") ? pathname.slice(0, -4) : pathname;
149
144
  }
150
- function createRscRequestHeaders(options = {}) {
145
+ function createCanonicalRscRequestHeaders(deploymentId = getDeploymentId()) {
151
146
  const headers = new Headers({
152
147
  Accept: VINEXT_RSC_CONTENT_TYPE,
153
148
  ["RSC"]: "1"
154
149
  });
155
- applyDeploymentIdHeader(headers);
150
+ applyDeploymentIdHeader(headers, deploymentId ?? void 0);
151
+ return headers;
152
+ }
153
+ /** Headers for the deterministic loading-boundary prefetch representation. */
154
+ function createCanonicalLoadingShellRscRequestHeaders(deploymentId = getDeploymentId()) {
155
+ const headers = createCanonicalRscRequestHeaders(deploymentId);
156
+ headers.set(NEXT_ROUTER_PREFETCH_HEADER, "1");
157
+ headers.set(NEXT_ROUTER_SEGMENT_PREFETCH_HEADER, "1");
158
+ headers.set(VINEXT_RSC_RENDER_MODE_HEADER, APP_RSC_RENDER_MODE_PREFETCH_LOADING_SHELL);
159
+ return headers;
160
+ }
161
+ function createRscRequestHeaders(options = {}) {
162
+ const headers = createCanonicalRscRequestHeaders(options.deploymentId);
156
163
  if (options.prefetchRouterState) {
157
164
  if (options.includePrefetchHeader !== false) headers.set(NEXT_ROUTER_PREFETCH_HEADER, "1");
158
165
  headers.set(NEXT_ROUTER_STATE_TREE_HEADER, encodeURIComponent(JSON.stringify(options.prefetchRouterState)));
@@ -169,15 +176,58 @@ function createRscRequestHeaders(options = {}) {
169
176
  if (renderMode !== "navigation") headers.set(VINEXT_RSC_RENDER_MODE_HEADER, renderMode);
170
177
  return headers;
171
178
  }
179
+ /**
180
+ * Convert a full, non-contextual navigation request to the definitive ISR RSC
181
+ * variant. Partial/intercepted/mounted-slot payloads remain contextual because
182
+ * their bytes can legitimately differ from the destination's full route.
183
+ */
184
+ function canonicalizePrewarmableRscRequestHeaders(headers) {
185
+ if (headers.has("X-Vinext-Rsc-Render-Mode") || headers.has("X-Vinext-Interception-Context") || headers.has("X-Vinext-Interception-Id") || headers.has("X-Vinext-Mounted-Slots")) return false;
186
+ headers.delete(NEXT_ROUTER_PREFETCH_HEADER);
187
+ headers.delete(NEXT_ROUTER_SEGMENT_PREFETCH_HEADER);
188
+ headers.delete(NEXT_ROUTER_STATE_TREE_HEADER);
189
+ headers.delete(NEXT_URL_HEADER);
190
+ headers.delete(VINEXT_RSC_STATE_FINGERPRINT_HEADER);
191
+ headers.delete(VINEXT_CLIENT_REUSE_MANIFEST_HEADER);
192
+ return true;
193
+ }
194
+ /**
195
+ * Convert an ordinary loading-boundary prefetch to the shared loading-shell
196
+ * variant. The payload is selected by the three retained mode headers; visible
197
+ * router state is transport context and must not fragment the shared response.
198
+ */
199
+ function canonicalizeLoadingShellRscRequestHeaders(headers) {
200
+ if (headers.get("X-Vinext-Rsc-Render-Mode") !== "prefetch-loading-shell" || headers.has("X-Vinext-Interception-Context") || headers.has("X-Vinext-Interception-Id") || headers.has("X-Vinext-Mounted-Slots")) return false;
201
+ headers.set(NEXT_ROUTER_PREFETCH_HEADER, "1");
202
+ headers.set(NEXT_ROUTER_SEGMENT_PREFETCH_HEADER, "1");
203
+ headers.delete(NEXT_ROUTER_STATE_TREE_HEADER);
204
+ headers.delete(NEXT_URL_HEADER);
205
+ headers.delete(VINEXT_RSC_STATE_FINGERPRINT_HEADER);
206
+ headers.delete(VINEXT_CLIENT_REUSE_MANIFEST_HEADER);
207
+ return true;
208
+ }
172
209
  function toRscRequestPath(href) {
173
210
  const hashIndex = href.indexOf("#");
174
211
  return hashIndex === -1 ? href : href.slice(0, hashIndex);
175
212
  }
176
213
  async function createRscRequestUrl(href, headers) {
177
214
  const url = new URL(toRscRequestPath(href), "http://vinext.local");
215
+ if (typeof window !== "undefined" && process.env.NODE_ENV === "production" && process.env.__NEXT_CONFIG_OUTPUT === "export") {
216
+ const basePath = process.env.__NEXT_ROUTER_BASEPATH ?? "";
217
+ const isBasePathRoot = basePath !== "" && url.pathname === basePath;
218
+ if (url.pathname.endsWith("/")) url.pathname += "index.txt";
219
+ else url.pathname += isBasePathRoot ? "/index.txt" : ".txt";
220
+ return `${url.pathname}${url.search}`;
221
+ }
178
222
  setRscCacheBustingSearchParam(url, await computeRscCacheBustingSearchParam(headers));
179
223
  return `${url.pathname}${url.search}`;
180
224
  }
225
+ /** Build the definitive full-route RSC URL shared by prefetch and navigation. */
226
+ function createCanonicalRscRequestUrl(href) {
227
+ const url = new URL(toRscRequestPath(href), "http://vinext.local");
228
+ setRscCacheBustingSearchParam(url, "");
229
+ return `${url.pathname}${url.search}`;
230
+ }
181
231
  function createServerActionRequestUrl(href) {
182
232
  const hashIndex = href.indexOf("#");
183
233
  const beforeHash = hashIndex === -1 ? href : href.slice(0, hashIndex);
@@ -247,4 +297,4 @@ async function resolveInvalidRscCacheBustingRequest(options) {
247
297
  });
248
298
  }
249
299
  //#endregion
250
- export { VINEXT_RSC_CACHE_BUSTING_SEARCH_PARAM, VINEXT_RSC_COMPATIBILITY_ID_HEADER, VINEXT_RSC_CONTENT_TYPE, VINEXT_RSC_RENDER_MODE_HEADER, VINEXT_RSC_VARY_HEADER, applyRscCompatibilityIdHeader, applyRscDeploymentIdHeader, computeRscCacheBustingSearchParam, createRscRedirectLocation, createRscRequestHeaders, createRscRequestUrl, createServerActionRequestUrl, getVinextRscCompatibilityId, hasRscCacheBustingSearchParam, isRscCompatibilityIdCompatible, resolveHardNavigationTargetFromRscResponse, resolveInvalidRscCacheBustingRequest, resolveRscCompatibilityNavigationDecision, setRscCacheBustingSearchParam, stripRscCacheBustingSearchParam, stripRscSuffix };
300
+ export { VINEXT_RSC_BUILD_ID_HEADER, VINEXT_RSC_CACHE_BUSTING_SEARCH_PARAM, VINEXT_RSC_COMPATIBILITY_ID_HEADER, VINEXT_RSC_CONTENT_TYPE, VINEXT_RSC_RENDER_MODE_HEADER, VINEXT_RSC_VARY_HEADER, applyRscCompatibilityIdHeader, applyRscDeploymentIdHeader, canonicalizeLoadingShellRscRequestHeaders, canonicalizePrewarmableRscRequestHeaders, computeRscCacheBustingSearchParam, createCanonicalLoadingShellRscRequestHeaders, createCanonicalRscRequestHeaders, createCanonicalRscRequestUrl, createRscRedirectLocation, createRscRequestHeaders, createRscRequestUrl, createServerActionRequestUrl, getVinextRscCompatibilityId, hasRscCacheBustingSearchParam, isRscCompatibilityIdCompatible, resolveHardNavigationTargetFromRscResponse, resolveInvalidRscCacheBustingRequest, resolveRscCompatibilityNavigationDecision, setRscCacheBustingSearchParam, stripRscCacheBustingSearchParam, stripRscSuffix };
@@ -38,10 +38,12 @@ type AppRscHandlerRoute = {
38
38
  slots?: AppRouteTreePrefetchRoute["slots"];
39
39
  };
40
40
  type AppRscRouteMatch<TRoute> = {
41
+ interceptionSourceIsConcrete?: boolean;
41
42
  params: AppPageParams;
42
43
  route: TRoute;
43
44
  };
44
45
  type DispatchMatchedPageOptions<TRoute> = {
46
+ bypassInterceptionContextCache: boolean;
45
47
  clientReuseManifest: ClientReuseManifestParseResult;
46
48
  cleanPathname: string;
47
49
  displayPathname: string;