npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

bencodec

v4.1.0

Published

Universal library for decoding and encoding bencode data

Downloads

245

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
});

Recommended Settings for Untrusted Data

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