An open API service indexing awesome lists of open source software.

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.

Awesome Lists containing this project

README

          

# bencodec

![CI](https://github.com/isolomak/bencodec/workflows/ci/badge.svg)
[![Coverage Status](https://coveralls.io/repos/github/isolomak/bencodec/badge.svg)](https://coveralls.io/github/isolomak/bencodec)
[![npm version](https://img.shields.io/npm/v/bencodec.svg)](https://www.npmjs.com/package/bencodec)
[![npm downloads](https://img.shields.io/npm/dm/bencodec.svg)](https://www.npmjs.com/package/bencodec)
[![License: MIT](https://img.shields.io/npm/l/bencodec)](LICENSE.md)
![TypeScript](https://img.shields.io/badge/TypeScript-5.0+-blue)
![Zero Dependencies](https://img.shields.io/badge/dependencies-0-brightgreen)

**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)