/** * @lazarv/rsc - Bundler-agnostic RSC types * * Type definitions for React Server Components serialization/deserialization */ /** * A thenable with status/value properties for synchronous inspection. * Compatible with React's use() protocol. */ export interface Thenable extends Promise { status: "pending" | "fulfilled" | "rejected"; value: T | unknown | undefined; } /** * Client reference metadata for serialization */ export interface ClientReferenceMetadata { /** Module ID/path */ id: string; /** Named export or default */ name: string; /** Chunks required to load this module (optional) */ chunks?: string[]; } /** * Server reference metadata for serialization */ export interface ServerReferenceMetadata { /** Action/function ID */ id: string; /** Whether this is bound */ bound?: boolean; } /** * Module resolver function type * Called during serialization to resolve client/server references to metadata */ export type ModuleResolver = { /** * Resolve a client reference to its metadata * @param reference The client component reference (function or object with $$typeof) * @returns The metadata to serialize, or null if not a client reference */ resolveClientReference?: ( reference: unknown ) => ClientReferenceMetadata | null; /** * Resolve a server reference to its metadata * @param reference The server action reference * @returns The metadata to serialize, or null if not a server reference */ resolveServerReference?: ( reference: unknown ) => ServerReferenceMetadata | null; }; /** * Module loader function type * Called during deserialization to load client modules */ export type ModuleLoader = { /** * Preload a module's chunks (optional, for optimization) * @param metadata The client reference metadata * @returns A promise that resolves when preloading is complete */ preloadModule?: (metadata: ClientReferenceMetadata) => Promise | void; /** * Load/require a module * @param metadata The client reference metadata * @returns The module exports */ requireModule: (metadata: ClientReferenceMetadata) => unknown; /** * Load a server action by ID * @param id The server action ID * @returns The server action function */ loadServerAction?: (id: string) => Promise | Function; }; /** * Options for renderToReadableStream */ export interface RenderToReadableStreamOptions { /** * Module resolver for client/server references */ moduleResolver?: ModuleResolver; /** * Called when an error occurs during rendering */ onError?: (error: unknown) => string | void; /** * Prefix for generated IDs */ identifierPrefix?: string; /** * Temporary references for streaming */ temporaryReferences?: Map; /** * Environment name for debugging */ environmentName?: string; /** * Filter stack frames in error stacks */ filterStackFrame?: (sourceURL: string, functionName: string) => boolean; /** * Signal to abort the render */ signal?: AbortSignal; } /** * Options for createFromReadableStream */ export interface CreateFromReadableStreamOptions { /** * Module loader for client modules */ moduleLoader?: ModuleLoader; /** * Temporary references for streaming */ temporaryReferences?: Map; /** * Server action caller */ callServer?: (id: string, args: unknown[]) => Promise; /** * Registry of custom classes for TypedArray/DataView deserialization. * Maps type name (e.g., "CustomDataView") to the constructor class. * Used when deserializing custom TypedArray or DataView subclasses. */ typeRegistry?: Record ArrayBufferView>; } /** * Resource ceilings enforced by the reply decoder. * * Each limit is independent. Omit a field to use the built-in default. * When a request exceeds a limit, the decoder throws a DecodeLimitError * before any server action is invoked. */ export interface DecodeReplyLimits { /** Maximum number of outlined rows per reply. Default: 10000. */ maxRows?: number; /** Maximum recursion depth when materialising a row's value tree. Default: 128. */ maxDepth?: number; /** Maximum total payload size in bytes (sum of FormData entries). Default: 32 MiB. */ maxBytes?: number; /** Maximum bound arguments on a server reference. Default: 256. */ maxBoundArgs?: number; /** Maximum digits in a decoded BigInt literal. Default: 4096. */ maxBigIntDigits?: number; /** Maximum length of a single string row before decoding. Default: 16 MiB. */ maxStringLength?: number; /** Maximum chunks materialised for a decoded stream/iterable. Default: 10000. */ maxStreamChunks?: number; } /** * Result returned by `validateArg`. `success: true` means the value * passed; `data` replaces the original (so coercive validators like * Zod's `.transform()` flow back into the args list). `success: false` * triggers a `DecodeValidationError` whose `original` field is `error`. */ export type ValidateArgResult = | { success: true; data: unknown } | { success: false; error: unknown }; /** * Host-supplied bridge from a Standard Schema (Zod / Valibot / ArkType / …) * to the decoder's library-agnostic dispatch. The decoder does not import * any schema library directly — the host is responsible for duck-typing * the spec and invoking the right `safeParse` / `safeValidate` method. */ export type ValidateArgHook = ( spec: unknown, value: unknown, ctx: { argIndex: number; actionId: string | null; entry?: string } ) => ValidateArgResult; /** * Options for decodeReply */ export interface DecodeReplyOptions { /** * Module loader for server actions */ moduleLoader?: ModuleLoader; /** * Temporary references */ temporaryReferences?: Map; /** * Resource ceilings applied to the decoded payload. * Defaults match the decoder's built-in safe ceilings. */ limits?: DecodeReplyLimits; /** * Hook invoked on the `id` field of a server-reference payload (the * `parsed.id` in `{id, bound}`). When the host implements opaque * AEAD-encrypted action tokens, this hook decrypts the token to * recover the underlying action id and any server-emitted bound * captures. The decoder treats `actionId` as authoritative for * registry lookup, and prepends `bound` to any wire-supplied bound at * bind time. Return `null` to fall through to the legacy behavior * (parsed.id used as-is). */ decryptServerReferenceId?: ( encryptedId: string ) => { actionId: string; bound?: unknown[] } | null; /** * Recovered (decrypted) action id for the current request, set by the * dispatcher *before* calling `decodeReply`. Together with * `resolveServerFunctionMeta` this drives per-slot parse/validate * during the args walk. Leave unset for non-server-function callers * of `decodeReply`. */ actionId?: string; /** * Look up the registered metadata for an action id (typically a thin * wrapper around `lookupServerFunctionMeta`). When this resolves to a * non-null value the decoder switches to the meta-driven slot-walk; * when it resolves to null the decoder falls through to the legacy * whole-tree walk (back-compat for bare `"use server"` actions). */ resolveServerFunctionMeta?: (actionId: string) => unknown; /** * Library-agnostic Standard-Schema bridge. Required when meta declares * `validate.args[i]` against a schema (Zod / Valibot / ArkType / …). * Wire-aware constraints (`_kind: "file" | "blob" | "formdata"`) bypass * this hook and are enforced directly by the decoder. */ validateArg?: ValidateArgHook; } /** * Server-side RSC API */ export interface RSCServerAPI { /** * Render a React element tree to a ReadableStream of RSC protocol */ renderToReadableStream( model: unknown, options?: RenderToReadableStreamOptions ): ReadableStream; /** * Decode a reply (form data or body) from client action */ decodeReply( body: FormData | string, options?: DecodeReplyOptions ): Promise; /** * Decode a form action */ decodeAction( body: FormData, options?: DecodeReplyOptions ): Promise; /** * Decode form state for progressive enhancement */ decodeFormState( actionResult: unknown, body: FormData, options?: DecodeReplyOptions ): Promise; /** * Register a server reference (action) */ registerServerReference( action: Function, id: string, exportName: string ): Function; /** * Register a client reference */ registerClientReference( proxy: unknown, id: string, exportName: string ): unknown; /** * Create a temporary reference set for server-side use. * Returns a WeakMap that maps opaque proxy objects to their path strings. * Pass to both `decodeReply` and `renderToReadableStream` options. */ createTemporaryReferenceSet(): WeakMap; /** * Create a client module proxy for dynamic client reference creation */ createClientModuleProxy(moduleId: string): unknown; /** * Prerender a model to a static prelude */ prerender( model: unknown, options?: RenderToReadableStreamOptions ): Promise<{ prelude: ReadableStream; }>; /** * Decode reply from an async iterable (streaming) */ decodeReplyFromAsyncIterable( iterable: AsyncIterable, options?: DecodeReplyOptions ): Promise; } /** * Options for prerender */ export interface PrerenderOptions extends RenderToReadableStreamOptions {} /** * Result of prerender */ export interface PrerenderResult { prelude: ReadableStream; } /** * Client-side RSC API */ export interface RSCClientAPI { /** * Create a React element tree from a ReadableStream of RSC protocol. * Returns a thenable synchronously. The stream is consumed in the background. * The thenable has .status and .value properties for synchronous inspection * (compatible with React's use() protocol). */ createFromReadableStream( stream: ReadableStream, options?: CreateFromReadableStreamOptions ): Thenable; /** * Create from a fetch response. * Returns a thenable synchronously. */ createFromFetch( promiseForResponse: Promise, options?: CreateFromReadableStreamOptions ): Thenable; /** * Encode arguments for a server action call */ encodeReply( value: unknown, options?: { temporaryReferences?: Map } ): Promise; /** * Create a server reference for calling server actions */ createServerReference( id: string, callServer: (id: string, args: unknown[]) => Promise ): (...args: unknown[]) => Promise; /** * Create a temporary reference set for client-side use. * Returns a Map that stores non-serializable values keyed by their path strings. * Pass to `encodeReply` to populate, then to `createFromReadableStream` to recover values. */ createTemporaryReferenceSet(): Map; }