Random-access struct encoding/decoding as an extensible class for msgpackr, cbor-x, and compatible binary encoders.
structon extracts the random-access struct functionality from msgpackr's struct.js into a standalone, class-based package. It exposes a createStructon(BaseClass) factory that returns a class extending any compatible encoder. The resulting class encodes top-level plain objects as compact fixed-width structs with lazy-access property getters, while delegating inner nested-object encoding to the underlying base encoder (msgpackr or cbor-x).
Binary format compatibility: the byte layout produced by structon is identical to msgpackr's struct.js format. Data written by either implementation can be read by the other.
- Zero-copy lazy access — property getters read directly from the raw buffer; no full deserialization needed to access a single field.
- Extensible base class — extend msgpackr's
Packr/Encoderor cbor-x'sEncoder(or any compatible encoder). - Structure caching — structure definitions are incrementally learned and can be persisted via
saveStructures/getStructurescallbacks. - Nested struct support — nested plain objects are encoded by the base encoder; nested structs within a struct field are handled recursively.
- Binary compatible with msgpackr's
struct.jsrandom-access format.
npm install structon
# peer dependency for msgpackr usage:
npm install msgpackr
# or for cbor-x:
npm install cbor-ximport { Packr } from 'msgpackr';
import { createStructon } from 'structon';
const Structon = createStructon(Packr);
// Persist structures so they survive process restarts
let savedStructures = null;
const enc = new Structon({
structures: [],
saveStructures(s) { savedStructures = s; },
getStructures() { return savedStructures; },
});
const buf = enc.encode({ name: 'Alice', age: 30, score: 99.5 });
const obj = enc.decode(buf);
console.log(obj.name); // 'Alice' — reads from buffer without full decode
console.log(obj.age); // 30
console.log(obj.toJSON()); // { name: 'Alice', age: 30, score: 99.5 }import { Encoder } from 'cbor-x';
import { createStructon } from 'structon';
const Structon = createStructon(Encoder);
const enc = new Structon({ structures: [] });
const buf = enc.encode({ id: 1, label: 'widget' });
const obj = enc.decode(buf);
console.log(obj.id); // 1
console.log(obj.label); // 'widget'Values that are not plain objects (arrays, strings, numbers, Maps, etc.) are encoded and decoded by the base class transparently.
enc.encode([1, 2, 3]); // regular msgpack/cbor array
enc.encode('hello'); // regular msgpack/cbor string
enc.encode(new Map([['a', 1]])); // regular msgpack/cbor mapReturns a Structon class that extends BaseClass.
Parameters
BaseClass—Packrfrom msgpackr,Encoderfrom cbor-x, or any compatible encoder class.
Returns a class with the following additions over BaseClass:
| Member | Description |
|---|---|
encode(value) |
Encodes plain objects as structs; other values via base class. |
decode(buffer) |
Decodes struct buffers into lazy objects; other buffers via base class. |
typedStructs |
Array of learned structure definitions (auto-populated). |
_decodeSliceDirect(src, start, end) |
Low-level slice decoder used by struct getters. |
All options from the base class are forwarded. Additional behaviour:
| Option | Description |
|---|---|
structures |
Shared structure definitions array (passed to base class). |
saveStructures(s) |
Called when new struct definitions are learned (msgpackr API). |
getStructures() |
Called to load previously saved structures (msgpackr API). |
saveShared(s) |
cbor-x equivalent of saveStructures. |
getShared() |
cbor-x equivalent of getStructures. |
The format is identical to msgpackr's struct.js:
[header (1-4 bytes)][fixed-width field section][variable ref section]
- Header: record ID encoded as
0x20 + id(single byte for ids 0-15), or0x38/0x39/0x3awith 1/2/3 extra bytes for larger IDs. - Fixed section: one fixed-width slot per field — numbers as
uint8/uint32/float64, strings as 0/1/2-byte ref offsets, objects as 2/4-byte ref offsets, dates asfloat64. - Ref section: UTF-8 string bytes followed by msgpack/cbor-encoded object bytes.
Field access resolves directly from the buffer — only the bytes for the accessed field are read.
structon is bidirectionally byte-compatible with msgpackr's randomAccessStructure: true mode (msgpackr ≤ 1.11.10, before its struct.js was removed). The test suite verifies all four directions:
- Structon writes → native
Packrreads - native
Packrwrites → Structon reads - byte-identical output for a given plain object
- mixed types + nested objects round-trip when
structuresandtypedStructsare shared
These compatibility tests pin msgpackr 1.11.10 as a dev dependency.
Apache 2.0 — see LICENSE.