[
  {
    "path": ".deepsource.toml",
    "content": "version = 1\n\ntest_patterns = [\"test/**\"]\n\n[[analyzers]]\nname = \"javascript\"\nenabled = true\n\n  [analyzers.meta]\n  environment = [\n    \"nodejs\",\n    \"mocha\"\n  ]\n  dialect = \"typescript\"\n"
  },
  {
    "path": ".github/FUNDING.yml",
    "content": "github: peers\nopen_collective: peer\n"
  },
  {
    "path": ".github/workflows/browserstack.yml",
    "content": "name: \"BrowserStack Test\"\non: [push, pull_request]\n\nconcurrency:\n  group: browserstack\njobs:\n  ubuntu-job:\n    name: \"BrowserStack Test on Ubuntu\"\n    runs-on: ubuntu-latest # Can be self-hosted runner also\n    steps:\n      - name: \"BrowserStack Env Setup\" # Invokes the setup-env action\n        uses: browserstack/github-actions/setup-env@master\n        with:\n          username: ${{ secrets.BROWSERSTACK_USERNAME }}\n          access-key: ${{ secrets.BROWSERSTACK_ACCESS_KEY }}\n\n      - name: \"BrowserStack Local Tunnel Setup\" # Invokes the setup-local action\n        uses: browserstack/github-actions/setup-local@master\n        with:\n          local-testing: \"start\"\n          local-logging-level: \"all-logs\"\n          local-identifier: \"random\"\n\n      # The next 3 steps are for building the web application to be tested and starting the web server on the runner environment\n\n      - name: \"Checkout the repository\"\n        uses: actions/checkout@v4\n\n      - name: \"Building web application to be tested\"\n        run: npm install && npm run build\n\n      - name: \"Running application under test\"\n        run: npx http-server -p 3000 --cors &\n\n      - name: \"Running test on BrowserStack\" # Invokes the actual test script that would run on BrowserStack browsers\n        run: npm run e2e:bstack # See sample test script above\n        env:\n          BYPASS_WAF: ${{ secrets.BYPASS_WAF }}\n\n      - name: \"BrowserStackLocal Stop\" # Terminating the BrowserStackLocal tunnel connection\n        uses: browserstack/github-actions/setup-local@master\n        with:\n          local-testing: \"stop\"\n"
  },
  {
    "path": ".github/workflows/codeql-analysis.yml",
    "content": "# For most projects, this workflow file will not need changing; you simply need\n# to commit it to your repository.\n#\n# You may wish to alter this file to override the set of languages analyzed,\n# or to provide custom queries or build logic.\n#\n# ******** NOTE ********\n# We have attempted to detect the languages in your repository. Please check\n# the `language` matrix defined below to confirm you have the correct set of\n# supported CodeQL languages.\n#\nname: \"CodeQL\"\n\non:\n  push:\n    branches: [master, rc, stable]\n  pull_request:\n    # The branches below must be a subset of the branches above\n    branches: [master]\n  schedule:\n    - cron: \"15 2 * * 5\"\n\njobs:\n  analyze:\n    name: Analyze\n    runs-on: ubuntu-latest\n    permissions:\n      actions: read\n      contents: read\n      security-events: write\n\n    strategy:\n      fail-fast: false\n      matrix:\n        language: [\"javascript\"]\n        # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]\n        # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support\n\n    steps:\n      - name: Checkout repository\n        uses: actions/checkout@v4\n\n      # Initializes the CodeQL tools for scanning.\n      - name: Initialize CodeQL\n        uses: github/codeql-action/init@v3\n        with:\n          languages: ${{ matrix.language }}\n          # If you wish to specify custom queries, you can do so here or in a config file.\n          # By default, queries listed here will override any specified in a config file.\n          # Prefix the list here with \"+\" to use these queries and those in the config file.\n\n          # Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs\n          # queries: security-extended,security-and-quality\n\n      # Autobuild attempts to build any compiled languages  (C/C++, C#, or Java).\n      # If this step fails, then you should remove it and run the build manually (see below)\n      - name: Autobuild\n        uses: github/codeql-action/autobuild@v3\n\n      # ℹ️ Command-line programs to run using the OS shell.\n      # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun\n\n      #   If the Autobuild fails above, remove it and uncomment the following three lines.\n      #   modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance.\n\n      # - run: |\n      #   echo \"Run, Build Application using script\"\n      #   ./location_of_script_within_repo/buildscript.sh\n\n      - name: Perform CodeQL Analysis\n        uses: github/codeql-action/analyze@v3\n"
  },
  {
    "path": ".github/workflows/prettier.yml",
    "content": "# From https://til.simonwillison.net/github-actions/prettier-github-actions\nname: Check JavaScript for conformance with Prettier\n\non:\n  push:\n  pull_request:\n\njobs:\n  prettier:\n    runs-on: ubuntu-latest\n    steps:\n      - name: Check out repo\n        uses: actions/checkout@v4\n      - uses: actions/setup-node@v4\n        with:\n          node-version: 16\n          cache: \"npm\"\n      - run: npm ci\n      - name: Run prettier\n        run: |-\n          npm run format:check\n"
  },
  {
    "path": ".github/workflows/release.yml",
    "content": "name: Release\non:\n  push:\n    branches:\n      - rc\n      - stable\njobs:\n  release:\n    name: Release\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v4\n        with:\n          fetch-depth: 0\n      - name: Setup Node.js\n        uses: actions/setup-node@v4\n        with:\n          node-version: \"lts/*\"\n      - name: Install dependencies\n        run: npm ci\n      - name: Import GPG key\n        id: import_gpg\n        uses: crazy-max/ghaction-import-gpg@v6\n        with:\n          gpg_private_key: ${{ secrets.GPG_PRIVATE_KEY }}\n          passphrase: ${{ secrets.GPG_PASSPHRASE }}\n          git_user_signingkey: true\n          git_commit_gpgsign: true\n      - name: Release\n        env:\n          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n          NPM_TOKEN: ${{ secrets.NPM_TOKEN }}\n          GIT_COMMITTER_NAME: ${{ steps.import_gpg.outputs.name }}\n          GIT_COMMITTER_EMAIL: ${{ steps.import_gpg.outputs.email }}\n        run: npx semantic-release\n"
  },
  {
    "path": ".github/workflows/test.yml",
    "content": "# This workflow will do a clean installation of node dependencies, cache/restore them, build the source code and run tests across different versions of node\n# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-nodejs\n\nname: Node.js CI\n\non:\n  push:\n    branches: [\"master\"]\n  pull_request:\n    branches: [\"master\"]\n\njobs:\n  build:\n    runs-on: ubuntu-latest\n\n    strategy:\n      matrix:\n        node-version: [16.x, 18.x, 20.x]\n        # See supported Node.js release schedule at https://nodejs.org/en/about/releases/\n\n    steps:\n      - uses: actions/checkout@v4\n      - name: Use Node.js ${{ matrix.node-version }}\n        uses: actions/setup-node@v4\n        with:\n          node-version: ${{ matrix.node-version }}\n          cache: \"npm\"\n      - run: npm ci\n      - run: npm run check\n      - run: npm run build\n      # - run: npm run lint\n      - run: npm run coverage\n      - name: Publish code coverage to CodeClimate\n        uses: paambaati/codeclimate-action@v6.0.0\n        env:\n          CC_TEST_REPORTER_ID: ${{secrets.CC_TEST_REPORTER_ID}}\n"
  },
  {
    "path": ".gitignore",
    "content": "lib-cov\ncoverage\n*.seed\n*.log\n*.csv\n*.dat\n*.out\n*.pid\n*.[t]gz\n\ndist\npids\nlogs\nresults\ndemo\nbower.json\nnode_modules\n.parcel-cache\n.idea\nnpm-debug.log\n.DS_STORE\n\ntest/output\nbrowserstack.err\n.tscache\ntest/public\n.vscode/\n"
  },
  {
    "path": ".prettierignore",
    "content": "dist\ndocs\npackage-json.lock\n\n# semantic-release\nCHANGELOG.md"
  },
  {
    "path": ".prettierrc.toml",
    "content": "trailingComma = \"all\"\nsemi = true\nuseTabs = true"
  },
  {
    "path": ".releaserc.json",
    "content": "{\n\t\"branches\": [\"stable\", { \"name\": \"rc\", \"prerelease\": true }],\n\t\"plugins\": [\n\t\t\"@semantic-release/commit-analyzer\",\n\t\t\"@semantic-release/release-notes-generator\",\n\t\t\"@semantic-release/changelog\",\n\t\t\"@semantic-release/npm\",\n\t\t\"@semantic-release/git\",\n\t\t\"@semantic-release/github\"\n\t]\n}\n"
  },
  {
    "path": "CHANGELOG.md",
    "content": "## [1.5.5](https://github.com/peers/peerjs/compare/v1.5.4...v1.5.5) (2025-06-07)\n\n\n### Bug Fixes\n\n* inline package version ([bd6e017](https://github.com/peers/peerjs/commit/bd6e0170c16a0dedc16a4128b81482f109b390a0)), closes [#1322](https://github.com/peers/peerjs/issues/1322)\n\n## [1.5.4](https://github.com/peers/peerjs/compare/v1.5.3...v1.5.4) (2024-05-14)\n\n\n### Bug Fixes\n\n* **deps:** update dependency webrtc-adapter to v9 ([#1266](https://github.com/peers/peerjs/issues/1266)) ([5536abf](https://github.com/peers/peerjs/commit/5536abf8d6345c248df875e0e22c520a20cb2919))\n* remove CBOR ([badc9e8](https://github.com/peers/peerjs/commit/badc9e8bc4f7ce5517de3a58abcaec1d566eccf5)), closes [#1271](https://github.com/peers/peerjs/issues/1271) [#1247](https://github.com/peers/peerjs/issues/1247) [#1271](https://github.com/peers/peerjs/issues/1271)\n\n## [1.5.3](https://github.com/peers/peerjs/compare/v1.5.2...v1.5.3) (2024-05-11)\n\n\n### Bug Fixes\n\n* navigator is not defined. ([#1202](https://github.com/peers/peerjs/issues/1202)) ([4b7a74d](https://github.com/peers/peerjs/commit/4b7a74d74c50461fde80e84992d88a9d564dbe72)), closes [#1165](https://github.com/peers/peerjs/issues/1165)\n* remove need for `unsafe-eval` ([3fb31b3](https://github.com/peers/peerjs/commit/3fb31b316b8f4d699d087e1b465e908688be3872))\n\n## [1.5.2](https://github.com/peers/peerjs/compare/v1.5.1...v1.5.2) (2023-12-05)\n\n\n### Bug Fixes\n\n* support Blobs nested in objects ([7956dd6](https://github.com/peers/peerjs/commit/7956dd640388fce62c83453d56e1a20aec2212b2)), closes [#1163](https://github.com/peers/peerjs/issues/1163)\n\n## [1.5.1](https://github.com/peers/peerjs/compare/v1.5.0...v1.5.1) (2023-09-23)\n\n\n### Bug Fixes\n\n* convert `Blob`s to `ArrayBuffer`s during `.send()` ([95bb0f7](https://github.com/peers/peerjs/commit/95bb0f7fa9aa0d119613727c32857e5af33e14a1)), closes [#1137](https://github.com/peers/peerjs/issues/1137)\n* convert `Blob`s to `ArrayBuffer`s during `.send()` ([#1142](https://github.com/peers/peerjs/issues/1142)) ([094f849](https://github.com/peers/peerjs/commit/094f849816d327bf74a447fbf7d58195c1a4fc66))\n\n# [1.5.0](https://github.com/peers/peerjs/compare/v1.4.7...v1.5.0) (2023-09-03)\n\n\n### Bug Fixes\n\n* **datachannel:** sending order is now preserved correctly ([#1038](https://github.com/peers/peerjs/issues/1038)) ([0fb6179](https://github.com/peers/peerjs/commit/0fb61792ed3afe91123550a159c8633ed0976f89)), closes [#746](https://github.com/peers/peerjs/issues/746)\n* **deps:** update dependency @swc/helpers to ^0.4.0 ([a7de8b7](https://github.com/peers/peerjs/commit/a7de8b78f57a5cf9708fa54e9f82f4ab43c0bca2))\n* **deps:** update dependency cbor-x to v1.5.4 ([c1f04ec](https://github.com/peers/peerjs/commit/c1f04ecf686e64266fb54b3e4992c73c1522ae79))\n* **deps:** update dependency eventemitter3 to v5 ([caf01c6](https://github.com/peers/peerjs/commit/caf01c6440534cbe190facd84cecf9ca62e4a5ce))\n* **deps:** update dependency peerjs-js-binarypack to v1.0.2 ([7452e75](https://github.com/peers/peerjs/commit/7452e7591d4982d9472c524d6ad30e66c2a2b44f))\n* **deps:** update dependency webrtc-adapter to v8 ([431f00b](https://github.com/peers/peerjs/commit/431f00bd89809867a19c98224509982b82769558))\n* **deps:** update dependency webrtc-adapter to v8.2.2 ([62402fc](https://github.com/peers/peerjs/commit/62402fcae03c78382d7fa80c11f459aca8d21620))\n* **deps:** update dependency webrtc-adapter to v8.2.3 ([963455e](https://github.com/peers/peerjs/commit/963455ee383a069e53bd93b1128d82615a698245))\n* **MediaConnection:** `close` event is fired on remote Peer ([0836356](https://github.com/peers/peerjs/commit/0836356d18c91449f4cbb23e4d4660a4351d7f56)), closes [#636](https://github.com/peers/peerjs/issues/636) [#1089](https://github.com/peers/peerjs/issues/1089) [#1032](https://github.com/peers/peerjs/issues/1032) [#832](https://github.com/peers/peerjs/issues/832) [#780](https://github.com/peers/peerjs/issues/780) [#653](https://github.com/peers/peerjs/issues/653)\n* **npm audit:** Updates all dependencies that cause npm audit to issue a warning ([6ef5707](https://github.com/peers/peerjs/commit/6ef5707dc85d8b921d8dfea74890b110ddf5cd4f))\n\n\n### Features\n\n* `.type` property on `Error`s emitted from connections ([#1126](https://github.com/peers/peerjs/issues/1126)) ([debe7a6](https://github.com/peers/peerjs/commit/debe7a63474b9cdb705676d4c7892b0cd294402a))\n* `PeerError` from connections ([ad3a0cb](https://github.com/peers/peerjs/commit/ad3a0cbe8c5346509099116441e6c3ff0b6ca6c4))\n* **DataConnection:** handle close messages and flush option ([6ca38d3](https://github.com/peers/peerjs/commit/6ca38d32b0929745b92a55c8f6aada1ee0895ce7)), closes [#982](https://github.com/peers/peerjs/issues/982)\n* **MediaChannel:** Add experimental `willCloseOnRemote` event to MediaConnection. ([ed84829](https://github.com/peers/peerjs/commit/ed84829a1092422f3d7f92f467bcf5b8ada82891))\n* MsgPack/Cbor serialization ([fcffbf2](https://github.com/peers/peerjs/commit/fcffbf243cb7d6dabfc773211c155c0ae1e00baf))\n* MsgPack/Cbor serialization ([#1120](https://github.com/peers/peerjs/issues/1120)) ([4367256](https://github.com/peers/peerjs/commit/43672564ee9edcb15e736b0333c6ad8aeae20c59)), closes [#611](https://github.com/peers/peerjs/issues/611)\n\n## [1.4.7](https://github.com/peers/peerjs/compare/v1.4.6...v1.4.7) (2022-08-09)\n\n\n### Bug Fixes\n\n* **browser-bundle:** Leaked private functions in the global scope ([857d425](https://github.com/peers/peerjs/commit/857d42524a929388b352a2330f18fdfc15df6c22)), closes [#989](https://github.com/peers/peerjs/issues/989)\n\n## [1.4.6](https://github.com/peers/peerjs/compare/v1.4.5...v1.4.6) (2022-05-25)\n\n\n### Bug Fixes\n\n* **typings:** `MediaConnection.answer()` doesn’t need a `stream` anymore, thanks [@matallui](https://github.com/matallui)! ([666dcd9](https://github.com/peers/peerjs/commit/666dcd9770fe080e00898b9138664e8996bf5162))\n* **typings:** much stronger event typings for `DataConnection`,`MediaConnection` ([0c96603](https://github.com/peers/peerjs/commit/0c96603a3f97f28eabe24906e692c31ef0ebca13))\n\n\n### Performance Improvements\n\n* **turn:** reduce turn server count ([8816f54](https://github.com/peers/peerjs/commit/8816f54c4b4bff5f6bd0c7ccf5327ec84e80a8ca))\n\n## [1.4.5](https://github.com/peers/peerjs/compare/v1.4.4...v1.4.5) (2022-05-24)\n\n\n### Bug Fixes\n\n* **referrerPolicy:** you can now set a custom referrerPolicy for api requests ([c0ba9e4](https://github.com/peers/peerjs/commit/c0ba9e4b64f233c2733a8c5e904a8536ae37eb42)), closes [#955](https://github.com/peers/peerjs/issues/955)\n* **typings:** add missing type exports ([#959](https://github.com/peers/peerjs/issues/959)) ([3c915d5](https://github.com/peers/peerjs/commit/3c915d57bb18ac822d3438d879717266ee84b635)), closes [#961](https://github.com/peers/peerjs/issues/961)\n\n## [1.4.4](https://github.com/peers/peerjs/compare/v1.4.3...v1.4.4) (2022-05-13)\n\n\n### Bug Fixes\n\n* **CRA@4:** import hack ([41c3ba7](https://github.com/peers/peerjs/commit/41c3ba7b2ca6adc226efd0e2add546a570a4aa3a)), closes [#954](https://github.com/peers/peerjs/issues/954)\n* **source maps:** enable source map inlining ([97a724b](https://github.com/peers/peerjs/commit/97a724b6a1e04817d79ecaf91d4384ae3a94cf99))\n\n## [1.4.3](https://github.com/peers/peerjs/compare/v1.4.2...v1.4.3) (2022-05-13)\n\n\n### Bug Fixes\n\n* **typings:** export interfaces ([979e695](https://github.com/peers/peerjs/commit/979e69545cc2fe10c60535ac9793140ef8dba4ec)), closes [#953](https://github.com/peers/peerjs/issues/953)\n\n## [1.4.2](https://github.com/peers/peerjs/compare/v1.4.1...v1.4.2) (2022-05-12)\n\n\n### Bug Fixes\n\n* **bundler import:** enable module target ([b5beec4](https://github.com/peers/peerjs/commit/b5beec4a07827f82c5e50c79c71a8cfb1ec3c40e)), closes [#761](https://github.com/peers/peerjs/issues/761)\n\n## [1.4.1](https://github.com/peers/peerjs/compare/v1.4.0...v1.4.1) (2022-05-11)\n\n\n### Bug Fixes\n\n* **old bundlers:** include support for Node 10 (EOL since 2021-04-01) / old bundlers ([c0f4648](https://github.com/peers/peerjs/commit/c0f4648b1c104e5e0e5967bb239c217288aa83e0)), closes [#952](https://github.com/peers/peerjs/issues/952)\n\n# [1.4.0](https://github.com/peers/peerjs/compare/v1.3.2...v1.4.0) (2022-05-10)\n\n\n### Bug Fixes\n\n* add changelog and npm version to the repo ([d5bd955](https://github.com/peers/peerjs/commit/d5bd9552daf5d42f9d04b3087ddc34c729004daa))\n* add token to PeerJSOption type definition ([e7675e1](https://github.com/peers/peerjs/commit/e7675e1474b079b2804167c70335a6c6e2b8ec08))\n* websocket connection string ([82b8c71](https://github.com/peers/peerjs/commit/82b8c713bc03be34c2526bdf442a583c4d547c83))\n\n\n### Features\n\n* upgrade to Parcel@2 ([aae9d1f](https://github.com/peers/peerjs/commit/aae9d1fa37731d0819f93535b8ad78fe4b685d1e)), closes [#845](https://github.com/peers/peerjs/issues/845) [#859](https://github.com/peers/peerjs/issues/859) [#552](https://github.com/peers/peerjs/issues/552) [#585](https://github.com/peers/peerjs/issues/585)\n\n\n### Performance Improvements\n\n* **turn:** lower TURN-latency due to more local servers ([a412ea4](https://github.com/peers/peerjs/commit/a412ea4984a46d50de8873904b7067897b0f29f9))\n\n<a name=\"1.3.2\"></a>\n\n## 1.3.2 (2021-03-11)\n\n- fixed issues #800, #803 in PR #806, thanks @jordanaustin\n- updated devDeps: `typescript` to 4.2\n\n<a name=\"1.3.1\"></a>\n\n## 1.3.1 (2020-07-11)\n\n- fixed: map file resolving\n- removed: @types/webrtc because it contains in ts dom lib.\n\n<a name=\"1.3.0\"></a>\n\n## 1.3.0 (2020-07-03)\n\n- changed: don't close the Connection if `iceConnectionState` changed to `disconnected`\n\n<a name=\"1.2.0\"></a>\n\n## 1.2.0 (2019-12-24)\n\n- added: ability to change json stringify / json parse methods for DataConnection #592\n\n- removed: `peerBrowser` field from `dataConnection` because unused\n\n- fixed: lastServerId and reconnect #580 #534 #265\n\n<a name=\"1.1.0\"></a>\n\n## 1.1.0 (2019-09-16)\n\n- removed: deprecated `RtpDataChannels` and `DtlsSrtpKeyAgreement` options\n- removed: grunt from deps, upgrade deps versions\n- removed: Reliable dep because modern browsers supports `RTCDataChannel.ordered` property\n\n- added: TURN server to default config\n\n- fixed: emit error message, then destroy/disconnect when error occurred\n- fixed: use `peerjs-js-binarypack` instead of `js-binarypack`\n- fixed: sending large files via DataConnection #121\n\n<a name=\"1.0.4\"></a>\n\n## 1.0.4 (2019-08-31)\n\n- fixed: 'close' event for DataConnection #568\n\n<a name=\"1.0.3\"></a>\n\n## 1.0.3 (2019-08-21)\n\n- add pingInterval option\n\n<a name=\"1.0.2\"></a>\n\n## 1.0.2 (2019-07-20)\n\n### Bug Fixes\n\n- fixed: memory leak in DataConnection #556\n- fixed: missing sdpMid in IceServer #550\n\n### Other\n\n- updated: old @types/webrtc dependency #549\n\n<a name=\"1.0.1\"></a>\n\n## 1.0.1 (2019-07-09)\n\n### Bug Fixes\n\n- fixed: readyState of undefined #520\n- fixed: call sdpTransform in Answer #524\n- fixed: sdpTransform does not apply to makeAnswer SDP #523\n\n<a name=\"1.0.0\"></a>\n\n## 1.0.0 (2019-04-10)\n\n### Refactoring\n\nAlmost all project was refactored!!!\n\n- removed: xhr long-pooling #506\n- changed: fetch api instead of xhr\n\n### Features\n\n- added: heartbeat #502\n\n### Bug Fixes\n\n- fixed: destroy RTCPeerConnection #513\n- fixed: MediaStream memory leak #514\n\n<a name=\"0.3.18\"></a>\n\n## 0.3.18 (2018-10-30)\n\n### Features\n\n- **typescript:** First commit ([0c77a5b](https://github.com/peers/peerjs/commit/0c77a5b))\n\n<a name=\"0.3.16\"></a>\n\n## 0.3.16 (2018-08-21)\n\n### Bug Fixes\n\n- fixed typo in README ([f1bd47e](https://github.com/peers/peerjs/commit/f1bd47e))\n\n## Version 0.3.14\n\n- Patch for #246, which started as of Chrome 38.\n\n## Version 0.3.11 (28 Sep 2014)\n\n- Browserify build system\n\n## Version 0.3.10 (29 Aug 2014)\n\n- Fixed a bug where `disconnected` would be emitted for XHR requests that were aborted on purpose.\n\n## Version 0.3.9 (11 July 2014)\n\n- Allow an external adapter to be used (for `RTCPeerConnection` and such). (Thanks, @khankuan!)\n- Fixed a bug where `_chunkedData` was not being cleared recursively, causing memory to be eaten up unnecessarily. (Thanks, @UnsungHero97!)\n- Added `peer.reconnect()`, which allows a peer to reconnect to the signalling server with the same ID it had before after it has been disconnected. (Thanks, @jure, for the amazing input :)!)\n- Added previously-missing error types, such as `webrtc`, `network`, and `peer-unavailable` error types. (Thanks, @mmis1000 for reporting!)\n- Fixed a bug where the peer would infinitely attempt to start XHR streaming when there is no network connection available. Now, the peer will simply emit a `network` error and disconnect. (Thanks, @UnsungHero97 for reporting!)\n\n## Version 0.3.8 beta (18 Mar 2014)\n\n- **The following changes are only compatible with PeerServer 0.2.4.**\n- Added the ability to specify a custom path when connecting to a self-hosted\n  PeerServer.\n- Added the ability to retrieve a list of all peers connected to the server.\n\n## Version 0.3.7 beta (23 Dec 2013)\n\n- Chrome 31+/Firefox 27+ DataConnection interop for files.\n- Deprecate `binary-utf8` in favor of faster support for UTF8 in the regular\n  `binary` serialization.\n- Fix `invalid-key` error message.\n\n## Version 0.3.6 beta (3 Dec 2013)\n\n- Workaround for hitting Chrome 31+ buffer limit.\n- Add `.bufferSize` to DataConnection to indicate the size of the buffer queue.\n- Add `.dataChannel` to DataConnection as an alias for `._dc`, which contains\n  the RTCDataChannel object associated with the DataConnection.\n- Update BinaryPack dependency.\n\n## Version 0.3.5 beta (26 Nov 2013)\n\n- Fix bug where chunks were being emitted.\n\n## Version 0.3.4 beta (11 Nov 2013)\n\n- Fix file transfer issue in Chrome by chunking for data over 120KB.\n- Use binary data when possible.\n- Update BinaryPack dependency to fix inefficiencies.\n\n## Version 0.3.3 beta (2 Nov 2013)\n\n- Fix exceptions when peer emits errors upon creation\n- Remove extra commas\n\n## Version 0.3.2 beta (25 Oct 2013)\n\n- Use SCTP in Chrome 31+.\n- Work around Chrome 31+ tab crash. The crashes were due to Chrome's lack of support for the `maxRetransmits` parameter for modifying SDP.\n- Fix exceptions in Chrome 29 and below.\n- DataChannels are unreliable by default in Chrome 30 and below. In setting\n  reliable to `true`, the reliable shim is used only in Chrome 30 and below.\n\n## Version 0.3.1 beta (19 Oct 2013)\n\n- Updated docs and examples for TURN server usage\n- Fixed global variable leak\n- DataConnections now have reliable: false by default. This will switch to on when reliable: true works in more browsers\n\n## Version 0.3.0 beta (20 Sept 2013)\n\n### Highlights\n\n- Support for WebRTC video and audio streams in both Firefox and Chrome.\n- Add `util.supports.[FEATURE]` flags, which represent the WebRTC features\n  supported by your browser.\n- **Breaking:** Deprecate current `Peer#connections` format. Connections will no longer be\n  keyed by label and will instead be in a list.\n\n### Other changes\n\n- **Breaking:** Deprecate `Peer.browser` in favor of `util.browser`.\n- Additional logging levels (warnings, errors, all).\n- Additional logging functionality (`logFunction`).\n- SSL option now in config rather than automatic.\n\n## Version 0.2.8 (1 July 2013)\n\n- Fix bug, no error on Firefox 24 due to missing error callback.\n- TLS secure PeerServers now supported.\n- Updated version of Reliable shim.\n\n## Version 0.2.7 (28 May 2013)\n\n- Fix bug, no error when .disconnect called in before socket connection established.\n- Fix bug, failure to enter debug mode when aborting because browser not supported.\n\n## Version 0.2.6 (2 May 2013)\n\n- Peer.browser to check browser type.\n- Update Reliable library and fix Reliable functionality in Chrome.\n\n## Version 0.2.5 (24 Apr 2013)\n\n- **Firefox compatibility for Firefox Nightly.**\n- Misc bug fixes.\n\n## Version 0.2.1 (3 Apr 2013)\n\n- **Warning**: this build changes the error of type `peer-destroyed` to `server-disconnected`.\n- ~~**Firefox compatibility.**~~ - Pushed back due to volatility of Firefox Nightly DataChannel APIs.\n- Browser detection added. If an incompatible browser is detected, the `browser-incompatible` error is emitted from the `Peer`.\n- Added a `.disconnect()` method to `Peer`, which can be called to close connections to the PeerServer (but not any active DataConnections).\n\n## Version 0.2.0 (24 Mar 2013)\n\n- **Warning**: this build introduces the following API changes that may break existing code.\n  - `peer.connections` is no longer a hash mapping peer IDs to connections.\n  - Connections no longer emit errors from `PeerConnection`; `PeerConnection` errors are now forwarded to the `Peer` object.\n- Add support for multiple DataConnections with different labels.\n- Update Reliable version to support faster file transfer.\n- Fix bug where using XHR streaming to broker a connection occasionally fails.\n\n## Version 0.1.7 (6 Mar 2013)\n\n- Add experimental `reliable` messaging option. [See documentation.](https://github.com/peers/peerjs/blob/master/docs/api.md#experimental-reliable-and-large-file-transfer)\n- Fix bug where the ID /GET request was cached and so two Peers created simultaneously would get the same ID: [See issue.](https://github.com/peers/peerjs-server/issues/2)\n- Add support for relative hostname. [See documentation.](https://github.com/peers/peerjs/blob/master/docs/api.md#new-peerid-options)\n"
  },
  {
    "path": "LICENSE",
    "content": "Copyright (c) 2015 Michelle Bu and Eric Zhang, http://peerjs.com\n\n(The MIT License)\n\nPermission is hereby granted, free of charge, to any person obtaining\na copy of this software and associated documentation files (the\n\"Software\"), to deal in the Software without restriction, including\nwithout limitation the rights to use, copy, modify, merge, publish,\ndistribute, sublicense, and/or sell copies of the Software, and to\npermit persons to whom the Software is furnished to do so, subject to\nthe following conditions:\n\nThe above copyright notice and this permission notice shall be\nincluded in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\nNONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\nLIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\nWITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "# PeerJS: Simple peer-to-peer with WebRTC\n\n[![Backers on Open Collective](https://opencollective.com/peer/backers/badge.svg)](#backers)\n[![Sponsors on Open Collective](https://opencollective.com/peer/sponsors/badge.svg)](#sponsors)\n[![Discord](https://img.shields.io/discord/1016419835455996076?color=5865F2&label=Discord&logo=discord&logoColor=white)](https://discord.gg/Ud2PvAtK37)\n\nPeerJS provides a complete, configurable, and easy-to-use peer-to-peer API built on top of WebRTC, supporting both data channels and media streams.\n\n## Live Example\n\nHere's an example application that uses both media and data connections: https://glitch.com/~peerjs-video. The example also uses its own [PeerServer](https://github.com/peers/peerjs-server).\n\n---\n\n<p align=\"center\">\n  <sup>Special Announcement:</sup>\n  <br>\n  <a href=\"https://discord.gg/Ud2PvAtK37\">\n    <img width=\"70px\" src=\"https://assets-global.website-files.com/6257adef93867e50d84d30e2/625e5fcef7ab80b8c1fe559e_Discord-Logo-Color.png\" />\n  </a>\n  <br>\n  <sub><b>We now have a Discord Channel</b></sub>\n  <br>\n  <sub>There we plan to discuss roadmaps, feature requests, and more<br><a href=\"https://discord.gg/Ud2PvAtK37\">Join us today</a></sub>\n</p>\n\n---\n\n## Setup\n\n**Include the library**\n\nwith npm:\n`npm install peerjs`\n\nwith yarn:\n`yarn add peerjs`\n\n```js\n// The usage -\nimport { Peer } from \"peerjs\";\n```\n\n**Create a Peer**\n\n```javascript\nconst peer = new Peer(\"pick-an-id\");\n// You can pick your own id or omit the id if you want to get a random one from the server.\n```\n\n## Data connections\n\n**Connect**\n\n```javascript\nconst conn = peer.connect(\"another-peers-id\");\nconn.on(\"open\", () => {\n\tconn.send(\"hi!\");\n});\n```\n\n**Receive**\n\n```javascript\npeer.on(\"connection\", (conn) => {\n\tconn.on(\"data\", (data) => {\n\t\t// Will print 'hi!'\n\t\tconsole.log(data);\n\t});\n\tconn.on(\"open\", () => {\n\t\tconn.send(\"hello!\");\n\t});\n});\n```\n\n## Media calls\n\n**Call**\n\n```javascript\nnavigator.mediaDevices.getUserMedia(\n\t{ video: true, audio: true },\n\t(stream) => {\n\t\tconst call = peer.call(\"another-peers-id\", stream);\n\t\tcall.on(\"stream\", (remoteStream) => {\n\t\t\t// Show stream in some <video> element.\n\t\t});\n\t},\n\t(err) => {\n\t\tconsole.error(\"Failed to get local stream\", err);\n\t},\n);\n```\n\n**Answer**\n\n```javascript\npeer.on(\"call\", (call) => {\n\tnavigator.mediaDevices.getUserMedia(\n\t\t{ video: true, audio: true },\n\t\t(stream) => {\n\t\t\tcall.answer(stream); // Answer the call with an A/V stream.\n\t\t\tcall.on(\"stream\", (remoteStream) => {\n\t\t\t\t// Show stream in some <video> element.\n\t\t\t});\n\t\t},\n\t\t(err) => {\n\t\t\tconsole.error(\"Failed to get local stream\", err);\n\t\t},\n\t);\n});\n```\n\n## Running tests\n\n```bash\nnpm test\n```\n\n## Browser support\n\n| [<img src=\"https://raw.githubusercontent.com/alrra/browser-logos/master/src/firefox/firefox_48x48.png\" alt=\"Firefox\" width=\"24px\" height=\"24px\" />](http://godban.github.io/browsers-support-badges/)<br/>Firefox | [<img src=\"https://raw.githubusercontent.com/alrra/browser-logos/master/src/chrome/chrome_48x48.png\" alt=\"Chrome\" width=\"24px\" height=\"24px\" />](http://godban.github.io/browsers-support-badges/)<br/>Chrome | [<img src=\"https://raw.githubusercontent.com/alrra/browser-logos/master/src/edge/edge_48x48.png\" alt=\"Safari\" width=\"24px\" height=\"24px\" />](http://godban.github.io/browsers-support-badges/)<br/>Edge | [<img src=\"https://raw.githubusercontent.com/alrra/browser-logos/master/src/safari/safari_48x48.png\" alt=\"Safari\" width=\"24px\" height=\"24px\" />](http://godban.github.io/browsers-support-badges/)<br/>Safari |\n| ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| 80+                                                                                                                                                                                                               | 83+                                                                                                                                                                                                           | 83+                                                                                                                                                                                                     | 15+                                                                                                                                                                                                           |\n\nWe test PeerJS against these versions of Chrome, Edge, Firefox, and Safari with [BrowserStack](https://www.browserstack.com) to ensure compatibility.\nIt may work in other and older browsers, but we don't officially support them.\nChanges to browser support will be a breaking change going forward.\n\n> [!NOTE]\n> Firefox 102+ is required for CBOR / MessagePack support.\n\n## FAQ\n\nQ. I have a message `Critical dependency: the request of a dependency is an expression` in browser's console\n\nA. The message occurs when you use PeerJS with Webpack. It is not critical! It relates to Parcel https://github.com/parcel-bundler/parcel/issues/2883 We'll resolve it when updated to Parcel V2.\n\n## Links\n\n### [Documentation / API Reference](https://peerjs.com/docs/)\n\n### [PeerServer](https://github.com/peers/peerjs-server)\n\n### [Discuss PeerJS on our Telegram Channel](https://t.me/joinchat/ENhPuhTvhm8WlIxTjQf7Og)\n\n### [Changelog](https://github.com/peers/peerjs/blob/master/CHANGELOG.md)\n\n## Contributors\n\nThis project exists thanks to all the people who contribute.\n<a href=\"https://github.com/peers/peerjs/graphs/contributors\"><img src=\"https://opencollective.com/peer/contributors.svg?width=890&button=false\" /></a>\n\n## Backers\n\nThank you to all our backers! [[Become a backer](https://opencollective.com/peer#backer)]\n\n<a href=\"https://opencollective.com/peer/backer/0/website?requireActive=false\" target=\"_blank\"><img src=\"https://opencollective.com/peer/backer/0/avatar.svg?requireActive=false\"/></a>\n<a href=\"https://opencollective.com/peer/backer/1/website?requireActive=false\" target=\"_blank\"><img src=\"https://opencollective.com/peer/backer/1/avatar.svg?requireActive=false\"/></a>\n<a href=\"https://opencollective.com/peer/backer/2/website?requireActive=false\" target=\"_blank\"><img src=\"https://opencollective.com/peer/backer/2/avatar.svg?requireActive=false\"/></a>\n<a href=\"https://opencollective.com/peer/backer/3/website?requireActive=false\" target=\"_blank\"><img src=\"https://opencollective.com/peer/backer/3/avatar.svg?requireActive=false\"/></a>\n<a href=\"https://opencollective.com/peer/backer/4/website?requireActive=false\" target=\"_blank\"><img src=\"https://opencollective.com/peer/backer/4/avatar.svg?requireActive=false\"/></a>\n<a href=\"https://opencollective.com/peer/backer/5/website?requireActive=false\" target=\"_blank\"><img src=\"https://opencollective.com/peer/backer/5/avatar.svg?requireActive=false\"/></a>\n<a href=\"https://opencollective.com/peer/backer/6/website?requireActive=false\" target=\"_blank\"><img src=\"https://opencollective.com/peer/backer/6/avatar.svg?requireActive=false\"/></a>\n<a href=\"https://opencollective.com/peer/backer/7/website?requireActive=false\" target=\"_blank\"><img src=\"https://opencollective.com/peer/backer/7/avatar.svg?requireActive=false\"/></a>\n<a href=\"https://opencollective.com/peer/backer/8/website?requireActive=false\" target=\"_blank\"><img src=\"https://opencollective.com/peer/backer/8/avatar.svg?requireActive=false\"/></a>\n<a href=\"https://opencollective.com/peer/backer/9/website?requireActive=false\" target=\"_blank\"><img src=\"https://opencollective.com/peer/backer/9/avatar.svg?requireActive=false\"/></a>\n<a href=\"https://opencollective.com/peer/backer/10/website?requireActive=false\" target=\"_blank\"><img src=\"https://opencollective.com/peer/backer/10/avatar.svg?requireActive=false\"/></a>\n<a href=\"https://opencollective.com/peer/backer/11/website?requireActive=false\" target=\"_blank\"><img src=\"https://opencollective.com/peer/backer/11/avatar.svg?requireActive=false\"/></a>\n<a href=\"https://opencollective.com/peer/backer/12/website?requireActive=false\" target=\"_blank\"><img src=\"https://opencollective.com/peer/backer/12/avatar.svg?requireActive=false\"/></a>\n<a href=\"https://opencollective.com/peer/backer/13/website?requireActive=false\" target=\"_blank\"><img src=\"https://opencollective.com/peer/backer/13/avatar.svg?requireActive=false\"/></a>\n<a href=\"https://opencollective.com/peer/backer/14/website?requireActive=false\" target=\"_blank\"><img src=\"https://opencollective.com/peer/backer/14/avatar.svg?requireActive=false\"/></a>\n<a href=\"https://opencollective.com/peer/backer/15/website?requireActive=false\" target=\"_blank\"><img src=\"https://opencollective.com/peer/backer/15/avatar.svg?requireActive=false\"/></a>\n<a href=\"https://opencollective.com/peer/backer/16/website?requireActive=false\" target=\"_blank\"><img src=\"https://opencollective.com/peer/backer/16/avatar.svg?requireActive=false\"/></a>\n<a href=\"https://opencollective.com/peer/backer/17/website?requireActive=false\" target=\"_blank\"><img src=\"https://opencollective.com/peer/backer/17/avatar.svg?requireActive=false\"/></a>\n<a href=\"https://opencollective.com/peer/backer/18/website?requireActive=false\" target=\"_blank\"><img src=\"https://opencollective.com/peer/backer/18/avatar.svg?requireActive=false\"/></a>\n<a href=\"https://opencollective.com/peer/backer/19/website?requireActive=false\" target=\"_blank\"><img src=\"https://opencollective.com/peer/backer/19/avatar.svg?requireActive=false\"/></a>\n<a href=\"https://opencollective.com/peer/backer/20/website?requireActive=false\" target=\"_blank\"><img src=\"https://opencollective.com/peer/backer/20/avatar.svg?requireActive=false\"/></a>\n<a href=\"https://opencollective.com/peer/backer/21/website?requireActive=false\" target=\"_blank\"><img src=\"https://opencollective.com/peer/backer/21/avatar.svg?requireActive=false\"/></a>\n<a href=\"https://opencollective.com/peer/backer/22/website?requireActive=false\" target=\"_blank\"><img src=\"https://opencollective.com/peer/backer/22/avatar.svg?requireActive=false\"/></a>\n<a href=\"https://opencollective.com/peer/backer/23/website?requireActive=false\" target=\"_blank\"><img src=\"https://opencollective.com/peer/backer/23/avatar.svg?requireActive=false\"/></a>\n<a href=\"https://opencollective.com/peer/backer/24/website?requireActive=false\" target=\"_blank\"><img src=\"https://opencollective.com/peer/backer/24/avatar.svg?requireActive=false\"/></a>\n<a href=\"https://opencollective.com/peer/backer/25/website?requireActive=false\" target=\"_blank\"><img src=\"https://opencollective.com/peer/backer/25/avatar.svg?requireActive=false\"/></a>\n<a href=\"https://opencollective.com/peer/backer/26/website?requireActive=false\" target=\"_blank\"><img src=\"https://opencollective.com/peer/backer/26/avatar.svg?requireActive=false\"/></a>\n<a href=\"https://opencollective.com/peer/backer/27/website?requireActive=false\" target=\"_blank\"><img src=\"https://opencollective.com/peer/backer/27/avatar.svg?requireActive=false\"/></a>\n<a href=\"https://opencollective.com/peer/backer/28/website?requireActive=false\" target=\"_blank\"><img src=\"https://opencollective.com/peer/backer/28/avatar.svg?requireActive=false\"/></a>\n<a href=\"https://opencollective.com/peer/backer/29/website?requireActive=false\" target=\"_blank\"><img src=\"https://opencollective.com/peer/backer/29/avatar.svg?requireActive=false\"/></a>\n<a href=\"https://opencollective.com/peer/backer/30/website?requireActive=false\" target=\"_blank\"><img src=\"https://opencollective.com/peer/backer/30/avatar.svg?requireActive=false\"/></a>\n<a href=\"https://opencollective.com/peer/backer/31/website?requireActive=false\" target=\"_blank\"><img src=\"https://opencollective.com/peer/backer/31/avatar.svg?requireActive=false\"/></a>\n<a href=\"https://opencollective.com/peer/backer/32/website?requireActive=false\" target=\"_blank\"><img src=\"https://opencollective.com/peer/backer/32/avatar.svg?requireActive=false\"/></a>\n<a href=\"https://opencollective.com/peer/backer/33/website?requireActive=false\" target=\"_blank\"><img src=\"https://opencollective.com/peer/backer/33/avatar.svg?requireActive=false\"/></a>\n<a href=\"https://opencollective.com/peer/backer/34/website?requireActive=false\" target=\"_blank\"><img src=\"https://opencollective.com/peer/backer/34/avatar.svg?requireActive=false\"/></a>\n<a href=\"https://opencollective.com/peer/backer/35/website?requireActive=false\" target=\"_blank\"><img src=\"https://opencollective.com/peer/backer/35/avatar.svg?requireActive=false\"/></a>\n<a href=\"https://opencollective.com/peer/backer/36/website?requireActive=false\" target=\"_blank\"><img src=\"https://opencollective.com/peer/backer/36/avatar.svg?requireActive=false\"/></a>\n<a href=\"https://opencollective.com/peer/backer/37/website?requireActive=false\" target=\"_blank\"><img src=\"https://opencollective.com/peer/backer/37/avatar.svg?requireActive=false\"/></a>\n<a href=\"https://opencollective.com/peer/backer/38/website?requireActive=false\" target=\"_blank\"><img src=\"https://opencollective.com/peer/backer/38/avatar.svg?requireActive=false\"/></a>\n<a href=\"https://opencollective.com/peer/backer/39/website?requireActive=false\" target=\"_blank\"><img src=\"https://opencollective.com/peer/backer/39/avatar.svg?requireActive=false\"/></a>\n<a href=\"https://opencollective.com/peer/backer/40/website?requireActive=false\" target=\"_blank\"><img src=\"https://opencollective.com/peer/backer/40/avatar.svg?requireActive=false\"/></a>\n<a href=\"https://opencollective.com/peer/backer/41/website?requireActive=false\" target=\"_blank\"><img src=\"https://opencollective.com/peer/backer/41/avatar.svg?requireActive=false\"/></a>\n<a href=\"https://opencollective.com/peer/backer/42/website?requireActive=false\" target=\"_blank\"><img src=\"https://opencollective.com/peer/backer/42/avatar.svg?requireActive=false\"/></a>\n<a href=\"https://opencollective.com/peer/backer/43/website?requireActive=false\" target=\"_blank\"><img src=\"https://opencollective.com/peer/backer/43/avatar.svg?requireActive=false\"/></a>\n<a href=\"https://opencollective.com/peer/backer/44/website?requireActive=false\" target=\"_blank\"><img src=\"https://opencollective.com/peer/backer/44/avatar.svg?requireActive=false\"/></a>\n<a href=\"https://opencollective.com/peer/backer/45/website?requireActive=false\" target=\"_blank\"><img src=\"https://opencollective.com/peer/backer/45/avatar.svg?requireActive=false\"/></a>\n<a href=\"https://opencollective.com/peer/backer/46/website?requireActive=false\" target=\"_blank\"><img src=\"https://opencollective.com/peer/backer/46/avatar.svg?requireActive=false\"/></a>\n<a href=\"https://opencollective.com/peer/backer/47/website?requireActive=false\" target=\"_blank\"><img src=\"https://opencollective.com/peer/backer/47/avatar.svg?requireActive=false\"/></a>\n<a href=\"https://opencollective.com/peer/backer/48/website?requireActive=false\" target=\"_blank\"><img src=\"https://opencollective.com/peer/backer/48/avatar.svg?requireActive=false\"/></a>\n<a href=\"https://opencollective.com/peer/backer/49/website?requireActive=false\" target=\"_blank\"><img src=\"https://opencollective.com/peer/backer/49/avatar.svg?requireActive=false\"/></a>\n<a href=\"https://opencollective.com/peer/backer/50/website?requireActive=false\" target=\"_blank\"><img src=\"https://opencollective.com/peer/backer/50/avatar.svg?requireActive=false\"/></a>\n<a href=\"https://opencollective.com/peer/backer/51/website?requireActive=false\" target=\"_blank\"><img src=\"https://opencollective.com/peer/backer/51/avatar.svg?requireActive=false\"/></a>\n<a href=\"https://opencollective.com/peer/backer/52/website?requireActive=false\" target=\"_blank\"><img src=\"https://opencollective.com/peer/backer/52/avatar.svg?requireActive=false\"/></a>\n<a href=\"https://opencollective.com/peer/backer/53/website?requireActive=false\" target=\"_blank\"><img src=\"https://opencollective.com/peer/backer/53/avatar.svg?requireActive=false\"/></a>\n<a href=\"https://opencollective.com/peer/backer/54/website?requireActive=false\" target=\"_blank\"><img src=\"https://opencollective.com/peer/backer/54/avatar.svg?requireActive=false\"/></a>\n<a href=\"https://opencollective.com/peer/backer/55/website?requireActive=false\" target=\"_blank\"><img src=\"https://opencollective.com/peer/backer/55/avatar.svg?requireActive=false\"/></a>\n<a href=\"https://opencollective.com/peer/backer/56/website?requireActive=false\" target=\"_blank\"><img src=\"https://opencollective.com/peer/backer/56/avatar.svg?requireActive=false\"/></a>\n<a href=\"https://opencollective.com/peer/backer/57/website?requireActive=false\" target=\"_blank\"><img src=\"https://opencollective.com/peer/backer/57/avatar.svg?requireActive=false\"/></a>\n<a href=\"https://opencollective.com/peer/backer/58/website?requireActive=false\" target=\"_blank\"><img src=\"https://opencollective.com/peer/backer/58/avatar.svg?requireActive=false\"/></a>\n<a href=\"https://opencollective.com/peer/backer/59/website?requireActive=false\" target=\"_blank\"><img src=\"https://opencollective.com/peer/backer/59/avatar.svg?requireActive=false\"/></a>\n<a href=\"https://opencollective.com/peer/backer/60/website?requireActive=false\" target=\"_blank\"><img src=\"https://opencollective.com/peer/backer/60/avatar.svg?requireActive=false\"/></a>\n<a href=\"https://opencollective.com/peer/backer/61/website?requireActive=false\" target=\"_blank\"><img src=\"https://opencollective.com/peer/backer/61/avatar.svg?requireActive=false\"/></a>\n<a href=\"https://opencollective.com/peer/backer/62/website?requireActive=false\" target=\"_blank\"><img src=\"https://opencollective.com/peer/backer/62/avatar.svg?requireActive=false\"/></a>\n<a href=\"https://opencollective.com/peer/backer/63/website?requireActive=false\" target=\"_blank\"><img src=\"https://opencollective.com/peer/backer/63/avatar.svg?requireActive=false\"/></a>\n<a href=\"https://opencollective.com/peer/backer/64/website?requireActive=false\" target=\"_blank\"><img src=\"https://opencollective.com/peer/backer/64/avatar.svg?requireActive=false\"/></a>\n<a href=\"https://opencollective.com/peer/backer/65/website?requireActive=false\" target=\"_blank\"><img src=\"https://opencollective.com/peer/backer/65/avatar.svg?requireActive=false\"/></a>\n<a href=\"https://opencollective.com/peer/backer/66/website?requireActive=false\" target=\"_blank\"><img src=\"https://opencollective.com/peer/backer/66/avatar.svg?requireActive=false\"/></a>\n<a href=\"https://opencollective.com/peer/backer/67/website?requireActive=false\" target=\"_blank\"><img src=\"https://opencollective.com/peer/backer/67/avatar.svg?requireActive=false\"/></a>\n<a href=\"https://opencollective.com/peer/backer/68/website?requireActive=false\" target=\"_blank\"><img src=\"https://opencollective.com/peer/backer/68/avatar.svg?requireActive=false\"/></a>\n<a href=\"https://opencollective.com/peer/backer/69/website?requireActive=false\" target=\"_blank\"><img src=\"https://opencollective.com/peer/backer/69/avatar.svg?requireActive=false\"/></a>\n<a href=\"https://opencollective.com/peer/backer/70/website?requireActive=false\" target=\"_blank\"><img src=\"https://opencollective.com/peer/backer/70/avatar.svg?requireActive=false\"/></a>\n<a href=\"https://opencollective.com/peer/backer/71/website?requireActive=false\" target=\"_blank\"><img src=\"https://opencollective.com/peer/backer/71/avatar.svg?requireActive=false\"/></a>\n<a href=\"https://opencollective.com/peer/backer/72/website?requireActive=false\" target=\"_blank\"><img src=\"https://opencollective.com/peer/backer/72/avatar.svg?requireActive=false\"/></a>\n<a href=\"https://opencollective.com/peer/backer/73/website?requireActive=false\" target=\"_blank\"><img src=\"https://opencollective.com/peer/backer/73/avatar.svg?requireActive=false\"/></a>\n<a href=\"https://opencollective.com/peer/backer/74/website?requireActive=false\" target=\"_blank\"><img src=\"https://opencollective.com/peer/backer/74/avatar.svg?requireActive=false\"/></a>\n<a href=\"https://opencollective.com/peer/backer/75/website?requireActive=false\" target=\"_blank\"><img src=\"https://opencollective.com/peer/backer/75/avatar.svg?requireActive=false\"/></a>\n<a href=\"https://opencollective.com/peer/backer/76/website?requireActive=false\" target=\"_blank\"><img src=\"https://opencollective.com/peer/backer/76/avatar.svg?requireActive=false\"/></a>\n<a href=\"https://opencollective.com/peer/backer/77/website?requireActive=false\" target=\"_blank\"><img src=\"https://opencollective.com/peer/backer/77/avatar.svg?requireActive=false\"/></a>\n<a href=\"https://opencollective.com/peer/backer/78/website?requireActive=false\" target=\"_blank\"><img src=\"https://opencollective.com/peer/backer/78/avatar.svg?requireActive=false\"/></a>\n<a href=\"https://opencollective.com/peer/backer/79/website?requireActive=false\" target=\"_blank\"><img src=\"https://opencollective.com/peer/backer/79/avatar.svg?requireActive=false\"/></a>\n<a href=\"https://opencollective.com/peer/backer/80/website?requireActive=false\" target=\"_blank\"><img src=\"https://opencollective.com/peer/backer/80/avatar.svg?requireActive=false\"/></a>\n<a href=\"https://opencollective.com/peer/backer/81/website?requireActive=false\" target=\"_blank\"><img src=\"https://opencollective.com/peer/backer/81/avatar.svg?requireActive=false\"/></a>\n<a href=\"https://opencollective.com/peer/backer/82/website?requireActive=false\" target=\"_blank\"><img src=\"https://opencollective.com/peer/backer/82/avatar.svg?requireActive=false\"/></a>\n<a href=\"https://opencollective.com/peer/backer/83/website?requireActive=false\" target=\"_blank\"><img src=\"https://opencollective.com/peer/backer/83/avatar.svg?requireActive=false\"/></a>\n<a href=\"https://opencollective.com/peer/backer/84/website?requireActive=false\" target=\"_blank\"><img src=\"https://opencollective.com/peer/backer/84/avatar.svg?requireActive=false\"/></a>\n<a href=\"https://opencollective.com/peer/backer/85/website?requireActive=false\" target=\"_blank\"><img src=\"https://opencollective.com/peer/backer/85/avatar.svg?requireActive=false\"/></a>\n<a href=\"https://opencollective.com/peer/backer/86/website?requireActive=false\" target=\"_blank\"><img src=\"https://opencollective.com/peer/backer/86/avatar.svg?requireActive=false\"/></a>\n<a href=\"https://opencollective.com/peer/backer/87/website?requireActive=false\" target=\"_blank\"><img src=\"https://opencollective.com/peer/backer/87/avatar.svg?requireActive=false\"/></a>\n<a href=\"https://opencollective.com/peer/backer/88/website?requireActive=false\" target=\"_blank\"><img src=\"https://opencollective.com/peer/backer/88/avatar.svg?requireActive=false\"/></a>\n<a href=\"https://opencollective.com/peer/backer/89/website?requireActive=false\" target=\"_blank\"><img src=\"https://opencollective.com/peer/backer/89/avatar.svg?requireActive=false\"/></a>\n<a href=\"https://opencollective.com/peer/backer/90/website?requireActive=false\" target=\"_blank\"><img src=\"https://opencollective.com/peer/backer/90/avatar.svg?requireActive=false\"/></a>\n<a href=\"https://opencollective.com/peer/backer/91/website?requireActive=false\" target=\"_blank\"><img src=\"https://opencollective.com/peer/backer/91/avatar.svg?requireActive=false\"/></a>\n<a href=\"https://opencollective.com/peer/backer/92/website?requireActive=false\" target=\"_blank\"><img src=\"https://opencollective.com/peer/backer/92/avatar.svg?requireActive=false\"/></a>\n<a href=\"https://opencollective.com/peer/backer/93/website?requireActive=false\" target=\"_blank\"><img src=\"https://opencollective.com/peer/backer/93/avatar.svg?requireActive=false\"/></a>\n<a href=\"https://opencollective.com/peer/backer/94/website?requireActive=false\" target=\"_blank\"><img src=\"https://opencollective.com/peer/backer/94/avatar.svg?requireActive=false\"/></a>\n<a href=\"https://opencollective.com/peer/backer/95/website?requireActive=false\" target=\"_blank\"><img src=\"https://opencollective.com/peer/backer/95/avatar.svg?requireActive=false\"/></a>\n<a href=\"https://opencollective.com/peer/backer/96/website?requireActive=false\" target=\"_blank\"><img src=\"https://opencollective.com/peer/backer/96/avatar.svg?requireActive=false\"/></a>\n<a href=\"https://opencollective.com/peer/backer/97/website?requireActive=false\" target=\"_blank\"><img src=\"https://opencollective.com/peer/backer/97/avatar.svg?requireActive=false\"/></a>\n<a href=\"https://opencollective.com/peer/backer/98/website?requireActive=false\" target=\"_blank\"><img src=\"https://opencollective.com/peer/backer/98/avatar.svg?requireActive=false\"/></a>\n<a href=\"https://opencollective.com/peer/backer/99/website?requireActive=false\" target=\"_blank\"><img src=\"https://opencollective.com/peer/backer/99/avatar.svg?requireActive=false\"/></a>\n<a href=\"https://opencollective.com/peer/backer/100/website?requireActive=false\" target=\"_blank\"><img src=\"https://opencollective.com/peer/backer/100/avatar.svg?requireActive=false\"/></a>\n\n## Sponsors\n\nSupport this project by becoming a sponsor. Your logo will show up here with a link to your website. [[Become a sponsor](https://opencollective.com/peer#sponsor)]\n\n<a href=\"https://opencollective.com/peer/sponsor/1/website\" target=\"_blank\"><img src=\"https://opencollective.com/peer/sponsor/1/avatar.svg\"/></a>\n<a href=\"https://opencollective.com/peer/sponsor/2/website\" target=\"_blank\"><img src=\"https://opencollective.com/peer/sponsor/2/avatar.svg\"/></a>\n<a href=\"https://opencollective.com/peer/sponsor/0/website\" target=\"_blank\"><img src=\"https://opencollective.com/peer/sponsor/0/avatar.svg\"/></a>\n<a href=\"https://opencollective.com/peer/sponsor/3/website\" target=\"_blank\"><img src=\"https://opencollective.com/peer/sponsor/3/avatar.svg\"/></a>\n<a href=\"https://opencollective.com/peer/sponsor/4/website\" target=\"_blank\"><img src=\"https://opencollective.com/peer/sponsor/4/avatar.svg\"/></a>\n<a href=\"https://opencollective.com/peer/sponsor/5/website\" target=\"_blank\"><img src=\"https://opencollective.com/peer/sponsor/5/avatar.svg\"/></a>\n<a href=\"https://opencollective.com/peer/sponsor/6/website\" target=\"_blank\"><img src=\"https://opencollective.com/peer/sponsor/6/avatar.svg\"/></a>\n<a href=\"https://opencollective.com/peer/sponsor/7/website\" target=\"_blank\"><img src=\"https://opencollective.com/peer/sponsor/7/avatar.svg\"/></a>\n<a href=\"https://opencollective.com/peer/sponsor/8/website\" target=\"_blank\"><img src=\"https://opencollective.com/peer/sponsor/8/avatar.svg\"/></a>\n<a href=\"https://opencollective.com/peer/sponsor/9/website\" target=\"_blank\"><img src=\"https://opencollective.com/peer/sponsor/9/avatar.svg\"/></a>\n\n## License\n\nPeerJS is licensed under the [MIT License](https://tldrlegal.com/l/mit).\n"
  },
  {
    "path": "__test__/faker.ts",
    "content": "import { WebSocket } from \"mock-socket\";\nimport \"webrtc-adapter\";\n\nconst fakeGlobals = {\n\tWebSocket,\n\tMediaStream: class MediaStream {\n\t\tprivate readonly _tracks: MediaStreamTrack[] = [];\n\n\t\tconstructor(tracks?: MediaStreamTrack[]) {\n\t\t\tif (tracks) {\n\t\t\t\tthis._tracks = tracks;\n\t\t\t}\n\t\t}\n\n\t\tgetTracks(): MediaStreamTrack[] {\n\t\t\treturn this._tracks;\n\t\t}\n\n\t\taddTrack(track: MediaStreamTrack) {\n\t\t\tthis._tracks.push(track);\n\t\t}\n\t},\n\tMediaStreamTrack: class MediaStreamTrack {\n\t\tkind: string;\n\t\tid: string;\n\n\t\tprivate static _idCounter = 0;\n\n\t\tconstructor() {\n\t\t\tthis.id = `track#${fakeGlobals.MediaStreamTrack._idCounter++}`;\n\t\t}\n\t},\n\tRTCPeerConnection: class RTCPeerConnection {\n\t\tprivate _senders: RTCRtpSender[] = [];\n\n\t\tclose() {}\n\n\t\taddTrack(track: MediaStreamTrack, ..._stream: MediaStream[]): RTCRtpSender {\n\t\t\tconst newSender = new RTCRtpSender();\n\t\t\tnewSender.replaceTrack(track);\n\n\t\t\tthis._senders.push(newSender);\n\n\t\t\treturn newSender;\n\t\t}\n\n\t\t// removeTrack(_: RTCRtpSender): void { }\n\n\t\tgetSenders(): RTCRtpSender[] {\n\t\t\treturn this._senders;\n\t\t}\n\t},\n\tRTCRtpSender: class RTCRtpSender {\n\t\treadonly dtmf: RTCDTMFSender | null;\n\t\treadonly rtcpTransport: RTCDtlsTransport | null;\n\t\ttrack: MediaStreamTrack | null;\n\t\treadonly transport: RTCDtlsTransport | null;\n\n\t\treplaceTrack(withTrack: MediaStreamTrack | null): Promise<void> {\n\t\t\tthis.track = withTrack;\n\n\t\t\treturn Promise.resolve();\n\t\t}\n\t},\n};\n\nObject.assign(global, fakeGlobals);\nObject.assign(window, fakeGlobals);\n"
  },
  {
    "path": "__test__/logger.spec.ts",
    "content": "import Logger, { LogLevel } from \"../lib/logger\";\nimport { expect, beforeAll, afterAll, describe, it } from \"@jest/globals\";\n\ndescribe(\"Logger\", () => {\n\tlet oldLoggerPrint;\n\tbeforeAll(() => {\n\t\t//@ts-ignore\n\t\toldLoggerPrint = Logger._print;\n\t});\n\n\tit(\"should be disabled by default\", () => {\n\t\texpect(Logger.logLevel).toBe(LogLevel.Disabled);\n\t});\n\n\tit(\"should be accept new log level\", () => {\n\t\tconst checkedLevels = [];\n\n\t\tLogger.setLogFunction((logLevel) => {\n\t\t\tcheckedLevels.push(logLevel);\n\t\t});\n\n\t\tLogger.logLevel = LogLevel.Warnings;\n\n\t\texpect(Logger.logLevel).toBe(LogLevel.Warnings);\n\n\t\tLogger.log(\"\");\n\t\tLogger.warn(\"\");\n\t\tLogger.error(\"\");\n\n\t\texpect(checkedLevels).toEqual([LogLevel.Warnings, LogLevel.Errors]);\n\t});\n\n\tit(\"should accept new log function\", () => {\n\t\tLogger.logLevel = LogLevel.All;\n\n\t\tconst checkedLevels = [];\n\t\tconst testMessage = \"test it\";\n\n\t\tLogger.setLogFunction((logLevel, ...args) => {\n\t\t\tcheckedLevels.push(logLevel);\n\n\t\t\texpect(args[0]).toBe(testMessage);\n\t\t});\n\n\t\tLogger.log(testMessage);\n\t\tLogger.warn(testMessage);\n\t\tLogger.error(testMessage);\n\n\t\texpect(checkedLevels).toEqual([\n\t\t\tLogLevel.All,\n\t\t\tLogLevel.Warnings,\n\t\t\tLogLevel.Errors,\n\t\t]);\n\t});\n\n\tafterAll(() => {\n\t\tLogger.setLogFunction(oldLoggerPrint);\n\t});\n});\n"
  },
  {
    "path": "__test__/peer.spec.ts",
    "content": "import \"./setup\";\nimport { Peer } from \"../lib/peer\";\nimport { Server } from \"mock-socket\";\nimport { ConnectionType, PeerErrorType, ServerMessageType } from \"../lib/enums\";\nimport { expect, beforeAll, afterAll, describe, it } from \"@jest/globals\";\n\nconst createMockServer = (): Server => {\n\tconst fakeURL = \"ws://localhost:8080/peerjs?key=peerjs&id=1&token=testToken\";\n\tconst mockServer = new Server(fakeURL);\n\n\tmockServer.on(\"connection\", (socket) => {\n\t\t//@ts-ignore\n\t\tsocket.on(\"message\", (data) => {\n\t\t\tsocket.send(\"test message from mock server\");\n\t\t});\n\n\t\tsocket.send(JSON.stringify({ type: ServerMessageType.Open }));\n\t});\n\n\treturn mockServer;\n};\ndescribe(\"Peer\", () => {\n\tdescribe(\"after construct without parameters\", () => {\n\t\tit(\"shouldn't contains any connection\", () => {\n\t\t\tconst peer = new Peer();\n\n\t\t\texpect(peer.open).toBe(false);\n\t\t\texpect(peer.connections).toEqual({});\n\t\t\texpect(peer.id).toBeNull();\n\t\t\texpect(peer.disconnected).toBe(false);\n\t\t\texpect(peer.destroyed).toBe(false);\n\n\t\t\tpeer.destroy();\n\t\t});\n\t});\n\n\tdescribe(\"after construct with parameters\", () => {\n\t\tit(\"should contains id and key\", () => {\n\t\t\tconst peer = new Peer(\"1\", { key: \"anotherKey\" });\n\n\t\t\texpect(peer.id).toBe(\"1\");\n\t\t\texpect(peer.options.key).toBe(\"anotherKey\");\n\n\t\t\tpeer.destroy();\n\t\t});\n\t});\n\n\tdescribe.skip(\"after call to peer #2\", () => {\n\t\tlet mockServer;\n\n\t\tbeforeAll(() => {\n\t\t\tmockServer = createMockServer();\n\t\t});\n\n\t\tit(\"Peer#1 should has id #1\", (done) => {\n\t\t\tconst peer1 = new Peer(\"1\", { port: 8080, host: \"localhost\" });\n\t\t\texpect(peer1.open).toBe(false);\n\n\t\t\tconst mediaOptions = {\n\t\t\t\tmetadata: { var: \"123\" },\n\t\t\t\tconstraints: {\n\t\t\t\t\tmandatory: {\n\t\t\t\t\t\tOfferToReceiveAudio: true,\n\t\t\t\t\t\tOfferToReceiveVideo: true,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t};\n\n\t\t\tconst track = new MediaStreamTrack();\n\t\t\tconst mediaStream = new MediaStream([track]);\n\n\t\t\tconst mediaConnection = peer1.call(\"2\", mediaStream, { ...mediaOptions });\n\n\t\t\texpect(typeof mediaConnection.connectionId).toBe(\"string\");\n\t\t\texpect(mediaConnection.type).toBe(ConnectionType.Media);\n\t\t\texpect(mediaConnection.peer).toBe(\"2\");\n\t\t\texpect(mediaConnection.options).toEqual(\n\t\t\t\t// expect.arrayContaining([mediaOptions]),mediaOptions\n\t\t\t\texpect.objectContaining(mediaOptions),\n\t\t\t);\n\t\t\texpect(mediaConnection.metadata).toEqual(mediaOptions.metadata);\n\t\t\texpect(mediaConnection.peerConnection.getSenders()[0].track.id).toBe(\n\t\t\t\ttrack.id,\n\t\t\t);\n\n\t\t\tpeer1.once(\"open\", (id) => {\n\t\t\t\texpect(id).toBe(\"1\");\n\t\t\t\t//@ts-ignore\n\t\t\t\texpect(peer1._lastServerId).toBe(\"1\");\n\t\t\t\texpect(peer1.disconnected).toBe(false);\n\t\t\t\texpect(peer1.destroyed).toBe(false);\n\t\t\t\texpect(peer1.open).toBe(true);\n\n\t\t\t\tpeer1.destroy();\n\n\t\t\t\texpect(peer1.disconnected).toBe(true);\n\t\t\t\texpect(peer1.destroyed).toBe(true);\n\t\t\t\texpect(peer1.open).toBe(false);\n\t\t\t\texpect(peer1.connections).toEqual({});\n\n\t\t\t\tdone();\n\t\t\t});\n\t\t});\n\n\t\tafterAll(() => {\n\t\t\tmockServer.stop();\n\t\t});\n\t});\n\n\tdescribe(\"reconnect\", () => {\n\t\tlet mockServer;\n\n\t\tbeforeAll(() => {\n\t\t\tmockServer = createMockServer();\n\t\t});\n\n\t\tit(\"connect to server => disconnect => reconnect => destroy\", (done) => {\n\t\t\tconst peer1 = new Peer(\"1\", { port: 8080, host: \"localhost\" });\n\n\t\t\tpeer1.once(\"open\", () => {\n\t\t\t\texpect(peer1.open).toBe(true);\n\n\t\t\t\tpeer1.once(\"disconnected\", () => {\n\t\t\t\t\texpect(peer1.disconnected).toBe(true);\n\t\t\t\t\texpect(peer1.destroyed).toBe(false);\n\t\t\t\t\texpect(peer1.open).toBe(false);\n\n\t\t\t\t\tpeer1.once(\"open\", (id) => {\n\t\t\t\t\t\texpect(id).toBe(\"1\");\n\t\t\t\t\t\texpect(peer1.disconnected).toBe(false);\n\t\t\t\t\t\texpect(peer1.destroyed).toBe(false);\n\t\t\t\t\t\texpect(peer1.open).toBe(true);\n\n\t\t\t\t\t\tpeer1.once(\"disconnected\", () => {\n\t\t\t\t\t\t\texpect(peer1.disconnected).toBe(true);\n\t\t\t\t\t\t\texpect(peer1.destroyed).toBe(false);\n\t\t\t\t\t\t\texpect(peer1.open).toBe(false);\n\n\t\t\t\t\t\t\tpeer1.once(\"close\", () => {\n\t\t\t\t\t\t\t\texpect(peer1.disconnected).toBe(true);\n\t\t\t\t\t\t\t\texpect(peer1.destroyed).toBe(true);\n\t\t\t\t\t\t\t\texpect(peer1.open).toBe(false);\n\n\t\t\t\t\t\t\t\tdone();\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t});\n\n\t\t\t\t\t\tpeer1.destroy();\n\t\t\t\t\t});\n\n\t\t\t\t\tpeer1.reconnect();\n\t\t\t\t});\n\n\t\t\t\tpeer1.disconnect();\n\t\t\t});\n\t\t});\n\n\t\tit(\"disconnect => reconnect => destroy\", (done) => {\n\t\t\tmockServer.stop();\n\n\t\t\tconst peer1 = new Peer(\"1\", { port: 8080, host: \"localhost\" });\n\n\t\t\tpeer1.once(\"disconnected\", (id) => {\n\t\t\t\texpect(id).toBe(\"1\");\n\t\t\t\texpect(peer1.disconnected).toBe(true);\n\t\t\t\texpect(peer1.destroyed).toBe(false);\n\t\t\t\texpect(peer1.open).toBe(false);\n\n\t\t\t\tpeer1.once(\"open\", (id) => {\n\t\t\t\t\texpect(id).toBe(\"1\");\n\t\t\t\t\texpect(peer1.disconnected).toBe(false);\n\t\t\t\t\texpect(peer1.destroyed).toBe(false);\n\t\t\t\t\texpect(peer1.open).toBe(true);\n\n\t\t\t\t\tpeer1.once(\"disconnected\", () => {\n\t\t\t\t\t\texpect(peer1.disconnected).toBe(true);\n\t\t\t\t\t\texpect(peer1.destroyed).toBe(false);\n\t\t\t\t\t\texpect(peer1.open).toBe(false);\n\n\t\t\t\t\t\tpeer1.once(\"close\", () => {\n\t\t\t\t\t\t\texpect(peer1.disconnected).toBe(true);\n\t\t\t\t\t\t\texpect(peer1.destroyed).toBe(true);\n\t\t\t\t\t\t\texpect(peer1.open).toBe(false);\n\n\t\t\t\t\t\t\tdone();\n\t\t\t\t\t\t});\n\t\t\t\t\t});\n\n\t\t\t\t\tpeer1.destroy();\n\t\t\t\t});\n\n\t\t\t\tmockServer = createMockServer();\n\n\t\t\t\tpeer1.reconnect();\n\t\t\t});\n\t\t});\n\n\t\tit(\"destroy peer if no id and no connection\", (done) => {\n\t\t\tmockServer.stop();\n\n\t\t\tconst peer1 = new Peer({ port: 8080, host: \"localhost\" });\n\n\t\t\tpeer1.once(\"error\", (error) => {\n\t\t\t\texpect(error.type).toBe(PeerErrorType.ServerError);\n\n\t\t\t\tpeer1.once(\"close\", () => {\n\t\t\t\t\texpect(peer1.disconnected).toBe(true);\n\t\t\t\t\texpect(peer1.destroyed).toBe(true);\n\t\t\t\t\texpect(peer1.open).toBe(false);\n\n\t\t\t\t\tdone();\n\t\t\t\t});\n\n\t\t\t\tmockServer = createMockServer();\n\t\t\t});\n\t\t});\n\n\t\tafterAll(() => {\n\t\t\tmockServer.stop();\n\t\t});\n\t});\n});\n"
  },
  {
    "path": "__test__/setup.ts",
    "content": "import \"./faker\";\nimport { util } from \"../lib/util\";\n\n//enable support for WebRTC\nutil.supports.audioVideo = true;\nutil.randomToken = () => \"testToken\";\n"
  },
  {
    "path": "__test__/util.spec.ts",
    "content": "import \"./setup\";\nimport { util } from \"../lib/util\";\nimport { expect, describe, it } from \"@jest/globals\";\n\ndescribe(\"util\", () => {\n\tdescribe(\"#chunkedMTU\", () => {\n\t\tit(\"should be 16300\", () => {\n\t\t\texpect(util.chunkedMTU).toBe(16300);\n\t\t});\n\t});\n});\n"
  },
  {
    "path": "e2e/.eslintrc",
    "content": "{\n\t\"env\": {\n\t\t\"browser\": true\n\t}\n}\n"
  },
  {
    "path": "e2e/alice.html",
    "content": "<html>\n\t<head>\n\t\t<title>Alice</title>\n\t</head>\n</html>\n"
  },
  {
    "path": "e2e/bob.html",
    "content": "<html>\n\t<head>\n\t\t<title>Bob</title>\n\t</head>\n</html>\n"
  },
  {
    "path": "e2e/commit_data.js",
    "content": "export const commit_data = [\n\t{\n\t\tcreated_at: \"2013-11-07T19:41:25-08:00\",\n\t\tpayload: { issue_id: 22111847, comment_id: 28032260 },\n\t\tpublic: true,\n\t\ttype: \"IssueCommentEvent\",\n\t\turl: \"https://github.com/peers/peerjs/issues/101#issuecomment-28032260\",\n\t\tactor: \"ericz\",\n\t\tactor_attributes: {\n\t\t\tlogin: \"ericz\",\n\t\t\ttype: \"User\",\n\t\t\tgravatar_id: \"c584ef7fe434331f7068ea49cacd88b9\",\n\t\t\tname: \"Eric Zhang\",\n\t\t\tcompany: \"Lever\",\n\t\t\tblog: \"https://twitter.com/reallyez\",\n\t\t\tlocation: \"Berkeley\",\n\t\t\temail: \"eric@ericzhang.com\",\n\t\t},\n\t\trepository: {\n\t\t\tid: 7292898,\n\t\t\tname: \"peerjs\",\n\t\t\turl: \"https://github.com/peers/peerjs\",\n\t\t\tdescription: \"Peer-to-peer data in the browser.\",\n\t\t\thomepage: \"https://peerjs.com\",\n\t\t\twatchers: 1647,\n\t\t\tstargazers: 1647,\n\t\t\tforks: 145,\n\t\t\tfork: false,\n\t\t\tsize: 2188,\n\t\t\towner: \"peers\",\n\t\t\tprivate: false,\n\t\t\topen_issues: 20,\n\t\t\thas_issues: true,\n\t\t\thas_downloads: true,\n\t\t\thas_wiki: true,\n\t\t\tlanguage: \"JavaScript\",\n\t\t\tcreated_at: \"2012-12-22T23:28:47-08:00\",\n\t\t\tpushed_at: \"2013-11-09T22:58:31-08:00\",\n\t\t\tmaster_branch: \"master\",\n\t\t\torganization: \"peers\",\n\t\t},\n\t},\n\t{\n\t\tcreated_at: \"2013-11-07T19:35:50-08:00\",\n\t\tpayload: { issue_id: 22196839, comment_id: 28031970 },\n\t\tpublic: true,\n\t\ttype: \"IssueCommentEvent\",\n\t\turl: \"https://github.com/peers/peerjs/issues/103#issuecomment-28031970\",\n\t\tactor: \"ericz\",\n\t\tactor_attributes: {\n\t\t\tlogin: \"ericz\",\n\t\t\ttype: \"User\",\n\t\t\tgravatar_id: \"c584ef7fe434331f7068ea49cacd88b9\",\n\t\t\tname: \"Eric Zhang\",\n\t\t\tcompany: \"Lever\",\n\t\t\tblog: \"https://twitter.com/reallyez\",\n\t\t\tlocation: \"Berkeley\",\n\t\t\temail: \"eric@ericzhang.com\",\n\t\t},\n\t\trepository: {\n\t\t\tid: 7292898,\n\t\t\tname: \"peerjs\",\n\t\t\turl: \"https://github.com/peers/peerjs\",\n\t\t\tdescription: \"Peer-to-peer data in the browser.\",\n\t\t\thomepage: \"https://peerjs.com\",\n\t\t\twatchers: 1647,\n\t\t\tstargazers: 1647,\n\t\t\tforks: 145,\n\t\t\tfork: false,\n\t\t\tsize: 2188,\n\t\t\towner: \"peers\",\n\t\t\tprivate: false,\n\t\t\topen_issues: 20,\n\t\t\thas_issues: true,\n\t\t\thas_downloads: true,\n\t\t\thas_wiki: true,\n\t\t\tlanguage: \"JavaScript\",\n\t\t\tcreated_at: \"2012-12-22T23:28:47-08:00\",\n\t\t\tpushed_at: \"2013-11-09T22:58:31-08:00\",\n\t\t\tmaster_branch: \"master\",\n\t\t\torganization: \"peers\",\n\t\t},\n\t},\n\t{\n\t\tcreated_at: \"2013-11-02T15:18:51-07:00\",\n\t\tpayload: {\n\t\t\tref: \"0.3.3\",\n\t\t\tref_type: \"tag\",\n\t\t\tmaster_branch: \"master\",\n\t\t\tdescription: \"Peer-to-peer data in the browser.\",\n\t\t},\n\t\tpublic: true,\n\t\ttype: \"CreateEvent\",\n\t\turl: \"https://github.com/peers/peerjs/tree/0.3.3\",\n\t\tactor: \"ericz\",\n\t\tactor_attributes: {\n\t\t\tlogin: \"ericz\",\n\t\t\ttype: \"User\",\n\t\t\tgravatar_id: \"c584ef7fe434331f7068ea49cacd88b9\",\n\t\t\tname: \"Eric Zhang\",\n\t\t\tcompany: \"Lever\",\n\t\t\tblog: \"https://twitter.com/reallyez\",\n\t\t\tlocation: \"Berkeley\",\n\t\t\temail: \"eric@ericzhang.com\",\n\t\t},\n\t\trepository: {\n\t\t\tid: 7292898,\n\t\t\tname: \"peerjs\",\n\t\t\turl: \"https://github.com/peers/peerjs\",\n\t\t\tdescription: \"Peer-to-peer data in the browser.\",\n\t\t\thomepage: \"https://peerjs.com\",\n\t\t\twatchers: 1647,\n\t\t\tstargazers: 1647,\n\t\t\tforks: 145,\n\t\t\tfork: false,\n\t\t\tsize: 2188,\n\t\t\towner: \"peers\",\n\t\t\tprivate: false,\n\t\t\topen_issues: 20,\n\t\t\thas_issues: true,\n\t\t\thas_downloads: true,\n\t\t\thas_wiki: true,\n\t\t\tlanguage: \"JavaScript\",\n\t\t\tcreated_at: \"2012-12-22T23:28:47-08:00\",\n\t\t\tpushed_at: \"2013-11-09T22:58:31-08:00\",\n\t\t\tmaster_branch: \"master\",\n\t\t\torganization: \"peers\",\n\t\t},\n\t},\n\t{\n\t\tcreated_at: \"2013-11-02T15:18:49-07:00\",\n\t\tpayload: {\n\t\t\tshas: [\n\t\t\t\t[\n\t\t\t\t\t\"9976990c61c0f9c3c44f1de2a997ff8f21013d2a\",\n\t\t\t\t\t\"really.ez@gmail.com\",\n\t\t\t\t\t\"Bump to 0.3.3\",\n\t\t\t\t\t\"ericz\",\n\t\t\t\t\ttrue,\n\t\t\t\t],\n\t\t\t],\n\t\t\tsize: 1,\n\t\t\tref: \"refs/heads/master\",\n\t\t\thead: \"9976990c61c0f9c3c44f1de2a997ff8f21013d2a\",\n\t\t},\n\t\tpublic: true,\n\t\ttype: \"PushEvent\",\n\t\turl: \"https://github.com/peers/peerjs/compare/c9adf5076e...9976990c61\",\n\t\tactor: \"ericz\",\n\t\tactor_attributes: {\n\t\t\tlogin: \"ericz\",\n\t\t\ttype: \"User\",\n\t\t\tgravatar_id: \"c584ef7fe434331f7068ea49cacd88b9\",\n\t\t\tname: \"Eric Zhang\",\n\t\t\tcompany: \"Lever\",\n\t\t\tblog: \"https://twitter.com/reallyez\",\n\t\t\tlocation: \"Berkeley\",\n\t\t\temail: \"eric@ericzhang.com\",\n\t\t},\n\t\trepository: {\n\t\t\tid: 7292898,\n\t\t\tname: \"peerjs\",\n\t\t\turl: \"https://github.com/peers/peerjs\",\n\t\t\tdescription: \"Peer-to-peer data in the browser.\",\n\t\t\thomepage: \"https://peerjs.com\",\n\t\t\twatchers: 1647,\n\t\t\tstargazers: 1647,\n\t\t\tforks: 145,\n\t\t\tfork: false,\n\t\t\tsize: 2188,\n\t\t\towner: \"peers\",\n\t\t\tprivate: false,\n\t\t\topen_issues: 20,\n\t\t\thas_issues: true,\n\t\t\thas_downloads: true,\n\t\t\thas_wiki: true,\n\t\t\tlanguage: \"JavaScript\",\n\t\t\tcreated_at: \"2012-12-22T23:28:47-08:00\",\n\t\t\tpushed_at: \"2013-11-09T22:58:31-08:00\",\n\t\t\tmaster_branch: \"master\",\n\t\t\torganization: \"peers\",\n\t\t},\n\t},\n\t{\n\t\tcreated_at: \"2013-11-02T15:15:18-07:00\",\n\t\tpayload: {\n\t\t\tshas: [\n\t\t\t\t[\n\t\t\t\t\t\"ec1424c0f264ea5d8ce08ac2cc9ea2bd027a6a71\",\n\t\t\t\t\t\"really.ez@gmail.com\",\n\t\t\t\t\t\"Errant comma\",\n\t\t\t\t\t\"ericz\",\n\t\t\t\t\ttrue,\n\t\t\t\t],\n\t\t\t\t[\n\t\t\t\t\t\"c9adf5076ea41df60231e11d032f371939228609\",\n\t\t\t\t\t\"really.ez@gmail.com\",\n\t\t\t\t\t\"Dont throw exception on failures\",\n\t\t\t\t\t\"ericz\",\n\t\t\t\t\ttrue,\n\t\t\t\t],\n\t\t\t],\n\t\t\tsize: 2,\n\t\t\tref: \"refs/heads/master\",\n\t\t\thead: \"c9adf5076ea41df60231e11d032f371939228609\",\n\t\t},\n\t\tpublic: true,\n\t\ttype: \"PushEvent\",\n\t\turl: \"https://github.com/peers/peerjs/compare/bb1045bf92...c9adf5076e\",\n\t\tactor: \"ericz\",\n\t\tactor_attributes: {\n\t\t\tlogin: \"ericz\",\n\t\t\ttype: \"User\",\n\t\t\tgravatar_id: \"c584ef7fe434331f7068ea49cacd88b9\",\n\t\t\tname: \"Eric Zhang\",\n\t\t\tcompany: \"Lever\",\n\t\t\tblog: \"https://twitter.com/reallyez\",\n\t\t\tlocation: \"Berkeley\",\n\t\t\temail: \"eric@ericzhang.com\",\n\t\t},\n\t\trepository: {\n\t\t\tid: 7292898,\n\t\t\tname: \"peerjs\",\n\t\t\turl: \"https://github.com/peers/peerjs\",\n\t\t\tdescription: \"Peer-to-peer data in the browser.\",\n\t\t\thomepage: \"https://peerjs.com\",\n\t\t\twatchers: 1647,\n\t\t\tstargazers: 1647,\n\t\t\tforks: 145,\n\t\t\tfork: false,\n\t\t\tsize: 2188,\n\t\t\towner: \"peers\",\n\t\t\tprivate: false,\n\t\t\topen_issues: 20,\n\t\t\thas_issues: true,\n\t\t\thas_downloads: true,\n\t\t\thas_wiki: true,\n\t\t\tlanguage: \"JavaScript\",\n\t\t\tcreated_at: \"2012-12-22T23:28:47-08:00\",\n\t\t\tpushed_at: \"2013-11-09T22:58:31-08:00\",\n\t\t\tmaster_branch: \"master\",\n\t\t\torganization: \"peers\",\n\t\t},\n\t},\n\t{\n\t\tcreated_at: \"2013-11-02T15:02:02-07:00\",\n\t\tpayload: {\n\t\t\tshas: [\n\t\t\t\t[\n\t\t\t\t\t\"bb1045bf92fbaef6e2156c3ac47015f70af5d866\",\n\t\t\t\t\t\"really.ez@gmail.com\",\n\t\t\t\t\t\"Fix errant comma\",\n\t\t\t\t\t\"ericz\",\n\t\t\t\t\ttrue,\n\t\t\t\t],\n\t\t\t],\n\t\t\tsize: 1,\n\t\t\tref: \"refs/heads/master\",\n\t\t\thead: \"bb1045bf92fbaef6e2156c3ac47015f70af5d866\",\n\t\t},\n\t\tpublic: true,\n\t\ttype: \"PushEvent\",\n\t\turl: \"https://github.com/peers/peerjs/compare/2db1c59998...bb1045bf92\",\n\t\tactor: \"ericz\",\n\t\tactor_attributes: {\n\t\t\tlogin: \"ericz\",\n\t\t\ttype: \"User\",\n\t\t\tgravatar_id: \"c584ef7fe434331f7068ea49cacd88b9\",\n\t\t\tname: \"Eric Zhang\",\n\t\t\tcompany: \"Lever\",\n\t\t\tblog: \"https://twitter.com/reallyez\",\n\t\t\tlocation: \"Berkeley\",\n\t\t\temail: \"eric@ericzhang.com\",\n\t\t},\n\t\trepository: {\n\t\t\tid: 7292898,\n\t\t\tname: \"peerjs\",\n\t\t\turl: \"https://github.com/peers/peerjs\",\n\t\t\tdescription: \"Peer-to-peer data in the browser.\",\n\t\t\thomepage: \"https://peerjs.com\",\n\t\t\twatchers: 1647,\n\t\t\tstargazers: 1647,\n\t\t\tforks: 145,\n\t\t\tfork: false,\n\t\t\tsize: 2188,\n\t\t\towner: \"peers\",\n\t\t\tprivate: false,\n\t\t\topen_issues: 20,\n\t\t\thas_issues: true,\n\t\t\thas_downloads: true,\n\t\t\thas_wiki: true,\n\t\t\tlanguage: \"JavaScript\",\n\t\t\tcreated_at: \"2012-12-22T23:28:47-08:00\",\n\t\t\tpushed_at: \"2013-11-09T22:58:31-08:00\",\n\t\t\tmaster_branch: \"master\",\n\t\t\torganization: \"peers\",\n\t\t},\n\t},\n\t{\n\t\tcreated_at: \"2013-11-02T14:56:14-07:00\",\n\t\tpayload: {\n\t\t\tshas: [\n\t\t\t\t[\n\t\t\t\t\t\"2db1c599987753079d197f73272a9e40e9290f73\",\n\t\t\t\t\t\"really.ez@gmail.com\",\n\t\t\t\t\t\"Remove errant comma\",\n\t\t\t\t\t\"ericz\",\n\t\t\t\t\ttrue,\n\t\t\t\t],\n\t\t\t],\n\t\t\tsize: 1,\n\t\t\tref: \"refs/heads/master\",\n\t\t\thead: \"2db1c599987753079d197f73272a9e40e9290f73\",\n\t\t},\n\t\tpublic: true,\n\t\ttype: \"PushEvent\",\n\t\turl: \"https://github.com/peers/peerjs/compare/214a14cc10...2db1c59998\",\n\t\tactor: \"ericz\",\n\t\tactor_attributes: {\n\t\t\tlogin: \"ericz\",\n\t\t\ttype: \"User\",\n\t\t\tgravatar_id: \"c584ef7fe434331f7068ea49cacd88b9\",\n\t\t\tname: \"Eric Zhang\",\n\t\t\tcompany: \"Lever\",\n\t\t\tblog: \"https://twitter.com/reallyez\",\n\t\t\tlocation: \"Berkeley\",\n\t\t\temail: \"eric@ericzhang.com\",\n\t\t},\n\t\trepository: {\n\t\t\tid: 7292898,\n\t\t\tname: \"peerjs\",\n\t\t\turl: \"https://github.com/peers/peerjs\",\n\t\t\tdescription: \"Peer-to-peer data in the browser.\",\n\t\t\thomepage: \"https://peerjs.com\",\n\t\t\twatchers: 1647,\n\t\t\tstargazers: 1647,\n\t\t\tforks: 145,\n\t\t\tfork: false,\n\t\t\tsize: 2188,\n\t\t\towner: \"peers\",\n\t\t\tprivate: false,\n\t\t\topen_issues: 20,\n\t\t\thas_issues: true,\n\t\t\thas_downloads: true,\n\t\t\thas_wiki: true,\n\t\t\tlanguage: \"JavaScript\",\n\t\t\tcreated_at: \"2012-12-22T23:28:47-08:00\",\n\t\t\tpushed_at: \"2013-11-09T22:58:31-08:00\",\n\t\t\tmaster_branch: \"master\",\n\t\t\torganization: \"peers\",\n\t\t},\n\t},\n\t{\n\t\tcreated_at: \"2013-11-02T14:56:13-07:00\",\n\t\tpayload: {\n\t\t\tshas: [\n\t\t\t\t[\n\t\t\t\t\t\"841921c349aff234b022bb966774d00ae22fef5e\",\n\t\t\t\t\t\"really.ez@gmail.com\",\n\t\t\t\t\t\"Errant comma\",\n\t\t\t\t\t\"ericz\",\n\t\t\t\t\ttrue,\n\t\t\t\t],\n\t\t\t],\n\t\t\tsize: 1,\n\t\t\tref: \"refs/heads/better-supports\",\n\t\t\thead: \"841921c349aff234b022bb966774d00ae22fef5e\",\n\t\t},\n\t\tpublic: true,\n\t\ttype: \"PushEvent\",\n\t\turl: \"https://github.com/peers/peerjs/compare/ccd80612ae...841921c349\",\n\t\tactor: \"ericz\",\n\t\tactor_attributes: {\n\t\t\tlogin: \"ericz\",\n\t\t\ttype: \"User\",\n\t\t\tgravatar_id: \"c584ef7fe434331f7068ea49cacd88b9\",\n\t\t\tname: \"Eric Zhang\",\n\t\t\tcompany: \"Lever\",\n\t\t\tblog: \"https://twitter.com/reallyez\",\n\t\t\tlocation: \"Berkeley\",\n\t\t\temail: \"eric@ericzhang.com\",\n\t\t},\n\t\trepository: {\n\t\t\tid: 7292898,\n\t\t\tname: \"peerjs\",\n\t\t\turl: \"https://github.com/peers/peerjs\",\n\t\t\tdescription: \"Peer-to-peer data in the browser.\",\n\t\t\thomepage: \"https://peerjs.com\",\n\t\t\twatchers: 1647,\n\t\t\tstargazers: 1647,\n\t\t\tforks: 145,\n\t\t\tfork: false,\n\t\t\tsize: 2188,\n\t\t\towner: \"peers\",\n\t\t\tprivate: false,\n\t\t\topen_issues: 20,\n\t\t\thas_issues: true,\n\t\t\thas_downloads: true,\n\t\t\thas_wiki: true,\n\t\t\tlanguage: \"JavaScript\",\n\t\t\tcreated_at: \"2012-12-22T23:28:47-08:00\",\n\t\t\tpushed_at: \"2013-11-09T22:58:31-08:00\",\n\t\t\tmaster_branch: \"master\",\n\t\t\torganization: \"peers\",\n\t\t},\n\t},\n\t{\n\t\tcreated_at: \"2013-11-01T11:41:34-07:00\",\n\t\tpayload: {\n\t\t\tshas: [\n\t\t\t\t[\n\t\t\t\t\t\"37350aaef3763d5a1bc63c4903f0f34ea9780d36\",\n\t\t\t\t\t\"really.ez@gmail.com\",\n\t\t\t\t\t\"Fix\",\n\t\t\t\t\t\"Eric Zhang\",\n\t\t\t\t\ttrue,\n\t\t\t\t],\n\t\t\t],\n\t\t\tsize: 1,\n\t\t\tref: \"refs/heads/master\",\n\t\t\thead: \"37350aaef3763d5a1bc63c4903f0f34ea9780d36\",\n\t\t},\n\t\tpublic: true,\n\t\ttype: \"PushEvent\",\n\t\turl: \"https://github.com/HackBerkeley/ascam/compare/7d76223acc...37350aaef3\",\n\t\tactor: \"ericz\",\n\t\tactor_attributes: {\n\t\t\tlogin: \"ericz\",\n\t\t\ttype: \"User\",\n\t\t\tgravatar_id: \"c584ef7fe434331f7068ea49cacd88b9\",\n\t\t\tname: \"Eric Zhang\",\n\t\t\tcompany: \"Lever\",\n\t\t\tblog: \"https://twitter.com/reallyez\",\n\t\t\tlocation: \"Berkeley\",\n\t\t\temail: \"eric@ericzhang.com\",\n\t\t},\n\t\trepository: {\n\t\t\tid: 5557070,\n\t\t\tname: \"ascam\",\n\t\t\turl: \"https://github.com/HackBerkeley/ascam\",\n\t\t\tdescription: \"Ascii Webcam\",\n\t\t\thomepage: \"\",\n\t\t\twatchers: 1,\n\t\t\tstargazers: 1,\n\t\t\tforks: 1,\n\t\t\tfork: true,\n\t\t\tsize: 1973,\n\t\t\towner: \"HackBerkeley\",\n\t\t\tprivate: false,\n\t\t\topen_issues: 0,\n\t\t\thas_issues: false,\n\t\t\thas_downloads: true,\n\t\t\thas_wiki: true,\n\t\t\tlanguage: \"JavaScript\",\n\t\t\tcreated_at: \"2012-08-25T20:19:01-07:00\",\n\t\t\tpushed_at: \"2013-11-01T11:41:33-07:00\",\n\t\t\tmaster_branch: \"master\",\n\t\t\torganization: \"HackBerkeley\",\n\t\t},\n\t},\n\t{\n\t\tcreated_at: \"2013-10-31T10:56:20-07:00\",\n\t\tpayload: { issue_id: 13813188, comment_id: 27509702 },\n\t\tpublic: true,\n\t\ttype: \"IssueCommentEvent\",\n\t\turl: \"https://github.com/peers/peerjs/pull/45#issuecomment-27509702\",\n\t\tactor: \"ericz\",\n\t\tactor_attributes: {\n\t\t\tlogin: \"ericz\",\n\t\t\ttype: \"User\",\n\t\t\tgravatar_id: \"c584ef7fe434331f7068ea49cacd88b9\",\n\t\t\tname: \"Eric Zhang\",\n\t\t\tcompany: \"Lever\",\n\t\t\tblog: \"https://twitter.com/reallyez\",\n\t\t\tlocation: \"Berkeley\",\n\t\t\temail: \"eric@ericzhang.com\",\n\t\t},\n\t\trepository: {\n\t\t\tid: 7292898,\n\t\t\tname: \"peerjs\",\n\t\t\turl: \"https://github.com/peers/peerjs\",\n\t\t\tdescription: \"Peer-to-peer data in the browser.\",\n\t\t\thomepage: \"https://peerjs.com\",\n\t\t\twatchers: 1647,\n\t\t\tstargazers: 1647,\n\t\t\tforks: 145,\n\t\t\tfork: false,\n\t\t\tsize: 2188,\n\t\t\towner: \"peers\",\n\t\t\tprivate: false,\n\t\t\topen_issues: 20,\n\t\t\thas_issues: true,\n\t\t\thas_downloads: true,\n\t\t\thas_wiki: true,\n\t\t\tlanguage: \"JavaScript\",\n\t\t\tcreated_at: \"2012-12-22T23:28:47-08:00\",\n\t\t\tpushed_at: \"2013-11-09T22:58:31-08:00\",\n\t\t\tmaster_branch: \"master\",\n\t\t\torganization: \"peers\",\n\t\t},\n\t},\n\t{\n\t\tcreated_at: \"2013-10-26T22:53:49-07:00\",\n\t\tpayload: { issue_id: 21646195, comment_id: 27163465 },\n\t\tpublic: true,\n\t\ttype: \"IssueCommentEvent\",\n\t\turl: \"https://github.com/peers/peerjs-server/issues/25#issuecomment-27163465\",\n\t\tactor: \"ericz\",\n\t\tactor_attributes: {\n\t\t\tlogin: \"ericz\",\n\t\t\ttype: \"User\",\n\t\t\tgravatar_id: \"c584ef7fe434331f7068ea49cacd88b9\",\n\t\t\tname: \"Eric Zhang\",\n\t\t\tcompany: \"Lever\",\n\t\t\tblog: \"https://twitter.com/reallyez\",\n\t\t\tlocation: \"Berkeley\",\n\t\t\temail: \"eric@ericzhang.com\",\n\t\t},\n\t\trepository: {\n\t\t\tid: 7452705,\n\t\t\tname: \"peerjs-server\",\n\t\t\turl: \"https://github.com/peers/peerjs-server\",\n\t\t\tdescription: \"Server for PeerJS\",\n\t\t\thomepage: \"https://peerjs.com\",\n\t\t\twatchers: 328,\n\t\t\tstargazers: 328,\n\t\t\tforks: 56,\n\t\t\tfork: false,\n\t\t\tsize: 389,\n\t\t\towner: \"peers\",\n\t\t\tprivate: false,\n\t\t\topen_issues: 12,\n\t\t\thas_issues: true,\n\t\t\thas_downloads: true,\n\t\t\thas_wiki: true,\n\t\t\tlanguage: \"JavaScript\",\n\t\t\tcreated_at: \"2013-01-04T22:49:08-08:00\",\n\t\t\tpushed_at: \"2013-10-24T00:40:28-07:00\",\n\t\t\tmaster_branch: \"master\",\n\t\t\torganization: \"peers\",\n\t\t},\n\t},\n\t{\n\t\tcreated_at: \"2013-10-25T11:04:07-07:00\",\n\t\tpayload: { action: \"closed\", issue: 16608955, number: 68 },\n\t\tpublic: true,\n\t\ttype: \"IssuesEvent\",\n\t\turl: \"https://github.com/peers/peerjs/issues/68\",\n\t\tactor: \"ericz\",\n\t\tactor_attributes: {\n\t\t\tlogin: \"ericz\",\n\t\t\ttype: \"User\",\n\t\t\tgravatar_id: \"c584ef7fe434331f7068ea49cacd88b9\",\n\t\t\tname: \"Eric Zhang\",\n\t\t\tcompany: \"Lever\",\n\t\t\tblog: \"https://twitter.com/reallyez\",\n\t\t\tlocation: \"Berkeley\",\n\t\t\temail: \"eric@ericzhang.com\",\n\t\t},\n\t\trepository: {\n\t\t\tid: 7292898,\n\t\t\tname: \"peerjs\",\n\t\t\turl: \"https://github.com/peers/peerjs\",\n\t\t\tdescription: \"Peer-to-peer data in the browser.\",\n\t\t\thomepage: \"https://peerjs.com\",\n\t\t\twatchers: 1647,\n\t\t\tstargazers: 1647,\n\t\t\tforks: 145,\n\t\t\tfork: false,\n\t\t\tsize: 2188,\n\t\t\towner: \"peers\",\n\t\t\tprivate: false,\n\t\t\topen_issues: 20,\n\t\t\thas_issues: true,\n\t\t\thas_downloads: true,\n\t\t\thas_wiki: true,\n\t\t\tlanguage: \"JavaScript\",\n\t\t\tcreated_at: \"2012-12-22T23:28:47-08:00\",\n\t\t\tpushed_at: \"2013-11-09T22:58:31-08:00\",\n\t\t\tmaster_branch: \"master\",\n\t\t\torganization: \"peers\",\n\t\t},\n\t},\n\t{\n\t\tcreated_at: \"2013-10-25T11:02:50-07:00\",\n\t\tpayload: { issue_id: 21379154, comment_id: 27113277 },\n\t\tpublic: true,\n\t\ttype: \"IssueCommentEvent\",\n\t\turl: \"https://github.com/peers/peerjs/issues/91#issuecomment-27113277\",\n\t\tactor: \"ericz\",\n\t\tactor_attributes: {\n\t\t\tlogin: \"ericz\",\n\t\t\ttype: \"User\",\n\t\t\tgravatar_id: \"c584ef7fe434331f7068ea49cacd88b9\",\n\t\t\tname: \"Eric Zhang\",\n\t\t\tcompany: \"Lever\",\n\t\t\tblog: \"https://twitter.com/reallyez\",\n\t\t\tlocation: \"Berkeley\",\n\t\t\temail: \"eric@ericzhang.com\",\n\t\t},\n\t\trepository: {\n\t\t\tid: 7292898,\n\t\t\tname: \"peerjs\",\n\t\t\turl: \"https://github.com/peers/peerjs\",\n\t\t\tdescription: \"Peer-to-peer data in the browser.\",\n\t\t\thomepage: \"https://peerjs.com\",\n\t\t\twatchers: 1647,\n\t\t\tstargazers: 1647,\n\t\t\tforks: 145,\n\t\t\tfork: false,\n\t\t\tsize: 2188,\n\t\t\towner: \"peers\",\n\t\t\tprivate: false,\n\t\t\topen_issues: 20,\n\t\t\thas_issues: true,\n\t\t\thas_downloads: true,\n\t\t\thas_wiki: true,\n\t\t\tlanguage: \"JavaScript\",\n\t\t\tcreated_at: \"2012-12-22T23:28:47-08:00\",\n\t\t\tpushed_at: \"2013-11-09T22:58:31-08:00\",\n\t\t\tmaster_branch: \"master\",\n\t\t\torganization: \"peers\",\n\t\t},\n\t},\n\t{\n\t\tcreated_at: \"2013-10-25T11:01:53-07:00\",\n\t\tpayload: { action: \"closed\", issue: 21531959, number: 96 },\n\t\tpublic: true,\n\t\ttype: \"IssuesEvent\",\n\t\turl: \"https://github.com/peers/peerjs/issues/96\",\n\t\tactor: \"ericz\",\n\t\tactor_attributes: {\n\t\t\tlogin: \"ericz\",\n\t\t\ttype: \"User\",\n\t\t\tgravatar_id: \"c584ef7fe434331f7068ea49cacd88b9\",\n\t\t\tname: \"Eric Zhang\",\n\t\t\tcompany: \"Lever\",\n\t\t\tblog: \"https://twitter.com/reallyez\",\n\t\t\tlocation: \"Berkeley\",\n\t\t\temail: \"eric@ericzhang.com\",\n\t\t},\n\t\trepository: {\n\t\t\tid: 7292898,\n\t\t\tname: \"peerjs\",\n\t\t\turl: \"https://github.com/peers/peerjs\",\n\t\t\tdescription: \"Peer-to-peer data in the browser.\",\n\t\t\thomepage: \"https://peerjs.com\",\n\t\t\twatchers: 1647,\n\t\t\tstargazers: 1647,\n\t\t\tforks: 145,\n\t\t\tfork: false,\n\t\t\tsize: 2188,\n\t\t\towner: \"peers\",\n\t\t\tprivate: false,\n\t\t\topen_issues: 20,\n\t\t\thas_issues: true,\n\t\t\thas_downloads: true,\n\t\t\thas_wiki: true,\n\t\t\tlanguage: \"JavaScript\",\n\t\t\tcreated_at: \"2012-12-22T23:28:47-08:00\",\n\t\t\tpushed_at: \"2013-11-09T22:58:31-08:00\",\n\t\t\tmaster_branch: \"master\",\n\t\t\torganization: \"peers\",\n\t\t},\n\t},\n\t{\n\t\tcreated_at: \"2013-10-25T11:01:25-07:00\",\n\t\tpayload: { issue_id: 21379154, comment_id: 27113184 },\n\t\tpublic: true,\n\t\ttype: \"IssueCommentEvent\",\n\t\turl: \"https://github.com/peers/peerjs/issues/91#issuecomment-27113184\",\n\t\tactor: \"ericz\",\n\t\tactor_attributes: {\n\t\t\tlogin: \"ericz\",\n\t\t\ttype: \"User\",\n\t\t\tgravatar_id: \"c584ef7fe434331f7068ea49cacd88b9\",\n\t\t\tname: \"Eric Zhang\",\n\t\t\tcompany: \"Lever\",\n\t\t\tblog: \"https://twitter.com/reallyez\",\n\t\t\tlocation: \"Berkeley\",\n\t\t\temail: \"eric@ericzhang.com\",\n\t\t},\n\t\trepository: {\n\t\t\tid: 7292898,\n\t\t\tname: \"peerjs\",\n\t\t\turl: \"https://github.com/peers/peerjs\",\n\t\t\tdescription: \"Peer-to-peer data in the browser.\",\n\t\t\thomepage: \"https://peerjs.com\",\n\t\t\twatchers: 1647,\n\t\t\tstargazers: 1647,\n\t\t\tforks: 145,\n\t\t\tfork: false,\n\t\t\tsize: 2188,\n\t\t\towner: \"peers\",\n\t\t\tprivate: false,\n\t\t\topen_issues: 20,\n\t\t\thas_issues: true,\n\t\t\thas_downloads: true,\n\t\t\thas_wiki: true,\n\t\t\tlanguage: \"JavaScript\",\n\t\t\tcreated_at: \"2012-12-22T23:28:47-08:00\",\n\t\t\tpushed_at: \"2013-11-09T22:58:31-08:00\",\n\t\t\tmaster_branch: \"master\",\n\t\t\torganization: \"peers\",\n\t\t},\n\t},\n\t{\n\t\tcreated_at: \"2013-10-25T10:42:32-07:00\",\n\t\tpayload: { issue_id: 21531959, comment_id: 27111740 },\n\t\tpublic: true,\n\t\ttype: \"IssueCommentEvent\",\n\t\turl: \"https://github.com/peers/peerjs/issues/96#issuecomment-27111740\",\n\t\tactor: \"ericz\",\n\t\tactor_attributes: {\n\t\t\tlogin: \"ericz\",\n\t\t\ttype: \"User\",\n\t\t\tgravatar_id: \"c584ef7fe434331f7068ea49cacd88b9\",\n\t\t\tname: \"Eric Zhang\",\n\t\t\tcompany: \"Lever\",\n\t\t\tblog: \"https://twitter.com/reallyez\",\n\t\t\tlocation: \"Berkeley\",\n\t\t\temail: \"eric@ericzhang.com\",\n\t\t},\n\t\trepository: {\n\t\t\tid: 7292898,\n\t\t\tname: \"peerjs\",\n\t\t\turl: \"https://github.com/peers/peerjs\",\n\t\t\tdescription: \"Peer-to-peer data in the browser.\",\n\t\t\thomepage: \"https://peerjs.com\",\n\t\t\twatchers: 1647,\n\t\t\tstargazers: 1647,\n\t\t\tforks: 145,\n\t\t\tfork: false,\n\t\t\tsize: 2188,\n\t\t\towner: \"peers\",\n\t\t\tprivate: false,\n\t\t\topen_issues: 20,\n\t\t\thas_issues: true,\n\t\t\thas_downloads: true,\n\t\t\thas_wiki: true,\n\t\t\tlanguage: \"JavaScript\",\n\t\t\tcreated_at: \"2012-12-22T23:28:47-08:00\",\n\t\t\tpushed_at: \"2013-11-09T22:58:31-08:00\",\n\t\t\tmaster_branch: \"master\",\n\t\t\torganization: \"peers\",\n\t\t},\n\t},\n\t{\n\t\tcreated_at: \"2013-10-25T10:42:13-07:00\",\n\t\tpayload: { issue_id: 21531959, comment_id: 27111710 },\n\t\tpublic: true,\n\t\ttype: \"IssueCommentEvent\",\n\t\turl: \"https://github.com/peers/peerjs/issues/96#issuecomment-27111710\",\n\t\tactor: \"ericz\",\n\t\tactor_attributes: {\n\t\t\tlogin: \"ericz\",\n\t\t\ttype: \"User\",\n\t\t\tgravatar_id: \"c584ef7fe434331f7068ea49cacd88b9\",\n\t\t\tname: \"Eric Zhang\",\n\t\t\tcompany: \"Lever\",\n\t\t\tblog: \"https://twitter.com/reallyez\",\n\t\t\tlocation: \"Berkeley\",\n\t\t\temail: \"eric@ericzhang.com\",\n\t\t},\n\t\trepository: {\n\t\t\tid: 7292898,\n\t\t\tname: \"peerjs\",\n\t\t\turl: \"https://github.com/peers/peerjs\",\n\t\t\tdescription: \"Peer-to-peer data in the browser.\",\n\t\t\thomepage: \"https://peerjs.com\",\n\t\t\twatchers: 1647,\n\t\t\tstargazers: 1647,\n\t\t\tforks: 145,\n\t\t\tfork: false,\n\t\t\tsize: 2188,\n\t\t\towner: \"peers\",\n\t\t\tprivate: false,\n\t\t\topen_issues: 20,\n\t\t\thas_issues: true,\n\t\t\thas_downloads: true,\n\t\t\thas_wiki: true,\n\t\t\tlanguage: \"JavaScript\",\n\t\t\tcreated_at: \"2012-12-22T23:28:47-08:00\",\n\t\t\tpushed_at: \"2013-11-09T22:58:31-08:00\",\n\t\t\tmaster_branch: \"master\",\n\t\t\torganization: \"peers\",\n\t\t},\n\t},\n\t{\n\t\tcreated_at: \"2013-10-25T10:36:48-07:00\",\n\t\tpayload: { issue_id: 21568882, comment_id: 27111312 },\n\t\tpublic: true,\n\t\ttype: \"IssueCommentEvent\",\n\t\turl: \"https://github.com/peers/peerjs/issues/97#issuecomment-27111312\",\n\t\tactor: \"ericz\",\n\t\tactor_attributes: {\n\t\t\tlogin: \"ericz\",\n\t\t\ttype: \"User\",\n\t\t\tgravatar_id: \"c584ef7fe434331f7068ea49cacd88b9\",\n\t\t\tname: \"Eric Zhang\",\n\t\t\tcompany: \"Lever\",\n\t\t\tblog: \"https://twitter.com/reallyez\",\n\t\t\tlocation: \"Berkeley\",\n\t\t\temail: \"eric@ericzhang.com\",\n\t\t},\n\t\trepository: {\n\t\t\tid: 7292898,\n\t\t\tname: \"peerjs\",\n\t\t\turl: \"https://github.com/peers/peerjs\",\n\t\t\tdescription: \"Peer-to-peer data in the browser.\",\n\t\t\thomepage: \"https://peerjs.com\",\n\t\t\twatchers: 1647,\n\t\t\tstargazers: 1647,\n\t\t\tforks: 145,\n\t\t\tfork: false,\n\t\t\tsize: 2188,\n\t\t\towner: \"peers\",\n\t\t\tprivate: false,\n\t\t\topen_issues: 20,\n\t\t\thas_issues: true,\n\t\t\thas_downloads: true,\n\t\t\thas_wiki: true,\n\t\t\tlanguage: \"JavaScript\",\n\t\t\tcreated_at: \"2012-12-22T23:28:47-08:00\",\n\t\t\tpushed_at: \"2013-11-09T22:58:31-08:00\",\n\t\t\tmaster_branch: \"master\",\n\t\t\torganization: \"peers\",\n\t\t},\n\t},\n\t{\n\t\tcreated_at: \"2013-10-23T15:52:46-07:00\",\n\t\tpayload: { issue_id: 21487688, comment_id: 26953214 },\n\t\tpublic: true,\n\t\ttype: \"IssueCommentEvent\",\n\t\turl: \"https://github.com/peers/peerjs/issues/95#issuecomment-26953214\",\n\t\tactor: \"ericz\",\n\t\tactor_attributes: {\n\t\t\tlogin: \"ericz\",\n\t\t\ttype: \"User\",\n\t\t\tgravatar_id: \"c584ef7fe434331f7068ea49cacd88b9\",\n\t\t\tname: \"Eric Zhang\",\n\t\t\tcompany: \"Lever\",\n\t\t\tblog: \"https://twitter.com/reallyez\",\n\t\t\tlocation: \"Berkeley\",\n\t\t\temail: \"eric@ericzhang.com\",\n\t\t},\n\t\trepository: {\n\t\t\tid: 7292898,\n\t\t\tname: \"peerjs\",\n\t\t\turl: \"https://github.com/peers/peerjs\",\n\t\t\tdescription: \"Peer-to-peer data in the browser.\",\n\t\t\thomepage: \"https://peerjs.com\",\n\t\t\twatchers: 1647,\n\t\t\tstargazers: 1647,\n\t\t\tforks: 145,\n\t\t\tfork: false,\n\t\t\tsize: 2188,\n\t\t\towner: \"peers\",\n\t\t\tprivate: false,\n\t\t\topen_issues: 20,\n\t\t\thas_issues: true,\n\t\t\thas_downloads: true,\n\t\t\thas_wiki: true,\n\t\t\tlanguage: \"JavaScript\",\n\t\t\tcreated_at: \"2012-12-22T23:28:47-08:00\",\n\t\t\tpushed_at: \"2013-11-09T22:58:31-08:00\",\n\t\t\tmaster_branch: \"master\",\n\t\t\torganization: \"peers\",\n\t\t},\n\t},\n\t{\n\t\tcreated_at: \"2013-10-22T20:07:24-07:00\",\n\t\tpayload: { action: \"opened\", issue: 21431237, number: 93 },\n\t\tpublic: true,\n\t\ttype: \"IssuesEvent\",\n\t\turl: \"https://github.com/peers/peerjs/issues/93\",\n\t\tactor: \"ericz\",\n\t\tactor_attributes: {\n\t\t\tlogin: \"ericz\",\n\t\t\ttype: \"User\",\n\t\t\tgravatar_id: \"c584ef7fe434331f7068ea49cacd88b9\",\n\t\t\tname: \"Eric Zhang\",\n\t\t\tcompany: \"Lever\",\n\t\t\tblog: \"https://twitter.com/reallyez\",\n\t\t\tlocation: \"Berkeley\",\n\t\t\temail: \"eric@ericzhang.com\",\n\t\t},\n\t\trepository: {\n\t\t\tid: 7292898,\n\t\t\tname: \"peerjs\",\n\t\t\turl: \"https://github.com/peers/peerjs\",\n\t\t\tdescription: \"Peer-to-peer data in the browser.\",\n\t\t\thomepage: \"https://peerjs.com\",\n\t\t\twatchers: 1647,\n\t\t\tstargazers: 1647,\n\t\t\tforks: 145,\n\t\t\tfork: false,\n\t\t\tsize: 2188,\n\t\t\towner: \"peers\",\n\t\t\tprivate: false,\n\t\t\topen_issues: 20,\n\t\t\thas_issues: true,\n\t\t\thas_downloads: true,\n\t\t\thas_wiki: true,\n\t\t\tlanguage: \"JavaScript\",\n\t\t\tcreated_at: \"2012-12-22T23:28:47-08:00\",\n\t\t\tpushed_at: \"2013-11-09T22:58:31-08:00\",\n\t\t\tmaster_branch: \"master\",\n\t\t\torganization: \"peers\",\n\t\t},\n\t},\n\t{\n\t\tcreated_at: \"2013-10-22T19:15:46-07:00\",\n\t\tpayload: { issue_id: 21259595, comment_id: 26875922 },\n\t\tpublic: true,\n\t\ttype: \"IssueCommentEvent\",\n\t\turl: \"https://github.com/peers/peerjs/issues/89#issuecomment-26875922\",\n\t\tactor: \"ericz\",\n\t\tactor_attributes: {\n\t\t\tlogin: \"ericz\",\n\t\t\ttype: \"User\",\n\t\t\tgravatar_id: \"c584ef7fe434331f7068ea49cacd88b9\",\n\t\t\tname: \"Eric Zhang\",\n\t\t\tcompany: \"Lever\",\n\t\t\tblog: \"https://twitter.com/reallyez\",\n\t\t\tlocation: \"Berkeley\",\n\t\t\temail: \"eric@ericzhang.com\",\n\t\t},\n\t\trepository: {\n\t\t\tid: 7292898,\n\t\t\tname: \"peerjs\",\n\t\t\turl: \"https://github.com/peers/peerjs\",\n\t\t\tdescription: \"Peer-to-peer data in the browser.\",\n\t\t\thomepage: \"https://peerjs.com\",\n\t\t\twatchers: 1647,\n\t\t\tstargazers: 1647,\n\t\t\tforks: 145,\n\t\t\tfork: false,\n\t\t\tsize: 2188,\n\t\t\towner: \"peers\",\n\t\t\tprivate: false,\n\t\t\topen_issues: 20,\n\t\t\thas_issues: true,\n\t\t\thas_downloads: true,\n\t\t\thas_wiki: true,\n\t\t\tlanguage: \"JavaScript\",\n\t\t\tcreated_at: \"2012-12-22T23:28:47-08:00\",\n\t\t\tpushed_at: \"2013-11-09T22:58:31-08:00\",\n\t\t\tmaster_branch: \"master\",\n\t\t\torganization: \"peers\",\n\t\t},\n\t},\n\t{\n\t\tcreated_at: \"2013-10-22T19:06:59-07:00\",\n\t\tpayload: { issue_id: 20917093, comment_id: 26875655 },\n\t\tpublic: true,\n\t\ttype: \"IssueCommentEvent\",\n\t\turl: \"https://github.com/peers/peerjs/issues/86#issuecomment-26875655\",\n\t\tactor: \"ericz\",\n\t\tactor_attributes: {\n\t\t\tlogin: \"ericz\",\n\t\t\ttype: \"User\",\n\t\t\tgravatar_id: \"c584ef7fe434331f7068ea49cacd88b9\",\n\t\t\tname: \"Eric Zhang\",\n\t\t\tcompany: \"Lever\",\n\t\t\tblog: \"https://twitter.com/reallyez\",\n\t\t\tlocation: \"Berkeley\",\n\t\t\temail: \"eric@ericzhang.com\",\n\t\t},\n\t\trepository: {\n\t\t\tid: 7292898,\n\t\t\tname: \"peerjs\",\n\t\t\turl: \"https://github.com/peers/peerjs\",\n\t\t\tdescription: \"Peer-to-peer data in the browser.\",\n\t\t\thomepage: \"https://peerjs.com\",\n\t\t\twatchers: 1647,\n\t\t\tstargazers: 1647,\n\t\t\tforks: 145,\n\t\t\tfork: false,\n\t\t\tsize: 2188,\n\t\t\towner: \"peers\",\n\t\t\tprivate: false,\n\t\t\topen_issues: 20,\n\t\t\thas_issues: true,\n\t\t\thas_downloads: true,\n\t\t\thas_wiki: true,\n\t\t\tlanguage: \"JavaScript\",\n\t\t\tcreated_at: \"2012-12-22T23:28:47-08:00\",\n\t\t\tpushed_at: \"2013-11-09T22:58:31-08:00\",\n\t\t\tmaster_branch: \"master\",\n\t\t\torganization: \"peers\",\n\t\t},\n\t},\n\t{\n\t\tcreated_at: \"2013-10-22T10:42:17-07:00\",\n\t\tpayload: { issue_id: 21337614, comment_id: 26824752 },\n\t\tpublic: true,\n\t\ttype: \"IssueCommentEvent\",\n\t\turl: \"https://github.com/peers/peerjs/issues/90#issuecomment-26824752\",\n\t\tactor: \"ericz\",\n\t\tactor_attributes: {\n\t\t\tlogin: \"ericz\",\n\t\t\ttype: \"User\",\n\t\t\tgravatar_id: \"c584ef7fe434331f7068ea49cacd88b9\",\n\t\t\tname: \"Eric Zhang\",\n\t\t\tcompany: \"Lever\",\n\t\t\tblog: \"https://twitter.com/reallyez\",\n\t\t\tlocation: \"Berkeley\",\n\t\t\temail: \"eric@ericzhang.com\",\n\t\t},\n\t\trepository: {\n\t\t\tid: 7292898,\n\t\t\tname: \"peerjs\",\n\t\t\turl: \"https://github.com/peers/peerjs\",\n\t\t\tdescription: \"Peer-to-peer data in the browser.\",\n\t\t\thomepage: \"https://peerjs.com\",\n\t\t\twatchers: 1647,\n\t\t\tstargazers: 1647,\n\t\t\tforks: 145,\n\t\t\tfork: false,\n\t\t\tsize: 2188,\n\t\t\towner: \"peers\",\n\t\t\tprivate: false,\n\t\t\topen_issues: 20,\n\t\t\thas_issues: true,\n\t\t\thas_downloads: true,\n\t\t\thas_wiki: true,\n\t\t\tlanguage: \"JavaScript\",\n\t\t\tcreated_at: \"2012-12-22T23:28:47-08:00\",\n\t\t\tpushed_at: \"2013-11-09T22:58:31-08:00\",\n\t\t\tmaster_branch: \"master\",\n\t\t\torganization: \"peers\",\n\t\t},\n\t},\n\t{\n\t\tcreated_at: \"2013-10-21T21:00:18-07:00\",\n\t\tpayload: { issue_id: 21337614, comment_id: 26775684 },\n\t\tpublic: true,\n\t\ttype: \"IssueCommentEvent\",\n\t\turl: \"https://github.com/peers/peerjs/issues/90#issuecomment-26775684\",\n\t\tactor: \"ericz\",\n\t\tactor_attributes: {\n\t\t\tlogin: \"ericz\",\n\t\t\ttype: \"User\",\n\t\t\tgravatar_id: \"c584ef7fe434331f7068ea49cacd88b9\",\n\t\t\tname: \"Eric Zhang\",\n\t\t\tcompany: \"Lever\",\n\t\t\tblog: \"https://twitter.com/reallyez\",\n\t\t\tlocation: \"Berkeley\",\n\t\t\temail: \"eric@ericzhang.com\",\n\t\t},\n\t\trepository: {\n\t\t\tid: 7292898,\n\t\t\tname: \"peerjs\",\n\t\t\turl: \"https://github.com/peers/peerjs\",\n\t\t\tdescription: \"Peer-to-peer data in the browser.\",\n\t\t\thomepage: \"https://peerjs.com\",\n\t\t\twatchers: 1647,\n\t\t\tstargazers: 1647,\n\t\t\tforks: 145,\n\t\t\tfork: false,\n\t\t\tsize: 2188,\n\t\t\towner: \"peers\",\n\t\t\tprivate: false,\n\t\t\topen_issues: 20,\n\t\t\thas_issues: true,\n\t\t\thas_downloads: true,\n\t\t\thas_wiki: true,\n\t\t\tlanguage: \"JavaScript\",\n\t\t\tcreated_at: \"2012-12-22T23:28:47-08:00\",\n\t\t\tpushed_at: \"2013-11-09T22:58:31-08:00\",\n\t\t\tmaster_branch: \"master\",\n\t\t\torganization: \"peers\",\n\t\t},\n\t},\n\t{\n\t\tcreated_at: \"2013-10-19T02:06:52-07:00\",\n\t\tpayload: {\n\t\t\tref: \"0.3.1\",\n\t\t\tref_type: \"tag\",\n\t\t\tmaster_branch: \"master\",\n\t\t\tdescription: \"Peer-to-peer data in the browser.\",\n\t\t},\n\t\tpublic: true,\n\t\ttype: \"CreateEvent\",\n\t\turl: \"https://github.com/peers/peerjs/tree/0.3.1\",\n\t\tactor: \"ericz\",\n\t\tactor_attributes: {\n\t\t\tlogin: \"ericz\",\n\t\t\ttype: \"User\",\n\t\t\tgravatar_id: \"c584ef7fe434331f7068ea49cacd88b9\",\n\t\t\tname: \"Eric Zhang\",\n\t\t\tcompany: \"Lever\",\n\t\t\tblog: \"https://twitter.com/reallyez\",\n\t\t\tlocation: \"Berkeley\",\n\t\t\temail: \"eric@ericzhang.com\",\n\t\t},\n\t\trepository: {\n\t\t\tid: 7292898,\n\t\t\tname: \"peerjs\",\n\t\t\turl: \"https://github.com/peers/peerjs\",\n\t\t\tdescription: \"Peer-to-peer data in the browser.\",\n\t\t\thomepage: \"https://peerjs.com\",\n\t\t\twatchers: 1647,\n\t\t\tstargazers: 1647,\n\t\t\tforks: 145,\n\t\t\tfork: false,\n\t\t\tsize: 2188,\n\t\t\towner: \"peers\",\n\t\t\tprivate: false,\n\t\t\topen_issues: 20,\n\t\t\thas_issues: true,\n\t\t\thas_downloads: true,\n\t\t\thas_wiki: true,\n\t\t\tlanguage: \"JavaScript\",\n\t\t\tcreated_at: \"2012-12-22T23:28:47-08:00\",\n\t\t\tpushed_at: \"2013-11-09T22:58:31-08:00\",\n\t\t\tmaster_branch: \"master\",\n\t\t\torganization: \"peers\",\n\t\t},\n\t},\n\t{\n\t\tcreated_at: \"2013-10-19T02:06:43-07:00\",\n\t\tpayload: { ref: \"0.3.1\", ref_type: \"tag\" },\n\t\tpublic: true,\n\t\ttype: \"DeleteEvent\",\n\t\turl: \"https://github.com/\",\n\t\tactor: \"ericz\",\n\t\tactor_attributes: {\n\t\t\tlogin: \"ericz\",\n\t\t\ttype: \"User\",\n\t\t\tgravatar_id: \"c584ef7fe434331f7068ea49cacd88b9\",\n\t\t\tname: \"Eric Zhang\",\n\t\t\tcompany: \"Lever\",\n\t\t\tblog: \"https://twitter.com/reallyez\",\n\t\t\tlocation: \"Berkeley\",\n\t\t\temail: \"eric@ericzhang.com\",\n\t\t},\n\t\trepository: {\n\t\t\tid: 7292898,\n\t\t\tname: \"peerjs\",\n\t\t\turl: \"https://github.com/peers/peerjs\",\n\t\t\tdescription: \"Peer-to-peer data in the browser.\",\n\t\t\thomepage: \"https://peerjs.com\",\n\t\t\twatchers: 1647,\n\t\t\tstargazers: 1647,\n\t\t\tforks: 145,\n\t\t\tfork: false,\n\t\t\tsize: 2188,\n\t\t\towner: \"peers\",\n\t\t\tprivate: false,\n\t\t\topen_issues: 20,\n\t\t\thas_issues: true,\n\t\t\thas_downloads: true,\n\t\t\thas_wiki: true,\n\t\t\tlanguage: \"JavaScript\",\n\t\t\tcreated_at: \"2012-12-22T23:28:47-08:00\",\n\t\t\tpushed_at: \"2013-11-09T22:58:31-08:00\",\n\t\t\tmaster_branch: \"master\",\n\t\t\torganization: \"peers\",\n\t\t},\n\t},\n\t{\n\t\tcreated_at: \"2013-10-19T02:05:42-07:00\",\n\t\tpayload: {\n\t\t\tshas: [\n\t\t\t\t[\n\t\t\t\t\t\"720eb3e881220f78eaca3d715ce7afe9324d1a3e\",\n\t\t\t\t\t\"really.ez@gmail.com\",\n\t\t\t\t\t\"0.3.1\",\n\t\t\t\t\t\"ericz\",\n\t\t\t\t\ttrue,\n\t\t\t\t],\n\t\t\t\t[\n\t\t\t\t\t\"79e10688c56524479f3b2c0cb069c4ac7e065b57\",\n\t\t\t\t\t\"really.ez@gmail.com\",\n\t\t\t\t\t\"Set maxRetransmits to 0 when reliable false\",\n\t\t\t\t\t\"ericz\",\n\t\t\t\t\ttrue,\n\t\t\t\t],\n\t\t\t],\n\t\t\tsize: 2,\n\t\t\tref: \"refs/heads/master\",\n\t\t\thead: \"79e10688c56524479f3b2c0cb069c4ac7e065b57\",\n\t\t},\n\t\tpublic: true,\n\t\ttype: \"PushEvent\",\n\t\turl: \"https://github.com/peers/peerjs/compare/b474a4cba6...79e10688c5\",\n\t\tactor: \"ericz\",\n\t\tactor_attributes: {\n\t\t\tlogin: \"ericz\",\n\t\t\ttype: \"User\",\n\t\t\tgravatar_id: \"c584ef7fe434331f7068ea49cacd88b9\",\n\t\t\tname: \"Eric Zhang\",\n\t\t\tcompany: \"Lever\",\n\t\t\tblog: \"https://twitter.com/reallyez\",\n\t\t\tlocation: \"Berkeley\",\n\t\t\temail: \"eric@ericzhang.com\",\n\t\t},\n\t\trepository: {\n\t\t\tid: 7292898,\n\t\t\tname: \"peerjs\",\n\t\t\turl: \"https://github.com/peers/peerjs\",\n\t\t\tdescription: \"Peer-to-peer data in the browser.\",\n\t\t\thomepage: \"https://peerjs.com\",\n\t\t\twatchers: 1647,\n\t\t\tstargazers: 1647,\n\t\t\tforks: 145,\n\t\t\tfork: false,\n\t\t\tsize: 2188,\n\t\t\towner: \"peers\",\n\t\t\tprivate: false,\n\t\t\topen_issues: 20,\n\t\t\thas_issues: true,\n\t\t\thas_downloads: true,\n\t\t\thas_wiki: true,\n\t\t\tlanguage: \"JavaScript\",\n\t\t\tcreated_at: \"2012-12-22T23:28:47-08:00\",\n\t\t\tpushed_at: \"2013-11-09T22:58:31-08:00\",\n\t\t\tmaster_branch: \"master\",\n\t\t\torganization: \"peers\",\n\t\t},\n\t},\n\t{\n\t\tcreated_at: \"2013-10-19T02:02:15-07:00\",\n\t\tpayload: {\n\t\t\tref: \"0.3.1\",\n\t\t\tref_type: \"tag\",\n\t\t\tmaster_branch: \"master\",\n\t\t\tdescription: \"Peer-to-peer data in the browser.\",\n\t\t},\n\t\tpublic: true,\n\t\ttype: \"CreateEvent\",\n\t\turl: \"https://github.com/peers/peerjs/tree/0.3.1\",\n\t\tactor: \"ericz\",\n\t\tactor_attributes: {\n\t\t\tlogin: \"ericz\",\n\t\t\ttype: \"User\",\n\t\t\tgravatar_id: \"c584ef7fe434331f7068ea49cacd88b9\",\n\t\t\tname: \"Eric Zhang\",\n\t\t\tcompany: \"Lever\",\n\t\t\tblog: \"https://twitter.com/reallyez\",\n\t\t\tlocation: \"Berkeley\",\n\t\t\temail: \"eric@ericzhang.com\",\n\t\t},\n\t\trepository: {\n\t\t\tid: 7292898,\n\t\t\tname: \"peerjs\",\n\t\t\turl: \"https://github.com/peers/peerjs\",\n\t\t\tdescription: \"Peer-to-peer data in the browser.\",\n\t\t\thomepage: \"https://peerjs.com\",\n\t\t\twatchers: 1647,\n\t\t\tstargazers: 1647,\n\t\t\tforks: 145,\n\t\t\tfork: false,\n\t\t\tsize: 2188,\n\t\t\towner: \"peers\",\n\t\t\tprivate: false,\n\t\t\topen_issues: 20,\n\t\t\thas_issues: true,\n\t\t\thas_downloads: true,\n\t\t\thas_wiki: true,\n\t\t\tlanguage: \"JavaScript\",\n\t\t\tcreated_at: \"2012-12-22T23:28:47-08:00\",\n\t\t\tpushed_at: \"2013-11-09T22:58:31-08:00\",\n\t\t\tmaster_branch: \"master\",\n\t\t\torganization: \"peers\",\n\t\t},\n\t},\n\t{\n\t\tcreated_at: \"2013-10-19T02:02:13-07:00\",\n\t\tpayload: {\n\t\t\tshas: [\n\t\t\t\t[\n\t\t\t\t\t\"b474a4cba6156dabd1312cd25a520b4286e362f6\",\n\t\t\t\t\t\"really.ez@gmail.com\",\n\t\t\t\t\t\"Setting reliable to false by default\",\n\t\t\t\t\t\"ericz\",\n\t\t\t\t\ttrue,\n\t\t\t\t],\n\t\t\t],\n\t\t\tsize: 1,\n\t\t\tref: \"refs/heads/master\",\n\t\t\thead: \"b474a4cba6156dabd1312cd25a520b4286e362f6\",\n\t\t},\n\t\tpublic: true,\n\t\ttype: \"PushEvent\",\n\t\turl: \"https://github.com/peers/peerjs/compare/93fc4931b2...b474a4cba6\",\n\t\tactor: \"ericz\",\n\t\tactor_attributes: {\n\t\t\tlogin: \"ericz\",\n\t\t\ttype: \"User\",\n\t\t\tgravatar_id: \"c584ef7fe434331f7068ea49cacd88b9\",\n\t\t\tname: \"Eric Zhang\",\n\t\t\tcompany: \"Lever\",\n\t\t\tblog: \"https://twitter.com/reallyez\",\n\t\t\tlocation: \"Berkeley\",\n\t\t\temail: \"eric@ericzhang.com\",\n\t\t},\n\t\trepository: {\n\t\t\tid: 7292898,\n\t\t\tname: \"peerjs\",\n\t\t\turl: \"https://github.com/peers/peerjs\",\n\t\t\tdescription: \"Peer-to-peer data in the browser.\",\n\t\t\thomepage: \"https://peerjs.com\",\n\t\t\twatchers: 1647,\n\t\t\tstargazers: 1647,\n\t\t\tforks: 145,\n\t\t\tfork: false,\n\t\t\tsize: 2188,\n\t\t\towner: \"peers\",\n\t\t\tprivate: false,\n\t\t\topen_issues: 20,\n\t\t\thas_issues: true,\n\t\t\thas_downloads: true,\n\t\t\thas_wiki: true,\n\t\t\tlanguage: \"JavaScript\",\n\t\t\tcreated_at: \"2012-12-22T23:28:47-08:00\",\n\t\t\tpushed_at: \"2013-11-09T22:58:31-08:00\",\n\t\t\tmaster_branch: \"master\",\n\t\t\torganization: \"peers\",\n\t\t},\n\t},\n\t{\n\t\tcreated_at: \"2013-10-19T01:53:16-07:00\",\n\t\tpayload: {\n\t\t\tshas: [\n\t\t\t\t[\n\t\t\t\t\t\"3949c236345171987b9291059fbaf9024eeca680\",\n\t\t\t\t\t\"really.ez@gmail.com\",\n\t\t\t\t\t\"0.3.1\",\n\t\t\t\t\t\"ericz\",\n\t\t\t\t\ttrue,\n\t\t\t\t],\n\t\t\t\t[\n\t\t\t\t\t\"93fc4931b24c0261c5fda71e0441a5ee8bda70b2\",\n\t\t\t\t\t\"really.ez@gmail.com\",\n\t\t\t\t\t\"Update reliable doc\",\n\t\t\t\t\t\"ericz\",\n\t\t\t\t\ttrue,\n\t\t\t\t],\n\t\t\t],\n\t\t\tsize: 2,\n\t\t\tref: \"refs/heads/master\",\n\t\t\thead: \"93fc4931b24c0261c5fda71e0441a5ee8bda70b2\",\n\t\t},\n\t\tpublic: true,\n\t\ttype: \"PushEvent\",\n\t\turl: \"https://github.com/peers/peerjs/compare/cd287e2fae...93fc4931b2\",\n\t\tactor: \"ericz\",\n\t\tactor_attributes: {\n\t\t\tlogin: \"ericz\",\n\t\t\ttype: \"User\",\n\t\t\tgravatar_id: \"c584ef7fe434331f7068ea49cacd88b9\",\n\t\t\tname: \"Eric Zhang\",\n\t\t\tcompany: \"Lever\",\n\t\t\tblog: \"https://twitter.com/reallyez\",\n\t\t\tlocation: \"Berkeley\",\n\t\t\temail: \"eric@ericzhang.com\",\n\t\t},\n\t\trepository: {\n\t\t\tid: 7292898,\n\t\t\tname: \"peerjs\",\n\t\t\turl: \"https://github.com/peers/peerjs\",\n\t\t\tdescription: \"Peer-to-peer data in the browser.\",\n\t\t\thomepage: \"https://peerjs.com\",\n\t\t\twatchers: 1647,\n\t\t\tstargazers: 1647,\n\t\t\tforks: 145,\n\t\t\tfork: false,\n\t\t\tsize: 2188,\n\t\t\towner: \"peers\",\n\t\t\tprivate: false,\n\t\t\topen_issues: 20,\n\t\t\thas_issues: true,\n\t\t\thas_downloads: true,\n\t\t\thas_wiki: true,\n\t\t\tlanguage: \"JavaScript\",\n\t\t\tcreated_at: \"2012-12-22T23:28:47-08:00\",\n\t\t\tpushed_at: \"2013-11-09T22:58:31-08:00\",\n\t\t\tmaster_branch: \"master\",\n\t\t\torganization: \"peers\",\n\t\t},\n\t},\n];\n"
  },
  {
    "path": "e2e/data.js",
    "content": "export const numbers = [\n\t0,\n\t1,\n\t-1,\n\t//\n\tMath.PI,\n\t-Math.PI,\n\t//8 bit\n\t0x7f,\n\t0x0f,\n\t//16 bit\n\t0x7fff,\n\t0x0fff,\n\t//32 bit\n\t0x7fffffff,\n\t0x0fffffff,\n\t//64 bit\n\t// 0x7FFFFFFFFFFFFFFF,\n\t// eslint-disable-next-line no-loss-of-precision\n\t0x0fffffffffffffff,\n];\nexport const long_string = \"ThisIsÁTèstString\".repeat(100000);\nexport const strings = [\n\t\"\",\n\t\"hello\",\n\t\"café\",\n\t\"中文\",\n\t\"broccoli🥦līp𨋢grin😃ok\",\n\t\"\\u{10ffff}\",\n];\n\nexport { commit_data } from \"./commit_data.js\";\n\nexport const uint8_arrays = [\n\tnew Uint8Array(),\n\tnew Uint8Array([0]),\n\tnew Uint8Array([0, 1, 2, 3, 4, 6, 7]),\n\tnew Uint8Array([0, 1, 2, 3, 4, 6, 78, 9, 10, 11, 12, 13, 14, 15]),\n\tnew Uint8Array([\n\t\t0, 1, 2, 3, 4, 6, 78, 9, 10, 11, 12, 13, 14, 15, 17, 18, 19, 20, 21, 22, 23,\n\t\t24, 25, 26, 27, 28, 30, 31,\n\t]),\n];\n\nexport const int32_arrays = [\n\tnew Int32Array([0].map((x) => -x)),\n\tnew Int32Array([0, 1, 2, 3, 4, 6, 7].map((x) => -x)),\n\tnew Int32Array(\n\t\t[0, 1, 2, 3, 4, 6, 78, 9, 10, 11, 12, 13, 14, 15].map((x) => -x),\n\t),\n\tnew Int32Array(\n\t\t[\n\t\t\t0, 1, 2, 3, 4, 6, 78, 9, 10, 11, 12, 13, 14, 15, 17, 18, 19, 20, 21, 22,\n\t\t\t23, 24, 25, 26, 27, 28, 30, 31,\n\t\t].map((x) => -x),\n\t),\n];\n\nexport const typed_array_view = new Uint8Array(uint8_arrays[4].buffer, 4);\n\nexport const array_buffers = [\n\tnew Uint8Array(),\n\tnew Uint8Array([0]),\n\tnew Uint8Array([0, 1, 2, 3, 4, 6, 7]),\n\tnew Uint8Array([0, 1, 2, 3, 4, 6, 78, 9, 10, 11, 12, 13, 14, 15]),\n\tnew Uint8Array([\n\t\t0, 1, 2, 3, 4, 6, 78, 9, 10, 11, 12, 13, 14, 15, 17, 18, 19, 20, 21, 22, 23,\n\t\t24, 25, 26, 27, 28, 30, 31,\n\t]),\n].map((x) => x.buffer);\n\nexport const dates = [\n\tnew Date(Date.UTC(2024, 1, 1, 1, 1, 1, 1)),\n\tnew Date(Date.UTC(1, 1, 1, 1, 1, 1, 1)),\n];\n"
  },
  {
    "path": "e2e/datachannel/Int32Array.js",
    "content": "import { int32_arrays } from \"../data.js\";\nimport { expect } from \"https://esm.sh/v126/chai@4.3.7/X-dHMvZXhwZWN0/es2021/chai.bundle.mjs\";\n\n/** @param {unknown[]} received */\nexport const check = (received) => {\n\tfor (const [i, typed_array] of int32_arrays.entries()) {\n\t\texpect(received[i]).to.be.an.instanceof(Int32Array);\n\t\texpect(received[i]).to.deep.equal(typed_array);\n\t}\n};\n/**\n * @param {import(\"../peerjs\").DataConnection} dataConnection\n */\nexport const send = (dataConnection) => {\n\tfor (const typed_array of int32_arrays) {\n\t\tdataConnection.send(typed_array);\n\t}\n};\n"
  },
  {
    "path": "e2e/datachannel/Int32Array_as_ArrayBuffer.js",
    "content": "import { int32_arrays } from \"../data.js\";\nimport { expect } from \"https://esm.sh/v126/chai@4.3.7/X-dHMvZXhwZWN0/es2021/chai.bundle.mjs\";\n\n/** @param {unknown[]} received */\nexport const check = (received) => {\n\tfor (const [i, typed_array] of int32_arrays.entries()) {\n\t\texpect(received[i]).to.be.an.instanceof(ArrayBuffer);\n\t\texpect(new Int32Array(received[i])).to.deep.equal(typed_array);\n\t}\n};\n/**\n * @param {import(\"../peerjs\").DataConnection} dataConnection\n */\nexport const send = (dataConnection) => {\n\tfor (const typed_array of int32_arrays) {\n\t\tdataConnection.send(typed_array);\n\t}\n};\n"
  },
  {
    "path": "e2e/datachannel/Int32Array_as_Uint8Array.js",
    "content": "import { int32_arrays } from \"../data.js\";\nimport { expect } from \"https://esm.sh/v126/chai@4.3.7/X-dHMvZXhwZWN0/es2021/chai.bundle.mjs\";\n\n/** @param {unknown[]} received */\nexport const check = (received) => {\n\tfor (const [i, typed_array] of int32_arrays.entries()) {\n\t\texpect(received[i]).to.be.an.instanceof(Uint8Array);\n\t\texpect(received[i]).to.deep.equal(new Uint8Array(typed_array.buffer));\n\t}\n};\n/**\n * @param {import(\"../peerjs\").DataConnection} dataConnection\n */\nexport const send = (dataConnection) => {\n\tfor (const typed_array of int32_arrays) {\n\t\tdataConnection.send(typed_array);\n\t}\n};\n"
  },
  {
    "path": "e2e/datachannel/TypedArrayView_as_ArrayBuffer.js",
    "content": "import { typed_array_view } from \"../data.js\";\nimport { expect } from \"https://esm.sh/v126/chai@4.3.7/X-dHMvZXhwZWN0/es2021/chai.bundle.mjs\";\n\n/** @param {unknown[]} received */\nexport const check = (received) => {\n\tconst received_typed_array_view = received[0];\n\texpect(received_typed_array_view).to.be.instanceOf(ArrayBuffer);\n\texpect(new Uint8Array(received_typed_array_view)).to.deep.equal(\n\t\ttyped_array_view,\n\t);\n};\n/**\n * @param {import(\"../peerjs\").DataConnection} dataConnection\n */\nexport const send = (dataConnection) => {\n\tdataConnection.send(typed_array_view);\n};\n"
  },
  {
    "path": "e2e/datachannel/Uint8Array.js",
    "content": "import { uint8_arrays } from \"../data.js\";\nimport { expect } from \"https://esm.sh/v126/chai@4.3.7/X-dHMvZXhwZWN0/es2021/chai.bundle.mjs\";\n\n/** @param {unknown[]} received */\nexport const check = (received) => {\n\tfor (const [i, typed_array] of uint8_arrays.entries()) {\n\t\texpect(received[i]).to.be.an.instanceof(Uint8Array);\n\t\texpect(received[i]).to.deep.equal(typed_array);\n\t}\n};\n/**\n * @param {import(\"../peerjs\").DataConnection} dataConnection\n */\nexport const send = (dataConnection) => {\n\tfor (const typed_array of uint8_arrays) {\n\t\tdataConnection.send(typed_array);\n\t}\n};\n"
  },
  {
    "path": "e2e/datachannel/Uint8Array_as_ArrayBuffer.js",
    "content": "import { uint8_arrays } from \"../data.js\";\nimport { expect } from \"https://esm.sh/v126/chai@4.3.7/X-dHMvZXhwZWN0/es2021/chai.bundle.mjs\";\n\n/** @param {unknown[]} received */\nexport const check = (received) => {\n\tfor (const [i, typed_array] of uint8_arrays.entries()) {\n\t\texpect(received[i]).to.be.an.instanceof(ArrayBuffer);\n\t\texpect(new Uint8Array(received[i])).to.deep.equal(typed_array);\n\t}\n};\n/**\n * @param {import(\"../peerjs\").DataConnection} dataConnection\n */\nexport const send = (dataConnection) => {\n\tfor (const typed_array of uint8_arrays) {\n\t\tdataConnection.send(typed_array);\n\t}\n};\n"
  },
  {
    "path": "e2e/datachannel/arraybuffers.js",
    "content": "import { array_buffers } from \"../data.js\";\nimport { expect } from \"https://esm.sh/v126/chai@4.3.7/X-dHMvZXhwZWN0/es2021/chai.bundle.mjs\";\n\n/** @param {unknown[]} received */\nexport const check = (received) => {\n\tfor (const [i, array_buffer] of array_buffers.entries()) {\n\t\texpect(received[i]).to.be.an.instanceof(ArrayBuffer);\n\t\texpect(received[i]).to.deep.equal(array_buffer);\n\t}\n};\n/**\n * @param {import(\"../peerjs\").DataConnection} dataConnection\n */\nexport const send = (dataConnection) => {\n\tfor (const array_buffer of array_buffers) {\n\t\tdataConnection.send(array_buffer);\n\t}\n};\n"
  },
  {
    "path": "e2e/datachannel/arraybuffers_as_uint8array.js",
    "content": "import { array_buffers } from \"../data.js\";\nimport { expect } from \"https://esm.sh/v126/chai@4.3.7/X-dHMvZXhwZWN0/es2021/chai.bundle.mjs\";\n\n/** @param {unknown[]} received */\nexport const check = (received) => {\n\tfor (const [i, array_buffer] of array_buffers.entries()) {\n\t\texpect(received[i]).to.be.an.instanceof(Uint8Array);\n\t\texpect(received[i]).to.deep.equal(new Uint8Array(array_buffer));\n\t}\n};\n/**\n * @param {import(\"../peerjs\").DataConnection} dataConnection\n */\nexport const send = (dataConnection) => {\n\tfor (const array_buffer of array_buffers) {\n\t\tdataConnection.send(array_buffer);\n\t}\n};\n"
  },
  {
    "path": "e2e/datachannel/arrays.js",
    "content": "import { commit_data } from \"../data.js\";\nimport { expect } from \"https://esm.sh/v126/chai@4.3.7/X-dHMvZXhwZWN0/es2021/chai.bundle.mjs\";\n\n/** @param {unknown[]} received */\nexport const check = (received) => {\n\texpect(received[1]).to.be.an(\"array\").with.lengthOf(commit_data.length);\n\texpect(received).to.deep.equal([[], commit_data]);\n};\n/**\n * @param {import(\"../peerjs\").DataConnection} dataConnection\n */\nexport const send = (dataConnection) => {\n\tdataConnection.send([]);\n\tdataConnection.send(commit_data);\n};\n"
  },
  {
    "path": "e2e/datachannel/arrays_unchunked.js",
    "content": "/**\n * JSON transfer does not chunk, commit_data is too large to send\n */\n\nimport { commit_data } from \"../data.js\";\nimport { expect } from \"https://esm.sh/v126/chai@4.3.7/X-dHMvZXhwZWN0/es2021/chai.bundle.mjs\";\n\n/** @param {unknown[]} received */\nexport const check = (received) => {\n\texpect(received).to.deep.equal([[], commit_data.slice(0, 2)]);\n};\n/**\n * @param {import(\"../peerjs\").DataConnection} dataConnection\n */\nexport const send = (dataConnection) => {\n\tdataConnection.send([]);\n\tdataConnection.send(commit_data.slice(0, 2));\n};\n"
  },
  {
    "path": "e2e/datachannel/blobs.js",
    "content": "import { commit_data } from \"../data.js\";\nimport { expect } from \"https://esm.sh/v126/chai@4.3.7/X-dHMvZXhwZWN0/es2021/chai.bundle.mjs\";\n\nconst Encoder = new TextEncoder();\nconst Decoder = new TextDecoder();\n\n/** @param {unknown[]} received */\nexport const check = (received) => {\n\texpect(received).to.be.an(\"array\").with.lengthOf(commit_data.length);\n\tconst commits_as_arraybuffer = commit_data.map(\n\t\t(blob) => Encoder.encode(JSON.stringify(blob)).buffer,\n\t);\n\texpect(received).to.deep.equal(commits_as_arraybuffer);\n\tconst parsed_received = received.map((r) => JSON.parse(Decoder.decode(r)));\n\texpect(parsed_received).to.deep.equal(commit_data);\n};\n/**\n * @param {import(\"../peerjs\").DataConnection} dataConnection\n */\nexport const send = async (dataConnection) => {\n\tfor (const commit of commit_data) {\n\t\tawait dataConnection.send(new Blob([JSON.stringify(commit)]));\n\t}\n};\n"
  },
  {
    "path": "e2e/datachannel/dates.js",
    "content": "import { dates } from \"../data.js\";\nimport { expect } from \"https://esm.sh/v126/chai@4.3.7/X-dHMvZXhwZWN0/es2021/chai.bundle.mjs\";\n\n/** @param {unknown[]} received */\nexport const check = (received) => {\n\texpect(received).to.deep.equal(dates);\n};\n/**\n * @param {import(\"../peerjs\").DataConnection} dataConnection\n */\nexport const send = (dataConnection) => {\n\tfor (const date of dates) {\n\t\tdataConnection.send(date);\n\t}\n};\n"
  },
  {
    "path": "e2e/datachannel/dates_as_json_string.js",
    "content": "import { dates } from \"../data.js\";\nimport { expect } from \"https://esm.sh/v126/chai@4.3.7/X-dHMvZXhwZWN0/es2021/chai.bundle.mjs\";\n\n/** @param {unknown[]} received */\nexport const check = (received) => {\n\tconsole.log(dates.map((date) => date.toString()));\n\texpect(received).to.deep.equal(dates.map((date) => date.toJSON()));\n};\n/**\n * @param {import(\"../peerjs\").DataConnection} dataConnection\n */\nexport const send = (dataConnection) => {\n\tfor (const date of dates) {\n\t\tdataConnection.send(date);\n\t}\n};\n"
  },
  {
    "path": "e2e/datachannel/dates_as_string.js",
    "content": "import { dates } from \"../data.js\";\nimport { expect } from \"https://esm.sh/v126/chai@4.3.7/X-dHMvZXhwZWN0/es2021/chai.bundle.mjs\";\n\n/** @param {unknown[]} received */\nexport const check = (received) => {\n\tconsole.log(dates.map((date) => date.toString()));\n\texpect(received).to.deep.equal(dates.map((date) => date.toString()));\n};\n/**\n * @param {import(\"../peerjs\").DataConnection} dataConnection\n */\nexport const send = (dataConnection) => {\n\tfor (const date of dates) {\n\t\tdataConnection.send(date);\n\t}\n};\n"
  },
  {
    "path": "e2e/datachannel/files.js",
    "content": "import { commit_data } from \"../data.js\";\nimport { expect } from \"https://esm.sh/v126/chai@4.3.7/X-dHMvZXhwZWN0/es2021/chai.bundle.mjs\";\n\nconst Encoder = new TextEncoder();\n\n/** @param {unknown[]} received */\nexport const check = (received) => {\n\texpect(received).to.be.an(\"array\").with.lengthOf(commit_data.length);\n\tconst commits_as_arraybuffer = commit_data.map(\n\t\t(blob) => Encoder.encode(JSON.stringify(blob)).buffer,\n\t);\n\texpect(received).to.deep.equal(commits_as_arraybuffer);\n};\n/**\n * @param {import(\"../peerjs\").DataConnection} dataConnection\n */\nexport const send = async (dataConnection) => {\n\tfor (const commit of commit_data) {\n\t\tawait dataConnection.send(\n\t\t\tnew File([JSON.stringify(commit)], \"commit.txt\", {\n\t\t\t\ttype: \"dummy/data\",\n\t\t\t}),\n\t\t);\n\t}\n};\n"
  },
  {
    "path": "e2e/datachannel/long_string.js",
    "content": "import { long_string } from \"../data.js\";\nimport { expect } from \"https://esm.sh/v126/chai@4.3.7/X-dHMvZXhwZWN0/es2021/chai.bundle.mjs\";\n\n/** @param {unknown[]} received */\nexport const check = (received) => {\n\texpect(received).to.deep.equal([long_string]);\n};\n/**\n * @param {import(\"../peerjs\").DataConnection} dataConnection\n */\nexport const send = (dataConnection) => {\n\tdataConnection.send(long_string);\n};\n"
  },
  {
    "path": "e2e/datachannel/numbers.js",
    "content": "import { numbers } from \"../data.js\";\nimport { expect } from \"https://esm.sh/v126/chai@4.3.7/X-dHMvZXhwZWN0/es2021/chai.bundle.mjs\";\n\n/** @param {unknown[]} received */\nexport const check = (received) => {\n\texpect(received).to.deep.equal(numbers);\n};\n/**\n * @param {import(\"../peerjs\").DataConnection} dataConnection\n */\nexport const send = (dataConnection) => {\n\tfor (const number of numbers) {\n\t\tdataConnection.send(number);\n\t}\n};\n"
  },
  {
    "path": "e2e/datachannel/objects.js",
    "content": "import { commit_data } from \"../data.js\";\nimport { expect } from \"https://esm.sh/v126/chai@4.3.7/X-dHMvZXhwZWN0/es2021/chai.bundle.mjs\";\n\n/** @param {unknown[]} received */\nexport const check = (received) => {\n\texpect(received).to.be.an(\"array\").with.lengthOf(commit_data.length);\n\texpect(received).to.deep.equal(commit_data);\n};\n/**\n * @param {import(\"../peerjs\").DataConnection} dataConnection\n */\nexport const send = (dataConnection) => {\n\tfor (const commit of commit_data) {\n\t\tdataConnection.send(commit);\n\t}\n};\n"
  },
  {
    "path": "e2e/datachannel/serialization.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n\t<head>\n\t\t<meta charset=\"UTF-8\" />\n\t\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n\t\t<title></title>\n\t\t<link rel=\"stylesheet\" href=\"../style.css\" />\n\t</head>\n\t<body>\n\t\t<h1>Serialization</h1>\n\t\t<div id=\"inputs\">\n\t\t\t<input type=\"text\" id=\"receiver-id\" placeholder=\"Receiver ID\" />\n\t\t\t<button id=\"connect-btn\" disabled>Connect</button>\n\t\t\t<button id=\"send-btn\">Send</button>\n\t\t\t<button id=\"check-btn\">Check</button>\n\t\t</div>\n\t\t<div id=\"messages\"></div>\n\t\t<div id=\"result\"></div>\n\t\t<div id=\"error-message\"></div>\n\t\t<script src=\"/dist/peerjs.min.js\" type=\"application/javascript\"></script>\n\t\t<script src=\"serialization.js\" type=\"module\"></script>\n\t</body>\n</html>\n"
  },
  {
    "path": "e2e/datachannel/serialization.js",
    "content": "/**\n * @type {typeof import(\"../../lib/exports.js\").Peer}\n */\nconst Peer = window.peerjs.Peer;\n\nconst params = new URLSearchParams(document.location.search);\nconst testfile = params.get(\"testfile\");\nconst serialization = params.get(\"serialization\");\n\n(async () => {\n\tlet serializers = {};\n\ttry {\n\t\tconst { MsgPack } = await import(\"/dist/serializer.msgpack.mjs\");\n\t\tserializers = {\n\t\t\tMsgPack,\n\t\t};\n\t} catch (e) {\n\t\tconsole.log(e);\n\t}\n\n\tconst { check, send } = await import(`./${testfile}.js`);\n\tdocument.getElementsByTagName(\"title\")[0].innerText =\n\t\twindow.location.hash.substring(1);\n\n\tconst checkBtn = document.getElementById(\"check-btn\");\n\tconst sendBtn = document.getElementById(\"send-btn\");\n\tconst receiverIdInput = document.getElementById(\"receiver-id\");\n\tconst connectBtn = document.getElementById(\"connect-btn\");\n\tconst messages = document.getElementById(\"messages\");\n\tconst result = document.getElementById(\"result\");\n\tconst errorMessage = document.getElementById(\"error-message\");\n\n\tconst peer = new Peer({\n\t\tdebug: 3,\n\t\tserializers,\n\t\tkey: params.get(\"key\"),\n\t});\n\tconst received = [];\n\t/**\n\t * @type {import(\"../../lib/exports.js\").DataConnection}\n\t */\n\tlet dataConnection;\n\tpeer\n\t\t.once(\"open\", (id) => {\n\t\t\tmessages.textContent = `Your Peer ID: ${id}`;\n\t\t})\n\t\t.once(\"error\", (error) => {\n\t\t\terrorMessage.textContent = JSON.stringify(error);\n\t\t})\n\t\t.once(\"connection\", (connection) => {\n\t\t\tdataConnection = connection;\n\t\t\tdataConnection.on(\"data\", (data) => {\n\t\t\t\tconsole.log(data);\n\t\t\t\treceived.push(data);\n\t\t\t});\n\t\t\tdataConnection.once(\"close\", () => {\n\t\t\t\tmessages.textContent = \"Closed!\";\n\t\t\t});\n\t\t});\n\n\tconnectBtn.addEventListener(\"click\", () => {\n\t\tconst receiverId = receiverIdInput.value;\n\t\tif (receiverId) {\n\t\t\tdataConnection = peer.connect(receiverId, {\n\t\t\t\treliable: true,\n\t\t\t\tserialization,\n\t\t\t});\n\t\t\tdataConnection.once(\"open\", () => {\n\t\t\t\tmessages.textContent = \"Connected!\";\n\t\t\t});\n\t\t}\n\t});\n\n\tcheckBtn.addEventListener(\"click\", async () => {\n\t\ttry {\n\t\t\tconsole.log(received);\n\t\t\tcheck(received);\n\t\t\tresult.textContent = \"Success!\";\n\t\t} catch (e) {\n\t\t\tresult.textContent = \"Failed!\";\n\t\t\terrorMessage.textContent = JSON.stringify(e.message);\n\t\t} finally {\n\t\t\tmessages.textContent = \"Checked!\";\n\t\t}\n\t});\n\n\tsendBtn.addEventListener(\"click\", async () => {\n\t\tdataConnection.once(\"error\", (err) => {\n\t\t\terrorMessage.innerText = err.toString();\n\t\t});\n\t\tawait send(dataConnection);\n\t\tdataConnection.close({ flush: true });\n\t\tmessages.textContent = \"Sent!\";\n\t});\n\twindow[\"connect-btn\"].disabled = false;\n})();\n"
  },
  {
    "path": "e2e/datachannel/serialization.page.ts",
    "content": "import { browser, $ } from \"@wdio/globals\";\n\nconst { BYPASS_WAF } = process.env;\n\nclass SerializationPage {\n\tget sendBtn() {\n\t\treturn $(\"button[id='send-btn']\");\n\t}\n\n\tget checkBtn() {\n\t\treturn $(\"button[id='check-btn']\");\n\t}\n\tget connectBtn() {\n\t\treturn $(\"button[id='connect-btn']\");\n\t}\n\n\tget receiverId() {\n\t\treturn $(\"input[id='receiver-id']\");\n\t}\n\n\tget messages() {\n\t\treturn $(\"div[id='messages']\");\n\t}\n\n\tget errorMessage() {\n\t\treturn $(\"div[id='error-message']\");\n\t}\n\n\tget result() {\n\t\treturn $(\"div[id='result']\");\n\t}\n\n\twaitForMessage(right: string) {\n\t\treturn browser.waitUntil(\n\t\t\tasync () => {\n\t\t\t\tconst messages = await this.messages.getText();\n\t\t\t\treturn messages.startsWith(right);\n\t\t\t},\n\t\t\t{ timeoutMsg: `Expected message to start with ${right}`, timeout: 10000 },\n\t\t);\n\t}\n\n\tasync open(testFile: string, serialization: string) {\n\t\tawait browser.switchWindow(\"Alice\");\n\t\tawait browser.url(\n\t\t\t`/e2e/datachannel/serialization.html?testfile=${testFile}&serialization=${serialization}&key=${BYPASS_WAF}#Alice`,\n\t\t);\n\t\tawait this.connectBtn.waitForEnabled();\n\n\t\tawait browser.switchWindow(\"Bob\");\n\t\tawait browser.url(\n\t\t\t`/e2e/datachannel/serialization.html?testfile=${testFile}&key=${BYPASS_WAF}#Bob`,\n\t\t);\n\t\tawait this.connectBtn.waitForEnabled();\n\t}\n\tasync init() {\n\t\tawait browser.url(\"/e2e/alice.html\");\n\t\tawait browser.waitUntil(async () => {\n\t\t\tconst title = await browser.getTitle();\n\t\t\treturn title === \"Alice\";\n\t\t});\n\t\tawait browser.newWindow(\"/e2e/bob.html\");\n\t\tawait browser.waitUntil(async () => {\n\t\t\tconst title = await browser.getTitle();\n\t\t\treturn title === \"Bob\";\n\t\t});\n\t}\n}\n\nexport default new SerializationPage();\n"
  },
  {
    "path": "e2e/datachannel/serializationTest.ts",
    "content": "import P from \"./serialization.page.js\";\nimport { browser, expect } from \"@wdio/globals\";\n\nexport const serializationTest =\n\t(testFile: string, serialization: string) => async () => {\n\t\tawait P.open(testFile, serialization);\n\t\tawait P.waitForMessage(\"Your Peer ID: \");\n\t\tconst bobId = (await P.messages.getText()).split(\"Your Peer ID: \")[1];\n\t\tawait browser.switchWindow(\"Alice\");\n\t\tawait P.waitForMessage(\"Your Peer ID: \");\n\t\tawait P.receiverId.setValue(bobId);\n\t\tawait P.connectBtn.click();\n\t\tawait P.waitForMessage(\"Connected!\");\n\t\tawait P.sendBtn.click();\n\t\tawait P.waitForMessage(\"Sent!\");\n\t\tawait browser.switchWindow(\"Bob\");\n\t\tawait P.waitForMessage(\"Closed!\");\n\t\tawait P.checkBtn.click();\n\t\tawait P.waitForMessage(\"Checked!\");\n\n\t\tawait expect(await P.errorMessage.getText()).toBe(\"\");\n\t\tawait expect(await P.result.getText()).toBe(\"Success!\");\n\t};\n"
  },
  {
    "path": "e2e/datachannel/serialization_binary.spec.ts",
    "content": "import P from \"./serialization.page.js\";\nimport { serializationTest } from \"./serializationTest.js\";\ndescribe(\"DataChannel:Binary\", () => {\n\tbeforeAll(\n\t\tasync () => {\n\t\t\tawait P.init();\n\t\t},\n\t\tjasmine.DEFAULT_TIMEOUT_INTERVAL,\n\t\t2,\n\t);\n\tit(\n\t\t\"should transfer numbers\",\n\t\tserializationTest(\"./numbers\", \"binary\"),\n\t\tjasmine.DEFAULT_TIMEOUT_INTERVAL,\n\t\t2,\n\t);\n\tit(\n\t\t\"should transfer strings\",\n\t\tserializationTest(\"./strings\", \"binary\"),\n\t\tjasmine.DEFAULT_TIMEOUT_INTERVAL,\n\t\t2,\n\t);\n\tit(\n\t\t\"should transfer long string\",\n\t\tserializationTest(\"./long_string\", \"binary\"),\n\t\tjasmine.DEFAULT_TIMEOUT_INTERVAL,\n\t\t2,\n\t);\n\tit(\n\t\t\"should transfer objects\",\n\t\tserializationTest(\"./objects\", \"binary\"),\n\t\tjasmine.DEFAULT_TIMEOUT_INTERVAL,\n\t\t2,\n\t);\n\tit(\n\t\t\"should transfer Blobs\",\n\t\tserializationTest(\"./blobs\", \"binary\"),\n\t\tjasmine.DEFAULT_TIMEOUT_INTERVAL,\n\t\t2,\n\t);\n\tit(\n\t\t\"should transfer Files\",\n\t\tserializationTest(\"./files\", \"binary\"),\n\t\tjasmine.DEFAULT_TIMEOUT_INTERVAL,\n\t\t2,\n\t);\n\tit(\n\t\t\"should transfer arrays\",\n\t\tserializationTest(\"./arrays\", \"binary\"),\n\t\tjasmine.DEFAULT_TIMEOUT_INTERVAL,\n\t\t2,\n\t);\n\tit(\n\t\t\"should transfer Dates as strings\",\n\t\tserializationTest(\"./dates_as_string\", \"binary\"),\n\t\tjasmine.DEFAULT_TIMEOUT_INTERVAL,\n\t\t2,\n\t);\n\tit(\n\t\t\"should transfer ArrayBuffers\",\n\t\tserializationTest(\"./arraybuffers\", \"binary\"),\n\t\tjasmine.DEFAULT_TIMEOUT_INTERVAL,\n\t\t2,\n\t);\n\tit(\n\t\t\"should transfer TypedArrayView as ArrayBuffer\",\n\t\tserializationTest(\"./TypedArrayView_as_ArrayBuffer\", \"binary\"),\n\t\tjasmine.DEFAULT_TIMEOUT_INTERVAL,\n\t\t2,\n\t);\n\tit(\n\t\t\"should transfer Uint8Arrays as ArrayBuffer\",\n\t\tserializationTest(\"./Uint8Array_as_ArrayBuffer\", \"binary\"),\n\t\tjasmine.DEFAULT_TIMEOUT_INTERVAL,\n\t\t2,\n\t);\n\tit(\n\t\t\"should transfer Int32Arrays as ArrayBuffer\",\n\t\tserializationTest(\"./Int32Array_as_ArrayBuffer\", \"binary\"),\n\t\tjasmine.DEFAULT_TIMEOUT_INTERVAL,\n\t\t2,\n\t);\n});\n"
  },
  {
    "path": "e2e/datachannel/serialization_json.spec.ts",
    "content": "import P from \"./serialization.page.js\";\nimport { serializationTest } from \"./serializationTest.js\";\n\ndescribe(\"DataChannel:JSON\", () => {\n\tbeforeAll(\n\t\tasync () => {\n\t\t\tawait P.init();\n\t\t},\n\t\tjasmine.DEFAULT_TIMEOUT_INTERVAL,\n\t\t2,\n\t);\n\tit(\n\t\t\"should transfer numbers\",\n\t\tserializationTest(\"./numbers\", \"json\"),\n\t\tjasmine.DEFAULT_TIMEOUT_INTERVAL,\n\t\t2,\n\t);\n\tit(\n\t\t\"should transfer strings\",\n\t\tserializationTest(\"./strings\", \"json\"),\n\t\tjasmine.DEFAULT_TIMEOUT_INTERVAL,\n\t\t2,\n\t);\n\t// it(\"should transfer long string\", serializationTest(\"./long_string\", \"json\"));\n\tit(\n\t\t\"should transfer objects\",\n\t\tserializationTest(\"./objects\", \"json\"),\n\t\tjasmine.DEFAULT_TIMEOUT_INTERVAL,\n\t\t2,\n\t);\n\tit(\n\t\t\"should transfer arrays (no chunks)\",\n\t\tserializationTest(\"./arrays_unchunked\", \"json\"),\n\t\tjasmine.DEFAULT_TIMEOUT_INTERVAL,\n\t\t2,\n\t);\n\tit(\n\t\t\"should transfer Dates as strings\",\n\t\tserializationTest(\"./dates_as_json_string\", \"json\"),\n\t\tjasmine.DEFAULT_TIMEOUT_INTERVAL,\n\t\t2,\n\t);\n\t// it(\"should transfer ArrayBuffers\", serializationTest(\"./arraybuffers\", \"json\"));\n\t// it(\"should transfer TypedArrayView\", serializationTest(\"./typed_array_view\", \"json\"));\n\t// it(\n\t// \t\"should transfer Uint8Arrays\",\n\t// \tserializationTest(\"./Uint8Array\", \"json\"),\n\t// );\n\t// it(\n\t// \t\"should transfer Int32Arrays\",\n\t// \tserializationTest(\"./Int32Array\", \"json\"),\n\t// );\n});\n"
  },
  {
    "path": "e2e/datachannel/serialization_msgpack.spec.ts",
    "content": "import P from \"./serialization.page.js\";\nimport { serializationTest } from \"./serializationTest.js\";\nimport { browser } from \"@wdio/globals\";\n\ndescribe(\"DataChannel:MsgPack\", function () {\n\tbeforeAll(async function () {\n\t\tawait P.init();\n\t});\n\tbeforeEach(async function () {\n\t\tif (\n\t\t\t// @ts-ignore\n\t\t\tbrowser.capabilities.browserName === \"firefox\" &&\n\t\t\t// @ts-ignore\n\t\t\tparseInt(browser.capabilities.browserVersion) < 102\n\t\t) {\n\t\t\tpending(\"Firefox 102+ required for Streams\");\n\t\t}\n\t});\n\tit(\"should transfer numbers\", serializationTest(\"./numbers\", \"MsgPack\"));\n\tit(\"should transfer strings\", serializationTest(\"./strings\", \"MsgPack\"));\n\tit(\n\t\t\"should transfer long string\",\n\t\tserializationTest(\"./long_string\", \"MsgPack\"),\n\t);\n\tit(\"should transfer objects\", serializationTest(\"./objects\", \"MsgPack\"));\n\tit(\"should transfer arrays\", serializationTest(\"./arrays\", \"MsgPack\"));\n\tit(\n\t\t\"should transfer Dates as strings\",\n\t\tserializationTest(\"./dates\", \"MsgPack\"),\n\t);\n\t// it(\"should transfer ArrayBuffers\", serializationTest(\"./arraybuffers\", \"MsgPack\"));\n\tit(\n\t\t\"should transfer TypedArrayView\",\n\t\tserializationTest(\"./typed_array_view\", \"MsgPack\"),\n\t);\n\tit(\n\t\t\"should transfer Uint8Arrays\",\n\t\tserializationTest(\"./Uint8Array\", \"MsgPack\"),\n\t);\n\tit(\n\t\t\"should transfer Int32Arrays as Uint8Arrays\",\n\t\tserializationTest(\"./Int32Array_as_Uint8Array\", \"MsgPack\"),\n\t);\n});\n"
  },
  {
    "path": "e2e/datachannel/strings.js",
    "content": "import { strings } from \"../data.js\";\nimport { expect } from \"https://esm.sh/v126/chai@4.3.7/X-dHMvZXhwZWN0/es2021/chai.bundle.mjs\";\n\n/** @param {unknown[]} received */\nexport const check = (received) => {\n\texpect(received).to.deep.equal(strings);\n};\n/**\n * @param {import(\"../peerjs\").DataConnection} dataConnection\n */\nexport const send = (dataConnection) => {\n\tfor (const string of strings) {\n\t\tdataConnection.send(string);\n\t}\n};\n"
  },
  {
    "path": "e2e/datachannel/typed_array_view.js",
    "content": "import { typed_array_view } from \"../data.js\";\nimport { expect } from \"https://esm.sh/v126/chai@4.3.7/X-dHMvZXhwZWN0/es2021/chai.bundle.mjs\";\n\n/** @param {unknown[]} received */\nexport const check = (received) => {\n\tconst received_typed_array_view = received[0];\n\texpect(received_typed_array_view).to.deep.equal(typed_array_view);\n\tfor (const [i, v] of received_typed_array_view.entries()) {\n\t\texpect(v).to.deep.equal(typed_array_view[i]);\n\t}\n};\n/**\n * @param {import(\"../peerjs\").DataConnection} dataConnection\n */\nexport const send = (dataConnection) => {\n\tdataConnection.send(typed_array_view);\n};\n"
  },
  {
    "path": "e2e/mediachannel/close.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n\t<head>\n\t\t<meta charset=\"UTF-8\" />\n\t\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n\t\t<title></title>\n\t\t<link rel=\"stylesheet\" href=\"../style.css\" />\n\t</head>\n\t<body>\n\t\t<h1>MediaChannel</h1>\n\t\t<canvas id=\"sender-stream\" width=\"200\" height=\"100\"></canvas>\n\t\t<video id=\"receiver-stream\" autoplay></video>\n\t\t<script>\n\t\t\tconst canvas = document.getElementById(\"sender-stream\");\n\t\t\tconst ctx = canvas.getContext(\"2d\");\n\n\t\t\t// Set the canvas background color to white\n\t\t\tctx.fillStyle = \"white\";\n\t\t\tctx.fillRect(0, 0, canvas.width, canvas.height);\n\n\t\t\t// Draw the text \"Alice\" in black\n\t\t\tctx.font = \"30px sans-serif\";\n\t\t\tctx.fillStyle = \"black\";\n\t\t\tctx.fillText(window.location.hash.substring(1), 50, 50);\n\t\t</script>\n\t\t<div id=\"inputs\">\n\t\t\t<input type=\"text\" id=\"receiver-id\" placeholder=\"Receiver ID\" />\n\t\t\t<button id=\"call-btn\" disabled>Call</button>\n\t\t\t<button id=\"close-btn\">Hang up</button>\n\t\t</div>\n\t\t<div id=\"messages\"></div>\n\t\t<div id=\"result\"></div>\n\t\t<div id=\"error-message\"></div>\n\t\t<video></video>\n\t\t<script src=\"/dist/peerjs.js\"></script>\n\t\t<script src=\"close.js\" type=\"application/javascript\"></script>\n\t</body>\n</html>\n"
  },
  {
    "path": "e2e/mediachannel/close.js",
    "content": "/**\n * @type {typeof import(\"../..\").Peer}\n */\nconst Peer = window.peerjs.Peer;\n\nconst params = new URLSearchParams(document.location.search);\n\ndocument.getElementsByTagName(\"title\")[0].innerText =\n\twindow.location.hash.substring(1);\n\nconst callBtn = document.getElementById(\"call-btn\");\nconsole.log(callBtn);\nconst receiverIdInput = document.getElementById(\"receiver-id\");\nconst closeBtn = document.getElementById(\"close-btn\");\nconst messages = document.getElementById(\"messages\");\nconst errorMessage = document.getElementById(\"error-message\");\n\nconst stream = window[\"sender-stream\"].captureStream(30);\nconst peer = new Peer({ debug: 3, key: params.get(\"key\") });\n/**\n * @type {import(\"peerjs\").MediaConnection}\n */\nlet mediaConnection;\npeer\n\t.once(\"open\", (id) => {\n\t\tmessages.textContent = `Your Peer ID: ${id}`;\n\t})\n\t.once(\"error\", (error) => {\n\t\terrorMessage.textContent = JSON.stringify(error);\n\t})\n\t.once(\"call\", (call) => {\n\t\tmediaConnection = call;\n\t\tmediaConnection\n\t\t\t.once(\"stream\", function (stream) {\n\t\t\t\tconst video = document.getElementById(\"receiver-stream\");\n\t\t\t\tvideo.srcObject = stream;\n\t\t\t\tvideo.play();\n\t\t\t})\n\t\t\t.once(\"close\", () => {\n\t\t\t\tmessages.textContent = \"Closed!\";\n\t\t\t})\n\t\t\t.once(\"willCloseOnRemote\", () => {\n\t\t\t\tmessages.textContent = \"Connected!\";\n\t\t\t});\n\t\tcall.answer(stream);\n\t});\n\ncallBtn.addEventListener(\"click\", async () => {\n\tconsole.log(\"calling\");\n\n\t/** @type {string} */\n\tconst receiverId = receiverIdInput.value;\n\tif (receiverId) {\n\t\tmediaConnection = peer.call(receiverId, stream);\n\t\tmediaConnection\n\t\t\t.once(\"stream\", (stream) => {\n\t\t\t\tconst video = document.getElementById(\"receiver-stream\");\n\t\t\t\tvideo.srcObject = stream;\n\t\t\t\tvideo.play();\n\t\t\t\tmessages.innerText = \"Connected!\";\n\t\t\t})\n\t\t\t.once(\"close\", () => {\n\t\t\t\tmessages.textContent = \"Closed!\";\n\t\t\t});\n\t}\n});\n\ncloseBtn.addEventListener(\"click\", () => {\n\tmediaConnection.close();\n});\n\ncallBtn.disabled = false;\n"
  },
  {
    "path": "e2e/mediachannel/close.page.ts",
    "content": "import { browser, $ } from \"@wdio/globals\";\n\nconst { BYPASS_WAF } = process.env;\n\nclass SerializationPage {\n\tget receiverId() {\n\t\treturn $(\"input[id='receiver-id']\");\n\t}\n\tget callBtn() {\n\t\treturn $(\"button[id='call-btn']\");\n\t}\n\n\tget messages() {\n\t\treturn $(\"div[id='messages']\");\n\t}\n\n\tget closeBtn() {\n\t\treturn $(\"button[id='close-btn']\");\n\t}\n\n\tget errorMessage() {\n\t\treturn $(\"div[id='error-message']\");\n\t}\n\n\tget result() {\n\t\treturn $(\"div[id='result']\");\n\t}\n\n\twaitForMessage(right: string) {\n\t\treturn browser.waitUntil(\n\t\t\tasync () => {\n\t\t\t\tconst messages = await this.messages.getText();\n\t\t\t\treturn messages.startsWith(right);\n\t\t\t},\n\t\t\t{ timeoutMsg: `Expected message to start with ${right}`, timeout: 10000 },\n\t\t);\n\t}\n\n\tasync open() {\n\t\tawait browser.switchWindow(\"Alice\");\n\t\tawait browser.url(`/e2e/mediachannel/close.html?key=${BYPASS_WAF}#Alice`);\n\t\tawait this.callBtn.waitForEnabled();\n\n\t\tawait browser.switchWindow(\"Bob\");\n\t\tawait browser.url(`/e2e/mediachannel/close.html?key=${BYPASS_WAF}#Bob`);\n\t\tawait this.callBtn.waitForEnabled();\n\t}\n\tasync init() {\n\t\tawait browser.url(\"/e2e/alice.html\");\n\t\tawait browser.waitUntil(async () => {\n\t\t\tconst title = await browser.getTitle();\n\t\t\treturn title === \"Alice\";\n\t\t});\n\t\tawait browser.pause(1000);\n\t\tawait browser.newWindow(\"/e2e/bob.html\");\n\t\tawait browser.waitUntil(async () => {\n\t\t\tconst title = await browser.getTitle();\n\t\t\treturn title === \"Bob\";\n\t\t});\n\t\tawait browser.pause(1000);\n\t}\n}\n\nexport default new SerializationPage();\n"
  },
  {
    "path": "e2e/mediachannel/close.spec.ts",
    "content": "import P from \"./close.page.js\";\nimport { browser } from \"@wdio/globals\";\n\ndescribe(\"MediaStream\", () => {\n\tbeforeAll(\n\t\tasync () => {\n\t\t\tawait P.init();\n\t\t},\n\t\tjasmine.DEFAULT_TIMEOUT_INTERVAL,\n\t\t1,\n\t);\n\tit(\n\t\t\"should close the remote stream\",\n\t\tasync () => {\n\t\t\tawait P.open();\n\t\t\tawait P.waitForMessage(\"Your Peer ID: \");\n\t\t\tconst bobId = (await P.messages.getText()).split(\"Your Peer ID: \")[1];\n\t\t\tawait browser.switchWindow(\"Alice\");\n\t\t\tawait P.waitForMessage(\"Your Peer ID: \");\n\t\t\tawait P.receiverId.setValue(bobId);\n\t\t\tawait P.callBtn.click();\n\t\t\tawait P.waitForMessage(\"Connected!\");\n\t\t\tawait browser.switchWindow(\"Bob\");\n\t\t\tawait P.waitForMessage(\"Connected!\");\n\t\t\tawait P.closeBtn.click();\n\t\t\tawait P.waitForMessage(\"Closed!\");\n\t\t\tawait browser.switchWindow(\"Alice\");\n\t\t\tawait P.waitForMessage(\"Closed!\");\n\t\t},\n\t\tjasmine.DEFAULT_TIMEOUT_INTERVAL,\n\t\t2,\n\t);\n});\n"
  },
  {
    "path": "e2e/package.json",
    "content": "{\n\t\"type\": \"module\"\n}\n"
  },
  {
    "path": "e2e/peer/disconnected.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n\t<head>\n\t\t<meta charset=\"UTF-8\" />\n\t\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n\t\t<title></title>\n\t\t<link rel=\"stylesheet\" href=\"../style.css\" />\n\t</head>\n\t<body>\n\t\t<h1>ID-TAKEN</h1>\n\t\t<div id=\"messages\"></div>\n\t\t<div id=\"error-message\"></div>\n\t\t<script src=\"/dist/peerjs.js\"></script>\n\t\t<script type=\"application/javascript\">\n\t\t\t/**\n\t\t\t * @type {typeof import(\"../..\").Peer}\n\t\t\t */\n\t\t\tconst Peer = window.peerjs.Peer;\n\n\t\t\tconst params = new URLSearchParams(document.location.search);\n\n\t\t\tconst messages = document.getElementById(\"messages\");\n\n\t\t\tconst peer = new Peer({ key: params.get(\"key\") });\n\t\t\tpeer\n\t\t\t\t.once(\n\t\t\t\t\t\"error\",\n\t\t\t\t\t(error) => void (messages.textContent = JSON.stringify(error)),\n\t\t\t\t)\n\t\t\t\t.once(\"open\", (id) => {\n\t\t\t\t\tpeer.disconnect();\n\t\t\t\t\tpeer.connect(\"otherid\");\n\t\t\t\t});\n\t\t</script>\n\t</body>\n</html>\n"
  },
  {
    "path": "e2e/peer/id-taken.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n\t<head>\n\t\t<meta charset=\"UTF-8\" />\n\t\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n\t\t<title></title>\n\t\t<link rel=\"stylesheet\" href=\"../style.css\" />\n\t</head>\n\t<body>\n\t\t<h1>ID-TAKEN</h1>\n\t\t<div id=\"messages\"></div>\n\t\t<div id=\"error-message\"></div>\n\t\t<script src=\"/dist/peerjs.js\"></script>\n\t\t<script type=\"application/javascript\">\n\t\t\t/**\n\t\t\t * @type {typeof import(\"../..\").Peer}\n\t\t\t */\n\t\t\tconst Peer = window.peerjs.Peer;\n\n\t\t\tconst params = new URLSearchParams(document.location.search);\n\n\t\t\tconst messages = document.getElementById(\"messages\");\n\t\t\tconst errorMessage = document.getElementById(\"error-message\");\n\n\t\t\t// Peer A should be created without an error\n\t\t\tconst peerA = new Peer({ key: params.get(\"key\") })\n\t\t\t\t.once(\n\t\t\t\t\t\"error\",\n\t\t\t\t\t(err) => (errorMessage.textContent += JSON.stringify(err)),\n\t\t\t\t)\n\t\t\t\t.once(\"open\", (id) => {\n\t\t\t\t\t// Create 10 new `Peer`s that will try to steel A's id\n\t\t\t\t\tlet peers_try_to_take = Array.from(\n\t\t\t\t\t\t{ length: 10 },\n\t\t\t\t\t\t(_, i) =>\n\t\t\t\t\t\t\tnew Promise((resolve, reject) =>\n\t\t\t\t\t\t\t\tnew Peer(id, { key: params.get(\"BYPASS_WAF\") })\n\t\t\t\t\t\t\t\t\t.once(\"open\", () =>\n\t\t\t\t\t\t\t\t\t\treject(`Peer ${i} failed! Connection got established.`),\n\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t\t.once(\"error\", (error) => {\n\t\t\t\t\t\t\t\t\t\tif (error.type === \"unavailable-id\") {\n\t\t\t\t\t\t\t\t\t\t\tresolve(`ID already taken. (${i})`);\n\t\t\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\t\treject(error);\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t),\n\t\t\t\t\t);\n\t\t\t\t\tPromise.all(peers_try_to_take)\n\t\t\t\t\t\t.then(() => (messages.textContent = \"No ID takeover\"))\n\t\t\t\t\t\t.catch(\n\t\t\t\t\t\t\t(error) => (errorMessage.textContent += JSON.stringify(error)),\n\t\t\t\t\t\t);\n\t\t\t\t});\n\t\t</script>\n\t</body>\n</html>\n"
  },
  {
    "path": "e2e/peer/peer.page.ts",
    "content": "import { browser, $ } from \"@wdio/globals\";\n\nconst { BYPASS_WAF } = process.env;\n\nclass PeerPage {\n\tget messages() {\n\t\treturn $(\"div[id='messages']\");\n\t}\n\n\tget errorMessage() {\n\t\treturn $(\"div[id='error-message']\");\n\t}\n\n\twaitForMessage(right: string) {\n\t\treturn browser.waitUntil(\n\t\t\tasync () => {\n\t\t\t\tconst messages = await this.messages.getText();\n\t\t\t\treturn messages.startsWith(right);\n\t\t\t},\n\t\t\t{ timeoutMsg: `Expected message to start with ${right}`, timeout: 10000 },\n\t\t);\n\t}\n\n\tasync open(test: string) {\n\t\tawait browser.url(`/e2e/peer/${test}.html?key=${BYPASS_WAF}`);\n\t}\n}\n\nexport default new PeerPage();\n"
  },
  {
    "path": "e2e/peer/peer.spec.ts",
    "content": "import P from \"./peer.page.js\";\nimport { browser, expect } from \"@wdio/globals\";\n\ndescribe(\"Peer\", () => {\n\tit(\"should emit an error, when the ID is already taken\", async () => {\n\t\tawait P.open(\"id-taken\");\n\t\tawait P.waitForMessage(\"No ID takeover\");\n\t\texpect(await P.errorMessage.getText()).toBe(\"\");\n\t});\n\tit(\"should emit an error, when the server is unavailable\", async () => {\n\t\tawait P.open(\"server-unavailable\");\n\t\tawait P.waitForMessage('{\"type\":\"server-error\"}');\n\t\texpect(await P.errorMessage.getText()).toBe(\"\");\n\t});\n\tit(\"should emit an error, when it got disconnected from the server\", async () => {\n\t\tawait P.open(\"disconnected\");\n\t\tawait P.waitForMessage('{\"type\":\"disconnected\"}');\n\t\texpect(await P.errorMessage.getText()).toBe(\"\");\n\t});\n});\n"
  },
  {
    "path": "e2e/peer/server-unavailable.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n\t<head>\n\t\t<meta charset=\"UTF-8\" />\n\t\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n\t\t<title></title>\n\t\t<link rel=\"stylesheet\" href=\"../style.css\" />\n\t</head>\n\t<body>\n\t\t<h1>ID-TAKEN</h1>\n\t\t<div id=\"messages\"></div>\n\t\t<div id=\"error-message\"></div>\n\t\t<script src=\"/dist/peerjs.js\"></script>\n\t\t<script type=\"application/javascript\">\n\t\t\t/**\n\t\t\t * @type {typeof import(\"../..\").Peer}\n\t\t\t */\n\t\t\tconst Peer = window.peerjs.Peer;\n\n\t\t\tconst messages = document.getElementById(\"messages\");\n\t\t\tconst errorMessage = document.getElementById(\"error-message\");\n\n\t\t\tnew Peer({\n\t\t\t\thost: \"thisserverwillneverexist.example.com\",\n\t\t\t}).once(\n\t\t\t\t\"error\",\n\t\t\t\t(error) => void (messages.textContent = JSON.stringify(error)),\n\t\t\t);\n\t\t</script>\n\t</body>\n</html>\n"
  },
  {
    "path": "e2e/style.css",
    "content": "body {\n\tfont-family: Arial, sans-serif;\n\tmax-width: 800px;\n\tmargin: 0 auto;\n\tpadding: 20px;\n}\n\n#inputs {\n\tdisplay: flex;\n\tflex-wrap: wrap;\n\tgap: 10px;\n\talign-items: center;\n}\n\nbutton {\n\tbackground-color: #007bff;\n\tcolor: white;\n\tborder: none;\n\tpadding: 8px 16px;\n\tborder-radius: 4px;\n\tcursor: pointer;\n}\n\nbutton:hover {\n\tbackground-color: #0056b3;\n}\n"
  },
  {
    "path": "e2e/tsconfig.json",
    "content": "{\n\t\"compilerOptions\": {\n\t\t/* Visit https://aka.ms/tsconfig.json to read more about this file */\n\n\t\t/* Basic Options */\n\t\t// \"incremental\": true,                   /* Enable incremental compilation */\n\t\t\"target\": \"ES2022\" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */,\n\t\t\"module\": \"ESNext\" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */,\n\t\t// \"lib\": [],                             /* Specify library files to be included in the compilation. */\n\t\t// \"allowJs\": true,                       /* Allow javascript files to be compiled. */\n\t\t// \"checkJs\": true,                       /* Report errors in .js files. */\n\t\t// \"jsx\": \"preserve\",                     /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */\n\t\t// \"declaration\": true,                   /* Generates corresponding '.d.ts' file. */\n\t\t// \"declarationMap\": true,                /* Generates a sourcemap for each corresponding '.d.ts' file. */\n\t\t// \"sourceMap\": true,                     /* Generates corresponding '.map' file. */\n\t\t// \"outFile\": \"./\",                       /* Concatenate and emit output to single file. */\n\t\t// \"outDir\": \"./\",                        /* Redirect output structure to the directory. */\n\t\t// \"rootDir\": \"./\",                       /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */\n\t\t// \"composite\": true,                     /* Enable project compilation */\n\t\t// \"tsBuildInfoFile\": \"./\",               /* Specify file to store incremental compilation information */\n\t\t// \"removeComments\": true,                /* Do not emit comments to output. */\n\t\t// \"noEmit\": true,                        /* Do not emit outputs. */\n\t\t// \"importHelpers\": true,                 /* Import emit helpers from 'tslib'. */\n\t\t// \"downlevelIteration\": true,            /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */\n\t\t// \"isolatedModules\": true,               /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */\n\n\t\t/* Strict Type-Checking Options */\n\t\t\"strict\": true /* Enable all strict type-checking options. */,\n\t\t// \"noImplicitAny\": true,                 /* Raise error on expressions and declarations with an implied 'any' type. */\n\t\t// \"strictNullChecks\": true,              /* Enable strict null checks. */\n\t\t// \"strictFunctionTypes\": true,           /* Enable strict checking of function types. */\n\t\t// \"strictBindCallApply\": true,           /* Enable strict 'bind', 'call', and 'apply' methods on functions. */\n\t\t// \"strictPropertyInitialization\": true,  /* Enable strict checking of property initialization in classes. */\n\t\t// \"noImplicitThis\": true,                /* Raise error on 'this' expressions with an implied 'any' type. */\n\t\t// \"alwaysStrict\": true,                  /* Parse in strict mode and emit \"use strict\" for each source file. */\n\n\t\t/* Additional Checks */\n\t\t// \"noUnusedLocals\": true,                /* Report errors on unused locals. */\n\t\t// \"noUnusedParameters\": true,            /* Report errors on unused parameters. */\n\t\t// \"noImplicitReturns\": true,             /* Report error when not all code paths in function return a value. */\n\t\t// \"noFallthroughCasesInSwitch\": true,    /* Report errors for fallthrough cases in switch statement. */\n\t\t// \"noUncheckedIndexedAccess\": true,      /* Include 'undefined' in index signature results */\n\n\t\t/* Module Resolution Options */\n\t\t\"moduleResolution\": \"node\" /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */,\n\t\t// \"baseUrl\": \"./\",                       /* Base directory to resolve non-absolute module names. */\n\t\t// \"paths\": {},                           /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */\n\t\t// \"rootDirs\": [],                        /* List of root folders whose combined content represents the structure of the project at runtime. */\n\t\t// \"typeRoots\": [],                       /* List of folders to include type definitions from. */\n\t\t\"types\": [\n\t\t\t\"node\",\n\t\t\t\"@wdio/globals/types\",\n\t\t\t\"jasmine\",\n\t\t\t\"@wdio/jasmine-framework\"\n\t\t],\n\t\t\"allowSyntheticDefaultImports\": true /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */,\n\t\t// \"esModuleInterop\": true,               /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */\n\t\t// \"preserveSymlinks\": true,              /* Do not resolve the real path of symlinks. */\n\t\t// \"allowUmdGlobalAccess\": true,          /* Allow accessing UMD globals from modules. */\n\n\t\t/* Source Map Options */\n\t\t// \"sourceRoot\": \"\",                      /* Specify the location where debugger should locate TypeScript files instead of source locations. */\n\t\t// \"mapRoot\": \"\",                         /* Specify the location where debugger should locate map files instead of generated locations. */\n\t\t// \"inlineSourceMap\": true,               /* Emit a single file with source maps instead of having a separate file. */\n\t\t// \"inlineSources\": true,                 /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */\n\n\t\t/* Experimental Options */\n\t\t// \"experimentalDecorators\": true,        /* Enables experimental support for ES7 decorators. */\n\t\t// \"emitDecoratorMetadata\": true,         /* Enables experimental support for emitting type metadata for decorators. */\n\n\t\t/* Advanced Options */\n\t\t\"skipLibCheck\": true /* Skip type checking of declaration files. */,\n\t\t\"forceConsistentCasingInFileNames\": true /* Disallow inconsistently-cased references to the same file. */\n\t}\n}\n"
  },
  {
    "path": "e2e/types.d.ts",
    "content": "/* eslint no-unused-vars: 0 */\n\ndeclare module \"https://esm.sh/v126/chai@4.3.7/X-dHMvZXhwZWN0/es2021/chai.bundle.mjs\" {\n\texport = chai;\n}\n\ninterface Window {\n\t\"connect-btn\": HTMLButtonElement;\n\tsend: (dataConnection: import(\"../../peerjs\").DataConnection) => void;\n\tcheck: (received: unknown[]) => void;\n}\n"
  },
  {
    "path": "e2e/wdio.bstack.conf.ts",
    "content": "import { config as sharedConfig } from \"./wdio.shared.conf.js\";\n\nexport const config: WebdriverIO.Config = {\n\t...sharedConfig,\n\t...{\n\t\tmaxInstances: 5,\n\t\tuser: process.env.BROWSERSTACK_USERNAME,\n\t\tkey: process.env.BROWSERSTACK_ACCESS_KEY,\n\t\thostname: \"hub.browserstack.com\",\n\t\tservices: [\n\t\t\t[\n\t\t\t\t\"browserstack\",\n\t\t\t\t{\n\t\t\t\t\tbrowserstackLocal: true,\n\t\t\t\t},\n\t\t\t],\n\t\t],\n\t\tcapabilities: [\n\t\t\t{\n\t\t\t\tbrowserName: \"Edge\",\n\t\t\t\t\"bstack:options\": {\n\t\t\t\t\tos: \"Windows\",\n\t\t\t\t\tosVersion: \"11\",\n\t\t\t\t\tbrowserVersion: \"83\",\n\t\t\t\t\tlocalIdentifier: process.env.BROWSERSTACK_LOCAL_IDENTIFIER,\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tbrowserName: \"Chrome\",\n\t\t\t\t\"bstack:options\": {\n\t\t\t\t\tos: \"Windows\",\n\t\t\t\t\tosVersion: \"11\",\n\t\t\t\t\tbrowserVersion: \"83\",\n\t\t\t\t\tlocalIdentifier: process.env.BROWSERSTACK_LOCAL_IDENTIFIER,\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tbrowserName: \"Chrome\",\n\t\t\t\t\"bstack:options\": {\n\t\t\t\t\tbrowserVersion: \"latest\",\n\t\t\t\t\tos: \"Windows\",\n\t\t\t\t\tosVersion: \"11\",\n\t\t\t\t\tlocalIdentifier: process.env.BROWSERSTACK_LOCAL_IDENTIFIER,\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tbrowserName: \"Firefox\",\n\t\t\t\t\"bstack:options\": {\n\t\t\t\t\tos: \"Windows\",\n\t\t\t\t\tosVersion: \"7\",\n\t\t\t\t\tbrowserVersion: \"80.0\",\n\t\t\t\t\tlocalIdentifier: process.env.BROWSERSTACK_LOCAL_IDENTIFIER,\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tbrowserName: \"Firefox\",\n\t\t\t\t\"bstack:options\": {\n\t\t\t\t\tbrowserVersion: \"105\",\n\t\t\t\t\tos: \"OS X\",\n\t\t\t\t\tosVersion: \"Ventura\",\n\t\t\t\t\tlocalIdentifier: process.env.BROWSERSTACK_LOCAL_IDENTIFIER,\n\t\t\t\t},\n\t\t\t},\n\t\t\t// {\n\t\t\t//     browserName: \"Safari\",\n\t\t\t//     \"bstack:options\": {\n\t\t\t//         browserVersion: \"latest\",\n\t\t\t//         os: \"OS X\",\n\t\t\t//         osVersion: \"Monterey\",\n\t\t\t//         localIdentifier: process.env.BROWSERSTACK_LOCAL_IDENTIFIER,\n\t\t\t//     },\n\t\t\t// },\n\t\t\t// {\n\t\t\t//     browserName: 'Safari',\n\t\t\t//     'bstack:options': {\n\t\t\t//         browserVersion: 'latest',\n\t\t\t//         os: 'OS X',\n\t\t\t//         osVersion: 'Ventura',\n\t\t\t//         localIdentifier: process.env.BROWSERSTACK_LOCAL_IDENTIFIER,\n\t\t\t//     }\n\t\t\t// },\n\t\t],\n\t},\n};\n"
  },
  {
    "path": "e2e/wdio.local.conf.ts",
    "content": "import { config as sharedConfig } from \"./wdio.shared.conf.js\";\n\nexport const config: WebdriverIO.Config = {\n\trunner: \"local\",\n\t...sharedConfig,\n\tservices: [\"geckodriver\"],\n\t...{\n\t\tcapabilities: [\n\t\t\t{\n\t\t\t\tbrowserName: \"chrome\",\n\t\t\t\t\"goog:chromeOptions\": {\n\t\t\t\t\targs: [\"headless\", \"disable-gpu\"],\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tbrowserName: \"firefox\",\n\t\t\t\t\"moz:firefoxOptions\": {\n\t\t\t\t\targs: [\"-headless\"],\n\t\t\t\t},\n\t\t\t},\n\t\t],\n\t},\n};\n"
  },
  {
    "path": "e2e/wdio.shared.conf.ts",
    "content": "import url from \"node:url\";\nimport path from \"node:path\";\n\nconst __dirname = url.fileURLToPath(new URL(\".\", import.meta.url));\n\nexport const config: Omit<WebdriverIO.Config, \"capabilities\"> = {\n\tinjectGlobals: false,\n\t//\n\t// ====================\n\t// Runner Configuration\n\t// ====================\n\t//\n\t// WebdriverIO allows it to run your tests in arbitrary locations (e.g. locally or\n\t// on a remote machine).\n\t// runner: 'local',\n\t//\n\t// ==================\n\t// Specify Test Files\n\t// ==================\n\t// Define which test specs should run. The pattern is relative to the directory\n\t// from which `wdio` was called. Notice that, if you are calling `wdio` from an\n\t// NPM script (see https://docs.npmjs.com/cli/run-script) then the current working\n\t// directory is where your package.json resides, so `wdio` will be called from there.\n\t//\n\tspecs: [\"./**/*.spec.ts\"],\n\t// Patterns to exclude.\n\texclude: [\n\t\t// 'path/to/excluded/files'\n\t],\n\t//\n\t// ============\n\t// Capabilities\n\t// ============\n\t// Define your capabilities here. WebdriverIO can run multiple capabilities at the same\n\t// time. Depending on the number of capabilities, WebdriverIO launches several test\n\t// sessions. Within your capabilities you can overwrite the spec and exclude options in\n\t// order to group specific specs to a specific capability.\n\t//\n\t// First, you can define how many instances should be started at the same time. Let's\n\t// say you have 3 different capabilities (Chrome, Firefox, and Safari) and you have\n\t// set maxInstances to 1; wdio will spawn 3 processes. Therefore, if you have 10 spec\n\t// files and you set maxInstances to 10, all spec files will get tested at the same time\n\t// and 30 processes will get spawned. The property handles how many capabilities\n\t// from the same test should run tests.\n\t//\n\tmaxInstances: 5,\n\t//\n\t// ===================\n\t// Test Configurations\n\t// ===================\n\t// Define all options that are relevant for the WebdriverIO instance here\n\t//\n\t// Level of logging verbosity: trace | debug | info | warn | error | silent\n\tlogLevel: \"trace\",\n\toutputDir: path.resolve(__dirname, \"logs\"),\n\t//\n\t// Set specific log levels per logger\n\t// loggers:\n\t// - webdriver, webdriverio\n\t// - @wdio/applitools-service, @wdio/browserstack-service, @wdio/devtools-service, @wdio/sauce-service\n\t// - @wdio/mocha-framework, @wdio/jasmine-framework\n\t// - @wdio/local-runner, @wdio/lambda-runner\n\t// - @wdio/sumologic-reporter\n\t// - @wdio/cli, @wdio/config, @wdio/sync, @wdio/utils\n\t// Level of logging verbosity: trace | debug | info | warn | error | silent\n\t// logLevels: {\n\t//     webdriver: 'info',\n\t//     '@wdio/applitools-service': 'info'\n\t// },\n\t//\n\t// If you only want to run your tests until a specific amount of tests have failed use\n\t// bail (default is 0 - don't bail, run all tests).\n\tbail: 0,\n\t//\n\t// Set a base URL in order to shorten url command calls. If your `url` parameter starts\n\t// with `/`, the base url gets prepended, not including the path portion of your baseUrl.\n\t// If your `url` parameter starts without a scheme or `/` (like `some/path`), the base url\n\t// gets prepended directly.\n\tbaseUrl: \"http://localhost:3000\",\n\t//\n\t// Default timeout for all waitFor* commands.\n\twaitforTimeout: 10000,\n\t//\n\t// Default timeout in milliseconds for request\n\t// if browser driver or grid doesn't send response\n\tconnectionRetryTimeout: 90000,\n\t//\n\t// Default request retries count\n\tconnectionRetryCount: 3,\n\t//\n\t// Framework you want to run your specs with.\n\t// The following are supported: Mocha, Jasmine, and Cucumber\n\t// see also: https://webdriver.io/docs/frameworks.html\n\t//\n\t// Make sure you have the wdio adapter package for the specific framework installed\n\t// before running any tests.\n\tframework: \"jasmine\",\n\t//\n\t// The number of times to retry the entire specfile when it fails as a whole\n\tspecFileRetries: 1,\n\t//\n\t// Whether or not retried specfiles should be retried immediately or deferred to the end of the queue\n\tspecFileRetriesDeferred: true,\n\t//\n\t// Test reporter for stdout.\n\t// The only one supported by default is 'dot'\n\t// see also: https://webdriver.io/docs/dot-reporter.html\n\treporters: [\"spec\"],\n\t//\n\t// Options to be passed to Jasmine.\n\tjasmineOpts: {\n\t\t// Jasmine default timeout\n\t\tdefaultTimeoutInterval: 60000,\n\t\t//\n\t\t// The Jasmine framework allows interception of each assertion in order to log the state of the application\n\t\t// or website depending on the result. For example, it is pretty handy to take a screenshot every time\n\t\t// an assertion fails.\n\t\t// expectationResultHandler: function(passed, assertion) {\n\t\t// do something\n\t\t// }\n\t},\n\t//\n\t// =====\n\t// Hooks\n\t// =====\n\t// WebdriverIO provides several hooks you can use to interfere with the test process in order to enhance\n\t// it and to build services around it. You can either apply a single function or an array of\n\t// methods to it. If one of them returns with a promise, WebdriverIO will wait until that promise got\n\t// resolved to continue.\n\t/**\n\t * Gets executed once before all workers get launched.\n\t * @param {Object} config wdio configuration object\n\t * @param {Array.<Object>} capabilities list of capabilities details\n\t */\n\t// onPrepare: function (config, capabilities) {\n\t// },\n\t/**\n\t * Gets executed before a worker process is spawned and can be used to initialise specific service\n\t * for that worker as well as modify runtime environments in an async fashion.\n\t * @param  {String} cid      capability id (e.g 0-0)\n\t * @param  {[type]} caps     object containing capabilities for session that will be spawn in the worker\n\t * @param  {[type]} specs    specs to be run in the worker process\n\t * @param  {[type]} args     object that will be merged with the main configuration once worker is initialised\n\t * @param  {[type]} execArgv list of string arguments passed to the worker process\n\t */\n\t// onWorkerStart: function (cid, caps, specs, args, execArgv) {\n\t// },\n\t/**\n\t * Gets executed just before initialising the webdriver session and test framework. It allows you\n\t * to manipulate configurations depending on the capability or spec.\n\t * @param {Object} config wdio configuration object\n\t * @param {Array.<Object>} capabilities list of capabilities details\n\t * @param {Array.<String>} specs List of spec file paths that are to be run\n\t */\n\t// beforeSession: function (config, capabilities, specs) {\n\t// },\n\t/**\n\t * Gets executed before test execution begins. At this point you can access to all global\n\t * variables like `browser`. It is the perfect place to define custom commands.\n\t * @param {Array.<Object>} capabilities list of capabilities details\n\t * @param {Array.<String>} specs List of spec file paths that are to be run\n\t */\n\t// before: function (capabilities, specs) {\n\t// },\n\t/**\n\t * Runs before a WebdriverIO command gets executed.\n\t * @param {String} commandName hook command name\n\t * @param {Array} args arguments that command would receive\n\t */\n\t// beforeCommand: function (commandName, args) {\n\t// },\n\t/**\n\t * Hook that gets executed before the suite starts\n\t * @param {Object} suite suite details\n\t */\n\t// beforeSuite: function (suite) {\n\t// },\n\t/**\n\t * Function to be executed before a test (in Mocha/Jasmine) starts.\n\t */\n\t// beforeTest: function (test, context) {\n\t// },\n\t/**\n\t * Hook that gets executed _before_ a hook within the suite starts (e.g. runs before calling\n\t * beforeEach in Mocha)\n\t */\n\t// beforeHook: function (test, context) {\n\t// },\n\t/**\n\t * Hook that gets executed _after_ a hook within the suite starts (e.g. runs after calling\n\t * afterEach in Mocha)\n\t */\n\t// afterHook: function (test, context, { error, result, duration, passed, retries }) {\n\t// },\n\t/**\n\t * Function to be executed after a test (in Mocha/Jasmine).\n\t */\n\t// afterTest: function(test, context, { error, result, duration, passed, retries }) {\n\t// },\n\n\t/**\n\t * Hook that gets executed after the suite has ended\n\t * @param {Object} suite suite details\n\t */\n\t// afterSuite: function (suite) {\n\t// },\n\t/**\n\t * Runs after a WebdriverIO command gets executed\n\t * @param {String} commandName hook command name\n\t * @param {Array} args arguments that command would receive\n\t * @param {Number} result 0 - command success, 1 - command error\n\t * @param {Object} error error object if any\n\t */\n\t// afterCommand: function (commandName, args, result, error) {\n\t// },\n\t/**\n\t * Gets executed after all tests are done. You still have access to all global variables from\n\t * the test.\n\t * @param {Number} result 0 - test pass, 1 - test fail\n\t * @param {Array.<Object>} capabilities list of capabilities details\n\t * @param {Array.<String>} specs List of spec file paths that ran\n\t */\n\t// after: function (result, capabilities, specs) {\n\t// },\n\t/**\n\t * Gets executed right after terminating the webdriver session.\n\t * @param {Object} config wdio configuration object\n\t * @param {Array.<Object>} capabilities list of capabilities details\n\t * @param {Array.<String>} specs List of spec file paths that ran\n\t */\n\t// afterSession: function (config, capabilities, specs) {\n\t// },\n\t/**\n\t * Gets executed after all workers got shut down and the process is about to exit. An error\n\t * thrown in the onComplete hook will result in the test run failing.\n\t * @param {Object} exitCode 0 - success, 1 - fail\n\t * @param {Object} config wdio configuration object\n\t * @param {Array.<Object>} capabilities list of capabilities details\n\t * @param {<Object>} results object containing test results\n\t */\n\t// onComplete: function(exitCode, config, capabilities, results) {\n\t// },\n\t/**\n\t * Gets executed when a refresh happens.\n\t * @param {String} oldSessionId session ID of the old session\n\t * @param {String} newSessionId session ID of the new session\n\t */\n\t//onReload: function(oldSessionId, newSessionId) {\n\t//}\n};\n"
  },
  {
    "path": "jest.config.cjs",
    "content": "/** @type {import('ts-jest').JestConfigWithTsJest} */\nmodule.exports = {\n\ttestEnvironment: \"jsdom\",\n\ttransform: {\n\t\t\"^.+\\\\.(t|j)sx?$\": [\"@swc/jest\"],\n\t},\n\tmodulePathIgnorePatterns: [\"e2e\"],\n};\n"
  },
  {
    "path": "lib/api.ts",
    "content": "import { util } from \"./util\";\nimport logger from \"./logger\";\nimport type { PeerJSOption } from \"./optionInterfaces\";\nimport { version } from \"./version\";\n\nexport class API {\n\tconstructor(private readonly _options: PeerJSOption) {}\n\n\tprivate _buildRequest(method: string): Promise<Response> {\n\t\tconst protocol = this._options.secure ? \"https\" : \"http\";\n\t\tconst { host, port, path, key } = this._options;\n\t\tconst url = new URL(`${protocol}://${host}:${port}${path}${key}/${method}`);\n\t\t// TODO: Why timestamp, why random?\n\t\turl.searchParams.set(\"ts\", `${Date.now()}${Math.random()}`);\n\t\turl.searchParams.set(\"version\", version);\n\t\treturn fetch(url.href, {\n\t\t\treferrerPolicy: this._options.referrerPolicy,\n\t\t});\n\t}\n\n\t/** Get a unique ID from the server via XHR and initialize with it. */\n\tasync retrieveId(): Promise<string> {\n\t\ttry {\n\t\t\tconst response = await this._buildRequest(\"id\");\n\n\t\t\tif (response.status !== 200) {\n\t\t\t\tthrow new Error(`Error. Status:${response.status}`);\n\t\t\t}\n\n\t\t\treturn response.text();\n\t\t} catch (error) {\n\t\t\tlogger.error(\"Error retrieving ID\", error);\n\n\t\t\tlet pathError = \"\";\n\n\t\t\tif (\n\t\t\t\tthis._options.path === \"/\" &&\n\t\t\t\tthis._options.host !== util.CLOUD_HOST\n\t\t\t) {\n\t\t\t\tpathError =\n\t\t\t\t\t\" If you passed in a `path` to your self-hosted PeerServer, \" +\n\t\t\t\t\t\"you'll also need to pass in that same path when creating a new \" +\n\t\t\t\t\t\"Peer.\";\n\t\t\t}\n\n\t\t\tthrow new Error(\"Could not get an ID from the server.\" + pathError);\n\t\t}\n\t}\n\n\t/** @deprecated */\n\tasync listAllPeers(): Promise<any[]> {\n\t\ttry {\n\t\t\tconst response = await this._buildRequest(\"peers\");\n\n\t\t\tif (response.status !== 200) {\n\t\t\t\tif (response.status === 401) {\n\t\t\t\t\tlet helpfulError = \"\";\n\n\t\t\t\t\tif (this._options.host === util.CLOUD_HOST) {\n\t\t\t\t\t\thelpfulError =\n\t\t\t\t\t\t\t\"It looks like you're using the cloud server. You can email \" +\n\t\t\t\t\t\t\t\"team@peerjs.com to enable peer listing for your API key.\";\n\t\t\t\t\t} else {\n\t\t\t\t\t\thelpfulError =\n\t\t\t\t\t\t\t\"You need to enable `allow_discovery` on your self-hosted \" +\n\t\t\t\t\t\t\t\"PeerServer to use this feature.\";\n\t\t\t\t\t}\n\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t\"It doesn't look like you have permission to list peers IDs. \" +\n\t\t\t\t\t\t\thelpfulError,\n\t\t\t\t\t);\n\t\t\t\t}\n\n\t\t\t\tthrow new Error(`Error. Status:${response.status}`);\n\t\t\t}\n\n\t\t\treturn response.json();\n\t\t} catch (error) {\n\t\t\tlogger.error(\"Error retrieving list peers\", error);\n\n\t\t\tthrow new Error(\"Could not get list peers from the server.\" + error);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "lib/baseconnection.ts",
    "content": "import type { Peer } from \"./peer\";\nimport type { ServerMessage } from \"./servermessage\";\nimport type { ConnectionType } from \"./enums\";\nimport { BaseConnectionErrorType } from \"./enums\";\nimport {\n\tEventEmitterWithError,\n\ttype EventsWithError,\n\tPeerError,\n} from \"./peerError\";\nimport type { ValidEventTypes } from \"eventemitter3\";\n\nexport interface BaseConnectionEvents<\n\tErrorType extends string = BaseConnectionErrorType,\n> extends EventsWithError<ErrorType> {\n\t/**\n\t * Emitted when either you or the remote peer closes the connection.\n\t *\n\t * ```ts\n\t * connection.on('close', () => { ... });\n\t * ```\n\t */\n\tclose: () => void;\n\t/**\n\t * ```ts\n\t * connection.on('error', (error) => { ... });\n\t * ```\n\t */\n\terror: (error: PeerError<`${ErrorType}`>) => void;\n\ticeStateChanged: (state: RTCIceConnectionState) => void;\n}\n\nexport abstract class BaseConnection<\n\tSubClassEvents extends ValidEventTypes,\n\tErrorType extends string = never,\n> extends EventEmitterWithError<\n\tErrorType | BaseConnectionErrorType,\n\tSubClassEvents & BaseConnectionEvents<BaseConnectionErrorType | ErrorType>\n> {\n\tprotected _open = false;\n\n\t/**\n\t * Any type of metadata associated with the connection,\n\t * passed in by whoever initiated the connection.\n\t */\n\treadonly metadata: any;\n\tconnectionId: string;\n\n\tpeerConnection: RTCPeerConnection;\n\tdataChannel: RTCDataChannel;\n\n\tabstract get type(): ConnectionType;\n\n\t/**\n\t * The optional label passed in or assigned by PeerJS when the connection was initiated.\n\t */\n\tlabel: string;\n\n\t/**\n\t * Whether the media connection is active (e.g. your call has been answered).\n\t * You can check this if you want to set a maximum wait time for a one-sided call.\n\t */\n\tget open() {\n\t\treturn this._open;\n\t}\n\n\tprotected constructor(\n\t\t/**\n\t\t * The ID of the peer on the other end of this connection.\n\t\t */\n\t\treadonly peer: string,\n\t\tpublic provider: Peer,\n\t\treadonly options: any,\n\t) {\n\t\tsuper();\n\n\t\tthis.metadata = options.metadata;\n\t}\n\n\tabstract close(): void;\n\n\t/**\n\t * @internal\n\t */\n\tabstract handleMessage(message: ServerMessage): void;\n\n\t/**\n\t * Called by the Negotiator when the DataChannel is ready.\n\t * @internal\n\t * */\n\tabstract _initializeDataChannel(dc: RTCDataChannel): void;\n}\n"
  },
  {
    "path": "lib/dataconnection/BufferedConnection/BinaryPack.ts",
    "content": "import { BinaryPackChunker, concatArrayBuffers } from \"./binaryPackChunker\";\nimport logger from \"../../logger\";\nimport type { Peer } from \"../../peer\";\nimport { BufferedConnection } from \"./BufferedConnection\";\nimport { SerializationType } from \"../../enums\";\nimport { pack, type Packable, unpack } from \"peerjs-js-binarypack\";\n\nexport class BinaryPack extends BufferedConnection {\n\tprivate readonly chunker = new BinaryPackChunker();\n\treadonly serialization = SerializationType.Binary;\n\n\tprivate _chunkedData: {\n\t\t[id: number]: {\n\t\t\tdata: Uint8Array[];\n\t\t\tcount: number;\n\t\t\ttotal: number;\n\t\t};\n\t} = {};\n\n\tpublic override close(options?: { flush?: boolean }) {\n\t\tsuper.close(options);\n\t\tthis._chunkedData = {};\n\t}\n\n\tconstructor(peerId: string, provider: Peer, options: any) {\n\t\tsuper(peerId, provider, options);\n\t}\n\n\t// Handles a DataChannel message.\n\tprotected override _handleDataMessage({ data }: { data: Uint8Array }): void {\n\t\tconst deserializedData = unpack(data);\n\n\t\t// PeerJS specific message\n\t\tconst peerData = deserializedData[\"__peerData\"];\n\t\tif (peerData) {\n\t\t\tif (peerData.type === \"close\") {\n\t\t\t\tthis.close();\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Chunked data -- piece things back together.\n\t\t\t// @ts-ignore\n\t\t\tthis._handleChunk(deserializedData);\n\t\t\treturn;\n\t\t}\n\n\t\tthis.emit(\"data\", deserializedData);\n\t}\n\n\tprivate _handleChunk(data: {\n\t\t__peerData: number;\n\t\tn: number;\n\t\ttotal: number;\n\t\tdata: ArrayBuffer;\n\t}): void {\n\t\tconst id = data.__peerData;\n\t\tconst chunkInfo = this._chunkedData[id] || {\n\t\t\tdata: [],\n\t\t\tcount: 0,\n\t\t\ttotal: data.total,\n\t\t};\n\n\t\tchunkInfo.data[data.n] = new Uint8Array(data.data);\n\t\tchunkInfo.count++;\n\t\tthis._chunkedData[id] = chunkInfo;\n\n\t\tif (chunkInfo.total === chunkInfo.count) {\n\t\t\t// Clean up before making the recursive call to `_handleDataMessage`.\n\t\t\tdelete this._chunkedData[id];\n\n\t\t\t// We've received all the chunks--time to construct the complete data.\n\t\t\t// const data = new Blob(chunkInfo.data);\n\t\t\tconst data = concatArrayBuffers(chunkInfo.data);\n\t\t\tthis._handleDataMessage({ data });\n\t\t}\n\t}\n\n\tprotected override _send(data: Packable, chunked: boolean) {\n\t\tconst blob = pack(data);\n\t\tif (blob instanceof Promise) {\n\t\t\treturn this._send_blob(blob);\n\t\t}\n\n\t\tif (!chunked && blob.byteLength > this.chunker.chunkedMTU) {\n\t\t\tthis._sendChunks(blob);\n\t\t\treturn;\n\t\t}\n\n\t\tthis._bufferedSend(blob);\n\t}\n\tprivate async _send_blob(blobPromise: Promise<ArrayBufferLike>) {\n\t\tconst blob = await blobPromise;\n\t\tif (blob.byteLength > this.chunker.chunkedMTU) {\n\t\t\tthis._sendChunks(blob);\n\t\t\treturn;\n\t\t}\n\n\t\tthis._bufferedSend(blob);\n\t}\n\n\tprivate _sendChunks(blob: ArrayBuffer) {\n\t\tconst blobs = this.chunker.chunk(blob);\n\t\tlogger.log(`DC#${this.connectionId} Try to send ${blobs.length} chunks...`);\n\n\t\tfor (const blob of blobs) {\n\t\t\tthis.send(blob, true);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "lib/dataconnection/BufferedConnection/BufferedConnection.ts",
    "content": "import logger from \"../../logger\";\nimport { DataConnection } from \"../DataConnection\";\n\nexport abstract class BufferedConnection extends DataConnection {\n\tprivate _buffer: any[] = [];\n\tprivate _bufferSize = 0;\n\tprivate _buffering = false;\n\n\tpublic get bufferSize(): number {\n\t\treturn this._bufferSize;\n\t}\n\n\tpublic override _initializeDataChannel(dc: RTCDataChannel) {\n\t\tsuper._initializeDataChannel(dc);\n\t\tthis.dataChannel.binaryType = \"arraybuffer\";\n\t\tthis.dataChannel.addEventListener(\"message\", (e) =>\n\t\t\tthis._handleDataMessage(e),\n\t\t);\n\t}\n\n\tprotected abstract _handleDataMessage(e: MessageEvent): void;\n\n\tprotected _bufferedSend(msg: ArrayBuffer): void {\n\t\tif (this._buffering || !this._trySend(msg)) {\n\t\t\tthis._buffer.push(msg);\n\t\t\tthis._bufferSize = this._buffer.length;\n\t\t}\n\t}\n\n\t// Returns true if the send succeeds.\n\tprivate _trySend(msg: ArrayBuffer): boolean {\n\t\tif (!this.open) {\n\t\t\treturn false;\n\t\t}\n\n\t\tif (this.dataChannel.bufferedAmount > DataConnection.MAX_BUFFERED_AMOUNT) {\n\t\t\tthis._buffering = true;\n\t\t\tsetTimeout(() => {\n\t\t\t\tthis._buffering = false;\n\t\t\t\tthis._tryBuffer();\n\t\t\t}, 50);\n\n\t\t\treturn false;\n\t\t}\n\n\t\ttry {\n\t\t\tthis.dataChannel.send(msg);\n\t\t} catch (e) {\n\t\t\tlogger.error(`DC#:${this.connectionId} Error when sending:`, e);\n\t\t\tthis._buffering = true;\n\n\t\t\tthis.close();\n\n\t\t\treturn false;\n\t\t}\n\n\t\treturn true;\n\t}\n\n\t// Try to send the first message in the buffer.\n\tprivate _tryBuffer(): void {\n\t\tif (!this.open) {\n\t\t\treturn;\n\t\t}\n\n\t\tif (this._buffer.length === 0) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst msg = this._buffer[0];\n\n\t\tif (this._trySend(msg)) {\n\t\t\tthis._buffer.shift();\n\t\t\tthis._bufferSize = this._buffer.length;\n\t\t\tthis._tryBuffer();\n\t\t}\n\t}\n\n\tpublic override close(options?: { flush?: boolean }) {\n\t\tif (options?.flush) {\n\t\t\tthis.send({\n\t\t\t\t__peerData: {\n\t\t\t\t\ttype: \"close\",\n\t\t\t\t},\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\t\tthis._buffer = [];\n\t\tthis._bufferSize = 0;\n\t\tsuper.close();\n\t}\n}\n"
  },
  {
    "path": "lib/dataconnection/BufferedConnection/Json.ts",
    "content": "import { BufferedConnection } from \"./BufferedConnection\";\nimport { DataConnectionErrorType, SerializationType } from \"../../enums\";\nimport { util } from \"../../util\";\n\nexport class Json extends BufferedConnection {\n\treadonly serialization = SerializationType.JSON;\n\tprivate readonly encoder = new TextEncoder();\n\tprivate readonly decoder = new TextDecoder();\n\n\tstringify: (data: any) => string = JSON.stringify;\n\tparse: (data: string) => any = JSON.parse;\n\n\t// Handles a DataChannel message.\n\tprotected override _handleDataMessage({ data }: { data: Uint8Array }): void {\n\t\tconst deserializedData = this.parse(this.decoder.decode(data));\n\n\t\t// PeerJS specific message\n\t\tconst peerData = deserializedData[\"__peerData\"];\n\t\tif (peerData && peerData.type === \"close\") {\n\t\t\tthis.close();\n\t\t\treturn;\n\t\t}\n\n\t\tthis.emit(\"data\", deserializedData);\n\t}\n\n\toverride _send(data, _chunked) {\n\t\tconst encodedData = this.encoder.encode(this.stringify(data));\n\t\tif (encodedData.byteLength >= util.chunkedMTU) {\n\t\t\tthis.emitError(\n\t\t\t\tDataConnectionErrorType.MessageToBig,\n\t\t\t\t\"Message too big for JSON channel\",\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\t\tthis._bufferedSend(encodedData);\n\t}\n}\n"
  },
  {
    "path": "lib/dataconnection/BufferedConnection/Raw.ts",
    "content": "import { BufferedConnection } from \"./BufferedConnection\";\nimport { SerializationType } from \"../../enums\";\n\nexport class Raw extends BufferedConnection {\n\treadonly serialization = SerializationType.None;\n\n\tprotected _handleDataMessage({ data }) {\n\t\tsuper.emit(\"data\", data);\n\t}\n\n\toverride _send(data, _chunked) {\n\t\tthis._bufferedSend(data);\n\t}\n}\n"
  },
  {
    "path": "lib/dataconnection/BufferedConnection/binaryPackChunker.ts",
    "content": "export class BinaryPackChunker {\n\treadonly chunkedMTU = 16300; // The original 60000 bytes setting does not work when sending data from Firefox to Chrome, which is \"cut off\" after 16384 bytes and delivered individually.\n\n\t// Binary stuff\n\n\tprivate _dataCount: number = 1;\n\n\tchunk = (\n\t\tblob: ArrayBuffer,\n\t): { __peerData: number; n: number; total: number; data: Uint8Array }[] => {\n\t\tconst chunks = [];\n\t\tconst size = blob.byteLength;\n\t\tconst total = Math.ceil(size / this.chunkedMTU);\n\n\t\tlet index = 0;\n\t\tlet start = 0;\n\n\t\twhile (start < size) {\n\t\t\tconst end = Math.min(size, start + this.chunkedMTU);\n\t\t\tconst b = blob.slice(start, end);\n\n\t\t\tconst chunk = {\n\t\t\t\t__peerData: this._dataCount,\n\t\t\t\tn: index,\n\t\t\t\tdata: b,\n\t\t\t\ttotal,\n\t\t\t};\n\n\t\t\tchunks.push(chunk);\n\n\t\t\tstart = end;\n\t\t\tindex++;\n\t\t}\n\n\t\tthis._dataCount++;\n\n\t\treturn chunks;\n\t};\n}\n\nexport function concatArrayBuffers(bufs: Uint8Array[]) {\n\tlet size = 0;\n\tfor (const buf of bufs) {\n\t\tsize += buf.byteLength;\n\t}\n\tconst result = new Uint8Array(size);\n\tlet offset = 0;\n\tfor (const buf of bufs) {\n\t\tresult.set(buf, offset);\n\t\toffset += buf.byteLength;\n\t}\n\treturn result;\n}\n"
  },
  {
    "path": "lib/dataconnection/DataConnection.ts",
    "content": "import logger from \"../logger\";\nimport { Negotiator } from \"../negotiator\";\nimport {\n\tBaseConnectionErrorType,\n\tConnectionType,\n\tDataConnectionErrorType,\n\tServerMessageType,\n} from \"../enums\";\nimport type { Peer } from \"../peer\";\nimport { BaseConnection, type BaseConnectionEvents } from \"../baseconnection\";\nimport type { ServerMessage } from \"../servermessage\";\nimport type { EventsWithError } from \"../peerError\";\nimport { randomToken } from \"../utils/randomToken\";\n\nexport interface DataConnectionEvents\n\textends EventsWithError<DataConnectionErrorType | BaseConnectionErrorType>,\n\t\tBaseConnectionEvents<DataConnectionErrorType | BaseConnectionErrorType> {\n\t/**\n\t * Emitted when data is received from the remote peer.\n\t */\n\tdata: (data: unknown) => void;\n\t/**\n\t * Emitted when the connection is established and ready-to-use.\n\t */\n\topen: () => void;\n}\n\n/**\n * Wraps a DataChannel between two Peers.\n */\nexport abstract class DataConnection extends BaseConnection<\n\tDataConnectionEvents,\n\tDataConnectionErrorType\n> {\n\tprotected static readonly ID_PREFIX = \"dc_\";\n\tprotected static readonly MAX_BUFFERED_AMOUNT = 8 * 1024 * 1024;\n\n\tprivate _negotiator: Negotiator<DataConnectionEvents, this>;\n\tabstract readonly serialization: string;\n\treadonly reliable: boolean;\n\n\tpublic get type() {\n\t\treturn ConnectionType.Data;\n\t}\n\n\tconstructor(peerId: string, provider: Peer, options: any) {\n\t\tsuper(peerId, provider, options);\n\n\t\tthis.connectionId =\n\t\t\tthis.options.connectionId || DataConnection.ID_PREFIX + randomToken();\n\n\t\tthis.label = this.options.label || this.connectionId;\n\t\tthis.reliable = !!this.options.reliable;\n\n\t\tthis._negotiator = new Negotiator(this);\n\n\t\tthis._negotiator.startConnection(\n\t\t\tthis.options._payload || {\n\t\t\t\toriginator: true,\n\t\t\t\treliable: this.reliable,\n\t\t\t},\n\t\t);\n\t}\n\n\t/** Called by the Negotiator when the DataChannel is ready. */\n\toverride _initializeDataChannel(dc: RTCDataChannel): void {\n\t\tthis.dataChannel = dc;\n\n\t\tthis.dataChannel.onopen = () => {\n\t\t\tlogger.log(`DC#${this.connectionId} dc connection success`);\n\t\t\tthis._open = true;\n\t\t\tthis.emit(\"open\");\n\t\t};\n\n\t\tthis.dataChannel.onmessage = (e) => {\n\t\t\tlogger.log(`DC#${this.connectionId} dc onmessage:`, e.data);\n\t\t\t// this._handleDataMessage(e);\n\t\t};\n\n\t\tthis.dataChannel.onclose = () => {\n\t\t\tlogger.log(`DC#${this.connectionId} dc closed for:`, this.peer);\n\t\t\tthis.close();\n\t\t};\n\t}\n\n\t/**\n\t * Exposed functionality for users.\n\t */\n\n\t/** Allows user to close connection. */\n\tclose(options?: { flush?: boolean }): void {\n\t\tif (options?.flush) {\n\t\t\tthis.send({\n\t\t\t\t__peerData: {\n\t\t\t\t\ttype: \"close\",\n\t\t\t\t},\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\t\tif (this._negotiator) {\n\t\t\tthis._negotiator.cleanup();\n\t\t\tthis._negotiator = null;\n\t\t}\n\n\t\tif (this.provider) {\n\t\t\tthis.provider._removeConnection(this);\n\n\t\t\tthis.provider = null;\n\t\t}\n\n\t\tif (this.dataChannel) {\n\t\t\tthis.dataChannel.onopen = null;\n\t\t\tthis.dataChannel.onmessage = null;\n\t\t\tthis.dataChannel.onclose = null;\n\t\t\tthis.dataChannel = null;\n\t\t}\n\n\t\tif (!this.open) {\n\t\t\treturn;\n\t\t}\n\n\t\tthis._open = false;\n\n\t\tsuper.emit(\"close\");\n\t}\n\n\tprotected abstract _send(data: any, chunked: boolean): void | Promise<void>;\n\n\t/** Allows user to send data. */\n\tpublic send(data: any, chunked = false) {\n\t\tif (!this.open) {\n\t\t\tthis.emitError(\n\t\t\t\tDataConnectionErrorType.NotOpenYet,\n\t\t\t\t\"Connection is not open. You should listen for the `open` event before sending messages.\",\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\t\treturn this._send(data, chunked);\n\t}\n\n\tasync handleMessage(message: ServerMessage) {\n\t\tconst payload = message.payload;\n\n\t\tswitch (message.type) {\n\t\t\tcase ServerMessageType.Answer:\n\t\t\t\tawait this._negotiator.handleSDP(message.type, payload.sdp);\n\t\t\t\tbreak;\n\t\t\tcase ServerMessageType.Candidate:\n\t\t\t\tawait this._negotiator.handleCandidate(payload.candidate);\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tlogger.warn(\n\t\t\t\t\t\"Unrecognized message type:\",\n\t\t\t\t\tmessage.type,\n\t\t\t\t\t\"from peer:\",\n\t\t\t\t\tthis.peer,\n\t\t\t\t);\n\t\t\t\tbreak;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "lib/dataconnection/StreamConnection/MsgPack.ts",
    "content": "import { decodeMultiStream, Encoder } from \"@msgpack/msgpack\";\nimport { StreamConnection } from \"./StreamConnection.js\";\nimport type { Peer } from \"../../peer.js\";\n\nexport class MsgPack extends StreamConnection {\n\treadonly serialization = \"MsgPack\";\n\tprivate _encoder = new Encoder();\n\n\tconstructor(peerId: string, provider: Peer, options: any) {\n\t\tsuper(peerId, provider, options);\n\n\t\t(async () => {\n\t\t\tfor await (const msg of decodeMultiStream(this._rawReadStream)) {\n\t\t\t\t// @ts-ignore\n\t\t\t\tif (msg.__peerData?.type === \"close\") {\n\t\t\t\t\tthis.close();\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tthis.emit(\"data\", msg);\n\t\t\t}\n\t\t})();\n\t}\n\n\tprotected override _send(data) {\n\t\treturn this.writer.write(this._encoder.encode(data));\n\t}\n}\n"
  },
  {
    "path": "lib/dataconnection/StreamConnection/StreamConnection.ts",
    "content": "import logger from \"../../logger.js\";\nimport type { Peer } from \"../../peer.js\";\nimport { DataConnection } from \"../DataConnection.js\";\n\nexport abstract class StreamConnection extends DataConnection {\n\tprivate _CHUNK_SIZE = 1024 * 8 * 4;\n\tprivate _splitStream = new TransformStream<Uint8Array>({\n\t\ttransform: (chunk, controller) => {\n\t\t\tfor (let split = 0; split < chunk.length; split += this._CHUNK_SIZE) {\n\t\t\t\tcontroller.enqueue(chunk.subarray(split, split + this._CHUNK_SIZE));\n\t\t\t}\n\t\t},\n\t});\n\tprivate _rawSendStream = new WritableStream<ArrayBuffer>({\n\t\twrite: async (chunk, controller) => {\n\t\t\tconst openEvent = new Promise((resolve) =>\n\t\t\t\tthis.dataChannel.addEventListener(\"bufferedamountlow\", resolve, {\n\t\t\t\t\tonce: true,\n\t\t\t\t}),\n\t\t\t);\n\n\t\t\t// if we can send the chunk now, send it\n\t\t\t// if not, we wait until at least half of the sending buffer is free again\n\t\t\tawait (this.dataChannel.bufferedAmount <=\n\t\t\t\tDataConnection.MAX_BUFFERED_AMOUNT - chunk.byteLength || openEvent);\n\n\t\t\t// TODO: what can go wrong here?\n\t\t\ttry {\n\t\t\t\tthis.dataChannel.send(chunk);\n\t\t\t} catch (e) {\n\t\t\t\tlogger.error(`DC#:${this.connectionId} Error when sending:`, e);\n\t\t\t\tcontroller.error(e);\n\t\t\t\tthis.close();\n\t\t\t}\n\t\t},\n\t});\n\tprotected writer = this._splitStream.writable.getWriter();\n\n\tprotected _rawReadStream = new ReadableStream<ArrayBuffer>({\n\t\tstart: (controller) => {\n\t\t\tthis.once(\"open\", () => {\n\t\t\t\tthis.dataChannel.addEventListener(\"message\", (e) => {\n\t\t\t\t\tcontroller.enqueue(e.data);\n\t\t\t\t});\n\t\t\t});\n\t\t},\n\t});\n\n\tprotected constructor(peerId: string, provider: Peer, options: any) {\n\t\tsuper(peerId, provider, { ...options, reliable: true });\n\n\t\tvoid this._splitStream.readable.pipeTo(this._rawSendStream);\n\t}\n\n\tpublic override _initializeDataChannel(dc) {\n\t\tsuper._initializeDataChannel(dc);\n\t\tthis.dataChannel.binaryType = \"arraybuffer\";\n\t\tthis.dataChannel.bufferedAmountLowThreshold =\n\t\t\tDataConnection.MAX_BUFFERED_AMOUNT / 2;\n\t}\n}\n"
  },
  {
    "path": "lib/encodingQueue.ts",
    "content": "import { EventEmitter } from \"eventemitter3\";\nimport logger from \"./logger\";\n\nexport class EncodingQueue extends EventEmitter {\n\treadonly fileReader: FileReader = new FileReader();\n\n\tprivate _queue: Blob[] = [];\n\tprivate _processing: boolean = false;\n\n\tconstructor() {\n\t\tsuper();\n\n\t\tthis.fileReader.onload = (evt) => {\n\t\t\tthis._processing = false;\n\n\t\t\tif (evt.target) {\n\t\t\t\tthis.emit(\"done\", evt.target.result as ArrayBuffer);\n\t\t\t}\n\n\t\t\tthis.doNextTask();\n\t\t};\n\n\t\tthis.fileReader.onerror = (evt) => {\n\t\t\tlogger.error(`EncodingQueue error:`, evt);\n\t\t\tthis._processing = false;\n\t\t\tthis.destroy();\n\t\t\tthis.emit(\"error\", evt);\n\t\t};\n\t}\n\n\tget queue(): Blob[] {\n\t\treturn this._queue;\n\t}\n\n\tget size(): number {\n\t\treturn this.queue.length;\n\t}\n\n\tget processing(): boolean {\n\t\treturn this._processing;\n\t}\n\n\tenque(blob: Blob): void {\n\t\tthis.queue.push(blob);\n\n\t\tif (this.processing) return;\n\n\t\tthis.doNextTask();\n\t}\n\n\tdestroy(): void {\n\t\tthis.fileReader.abort();\n\t\tthis._queue = [];\n\t}\n\n\tprivate doNextTask(): void {\n\t\tif (this.size === 0) return;\n\t\tif (this.processing) return;\n\n\t\tthis._processing = true;\n\n\t\tthis.fileReader.readAsArrayBuffer(this.queue.shift());\n\t}\n}\n"
  },
  {
    "path": "lib/enums.ts",
    "content": "export enum ConnectionType {\n\tData = \"data\",\n\tMedia = \"media\",\n}\n\nexport enum PeerErrorType {\n\t/**\n\t * The client's browser does not support some or all WebRTC features that you are trying to use.\n\t */\n\tBrowserIncompatible = \"browser-incompatible\",\n\t/**\n\t * You've already disconnected this peer from the server and can no longer make any new connections on it.\n\t */\n\tDisconnected = \"disconnected\",\n\t/**\n\t * The ID passed into the Peer constructor contains illegal characters.\n\t */\n\tInvalidID = \"invalid-id\",\n\t/**\n\t * The API key passed into the Peer constructor contains illegal characters or is not in the system (cloud server only).\n\t */\n\tInvalidKey = \"invalid-key\",\n\t/**\n\t * Lost or cannot establish a connection to the signalling server.\n\t */\n\tNetwork = \"network\",\n\t/**\n\t * The peer you're trying to connect to does not exist.\n\t */\n\tPeerUnavailable = \"peer-unavailable\",\n\t/**\n\t * PeerJS is being used securely, but the cloud server does not support SSL. Use a custom PeerServer.\n\t */\n\tSslUnavailable = \"ssl-unavailable\",\n\t/**\n\t * Unable to reach the server.\n\t */\n\tServerError = \"server-error\",\n\t/**\n\t * An error from the underlying socket.\n\t */\n\tSocketError = \"socket-error\",\n\t/**\n\t * The underlying socket closed unexpectedly.\n\t */\n\tSocketClosed = \"socket-closed\",\n\t/**\n\t * The ID passed into the Peer constructor is already taken.\n\t *\n\t * :::caution\n\t * This error is not fatal if your peer has open peer-to-peer connections.\n\t * This can happen if you attempt to {@apilink Peer.reconnect} a peer that has been disconnected from the server,\n\t * but its old ID has now been taken.\n\t * :::\n\t */\n\tUnavailableID = \"unavailable-id\",\n\t/**\n\t * Native WebRTC errors.\n\t */\n\tWebRTC = \"webrtc\",\n}\n\nexport enum BaseConnectionErrorType {\n\tNegotiationFailed = \"negotiation-failed\",\n\tConnectionClosed = \"connection-closed\",\n}\n\nexport enum DataConnectionErrorType {\n\tNotOpenYet = \"not-open-yet\",\n\tMessageToBig = \"message-too-big\",\n}\n\nexport enum SerializationType {\n\tBinary = \"binary\",\n\tBinaryUTF8 = \"binary-utf8\",\n\tJSON = \"json\",\n\tNone = \"raw\",\n}\n\nexport enum SocketEventType {\n\tMessage = \"message\",\n\tDisconnected = \"disconnected\",\n\tError = \"error\",\n\tClose = \"close\",\n}\n\nexport enum ServerMessageType {\n\tHeartbeat = \"HEARTBEAT\",\n\tCandidate = \"CANDIDATE\",\n\tOffer = \"OFFER\",\n\tAnswer = \"ANSWER\",\n\tOpen = \"OPEN\", // The connection to the server is open.\n\tError = \"ERROR\", // Server error.\n\tIdTaken = \"ID-TAKEN\", // The selected ID is taken.\n\tInvalidKey = \"INVALID-KEY\", // The given API key cannot be found.\n\tLeave = \"LEAVE\", // Another peer has closed its connection to this peer.\n\tExpire = \"EXPIRE\", // The offer sent to a peer has expired without response.\n}\n"
  },
  {
    "path": "lib/exports.ts",
    "content": "export { util, type Util } from \"./util\";\nimport { Peer } from \"./peer\";\nimport { MsgPackPeer } from \"./msgPackPeer\";\n\nexport type { PeerEvents, PeerOptions } from \"./peer\";\n\nexport type {\n\tPeerJSOption,\n\tPeerConnectOption,\n\tAnswerOption,\n\tCallOption,\n} from \"./optionInterfaces\";\nexport type { UtilSupportsObj } from \"./util\";\nexport type { DataConnection } from \"./dataconnection/DataConnection\";\nexport type { MediaConnection } from \"./mediaconnection\";\nexport type { LogLevel } from \"./logger\";\nexport * from \"./enums\";\n\nexport { BufferedConnection } from \"./dataconnection/BufferedConnection/BufferedConnection\";\nexport { StreamConnection } from \"./dataconnection/StreamConnection/StreamConnection\";\nexport { MsgPack } from \"./dataconnection/StreamConnection/MsgPack\";\nexport type { SerializerMapping } from \"./peer\";\n\nexport { Peer, MsgPackPeer };\n\nexport { PeerError } from \"./peerError\";\nexport default Peer;\n"
  },
  {
    "path": "lib/global.ts",
    "content": "import { util } from \"./util\";\nimport { Peer } from \"./peer\";\n\n(<any>window).peerjs = {\n\tPeer,\n\tutil,\n};\n/** @deprecated Should use peerjs namespace */\n(<any>window).Peer = Peer;\n"
  },
  {
    "path": "lib/logger.ts",
    "content": "const LOG_PREFIX = \"PeerJS: \";\n\n/*\nPrints log messages depending on the debug level passed in. Defaults to 0.\n0  Prints no logs.\n1  Prints only errors.\n2  Prints errors and warnings.\n3  Prints all logs.\n*/\nexport enum LogLevel {\n\t/**\n\t * Prints no logs.\n\t */\n\tDisabled,\n\t/**\n\t * Prints only errors.\n\t */\n\tErrors,\n\t/**\n\t * Prints errors and warnings.\n\t */\n\tWarnings,\n\t/**\n\t * Prints all logs.\n\t */\n\tAll,\n}\n\nclass Logger {\n\tprivate _logLevel = LogLevel.Disabled;\n\n\tget logLevel(): LogLevel {\n\t\treturn this._logLevel;\n\t}\n\n\tset logLevel(logLevel: LogLevel) {\n\t\tthis._logLevel = logLevel;\n\t}\n\n\tlog(...args: any[]) {\n\t\tif (this._logLevel >= LogLevel.All) {\n\t\t\tthis._print(LogLevel.All, ...args);\n\t\t}\n\t}\n\n\twarn(...args: any[]) {\n\t\tif (this._logLevel >= LogLevel.Warnings) {\n\t\t\tthis._print(LogLevel.Warnings, ...args);\n\t\t}\n\t}\n\n\terror(...args: any[]) {\n\t\tif (this._logLevel >= LogLevel.Errors) {\n\t\t\tthis._print(LogLevel.Errors, ...args);\n\t\t}\n\t}\n\n\tsetLogFunction(fn: (logLevel: LogLevel, ..._: any[]) => void): void {\n\t\tthis._print = fn;\n\t}\n\n\tprivate _print(logLevel: LogLevel, ...rest: any[]): void {\n\t\tconst copy = [LOG_PREFIX, ...rest];\n\n\t\tfor (const i in copy) {\n\t\t\tif (copy[i] instanceof Error) {\n\t\t\t\tcopy[i] = \"(\" + copy[i].name + \") \" + copy[i].message;\n\t\t\t}\n\t\t}\n\n\t\tif (logLevel >= LogLevel.All) {\n\t\t\tconsole.log(...copy);\n\t\t} else if (logLevel >= LogLevel.Warnings) {\n\t\t\tconsole.warn(\"WARNING\", ...copy);\n\t\t} else if (logLevel >= LogLevel.Errors) {\n\t\t\tconsole.error(\"ERROR\", ...copy);\n\t\t}\n\t}\n}\n\nexport default new Logger();\n"
  },
  {
    "path": "lib/mediaconnection.ts",
    "content": "import { util } from \"./util\";\nimport logger from \"./logger\";\nimport { Negotiator } from \"./negotiator\";\nimport { ConnectionType, ServerMessageType } from \"./enums\";\nimport type { Peer } from \"./peer\";\nimport { BaseConnection, type BaseConnectionEvents } from \"./baseconnection\";\nimport type { ServerMessage } from \"./servermessage\";\nimport type { AnswerOption } from \"./optionInterfaces\";\n\nexport interface MediaConnectionEvents extends BaseConnectionEvents<never> {\n\t/**\n\t * Emitted when a connection to the PeerServer is established.\n\t *\n\t * ```ts\n\t * mediaConnection.on('stream', (stream) => { ... });\n\t * ```\n\t */\n\tstream: (stream: MediaStream) => void;\n\t/**\n\t * Emitted when the auxiliary data channel is established.\n\t * After this event, hanging up will close the connection cleanly on the remote peer.\n\t * @beta\n\t */\n\twillCloseOnRemote: () => void;\n}\n\n/**\n * Wraps WebRTC's media streams.\n * To get one, use {@apilink Peer.call} or listen for the {@apilink PeerEvents | `call`} event.\n */\nexport class MediaConnection extends BaseConnection<MediaConnectionEvents> {\n\tprivate static readonly ID_PREFIX = \"mc_\";\n\treadonly label: string;\n\n\tprivate _negotiator: Negotiator<MediaConnectionEvents, this>;\n\tprivate _localStream: MediaStream;\n\tprivate _remoteStream: MediaStream;\n\n\t/**\n\t * For media connections, this is always 'media'.\n\t */\n\tget type() {\n\t\treturn ConnectionType.Media;\n\t}\n\n\tget localStream(): MediaStream {\n\t\treturn this._localStream;\n\t}\n\n\tget remoteStream(): MediaStream {\n\t\treturn this._remoteStream;\n\t}\n\n\tconstructor(peerId: string, provider: Peer, options: any) {\n\t\tsuper(peerId, provider, options);\n\n\t\tthis._localStream = this.options._stream;\n\t\tthis.connectionId =\n\t\t\tthis.options.connectionId ||\n\t\t\tMediaConnection.ID_PREFIX + util.randomToken();\n\n\t\tthis._negotiator = new Negotiator(this);\n\n\t\tif (this._localStream) {\n\t\t\tthis._negotiator.startConnection({\n\t\t\t\t_stream: this._localStream,\n\t\t\t\toriginator: true,\n\t\t\t});\n\t\t}\n\t}\n\n\t/** Called by the Negotiator when the DataChannel is ready. */\n\toverride _initializeDataChannel(dc: RTCDataChannel): void {\n\t\tthis.dataChannel = dc;\n\n\t\tthis.dataChannel.onopen = () => {\n\t\t\tlogger.log(`DC#${this.connectionId} dc connection success`);\n\t\t\tthis.emit(\"willCloseOnRemote\");\n\t\t};\n\n\t\tthis.dataChannel.onclose = () => {\n\t\t\tlogger.log(`DC#${this.connectionId} dc closed for:`, this.peer);\n\t\t\tthis.close();\n\t\t};\n\t}\n\taddStream(remoteStream) {\n\t\tlogger.log(\"Receiving stream\", remoteStream);\n\n\t\tthis._remoteStream = remoteStream;\n\t\tsuper.emit(\"stream\", remoteStream); // Should we call this `open`?\n\t}\n\n\t/**\n\t * @internal\n\t */\n\thandleMessage(message: ServerMessage): void {\n\t\tconst type = message.type;\n\t\tconst payload = message.payload;\n\n\t\tswitch (message.type) {\n\t\t\tcase ServerMessageType.Answer:\n\t\t\t\t// Forward to negotiator\n\t\t\t\tvoid this._negotiator.handleSDP(type, payload.sdp);\n\t\t\t\tthis._open = true;\n\t\t\t\tbreak;\n\t\t\tcase ServerMessageType.Candidate:\n\t\t\t\tvoid this._negotiator.handleCandidate(payload.candidate);\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tlogger.warn(`Unrecognized message type:${type} from peer:${this.peer}`);\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\t/**\n     * When receiving a {@apilink PeerEvents | `call`} event on a peer, you can call\n     * `answer` on the media connection provided by the callback to accept the call\n     * and optionally send your own media stream.\n\n     *\n     * @param stream A WebRTC media stream.\n     * @param options\n     * @returns\n     */\n\tanswer(stream?: MediaStream, options: AnswerOption = {}): void {\n\t\tif (this._localStream) {\n\t\t\tlogger.warn(\n\t\t\t\t\"Local stream already exists on this MediaConnection. Are you answering a call twice?\",\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\n\t\tthis._localStream = stream;\n\n\t\tif (options && options.sdpTransform) {\n\t\t\tthis.options.sdpTransform = options.sdpTransform;\n\t\t}\n\n\t\tthis._negotiator.startConnection({\n\t\t\t...this.options._payload,\n\t\t\t_stream: stream,\n\t\t});\n\t\t// Retrieve lost messages stored because PeerConnection not set up.\n\t\tconst messages = this.provider._getMessages(this.connectionId);\n\n\t\tfor (const message of messages) {\n\t\t\tthis.handleMessage(message);\n\t\t}\n\n\t\tthis._open = true;\n\t}\n\n\t/**\n\t * Exposed functionality for users.\n\t */\n\n\t/**\n\t * Closes the media connection.\n\t */\n\tclose(): void {\n\t\tif (this._negotiator) {\n\t\t\tthis._negotiator.cleanup();\n\t\t\tthis._negotiator = null;\n\t\t}\n\n\t\tthis._localStream = null;\n\t\tthis._remoteStream = null;\n\n\t\tif (this.provider) {\n\t\t\tthis.provider._removeConnection(this);\n\n\t\t\tthis.provider = null;\n\t\t}\n\n\t\tif (this.options && this.options._stream) {\n\t\t\tthis.options._stream = null;\n\t\t}\n\n\t\tif (!this.open) {\n\t\t\treturn;\n\t\t}\n\n\t\tthis._open = false;\n\n\t\tsuper.emit(\"close\");\n\t}\n}\n"
  },
  {
    "path": "lib/msgPackPeer.ts",
    "content": "import { Peer, type SerializerMapping } from \"./peer\";\nimport { MsgPack } from \"./exports\";\n\n/**\n * @experimental\n */\nexport class MsgPackPeer extends Peer {\n\toverride _serializers: SerializerMapping = {\n\t\tMsgPack,\n\t\tdefault: MsgPack,\n\t};\n}\n"
  },
  {
    "path": "lib/negotiator.ts",
    "content": "import logger from \"./logger\";\nimport type { MediaConnection } from \"./mediaconnection\";\nimport type { DataConnection } from \"./dataconnection/DataConnection\";\nimport {\n\tBaseConnectionErrorType,\n\tConnectionType,\n\tPeerErrorType,\n\tServerMessageType,\n} from \"./enums\";\nimport type { BaseConnection, BaseConnectionEvents } from \"./baseconnection\";\nimport type { ValidEventTypes } from \"eventemitter3\";\n\n/**\n * Manages all negotiations between Peers.\n */\nexport class Negotiator<\n\tEvents extends ValidEventTypes,\n\tConnectionType extends BaseConnection<Events | BaseConnectionEvents>,\n> {\n\tconstructor(readonly connection: ConnectionType) {}\n\n\t/** Returns a PeerConnection object set up correctly (for data, media). */\n\tstartConnection(options: any) {\n\t\tconst peerConnection = this._startPeerConnection();\n\n\t\t// Set the connection's PC.\n\t\tthis.connection.peerConnection = peerConnection;\n\n\t\tif (this.connection.type === ConnectionType.Media && options._stream) {\n\t\t\tthis._addTracksToConnection(options._stream, peerConnection);\n\t\t}\n\n\t\t// What do we need to do now?\n\t\tif (options.originator) {\n\t\t\tconst dataConnection = this.connection;\n\n\t\t\tconst config: RTCDataChannelInit = { ordered: !!options.reliable };\n\n\t\t\tconst dataChannel = peerConnection.createDataChannel(\n\t\t\t\tdataConnection.label,\n\t\t\t\tconfig,\n\t\t\t);\n\t\t\tdataConnection._initializeDataChannel(dataChannel);\n\n\t\t\tvoid this._makeOffer();\n\t\t} else {\n\t\t\tvoid this.handleSDP(\"OFFER\", options.sdp);\n\t\t}\n\t}\n\n\t/** Start a PC. */\n\tprivate _startPeerConnection(): RTCPeerConnection {\n\t\tlogger.log(\"Creating RTCPeerConnection.\");\n\n\t\tconst peerConnection = new RTCPeerConnection(\n\t\t\tthis.connection.provider.options.config,\n\t\t);\n\n\t\tthis._setupListeners(peerConnection);\n\n\t\treturn peerConnection;\n\t}\n\n\t/** Set up various WebRTC listeners. */\n\tprivate _setupListeners(peerConnection: RTCPeerConnection) {\n\t\tconst peerId = this.connection.peer;\n\t\tconst connectionId = this.connection.connectionId;\n\t\tconst connectionType = this.connection.type;\n\t\tconst provider = this.connection.provider;\n\n\t\t// ICE CANDIDATES.\n\t\tlogger.log(\"Listening for ICE candidates.\");\n\n\t\tpeerConnection.onicecandidate = (evt) => {\n\t\t\tif (!evt.candidate || !evt.candidate.candidate) return;\n\n\t\t\tlogger.log(`Received ICE candidates for ${peerId}:`, evt.candidate);\n\n\t\t\tprovider.socket.send({\n\t\t\t\ttype: ServerMessageType.Candidate,\n\t\t\t\tpayload: {\n\t\t\t\t\tcandidate: evt.candidate,\n\t\t\t\t\ttype: connectionType,\n\t\t\t\t\tconnectionId: connectionId,\n\t\t\t\t},\n\t\t\t\tdst: peerId,\n\t\t\t});\n\t\t};\n\n\t\tpeerConnection.oniceconnectionstatechange = () => {\n\t\t\tswitch (peerConnection.iceConnectionState) {\n\t\t\t\tcase \"failed\":\n\t\t\t\t\tlogger.log(\n\t\t\t\t\t\t\"iceConnectionState is failed, closing connections to \" + peerId,\n\t\t\t\t\t);\n\t\t\t\t\tthis.connection.emitError(\n\t\t\t\t\t\tBaseConnectionErrorType.NegotiationFailed,\n\t\t\t\t\t\t\"Negotiation of connection to \" + peerId + \" failed.\",\n\t\t\t\t\t);\n\t\t\t\t\tthis.connection.close();\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"closed\":\n\t\t\t\t\tlogger.log(\n\t\t\t\t\t\t\"iceConnectionState is closed, closing connections to \" + peerId,\n\t\t\t\t\t);\n\t\t\t\t\tthis.connection.emitError(\n\t\t\t\t\t\tBaseConnectionErrorType.ConnectionClosed,\n\t\t\t\t\t\t\"Connection to \" + peerId + \" closed.\",\n\t\t\t\t\t);\n\t\t\t\t\tthis.connection.close();\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"disconnected\":\n\t\t\t\t\tlogger.log(\n\t\t\t\t\t\t\"iceConnectionState changed to disconnected on the connection with \" +\n\t\t\t\t\t\t\tpeerId,\n\t\t\t\t\t);\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"completed\":\n\t\t\t\t\tpeerConnection.onicecandidate = () => {};\n\t\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tthis.connection.emit(\n\t\t\t\t\"iceStateChanged\",\n\t\t\t\tpeerConnection.iceConnectionState,\n\t\t\t);\n\t\t};\n\n\t\t// DATACONNECTION.\n\t\tlogger.log(\"Listening for data channel\");\n\t\t// Fired between offer and answer, so options should already be saved\n\t\t// in the options hash.\n\t\tpeerConnection.ondatachannel = (evt) => {\n\t\t\tlogger.log(\"Received data channel\");\n\n\t\t\tconst dataChannel = evt.channel;\n\t\t\tconst connection = <DataConnection>(\n\t\t\t\tprovider.getConnection(peerId, connectionId)\n\t\t\t);\n\n\t\t\tconnection._initializeDataChannel(dataChannel);\n\t\t};\n\n\t\t// MEDIACONNECTION.\n\t\tlogger.log(\"Listening for remote stream\");\n\n\t\tpeerConnection.ontrack = (evt) => {\n\t\t\tlogger.log(\"Received remote stream\");\n\n\t\t\tconst stream = evt.streams[0];\n\t\t\tconst connection = provider.getConnection(peerId, connectionId);\n\n\t\t\tif (connection.type === ConnectionType.Media) {\n\t\t\t\tconst mediaConnection = <MediaConnection>connection;\n\n\t\t\t\tthis._addStreamToMediaConnection(stream, mediaConnection);\n\t\t\t}\n\t\t};\n\t}\n\n\tcleanup(): void {\n\t\tlogger.log(\"Cleaning up PeerConnection to \" + this.connection.peer);\n\n\t\tconst peerConnection = this.connection.peerConnection;\n\n\t\tif (!peerConnection) {\n\t\t\treturn;\n\t\t}\n\n\t\tthis.connection.peerConnection = null;\n\n\t\t//unsubscribe from all PeerConnection's events\n\t\tpeerConnection.onicecandidate =\n\t\t\tpeerConnection.oniceconnectionstatechange =\n\t\t\tpeerConnection.ondatachannel =\n\t\t\tpeerConnection.ontrack =\n\t\t\t\t() => {};\n\n\t\tconst peerConnectionNotClosed = peerConnection.signalingState !== \"closed\";\n\t\tlet dataChannelNotClosed = false;\n\n\t\tconst dataChannel = this.connection.dataChannel;\n\n\t\tif (dataChannel) {\n\t\t\tdataChannelNotClosed =\n\t\t\t\t!!dataChannel.readyState && dataChannel.readyState !== \"closed\";\n\t\t}\n\n\t\tif (peerConnectionNotClosed || dataChannelNotClosed) {\n\t\t\tpeerConnection.close();\n\t\t}\n\t}\n\n\tprivate async _makeOffer(): Promise<void> {\n\t\tconst peerConnection = this.connection.peerConnection;\n\t\tconst provider = this.connection.provider;\n\n\t\ttry {\n\t\t\tconst offer = await peerConnection.createOffer(\n\t\t\t\tthis.connection.options.constraints,\n\t\t\t);\n\n\t\t\tlogger.log(\"Created offer.\");\n\n\t\t\tif (\n\t\t\t\tthis.connection.options.sdpTransform &&\n\t\t\t\ttypeof this.connection.options.sdpTransform === \"function\"\n\t\t\t) {\n\t\t\t\toffer.sdp =\n\t\t\t\t\tthis.connection.options.sdpTransform(offer.sdp) || offer.sdp;\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tawait peerConnection.setLocalDescription(offer);\n\n\t\t\t\tlogger.log(\n\t\t\t\t\t\"Set localDescription:\",\n\t\t\t\t\toffer,\n\t\t\t\t\t`for:${this.connection.peer}`,\n\t\t\t\t);\n\n\t\t\t\tlet payload: any = {\n\t\t\t\t\tsdp: offer,\n\t\t\t\t\ttype: this.connection.type,\n\t\t\t\t\tconnectionId: this.connection.connectionId,\n\t\t\t\t\tmetadata: this.connection.metadata,\n\t\t\t\t};\n\n\t\t\t\tif (this.connection.type === ConnectionType.Data) {\n\t\t\t\t\tconst dataConnection = <DataConnection>(<unknown>this.connection);\n\n\t\t\t\t\tpayload = {\n\t\t\t\t\t\t...payload,\n\t\t\t\t\t\tlabel: dataConnection.label,\n\t\t\t\t\t\treliable: dataConnection.reliable,\n\t\t\t\t\t\tserialization: dataConnection.serialization,\n\t\t\t\t\t};\n\t\t\t\t}\n\n\t\t\t\tprovider.socket.send({\n\t\t\t\t\ttype: ServerMessageType.Offer,\n\t\t\t\t\tpayload,\n\t\t\t\t\tdst: this.connection.peer,\n\t\t\t\t});\n\t\t\t} catch (err) {\n\t\t\t\t// TODO: investigate why _makeOffer is being called from the answer\n\t\t\t\tif (\n\t\t\t\t\terr !=\n\t\t\t\t\t\"OperationError: Failed to set local offer sdp: Called in wrong state: kHaveRemoteOffer\"\n\t\t\t\t) {\n\t\t\t\t\tprovider.emitError(PeerErrorType.WebRTC, err);\n\t\t\t\t\tlogger.log(\"Failed to setLocalDescription, \", err);\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (err_1) {\n\t\t\tprovider.emitError(PeerErrorType.WebRTC, err_1);\n\t\t\tlogger.log(\"Failed to createOffer, \", err_1);\n\t\t}\n\t}\n\n\tprivate async _makeAnswer(): Promise<void> {\n\t\tconst peerConnection = this.connection.peerConnection;\n\t\tconst provider = this.connection.provider;\n\n\t\ttry {\n\t\t\tconst answer = await peerConnection.createAnswer();\n\t\t\tlogger.log(\"Created answer.\");\n\n\t\t\tif (\n\t\t\t\tthis.connection.options.sdpTransform &&\n\t\t\t\ttypeof this.connection.options.sdpTransform === \"function\"\n\t\t\t) {\n\t\t\t\tanswer.sdp =\n\t\t\t\t\tthis.connection.options.sdpTransform(answer.sdp) || answer.sdp;\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tawait peerConnection.setLocalDescription(answer);\n\n\t\t\t\tlogger.log(\n\t\t\t\t\t`Set localDescription:`,\n\t\t\t\t\tanswer,\n\t\t\t\t\t`for:${this.connection.peer}`,\n\t\t\t\t);\n\n\t\t\t\tprovider.socket.send({\n\t\t\t\t\ttype: ServerMessageType.Answer,\n\t\t\t\t\tpayload: {\n\t\t\t\t\t\tsdp: answer,\n\t\t\t\t\t\ttype: this.connection.type,\n\t\t\t\t\t\tconnectionId: this.connection.connectionId,\n\t\t\t\t\t},\n\t\t\t\t\tdst: this.connection.peer,\n\t\t\t\t});\n\t\t\t} catch (err) {\n\t\t\t\tprovider.emitError(PeerErrorType.WebRTC, err);\n\t\t\t\tlogger.log(\"Failed to setLocalDescription, \", err);\n\t\t\t}\n\t\t} catch (err_1) {\n\t\t\tprovider.emitError(PeerErrorType.WebRTC, err_1);\n\t\t\tlogger.log(\"Failed to create answer, \", err_1);\n\t\t}\n\t}\n\n\t/** Handle an SDP. */\n\tasync handleSDP(type: string, sdp: any): Promise<void> {\n\t\tsdp = new RTCSessionDescription(sdp);\n\t\tconst peerConnection = this.connection.peerConnection;\n\t\tconst provider = this.connection.provider;\n\n\t\tlogger.log(\"Setting remote description\", sdp);\n\n\t\tconst self = this;\n\n\t\ttry {\n\t\t\tawait peerConnection.setRemoteDescription(sdp);\n\t\t\tlogger.log(`Set remoteDescription:${type} for:${this.connection.peer}`);\n\t\t\tif (type === \"OFFER\") {\n\t\t\t\tawait self._makeAnswer();\n\t\t\t}\n\t\t} catch (err) {\n\t\t\tprovider.emitError(PeerErrorType.WebRTC, err);\n\t\t\tlogger.log(\"Failed to setRemoteDescription, \", err);\n\t\t}\n\t}\n\n\t/** Handle a candidate. */\n\tasync handleCandidate(ice: RTCIceCandidate) {\n\t\tlogger.log(`handleCandidate:`, ice);\n\n\t\ttry {\n\t\t\tawait this.connection.peerConnection.addIceCandidate(ice);\n\t\t\tlogger.log(`Added ICE candidate for:${this.connection.peer}`);\n\t\t} catch (err) {\n\t\t\tthis.connection.provider.emitError(PeerErrorType.WebRTC, err);\n\t\t\tlogger.log(\"Failed to handleCandidate, \", err);\n\t\t}\n\t}\n\n\tprivate _addTracksToConnection(\n\t\tstream: MediaStream,\n\t\tpeerConnection: RTCPeerConnection,\n\t): void {\n\t\tlogger.log(`add tracks from stream ${stream.id} to peer connection`);\n\n\t\tif (!peerConnection.addTrack) {\n\t\t\treturn logger.error(\n\t\t\t\t`Your browser does't support RTCPeerConnection#addTrack. Ignored.`,\n\t\t\t);\n\t\t}\n\n\t\tstream.getTracks().forEach((track) => {\n\t\t\tpeerConnection.addTrack(track, stream);\n\t\t});\n\t}\n\n\tprivate _addStreamToMediaConnection(\n\t\tstream: MediaStream,\n\t\tmediaConnection: MediaConnection,\n\t): void {\n\t\tlogger.log(\n\t\t\t`add stream ${stream.id} to media connection ${mediaConnection.connectionId}`,\n\t\t);\n\n\t\tmediaConnection.addStream(stream);\n\t}\n}\n"
  },
  {
    "path": "lib/optionInterfaces.ts",
    "content": "export interface AnswerOption {\n\t/**\n\t * Function which runs before create answer to modify sdp answer message.\n\t */\n\tsdpTransform?: Function;\n}\n\nexport interface PeerJSOption {\n\tkey?: string;\n\thost?: string;\n\tport?: number;\n\tpath?: string;\n\tsecure?: boolean;\n\ttoken?: string;\n\tconfig?: RTCConfiguration;\n\tdebug?: number;\n\treferrerPolicy?: ReferrerPolicy;\n}\n\nexport interface PeerConnectOption {\n\t/**\n\t * A unique label by which you want to identify this data connection.\n\t * If left unspecified, a label will be generated at random.\n\t *\n\t * Can be accessed with {@apilink DataConnection.label}\n\t */\n\tlabel?: string;\n\t/**\n\t * Metadata associated with the connection, passed in by whoever initiated the connection.\n\t *\n\t * Can be accessed with {@apilink DataConnection.metadata}.\n\t * Can be any serializable type.\n\t */\n\tmetadata?: any;\n\tserialization?: string;\n\treliable?: boolean;\n}\n\nexport interface CallOption {\n\t/**\n\t * Metadata associated with the connection, passed in by whoever initiated the connection.\n\t *\n\t * Can be accessed with {@apilink MediaConnection.metadata}.\n\t * Can be any serializable type.\n\t */\n\tmetadata?: any;\n\t/**\n\t * Function which runs before create offer to modify sdp offer message.\n\t */\n\tsdpTransform?: Function;\n}\n"
  },
  {
    "path": "lib/peer.ts",
    "content": "import { util } from \"./util\";\nimport logger, { LogLevel } from \"./logger\";\nimport { Socket } from \"./socket\";\nimport { MediaConnection } from \"./mediaconnection\";\nimport type { DataConnection } from \"./dataconnection/DataConnection\";\nimport {\n\tConnectionType,\n\tPeerErrorType,\n\tServerMessageType,\n\tSocketEventType,\n} from \"./enums\";\nimport type { ServerMessage } from \"./servermessage\";\nimport { API } from \"./api\";\nimport type {\n\tCallOption,\n\tPeerConnectOption,\n\tPeerJSOption,\n} from \"./optionInterfaces\";\nimport { BinaryPack } from \"./dataconnection/BufferedConnection/BinaryPack\";\nimport { Raw } from \"./dataconnection/BufferedConnection/Raw\";\nimport { Json } from \"./dataconnection/BufferedConnection/Json\";\n\nimport { EventEmitterWithError, PeerError } from \"./peerError\";\n\nclass PeerOptions implements PeerJSOption {\n\t/**\n\t * Prints log messages depending on the debug level passed in.\n\t */\n\tdebug?: LogLevel;\n\t/**\n\t * Server host. Defaults to `0.peerjs.com`.\n\t * Also accepts `'/'` to signify relative hostname.\n\t */\n\thost?: string;\n\t/**\n\t * Server port. Defaults to `443`.\n\t */\n\tport?: number;\n\t/**\n\t * The path where your self-hosted PeerServer is running. Defaults to `'/'`\n\t */\n\tpath?: string;\n\t/**\n\t * API key for the PeerServer.\n\t * This is not used anymore.\n\t * @deprecated\n\t */\n\tkey?: string;\n\ttoken?: string;\n\t/**\n\t * Configuration hash passed to RTCPeerConnection.\n\t * This hash contains any custom ICE/TURN server configuration.\n\t *\n\t * Defaults to {@apilink util.defaultConfig}\n\t */\n\tconfig?: any;\n\t/**\n\t * Set to true `true` if you're using TLS.\n\t * :::danger\n\t * If possible *always use TLS*\n\t * :::\n\t */\n\tsecure?: boolean;\n\tpingInterval?: number;\n\treferrerPolicy?: ReferrerPolicy;\n\tlogFunction?: (logLevel: LogLevel, ...rest: any[]) => void;\n\tserializers?: SerializerMapping;\n}\n\nexport { type PeerOptions };\n\nexport interface SerializerMapping {\n\t[key: string]: new (\n\t\tpeerId: string,\n\t\tprovider: Peer,\n\t\toptions: any,\n\t) => DataConnection;\n}\n\nexport interface PeerEvents {\n\t/**\n\t * Emitted when a connection to the PeerServer is established.\n\t *\n\t * You may use the peer before this is emitted, but messages to the server will be queued. <code>id</code> is the brokering ID of the peer (which was either provided in the constructor or assigned by the server).<span class='tip'>You should not wait for this event before connecting to other peers if connection speed is important.</span>\n\t */\n\topen: (id: string) => void;\n\t/**\n\t * Emitted when a new data connection is established from a remote peer.\n\t */\n\tconnection: (dataConnection: DataConnection) => void;\n\t/**\n\t * Emitted when a remote peer attempts to call you.\n\t */\n\tcall: (mediaConnection: MediaConnection) => void;\n\t/**\n\t * Emitted when the peer is destroyed and can no longer accept or create any new connections.\n\t */\n\tclose: () => void;\n\t/**\n\t * Emitted when the peer is disconnected from the signalling server\n\t */\n\tdisconnected: (currentId: string) => void;\n\t/**\n\t * Errors on the peer are almost always fatal and will destroy the peer.\n\t *\n\t * Errors from the underlying socket and PeerConnections are forwarded here.\n\t */\n\terror: (error: PeerError<`${PeerErrorType}`>) => void;\n}\n/**\n * A peer who can initiate connections with other peers.\n */\nexport class Peer extends EventEmitterWithError<PeerErrorType, PeerEvents> {\n\tprivate static readonly DEFAULT_KEY = \"peerjs\";\n\n\tprotected readonly _serializers: SerializerMapping = {\n\t\traw: Raw,\n\t\tjson: Json,\n\t\tbinary: BinaryPack,\n\t\t\"binary-utf8\": BinaryPack,\n\n\t\tdefault: BinaryPack,\n\t};\n\tprivate readonly _options: PeerOptions;\n\tprivate readonly _api: API;\n\tprivate readonly _socket: Socket;\n\n\tprivate _id: string | null = null;\n\tprivate _lastServerId: string | null = null;\n\n\t// States.\n\tprivate _destroyed = false; // Connections have been killed\n\tprivate _disconnected = false; // Connection to PeerServer killed but P2P connections still active\n\tprivate _open = false; // Sockets and such are not yet open.\n\tprivate readonly _connections: Map<\n\t\tstring,\n\t\t(DataConnection | MediaConnection)[]\n\t> = new Map(); // All connections for this peer.\n\tprivate readonly _lostMessages: Map<string, ServerMessage[]> = new Map(); // src => [list of messages]\n\t/**\n\t * The brokering ID of this peer\n\t *\n\t * If no ID was specified in {@apilink Peer | the constructor},\n\t * this will be `undefined` until the {@apilink PeerEvents | `open`} event is emitted.\n\t */\n\tget id() {\n\t\treturn this._id;\n\t}\n\n\tget options() {\n\t\treturn this._options;\n\t}\n\n\tget open() {\n\t\treturn this._open;\n\t}\n\n\t/**\n\t * @internal\n\t */\n\tget socket() {\n\t\treturn this._socket;\n\t}\n\n\t/**\n\t * A hash of all connections associated with this peer, keyed by the remote peer's ID.\n\t * @deprecated\n\t * Return type will change from Object to Map<string,[]>\n\t */\n\tget connections(): Object {\n\t\tconst plainConnections = Object.create(null);\n\n\t\tfor (const [k, v] of this._connections) {\n\t\t\tplainConnections[k] = v;\n\t\t}\n\n\t\treturn plainConnections;\n\t}\n\n\t/**\n\t * true if this peer and all of its connections can no longer be used.\n\t */\n\tget destroyed() {\n\t\treturn this._destroyed;\n\t}\n\t/**\n\t * false if there is an active connection to the PeerServer.\n\t */\n\tget disconnected() {\n\t\treturn this._disconnected;\n\t}\n\n\t/**\n\t * A peer can connect to other peers and listen for connections.\n\t */\n\tconstructor();\n\n\t/**\n\t * A peer can connect to other peers and listen for connections.\n\t * @param options for specifying details about PeerServer\n\t */\n\tconstructor(options: PeerOptions);\n\n\t/**\n\t * A peer can connect to other peers and listen for connections.\n\t * @param id Other peers can connect to this peer using the provided ID.\n\t *     If no ID is given, one will be generated by the brokering server.\n\t * The ID must start and end with an alphanumeric character (lower or upper case character or a digit). In the middle of the ID spaces, dashes (-) and underscores (_) are allowed. Use {@apilink PeerOptions.metadata } to send identifying information.\n\t * @param options for specifying details about PeerServer\n\t */\n\tconstructor(id: string, options?: PeerOptions);\n\n\tconstructor(id?: string | PeerOptions, options?: PeerOptions) {\n\t\tsuper();\n\n\t\tlet userId: string | undefined;\n\n\t\t// Deal with overloading\n\t\tif (id && id.constructor == Object) {\n\t\t\toptions = id as PeerOptions;\n\t\t} else if (id) {\n\t\t\tuserId = id.toString();\n\t\t}\n\n\t\t// Configurize options\n\t\toptions = {\n\t\t\tdebug: 0, // 1: Errors, 2: Warnings, 3: All logs\n\t\t\thost: util.CLOUD_HOST,\n\t\t\tport: util.CLOUD_PORT,\n\t\t\tpath: \"/\",\n\t\t\tkey: Peer.DEFAULT_KEY,\n\t\t\ttoken: util.randomToken(),\n\t\t\tconfig: util.defaultConfig,\n\t\t\treferrerPolicy: \"strict-origin-when-cross-origin\",\n\t\t\tserializers: {},\n\t\t\t...options,\n\t\t};\n\t\tthis._options = options;\n\t\tthis._serializers = { ...this._serializers, ...this.options.serializers };\n\n\t\t// Detect relative URL host.\n\t\tif (this._options.host === \"/\") {\n\t\t\tthis._options.host = window.location.hostname;\n\t\t}\n\n\t\t// Set path correctly.\n\t\tif (this._options.path) {\n\t\t\tif (this._options.path[0] !== \"/\") {\n\t\t\t\tthis._options.path = \"/\" + this._options.path;\n\t\t\t}\n\t\t\tif (this._options.path[this._options.path.length - 1] !== \"/\") {\n\t\t\t\tthis._options.path += \"/\";\n\t\t\t}\n\t\t}\n\n\t\t// Set whether we use SSL to same as current host\n\t\tif (\n\t\t\tthis._options.secure === undefined &&\n\t\t\tthis._options.host !== util.CLOUD_HOST\n\t\t) {\n\t\t\tthis._options.secure = util.isSecure();\n\t\t} else if (this._options.host == util.CLOUD_HOST) {\n\t\t\tthis._options.secure = true;\n\t\t}\n\t\t// Set a custom log function if present\n\t\tif (this._options.logFunction) {\n\t\t\tlogger.setLogFunction(this._options.logFunction);\n\t\t}\n\n\t\tlogger.logLevel = this._options.debug || 0;\n\n\t\tthis._api = new API(options);\n\t\tthis._socket = this._createServerConnection();\n\n\t\t// Sanity checks\n\t\t// Ensure WebRTC supported\n\t\tif (!util.supports.audioVideo && !util.supports.data) {\n\t\t\tthis._delayedAbort(\n\t\t\t\tPeerErrorType.BrowserIncompatible,\n\t\t\t\t\"The current browser does not support WebRTC\",\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\n\t\t// Ensure alphanumeric id\n\t\tif (!!userId && !util.validateId(userId)) {\n\t\t\tthis._delayedAbort(PeerErrorType.InvalidID, `ID \"${userId}\" is invalid`);\n\t\t\treturn;\n\t\t}\n\n\t\tif (userId) {\n\t\t\tthis._initialize(userId);\n\t\t} else {\n\t\t\tthis._api\n\t\t\t\t.retrieveId()\n\t\t\t\t.then((id) => this._initialize(id))\n\t\t\t\t.catch((error) => this._abort(PeerErrorType.ServerError, error));\n\t\t}\n\t}\n\n\tprivate _createServerConnection(): Socket {\n\t\tconst socket = new Socket(\n\t\t\tthis._options.secure,\n\t\t\tthis._options.host!,\n\t\t\tthis._options.port!,\n\t\t\tthis._options.path!,\n\t\t\tthis._options.key!,\n\t\t\tthis._options.pingInterval,\n\t\t);\n\n\t\tsocket.on(SocketEventType.Message, (data: ServerMessage) => {\n\t\t\tthis._handleMessage(data);\n\t\t});\n\n\t\tsocket.on(SocketEventType.Error, (error: string) => {\n\t\t\tthis._abort(PeerErrorType.SocketError, error);\n\t\t});\n\n\t\tsocket.on(SocketEventType.Disconnected, () => {\n\t\t\tif (this.disconnected) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tthis.emitError(PeerErrorType.Network, \"Lost connection to server.\");\n\t\t\tthis.disconnect();\n\t\t});\n\n\t\tsocket.on(SocketEventType.Close, () => {\n\t\t\tif (this.disconnected) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tthis._abort(\n\t\t\t\tPeerErrorType.SocketClosed,\n\t\t\t\t\"Underlying socket is already closed.\",\n\t\t\t);\n\t\t});\n\n\t\treturn socket;\n\t}\n\n\t/** Initialize a connection with the server. */\n\tprivate _initialize(id: string): void {\n\t\tthis._id = id;\n\t\tthis.socket.start(id, this._options.token!);\n\t}\n\n\t/** Handles messages from the server. */\n\tprivate _handleMessage(message: ServerMessage): void {\n\t\tconst type = message.type;\n\t\tconst payload = message.payload;\n\t\tconst peerId = message.src;\n\n\t\tswitch (type) {\n\t\t\tcase ServerMessageType.Open: // The connection to the server is open.\n\t\t\t\tthis._lastServerId = this.id;\n\t\t\t\tthis._open = true;\n\t\t\t\tthis.emit(\"open\", this.id);\n\t\t\t\tbreak;\n\t\t\tcase ServerMessageType.Error: // Server error.\n\t\t\t\tthis._abort(PeerErrorType.ServerError, payload.msg);\n\t\t\t\tbreak;\n\t\t\tcase ServerMessageType.IdTaken: // The selected ID is taken.\n\t\t\t\tthis._abort(PeerErrorType.UnavailableID, `ID \"${this.id}\" is taken`);\n\t\t\t\tbreak;\n\t\t\tcase ServerMessageType.InvalidKey: // The given API key cannot be found.\n\t\t\t\tthis._abort(\n\t\t\t\t\tPeerErrorType.InvalidKey,\n\t\t\t\t\t`API KEY \"${this._options.key}\" is invalid`,\n\t\t\t\t);\n\t\t\t\tbreak;\n\t\t\tcase ServerMessageType.Leave: // Another peer has closed its connection to this peer.\n\t\t\t\tlogger.log(`Received leave message from ${peerId}`);\n\t\t\t\tthis._cleanupPeer(peerId);\n\t\t\t\tthis._connections.delete(peerId);\n\t\t\t\tbreak;\n\t\t\tcase ServerMessageType.Expire: // The offer sent to a peer has expired without response.\n\t\t\t\tthis.emitError(\n\t\t\t\t\tPeerErrorType.PeerUnavailable,\n\t\t\t\t\t`Could not connect to peer ${peerId}`,\n\t\t\t\t);\n\t\t\t\tbreak;\n\t\t\tcase ServerMessageType.Offer: {\n\t\t\t\t// we should consider switching this to CALL/CONNECT, but this is the least breaking option.\n\t\t\t\tconst connectionId = payload.connectionId;\n\t\t\t\tlet connection = this.getConnection(peerId, connectionId);\n\n\t\t\t\tif (connection) {\n\t\t\t\t\tconnection.close();\n\t\t\t\t\tlogger.warn(\n\t\t\t\t\t\t`Offer received for existing Connection ID:${connectionId}`,\n\t\t\t\t\t);\n\t\t\t\t}\n\n\t\t\t\t// Create a new connection.\n\t\t\t\tif (payload.type === ConnectionType.Media) {\n\t\t\t\t\tconst mediaConnection = new MediaConnection(peerId, this, {\n\t\t\t\t\t\tconnectionId: connectionId,\n\t\t\t\t\t\t_payload: payload,\n\t\t\t\t\t\tmetadata: payload.metadata,\n\t\t\t\t\t});\n\t\t\t\t\tconnection = mediaConnection;\n\t\t\t\t\tthis._addConnection(peerId, connection);\n\t\t\t\t\tthis.emit(\"call\", mediaConnection);\n\t\t\t\t} else if (payload.type === ConnectionType.Data) {\n\t\t\t\t\tconst dataConnection = new this._serializers[payload.serialization](\n\t\t\t\t\t\tpeerId,\n\t\t\t\t\t\tthis,\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tconnectionId: connectionId,\n\t\t\t\t\t\t\t_payload: payload,\n\t\t\t\t\t\t\tmetadata: payload.metadata,\n\t\t\t\t\t\t\tlabel: payload.label,\n\t\t\t\t\t\t\tserialization: payload.serialization,\n\t\t\t\t\t\t\treliable: payload.reliable,\n\t\t\t\t\t\t},\n\t\t\t\t\t);\n\t\t\t\t\tconnection = dataConnection;\n\n\t\t\t\t\tthis._addConnection(peerId, connection);\n\t\t\t\t\tthis.emit(\"connection\", dataConnection);\n\t\t\t\t} else {\n\t\t\t\t\tlogger.warn(`Received malformed connection type:${payload.type}`);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\t// Find messages.\n\t\t\t\tconst messages = this._getMessages(connectionId);\n\t\t\t\tfor (const message of messages) {\n\t\t\t\t\tconnection.handleMessage(message);\n\t\t\t\t}\n\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tdefault: {\n\t\t\t\tif (!payload) {\n\t\t\t\t\tlogger.warn(\n\t\t\t\t\t\t`You received a malformed message from ${peerId} of type ${type}`,\n\t\t\t\t\t);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tconst connectionId = payload.connectionId;\n\t\t\t\tconst connection = this.getConnection(peerId, connectionId);\n\n\t\t\t\tif (connection && connection.peerConnection) {\n\t\t\t\t\t// Pass it on.\n\t\t\t\t\tconnection.handleMessage(message);\n\t\t\t\t} else if (connectionId) {\n\t\t\t\t\t// Store for possible later use\n\t\t\t\t\tthis._storeMessage(connectionId, message);\n\t\t\t\t} else {\n\t\t\t\t\tlogger.warn(\"You received an unrecognized message:\", message);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\t/** Stores messages without a set up connection, to be claimed later. */\n\tprivate _storeMessage(connectionId: string, message: ServerMessage): void {\n\t\tif (!this._lostMessages.has(connectionId)) {\n\t\t\tthis._lostMessages.set(connectionId, []);\n\t\t}\n\n\t\tthis._lostMessages.get(connectionId).push(message);\n\t}\n\n\t/**\n\t * Retrieve messages from lost message store\n\t * @internal\n\t */\n\t//TODO Change it to private\n\tpublic _getMessages(connectionId: string): ServerMessage[] {\n\t\tconst messages = this._lostMessages.get(connectionId);\n\n\t\tif (messages) {\n\t\t\tthis._lostMessages.delete(connectionId);\n\t\t\treturn messages;\n\t\t}\n\n\t\treturn [];\n\t}\n\n\t/**\n\t * Connects to the remote peer specified by id and returns a data connection.\n\t * @param peer The brokering ID of the remote peer (their {@apilink Peer.id}).\n\t * @param options for specifying details about Peer Connection\n\t */\n\tconnect(peer: string, options: PeerConnectOption = {}): DataConnection {\n\t\toptions = {\n\t\t\tserialization: \"default\",\n\t\t\t...options,\n\t\t};\n\t\tif (this.disconnected) {\n\t\t\tlogger.warn(\n\t\t\t\t\"You cannot connect to a new Peer because you called \" +\n\t\t\t\t\t\".disconnect() on this Peer and ended your connection with the \" +\n\t\t\t\t\t\"server. You can create a new Peer to reconnect, or call reconnect \" +\n\t\t\t\t\t\"on this peer if you believe its ID to still be available.\",\n\t\t\t);\n\t\t\tthis.emitError(\n\t\t\t\tPeerErrorType.Disconnected,\n\t\t\t\t\"Cannot connect to new Peer after disconnecting from server.\",\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\n\t\tconst dataConnection = new this._serializers[options.serialization](\n\t\t\tpeer,\n\t\t\tthis,\n\t\t\toptions,\n\t\t);\n\t\tthis._addConnection(peer, dataConnection);\n\t\treturn dataConnection;\n\t}\n\n\t/**\n\t * Calls the remote peer specified by id and returns a media connection.\n\t * @param peer The brokering ID of the remote peer (their peer.id).\n\t * @param stream The caller's media stream\n\t * @param options Metadata associated with the connection, passed in by whoever initiated the connection.\n\t */\n\tcall(\n\t\tpeer: string,\n\t\tstream: MediaStream,\n\t\toptions: CallOption = {},\n\t): MediaConnection {\n\t\tif (this.disconnected) {\n\t\t\tlogger.warn(\n\t\t\t\t\"You cannot connect to a new Peer because you called \" +\n\t\t\t\t\t\".disconnect() on this Peer and ended your connection with the \" +\n\t\t\t\t\t\"server. You can create a new Peer to reconnect.\",\n\t\t\t);\n\t\t\tthis.emitError(\n\t\t\t\tPeerErrorType.Disconnected,\n\t\t\t\t\"Cannot connect to new Peer after disconnecting from server.\",\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\n\t\tif (!stream) {\n\t\t\tlogger.error(\n\t\t\t\t\"To call a peer, you must provide a stream from your browser's `getUserMedia`.\",\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\n\t\tconst mediaConnection = new MediaConnection(peer, this, {\n\t\t\t...options,\n\t\t\t_stream: stream,\n\t\t});\n\t\tthis._addConnection(peer, mediaConnection);\n\t\treturn mediaConnection;\n\t}\n\n\t/** Add a data/media connection to this peer. */\n\tprivate _addConnection(\n\t\tpeerId: string,\n\t\tconnection: MediaConnection | DataConnection,\n\t): void {\n\t\tlogger.log(\n\t\t\t`add connection ${connection.type}:${connection.connectionId} to peerId:${peerId}`,\n\t\t);\n\n\t\tif (!this._connections.has(peerId)) {\n\t\t\tthis._connections.set(peerId, []);\n\t\t}\n\t\tthis._connections.get(peerId).push(connection);\n\t}\n\n\t//TODO should be private\n\t_removeConnection(connection: DataConnection | MediaConnection): void {\n\t\tconst connections = this._connections.get(connection.peer);\n\n\t\tif (connections) {\n\t\t\tconst index = connections.indexOf(connection);\n\n\t\t\tif (index !== -1) {\n\t\t\t\tconnections.splice(index, 1);\n\t\t\t}\n\t\t}\n\n\t\t//remove from lost messages\n\t\tthis._lostMessages.delete(connection.connectionId);\n\t}\n\n\t/** Retrieve a data/media connection for this peer. */\n\tgetConnection(\n\t\tpeerId: string,\n\t\tconnectionId: string,\n\t): null | DataConnection | MediaConnection {\n\t\tconst connections = this._connections.get(peerId);\n\t\tif (!connections) {\n\t\t\treturn null;\n\t\t}\n\n\t\tfor (const connection of connections) {\n\t\t\tif (connection.connectionId === connectionId) {\n\t\t\t\treturn connection;\n\t\t\t}\n\t\t}\n\n\t\treturn null;\n\t}\n\n\tprivate _delayedAbort(type: PeerErrorType, message: string | Error): void {\n\t\tsetTimeout(() => {\n\t\t\tthis._abort(type, message);\n\t\t}, 0);\n\t}\n\n\t/**\n\t * Emits an error message and destroys the Peer.\n\t * The Peer is not destroyed if it's in a disconnected state, in which case\n\t * it retains its disconnected state and its existing connections.\n\t */\n\tprivate _abort(type: PeerErrorType, message: string | Error): void {\n\t\tlogger.error(\"Aborting!\");\n\n\t\tthis.emitError(type, message);\n\n\t\tif (!this._lastServerId) {\n\t\t\tthis.destroy();\n\t\t} else {\n\t\t\tthis.disconnect();\n\t\t}\n\t}\n\n\t/**\n\t * Destroys the Peer: closes all active connections as well as the connection\n\t * to the server.\n\t *\n\t * :::caution\n\t * This cannot be undone; the respective peer object will no longer be able\n\t * to create or receive any connections, its ID will be forfeited on the server,\n\t * and all of its data and media connections will be closed.\n\t * :::\n\t */\n\tdestroy(): void {\n\t\tif (this.destroyed) {\n\t\t\treturn;\n\t\t}\n\n\t\tlogger.log(`Destroy peer with ID:${this.id}`);\n\n\t\tthis.disconnect();\n\t\tthis._cleanup();\n\n\t\tthis._destroyed = true;\n\n\t\tthis.emit(\"close\");\n\t}\n\n\t/** Disconnects every connection on this peer. */\n\tprivate _cleanup(): void {\n\t\tfor (const peerId of this._connections.keys()) {\n\t\t\tthis._cleanupPeer(peerId);\n\t\t\tthis._connections.delete(peerId);\n\t\t}\n\n\t\tthis.socket.removeAllListeners();\n\t}\n\n\t/** Closes all connections to this peer. */\n\tprivate _cleanupPeer(peerId: string): void {\n\t\tconst connections = this._connections.get(peerId);\n\n\t\tif (!connections) return;\n\n\t\tfor (const connection of connections) {\n\t\t\tconnection.close();\n\t\t}\n\t}\n\n\t/**\n\t * Disconnects the Peer's connection to the PeerServer. Does not close any\n\t *  active connections.\n\t * Warning: The peer can no longer create or accept connections after being\n\t *  disconnected. It also cannot reconnect to the server.\n\t */\n\tdisconnect(): void {\n\t\tif (this.disconnected) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst currentId = this.id;\n\n\t\tlogger.log(`Disconnect peer with ID:${currentId}`);\n\n\t\tthis._disconnected = true;\n\t\tthis._open = false;\n\n\t\tthis.socket.close();\n\n\t\tthis._lastServerId = currentId;\n\t\tthis._id = null;\n\n\t\tthis.emit(\"disconnected\", currentId);\n\t}\n\n\t/** Attempts to reconnect with the same ID.\n\t *\n\t * Only {@apilink Peer.disconnect | disconnected peers} can be reconnected.\n\t * Destroyed peers cannot be reconnected.\n\t * If the connection fails (as an example, if the peer's old ID is now taken),\n\t * the peer's existing connections will not close, but any associated errors events will fire.\n\t */\n\treconnect(): void {\n\t\tif (this.disconnected && !this.destroyed) {\n\t\t\tlogger.log(\n\t\t\t\t`Attempting reconnection to server with ID ${this._lastServerId}`,\n\t\t\t);\n\t\t\tthis._disconnected = false;\n\t\t\tthis._initialize(this._lastServerId!);\n\t\t} else if (this.destroyed) {\n\t\t\tthrow new Error(\n\t\t\t\t\"This peer cannot reconnect to the server. It has already been destroyed.\",\n\t\t\t);\n\t\t} else if (!this.disconnected && !this.open) {\n\t\t\t// Do nothing. We're still connecting the first time.\n\t\t\tlogger.error(\n\t\t\t\t\"In a hurry? We're still trying to make the initial connection!\",\n\t\t\t);\n\t\t} else {\n\t\t\tthrow new Error(\n\t\t\t\t`Peer ${this.id} cannot reconnect because it is not disconnected from the server!`,\n\t\t\t);\n\t\t}\n\t}\n\n\t/**\n\t * Get a list of available peer IDs. If you're running your own server, you'll\n\t * want to set allow_discovery: true in the PeerServer options. If you're using\n\t * the cloud server, email team@peerjs.com to get the functionality enabled for\n\t * your key.\n\t */\n\tlistAllPeers(cb = (_: any[]) => {}): void {\n\t\tthis._api\n\t\t\t.listAllPeers()\n\t\t\t.then((peers) => cb(peers))\n\t\t\t.catch((error) => this._abort(PeerErrorType.ServerError, error));\n\t}\n}\n"
  },
  {
    "path": "lib/peerError.ts",
    "content": "import { EventEmitter } from \"eventemitter3\";\nimport logger from \"./logger\";\n\nexport interface EventsWithError<ErrorType extends string> {\n\terror: (error: PeerError<`${ErrorType}`>) => void;\n}\n\nexport class EventEmitterWithError<\n\tErrorType extends string,\n\tEvents extends EventsWithError<ErrorType>,\n> extends EventEmitter<Events, never> {\n\t/**\n\t * Emits a typed error message.\n\t *\n\t * @internal\n\t */\n\temitError(type: ErrorType, err: string | Error): void {\n\t\tlogger.error(\"Error:\", err);\n\n\t\t// @ts-ignore\n\t\tthis.emit(\"error\", new PeerError<`${ErrorType}`>(`${type}`, err));\n\t}\n}\n/**\n * A PeerError is emitted whenever an error occurs.\n * It always has a `.type`, which can be used to identify the error.\n */\nexport class PeerError<T extends string> extends Error {\n\t/**\n\t * @internal\n\t */\n\tconstructor(type: T, err: Error | string) {\n\t\tif (typeof err === \"string\") {\n\t\t\tsuper(err);\n\t\t} else {\n\t\t\tsuper();\n\t\t\tObject.assign(this, err);\n\t\t}\n\n\t\tthis.type = type;\n\t}\n\n\tpublic type: T;\n}\n"
  },
  {
    "path": "lib/servermessage.ts",
    "content": "import type { ServerMessageType } from \"./enums\";\n\nexport class ServerMessage {\n\ttype: ServerMessageType;\n\tpayload: any;\n\tsrc: string;\n}\n"
  },
  {
    "path": "lib/socket.ts",
    "content": "import { EventEmitter } from \"eventemitter3\";\nimport logger from \"./logger\";\nimport { ServerMessageType, SocketEventType } from \"./enums\";\nimport { version } from \"./version\";\n\n/**\n * An abstraction on top of WebSockets to provide fastest\n * possible connection for peers.\n */\nexport class Socket extends EventEmitter {\n\tprivate _disconnected: boolean = true;\n\tprivate _id?: string;\n\tprivate _messagesQueue: Array<object> = [];\n\tprivate _socket?: WebSocket;\n\tprivate _wsPingTimer?: any;\n\tprivate readonly _baseUrl: string;\n\n\tconstructor(\n\t\tsecure: any,\n\t\thost: string,\n\t\tport: number,\n\t\tpath: string,\n\t\tkey: string,\n\t\tprivate readonly pingInterval: number = 5000,\n\t) {\n\t\tsuper();\n\n\t\tconst wsProtocol = secure ? \"wss://\" : \"ws://\";\n\n\t\tthis._baseUrl = wsProtocol + host + \":\" + port + path + \"peerjs?key=\" + key;\n\t}\n\n\tstart(id: string, token: string): void {\n\t\tthis._id = id;\n\n\t\tconst wsUrl = `${this._baseUrl}&id=${id}&token=${token}`;\n\n\t\tif (!!this._socket || !this._disconnected) {\n\t\t\treturn;\n\t\t}\n\n\t\tthis._socket = new WebSocket(wsUrl + \"&version=\" + version);\n\t\tthis._disconnected = false;\n\n\t\tthis._socket.onmessage = (event) => {\n\t\t\tlet data;\n\n\t\t\ttry {\n\t\t\t\tdata = JSON.parse(event.data);\n\t\t\t\tlogger.log(\"Server message received:\", data);\n\t\t\t} catch (e) {\n\t\t\t\tlogger.log(\"Invalid server message\", event.data);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tthis.emit(SocketEventType.Message, data);\n\t\t};\n\n\t\tthis._socket.onclose = (event) => {\n\t\t\tif (this._disconnected) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tlogger.log(\"Socket closed.\", event);\n\n\t\t\tthis._cleanup();\n\t\t\tthis._disconnected = true;\n\n\t\t\tthis.emit(SocketEventType.Disconnected);\n\t\t};\n\n\t\t// Take care of the queue of connections if necessary and make sure Peer knows\n\t\t// socket is open.\n\t\tthis._socket.onopen = () => {\n\t\t\tif (this._disconnected) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tthis._sendQueuedMessages();\n\n\t\t\tlogger.log(\"Socket open\");\n\n\t\t\tthis._scheduleHeartbeat();\n\t\t};\n\t}\n\n\tprivate _scheduleHeartbeat(): void {\n\t\tthis._wsPingTimer = setTimeout(() => {\n\t\t\tthis._sendHeartbeat();\n\t\t}, this.pingInterval);\n\t}\n\n\tprivate _sendHeartbeat(): void {\n\t\tif (!this._wsOpen()) {\n\t\t\tlogger.log(`Cannot send heartbeat, because socket closed`);\n\t\t\treturn;\n\t\t}\n\n\t\tconst message = JSON.stringify({ type: ServerMessageType.Heartbeat });\n\n\t\tthis._socket!.send(message);\n\n\t\tthis._scheduleHeartbeat();\n\t}\n\n\t/** Is the websocket currently open? */\n\tprivate _wsOpen(): boolean {\n\t\treturn !!this._socket && this._socket.readyState === 1;\n\t}\n\n\t/** Send queued messages. */\n\tprivate _sendQueuedMessages(): void {\n\t\t//Create copy of queue and clear it,\n\t\t//because send method push the message back to queue if smth will go wrong\n\t\tconst copiedQueue = [...this._messagesQueue];\n\t\tthis._messagesQueue = [];\n\n\t\tfor (const message of copiedQueue) {\n\t\t\tthis.send(message);\n\t\t}\n\t}\n\n\t/** Exposed send for DC & Peer. */\n\tsend(data: any): void {\n\t\tif (this._disconnected) {\n\t\t\treturn;\n\t\t}\n\n\t\t// If we didn't get an ID yet, we can't yet send anything so we should queue\n\t\t// up these messages.\n\t\tif (!this._id) {\n\t\t\tthis._messagesQueue.push(data);\n\t\t\treturn;\n\t\t}\n\n\t\tif (!data.type) {\n\t\t\tthis.emit(SocketEventType.Error, \"Invalid message\");\n\t\t\treturn;\n\t\t}\n\n\t\tif (!this._wsOpen()) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst message = JSON.stringify(data);\n\n\t\tthis._socket!.send(message);\n\t}\n\n\tclose(): void {\n\t\tif (this._disconnected) {\n\t\t\treturn;\n\t\t}\n\n\t\tthis._cleanup();\n\n\t\tthis._disconnected = true;\n\t}\n\n\tprivate _cleanup(): void {\n\t\tif (this._socket) {\n\t\t\tthis._socket.onopen =\n\t\t\t\tthis._socket.onmessage =\n\t\t\t\tthis._socket.onclose =\n\t\t\t\t\tnull;\n\t\t\tthis._socket.close();\n\t\t\tthis._socket = undefined;\n\t\t}\n\n\t\tclearTimeout(this._wsPingTimer!);\n\t}\n}\n"
  },
  {
    "path": "lib/supports.ts",
    "content": "import webRTCAdapter_import from \"webrtc-adapter\";\n\nconst webRTCAdapter: typeof webRTCAdapter_import =\n\t//@ts-ignore\n\twebRTCAdapter_import.default || webRTCAdapter_import;\n\nexport const Supports = new (class {\n\treadonly isIOS =\n\t\ttypeof navigator !== \"undefined\"\n\t\t\t? [\"iPad\", \"iPhone\", \"iPod\"].includes(navigator.platform)\n\t\t\t: false;\n\treadonly supportedBrowsers = [\"firefox\", \"chrome\", \"safari\"];\n\n\treadonly minFirefoxVersion = 59;\n\treadonly minChromeVersion = 72;\n\treadonly minSafariVersion = 605;\n\n\tisWebRTCSupported(): boolean {\n\t\treturn typeof RTCPeerConnection !== \"undefined\";\n\t}\n\n\tisBrowserSupported(): boolean {\n\t\tconst browser = this.getBrowser();\n\t\tconst version = this.getVersion();\n\n\t\tconst validBrowser = this.supportedBrowsers.includes(browser);\n\n\t\tif (!validBrowser) return false;\n\n\t\tif (browser === \"chrome\") return version >= this.minChromeVersion;\n\t\tif (browser === \"firefox\") return version >= this.minFirefoxVersion;\n\t\tif (browser === \"safari\")\n\t\t\treturn !this.isIOS && version >= this.minSafariVersion;\n\n\t\treturn false;\n\t}\n\n\tgetBrowser(): string {\n\t\treturn webRTCAdapter.browserDetails.browser;\n\t}\n\n\tgetVersion(): number {\n\t\treturn webRTCAdapter.browserDetails.version || 0;\n\t}\n\n\tisUnifiedPlanSupported(): boolean {\n\t\tconst browser = this.getBrowser();\n\t\tconst version = webRTCAdapter.browserDetails.version || 0;\n\n\t\tif (browser === \"chrome\" && version < this.minChromeVersion) return false;\n\t\tif (browser === \"firefox\" && version >= this.minFirefoxVersion) return true;\n\t\tif (\n\t\t\t!window.RTCRtpTransceiver ||\n\t\t\t!(\"currentDirection\" in RTCRtpTransceiver.prototype)\n\t\t)\n\t\t\treturn false;\n\n\t\tlet tempPc: RTCPeerConnection;\n\t\tlet supported = false;\n\n\t\ttry {\n\t\t\ttempPc = new RTCPeerConnection();\n\t\t\ttempPc.addTransceiver(\"audio\");\n\t\t\tsupported = true;\n\t\t} catch (e) {\n\t\t} finally {\n\t\t\tif (tempPc) {\n\t\t\t\ttempPc.close();\n\t\t\t}\n\t\t}\n\n\t\treturn supported;\n\t}\n\n\ttoString(): string {\n\t\treturn `Supports:\n    browser:${this.getBrowser()}\n    version:${this.getVersion()}\n    isIOS:${this.isIOS}\n    isWebRTCSupported:${this.isWebRTCSupported()}\n    isBrowserSupported:${this.isBrowserSupported()}\n    isUnifiedPlanSupported:${this.isUnifiedPlanSupported()}`;\n\t}\n})();\n"
  },
  {
    "path": "lib/util.ts",
    "content": "import { BinaryPackChunker } from \"./dataconnection/BufferedConnection/binaryPackChunker\";\nimport * as BinaryPack from \"peerjs-js-binarypack\";\nimport { Supports } from \"./supports\";\nimport { validateId } from \"./utils/validateId\";\nimport { randomToken } from \"./utils/randomToken\";\n\nexport interface UtilSupportsObj {\n\t/**\n\t * The current browser.\n\t * This property can be useful in determining whether two peers can connect.\n\t *\n\t * ```ts\n\t * if (util.browser === 'firefox') {\n\t *  // OK to peer with Firefox peers.\n\t * }\n\t * ```\n\t *\n\t * `util.browser` can currently have the values\n\t * `'firefox', 'chrome', 'safari', 'edge', 'Not a supported browser.', 'Not a browser.' (unknown WebRTC-compatible agent).\n\t */\n\tbrowser: boolean;\n\twebRTC: boolean;\n\t/**\n\t * True if the current browser supports media streams and PeerConnection.\n\t */\n\taudioVideo: boolean;\n\t/**\n\t * True if the current browser supports DataChannel and PeerConnection.\n\t */\n\tdata: boolean;\n\tbinaryBlob: boolean;\n\t/**\n\t * True if the current browser supports reliable DataChannels.\n\t */\n\treliable: boolean;\n}\n\nconst DEFAULT_CONFIG = {\n\ticeServers: [\n\t\t{ urls: \"stun:stun.l.google.com:19302\" },\n\t\t{\n\t\t\turls: [\n\t\t\t\t\"turn:eu-0.turn.peerjs.com:3478\",\n\t\t\t\t\"turn:us-0.turn.peerjs.com:3478\",\n\t\t\t],\n\t\t\tusername: \"peerjs\",\n\t\t\tcredential: \"peerjsp\",\n\t\t},\n\t],\n\tsdpSemantics: \"unified-plan\",\n};\n\nexport class Util extends BinaryPackChunker {\n\tnoop(): void {}\n\n\treadonly CLOUD_HOST = \"0.peerjs.com\";\n\treadonly CLOUD_PORT = 443;\n\n\t// Browsers that need chunking:\n\treadonly chunkedBrowsers = { Chrome: 1, chrome: 1 };\n\n\t// Returns browser-agnostic default config\n\treadonly defaultConfig = DEFAULT_CONFIG;\n\n\treadonly browser = Supports.getBrowser();\n\treadonly browserVersion = Supports.getVersion();\n\n\tpack = BinaryPack.pack;\n\tunpack = BinaryPack.unpack;\n\n\t/**\n\t * A hash of WebRTC features mapped to booleans that correspond to whether the feature is supported by the current browser.\n\t *\n\t * :::caution\n\t * Only the properties documented here are guaranteed to be present on `util.supports`\n\t * :::\n\t */\n\treadonly supports = (function () {\n\t\tconst supported: UtilSupportsObj = {\n\t\t\tbrowser: Supports.isBrowserSupported(),\n\t\t\twebRTC: Supports.isWebRTCSupported(),\n\t\t\taudioVideo: false,\n\t\t\tdata: false,\n\t\t\tbinaryBlob: false,\n\t\t\treliable: false,\n\t\t};\n\n\t\tif (!supported.webRTC) return supported;\n\n\t\tlet pc: RTCPeerConnection;\n\n\t\ttry {\n\t\t\tpc = new RTCPeerConnection(DEFAULT_CONFIG);\n\n\t\t\tsupported.audioVideo = true;\n\n\t\t\tlet dc: RTCDataChannel;\n\n\t\t\ttry {\n\t\t\t\tdc = pc.createDataChannel(\"_PEERJSTEST\", { ordered: true });\n\t\t\t\tsupported.data = true;\n\t\t\t\tsupported.reliable = !!dc.ordered;\n\n\t\t\t\t// Binary test\n\t\t\t\ttry {\n\t\t\t\t\tdc.binaryType = \"blob\";\n\t\t\t\t\tsupported.binaryBlob = !Supports.isIOS;\n\t\t\t\t} catch (e) {}\n\t\t\t} catch (e) {\n\t\t\t} finally {\n\t\t\t\tif (dc) {\n\t\t\t\t\tdc.close();\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (e) {\n\t\t} finally {\n\t\t\tif (pc) {\n\t\t\t\tpc.close();\n\t\t\t}\n\t\t}\n\n\t\treturn supported;\n\t})();\n\n\t// Ensure alphanumeric ids\n\tvalidateId = validateId;\n\trandomToken = randomToken;\n\n\tblobToArrayBuffer(\n\t\tblob: Blob,\n\t\tcb: (arg: ArrayBuffer | null) => void,\n\t): FileReader {\n\t\tconst fr = new FileReader();\n\n\t\tfr.onload = function (evt) {\n\t\t\tif (evt.target) {\n\t\t\t\tcb(evt.target.result as ArrayBuffer);\n\t\t\t}\n\t\t};\n\n\t\tfr.readAsArrayBuffer(blob);\n\n\t\treturn fr;\n\t}\n\n\tbinaryStringToArrayBuffer(binary: string): ArrayBuffer | SharedArrayBuffer {\n\t\tconst byteArray = new Uint8Array(binary.length);\n\n\t\tfor (let i = 0; i < binary.length; i++) {\n\t\t\tbyteArray[i] = binary.charCodeAt(i) & 0xff;\n\t\t}\n\n\t\treturn byteArray.buffer;\n\t}\n\tisSecure(): boolean {\n\t\treturn location.protocol === \"https:\";\n\t}\n}\n\n/**\n * Provides a variety of helpful utilities.\n *\n * :::caution\n * Only the utilities documented here are guaranteed to be present on `util`.\n * Undocumented utilities can be removed without warning.\n * We don't consider these to be breaking changes.\n * :::\n */\nexport const util = new Util();\n"
  },
  {
    "path": "lib/utils/randomToken.ts",
    "content": "export const randomToken = () => Math.random().toString(36).slice(2);\n"
  },
  {
    "path": "lib/utils/validateId.ts",
    "content": "export const validateId = (id: string): boolean => {\n\t// Allow empty ids\n\treturn !id || /^[A-Za-z0-9]+(?:[ _-][A-Za-z0-9]+)*$/.test(id);\n};\n"
  },
  {
    "path": "lib/version.ts",
    "content": "export const version = \"1.5.4\";\n"
  },
  {
    "path": "package.json",
    "content": "{\n\t\"name\": \"peerjs\",\n\t\"version\": \"1.5.5\",\n\t\"keywords\": [\n\t\t\"peerjs\",\n\t\t\"webrtc\",\n\t\t\"p2p\",\n\t\t\"rtc\"\n\t],\n\t\"description\": \"PeerJS client\",\n\t\"homepage\": \"https://peerjs.com\",\n\t\"bugs\": {\n\t\t\"url\": \"https://github.com/peers/peerjs/issues\"\n\t},\n\t\"repository\": {\n\t\t\"type\": \"git\",\n\t\t\"url\": \"https://github.com/peers/peerjs\"\n\t},\n\t\"license\": \"MIT\",\n\t\"contributors\": [\n\t\t\"Michelle Bu <michelle@michellebu.com>\",\n\t\t\"afrokick <devbyru@gmail.com>\",\n\t\t\"ericz <really.ez@gmail.com>\",\n\t\t\"Jairo <kidandcat@gmail.com>\",\n\t\t\"Jonas Gloning <34194370+jonasgloning@users.noreply.github.com>\",\n\t\t\"Jairo Caro-Accino Viciana <jairo@galax.be>\",\n\t\t\"Carlos Caballero <carlos.caballero.gonzalez@gmail.com>\",\n\t\t\"hc <hheennrryy@gmail.com>\",\n\t\t\"Muhammad Asif <capripio@gmail.com>\",\n\t\t\"PrashoonB <prashoonbhattacharjee@gmail.com>\",\n\t\t\"Harsh Bardhan Mishra <47351025+HarshCasper@users.noreply.github.com>\",\n\t\t\"akotynski <aleksanderkotbury@gmail.com>\",\n\t\t\"lmb <i@lmb.io>\",\n\t\t\"Jairooo <jairocaro@msn.com>\",\n\t\t\"Moritz Stückler <moritz.stueckler@gmail.com>\",\n\t\t\"Simon <crydotsnakegithub@gmail.com>\",\n\t\t\"Denis Lukov <denismassters@gmail.com>\",\n\t\t\"Philipp Hancke <fippo@andyet.net>\",\n\t\t\"Hans Oksendahl <hansoksendahl@gmail.com>\",\n\t\t\"Jess <jessachandler@gmail.com>\",\n\t\t\"khankuan <khankuan@gmail.com>\",\n\t\t\"DUODVK <kurmanov.work@gmail.com>\",\n\t\t\"XiZhao <kwang1imsa@gmail.com>\",\n\t\t\"Matthias Lohr <matthias@lohr.me>\",\n\t\t\"=frank tree <=frnktrb@googlemail.com>\",\n\t\t\"Andre Eckardt <aeckardt@outlook.com>\",\n\t\t\"Chris Cowan <agentme49@gmail.com>\",\n\t\t\"Alex Chuev <alex@chuev.com>\",\n\t\t\"alxnull <alxnull@e.mail.de>\",\n\t\t\"Yemel Jardi <angel.jardi@gmail.com>\",\n\t\t\"Ben Parnell <benjaminparnell.94@gmail.com>\",\n\t\t\"Benny Lichtner <bennlich@gmail.com>\",\n\t\t\"fresheneesz <bitetrudpublic@gmail.com>\",\n\t\t\"bob.barstead@exaptive.com <bob.barstead@exaptive.com>\",\n\t\t\"chandika <chandika@gmail.com>\",\n\t\t\"emersion <contact@emersion.fr>\",\n\t\t\"Christopher Van <cvan@users.noreply.github.com>\",\n\t\t\"eddieherm <edhermoso@gmail.com>\",\n\t\t\"Eduardo Pinho <enet4mikeenet@gmail.com>\",\n\t\t\"Evandro Zanatta <ezanatta@tray.net.br>\",\n\t\t\"Gardner Bickford <gardner@users.noreply.github.com>\",\n\t\t\"Gian Luca <gianluca.cecchi@cynny.com>\",\n\t\t\"PatrickJS <github@gdi2290.com>\",\n\t\t\"jonnyf <github@jonathanfoss.co.uk>\",\n\t\t\"Hizkia Felix <hizkifw@gmail.com>\",\n\t\t\"Hristo Oskov <hristo.oskov@gmail.com>\",\n\t\t\"Isaac Madwed <i.madwed@gmail.com>\",\n\t\t\"Ilya Konanykhin <ilya.konanykhin@gmail.com>\",\n\t\t\"jasonbarry <jasbarry@me.com>\",\n\t\t\"Jonathan Burke <jonathan.burke.1311@googlemail.com>\",\n\t\t\"Josh Hamit <josh.hamit@gmail.com>\",\n\t\t\"Jordan Austin <jrax86@gmail.com>\",\n\t\t\"Joel Wetzell <jwetzell@yahoo.com>\",\n\t\t\"xizhao <kevin.wang@cloudera.com>\",\n\t\t\"Alberto Torres <kungfoobar@gmail.com>\",\n\t\t\"Jonathan Mayol <mayoljonathan@gmail.com>\",\n\t\t\"Jefferson Felix <me@jsfelix.dev>\",\n\t\t\"Rolf Erik Lekang <me@rolflekang.com>\",\n\t\t\"Kevin Mai-Husan Chia <mhchia@users.noreply.github.com>\",\n\t\t\"Pepijn de Vos <pepijndevos@gmail.com>\",\n\t\t\"JooYoung <qkdlql@naver.com>\",\n\t\t\"Tobias Speicher <rootcommander@gmail.com>\",\n\t\t\"Steve Blaurock <sblaurock@gmail.com>\",\n\t\t\"Kyrylo Shegeda <shegeda@ualberta.ca>\",\n\t\t\"Diwank Singh Tomer <singh@diwank.name>\",\n\t\t\"Sören Balko <Soeren.Balko@gmail.com>\",\n\t\t\"Arpit Solanki <solankiarpit1997@gmail.com>\",\n\t\t\"Yuki Ito <yuki@gnnk.net>\",\n\t\t\"Artur Zayats <zag2art@gmail.com>\"\n\t],\n\t\"funding\": {\n\t\t\"type\": \"opencollective\",\n\t\t\"url\": \"https://opencollective.com/peer\"\n\t},\n\t\"collective\": {\n\t\t\"type\": \"opencollective\",\n\t\t\"url\": \"https://opencollective.com/peer\"\n\t},\n\t\"files\": [\n\t\t\"dist/*\"\n\t],\n\t\"sideEffects\": [\n\t\t\"lib/global.ts\",\n\t\t\"lib/supports.ts\"\n\t],\n\t\"main\": \"dist/bundler.cjs\",\n\t\"module\": \"dist/bundler.mjs\",\n\t\"browser-minified\": \"dist/peerjs.min.js\",\n\t\"browser-unminified\": \"dist/peerjs.js\",\n\t\"browser-minified-msgpack\": \"dist/serializer.msgpack.mjs\",\n\t\"types\": \"dist/types.d.ts\",\n\t\"engines\": {\n\t\t\"node\": \">= 14\"\n\t},\n\t\"targets\": {\n\t\t\"types\": {\n\t\t\t\"source\": \"lib/exports.ts\"\n\t\t},\n\t\t\"main\": {\n\t\t\t\"source\": \"lib/exports.ts\",\n\t\t\t\"sourceMap\": {\n\t\t\t\t\"inlineSources\": true\n\t\t\t}\n\t\t},\n\t\t\"module\": {\n\t\t\t\"source\": \"lib/exports.ts\",\n\t\t\t\"includeNodeModules\": [\n\t\t\t\t\"eventemitter3\"\n\t\t\t],\n\t\t\t\"sourceMap\": {\n\t\t\t\t\"inlineSources\": true\n\t\t\t}\n\t\t},\n\t\t\"browser-minified\": {\n\t\t\t\"context\": \"browser\",\n\t\t\t\"outputFormat\": \"global\",\n\t\t\t\"optimize\": true,\n\t\t\t\"engines\": {\n\t\t\t\t\"browsers\": \"chrome >= 83, edge >= 83, firefox >= 80, safari >= 15\"\n\t\t\t},\n\t\t\t\"source\": \"lib/global.ts\"\n\t\t},\n\t\t\"browser-unminified\": {\n\t\t\t\"context\": \"browser\",\n\t\t\t\"outputFormat\": \"global\",\n\t\t\t\"optimize\": false,\n\t\t\t\"engines\": {\n\t\t\t\t\"browsers\": \"chrome >= 83, edge >= 83, firefox >= 80, safari >= 15\"\n\t\t\t},\n\t\t\t\"source\": \"lib/global.ts\"\n\t\t},\n\t\t\"browser-minified-msgpack\": {\n\t\t\t\"context\": \"browser\",\n\t\t\t\"outputFormat\": \"esmodule\",\n\t\t\t\"isLibrary\": true,\n\t\t\t\"optimize\": true,\n\t\t\t\"engines\": {\n\t\t\t\t\"browsers\": \"chrome >= 83, edge >= 83, firefox >= 102, safari >= 15\"\n\t\t\t},\n\t\t\t\"source\": \"lib/dataconnection/StreamConnection/MsgPack.ts\"\n\t\t}\n\t},\n\t\"scripts\": {\n\t\t\"contributors\": \"git-authors-cli --print=false && prettier --write package.json && git add package.json package-lock.json && git commit -m \\\"chore(contributors): update and sort contributors list\\\"\",\n\t\t\"check\": \"tsc --noEmit && tsc -p e2e/tsconfig.json --noEmit\",\n\t\t\"watch\": \"parcel watch\",\n\t\t\"build\": \"npm run build:version && rm -rf dist && parcel build\",\n\t\t\"build:version\": \"echo \\\"export const version = \\\\\\\"$(node -p \\\"require('./package.json').version\\\")\\\\\\\";\\\" > lib/version.ts\",\n\t\t\"prepublishOnly\": \"npm run build\",\n\t\t\"test\": \"jest\",\n\t\t\"test:watch\": \"jest --watch\",\n\t\t\"coverage\": \"jest --coverage --collectCoverageFrom=\\\"./lib/**\\\"\",\n\t\t\"format\": \"prettier --write .\",\n\t\t\"format:check\": \"prettier --check .\",\n\t\t\"semantic-release\": \"semantic-release\",\n\t\t\"e2e\": \"wdio run e2e/wdio.local.conf.ts\",\n\t\t\"e2e:bstack\": \"wdio run e2e/wdio.bstack.conf.ts\"\n\t},\n\t\"devDependencies\": {\n\t\t\"@parcel/config-default\": \"^2.9.3\",\n\t\t\"@parcel/packager-ts\": \"^2.9.3\",\n\t\t\"@parcel/transformer-typescript-tsc\": \"^2.9.3\",\n\t\t\"@parcel/transformer-typescript-types\": \"^2.9.3\",\n\t\t\"@semantic-release/changelog\": \"^6.0.1\",\n\t\t\"@semantic-release/git\": \"^10.0.1\",\n\t\t\"@swc/core\": \"^1.3.27\",\n\t\t\"@swc/jest\": \"^0.2.24\",\n\t\t\"@types/jasmine\": \"^5.0.0\",\n\t\t\"@wdio/browserstack-service\": \"^8.11.2\",\n\t\t\"@wdio/cli\": \"^8.11.2\",\n\t\t\"@wdio/globals\": \"^8.11.2\",\n\t\t\"@wdio/jasmine-framework\": \"^8.11.2\",\n\t\t\"@wdio/local-runner\": \"^8.11.2\",\n\t\t\"@wdio/spec-reporter\": \"^8.11.2\",\n\t\t\"@wdio/types\": \"^8.10.4\",\n\t\t\"http-server\": \"^14.1.1\",\n\t\t\"jest\": \"^29.3.1\",\n\t\t\"jest-environment-jsdom\": \"^29.3.1\",\n\t\t\"mock-socket\": \"^9.0.0\",\n\t\t\"parcel\": \"^2.9.3\",\n\t\t\"prettier\": \"^3.0.0\",\n\t\t\"semantic-release\": \"^23.0.0\",\n\t\t\"ts-node\": \"^10.9.1\",\n\t\t\"typescript\": \"^5.0.0\",\n\t\t\"wdio-geckodriver-service\": \"^5.0.1\"\n\t},\n\t\"dependencies\": {\n\t\t\"@msgpack/msgpack\": \"^2.8.0\",\n\t\t\"eventemitter3\": \"^4.0.7\",\n\t\t\"peerjs-js-binarypack\": \"^2.1.0\",\n\t\t\"webrtc-adapter\": \"^9.0.0\"\n\t},\n\t\"alias\": {\n\t\t\"process\": false,\n\t\t\"buffer\": false\n\t}\n}\n"
  },
  {
    "path": "renovate.json",
    "content": "{\n\t\"$schema\": \"https://docs.renovatebot.com/renovate-schema.json\",\n\t\"extends\": [\"config:recommended\", \":assignAndReview(jonasgloning)\"],\n\t\"labels\": [\"dependencies\"],\n\t\"assignees\": [\"jonasgloning\"],\n\t\"major\": {\n\t\t\"dependencyDashboardApproval\": true\n\t},\n\t\"packageRules\": [\n\t\t{\n\t\t\t\"matchDepTypes\": [\"devDependencies\"],\n\t\t\t\"addLabels\": [\"dev-dependencies\"],\n\t\t\t\"automerge\": true,\n\t\t\t\"automergeType\": \"branch\"\n\t\t},\n\t\t{\n\t\t\t\"matchUpdateTypes\": [\"minor\", \"patch\"],\n\t\t\t\"matchCurrentVersion\": \"!/^0/\",\n\t\t\t\"automerge\": true,\n\t\t\t\"automergeType\": \"branch\"\n\t\t}\n\t],\n\t\"lockFileMaintenance\": {\n\t\t\"enabled\": true,\n\t\t\"automerge\": true,\n\t\t\"automergeType\": \"branch\"\n\t}\n}\n"
  },
  {
    "path": "tsconfig.json",
    "content": "{\n\t\"compilerOptions\": {\n\t\t\"target\": \"es5\",\n\t\t\"module\": \"commonjs\",\n\t\t\"downlevelIteration\": true,\n\t\t\"noUnusedLocals\": true,\n\t\t\"noUnusedParameters\": true,\n\t\t\"skipLibCheck\": true,\n\t\t\"isolatedModules\": true,\n\t\t\"resolveJsonModule\": true,\n\t\t\"lib\": [\"es2020\", \"dom\"],\n\t\t\"paths\": {\n\t\t\t\"cbor-x/index-no-eval\": [\"./node_modules/cbor-x\"]\n\t\t}\n\t},\n\t\"exclude\": [\"node_modules\", \"dist\", \"e2e\"]\n}\n"
  }
]