vinext 1.4.0 → 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 (156) hide show
  1. package/README.md +21 -21
  2. package/dist/build/action-owner-manifest.js +1 -0
  3. package/dist/build/prerender.js +3 -2
  4. package/dist/check.js +4 -7
  5. package/dist/client/dev-error-overlay-store.d.ts +1 -0
  6. package/dist/client/dev-error-overlay.d.ts +2 -0
  7. package/dist/client/dev-error-overlay.js +19 -3
  8. package/dist/config/config-matchers.js +4 -2
  9. package/dist/config/destination-params.d.ts +2 -1
  10. package/dist/config/destination-params.js +55 -2
  11. package/dist/config/next-config.d.ts +22 -1
  12. package/dist/config/next-config.js +49 -4
  13. package/dist/entries/app-rsc-entry.js +41 -19
  14. package/dist/entries/app-rsc-manifest.js +4 -0
  15. package/dist/entries/pages-server-entry.js +5 -1
  16. package/dist/index.js +44 -32
  17. package/dist/init-cloudflare.js +18 -2
  18. package/dist/plugins/css-target.d.ts +18 -0
  19. package/dist/plugins/css-target.js +93 -0
  20. package/dist/plugins/strip-server-exports.js +2 -1
  21. package/dist/routing/app-route-graph.d.ts +10 -0
  22. package/dist/routing/app-route-graph.js +15 -1
  23. package/dist/server/api-handler.d.ts +2 -0
  24. package/dist/server/api-handler.js +5 -6
  25. package/dist/server/app-action-forwarding.js +3 -1
  26. package/dist/server/app-browser-entry.js +28 -7
  27. package/dist/server/app-browser-rsc-redirect.js +3 -3
  28. package/dist/server/app-browser-server-action-client.js +2 -2
  29. package/dist/server/app-browser-visible-commit.js +2 -2
  30. package/dist/server/app-elements.d.ts +3 -0
  31. package/dist/server/app-elements.js +14 -1
  32. package/dist/server/app-fallback-renderer.js +2 -2
  33. package/dist/server/app-optimistic-routing.d.ts +12 -1
  34. package/dist/server/app-optimistic-routing.js +40 -19
  35. package/dist/server/app-page-boundary-render.js +7 -12
  36. package/dist/server/app-page-cache-render.js +1 -1
  37. package/dist/server/app-page-dispatch.d.ts +10 -2
  38. package/dist/server/app-page-dispatch.js +73 -31
  39. package/dist/server/app-page-element-builder.d.ts +9 -20
  40. package/dist/server/app-page-element-builder.js +40 -52
  41. package/dist/server/app-page-execution.js +9 -6
  42. package/dist/server/app-page-intercept-segment-config.d.ts +56 -0
  43. package/dist/server/app-page-intercept-segment-config.js +90 -0
  44. package/dist/server/app-page-navigation-params.d.ts +23 -0
  45. package/dist/server/app-page-navigation-params.js +49 -0
  46. package/dist/server/app-page-render.d.ts +5 -3
  47. package/dist/server/app-page-render.js +31 -17
  48. package/dist/server/app-page-request.d.ts +24 -2
  49. package/dist/server/app-page-request.js +10 -10
  50. package/dist/server/app-page-response.js +1 -1
  51. package/dist/server/app-page-route-wiring.d.ts +10 -0
  52. package/dist/server/app-page-route-wiring.js +34 -13
  53. package/dist/server/app-page-segment-plan.js +9 -1
  54. package/dist/server/app-page-segment-state.d.ts +9 -0
  55. package/dist/server/app-page-segment-state.js +54 -1
  56. package/dist/server/app-page-stream.d.ts +10 -8
  57. package/dist/server/app-page-stream.js +2 -1
  58. package/dist/server/app-request-stage-independent-entry.js +1 -1
  59. package/dist/server/app-response-stage-entry.js +1 -0
  60. package/dist/server/app-route-handler-execution.js +3 -2
  61. package/dist/server/app-route-handler-runtime.d.ts +1 -1
  62. package/dist/server/app-route-handler-runtime.js +11 -7
  63. package/dist/server/app-route-module-loader.d.ts +2 -0
  64. package/dist/server/app-route-module-loader.js +1 -0
  65. package/dist/server/app-router-entry.js +1 -1
  66. package/dist/server/app-rsc-cache-busting.d.ts +10 -10
  67. package/dist/server/app-rsc-cache-busting.js +27 -68
  68. package/dist/server/app-rsc-error-handler.js +1 -1
  69. package/dist/server/app-rsc-errors.js +4 -1
  70. package/dist/server/app-rsc-handler.d.ts +10 -1
  71. package/dist/server/app-rsc-handler.js +126 -103
  72. package/dist/server/app-rsc-request-normalization.d.ts +10 -12
  73. package/dist/server/app-rsc-request-normalization.js +11 -14
  74. package/dist/server/app-rsc-response-finalizer.d.ts +3 -0
  75. package/dist/server/app-rsc-response-finalizer.js +11 -1
  76. package/dist/server/app-rsc-response-stage.js +4 -12
  77. package/dist/server/app-rsc-route-matching.d.ts +14 -0
  78. package/dist/server/app-rsc-route-matching.js +42 -18
  79. package/dist/server/app-server-action-execution.js +24 -14
  80. package/dist/server/app-ssr-entry.d.ts +6 -0
  81. package/dist/server/app-ssr-entry.js +13 -8
  82. package/dist/server/app-worker-stages.d.ts +12 -3
  83. package/dist/server/app-worker-stages.js +37 -4
  84. package/dist/server/bfcache-identity.d.ts +7 -0
  85. package/dist/server/bfcache-identity.js +10 -1
  86. package/dist/server/cache-control.d.ts +2 -0
  87. package/dist/server/cache-control.js +3 -1
  88. package/dist/server/cacheability-manifest.js +1 -2
  89. package/dist/server/dev-origin-check.d.ts +2 -1
  90. package/dist/server/dev-origin-check.js +2 -1
  91. package/dist/server/dev-server.js +2 -1
  92. package/dist/server/image-local-patterns.d.ts +21 -0
  93. package/dist/server/image-local-patterns.js +59 -0
  94. package/dist/server/image-optimization.d.ts +28 -22
  95. package/dist/server/image-optimization.js +17 -2
  96. package/dist/server/isr-cache.js +4 -1
  97. package/dist/server/metadata-route-response.js +25 -7
  98. package/dist/server/middleware-matcher.js +19 -6
  99. package/dist/server/middleware-runtime.js +6 -2
  100. package/dist/server/multi-stage.d.ts +7 -3
  101. package/dist/server/nitro-public-files-plugin.js +20 -8
  102. package/dist/server/pages-data-route.d.ts +5 -1
  103. package/dist/server/pages-data-route.js +14 -6
  104. package/dist/server/pages-node-compat.d.ts +1 -1
  105. package/dist/server/pages-node-compat.js +2 -0
  106. package/dist/server/pages-page-data.js +2 -1
  107. package/dist/server/pages-page-handler.js +17 -6
  108. package/dist/server/pages-page-response.d.ts +9 -0
  109. package/dist/server/pages-page-response.js +18 -4
  110. package/dist/server/pages-request-pipeline.js +6 -5
  111. package/dist/server/pages-request-stage-entry.js +25 -4
  112. package/dist/server/pages-router-entry.js +3 -2
  113. package/dist/server/prod-server.d.ts +2 -2
  114. package/dist/server/prod-server.js +59 -17
  115. package/dist/server/request-error-log.d.ts +9 -0
  116. package/dist/server/request-error-log.js +13 -0
  117. package/dist/server/request-pipeline.d.ts +14 -4
  118. package/dist/server/request-pipeline.js +33 -15
  119. package/dist/server/response-stage-cacheability.d.ts +7 -0
  120. package/dist/server/response-stage-cacheability.js +6 -5
  121. package/dist/server/rsc-completion-metadata.js +3 -0
  122. package/dist/server/static-file-cache.d.ts +9 -0
  123. package/dist/server/static-file-cache.js +13 -2
  124. package/dist/server/worker-image-optimization.d.ts +2 -2
  125. package/dist/server/worker-image-optimization.js +18 -3
  126. package/dist/server/worker-utils.d.ts +8 -0
  127. package/dist/server/worker-utils.js +19 -2
  128. package/dist/shims/cache-request-state.d.ts +6 -6
  129. package/dist/shims/cache-request-state.js +6 -36
  130. package/dist/shims/cache-runtime.d.ts +3 -1
  131. package/dist/shims/cache-runtime.js +77 -47
  132. package/dist/shims/cache.d.ts +10 -5
  133. package/dist/shims/cache.js +35 -22
  134. package/dist/shims/client-hook-error.d.ts +2 -0
  135. package/dist/shims/client-hook-error.js +16 -1
  136. package/dist/shims/fetch-cache.js +6 -2
  137. package/dist/shims/headers.d.ts +2 -1
  138. package/dist/shims/headers.js +5 -3
  139. package/dist/shims/image-props.d.ts +6 -0
  140. package/dist/shims/image-props.js +39 -3
  141. package/dist/shims/image.js +3 -3
  142. package/dist/shims/internal/cache-context-accessor.d.ts +1 -1
  143. package/dist/shims/link.js +5 -10
  144. package/dist/shims/navigation.js +2 -3
  145. package/dist/shims/request-state-types.d.ts +2 -1
  146. package/dist/shims/slot.d.ts +9 -2
  147. package/dist/shims/slot.js +76 -11
  148. package/dist/shims/unified-request-context.d.ts +6 -0
  149. package/dist/shims/unified-request-context.js +1 -0
  150. package/dist/utils/base-path.d.ts +7 -0
  151. package/dist/utils/base-path.js +11 -1
  152. package/dist/utils/cache-life-profiles.d.ts +33 -0
  153. package/dist/utils/cache-life-profiles.js +111 -0
  154. package/dist/utils/middleware-request-headers.d.ts +14 -3
  155. package/dist/utils/middleware-request-headers.js +54 -7
  156. package/package.json +1 -1
@@ -1,4 +1,4 @@
1
- import { CacheLifeConfig } from "../cache-request-state.js";
1
+ import { CacheLifeConfig } from "../../utils/cache-life-profiles.js";
2
2
  //#region src/shims/internal/cache-context-accessor.d.ts
3
3
  type CacheContextLike = {
4
4
  tags: string[];
@@ -149,7 +149,7 @@ function getLinkPrefetchRouterMode() {
149
149
  /**
150
150
  * Prefetch a URL for faster navigation.
151
151
  *
152
- * For App Router (RSC): fetches the .rsc payload in the background and
152
+ * For App Router (RSC): fetches the RSC payload in the background and
153
153
  * stores it in an in-memory cache for instant use during navigation.
154
154
  * For Pages Router: warms the page chunk, prefetches data only for SSG pages,
155
155
  * and falls back to a document prefetch hint when no page loader matches.
@@ -381,7 +381,7 @@ function prefetchUrl(href, mode, priority = "low", pagesRouteHref, locale, owner
381
381
  async function promotePrefetchEntriesForNavigation(href) {
382
382
  if (typeof window === "undefined") return;
383
383
  if (!hasLinkAppNavigationRuntime()) return;
384
- const [{ getMountedSlotsHeader, getPrefetchCache, getPrefetchInterceptionContext, promotePrefetchCacheEntryForNavigation }, { parseRscRequestPath, stripRscCacheBustingSearchParam, stripRscSuffix }] = await Promise.all([loadNavigationModule(), loadAppRscCacheBustingModule()]);
384
+ const [{ getMountedSlotsHeader, getPrefetchCache, getPrefetchInterceptionContext, promotePrefetchCacheEntryForNavigation }, { parseRscRequestPath, stripRscCacheBustingSearchParam }] = await Promise.all([loadNavigationModule(), loadAppRscCacheBustingModule()]);
385
385
  let target;
386
386
  const fullHref = toCanonicalBrowserNavigationHref(href, window.location.href, __basePath, __trailingSlash);
387
387
  try {
@@ -405,7 +405,7 @@ async function promotePrefetchEntriesForNavigation(href) {
405
405
  continue;
406
406
  }
407
407
  stripRscCacheBustingSearchParam(cached);
408
- if (stripRscSuffix(cached.pathname) === target.pathname && cached.search === target.search) promotePrefetchCacheEntryForNavigation(cacheKey, entry);
408
+ if (cached.pathname === target.pathname && cached.search === target.search) promotePrefetchCacheEntryForNavigation(cacheKey, entry);
409
409
  }
410
410
  }
411
411
  /**
@@ -627,16 +627,11 @@ const Link = forwardRef(function Link({ href, as, replace = false, prefetch: pre
627
627
  })
628
628
  });
629
629
  const [pending, setPending] = useState(false);
630
- const mountedRef = useRef(true);
631
630
  const pendingPagesIntentPrefetchRef = useRef(null);
632
- const setPendingRef = useRef((next) => {
633
- if (mountedRef.current) setPending(next);
634
- });
631
+ const setPendingRef = useRef(setPending);
635
632
  useEffect(() => {
636
- mountedRef.current = true;
637
633
  const setter = setPendingRef.current;
638
634
  return () => {
639
- mountedRef.current = false;
640
635
  pendingPagesIntentPrefetchRef.current?.();
641
636
  pendingPagesIntentPrefetchRef.current = null;
642
637
  if (setter) clearLinkForCurrentNavigation(setter);
@@ -802,7 +797,7 @@ const Link = forwardRef(function Link({ href, as, replace = false, prefetch: pre
802
797
  setPending(true);
803
798
  React.startTransition(() => {
804
799
  navigateClientSide(navigateHref, replace ? "replace" : "push", scroll, false).finally(() => {
805
- if (mountedRef.current) setPending(false);
800
+ setPending(false);
806
801
  if (setter) clearLinkForCurrentNavigation(setter);
807
802
  });
808
803
  });
@@ -11,7 +11,7 @@ import { resolveDirectHybridClientRouteOwner } from "./internal/hybrid-client-ro
11
11
  import { isHashOnlyBrowserUrlChange, toCanonicalBrowserNavigationHref, toSameOriginAppPath, withBasePath } from "./url-utils.js";
12
12
  import { disableSmoothScrollDuringRouteTransition } from "./disable-smooth-scroll.js";
13
13
  import { retryScrollTo, scrollToHashTarget } from "./hash-scroll.js";
14
- import { VINEXT_RSC_COMPATIBILITY_ID_HEADER, VINEXT_RSC_CONTENT_TYPE, createRscRequestHeaders, createRscRequestUrl, formatRscRequestPath, parseRscRequestPath, stripRscCacheBustingSearchParam, stripRscSuffix } from "../server/app-rsc-cache-busting.js";
14
+ import { VINEXT_RSC_COMPATIBILITY_ID_HEADER, VINEXT_RSC_CONTENT_TYPE, createRscRequestHeaders, createRscRequestUrl, formatRscRequestPath, parseRscRequestPath, stripRscCacheBustingSearchParam } from "../server/app-rsc-cache-busting.js";
15
15
  import { clearAppNavigationFailureTarget, stageAppNavigationFailureTarget } from "../client/app-nav-failure-handler.js";
16
16
  import { notifyAppRouterTransitionStart } from "../client/instrumentation-client-state.js";
17
17
  import { getNavigationRuntime, hasAppNavigationRuntime, loadNavigationRuntimeRouteManifest } from "../client/navigation-runtime.js";
@@ -245,8 +245,7 @@ function normalizeRscCacheLookupUrl(rscUrl) {
245
245
  }
246
246
  function normalizeRscCacheLookupPathname(rscUrl) {
247
247
  try {
248
- const url = parseRscRequestPath(rscUrl);
249
- return stripRscSuffix(url.pathname);
248
+ return parseRscRequestPath(rscUrl).pathname;
250
249
  } catch {
251
250
  return null;
252
251
  }
@@ -1,3 +1,4 @@
1
+ import { CacheLifeConfig } from "../utils/cache-life-profiles.js";
1
2
  import { NavigationContext } from "./navigation-context-state.js";
2
3
  import "./navigation.js";
3
4
  import { ExecutionContextLike } from "./request-context.js";
@@ -6,7 +7,7 @@ import { FetchCacheState } from "./fetch-cache-state.js";
6
7
  import "./fetch-cache.js";
7
8
  import { HeadersAccessPhase, VinextHeadersShimState } from "./internal/headers-state.js";
8
9
  import { HeadersContext } from "./headers.js";
9
- import { CacheLifeConfig, CacheState } from "./cache-request-state.js";
10
+ import { CacheState } from "./cache-request-state.js";
10
11
  import { RouterState, SSRContext } from "./router-state.js";
11
12
  import { PrivateCacheState } from "./cache-runtime.js";
12
13
  import { HeadState } from "./head-state.js";
@@ -28,10 +28,13 @@ export declare function setAppElementsRenderedSearch(elements: AppElements, sear
28
28
  * that arrives after the head (initial hydration) is still seen.
29
29
  */
30
30
  export declare function bindAppElementsRenderedSearch(elements: AppElements, snapshot: Pick<ClientNavigationRenderSnapshot, "renderedSearch" | "search">): void;
31
+ export declare function isBfcacheIdentityRetained(identity: string): boolean;
31
32
  export declare const BfcacheIdentityMapContext: React$1.Context<Readonly<Record<string, string>>>;
32
33
  export type BfcacheSlotEntry = {
33
34
  content: React$1.ReactNode;
34
35
  elements?: AppElements;
36
+ /** Every entry key inside a hidden entry, its own included. */
37
+ retainedKeys?: readonly string[];
35
38
  segmentId?: string;
36
39
  stateKey: string;
37
40
  stateKeyMap?: Readonly<Record<string, string>>;
@@ -40,6 +43,7 @@ type MergeElementsOptions = {
40
43
  clearAbsentSlots?: boolean;
41
44
  preserveAbsentSlots?: boolean;
42
45
  preserveElementIds?: readonly string[];
46
+ preserveElementSeedIds?: readonly string[];
43
47
  preservePreviousSlotIds?: readonly string[];
44
48
  };
45
49
  export declare function updateBfcacheSlotEntryOrder(previousOrder: readonly string[], activeStateKey: string, maxEntries?: number): string[];
@@ -67,9 +71,12 @@ export declare function BfcacheSegmentBoundary({ children, id, stateKey }: {
67
71
  * the parent's layout router, above the child's boundaries, so a page left for a
68
72
  * sibling with its own layout keeps its state when navigating back.
69
73
  */
70
- export declare function BfcacheChildrenBoundary({ children, id }: {
74
+ export declare function BfcacheChildrenBoundary({ children, id, stateKey }: {
71
75
  children: React$1.ReactNode;
72
- id: string;
76
+ /** The child element whose identity keys the entries. */
77
+ id?: string;
78
+ /** A fixed key, for a segment level with no element of its own. */
79
+ stateKey?: string;
73
80
  }): React$1.JSX.Element;
74
81
  export declare function getNonCacheComponentsSegmentKey(id: string, activeStateKey: string): string | undefined;
75
82
  export declare function resolveBfcacheSegmentStateKey(id: string, identityMap: Readonly<Record<string, string>>, bfcacheIdMap: Readonly<Record<string, string>> | null): string | undefined;
@@ -1,6 +1,6 @@
1
1
  "use client";
2
2
  import { APP_BFCACHE_SEGMENT_IDENTITIES_KEY, AppElementsWire, UNMATCHED_SLOT } from "../server/app-elements-wire.js";
3
- import "../server/app-elements.js";
3
+ import { readOptimisticStateKeyRebinder } from "../server/app-elements.js";
4
4
  import { isNestedBfcacheSlotSegmentId, isNestedBfcacheSlotSegmentIdFor } from "../server/bfcache-identity.js";
5
5
  import { notFound } from "./navigation-errors.js";
6
6
  import { getBfcacheIdMapContext, getBfcacheSegmentIdContext } from "./navigation-context-state.js";
@@ -51,6 +51,25 @@ const BfcacheSegmentIdContext = getBfcacheSegmentIdContext();
51
51
  const EMPTY_BFCACHE_STATE_KEYS = Object.freeze({});
52
52
  const MAX_BFCACHE_SLOT_ENTRIES_WITH_CACHE_COMPONENTS = 3;
53
53
  const MAX_BFCACHE_SLOT_ENTRIES_WITHOUT_CACHE_COMPONENTS = 1;
54
+ const rootBfcacheRetainedKeysReports = /* @__PURE__ */ new Map();
55
+ function isBfcacheIdentityRetained(identity) {
56
+ return hasBfcacheRetainedKey(rootBfcacheRetainedKeysReports, identity);
57
+ }
58
+ function hasBfcacheRetainedKey(reports, identity) {
59
+ for (const report of reports.values()) {
60
+ if (report.keys.includes(identity) || report.hiddenKeys.includes(identity)) return true;
61
+ if (hasBfcacheRetainedKey(report.activeReports, identity)) return true;
62
+ }
63
+ return false;
64
+ }
65
+ const BfcacheRetainedKeysContext = React$1.createContext(rootBfcacheRetainedKeysReports);
66
+ function collectBfcacheRetainedKeys(reports, keys) {
67
+ for (const report of reports.values()) {
68
+ for (const key of report.keys) keys.add(key);
69
+ for (const key of report.hiddenKeys) keys.add(key);
70
+ collectBfcacheRetainedKeys(report.activeReports, keys);
71
+ }
72
+ }
54
73
  const BfcacheIdentityMapContext = React$1.createContext(EMPTY_BFCACHE_STATE_KEYS);
55
74
  function isCacheComponentsEnabled() {
56
75
  return String(process.env.__NEXT_CACHE_COMPONENTS) === "true";
@@ -136,34 +155,71 @@ function warnTransportMetadataEntry(id) {
136
155
  * falling back to the live boundary values for entries that predate per-entry
137
156
  * capture.
138
157
  */
139
- function BfcacheEntryProviders({ entry, fallbackElements, fallbackSegmentId, fallbackStateKeyMap, SegmentContext }) {
158
+ function BfcacheEntryProviders({ entry, fallbackElements, fallbackSegmentId, fallbackStateKeyMap, retainedKeysReports, SegmentContext }) {
140
159
  return /* @__PURE__ */ jsx(BfcacheIdentityMapContext.Provider, {
141
160
  value: entry.stateKeyMap ?? fallbackStateKeyMap,
142
161
  children: /* @__PURE__ */ jsx(ElementsContext.Provider, {
143
162
  value: entry.elements ?? fallbackElements,
144
163
  children: /* @__PURE__ */ jsx(SegmentContext.Provider, {
145
164
  value: entry.segmentId ?? fallbackSegmentId,
146
- children: entry.content
165
+ children: /* @__PURE__ */ jsx(BfcacheRetainedKeysContext.Provider, {
166
+ value: retainedKeysReports,
167
+ children: entry.content
168
+ })
147
169
  })
148
170
  })
149
171
  });
150
172
  }
151
- function useBfcacheSlotEntries(activeEntry) {
173
+ function useBfcacheSlotEntries(listId, activeEntry) {
152
174
  const snapshotsByStateKey = React$1.useRef(/* @__PURE__ */ new Map());
153
175
  const [entryOrder, setEntryOrder] = React$1.useState(() => [activeEntry.stateKey]);
176
+ const [retainedKeysReportsByStateKey] = React$1.useState(() => /* @__PURE__ */ new Map());
177
+ const getRetainedKeysReports = (stateKey) => {
178
+ let reports = retainedKeysReportsByStateKey.get(stateKey);
179
+ if (!reports) {
180
+ reports = /* @__PURE__ */ new Map();
181
+ retainedKeysReportsByStateKey.set(stateKey, reports);
182
+ }
183
+ return reports;
184
+ };
154
185
  const committedSnapshots = snapshotsByStateKey.current;
155
186
  const staged = stageBfcacheSlotEntryForRender(committedSnapshots, entryOrder, activeEntry);
187
+ for (const [index, entry] of staged.entries.entries()) {
188
+ if (entry.stateKey === activeEntry.stateKey || entry.retainedKeys) continue;
189
+ const retainedKeys = /* @__PURE__ */ new Set([entry.stateKey]);
190
+ collectBfcacheRetainedKeys(getRetainedKeysReports(entry.stateKey), retainedKeys);
191
+ const hiddenEntry = {
192
+ ...entry,
193
+ retainedKeys: [...retainedKeys]
194
+ };
195
+ staged.entries[index] = hiddenEntry;
196
+ staged.snapshots.set(entry.stateKey, hiddenEntry);
197
+ }
156
198
  const nextOrder = staged.order;
157
199
  const orderChanged = !haveSameBfcacheSlotEntryOrder(entryOrder, nextOrder);
158
200
  React$1.useLayoutEffect(() => {
159
201
  snapshotsByStateKey.current = staged.snapshots;
160
- }, [staged.snapshots]);
202
+ for (const stateKey of retainedKeysReportsByStateKey.keys()) if (!staged.snapshots.has(stateKey)) retainedKeysReportsByStateKey.delete(stateKey);
203
+ }, [retainedKeysReportsByStateKey, staged.snapshots]);
204
+ const hiddenKeys = [...new Set(staged.entries.flatMap((entry) => entry.retainedKeys ?? []))];
205
+ const enclosingReports = React$1.useContext(BfcacheRetainedKeysContext);
206
+ const report = {
207
+ activeReports: getRetainedKeysReports(activeEntry.stateKey),
208
+ hiddenKeys,
209
+ keys: staged.entries.map((entry) => entry.stateKey)
210
+ };
211
+ React$1.useLayoutEffect(() => {
212
+ enclosingReports.set(listId, report);
213
+ });
161
214
  if (process.env.NODE_ENV !== "production" && !staged.snapshots.has(activeEntry.stateKey)) throw new Error("BFCache Activity slot is missing the active entry snapshot");
162
215
  if (orderChanged) setEntryOrder(nextOrder);
163
- return staged.entries;
216
+ return {
217
+ entries: staged.entries,
218
+ getRetainedKeysReports
219
+ };
164
220
  }
165
221
  function BfcacheActivitySlotBoundary({ activeStateKey, content, elements, id, SegmentContext, stateKeyMap }) {
166
- const renderEntries = useBfcacheSlotEntries({
222
+ const { entries: renderEntries, getRetainedKeysReports } = useBfcacheSlotEntries(id, {
167
223
  content,
168
224
  elements,
169
225
  segmentId: id,
@@ -177,6 +233,7 @@ function BfcacheActivitySlotBoundary({ activeStateKey, content, elements, id, Se
177
233
  fallbackElements: elements,
178
234
  fallbackSegmentId: id,
179
235
  fallbackStateKeyMap: stateKeyMap,
236
+ retainedKeysReports: getRetainedKeysReports(entry.stateKey),
180
237
  SegmentContext
181
238
  })
182
239
  }, entry.stateKey)) });
@@ -213,21 +270,23 @@ function BfcacheSegmentBoundary({ children, id, stateKey }) {
213
270
  * the parent's layout router, above the child's boundaries, so a page left for a
214
271
  * sibling with its own layout keeps its state when navigating back.
215
272
  */
216
- function BfcacheChildrenBoundary({ children, id }) {
273
+ function BfcacheChildrenBoundary({ children, id, stateKey }) {
217
274
  if (!BfcacheIdMapContext || !BfcacheSegmentIdContext || !isCacheComponentsEnabled()) return /* @__PURE__ */ jsx(Fragment$1, { children });
218
275
  return /* @__PURE__ */ jsx(BfcacheChildrenActivityBoundary, {
219
276
  id,
220
277
  IdMapContext: BfcacheIdMapContext,
221
278
  SegmentContext: BfcacheSegmentIdContext,
279
+ stateKey,
222
280
  children
223
281
  });
224
282
  }
225
- function BfcacheChildrenActivityBoundary({ children, id, IdMapContext, SegmentContext }) {
283
+ function BfcacheChildrenActivityBoundary({ children, id, IdMapContext, SegmentContext, stateKey }) {
226
284
  const elements = React$1.useContext(ElementsContext);
227
285
  const identityMap = React$1.useContext(BfcacheIdentityMapContext);
228
286
  const bfcacheIdMap = React$1.useContext(IdMapContext);
229
287
  const segmentId = React$1.useContext(SegmentContext);
230
- const activeStateKey = resolveBfcacheSegmentStateKey(id, identityMap, bfcacheIdMap);
288
+ const rebindStateKey = readOptimisticStateKeyRebinder(elements);
289
+ const activeStateKey = stateKey !== void 0 ? rebindStateKey?.(stateKey) ?? stateKey : id === void 0 ? void 0 : resolveBfcacheSegmentStateKey(id, identityMap, bfcacheIdMap);
231
290
  if (segmentId === null || activeStateKey === void 0) return /* @__PURE__ */ jsx(Fragment$1, { children });
232
291
  return /* @__PURE__ */ jsx(BfcacheActivitySlotBoundary, {
233
292
  activeStateKey,
@@ -272,6 +331,7 @@ function mergeElements(prev, next, options = {}) {
272
331
  const clearAbsentSlots = typeof options === "boolean" ? options : options.clearAbsentSlots ?? false;
273
332
  const preserveAbsentSlots = typeof options === "boolean" ? !options : options.preserveAbsentSlots ?? true;
274
333
  const preserveElementIds = typeof options === "boolean" ? [] : options.preserveElementIds ?? [];
334
+ const preserveElementSeedIds = typeof options === "boolean" ? [] : options.preserveElementSeedIds ?? [];
275
335
  const preservePreviousSlotIds = typeof options === "boolean" ? [] : options.preservePreviousSlotIds ?? [];
276
336
  const merged = { ...next };
277
337
  const preservedIdentityIds = /* @__PURE__ */ new Set();
@@ -282,6 +342,11 @@ function mergeElements(prev, next, options = {}) {
282
342
  preservedIdentityIds.add(id);
283
343
  }
284
344
  }
345
+ for (const id of preserveElementSeedIds) {
346
+ if (preservedIdentityIds.has(id) || !Object.hasOwn(prev, id)) continue;
347
+ const value = prev[id];
348
+ if (value !== void 0) merged[id] = value;
349
+ }
285
350
  const slotKeys = new Set([...Object.keys(prev), ...Object.keys(next)].filter((key) => AppElementsWire.isSlotId(key) && !isNestedBfcacheSlotSegmentId(key)));
286
351
  if (clearAbsentSlots) {
287
352
  for (const key of slotKeys) if (!Object.hasOwn(next, key)) delete merged[key];
@@ -368,4 +433,4 @@ function ParallelSlot({ name }) {
368
433
  return React$1.useContext(ParallelSlotsContext)?.[name] ?? null;
369
434
  }
370
435
  //#endregion
371
- export { BfcacheChildrenBoundary, BfcacheIdentityMapContext, BfcacheSegmentBoundary, Children, ChildrenContext, ElementsContext, ParallelSlot, ParallelSlotsContext, RenderedSearchContext, Slot, UNMATCHED_SLOT, bindAppElementsRenderedSearch, getNonCacheComponentsSegmentKey, mergeElements, resolveBfcacheSegmentStateKey, setAppElementsRenderedSearch, stageBfcacheSlotEntryForRender, updateBfcacheSlotEntryOrder };
436
+ export { BfcacheChildrenBoundary, BfcacheIdentityMapContext, BfcacheSegmentBoundary, Children, ChildrenContext, ElementsContext, ParallelSlot, ParallelSlotsContext, RenderedSearchContext, Slot, UNMATCHED_SLOT, bindAppElementsRenderedSearch, getNonCacheComponentsSegmentKey, isBfcacheIdentityRetained, mergeElements, resolveBfcacheSegmentStateKey, setAppElementsRenderedSearch, stageBfcacheSlotEntryForRender, updateBfcacheSlotEntryOrder };
@@ -30,6 +30,12 @@ export type UnifiedRequestContext = {
30
30
  requestCache: WeakMap<(...args: any[]) => any, unknown>;
31
31
  /** Shared lifecycle state for work deferred until the response closes. */
32
32
  afterContext: AfterRequestContext;
33
+ /**
34
+ * Draft-mode state of the headers context this request last cleared. The
35
+ * render can outlive its headers context (cleared once the response body
36
+ * closes), and so do after() callbacks; cache bypasses keep reading this.
37
+ */
38
+ draftModeAtHeadersClear: boolean;
33
39
  } & VinextHeadersShimState & I18nState & NavigationState & CacheState & PrivateCacheState & FetchCacheState & RouterState & HeadState & RootParamsState;
34
40
  export type AfterRequestContext = {
35
41
  callbacks: Array<() => unknown>;
@@ -53,6 +53,7 @@ function createRequestContext(opts) {
53
53
  executionContext: _getInheritedExecutionContext(),
54
54
  requestSignal: null,
55
55
  requestCache: /* @__PURE__ */ new WeakMap(),
56
+ draftModeAtHeadersClear: false,
56
57
  afterContext: {
57
58
  callbacks: [],
58
59
  responseClosed: false,
@@ -21,6 +21,13 @@ export declare function stripBasePath(pathname: string, basePath: string): strin
21
21
  * this pathname-only helper.
22
22
  */
23
23
  export declare function addBasePathToPathname(pathname: string, basePath: string | undefined): string;
24
+ /**
25
+ * Re-add a basePath that `stripBasePath` removed. Unlike
26
+ * `addBasePathToPathname`, this always prepends it: a stripped route whose
27
+ * first segment equals the basePath (basePath "/docs", route "/docs/a") still
28
+ * starts with the basePath and must not be mistaken for the original URL.
29
+ */
30
+ export declare function restoreBasePath(pathname: string, basePath: string | undefined): string;
24
31
  /**
25
32
  * Remove trailing slashes from a pathname while preserving the root "/".
26
33
  * Collapses any number of trailing slashes ("/a//" → "/a"). Used by the
@@ -31,6 +31,16 @@ function addBasePathToPathname(pathname, basePath) {
31
31
  return pathname === "/" ? basePath : `${basePath}${pathname}`;
32
32
  }
33
33
  /**
34
+ * Re-add a basePath that `stripBasePath` removed. Unlike
35
+ * `addBasePathToPathname`, this always prepends it: a stripped route whose
36
+ * first segment equals the basePath (basePath "/docs", route "/docs/a") still
37
+ * starts with the basePath and must not be mistaken for the original URL.
38
+ */
39
+ function restoreBasePath(pathname, basePath) {
40
+ if (!basePath) return pathname;
41
+ return pathname === "/" ? basePath : `${basePath}${pathname}`;
42
+ }
43
+ /**
34
44
  * Remove trailing slashes from a pathname while preserving the root "/".
35
45
  * Collapses any number of trailing slashes ("/a//" → "/a"). Used by the
36
46
  * trailing-slash redirect path and route pattern normalization.
@@ -42,4 +52,4 @@ function removeTrailingSlash(pathname) {
42
52
  return end === 0 ? "/" : pathname.slice(0, end);
43
53
  }
44
54
  //#endregion
45
- export { addBasePathToPathname, hasBasePath, removeTrailingSlash, stripBasePath };
55
+ export { addBasePathToPathname, hasBasePath, removeTrailingSlash, restoreBasePath, stripBasePath };
@@ -0,0 +1,33 @@
1
+ //#region src/utils/cache-life-profiles.d.ts
2
+ export type CacheLifeConfig = {
3
+ stale?: number;
4
+ revalidate?: number;
5
+ expire?: number;
6
+ };
7
+ /** Next.js's built-in profiles (`defaultConfig.cacheLife` in config-shared.ts). */
8
+ export declare const BUILT_IN_CACHE_LIFE_PROFILES: Readonly<Record<string, Readonly<CacheLifeConfig>>>;
9
+ /**
10
+ * Merge next.config `cacheLife` profiles over the built-ins, as Next.js does
11
+ * in `assignDefaultsAndValidate` (server/config.ts): a user profile replaces
12
+ * the built-in of the same name whole, and only `default` gets its missing
13
+ * fields filled: `stale` from `staleTimes.static`, `revalidate` from the
14
+ * built-in, `expire` from `expireTime`. That includes the built-in `default`,
15
+ * whose `stale` is unset in Next.js's defaults too. The other profiles keep
16
+ * their gaps, which the "use cache" store resolves from `default`.
17
+ *
18
+ * Next.js only schema-checks these profiles (fields must be numbers) and
19
+ * warns; `revalidate > expire` is checked for `cacheLife({...})` calls, not
20
+ * for config profiles.
21
+ */
22
+ export declare function resolveCacheLifeProfiles(userProfiles: unknown, defaults: {
23
+ expireTime: number;
24
+ staticStaleTime: number;
25
+ }, warn: (message: string) => void): Record<string, CacheLifeConfig>;
26
+ /** Serialize resolved profiles for the `__VINEXT_CACHE_LIFE_PROFILES` define. */
27
+ export declare function serializeCacheLifeProfiles(profiles: Record<string, CacheLifeConfig>): string;
28
+ /**
29
+ * Read the `__VINEXT_CACHE_LIFE_PROFILES` define. When absent, the profiles a
30
+ * next.config without `cacheLife` resolves to.
31
+ */
32
+ export declare function parseCacheLifeProfiles(serialized: string | undefined): Record<string, CacheLifeConfig>;
33
+ //#endregion
@@ -0,0 +1,111 @@
1
+ import { isUnknownRecord } from "./record.js";
2
+ //#region src/utils/cache-life-profiles.ts
3
+ /** Next.js's built-in profiles (`defaultConfig.cacheLife` in config-shared.ts). */
4
+ const BUILT_IN_CACHE_LIFE_PROFILES = {
5
+ default: {
6
+ revalidate: 900,
7
+ expire: 4294967294
8
+ },
9
+ seconds: {
10
+ stale: 30,
11
+ revalidate: 1,
12
+ expire: 60
13
+ },
14
+ minutes: {
15
+ stale: 300,
16
+ revalidate: 60,
17
+ expire: 3600
18
+ },
19
+ hours: {
20
+ stale: 300,
21
+ revalidate: 3600,
22
+ expire: 86400
23
+ },
24
+ days: {
25
+ stale: 300,
26
+ revalidate: 86400,
27
+ expire: 604800
28
+ },
29
+ weeks: {
30
+ stale: 300,
31
+ revalidate: 604800,
32
+ expire: 2592e3
33
+ },
34
+ max: {
35
+ stale: 300,
36
+ revalidate: 2592e3,
37
+ expire: 31536e3
38
+ }
39
+ };
40
+ const CACHE_LIFE_FIELDS = [
41
+ "stale",
42
+ "revalidate",
43
+ "expire"
44
+ ];
45
+ function copyBuiltInProfiles() {
46
+ const profiles = {};
47
+ for (const [name, profile] of Object.entries(BUILT_IN_CACHE_LIFE_PROFILES)) profiles[name] = { ...profile };
48
+ return profiles;
49
+ }
50
+ /**
51
+ * Merge next.config `cacheLife` profiles over the built-ins, as Next.js does
52
+ * in `assignDefaultsAndValidate` (server/config.ts): a user profile replaces
53
+ * the built-in of the same name whole, and only `default` gets its missing
54
+ * fields filled: `stale` from `staleTimes.static`, `revalidate` from the
55
+ * built-in, `expire` from `expireTime`. That includes the built-in `default`,
56
+ * whose `stale` is unset in Next.js's defaults too. The other profiles keep
57
+ * their gaps, which the "use cache" store resolves from `default`.
58
+ *
59
+ * Next.js only schema-checks these profiles (fields must be numbers) and
60
+ * warns; `revalidate > expire` is checked for `cacheLife({...})` calls, not
61
+ * for config profiles.
62
+ */
63
+ function resolveCacheLifeProfiles(userProfiles, defaults, warn) {
64
+ const profiles = copyBuiltInProfiles();
65
+ for (const [name, rawProfile] of Object.entries(isUnknownRecord(userProfiles) ? userProfiles : {})) {
66
+ if (!isUnknownRecord(rawProfile)) {
67
+ warn(`[vinext] Invalid next.config option \`cacheLife.${name}\`: expected an object.`);
68
+ continue;
69
+ }
70
+ const profile = {};
71
+ for (const field of CACHE_LIFE_FIELDS) {
72
+ const value = rawProfile[field];
73
+ if (value === void 0) continue;
74
+ if (typeof value !== "number" || Number.isNaN(value)) {
75
+ warn(`[vinext] Invalid next.config option \`cacheLife.${name}.${field}\`: expected a number of seconds.`);
76
+ continue;
77
+ }
78
+ profile[field] = value;
79
+ }
80
+ profiles[name] = profile;
81
+ }
82
+ const defaultProfile = profiles.default;
83
+ defaultProfile.stale ??= defaults.staticStaleTime;
84
+ defaultProfile.revalidate ??= BUILT_IN_CACHE_LIFE_PROFILES.default.revalidate;
85
+ defaultProfile.expire ??= defaults.expireTime;
86
+ return profiles;
87
+ }
88
+ function encodeNonFiniteNumber(_key, value) {
89
+ return value === Infinity || value === -Infinity ? String(value) : value;
90
+ }
91
+ function decodeNonFiniteNumber(_key, value) {
92
+ return value === "Infinity" || value === "-Infinity" ? Number(value) : value;
93
+ }
94
+ /** Serialize resolved profiles for the `__VINEXT_CACHE_LIFE_PROFILES` define. */
95
+ function serializeCacheLifeProfiles(profiles) {
96
+ return JSON.stringify(profiles, encodeNonFiniteNumber);
97
+ }
98
+ /**
99
+ * Read the `__VINEXT_CACHE_LIFE_PROFILES` define. When absent, the profiles a
100
+ * next.config without `cacheLife` resolves to.
101
+ */
102
+ function parseCacheLifeProfiles(serialized) {
103
+ if (!serialized) {
104
+ const profiles = copyBuiltInProfiles();
105
+ profiles.default.stale = 300;
106
+ return profiles;
107
+ }
108
+ return JSON.parse(serialized, decodeNonFiniteNumber);
109
+ }
110
+ //#endregion
111
+ export { BUILT_IN_CACHE_LIFE_PROFILES, parseCacheLifeProfiles, resolveCacheLifeProfiles, serializeCacheLifeProfiles };
@@ -7,7 +7,17 @@ type MiddlewareHeaderSource = Headers | Record<string, MiddlewareHeaderValue>;
7
7
  * under their literal protocol-header names on both the request and response.
8
8
  */
9
9
  export declare function getUnconsumedMiddlewareRequestHeaders(source: MiddlewareHeaderSource): Map<string, string>;
10
- /** Whether middleware changed the headers seen by downstream application code. */
10
+ /**
11
+ * `baseHeaders` with only Next.js's generic merge of ordinary middleware
12
+ * response headers applied, without the request-header protocol.
13
+ */
14
+ export declare function applyMiddlewareResponseHeadersToRequest(baseHeaders: Headers, middlewareHeaders: MiddlewareHeaderSource): Headers;
15
+ /**
16
+ * Whether middleware changed the headers seen by downstream application code
17
+ * through its request-header protocol. Ordinary response headers are copied to
18
+ * the request as well, but like a client's own headers they do not opt a
19
+ * render out of the shared response stage.
20
+ */
11
21
  export declare function hasMiddlewareRequestHeaderOverrides(source: MiddlewareHeaderSource | null): boolean;
12
22
  export declare function encodeMiddlewareRequestHeaders(targetHeaders: Headers, requestHeaders: Headers): void;
13
23
  /**
@@ -16,8 +26,9 @@ export declare function encodeMiddlewareRequestHeaders(targetHeaders: Headers, r
16
26
  * middleware. Never re-add absent headers from the base request — that would
17
27
  * resurrect credentials the app explicitly stripped before an external
18
28
  * rewrite. Next.js treats the empty value emitted for `new Headers()` as no
19
- * override. Any unconsumed `x-middleware-request-*` values are subsequently
20
- * copied to the request under their literal protocol-header names.
29
+ * override. Any unconsumed `x-middleware-request-*` values and every other
30
+ * truthy middleware response header are subsequently copied to the request
31
+ * under their literal names, replacing values the client sent.
21
32
  */
22
33
  export declare function buildRequestHeadersFromMiddlewareResponse(baseHeaders: Headers, middlewareHeaders: MiddlewareHeaderSource): Headers | null;
23
34
  export declare function shouldKeepMiddlewareHeader(key: string): boolean;
@@ -34,7 +34,52 @@ function getUnconsumedMiddlewareRequestHeaders(source) {
34
34
  for (const [key, value] of getForwardedRequestHeaders(source)) if (value && !overriddenHeaders?.has(key)) unconsumedHeaders.set(`${MIDDLEWARE_REQUEST_HEADER_PREFIX}${key}`, value);
35
35
  return unconsumedHeaders;
36
36
  }
37
- /** Whether middleware changed the headers seen by downstream application code. */
37
+ const UNMERGED_MIDDLEWARE_RESPONSE_HEADERS = /* @__PURE__ */ new Set([
38
+ "accept-encoding",
39
+ "connection",
40
+ "content-encoding",
41
+ "content-length",
42
+ "expect",
43
+ "keep-alive",
44
+ "keepalive",
45
+ "transfer-encoding"
46
+ ]);
47
+ /**
48
+ * Return the truthy ordinary middleware response headers. Next.js's generic
49
+ * middleware-header merge writes each of them to the request too, so a value
50
+ * middleware sets replaces one the client sent under the same name. The
51
+ * `x-middleware-*` namespace carries vinext's middleware protocol and is
52
+ * handled separately.
53
+ */
54
+ function getMergedMiddlewareResponseHeaders(source) {
55
+ const mergedHeaders = new Headers();
56
+ const entries = source instanceof Headers ? source : Object.entries(source).flatMap(([key, value]) => Array.isArray(value) ? value.map((item) => [key, item]) : [[key, value]]);
57
+ for (const [key, value] of entries) {
58
+ const lowerKey = key.toLowerCase();
59
+ if (value && !lowerKey.startsWith("x-middleware-") && !UNMERGED_MIDDLEWARE_RESPONSE_HEADERS.has(lowerKey)) mergedHeaders.append(lowerKey, value);
60
+ }
61
+ return mergedHeaders;
62
+ }
63
+ function applyMergedMiddlewareHeaders(targetHeaders, unconsumedHeaders, mergedHeaders) {
64
+ for (const [key, value] of unconsumedHeaders) targetHeaders.set(key, value);
65
+ for (const key of new Set(mergedHeaders.keys())) targetHeaders.delete(key);
66
+ for (const [key, value] of mergedHeaders) targetHeaders.append(key, value);
67
+ }
68
+ /**
69
+ * `baseHeaders` with only Next.js's generic merge of ordinary middleware
70
+ * response headers applied, without the request-header protocol.
71
+ */
72
+ function applyMiddlewareResponseHeadersToRequest(baseHeaders, middlewareHeaders) {
73
+ const nextHeaders = new Headers(baseHeaders);
74
+ applyMergedMiddlewareHeaders(nextHeaders, /* @__PURE__ */ new Map(), getMergedMiddlewareResponseHeaders(middlewareHeaders));
75
+ return nextHeaders;
76
+ }
77
+ /**
78
+ * Whether middleware changed the headers seen by downstream application code
79
+ * through its request-header protocol. Ordinary response headers are copied to
80
+ * the request as well, but like a client's own headers they do not opt a
81
+ * render out of the shared response stage.
82
+ */
38
83
  function hasMiddlewareRequestHeaderOverrides(source) {
39
84
  if (!source) return false;
40
85
  return Boolean(getMiddlewareHeaderValue(source, "x-middleware-override-headers")) || getUnconsumedMiddlewareRequestHeaders(source).size > 0;
@@ -50,17 +95,19 @@ function encodeMiddlewareRequestHeaders(targetHeaders, requestHeaders) {
50
95
  * middleware. Never re-add absent headers from the base request — that would
51
96
  * resurrect credentials the app explicitly stripped before an external
52
97
  * rewrite. Next.js treats the empty value emitted for `new Headers()` as no
53
- * override. Any unconsumed `x-middleware-request-*` values are subsequently
54
- * copied to the request under their literal protocol-header names.
98
+ * override. Any unconsumed `x-middleware-request-*` values and every other
99
+ * truthy middleware response header are subsequently copied to the request
100
+ * under their literal names, replacing values the client sent.
55
101
  */
56
102
  function buildRequestHeadersFromMiddlewareResponse(baseHeaders, middlewareHeaders) {
57
103
  const forwardedHeaders = getForwardedRequestHeaders(middlewareHeaders);
58
104
  const unconsumedHeaders = getUnconsumedMiddlewareRequestHeaders(middlewareHeaders);
105
+ const mergedHeaders = getMergedMiddlewareResponseHeaders(middlewareHeaders);
59
106
  const rawOverrideHeader = getMiddlewareHeaderValue(middlewareHeaders, MIDDLEWARE_OVERRIDE_HEADERS);
60
107
  if (!rawOverrideHeader) {
61
- if (unconsumedHeaders.size === 0) return null;
108
+ if (unconsumedHeaders.size === 0 && mergedHeaders.keys().next().done) return null;
62
109
  const nextHeaders = new Headers(baseHeaders);
63
- for (const [key, value] of unconsumedHeaders) nextHeaders.set(key, value);
110
+ applyMergedMiddlewareHeaders(nextHeaders, unconsumedHeaders, mergedHeaders);
64
111
  return nextHeaders;
65
112
  }
66
113
  const overrideHeaderNames = parseOverrideHeaderNames(rawOverrideHeader);
@@ -69,11 +116,11 @@ function buildRequestHeadersFromMiddlewareResponse(baseHeaders, middlewareHeader
69
116
  const value = forwardedHeaders.get(key);
70
117
  if (value !== void 0) nextHeaders.set(key, value);
71
118
  }
72
- for (const [key, value] of unconsumedHeaders) nextHeaders.set(key, value);
119
+ applyMergedMiddlewareHeaders(nextHeaders, unconsumedHeaders, mergedHeaders);
73
120
  return nextHeaders;
74
121
  }
75
122
  function shouldKeepMiddlewareHeader(key) {
76
123
  return key === "x-middleware-override-headers" || key === "x-middleware-cache" || key === "x-middleware-set-cookie" || key.startsWith("x-middleware-request-");
77
124
  }
78
125
  //#endregion
79
- export { buildRequestHeadersFromMiddlewareResponse, encodeMiddlewareRequestHeaders, getUnconsumedMiddlewareRequestHeaders, hasMiddlewareRequestHeaderOverrides, shouldKeepMiddlewareHeader };
126
+ export { applyMiddlewareResponseHeadersToRequest, buildRequestHeadersFromMiddlewareResponse, encodeMiddlewareRequestHeaders, getUnconsumedMiddlewareRequestHeaders, hasMiddlewareRequestHeaderOverrides, shouldKeepMiddlewareHeader };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vinext",
3
- "version": "1.4.0",
3
+ "version": "1.4.2",
4
4
  "description": "Run Next.js apps on Vite. Drop-in replacement for the next CLI.",
5
5
  "homepage": "https://vinext.dev/",
6
6
  "license": "MIT",