vinext 0.0.53 → 0.0.54

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 (188) hide show
  1. package/dist/build/inline-css.d.ts +7 -0
  2. package/dist/build/inline-css.js +50 -0
  3. package/dist/build/inline-css.js.map +1 -0
  4. package/dist/build/prerender.js +2 -1
  5. package/dist/build/prerender.js.map +1 -1
  6. package/dist/check.js +4 -0
  7. package/dist/check.js.map +1 -1
  8. package/dist/client/navigation-runtime.d.ts +2 -1
  9. package/dist/client/navigation-runtime.js.map +1 -1
  10. package/dist/client/window-next.d.ts +7 -0
  11. package/dist/client/window-next.js.map +1 -1
  12. package/dist/config/next-config.d.ts +83 -1
  13. package/dist/config/next-config.js +131 -2
  14. package/dist/config/next-config.js.map +1 -1
  15. package/dist/deploy.js +13 -0
  16. package/dist/deploy.js.map +1 -1
  17. package/dist/entries/app-browser-entry.d.ts +11 -1
  18. package/dist/entries/app-browser-entry.js +16 -6
  19. package/dist/entries/app-browser-entry.js.map +1 -1
  20. package/dist/entries/app-rsc-entry.d.ts +8 -1
  21. package/dist/entries/app-rsc-entry.js +18 -5
  22. package/dist/entries/app-rsc-entry.js.map +1 -1
  23. package/dist/entries/app-rsc-manifest.d.ts +21 -1
  24. package/dist/entries/app-rsc-manifest.js +6 -4
  25. package/dist/entries/app-rsc-manifest.js.map +1 -1
  26. package/dist/entries/pages-client-entry.d.ts +4 -1
  27. package/dist/entries/pages-client-entry.js +18 -2
  28. package/dist/entries/pages-client-entry.js.map +1 -1
  29. package/dist/entries/pages-server-entry.js +82 -4
  30. package/dist/entries/pages-server-entry.js.map +1 -1
  31. package/dist/entries/runtime-entry-module.d.ts +1 -10
  32. package/dist/entries/runtime-entry-module.js +2 -12
  33. package/dist/entries/runtime-entry-module.js.map +1 -1
  34. package/dist/index.js +63 -5
  35. package/dist/index.js.map +1 -1
  36. package/dist/plugins/remove-console.d.ts +16 -0
  37. package/dist/plugins/remove-console.js +176 -0
  38. package/dist/plugins/remove-console.js.map +1 -0
  39. package/dist/routing/app-route-graph.d.ts +24 -1
  40. package/dist/routing/app-route-graph.js +52 -4
  41. package/dist/routing/app-route-graph.js.map +1 -1
  42. package/dist/routing/app-router.d.ts +2 -2
  43. package/dist/routing/app-router.js +2 -2
  44. package/dist/routing/app-router.js.map +1 -1
  45. package/dist/routing/file-matcher.d.ts +21 -1
  46. package/dist/routing/file-matcher.js +39 -1
  47. package/dist/routing/file-matcher.js.map +1 -1
  48. package/dist/routing/pages-router.d.ts +1 -1
  49. package/dist/routing/pages-router.js +10 -3
  50. package/dist/routing/pages-router.js.map +1 -1
  51. package/dist/server/api-handler.js +1 -1
  52. package/dist/server/app-browser-entry.js +25 -16
  53. package/dist/server/app-browser-entry.js.map +1 -1
  54. package/dist/server/app-browser-navigation-controller.d.ts +2 -0
  55. package/dist/server/app-browser-navigation-controller.js +4 -0
  56. package/dist/server/app-browser-navigation-controller.js.map +1 -1
  57. package/dist/server/app-elements-wire.d.ts +13 -4
  58. package/dist/server/app-elements-wire.js +10 -1
  59. package/dist/server/app-elements-wire.js.map +1 -1
  60. package/dist/server/app-elements.d.ts +2 -2
  61. package/dist/server/app-elements.js +2 -2
  62. package/dist/server/app-elements.js.map +1 -1
  63. package/dist/server/app-fallback-renderer.d.ts +15 -5
  64. package/dist/server/app-fallback-renderer.js +10 -4
  65. package/dist/server/app-fallback-renderer.js.map +1 -1
  66. package/dist/server/app-inline-css-client.d.ts +7 -0
  67. package/dist/server/app-inline-css-client.js +37 -0
  68. package/dist/server/app-inline-css-client.js.map +1 -0
  69. package/dist/server/app-page-boundary.d.ts +21 -1
  70. package/dist/server/app-page-boundary.js +28 -3
  71. package/dist/server/app-page-boundary.js.map +1 -1
  72. package/dist/server/app-page-cache.d.ts +7 -3
  73. package/dist/server/app-page-cache.js +7 -7
  74. package/dist/server/app-page-cache.js.map +1 -1
  75. package/dist/server/app-page-dispatch.d.ts +10 -1
  76. package/dist/server/app-page-dispatch.js +126 -79
  77. package/dist/server/app-page-dispatch.js.map +1 -1
  78. package/dist/server/app-page-element-builder.js +12 -28
  79. package/dist/server/app-page-element-builder.js.map +1 -1
  80. package/dist/server/app-page-render-identity.d.ts +22 -0
  81. package/dist/server/app-page-render-identity.js +42 -0
  82. package/dist/server/app-page-render-identity.js.map +1 -0
  83. package/dist/server/app-page-render.d.ts +8 -1
  84. package/dist/server/app-page-render.js +4 -1
  85. package/dist/server/app-page-render.js.map +1 -1
  86. package/dist/server/app-page-request.d.ts +6 -3
  87. package/dist/server/app-page-request.js +5 -2
  88. package/dist/server/app-page-request.js.map +1 -1
  89. package/dist/server/app-page-response.js +2 -2
  90. package/dist/server/app-page-response.js.map +1 -1
  91. package/dist/server/app-page-route-wiring.d.ts +15 -0
  92. package/dist/server/app-page-route-wiring.js +7 -5
  93. package/dist/server/app-page-route-wiring.js.map +1 -1
  94. package/dist/server/app-page-stream.d.ts +11 -0
  95. package/dist/server/app-page-stream.js +1 -0
  96. package/dist/server/app-page-stream.js.map +1 -1
  97. package/dist/server/app-route-handler-response.js +37 -5
  98. package/dist/server/app-route-handler-response.js.map +1 -1
  99. package/dist/server/app-rsc-handler.d.ts +14 -3
  100. package/dist/server/app-rsc-handler.js +45 -5
  101. package/dist/server/app-rsc-handler.js.map +1 -1
  102. package/dist/server/app-rsc-request-normalization.d.ts +2 -1
  103. package/dist/server/app-rsc-request-normalization.js +3 -2
  104. package/dist/server/app-rsc-request-normalization.js.map +1 -1
  105. package/dist/server/app-server-action-execution.d.ts +21 -3
  106. package/dist/server/app-server-action-execution.js +42 -7
  107. package/dist/server/app-server-action-execution.js.map +1 -1
  108. package/dist/server/app-ssr-entry.d.ts +6 -0
  109. package/dist/server/app-ssr-entry.js +22 -7
  110. package/dist/server/app-ssr-entry.js.map +1 -1
  111. package/dist/server/app-ssr-error-meta.js +3 -3
  112. package/dist/server/app-ssr-error-meta.js.map +1 -1
  113. package/dist/server/app-ssr-stream.d.ts +2 -1
  114. package/dist/server/app-ssr-stream.js +176 -31
  115. package/dist/server/app-ssr-stream.js.map +1 -1
  116. package/dist/server/client-trace-metadata.d.ts +31 -0
  117. package/dist/server/client-trace-metadata.js +83 -0
  118. package/dist/server/client-trace-metadata.js.map +1 -0
  119. package/dist/server/cookie-utils.d.ts +13 -0
  120. package/dist/server/cookie-utils.js +20 -0
  121. package/dist/server/cookie-utils.js.map +1 -0
  122. package/dist/server/dev-server.d.ts +8 -1
  123. package/dist/server/dev-server.js +34 -5
  124. package/dist/server/dev-server.js.map +1 -1
  125. package/dist/server/html.d.ts +2 -1
  126. package/dist/server/html.js +6 -1
  127. package/dist/server/html.js.map +1 -1
  128. package/dist/server/isr-cache.d.ts +7 -5
  129. package/dist/server/isr-cache.js +17 -6
  130. package/dist/server/isr-cache.js.map +1 -1
  131. package/dist/server/middleware-runtime.js +1 -2
  132. package/dist/server/middleware-runtime.js.map +1 -1
  133. package/dist/server/pages-document-initial-props.d.ts +7 -0
  134. package/dist/server/pages-document-initial-props.js +14 -0
  135. package/dist/server/pages-document-initial-props.js.map +1 -0
  136. package/dist/server/pages-page-data.js +3 -0
  137. package/dist/server/pages-page-data.js.map +1 -1
  138. package/dist/server/pages-page-method.d.ts +48 -0
  139. package/dist/server/pages-page-method.js +19 -0
  140. package/dist/server/pages-page-method.js.map +1 -0
  141. package/dist/server/pages-page-response.d.ts +6 -0
  142. package/dist/server/pages-page-response.js +10 -3
  143. package/dist/server/pages-page-response.js.map +1 -1
  144. package/dist/server/pages-serializable-props.d.ts +25 -0
  145. package/dist/server/pages-serializable-props.js +69 -0
  146. package/dist/server/pages-serializable-props.js.map +1 -0
  147. package/dist/server/prod-server.js +3 -0
  148. package/dist/server/prod-server.js.map +1 -1
  149. package/dist/server/server-action-not-found.js +3 -2
  150. package/dist/server/server-action-not-found.js.map +1 -1
  151. package/dist/server/static-file-cache.js +2 -1
  152. package/dist/server/static-file-cache.js.map +1 -1
  153. package/dist/shims/app-router-scroll-state.d.ts +4 -2
  154. package/dist/shims/app-router-scroll-state.js +16 -3
  155. package/dist/shims/app-router-scroll-state.js.map +1 -1
  156. package/dist/shims/app-router-scroll.d.ts +16 -2
  157. package/dist/shims/app-router-scroll.js +18 -3
  158. package/dist/shims/app-router-scroll.js.map +1 -1
  159. package/dist/shims/cache.d.ts +6 -0
  160. package/dist/shims/cache.js +7 -0
  161. package/dist/shims/cache.js.map +1 -1
  162. package/dist/shims/error.js +3 -0
  163. package/dist/shims/error.js.map +1 -1
  164. package/dist/shims/headers.d.ts +7 -0
  165. package/dist/shims/headers.js +9 -1
  166. package/dist/shims/headers.js.map +1 -1
  167. package/dist/shims/internal/app-route-detection.d.ts +37 -0
  168. package/dist/shims/internal/app-route-detection.js +69 -0
  169. package/dist/shims/internal/app-route-detection.js.map +1 -0
  170. package/dist/shims/link.d.ts +18 -2
  171. package/dist/shims/link.js +70 -6
  172. package/dist/shims/link.js.map +1 -1
  173. package/dist/shims/metadata.d.ts +7 -6
  174. package/dist/shims/metadata.js +9 -5
  175. package/dist/shims/metadata.js.map +1 -1
  176. package/dist/shims/navigation.d.ts +1 -2
  177. package/dist/shims/navigation.js +63 -12
  178. package/dist/shims/navigation.js.map +1 -1
  179. package/dist/shims/router.d.ts +5 -0
  180. package/dist/shims/router.js +14 -4
  181. package/dist/shims/router.js.map +1 -1
  182. package/dist/shims/script.d.ts +11 -1
  183. package/dist/shims/script.js +75 -6
  184. package/dist/shims/script.js.map +1 -1
  185. package/dist/utils/path.d.ts +13 -0
  186. package/dist/utils/path.js +16 -0
  187. package/dist/utils/path.js.map +1 -0
  188. package/package.json +1 -1
@@ -1,7 +1,7 @@
1
1
  import { createRequestContext, runWithRequestContext } from "../shims/unified-request-context.js";
2
2
  import { hasBasePath } from "../utils/base-path.js";
3
3
  import { getRequestExecutionContext } from "../shims/request-context.js";
4
- import { VINEXT_MW_CTX_HEADER, VINEXT_PRERENDER_ROUTE_PARAMS_HEADER } from "./headers.js";
4
+ import { ACTION_REVALIDATED_HEADER, VINEXT_MW_CTX_HEADER, VINEXT_PRERENDER_ROUTE_PARAMS_HEADER } from "./headers.js";
5
5
  import { isExternalUrl, matchRedirect, matchRewrite, proxyExternalRequest, requestContextFromRequest, sanitizeDestination } from "../config/config-matchers.js";
6
6
  import { notFoundResponse } from "./http-error-responses.js";
7
7
  import { applyConfigHeadersToResponse, cloneRequestWithHeaders, filterInternalHeaders, normalizeTrailingSlash, resolvePublicFileRoute, validateImageUrl } from "./request-pipeline.js";
@@ -12,14 +12,14 @@ import { getScriptNonceFromHeaderSources } from "./csp.js";
12
12
  import { normalizeDefaultLocalePathname } from "./pages-i18n.js";
13
13
  import { isImageOptimizationPath } from "./image-optimization.js";
14
14
  import { prerenderRouteParamsPayloadMatchesRoute, readTrustedPrerenderRouteParams, serializePrerenderRouteParamsHeader } from "./prerender-route-params.js";
15
- import { pickRootParams, setRootParams } from "../shims/root-params.js";
15
+ import { flattenErrorCauses } from "../utils/error-cause.js";
16
16
  import { mergeMiddlewareResponseHeaders } from "./middleware-response-headers.js";
17
17
  import "./app-page-response.js";
18
18
  import { applyAppMiddleware } from "./app-middleware.js";
19
19
  import { buildPageCacheTags } from "./implicit-tags.js";
20
20
  import { buildPostMwRequestContext } from "./app-post-middleware-context.js";
21
+ import { pickRootParams, setRootParams } from "../shims/root-params.js";
21
22
  import { handleAppPrerenderEndpoint } from "./app-prerender-endpoints.js";
22
- import { flattenErrorCauses } from "../utils/error-cause.js";
23
23
  import { finalizeAppRscResponse } from "./app-rsc-response-finalizer.js";
24
24
  import { normalizeRscRequest } from "./app-rsc-request-normalization.js";
25
25
  import { handleMetadataRouteRequest } from "./metadata-route-response.js";
@@ -173,6 +173,8 @@ async function handleAppRscRequest(options, request, preMiddlewareRequestContext
173
173
  searchParams: url.searchParams,
174
174
  params: {}
175
175
  });
176
+ const preActionMatch = options.matchRoute(cleanPathname);
177
+ if (preActionMatch) setRootParams(pickRootParams(preActionMatch.params, preActionMatch.route.rootParamNames));
176
178
  const actionId = request.headers.get("x-rsc-action") ?? request.headers.get("next-action");
177
179
  const contentType = request.headers.get("content-type") || "";
178
180
  const progressiveActionResult = await options.handleProgressiveActionRequest({
@@ -200,7 +202,7 @@ async function handleAppRscRequest(options, request, preMiddlewareRequestContext
200
202
  searchParams: url.searchParams
201
203
  });
202
204
  if (serverActionResponse) return serverActionResponse;
203
- let match = options.matchRoute(cleanPathname);
205
+ let match = preActionMatch;
204
206
  if (!match || match.route.isDynamic) {
205
207
  const afterFilesRewrite = await applyRewrite({
206
208
  basePathState,
@@ -230,6 +232,10 @@ async function handleAppRscRequest(options, request, preMiddlewareRequestContext
230
232
  }
231
233
  }
232
234
  if (!match) {
235
+ if (process.env.NODE_ENV !== "production" && canonicalPathname === "/favicon.ico") {
236
+ options.clearRequestContext();
237
+ return new Response("", { status: 404 });
238
+ }
233
239
  const pagesFallbackResponse = await options.renderPagesFallback?.({
234
240
  isRscRequest,
235
241
  middlewareContext,
@@ -275,7 +281,7 @@ async function handleAppRscRequest(options, request, preMiddlewareRequestContext
275
281
  searchParams: url.searchParams
276
282
  });
277
283
  }
278
- return options.dispatchMatchedPage({
284
+ const pageResponse = await options.dispatchMatchedPage({
279
285
  cleanPathname,
280
286
  formState,
281
287
  actionError,
@@ -295,6 +301,40 @@ async function handleAppRscRequest(options, request, preMiddlewareRequestContext
295
301
  searchParams: url.searchParams,
296
302
  renderMode
297
303
  });
304
+ if (isProgressiveActionRender) return applyProgressiveActionSideEffects(pageResponse, progressiveActionResult);
305
+ return pageResponse;
306
+ }
307
+ /**
308
+ * Append `Set-Cookie` headers and the `x-action-revalidated` marker captured
309
+ * during progressive (no-JS) server action execution to the page render
310
+ * response. See issue #1483.
311
+ *
312
+ * Falls back to rebuilding the response when the headers object is immutable
313
+ * (e.g. `Response.redirect()`), so cookies set by the action ride out on a
314
+ * redirect issued during the rerender too.
315
+ */
316
+ function applyProgressiveActionSideEffects(response, sideEffects) {
317
+ const hasPendingCookies = sideEffects.pendingCookies.length > 0;
318
+ const hasDraftCookie = Boolean(sideEffects.draftCookie);
319
+ const hasRevalidationKind = sideEffects.revalidationKind !== 0;
320
+ if (!hasPendingCookies && !hasDraftCookie && !hasRevalidationKind) return response;
321
+ const applyTo = (headers) => {
322
+ for (const cookie of sideEffects.pendingCookies) headers.append("Set-Cookie", cookie);
323
+ if (sideEffects.draftCookie) headers.append("Set-Cookie", sideEffects.draftCookie);
324
+ if (hasRevalidationKind) headers.set(ACTION_REVALIDATED_HEADER, JSON.stringify(sideEffects.revalidationKind));
325
+ };
326
+ try {
327
+ applyTo(response.headers);
328
+ return response;
329
+ } catch {
330
+ const headers = new Headers(response.headers);
331
+ applyTo(headers);
332
+ return new Response(response.body, {
333
+ status: response.status,
334
+ statusText: response.statusText,
335
+ headers
336
+ });
337
+ }
298
338
  }
299
339
  function createAppRscHandler(options) {
300
340
  return async function appRscHandler(rawRequest, ctx) {
@@ -1 +1 @@
1
- {"version":3,"file":"app-rsc-handler.js","names":[],"sources":["../../src/server/app-rsc-handler.ts"],"sourcesContent":["import type {\n NextHeader,\n NextI18nConfig,\n NextRedirect,\n NextRewrite,\n} from \"../config/next-config.js\";\nimport {\n isExternalUrl,\n matchRedirect,\n matchRewrite,\n proxyExternalRequest,\n requestContextFromRequest,\n sanitizeDestination,\n type BasePathMatchState,\n} from \"../config/config-matchers.js\";\nimport { headersContextFromRequest } from \"vinext/shims/headers\";\nimport {\n NEXT_ACTION_HEADER,\n RSC_ACTION_HEADER,\n RSC_HEADER,\n VINEXT_MW_CTX_HEADER,\n VINEXT_PRERENDER_ROUTE_PARAMS_HEADER,\n} from \"./headers.js\";\nimport { ensureFetchPatch, setCurrentFetchSoftTags } from \"vinext/shims/fetch-cache\";\nimport type { ReactFormState } from \"react-dom/client\";\nimport {\n getRequestExecutionContext,\n type ExecutionContextLike,\n} from \"vinext/shims/request-context\";\nimport { pickRootParams, setRootParams, type RootParams } from \"vinext/shims/root-params\";\nimport { createRequestContext, runWithRequestContext } from \"vinext/shims/unified-request-context\";\nimport { flattenErrorCauses } from \"../utils/error-cause.js\";\nimport { hasBasePath } from \"../utils/base-path.js\";\nimport { applyAppMiddleware, type AppMiddlewareContext } from \"./app-middleware.js\";\nimport { mergeMiddlewareResponseHeaders } from \"./app-page-response.js\";\nimport { handleAppPrerenderEndpoint } from \"./app-prerender-endpoints.js\";\nimport {\n createRscRedirectLocation,\n resolveInvalidRscCacheBustingRequest,\n stripRscCacheBustingSearchParam,\n stripRscSuffix,\n} from \"./app-rsc-cache-busting.js\";\nimport { finalizeAppRscResponse } from \"./app-rsc-response-finalizer.js\";\nimport { normalizeRscRequest } from \"./app-rsc-request-normalization.js\";\nimport { normalizeDefaultLocalePathname } from \"./pages-i18n.js\";\nimport { notFoundResponse } from \"./http-error-responses.js\";\nimport { getScriptNonceFromHeaderSources } from \"./csp.js\";\nimport { buildPageCacheTags } from \"./implicit-tags.js\";\nimport { isImageOptimizationPath } from \"./image-optimization.js\";\nimport { handleMetadataRouteRequest } from \"./metadata-route-response.js\";\nimport type { MiddlewareModule } from \"./middleware-runtime.js\";\nimport { runWithPrerenderWorkUnit } from \"./prerender-work-unit-setup.js\";\nimport { buildPostMwRequestContext } from \"./app-post-middleware-context.js\";\nimport type { AppRscRenderMode } from \"./app-rsc-render-mode.js\";\nimport {\n cloneRequestWithHeaders,\n filterInternalHeaders,\n applyConfigHeadersToResponse,\n normalizeTrailingSlash,\n resolvePublicFileRoute,\n validateImageUrl,\n} from \"./request-pipeline.js\";\nimport {\n prerenderRouteParamsPayloadMatchesRoute,\n readTrustedPrerenderRouteParams,\n serializePrerenderRouteParamsHeader,\n} from \"./prerender-route-params.js\";\n\ntype AppPageParams = Record<string, string | string[]>;\ntype RequestContext = ReturnType<typeof requestContextFromRequest>;\ntype MetadataRoutes = Parameters<typeof handleMetadataRouteRequest>[0][\"metadataRoutes\"];\ntype MakeThenableParams = Parameters<typeof handleMetadataRouteRequest>[0][\"makeThenableParams\"];\ntype StaticParamsMap = Parameters<typeof handleAppPrerenderEndpoint>[1][\"staticParamsMap\"];\ntype RootParamNamesMap = Parameters<\n typeof handleAppPrerenderEndpoint\n>[1][\"rootParamNamesByPattern\"];\n\ntype AppRscMiddlewareContext = AppMiddlewareContext;\n\ntype AppRscHandlerRoute = {\n isDynamic: boolean;\n page?: unknown;\n pattern: string;\n rootParamNames?: readonly string[];\n routeHandler?: unknown;\n routeSegments: readonly string[];\n};\n\ntype AppRscRouteMatch<TRoute> = {\n params: AppPageParams;\n route: TRoute;\n};\n\ntype DispatchMatchedPageOptions<TRoute> = {\n cleanPathname: string;\n formState: ReactFormState | null;\n actionError?: unknown;\n actionFailed?: boolean;\n handlerStart: number;\n interceptionContext: string | null;\n isProgressiveActionRender: boolean;\n isRscRequest: boolean;\n middlewareContext: AppRscMiddlewareContext;\n mountedSlotsHeader: string | null;\n params: AppPageParams;\n staticParamsValidationParams?: AppPageParams;\n rootParams?: RootParams;\n request: Request;\n route: TRoute;\n scriptNonce?: string;\n searchParams: URLSearchParams;\n renderMode: AppRscRenderMode;\n};\n\ntype DispatchMatchedRouteHandlerOptions<TRoute> = {\n cleanPathname: string;\n middlewareContext: AppRscMiddlewareContext;\n /**\n * `null` for non-dynamic routes. Mirrors Next.js' route handler context\n * shape: user code that does `params ? await params : null` resolves to\n * `null` for routes without dynamic segments. Dynamic routes receive the\n * matched params object.\n */\n params: AppPageParams | null;\n request: Request;\n route: TRoute;\n searchParams: URLSearchParams;\n};\n\ntype HandleProgressiveActionRequestOptions = {\n actionId: string | null;\n cleanPathname: string;\n contentType: string;\n middlewareContext: AppRscMiddlewareContext;\n request: Request;\n};\n\ntype ProgressiveActionFormStateResult =\n | {\n formState: ReactFormState | null;\n kind: \"form-state\";\n }\n | {\n actionError: unknown;\n actionFailed: true;\n formState: null;\n kind: \"form-state\";\n };\n\ntype HandleServerActionRequestOptions = {\n actionId: string | null;\n cleanPathname: string;\n contentType: string;\n interceptionContext: string | null;\n isRscRequest: boolean;\n middlewareContext: AppRscMiddlewareContext;\n mountedSlotsHeader: string | null;\n request: Request;\n searchParams: URLSearchParams;\n};\n\ntype RenderNotFoundOptions<TRoute> = {\n isRscRequest: boolean;\n matchedParams?: AppPageParams;\n middlewareContext: AppRscMiddlewareContext;\n request: Request;\n route: TRoute | null;\n scriptNonce?: string;\n};\n\ntype RenderPagesFallbackOptions = {\n isRscRequest: boolean;\n middlewareContext: AppRscMiddlewareContext;\n request: Request;\n url: URL;\n};\n\ntype NavigationContextValue = {\n params: AppPageParams;\n pathname: string;\n searchParams: URLSearchParams;\n};\n\ntype CreateAppRscHandlerOptions<TRoute extends AppRscHandlerRoute> = {\n basePath: string;\n clearRequestContext: () => void;\n configHeaders: NextHeader[];\n configRedirects: NextRedirect[];\n configRewrites: {\n afterFiles: NextRewrite[];\n beforeFiles: NextRewrite[];\n fallback: NextRewrite[];\n };\n draftModeSecret: string;\n dispatchMatchedPage: (options: DispatchMatchedPageOptions<TRoute>) => Promise<Response>;\n dispatchMatchedRouteHandler: (\n options: DispatchMatchedRouteHandlerOptions<TRoute>,\n ) => Promise<Response>;\n ensureInstrumentation?: () => Promise<void>;\n handleProgressiveActionRequest: (\n options: HandleProgressiveActionRequestOptions,\n ) => Promise<Response | ProgressiveActionFormStateResult | null>;\n handleServerActionRequest: (\n options: HandleServerActionRequestOptions,\n ) => Promise<Response | null>;\n i18nConfig: NextI18nConfig | null;\n isMiddlewareProxy: boolean;\n loadPrerenderPagesRoutes?: () => Promise<unknown>;\n makeThenableParams: MakeThenableParams;\n matchRoute: (pathname: string) => AppRscRouteMatch<TRoute> | null;\n metadataRoutes: MetadataRoutes;\n middlewareModule: MiddlewareModule | null;\n publicFiles: ReadonlySet<string>;\n renderNotFound: (options: RenderNotFoundOptions<TRoute>) => Promise<Response | null>;\n renderPagesFallback?: (options: RenderPagesFallbackOptions) => Promise<Response | null>;\n rootParamNamesByPattern?: RootParamNamesMap;\n setNavigationContext: (context: NavigationContextValue) => void;\n staticParamsMap: StaticParamsMap;\n trailingSlash: boolean;\n validateDevRequestOrigin?: (request: Request) => Response | null;\n};\n\nfunction hasProperty<TKey extends PropertyKey>(\n value: object,\n key: TKey,\n): value is object & Record<TKey, unknown> {\n return key in value;\n}\n\nfunction isExecutionContextLike(value: unknown): value is ExecutionContextLike {\n if (!value || typeof value !== \"object\") return false;\n return hasProperty(value, \"waitUntil\") && typeof value.waitUntil === \"function\";\n}\n\n// TODO(#1333): once App Router supports `basePath: false` rules (see\n// `normalizeRscRequest` — it 404s out-of-basePath requests before they\n// reach this code), pass `hadBasePath` here and skip the prefix when\n// false, mirroring the same guard in `prod-server.ts` and `deploy.ts`.\nfunction redirectDestinationWithBasePath(destination: string, basePath: string): string {\n if (!basePath || isExternalUrl(destination) || hasBasePath(destination, basePath)) {\n return destination;\n }\n return basePath + destination;\n}\n\nasync function applyRewrite(\n options: {\n basePathState: BasePathMatchState;\n clearRequestContext: () => void;\n request: Request;\n requestContext: RequestContext;\n rewrites: NextRewrite[];\n },\n cleanPathname: string,\n): Promise<Response | string | null> {\n if (!options.rewrites.length) return null;\n\n const rewritten = matchRewrite(\n cleanPathname,\n options.rewrites,\n options.requestContext,\n options.basePathState,\n );\n if (!rewritten) return null;\n\n if (isExternalUrl(rewritten)) {\n options.clearRequestContext();\n return proxyExternalRequest(options.request, rewritten);\n }\n\n return rewritten;\n}\n\nfunction applyConfigHeadersToMiddlewareRedirect(\n response: Response,\n options: {\n basePathState: BasePathMatchState;\n configHeaders: NextHeader[];\n pathname: string;\n requestContext: RequestContext;\n },\n): Response {\n // Non-redirect middleware responses still pass through finalization, where\n // config headers are applied once. Redirects skip finalization to avoid\n // mutating immutable redirect headers, so they need the earlier header layer here.\n if (response.status < 300 || response.status >= 400) return response;\n if (!options.configHeaders.length) return response;\n\n const headers = new Headers();\n applyConfigHeadersToResponse(headers, {\n configHeaders: options.configHeaders,\n pathname: options.pathname,\n requestContext: options.requestContext,\n basePathState: options.basePathState,\n });\n\n if (!headers.entries().next().done) {\n mergeMiddlewareResponseHeaders(headers, response.headers);\n return new Response(response.body, {\n status: response.status,\n statusText: response.statusText,\n headers,\n });\n }\n\n return response;\n}\n\nasync function handleAppRscRequest<TRoute extends AppRscHandlerRoute>(\n options: CreateAppRscHandlerOptions<TRoute>,\n request: Request,\n preMiddlewareRequestContext: RequestContext,\n isDataRequest: boolean,\n): Promise<Response> {\n const handlerStart = process.env.NODE_ENV !== \"production\" ? performance.now() : 0;\n\n if (process.env.NODE_ENV !== \"production\") {\n const originBlock = options.validateDevRequestOrigin?.(request);\n if (originBlock) return originBlock;\n }\n\n const normalized = normalizeRscRequest(request, options.basePath);\n if (normalized instanceof Response) return normalized;\n\n const { url, isRscRequest, interceptionContextHeader, mountedSlotsHeader, renderMode } =\n normalized;\n let { pathname, cleanPathname } = normalized;\n // Canonical (external) pathname the user requested. Middleware rewrites and\n // next.config.js rewrites mutate `cleanPathname` so internal route matching\n // can find the destination page, but hooks like `usePathname()` must reflect\n // the original URL the user sees in the address bar.\n // Matches Next.js: test/e2e/app-dir/hooks/hooks.test.ts —\n // \"should have the canonical url pathname on rewrite\"\n const canonicalPathname = cleanPathname;\n\n // The request reached this point so it was either under basePath (stripped\n // by normalizeRscRequest) or basePath is empty. In both cases the matcher\n // gating below treats default (basePath: true) rules as eligible. The App\n // Router does not yet support `basePath: false` rules — they would need a\n // pre-strip hook in normalizeRscRequest to fire. Tracked as follow-up to\n // issue #1333.\n const basePathState = { basePath: options.basePath, hadBasePath: true };\n\n const prerenderEndpointResponse = await handleAppPrerenderEndpoint(request, {\n isPrerenderEnabled() {\n return process.env.VINEXT_PRERENDER === \"1\";\n },\n loadPagesRoutes: options.loadPrerenderPagesRoutes,\n pathname,\n rootParamNamesByPattern: options.rootParamNamesByPattern,\n staticParamsMap: options.staticParamsMap,\n });\n if (prerenderEndpointResponse) return prerenderEndpointResponse;\n\n const trailingSlashRedirect = normalizeTrailingSlash(\n pathname,\n options.basePath,\n options.trailingSlash,\n url.search,\n );\n if (trailingSlashRedirect) return trailingSlashRedirect;\n\n // Default-locale path normalisation (issue #1336, item 4). Next.js\n // splices in the (domain-aware) default locale on every request that\n // arrives without a locale prefix before running config redirect / rewrite\n // / header matching. Mirrors resolve-routes.ts lines ~250-263.\n //\n // Defined once here so the same helper is reused for the redirect match\n // below, the middleware-redirect config header match further down, and the\n // post-middleware rewrite matches. `i18nConfig` and `url.hostname` are\n // request-scoped constants from this point on.\n const matchPathname = (p: string): string =>\n normalizeDefaultLocalePathname(p, options.i18nConfig, { hostname: url.hostname });\n\n const redirectPathname = matchPathname(stripRscSuffix(pathname));\n const redirect = matchRedirect(\n redirectPathname,\n options.configRedirects,\n preMiddlewareRequestContext,\n basePathState,\n );\n if (redirect) {\n const destination = sanitizeDestination(\n redirectDestinationWithBasePath(redirect.destination, options.basePath),\n );\n const location =\n isRscRequest && request.headers.get(RSC_HEADER) === \"1\"\n ? await createRscRedirectLocation(destination, request)\n : destination;\n return new Response(null, {\n status: redirect.permanent ? 308 : 307,\n headers: { Location: location },\n });\n }\n\n const rscCacheBustingRedirect = await resolveInvalidRscCacheBustingRequest({\n isRscRequest,\n request,\n });\n if (rscCacheBustingRedirect) return rscCacheBustingRedirect;\n\n const middlewareContext: AppRscMiddlewareContext = {\n headers: null,\n requestHeaders: null,\n status: null,\n };\n\n if (options.middlewareModule) {\n const middlewareResult = await applyAppMiddleware({\n basePath: options.basePath,\n cleanPathname,\n context: middlewareContext,\n i18nConfig: options.i18nConfig,\n isDataRequest,\n isProxy: options.isMiddlewareProxy,\n module: options.middlewareModule,\n request,\n trailingSlash: options.trailingSlash,\n });\n if (middlewareResult.kind === \"response\") {\n return applyConfigHeadersToMiddlewareRedirect(middlewareResult.response, {\n basePathState,\n configHeaders: options.configHeaders,\n pathname: matchPathname(cleanPathname),\n requestContext: preMiddlewareRequestContext,\n });\n }\n\n cleanPathname = middlewareResult.cleanPathname;\n if (middlewareResult.search !== null) {\n url.search = middlewareResult.search;\n }\n }\n\n const scriptNonce = getScriptNonceFromHeaderSources(request.headers, middlewareContext.headers);\n const postMiddlewareRequestContext = buildPostMwRequestContext(request);\n\n // Rewrites (beforeFiles, afterFiles, fallback) use `matchPathname` from\n // above to splice in the default locale before matching. Route matching\n // itself continues to use the un-prefixed `cleanPathname` because App\n // Router files live under `app/...` with no locale segment. See issue\n // #1336 item 4 / pages-i18n.normalizeDefaultLocalePathname.\n const beforeFilesRewrite = await applyRewrite(\n {\n basePathState,\n clearRequestContext: options.clearRequestContext,\n request,\n requestContext: postMiddlewareRequestContext,\n rewrites: options.configRewrites.beforeFiles,\n },\n matchPathname(cleanPathname),\n );\n if (beforeFilesRewrite instanceof Response) return beforeFilesRewrite;\n if (beforeFilesRewrite) cleanPathname = beforeFilesRewrite;\n\n if (isImageOptimizationPath(cleanPathname)) {\n const imageUrlResult = validateImageUrl(url.searchParams.get(\"url\"), request.url);\n if (imageUrlResult instanceof Response) return imageUrlResult;\n return Response.redirect(new URL(imageUrlResult, url.origin).href, 302);\n }\n\n const metadataRouteResponse = await handleMetadataRouteRequest({\n metadataRoutes: options.metadataRoutes,\n cleanPathname,\n makeThenableParams: options.makeThenableParams,\n });\n if (metadataRouteResponse) return metadataRouteResponse;\n\n const publicFileResponse = resolvePublicFileRoute({\n cleanPathname,\n middlewareContext,\n pathname,\n publicFiles: options.publicFiles,\n request,\n });\n if (publicFileResponse) {\n options.clearRequestContext();\n return publicFileResponse;\n }\n\n if (isRscRequest) {\n stripRscCacheBustingSearchParam(url);\n }\n\n options.setNavigationContext({\n pathname: canonicalPathname,\n searchParams: url.searchParams,\n params: {},\n });\n\n const actionId =\n request.headers.get(RSC_ACTION_HEADER) ?? request.headers.get(NEXT_ACTION_HEADER);\n const contentType = request.headers.get(\"content-type\") || \"\";\n\n const progressiveActionResult = await options.handleProgressiveActionRequest({\n actionId,\n cleanPathname,\n contentType,\n middlewareContext,\n request,\n });\n if (progressiveActionResult instanceof Response) return progressiveActionResult;\n const isProgressiveActionRender = progressiveActionResult?.kind === \"form-state\";\n const formState = isProgressiveActionRender ? progressiveActionResult.formState : null;\n const failedProgressiveActionResult =\n isProgressiveActionRender && \"actionFailed\" in progressiveActionResult\n ? progressiveActionResult\n : null;\n const actionFailed = failedProgressiveActionResult !== null;\n const actionError = failedProgressiveActionResult?.actionError;\n\n const serverActionResponse = await options.handleServerActionRequest({\n actionId,\n cleanPathname,\n contentType,\n interceptionContext: interceptionContextHeader,\n isRscRequest,\n middlewareContext,\n mountedSlotsHeader,\n request,\n searchParams: url.searchParams,\n });\n if (serverActionResponse) return serverActionResponse;\n\n let match = options.matchRoute(cleanPathname);\n if (!match || match.route.isDynamic) {\n const afterFilesRewrite = await applyRewrite(\n {\n basePathState,\n clearRequestContext: options.clearRequestContext,\n request,\n requestContext: postMiddlewareRequestContext,\n rewrites: options.configRewrites.afterFiles,\n },\n matchPathname(cleanPathname),\n );\n if (afterFilesRewrite instanceof Response) return afterFilesRewrite;\n if (afterFilesRewrite) {\n cleanPathname = afterFilesRewrite;\n match = options.matchRoute(cleanPathname);\n }\n }\n\n if (!match) {\n const fallbackRewrite = await applyRewrite(\n {\n basePathState,\n clearRequestContext: options.clearRequestContext,\n request,\n requestContext: postMiddlewareRequestContext,\n rewrites: options.configRewrites.fallback,\n },\n matchPathname(cleanPathname),\n );\n if (fallbackRewrite instanceof Response) return fallbackRewrite;\n if (fallbackRewrite) {\n cleanPathname = fallbackRewrite;\n match = options.matchRoute(cleanPathname);\n }\n }\n\n if (!match) {\n const pagesFallbackResponse = await options.renderPagesFallback?.({\n isRscRequest,\n middlewareContext,\n request,\n url,\n });\n if (pagesFallbackResponse) {\n options.clearRequestContext();\n return pagesFallbackResponse;\n }\n\n const renderedNotFoundResponse = await options.renderNotFound({\n isRscRequest,\n middlewareContext,\n request,\n route: null,\n scriptNonce,\n });\n if (renderedNotFoundResponse) return renderedNotFoundResponse;\n\n options.clearRequestContext();\n const headers = new Headers();\n mergeMiddlewareResponseHeaders(headers, middlewareContext.headers);\n return notFoundResponse({ headers });\n }\n\n const { route, params } = match;\n const prerenderRouteParamsPayload = readTrustedPrerenderRouteParams(request);\n const prerenderRouteParams = prerenderRouteParamsPayloadMatchesRoute(\n prerenderRouteParamsPayload,\n route.pattern,\n params,\n )\n ? prerenderRouteParamsPayload.params\n : null;\n const renderParams = prerenderRouteParams ?? params;\n options.setNavigationContext({\n pathname: canonicalPathname,\n searchParams: url.searchParams,\n params: renderParams,\n });\n const rootParams = pickRootParams(renderParams, route.rootParamNames);\n setRootParams(rootParams);\n\n if (route.routeHandler) {\n setCurrentFetchSoftTags(\n buildPageCacheTags(cleanPathname, [], [...route.routeSegments], \"route\"),\n );\n return options.dispatchMatchedRouteHandler({\n cleanPathname,\n middlewareContext,\n // Non-dynamic routes report params as `null` to match Next.js. Internal\n // bookkeeping above (navigation context, root params) keeps the matched\n // object (always `{}` for non-dynamic) so `useParams()` etc. still see\n // an object shape; only the user-facing handler context surfaces null.\n params: route.isDynamic ? renderParams : null,\n request,\n route,\n searchParams: url.searchParams,\n });\n }\n\n return options.dispatchMatchedPage({\n cleanPathname,\n formState,\n actionError,\n actionFailed,\n handlerStart,\n interceptionContext: interceptionContextHeader,\n isProgressiveActionRender,\n isRscRequest,\n middlewareContext,\n mountedSlotsHeader,\n params: renderParams,\n staticParamsValidationParams: prerenderRouteParams === null ? undefined : params,\n rootParams,\n request,\n route,\n scriptNonce,\n searchParams: url.searchParams,\n renderMode,\n });\n}\n\nexport function createAppRscHandler<TRoute extends AppRscHandlerRoute>(\n options: CreateAppRscHandlerOptions<TRoute>,\n): (request: Request, ctx: unknown) => Promise<Response> {\n return async function appRscHandler(rawRequest, ctx) {\n await options.ensureInstrumentation?.();\n\n // Strip forged internal headers at the App Router request boundary.\n // Must happen BEFORE headersContextFromRequest() and\n // requestContextFromRequest() so the captured context never contains\n // attacker-controlled internal headers. This is the correct boundary\n // for pure App Router requests; in hybrid app+pages mode the connect\n // handler already filtered headers upstream and x-vinext-mw-ctx\n // (not in INTERNAL_HEADERS) carries the forwarded middleware context.\n // srvx's NodeRequestHeaders reads from rawHeaders for iteration but falls\n // back to req.headers for .get() / .has(). In the dev server we add\n // x-vinext-mw-ctx to req.headers after the Request is built, so it is\n // visible to .get() but lost when filterInternalHeaders iterates. Read it\n // BEFORE iterating so applyForwardedMiddlewareContext can skip middleware.\n const mwCtx = rawRequest.headers.get(VINEXT_MW_CTX_HEADER);\n // Capture `x-nextjs-data` before filtering — the middleware redirect\n // protocol needs to know whether the inbound request was a `_next/data`\n // fetch to emit `x-nextjs-redirect` instead of an HTTP redirect.\n const isDataRequest = rawRequest.headers.get(\"x-nextjs-data\") === \"1\";\n // Read the trusted prerender route params before filtering strips the\n // route-params header (it IS in VINEXT_INTERNAL_HEADERS), then re-attach the\n // validated value below so the second read in handleAppRscRequest still sees\n // it. The secret was already verified upstream at prod-server's\n // nodeToWebRequest boundary; the surviving secret header (NOT in either\n // internal-header list) lets readTrustedPrerenderRouteParams's\n // VINEXT_PRERENDER gate pass on the reconstructed request. If the secret\n // header is ever added to VINEXT_INTERNAL_HEADERS, that second read breaks.\n const prerenderRouteParamsPayload = readTrustedPrerenderRouteParams(rawRequest);\n const filteredHeaders = filterInternalHeaders(rawRequest.headers);\n if (mwCtx !== null) {\n filteredHeaders.set(VINEXT_MW_CTX_HEADER, mwCtx);\n }\n const prerenderRouteParamsHeader = serializePrerenderRouteParamsHeader(\n prerenderRouteParamsPayload,\n );\n if (prerenderRouteParamsHeader !== null) {\n filteredHeaders.set(VINEXT_PRERENDER_ROUTE_PARAMS_HEADER, prerenderRouteParamsHeader);\n }\n const request = cloneRequestWithHeaders(rawRequest, filteredHeaders);\n\n const executionContext = isExecutionContextLike(ctx)\n ? ctx\n : (getRequestExecutionContext() ?? null);\n const headersContext = headersContextFromRequest(request, {\n draftModeSecret: options.draftModeSecret,\n });\n const requestContext = createRequestContext({\n headersContext,\n executionContext,\n unstableCacheRevalidation: \"background\",\n });\n\n return runWithRequestContext(requestContext, () =>\n runWithPrerenderWorkUnit(\n async () => {\n ensureFetchPatch();\n const preMiddlewareRequestContext = requestContextFromRequest(request);\n let response: Response;\n\n try {\n response = await handleAppRscRequest(\n options,\n request,\n preMiddlewareRequestContext,\n isDataRequest,\n );\n } catch (error) {\n if (process.env.NODE_ENV !== \"production\") {\n flattenErrorCauses(error);\n }\n throw error;\n }\n\n return finalizeAppRscResponse(response, request, {\n basePath: options.basePath,\n configHeaders: options.configHeaders,\n i18nConfig: options.i18nConfig,\n requestContext: preMiddlewareRequestContext,\n });\n },\n { route: () => new URL(request.url).pathname },\n ),\n );\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;AA8NA,SAAS,YACP,OACA,KACyC;CACzC,OAAO,OAAO;;AAGhB,SAAS,uBAAuB,OAA+C;CAC7E,IAAI,CAAC,SAAS,OAAO,UAAU,UAAU,OAAO;CAChD,OAAO,YAAY,OAAO,YAAY,IAAI,OAAO,MAAM,cAAc;;AAOvE,SAAS,gCAAgC,aAAqB,UAA0B;CACtF,IAAI,CAAC,YAAY,cAAc,YAAY,IAAI,YAAY,aAAa,SAAS,EAC/E,OAAO;CAET,OAAO,WAAW;;AAGpB,eAAe,aACb,SAOA,eACmC;CACnC,IAAI,CAAC,QAAQ,SAAS,QAAQ,OAAO;CAErC,MAAM,YAAY,aAChB,eACA,QAAQ,UACR,QAAQ,gBACR,QAAQ,cACT;CACD,IAAI,CAAC,WAAW,OAAO;CAEvB,IAAI,cAAc,UAAU,EAAE;EAC5B,QAAQ,qBAAqB;EAC7B,OAAO,qBAAqB,QAAQ,SAAS,UAAU;;CAGzD,OAAO;;AAGT,SAAS,uCACP,UACA,SAMU;CAIV,IAAI,SAAS,SAAS,OAAO,SAAS,UAAU,KAAK,OAAO;CAC5D,IAAI,CAAC,QAAQ,cAAc,QAAQ,OAAO;CAE1C,MAAM,UAAU,IAAI,SAAS;CAC7B,6BAA6B,SAAS;EACpC,eAAe,QAAQ;EACvB,UAAU,QAAQ;EAClB,gBAAgB,QAAQ;EACxB,eAAe,QAAQ;EACxB,CAAC;CAEF,IAAI,CAAC,QAAQ,SAAS,CAAC,MAAM,CAAC,MAAM;EAClC,+BAA+B,SAAS,SAAS,QAAQ;EACzD,OAAO,IAAI,SAAS,SAAS,MAAM;GACjC,QAAQ,SAAS;GACjB,YAAY,SAAS;GACrB;GACD,CAAC;;CAGJ,OAAO;;AAGT,eAAe,oBACb,SACA,SACA,6BACA,eACmB;CACnB,MAAM,eAAe,QAAQ,IAAI,aAAa,eAAe,YAAY,KAAK,GAAG;CAEjF,IAAI,QAAQ,IAAI,aAAa,cAAc;EACzC,MAAM,cAAc,QAAQ,2BAA2B,QAAQ;EAC/D,IAAI,aAAa,OAAO;;CAG1B,MAAM,aAAa,oBAAoB,SAAS,QAAQ,SAAS;CACjE,IAAI,sBAAsB,UAAU,OAAO;CAE3C,MAAM,EAAE,KAAK,cAAc,2BAA2B,oBAAoB,eACxE;CACF,IAAI,EAAE,UAAU,kBAAkB;CAOlC,MAAM,oBAAoB;CAQ1B,MAAM,gBAAgB;EAAE,UAAU,QAAQ;EAAU,aAAa;EAAM;CAEvE,MAAM,4BAA4B,MAAM,2BAA2B,SAAS;EAC1E,qBAAqB;GACnB,OAAO,QAAQ,IAAI,qBAAqB;;EAE1C,iBAAiB,QAAQ;EACzB;EACA,yBAAyB,QAAQ;EACjC,iBAAiB,QAAQ;EAC1B,CAAC;CACF,IAAI,2BAA2B,OAAO;CAEtC,MAAM,wBAAwB,uBAC5B,UACA,QAAQ,UACR,QAAQ,eACR,IAAI,OACL;CACD,IAAI,uBAAuB,OAAO;CAWlC,MAAM,iBAAiB,MACrB,+BAA+B,GAAG,QAAQ,YAAY,EAAE,UAAU,IAAI,UAAU,CAAC;CAGnF,MAAM,WAAW,cADQ,cAAc,eAAe,SAAS,CAE7C,EAChB,QAAQ,iBACR,6BACA,cACD;CACD,IAAI,UAAU;EACZ,MAAM,cAAc,oBAClB,gCAAgC,SAAS,aAAa,QAAQ,SAAS,CACxE;EACD,MAAM,WACJ,gBAAgB,QAAQ,QAAQ,IAAA,MAAe,KAAK,MAChD,MAAM,0BAA0B,aAAa,QAAQ,GACrD;EACN,OAAO,IAAI,SAAS,MAAM;GACxB,QAAQ,SAAS,YAAY,MAAM;GACnC,SAAS,EAAE,UAAU,UAAU;GAChC,CAAC;;CAGJ,MAAM,0BAA0B,MAAM,qCAAqC;EACzE;EACA;EACD,CAAC;CACF,IAAI,yBAAyB,OAAO;CAEpC,MAAM,oBAA6C;EACjD,SAAS;EACT,gBAAgB;EAChB,QAAQ;EACT;CAED,IAAI,QAAQ,kBAAkB;EAC5B,MAAM,mBAAmB,MAAM,mBAAmB;GAChD,UAAU,QAAQ;GAClB;GACA,SAAS;GACT,YAAY,QAAQ;GACpB;GACA,SAAS,QAAQ;GACjB,QAAQ,QAAQ;GAChB;GACA,eAAe,QAAQ;GACxB,CAAC;EACF,IAAI,iBAAiB,SAAS,YAC5B,OAAO,uCAAuC,iBAAiB,UAAU;GACvE;GACA,eAAe,QAAQ;GACvB,UAAU,cAAc,cAAc;GACtC,gBAAgB;GACjB,CAAC;EAGJ,gBAAgB,iBAAiB;EACjC,IAAI,iBAAiB,WAAW,MAC9B,IAAI,SAAS,iBAAiB;;CAIlC,MAAM,cAAc,gCAAgC,QAAQ,SAAS,kBAAkB,QAAQ;CAC/F,MAAM,+BAA+B,0BAA0B,QAAQ;CAOvE,MAAM,qBAAqB,MAAM,aAC/B;EACE;EACA,qBAAqB,QAAQ;EAC7B;EACA,gBAAgB;EAChB,UAAU,QAAQ,eAAe;EAClC,EACD,cAAc,cAAc,CAC7B;CACD,IAAI,8BAA8B,UAAU,OAAO;CACnD,IAAI,oBAAoB,gBAAgB;CAExC,IAAI,wBAAwB,cAAc,EAAE;EAC1C,MAAM,iBAAiB,iBAAiB,IAAI,aAAa,IAAI,MAAM,EAAE,QAAQ,IAAI;EACjF,IAAI,0BAA0B,UAAU,OAAO;EAC/C,OAAO,SAAS,SAAS,IAAI,IAAI,gBAAgB,IAAI,OAAO,CAAC,MAAM,IAAI;;CAGzE,MAAM,wBAAwB,MAAM,2BAA2B;EAC7D,gBAAgB,QAAQ;EACxB;EACA,oBAAoB,QAAQ;EAC7B,CAAC;CACF,IAAI,uBAAuB,OAAO;CAElC,MAAM,qBAAqB,uBAAuB;EAChD;EACA;EACA;EACA,aAAa,QAAQ;EACrB;EACD,CAAC;CACF,IAAI,oBAAoB;EACtB,QAAQ,qBAAqB;EAC7B,OAAO;;CAGT,IAAI,cACF,gCAAgC,IAAI;CAGtC,QAAQ,qBAAqB;EAC3B,UAAU;EACV,cAAc,IAAI;EAClB,QAAQ,EAAE;EACX,CAAC;CAEF,MAAM,WACJ,QAAQ,QAAQ,IAAA,eAAsB,IAAI,QAAQ,QAAQ,IAAA,cAAuB;CACnF,MAAM,cAAc,QAAQ,QAAQ,IAAI,eAAe,IAAI;CAE3D,MAAM,0BAA0B,MAAM,QAAQ,+BAA+B;EAC3E;EACA;EACA;EACA;EACA;EACD,CAAC;CACF,IAAI,mCAAmC,UAAU,OAAO;CACxD,MAAM,4BAA4B,yBAAyB,SAAS;CACpE,MAAM,YAAY,4BAA4B,wBAAwB,YAAY;CAClF,MAAM,gCACJ,6BAA6B,kBAAkB,0BAC3C,0BACA;CACN,MAAM,eAAe,kCAAkC;CACvD,MAAM,cAAc,+BAA+B;CAEnD,MAAM,uBAAuB,MAAM,QAAQ,0BAA0B;EACnE;EACA;EACA;EACA,qBAAqB;EACrB;EACA;EACA;EACA;EACA,cAAc,IAAI;EACnB,CAAC;CACF,IAAI,sBAAsB,OAAO;CAEjC,IAAI,QAAQ,QAAQ,WAAW,cAAc;CAC7C,IAAI,CAAC,SAAS,MAAM,MAAM,WAAW;EACnC,MAAM,oBAAoB,MAAM,aAC9B;GACE;GACA,qBAAqB,QAAQ;GAC7B;GACA,gBAAgB;GAChB,UAAU,QAAQ,eAAe;GAClC,EACD,cAAc,cAAc,CAC7B;EACD,IAAI,6BAA6B,UAAU,OAAO;EAClD,IAAI,mBAAmB;GACrB,gBAAgB;GAChB,QAAQ,QAAQ,WAAW,cAAc;;;CAI7C,IAAI,CAAC,OAAO;EACV,MAAM,kBAAkB,MAAM,aAC5B;GACE;GACA,qBAAqB,QAAQ;GAC7B;GACA,gBAAgB;GAChB,UAAU,QAAQ,eAAe;GAClC,EACD,cAAc,cAAc,CAC7B;EACD,IAAI,2BAA2B,UAAU,OAAO;EAChD,IAAI,iBAAiB;GACnB,gBAAgB;GAChB,QAAQ,QAAQ,WAAW,cAAc;;;CAI7C,IAAI,CAAC,OAAO;EACV,MAAM,wBAAwB,MAAM,QAAQ,sBAAsB;GAChE;GACA;GACA;GACA;GACD,CAAC;EACF,IAAI,uBAAuB;GACzB,QAAQ,qBAAqB;GAC7B,OAAO;;EAGT,MAAM,2BAA2B,MAAM,QAAQ,eAAe;GAC5D;GACA;GACA;GACA,OAAO;GACP;GACD,CAAC;EACF,IAAI,0BAA0B,OAAO;EAErC,QAAQ,qBAAqB;EAC7B,MAAM,UAAU,IAAI,SAAS;EAC7B,+BAA+B,SAAS,kBAAkB,QAAQ;EAClE,OAAO,iBAAiB,EAAE,SAAS,CAAC;;CAGtC,MAAM,EAAE,OAAO,WAAW;CAC1B,MAAM,8BAA8B,gCAAgC,QAAQ;CAC5E,MAAM,uBAAuB,wCAC3B,6BACA,MAAM,SACN,OACD,GACG,4BAA4B,SAC5B;CACJ,MAAM,eAAe,wBAAwB;CAC7C,QAAQ,qBAAqB;EAC3B,UAAU;EACV,cAAc,IAAI;EAClB,QAAQ;EACT,CAAC;CACF,MAAM,aAAa,eAAe,cAAc,MAAM,eAAe;CACrE,cAAc,WAAW;CAEzB,IAAI,MAAM,cAAc;EACtB,wBACE,mBAAmB,eAAe,EAAE,EAAE,CAAC,GAAG,MAAM,cAAc,EAAE,QAAQ,CACzE;EACD,OAAO,QAAQ,4BAA4B;GACzC;GACA;GAKA,QAAQ,MAAM,YAAY,eAAe;GACzC;GACA;GACA,cAAc,IAAI;GACnB,CAAC;;CAGJ,OAAO,QAAQ,oBAAoB;EACjC;EACA;EACA;EACA;EACA;EACA,qBAAqB;EACrB;EACA;EACA;EACA;EACA,QAAQ;EACR,8BAA8B,yBAAyB,OAAO,KAAA,IAAY;EAC1E;EACA;EACA;EACA;EACA,cAAc,IAAI;EAClB;EACD,CAAC;;AAGJ,SAAgB,oBACd,SACuD;CACvD,OAAO,eAAe,cAAc,YAAY,KAAK;EACnD,MAAM,QAAQ,yBAAyB;EAcvC,MAAM,QAAQ,WAAW,QAAQ,IAAI,qBAAqB;EAI1D,MAAM,gBAAgB,WAAW,QAAQ,IAAI,gBAAgB,KAAK;EASlE,MAAM,8BAA8B,gCAAgC,WAAW;EAC/E,MAAM,kBAAkB,sBAAsB,WAAW,QAAQ;EACjE,IAAI,UAAU,MACZ,gBAAgB,IAAI,sBAAsB,MAAM;EAElD,MAAM,6BAA6B,oCACjC,4BACD;EACD,IAAI,+BAA+B,MACjC,gBAAgB,IAAI,sCAAsC,2BAA2B;EAEvF,MAAM,UAAU,wBAAwB,YAAY,gBAAgB;EAEpE,MAAM,mBAAmB,uBAAuB,IAAI,GAChD,MACC,4BAA4B,IAAI;EAUrC,OAAO,sBANgB,qBAAqB;GAC1C,gBAJqB,0BAA0B,SAAS,EACxD,iBAAiB,QAAQ,iBAC1B,CAEe;GACd;GACA,2BAA2B;GAC5B,CAE0C,QACzC,yBACE,YAAY;GACV,kBAAkB;GAClB,MAAM,8BAA8B,0BAA0B,QAAQ;GACtE,IAAI;GAEJ,IAAI;IACF,WAAW,MAAM,oBACf,SACA,SACA,6BACA,cACD;YACM,OAAO;IACd,IAAI,QAAQ,IAAI,aAAa,cAC3B,mBAAmB,MAAM;IAE3B,MAAM;;GAGR,OAAO,uBAAuB,UAAU,SAAS;IAC/C,UAAU,QAAQ;IAClB,eAAe,QAAQ;IACvB,YAAY,QAAQ;IACpB,gBAAgB;IACjB,CAAC;KAEJ,EAAE,aAAa,IAAI,IAAI,QAAQ,IAAI,CAAC,UAAU,CAC/C,CACF"}
1
+ {"version":3,"file":"app-rsc-handler.js","names":[],"sources":["../../src/server/app-rsc-handler.ts"],"sourcesContent":["import type {\n NextHeader,\n NextI18nConfig,\n NextRedirect,\n NextRewrite,\n} from \"../config/next-config.js\";\nimport {\n isExternalUrl,\n matchRedirect,\n matchRewrite,\n proxyExternalRequest,\n requestContextFromRequest,\n sanitizeDestination,\n type BasePathMatchState,\n} from \"../config/config-matchers.js\";\nimport { headersContextFromRequest } from \"vinext/shims/headers\";\nimport {\n ACTION_REVALIDATED_HEADER,\n NEXT_ACTION_HEADER,\n RSC_ACTION_HEADER,\n RSC_HEADER,\n VINEXT_MW_CTX_HEADER,\n VINEXT_PRERENDER_ROUTE_PARAMS_HEADER,\n} from \"./headers.js\";\nimport { ensureFetchPatch, setCurrentFetchSoftTags } from \"vinext/shims/fetch-cache\";\nimport type { ReactFormState } from \"react-dom/client\";\nimport {\n getRequestExecutionContext,\n type ExecutionContextLike,\n} from \"vinext/shims/request-context\";\nimport { pickRootParams, setRootParams, type RootParams } from \"vinext/shims/root-params\";\nimport { createRequestContext, runWithRequestContext } from \"vinext/shims/unified-request-context\";\nimport { flattenErrorCauses } from \"../utils/error-cause.js\";\nimport { hasBasePath } from \"../utils/base-path.js\";\nimport { applyAppMiddleware, type AppMiddlewareContext } from \"./app-middleware.js\";\nimport { mergeMiddlewareResponseHeaders } from \"./app-page-response.js\";\nimport { handleAppPrerenderEndpoint } from \"./app-prerender-endpoints.js\";\nimport {\n createRscRedirectLocation,\n resolveInvalidRscCacheBustingRequest,\n stripRscCacheBustingSearchParam,\n stripRscSuffix,\n} from \"./app-rsc-cache-busting.js\";\nimport { finalizeAppRscResponse } from \"./app-rsc-response-finalizer.js\";\nimport { normalizeRscRequest } from \"./app-rsc-request-normalization.js\";\nimport { normalizeDefaultLocalePathname } from \"./pages-i18n.js\";\nimport { notFoundResponse } from \"./http-error-responses.js\";\nimport { getScriptNonceFromHeaderSources } from \"./csp.js\";\nimport { buildPageCacheTags } from \"./implicit-tags.js\";\nimport { isImageOptimizationPath } from \"./image-optimization.js\";\nimport { handleMetadataRouteRequest } from \"./metadata-route-response.js\";\nimport type { MiddlewareModule } from \"./middleware-runtime.js\";\nimport { runWithPrerenderWorkUnit } from \"./prerender-work-unit-setup.js\";\nimport { buildPostMwRequestContext } from \"./app-post-middleware-context.js\";\nimport type { AppRscRenderMode } from \"./app-rsc-render-mode.js\";\nimport {\n cloneRequestWithHeaders,\n filterInternalHeaders,\n applyConfigHeadersToResponse,\n normalizeTrailingSlash,\n resolvePublicFileRoute,\n validateImageUrl,\n} from \"./request-pipeline.js\";\nimport {\n prerenderRouteParamsPayloadMatchesRoute,\n readTrustedPrerenderRouteParams,\n serializePrerenderRouteParamsHeader,\n} from \"./prerender-route-params.js\";\n\ntype AppPageParams = Record<string, string | string[]>;\ntype RequestContext = ReturnType<typeof requestContextFromRequest>;\ntype MetadataRoutes = Parameters<typeof handleMetadataRouteRequest>[0][\"metadataRoutes\"];\ntype MakeThenableParams = Parameters<typeof handleMetadataRouteRequest>[0][\"makeThenableParams\"];\ntype StaticParamsMap = Parameters<typeof handleAppPrerenderEndpoint>[1][\"staticParamsMap\"];\ntype RootParamNamesMap = Parameters<\n typeof handleAppPrerenderEndpoint\n>[1][\"rootParamNamesByPattern\"];\n\ntype AppRscMiddlewareContext = AppMiddlewareContext;\n\ntype AppRscHandlerRoute = {\n isDynamic: boolean;\n page?: unknown;\n pattern: string;\n rootParamNames?: readonly string[];\n routeHandler?: unknown;\n routeSegments: readonly string[];\n};\n\ntype AppRscRouteMatch<TRoute> = {\n params: AppPageParams;\n route: TRoute;\n};\n\ntype DispatchMatchedPageOptions<TRoute> = {\n cleanPathname: string;\n formState: ReactFormState | null;\n actionError?: unknown;\n actionFailed?: boolean;\n handlerStart: number;\n interceptionContext: string | null;\n isProgressiveActionRender: boolean;\n isRscRequest: boolean;\n middlewareContext: AppRscMiddlewareContext;\n mountedSlotsHeader: string | null;\n params: AppPageParams;\n staticParamsValidationParams?: AppPageParams;\n rootParams?: RootParams;\n request: Request;\n route: TRoute;\n scriptNonce?: string;\n searchParams: URLSearchParams;\n renderMode: AppRscRenderMode;\n};\n\ntype DispatchMatchedRouteHandlerOptions<TRoute> = {\n cleanPathname: string;\n middlewareContext: AppRscMiddlewareContext;\n /**\n * `null` for non-dynamic routes. Mirrors Next.js' route handler context\n * shape: user code that does `params ? await params : null` resolves to\n * `null` for routes without dynamic segments. Dynamic routes receive the\n * matched params object.\n */\n params: AppPageParams | null;\n request: Request;\n route: TRoute;\n searchParams: URLSearchParams;\n};\n\ntype HandleProgressiveActionRequestOptions = {\n actionId: string | null;\n cleanPathname: string;\n contentType: string;\n middlewareContext: AppRscMiddlewareContext;\n request: Request;\n};\n\n/**\n * Side-effect headers captured during a progressive (no-JS) server action's\n * non-redirect execution. Forwarded onto the page render response so that\n * `cookies().set(...)` and revalidation kinds reach the browser. See\n * `app-server-action-execution.ts` and issue #1483 for the full rationale.\n */\ntype ProgressiveActionSideEffects = {\n pendingCookies: string[];\n draftCookie: string | null | undefined;\n /** Numeric revalidation kind: `0` (none), `1` (static+dynamic), etc. */\n revalidationKind: number;\n};\n\ntype ProgressiveActionFormStateResult =\n | ({\n formState: ReactFormState | null;\n kind: \"form-state\";\n } & ProgressiveActionSideEffects)\n | ({\n actionError: unknown;\n actionFailed: true;\n formState: null;\n kind: \"form-state\";\n } & ProgressiveActionSideEffects);\n\ntype HandleServerActionRequestOptions = {\n actionId: string | null;\n cleanPathname: string;\n contentType: string;\n interceptionContext: string | null;\n isRscRequest: boolean;\n middlewareContext: AppRscMiddlewareContext;\n mountedSlotsHeader: string | null;\n request: Request;\n searchParams: URLSearchParams;\n};\n\ntype RenderNotFoundOptions<TRoute> = {\n isRscRequest: boolean;\n matchedParams?: AppPageParams;\n middlewareContext: AppRscMiddlewareContext;\n request: Request;\n route: TRoute | null;\n scriptNonce?: string;\n};\n\ntype RenderPagesFallbackOptions = {\n isRscRequest: boolean;\n middlewareContext: AppRscMiddlewareContext;\n request: Request;\n url: URL;\n};\n\ntype NavigationContextValue = {\n params: AppPageParams;\n pathname: string;\n searchParams: URLSearchParams;\n};\n\ntype CreateAppRscHandlerOptions<TRoute extends AppRscHandlerRoute> = {\n basePath: string;\n clearRequestContext: () => void;\n configHeaders: NextHeader[];\n configRedirects: NextRedirect[];\n configRewrites: {\n afterFiles: NextRewrite[];\n beforeFiles: NextRewrite[];\n fallback: NextRewrite[];\n };\n draftModeSecret: string;\n dispatchMatchedPage: (options: DispatchMatchedPageOptions<TRoute>) => Promise<Response>;\n dispatchMatchedRouteHandler: (\n options: DispatchMatchedRouteHandlerOptions<TRoute>,\n ) => Promise<Response>;\n ensureInstrumentation?: () => Promise<void>;\n handleProgressiveActionRequest: (\n options: HandleProgressiveActionRequestOptions,\n ) => Promise<Response | ProgressiveActionFormStateResult | null>;\n handleServerActionRequest: (\n options: HandleServerActionRequestOptions,\n ) => Promise<Response | null>;\n i18nConfig: NextI18nConfig | null;\n isMiddlewareProxy: boolean;\n loadPrerenderPagesRoutes?: () => Promise<unknown>;\n makeThenableParams: MakeThenableParams;\n matchRoute: (pathname: string) => AppRscRouteMatch<TRoute> | null;\n metadataRoutes: MetadataRoutes;\n middlewareModule: MiddlewareModule | null;\n publicFiles: ReadonlySet<string>;\n renderNotFound: (options: RenderNotFoundOptions<TRoute>) => Promise<Response | null>;\n renderPagesFallback?: (options: RenderPagesFallbackOptions) => Promise<Response | null>;\n rootParamNamesByPattern?: RootParamNamesMap;\n setNavigationContext: (context: NavigationContextValue) => void;\n staticParamsMap: StaticParamsMap;\n trailingSlash: boolean;\n validateDevRequestOrigin?: (request: Request) => Response | null;\n};\n\nfunction hasProperty<TKey extends PropertyKey>(\n value: object,\n key: TKey,\n): value is object & Record<TKey, unknown> {\n return key in value;\n}\n\nfunction isExecutionContextLike(value: unknown): value is ExecutionContextLike {\n if (!value || typeof value !== \"object\") return false;\n return hasProperty(value, \"waitUntil\") && typeof value.waitUntil === \"function\";\n}\n\n// TODO(#1333): once App Router supports `basePath: false` rules (see\n// `normalizeRscRequest` — it 404s out-of-basePath requests before they\n// reach this code), pass `hadBasePath` here and skip the prefix when\n// false, mirroring the same guard in `prod-server.ts` and `deploy.ts`.\nfunction redirectDestinationWithBasePath(destination: string, basePath: string): string {\n if (!basePath || isExternalUrl(destination) || hasBasePath(destination, basePath)) {\n return destination;\n }\n return basePath + destination;\n}\n\nasync function applyRewrite(\n options: {\n basePathState: BasePathMatchState;\n clearRequestContext: () => void;\n request: Request;\n requestContext: RequestContext;\n rewrites: NextRewrite[];\n },\n cleanPathname: string,\n): Promise<Response | string | null> {\n if (!options.rewrites.length) return null;\n\n const rewritten = matchRewrite(\n cleanPathname,\n options.rewrites,\n options.requestContext,\n options.basePathState,\n );\n if (!rewritten) return null;\n\n if (isExternalUrl(rewritten)) {\n options.clearRequestContext();\n return proxyExternalRequest(options.request, rewritten);\n }\n\n return rewritten;\n}\n\nfunction applyConfigHeadersToMiddlewareRedirect(\n response: Response,\n options: {\n basePathState: BasePathMatchState;\n configHeaders: NextHeader[];\n pathname: string;\n requestContext: RequestContext;\n },\n): Response {\n // Non-redirect middleware responses still pass through finalization, where\n // config headers are applied once. Redirects skip finalization to avoid\n // mutating immutable redirect headers, so they need the earlier header layer here.\n if (response.status < 300 || response.status >= 400) return response;\n if (!options.configHeaders.length) return response;\n\n const headers = new Headers();\n applyConfigHeadersToResponse(headers, {\n configHeaders: options.configHeaders,\n pathname: options.pathname,\n requestContext: options.requestContext,\n basePathState: options.basePathState,\n });\n\n if (!headers.entries().next().done) {\n mergeMiddlewareResponseHeaders(headers, response.headers);\n return new Response(response.body, {\n status: response.status,\n statusText: response.statusText,\n headers,\n });\n }\n\n return response;\n}\n\nasync function handleAppRscRequest<TRoute extends AppRscHandlerRoute>(\n options: CreateAppRscHandlerOptions<TRoute>,\n request: Request,\n preMiddlewareRequestContext: RequestContext,\n isDataRequest: boolean,\n): Promise<Response> {\n const handlerStart = process.env.NODE_ENV !== \"production\" ? performance.now() : 0;\n\n if (process.env.NODE_ENV !== \"production\") {\n const originBlock = options.validateDevRequestOrigin?.(request);\n if (originBlock) return originBlock;\n }\n\n const normalized = normalizeRscRequest(request, options.basePath);\n if (normalized instanceof Response) return normalized;\n\n const { url, isRscRequest, interceptionContextHeader, mountedSlotsHeader, renderMode } =\n normalized;\n let { pathname, cleanPathname } = normalized;\n // Canonical (external) pathname the user requested. Middleware rewrites and\n // next.config.js rewrites mutate `cleanPathname` so internal route matching\n // can find the destination page, but hooks like `usePathname()` must reflect\n // the original URL the user sees in the address bar.\n // Matches Next.js: test/e2e/app-dir/hooks/hooks.test.ts —\n // \"should have the canonical url pathname on rewrite\"\n const canonicalPathname = cleanPathname;\n\n // The request reached this point so it was either under basePath (stripped\n // by normalizeRscRequest) or basePath is empty. In both cases the matcher\n // gating below treats default (basePath: true) rules as eligible. The App\n // Router does not yet support `basePath: false` rules — they would need a\n // pre-strip hook in normalizeRscRequest to fire. Tracked as follow-up to\n // issue #1333.\n const basePathState = { basePath: options.basePath, hadBasePath: true };\n\n const prerenderEndpointResponse = await handleAppPrerenderEndpoint(request, {\n isPrerenderEnabled() {\n return process.env.VINEXT_PRERENDER === \"1\";\n },\n loadPagesRoutes: options.loadPrerenderPagesRoutes,\n pathname,\n rootParamNamesByPattern: options.rootParamNamesByPattern,\n staticParamsMap: options.staticParamsMap,\n });\n if (prerenderEndpointResponse) return prerenderEndpointResponse;\n\n const trailingSlashRedirect = normalizeTrailingSlash(\n pathname,\n options.basePath,\n options.trailingSlash,\n url.search,\n );\n if (trailingSlashRedirect) return trailingSlashRedirect;\n\n // Default-locale path normalisation (issue #1336, item 4). Next.js\n // splices in the (domain-aware) default locale on every request that\n // arrives without a locale prefix before running config redirect / rewrite\n // / header matching. Mirrors resolve-routes.ts lines ~250-263.\n //\n // Defined once here so the same helper is reused for the redirect match\n // below, the middleware-redirect config header match further down, and the\n // post-middleware rewrite matches. `i18nConfig` and `url.hostname` are\n // request-scoped constants from this point on.\n const matchPathname = (p: string): string =>\n normalizeDefaultLocalePathname(p, options.i18nConfig, { hostname: url.hostname });\n\n const redirectPathname = matchPathname(stripRscSuffix(pathname));\n const redirect = matchRedirect(\n redirectPathname,\n options.configRedirects,\n preMiddlewareRequestContext,\n basePathState,\n );\n if (redirect) {\n const destination = sanitizeDestination(\n redirectDestinationWithBasePath(redirect.destination, options.basePath),\n );\n const location =\n isRscRequest && request.headers.get(RSC_HEADER) === \"1\"\n ? await createRscRedirectLocation(destination, request)\n : destination;\n return new Response(null, {\n status: redirect.permanent ? 308 : 307,\n headers: { Location: location },\n });\n }\n\n const rscCacheBustingRedirect = await resolveInvalidRscCacheBustingRequest({\n isRscRequest,\n request,\n });\n if (rscCacheBustingRedirect) return rscCacheBustingRedirect;\n\n const middlewareContext: AppRscMiddlewareContext = {\n headers: null,\n requestHeaders: null,\n status: null,\n };\n\n if (options.middlewareModule) {\n const middlewareResult = await applyAppMiddleware({\n basePath: options.basePath,\n cleanPathname,\n context: middlewareContext,\n i18nConfig: options.i18nConfig,\n isDataRequest,\n isProxy: options.isMiddlewareProxy,\n module: options.middlewareModule,\n request,\n trailingSlash: options.trailingSlash,\n });\n if (middlewareResult.kind === \"response\") {\n return applyConfigHeadersToMiddlewareRedirect(middlewareResult.response, {\n basePathState,\n configHeaders: options.configHeaders,\n pathname: matchPathname(cleanPathname),\n requestContext: preMiddlewareRequestContext,\n });\n }\n\n cleanPathname = middlewareResult.cleanPathname;\n if (middlewareResult.search !== null) {\n url.search = middlewareResult.search;\n }\n }\n\n const scriptNonce = getScriptNonceFromHeaderSources(request.headers, middlewareContext.headers);\n const postMiddlewareRequestContext = buildPostMwRequestContext(request);\n\n // Rewrites (beforeFiles, afterFiles, fallback) use `matchPathname` from\n // above to splice in the default locale before matching. Route matching\n // itself continues to use the un-prefixed `cleanPathname` because App\n // Router files live under `app/...` with no locale segment. See issue\n // #1336 item 4 / pages-i18n.normalizeDefaultLocalePathname.\n const beforeFilesRewrite = await applyRewrite(\n {\n basePathState,\n clearRequestContext: options.clearRequestContext,\n request,\n requestContext: postMiddlewareRequestContext,\n rewrites: options.configRewrites.beforeFiles,\n },\n matchPathname(cleanPathname),\n );\n if (beforeFilesRewrite instanceof Response) return beforeFilesRewrite;\n if (beforeFilesRewrite) cleanPathname = beforeFilesRewrite;\n\n if (isImageOptimizationPath(cleanPathname)) {\n const imageUrlResult = validateImageUrl(url.searchParams.get(\"url\"), request.url);\n if (imageUrlResult instanceof Response) return imageUrlResult;\n return Response.redirect(new URL(imageUrlResult, url.origin).href, 302);\n }\n\n const metadataRouteResponse = await handleMetadataRouteRequest({\n metadataRoutes: options.metadataRoutes,\n cleanPathname,\n makeThenableParams: options.makeThenableParams,\n });\n if (metadataRouteResponse) return metadataRouteResponse;\n\n const publicFileResponse = resolvePublicFileRoute({\n cleanPathname,\n middlewareContext,\n pathname,\n publicFiles: options.publicFiles,\n request,\n });\n if (publicFileResponse) {\n options.clearRequestContext();\n return publicFileResponse;\n }\n\n if (isRscRequest) {\n stripRscCacheBustingSearchParam(url);\n }\n\n options.setNavigationContext({\n pathname: canonicalPathname,\n searchParams: url.searchParams,\n params: {},\n });\n\n // Eagerly seed `setRootParams` from the current cleanPathname before any\n // action dispatch so that user code which reads `unstable_rootParams()`\n // inside route handlers, `\"use cache\"` functions, and the page rerender\n // that follows a successful server action observes the matched layout's\n // root params. Without this seeding the rootParams remain null until the\n // post-action match block below runs, which is too late for action\n // execution and route-handler dispatch (both happen earlier).\n //\n // The route is matched against the pre-rewrite cleanPathname here. If the\n // afterFiles / fallback rewrites further down land on a different route,\n // the second `setRootParams` call below replaces this value before the\n // page renders, so there is no stale-value risk for ordinary page renders.\n // For action requests we intentionally do not re-run rewrites — actions\n // are always processed against the cleanPathname they were posted to.\n const preActionMatch = options.matchRoute(cleanPathname);\n if (preActionMatch) {\n setRootParams(pickRootParams(preActionMatch.params, preActionMatch.route.rootParamNames));\n }\n\n const actionId =\n request.headers.get(RSC_ACTION_HEADER) ?? request.headers.get(NEXT_ACTION_HEADER);\n const contentType = request.headers.get(\"content-type\") || \"\";\n\n const progressiveActionResult = await options.handleProgressiveActionRequest({\n actionId,\n cleanPathname,\n contentType,\n middlewareContext,\n request,\n });\n if (progressiveActionResult instanceof Response) return progressiveActionResult;\n const isProgressiveActionRender = progressiveActionResult?.kind === \"form-state\";\n const formState = isProgressiveActionRender ? progressiveActionResult.formState : null;\n const failedProgressiveActionResult =\n isProgressiveActionRender && \"actionFailed\" in progressiveActionResult\n ? progressiveActionResult\n : null;\n const actionFailed = failedProgressiveActionResult !== null;\n const actionError = failedProgressiveActionResult?.actionError;\n\n const serverActionResponse = await options.handleServerActionRequest({\n actionId,\n cleanPathname,\n contentType,\n interceptionContext: interceptionContextHeader,\n isRscRequest,\n middlewareContext,\n mountedSlotsHeader,\n request,\n searchParams: url.searchParams,\n });\n if (serverActionResponse) return serverActionResponse;\n\n let match = preActionMatch;\n if (!match || match.route.isDynamic) {\n const afterFilesRewrite = await applyRewrite(\n {\n basePathState,\n clearRequestContext: options.clearRequestContext,\n request,\n requestContext: postMiddlewareRequestContext,\n rewrites: options.configRewrites.afterFiles,\n },\n matchPathname(cleanPathname),\n );\n if (afterFilesRewrite instanceof Response) return afterFilesRewrite;\n if (afterFilesRewrite) {\n cleanPathname = afterFilesRewrite;\n match = options.matchRoute(cleanPathname);\n }\n }\n\n if (!match) {\n const fallbackRewrite = await applyRewrite(\n {\n basePathState,\n clearRequestContext: options.clearRequestContext,\n request,\n requestContext: postMiddlewareRequestContext,\n rewrites: options.configRewrites.fallback,\n },\n matchPathname(cleanPathname),\n );\n if (fallbackRewrite instanceof Response) return fallbackRewrite;\n if (fallbackRewrite) {\n cleanPathname = fallbackRewrite;\n match = options.matchRoute(cleanPathname);\n }\n }\n\n if (!match) {\n // Dev-only favicon short-circuit: browsers auto-request /favicon.ico on\n // every page load. Don't compile/render the not-found page for it.\n // Check `canonicalPathname` (the original browser-requested URL) so a\n // middleware rewrite that lands on `/favicon.ico` still falls through to\n // the normal not-found render.\n // Matches Next.js: packages/next/src/server/lib/router-server.ts —\n // condition `parsedUrl.pathname === '/favicon.ico'`.\n if (process.env.NODE_ENV !== \"production\" && canonicalPathname === \"/favicon.ico\") {\n options.clearRequestContext();\n return new Response(\"\", { status: 404 });\n }\n\n const pagesFallbackResponse = await options.renderPagesFallback?.({\n isRscRequest,\n middlewareContext,\n request,\n url,\n });\n if (pagesFallbackResponse) {\n options.clearRequestContext();\n return pagesFallbackResponse;\n }\n\n const renderedNotFoundResponse = await options.renderNotFound({\n isRscRequest,\n middlewareContext,\n request,\n route: null,\n scriptNonce,\n });\n if (renderedNotFoundResponse) return renderedNotFoundResponse;\n\n options.clearRequestContext();\n const headers = new Headers();\n mergeMiddlewareResponseHeaders(headers, middlewareContext.headers);\n return notFoundResponse({ headers });\n }\n\n const { route, params } = match;\n const prerenderRouteParamsPayload = readTrustedPrerenderRouteParams(request);\n const prerenderRouteParams = prerenderRouteParamsPayloadMatchesRoute(\n prerenderRouteParamsPayload,\n route.pattern,\n params,\n )\n ? prerenderRouteParamsPayload.params\n : null;\n const renderParams = prerenderRouteParams ?? params;\n options.setNavigationContext({\n pathname: canonicalPathname,\n searchParams: url.searchParams,\n params: renderParams,\n });\n const rootParams = pickRootParams(renderParams, route.rootParamNames);\n setRootParams(rootParams);\n\n if (route.routeHandler) {\n setCurrentFetchSoftTags(\n buildPageCacheTags(cleanPathname, [], [...route.routeSegments], \"route\"),\n );\n return options.dispatchMatchedRouteHandler({\n cleanPathname,\n middlewareContext,\n // Non-dynamic routes report params as `null` to match Next.js. Internal\n // bookkeeping above (navigation context, root params) keeps the matched\n // object (always `{}` for non-dynamic) so `useParams()` etc. still see\n // an object shape; only the user-facing handler context surfaces null.\n params: route.isDynamic ? renderParams : null,\n request,\n route,\n searchParams: url.searchParams,\n });\n }\n\n const pageResponse = await options.dispatchMatchedPage({\n cleanPathname,\n formState,\n actionError,\n actionFailed,\n handlerStart,\n interceptionContext: interceptionContextHeader,\n isProgressiveActionRender,\n isRscRequest,\n middlewareContext,\n mountedSlotsHeader,\n params: renderParams,\n staticParamsValidationParams: prerenderRouteParams === null ? undefined : params,\n rootParams,\n request,\n route,\n scriptNonce,\n searchParams: url.searchParams,\n renderMode,\n });\n\n // No-JS progressive form actions write cookies via cookies().set() / draftMode()\n // *during action execution*, before the page rerender begins. Those writes only\n // exist on the request-scoped headers state; the page-render path never flushes\n // them. We attach them here so the rendered Response carries the action's\n // Set-Cookie headers and revalidation marker, mirroring Next.js'\n // res.setHeader('set-cookie', ...) flush in action-handler.ts / app-render.tsx.\n // Issue: https://github.com/cloudflare/vinext/issues/1483\n if (isProgressiveActionRender) {\n return applyProgressiveActionSideEffects(pageResponse, progressiveActionResult);\n }\n return pageResponse;\n}\n\n/**\n * Append `Set-Cookie` headers and the `x-action-revalidated` marker captured\n * during progressive (no-JS) server action execution to the page render\n * response. See issue #1483.\n *\n * Falls back to rebuilding the response when the headers object is immutable\n * (e.g. `Response.redirect()`), so cookies set by the action ride out on a\n * redirect issued during the rerender too.\n */\nfunction applyProgressiveActionSideEffects(\n response: Response,\n sideEffects: ProgressiveActionFormStateResult,\n): Response {\n const hasPendingCookies = sideEffects.pendingCookies.length > 0;\n const hasDraftCookie = Boolean(sideEffects.draftCookie);\n const hasRevalidationKind = sideEffects.revalidationKind !== 0;\n if (!hasPendingCookies && !hasDraftCookie && !hasRevalidationKind) {\n return response;\n }\n\n const applyTo = (headers: Headers): void => {\n for (const cookie of sideEffects.pendingCookies) {\n headers.append(\"Set-Cookie\", cookie);\n }\n if (sideEffects.draftCookie) {\n headers.append(\"Set-Cookie\", sideEffects.draftCookie);\n }\n if (hasRevalidationKind) {\n headers.set(ACTION_REVALIDATED_HEADER, JSON.stringify(sideEffects.revalidationKind));\n }\n };\n\n try {\n applyTo(response.headers);\n return response;\n } catch {\n // Headers were immutable (Response.redirect()/Response.error()) — rebuild\n // with a fresh mutable Headers seeded from the original response.\n const headers = new Headers(response.headers);\n applyTo(headers);\n return new Response(response.body, {\n status: response.status,\n statusText: response.statusText,\n headers,\n });\n }\n}\n\nexport function createAppRscHandler<TRoute extends AppRscHandlerRoute>(\n options: CreateAppRscHandlerOptions<TRoute>,\n): (request: Request, ctx: unknown) => Promise<Response> {\n return async function appRscHandler(rawRequest, ctx) {\n await options.ensureInstrumentation?.();\n\n // Strip forged internal headers at the App Router request boundary.\n // Must happen BEFORE headersContextFromRequest() and\n // requestContextFromRequest() so the captured context never contains\n // attacker-controlled internal headers. This is the correct boundary\n // for pure App Router requests; in hybrid app+pages mode the connect\n // handler already filtered headers upstream and x-vinext-mw-ctx\n // (not in INTERNAL_HEADERS) carries the forwarded middleware context.\n // srvx's NodeRequestHeaders reads from rawHeaders for iteration but falls\n // back to req.headers for .get() / .has(). In the dev server we add\n // x-vinext-mw-ctx to req.headers after the Request is built, so it is\n // visible to .get() but lost when filterInternalHeaders iterates. Read it\n // BEFORE iterating so applyForwardedMiddlewareContext can skip middleware.\n const mwCtx = rawRequest.headers.get(VINEXT_MW_CTX_HEADER);\n // Capture `x-nextjs-data` before filtering — the middleware redirect\n // protocol needs to know whether the inbound request was a `_next/data`\n // fetch to emit `x-nextjs-redirect` instead of an HTTP redirect.\n const isDataRequest = rawRequest.headers.get(\"x-nextjs-data\") === \"1\";\n // Read the trusted prerender route params before filtering strips the\n // route-params header (it IS in VINEXT_INTERNAL_HEADERS), then re-attach the\n // validated value below so the second read in handleAppRscRequest still sees\n // it. The secret was already verified upstream at prod-server's\n // nodeToWebRequest boundary; the surviving secret header (NOT in either\n // internal-header list) lets readTrustedPrerenderRouteParams's\n // VINEXT_PRERENDER gate pass on the reconstructed request. If the secret\n // header is ever added to VINEXT_INTERNAL_HEADERS, that second read breaks.\n const prerenderRouteParamsPayload = readTrustedPrerenderRouteParams(rawRequest);\n const filteredHeaders = filterInternalHeaders(rawRequest.headers);\n if (mwCtx !== null) {\n filteredHeaders.set(VINEXT_MW_CTX_HEADER, mwCtx);\n }\n const prerenderRouteParamsHeader = serializePrerenderRouteParamsHeader(\n prerenderRouteParamsPayload,\n );\n if (prerenderRouteParamsHeader !== null) {\n filteredHeaders.set(VINEXT_PRERENDER_ROUTE_PARAMS_HEADER, prerenderRouteParamsHeader);\n }\n const request = cloneRequestWithHeaders(rawRequest, filteredHeaders);\n\n const executionContext = isExecutionContextLike(ctx)\n ? ctx\n : (getRequestExecutionContext() ?? null);\n const headersContext = headersContextFromRequest(request, {\n draftModeSecret: options.draftModeSecret,\n });\n const requestContext = createRequestContext({\n headersContext,\n executionContext,\n unstableCacheRevalidation: \"background\",\n });\n\n return runWithRequestContext(requestContext, () =>\n runWithPrerenderWorkUnit(\n async () => {\n ensureFetchPatch();\n const preMiddlewareRequestContext = requestContextFromRequest(request);\n let response: Response;\n\n try {\n response = await handleAppRscRequest(\n options,\n request,\n preMiddlewareRequestContext,\n isDataRequest,\n );\n } catch (error) {\n if (process.env.NODE_ENV !== \"production\") {\n flattenErrorCauses(error);\n }\n throw error;\n }\n\n return finalizeAppRscResponse(response, request, {\n basePath: options.basePath,\n configHeaders: options.configHeaders,\n i18nConfig: options.i18nConfig,\n requestContext: preMiddlewareRequestContext,\n });\n },\n { route: () => new URL(request.url).pathname },\n ),\n );\n };\n}\n"],"mappings":"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"}
@@ -30,7 +30,8 @@ type NormalizedRscRequest = {
30
30
  * 4. Collapse double-slashes, resolve `.` and `..` segments (normalizePath)
31
31
  * 5. basePath check + strip — 404 when pathname lacks the basePath prefix.
32
32
  * `/__vinext/` bypasses this for internal prerender endpoints.
33
- * 6. RSC detection: `.rsc` suffix only. RSC headers do not select payload
33
+ * 6. RSC detection: `.rsc` suffix, or Next-style `RSC: 1` plus the internal
34
+ * `_rsc` cache-busting query. The header alone does not select payload
34
35
  * rendering at the canonical HTML URL, so caches that ignore Vary cannot
35
36
  * store Flight responses under HTML URLs.
36
37
  * 7. cleanPathname — pathname with `.rsc` suffix stripped
@@ -27,7 +27,8 @@ import { parseClientReuseManifestHeader } from "./client-reuse-manifest.js";
27
27
  * 4. Collapse double-slashes, resolve `.` and `..` segments (normalizePath)
28
28
  * 5. basePath check + strip — 404 when pathname lacks the basePath prefix.
29
29
  * `/__vinext/` bypasses this for internal prerender endpoints.
30
- * 6. RSC detection: `.rsc` suffix only. RSC headers do not select payload
30
+ * 6. RSC detection: `.rsc` suffix, or Next-style `RSC: 1` plus the internal
31
+ * `_rsc` cache-busting query. The header alone does not select payload
31
32
  * rendering at the canonical HTML URL, so caches that ignore Vary cannot
32
33
  * store Flight responses under HTML URLs.
33
34
  * 7. cleanPathname — pathname with `.rsc` suffix stripped
@@ -54,7 +55,7 @@ function normalizeRscRequest(request, basePath) {
54
55
  if (!hasBasePath(pathname, basePath) && !pathname.startsWith("/__vinext/")) return notFoundResponse();
55
56
  pathname = stripBasePath(pathname, basePath);
56
57
  }
57
- const isRscRequest = pathname.endsWith(".rsc");
58
+ const isRscRequest = pathname.endsWith(".rsc") || request.headers.get("RSC") === "1" && url.searchParams.has("_rsc");
58
59
  const cleanPathname = stripRscSuffix(pathname);
59
60
  const interceptionContextHeader = normalizeInterceptionContextHeader(request.headers.get(VINEXT_INTERCEPTION_CONTEXT_HEADER));
60
61
  const mountedSlotsHeader = normalizeMountedSlotsHeader(request.headers.get(VINEXT_MOUNTED_SLOTS_HEADER));
@@ -1 +1 @@
1
- {"version":3,"file":"app-rsc-request-normalization.js","names":[],"sources":["../../src/server/app-rsc-request-normalization.ts"],"sourcesContent":["import { normalizePath } from \"./normalize-path.js\";\nimport { normalizePathnameForRouteMatchStrict } from \"../routing/utils.js\";\nimport { guardProtocolRelativeUrl } from \"./request-pipeline.js\";\nimport { hasBasePath, stripBasePath } from \"../utils/base-path.js\";\nimport {\n VINEXT_CLIENT_REUSE_MANIFEST_HEADER,\n VINEXT_INTERCEPTION_CONTEXT_HEADER,\n VINEXT_MOUNTED_SLOTS_HEADER,\n VINEXT_RSC_RENDER_MODE_HEADER,\n} from \"./headers.js\";\nimport {\n parseClientReuseManifestHeader,\n type ClientReuseManifestParseResult,\n} from \"./client-reuse-manifest.js\";\nimport { normalizeInterceptionContextHeader } from \"./app-interception-context-header.js\";\nimport { normalizeMountedSlotsHeader } from \"./app-mounted-slots-header.js\";\nimport { stripRscSuffix } from \"./app-rsc-cache-busting.js\";\nimport {\n APP_RSC_RENDER_MODE_NAVIGATION,\n parseAppRscRenderMode,\n type AppRscRenderMode,\n} from \"./app-rsc-render-mode.js\";\nimport { badRequestResponse, notFoundResponse } from \"./http-error-responses.js\";\n\nexport { normalizeMountedSlotsHeader } from \"./app-mounted-slots-header.js\";\n\nexport type NormalizedRscRequest = {\n /** Parsed URL. Callers may mutate `url.search` after middleware runs. */\n url: URL;\n /** Normalized pathname with basePath stripped. Used for all internal routing. */\n pathname: string;\n /** Pathname with `.rsc` suffix removed. Used for route matching and navigation context. */\n cleanPathname: string;\n /** True when the request targets a canonical `.rsc` payload URL. */\n isRscRequest: boolean;\n /** Sanitized X-Vinext-Interception-Context header (null bytes stripped). null when absent. */\n interceptionContextHeader: string | null;\n /** Normalized x-vinext-mounted-slots header (deduplicated, sorted). null when absent or blank. */\n mountedSlotsHeader: string | null;\n /** Semantic RSC payload mode. HTML requests always normalize to \"navigation\". */\n renderMode: AppRscRenderMode;\n /** Disabled ClientReuseManifest hint. Never authorizes skip transport in this stage. */\n clientReuseManifest: ClientReuseManifestParseResult;\n};\n\n/**\n * Normalize an App Router RSC request.\n *\n * Performs all security-sensitive and compatibility-sensitive preprocessing before\n * route matching. The ordering of steps is security-critical — changing it introduces\n * vulnerabilities:\n *\n * 1. Parse URL\n * 2. Protocol-relative URL guard — on the raw pathname, BEFORE normalizePath collapses\n * `//` to `/`. If the guard ran after normalization, `//evil.com` → `/evil.com`\n * would bypass the check and reach the trailing-slash redirector, which echoes the\n * path into a `Location` header that browsers interpret as protocol-relative.\n * 3. Strict percent-decode each segment — throws on malformed sequences (→ 400). Must\n * run before basePath check so %2F-encoded slashes cannot create fake basePath prefixes.\n * 4. Collapse double-slashes, resolve `.` and `..` segments (normalizePath)\n * 5. basePath check + strip — 404 when pathname lacks the basePath prefix.\n * `/__vinext/` bypasses this for internal prerender endpoints.\n * 6. RSC detection: `.rsc` suffix only. RSC headers do not select payload\n * rendering at the canonical HTML URL, so caches that ignore Vary cannot\n * store Flight responses under HTML URLs.\n * 7. cleanPathname — pathname with `.rsc` suffix stripped\n * 8. Sanitize X-Vinext-Interception-Context — strip null bytes (header injection)\n * 9. Normalize x-vinext-mounted-slots — dedup and sort for canonical cache keys\n * 10. Read semantic render mode for refresh/action payload rendering\n * 11. Parse disabled ClientReuseManifest hints on canonical RSC payload requests\n *\n * @returns A 400 or 404 Response for invalid or out-of-scope inputs,\n * or a NormalizedRscRequest for valid requests.\n */\nexport function normalizeRscRequest(\n request: Request,\n basePath: string,\n): Response | NormalizedRscRequest {\n const url = new URL(request.url);\n\n // Step 2: Guard against protocol-relative open redirects on the raw pathname.\n // normalizePath (step 4) would collapse //evil.com to /evil.com, causing the\n // guard to miss it. Raw pathname must be checked first.\n const protoGuard = guardProtocolRelativeUrl(url.pathname);\n if (protoGuard) return protoGuard;\n\n // Step 3: Strict segment-wise percent-decode. Preserves encoded path delimiters\n // (%2F stays %2F) to prevent encoded slashes from acting as path separators.\n // Throws on malformed sequences like %GG — caller must return 400.\n let decoded: string;\n try {\n decoded = normalizePathnameForRouteMatchStrict(url.pathname);\n } catch {\n return badRequestResponse();\n }\n\n // Step 4: Collapse double-slashes and resolve . / .. segments.\n let pathname = normalizePath(decoded);\n\n // Step 5: basePath check and strip.\n // Skipped when basePath is empty (no basePath configured).\n // /__vinext/ prefix bypasses the check for internal prerender endpoints\n // that must be reachable regardless of basePath configuration.\n if (basePath) {\n if (!hasBasePath(pathname, basePath) && !pathname.startsWith(\"/__vinext/\")) {\n return notFoundResponse();\n }\n pathname = stripBasePath(pathname, basePath);\n }\n\n // Steps 6-7: RSC detection and cleanPathname.\n const isRscRequest = pathname.endsWith(\".rsc\");\n const cleanPathname = stripRscSuffix(pathname);\n\n // Step 8: Validate and sanitize X-Vinext-Interception-Context.\n //\n // The legitimate value is always a same-origin URL pathname (`/feed`,\n // `/photos/42`, …) emitted by the vinext browser entry. We strip null bytes\n // (header-injection defense), bound length, and require a pathname-shaped\n // value so an attacker cannot fan out unbounded distinct values into the\n // RSC / optimistic-route cache keys. See SECURITY-AUDIT-2026-05.md F-PROD-1.\n const interceptionContextHeader = normalizeInterceptionContextHeader(\n request.headers.get(VINEXT_INTERCEPTION_CONTEXT_HEADER),\n );\n\n // Step 9: Normalize mounted-slots header for canonical cache keying.\n const mountedSlotsHeader = normalizeMountedSlotsHeader(\n request.headers.get(VINEXT_MOUNTED_SLOTS_HEADER),\n );\n const renderMode = isRscRequest\n ? parseAppRscRenderMode(request.headers.get(VINEXT_RSC_RENDER_MODE_HEADER))\n : APP_RSC_RENDER_MODE_NAVIGATION;\n const clientReuseManifest = isRscRequest\n ? parseClientReuseManifestHeader(request.headers.get(VINEXT_CLIENT_REUSE_MANIFEST_HEADER))\n : ({ kind: \"absent\" } satisfies ClientReuseManifestParseResult);\n\n return {\n clientReuseManifest,\n url,\n pathname,\n cleanPathname,\n isRscRequest,\n interceptionContextHeader,\n mountedSlotsHeader,\n renderMode,\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA0EA,SAAgB,oBACd,SACA,UACiC;CACjC,MAAM,MAAM,IAAI,IAAI,QAAQ,IAAI;CAKhC,MAAM,aAAa,yBAAyB,IAAI,SAAS;CACzD,IAAI,YAAY,OAAO;CAKvB,IAAI;CACJ,IAAI;EACF,UAAU,qCAAqC,IAAI,SAAS;SACtD;EACN,OAAO,oBAAoB;;CAI7B,IAAI,WAAW,cAAc,QAAQ;CAMrC,IAAI,UAAU;EACZ,IAAI,CAAC,YAAY,UAAU,SAAS,IAAI,CAAC,SAAS,WAAW,aAAa,EACxE,OAAO,kBAAkB;EAE3B,WAAW,cAAc,UAAU,SAAS;;CAI9C,MAAM,eAAe,SAAS,SAAS,OAAO;CAC9C,MAAM,gBAAgB,eAAe,SAAS;CAS9C,MAAM,4BAA4B,mCAChC,QAAQ,QAAQ,IAAI,mCAAmC,CACxD;CAGD,MAAM,qBAAqB,4BACzB,QAAQ,QAAQ,IAAI,4BAA4B,CACjD;CACD,MAAM,aAAa,eACf,sBAAsB,QAAQ,QAAQ,IAAI,8BAA8B,CAAC,GACzE;CAKJ,OAAO;EACL,qBAL0B,eACxB,+BAA+B,QAAQ,QAAQ,IAAI,oCAAoC,CAAC,GACvF,EAAE,MAAM,UAAU;EAIrB;EACA;EACA;EACA;EACA;EACA;EACA;EACD"}
1
+ {"version":3,"file":"app-rsc-request-normalization.js","names":[],"sources":["../../src/server/app-rsc-request-normalization.ts"],"sourcesContent":["import { normalizePath } from \"./normalize-path.js\";\nimport { normalizePathnameForRouteMatchStrict } from \"../routing/utils.js\";\nimport { guardProtocolRelativeUrl } from \"./request-pipeline.js\";\nimport { hasBasePath, stripBasePath } from \"../utils/base-path.js\";\nimport {\n RSC_HEADER,\n VINEXT_CLIENT_REUSE_MANIFEST_HEADER,\n VINEXT_INTERCEPTION_CONTEXT_HEADER,\n VINEXT_MOUNTED_SLOTS_HEADER,\n VINEXT_RSC_RENDER_MODE_HEADER,\n} from \"./headers.js\";\nimport {\n parseClientReuseManifestHeader,\n type ClientReuseManifestParseResult,\n} from \"./client-reuse-manifest.js\";\nimport { normalizeInterceptionContextHeader } from \"./app-interception-context-header.js\";\nimport { normalizeMountedSlotsHeader } from \"./app-mounted-slots-header.js\";\nimport { stripRscSuffix, VINEXT_RSC_CACHE_BUSTING_SEARCH_PARAM } from \"./app-rsc-cache-busting.js\";\nimport {\n APP_RSC_RENDER_MODE_NAVIGATION,\n parseAppRscRenderMode,\n type AppRscRenderMode,\n} from \"./app-rsc-render-mode.js\";\nimport { badRequestResponse, notFoundResponse } from \"./http-error-responses.js\";\n\nexport { normalizeMountedSlotsHeader } from \"./app-mounted-slots-header.js\";\n\nexport type NormalizedRscRequest = {\n /** Parsed URL. Callers may mutate `url.search` after middleware runs. */\n url: URL;\n /** Normalized pathname with basePath stripped. Used for all internal routing. */\n pathname: string;\n /** Pathname with `.rsc` suffix removed. Used for route matching and navigation context. */\n cleanPathname: string;\n /** True when the request targets a canonical `.rsc` payload URL. */\n isRscRequest: boolean;\n /** Sanitized X-Vinext-Interception-Context header (null bytes stripped). null when absent. */\n interceptionContextHeader: string | null;\n /** Normalized x-vinext-mounted-slots header (deduplicated, sorted). null when absent or blank. */\n mountedSlotsHeader: string | null;\n /** Semantic RSC payload mode. HTML requests always normalize to \"navigation\". */\n renderMode: AppRscRenderMode;\n /** Disabled ClientReuseManifest hint. Never authorizes skip transport in this stage. */\n clientReuseManifest: ClientReuseManifestParseResult;\n};\n\n/**\n * Normalize an App Router RSC request.\n *\n * Performs all security-sensitive and compatibility-sensitive preprocessing before\n * route matching. The ordering of steps is security-critical — changing it introduces\n * vulnerabilities:\n *\n * 1. Parse URL\n * 2. Protocol-relative URL guard — on the raw pathname, BEFORE normalizePath collapses\n * `//` to `/`. If the guard ran after normalization, `//evil.com` → `/evil.com`\n * would bypass the check and reach the trailing-slash redirector, which echoes the\n * path into a `Location` header that browsers interpret as protocol-relative.\n * 3. Strict percent-decode each segment — throws on malformed sequences (→ 400). Must\n * run before basePath check so %2F-encoded slashes cannot create fake basePath prefixes.\n * 4. Collapse double-slashes, resolve `.` and `..` segments (normalizePath)\n * 5. basePath check + strip — 404 when pathname lacks the basePath prefix.\n * `/__vinext/` bypasses this for internal prerender endpoints.\n * 6. RSC detection: `.rsc` suffix, or Next-style `RSC: 1` plus the internal\n * `_rsc` cache-busting query. The header alone does not select payload\n * rendering at the canonical HTML URL, so caches that ignore Vary cannot\n * store Flight responses under HTML URLs.\n * 7. cleanPathname — pathname with `.rsc` suffix stripped\n * 8. Sanitize X-Vinext-Interception-Context — strip null bytes (header injection)\n * 9. Normalize x-vinext-mounted-slots — dedup and sort for canonical cache keys\n * 10. Read semantic render mode for refresh/action payload rendering\n * 11. Parse disabled ClientReuseManifest hints on canonical RSC payload requests\n *\n * @returns A 400 or 404 Response for invalid or out-of-scope inputs,\n * or a NormalizedRscRequest for valid requests.\n */\nexport function normalizeRscRequest(\n request: Request,\n basePath: string,\n): Response | NormalizedRscRequest {\n const url = new URL(request.url);\n\n // Step 2: Guard against protocol-relative open redirects on the raw pathname.\n // normalizePath (step 4) would collapse //evil.com to /evil.com, causing the\n // guard to miss it. Raw pathname must be checked first.\n const protoGuard = guardProtocolRelativeUrl(url.pathname);\n if (protoGuard) return protoGuard;\n\n // Step 3: Strict segment-wise percent-decode. Preserves encoded path delimiters\n // (%2F stays %2F) to prevent encoded slashes from acting as path separators.\n // Throws on malformed sequences like %GG — caller must return 400.\n let decoded: string;\n try {\n decoded = normalizePathnameForRouteMatchStrict(url.pathname);\n } catch {\n return badRequestResponse();\n }\n\n // Step 4: Collapse double-slashes and resolve . / .. segments.\n let pathname = normalizePath(decoded);\n\n // Step 5: basePath check and strip.\n // Skipped when basePath is empty (no basePath configured).\n // /__vinext/ prefix bypasses the check for internal prerender endpoints\n // that must be reachable regardless of basePath configuration.\n if (basePath) {\n if (!hasBasePath(pathname, basePath) && !pathname.startsWith(\"/__vinext/\")) {\n return notFoundResponse();\n }\n pathname = stripBasePath(pathname, basePath);\n }\n\n // Steps 6-7: RSC detection and cleanPathname.\n const isRscRequest =\n pathname.endsWith(\".rsc\") ||\n (request.headers.get(RSC_HEADER) === \"1\" &&\n url.searchParams.has(VINEXT_RSC_CACHE_BUSTING_SEARCH_PARAM));\n const cleanPathname = stripRscSuffix(pathname);\n\n // Step 8: Validate and sanitize X-Vinext-Interception-Context.\n //\n // The legitimate value is always a same-origin URL pathname (`/feed`,\n // `/photos/42`, …) emitted by the vinext browser entry. We strip null bytes\n // (header-injection defense), bound length, and require a pathname-shaped\n // value so an attacker cannot fan out unbounded distinct values into the\n // RSC / optimistic-route cache keys. See SECURITY-AUDIT-2026-05.md F-PROD-1.\n const interceptionContextHeader = normalizeInterceptionContextHeader(\n request.headers.get(VINEXT_INTERCEPTION_CONTEXT_HEADER),\n );\n\n // Step 9: Normalize mounted-slots header for canonical cache keying.\n const mountedSlotsHeader = normalizeMountedSlotsHeader(\n request.headers.get(VINEXT_MOUNTED_SLOTS_HEADER),\n );\n const renderMode = isRscRequest\n ? parseAppRscRenderMode(request.headers.get(VINEXT_RSC_RENDER_MODE_HEADER))\n : APP_RSC_RENDER_MODE_NAVIGATION;\n const clientReuseManifest = isRscRequest\n ? parseClientReuseManifestHeader(request.headers.get(VINEXT_CLIENT_REUSE_MANIFEST_HEADER))\n : ({ kind: \"absent\" } satisfies ClientReuseManifestParseResult);\n\n return {\n clientReuseManifest,\n url,\n pathname,\n cleanPathname,\n isRscRequest,\n interceptionContextHeader,\n mountedSlotsHeader,\n renderMode,\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA4EA,SAAgB,oBACd,SACA,UACiC;CACjC,MAAM,MAAM,IAAI,IAAI,QAAQ,IAAI;CAKhC,MAAM,aAAa,yBAAyB,IAAI,SAAS;CACzD,IAAI,YAAY,OAAO;CAKvB,IAAI;CACJ,IAAI;EACF,UAAU,qCAAqC,IAAI,SAAS;SACtD;EACN,OAAO,oBAAoB;;CAI7B,IAAI,WAAW,cAAc,QAAQ;CAMrC,IAAI,UAAU;EACZ,IAAI,CAAC,YAAY,UAAU,SAAS,IAAI,CAAC,SAAS,WAAW,aAAa,EACxE,OAAO,kBAAkB;EAE3B,WAAW,cAAc,UAAU,SAAS;;CAI9C,MAAM,eACJ,SAAS,SAAS,OAAO,IACxB,QAAQ,QAAQ,IAAA,MAAe,KAAK,OACnC,IAAI,aAAa,IAAA,OAA0C;CAC/D,MAAM,gBAAgB,eAAe,SAAS;CAS9C,MAAM,4BAA4B,mCAChC,QAAQ,QAAQ,IAAI,mCAAmC,CACxD;CAGD,MAAM,qBAAqB,4BACzB,QAAQ,QAAQ,IAAI,4BAA4B,CACjD;CACD,MAAM,aAAa,eACf,sBAAsB,QAAQ,QAAQ,IAAI,8BAA8B,CAAC,GACzE;CAKJ,OAAO;EACL,qBAL0B,eACxB,+BAA+B,QAAQ,QAAQ,IAAI,oCAAoC,CAAC,GACvF,EAAE,MAAM,UAAU;EAIrB;EACA;EACA;EACA;EACA;EACA;EACA;EACD"}
@@ -1,3 +1,4 @@
1
+ import { ActionRevalidationKind } from "../shims/cache.js";
1
2
  import { AppRscRenderMode } from "./app-rsc-render-mode.js";
2
3
  import { FetchCacheMode } from "../shims/fetch-cache.js";
3
4
  import { HeadersAccessPhase } from "../shims/headers.js";
@@ -28,15 +29,32 @@ type AppServerActionReturnValue = {
28
29
  type AppServerActionRoute = {
29
30
  pattern: string;
30
31
  };
31
- type ProgressiveServerActionResult = {
32
+ /**
33
+ * Side-effect headers captured during a progressive (no-JS) server action's
34
+ * non-redirect execution. The caller (app-rsc-handler) must apply these to the
35
+ * page render response so that `cookies().set(...)` and revalidation kinds
36
+ * propagate to the browser. Without this, no-JS form submissions silently
37
+ * lose cookie/header mutations — see issue #1483.
38
+ *
39
+ * Next.js' equivalent path mutates `res.setHeader('set-cookie', ...)` during
40
+ * action execution (action-handler.ts → app-render.tsx), then `sendResponse`
41
+ * merges those headers with the rendered Response. vinext works with Response
42
+ * objects directly so the cookies must ride out via the result instead.
43
+ */
44
+ type ProgressiveServerActionSideEffects = {
45
+ /** `Set-Cookie` headers from `cookies().set(...)` / `cookies().delete(...)`. */pendingCookies: string[]; /** `Set-Cookie` header from `draftMode().enable()/disable()` (if any). */
46
+ draftCookie: string | null | undefined; /** Resolved revalidation kind to emit via `x-action-revalidated`. */
47
+ revalidationKind: ActionRevalidationKind;
48
+ };
49
+ type ProgressiveServerActionResult = ({
32
50
  formState: ReactFormState | null;
33
51
  kind: "form-state";
34
- } | {
52
+ } & ProgressiveServerActionSideEffects) | ({
35
53
  actionError: unknown;
36
54
  actionFailed: true;
37
55
  formState: null;
38
56
  kind: "form-state";
39
- };
57
+ } & ProgressiveServerActionSideEffects);
40
58
  type AppServerActionMatch<TRoute extends AppServerActionRoute> = {
41
59
  params: AppPageParams;
42
60
  route: TRoute;
@@ -7,12 +7,13 @@ import { getAndClearActionRevalidationKind } from "../shims/cache.js";
7
7
  import { APP_RSC_RENDER_MODE_ACTION_RERENDER_PRESERVE_UI } from "./app-rsc-render-mode.js";
8
8
  import { setCurrentFetchCacheMode } from "../shims/fetch-cache.js";
9
9
  import { VINEXT_RSC_CONTENT_TYPE, VINEXT_RSC_VARY_HEADER, applyRscCompatibilityIdHeader } from "./app-rsc-cache-busting.js";
10
+ import { readStreamAsTextWithLimit } from "../utils/text-stream.js";
10
11
  import { createServerActionNotFoundResponse, getServerActionNotFoundMessage, isServerActionNotFoundError } from "./server-action-not-found.js";
11
12
  import { mergeMiddlewareResponseHeaders } from "./middleware-response-headers.js";
12
13
  import { applyEdgeRuntimeHeader } from "./app-page-response.js";
13
14
  import { getNextErrorDigest, parseNextHttpErrorDigest, parseNextRedirectDigest } from "./next-error-digest.js";
14
- import { readStreamAsTextWithLimit } from "../utils/text-stream.js";
15
15
  import { resolveAppPageActionRerenderTarget } from "./app-page-request.js";
16
+ import { getSetCookieName } from "./cookie-utils.js";
16
17
  //#region src/server/app-server-action-execution.ts
17
18
  /**
18
19
  * Matches Next.js' server action argument cap to prevent stack overflow in
@@ -33,6 +34,32 @@ function resolveActionRevalidationKind(hasModifiedCookies) {
33
34
  function isRequestBodyTooLarge(error) {
34
35
  return error instanceof Error && error.message === "Request body too large";
35
36
  }
37
+ /**
38
+ * Collapse repeated `cookies().set(name, ...)` / `cookies().delete(name)`
39
+ * calls down to the last value per name, matching Next.js'
40
+ * `MutableRequestCookiesAdapter` semantics. Next.js stores response cookies in
41
+ * a `ResponseCookies` Map keyed by name — multiple sets for the same cookie
42
+ * collapse to the final value, and emit a single Set-Cookie header.
43
+ *
44
+ * Insertion order is preserved by first occurrence (Map iteration order),
45
+ * which mirrors how `ResponseCookies` iterates its underlying Map. See
46
+ * packages/next/src/server/web/spec-extension/adapters/request-cookies.ts.
47
+ * Issue: https://github.com/cloudflare/vinext/issues/1481
48
+ */
49
+ function dedupePendingCookies(cookies) {
50
+ if (cookies.length <= 1) return cookies.slice();
51
+ const byName = /* @__PURE__ */ new Map();
52
+ const unkeyed = [];
53
+ for (const cookie of cookies) {
54
+ const name = getSetCookieName(cookie);
55
+ if (name === null) {
56
+ unkeyed.push(cookie);
57
+ continue;
58
+ }
59
+ byName.set(name, cookie);
60
+ }
61
+ return [...unkeyed, ...byName.values()];
62
+ }
36
63
  function isAppServerActionFunction(action) {
37
64
  return typeof action === "function";
38
65
  }
@@ -210,19 +237,27 @@ async function handleProgressiveServerActionRequest(options) {
210
237
  options.setHeadersAccessPhase(previousHeadersPhase);
211
238
  }
212
239
  if (!actionRedirect) {
213
- getAndClearActionRevalidationKind();
240
+ const actionPendingCookies = options.getAndClearPendingCookies();
241
+ const actionDraftCookie = options.getDraftModeCookieHeader();
242
+ const revalidationKind = resolveActionRevalidationKind(actionPendingCookies.length > 0 || Boolean(actionDraftCookie));
214
243
  if (actionFailed) return {
215
244
  kind: "form-state",
216
245
  formState: null,
217
246
  actionError,
218
- actionFailed
247
+ actionFailed,
248
+ pendingCookies: actionPendingCookies,
249
+ draftCookie: actionDraftCookie,
250
+ revalidationKind
219
251
  };
220
252
  return {
221
253
  kind: "form-state",
222
- formState: await options.decodeFormState(actionResult, body) ?? null
254
+ formState: await options.decodeFormState(actionResult, body) ?? null,
255
+ pendingCookies: actionPendingCookies,
256
+ draftCookie: actionDraftCookie,
257
+ revalidationKind
223
258
  };
224
259
  }
225
- const actionPendingCookies = options.getAndClearPendingCookies();
260
+ const actionPendingCookies = dedupePendingCookies(options.getAndClearPendingCookies());
226
261
  const actionDraftCookie = options.getDraftModeCookieHeader();
227
262
  const actionRevalidationKind = resolveActionRevalidationKind(actionPendingCookies.length > 0 || Boolean(actionDraftCookie));
228
263
  options.clearRequestContext();
@@ -340,7 +375,7 @@ async function handleServerActionRscRequest(options) {
340
375
  options.setHeadersAccessPhase(previousHeadersPhase);
341
376
  }
342
377
  if (actionRedirect) {
343
- const actionPendingCookies = options.getAndClearPendingCookies();
378
+ const actionPendingCookies = dedupePendingCookies(options.getAndClearPendingCookies());
344
379
  const actionDraftCookie = options.getDraftModeCookieHeader();
345
380
  const actionRevalidationKind = resolveActionRevalidationKind(actionPendingCookies.length > 0 || Boolean(actionDraftCookie));
346
381
  options.clearRequestContext();
@@ -362,7 +397,7 @@ async function handleServerActionRscRequest(options) {
362
397
  headers: redirectHeaders
363
398
  });
364
399
  }
365
- const actionPendingCookies = options.getAndClearPendingCookies();
400
+ const actionPendingCookies = dedupePendingCookies(options.getAndClearPendingCookies());
366
401
  const actionDraftCookie = options.getDraftModeCookieHeader();
367
402
  const actionRevalidationKind = resolveActionRevalidationKind(actionPendingCookies.length > 0 || Boolean(actionDraftCookie));
368
403
  if (actionRevalidationKind === ACTION_DID_NOT_REVALIDATE) {