/** * Tiered codec — the cheapest sufficient wire serialization for each call/stream argument and result. * * @remarks * Three tiers, escalating only as far as a given value actually requires: * * 1. **`raw`** — the value crosses untouched (same reference on a linked in-memory port; structurally * cloned by a real transport). Zero-copy, zero-clone at this layer. Used whenever a value contains * no "exotic" leaf. * 2. **Path-sentineled raw** — a container that has ordinary JSON-safe data EXCEPT for a small number * of exotic leaves (e.g. one callback buried in an options bag) is cloned ONLY along the paths that * lead to those leaves, with each exotic leaf replaced by a `{ __nhtio$: }` * sentinel; the rest of the container (and the caller's original object) is untouched. * 3. **`nhtio`** — a whole exotic value (a bare function/Error/custom-encodable passed directly as an * argument) is encoded in full via `@nhtio/encoder` (or a BYO codec) and shipped as a plain string. * * "Exotic leaf" = a function, an `Error`, or (when the `@nhtio/encoder` peer is installed) a value the * encoder recognizes as a registered custom-encodable. `TypedArray`/`ArrayBuffer`/`DataView`/`Date`/ * `RegExp`/`Map`/`Set` are treated as OPAQUE traversal leaves — the traverser never descends into their * contents (so a huge `Float32Array` costs O(1) traversal step, not O(bytes)) and they ship raw as-is * (a linked in-memory port hands the same reference through; a real transport structurally clones or * transfers them). * * The `@nhtio/encoder` peer is OPTIONAL and NEVER statically imported — every reference to it goes * through {@link loadEncoder}, a lazy + memoized dynamic `import()` with an injectable seam for tests. * * Circular references are fine at the `raw` tier (the traverser's `ctx.circular` flag stops descent * without escalating). A circular reference reachable only through an exotic leaf's container throws * `E_ISOLATION_UNENCODABLE` — the encoder itself cannot represent it either. */ import type { CodecMode } from "./types"; import type { WireError, WireValue } from "./protocol"; /** The slice of `@nhtio/encoder`'s API surface this codec uses. Deliberately non-generic (`unknown` in, * `unknown` out) — this codec always encodes/decodes values whose shape it cannot statically know, so * the real encoder's ``-constrained signature is narrowed away via * {@link defaultEncoderLoader}'s adapter rather than reflected here. */ export interface EncoderModule { /** Encode an arbitrary value to its `@nhtio/encoder` wire string. */ encode: (value: unknown) => string; /** Decode a `@nhtio/encoder` wire string back into the original value. */ decode: (encoded: string) => unknown; /** Register a class as custom-encodable so `encode`/`decode` round-trip its instances. */ registerClass: (ctor: { readonly name: string; }) => void; /** Whether `value` is an instance of a class previously passed to `registerClass`. */ isCustomEncodable: (value: unknown) => boolean; /** Whether `value` is an `Error` (or subclass), per the encoder's own classification. */ isError: (value: unknown) => boolean; } /** Whether the encoder peer is currently available. Used to populate `ready.encoderAvailable`. */ export declare const isEncoderAvailable: () => Promise; /** * Mark `value` for transfer (rather than clone) across a `postMessage`-based transport — the Web Worker * transport unwraps this into the message's transfer list. The codec passes marked values through as * `raw` with `transferables` preserved on the {@link WireValue} envelope's `transfer` field. Node * transports ignore the marker entirely (structured-clone/pipe semantics don't have a transfer * list), so `transfer()` is safe to use in transport-agnostic code that may run over either. */ export declare const transfer: (value: T, transferables: unknown[]) => T; /** Options threaded through {@link encodeArgument} for observability + BYO-codec support. */ export interface CodecContext { /** Codec mode/override for this argument (method/stream-level `codec` option). Default `'auto'`. */ mode?: CodecMode; /** Called once per exotic leaf found in `'auto'` mode, before encoding it — observability hook seam * (`host.ts`/`serve.ts` wire this to `codec:escalate` reports). Given the ARGUMENT-RELATIVE path * (e.g. `['onProgress']`) and the classification reason. */ onEscalate?: (path: PropertyKey[], reason: string) => void; /** Human-readable label for this argument, used in thrown exception messages (e.g. `'args[0]'`). */ label: string; } /** * Encode a single call/stream argument (or return value) into a {@link WireValue} per the tiered * strategy described in this module's header. * * @throws {@link @nhtio/adk/batteries/isolation!E_ISOLATION_ENCODER_REQUIRED} when escalation is needed * but no encoder (peer or BYO) is available. * @throws {@link @nhtio/adk/batteries/isolation!E_ISOLATION_UNENCODABLE} when a value contains a * circular reference alongside an exotic leaf, or the encoder itself rejects the value. */ export declare const encodeArgument: (arg: unknown, ctx: CodecContext) => Promise; /** * Decode a {@link WireValue} back into the original value, rehydrating any `{ __nhtio$ }` sentinels * found while re-traversing a `raw` payload. * * @param wireValue - The value as it arrived over the wire. * @param mode - The SAME codec mode the sender used to encode it (needed for the BYO-codec case; ignored * otherwise — the wire tier (`raw` vs `nhtio`) is otherwise self-describing). * @param label - Human-readable label for thrown exception messages. */ export declare const decodeArgument: (wireValue: WireValue, mode: CodecMode | undefined, label: string) => Promise; /** Register classes for `@nhtio/encoder`'s custom-encodable round-trip (sugar over `registerClass`, * called lazily once the encoder is loaded). Throws `E_ISOLATION_ENCODER_REQUIRED` when classes are * listed but the peer is not installed. */ export declare const registerEncodableClasses: (encodables: ReadonlyArray<{ readonly name: string; }>) => Promise; /** * Build a {@link WireError} from a thrown value. The baseline `message`/`name`/`stack` fields are * ALWAYS populated (never omitted regardless of encoder availability) — error-classification-by- * message-signature must keep working even when the encoder is unavailable or a decode later fails. * When `includeRich` is `true` (both sides advertised `encoderAvailable`), ALSO attempts to encode the * original error via `@nhtio/encoder` onto `nhtio` — best-effort: an encode failure silently omits * `nhtio` rather than failing the whole error-crossing. */ export declare const toWireError: (err: unknown, includeRich: boolean) => Promise; /** * Reconstruct an `Error` from a {@link WireError}, preferring the `nhtio`-encoded original when present * and decodable. Falls back silently to the baseline `name`/`message`/`stack` fields on ANY decode * failure (missing encoder, corrupt payload, version mismatch) — the baseline is always sufficient for * message-signature-based classification. */ export declare const fromWireError: (wireError: WireError) => Promise;