Universal library for decoding and encoding bencode data
npm install bencodec!CI




!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.
- 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
``bashnpm
npm install bencodec
Quick Start
`typescript
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
$3
`typescript
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(buffer, { stringify: true });
`$3
#### Encode to Bytes (Uint8Array)
`typescript
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
`typescript
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)
`typescript
import { encode } from 'bencodec';// Deprecated - use encodeToBytes or encodeToString instead
encode(42); // Returns Uint8Array
encode({ foo: 'bar' }, { stringify: true }); // Returns string
`$3
`typescript
import bencodec from 'bencodec';bencodec.encodeToBytes({ foo: 42 });
bencodec.encodeToString({ foo: 42 });
bencodec.decode('d3:fooi42ee');
`$3
#### Decode Options (IBencodecOptions)
`typescript
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)
`typescript
interface IBencodeEncodeOptions {
/* Character encoding for output (default: 'utf8') /
encoding?: 'utf8' | 'utf-8' | 'latin1' | 'binary' | 'ascii';
}
`Strict Mode
Enable strict mode for BitTorrent specification compliance:
`typescript
// 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.
$3
Prevent memory exhaustion from maliciously large strings:
`typescript
decode(untrustedData, {
maxStringLength: 10 1024 1024 // 10 MB limit
});
`$3
Prevent stack overflow from deeply nested structures:
`typescript
decode(untrustedData, {
maxDepth: 100 // Maximum nesting depth
});
`$3
`typescript
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.
$3
`typescript
import {
BencodeError, // Base class
BencodeDecodeError, // Decode errors (includes position)
BencodeEncodeError, // Encode errors (includes path)
BencodeErrorCode
} from 'bencodec';
`$3
| 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 |$3
`typescript
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;
}
}
}
`$3
`typescript
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 |
$3
`html
`$3
`typescript
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:
`typescript
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`) |