vinext 1.0.0-beta.2 → 1.0.0-beta.4
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 +6 -9
- package/dist/build/client-build-config.js +7 -1
- package/dist/build/precompress.js +1 -1
- package/dist/build/prerender.d.ts +2 -1
- package/dist/build/prerender.js +12 -2
- package/dist/check.js +4 -0
- package/dist/cli-args.d.ts +2 -1
- package/dist/cli-args.js +10 -0
- package/dist/cli.js +14 -8
- package/dist/client/navigation-runtime.d.ts +6 -0
- package/dist/client/navigation-runtime.js +1 -1
- package/dist/config/config-matchers.js +4 -1
- package/dist/config/dotenv.d.ts +3 -4
- package/dist/config/dotenv.js +30 -22
- package/dist/config/next-config.d.ts +16 -5
- package/dist/config/next-config.js +7 -0
- package/dist/entries/app-browser-entry.d.ts +4 -10
- package/dist/entries/app-browser-entry.js +20 -4
- package/dist/entries/app-rsc-entry.js +21 -3
- package/dist/entries/app-rsc-manifest.js +19 -0
- package/dist/entries/pages-client-entry.js +0 -1
- package/dist/entries/pages-server-entry.js +13 -3
- package/dist/index.js +122 -49
- package/dist/init-cloudflare.d.ts +3 -1
- package/dist/init-cloudflare.js +59 -5
- package/dist/plugins/client-reference-dedup.js +7 -5
- package/dist/plugins/extensionless-dynamic-import.js +23 -18
- package/dist/plugins/ignore-dynamic-requests.js +3 -1
- package/dist/plugins/import-meta-url.js +17 -6
- package/dist/plugins/require-context.js +20 -15
- package/dist/plugins/sass.js +3 -4
- package/dist/plugins/transform-cache.d.ts +19 -0
- package/dist/plugins/transform-cache.js +36 -0
- package/dist/routing/app-route-graph.d.ts +14 -6
- package/dist/routing/app-route-graph.js +106 -16
- package/dist/server/api-handler.d.ts +2 -0
- package/dist/server/api-handler.js +7 -6
- package/dist/server/app-bfcache-identity.js +28 -2
- package/dist/server/app-browser-entry.js +175 -75
- package/dist/server/app-browser-navigation-controller.d.ts +23 -21
- package/dist/server/app-browser-navigation-controller.js +30 -5
- package/dist/server/app-browser-prefetch-response.d.ts +9 -0
- package/dist/server/app-browser-prefetch-response.js +32 -0
- package/dist/server/app-browser-state.d.ts +8 -3
- package/dist/server/app-browser-state.js +9 -3
- package/dist/server/app-page-cache-finalizer.js +1 -1
- package/dist/server/app-page-cache.d.ts +1 -1
- package/dist/server/app-page-cache.js +16 -2
- package/dist/server/app-page-dispatch.d.ts +9 -1
- package/dist/server/app-page-dispatch.js +37 -8
- package/dist/server/app-page-element-builder.d.ts +4 -1
- package/dist/server/app-page-element-builder.js +34 -7
- package/dist/server/app-page-head.d.ts +3 -3
- package/dist/server/app-page-head.js +58 -13
- package/dist/server/app-page-http-access-fallback-metadata.d.ts +2 -2
- package/dist/server/app-page-http-access-fallback-metadata.js +9 -1
- package/dist/server/app-page-probe.d.ts +4 -0
- package/dist/server/app-page-probe.js +6 -2
- package/dist/server/app-page-render.d.ts +1 -1
- package/dist/server/app-page-render.js +13 -3
- package/dist/server/app-page-request.d.ts +3 -1
- package/dist/server/app-page-request.js +4 -13
- package/dist/server/app-page-response.d.ts +2 -0
- package/dist/server/app-page-response.js +7 -2
- package/dist/server/app-page-route-wiring.d.ts +10 -2
- package/dist/server/app-page-route-wiring.js +269 -59
- package/dist/server/app-pages-bridge.d.ts +2 -1
- package/dist/server/app-pages-bridge.js +3 -1
- package/dist/server/app-route-handler-cache.js +4 -0
- package/dist/server/app-route-handler-dispatch.js +9 -3
- package/dist/server/app-route-handler-response.js +1 -1
- package/dist/server/app-route-module-loader.d.ts +6 -0
- package/dist/server/app-route-module-loader.js +3 -0
- package/dist/server/app-router-entry.js +6 -3
- package/dist/server/app-rsc-cache-busting.d.ts +7 -0
- package/dist/server/app-rsc-cache-busting.js +6 -0
- package/dist/server/app-rsc-errors.js +10 -0
- package/dist/server/app-rsc-handler.d.ts +2 -0
- package/dist/server/app-rsc-handler.js +30 -13
- 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.js +65 -4
- package/dist/server/app-rsc-route-matching.d.ts +9 -0
- package/dist/server/app-rsc-route-matching.js +6 -0
- package/dist/server/app-server-action-execution.d.ts +1 -1
- package/dist/server/app-ssr-entry.js +4 -4
- package/dist/server/app-visited-response-cache.d.ts +6 -1
- package/dist/server/app-visited-response-cache.js +49 -1
- package/dist/server/cache-control.d.ts +2 -2
- package/dist/server/cache-control.js +2 -1
- package/dist/server/dev-server.d.ts +3 -1
- package/dist/server/dev-server.js +204 -348
- package/dist/server/edge-api-runtime.d.ts +10 -1
- package/dist/server/edge-api-runtime.js +29 -1
- package/dist/server/headers.d.ts +4 -2
- package/dist/server/headers.js +6 -3
- package/dist/server/isr-cache.d.ts +10 -33
- package/dist/server/isr-cache.js +33 -43
- package/dist/server/isr-decision.d.ts +2 -2
- package/dist/server/pages-api-route.d.ts +4 -0
- package/dist/server/pages-api-route.js +6 -3
- package/dist/server/pages-asset-tags.d.ts +1 -0
- package/dist/server/pages-asset-tags.js +6 -4
- package/dist/server/pages-dev-hydration.js +0 -1
- package/dist/server/pages-document-asset-props.d.ts +26 -0
- package/dist/server/pages-document-asset-props.js +69 -0
- package/dist/server/pages-get-initial-props.d.ts +1 -1
- package/dist/server/pages-get-initial-props.js +1 -4
- package/dist/server/pages-i18n.js +64 -16
- package/dist/server/pages-node-compat.d.ts +2 -0
- package/dist/server/pages-node-compat.js +10 -6
- package/dist/server/pages-page-data.d.ts +61 -9
- package/dist/server/pages-page-data.js +334 -74
- package/dist/server/pages-page-handler.d.ts +6 -1
- package/dist/server/pages-page-handler.js +119 -20
- package/dist/server/pages-page-response.d.ts +7 -3
- package/dist/server/pages-page-response.js +41 -33
- package/dist/server/pages-request-pipeline.d.ts +7 -0
- package/dist/server/pages-request-pipeline.js +5 -1
- package/dist/server/pages-revalidate.d.ts +1 -1
- package/dist/server/pages-revalidate.js +54 -8
- package/dist/server/pages-router-entry.d.ts +1 -0
- package/dist/server/pages-router-entry.js +10 -4
- package/dist/server/prerender-manifest.d.ts +1 -0
- package/dist/server/prod-server.d.ts +2 -1
- package/dist/server/prod-server.js +38 -13
- package/dist/server/revalidation-host.d.ts +11 -0
- package/dist/server/revalidation-host.js +19 -0
- package/dist/server/seed-cache.js +1 -1
- package/dist/server/worker-revalidation-context.d.ts +13 -0
- package/dist/server/worker-revalidation-context.js +30 -0
- package/dist/shims/app-router-scroll-state.d.ts +2 -1
- package/dist/shims/app-router-scroll-state.js +7 -2
- package/dist/shims/cache-handler.d.ts +1 -1
- package/dist/shims/cache-handler.js +10 -7
- package/dist/shims/cache-runtime.d.ts +6 -0
- package/dist/shims/cache-runtime.js +4 -1
- package/dist/shims/cache.js +1 -1
- package/dist/shims/document.js +15 -2
- package/dist/shims/error.js +1 -1
- package/dist/shims/fetch-cache.js +1 -1
- package/dist/shims/font-google-base.js +17 -8
- package/dist/shims/font-local.js +41 -10
- package/dist/shims/hash-scroll.d.ts +1 -1
- package/dist/shims/hash-scroll.js +2 -1
- package/dist/shims/headers.d.ts +10 -13
- package/dist/shims/headers.js +85 -36
- package/dist/shims/internal/cookie-serialize.d.ts +10 -9
- package/dist/shims/internal/cookie-serialize.js +9 -8
- package/dist/shims/internal/interpolate-as.d.ts +15 -1
- package/dist/shims/internal/interpolate-as.js +27 -2
- package/dist/shims/link.d.ts +2 -0
- package/dist/shims/link.js +77 -24
- package/dist/shims/metadata.d.ts +18 -34
- package/dist/shims/metadata.js +98 -56
- package/dist/shims/navigation.d.ts +18 -2
- package/dist/shims/navigation.js +70 -21
- package/dist/shims/request-context.d.ts +6 -2
- package/dist/shims/router.js +18 -7
- package/dist/shims/script.js +1 -1
- package/dist/shims/server.d.ts +7 -6
- package/dist/shims/server.js +156 -57
- package/dist/shims/slot.d.ts +2 -1
- package/dist/shims/slot.js +6 -2
- package/dist/shims/streamed-icons.d.ts +9 -0
- package/dist/shims/streamed-icons.js +33 -0
- package/dist/shims/unified-request-context.d.ts +21 -2
- package/dist/shims/unified-request-context.js +82 -1
- package/dist/utils/cache-control-metadata.d.ts +2 -1
- package/dist/utils/cache-control-metadata.js +5 -1
- package/dist/utils/html-limited-bots.js +1 -1
- package/dist/utils/protocol-headers.d.ts +7 -1
- package/dist/utils/protocol-headers.js +7 -1
- package/package.json +1 -1
|
@@ -12,7 +12,7 @@ import { DevRecoveryBoundary, GlobalErrorBoundary, RedirectBoundary } from "../s
|
|
|
12
12
|
import { beginAppRouterScrollIntent, consumeAppRouterScrollIntent } from "../shims/app-router-scroll-state.js";
|
|
13
13
|
import { AppRouterScrollCommitProvider } from "../shims/app-router-scroll.js";
|
|
14
14
|
import { BfcacheStateKeyMapContext, ElementsContext, Slot } from "../shims/slot.js";
|
|
15
|
-
import { VINEXT_RSC_COMPATIBILITY_ID_HEADER, VINEXT_RSC_CONTENT_TYPE, createRscRequestHeaders, createRscRequestUrl, getVinextRscCompatibilityId
|
|
15
|
+
import { VINEXT_RSC_COMPATIBILITY_ID_HEADER, VINEXT_RSC_CONTENT_TYPE, createRscRequestHeaders, createRscRequestUrl, getVinextRscCompatibilityId } from "./app-rsc-cache-busting.js";
|
|
16
16
|
import { getNavigationRuntime, registerNavigationRuntimeBootstrap, registerNavigationRuntimeFunctions } from "../client/navigation-runtime.js";
|
|
17
17
|
import { notifyAppRouterTransitionStart } from "../client/instrumentation-client-state.js";
|
|
18
18
|
import "../client/instrumentation-client.js";
|
|
@@ -24,17 +24,18 @@ import { readHistoryStatePreviousNextUrl } from "./app-history-state.js";
|
|
|
24
24
|
import { AppBrowserMpaNavigationScheduler } from "./app-browser-mpa-navigation.js";
|
|
25
25
|
import { blockDangerousStreamedRscRedirect } from "./app-browser-rsc-redirect.js";
|
|
26
26
|
import { navigationPlanner } from "./navigation-planner.js";
|
|
27
|
-
import { DYNAMIC_NAVIGATION_CACHE_TTL, PREFETCH_CACHE_TTL, __basePath, appRouterInstance, commitClientNavigationState, consumePrefetchResponseForNavigation, createCachedRscResponseSnapshot, createClientNavigationRenderSnapshot, createSnapshotPathAndSearch, deletePrefetchResponseSnapshot, getClientNavigationRenderContext, getMountedSlotsHeader, getPrefetchCache, hasPrefetchCacheEntryForNavigation, invalidatePrefetchCache, preloadHybridClientRouteOwner, pushHistoryStateWithoutNotify, replaceClientParamsWithoutNotify, replaceHistoryStateWithoutNotify, resolveLoadedHybridClientRewriteHref, resolvePrefetchCacheEntryMountedSlotsHeader, restoreRscResponse, saveScrollPosition, seedPrefetchResponseSnapshot, setClientParams, setMountedSlotsHeader, setPendingPathname, useRouter } from "../shims/navigation.js";
|
|
27
|
+
import { DYNAMIC_NAVIGATION_CACHE_TTL, PREFETCH_CACHE_TTL, __basePath, appRouterInstance, commitClientNavigationState, consumePrefetchResponse, consumePrefetchResponseForNavigation, createCachedRscResponseSnapshot, createClientNavigationRenderSnapshot, createSnapshotPathAndSearch, deletePrefetchResponseSnapshot, getClientNavigationRenderContext, getMountedSlotsHeader, getPrefetchCache, hasPrefetchCacheEntryForNavigation, invalidatePrefetchCache, preloadHybridClientRouteOwner, pushHistoryStateWithoutNotify, replaceClientParamsWithoutNotify, replaceHistoryStateWithoutNotify, resolveLoadedHybridClientRewriteHref, resolvePrefetchCacheEntryMountedSlotsHeader, restoreRscResponse, saveScrollPosition, seedPrefetchResponseSnapshot, setClientParams, setMountedSlotsHeader, setPendingPathname, useRouter } from "../shims/navigation.js";
|
|
28
28
|
import { chunksToReadableStream, createProgressiveRscStream, getVinextBrowserGlobal } from "./app-browser-stream.js";
|
|
29
29
|
import { COMMITTED_CACHE_APP_NAVIGATION_PAYLOAD_ORIGIN, FRESH_APP_NAVIGATION_PAYLOAD_ORIGIN, VISITED_CACHE_APP_NAVIGATION_PAYLOAD_ORIGIN, isCacheRestorableAppPayloadMetadata, isCompleteAppPayloadMetadata, resolveInterceptionContextFromPreviousNextUrl } from "./app-browser-state.js";
|
|
30
30
|
import { clearHardNavigationLoopGuard, createAppBrowserNavigationController, createBasePathStrippedPathAndSearch } from "./app-browser-navigation-controller.js";
|
|
31
31
|
import { consumeInitialFormState, createVinextHydrateRootOptions, hydrateRootInTransition } from "./app-browser-hydration.js";
|
|
32
32
|
import { AppBrowserHistoryController } from "./app-browser-history-controller.js";
|
|
33
|
-
import { createVisitedResponseCacheEntry, isVisitedResponseCacheEntryFresh } from "./app-visited-response-cache.js";
|
|
33
|
+
import { createVisitedResponseCacheEntry, deleteVisitedResponseCacheEntry, findVisitedResponseCacheEntry, isVisitedResponseCacheEntryFresh } from "./app-visited-response-cache.js";
|
|
34
34
|
import { createPopstateRestoreHandler, restoreSynchronousPopstateScrollPosition } from "./app-browser-popstate.js";
|
|
35
35
|
import { createDevOnCaughtError, createOnUncaughtError, createProdOnCaughtError, prodOnRecoverableError } from "./app-browser-error.js";
|
|
36
36
|
import { createHydrationCachePublication } from "./app-hydration-cache-publication.js";
|
|
37
|
-
import {
|
|
37
|
+
import { resolvePrefetchNavigationResponseUrl } from "./app-browser-prefetch-response.js";
|
|
38
|
+
import { createOptimisticRouteTemplate, getOptimisticPrefetchSourceKey, getOptimisticRouteTemplateKey, matchOptimisticRouteManifestRoute, resolveOptimisticNavigationPayload } from "./app-optimistic-routing.js";
|
|
38
39
|
import { removeStylesheetLinksCoveredByInlineCss } from "./app-inline-css-client.js";
|
|
39
40
|
import { createElement, startTransition, use, useEffect, useLayoutEffect, useMemo, useRef, useState } from "react";
|
|
40
41
|
import { flushSync } from "react-dom";
|
|
@@ -126,6 +127,7 @@ let latestClientParams = {};
|
|
|
126
127
|
const visitedResponseCache = /* @__PURE__ */ new Map();
|
|
127
128
|
let clientNavigationCacheGeneration = 0;
|
|
128
129
|
let browserRouterStateHasEverCommitted = false;
|
|
130
|
+
let initialPrefetchRouterState = null;
|
|
129
131
|
const mpaNavigationScheduler = new AppBrowserMpaNavigationScheduler();
|
|
130
132
|
const unresolvedMpaNavigation = new Promise(() => {});
|
|
131
133
|
const RSC_HMR_SETTLE_DELAY_MS = 150;
|
|
@@ -193,21 +195,6 @@ function clearClientNavigationCaches() {
|
|
|
193
195
|
clearPrefetchState();
|
|
194
196
|
historyController.invalidateRestorableClientState();
|
|
195
197
|
}
|
|
196
|
-
function normalizeBrowserRscUrlForReuse(url) {
|
|
197
|
-
if (!url) return null;
|
|
198
|
-
try {
|
|
199
|
-
const parsed = new URL(url, window.location.origin);
|
|
200
|
-
stripRscCacheBustingSearchParam(parsed);
|
|
201
|
-
return `${parsed.pathname}${parsed.search}`;
|
|
202
|
-
} catch {
|
|
203
|
-
return null;
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
function isAlternatePrefetchResponseUrl(responseUrl, additionalRscUrls) {
|
|
207
|
-
const normalizedResponseUrl = normalizeBrowserRscUrlForReuse(responseUrl);
|
|
208
|
-
if (normalizedResponseUrl === null) return false;
|
|
209
|
-
return additionalRscUrls.some((additionalRscUrl) => normalizeBrowserRscUrlForReuse(additionalRscUrl) === normalizedResponseUrl);
|
|
210
|
-
}
|
|
211
198
|
function isSettledPrefetchCacheEntry(entry) {
|
|
212
199
|
return entry.outcome === "cache-seeded" && entry.pending === void 0 && entry.snapshot !== void 0;
|
|
213
200
|
}
|
|
@@ -302,28 +289,29 @@ function createNavigationCommitEffect(options) {
|
|
|
302
289
|
commitClientNavigationState(navId);
|
|
303
290
|
};
|
|
304
291
|
}
|
|
305
|
-
async function renderNavigationPayload(
|
|
292
|
+
async function renderNavigationPayload(options) {
|
|
306
293
|
syncServerActionHttpFallbackHead(null);
|
|
307
294
|
return browserNavigationController.renderNavigationPayload({
|
|
308
|
-
actionType,
|
|
295
|
+
actionType: options.actionType ?? "navigate",
|
|
309
296
|
createNavigationCommitEffect,
|
|
310
|
-
historyUpdateMode,
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
297
|
+
historyUpdateMode: options.historyUpdateMode,
|
|
298
|
+
navigationCommitKind: options.navigationCommitKind,
|
|
299
|
+
navigationInitiationState: options.navigationInitiationState,
|
|
300
|
+
navigationSnapshot: options.navigationSnapshot,
|
|
301
|
+
navId: options.navId,
|
|
302
|
+
nextElements: options.payload,
|
|
303
|
+
onCommittedState: options.onCommittedState,
|
|
304
|
+
operationLane: options.operationLane ?? "navigation",
|
|
305
|
+
params: options.params,
|
|
306
|
+
payloadOrigin: options.payloadOrigin,
|
|
307
|
+
pendingRouterState: options.pendingRouterState,
|
|
308
|
+
previousNextUrl: options.previousNextUrl,
|
|
309
|
+
restoredBfcacheIds: options.restoredBfcacheIds ?? null,
|
|
310
|
+
reuseCurrentBfcacheIds: options.reuseCurrentBfcacheIds ?? true,
|
|
311
|
+
scrollIntent: options.scrollIntent ?? null,
|
|
312
|
+
targetHistoryIndex: options.traversalIntent?.targetHistoryIndex,
|
|
313
|
+
targetHref: options.targetHref,
|
|
314
|
+
visibleCommitMode: options.visibleCommitMode ?? "transition"
|
|
327
315
|
});
|
|
328
316
|
}
|
|
329
317
|
async function commitSameUrlNavigatePayload(nextElements, actionInitiation, returnValue, revalidation = "none") {
|
|
@@ -346,8 +334,8 @@ function evictVisitedResponseCacheIfNeeded() {
|
|
|
346
334
|
}
|
|
347
335
|
function readVisitedResponseCacheCandidate(rscUrl, interceptionContext, mountedSlotsHeader, navigationKind) {
|
|
348
336
|
const cacheKey = AppElementsWire.encodeCacheKey(rscUrl, interceptionContext);
|
|
349
|
-
const
|
|
350
|
-
if (!
|
|
337
|
+
const match = findVisitedResponseCacheEntry(visitedResponseCache, rscUrl, interceptionContext);
|
|
338
|
+
if (!match) return {
|
|
351
339
|
cacheKey,
|
|
352
340
|
entry: null,
|
|
353
341
|
facts: {
|
|
@@ -356,15 +344,15 @@ function readVisitedResponseCacheCandidate(rscUrl, interceptionContext, mountedS
|
|
|
356
344
|
}
|
|
357
345
|
};
|
|
358
346
|
return {
|
|
359
|
-
cacheKey,
|
|
360
|
-
entry:
|
|
347
|
+
cacheKey: match.cacheKey,
|
|
348
|
+
entry: match.entry,
|
|
361
349
|
facts: {
|
|
362
350
|
candidate: "present",
|
|
363
|
-
fresh: isVisitedResponseCacheEntryFresh(
|
|
351
|
+
fresh: isVisitedResponseCacheEntryFresh(match.entry, {
|
|
364
352
|
navigationKind,
|
|
365
353
|
now: Date.now()
|
|
366
354
|
}),
|
|
367
|
-
mountedSlotsMatch:
|
|
355
|
+
mountedSlotsMatch: match.entry.elements !== void 0 || match.entry.mountedSlotsHeader === mountedSlotsHeader,
|
|
368
356
|
navigationKind
|
|
369
357
|
}
|
|
370
358
|
};
|
|
@@ -380,9 +368,9 @@ function applyVisitedResponseCacheCandidateDecision(candidate, decision) {
|
|
|
380
368
|
return null;
|
|
381
369
|
}
|
|
382
370
|
function deleteVisitedResponse(rscUrl, interceptionContext) {
|
|
383
|
-
visitedResponseCache
|
|
371
|
+
deleteVisitedResponseCacheEntry(visitedResponseCache, rscUrl, interceptionContext);
|
|
384
372
|
}
|
|
385
|
-
function storeVisitedResponseSnapshot(rscUrl, interceptionContext, snapshot, params, prefetchFallbackTtlMs = DYNAMIC_NAVIGATION_CACHE_TTL, requestMountedSlotsHeader = snapshot.mountedSlotsHeader ?? null, elements) {
|
|
373
|
+
function storeVisitedResponseSnapshot(rscUrl, interceptionContext, snapshot, params, prefetchFallbackTtlMs = DYNAMIC_NAVIGATION_CACHE_TTL, requestMountedSlotsHeader = snapshot.mountedSlotsHeader ?? null, elements, seedPrefetchCache = true) {
|
|
386
374
|
const cacheKey = AppElementsWire.encodeCacheKey(rscUrl, interceptionContext);
|
|
387
375
|
visitedResponseCache.delete(cacheKey);
|
|
388
376
|
evictVisitedResponseCacheIfNeeded();
|
|
@@ -395,10 +383,10 @@ function storeVisitedResponseSnapshot(rscUrl, interceptionContext, snapshot, par
|
|
|
395
383
|
response: snapshot
|
|
396
384
|
});
|
|
397
385
|
visitedResponseCache.set(cacheKey, entry);
|
|
398
|
-
seedPrefetchResponseSnapshot(rscUrl, snapshot, interceptionContext, requestMountedSlotsHeader, prefetchFallbackTtlMs);
|
|
386
|
+
if (seedPrefetchCache) seedPrefetchResponseSnapshot(rscUrl, snapshot, interceptionContext, requestMountedSlotsHeader, prefetchFallbackTtlMs);
|
|
399
387
|
return () => {
|
|
400
388
|
if (visitedResponseCache.get(cacheKey) === entry) visitedResponseCache.delete(cacheKey);
|
|
401
|
-
deletePrefetchResponseSnapshot(rscUrl, snapshot, interceptionContext);
|
|
389
|
+
if (seedPrefetchCache) deletePrefetchResponseSnapshot(rscUrl, snapshot, interceptionContext);
|
|
402
390
|
};
|
|
403
391
|
}
|
|
404
392
|
function clientNavigationSnapshotHref(snapshot) {
|
|
@@ -725,7 +713,21 @@ function registerServerActionCallback() {
|
|
|
725
713
|
const hashIdx = target.href.indexOf("#");
|
|
726
714
|
const actionScrollIntent = beginAppRouterScrollIntent((hashIdx !== -1 ? target.href.slice(hashIdx) : "") || null);
|
|
727
715
|
if (target.type === "push") saveScrollPosition();
|
|
728
|
-
renderNavigationPayload(
|
|
716
|
+
renderNavigationPayload({
|
|
717
|
+
actionType: target.type === "push" ? "navigate" : "replace",
|
|
718
|
+
historyUpdateMode: target.type === "push" ? "push" : "replace",
|
|
719
|
+
navigationInitiationState: actionInitiation.routerState,
|
|
720
|
+
navigationSnapshot: createClientNavigationRenderSnapshot(target.href, actionInitiation.routerState.navigationSnapshot.params),
|
|
721
|
+
navId: actionInitiation.navigationId,
|
|
722
|
+
operationLane: resolveServerActionOperationLane(revalidation),
|
|
723
|
+
params: {},
|
|
724
|
+
payload: Promise.resolve(elements),
|
|
725
|
+
payloadOrigin: FRESH_APP_NAVIGATION_PAYLOAD_ORIGIN,
|
|
726
|
+
pendingRouterState: null,
|
|
727
|
+
previousNextUrl: null,
|
|
728
|
+
scrollIntent: actionScrollIntent,
|
|
729
|
+
targetHref: target.href
|
|
730
|
+
}).catch(() => {
|
|
729
731
|
browserNavigationController.performHardNavigation(target.href);
|
|
730
732
|
});
|
|
731
733
|
},
|
|
@@ -763,6 +765,10 @@ function bootstrapHydration(rscStream, devErrorOverlay, initialRscBootstrap) {
|
|
|
763
765
|
Promise.all([root, initialCacheBuffer]).then(async ([elements, buffer]) => {
|
|
764
766
|
if (cacheGeneration !== clientNavigationCacheGeneration) return;
|
|
765
767
|
const metadata = AppElementsWire.readMetadata(elements);
|
|
768
|
+
initialPrefetchRouterState = {
|
|
769
|
+
pathAndSearch: initialPathAndSearch,
|
|
770
|
+
routeId: metadata.routeId
|
|
771
|
+
};
|
|
766
772
|
if (!isCompleteAppPayloadMetadata(metadata)) return;
|
|
767
773
|
const mountedSlotsHeader = getMountedSlotIdsHeader(elements);
|
|
768
774
|
const headers = createRscRequestHeaders({ mountedSlotsHeader });
|
|
@@ -781,8 +787,7 @@ function bootstrapHydration(rscStream, devErrorOverlay, initialRscBootstrap) {
|
|
|
781
787
|
const fallbackTtlMs = initialRscBootstrap?.initialCacheKind === "static" ? PREFETCH_CACHE_TTL : DYNAMIC_NAVIGATION_CACHE_TTL;
|
|
782
788
|
hydrationCachePublication.publish(() => {
|
|
783
789
|
if (cacheGeneration !== clientNavigationCacheGeneration) return () => {};
|
|
784
|
-
|
|
785
|
-
return storeVisitedResponseSnapshot(rscUrl, metadata.interceptionContext, snapshot, initialParams, fallbackTtlMs, mountedSlotsHeader, elements);
|
|
790
|
+
return storeVisitedResponseSnapshot(rscUrl, metadata.interceptionContext, snapshot, initialParams, fallbackTtlMs, mountedSlotsHeader, elements, false);
|
|
786
791
|
});
|
|
787
792
|
}).catch(() => {});
|
|
788
793
|
historyController.writeBootstrapHistoryMetadata();
|
|
@@ -840,6 +845,26 @@ function bootstrapHydration(rscStream, devErrorOverlay, initialRscBootstrap) {
|
|
|
840
845
|
registerNavigationRuntimeFunctions({
|
|
841
846
|
clearNavigationCaches: clearClientNavigationCaches,
|
|
842
847
|
commitHashNavigation: (href, historyUpdateMode, scroll) => historyController.commitHashOnlyNavigation(href, historyUpdateMode, scroll),
|
|
848
|
+
getPrefetchRouterState: () => {
|
|
849
|
+
if (!browserNavigationController.hasBrowserRouterState()) {
|
|
850
|
+
if (initialPrefetchRouterState) return initialPrefetchRouterState;
|
|
851
|
+
const routeManifest = getNavigationRuntime()?.bootstrap.routeManifest ?? null;
|
|
852
|
+
const match = routeManifest ? matchOptimisticRouteManifestRoute({
|
|
853
|
+
basePath: __basePath,
|
|
854
|
+
href: window.location.href,
|
|
855
|
+
routeManifest
|
|
856
|
+
}) : null;
|
|
857
|
+
return match ? {
|
|
858
|
+
pathAndSearch: createBasePathStrippedPathAndSearch(new URL(window.location.href), __basePath),
|
|
859
|
+
routeId: match.route.id
|
|
860
|
+
} : null;
|
|
861
|
+
}
|
|
862
|
+
const state = browserNavigationController.getBrowserRouterState();
|
|
863
|
+
return {
|
|
864
|
+
pathAndSearch: createSnapshotPathAndSearch(state.navigationSnapshot),
|
|
865
|
+
routeId: state.routeId
|
|
866
|
+
};
|
|
867
|
+
},
|
|
843
868
|
navigate: async function navigateRsc(href, redirectDepth = 0, navigationKind = "navigate", historyUpdateMode, previousNextUrlOverride, programmaticTransition = false, traversalIntent, scrollIntent, visibleCommitMode = "transition") {
|
|
844
869
|
abortSupersededNavigation();
|
|
845
870
|
const navigationAbortController = new AbortController();
|
|
@@ -864,12 +889,15 @@ function bootstrapHydration(rscStream, devErrorOverlay, initialRscBootstrap) {
|
|
|
864
889
|
const reuseCurrentBfcacheIds = navigationKind !== "traverse" || !historyController.isCacheInvalidationGuarded() && historyController.isCurrentBfcacheVersion(activeTraversalIntent?.historyState ?? window.history.state);
|
|
865
890
|
try {
|
|
866
891
|
const shouldUsePendingRouterState = programmaticTransition;
|
|
867
|
-
if (
|
|
868
|
-
|
|
892
|
+
if (hasBrowserRouterState()) {
|
|
893
|
+
if (shouldUsePendingRouterState) pendingRouterState = beginPendingBrowserRouterState();
|
|
894
|
+
} else {
|
|
869
895
|
await waitForBrowserRouterStateReady();
|
|
870
896
|
if (!browserNavigationController.isCurrentNavigation(navId)) return;
|
|
871
897
|
if (shouldUsePendingRouterState) pendingRouterState = beginPendingBrowserRouterState();
|
|
872
898
|
}
|
|
899
|
+
const navigationInitiationState = getBrowserRouterState();
|
|
900
|
+
const mountedSlotsHeader = getMountedSlotIdsHeader(navigationInitiationState.elements);
|
|
873
901
|
while (true) {
|
|
874
902
|
const url = new URL(currentHref, window.location.origin);
|
|
875
903
|
const requestState = getRequestState(navigationKind, url.pathname, currentPrevNextUrl, activeTraversalIntent?.historyState);
|
|
@@ -877,24 +905,30 @@ function bootstrapHydration(rscStream, devErrorOverlay, initialRscBootstrap) {
|
|
|
877
905
|
const requestPreviousNextUrl = requestState.previousNextUrl;
|
|
878
906
|
if (navigationKind === "refresh") historyController.syncCurrentHistoryStatePreviousNextUrl(requestPreviousNextUrl, getBrowserRouterState().bfcacheIds);
|
|
879
907
|
setPendingPathname(url.pathname, navId);
|
|
880
|
-
const routerStateAtNavStart = getBrowserRouterState();
|
|
881
|
-
const elementsAtNavStart = routerStateAtNavStart.elements;
|
|
882
|
-
const mountedSlotsHeader = getMountedSlotIdsHeader(elementsAtNavStart);
|
|
883
908
|
const earlyIntentDecision = navigationKind === "navigate" ? navigationPlanner.classifyEarlyNavigationIntent({
|
|
884
909
|
basePath: __basePath,
|
|
885
|
-
currentHref: clientNavigationSnapshotHref(
|
|
910
|
+
currentHref: clientNavigationSnapshotHref(navigationInitiationState.navigationSnapshot),
|
|
886
911
|
mode: "push",
|
|
887
912
|
scroll: false,
|
|
888
913
|
targetHref: url.href
|
|
889
914
|
}) : null;
|
|
890
915
|
const shouldBypassNavigationCache = earlyIntentDecision?.kind === "flightNavigation" && earlyIntentDecision.bypassNavigationCache;
|
|
891
916
|
const requestHeaders = createRscRequestHeaders({
|
|
917
|
+
fetchPriority: "auto",
|
|
892
918
|
interceptionContext: requestInterceptionContext,
|
|
893
919
|
mountedSlotsHeader
|
|
894
920
|
});
|
|
895
|
-
const rscUrl = await createRscRequestUrl(url.pathname + url.search, requestHeaders);
|
|
896
921
|
const rewrittenNavigationHref = navigationKind === "navigate" && HAS_CLIENT_REWRITES ? resolveLoadedHybridClientRewriteHref(currentHref, __basePath) : null;
|
|
897
|
-
const
|
|
922
|
+
const targetPathAndSearch = url.pathname + url.search;
|
|
923
|
+
const additionalPrefetchPathAndSearch = rewrittenNavigationHref && rewrittenNavigationHref !== currentHref ? [rewrittenNavigationHref] : [];
|
|
924
|
+
const settledPrefetchedResponse = navigationKind === "navigate" && !shouldBypassNavigationCache ? consumePrefetchResponse(targetPathAndSearch, requestInterceptionContext, mountedSlotsHeader, { additionalRscUrls: additionalPrefetchPathAndSearch }) : null;
|
|
925
|
+
const rscUrl = settledPrefetchedResponse ? resolvePrefetchNavigationResponseUrl({
|
|
926
|
+
additionalRscUrls: additionalPrefetchPathAndSearch,
|
|
927
|
+
origin: window.location.origin,
|
|
928
|
+
responseUrl: settledPrefetchedResponse.url,
|
|
929
|
+
visibleRscUrl: targetPathAndSearch
|
|
930
|
+
}) : await createRscRequestUrl(targetPathAndSearch, requestHeaders);
|
|
931
|
+
const additionalPrefetchRscUrls = settledPrefetchedResponse ? additionalPrefetchPathAndSearch : await Promise.all(additionalPrefetchPathAndSearch.map((href) => createRscRequestUrl(href, requestHeaders)));
|
|
898
932
|
const visitedResponseCandidate = shouldBypassNavigationCache ? {
|
|
899
933
|
cacheKey: AppElementsWire.encodeCacheKey(rscUrl, requestInterceptionContext),
|
|
900
934
|
entry: null,
|
|
@@ -911,7 +945,7 @@ function bootstrapHydration(rscStream, devErrorOverlay, initialRscBootstrap) {
|
|
|
911
945
|
visitedResponse
|
|
912
946
|
});
|
|
913
947
|
let routeManifest = navigationKind === "navigate" ? getBrowserRouteManifest() : null;
|
|
914
|
-
const hasPrefetchCandidate = prefetchProbeDecision.kind === "probe" && hasPrefetchCacheEntryForNavigation(rscUrl, requestInterceptionContext, mountedSlotsHeader, {
|
|
948
|
+
const hasPrefetchCandidate = settledPrefetchedResponse !== null || prefetchProbeDecision.kind === "probe" && hasPrefetchCacheEntryForNavigation(rscUrl, requestInterceptionContext, mountedSlotsHeader, {
|
|
915
949
|
additionalRscUrls: additionalPrefetchRscUrls,
|
|
916
950
|
notifyInvalidation: false
|
|
917
951
|
});
|
|
@@ -960,7 +994,26 @@ function bootstrapHydration(rscStream, devErrorOverlay, initialRscBootstrap) {
|
|
|
960
994
|
const cachedNavigationSnapshot = createClientNavigationRenderSnapshot(currentHref, cachedParams);
|
|
961
995
|
const cachedPayload = cachedRoute.elements ? Promise.resolve(cachedRoute.elements) : decodeAppElementsPromise(createFromFetch(Promise.resolve(restoreRscResponse(cachedRoute.response))));
|
|
962
996
|
if (!browserNavigationController.isCurrentNavigation(navId)) return;
|
|
963
|
-
if (await renderNavigationPayload(
|
|
997
|
+
if (await renderNavigationPayload({
|
|
998
|
+
actionType: toActionType(navigationKind),
|
|
999
|
+
historyUpdateMode: currentHistoryMode,
|
|
1000
|
+
navigationCommitKind: detachedNavigationCommits ? "authoritative" : void 0,
|
|
1001
|
+
navigationInitiationState,
|
|
1002
|
+
navigationSnapshot: cachedNavigationSnapshot,
|
|
1003
|
+
navId,
|
|
1004
|
+
operationLane: toOperationLane(navigationKind),
|
|
1005
|
+
params: cachedParams,
|
|
1006
|
+
payload: cachedPayload,
|
|
1007
|
+
payloadOrigin: cachedRoute.elements ? COMMITTED_CACHE_APP_NAVIGATION_PAYLOAD_ORIGIN : VISITED_CACHE_APP_NAVIGATION_PAYLOAD_ORIGIN,
|
|
1008
|
+
pendingRouterState: detachedNavigationCommits ? null : pendingRouterState,
|
|
1009
|
+
previousNextUrl: requestPreviousNextUrl,
|
|
1010
|
+
restoredBfcacheIds,
|
|
1011
|
+
reuseCurrentBfcacheIds,
|
|
1012
|
+
scrollIntent,
|
|
1013
|
+
targetHref: currentHref,
|
|
1014
|
+
traversalIntent: activeTraversalIntent,
|
|
1015
|
+
visibleCommitMode
|
|
1016
|
+
}) === "no-commit") {
|
|
964
1017
|
if (!browserNavigationController.isCurrentNavigation(navId)) return;
|
|
965
1018
|
deleteVisitedResponse(rscUrl, requestInterceptionContext);
|
|
966
1019
|
continue;
|
|
@@ -970,17 +1023,24 @@ function bootstrapHydration(rscStream, devErrorOverlay, initialRscBootstrap) {
|
|
|
970
1023
|
let navResponse;
|
|
971
1024
|
let navResponseExpiresAt;
|
|
972
1025
|
let navResponseUrl = null;
|
|
1026
|
+
let prefetchedElements;
|
|
973
1027
|
let fallbackReuseDecision = reuseDecision;
|
|
974
1028
|
if (reuseDecision.kind === "consumePrefetch") {
|
|
975
|
-
const prefetchedResponse = await consumePrefetchResponseForNavigation(rscUrl, requestInterceptionContext, mountedSlotsHeader, {
|
|
1029
|
+
const prefetchedResponse = settledPrefetchedResponse ?? await consumePrefetchResponseForNavigation(rscUrl, requestInterceptionContext, mountedSlotsHeader, {
|
|
976
1030
|
additionalRscUrls: additionalPrefetchRscUrls,
|
|
977
1031
|
shouldConsume: () => browserNavigationController.isCurrentNavigation(navId)
|
|
978
1032
|
});
|
|
979
1033
|
if (!browserNavigationController.isCurrentNavigation(navId)) return;
|
|
980
1034
|
if (prefetchedResponse) {
|
|
1035
|
+
prefetchedElements = prefetchedResponse.preparedElements;
|
|
981
1036
|
navResponse = restoreRscResponse(prefetchedResponse, false);
|
|
982
1037
|
navResponseExpiresAt = prefetchedResponse.expiresAt;
|
|
983
|
-
navResponseUrl =
|
|
1038
|
+
navResponseUrl = resolvePrefetchNavigationResponseUrl({
|
|
1039
|
+
additionalRscUrls: additionalPrefetchRscUrls,
|
|
1040
|
+
origin: window.location.origin,
|
|
1041
|
+
responseUrl: prefetchedResponse.url,
|
|
1042
|
+
visibleRscUrl: rscUrl
|
|
1043
|
+
});
|
|
984
1044
|
}
|
|
985
1045
|
if (!navResponse) {
|
|
986
1046
|
routeManifest = navigationKind === "navigate" ? getBrowserRouteManifest() : null;
|
|
@@ -1016,7 +1076,26 @@ function bootstrapHydration(rscStream, devErrorOverlay, initialRscBootstrap) {
|
|
|
1016
1076
|
if (optimisticPayload !== null) {
|
|
1017
1077
|
detachedNavigationCommits = true;
|
|
1018
1078
|
const optimisticNavigationSnapshot = createClientNavigationRenderSnapshot(currentHref, optimisticPayload.params);
|
|
1019
|
-
renderNavigationPayload(
|
|
1079
|
+
renderNavigationPayload({
|
|
1080
|
+
actionType: toActionType(navigationKind),
|
|
1081
|
+
historyUpdateMode: currentHistoryMode,
|
|
1082
|
+
navigationCommitKind: "detached",
|
|
1083
|
+
navigationInitiationState,
|
|
1084
|
+
navigationSnapshot: optimisticNavigationSnapshot,
|
|
1085
|
+
navId,
|
|
1086
|
+
operationLane: toOperationLane(navigationKind),
|
|
1087
|
+
params: optimisticPayload.params,
|
|
1088
|
+
payload: Promise.resolve(optimisticPayload.elements),
|
|
1089
|
+
payloadOrigin: FRESH_APP_NAVIGATION_PAYLOAD_ORIGIN,
|
|
1090
|
+
pendingRouterState: null,
|
|
1091
|
+
previousNextUrl: requestPreviousNextUrl,
|
|
1092
|
+
restoredBfcacheIds,
|
|
1093
|
+
reuseCurrentBfcacheIds,
|
|
1094
|
+
scrollIntent,
|
|
1095
|
+
targetHref: currentHref,
|
|
1096
|
+
traversalIntent: activeTraversalIntent,
|
|
1097
|
+
visibleCommitMode
|
|
1098
|
+
}).catch((error) => {
|
|
1020
1099
|
if (browserNavigationController.isCurrentNavigation(navId)) console.error("[vinext] Optimistic RSC navigation error:", error);
|
|
1021
1100
|
});
|
|
1022
1101
|
}
|
|
@@ -1024,12 +1103,13 @@ function bootstrapHydration(rscStream, devErrorOverlay, initialRscBootstrap) {
|
|
|
1024
1103
|
}
|
|
1025
1104
|
if (!navResponse) {
|
|
1026
1105
|
if (navigationKind === "navigate") {
|
|
1027
|
-
const clientReuseManifestHeader = createClientReuseManifestHeaderFromVisibleAppState(
|
|
1106
|
+
const clientReuseManifestHeader = createClientReuseManifestHeaderFromVisibleAppState(navigationInitiationState);
|
|
1028
1107
|
if (clientReuseManifestHeader !== null) requestHeaders.set(VINEXT_CLIENT_REUSE_MANIFEST_HEADER, clientReuseManifestHeader);
|
|
1029
1108
|
}
|
|
1030
1109
|
navResponse = await fetch(rscUrl, {
|
|
1031
1110
|
headers: requestHeaders,
|
|
1032
1111
|
credentials: "include",
|
|
1112
|
+
priority: "auto",
|
|
1033
1113
|
signal: navigationAbortController.signal
|
|
1034
1114
|
});
|
|
1035
1115
|
}
|
|
@@ -1083,13 +1163,35 @@ function bootstrapHydration(rscStream, devErrorOverlay, initialRscBootstrap) {
|
|
|
1083
1163
|
const cacheBufferPromise = new Response(cacheBranch).arrayBuffer();
|
|
1084
1164
|
cacheBufferPromise.catch(() => {});
|
|
1085
1165
|
if (!browserNavigationController.isCurrentNavigation(navId)) return;
|
|
1086
|
-
|
|
1166
|
+
if (prefetchedElements) reactBranch.cancel().catch(() => {});
|
|
1167
|
+
const rscPayload = prefetchedElements ? prefetchedElements : decodeAppElementsPromise(createFromFetch(Promise.resolve(reactResponse)));
|
|
1087
1168
|
if (!browserNavigationController.isCurrentNavigation(navId)) return;
|
|
1088
1169
|
let committedState = null;
|
|
1089
|
-
if (await renderNavigationPayload(
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1170
|
+
if (await renderNavigationPayload({
|
|
1171
|
+
actionType: toActionType(navigationKind),
|
|
1172
|
+
historyUpdateMode: currentHistoryMode,
|
|
1173
|
+
navigationCommitKind: detachedNavigationCommits ? "authoritative" : void 0,
|
|
1174
|
+
navigationInitiationState,
|
|
1175
|
+
navigationSnapshot,
|
|
1176
|
+
navId,
|
|
1177
|
+
onCommittedState: (state) => {
|
|
1178
|
+
committedState = state;
|
|
1179
|
+
if (activeNavigationAbortController === navigationAbortController) activeNavigationAbortController = null;
|
|
1180
|
+
},
|
|
1181
|
+
operationLane: toOperationLane(navigationKind),
|
|
1182
|
+
params: navParams,
|
|
1183
|
+
payload: rscPayload,
|
|
1184
|
+
payloadOrigin: FRESH_APP_NAVIGATION_PAYLOAD_ORIGIN,
|
|
1185
|
+
pendingRouterState: detachedNavigationCommits ? null : pendingRouterState,
|
|
1186
|
+
previousNextUrl: requestPreviousNextUrl,
|
|
1187
|
+
restoredBfcacheIds,
|
|
1188
|
+
reuseCurrentBfcacheIds,
|
|
1189
|
+
scrollIntent,
|
|
1190
|
+
targetHref: currentHref,
|
|
1191
|
+
traversalIntent: activeTraversalIntent,
|
|
1192
|
+
visibleCommitMode: prefetchedElements ? "synchronous" : visibleCommitMode
|
|
1193
|
+
}) !== "committed") return;
|
|
1194
|
+
committedState ??= getBrowserRouterState();
|
|
1093
1195
|
try {
|
|
1094
1196
|
const renderedElements = await rscPayload;
|
|
1095
1197
|
if (navigationCacheGeneration !== clientNavigationCacheGeneration) return;
|
|
@@ -1110,7 +1212,7 @@ function bootstrapHydration(rscStream, devErrorOverlay, initialRscBootstrap) {
|
|
|
1110
1212
|
if (isCacheRestorableAppPayloadMetadata(metadata)) {
|
|
1111
1213
|
if (navigationCacheGeneration !== clientNavigationCacheGeneration) return;
|
|
1112
1214
|
storeVisitedResponseSnapshot(rscUrl, interceptionContext, snapshot, navParams, PREFETCH_CACHE_TTL, mountedSlotsHeader);
|
|
1113
|
-
} else
|
|
1215
|
+
} else {
|
|
1114
1216
|
const state = committedState;
|
|
1115
1217
|
const committedElements = {
|
|
1116
1218
|
...state.elements,
|
|
@@ -1121,9 +1223,6 @@ function bootstrapHydration(rscStream, devErrorOverlay, initialRscBootstrap) {
|
|
|
1121
1223
|
};
|
|
1122
1224
|
if (navigationCacheGeneration !== clientNavigationCacheGeneration) return;
|
|
1123
1225
|
storeVisitedResponseSnapshot(rscUrl, interceptionContext, snapshot, navParams, DYNAMIC_NAVIGATION_CACHE_TTL, mountedSlotsHeader, committedElements);
|
|
1124
|
-
} else {
|
|
1125
|
-
if (navigationCacheGeneration !== clientNavigationCacheGeneration) return;
|
|
1126
|
-
seedPrefetchResponseSnapshot(rscUrl, snapshot, interceptionContext, mountedSlotsHeader, DYNAMIC_NAVIGATION_CACHE_TTL);
|
|
1127
1226
|
}
|
|
1128
1227
|
} catch {}
|
|
1129
1228
|
return;
|
|
@@ -1137,7 +1236,8 @@ function bootstrapHydration(rscStream, devErrorOverlay, initialRscBootstrap) {
|
|
|
1137
1236
|
browserNavigationController.finalizeNavigation(navId, pendingRouterState);
|
|
1138
1237
|
discardedServerActionRefreshScheduler.markNavigationSettled();
|
|
1139
1238
|
}
|
|
1140
|
-
}
|
|
1239
|
+
},
|
|
1240
|
+
preparePrefetchResponse: (response) => decodeAppElementsPromise(createFromFetch(Promise.resolve(response)))
|
|
1141
1241
|
});
|
|
1142
1242
|
const handlePopstate = createPopstateRestoreHandler({
|
|
1143
1243
|
getActiveNavigationId: browserNavigationController.getActiveNavigationId.bind(browserNavigationController),
|
|
@@ -43,6 +43,28 @@ type BrowserNavigationControllerDeps = {
|
|
|
43
43
|
getRouteManifest?: () => RouteManifest | null;
|
|
44
44
|
syncHistoryStatePreviousNextUrl?: (previousNextUrl: string | null, bfcacheIds?: Readonly<Record<string, string>> | null) => void;
|
|
45
45
|
};
|
|
46
|
+
type BrowserNavigationPayloadOptions = {
|
|
47
|
+
actionType: "navigate" | "replace" | "traverse";
|
|
48
|
+
createNavigationCommitEffect: BrowserNavigationCommitEffectFactory;
|
|
49
|
+
historyUpdateMode: HistoryUpdateMode | undefined;
|
|
50
|
+
navigationCommitKind?: "authoritative" | "detached";
|
|
51
|
+
navigationInitiationState: AppRouterState;
|
|
52
|
+
navigationSnapshot: ClientNavigationRenderSnapshot;
|
|
53
|
+
navId: number;
|
|
54
|
+
nextElements: Promise<AppElements> | AppElements;
|
|
55
|
+
onCommittedState?: (state: AppRouterState) => void;
|
|
56
|
+
operationLane: OperationLane;
|
|
57
|
+
params: Record<string, string | string[]>;
|
|
58
|
+
payloadOrigin: AppNavigationPayloadOrigin;
|
|
59
|
+
pendingRouterState: PendingBrowserRouterState | null;
|
|
60
|
+
previousNextUrl: string | null;
|
|
61
|
+
restoredBfcacheIds?: Readonly<Record<string, string>> | null;
|
|
62
|
+
reuseCurrentBfcacheIds?: boolean;
|
|
63
|
+
scrollIntent?: AppRouterScrollIntent | null;
|
|
64
|
+
targetHistoryIndex?: number | null;
|
|
65
|
+
targetHref: string;
|
|
66
|
+
visibleCommitMode?: NavigationRuntimeVisibleCommitMode;
|
|
67
|
+
};
|
|
46
68
|
type BrowserNavigationController = {
|
|
47
69
|
beginNavigation(): number;
|
|
48
70
|
getActiveNavigationId(): number;
|
|
@@ -60,27 +82,7 @@ type BrowserNavigationController = {
|
|
|
60
82
|
state: AppRouterState;
|
|
61
83
|
targetHref: string;
|
|
62
84
|
}): boolean;
|
|
63
|
-
renderNavigationPayload(options:
|
|
64
|
-
actionType: "navigate" | "replace" | "traverse";
|
|
65
|
-
createNavigationCommitEffect: BrowserNavigationCommitEffectFactory;
|
|
66
|
-
historyUpdateMode: HistoryUpdateMode | undefined;
|
|
67
|
-
navigationSnapshot: ClientNavigationRenderSnapshot;
|
|
68
|
-
nextElements: Promise<AppElements>;
|
|
69
|
-
operationLane: OperationLane;
|
|
70
|
-
payloadOrigin: AppNavigationPayloadOrigin;
|
|
71
|
-
params: Record<string, string | string[]>;
|
|
72
|
-
pendingRouterState: PendingBrowserRouterState | null;
|
|
73
|
-
previousNextUrl: string | null;
|
|
74
|
-
scrollIntent?: AppRouterScrollIntent | null;
|
|
75
|
-
restoredBfcacheIds?: Readonly<Record<string, string>> | null;
|
|
76
|
-
reuseCurrentBfcacheIds?: boolean;
|
|
77
|
-
targetHistoryIndex?: number | null;
|
|
78
|
-
targetHref: string;
|
|
79
|
-
navId: number;
|
|
80
|
-
navigationCommitKind?: "authoritative" | "detached";
|
|
81
|
-
visibleCommitMode?: NavigationRuntimeVisibleCommitMode;
|
|
82
|
-
onCommittedState?: (state: AppRouterState) => void;
|
|
83
|
-
}): Promise<NavigationPayloadOutcome>;
|
|
85
|
+
renderNavigationPayload(options: BrowserNavigationPayloadOptions): Promise<NavigationPayloadOutcome>;
|
|
84
86
|
commitSameUrlNavigatePayload(nextElements: Promise<AppElements>, navigationSnapshot: ClientNavigationRenderSnapshot, returnValue?: {
|
|
85
87
|
ok: boolean;
|
|
86
88
|
data: unknown;
|
|
@@ -3,7 +3,7 @@ import { clearAppNavigationFailureTarget, getAppNavigationFailureTarget } from "
|
|
|
3
3
|
import { claimAppRouterScrollIntentForCommit, consumeAppRouterScrollIntent } from "../shims/app-router-scroll-state.js";
|
|
4
4
|
import { resolveServerActionOperationLane, shouldScheduleRefreshForDiscardedServerAction } from "./app-browser-action-result.js";
|
|
5
5
|
import { activateNavigationSnapshot, clearPendingPathname, commitClientNavigationState, createSnapshotPathAndSearch } from "../shims/navigation.js";
|
|
6
|
-
import { FRESH_APP_NAVIGATION_PAYLOAD_ORIGIN, createPendingNavigationCommit } from "./app-browser-state.js";
|
|
6
|
+
import { FRESH_APP_NAVIGATION_PAYLOAD_ORIGIN, createPendingNavigationCommit, createPendingNavigationCommitFromElements } from "./app-browser-state.js";
|
|
7
7
|
import { applyApprovedVisibleCommit, approveHmrVisibleCommit, approvePendingNavigationCommit, resolveAndClassifyNavigationCommit } from "./app-browser-visible-commit.js";
|
|
8
8
|
import { startTransition, useInsertionEffect, useLayoutEffect } from "react";
|
|
9
9
|
import { flushSync } from "react-dom";
|
|
@@ -255,6 +255,9 @@ function createAppBrowserNavigationController(deps = {}) {
|
|
|
255
255
|
pendingRouterState.settled = true;
|
|
256
256
|
pendingRouterState.resolve(committedState);
|
|
257
257
|
if (activePendingBrowserRouterState === pendingRouterState) activePendingBrowserRouterState = null;
|
|
258
|
+
if (visibleCommitMode === "synchronous") flushSync(() => {
|
|
259
|
+
setter(committedState);
|
|
260
|
+
});
|
|
258
261
|
return;
|
|
259
262
|
}
|
|
260
263
|
if (visibleCommitMode === "synchronous") {
|
|
@@ -332,6 +335,22 @@ function createAppBrowserNavigationController(deps = {}) {
|
|
|
332
335
|
if (!shouldScheduleRefreshForDiscardedServerAction(lifecycleOptions?.revalidation ?? "none")) return;
|
|
333
336
|
lifecycleOptions?.onDiscardedRevalidation?.();
|
|
334
337
|
}
|
|
338
|
+
function isSamePathSearchHashCommit(targetHref) {
|
|
339
|
+
if (typeof window === "undefined") return false;
|
|
340
|
+
try {
|
|
341
|
+
const currentUrl = new URL(window.location.href);
|
|
342
|
+
const targetUrl = new URL(targetHref, currentUrl.href);
|
|
343
|
+
return targetUrl.origin === currentUrl.origin && targetUrl.pathname === currentUrl.pathname && targetUrl.search !== currentUrl.search && targetUrl.hash !== "";
|
|
344
|
+
} catch {
|
|
345
|
+
return false;
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
function shouldForceSynchronousCommit(options) {
|
|
349
|
+
if (options.actionType === "traverse") return false;
|
|
350
|
+
if (options.historyUpdateMode === void 0) return false;
|
|
351
|
+
if (options.scrollIntent?.hash == null) return false;
|
|
352
|
+
return isSamePathSearchHashCommit(options.targetHref);
|
|
353
|
+
}
|
|
335
354
|
async function renderNavigationPayload(options) {
|
|
336
355
|
if (options.navId === pendingUserNavigationId) pendingUserNavigationLane = options.operationLane;
|
|
337
356
|
const renderId = allocateRenderId();
|
|
@@ -348,11 +367,10 @@ function createAppBrowserNavigationController(deps = {}) {
|
|
|
348
367
|
});
|
|
349
368
|
let snapshotActivated = false;
|
|
350
369
|
try {
|
|
351
|
-
const
|
|
352
|
-
currentState:
|
|
370
|
+
const pendingOptions = {
|
|
371
|
+
currentState: options.navigationInitiationState,
|
|
353
372
|
navigationCommitKind: options.navigationCommitKind,
|
|
354
373
|
navigationId: options.navId,
|
|
355
|
-
nextElements: options.nextElements,
|
|
356
374
|
navigationSnapshot: options.navigationSnapshot,
|
|
357
375
|
operationLane: options.operationLane,
|
|
358
376
|
payloadOrigin: options.payloadOrigin,
|
|
@@ -361,6 +379,13 @@ function createAppBrowserNavigationController(deps = {}) {
|
|
|
361
379
|
restoredBfcacheIds: options.restoredBfcacheIds,
|
|
362
380
|
reuseCurrentBfcacheIds: options.reuseCurrentBfcacheIds,
|
|
363
381
|
type: options.actionType
|
|
382
|
+
};
|
|
383
|
+
const pending = options.nextElements instanceof Promise ? await createPendingNavigationCommit({
|
|
384
|
+
...pendingOptions,
|
|
385
|
+
nextElements: options.nextElements
|
|
386
|
+
}) : createPendingNavigationCommitFromElements({
|
|
387
|
+
...pendingOptions,
|
|
388
|
+
nextElements: options.nextElements
|
|
364
389
|
});
|
|
365
390
|
const approval = approvePendingNavigationCommit({
|
|
366
391
|
activeNavigationId,
|
|
@@ -402,7 +427,7 @@ function createAppBrowserNavigationController(deps = {}) {
|
|
|
402
427
|
claimAppRouterScrollIntentForCommit(options.scrollIntent, renderId);
|
|
403
428
|
activateNavigationSnapshot();
|
|
404
429
|
snapshotActivated = true;
|
|
405
|
-
dispatchApprovedVisibleCommit(renderId, approvedCommit, options.pendingRouterState, options.visibleCommitMode ?? "transition");
|
|
430
|
+
dispatchApprovedVisibleCommit(renderId, approvedCommit, options.pendingRouterState, shouldForceSynchronousCommit(options) ? "synchronous" : options.visibleCommitMode ?? "transition");
|
|
406
431
|
} catch (error) {
|
|
407
432
|
pendingNavigationFailureTargets.delete(renderId);
|
|
408
433
|
pendingNavigationPrePaintEffects.delete(renderId);
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
//#region src/server/app-browser-prefetch-response.d.ts
|
|
2
|
+
declare function resolvePrefetchNavigationResponseUrl(options: {
|
|
3
|
+
additionalRscUrls: readonly string[];
|
|
4
|
+
origin: string;
|
|
5
|
+
responseUrl: string;
|
|
6
|
+
visibleRscUrl: string;
|
|
7
|
+
}): string;
|
|
8
|
+
//#endregion
|
|
9
|
+
export { resolvePrefetchNavigationResponseUrl };
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { stripRscCacheBustingSearchParam } from "./app-rsc-cache-busting.js";
|
|
2
|
+
//#region src/server/app-browser-prefetch-response.ts
|
|
3
|
+
function normalizeBrowserRscUrlForReuse(url, origin) {
|
|
4
|
+
if (!url) return null;
|
|
5
|
+
try {
|
|
6
|
+
const parsed = new URL(url, origin);
|
|
7
|
+
stripRscCacheBustingSearchParam(parsed);
|
|
8
|
+
return `${parsed.pathname}${parsed.search}`;
|
|
9
|
+
} catch {
|
|
10
|
+
return null;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
function isSameOriginBrowserRscUrl(url, origin) {
|
|
14
|
+
try {
|
|
15
|
+
return new URL(url, origin).origin === new URL(origin).origin;
|
|
16
|
+
} catch {
|
|
17
|
+
return false;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
function canonicalizeBrowserRscResponseUrl(responseUrl, origin) {
|
|
21
|
+
try {
|
|
22
|
+
const parsed = new URL(responseUrl, origin);
|
|
23
|
+
if (parsed.origin === new URL(origin).origin) return `${parsed.pathname}${parsed.search}`;
|
|
24
|
+
} catch {}
|
|
25
|
+
return responseUrl;
|
|
26
|
+
}
|
|
27
|
+
function resolvePrefetchNavigationResponseUrl(options) {
|
|
28
|
+
const normalizedResponseUrl = normalizeBrowserRscUrlForReuse(options.responseUrl, options.origin);
|
|
29
|
+
return normalizedResponseUrl !== null && isSameOriginBrowserRscUrl(options.responseUrl, options.origin) && options.additionalRscUrls.some((additionalRscUrl) => normalizeBrowserRscUrlForReuse(additionalRscUrl, options.origin) === normalizedResponseUrl) ? options.visibleRscUrl : canonicalizeBrowserRscResponseUrl(options.responseUrl, options.origin);
|
|
30
|
+
}
|
|
31
|
+
//#endregion
|
|
32
|
+
export { resolvePrefetchNavigationResponseUrl };
|