UNPKG

next

Version:

The React Framework

120 lines (119 loc) 5.85 kB
import type { InitialRSCPayload, Segment } from '../../../shared/lib/app-router-types'; import { RenderStage } from '../staged-rendering'; import type { ValidationBoundaryTracking } from './boundary-tracking'; import { type LoaderTree } from '../../lib/app-dir-module'; import type { GetDynamicParamFromSegment } from '../app-render'; import type { Instant } from '../../../build/segment-config/app/app-segment-config'; import { Readable } from 'node:stream'; import type { NextParsedUrlQuery } from '../../request-meta'; type ClientReferenceManifest = Record<string, any>; /** Used to identify a segment. Conceptually similar to request keys in the Client Segment Cache. */ export type SegmentPath = string & { _tag: 'SegmentPath'; }; /** * Isomorphic to a FlightRouterState, but with extra data attached. * Carries the segment path for each segment so we can easily get it from the cache. * */ export type RouteTree = { path: SegmentPath; segment: Segment; module: null | { type: 'layout' | 'page'; instantConfig: Instant | null; conventionPath: string; createInstantStack: (() => Error) | null; }; slots: { [parallelRouteKey: string]: RouteTree; } | null; }; export type SegmentStage = RenderStage.Static | RenderStage.Runtime | RenderStage.Dynamic; export type StageChunks = Record<SegmentStage, Uint8Array[]>; export type StageEndTimes = { [RenderStage.Static]: number; [RenderStage.Runtime]: number; }; /** * Splits an existing staged stream (represented as arrays of chunks) * into separate staged streams (also in arrays-of-chunks form), one for each segment. * */ export declare function collectStagedSegmentData(fullPageChunks: StageChunks, fullPageDebugChunks: Uint8Array[] | null, startTime: number, hasRuntimePrefetch: boolean, clientReferenceManifest: ClientReferenceManifest): Promise<{ cache: SegmentCache; payload: InitialRSCPayload; stageEndTimes: StageEndTimes; }>; /** * Creates a late-release stream for a given payload. * When `renderSignal` is triggered, the stream will release late chunks * to provide extra debug info. * */ export declare function createCombinedPayloadStream(payload: InitialRSCPayload, extraChunksAbortController: AbortController, renderSignal: AbortSignal, clientReferenceManifest: ClientReferenceManifest, startTime: number, isDebugChannelEnabled: boolean): Promise<{ stream: Readable; debugStream: Readable | null; }>; export type SegmentCache = { head: SegmentCacheItem | null; segments: Map<SegmentPath, SegmentCacheItem>; }; type SegmentCacheItem = { chunks: StageChunks; debugChunks: Uint8Array[] | null; }; /** * Walks the LoaderTree to discover validation depth bounds. * * Each route group between URL segments represents a potential * shared/new boundary in a client navigation. When a user navigates * between sibling routes that share a route group layout, that * layout is already mounted — its Suspense boundaries are revealed * and don't cover new content below. By tracking the max group * depth at each URL depth, we can iterate all possible group * boundaries and validate that blocking code is always covered by * Suspense in the new tree. This is conservative: some boundaries * may not correspond to real navigations (e.g. a route group with * no siblings), but it ensures we don't miss real violations. * * The max is taken across all parallel slots. When slots have * different numbers of groups, the deepest slot determines the * iteration range. Shallower slots simply stay entirely shared * at group depths beyond their own group count — they run out * of groups before reaching the boundary, so their content * remains in the Dynamic stage. * * Returns an array where: * - length = max URL depth (number of URL-consuming segments) * - array[i] = max group depth at URL depth i (number of route group * segments between this URL depth and the next) * * For example, a tree like: * '' / (outer) / (inner) / dashboard / page * returns [2, 0] — URL depth 0 (root) has 2 group layers before * the next URL segment (dashboard), and URL depth 1 (dashboard) has * 0 group layers before the leaf. */ export declare function discoverValidationDepths(loaderTree: LoaderTree): number[]; /** * Builds a combined RSC payload for validation at a given URL depth. * * Walks the LoaderTree directly, loading modules and counting * URL-contributing layouts. When `depth` URL segments have been * consumed, the boundary flips from shared (dynamic stage) to new * (static/runtime stage). As the new subtree is built, we check for * instant configs. If none are found, returns null — no validation * needed at this depth or deeper. * * This combines module loading, tree walking, config discovery, and * payload construction into a single pass. */ export type ValidationPayloadResult = { payload: InitialRSCPayload; /** Whether errors from this payload could be ambiguous between runtime * API access (cookies, headers) and uncached IO (connection, fetch). * True when some segments used Static stage. False when all segments * used Runtime stage and errors are definitively from uncached IO. */ hasAmbiguousErrors: boolean; createInstantStack: (() => Error) | null; }; export declare function createCombinedPayloadAtDepth(initialRSCPayload: InitialRSCPayload, cache: SegmentCache, initialLoaderTree: LoaderTree, getDynamicParamFromSegment: GetDynamicParamFromSegment, query: NextParsedUrlQuery | null, depth: number, groupDepth: number, releaseSignal: AbortSignal, boundaryState: ValidationBoundaryTracking, clientReferenceManifest: ClientReferenceManifest, stageEndTimes: StageEndTimes, useRuntimeStageForPartialSegments: boolean): Promise<ValidationPayloadResult | null>; export {};