/** * Format registry — single source of truth for `name ↔ MIME ↔ renderer` mappings. * * Plan reference: server-provided-what-a-reactive-globe.md → Track A. * * Today's runtime has three separate format systems: * - CLI rendering in @portel/cli's cli-formatter.js * - Beam HTML rendering in src/auto-ui/bridge/renderers.ts (FORMAT_CATALOG) * - MCP `_metaFormatted` content shaping * * This module unifies the HTTP target. Other targets adopt incrementally — * the registry shape is target-pluggable via `FormatSpec.render`. * * v1.29 Track A scope: HTTP target renderers + content negotiation hooks. * Subsequent tracks consume the registry without redefining `FormatSpec`. */ export type RenderTarget = 'cli' | 'http' | 'beam' | 'mcp'; export interface RenderResult { /** Bytes written to the response. UTF-8 strings work; Buffer for binary. */ body: string | Uint8Array; /** MIME type sent on Content-Type. Charset suffix (`; charset=utf-8`) included. */ mime: string; } /** * A format spec. Each format declares its primary target, an optional canonical * MIME (used for Accept-header lookup), and per-target render functions. * * If a target has no renderer registered, the registry falls back to JSON. */ export interface FormatSpec { name: string; primaryTarget: RenderTarget; /** Canonical MIME for HTTP content negotiation. Lookup key in `lookupByMime`. */ httpMime?: string; /** Tie-breaker when multiple formats share a MIME (e.g. text/html). */ isCanonicalForMime?: boolean; /** Per-target renderers. Missing entries fall back to JSON. */ render: Partial RenderResult>>; /** Explicit fallback contract — JSON is always available. */ fallback: 'json'; } export declare class FormatRegistry { private byName; private byMime; register(spec: FormatSpec): void; get(name: string): FormatSpec | undefined; /** Return the canonical FormatSpec for an exact MIME, or undefined. */ lookupByMime(mime: string): FormatSpec | undefined; list(): FormatSpec[]; } /** * Parse an Accept header into an ordered list of (mime, q) preferences. * Highest q first; ties keep input order. */ export declare function parseAccept(header: string | null | undefined): Array<{ mime: string; q: number; }>; export interface NegotiateOptions { /** Accept header from request. */ accept: string | null | undefined; /** Declared @format on the handler (e.g. 'table', 'json'). May be undefined. */ declaredFormat?: string; /** The handler's return value. */ value: unknown; /** Registry to consult. */ registry: FormatRegistry; } /** * Negotiate a representation for `value` given the request's Accept header * and the handler's declared `@format`. Always succeeds — falls back to JSON * when no declared format / requested MIME / renderer combination produces output. * * Algorithm: * 1. Parse Accept into ordered (mime, q) preferences. * 2. For each preference, try (in order): the declared format's MIME match, * then the registry's MIME lookup. The first that produces a non-error * RenderResult wins. * 3. If nothing matched, render JSON. */ export declare function negotiateAccept(opts: NegotiateOptions): RenderResult; export declare function renderJsonFallback(value: unknown): RenderResult; //# sourceMappingURL=registry.d.ts.map