vinext 1.4.1 → 1.4.2

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 (83) hide show
  1. package/README.md +1 -1
  2. package/dist/build/action-owner-manifest.js +1 -0
  3. package/dist/check.js +1 -1
  4. package/dist/config/config-matchers.js +2 -2
  5. package/dist/config/destination-params.js +5 -2
  6. package/dist/config/next-config.d.ts +16 -1
  7. package/dist/config/next-config.js +31 -1
  8. package/dist/entries/app-rsc-entry.js +41 -18
  9. package/dist/entries/app-rsc-manifest.js +4 -0
  10. package/dist/entries/pages-server-entry.js +2 -0
  11. package/dist/index.js +15 -12
  12. package/dist/init-cloudflare.js +18 -2
  13. package/dist/plugins/css-target.d.ts +18 -0
  14. package/dist/plugins/css-target.js +93 -0
  15. package/dist/routing/app-route-graph.d.ts +10 -0
  16. package/dist/routing/app-route-graph.js +15 -1
  17. package/dist/server/api-handler.d.ts +2 -0
  18. package/dist/server/api-handler.js +3 -5
  19. package/dist/server/app-action-forwarding.js +3 -1
  20. package/dist/server/app-browser-entry.js +21 -4
  21. package/dist/server/app-elements.d.ts +3 -0
  22. package/dist/server/app-elements.js +14 -1
  23. package/dist/server/app-fallback-renderer.js +2 -2
  24. package/dist/server/app-optimistic-routing.d.ts +12 -1
  25. package/dist/server/app-optimistic-routing.js +38 -16
  26. package/dist/server/app-page-boundary-render.js +2 -2
  27. package/dist/server/app-page-cache-render.js +1 -1
  28. package/dist/server/app-page-dispatch.d.ts +10 -1
  29. package/dist/server/app-page-dispatch.js +73 -30
  30. package/dist/server/app-page-element-builder.d.ts +9 -20
  31. package/dist/server/app-page-element-builder.js +37 -49
  32. package/dist/server/app-page-intercept-segment-config.d.ts +56 -0
  33. package/dist/server/app-page-intercept-segment-config.js +90 -0
  34. package/dist/server/app-page-navigation-params.d.ts +23 -0
  35. package/dist/server/app-page-navigation-params.js +49 -0
  36. package/dist/server/app-page-render.d.ts +5 -2
  37. package/dist/server/app-page-render.js +17 -3
  38. package/dist/server/app-page-request.d.ts +24 -2
  39. package/dist/server/app-page-request.js +10 -10
  40. package/dist/server/app-page-route-wiring.d.ts +10 -0
  41. package/dist/server/app-page-route-wiring.js +16 -12
  42. package/dist/server/app-page-segment-plan.js +9 -1
  43. package/dist/server/app-page-segment-state.d.ts +9 -0
  44. package/dist/server/app-page-segment-state.js +54 -1
  45. package/dist/server/app-response-stage-entry.js +1 -0
  46. package/dist/server/app-route-module-loader.d.ts +2 -0
  47. package/dist/server/app-route-module-loader.js +1 -0
  48. package/dist/server/app-rsc-handler.d.ts +10 -1
  49. package/dist/server/app-rsc-handler.js +75 -43
  50. package/dist/server/app-rsc-response-finalizer.d.ts +3 -0
  51. package/dist/server/app-rsc-response-finalizer.js +11 -1
  52. package/dist/server/app-rsc-route-matching.d.ts +14 -0
  53. package/dist/server/app-rsc-route-matching.js +42 -18
  54. package/dist/server/app-server-action-execution.js +16 -10
  55. package/dist/server/cache-control.d.ts +2 -0
  56. package/dist/server/cache-control.js +3 -1
  57. package/dist/server/image-local-patterns.d.ts +21 -0
  58. package/dist/server/image-local-patterns.js +59 -0
  59. package/dist/server/image-optimization.d.ts +23 -22
  60. package/dist/server/image-optimization.js +12 -2
  61. package/dist/server/metadata-route-response.js +24 -6
  62. package/dist/server/nitro-public-files-plugin.js +20 -8
  63. package/dist/server/pages-data-route.d.ts +5 -1
  64. package/dist/server/pages-data-route.js +14 -6
  65. package/dist/server/pages-request-pipeline.js +6 -5
  66. package/dist/server/pages-request-stage-entry.js +1 -0
  67. package/dist/server/prod-server.js +17 -9
  68. package/dist/server/request-pipeline.d.ts +5 -0
  69. package/dist/server/request-pipeline.js +11 -1
  70. package/dist/server/response-stage-cacheability.d.ts +7 -0
  71. package/dist/server/response-stage-cacheability.js +6 -5
  72. package/dist/server/worker-image-optimization.d.ts +2 -2
  73. package/dist/server/worker-image-optimization.js +18 -3
  74. package/dist/server/worker-utils.d.ts +8 -0
  75. package/dist/server/worker-utils.js +19 -2
  76. package/dist/shims/image-props.d.ts +6 -0
  77. package/dist/shims/image-props.js +39 -3
  78. package/dist/shims/image.js +3 -3
  79. package/dist/shims/slot.d.ts +3 -0
  80. package/dist/shims/slot.js +67 -9
  81. package/dist/utils/base-path.d.ts +7 -0
  82. package/dist/utils/base-path.js +11 -1
  83. package/package.json +1 -1
@@ -669,9 +669,11 @@ function buildAppPageElements(options) {
669
669
  branchAccessBoundaries.set(treePosition, entries);
670
670
  }
671
671
  const branchErrors = /* @__PURE__ */ new Map();
672
- if (overrideOrPageComponent && hasSlotTreeOverride) for (const [index, errorModule] of (slotOverride?.errorModules ?? []).entries()) {
672
+ const slotErrorModules = !overrideOrPageComponent ? void 0 : hasSlotTreeOverride ? slotOverride?.errorModules : slot.errors;
673
+ const slotErrorTreePositions = hasSlotTreeOverride ? slotOverride?.errorTreePositions : slot.errorTreePositions;
674
+ for (const [index, errorModule] of (slotErrorModules ?? []).entries()) {
673
675
  const component = getErrorBoundaryExport(errorModule);
674
- const treePosition = slotOverride?.errorTreePositions?.[index];
676
+ const treePosition = slotErrorTreePositions?.[index];
675
677
  if (component && treePosition !== void 0) branchErrors.set(treePosition, component);
676
678
  }
677
679
  const resolveSlotNotFoundBoundary = (treePosition) => {
@@ -753,12 +755,6 @@ function buildAppPageElements(options) {
753
755
  ])).filter((treePosition) => !prefetchSlotLoadingEntry || !isOwnedAtRoutePrefetchCutoff && treePosition <= prefetchSlotLoadingEntry.treePosition).sort((left, right) => left - right);
754
756
  for (let index = branchTreePositions.length - 1; index >= 0; index--) {
755
757
  const treePosition = branchTreePositions[index];
756
- const nestedBfcacheSegment = nestedBfcacheSegments.find((segment) => segment.treePosition === treePosition);
757
- if (nestedBfcacheSegment) slotElement = /* @__PURE__ */ jsx(BfcacheSegmentBoundary, {
758
- id: nestedBfcacheSegment.id,
759
- stateKey: nestedBfcacheSegment.stateKey,
760
- children: slotElement
761
- });
762
758
  const loadingComponents = prefetchSlotLoadingEntry ? [] : branchLoadings.get(treePosition) ?? [];
763
759
  for (let loadingIndex = loadingComponents.length - 1; loadingIndex >= 0; loadingIndex--) {
764
760
  const LoadingComponent = loadingComponents[loadingIndex];
@@ -786,6 +782,12 @@ function buildAppPageElements(options) {
786
782
  fallback: SegmentErrorComponent,
787
783
  children: slotElement
788
784
  });
785
+ const nestedBfcacheSegment = nestedBfcacheSegments.find((segment) => segment.treePosition === treePosition);
786
+ if (nestedBfcacheSegment) slotElement = /* @__PURE__ */ jsx(BfcacheSegmentBoundary, {
787
+ id: nestedBfcacheSegment.id,
788
+ stateKey: nestedBfcacheSegment.stateKey,
789
+ children: slotElement
790
+ });
789
791
  const layoutEntriesAtPosition = branchLayouts.get(treePosition) ?? [];
790
792
  for (let layoutIndex = layoutEntriesAtPosition.length - 1; layoutIndex >= 0; layoutIndex--) {
791
793
  const layoutEntry = layoutEntriesAtPosition[layoutIndex];
@@ -878,17 +880,19 @@ function buildAppPageElements(options) {
878
880
  children: routeChildren
879
881
  });
880
882
  const firstLayoutWithElementTreePosition = layoutEntries.find((entry) => getDefaultExport(entry.layoutModule) !== null)?.treePosition;
881
- const keepsSegmentLevelLists = String(process.env.__NEXT_CACHE_COMPONENTS) === "true" && firstLayoutWithElementTreePosition !== void 0 && !options.route.childrenSlot && options.semanticPageIdentity === void 0;
882
- const spineTreePositions = keepsSegmentLevelLists ? Array.from(/* @__PURE__ */ new Set([...orderedTreePositions, ...Array.from({ length: Math.max(0, routeSegments.length - firstLayoutWithElementTreePosition) }, (_, offset) => firstLayoutWithElementTreePosition + 1 + offset)])).sort((left, right) => left - right) : orderedTreePositions;
883
+ const siblingInterceptSegmentKeys = options.siblingInterceptSegmentKeys ?? null;
884
+ const keepsSegmentLevelLists = String(process.env.__NEXT_CACHE_COMPONENTS) === "true" && firstLayoutWithElementTreePosition !== void 0 && !options.route.childrenSlot?.synthetic && (options.semanticPageIdentity === void 0 || siblingInterceptSegmentKeys !== null);
885
+ const spineSegmentCount = siblingInterceptSegmentKeys?.length ?? routeSegments.length;
886
+ const spineTreePositions = keepsSegmentLevelLists ? Array.from(/* @__PURE__ */ new Set([...orderedTreePositions, ...Array.from({ length: Math.max(0, spineSegmentCount - firstLayoutWithElementTreePosition) }, (_, offset) => firstLayoutWithElementTreePosition + 1 + offset)])).sort((left, right) => left - right) : orderedTreePositions;
883
887
  const renderedTreePositions = isPrefetchLoadingShell ? spineTreePositions.filter(includesPrefetchTreePosition) : spineTreePositions;
884
888
  const firstLayoutBelowShell = keepsSegmentLevelLists && isPrefetchLoadingShell ? layoutEntries.find((entry) => !includesPrefetchTreePosition(entry.treePosition) && getDefaultExport(entry.layoutModule) !== null) : void 0;
885
889
  let childSegmentElementId = firstLayoutBelowShell?.id ?? pageElementId;
886
- let childSegmentTreePosition = firstLayoutBelowShell?.treePosition ?? routeSegments.length + 1;
890
+ let childSegmentTreePosition = firstLayoutBelowShell?.treePosition ?? spineSegmentCount + 1;
887
891
  const retainChildSegment = (treePosition, children) => treePosition === childSegmentTreePosition - 1 ? /* @__PURE__ */ jsx(BfcacheChildrenBoundary, {
888
892
  id: childSegmentElementId,
889
893
  children
890
894
  }) : /* @__PURE__ */ jsx(BfcacheChildrenBoundary, {
891
- stateKey: createBfcacheChildSegmentStateKey(resolveAppPageTemplateStateKey(routeSegments, treePosition, options.matchedParams)),
895
+ stateKey: createBfcacheChildSegmentStateKey(siblingInterceptSegmentKeys ? JSON.stringify(siblingInterceptSegmentKeys.slice(0, treePosition + 1)) : resolveAppPageTemplateStateKey(routeSegments, treePosition, options.matchedParams)),
892
896
  children
893
897
  });
894
898
  for (let index = renderedTreePositions.length - 1; index >= 0; index--) {
@@ -1,7 +1,7 @@
1
1
  import { AppElementsWire } from "./app-elements-wire.js";
2
2
  import "./app-elements.js";
3
3
  import { createNestedBfcacheSlotSegmentId, deriveBfcacheSegmentIdentity } from "./bfcache-identity.js";
4
- import { resolveAppPageRouteStateKey, resolveAppPageSemanticSegmentStateKey, resolveAppPageTemplateStateKey } from "./app-page-segment-state.js";
4
+ import { APP_PAGE_DEFAULT_SEGMENT_KEY, APP_PAGE_SEGMENT_KEY, resolveAppPageRouteStateKey, resolveAppPageSemanticSegmentStateKey, resolveAppPageTemplateStateKey } from "./app-page-segment-state.js";
5
5
  //#region src/server/app-page-segment-plan.ts
6
6
  function requireGraphSequenceId(ids, index, kind) {
7
7
  const id = ids[index];
@@ -118,6 +118,14 @@ function createAppPageSegmentPlan(options) {
118
118
  treePosition
119
119
  });
120
120
  }
121
+ if (semanticSegments.length > 0) {
122
+ const leafKey = slotBinding.state === "default" ? APP_PAGE_DEFAULT_SEGMENT_KEY : APP_PAGE_SEGMENT_KEY;
123
+ semanticSegments.push({
124
+ identityKey: JSON.stringify([...boundSegmentKeys, leafKey]),
125
+ stateKey: leafKey,
126
+ treePosition: branchSegments.length
127
+ });
128
+ }
121
129
  }
122
130
  const includedInPayload = options.includeSlot(ownerTreePosition, targetTreePosition);
123
131
  const graphId = graphIds ? graphIds.slots[slotKey] : slot.id ?? slotId;
@@ -1,8 +1,17 @@
1
1
  import { AppPageParams } from "./app-page-boundary.js";
2
2
  //#region src/server/app-page-segment-state.d.ts
3
3
  export declare const APP_PAGE_SEGMENT_KEY = "__PAGE__";
4
+ export declare const APP_PAGE_DEFAULT_SEGMENT_KEY = "__DEFAULT__";
4
5
  export declare function isAppPageRouteGroupSegment(segment: string): boolean;
5
6
  export declare function canonicalizeAppPageParams(params: AppPageParams): void;
7
+ /**
8
+ * Rebinds segment state keys and BFCache identities resolved with one set of
9
+ * params to another. Keys are JSON arrays of segment keys, nested to any
10
+ * depth, so each whole segment key is compared with the param tokens
11
+ * `resolveSingleSegmentStateKey` produced for the learned params; only those
12
+ * whose value differs are replaced. Returns null when no token changes.
13
+ */
14
+ export declare function createAppPageSegmentStateKeyRebinder(fromParams: AppPageParams, toParams: AppPageParams): ((stateKey: string) => string) | null;
6
15
  export declare function resolveAppPageChildSegments(routeSegments: readonly string[], treePosition: number, params: AppPageParams): string[];
7
16
  export declare function resolveAppPageSegmentStateKey(routeSegments: readonly string[], treePosition: number, params: AppPageParams): string;
8
17
  /**
@@ -1,6 +1,7 @@
1
1
  import { isCatchAllSegment, isOptionalCatchAllSegment } from "../routing/utils.js";
2
2
  //#region src/server/app-page-segment-state.ts
3
3
  const APP_PAGE_SEGMENT_KEY = "__PAGE__";
4
+ const APP_PAGE_DEFAULT_SEGMENT_KEY = "__DEFAULT__";
4
5
  function isDynamicSegment(segment) {
5
6
  return segment.startsWith("[") && segment.endsWith("]") && !segment.includes(".");
6
7
  }
@@ -49,6 +50,58 @@ function resolveSingleSegmentStateKey(segment, params) {
49
50
  if (!param) return segment;
50
51
  return `${param.name}|${formatSegmentStateParamValue(param, params, segment)}|${param.type}`;
51
52
  }
53
+ const SEGMENT_PARAM_TYPES = [
54
+ "d",
55
+ "c",
56
+ "oc"
57
+ ];
58
+ function formatSegmentParamPattern(param) {
59
+ if (param.type === "oc") return `[[...${param.name}]]`;
60
+ if (param.type === "c") return `[...${param.name}]`;
61
+ return `[${param.name}]`;
62
+ }
63
+ /**
64
+ * Rebinds segment state keys and BFCache identities resolved with one set of
65
+ * params to another. Keys are JSON arrays of segment keys, nested to any
66
+ * depth, so each whole segment key is compared with the param tokens
67
+ * `resolveSingleSegmentStateKey` produced for the learned params; only those
68
+ * whose value differs are replaced. Returns null when no token changes.
69
+ */
70
+ function createAppPageSegmentStateKeyRebinder(fromParams, toParams) {
71
+ const tokens = /* @__PURE__ */ new Map();
72
+ for (const name of /* @__PURE__ */ new Set([...Object.keys(fromParams), ...Object.keys(toParams)])) for (const type of SEGMENT_PARAM_TYPES) {
73
+ const param = {
74
+ name,
75
+ type
76
+ };
77
+ const fallbackSegment = formatSegmentParamPattern(param);
78
+ const from = formatSegmentStateParamValue(param, fromParams, fallbackSegment);
79
+ const to = formatSegmentStateParamValue(param, toParams, fallbackSegment);
80
+ if (from !== to) tokens.set(`${name}|${from}|${type}`, `${name}|${to}|${type}`);
81
+ }
82
+ if (tokens.size === 0) return null;
83
+ const rebind = (stateKey) => {
84
+ const token = tokens.get(stateKey);
85
+ if (token !== void 0) return token;
86
+ if (!stateKey.startsWith("[")) return stateKey;
87
+ let parts;
88
+ try {
89
+ parts = JSON.parse(stateKey);
90
+ } catch {
91
+ return stateKey;
92
+ }
93
+ if (!Array.isArray(parts)) return stateKey;
94
+ let changed = false;
95
+ const rebound = parts.map((part) => {
96
+ if (typeof part !== "string") return part;
97
+ const next = rebind(part);
98
+ if (next !== part) changed = true;
99
+ return next;
100
+ });
101
+ return changed ? JSON.stringify(rebound) : stateKey;
102
+ };
103
+ return rebind;
104
+ }
52
105
  function resolveAppPageChildSegments(routeSegments, treePosition, params) {
53
106
  const rawSegments = routeSegments.slice(treePosition);
54
107
  const resolvedSegments = [];
@@ -104,4 +157,4 @@ function resolveAppPageTemplateStateKey(routeSegments, treePosition, params) {
104
157
  return statePath.length > 0 ? JSON.stringify(statePath) : "";
105
158
  }
106
159
  //#endregion
107
- export { APP_PAGE_SEGMENT_KEY, canonicalizeAppPageParams, isAppPageRouteGroupSegment, resolveAppPageChildSegments, resolveAppPagePatternStateKey, resolveAppPageRouteStateKey, resolveAppPageSegmentStateKey, resolveAppPageSemanticSegmentStateKey, resolveAppPageTemplateStateKey };
160
+ export { APP_PAGE_DEFAULT_SEGMENT_KEY, APP_PAGE_SEGMENT_KEY, canonicalizeAppPageParams, createAppPageSegmentStateKeyRebinder, isAppPageRouteGroupSegment, resolveAppPageChildSegments, resolveAppPagePatternStateKey, resolveAppPageRouteStateKey, resolveAppPageSegmentStateKey, resolveAppPageSemanticSegmentStateKey, resolveAppPageTemplateStateKey };
@@ -58,6 +58,7 @@ async function handleResponseStage(request, env, platformCtx, props, dispatchReq
58
58
  probeMode: props.cacheability.probeMode,
59
59
  rawManifest: __cacheabilityManifest,
60
60
  recomposesRequestScopedHeaders: props.kind === "app-page" && props.isRscRequest,
61
+ keepsParamsHeader: props.kind === "app-page" && props.interceptionContext !== null,
61
62
  registerCacheAdapters: () => registerConfiguredCacheAdapters(env),
62
63
  request,
63
64
  representation: props.cacheability.representation,
@@ -67,6 +67,7 @@ type LazyLoadableSlot = {
67
67
  loading?: unknown;
68
68
  loadings?: readonly unknown[];
69
69
  error?: unknown;
70
+ errors?: readonly unknown[];
70
71
  notFound?: unknown;
71
72
  notFounds?: readonly unknown[];
72
73
  __loadPage?: LazyModuleThunk | null;
@@ -76,6 +77,7 @@ type LazyLoadableSlot = {
76
77
  __loadLoading?: LazyModuleThunk | null;
77
78
  __loadLoadings?: LazyModuleLoaderArray | null;
78
79
  __loadError?: LazyModuleThunk | null;
80
+ __loadErrors?: LazyModuleLoaderArray | null;
79
81
  __loadNotFound?: LazyModuleThunk | null;
80
82
  __loadNotFounds?: LazyModuleLoaderArray | null;
81
83
  /** Hydrated only after an intercept matches, not with the slot's base modules. */
@@ -145,6 +145,7 @@ function ensureAppRouteModulesLoaded(route) {
145
145
  pushFieldLoad(loads, slot, "loading", slot.__loadLoading);
146
146
  pushArrayLoads(loads, slot.loadings, slot.__loadLoadings);
147
147
  pushFieldLoad(loads, slot, "error", slot.__loadError);
148
+ pushArrayLoads(loads, slot.errors, slot.__loadErrors);
148
149
  pushFieldLoad(loads, slot, "notFound", slot.__loadNotFound);
149
150
  pushArrayLoads(loads, slot.notFounds, slot.__loadNotFounds);
150
151
  }
@@ -6,6 +6,7 @@ import { ExecutionContextLike } from "../shims/request-context.js";
6
6
  import { AppMiddlewareContext, ApplyAppMiddlewareResult } from "./app-middleware.js";
7
7
  import { RootParams } from "../shims/root-params.js";
8
8
  import { AppPagePprFallbackCacheShell } from "./app-ppr-fallback-shell.js";
9
+ import { AppPageNavigationParamSlot } from "./app-page-navigation-params.js";
9
10
  import { PagesRouteDataKind } from "./pages-route-data-kind.js";
10
11
  import { AppPrerenderRootParamNamesMap, AppPrerenderStaticParamsMap } from "./app-prerender-endpoints.js";
11
12
  import { TrustedPrerenderState } from "./prerender-route-params.js";
@@ -41,9 +42,17 @@ export type AppRscHandlerRoute = {
41
42
  rootParamNames?: readonly string[];
42
43
  routeHandler?: unknown;
43
44
  routeSegments: readonly string[];
44
- slots?: AppRouteTreePrefetchRoute["slots"];
45
+ slots?: Readonly<Record<string, NonNullable<AppRouteTreePrefetchRoute["slots"]>[string] & AppPageNavigationParamSlot>> | null;
45
46
  };
46
47
  type AppRscRouteMatch<TRoute> = {
48
+ /**
49
+ * For an interception match, the source route and params the interception
50
+ * context names, when they differ from the rendered `route` and `params`:
51
+ * a sibling intercept opened from a deeper source renders the intercepting
52
+ * route itself, while the deeper source is what gets authorized.
53
+ */
54
+ authorizationParams?: AppPageParams;
55
+ authorizationRoute?: TRoute;
47
56
  interceptionSourceIsConcrete?: boolean;
48
57
  params: AppPageParams;
49
58
  route: TRoute;
@@ -4,11 +4,11 @@ import { PRERENDER_REVALIDATE_HEADER, VINEXT_MW_CTX_HEADER, VINEXT_PRERENDER_ROU
4
4
  import { buildRequestHeadersFromMiddlewareResponse, hasMiddlewareRequestHeaderOverrides } from "../utils/middleware-request-headers.js";
5
5
  import { requestContextFromRequest } from "../config/request-context.js";
6
6
  import { isExternalUrl } from "../utils/external-url.js";
7
- import { addBasePathToPathname, hasBasePath, stripBasePath } from "../utils/base-path.js";
7
+ import { hasBasePath, restoreBasePath, stripBasePath } from "../utils/base-path.js";
8
8
  import "./app-rsc-vary.js";
9
9
  import { ACTION_REVALIDATED_HEADER, FLIGHT_HEADERS, VINEXT_PARAMS_HEADER, VINEXT_PRERENDER_REWRITTEN_HEADER, VINEXT_RENDERED_PATH_AND_SEARCH_HEADER, VINEXT_SPECIAL_ERROR_STATUS_HEADER } from "./headers.js";
10
10
  import { badRequestResponse, notFoundResponse, notFoundStaticAssetResponse } from "./http-error-responses.js";
11
- import { cloneRequestWithHeaders, cloneRequestWithUrl, filterInternalHeaders, normalizeTrailingSlash, normalizeTrailingSlashPathname, resolvePublicFileRoute, stripRequestUrlFragment } from "./request-pipeline.js";
11
+ import { cloneRequestWithHeaders, cloneRequestWithUrl, filterInternalHeaders, hasUrlParserDotSegment, normalizeTrailingSlash, normalizeTrailingSlashPathname, resolvePublicFileRoute, stripRequestUrlFragment } from "./request-pipeline.js";
12
12
  import { assetPrefixPathname, isNextStaticPath } from "../utils/asset-prefix.js";
13
13
  import { normalizePathnameForRouteMatch, normalizePathnameForRouteMatchStrict } from "../routing/utils.js";
14
14
  import { patternToNextFormat } from "../routing/route-validation.js";
@@ -18,7 +18,7 @@ import { isInterceptionMatchedUrlPath, normalizePath } from "./normalize-path.js
18
18
  import { normalizeDefaultLocalePathname, shouldRestoreLocaleRootTrailingSlash } from "./pages-i18n.js";
19
19
  import { buildNextDataNotFoundResponse, normalizePagesDataRequest } from "./pages-data-route.js";
20
20
  import { markRouteCacheabilityDynamic, preserveRouteCacheabilityResponsePolicy } from "../shims/cacheability-classification.js";
21
- import { NEVER_CACHE_CONTROL, applyCdnResponseHeaders, captureCdnResponsePolicyOverrides, isCdnResponsePolicyHeader, reconcileCdnResponseHeadersAfterOuterPolicy } from "./cache-control.js";
21
+ import { NEVER_CACHE_CONTROL, SERVER_ACTION_CACHE_CONTROL, applyCdnResponseHeaders, captureCdnResponsePolicyOverrides, isCdnResponsePolicyHeader, reconcileCdnResponseHeadersAfterOuterPolicy } from "./cache-control.js";
22
22
  import { isOnDemandRevalidateRequest } from "./revalidation-request.js";
23
23
  import { getDraftModeCookieHeader, getEffectiveRequestCookieHeader, getHeadersContext, hasEffectiveRequestCookieChanges, headersContextFromRequest, isDraftModeEnabled, isDraftModeRequest, runWithHeadersContext } from "../shims/headers.js";
24
24
  import { mergeRewriteQuery } from "../utils/query.js";
@@ -33,11 +33,13 @@ import { setFrameworkRequestRoute, traceFrameworkRequest } from "./request-traci
33
33
  import { mergeMiddlewareResponseHeaders, mergeMiddlewareResponseHeadersOntoResponse, withExternalRewriteResponseHeaders } from "./middleware-response-headers.js";
34
34
  import { VINEXT_RSC_CACHE_BUSTING_SEARCH_PARAM, canonicalizeLoadingShellRscRequestHeaders, canonicalizePrewarmableRscRequestHeaders, createCanonicalRscRequestUrl, createRscRedirectLocation, createRscRequestUrl, hasRscCacheBustingSearchParam, resolveInvalidRscCacheBustingRequest, stripRscCacheBustingSearchParam } from "./app-rsc-cache-busting.js";
35
35
  import { matchPrerenderRouteParamsPayload, readTrustedPrerenderRouteParams, serializePrerenderRouteParamsHeader } from "./prerender-route-params.js";
36
+ import { applyAppRscConfigHeaders, finalizeAppRscResponse, isStreamedActionRedirectResponse, markAppRscResponseConfigHeadersApplied, markStreamedActionRedirectResponse } from "./app-rsc-response-finalizer.js";
36
37
  import { createServerActionNotFoundResponse, getServerActionNotFoundMessage } from "./server-action-not-found.js";
37
38
  import { isPossibleAppRouteActionRequest } from "./app-action-request.js";
38
39
  import "./app-page-response.js";
39
40
  import { parseNextHttpErrorDigest } from "./next-error-digest.js";
40
41
  import { isAppRouteHandlerRoute } from "./app-rsc-route-matching.js";
42
+ import { resolveAppPageNavigationParams } from "./app-page-navigation-params.js";
41
43
  import { traceAppPageRender } from "./app-page-tracing.js";
42
44
  import { normalizeInterceptionContextHeader } from "./app-interception-context-header.js";
43
45
  import { buildPageCacheTags } from "./implicit-tags.js";
@@ -46,7 +48,6 @@ import { pickRootParams, setRootParams } from "../shims/root-params.js";
46
48
  import { createRouteTreePrefetchResponse, isRouteTreePrefetchRequest } from "./app-route-tree-prefetch.js";
47
49
  import { createSharedAppCacheIdentity, createSharedAppPageCacheIdentity, createSharedAppRouteCacheIdentity, createSharedHybridPagesCacheIdentity, isStaticCandidateAppPageDispatch, prepareSharedAppPageDispatch, withoutAppPageDispatchQuery } from "./app-worker-stages.js";
48
50
  import { flattenErrorCauses } from "../utils/error-cause.js";
49
- import { applyAppRscConfigHeaders, finalizeAppRscResponse, markAppRscResponseConfigHeadersApplied } from "./app-rsc-response-finalizer.js";
50
51
  import { normalizeRscRequest } from "./app-rsc-request-normalization.js";
51
52
  import { hasPagesPreviewCookie } from "./pages-response-stage.js";
52
53
  import { runWithPrerenderWorkUnit } from "./prerender-work-unit-setup.js";
@@ -132,11 +133,8 @@ function readCacheabilityRequestProjection(options) {
132
133
  };
133
134
  return parsedCacheabilityRequestProjection.manifest;
134
135
  }
135
- function hasUrlParserDotSegment(pathname) {
136
- return pathname.split("/").some((segment) => {
137
- const decodedDots = segment.replaceAll(/%2e/gi, ".");
138
- return decodedDots === "." || decodedDots === "..";
139
- });
136
+ function isSameInterceptionSource(a, b) {
137
+ return b != null && (a.authorizationRoute ?? a.route) === b.route && haveSamePageParams(a.authorizationParams ?? a.params, b.params);
140
138
  }
141
139
  function applyMiddlewareContextToResponse(response, middlewareContext, appendToPostConfigLink = false) {
142
140
  if (!middlewareContext.headers && middlewareContext.status == null) return response;
@@ -152,6 +150,27 @@ function applyMiddlewareContextToResponse(response, middlewareContext, appendToP
152
150
  }));
153
151
  }
154
152
  /**
153
+ * These headers describe the current routed request, not the shared RSC
154
+ * bytes, so they are composed above a transported response stage: a cache HIT
155
+ * cannot replay another query/path and never loses dynamic params. An
156
+ * intercepted response's params include its target's, which this match lacks,
157
+ * so the response stage keeps them under an interception context, in its
158
+ * cache identity.
159
+ */
160
+ function withRoutedRscHeaders(response, navigationParams, resolvedUrl, keepsRenderedParams) {
161
+ const headers = new Headers(response.headers);
162
+ if (!keepsRenderedParams || !headers.has("X-Vinext-Params")) {
163
+ if (Object.keys(navigationParams).length > 0) headers.set(VINEXT_PARAMS_HEADER, encodeURIComponent(JSON.stringify(navigationParams)));
164
+ else headers.delete(VINEXT_PARAMS_HEADER);
165
+ }
166
+ headers.set(VINEXT_RENDERED_PATH_AND_SEARCH_HEADER, encodeURIComponent(resolvedUrl));
167
+ return preserveFullyBufferedBodyMetadata(response, new Response(response.body, {
168
+ headers,
169
+ status: response.status,
170
+ statusText: response.statusText
171
+ }));
172
+ }
173
+ /**
155
174
  * Next.js answers a Link's segment prefetch of a page whose notFound(),
156
175
  * forbidden() or unauthorized() it stored with a 200, so its router renders
157
176
  * the page's fallback without loading the document. A shared response stage
@@ -257,10 +276,10 @@ function requestWithoutRscCacheBustingSearchParam(request) {
257
276
  stripRscCacheBustingSearchParam(url);
258
277
  return cloneRequestWithUrl(request, url.toString());
259
278
  }
260
- /** Send a response with the never-cache policy, rebuilding immutable headers. */
261
- function withNeverCacheControl(response) {
279
+ /** Send a response with a never-cache policy, rebuilding immutable headers. */
280
+ function withNeverCacheControl(response, cacheControl = NEVER_CACHE_CONTROL) {
262
281
  const applyNoStore = (headers) => {
263
- applyCdnResponseHeaders(headers, { cacheControl: NEVER_CACHE_CONTROL });
282
+ applyCdnResponseHeaders(headers, { cacheControl });
264
283
  };
265
284
  let markedResponse = response;
266
285
  try {
@@ -283,6 +302,19 @@ function withUnmatchedRouteCacheControl(response) {
283
302
  unmatchedRouteResponses.add(markedResponse);
284
303
  return markedResponse;
285
304
  }
305
+ const serverActionResponses = /* @__PURE__ */ new WeakSet();
306
+ function markServerActionResponse(response) {
307
+ serverActionResponses.add(response);
308
+ return response;
309
+ }
310
+ /**
311
+ * A fetch action's redirect that streams its target's render keeps the
312
+ * target's policy, as in Next.js. Every other action response is never cached.
313
+ */
314
+ function withServerActionCacheControl(response) {
315
+ if (isStreamedActionRedirectResponse(response)) return response;
316
+ return withNeverCacheControl(response, SERVER_ACTION_CACHE_CONTROL);
317
+ }
286
318
  async function handleAppRscRequest(options, request, preMiddlewareRequestContext, middlewareContext, isDataRequest, isMiddlewareDataRequest, pagesDataRequest, dispatchInternalRequest, allowInternalRscDocumentFallback, dispatchResponseStage, responseStageProbeMode = null, setInterceptionResponseUncacheable = () => {}) {
287
319
  const handlerStart = process.env.NODE_ENV !== "production" ? performance.now() : 0;
288
320
  if (process.env.NODE_ENV !== "production") {
@@ -339,7 +371,7 @@ async function handleAppRscRequest(options, request, preMiddlewareRequestContext
339
371
  const provesConcreteInterceptionSource = (sourceMatch, sourceContext = interceptionContextHeader) => {
340
372
  if (sourceMatch === null) return false;
341
373
  if (sourceMatch.interceptionSourceIsConcrete !== void 0) return sourceMatch.interceptionSourceIsConcrete;
342
- return sourceContext !== null && (options.matchRequestRoute ?? options.matchRoute)(sourceContext)?.route === sourceMatch.route;
374
+ return sourceContext !== null && (options.matchRequestRoute ?? options.matchRoute)(sourceContext)?.route === (sourceMatch.authorizationRoute ?? sourceMatch.route);
343
375
  };
344
376
  if (provesConcreteInterceptionSource(hadBasePath && hasRawInterceptionContext && interceptionContextHeader !== null ? options.matchInterceptRoute?.(requestPathname, interceptionContextHeader, interceptionIdHeader) ?? null : null)) setInterceptionResponseUncacheable(false);
345
377
  if (pathname === "/__vinext/prerender/static-params" || pathname === "/__vinext/prerender/pages-static-paths" || pathname === "/__vinext/prerender/metadata-routes") {
@@ -510,16 +542,7 @@ async function handleAppRscRequest(options, request, preMiddlewareRequestContext
510
542
  statusText: response.statusText
511
543
  });
512
544
  }
513
- if (props.kind !== "app-page" || !props.isRscRequest) return response;
514
- const headers = new Headers(response.headers);
515
- if (Object.keys(props.params).length > 0) headers.set(VINEXT_PARAMS_HEADER, encodeURIComponent(JSON.stringify(props.params)));
516
- else headers.delete(VINEXT_PARAMS_HEADER);
517
- headers.set(VINEXT_RENDERED_PATH_AND_SEARCH_HEADER, encodeURIComponent(props.resolvedUrl));
518
- return preserveFullyBufferedBodyMetadata(response, new Response(response.body, {
519
- headers,
520
- status: response.status,
521
- statusText: response.statusText
522
- }));
545
+ return response;
523
546
  } : void 0;
524
547
  const responseStageRequest = (stageRequest = request) => {
525
548
  const activeHeaders = getHeadersContext()?.headers;
@@ -629,10 +652,13 @@ async function handleAppRscRequest(options, request, preMiddlewareRequestContext
629
652
  const isPostRequest = request.method.toUpperCase() === "POST";
630
653
  const contentType = request.headers.get("content-type") || "";
631
654
  const isProgressiveActionRequest = isPostRequest && !actionId && contentType.startsWith("multipart/form-data");
632
- const serveImageEndpointIfMatched = () => {
633
- if (!filesystemRouteEligible || !resolvedPathnameHasBasePath || !isImageOptimizationPath(cleanPathname)) return null;
655
+ const serveImageEndpointIfMatched = async () => {
656
+ if (!filesystemRouteEligible || !resolvedPathnameHasBasePath || !isImageOptimizationPath(cleanPathnameIsRequestPathname ? requestPathname : cleanPathname)) return null;
634
657
  const imageUrl = new URL(resolvedUrl, url);
635
- const imageRedirect = resolveDevImageRedirect(imageUrl, [...options.imageConfig?.deviceSizes ?? DEFAULT_DEVICE_SIZES, ...options.imageConfig?.imageSizes ?? DEFAULT_IMAGE_SIZES], options.imageConfig?.qualities, { isDev: options.isDev });
658
+ const imageRedirect = resolveDevImageRedirect(imageUrl, [...options.imageConfig?.deviceSizes ?? DEFAULT_DEVICE_SIZES, ...options.imageConfig?.imageSizes ?? DEFAULT_IMAGE_SIZES], options.imageConfig?.qualities, {
659
+ isDev: options.isDev,
660
+ localPatterns: options.imageConfig?.localPatterns
661
+ });
636
662
  const middlewareHeaders = new Headers();
637
663
  mergeMiddlewareResponseHeaders(middlewareHeaders, middlewareContext.headers);
638
664
  if (!imageRedirect) return new Response("Invalid image optimization parameters", {
@@ -641,12 +667,12 @@ async function handleAppRscRequest(options, request, preMiddlewareRequestContext
641
667
  });
642
668
  const overrideHeaders = middlewareContext.requestHeaders ?? middlewareContext.headers;
643
669
  return markImageOptimizationSignal(Response.redirect(new URL(imageRedirect, url.origin).href, 302), {
644
- middlewareHeaders,
670
+ middlewareHeaders: new Headers(await loadPreHandlerResponseHeaders()),
645
671
  requestHeaders: overrideHeaders ? buildRequestHeadersFromMiddlewareResponse(request.headers, overrideHeaders) : null,
646
672
  search: imageUrl.search
647
673
  });
648
674
  };
649
- const imageEndpointResponse = serveImageEndpointIfMatched();
675
+ const imageEndpointResponse = await serveImageEndpointIfMatched();
650
676
  if (imageEndpointResponse) return imageEndpointResponse;
651
677
  const metadataRouteResponse = await renderMetadataRouteIfMatched();
652
678
  if (metadataRouteResponse) return composeResponseStageResponse(metadataRouteResponse);
@@ -689,7 +715,7 @@ async function handleAppRscRequest(options, request, preMiddlewareRequestContext
689
715
  bypassInterceptionContextCache = true;
690
716
  setInterceptionResponseUncacheable(true);
691
717
  };
692
- if (interceptionSourceMatch !== null && interceptionContextHeader !== null && interceptionSourcePathname !== null && !(interceptionSourceMatch.route === directPreActionMatch?.route && haveSamePageParams(interceptionSourceMatch.params, directPreActionMatch.params))) {
718
+ if (interceptionSourceMatch !== null && interceptionContextHeader !== null && interceptionSourcePathname !== null && !isSameInterceptionSource(interceptionSourceMatch, directPreActionMatch)) {
693
719
  const sourceRequestPathname = options.skipTrailingSlashRedirect ? interceptionContextHeader : normalizeTrailingSlashPathname(interceptionContextHeader, options.trailingSlash) ?? interceptionContextHeader;
694
720
  const decodedSourceRequestPathname = options.skipTrailingSlashRedirect ? interceptionSourcePathname : normalizeTrailingSlashPathname(interceptionSourcePathname, options.trailingSlash) ?? interceptionSourcePathname;
695
721
  const adoptRewrittenInterceptionSource = (rewrittenPathname) => {
@@ -699,7 +725,7 @@ async function handleAppRscRequest(options, request, preMiddlewareRequestContext
699
725
  interceptionSourceContext = rewrittenPathname;
700
726
  }
701
727
  const rewrittenSourceMatch = options.matchRoute(rewrittenPathname);
702
- return interceptionSourceMatch !== null && rewrittenSourceMatch?.route === interceptionSourceMatch.route && haveSamePageParams(rewrittenSourceMatch.params, interceptionSourceMatch.params);
728
+ return interceptionSourceMatch !== null && isSameInterceptionSource(interceptionSourceMatch, rewrittenSourceMatch);
703
729
  };
704
730
  if (configMatchers?.matchRedirect(matchPathname(sourceRequestPathname), options.configRedirects, preMiddlewareRequestContext, currentBasePathState(), dispatchResponseStage ? void 0 : markConditionalRedirectCacheability)) {
705
731
  options.clearRequestContext();
@@ -709,7 +735,7 @@ async function handleAppRscRequest(options, request, preMiddlewareRequestContext
709
735
  if (runMiddleware) {
710
736
  const sourceUrl = new URL(userlandRequest.url);
711
737
  sourceUrl.search = new URL(resolvedUrl, url).search;
712
- const sourceUrlPathname = resolvedPathnameHasBasePath && options.basePath ? sourceRequestPathname === "/" ? options.basePath : options.basePath + sourceRequestPathname : sourceRequestPathname;
738
+ const sourceUrlPathname = resolvedPathnameHasBasePath ? restoreBasePath(sourceRequestPathname, options.basePath) : sourceRequestPathname;
713
739
  sourceUrl.pathname = sourceUrlPathname;
714
740
  if (sourceUrl.pathname !== sourceUrlPathname) {
715
741
  options.clearRequestContext();
@@ -798,7 +824,7 @@ async function handleAppRscRequest(options, request, preMiddlewareRequestContext
798
824
  }
799
825
  return !didRewriteSource || adoptRewrittenInterceptionSource(sourceRulesPathname);
800
826
  };
801
- if (!rewriteSource(options.configRewrites.beforeFiles, false) || interceptionSourceMatch.route.isDynamic && !rewriteSource(options.configRewrites.afterFiles, true)) {
827
+ if (!rewriteSource(options.configRewrites.beforeFiles, false) || (interceptionSourceMatch.authorizationRoute ?? interceptionSourceMatch.route).isDynamic && !rewriteSource(options.configRewrites.afterFiles, true)) {
802
828
  options.clearRequestContext();
803
829
  return notFoundResponse();
804
830
  }
@@ -844,7 +870,7 @@ async function handleAppRscRequest(options, request, preMiddlewareRequestContext
844
870
  const handleServerAction = async (routeMatch, routePathname) => {
845
871
  if (!filesystemRouteEligible || !isPostRequest || !actionId) return null;
846
872
  if (routeMatch && isAppRouteHandlerRoute(routeMatch.route)) return null;
847
- if (!options.handleServerActionRequest) return createMissingServerActionResponse(options, actionId);
873
+ if (!options.handleServerActionRequest) return markServerActionResponse(createMissingServerActionResponse(options, actionId));
848
874
  const sourceConfigHeaders = new Headers();
849
875
  await applyAppRscConfigHeaders(sourceConfigHeaders, request, {
850
876
  basePath: options.basePath,
@@ -855,7 +881,7 @@ async function handleAppRscRequest(options, request, preMiddlewareRequestContext
855
881
  recordCacheability: dispatchResponseStage === void 0,
856
882
  requestContext: preMiddlewareRequestContext
857
883
  });
858
- return options.handleServerActionRequest({
884
+ const serverActionResponse = await options.handleServerActionRequest({
859
885
  actionId,
860
886
  cleanPathname,
861
887
  contentType,
@@ -871,13 +897,14 @@ async function handleAppRscRequest(options, request, preMiddlewareRequestContext
871
897
  sourceConfigHeaders,
872
898
  searchParams: getResolvedSearchParams()
873
899
  });
900
+ return serverActionResponse && markServerActionResponse(serverActionResponse);
874
901
  };
875
902
  let progressiveActionHandled = false;
876
903
  let progressiveActionFormState = null;
877
904
  const handleProgressiveAction = async (routeMatch, unrenderedRouteMatch) => {
878
905
  if (!filesystemRouteEligible || !isProgressiveActionRequest) return null;
879
906
  if (routeMatch && isAppRouteHandlerRoute(routeMatch.route)) return null;
880
- if (!options.handleProgressiveActionRequest) return unrenderedRouteMatch?.route.__loadPage && !unrenderedRouteMatch.route.__loadRouteHandler ? createMissingServerActionResponse(options, null) : null;
907
+ if (!options.handleProgressiveActionRequest) return unrenderedRouteMatch?.route.__loadPage && !unrenderedRouteMatch.route.__loadRouteHandler ? markServerActionResponse(createMissingServerActionResponse(options, null)) : null;
881
908
  const progressiveActionResult = await options.handleProgressiveActionRequest({
882
909
  actionId,
883
910
  cleanPathname,
@@ -886,7 +913,7 @@ async function handleAppRscRequest(options, request, preMiddlewareRequestContext
886
913
  request,
887
914
  routeMatch
888
915
  });
889
- if (progressiveActionResult instanceof Response) return progressiveActionResult;
916
+ if (progressiveActionResult instanceof Response) return markServerActionResponse(progressiveActionResult);
890
917
  if (progressiveActionResult?.kind !== "form-state") return null;
891
918
  if ("actionError" in progressiveActionResult && middlewareContext.status === null && progressiveActionHttpFallbackStatus(progressiveActionResult.actionError) === null) middlewareContext.status = 500;
892
919
  return progressiveActionResult;
@@ -1018,7 +1045,7 @@ async function handleAppRscRequest(options, request, preMiddlewareRequestContext
1018
1045
  filesystemRouteEligible = resolvedPathnameHasBasePath;
1019
1046
  const claimedRscCacheBustingRedirect = await validateClaimedOutsideBasePathRsc();
1020
1047
  if (claimedRscCacheBustingRedirect) return claimedRscCacheBustingRedirect;
1021
- const rewrittenImageEndpointResponse = serveImageEndpointIfMatched();
1048
+ const rewrittenImageEndpointResponse = await serveImageEndpointIfMatched();
1022
1049
  if (rewrittenImageEndpointResponse) return rewrittenImageEndpointResponse;
1023
1050
  const rewrittenMetadataResponse = await renderMetadataRouteIfMatched();
1024
1051
  if (rewrittenMetadataResponse) return composeResponseStageResponse(rewrittenMetadataResponse);
@@ -1066,7 +1093,7 @@ async function handleAppRscRequest(options, request, preMiddlewareRequestContext
1066
1093
  filesystemRouteEligible = resolvedPathnameHasBasePath;
1067
1094
  const claimedRscCacheBustingRedirect = await validateClaimedOutsideBasePathRsc();
1068
1095
  if (claimedRscCacheBustingRedirect) return claimedRscCacheBustingRedirect;
1069
- const rewrittenImageEndpointResponse = serveImageEndpointIfMatched();
1096
+ const rewrittenImageEndpointResponse = await serveImageEndpointIfMatched();
1070
1097
  if (rewrittenImageEndpointResponse) return rewrittenImageEndpointResponse;
1071
1098
  const rewrittenMetadataResponse = await renderMetadataRouteIfMatched();
1072
1099
  if (rewrittenMetadataResponse) return composeResponseStageResponse(rewrittenMetadataResponse);
@@ -1088,7 +1115,10 @@ async function handleAppRscRequest(options, request, preMiddlewareRequestContext
1088
1115
  if (interceptionCacheProofInvalidated && interceptionSourceContext !== null) {
1089
1116
  const finalInterceptionTargetPathname = cleanPathnameIsRequestPathname ? requestPathname : cleanPathname;
1090
1117
  const finalInterceptionSourceMatch = filesystemRouteEligible && match !== null ? options.matchInterceptRoute?.(finalInterceptionTargetPathname, interceptionSourceContext, interceptionIdHeader) ?? null : null;
1091
- const hasVerifiedFinalInterceptionSource = hasVerifiedInterceptionSource && interceptionSourceMatch !== null && provesConcreteInterceptionSource(finalInterceptionSourceMatch) && finalInterceptionSourceMatch?.route === interceptionSourceMatch.route && haveSamePageParams(finalInterceptionSourceMatch.params, interceptionSourceMatch.params);
1118
+ const hasVerifiedFinalInterceptionSource = hasVerifiedInterceptionSource && interceptionSourceMatch !== null && provesConcreteInterceptionSource(finalInterceptionSourceMatch) && finalInterceptionSourceMatch !== null && finalInterceptionSourceMatch.route === interceptionSourceMatch.route && haveSamePageParams(finalInterceptionSourceMatch.params, interceptionSourceMatch.params) && isSameInterceptionSource(finalInterceptionSourceMatch, {
1119
+ params: interceptionSourceMatch.authorizationParams ?? interceptionSourceMatch.params,
1120
+ route: interceptionSourceMatch.authorizationRoute ?? interceptionSourceMatch.route
1121
+ });
1092
1122
  if (interceptionIdHeader !== null && !hasVerifiedFinalInterceptionSource) {
1093
1123
  options.clearRequestContext();
1094
1124
  setInterceptionResponseUncacheable(true);
@@ -1291,7 +1321,7 @@ async function handleAppRscRequest(options, request, preMiddlewareRequestContext
1291
1321
  routePattern: route.pattern,
1292
1322
  routePathname: responseStageRoutePathname,
1293
1323
  scriptNonce: scriptNonce ?? null
1294
- }).then(composeResponseStageResponse) : await options.dispatchMatchedPage({
1324
+ }).then((response) => transportedResponseStage && isRscRequest ? withRoutedRscHeaders(response, resolveAppPageNavigationParams(route, params, cleanPathname), resolvedUrl, interceptionSourceContext !== null) : response).then(composeResponseStageResponse) : await options.dispatchMatchedPage({
1295
1325
  bypassInterceptionContextCache,
1296
1326
  cachePathname,
1297
1327
  cleanPathname,
@@ -1324,7 +1354,7 @@ async function handleAppRscRequest(options, request, preMiddlewareRequestContext
1324
1354
  searchParams: resolvedSearchParams,
1325
1355
  renderMode
1326
1356
  }), isRscRequest && request.headers.get("Next-Router-Prefetch") === "1" && request.headers.has("Next-Router-Segment-Prefetch"));
1327
- if (progressiveActionFormState) return applyProgressiveActionSideEffects(pageResponse, progressiveActionFormState);
1357
+ if (progressiveActionFormState) return markServerActionResponse(applyProgressiveActionSideEffects(pageResponse, progressiveActionFormState));
1328
1358
  return pageResponse;
1329
1359
  }
1330
1360
  function progressiveActionHttpFallbackStatus(actionError) {
@@ -1393,7 +1423,7 @@ function createAppRscRequestHandler(options) {
1393
1423
  let appRequest = rawRequest;
1394
1424
  if (pagesDataNormalization?.isDataReq) {
1395
1425
  const appRequestUrl = new URL(pagesDataNormalization.request.url);
1396
- appRequestUrl.pathname = addBasePathToPathname(appRequestUrl.pathname, options.basePath);
1426
+ appRequestUrl.pathname = restoreBasePath(appRequestUrl.pathname, options.basePath);
1397
1427
  appRequest = cloneRequestWithUrl(pagesDataCandidate, appRequestUrl.toString());
1398
1428
  }
1399
1429
  const request = cloneRequestWithHeaders(appRequest, filteredHeaders);
@@ -1436,6 +1466,7 @@ function createAppRscRequestHandler(options) {
1436
1466
  recordCacheability: dispatchResponseStage === void 0,
1437
1467
  requestContext: preMiddlewareRequestContext
1438
1468
  });
1469
+ if (serverActionResponses.has(response)) return withServerActionCacheControl(response);
1439
1470
  return interceptionResponseUncacheable || unmatchedRouteResponses.has(response) ? withNeverCacheControl(response) : response;
1440
1471
  }, {
1441
1472
  cacheComponents: options.createPprFallbackShells !== void 0,
@@ -1448,7 +1479,8 @@ function createAppRscRequestHandler(options) {
1448
1479
  await closeAfterResponse(requestContext);
1449
1480
  throw error;
1450
1481
  }
1451
- return closeAfterResponseWithBody(response, requestContext);
1482
+ const closedResponse = closeAfterResponseWithBody(response, requestContext);
1483
+ return isStreamedActionRedirectResponse(response) ? markStreamedActionRedirectResponse(closedResponse) : closedResponse;
1452
1484
  };
1453
1485
  const appRscHandler = async function appRscHandler(rawRequest, ctx, allowInternalRscDocumentFallback = false, dispatchResponseStage, responseStageProbeMode = null, transportedPrerenderState, detachedTrace = false) {
1454
1486
  options.registerCacheAdapters();
@@ -32,6 +32,9 @@ type FinalizeAppRscResponseOptions = {
32
32
  };
33
33
  /** Mark a response whose final target pipeline has already applied config headers. */
34
34
  export declare function markAppRscResponseConfigHeadersApplied(response: Response): Response;
35
+ /** Mark a Server Action redirect that streams its target's render, and so keeps its policy. */
36
+ export declare function markStreamedActionRedirectResponse(response: Response): Response;
37
+ export declare function isStreamedActionRedirectResponse(response: Response): boolean;
35
38
  /** Apply only the matching next.config headers for an App Router request. */
36
39
  export declare function applyAppRscConfigHeaders(headers: Headers, request: Request, options: FinalizeAppRscResponseOptions): Promise<void>;
37
40
  /**
@@ -25,6 +25,15 @@ function markAppRscResponseConfigHeadersApplied(response) {
25
25
  configHeadersAlreadyApplied.add(response);
26
26
  return response;
27
27
  }
28
+ const streamedActionRedirects = /* @__PURE__ */ new WeakSet();
29
+ /** Mark a Server Action redirect that streams its target's render, and so keeps its policy. */
30
+ function markStreamedActionRedirectResponse(response) {
31
+ streamedActionRedirects.add(response);
32
+ return response;
33
+ }
34
+ function isStreamedActionRedirectResponse(response) {
35
+ return streamedActionRedirects.has(response);
36
+ }
28
37
  /** Apply only the matching next.config headers for an App Router request. */
29
38
  async function applyAppRscConfigHeaders(headers, request, options) {
30
39
  if (!HAS_CONFIG_HEADERS || !options.configHeaders.length) return;
@@ -73,6 +82,7 @@ async function finalizeAppRscResponse(response, request, options) {
73
82
  if (!response.headers.has("Cache-Control")) {
74
83
  applyCdnResponseHeaders(response.headers, { cacheControl: "" });
75
84
  captureRouteCacheabilityResponsePolicy(captureCdnResponsePolicyHeaders(response.headers));
85
+ if (isStaticFileSignal(response)) response.headers.delete("Cache-Control");
76
86
  }
77
87
  if (configHeadersAlreadyApplied.has(response)) {
78
88
  normalizeExplicitNonCacheablePolicy(response.headers);
@@ -84,4 +94,4 @@ async function finalizeAppRscResponse(response, request, options) {
84
94
  return response;
85
95
  }
86
96
  //#endregion
87
- export { applyAppRscConfigHeaders, finalizeAppRscResponse, markAppRscResponseConfigHeadersApplied };
97
+ export { applyAppRscConfigHeaders, finalizeAppRscResponse, isStreamedActionRedirectResponse, markAppRscResponseConfigHeadersApplied, markStreamedActionRedirectResponse };