Aller au contenu
Français

CBOR serializer

Ce contenu n’est pas encore disponible dans votre langue.

CborSerializer encodes values using CBOR (RFC 8949) — a compact binary serialization format. More compact than JSON, faster for binary-heavy data, similar API surface.

import { CborSerializer } from 'actor-ts';
const serializer = new CborSerializer();
const bytes = serializer.toBinary({ kind: 'increment', n: 1 });
// → Uint8Array (binary CBOR, smaller than JSON)
const decoded = serializer.fromBinary(bytes, '');
// → { kind: 'increment', n: 1 }
You should switch to CBOR if…
Messages carry binary data (images, encoded payloads).
Cluster bandwidth is measurable / metered (cross-region, cellular IoT).
Persistence storage costs scale with payload size.
Sustained millions of small messages where JSON parse cost shows in profiles.

For most apps, JSON is fine — CBOR’s gains don’t justify the loss of human-debuggability.

PayloadJSONCBORSavings
{ kind: 'increment' }14 bytes9 bytes36 %
Order with 5 items250 bytes180 bytes28 %
Image bytes (10 KB)13.3 KB (base64)10 KB (native)25 %
Deeply nested objectvariesvariestypically 20-40 %

Bigger savings on binary data (no base64 overhead) and repeated field names (CBOR can string-table at the protocol level).

Both serializers round-trip the same rich types, so the win is mostly size — CBOR encodes them as compact binary instead of tagged JSON text:

TypeJsonSerializerCborSerializer
Uint8Arraybase64 string (+33 %)native byte string
Numbersdecimal textcompact binary int / float
Date{"__date__":"…"} tag objecttag 1 + epoch (compact)
bigint{"__bigint__":"…"} tag objectbignum tag 2 / 3 (compact)
Map{"__map__":[…]} tag objecttag 259 + native CBOR map
Set{"__set__":[…]} tag objecttag 258 + array
URL{"__url__":"…"} tag objecttag 32 + text
Typed arraysbase64 string (+33 %)native byte string

“The same rich types” is meant literally: Map, Set, BidirectionalMap, BidirectionalMultiMap, RegExp, URL, Error, every typed array, NaN / Infinity / -0 and bigint all come back as real instances from either serializer, and a shared test suite asserts the two agree on every one of them.

This was not always true: CborSerializer used to flatten Map, Set and the two bidirectional collections to an empty {} and lose every entry without raising anything (#1036). If you worked around that by converting to arrays or plain objects before encoding, you no longer need to.

Two differences remain, both deliberate:

  • undefined. CborSerializer carries it natively (CBOR simple value 23), so { a: undefined } round-trips with a present and undefined. JsonSerializer rejects undefined outright — which makes CBOR the more permissive of the two here.
  • Non-Uint8Array binary is little-endian. Typed arrays travel as their platform’s bytes. Every runtime actor-ts supports is little-endian, so this only matters if you hand the bytes to a big-endian peer yourself.
import { SerializationExtensionId, CborSerializer } from 'actor-ts';
const ext = system.extension(SerializationExtensionId);
ext.setDefault(new CborSerializer());

This changes which serializer the SerializationExtension treats as its default — the one ext.encode(value) uses for any value without a specific class binding. It does not retrofit CBOR onto the cluster wire or the persistence stores: cross-node tells still go over the wire as JSON.stringify (see the overview), and the journal / durable-state / snapshot stores write their own JSON. To get CBOR bytes for a specific payload, use CborSerializer directly (see Per-call serialization below).

Per-class binding still works — register the serializer, then bind the class to its numeric id:

const eventSerializer = new ProtobufSerializer<MyEvent>();
ext.register(eventSerializer);
ext.bind(MyEvent, eventSerializer.id);
// MyEvent → Protobuf; other values → the extension default
import { CborSerializer } from 'actor-ts';
const ser = new CborSerializer();
const bytes = ser.toBinary({ data: someUint8Array });
// Later:
const back = ser.fromBinary(bytes, '') as { data: Uint8Array };

Useful when you want CBOR for a specific use case (e.g., HTTP response bodies for binary data) without switching the cluster default.

CBOR is a binary tagged format. Inspecting raw bytes requires a CBOR-aware tool:

Terminal window
$ cat events.cbor | cbor-diag
# decoded to diagnostic notation

For debugging, you may want to briefly switch to JSON when hunting a bug, then switch back.

The framework’s CBOR implementation is internal — pure JS, no extra dependencies. Compliant with RFC 8949.

For external interop (sending CBOR to non-actor-ts systems), the framework’s encoding follows the standard — any RFC-8949-compliant decoder reads it.