https://github.com/isolomak/bencodec
Library for decoding and encoding bencoded data.
https://github.com/isolomak/bencodec
bencode bittorrent codec create decode encode generate parse read torrent
Last synced: 17 days ago
JSON representation
Library for decoding and encoding bencoded data.
- Host: GitHub
- URL: https://github.com/isolomak/bencodec
- Owner: isolomak
- License: mit
- Created: 2018-10-12T20:16:31.000Z (about 7 years ago)
- Default Branch: master
- Last Pushed: 2025-06-13T12:46:53.000Z (7 months ago)
- Last Synced: 2025-09-17T21:59:47.520Z (4 months ago)
- Topics: bencode, bittorrent, codec, create, decode, encode, generate, parse, read, torrent
- Language: TypeScript
- Homepage:
- Size: 402 KB
- Stars: 9
- Watchers: 1
- Forks: 3
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE.md
- Code of conduct: CODE_OF_CONDUCT.md
Awesome Lists containing this project
README
# bencodec

[](https://coveralls.io/github/isolomak/bencodec)
[](https://www.npmjs.com/package/bencodec)
[](https://www.npmjs.com/package/bencodec)
[](LICENSE.md)


**A fast, secure, zero-dependency bencode encoder/decoder for modern JavaScript runtimes.**
Universal TypeScript library compliant with the [BitTorrent bencoding specification](https://wiki.theory.org/index.php/BitTorrentSpecification#Bencoding). 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
```bash
# npm
npm install bencodec
# yarn
yarn add bencodec
# pnpm
pnpm add bencodec
# bun
bun add bencodec
```
## Quick Start
```typescript
import { encode, decode } from 'bencodec';
// Encode JavaScript values to bencode
const encoded = encode({ announce: 'http://tracker.example.com', info: { name: 'file.txt' } });
// Decode bencode data
const decoded = decode(encoded, { stringify: true });
// { announce: 'http://tracker.example.com', info: { name: 'file.txt' } }
```
## Usage
### Decoding
```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 });
```
### Encoding
```typescript
import { encode } from 'bencodec';
// Encode integers
encode(42); // Uint8Array for 'i42e'
// Encode strings
encode('hello'); // Uint8Array for '5:hello'
// Encode lists
encode([1, 2, 3]); // Uint8Array for 'li1ei2ei3ee'
// Encode dictionaries (keys auto-sorted per spec)
encode({ z: 1, a: 2 }); // Uint8Array for 'd1:ai2e1:zi1ee'
// Get result as string
encode({ foo: 'bar' }, { stringify: true }); // 'd3:foo3:bare'
// Encode binary data
encode(new Uint8Array([0x00, 0xff]));
```
### Default Export
```typescript
import bencodec from 'bencodec';
bencodec.encode({ foo: 42 });
bencodec.decode('d3:fooi42ee');
```
### Options
```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;
}
```
## 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.
### Memory Exhaustion Protection
Prevent memory exhaustion from maliciously large strings:
```typescript
decode(untrustedData, {
maxStringLength: 10 * 1024 * 1024 // 10 MB limit
});
```
### Stack Overflow Protection
Prevent stack overflow from deeply nested structures:
```typescript
decode(untrustedData, {
maxDepth: 100 // Maximum nesting depth
});
```
### Recommended Settings for Untrusted Data
```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.
### Error Classes
```typescript
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
```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;
}
}
}
```
### Encode Error Example
```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 |
### Browser Usage
```html
import { encode, decode } from 'https://esm.sh/bencodec';
const encoded = encode({ hello: 'world' });
console.log(decode(encoded, { stringify: true }));
```
### Deno Usage
```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,
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](LICENSE.md)