UNPKG

next

Version:

The React Framework

231 lines (230 loc) • 11.3 kB
/** * The transport format for RSC responses. * * Every type in this module is prefixed with `Transport` to distinguish the * wire format from client-local types (`Segment`, `RouteTree`, `CacheNode`, * `FlightRouterState`). Client code converts out of Transport types at the * decode boundary and never stores them. * * A response carries a single tree, anchored at the root segment, where each * node has both its identity and (optionally) its render output. Partial * responses are expressed per node: the convention across the format is that * absence means "nothing here" — an omitted `d` means the segment was not * rendered as part of the response, an omitted slot key means the response * carries no information about that slot. */ import type React from 'react'; import type { DynamicParamTypesShort, FlightRouterState, Segment } from './app-router-types'; import type { VaryParamsIterable } from './segment-cache/vary-params-decoding'; /** * Segment identity on the wire. A string for static segments; an object for * dynamic (parameterized) segments. * * Page segments are always `__PAGE__`. The search params are sent separately, * in the response's `q` field. */ export type TransportSegment = string | TransportDynamicSegment; export type TransportDynamicSegment = { /** name — the param name, e.g. 'id' for [id] */ n: string; /** type — dynamic param type */ t: DynamicParamTypesShort; /** * key — the param value / cache key. null when the client is expected to * parse the value from the URL itself, which keeps the response cacheable * across param values. */ k: string | null; /** siblings — static sibling segments at this URL level; null = unknown */ s: readonly string[] | null; }; /** * Render output for a single segment. Grouped into one object so a node's * "rendered vs skipped" state is a single presence check. Also used for the * head (metadata/viewport), which is output without structure. * * `r` may be null: the segment was skipped — the response acknowledges its * position without rendering anything for it, e.g. a shared layout the * client is expected to already have. This is distinct from `d` being absent * on the node, which means the response makes no claim about the segment's * output at all (the client should fetch it lazily if it doesn't have it). */ export type TransportSegmentData = { /** rsc — the React node for this segment; null = skipped (not rendered) */ r: React.ReactNode; /** * isPartial — whether anything in the segment's output is not fully * resolved: dynamic holes, runtime holes, anything suspended. * * Navigation responses carry a plain boolean, known at render time. It is * a render-wide constant (isPossiblyPartialResponse in * create-component-tree.tsx), identical on every node and only * conservatively accurate, so the client resolves a boolean-form node's * actual partiality from the response-level signal instead (see * decodeTransportNode). * * Per-segment prefetch responses carry the rewindable promise encoding * instead: fulfilled = complete, pending forever = partial. Partiality is * only discovered there by probing the buffered prerender (a partial * segment's probe never resolves, the same way Flight encodes the holes * themselves), and the encoding must survive the client's shell * double-decode — the response buffer is decoded a second time truncated * at the shell byte boundary (`a`), and a fulfillment row that landed past * the boundary must read as "partial" in that decode. The promise form is * only ever read off a fully-buffered decode's thenable status. */ p: boolean | Promise<void>; /** * varyParams — an iterable of the route params this segment's output * depends on (one name per yield, deduped). Used by the client router to * determine cache key specificity: segments that only access certain * params can be reused across navigations where unaccessed params change. * * Does NOT include root params; those are emitted once at the response * level (`r` on the response wrapper) and unioned in by the consumer. * * - null: tracking was not enabled for this render (e.g., not a * prerender). Treat conservatively — assume all params vary. * - Drains to empty: segment accesses no params (e.g., client components, * or server components that don't read params). Can be shared across * all param values. * - Drains to non-empty: segment depends on those params. Can only be * reused when those specific params match. */ v: VaryParamsIterable | null; /** * staleTime in seconds — present only in per-segment prefetch responses, * where staleness is tracked per node. Navigation responses carry * staleness at the response level instead (the wrapper's `s` iterable or * the Next-Router-Stale-Time header). * * An async iterable rather than a plain number because the final value is * only known late in the stream, and the iterable form survives a * truncated/rewound shell decode (read via thenable status from the * buffered response). The client takes the last yielded value. */ s?: AsyncIterable<number>; }; /** * Structure of a segment node, independent of whether render output * is attached. Shared by both response variants. */ type TransportNodeShape = { /** segment — identity */ s: TransportSegment; /** hints — PrefetchHint bitmask; omitted when zero or unknown */ h?: number; }; /** * A node in a FULL response tree: every node carries data, and slot maps are * exhaustive. Produced by full renders: the initial document payload and * error payloads. "Full" means the response accounts for every node — there * is nothing for the client to fetch lazily. Data-level holes * (`d.p === true`) and skipped nodes (`d.r === null`) are still possible. */ export type FullTransportNode = TransportNodeShape & { /** data — always present in a full tree */ d: TransportSegmentData; /** * children — parallel routes; omitted for leaf segments. A Map rather * than a plain object because slot names are app-defined; with a plain * object, every distinct combination of slot names creates a different * hidden class, making keyed access megamorphic. */ c?: Map<string, FullTransportNode>; }; /** * A node in a PARTIAL response tree: produced by navigations, refreshes, and * actions — anywhere the server diffs against client state or renders a * subset of the route. * * The client treats the response as an overlay over its current tree: * * - A node present in the tree is authoritative for that position's identity. * - `d` present: the response accounts for this segment. Its output is `d.r`, * which may be null when the segment was skipped — not rendered because * the client already has it (e.g. a shared layout). * - `d` omitted: the response makes no claim about this segment's output. * The client keeps what it has, or lazily fetches when it renders (e.g. * segments beneath a loading boundary in a non-PPR prefetch). * - A slot key omitted from `c` (or `c` omitted): on a skipped node * (`d.r === null`), the response carries no information about that slot * and the client keeps its subtree untouched. On any other node the * subtree is authoritative, so an omitted slot is simply absent. * - `h` omitted: on a skipped node, the client keeps its existing hints for * the segment; on any other node it means the hints are zero. * * The client's cache write descends through nodes with `d` omitted (see * writeTreeDataIntoCache), so data may appear at any depth below a no-claim * node — the identity spine of a per-segment prefetch response is the * canonical producer of that shape. */ export type PartialTransportNode = TransportNodeShape & { /** data — omitted when this segment was not rendered in this response */ d?: TransportSegmentData; /** children — omitted field or key = no information about those slots */ c?: Map<string, PartialTransportNode>; }; /** * Shared type for code that walks either variant. Note: FullTransportNode is * structurally assignable to PartialTransportNode, so this union collapses * to PartialTransportNode for the type checker; the union spelling * documents intent. */ export type TransportNode = FullTransportNode | PartialTransportNode; /** * The tree + head bundle of a response. */ export type FullTransportData = { /** tree */ t: FullTransportNode; /** head — always present and complete in a full response */ h: TransportSegmentData; }; export type PartialTransportData = { t: PartialTransportNode; /** omitted = this response carries no head (e.g. router-state-only responses) */ h?: TransportSegmentData; }; export type TransportData = FullTransportData | PartialTransportData; /** * Creates the data for a skipped segment: a null `r` marks the position as * acknowledged by the response, with no output attached (see the note on * TransportSegmentData). Used by producers for the segments along the path * from the root down to the rendered subtrees. */ export declare function createSkippedSegmentData(): TransportSegmentData; /** * Reads a late-resolving value from a fully-buffered Flight decode via the * thenable's status. Flight sets `status`/`value` on a row's promise once * its bytes are processed, and a buffered decode processes every byte * synchronously, so any row that made it into the payload is readable * without awaiting. Returns `unresolvedValue` for a row that is pending or * absent in this decode — e.g. one whose fulfillment landed past a * truncated shell decode's boundary; that's what scopes a response's * late-resolving signals to the payload being decoded. Returns * `rejectedValue` (defaults to `unresolvedValue`) for a rejected row — an * aborted render errors rows that were still pending when it happened. * * Shared by client and server: the client reads buffered prefetch * responses; the server (collect-segment-data) reads the buffered page * payload it re-serializes into per-segment responses. */ export declare function readFulfilledValue<T, TFallback>(valueFromServer: PromiseLike<T>, unresolvedValue: TFallback, rejectedValue?: T | TFallback): T | TFallback; /** * Converts a segment's client/server-internal representation to its wire * representation. */ export declare function segmentToTransportSegment(segment: Segment): TransportSegment; /** * Converts a segment's wire representation to the client/server-internal * `Segment` type. */ export declare function transportSegmentToSegment(transportSegment: TransportSegment): Segment; /** * Derives a FlightRouterState from a transport tree. Used where the client * needs a router-state representation of a full response (e.g. the initial * hydration payload). Render output (`d`) is not carried over. */ export declare function transportNodeToFlightRouterState(node: TransportNode, renderedSearch: string): FlightRouterState; export {};