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.
- package/README.md +20 -20
- package/dist/build/prerender.js +3 -2
- package/dist/check.js +3 -6
- package/dist/client/dev-error-overlay-store.d.ts +1 -0
- package/dist/client/dev-error-overlay.d.ts +2 -0
- package/dist/client/dev-error-overlay.js +19 -3
- package/dist/config/config-matchers.js +2 -0
- package/dist/config/destination-params.d.ts +2 -1
- package/dist/config/destination-params.js +52 -2
- package/dist/config/next-config.d.ts +6 -0
- package/dist/config/next-config.js +18 -3
- package/dist/entries/app-rsc-entry.js +0 -1
- package/dist/entries/pages-server-entry.js +3 -1
- package/dist/index.js +29 -20
- package/dist/plugins/strip-server-exports.js +2 -1
- package/dist/server/api-handler.js +2 -1
- package/dist/server/app-browser-entry.js +7 -3
- package/dist/server/app-browser-rsc-redirect.js +3 -3
- package/dist/server/app-browser-server-action-client.js +2 -2
- package/dist/server/app-browser-visible-commit.js +2 -2
- package/dist/server/app-optimistic-routing.js +2 -3
- package/dist/server/app-page-boundary-render.js +5 -10
- package/dist/server/app-page-dispatch.d.ts +0 -1
- package/dist/server/app-page-dispatch.js +0 -1
- package/dist/server/app-page-element-builder.js +3 -3
- package/dist/server/app-page-execution.js +9 -6
- package/dist/server/app-page-render.d.ts +0 -1
- package/dist/server/app-page-render.js +14 -14
- package/dist/server/app-page-response.js +1 -1
- package/dist/server/app-page-route-wiring.js +22 -5
- package/dist/server/app-page-stream.d.ts +10 -8
- package/dist/server/app-page-stream.js +2 -1
- package/dist/server/app-request-stage-independent-entry.js +1 -1
- package/dist/server/app-route-handler-execution.js +3 -2
- package/dist/server/app-route-handler-runtime.d.ts +1 -1
- package/dist/server/app-route-handler-runtime.js +11 -7
- package/dist/server/app-router-entry.js +1 -1
- package/dist/server/app-rsc-cache-busting.d.ts +10 -10
- package/dist/server/app-rsc-cache-busting.js +27 -68
- package/dist/server/app-rsc-error-handler.js +1 -1
- package/dist/server/app-rsc-errors.js +4 -1
- package/dist/server/app-rsc-handler.js +53 -62
- package/dist/server/app-rsc-request-normalization.d.ts +10 -12
- package/dist/server/app-rsc-request-normalization.js +11 -14
- package/dist/server/app-rsc-response-stage.js +4 -12
- package/dist/server/app-server-action-execution.js +8 -4
- package/dist/server/app-ssr-entry.d.ts +6 -0
- package/dist/server/app-ssr-entry.js +13 -8
- package/dist/server/app-worker-stages.d.ts +12 -3
- package/dist/server/app-worker-stages.js +37 -4
- package/dist/server/bfcache-identity.d.ts +7 -0
- package/dist/server/bfcache-identity.js +10 -1
- package/dist/server/cacheability-manifest.js +1 -2
- package/dist/server/dev-origin-check.d.ts +2 -1
- package/dist/server/dev-origin-check.js +2 -1
- package/dist/server/dev-server.js +2 -1
- package/dist/server/image-optimization.d.ts +5 -0
- package/dist/server/image-optimization.js +6 -1
- package/dist/server/isr-cache.js +4 -1
- package/dist/server/metadata-route-response.js +1 -1
- package/dist/server/middleware-matcher.js +19 -6
- package/dist/server/middleware-runtime.js +6 -2
- package/dist/server/multi-stage.d.ts +7 -3
- package/dist/server/pages-node-compat.d.ts +1 -1
- package/dist/server/pages-node-compat.js +2 -0
- package/dist/server/pages-page-data.js +2 -1
- package/dist/server/pages-page-handler.js +17 -6
- package/dist/server/pages-page-response.d.ts +9 -0
- package/dist/server/pages-page-response.js +18 -4
- package/dist/server/pages-request-stage-entry.js +24 -4
- package/dist/server/pages-router-entry.js +3 -2
- package/dist/server/prod-server.d.ts +2 -2
- package/dist/server/prod-server.js +42 -8
- package/dist/server/request-error-log.d.ts +9 -0
- package/dist/server/request-error-log.js +13 -0
- package/dist/server/request-pipeline.d.ts +9 -4
- package/dist/server/request-pipeline.js +23 -15
- package/dist/server/rsc-completion-metadata.js +3 -0
- package/dist/server/static-file-cache.d.ts +9 -0
- package/dist/server/static-file-cache.js +13 -2
- package/dist/shims/cache-request-state.d.ts +6 -6
- package/dist/shims/cache-request-state.js +6 -36
- package/dist/shims/cache-runtime.d.ts +3 -1
- package/dist/shims/cache-runtime.js +77 -47
- package/dist/shims/cache.d.ts +10 -5
- package/dist/shims/cache.js +35 -22
- package/dist/shims/client-hook-error.d.ts +2 -0
- package/dist/shims/client-hook-error.js +16 -1
- package/dist/shims/fetch-cache.js +6 -2
- package/dist/shims/headers.d.ts +2 -1
- package/dist/shims/headers.js +5 -3
- package/dist/shims/internal/cache-context-accessor.d.ts +1 -1
- package/dist/shims/link.js +5 -10
- package/dist/shims/navigation.js +2 -3
- package/dist/shims/request-state-types.d.ts +2 -1
- package/dist/shims/slot.d.ts +6 -2
- package/dist/shims/slot.js +10 -3
- package/dist/shims/unified-request-context.d.ts +6 -0
- package/dist/shims/unified-request-context.js +1 -0
- package/dist/utils/cache-life-profiles.d.ts +33 -0
- package/dist/utils/cache-life-profiles.js +111 -0
- package/dist/utils/middleware-request-headers.d.ts +14 -3
- package/dist/utils/middleware-request-headers.js +54 -7
- 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
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
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
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
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 "
|
|
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
|
|
194
|
-
*
|
|
195
|
-
*
|
|
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(
|
|
198
|
-
|
|
199
|
-
|
|
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
|
|
202
|
-
|
|
203
|
-
if (
|
|
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
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
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
|
-
|
|
499
|
+
write = handler.set(specificCacheKey, cacheValue, {
|
|
475
500
|
...cacheContext,
|
|
476
501
|
...staleEntry && specificCacheKey === cacheKey ? { staleEntry } : {}
|
|
477
502
|
});
|
|
478
|
-
} else
|
|
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
|
|
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,
|
|
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
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
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 });
|
package/dist/shims/cache.d.ts
CHANGED
|
@@ -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,
|
|
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
|
|
105
|
-
*
|
|
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 };
|
package/dist/shims/cache.js
CHANGED
|
@@ -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
|
|
234
|
-
*
|
|
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
|
-
|
|
246
|
-
|
|
247
|
-
|
|
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 = { ...
|
|
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:
|
|
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
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
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;
|
package/dist/shims/headers.d.ts
CHANGED
|
@@ -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 = {
|
package/dist/shims/headers.js
CHANGED
|
@@ -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() ??
|
|
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.`);
|
package/dist/shims/link.js
CHANGED
|
@@ -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
|
|
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
|
|
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 (
|
|
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(
|
|
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
|
-
|
|
800
|
+
setPending(false);
|
|
806
801
|
if (setter) clearLinkForCurrentNavigation(setter);
|
|
807
802
|
});
|
|
808
803
|
});
|
package/dist/shims/navigation.js
CHANGED
|
@@ -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
|
|
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
|
-
|
|
249
|
-
return stripRscSuffix(url.pathname);
|
|
248
|
+
return parseRscRequestPath(rscUrl).pathname;
|
|
250
249
|
} catch {
|
|
251
250
|
return null;
|
|
252
251
|
}
|