binary-codec
v1.0.0
Published
A lightweight TypeScript utility library for working with binary data.
Readme
binary-codec
A lightweight TypeScript utility library for working with binary data. Encode and decode numbers, strings, bitmasks, arrays, and objects directly from
ArrayBuffer/DataView.
✨ Features
- 📦 Lightweight – minimal runtime dependency, pure TypeScript.
- 🧩 Modular codecs – raw, numbers, strings, bitset, bitmasks, arrays, objects.
- 🔧 Extensible – define and register your own custom codec.
- 🔄 Symmetric design – consistent
read/writeAPI. - 🌐 Cross-platform – works in Node.js and modern browsers.
- 🧠 Type inference from config – generate precise TypeScript types automatically from your codec configuration, no manual typing needed.
- ✅ Built-in validation – comprehensive validation system to catch configuration errors early.
- 🔍 Recursive validation – validates nested structures with precise error paths.
📚 Documentations
Please follow the details on the Documentation!
🚀 Installation
pnpm add binary-codec
# or
npm install binary-codec
# or
yarn add binary-codec⚡ Quick Start
Basic Usage
import type { CodecSpec } from 'binary-codec';
import { deserialize, serialize } from 'binary-codec';
// Define your data structure
const packetSpec = {
byteLength: 21,
fields: [
{
name: 'id',
type: 'number',
numberType: 'uint',
byteOffset: 0,
byteLength: 4
},
{
name: 'name',
type: 'string',
byteOffset: 4,
byteLength: 16
},
{
name: 'flags',
type: 'bitmask',
byteOffset: 20,
byteLength: 1,
map: {
enabled: {
bits: 0,
type: 'boolean'
},
priority: {
bits: [3, 1],
type: 'uint'
}
}
}
]
} as const satisfies CodecSpec;
// Deserialize binary data
const buffer = new Uint8Array(21); // mock data
const data = deserialize(buffer, packetSpec);
// Type: { id: number; name: string; flags: { enabled: boolean; priority: number } }
// Serialize data to binary
const binaryData = serialize(data, packetSpec);Manual Validation
import { createRegistry, validateCodecSpec } from 'binary-codec';
const registry = createRegistry();
const results = validateCodecSpec(spec, registry);
results.forEach(result => {
console.log(`${result.level}: ${result.message} at ${result.path}`);
});Validation Levels
- FATAL: Critical errors that prevent operation
- ERROR: Serious issues that should be fixed
- WARNING: Potential problems worth noting
- INFO: Informational messages
Recursive Validation
The validation system automatically validates nested structures:
const complexSpec = {
fields: [
{
name: 'data',
type: 'array',
byteOffset: 0,
byteLength: 8,
item: {
type: 'bitmask',
byteLength: 2,
map: {
invalid: {
bits: 20, // ❌ Error: exceeds 16 bits
type: 'boolean'
}
}
}
}
]
};
// Error path: "fields[0].item.map.invalid"🔧 Custom Codecs
Extend the library with your own codecs:
import { Codec, ValidationLevel } from 'binary-codec';
// Define your codec
const customCodec: Codec<CustomSpec, CustomType> = {
type: 'custom',
read: (view, spec, ctx) => {
// Your read implementation
},
write: (view, spec, value, ctx) => {
// Your write implementation
},
validate: (spec, path, ctx) => {
// Optional validation
const results = [];
if (/* invalid condition */) {
results.push({
level: ValidationLevel.ERROR,
message: 'Custom validation error',
path,
code: 'CUSTOM_ERROR'
});
}
return results;
}
};
// Register it
const registry = createRegistry();
registry.install(customCodec);🧠 Type Inference
Get precise TypeScript types automatically from your configuration:
import { Infer } from 'binary-codec';
const packetSpec = {
byteLength: 13,
fields: [
{
name: 'id',
type: 'number',
numberType: 'uint',
byteOffset: 0,
byteLength: 4
},
{
name: 'flags',
type: 'bitmask',
byteOffset: 4,
byteLength: 1,
map: {
active: {
bits: 0,
type: 'boolean'
},
priority: {
bits: [3, 1],
type: 'uint'
}
}
},
{
name: 'items',
type: 'array',
byteOffset: 5,
byteLength: 8,
item: {
type: 'number',
numberType: 'uint',
byteLength: 2
}
}
]
} as const satisfies CodecSpec;
// Automatically inferred type:
type PacketType = Infer<typeof packetSpec>;
// {
// id: number;
// flags: { active: boolean; priority: number };
// items: number[];
// }🌐 Browser & Node.js
Works seamlessly in both environments:
// Node.js
import fs from 'node:fs';
import { deserialize } from 'binary-codec';
const buffer = fs.readFileSync('data.bin');
const data = deserialize(buffer, spec);
// Browser
fetch('/api/data')
.then(response => response.arrayBuffer())
.then(buffer => deserialize(buffer, spec));📊 Performance
- Zero dependencies – minimal bundle size
- Efficient operations – direct
DataViewmanipulation - Type-safe – compile-time type checking
- Memory efficient – no intermediate object creation during serialization
🔄 Endianness
Control byte order for multi-byte values:
{
type: 'number',
numberType: 'uint',
byteLength: 4,
littleEndian: true // default: false (big-endian)
}📝 Examples
Network Protocol Parsing
import type { CodecSpec } from 'binary-codec';
const tcpHeaderSpec = {
byteLength: 14,
fields: [
{
name: 'srcPort',
type: 'number',
numberType: 'uint',
byteOffset: 0,
byteLength: 2
},
{
name: 'dstPort',
type: 'number',
numberType: 'uint',
byteOffset: 2,
byteLength: 2
},
{
name: 'seqNum',
type: 'number',
numberType: 'uint',
byteOffset: 4,
byteLength: 4
},
{
name: 'ackNum',
type: 'number',
numberType: 'uint',
byteOffset: 8,
byteLength: 4
},
{
name: 'flags',
type: 'bitmask',
byteOffset: 13,
byteLength: 1,
map: {
fin: {
bits: 0,
type: 'boolean'
},
syn: {
bits: 1,
type: 'boolean'
},
rst: {
bits: 2,
type: 'boolean'
},
psh: {
bits: 3,
type: 'boolean'
},
ack: {
bits: 4,
type: 'boolean'
},
urg: {
bits: 5,
type: 'boolean'
}
}
}
]
} as const satisfies CodecSpec;
const header = deserialize(packetBuffer, tcpHeaderSpec);
console.log(header.flags.syn); // booleanFile Format Parsing
const bmpHeaderSpec = {
byteLength: 26,
fields: [
{
name: 'signature',
type: 'string',
byteOffset: 0,
byteLength: 2
},
{
name: 'fileSize',
type: 'number',
numberType: 'uint',
byteOffset: 2,
byteLength: 4,
littleEndian: true
},
{
name: 'dataOffset',
type: 'number',
numberType: 'uint',
byteOffset: 10,
byteLength: 4,
littleEndian: true
},
{
name: 'width',
type: 'number',
numberType: 'uint',
byteOffset: 18,
byteLength: 4,
littleEndian: true
},
{
name: 'height',
type: 'number',
numberType: 'uint',
byteOffset: 22,
byteLength: 4,
littleEndian: true
}
]
} as const;
const bmpHeader = deserialize(fileBuffer, bmpHeaderSpec);🤝 Contributing
Contributions are welcome! Please feel free to submit issues and pull requests.
📄 License
MIT License - see LICENSE file for details.
