Serialization

Crous provides a simple and familiar API for serializing JavaScript objects into compact binary format and deserializing them back. If you've used JSON.stringify /JSON.parse, you already know the pattern.

In-Memory Serialization

dumps — Serialize to Buffer

dumps.js
const crous = require('crous');

const data = {
    name: 'Alice',
    scores: [95.5, 88.0, 100.0],
    active: true
};

// Serialize to Buffer
const buffer = crous.dumps(data);
console.log(buffer);        // <Buffer ...>
console.log(buffer.length); // compact binary representation

loads — Deserialize from Buffer

loads.js
const crous = require('crous');

// Deserialize from Buffer
const data = crous.loads(buffer);
console.log(data.name);   // 'Alice'
console.log(data.scores); // [95.5, 88, 100]

Buffer Input

loads() accepts both Buffer and Uint8Array objects.

File Serialization

dump — Write to File

file_write.js
const crous = require('crous');

const config = {
    database: {
        host: 'localhost',
        port: 5432,
        name: 'myapp'
    },
    features: ['auth', 'logging', 'cache'],
    debug: false
};

// Write directly to a file path
crous.dump(config, 'config.crous');

load — Read from File

file_read.js
const crous = require('crous');

// Read from file path
const config = crous.load('config.crous');
console.log(config.database.host); // 'localhost'
console.log(config.features);      // ['auth', 'logging', 'cache']

Stream Serialization

dump and load also accept Node.js streams for integration with pipelines.

stream_io.js
const crous = require('crous');
const fs = require('fs');

// Write to a writable stream
const wstream = fs.createWriteStream('output.crous');
crous.dump({ key: 'value' }, wstream);
wstream.end();

// Read from a readable stream (buffered)
const rstream = fs.createReadStream('output.crous');
const chunks = [];
rstream.on('data', (chunk) => chunks.push(chunk));
rstream.on('end', () => {
    const buffer = Buffer.concat(chunks);
    const data = crous.loads(buffer);
    console.log(data); // { key: 'value' }
});

The default Function

When Crous encounters an object it doesn't know how to serialize, you can provide a default function that converts it to a serializable form.

default_fn.js
const crous = require('crous');

class Point {
    constructor(x, y) {
        this.x = x;
        this.y = y;
    }
}

const data = { origin: new Point(0, 0), cursor: new Point(10, 20) };

// Without default → CrousEncodeError
// With default → works!
const buffer = crous.dumps(data, {
    default: (obj) => {
        if (obj instanceof Point) {
            return { __point__: true, x: obj.x, y: obj.y };
        }
        throw new Error(`Cannot serialize ${typeof obj}`);
    }
});

console.log(crous.loads(buffer));
// { origin: { __point__: true, x: 0, y: 0 },
//   cursor: { __point__: true, x: 10, y: 20 } }

The object_hook Function

On the decoding side, object_hook lets you intercept every decoded object and transform it — perfect for reviving class instances.

object_hook.js
const crous = require('crous');

class Point {
    constructor(x, y) {
        this.x = x;
        this.y = y;
    }
    toString() {
        return `Point(${this.x}, ${this.y})`;
    }
}

const buffer = crous.dumps({
    origin: { __point__: true, x: 0, y: 0 },
    cursor: { __point__: true, x: 10, y: 20 }
});

const data = crous.loads(buffer, {
    object_hook: (obj) => {
        if (obj.__point__) {
            return new Point(obj.x, obj.y);
        }
        return obj;
    }
});

console.log(data.origin.toString());  // Point(0, 0)
console.log(data.cursor.toString());  // Point(10, 20)

CrousEncoder & CrousDecoder Classes

For repeated serialization with the same options, the class-based API avoids passing options on every call.

encoder_decoder.js
const { CrousEncoder, CrousDecoder } = require('crous');

// Create an encoder with a default handler
const encoder = new CrousEncoder({
    default: (obj) => {
        if (obj instanceof Date) {
            return { __date__: obj.toISOString() };
        }
        throw new TypeError(`Unserializable: ${typeof obj}`);
    }
});

// Create a decoder with an object hook
const decoder = new CrousDecoder({
    object_hook: (obj) => {
        if (obj.__date__) {
            return new Date(obj.__date__);
        }
        return obj;
    }
});

// Use them repeatedly
const buf = encoder.dumps({ created: new Date() });
const result = decoder.loads(buf);
console.log(result.created instanceof Date); // true

Comparison with JSON

FeatureJSONCrous
FormatText (UTF-8)Binary (compact)
Buffer / Bytes❌ Base64 workaround✅ Native
Set❌ No✅ Tagged type (90)
Integer precision⚠️ 53-bit✅ Full 64-bit
NaN / Infinity❌ No✅ Yes
default / replacer✅ Yes✅ Yes
object_hook / reviver✅ Yes✅ Yes
Human-readable✅ Yes❌ No