vinext 0.2.0 → 0.2.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/dist/build/css-url-assets.d.ts +3 -1
- package/dist/build/css-url-assets.js +18 -1
- package/dist/build/inject-pregenerated-paths.d.ts +3 -0
- package/dist/build/inject-pregenerated-paths.js +4 -1
- package/dist/build/prerender-server-entry.d.ts +1 -0
- package/dist/build/prerender-server-entry.js +49 -0
- package/dist/build/prerender-server-pool.d.ts +44 -0
- package/dist/build/prerender-server-pool.js +194 -0
- package/dist/build/prerender.js +86 -22
- package/dist/build/run-prerender.js +2 -1
- package/dist/check.d.ts +4 -6
- package/dist/check.js +8 -9
- package/dist/cli.js +1 -1
- package/dist/client/vinext-next-data.d.ts +4 -1
- package/dist/config/config-matchers.js +33 -11
- package/dist/config/next-config.d.ts +36 -6
- package/dist/config/next-config.js +16 -2
- package/dist/entries/app-browser-entry.js +5 -1
- package/dist/entries/app-rsc-entry.d.ts +3 -2
- package/dist/entries/app-rsc-entry.js +4 -0
- package/dist/entries/pages-client-entry.js +14 -3
- package/dist/entries/pages-server-entry.js +18 -4
- package/dist/index.js +320 -25
- package/dist/packages/cloudflare/src/cache/kv-data-adapter.runtime.js +8 -1
- package/dist/packages/cloudflare/src/deploy.js +1 -5
- package/dist/packages/cloudflare/src/tpr.js +26 -5
- package/dist/plugins/fonts.js +1 -1
- package/dist/plugins/ignore-dynamic-requests.js +1 -1
- package/dist/plugins/og-assets.js +2 -1
- package/dist/plugins/optimize-imports.js +1 -1
- package/dist/plugins/rsc-reference-validation-normalizer.d.ts +12 -0
- package/dist/plugins/rsc-reference-validation-normalizer.js +48 -0
- package/dist/plugins/sass.d.ts +17 -1
- package/dist/plugins/sass.js +74 -1
- package/dist/plugins/styled-jsx.d.ts +16 -0
- package/dist/plugins/styled-jsx.js +149 -0
- package/dist/routing/app-route-graph.js +22 -16
- package/dist/routing/file-matcher.d.ts +8 -1
- package/dist/routing/file-matcher.js +15 -3
- package/dist/server/app-browser-entry.js +46 -14
- package/dist/server/app-browser-navigation-controller.js +1 -1
- package/dist/server/app-browser-server-action-client.js +5 -3
- package/dist/server/app-browser-state.d.ts +1 -0
- package/dist/server/app-browser-state.js +2 -2
- package/dist/server/app-browser-visible-commit.js +1 -1
- package/dist/server/app-page-boundary.js +2 -1
- package/dist/server/app-page-cache-finalizer.d.ts +1 -0
- package/dist/server/app-page-cache-finalizer.js +3 -3
- package/dist/server/app-page-cache.js +23 -6
- package/dist/server/app-page-dispatch.d.ts +1 -0
- package/dist/server/app-page-dispatch.js +25 -8
- package/dist/server/app-page-element-builder.js +1 -0
- package/dist/server/app-page-execution.js +2 -1
- package/dist/server/app-page-render-identity.d.ts +1 -0
- package/dist/server/app-page-render-identity.js +2 -1
- package/dist/server/app-page-render.d.ts +2 -0
- package/dist/server/app-page-render.js +67 -16
- package/dist/server/app-page-response.d.ts +7 -0
- package/dist/server/app-page-response.js +16 -4
- package/dist/server/app-page-stream.d.ts +2 -1
- package/dist/server/app-page-stream.js +1 -1
- package/dist/server/app-route-handler-execution.js +1 -1
- package/dist/server/app-route-tree-prefetch.d.ts +43 -0
- package/dist/server/app-route-tree-prefetch.js +187 -0
- package/dist/server/app-router-entry.js +1 -1
- package/dist/server/app-rsc-cache-busting.d.ts +2 -1
- package/dist/server/app-rsc-cache-busting.js +9 -4
- package/dist/server/app-rsc-handler.d.ts +6 -0
- package/dist/server/app-rsc-handler.js +89 -11
- package/dist/server/app-rsc-render-mode.d.ts +3 -2
- package/dist/server/app-rsc-render-mode.js +4 -1
- package/dist/server/app-rsc-request-normalization.d.ts +3 -4
- package/dist/server/app-rsc-request-normalization.js +4 -5
- package/dist/server/app-server-action-execution.js +4 -3
- package/dist/server/app-ssr-entry.js +3 -3
- package/dist/server/cache-control.d.ts +3 -1
- package/dist/server/cache-control.js +13 -1
- package/dist/server/dev-server.d.ts +11 -1
- package/dist/server/dev-server.js +78 -20
- package/dist/server/headers.d.ts +6 -2
- package/dist/server/headers.js +11 -5
- package/dist/server/isr-cache.d.ts +2 -1
- package/dist/server/isr-cache.js +7 -3
- package/dist/server/pages-data-route.d.ts +4 -2
- package/dist/server/pages-data-route.js +18 -4
- package/dist/server/pages-dev-module-url.d.ts +2 -1
- package/dist/server/pages-dev-module-url.js +6 -3
- package/dist/server/pages-node-compat.d.ts +12 -1
- package/dist/server/pages-node-compat.js +50 -1
- package/dist/server/pages-page-data.d.ts +9 -0
- package/dist/server/pages-page-data.js +17 -9
- package/dist/server/pages-page-handler.js +19 -7
- package/dist/server/pages-page-response.d.ts +1 -0
- package/dist/server/pages-page-response.js +3 -2
- package/dist/server/pages-request-pipeline.d.ts +8 -6
- package/dist/server/pages-request-pipeline.js +30 -6
- package/dist/server/pages-router-entry.js +1 -1
- package/dist/server/prod-server.d.ts +4 -2
- package/dist/server/prod-server.js +38 -22
- package/dist/server/request-pipeline.js +1 -1
- package/dist/shims/cache-handler.js +8 -1
- package/dist/shims/cache.js +3 -0
- package/dist/shims/fetch-cache.d.ts +3 -1
- package/dist/shims/fetch-cache.js +77 -52
- package/dist/shims/form.d.ts +1 -1
- package/dist/shims/internal/app-prefetch-fetch-queue.d.ts +10 -0
- package/dist/shims/internal/app-prefetch-fetch-queue.js +61 -0
- package/dist/shims/internal/hybrid-client-route-owner.d.ts +2 -1
- package/dist/shims/internal/hybrid-client-route-owner.js +34 -1
- package/dist/shims/internal/pages-data-fetch-dedup.d.ts +2 -1
- package/dist/shims/internal/pages-data-fetch-dedup.js +13 -2
- package/dist/shims/internal/pages-data-target.d.ts +8 -3
- package/dist/shims/internal/pages-data-target.js +9 -4
- package/dist/shims/link.d.ts +1 -1
- package/dist/shims/link.js +179 -42
- package/dist/shims/navigation.d.ts +15 -3
- package/dist/shims/navigation.js +202 -39
- package/dist/shims/navigation.react-server.js +1 -1
- package/dist/shims/request-context.js +18 -0
- package/dist/shims/router.js +31 -15
- package/dist/shims/unified-request-context.js +1 -0
- package/dist/typegen.js +1 -1
- package/dist/utils/middleware-request-headers.js +1 -1
- package/dist/utils/protocol-headers.d.ts +7 -1
- package/dist/utils/protocol-headers.js +7 -1
- package/dist/utils/vite-version.d.ts +2 -0
- package/dist/utils/vite-version.js +11 -1
- package/package.json +2 -2
|
@@ -3,6 +3,7 @@ import { getRequestContext, isInsideUnifiedScope, runWithUnifiedStateMutation }
|
|
|
3
3
|
import { getRequestExecutionContext } from "./request-context.js";
|
|
4
4
|
import { getDataCacheHandler } from "./cache-handler.js";
|
|
5
5
|
import { markDynamicUsage } from "./headers.js";
|
|
6
|
+
import { _setRequestScopedCacheLife } from "./cache-request-state.js";
|
|
6
7
|
import { encodeCacheTags } from "../utils/encode-cache-tag.js";
|
|
7
8
|
//#region src/shims/fetch-cache.ts
|
|
8
9
|
/**
|
|
@@ -296,6 +297,7 @@ const _fallbackState = _g[_FALLBACK_KEY] ??= {
|
|
|
296
297
|
currentFetchCacheMode: null,
|
|
297
298
|
currentForceDynamicFetchDefault: false,
|
|
298
299
|
dynamicFetchUrls: /* @__PURE__ */ new Set(),
|
|
300
|
+
refreshStaleFetchesInForeground: false,
|
|
299
301
|
isFetchDedupeActive: false,
|
|
300
302
|
currentFetchDedupeEntries: /* @__PURE__ */ new Map()
|
|
301
303
|
};
|
|
@@ -314,6 +316,7 @@ function _resetFallbackState(isFetchDedupeActive) {
|
|
|
314
316
|
_fallbackState.currentFetchCacheMode = null;
|
|
315
317
|
_fallbackState.currentForceDynamicFetchDefault = false;
|
|
316
318
|
_fallbackState.dynamicFetchUrls = /* @__PURE__ */ new Set();
|
|
319
|
+
_fallbackState.refreshStaleFetchesInForeground = false;
|
|
317
320
|
_fallbackState.isFetchDedupeActive = isFetchDedupeActive;
|
|
318
321
|
_fallbackState.currentFetchDedupeEntries = /* @__PURE__ */ new Map();
|
|
319
322
|
}
|
|
@@ -330,6 +333,56 @@ function markUncachedFetchForPageOutput(input) {
|
|
|
330
333
|
function recordCacheableFetchObservation(input) {
|
|
331
334
|
_getState().cacheableFetchUrls.add(getFetchObservationUrl(input));
|
|
332
335
|
}
|
|
336
|
+
function recordFiniteFetchRevalidate(revalidateSeconds) {
|
|
337
|
+
if (Number.isFinite(revalidateSeconds) && revalidateSeconds > 0) _setRequestScopedCacheLife({ revalidate: revalidateSeconds });
|
|
338
|
+
}
|
|
339
|
+
function shouldRefreshStaleFetchInForeground() {
|
|
340
|
+
return _getState().refreshStaleFetchesInForeground;
|
|
341
|
+
}
|
|
342
|
+
async function buildFetchCacheValue(response, tags, revalidateSeconds, options) {
|
|
343
|
+
if (response.status !== 200) return null;
|
|
344
|
+
const responseForCache = options?.cloneForReturn === false ? response : response.clone();
|
|
345
|
+
const body = await responseForCache.text();
|
|
346
|
+
const headers = {};
|
|
347
|
+
responseForCache.headers.forEach((v, k) => {
|
|
348
|
+
if (k.toLowerCase() === "set-cookie") return;
|
|
349
|
+
headers[k] = v;
|
|
350
|
+
});
|
|
351
|
+
return {
|
|
352
|
+
kind: "FETCH",
|
|
353
|
+
data: {
|
|
354
|
+
headers,
|
|
355
|
+
body,
|
|
356
|
+
url: response.url,
|
|
357
|
+
status: responseForCache.status
|
|
358
|
+
},
|
|
359
|
+
tags,
|
|
360
|
+
revalidate: revalidateSeconds
|
|
361
|
+
};
|
|
362
|
+
}
|
|
363
|
+
async function writeFetchCacheResponse(handler, cacheKey, response, tags, revalidateSeconds, options) {
|
|
364
|
+
const cacheValue = await buildFetchCacheValue(response, tags, revalidateSeconds, options);
|
|
365
|
+
if (!cacheValue) return;
|
|
366
|
+
await handler.set(cacheKey, cacheValue, {
|
|
367
|
+
fetchCache: true,
|
|
368
|
+
tags,
|
|
369
|
+
revalidate: revalidateSeconds
|
|
370
|
+
});
|
|
371
|
+
}
|
|
372
|
+
async function lowerFetchCacheRevalidateIfNeeded(handler, cacheKey, cachedValue, tags, revalidateSeconds) {
|
|
373
|
+
if (!Number.isFinite(revalidateSeconds) || revalidateSeconds <= 0 || typeof cachedValue.revalidate !== "number" || cachedValue.revalidate <= revalidateSeconds) return;
|
|
374
|
+
const mergedTags = Array.from(new Set([...cachedValue.tags ?? [], ...tags]));
|
|
375
|
+
const updatedValue = {
|
|
376
|
+
...cachedValue,
|
|
377
|
+
tags: mergedTags,
|
|
378
|
+
revalidate: revalidateSeconds
|
|
379
|
+
};
|
|
380
|
+
await handler.set(cacheKey, updatedValue, {
|
|
381
|
+
fetchCache: true,
|
|
382
|
+
tags: mergedTags,
|
|
383
|
+
revalidate: revalidateSeconds
|
|
384
|
+
});
|
|
385
|
+
}
|
|
333
386
|
function peekCacheableFetchObservations() {
|
|
334
387
|
return [..._getState().cacheableFetchUrls].sort();
|
|
335
388
|
}
|
|
@@ -393,6 +446,9 @@ function setCurrentFetchCacheMode(mode) {
|
|
|
393
446
|
function setCurrentForceDynamicFetchDefault(enabled) {
|
|
394
447
|
_getState().currentForceDynamicFetchDefault = enabled;
|
|
395
448
|
}
|
|
449
|
+
function setRefreshStaleFetchesInForeground(enabled) {
|
|
450
|
+
_getState().refreshStaleFetchesInForeground = enabled;
|
|
451
|
+
}
|
|
396
452
|
function isNoStoreFetch(cacheDirective, nextOpts) {
|
|
397
453
|
return cacheDirective === "no-store" || cacheDirective === "no-cache" || nextOpts?.revalidate === 0;
|
|
398
454
|
}
|
|
@@ -562,6 +618,7 @@ function createPatchedFetch() {
|
|
|
562
618
|
return dedupeFetch(input, cleanInit);
|
|
563
619
|
}
|
|
564
620
|
recordCacheableFetchObservation(input);
|
|
621
|
+
recordFiniteFetchRevalidate(revalidateSeconds);
|
|
565
622
|
const reqTags = _getState().currentRequestTags;
|
|
566
623
|
const tags = encodeCacheTags(nextOpts?.tags ?? []);
|
|
567
624
|
if (tags.length > 0) {
|
|
@@ -586,39 +643,24 @@ function createPatchedFetch() {
|
|
|
586
643
|
const cached = await handler.get(cacheKey, {
|
|
587
644
|
kind: "FETCH",
|
|
588
645
|
tags,
|
|
589
|
-
softTags
|
|
646
|
+
softTags,
|
|
647
|
+
revalidate: revalidateSeconds
|
|
590
648
|
});
|
|
591
649
|
if (cached?.value && cached.value.kind === "FETCH" && cached.cacheState !== "stale") {
|
|
650
|
+
await lowerFetchCacheRevalidateIfNeeded(handler, cacheKey, cached.value, tags, revalidateSeconds);
|
|
592
651
|
const cachedData = cached.value.data;
|
|
593
652
|
return buildCachedFetchResponse(cachedData, input);
|
|
594
653
|
}
|
|
595
654
|
if (cached?.value && cached.value.kind === "FETCH" && cached.cacheState === "stale") {
|
|
655
|
+
if (shouldRefreshStaleFetchInForeground()) {
|
|
656
|
+
const freshResponse = await dedupeFetch(input, fetchInit);
|
|
657
|
+
await writeFetchCacheResponse(handler, cacheKey, freshResponse, tags, revalidateSeconds);
|
|
658
|
+
return freshResponse;
|
|
659
|
+
}
|
|
596
660
|
const staleData = cached.value.data;
|
|
597
661
|
if (!pendingRefetches.has(cacheKey)) {
|
|
598
662
|
const refetchPromise = originalFetch(input, fetchInit).then(async (freshResp) => {
|
|
599
|
-
|
|
600
|
-
const freshBody = await freshResp.text();
|
|
601
|
-
const freshHeaders = {};
|
|
602
|
-
freshResp.headers.forEach((v, k) => {
|
|
603
|
-
if (k.toLowerCase() === "set-cookie") return;
|
|
604
|
-
freshHeaders[k] = v;
|
|
605
|
-
});
|
|
606
|
-
const freshValue = {
|
|
607
|
-
kind: "FETCH",
|
|
608
|
-
data: {
|
|
609
|
-
headers: freshHeaders,
|
|
610
|
-
body: freshBody,
|
|
611
|
-
url: freshResp.url,
|
|
612
|
-
status: freshResp.status
|
|
613
|
-
},
|
|
614
|
-
tags,
|
|
615
|
-
revalidate: revalidateSeconds
|
|
616
|
-
};
|
|
617
|
-
await handler.set(cacheKey, freshValue, {
|
|
618
|
-
fetchCache: true,
|
|
619
|
-
tags,
|
|
620
|
-
revalidate: revalidateSeconds
|
|
621
|
-
});
|
|
663
|
+
await writeFetchCacheResponse(handler, cacheKey, freshResp, tags, revalidateSeconds, { cloneForReturn: false });
|
|
622
664
|
}).catch((err) => {
|
|
623
665
|
const url = typeof input === "string" ? input : input instanceof URL ? input.toString() : input.url;
|
|
624
666
|
console.error(`[vinext] fetch cache background revalidation failed for ${url} (key=${cacheKey.slice(0, 12)}...):`, err);
|
|
@@ -638,33 +680,14 @@ function createPatchedFetch() {
|
|
|
638
680
|
console.error("[vinext] fetch cache read error:", cacheErr);
|
|
639
681
|
}
|
|
640
682
|
const response = await dedupeFetch(input, fetchInit);
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
const cacheValue = {
|
|
650
|
-
kind: "FETCH",
|
|
651
|
-
data: {
|
|
652
|
-
headers,
|
|
653
|
-
body,
|
|
654
|
-
url: response.url,
|
|
655
|
-
status: cloned.status
|
|
656
|
-
},
|
|
657
|
-
tags,
|
|
658
|
-
revalidate: revalidateSeconds
|
|
659
|
-
};
|
|
660
|
-
handler.set(cacheKey, cacheValue, {
|
|
661
|
-
fetchCache: true,
|
|
662
|
-
tags,
|
|
663
|
-
revalidate: revalidateSeconds
|
|
664
|
-
}).catch((err) => {
|
|
665
|
-
console.error("[vinext] fetch cache write error:", err);
|
|
666
|
-
});
|
|
667
|
-
}
|
|
683
|
+
const cacheValue = await buildFetchCacheValue(response, tags, revalidateSeconds);
|
|
684
|
+
if (cacheValue) handler.set(cacheKey, cacheValue, {
|
|
685
|
+
fetchCache: true,
|
|
686
|
+
tags,
|
|
687
|
+
revalidate: revalidateSeconds
|
|
688
|
+
}).catch((err) => {
|
|
689
|
+
console.error("[vinext] fetch cache write error:", err);
|
|
690
|
+
});
|
|
668
691
|
return response;
|
|
669
692
|
};
|
|
670
693
|
}
|
|
@@ -716,6 +739,7 @@ async function runWithFetchCache(fn) {
|
|
|
716
739
|
uCtx.currentRequestTags = [];
|
|
717
740
|
uCtx.currentFetchSoftTags = [];
|
|
718
741
|
uCtx.dynamicFetchUrls = /* @__PURE__ */ new Set();
|
|
742
|
+
uCtx.refreshStaleFetchesInForeground = false;
|
|
719
743
|
uCtx.isFetchDedupeActive = true;
|
|
720
744
|
uCtx.currentFetchDedupeEntries = /* @__PURE__ */ new Map();
|
|
721
745
|
}, fn);
|
|
@@ -726,6 +750,7 @@ async function runWithFetchCache(fn) {
|
|
|
726
750
|
currentFetchCacheMode: null,
|
|
727
751
|
currentForceDynamicFetchDefault: false,
|
|
728
752
|
dynamicFetchUrls: /* @__PURE__ */ new Set(),
|
|
753
|
+
refreshStaleFetchesInForeground: false,
|
|
729
754
|
isFetchDedupeActive: true,
|
|
730
755
|
currentFetchDedupeEntries: /* @__PURE__ */ new Map()
|
|
731
756
|
}, fn);
|
|
@@ -763,4 +788,4 @@ function getOriginalFetch() {
|
|
|
763
788
|
return originalFetch;
|
|
764
789
|
}
|
|
765
790
|
//#endregion
|
|
766
|
-
export { _resetPendingRefetches, addCollectedRequestTags, consumeDynamicFetchObservations, ensureFetchPatch, getCollectedFetchTags, getCurrentFetchSoftTags, getOriginalFetch, peekCacheableFetchObservations, peekDynamicFetchObservations, runWithFetchCache, runWithFetchDedupe, setCurrentFetchCacheMode, setCurrentFetchSoftTags, setCurrentForceDynamicFetchDefault, withFetchCache };
|
|
791
|
+
export { _resetPendingRefetches, addCollectedRequestTags, consumeDynamicFetchObservations, ensureFetchPatch, getCollectedFetchTags, getCurrentFetchSoftTags, getOriginalFetch, peekCacheableFetchObservations, peekDynamicFetchObservations, runWithFetchCache, runWithFetchDedupe, setCurrentFetchCacheMode, setCurrentFetchSoftTags, setCurrentForceDynamicFetchDefault, setRefreshStaleFetchesInForeground, withFetchCache };
|
package/dist/shims/form.d.ts
CHANGED
|
@@ -16,6 +16,6 @@ declare const Form: import("react").ForwardRefExoticComponent<{
|
|
|
16
16
|
* In pages dir, prefetch is not supported and the prop has no effect.
|
|
17
17
|
*/
|
|
18
18
|
prefetch?: false | null;
|
|
19
|
-
} & Omit<FormHTMLAttributes<HTMLFormElement>, "
|
|
19
|
+
} & Omit<FormHTMLAttributes<HTMLFormElement>, "target" | "method" | "encType"> & import("react").RefAttributes<HTMLFormElement>>;
|
|
20
20
|
//#endregion
|
|
21
21
|
export { createFormSubmitDestinationUrl, Form as default, useActionState };
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
//#region src/shims/internal/app-prefetch-fetch-queue.d.ts
|
|
2
|
+
declare function releaseAppPrefetchFetchSlot(response: Response): void;
|
|
3
|
+
/**
|
|
4
|
+
* Low-priority App Router prefetches share a small request queue. The consumer
|
|
5
|
+
* must either snapshot the returned Response with snapshotRscResponse() or call
|
|
6
|
+
* releaseAppPrefetchFetchSlot() when it drops the response without consuming it.
|
|
7
|
+
*/
|
|
8
|
+
declare function scheduleAppPrefetchFetch(fetcher: () => Promise<Response>, priority: "low" | "high"): Promise<Response>;
|
|
9
|
+
//#endregion
|
|
10
|
+
export { releaseAppPrefetchFetchSlot, scheduleAppPrefetchFetch };
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
//#region src/shims/internal/app-prefetch-fetch-queue.ts
|
|
3
|
+
const APP_PREFETCH_FETCH_SLOT_RELEASE_KEY = Symbol.for("vinext.appPrefetchFetchSlotRelease");
|
|
4
|
+
const MAX_DEFAULT_APP_PREFETCH_REQUESTS = 4;
|
|
5
|
+
const defaultAppPrefetchQueue = [];
|
|
6
|
+
let activeDefaultAppPrefetchRequests = 0;
|
|
7
|
+
let defaultAppPrefetchDrainScheduled = false;
|
|
8
|
+
function drainDefaultAppPrefetchQueue() {
|
|
9
|
+
defaultAppPrefetchDrainScheduled = false;
|
|
10
|
+
while (activeDefaultAppPrefetchRequests < MAX_DEFAULT_APP_PREFETCH_REQUESTS) {
|
|
11
|
+
const run = defaultAppPrefetchQueue.shift();
|
|
12
|
+
if (!run) return;
|
|
13
|
+
activeDefaultAppPrefetchRequests += 1;
|
|
14
|
+
run();
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
function scheduleDefaultAppPrefetchDrain() {
|
|
18
|
+
if (defaultAppPrefetchDrainScheduled) return;
|
|
19
|
+
defaultAppPrefetchDrainScheduled = true;
|
|
20
|
+
queueMicrotask(drainDefaultAppPrefetchQueue);
|
|
21
|
+
}
|
|
22
|
+
function releaseAppPrefetchFetchSlot(response) {
|
|
23
|
+
const release = response[APP_PREFETCH_FETCH_SLOT_RELEASE_KEY];
|
|
24
|
+
if (release === void 0) return;
|
|
25
|
+
response[APP_PREFETCH_FETCH_SLOT_RELEASE_KEY] = void 0;
|
|
26
|
+
release();
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Low-priority App Router prefetches share a small request queue. The consumer
|
|
30
|
+
* must either snapshot the returned Response with snapshotRscResponse() or call
|
|
31
|
+
* releaseAppPrefetchFetchSlot() when it drops the response without consuming it.
|
|
32
|
+
*/
|
|
33
|
+
function scheduleAppPrefetchFetch(fetcher, priority) {
|
|
34
|
+
if (priority === "high") return fetcher();
|
|
35
|
+
return new Promise((resolve, reject) => {
|
|
36
|
+
defaultAppPrefetchQueue.push(() => {
|
|
37
|
+
let didRelease = false;
|
|
38
|
+
const release = () => {
|
|
39
|
+
if (didRelease) return;
|
|
40
|
+
didRelease = true;
|
|
41
|
+
activeDefaultAppPrefetchRequests -= 1;
|
|
42
|
+
drainDefaultAppPrefetchQueue();
|
|
43
|
+
};
|
|
44
|
+
try {
|
|
45
|
+
fetcher().then((response) => {
|
|
46
|
+
response[APP_PREFETCH_FETCH_SLOT_RELEASE_KEY] = release;
|
|
47
|
+
resolve(response);
|
|
48
|
+
}, (error) => {
|
|
49
|
+
release();
|
|
50
|
+
reject(error);
|
|
51
|
+
});
|
|
52
|
+
} catch (error) {
|
|
53
|
+
release();
|
|
54
|
+
reject(error);
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
scheduleDefaultAppPrefetchDrain();
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
//#endregion
|
|
61
|
+
export { releaseAppPrefetchFetchSlot, scheduleAppPrefetchFetch };
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { HybridClientOwner } from "./hybrid-client-route-owner-direct.js";
|
|
2
2
|
|
|
3
3
|
//#region src/shims/internal/hybrid-client-route-owner.d.ts
|
|
4
|
+
declare function resolveHybridClientRewriteHref(href: string, basePath: string): string | null;
|
|
4
5
|
/**
|
|
5
6
|
* Decide which router should own a soft-navigated URL. Returns:
|
|
6
7
|
* - "app" → the App Router runtime handles the navigation (RSC fetch).
|
|
@@ -15,4 +16,4 @@ import { HybridClientOwner } from "./hybrid-client-route-owner-direct.js";
|
|
|
15
16
|
*/
|
|
16
17
|
declare function resolveHybridClientRouteOwner(href: string, basePath: string): HybridClientOwner | null;
|
|
17
18
|
//#endregion
|
|
18
|
-
export { type HybridClientOwner, resolveHybridClientRouteOwner };
|
|
19
|
+
export { type HybridClientOwner, resolveHybridClientRewriteHref, resolveHybridClientRouteOwner };
|
|
@@ -49,6 +49,39 @@ function resolveClientRewrite(href, basePath, rewrites, continueAfterMatch = fal
|
|
|
49
49
|
kind: "rewrite"
|
|
50
50
|
} : null;
|
|
51
51
|
}
|
|
52
|
+
function resolveHybridClientRewriteHref(href, basePath) {
|
|
53
|
+
if (typeof window === "undefined") return null;
|
|
54
|
+
const rewrites = window.__VINEXT_CLIENT_REWRITES__;
|
|
55
|
+
if (!rewrites) return null;
|
|
56
|
+
let currentHref = href;
|
|
57
|
+
let didRewrite = false;
|
|
58
|
+
const beforeFilesRewrite = resolveClientRewrite(currentHref, basePath, rewrites.beforeFiles, true);
|
|
59
|
+
if (beforeFilesRewrite?.kind === "document") return null;
|
|
60
|
+
if (beforeFilesRewrite?.kind === "rewrite") {
|
|
61
|
+
currentHref = beforeFilesRewrite.href;
|
|
62
|
+
didRewrite = true;
|
|
63
|
+
}
|
|
64
|
+
let matches = matchDirectHybridClientRoutes(currentHref, basePath);
|
|
65
|
+
if ((matches.appMatch === null || matches.appMatch.isDynamic) && (matches.pagesMatch === null || matches.pagesMatch.isDynamic)) for (const rewrite of rewrites.afterFiles) {
|
|
66
|
+
const afterFilesRewrite = resolveClientRewrite(currentHref, basePath, [rewrite]);
|
|
67
|
+
if (afterFilesRewrite?.kind === "document") return didRewrite ? currentHref : null;
|
|
68
|
+
if (afterFilesRewrite?.kind !== "rewrite") continue;
|
|
69
|
+
currentHref = afterFilesRewrite.href;
|
|
70
|
+
didRewrite = true;
|
|
71
|
+
matches = matchDirectHybridClientRoutes(currentHref, basePath);
|
|
72
|
+
if (matches.appMatch || matches.pagesMatch) break;
|
|
73
|
+
}
|
|
74
|
+
if (matches.appMatch === null && matches.pagesMatch === null) for (const rewrite of rewrites.fallback) {
|
|
75
|
+
const fallbackRewrite = resolveClientRewrite(currentHref, basePath, [rewrite]);
|
|
76
|
+
if (fallbackRewrite?.kind === "document") return didRewrite ? currentHref : null;
|
|
77
|
+
if (fallbackRewrite?.kind !== "rewrite") continue;
|
|
78
|
+
currentHref = fallbackRewrite.href;
|
|
79
|
+
didRewrite = true;
|
|
80
|
+
matches = matchDirectHybridClientRoutes(currentHref, basePath);
|
|
81
|
+
if (matches.appMatch || matches.pagesMatch) break;
|
|
82
|
+
}
|
|
83
|
+
return didRewrite ? currentHref : null;
|
|
84
|
+
}
|
|
52
85
|
/**
|
|
53
86
|
* Decide which router should own a soft-navigated URL. Returns:
|
|
54
87
|
* - "app" → the App Router runtime handles the navigation (RSC fetch).
|
|
@@ -89,4 +122,4 @@ function resolveHybridClientRouteOwner(href, basePath) {
|
|
|
89
122
|
return resolveMatchedHybridClientRouteOwner(matches);
|
|
90
123
|
}
|
|
91
124
|
//#endregion
|
|
92
|
-
export { resolveHybridClientRouteOwner };
|
|
125
|
+
export { resolveHybridClientRewriteHref, resolveHybridClientRouteOwner };
|
|
@@ -35,6 +35,7 @@
|
|
|
35
35
|
declare function getPagesStaticDataCache(): Record<string, Promise<Response>>;
|
|
36
36
|
declare function fetchCachedPagesData(dataHref: string, init?: RequestInit): Promise<Response>;
|
|
37
37
|
declare function fetchStaticPagesData(dataHref: string, init?: RequestInit): Promise<Response>;
|
|
38
|
+
declare function fetchUncachedPagesData(dataHref: string, init?: RequestInit): Promise<Response>;
|
|
38
39
|
declare function evictPagesDataCache(dataHref: string): void;
|
|
39
40
|
/**
|
|
40
41
|
* Dedupe a `fetch()` against the `_next/data` endpoint. Multiple concurrent
|
|
@@ -55,4 +56,4 @@ declare function dedupedPagesDataFetch(dataHref: string, init?: RequestInit): Pr
|
|
|
55
56
|
*/
|
|
56
57
|
declare function clearPagesDataInflight(): void;
|
|
57
58
|
//#endregion
|
|
58
|
-
export { clearPagesDataInflight, dedupedPagesDataFetch, evictPagesDataCache, fetchCachedPagesData, fetchStaticPagesData, getPagesStaticDataCache };
|
|
59
|
+
export { clearPagesDataInflight, dedupedPagesDataFetch, evictPagesDataCache, fetchCachedPagesData, fetchStaticPagesData, fetchUncachedPagesData, getPagesStaticDataCache };
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import "../../utils/protocol-headers.js";
|
|
1
2
|
import { getDeploymentId } from "../../utils/deployment-id.js";
|
|
2
3
|
//#region src/shims/internal/pages-data-fetch-dedup.ts
|
|
3
4
|
/**
|
|
@@ -71,7 +72,7 @@ function fetchCachedPagesData(dataHref, init) {
|
|
|
71
72
|
cached = dedupedPagesDataFetch(dataHref, sharedInit).then((response) => {
|
|
72
73
|
const expectedDeploymentId = getDeploymentId() ?? null;
|
|
73
74
|
const responseDeploymentId = response.headers.get("x-nextjs-deployment-id");
|
|
74
|
-
if (!response.ok || response.headers.get("x-middleware-cache") === "no-cache" || responseDeploymentId !== null && responseDeploymentId !== expectedDeploymentId) {
|
|
75
|
+
if (!response.ok || response.headers.get("x-middleware-cache") === "no-cache" || response.headers.get("x-middleware-skip") !== null || responseDeploymentId !== null && responseDeploymentId !== expectedDeploymentId) {
|
|
75
76
|
delete staticDataCache[key];
|
|
76
77
|
staticDataSources.delete(key);
|
|
77
78
|
}
|
|
@@ -91,6 +92,16 @@ function fetchCachedPagesData(dataHref, init) {
|
|
|
91
92
|
function fetchStaticPagesData(dataHref, init) {
|
|
92
93
|
return fetchCachedPagesData(dataHref, init);
|
|
93
94
|
}
|
|
95
|
+
function fetchUncachedPagesData(dataHref, init) {
|
|
96
|
+
return fetch(dataHref, init).then(async (response) => {
|
|
97
|
+
const body = await response.arrayBuffer();
|
|
98
|
+
return new Response(body, {
|
|
99
|
+
headers: response.headers,
|
|
100
|
+
status: response.status,
|
|
101
|
+
statusText: response.statusText
|
|
102
|
+
});
|
|
103
|
+
});
|
|
104
|
+
}
|
|
94
105
|
function evictPagesDataCache(dataHref) {
|
|
95
106
|
const key = getStaticDataKey(dataHref);
|
|
96
107
|
delete staticDataCache[key];
|
|
@@ -177,4 +188,4 @@ function clearPagesDataInflight() {
|
|
|
177
188
|
for (const key of Object.keys(staticDataCache)) delete staticDataCache[key];
|
|
178
189
|
}
|
|
179
190
|
//#endregion
|
|
180
|
-
export { clearPagesDataInflight, dedupedPagesDataFetch, evictPagesDataCache, fetchCachedPagesData, fetchStaticPagesData, getPagesStaticDataCache };
|
|
191
|
+
export { clearPagesDataInflight, dedupedPagesDataFetch, evictPagesDataCache, fetchCachedPagesData, fetchStaticPagesData, fetchUncachedPagesData, getPagesStaticDataCache };
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
//#region src/shims/internal/pages-data-target.d.ts
|
|
2
2
|
type PagesDataTarget = {
|
|
3
|
-
/** Final fetch URL for the data endpoint, including basePath and search. */dataHref: string; /**
|
|
3
|
+
/** Final fetch URL for the data endpoint, including basePath and search. */dataHref: string; /** Locale-qualified data endpoint used by Pages Router prefetch. */
|
|
4
|
+
prefetchDataHref?: string; /** Matched route pattern (e.g. `/blog/[slug]`). */
|
|
4
5
|
pattern: string; /** Dynamic params extracted from the URL by the pattern matcher. */
|
|
5
6
|
params: Record<string, string | string[]>; /** Code-split loader thunk for the matched route's page module. */
|
|
6
7
|
loader: () => Promise<{
|
|
@@ -11,7 +12,8 @@ type PagesDataTarget = {
|
|
|
11
12
|
middlewareDataHref?: string; /** Current buildId snapshot, used by the data URL and consistency checks. */
|
|
12
13
|
buildId: string; /** Locale-prefixed (server-routable) page path. */
|
|
13
14
|
pagePath: string; /** URL search string including the leading `?`. */
|
|
14
|
-
search: string;
|
|
15
|
+
search: string; /** Locale selected for middleware-prefetch data URLs when the visible URL is unprefixed. */
|
|
16
|
+
prefetchLocale?: string;
|
|
15
17
|
/**
|
|
16
18
|
* Locale prefix detected on the URL, or `undefined` when the URL is
|
|
17
19
|
* unprefixed (default locale, or no i18n configured). Lets the caller refresh
|
|
@@ -20,6 +22,9 @@ type PagesDataTarget = {
|
|
|
20
22
|
*/
|
|
21
23
|
locale: string | undefined;
|
|
22
24
|
};
|
|
25
|
+
type PagesDataNavigationTargetOptions = {
|
|
26
|
+
locale?: string | false;
|
|
27
|
+
};
|
|
23
28
|
declare function getPagesMiddlewareDataHref(browserUrl: string, basePath: string): string | null;
|
|
24
29
|
/**
|
|
25
30
|
* Decide whether the JSON data-endpoint navigation path is usable for this
|
|
@@ -44,7 +49,7 @@ declare function getPagesMiddlewareDataHref(browserUrl: string, basePath: string
|
|
|
44
49
|
* (`getDataHref`). vinext's equivalent uses an in-memory loader map instead
|
|
45
50
|
* of Next.js' `_buildManifest.js`.
|
|
46
51
|
*/
|
|
47
|
-
declare function resolvePagesDataNavigationTarget(browserUrl: string, basePath: string): PagesDataTarget | null;
|
|
52
|
+
declare function resolvePagesDataNavigationTarget(browserUrl: string, basePath: string, options?: PagesDataNavigationTargetOptions): PagesDataTarget | null;
|
|
48
53
|
/**
|
|
49
54
|
* Kick off the code-split loader and, for SSG pages, prefetch the data JSON so
|
|
50
55
|
* the chunk and payload are warm by the time the user clicks.
|
|
@@ -3,7 +3,7 @@ import { isUnknownRecord } from "../../utils/record.js";
|
|
|
3
3
|
import { getLocalePathPrefix } from "../../utils/domain-locale.js";
|
|
4
4
|
import { buildPagesDataHref, matchPagesPattern } from "./pages-data-url.js";
|
|
5
5
|
import { NEXT_DEPLOYMENT_ID_HEADER, getDeploymentId } from "../../utils/deployment-id.js";
|
|
6
|
-
import {
|
|
6
|
+
import { fetchStaticPagesData, fetchUncachedPagesData } from "./pages-data-fetch-dedup.js";
|
|
7
7
|
//#region src/shims/internal/pages-data-target.ts
|
|
8
8
|
/**
|
|
9
9
|
* Shared decision helper for the Pages Router `/_next/data/<id>/<page>.json`
|
|
@@ -106,7 +106,7 @@ function getPagesMiddlewareDataHref(browserUrl, basePath) {
|
|
|
106
106
|
* (`getDataHref`). vinext's equivalent uses an in-memory loader map instead
|
|
107
107
|
* of Next.js' `_buildManifest.js`.
|
|
108
108
|
*/
|
|
109
|
-
function resolvePagesDataNavigationTarget(browserUrl, basePath) {
|
|
109
|
+
function resolvePagesDataNavigationTarget(browserUrl, basePath, options = {}) {
|
|
110
110
|
if (typeof window === "undefined") return null;
|
|
111
111
|
const loaders = window.__VINEXT_PAGE_LOADERS__;
|
|
112
112
|
const patterns = window.__VINEXT_PAGE_PATTERNS__;
|
|
@@ -129,8 +129,12 @@ function resolvePagesDataNavigationTarget(browserUrl, basePath) {
|
|
|
129
129
|
const ssgPatterns = window.__VINEXT_PAGES_SSG_PATTERNS__;
|
|
130
130
|
const sspPatterns = window.__VINEXT_PAGES_SSP_PATTERNS__;
|
|
131
131
|
const dataKind = ssgPatterns?.includes(match.pattern) ? "static" : sspPatterns?.includes(match.pattern) ? "server" : ssgPatterns === void 0 || sspPatterns === void 0 ? "server" : "none";
|
|
132
|
+
const explicitLocale = options.locale === false ? window.__VINEXT_DEFAULT_LOCALE__ : options.locale;
|
|
133
|
+
const currentLocale = locale ?? explicitLocale ?? window.__VINEXT_LOCALE__;
|
|
134
|
+
const prefetchPagePath = locale || !currentLocale || !window.__VINEXT_LOCALES__?.includes(currentLocale) ? pagePath : `/${currentLocale}${pagePath === "/" ? "" : pagePath}`;
|
|
132
135
|
return {
|
|
133
136
|
dataHref: buildPagesDataHref(basePath, buildId, pagePath, parsed.search),
|
|
137
|
+
prefetchDataHref: buildPagesDataHref(basePath, buildId, prefetchPagePath, parsed.search),
|
|
134
138
|
pattern: match.pattern,
|
|
135
139
|
params: match.params,
|
|
136
140
|
loader,
|
|
@@ -139,6 +143,7 @@ function resolvePagesDataNavigationTarget(browserUrl, basePath) {
|
|
|
139
143
|
buildId,
|
|
140
144
|
pagePath,
|
|
141
145
|
search: parsed.search,
|
|
146
|
+
prefetchLocale: currentLocale,
|
|
142
147
|
locale
|
|
143
148
|
};
|
|
144
149
|
}
|
|
@@ -168,10 +173,10 @@ function prefetchPagesData(target) {
|
|
|
168
173
|
const deploymentId = getDeploymentId();
|
|
169
174
|
if (deploymentId) headers[NEXT_DEPLOYMENT_ID_HEADER] = deploymentId;
|
|
170
175
|
if (target.dataKind === "static") {
|
|
171
|
-
fetchStaticPagesData(target.middlewareDataHref ?? target.dataHref, { headers }).catch(() => {});
|
|
176
|
+
fetchStaticPagesData(target.middlewareDataHref && target.middlewareDataHref !== target.dataHref ? target.middlewareDataHref : target.prefetchDataHref ?? target.middlewareDataHref ?? target.dataHref, { headers }).then((response) => response.arrayBuffer()).catch(() => {});
|
|
172
177
|
return;
|
|
173
178
|
}
|
|
174
|
-
if (target.middlewareDataHref)
|
|
179
|
+
if (target.middlewareDataHref) fetchUncachedPagesData(target.middlewareDataHref, { headers }).catch(() => {});
|
|
175
180
|
}
|
|
176
181
|
//#endregion
|
|
177
182
|
export { getPagesMiddlewareDataHref, prefetchPagesData, resolvePagesDataNavigationTarget };
|
package/dist/shims/link.d.ts
CHANGED
|
@@ -42,7 +42,7 @@ type LinkProps = {
|
|
|
42
42
|
onNavigate?: (event: NavigateEvent) => void;
|
|
43
43
|
children?: React.ReactNode;
|
|
44
44
|
} & Omit<AnchorHTMLAttributes<HTMLAnchorElement>, "href">;
|
|
45
|
-
type LinkPrefetchMode = "disabled" | "auto" | "full";
|
|
45
|
+
type LinkPrefetchMode = "disabled" | "auto" | "full" | "full-after-shell";
|
|
46
46
|
declare global {
|
|
47
47
|
interface Window {
|
|
48
48
|
__VINEXT_LINK_PREFETCH_ROUTES__?: VinextLinkPrefetchRoute[];
|