{"id":34830107,"url":"https://github.com/isolomak/bencodec","last_synced_at":"2025-12-25T15:48:25.159Z","repository":{"id":33133192,"uuid":"152803174","full_name":"isolomak/bencodec","owner":"isolomak","description":"Library for decoding and encoding bencoded data.","archived":false,"fork":false,"pushed_at":"2025-06-13T12:46:53.000Z","size":412,"stargazers_count":9,"open_issues_count":0,"forks_count":3,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-09-17T21:59:47.520Z","etag":null,"topics":["bencode","bittorrent","codec","create","decode","encode","generate","parse","read","torrent"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/isolomak.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2018-10-12T20:16:31.000Z","updated_at":"2025-06-13T12:42:02.000Z","dependencies_parsed_at":"2024-06-21T14:05:36.475Z","dependency_job_id":"403f33b6-736b-408d-8e50-54cb44237269","html_url":"https://github.com/isolomak/bencodec","commit_stats":{"total_commits":108,"total_committers":3,"mean_commits":36.0,"dds":"0.11111111111111116","last_synced_commit":"bd0ab57131bce2f2259e77f5866c9a6d663489c6"},"previous_names":["ivansolomakhin/bencodec"],"tags_count":18,"template":false,"template_full_name":null,"purl":"pkg:github/isolomak/bencodec","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/isolomak%2Fbencodec","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/isolomak%2Fbencodec/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/isolomak%2Fbencodec/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/isolomak%2Fbencodec/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/isolomak","download_url":"https://codeload.github.com/isolomak/bencodec/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/isolomak%2Fbencodec/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28031040,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-12-25T02:00:05.988Z","response_time":58,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["bencode","bittorrent","codec","create","decode","encode","generate","parse","read","torrent"],"created_at":"2025-12-25T15:48:24.520Z","updated_at":"2025-12-25T15:48:25.150Z","avatar_url":"https://github.com/isolomak.png","language":"TypeScript","readme":"# bencodec\n\n![CI](https://github.com/isolomak/bencodec/workflows/ci/badge.svg)\n[![Coverage Status](https://coveralls.io/repos/github/isolomak/bencodec/badge.svg)](https://coveralls.io/github/isolomak/bencodec)\n[![npm version](https://img.shields.io/npm/v/bencodec.svg)](https://www.npmjs.com/package/bencodec)\n[![npm downloads](https://img.shields.io/npm/dm/bencodec.svg)](https://www.npmjs.com/package/bencodec)\n[![License: MIT](https://img.shields.io/npm/l/bencodec)](LICENSE.md)\n![TypeScript](https://img.shields.io/badge/TypeScript-5.0+-blue)\n![Zero Dependencies](https://img.shields.io/badge/dependencies-0-brightgreen)\n\n**A fast, secure, zero-dependency bencode encoder/decoder for modern JavaScript runtimes.**\n\nUniversal TypeScript library compliant with the [BitTorrent bencoding specification](https://wiki.theory.org/index.php/BitTorrentSpecification#Bencoding). Works in Node.js, browsers, Deno, and Bun.\n\n## Highlights\n\n- **Zero Dependencies** - No external packages, minimal attack surface\n- **Universal** - Works in Node.js, browsers, Deno, and Bun\n- **TypeScript First** - Full type definitions with generics support\n- **Security Built-in** - DoS protection with configurable limits\n- **BitTorrent Compliant** - Strict mode for spec validation\n- **Modern API** - Uses `Uint8Array` (not Node.js Buffer)\n- **Dual Package** - ESM and CommonJS exports\n- **100% Tested** - Complete code coverage\n\n## Installation\n\n```bash\n# npm\nnpm install bencodec\n\n# yarn\nyarn add bencodec\n\n# pnpm\npnpm add bencodec\n\n# bun\nbun add bencodec\n```\n\n## Quick Start\n\n```typescript\nimport { encode, decode } from 'bencodec';\n\n// Encode JavaScript values to bencode\nconst encoded = encode({ announce: 'http://tracker.example.com', info: { name: 'file.txt' } });\n\n// Decode bencode data\nconst decoded = decode(encoded, { stringify: true });\n// { announce: 'http://tracker.example.com', info: { name: 'file.txt' } }\n```\n\n## Usage\n\n### Decoding\n\n```typescript\nimport { decode } from 'bencodec';\n\n// Decode integers\ndecode('i42e');  // 42\n\n// Decode strings (returns Uint8Array by default)\ndecode('5:hello');  // Uint8Array [0x68, 0x65, 0x6c, 0x6c, 0x6f]\n\n// Decode strings as JavaScript strings\ndecode('5:hello', { stringify: true });  // 'hello'\n\n// Decode lists\ndecode('li1ei2ei3ee', { stringify: true });  // [1, 2, 3]\n\n// Decode dictionaries\ndecode('d3:fooi42e3:bar4:spame', { stringify: true });  // { bar: 'spam', foo: 42 }\n\n// Type the result with generics\ninterface Torrent {\n  announce: string;\n  info: { name: string };\n}\nconst torrent = decode\u003cTorrent\u003e(buffer, { stringify: true });\n```\n\n### Encoding\n\n```typescript\nimport { encode } from 'bencodec';\n\n// Encode integers\nencode(42);  // Uint8Array for 'i42e'\n\n// Encode strings\nencode('hello');  // Uint8Array for '5:hello'\n\n// Encode lists\nencode([1, 2, 3]);  // Uint8Array for 'li1ei2ei3ee'\n\n// Encode dictionaries (keys auto-sorted per spec)\nencode({ z: 1, a: 2 });  // Uint8Array for 'd1:ai2e1:zi1ee'\n\n// Get result as string\nencode({ foo: 'bar' }, { stringify: true });  // 'd3:foo3:bare'\n\n// Encode binary data\nencode(new Uint8Array([0x00, 0xff]));\n```\n\n### Default Export\n\n```typescript\nimport bencodec from 'bencodec';\n\nbencodec.encode({ foo: 42 });\nbencodec.decode('d3:fooi42ee');\n```\n\n### Options\n\n```typescript\ninterface IBencodecOptions {\n  /** Return strings instead of Uint8Array (default: false) */\n  stringify?: boolean;\n\n  /** Enable strict BitTorrent spec validation (default: false) */\n  strict?: boolean;\n\n  /** Character encoding: 'utf8' | 'latin1' | 'ascii' | 'binary' (default: 'utf8') */\n  encoding?: ByteEncoding;\n\n  /** Maximum string length in bytes - security limit */\n  maxStringLength?: number;\n\n  /** Maximum nesting depth - security limit */\n  maxDepth?: number;\n}\n```\n\n## Strict Mode\n\nEnable strict mode for BitTorrent specification compliance:\n\n```typescript\n// Enforces sorted dictionary keys\ndecode('d1:bi1e1:ai2ee', { strict: true });\n// Throws: UNSORTED_KEYS\n\n// Rejects trailing data\ndecode('i42eextra', { strict: true });\n// Throws: TRAILING_DATA\n```\n\n## Security\n\nBencodec includes built-in protections against denial-of-service attacks when parsing untrusted data.\n\n### Memory Exhaustion Protection\n\nPrevent memory exhaustion from maliciously large strings:\n\n```typescript\ndecode(untrustedData, {\n  maxStringLength: 10 * 1024 * 1024  // 10 MB limit\n});\n```\n\n### Stack Overflow Protection\n\nPrevent stack overflow from deeply nested structures:\n\n```typescript\ndecode(untrustedData, {\n  maxDepth: 100  // Maximum nesting depth\n});\n```\n\n### Recommended Settings for Untrusted Data\n\n```typescript\nconst SAFE_OPTIONS = {\n  maxStringLength: 10 * 1024 * 1024,  // 10 MB\n  maxDepth: 100,\n  strict: true\n};\n\ndecode(untrustedData, SAFE_OPTIONS);\n```\n\n## Error Handling\n\nBencodec provides structured error classes with specific error codes for programmatic handling.\n\n### Error Classes\n\n```typescript\nimport {\n  BencodeError,        // Base class\n  BencodeDecodeError,  // Decode errors (includes position)\n  BencodeEncodeError,  // Encode errors (includes path)\n  BencodeErrorCode\n} from 'bencodec';\n```\n\n### Error Codes\n\n| Code | Description |\n|------|-------------|\n| `EMPTY_INPUT` | Input data is empty or falsy |\n| `UNEXPECTED_END` | Data ends unexpectedly |\n| `INVALID_FORMAT` | Invalid bencode format |\n| `LEADING_ZEROS` | Integer has leading zeros (e.g., `i03e`) |\n| `NEGATIVE_ZERO` | Negative zero (`i-0e`) is not allowed |\n| `UNSORTED_KEYS` | Dictionary keys not sorted (strict mode) |\n| `TRAILING_DATA` | Extra data after valid bencode (strict mode) |\n| `MAX_DEPTH_EXCEEDED` | Nesting depth exceeds limit |\n| `MAX_SIZE_EXCEEDED` | String length exceeds limit |\n| `UNSUPPORTED_TYPE` | Attempted to encode unsupported type |\n| `CIRCULAR_REFERENCE` | Circular reference detected |\n\n### Decode Error Example\n\n```typescript\nimport { decode, BencodeDecodeError, BencodeErrorCode } from 'bencodec';\n\ntry {\n  decode(untrustedData, { strict: true, maxDepth: 50 });\n} catch (error) {\n  if (error instanceof BencodeDecodeError) {\n    switch (error.code) {\n      case BencodeErrorCode.MAX_DEPTH_EXCEEDED:\n        console.error(`Too deeply nested at position ${error.position}`);\n        break;\n      case BencodeErrorCode.INVALID_FORMAT:\n        console.error(`Malformed data at position ${error.position}`);\n        break;\n    }\n  }\n}\n```\n\n### Encode Error Example\n\n```typescript\nimport { encode, BencodeEncodeError } from 'bencodec';\n\ntry {\n  const circular: any = { a: 1 };\n  circular.self = circular;\n  encode(circular);\n} catch (error) {\n  if (error instanceof BencodeEncodeError) {\n    console.error(`Error at path: ${error.path?.join('.')}`);\n    // Output: Error at path: self\n  }\n}\n```\n\n## Platform Support\n\n| Platform | Version | Notes |\n|----------|---------|-------|\n| Node.js | 18+ | Full support |\n| Browsers | Modern | Chrome, Firefox, Safari, Edge |\n| Deno | 1.0+ | Full support |\n| Bun | 1.0+ | Full support |\n\n### Browser Usage\n\n```html\n\u003cscript type=\"module\"\u003e\n  import { encode, decode } from 'https://esm.sh/bencodec';\n\n  const encoded = encode({ hello: 'world' });\n  console.log(decode(encoded, { stringify: true }));\n\u003c/script\u003e\n```\n\n### Deno Usage\n\n```typescript\nimport { encode, decode } from 'npm:bencodec';\n\nconst encoded = encode({ hello: 'world' });\nconsole.log(decode(encoded, { stringify: true }));\n```\n\n## Type Definitions\n\nFull TypeScript support with exported types:\n\n```typescript\nimport type {\n  IBencodecOptions,\n  BencodeDecodedValue,\n  BencodeEncodableValue,\n  ByteEncoding\n} from 'bencodec';\n```\n\n## Non-Standard Behaviors\n\nFor maximum compatibility, bencodec handles some edge cases beyond the strict spec:\n\n| Behavior | Description |\n|----------|-------------|\n| Plus sign in integers | Leading `+` is silently ignored (`i+42e` -\u003e `42`) |\n| Float truncation | Decimal numbers truncated toward zero |\n| Boolean encoding | Booleans encoded as integers (`true` -\u003e `i1e`) |\n| Null/undefined | Silently skipped in lists and dictionaries |\n\n## License\n\n[MIT](LICENSE.md)\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fisolomak%2Fbencodec","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fisolomak%2Fbencodec","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fisolomak%2Fbencodec/lists"}