Skip to content

Core Concepts

Field Specifications

Fields define how data is structured in binary format. Every field must specify:

  • name: Field identifier
  • type: Codec type to use
  • byteOffset: Position in buffer
  • byteLength: Size in bytes
ts
import type { 
CodecSpec
} from 'binary-codec';
const
spec
= {
byteLength
: 8,
fields
: [
{
name
: 'header', // Field name
type
: 'number', // Codec type
numberType
: 'uint',
byteOffset
: 0, // Start position
byteLength
: 4 // Size in bytes
}, {
name
: 'data',
type
: 'raw',
byteOffset
: 4,
byteLength
: 4
} ] } as
const
satisfies
CodecSpec
;

The as const satisfies Pattern

This TypeScript pattern is crucial for proper type inference:

ts
import type { 
CodecSpec
} from 'binary-codec';
// ✅ CORRECT: Full type inference preserved const
goodSpec
= {
byteLength
: 4,
fields
: [
{
name
: 'value',
type
: 'number',
numberType
: 'uint',
byteOffset
: 0,
byteLength
: 4
} ] } as
const
satisfies
CodecSpec
;
// ❌ WRONG: Type information lost const
badSpec
:
CodecSpec
= {
byteLength
: 4,
fields
: [
{
name
: 'value',
type
: 'number',
numberType
: 'uint',
byteOffset
: 0,
byteLength
: 4
} ] };

Why This Matters

  • as const: Preserves literal types and makes the object readonly
  • satisfies CodecSpec: Ensures type safety while preserving exact structure
  • Together: Enables precise type inference without losing information

Type Inference Magic

The core feature of Binary Codec is automatic type inference from your field specifications.

The Infer Type

ts
import type { 
CodecSpec
,
Infer
} from 'binary-codec';
const
messageSpec
= {
byteLength
: 7,
fields
: [
{
name
: 'id',
type
: 'number',
numberType
: 'uint',
byteOffset
: 0,
byteLength
: 2
}, {
name
: 'text',
type
: 'string',
byteOffset
: 2,
byteLength
: 5
} ] } as
const
satisfies
CodecSpec
;
// Hover over Message to see the inferred structure type
Message
=
Infer
<typeof
messageSpec
>;

Function Return Type Inference

The deserialize function automatically returns the correctly typed result:

ts
import type { 
CodecSpec
} from 'binary-codec';
import {
deserialize
} from 'binary-codec';
const
spec
= {
byteLength
: 3,
fields
: [
{
name
: 'count',
type
: 'number',
numberType
: 'uint',
byteOffset
: 0,
byteLength
: 1
}, {
name
: 'flags',
type
: 'raw',
byteOffset
: 1,
byteLength
: 2
} ] } as
const
satisfies
CodecSpec
;
const
buffer
= new
Uint8Array
(3);
// Hover over result to see the inferred type const
result
=
deserialize
(
spec
,
buffer
);
// TypeScript knows the exact structure
console
.
log
(
result
.
count
); // number
console
.
log
(
result
.
flags
); // Uint8Array

Registry and Codec System

Binary Codec uses a registry-based architecture where codecs are registered and resolved at runtime.

Entry Functions with Default Registry

Binary Codec provides two main entry functions that automatically use the default registry:

ts
import type { 
CodecSpec
} from 'binary-codec';
import {
deserialize
,
getDefaultRegistry
,
serialize
} from 'binary-codec';
const
spec
= {
byteLength
: 4,
fields
: [
{
name
: 'value',
type
: 'number',
numberType
: 'uint',
byteOffset
: 0,
byteLength
: 4
} ] } as
const
satisfies
CodecSpec
;
// These functions automatically use the default registry const
data
= {
value
: 42
}; const
buffer
=
serialize
(
spec
,
data
); // Uses default registry
const
result
=
deserialize
(
spec
,
buffer
); // Uses default registry
// The default registry includes all built-in codecs const
registry
=
getDefaultRegistry
();
console
.
log
(
registry
.
listTypes
());
// ['raw', 'string', 'number', 'array', 'bitmask', 'bitset', 'object']

Custom Registry (Advanced)

For advanced use cases, you can create custom registries:

ts
import { 
CodecRegistry
} from 'binary-codec';
// Create a custom registry const
customRegistry
= new
CodecRegistry
();
// Register only specific codecs you need // customRegistry.install(rawCodec) // customRegistry.install(numberCodec) // Use with entry functions // const result = deserialize(spec, buffer, customRegistry)

TIP

Most users should use the default registry and entry functions. Custom registries are for advanced scenarios like plugin systems or minimal bundles.

Codec Dependencies

Codecs have dependencies on each other, creating a learning hierarchy:

  • raw - Foundation codec, no dependencies
  • string - Built on raw for text encoding
  • number - Independent numeric data handling
  • array - Can contain any non-array codec
  • bitset - Independent bit manipulation
  • bitmask - Independent bit field extraction
  • object - Can contain any other codec

Understanding these dependencies helps you:

  • Learn codecs in the right order
  • Understand how complex types are built
  • Debug type inference issues

Validation System

Binary Codec includes a comprehensive validation system with different severity levels:

Validation Levels

ts
// import { ValidationLevel } from 'binary-codec';

// Available validation levels
enum 
ValidationLevel
{
FATAL
= 'fatal', // Throw error, stop execution
ERROR
= 'error', // Log error, may affect result
WARNING
= 'warning', // Warning, doesn't affect functionality
INFO
= 'info' // Information only
}
  • FATAL: Critical errors that prevent execution (e.g., invalid codec type)
  • ERROR: Serious issues that may cause incorrect results (e.g., data type mismatch)
  • WARNING: Issues that don't break functionality (e.g., string too long)
  • INFO: Informational messages for debugging

Validation Stages

Binary Codec performs different validations depending on the operation:

During Deserialization

ts
import type { 
CodecSpec
} from 'binary-codec';
import {
deserialize
} from 'binary-codec';
const
spec
= {
byteLength
: 4,
fields
: [
{
name
: 'value',
type
: 'number',
numberType
: 'uint',
byteOffset
: 0,
byteLength
: 4
} ] } as
const
satisfies
CodecSpec
;
const
buffer
= new
Uint8Array
(4);
// Validation during deserialize: // 1. Basic CodecSpec validation (structure, field bounds) // 2. Codec-specific validation (via codec.validate) // 3. Buffer size validation const
result
=
deserialize
(
spec
,
buffer
);

During Serialization

ts
import type { 
CodecSpec
} from 'binary-codec';
import {
serialize
} from 'binary-codec';
const
spec
= {
byteLength
: 4,
fields
: [
{
name
: 'value',
type
: 'number',
numberType
: 'uint',
byteOffset
: 0,
byteLength
: 4
} ] } as
const
satisfies
CodecSpec
;
const
data
= {
value
: 42
}; // Validation during serialize: // 1. Basic CodecSpec validation (same as deserialize) // 2. Codec-specific validation (via codec.validate) // 3. Runtime data validation (via codec.validateData) const
buffer
=
serialize
(
spec
,
data
);

Validation Options

ts
import type { 
CodecSpec
,
ValidationOptions
} from 'binary-codec';
import {
deserialize
} from 'binary-codec';
const
spec
= {
byteLength
: 4,
fields
: [
{
name
: 'value',
type
: 'number',
numberType
: 'uint',
byteOffset
: 0,
byteLength
: 4
} ] } as
const
satisfies
CodecSpec
;
const
options
:
ValidationOptions
= {
validate
: true, // Enable validation (default: true)
throwOnFatal
: true, // Throw on fatal errors (default: true)
onValidation
:
results
=> { // Custom validation handler
console
.
log
('Validation results:',
results
);
} }; const
buffer
= new
Uint8Array
(4);
const
result
=
deserialize
(
spec
,
buffer
,
undefined
,
options
);

Customization (Advanced)

Binary Codec supports full customization of both registries and codecs:

  • Custom Codecs: Create your own data type handlers
  • Custom Registries: Control which codecs are available
  • Plugin Systems: Build extensible binary format libraries

Coming Soon

Detailed customization documentation is currently being prepared. This will cover creating custom codecs, building plugin systems, and advanced registry management.

Next Steps

Now that you understand the core concepts, let's explore the basic types in dependency order:

Released under the MIT License.