@@ -42,6 +42,7 @@ import type {
42
42
NormalizedSearch ,
43
43
RouteCacheKey ,
44
44
} from './cache-key'
45
+ import { getRenderedSearch } from './cache-key'
45
46
import { createTupleMap , type TupleMap , type Prefix } from './tuple-map'
46
47
import { createLRU } from './lru'
47
48
import {
@@ -130,6 +131,7 @@ type PendingRouteCacheEntry = RouteCacheEntryShared & {
130
131
status : EntryStatus . Empty | EntryStatus . Pending
131
132
blockedTasks : Set < PrefetchTask > | null
132
133
canonicalUrl : null
134
+ renderedSearch : null
133
135
tree : null
134
136
head : HeadData | null
135
137
isHeadPartial : true
@@ -140,6 +142,7 @@ type RejectedRouteCacheEntry = RouteCacheEntryShared & {
140
142
status : EntryStatus . Rejected
141
143
blockedTasks : Set < PrefetchTask > | null
142
144
canonicalUrl : null
145
+ renderedSearch : null
143
146
tree : null
144
147
head : null
145
148
isHeadPartial : true
@@ -150,6 +153,7 @@ export type FulfilledRouteCacheEntry = RouteCacheEntryShared & {
150
153
status : EntryStatus . Fulfilled
151
154
blockedTasks : null
152
155
canonicalUrl : string
156
+ renderedSearch : NormalizedSearch
153
157
tree : RouteTree
154
158
head : HeadData
155
159
isHeadPartial : boolean
@@ -413,29 +417,32 @@ export function getSegmentKeypathForTask(
413
417
// cache entry is valid for all possible search param values.
414
418
const isDynamicTask = task . includeDynamicData || ! route . isPPREnabled
415
419
return isDynamicTask && path . endsWith ( '/' + PAGE_SEGMENT_KEY )
416
- ? [ path , task . key . search ]
420
+ ? [ path , route . renderedSearch ]
417
421
: [ path ]
418
422
}
419
423
420
424
export function readSegmentCacheEntry (
421
425
now : number ,
422
- routeCacheKey : RouteCacheKey ,
426
+ route : FulfilledRouteCacheEntry ,
423
427
path : string
424
428
) : SegmentCacheEntry | null {
425
429
if ( ! path . endsWith ( '/' + PAGE_SEGMENT_KEY ) ) {
426
430
// Fast path. Search params only exist on page segments.
427
431
return readExactSegmentCacheEntry ( now , [ path ] )
428
432
}
429
433
430
- // Page segments may or may not contain search params. If they were prefetched
431
- // using a dynamic request, then we will have an entry with search params.
432
- // Check for that case first.
433
- const entryWithSearchParams = readExactSegmentCacheEntry ( now , [
434
- path ,
435
- routeCacheKey . search ,
436
- ] )
437
- if ( entryWithSearchParams !== null ) {
438
- return entryWithSearchParams
434
+ const renderedSearch = route . renderedSearch
435
+ if ( renderedSearch !== null ) {
436
+ // Page segments may or may not contain search params. If they were prefetched
437
+ // using a dynamic request, then we will have an entry with search params.
438
+ // Check for that case first.
439
+ const entryWithSearchParams = readExactSegmentCacheEntry ( now , [
440
+ path ,
441
+ renderedSearch ,
442
+ ] )
443
+ if ( entryWithSearchParams !== null ) {
444
+ return entryWithSearchParams
445
+ }
439
446
}
440
447
441
448
// If we did not find an entry with the given search params, check for a
@@ -550,6 +557,7 @@ export function readOrCreateRouteCacheEntry(
550
557
couldBeIntercepted : true ,
551
558
// Similarly, we don't yet know if the route supports PPR.
552
559
isPPREnabled : false ,
560
+ renderedSearch : null ,
553
561
554
562
// LRU-related fields
555
563
keypath : null ,
@@ -783,6 +791,7 @@ function fulfillRouteCacheEntry(
783
791
staleAt : number ,
784
792
couldBeIntercepted : boolean ,
785
793
canonicalUrl : string ,
794
+ renderedSearch : NormalizedSearch ,
786
795
isPPREnabled : boolean
787
796
) : FulfilledRouteCacheEntry {
788
797
const fulfilledEntry : FulfilledRouteCacheEntry = entry as any
@@ -793,6 +802,7 @@ function fulfillRouteCacheEntry(
793
802
fulfilledEntry . staleAt = staleAt
794
803
fulfilledEntry . couldBeIntercepted = couldBeIntercepted
795
804
fulfilledEntry . canonicalUrl = canonicalUrl
805
+ fulfilledEntry . renderedSearch = renderedSearch
796
806
fulfilledEntry . isPPREnabled = isPPREnabled
797
807
pingBlockedTasks ( entry )
798
808
return fulfilledEntry
@@ -1133,6 +1143,11 @@ export async function fetchRouteOnCacheMiss(
1133
1143
return null
1134
1144
}
1135
1145
1146
+ // Get the search params that were used to render the target page. This may
1147
+ // be different from the search params in the request URL, if the page
1148
+ // was rewritten.
1149
+ const renderedSearch = getRenderedSearch ( response )
1150
+
1136
1151
const staleTimeMs = serverData . staleTime * 1000
1137
1152
fulfillRouteCacheEntry (
1138
1153
entry ,
@@ -1142,6 +1157,7 @@ export async function fetchRouteOnCacheMiss(
1142
1157
Date . now ( ) + staleTimeMs ,
1143
1158
couldBeIntercepted ,
1144
1159
canonicalUrl ,
1160
+ renderedSearch ,
1145
1161
routeIsPPREnabled
1146
1162
)
1147
1163
} else {
@@ -1366,6 +1382,19 @@ export async function fetchSegmentPrefetchesUsingDynamicRequest(
1366
1382
return null
1367
1383
}
1368
1384
1385
+ const renderedSearch = getRenderedSearch ( response )
1386
+ if ( renderedSearch !== route . renderedSearch ) {
1387
+ // The search params that were used to render the target page are
1388
+ // different from the search params in the request URL. This only happens
1389
+ // when there's a dynamic rewrite in between the tree prefetch and the
1390
+ // data prefetch.
1391
+ // TODO: For now, since this is an edge case, we reject the prefetch, but
1392
+ // the proper way to handle this is to evict the stale route tree entry
1393
+ // then fill the cache with the new response.
1394
+ rejectSegmentEntriesIfStillPending ( spawnedEntries , Date . now ( ) + 10 * 1000 )
1395
+ return null
1396
+ }
1397
+
1369
1398
// Track when the network connection closes.
1370
1399
const closed = createPromiseWithResolvers < void > ( )
1371
1400
@@ -1462,6 +1491,11 @@ function writeDynamicTreeResponseIntoCache(
1462
1491
const isResponsePartial =
1463
1492
response . headers . get ( NEXT_DID_POSTPONE_HEADER ) === '1'
1464
1493
1494
+ // Get the search params that were used to render the target page. This may
1495
+ // be different from the search params in the request URL, if the page
1496
+ // was rewritten.
1497
+ const renderedSearch = getRenderedSearch ( response )
1498
+
1465
1499
const fulfilledEntry = fulfillRouteCacheEntry (
1466
1500
entry ,
1467
1501
convertRootFlightRouterStateToRouteTree ( flightRouterState ) ,
@@ -1470,6 +1504,7 @@ function writeDynamicTreeResponseIntoCache(
1470
1504
now + staleTimeMs ,
1471
1505
couldBeIntercepted ,
1472
1506
canonicalUrl ,
1507
+ renderedSearch ,
1473
1508
routeIsPPREnabled
1474
1509
)
1475
1510
0 commit comments