{"version":3,"sources":["jsdelivr-header.js","/npm/webtorrent@3.0.16/index.js"],"names":[],"mappings":"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","file":"/npm/webtorrent@3.0.16/index.js","sourceRoot":"","sourcesContent":["/**\n * Minified by jsDelivr using Terser v5.48.0.\n * Original file: /npm/webtorrent@3.0.16/index.js\n *\n * Do NOT use SRI with dynamically generated files! More information: https://www.jsdelivr.com/using-sri-with-dynamic-files\n */\n","/*! webtorrent. MIT License. WebTorrent LLC <https://webtorrent.io/opensource> */\nimport EventEmitter from 'events'\nimport path from 'path'\nimport Wire from 'bittorrent-protocol'\nimport createTorrent, { parseInput } from 'create-torrent'\nimport debugFactory from 'debug'\nimport { Client as DHT } from 'bittorrent-dht' // browser exclude\nimport loadIPSet from 'load-ip-set' // browser exclude\nimport parallel from 'run-parallel'\nimport parseTorrent from 'parse-torrent'\nimport Peer from '@thaunknown/simple-peer/lite.js'\nimport queueMicrotask from 'queue-microtask'\nimport { hash, hex2arr, arr2hex, arr2base, text2arr, randomBytes, concat } from 'uint8-util'\nimport throughput from 'throughput'\nimport { ThrottleGroup } from 'speed-limiter'\nimport NatAPI from '@silentbot1/nat-api' // browser exclude\nimport ConnPool from './lib/conn-pool.js' // browser exclude\nimport Torrent from './lib/torrent.js'\nimport { NodeServer, BrowserServer } from './lib/server.js'\n\nimport VERSION from './version.cjs'\n\nconst debug = debugFactory('webtorrent')\n\n/**\n * Version number in Azureus-style. Generated from major and minor semver version.\n * For example:\n *   '0.16.1' -> '0016'\n *   '1.2.5' -> '0102'\n */\nconst VERSION_STR = VERSION\n  .replace(/\\d*./g, v => `0${v % 100}`.slice(-2))\n  .slice(0, 4)\n\n/**\n * Version prefix string (used in peer ID). WebTorrent uses the Azureus-style\n * encoding: '-', two characters for client id ('WW'), four ascii digits for version\n * number, '-', followed by random numbers.\n * For example:\n *   '-WW0102-'...\n */\nconst VERSION_PREFIX = `-WW${VERSION_STR}-`\n\n/**\n * WebTorrent Client\n * @param {Object=} opts\n */\nexport default class WebTorrent extends EventEmitter {\n  constructor (opts = {}) {\n    super()\n\n    if (typeof opts.peerId === 'string') {\n      this.peerId = opts.peerId\n    } else if (ArrayBuffer.isView(opts.peerId)) {\n      this.peerId = arr2hex(opts.peerId)\n    } else {\n      this.peerId = arr2hex(text2arr(VERSION_PREFIX + arr2base(randomBytes(9))))\n    }\n    this.peerIdBuffer = hex2arr(this.peerId)\n\n    if (typeof opts.nodeId === 'string') {\n      this.nodeId = opts.nodeId\n    } else if (ArrayBuffer.isView(opts.nodeId)) {\n      this.nodeId = arr2hex(opts.nodeId)\n    } else {\n      this.nodeId = arr2hex(randomBytes(20))\n    }\n    this.nodeIdBuffer = hex2arr(this.nodeId)\n\n    this.userAgent = opts.userAgent ?? `WebTorrent/${VERSION} (https://webtorrent.io)`\n\n    this._debugId = this.peerId.substring(0, 7)\n\n    this.destroyed = false\n    this.listening = false\n    this.torrentPort = opts.torrentPort || 0\n    this.dhtPort = opts.dhtPort || 0\n    this.tracker = opts.tracker !== undefined ? opts.tracker : {}\n    this.lsd = opts.lsd !== false\n    this.utPex = opts.utPex !== false\n    this.natUpnp = opts.natUpnp ?? true\n    this.natPmp = opts.natPmp ?? true\n    this.torrents = []\n    this.maxConns = Number(opts.maxConns) || 55\n    this.utp = WebTorrent.UTP_SUPPORT && opts.utp !== false\n    this.seedOutgoingConnections = opts.seedOutgoingConnections ?? true\n\n    this._downloadLimit = Math.max((typeof opts.downloadLimit === 'number') ? opts.downloadLimit : -1, -1)\n    this._uploadLimit = Math.max((typeof opts.uploadLimit === 'number') ? opts.uploadLimit : -1, -1)\n\n    if ((this.natUpnp || this.natPmp) && typeof NatAPI === 'function') {\n      this.natTraversal = new NatAPI({\n        enableUPNP: this.natUpnp,\n        enablePMP: this.natPmp,\n        upnpPermanentFallback: opts.natUpnp === 'permanent'\n      })\n    }\n\n    this.secure = Number(opts.secure ?? 1)\n\n    if (this.secure === 2 && !Wire.nativeRC4) console.warn('Native RC4 implementation is not available. This WILL cause performance issues when using `opts.secure=2`. Consider enabling `--openssl-legacy-provider` or using `opts.secure=1` instead.')\n\n    this._debug(\n      'new webtorrent (peerId %s, nodeId %s, port %s)',\n      this.peerId, this.nodeId, this.torrentPort\n    )\n\n    this.throttleGroups = {\n      down: new ThrottleGroup({ rate: Math.max(this._downloadLimit, 0), enabled: this._downloadLimit >= 0 }),\n      up: new ThrottleGroup({ rate: Math.max(this._uploadLimit, 0), enabled: this._uploadLimit >= 0 })\n    }\n\n    if (this.tracker) {\n      if (typeof this.tracker !== 'object') this.tracker = {}\n      if (globalThis.WRTC && !this.tracker.wrtc) this.tracker.wrtc = globalThis.WRTC\n    }\n\n    if (typeof ConnPool === 'function') {\n      this._connPool = new ConnPool(this)\n    } else {\n      queueMicrotask(() => {\n        this._onListening()\n      })\n    }\n\n    // stats\n    this._downloadSpeed = throughput()\n    this._uploadSpeed = throughput()\n\n    if (opts.dht !== false && typeof DHT === 'function' /* browser exclude */) {\n      // use a single DHT instance for all torrents, so the routing table can be reused\n      this.dht = new DHT(Object.assign({}, { nodeId: this.nodeId }, opts.dht))\n\n      this.dht.once('error', err => {\n        this._destroy(err)\n      })\n\n      this.dht.once('listening', () => {\n        const address = this.dht.address()\n        if (address) {\n          this.dhtPort = address.port\n          if (this.natTraversal) {\n            this.natTraversal.map({\n              publicPort: this.dhtPort,\n              privatePort: this.dhtPort,\n              protocol: 'udp',\n              description: 'WebTorrent DHT'\n            }).catch(err => {\n              debug('error mapping DHT port via UPnP/PMP: %o', err)\n            })\n          }\n        }\n      })\n\n      // Ignore warning when there are > 10 torrents in the client\n      this.dht.setMaxListeners(0)\n\n      this.dht.listen(this.dhtPort)\n    } else {\n      this.dht = false\n    }\n\n    // Enable or disable BEP19 (Web Seeds). Enabled by default:\n    this.enableWebSeeds = opts.webSeeds !== false\n\n    const ready = () => {\n      if (this.destroyed) return\n      this.ready = true\n      this.emit('ready')\n    }\n\n    if (typeof loadIPSet === 'function' && opts.blocklist != null) {\n      loadIPSet(opts.blocklist, {\n        headers: {\n          'user-agent': `WebTorrent/${VERSION} (https://webtorrent.io)`\n        }\n      }, (err, ipSet) => {\n        if (err) return console.error(`Failed to load blocklist: ${err.message}`)\n        this.blocked = ipSet\n        ready()\n      })\n    } else {\n      queueMicrotask(ready)\n    }\n  }\n\n  /**\n   * Creates an http server to serve the contents of this torrent,\n   * dynamically fetching the needed torrent pieces to satisfy http requests.\n   * Range requests are supported.\n   *\n   * @param {{controller: ServiceWorkerRegistration}} options\n   * @param {'browser' | 'node'} [force]\n   * @return {BrowserServer | NodeServer}\n   */\n  createServer (options, force) {\n    if (this.destroyed) throw new Error('torrent is destroyed')\n    if (this._server) throw new Error('server already created')\n    if ((typeof window === 'undefined' || force === 'node') && force !== 'browser') {\n      // node implementation\n      this._server = new NodeServer(this, options)\n      return this._server\n    } else {\n      // browser implementation\n      if (!(options?.controller instanceof ServiceWorkerRegistration)) throw new Error('Invalid worker registration')\n      if (options.controller.active?.state !== 'activated' && options.controller.active?.state !== 'activating') throw new Error('Worker isn\\'t activated')\n      this._server = new BrowserServer(this, options)\n      return this._server\n    }\n  }\n\n  get downloadSpeed () { return this._downloadSpeed() }\n\n  get uploadSpeed () { return this._uploadSpeed() }\n\n  get progress () {\n    const torrents = this.torrents.filter(torrent => torrent.progress !== 1)\n    const downloaded = torrents.reduce((total, torrent) => total + torrent.downloaded, 0)\n    const length = torrents.reduce((total, torrent) => total + (torrent.length || 0), 0) || 1\n    return downloaded / length\n  }\n\n  get ratio () {\n    const uploaded = this.torrents.reduce((total, torrent) => total + torrent.uploaded, 0)\n    const received = this.torrents.reduce((total, torrent) => total + torrent.received, 0) || 1\n    return uploaded / received\n  }\n\n  /**\n   * Returns the torrent with the given `torrentId`. Convenience method. Easier than\n   * searching through the `client.torrents` array. Returns `null` if no matching torrent\n   * found.\n   *\n   * @param  {string|Buffer|Object|Torrent} torrentId\n   * @return {Promise<Torrent|null>}\n   */\n  async get (torrentId) {\n    if (torrentId instanceof Torrent) {\n      if (this.torrents.includes(torrentId)) return torrentId\n    } else {\n      const torrents = this.torrents\n      let parsed\n      try { parsed = await parseTorrent(torrentId) } catch (err) {}\n      if (!parsed) return null\n      if (!parsed.infoHash) throw new Error('Invalid torrent identifier')\n\n      for (const torrent of torrents) {\n        if (torrent.infoHash === parsed.infoHash) return torrent\n      }\n    }\n    return null\n  }\n\n  /**\n   * Start downloading a new torrent. Aliased as `client.download`.\n   * @param {string|Buffer|Object} torrentId\n   * @param {import('./lib/torrent.js').TorrentOpts | function(torrent: Torrent): void=} opts torrent-specific options\n   * @param {function(torrent: Torrent): void=} ontorrent called when the torrent is ready (has metadata)\n   */\n  add (torrentId, opts = {}, ontorrent = () => {}) {\n    if (this.destroyed) throw new Error('client is destroyed')\n    if (typeof opts === 'function') [opts, ontorrent] = [{}, opts]\n\n    const onInfoHash = () => {\n      if (this.destroyed) return\n      for (const t of this.torrents) {\n        if (t.infoHash === torrent.infoHash && t !== torrent) {\n          torrent._destroy(new Error(`Cannot add duplicate torrent ${torrent.infoHash}`))\n          ontorrent(t)\n          return\n        }\n      }\n    }\n\n    const onReady = () => {\n      if (this.destroyed) return\n      ontorrent(torrent)\n      this.emit('torrent', torrent)\n    }\n\n    function onClose () {\n      torrent.removeListener('_infoHash', onInfoHash)\n      torrent.removeListener('ready', onReady)\n      torrent.removeListener('close', onClose)\n    }\n\n    this._debug('add')\n    opts = opts ? Object.assign({}, opts) : {}\n\n    const torrent = new Torrent(torrentId, this, opts)\n    this.torrents.push(torrent)\n\n    torrent.once('_infoHash', onInfoHash)\n    torrent.once('ready', onReady)\n    torrent.once('close', onClose)\n\n    this.emit('add', torrent)\n    return torrent\n  }\n\n  /**\n   * Start seeding a new file/folder.\n   * @param  {string|File|FileList|Buffer|Array.<string|File|Buffer>} input\n   * @param  {Object=} opts\n   * @param  {function=} onseed called when torrent is seeding\n   */\n  seed (input, opts, onseed) {\n    if (this.destroyed) throw new Error('client is destroyed')\n    if (typeof opts === 'function') [opts, onseed] = [{}, opts]\n\n    this._debug('seed')\n    opts = opts ? Object.assign({}, opts) : {}\n\n    // no need to verify the hashes we create\n    opts.skipVerify = true\n\n    const isFilePath = typeof input === 'string'\n\n    // When seeding from fs path, initialize store from that path to avoid a copy\n    if (isFilePath) opts.path = path.dirname(input)\n    if (!opts.createdBy) opts.createdBy = `WebTorrent/${VERSION_STR}`\n\n    const onTorrent = torrent => {\n      const tasks = [\n        cb => {\n          // when a filesystem path is specified or the store is preloaded, files are already in the FS store\n          if (isFilePath || opts.preloadedStore) return cb()\n          torrent.load(streams, cb)\n        }\n      ]\n      if (this.dht) {\n        tasks.push(cb => {\n          torrent.once('dhtAnnounce', cb)\n        })\n      }\n      parallel(tasks, err => {\n        if (this.destroyed) return\n        if (err) return torrent._destroy(err)\n        _onseed(torrent)\n      })\n    }\n\n    const _onseed = torrent => {\n      this._debug('on seed')\n      if (typeof onseed === 'function') onseed(torrent)\n      torrent.emit('seed')\n      this.emit('seed', torrent)\n    }\n\n    const torrent = this.add(null, opts, onTorrent)\n    let streams\n\n    if (isFileList(input)) input = Array.from(input)\n    else if (!Array.isArray(input)) input = [input]\n\n    parallel(input.map(item => async cb => {\n      if (!opts.preloadedStore && isReadable(item)) {\n        const chunks = []\n        try {\n          for await (const chunk of item) {\n            chunks.push(chunk)\n          }\n        } catch (err) {\n          return cb(err)\n        }\n        const buf = concat(chunks)\n        buf.name = item.name\n        cb(null, buf)\n      } else {\n        cb(null, item)\n      }\n    }), (err, input) => {\n      if (this.destroyed) return\n      if (err) return torrent._destroy(err)\n\n      parseInput(input, opts, (err, files) => {\n        if (this.destroyed) return\n        if (err) return torrent._destroy(err)\n\n        streams = files.map(file => file.getStream)\n\n        createTorrent(input, opts, async (err, torrentBuf) => {\n          if (this.destroyed) return\n          if (err) return torrent._destroy(err)\n\n          const existingTorrent = await this.get(torrentBuf)\n          if (existingTorrent) {\n            console.warn('A torrent with the same id is already being seeded')\n            torrent._destroy()\n            if (typeof onseed === 'function') onseed(existingTorrent)\n          } else {\n            torrent._onTorrentId(torrentBuf)\n          }\n        })\n      })\n    })\n\n    return torrent\n  }\n\n  /**\n   * Remove a torrent from the client.\n   * @param  {string|Buffer|Torrent}   torrentId\n   * @param  {function} cb\n   */\n  async remove (torrentId, opts, cb) {\n    if (typeof opts === 'function') return this.remove(torrentId, null, opts)\n\n    this._debug('remove')\n    const torrent = await this.get(torrentId)\n    if (!torrent) throw new Error(`No torrent with id ${torrentId}`)\n    this._remove(torrent, opts, cb)\n  }\n\n  _remove (torrent, opts, cb) {\n    if (!torrent) return\n    if (typeof opts === 'function') return this._remove(torrent, null, opts)\n    const index = this.torrents.indexOf(torrent)\n    if (index === -1) return\n    this.torrents.splice(index, 1)\n    torrent.destroy(opts, cb)\n    if (this.dht) {\n      this.dht._tables.remove(torrent.infoHash)\n    }\n    this.emit('remove', torrent)\n  }\n\n  address () {\n    if (!this.listening) return null\n    return this._connPool\n      ? this._connPool.tcpServer.address()\n      : { address: '0.0.0.0', family: 'IPv4', port: 0 }\n  }\n\n  /**\n   * Set global download throttle rate.\n   * @param  {Number} rate (must be bigger or equal than zero, or -1 to disable throttling)\n   */\n  throttleDownload (rate) {\n    rate = Number(rate)\n    if (isNaN(rate) || !isFinite(rate) || (rate < 0 && rate !== -1)) return false\n    this._downloadLimit = Math.round(rate)\n    if (this._downloadLimit === -1) return this.throttleGroups.down.setEnabled(false)\n    this.throttleGroups.down.setEnabled(true)\n    this.throttleGroups.down.setRate(this._downloadLimit)\n  }\n\n  /**\n   * Set global upload throttle rate\n   * @param  {Number} rate (must be bigger or equal than zero, or -1 to disable throttling)\n   */\n  throttleUpload (rate) {\n    rate = Number(rate)\n    if (isNaN(rate) || !isFinite(rate) || (rate < 0 && rate !== -1)) return false\n    this._uploadLimit = Math.round(rate)\n    if (this._uploadLimit === -1) return this.throttleGroups.up.setEnabled(false)\n    this.throttleGroups.up.setEnabled(true)\n    this.throttleGroups.up.setRate(this._uploadLimit)\n  }\n\n  /**\n   * Destroy the client, including all torrents and connections to peers.\n   * @param  {function} cb\n   */\n  destroy (cb) {\n    if (this.destroyed) throw new Error('client already destroyed')\n    this._destroy(null, cb)\n  }\n\n  _destroy (err, cb) {\n    this._debug('client destroy')\n    this.destroyed = true\n\n    const tasks = this.torrents.map(torrent => cb => {\n      torrent.destroy(cb)\n    })\n\n    if (this._connPool) {\n      tasks.push(cb => {\n        this._connPool.destroy(cb)\n      })\n    }\n\n    if (this.dht) {\n      tasks.push(cb => {\n        this.dht.destroy(cb)\n      })\n    }\n\n    if (this._server) {\n      tasks.push(cb => {\n        this._server.destroy(cb)\n      })\n    }\n\n    if (this.natTraversal) {\n      tasks.push(cb => {\n        this.natTraversal.destroy()\n          .then(() => cb())\n      })\n    }\n\n    parallel(tasks, cb)\n\n    if (err) this.emit('error', err)\n\n    this.torrents = []\n    this._connPool = null\n    this.dht = null\n\n    this.throttleGroups.down.destroy()\n    this.throttleGroups.up.destroy()\n  }\n\n  _onListening () {\n    this._debug('listening')\n    this.listening = true\n\n    if (this._connPool) {\n      // Sometimes server.address() returns `null` in Docker.\n      const address = this._connPool.tcpServer.address()\n      if (address) {\n        this.torrentPort = address.port\n        if (this.natTraversal) {\n          this.natTraversal.map({\n            publicPort: this.torrentPort,\n            privatePort: this.torrentPort,\n            protocol: this.utp ? null : 'tcp',\n            description: 'WebTorrent Torrent'\n          }).catch(err => {\n            debug('error mapping WebTorrent port via UPnP/PMP: %o', err)\n          })\n        }\n      }\n    }\n\n    this.emit('listening')\n  }\n\n  _debug () {\n    const args = [].slice.call(arguments)\n    args[0] = `[${this._debugId}] ${args[0]}`\n    debug(...args)\n  }\n\n  get _utpServer () {\n    return this._connPool?.utpServer || null\n  }\n\n  async _getByHash (infoHashHash) {\n    for (const torrent of this.torrents) {\n      if (!torrent.infoHashHash) {\n        torrent.infoHashHash = await hash(hex2arr('72657132' /* 'req2' */ + torrent.infoHash), 'hex')\n      }\n      if (infoHashHash === torrent.infoHashHash) {\n        return torrent\n      }\n    }\n\n    return null\n  }\n}\n\nWebTorrent.WEBRTC_SUPPORT = Peer.WEBRTC_SUPPORT\nWebTorrent.UTP_SUPPORT = ConnPool.UTP_SUPPORT\nWebTorrent.VERSION = VERSION\n\n/**\n * Check if `obj` is a node Readable stream\n * @param  {*} obj\n * @return {boolean}\n */\nfunction isReadable (obj) {\n  return typeof obj === 'object' && obj != null && typeof obj.pipe === 'function'\n}\n\n/**\n * Check if `obj` is a W3C `FileList` object\n * @param  {*} obj\n * @return {boolean}\n */\nfunction isFileList (obj) {\n  return typeof FileList !== 'undefined' && obj instanceof FileList\n}\n"]}