UNPKG

bencodec

Version:

Universal library for decoding and encoding bencode data

813 lines (806 loc) 28 kB
"use strict"; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // src/index.ts var index_exports = {}; __export(index_exports, { BencodeDecodeError: () => BencodeDecodeError, BencodeEncodeError: () => BencodeEncodeError, BencodeError: () => BencodeError, BencodeErrorCode: () => BencodeErrorCode, bencodec: () => bencodec, decode: () => decode, default: () => index_default, encode: () => encode, encodeToBytes: () => encodeToBytes, encodeToString: () => encodeToString }); module.exports = __toCommonJS(index_exports); // src/errors.ts var BencodeErrorCode = /* @__PURE__ */ ((BencodeErrorCode2) => { BencodeErrorCode2["EMPTY_INPUT"] = "EMPTY_INPUT"; BencodeErrorCode2["UNEXPECTED_END"] = "UNEXPECTED_END"; BencodeErrorCode2["INVALID_FORMAT"] = "INVALID_FORMAT"; BencodeErrorCode2["LEADING_ZEROS"] = "LEADING_ZEROS"; BencodeErrorCode2["NEGATIVE_ZERO"] = "NEGATIVE_ZERO"; BencodeErrorCode2["UNSORTED_KEYS"] = "UNSORTED_KEYS"; BencodeErrorCode2["TRAILING_DATA"] = "TRAILING_DATA"; BencodeErrorCode2["MAX_DEPTH_EXCEEDED"] = "MAX_DEPTH_EXCEEDED"; BencodeErrorCode2["MAX_SIZE_EXCEEDED"] = "MAX_SIZE_EXCEEDED"; BencodeErrorCode2["UNSUPPORTED_TYPE"] = "UNSUPPORTED_TYPE"; BencodeErrorCode2["CIRCULAR_REFERENCE"] = "CIRCULAR_REFERENCE"; return BencodeErrorCode2; })(BencodeErrorCode || {}); var BencodeError = class extends Error { /** * @param code - The error code identifying the error type * @param message - Human-readable error message */ constructor(code, message) { super(message); this.code = code; this.name = this.constructor.name; Error.captureStackTrace?.(this, this.constructor); } }; var BencodeDecodeError = class extends BencodeError { /** * @param code - The error code identifying the error type * @param message - Human-readable error message * @param position - The byte position where the error occurred (optional) */ constructor(code, message, position) { super(code, message); this.position = position; } }; var BencodeEncodeError = class extends BencodeError { /** * @param code - The error code identifying the error type * @param message - Human-readable error message * @param path - The path to the problematic value (optional) */ constructor(code, message, path) { super(code, message); this.path = path; } }; // src/bytes.ts var _Bytes = class _Bytes { /** * Convert a string to Uint8Array using the specified encoding. * * @param str - The string to convert * @param encoding - Character encoding (default: 'utf8') * @returns Uint8Array containing the encoded bytes * * @example * ```typescript * Bytes.fromString('hello'); // UTF-8 encoded * Bytes.fromString('hello', 'latin1'); // Latin-1 encoded * ``` */ static fromString(str, encoding = "utf8") { if (encoding === "utf8" || encoding === "utf-8") { return _Bytes._encoder.encode(str); } const bytes = new Uint8Array(str.length); for (let i = 0; i < str.length; i++) { bytes[i] = str.charCodeAt(i) & 255; } return bytes; } /** * Convert Uint8Array to string using the specified encoding. * * @param bytes - The byte array to convert * @param encoding - Character encoding (default: 'utf8') * @returns The decoded string * * @example * ```typescript * Bytes.toString(bytes); // UTF-8 decoded * Bytes.toString(bytes, 'latin1'); // Latin-1 decoded * ``` */ static toString(bytes, encoding = "utf8") { if (encoding === "utf8" || encoding === "utf-8") { return _Bytes._decoder.decode(bytes); } let result = ""; for (let i = 0; i < bytes.length; i++) { result += String.fromCharCode(bytes[i]); } return result; } /** * Concatenate multiple Uint8Arrays into a single Uint8Array. * * @param arrays - Array of Uint8Arrays to concatenate * @returns A new Uint8Array containing all bytes * * @example * ```typescript * const combined = Bytes.concat([bytes1, bytes2, bytes3]); * ``` */ static concat(arrays) { let totalLength = 0; for (const arr of arrays) { totalLength += arr.length; } const result = new Uint8Array(totalLength); let offset = 0; for (const arr of arrays) { result.set(arr, offset); offset += arr.length; } return result; } /** * Compare two Uint8Arrays lexicographically. * * @param a - First byte array * @param b - Second byte array * @returns -1 if a < b, 0 if a === b, 1 if a > b * * @example * ```typescript * Bytes.compare(a, b); // -1, 0, or 1 * ``` */ static compare(a, b) { const minLength = Math.min(a.length, b.length); for (let i = 0; i < minLength; i++) { if (a[i] < b[i]) { return -1; } if (a[i] > b[i]) { return 1; } } if (a.length < b.length) { return -1; } if (a.length > b.length) { return 1; } return 0; } /** * Calculate the byte length of a string when encoded as UTF-8. * * @param str - The string to measure * @returns The byte length in UTF-8 encoding * * @example * ```typescript * Bytes.byteLength('hello'); // 5 * Bytes.byteLength(''); // 6 (2 bytes per character) * ``` */ static byteLength(str) { return _Bytes._encoder.encode(str).length; } /** * Type guard to check if a value is a Uint8Array or Buffer. * * @param value - The value to check * @returns true if the value is a Uint8Array (or Buffer in Node.js) * * @example * ```typescript * if (Bytes.isBytes(data)) { * // data is Uint8Array * } * ``` */ static isBytes(value) { return value instanceof Uint8Array; } }; _Bytes._encoder = new TextEncoder(); _Bytes._decoder = new TextDecoder(); var Bytes = _Bytes; // src/BencodeDecoder.ts var BencodeDecoder = class _BencodeDecoder { /** * Checks if a byte value represents an ASCII digit (0-9). * * @param char - The byte value to check * @returns `true` if the byte is an ASCII digit (0x30-0x39) */ static _isInteger(char) { return char >= 48 && char <= 57; } /** * Creates a new BencodeDecoder instance. * * @param data - The bencode data to decode. Strings are converted to Uint8Array internally. * @param options - Configuration options for decoding behavior. * * @throws {BencodeDecodeError} With code `EMPTY_INPUT` if data is empty or falsy. * * @example * ```typescript * // From string * const decoder = new BencodeDecoder('i42e'); * * // From Uint8Array * const decoder = new BencodeDecoder(new Uint8Array([0x69, 0x34, 0x32, 0x65])); * * // With options * const decoder = new BencodeDecoder(data, { stringify: true, strict: true }); * ``` */ constructor(data, options) { if (!data) { throw new BencodeDecodeError("EMPTY_INPUT" /* EMPTY_INPUT */, "Nothing to decode"); } this._index = 0; this._currentDepth = 0; this._options = options || {}; this._buffer = typeof data === "string" ? Bytes.fromString(data) : data; } /** * Checks if there is remaining data in the buffer after decoding. * * Useful for detecting trailing garbage data, which may indicate malformed input * or concatenated bencode values. * * @returns `true` if the current position is before the end of the buffer. * * @example * ```typescript * const decoder = new BencodeDecoder('i42eextra'); * decoder.decode(); // 42 * decoder.hasRemainingData(); // true (5 bytes remaining: "extra") * ``` */ hasRemainingData() { return this._index < this._buffer.length; } /** * Gets the current byte position in the buffer. * * Useful for error reporting, debugging, or implementing custom parsing logic. * * @returns The zero-based byte offset of the current position. * * @example * ```typescript * const decoder = new BencodeDecoder('i42e5:hello'); * decoder.decode(); // 42 * decoder.getCurrentPosition(); // 4 (after 'i42e') * decoder.decode(); // <Buffer 68 65 6c 6c 6f> * decoder.getCurrentPosition(); // 11 (end of buffer) * ``` */ getCurrentPosition() { return this._index; } /** * Decodes the next bencode value from the buffer. * * Advances the internal position pointer past the decoded value. * Can be called multiple times to decode concatenated bencode values. * * @returns The decoded JavaScript value: * - Bencode integers → `number` * - Bencode strings → `Uint8Array` (default) or `string` (if `stringify: true`) * - Bencode lists → `BencodeDecodedList` * - Bencode dictionaries → `BencodeDecodedDictionary` * * @throws {BencodeDecodeError} With code `UNEXPECTED_END` if the buffer ends unexpectedly. * @throws {BencodeDecodeError} With code `INVALID_FORMAT` if an invalid type marker is found. * @throws {BencodeDecodeError} With code `LEADING_ZEROS` if an integer has leading zeros. * @throws {BencodeDecodeError} With code `NEGATIVE_ZERO` if negative zero is encountered. * @throws {BencodeDecodeError} With code `UNSORTED_KEYS` if `strict: true` and dictionary * keys are not in lexicographic order. * @throws {BencodeDecodeError} With code `MAX_SIZE_EXCEEDED` if a string exceeds `maxStringLength`. * @throws {BencodeDecodeError} With code `MAX_DEPTH_EXCEEDED` if nesting exceeds `maxDepth`. * * @example * ```typescript * const decoder = new BencodeDecoder('i42e'); * const value = decoder.decode(); // 42 * * // Decode multiple values * const decoder = new BencodeDecoder('i1ei2ei3e'); * while (!decoder.hasRemainingData() === false) { * console.log(decoder.decode()); // 1, 2, 3 * } * ``` */ decode() { if (this._isEOF()) { throw this._decodeError("UNEXPECTED_END" /* UNEXPECTED_END */, "Unexpected end of data"); } if (_BencodeDecoder._isInteger(this._currentChar())) { return this._decodeString(); } if (this._currentChar() === 105 /* INTEGER */) { return this._decodeInteger(); } if (this._currentChar() === 108 /* LIST */) { return this._decodeList(); } if (this._currentChar() === 100 /* DICTIONARY */) { return this._decodeDictionary(); } throw this._decodeError("INVALID_FORMAT" /* INVALID_FORMAT */, "Invalid bencode data"); } /** * Gets the byte at the current buffer position without advancing. * * @returns The byte value at the current position. */ _currentChar() { return this._buffer[this._index]; } /** * Checks if the current position has reached or exceeded the buffer length. * * @returns `true` if at or past end of buffer. */ _isEOF() { return this._index >= this._buffer.length; } /** * Formats a byte value for display in error messages. * * Printable ASCII characters (0x20-0x7e) are shown as quoted characters. * Non-printable bytes are shown as hex values. * * @param char - The byte value to format. * @returns A human-readable string representation. */ static _formatChar(char) { if (char === void 0 || char === null) { return "undefined"; } if (char >= 32 && char <= 126) { return `'${String.fromCharCode(char)}'`; } return `0x${char.toString(16).padStart(2, "0")}`; } /** * Creates a decode error with position context. * * Appends the current buffer position and (if not at EOF) the current character * to the error message for debugging. * * @param code - The error code identifying the error type. * @param message - The base error message. * @returns A BencodeDecodeError with position information. */ _decodeError(code, message) { let fullMessage; if (this._isEOF()) { fullMessage = `${message} at position ${this._index}`; } else { fullMessage = `${message} at position ${this._index} (found ${_BencodeDecoder._formatChar(this._currentChar())})`; } return new BencodeDecodeError(code, fullMessage, this._index); } /** * Gets the byte at the current position and advances to the next position. * * @returns The byte value at the current position before advancing. */ _next() { return this._buffer[this._index++]; } /** * Decodes a bencode string value. * * Bencode strings are formatted as `<length>:<content>` where length is a * non-negative integer. The content is returned as a Uint8Array by default, * or as a string if the `stringify` option is enabled. * * @returns The decoded string as a Uint8Array or string. * @throws {BencodeDecodeError} With code `MAX_SIZE_EXCEEDED` if length exceeds `maxStringLength`. * @throws {BencodeDecodeError} With code `UNEXPECTED_END` if buffer doesn't contain enough bytes. */ _decodeString() { const length = this._decodeInteger(); if (this._options.maxStringLength && length > this._options.maxStringLength) { throw new BencodeDecodeError("MAX_SIZE_EXCEEDED" /* MAX_SIZE_EXCEEDED */, `String length ${length} exceeds maximum ${this._options.maxStringLength}`, this._index); } if (this._index + length > this._buffer.length) { throw this._decodeError("UNEXPECTED_END" /* UNEXPECTED_END */, `Unexpected end of data: expected ${length} bytes for string`); } const bytes = new Uint8Array(length); for (let i = 0; i < length; i++) { bytes[i] = this._next(); } return this._options.stringify ? Bytes.toString(bytes, this._options.encoding || "utf8") : bytes; } /** * Decodes a bencode integer value or string length prefix. * * Bencode integers are formatted as `i<number>e` (e.g., `i42e`, `i-17e`). * String length prefixes are just digits followed by `:` (e.g., `5:`). * * **Non-standard extensions:** * - A leading `+` sign is silently ignored (`i+42e` → `42`) * - Decimal points cause the fractional part to be discarded (`i3.14e` → `3`) * * @returns The decoded integer value. * @throws {BencodeDecodeError} With code `LEADING_ZEROS` if the integer has leading zeros. * @throws {BencodeDecodeError} With code `NEGATIVE_ZERO` if negative zero is encountered. * @throws {BencodeDecodeError} With code `UNEXPECTED_END` if the terminating `e` is missing. */ _decodeInteger() { let sign = 1; let isFloat = false; let integer = 0; let isBencodeInteger = false; if (this._currentChar() === 105 /* INTEGER */) { this._index++; isBencodeInteger = true; } if (this._currentChar() === 43 /* PLUS */) { this._index++; } if (this._currentChar() === 45 /* MINUS */) { this._index++; sign = -1; } if (isBencodeInteger && this._currentChar() === 48 && _BencodeDecoder._isInteger(this._buffer[this._index + 1])) { throw this._decodeError("LEADING_ZEROS" /* LEADING_ZEROS */, "Invalid bencode: leading zeros are not allowed"); } while (_BencodeDecoder._isInteger(this._currentChar()) || this._currentChar() === 46 /* DOT */) { if (this._currentChar() === 46 /* DOT */) { isFloat = true; } isFloat === false ? integer = integer * 10 + (this._next() - 48) : this._index++; } if (isBencodeInteger) { if (this._isEOF() || this._currentChar() !== 101 /* END */) { throw this._decodeError("UNEXPECTED_END" /* UNEXPECTED_END */, "Unexpected end of data: expected 'e' to terminate integer"); } this._index++; } else if (this._currentChar() === 58 /* STR_DELIMITER */) { this._index++; } if (sign === -1 && integer === 0) { throw this._decodeError("NEGATIVE_ZERO" /* NEGATIVE_ZERO */, "Invalid bencode: negative zero is not allowed"); } return integer * sign; } /** * Decodes a bencode list value. * * Bencode lists are formatted as `l<items>e` where items are any valid * bencode values. Lists can be nested and contain mixed types. * * @returns The decoded list as a JavaScript array. * @throws {BencodeDecodeError} With code `MAX_DEPTH_EXCEEDED` if nesting exceeds `maxDepth`. * @throws {BencodeDecodeError} With code `UNEXPECTED_END` if the terminating `e` is missing. */ _decodeList() { this._currentDepth++; if (this._options.maxDepth && this._currentDepth > this._options.maxDepth) { throw new BencodeDecodeError("MAX_DEPTH_EXCEEDED" /* MAX_DEPTH_EXCEEDED */, `Nesting depth ${this._currentDepth} exceeds maximum ${this._options.maxDepth}`, this._index); } const acc = []; this._next(); while (!this._isEOF() && this._currentChar() !== 101 /* END */) { acc.push(this.decode()); } if (this._isEOF()) { this._currentDepth--; throw this._decodeError("UNEXPECTED_END" /* UNEXPECTED_END */, "Unexpected end of data: expected 'e' to terminate list"); } this._next(); this._currentDepth--; return acc; } /** * Decodes a bencode dictionary value. * * Bencode dictionaries are formatted as `d<key><value>...e` where keys are * bencode strings and values are any valid bencode values. According to the * specification, keys must be in sorted lexicographic order, but this is only * enforced when `strict: true`. * * @returns The decoded dictionary as a JavaScript object. * @throws {BencodeDecodeError} With code `MAX_DEPTH_EXCEEDED` if nesting exceeds `maxDepth`. * @throws {BencodeDecodeError} With code `UNSORTED_KEYS` if `strict: true` and keys are not sorted. * @throws {BencodeDecodeError} With code `UNEXPECTED_END` if the terminating `e` is missing. */ _decodeDictionary() { this._currentDepth++; if (this._options.maxDepth && this._currentDepth > this._options.maxDepth) { throw new BencodeDecodeError("MAX_DEPTH_EXCEEDED" /* MAX_DEPTH_EXCEEDED */, `Nesting depth ${this._currentDepth} exceeds maximum ${this._options.maxDepth}`, this._index); } const acc = {}; let prevKey = null; this._next(); while (!this._isEOF() && this._currentChar() !== 101 /* END */) { const key = this._decodeString(); const keyBytes = Bytes.isBytes(key) ? key : Bytes.fromString(key); if (this._options.strict && prevKey !== null && Bytes.compare(prevKey, keyBytes) >= 0) { const keyStr = typeof key === "string" ? key : Bytes.toString(key); const prevKeyStr = Bytes.toString(prevKey); throw this._decodeError("UNSORTED_KEYS" /* UNSORTED_KEYS */, `Invalid bencode: dictionary keys must be in sorted order (key '${keyStr}' after '${prevKeyStr}')`); } prevKey = keyBytes; acc[typeof key === "string" ? key : Bytes.toString(key)] = this.decode(); } if (this._isEOF()) { this._currentDepth--; throw this._decodeError("UNEXPECTED_END" /* UNEXPECTED_END */, "Unexpected end of data: expected 'e' to terminate dictionary"); } this._next(); this._currentDepth--; return acc; } }; // src/BencodeEncoder.ts var BencodeEncoder = class { /** * Creates a new BencodeEncoder instance. * * @param options - Configuration options for encoding behavior. * * @example * ```typescript * // Default options (returns Uint8Array) * const encoder = new BencodeEncoder(); * * // Return string instead of Uint8Array * const encoder = new BencodeEncoder({ stringify: true }); * ``` */ constructor(options) { /** Bencode integer start marker: 'i' */ this._integerIdentifier = new Uint8Array([105 /* INTEGER */]); /** Bencode string delimiter: ':' */ this._stringDelimiterIdentifier = new Uint8Array([58 /* STR_DELIMITER */]); /** Bencode list start marker: 'l' */ this._listIdentifier = new Uint8Array([108 /* LIST */]); /** Bencode dictionary start marker: 'd' */ this._dictionaryIdentifier = new Uint8Array([100 /* DICTIONARY */]); /** Bencode end marker: 'e' */ this._endIdentifier = new Uint8Array([101 /* END */]); this._buffer = []; this._options = options || {}; this._visited = /* @__PURE__ */ new WeakSet(); this._path = []; } /** * Encodes a JavaScript value to bencode format. * * @param data - The value to encode. See {@link BencodeEncodableValue} for supported types. * * @returns The bencode-encoded data as a Uint8Array (default) or string (if `stringify: true`). * * @throws {BencodeEncodeError} With code `UNSUPPORTED_TYPE` if the value contains an * unsupported type (e.g., functions, symbols, BigInt). * @throws {BencodeEncodeError} With code `CIRCULAR_REFERENCE` if the data contains * circular references. * * @example * ```typescript * const encoder = new BencodeEncoder(); * * encoder.encode(42); // Uint8Array [0x69, 0x34, 0x32, 0x65] ('i42e') * encoder.encode('hello'); // Uint8Array [0x35, 0x3a, 0x68, 0x65, 0x6c, 0x6c, 0x6f] ('5:hello') * encoder.encode([1, 2]); // Uint8Array [...] ('li1ei2ee') * encoder.encode({ a: 1 }); // Uint8Array [...] ('d1:ai1ee') * ``` */ encode(data) { this._encodeType(data); return this._options.stringify ? Bytes.toString(Bytes.concat(this._buffer)) : Bytes.concat(this._buffer); } /** * Routes encoding to the appropriate type-specific method. * * Determines the JavaScript type of the input and calls the corresponding * encoder method. Type detection order matters for correct handling of * Uint8Array vs ArrayBufferView vs generic object. * * @param data - The value to encode. * @throws {BencodeEncodeError} With code `UNSUPPORTED_TYPE` for unsupported types. */ _encodeType(data) { if (Bytes.isBytes(data)) { return this._encodeBytes(data); } if (Array.isArray(data)) { return this._encodeList(data); } if (ArrayBuffer.isView(data)) { return this._encodeBytes(new Uint8Array(data.buffer, data.byteOffset, data.byteLength)); } if (data instanceof ArrayBuffer) { return this._encodeBytes(new Uint8Array(data)); } if (typeof data === "boolean") { return this._encodeInteger(data ? 1 : 0); } if (typeof data === "number") { return this._encodeInteger(data); } if (typeof data === "string") { return this._encodeString(data); } if (typeof data === "object") { return this._encodeDictionary(data); } throw new BencodeEncodeError( "UNSUPPORTED_TYPE" /* UNSUPPORTED_TYPE */, `${typeof data} is unsupported type.`, [...this._path] ); } /** * Encodes a Uint8Array as a bencode string. * * Bencode strings are formatted as `<length>:<content>` where length is the * byte length of the content. * * @param data - The Uint8Array to encode. */ _encodeBytes(data) { this._buffer.push( Bytes.fromString(String(data.length)), this._stringDelimiterIdentifier, data ); } /** * Encodes a JavaScript string as a bencode string. * * The string is converted to UTF-8 bytes and encoded as `<byte_length>:<utf8_bytes>`. * Note that the length prefix is the byte length, not the character count. * * @param data - The string to encode. */ _encodeString(data) { const encoded = Bytes.fromString(data); this._buffer.push( Bytes.fromString(String(encoded.length)), this._stringDelimiterIdentifier, encoded ); } /** * Encodes a number as a bencode integer. * * Bencode integers are formatted as `i<number>e`. Floating-point numbers are * truncated toward zero (not rounded) before encoding. * * @param data - The number to encode. */ _encodeInteger(data) { this._buffer.push( this._integerIdentifier, Bytes.fromString(String(Math.trunc(data))), this._endIdentifier ); } /** * Encodes a JavaScript array as a bencode list. * * Bencode lists are formatted as `l<items>e`. Elements are encoded in order. * `null` and `undefined` values are silently skipped. * * Tracks visited objects to detect circular references. * * @param data - The array to encode. * @throws {BencodeEncodeError} With code `CIRCULAR_REFERENCE` if the array was already visited. */ _encodeList(data) { if (this._visited.has(data)) { throw new BencodeEncodeError( "CIRCULAR_REFERENCE" /* CIRCULAR_REFERENCE */, "Circular reference detected", [...this._path] ); } this._visited.add(data); this._buffer.push(this._listIdentifier); for (let i = 0; i < data.length; i++) { const item = data[i]; if (item === null || item === void 0) { continue; } this._path.push(i); this._encodeType(item); this._path.pop(); } this._buffer.push(this._endIdentifier); this._visited.delete(data); } /** * Encodes a JavaScript object as a bencode dictionary. * * Bencode dictionaries are formatted as `d<key><value>...e`. Keys are automatically * sorted lexicographically (by raw byte value) to comply with the bencode specification. * Properties with `null` or `undefined` values are silently skipped. * * Tracks visited objects to detect circular references. * * @param data - The object to encode. * @throws {BencodeEncodeError} With code `CIRCULAR_REFERENCE` if the object was already visited. */ _encodeDictionary(data) { if (this._visited.has(data)) { throw new BencodeEncodeError( "CIRCULAR_REFERENCE" /* CIRCULAR_REFERENCE */, "Circular reference detected", [...this._path] ); } this._visited.add(data); this._buffer.push(this._dictionaryIdentifier); const keys = Object.keys(data).sort(); for (const key of keys) { if (data[key] === null || data[key] === void 0) { continue; } this._encodeString(key); this._path.push(key); this._encodeType(data[key]); this._path.pop(); } this._buffer.push(this._endIdentifier); this._visited.delete(data); } }; // src/index.ts function decode(data, options) { const decoder = new BencodeDecoder(data, options); const result = decoder.decode(); if (options?.strict && decoder.hasRemainingData()) { throw new BencodeDecodeError("TRAILING_DATA" /* TRAILING_DATA */, "Invalid bencode: unexpected data after valid bencode"); } return result; } function encode(data, options) { const encoder = new BencodeEncoder(options); return encoder.encode(data); } function encodeToBytes(data) { const encoder = new BencodeEncoder(); return encoder.encode(data); } function encodeToString(data, options) { const encoding = options?.encoding ?? "utf8"; const encoder = new BencodeEncoder({ stringify: false }); const bytes = encoder.encode(data); return Bytes.toString(bytes, encoding); } var bencodec = { decode, encode, encodeToBytes, encodeToString }; var index_default = bencodec; // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { BencodeDecodeError, BencodeEncodeError, BencodeError, BencodeErrorCode, bencodec, decode, encode, encodeToBytes, encodeToString });