vinext 1.0.1 → 1.1.0

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 (159) hide show
  1. package/README.md +72 -1
  2. package/dist/build/app-page-static-eligibility.d.ts +15 -0
  3. package/dist/build/app-page-static-eligibility.js +82 -0
  4. package/dist/build/prerender-paths.js +3 -78
  5. package/dist/build/prerender.js +33 -8
  6. package/dist/build/server-manifest.d.ts +7 -0
  7. package/dist/build/server-manifest.js +11 -1
  8. package/dist/cache/cache-adapters-virtual.d.ts +7 -1
  9. package/dist/cache/cache-adapters-virtual.js +3 -3
  10. package/dist/client/navigation-runtime.d.ts +11 -0
  11. package/dist/client/navigation-runtime.js +1 -1
  12. package/dist/config/config-matchers.js +3 -1
  13. package/dist/config/next-config.d.ts +7 -0
  14. package/dist/config/next-config.js +18 -3
  15. package/dist/entries/app-rsc-entry.d.ts +2 -0
  16. package/dist/entries/app-rsc-entry.js +64 -43
  17. package/dist/index.d.ts +4 -1
  18. package/dist/index.js +59 -30
  19. package/dist/plugins/cache-flight-codec.d.ts +19 -0
  20. package/dist/plugins/cache-flight-codec.js +68 -0
  21. package/dist/plugins/commonjs-esm-facade.d.ts +35 -0
  22. package/dist/plugins/commonjs-esm-facade.js +80 -0
  23. package/dist/plugins/fonts.d.ts +8 -11
  24. package/dist/plugins/fonts.js +71 -34
  25. package/dist/routing/file-matcher.d.ts +1 -0
  26. package/dist/routing/file-matcher.js +1 -1
  27. package/dist/server/api-handler.js +17 -7
  28. package/dist/server/app-browser-entry.js +11 -7
  29. package/dist/server/app-browser-history-controller.d.ts +2 -0
  30. package/dist/server/app-browser-history-controller.js +8 -0
  31. package/dist/server/app-browser-interception-context.js +26 -2
  32. package/dist/server/app-browser-navigation-controller.d.ts +7 -1
  33. package/dist/server/app-browser-navigation-controller.js +93 -10
  34. package/dist/server/app-fallback-renderer.d.ts +2 -0
  35. package/dist/server/app-fallback-renderer.js +1 -0
  36. package/dist/server/app-history-state.d.ts +3 -0
  37. package/dist/server/app-history-state.js +7 -0
  38. package/dist/server/app-middleware.js +2 -1
  39. package/dist/server/app-optimistic-routing.js +1 -1
  40. package/dist/server/app-page-boundary-render.d.ts +5 -0
  41. package/dist/server/app-page-boundary-render.js +6 -4
  42. package/dist/server/app-page-cache-finalizer.d.ts +11 -0
  43. package/dist/server/app-page-cache-finalizer.js +41 -7
  44. package/dist/server/app-page-cache-render.d.ts +18 -0
  45. package/dist/server/app-page-cache-render.js +63 -43
  46. package/dist/server/app-page-cache.d.ts +5 -0
  47. package/dist/server/app-page-cache.js +27 -7
  48. package/dist/server/app-page-dispatch.d.ts +9 -1
  49. package/dist/server/app-page-dispatch.js +47 -30
  50. package/dist/server/app-page-element-builder.d.ts +7 -0
  51. package/dist/server/app-page-element-builder.js +27 -42
  52. package/dist/server/app-page-execution.d.ts +15 -12
  53. package/dist/server/app-page-execution.js +30 -40
  54. package/dist/server/app-page-head.d.ts +5 -0
  55. package/dist/server/app-page-head.js +9 -3
  56. package/dist/server/app-page-probe.d.ts +0 -109
  57. package/dist/server/app-page-probe.js +3 -136
  58. package/dist/server/app-page-render-identity.d.ts +0 -1
  59. package/dist/server/app-page-render-identity.js +1 -1
  60. package/dist/server/app-page-render-observation.js +2 -2
  61. package/dist/server/app-page-render.d.ts +13 -5
  62. package/dist/server/app-page-render.js +219 -119
  63. package/dist/server/app-page-request.d.ts +0 -1
  64. package/dist/server/app-page-request.js +1 -2
  65. package/dist/server/app-page-response.d.ts +4 -3
  66. package/dist/server/app-page-response.js +4 -3
  67. package/dist/server/app-page-route-wiring.d.ts +17 -5
  68. package/dist/server/app-page-route-wiring.js +55 -33
  69. package/dist/server/app-page-rsc-render-status.d.ts +48 -0
  70. package/dist/server/app-page-rsc-render-status.js +54 -0
  71. package/dist/server/app-page-stream.d.ts +15 -8
  72. package/dist/server/app-page-stream.js +6 -0
  73. package/dist/server/app-request-stage-dispatch.js +1 -1
  74. package/dist/server/app-request-stage-independent-entry.js +4 -4
  75. package/dist/server/app-response-header-provenance.d.ts +2 -0
  76. package/dist/server/app-response-header-provenance.js +10 -4
  77. package/dist/server/app-route-handler-dispatch.js +1 -1
  78. package/dist/server/app-router-entry.js +3 -3
  79. package/dist/server/app-rsc-handler.d.ts +4 -0
  80. package/dist/server/app-rsc-handler.js +212 -96
  81. package/dist/server/app-rsc-request-normalization.d.ts +3 -1
  82. package/dist/server/app-rsc-request-normalization.js +4 -2
  83. package/dist/server/app-rsc-response-stage.d.ts +1 -1
  84. package/dist/server/app-rsc-response-stage.js +6 -4
  85. package/dist/server/app-rsc-route-matching.d.ts +9 -1
  86. package/dist/server/app-rsc-route-matching.js +36 -19
  87. package/dist/server/app-server-action-execution.js +1 -1
  88. package/dist/server/app-ssr-entry.d.ts +3 -2
  89. package/dist/server/app-ssr-entry.js +6 -4
  90. package/dist/server/app-ssr-stream.d.ts +6 -1
  91. package/dist/server/app-ssr-stream.js +34 -15
  92. package/dist/server/app-worker-stages.d.ts +2 -2
  93. package/dist/server/app-worker-stages.js +3 -3
  94. package/dist/server/cacheability-request.js +9 -5
  95. package/dist/server/config-headers.js +2 -0
  96. package/dist/server/dev-server.js +12 -2
  97. package/dist/server/headers.d.ts +14 -0
  98. package/dist/server/headers.js +15 -1
  99. package/dist/server/image-optimization.d.ts +16 -0
  100. package/dist/server/image-optimization.js +26 -6
  101. package/dist/server/isr-cache.js +2 -2
  102. package/dist/server/middleware-matcher-pattern.js +8 -4
  103. package/dist/server/middleware-response-headers.js +2 -0
  104. package/dist/server/multi-stage.d.ts +8 -0
  105. package/dist/server/navigation-planner.js +1 -1
  106. package/dist/server/node-response-signal.d.ts +16 -0
  107. package/dist/server/node-response-signal.js +26 -0
  108. package/dist/server/open-redirect.d.ts +47 -0
  109. package/dist/server/open-redirect.js +74 -1
  110. package/dist/server/pages-page-handler.js +21 -4
  111. package/dist/server/pages-request-stage-entry.js +8 -4
  112. package/dist/server/prod-server.d.ts +8 -7
  113. package/dist/server/prod-server.js +153 -69
  114. package/dist/server/request-pipeline.d.ts +20 -8
  115. package/dist/server/request-pipeline.js +21 -7
  116. package/dist/server/response-compression.d.ts +43 -0
  117. package/dist/server/response-compression.js +111 -0
  118. package/dist/server/seed-cache.js +10 -7
  119. package/dist/server/socket-error-backstop.d.ts +14 -0
  120. package/dist/server/socket-error-backstop.js +27 -4
  121. package/dist/shims/cache-adapter-instantiate.d.ts +50 -0
  122. package/dist/shims/cache-adapter-instantiate.js +118 -0
  123. package/dist/shims/cache-callable-runtime.d.ts +2 -40
  124. package/dist/shims/cache-callable-runtime.js +50 -168
  125. package/dist/shims/cache-flight-arguments.d.ts +22 -0
  126. package/dist/shims/cache-flight-arguments.js +56 -0
  127. package/dist/shims/cache-request-state.d.ts +15 -0
  128. package/dist/shims/cache-request-state.js +31 -8
  129. package/dist/shims/cache-runtime.d.ts +6 -19
  130. package/dist/shims/cache-runtime.js +165 -225
  131. package/dist/shims/cache.d.ts +2 -2
  132. package/dist/shims/cache.js +5 -4
  133. package/dist/shims/cacheability-classification.d.ts +7 -0
  134. package/dist/shims/error-boundary-navigation.d.ts +7 -0
  135. package/dist/shims/error-boundary-navigation.js +19 -1
  136. package/dist/shims/error-boundary.d.ts +10 -10
  137. package/dist/shims/error-boundary.js +26 -7
  138. package/dist/shims/fetch-cache.d.ts +6 -0
  139. package/dist/shims/fetch-cache.js +19 -1
  140. package/dist/shims/form.js +1 -1
  141. package/dist/shims/headers.d.ts +13 -0
  142. package/dist/shims/headers.js +47 -4
  143. package/dist/shims/internal/app-page-props-cache-key.d.ts +5 -2
  144. package/dist/shims/internal/app-page-props-cache-key.js +24 -15
  145. package/dist/shims/internal/headers-state.d.ts +0 -1
  146. package/dist/shims/metadata.js +3 -3
  147. package/dist/shims/navigation-errors.d.ts +6 -0
  148. package/dist/shims/navigation-errors.js +11 -1
  149. package/dist/shims/navigation.d.ts +7 -0
  150. package/dist/shims/navigation.js +14 -4
  151. package/dist/shims/request-context.d.ts +9 -0
  152. package/dist/shims/thenable-params.d.ts +4 -0
  153. package/dist/shims/thenable-params.js +25 -3
  154. package/dist/shims/unified-request-context.js +1 -0
  155. package/dist/utils/mdx-scan.d.ts +5 -0
  156. package/dist/utils/mdx-scan.js +8 -1
  157. package/dist/utils/regex-safety.d.ts +10 -0
  158. package/dist/utils/regex-safety.js +70 -18
  159. package/package.json +3 -3
@@ -1,4 +1,4 @@
1
- import { decodeMatchedParams, splitPathSegments, splitPathnameForRouteMatch } from "../routing/utils.js";
1
+ import { decodeMatchedParams, decodeRouteSegment, splitPathSegments, splitPathnameForRouteMatch } from "../routing/utils.js";
2
2
  import { createAppRouteGraphInterceptionId } from "../routing/app-route-ids.js";
3
3
  import { buildRouteTrie, trieMatchRaw } from "../routing/route-trie.js";
4
4
  import { extractRawRoutePatternParams, matchRoutePatternPrefix, matchRoutePatternRaw } from "../routing/route-pattern.js";
@@ -18,17 +18,6 @@ function appRscPathnameParts(pathname, isNormalized = false) {
18
18
  const normalizedPathname = pathOnly === "/" ? "/" : pathOnly.replace(/\/$/, "");
19
19
  return isNormalized ? splitPathSegments(normalizedPathname) : splitPathnameForRouteMatch(normalizedPathname);
20
20
  }
21
- function appRscInterceptionSourcePathnameParts(pathname) {
22
- const pathOnly = pathname.split("?")[0];
23
- const normalizedPathname = pathOnly === "/" ? "/" : pathOnly.replace(/\/$/, "");
24
- return splitPathSegments(normalizedPathname).map((segment) => {
25
- try {
26
- return decodeURIComponent(segment);
27
- } catch {
28
- return segment;
29
- }
30
- });
31
- }
32
21
  function isAppRouteHandlerRoute(route) {
33
22
  return route.routeHandler != null || typeof route.__loadRouteHandler === "function";
34
23
  }
@@ -36,11 +25,38 @@ function normalizeMatchedParamsForRoute(result) {
36
25
  if (isAppRouteHandlerRoute(result.route)) decodeMatchedParams(result.params);
37
26
  else canonicalizeAppPageParams(result.params);
38
27
  }
39
- function createAppRscRouteMatcher(routes) {
28
+ /**
29
+ * @param decodedPathnamePatterns Static page patterns that answer their
30
+ * segment-decoded pathname (built by `buildAppDecodedPathnamePatterns`).
31
+ */
32
+ function createAppRscRouteMatcher(routes, decodedPathnamePatterns = []) {
40
33
  const routeTrie = buildRouteTrie(routes);
41
34
  const interceptLookup = createInterceptLookup(routes);
42
35
  const interceptionIds = new Set(interceptLookup.flatMap((entry) => entry.interceptionId === null ? [] : [entry.interceptionId]));
43
36
  const routeIndexes = new Map(routes.map((route, index) => [route, index]));
37
+ const decodedPathnamePatternSet = new Set(decodedPathnamePatterns);
38
+ const decodedPathnameRoutes = /* @__PURE__ */ new Map();
39
+ for (const route of routes) {
40
+ const key = route.patternParts.join("/");
41
+ if (decodedPathnamePatternSet.has(route.pattern) && !decodedPathnameRoutes.has(key)) decodedPathnameRoutes.set(key, route);
42
+ }
43
+ function matchRawRequestParts(rawParts) {
44
+ const result = trieMatchRaw(routeTrie, rawParts);
45
+ if (result) normalizeMatchedParamsForRoute(result);
46
+ return result;
47
+ }
48
+ function matchRequestParts(rawParts) {
49
+ const result = matchRawRequestParts(rawParts);
50
+ if (!result) return null;
51
+ if (decodedPathnameRoutes.size > 0 && result.route.patternParts.some((part) => part.startsWith(":")) && !isAppRouteHandlerRoute(result.route) && rawParts.some((part) => part.includes("%"))) {
52
+ const route = decodedPathnameRoutes.get(rawParts.map(decodeRouteSegment).join("/"));
53
+ if (route && route !== result.route) return {
54
+ route,
55
+ params: createRouteParams()
56
+ };
57
+ }
58
+ return result;
59
+ }
44
60
  return {
45
61
  hasInterceptionId(interceptionId) {
46
62
  return interceptionIds.has(interceptionId);
@@ -54,16 +70,16 @@ function createAppRscRouteMatcher(routes) {
54
70
  return result;
55
71
  },
56
72
  matchRequestRoute(url) {
57
- const result = trieMatchRaw(routeTrie, appRscPathnameParts(url, true));
58
- if (!result) return null;
59
- normalizeMatchedParamsForRoute(result);
60
- return result;
73
+ return matchRequestParts(appRscPathnameParts(url, true));
74
+ },
75
+ matchRawRequestRoute(url) {
76
+ return matchRawRequestParts(appRscPathnameParts(url, true));
61
77
  },
62
78
  findIntercept(pathname, sourcePathname = null, interceptionId = null) {
63
79
  if (sourcePathname === null) return null;
64
80
  const urlParts = appRscPathnameParts(pathname, true);
65
- const sourceParts = appRscInterceptionSourcePathnameParts(sourcePathname);
66
- const matchedSourceRoute = trieMatchRaw(routeTrie, sourceParts);
81
+ const sourceParts = appRscPathnameParts(sourcePathname, true);
82
+ const matchedSourceRoute = matchRawRequestParts(sourceParts);
67
83
  for (const entry of interceptLookup) {
68
84
  if (interceptionId !== null && entry.interceptionId !== interceptionId) continue;
69
85
  if (!matchInterceptSource(sourceParts, entry)) continue;
@@ -75,6 +91,7 @@ function createAppRscRouteMatcher(routes) {
75
91
  const sourceRoute = routes[concreteSourceRouteIndex];
76
92
  if (sourceRoute && isAppRouteHandlerRoute(sourceRoute)) continue;
77
93
  const matchedSourceParams = concreteSourceRoute ? concreteSourceRoute.params : sourceRoute ? matchSlotOwnerSourceParams(sourceParts, sourceRoute.patternParts, entry.sourceMatchPatternParts !== null) : null;
94
+ if (!concreteSourceRoute && matchedSourceParams) canonicalizeAppPageParams(matchedSourceParams);
78
95
  if (matchedSourceParams === null && entry.sourceMatchPatternParts === null) continue;
79
96
  const sourceParams = matchedSourceParams && entry.sourceMatchPatternParts !== null ? pickPatternParams(matchedSourceParams, entry.sourceMatchPatternParts) : matchedSourceParams ?? createRouteParams();
80
97
  return {
@@ -19,10 +19,10 @@ import { mergeMiddlewareResponseHeaders } from "./middleware-response-headers.js
19
19
  import { applyEdgeRuntimeHeader } from "./app-page-response.js";
20
20
  import { getNextErrorDigest, parseNextHttpErrorDigest, parseNextRedirectDigest } from "./next-error-digest.js";
21
21
  import { buildAppPageTags } from "./implicit-tags.js";
22
+ import "./app-page-stream.js";
22
23
  import { getSetCookieName } from "./cookie-utils.js";
23
24
  import { createServerActionNotFoundResponse, getServerActionNotFoundMessage, isServerActionNotFoundError } from "./server-action-not-found.js";
24
25
  import { resolveAppPageNavigationParams } from "./app-page-element-builder.js";
25
- import "./app-page-stream.js";
26
26
  import { resolveAppPageActionRerenderTarget } from "./app-page-request.js";
27
27
  import { createRootParamsUsageController, runWithRootParamsUsage } from "../shims/root-params.js";
28
28
  import { createStaticGenerationHeadersContext } from "./app-static-generation.js";
@@ -46,8 +46,9 @@ export declare function handleSsr(rscStream: ReadableStream<Uint8Array>, navCont
46
46
  initialDevServerError?: unknown;
47
47
  /** Report an SSR/Fizz render failure through instrumentation. */
48
48
  onSsrError?: (error: unknown) => unknown;
49
- /** Mirror inline Flight chunks into Next.js's `self.__next_f` transport. */
50
- mirrorNextFlight?: boolean;
49
+ /** Mirror inline Flight chunks into Next.js's `self.__next_f` transport, or start
50
+ * mirroring once a function returns true. */
51
+ mirrorNextFlight?: boolean | (() => boolean);
51
52
  /** When true, wait for the full React tree (including Suspense boundaries)
52
53
  * to resolve before returning the HTML stream. Used for static prerender
53
54
  * and ISR cache writes to avoid caching fallback content. */
@@ -1,8 +1,6 @@
1
1
  import "./server-globals.js";
2
2
  import { notFoundResponse } from "./http-error-responses.js";
3
- import { isOpenRedirectShaped } from "./open-redirect.js";
4
- import { AppElementsWire } from "./app-elements-wire.js";
5
- import "./app-elements.js";
3
+ import { isOpenRedirectShaped, repeatedSlashRedirectResponse } from "./open-redirect.js";
6
4
  import { isPprFallbackShellAbortError } from "../shims/ppr-fallback-shell.js";
7
5
  import { onRenderDynamicLatched } from "../shims/internal/headers-state.js";
8
6
  import { AppRouterContext } from "../shims/internal/app-router-context.js";
@@ -12,18 +10,20 @@ import { createInlineScriptTag, createNonceAttribute, escapeHtmlAttr, safeJsonSt
12
10
  import { getClientTraceMetadataHTML, markClientTraceMetadataBlock } from "./client-trace-metadata.js";
13
11
  import { deferUntilStreamConsumed } from "./defer-until-stream-consumed.js";
14
12
  import { setPagesClientAssets } from "./pages-client-assets.js";
13
+ import { AppElementsWire } from "./app-elements-wire.js";
14
+ import "./app-elements.js";
15
15
  import { ServerInsertedHTMLContext, clearServerInsertedHTML, getBfcacheIdMapContext, registerServerInsertedHTMLCallback, renderServerInsertedHTML, setNavigationContext } from "../shims/navigation-context-state.js";
16
16
  import "../shims/navigation-server.js";
17
17
  import DefaultGlobalError from "../shims/default-global-error.js";
18
18
  import { BfcacheIdentityMapContext, ElementsContext, Slot } from "../shims/slot.js";
19
19
  import { getNextErrorDigest } from "./next-error-digest.js";
20
20
  import { createSsrErrorMetaRenderer } from "./app-ssr-error-meta.js";
21
- import { createClientPageSsrSearchParamsSource } from "./app-page-search-params-observation.js";
22
21
  import { isAppRenderAbortError } from "./app-render-abort-error.js";
23
22
  import { RSC_FORM_STATE_GLOBAL } from "../client/browser-globals.js";
24
23
  import { createNavigationRuntimeRenderedSearchScript, createNavigationRuntimeRscMetadataScript, createRscEmbedTransform, createTickBufferedTransform, waitAtLeastOneReactRenderTask } from "./app-ssr-stream.js";
25
24
  import { createInitialBfcacheMaps } from "./app-bfcache-identity.js";
26
25
  import { createClientReferencePreloader } from "./app-client-reference-preloader.js";
26
+ import { createClientPageSsrSearchParamsSource } from "./app-page-search-params-observation.js";
27
27
  import { runWithRootParamsScope } from "../shims/root-params.js";
28
28
  import { runWithNavigationContext } from "../shims/navigation-state.js";
29
29
  import { startCandidateSearchParamsGate } from "./app-ssr-search-params-gate.js";
@@ -357,6 +357,8 @@ async function handleSsr(rscStream, navContext, fontData, options) {
357
357
  }
358
358
  var app_ssr_entry_default = { async fetch(request) {
359
359
  const url = new URL(request.url);
360
+ const slashRedirect = repeatedSlashRedirectResponse(url.pathname + url.search);
361
+ if (slashRedirect) return slashRedirect;
360
362
  if (isOpenRedirectShaped(url.pathname)) return notFoundResponse();
361
363
  const result = await (await import.meta.viteRsc.loadModule("rsc", "index")).default(request);
362
364
  if (result instanceof Response) return result;
@@ -6,7 +6,12 @@ type RscEmbedTransform = {
6
6
  getRawBuffer(): Promise<ArrayBuffer>;
7
7
  };
8
8
  type RscEmbedTransformOptions = {
9
- mirrorNextFlight?: boolean;
9
+ /**
10
+ * Mirror inline Flight chunks into Next.js's `self.__next_f` transport. A
11
+ * function is checked at each flush; once it returns true, the chunks
12
+ * already embedded are mirrored first.
13
+ */
14
+ mirrorNextFlight?: boolean | (() => boolean);
10
15
  scriptNonce?: string;
11
16
  getInitialNavigationCacheMetadata?: () => InitialNavigationCacheMetadata;
12
17
  };
@@ -49,12 +49,36 @@ function createRscEmbedTransform(embedStream, optionsOrNonce = {}) {
49
49
  const reader = embedStream.getReader();
50
50
  let pendingChunks = [];
51
51
  const rawChunks = [];
52
+ let embeddedRawChunkCount = 0;
53
+ let rawBufferTaken = false;
54
+ let finalized = false;
52
55
  let reading = false;
53
56
  let mirroredNextFlightBootstrap = false;
54
57
  const textDecoder = new TextDecoder("utf-8", {
55
58
  fatal: true,
56
59
  ignoreBOM: true
57
60
  });
61
+ function decodeChunk(bytes) {
62
+ try {
63
+ return textDecoder.decode(bytes);
64
+ } catch {
65
+ return [3, bytesToBase64(bytes)];
66
+ }
67
+ }
68
+ function releaseRawChunks() {
69
+ const mayStartMirror = typeof options.mirrorNextFlight === "function" && !mirroredNextFlightBootstrap && !finalized;
70
+ if (rawBufferTaken && !mayStartMirror) rawChunks.length = 0;
71
+ }
72
+ function startNextFlightMirror() {
73
+ if (mirroredNextFlightBootstrap) return "";
74
+ const mirror = options.mirrorNextFlight;
75
+ if (mirror !== true && (typeof mirror !== "function" || !mirror())) return "";
76
+ mirroredNextFlightBootstrap = true;
77
+ let scripts = createInlineScriptTag(createNextFlightBootstrapScript(), options.scriptNonce) + createInlineScriptTag(createNextFlightCleanupScript(), options.scriptNonce);
78
+ for (let index = 0; index < embeddedRawChunkCount; index++) scripts += createInlineScriptTag(createNextFlightChunkScript(decodeChunk(rawChunks[index])), options.scriptNonce);
79
+ releaseRawChunks();
80
+ return scripts;
81
+ }
58
82
  async function pumpReader() {
59
83
  if (reading) return;
60
84
  reading = true;
@@ -63,11 +87,7 @@ function createRscEmbedTransform(embedStream, optionsOrNonce = {}) {
63
87
  const result = await reader.read();
64
88
  if (result.done) break;
65
89
  rawChunks.push(result.value);
66
- try {
67
- pendingChunks.push(textDecoder.decode(result.value));
68
- } catch {
69
- pendingChunks.push([3, bytesToBase64(result.value)]);
70
- }
90
+ pendingChunks.push(decodeChunk(result.value));
71
91
  }
72
92
  } catch (error) {
73
93
  if (process.env.NODE_ENV !== "production") console.warn("[vinext] RSC embed stream read error:", error);
@@ -80,8 +100,10 @@ function createRscEmbedTransform(embedStream, optionsOrNonce = {}) {
80
100
  return {
81
101
  flush() {
82
102
  if (pendingChunks.length === 0) return "";
103
+ const mirrorScripts = startNextFlightMirror();
83
104
  const chunks = pendingChunks;
84
105
  pendingChunks = [];
106
+ embeddedRawChunkCount += chunks.length;
85
107
  const embeddedChunks = [];
86
108
  let textChunks = [];
87
109
  let textLength = 0;
@@ -100,30 +122,27 @@ function createRscEmbedTransform(embedStream, optionsOrNonce = {}) {
100
122
  embeddedChunks.push(chunk);
101
123
  }
102
124
  flushTextChunks();
103
- let scripts = "";
125
+ let scripts = mirrorScripts;
104
126
  for (const chunk of embeddedChunks) {
105
127
  scripts += createInlineScriptTag(createNavigationRuntimeRscChunkScript(chunk), options.scriptNonce);
106
- if (options.mirrorNextFlight) {
107
- if (!mirroredNextFlightBootstrap) {
108
- scripts += createInlineScriptTag(createNextFlightBootstrapScript(), options.scriptNonce);
109
- scripts += createInlineScriptTag(createNextFlightCleanupScript(), options.scriptNonce);
110
- mirroredNextFlightBootstrap = true;
111
- }
112
- scripts += createInlineScriptTag(createNextFlightChunkScript(chunk), options.scriptNonce);
113
- }
128
+ if (mirroredNextFlightBootstrap) scripts += createInlineScriptTag(createNextFlightChunkScript(chunk), options.scriptNonce);
114
129
  }
115
130
  return scripts;
116
131
  },
117
132
  async finalize() {
118
133
  await pumpPromise;
119
134
  let scripts = this.flush();
135
+ scripts += startNextFlightMirror();
136
+ finalized = true;
137
+ releaseRawChunks();
120
138
  scripts += createInlineScriptTag(createNavigationRuntimeRscDoneScript(options.getInitialNavigationCacheMetadata?.()), options.scriptNonce);
121
139
  return scripts;
122
140
  },
123
141
  async getRawBuffer() {
124
142
  await pumpPromise;
125
143
  const buffer = concatUint8Arrays(rawChunks);
126
- rawChunks.length = 0;
144
+ rawBufferTaken = true;
145
+ releaseRawChunks();
127
146
  return buffer.buffer;
128
147
  }
129
148
  };
@@ -3,7 +3,7 @@ import { VinextResponseStageCacheability, VinextResponseStageDispatchOptions, Vi
3
3
  import { AppRscRenderMode } from "./app-rsc-render-mode.js";
4
4
  import { TrustedPrerenderState } from "./prerender-route-params.js";
5
5
  //#region src/server/app-worker-stages.d.ts
6
- export declare const APP_WORKER_RESPONSE_STAGE_PROTOCOL_VERSION = 10;
6
+ export declare const APP_WORKER_RESPONSE_STAGE_PROTOCOL_VERSION = 11;
7
7
  export declare const APP_METADATA_RESPONSE_STAGE_NO_MATCH_HEADER = "x-vinext-app-metadata-stage-no-match";
8
8
  type AppPageParams = Record<string, string | string[]>;
9
9
  type AppWorkerResponseStageEnvelope = {
@@ -34,7 +34,7 @@ export type AppMatchedWorkerResponseStageProps = AppWorkerResponseStageEnvelope
34
34
  interceptionContext: string | null;
35
35
  interceptionId: string | null;
36
36
  isRscRequest: boolean;
37
- matchKind: "interception" | "request" | "resolved";
37
+ matchKind: "interception" | "raw-request" | "request" | "resolved";
38
38
  mountedSlotsHeader: string | null;
39
39
  params: AppPageParams;
40
40
  resolvedUrl: string;
@@ -3,7 +3,7 @@ import { cacheabilityManifestPageState, cacheabilityRequestIdentity, cacheabilit
3
3
  import { keepOnlyValidatedRscCacheBustingSearchParam } from "./app-rsc-cache-busting.js";
4
4
  import { isTrustedPrerenderState } from "./prerender-route-params.js";
5
5
  //#region src/server/app-worker-stages.ts
6
- const APP_WORKER_RESPONSE_STAGE_PROTOCOL_VERSION = 10;
6
+ const APP_WORKER_RESPONSE_STAGE_PROTOCOL_VERSION = 11;
7
7
  const APP_METADATA_RESPONSE_STAGE_NO_MATCH_HEADER = "x-vinext-app-metadata-stage-no-match";
8
8
  const STATIC_FILE_SIGNAL_TOKEN_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
9
9
  /** Normalize a shared App page HEAD request onto its method-invariant GET representation. */
@@ -80,7 +80,7 @@ function isCanonicalHttpOrigin(value) {
80
80
  function isAppWorkerResponseStageProps(value) {
81
81
  if (!value || typeof value !== "object") return false;
82
82
  const props = value;
83
- if (props.protocolVersion !== 10 || props.buildId !== null && typeof props.buildId !== "string" || !isResponseStageCacheability(props.cacheability) || props.draftModeCookie !== null && typeof props.draftModeCookie !== "string" || props.middlewareCookieOverlay !== null && typeof props.middlewareCookieOverlay !== "string" || !isCanonicalHttpOrigin(props.requestOrigin) || props.scriptNonce !== null && typeof props.scriptNonce !== "string") return false;
83
+ if (props.protocolVersion !== 11 || props.buildId !== null && typeof props.buildId !== "string" || !isResponseStageCacheability(props.cacheability) || props.draftModeCookie !== null && typeof props.draftModeCookie !== "string" || props.middlewareCookieOverlay !== null && typeof props.middlewareCookieOverlay !== "string" || !isCanonicalHttpOrigin(props.requestOrigin) || props.scriptNonce !== null && typeof props.scriptNonce !== "string") return false;
84
84
  if (props.kind === "app-full-request") return typeof props.prerenderDiscovery === "boolean" && typeof props.staticFileSignalToken === "string" && STATIC_FILE_SIGNAL_TOKEN_RE.test(props.staticFileSignalToken) && (props.trustedPrerenderState === null || isTrustedPrerenderState(props.trustedPrerenderState));
85
85
  if (props.kind === "hybrid-pages") {
86
86
  const hybrid = props;
@@ -91,7 +91,7 @@ function isAppWorkerResponseStageProps(value) {
91
91
  const special = props;
92
92
  return typeof special.canonicalPathname === "string" && special.canonicalPathname.startsWith("/") && typeof special.cleanPathname === "string" && special.cleanPathname.startsWith("/") && typeof special.isRscRequest === "boolean" && (special.mountedSlotsHeader === null || typeof special.mountedSlotsHeader === "string") && typeof special.resolvedUrl === "string" && special.resolvedUrl.startsWith("/") && (props.kind !== "app-metadata" || typeof props.routePathname === "string" && props.routePathname.startsWith("/")) && (special.renderMode === "navigation" || special.renderMode === "prefetch-empty" || special.renderMode === "prefetch-dynamic-shell" || special.renderMode === "prefetch-loading-shell");
93
93
  }
94
- return (props.kind === "app-page" || props.kind === "app-route-handler") && typeof props.bypassInterceptionContextCache === "boolean" && typeof props.cachePathname === "string" && props.cachePathname.startsWith("/") && typeof props.canUseCanonicalLoadingShell === "boolean" && typeof props.canonicalPathname === "string" && props.canonicalPathname.startsWith("/") && typeof props.cleanPathname === "string" && props.cleanPathname.startsWith("/") && (props.forceDynamic === void 0 || typeof props.forceDynamic === "boolean") && (props.interceptionContext === null || typeof props.interceptionContext === "string") && (props.interceptionId === null || typeof props.interceptionId === "string") && typeof props.isRscRequest === "boolean" && (props.matchKind === "interception" || props.matchKind === "request" || props.matchKind === "resolved") && (props.mountedSlotsHeader === null || typeof props.mountedSlotsHeader === "string") && isAppPageParams(props.params) && typeof props.resolvedUrl === "string" && props.resolvedUrl.startsWith("/") && typeof props.routePattern === "string" && props.routePattern.startsWith("/") && typeof props.routePathname === "string" && props.routePathname.startsWith("/") && (props.renderMode === "navigation" || props.renderMode === "prefetch-empty" || props.renderMode === "prefetch-dynamic-shell" || props.renderMode === "prefetch-loading-shell");
94
+ return (props.kind === "app-page" || props.kind === "app-route-handler") && typeof props.bypassInterceptionContextCache === "boolean" && typeof props.cachePathname === "string" && props.cachePathname.startsWith("/") && typeof props.canUseCanonicalLoadingShell === "boolean" && typeof props.canonicalPathname === "string" && props.canonicalPathname.startsWith("/") && typeof props.cleanPathname === "string" && props.cleanPathname.startsWith("/") && (props.forceDynamic === void 0 || typeof props.forceDynamic === "boolean") && (props.interceptionContext === null || typeof props.interceptionContext === "string") && (props.interceptionId === null || typeof props.interceptionId === "string") && typeof props.isRscRequest === "boolean" && (props.matchKind === "interception" || props.matchKind === "raw-request" || props.matchKind === "request" || props.matchKind === "resolved") && (props.mountedSlotsHeader === null || typeof props.mountedSlotsHeader === "string") && isAppPageParams(props.params) && typeof props.resolvedUrl === "string" && props.resolvedUrl.startsWith("/") && typeof props.routePattern === "string" && props.routePattern.startsWith("/") && typeof props.routePathname === "string" && props.routePathname.startsWith("/") && (props.renderMode === "navigation" || props.renderMode === "prefetch-empty" || props.renderMode === "prefetch-dynamic-shell" || props.renderMode === "prefetch-loading-shell");
95
95
  }
96
96
  function isSerializedHeaders(value) {
97
97
  return Array.isArray(value) && value.every((entry) => Array.isArray(entry) && entry.length === 2 && typeof entry[0] === "string" && typeof entry[1] === "string");
@@ -1,6 +1,6 @@
1
1
  import "../utils/protocol-headers.js";
2
2
  import { isVinextRscVaryField } from "./app-rsc-vary.js";
3
- import { VINEXT_CACHEABILITY_PROBE_HEADER, VINEXT_CACHEABILITY_PROBE_ROUTE_HEADER } from "./headers.js";
3
+ import { VINEXT_CACHEABILITY_PROBE_HEADER, VINEXT_CACHEABILITY_PROBE_ROUTE_HEADER, VINEXT_SPECIAL_ERROR_STATUS_HEADER } from "./headers.js";
4
4
  import { isNonCacheableCacheControl } from "../shims/cdn-cache.js";
5
5
  import { CACHEABILITY_REQUEST_STATE, recordConfigCdnCachePolicyHeader } from "../shims/cacheability-classification.js";
6
6
  import { NO_STORE_CACHE_CONTROL, applyCdnResponseBuildIdentityHeaders, applyCdnResponseHeaders, hasExplicitNonCacheableResponsePolicy, isCdnResponsePolicyHeader, readCdnResponseCacheControl, readCdnResponsePolicyHeaderName } from "./cache-control.js";
@@ -348,9 +348,10 @@ async function captureCacheabilityAdmissionBody(body, deadlineAt, limit = CACHEA
348
348
  throw error;
349
349
  }
350
350
  }
351
- function responseWithCachePolicy(response, body, outcome, browserCacheControl) {
351
+ function responseWithCachePolicy(response, body, outcome, browserCacheControl, completedStatus) {
352
352
  const headers = new Headers(response.headers);
353
353
  if (typeof body === "string") headers.delete("Content-Length");
354
+ for (const [name, value] of Object.entries(completedStatus?.headers ?? {})) if (!headers.has(name)) headers.set(name, value);
354
355
  applyCdnResponseHeaders(headers, outcome?.cacheable === true && outcome.cacheControl ? {
355
356
  cacheControl: outcome.cacheControl,
356
357
  tags: outcome.tags,
@@ -359,10 +360,13 @@ function responseWithCachePolicy(response, body, outcome, browserCacheControl) {
359
360
  cacheControl: NO_STORE_CACHE_CONTROL,
360
361
  browserCacheControl
361
362
  });
363
+ const status = completedStatus?.status ?? response.status;
364
+ const completed = completedStatus?.status;
365
+ if (completed === 401 || completed === 403 || completed === 404) headers.set(VINEXT_SPECIAL_ERROR_STATUS_HEADER, String(completed));
362
366
  return new Response(body, {
363
367
  headers,
364
- status: response.status,
365
- statusText: response.statusText
368
+ status,
369
+ statusText: status === response.status ? response.statusText : ""
366
370
  });
367
371
  }
368
372
  async function stripSharedHtmlClientTraceMetadata(body, marker) {
@@ -499,7 +503,7 @@ async function finalizeWorkerCacheabilityAdmission(response, state) {
499
503
  const effectivePolicyHeader = readCdnResponsePolicyHeaderName(response.headers);
500
504
  const replacesRendererPolicy = outcome !== rendererOutcome && effectivePolicyHeader !== null && state.configCdnCachePolicy?.get(effectivePolicyHeader) === response.headers.get(effectivePolicyHeader);
501
505
  if (state.route.kind === "app-page" && !replacesRendererPolicy && rendererOutcome?.searchParamsUnread !== true) return responseWithCachePolicy(response, captured.body, null, browserCacheControl);
502
- return responseWithCachePolicy(response, representation === "html" && state.clientTraceMetadataMarker ? await stripSharedHtmlClientTraceMetadata(captured.body, state.clientTraceMetadataMarker) : captured.body, outcome, browserCacheControl);
506
+ return responseWithCachePolicy(response, representation === "html" && state.clientTraceMetadataMarker ? await stripSharedHtmlClientTraceMetadata(captured.body, state.clientTraceMetadataMarker) : captured.body, outcome, browserCacheControl, rendererOutcome ?? void 0);
503
507
  }
504
508
  async function finalizeWorkerCacheabilityResponse(response, ctx) {
505
509
  const state = readState(ctx);
@@ -1,4 +1,5 @@
1
1
  import { matchHeaders } from "../config/config-matchers.js";
2
+ import "./headers.js";
2
3
  import { markRouteCacheabilityConfigCdnCachePolicy, markRouteCacheabilityDynamic, markRouteCacheabilityFinalResponseUncacheable } from "../shims/cacheability-classification.js";
3
4
  import { isCdnResponsePolicyHeader, isNonCacheableCdnResponsePolicy } from "./cache-control.js";
4
5
  import { mergeVaryHeader } from "./middleware-response-headers.js";
@@ -77,6 +78,7 @@ function applyConfigHeadersToResponse(responseHeaders, options) {
77
78
  if (options.recordCacheability !== false) markExplicitConfigResponseVeto(matched);
78
79
  for (const header of matched) {
79
80
  const lowerName = header.key.toLowerCase();
81
+ if (lowerName === "x-vinext-special-error-status") continue;
80
82
  if (lowerName === "link") {
81
83
  if (options.middlewareHeaders?.get(lowerName)) continue;
82
84
  const postConfigLink = options.appendToPostConfigLink ? responseHeaders.get(lowerName) : null;
@@ -21,6 +21,7 @@ import { createInlineScriptTag, createNonceAttribute, safeJsonStringify } from "
21
21
  import { applyDocumentAssetProps, extractDocumentAssetProps, markDocumentAssetPropsProtectedTags, stripDocumentAssetPropsProtectionMarkers } from "./pages-document-asset-props.js";
22
22
  import { getClientTraceMetadataHTML } from "./client-trace-metadata.js";
23
23
  import { getScriptNonceFromNodeHeaderSources } from "./csp.js";
24
+ import { stripViteModuleQuery } from "../utils/path.js";
24
25
  import { logRequest, now } from "./request-log.js";
25
26
  import { buildDefaultPagesNotFoundResponse } from "./pages-default-404.js";
26
27
  import { buildPagesReadinessNextData } from "./pages-readiness.js";
@@ -148,6 +149,15 @@ function stripDevPagesNotFoundFramingHeaders(res) {
148
149
  res.removeHeader("Transfer-Encoding");
149
150
  }
150
151
  /**
152
+ * Vite's `transformIndexHtml` treats its URL as the HTML document's path, so
153
+ * the request query carries no meaning there. Since Vite 8.3.3 it also maps
154
+ * `/?q` onto the project root directory and then watches the filesystem root
155
+ * for each inline `<style>` proxy module, so pass the bare path.
156
+ */
157
+ function toHtmlTransformPath(url) {
158
+ return stripViteModuleQuery(url);
159
+ }
160
+ /**
151
161
  * Stream a Pages Router page response using progressive SSR.
152
162
  *
153
163
  * Sends the HTML shell (head, layout, Suspense fallbacks) immediately
@@ -244,7 +254,7 @@ async function streamPageToResponseImpl(routePattern, res, element, options) {
244
254
  </body>
245
255
  </html>`;
246
256
  }
247
- let transformedShell = await server.transformIndexHtml(url, shellTemplate);
257
+ let transformedShell = await server.transformIndexHtml(toHtmlTransformPath(url), shellTemplate);
248
258
  transformedShell = stripDocumentAssetPropsProtectionMarkers(applyDocumentAssetProps(transformedShell, documentAssetProps, {
249
259
  configuredCrossOrigin: crossOrigin,
250
260
  protectedAssetMarker,
@@ -1130,7 +1140,7 @@ async function renderErrorPage(server, runner, req, res, url, pagesDir, statusCo
1130
1140
  ${protectedErrorScripts}
1131
1141
  </body>
1132
1142
  </html>`;
1133
- const transformedHtml = stripDocumentAssetPropsProtectionMarkers(applyDocumentAssetProps(await server.transformIndexHtml(url, html), {}, {
1143
+ const transformedHtml = stripDocumentAssetPropsProtectionMarkers(applyDocumentAssetProps(await server.transformIndexHtml(toHtmlTransformPath(url), html), {}, {
1134
1144
  configuredCrossOrigin: context.crossOrigin,
1135
1145
  protectedAssetMarker
1136
1146
  }), protectedAssetMarker);
@@ -58,6 +58,12 @@ export declare const VINEXT_PARAMS_HEADER = "X-Vinext-Params";
58
58
  export declare const VINEXT_DYNAMIC_STALE_TIME_HEADER = "X-Vinext-Dynamic-Stale-Time";
59
59
  /** Marks an RSC body carrying completion metadata after the Flight payload. */
60
60
  export declare const VINEXT_RSC_COMPLETION_METADATA_HEADER = "X-Vinext-Rsc-Completion-Metadata";
61
+ /**
62
+ * Carries the stored status of an App page RSC response whose 401, 403 or 404
63
+ * is its page's notFound(), forbidden() or unauthorized(). Middleware and
64
+ * config headers can't set it, and the request stage strips it.
65
+ */
66
+ export declare const VINEXT_SPECIAL_ERROR_STATUS_HEADER = "x-vinext-special-error-status";
61
67
  /** URL-encoded rendered path and search after middleware/config rewrites. */
62
68
  export declare const VINEXT_RENDERED_PATH_AND_SEARCH_HEADER = "X-Vinext-Rendered-Path-And-Search";
63
69
  /** Prerender-only JSON side channel carrying request cacheLife metadata. */
@@ -66,6 +72,14 @@ export declare const VINEXT_PRERENDER_CACHE_LIFE_HEADER = "x-vinext-prerender-ca
66
72
  export declare const VINEXT_PRERENDER_REWRITTEN_HEADER = "x-vinext-prerender-rewritten";
67
73
  /** Marks a local prerender-server 500 that originated from a thrown render error. */
68
74
  export declare const VINEXT_PRERENDER_RENDER_ERROR_HEADER = "x-vinext-prerender-render-error";
75
+ /**
76
+ * Prerender-only marker: the response's 401, 403, 404 or redirect is its page's
77
+ * notFound(), forbidden(), unauthorized() or redirect(), which escaped the shell.
78
+ * Its value is the JSON of the headers to store: a redirect's own location, or
79
+ * `{}`. Only the prerender server sets it, from the special error the page
80
+ * recorded on its ctx, and it drops one that the response carries.
81
+ */
82
+ export declare const VINEXT_PRERENDER_SPECIAL_ERROR_HEADER = "x-vinext-prerender-special-error";
69
83
  /** Internal marker persisted only inside metadata-route APP_ROUTE cache values. */
70
84
  export declare const VINEXT_METADATA_ROUTE_CACHE_HEADER = "x-vinext-metadata-route-cache";
71
85
  /** Disabled-by-default client hint describing already-held App Router payload entries. */
@@ -58,6 +58,12 @@ const VINEXT_PARAMS_HEADER = "X-Vinext-Params";
58
58
  const VINEXT_DYNAMIC_STALE_TIME_HEADER = "X-Vinext-Dynamic-Stale-Time";
59
59
  /** Marks an RSC body carrying completion metadata after the Flight payload. */
60
60
  const VINEXT_RSC_COMPLETION_METADATA_HEADER = "X-Vinext-Rsc-Completion-Metadata";
61
+ /**
62
+ * Carries the stored status of an App page RSC response whose 401, 403 or 404
63
+ * is its page's notFound(), forbidden() or unauthorized(). Middleware and
64
+ * config headers can't set it, and the request stage strips it.
65
+ */
66
+ const VINEXT_SPECIAL_ERROR_STATUS_HEADER = "x-vinext-special-error-status";
61
67
  /** URL-encoded rendered path and search after middleware/config rewrites. */
62
68
  const VINEXT_RENDERED_PATH_AND_SEARCH_HEADER = "X-Vinext-Rendered-Path-And-Search";
63
69
  /** Prerender-only JSON side channel carrying request cacheLife metadata. */
@@ -66,6 +72,14 @@ const VINEXT_PRERENDER_CACHE_LIFE_HEADER = "x-vinext-prerender-cache-life";
66
72
  const VINEXT_PRERENDER_REWRITTEN_HEADER = "x-vinext-prerender-rewritten";
67
73
  /** Marks a local prerender-server 500 that originated from a thrown render error. */
68
74
  const VINEXT_PRERENDER_RENDER_ERROR_HEADER = "x-vinext-prerender-render-error";
75
+ /**
76
+ * Prerender-only marker: the response's 401, 403, 404 or redirect is its page's
77
+ * notFound(), forbidden(), unauthorized() or redirect(), which escaped the shell.
78
+ * Its value is the JSON of the headers to store: a redirect's own location, or
79
+ * `{}`. Only the prerender server sets it, from the special error the page
80
+ * recorded on its ctx, and it drops one that the response carries.
81
+ */
82
+ const VINEXT_PRERENDER_SPECIAL_ERROR_HEADER = "x-vinext-prerender-special-error";
69
83
  /** Internal marker persisted only inside metadata-route APP_ROUTE cache values. */
70
84
  const VINEXT_METADATA_ROUTE_CACHE_HEADER = "x-vinext-metadata-route-cache";
71
85
  /** Disabled-by-default client hint describing already-held App Router payload entries. */
@@ -175,4 +189,4 @@ const VINEXT_INTERNAL_HEADERS = [
175
189
  VINEXT_TRACE_ROUTE_HEADER.toLowerCase()
176
190
  ];
177
191
  //#endregion
178
- export { ACTION_FORWARDED_HEADER, ACTION_REDIRECT_HEADER, ACTION_REDIRECT_STATUS_HEADER, ACTION_REDIRECT_TYPE_HEADER, ACTION_REVALIDATED_HEADER, FLIGHT_HEADERS, INTERNAL_HEADERS, MIDDLEWARE_HEADER_PREFIX, MIDDLEWARE_NEXT_HEADER, MIDDLEWARE_REWRITE_HEADER, MIDDLEWARE_SET_COOKIE_HEADER, MIDDLEWARE_SKIP_HEADER, NEXTJS_ACTION_NOT_FOUND_HEADER, NEXTJS_CACHE_HEADER, NEXTJS_DEPLOYMENT_ID_HEADER, NEXT_ACTION_HEADER, NEXT_CACHE_TAGS_HEADER, NEXT_HTML_REQUEST_ID_HEADER, NEXT_REQUEST_ID_HEADER, NEXT_ROUTER_PREFETCH_HEADER, NEXT_ROUTER_SEGMENT_PREFETCH_HEADER, NEXT_ROUTER_STALE_TIME_HEADER, NEXT_ROUTER_STATE_TREE_HEADER, NEXT_URL_HEADER, RSC_ACTION_HEADER, RSC_HEADER, VINEXT_CACHEABILITY_PROBE_HEADER, VINEXT_CACHEABILITY_PROBE_QUERY_PARAM, VINEXT_CACHEABILITY_PROBE_ROUTE_HEADER, VINEXT_CACHE_HEADER, VINEXT_CLIENT_REUSE_MANIFEST_HEADER, VINEXT_DYNAMIC_STALE_TIME_HEADER, VINEXT_EXPECTED_WORKER_VERSION_HEADER, VINEXT_INTERCEPTION_CONTEXT_HEADER, VINEXT_INTERCEPTION_ID_HEADER, VINEXT_INTERNAL_HEADERS, VINEXT_METADATA_ROUTE_CACHE_HEADER, VINEXT_MOUNTED_SLOTS_HEADER, VINEXT_MW_CTX_HEADER, VINEXT_PARAMS_HEADER, VINEXT_PRERENDER_CACHE_LIFE_HEADER, VINEXT_PRERENDER_METADATA_ROUTES_PATH, VINEXT_PRERENDER_PAGES_STATIC_PATHS_PATH, VINEXT_PRERENDER_READINESS_HEADER, VINEXT_PRERENDER_READINESS_PATH, VINEXT_PRERENDER_RENDER_ERROR_HEADER, VINEXT_PRERENDER_REWRITTEN_HEADER, VINEXT_PRERENDER_ROUTE_PARAMS_HEADER, VINEXT_PRERENDER_SECRET_HEADER, VINEXT_PRERENDER_SPECULATIVE_HEADER, VINEXT_PRERENDER_STATIC_PARAMS_PATH, VINEXT_RENDERED_PATH_AND_SEARCH_HEADER, VINEXT_REVALIDATED_CACHE_TAG_HEADER, VINEXT_REVALIDATE_HEADER, VINEXT_REVALIDATE_HOST_HEADER, VINEXT_RSC_COMPLETION_METADATA_HEADER, VINEXT_RSC_MARKER_HEADER, VINEXT_RSC_REDIRECT_HEADER, VINEXT_RSC_REDIRECT_TYPE_HEADER, VINEXT_RSC_RENDER_MODE_HEADER, VINEXT_RSC_STATE_FINGERPRINT_HEADER, VINEXT_RSC_VARY_HEADER, VINEXT_STALE_TIME_PENDING_HEADER, VINEXT_TIMING_HEADER, VINEXT_TRACE_BUFFERED_BODY_HEADER, VINEXT_TRACE_ERROR_HEADER, VINEXT_TRACE_ROUTE_HEADER, isVinextRscVaryField };
192
+ export { ACTION_FORWARDED_HEADER, ACTION_REDIRECT_HEADER, ACTION_REDIRECT_STATUS_HEADER, ACTION_REDIRECT_TYPE_HEADER, ACTION_REVALIDATED_HEADER, FLIGHT_HEADERS, INTERNAL_HEADERS, MIDDLEWARE_HEADER_PREFIX, MIDDLEWARE_NEXT_HEADER, MIDDLEWARE_REWRITE_HEADER, MIDDLEWARE_SET_COOKIE_HEADER, MIDDLEWARE_SKIP_HEADER, NEXTJS_ACTION_NOT_FOUND_HEADER, NEXTJS_CACHE_HEADER, NEXTJS_DEPLOYMENT_ID_HEADER, NEXT_ACTION_HEADER, NEXT_CACHE_TAGS_HEADER, NEXT_HTML_REQUEST_ID_HEADER, NEXT_REQUEST_ID_HEADER, NEXT_ROUTER_PREFETCH_HEADER, NEXT_ROUTER_SEGMENT_PREFETCH_HEADER, NEXT_ROUTER_STALE_TIME_HEADER, NEXT_ROUTER_STATE_TREE_HEADER, NEXT_URL_HEADER, RSC_ACTION_HEADER, RSC_HEADER, VINEXT_CACHEABILITY_PROBE_HEADER, VINEXT_CACHEABILITY_PROBE_QUERY_PARAM, VINEXT_CACHEABILITY_PROBE_ROUTE_HEADER, VINEXT_CACHE_HEADER, VINEXT_CLIENT_REUSE_MANIFEST_HEADER, VINEXT_DYNAMIC_STALE_TIME_HEADER, VINEXT_EXPECTED_WORKER_VERSION_HEADER, VINEXT_INTERCEPTION_CONTEXT_HEADER, VINEXT_INTERCEPTION_ID_HEADER, VINEXT_INTERNAL_HEADERS, VINEXT_METADATA_ROUTE_CACHE_HEADER, VINEXT_MOUNTED_SLOTS_HEADER, VINEXT_MW_CTX_HEADER, VINEXT_PARAMS_HEADER, VINEXT_PRERENDER_CACHE_LIFE_HEADER, VINEXT_PRERENDER_METADATA_ROUTES_PATH, VINEXT_PRERENDER_PAGES_STATIC_PATHS_PATH, VINEXT_PRERENDER_READINESS_HEADER, VINEXT_PRERENDER_READINESS_PATH, VINEXT_PRERENDER_RENDER_ERROR_HEADER, VINEXT_PRERENDER_REWRITTEN_HEADER, VINEXT_PRERENDER_ROUTE_PARAMS_HEADER, VINEXT_PRERENDER_SECRET_HEADER, VINEXT_PRERENDER_SPECIAL_ERROR_HEADER, VINEXT_PRERENDER_SPECULATIVE_HEADER, VINEXT_PRERENDER_STATIC_PARAMS_PATH, VINEXT_RENDERED_PATH_AND_SEARCH_HEADER, VINEXT_REVALIDATED_CACHE_TAG_HEADER, VINEXT_REVALIDATE_HEADER, VINEXT_REVALIDATE_HOST_HEADER, VINEXT_RSC_COMPLETION_METADATA_HEADER, VINEXT_RSC_MARKER_HEADER, VINEXT_RSC_REDIRECT_HEADER, VINEXT_RSC_REDIRECT_TYPE_HEADER, VINEXT_RSC_RENDER_MODE_HEADER, VINEXT_RSC_STATE_FINGERPRINT_HEADER, VINEXT_RSC_VARY_HEADER, VINEXT_SPECIAL_ERROR_STATUS_HEADER, VINEXT_STALE_TIME_PENDING_HEADER, VINEXT_TIMING_HEADER, VINEXT_TRACE_BUFFERED_BODY_HEADER, VINEXT_TRACE_ERROR_HEADER, VINEXT_TRACE_ROUTE_HEADER, isVinextRscVaryField };
@@ -117,6 +117,22 @@ export declare const IMAGE_CONTENT_SECURITY_POLICY = "script-src 'none'; frame-s
117
117
  * Returns false for SVG (unless dangerouslyAllowSVG is true), HTML, or any non-image type.
118
118
  */
119
119
  export declare function isSafeImageContentType(contentType: string | null, dangerouslyAllowSVG?: boolean): boolean;
120
+ /**
121
+ * Cache state for a successful image response, sent as both `X-Nextjs-Cache`
122
+ * and `X-Vinext-Cache`.
123
+ *
124
+ * Next.js keeps optimized images in an image cache and labels each 200 with
125
+ * where the bytes came from: `MISS` when the optimizer ran for this request,
126
+ * `HIT` or `STALE` when the cache served them. vinext has no image cache on any
127
+ * runtime, so every successful response is transformed (or passed through) from
128
+ * the source for the request that receives it. Next.js behaves the same way
129
+ * when its image cache is disabled (`images.maximumDiskCacheSize: 0`): every
130
+ * 200 is a `MISS`. `HIT` and `STALE` need a cache of optimized images.
131
+ *
132
+ * Next.js sets the header after its ETag check, so a 304 and error responses
133
+ * go out without it. Only full image responses carry either header here too.
134
+ */
135
+ export declare const IMAGE_RESPONSE_CACHE_STATE = "MISS";
120
136
  /**
121
137
  * Handlers for image optimization I/O operations.
122
138
  * Workers provide these callbacks to adapt their specific bindings.
@@ -1,4 +1,5 @@
1
1
  import { badRequestResponse } from "./http-error-responses.js";
2
+ import { setCacheStateHeaders } from "./cache-headers.js";
2
3
  //#region src/server/image-optimization.ts
3
4
  /**
4
5
  * Image optimization request handler.
@@ -182,11 +183,32 @@ function setImageSecurityHeaders(headers, config) {
182
183
  headers.set("X-Content-Type-Options", "nosniff");
183
184
  headers.set("Content-Disposition", config?.contentDispositionType === "attachment" ? "attachment" : "inline");
184
185
  }
185
- function createPassthroughImageResponse(source, config) {
186
- const headers = new Headers(source.headers);
186
+ /**
187
+ * Cache state for a successful image response, sent as both `X-Nextjs-Cache`
188
+ * and `X-Vinext-Cache`.
189
+ *
190
+ * Next.js keeps optimized images in an image cache and labels each 200 with
191
+ * where the bytes came from: `MISS` when the optimizer ran for this request,
192
+ * `HIT` or `STALE` when the cache served them. vinext has no image cache on any
193
+ * runtime, so every successful response is transformed (or passed through) from
194
+ * the source for the request that receives it. Next.js behaves the same way
195
+ * when its image cache is disabled (`images.maximumDiskCacheSize: 0`): every
196
+ * 200 is a `MISS`. `HIT` and `STALE` need a cache of optimized images.
197
+ *
198
+ * Next.js sets the header after its ETag check, so a 304 and error responses
199
+ * go out without it. Only full image responses carry either header here too.
200
+ */
201
+ const IMAGE_RESPONSE_CACHE_STATE = "MISS";
202
+ /** Headers shared by every successful image response, transformed or not. */
203
+ function setImageResponseHeaders(headers, config) {
187
204
  headers.set("Cache-Control", IMAGE_CACHE_CONTROL);
188
205
  headers.set("Vary", "Accept");
189
206
  setImageSecurityHeaders(headers, config);
207
+ setCacheStateHeaders(headers, IMAGE_RESPONSE_CACHE_STATE);
208
+ }
209
+ function createPassthroughImageResponse(source, config) {
210
+ const headers = new Headers(source.headers);
211
+ setImageResponseHeaders(headers, config);
190
212
  return new Response(source.body, {
191
213
  status: 200,
192
214
  headers
@@ -216,9 +238,7 @@ async function handleImageOptimization(request, handlers, allowedWidths, imageCo
216
238
  quality
217
239
  });
218
240
  const headers = new Headers(transformed.headers);
219
- headers.set("Cache-Control", IMAGE_CACHE_CONTROL);
220
- headers.set("Vary", "Accept");
221
- setImageSecurityHeaders(headers, imageConfig);
241
+ setImageResponseHeaders(headers, imageConfig);
222
242
  if (!isSafeImageContentType(headers.get("Content-Type"), imageConfig?.dangerouslyAllowSVG)) headers.set("Content-Type", format);
223
243
  return new Response(transformed.body, {
224
244
  status: 200,
@@ -287,4 +307,4 @@ function handleConfiguredImageOptimization(request, fetchAsset, allowedWidths, i
287
307
  }, allowedWidths, imageConfig);
288
308
  }
289
309
  //#endregion
290
- export { DEFAULT_DEVICE_SIZES, DEFAULT_IMAGE_SIZES, IMAGE_CACHE_CONTROL, IMAGE_CONTENT_SECURITY_POLICY, IMAGE_OPTIMIZATION_PATH, VINEXT_IMAGE_OPTIMIZATION_PATH, getImageOptimizer, handleConfiguredImageOptimization, handleImageOptimization, isImageOptimizationPath, isSafeImageContentType, negotiateImageFormat, parseImageParams, resolveDevImageRedirect, setImageOptimizer };
310
+ export { DEFAULT_DEVICE_SIZES, DEFAULT_IMAGE_SIZES, IMAGE_CACHE_CONTROL, IMAGE_CONTENT_SECURITY_POLICY, IMAGE_OPTIMIZATION_PATH, IMAGE_RESPONSE_CACHE_STATE, VINEXT_IMAGE_OPTIMIZATION_PATH, getImageOptimizer, handleConfiguredImageOptimization, handleImageOptimization, isImageOptimizationPath, isSafeImageContentType, negotiateImageFormat, parseImageParams, resolveDevImageRedirect, setImageOptimizer };
@@ -1,12 +1,12 @@
1
1
  import { PRERENDER_REVALIDATE_HEADER, PRERENDER_REVALIDATE_ONLY_GENERATED_HEADER } from "../utils/protocol-headers.js";
2
2
  import { normalizePathnameForRouteMatch } from "../routing/utils.js";
3
3
  import { getRequestExecutionContext } from "../shims/request-context.js";
4
+ import { isInterceptionMatchedUrlPath } from "./normalize-path.js";
4
5
  import { reportRequestError } from "./instrumentation.js";
5
6
  import { getCdnCacheAdapter } from "../shims/cdn-cache.js";
6
7
  import { fnv1a64 } from "../utils/hash.js";
7
8
  import { normalizeMountedSlotsHeader } from "./app-mounted-slots-header.js";
8
9
  import { APP_RSC_RENDER_MODE_NAVIGATION, getRscRenderModeCacheVariant } from "./app-rsc-render-mode.js";
9
- import { normalizeAppPageInterceptionProofPathname } from "./app-page-render-identity.js";
10
10
  import { getRevalidateSecret, isOnDemandRevalidateRequest, isRevalidateSecret } from "./revalidation-request.js";
11
11
  //#region src/server/isr-cache.ts
12
12
  /**
@@ -183,7 +183,7 @@ function appIsrHtmlKey(pathname) {
183
183
  return appIsrCacheKey(pathname, "html");
184
184
  }
185
185
  function normalizeInterceptionContextForCacheKey(interceptionContext) {
186
- return normalizeAppPageInterceptionProofPathname(interceptionContext);
186
+ return isInterceptionMatchedUrlPath(interceptionContext) ? interceptionContext : null;
187
187
  }
188
188
  /**
189
189
  * Build the ISR cache key for an RSC payload.