JSPM

  • ESM via JSPM
  • ES Module Entrypoint
  • Export Map
  • Keywords
  • License
  • Repository URL
  • TypeScript Types
  • README
  • Created
  • Published
  • Downloads 27
  • Score
    100M100P100Q59463F
  • License MIT

Universal library for decoding and encoding bencode data

Package Exports

  • bencodec

Readme

bencodec

CI Coverage Status npm version npm downloads License: MIT TypeScript Zero Dependencies

A fast, secure, zero-dependency bencode encoder/decoder for modern JavaScript runtimes.

Universal TypeScript library compliant with the BitTorrent bencoding specification. Works in Node.js, browsers, Deno, and Bun.

Highlights

  • Zero Dependencies - No external packages, minimal attack surface
  • Universal - Works in Node.js, browsers, Deno, and Bun
  • TypeScript First - Full type definitions with generics support
  • Security Built-in - DoS protection with configurable limits
  • BitTorrent Compliant - Strict mode for spec validation
  • Modern API - Uses Uint8Array (not Node.js Buffer)
  • Dual Package - ESM and CommonJS exports
  • 100% Tested - Complete code coverage

Installation

# npm
npm install bencodec

# yarn
yarn add bencodec

# pnpm
pnpm add bencodec

# bun
bun add bencodec

Quick Start

import { encodeToBytes, encodeToString, decode } from 'bencodec';

// Encode JavaScript values to bencode (Uint8Array)
const bytes = encodeToBytes({ announce: 'http://tracker.example.com', info: { name: 'file.txt' } });

// Encode JavaScript values to bencode (string)
const str = encodeToString({ announce: 'http://tracker.example.com', info: { name: 'file.txt' } });

// Decode bencode data
const decoded = decode(bytes, { stringify: true });
// { announce: 'http://tracker.example.com', info: { name: 'file.txt' } }

Usage

Decoding

import { decode } from 'bencodec';

// Decode integers
decode('i42e');  // 42

// Decode strings (returns Uint8Array by default)
decode('5:hello');  // Uint8Array [0x68, 0x65, 0x6c, 0x6c, 0x6f]

// Decode strings as JavaScript strings
decode('5:hello', { stringify: true });  // 'hello'

// Decode lists
decode('li1ei2ei3ee', { stringify: true });  // [1, 2, 3]

// Decode dictionaries
decode('d3:fooi42e3:bar4:spame', { stringify: true });  // { bar: 'spam', foo: 42 }

// Type the result with generics
interface Torrent {
  announce: string;
  info: { name: string };
}
const torrent = decode<Torrent>(buffer, { stringify: true });

Encoding

Encode to Bytes (Uint8Array)

import { encodeToBytes } from 'bencodec';

// Encode integers
encodeToBytes(42);  // Uint8Array for 'i42e'

// Encode strings
encodeToBytes('hello');  // Uint8Array for '5:hello'

// Encode lists
encodeToBytes([1, 2, 3]);  // Uint8Array for 'li1ei2ei3ee'

// Encode dictionaries (keys auto-sorted per spec)
encodeToBytes({ z: 1, a: 2 });  // Uint8Array for 'd1:ai2e1:zi1ee'

// Encode binary data
encodeToBytes(new Uint8Array([0x00, 0xff]));

Encode to String

import { encodeToString } from 'bencodec';

// Encode to string (UTF-8 by default)
encodeToString({ foo: 'bar' });  // 'd3:foo3:bare'

// Encode integers
encodeToString(42);  // 'i42e'

// Use latin1 encoding for binary data preservation
encodeToString(new Uint8Array([0x00, 0xff]), { encoding: 'latin1' });  // '2:\x00\xff'

// Supported encodings: 'utf8', 'utf-8', 'latin1', 'binary', 'ascii'
encodeToString({ foo: 42 }, { encoding: 'utf8' });

Legacy encode() (Deprecated)

import { encode } from 'bencodec';

// Deprecated - use encodeToBytes or encodeToString instead
encode(42);                        // Returns Uint8Array
encode({ foo: 'bar' }, { stringify: true });  // Returns string

Default Export

import bencodec from 'bencodec';

bencodec.encodeToBytes({ foo: 42 });
bencodec.encodeToString({ foo: 42 });
bencodec.decode('d3:fooi42ee');

Options

Decode Options (IBencodecOptions)

interface IBencodecOptions {
  /** Return strings instead of Uint8Array (default: false) */
  stringify?: boolean;

  /** Enable strict BitTorrent spec validation (default: false) */
  strict?: boolean;

  /** Character encoding: 'utf8' | 'latin1' | 'ascii' | 'binary' (default: 'utf8') */
  encoding?: ByteEncoding;

  /** Maximum string length in bytes - security limit */
  maxStringLength?: number;

  /** Maximum nesting depth - security limit */
  maxDepth?: number;
}

Encode to String Options (IBencodeEncodeOptions)

interface IBencodeEncodeOptions {
  /** Character encoding for output (default: 'utf8') */
  encoding?: 'utf8' | 'utf-8' | 'latin1' | 'binary' | 'ascii';
}

Strict Mode

Enable strict mode for BitTorrent specification compliance:

// Enforces sorted dictionary keys
decode('d1:bi1e1:ai2ee', { strict: true });
// Throws: UNSORTED_KEYS

// Rejects trailing data
decode('i42eextra', { strict: true });
// Throws: TRAILING_DATA

Security

Bencodec includes built-in protections against denial-of-service attacks when parsing untrusted data.

Memory Exhaustion Protection

Prevent memory exhaustion from maliciously large strings:

decode(untrustedData, {
  maxStringLength: 10 * 1024 * 1024  // 10 MB limit
});

Stack Overflow Protection

Prevent stack overflow from deeply nested structures:

decode(untrustedData, {
  maxDepth: 100  // Maximum nesting depth
});
const SAFE_OPTIONS = {
  maxStringLength: 10 * 1024 * 1024,  // 10 MB
  maxDepth: 100,
  strict: true
};

decode(untrustedData, SAFE_OPTIONS);

Error Handling

Bencodec provides structured error classes with specific error codes for programmatic handling.

Error Classes

import {
  BencodeError,        // Base class
  BencodeDecodeError,  // Decode errors (includes position)
  BencodeEncodeError,  // Encode errors (includes path)
  BencodeErrorCode
} from 'bencodec';

Error Codes

Code Description
EMPTY_INPUT Input data is empty or falsy
UNEXPECTED_END Data ends unexpectedly
INVALID_FORMAT Invalid bencode format
LEADING_ZEROS Integer has leading zeros (e.g., i03e)
NEGATIVE_ZERO Negative zero (i-0e) is not allowed
UNSORTED_KEYS Dictionary keys not sorted (strict mode)
TRAILING_DATA Extra data after valid bencode (strict mode)
MAX_DEPTH_EXCEEDED Nesting depth exceeds limit
MAX_SIZE_EXCEEDED String length exceeds limit
UNSUPPORTED_TYPE Attempted to encode unsupported type
CIRCULAR_REFERENCE Circular reference detected

Decode Error Example

import { decode, BencodeDecodeError, BencodeErrorCode } from 'bencodec';

try {
  decode(untrustedData, { strict: true, maxDepth: 50 });
} catch (error) {
  if (error instanceof BencodeDecodeError) {
    switch (error.code) {
      case BencodeErrorCode.MAX_DEPTH_EXCEEDED:
        console.error(`Too deeply nested at position ${error.position}`);
        break;
      case BencodeErrorCode.INVALID_FORMAT:
        console.error(`Malformed data at position ${error.position}`);
        break;
    }
  }
}

Encode Error Example

import { encode, BencodeEncodeError } from 'bencodec';

try {
  const circular: any = { a: 1 };
  circular.self = circular;
  encode(circular);
} catch (error) {
  if (error instanceof BencodeEncodeError) {
    console.error(`Error at path: ${error.path?.join('.')}`);
    // Output: Error at path: self
  }
}

Platform Support

Platform Version Notes
Node.js 18+ Full support
Browsers Modern Chrome, Firefox, Safari, Edge
Deno 1.0+ Full support
Bun 1.0+ Full support

Browser Usage

<script type="module">
  import { encode, decode } from 'https://esm.sh/bencodec';

  const encoded = encode({ hello: 'world' });
  console.log(decode(encoded, { stringify: true }));
</script>

Deno Usage

import { encode, decode } from 'npm:bencodec';

const encoded = encode({ hello: 'world' });
console.log(decode(encoded, { stringify: true }));

Type Definitions

Full TypeScript support with exported types:

import type {
  IBencodecOptions,
  IBencodeEncodeOptions,
  BencodeDecodedValue,
  BencodeEncodableValue,
  ByteEncoding
} from 'bencodec';

Non-Standard Behaviors

For maximum compatibility, bencodec handles some edge cases beyond the strict spec:

Behavior Description
Plus sign in integers Leading + is silently ignored (i+42e -> 42)
Float truncation Decimal numbers truncated toward zero
Boolean encoding Booleans encoded as integers (true -> i1e)
Null/undefined Silently skipped in lists and dictionaries

License

MIT