CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-bser

JavaScript implementation of the BSER Binary Serialization protocol for efficient local inter-process communication

Pending

Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

SecuritybySnyk

Pending

The risk profile of this skill

Overview
Eval results
Files

BSER

BSER (Binary Serialization) is a JavaScript library that provides binary serialization as an alternative to JSON. It uses a framed encoding that makes it simpler to stream sequences of encoded values. Designed for local inter-process communication, strings are represented as binary data matching operating system filename conventions.

Package Information

  • Package Name: bser
  • Package Type: npm
  • Language: JavaScript
  • Installation: npm install bser

Core Imports

const bser = require('bser');

For ES modules:

import * as bser from 'bser';

Individual imports:

const { loadFromBuffer, dumpToBuffer, BunserBuf, Accumulator } = require('bser');

Basic Usage

const bser = require('bser');

// Synchronous encoding and decoding
const data = { message: "hello", numbers: [1, 2, 3], active: true };
const encoded = bser.dumpToBuffer(data);
const decoded = bser.loadFromBuffer(encoded);
console.log(decoded); // { message: "hello", numbers: [1, 2, 3], active: true }

// Asynchronous streaming decoding
const bunser = new bser.BunserBuf();
bunser.on('value', (obj) => {
  console.log('Received:', obj);
});

// In your socket data handler
socket.on('data', (buf) => {
  bunser.append(buf);
});

Architecture

BSER is built around several key components:

  • Synchronous API: Direct encoding/decoding functions (loadFromBuffer, dumpToBuffer) for simple operations
  • Asynchronous API: Event-driven streaming decoder (BunserBuf) for handling partial data and streaming scenarios
  • Buffer Management: Efficient buffer handling (Accumulator) for managing expandable data storage
  • Type System: Support for JavaScript primitives, arrays, objects, and 64-bit integers via node-int64
  • Endianness Handling: Automatic detection and handling of system byte order for optimal performance

Capabilities

Synchronous Encoding

Converts JavaScript values to BSER binary format in a single operation.

/**
 * Synchronously encodes a value as BSER
 * @param {any} val - JavaScript value to encode
 * @returns {Buffer} BSER-encoded data
 * @throws {Error} If value cannot be serialized
 */
function dumpToBuffer(val);

Usage Examples:

const bser = require('bser');

// Encode various data types
const encoded1 = bser.dumpToBuffer(42);
const encoded2 = bser.dumpToBuffer("hello world");
const encoded3 = bser.dumpToBuffer([1, 2, 3]);
const encoded4 = bser.dumpToBuffer({ name: "Alice", age: 30 });

// Encode with 64-bit integers
const Int64 = require('node-int64');
const bigNum = new Int64('0x0123456789abcdef');
const encodedBig = bser.dumpToBuffer(bigNum);

Synchronous Decoding

Decodes BSER binary data back to JavaScript values in a single operation.

/**
 * Synchronously decodes BSER data from a buffer
 * @param {Buffer} input - Buffer containing BSER-encoded data
 * @returns {any} Decoded JavaScript value
 * @throws {Error} If input is invalid or has excess data
 */
function loadFromBuffer(input);

Usage Examples:

const bser = require('bser');

// Decode BSER data
const encoded = bser.dumpToBuffer({ message: "test", count: 5 });
const decoded = bser.loadFromBuffer(encoded);
console.log(decoded); // { message: "test", count: 5 }

// Error handling
try {
  const invalid = Buffer.from([0x01, 0x02, 0x03]); // Invalid BSER data
  const result = bser.loadFromBuffer(invalid);
} catch (error) {
  console.error('Decoding failed:', error.message);
}

Asynchronous Streaming Decoding

Event-driven decoder for processing BSER data incrementally, ideal for network streams and large data sets.

/**
 * Asynchronous BSER decoder for streaming/incremental processing
 * @extends EventEmitter
 */
class BunserBuf extends EventEmitter {
  constructor();
  
  /**
   * Append data to the buffer for processing
   * @param {Buffer} buf - Data to append
   * @param {boolean} [synchronous] - If true, processes synchronously and returns result
   * @returns {any} Decoded value if synchronous, undefined otherwise  
   */
  append(buf, synchronous);
}

Events:

  • value: Emitted when a complete value is decoded
  • error: Emitted when an error occurs during processing

Usage Examples:

const bser = require('bser');
const net = require('net');

// Stream processing
const bunser = new bser.BunserBuf();

bunser.on('value', (obj) => {
  console.log('Received complete object:', obj);
});

bunser.on('error', (err) => {
  console.error('BSER decoding error:', err);
});

// Process data from a socket
const socket = net.connect('/some/socket/path');
socket.on('data', (chunk) => {
  bunser.append(chunk);
});

// Synchronous processing
const encoded = bser.dumpToBuffer({ test: true });
const result = bunser.append(encoded, true);
console.log('Synchronous result:', result);

Buffer Management

Expandable buffer utility for efficient data accumulation with automatic resizing and read/write position tracking.

/**
 * Expandable buffer for efficient data accumulation
 */
class Accumulator {
  /**
   * Create a new accumulator
   * @param {number} [initsize=8192] - Initial buffer size
   */
  constructor(initsize);
  
  /**
   * Returns available write space
   * @returns {number} Available bytes for writing
   */
  writeAvail();
  
  /**
   * Returns available read data
   * @returns {number} Available bytes for reading  
   */
  readAvail();
  
  /**
   * Ensure buffer has enough space for size bytes
   * @param {number} size - Required space in bytes
   */
  reserve(size);
  
  /**
   * Append buffer or string data
   * @param {Buffer|string} buf - Data to append
   */
  append(buf);
  
  /**
   * Read string without advancing read position
   * @param {number} size - Number of bytes to read
   * @returns {string} UTF-8 decoded string
   * @throws {Error} If not enough data available
   */
  peekString(size);
  
  /**
   * Read string and advance read position
   * @param {number} size - Number of bytes to read
   * @returns {string} UTF-8 decoded string
   * @throws {Error} If not enough data available
   */
  readString(size);
  
  /**
   * Read integer without advancing read position
   * @param {number} size - Size in bytes (1, 2, 4, or 8)
   * @returns {number|Int64} Integer value
   * @throws {Error} If invalid size or not enough data
   */
  peekInt(size);
  
  /**
   * Read integer and advance read position
   * @param {number} bytes - Size in bytes (1, 2, 4, or 8)
   * @returns {number} Integer value
   * @throws {Error} If invalid size or not enough data
   */
  readInt(bytes);
  
  /**
   * Read double without advancing read position
   * @returns {number} Double-precision floating point value
   * @throws {Error} If not enough data available
   */
  peekDouble();
  
  /**
   * Read double and advance read position
   * @returns {number} Double-precision floating point value
   * @throws {Error} If not enough data available
   */
  readDouble();
  
  /**
   * Advance read position by size bytes
   * @param {number} size - Bytes to advance (can be negative)
   * @throws {Error} If would seek beyond buffer bounds
   */
  readAdvance(size);
  
  /**
   * Write single byte value
   * @param {number} value - Byte value to write
   */
  writeByte(value);
  
  /**
   * Write integer value of specified byte size
   * @param {number} value - Integer value to write
   * @param {number} size - Size in bytes (1, 2, or 4)
   * @throws {Error} If unsupported size
   */
  writeInt(value, size);
  
  /**
   * Write double-precision floating point value
   * @param {number} value - Double value to write
   */
  writeDouble(value);
}

Usage Examples:

const bser = require('bser');

// Manual buffer operations
const acc = new bser.Accumulator(1024);

// Write data
acc.append("Hello");
acc.writeByte(0x20); // Space character
acc.append("World");

// Read data
console.log(acc.readAvail()); // 11
console.log(acc.peekString(5)); // "Hello" (doesn't advance)
console.log(acc.readString(5)); // "Hello" (advances position)
console.log(acc.readString(6)); // " World"

// Integer operations
acc.writeInt(42, 4);
acc.writeDouble(3.14159);

Types

Supported Data Types

BSER can serialize and deserialize the following JavaScript types:

// Primitive types
type SerializableValue = 
  | number          // Integers and floating-point numbers
  | string          // UTF-8 strings (stored as binary)
  | boolean         // true/false values  
  | null            // null value
  | Int64           // 64-bit integers (from node-int64 package)
  | Array<SerializableValue>
  | { [key: string]: SerializableValue };

// Integer limits for automatic type selection
const MAX_INT8 = 127;
const MAX_INT16 = 32767; 
const MAX_INT32 = 2147483647;

Error Types

// Standard Error objects are thrown for various conditions:
// - "wanted to read N bytes but only have M" - Insufficient buffer data
// - "excess data found after input buffer" - Extra data after valid BSER
// - "no bser found in string and no error raised" - No valid BSER data
// - "cannot serialize type X to BSER" - Unsupported data type
// - "invalid bser int encoding N" - Corrupted integer encoding
// - "unhandled bser opcode N" - Unknown BSER type code

Advanced Usage

64-bit Integer Support

const bser = require('bser');
const Int64 = require('node-int64');

// Create 64-bit integers
const bigNum = new Int64('0x0123456789abcdef');
const encoded = bser.dumpToBuffer(bigNum);
const decoded = bser.loadFromBuffer(encoded);

console.log(decoded instanceof Int64); // true
console.log(decoded.toString(16)); // "123456789abcdef"

Template Optimization

BSER supports template encoding for arrays of objects with similar structures:

// Template data is automatically handled by the decoder
const templateData = [
  { name: "fred", age: 20 },
  { name: "pete", age: 30 }, 
  { age: 25 } // Missing 'name' field
];

const encoded = bser.dumpToBuffer(templateData);
const decoded = bser.loadFromBuffer(encoded);
// Templates are transparently decoded back to regular arrays

Error Handling Best Practices

const bser = require('bser');

function safeDecode(buffer) {
  try {
    return { success: true, data: bser.loadFromBuffer(buffer) };
  } catch (error) {
    return { success: false, error: error.message };
  }
}

function safeEncode(value) {
  try {
    return { success: true, data: bser.dumpToBuffer(value) };
  } catch (error) {
    return { success: false, error: error.message };
  }
}

// Stream error handling
const bunser = new bser.BunserBuf();
bunser.on('error', (err) => {
  console.error('Stream decoding failed:', err.message);
  // Reset or recreate bunser as needed
});
Workspace
tessl
Visibility
Public
Created
Last updated
Describes
npmpkg:npm/bser@2.1.x
Publish Source
CLI
Badge
tessl/npm-bser badge