diff --git a/packages/next/src/client/components/router-reducer/create-initial-router-state.ts b/packages/next/src/client/components/router-reducer/create-initial-router-state.ts index 9e314b6dc08d4..beaa8ccf8de33 100644 --- a/packages/next/src/client/components/router-reducer/create-initial-router-state.ts +++ b/packages/next/src/client/components/router-reducer/create-initial-router-state.ts @@ -102,7 +102,7 @@ export function createInitialRouterState({ createPrefetchCacheEntryForInitialLoad({ url, kind: PrefetchKind.AUTO, - data: [initialFlightData, undefined, couldBeIntercepted], + data: { f: initialFlightData, c: undefined, i: !!couldBeIntercepted }, tree: initialState.tree, prefetchCache: initialState.prefetchCache, nextUrl: initialState.nextUrl, diff --git a/packages/next/src/client/components/router-reducer/fetch-server-response.ts b/packages/next/src/client/components/router-reducer/fetch-server-response.ts index 7ca5a44909427..11f9b98a7f8c0 100644 --- a/packages/next/src/client/components/router-reducer/fetch-server-response.ts +++ b/packages/next/src/client/components/router-reducer/fetch-server-response.ts @@ -13,8 +13,8 @@ const { createFromFetch } = ( import type { FlightRouterState, - FlightData, NavigationFlightResponse, + FetchServerResponseResult, } from '../../../server/app-render/types' import { NEXT_ROUTER_PREFETCH_HEADER, @@ -37,12 +37,6 @@ export interface FetchServerResponseOptions { readonly isHmrRefresh?: boolean } -export type FetchServerResponseResult = [ - flightData: FlightData, - canonicalUrlOverride: URL | undefined, - intercepted?: boolean, -] - function urlToUrlWithoutFlightMarker(url: string): URL { const urlWithoutFlightParameters = new URL(url, location.origin) urlWithoutFlightParameters.searchParams.delete(NEXT_RSC_UNION_QUERY) @@ -61,7 +55,11 @@ function urlToUrlWithoutFlightMarker(url: string): URL { } function doMpaNavigation(url: string): FetchServerResponseResult { - return [urlToUrlWithoutFlightMarker(url).toString(), undefined, false] + return { + f: urlToUrlWithoutFlightMarker(url).toString(), + c: undefined, + i: false, + } } /** @@ -191,7 +189,11 @@ export async function fetchServerResponse( return doMpaNavigation(res.url) } - return [response.f, canonicalUrl, interception] + return { + f: response.f, + c: canonicalUrl, + i: interception, + } } catch (err) { console.error( `Failed to fetch RSC payload for ${url}. Falling back to browser navigation.`, @@ -200,6 +202,10 @@ export async function fetchServerResponse( // If fetch fails handle it like a mpa navigation // TODO-APP: Add a test for the case where a CORS request fails, e.g. external url redirect coming from the response. // See https://github.com/vercel/next.js/issues/43605#issuecomment-1451617521 for a reproduction. - return [url.toString(), undefined, false] + return { + f: url.toString(), + c: undefined, + i: false, + } } } diff --git a/packages/next/src/client/components/router-reducer/ppr-navigations.ts b/packages/next/src/client/components/router-reducer/ppr-navigations.ts index 0dc4dd65325f9..9c43ec66895d5 100644 --- a/packages/next/src/client/components/router-reducer/ppr-navigations.ts +++ b/packages/next/src/client/components/router-reducer/ppr-navigations.ts @@ -3,6 +3,7 @@ import type { FlightRouterState, FlightSegmentPath, Segment, + FetchServerResponseResult, } from '../../../server/app-render/types' import type { CacheNode, @@ -15,7 +16,6 @@ import { } from '../../../shared/lib/segment' import { matchSegment } from '../match-segments' import { createRouterCacheKey } from './create-router-cache-key' -import type { FetchServerResponseResult } from './fetch-server-response' // This is yet another tree type that is used to track pending promises that // need to be fulfilled once the dynamic data is received. The terminal nodes of @@ -353,8 +353,7 @@ export function listenForDynamicRequest( responsePromise: Promise ) { responsePromise.then( - (response: FetchServerResponseResult) => { - const flightData = response[0] + ({ f: flightData }: FetchServerResponseResult) => { for (const flightDataPath of flightData) { const segmentPath = flightDataPath.slice(0, -3) const serverRouterState = flightDataPath[flightDataPath.length - 3] diff --git a/packages/next/src/client/components/router-reducer/prefetch-cache-utils.ts b/packages/next/src/client/components/router-reducer/prefetch-cache-utils.ts index db128d709b2e5..9d6653d7cfbfa 100644 --- a/packages/next/src/client/components/router-reducer/prefetch-cache-utils.ts +++ b/packages/next/src/client/components/router-reducer/prefetch-cache-utils.ts @@ -1,8 +1,5 @@ import { createHrefFromUrl } from './create-href-from-url' -import { - fetchServerResponse, - type FetchServerResponseResult, -} from './fetch-server-response' +import { fetchServerResponse } from './fetch-server-response' import { PrefetchCacheEntryStatus, type PrefetchCacheEntry, @@ -10,6 +7,7 @@ import { type ReadonlyReducerState, } from './router-reducer-types' import { prefetchQueue } from './reducers/prefetch-reducer' +import type { FetchServerResponseResult } from '../../../server/app-render/types' /** * Creates a cache key for the router prefetch cache @@ -152,9 +150,8 @@ export function createPrefetchCacheEntryForInitialLoad({ kind: PrefetchKind data: FetchServerResponseResult }) { - const [, , intercept] = data // if the prefetch corresponds with an interception route, we use the nextUrl to prefix the cache key - const prefetchCacheKey = intercept + const prefetchCacheKey = data.i ? createPrefetchCacheKey(url, nextUrl) : createPrefetchCacheKey(url) @@ -204,8 +201,7 @@ function createLazyPrefetchEntry({ // TODO: `fetchServerResponse` should be more tighly coupled to these prefetch cache operations // to avoid drift between this cache key prefixing logic // (which is currently directly influenced by the server response) - const [, , intercepted] = prefetchResponse - if (intercepted) { + if (prefetchResponse.i) { prefixExistingPrefetchCacheEntry({ url, nextUrl, prefetchCache }) } diff --git a/packages/next/src/client/components/router-reducer/reducers/hmr-refresh-reducer.ts b/packages/next/src/client/components/router-reducer/reducers/hmr-refresh-reducer.ts index 66095a8d327f4..5fd1d0bbd7df4 100644 --- a/packages/next/src/client/components/router-reducer/reducers/hmr-refresh-reducer.ts +++ b/packages/next/src/client/components/router-reducer/reducers/hmr-refresh-reducer.ts @@ -42,7 +42,7 @@ function hmrRefreshReducerImpl( }) return cache.lazyData.then( - ([flightData, canonicalUrlOverride]) => { + ({ f: flightData, c: canonicalUrlOverride }) => { // Handle case when navigating to page in `pages` from `app` if (typeof flightData === 'string') { return handleExternalUrl( diff --git a/packages/next/src/client/components/router-reducer/reducers/navigate-reducer.ts b/packages/next/src/client/components/router-reducer/reducers/navigate-reducer.ts index 507b68db322b5..c27b2ba3c2d84 100644 --- a/packages/next/src/client/components/router-reducer/reducers/navigate-reducer.ts +++ b/packages/next/src/client/components/router-reducer/reducers/navigate-reducer.ts @@ -136,7 +136,7 @@ function navigateReducer_noPPR( prefetchQueue.bump(data) return data.then( - ([flightData, canonicalUrlOverride]) => { + ({ f: flightData, c: canonicalUrlOverride }) => { let isFirstRead = false // we only want to mark this once if (!prefetchValues.lastUsedTime) { @@ -324,7 +324,7 @@ function navigateReducer_PPR( prefetchQueue.bump(data) return data.then( - ([flightData, canonicalUrlOverride]) => { + ({ f: flightData, c: canonicalUrlOverride }) => { let isFirstRead = false // we only want to mark this once if (!prefetchValues.lastUsedTime) { diff --git a/packages/next/src/client/components/router-reducer/reducers/refresh-reducer.ts b/packages/next/src/client/components/router-reducer/reducers/refresh-reducer.ts index d156be25a878b..673a31406d5e4 100644 --- a/packages/next/src/client/components/router-reducer/reducers/refresh-reducer.ts +++ b/packages/next/src/client/components/router-reducer/reducers/refresh-reducer.ts @@ -49,7 +49,7 @@ export function refreshReducer( }) return cache.lazyData.then( - async ([flightData, canonicalUrlOverride]) => { + async ({ f: flightData, c: canonicalUrlOverride }) => { // Handle case when navigating to page in `pages` from `app` if (typeof flightData === 'string') { return handleExternalUrl( diff --git a/packages/next/src/client/components/router-reducer/reducers/server-patch-reducer.ts b/packages/next/src/client/components/router-reducer/reducers/server-patch-reducer.ts index 5fe183d7eaea7..903f748c04e03 100644 --- a/packages/next/src/client/components/router-reducer/reducers/server-patch-reducer.ts +++ b/packages/next/src/client/components/router-reducer/reducers/server-patch-reducer.ts @@ -18,8 +18,9 @@ export function serverPatchReducer( state: ReadonlyReducerState, action: ServerPatchAction ): ReducerState { - const { serverResponse } = action - const [flightData, overrideCanonicalUrl] = serverResponse + const { + serverResponse: { f: flightData, c: canonicalUrlOverride }, + } = action const mutable: Mutable = {} @@ -64,8 +65,8 @@ export function serverPatchReducer( ) } - const canonicalUrlOverrideHref = overrideCanonicalUrl - ? createHrefFromUrl(overrideCanonicalUrl) + const canonicalUrlOverrideHref = canonicalUrlOverride + ? createHrefFromUrl(canonicalUrlOverride) : undefined if (canonicalUrlOverrideHref) { diff --git a/packages/next/src/client/components/router-reducer/refetch-inactive-parallel-segments.ts b/packages/next/src/client/components/router-reducer/refetch-inactive-parallel-segments.ts index 70190c1c8a8fa..bc5f2055380a5 100644 --- a/packages/next/src/client/components/router-reducer/refetch-inactive-parallel-segments.ts +++ b/packages/next/src/client/components/router-reducer/refetch-inactive-parallel-segments.ts @@ -71,8 +71,7 @@ async function refreshInactiveParallelSegmentsImpl({ nextUrl: includeNextUrl ? state.nextUrl : null, buildId: state.buildId, } - ).then((fetchResponse) => { - const flightData = fetchResponse[0] + ).then(({ f: flightData }) => { if (typeof flightData !== 'string') { for (const flightDataPath of flightData) { // we only pass the new cache as this function is called after clearing the router cache diff --git a/packages/next/src/client/components/router-reducer/router-reducer-types.ts b/packages/next/src/client/components/router-reducer/router-reducer-types.ts index 13722a9dcd046..dd9a04624f1a3 100644 --- a/packages/next/src/client/components/router-reducer/router-reducer-types.ts +++ b/packages/next/src/client/components/router-reducer/router-reducer-types.ts @@ -2,8 +2,8 @@ import type { CacheNode } from '../../../shared/lib/app-router-context.shared-ru import type { FlightRouterState, FlightSegmentPath, + FetchServerResponseResult, } from '../../../server/app-render/types' -import type { FetchServerResponseResult } from './fetch-server-response' export const ACTION_REFRESH = 'refresh' export const ACTION_NAVIGATE = 'navigate' diff --git a/packages/next/src/server/app-render/types.ts b/packages/next/src/server/app-render/types.ts index 362e2bf663115..4b71c881acd99 100644 --- a/packages/next/src/server/app-render/types.ts +++ b/packages/next/src/server/app-render/types.ts @@ -229,6 +229,15 @@ export type ActionFlightResponse = { f: FlightData | null } +export type FetchServerResponseResult = { + /** flightData */ + f: FlightData + /** canonicalUrl */ + c: URL | undefined + /** couldBeIntercepted */ + i: boolean +} + export type RSCPayload = | InitialRSCPayload | NavigationFlightResponse diff --git a/packages/next/src/shared/lib/app-router-context.shared-runtime.ts b/packages/next/src/shared/lib/app-router-context.shared-runtime.ts index 6b884348e208b..b6de5bd741481 100644 --- a/packages/next/src/shared/lib/app-router-context.shared-runtime.ts +++ b/packages/next/src/shared/lib/app-router-context.shared-runtime.ts @@ -5,8 +5,10 @@ import type { PrefetchKind, RouterChangeByServerResponse, } from '../../client/components/router-reducer/router-reducer-types' -import type { FetchServerResponseResult } from '../../client/components/router-reducer/fetch-server-response' -import type { FlightRouterState } from '../../server/app-render/types' +import type { + FlightRouterState, + FetchServerResponseResult, +} from '../../server/app-render/types' import React from 'react' export type ChildSegmentMap = Map