vinext 0.0.47 → 0.0.49

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 (271) hide show
  1. package/README.md +1 -1
  2. package/dist/build/layout-classification.js +3 -1
  3. package/dist/build/layout-classification.js.map +1 -1
  4. package/dist/build/prerender.js +10 -10
  5. package/dist/build/prerender.js.map +1 -1
  6. package/dist/build/report.d.ts +8 -4
  7. package/dist/build/report.js +17 -7
  8. package/dist/build/report.js.map +1 -1
  9. package/dist/build/run-prerender.d.ts +5 -0
  10. package/dist/build/run-prerender.js +4 -1
  11. package/dist/build/run-prerender.js.map +1 -1
  12. package/dist/build/server-manifest.js +2 -7
  13. package/dist/build/server-manifest.js.map +1 -1
  14. package/dist/build/standalone.js +3 -5
  15. package/dist/build/standalone.js.map +1 -1
  16. package/dist/check.js +45 -29
  17. package/dist/check.js.map +1 -1
  18. package/dist/cli-args.d.ts +3 -1
  19. package/dist/cli-args.js +18 -1
  20. package/dist/cli-args.js.map +1 -1
  21. package/dist/cli.js +9 -1
  22. package/dist/cli.js.map +1 -1
  23. package/dist/config/config-matchers.js +46 -37
  24. package/dist/config/config-matchers.js.map +1 -1
  25. package/dist/deploy.d.ts +18 -2
  26. package/dist/deploy.js +47 -4
  27. package/dist/deploy.js.map +1 -1
  28. package/dist/entries/app-rsc-entry.js +11 -9
  29. package/dist/entries/app-rsc-entry.js.map +1 -1
  30. package/dist/entries/app-rsc-manifest.js +4 -1
  31. package/dist/entries/app-rsc-manifest.js.map +1 -1
  32. package/dist/entries/pages-client-entry.js +3 -2
  33. package/dist/entries/pages-client-entry.js.map +1 -1
  34. package/dist/entries/pages-server-entry.js +21 -62
  35. package/dist/entries/pages-server-entry.js.map +1 -1
  36. package/dist/entries/runtime-entry-module.d.ts +12 -3
  37. package/dist/entries/runtime-entry-module.js +15 -4
  38. package/dist/entries/runtime-entry-module.js.map +1 -1
  39. package/dist/index.js +12 -7
  40. package/dist/index.js.map +1 -1
  41. package/dist/init.d.ts +1 -1
  42. package/dist/init.js +2 -2
  43. package/dist/init.js.map +1 -1
  44. package/dist/plugins/og-assets.js +15 -16
  45. package/dist/plugins/og-assets.js.map +1 -1
  46. package/dist/plugins/rsc-client-shim-excludes.d.ts +2 -1
  47. package/dist/plugins/rsc-client-shim-excludes.js +10 -1
  48. package/dist/plugins/rsc-client-shim-excludes.js.map +1 -1
  49. package/dist/routing/app-route-graph.d.ts +90 -4
  50. package/dist/routing/app-route-graph.js +210 -7
  51. package/dist/routing/app-route-graph.js.map +1 -1
  52. package/dist/routing/app-router.d.ts +15 -3
  53. package/dist/routing/app-router.js +20 -23
  54. package/dist/routing/app-router.js.map +1 -1
  55. package/dist/routing/file-matcher.d.ts +3 -1
  56. package/dist/routing/file-matcher.js +6 -1
  57. package/dist/routing/file-matcher.js.map +1 -1
  58. package/dist/routing/pages-router.js +10 -19
  59. package/dist/routing/pages-router.js.map +1 -1
  60. package/dist/routing/route-matching.d.ts +28 -0
  61. package/dist/routing/route-matching.js +44 -0
  62. package/dist/routing/route-matching.js.map +1 -0
  63. package/dist/routing/route-pattern.js +4 -1
  64. package/dist/routing/route-pattern.js.map +1 -1
  65. package/dist/routing/route-trie.d.ts +8 -0
  66. package/dist/routing/route-trie.js +12 -1
  67. package/dist/routing/route-trie.js.map +1 -1
  68. package/dist/routing/route-validation.js +3 -4
  69. package/dist/routing/route-validation.js.map +1 -1
  70. package/dist/routing/utils.d.ts +8 -1
  71. package/dist/routing/utils.js +25 -2
  72. package/dist/routing/utils.js.map +1 -1
  73. package/dist/server/api-handler.js +2 -8
  74. package/dist/server/api-handler.js.map +1 -1
  75. package/dist/server/app-browser-entry.js +66 -49
  76. package/dist/server/app-browser-entry.js.map +1 -1
  77. package/dist/server/app-browser-navigation-controller.d.ts +7 -5
  78. package/dist/server/app-browser-navigation-controller.js +43 -35
  79. package/dist/server/app-browser-navigation-controller.js.map +1 -1
  80. package/dist/server/app-browser-state.d.ts +33 -15
  81. package/dist/server/app-browser-state.js +52 -59
  82. package/dist/server/app-browser-state.js.map +1 -1
  83. package/dist/server/app-browser-visible-commit.d.ts +68 -0
  84. package/dist/server/app-browser-visible-commit.js +182 -0
  85. package/dist/server/app-browser-visible-commit.js.map +1 -0
  86. package/dist/server/app-client-reference-preloader.d.ts +15 -0
  87. package/dist/server/app-client-reference-preloader.js +46 -0
  88. package/dist/server/app-client-reference-preloader.js.map +1 -0
  89. package/dist/server/app-elements-wire.d.ts +130 -0
  90. package/dist/server/app-elements-wire.js +205 -0
  91. package/dist/server/app-elements-wire.js.map +1 -0
  92. package/dist/server/app-elements.d.ts +2 -84
  93. package/dist/server/app-elements.js +3 -102
  94. package/dist/server/app-elements.js.map +1 -1
  95. package/dist/server/app-fallback-renderer.d.ts +1 -1
  96. package/dist/server/app-middleware.d.ts +2 -1
  97. package/dist/server/app-middleware.js +34 -11
  98. package/dist/server/app-middleware.js.map +1 -1
  99. package/dist/server/app-page-boundary-render.d.ts +1 -1
  100. package/dist/server/app-page-boundary-render.js +8 -5
  101. package/dist/server/app-page-boundary-render.js.map +1 -1
  102. package/dist/server/app-page-boundary.js +2 -1
  103. package/dist/server/app-page-boundary.js.map +1 -1
  104. package/dist/server/app-page-cache.d.ts +1 -0
  105. package/dist/server/app-page-cache.js +8 -13
  106. package/dist/server/app-page-cache.js.map +1 -1
  107. package/dist/server/app-page-dispatch.d.ts +2 -1
  108. package/dist/server/app-page-dispatch.js +18 -10
  109. package/dist/server/app-page-dispatch.js.map +1 -1
  110. package/dist/server/app-page-element-builder.d.ts +1 -1
  111. package/dist/server/app-page-element-builder.js +8 -5
  112. package/dist/server/app-page-element-builder.js.map +1 -1
  113. package/dist/server/app-page-execution.d.ts +23 -5
  114. package/dist/server/app-page-execution.js +39 -24
  115. package/dist/server/app-page-execution.js.map +1 -1
  116. package/dist/server/app-page-head.js +2 -1
  117. package/dist/server/app-page-head.js.map +1 -1
  118. package/dist/server/app-page-method.js +2 -5
  119. package/dist/server/app-page-method.js.map +1 -1
  120. package/dist/server/app-page-probe.d.ts +1 -1
  121. package/dist/server/app-page-probe.js +5 -1
  122. package/dist/server/app-page-probe.js.map +1 -1
  123. package/dist/server/app-page-render.d.ts +1 -1
  124. package/dist/server/app-page-render.js +38 -3
  125. package/dist/server/app-page-render.js.map +1 -1
  126. package/dist/server/app-page-request.d.ts +0 -1
  127. package/dist/server/app-page-request.js +7 -10
  128. package/dist/server/app-page-request.js.map +1 -1
  129. package/dist/server/app-page-response.js +3 -2
  130. package/dist/server/app-page-response.js.map +1 -1
  131. package/dist/server/app-page-route-wiring.d.ts +5 -2
  132. package/dist/server/app-page-route-wiring.js +15 -12
  133. package/dist/server/app-page-route-wiring.js.map +1 -1
  134. package/dist/server/app-page-stream.d.ts +7 -0
  135. package/dist/server/app-page-stream.js +9 -2
  136. package/dist/server/app-page-stream.js.map +1 -1
  137. package/dist/server/app-prerender-endpoints.js +3 -2
  138. package/dist/server/app-prerender-endpoints.js.map +1 -1
  139. package/dist/server/app-route-handler-cache.js +2 -1
  140. package/dist/server/app-route-handler-cache.js.map +1 -1
  141. package/dist/server/app-route-handler-dispatch.js +6 -5
  142. package/dist/server/app-route-handler-dispatch.js.map +1 -1
  143. package/dist/server/app-route-handler-policy.js +13 -13
  144. package/dist/server/app-route-handler-policy.js.map +1 -1
  145. package/dist/server/app-route-handler-response.js +2 -1
  146. package/dist/server/app-route-handler-response.js.map +1 -1
  147. package/dist/server/app-route-handler-runtime.d.ts +9 -1
  148. package/dist/server/app-route-handler-runtime.js +11 -1
  149. package/dist/server/app-route-handler-runtime.js.map +1 -1
  150. package/dist/server/app-router-entry.js +9 -4
  151. package/dist/server/app-router-entry.js.map +1 -1
  152. package/dist/server/app-rsc-cache-busting.d.ts +34 -0
  153. package/dist/server/app-rsc-cache-busting.js +137 -0
  154. package/dist/server/app-rsc-cache-busting.js.map +1 -0
  155. package/dist/server/app-rsc-handler.js +22 -11
  156. package/dist/server/app-rsc-handler.js.map +1 -1
  157. package/dist/server/app-rsc-request-normalization.d.ts +4 -2
  158. package/dist/server/app-rsc-request-normalization.js +10 -6
  159. package/dist/server/app-rsc-request-normalization.js.map +1 -1
  160. package/dist/server/app-rsc-response-finalizer.js +1 -1
  161. package/dist/server/app-rsc-route-matching.js +8 -4
  162. package/dist/server/app-rsc-route-matching.js.map +1 -1
  163. package/dist/server/app-segment-config.js +4 -0
  164. package/dist/server/app-segment-config.js.map +1 -1
  165. package/dist/server/app-server-action-execution.js +43 -51
  166. package/dist/server/app-server-action-execution.js.map +1 -1
  167. package/dist/server/app-ssr-entry.js +21 -20
  168. package/dist/server/app-ssr-entry.js.map +1 -1
  169. package/dist/server/artifact-compatibility.d.ts +44 -0
  170. package/dist/server/artifact-compatibility.js +82 -0
  171. package/dist/server/artifact-compatibility.js.map +1 -0
  172. package/dist/server/cache-proof.d.ts +200 -0
  173. package/dist/server/cache-proof.js +342 -0
  174. package/dist/server/cache-proof.js.map +1 -0
  175. package/dist/server/dev-origin-check.js +8 -4
  176. package/dist/server/dev-origin-check.js.map +1 -1
  177. package/dist/server/dev-server.js +6 -16
  178. package/dist/server/dev-server.js.map +1 -1
  179. package/dist/server/http-error-responses.d.ts +67 -0
  180. package/dist/server/http-error-responses.js +77 -0
  181. package/dist/server/http-error-responses.js.map +1 -0
  182. package/dist/server/image-optimization.js +2 -1
  183. package/dist/server/image-optimization.js.map +1 -1
  184. package/dist/server/metadata-route-response.js +6 -5
  185. package/dist/server/metadata-route-response.js.map +1 -1
  186. package/dist/server/metadata-routes.d.ts +1 -0
  187. package/dist/server/metadata-routes.js +6 -0
  188. package/dist/server/metadata-routes.js.map +1 -1
  189. package/dist/server/middleware-matcher.js +2 -2
  190. package/dist/server/middleware-matcher.js.map +1 -1
  191. package/dist/server/middleware-response-headers.js +21 -0
  192. package/dist/server/middleware-response-headers.js.map +1 -1
  193. package/dist/server/middleware-runtime.js +3 -3
  194. package/dist/server/middleware-runtime.js.map +1 -1
  195. package/dist/server/navigation-trace.d.ts +33 -0
  196. package/dist/server/navigation-trace.js +35 -0
  197. package/dist/server/navigation-trace.js.map +1 -0
  198. package/dist/server/next-error-digest.d.ts +44 -0
  199. package/dist/server/next-error-digest.js +40 -0
  200. package/dist/server/next-error-digest.js.map +1 -0
  201. package/dist/server/pages-api-route.js +4 -7
  202. package/dist/server/pages-api-route.js.map +1 -1
  203. package/dist/server/pages-node-compat.js +4 -16
  204. package/dist/server/pages-node-compat.js.map +1 -1
  205. package/dist/server/pages-page-response.d.ts +2 -8
  206. package/dist/server/pages-page-response.js +44 -14
  207. package/dist/server/pages-page-response.js.map +1 -1
  208. package/dist/server/prod-server.d.ts +6 -0
  209. package/dist/server/prod-server.js +28 -21
  210. package/dist/server/prod-server.js.map +1 -1
  211. package/dist/server/request-pipeline.d.ts +42 -1
  212. package/dist/server/request-pipeline.js +97 -17
  213. package/dist/server/request-pipeline.js.map +1 -1
  214. package/dist/shims/cache-runtime.d.ts +2 -2
  215. package/dist/shims/cache-runtime.js +3 -6
  216. package/dist/shims/cache-runtime.js.map +1 -1
  217. package/dist/shims/cache.js +3 -5
  218. package/dist/shims/cache.js.map +1 -1
  219. package/dist/shims/fetch-cache.js +2 -3
  220. package/dist/shims/fetch-cache.js.map +1 -1
  221. package/dist/shims/head-state.js +2 -3
  222. package/dist/shims/head-state.js.map +1 -1
  223. package/dist/shims/headers.js +4 -44
  224. package/dist/shims/headers.js.map +1 -1
  225. package/dist/shims/i18n-state.js +2 -3
  226. package/dist/shims/i18n-state.js.map +1 -1
  227. package/dist/shims/internal/als-registry.d.ts +15 -0
  228. package/dist/shims/internal/als-registry.js +55 -0
  229. package/dist/shims/internal/als-registry.js.map +1 -0
  230. package/dist/shims/internal/cookie-serialize.d.ts +46 -0
  231. package/dist/shims/internal/cookie-serialize.js +51 -0
  232. package/dist/shims/internal/cookie-serialize.js.map +1 -0
  233. package/dist/shims/link.js +31 -26
  234. package/dist/shims/link.js.map +1 -1
  235. package/dist/shims/metadata.d.ts +26 -1
  236. package/dist/shims/metadata.js +94 -4
  237. package/dist/shims/metadata.js.map +1 -1
  238. package/dist/shims/navigation-state.js +2 -3
  239. package/dist/shims/navigation-state.js.map +1 -1
  240. package/dist/shims/navigation.d.ts +2 -7
  241. package/dist/shims/navigation.js +44 -36
  242. package/dist/shims/navigation.js.map +1 -1
  243. package/dist/shims/request-context.js +2 -4
  244. package/dist/shims/request-context.js.map +1 -1
  245. package/dist/shims/router-state.js +2 -3
  246. package/dist/shims/router-state.js.map +1 -1
  247. package/dist/shims/router.js +2 -2
  248. package/dist/shims/router.js.map +1 -1
  249. package/dist/shims/server.js +5 -30
  250. package/dist/shims/server.js.map +1 -1
  251. package/dist/shims/slot.d.ts +1 -1
  252. package/dist/shims/slot.js +5 -4
  253. package/dist/shims/slot.js.map +1 -1
  254. package/dist/shims/thenable-params.d.ts +5 -2
  255. package/dist/shims/thenable-params.js +26 -6
  256. package/dist/shims/thenable-params.js.map +1 -1
  257. package/dist/shims/unified-request-context.js +2 -14
  258. package/dist/shims/unified-request-context.js.map +1 -1
  259. package/dist/utils/base-path.d.ts +7 -1
  260. package/dist/utils/base-path.js +12 -1
  261. package/dist/utils/base-path.js.map +1 -1
  262. package/dist/utils/query.d.ts +8 -1
  263. package/dist/utils/query.js +12 -1
  264. package/dist/utils/query.js.map +1 -1
  265. package/dist/utils/safe-json-file.d.ts +18 -0
  266. package/dist/utils/safe-json-file.js +25 -0
  267. package/dist/utils/safe-json-file.js.map +1 -0
  268. package/dist/utils/text-stream.d.ts +29 -0
  269. package/dist/utils/text-stream.js +66 -0
  270. package/dist/utils/text-stream.js.map +1 -0
  271. package/package.json +5 -5
@@ -1,13 +1,16 @@
1
1
  import { createRequestContext, runWithRequestContext } from "../shims/unified-request-context.js";
2
+ import { hasBasePath } from "../utils/base-path.js";
2
3
  import { getRequestExecutionContext } from "../shims/request-context.js";
3
4
  import { isExternalUrl, matchRedirect, matchRewrite, proxyExternalRequest, requestContextFromRequest, sanitizeDestination } from "../config/config-matchers.js";
4
- import { hasBasePath } from "../utils/base-path.js";
5
- import { normalizeTrailingSlash, resolvePublicFileRoute, validateImageUrl } from "./request-pipeline.js";
5
+ import { notFoundResponse } from "./http-error-responses.js";
6
+ import { cloneRequestWithHeaders, filterInternalHeaders, normalizeTrailingSlash, resolvePublicFileRoute, validateImageUrl } from "./request-pipeline.js";
6
7
  import { headersContextFromRequest } from "../shims/headers.js";
7
8
  import { ensureFetchPatch, setCurrentFetchSoftTags } from "../shims/fetch-cache.js";
9
+ import { createRscRedirectLocation, resolveInvalidRscCacheBustingRequest, stripRscCacheBustingSearchParam, stripRscSuffix } from "./app-rsc-cache-busting.js";
8
10
  import { getScriptNonceFromHeaderSources } from "./csp.js";
9
11
  import { mergeMiddlewareResponseHeaders } from "./middleware-response-headers.js";
10
12
  import { applyAppMiddleware } from "./app-middleware.js";
13
+ import "./app-page-response.js";
11
14
  import { buildPageCacheTags } from "./implicit-tags.js";
12
15
  import { buildPostMwRequestContext } from "./app-post-middleware-context.js";
13
16
  import { pickRootParams, setRootParams } from "../shims/root-params.js";
@@ -61,14 +64,20 @@ async function handleAppRscRequest(options, request, preMiddlewareRequestContext
61
64
  if (prerenderEndpointResponse) return prerenderEndpointResponse;
62
65
  const trailingSlashRedirect = normalizeTrailingSlash(pathname, options.basePath, options.trailingSlash, url.search);
63
66
  if (trailingSlashRedirect) return trailingSlashRedirect;
64
- const redirect = matchRedirect(pathname.endsWith(".rsc") ? pathname.slice(0, -4) : pathname, options.configRedirects, preMiddlewareRequestContext);
67
+ const redirect = matchRedirect(stripRscSuffix(pathname), options.configRedirects, preMiddlewareRequestContext);
65
68
  if (redirect) {
66
69
  const destination = sanitizeDestination(redirectDestinationWithBasePath(redirect.destination, options.basePath));
70
+ const location = isRscRequest && request.headers.get("RSC") === "1" ? await createRscRedirectLocation(destination, request) : destination;
67
71
  return new Response(null, {
68
72
  status: redirect.permanent ? 308 : 307,
69
- headers: { Location: destination }
73
+ headers: { Location: location }
70
74
  });
71
75
  }
76
+ const rscCacheBustingRedirect = await resolveInvalidRscCacheBustingRequest({
77
+ isRscRequest,
78
+ request
79
+ });
80
+ if (rscCacheBustingRedirect) return rscCacheBustingRedirect;
72
81
  const middlewareContext = {
73
82
  headers: null,
74
83
  requestHeaders: null,
@@ -120,6 +129,7 @@ async function handleAppRscRequest(options, request, preMiddlewareRequestContext
120
129
  options.clearRequestContext();
121
130
  return publicFileResponse;
122
131
  }
132
+ if (isRscRequest) stripRscCacheBustingSearchParam(url);
123
133
  options.setNavigationContext({
124
134
  pathname: cleanPathname,
125
135
  searchParams: url.searchParams,
@@ -180,21 +190,18 @@ async function handleAppRscRequest(options, request, preMiddlewareRequestContext
180
190
  options.clearRequestContext();
181
191
  return pagesFallbackResponse;
182
192
  }
183
- const notFoundResponse = await options.renderNotFound({
193
+ const renderedNotFoundResponse = await options.renderNotFound({
184
194
  isRscRequest,
185
195
  middlewareContext,
186
196
  request,
187
197
  route: null,
188
198
  scriptNonce
189
199
  });
190
- if (notFoundResponse) return notFoundResponse;
200
+ if (renderedNotFoundResponse) return renderedNotFoundResponse;
191
201
  options.clearRequestContext();
192
202
  const headers = new Headers();
193
203
  mergeMiddlewareResponseHeaders(headers, middlewareContext.headers);
194
- return new Response("Not Found", {
195
- status: 404,
196
- headers
197
- });
204
+ return notFoundResponse({ headers });
198
205
  }
199
206
  const { route, params } = match;
200
207
  options.setNavigationContext({
@@ -229,8 +236,12 @@ async function handleAppRscRequest(options, request, preMiddlewareRequestContext
229
236
  });
230
237
  }
231
238
  function createAppRscHandler(options) {
232
- return async function appRscHandler(request, ctx) {
239
+ return async function appRscHandler(rawRequest, ctx) {
233
240
  await options.ensureInstrumentation?.();
241
+ const mwCtx = rawRequest.headers.get("x-vinext-mw-ctx");
242
+ const filteredHeaders = filterInternalHeaders(rawRequest.headers);
243
+ if (mwCtx !== null) filteredHeaders.set("x-vinext-mw-ctx", mwCtx);
244
+ const request = cloneRequestWithHeaders(rawRequest, filteredHeaders);
234
245
  const executionContext = isExecutionContextLike(ctx) ? ctx : getRequestExecutionContext() ?? null;
235
246
  return runWithRequestContext(createRequestContext({
236
247
  headersContext: headersContextFromRequest(request),
@@ -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} from \"../config/config-matchers.js\";\nimport { headersContextFromRequest } from \"vinext/shims/headers\";\nimport { ensureFetchPatch, setCurrentFetchSoftTags } from \"vinext/shims/fetch-cache\";\nimport {\n getRequestExecutionContext,\n type ExecutionContextLike,\n} from \"vinext/shims/request-context\";\nimport { pickRootParams, setRootParams } 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 { finalizeAppRscResponse } from \"./app-rsc-response-finalizer.js\";\nimport { normalizeRscRequest } from \"./app-rsc-request-normalization.js\";\nimport { getScriptNonceFromHeaderSources } from \"./csp.js\";\nimport { buildPageCacheTags } from \"./implicit-tags.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 {\n normalizeTrailingSlash,\n resolvePublicFileRoute,\n validateImageUrl,\n} from \"./request-pipeline.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 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 handlerStart: number;\n interceptionContext: string | null;\n isRscRequest: boolean;\n middlewareContext: AppRscMiddlewareContext;\n mountedSlotsHeader: string | null;\n params: AppPageParams;\n request: Request;\n route: TRoute;\n scriptNonce?: string;\n searchParams: URLSearchParams;\n};\n\ntype DispatchMatchedRouteHandlerOptions<TRoute> = {\n cleanPathname: string;\n middlewareContext: AppRscMiddlewareContext;\n params: AppPageParams;\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 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 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 | 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\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 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(cleanPathname, options.rewrites, options.requestContext);\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\nasync function handleAppRscRequest<TRoute extends AppRscHandlerRoute>(\n options: CreateAppRscHandlerOptions<TRoute>,\n request: Request,\n preMiddlewareRequestContext: RequestContext,\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 } = normalized;\n let { pathname, cleanPathname } = normalized;\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 const redirectPathname = pathname.endsWith(\".rsc\") ? pathname.slice(0, -4) : pathname;\n const redirect = matchRedirect(\n redirectPathname,\n options.configRedirects,\n preMiddlewareRequestContext,\n );\n if (redirect) {\n const destination = sanitizeDestination(\n redirectDestinationWithBasePath(redirect.destination, options.basePath),\n );\n return new Response(null, {\n status: redirect.permanent ? 308 : 307,\n headers: { Location: destination },\n });\n }\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 isProxy: options.isMiddlewareProxy,\n module: options.middlewareModule,\n request,\n });\n if (middlewareResult.kind === \"response\") return middlewareResult.response;\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 const beforeFilesRewrite = await applyRewrite(\n {\n clearRequestContext: options.clearRequestContext,\n request,\n requestContext: postMiddlewareRequestContext,\n rewrites: options.configRewrites.beforeFiles,\n },\n cleanPathname,\n );\n if (beforeFilesRewrite instanceof Response) return beforeFilesRewrite;\n if (beforeFilesRewrite) cleanPathname = beforeFilesRewrite;\n\n if (cleanPathname === \"/_vinext/image\") {\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 options.setNavigationContext({\n pathname: cleanPathname,\n searchParams: url.searchParams,\n params: {},\n });\n\n const actionId = request.headers.get(\"x-rsc-action\") ?? request.headers.get(\"next-action\");\n const contentType = request.headers.get(\"content-type\") || \"\";\n\n const progressiveActionResponse = await options.handleProgressiveActionRequest({\n actionId,\n cleanPathname,\n contentType,\n middlewareContext,\n request,\n });\n if (progressiveActionResponse) return progressiveActionResponse;\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 const afterFilesRewrite = await applyRewrite(\n {\n clearRequestContext: options.clearRequestContext,\n request,\n requestContext: postMiddlewareRequestContext,\n rewrites: options.configRewrites.afterFiles,\n },\n cleanPathname,\n );\n if (afterFilesRewrite instanceof Response) return afterFilesRewrite;\n if (afterFilesRewrite) cleanPathname = afterFilesRewrite;\n\n let match = options.matchRoute(cleanPathname);\n if (!match) {\n const fallbackRewrite = await applyRewrite(\n {\n clearRequestContext: options.clearRequestContext,\n request,\n requestContext: postMiddlewareRequestContext,\n rewrites: options.configRewrites.fallback,\n },\n 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 notFoundResponse = await options.renderNotFound({\n isRscRequest,\n middlewareContext,\n request,\n route: null,\n scriptNonce,\n });\n if (notFoundResponse) return notFoundResponse;\n\n options.clearRequestContext();\n const headers = new Headers();\n mergeMiddlewareResponseHeaders(headers, middlewareContext.headers);\n return new Response(\"Not Found\", { status: 404, headers });\n }\n\n const { route, params } = match;\n options.setNavigationContext({\n pathname: cleanPathname,\n searchParams: url.searchParams,\n params,\n });\n setRootParams(pickRootParams(params, route.rootParamNames));\n\n if (route.routeHandler) {\n setCurrentFetchSoftTags(\n buildPageCacheTags(cleanPathname, [], [...route.routeSegments], \"route\"),\n );\n return options.dispatchMatchedRouteHandler({\n cleanPathname,\n middlewareContext,\n params,\n request,\n route,\n searchParams: url.searchParams,\n });\n }\n\n return options.dispatchMatchedPage({\n cleanPathname,\n handlerStart,\n interceptionContext: interceptionContextHeader,\n isRscRequest,\n middlewareContext,\n mountedSlotsHeader,\n params,\n request,\n route,\n scriptNonce,\n searchParams: url.searchParams,\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(request, ctx) {\n await options.ensureInstrumentation?.();\n\n const executionContext = isExecutionContextLike(ctx)\n ? ctx\n : (getRequestExecutionContext() ?? null);\n const headersContext = headersContextFromRequest(request);\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(options, request, preMiddlewareRequestContext);\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 requestContext: preMiddlewareRequestContext,\n });\n },\n { route: () => new URL(request.url).pathname },\n ),\n );\n };\n}\n"],"mappings":"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"}
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} from \"../config/config-matchers.js\";\nimport { headersContextFromRequest } from \"vinext/shims/headers\";\nimport { ensureFetchPatch, setCurrentFetchSoftTags } from \"vinext/shims/fetch-cache\";\nimport {\n getRequestExecutionContext,\n type ExecutionContextLike,\n} from \"vinext/shims/request-context\";\nimport { pickRootParams, setRootParams } 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 { notFoundResponse } from \"./http-error-responses.js\";\nimport { getScriptNonceFromHeaderSources } from \"./csp.js\";\nimport { buildPageCacheTags } from \"./implicit-tags.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 {\n cloneRequestWithHeaders,\n filterInternalHeaders,\n normalizeTrailingSlash,\n resolvePublicFileRoute,\n validateImageUrl,\n} from \"./request-pipeline.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 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 handlerStart: number;\n interceptionContext: string | null;\n isRscRequest: boolean;\n middlewareContext: AppRscMiddlewareContext;\n mountedSlotsHeader: string | null;\n params: AppPageParams;\n request: Request;\n route: TRoute;\n scriptNonce?: string;\n searchParams: URLSearchParams;\n};\n\ntype DispatchMatchedRouteHandlerOptions<TRoute> = {\n cleanPathname: string;\n middlewareContext: AppRscMiddlewareContext;\n params: AppPageParams;\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 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 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 | 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\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 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(cleanPathname, options.rewrites, options.requestContext);\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\nasync function handleAppRscRequest<TRoute extends AppRscHandlerRoute>(\n options: CreateAppRscHandlerOptions<TRoute>,\n request: Request,\n preMiddlewareRequestContext: RequestContext,\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 } = normalized;\n let { pathname, cleanPathname } = normalized;\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 const redirectPathname = stripRscSuffix(pathname);\n const redirect = matchRedirect(\n redirectPathname,\n options.configRedirects,\n preMiddlewareRequestContext,\n );\n if (redirect) {\n const destination = sanitizeDestination(\n redirectDestinationWithBasePath(redirect.destination, options.basePath),\n );\n const location =\n isRscRequest && request.headers.get(\"RSC\") === \"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 isProxy: options.isMiddlewareProxy,\n module: options.middlewareModule,\n request,\n });\n if (middlewareResult.kind === \"response\") return middlewareResult.response;\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 const beforeFilesRewrite = await applyRewrite(\n {\n clearRequestContext: options.clearRequestContext,\n request,\n requestContext: postMiddlewareRequestContext,\n rewrites: options.configRewrites.beforeFiles,\n },\n cleanPathname,\n );\n if (beforeFilesRewrite instanceof Response) return beforeFilesRewrite;\n if (beforeFilesRewrite) cleanPathname = beforeFilesRewrite;\n\n if (cleanPathname === \"/_vinext/image\") {\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: cleanPathname,\n searchParams: url.searchParams,\n params: {},\n });\n\n const actionId = request.headers.get(\"x-rsc-action\") ?? request.headers.get(\"next-action\");\n const contentType = request.headers.get(\"content-type\") || \"\";\n\n const progressiveActionResponse = await options.handleProgressiveActionRequest({\n actionId,\n cleanPathname,\n contentType,\n middlewareContext,\n request,\n });\n if (progressiveActionResponse) return progressiveActionResponse;\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 const afterFilesRewrite = await applyRewrite(\n {\n clearRequestContext: options.clearRequestContext,\n request,\n requestContext: postMiddlewareRequestContext,\n rewrites: options.configRewrites.afterFiles,\n },\n cleanPathname,\n );\n if (afterFilesRewrite instanceof Response) return afterFilesRewrite;\n if (afterFilesRewrite) cleanPathname = afterFilesRewrite;\n\n let match = options.matchRoute(cleanPathname);\n if (!match) {\n const fallbackRewrite = await applyRewrite(\n {\n clearRequestContext: options.clearRequestContext,\n request,\n requestContext: postMiddlewareRequestContext,\n rewrites: options.configRewrites.fallback,\n },\n 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 options.setNavigationContext({\n pathname: cleanPathname,\n searchParams: url.searchParams,\n params,\n });\n setRootParams(pickRootParams(params, route.rootParamNames));\n\n if (route.routeHandler) {\n setCurrentFetchSoftTags(\n buildPageCacheTags(cleanPathname, [], [...route.routeSegments], \"route\"),\n );\n return options.dispatchMatchedRouteHandler({\n cleanPathname,\n middlewareContext,\n params,\n request,\n route,\n searchParams: url.searchParams,\n });\n }\n\n return options.dispatchMatchedPage({\n cleanPathname,\n handlerStart,\n interceptionContext: interceptionContextHeader,\n isRscRequest,\n middlewareContext,\n mountedSlotsHeader,\n params,\n request,\n route,\n scriptNonce,\n searchParams: url.searchParams,\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(\"x-vinext-mw-ctx\");\n const filteredHeaders = filterInternalHeaders(rawRequest.headers);\n if (mwCtx !== null) {\n filteredHeaders.set(\"x-vinext-mw-ctx\", mwCtx);\n }\n const request = cloneRequestWithHeaders(rawRequest, filteredHeaders);\n\n const executionContext = isExecutionContextLike(ctx)\n ? ctx\n : (getRequestExecutionContext() ?? null);\n const headersContext = headersContextFromRequest(request);\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(options, request, preMiddlewareRequestContext);\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 requestContext: preMiddlewareRequestContext,\n });\n },\n { route: () => new URL(request.url).pathname },\n ),\n );\n };\n}\n"],"mappings":"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"}
@@ -4,7 +4,7 @@ import { normalizeMountedSlotsHeader } from "./app-mounted-slots-header.js";
4
4
  type NormalizedRscRequest = {
5
5
  /** Parsed URL. Callers may mutate `url.search` after middleware runs. */url: URL; /** Normalized pathname with basePath stripped. Used for all internal routing. */
6
6
  pathname: string; /** Pathname with `.rsc` suffix removed. Used for route matching and navigation context. */
7
- cleanPathname: string; /** True when the client requests the RSC payload (.rsc suffix or Accept: text/x-component). */
7
+ cleanPathname: string; /** True when the request targets a canonical `.rsc` payload URL. */
8
8
  isRscRequest: boolean; /** Sanitized X-Vinext-Interception-Context header (null bytes stripped). null when absent. */
9
9
  interceptionContextHeader: string | null; /** Normalized x-vinext-mounted-slots header (deduplicated, sorted). null when absent or blank. */
10
10
  mountedSlotsHeader: string | null;
@@ -26,7 +26,9 @@ type NormalizedRscRequest = {
26
26
  * 4. Collapse double-slashes, resolve `.` and `..` segments (normalizePath)
27
27
  * 5. basePath check + strip — 404 when pathname lacks the basePath prefix.
28
28
  * `/__vinext/` bypasses this for internal prerender endpoints.
29
- * 6. RSC detection: `.rsc` suffix or `Accept: text/x-component`
29
+ * 6. RSC detection: `.rsc` suffix only. RSC headers do not select payload
30
+ * rendering at the canonical HTML URL, so caches that ignore Vary cannot
31
+ * store Flight responses under HTML URLs.
30
32
  * 7. cleanPathname — pathname with `.rsc` suffix stripped
31
33
  * 8. Sanitize X-Vinext-Interception-Context — strip null bytes (header injection)
32
34
  * 9. Normalize x-vinext-mounted-slots — dedup and sort for canonical cache keys
@@ -1,8 +1,10 @@
1
1
  import { normalizePathnameForRouteMatchStrict } from "../routing/utils.js";
2
- import { normalizePath } from "./normalize-path.js";
3
2
  import { hasBasePath, stripBasePath } from "../utils/base-path.js";
3
+ import { normalizePath } from "./normalize-path.js";
4
+ import { badRequestResponse, notFoundResponse } from "./http-error-responses.js";
4
5
  import { guardProtocolRelativeUrl } from "./request-pipeline.js";
5
6
  import { normalizeMountedSlotsHeader } from "./app-mounted-slots-header.js";
7
+ import { stripRscSuffix } from "./app-rsc-cache-busting.js";
6
8
  //#region src/server/app-rsc-request-normalization.ts
7
9
  /**
8
10
  * Normalize an App Router RSC request.
@@ -21,7 +23,9 @@ import { normalizeMountedSlotsHeader } from "./app-mounted-slots-header.js";
21
23
  * 4. Collapse double-slashes, resolve `.` and `..` segments (normalizePath)
22
24
  * 5. basePath check + strip — 404 when pathname lacks the basePath prefix.
23
25
  * `/__vinext/` bypasses this for internal prerender endpoints.
24
- * 6. RSC detection: `.rsc` suffix or `Accept: text/x-component`
26
+ * 6. RSC detection: `.rsc` suffix only. RSC headers do not select payload
27
+ * rendering at the canonical HTML URL, so caches that ignore Vary cannot
28
+ * store Flight responses under HTML URLs.
25
29
  * 7. cleanPathname — pathname with `.rsc` suffix stripped
26
30
  * 8. Sanitize X-Vinext-Interception-Context — strip null bytes (header injection)
27
31
  * 9. Normalize x-vinext-mounted-slots — dedup and sort for canonical cache keys
@@ -37,15 +41,15 @@ function normalizeRscRequest(request, basePath) {
37
41
  try {
38
42
  decoded = normalizePathnameForRouteMatchStrict(url.pathname);
39
43
  } catch {
40
- return new Response("Bad Request", { status: 400 });
44
+ return badRequestResponse();
41
45
  }
42
46
  let pathname = normalizePath(decoded);
43
47
  if (basePath) {
44
- if (!hasBasePath(pathname, basePath) && !pathname.startsWith("/__vinext/")) return new Response("Not Found", { status: 404 });
48
+ if (!hasBasePath(pathname, basePath) && !pathname.startsWith("/__vinext/")) return notFoundResponse();
45
49
  pathname = stripBasePath(pathname, basePath);
46
50
  }
47
- const isRscRequest = pathname.endsWith(".rsc") || (request.headers.get("accept")?.includes("text/x-component") ?? false);
48
- const cleanPathname = pathname.replace(/\.rsc$/, "");
51
+ const isRscRequest = pathname.endsWith(".rsc");
52
+ const cleanPathname = stripRscSuffix(pathname);
49
53
  const interceptionContextHeader = request.headers.get("X-Vinext-Interception-Context")?.replaceAll("\0", "") || null;
50
54
  const mountedSlotsHeader = normalizeMountedSlotsHeader(request.headers.get("x-vinext-mounted-slots"));
51
55
  return {
@@ -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 { normalizeMountedSlotsHeader } from \"./app-mounted-slots-header.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 client requests the RSC payload (.rsc suffix or Accept: text/x-component). */\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};\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 `Accept: text/x-component`\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 *\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 new Response(\"Bad Request\", { status: 400 });\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 new Response(\"Not Found\", { status: 404 });\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(\"accept\")?.includes(\"text/x-component\") ?? false);\n const cleanPathname = pathname.replace(/\\.rsc$/, \"\");\n\n // Step 8: Sanitize X-Vinext-Interception-Context.\n // Null bytes in header values can be used for injection in some HTTP stacks.\n const interceptionContextHeader =\n request.headers.get(\"X-Vinext-Interception-Context\")?.replaceAll(\"\\0\", \"\") || null;\n\n // Step 9: Normalize mounted-slots header for canonical cache keying.\n const mountedSlotsHeader = normalizeMountedSlotsHeader(\n request.headers.get(\"x-vinext-mounted-slots\"),\n );\n\n return {\n url,\n pathname,\n cleanPathname,\n isRscRequest,\n interceptionContextHeader,\n mountedSlotsHeader,\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgDA,SAAgB,oBACd,SACA,UACiC;CACjC,MAAM,MAAM,IAAI,IAAI,QAAQ,IAAI;CAKhC,MAAM,aAAa,yBAAyB,IAAI,SAAS;AACzD,KAAI,WAAY,QAAO;CAKvB,IAAI;AACJ,KAAI;AACF,YAAU,qCAAqC,IAAI,SAAS;SACtD;AACN,SAAO,IAAI,SAAS,eAAe,EAAE,QAAQ,KAAK,CAAC;;CAIrD,IAAI,WAAW,cAAc,QAAQ;AAMrC,KAAI,UAAU;AACZ,MAAI,CAAC,YAAY,UAAU,SAAS,IAAI,CAAC,SAAS,WAAW,aAAa,CACxE,QAAO,IAAI,SAAS,aAAa,EAAE,QAAQ,KAAK,CAAC;AAEnD,aAAW,cAAc,UAAU,SAAS;;CAI9C,MAAM,eACJ,SAAS,SAAS,OAAO,KACxB,QAAQ,QAAQ,IAAI,SAAS,EAAE,SAAS,mBAAmB,IAAI;CAClE,MAAM,gBAAgB,SAAS,QAAQ,UAAU,GAAG;CAIpD,MAAM,4BACJ,QAAQ,QAAQ,IAAI,gCAAgC,EAAE,WAAW,MAAM,GAAG,IAAI;CAGhF,MAAM,qBAAqB,4BACzB,QAAQ,QAAQ,IAAI,yBAAyB,CAC9C;AAED,QAAO;EACL;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 { normalizeMountedSlotsHeader } from \"./app-mounted-slots-header.js\";\nimport { stripRscSuffix } from \"./app-rsc-cache-busting.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};\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 *\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: Sanitize X-Vinext-Interception-Context.\n // Null bytes in header values can be used for injection in some HTTP stacks.\n const interceptionContextHeader =\n request.headers.get(\"X-Vinext-Interception-Context\")?.replaceAll(\"\\0\", \"\") || null;\n\n // Step 9: Normalize mounted-slots header for canonical cache keying.\n const mountedSlotsHeader = normalizeMountedSlotsHeader(\n request.headers.get(\"x-vinext-mounted-slots\"),\n );\n\n return {\n url,\n pathname,\n cleanPathname,\n isRscRequest,\n interceptionContextHeader,\n mountedSlotsHeader,\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAoDA,SAAgB,oBACd,SACA,UACiC;CACjC,MAAM,MAAM,IAAI,IAAI,QAAQ,IAAI;CAKhC,MAAM,aAAa,yBAAyB,IAAI,SAAS;AACzD,KAAI,WAAY,QAAO;CAKvB,IAAI;AACJ,KAAI;AACF,YAAU,qCAAqC,IAAI,SAAS;SACtD;AACN,SAAO,oBAAoB;;CAI7B,IAAI,WAAW,cAAc,QAAQ;AAMrC,KAAI,UAAU;AACZ,MAAI,CAAC,YAAY,UAAU,SAAS,IAAI,CAAC,SAAS,WAAW,aAAa,CACxE,QAAO,kBAAkB;AAE3B,aAAW,cAAc,UAAU,SAAS;;CAI9C,MAAM,eAAe,SAAS,SAAS,OAAO;CAC9C,MAAM,gBAAgB,eAAe,SAAS;CAI9C,MAAM,4BACJ,QAAQ,QAAQ,IAAI,gCAAgC,EAAE,WAAW,MAAM,GAAG,IAAI;CAGhF,MAAM,qBAAqB,4BACzB,QAAQ,QAAQ,IAAI,yBAAyB,CAC9C;AAED,QAAO;EACL;EACA;EACA;EACA;EACA;EACA;EACD"}
@@ -1,6 +1,6 @@
1
1
  import { normalizePathnameForRouteMatch } from "../routing/utils.js";
2
- import { normalizePath } from "./normalize-path.js";
3
2
  import { stripBasePath } from "../utils/base-path.js";
3
+ import { normalizePath } from "./normalize-path.js";
4
4
  import { applyConfigHeadersToResponse } from "./request-pipeline.js";
5
5
  //#region src/server/app-rsc-response-finalizer.ts
6
6
  /**
@@ -1,26 +1,30 @@
1
+ import { normalizePathnameForRouteMatch } from "../routing/utils.js";
1
2
  import { buildRouteTrie, trieMatch } from "../routing/route-trie.js";
2
3
  import { matchRoutePattern } from "../routing/route-pattern.js";
3
4
  //#region src/server/app-rsc-route-matching.ts
4
5
  function createRouteParams() {
5
6
  return Object.create(null);
6
7
  }
8
+ function appRscPathnameParts(pathname) {
9
+ const pathOnly = pathname.split("?")[0];
10
+ return normalizePathnameForRouteMatch(pathOnly === "/" ? "/" : pathOnly.replace(/\/$/, "")).split("/").filter(Boolean);
11
+ }
7
12
  function createAppRscRouteMatcher(routes) {
8
13
  const routeTrie = buildRouteTrie(routes);
9
14
  const interceptLookup = createInterceptLookup(routes);
10
15
  return {
11
16
  matchRoute(url) {
12
- const pathname = url.split("?")[0];
13
- return trieMatch(routeTrie, (pathname === "/" ? "/" : pathname.replace(/\/$/, "")).split("/").filter(Boolean));
17
+ return trieMatch(routeTrie, appRscPathnameParts(url));
14
18
  },
15
19
  findIntercept(pathname, sourcePathname = null) {
16
- const urlParts = pathname.split("/").filter(Boolean);
20
+ const urlParts = appRscPathnameParts(pathname);
17
21
  for (const entry of interceptLookup) {
18
22
  const params = matchAppRscRoutePattern(urlParts, entry.targetPatternParts);
19
23
  if (params !== null) {
20
24
  let sourceParams = createRouteParams();
21
25
  if (sourcePathname !== null) {
22
26
  const sourceRoute = routes[entry.sourceRouteIndex];
23
- const sourceParts = sourcePathname.split("/").filter(Boolean);
27
+ const sourceParts = appRscPathnameParts(sourcePathname);
24
28
  const matchedSourceParams = sourceRoute ? matchAppRscRoutePattern(sourceParts, sourceRoute.patternParts) : null;
25
29
  if (matchedSourceParams !== null) sourceParams = matchedSourceParams;
26
30
  }
@@ -1 +1 @@
1
- {"version":3,"file":"app-rsc-route-matching.js","names":[],"sources":["../../src/server/app-rsc-route-matching.ts"],"sourcesContent":["import { buildRouteTrie, trieMatch } from \"../routing/route-trie.js\";\nimport { matchRoutePattern, type RoutePatternParams } from \"../routing/route-pattern.js\";\n\ntype AppRscRouteParams = RoutePatternParams;\n\ntype AppRscInterceptForMatching = {\n targetPattern: string;\n interceptLayouts: readonly unknown[];\n page: unknown;\n params: readonly string[];\n};\n\ntype AppRscSlotForMatching = {\n intercepts?: readonly AppRscInterceptForMatching[];\n};\n\ntype AppRscRouteForMatching = {\n patternParts: string[];\n slots?: Record<string, AppRscSlotForMatching>;\n};\n\ntype AppRscInterceptMatch = AppRscInterceptLookupEntry & {\n matchedParams: AppRscRouteParams;\n};\n\ntype AppRscInterceptLookupEntry = {\n sourceRouteIndex: number;\n slotKey: string;\n targetPattern: string;\n targetPatternParts: string[];\n interceptLayouts: readonly unknown[];\n page: unknown;\n params: readonly string[];\n};\n\nfunction createRouteParams(): AppRscRouteParams {\n return Object.create(null);\n}\n\nexport function createAppRscRouteMatcher<Route extends AppRscRouteForMatching>(\n routes: Route[],\n): {\n matchRoute(url: string): { route: Route; params: AppRscRouteParams } | null;\n findIntercept(pathname: string, sourcePathname?: string | null): AppRscInterceptMatch | null;\n} {\n const routeTrie = buildRouteTrie(routes);\n const interceptLookup = createInterceptLookup(routes);\n\n return {\n matchRoute(url) {\n const pathname = url.split(\"?\")[0];\n const normalizedUrl = pathname === \"/\" ? \"/\" : pathname.replace(/\\/$/, \"\");\n // The request entry point owns decoding. Matching here preserves the\n // already-normalized segment bytes so middleware and routing stay aligned.\n const urlParts = normalizedUrl.split(\"/\").filter(Boolean);\n return trieMatch(routeTrie, urlParts);\n },\n findIntercept(pathname, sourcePathname = null) {\n const urlParts = pathname.split(\"/\").filter(Boolean);\n for (const entry of interceptLookup) {\n const params = matchAppRscRoutePattern(urlParts, entry.targetPatternParts);\n if (params !== null) {\n let sourceParams = createRouteParams();\n if (sourcePathname !== null) {\n const sourceRoute = routes[entry.sourceRouteIndex];\n const sourceParts = sourcePathname.split(\"/\").filter(Boolean);\n const matchedSourceParams = sourceRoute\n ? matchAppRscRoutePattern(sourceParts, sourceRoute.patternParts)\n : null;\n if (matchedSourceParams !== null) {\n sourceParams = matchedSourceParams;\n }\n }\n return { ...entry, matchedParams: mergeMatchedParams(sourceParams, params) };\n }\n }\n return null;\n },\n };\n}\n\nfunction createInterceptLookup<Route extends AppRscRouteForMatching>(\n routes: Route[],\n): AppRscInterceptLookupEntry[] {\n const interceptLookup: AppRscInterceptLookupEntry[] = [];\n for (let routeIndex = 0; routeIndex < routes.length; routeIndex++) {\n const route = routes[routeIndex];\n if (!route.slots) continue;\n for (const [slotKey, slotModule] of Object.entries(route.slots)) {\n if (!slotModule.intercepts) continue;\n for (const intercept of slotModule.intercepts) {\n interceptLookup.push({\n sourceRouteIndex: routeIndex,\n slotKey,\n targetPattern: intercept.targetPattern,\n targetPatternParts: intercept.targetPattern.split(\"/\").filter(Boolean),\n interceptLayouts: intercept.interceptLayouts,\n page: intercept.page,\n params: intercept.params,\n });\n }\n }\n }\n return interceptLookup;\n}\n\nexport function matchAppRscRoutePattern(\n urlParts: string[],\n patternParts: string[],\n): AppRscRouteParams | null {\n return matchRoutePattern(urlParts, patternParts);\n}\n\nfunction mergeMatchedParams(\n sourceParams: AppRscRouteParams,\n targetParams: AppRscRouteParams,\n): AppRscRouteParams {\n return Object.assign(createRouteParams(), sourceParams, targetParams);\n}\n"],"mappings":";;;AAmCA,SAAS,oBAAuC;AAC9C,QAAO,OAAO,OAAO,KAAK;;AAG5B,SAAgB,yBACd,QAIA;CACA,MAAM,YAAY,eAAe,OAAO;CACxC,MAAM,kBAAkB,sBAAsB,OAAO;AAErD,QAAO;EACL,WAAW,KAAK;GACd,MAAM,WAAW,IAAI,MAAM,IAAI,CAAC;AAKhC,UAAO,UAAU,YAJK,aAAa,MAAM,MAAM,SAAS,QAAQ,OAAO,GAAG,EAG3C,MAAM,IAAI,CAAC,OAAO,QAAQ,CACpB;;EAEvC,cAAc,UAAU,iBAAiB,MAAM;GAC7C,MAAM,WAAW,SAAS,MAAM,IAAI,CAAC,OAAO,QAAQ;AACpD,QAAK,MAAM,SAAS,iBAAiB;IACnC,MAAM,SAAS,wBAAwB,UAAU,MAAM,mBAAmB;AAC1E,QAAI,WAAW,MAAM;KACnB,IAAI,eAAe,mBAAmB;AACtC,SAAI,mBAAmB,MAAM;MAC3B,MAAM,cAAc,OAAO,MAAM;MACjC,MAAM,cAAc,eAAe,MAAM,IAAI,CAAC,OAAO,QAAQ;MAC7D,MAAM,sBAAsB,cACxB,wBAAwB,aAAa,YAAY,aAAa,GAC9D;AACJ,UAAI,wBAAwB,KAC1B,gBAAe;;AAGnB,YAAO;MAAE,GAAG;MAAO,eAAe,mBAAmB,cAAc,OAAO;MAAE;;;AAGhF,UAAO;;EAEV;;AAGH,SAAS,sBACP,QAC8B;CAC9B,MAAM,kBAAgD,EAAE;AACxD,MAAK,IAAI,aAAa,GAAG,aAAa,OAAO,QAAQ,cAAc;EACjE,MAAM,QAAQ,OAAO;AACrB,MAAI,CAAC,MAAM,MAAO;AAClB,OAAK,MAAM,CAAC,SAAS,eAAe,OAAO,QAAQ,MAAM,MAAM,EAAE;AAC/D,OAAI,CAAC,WAAW,WAAY;AAC5B,QAAK,MAAM,aAAa,WAAW,WACjC,iBAAgB,KAAK;IACnB,kBAAkB;IAClB;IACA,eAAe,UAAU;IACzB,oBAAoB,UAAU,cAAc,MAAM,IAAI,CAAC,OAAO,QAAQ;IACtE,kBAAkB,UAAU;IAC5B,MAAM,UAAU;IAChB,QAAQ,UAAU;IACnB,CAAC;;;AAIR,QAAO;;AAGT,SAAgB,wBACd,UACA,cAC0B;AAC1B,QAAO,kBAAkB,UAAU,aAAa;;AAGlD,SAAS,mBACP,cACA,cACmB;AACnB,QAAO,OAAO,OAAO,mBAAmB,EAAE,cAAc,aAAa"}
1
+ {"version":3,"file":"app-rsc-route-matching.js","names":[],"sources":["../../src/server/app-rsc-route-matching.ts"],"sourcesContent":["import { buildRouteTrie, trieMatch } from \"../routing/route-trie.js\";\nimport { matchRoutePattern, type RoutePatternParams } from \"../routing/route-pattern.js\";\nimport { normalizePathnameForRouteMatch } from \"../routing/utils.js\";\n\ntype AppRscRouteParams = RoutePatternParams;\n\ntype AppRscInterceptForMatching = {\n targetPattern: string;\n interceptLayouts: readonly unknown[];\n page: unknown;\n params: readonly string[];\n};\n\ntype AppRscSlotForMatching = {\n intercepts?: readonly AppRscInterceptForMatching[];\n};\n\ntype AppRscRouteForMatching = {\n patternParts: string[];\n slots?: Record<string, AppRscSlotForMatching>;\n};\n\ntype AppRscInterceptMatch = AppRscInterceptLookupEntry & {\n matchedParams: AppRscRouteParams;\n};\n\ntype AppRscInterceptLookupEntry = {\n sourceRouteIndex: number;\n slotKey: string;\n targetPattern: string;\n targetPatternParts: string[];\n interceptLayouts: readonly unknown[];\n page: unknown;\n params: readonly string[];\n};\n\nfunction createRouteParams(): AppRscRouteParams {\n return Object.create(null);\n}\n\nfunction appRscPathnameParts(pathname: string): string[] {\n const pathOnly = pathname.split(\"?\")[0];\n const normalized = pathOnly === \"/\" ? \"/\" : pathOnly.replace(/\\/$/, \"\");\n return normalizePathnameForRouteMatch(normalized).split(\"/\").filter(Boolean);\n}\n\nexport function createAppRscRouteMatcher<Route extends AppRscRouteForMatching>(\n routes: Route[],\n): {\n matchRoute(url: string): { route: Route; params: AppRscRouteParams } | null;\n findIntercept(pathname: string, sourcePathname?: string | null): AppRscInterceptMatch | null;\n} {\n const routeTrie = buildRouteTrie(routes);\n const interceptLookup = createInterceptLookup(routes);\n\n return {\n matchRoute(url) {\n return trieMatch(routeTrie, appRscPathnameParts(url));\n },\n findIntercept(pathname, sourcePathname = null) {\n const urlParts = appRscPathnameParts(pathname);\n for (const entry of interceptLookup) {\n const params = matchAppRscRoutePattern(urlParts, entry.targetPatternParts);\n if (params !== null) {\n let sourceParams = createRouteParams();\n if (sourcePathname !== null) {\n const sourceRoute = routes[entry.sourceRouteIndex];\n const sourceParts = appRscPathnameParts(sourcePathname);\n const matchedSourceParams = sourceRoute\n ? matchAppRscRoutePattern(sourceParts, sourceRoute.patternParts)\n : null;\n if (matchedSourceParams !== null) {\n sourceParams = matchedSourceParams;\n }\n }\n return { ...entry, matchedParams: mergeMatchedParams(sourceParams, params) };\n }\n }\n return null;\n },\n };\n}\n\nfunction createInterceptLookup<Route extends AppRscRouteForMatching>(\n routes: Route[],\n): AppRscInterceptLookupEntry[] {\n const interceptLookup: AppRscInterceptLookupEntry[] = [];\n for (let routeIndex = 0; routeIndex < routes.length; routeIndex++) {\n const route = routes[routeIndex];\n if (!route.slots) continue;\n for (const [slotKey, slotModule] of Object.entries(route.slots)) {\n if (!slotModule.intercepts) continue;\n for (const intercept of slotModule.intercepts) {\n interceptLookup.push({\n sourceRouteIndex: routeIndex,\n slotKey,\n targetPattern: intercept.targetPattern,\n targetPatternParts: intercept.targetPattern.split(\"/\").filter(Boolean),\n interceptLayouts: intercept.interceptLayouts,\n page: intercept.page,\n params: intercept.params,\n });\n }\n }\n }\n return interceptLookup;\n}\n\nexport function matchAppRscRoutePattern(\n urlParts: string[],\n patternParts: string[],\n): AppRscRouteParams | null {\n return matchRoutePattern(urlParts, patternParts);\n}\n\nfunction mergeMatchedParams(\n sourceParams: AppRscRouteParams,\n targetParams: AppRscRouteParams,\n): AppRscRouteParams {\n return Object.assign(createRouteParams(), sourceParams, targetParams);\n}\n"],"mappings":";;;;AAoCA,SAAS,oBAAuC;AAC9C,QAAO,OAAO,OAAO,KAAK;;AAG5B,SAAS,oBAAoB,UAA4B;CACvD,MAAM,WAAW,SAAS,MAAM,IAAI,CAAC;AAErC,QAAO,+BADY,aAAa,MAAM,MAAM,SAAS,QAAQ,OAAO,GAAG,CACtB,CAAC,MAAM,IAAI,CAAC,OAAO,QAAQ;;AAG9E,SAAgB,yBACd,QAIA;CACA,MAAM,YAAY,eAAe,OAAO;CACxC,MAAM,kBAAkB,sBAAsB,OAAO;AAErD,QAAO;EACL,WAAW,KAAK;AACd,UAAO,UAAU,WAAW,oBAAoB,IAAI,CAAC;;EAEvD,cAAc,UAAU,iBAAiB,MAAM;GAC7C,MAAM,WAAW,oBAAoB,SAAS;AAC9C,QAAK,MAAM,SAAS,iBAAiB;IACnC,MAAM,SAAS,wBAAwB,UAAU,MAAM,mBAAmB;AAC1E,QAAI,WAAW,MAAM;KACnB,IAAI,eAAe,mBAAmB;AACtC,SAAI,mBAAmB,MAAM;MAC3B,MAAM,cAAc,OAAO,MAAM;MACjC,MAAM,cAAc,oBAAoB,eAAe;MACvD,MAAM,sBAAsB,cACxB,wBAAwB,aAAa,YAAY,aAAa,GAC9D;AACJ,UAAI,wBAAwB,KAC1B,gBAAe;;AAGnB,YAAO;MAAE,GAAG;MAAO,eAAe,mBAAmB,cAAc,OAAO;MAAE;;;AAGhF,UAAO;;EAEV;;AAGH,SAAS,sBACP,QAC8B;CAC9B,MAAM,kBAAgD,EAAE;AACxD,MAAK,IAAI,aAAa,GAAG,aAAa,OAAO,QAAQ,cAAc;EACjE,MAAM,QAAQ,OAAO;AACrB,MAAI,CAAC,MAAM,MAAO;AAClB,OAAK,MAAM,CAAC,SAAS,eAAe,OAAO,QAAQ,MAAM,MAAM,EAAE;AAC/D,OAAI,CAAC,WAAW,WAAY;AAC5B,QAAK,MAAM,aAAa,WAAW,WACjC,iBAAgB,KAAK;IACnB,kBAAkB;IAClB;IACA,eAAe,UAAU;IACzB,oBAAoB,UAAU,cAAc,MAAM,IAAI,CAAC,OAAO,QAAQ;IACtE,kBAAkB,UAAU;IAC5B,MAAM,UAAU;IAChB,QAAQ,UAAU;IACnB,CAAC;;;AAIR,QAAO;;AAGT,SAAgB,wBACd,UACA,cAC0B;AAC1B,QAAO,kBAAkB,UAAU,aAAa;;AAGlD,SAAS,mBACP,cACA,cACmB;AACnB,QAAO,OAAO,OAAO,mBAAmB,EAAE,cAAc,aAAa"}
@@ -21,6 +21,10 @@ function isRouteSegmentFetchCache(value) {
21
21
  return FETCH_CACHE_VALUES.has(value);
22
22
  }
23
23
  function resolveRevalidateSeconds(current, value) {
24
+ if (value === false) {
25
+ if (current === null) return Infinity;
26
+ return current === Infinity ? Infinity : current;
27
+ }
24
28
  if (typeof value !== "number") return current;
25
29
  if (current === null) return value;
26
30
  return value < current ? value : current;
@@ -1 +1 @@
1
- {"version":3,"file":"app-segment-config.js","names":[],"sources":["../../src/server/app-segment-config.ts"],"sourcesContent":["import type { FetchCacheMode } from \"vinext/shims/fetch-cache\";\n\ntype AppRouteSegmentDynamic = \"auto\" | \"error\" | \"force-dynamic\" | \"force-static\";\n\ntype AppRouteSegmentConfigModule = {\n dynamic?: unknown;\n dynamicParams?: unknown;\n fetchCache?: unknown;\n revalidate?: unknown;\n};\n\ntype EffectiveAppPageSegmentConfig = {\n dynamicConfig?: AppRouteSegmentDynamic;\n dynamicParamsConfig?: boolean;\n fetchCache?: FetchCacheMode;\n revalidateSeconds: number | null;\n};\n\ntype ResolveAppPageSegmentConfigOptions = {\n layouts?: readonly (AppRouteSegmentConfigModule | null | undefined)[];\n page?: AppRouteSegmentConfigModule | null;\n};\n\nconst DYNAMIC_VALUES = new Set<unknown>([\"auto\", \"error\", \"force-dynamic\", \"force-static\"]);\nconst FETCH_CACHE_VALUES = new Set<unknown>([\n \"auto\",\n \"default-cache\",\n \"default-no-store\",\n \"force-cache\",\n \"force-no-store\",\n \"only-cache\",\n \"only-no-store\",\n]);\n\nfunction isRouteSegmentDynamic(value: unknown): value is AppRouteSegmentDynamic {\n return DYNAMIC_VALUES.has(value);\n}\n\nfunction isRouteSegmentFetchCache(value: unknown): value is FetchCacheMode {\n return FETCH_CACHE_VALUES.has(value);\n}\n\nfunction resolveRevalidateSeconds(current: number | null, value: unknown): number | null {\n // TODO: Next.js also accepts `revalidate = false` for indefinite caching.\n // Existing vinext code ignores non-numeric values; keep that behavior until\n // the cache layer can represent \"cache forever\" distinctly from \"unset\".\n if (typeof value !== \"number\") {\n return current;\n }\n\n if (current === null) {\n return value;\n }\n\n return value < current ? value : current;\n}\n\nfunction isCacheFetchCacheMode(value: FetchCacheMode): boolean {\n return value === \"default-cache\" || value === \"force-cache\" || value === \"only-cache\";\n}\n\nfunction describeFetchCacheConflict(value: FetchCacheMode): string {\n return `Route segment config has incompatible fetchCache values including \"${value}\".`;\n}\n\n/**\n * Resolve the route segment config that applies to an App page route.\n *\n * Next.js collects config from every segment in the loader tree and reduces it\n * into the effective route config. The generated vinext entry already knows\n * the concrete layout/page modules for a route, so it should only describe\n * those modules and delegate the behavior to this helper.\n */\nexport function resolveAppPageSegmentConfig(\n options: ResolveAppPageSegmentConfigOptions,\n): EffectiveAppPageSegmentConfig {\n const segments = [...(options.layouts ?? []), options.page];\n // Reduction strategies differ by field:\n // - dynamic: child segments override parents.\n // - dynamicParams: false is sticky across the route tree.\n // - fetchCache: force/only modes take route-level precedence and reject conflicts.\n // - revalidate: the shortest numeric interval wins.\n const config: EffectiveAppPageSegmentConfig = {\n revalidateSeconds: null,\n };\n let hasForceCache = false;\n let hasForceNoStore = false;\n let hasOnlyCache = false;\n let hasOnlyNoStore = false;\n let hasParentDefaultNoStore = false;\n\n for (const segment of segments) {\n if (!segment) continue;\n\n if (isRouteSegmentDynamic(segment.dynamic)) {\n config.dynamicConfig = segment.dynamic;\n }\n\n if (segment.dynamicParams === false) {\n config.dynamicParamsConfig = false;\n } else if (segment.dynamicParams === true && config.dynamicParamsConfig !== false) {\n config.dynamicParamsConfig = true;\n }\n\n if (isRouteSegmentFetchCache(segment.fetchCache)) {\n const fetchCache = segment.fetchCache;\n\n if (hasParentDefaultNoStore && (fetchCache === \"auto\" || isCacheFetchCacheMode(fetchCache))) {\n throw new Error(describeFetchCacheConflict(fetchCache));\n }\n\n if (fetchCache === \"force-cache\") hasForceCache = true;\n if (fetchCache === \"force-no-store\") hasForceNoStore = true;\n if (fetchCache === \"only-cache\") hasOnlyCache = true;\n if (fetchCache === \"only-no-store\") hasOnlyNoStore = true;\n\n const hasCacheEnforcer = hasForceCache || hasOnlyCache;\n const hasNoStoreEnforcer = hasForceNoStore || hasOnlyNoStore;\n if (hasCacheEnforcer && hasNoStoreEnforcer) {\n throw new Error(describeFetchCacheConflict(fetchCache));\n }\n\n if (fetchCache === \"default-no-store\") {\n hasParentDefaultNoStore = true;\n }\n\n if (hasForceCache) {\n config.fetchCache = \"force-cache\";\n } else if (hasForceNoStore) {\n config.fetchCache = \"force-no-store\";\n } else if (hasOnlyCache) {\n config.fetchCache = \"only-cache\";\n } else if (hasOnlyNoStore) {\n config.fetchCache = \"only-no-store\";\n } else {\n config.fetchCache = fetchCache;\n }\n }\n\n config.revalidateSeconds = resolveRevalidateSeconds(\n config.revalidateSeconds,\n segment.revalidate,\n );\n }\n\n if (config.dynamicConfig === \"force-dynamic\") {\n config.revalidateSeconds = 0;\n }\n\n // Top-level dynamic modes supply fetchCache defaults unless a segment does.\n if (config.fetchCache === undefined) {\n if (config.dynamicConfig === \"force-dynamic\") {\n config.fetchCache = \"force-no-store\";\n } else if (config.dynamicConfig === \"error\") {\n config.fetchCache = \"only-cache\";\n }\n }\n\n // Static-only dynamic modes change the default, but explicit dynamicParams wins.\n if (\n config.dynamicParamsConfig === undefined &&\n (config.dynamicConfig === \"error\" || config.dynamicConfig === \"force-static\")\n ) {\n config.dynamicParamsConfig = false;\n }\n\n return config;\n}\n\nexport function resolveAppPageFetchCacheMode(\n options: ResolveAppPageSegmentConfigOptions,\n): FetchCacheMode | null {\n return resolveAppPageSegmentConfig(options).fetchCache ?? null;\n}\n"],"mappings":";AAuBA,MAAM,iBAAiB,IAAI,IAAa;CAAC;CAAQ;CAAS;CAAiB;CAAe,CAAC;AAC3F,MAAM,qBAAqB,IAAI,IAAa;CAC1C;CACA;CACA;CACA;CACA;CACA;CACA;CACD,CAAC;AAEF,SAAS,sBAAsB,OAAiD;AAC9E,QAAO,eAAe,IAAI,MAAM;;AAGlC,SAAS,yBAAyB,OAAyC;AACzE,QAAO,mBAAmB,IAAI,MAAM;;AAGtC,SAAS,yBAAyB,SAAwB,OAA+B;AAIvF,KAAI,OAAO,UAAU,SACnB,QAAO;AAGT,KAAI,YAAY,KACd,QAAO;AAGT,QAAO,QAAQ,UAAU,QAAQ;;AAGnC,SAAS,sBAAsB,OAAgC;AAC7D,QAAO,UAAU,mBAAmB,UAAU,iBAAiB,UAAU;;AAG3E,SAAS,2BAA2B,OAA+B;AACjE,QAAO,sEAAsE,MAAM;;;;;;;;;;AAWrF,SAAgB,4BACd,SAC+B;CAC/B,MAAM,WAAW,CAAC,GAAI,QAAQ,WAAW,EAAE,EAAG,QAAQ,KAAK;CAM3D,MAAM,SAAwC,EAC5C,mBAAmB,MACpB;CACD,IAAI,gBAAgB;CACpB,IAAI,kBAAkB;CACtB,IAAI,eAAe;CACnB,IAAI,iBAAiB;CACrB,IAAI,0BAA0B;AAE9B,MAAK,MAAM,WAAW,UAAU;AAC9B,MAAI,CAAC,QAAS;AAEd,MAAI,sBAAsB,QAAQ,QAAQ,CACxC,QAAO,gBAAgB,QAAQ;AAGjC,MAAI,QAAQ,kBAAkB,MAC5B,QAAO,sBAAsB;WACpB,QAAQ,kBAAkB,QAAQ,OAAO,wBAAwB,MAC1E,QAAO,sBAAsB;AAG/B,MAAI,yBAAyB,QAAQ,WAAW,EAAE;GAChD,MAAM,aAAa,QAAQ;AAE3B,OAAI,4BAA4B,eAAe,UAAU,sBAAsB,WAAW,EACxF,OAAM,IAAI,MAAM,2BAA2B,WAAW,CAAC;AAGzD,OAAI,eAAe,cAAe,iBAAgB;AAClD,OAAI,eAAe,iBAAkB,mBAAkB;AACvD,OAAI,eAAe,aAAc,gBAAe;AAChD,OAAI,eAAe,gBAAiB,kBAAiB;AAIrD,QAFyB,iBAAiB,kBACf,mBAAmB,gBAE5C,OAAM,IAAI,MAAM,2BAA2B,WAAW,CAAC;AAGzD,OAAI,eAAe,mBACjB,2BAA0B;AAG5B,OAAI,cACF,QAAO,aAAa;YACX,gBACT,QAAO,aAAa;YACX,aACT,QAAO,aAAa;YACX,eACT,QAAO,aAAa;OAEpB,QAAO,aAAa;;AAIxB,SAAO,oBAAoB,yBACzB,OAAO,mBACP,QAAQ,WACT;;AAGH,KAAI,OAAO,kBAAkB,gBAC3B,QAAO,oBAAoB;AAI7B,KAAI,OAAO,eAAe,KAAA;MACpB,OAAO,kBAAkB,gBAC3B,QAAO,aAAa;WACX,OAAO,kBAAkB,QAClC,QAAO,aAAa;;AAKxB,KACE,OAAO,wBAAwB,KAAA,MAC9B,OAAO,kBAAkB,WAAW,OAAO,kBAAkB,gBAE9D,QAAO,sBAAsB;AAG/B,QAAO;;AAGT,SAAgB,6BACd,SACuB;AACvB,QAAO,4BAA4B,QAAQ,CAAC,cAAc"}
1
+ {"version":3,"file":"app-segment-config.js","names":[],"sources":["../../src/server/app-segment-config.ts"],"sourcesContent":["import type { FetchCacheMode } from \"vinext/shims/fetch-cache\";\n\ntype AppRouteSegmentDynamic = \"auto\" | \"error\" | \"force-dynamic\" | \"force-static\";\n\ntype AppRouteSegmentConfigModule = {\n dynamic?: unknown;\n dynamicParams?: unknown;\n fetchCache?: unknown;\n revalidate?: unknown;\n};\n\ntype EffectiveAppPageSegmentConfig = {\n dynamicConfig?: AppRouteSegmentDynamic;\n dynamicParamsConfig?: boolean;\n fetchCache?: FetchCacheMode;\n revalidateSeconds: number | null;\n};\n\ntype ResolveAppPageSegmentConfigOptions = {\n layouts?: readonly (AppRouteSegmentConfigModule | null | undefined)[];\n page?: AppRouteSegmentConfigModule | null;\n};\n\nconst DYNAMIC_VALUES = new Set<unknown>([\"auto\", \"error\", \"force-dynamic\", \"force-static\"]);\nconst FETCH_CACHE_VALUES = new Set<unknown>([\n \"auto\",\n \"default-cache\",\n \"default-no-store\",\n \"force-cache\",\n \"force-no-store\",\n \"only-cache\",\n \"only-no-store\",\n]);\n\nfunction isRouteSegmentDynamic(value: unknown): value is AppRouteSegmentDynamic {\n return DYNAMIC_VALUES.has(value);\n}\n\nfunction isRouteSegmentFetchCache(value: unknown): value is FetchCacheMode {\n return FETCH_CACHE_VALUES.has(value);\n}\n\nfunction resolveRevalidateSeconds(current: number | null, value: unknown): number | null {\n // revalidate = false means \"cache indefinitely\" in Next.js segment config.\n // Represent it as Infinity so downstream code can distinguish \"never\n // revalidate\" (Infinity) from \"no config / unset\" (null).\n if (value === false) {\n if (current === null) return Infinity;\n // Shortest-wins: any finite interval is shorter than Infinity.\n return current === Infinity ? Infinity : current;\n }\n\n if (typeof value !== \"number\") {\n return current;\n }\n\n if (current === null) {\n return value;\n }\n\n return value < current ? value : current;\n}\n\nfunction isCacheFetchCacheMode(value: FetchCacheMode): boolean {\n return value === \"default-cache\" || value === \"force-cache\" || value === \"only-cache\";\n}\n\nfunction describeFetchCacheConflict(value: FetchCacheMode): string {\n return `Route segment config has incompatible fetchCache values including \"${value}\".`;\n}\n\n/**\n * Resolve the route segment config that applies to an App page route.\n *\n * Next.js collects config from every segment in the loader tree and reduces it\n * into the effective route config. The generated vinext entry already knows\n * the concrete layout/page modules for a route, so it should only describe\n * those modules and delegate the behavior to this helper.\n */\nexport function resolveAppPageSegmentConfig(\n options: ResolveAppPageSegmentConfigOptions,\n): EffectiveAppPageSegmentConfig {\n const segments = [...(options.layouts ?? []), options.page];\n // Reduction strategies differ by field:\n // - dynamic: child segments override parents.\n // - dynamicParams: false is sticky across the route tree.\n // - fetchCache: force/only modes take route-level precedence and reject conflicts.\n // - revalidate: the shortest numeric interval wins.\n const config: EffectiveAppPageSegmentConfig = {\n revalidateSeconds: null,\n };\n let hasForceCache = false;\n let hasForceNoStore = false;\n let hasOnlyCache = false;\n let hasOnlyNoStore = false;\n let hasParentDefaultNoStore = false;\n\n for (const segment of segments) {\n if (!segment) continue;\n\n if (isRouteSegmentDynamic(segment.dynamic)) {\n config.dynamicConfig = segment.dynamic;\n }\n\n if (segment.dynamicParams === false) {\n config.dynamicParamsConfig = false;\n } else if (segment.dynamicParams === true && config.dynamicParamsConfig !== false) {\n config.dynamicParamsConfig = true;\n }\n\n if (isRouteSegmentFetchCache(segment.fetchCache)) {\n const fetchCache = segment.fetchCache;\n\n if (hasParentDefaultNoStore && (fetchCache === \"auto\" || isCacheFetchCacheMode(fetchCache))) {\n throw new Error(describeFetchCacheConflict(fetchCache));\n }\n\n if (fetchCache === \"force-cache\") hasForceCache = true;\n if (fetchCache === \"force-no-store\") hasForceNoStore = true;\n if (fetchCache === \"only-cache\") hasOnlyCache = true;\n if (fetchCache === \"only-no-store\") hasOnlyNoStore = true;\n\n const hasCacheEnforcer = hasForceCache || hasOnlyCache;\n const hasNoStoreEnforcer = hasForceNoStore || hasOnlyNoStore;\n if (hasCacheEnforcer && hasNoStoreEnforcer) {\n throw new Error(describeFetchCacheConflict(fetchCache));\n }\n\n if (fetchCache === \"default-no-store\") {\n hasParentDefaultNoStore = true;\n }\n\n if (hasForceCache) {\n config.fetchCache = \"force-cache\";\n } else if (hasForceNoStore) {\n config.fetchCache = \"force-no-store\";\n } else if (hasOnlyCache) {\n config.fetchCache = \"only-cache\";\n } else if (hasOnlyNoStore) {\n config.fetchCache = \"only-no-store\";\n } else {\n config.fetchCache = fetchCache;\n }\n }\n\n config.revalidateSeconds = resolveRevalidateSeconds(\n config.revalidateSeconds,\n segment.revalidate,\n );\n }\n\n if (config.dynamicConfig === \"force-dynamic\") {\n config.revalidateSeconds = 0;\n }\n\n // Top-level dynamic modes supply fetchCache defaults unless a segment does.\n if (config.fetchCache === undefined) {\n if (config.dynamicConfig === \"force-dynamic\") {\n config.fetchCache = \"force-no-store\";\n } else if (config.dynamicConfig === \"error\") {\n config.fetchCache = \"only-cache\";\n }\n }\n\n // Static-only dynamic modes change the default, but explicit dynamicParams wins.\n if (\n config.dynamicParamsConfig === undefined &&\n (config.dynamicConfig === \"error\" || config.dynamicConfig === \"force-static\")\n ) {\n config.dynamicParamsConfig = false;\n }\n\n return config;\n}\n\nexport function resolveAppPageFetchCacheMode(\n options: ResolveAppPageSegmentConfigOptions,\n): FetchCacheMode | null {\n return resolveAppPageSegmentConfig(options).fetchCache ?? null;\n}\n"],"mappings":";AAuBA,MAAM,iBAAiB,IAAI,IAAa;CAAC;CAAQ;CAAS;CAAiB;CAAe,CAAC;AAC3F,MAAM,qBAAqB,IAAI,IAAa;CAC1C;CACA;CACA;CACA;CACA;CACA;CACA;CACD,CAAC;AAEF,SAAS,sBAAsB,OAAiD;AAC9E,QAAO,eAAe,IAAI,MAAM;;AAGlC,SAAS,yBAAyB,OAAyC;AACzE,QAAO,mBAAmB,IAAI,MAAM;;AAGtC,SAAS,yBAAyB,SAAwB,OAA+B;AAIvF,KAAI,UAAU,OAAO;AACnB,MAAI,YAAY,KAAM,QAAO;AAE7B,SAAO,YAAY,WAAW,WAAW;;AAG3C,KAAI,OAAO,UAAU,SACnB,QAAO;AAGT,KAAI,YAAY,KACd,QAAO;AAGT,QAAO,QAAQ,UAAU,QAAQ;;AAGnC,SAAS,sBAAsB,OAAgC;AAC7D,QAAO,UAAU,mBAAmB,UAAU,iBAAiB,UAAU;;AAG3E,SAAS,2BAA2B,OAA+B;AACjE,QAAO,sEAAsE,MAAM;;;;;;;;;;AAWrF,SAAgB,4BACd,SAC+B;CAC/B,MAAM,WAAW,CAAC,GAAI,QAAQ,WAAW,EAAE,EAAG,QAAQ,KAAK;CAM3D,MAAM,SAAwC,EAC5C,mBAAmB,MACpB;CACD,IAAI,gBAAgB;CACpB,IAAI,kBAAkB;CACtB,IAAI,eAAe;CACnB,IAAI,iBAAiB;CACrB,IAAI,0BAA0B;AAE9B,MAAK,MAAM,WAAW,UAAU;AAC9B,MAAI,CAAC,QAAS;AAEd,MAAI,sBAAsB,QAAQ,QAAQ,CACxC,QAAO,gBAAgB,QAAQ;AAGjC,MAAI,QAAQ,kBAAkB,MAC5B,QAAO,sBAAsB;WACpB,QAAQ,kBAAkB,QAAQ,OAAO,wBAAwB,MAC1E,QAAO,sBAAsB;AAG/B,MAAI,yBAAyB,QAAQ,WAAW,EAAE;GAChD,MAAM,aAAa,QAAQ;AAE3B,OAAI,4BAA4B,eAAe,UAAU,sBAAsB,WAAW,EACxF,OAAM,IAAI,MAAM,2BAA2B,WAAW,CAAC;AAGzD,OAAI,eAAe,cAAe,iBAAgB;AAClD,OAAI,eAAe,iBAAkB,mBAAkB;AACvD,OAAI,eAAe,aAAc,gBAAe;AAChD,OAAI,eAAe,gBAAiB,kBAAiB;AAIrD,QAFyB,iBAAiB,kBACf,mBAAmB,gBAE5C,OAAM,IAAI,MAAM,2BAA2B,WAAW,CAAC;AAGzD,OAAI,eAAe,mBACjB,2BAA0B;AAG5B,OAAI,cACF,QAAO,aAAa;YACX,gBACT,QAAO,aAAa;YACX,aACT,QAAO,aAAa;YACX,eACT,QAAO,aAAa;OAEpB,QAAO,aAAa;;AAIxB,SAAO,oBAAoB,yBACzB,OAAO,mBACP,QAAQ,WACT;;AAGH,KAAI,OAAO,kBAAkB,gBAC3B,QAAO,oBAAoB;AAI7B,KAAI,OAAO,eAAe,KAAA;MACpB,OAAO,kBAAkB,gBAC3B,QAAO,aAAa;WACX,OAAO,kBAAkB,QAClC,QAAO,aAAa;;AAKxB,KACE,OAAO,wBAAwB,KAAA,MAC9B,OAAO,kBAAkB,WAAW,OAAO,kBAAkB,gBAE9D,QAAO,sBAAsB;AAG/B,QAAO;;AAGT,SAAgB,6BACd,SACuB;AACvB,QAAO,4BAA4B,QAAQ,CAAC,cAAc"}
@@ -1,9 +1,18 @@
1
+ import { internalServerErrorResponse, payloadTooLargeResponse } from "./http-error-responses.js";
1
2
  import { validateCsrfOrigin, validateServerActionPayload } from "./request-pipeline.js";
2
3
  import { setCurrentFetchCacheMode } from "../shims/fetch-cache.js";
4
+ import { VINEXT_RSC_VARY_HEADER } from "./app-rsc-cache-busting.js";
3
5
  import { createServerActionNotFoundResponse, getServerActionNotFoundMessage, isServerActionNotFoundError } from "./server-action-not-found.js";
4
6
  import { mergeMiddlewareResponseHeaders } from "./middleware-response-headers.js";
7
+ import { readStreamAsTextWithLimit } from "../utils/text-stream.js";
8
+ import { getNextErrorDigest, parseNextHttpErrorDigest, parseNextRedirectDigest } from "./next-error-digest.js";
5
9
  import { resolveAppPageActionRerenderTarget } from "./app-page-request.js";
6
10
  //#region src/server/app-server-action-execution.ts
11
+ /**
12
+ * Matches Next.js' server action argument cap to prevent stack overflow in
13
+ * Function.prototype.apply when decoding hostile action payloads.
14
+ */
15
+ const SERVER_ACTION_ARGS_LIMIT = 1e3;
7
16
  function isRequestBodyTooLarge(error) {
8
17
  return error instanceof Error && error.message === "Request body too large";
9
18
  }
@@ -16,24 +25,14 @@ function normalizeError(error) {
16
25
  function getServerActionFailureMessage(error) {
17
26
  return error instanceof Error && error.message ? error.message : String(error);
18
27
  }
28
+ function validateServerActionArgs(args) {
29
+ if (args.length > SERVER_ACTION_ARGS_LIMIT) throw new Error(`Server Action arguments list is too long (${args.length}). Maximum allowed is ${SERVER_ACTION_ARGS_LIMIT}.`);
30
+ }
19
31
  async function readActionBodyWithLimit(request, maxBytes) {
20
32
  if (!request.body) return "";
21
- const reader = request.body.getReader();
22
- const decoder = new TextDecoder();
23
- const chunks = [];
24
- let totalSize = 0;
25
- for (;;) {
26
- const result = await reader.read();
27
- if (result.done) break;
28
- totalSize += result.value.byteLength;
29
- if (totalSize > maxBytes) {
30
- await reader.cancel();
31
- throw new Error("Request body too large");
32
- }
33
- chunks.push(decoder.decode(result.value, { stream: true }));
34
- }
35
- chunks.push(decoder.decode());
36
- return chunks.join("");
33
+ return readStreamAsTextWithLimit(request.body, maxBytes, () => {
34
+ throw new Error("Request body too large");
35
+ });
37
36
  }
38
37
  async function readActionFormDataWithLimit(request, maxBytes) {
39
38
  if (!request.body) return new FormData();
@@ -58,46 +57,38 @@ async function readActionFormDataWithLimit(request, maxBytes) {
58
57
  }
59
58
  return new Response(combined, { headers: { "Content-Type": request.headers.get("content-type") || "" } }).formData();
60
59
  }
61
- function getErrorDigest(error) {
62
- if (!error || typeof error !== "object" || !("digest" in error)) return null;
63
- return String(error.digest);
64
- }
65
60
  function getActionControlResponse(error) {
66
- const digest = getErrorDigest(error);
61
+ const digest = getNextErrorDigest(error);
67
62
  if (!digest) return null;
68
- if (digest.startsWith("NEXT_REDIRECT;")) {
69
- const encodedUrl = digest.split(";")[2];
70
- if (!encodedUrl) return null;
71
- return {
72
- kind: "redirect",
73
- url: decodeURIComponent(encodedUrl)
74
- };
75
- }
76
- if (digest === "NEXT_NOT_FOUND" || digest.startsWith("NEXT_HTTP_ERROR_FALLBACK;")) {
77
- const statusCode = digest === "NEXT_NOT_FOUND" ? 404 : parseInt(digest.split(";")[1], 10);
78
- if (!Number.isInteger(statusCode)) return null;
63
+ const redirect = parseNextRedirectDigest(digest);
64
+ if (redirect) return {
65
+ kind: "redirect",
66
+ url: redirect.url
67
+ };
68
+ const httpError = parseNextHttpErrorDigest(digest);
69
+ if (httpError) {
70
+ if (!Number.isInteger(httpError.status)) return null;
79
71
  return {
80
72
  kind: "status",
81
- statusCode
73
+ statusCode: httpError.status
82
74
  };
83
75
  }
84
76
  return null;
85
77
  }
86
78
  function getActionRedirect(error) {
87
- const digest = getErrorDigest(error);
88
- if (!digest?.startsWith("NEXT_REDIRECT;")) return null;
89
- const parts = digest.split(";");
90
- const encodedUrl = parts[2];
91
- if (!encodedUrl) return null;
79
+ const digest = getNextErrorDigest(error);
80
+ if (!digest) return null;
81
+ const redirect = parseNextRedirectDigest(digest);
82
+ if (!redirect) return null;
92
83
  return {
93
- status: parts[3] ? parseInt(parts[3], 10) : 307,
94
- type: parts[1] || "push",
95
- url: decodeURIComponent(encodedUrl)
84
+ status: redirect.status,
85
+ type: redirect.type,
86
+ url: redirect.url
96
87
  };
97
88
  }
98
89
  function isActionHttpFallback(error) {
99
- const digest = getErrorDigest(error);
100
- return digest === "NEXT_NOT_FOUND" || digest?.startsWith("NEXT_HTTP_ERROR_FALLBACK;") === true;
90
+ const digest = getNextErrorDigest(error);
91
+ return digest !== null && parseNextHttpErrorDigest(digest) !== null;
101
92
  }
102
93
  function createServerActionErrorResponse(error, options) {
103
94
  options.getAndClearPendingCookies();
@@ -112,7 +103,7 @@ function createServerActionErrorResponse(error, options) {
112
103
  routeType: "action"
113
104
  });
114
105
  options.clearRequestContext();
115
- return new Response(process.env.NODE_ENV === "production" ? "Internal Server Error" : "Server action failed: " + getServerActionFailureMessage(error), { status: 500 });
106
+ return internalServerErrorResponse(process.env.NODE_ENV === "production" ? void 0 : "Server action failed: " + getServerActionFailureMessage(error));
116
107
  }
117
108
  function createActionNotFoundResponse(actionId, options) {
118
109
  options.getAndClearPendingCookies();
@@ -129,7 +120,7 @@ async function handleProgressiveServerActionRequest(options) {
129
120
  if (csrfResponse) return csrfResponse;
130
121
  if (parseInt(options.request.headers.get("content-length") || "0", 10) > options.maxActionBodySize) {
131
122
  options.clearRequestContext();
132
- return new Response("Payload Too Large", { status: 413 });
123
+ return payloadTooLargeResponse();
133
124
  }
134
125
  try {
135
126
  let body;
@@ -138,7 +129,7 @@ async function handleProgressiveServerActionRequest(options) {
138
129
  } catch (error) {
139
130
  if (isRequestBodyTooLarge(error)) {
140
131
  options.clearRequestContext();
141
- return new Response("Payload Too Large", { status: 413 });
132
+ return payloadTooLargeResponse();
142
133
  }
143
134
  throw error;
144
135
  }
@@ -189,7 +180,7 @@ async function handleProgressiveServerActionRequest(options) {
189
180
  routeType: "action"
190
181
  });
191
182
  options.clearRequestContext();
192
- return new Response(process.env.NODE_ENV === "production" ? "Internal Server Error" : "Server action failed: " + getServerActionFailureMessage(error), { status: 500 });
183
+ return internalServerErrorResponse(process.env.NODE_ENV === "production" ? void 0 : "Server action failed: " + getServerActionFailureMessage(error));
193
184
  }
194
185
  }
195
186
  async function handleServerActionRscRequest(options) {
@@ -198,7 +189,7 @@ async function handleServerActionRscRequest(options) {
198
189
  if (csrfResponse) return csrfResponse;
199
190
  if (parseInt(options.request.headers.get("content-length") || "0", 10) > options.maxActionBodySize) {
200
191
  options.clearRequestContext();
201
- return new Response("Payload Too Large", { status: 413 });
192
+ return payloadTooLargeResponse();
202
193
  }
203
194
  try {
204
195
  let body;
@@ -207,7 +198,7 @@ async function handleServerActionRscRequest(options) {
207
198
  } catch (error) {
208
199
  if (isRequestBodyTooLarge(error)) {
209
200
  options.clearRequestContext();
210
- return new Response("Payload Too Large", { status: 413 });
201
+ return payloadTooLargeResponse();
211
202
  }
212
203
  throw error;
213
204
  }
@@ -237,6 +228,7 @@ async function handleServerActionRscRequest(options) {
237
228
  const previousHeadersPhase = options.setHeadersAccessPhase("action");
238
229
  try {
239
230
  try {
231
+ validateServerActionArgs(args);
240
232
  returnValue = {
241
233
  ok: true,
242
234
  data: await action.apply(null, args)
@@ -268,7 +260,7 @@ async function handleServerActionRscRequest(options) {
268
260
  options.clearRequestContext();
269
261
  const redirectHeaders = new Headers({
270
262
  "Content-Type": "text/x-component; charset=utf-8",
271
- Vary: "RSC, Accept"
263
+ Vary: VINEXT_RSC_VARY_HEADER
272
264
  });
273
265
  mergeMiddlewareResponseHeaders(redirectHeaders, options.middlewareHeaders);
274
266
  redirectHeaders.set("x-action-redirect", actionRedirect.url);
@@ -329,7 +321,7 @@ async function handleServerActionRscRequest(options) {
329
321
  const actionDraftCookie = options.getDraftModeCookieHeader();
330
322
  const actionHeaders = new Headers({
331
323
  "Content-Type": "text/x-component; charset=utf-8",
332
- Vary: "RSC, Accept"
324
+ Vary: VINEXT_RSC_VARY_HEADER
333
325
  });
334
326
  mergeMiddlewareResponseHeaders(actionHeaders, options.middlewareHeaders);
335
327
  const actionResponse = new Response(rscStream, {