File size: 9,222 Bytes
fea495a | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 | import type { HeadData, LoadingModuleData } from '../../../shared/lib/app-router-context.shared-runtime';
import type { Segment as FlightRouterStateSegment } from '../../../server/app-render/types';
import { HasLoadingBoundary } from '../../../server/app-render/types';
import { type PrefetchTask, type PrefetchSubtaskResult } from './scheduler';
import type { NormalizedHref, NormalizedNextUrl, NormalizedSearch, RouteCacheKey } from './cache-key';
import { type RouteParam } from '../../route-params';
import { type Prefix } from './tuple-map';
import { type SegmentCacheKey, type SegmentRequestKey } from '../../../shared/lib/segment-cache/segment-value-encoding';
import type { FlightRouterState } from '../../../server/app-render/types';
import { FetchStrategy } from '../segment-cache';
export type RouteTree = {
cacheKey: SegmentCacheKey;
requestKey: SegmentRequestKey;
segment: FlightRouterStateSegment;
param: RouteParam | null;
slots: null | {
[parallelRouteKey: string]: RouteTree;
};
isRootLayout: boolean;
hasLoadingBoundary: HasLoadingBoundary;
};
type RouteCacheEntryShared = {
staleAt: number;
couldBeIntercepted: boolean;
TODO_metadataStatus: EntryStatus.Empty | EntryStatus.Fulfilled;
TODO_isHeadDynamic: boolean;
keypath: null | Prefix<RouteCacheKeypath>;
next: null | RouteCacheEntry;
prev: null | RouteCacheEntry;
size: number;
};
/**
* Tracks the status of a cache entry as it progresses from no data (Empty),
* waiting for server data (Pending), and finished (either Fulfilled or
* Rejected depending on the response from the server.
*/
export declare const enum EntryStatus {
Empty = 0,
Pending = 1,
Fulfilled = 2,
Rejected = 3
}
type PendingRouteCacheEntry = RouteCacheEntryShared & {
status: EntryStatus.Empty | EntryStatus.Pending;
blockedTasks: Set<PrefetchTask> | null;
canonicalUrl: null;
renderedSearch: null;
tree: null;
head: HeadData | null;
isHeadPartial: true;
isPPREnabled: false;
};
type RejectedRouteCacheEntry = RouteCacheEntryShared & {
status: EntryStatus.Rejected;
blockedTasks: Set<PrefetchTask> | null;
canonicalUrl: null;
renderedSearch: null;
tree: null;
head: null;
isHeadPartial: true;
isPPREnabled: boolean;
};
export type FulfilledRouteCacheEntry = RouteCacheEntryShared & {
status: EntryStatus.Fulfilled;
blockedTasks: null;
canonicalUrl: string;
renderedSearch: NormalizedSearch;
tree: RouteTree;
head: HeadData;
isHeadPartial: boolean;
isPPREnabled: boolean;
};
export type RouteCacheEntry = PendingRouteCacheEntry | FulfilledRouteCacheEntry | RejectedRouteCacheEntry;
type SegmentCacheEntryShared = {
staleAt: number;
fetchStrategy: FetchStrategy;
revalidating: SegmentCacheEntry | null;
keypath: null | Prefix<SegmentCacheKeypath>;
next: null | SegmentCacheEntry;
prev: null | SegmentCacheEntry;
size: number;
};
export type EmptySegmentCacheEntry = SegmentCacheEntryShared & {
status: EntryStatus.Empty;
rsc: null;
loading: null;
isPartial: true;
promise: null;
};
export type PendingSegmentCacheEntry = SegmentCacheEntryShared & {
status: EntryStatus.Pending;
rsc: null;
loading: null;
isPartial: true;
promise: null | PromiseWithResolvers<FulfilledSegmentCacheEntry | null>;
};
type RejectedSegmentCacheEntry = SegmentCacheEntryShared & {
status: EntryStatus.Rejected;
rsc: null;
loading: null;
isPartial: true;
promise: null;
};
export type FulfilledSegmentCacheEntry = SegmentCacheEntryShared & {
status: EntryStatus.Fulfilled;
rsc: React.ReactNode | null;
loading: LoadingModuleData | Promise<LoadingModuleData>;
isPartial: boolean;
promise: null;
};
export type SegmentCacheEntry = EmptySegmentCacheEntry | PendingSegmentCacheEntry | RejectedSegmentCacheEntry | FulfilledSegmentCacheEntry;
export type NonEmptySegmentCacheEntry = Exclude<SegmentCacheEntry, EmptySegmentCacheEntry>;
type RouteCacheKeypath = [NormalizedHref, NormalizedNextUrl];
type SegmentCacheKeypath = [string, NormalizedSearch];
export declare function getCurrentCacheVersion(): number;
/**
* Used to clear the client prefetch cache when a server action calls
* revalidatePath or revalidateTag. Eventually we will support only clearing the
* segments that were actually affected, but there's more work to be done on the
* server before the client is able to do this correctly.
*/
export declare function revalidateEntireCache(nextUrl: string | null, tree: FlightRouterState): void;
export declare function pingInvalidationListeners(nextUrl: string | null, tree: FlightRouterState): void;
export declare function readExactRouteCacheEntry(now: number, href: NormalizedHref, nextUrl: NormalizedNextUrl | null): RouteCacheEntry | null;
export declare function readRouteCacheEntry(now: number, key: RouteCacheKey): RouteCacheEntry | null;
export declare function getSegmentKeypathForTask(task: PrefetchTask, route: FulfilledRouteCacheEntry, cacheKey: SegmentCacheKey): Prefix<SegmentCacheKeypath>;
export declare function readSegmentCacheEntry(now: number, route: FulfilledRouteCacheEntry, cacheKey: SegmentCacheKey): SegmentCacheEntry | null;
export declare function waitForSegmentCacheEntry(pendingEntry: PendingSegmentCacheEntry): Promise<FulfilledSegmentCacheEntry | null>;
/**
* Checks if an entry for a route exists in the cache. If so, it returns the
* entry, If not, it adds an empty entry to the cache and returns it.
*/
export declare function readOrCreateRouteCacheEntry(now: number, task: PrefetchTask): RouteCacheEntry;
export declare function requestOptimisticRouteCacheEntry(now: number, requestedUrl: URL, nextUrl: string | null): FulfilledRouteCacheEntry | null;
/**
* Checks if an entry for a segment exists in the cache. If so, it returns the
* entry, If not, it adds an empty entry to the cache and returns it.
*/
export declare function readOrCreateSegmentCacheEntry(now: number, task: PrefetchTask, route: FulfilledRouteCacheEntry, cacheKey: SegmentCacheKey): SegmentCacheEntry;
export declare function readOrCreateRevalidatingSegmentEntry(now: number, prevEntry: SegmentCacheEntry): SegmentCacheEntry;
export declare function upsertSegmentEntry(now: number, keypath: Prefix<SegmentCacheKeypath>, candidateEntry: SegmentCacheEntry): SegmentCacheEntry | null;
export declare function createDetachedSegmentCacheEntry(staleAt: number): EmptySegmentCacheEntry;
export declare function upgradeToPendingSegment(emptyEntry: EmptySegmentCacheEntry, fetchStrategy: FetchStrategy): PendingSegmentCacheEntry;
export declare function resetRevalidatingSegmentEntry(owner: SegmentCacheEntry): EmptySegmentCacheEntry;
export declare function convertRouteTreeToFlightRouterState(routeTree: RouteTree): FlightRouterState;
export declare function fetchRouteOnCacheMiss(entry: PendingRouteCacheEntry, task: PrefetchTask): Promise<PrefetchSubtaskResult<null> | null>;
export declare function fetchSegmentOnCacheMiss(route: FulfilledRouteCacheEntry, segmentCacheEntry: PendingSegmentCacheEntry, routeKey: RouteCacheKey, tree: RouteTree): Promise<PrefetchSubtaskResult<FulfilledSegmentCacheEntry> | null>;
export declare function fetchSegmentPrefetchesUsingDynamicRequest(task: PrefetchTask, route: FulfilledRouteCacheEntry, fetchStrategy: FetchStrategy.LoadingBoundary | FetchStrategy.PPRRuntime | FetchStrategy.Full, dynamicRequestTree: FlightRouterState, spawnedEntries: Map<SegmentCacheKey, PendingSegmentCacheEntry>): Promise<PrefetchSubtaskResult<null> | null>;
/**
* Checks whether the new fetch strategy is likely to provide more content than the old one.
*
* Generally, when an app uses dynamic data, a "more specific" fetch strategy is expected to provide more content:
* - `LoadingBoundary` only provides static layouts
* - `PPR` can provide shells for each segment (even for segments that use dynamic data)
* - `PPRRuntime` can additionally include content that uses searchParams, params, or cookies
* - `Full` includes all the content, even if it uses dynamic data
*
* However, it's possible that a more specific fetch strategy *won't* give us more content if:
* - a segment is fully static
* (then, `PPR`/`PPRRuntime`/`Full` will all yield equivalent results)
* - providing searchParams/params/cookies doesn't reveal any more content, e.g. because of an `await connection()`
* (then, `PPR` and `PPRRuntime` will yield equivalent results, only `Full` will give us more)
* Because of this, when comparing two segments, we should also check if the existing segment is partial.
* If it's not partial, then there's no need to prefetch it again, even using a "more specific" strategy.
* There's currently no way to know if `PPRRuntime` will yield more data that `PPR`, so we have to assume it will.
*
* Also note that, in practice, we don't expect to be comparing `LoadingBoundary` to `PPR`/`PPRRuntime`,
* because a non-PPR-enabled route wouldn't ever use the latter strategies. It might however use `Full`.
*/
export declare function canNewFetchStrategyProvideMoreContent(currentStrategy: FetchStrategy, newStrategy: FetchStrategy): boolean;
export {};
|