vinext 0.1.8 → 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/README.md +51 -30
- 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 +7 -0
- package/dist/build/inject-pregenerated-paths.js +21 -0
- package/dist/build/layout-classification.js +1 -1
- package/dist/build/nitro-route-rules.js +1 -1
- package/dist/build/pages-client-assets-module.d.ts +11 -0
- package/dist/build/pages-client-assets-module.js +27 -0
- 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.d.ts +2 -1
- package/dist/build/prerender.js +98 -27
- package/dist/build/report.d.ts +13 -2
- package/dist/build/report.js +99 -2
- package/dist/build/run-prerender.d.ts +7 -0
- package/dist/build/run-prerender.js +12 -2
- package/dist/build/standalone.js +2 -0
- package/dist/cache/cache-adapters-virtual.js +3 -3
- package/dist/check.d.ts +4 -6
- package/dist/check.js +48 -11
- package/dist/cli-args.d.ts +1 -2
- package/dist/cli-args.js +1 -1
- package/dist/cli-dev-config.d.ts +12 -0
- package/dist/cli-dev-config.js +23 -0
- package/dist/cli.js +112 -96
- package/dist/{server → client}/dev-error-overlay-store.d.ts +1 -1
- package/dist/{server → client}/dev-error-overlay-store.js +1 -1
- package/dist/{server → client}/dev-error-overlay.d.ts +1 -1
- package/dist/{server → client}/dev-error-overlay.js +2 -2
- package/dist/client/navigation-runtime.d.ts +2 -0
- package/dist/client/navigation-runtime.js +3 -1
- package/dist/client/vinext-next-data.d.ts +4 -1
- package/dist/client/window-next.d.ts +2 -1
- package/dist/cloudflare/index.d.ts +2 -2
- package/dist/cloudflare/index.js +2 -2
- package/dist/config/config-matchers.js +45 -19
- package/dist/config/next-config.d.ts +41 -9
- package/dist/config/next-config.js +60 -3
- package/dist/config/prerender.d.ts +34 -0
- package/dist/config/prerender.js +55 -0
- package/dist/entries/app-browser-entry.js +5 -1
- package/dist/entries/app-rsc-entry.d.ts +5 -3
- package/dist/entries/app-rsc-entry.js +44 -10
- package/dist/entries/app-rsc-manifest.js +2 -0
- package/dist/entries/pages-client-entry.d.ts +3 -0
- package/dist/entries/pages-client-entry.js +28 -3
- package/dist/entries/pages-server-entry.js +23 -6
- package/dist/image/image-adapters-virtual.d.ts +59 -0
- package/dist/image/image-adapters-virtual.js +59 -0
- package/dist/index.d.ts +33 -0
- package/dist/index.js +482 -120
- package/dist/init-cloudflare.d.ts +43 -0
- package/dist/init-cloudflare.js +839 -0
- package/dist/init-platform.d.ts +40 -0
- package/dist/init-platform.js +195 -0
- package/dist/init.d.ts +16 -38
- package/dist/init.js +209 -99
- package/dist/node_modules/.pnpm/am-i-vibing@0.5.0/node_modules/am-i-vibing/dist/detector-1yx2Hoe0.js +294 -0
- package/dist/node_modules/.pnpm/process-ancestry@0.1.0/node_modules/process-ancestry/dist/index.js +94 -0
- package/dist/{cloudflare → packages/cloudflare}/src/cache/cdn-adapter.runtime.js +1 -1
- package/dist/{cloudflare → packages/cloudflare}/src/cache/kv-data-adapter.runtime.d.ts +2 -2
- package/dist/{cloudflare → packages/cloudflare}/src/cache/kv-data-adapter.runtime.js +9 -2
- package/dist/packages/cloudflare/src/deploy-config.js +150 -0
- package/dist/packages/cloudflare/src/deploy-help.js +55 -0
- package/dist/packages/cloudflare/src/deploy.js +272 -0
- package/dist/{cloudflare → packages/cloudflare/src}/tpr.d.ts +4 -41
- package/dist/{cloudflare → packages/cloudflare/src}/tpr.js +44 -20
- package/dist/plugins/ast-scope.d.ts +16 -0
- package/dist/plugins/ast-scope.js +62 -0
- package/dist/plugins/ast-utils.d.ts +27 -1
- package/dist/plugins/ast-utils.js +32 -1
- package/dist/plugins/css-module-imports.d.ts +14 -0
- package/dist/plugins/css-module-imports.js +59 -0
- package/dist/plugins/extensionless-dynamic-import.js +2 -2
- package/dist/plugins/fonts.js +1 -1
- package/dist/plugins/ignore-dynamic-requests.d.ts +11 -0
- package/dist/plugins/ignore-dynamic-requests.js +530 -0
- 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/plugins/typeof-window.d.ts +1 -1
- package/dist/plugins/typeof-window.js +28 -56
- package/dist/routing/app-route-graph.d.ts +23 -2
- package/dist/routing/app-route-graph.js +138 -38
- package/dist/routing/file-matcher.d.ts +11 -1
- package/dist/routing/file-matcher.js +18 -3
- package/dist/routing/pages-router.js +2 -2
- package/dist/server/api-handler.js +9 -14
- package/dist/server/app-browser-action-result.d.ts +4 -1
- package/dist/server/app-browser-action-result.js +8 -1
- package/dist/server/app-browser-entry.js +198 -57
- package/dist/server/app-browser-interception-context.d.ts +1 -0
- package/dist/server/app-browser-interception-context.js +4 -2
- package/dist/server/app-browser-navigation-controller.d.ts +2 -0
- package/dist/server/app-browser-navigation-controller.js +60 -31
- package/dist/server/app-browser-server-action-client.d.ts +1 -1
- package/dist/server/app-browser-server-action-client.js +8 -7
- package/dist/server/app-browser-state.d.ts +12 -1
- package/dist/server/app-browser-state.js +25 -8
- package/dist/server/app-browser-visible-commit.d.ts +6 -1
- package/dist/server/app-browser-visible-commit.js +42 -24
- package/dist/server/app-elements-wire.d.ts +9 -2
- package/dist/server/app-elements-wire.js +8 -1
- package/dist/server/app-elements.d.ts +2 -2
- package/dist/server/app-elements.js +4 -3
- package/dist/server/app-fallback-renderer.d.ts +2 -1
- package/dist/server/app-fallback-renderer.js +10 -6
- package/dist/server/app-hydration-cache-publication.d.ts +11 -0
- package/dist/server/app-hydration-cache-publication.js +43 -0
- package/dist/server/app-middleware.d.ts +4 -3
- package/dist/server/app-middleware.js +8 -3
- package/dist/server/app-optimistic-routing.js +11 -4
- 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 +5 -4
- package/dist/server/app-page-cache-render.d.ts +1 -0
- package/dist/server/app-page-cache-render.js +8 -4
- package/dist/server/app-page-cache.d.ts +1 -0
- package/dist/server/app-page-cache.js +27 -7
- package/dist/server/app-page-dispatch.d.ts +1 -0
- package/dist/server/app-page-dispatch.js +26 -8
- package/dist/server/app-page-element-builder.d.ts +2 -1
- package/dist/server/app-page-element-builder.js +59 -4
- package/dist/server/app-page-execution.js +2 -1
- package/dist/server/app-page-probe.js +5 -0
- package/dist/server/app-page-render-identity.d.ts +2 -0
- package/dist/server/app-page-render-identity.js +3 -2
- package/dist/server/app-page-render.d.ts +2 -0
- package/dist/server/app-page-render.js +85 -16
- package/dist/server/app-page-request.d.ts +14 -0
- package/dist/server/app-page-request.js +69 -4
- package/dist/server/app-page-response.d.ts +7 -0
- package/dist/server/app-page-response.js +16 -4
- package/dist/server/app-page-route-wiring.d.ts +8 -1
- package/dist/server/app-page-route-wiring.js +32 -18
- package/dist/server/app-page-segment-state.d.ts +2 -1
- package/dist/server/app-page-segment-state.js +3 -1
- package/dist/server/app-page-stream.d.ts +7 -1
- package/dist/server/app-page-stream.js +3 -1
- package/dist/server/app-route-handler-execution.js +5 -1
- package/dist/server/app-route-handler-response.js +1 -0
- package/dist/server/app-route-handler-runtime.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 +9 -2
- 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 +7 -0
- package/dist/server/app-rsc-handler.js +148 -39
- 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 +6 -6
- package/dist/server/app-rsc-request-normalization.js +10 -8
- package/dist/server/app-segment-config.d.ts +11 -0
- package/dist/server/app-segment-config.js +62 -6
- package/dist/server/app-server-action-execution.d.ts +1 -0
- package/dist/server/app-server-action-execution.js +61 -30
- package/dist/server/app-ssr-entry.d.ts +3 -0
- package/dist/server/app-ssr-entry.js +13 -5
- package/dist/server/app-ssr-stream.d.ts +7 -3
- package/dist/server/app-ssr-stream.js +10 -6
- package/dist/server/app-visited-response-cache.d.ts +6 -0
- package/dist/server/app-visited-response-cache.js +3 -1
- package/dist/server/cache-control.d.ts +3 -1
- package/dist/server/cache-control.js +13 -1
- package/dist/server/client-trace-metadata.js +26 -0
- package/dist/server/default-global-not-found-module.d.ts +14 -0
- package/dist/server/default-global-not-found-module.js +14 -0
- package/dist/server/dev-route-files.js +4 -0
- package/dist/server/dev-server.d.ts +11 -1
- package/dist/server/dev-server.js +94 -35
- package/dist/server/dev-stack-sourcemap.d.ts +1 -1
- package/dist/server/dev-stack-sourcemap.js +1 -1
- package/dist/server/fetch-handler.d.ts +2 -0
- package/dist/server/fetch-handler.js +18 -0
- package/dist/server/headers.d.ts +7 -15
- package/dist/server/headers.js +11 -18
- package/dist/server/image-optimization.d.ts +51 -1
- package/dist/server/image-optimization.js +52 -2
- package/dist/server/implicit-tags.js +1 -1
- package/dist/server/isr-cache.d.ts +3 -2
- package/dist/server/isr-cache.js +9 -5
- package/dist/server/metadata-routes.d.ts +3 -1
- package/dist/server/metadata-routes.js +12 -2
- package/dist/server/middleware-runtime.js +6 -1
- package/dist/server/navigation-planner.d.ts +1 -0
- package/dist/server/navigation-planner.js +14 -3
- package/dist/server/pages-api-route.js +2 -2
- package/dist/server/pages-asset-tags.d.ts +4 -6
- package/dist/server/pages-asset-tags.js +12 -12
- package/dist/server/pages-client-assets.d.ts +12 -0
- package/dist/server/pages-client-assets.js +10 -0
- 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 +19 -1
- package/dist/server/pages-node-compat.js +81 -4
- package/dist/server/pages-page-data.d.ts +33 -1
- package/dist/server/pages-page-data.js +62 -33
- package/dist/server/pages-page-handler.d.ts +2 -1
- package/dist/server/pages-page-handler.js +38 -19
- package/dist/server/pages-page-response.d.ts +8 -0
- package/dist/server/pages-page-response.js +7 -3
- package/dist/server/pages-request-pipeline.d.ts +9 -5
- package/dist/server/pages-request-pipeline.js +55 -7
- package/dist/server/pages-router-entry.d.ts +30 -0
- package/dist/server/pages-router-entry.js +108 -0
- package/dist/server/prerender-manifest.d.ts +3 -1
- package/dist/server/prerender-route-params.js +1 -1
- package/dist/server/prod-server.d.ts +5 -3
- package/dist/server/prod-server.js +68 -39
- package/dist/server/request-pipeline.js +2 -1
- package/dist/server/seed-cache.js +4 -4
- package/dist/shims/app-router-scroll-state.d.ts +1 -0
- package/dist/shims/app-router-scroll-state.js +1 -0
- package/dist/shims/app-router-scroll.js +2 -1
- package/dist/shims/cache-handler.js +8 -1
- package/dist/shims/cache.js +22 -15
- package/dist/shims/cdn-cache.js +1 -1
- package/dist/shims/dynamic-preload-chunks.js +2 -1
- package/dist/shims/fetch-cache.d.ts +3 -1
- package/dist/shims/fetch-cache.js +77 -52
- package/dist/shims/form.d.ts +4 -2
- package/dist/shims/form.js +37 -43
- package/dist/shims/headers.d.ts +7 -1
- package/dist/shims/headers.js +17 -3
- package/dist/shims/image.js +75 -14
- 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/app-route-detection.d.ts +2 -17
- package/dist/shims/internal/app-route-detection.js +4 -17
- package/dist/shims/internal/hybrid-client-route-owner-direct.d.ts +23 -0
- package/dist/shims/internal/hybrid-client-route-owner-direct.js +51 -0
- package/dist/shims/internal/hybrid-client-route-owner.d.ts +3 -15
- package/dist/shims/internal/hybrid-client-route-owner.js +40 -58
- package/dist/shims/internal/pages-data-fetch-dedup.d.ts +10 -5
- package/dist/shims/internal/pages-data-fetch-dedup.js +84 -14
- package/dist/shims/internal/pages-data-target.d.ts +18 -12
- package/dist/shims/internal/pages-data-target.js +97 -13
- package/dist/shims/internal/pages-router-components.d.ts +7 -0
- package/dist/shims/internal/pages-router-components.js +13 -0
- package/dist/shims/layout-segment-context.d.ts +4 -1
- package/dist/shims/layout-segment-context.js +18 -4
- package/dist/shims/link.d.ts +1 -1
- package/dist/shims/link.js +224 -58
- package/dist/shims/navigation.d.ts +19 -3
- package/dist/shims/navigation.js +245 -47
- package/dist/shims/request-context.js +18 -0
- package/dist/shims/root-params.d.ts +15 -1
- package/dist/shims/root-params.js +21 -1
- package/dist/shims/router.d.ts +5 -7
- package/dist/shims/router.js +387 -102
- package/dist/shims/server.js +3 -2
- package/dist/shims/slot.js +3 -6
- package/dist/shims/unified-request-context.js +1 -0
- package/dist/typegen.js +1 -1
- package/dist/utils/client-runtime-metadata.d.ts +2 -18
- package/dist/utils/client-runtime-metadata.js +31 -22
- package/dist/utils/dev-stack-sourcemap-endpoint.d.ts +4 -0
- package/dist/{server → utils}/dev-stack-sourcemap-endpoint.js +1 -1
- package/dist/utils/domain-locale.d.ts +6 -3
- package/dist/{server → utils}/middleware-request-headers.d.ts +1 -1
- package/dist/{server → utils}/middleware-request-headers.js +3 -3
- package/dist/utils/project.d.ts +48 -2
- package/dist/utils/project.js +225 -4
- package/dist/utils/protocol-headers.d.ts +23 -0
- package/dist/utils/protocol-headers.js +23 -0
- package/dist/utils/react-version.d.ts +4 -0
- package/dist/utils/react-version.js +44 -0
- package/dist/utils/vite-version.d.ts +2 -0
- package/dist/utils/vite-version.js +11 -1
- package/package.json +47 -2
- package/dist/deploy.d.ts +0 -197
- package/dist/deploy.js +0 -1040
- package/dist/server/dev-stack-sourcemap-endpoint.d.ts +0 -4
- /package/dist/{cloudflare → packages/cloudflare}/src/utils/cache-control-metadata.js +0 -0
package/dist/shims/navigation.js
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
import { stripBasePath } from "../utils/base-path.js";
|
|
2
|
-
import { VINEXT_DYNAMIC_STALE_TIME_HEADER, VINEXT_MOUNTED_SLOTS_HEADER, VINEXT_PARAMS_HEADER } from "../server/headers.js";
|
|
2
|
+
import { VINEXT_DYNAMIC_STALE_TIME_HEADER, VINEXT_MOUNTED_SLOTS_HEADER, VINEXT_PARAMS_HEADER, VINEXT_RENDERED_PATH_AND_SEARCH_HEADER } from "../server/headers.js";
|
|
3
3
|
import { assertSafeNavigationUrl } from "./url-safety.js";
|
|
4
4
|
import { AppElementsWire } from "../server/app-elements-wire.js";
|
|
5
5
|
import "../server/app-elements.js";
|
|
6
6
|
import { markPprFallbackShellDynamicBoundary } from "./ppr-fallback-shell.js";
|
|
7
7
|
import { AppRouterContext } from "./internal/app-router-context.js";
|
|
8
|
-
import { resolveHybridClientRouteOwner } from "./internal/hybrid-client-route-owner.js";
|
|
9
8
|
import { isAbsoluteOrProtocolRelativeUrl, toBrowserNavigationHref, toSameOriginAppPath, withBasePath } from "./url-utils.js";
|
|
10
9
|
import { retryScrollTo, scrollToHashTarget } from "./hash-scroll.js";
|
|
11
10
|
import { GLOBAL_ACCESSORS_KEY, ServerInsertedHTMLContext, _registerStateAccessors, clearClientHydrationContext, clearServerInsertedHTML, flushServerInsertedHTML, getBfcacheIdMapContext, getBfcacheSegmentIdContext, getLayoutSegmentContext, getNavigationContext, registerServerInsertedHTMLCallback, renderServerInsertedHTML, setNavigationContext } from "./navigation-context-state.js";
|
|
12
11
|
import { BailoutToCSRError, DynamicServerError, HTTP_ERROR_FALLBACK_ERROR_CODE, RedirectType, decodeRedirectError, forbidden, getAccessFallbackHTTPStatus, isBailoutToCSRError, isDynamicServerError, isHTTPAccessFallbackError, isNextRouterError, isRedirectError, notFound, permanentRedirect, redirect, unauthorized, unstable_rethrow } from "./navigation-errors.js";
|
|
12
|
+
import { isBotUserAgent } from "../utils/html-limited-bots.js";
|
|
13
13
|
import { clearAppNavigationFailureTarget, stageAppNavigationFailureTarget } from "../client/app-nav-failure-handler.js";
|
|
14
14
|
import { beginAppRouterScrollIntent, clearAppRouterScrollIntent, consumeAppRouterScrollIntent, getPendingAppRouterScrollIntent } from "./app-router-scroll-state.js";
|
|
15
|
-
import { VINEXT_RSC_COMPATIBILITY_ID_HEADER, createRscRequestHeaders, createRscRequestUrl, stripRscCacheBustingSearchParam } from "../server/app-rsc-cache-busting.js";
|
|
15
|
+
import { VINEXT_RSC_COMPATIBILITY_ID_HEADER, createRscRequestHeaders, createRscRequestUrl, stripRscCacheBustingSearchParam, stripRscSuffix } from "../server/app-rsc-cache-busting.js";
|
|
16
16
|
import { getNavigationRuntime, hasAppNavigationRuntime } from "../client/navigation-runtime.js";
|
|
17
17
|
import { notifyAppRouterTransitionStart } from "../client/instrumentation-client-state.js";
|
|
18
18
|
import { PUBLIC_INITIAL_BFCACHE_ID } from "../server/app-bfcache-id.js";
|
|
@@ -22,6 +22,8 @@ import { hasPendingAppRouterPageRedirect } from "../server/app-browser-mpa-navig
|
|
|
22
22
|
import { navigationPlanner } from "../server/navigation-planner.js";
|
|
23
23
|
import { ReadonlyURLSearchParams } from "./readonly-url-search-params.js";
|
|
24
24
|
import { getPagesNavigationContext } from "./internal/pages-router-accessor.js";
|
|
25
|
+
import { resolveHybridClientRouteOwner } from "./internal/hybrid-client-route-owner.js";
|
|
26
|
+
import { releaseAppPrefetchFetchSlot, scheduleAppPrefetchFetch } from "./internal/app-prefetch-fetch-queue.js";
|
|
25
27
|
import { UnrecognizedActionError, unstable_isUnrecognizedActionError } from "./unrecognized-action-error.js";
|
|
26
28
|
import * as React$1 from "react";
|
|
27
29
|
//#region src/shims/navigation.ts
|
|
@@ -41,7 +43,7 @@ function useChildSegments(parallelRoutesKey = "children") {
|
|
|
41
43
|
const ctx = getLayoutSegmentContext();
|
|
42
44
|
if (!ctx) return [];
|
|
43
45
|
try {
|
|
44
|
-
return React$1.useContext(ctx)[parallelRoutesKey] ?? [];
|
|
46
|
+
return (React$1.useContext(ctx)[parallelRoutesKey] ?? []).filter((segment) => !segment.startsWith("__PAGE__"));
|
|
45
47
|
} catch {
|
|
46
48
|
return [];
|
|
47
49
|
}
|
|
@@ -53,8 +55,9 @@ const PAGES_NAVIGATION_NOTIFY_KEY = Symbol.for("vinext.navigation.pagesNavigatio
|
|
|
53
55
|
const isServer = typeof window === "undefined";
|
|
54
56
|
/** basePath from next.config.js, injected by the plugin at build time */
|
|
55
57
|
const __basePath = process.env.__NEXT_ROUTER_BASEPATH ?? "";
|
|
56
|
-
/** Maximum
|
|
57
|
-
const MAX_PREFETCH_CACHE_SIZE = 50;
|
|
58
|
+
/** Maximum buffered bytes in the RSC prefetch cache. Mirrors Next.js' 50 MB LRU. */
|
|
59
|
+
const MAX_PREFETCH_CACHE_SIZE = 50 * 1024 * 1024;
|
|
60
|
+
const PREFETCH_CACHE_EVICTION_TARGET_SIZE = MAX_PREFETCH_CACHE_SIZE * .9;
|
|
58
61
|
/**
|
|
59
62
|
* TTL for prefetch cache entries in ms.
|
|
60
63
|
*
|
|
@@ -69,14 +72,15 @@ const MAX_PREFETCH_CACHE_SIZE = 50;
|
|
|
69
72
|
* has not set `experimental.staleTimes`, Next.js' 300s default applies
|
|
70
73
|
* (see `resolveStaleTimes` in `config/next-config.ts`).
|
|
71
74
|
*/
|
|
72
|
-
function
|
|
73
|
-
|
|
74
|
-
if (raw === void 0 || raw === "") return 3e4;
|
|
75
|
+
function resolveClientRouterStaleTime(raw, fallbackMs) {
|
|
76
|
+
if (raw === void 0 || raw === "") return fallbackMs;
|
|
75
77
|
const seconds = Number(raw);
|
|
76
|
-
if (!Number.isFinite(seconds) || seconds < 0) return
|
|
78
|
+
if (!Number.isFinite(seconds) || seconds < 0) return fallbackMs;
|
|
77
79
|
return seconds * 1e3;
|
|
78
80
|
}
|
|
79
|
-
const
|
|
81
|
+
const DYNAMIC_NAVIGATION_CACHE_TTL = resolveClientRouterStaleTime(process.env.__NEXT_CLIENT_ROUTER_DYNAMIC_STALETIME, 3e4);
|
|
82
|
+
const PREFETCH_CACHE_TTL = resolveClientRouterStaleTime(process.env.__NEXT_CLIENT_ROUTER_STATIC_STALETIME, 3e4);
|
|
83
|
+
const MIN_PREFETCH_STALE_TIME_MS = 3e4;
|
|
80
84
|
function getCurrentInterceptionContext() {
|
|
81
85
|
if (isServer) return null;
|
|
82
86
|
return stripBasePath(window.location.pathname, __basePath);
|
|
@@ -135,6 +139,12 @@ function resolveCachedRscResponseExpiresAt(timestamp, cached, fallbackTtlMs) {
|
|
|
135
139
|
if (isCacheExpiresAt(cached.expiresAt)) return cached.expiresAt;
|
|
136
140
|
return timestamp + resolveCachedRscResponseTtlMs(cached, fallbackTtlMs);
|
|
137
141
|
}
|
|
142
|
+
function resolvePrefetchedRscResponseExpiresAt(timestamp, cached, fallbackTtlMs) {
|
|
143
|
+
if (isCacheExpiresAt(cached.expiresAt)) return cached.expiresAt;
|
|
144
|
+
const seconds = cached.dynamicStaleTimeSeconds;
|
|
145
|
+
if (!isDynamicStaleTimeSeconds(seconds)) return timestamp + Math.max(fallbackTtlMs, MIN_PREFETCH_STALE_TIME_MS);
|
|
146
|
+
return timestamp + Math.max(seconds * 1e3, MIN_PREFETCH_STALE_TIME_MS);
|
|
147
|
+
}
|
|
138
148
|
function resolvePrefetchCacheEntryExpiresAt(entry) {
|
|
139
149
|
if (entry.expiresAt !== void 0) return entry.expiresAt;
|
|
140
150
|
if (entry.snapshot) return resolveCachedRscResponseExpiresAt(entry.timestamp, entry.snapshot, PREFETCH_CACHE_TTL);
|
|
@@ -153,6 +163,13 @@ function normalizeRscCacheLookupUrl(rscUrl) {
|
|
|
153
163
|
return null;
|
|
154
164
|
}
|
|
155
165
|
}
|
|
166
|
+
function normalizeRscCacheLookupPathname(rscUrl) {
|
|
167
|
+
try {
|
|
168
|
+
return stripRscSuffix(new URL(rscUrl, "http://vinext.local").pathname);
|
|
169
|
+
} catch {
|
|
170
|
+
return null;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
156
173
|
function parsePrefetchCacheKey(cacheKey) {
|
|
157
174
|
const separatorIndex = cacheKey.indexOf("\0");
|
|
158
175
|
if (separatorIndex === -1) return {
|
|
@@ -168,22 +185,25 @@ function isPrefetchCacheEntryCompatibleWithMountedSlots(entry, mountedSlotsHeade
|
|
|
168
185
|
if (resolvePrefetchCacheEntryMountedSlotsHeader(entry) === mountedSlotsHeader) return true;
|
|
169
186
|
return (entry.snapshot?.mountedSlotsHeader ?? null) === mountedSlotsHeader;
|
|
170
187
|
}
|
|
171
|
-
function findPrefetchCacheEntryForNavigation(rscUrl, interceptionContext, mountedSlotsHeader) {
|
|
172
|
-
const exactCacheKey = AppElementsWire.encodeCacheKey(rscUrl, interceptionContext);
|
|
188
|
+
function findPrefetchCacheEntryForNavigation(rscUrl, interceptionContext, mountedSlotsHeader, additionalRscUrls = []) {
|
|
173
189
|
const cache = getPrefetchCache();
|
|
174
|
-
const
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
190
|
+
const rscUrls = [rscUrl, ...additionalRscUrls];
|
|
191
|
+
for (const lookupRscUrl of rscUrls) {
|
|
192
|
+
const exactCacheKey = AppElementsWire.encodeCacheKey(lookupRscUrl, interceptionContext);
|
|
193
|
+
const exactEntry = cache.get(exactCacheKey);
|
|
194
|
+
if (exactEntry && exactEntry.cacheForNavigation !== false && isPrefetchCacheEntryCompatibleWithMountedSlots(exactEntry, mountedSlotsHeader)) return {
|
|
195
|
+
cacheKey: exactCacheKey,
|
|
196
|
+
entry: exactEntry
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
const normalizedTargets = new Set(rscUrls.map((lookupRscUrl) => normalizeRscCacheLookupUrl(lookupRscUrl)).filter((lookupRscUrl) => lookupRscUrl !== null));
|
|
200
|
+
if (normalizedTargets.size === 0) return null;
|
|
181
201
|
for (const [cacheKey, entry] of cache) {
|
|
182
|
-
if (cacheKey === exactCacheKey) continue;
|
|
183
202
|
if (entry.cacheForNavigation === false) continue;
|
|
184
203
|
const source = parsePrefetchCacheKey(cacheKey);
|
|
185
204
|
if (source.interceptionContext !== interceptionContext) continue;
|
|
186
|
-
|
|
205
|
+
const normalizedSource = normalizeRscCacheLookupUrl(source.rscUrl);
|
|
206
|
+
if (normalizedSource === null || !normalizedTargets.has(normalizedSource)) continue;
|
|
187
207
|
if (!isPrefetchCacheEntryCompatibleWithMountedSlots(entry, mountedSlotsHeader)) continue;
|
|
188
208
|
return {
|
|
189
209
|
cacheKey,
|
|
@@ -193,31 +213,100 @@ function findPrefetchCacheEntryForNavigation(rscUrl, interceptionContext, mounte
|
|
|
193
213
|
return null;
|
|
194
214
|
}
|
|
195
215
|
function hasPrefetchCacheEntryForNavigation(rscUrl, interceptionContext = null, mountedSlotsHeader = null, options = {}) {
|
|
196
|
-
const match = findPrefetchCacheEntryForNavigation(rscUrl, interceptionContext, mountedSlotsHeader);
|
|
216
|
+
const match = findPrefetchCacheEntryForNavigation(rscUrl, interceptionContext, mountedSlotsHeader, options.additionalRscUrls);
|
|
197
217
|
if (match === null) return false;
|
|
198
|
-
if (match.entry.pending !== void 0)
|
|
199
|
-
|
|
218
|
+
if (match.entry.pending !== void 0) {
|
|
219
|
+
touchPrefetchCacheEntry(getPrefetchCache(), match.cacheKey, match.entry);
|
|
220
|
+
return true;
|
|
221
|
+
}
|
|
222
|
+
if (resolvePrefetchCacheEntryExpiresAt(match.entry) > Date.now()) {
|
|
223
|
+
touchPrefetchCacheEntry(getPrefetchCache(), match.cacheKey, match.entry);
|
|
224
|
+
return true;
|
|
225
|
+
}
|
|
200
226
|
deletePrefetchCacheEntry(getPrefetchCache(), getPrefetchedUrls(), match.cacheKey, match.entry, options.notifyInvalidation ?? true);
|
|
201
227
|
return false;
|
|
202
228
|
}
|
|
229
|
+
function hasSearchAgnosticPrefetchShellForRoute(rscUrl, interceptionContext = null, mountedSlotsHeader = null) {
|
|
230
|
+
const normalizedTargetPathname = normalizeRscCacheLookupPathname(rscUrl);
|
|
231
|
+
if (normalizedTargetPathname === null) return false;
|
|
232
|
+
const cache = getPrefetchCache();
|
|
233
|
+
for (const [cacheKey, entry] of cache) {
|
|
234
|
+
if (entry.searchAgnosticShell !== true) continue;
|
|
235
|
+
const source = parsePrefetchCacheKey(cacheKey);
|
|
236
|
+
if (source.interceptionContext !== interceptionContext) continue;
|
|
237
|
+
if (normalizeRscCacheLookupPathname(source.rscUrl) !== normalizedTargetPathname) continue;
|
|
238
|
+
if (!isPrefetchCacheEntryCompatibleWithMountedSlots(entry, mountedSlotsHeader)) continue;
|
|
239
|
+
if (entry.pending !== void 0) return true;
|
|
240
|
+
if (resolvePrefetchCacheEntryExpiresAt(entry) > Date.now()) return true;
|
|
241
|
+
deletePrefetchCacheEntry(cache, getPrefetchedUrls(), cacheKey, entry, true);
|
|
242
|
+
}
|
|
243
|
+
return false;
|
|
244
|
+
}
|
|
245
|
+
function getPrefetchCacheEntrySize(entry) {
|
|
246
|
+
return entry.snapshot?.buffer.byteLength ?? entry.size ?? 0;
|
|
247
|
+
}
|
|
248
|
+
let trackedPrefetchCache = null;
|
|
249
|
+
let trackedPrefetchCacheByteSize = 0;
|
|
250
|
+
function getPrefetchCacheByteSize(cache) {
|
|
251
|
+
if (trackedPrefetchCache === cache) return trackedPrefetchCacheByteSize;
|
|
252
|
+
let total = 0;
|
|
253
|
+
const seen = /* @__PURE__ */ new Set();
|
|
254
|
+
for (const entry of cache.values()) {
|
|
255
|
+
if (seen.has(entry)) continue;
|
|
256
|
+
seen.add(entry);
|
|
257
|
+
total += getPrefetchCacheEntrySize(entry);
|
|
258
|
+
}
|
|
259
|
+
trackedPrefetchCache = cache;
|
|
260
|
+
trackedPrefetchCacheByteSize = total;
|
|
261
|
+
return total;
|
|
262
|
+
}
|
|
263
|
+
function adjustPrefetchCacheByteSize(cache, delta) {
|
|
264
|
+
if (trackedPrefetchCache !== cache) return;
|
|
265
|
+
trackedPrefetchCacheByteSize = Math.max(0, trackedPrefetchCacheByteSize + delta);
|
|
266
|
+
}
|
|
267
|
+
function touchPrefetchCacheEntry(cache, cacheKey, entry) {
|
|
268
|
+
if (cache.get(cacheKey) !== entry) return;
|
|
269
|
+
cache.delete(cacheKey);
|
|
270
|
+
cache.set(cacheKey, entry);
|
|
271
|
+
for (const key of entry.cacheKeys ?? []) {
|
|
272
|
+
if (key === cacheKey || cache.get(key) !== entry) continue;
|
|
273
|
+
cache.delete(key);
|
|
274
|
+
cache.set(key, entry);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
203
277
|
/**
|
|
204
|
-
* Evict prefetch cache entries if
|
|
205
|
-
*
|
|
278
|
+
* Evict prefetch cache entries if buffered payloads exceed the byte budget.
|
|
279
|
+
* Sweeps expired entries only after the cheap byte-budget check says cleanup is
|
|
280
|
+
* needed, then evicts least-recently-used entries down to the target size.
|
|
206
281
|
*/
|
|
207
282
|
function evictPrefetchCacheIfNeeded() {
|
|
208
283
|
const cache = getPrefetchCache();
|
|
209
|
-
|
|
284
|
+
let totalSize = getPrefetchCacheByteSize(cache);
|
|
285
|
+
if (totalSize <= 52428800) return;
|
|
210
286
|
const now = Date.now();
|
|
211
287
|
const prefetched = getPrefetchedUrls();
|
|
212
288
|
for (const [key, entry] of cache) if (resolvePrefetchCacheEntryExpiresAt(entry) <= now) deletePrefetchCacheEntry(cache, prefetched, key, entry, true);
|
|
213
|
-
|
|
289
|
+
totalSize = getPrefetchCacheByteSize(cache);
|
|
290
|
+
if (totalSize <= 52428800) return;
|
|
291
|
+
let inspectedEntries = 0;
|
|
292
|
+
while (totalSize > PREFETCH_CACHE_EVICTION_TARGET_SIZE && inspectedEntries < cache.size) {
|
|
214
293
|
const oldest = cache.keys().next().value;
|
|
215
294
|
if (oldest !== void 0) {
|
|
216
295
|
const entry = cache.get(oldest);
|
|
217
|
-
if (entry)
|
|
218
|
-
|
|
296
|
+
if (entry) {
|
|
297
|
+
const entrySize = getPrefetchCacheEntrySize(entry);
|
|
298
|
+
if (entry.pending !== void 0 && entrySize === 0) {
|
|
299
|
+
touchPrefetchCacheEntry(cache, oldest, entry);
|
|
300
|
+
inspectedEntries += 1;
|
|
301
|
+
continue;
|
|
302
|
+
}
|
|
303
|
+
totalSize -= entrySize;
|
|
304
|
+
deletePrefetchCacheEntry(cache, prefetched, oldest, entry, true);
|
|
305
|
+
inspectedEntries = 0;
|
|
306
|
+
} else {
|
|
219
307
|
cache.delete(oldest);
|
|
220
308
|
prefetched.delete(oldest);
|
|
309
|
+
inspectedEntries += 1;
|
|
221
310
|
}
|
|
222
311
|
} else break;
|
|
223
312
|
}
|
|
@@ -241,8 +330,13 @@ function notifyPrefetchInvalidated(entry) {
|
|
|
241
330
|
}
|
|
242
331
|
}
|
|
243
332
|
function deletePrefetchCacheEntry(cache, prefetched, cacheKey, entry, notify) {
|
|
244
|
-
cache
|
|
245
|
-
|
|
333
|
+
adjustPrefetchCacheByteSize(cache, -getPrefetchCacheEntrySize(entry));
|
|
334
|
+
const cacheKeys = entry.cacheKeys ?? new Set([cacheKey]);
|
|
335
|
+
for (const key of cacheKeys) {
|
|
336
|
+
if (cache.get(key) === entry) cache.delete(key);
|
|
337
|
+
prefetched.delete(key);
|
|
338
|
+
}
|
|
339
|
+
entry.cacheKeys = void 0;
|
|
246
340
|
if (notify) notifyPrefetchInvalidated(entry);
|
|
247
341
|
else {
|
|
248
342
|
clearPrefetchInvalidation(entry);
|
|
@@ -282,6 +376,35 @@ function invalidatePrefetchCache() {
|
|
|
282
376
|
prefetched.clear();
|
|
283
377
|
if (!isServer) getNavigationRuntime()?.functions.pingVisibleLinks?.();
|
|
284
378
|
}
|
|
379
|
+
function seedPrefetchResponseSnapshot(rscUrl, snapshot, interceptionContext = null, mountedSlotsHeader = null, fallbackTtlMs = DYNAMIC_NAVIGATION_CACHE_TTL) {
|
|
380
|
+
const cacheKey = AppElementsWire.encodeCacheKey(rscUrl, interceptionContext);
|
|
381
|
+
const cache = getPrefetchCache();
|
|
382
|
+
const existing = cache.get(cacheKey);
|
|
383
|
+
if (existing) deletePrefetchCacheEntry(cache, getPrefetchedUrls(), cacheKey, existing, false);
|
|
384
|
+
const timestamp = Date.now();
|
|
385
|
+
const entry = {
|
|
386
|
+
cacheForNavigation: true,
|
|
387
|
+
cacheKeys: new Set([cacheKey]),
|
|
388
|
+
expiresAt: resolveCachedRscResponseExpiresAt(timestamp, snapshot, fallbackTtlMs),
|
|
389
|
+
mountedSlotsHeader,
|
|
390
|
+
outcome: "cache-seeded",
|
|
391
|
+
size: snapshot.buffer.byteLength,
|
|
392
|
+
snapshot,
|
|
393
|
+
timestamp
|
|
394
|
+
};
|
|
395
|
+
cache.set(cacheKey, entry);
|
|
396
|
+
adjustPrefetchCacheByteSize(cache, snapshot.buffer.byteLength);
|
|
397
|
+
getPrefetchedUrls().add(cacheKey);
|
|
398
|
+
schedulePrefetchInvalidation(cacheKey, entry);
|
|
399
|
+
evictPrefetchCacheIfNeeded();
|
|
400
|
+
}
|
|
401
|
+
function deletePrefetchResponseSnapshot(rscUrl, snapshot, interceptionContext = null) {
|
|
402
|
+
const cacheKey = AppElementsWire.encodeCacheKey(rscUrl, interceptionContext);
|
|
403
|
+
const cache = getPrefetchCache();
|
|
404
|
+
const entry = cache.get(cacheKey);
|
|
405
|
+
if (entry?.snapshot !== snapshot) return;
|
|
406
|
+
deletePrefetchCacheEntry(cache, getPrefetchedUrls(), cacheKey, entry, false);
|
|
407
|
+
}
|
|
285
408
|
/**
|
|
286
409
|
* Store a prefetched RSC response in the cache by snapshotting it to an
|
|
287
410
|
* ArrayBuffer. The snapshot completes asynchronously; during that window
|
|
@@ -300,27 +423,37 @@ function invalidatePrefetchCache() {
|
|
|
300
423
|
*/
|
|
301
424
|
function storePrefetchResponse(rscUrl, response, interceptionContext = null, options) {
|
|
302
425
|
const cacheKey = AppElementsWire.encodeCacheKey(rscUrl, interceptionContext);
|
|
303
|
-
|
|
426
|
+
const cache = getPrefetchCache();
|
|
427
|
+
const prefetched = getPrefetchedUrls();
|
|
428
|
+
const existing = cache.get(cacheKey);
|
|
429
|
+
if (existing) deletePrefetchCacheEntry(cache, prefetched, cacheKey, existing, false);
|
|
304
430
|
const entry = {
|
|
431
|
+
cacheKeys: new Set([cacheKey]),
|
|
305
432
|
mountedSlotsHeader: null,
|
|
306
433
|
outcome: "pending",
|
|
307
434
|
timestamp: Date.now()
|
|
308
435
|
};
|
|
309
436
|
addPrefetchInvalidationCallback(entry, options?.onInvalidate);
|
|
310
437
|
entry.pending = snapshotRscResponse(response).then((snapshot) => {
|
|
438
|
+
if (cache.get(cacheKey) !== entry) return;
|
|
439
|
+
const previousSize = getPrefetchCacheEntrySize(entry);
|
|
311
440
|
entry.mountedSlotsHeader = snapshot.mountedSlotsHeader ?? null;
|
|
312
441
|
entry.snapshot = snapshot;
|
|
442
|
+
entry.size = snapshot.buffer.byteLength;
|
|
443
|
+
adjustPrefetchCacheByteSize(cache, entry.size - previousSize);
|
|
313
444
|
entry.expiresAt = resolveCachedRscResponseExpiresAt(entry.timestamp, snapshot, PREFETCH_CACHE_TTL);
|
|
445
|
+
evictPrefetchCacheIfNeeded();
|
|
314
446
|
}).catch(() => {
|
|
315
|
-
deletePrefetchCacheEntry(
|
|
447
|
+
deletePrefetchCacheEntry(cache, prefetched, cacheKey, entry, false);
|
|
316
448
|
}).finally(() => {
|
|
449
|
+
if (cache.get(cacheKey) !== entry) return;
|
|
317
450
|
entry.pending = void 0;
|
|
318
451
|
if (entry.snapshot) {
|
|
319
452
|
entry.outcome = "cache-seeded";
|
|
320
453
|
schedulePrefetchInvalidation(cacheKey, entry);
|
|
321
454
|
}
|
|
322
455
|
});
|
|
323
|
-
|
|
456
|
+
cache.set(cacheKey, entry);
|
|
324
457
|
}
|
|
325
458
|
function createCachedRscResponseSnapshot(response, buffer, responseUrl = null) {
|
|
326
459
|
const dynamicStaleTimeSeconds = parseDynamicStaleTimeSeconds(response.headers.get(VINEXT_DYNAMIC_STALE_TIME_HEADER));
|
|
@@ -331,15 +464,29 @@ function createCachedRscResponseSnapshot(response, buffer, responseUrl = null) {
|
|
|
331
464
|
...dynamicStaleTimeSeconds !== void 0 ? { dynamicStaleTimeSeconds } : {},
|
|
332
465
|
mountedSlotsHeader: response.headers.get(VINEXT_MOUNTED_SLOTS_HEADER),
|
|
333
466
|
paramsHeader: response.headers.get(VINEXT_PARAMS_HEADER),
|
|
467
|
+
renderedPathAndSearch: parseRenderedPathAndSearchHeader(response.headers.get(VINEXT_RENDERED_PATH_AND_SEARCH_HEADER)),
|
|
334
468
|
url: responseUrl ?? response.url
|
|
335
469
|
};
|
|
336
470
|
}
|
|
471
|
+
function parseRenderedPathAndSearchHeader(value) {
|
|
472
|
+
if (value === null || value === "") return null;
|
|
473
|
+
try {
|
|
474
|
+
const decoded = decodeURIComponent(value);
|
|
475
|
+
return decoded.startsWith("/") ? decoded : null;
|
|
476
|
+
} catch {
|
|
477
|
+
return null;
|
|
478
|
+
}
|
|
479
|
+
}
|
|
337
480
|
/**
|
|
338
481
|
* Snapshot an RSC response to an ArrayBuffer for caching and replay.
|
|
339
482
|
* Consumes the response body and stores it with content-type and URL metadata.
|
|
340
483
|
*/
|
|
341
484
|
async function snapshotRscResponse(response) {
|
|
342
|
-
|
|
485
|
+
try {
|
|
486
|
+
return createCachedRscResponseSnapshot(response, await response.arrayBuffer());
|
|
487
|
+
} finally {
|
|
488
|
+
releaseAppPrefetchFetchSlot(response);
|
|
489
|
+
}
|
|
343
490
|
}
|
|
344
491
|
/**
|
|
345
492
|
* Reconstruct a Response from a cached RSC snapshot.
|
|
@@ -362,6 +509,7 @@ function restoreRscResponse(cached, copy = true) {
|
|
|
362
509
|
if (cached.compatibilityIdHeader != null) headers.set(VINEXT_RSC_COMPATIBILITY_ID_HEADER, cached.compatibilityIdHeader);
|
|
363
510
|
if (isDynamicStaleTimeSeconds(cached.dynamicStaleTimeSeconds)) headers.set(VINEXT_DYNAMIC_STALE_TIME_HEADER, String(cached.dynamicStaleTimeSeconds));
|
|
364
511
|
if (cached.paramsHeader != null) headers.set(VINEXT_PARAMS_HEADER, cached.paramsHeader);
|
|
512
|
+
if (cached.renderedPathAndSearch != null) headers.set(VINEXT_RENDERED_PATH_AND_SEARCH_HEADER, encodeURIComponent(cached.renderedPathAndSearch));
|
|
365
513
|
return new Response(copy ? cached.buffer.slice(0) : cached.buffer, {
|
|
366
514
|
status: 200,
|
|
367
515
|
headers
|
|
@@ -379,22 +527,38 @@ function prefetchRscResponse(rscUrl, fetchPromise, interceptionContext = null, m
|
|
|
379
527
|
const cache = getPrefetchCache();
|
|
380
528
|
const prefetched = getPrefetchedUrls();
|
|
381
529
|
const now = Date.now();
|
|
530
|
+
const existing = cache.get(cacheKey);
|
|
531
|
+
if (existing) deletePrefetchCacheEntry(cache, prefetched, cacheKey, existing, false);
|
|
382
532
|
const entry = {
|
|
383
533
|
cacheForNavigation: behavior.cacheForNavigation ?? true,
|
|
534
|
+
cacheKeys: new Set([cacheKey]),
|
|
384
535
|
mountedSlotsHeader,
|
|
385
536
|
optimisticRouteShell: behavior.optimisticRouteShell === true,
|
|
386
537
|
outcome: "pending",
|
|
538
|
+
prefetchKind: behavior.prefetchKind ?? (behavior.optimisticRouteShell === true ? "loading-shell" : "navigation"),
|
|
539
|
+
searchAgnosticShell: behavior.searchAgnosticShell === true,
|
|
387
540
|
timestamp: now
|
|
388
541
|
};
|
|
389
542
|
addPrefetchInvalidationCallback(entry, options?.onInvalidate);
|
|
390
543
|
entry.pending = fetchPromise.then(async (response) => {
|
|
391
544
|
if (response.ok) {
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
545
|
+
const snapshot = await snapshotRscResponse(response);
|
|
546
|
+
if (cache.get(cacheKey) !== entry) return;
|
|
547
|
+
const previousSize = getPrefetchCacheEntrySize(entry);
|
|
548
|
+
entry.snapshot = snapshot;
|
|
549
|
+
entry.size = snapshot.buffer.byteLength;
|
|
550
|
+
adjustPrefetchCacheByteSize(cache, entry.size - previousSize);
|
|
551
|
+
entry.expiresAt = resolvePrefetchedRscResponseExpiresAt(entry.timestamp, entry.snapshot, behavior.fallbackTtlMs ?? PREFETCH_CACHE_TTL);
|
|
552
|
+
addRenderedPathAndSearchPrefetchAlias(cache, prefetched, cacheKey, entry);
|
|
553
|
+
evictPrefetchCacheIfNeeded();
|
|
554
|
+
} else {
|
|
555
|
+
releaseAppPrefetchFetchSlot(response);
|
|
556
|
+
deletePrefetchCacheEntry(cache, prefetched, cacheKey, entry, false);
|
|
557
|
+
}
|
|
395
558
|
}).catch(() => {
|
|
396
559
|
deletePrefetchCacheEntry(cache, prefetched, cacheKey, entry, false);
|
|
397
560
|
}).finally(() => {
|
|
561
|
+
if (cache.get(cacheKey) !== entry) return;
|
|
398
562
|
entry.pending = void 0;
|
|
399
563
|
if (entry.snapshot) {
|
|
400
564
|
entry.outcome = "cache-seeded";
|
|
@@ -404,6 +568,36 @@ function prefetchRscResponse(rscUrl, fetchPromise, interceptionContext = null, m
|
|
|
404
568
|
cache.set(cacheKey, entry);
|
|
405
569
|
evictPrefetchCacheIfNeeded();
|
|
406
570
|
}
|
|
571
|
+
function addRenderedPathAndSearchPrefetchAlias(cache, prefetched, primaryCacheKey, entry) {
|
|
572
|
+
if (entry.cacheForNavigation === false) return;
|
|
573
|
+
const renderedPathAndSearch = entry.snapshot?.renderedPathAndSearch;
|
|
574
|
+
if (!renderedPathAndSearch) return;
|
|
575
|
+
const source = parsePrefetchCacheKey(primaryCacheKey);
|
|
576
|
+
const aliasCacheKey = AppElementsWire.encodeCacheKey(renderedPathAndSearch, source.interceptionContext);
|
|
577
|
+
if (aliasCacheKey === primaryCacheKey) return;
|
|
578
|
+
const existing = cache.get(aliasCacheKey);
|
|
579
|
+
if (existing && existing !== entry) deletePrefetchCacheEntry(cache, prefetched, aliasCacheKey, existing, false);
|
|
580
|
+
entry.cacheKeys ??= new Set([primaryCacheKey]);
|
|
581
|
+
entry.cacheKeys.add(aliasCacheKey);
|
|
582
|
+
cache.set(aliasCacheKey, entry);
|
|
583
|
+
prefetched.add(aliasCacheKey);
|
|
584
|
+
}
|
|
585
|
+
function peekPrefetchResponseForNavigation(rscUrl, interceptionContext = null, mountedSlotsHeader = null) {
|
|
586
|
+
const match = findPrefetchCacheEntryForNavigation(rscUrl, interceptionContext, mountedSlotsHeader);
|
|
587
|
+
if (!match) return null;
|
|
588
|
+
const { cacheKey, entry } = match;
|
|
589
|
+
if (entry.pending || entry.outcome !== "cache-seeded") return null;
|
|
590
|
+
if (entry.cacheForNavigation === false || !entry.snapshot) return null;
|
|
591
|
+
if (resolvePrefetchCacheEntryExpiresAt(entry) <= Date.now()) {
|
|
592
|
+
deletePrefetchCacheEntry(getPrefetchCache(), getPrefetchedUrls(), cacheKey, entry, true);
|
|
593
|
+
return null;
|
|
594
|
+
}
|
|
595
|
+
if (entry.expiresAt !== void 0 || entry.snapshot.expiresAt !== void 0) return {
|
|
596
|
+
...entry.snapshot,
|
|
597
|
+
expiresAt: resolvePrefetchCacheEntryExpiresAt(entry)
|
|
598
|
+
};
|
|
599
|
+
return entry.snapshot;
|
|
600
|
+
}
|
|
407
601
|
/**
|
|
408
602
|
* Consume a prefetched response for a given rscUrl.
|
|
409
603
|
* Only returns settled (non-pending) snapshots synchronously.
|
|
@@ -417,9 +611,12 @@ function consumePrefetchResponse(rscUrl, interceptionContext = null, mountedSlot
|
|
|
417
611
|
const match = findPrefetchCacheEntryForNavigation(rscUrl, interceptionContext, mountedSlotsHeader);
|
|
418
612
|
if (!match) return null;
|
|
419
613
|
const { cacheKey, entry } = match;
|
|
614
|
+
return consumeMatchedPrefetchResponse(cacheKey, entry, mountedSlotsHeader);
|
|
615
|
+
}
|
|
616
|
+
function consumeMatchedPrefetchResponse(cacheKey, entry, mountedSlotsHeader) {
|
|
420
617
|
if (entry.pending || entry.outcome !== "cache-seeded") return null;
|
|
421
618
|
if (entry.cacheForNavigation === false) return null;
|
|
422
|
-
deletePrefetchCacheEntry(
|
|
619
|
+
deletePrefetchCacheEntry(getPrefetchCache(), getPrefetchedUrls(), cacheKey, entry, false);
|
|
423
620
|
if (entry.snapshot) {
|
|
424
621
|
if (!isPrefetchCacheEntryCompatibleWithMountedSlots(entry, mountedSlotsHeader)) return null;
|
|
425
622
|
if (resolvePrefetchCacheEntryExpiresAt(entry) <= Date.now()) return null;
|
|
@@ -433,7 +630,7 @@ function consumePrefetchResponse(rscUrl, interceptionContext = null, mountedSlot
|
|
|
433
630
|
}
|
|
434
631
|
async function consumePrefetchResponseForNavigation(rscUrl, interceptionContext = null, mountedSlotsHeader = null, options) {
|
|
435
632
|
const cache = getPrefetchCache();
|
|
436
|
-
const match = findPrefetchCacheEntryForNavigation(rscUrl, interceptionContext, mountedSlotsHeader);
|
|
633
|
+
const match = findPrefetchCacheEntryForNavigation(rscUrl, interceptionContext, mountedSlotsHeader, options?.additionalRscUrls);
|
|
437
634
|
if (!match) return null;
|
|
438
635
|
const { cacheKey, entry } = match;
|
|
439
636
|
if (entry.pending !== void 0) {
|
|
@@ -441,7 +638,7 @@ async function consumePrefetchResponseForNavigation(rscUrl, interceptionContext
|
|
|
441
638
|
if (cache.get(cacheKey) !== entry) return null;
|
|
442
639
|
}
|
|
443
640
|
if (options?.shouldConsume?.() === false) return null;
|
|
444
|
-
return
|
|
641
|
+
return consumeMatchedPrefetchResponse(cacheKey, entry, mountedSlotsHeader);
|
|
445
642
|
}
|
|
446
643
|
const _CLIENT_NAV_STATE_KEY = Symbol.for("vinext.clientNavigationState");
|
|
447
644
|
const _MOUNTED_SLOTS_HEADER_KEY = Symbol.for("vinext.mountedSlotsHeader");
|
|
@@ -772,7 +969,7 @@ function commitClientNavigationState(navId, options) {
|
|
|
772
969
|
if (isServer) return;
|
|
773
970
|
const state = getClientNavigationState();
|
|
774
971
|
if (!state) return;
|
|
775
|
-
if ((navId !== void 0
|
|
972
|
+
if ((options?.releaseSnapshot ?? navId !== void 0) && state.navigationSnapshotActiveCount > 0) state.navigationSnapshotActiveCount -= 1;
|
|
776
973
|
const urlChanged = syncCommittedUrlStateFromLocation();
|
|
777
974
|
let paramsChanged = false;
|
|
778
975
|
if (state.pendingClientParams !== null && state.pendingClientParamsJson !== null) {
|
|
@@ -1033,6 +1230,7 @@ const _appRouter = {
|
|
|
1033
1230
|
prefetch(href, options) {
|
|
1034
1231
|
assertSafeNavigationUrl(href);
|
|
1035
1232
|
if (isServer) return;
|
|
1233
|
+
if (isBotUserAgent(window.navigator?.userAgent ?? "")) return;
|
|
1036
1234
|
try {
|
|
1037
1235
|
new URL(withBasePath(href, __basePath), window.location.href);
|
|
1038
1236
|
} catch {
|
|
@@ -1060,11 +1258,11 @@ const _appRouter = {
|
|
|
1060
1258
|
return;
|
|
1061
1259
|
}
|
|
1062
1260
|
prefetched.add(cacheKey);
|
|
1063
|
-
prefetchRscResponse(rscUrl, fetch(rscUrl, {
|
|
1261
|
+
prefetchRscResponse(rscUrl, scheduleAppPrefetchFetch(() => fetch(rscUrl, {
|
|
1064
1262
|
headers,
|
|
1065
1263
|
credentials: "include",
|
|
1066
1264
|
priority: "low"
|
|
1067
|
-
}), interceptionContext, mountedSlotsHeader, options);
|
|
1265
|
+
}), "low"), interceptionContext, mountedSlotsHeader, options);
|
|
1068
1266
|
})().catch((error) => {
|
|
1069
1267
|
console.error("[vinext] RSC prefetch setup error:", error);
|
|
1070
1268
|
});
|
|
@@ -1203,4 +1401,4 @@ if (!isServer) {
|
|
|
1203
1401
|
}
|
|
1204
1402
|
}
|
|
1205
1403
|
//#endregion
|
|
1206
|
-
export { BailoutToCSRError, DynamicServerError, GLOBAL_ACCESSORS_KEY, HTTP_ERROR_FALLBACK_ERROR_CODE, MAX_PREFETCH_CACHE_SIZE, PREFETCH_CACHE_TTL, ReadonlyURLSearchParams, RedirectType, ServerInsertedHTMLContext, UnrecognizedActionError, __basePath, _registerStateAccessors, activateNavigationSnapshot, appRouterInstance, applyAppRouterScrollFallback, clearPendingPathname, clearServerInsertedHTML, commitClientNavigationState, consumePrefetchResponse, consumePrefetchResponseForNavigation, createCachedRscResponseSnapshot, createClientNavigationRenderSnapshot, createSnapshotPathAndSearch, decodeRedirectError, flushServerInsertedHTML, forbidden, getAccessFallbackHTTPStatus, getBfcacheIdMapContext, getBfcacheSegmentIdContext, getClientNavigationRenderContext, getClientNavigationState, getClientParams, getCurrentInterceptionContext, getCurrentNextUrl, getLayoutSegmentContext, getMountedSlotsHeader, getNavigationContext, getPrefetchCache, getPrefetchInterceptionContext, getPrefetchedUrls, hasPrefetchCacheEntryForNavigation, invalidatePrefetchCache, isBailoutToCSRError, isDynamicServerError, isHTTPAccessFallbackError, isNextRouterError, isRedirectError, navigateClientSide, notFound, permanentRedirect, prefetchRscResponse, pushHistoryStateWithoutNotify, redirect, renderServerInsertedHTML, replaceClientParamsWithoutNotify, replaceHistoryStateWithoutNotify, resolveCachedRscResponseExpiresAt, resolveCachedRscResponseTtlMs, resolvePrefetchCacheEntryMountedSlotsHeader, restoreRscResponse, saveScrollPosition, setClientParams, setMountedSlotsHeader, setNavigationContext, setPendingPathname, snapshotRscResponse, storePrefetchResponse, unauthorized, unstable_isUnrecognizedActionError, unstable_rethrow, useClientNavigationRenderSnapshot, useParams, usePathname, useRouter, useSearchParams, useSelectedLayoutSegment, useSelectedLayoutSegments, useServerInsertedHTML };
|
|
1404
|
+
export { BailoutToCSRError, DYNAMIC_NAVIGATION_CACHE_TTL, DynamicServerError, GLOBAL_ACCESSORS_KEY, HTTP_ERROR_FALLBACK_ERROR_CODE, MAX_PREFETCH_CACHE_SIZE, PREFETCH_CACHE_TTL, ReadonlyURLSearchParams, RedirectType, ServerInsertedHTMLContext, UnrecognizedActionError, __basePath, _registerStateAccessors, activateNavigationSnapshot, appRouterInstance, applyAppRouterScrollFallback, clearPendingPathname, clearServerInsertedHTML, commitClientNavigationState, consumePrefetchResponse, consumePrefetchResponseForNavigation, createCachedRscResponseSnapshot, createClientNavigationRenderSnapshot, createSnapshotPathAndSearch, decodeRedirectError, deletePrefetchResponseSnapshot, flushServerInsertedHTML, forbidden, getAccessFallbackHTTPStatus, getBfcacheIdMapContext, getBfcacheSegmentIdContext, getClientNavigationRenderContext, getClientNavigationState, getClientParams, getCurrentInterceptionContext, getCurrentNextUrl, getLayoutSegmentContext, getMountedSlotsHeader, getNavigationContext, getPrefetchCache, getPrefetchInterceptionContext, getPrefetchedUrls, hasPrefetchCacheEntryForNavigation, hasSearchAgnosticPrefetchShellForRoute, invalidatePrefetchCache, isBailoutToCSRError, isDynamicServerError, isHTTPAccessFallbackError, isNextRouterError, isRedirectError, navigateClientSide, notFound, peekPrefetchResponseForNavigation, permanentRedirect, prefetchRscResponse, pushHistoryStateWithoutNotify, redirect, renderServerInsertedHTML, replaceClientParamsWithoutNotify, replaceHistoryStateWithoutNotify, resolveCachedRscResponseExpiresAt, resolveCachedRscResponseTtlMs, resolvePrefetchCacheEntryMountedSlotsHeader, restoreRscResponse, saveScrollPosition, seedPrefetchResponseSnapshot, setClientParams, setMountedSlotsHeader, setNavigationContext, setPendingPathname, snapshotRscResponse, storePrefetchResponse, unauthorized, unstable_isUnrecognizedActionError, unstable_rethrow, useClientNavigationRenderSnapshot, useParams, usePathname, useRouter, useSearchParams, useSelectedLayoutSegment, useSelectedLayoutSegments, useServerInsertedHTML };
|
|
@@ -24,6 +24,24 @@ import { getRequestContext, isInsideUnifiedScope, runWithUnifiedStateMutation }
|
|
|
24
24
|
* ctx?.waitUntil(somePromise);
|
|
25
25
|
*/
|
|
26
26
|
const _als = getOrCreateAls("vinext.requestContext.als");
|
|
27
|
+
const OPEN_NEXT_CLOUDFLARE_CONTEXT_SYMBOL = Symbol.for("__cloudflare-context__");
|
|
28
|
+
let openNextCloudflareContextFallback;
|
|
29
|
+
function installOpenNextCloudflareContextBridge() {
|
|
30
|
+
const descriptor = Object.getOwnPropertyDescriptor(globalThis, OPEN_NEXT_CLOUDFLARE_CONTEXT_SYMBOL);
|
|
31
|
+
if (descriptor && !descriptor.configurable) return;
|
|
32
|
+
openNextCloudflareContextFallback = descriptor && "value" in descriptor ? descriptor.value : descriptor?.get?.call(globalThis);
|
|
33
|
+
Object.defineProperty(globalThis, OPEN_NEXT_CLOUDFLARE_CONTEXT_SYMBOL, {
|
|
34
|
+
configurable: true,
|
|
35
|
+
get() {
|
|
36
|
+
const ctx = getRequestExecutionContext();
|
|
37
|
+
return ctx ? { ctx } : openNextCloudflareContextFallback;
|
|
38
|
+
},
|
|
39
|
+
set(value) {
|
|
40
|
+
openNextCloudflareContextFallback = value;
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
installOpenNextCloudflareContextBridge();
|
|
27
45
|
function runWithExecutionContext(ctx, fn) {
|
|
28
46
|
if (isInsideUnifiedScope()) return runWithUnifiedStateMutation((uCtx) => {
|
|
29
47
|
uCtx.executionContext = ctx;
|
|
@@ -3,10 +3,24 @@ type RootParams = Record<string, string | string[] | undefined>;
|
|
|
3
3
|
type RootParamsState = {
|
|
4
4
|
rootParams: RootParams | null;
|
|
5
5
|
};
|
|
6
|
+
type RootParamsUsage = {
|
|
7
|
+
kind: "route";
|
|
8
|
+
} | {
|
|
9
|
+
kind: "server-action";
|
|
10
|
+
} | {
|
|
11
|
+
kind: "route-handler";
|
|
12
|
+
routePattern: string;
|
|
13
|
+
};
|
|
14
|
+
type RootParamsUsageController = {
|
|
15
|
+
transitionToRender(): void;
|
|
16
|
+
};
|
|
6
17
|
declare function pickRootParams(params: RootParams, rootParamNames: readonly string[] | null | undefined): RootParams;
|
|
7
18
|
declare function setRootParams(params: RootParams | null): void;
|
|
8
19
|
declare function getRootParam(name: string): Promise<string | string[] | undefined>;
|
|
20
|
+
declare function runWithRootParamsUsage<T>(usage: RootParamsUsage, fn: () => Promise<T>, controller?: RootParamsUsageController): Promise<T>;
|
|
21
|
+
declare function runWithRootParamsUsage<T>(usage: RootParamsUsage, fn: () => T | Promise<T>, controller?: RootParamsUsageController): T | Promise<T>;
|
|
22
|
+
declare function createRootParamsUsageController(): RootParamsUsageController;
|
|
9
23
|
declare function runWithRootParamsScope<T>(params: RootParams, fn: () => Promise<T>): Promise<T>;
|
|
10
24
|
declare function runWithRootParamsScope<T>(params: RootParams, fn: () => T | Promise<T>): T | Promise<T>;
|
|
11
25
|
//#endregion
|
|
12
|
-
export { RootParams, RootParamsState, getRootParam, pickRootParams, runWithRootParamsScope, setRootParams };
|
|
26
|
+
export { RootParams, RootParamsState, RootParamsUsage, RootParamsUsageController, createRootParamsUsageController, getRootParam, pickRootParams, runWithRootParamsScope, runWithRootParamsUsage, setRootParams };
|
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
import { getOrCreateAls } from "./internal/als-registry.js";
|
|
2
2
|
import { getRequestContext, isInsideUnifiedScope, runWithUnifiedStateMutation } from "./unified-request-context.js";
|
|
3
3
|
//#region src/shims/root-params.ts
|
|
4
|
+
function createRootParamsUsageError(message) {
|
|
5
|
+
return new Error(message);
|
|
6
|
+
}
|
|
4
7
|
const _FALLBACK_KEY = Symbol.for("vinext.rootParams.fallback");
|
|
5
8
|
const _g = globalThis;
|
|
6
9
|
const _als = getOrCreateAls("vinext.rootParams.als");
|
|
10
|
+
const _usageAls = getOrCreateAls("vinext.rootParams.usage.als");
|
|
7
11
|
const _fallbackState = _g[_FALLBACK_KEY] ??= { rootParams: null };
|
|
8
12
|
function getState() {
|
|
9
13
|
if (isInsideUnifiedScope()) return getRequestContext();
|
|
@@ -18,8 +22,24 @@ function setRootParams(params) {
|
|
|
18
22
|
getState().rootParams = params;
|
|
19
23
|
}
|
|
20
24
|
function getRootParam(name) {
|
|
25
|
+
const usage = _usageAls.getStore();
|
|
26
|
+
if (usage?.kind === "server-action" && usage.phase === "active") throw createRootParamsUsageError(`\`import('next/root-params').${name}()\` was used inside a Server Action. This is not supported. Functions from 'next/root-params' can only be called in the context of a route.`);
|
|
27
|
+
if (usage?.kind === "route-handler" && usage.phase === "active") throw createRootParamsUsageError(`Route ${usage.routePattern} used \`import('next/root-params').${name}()\` inside a Route Handler. Support for this API in Route Handlers is planned for a future version of Next.js.`);
|
|
21
28
|
return Promise.resolve(getState().rootParams?.[name]);
|
|
22
29
|
}
|
|
30
|
+
function runWithRootParamsUsage(usage, fn, controller) {
|
|
31
|
+
const state = {
|
|
32
|
+
...usage,
|
|
33
|
+
phase: "active"
|
|
34
|
+
};
|
|
35
|
+
if (controller) controller.transitionToRender = () => {
|
|
36
|
+
if (usage.kind === "server-action") state.phase = "render";
|
|
37
|
+
};
|
|
38
|
+
return _usageAls.run(state, fn);
|
|
39
|
+
}
|
|
40
|
+
function createRootParamsUsageController() {
|
|
41
|
+
return { transitionToRender() {} };
|
|
42
|
+
}
|
|
23
43
|
function runWithRootParamsScope(params, fn) {
|
|
24
44
|
if (isInsideUnifiedScope()) return runWithUnifiedStateMutation((ctx) => {
|
|
25
45
|
ctx.rootParams = params;
|
|
@@ -27,4 +47,4 @@ function runWithRootParamsScope(params, fn) {
|
|
|
27
47
|
else return _als.run({ rootParams: params }, fn);
|
|
28
48
|
}
|
|
29
49
|
//#endregion
|
|
30
|
-
export { getRootParam, pickRootParams, runWithRootParamsScope, setRootParams };
|
|
50
|
+
export { createRootParamsUsageController, getRootParam, pickRootParams, runWithRootParamsScope, runWithRootParamsUsage, setRootParams };
|
package/dist/shims/router.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { VinextNextData } from "../client/vinext-next-data.js";
|
|
2
|
+
import { PagesRouterComponentsMap } from "./internal/pages-router-components.js";
|
|
2
3
|
import { UrlQuery } from "../utils/query.js";
|
|
3
4
|
import { ComponentType, ReactElement } from "react";
|
|
4
5
|
|
|
@@ -25,7 +26,7 @@ type NextRouter = {
|
|
|
25
26
|
replace(url: string | UrlObject, as?: string, options?: TransitionOptions): Promise<boolean>; /** Go back */
|
|
26
27
|
back(): void; /** Reload the page */
|
|
27
28
|
reload(): void; /** Prefetch a page (injects <link rel="prefetch">) */
|
|
28
|
-
prefetch(url: string): Promise<void>; /** Register a callback to run before popstate navigation */
|
|
29
|
+
prefetch(url: string, as?: string): Promise<void>; /** Register a callback to run before popstate navigation */
|
|
29
30
|
beforePopState(cb: BeforePopStateCallback): void; /** Listen for route changes */
|
|
30
31
|
events: RouterEvents;
|
|
31
32
|
};
|
|
@@ -174,16 +175,13 @@ type ExcludeRouterProps<P> = Pick<P, Exclude<keyof P, keyof WithRouterProps>>;
|
|
|
174
175
|
*/
|
|
175
176
|
declare function withRouter<P extends WithRouterProps>(ComposedComponent: ComponentType<P>): ComponentType<ExcludeRouterProps<P>>;
|
|
176
177
|
declare const RouterMethods: {
|
|
177
|
-
/** See `_components` comment above for the dual role this map plays. */components:
|
|
178
|
-
|
|
179
|
-
__appRouter: true;
|
|
180
|
-
} | Record<string, unknown>;
|
|
181
|
-
};
|
|
178
|
+
/** See `_components` comment above for the dual role this map plays. */components: PagesRouterComponentsMap;
|
|
179
|
+
sdc: Record<string, Promise<Response>>;
|
|
182
180
|
push: (url: string | UrlObject, as?: string, options?: TransitionOptions) => Promise<boolean>;
|
|
183
181
|
replace: (url: string | UrlObject, as?: string, options?: TransitionOptions) => Promise<boolean>;
|
|
184
182
|
back: () => void;
|
|
185
183
|
reload: () => void;
|
|
186
|
-
prefetch: (url: string) => Promise<void>;
|
|
184
|
+
prefetch: (url: string, as?: string) => Promise<void>;
|
|
187
185
|
beforePopState: (cb: BeforePopStateCallback) => void;
|
|
188
186
|
events: RouterEvents;
|
|
189
187
|
};
|