vinext 1.4.0 → 1.4.1

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 (104) hide show
  1. package/README.md +20 -20
  2. package/dist/build/prerender.js +3 -2
  3. package/dist/check.js +3 -6
  4. package/dist/client/dev-error-overlay-store.d.ts +1 -0
  5. package/dist/client/dev-error-overlay.d.ts +2 -0
  6. package/dist/client/dev-error-overlay.js +19 -3
  7. package/dist/config/config-matchers.js +2 -0
  8. package/dist/config/destination-params.d.ts +2 -1
  9. package/dist/config/destination-params.js +52 -2
  10. package/dist/config/next-config.d.ts +6 -0
  11. package/dist/config/next-config.js +18 -3
  12. package/dist/entries/app-rsc-entry.js +0 -1
  13. package/dist/entries/pages-server-entry.js +3 -1
  14. package/dist/index.js +29 -20
  15. package/dist/plugins/strip-server-exports.js +2 -1
  16. package/dist/server/api-handler.js +2 -1
  17. package/dist/server/app-browser-entry.js +7 -3
  18. package/dist/server/app-browser-rsc-redirect.js +3 -3
  19. package/dist/server/app-browser-server-action-client.js +2 -2
  20. package/dist/server/app-browser-visible-commit.js +2 -2
  21. package/dist/server/app-optimistic-routing.js +2 -3
  22. package/dist/server/app-page-boundary-render.js +5 -10
  23. package/dist/server/app-page-dispatch.d.ts +0 -1
  24. package/dist/server/app-page-dispatch.js +0 -1
  25. package/dist/server/app-page-element-builder.js +3 -3
  26. package/dist/server/app-page-execution.js +9 -6
  27. package/dist/server/app-page-render.d.ts +0 -1
  28. package/dist/server/app-page-render.js +14 -14
  29. package/dist/server/app-page-response.js +1 -1
  30. package/dist/server/app-page-route-wiring.js +22 -5
  31. package/dist/server/app-page-stream.d.ts +10 -8
  32. package/dist/server/app-page-stream.js +2 -1
  33. package/dist/server/app-request-stage-independent-entry.js +1 -1
  34. package/dist/server/app-route-handler-execution.js +3 -2
  35. package/dist/server/app-route-handler-runtime.d.ts +1 -1
  36. package/dist/server/app-route-handler-runtime.js +11 -7
  37. package/dist/server/app-router-entry.js +1 -1
  38. package/dist/server/app-rsc-cache-busting.d.ts +10 -10
  39. package/dist/server/app-rsc-cache-busting.js +27 -68
  40. package/dist/server/app-rsc-error-handler.js +1 -1
  41. package/dist/server/app-rsc-errors.js +4 -1
  42. package/dist/server/app-rsc-handler.js +53 -62
  43. package/dist/server/app-rsc-request-normalization.d.ts +10 -12
  44. package/dist/server/app-rsc-request-normalization.js +11 -14
  45. package/dist/server/app-rsc-response-stage.js +4 -12
  46. package/dist/server/app-server-action-execution.js +8 -4
  47. package/dist/server/app-ssr-entry.d.ts +6 -0
  48. package/dist/server/app-ssr-entry.js +13 -8
  49. package/dist/server/app-worker-stages.d.ts +12 -3
  50. package/dist/server/app-worker-stages.js +37 -4
  51. package/dist/server/bfcache-identity.d.ts +7 -0
  52. package/dist/server/bfcache-identity.js +10 -1
  53. package/dist/server/cacheability-manifest.js +1 -2
  54. package/dist/server/dev-origin-check.d.ts +2 -1
  55. package/dist/server/dev-origin-check.js +2 -1
  56. package/dist/server/dev-server.js +2 -1
  57. package/dist/server/image-optimization.d.ts +5 -0
  58. package/dist/server/image-optimization.js +6 -1
  59. package/dist/server/isr-cache.js +4 -1
  60. package/dist/server/metadata-route-response.js +1 -1
  61. package/dist/server/middleware-matcher.js +19 -6
  62. package/dist/server/middleware-runtime.js +6 -2
  63. package/dist/server/multi-stage.d.ts +7 -3
  64. package/dist/server/pages-node-compat.d.ts +1 -1
  65. package/dist/server/pages-node-compat.js +2 -0
  66. package/dist/server/pages-page-data.js +2 -1
  67. package/dist/server/pages-page-handler.js +17 -6
  68. package/dist/server/pages-page-response.d.ts +9 -0
  69. package/dist/server/pages-page-response.js +18 -4
  70. package/dist/server/pages-request-stage-entry.js +24 -4
  71. package/dist/server/pages-router-entry.js +3 -2
  72. package/dist/server/prod-server.d.ts +2 -2
  73. package/dist/server/prod-server.js +42 -8
  74. package/dist/server/request-error-log.d.ts +9 -0
  75. package/dist/server/request-error-log.js +13 -0
  76. package/dist/server/request-pipeline.d.ts +9 -4
  77. package/dist/server/request-pipeline.js +23 -15
  78. package/dist/server/rsc-completion-metadata.js +3 -0
  79. package/dist/server/static-file-cache.d.ts +9 -0
  80. package/dist/server/static-file-cache.js +13 -2
  81. package/dist/shims/cache-request-state.d.ts +6 -6
  82. package/dist/shims/cache-request-state.js +6 -36
  83. package/dist/shims/cache-runtime.d.ts +3 -1
  84. package/dist/shims/cache-runtime.js +77 -47
  85. package/dist/shims/cache.d.ts +10 -5
  86. package/dist/shims/cache.js +35 -22
  87. package/dist/shims/client-hook-error.d.ts +2 -0
  88. package/dist/shims/client-hook-error.js +16 -1
  89. package/dist/shims/fetch-cache.js +6 -2
  90. package/dist/shims/headers.d.ts +2 -1
  91. package/dist/shims/headers.js +5 -3
  92. package/dist/shims/internal/cache-context-accessor.d.ts +1 -1
  93. package/dist/shims/link.js +5 -10
  94. package/dist/shims/navigation.js +2 -3
  95. package/dist/shims/request-state-types.d.ts +2 -1
  96. package/dist/shims/slot.d.ts +6 -2
  97. package/dist/shims/slot.js +10 -3
  98. package/dist/shims/unified-request-context.d.ts +6 -0
  99. package/dist/shims/unified-request-context.js +1 -0
  100. package/dist/utils/cache-life-profiles.d.ts +33 -0
  101. package/dist/utils/cache-life-profiles.js +111 -0
  102. package/dist/utils/middleware-request-headers.d.ts +14 -3
  103. package/dist/utils/middleware-request-headers.js +54 -7
  104. package/package.json +1 -1
@@ -1,10 +1,10 @@
1
+ import { CacheLifeConfig } from "../utils/cache-life-profiles.js";
1
2
  import { _recordUseCacheRootParamRead, _registerCacheContextAccessor, getRegisteredCacheContext } from "./internal/cache-context-accessor.js";
2
3
  //#region src/shims/cache-request-state.d.ts
3
- export type CacheLifeConfig = {
4
- stale?: number;
5
- revalidate?: number;
6
- expire?: number;
7
- };
4
+ /**
5
+ * The built-in profiles merged with next.config `cacheLife`, baked in at
6
+ * build time like Next.js's `process.env.__NEXT_CACHE_LIFE` define.
7
+ */
8
8
  export declare const cacheLifeProfiles: Record<string, CacheLifeConfig>;
9
9
  export type UnstableCacheRevalidationMode = "foreground" | "background";
10
10
  export type ActionRevalidationKind = 0 | 1 | 2;
@@ -121,4 +121,4 @@ export declare function _captureRequestScopedCacheLifeAccessors(): {
121
121
  export declare function runWithDetachedCacheObservations<T>(fn: () => Promise<T>): Promise<T>;
122
122
  export declare function shouldServeStaleUnstableCacheEntry(): boolean;
123
123
  //#endregion
124
- export { _recordUseCacheRootParamRead, _registerCacheContextAccessor, getRegisteredCacheContext };
124
+ export { type CacheLifeConfig, _recordUseCacheRootParamRead, _registerCacheContextAccessor, getRegisteredCacheContext };
@@ -1,45 +1,15 @@
1
1
  import { getOrCreateAls } from "./internal/als-registry.js";
2
2
  import { getRequestContext, isInsideUnifiedScope, runWithUnifiedStateMutation } from "./unified-request-context.js";
3
+ import { parseCacheLifeProfiles } from "../utils/cache-life-profiles.js";
3
4
  import { getCdnCacheAdapter } from "./cdn-cache.js";
4
5
  import { getHeadersAccessPhase } from "./internal/headers-state.js";
5
6
  import { _recordUseCacheRootParamRead, _registerCacheContextAccessor, getRegisteredCacheContext } from "./internal/cache-context-accessor.js";
6
7
  //#region src/shims/cache-request-state.ts
7
- const cacheLifeProfiles = {
8
- default: {
9
- revalidate: 900,
10
- expire: 4294967294
11
- },
12
- seconds: {
13
- stale: 30,
14
- revalidate: 1,
15
- expire: 60
16
- },
17
- minutes: {
18
- stale: 300,
19
- revalidate: 60,
20
- expire: 3600
21
- },
22
- hours: {
23
- stale: 300,
24
- revalidate: 3600,
25
- expire: 86400
26
- },
27
- days: {
28
- stale: 300,
29
- revalidate: 86400,
30
- expire: 604800
31
- },
32
- weeks: {
33
- stale: 300,
34
- revalidate: 604800,
35
- expire: 2592e3
36
- },
37
- max: {
38
- stale: 300,
39
- revalidate: 2592e3,
40
- expire: 31536e3
41
- }
42
- };
8
+ /**
9
+ * The built-in profiles merged with next.config `cacheLife`, baked in at
10
+ * build time like Next.js's `process.env.__NEXT_CACHE_LIFE` define.
11
+ */
12
+ const cacheLifeProfiles = parseCacheLifeProfiles(process.env.__VINEXT_CACHE_LIFE_PROFILES);
43
13
  const FALLBACK_KEY = Symbol.for("vinext.cache.fallback");
44
14
  const globalState = globalThis;
45
15
  const cacheAls = getOrCreateAls("vinext.cache.als");
@@ -1,4 +1,4 @@
1
- import { CacheLifeConfig } from "./cache-request-state.js";
1
+ import { CacheLifeConfig } from "../utils/cache-life-profiles.js";
2
2
  import { CacheFlightArguments } from "./cache-flight-arguments.js";
3
3
  //#region src/shims/cache-runtime.d.ts
4
4
  export declare function replayCachedFunction(fn: object, args: CacheFlightArguments): Promise<unknown>;
@@ -16,6 +16,8 @@ export type CacheContext = {
16
16
  tags: string[];
17
17
  /** Cache life configs collected via cacheLife() — minimum-wins rule applies */
18
18
  lifeConfigs: CacheLifeConfig[];
19
+ /** Resolved lives of nested caches; they only constrain fields cacheLife() left unset */
20
+ nestedLifeConfigs: CacheLifeConfig[];
19
21
  /** Cache variant: "default" | "remote" | "private" */
20
22
  variant: string;
21
23
  /** Root params observed while producing this public cache entry. */
@@ -1,6 +1,7 @@
1
1
  import { getOrCreateAls } from "./internal/als-registry.js";
2
2
  import { getRequestContext, isInsideUnifiedScope, runWithUnifiedStateMutation } from "./unified-request-context.js";
3
3
  import { VINEXT_RSC_MARKER_HEADER } from "../server/headers.js";
4
+ import { isInsideWorkStoreScope } from "./internal/work-store-scope.js";
4
5
  import { workUnitAsyncStorage } from "./internal/work-unit-async-storage.js";
5
6
  import { isRouteCacheabilityProbe, recordRouteCacheabilityProbeBailout } from "./cacheability-classification.js";
6
7
  import { shareIsolatePromise } from "../server/isolate-shared-promise.js";
@@ -189,22 +190,46 @@ async function serializeCacheResult(result, rsc) {
189
190
  throw serializationError?.value ?? error;
190
191
  }
191
192
  }
193
+ const CACHE_LIFE_FIELDS = [
194
+ "stale",
195
+ "revalidate",
196
+ "expire"
197
+ ];
198
+ /** Per field, the smallest value any config sets. */
199
+ function minimumCacheLife(configs) {
200
+ const result = {};
201
+ for (const config of configs) for (const field of CACHE_LIFE_FIELDS) {
202
+ const value = config[field];
203
+ const current = result[field];
204
+ if (value !== void 0 && (current === void 0 || value < current)) result[field] = value;
205
+ }
206
+ return result;
207
+ }
192
208
  /**
193
- * Resolve collected cacheLife configs into a single effective config.
194
- * The "minimum-wins" rule: if multiple cacheLife() calls are made,
195
- * each field takes the smallest value across all calls.
209
+ * Resolve a cache scope's effective life, like Next.js's `collectResult`
210
+ * (use-cache-wrapper.ts). A field set by `cacheLife()` takes the minimum
211
+ * across those calls and wins over nested caches. Any other field takes the
212
+ * minimum of the default profile and the nested caches' lives.
196
213
  */
197
- function resolveCacheLife(configs) {
198
- if (configs.length === 0) return { ...cacheLifeProfiles.default };
199
- if (configs.length === 1) return { ...configs[0] };
214
+ function resolveCacheLife(ctx) {
215
+ const explicit = minimumCacheLife(ctx.lifeConfigs);
216
+ const collected = minimumCacheLife([cacheLifeProfiles.default, ...ctx.nestedLifeConfigs]);
200
217
  const result = {};
201
- for (const config of configs) {
202
- if (config.stale !== void 0) result.stale = result.stale !== void 0 ? Math.min(result.stale, config.stale) : config.stale;
203
- if (config.revalidate !== void 0) result.revalidate = result.revalidate !== void 0 ? Math.min(result.revalidate, config.revalidate) : config.revalidate;
204
- if (config.expire !== void 0) result.expire = result.expire !== void 0 ? Math.min(result.expire, config.expire) : config.expire;
218
+ for (const field of CACHE_LIFE_FIELDS) {
219
+ const value = explicit[field] ?? collected[field];
220
+ if (value !== void 0) result[field] = value;
205
221
  }
206
222
  return result;
207
223
  }
224
+ /**
225
+ * Hand a finished cache scope's life to what encloses it, like Next.js's
226
+ * `propagateCacheEntryMetadata`: an enclosing cache collects it as a nested
227
+ * life, and only an outermost cache reaches the request.
228
+ */
229
+ function propagateCacheLife(parentCtx, life) {
230
+ if (parentCtx) parentCtx.nestedLifeConfigs.push(life);
231
+ else _setRequestScopedCacheLife(life);
232
+ }
208
233
  const _PRIVATE_FALLBACK_KEY = Symbol.for("vinext.cacheRuntime.privateFallback");
209
234
  const _g = globalThis;
210
235
  const _privateAls = getOrCreateAls("vinext.cacheRuntime.privateAls");
@@ -416,10 +441,7 @@ function registerCachedFunction(fn, id, variant, options = {}) {
416
441
  const timestamp = Date.now();
417
442
  const { result, ctx, effectiveLife, collectedResult } = await runCachedFunctionWithContext(callFn, callArgs, cacheVariant, (value) => serializeCacheResult(value, rsc));
418
443
  const rootParamNames = ctx.readRootParamNames && ctx.readRootParamNames.size > 0 ? addKnownRootParamNames(id, ctx.readRootParamNames) : knownRootParamsByFunctionId.get(id);
419
- if (propagate) {
420
- recordRequestScopedCacheLife(effectiveLife);
421
- propagateCacheTagsToRequest(ctx.tags);
422
- }
444
+ if (propagate) propagateCacheTagsToRequest(ctx.tags);
423
445
  const revalidateSeconds = effectiveLife.revalidate ?? cacheLifeProfiles.default.revalidate ?? 900;
424
446
  if (collectedResult?.cacheEntry) try {
425
447
  let cacheFunctionInvocation;
@@ -453,32 +475,39 @@ function registerCachedFunction(fn, id, variant, options = {}) {
453
475
  stale: effectiveLife.stale
454
476
  }
455
477
  };
478
+ let write;
456
479
  if (rootParamNames && rootParamNames.size > 0 && rootParams) {
457
480
  const specificCacheKey = coarseCacheKey + computeRootParamsCacheKeySuffix(rootParams, rootParamNames);
458
481
  const redirectTags = [...ctx.tags, ...[...rootParamNames].map((name) => ROOT_PARAM_TAG_PREFIX + name)];
459
- await handler.set(coarseCacheKey, {
460
- kind: "FETCH",
461
- data: {
462
- headers: { [ROOT_PARAM_REDIRECT_HEADER]: "1" },
463
- body: "",
464
- url: coarseCacheKey
465
- },
466
- tags: redirectTags,
467
- revalidate: revalidateSeconds
468
- }, {
469
- ...cacheContext,
470
- tags: redirectTags,
471
- ...coarseCacheKey === cacheKey ? staleEntry && { staleEntry } : staleRedirect && { staleEntry: staleRedirect }
472
- });
482
+ try {
483
+ await handler.set(coarseCacheKey, {
484
+ kind: "FETCH",
485
+ data: {
486
+ headers: { [ROOT_PARAM_REDIRECT_HEADER]: "1" },
487
+ body: "",
488
+ url: coarseCacheKey
489
+ },
490
+ tags: redirectTags,
491
+ revalidate: revalidateSeconds
492
+ }, {
493
+ ...cacheContext,
494
+ tags: redirectTags,
495
+ ...coarseCacheKey === cacheKey ? staleEntry && { staleEntry } : staleRedirect && { staleEntry: staleRedirect }
496
+ });
497
+ } catch {}
473
498
  cacheValue.data.url = specificCacheKey;
474
- await handler.set(specificCacheKey, cacheValue, {
499
+ write = handler.set(specificCacheKey, cacheValue, {
475
500
  ...cacheContext,
476
501
  ...staleEntry && specificCacheKey === cacheKey ? { staleEntry } : {}
477
502
  });
478
- } else await handler.set(cacheKey, cacheValue, {
503
+ } else write = handler.set(cacheKey, cacheValue, {
479
504
  ...cacheContext,
480
505
  ...staleEntry ? { staleEntry } : {}
481
506
  });
507
+ const stored = write.then(() => void 0, () => {});
508
+ if (propagate && isInsideWorkStoreScope()) {
509
+ if (isInsideUnifiedScope()) getRequestContext().executionContext?.waitUntil(stored);
510
+ } else await stored;
482
511
  } catch {}
483
512
  return collectedResult ? collectedResult.result : result;
484
513
  };
@@ -539,16 +568,12 @@ function recordRequestScopedCacheControl(cacheControl) {
539
568
  stale: cacheControl.stale
540
569
  };
541
570
  const parentCtx = cacheContextStorage.getStore();
542
- parentCtx?.lifeConfigs.push(life);
571
+ propagateCacheLife(parentCtx, life);
543
572
  if (parentCtx && parentCtx.variant !== "private" && (cacheControl.revalidate === 0 || cacheControl.expire !== void 0 && cacheControl.expire < DYNAMIC_EXPIRE)) {
544
573
  const error = new NestedDynamicUseCacheError();
545
574
  if (typeof Error.captureStackTrace === "function") Error.captureStackTrace(error, recordRequestScopedCacheControl);
546
575
  parentCtx.dynamicNestedCacheError ??= error;
547
576
  }
548
- _setRequestScopedCacheLife(life);
549
- }
550
- function recordRequestScopedCacheLife(cacheLife) {
551
- _setRequestScopedCacheLife(cacheLife);
552
577
  }
553
578
  /**
554
579
  * Bubble a `"use cache"` scope's tags toward where they can drive invalidation.
@@ -575,8 +600,7 @@ function propagateCacheTagsToRequest(tags) {
575
600
  addCollectedRequestTags(tags);
576
601
  }
577
602
  async function executeWithContext(fn, args, variant, rsc) {
578
- const { result, ctx: _ctx, effectiveLife, collectedResult } = await runCachedFunctionWithContext(fn, args, variant, (value) => serializeCacheResult(value, rsc));
579
- recordRequestScopedCacheLife(effectiveLife);
603
+ const { result, collectedResult } = await runCachedFunctionWithContext(fn, args, variant, (value) => serializeCacheResult(value, rsc));
580
604
  return {
581
605
  result: collectedResult ? collectedResult.result : result,
582
606
  cacheable: collectedResult?.cacheEntry !== null,
@@ -593,6 +617,7 @@ async function runCachedFunctionWithContext(fn, args, variant, collectResult) {
593
617
  const ctx = {
594
618
  tags: [],
595
619
  lifeConfigs: [],
620
+ nestedLifeConfigs: [],
596
621
  variant: variant || "default",
597
622
  readRootParamNames: /* @__PURE__ */ new Set(),
598
623
  hasExplicitRevalidate: false,
@@ -602,21 +627,26 @@ async function runCachedFunctionWithContext(fn, args, variant, collectResult) {
602
627
  };
603
628
  let collectedResult;
604
629
  const workUnitType = variant === "private" ? "private-cache" : "cache";
605
- const result = await workUnitAsyncStorage.run({ type: workUnitType }, () => cacheContextStorage.run(ctx, async () => {
606
- if (!collectResult) return await fn(...args);
607
- let value;
608
- const lazyValue = { then: (onFulfilled, onRejected) => (value ??= new Promise((resolve) => resolve(fn(...args)))).then(onFulfilled, onRejected) };
609
- collectedResult = await collectResult(lazyValue, ctx);
610
- return await lazyValue;
611
- }));
630
+ let result;
631
+ let effectiveLife;
632
+ try {
633
+ result = await workUnitAsyncStorage.run({ type: workUnitType }, () => cacheContextStorage.run(ctx, async () => {
634
+ if (!collectResult) return await fn(...args);
635
+ let value;
636
+ const lazyValue = { then: (onFulfilled, onRejected) => (value ??= new Promise((resolve) => resolve(fn(...args)))).then(onFulfilled, onRejected) };
637
+ collectedResult = await collectResult(lazyValue, ctx);
638
+ return await lazyValue;
639
+ }));
640
+ } finally {
641
+ effectiveLife = resolveCacheLife(ctx);
642
+ propagateCacheLife(parentCtx, effectiveLife);
643
+ if (parentCtx && eagerError && (effectiveLife.revalidate === 0 || effectiveLife.expire !== void 0 && effectiveLife.expire < DYNAMIC_EXPIRE)) parentCtx.dynamicNestedCacheError ??= eagerError;
644
+ }
612
645
  if (ctx.invalidDynamicUsageError) throw ctx.invalidDynamicUsageError;
613
- const effectiveLife = resolveCacheLife(ctx.lifeConfigs);
614
646
  if (parentCtx) {
615
- parentCtx.lifeConfigs.push(effectiveLife);
616
647
  for (const tag of ctx.tags) if (!parentCtx.tags.includes(tag)) parentCtx.tags.push(tag);
617
648
  if (parentCtx.variant !== "private") for (const name of ctx.readRootParamNames ?? []) parentCtx.readRootParamNames?.add(name);
618
649
  }
619
- if (parentCtx && eagerError && (effectiveLife.revalidate === 0 || effectiveLife.expire !== void 0 && effectiveLife.expire < DYNAMIC_EXPIRE)) parentCtx.dynamicNestedCacheError ??= eagerError;
620
650
  if (typeof process !== "undefined" && (process.env.VINEXT_PRERENDER === "1" || process.env.NODE_ENV === "development") && ctx.dynamicNestedCacheError) {
621
651
  if (effectiveLife.revalidate === 0 && !ctx.hasExplicitRevalidate) throw new Error(getNestedCacheZeroRevalidateErrorMessage(), { cause: ctx.dynamicNestedCacheError });
622
652
  if (effectiveLife.expire !== void 0 && effectiveLife.expire < DYNAMIC_EXPIRE && !ctx.hasExplicitExpire) throw new Error(getNestedCacheShortExpireErrorMessage(), { cause: ctx.dynamicNestedCacheError });
@@ -1,7 +1,8 @@
1
+ import { CacheLifeConfig } from "../utils/cache-life-profiles.js";
1
2
  import { ExecutionContextLike, getRequestExecutionContext, runWithExecutionContext } from "./request-context.js";
2
3
  import { CacheControlMetadata, CacheHandler, CacheHandlerContext, CacheHandlerValue, CachedAppPageValue, CachedFetchValue, CachedImageValue, CachedPagesValue, CachedRedirectValue, CachedRouteValue, IncrementalCacheValue, MemoryCacheHandler, NoOpCacheHandler, configureMemoryCacheHandler, getCacheHandler, getDataCacheHandler, registerDataCacheHandler, registerLazyDataCacheHandler, setCacheHandler, setDataCacheHandler } from "./cache-handler.js";
3
4
  import { _recordUseCacheRootParamRead, _registerCacheContextAccessor, getRegisteredCacheContext } from "./internal/cache-context-accessor.js";
4
- import { ACTION_DID_REVALIDATE_DYNAMIC_ONLY, ACTION_DID_REVALIDATE_STATIC_AND_DYNAMIC, ActionRevalidationKind, CacheLifeConfig, CacheState, PendingCdnTagInvalidation, UnstableCacheRevalidationMode, _captureRequestScopedCacheLifeAccessors, _consumeRequestScopedCacheLife, _drainPendingRevalidations, _hasPendingRevalidatedTag, _initRequestScopedCacheState, _isOnDemandRevalidate, _markPendingRevalidatedTag, _peekRequestScopedCacheLife, _queuePendingCdnTagInvalidation, _queuePendingRevalidation, _revalidateCdnTags, _runWithCacheState, _setRequestScopedCacheLife, cacheLifeProfiles, getAndClearActionRevalidationKind, markActionRevalidation, runWithDetachedCacheObservations, shouldServeStaleUnstableCacheEntry } from "./cache-request-state.js";
5
+ import { ACTION_DID_REVALIDATE_DYNAMIC_ONLY, ACTION_DID_REVALIDATE_STATIC_AND_DYNAMIC, ActionRevalidationKind, CacheState, PendingCdnTagInvalidation, UnstableCacheRevalidationMode, _captureRequestScopedCacheLifeAccessors, _consumeRequestScopedCacheLife, _drainPendingRevalidations, _hasPendingRevalidatedTag, _initRequestScopedCacheState, _isOnDemandRevalidate, _markPendingRevalidatedTag, _peekRequestScopedCacheLife, _queuePendingCdnTagInvalidation, _queuePendingRevalidation, _revalidateCdnTags, _runWithCacheState, _setRequestScopedCacheLife, cacheLifeProfiles, getAndClearActionRevalidationKind, markActionRevalidation, runWithDetachedCacheObservations, shouldServeStaleUnstableCacheEntry } from "./cache-request-state.js";
5
6
  //#region src/shims/cache.d.ts
6
7
  /**
7
8
  * Revalidate cached data associated with a specific cache tag.
@@ -101,12 +102,16 @@ export declare function unstable_io(): Promise<void>;
101
102
  /**
102
103
  * Set the cache lifetime for a "use cache" function.
103
104
  *
104
- * Accepts either a built-in profile name (e.g., "hours", "days") or a custom
105
- * configuration object. In Next.js, this only works inside "use cache" functions.
105
+ * Accepts either a profile name (a built-in like "hours", or one configured
106
+ * in next.config `cacheLife`) or a custom configuration object; an unknown
107
+ * profile name throws, like Next.js. In Next.js, this only works inside
108
+ * "use cache" functions.
106
109
  *
107
110
  * When called inside a "use cache" function, this sets the cache TTL.
108
111
  * The "minimum-wins" rule applies: if called multiple times, the shortest
109
- * duration for each field wins.
112
+ * duration for each field wins. Like Next.js, only the fields a call sets are
113
+ * explicit: the others resolve from the default profile and any nested caches
114
+ * (see `resolveCacheLife` in cache-runtime.ts).
110
115
  *
111
116
  * When called outside a "use cache" context, this is a validated no-op.
112
117
  */
@@ -143,4 +148,4 @@ type UnstableCacheOptions = {
143
148
  */
144
149
  export declare function unstable_cache<T extends (...args: any[]) => Promise<any>>(fn: T, keyParts?: string[], options?: UnstableCacheOptions): T;
145
150
  //#endregion
146
- export { ACTION_DID_REVALIDATE_DYNAMIC_ONLY, ACTION_DID_REVALIDATE_STATIC_AND_DYNAMIC, ActionRevalidationKind, CacheControlMetadata, CacheHandler, CacheHandlerContext, CacheHandlerValue, CacheLifeConfig, CacheState, CachedAppPageValue, CachedFetchValue, CachedImageValue, CachedPagesValue, CachedRedirectValue, CachedRouteValue, type ExecutionContextLike, IncrementalCacheValue, MemoryCacheHandler, NoOpCacheHandler, PendingCdnTagInvalidation, UnstableCacheRevalidationMode, _captureRequestScopedCacheLifeAccessors, _consumeRequestScopedCacheLife, _drainPendingRevalidations, _hasPendingRevalidatedTag, _initRequestScopedCacheState, _isOnDemandRevalidate, _markPendingRevalidatedTag, _peekRequestScopedCacheLife, _queuePendingCdnTagInvalidation, _queuePendingRevalidation, _recordUseCacheRootParamRead, _registerCacheContextAccessor, _revalidateCdnTags, _runWithCacheState, _setRequestScopedCacheLife, cacheLifeProfiles, configureMemoryCacheHandler, getAndClearActionRevalidationKind, getCacheHandler, getDataCacheHandler, getRegisteredCacheContext, getRequestExecutionContext, markActionRevalidation, unstable_noStore as noStore, registerDataCacheHandler, registerLazyDataCacheHandler, runWithDetachedCacheObservations, runWithExecutionContext, setCacheHandler, setDataCacheHandler, shouldServeStaleUnstableCacheEntry };
151
+ export { ACTION_DID_REVALIDATE_DYNAMIC_ONLY, ACTION_DID_REVALIDATE_STATIC_AND_DYNAMIC, ActionRevalidationKind, CacheControlMetadata, CacheHandler, CacheHandlerContext, CacheHandlerValue, type CacheLifeConfig, CacheState, CachedAppPageValue, CachedFetchValue, CachedImageValue, CachedPagesValue, CachedRedirectValue, CachedRouteValue, type ExecutionContextLike, IncrementalCacheValue, MemoryCacheHandler, NoOpCacheHandler, PendingCdnTagInvalidation, UnstableCacheRevalidationMode, _captureRequestScopedCacheLifeAccessors, _consumeRequestScopedCacheLife, _drainPendingRevalidations, _hasPendingRevalidatedTag, _initRequestScopedCacheState, _isOnDemandRevalidate, _markPendingRevalidatedTag, _peekRequestScopedCacheLife, _queuePendingCdnTagInvalidation, _queuePendingRevalidation, _recordUseCacheRootParamRead, _registerCacheContextAccessor, _revalidateCdnTags, _runWithCacheState, _setRequestScopedCacheLife, cacheLifeProfiles, configureMemoryCacheHandler, getAndClearActionRevalidationKind, getCacheHandler, getDataCacheHandler, getRegisteredCacheContext, getRequestExecutionContext, markActionRevalidation, unstable_noStore as noStore, registerDataCacheHandler, registerLazyDataCacheHandler, runWithDetachedCacheObservations, runWithExecutionContext, setCacheHandler, setDataCacheHandler, shouldServeStaleUnstableCacheEntry };
@@ -230,29 +230,34 @@ let _unstableIoWarned = false;
230
230
  /**
231
231
  * Set the cache lifetime for a "use cache" function.
232
232
  *
233
- * Accepts either a built-in profile name (e.g., "hours", "days") or a custom
234
- * configuration object. In Next.js, this only works inside "use cache" functions.
233
+ * Accepts either a profile name (a built-in like "hours", or one configured
234
+ * in next.config `cacheLife`) or a custom configuration object; an unknown
235
+ * profile name throws, like Next.js. In Next.js, this only works inside
236
+ * "use cache" functions.
235
237
  *
236
238
  * When called inside a "use cache" function, this sets the cache TTL.
237
239
  * The "minimum-wins" rule applies: if called multiple times, the shortest
238
- * duration for each field wins.
240
+ * duration for each field wins. Like Next.js, only the fields a call sets are
241
+ * explicit: the others resolve from the default profile and any nested caches
242
+ * (see `resolveCacheLife` in cache-runtime.ts).
239
243
  *
240
244
  * When called outside a "use cache" context, this is a validated no-op.
241
245
  */
242
246
  function cacheLife(profile) {
243
247
  let resolvedConfig;
244
248
  if (typeof profile === "string") {
245
- if (!cacheLifeProfiles[profile]) {
246
- console.warn(`[vinext] cacheLife: unknown profile "${profile}". Available profiles: ${Object.keys(cacheLifeProfiles).join(", ")}`);
247
- return;
249
+ const configuredProfile = Object.hasOwn(cacheLifeProfiles, profile) ? cacheLifeProfiles[profile] : void 0;
250
+ if (configuredProfile === void 0) {
251
+ if (Object.hasOwn(cacheLifeProfiles, profile.trim())) throw new Error(`Unknown \`cacheLife()\` profile "${profile}" is not configured in next.config.js\nDid you mean "${profile.trim()}" without the spaces?`);
252
+ throw new Error(`Unknown \`cacheLife()\` profile "${profile}" is not configured in next.config.js\nmodule.exports = {
253
+ cacheLife: {
254
+ "${profile}": ...\n }
255
+ }`);
248
256
  }
249
- resolvedConfig = { ...cacheLifeProfiles[profile] };
257
+ resolvedConfig = { ...configuredProfile };
250
258
  } else if (typeof profile === "object" && profile !== null) {
251
259
  if (profile.expire !== void 0 && profile.revalidate !== void 0 && profile.expire < profile.revalidate) console.warn("[vinext] cacheLife: expire must be >= revalidate");
252
- resolvedConfig = {
253
- ...cacheLifeProfiles.default,
254
- ...profile
255
- };
260
+ resolvedConfig = { ...profile };
256
261
  } else return;
257
262
  try {
258
263
  const ctx = getRegisteredCacheContext();
@@ -260,11 +265,13 @@ function cacheLife(profile) {
260
265
  ctx.lifeConfigs.push(resolvedConfig);
261
266
  if (resolvedConfig.revalidate !== void 0) ctx.hasExplicitRevalidate = true;
262
267
  if (resolvedConfig.expire !== void 0) ctx.hasExplicitExpire = true;
263
- _setRequestScopedCacheLife(resolvedConfig);
264
268
  return;
265
269
  }
266
270
  } catch {}
267
- _setRequestScopedCacheLife(resolvedConfig);
271
+ _setRequestScopedCacheLife(typeof profile === "string" ? resolvedConfig : {
272
+ ...cacheLifeProfiles.default,
273
+ ...profile
274
+ });
268
275
  }
269
276
  /**
270
277
  * Tag a "use cache" function's cached result for on-demand revalidation.
@@ -327,7 +334,7 @@ function unstable_cacheTag(...tags) {
327
334
  }
328
335
  return cacheTag(...tags);
329
336
  }
330
- const UNSTABLE_CACHE_KEY_PREFIX = "unstable_cache:v2";
337
+ const UNSTABLE_CACHE_KEY_PREFIX = "unstable_cache:v3";
331
338
  /**
332
339
  * AsyncLocalStorage to track whether we're inside an unstable_cache() callback.
333
340
  * Stored on globalThis via Symbol so headers.ts can detect the scope without
@@ -396,7 +403,7 @@ function scheduleUnstableCacheBackgroundRevalidation(cacheKey, refresh) {
396
403
  pending.set(cacheKey, trackedRevalidation);
397
404
  waitUntilUnstableCacheRevalidation(trackedRevalidation);
398
405
  }
399
- async function refreshUnstableCacheResult(fn, args, keyedArgs, cacheKey, tags, revalidateSeconds, staleEntry) {
406
+ async function refreshUnstableCacheResult(fn, args, keyedArgs, cacheKey, tags, revalidateSeconds, staleEntry, deferWrite) {
400
407
  const argsUnchanged = serializeArgsOrNull(args) === keyedArgs;
401
408
  const result = await _unstableCacheAls.run(true, () => _unstableCacheCallbackAls.run({ useCacheContext: _useCacheContextAls.getStore() }, () => fn(...args)));
402
409
  if (!argsUnchanged) return result;
@@ -410,12 +417,18 @@ async function refreshUnstableCacheResult(fn, args, keyedArgs, cacheKey, tags, r
410
417
  tags,
411
418
  revalidate: typeof revalidateSeconds === "number" ? revalidateSeconds : false
412
419
  };
413
- await getDataCacheHandler().set(cacheKey, cacheValue, {
414
- fetchCache: true,
415
- tags,
416
- revalidate: revalidateSeconds,
417
- ...staleEntry ? { staleEntry } : {}
420
+ const write = new Promise((resolve) => {
421
+ resolve(getDataCacheHandler().set(cacheKey, cacheValue, {
422
+ fetchCache: true,
423
+ tags,
424
+ revalidate: revalidateSeconds,
425
+ ...staleEntry ? { staleEntry } : {}
426
+ }));
427
+ }).catch((err) => {
428
+ console.error("[vinext] unstable_cache write error:", err);
418
429
  });
430
+ if (deferWrite) getRequestExecutionContext()?.waitUntil(write);
431
+ else await write;
419
432
  return result;
420
433
  }
421
434
  function serializeArgsOrNull(args) {
@@ -458,7 +471,7 @@ function unstable_cache(fn, keyParts, options) {
458
471
  if (cached.ok) {
459
472
  if (existing.cacheState === "stale") {
460
473
  if (shouldServeStaleUnstableCacheEntry()) {
461
- scheduleUnstableCacheBackgroundRevalidation(cacheKey, () => refreshUnstableCacheResult(fn, args, keyedArgs, cacheKey, tags, revalidateSeconds, staleEntry));
474
+ scheduleUnstableCacheBackgroundRevalidation(cacheKey, () => refreshUnstableCacheResult(fn, args, keyedArgs, cacheKey, tags, revalidateSeconds, staleEntry, false));
462
475
  return cached.value;
463
476
  }
464
477
  } else return cached.value;
@@ -466,7 +479,7 @@ function unstable_cache(fn, keyParts, options) {
466
479
  }
467
480
  }
468
481
  if (isDraftMode) return await _unstableCacheAls.run(true, () => fn(...args));
469
- return await refreshUnstableCacheResult(fn, args, keyedArgs, cacheKey, tags, revalidateSeconds, staleEntry);
482
+ return await refreshUnstableCacheResult(fn, args, keyedArgs, cacheKey, tags, revalidateSeconds, staleEntry, !staleEntry && isInsideWorkStoreScope());
470
483
  };
471
484
  return cachedFn;
472
485
  }
@@ -9,4 +9,6 @@
9
9
  */
10
10
  export declare function buildClientHookErrorMessage(hookName: string): string;
11
11
  export declare function throwClientHookError(hookName: string): never;
12
+ /** Rewrites that TypeError into the actionable message, like Next.js's formatServerError. */
13
+ export declare function rewriteClientHookError(error: Error): void;
12
14
  //#endregion
@@ -13,5 +13,20 @@ function buildClientHookErrorMessage(hookName) {
13
13
  function throwClientHookError(hookName) {
14
14
  throw new Error(buildClientHookErrorMessage(hookName));
15
15
  }
16
+ const clientHookPattern = /\b(useState|useEffect|useReducer|useRef|useContext|useLayoutEffect|useInsertionEffect|useSyncExternalStore|useTransition|useImperativeHandle|useDeferredValue|useActionState|useOptimistic|useEffectEvent)\b.*is not a function/;
17
+ /** Rewrites that TypeError into the actionable message, like Next.js's formatServerError. */
18
+ function rewriteClientHookError(error) {
19
+ const match = error.message.match(clientHookPattern);
20
+ if (!match) return;
21
+ const message = buildClientHookErrorMessage(match[1]);
22
+ try {
23
+ if (error.stack) {
24
+ const lines = error.stack.split("\n");
25
+ lines[0] = message;
26
+ error.stack = lines.join("\n");
27
+ }
28
+ error.message = message;
29
+ } catch {}
30
+ }
16
31
  //#endregion
17
- export { buildClientHookErrorMessage, throwClientHookError };
32
+ export { buildClientHookErrorMessage, rewriteClientHookError, throwClientHookError };
@@ -804,7 +804,9 @@ function createPatchedFetch() {
804
804
  cached = null;
805
805
  }
806
806
  if (cached?.value && cached.value.kind === "FETCH" && cached.cacheState !== "stale") {
807
- await lowerFetchCacheRevalidateIfNeeded(handler, cacheKey, cached.value, tags, revalidateSeconds);
807
+ await lowerFetchCacheRevalidateIfNeeded(handler, cacheKey, cached.value, tags, revalidateSeconds).catch((err) => {
808
+ console.error("[vinext] fetch cache write error:", err);
809
+ });
808
810
  const cachedData = cached.value.data;
809
811
  return recordFetchOutcome(buildCachedFetchResponse(cachedData, input), "hit", cacheReason);
810
812
  }
@@ -812,7 +814,9 @@ function createPatchedFetch() {
812
814
  staleEntry = cached;
813
815
  if (shouldRefreshStaleFetchInForeground()) {
814
816
  const freshResponse = await dedupeFetch(input, fetchInit);
815
- await writeFetchCacheResponse(handler, cacheKey, freshResponse, tags, revalidateSeconds, { staleEntry: cached });
817
+ await writeFetchCacheResponse(handler, cacheKey, freshResponse, tags, revalidateSeconds, { staleEntry: cached }).catch((err) => {
818
+ console.error("[vinext] fetch cache write error:", err);
819
+ });
816
820
  return recordFetchOutcome(freshResponse, "miss", cacheReason);
817
821
  }
818
822
  const staleData = cached.value.data;
@@ -168,7 +168,8 @@ export declare function getActiveDraftModeState(): boolean | null;
168
168
  /**
169
169
  * Read the active request's draft-mode state without recording request API usage.
170
170
  * Internal cache implementations use this to bypass persistent reads and writes,
171
- * matching Next.js's request-level `workStore.isDraftMode` guard.
171
+ * matching Next.js's request-level `workStore.isDraftMode` guard. Once the
172
+ * headers context is cleared, the request keeps the state it had then.
172
173
  */
173
174
  export declare function isDraftModeEnabled(): boolean;
174
175
  type DraftModeResult = {
@@ -1,4 +1,4 @@
1
- import { ensureRenderDynamicLatch, isInsideUnifiedScope, runWithUnifiedStateMutation } from "./unified-request-context.js";
1
+ import { ensureRenderDynamicLatch, getRequestContext, isInsideUnifiedScope, runWithUnifiedStateMutation } from "./unified-request-context.js";
2
2
  import { MIDDLEWARE_SET_COOKIE_HEADER } from "../utils/protocol-headers.js";
3
3
  import { buildRequestHeadersFromMiddlewareResponse } from "../utils/middleware-request-headers.js";
4
4
  import { parseEdgeRequestCookieHeader } from "../utils/parse-cookie.js";
@@ -292,6 +292,7 @@ function setHeadersContext(ctx) {
292
292
  state.draftModeCookieHeader = null;
293
293
  state.phase = "render";
294
294
  } else {
295
+ if (state.headersContext && isInsideUnifiedScope()) getRequestContext().draftModeAtHeadersClear = getActiveDraftModeState() === true;
295
296
  state.headersContext = null;
296
297
  state.phase = "render";
297
298
  }
@@ -638,10 +639,11 @@ function getActiveDraftModeState() {
638
639
  /**
639
640
  * Read the active request's draft-mode state without recording request API usage.
640
641
  * Internal cache implementations use this to bypass persistent reads and writes,
641
- * matching Next.js's request-level `workStore.isDraftMode` guard.
642
+ * matching Next.js's request-level `workStore.isDraftMode` guard. Once the
643
+ * headers context is cleared, the request keeps the state it had then.
642
644
  */
643
645
  function isDraftModeEnabled() {
644
- return getActiveDraftModeState() ?? false;
646
+ return getActiveDraftModeState() ?? (isInsideUnifiedScope() && getRequestContext().draftModeAtHeadersClear === true);
645
647
  }
646
648
  function createDraftModeScopeError(expression) {
647
649
  return /* @__PURE__ */ new Error(`${expression} can only be called from a Server Component, Route Handler, or Server Action.`);
@@ -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
  }