Skip to content

Pluggable wire serialization for TypeScript

One tiny interface — encode a value to a wire format, decode it back — and a family of format adapters that implement it. Swap JSON for MessagePack, CBOR, or Avro with a one-argument change, never a call-site rewrite.

One adapter install yields a working serde — the JSON adapter brings the interface package with it:

Terminal window
bun add @insler/serde-json
import { jsonSerde } from '@insler/serde-json';
const wire = jsonSerde.encode({ createdAt: new Date(), tags: new Set(['a', 'b']) });
const value = jsonSerde.decode(wire);
// { createdAt: Date, tags: Set(['a', 'b']) } — rich types survive (SuperJSON)

The getting-started guide walks from this install to swapping in every binary format behind the same seam.

The zero-dependency core @insler/serde owns a single contract — Serde<Wire>, two methods, Wire as the only knob. Every format binding is its own adapter package implementing it: JSON (SuperJSON-backed — Date, Map, Set, BigInt, and RegExp survive), MessagePack, CBOR, and Avro. The binary adapters are all Serde<Uint8Array>, so they are interchangeable wherever a binary wire is expected.

Anything that moves values over a wire takes a Serde — the rpc subsystem’s transports accept one as their serde option. Develop on JSON, deploy on MessagePack or CBOR, without touching a call site. The core is the bottom of the stack: zero dependencies, nothing internal sits below it.

Every implementation honors the same conventions: encode(undefined) produces an empty wire, decoding an empty wire returns undefined, and what you decode is what you encoded. The reference documents the interface and each adapter’s exact surface.