npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

class-binary-codec

v0.2.0

Published

Dependency-free fixed-layout binary serialization for decorated TypeScript classes.

Readme

Class Binary Codec

Class Binary Codec serializes decorated TypeScript classes into compact, deterministic, fixed-layout Node.js Buffer values. The wire payload contains only field values: it omits field names, object keys, and structural metadata. This is useful when both producer and consumer already share the same schema, such as pagination tokens, cursors, cache keys, compact identifiers, and small internal payloads.

This is serialization, not entropy compression such as gzip or zstd. Use a self-describing format such as JSON when schemas must evolve independently, consumers do not share the class definition, or optional fields and collections are required. Use a compression library for large or arbitrary data.

Install

npm install class-binary-codec

Requires Node.js 20.0.0 or newer.

Usage

Decorate every serialized field, then pass the root class as the schema.

import {
  BinaryCompactString,
  BinaryDate,
  BinaryObject,
  BinaryUInt32,
  BinaryUuid,
  decodeBinaryBase64,
  encodeBinaryBase64Url,
} from 'class-binary-codec';

class Anchor {
  @BinaryUInt32()
  position!: number;

  @BinaryCompactString()
  key!: string;
}

class PageToken {
  @BinaryUuid()
  userId!: string;

  @BinaryDate()
  createdAt!: Date;

  @BinaryObject(() => Anchor)
  anchor!: Anchor;
}

const payload: PageToken = {
  userId: 'b84287c5-7f25-11f1-addc-7a0328b9b7d0',
  createdAt: new Date('2026-07-24T08:10:11.123Z'),
  anchor: {
    position: 1_783_993_532,
    key: '00042',
  },
};

const encoded = encodeBinaryBase64Url(payload, PageToken);
if (encoded === undefined) {
  throw new Error('Encoding failed');
}

console.log(encoded);
// uEKHxX8lEfGt3HoDKLm30AAAAZ-TLL8zalWUvAMFMDAwNDI

const decoded = decodeBinaryBase64(encoded, PageToken);
if (decoded === undefined) {
  throw new Error('Decoding failed');
}

console.log(decoded.createdAt.toISOString());
// 2026-07-24T08:10:11.123Z

Run the example

The complete runnable example is examples/binary-codec-example.ts:

npx tsx examples/binary-codec-example.ts

The example registers the same property decorators as functions, for example BinaryUInt32()(Anchor.prototype, 'position'). This makes it runnable without a tsconfig configured for legacy TypeScript decorator syntax.

For the checked-in payload, the wire output is deterministic:

| Representation | Output | | --- | --- | | Hex | b84287c57f2511f1addc7a0328b9b7d00000019f932cbf336a5594bc03053030303432 | | Base64URL | uEKHxX8lEfGt3HoDKLm30AAAAZ-TLL8zalWUvAMFMDAwNDI | | Binary size | 35 bytes | | Base64URL size | 47 characters | | JSON UTF-8 size | 135 bytes | | Binary size reduction | 74.1% versus this JSON payload |

Wire format

Values use network byte order (big-endian). No field identifiers, presence markers, object lengths, schema IDs, or version bytes are added.

| Decorator | Accepted value | Wire size | | --- | --- | --- | | @BinaryUInt32() | Integer from 0 through 4_294_967_295 | 4 bytes | | @BinaryDate() | Valid Date or date string with a non-negative, safe-integer millisecond timestamp | 8 bytes | | @BinaryUuid() | Canonical hyphenated UUID string | 16 bytes | | @BinaryCompactString() | Canonical UUID string | 17 bytes: 1-byte type + 16 bytes | | @BinaryCompactString() | Unsigned decimal string without leading zeroes, except "0", through 2^64 - 1 | 9 bytes: 1-byte type + 8 bytes | | @BinaryCompactString() | Other UTF-8 string | 2-257 bytes: 1-byte type + 1-byte length + 0-255 data bytes | | @BinaryObject(() => Type) | Nested decorated object | Sum of its decorated fields; no wrapper bytes |

BinaryCompactString preserves leading-zero strings such as "00042" by using the UTF-8 representation rather than the unsigned-integer representation.

Schema contract and constraints

  • Decorated field order is the wire contract. Base-class fields are encoded before subclass fields; nested objects are traversed recursively. Reordering, adding, removing, or changing a decorator changes the bytes expected by readers.
  • Schema classes must have a zero-argument constructor because decoding creates each result with new schema().
  • Every decorated field is required by default. Optional / nullable fields are supported by passing { nullable: true } to field decorators (which adds a 1-byte presence flag 0x00 for null/undefined vs 0x01 + value when present).
  • There is no built-in magic prefix, schema identifier, version framing, or migration support. Add an external envelope when a payload needs versioning.
  • The general BinaryCompactString UTF-8 form is limited to 255 encoded bytes.
  • Decoding rejects truncated data, trailing data, invalid UTF-8, unknown compact string tags, and numeric values that cannot be represented safely.
  • Normal encoding and decoding failures return undefined. BinarySchemaError is thrown for schema conflicts such as decorating the same property more than once, including conflicts across an inheritance chain.
  • decodeBinaryBase64 accepts canonical Base64URL and standard Base64 input; encodeBinaryBase64Url emits unpadded Base64URL.

Performance

One local reference run of examples/binary-codec-example.ts used 100,000 iterations on Node.js 26.5.0 (darwin/arm64):

| Operation | Throughput | | --- | ---: | | Binary encode | 869,962 ops/s | | Binary decode | 1,120,890 ops/s | | JSON.stringify | 858,940 ops/s | | JSON.parse | 2,155,773 ops/s |

These figures are a single local measurement, not a universal performance claim. Benchmark results vary with hardware, Node.js version, schema, payload, and warm-up effects; rerun the example on the target system. JSON parsing also does not restore Date instances, while binary decoding does. Size reduction depends on the schema and data; the 74.1% result applies only to the example payload above.

Development

npm install
npm run check

npm run check type-checks the source, runs the Vitest suite, and builds ESM, CommonJS, source maps, and TypeScript declarations.

Before publishing, inspect the package contents:

npm pack --dry-run

License

MIT