Type System
Crous maps JavaScript types to its internal binary type system. Understanding these mappings is essential for predictable serialization, especially for edge cases around numbers and types that don't exist natively in JavaScript.
Type Mapping Table
| JavaScript Type | Crous Wire Type | Decoded As | Notes |
|---|---|---|---|
null | NULL (0x01) | null | |
undefined | NULL (0x01) | null | Lossy — becomes null |
true / false | BOOL (0x02) | boolean | |
number (integer) | INT64 (0x03) | number | Heuristic applied |
number (float) | FLOAT64 (0x04) | number | |
string | STRING (0x05) | string | UTF-8 encoded |
Buffer | BYTES (0x06) | Buffer | |
Uint8Array | BYTES (0x06) | Buffer | Decoded as Buffer |
Array | LIST (0x07) | Array | Heterogeneous OK |
Object (plain) | DICT (0x09) | Object | String keys only |
Set | TAGGED(90) → LIST | Set | Via tagged type |
Number Encoding Heuristic
JavaScript has a single number type (IEEE 754 double). Crous uses a heuristic to determine whether a number should be encoded as an integer or float:
// Internal C logic (simplified)
double num = napi_get_value_double(value);
int64_t as_int = (int64_t)num;
if ((double)as_int == num &&
as_int >= INT64_MIN &&
as_int <= INT64_MAX) {
// Encode as INT64
} else {
// Encode as FLOAT64
}In practice, this means:
const crous = require('crous');
// These encode as INT64
crous.dumps(42); // integer
crous.dumps(0); // zero
crous.dumps(-100); // negative integer
crous.dumps(2 ** 53 - 1); // max safe integer
// These encode as FLOAT64
crous.dumps(3.14); // has decimal
crous.dumps(NaN); // NaN
crous.dumps(Infinity); // Infinity
crous.dumps(-Infinity); // -Infinity
crous.dumps(1.0); // ⚠️ This is INT64! (1.0 === 1 in JS)1.0 is an Integer
1.0 === 1 is true. There's no way to distinguish them. Crous encodes 1.0 as INT64 because the heuristic sees a whole number. This is generally not a problem, but be aware of it.Null and Undefined
Both null and undefined map to the same wire type (NULL). On decode, they always come back as null.
const crous = require('crous');
const data = {
a: null,
b: undefined
};
const result = crous.loads(crous.dumps(data));
console.log(result);
// { a: null, b: null }
// undefined was converted to null!Lossy Mapping
undefined → null is a lossy conversion. If you need to preserve the distinction, filter out undefined values before serialization or use a sentinel value.Strings
Strings are encoded as UTF-8 with a 4-byte length prefix. There is no practical length limit beyond available memory.
const crous = require('crous');
// Regular strings
crous.dumps('Hello, World!');
// Unicode — full support
crous.dumps('こんにちは 🌍');
// Empty strings
crous.dumps('');
// Very long strings — works fine
crous.dumps('x'.repeat(1_000_000));Binary Data (Buffer)
Crous natively supports binary data through Buffer. No base64 encoding needed — bytes go straight into the wire format.
const crous = require('crous');
const fs = require('fs');
// Buffer from array
const buf = Buffer.from([0x89, 0x50, 0x4E, 0x47]);
crous.dumps(buf);
// Read a file as binary
const image = fs.readFileSync('photo.jpg');
const encoded = crous.dumps({ image });
// The image bytes are stored efficiently — no base64 bloat!
// Uint8Array also works
const arr = new Uint8Array([1, 2, 3, 4]);
crous.dumps(arr); // encoded as BYTESArrays
JavaScript arrays map to Crous LIST. They can contain any mix of types.
const crous = require('crous');
// Homogeneous
crous.dumps([1, 2, 3, 4, 5]);
// Heterogeneous — no problem
crous.dumps([1, 'two', true, null, Buffer.from([3])]);
// Nested
crous.dumps([[1, 2], [3, 4], [5, 6]]);
// Empty
crous.dumps([]);No Tuples
Objects (Dictionaries)
Plain objects are encoded as Crous DICT. Keys must be strings.
const crous = require('crous');
// Plain objects
crous.dumps({ name: 'Alice', age: 30 });
// Nested objects
crous.dumps({
user: {
profile: {
bio: 'Hello!'
}
}
});
// Empty object
crous.dumps({});
// ⚠️ Non-string keys will throw CrousEncodeError
// crous.dumps({ [Symbol()]: 'value' }); // Error!Sets
JavaScript Set objects are serialized using the tagged type mechanism with tag 90. The set elements are stored as a list internally.
const crous = require('crous');
const data = {
tags: new Set(['admin', 'user', 'moderator']),
ids: new Set([1, 2, 3])
};
const result = crous.loads(crous.dumps(data));
console.log(result.tags instanceof Set); // true
console.log(result.tags.has('admin')); // trueCross-SDK Type Compatibility
When exchanging Crous data between the Python and Node.js SDKs, be aware of these type differences:
| Python Type | Wire Type | JavaScript Type | Lossy? |
|---|---|---|---|
None | NULL | null | No |
bool | BOOL | boolean | No |
int | INT64 | number | ⚠️ > 2⁵³ |
float | FLOAT64 | number | No |
str | STRING | string | No |
bytes | BYTES | Buffer | No |
list | LIST | Array | No |
tuple | TUPLE | Array | Yes |
dict | DICT | Object | No |
set | SET | Set | No |
frozenset | FROZENSET | Set | Yes |
Large Integers