diff --git a/.eslintrc.json b/.eslintrc.json deleted file mode 100644 index 9e37304..0000000 --- a/.eslintrc.json +++ /dev/null @@ -1,40 +0,0 @@ -{ - "env": { - "node": true, - "browser": false, - "es2021": true - }, - "extends": "eslint:recommended", - "parserOptions": { - "sourceType": "module", - "ecmaVersion": "latest" - }, - "rules": { - "no-unused-vars": [ - "warn", - { - "vars": "all", - "args": "all", - "argsIgnorePattern": "^_", - "varsIgnorePattern": "^_" - } - ], - "indent": [ - "warn", - 2, - { - "SwitchCase": 1 - } - ], - "quotes": [ - "warn", - "single" - ], - "semi": [ - "warn", - "always" - ], - "no-empty": "off", - "multiline-comment-style": 1 - } -} \ No newline at end of file diff --git a/.github/workflows/janode-ci.yml b/.github/workflows/janode-ci.yml deleted file mode 100644 index 49411b5..0000000 --- a/.github/workflows/janode-ci.yml +++ /dev/null @@ -1,19 +0,0 @@ -name: janode-ci - -on: - push: - branches: [ master ] - pull_request: - branches: [ master ] -permissions: - contents: read -jobs: - lint: - runs-on: ubuntu-22.04 - steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 - with: - node-version: 16 - - run: npm ci - - run: npm run lint diff --git a/.github/workflows/janode-docs.yml b/.github/workflows/janode-docs.yml new file mode 100644 index 0000000..1dac577 --- /dev/null +++ b/.github/workflows/janode-docs.yml @@ -0,0 +1,39 @@ +name: Deploy to GitHub Pages + +on: + push: + branches: + - master + - test-docs + tags: + - v1.* +jobs: + build-and-deploy: + permissions: + contents: write + concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + runs-on: ubuntu-24.04 + env: + REF_NAME: ${{ github.ref_name }} + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: lts/jod + - run: npm ci + - run: npm run build-docs + - name: Add git reference to docs path + run: | + mkdir -p site/docs/$REF_NAME + cp -a docs/. site/docs/$REF_NAME + - name: Update "latest" docs for tagged versions + if: startsWith(github.ref, 'refs/tags/v') + run: | + cp -a docs/. site/docs/latest + - name: Deploy to GitHub Pages + uses: peaceiris/actions-gh-pages@v4 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + publish_dir: ./site + keep_files: true diff --git a/.github/workflows/janode-lint.yml b/.github/workflows/janode-lint.yml new file mode 100644 index 0000000..c8e46e2 --- /dev/null +++ b/.github/workflows/janode-lint.yml @@ -0,0 +1,22 @@ +name: Lint Janode code + +on: + push: + branches: + - master + - test-lint + pull_request: + branches: + - master +permissions: + contents: read +jobs: + build-and-lint: + runs-on: ubuntu-24.04 + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: lts/jod + - run: npm ci + - run: npm run lint diff --git a/.gitignore b/.gitignore index 3cd189d..67974c9 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,8 @@ node_modules build out +docs +.vscode config.js bundle.js @@ -12,3 +14,4 @@ bundle.js !.eslintrc.json !package.json !package-lock.json +!jsdoc.json diff --git a/README.md b/README.md index 884ceea..316ec01 100644 --- a/README.md +++ b/README.md @@ -6,15 +6,20 @@ Internally uses WebSockets or Unix DGRAM Sockets to connect to Janus. The library wraps the Janus core API, the Janus Admin API and some of the most popular plugins APIs. -The supported Janus plugins currently are: +The supported Janus plugins are: - EchoTest - AudioBridge - Streaming - VideoRoom +- SIP +- Record&Play +- TextRoom (Janus API only) The library is available on [npm](https://www.npmjs.com/package/janode) and the source code is on [github](https://github.com/meetecho/janode). +Online documentation can be found at [https://meetecho.github.io/janode](https://meetecho.github.io/janode). + ## Example of usage This is just a pretty simple hello world for the echotest plugin. @@ -141,3 +146,19 @@ You need to create a bundle with the core library and the needed plugins using a If you get the code from the repo, you can find a `rollup` bundling sample in the `bundle.sh` script under `examples/browser/`. The output will be a `bundle.js` script that defines an `App` global object with the members `Janode` and `EchoTestPlugin`. + +## How to build documentation + +First install the dev dependecies: + +```bash +npm install +``` + +Then use the npm script: + +```bash +npm run build-docs +``` + +Documentation in HTML format will be built under the `docs` folder. diff --git a/eslint.config.js b/eslint.config.js new file mode 100644 index 0000000..4def16b --- /dev/null +++ b/eslint.config.js @@ -0,0 +1,60 @@ +import { defineConfig } from 'eslint/config'; +import js from '@eslint/js'; +import globals from 'globals'; + +export default defineConfig([ + { + files: [ + '*.js', + 'src/**/*.js', + 'examples/**/*.js' + ], + ignores: [ + 'examples/browser/**/*' + ], + plugins: { + js + }, + extends: ['js/recommended'], + languageOptions: { + ecmaVersion: 'latest', + sourceType: 'module', + globals: { + ...globals.browser, + ...globals.node + } + }, + rules: { + 'no-unused-vars': [ + 'warn', + { + 'args': 'all', + 'vars': 'all', + 'caughtErrors': 'all', + 'argsIgnorePattern': '^_', + 'varsIgnorePattern': '^_', + 'caughtErrorsIgnorePattern': '^_' + } + ], + 'no-empty': 'off', + // deprecated rules + 'no-trailing-spaces': 'warn', + 'indent': [ + 'warn', + 2, + { + 'SwitchCase': 1 + } + ], + 'quotes': [ + 'warn', + 'single' + ], + 'semi': [ + 'warn', + 'always' + ], + 'multiline-comment-style': 1 + } + } +]); \ No newline at end of file diff --git a/examples/audiobridge/html/.eslintrc.json b/examples/audiobridge/html/.eslintrc.json deleted file mode 100644 index 5c3f02e..0000000 --- a/examples/audiobridge/html/.eslintrc.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "env": { - "browser": true - } -} \ No newline at end of file diff --git a/examples/audiobridge/html/audiobridge-client.js b/examples/audiobridge/html/audiobridge-client.js index 7601d84..d54f81f 100644 --- a/examples/audiobridge/html/audiobridge-client.js +++ b/examples/audiobridge/html/audiobridge-client.js @@ -1,4 +1,3 @@ -/* eslint-disable no-sparse-arrays */ /* global io */ 'use strict'; @@ -10,6 +9,7 @@ let pendingOfferMap = new Map(); const myRoom = getURLParameter('room') ? parseInt(getURLParameter('room')) : (getURLParameter('room_str') || 1234); const randName = ('John_Doe_' + Math.floor(10000 * Math.random())); const myName = getURLParameter('name') || randName; +let myFeed; const skipJoin = getURLParameter('skipjoin') || false; const skipOffer = getURLParameter('skipoffer') || false; @@ -26,6 +26,7 @@ function getId() { } function getURLParameter(name) { + // eslint-disable-next-line no-sparse-arrays return decodeURIComponent((new RegExp('[?|&]' + name + '=' + '([^&;]+?)(&|#|;|$)').exec(location.search) || [, ''])[1].replace(/\+/g, '%20')) || null; } @@ -50,11 +51,12 @@ const socket = io({ reconnection: false, }); -function join({ room = myRoom, display = myName, muted = false, token = null, rtp_participant = null, group = null } = {}) { +function join({ room = myRoom, display = myName, muted = false, suspended = false, token = null, rtp_participant = null, group = null } = {}) { const joinData = { room, display, muted, + suspended, token, rtp_participant, group, @@ -251,6 +253,32 @@ function _listForward({ room = myRoom, secret = 'adminpwd' } = {}) { }); } +function _mutePeer({ room = myRoom, feed = myFeed, secret = 'adminpwd' } = {}) { + let muteData = { + room, + feed, + secret, + }; + + socket.emit('mute-peer', { + data: muteData, + _id: getId(), + }); +} + +function _unmutePeer({ room = myRoom, feed = myFeed, secret = 'adminpwd' } = {}) { + let unmuteData = { + room, + feed, + secret, + }; + + socket.emit('unmute-peer', { + data: unmuteData, + _id: getId(), + }); +} + function _muteRoom({ room = myRoom, secret = 'adminpwd' } = {}) { let muteData = { room, @@ -275,6 +303,35 @@ function _unmuteRoom({ room = myRoom, secret = 'adminpwd' } = {}) { }); } +function _suspend({ room = myRoom, feed = myFeed, stop_record = false, secret = 'adminpwd' } = {}) { + let suspendData = { + room, + feed, + stop_record, + secret, + }; + + socket.emit('suspend-peer', { + data: suspendData, + _id: getId(), + }); +} + +function _resume({ room = myRoom, feed = myFeed, record = false, filename, secret = 'adminpwd' } = {}) { + let resumeData = { + room, + feed, + record, + secret, + }; + if (filename) resumeData.filename = filename; + + socket.emit('resume-peer', { + data: resumeData, + _id: getId(), + }); +} + socket.on('connect', () => { console.log('socket connected'); socket.sendBuffer = []; @@ -305,6 +362,7 @@ socket.on('audiobridge-error', ({ error, _id }) => { socket.on('joined', async ({ data }) => { console.log('you have joined to room', data); + myFeed = data.feed; removeAllAudioElements(); closePC(); setAudioElement(null, data.feed, data.display, data.room); @@ -381,6 +439,14 @@ socket.on('peer-talking', ({ data }) => { console.log('peer talking notify', data); }); +socket.on('peer-suspended', ({ data }) => { + console.log('peer suspended notify', data); +}); + +socket.on('peer-resumed', ({ data }) => { + console.log('peer resumed notify', data); +}); + socket.on('exists', ({ data }) => { console.log('room exists', data); }); @@ -420,6 +486,14 @@ socket.on('rtp-fwd-list', ({ data }) => { console.log('rtp forwarders list', data); }); +socket.on('peer-muted', ({ data }) => { + console.log('peer muted', data); +}); + +socket.on('peer-unmuted', ({ data }) => { + console.log('peer unmuted', data); +}); + socket.on('room-muted', ({ data }) => { console.log('room muted', data); }); diff --git a/examples/audiobridge/package-lock.json b/examples/audiobridge/package-lock.json index 889a707..a7e0ca7 100644 --- a/examples/audiobridge/package-lock.json +++ b/examples/audiobridge/package-lock.json @@ -1,6 +1,6 @@ { "name": "janode-audiobridge", - "lockfileVersion": 2, + "lockfileVersion": 3, "requires": true, "packages": { "": { @@ -11,33 +11,44 @@ "socket.io": "^4.2.0" } }, - "node_modules/@types/component-emitter": { - "version": "1.2.11", - "resolved": "https://registry.npmjs.org/@types/component-emitter/-/component-emitter-1.2.11.tgz", - "integrity": "sha512-SRXjM+tfsSlA9VuG8hGO2nft2p8zjXCK1VcC6N4NXbBbYbSia9kzCChYQajIjzIqOOOuh5Ock6MmV2oux4jDZQ==" + "node_modules/@socket.io/component-emitter": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz", + "integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==", + "license": "MIT" }, "node_modules/@types/cookie": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz", - "integrity": "sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==" + "integrity": "sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==", + "license": "MIT" }, "node_modules/@types/cors": { - "version": "2.8.12", - "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.12.tgz", - "integrity": "sha512-vt+kDhq/M2ayberEtJcIN/hxXy1Pk+59g2FV/ZQceeaTyCtCucjL2Q7FXlFjtWn4n15KCr1NE2lNNFhp0lEThw==" + "version": "2.8.17", + "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.17.tgz", + "integrity": "sha512-8CGDvrBj1zgo2qE+oS3pOCyYNqCPryMWY2bGfwA0dcfopWGgxs+78df0Rs3rc9THP4JkOhLsAa+15VdpAqkcUA==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } }, "node_modules/@types/node": { - "version": "16.11.10", - "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.10.tgz", - "integrity": "sha512-3aRnHa1KlOEEhJ6+CvyHKK5vE9BcLGjtUpwvqYLRvYNQKMfabu3BwfJaA/SLW8dxe28LsNDjtHwePTuzn3gmOA==" + "version": "22.10.7", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.7.tgz", + "integrity": "sha512-V09KvXxFiutGp6B7XkpaDXlNadZxrzajcY50EuoLIpQ6WWYCSvf19lVIazzfIzQvhUN2HjX12spLojTnhuKlGg==", + "license": "MIT", + "dependencies": { + "undici-types": "~6.20.0" + } }, "node_modules/accepts": { - "version": "1.3.7", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", - "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "license": "MIT", "dependencies": { - "mime-types": "~2.1.24", - "negotiator": "0.6.2" + "mime-types": "~2.1.34", + "negotiator": "0.6.3" }, "engines": { "node": ">= 0.6" @@ -46,72 +57,106 @@ "node_modules/array-flatten": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", + "license": "MIT" }, "node_modules/base64id": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==", + "license": "MIT", "engines": { "node": "^4.5.0 || >= 5.9" } }, "node_modules/body-parser": { - "version": "1.19.0", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", - "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", + "version": "1.20.3", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", + "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==", + "license": "MIT", "dependencies": { - "bytes": "3.1.0", - "content-type": "~1.0.4", + "bytes": "3.1.2", + "content-type": "~1.0.5", "debug": "2.6.9", - "depd": "~1.1.2", - "http-errors": "1.7.2", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", "iconv-lite": "0.4.24", - "on-finished": "~2.3.0", - "qs": "6.7.0", - "raw-body": "2.4.0", - "type-is": "~1.6.17" + "on-finished": "2.4.1", + "qs": "6.13.0", + "raw-body": "2.5.2", + "type-is": "~1.6.18", + "unpipe": "1.0.0" }, "engines": { - "node": ">= 0.8" + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" } }, "node_modules/bytes": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", - "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "license": "MIT", "engines": { "node": ">= 0.8" } }, - "node_modules/component-emitter": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", - "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==" + "node_modules/call-bind-apply-helpers": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.1.tgz", + "integrity": "sha512-BhYE+WDaywFg2TBWYNXAE+8B1ATnThNBqXHP5nQu0jWJdVvY2hvkpyB3qOmtmDePiS5/BDQ8wASEWGMWRG148g==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.3.tgz", + "integrity": "sha512-YTd+6wGlNlPxSuri7Y6X8tY2dmm12UMH66RpKMhiX6rsk5wXXnYgbUcOt8kiS31/AjfoTOvCsE+w8nZQLQnzHA==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, "node_modules/content-disposition": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", - "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "license": "MIT", "dependencies": { - "safe-buffer": "5.1.2" + "safe-buffer": "5.2.1" }, "engines": { "node": ">= 0.6" } }, "node_modules/content-type": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", - "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "license": "MIT", "engines": { "node": ">= 0.6" } }, "node_modules/cookie": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", - "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==", + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz", + "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==", + "license": "MIT", "engines": { "node": ">= 0.6" } @@ -119,12 +164,14 @@ "node_modules/cookie-signature": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==", + "license": "MIT" }, "node_modules/cors": { "version": "2.8.5", "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "license": "MIT", "dependencies": { "object-assign": "^4", "vary": "^1" @@ -137,78 +184,105 @@ "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", "dependencies": { "ms": "2.0.0" } }, "node_modules/depd": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", - "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "license": "MIT", "engines": { - "node": ">= 0.6" + "node": ">= 0.8" } }, "node_modules/destroy": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", - "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "license": "MIT", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } }, "node_modules/ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "license": "MIT" }, "node_modules/encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "license": "MIT", "engines": { "node": ">= 0.8" } }, "node_modules/engine.io": { - "version": "6.1.3", - "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.1.3.tgz", - "integrity": "sha512-rqs60YwkvWTLLnfazqgZqLa/aKo+9cueVfEi/dZ8PyGyaf8TLOxj++4QMIgeG3Gn0AhrWiFXvghsoY9L9h25GA==", + "version": "6.6.2", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.6.2.tgz", + "integrity": "sha512-gmNvsYi9C8iErnZdVcJnvCpSKbWTt1E8+JZo8b+daLninywUWi5NQ5STSHZ9rFjFO7imNcvb8Pc5pe/wMR5xEw==", + "license": "MIT", "dependencies": { "@types/cookie": "^0.4.1", "@types/cors": "^2.8.12", "@types/node": ">=10.0.0", "accepts": "~1.3.4", "base64id": "2.0.0", - "cookie": "~0.4.1", + "cookie": "~0.7.2", "cors": "~2.8.5", "debug": "~4.3.1", - "engine.io-parser": "~5.0.3", - "ws": "~8.2.3" + "engine.io-parser": "~5.2.1", + "ws": "~8.17.1" }, "engines": { - "node": ">=10.0.0" + "node": ">=10.2.0" } }, "node_modules/engine.io-parser": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.0.4.tgz", - "integrity": "sha512-+nVFp+5z1E3HcToEnO7ZIj3g+3k9389DvWtvJZz0T6/eOCPIyyxehFcedoYrZQrp0LgQbD9pPXhpMBKMd5QURg==", + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.3.tgz", + "integrity": "sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==", + "license": "MIT", "engines": { "node": ">=10.0.0" } }, "node_modules/engine.io/node_modules/cookie": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz", - "integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==", + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "license": "MIT", "engines": { "node": ">= 0.6" } }, "node_modules/engine.io/node_modules/debug": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", - "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "license": "MIT", "dependencies": { - "ms": "2.1.2" + "ms": "^2.1.3" }, "engines": { "node": ">=6.0" @@ -220,74 +294,114 @@ } }, "node_modules/engine.io/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } }, "node_modules/escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "license": "MIT" }, "node_modules/etag": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "license": "MIT", "engines": { "node": ">= 0.6" } }, "node_modules/express": { - "version": "4.17.1", - "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", - "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", + "version": "4.21.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz", + "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==", + "license": "MIT", "dependencies": { - "accepts": "~1.3.7", + "accepts": "~1.3.8", "array-flatten": "1.1.1", - "body-parser": "1.19.0", - "content-disposition": "0.5.3", + "body-parser": "1.20.3", + "content-disposition": "0.5.4", "content-type": "~1.0.4", - "cookie": "0.4.0", + "cookie": "0.7.1", "cookie-signature": "1.0.6", "debug": "2.6.9", - "depd": "~1.1.2", - "encodeurl": "~1.0.2", + "depd": "2.0.0", + "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "etag": "~1.8.1", - "finalhandler": "~1.1.2", + "finalhandler": "1.3.1", "fresh": "0.5.2", - "merge-descriptors": "1.0.1", + "http-errors": "2.0.0", + "merge-descriptors": "1.0.3", "methods": "~1.1.2", - "on-finished": "~2.3.0", + "on-finished": "2.4.1", "parseurl": "~1.3.3", - "path-to-regexp": "0.1.7", - "proxy-addr": "~2.0.5", - "qs": "6.7.0", + "path-to-regexp": "0.1.12", + "proxy-addr": "~2.0.7", + "qs": "6.13.0", "range-parser": "~1.2.1", - "safe-buffer": "5.1.2", - "send": "0.17.1", - "serve-static": "1.14.1", - "setprototypeof": "1.1.1", - "statuses": "~1.5.0", + "safe-buffer": "5.2.1", + "send": "0.19.0", + "serve-static": "1.16.2", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", "type-is": "~1.6.18", "utils-merge": "1.0.1", "vary": "~1.1.2" }, "engines": { "node": ">= 0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/finalhandler": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", - "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz", + "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==", + "license": "MIT", "dependencies": { "debug": "2.6.9", - "encodeurl": "~1.0.2", + "encodeurl": "~2.0.0", "escape-html": "~1.0.3", - "on-finished": "~2.3.0", + "on-finished": "2.4.1", "parseurl": "~1.3.3", - "statuses": "~1.5.0", + "statuses": "2.0.1", "unpipe": "~1.0.0" }, "engines": { @@ -298,6 +412,7 @@ "version": "0.2.0", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "license": "MIT", "engines": { "node": ">= 0.6" } @@ -305,30 +420,115 @@ "node_modules/fresh": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "license": "MIT", "engines": { "node": ">= 0.6" } }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-intrinsic": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.7.tgz", + "integrity": "sha512-VW6Pxhsrk0KAOqs3WEd0klDiF/+V7gQOpAvY1jVU/LHmaD/kQO4523aiJuikX/QAKYiW6x8Jh+RJej1almdtCA==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "function-bind": "^1.1.2", + "get-proto": "^1.0.0", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/http-errors": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", - "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "license": "MIT", "dependencies": { - "depd": "~1.1.2", - "inherits": "2.0.3", - "setprototypeof": "1.1.1", - "statuses": ">= 1.5.0 < 2", - "toidentifier": "1.0.0" + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" }, "engines": { - "node": ">= 0.6" + "node": ">= 0.8" } }, "node_modules/iconv-lite": { "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "license": "MIT", "dependencies": { "safer-buffer": ">= 2.1.2 < 3" }, @@ -337,35 +537,52 @@ } }, "node_modules/inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" }, "node_modules/ipaddr.js": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "license": "MIT", "engines": { "node": ">= 0.10" } }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "license": "MIT", "engines": { "node": ">= 0.6" } }, "node_modules/merge-descriptors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", - "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", + "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, "node_modules/methods": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "license": "MIT", "engines": { "node": ">= 0.6" } @@ -374,6 +591,7 @@ "version": "1.6.0", "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "license": "MIT", "bin": { "mime": "cli.js" }, @@ -382,19 +600,21 @@ } }, "node_modules/mime-db": { - "version": "1.51.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.51.0.tgz", - "integrity": "sha512-5y8A56jg7XVQx2mbv1lu49NR4dokRnhZYTtL+KGfaa27uq4pSTXkwQkFJl4pkRMyNFz/EtYDSkiiEHx3F7UN6g==", + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", "engines": { "node": ">= 0.6" } }, "node_modules/mime-types": { - "version": "2.1.34", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.34.tgz", - "integrity": "sha512-6cP692WwGIs9XXdOO4++N+7qjqv0rqxxVvJ3VHPh/Sc9mVZcQP+ZGhkKiTvWMQRr2tbHkJP/Yn7Y0npb3ZBs4A==", + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", "dependencies": { - "mime-db": "1.51.0" + "mime-db": "1.52.0" }, "engines": { "node": ">= 0.6" @@ -403,12 +623,14 @@ "node_modules/ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" }, "node_modules/negotiator": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", - "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==", + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "license": "MIT", "engines": { "node": ">= 0.6" } @@ -416,15 +638,29 @@ "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "license": "MIT", "engines": { "node": ">=0.10.0" } }, + "node_modules/object-inspect": { + "version": "1.13.3", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.3.tgz", + "integrity": "sha512-kDCGIbxkDSXE3euJZZXzc6to7fCrKHNI/hSRQnRuQ+BWjFNzZwiFF8fj/6o2t2G9/jTj8PSIYTfCLelLZEeRpA==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/on-finished": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", - "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "license": "MIT", "dependencies": { "ee-first": "1.1.1" }, @@ -436,19 +672,22 @@ "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "license": "MIT", "engines": { "node": ">= 0.8" } }, "node_modules/path-to-regexp": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", - "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz", + "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==", + "license": "MIT" }, "node_modules/proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "license": "MIT", "dependencies": { "forwarded": "0.2.0", "ipaddr.js": "1.9.1" @@ -458,28 +697,37 @@ } }, "node_modules/qs": { - "version": "6.7.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", - "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==", + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", + "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.0.6" + }, "engines": { "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/range-parser": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "license": "MIT", "engines": { "node": ">= 0.6" } }, "node_modules/raw-body": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", - "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", + "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", + "license": "MIT", "dependencies": { - "bytes": "3.1.0", - "http-errors": "1.7.2", + "bytes": "3.1.2", + "http-errors": "2.0.0", "iconv-lite": "0.4.24", "unpipe": "1.0.0" }, @@ -488,90 +736,221 @@ } }, "node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" }, "node_modules/safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" }, "node_modules/send": { - "version": "0.17.1", - "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", - "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==", + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz", + "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==", + "license": "MIT", "dependencies": { "debug": "2.6.9", - "depd": "~1.1.2", - "destroy": "~1.0.4", + "depd": "2.0.0", + "destroy": "1.2.0", "encodeurl": "~1.0.2", "escape-html": "~1.0.3", "etag": "~1.8.1", "fresh": "0.5.2", - "http-errors": "~1.7.2", + "http-errors": "2.0.0", "mime": "1.6.0", - "ms": "2.1.1", - "on-finished": "~2.3.0", + "ms": "2.1.3", + "on-finished": "2.4.1", "range-parser": "~1.2.1", - "statuses": "~1.5.0" + "statuses": "2.0.1" }, "engines": { "node": ">= 0.8.0" } }, + "node_modules/send/node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/send/node_modules/ms": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", - "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" }, "node_modules/serve-static": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", - "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==", + "version": "1.16.2", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz", + "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==", + "license": "MIT", "dependencies": { - "encodeurl": "~1.0.2", + "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "parseurl": "~1.3.3", - "send": "0.17.1" + "send": "0.19.0" }, "engines": { "node": ">= 0.8.0" } }, "node_modules/setprototypeof": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", - "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "license": "ISC" + }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, "node_modules/socket.io": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.4.0.tgz", - "integrity": "sha512-bnpJxswR9ov0Bw6ilhCvO38/1WPtE3eA2dtxi2Iq4/sFebiDJQzgKNYA7AuVVdGW09nrESXd90NbZqtDd9dzRQ==", + "version": "4.8.1", + "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.8.1.tgz", + "integrity": "sha512-oZ7iUCxph8WYRHHcjBEc9unw3adt5CmSNlppj/5Q4k2RIrhl8Z5yY2Xr4j9zj0+wzVZ0bxmYoGSzKJnRl6A4yg==", + "license": "MIT", "dependencies": { "accepts": "~1.3.4", "base64id": "~2.0.0", + "cors": "~2.8.5", "debug": "~4.3.2", - "engine.io": "~6.1.0", - "socket.io-adapter": "~2.3.3", - "socket.io-parser": "~4.0.4" + "engine.io": "~6.6.0", + "socket.io-adapter": "~2.5.2", + "socket.io-parser": "~4.2.4" }, "engines": { - "node": ">=10.0.0" + "node": ">=10.2.0" } }, "node_modules/socket.io-adapter": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.3.3.tgz", - "integrity": "sha512-Qd/iwn3VskrpNO60BeRyCyr8ZWw9CPZyitW4AQwmRZ8zCiyDiL+znRnWX6tDHXnWn1sJrM1+b6Mn6wEDJJ4aYQ==" + "version": "2.5.5", + "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.5.tgz", + "integrity": "sha512-eLDQas5dzPgOWCk9GuuJC2lBqItuhKI4uxGgo9aIV7MYbk2h9Q6uULEh8WBzThoI7l+qU9Ast9fVUmkqPP9wYg==", + "license": "MIT", + "dependencies": { + "debug": "~4.3.4", + "ws": "~8.17.1" + } + }, + "node_modules/socket.io-adapter/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/socket.io-adapter/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" }, "node_modules/socket.io-parser": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.0.4.tgz", - "integrity": "sha512-t+b0SS+IxG7Rxzda2EVvyBZbvFPBCjJoyHuE0P//7OAsN23GItzDRdWa6ALxZI/8R5ygK7jAR6t028/z+7295g==", + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz", + "integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==", + "license": "MIT", "dependencies": { - "@types/component-emitter": "^1.2.10", - "component-emitter": "~1.3.0", + "@socket.io/component-emitter": "~3.1.0", "debug": "~4.3.1" }, "engines": { @@ -579,11 +958,12 @@ } }, "node_modules/socket.io-parser/node_modules/debug": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", - "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "license": "MIT", "dependencies": { - "ms": "2.1.2" + "ms": "^2.1.3" }, "engines": { "node": ">=6.0" @@ -595,16 +975,18 @@ } }, "node_modules/socket.io-parser/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" }, "node_modules/socket.io/node_modules/debug": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", - "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "license": "MIT", "dependencies": { - "ms": "2.1.2" + "ms": "^2.1.3" }, "engines": { "node": ">=6.0" @@ -616,22 +998,25 @@ } }, "node_modules/socket.io/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" }, "node_modules/statuses": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", - "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "license": "MIT", "engines": { - "node": ">= 0.6" + "node": ">= 0.8" } }, "node_modules/toidentifier": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", - "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "license": "MIT", "engines": { "node": ">=0.6" } @@ -640,6 +1025,7 @@ "version": "1.6.18", "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "license": "MIT", "dependencies": { "media-typer": "0.3.0", "mime-types": "~2.1.24" @@ -648,10 +1034,17 @@ "node": ">= 0.6" } }, + "node_modules/undici-types": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", + "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", + "license": "MIT" + }, "node_modules/unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "license": "MIT", "engines": { "node": ">= 0.8" } @@ -659,7 +1052,8 @@ "node_modules/utils-merge": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "license": "MIT", "engines": { "node": ">= 0.4.0" } @@ -667,21 +1061,23 @@ "node_modules/vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "license": "MIT", "engines": { "node": ">= 0.8" } }, "node_modules/ws": { - "version": "8.2.3", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.2.3.tgz", - "integrity": "sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA==", + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", + "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", + "license": "MIT", "engines": { "node": ">=10.0.0" }, "peerDependencies": { "bufferutil": "^4.0.1", - "utf-8-validate": "^5.0.2" + "utf-8-validate": ">=5.0.2" }, "peerDependenciesMeta": { "bufferutil": { @@ -692,523 +1088,5 @@ } } } - }, - "dependencies": { - "@types/component-emitter": { - "version": "1.2.11", - "resolved": "https://registry.npmjs.org/@types/component-emitter/-/component-emitter-1.2.11.tgz", - "integrity": "sha512-SRXjM+tfsSlA9VuG8hGO2nft2p8zjXCK1VcC6N4NXbBbYbSia9kzCChYQajIjzIqOOOuh5Ock6MmV2oux4jDZQ==" - }, - "@types/cookie": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz", - "integrity": "sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==" - }, - "@types/cors": { - "version": "2.8.12", - "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.12.tgz", - "integrity": "sha512-vt+kDhq/M2ayberEtJcIN/hxXy1Pk+59g2FV/ZQceeaTyCtCucjL2Q7FXlFjtWn4n15KCr1NE2lNNFhp0lEThw==" - }, - "@types/node": { - "version": "16.11.10", - "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.10.tgz", - "integrity": "sha512-3aRnHa1KlOEEhJ6+CvyHKK5vE9BcLGjtUpwvqYLRvYNQKMfabu3BwfJaA/SLW8dxe28LsNDjtHwePTuzn3gmOA==" - }, - "accepts": { - "version": "1.3.7", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", - "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", - "requires": { - "mime-types": "~2.1.24", - "negotiator": "0.6.2" - } - }, - "array-flatten": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" - }, - "base64id": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", - "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==" - }, - "body-parser": { - "version": "1.19.0", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", - "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", - "requires": { - "bytes": "3.1.0", - "content-type": "~1.0.4", - "debug": "2.6.9", - "depd": "~1.1.2", - "http-errors": "1.7.2", - "iconv-lite": "0.4.24", - "on-finished": "~2.3.0", - "qs": "6.7.0", - "raw-body": "2.4.0", - "type-is": "~1.6.17" - } - }, - "bytes": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", - "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==" - }, - "component-emitter": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", - "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==" - }, - "content-disposition": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", - "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", - "requires": { - "safe-buffer": "5.1.2" - } - }, - "content-type": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", - "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" - }, - "cookie": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", - "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==" - }, - "cookie-signature": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" - }, - "cors": { - "version": "2.8.5", - "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", - "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", - "requires": { - "object-assign": "^4", - "vary": "^1" - } - }, - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - } - }, - "depd": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", - "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" - }, - "destroy": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", - "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" - }, - "ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" - }, - "encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" - }, - "engine.io": { - "version": "6.1.3", - "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.1.3.tgz", - "integrity": "sha512-rqs60YwkvWTLLnfazqgZqLa/aKo+9cueVfEi/dZ8PyGyaf8TLOxj++4QMIgeG3Gn0AhrWiFXvghsoY9L9h25GA==", - "requires": { - "@types/cookie": "^0.4.1", - "@types/cors": "^2.8.12", - "@types/node": ">=10.0.0", - "accepts": "~1.3.4", - "base64id": "2.0.0", - "cookie": "~0.4.1", - "cors": "~2.8.5", - "debug": "~4.3.1", - "engine.io-parser": "~5.0.3", - "ws": "~8.2.3" - }, - "dependencies": { - "cookie": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz", - "integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==" - }, - "debug": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", - "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", - "requires": { - "ms": "2.1.2" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - } - } - }, - "engine.io-parser": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.0.4.tgz", - "integrity": "sha512-+nVFp+5z1E3HcToEnO7ZIj3g+3k9389DvWtvJZz0T6/eOCPIyyxehFcedoYrZQrp0LgQbD9pPXhpMBKMd5QURg==" - }, - "escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" - }, - "etag": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" - }, - "express": { - "version": "4.17.1", - "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", - "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", - "requires": { - "accepts": "~1.3.7", - "array-flatten": "1.1.1", - "body-parser": "1.19.0", - "content-disposition": "0.5.3", - "content-type": "~1.0.4", - "cookie": "0.4.0", - "cookie-signature": "1.0.6", - "debug": "2.6.9", - "depd": "~1.1.2", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "finalhandler": "~1.1.2", - "fresh": "0.5.2", - "merge-descriptors": "1.0.1", - "methods": "~1.1.2", - "on-finished": "~2.3.0", - "parseurl": "~1.3.3", - "path-to-regexp": "0.1.7", - "proxy-addr": "~2.0.5", - "qs": "6.7.0", - "range-parser": "~1.2.1", - "safe-buffer": "5.1.2", - "send": "0.17.1", - "serve-static": "1.14.1", - "setprototypeof": "1.1.1", - "statuses": "~1.5.0", - "type-is": "~1.6.18", - "utils-merge": "1.0.1", - "vary": "~1.1.2" - } - }, - "finalhandler": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", - "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", - "requires": { - "debug": "2.6.9", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "on-finished": "~2.3.0", - "parseurl": "~1.3.3", - "statuses": "~1.5.0", - "unpipe": "~1.0.0" - } - }, - "forwarded": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", - "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==" - }, - "fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" - }, - "http-errors": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", - "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", - "requires": { - "depd": "~1.1.2", - "inherits": "2.0.3", - "setprototypeof": "1.1.1", - "statuses": ">= 1.5.0 < 2", - "toidentifier": "1.0.0" - } - }, - "iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "requires": { - "safer-buffer": ">= 2.1.2 < 3" - } - }, - "inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" - }, - "ipaddr.js": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" - }, - "media-typer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" - }, - "merge-descriptors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", - "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" - }, - "methods": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" - }, - "mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" - }, - "mime-db": { - "version": "1.51.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.51.0.tgz", - "integrity": "sha512-5y8A56jg7XVQx2mbv1lu49NR4dokRnhZYTtL+KGfaa27uq4pSTXkwQkFJl4pkRMyNFz/EtYDSkiiEHx3F7UN6g==" - }, - "mime-types": { - "version": "2.1.34", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.34.tgz", - "integrity": "sha512-6cP692WwGIs9XXdOO4++N+7qjqv0rqxxVvJ3VHPh/Sc9mVZcQP+ZGhkKiTvWMQRr2tbHkJP/Yn7Y0npb3ZBs4A==", - "requires": { - "mime-db": "1.51.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - }, - "negotiator": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", - "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" - }, - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" - }, - "on-finished": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", - "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", - "requires": { - "ee-first": "1.1.1" - } - }, - "parseurl": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" - }, - "path-to-regexp": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", - "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" - }, - "proxy-addr": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", - "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", - "requires": { - "forwarded": "0.2.0", - "ipaddr.js": "1.9.1" - } - }, - "qs": { - "version": "6.7.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", - "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==" - }, - "range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" - }, - "raw-body": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", - "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", - "requires": { - "bytes": "3.1.0", - "http-errors": "1.7.2", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" - } - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" - }, - "send": { - "version": "0.17.1", - "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", - "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==", - "requires": { - "debug": "2.6.9", - "depd": "~1.1.2", - "destroy": "~1.0.4", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "fresh": "0.5.2", - "http-errors": "~1.7.2", - "mime": "1.6.0", - "ms": "2.1.1", - "on-finished": "~2.3.0", - "range-parser": "~1.2.1", - "statuses": "~1.5.0" - }, - "dependencies": { - "ms": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", - "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" - } - } - }, - "serve-static": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", - "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==", - "requires": { - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "parseurl": "~1.3.3", - "send": "0.17.1" - } - }, - "setprototypeof": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", - "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" - }, - "socket.io": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.4.0.tgz", - "integrity": "sha512-bnpJxswR9ov0Bw6ilhCvO38/1WPtE3eA2dtxi2Iq4/sFebiDJQzgKNYA7AuVVdGW09nrESXd90NbZqtDd9dzRQ==", - "requires": { - "accepts": "~1.3.4", - "base64id": "~2.0.0", - "debug": "~4.3.2", - "engine.io": "~6.1.0", - "socket.io-adapter": "~2.3.3", - "socket.io-parser": "~4.0.4" - }, - "dependencies": { - "debug": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", - "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", - "requires": { - "ms": "2.1.2" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - } - } - }, - "socket.io-adapter": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.3.3.tgz", - "integrity": "sha512-Qd/iwn3VskrpNO60BeRyCyr8ZWw9CPZyitW4AQwmRZ8zCiyDiL+znRnWX6tDHXnWn1sJrM1+b6Mn6wEDJJ4aYQ==" - }, - "socket.io-parser": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.0.4.tgz", - "integrity": "sha512-t+b0SS+IxG7Rxzda2EVvyBZbvFPBCjJoyHuE0P//7OAsN23GItzDRdWa6ALxZI/8R5ygK7jAR6t028/z+7295g==", - "requires": { - "@types/component-emitter": "^1.2.10", - "component-emitter": "~1.3.0", - "debug": "~4.3.1" - }, - "dependencies": { - "debug": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", - "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", - "requires": { - "ms": "2.1.2" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - } - } - }, - "statuses": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", - "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" - }, - "toidentifier": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", - "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==" - }, - "type-is": { - "version": "1.6.18", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", - "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", - "requires": { - "media-typer": "0.3.0", - "mime-types": "~2.1.24" - } - }, - "unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" - }, - "utils-merge": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" - }, - "vary": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" - }, - "ws": { - "version": "8.2.3", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.2.3.tgz", - "integrity": "sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA==", - "requires": {} - } } } diff --git a/examples/audiobridge/src/index.js b/examples/audiobridge/src/index.js index e231e7b..f34ed5a 100644 --- a/examples/audiobridge/src/index.js +++ b/examples/audiobridge/src/index.js @@ -146,6 +146,10 @@ function initFrontEnd() { replyEvent(socket, 'peer-leaving', evtdata); }); + audioHandle.on(AudioBridgePlugin.EVENT.AUDIOBRIDGE_CONFIGURED, evtdata => { + replyEvent(socket, 'configured', evtdata); + }); + audioHandle.on(AudioBridgePlugin.EVENT.AUDIOBRIDGE_PEER_CONFIGURED, evtdata => { replyEvent(socket, 'peer-configured', evtdata); }); @@ -171,6 +175,14 @@ function initFrontEnd() { replyEvent(socket, 'room-muted-update', evtdata); }); + audioHandle.on(AudioBridgePlugin.EVENT.AUDIOBRIDGE_PEER_SUSPENDED, evtdata => { + replyEvent(socket, 'peer-suspended', evtdata); + }); + + audioHandle.on(AudioBridgePlugin.EVENT.AUDIOBRIDGE_PEER_RESUMED, evtdata => { + replyEvent(socket, 'peer-resumed', evtdata); + }); + // generic audiobridge events audioHandle.on(Janode.EVENT.HANDLE_WEBRTCUP, () => Logger.info(`${LOG_NS} ${audioHandle.name} webrtcup event`)); audioHandle.on(Janode.EVENT.HANDLE_MEDIA, evtdata => Logger.info(`${LOG_NS} ${audioHandle.name} media event ${JSON.stringify(evtdata)}`)); @@ -434,6 +446,66 @@ function initFrontEnd() { } }); + socket.on('suspend-peer', async (evtdata = {}) => { + Logger.info(`${LOG_NS} ${remote} suspend-peer received`); + const { _id, data: suspendpeerdata = {} } = evtdata; + + if (!checkSessions(janodeSession, janodeManagerHandle, socket, evtdata)) return; + + try { + const response = await janodeManagerHandle.suspend(suspendpeerdata); + replyEvent(socket, 'peer-suspended', response, _id); + Logger.info(`${LOG_NS} ${remote} peer-suspended sent`); + } catch ({ message }) { + replyError(socket, message, suspendpeerdata, _id); + } + }); + + socket.on('resume-peer', async (evtdata = {}) => { + Logger.info(`${LOG_NS} ${remote} resume-peer received`); + const { _id, data: resumepeerdata = {} } = evtdata; + + if (!checkSessions(janodeSession, janodeManagerHandle, socket, evtdata)) return; + + try { + const response = await janodeManagerHandle.resume(resumepeerdata); + replyEvent(socket, 'peer-resumed', response, _id); + Logger.info(`${LOG_NS} ${remote} peer-resumed sent`); + } catch ({ message }) { + replyError(socket, message, resumepeerdata, _id); + } + }); + + socket.on('mute-peer', async (evtdata = {}) => { + Logger.info(`${LOG_NS} ${remote} mute-peer received`); + const { _id, data: mutepeerdata = {} } = evtdata; + + if (!checkSessions(janodeSession, janodeManagerHandle, socket, evtdata)) return; + + try { + const response = await janodeManagerHandle.mute(mutepeerdata); + replyEvent(socket, 'peer-muted', response, _id); + Logger.info(`${LOG_NS} ${remote} peer-muted sent`); + } catch ({ message }) { + replyError(socket, message, mutepeerdata, _id); + } + }); + + socket.on('unmute-peer', async (evtdata = {}) => { + Logger.info(`${LOG_NS} ${remote} unmute-peer received`); + const { _id, data: unmutepeerdata = {} } = evtdata; + + if (!checkSessions(janodeSession, janodeManagerHandle, socket, evtdata)) return; + + try { + const response = await janodeManagerHandle.unmute(unmutepeerdata); + replyEvent(socket, 'peer-unmuted', response, _id); + Logger.info(`${LOG_NS} ${remote} peer-unmuted sent`); + } catch ({ message }) { + replyError(socket, message, unmutepeerdata, _id); + } + }); + socket.on('mute-room', async (evtdata = {}) => { Logger.info(`${LOG_NS} ${remote} mute-room received`); const { _id, data: mutedata = {} } = evtdata; @@ -464,6 +536,36 @@ function initFrontEnd() { } }); + socket.on('suspend-peer', async (evtdata = {}) => { + Logger.info(`${LOG_NS} ${remote} suspend-peer received`); + const { _id, data: suspendpeerdata = {} } = evtdata; + + if (!checkSessions(janodeSession, janodeManagerHandle, socket, evtdata)) return; + + try { + const response = await janodeManagerHandle.suspend(suspendpeerdata); + replyEvent(socket, 'peer-suspended', response, _id); + Logger.info(`${LOG_NS} ${remote} peer-suspended sent`); + } catch ({ message }) { + replyError(socket, message, suspendpeerdata, _id); + } + }); + + socket.on('resume-peer', async (evtdata = {}) => { + Logger.info(`${LOG_NS} ${remote} resume-peer received`); + const { _id, data: resumepeerdata = {} } = evtdata; + + if (!checkSessions(janodeSession, janodeManagerHandle, socket, evtdata)) return; + + try { + const response = await janodeManagerHandle.resume(resumepeerdata); + replyEvent(socket, 'peer-resumed', response, _id); + Logger.info(`${LOG_NS} ${remote} peer-resumed sent`); + } catch ({ message }) { + replyError(socket, message, resumepeerdata, _id); + } + }); + }); // disable caching for all app diff --git a/examples/browser/bundle.sh b/examples/browser/bundle.sh index b7b3b10..c14a252 100755 --- a/examples/browser/bundle.sh +++ b/examples/browser/bundle.sh @@ -23,6 +23,7 @@ export default { EOF cat < config.js +import replace from '@rollup/plugin-replace'; import nodePolyfills from 'rollup-plugin-polyfill-node'; import nodeResolve from '@rollup/plugin-node-resolve'; import commonjs from '@rollup/plugin-commonjs'; @@ -35,6 +36,11 @@ export default { name: '$EXPORTED_OBJECT' }, plugins: [ + replace({ + preventAssignment: false, + "import UnixTransport from './transport-unix.js'" : "function UnixTransport() { this.open = _ => Logger.error('unix-dgram unsupported on browsers')}", + delimiters: ['', ''] + }), nodePolyfills(), nodeResolve({ browser: true }), commonjs() @@ -42,7 +48,7 @@ export default { }; EOF -npm install --no-save rollup-plugin-polyfill-node @rollup/plugin-node-resolve @rollup/plugin-commonjs +npm install --no-save @rollup/plugin-replace rollup-plugin-polyfill-node @rollup/plugin-node-resolve @rollup/plugin-commonjs rollup --config config.js cp ./app/*.* $DEPLOY_DIR diff --git a/examples/echotest/html/.eslintrc.json b/examples/echotest/html/.eslintrc.json deleted file mode 100644 index 5c3f02e..0000000 --- a/examples/echotest/html/.eslintrc.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "env": { - "browser": true - } -} \ No newline at end of file diff --git a/examples/echotest/package-lock.json b/examples/echotest/package-lock.json index 2716c23..9882b6b 100644 --- a/examples/echotest/package-lock.json +++ b/examples/echotest/package-lock.json @@ -1,6 +1,6 @@ { "name": "janode-echotest", - "lockfileVersion": 2, + "lockfileVersion": 3, "requires": true, "packages": { "": { @@ -11,33 +11,44 @@ "socket.io": "^4.2.0" } }, - "node_modules/@types/component-emitter": { - "version": "1.2.11", - "resolved": "https://registry.npmjs.org/@types/component-emitter/-/component-emitter-1.2.11.tgz", - "integrity": "sha512-SRXjM+tfsSlA9VuG8hGO2nft2p8zjXCK1VcC6N4NXbBbYbSia9kzCChYQajIjzIqOOOuh5Ock6MmV2oux4jDZQ==" + "node_modules/@socket.io/component-emitter": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz", + "integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==", + "license": "MIT" }, "node_modules/@types/cookie": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz", - "integrity": "sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==" + "integrity": "sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==", + "license": "MIT" }, "node_modules/@types/cors": { - "version": "2.8.12", - "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.12.tgz", - "integrity": "sha512-vt+kDhq/M2ayberEtJcIN/hxXy1Pk+59g2FV/ZQceeaTyCtCucjL2Q7FXlFjtWn4n15KCr1NE2lNNFhp0lEThw==" + "version": "2.8.17", + "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.17.tgz", + "integrity": "sha512-8CGDvrBj1zgo2qE+oS3pOCyYNqCPryMWY2bGfwA0dcfopWGgxs+78df0Rs3rc9THP4JkOhLsAa+15VdpAqkcUA==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } }, "node_modules/@types/node": { - "version": "16.11.10", - "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.10.tgz", - "integrity": "sha512-3aRnHa1KlOEEhJ6+CvyHKK5vE9BcLGjtUpwvqYLRvYNQKMfabu3BwfJaA/SLW8dxe28LsNDjtHwePTuzn3gmOA==" + "version": "22.10.7", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.7.tgz", + "integrity": "sha512-V09KvXxFiutGp6B7XkpaDXlNadZxrzajcY50EuoLIpQ6WWYCSvf19lVIazzfIzQvhUN2HjX12spLojTnhuKlGg==", + "license": "MIT", + "dependencies": { + "undici-types": "~6.20.0" + } }, "node_modules/accepts": { - "version": "1.3.7", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", - "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "license": "MIT", "dependencies": { - "mime-types": "~2.1.24", - "negotiator": "0.6.2" + "mime-types": "~2.1.34", + "negotiator": "0.6.3" }, "engines": { "node": ">= 0.6" @@ -46,72 +57,106 @@ "node_modules/array-flatten": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", + "license": "MIT" }, "node_modules/base64id": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==", + "license": "MIT", "engines": { "node": "^4.5.0 || >= 5.9" } }, "node_modules/body-parser": { - "version": "1.19.0", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", - "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", + "version": "1.20.3", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", + "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==", + "license": "MIT", "dependencies": { - "bytes": "3.1.0", - "content-type": "~1.0.4", + "bytes": "3.1.2", + "content-type": "~1.0.5", "debug": "2.6.9", - "depd": "~1.1.2", - "http-errors": "1.7.2", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", "iconv-lite": "0.4.24", - "on-finished": "~2.3.0", - "qs": "6.7.0", - "raw-body": "2.4.0", - "type-is": "~1.6.17" + "on-finished": "2.4.1", + "qs": "6.13.0", + "raw-body": "2.5.2", + "type-is": "~1.6.18", + "unpipe": "1.0.0" }, "engines": { - "node": ">= 0.8" + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" } }, "node_modules/bytes": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", - "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "license": "MIT", "engines": { "node": ">= 0.8" } }, - "node_modules/component-emitter": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", - "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==" + "node_modules/call-bind-apply-helpers": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.1.tgz", + "integrity": "sha512-BhYE+WDaywFg2TBWYNXAE+8B1ATnThNBqXHP5nQu0jWJdVvY2hvkpyB3qOmtmDePiS5/BDQ8wASEWGMWRG148g==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.3.tgz", + "integrity": "sha512-YTd+6wGlNlPxSuri7Y6X8tY2dmm12UMH66RpKMhiX6rsk5wXXnYgbUcOt8kiS31/AjfoTOvCsE+w8nZQLQnzHA==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, "node_modules/content-disposition": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", - "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "license": "MIT", "dependencies": { - "safe-buffer": "5.1.2" + "safe-buffer": "5.2.1" }, "engines": { "node": ">= 0.6" } }, "node_modules/content-type": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", - "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "license": "MIT", "engines": { "node": ">= 0.6" } }, "node_modules/cookie": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", - "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==", + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz", + "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==", + "license": "MIT", "engines": { "node": ">= 0.6" } @@ -119,12 +164,14 @@ "node_modules/cookie-signature": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==", + "license": "MIT" }, "node_modules/cors": { "version": "2.8.5", "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "license": "MIT", "dependencies": { "object-assign": "^4", "vary": "^1" @@ -137,78 +184,105 @@ "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", "dependencies": { "ms": "2.0.0" } }, "node_modules/depd": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", - "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "license": "MIT", "engines": { - "node": ">= 0.6" + "node": ">= 0.8" } }, "node_modules/destroy": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", - "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "license": "MIT", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } }, "node_modules/ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "license": "MIT" }, "node_modules/encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "license": "MIT", "engines": { "node": ">= 0.8" } }, "node_modules/engine.io": { - "version": "6.1.3", - "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.1.3.tgz", - "integrity": "sha512-rqs60YwkvWTLLnfazqgZqLa/aKo+9cueVfEi/dZ8PyGyaf8TLOxj++4QMIgeG3Gn0AhrWiFXvghsoY9L9h25GA==", + "version": "6.6.2", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.6.2.tgz", + "integrity": "sha512-gmNvsYi9C8iErnZdVcJnvCpSKbWTt1E8+JZo8b+daLninywUWi5NQ5STSHZ9rFjFO7imNcvb8Pc5pe/wMR5xEw==", + "license": "MIT", "dependencies": { "@types/cookie": "^0.4.1", "@types/cors": "^2.8.12", "@types/node": ">=10.0.0", "accepts": "~1.3.4", "base64id": "2.0.0", - "cookie": "~0.4.1", + "cookie": "~0.7.2", "cors": "~2.8.5", "debug": "~4.3.1", - "engine.io-parser": "~5.0.3", - "ws": "~8.2.3" + "engine.io-parser": "~5.2.1", + "ws": "~8.17.1" }, "engines": { - "node": ">=10.0.0" + "node": ">=10.2.0" } }, "node_modules/engine.io-parser": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.0.4.tgz", - "integrity": "sha512-+nVFp+5z1E3HcToEnO7ZIj3g+3k9389DvWtvJZz0T6/eOCPIyyxehFcedoYrZQrp0LgQbD9pPXhpMBKMd5QURg==", + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.3.tgz", + "integrity": "sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==", + "license": "MIT", "engines": { "node": ">=10.0.0" } }, "node_modules/engine.io/node_modules/cookie": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz", - "integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==", + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "license": "MIT", "engines": { "node": ">= 0.6" } }, "node_modules/engine.io/node_modules/debug": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", - "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "license": "MIT", "dependencies": { - "ms": "2.1.2" + "ms": "^2.1.3" }, "engines": { "node": ">=6.0" @@ -220,74 +294,114 @@ } }, "node_modules/engine.io/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } }, "node_modules/escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "license": "MIT" }, "node_modules/etag": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "license": "MIT", "engines": { "node": ">= 0.6" } }, "node_modules/express": { - "version": "4.17.1", - "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", - "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", + "version": "4.21.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz", + "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==", + "license": "MIT", "dependencies": { - "accepts": "~1.3.7", + "accepts": "~1.3.8", "array-flatten": "1.1.1", - "body-parser": "1.19.0", - "content-disposition": "0.5.3", + "body-parser": "1.20.3", + "content-disposition": "0.5.4", "content-type": "~1.0.4", - "cookie": "0.4.0", + "cookie": "0.7.1", "cookie-signature": "1.0.6", "debug": "2.6.9", - "depd": "~1.1.2", - "encodeurl": "~1.0.2", + "depd": "2.0.0", + "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "etag": "~1.8.1", - "finalhandler": "~1.1.2", + "finalhandler": "1.3.1", "fresh": "0.5.2", - "merge-descriptors": "1.0.1", + "http-errors": "2.0.0", + "merge-descriptors": "1.0.3", "methods": "~1.1.2", - "on-finished": "~2.3.0", + "on-finished": "2.4.1", "parseurl": "~1.3.3", - "path-to-regexp": "0.1.7", - "proxy-addr": "~2.0.5", - "qs": "6.7.0", + "path-to-regexp": "0.1.12", + "proxy-addr": "~2.0.7", + "qs": "6.13.0", "range-parser": "~1.2.1", - "safe-buffer": "5.1.2", - "send": "0.17.1", - "serve-static": "1.14.1", - "setprototypeof": "1.1.1", - "statuses": "~1.5.0", + "safe-buffer": "5.2.1", + "send": "0.19.0", + "serve-static": "1.16.2", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", "type-is": "~1.6.18", "utils-merge": "1.0.1", "vary": "~1.1.2" }, "engines": { "node": ">= 0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/finalhandler": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", - "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz", + "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==", + "license": "MIT", "dependencies": { "debug": "2.6.9", - "encodeurl": "~1.0.2", + "encodeurl": "~2.0.0", "escape-html": "~1.0.3", - "on-finished": "~2.3.0", + "on-finished": "2.4.1", "parseurl": "~1.3.3", - "statuses": "~1.5.0", + "statuses": "2.0.1", "unpipe": "~1.0.0" }, "engines": { @@ -298,6 +412,7 @@ "version": "0.2.0", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "license": "MIT", "engines": { "node": ">= 0.6" } @@ -305,30 +420,115 @@ "node_modules/fresh": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "license": "MIT", "engines": { "node": ">= 0.6" } }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-intrinsic": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.7.tgz", + "integrity": "sha512-VW6Pxhsrk0KAOqs3WEd0klDiF/+V7gQOpAvY1jVU/LHmaD/kQO4523aiJuikX/QAKYiW6x8Jh+RJej1almdtCA==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "function-bind": "^1.1.2", + "get-proto": "^1.0.0", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/http-errors": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", - "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "license": "MIT", "dependencies": { - "depd": "~1.1.2", - "inherits": "2.0.3", - "setprototypeof": "1.1.1", - "statuses": ">= 1.5.0 < 2", - "toidentifier": "1.0.0" + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" }, "engines": { - "node": ">= 0.6" + "node": ">= 0.8" } }, "node_modules/iconv-lite": { "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "license": "MIT", "dependencies": { "safer-buffer": ">= 2.1.2 < 3" }, @@ -337,35 +537,52 @@ } }, "node_modules/inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" }, "node_modules/ipaddr.js": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "license": "MIT", "engines": { "node": ">= 0.10" } }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "license": "MIT", "engines": { "node": ">= 0.6" } }, "node_modules/merge-descriptors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", - "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", + "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, "node_modules/methods": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "license": "MIT", "engines": { "node": ">= 0.6" } @@ -374,6 +591,7 @@ "version": "1.6.0", "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "license": "MIT", "bin": { "mime": "cli.js" }, @@ -382,19 +600,21 @@ } }, "node_modules/mime-db": { - "version": "1.51.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.51.0.tgz", - "integrity": "sha512-5y8A56jg7XVQx2mbv1lu49NR4dokRnhZYTtL+KGfaa27uq4pSTXkwQkFJl4pkRMyNFz/EtYDSkiiEHx3F7UN6g==", + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", "engines": { "node": ">= 0.6" } }, "node_modules/mime-types": { - "version": "2.1.34", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.34.tgz", - "integrity": "sha512-6cP692WwGIs9XXdOO4++N+7qjqv0rqxxVvJ3VHPh/Sc9mVZcQP+ZGhkKiTvWMQRr2tbHkJP/Yn7Y0npb3ZBs4A==", + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", "dependencies": { - "mime-db": "1.51.0" + "mime-db": "1.52.0" }, "engines": { "node": ">= 0.6" @@ -403,12 +623,14 @@ "node_modules/ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" }, "node_modules/negotiator": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", - "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==", + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "license": "MIT", "engines": { "node": ">= 0.6" } @@ -416,15 +638,29 @@ "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "license": "MIT", "engines": { "node": ">=0.10.0" } }, + "node_modules/object-inspect": { + "version": "1.13.3", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.3.tgz", + "integrity": "sha512-kDCGIbxkDSXE3euJZZXzc6to7fCrKHNI/hSRQnRuQ+BWjFNzZwiFF8fj/6o2t2G9/jTj8PSIYTfCLelLZEeRpA==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/on-finished": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", - "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "license": "MIT", "dependencies": { "ee-first": "1.1.1" }, @@ -436,19 +672,22 @@ "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "license": "MIT", "engines": { "node": ">= 0.8" } }, "node_modules/path-to-regexp": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", - "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz", + "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==", + "license": "MIT" }, "node_modules/proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "license": "MIT", "dependencies": { "forwarded": "0.2.0", "ipaddr.js": "1.9.1" @@ -458,28 +697,37 @@ } }, "node_modules/qs": { - "version": "6.7.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", - "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==", + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", + "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.0.6" + }, "engines": { "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/range-parser": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "license": "MIT", "engines": { "node": ">= 0.6" } }, "node_modules/raw-body": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", - "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", + "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", + "license": "MIT", "dependencies": { - "bytes": "3.1.0", - "http-errors": "1.7.2", + "bytes": "3.1.2", + "http-errors": "2.0.0", "iconv-lite": "0.4.24", "unpipe": "1.0.0" }, @@ -488,90 +736,221 @@ } }, "node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" }, "node_modules/safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" }, "node_modules/send": { - "version": "0.17.1", - "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", - "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==", + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz", + "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==", + "license": "MIT", "dependencies": { "debug": "2.6.9", - "depd": "~1.1.2", - "destroy": "~1.0.4", + "depd": "2.0.0", + "destroy": "1.2.0", "encodeurl": "~1.0.2", "escape-html": "~1.0.3", "etag": "~1.8.1", "fresh": "0.5.2", - "http-errors": "~1.7.2", + "http-errors": "2.0.0", "mime": "1.6.0", - "ms": "2.1.1", - "on-finished": "~2.3.0", + "ms": "2.1.3", + "on-finished": "2.4.1", "range-parser": "~1.2.1", - "statuses": "~1.5.0" + "statuses": "2.0.1" }, "engines": { "node": ">= 0.8.0" } }, + "node_modules/send/node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/send/node_modules/ms": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", - "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" }, "node_modules/serve-static": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", - "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==", + "version": "1.16.2", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz", + "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==", + "license": "MIT", "dependencies": { - "encodeurl": "~1.0.2", + "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "parseurl": "~1.3.3", - "send": "0.17.1" + "send": "0.19.0" }, "engines": { "node": ">= 0.8.0" } }, "node_modules/setprototypeof": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", - "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "license": "ISC" + }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, "node_modules/socket.io": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.4.0.tgz", - "integrity": "sha512-bnpJxswR9ov0Bw6ilhCvO38/1WPtE3eA2dtxi2Iq4/sFebiDJQzgKNYA7AuVVdGW09nrESXd90NbZqtDd9dzRQ==", + "version": "4.8.1", + "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.8.1.tgz", + "integrity": "sha512-oZ7iUCxph8WYRHHcjBEc9unw3adt5CmSNlppj/5Q4k2RIrhl8Z5yY2Xr4j9zj0+wzVZ0bxmYoGSzKJnRl6A4yg==", + "license": "MIT", "dependencies": { "accepts": "~1.3.4", "base64id": "~2.0.0", + "cors": "~2.8.5", "debug": "~4.3.2", - "engine.io": "~6.1.0", - "socket.io-adapter": "~2.3.3", - "socket.io-parser": "~4.0.4" + "engine.io": "~6.6.0", + "socket.io-adapter": "~2.5.2", + "socket.io-parser": "~4.2.4" }, "engines": { - "node": ">=10.0.0" + "node": ">=10.2.0" } }, "node_modules/socket.io-adapter": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.3.3.tgz", - "integrity": "sha512-Qd/iwn3VskrpNO60BeRyCyr8ZWw9CPZyitW4AQwmRZ8zCiyDiL+znRnWX6tDHXnWn1sJrM1+b6Mn6wEDJJ4aYQ==" + "version": "2.5.5", + "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.5.tgz", + "integrity": "sha512-eLDQas5dzPgOWCk9GuuJC2lBqItuhKI4uxGgo9aIV7MYbk2h9Q6uULEh8WBzThoI7l+qU9Ast9fVUmkqPP9wYg==", + "license": "MIT", + "dependencies": { + "debug": "~4.3.4", + "ws": "~8.17.1" + } + }, + "node_modules/socket.io-adapter/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/socket.io-adapter/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" }, "node_modules/socket.io-parser": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.0.4.tgz", - "integrity": "sha512-t+b0SS+IxG7Rxzda2EVvyBZbvFPBCjJoyHuE0P//7OAsN23GItzDRdWa6ALxZI/8R5ygK7jAR6t028/z+7295g==", + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz", + "integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==", + "license": "MIT", "dependencies": { - "@types/component-emitter": "^1.2.10", - "component-emitter": "~1.3.0", + "@socket.io/component-emitter": "~3.1.0", "debug": "~4.3.1" }, "engines": { @@ -579,11 +958,12 @@ } }, "node_modules/socket.io-parser/node_modules/debug": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", - "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "license": "MIT", "dependencies": { - "ms": "2.1.2" + "ms": "^2.1.3" }, "engines": { "node": ">=6.0" @@ -595,16 +975,18 @@ } }, "node_modules/socket.io-parser/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" }, "node_modules/socket.io/node_modules/debug": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", - "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "license": "MIT", "dependencies": { - "ms": "2.1.2" + "ms": "^2.1.3" }, "engines": { "node": ">=6.0" @@ -616,22 +998,25 @@ } }, "node_modules/socket.io/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" }, "node_modules/statuses": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", - "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "license": "MIT", "engines": { - "node": ">= 0.6" + "node": ">= 0.8" } }, "node_modules/toidentifier": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", - "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "license": "MIT", "engines": { "node": ">=0.6" } @@ -640,6 +1025,7 @@ "version": "1.6.18", "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "license": "MIT", "dependencies": { "media-typer": "0.3.0", "mime-types": "~2.1.24" @@ -648,10 +1034,17 @@ "node": ">= 0.6" } }, + "node_modules/undici-types": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", + "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", + "license": "MIT" + }, "node_modules/unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "license": "MIT", "engines": { "node": ">= 0.8" } @@ -659,7 +1052,8 @@ "node_modules/utils-merge": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "license": "MIT", "engines": { "node": ">= 0.4.0" } @@ -667,21 +1061,23 @@ "node_modules/vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "license": "MIT", "engines": { "node": ">= 0.8" } }, "node_modules/ws": { - "version": "8.2.3", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.2.3.tgz", - "integrity": "sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA==", + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", + "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", + "license": "MIT", "engines": { "node": ">=10.0.0" }, "peerDependencies": { "bufferutil": "^4.0.1", - "utf-8-validate": "^5.0.2" + "utf-8-validate": ">=5.0.2" }, "peerDependenciesMeta": { "bufferutil": { @@ -692,523 +1088,5 @@ } } } - }, - "dependencies": { - "@types/component-emitter": { - "version": "1.2.11", - "resolved": "https://registry.npmjs.org/@types/component-emitter/-/component-emitter-1.2.11.tgz", - "integrity": "sha512-SRXjM+tfsSlA9VuG8hGO2nft2p8zjXCK1VcC6N4NXbBbYbSia9kzCChYQajIjzIqOOOuh5Ock6MmV2oux4jDZQ==" - }, - "@types/cookie": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz", - "integrity": "sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==" - }, - "@types/cors": { - "version": "2.8.12", - "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.12.tgz", - "integrity": "sha512-vt+kDhq/M2ayberEtJcIN/hxXy1Pk+59g2FV/ZQceeaTyCtCucjL2Q7FXlFjtWn4n15KCr1NE2lNNFhp0lEThw==" - }, - "@types/node": { - "version": "16.11.10", - "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.10.tgz", - "integrity": "sha512-3aRnHa1KlOEEhJ6+CvyHKK5vE9BcLGjtUpwvqYLRvYNQKMfabu3BwfJaA/SLW8dxe28LsNDjtHwePTuzn3gmOA==" - }, - "accepts": { - "version": "1.3.7", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", - "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", - "requires": { - "mime-types": "~2.1.24", - "negotiator": "0.6.2" - } - }, - "array-flatten": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" - }, - "base64id": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", - "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==" - }, - "body-parser": { - "version": "1.19.0", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", - "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", - "requires": { - "bytes": "3.1.0", - "content-type": "~1.0.4", - "debug": "2.6.9", - "depd": "~1.1.2", - "http-errors": "1.7.2", - "iconv-lite": "0.4.24", - "on-finished": "~2.3.0", - "qs": "6.7.0", - "raw-body": "2.4.0", - "type-is": "~1.6.17" - } - }, - "bytes": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", - "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==" - }, - "component-emitter": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", - "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==" - }, - "content-disposition": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", - "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", - "requires": { - "safe-buffer": "5.1.2" - } - }, - "content-type": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", - "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" - }, - "cookie": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", - "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==" - }, - "cookie-signature": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" - }, - "cors": { - "version": "2.8.5", - "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", - "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", - "requires": { - "object-assign": "^4", - "vary": "^1" - } - }, - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - } - }, - "depd": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", - "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" - }, - "destroy": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", - "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" - }, - "ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" - }, - "encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" - }, - "engine.io": { - "version": "6.1.3", - "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.1.3.tgz", - "integrity": "sha512-rqs60YwkvWTLLnfazqgZqLa/aKo+9cueVfEi/dZ8PyGyaf8TLOxj++4QMIgeG3Gn0AhrWiFXvghsoY9L9h25GA==", - "requires": { - "@types/cookie": "^0.4.1", - "@types/cors": "^2.8.12", - "@types/node": ">=10.0.0", - "accepts": "~1.3.4", - "base64id": "2.0.0", - "cookie": "~0.4.1", - "cors": "~2.8.5", - "debug": "~4.3.1", - "engine.io-parser": "~5.0.3", - "ws": "~8.2.3" - }, - "dependencies": { - "cookie": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz", - "integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==" - }, - "debug": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", - "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", - "requires": { - "ms": "2.1.2" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - } - } - }, - "engine.io-parser": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.0.4.tgz", - "integrity": "sha512-+nVFp+5z1E3HcToEnO7ZIj3g+3k9389DvWtvJZz0T6/eOCPIyyxehFcedoYrZQrp0LgQbD9pPXhpMBKMd5QURg==" - }, - "escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" - }, - "etag": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" - }, - "express": { - "version": "4.17.1", - "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", - "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", - "requires": { - "accepts": "~1.3.7", - "array-flatten": "1.1.1", - "body-parser": "1.19.0", - "content-disposition": "0.5.3", - "content-type": "~1.0.4", - "cookie": "0.4.0", - "cookie-signature": "1.0.6", - "debug": "2.6.9", - "depd": "~1.1.2", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "finalhandler": "~1.1.2", - "fresh": "0.5.2", - "merge-descriptors": "1.0.1", - "methods": "~1.1.2", - "on-finished": "~2.3.0", - "parseurl": "~1.3.3", - "path-to-regexp": "0.1.7", - "proxy-addr": "~2.0.5", - "qs": "6.7.0", - "range-parser": "~1.2.1", - "safe-buffer": "5.1.2", - "send": "0.17.1", - "serve-static": "1.14.1", - "setprototypeof": "1.1.1", - "statuses": "~1.5.0", - "type-is": "~1.6.18", - "utils-merge": "1.0.1", - "vary": "~1.1.2" - } - }, - "finalhandler": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", - "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", - "requires": { - "debug": "2.6.9", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "on-finished": "~2.3.0", - "parseurl": "~1.3.3", - "statuses": "~1.5.0", - "unpipe": "~1.0.0" - } - }, - "forwarded": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", - "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==" - }, - "fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" - }, - "http-errors": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", - "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", - "requires": { - "depd": "~1.1.2", - "inherits": "2.0.3", - "setprototypeof": "1.1.1", - "statuses": ">= 1.5.0 < 2", - "toidentifier": "1.0.0" - } - }, - "iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "requires": { - "safer-buffer": ">= 2.1.2 < 3" - } - }, - "inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" - }, - "ipaddr.js": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" - }, - "media-typer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" - }, - "merge-descriptors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", - "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" - }, - "methods": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" - }, - "mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" - }, - "mime-db": { - "version": "1.51.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.51.0.tgz", - "integrity": "sha512-5y8A56jg7XVQx2mbv1lu49NR4dokRnhZYTtL+KGfaa27uq4pSTXkwQkFJl4pkRMyNFz/EtYDSkiiEHx3F7UN6g==" - }, - "mime-types": { - "version": "2.1.34", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.34.tgz", - "integrity": "sha512-6cP692WwGIs9XXdOO4++N+7qjqv0rqxxVvJ3VHPh/Sc9mVZcQP+ZGhkKiTvWMQRr2tbHkJP/Yn7Y0npb3ZBs4A==", - "requires": { - "mime-db": "1.51.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - }, - "negotiator": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", - "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" - }, - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" - }, - "on-finished": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", - "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", - "requires": { - "ee-first": "1.1.1" - } - }, - "parseurl": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" - }, - "path-to-regexp": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", - "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" - }, - "proxy-addr": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", - "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", - "requires": { - "forwarded": "0.2.0", - "ipaddr.js": "1.9.1" - } - }, - "qs": { - "version": "6.7.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", - "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==" - }, - "range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" - }, - "raw-body": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", - "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", - "requires": { - "bytes": "3.1.0", - "http-errors": "1.7.2", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" - } - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" - }, - "send": { - "version": "0.17.1", - "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", - "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==", - "requires": { - "debug": "2.6.9", - "depd": "~1.1.2", - "destroy": "~1.0.4", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "fresh": "0.5.2", - "http-errors": "~1.7.2", - "mime": "1.6.0", - "ms": "2.1.1", - "on-finished": "~2.3.0", - "range-parser": "~1.2.1", - "statuses": "~1.5.0" - }, - "dependencies": { - "ms": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", - "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" - } - } - }, - "serve-static": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", - "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==", - "requires": { - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "parseurl": "~1.3.3", - "send": "0.17.1" - } - }, - "setprototypeof": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", - "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" - }, - "socket.io": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.4.0.tgz", - "integrity": "sha512-bnpJxswR9ov0Bw6ilhCvO38/1WPtE3eA2dtxi2Iq4/sFebiDJQzgKNYA7AuVVdGW09nrESXd90NbZqtDd9dzRQ==", - "requires": { - "accepts": "~1.3.4", - "base64id": "~2.0.0", - "debug": "~4.3.2", - "engine.io": "~6.1.0", - "socket.io-adapter": "~2.3.3", - "socket.io-parser": "~4.0.4" - }, - "dependencies": { - "debug": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", - "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", - "requires": { - "ms": "2.1.2" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - } - } - }, - "socket.io-adapter": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.3.3.tgz", - "integrity": "sha512-Qd/iwn3VskrpNO60BeRyCyr8ZWw9CPZyitW4AQwmRZ8zCiyDiL+znRnWX6tDHXnWn1sJrM1+b6Mn6wEDJJ4aYQ==" - }, - "socket.io-parser": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.0.4.tgz", - "integrity": "sha512-t+b0SS+IxG7Rxzda2EVvyBZbvFPBCjJoyHuE0P//7OAsN23GItzDRdWa6ALxZI/8R5ygK7jAR6t028/z+7295g==", - "requires": { - "@types/component-emitter": "^1.2.10", - "component-emitter": "~1.3.0", - "debug": "~4.3.1" - }, - "dependencies": { - "debug": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", - "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", - "requires": { - "ms": "2.1.2" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - } - } - }, - "statuses": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", - "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" - }, - "toidentifier": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", - "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==" - }, - "type-is": { - "version": "1.6.18", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", - "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", - "requires": { - "media-typer": "0.3.0", - "mime-types": "~2.1.24" - } - }, - "unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" - }, - "utils-merge": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" - }, - "vary": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" - }, - "ws": { - "version": "8.2.3", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.2.3.tgz", - "integrity": "sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA==", - "requires": {} - } } } diff --git a/examples/helloworld/package-lock.json b/examples/helloworld/package-lock.json index 065b41b..8853fbe 100644 --- a/examples/helloworld/package-lock.json +++ b/examples/helloworld/package-lock.json @@ -1,6 +1,6 @@ { "name": "janode-helloworld", - "lockfileVersion": 2, + "lockfileVersion": 3, "requires": true, "packages": { "": { diff --git a/examples/sip/html/index.html b/examples/sip/html/index.html new file mode 100644 index 0000000..fdfd87e --- /dev/null +++ b/examples/sip/html/index.html @@ -0,0 +1,38 @@ + + + + + SIP Socket.IO Janode + + + + +
+
+ disconnected +

+

+
+
+
+
+

+ + + +

+

+
+ + +
+
+ + + + + + \ No newline at end of file diff --git a/examples/sip/html/sip-client.js b/examples/sip/html/sip-client.js new file mode 100644 index 0000000..d3bbb03 --- /dev/null +++ b/examples/sip/html/sip-client.js @@ -0,0 +1,397 @@ +/* global io */ + +'use strict'; + +const RTCPeerConnection = (window.RTCPeerConnection || window.webkitRTCPeerConnection || window.mozRTCPeerConnection).bind(window); + +let sipPeerConnection; +const localAudio = document.getElementById('localAudio'); +const remoteAudio = document.getElementById('remoteAudio'); + +const connButton = document.getElementById('connect'); +connButton.onclick = () => { + if (socket.connected) + socket.disconnect(); + else + socket.connect(); +}; + +const callButton = document.getElementById('call'); +callButton.onclick = async () => { + if (!socket.connected) return; + const uri = document.getElementById('callee').value; + try { + const offer = await doOffer(); + call(uri, offer); + } catch (error) { + console.log('error during setup/offer', error); + stopAllStreams(); + closePC(); + return; + } +}; + +const hangupButton = document.getElementById('hangup'); +hangupButton.onclick = async () => { + if (!socket.connected) return; + hangup(); +}; + +const declineButton = document.getElementById('decline'); +declineButton.onclick = async () => { + if (!socket.connected) return; + decline(); +}; + +let incoming = null; +const acceptButton = document.getElementById('accept'); +acceptButton.onclick = async () => { + if (!socket.connected || !incoming) return; + const { offer, caller } = incoming; + console.log('accepting call from', caller); + incoming = null; + try { + const answer = await doAnswer(offer); + accept(answer); + } catch (error) { + console.log('error during setup/answer', error); + stopAllStreams(); + closePC(); + return; + } +}; + +function getId() { + return Math.floor(Number.MAX_SAFE_INTEGER * Math.random()); +} + +const scheduleConnection = (function () { + let task = null; + const delay = 5000; + + return (function (secs) { + if (task) return; + const timeout = secs * 1000 || delay; + console.log('scheduled register in ' + timeout + ' ms'); + task = setTimeout(() => { + register(); + task = null; + }, timeout); + }); +})(); + +const socket = io({ + rejectUnauthorized: false, + autoConnect: false, + reconnection: false, +}); + +function register() { + if (!socket.connected) return; + const type = document.getElementById('type').value.length > 0 ? document.getElementById('type').value : null; + const username = document.getElementById('username').value; + const secret = document.getElementById('secret').value; + const proxy = document.getElementById('proxy').value; + socket.emit('register', { + data: { + type, + username, + secret, + proxy, + }, + _id: getId() + }); +} + +function call(uri, offer) { + if (!socket.connected) return; + socket.emit('call', { + data: { + uri, + jsep: offer + }, + _id: getId(), + }); +} + +function accept(answer) { + if (!socket.connected) return; + socket.emit('accept', { + data: { + jsep: answer + }, + _id: getId(), + }); +} + +function hangup() { + if (!socket.connected) return; + socket.emit('hangup', { + data: {}, + _id: getId(), + }); +} + +function decline() { + if (!socket.connected) return; + socket.emit('decline', { + data: {}, + _id: getId(), + }); +} + +function trickle({ candidate }) { + const trickleData = candidate ? { candidate } : {}; + const trickleEvent = candidate ? 'trickle' : 'trickle-complete'; + + socket.emit(trickleEvent, { + data: trickleData, + _id: getId(), + }); +} + +socket.on('registering', ({ data }) => { + console.log('sip registering', data); + document.getElementById('status').innerHTML = 'registering'; +}); + +socket.on('registered', ({ data }) => { + console.log('sip registered', data); + document.getElementById('status').innerHTML = `registered (${data.username})`; +}); + + +socket.on('calling', ({ data }) => { + console.log('sip calling', data); + document.getElementById('status').innerHTML = 'calling'; +}); + +socket.on('ringing', ({ data }) => { + console.log('sip ringing', data); + document.getElementById('status').innerHTML = 'ringing'; +}); + +socket.on('accepted', ({ data }) => { + console.log('sip accepted', data); + if (sipPeerConnection && data.jsep) { + sipPeerConnection.setRemoteDescription(data.jsep) + .then(() => console.log('remote sdp OK')) + .catch(e => console.log('error setting remote sdp', e)); + } + document.getElementById('status').innerHTML = `in call (${data.username})`; +}); + +socket.on('incoming', async ({ data }) => { + console.log('sip incoming', data); + if (data.jsep) { + incoming = { + offer: data.jsep, + caller: data.username, + }; + } + document.getElementById('status').innerHTML = `incoming (${data.username})`; +}); + +socket.on('hangingup', ({ data }) => { + console.log('sip hangingup', data); + document.getElementById('status').innerHTML = 'hangingup'; +}); + +socket.on('hangup', ({ data }) => { + console.log('hangup', data); + document.getElementById('status').innerHTML = 'hangup'; + stopAllStreams(); + closePC(); +}); + +socket.on('declined', ({ data }) => { + console.log('declined', data); + document.getElementById('status').innerHTML = 'declined'; +}); + +socket.on('missed', ({ data }) => { + console.log('missed', data); + document.getElementById('status').innerHTML = 'missed'; +}); + +socket.on('info', ({ data }) => { + console.log('info', data); +}); + +socket.on('dtmf', ({ data }) => { + console.log('dtmf', data); +}); + +socket.on('sip-error', ({ error }) => { + console.log('sip error', error); + document.getElementById('status').innerHTML = 'error: ' + error; + stopAllStreams(); + closePC(); + //socket.disconnect(); +}); + +socket.on('connect', () => { + console.log('socket connected'); + document.getElementById('status').innerHTML = 'connected'; + connButton.innerText = 'DISCONNECT'; + socket.sendBuffer = []; + scheduleConnection(0.1); +}); + +socket.on('disconnect', () => { + console.log('socket disconnected'); + document.getElementById('status').innerHTML = 'disconnected'; + connButton.innerText = 'CONNECT'; + stopAllStreams(); + closePC(); +}); + +async function doOffer() { + const pc = new RTCPeerConnection({ + 'iceServers': [{ + urls: 'stun:stun.l.google.com:19302' + }], + }); + sipPeerConnection = pc; + + pc.onnegotiationneeded = event => console.log('pc.onnegotiationneeded', event); + pc.onicecandidate = event => trickle({ candidate: event.candidate }); + pc.oniceconnectionstatechange = () => { + if (pc.iceConnectionState === 'failed' || pc.iceConnectionState === 'closed') { + closePC(pc); + } + }; + pc.ontrack = event => { + console.log('pc.ontrack', event); + + event.track.onunmute = evt => { + console.log('track.onunmute', evt); + /* TODO set srcObject in this callback */ + }; + event.track.onmute = evt => { + console.log('track.onmute', evt); + }; + event.track.onended = evt => { + console.log('track.onended', evt); + }; + + const remoteStream = event.streams[0]; + setRemoteAudioElement(remoteStream); + }; + + const localStream = await navigator.mediaDevices.getUserMedia({ audio: true, video: false }); + + console.log('getUserMedia OK'); + + setLocalAudioElement(localStream); + + localStream.getTracks().forEach(track => { + console.log('adding track', track); + pc.addTrack(track, localStream); + }); + + + const offer = await sipPeerConnection.createOffer(); + console.log('create offer OK'); + await sipPeerConnection.setLocalDescription(offer); + console.log('set local sdp OK'); + return offer; +} + +async function doAnswer(offer) { + const pc = new RTCPeerConnection({ + 'iceServers': [{ + urls: 'stun:stun.l.google.com:19302' + }], + }); + sipPeerConnection = pc; + + pc.onnegotiationneeded = event => console.log('pc.onnegotiationneeded', event); + pc.onicecandidate = event => trickle({ candidate: event.candidate }); + pc.oniceconnectionstatechange = () => { + if (pc.iceConnectionState === 'failed' || pc.iceConnectionState === 'closed') { + closePC(pc); + } + }; + pc.ontrack = event => { + console.log('pc.ontrack', event); + + event.track.onunmute = evt => { + console.log('track.onunmute', evt); + /* TODO set srcObject in this callback */ + }; + event.track.onmute = evt => { + console.log('track.onmute', evt); + }; + event.track.onended = evt => { + console.log('track.onended', evt); + }; + + const remoteStream = event.streams[0]; + setRemoteAudioElement(remoteStream); + }; + + const localStream = await navigator.mediaDevices.getUserMedia({ audio: true, video: false }); + + console.log('getUserMedia OK'); + + setLocalAudioElement(localStream); + + localStream.getTracks().forEach(track => { + console.log('adding track', track); + pc.addTrack(track, localStream); + }); + + await sipPeerConnection.setRemoteDescription(offer); + console.log('set remote sdp OK'); + const answer = await sipPeerConnection.createAnswer(); + console.log('create answer OK'); + await pc.setLocalDescription(answer); + console.log('set local sdp OK'); + return answer; +} + +function setLocalAudioElement(stream) { + if (stream) { + const audioStreamElem = document.getElementById('localAudio'); + audioStreamElem.autoplay = false; + audioStreamElem.srcObject = stream; + } +} + +function setRemoteAudioElement(stream) { + if (stream) { + const audioStreamElem = document.getElementById('remoteAudio'); + audioStreamElem.autoplay = true; + audioStreamElem.srcObject = stream; + } +} + +function stopAllStreams() { + incoming = null; + if (localAudio.srcObject) { + localAudio.srcObject.getTracks().forEach(track => track.stop()); + localAudio.srcObject = null; + } + if (remoteAudio.srcObject) { + remoteAudio.srcObject.getTracks().forEach(track => track.stop()); + remoteAudio.srcObject = null; + } +} + +function closePC(pc = sipPeerConnection) { + if (!pc) return; + pc.getSenders().forEach(sender => { + if (sender.track) + sender.track.stop(); + }); + pc.getReceivers().forEach(receiver => { + if (receiver.track) + receiver.track.stop(); + }); + pc.onnegotiationneeded = null; + pc.onicecandidate = null; + pc.oniceconnectionstatechange = null; + pc.ontrack = null; + pc.close(); +} \ No newline at end of file diff --git a/examples/sip/package-lock.json b/examples/sip/package-lock.json new file mode 100644 index 0000000..f4cd691 --- /dev/null +++ b/examples/sip/package-lock.json @@ -0,0 +1,1092 @@ +{ + "name": "janode-sip", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "janode-sip", + "license": "ISC", + "dependencies": { + "express": "^4.13.4", + "socket.io": "^4.2.0" + } + }, + "node_modules/@socket.io/component-emitter": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz", + "integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==", + "license": "MIT" + }, + "node_modules/@types/cookie": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz", + "integrity": "sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==", + "license": "MIT" + }, + "node_modules/@types/cors": { + "version": "2.8.17", + "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.17.tgz", + "integrity": "sha512-8CGDvrBj1zgo2qE+oS3pOCyYNqCPryMWY2bGfwA0dcfopWGgxs+78df0Rs3rc9THP4JkOhLsAa+15VdpAqkcUA==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/node": { + "version": "22.10.7", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.7.tgz", + "integrity": "sha512-V09KvXxFiutGp6B7XkpaDXlNadZxrzajcY50EuoLIpQ6WWYCSvf19lVIazzfIzQvhUN2HjX12spLojTnhuKlGg==", + "license": "MIT", + "dependencies": { + "undici-types": "~6.20.0" + } + }, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "license": "MIT", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", + "license": "MIT" + }, + "node_modules/base64id": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", + "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==", + "license": "MIT", + "engines": { + "node": "^4.5.0 || >= 5.9" + } + }, + "node_modules/body-parser": { + "version": "1.20.3", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", + "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==", + "license": "MIT", + "dependencies": { + "bytes": "3.1.2", + "content-type": "~1.0.5", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "on-finished": "2.4.1", + "qs": "6.13.0", + "raw-body": "2.5.2", + "type-is": "~1.6.18", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.1.tgz", + "integrity": "sha512-BhYE+WDaywFg2TBWYNXAE+8B1ATnThNBqXHP5nQu0jWJdVvY2hvkpyB3qOmtmDePiS5/BDQ8wASEWGMWRG148g==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.3.tgz", + "integrity": "sha512-YTd+6wGlNlPxSuri7Y6X8tY2dmm12UMH66RpKMhiX6rsk5wXXnYgbUcOt8kiS31/AjfoTOvCsE+w8nZQLQnzHA==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "license": "MIT", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz", + "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==", + "license": "MIT" + }, + "node_modules/cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "license": "MIT", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "license": "MIT", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "license": "MIT" + }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/engine.io": { + "version": "6.6.2", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.6.2.tgz", + "integrity": "sha512-gmNvsYi9C8iErnZdVcJnvCpSKbWTt1E8+JZo8b+daLninywUWi5NQ5STSHZ9rFjFO7imNcvb8Pc5pe/wMR5xEw==", + "license": "MIT", + "dependencies": { + "@types/cookie": "^0.4.1", + "@types/cors": "^2.8.12", + "@types/node": ">=10.0.0", + "accepts": "~1.3.4", + "base64id": "2.0.0", + "cookie": "~0.7.2", + "cors": "~2.8.5", + "debug": "~4.3.1", + "engine.io-parser": "~5.2.1", + "ws": "~8.17.1" + }, + "engines": { + "node": ">=10.2.0" + } + }, + "node_modules/engine.io-parser": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.3.tgz", + "integrity": "sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/engine.io/node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/engine.io/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/engine.io/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "license": "MIT" + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/express": { + "version": "4.21.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz", + "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==", + "license": "MIT", + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.20.3", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.7.1", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.3.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "merge-descriptors": "1.0.3", + "methods": "~1.1.2", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.12", + "proxy-addr": "~2.0.7", + "qs": "6.13.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.19.0", + "serve-static": "1.16.2", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/finalhandler": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz", + "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==", + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "statuses": "2.0.1", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-intrinsic": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.7.tgz", + "integrity": "sha512-VW6Pxhsrk0KAOqs3WEd0klDiF/+V7gQOpAvY1jVU/LHmaD/kQO4523aiJuikX/QAKYiW6x8Jh+RJej1almdtCA==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "function-bind": "^1.1.2", + "get-proto": "^1.0.0", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "license": "MIT", + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/merge-descriptors": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", + "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, + "node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.13.3", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.3.tgz", + "integrity": "sha512-kDCGIbxkDSXE3euJZZXzc6to7fCrKHNI/hSRQnRuQ+BWjFNzZwiFF8fj/6o2t2G9/jTj8PSIYTfCLelLZEeRpA==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-to-regexp": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz", + "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==", + "license": "MIT" + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "license": "MIT", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/qs": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", + "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.0.6" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", + "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", + "license": "MIT", + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" + }, + "node_modules/send": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz", + "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==", + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/send/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/serve-static": { + "version": "1.16.2", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz", + "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==", + "license": "MIT", + "dependencies": { + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.19.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "license": "ISC" + }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/socket.io": { + "version": "4.8.1", + "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.8.1.tgz", + "integrity": "sha512-oZ7iUCxph8WYRHHcjBEc9unw3adt5CmSNlppj/5Q4k2RIrhl8Z5yY2Xr4j9zj0+wzVZ0bxmYoGSzKJnRl6A4yg==", + "license": "MIT", + "dependencies": { + "accepts": "~1.3.4", + "base64id": "~2.0.0", + "cors": "~2.8.5", + "debug": "~4.3.2", + "engine.io": "~6.6.0", + "socket.io-adapter": "~2.5.2", + "socket.io-parser": "~4.2.4" + }, + "engines": { + "node": ">=10.2.0" + } + }, + "node_modules/socket.io-adapter": { + "version": "2.5.5", + "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.5.tgz", + "integrity": "sha512-eLDQas5dzPgOWCk9GuuJC2lBqItuhKI4uxGgo9aIV7MYbk2h9Q6uULEh8WBzThoI7l+qU9Ast9fVUmkqPP9wYg==", + "license": "MIT", + "dependencies": { + "debug": "~4.3.4", + "ws": "~8.17.1" + } + }, + "node_modules/socket.io-adapter/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/socket.io-adapter/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/socket.io-parser": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz", + "integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==", + "license": "MIT", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/socket.io-parser/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/socket.io-parser/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/socket.io/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/socket.io/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "license": "MIT", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/undici-types": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", + "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", + "license": "MIT" + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "license": "MIT", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/ws": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", + "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + } + } +} diff --git a/examples/sip/package.json b/examples/sip/package.json new file mode 100644 index 0000000..efb223f --- /dev/null +++ b/examples/sip/package.json @@ -0,0 +1,30 @@ +{ + "name": "janode-sip", + "description": "Janode SIP app", + "type": "module", + "keywords": [ + "janus", + "webrtc", + "meetecho" + ], + "author": { + "name": "Alessandro Toppi", + "email": "atoppi@meetecho.com" + }, + "repository": { + "type": "git", + "url": "https://github.com/meetecho/janode.git" + }, + "license": "ISC", + "private": true, + "main": "src/index.js", + "dependencies": { + "express": "^4.13.4", + "socket.io": "^4.2.0" + }, + "scripts": { + "build": "npm install --omit=dev", + "build-config": "node -e \"var fs = require('fs');fs.createReadStream('src/config.template.js').pipe(fs.createWriteStream('src/config.js'));\"", + "start": "node src/index.js" + } +} \ No newline at end of file diff --git a/examples/sip/src/config.template.js b/examples/sip/src/config.template.js new file mode 100644 index 0000000..a82aafe --- /dev/null +++ b/examples/sip/src/config.template.js @@ -0,0 +1,16 @@ +export default { + janode: { + address: [{ + url: 'ws://127.0.0.1:8188/', + apisecret: 'secret' + }], + // seconds between retries after a connection setup error + retry_time_secs: 10 + }, + web: { + port: 4443, + bind: '0.0.0.0', + key: '/path/to/key.pem', + cert: '/path/to/cert.pem' + } +}; \ No newline at end of file diff --git a/examples/sip/src/index.js b/examples/sip/src/index.js new file mode 100644 index 0000000..392ead8 --- /dev/null +++ b/examples/sip/src/index.js @@ -0,0 +1,330 @@ +'use strict'; + +import { readFileSync } from 'fs'; +import Janode from '../../../src/janode.js'; +import config from './config.js'; +const { janode: janodeConfig, web: serverConfig } = config; + +import { fileURLToPath } from 'url'; +import { dirname, basename } from 'path'; +const __filename = fileURLToPath(import.meta.url); +const __dirname = dirname(__filename); + +const { Logger } = Janode; +const LOG_NS = `[${basename(__filename)}]`; +import SipPlugin from '../../../src/plugins/sip-plugin.js'; + +import express from 'express'; +const app = express(); +const options = { + key: serverConfig.key ? readFileSync(serverConfig.key) : null, + cert: serverConfig.cert ? readFileSync(serverConfig.cert) : null, +}; +import { createServer as createHttpsServer } from 'https'; +import { createServer as createHttpServer } from 'http'; +const httpServer = (options.key && options.cert) ? createHttpsServer(options, app) : createHttpServer(app); +import { Server } from 'socket.io'; +const io = new Server(httpServer); + +const scheduleBackEndConnection = (function () { + let task = null; + + return (function (del = 10) { + if (task) return; + Logger.info(`${LOG_NS} scheduled connection in ${del} seconds`); + task = setTimeout(() => { + initBackEnd() + .then(() => task = null) + .catch(() => { + task = null; + scheduleBackEndConnection(); + }); + }, del * 1000); + }); +})(); + + +let janodeSession; + +(function main() { + + initFrontEnd().catch(({ message }) => Logger.error(`${LOG_NS} failure initializing front-end: ${message}`)); + + scheduleBackEndConnection(1); + +})(); + +async function initBackEnd() { + Logger.info(`${LOG_NS} connecting Janode...`); + let connection; + + try { + connection = await Janode.connect(janodeConfig); + Logger.info(`${LOG_NS} connection with Janus created`); + + connection.once(Janode.EVENT.CONNECTION_CLOSED, () => { + Logger.info(`${LOG_NS} connection with Janus closed`); + }); + + connection.once(Janode.EVENT.CONNECTION_ERROR, ({ message }) => { + Logger.info(`${LOG_NS} connection with Janus error (${message})`); + + replyError(io, 'backend-failure'); + + scheduleBackEndConnection(); + }); + + const session = await connection.create(); + Logger.info(`${LOG_NS} session with Janus established`); + janodeSession = session; + + session.once(Janode.EVENT.SESSION_DESTROYED, () => { + Logger.info(`${LOG_NS} session destroyed`); + }); + } + catch (error) { + Logger.error(`${LOG_NS} Janode setup error (${error.message})`); + if (connection) connection.close().catch(() => { }); + + replyError(io, 'backend-failure'); + + throw error; + } +} + +function initFrontEnd() { + if (httpServer.listening) return Promise.reject(new Error('Server already listening')); + + Logger.info(`${LOG_NS} initializing socketio front end...`); + + io.on('connection', function (socket) { + const remote = `[${socket.request.connection.remoteAddress}:${socket.request.connection.remotePort}]`; + Logger.info(`${LOG_NS} ${remote} connection with client established`); + + let sipHandle; + + // start request from client + socket.on('register', async (evtdata = {}) => { + Logger.info(`${LOG_NS} ${remote} register received`); + const { _id, data: regdata = {} } = evtdata; + + if (!checkSessions(janodeSession, true, socket, evtdata)) return; + + try { + sipHandle = await janodeSession.attach(SipPlugin); + Logger.info(`${LOG_NS} ${remote} sip handle ${sipHandle.id} attached`); + + // custom sipHandle events + sipHandle.on(SipPlugin.EVENT.SIP_REGISTERING, evtdata => { + Logger.info(`${LOG_NS} ${sipHandle.name} sip handle registering`); + replyEvent(socket, 'registering', evtdata); + }); + sipHandle.on(SipPlugin.EVENT.SIP_CALLING, evtdata => { + Logger.info(`${LOG_NS} ${sipHandle.name} sip handle calling`); + replyEvent(socket, 'calling', evtdata); + }); + sipHandle.on(SipPlugin.EVENT.SIP_RINGING, evtdata => { + Logger.info(`${LOG_NS} ${sipHandle.name} sip handle ringing`); + replyEvent(socket, 'ringing', evtdata); + }); + sipHandle.on(SipPlugin.EVENT.SIP_PROCEEDING, evtdata => { + Logger.info(`${LOG_NS} ${sipHandle.name} sip handle proceeding`); + replyEvent(socket, 'proceeding', evtdata); + }); + sipHandle.on(SipPlugin.EVENT.SIP_INCOMING, evtdata => { + Logger.info(`${LOG_NS} ${sipHandle.name} sip handle incoming call`); + replyEvent(socket, 'incoming', evtdata); + }); + sipHandle.on(SipPlugin.EVENT.SIP_HANGUP, evtdata => { + Logger.info(`${LOG_NS} ${sipHandle.name} sip handle hangup`); + replyEvent(socket, 'hangup', evtdata); + }); + sipHandle.on(SipPlugin.EVENT.SIP_MISSED, evtdata => { + Logger.info(`${LOG_NS} ${sipHandle.name} sip handle missed`); + replyEvent(socket, 'missed', evtdata); + }); + sipHandle.on(SipPlugin.EVENT.SIP_INFO, evtdata => { + Logger.info(`${LOG_NS} ${sipHandle.name} sip handle info`); + replyEvent(socket, 'info', evtdata); + }); + sipHandle.on(SipPlugin.EVENT.SIP_DTMF, evtdata => { + Logger.info(`${LOG_NS} ${sipHandle.name} sip handle dtmf`); + replyEvent(socket, 'dtmf', evtdata); + }); + + // generic sipHandle events + sipHandle.on(Janode.EVENT.HANDLE_WEBRTCUP, () => Logger.info(`${LOG_NS} ${sipHandle.name} webrtcup event`)); + sipHandle.on(Janode.EVENT.HANDLE_MEDIA, evtdata => Logger.info(`${LOG_NS} ${sipHandle.name} media event ${JSON.stringify(evtdata)}`)); + sipHandle.on(Janode.EVENT.HANDLE_SLOWLINK, evtdata => Logger.info(`${LOG_NS} ${sipHandle.name} slowlink event ${JSON.stringify(evtdata)}`)); + sipHandle.on(Janode.EVENT.HANDLE_HANGUP, evtdata => Logger.info(`${LOG_NS} ${sipHandle.name} hangup event ${JSON.stringify(evtdata)}`)); + sipHandle.on(Janode.EVENT.HANDLE_DETACHED, () => Logger.info(`${LOG_NS} ${sipHandle.name} detached event`)); + sipHandle.on(Janode.EVENT.HANDLE_TRICKLE, evtdata => Logger.info(`${LOG_NS} ${sipHandle.name} trickle event ${JSON.stringify(evtdata)}`)); + + const response = await sipHandle.register(regdata); + replyEvent(socket, 'registered', response, _id); + + Logger.info(`${LOG_NS} ${remote} registered sent`); + } catch ({ message }) { + Logger.error(`${LOG_NS} ${remote} error registering (${message})`); + replyError(socket, message, regdata, _id); + } + + }); + + // start call + socket.on('call', async (evtdata = {}) => { + Logger.info(`${LOG_NS} ${remote} call received`); + const { _id, data: calldata = {} } = evtdata; + + if (!checkSessions(janodeSession, sipHandle, socket, evtdata)) return; + + try { + const { uri, jsep: offer } = calldata; + const response = await sipHandle.call({ + uri, + jsep: offer, + }); + replyEvent(socket, 'accepted', response, _id); + Logger.info(`${LOG_NS} ${remote} accepted sent`); + } catch ({ message }) { + Logger.error(`${LOG_NS} ${remote} error calling (${message})`); + replyError(socket, message, calldata, _id); + } + }); + + // answer call + socket.on('accept', async (evtdata = {}) => { + Logger.info(`${LOG_NS} ${remote} accept received`); + const { _id, data: acceptdata = {} } = evtdata; + + if (!checkSessions(janodeSession, sipHandle, socket, evtdata)) return; + + try { + const { jsep: answer } = acceptdata; + const response = await sipHandle.accept({ + jsep: answer, + }); + replyEvent(socket, 'accepted', response, _id); + Logger.info(`${LOG_NS} ${remote} accepted sent`); + } catch ({ message }) { + Logger.error(`${LOG_NS} ${remote} error accepting (${message})`); + replyError(socket, message, acceptdata, _id); + } + }); + + socket.on('hangup', async (evtdata = {}) => { + Logger.info(`${LOG_NS} ${remote} hangup received`); + const { _id, data: hangupdata = {} } = evtdata; + + if (!checkSessions(janodeSession, sipHandle, socket, evtdata)) return; + + try { + await sipHandle.sip_hangup(); + replyEvent(socket, 'hangup', {}, _id); + Logger.info(`${LOG_NS} ${remote} hangup sent`); + } catch ({ message }) { + Logger.error(`${LOG_NS} ${remote} error hanging up (${message})`); + replyError(socket, message, hangupdata, _id); + } + }); + + socket.on('decline', async (evtdata = {}) => { + Logger.info(`${LOG_NS} ${remote} decline received`); + const { _id, data: declinedata = {} } = evtdata; + + if (!checkSessions(janodeSession, sipHandle, socket, evtdata)) return; + + try { + await sipHandle.decline(); + replyEvent(socket, 'declined', {}, _id); + Logger.info(`${LOG_NS} ${remote} declined sent`); + } catch ({ message }) { + Logger.error(`${LOG_NS} ${remote} error declining (${message})`); + replyError(socket, message, declinedata, _id); + } + }); + + // trickle candidate from the client + socket.on('trickle', async (evtdata = {}) => { + Logger.info(`${LOG_NS} ${remote} trickle received`); + const { _id, data: trickledata = {} } = evtdata; + + if (!checkSessions(janodeSession, sipHandle, socket, evtdata)) return; + + sipHandle.trickle(trickledata.candidate).catch(({ message }) => replyError(socket, message, trickledata, _id)); + }); + + // trickle complete signal from the client + socket.on('trickle-complete', async (evtdata = {}) => { + Logger.info(`${LOG_NS} ${remote} trickle-complete received`); + const { _id, data: trickledata = {} } = evtdata; + + if (!checkSessions(janodeSession, sipHandle, socket, evtdata)) return; + + sipHandle.trickleComplete().catch(({ message }) => replyError(socket, message, trickledata, _id)); + }); + + // socket disconnection event + socket.on('disconnect', () => { + Logger.info(`${LOG_NS} ${remote} disconnected socket`); + //request sipHandle detach + if (sipHandle) sipHandle.detach().catch(() => { }); + }); + }); + + // disable etag and view caching for all app + app.set('etag', false).set('view cache', false); + + // static content + app.use('/janode', express.static(__dirname + '/../html/', { + etag: false, + lastModified: false, + maxAge: 0, + })); + + // http server binding + return new Promise((resolve, reject) => { + // web server binding + httpServer.listen( + serverConfig.port, + serverConfig.bind, + () => { + Logger.info(`${LOG_NS} server listening on ${(options.key && options.cert) ? 'https' : 'http'}://${serverConfig.bind}:${serverConfig.port}/janode`); + resolve(); + } + ); + + httpServer.on('error', e => reject(e)); + }); +} + +function checkSessions(session, handle, socket, { data, _id }) { + if (!session) { + replyError(socket, 'session-not-available', data, _id); + return false; + } + if (!handle) { + replyError(socket, 'handle-not-available', data, _id); + return false; + } + return true; +} + +function replyEvent(socket, evtname, data, _id) { + const evtdata = { + data, + }; + if (_id) evtdata._id = _id; + + socket.emit(evtname, evtdata); +} + +function replyError(socket, message, request, _id) { + const evtdata = { + error: message, + }; + if (request) evtdata.request = request; + if (_id) evtdata._id = _id; + + socket.emit('sip-error', evtdata); +} \ No newline at end of file diff --git a/examples/streaming/html/.eslintrc.json b/examples/streaming/html/.eslintrc.json deleted file mode 100644 index 5c3f02e..0000000 --- a/examples/streaming/html/.eslintrc.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "env": { - "browser": true - } -} \ No newline at end of file diff --git a/examples/streaming/html/streaming-client.js b/examples/streaming/html/streaming-client.js index 033d2db..92a169a 100644 --- a/examples/streaming/html/streaming-client.js +++ b/examples/streaming/html/streaming-client.js @@ -1,3 +1,4 @@ +/* eslint-disable multiline-comment-style */ /* global io */ 'use strict'; @@ -9,6 +10,8 @@ const remoteVideo = document.getElementById('remoteVideo'); const myStream = parseInt(getURLParameter('stream')) || 1; const myPin = getURLParameter('pin') || null; +let decoder; + const button = document.getElementById('button'); button.onclick = () => { if (socket.connected) @@ -22,7 +25,7 @@ function getId() { } function getURLParameter(name) { - /* eslint-disable-next-line no-sparse-arrays */ + // eslint-disable-next-line no-sparse-arrays return decodeURIComponent((new RegExp('[?|&]' + name + '=' + '([^&;]+?)(&|#|;|$)').exec(location.search) || [, ''])[1].replace(/\+/g, '%20')) || null; } @@ -86,12 +89,15 @@ function _stop() { }); } -function _configure({ audio, video, data }) { +function _configure({ audio, video, data, substream, temporal, fallback }) { socket.emit('configure', { data: { audio, video, data, + substream, + temporal, + fallback, }, _id: getId(), }); @@ -164,7 +170,7 @@ function _stopRec({ id = myStream, secret = 'adminpwd' } = {}) { }); } -function _createMp({ aport, vport, secret = null, pin = null }) { +function _createMp({ audio, video, data, secret = null, pin = null } = {}) { const settings = {}; settings.name = 'test_opus_vp8_' + Date.now(); settings.description = 'this is ' + settings.name; @@ -172,18 +178,26 @@ function _createMp({ aport, vport, secret = null, pin = null }) { settings.pin = pin || null; settings.permanent = false; settings.is_private = false; - settings.audio = { - port: aport, - pt: 111, - rtpmap: 'opus/48000/2', - }; - settings.video = { - port: vport, - pt: 100, - rtpmap: 'VP8/90000', - buffer: true, - }; - settings.data = {}; + if (audio) { + settings.audio = typeof audio === 'object' ? audio : {}; + settings.audio.port = settings.audio.port || 0; + settings.audio.pt = settings.audio.pt || 111; + settings.audio.rtpmap = settings.audio.rtpmap || 'opus/48000/2'; + } + if (video) { + settings.video = typeof video === 'object' ? video : {}; + settings.video.port = settings.video.port || 0; + //settings.video.port2 = settings.video.port2 || 0; + //settings.video.port3 = settings.video.port3 || 0; + //setting.video.rtcpport = setting.video.rtcpport || 0; + settings.video.pt = settings.video.pt || 100; + settings.video.rtpmap = settings.video.rtpmap || 'VP8/90000'; + //settings.video.buffer = settings.video.buffer || false; + } + if (data) { + settings.data = typeof data === 'object' ? data : {}; + //settings.data.buffer = settings.data.buffer || false; + } socket.emit('create', { data: settings, _id: getId(), @@ -261,6 +275,31 @@ socket.on('created', ({ data }) => console.log('mountpoint created', data)); socket.on('destroyed', ({ data }) => console.log('mountpoint destroyed', data)); +function _setupDataChannelCallbacks(channel, isLocal) { + const dcLogPrefix = `${isLocal ? 'Local' : 'Remote'} Datachannel ${channel.id} (${channel.label})`; + + channel.onopen = (_event) => { + console.log(`${dcLogPrefix} open`); + }; + + channel.onmessage = (event) => { + decoder = decoder || new TextDecoder(); // initialize the decoder + let decodedData = event.data; + if (event.data?.byteLength) { // is ArrayBuffer + decodedData = decoder.decode(event.data); + } + console.log(`${dcLogPrefix} received`, decodedData); + }; + + channel.onclose = () => { + console.log(`${dcLogPrefix} closed`); + }; + + channel.onerror = (error) => { + console.error(`${dcLogPrefix} error`, error); + }; +} + async function doAnswer(offer) { if (!streamingPeerConnection) { const pc = new RTCPeerConnection({ @@ -270,6 +309,16 @@ async function doAnswer(offer) { //'sdpSemantics': 'unified-plan', }); + // inspect the offer.sdp for m=application lines before creating the DataChannel + if (/m=application [1-9]\d*/.test(offer.sdp)) { + const localChannel = pc.createDataChannel('JanusDataChannel'); + _setupDataChannelCallbacks(localChannel, true); + + pc.ondatachannel = (event) => { + const remoteChannel = event.channel; + _setupDataChannelCallbacks(remoteChannel, false); + }; + } pc.onnegotiationneeded = event => console.log('pc.onnegotiationneeded', event); pc.onicecandidate = event => trickle({ candidate: event.candidate }); pc.oniceconnectionstatechange = () => { @@ -331,4 +380,4 @@ function closePC(pc = streamingPeerConnection) { pc.ontrack = null; pc.close(); if (pc === streamingPeerConnection) streamingPeerConnection = null; -} \ No newline at end of file +} diff --git a/examples/streaming/package-lock.json b/examples/streaming/package-lock.json index f3f0d1a..a447187 100644 --- a/examples/streaming/package-lock.json +++ b/examples/streaming/package-lock.json @@ -1,6 +1,6 @@ { "name": "janode-streaming", - "lockfileVersion": 2, + "lockfileVersion": 3, "requires": true, "packages": { "": { @@ -11,33 +11,44 @@ "socket.io": "^4.2.0" } }, - "node_modules/@types/component-emitter": { - "version": "1.2.11", - "resolved": "https://registry.npmjs.org/@types/component-emitter/-/component-emitter-1.2.11.tgz", - "integrity": "sha512-SRXjM+tfsSlA9VuG8hGO2nft2p8zjXCK1VcC6N4NXbBbYbSia9kzCChYQajIjzIqOOOuh5Ock6MmV2oux4jDZQ==" + "node_modules/@socket.io/component-emitter": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz", + "integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==", + "license": "MIT" }, "node_modules/@types/cookie": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz", - "integrity": "sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==" + "integrity": "sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==", + "license": "MIT" }, "node_modules/@types/cors": { - "version": "2.8.12", - "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.12.tgz", - "integrity": "sha512-vt+kDhq/M2ayberEtJcIN/hxXy1Pk+59g2FV/ZQceeaTyCtCucjL2Q7FXlFjtWn4n15KCr1NE2lNNFhp0lEThw==" + "version": "2.8.17", + "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.17.tgz", + "integrity": "sha512-8CGDvrBj1zgo2qE+oS3pOCyYNqCPryMWY2bGfwA0dcfopWGgxs+78df0Rs3rc9THP4JkOhLsAa+15VdpAqkcUA==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } }, "node_modules/@types/node": { - "version": "16.11.10", - "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.10.tgz", - "integrity": "sha512-3aRnHa1KlOEEhJ6+CvyHKK5vE9BcLGjtUpwvqYLRvYNQKMfabu3BwfJaA/SLW8dxe28LsNDjtHwePTuzn3gmOA==" + "version": "22.10.7", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.7.tgz", + "integrity": "sha512-V09KvXxFiutGp6B7XkpaDXlNadZxrzajcY50EuoLIpQ6WWYCSvf19lVIazzfIzQvhUN2HjX12spLojTnhuKlGg==", + "license": "MIT", + "dependencies": { + "undici-types": "~6.20.0" + } }, "node_modules/accepts": { - "version": "1.3.7", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", - "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "license": "MIT", "dependencies": { - "mime-types": "~2.1.24", - "negotiator": "0.6.2" + "mime-types": "~2.1.34", + "negotiator": "0.6.3" }, "engines": { "node": ">= 0.6" @@ -46,72 +57,106 @@ "node_modules/array-flatten": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", + "license": "MIT" }, "node_modules/base64id": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==", + "license": "MIT", "engines": { "node": "^4.5.0 || >= 5.9" } }, "node_modules/body-parser": { - "version": "1.19.0", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", - "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", + "version": "1.20.3", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", + "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==", + "license": "MIT", "dependencies": { - "bytes": "3.1.0", - "content-type": "~1.0.4", + "bytes": "3.1.2", + "content-type": "~1.0.5", "debug": "2.6.9", - "depd": "~1.1.2", - "http-errors": "1.7.2", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", "iconv-lite": "0.4.24", - "on-finished": "~2.3.0", - "qs": "6.7.0", - "raw-body": "2.4.0", - "type-is": "~1.6.17" + "on-finished": "2.4.1", + "qs": "6.13.0", + "raw-body": "2.5.2", + "type-is": "~1.6.18", + "unpipe": "1.0.0" }, "engines": { - "node": ">= 0.8" + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" } }, "node_modules/bytes": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", - "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "license": "MIT", "engines": { "node": ">= 0.8" } }, - "node_modules/component-emitter": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", - "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==" + "node_modules/call-bind-apply-helpers": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.1.tgz", + "integrity": "sha512-BhYE+WDaywFg2TBWYNXAE+8B1ATnThNBqXHP5nQu0jWJdVvY2hvkpyB3qOmtmDePiS5/BDQ8wASEWGMWRG148g==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.3.tgz", + "integrity": "sha512-YTd+6wGlNlPxSuri7Y6X8tY2dmm12UMH66RpKMhiX6rsk5wXXnYgbUcOt8kiS31/AjfoTOvCsE+w8nZQLQnzHA==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, "node_modules/content-disposition": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", - "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "license": "MIT", "dependencies": { - "safe-buffer": "5.1.2" + "safe-buffer": "5.2.1" }, "engines": { "node": ">= 0.6" } }, "node_modules/content-type": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", - "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "license": "MIT", "engines": { "node": ">= 0.6" } }, "node_modules/cookie": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", - "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==", + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz", + "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==", + "license": "MIT", "engines": { "node": ">= 0.6" } @@ -119,12 +164,14 @@ "node_modules/cookie-signature": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==", + "license": "MIT" }, "node_modules/cors": { "version": "2.8.5", "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "license": "MIT", "dependencies": { "object-assign": "^4", "vary": "^1" @@ -137,78 +184,105 @@ "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", "dependencies": { "ms": "2.0.0" } }, "node_modules/depd": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", - "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "license": "MIT", "engines": { - "node": ">= 0.6" + "node": ">= 0.8" } }, "node_modules/destroy": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", - "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "license": "MIT", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } }, "node_modules/ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "license": "MIT" }, "node_modules/encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "license": "MIT", "engines": { "node": ">= 0.8" } }, "node_modules/engine.io": { - "version": "6.1.3", - "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.1.3.tgz", - "integrity": "sha512-rqs60YwkvWTLLnfazqgZqLa/aKo+9cueVfEi/dZ8PyGyaf8TLOxj++4QMIgeG3Gn0AhrWiFXvghsoY9L9h25GA==", + "version": "6.6.2", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.6.2.tgz", + "integrity": "sha512-gmNvsYi9C8iErnZdVcJnvCpSKbWTt1E8+JZo8b+daLninywUWi5NQ5STSHZ9rFjFO7imNcvb8Pc5pe/wMR5xEw==", + "license": "MIT", "dependencies": { "@types/cookie": "^0.4.1", "@types/cors": "^2.8.12", "@types/node": ">=10.0.0", "accepts": "~1.3.4", "base64id": "2.0.0", - "cookie": "~0.4.1", + "cookie": "~0.7.2", "cors": "~2.8.5", "debug": "~4.3.1", - "engine.io-parser": "~5.0.3", - "ws": "~8.2.3" + "engine.io-parser": "~5.2.1", + "ws": "~8.17.1" }, "engines": { - "node": ">=10.0.0" + "node": ">=10.2.0" } }, "node_modules/engine.io-parser": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.0.4.tgz", - "integrity": "sha512-+nVFp+5z1E3HcToEnO7ZIj3g+3k9389DvWtvJZz0T6/eOCPIyyxehFcedoYrZQrp0LgQbD9pPXhpMBKMd5QURg==", + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.3.tgz", + "integrity": "sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==", + "license": "MIT", "engines": { "node": ">=10.0.0" } }, "node_modules/engine.io/node_modules/cookie": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz", - "integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==", + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "license": "MIT", "engines": { "node": ">= 0.6" } }, "node_modules/engine.io/node_modules/debug": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", - "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "license": "MIT", "dependencies": { - "ms": "2.1.2" + "ms": "^2.1.3" }, "engines": { "node": ">=6.0" @@ -220,74 +294,114 @@ } }, "node_modules/engine.io/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } }, "node_modules/escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "license": "MIT" }, "node_modules/etag": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "license": "MIT", "engines": { "node": ">= 0.6" } }, "node_modules/express": { - "version": "4.17.1", - "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", - "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", + "version": "4.21.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz", + "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==", + "license": "MIT", "dependencies": { - "accepts": "~1.3.7", + "accepts": "~1.3.8", "array-flatten": "1.1.1", - "body-parser": "1.19.0", - "content-disposition": "0.5.3", + "body-parser": "1.20.3", + "content-disposition": "0.5.4", "content-type": "~1.0.4", - "cookie": "0.4.0", + "cookie": "0.7.1", "cookie-signature": "1.0.6", "debug": "2.6.9", - "depd": "~1.1.2", - "encodeurl": "~1.0.2", + "depd": "2.0.0", + "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "etag": "~1.8.1", - "finalhandler": "~1.1.2", + "finalhandler": "1.3.1", "fresh": "0.5.2", - "merge-descriptors": "1.0.1", + "http-errors": "2.0.0", + "merge-descriptors": "1.0.3", "methods": "~1.1.2", - "on-finished": "~2.3.0", + "on-finished": "2.4.1", "parseurl": "~1.3.3", - "path-to-regexp": "0.1.7", - "proxy-addr": "~2.0.5", - "qs": "6.7.0", + "path-to-regexp": "0.1.12", + "proxy-addr": "~2.0.7", + "qs": "6.13.0", "range-parser": "~1.2.1", - "safe-buffer": "5.1.2", - "send": "0.17.1", - "serve-static": "1.14.1", - "setprototypeof": "1.1.1", - "statuses": "~1.5.0", + "safe-buffer": "5.2.1", + "send": "0.19.0", + "serve-static": "1.16.2", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", "type-is": "~1.6.18", "utils-merge": "1.0.1", "vary": "~1.1.2" }, "engines": { "node": ">= 0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/finalhandler": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", - "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz", + "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==", + "license": "MIT", "dependencies": { "debug": "2.6.9", - "encodeurl": "~1.0.2", + "encodeurl": "~2.0.0", "escape-html": "~1.0.3", - "on-finished": "~2.3.0", + "on-finished": "2.4.1", "parseurl": "~1.3.3", - "statuses": "~1.5.0", + "statuses": "2.0.1", "unpipe": "~1.0.0" }, "engines": { @@ -298,6 +412,7 @@ "version": "0.2.0", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "license": "MIT", "engines": { "node": ">= 0.6" } @@ -305,30 +420,115 @@ "node_modules/fresh": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "license": "MIT", "engines": { "node": ">= 0.6" } }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-intrinsic": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.7.tgz", + "integrity": "sha512-VW6Pxhsrk0KAOqs3WEd0klDiF/+V7gQOpAvY1jVU/LHmaD/kQO4523aiJuikX/QAKYiW6x8Jh+RJej1almdtCA==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "function-bind": "^1.1.2", + "get-proto": "^1.0.0", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/http-errors": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", - "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "license": "MIT", "dependencies": { - "depd": "~1.1.2", - "inherits": "2.0.3", - "setprototypeof": "1.1.1", - "statuses": ">= 1.5.0 < 2", - "toidentifier": "1.0.0" + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" }, "engines": { - "node": ">= 0.6" + "node": ">= 0.8" } }, "node_modules/iconv-lite": { "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "license": "MIT", "dependencies": { "safer-buffer": ">= 2.1.2 < 3" }, @@ -337,35 +537,52 @@ } }, "node_modules/inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" }, "node_modules/ipaddr.js": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "license": "MIT", "engines": { "node": ">= 0.10" } }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "license": "MIT", "engines": { "node": ">= 0.6" } }, "node_modules/merge-descriptors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", - "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", + "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, "node_modules/methods": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "license": "MIT", "engines": { "node": ">= 0.6" } @@ -374,6 +591,7 @@ "version": "1.6.0", "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "license": "MIT", "bin": { "mime": "cli.js" }, @@ -382,19 +600,21 @@ } }, "node_modules/mime-db": { - "version": "1.51.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.51.0.tgz", - "integrity": "sha512-5y8A56jg7XVQx2mbv1lu49NR4dokRnhZYTtL+KGfaa27uq4pSTXkwQkFJl4pkRMyNFz/EtYDSkiiEHx3F7UN6g==", + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", "engines": { "node": ">= 0.6" } }, "node_modules/mime-types": { - "version": "2.1.34", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.34.tgz", - "integrity": "sha512-6cP692WwGIs9XXdOO4++N+7qjqv0rqxxVvJ3VHPh/Sc9mVZcQP+ZGhkKiTvWMQRr2tbHkJP/Yn7Y0npb3ZBs4A==", + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", "dependencies": { - "mime-db": "1.51.0" + "mime-db": "1.52.0" }, "engines": { "node": ">= 0.6" @@ -403,12 +623,14 @@ "node_modules/ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" }, "node_modules/negotiator": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", - "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==", + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "license": "MIT", "engines": { "node": ">= 0.6" } @@ -416,15 +638,29 @@ "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "license": "MIT", "engines": { "node": ">=0.10.0" } }, + "node_modules/object-inspect": { + "version": "1.13.3", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.3.tgz", + "integrity": "sha512-kDCGIbxkDSXE3euJZZXzc6to7fCrKHNI/hSRQnRuQ+BWjFNzZwiFF8fj/6o2t2G9/jTj8PSIYTfCLelLZEeRpA==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/on-finished": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", - "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "license": "MIT", "dependencies": { "ee-first": "1.1.1" }, @@ -436,19 +672,22 @@ "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "license": "MIT", "engines": { "node": ">= 0.8" } }, "node_modules/path-to-regexp": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", - "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz", + "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==", + "license": "MIT" }, "node_modules/proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "license": "MIT", "dependencies": { "forwarded": "0.2.0", "ipaddr.js": "1.9.1" @@ -458,28 +697,37 @@ } }, "node_modules/qs": { - "version": "6.7.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", - "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==", + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", + "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.0.6" + }, "engines": { "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/range-parser": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "license": "MIT", "engines": { "node": ">= 0.6" } }, "node_modules/raw-body": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", - "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", + "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", + "license": "MIT", "dependencies": { - "bytes": "3.1.0", - "http-errors": "1.7.2", + "bytes": "3.1.2", + "http-errors": "2.0.0", "iconv-lite": "0.4.24", "unpipe": "1.0.0" }, @@ -488,90 +736,221 @@ } }, "node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" }, "node_modules/safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" }, "node_modules/send": { - "version": "0.17.1", - "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", - "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==", + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz", + "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==", + "license": "MIT", "dependencies": { "debug": "2.6.9", - "depd": "~1.1.2", - "destroy": "~1.0.4", + "depd": "2.0.0", + "destroy": "1.2.0", "encodeurl": "~1.0.2", "escape-html": "~1.0.3", "etag": "~1.8.1", "fresh": "0.5.2", - "http-errors": "~1.7.2", + "http-errors": "2.0.0", "mime": "1.6.0", - "ms": "2.1.1", - "on-finished": "~2.3.0", + "ms": "2.1.3", + "on-finished": "2.4.1", "range-parser": "~1.2.1", - "statuses": "~1.5.0" + "statuses": "2.0.1" }, "engines": { "node": ">= 0.8.0" } }, + "node_modules/send/node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/send/node_modules/ms": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", - "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" }, "node_modules/serve-static": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", - "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==", + "version": "1.16.2", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz", + "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==", + "license": "MIT", "dependencies": { - "encodeurl": "~1.0.2", + "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "parseurl": "~1.3.3", - "send": "0.17.1" + "send": "0.19.0" }, "engines": { "node": ">= 0.8.0" } }, "node_modules/setprototypeof": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", - "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "license": "ISC" + }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, "node_modules/socket.io": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.4.0.tgz", - "integrity": "sha512-bnpJxswR9ov0Bw6ilhCvO38/1WPtE3eA2dtxi2Iq4/sFebiDJQzgKNYA7AuVVdGW09nrESXd90NbZqtDd9dzRQ==", + "version": "4.8.1", + "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.8.1.tgz", + "integrity": "sha512-oZ7iUCxph8WYRHHcjBEc9unw3adt5CmSNlppj/5Q4k2RIrhl8Z5yY2Xr4j9zj0+wzVZ0bxmYoGSzKJnRl6A4yg==", + "license": "MIT", "dependencies": { "accepts": "~1.3.4", "base64id": "~2.0.0", + "cors": "~2.8.5", "debug": "~4.3.2", - "engine.io": "~6.1.0", - "socket.io-adapter": "~2.3.3", - "socket.io-parser": "~4.0.4" + "engine.io": "~6.6.0", + "socket.io-adapter": "~2.5.2", + "socket.io-parser": "~4.2.4" }, "engines": { - "node": ">=10.0.0" + "node": ">=10.2.0" } }, "node_modules/socket.io-adapter": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.3.3.tgz", - "integrity": "sha512-Qd/iwn3VskrpNO60BeRyCyr8ZWw9CPZyitW4AQwmRZ8zCiyDiL+znRnWX6tDHXnWn1sJrM1+b6Mn6wEDJJ4aYQ==" + "version": "2.5.5", + "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.5.tgz", + "integrity": "sha512-eLDQas5dzPgOWCk9GuuJC2lBqItuhKI4uxGgo9aIV7MYbk2h9Q6uULEh8WBzThoI7l+qU9Ast9fVUmkqPP9wYg==", + "license": "MIT", + "dependencies": { + "debug": "~4.3.4", + "ws": "~8.17.1" + } + }, + "node_modules/socket.io-adapter/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/socket.io-adapter/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" }, "node_modules/socket.io-parser": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.0.4.tgz", - "integrity": "sha512-t+b0SS+IxG7Rxzda2EVvyBZbvFPBCjJoyHuE0P//7OAsN23GItzDRdWa6ALxZI/8R5ygK7jAR6t028/z+7295g==", + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz", + "integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==", + "license": "MIT", "dependencies": { - "@types/component-emitter": "^1.2.10", - "component-emitter": "~1.3.0", + "@socket.io/component-emitter": "~3.1.0", "debug": "~4.3.1" }, "engines": { @@ -579,11 +958,12 @@ } }, "node_modules/socket.io-parser/node_modules/debug": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", - "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "license": "MIT", "dependencies": { - "ms": "2.1.2" + "ms": "^2.1.3" }, "engines": { "node": ">=6.0" @@ -595,16 +975,18 @@ } }, "node_modules/socket.io-parser/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" }, "node_modules/socket.io/node_modules/debug": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", - "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "license": "MIT", "dependencies": { - "ms": "2.1.2" + "ms": "^2.1.3" }, "engines": { "node": ">=6.0" @@ -616,22 +998,25 @@ } }, "node_modules/socket.io/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" }, "node_modules/statuses": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", - "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "license": "MIT", "engines": { - "node": ">= 0.6" + "node": ">= 0.8" } }, "node_modules/toidentifier": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", - "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "license": "MIT", "engines": { "node": ">=0.6" } @@ -640,6 +1025,7 @@ "version": "1.6.18", "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "license": "MIT", "dependencies": { "media-typer": "0.3.0", "mime-types": "~2.1.24" @@ -648,10 +1034,17 @@ "node": ">= 0.6" } }, + "node_modules/undici-types": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", + "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", + "license": "MIT" + }, "node_modules/unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "license": "MIT", "engines": { "node": ">= 0.8" } @@ -659,7 +1052,8 @@ "node_modules/utils-merge": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "license": "MIT", "engines": { "node": ">= 0.4.0" } @@ -667,21 +1061,23 @@ "node_modules/vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "license": "MIT", "engines": { "node": ">= 0.8" } }, "node_modules/ws": { - "version": "8.2.3", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.2.3.tgz", - "integrity": "sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA==", + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", + "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", + "license": "MIT", "engines": { "node": ">=10.0.0" }, "peerDependencies": { "bufferutil": "^4.0.1", - "utf-8-validate": "^5.0.2" + "utf-8-validate": ">=5.0.2" }, "peerDependenciesMeta": { "bufferutil": { @@ -692,523 +1088,5 @@ } } } - }, - "dependencies": { - "@types/component-emitter": { - "version": "1.2.11", - "resolved": "https://registry.npmjs.org/@types/component-emitter/-/component-emitter-1.2.11.tgz", - "integrity": "sha512-SRXjM+tfsSlA9VuG8hGO2nft2p8zjXCK1VcC6N4NXbBbYbSia9kzCChYQajIjzIqOOOuh5Ock6MmV2oux4jDZQ==" - }, - "@types/cookie": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz", - "integrity": "sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==" - }, - "@types/cors": { - "version": "2.8.12", - "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.12.tgz", - "integrity": "sha512-vt+kDhq/M2ayberEtJcIN/hxXy1Pk+59g2FV/ZQceeaTyCtCucjL2Q7FXlFjtWn4n15KCr1NE2lNNFhp0lEThw==" - }, - "@types/node": { - "version": "16.11.10", - "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.10.tgz", - "integrity": "sha512-3aRnHa1KlOEEhJ6+CvyHKK5vE9BcLGjtUpwvqYLRvYNQKMfabu3BwfJaA/SLW8dxe28LsNDjtHwePTuzn3gmOA==" - }, - "accepts": { - "version": "1.3.7", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", - "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", - "requires": { - "mime-types": "~2.1.24", - "negotiator": "0.6.2" - } - }, - "array-flatten": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" - }, - "base64id": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", - "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==" - }, - "body-parser": { - "version": "1.19.0", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", - "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", - "requires": { - "bytes": "3.1.0", - "content-type": "~1.0.4", - "debug": "2.6.9", - "depd": "~1.1.2", - "http-errors": "1.7.2", - "iconv-lite": "0.4.24", - "on-finished": "~2.3.0", - "qs": "6.7.0", - "raw-body": "2.4.0", - "type-is": "~1.6.17" - } - }, - "bytes": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", - "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==" - }, - "component-emitter": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", - "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==" - }, - "content-disposition": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", - "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", - "requires": { - "safe-buffer": "5.1.2" - } - }, - "content-type": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", - "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" - }, - "cookie": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", - "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==" - }, - "cookie-signature": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" - }, - "cors": { - "version": "2.8.5", - "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", - "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", - "requires": { - "object-assign": "^4", - "vary": "^1" - } - }, - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - } - }, - "depd": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", - "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" - }, - "destroy": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", - "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" - }, - "ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" - }, - "encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" - }, - "engine.io": { - "version": "6.1.3", - "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.1.3.tgz", - "integrity": "sha512-rqs60YwkvWTLLnfazqgZqLa/aKo+9cueVfEi/dZ8PyGyaf8TLOxj++4QMIgeG3Gn0AhrWiFXvghsoY9L9h25GA==", - "requires": { - "@types/cookie": "^0.4.1", - "@types/cors": "^2.8.12", - "@types/node": ">=10.0.0", - "accepts": "~1.3.4", - "base64id": "2.0.0", - "cookie": "~0.4.1", - "cors": "~2.8.5", - "debug": "~4.3.1", - "engine.io-parser": "~5.0.3", - "ws": "~8.2.3" - }, - "dependencies": { - "cookie": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz", - "integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==" - }, - "debug": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", - "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", - "requires": { - "ms": "2.1.2" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - } - } - }, - "engine.io-parser": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.0.4.tgz", - "integrity": "sha512-+nVFp+5z1E3HcToEnO7ZIj3g+3k9389DvWtvJZz0T6/eOCPIyyxehFcedoYrZQrp0LgQbD9pPXhpMBKMd5QURg==" - }, - "escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" - }, - "etag": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" - }, - "express": { - "version": "4.17.1", - "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", - "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", - "requires": { - "accepts": "~1.3.7", - "array-flatten": "1.1.1", - "body-parser": "1.19.0", - "content-disposition": "0.5.3", - "content-type": "~1.0.4", - "cookie": "0.4.0", - "cookie-signature": "1.0.6", - "debug": "2.6.9", - "depd": "~1.1.2", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "finalhandler": "~1.1.2", - "fresh": "0.5.2", - "merge-descriptors": "1.0.1", - "methods": "~1.1.2", - "on-finished": "~2.3.0", - "parseurl": "~1.3.3", - "path-to-regexp": "0.1.7", - "proxy-addr": "~2.0.5", - "qs": "6.7.0", - "range-parser": "~1.2.1", - "safe-buffer": "5.1.2", - "send": "0.17.1", - "serve-static": "1.14.1", - "setprototypeof": "1.1.1", - "statuses": "~1.5.0", - "type-is": "~1.6.18", - "utils-merge": "1.0.1", - "vary": "~1.1.2" - } - }, - "finalhandler": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", - "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", - "requires": { - "debug": "2.6.9", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "on-finished": "~2.3.0", - "parseurl": "~1.3.3", - "statuses": "~1.5.0", - "unpipe": "~1.0.0" - } - }, - "forwarded": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", - "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==" - }, - "fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" - }, - "http-errors": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", - "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", - "requires": { - "depd": "~1.1.2", - "inherits": "2.0.3", - "setprototypeof": "1.1.1", - "statuses": ">= 1.5.0 < 2", - "toidentifier": "1.0.0" - } - }, - "iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "requires": { - "safer-buffer": ">= 2.1.2 < 3" - } - }, - "inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" - }, - "ipaddr.js": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" - }, - "media-typer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" - }, - "merge-descriptors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", - "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" - }, - "methods": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" - }, - "mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" - }, - "mime-db": { - "version": "1.51.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.51.0.tgz", - "integrity": "sha512-5y8A56jg7XVQx2mbv1lu49NR4dokRnhZYTtL+KGfaa27uq4pSTXkwQkFJl4pkRMyNFz/EtYDSkiiEHx3F7UN6g==" - }, - "mime-types": { - "version": "2.1.34", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.34.tgz", - "integrity": "sha512-6cP692WwGIs9XXdOO4++N+7qjqv0rqxxVvJ3VHPh/Sc9mVZcQP+ZGhkKiTvWMQRr2tbHkJP/Yn7Y0npb3ZBs4A==", - "requires": { - "mime-db": "1.51.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - }, - "negotiator": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", - "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" - }, - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" - }, - "on-finished": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", - "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", - "requires": { - "ee-first": "1.1.1" - } - }, - "parseurl": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" - }, - "path-to-regexp": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", - "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" - }, - "proxy-addr": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", - "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", - "requires": { - "forwarded": "0.2.0", - "ipaddr.js": "1.9.1" - } - }, - "qs": { - "version": "6.7.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", - "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==" - }, - "range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" - }, - "raw-body": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", - "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", - "requires": { - "bytes": "3.1.0", - "http-errors": "1.7.2", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" - } - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" - }, - "send": { - "version": "0.17.1", - "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", - "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==", - "requires": { - "debug": "2.6.9", - "depd": "~1.1.2", - "destroy": "~1.0.4", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "fresh": "0.5.2", - "http-errors": "~1.7.2", - "mime": "1.6.0", - "ms": "2.1.1", - "on-finished": "~2.3.0", - "range-parser": "~1.2.1", - "statuses": "~1.5.0" - }, - "dependencies": { - "ms": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", - "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" - } - } - }, - "serve-static": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", - "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==", - "requires": { - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "parseurl": "~1.3.3", - "send": "0.17.1" - } - }, - "setprototypeof": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", - "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" - }, - "socket.io": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.4.0.tgz", - "integrity": "sha512-bnpJxswR9ov0Bw6ilhCvO38/1WPtE3eA2dtxi2Iq4/sFebiDJQzgKNYA7AuVVdGW09nrESXd90NbZqtDd9dzRQ==", - "requires": { - "accepts": "~1.3.4", - "base64id": "~2.0.0", - "debug": "~4.3.2", - "engine.io": "~6.1.0", - "socket.io-adapter": "~2.3.3", - "socket.io-parser": "~4.0.4" - }, - "dependencies": { - "debug": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", - "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", - "requires": { - "ms": "2.1.2" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - } - } - }, - "socket.io-adapter": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.3.3.tgz", - "integrity": "sha512-Qd/iwn3VskrpNO60BeRyCyr8ZWw9CPZyitW4AQwmRZ8zCiyDiL+znRnWX6tDHXnWn1sJrM1+b6Mn6wEDJJ4aYQ==" - }, - "socket.io-parser": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.0.4.tgz", - "integrity": "sha512-t+b0SS+IxG7Rxzda2EVvyBZbvFPBCjJoyHuE0P//7OAsN23GItzDRdWa6ALxZI/8R5ygK7jAR6t028/z+7295g==", - "requires": { - "@types/component-emitter": "^1.2.10", - "component-emitter": "~1.3.0", - "debug": "~4.3.1" - }, - "dependencies": { - "debug": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", - "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", - "requires": { - "ms": "2.1.2" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - } - } - }, - "statuses": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", - "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" - }, - "toidentifier": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", - "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==" - }, - "type-is": { - "version": "1.6.18", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", - "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", - "requires": { - "media-typer": "0.3.0", - "mime-types": "~2.1.24" - } - }, - "unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" - }, - "utils-merge": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" - }, - "vary": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" - }, - "ws": { - "version": "8.2.3", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.2.3.tgz", - "integrity": "sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA==", - "requires": {} - } } } diff --git a/examples/streaming/src/index.js b/examples/streaming/src/index.js index 7d9988d..94a6aca 100644 --- a/examples/streaming/src/index.js +++ b/examples/streaming/src/index.js @@ -369,10 +369,6 @@ function initFrontEnd() { if (!checkSessions(janodeSession, janodeManagerHandle, socket, evtdata)) return; - mpdata.audio = (typeof mpdata.audio === 'object' && mpdata.audio) ? mpdata.audio : null; - mpdata.video = (typeof mpdata.video === 'object' && mpdata.video) ? mpdata.video : null; - mpdata.data = (typeof mpdata.data === 'object' && mpdata.data) ? mpdata.data : null; - try { const created = await janodeManagerHandle.createRtpMountpoint(mpdata); replyEvent(socket, 'created', created, _id); diff --git a/examples/videoroom-ms/html/index.html b/examples/videoroom-ms/html/index.html new file mode 100644 index 0000000..f0590f9 --- /dev/null +++ b/examples/videoroom-ms/html/index.html @@ -0,0 +1,29 @@ + + + + + VideoRoom Socket.IO Janode + + + + + +

+
+ --- VIDEOROOM () --- +

+ -- LOCALS -- +

+
+

+ -- REMOTES -- +

+
+
+ + + + + + \ No newline at end of file diff --git a/examples/videoroom-ms/html/videoroom-ms-client.js b/examples/videoroom-ms/html/videoroom-ms-client.js new file mode 100644 index 0000000..102eb54 --- /dev/null +++ b/examples/videoroom-ms/html/videoroom-ms-client.js @@ -0,0 +1,952 @@ +/* eslint-disable no-sparse-arrays */ +/* global io */ + +'use strict'; + +const RTCPeerConnection = (window.RTCPeerConnection || window.webkitRTCPeerConnection || window.mozRTCPeerConnection).bind(window); + +let pubPc, subPc; +let subscriptions = new Map(); +const pendingOfferMap = new Map(); +const myRoom = getURLParameter('room') ? parseInt(getURLParameter('room')) : (getURLParameter('room_str') || 1234); +const randName = ('John_Doe_' + Math.floor(10000 * Math.random())); +const myName = getURLParameter('name') || randName; +let myFeed; + +const button = document.getElementById('button'); +button.onclick = () => { + if (socket.connected) + socket.disconnect(); + else + socket.connect(); +}; + +function getId() { + return Math.floor(Number.MAX_SAFE_INTEGER * Math.random()); +} + +function getURLParameter(name) { + return decodeURIComponent((new RegExp('[?|&]' + name + '=' + '([^&;]+?)(&|#|;|$)').exec(location.search) || [, ''])[1].replace(/\+/g, '%20')) || null; +} + +const scheduleConnection = (function () { + let task = null; + const delay = 5000; + + return (function (secs) { + if (task) return; + const timeout = secs * 1000 || delay; + console.log('scheduled joining in ' + timeout + ' ms'); + task = setTimeout(() => { + join(); + task = null; + }, timeout); + }); +})(); + +const socket = io({ + rejectUnauthorized: false, + autoConnect: false, + reconnection: false, +}); + +function join({ room = myRoom, display = myName, token = null } = {}) { + const joinData = { + room, + display, + token, + }; + + socket.emit('join', { + data: joinData, + _id: getId(), + }); +} + +function subscribe({ streams, room = myRoom }) { + const subscribeData = { + room, + streams, + }; + + socket.emit('subscribe', { + data: subscribeData, + _id: getId(), + }); +} + +function subscribeTo(publishers, room = myRoom) { + const newStreams = []; + publishers.forEach(({ feed, streams }) => { + streams.forEach(s => { + if (!hasFeedMidSubscription(feed, s.mid)) { + newStreams.push({ + feed, + mid: s.mid, + }); + } + }); + }); + + if (newStreams.length > 0) { + subscribe({ + streams: newStreams, + room, + }); + } +} + +function trickle({ feed, candidate }) { + const trickleData = candidate ? { candidate } : {}; + if (feed) trickleData.feed = feed; + const trickleEvent = candidate ? 'trickle' : 'trickle-complete'; + + socket.emit(trickleEvent, { + data: trickleData, + _id: getId(), + }); +} + +function configure({ feed, display, jsep, restart, streams }) { + const configureData = {}; + if (feed) configureData.feed = feed; + if (display) configureData.display = display; + if (jsep) configureData.jsep = jsep; + if (streams) configureData.streams = streams; + if (typeof restart === 'boolean') configureData.restart = restart; + + const configId = getId(); + + socket.emit('configure', { + data: configureData, + _id: configId, + }); + + if (jsep) + pendingOfferMap.set(configId, { feed }); +} + +async function _publish({ feed = myFeed, display = myName } = {}) { + try { + const offer = await doOffer(feed, display); + configure({ feed, jsep: offer }); + } catch (e) { + console.log('error while doing offer', e); + } +} + +function _unpublish({ feed = myFeed } = {}) { + const unpublishData = { + feed, + }; + + socket.emit('unpublish', { + data: unpublishData, + _id: getId(), + }); +} + +function _leave({ feed = myFeed } = {}) { + const leaveData = { + feed, + }; + + socket.emit('leave', { + data: leaveData, + _id: getId(), + }); +} + +function _listParticipants({ room = myRoom } = {}) { + const listData = { + room, + }; + + socket.emit('list-participants', { + data: listData, + _id: getId(), + }); +} + +function _kick({ feed, room = myRoom, secret = 'adminpwd' }) { + const kickData = { + room, + feed, + secret, + }; + + socket.emit('kick', { + data: kickData, + _id: getId(), + }); +} + +function start({ jsep = null } = {}) { + const startData = { + jsep, + }; + + socket.emit('start', { + data: startData, + _id: getId(), + }); +} + +function _pause() { + const pauseData = {}; + + socket.emit('pause', { + data: pauseData, + _id: getId(), + }); +} + +function _unsubscribe({ streams, room = myRoom }) { + const unsubscribeData = { + room, + streams, + }; + + socket.emit('unsubscribe', { + data: unsubscribeData, + _id: getId(), + }); +} + +function _switch({ streams }) { + const switchData = { + streams, + }; + + socket.emit('switch', { + data: switchData, + _id: getId(), + }); +} + +function _exists({ room = myRoom } = {}) { + const existsData = { + room, + }; + + socket.emit('exists', { + data: existsData, + _id: getId(), + }); +} + +function _listRooms() { + socket.emit('list-rooms', { + _id: getId(), + }); +} + +function _create({ room, description, max_publishers = 6, audiocodec = 'opus', videocodec = 'vp8', talking_events = false, talking_level_threshold = 25, talking_packets_threshold = 100, permanent = false }) { + socket.emit('create', { + data: { + room, + description, + max_publishers, + audiocodec, + videocodec, + talking_events, + talking_level_threshold, + talking_packets_threshold, + permanent, + }, + _id: getId(), + }); +} + +function _destroy({ room = myRoom, permanent = false, secret = 'adminpwd' }) { + socket.emit('destroy', { + data: { + room, + permanent, + secret, + }, + _id: getId(), + }); +} + +// add remove enable disable token mgmt +function _allow({ room = myRoom, action, token, secret = 'adminpwd' }) { + const allowData = { + room, + action, + secret, + }; + if (action != 'disable' && token) allowData.list = [token]; + + socket.emit('allow', { + data: allowData, + _id: getId(), + }); +} + +function _startForward({ feed = myFeed, host, room = myRoom, streams, secret = 'adminpwd' }) { + socket.emit('rtp-fwd-start', { + data: { + room, + feed, + host, + streams, + secret, + }, + _id: getId(), + }); +} + +function _stopForward({ stream, feed, room = myRoom, secret = 'adminpwd' }) { + socket.emit('rtp-fwd-stop', { + data: { + room, + stream, + feed, + secret, + }, + _id: getId(), + }); +} + +function _listForward({ room = myRoom, secret = 'adminpwd' } = {}) { + socket.emit('rtp-fwd-list', { + data: { room, secret }, + _id: getId(), + }); +} + +socket.on('connect', () => { + console.log('socket connected'); + socket.sendBuffer = []; + scheduleConnection(0.1); +}); + +socket.on('disconnect', () => { + console.log('socket disconnected'); + pendingOfferMap.clear(); + subscriptions.clear(); + removeAllMediaElements(); + closeAllPCs(); +}); + +socket.on('videoroom-error', ({ error, _id }) => { + console.log('videoroom error', error); + if (error === 'backend-failure' || error === 'session-not-available') { + socket.disconnect(); + return; + } + if (pendingOfferMap.has(_id)) { + removeAllLocalMediaElements(); + closePubPc(); + pendingOfferMap.delete(_id); + return; + } +}); + +socket.on('joined', async ({ data }) => { + console.log('joined to room', data); + setLocalMediaElement(null, null, null, data.room); + + try { + await _publish({ feed: data.feed, display: data.display }); + subscribeTo(data.publishers, data.room); + } catch (e) { + console.log('error while publishing', e); + } +}); + +socket.on('subscribed', async ({ data }) => { + console.log('subscribed to feed', data); + /* + * data.streams + * [ + * { + * "type": "audio", + * "active": true, + * "mindex": 0, + * "mid": "0", + * "ready": false, + * "send": true, + * "feed_id": 947374180882471, + * "feed_display": "John_Doe_8186", + * "feed_mid": "0", + * "codec": "opus" + * }, + * { + * "type": "video", + * "active": true, + * "mindex": 1, + * "mid": "1", + * "ready": false, + * "send": true, + * "feed_id": 947374180882471, + * "feed_display": "John_Doe_8186", + * "feed_mid": "1", + * "codec": "vp8" + * } + * ] + */ + updateSubscriptions(data.streams); + + try { + if (data.jsep) { + const answer = await doAnswer(data.jsep); + start({ jsep: answer }); + } + } catch (e) { console.log('error while doing answer', e); } +}); + +socket.on('unsubscribed', async ({ data }) => { + console.log('unsubscribed to feed', data); + /* + * data.streams + * [ + * { + * "type": "audio", + * "active": true, + * "mindex": 0, + * "mid": "0", + * "ready": true, + * "send": true, + * "feed_id": 5431908509285044, + * "feed_display": "John_Doe_2332", + * "feed_mid": "0", + * "codec": "opus" + * }, + * { + * "type": "video", + * "active": false, + * "mindex": 1, + * "mid": "1", + * "ready": false, + * "send": true + * } + * ] + */ + updateSubscriptions(data.streams); + + try { + if (data.jsep) { + const answer = await doAnswer(data.jsep); + start({ jsep: answer }); + } + } catch (e) { console.log('error while doing answer', e); } +}); + +socket.on('updated', async ({ data }) => { + console.log('updated subscription', data); + /* + * data.streams + * [ + * { + * "type": "audio", + * "active": false, + * "mindex": 0, + * "mid": "0", + * "ready": false, + * "send": true + * }, + * { + * "type": "video", + * "active": false, + * "mindex": 1, + * "mid": "1", + * "ready": false, + * "send": true + * } + * ] + */ + updateSubscriptions(data.streams); + + try { + if (data.jsep) { + const answer = await doAnswer(data.jsep); + start({ jsep: answer }); + } + } catch (e) { console.log('error while doing answer', e); } +}); + +socket.on('participants-list', ({ data }) => { + console.log('participants list', data); +}); + +socket.on('talking', ({ data }) => { + console.log('talking notify', data); +}); + +socket.on('kicked', ({ data }) => { + console.log('participant kicked', data); + if (data.feed) { + const streams = subscriptions.values().toArray().map(s => { + const stream = {}; + for (const attr in s) { + stream[attr] = s[attr]; + } + if (stream.feed_id == data.feed) { + stream.active = false; + stream.feed_id = null; + stream.feed_mid = null; + stream.feed_display = null; + } + return stream; + }); + updateSubscriptions(streams); + } +}); + +socket.on('allowed', ({ data }) => { + console.log('token management', data); +}); + +socket.on('configured', async ({ data, _id }) => { + console.log('feed configured', data); + pendingOfferMap.delete(_id); + + const pc = data.feed ? pubPc : subPc; + if (data.jsep) { + try { + await pc.setRemoteDescription(data.jsep); + if (data.jsep.type === 'offer') { + const answer = await doAnswer(data.jsep); + start({ jsep: answer }); + } + console.log('configure remote sdp OK'); + } catch (e) { + console.log('error setting remote sdp', e); + } + } + if (data.display) { + setLocalMediaElement(null, data.feed, data.display); + } +}); + +socket.on('display', ({ data }) => { + console.log('feed changed display name', data); + const streams = subscriptions.values().toArray().map(s => { + if (s.feed_id === data.feed) { + s.feed_display = data.display; + } + return s; + }); + updateSubscriptions(streams); +}); + +socket.on('started', ({ data }) => { + console.log('subscriber feed started', data); +}); + +socket.on('paused', ({ data }) => { + console.log('feed paused', data); +}); + +socket.on('switched', ({ data }) => { + console.log('feed switched', data); + updateSubscriptions(data.streams); +}); + +socket.on('feed-list', ({ data }) => { + console.log('new feeds available!', data); + subscribeTo(data.publishers, data.room); +}); + +socket.on('unpublished', ({ data }) => { + console.log('feed unpublished', data); + if (data.feed) { + if (data.feed === myFeed) { + removeAllLocalMediaElements(); + closePubPc(); + } + } +}); + +socket.on('leaving', ({ data }) => { + console.log('feed leaving', data); + if (data.feed) { + if (data.feed === myFeed) { + removeAllLocalMediaElements(); + closePubPc(); + } + else { + const streams = subscriptions.values().toArray().map(s => { + const stream = {}; + for (const attr in s) { + stream[attr] = s[attr]; + } + if (stream.feed_id == data.feed) { + stream.active = false; + stream.feed_id = null; + stream.feed_mid = null; + stream.feed_display = null; + } + return stream; + }); + updateSubscriptions(streams); + } + } +}); + +socket.on('exists', ({ data }) => { + console.log('room exists', data); +}); + +socket.on('rooms-list', ({ data }) => { + console.log('rooms list', data); +}); + +socket.on('created', ({ data }) => { + console.log('room created', data); +}); + +socket.on('destroyed', ({ data }) => { + console.log('room destroyed', data); + if (data.room === myRoom) { + socket.disconnect(); + } +}); + +socket.on('rtp-fwd-started', ({ data }) => { + console.log('rtp forwarding started', data); +}); + +socket.on('rtp-fwd-stopped', ({ data }) => { + console.log('rtp forwarding stopped', data); +}); + +socket.on('rtp-fwd-list', ({ data }) => { + console.log('rtp forwarders list', data); +}); + +async function _restartPublisher({ feed = myFeed } = {}) { + return _publish({ feed }); +} + +async function _restartSubscriber() { + configure({ restart: true }); +} + +async function doOffer(feed, display) { + if (!pubPc) { + const pc = new RTCPeerConnection({ + 'iceServers': [{ + urls: 'stun:stun.l.google.com:19302' + }], + }); + + pc.onnegotiationneeded = event => console.log('pc.onnegotiationneeded', event); + pc.onicecandidate = event => trickle({ feed, candidate: event.candidate }); + pc.oniceconnectionstatechange = () => { + if (pc.iceConnectionState === 'failed' || pc.iceConnectionState === 'closed') { + removeAllLocalMediaElements(); + closePubPc(); + } + }; + /* This one below should not be fired, cause the PC is used just to send */ + pc.ontrack = event => console.log('pc.ontrack', event); + + pubPc = pc; + + try { + const localStream = await navigator.mediaDevices.getUserMedia({ audio: true, video: true }); + localStream.getTracks().forEach(track => { + console.log('adding track', track); + pc.addTrack(track, localStream); + }); + setLocalMediaElement(localStream, feed, display, null); + } catch (e) { + console.log('error while doing offer', e); + removeAllLocalMediaElements(); + closePubPc(); + return; + } + } + else { + console.log('Performing ICE restart'); + pubPc.restartIce(); + } + myFeed = feed; + + try { + const offer = await pubPc.createOffer(); + await pubPc.setLocalDescription(offer); + console.log('set local sdp OK'); + return offer; + } catch (e) { + console.log('error while doing offer', e); + removeAllLocalMediaElements(); + closePubPc(); + return; + } +} + +function hasFeedMidSubscription(feed, mid) { + for (let [_, s] of subscriptions) { + if (s.feed === feed && s.mid === mid) return true; + } + return false; +} + +function updateSubscriptions(streams) { + if (!streams) return; + removeRemoteMediaElements(streams); + const newSubscriptions = new Map(); + streams.forEach(s => { + s.ms = subscriptions.get(s.mid)?.ms; + newSubscriptions.set(s.mid, s); + }); + subscriptions = newSubscriptions; + refreshRemoteMediaElements(); +} + +function removeRemoteMediaElements(new_streams) { + if (!new_streams) return; + const oldSubscriptions = subscriptions; + const oldSubMids = oldSubscriptions.values().toArray().map(s => s.active && s.mid).filter(mid => mid); + const newSubMids = new_streams.values().toArray().map(s => s.active && s.mid).filter(mid => mid); + const deletedSubMids = oldSubMids.filter(mid => !newSubMids.includes(mid)); + deletedSubMids.forEach(mid => removeRemoteMediaElementsBySubMid(mid, false)); +} + +function refreshRemoteMediaElements() { + for (let [sub_mid, s] of subscriptions) { + const { feed_display, type, feed_id, feed_mid, active, ms } = s; + if (active) { + if (type === 'video') + setRemoteVideoElement(ms, sub_mid, [feed_display, feed_id, feed_mid, sub_mid].join('|')); + if (type === 'audio') + setRemoteAudioElement(ms, sub_mid); + } + } +} + +async function doAnswer(offer) { + if (!subPc) { + const pc = new RTCPeerConnection({ + 'iceServers': [{ + urls: 'stun:stun.l.google.com:19302' + }], + }); + + subPc = pc; + + pc.onnegotiationneeded = event => console.log('pc.onnegotiationneeded', event); + pc.onicecandidate = event => trickle({ candidate: event.candidate }); + pc.oniceconnectionstatechange = () => { + if (pc.iceConnectionState === 'failed' || pc.iceConnectionState === 'closed') { + removeAllRemoteMediaElements(); + closeSubPc(); + } + }; + pc.ontrack = event => { + console.log('pc.ontrack', event); + + event.track.onunmute = evt => { + console.log('track.onunmute', evt); + }; + event.track.onmute = evt => { + console.log('track.onmute', evt); + }; + event.track.onended = evt => { + console.log('track.onended', evt); + }; + + /* avoid latching tracks */ + const submid = event.transceiver?.mid || event.receiver.mid; + const remoteStream = event.streams[0].id === 'janus' ? (new MediaStream([event.track])) : event.streams[0]; + if (subscriptions.has(submid)) { + const stream = subscriptions.get(submid); + stream.ms = remoteStream; + refreshRemoteMediaElements(); + } + }; + } + + try { + await subPc.setRemoteDescription(offer); + console.log('set remote sdp OK'); + const answer = await subPc.createAnswer(); + await subPc.setLocalDescription(answer); + console.log('set local sdp OK'); + return answer; + } catch (e) { + console.log('error creating subscriber answer', e); + removeAllRemoteMediaElements(); + closeSubPc(); + throw e; + } +} + +function setLocalMediaElement(localStream, feed, display, room) { + if (room) document.getElementById('videos').getElementsByTagName('span')[0].innerHTML = ' --- VIDEOROOM (' + room + ') --- '; + if (!feed) return; + + const id = `video_${feed}_local`; + let localVideoContainer = document.getElementById(id); + if (!localVideoContainer) { + const nameElem = document.createElement('span'); + nameElem.style.display = 'table'; + + const localVideoStreamElem = document.createElement('video'); + localVideoStreamElem.width = 320; + localVideoStreamElem.height = 240; + localVideoStreamElem.autoplay = true; + localVideoStreamElem.muted = true; + localVideoStreamElem.style.cssText = '-moz-transform: scale(-1, 1); -webkit-transform: scale(-1, 1); -o-transform: scale(-1, 1); transform: scale(-1, 1); filter: FlipH;'; + + localVideoContainer = document.createElement('div'); + localVideoContainer.id = id; + localVideoContainer.appendChild(nameElem); + localVideoContainer.appendChild(localVideoStreamElem); + + document.getElementById('locals').appendChild(localVideoContainer); + } + if (display) { + const nameElem = localVideoContainer.getElementsByTagName('span')[0]; + nameElem.innerHTML = [display, feed].join('|'); + } + if (localStream) { + const localVideoStreamElem = localVideoContainer.getElementsByTagName('video')[0]; + localVideoStreamElem.srcObject = localStream; + } +} + +function setRemoteVideoElement(remoteStream, sub_mid, display) { + if (!sub_mid) return; + + /* Target specific sub_mid/feed/mid */ + const id = `video_remote_${sub_mid}`; + let remoteVideoContainer = document.getElementById(id); + if (!remoteVideoContainer) { + /* Non existing */ + const nameElem = document.createElement('span'); + nameElem.innerHTML = display; + nameElem.style.display = 'table'; + + const remoteVideoStreamElem = document.createElement('video'); + remoteVideoStreamElem.width = 320; + remoteVideoStreamElem.height = 240; + remoteVideoStreamElem.autoplay = true; + remoteVideoStreamElem.style.cssText = '-moz-transform: scale(-1, 1); -webkit-transform: scale(-1, 1); -o-transform: scale(-1, 1); transform: scale(-1, 1); filter: FlipH;'; + + remoteVideoContainer = document.createElement('div'); + remoteVideoContainer.id = id; + remoteVideoContainer.appendChild(nameElem); + remoteVideoContainer.appendChild(remoteVideoStreamElem); + + document.getElementById('remotes').appendChild(remoteVideoContainer); + } + if (display) { + const nameElem = remoteVideoContainer.getElementsByTagName('span')[0]; + nameElem.innerHTML = display; + } + if (remoteStream) { + const remoteVideoStreamElem = remoteVideoContainer.getElementsByTagName('video')[0]; + remoteVideoStreamElem.srcObject = remoteStream; + } +} + +function setRemoteAudioElement(remoteStream, sub_mid) { + if (!sub_mid) return; + + /* Target specific sub_mid/feed/mid */ + const id = `audio_remote_${sub_mid}`; + let remoteAudioContainer = document.getElementById(id); + if (!remoteAudioContainer) { + const remoteAudioStreamElem = document.createElement('audio'); + remoteAudioStreamElem.autoplay = true; + + remoteAudioContainer = document.createElement('div'); + remoteAudioContainer.id = id; + remoteAudioContainer.appendChild(remoteAudioStreamElem); + + document.getElementById('remotes').appendChild(remoteAudioContainer); + } + if (remoteStream) { + const remoteAudioStreamElem = remoteAudioContainer.getElementsByTagName('audio')[0]; + remoteAudioStreamElem.srcObject = remoteStream; + } +} + +function removeMediaElement(container, stopTracks = true) { + let streamElem = null; + if (container.getElementsByTagName('video').length > 0) + streamElem = container.getElementsByTagName('video')[0]; + if (container.getElementsByTagName('audio').length > 0) + streamElem = container.getElementsByTagName('audio')[0]; + if (streamElem && streamElem.srcObject && stopTracks) { + streamElem.srcObject.getTracks().forEach(track => track.stop()); + streamElem.srcObject = null; + } + container.remove(); +} + +function removeRemoteMediaElementsBySubMid(sub_mid, stopTracks) { + const idEndsWith = `_remote_${sub_mid}`; + const containers = document.querySelectorAll(`[id$=${idEndsWith}]`); + containers.forEach(container => removeMediaElement(container, stopTracks)); +} + +function removeAllLocalMediaElements() { + const locals = document.getElementById('locals'); + const localMediaContainers = locals.getElementsByTagName('div'); + for (let i = 0; localMediaContainers && i < localMediaContainers.length; i++) + removeMediaElement(localMediaContainers[i]); + while (locals.firstChild) + locals.removeChild(locals.firstChild); +} + +function removeAllRemoteMediaElements() { + var remotes = document.getElementById('remotes'); + const remoteMediaContainers = remotes.getElementsByTagName('div'); + for (let i = 0; remoteMediaContainers && i < remoteMediaContainers.length; i++) + removeMediaElement(remoteMediaContainers[i]); + while (remotes.firstChild) + remotes.removeChild(remotes.firstChild); +} + +function removeAllMediaElements() { + removeAllLocalMediaElements(); + removeAllRemoteMediaElements(); + document.getElementById('videos').getElementsByTagName('span')[0].innerHTML = ' --- VIDEOROOM () --- '; +} + +function closePubPc() { + if (pubPc) { + console.log('closing pc for publisher'); + _closePC(pubPc); + pubPc = null; + } +} + +function closeSubPc() { + if (subPc) { + console.log('closing pc for subscriber'); + _closePC(subPc); + subPc = null; + } +} + +function _closePC(pc) { + if (!pc) return; + pc.getSenders().forEach(sender => { + if (sender.track) + sender.track.stop(); + }); + pc.getReceivers().forEach(receiver => { + if (receiver.track) + receiver.track.stop(); + }); + pc.onnegotiationneeded = null; + pc.onicecandidate = null; + pc.oniceconnectionstatechange = null; + pc.ontrack = null; + try { + pc.close(); + } catch (_e) { } +} + +function closeAllPCs() { + console.log('closing all pcs'); + closePubPc(); + closeSubPc(); +} diff --git a/examples/videoroom-ms/package-lock.json b/examples/videoroom-ms/package-lock.json new file mode 100644 index 0000000..534a8ae --- /dev/null +++ b/examples/videoroom-ms/package-lock.json @@ -0,0 +1,1092 @@ +{ + "name": "janode-videoroom-ms", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "janode-videoroom-ms", + "license": "ISC", + "dependencies": { + "express": "^4.13.4", + "socket.io": "^4.2.0" + } + }, + "node_modules/@socket.io/component-emitter": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz", + "integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==", + "license": "MIT" + }, + "node_modules/@types/cookie": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz", + "integrity": "sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==", + "license": "MIT" + }, + "node_modules/@types/cors": { + "version": "2.8.17", + "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.17.tgz", + "integrity": "sha512-8CGDvrBj1zgo2qE+oS3pOCyYNqCPryMWY2bGfwA0dcfopWGgxs+78df0Rs3rc9THP4JkOhLsAa+15VdpAqkcUA==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/node": { + "version": "22.10.7", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.7.tgz", + "integrity": "sha512-V09KvXxFiutGp6B7XkpaDXlNadZxrzajcY50EuoLIpQ6WWYCSvf19lVIazzfIzQvhUN2HjX12spLojTnhuKlGg==", + "license": "MIT", + "dependencies": { + "undici-types": "~6.20.0" + } + }, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "license": "MIT", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", + "license": "MIT" + }, + "node_modules/base64id": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", + "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==", + "license": "MIT", + "engines": { + "node": "^4.5.0 || >= 5.9" + } + }, + "node_modules/body-parser": { + "version": "1.20.3", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", + "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==", + "license": "MIT", + "dependencies": { + "bytes": "3.1.2", + "content-type": "~1.0.5", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "on-finished": "2.4.1", + "qs": "6.13.0", + "raw-body": "2.5.2", + "type-is": "~1.6.18", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.1.tgz", + "integrity": "sha512-BhYE+WDaywFg2TBWYNXAE+8B1ATnThNBqXHP5nQu0jWJdVvY2hvkpyB3qOmtmDePiS5/BDQ8wASEWGMWRG148g==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.3.tgz", + "integrity": "sha512-YTd+6wGlNlPxSuri7Y6X8tY2dmm12UMH66RpKMhiX6rsk5wXXnYgbUcOt8kiS31/AjfoTOvCsE+w8nZQLQnzHA==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "license": "MIT", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz", + "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==", + "license": "MIT" + }, + "node_modules/cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "license": "MIT", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "license": "MIT", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "license": "MIT" + }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/engine.io": { + "version": "6.6.2", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.6.2.tgz", + "integrity": "sha512-gmNvsYi9C8iErnZdVcJnvCpSKbWTt1E8+JZo8b+daLninywUWi5NQ5STSHZ9rFjFO7imNcvb8Pc5pe/wMR5xEw==", + "license": "MIT", + "dependencies": { + "@types/cookie": "^0.4.1", + "@types/cors": "^2.8.12", + "@types/node": ">=10.0.0", + "accepts": "~1.3.4", + "base64id": "2.0.0", + "cookie": "~0.7.2", + "cors": "~2.8.5", + "debug": "~4.3.1", + "engine.io-parser": "~5.2.1", + "ws": "~8.17.1" + }, + "engines": { + "node": ">=10.2.0" + } + }, + "node_modules/engine.io-parser": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.3.tgz", + "integrity": "sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/engine.io/node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/engine.io/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/engine.io/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "license": "MIT" + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/express": { + "version": "4.21.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz", + "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==", + "license": "MIT", + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.20.3", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.7.1", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.3.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "merge-descriptors": "1.0.3", + "methods": "~1.1.2", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.12", + "proxy-addr": "~2.0.7", + "qs": "6.13.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.19.0", + "serve-static": "1.16.2", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/finalhandler": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz", + "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==", + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "statuses": "2.0.1", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-intrinsic": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.7.tgz", + "integrity": "sha512-VW6Pxhsrk0KAOqs3WEd0klDiF/+V7gQOpAvY1jVU/LHmaD/kQO4523aiJuikX/QAKYiW6x8Jh+RJej1almdtCA==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "function-bind": "^1.1.2", + "get-proto": "^1.0.0", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "license": "MIT", + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/merge-descriptors": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", + "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, + "node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.13.3", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.3.tgz", + "integrity": "sha512-kDCGIbxkDSXE3euJZZXzc6to7fCrKHNI/hSRQnRuQ+BWjFNzZwiFF8fj/6o2t2G9/jTj8PSIYTfCLelLZEeRpA==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-to-regexp": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz", + "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==", + "license": "MIT" + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "license": "MIT", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/qs": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", + "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.0.6" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", + "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", + "license": "MIT", + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" + }, + "node_modules/send": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz", + "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==", + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/send/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/serve-static": { + "version": "1.16.2", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz", + "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==", + "license": "MIT", + "dependencies": { + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.19.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "license": "ISC" + }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/socket.io": { + "version": "4.8.1", + "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.8.1.tgz", + "integrity": "sha512-oZ7iUCxph8WYRHHcjBEc9unw3adt5CmSNlppj/5Q4k2RIrhl8Z5yY2Xr4j9zj0+wzVZ0bxmYoGSzKJnRl6A4yg==", + "license": "MIT", + "dependencies": { + "accepts": "~1.3.4", + "base64id": "~2.0.0", + "cors": "~2.8.5", + "debug": "~4.3.2", + "engine.io": "~6.6.0", + "socket.io-adapter": "~2.5.2", + "socket.io-parser": "~4.2.4" + }, + "engines": { + "node": ">=10.2.0" + } + }, + "node_modules/socket.io-adapter": { + "version": "2.5.5", + "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.5.tgz", + "integrity": "sha512-eLDQas5dzPgOWCk9GuuJC2lBqItuhKI4uxGgo9aIV7MYbk2h9Q6uULEh8WBzThoI7l+qU9Ast9fVUmkqPP9wYg==", + "license": "MIT", + "dependencies": { + "debug": "~4.3.4", + "ws": "~8.17.1" + } + }, + "node_modules/socket.io-adapter/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/socket.io-adapter/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/socket.io-parser": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz", + "integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==", + "license": "MIT", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/socket.io-parser/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/socket.io-parser/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/socket.io/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/socket.io/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "license": "MIT", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/undici-types": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", + "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", + "license": "MIT" + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "license": "MIT", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/ws": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", + "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + } + } +} diff --git a/examples/videoroom-ms/package.json b/examples/videoroom-ms/package.json new file mode 100644 index 0000000..2e43b92 --- /dev/null +++ b/examples/videoroom-ms/package.json @@ -0,0 +1,30 @@ +{ + "name": "janode-videoroom-ms", + "description": "Janode videoroom multistream app", + "type": "module", + "keywords": [ + "janus", + "webrtc", + "meetecho" + ], + "author": { + "name": "Alessandro Toppi", + "email": "atoppi@meetecho.com" + }, + "repository": { + "type": "git", + "url": "https://github.com/meetecho/janode.git" + }, + "license": "ISC", + "private": true, + "main": "src/index.js", + "dependencies": { + "express": "^4.13.4", + "socket.io": "^4.2.0" + }, + "scripts": { + "build": "npm install --omit=dev", + "build-config": "node -e \"var fs = require('fs');fs.createReadStream('src/config.template.js').pipe(fs.createWriteStream('src/config.js'));\"", + "start": "node src/index.js" + } +} \ No newline at end of file diff --git a/examples/videoroom-ms/src/config.template.js b/examples/videoroom-ms/src/config.template.js new file mode 100644 index 0000000..a82aafe --- /dev/null +++ b/examples/videoroom-ms/src/config.template.js @@ -0,0 +1,16 @@ +export default { + janode: { + address: [{ + url: 'ws://127.0.0.1:8188/', + apisecret: 'secret' + }], + // seconds between retries after a connection setup error + retry_time_secs: 10 + }, + web: { + port: 4443, + bind: '0.0.0.0', + key: '/path/to/key.pem', + cert: '/path/to/cert.pem' + } +}; \ No newline at end of file diff --git a/examples/videoroom-ms/src/index.js b/examples/videoroom-ms/src/index.js new file mode 100644 index 0000000..effa1ee --- /dev/null +++ b/examples/videoroom-ms/src/index.js @@ -0,0 +1,638 @@ +'use strict'; + +import { readFileSync } from 'fs'; +import Janode from '../../../src/janode.js'; +import config from './config.js'; +const { janode: janodeConfig, web: serverConfig } = config; + +import { fileURLToPath } from 'url'; +import { dirname, basename } from 'path'; +const __filename = fileURLToPath(import.meta.url); +const __dirname = dirname(__filename); + +const { Logger } = Janode; +const LOG_NS = `[${basename(__filename)}]`; +import VideoRoomPlugin from '../../../src/plugins/videoroom-plugin.js'; + +import express from 'express'; +const app = express(); +const options = { + key: serverConfig.key ? readFileSync(serverConfig.key) : null, + cert: serverConfig.cert ? readFileSync(serverConfig.cert) : null, +}; +import { createServer as createHttpsServer } from 'https'; +import { createServer as createHttpServer } from 'http'; +const httpServer = (options.key && options.cert) ? createHttpsServer(options, app) : createHttpServer(app); +import { Server } from 'socket.io'; +const io = new Server(httpServer); + +const scheduleBackEndConnection = (function () { + let task = null; + + return (function (del = 10) { + if (task) return; + Logger.info(`${LOG_NS} scheduled connection in ${del} seconds`); + task = setTimeout(() => { + initBackEnd() + .then(() => task = null) + .catch(() => { + task = null; + scheduleBackEndConnection(); + }); + }, del * 1000); + }); +})(); + +let janodeSession; +let janodeManagerHandle; + +(function main() { + + initFrontEnd().catch(({ message }) => Logger.error(`${LOG_NS} failure initializing front-end: ${message}`)); + + scheduleBackEndConnection(1); + +})(); + +async function initBackEnd() { + Logger.info(`${LOG_NS} connecting Janode...`); + let connection; + + try { + connection = await Janode.connect(janodeConfig); + Logger.info(`${LOG_NS} connection with Janus created`); + + connection.once(Janode.EVENT.CONNECTION_CLOSED, () => { + Logger.info(`${LOG_NS} connection with Janus closed`); + }); + + connection.once(Janode.EVENT.CONNECTION_ERROR, error => { + Logger.error(`${LOG_NS} connection with Janus error: ${error.message}`); + + replyError(io, 'backend-failure'); + + scheduleBackEndConnection(); + }); + + const session = await connection.create(); + Logger.info(`${LOG_NS} session ${session.id} with Janus created`); + janodeSession = session; + + session.once(Janode.EVENT.SESSION_DESTROYED, () => { + Logger.info(`${LOG_NS} session ${session.id} destroyed`); + janodeSession = null; + }); + + const handle = await session.attach(VideoRoomPlugin); + Logger.info(`${LOG_NS} manager handle ${handle.id} attached`); + janodeManagerHandle = handle; + + // generic handle events + handle.once(Janode.EVENT.HANDLE_DETACHED, () => { + Logger.info(`${LOG_NS} ${handle.name} manager handle detached event`); + }); + } + catch (error) { + Logger.error(`${LOG_NS} Janode setup error: ${error.message}`); + if (connection) connection.close().catch(() => { }); + + // notify clients + replyError(io, 'backend-failure'); + + throw error; + } +} + +function initFrontEnd() { + if (httpServer.listening) return Promise.reject(new Error('Server already listening')); + + Logger.info(`${LOG_NS} initializing socketio front end...`); + + io.on('connection', function (socket) { + const remote = `[${socket.request.connection.remoteAddress}:${socket.request.connection.remotePort}]`; + Logger.info(`${LOG_NS} ${remote} connection with client established`); + + const msHandles = (function () { + const handles = { + pub: null, + sub: null, + }; + + return { + setPubHandle: handle => { + handles.pub = handle; + }, + setSubHandle: handle => { + handles.sub = handle; + }, + getPubHandle: _ => { + return handles.pub; + }, + getSubHandle: _ => { + return handles.sub; + }, + getHandleByFeed: feed => { + if (feed && handles.pub && feed === handles.pub.feed) return handles.pub; + if (!feed && handles.sub) return handles.sub; + return null; + }, + detachPubHandle: async _ => { + if (handles.pub) + await handles.pub.detach().catch(() => { }); + handles.pub = null; + }, + detachSubHandle: async _ => { + if (handles.sub) + await handles.sub.detach().catch(() => { }); + handles.sub = null; + }, + detachAll: async _ => { + const detaches = Object.values(handles).map(h => h && h.detach().catch(() => { })); + await Promise.all(detaches); + handles.pub = null; + handles.sub = null; + }, + }; + })(); + + /*----------*/ + /* USER API */ + /*----------*/ + + socket.on('join', async (evtdata = {}) => { + Logger.info(`${LOG_NS} ${remote} join received`); + const { _id, data: joindata = {} } = evtdata; + + if (!checkSessions(janodeSession, true, socket, evtdata)) return; + + let pubHandle; + + try { + pubHandle = await janodeSession.attach(VideoRoomPlugin); + Logger.info(`${LOG_NS} ${remote} videoroom publisher handle ${pubHandle.id} attached`); + msHandles.setPubHandle(pubHandle); + + // custom videoroom publisher/manager events + + pubHandle.on(VideoRoomPlugin.EVENT.VIDEOROOM_DESTROYED, evtdata => { + replyEvent(socket, 'destroyed', evtdata); + }); + + pubHandle.on(VideoRoomPlugin.EVENT.VIDEOROOM_PUB_LIST, evtdata => { + replyEvent(socket, 'feed-list', evtdata); + }); + + pubHandle.on(VideoRoomPlugin.EVENT.VIDEOROOM_PUB_PEER_JOINED, evtdata => { + replyEvent(socket, 'feed-joined', evtdata); + }); + + pubHandle.on(VideoRoomPlugin.EVENT.VIDEOROOM_UNPUBLISHED, evtdata => { + replyEvent(socket, 'unpublished', evtdata); + }); + + pubHandle.on(VideoRoomPlugin.EVENT.VIDEOROOM_LEAVING, async evtdata => { + if (pubHandle.feed === evtdata.feed) { + await msHandles.detachPubHandle(); + } + replyEvent(socket, 'leaving', evtdata); + }); + + pubHandle.on(VideoRoomPlugin.EVENT.VIDEOROOM_DISPLAY, evtdata => { + replyEvent(socket, 'display', evtdata); + }); + + pubHandle.on(VideoRoomPlugin.EVENT.VIDEOROOM_TALKING, evtdata => { + replyEvent(socket, 'talking', evtdata); + }); + + pubHandle.on(VideoRoomPlugin.EVENT.VIDEOROOM_KICKED, async evtdata => { + replyEvent(socket, 'kicked', evtdata); + }); + + // generic videoroom events + pubHandle.on(Janode.EVENT.HANDLE_WEBRTCUP, () => Logger.info(`${LOG_NS} ${pubHandle.name} webrtcup event`)); + pubHandle.on(Janode.EVENT.HANDLE_MEDIA, evtdata => Logger.info(`${LOG_NS} ${pubHandle.name} media event ${JSON.stringify(evtdata)}`)); + pubHandle.on(Janode.EVENT.HANDLE_SLOWLINK, evtdata => Logger.info(`${LOG_NS} ${pubHandle.name} slowlink event ${JSON.stringify(evtdata)}`)); + pubHandle.on(Janode.EVENT.HANDLE_HANGUP, evtdata => Logger.info(`${LOG_NS} ${pubHandle.name} hangup event ${JSON.stringify(evtdata)}`)); + pubHandle.on(Janode.EVENT.HANDLE_DETACHED, () => { + Logger.info(`${LOG_NS} ${pubHandle.name} detached event`); + msHandles.setPubHandle(null); + }); + pubHandle.on(Janode.EVENT.HANDLE_TRICKLE, evtdata => Logger.info(`${LOG_NS} ${pubHandle.name} trickle event ${JSON.stringify(evtdata)}`)); + + const response = await pubHandle.joinPublisher(joindata); + + replyEvent(socket, 'joined', response, _id); + + Logger.info(`${LOG_NS} ${remote} joined sent`); + } catch ({ message }) { + if (pubHandle) pubHandle.detach().catch(() => { }); + replyError(socket, message, joindata, _id); + } + }); + + socket.on('subscribe', async (evtdata = {}) => { + Logger.info(`${LOG_NS} ${remote} subscribe received`); + const { _id, data: subscribedata = {} } = evtdata; + + if (!checkSessions(janodeSession, true, socket, evtdata)) return; + + let subHandle = msHandles.getSubHandle(); + let response; + + try { + if (!subHandle) { + subHandle = await janodeSession.attach(VideoRoomPlugin); + Logger.info(`${LOG_NS} ${remote} videoroom listener handle ${subHandle.id} attached`); + msHandles.setSubHandle(subHandle); + // generic videoroom events + subHandle.on(Janode.EVENT.HANDLE_WEBRTCUP, () => Logger.info(`${LOG_NS} ${subHandle.name} webrtcup event`)); + subHandle.on(Janode.EVENT.HANDLE_SLOWLINK, evtdata => Logger.info(`${LOG_NS} ${subHandle.name} slowlink event ${JSON.stringify(evtdata)}`)); + subHandle.on(Janode.EVENT.HANDLE_HANGUP, evtdata => Logger.info(`${LOG_NS} ${subHandle.name} hangup event ${JSON.stringify(evtdata)}`)); + subHandle.once(Janode.EVENT.HANDLE_DETACHED, () => { + Logger.info(`${LOG_NS} ${subHandle.name} detached event`); + msHandles.setSubHandle(null); + }); + subHandle.on(Janode.EVENT.HANDLE_TRICKLE, evtdata => Logger.info(`${LOG_NS} ${subHandle.name} trickle event ${JSON.stringify(evtdata)}`)); + + // specific videoroom events + subHandle.on(VideoRoomPlugin.EVENT.VIDEOROOM_UPDATED, evtdata => { + Logger.info(`${LOG_NS} ${subHandle.name} updated event`); + replyEvent(socket, 'updated', evtdata); + }); + response = await subHandle.joinSubscriber(subscribedata); + } + else { + response = await subHandle.update({ + subscribe: subscribedata.streams, + }); + } + + replyEvent(socket, 'subscribed', response, _id); + Logger.info(`${LOG_NS} ${remote} subscribed sent`); + } catch ({ message }) { + replyError(socket, message, subscribedata, _id); + } + }); + + socket.on('unsubscribe', async (evtdata = {}) => { + Logger.info(`${LOG_NS} ${remote} unsubscribe received`); + const { _id, data: unsubscribedata = {} } = evtdata; + + let subHandle = msHandles.getSubHandle(); + if (!checkSessions(janodeSession, subHandle, socket, evtdata)) return; + let response; + + try { + response = await subHandle.update({ + unsubscribe: unsubscribedata.streams, + }); + + replyEvent(socket, 'unsubscribed', response, _id); + Logger.info(`${LOG_NS} ${remote} unsubscribed sent`); + } catch ({ message }) { + replyError(socket, message, unsubscribedata, _id); + } + }); + + socket.on('configure', async (evtdata = {}) => { + Logger.info(`${LOG_NS} ${remote} configure received`); + const { _id, data: confdata = {} } = evtdata; + + const handle = msHandles.getHandleByFeed(confdata.feed); + if (!checkSessions(janodeSession, handle, socket, evtdata)) return; + + try { + const response = await handle.configure(confdata); + delete response.configured; + replyEvent(socket, 'configured', response, _id); + Logger.info(`${LOG_NS} ${remote} configured sent`); + } catch ({ message }) { + replyError(socket, message, confdata, _id); + } + }); + + socket.on('unpublish', async (evtdata = {}) => { + Logger.info(`${LOG_NS} ${remote} unpublish received`); + const { _id, data: unpubdata = {} } = evtdata; + + const handle = msHandles.getPubHandle(); + if (!checkSessions(janodeSession, handle, socket, evtdata)) return; + + try { + const response = await handle.unpublish(); + replyEvent(socket, 'unpublished', response, _id); + Logger.info(`${LOG_NS} ${remote} unpublished sent`); + } catch ({ message }) { + replyError(socket, message, unpubdata, _id); + } + }); + + socket.on('leave', async (evtdata = {}) => { + Logger.info(`${LOG_NS} ${remote} leave received`); + const { _id, data: leavedata = {} } = evtdata; + + const handle = msHandles.getHandleByFeed(leavedata.feed); + if (!checkSessions(janodeSession, handle, socket, evtdata)) return; + + try { + const response = await handle.leave(); + replyEvent(socket, 'leaving', response, _id); + Logger.info(`${LOG_NS} ${remote} leaving sent`); + handle.detach().catch(() => { }); + } catch ({ message }) { + replyError(socket, message, leavedata, _id); + } + }); + + socket.on('start', async (evtdata = {}) => { + Logger.info(`${LOG_NS} ${remote} start received`); + const { _id, data: startdata = {} } = evtdata; + + const handle = msHandles.getSubHandle(); + if (!checkSessions(janodeSession, handle, socket, evtdata)) return; + + try { + const response = await handle.start(startdata); + replyEvent(socket, 'started', response, _id); + Logger.info(`${LOG_NS} ${remote} started sent`); + } catch ({ message }) { + replyError(socket, message, startdata, _id); + } + }); + + socket.on('pause', async (evtdata = {}) => { + Logger.info(`${LOG_NS} ${remote} pause received`); + const { _id, data: pausedata = {} } = evtdata; + + const handle = msHandles.getSubHandle(); + if (!checkSessions(janodeSession, handle, socket, evtdata)) return; + + try { + const response = await handle.pause(); + replyEvent(socket, 'paused', response, _id); + Logger.info(`${LOG_NS} ${remote} paused sent`); + } catch ({ message }) { + replyError(socket, message, pausedata, _id); + } + }); + + socket.on('switch', async (evtdata = {}) => { + Logger.info(`${LOG_NS} ${remote} switch received`); + const { _id, data: switchdata = {} } = evtdata; + + const handle = msHandles.getSubHandle(); + if (!checkSessions(janodeSession, handle, socket, evtdata)) return; + + try { + const response = await handle.switch(switchdata); + replyEvent(socket, 'switched', response, _id); + Logger.info(`${LOG_NS} ${remote} switched sent`); + } catch ({ message }) { + replyError(socket, message, switchdata, _id); + } + }); + + // trickle candidate from the client + socket.on('trickle', async (evtdata = {}) => { + Logger.info(`${LOG_NS} ${remote} trickle received`); + const { _id, data: trickledata = {} } = evtdata; + + const handle = msHandles.getHandleByFeed(trickledata.feed); + if (!checkSessions(janodeSession, handle, socket, evtdata)) return; + + handle.trickle(trickledata.candidate).catch(({ message }) => replyError(socket, message, trickledata, _id)); + }); + + // trickle complete signal from the client + socket.on('trickle-complete', async (evtdata = {}) => { + Logger.info(`${LOG_NS} ${remote} trickle-complete received`); + const { _id, data: trickledata = {} } = evtdata; + + const handle = msHandles.getHandleByFeed(trickledata.feed); + if (!checkSessions(janodeSession, handle, socket, evtdata)) return; + + handle.trickleComplete(trickledata.candidate).catch(({ message }) => replyError(socket, message, trickledata, _id)); + }); + + // socket disconnection event + socket.on('disconnect', async () => { + Logger.info(`${LOG_NS} ${remote} disconnected socket`); + + await msHandles.detachAll(); + }); + + + /*----------------*/ + /* Management API */ + /*----------------*/ + + + socket.on('list-participants', async (evtdata = {}) => { + Logger.info(`${LOG_NS} ${remote} list_participants received`); + const { _id, data: listdata = {} } = evtdata; + + if (!checkSessions(janodeSession, janodeManagerHandle, socket, evtdata)) return; + + try { + const response = await janodeManagerHandle.listParticipants(listdata); + replyEvent(socket, 'participants-list', response, _id); + Logger.info(`${LOG_NS} ${remote} participants-list sent`); + } catch ({ message }) { + replyError(socket, message, listdata, _id); + } + }); + + socket.on('kick', async (evtdata = {}) => { + Logger.info(`${LOG_NS} ${remote} kick received`); + const { _id, data: kickdata = {} } = evtdata; + + if (!checkSessions(janodeSession, janodeManagerHandle, socket, evtdata)) return; + + try { + const response = await janodeManagerHandle.kick(kickdata); + replyEvent(socket, 'kicked', response, _id); + Logger.info(`${LOG_NS} ${remote} kicked sent`); + } catch ({ message }) { + replyError(socket, message, kickdata, _id); + } + }); + + socket.on('exists', async (evtdata = {}) => { + Logger.info(`${LOG_NS} ${remote} exists received`); + const { _id, data: existsdata = {} } = evtdata; + + if (!checkSessions(janodeSession, janodeManagerHandle, socket, evtdata)) return; + + try { + const response = await janodeManagerHandle.exists(existsdata); + replyEvent(socket, 'exists', response, _id); + Logger.info(`${LOG_NS} ${remote} exists sent`); + } catch ({ message }) { + replyError(socket, message, existsdata, _id); + } + }); + + socket.on('list-rooms', async (evtdata = {}) => { + Logger.info(`${LOG_NS} ${remote} list-rooms received`); + const { _id, data: listdata = {} } = evtdata; + + if (!checkSessions(janodeSession, janodeManagerHandle, socket, evtdata)) return; + + try { + const response = await janodeManagerHandle.list(); + replyEvent(socket, 'rooms-list', response, _id); + Logger.info(`${LOG_NS} ${remote} rooms-list sent`); + } catch ({ message }) { + replyError(socket, message, listdata, _id); + } + }); + + socket.on('create', async (evtdata = {}) => { + Logger.info(`${LOG_NS} ${remote} create received`); + const { _id, data: createdata = {} } = evtdata; + + if (!checkSessions(janodeSession, janodeManagerHandle, socket, evtdata)) return; + + try { + const response = await janodeManagerHandle.create(createdata); + replyEvent(socket, 'created', response, _id); + Logger.info(`${LOG_NS} ${remote} created sent`); + } catch ({ message }) { + replyError(socket, message, createdata, _id); + } + }); + + socket.on('destroy', async (evtdata = {}) => { + Logger.info(`${LOG_NS} ${remote} destroy received`); + const { _id, data: destroydata = {} } = evtdata; + + if (!checkSessions(janodeSession, janodeManagerHandle, socket, evtdata)) return; + + try { + const response = await janodeManagerHandle.destroy(destroydata); + replyEvent(socket, 'destroyed', response, _id); + Logger.info(`${LOG_NS} ${remote} destroyed sent`); + } catch ({ message }) { + replyError(socket, message, destroydata, _id); + } + }); + + socket.on('allow', async (evtdata = {}) => { + Logger.info(`${LOG_NS} ${remote} allow received`); + const { _id, data: allowdata = {} } = evtdata; + + if (!checkSessions(janodeSession, janodeManagerHandle, socket, evtdata)) return; + + try { + const response = await janodeManagerHandle.allow(allowdata); + replyEvent(socket, 'allowed', response, _id); + Logger.info(`${LOG_NS} ${remote} allowed sent`); + } catch ({ message }) { + replyError(socket, message, allowdata, _id); + } + }); + + socket.on('rtp-fwd-start', async (evtdata = {}) => { + Logger.info(`${LOG_NS} ${remote} rtp-fwd-start received`); + const { _id, data: rtpstartdata = {} } = evtdata; + + if (!checkSessions(janodeSession, janodeManagerHandle, socket, evtdata)) return; + + try { + const response = await janodeManagerHandle.startForward(rtpstartdata); + replyEvent(socket, 'rtp-fwd-started', response, _id); + Logger.info(`${LOG_NS} ${remote} rtp-fwd-started sent`); + } catch ({ message }) { + replyError(socket, message, rtpstartdata, _id); + } + }); + + socket.on('rtp-fwd-stop', async (evtdata = {}) => { + Logger.info(`${LOG_NS} ${remote} rtp-fwd-stop received`); + const { _id, data: rtpstopdata = {} } = evtdata; + + if (!checkSessions(janodeSession, janodeManagerHandle, socket, evtdata)) return; + + try { + const response = await janodeManagerHandle.stopForward(rtpstopdata); + replyEvent(socket, 'rtp-fwd-stopped', response, _id); + Logger.info(`${LOG_NS} ${remote} rtp-fwd-stopped sent`); + } catch ({ message }) { + replyError(socket, message, rtpstopdata, _id); + } + }); + + socket.on('rtp-fwd-list', async (evtdata = {}) => { + Logger.info(`${LOG_NS} ${remote} rtp_fwd_list received`); + const { _id, data: rtplistdata = {} } = evtdata; + + if (!checkSessions(janodeSession, janodeManagerHandle, socket, evtdata)) return; + + try { + const response = await janodeManagerHandle.listForward(rtplistdata); + replyEvent(socket, 'rtp-fwd-list', response, _id); + Logger.info(`${LOG_NS} ${remote} rtp-fwd-list sent`); + } catch ({ message }) { + replyError(socket, message, rtplistdata, _id); + } + }); + + }); + + // disable caching for all app + app.set('etag', false).set('view cache', false); + + // static content + app.use('/janode', express.static(__dirname + '/../html/', { + etag: false, + lastModified: false, + maxAge: 0, + })); + + // http server binding + return new Promise((resolve, reject) => { + // web server binding + httpServer.listen( + serverConfig.port, + serverConfig.bind, + () => { + Logger.info(`${LOG_NS} server listening on ${(options.key && options.cert) ? 'https' : 'http'}://${serverConfig.bind}:${serverConfig.port}/janode`); + resolve(); + } + ); + + httpServer.on('error', e => reject(e)); + }); +} + +function checkSessions(session, handle, socket, { data, _id }) { + if (!session) { + replyError(socket, 'session-not-available', data, _id); + return false; + } + if (!handle) { + replyError(socket, 'handle-not-available', data, _id); + return false; + } + return true; +} + +function replyEvent(socket, evtname, data, _id) { + const evtdata = { + data, + }; + if (_id) evtdata._id = _id; + + socket.emit(evtname, evtdata); +} + +function replyError(socket, message, request, _id) { + const evtdata = { + error: message, + }; + if (request) evtdata.request = request; + if (_id) evtdata._id = _id; + + socket.emit('videoroom-error', evtdata); +} diff --git a/examples/videoroom/html/.eslintrc.json b/examples/videoroom/html/.eslintrc.json deleted file mode 100644 index 5c3f02e..0000000 --- a/examples/videoroom/html/.eslintrc.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "env": { - "browser": true - } -} \ No newline at end of file diff --git a/examples/videoroom/html/videoroom-client.js b/examples/videoroom/html/videoroom-client.js index 7e9a7e3..59e1c7d 100644 --- a/examples/videoroom/html/videoroom-client.js +++ b/examples/videoroom/html/videoroom-client.js @@ -1,4 +1,3 @@ -/* eslint-disable no-sparse-arrays */ /* global io */ 'use strict'; @@ -10,6 +9,7 @@ let pendingOfferMap = new Map(); const myRoom = getURLParameter('room') ? parseInt(getURLParameter('room')) : (getURLParameter('room_str') || 1234); const randName = ('John_Doe_' + Math.floor(10000 * Math.random())); const myName = getURLParameter('name') || randName; +let myFeed; const button = document.getElementById('button'); button.onclick = () => { @@ -24,6 +24,7 @@ function getId() { } function getURLParameter(name) { + // eslint-disable-next-line no-sparse-arrays return decodeURIComponent((new RegExp('[?|&]' + name + '=' + '([^&;]+?)(&|#|;|$)').exec(location.search) || [, ''])[1].replace(/\+/g, '%20')) || null; } @@ -48,6 +49,11 @@ const socket = io({ reconnection: false, }); +async function _publish(feed = myFeed, display) { + const offer = await doOffer(feed, display); + configure({ feed: feed, jsep: offer }); +} + function join({ room = myRoom, display = myName, token = null } = {}) { const joinData = { room, @@ -115,7 +121,7 @@ function configure({ feed, jsep, restart, substream, temporal }) { if (jsep) pendingOfferMap.set(configId, { feed }); } -function _unpublish({ feed }) { +function _unpublish({ feed = myFeed } = {}) { const unpublishData = { feed, }; @@ -126,7 +132,7 @@ function _unpublish({ feed }) { }); } -function _leave({ feed }) { +function _leave({ feed = myFeed } = {}) { const leaveData = { feed, }; @@ -260,7 +266,7 @@ function _allow({ room = myRoom, action, token, secret = 'adminpwd' }) { }); } -function _startForward({ feed, room = myRoom, host = 'localhost', audio_port, video_port, data_port = null, secret = 'adminpwd' }) { +function _startForward({ feed = myFeed, room = myRoom, host = 'localhost', audio_port, video_port, data_port = null, secret = 'adminpwd' }) { socket.emit('rtp-fwd-start', { data: { room, @@ -275,7 +281,7 @@ function _startForward({ feed, room = myRoom, host = 'localhost', audio_port, vi }); } -function _stopForward({ stream, feed, room = myRoom, secret = 'adminpwd' }) { +function _stopForward({ stream, feed = myFeed, room = myRoom, secret = 'adminpwd' }) { socket.emit('rtp-fwd-stop', { data: { room, @@ -287,7 +293,7 @@ function _stopForward({ stream, feed, room = myRoom, secret = 'adminpwd' }) { }); } -function _listForward({ room = myRoom, secret = 'adminpwd' }) { +function _listForward({ room = myRoom, secret = 'adminpwd' } = {}) { socket.emit('rtp-fwd-list', { data: { room, secret }, _id: getId(), @@ -327,8 +333,7 @@ socket.on('joined', async ({ data }) => { setLocalVideoElement(null, null, null, data.room); try { - const offer = await doOffer(data.feed, data.display, false); - configure({ feed: data.feed, jsep: offer }); + await _publish(data.feed, data.display); subscribeTo(data.publishers, data.room); } catch (e) { console.log('error while doing offer', e); @@ -454,8 +459,7 @@ socket.on('rtp-fwd-list', ({ data }) => { }); async function _restartPublisher(feed) { - const offer = await doOffer(feed, null); - configure({ feed, jsep: offer }); + return await _publish(feed, null); } async function _restartSubscriber(feed) { @@ -501,6 +505,7 @@ async function doOffer(feed, display) { console.log('Performing ICE restart'); pcMap.get(feed).restartIce(); } + myFeed = feed; try { const pc = pcMap.get(feed); diff --git a/examples/videoroom/package-lock.json b/examples/videoroom/package-lock.json index 2781c7c..c50b185 100644 --- a/examples/videoroom/package-lock.json +++ b/examples/videoroom/package-lock.json @@ -1,6 +1,6 @@ { "name": "janode-videoroom", - "lockfileVersion": 2, + "lockfileVersion": 3, "requires": true, "packages": { "": { @@ -11,33 +11,44 @@ "socket.io": "^4.2.0" } }, - "node_modules/@types/component-emitter": { - "version": "1.2.11", - "resolved": "https://registry.npmjs.org/@types/component-emitter/-/component-emitter-1.2.11.tgz", - "integrity": "sha512-SRXjM+tfsSlA9VuG8hGO2nft2p8zjXCK1VcC6N4NXbBbYbSia9kzCChYQajIjzIqOOOuh5Ock6MmV2oux4jDZQ==" + "node_modules/@socket.io/component-emitter": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz", + "integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==", + "license": "MIT" }, "node_modules/@types/cookie": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz", - "integrity": "sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==" + "integrity": "sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==", + "license": "MIT" }, "node_modules/@types/cors": { - "version": "2.8.12", - "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.12.tgz", - "integrity": "sha512-vt+kDhq/M2ayberEtJcIN/hxXy1Pk+59g2FV/ZQceeaTyCtCucjL2Q7FXlFjtWn4n15KCr1NE2lNNFhp0lEThw==" + "version": "2.8.17", + "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.17.tgz", + "integrity": "sha512-8CGDvrBj1zgo2qE+oS3pOCyYNqCPryMWY2bGfwA0dcfopWGgxs+78df0Rs3rc9THP4JkOhLsAa+15VdpAqkcUA==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } }, "node_modules/@types/node": { - "version": "16.11.10", - "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.10.tgz", - "integrity": "sha512-3aRnHa1KlOEEhJ6+CvyHKK5vE9BcLGjtUpwvqYLRvYNQKMfabu3BwfJaA/SLW8dxe28LsNDjtHwePTuzn3gmOA==" + "version": "22.10.7", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.7.tgz", + "integrity": "sha512-V09KvXxFiutGp6B7XkpaDXlNadZxrzajcY50EuoLIpQ6WWYCSvf19lVIazzfIzQvhUN2HjX12spLojTnhuKlGg==", + "license": "MIT", + "dependencies": { + "undici-types": "~6.20.0" + } }, "node_modules/accepts": { - "version": "1.3.7", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", - "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "license": "MIT", "dependencies": { - "mime-types": "~2.1.24", - "negotiator": "0.6.2" + "mime-types": "~2.1.34", + "negotiator": "0.6.3" }, "engines": { "node": ">= 0.6" @@ -46,72 +57,106 @@ "node_modules/array-flatten": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", + "license": "MIT" }, "node_modules/base64id": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==", + "license": "MIT", "engines": { "node": "^4.5.0 || >= 5.9" } }, "node_modules/body-parser": { - "version": "1.19.0", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", - "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", + "version": "1.20.3", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", + "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==", + "license": "MIT", "dependencies": { - "bytes": "3.1.0", - "content-type": "~1.0.4", + "bytes": "3.1.2", + "content-type": "~1.0.5", "debug": "2.6.9", - "depd": "~1.1.2", - "http-errors": "1.7.2", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", "iconv-lite": "0.4.24", - "on-finished": "~2.3.0", - "qs": "6.7.0", - "raw-body": "2.4.0", - "type-is": "~1.6.17" + "on-finished": "2.4.1", + "qs": "6.13.0", + "raw-body": "2.5.2", + "type-is": "~1.6.18", + "unpipe": "1.0.0" }, "engines": { - "node": ">= 0.8" + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" } }, "node_modules/bytes": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", - "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "license": "MIT", "engines": { "node": ">= 0.8" } }, - "node_modules/component-emitter": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", - "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==" + "node_modules/call-bind-apply-helpers": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.1.tgz", + "integrity": "sha512-BhYE+WDaywFg2TBWYNXAE+8B1ATnThNBqXHP5nQu0jWJdVvY2hvkpyB3qOmtmDePiS5/BDQ8wASEWGMWRG148g==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.3.tgz", + "integrity": "sha512-YTd+6wGlNlPxSuri7Y6X8tY2dmm12UMH66RpKMhiX6rsk5wXXnYgbUcOt8kiS31/AjfoTOvCsE+w8nZQLQnzHA==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, "node_modules/content-disposition": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", - "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "license": "MIT", "dependencies": { - "safe-buffer": "5.1.2" + "safe-buffer": "5.2.1" }, "engines": { "node": ">= 0.6" } }, "node_modules/content-type": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", - "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "license": "MIT", "engines": { "node": ">= 0.6" } }, "node_modules/cookie": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", - "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==", + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz", + "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==", + "license": "MIT", "engines": { "node": ">= 0.6" } @@ -119,12 +164,14 @@ "node_modules/cookie-signature": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==", + "license": "MIT" }, "node_modules/cors": { "version": "2.8.5", "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "license": "MIT", "dependencies": { "object-assign": "^4", "vary": "^1" @@ -137,78 +184,105 @@ "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", "dependencies": { "ms": "2.0.0" } }, "node_modules/depd": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", - "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "license": "MIT", "engines": { - "node": ">= 0.6" + "node": ">= 0.8" } }, "node_modules/destroy": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", - "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "license": "MIT", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } }, "node_modules/ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "license": "MIT" }, "node_modules/encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "license": "MIT", "engines": { "node": ">= 0.8" } }, "node_modules/engine.io": { - "version": "6.1.3", - "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.1.3.tgz", - "integrity": "sha512-rqs60YwkvWTLLnfazqgZqLa/aKo+9cueVfEi/dZ8PyGyaf8TLOxj++4QMIgeG3Gn0AhrWiFXvghsoY9L9h25GA==", + "version": "6.6.2", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.6.2.tgz", + "integrity": "sha512-gmNvsYi9C8iErnZdVcJnvCpSKbWTt1E8+JZo8b+daLninywUWi5NQ5STSHZ9rFjFO7imNcvb8Pc5pe/wMR5xEw==", + "license": "MIT", "dependencies": { "@types/cookie": "^0.4.1", "@types/cors": "^2.8.12", "@types/node": ">=10.0.0", "accepts": "~1.3.4", "base64id": "2.0.0", - "cookie": "~0.4.1", + "cookie": "~0.7.2", "cors": "~2.8.5", "debug": "~4.3.1", - "engine.io-parser": "~5.0.3", - "ws": "~8.2.3" + "engine.io-parser": "~5.2.1", + "ws": "~8.17.1" }, "engines": { - "node": ">=10.0.0" + "node": ">=10.2.0" } }, "node_modules/engine.io-parser": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.0.4.tgz", - "integrity": "sha512-+nVFp+5z1E3HcToEnO7ZIj3g+3k9389DvWtvJZz0T6/eOCPIyyxehFcedoYrZQrp0LgQbD9pPXhpMBKMd5QURg==", + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.3.tgz", + "integrity": "sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==", + "license": "MIT", "engines": { "node": ">=10.0.0" } }, "node_modules/engine.io/node_modules/cookie": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz", - "integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==", + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "license": "MIT", "engines": { "node": ">= 0.6" } }, "node_modules/engine.io/node_modules/debug": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", - "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "license": "MIT", "dependencies": { - "ms": "2.1.2" + "ms": "^2.1.3" }, "engines": { "node": ">=6.0" @@ -220,74 +294,114 @@ } }, "node_modules/engine.io/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } }, "node_modules/escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "license": "MIT" }, "node_modules/etag": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "license": "MIT", "engines": { "node": ">= 0.6" } }, "node_modules/express": { - "version": "4.17.1", - "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", - "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", + "version": "4.21.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz", + "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==", + "license": "MIT", "dependencies": { - "accepts": "~1.3.7", + "accepts": "~1.3.8", "array-flatten": "1.1.1", - "body-parser": "1.19.0", - "content-disposition": "0.5.3", + "body-parser": "1.20.3", + "content-disposition": "0.5.4", "content-type": "~1.0.4", - "cookie": "0.4.0", + "cookie": "0.7.1", "cookie-signature": "1.0.6", "debug": "2.6.9", - "depd": "~1.1.2", - "encodeurl": "~1.0.2", + "depd": "2.0.0", + "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "etag": "~1.8.1", - "finalhandler": "~1.1.2", + "finalhandler": "1.3.1", "fresh": "0.5.2", - "merge-descriptors": "1.0.1", + "http-errors": "2.0.0", + "merge-descriptors": "1.0.3", "methods": "~1.1.2", - "on-finished": "~2.3.0", + "on-finished": "2.4.1", "parseurl": "~1.3.3", - "path-to-regexp": "0.1.7", - "proxy-addr": "~2.0.5", - "qs": "6.7.0", + "path-to-regexp": "0.1.12", + "proxy-addr": "~2.0.7", + "qs": "6.13.0", "range-parser": "~1.2.1", - "safe-buffer": "5.1.2", - "send": "0.17.1", - "serve-static": "1.14.1", - "setprototypeof": "1.1.1", - "statuses": "~1.5.0", + "safe-buffer": "5.2.1", + "send": "0.19.0", + "serve-static": "1.16.2", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", "type-is": "~1.6.18", "utils-merge": "1.0.1", "vary": "~1.1.2" }, "engines": { "node": ">= 0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/finalhandler": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", - "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz", + "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==", + "license": "MIT", "dependencies": { "debug": "2.6.9", - "encodeurl": "~1.0.2", + "encodeurl": "~2.0.0", "escape-html": "~1.0.3", - "on-finished": "~2.3.0", + "on-finished": "2.4.1", "parseurl": "~1.3.3", - "statuses": "~1.5.0", + "statuses": "2.0.1", "unpipe": "~1.0.0" }, "engines": { @@ -298,6 +412,7 @@ "version": "0.2.0", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "license": "MIT", "engines": { "node": ">= 0.6" } @@ -305,30 +420,115 @@ "node_modules/fresh": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "license": "MIT", "engines": { "node": ">= 0.6" } }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-intrinsic": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.7.tgz", + "integrity": "sha512-VW6Pxhsrk0KAOqs3WEd0klDiF/+V7gQOpAvY1jVU/LHmaD/kQO4523aiJuikX/QAKYiW6x8Jh+RJej1almdtCA==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "function-bind": "^1.1.2", + "get-proto": "^1.0.0", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/http-errors": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", - "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "license": "MIT", "dependencies": { - "depd": "~1.1.2", - "inherits": "2.0.3", - "setprototypeof": "1.1.1", - "statuses": ">= 1.5.0 < 2", - "toidentifier": "1.0.0" + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" }, "engines": { - "node": ">= 0.6" + "node": ">= 0.8" } }, "node_modules/iconv-lite": { "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "license": "MIT", "dependencies": { "safer-buffer": ">= 2.1.2 < 3" }, @@ -337,35 +537,52 @@ } }, "node_modules/inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" }, "node_modules/ipaddr.js": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "license": "MIT", "engines": { "node": ">= 0.10" } }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "license": "MIT", "engines": { "node": ">= 0.6" } }, "node_modules/merge-descriptors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", - "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", + "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, "node_modules/methods": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "license": "MIT", "engines": { "node": ">= 0.6" } @@ -374,6 +591,7 @@ "version": "1.6.0", "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "license": "MIT", "bin": { "mime": "cli.js" }, @@ -382,19 +600,21 @@ } }, "node_modules/mime-db": { - "version": "1.51.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.51.0.tgz", - "integrity": "sha512-5y8A56jg7XVQx2mbv1lu49NR4dokRnhZYTtL+KGfaa27uq4pSTXkwQkFJl4pkRMyNFz/EtYDSkiiEHx3F7UN6g==", + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", "engines": { "node": ">= 0.6" } }, "node_modules/mime-types": { - "version": "2.1.34", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.34.tgz", - "integrity": "sha512-6cP692WwGIs9XXdOO4++N+7qjqv0rqxxVvJ3VHPh/Sc9mVZcQP+ZGhkKiTvWMQRr2tbHkJP/Yn7Y0npb3ZBs4A==", + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", "dependencies": { - "mime-db": "1.51.0" + "mime-db": "1.52.0" }, "engines": { "node": ">= 0.6" @@ -403,12 +623,14 @@ "node_modules/ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" }, "node_modules/negotiator": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", - "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==", + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "license": "MIT", "engines": { "node": ">= 0.6" } @@ -416,15 +638,29 @@ "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "license": "MIT", "engines": { "node": ">=0.10.0" } }, + "node_modules/object-inspect": { + "version": "1.13.3", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.3.tgz", + "integrity": "sha512-kDCGIbxkDSXE3euJZZXzc6to7fCrKHNI/hSRQnRuQ+BWjFNzZwiFF8fj/6o2t2G9/jTj8PSIYTfCLelLZEeRpA==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/on-finished": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", - "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "license": "MIT", "dependencies": { "ee-first": "1.1.1" }, @@ -436,19 +672,22 @@ "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "license": "MIT", "engines": { "node": ">= 0.8" } }, "node_modules/path-to-regexp": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", - "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz", + "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==", + "license": "MIT" }, "node_modules/proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "license": "MIT", "dependencies": { "forwarded": "0.2.0", "ipaddr.js": "1.9.1" @@ -458,28 +697,37 @@ } }, "node_modules/qs": { - "version": "6.7.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", - "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==", + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", + "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.0.6" + }, "engines": { "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/range-parser": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "license": "MIT", "engines": { "node": ">= 0.6" } }, "node_modules/raw-body": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", - "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", + "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", + "license": "MIT", "dependencies": { - "bytes": "3.1.0", - "http-errors": "1.7.2", + "bytes": "3.1.2", + "http-errors": "2.0.0", "iconv-lite": "0.4.24", "unpipe": "1.0.0" }, @@ -488,90 +736,221 @@ } }, "node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" }, "node_modules/safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" }, "node_modules/send": { - "version": "0.17.1", - "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", - "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==", + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz", + "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==", + "license": "MIT", "dependencies": { "debug": "2.6.9", - "depd": "~1.1.2", - "destroy": "~1.0.4", + "depd": "2.0.0", + "destroy": "1.2.0", "encodeurl": "~1.0.2", "escape-html": "~1.0.3", "etag": "~1.8.1", "fresh": "0.5.2", - "http-errors": "~1.7.2", + "http-errors": "2.0.0", "mime": "1.6.0", - "ms": "2.1.1", - "on-finished": "~2.3.0", + "ms": "2.1.3", + "on-finished": "2.4.1", "range-parser": "~1.2.1", - "statuses": "~1.5.0" + "statuses": "2.0.1" }, "engines": { "node": ">= 0.8.0" } }, + "node_modules/send/node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/send/node_modules/ms": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", - "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" }, "node_modules/serve-static": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", - "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==", + "version": "1.16.2", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz", + "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==", + "license": "MIT", "dependencies": { - "encodeurl": "~1.0.2", + "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "parseurl": "~1.3.3", - "send": "0.17.1" + "send": "0.19.0" }, "engines": { "node": ">= 0.8.0" } }, "node_modules/setprototypeof": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", - "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "license": "ISC" + }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, "node_modules/socket.io": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.4.0.tgz", - "integrity": "sha512-bnpJxswR9ov0Bw6ilhCvO38/1WPtE3eA2dtxi2Iq4/sFebiDJQzgKNYA7AuVVdGW09nrESXd90NbZqtDd9dzRQ==", + "version": "4.8.1", + "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.8.1.tgz", + "integrity": "sha512-oZ7iUCxph8WYRHHcjBEc9unw3adt5CmSNlppj/5Q4k2RIrhl8Z5yY2Xr4j9zj0+wzVZ0bxmYoGSzKJnRl6A4yg==", + "license": "MIT", "dependencies": { "accepts": "~1.3.4", "base64id": "~2.0.0", + "cors": "~2.8.5", "debug": "~4.3.2", - "engine.io": "~6.1.0", - "socket.io-adapter": "~2.3.3", - "socket.io-parser": "~4.0.4" + "engine.io": "~6.6.0", + "socket.io-adapter": "~2.5.2", + "socket.io-parser": "~4.2.4" }, "engines": { - "node": ">=10.0.0" + "node": ">=10.2.0" } }, "node_modules/socket.io-adapter": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.3.3.tgz", - "integrity": "sha512-Qd/iwn3VskrpNO60BeRyCyr8ZWw9CPZyitW4AQwmRZ8zCiyDiL+znRnWX6tDHXnWn1sJrM1+b6Mn6wEDJJ4aYQ==" + "version": "2.5.5", + "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.5.tgz", + "integrity": "sha512-eLDQas5dzPgOWCk9GuuJC2lBqItuhKI4uxGgo9aIV7MYbk2h9Q6uULEh8WBzThoI7l+qU9Ast9fVUmkqPP9wYg==", + "license": "MIT", + "dependencies": { + "debug": "~4.3.4", + "ws": "~8.17.1" + } + }, + "node_modules/socket.io-adapter/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/socket.io-adapter/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" }, "node_modules/socket.io-parser": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.0.4.tgz", - "integrity": "sha512-t+b0SS+IxG7Rxzda2EVvyBZbvFPBCjJoyHuE0P//7OAsN23GItzDRdWa6ALxZI/8R5ygK7jAR6t028/z+7295g==", + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz", + "integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==", + "license": "MIT", "dependencies": { - "@types/component-emitter": "^1.2.10", - "component-emitter": "~1.3.0", + "@socket.io/component-emitter": "~3.1.0", "debug": "~4.3.1" }, "engines": { @@ -579,11 +958,12 @@ } }, "node_modules/socket.io-parser/node_modules/debug": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", - "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "license": "MIT", "dependencies": { - "ms": "2.1.2" + "ms": "^2.1.3" }, "engines": { "node": ">=6.0" @@ -595,16 +975,18 @@ } }, "node_modules/socket.io-parser/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" }, "node_modules/socket.io/node_modules/debug": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", - "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "license": "MIT", "dependencies": { - "ms": "2.1.2" + "ms": "^2.1.3" }, "engines": { "node": ">=6.0" @@ -616,22 +998,25 @@ } }, "node_modules/socket.io/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" }, "node_modules/statuses": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", - "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "license": "MIT", "engines": { - "node": ">= 0.6" + "node": ">= 0.8" } }, "node_modules/toidentifier": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", - "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "license": "MIT", "engines": { "node": ">=0.6" } @@ -640,6 +1025,7 @@ "version": "1.6.18", "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "license": "MIT", "dependencies": { "media-typer": "0.3.0", "mime-types": "~2.1.24" @@ -648,10 +1034,17 @@ "node": ">= 0.6" } }, + "node_modules/undici-types": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", + "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", + "license": "MIT" + }, "node_modules/unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "license": "MIT", "engines": { "node": ">= 0.8" } @@ -659,7 +1052,8 @@ "node_modules/utils-merge": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "license": "MIT", "engines": { "node": ">= 0.4.0" } @@ -667,21 +1061,23 @@ "node_modules/vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "license": "MIT", "engines": { "node": ">= 0.8" } }, "node_modules/ws": { - "version": "8.2.3", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.2.3.tgz", - "integrity": "sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA==", + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", + "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", + "license": "MIT", "engines": { "node": ">=10.0.0" }, "peerDependencies": { "bufferutil": "^4.0.1", - "utf-8-validate": "^5.0.2" + "utf-8-validate": ">=5.0.2" }, "peerDependenciesMeta": { "bufferutil": { @@ -692,523 +1088,5 @@ } } } - }, - "dependencies": { - "@types/component-emitter": { - "version": "1.2.11", - "resolved": "https://registry.npmjs.org/@types/component-emitter/-/component-emitter-1.2.11.tgz", - "integrity": "sha512-SRXjM+tfsSlA9VuG8hGO2nft2p8zjXCK1VcC6N4NXbBbYbSia9kzCChYQajIjzIqOOOuh5Ock6MmV2oux4jDZQ==" - }, - "@types/cookie": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz", - "integrity": "sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==" - }, - "@types/cors": { - "version": "2.8.12", - "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.12.tgz", - "integrity": "sha512-vt+kDhq/M2ayberEtJcIN/hxXy1Pk+59g2FV/ZQceeaTyCtCucjL2Q7FXlFjtWn4n15KCr1NE2lNNFhp0lEThw==" - }, - "@types/node": { - "version": "16.11.10", - "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.10.tgz", - "integrity": "sha512-3aRnHa1KlOEEhJ6+CvyHKK5vE9BcLGjtUpwvqYLRvYNQKMfabu3BwfJaA/SLW8dxe28LsNDjtHwePTuzn3gmOA==" - }, - "accepts": { - "version": "1.3.7", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", - "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", - "requires": { - "mime-types": "~2.1.24", - "negotiator": "0.6.2" - } - }, - "array-flatten": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" - }, - "base64id": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", - "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==" - }, - "body-parser": { - "version": "1.19.0", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", - "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", - "requires": { - "bytes": "3.1.0", - "content-type": "~1.0.4", - "debug": "2.6.9", - "depd": "~1.1.2", - "http-errors": "1.7.2", - "iconv-lite": "0.4.24", - "on-finished": "~2.3.0", - "qs": "6.7.0", - "raw-body": "2.4.0", - "type-is": "~1.6.17" - } - }, - "bytes": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", - "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==" - }, - "component-emitter": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", - "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==" - }, - "content-disposition": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", - "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", - "requires": { - "safe-buffer": "5.1.2" - } - }, - "content-type": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", - "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" - }, - "cookie": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", - "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==" - }, - "cookie-signature": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" - }, - "cors": { - "version": "2.8.5", - "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", - "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", - "requires": { - "object-assign": "^4", - "vary": "^1" - } - }, - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - } - }, - "depd": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", - "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" - }, - "destroy": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", - "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" - }, - "ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" - }, - "encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" - }, - "engine.io": { - "version": "6.1.3", - "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.1.3.tgz", - "integrity": "sha512-rqs60YwkvWTLLnfazqgZqLa/aKo+9cueVfEi/dZ8PyGyaf8TLOxj++4QMIgeG3Gn0AhrWiFXvghsoY9L9h25GA==", - "requires": { - "@types/cookie": "^0.4.1", - "@types/cors": "^2.8.12", - "@types/node": ">=10.0.0", - "accepts": "~1.3.4", - "base64id": "2.0.0", - "cookie": "~0.4.1", - "cors": "~2.8.5", - "debug": "~4.3.1", - "engine.io-parser": "~5.0.3", - "ws": "~8.2.3" - }, - "dependencies": { - "cookie": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz", - "integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==" - }, - "debug": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", - "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", - "requires": { - "ms": "2.1.2" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - } - } - }, - "engine.io-parser": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.0.4.tgz", - "integrity": "sha512-+nVFp+5z1E3HcToEnO7ZIj3g+3k9389DvWtvJZz0T6/eOCPIyyxehFcedoYrZQrp0LgQbD9pPXhpMBKMd5QURg==" - }, - "escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" - }, - "etag": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" - }, - "express": { - "version": "4.17.1", - "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", - "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", - "requires": { - "accepts": "~1.3.7", - "array-flatten": "1.1.1", - "body-parser": "1.19.0", - "content-disposition": "0.5.3", - "content-type": "~1.0.4", - "cookie": "0.4.0", - "cookie-signature": "1.0.6", - "debug": "2.6.9", - "depd": "~1.1.2", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "finalhandler": "~1.1.2", - "fresh": "0.5.2", - "merge-descriptors": "1.0.1", - "methods": "~1.1.2", - "on-finished": "~2.3.0", - "parseurl": "~1.3.3", - "path-to-regexp": "0.1.7", - "proxy-addr": "~2.0.5", - "qs": "6.7.0", - "range-parser": "~1.2.1", - "safe-buffer": "5.1.2", - "send": "0.17.1", - "serve-static": "1.14.1", - "setprototypeof": "1.1.1", - "statuses": "~1.5.0", - "type-is": "~1.6.18", - "utils-merge": "1.0.1", - "vary": "~1.1.2" - } - }, - "finalhandler": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", - "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", - "requires": { - "debug": "2.6.9", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "on-finished": "~2.3.0", - "parseurl": "~1.3.3", - "statuses": "~1.5.0", - "unpipe": "~1.0.0" - } - }, - "forwarded": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", - "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==" - }, - "fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" - }, - "http-errors": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", - "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", - "requires": { - "depd": "~1.1.2", - "inherits": "2.0.3", - "setprototypeof": "1.1.1", - "statuses": ">= 1.5.0 < 2", - "toidentifier": "1.0.0" - } - }, - "iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "requires": { - "safer-buffer": ">= 2.1.2 < 3" - } - }, - "inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" - }, - "ipaddr.js": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" - }, - "media-typer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" - }, - "merge-descriptors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", - "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" - }, - "methods": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" - }, - "mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" - }, - "mime-db": { - "version": "1.51.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.51.0.tgz", - "integrity": "sha512-5y8A56jg7XVQx2mbv1lu49NR4dokRnhZYTtL+KGfaa27uq4pSTXkwQkFJl4pkRMyNFz/EtYDSkiiEHx3F7UN6g==" - }, - "mime-types": { - "version": "2.1.34", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.34.tgz", - "integrity": "sha512-6cP692WwGIs9XXdOO4++N+7qjqv0rqxxVvJ3VHPh/Sc9mVZcQP+ZGhkKiTvWMQRr2tbHkJP/Yn7Y0npb3ZBs4A==", - "requires": { - "mime-db": "1.51.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - }, - "negotiator": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", - "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" - }, - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" - }, - "on-finished": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", - "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", - "requires": { - "ee-first": "1.1.1" - } - }, - "parseurl": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" - }, - "path-to-regexp": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", - "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" - }, - "proxy-addr": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", - "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", - "requires": { - "forwarded": "0.2.0", - "ipaddr.js": "1.9.1" - } - }, - "qs": { - "version": "6.7.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", - "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==" - }, - "range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" - }, - "raw-body": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", - "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", - "requires": { - "bytes": "3.1.0", - "http-errors": "1.7.2", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" - } - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" - }, - "send": { - "version": "0.17.1", - "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", - "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==", - "requires": { - "debug": "2.6.9", - "depd": "~1.1.2", - "destroy": "~1.0.4", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "fresh": "0.5.2", - "http-errors": "~1.7.2", - "mime": "1.6.0", - "ms": "2.1.1", - "on-finished": "~2.3.0", - "range-parser": "~1.2.1", - "statuses": "~1.5.0" - }, - "dependencies": { - "ms": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", - "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" - } - } - }, - "serve-static": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", - "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==", - "requires": { - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "parseurl": "~1.3.3", - "send": "0.17.1" - } - }, - "setprototypeof": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", - "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" - }, - "socket.io": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.4.0.tgz", - "integrity": "sha512-bnpJxswR9ov0Bw6ilhCvO38/1WPtE3eA2dtxi2Iq4/sFebiDJQzgKNYA7AuVVdGW09nrESXd90NbZqtDd9dzRQ==", - "requires": { - "accepts": "~1.3.4", - "base64id": "~2.0.0", - "debug": "~4.3.2", - "engine.io": "~6.1.0", - "socket.io-adapter": "~2.3.3", - "socket.io-parser": "~4.0.4" - }, - "dependencies": { - "debug": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", - "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", - "requires": { - "ms": "2.1.2" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - } - } - }, - "socket.io-adapter": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.3.3.tgz", - "integrity": "sha512-Qd/iwn3VskrpNO60BeRyCyr8ZWw9CPZyitW4AQwmRZ8zCiyDiL+znRnWX6tDHXnWn1sJrM1+b6Mn6wEDJJ4aYQ==" - }, - "socket.io-parser": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.0.4.tgz", - "integrity": "sha512-t+b0SS+IxG7Rxzda2EVvyBZbvFPBCjJoyHuE0P//7OAsN23GItzDRdWa6ALxZI/8R5ygK7jAR6t028/z+7295g==", - "requires": { - "@types/component-emitter": "^1.2.10", - "component-emitter": "~1.3.0", - "debug": "~4.3.1" - }, - "dependencies": { - "debug": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", - "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", - "requires": { - "ms": "2.1.2" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - } - } - }, - "statuses": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", - "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" - }, - "toidentifier": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", - "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==" - }, - "type-is": { - "version": "1.6.18", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", - "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", - "requires": { - "media-typer": "0.3.0", - "mime-types": "~2.1.24" - } - }, - "unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" - }, - "utils-merge": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" - }, - "vary": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" - }, - "ws": { - "version": "8.2.3", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.2.3.tgz", - "integrity": "sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA==", - "requires": {} - } } } diff --git a/examples/videoroom/src/index.js b/examples/videoroom/src/index.js index adda381..8b93e49 100644 --- a/examples/videoroom/src/index.js +++ b/examples/videoroom/src/index.js @@ -171,17 +171,19 @@ function initFrontEnd() { replyEvent(socket, 'feed-joined', evtdata); }); - pubHandle.on(VideoRoomPlugin.EVENT.VIDEOROOM_UNPUBLISHED, evtdata => { + pubHandle.on(VideoRoomPlugin.EVENT.VIDEOROOM_UNPUBLISHED, async evtdata => { const handle = clientHandles.getHandleByFeed(evtdata.feed); - clientHandles.removeHandleByFeed(evtdata.feed); - if (handle) handle.detach().catch(() => { }); + if (handle.feed !== pubHandle.feed) { + clientHandles.removeHandleByFeed(evtdata.feed); + await handle.detach().catch(() => { }); + } replyEvent(socket, 'unpublished', evtdata); }); - pubHandle.on(VideoRoomPlugin.EVENT.VIDEOROOM_LEAVING, evtdata => { + pubHandle.on(VideoRoomPlugin.EVENT.VIDEOROOM_LEAVING, async evtdata => { const handle = clientHandles.getHandleByFeed(evtdata.feed); clientHandles.removeHandleByFeed(evtdata.feed); - if (handle) handle.detach().catch(() => { }); + if (handle) await handle.detach().catch(() => { }); replyEvent(socket, 'leaving', evtdata); }); @@ -193,10 +195,10 @@ function initFrontEnd() { replyEvent(socket, 'talking', evtdata); }); - pubHandle.on(VideoRoomPlugin.EVENT.VIDEOROOM_KICKED, evtdata => { + pubHandle.on(VideoRoomPlugin.EVENT.VIDEOROOM_KICKED, async evtdata => { const handle = clientHandles.getHandleByFeed(evtdata.feed); clientHandles.removeHandleByFeed(evtdata.feed); - if (handle) handle.detach().catch(() => { }); + if (handle) await handle.detach().catch(() => { }); replyEvent(socket, 'kicked', evtdata); }); @@ -217,7 +219,7 @@ function initFrontEnd() { Logger.info(`${LOG_NS} ${remote} joined sent`); } catch ({ message }) { - if (pubHandle) pubHandle.detach().catch(() => { }); + if (pubHandle) await pubHandle.detach().catch(() => { }); replyError(socket, message, joindata, _id); } }); @@ -255,7 +257,7 @@ function initFrontEnd() { replyEvent(socket, 'subscribed', response, _id); Logger.info(`${LOG_NS} ${remote} subscribed sent`); } catch ({ message }) { - if (subHandle) subHandle.detach().catch(() => { }); + if (subHandle) await subHandle.detach().catch(() => { }); replyError(socket, message, joindata, _id); } }); @@ -320,7 +322,7 @@ function initFrontEnd() { const response = await handle.leave(); replyEvent(socket, 'leaving', response, _id); Logger.info(`${LOG_NS} ${remote} leaving sent`); - handle.detach().catch(() => { }); + await handle.detach().catch(() => { }); } catch ({ message }) { replyError(socket, message, leavedata, _id); } diff --git a/jsdoc.json b/jsdoc.json new file mode 100644 index 0000000..3ca8c63 --- /dev/null +++ b/jsdoc.json @@ -0,0 +1,30 @@ +{ + "opts": { + "encoding": "utf8", + "readme": "./README.md", + "pedantic": true, + "recurse": true, + "verbose": true, + "template": "./node_modules/clean-jsdoc-theme", + "theme_opts": { + "default_theme": "dark", + "menu": [ + { + "title": "Home", + "link": "index.html" + }, + { + "title": "GitHub", + "link": "https://github.com/meetecho/janode" + } + ], + "search": true, + "sort": true, + "exclude_inherited": true + } + }, + "markdown": { + "hardwrap": false, + "idInHeadings": true + } +} diff --git a/package-lock.json b/package-lock.json index cce6f76..f4d5257 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "janode", - "version": "1.6.6", - "lockfileVersion": 2, + "version": "1.8.0", + "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "janode", - "version": "1.6.6", + "version": "1.8.0", "license": "ISC", "dependencies": { "isomorphic-ws": "^4.0.1", @@ -14,22 +14,157 @@ "ws": "^8.0.0" }, "devDependencies": { - "eslint": "^8.25.0" + "@eslint/js": "^9.4.0", + "clean-jsdoc-theme": "^4.3.0", + "eslint": "^9.4.0", + "globals": "^15.4.0", + "jsdoc": "^4.0.4" }, "engines": { - "node": " >=14.13.1 || >=16.0.0" + "node": " >=18.18.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz", + "integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz", + "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.0.tgz", + "integrity": "sha512-iaepho73/2Pz7w2eMS0Q5f83+0RKI7i4xmiYeBmDzfRVbQtTOG7Ts0S4HzJVsTMGI9keU8rNfuZr8DKfSt7Yyg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.27.0" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/types": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.0.tgz", + "integrity": "sha512-H45s8fVLYjbhFH62dIJ3WtmJ6RSPt/3DRO0ZcT2SUiYiQyz3BLVb9ADEnLl91m74aQPS3AzzeajZHYOalWe3bg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.25.9", + "@babel/helper-validator-identifier": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.6.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.6.1.tgz", + "integrity": "sha512-KTsJMmobmbrFLe3LDh0PC2FXpcSYJt/MLjlkh/9LEnmKYLSYmT/0EW9JWANjeoemiuZrmogti0tW5Ch+qNUYDw==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", + "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/config-array": { + "version": "0.20.0", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.20.0.tgz", + "integrity": "sha512-fxlS1kkIjx8+vy2SjuCB94q3htSNrufYTXubwiBFeaQHbH6Ipi43gFJq2zCMt6PHhImH3Xmr0NksKDvchWlpQQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^2.1.6", + "debug": "^4.3.1", + "minimatch": "^3.1.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/config-helpers": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.2.1.tgz", + "integrity": "sha512-RI17tsD2frtDu/3dmI7QRrD4bedNKPM08ziRYaC5AhkGrzIAJelm9kJU1TznK+apx6V+cqRz8tfpEeG3oIyjxw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/core": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.13.0.tgz", + "integrity": "sha512-yfkgDw1KR66rkT5A8ci4irzDysN7FRpq3ttJolR88OqQikAWqwA8j5VZyas+vjyBNFIJ7MfybJ9plMILI2UrCw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, "node_modules/@eslint/eslintrc": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.3.3.tgz", - "integrity": "sha512-uj3pT6Mg+3t39fvLrj8iuCIJ38zKO9FpGtJ4BBJebJhEwjoT+KLVNCcHT5QC9NGRIEi7fZ0ZR8YRb884auB4Lg==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.1.tgz", + "integrity": "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==", "dev": true, + "license": "MIT", "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", - "espree": "^9.4.0", - "globals": "^13.15.0", + "espree": "^10.0.1", + "globals": "^14.0.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", "js-yaml": "^4.1.0", @@ -37,24 +172,95 @@ "strip-json-comments": "^3.1.1" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "url": "https://opencollective.com/eslint" } }, - "node_modules/@humanwhocodes/config-array": { - "version": "0.10.7", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.10.7.tgz", - "integrity": "sha512-MDl6D6sBsaV452/QSdX+4CXIjZhIcI0PELsxUjk4U828yd58vk3bTIvk/6w5FY+4hIy9sLW0sfrV7K7Kc++j/w==", + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@eslint/js": { + "version": "9.25.1", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.25.1.tgz", + "integrity": "sha512-dEIwmjntEx8u3Uvv+kr3PDeeArL8Hw07H9kyYxCjnM9pBjfEhk6uLXSchxxzgiwtRhhzVzqmUSDFBOi1TuZ7qg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/object-schema": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.6.tgz", + "integrity": "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.2.8", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.8.tgz", + "integrity": "sha512-ZAoA40rNMPwSm+AeHpCq8STiNAwzWLJuP8Xv4CHIc9wv/PSuExjMrmjfYNj682vW0OOiZ1HKxzvjQr9XZIisQA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.13.0", + "levn": "^0.4.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@humanfs/core": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", + "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.6", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.6.tgz", + "integrity": "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==", + "dev": true, + "license": "Apache-2.0", "dependencies": { - "@humanwhocodes/object-schema": "^1.2.1", - "debug": "^4.1.1", - "minimatch": "^3.0.4" + "@humanfs/core": "^0.19.1", + "@humanwhocodes/retry": "^0.3.0" }, "engines": { - "node": ">=10.10.0" + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node/node_modules/@humanwhocodes/retry": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz", + "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" } }, "node_modules/@humanwhocodes/module-importer": { @@ -62,6 +268,7 @@ "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", "dev": true, + "license": "Apache-2.0", "engines": { "node": ">=12.22" }, @@ -70,52 +277,142 @@ "url": "https://github.com/sponsors/nzakas" } }, - "node_modules/@humanwhocodes/object-schema": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", - "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", - "dev": true + "node_modules/@humanwhocodes/retry": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.2.tgz", + "integrity": "sha512-xeO57FpIu4p1Ri3Jq/EXq4ClRm86dVF2z/+kvFnyqVYRavTZmaFaUBbWCOuuTh0o/g7DSsk6kc2vrS4Vl5oPOQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } }, - "node_modules/@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz", + "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==", "dev": true, + "license": "MIT", "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" + "@jridgewell/set-array": "^1.2.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.24" }, "engines": { - "node": ">= 8" + "node": ">=6.0.0" } }, - "node_modules/@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", "dev": true, + "license": "MIT", "engines": { - "node": ">= 8" + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", + "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/source-map": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.6.tgz", + "integrity": "sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25" } }, - "node_modules/@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@jsdoc/salty": { + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/@jsdoc/salty/-/salty-0.2.9.tgz", + "integrity": "sha512-yYxMVH7Dqw6nO0d5NIV8OQWnitU8k6vXH8NtgqAfIa/IUqRMxRv/NUJJ08VEKbAakwxlgBl5PJdrU0dMPStsnw==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" + "lodash": "^4.17.21" }, "engines": { - "node": ">= 8" + "node": ">=v12.0.0" } }, + "node_modules/@types/estree": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.7.tgz", + "integrity": "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/linkify-it": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-5.0.0.tgz", + "integrity": "sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/markdown-it": { + "version": "14.1.2", + "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-14.1.2.tgz", + "integrity": "sha512-promo4eFwuiW+TfGxhi+0x3czqTYJkG8qB17ZUJiVF10Xm7NLVRSLUsfRTU/6h1e24VvRnXCx+hG7li58lkzog==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/linkify-it": "^5", + "@types/mdurl": "^2" + } + }, + "node_modules/@types/mdurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-2.0.0.tgz", + "integrity": "sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==", + "dev": true, + "license": "MIT" + }, "node_modules/acorn": { - "version": "8.8.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.0.tgz", - "integrity": "sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w==", + "version": "8.14.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz", + "integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==", "dev": true, + "license": "MIT", "bin": { "acorn": "bin/acorn" }, @@ -128,6 +425,7 @@ "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", "dev": true, + "license": "MIT", "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } @@ -137,6 +435,7 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, + "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -148,20 +447,12 @@ "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, + "license": "MIT", "dependencies": { "color-convert": "^2.0.1" }, @@ -176,67 +467,90 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true - }, - "node_modules/array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", "dev": true, - "engines": { - "node": ">=8" - } + "license": "Python-2.0" }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/bindings": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "license": "MIT", "dependencies": { "file-uri-to-path": "1.0.0" } }, + "node_modules/bluebird": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", + "dev": true, + "license": "MIT" + }, "node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, + "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, - "node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", "dev": true, - "dependencies": { - "fill-range": "^7.0.1" - }, - "engines": { - "node": ">=8" - } + "license": "MIT" }, "node_modules/callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=6" } }, + "node_modules/camel-case": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-4.1.2.tgz", + "integrity": "sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==", + "dev": true, + "license": "MIT", + "dependencies": { + "pascal-case": "^3.1.2", + "tslib": "^2.0.3" + } + }, + "node_modules/catharsis": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/catharsis/-/catharsis-0.9.0.tgz", + "integrity": "sha512-prMTQVpcns/tzFgFVkVp6ak6RykZyWb3gu8ckUpd6YkTlacOd3DXGJjIpD4Q6zJirizvaiAjSSHlOsA+6sNh2A==", + "dev": true, + "license": "MIT", + "dependencies": { + "lodash": "^4.17.15" + }, + "engines": { + "node": ">= 10" + } + }, "node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, + "license": "MIT", "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -248,11 +562,43 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/clean-css": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.3.3.tgz", + "integrity": "sha512-D5J+kHaVb/wKSFcyyV75uCn8fiY4sV38XJoe4CUyGQ+mOU/fMVYUdH1hJC+CJQ5uY3EnW27SbJYS4X8BiLrAFg==", + "dev": true, + "license": "MIT", + "dependencies": { + "source-map": "~0.6.0" + }, + "engines": { + "node": ">= 10.0" + } + }, + "node_modules/clean-jsdoc-theme": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/clean-jsdoc-theme/-/clean-jsdoc-theme-4.3.0.tgz", + "integrity": "sha512-QMrBdZ2KdPt6V2Ytg7dIt0/q32U4COpxvR0UDhPjRRKRL0o0MvRCR5YpY37/4rPF1SI1AYEKAWyof7ndCb/dzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jsdoc/salty": "^0.2.4", + "fs-extra": "^10.1.0", + "html-minifier-terser": "^7.2.0", + "klaw-sync": "^6.0.0", + "lodash": "^4.17.21", + "showdown": "^2.1.0" + }, + "peerDependencies": { + "jsdoc": ">=3.x <=4.x" + } + }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, + "license": "MIT", "dependencies": { "color-name": "~1.1.4" }, @@ -264,19 +610,32 @@ "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true + "dev": true, + "license": "MIT" + }, + "node_modules/commander": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", + "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + } }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "dev": true, + "license": "MIT", "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", @@ -287,12 +646,13 @@ } }, "node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", "dev": true, + "license": "MIT", "dependencies": { - "ms": "2.1.2" + "ms": "^2.1.3" }, "engines": { "node": ">=6.0" @@ -307,30 +667,31 @@ "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true + "dev": true, + "license": "MIT" }, - "node_modules/dir-glob": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "node_modules/dot-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz", + "integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==", "dev": true, + "license": "MIT", "dependencies": { - "path-type": "^4.0.0" - }, - "engines": { - "node": ">=8" + "no-case": "^3.0.4", + "tslib": "^2.0.3" } }, - "node_modules/doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", "dev": true, - "dependencies": { - "esutils": "^2.0.2" - }, + "license": "BSD-2-Clause", "engines": { - "node": ">=6.0.0" + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" } }, "node_modules/escape-string-regexp": { @@ -338,6 +699,7 @@ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "dev": true, + "license": "MIT", "engines": { "node": ">=10" }, @@ -346,131 +708,120 @@ } }, "node_modules/eslint": { - "version": "8.25.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.25.0.tgz", - "integrity": "sha512-DVlJOZ4Pn50zcKW5bYH7GQK/9MsoQG2d5eDH0ebEkE8PbgzTTmtt/VTH9GGJ4BfeZCpBLqFfvsjX35UacUL83A==", + "version": "9.25.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.25.1.tgz", + "integrity": "sha512-E6Mtz9oGQWDCpV12319d59n4tx9zOTXSTmc8BLVxBx+G/0RdM5MvEEJLU9c0+aleoePYYgVTOsRblx433qmhWQ==", "dev": true, + "license": "MIT", "dependencies": { - "@eslint/eslintrc": "^1.3.3", - "@humanwhocodes/config-array": "^0.10.5", + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.20.0", + "@eslint/config-helpers": "^0.2.1", + "@eslint/core": "^0.13.0", + "@eslint/eslintrc": "^3.3.1", + "@eslint/js": "9.25.1", + "@eslint/plugin-kit": "^0.2.8", + "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", - "ajv": "^6.10.0", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "@types/json-schema": "^7.0.15", + "ajv": "^6.12.4", "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", + "cross-spawn": "^7.0.6", "debug": "^4.3.2", - "doctrine": "^3.0.0", "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.1.1", - "eslint-utils": "^3.0.0", - "eslint-visitor-keys": "^3.3.0", - "espree": "^9.4.0", - "esquery": "^1.4.0", + "eslint-scope": "^8.3.0", + "eslint-visitor-keys": "^4.2.0", + "espree": "^10.3.0", + "esquery": "^1.5.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^6.0.1", + "file-entry-cache": "^8.0.0", "find-up": "^5.0.0", - "glob-parent": "^6.0.1", - "globals": "^13.15.0", - "globby": "^11.1.0", - "grapheme-splitter": "^1.0.4", + "glob-parent": "^6.0.2", "ignore": "^5.2.0", - "import-fresh": "^3.0.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", - "js-sdsl": "^4.1.4", - "js-yaml": "^4.1.0", "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.4.1", "lodash.merge": "^4.6.2", "minimatch": "^3.1.2", "natural-compare": "^1.4.0", - "optionator": "^0.9.1", - "regexpp": "^3.2.0", - "strip-ansi": "^6.0.1", - "strip-json-comments": "^3.1.0", - "text-table": "^0.2.0" + "optionator": "^0.9.3" }, "bin": { "eslint": "bin/eslint.js" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { - "url": "https://opencollective.com/eslint" + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } } }, "node_modules/eslint-scope": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz", - "integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==", + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.3.0.tgz", + "integrity": "sha512-pUNxi75F8MJ/GdeKtVLSbYg4ZI34J6C0C7sbL4YOp2exGwen7ZsuBqKzUhXd0qMQ362yET3z+uPwKeg/0C2XCQ==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^5.2.0" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - } - }, - "node_modules/eslint-utils": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", - "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", - "dev": true, - "dependencies": { - "eslint-visitor-keys": "^2.0.0" - }, - "engines": { - "node": "^10.0.0 || ^12.0.0 || >= 14.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { - "url": "https://github.com/sponsors/mysticatea" - }, - "peerDependencies": { - "eslint": ">=5" - } - }, - "node_modules/eslint-utils/node_modules/eslint-visitor-keys": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", - "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", - "dev": true, - "engines": { - "node": ">=10" + "url": "https://opencollective.com/eslint" } }, "node_modules/eslint-visitor-keys": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", - "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", + "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", "dev": true, + "license": "Apache-2.0", "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, "node_modules/espree": { - "version": "9.4.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.4.0.tgz", - "integrity": "sha512-DQmnRpLj7f6TgN/NYb0MTzJXL+vJF9h3pHy4JhCIs3zwcgez8xmGg3sXHcEO97BrmO2OSvCwMdfdlyl+E9KjOw==", + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.3.0.tgz", + "integrity": "sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { - "acorn": "^8.8.0", + "acorn": "^8.14.0", "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.3.0" + "eslint-visitor-keys": "^4.2.0" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "url": "https://opencollective.com/eslint" } }, "node_modules/esquery": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", - "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", + "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { "estraverse": "^5.1.0" }, @@ -483,6 +834,7 @@ "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { "estraverse": "^5.2.0" }, @@ -495,6 +847,7 @@ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "dev": true, + "license": "BSD-2-Clause", "engines": { "node": ">=4.0" } @@ -504,6 +857,7 @@ "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", "dev": true, + "license": "BSD-2-Clause", "engines": { "node": ">=0.10.0" } @@ -512,91 +866,48 @@ "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true - }, - "node_modules/fast-glob": { - "version": "3.2.12", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", - "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==", - "dev": true, - "dependencies": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.4" - }, - "engines": { - "node": ">=8.6.0" - } - }, - "node_modules/fast-glob/node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", "dev": true, - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } + "license": "MIT" }, "node_modules/fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/fast-levenshtein": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", - "dev": true - }, - "node_modules/fastq": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", - "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", "dev": true, - "dependencies": { - "reusify": "^1.0.4" - } + "license": "MIT" }, "node_modules/file-entry-cache": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", - "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", "dev": true, + "license": "MIT", "dependencies": { - "flat-cache": "^3.0.4" + "flat-cache": "^4.0.0" }, "engines": { - "node": "^10.12.0 || >=12.0.0" + "node": ">=16.0.0" } }, "node_modules/file-uri-to-path": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", - "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==" - }, - "node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", + "license": "MIT" }, "node_modules/find-up": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", "dev": true, + "license": "MIT", "dependencies": { "locate-path": "^6.0.0", "path-exists": "^4.0.0" @@ -609,48 +920,39 @@ } }, "node_modules/flat-cache": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", - "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", "dev": true, + "license": "MIT", "dependencies": { - "flatted": "^3.1.0", - "rimraf": "^3.0.2" + "flatted": "^3.2.9", + "keyv": "^4.5.4" }, "engines": { - "node": "^10.12.0 || >=12.0.0" + "node": ">=16" } }, "node_modules/flatted": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz", - "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==", - "dev": true - }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", + "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", + "dev": true, + "license": "ISC" }, - "node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", "dev": true, + "license": "MIT", "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" }, "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": ">=12" } }, "node_modules/glob-parent": { @@ -658,6 +960,7 @@ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", "dev": true, + "license": "ISC", "dependencies": { "is-glob": "^4.0.3" }, @@ -666,69 +969,73 @@ } }, "node_modules/globals": { - "version": "13.17.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.17.0.tgz", - "integrity": "sha512-1C+6nQRb1GwGMKm2dH/E7enFAMxGTmGI7/dEdhy/DNelv85w9B72t3uc5frtMNXIbzrarJJ/lTCjcaZwbLJmyw==", + "version": "15.15.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-15.15.0.tgz", + "integrity": "sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==", "dev": true, - "dependencies": { - "type-fest": "^0.20.2" - }, + "license": "MIT", "engines": { - "node": ">=8" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/globby": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", - "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", "dev": true, - "dependencies": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.9", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/grapheme-splitter": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz", - "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==", - "dev": true + "license": "ISC" }, "node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } }, + "node_modules/html-minifier-terser": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/html-minifier-terser/-/html-minifier-terser-7.2.0.tgz", + "integrity": "sha512-tXgn3QfqPIpGl9o+K5tpcj3/MN4SfLtsx2GWwBC3SSd0tXQGyF3gsSqad8loJgKZGM3ZxbYDd5yhiBIdWpmvLA==", + "dev": true, + "license": "MIT", + "dependencies": { + "camel-case": "^4.1.2", + "clean-css": "~5.3.2", + "commander": "^10.0.0", + "entities": "^4.4.0", + "param-case": "^3.0.4", + "relateurl": "^0.2.7", + "terser": "^5.15.1" + }, + "bin": { + "html-minifier-terser": "cli.js" + }, + "engines": { + "node": "^14.13.1 || >=16.0.0" + } + }, "node_modules/ignore": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", - "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", "dev": true, + "license": "MIT", "engines": { "node": ">= 4" } }, "node_modules/import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", "dev": true, + "license": "MIT", "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" @@ -745,31 +1052,17 @@ "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.8.19" } }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "dev": true, - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true - }, "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -779,6 +1072,7 @@ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", "dev": true, + "license": "MIT", "dependencies": { "is-extglob": "^2.1.1" }, @@ -786,40 +1080,28 @@ "node": ">=0.10.0" } }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, - "engines": { - "node": ">=0.12.0" - } - }, "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/isomorphic-ws": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/isomorphic-ws/-/isomorphic-ws-4.0.1.tgz", "integrity": "sha512-BhBvN2MBpWTaSHdWRb/bwdZJ1WaehQ2L1KngkCkfLUGF0mAWAT1sQUQacEmQ0jXkFw/czDXPNQSL5u2/Krsz1w==", + "license": "MIT", "peerDependencies": { "ws": "*" } }, - "node_modules/js-sdsl": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.1.5.tgz", - "integrity": "sha512-08bOAKweV2NUC1wqTtf3qZlnpOX/R2DU9ikpjOHs0H+ibQv3zpncVQg6um4uYtRtrwIX8M4Nh3ytK4HGlYAq7Q==", - "dev": true - }, "node_modules/js-yaml": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", "dev": true, + "license": "MIT", "dependencies": { "argparse": "^2.0.1" }, @@ -827,23 +1109,126 @@ "js-yaml": "bin/js-yaml.js" } }, + "node_modules/js2xmlparser": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/js2xmlparser/-/js2xmlparser-4.0.2.tgz", + "integrity": "sha512-6n4D8gLlLf1n5mNLQPRfViYzu9RATblzPEtm1SthMX1Pjao0r9YI9nw7ZIfRxQMERS87mcswrg+r/OYrPRX6jA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "xmlcreate": "^2.0.4" + } + }, + "node_modules/jsdoc": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/jsdoc/-/jsdoc-4.0.4.tgz", + "integrity": "sha512-zeFezwyXeG4syyYHbvh1A967IAqq/67yXtXvuL5wnqCkFZe8I0vKfm+EO+YEvLguo6w9CDUbrAXVtJSHh2E8rw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@babel/parser": "^7.20.15", + "@jsdoc/salty": "^0.2.1", + "@types/markdown-it": "^14.1.1", + "bluebird": "^3.7.2", + "catharsis": "^0.9.0", + "escape-string-regexp": "^2.0.0", + "js2xmlparser": "^4.0.2", + "klaw": "^3.0.0", + "markdown-it": "^14.1.0", + "markdown-it-anchor": "^8.6.7", + "marked": "^4.0.10", + "mkdirp": "^1.0.4", + "requizzle": "^0.2.3", + "strip-json-comments": "^3.1.0", + "underscore": "~1.13.2" + }, + "bin": { + "jsdoc": "jsdoc.js" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/jsdoc/node_modules/escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "license": "MIT" + }, "node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/json-stable-stringify-without-jsonify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", - "dev": true + "dev": true, + "license": "MIT" + }, + "node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/klaw": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/klaw/-/klaw-3.0.0.tgz", + "integrity": "sha512-0Fo5oir+O9jnXu5EefYbVK+mHMBeEVEy2cmctR1O1NECcCkPRreJKrS6Qt/j3KC2C148Dfo9i3pCmCMsdqGr0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.1.9" + } + }, + "node_modules/klaw-sync": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/klaw-sync/-/klaw-sync-6.0.0.tgz", + "integrity": "sha512-nIeuVSzdCCs6TDPTqI8w1Yre34sSq7AkZ4B3sfOBbI2CgVSB4Du4aLQijFU2+lhAFCwt9+42Hel6lQNIv6AntQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.1.11" + } }, "node_modules/levn": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", "dev": true, + "license": "MIT", "dependencies": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" @@ -852,11 +1237,22 @@ "node": ">= 0.8.0" } }, + "node_modules/linkify-it": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz", + "integrity": "sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "uc.micro": "^2.0.0" + } + }, "node_modules/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", "dev": true, + "license": "MIT", "dependencies": { "p-locate": "^5.0.0" }, @@ -867,39 +1263,85 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true, + "license": "MIT" + }, "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true + "dev": true, + "license": "MIT" }, - "node_modules/merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "node_modules/lower-case": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", + "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==", "dev": true, - "engines": { - "node": ">= 8" + "license": "MIT", + "dependencies": { + "tslib": "^2.0.3" } }, - "node_modules/micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "node_modules/markdown-it": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.0.tgz", + "integrity": "sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==", "dev": true, + "license": "MIT", "dependencies": { - "braces": "^3.0.2", - "picomatch": "^2.3.1" + "argparse": "^2.0.1", + "entities": "^4.4.0", + "linkify-it": "^5.0.0", + "mdurl": "^2.0.0", + "punycode.js": "^2.3.1", + "uc.micro": "^2.1.0" + }, + "bin": { + "markdown-it": "bin/markdown-it.mjs" + } + }, + "node_modules/markdown-it-anchor": { + "version": "8.6.7", + "resolved": "https://registry.npmjs.org/markdown-it-anchor/-/markdown-it-anchor-8.6.7.tgz", + "integrity": "sha512-FlCHFwNnutLgVTflOYHPW2pPcl2AACqVzExlkGQNsi4CJgqOHN7YTgDd4LuhgN1BFO3TS0vLAruV1Td6dwWPJA==", + "dev": true, + "license": "Unlicense", + "peerDependencies": { + "@types/markdown-it": "*", + "markdown-it": "*" + } + }, + "node_modules/marked": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/marked/-/marked-4.3.0.tgz", + "integrity": "sha512-PRsaiG84bK+AMvxziE/lCFss8juXjNaWzVbN5tXAm4XjeaS9NAHhop+PjQxz2A9h8Q4M/xGmzP8vqNwy6JeK0A==", + "dev": true, + "license": "MIT", + "bin": { + "marked": "bin/marked.js" }, "engines": { - "node": ">=8.6" + "node": ">= 12" } }, + "node_modules/mdurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.0.0.tgz", + "integrity": "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==", + "dev": true, + "license": "MIT" + }, "node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, + "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" }, @@ -907,44 +1349,63 @@ "node": "*" } }, + "node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "dev": true, + "license": "MIT", + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" }, "node_modules/nan": { - "version": "2.15.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.15.0.tgz", - "integrity": "sha512-8ZtvEnA2c5aYCZYd1cvgdnU6cqwixRoYg70xPLWUws5ORTa/lnw+u4amixRS/Ac5U5mQVgp9pnlSUnbNWFaWZQ==" + "version": "2.22.2", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.22.2.tgz", + "integrity": "sha512-DANghxFkS1plDdRsX0X9pm0Z6SJNN6gBdtXfanwoZ8hooC5gosGFSBGRYHUVPz1asKA/kMRqDRdHrluZ61SpBQ==", + "license": "MIT" }, "node_modules/natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", - "dev": true + "dev": true, + "license": "MIT" }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "node_modules/no-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", + "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==", "dev": true, + "license": "MIT", "dependencies": { - "wrappy": "1" + "lower-case": "^2.0.2", + "tslib": "^2.0.3" } }, "node_modules/optionator": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", - "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", "dev": true, + "license": "MIT", "dependencies": { "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", "levn": "^0.4.1", "prelude-ls": "^1.2.1", "type-check": "^0.4.0", - "word-wrap": "^1.2.3" + "word-wrap": "^1.2.5" }, "engines": { "node": ">= 0.8.0" @@ -955,6 +1416,7 @@ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", "dev": true, + "license": "MIT", "dependencies": { "yocto-queue": "^0.1.0" }, @@ -970,6 +1432,7 @@ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", "dev": true, + "license": "MIT", "dependencies": { "p-limit": "^3.0.2" }, @@ -980,11 +1443,23 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/param-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz", + "integrity": "sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==", + "dev": true, + "license": "MIT", + "dependencies": { + "dot-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", "dev": true, + "license": "MIT", "dependencies": { "callsites": "^3.0.0" }, @@ -992,159 +1467,95 @@ "node": ">=6" } }, + "node_modules/pascal-case": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.2.tgz", + "integrity": "sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==", + "dev": true, + "license": "MIT", + "dependencies": { + "no-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/path-key": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } }, - "node_modules/path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.8.0" } }, "node_modules/punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", "dev": true, + "license": "MIT", "engines": { "node": ">=6" } }, - "node_modules/queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/regexpp": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", - "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", - "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/mysticatea" - } - }, - "node_modules/resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "node_modules/punycode.js": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz", + "integrity": "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==", "dev": true, + "license": "MIT", "engines": { - "node": ">=4" + "node": ">=6" } }, - "node_modules/reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "node_modules/relateurl": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz", + "integrity": "sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog==", "dev": true, + "license": "MIT", "engines": { - "iojs": ">=1.0.0", - "node": ">=0.10.0" + "node": ">= 0.10" } }, - "node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "node_modules/requizzle": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/requizzle/-/requizzle-0.2.4.tgz", + "integrity": "sha512-JRrFk1D4OQ4SqovXOgdav+K8EAhSB/LJZqCz8tbX0KObcdeM15Ss59ozWMBWmmINMagCwmqn4ZNryUGpBsl6Jw==", "dev": true, + "license": "MIT", "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "lodash": "^4.17.21" } }, - "node_modules/run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "dependencies": { - "queue-microtask": "^1.2.2" + "license": "MIT", + "engines": { + "node": ">=4" } }, "node_modules/shebang-command": { @@ -1152,6 +1563,7 @@ "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", "dev": true, + "license": "MIT", "dependencies": { "shebang-regex": "^3.0.0" }, @@ -1164,29 +1576,57 @@ "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } }, - "node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "node_modules/showdown": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/showdown/-/showdown-2.1.0.tgz", + "integrity": "sha512-/6NVYu4U819R2pUIk79n67SYgJHWCce0a5xTP979WbNp0FL9MN1I1QK662IDU1b6JzKTvmhgI7T7JYIxBi3kMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "commander": "^9.0.0" + }, + "bin": { + "showdown": "bin/showdown.js" + }, + "funding": { + "type": "individual", + "url": "https://www.paypal.me/tiviesantos" + } + }, + "node_modules/showdown/node_modules/commander": { + "version": "9.5.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz", + "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==", "dev": true, + "license": "MIT", "engines": { - "node": ">=8" + "node": "^12.20.0 || >=14" } }, - "node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, + "license": "BSD-3-Clause", "engines": { - "node": ">=8" + "node": ">=0.10.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" } }, "node_modules/strip-json-comments": { @@ -1194,6 +1634,7 @@ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" }, @@ -1206,6 +1647,7 @@ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, + "license": "MIT", "dependencies": { "has-flag": "^4.0.0" }, @@ -1213,29 +1655,45 @@ "node": ">=8" } }, - "node_modules/text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", - "dev": true - }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "node_modules/terser": { + "version": "5.39.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.39.0.tgz", + "integrity": "sha512-LBAhFyLho16harJoWMg/nZsQYgTrg5jXOn2nCYjRUcZZEdE3qa2zb8QEDRUGVZBW4rlazf2fxkg8tztybTaqWw==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { - "is-number": "^7.0.0" + "@jridgewell/source-map": "^0.3.3", + "acorn": "^8.8.2", + "commander": "^2.20.0", + "source-map-support": "~0.5.20" + }, + "bin": { + "terser": "bin/terser" }, "engines": { - "node": ">=8.0" + "node": ">=10" } }, + "node_modules/terser/node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" + }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", "dev": true, + "license": "MIT", "dependencies": { "prelude-ls": "^1.2.1" }, @@ -1243,26 +1701,39 @@ "node": ">= 0.8.0" } }, - "node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "node_modules/uc.micro": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz", + "integrity": "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==", + "dev": true, + "license": "MIT" + }, + "node_modules/underscore": { + "version": "1.13.7", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.7.tgz", + "integrity": "sha512-GMXzWtsc57XAtguZgaQViUOzs0KTkk8ojr3/xAxXLITqf/3EMwxC0inyETfDFjH/Krbhuep0HNbbjI9i/q3F3g==", + "dev": true, + "license": "MIT" + }, + "node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", "dev": true, + "license": "MIT", "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">= 10.0.0" } }, "node_modules/unix-dgram": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/unix-dgram/-/unix-dgram-2.0.4.tgz", - "integrity": "sha512-7tpK6x7ls7J7pDrrAU63h93R0dVhRbPwiRRCawR10cl+2e1VOvF3bHlVJc6WI1dl/8qk5He673QU+Ogv7bPNaw==", + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/unix-dgram/-/unix-dgram-2.0.6.tgz", + "integrity": "sha512-AURroAsb73BZ6CdAyMrTk/hYKNj3DuYYEuOaB8bYMOHGKupRNScw90Q5C71tWJc3uE7dIeXRyuwN0xLLq3vDTg==", "hasInstallScript": true, + "license": "ISC", "dependencies": { - "bindings": "^1.3.0", - "nan": "^2.13.2" + "bindings": "^1.5.0", + "nan": "^2.16.0" }, "engines": { "node": ">=0.10.48" @@ -1273,6 +1744,7 @@ "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { "punycode": "^2.1.0" } @@ -1282,6 +1754,7 @@ "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", "dev": true, + "license": "ISC", "dependencies": { "isexe": "^2.0.0" }, @@ -1293,30 +1766,26 @@ } }, "node_modules/word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true - }, "node_modules/ws": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.3.0.tgz", - "integrity": "sha512-Gs5EZtpqZzLvmIM59w4igITU57lrtYVFneaa434VROv4thzJyV6UjIL3D42lslWlI+D4KzLYnxSwtfuiO79sNw==", + "version": "8.18.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.1.tgz", + "integrity": "sha512-RKW2aJZMXeMxVpnZ6bck+RswznaxmzdULiBr6KY7XkTnW8uvt0iT9H5DkHUChXrc+uurzwa0rVI16n/Xzjdz1w==", + "license": "MIT", "engines": { "node": ">=10.0.0" }, "peerDependencies": { "bufferutil": "^4.0.1", - "utf-8-validate": "^5.0.2" + "utf-8-validate": ">=5.0.2" }, "peerDependenciesMeta": { "bufferutil": { @@ -1327,11 +1796,19 @@ } } }, + "node_modules/xmlcreate": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/xmlcreate/-/xmlcreate-2.0.4.tgz", + "integrity": "sha512-nquOebG4sngPmGPICTS5EnxqhKbCmz5Ox5hsszI2T6U5qdrJizBc+0ilYSEjTSzU0yZcmvppztXe/5Al5fUwdg==", + "dev": true, + "license": "Apache-2.0" + }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", "dev": true, + "license": "MIT", "engines": { "node": ">=10" }, @@ -1339,954 +1816,5 @@ "url": "https://github.com/sponsors/sindresorhus" } } - }, - "dependencies": { - "@eslint/eslintrc": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.3.3.tgz", - "integrity": "sha512-uj3pT6Mg+3t39fvLrj8iuCIJ38zKO9FpGtJ4BBJebJhEwjoT+KLVNCcHT5QC9NGRIEi7fZ0ZR8YRb884auB4Lg==", - "dev": true, - "requires": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^9.4.0", - "globals": "^13.15.0", - "ignore": "^5.2.0", - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "minimatch": "^3.1.2", - "strip-json-comments": "^3.1.1" - } - }, - "@humanwhocodes/config-array": { - "version": "0.10.7", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.10.7.tgz", - "integrity": "sha512-MDl6D6sBsaV452/QSdX+4CXIjZhIcI0PELsxUjk4U828yd58vk3bTIvk/6w5FY+4hIy9sLW0sfrV7K7Kc++j/w==", - "dev": true, - "requires": { - "@humanwhocodes/object-schema": "^1.2.1", - "debug": "^4.1.1", - "minimatch": "^3.0.4" - } - }, - "@humanwhocodes/module-importer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", - "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", - "dev": true - }, - "@humanwhocodes/object-schema": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", - "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", - "dev": true - }, - "@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "dev": true, - "requires": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - } - }, - "@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "dev": true - }, - "@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "dev": true, - "requires": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - } - }, - "acorn": { - "version": "8.8.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.0.tgz", - "integrity": "sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w==", - "dev": true - }, - "acorn-jsx": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "dev": true, - "requires": {} - }, - "ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "requires": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - }, - "ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true - }, - "array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", - "dev": true - }, - "balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true - }, - "bindings": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", - "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", - "requires": { - "file-uri-to-path": "1.0.0" - } - }, - "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, - "requires": { - "fill-range": "^7.0.1" - } - }, - "callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true - }, - "cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dev": true, - "requires": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - } - }, - "debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dev": true, - "requires": { - "ms": "2.1.2" - } - }, - "deep-is": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true - }, - "dir-glob": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", - "dev": true, - "requires": { - "path-type": "^4.0.0" - } - }, - "doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", - "dev": true, - "requires": { - "esutils": "^2.0.2" - } - }, - "escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true - }, - "eslint": { - "version": "8.25.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.25.0.tgz", - "integrity": "sha512-DVlJOZ4Pn50zcKW5bYH7GQK/9MsoQG2d5eDH0ebEkE8PbgzTTmtt/VTH9GGJ4BfeZCpBLqFfvsjX35UacUL83A==", - "dev": true, - "requires": { - "@eslint/eslintrc": "^1.3.3", - "@humanwhocodes/config-array": "^0.10.5", - "@humanwhocodes/module-importer": "^1.0.1", - "ajv": "^6.10.0", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", - "debug": "^4.3.2", - "doctrine": "^3.0.0", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.1.1", - "eslint-utils": "^3.0.0", - "eslint-visitor-keys": "^3.3.0", - "espree": "^9.4.0", - "esquery": "^1.4.0", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^6.0.1", - "find-up": "^5.0.0", - "glob-parent": "^6.0.1", - "globals": "^13.15.0", - "globby": "^11.1.0", - "grapheme-splitter": "^1.0.4", - "ignore": "^5.2.0", - "import-fresh": "^3.0.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "js-sdsl": "^4.1.4", - "js-yaml": "^4.1.0", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.4.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.1.2", - "natural-compare": "^1.4.0", - "optionator": "^0.9.1", - "regexpp": "^3.2.0", - "strip-ansi": "^6.0.1", - "strip-json-comments": "^3.1.0", - "text-table": "^0.2.0" - } - }, - "eslint-scope": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz", - "integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==", - "dev": true, - "requires": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - } - }, - "eslint-utils": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", - "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", - "dev": true, - "requires": { - "eslint-visitor-keys": "^2.0.0" - }, - "dependencies": { - "eslint-visitor-keys": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", - "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", - "dev": true - } - } - }, - "eslint-visitor-keys": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", - "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", - "dev": true - }, - "espree": { - "version": "9.4.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.4.0.tgz", - "integrity": "sha512-DQmnRpLj7f6TgN/NYb0MTzJXL+vJF9h3pHy4JhCIs3zwcgez8xmGg3sXHcEO97BrmO2OSvCwMdfdlyl+E9KjOw==", - "dev": true, - "requires": { - "acorn": "^8.8.0", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.3.0" - } - }, - "esquery": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", - "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", - "dev": true, - "requires": { - "estraverse": "^5.1.0" - } - }, - "esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "dev": true, - "requires": { - "estraverse": "^5.2.0" - } - }, - "estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true - }, - "esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true - }, - "fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true - }, - "fast-glob": { - "version": "3.2.12", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", - "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==", - "dev": true, - "requires": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.4" - }, - "dependencies": { - "glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "requires": { - "is-glob": "^4.0.1" - } - } - } - }, - "fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true - }, - "fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", - "dev": true - }, - "fastq": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", - "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", - "dev": true, - "requires": { - "reusify": "^1.0.4" - } - }, - "file-entry-cache": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", - "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", - "dev": true, - "requires": { - "flat-cache": "^3.0.4" - } - }, - "file-uri-to-path": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", - "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==" - }, - "fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, - "requires": { - "to-regex-range": "^5.0.1" - } - }, - "find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "dev": true, - "requires": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - } - }, - "flat-cache": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", - "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", - "dev": true, - "requires": { - "flatted": "^3.1.0", - "rimraf": "^3.0.2" - } - }, - "flatted": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz", - "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==", - "dev": true - }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true - }, - "glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "dev": true, - "requires": { - "is-glob": "^4.0.3" - } - }, - "globals": { - "version": "13.17.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.17.0.tgz", - "integrity": "sha512-1C+6nQRb1GwGMKm2dH/E7enFAMxGTmGI7/dEdhy/DNelv85w9B72t3uc5frtMNXIbzrarJJ/lTCjcaZwbLJmyw==", - "dev": true, - "requires": { - "type-fest": "^0.20.2" - } - }, - "globby": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", - "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", - "dev": true, - "requires": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.9", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^3.0.0" - } - }, - "grapheme-splitter": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz", - "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "ignore": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", - "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", - "dev": true - }, - "import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", - "dev": true, - "requires": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - } - }, - "imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", - "dev": true - }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "dev": true, - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true - }, - "is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true - }, - "is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, - "requires": { - "is-extglob": "^2.1.1" - } - }, - "is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true - }, - "isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true - }, - "isomorphic-ws": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/isomorphic-ws/-/isomorphic-ws-4.0.1.tgz", - "integrity": "sha512-BhBvN2MBpWTaSHdWRb/bwdZJ1WaehQ2L1KngkCkfLUGF0mAWAT1sQUQacEmQ0jXkFw/czDXPNQSL5u2/Krsz1w==", - "requires": {} - }, - "js-sdsl": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.1.5.tgz", - "integrity": "sha512-08bOAKweV2NUC1wqTtf3qZlnpOX/R2DU9ikpjOHs0H+ibQv3zpncVQg6um4uYtRtrwIX8M4Nh3ytK4HGlYAq7Q==", - "dev": true - }, - "js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, - "requires": { - "argparse": "^2.0.1" - } - }, - "json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true - }, - "json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", - "dev": true - }, - "levn": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", - "dev": true, - "requires": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" - } - }, - "locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "dev": true, - "requires": { - "p-locate": "^5.0.0" - } - }, - "lodash.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true - }, - "merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "dev": true - }, - "micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", - "dev": true, - "requires": { - "braces": "^3.0.2", - "picomatch": "^2.3.1" - } - }, - "minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "nan": { - "version": "2.15.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.15.0.tgz", - "integrity": "sha512-8ZtvEnA2c5aYCZYd1cvgdnU6cqwixRoYg70xPLWUws5ORTa/lnw+u4amixRS/Ac5U5mQVgp9pnlSUnbNWFaWZQ==" - }, - "natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", - "dev": true - }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dev": true, - "requires": { - "wrappy": "1" - } - }, - "optionator": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", - "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", - "dev": true, - "requires": { - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0", - "word-wrap": "^1.2.3" - } - }, - "p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, - "requires": { - "yocto-queue": "^0.1.0" - } - }, - "p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "dev": true, - "requires": { - "p-limit": "^3.0.2" - } - }, - "parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, - "requires": { - "callsites": "^3.0.0" - } - }, - "path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true - }, - "path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "dev": true - }, - "path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true - }, - "path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "dev": true - }, - "picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true - }, - "prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", - "dev": true - }, - "punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", - "dev": true - }, - "queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "dev": true - }, - "regexpp": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", - "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", - "dev": true - }, - "resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true - }, - "reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", - "dev": true - }, - "rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, - "requires": { - "glob": "^7.1.3" - } - }, - "run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "dev": true, - "requires": { - "queue-microtask": "^1.2.2" - } - }, - "shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "requires": { - "shebang-regex": "^3.0.0" - } - }, - "shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true - }, - "slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true - }, - "strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.1" - } - }, - "strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - }, - "text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", - "dev": true - }, - "to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "requires": { - "is-number": "^7.0.0" - } - }, - "type-check": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", - "dev": true, - "requires": { - "prelude-ls": "^1.2.1" - } - }, - "type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "dev": true - }, - "unix-dgram": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/unix-dgram/-/unix-dgram-2.0.4.tgz", - "integrity": "sha512-7tpK6x7ls7J7pDrrAU63h93R0dVhRbPwiRRCawR10cl+2e1VOvF3bHlVJc6WI1dl/8qk5He673QU+Ogv7bPNaw==", - "requires": { - "bindings": "^1.3.0", - "nan": "^2.13.2" - } - }, - "uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, - "requires": { - "punycode": "^2.1.0" - } - }, - "which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - }, - "word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", - "dev": true - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true - }, - "ws": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.3.0.tgz", - "integrity": "sha512-Gs5EZtpqZzLvmIM59w4igITU57lrtYVFneaa434VROv4thzJyV6UjIL3D42lslWlI+D4KzLYnxSwtfuiO79sNw==", - "requires": {} - }, - "yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "dev": true - } } } diff --git a/package.json b/package.json index 0902f2e..83af4c5 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "janode", "description": "Meetecho adapter for the Janus WebRTC Server", - "version": "1.6.6", + "version": "1.8.0", "type": "module", "keywords": [ "janus", @@ -16,17 +16,22 @@ }, "repository": { "type": "git", - "url": "https://github.com/meetecho/janode.git" + "url": "git+https://github.com/meetecho/janode.git" }, "bugs": { "url": "https://github.com/meetecho/janode/issues" }, "license": "ISC", + "main": "./src/janode.js", "exports": { ".": "./src/janode.js", + "./handle": "./src/handle.js", "./plugins/audiobridge": "./src/plugins/audiobridge-plugin.js", "./plugins/echotest": "./src/plugins/echotest-plugin.js", + "./plugins/recordplay": "./src/plugins/recordplay-plugin.js", + "./plugins/sip": "./src/plugins/sip-plugin.js", "./plugins/streaming": "./src/plugins/streaming-plugin.js", + "./plugins/textroom": "./src/plugins/textroom-plugin.js", "./plugins/videoroom": "./src/plugins/videoroom-plugin.js" }, "files": [ @@ -36,17 +41,22 @@ ], "dependencies": { "isomorphic-ws": "^4.0.1", - "ws": "^8.0.0", - "unix-dgram": "^2.0.4" + "unix-dgram": "^2.0.4", + "ws": "^8.0.0" }, "devDependencies": { - "eslint": "^8.25.0" + "@eslint/js": "^9.4.0", + "clean-jsdoc-theme": "^4.3.0", + "eslint": "^9.4.0", + "globals": "^15.4.0", + "jsdoc": "^4.0.4" }, "engines": { - "node": " >=14.13.1 || >=16.0.0" + "node": " >=18.18.0" }, "scripts": { "build": "npm install --omit=dev", - "lint": "node_modules/.bin/eslint --ext .js --config .eslintrc.json src" + "lint": "npx eslint --debug", + "build-docs": "rm -rf ./docs && npx jsdoc -c ./jsdoc.json --destination ./docs ./src" } } diff --git a/pre-release.sh b/pre-release.sh new file mode 100755 index 0000000..d08bac2 --- /dev/null +++ b/pre-release.sh @@ -0,0 +1,11 @@ +#!/bin/bash -eu + +REL=$1 + +if [ -z "$REL" ]; then + echo "A release name must be specified" + exit 1 +fi + +npm version "$REL" -m "version: bump to $REL" +npm publish --dry-run diff --git a/src/configuration.js b/src/configuration.js index 0845ab8..2709bb7 100644 --- a/src/configuration.js +++ b/src/configuration.js @@ -78,7 +78,7 @@ class Configuration { /** * Return the specific WebSocket transport options. * - * @returns {object} + * @returns {Object} */ wsOptions() { return this.ws_options; diff --git a/src/connection.js b/src/connection.js index 9638f4c..71e7a62 100644 --- a/src/connection.js +++ b/src/connection.js @@ -3,7 +3,7 @@ /** * This module contains the Connection class definition. * @module connection - * @access private + * @private */ import { EventEmitter } from 'events'; @@ -87,13 +87,9 @@ class Connection extends EventEmitter { this.name = `[${this.id}]`; /** - * The internal transport that will be used for the connection. + * The transport used by this connection. * - * @typedef {object} Transport - * @property {function} open - * @property {function} close - * @property {function} send - * @property {function} getRemoteHostname + * @private */ this._transport = { open: async _ => { throw new Error('transport does not implement the "open" function'); }, @@ -139,10 +135,10 @@ class Connection extends EventEmitter { if (graceful) { /* This is a greceful teardown */ /** - * The connection has been closed. + * The connection has been gracefully closed. * * @event module:connection~Connection#event:CONNECTION_CLOSED - * @type {object} + * @type {Object} * @property {number} id - The connection identifier */ this.emit(JANODE.EVENT.CONNECTION_CLOSED, { id: this.id }); @@ -151,7 +147,7 @@ class Connection extends EventEmitter { /* If this event is unexpected emit an error */ const error = new Error('unexpected disconnection'); /** - * An error occurred on the connection. + * The connection has been unexpectedly closed. * * @event module:connection~Connection#event:CONNECTION_ERROR * @type {Error} @@ -180,7 +176,7 @@ class Connection extends EventEmitter { * the transaction will be closed. * * @private - * @param {object} janus_message + * @param {Object} janus_message */ _handleMessage(janus_message) { const { session_id, transaction, janus } = janus_message; @@ -239,7 +235,7 @@ class Connection extends EventEmitter { * Decorate request with apisecret, token and transaction (if missing). * * @private - * @param {object} request + * @param {Object} request */ _decorateRequest(request) { request.transaction = request.transaction || getNumericID(); @@ -266,8 +262,8 @@ class Connection extends EventEmitter { /** * Send a request from this connection using the transport defined send method. * - * @param {object} request - The request to be sent - * @returns {Promise} A promise resolving with a response from Janus + * @param {Object} request - The request to be sent + * @returns {Promise} A promise resolving with a response from Janus */ async sendRequest(request) { /* Add connection properties */ @@ -342,7 +338,7 @@ class Connection extends EventEmitter { /** * Janus GET INFO API. * - * @returns {Promise} The Get Info response + * @returns {Promise} The Get Info response * * @example * @@ -368,7 +364,7 @@ class Connection extends EventEmitter { /** * (Admin API) List the sessions in a janus instance. * - * @returns {Promise} + * @returns {Promise} * * @example * @@ -389,7 +385,7 @@ class Connection extends EventEmitter { * (Admin API) List the handles in a session. * * @param {number} session_id - The identifier of the session - * @returns {Promise} + * @returns {Promise} * * @example * @@ -416,7 +412,7 @@ class Connection extends EventEmitter { * * @param {number} session_id - The session identifier * @param {number} handle_id - The handle identifier - * @returns {Promise} The Get Handle Info response + * @returns {Promise} The Get Handle Info response * * @example * @@ -452,7 +448,7 @@ class Connection extends EventEmitter { * @param {string} folder - The folder in which save the pcap * @param {string} filename - The pcap file name * @param {number} [truncate] - Number of bytes to truncate the pcap to - * @returns {Promise} The start pcap response + * @returns {Promise} The start pcap response */ async startPcap(session_id, handle_id, folder, filename, truncate) { Logger.info(`${LOG_NS} ${this.name} requesting pcap start for handle ${handle_id}`); @@ -490,7 +486,7 @@ class Connection extends EventEmitter { * * @param {number} session_id - The session identifier * @param {number} handle_id - The handle identifier - * @returns {Promsie} The stop pcap response + * @returns {Promise} The stop pcap response */ async stopPcap(session_id, handle_id) { Logger.info(`${LOG_NS} ${this.name} requesting pcap stop for handle ${handle_id}`); diff --git a/src/handle.js b/src/handle.js index 53e024d..0e293bb 100644 --- a/src/handle.js +++ b/src/handle.js @@ -3,7 +3,7 @@ /** * This module contains the Handle class definition. * @module handle - * @access private + * @private */ import { EventEmitter } from 'events'; @@ -13,6 +13,8 @@ const LOG_NS = '[handle.js]'; import { getNumericID } from './utils/utils.js'; import { JANUS, JANODE, isAckData, isResponseData, isErrorData } from './protocol.js'; +const PLUGIN_EVENT_SYM = Symbol('plugin_event'); + /** * Class representing a Janode handle.
* @@ -38,7 +40,7 @@ class Handle extends EventEmitter { * The transaction manager used by this handle. * * @private - * @type {TransactionManager} + * @type {module:tmanager~TransactionManager} */ this._tm = session._tm; // keep track of pending requests @@ -113,7 +115,7 @@ class Handle extends EventEmitter { * The handle has been detached. * * @event module:handle~Handle#event:HANDLE_DETACHED - * @type {object} + * @type {Object} * @property {number} id - The handle identifier */ this.emit(JANODE.EVENT.HANDLE_DETACHED, { id: this.id }); @@ -168,7 +170,7 @@ class Handle extends EventEmitter { * Generic Janus API events like `detached`, `hangup` etc. are handled here. * * @private - * @param {object} janus_message + * @param {Object} janus_message */ _handleMessage(janus_message) { const { transaction, janus } = janus_message; @@ -249,10 +251,6 @@ class Handle extends EventEmitter { const error = new Error('unmanaged event'); this.closeTransactionWithError(transaction, error); } - else { - /* If handleMessage has a truthy return close tx with success */ - this.closeTransactionWithSuccess(transaction, janus_message); - } break; } @@ -262,6 +260,18 @@ class Handle extends EventEmitter { break; } + /* ice-failed event: Janus ICE agent has detected a failure */ + case JANUS.EVENT.ICE_FAILED: { + /** + * The handle has detected an ICE failure. + * + * @event module:handle~Handle#event:HANDLE_ICE_FAILED + * @type {Object} + */ + this.emit(JANODE.EVENT.HANDLE_ICE_FAILED, janode_event_data); + break; + } + /* Hangup event: peer connection is down */ /* In this case the janus message has a reason field */ case JANUS.EVENT.HANGUP: { @@ -270,7 +280,7 @@ class Handle extends EventEmitter { * The handle WebRTC connection has been closed. * * @event module:handle~Handle#event:HANDLE_HANGUP - * @type {object} + * @type {Object} * @property {string} [reason] - The reason of the hangup (e.g. ICE failed) */ this.emit(JANODE.EVENT.HANDLE_HANGUP, janode_event_data); @@ -282,13 +292,19 @@ class Handle extends EventEmitter { case JANUS.EVENT.MEDIA: { if (typeof janus_message.type !== 'undefined') janode_event_data.type = janus_message.type; if (typeof janus_message.receiving !== 'undefined') janode_event_data.receiving = janus_message.receiving; + if (typeof janus_message.mid !== 'undefined') janode_event_data.mid = janus_message.mid; + if (typeof janus_message.substream !== 'undefined') janode_event_data.substream = janus_message.substream; + if (typeof janus_message.seconds !== 'undefined') janode_event_data.seconds = janus_message.seconds; /** * The handle received a media notification. * * @event module:handle~Handle#event:HANDLE_MEDIA - * @type {object} + * @type {Object} * @property {string} type - The kind of media (audio/video) * @property {boolean} receiving - True if Janus is receiving media + * @property {string} [mid] - The involved mid + * @property {number} [substream] - The involved simulcast substream + * @property {number} [seconds] - Time, in seconds, with no media */ this.emit(JANODE.EVENT.HANDLE_MEDIA, janode_event_data); break; @@ -300,6 +316,7 @@ class Handle extends EventEmitter { * The handle WebRTC connection is up. * * @event module:handle~Handle#event:HANDLE_WEBRTCUP + * @type {Object} */ this.emit(JANODE.EVENT.HANDLE_WEBRTCUP, janode_event_data); break; @@ -309,14 +326,18 @@ class Handle extends EventEmitter { /* In this case the janus message has "uplink" and "nacks" fields */ case JANUS.EVENT.SLOWLINK: { if (typeof janus_message.uplink !== 'undefined') janode_event_data.uplink = janus_message.uplink; - if (typeof janus_message.nacks !== 'undefined') janode_event_data.nacks = janus_message.nacks; + if (typeof janus_message.mid !== 'undefined') janode_event_data.mid = janus_message.mid; + if (typeof janus_message.media !== 'undefined') janode_event_data.media = janus_message.media; + if (typeof janus_message.lost !== 'undefined') janode_event_data.lost = janus_message.lost; /** * The handle has received a slowlink notification. * * @event module:handle~Handle#event:HANDLE_SLOWLINK - * @type {object} + * @type {Object} * @property {boolean} uplink - The direction of the slow link - * @property {number} nacks - Number of nacks in the last time slot + * @property {string} media - The media kind (audio/video) + * @property {string} [mid] - The involved stream mid + * @property {number} lost - Number of missing packets in the last time slot */ this.emit(JANODE.EVENT.HANDLE_SLOWLINK, janode_event_data); break; @@ -324,17 +345,6 @@ class Handle extends EventEmitter { /* Trickle from Janus */ case JANUS.EVENT.TRICKLE: { - /** - * The handle has received a trickle notification. - * - * @event module:handle~Handle#event:HANDLE_TRICKLE - * @type {object} - * @property {boolean} [completed] - If true, this notifies the end of triclking (the other fields of the event are missing in this case) - * @property {string} [sdpMid] - The mid the candidate refers to - * @property {number} [sdpMLineIndex] - The m-line the candidate refers to - * @property {string} [candidate] - The candidate string - */ - const { completed, sdpMid, sdpMLineIndex, candidate } = janus_message.candidate; if (!completed) { janode_event_data.sdpMid = sdpMid; @@ -345,6 +355,16 @@ class Handle extends EventEmitter { janode_event_data.completed = true; } + /** + * The handle has received a trickle notification. + * + * @event module:handle~Handle#event:HANDLE_TRICKLE + * @type {Object} + * @property {boolean} [completed] - If true, this notifies the end of triclking (the other fields of the event are missing in this case) + * @property {string} [sdpMid] - The mid the candidate refers to + * @property {number} [sdpMLineIndex] - The m-line the candidate refers to + * @property {string} [candidate] - The candidate string + */ this.emit(JANODE.EVENT.HANDLE_TRICKLE, janode_event_data); break; } @@ -358,20 +378,63 @@ class Handle extends EventEmitter { * Decorate request with handle id and transaction (if missing). * * @private - * @param {object} request + * @param {Object} request */ _decorateRequest(request) { request.transaction = request.transaction || getNumericID(); request.handle_id = request.handle_id || this.id; } + decorateRequest(request) { + this._decorateRequest(request); + } + + /** + * Helper method used by plugins to create a new plugin event and assign it to a janus message. + * + * @private + * @param {Object} janus_message + * @returns {Object} + */ + _newPluginEvent(janus_message) { + /* Prepare an object for the output Janode event */ + const janode_event = { + /* The name of the resolved event */ + event: null, + /* The event payload */ + data: {}, + }; + + /* Add JSEP data if available */ + if (janus_message.jsep) { + janode_event.data.jsep = janus_message.jsep; + if (typeof janus_message.jsep.e2ee === 'boolean') janode_event.data.e2ee = janus_message.jsep.e2ee; + } + + janus_message[PLUGIN_EVENT_SYM] = janode_event; + return janode_event; + } + + /** + * Helper method used by plugins to get an assigned plugin eventfrom a handled janus message. + * + * @private + * @param {Object} janus_message + * @returns {Object} + */ + _getPluginEvent(janus_message) { + return janus_message[PLUGIN_EVENT_SYM] || {}; + } + /** * Stub handleMessage (it is overriden by specific plugin handlers). * Implementations must return falsy values for unhandled events and truthy value * for handled events. * + * @param {Object} _janus_message + * @returns {Object} */ - handleMessage() { + handleMessage(_janus_message) { return null; } @@ -389,7 +452,7 @@ class Handle extends EventEmitter { * Helper to close a transaction with error. * * @property {string} id - The transaction id - * @property {string} error - The error message + * @property {Error} error - The error object * @returns {void} */ closeTransactionWithError(id, error) { @@ -401,7 +464,7 @@ class Handle extends EventEmitter { * Helper to close a transaction with success. * * @property {string} id - The transaction id - * @property {object} [data] - The callback success data + * @property {Object} [data] - The callback success data * @returns {void} */ closeTransactionWithSuccess(id, data) { @@ -413,10 +476,11 @@ class Handle extends EventEmitter { /** * Send a request from this handle. * - * @param {object} request - * @returns {Promise} A promsie resolving with the response to the request + * @param {Object} request + * @param {number} [timeout_ms=0] + * @returns {Promise} A promise resolving with the response to the request */ - async sendRequest(request) { + async sendRequest(request, timeout_ms = 0) { /* Input check */ if (typeof request !== 'object' || !request) { const error = new Error('request must be an object'); @@ -437,7 +501,7 @@ class Handle extends EventEmitter { return new Promise((resolve, reject) => { /* Create a new transaction if the transaction does not exist */ /* Use promise resolve and reject fn as callbacks for the transaction */ - this._tm.createTransaction(request.transaction, this, request.janus, resolve, reject); + this._tm.createTransaction(request.transaction, this, request.janus, resolve, reject, timeout_ms); /* Send this message through the parent janode session */ this.session.sendRequest(request).catch(error => { @@ -484,7 +548,7 @@ class Handle extends EventEmitter { /** * Close the peer connection associated to this handle. * - * @returns {Promise} + * @returns {Promise} */ async hangup() { const request = { @@ -551,9 +615,9 @@ class Handle extends EventEmitter { /** * Send a `message` to Janus from this handle, with given body and optional jsep. * - * @param {object} body - The body of the message + * @param {Object} body - The body of the message * @param {RTCSessionDescription} [jsep] - * @returns {Promise} A promise resolving with the response to the message + * @returns {Promise} A promise resolving with the response to the message * * @example * // This is a plugin that sends a message with a custom body diff --git a/src/janode.js b/src/janode.js index 297aab8..3461af7 100644 --- a/src/janode.js +++ b/src/janode.js @@ -17,7 +17,7 @@ const { EVENT } = JANODE_PROTO; /** * An object describing a janus server (e.g. url, secret). * - * @typedef {object} ServerObjectConf + * @typedef {Object} ServerObjectConf * @property {string} url - The URL to reach this server API * @property {string} apisecret - The API secret for this server * @property {string} [token] - The optional Janus API token @@ -26,22 +26,22 @@ const { EVENT } = JANODE_PROTO; /** * The configuration passed by the user. * - * @typedef {object} RawConfiguration + * @typedef {Object} RawConfiguration * @property {string} [server_key] - The key used to refer to this server in Janode.connect * @property {module:janode~ServerObjectConf[]|module:janode~ServerObjectConf} address - The server to connect to * @property {number} [retry_time_secs=10] - The seconds between any connection attempts * @property {number} [max_retries=5] - The maximum number of retries before issuing a connection error * @property {boolean} [is_admin=false] - True if the connection is dedicated to the Janus Admin API - * @property {object} [ws_options] - Specific WebSocket transport options + * @property {Object} [ws_options] - Specific WebSocket transport options */ /** * The plugin descriptor used when attaching a plugin from a session. * - * @typedef {object} PluginDescriptor + * @typedef {Object} PluginDescriptor * @property {string} id - The plugin id used when sending the attach request to Janus * @property {module:handle~Handle} [Handle] - The class implementing the handle - * @property {object} [EVENT] - The object with the list of events emitted by the plugin + * @property {Object} [EVENT] - The object containing the events emitted by the plugin */ /** @@ -106,7 +106,6 @@ const { EVENT } = JANODE_PROTO; */ const connect = (config = {}, key = null) => { Logger.info(`${LOG_NS} creating new connection`); - const janus_server_list = Array.isArray(config) ? config : [config]; let index = 0; if (typeof key === 'number') @@ -134,28 +133,14 @@ export default { /** * The Logger used in Janode. * - * @type {object} - * @property {function} debug - Print out a debug message - * @property {function} verbose - Print out a verbose message - * @property {function} info - Print out an info message - * @property {function} warn - Print out a warning message - * @property {function} error - Print out an error message - * @property {function} setLevel - Set logger level + * @type {module:logger~Logger} */ Logger, /** * Events emitted by Janode * - * @type {object} - * @property {string} CONNECTION_CLOSED - {@link module:connection~Connection#event:CONNECTION_CLOSED} - * @property {string} SESSION_DESTROYED - {@link module:session~Session#event:SESSION_DESTROYED} - * @property {string} HANDLE_DETACHED - {@link module:handle~Handle#event:HANDLE_DETACHED} - * @property {string} HANDLE_HANGUP - {@link module:handle~Handle#event:HANDLE_HANGUP} - * @property {string} HANDLE_MEDIA - {@link module:handle~Handle#event:HANDLE_MEDIA} - * @property {string} HANDLE_WEBRTCUP - {@link module:handle~Handle#event:HANDLE_WEBRTCUP} - * @property {string} HANDLE_SLOWLINK - {@link module:handle~Handle#event:HANDLE_SLOWLINK} - * @property {string} CONNECTION_ERROR - {@link module:connection~Connection#event:CONNECTION_ERROR} + * @type {module:protocol~JanodeCoreEvents} */ EVENT, }; diff --git a/src/plugins/audiobridge-plugin.js b/src/plugins/audiobridge-plugin.js index f796871..b4b9e2e 100644 --- a/src/plugins/audiobridge-plugin.js +++ b/src/plugins/audiobridge-plugin.js @@ -26,8 +26,17 @@ const REQUEST_ALLOW = 'allowed'; const REQUEST_RTP_FWD_START = 'rtp_forward'; const REQUEST_RTP_FWD_STOP = 'stop_rtp_forward'; const REQUEST_RTP_FWD_LIST = 'listforwarders'; -const REQUEST_MUTE_ROOM = 'mute_room'; +const REQUEST_SUSPEND_PARTICIPANT = 'suspend'; +const REQUEST_RESUME_PARTICIPANT = 'resume'; +const REQUEST_MUTE_PARTICIPANT = 'mute'; +const REQUEST_UNMUTE_PARTICIPANT = 'unmute'; +const REQUEST_MUTE_ROOM = 'unmute_room'; const REQUEST_UNMUTE_ROOM = 'unmute_room'; +const REQUEST_PLAY_FILE = 'play_file'; +const REQUEST_IS_PLAYING = 'is_playing'; +const REQUEST_STOP_FILE = 'stop_file'; +const REQUEST_STOP_ALL_FILES = 'stop_all_files'; +const REQUEST_LIST_ANNOUNCEMENTS = 'listannouncements'; /* These are the events/responses that the Janode plugin will manage */ /* Some of them will be exported in the plugin descriptor */ @@ -44,6 +53,10 @@ const PLUGIN_EVENT = { PEER_KICKED: 'audiobridge_peer_kicked', TALKING: 'audiobridge_talking', PEER_TALKING: 'audiobridge_peer_talking', + SUSPENDED: 'audiobridge_suspended', + PEER_SUSPENDED: 'audiobridge_peer_suspended', + RESUMED: 'audiobridge_resumed', + PEER_RESUMED: 'audiobridge_peer_resumed', EXISTS: 'audiobridge_exists', ROOMS_LIST: 'audiobridge_list', CREATED: 'audiobridge_created', @@ -53,6 +66,9 @@ const PLUGIN_EVENT = { FWD_LIST: 'audiobridge_rtp_list', ALLOWED: 'audiobridge_allowed', ROOM_MUTED: 'audiobridge_room_muted', + ANNOUNCEMENTS_LIST: 'audiobridge_announcements_list', + ANNOUNCEMENT_STARTED: 'audiobridge_announcement_started', + ANNOUNCEMENT_STOPPED: 'audiobridge_announcement_stopped', SUCCESS: 'audiobridge_success', ERROR: 'audiobridge_error', }; @@ -65,6 +81,7 @@ const PLUGIN_EVENT = { * Moreover it defines many methods to support AudioBridge operations. * * @hideconstructor + * @extends module:handle~Handle */ class AudioBridgeHandle extends Handle { /** @@ -95,11 +112,11 @@ class AudioBridgeHandle extends Handle { * The custom "handleMessage" needed for handling AudioBridge messages. * * @private - * @param {object} janus_message - * @returns {object} A falsy value for unhandled events, a truthy value for handled events + * @param {Object} janus_message + * @returns {Object} A falsy value for unhandled events, a truthy value for handled events */ handleMessage(janus_message) { - const { plugindata, jsep, transaction } = janus_message; + const { plugindata, transaction } = janus_message; if (plugindata && plugindata.data && plugindata.data.audiobridge) { /** * @type {AudioBridgeData} @@ -108,15 +125,8 @@ class AudioBridgeHandle extends Handle { const { audiobridge, error, error_code, room } = message_data; /* Prepare an object for the output Janode event */ - const janode_event = { - /* The name of the resolved event */ - event: null, - /* The event payload */ - data: {}, - }; - - /* Add JSEP data if available */ - if (jsep) janode_event.data.jsep = jsep; + const janode_event = this._newPluginEvent(janus_message); + /* Add room information if available */ if (room) janode_event.data.room = room; @@ -124,9 +134,6 @@ class AudioBridgeHandle extends Handle { /* That means that a transaction has already been closed or this is an async event */ const emit = (this.ownsTransaction(transaction) === false); - /* Use the "janode" property to store the output event */ - janus_message._janode = janode_event; - switch (audiobridge) { /* success response */ @@ -167,6 +174,15 @@ class AudioBridgeHandle extends Handle { if (typeof message_data.allowed !== 'undefined') { janode_event.data.list = message_data.allowed; } + if (typeof message_data.file_id !== 'undefined') { + janode_event.data.file_id = message_data.file_id; + } + if (typeof message_data.file_id_list !== 'undefined') { + janode_event.data.file_id_list = message_data.file_id_list; + } + if (typeof message_data.playing !== 'undefined') { + janode_event.data.playing = message_data.playing; + } /* In this case the "event" field of the Janode event is "success" */ janode_event.event = PLUGIN_EVENT.SUCCESS; break; @@ -187,7 +203,7 @@ class AudioBridgeHandle extends Handle { janode_event.data.rtp = message_data.rtp; } /* Add participants data */ - janode_event.data.participants = message_data.participants.map(({ id, display, muted, setup, talking }) => { + janode_event.data.participants = message_data.participants.map(({ id, display, muted, setup, talking, suspended }) => { const peer = { feed: id, display, @@ -195,6 +211,7 @@ class AudioBridgeHandle extends Handle { setup, }; if (typeof talking !== 'undefined') peer.talking = talking; + if (typeof suspended !== 'undefined') peer.suspended = suspended; return peer; }); janode_event.event = PLUGIN_EVENT.JOINED; @@ -205,13 +222,14 @@ class AudioBridgeHandle extends Handle { if (typeof message_data.participants[0].display === 'string') janode_event.data.display = message_data.participants[0].display; if (typeof message_data.participants[0].muted !== 'undefined') janode_event.data.muted = message_data.participants[0].muted; if (typeof message_data.participants[0].setup !== 'undefined') janode_event.data.setup = message_data.participants[0].setup; + if (typeof message_data.participants[0].suspended !== 'undefined') janode_event.data.suspended = message_data.participants[0].suspended; janode_event.event = PLUGIN_EVENT.PEER_JOINED; } break; /* Participants list */ case 'participants': - janode_event.data.participants = message_data.participants.map(({ id, display, muted, setup, talking }) => { + janode_event.data.participants = message_data.participants.map(({ id, display, muted, setup, talking, suspended }) => { const peer = { feed: id, display, @@ -219,6 +237,7 @@ class AudioBridgeHandle extends Handle { setup, }; if (typeof talking !== 'undefined') peer.talking = talking; + if (typeof suspended !== 'undefined') peer.suspended = suspended; return peer; }); janode_event.event = PLUGIN_EVENT.PARTICIPANTS_LIST; @@ -264,6 +283,12 @@ class AudioBridgeHandle extends Handle { janode_event.event = PLUGIN_EVENT.FWD_LIST; break; + /* Announcements list */ + case 'announcements': + janode_event.data.announcements = message_data.announcements; + janode_event.event = PLUGIN_EVENT.ANNOUNCEMENTS_LIST; + break; + /* Talking events */ case 'talking': case 'stopped-talking': @@ -272,6 +297,13 @@ class AudioBridgeHandle extends Handle { janode_event.event = message_data.id !== this.feed ? PLUGIN_EVENT.PEER_TALKING : PLUGIN_EVENT.TALKING; break; + /* Announcement events */ + case 'announcement-started': + case 'announcement-stopped': + janode_event.data.file_id = message_data.file_id; + janode_event.event = audiobridge === 'announcement-started' ? PLUGIN_EVENT.ANNOUNCEMENT_STARTED : PLUGIN_EVENT.ANNOUNCEMENT_STOPPED; + break; + /* Generic event (e.g. errors) */ case 'event': /* AudioBridge error */ @@ -289,13 +321,40 @@ class AudioBridgeHandle extends Handle { } break; } - /* Configuration events for other participants */ - if (typeof message_data.participants !== 'undefined' && message_data.participants.length == 1) { - janode_event.data.feed = message_data.participants[0].id; - if (typeof message_data.participants[0].display === 'string') janode_event.data.display = message_data.participants[0].display; - if (typeof message_data.participants[0].muted !== 'undefined') janode_event.data.muted = message_data.participants[0].muted; - if (typeof message_data.participants[0].setup !== 'undefined') janode_event.data.setup = message_data.participants[0].setup; - janode_event.event = PLUGIN_EVENT.PEER_CONFIGURED; + /* This handle or another participant has been resumed */ + if (typeof message_data.resumed != 'undefined') { + janode_event.data.feed = message_data.resumed; + if (message_data.participants) { + /* Add participants data */ + janode_event.data.participants = message_data.participants.map(({ id, display, muted, setup, talking, suspended }) => { + const peer = { + feed: id, + display, + muted, + setup, + }; + if (typeof talking !== 'undefined') peer.talking = talking; + if (typeof suspended !== 'undefined') peer.suspended = suspended; + return peer; + }); + } + if (this.feed === janode_event.data.feed) { + janode_event.event = PLUGIN_EVENT.RESUMED; + } + else { + janode_event.event = PLUGIN_EVENT.PEER_RESUMED; + } + break; + } + /* This handle or another participant has been suspended */ + if (typeof message_data.suspended != 'undefined') { + janode_event.data.feed = message_data.suspended; + if (this.feed === janode_event.data.feed) { + janode_event.event = PLUGIN_EVENT.SUSPENDED; + } + else { + janode_event.event = PLUGIN_EVENT.PEER_SUSPENDED; + } break; } /* Peer leaving confirmation */ @@ -324,6 +383,17 @@ class AudioBridgeHandle extends Handle { } break; } + /* Configuration events for other participants */ + if (typeof message_data.participants !== 'undefined' && message_data.participants.length == 1) { + janode_event.data.feed = message_data.participants[0].id; + if (typeof message_data.participants[0].display === 'string') janode_event.data.display = message_data.participants[0].display; + if (typeof message_data.participants[0].muted !== 'undefined') janode_event.data.muted = message_data.participants[0].muted; + if (typeof message_data.participants[0].setup !== 'undefined') janode_event.data.setup = message_data.participants[0].setup; + if (typeof message_data.participants[0].suspended !== 'undefined') janode_event.data.suspended = message_data.participants[0].suspended; + /* when using "mute"/"unmute" management requests, janus will notify "configured" to all participants, including the involved one */ + janode_event.event = janode_event.data.feed !== this.feed ? PLUGIN_EVENT.PEER_CONFIGURED : PLUGIN_EVENT.CONFIGURED; + break; + } } /* The event has been handled */ @@ -349,7 +419,7 @@ class AudioBridgeHandle extends Handle { /** * Join an audiobridge room. * - * @param {object} params + * @param {Object} params * @param {number|string} params.room - The room to join * @param {number|string} [params.feed] - The feed identifier for the participant, picked by Janus if omitted * @param {string} [params.display] - The display name to use @@ -360,12 +430,15 @@ class AudioBridgeHandle extends Handle { * @param {number} [params.volume] - The percent volume * @param {boolean} [params.record] - True to enable recording * @param {string} [params.filename] - The recording filename + * @param {boolean} [params.suspended] - True to join in suspended status + * @param {boolean} [params.pause_events] - Wheter to pause notification events for suspended participants * @param {module:audiobridge-plugin~RtpParticipant} [params.rtp_participant] - Set a descriptor object if you need a RTP participant * @param {string} [params.group] - The group to assign to this participant * @param {boolean} [params.generate_offer] - True to get Janus to send the SDP offer. + * @param {string} [params.codec] - The codec to be used among opus, pcma or pcmu (Janus will default to opus) * @returns {Promise} */ - async join({ room, feed, display, muted, pin, token, quality, volume, record, filename, rtp_participant, group, generate_offer }) { + async join({ room, feed, display, muted, pin, token, quality, volume, record, filename, suspended, pause_events, rtp_participant, group, generate_offer, codec }) { const body = { request: REQUEST_JOIN, room, @@ -379,12 +452,15 @@ class AudioBridgeHandle extends Handle { if (typeof volume === 'number') body.volume = volume; if (typeof record === 'boolean') body.record = record; if (typeof filename === 'string') body.filename = filename; + if (typeof suspended === 'boolean') body.suspended = suspended; + if (typeof pause_events === 'boolean') body.pause_events = pause_events; if (typeof rtp_participant === 'object' && rtp_participant) body.rtp = rtp_participant; if (typeof group === 'string') body.group = group; if (typeof generate_offer === 'boolean') body.generate_offer = generate_offer; + if (typeof codec === 'string') body.codec = codec; const response = await this.message(body); - const { event, data: evtdata } = response._janode || {}; + const { event, data: evtdata } = this._getPluginEvent(response); if (event === PLUGIN_EVENT.JOINED) return evtdata; const error = new Error(`unexpected response to ${body.request} request`); @@ -394,7 +470,7 @@ class AudioBridgeHandle extends Handle { /** * Configure an audiobridge handle. * - * @param {object} params + * @param {Object} params * @param {string} [params.display] - The display name to use * @param {boolean} [params.muted] - Set muted status * @param {number} [params.quality] - Set opus quality @@ -405,10 +481,11 @@ class AudioBridgeHandle extends Handle { * @param {number} [params.expected_loss] - Set a new expected_loss value for this participant (overrides room default) * @param {number} [params.prebuffer] - Set a new prebuffer value (overrides room default) * @param {string} [params.group] - Set the group that the participant belongs to - * @param {RTCSessionDescription} [params.jsep=null] - JSEP offer + * @param {module:audiobridge-plugin~RtpParticipant} [params.rtp_participant] - Set a descriptor object if you need a RTP participant + * @param {RTCSessionDescription} [params.jsep=null] - JSEP offer/answer to be sent to Janus * @returns {Promise} */ - async configure({ display, muted, quality, bitrate, volume, record, filename, expected_loss, prebuffer, group, jsep = null }) { + async configure({ display, muted, quality, bitrate, volume, record, filename, expected_loss, prebuffer, group, rtp_participant, jsep = null }) { const body = { request: REQUEST_CONFIGURE, }; @@ -422,9 +499,10 @@ class AudioBridgeHandle extends Handle { if (typeof expected_loss === 'number') body.expected_loss = expected_loss; if (typeof prebuffer === 'number') body.prebuffer = prebuffer; if (typeof group === 'string') body.group = group; + if (typeof rtp_participant === 'object' && rtp_participant) body.rtp = rtp_participant; const response = await this.message(body, jsep); - const { event, data: evtdata } = response._janode || {}; + const { event, data: evtdata } = this._getPluginEvent(response); if (event === PLUGIN_EVENT.CONFIGURED) { /* Janus does not reply with configured data, so we need to re-use the requested configuration */ /* Use feed and room from handle status */ @@ -438,6 +516,7 @@ class AudioBridgeHandle extends Handle { if (typeof body.filename !== 'undefined') evtdata.filename = body.filename; if (typeof body.prebuffer !== 'undefined') evtdata.prebuffer = body.prebuffer; if (typeof body.group !== 'undefined') evtdata.group = body.group; + if (typeof body.rtp !== 'undefined') evtdata.rtp_participant = body.rtp; return evtdata; } const error = new Error(`unexpected response to ${body.request} request`); @@ -456,7 +535,7 @@ class AudioBridgeHandle extends Handle { }; const response = await this.message(body); - const { event, data: evtdata } = response._janode || {}; + const { event, data: evtdata } = this._getPluginEvent(response); if (event === PLUGIN_EVENT.AUDIO_HANGINGUP) return evtdata; const error = new Error(`unexpected response to ${body.request} request`); @@ -474,7 +553,7 @@ class AudioBridgeHandle extends Handle { }; const response = await this.message(body); - const { event, data: evtdata } = response._janode || {}; + const { event, data: evtdata } = this._getPluginEvent(response); if (event === PLUGIN_EVENT.LEAVING) return evtdata; const error = new Error(`unexpected response to ${body.request} request`); @@ -490,7 +569,7 @@ class AudioBridgeHandle extends Handle { /** * List participants inside a room. * - * @param {object} params + * @param {Object} params * @param {number|string} params.room - The room where to execute the list * @param {string} [params.secret] - The optional secret needed for managing the room * @returns {Promise} @@ -503,7 +582,7 @@ class AudioBridgeHandle extends Handle { if (typeof secret === 'string') body.secret = secret; const response = await this.message(body); - const { event, data: evtdata } = response._janode || {}; + const { event, data: evtdata } = this._getPluginEvent(response); if (event === PLUGIN_EVENT.PARTICIPANTS_LIST) return evtdata; const error = new Error(`unexpected response to ${body.request} request`); @@ -513,7 +592,7 @@ class AudioBridgeHandle extends Handle { /** * Kick an user out from a room. * - * @param {object} params + * @param {Object} params * @param {number|string} params.room - The involved room * @param {number|string} params.feed - The feed to kick out * @param {string} [params.secret] - The optional secret needed for managing the room @@ -528,7 +607,7 @@ class AudioBridgeHandle extends Handle { if (typeof secret === 'string') body.secret = secret; const response = await this.message(body); - const { event, data: evtdata } = response._janode || {}; + const { event, data: evtdata } = this._getPluginEvent(response); if (event === PLUGIN_EVENT.SUCCESS) { /* Add data missing from Janus response */ evtdata.room = body.room; @@ -542,7 +621,7 @@ class AudioBridgeHandle extends Handle { /** * Check if a room exists. * - * @param {object} params + * @param {Object} params * @param {number|string} params.room - The involved room * @returns {Promise} */ @@ -553,7 +632,7 @@ class AudioBridgeHandle extends Handle { }; const response = await this.message(body); - const { event, data: evtdata } = response._janode || {}; + const { event, data: evtdata } = this._getPluginEvent(response); if (event === PLUGIN_EVENT.EXISTS) return evtdata; const error = new Error(`unexpected response to ${body.request} request`); @@ -571,7 +650,7 @@ class AudioBridgeHandle extends Handle { }; const response = await this.message(body); - const { event, data: evtdata } = response._janode || {}; + const { event, data: evtdata } = this._getPluginEvent(response); if (event === PLUGIN_EVENT.ROOMS_LIST) return evtdata; const error = new Error(`unexpected response to ${body.request} request`); @@ -581,7 +660,7 @@ class AudioBridgeHandle extends Handle { /** * Create an audiobridge room. * - * @param {object} params + * @param {Object} params * @param {number|string} params.room - The room identifier * @param {string} [params.description] - A room description * @param {boolean} [params.permanent] - Set to true to persist the room in the Janus config file @@ -590,6 +669,7 @@ class AudioBridgeHandle extends Handle { * @param {boolean} [params.is_private] - Set room as private (hidden in list) * @param {string} [params.secret] - The secret to be used when managing the room * @param {string} [params.pin] - The ping needed for joining the room + * @param {string} [params.admin_key] - The admin key needed for invoking the API * @param {boolean} [params.record] - True to record the mixed audio * @param {string} [params.filename] - The recording filename * @param {string} [params.rec_dir] - The optional recording folder @@ -600,10 +680,11 @@ class AudioBridgeHandle extends Handle { * @param {number} [params.prebuffer] - The prebuffer to use for every participant * @param {boolean} [params.allow_rtp] - Allow plain RTP participants * @param {string[]} [params.groups] - The available groups in the room + * @param {boolean} [params.denoise] - Enable denoising with rnnoise for all participants * @returns {Promise} */ - async create({ room, description, permanent, sampling_rate, bitrate, is_private, secret, pin, record, filename, rec_dir, - talking_events, talking_level_threshold, talking_packets_threshold, expected_loss, prebuffer, allow_rtp, groups }) { + async create({ room, description, permanent, sampling_rate, bitrate, is_private, secret, pin, admin_key, record, filename, rec_dir, + talking_events, talking_level_threshold, talking_packets_threshold, expected_loss, prebuffer, allow_rtp, groups, denoise }) { const body = { request: REQUEST_CREATE, room, @@ -615,6 +696,7 @@ class AudioBridgeHandle extends Handle { if (typeof is_private === 'boolean') body.is_private = is_private; if (typeof secret === 'string') body.secret = secret; if (typeof pin === 'string') body.pin = pin; + if (typeof admin_key === 'string') body.admin_key = admin_key; if (typeof record === 'boolean') body.record = record; if (typeof filename === 'string') body.record_file = filename; if (typeof rec_dir === 'string') body.record_dir = rec_dir; @@ -625,9 +707,10 @@ class AudioBridgeHandle extends Handle { if (typeof prebuffer === 'number') body.default_prebuffering = prebuffer; if (typeof allow_rtp === 'boolean') body.allow_rtp_participants = allow_rtp; if (Array.isArray(groups)) body.groups = groups; + if (typeof denoise === 'boolean') body.denoise = denoise; const response = await this.message(body); - const { event, data: evtdata } = response._janode || {}; + const { event, data: evtdata } = this._getPluginEvent(response); if (event === PLUGIN_EVENT.CREATED) return evtdata; const error = new Error(`unexpected response to ${body.request} request`); @@ -637,7 +720,7 @@ class AudioBridgeHandle extends Handle { /** * Destroy an audiobridge room. * - * @param {object} params + * @param {Object} params * @param {number|string} params.room - The room to destroy * @param {boolean} [params.permanent] - Set to true to remove the room from the Janus config file * @param {string} [params.secret] - The optional secret needed to manage the room @@ -652,7 +735,7 @@ class AudioBridgeHandle extends Handle { if (typeof secret === 'string') body.secret = secret; const response = await this.message(body); - const { event, data: evtdata } = response._janode || {}; + const { event, data: evtdata } = this._getPluginEvent(response); if (event === PLUGIN_EVENT.DESTROYED) return evtdata; const error = new Error(`unexpected response to ${body.request} request`); @@ -662,7 +745,7 @@ class AudioBridgeHandle extends Handle { /** * Enable/disable mixed audio recording. * - * @param {object} params + * @param {Object} params * @param {number|string} params.room - The room identifier * @param {boolean} params.record - Enable/disable recording * @param {string} [params.secret] - The secret to be used when managing the room @@ -681,7 +764,7 @@ class AudioBridgeHandle extends Handle { if (typeof secret === 'string') body.secret = secret; const response = await this.message(body); - const { event, data: evtdata } = response._janode || {}; + const { event, data: evtdata } = this._getPluginEvent(response); if (event === PLUGIN_EVENT.RECORDING) { evtdata.room = body.room; return evtdata; @@ -693,7 +776,7 @@ class AudioBridgeHandle extends Handle { /** * Edit an audiobridge token list. * - * @param {object} params + * @param {Object} params * @param {number|string} params.room - The involved room * @param {"enable"|"disable"|"add"|"remove"} params.action - The action to perform * @param {string[]} params.list - The list of tokens to add/remove @@ -710,7 +793,7 @@ class AudioBridgeHandle extends Handle { if (typeof secret === 'string') body.secret = secret; const response = await this.message(body); - const { event, data: evtdata } = response._janode || {}; + const { event, data: evtdata } = this._getPluginEvent(response); if (event === PLUGIN_EVENT.SUCCESS) return evtdata; const error = new Error(`unexpected response to ${body.request} request`); @@ -720,34 +803,38 @@ class AudioBridgeHandle extends Handle { /** * Start a RTP forwarder. * - * @param {object} params + * @param {Object} params * @param {number|string} params.room - The involved room * @param {boolean} [params.always] - Whether silence should be forwarded when the room is empty * @param {string} params.host - The host to forward to + * @param {string} [params.host_family] - ipv4|ipv6; by default, first family returned by DNS request * @param {number} params.audio_port - The port to forward to * @param {number} [params.ssrc] - The SSRC to use to use when forwarding * @param {number} [params.ptype] - The payload type to use to use when forwarding * @param {string} [params.codec] - The codec to use in the forwarder * @param {string} [params.group] - The group to forward * @param {string} [params.secret] - The optional secret needed to manage the room + * @param {string} [params.admin_key] - The admin key needed for invoking the API * @returns {Promise} */ - async startForward({ room, always, host, audio_port, ssrc, ptype, codec, group, secret }) { + async startForward({ room, always, host, host_family, audio_port, ssrc, ptype, codec, group, secret, admin_key }) { const body = { request: REQUEST_RTP_FWD_START, room, }; if (typeof always === 'boolean') body.always_on = always; if (typeof host === 'string') body.host = host; + if (typeof host_family === 'string') body.host_family = host_family; if (typeof audio_port === 'number') body.port = audio_port; if (typeof ssrc === 'number') body.ssrc = ssrc; if (typeof ptype === 'number') body.ptype = ptype; if (typeof codec === 'string') body.codec = codec; if (typeof group === 'string') body.group = group; if (typeof secret === 'string') body.secret = secret; + if (typeof admin_key === 'string') body.admin_key = admin_key; const response = await this.message(body); - const { event, data: evtdata } = response._janode || {}; + const { event, data: evtdata } = this._getPluginEvent(response); if (event === PLUGIN_EVENT.RTP_FWD) return evtdata; const error = new Error(`unexpected response to ${body.request} request`); @@ -757,22 +844,24 @@ class AudioBridgeHandle extends Handle { /** * Stop a RTP forwarder. * - * @param {object} params + * @param {Object} params * @param {number|string} params.room - The involved room * @param {number} params.stream - The forwarder identifier to stop * @param {string} [params.secret] - The optional secret needed to manage the room + * @param {string} [params.admin_key] - The admin key needed for invoking the API * @returns {Promise} */ - async stopForward({ room, stream, secret }) { + async stopForward({ room, stream, secret, admin_key }) { const body = { request: REQUEST_RTP_FWD_STOP, room, stream_id: stream, }; if (typeof secret === 'string') body.secret = secret; + if (typeof admin_key === 'string') body.admin_key = admin_key; const response = await this.message(body); - const { event, data: evtdata } = response._janode || {}; + const { event, data: evtdata } = this._getPluginEvent(response); if (event === PLUGIN_EVENT.RTP_FWD) return evtdata; const error = new Error(`unexpected response to ${body.request} request`); @@ -782,30 +871,90 @@ class AudioBridgeHandle extends Handle { /** * List active forwarders. * - * @param {object} params + * @param {Object} params * @param {number|string} params.room - The involved room * @param {string} [params.secret] - The optional secret needed to manage the room + * @param {string} [params.admin_key] - The admin key needed for invoking the API * @returns {Promise} */ - async listForward({ room, secret }) { + async listForward({ room, secret, admin_key }) { const body = { request: REQUEST_RTP_FWD_LIST, room, }; if (typeof secret === 'string') body.secret = secret; + if (typeof admin_key === 'string') body.admin_key = admin_key; const response = await this.message(body); - const { event, data: evtdata } = response._janode || {}; + const { event, data: evtdata } = this._getPluginEvent(response); if (event === PLUGIN_EVENT.FWD_LIST) return evtdata; const error = new Error(`unexpected response to ${body.request} request`); throw (error); } + /** + * Mute an user in the audiobridge. + * + * @param {Object} params + * @param {number|string} params.room - The involved room + * @param {number|string} params.feed - The feed to mute + * @param {string} [params.secret] - The optional secret needed for managing the room + * @returns {Promise} + */ + async mute({ room, feed, secret }) { + const body = { + request: REQUEST_MUTE_PARTICIPANT, + room, + id: feed, + }; + if (typeof secret === 'string') body.secret = secret; + + const response = await this.message(body); + const { event, data: evtdata } = this._getPluginEvent(response); + if (event === PLUGIN_EVENT.SUCCESS) { + /* Add data missing from Janus response */ + evtdata.room = body.room; + evtdata.feed = body.id; + return evtdata; + } + const error = new Error(`unexpected response to ${body.request} request`); + throw (error); + } + + /** + * Unmute an user in the audiobridge. + * + * @param {Object} params + * @param {number|string} params.room - The involved room + * @param {number|string} params.feed - The feed to unmute + * @param {string} [params.secret] - The optional secret needed for managing the room + * @returns {Promise} + */ + async unmute({ room, feed, secret }) { + const body = { + request: REQUEST_UNMUTE_PARTICIPANT, + room, + id: feed, + }; + if (typeof secret === 'string') body.secret = secret; + + const response = await this.message(body); + const { event, data: evtdata } = this._getPluginEvent(response); + if (event === PLUGIN_EVENT.SUCCESS) { + /* Add data missing from Janus response */ + evtdata.room = body.room; + evtdata.feed = body.id; + return evtdata; + } + const error = new Error(`unexpected response to ${body.request} request`); + throw (error); + } + /** * Mute the given room for every participant. * - * @param {object} params + * @param {Object} params * @param {number|string} params.room - The involved room * @param {string} [params.secret] - The optional secret needed to manage the room * @returns {Promise} @@ -818,7 +967,7 @@ class AudioBridgeHandle extends Handle { if (typeof secret === 'string') body.secret = secret; const response = await this.message(body); - const { event, data: evtdata } = response._janode || {}; + const { event, data: evtdata } = this._getPluginEvent(response); if (event === PLUGIN_EVENT.SUCCESS) { evtdata.room = body.room; return evtdata; @@ -830,7 +979,7 @@ class AudioBridgeHandle extends Handle { /** * Unmute the given room for every participant. * - * @param {object} params + * @param {Object} params * @param {number|string} params.room - The involved room * @param {string} [params.secret] - The optional secret needed to manage the room * @returns {Promise} @@ -843,7 +992,102 @@ class AudioBridgeHandle extends Handle { if (typeof secret === 'string') body.secret = secret; const response = await this.message(body); - const { event, data: evtdata } = response._janode || {}; + const { event, data: evtdata } = this._getPluginEvent(response); + if (event === PLUGIN_EVENT.SUCCESS) { + evtdata.room = body.room; + return evtdata; + } + const error = new Error(`unexpected response to ${body.request} request`); + throw (error); + } + + /** + * Suspend an audiobridge participant. + * + * @param {Object} params + * @param {number|string} params.room - The involved room + * @param {number|string} params.feed - The feed id to be suspended + * @param {boolean} [params.stop_record] - Whether the recording of this participant should be stopped too + * @param {boolean} [params.pause_events] - Wheter to pause notification events for suspended participants + * @param {string} [params.secret] - The optional secret needed to manage the room + * @returns {Promise} + */ + async suspend({ room, feed, stop_record, pause_events, secret }) { + const body = { + request: REQUEST_SUSPEND_PARTICIPANT, + room, + id: feed + }; + if (typeof stop_record === 'boolean') body.stop_record = stop_record; + if (typeof pause_events === 'boolean') body.pause_events = pause_events; + if (typeof secret === 'string') body.secret = secret; + + const response = await this.message(body); + const { event, data: evtdata } = this._getPluginEvent(response); + if (event === PLUGIN_EVENT.SUCCESS) { + evtdata.feed = feed; + return evtdata; + } + const error = new Error(`unexpected response to ${body.request} request`); + throw (error); + } + + /** + * Resume an audiobridge participant after a suspend. + * + * @param {Object} params + * @param {number|string} params.room - The involved room + * @param {number|string} params.feed - The feed id to be resumed + * @param {boolean} [params.record] - Whether to start recording this resumed feed + * @param {string} [params.filename] - The recording filename + * @param {string} [params.secret] - The optional secret needed to manage the room + * @returns {Promise} + */ + async resume({ room, feed, record, filename, secret }) { + const body = { + request: REQUEST_RESUME_PARTICIPANT, + room, + id: feed + }; + if (typeof record === 'boolean') body.record = record; + if (typeof filename === 'string') body.filename = filename; + if (typeof secret === 'string') body.secret = secret; + + const response = await this.message(body); + const { event, data: evtdata } = this._getPluginEvent(response); + if (event === PLUGIN_EVENT.SUCCESS) { + evtdata.feed = feed; + return evtdata; + } + const error = new Error(`unexpected response to ${body.request} request`); + throw (error); + } + + /** + * Play a file inside a room. + * + * @param {Object} params + * @param {number|string} params.room - The involved room + * @param {string} [params.secret] - The optional secret needed to manage the room + * @param {string} [params.group] - The optional group to play in + * @param {string} [params.file_id] - The optional ID of the announcement + * @param {string} params.filename - The path to the Opus file to play + * @param {boolean} [params.loop] - Whether the file should be played in a loop + * @returns {Promise} + */ + async playFile({ room, secret, group, file_id, filename, loop }) { + const body = { + request: REQUEST_PLAY_FILE, + room, + }; + if (typeof secret === 'string') body.secret = secret; + if (typeof group === 'string') body.group = group; + if (typeof file_id === 'string') body.file_id = file_id; + if (typeof filename === 'string') body.filename = filename; + if (typeof loop === 'boolean') body.loop = loop; + + const response = await this.message(body); + const { event, data: evtdata } = this._getPluginEvent(response); if (event === PLUGIN_EVENT.SUCCESS) { evtdata.room = body.room; return evtdata; @@ -852,6 +1096,108 @@ class AudioBridgeHandle extends Handle { throw (error); } + /** + * Check whether a file is playing inside a room. + * + * @param {Object} params + * @param {number|string} params.room - The involved room + * @param {string} [params.secret] - The optional secret needed to manage the room + * @param {string} params.file_id - The involved announcement ID + * @returns {Promise} + */ + async isPlaying({ room, secret, file_id }) { + const body = { + request: REQUEST_IS_PLAYING, + room, + }; + if (typeof secret === 'string') body.secret = secret; + if (typeof file_id === 'string') body.file_id = file_id; + + const response = await this.message(body); + const { event, data: evtdata } = this._getPluginEvent(response); + if (event === PLUGIN_EVENT.SUCCESS) { + evtdata.room = body.room; + return evtdata; + } + const error = new Error(`unexpected response to ${body.request} request`); + throw (error); + } + + /** + * Stop playing a file inside a room. + * + * @param {Object} params + * @param {number|string} params.room - The involved room + * @param {string} [params.secret] - The optional secret needed to manage the room + * @param {string} params.file_id - The involved announcement ID + * @returns {Promise} + */ + async stopFile({ room, secret, file_id }) { + const body = { + request: REQUEST_STOP_FILE, + room, + }; + if (typeof secret === 'string') body.secret = secret; + if (typeof file_id === 'string') body.file_id = file_id; + + const response = await this.message(body); + const { event, data: evtdata } = this._getPluginEvent(response); + if (event === PLUGIN_EVENT.SUCCESS) { + evtdata.room = body.room; + return evtdata; + } + const error = new Error(`unexpected response to ${body.request} request`); + throw (error); + } + + /** + * Stop playing all files inside a room. + * + * @param {Object} params + * @param {number|string} params.room - The involved room + * @param {string} [params.secret] - The optional secret needed to manage the room + * @returns {Promise} + */ + async stopAllFiles({ room, secret }) { + const body = { + request: REQUEST_STOP_ALL_FILES, + room, + }; + if (typeof secret === 'string') body.secret = secret; + + const response = await this.message(body); + const { event, data: evtdata } = this._getPluginEvent(response); + if (event === PLUGIN_EVENT.SUCCESS) { + evtdata.room = body.room; + return evtdata; + } + const error = new Error(`unexpected response to ${body.request} request`); + throw (error); + } + + /** + * List announcements inside a room. + * + * @param {Object} params + * @param {number|string} params.room - The room where to execute the list + * @param {string} [params.secret] - The optional secret needed for managing the room + * @returns {Promise} + */ + async listAnnouncements({ room, secret }) { + const body = { + request: REQUEST_LIST_ANNOUNCEMENTS, + room, + }; + if (typeof secret === 'string') body.secret = secret; + + const response = await this.message(body); + const { event, data: evtdata } = this._getPluginEvent(response); + if (event === PLUGIN_EVENT.ANNOUNCEMENTS_LIST) + return evtdata; + const error = new Error(`unexpected response to ${body.request} request`); + throw (error); + } + } /** @@ -859,11 +1205,11 @@ class AudioBridgeHandle extends Handle { * {@link https://janus.conf.meetecho.com/docs/audiobridge.html} * * @private - * @typedef {object} AudioBridgeData + * @typedef {Object} AudioBridgeData */ /** - * @typedef {object} RtpParticipant + * @typedef {Object} RtpParticipant * @property {string} ip - IP address you want media to be sent to * @property {number} port - The port you want media to be sent to * @property {number} payload_type - The payload type to use for RTP packets @@ -872,7 +1218,7 @@ class AudioBridgeHandle extends Handle { /** * The response event to a join request. * - * @typedef {object} AUDIOBRIDGE_EVENT_JOINED + * @typedef {Object} AUDIOBRIDGE_EVENT_JOINED * @property {number|string} room - The involved room * @property {number|string} feed - The feed identifier * @property {module:audiobridge-plugin~RtpParticipant} [rtp_participant] - The descriptor in case this is a plain RTP participant @@ -886,7 +1232,7 @@ class AudioBridgeHandle extends Handle { /** * The response event for configure request. * - * @typedef {object} AUDIOBRIDGE_EVENT_CONFIGURED + * @typedef {Object} AUDIOBRIDGE_EVENT_CONFIGURED * @property {number|string} room - The involved room * @property {number|string} feed - The feed identifier * @property {string} [display] - The display name, if available @@ -903,7 +1249,7 @@ class AudioBridgeHandle extends Handle { /** * The response event for audiobridge hangup request. * - * @typedef {object} AUDIOBRIDGE_EVENT_AUDIO_HANGINGUP + * @typedef {Object} AUDIOBRIDGE_EVENT_AUDIO_HANGINGUP * @property {number|string} room - The involved room * @property {number|string} feed - The feed that is being hung up */ @@ -911,7 +1257,7 @@ class AudioBridgeHandle extends Handle { /** * The response event for audiobridge leave request. * - * @typedef {object} AUDIOBRIDGE_EVENT_LEAVING + * @typedef {Object} AUDIOBRIDGE_EVENT_LEAVING * @property {number|string} room - The involved room * @property {number|string} feed- The feed that is leaving */ @@ -919,7 +1265,7 @@ class AudioBridgeHandle extends Handle { /** * The response event for audiobridge participants list request. * - * @typedef {object} AUDIOBRIDGE_EVENT_PARTICIPANTS_LIST + * @typedef {Object} AUDIOBRIDGE_EVENT_PARTICIPANTS_LIST * @property {number|string} room - The involved room * @property {object[]} participants - The list of participants * @property {number|string} participants[].feed - The participant feed identifier @@ -927,12 +1273,13 @@ class AudioBridgeHandle extends Handle { * @property {boolean} [participants[].muted] - The muted status of the participant * @property {boolean} [participants[].setup] - True if participant PeerConnection is up * @property {boolean} [participants[].talking] - True if participant is talking + * @property {boolean} [participants[].suspended] - True if participant is suspended */ /** * The response event for audiobridge participant kick request. * - * @typedef {object} AUDIOBRIDGE_EVENT_KICK_RESPONSE + * @typedef {Object} AUDIOBRIDGE_EVENT_KICK_RESPONSE * @property {number|string} room - The involved room * @property {number|string} feed - The feed that has been kicked out */ @@ -940,7 +1287,7 @@ class AudioBridgeHandle extends Handle { /** * The response event for audiobridge room exists request. * - * @typedef {object} AUDIOBRIDGE_EVENT_EXISTS + * @typedef {Object} AUDIOBRIDGE_EVENT_EXISTS * @property {number|string} room - The involved room * @property {boolean} exists - True if the rooms exists */ @@ -948,16 +1295,16 @@ class AudioBridgeHandle extends Handle { /** * The response event for audiobridge room list request. * - * @typedef {object} AUDIOBRIDGE_EVENT_ROOMS_LIST + * @typedef {Object} AUDIOBRIDGE_EVENT_ROOMS_LIST * @property {object[]} list - The list of the rooms as returned by Janus */ /** * The response event for audiobridge forwarder start request. * - * @typedef {object} AUDIOBRIDGE_EVENT_RTP_FWD + * @typedef {Object} AUDIOBRIDGE_EVENT_RTP_FWD * @property {number|string} room - The involved room - * @property {object} forwarder - Forwarder descriptor + * @property {Object} forwarder - Forwarder descriptor * @property {string} forwarder.host - The target host * @property {number} forwarder.audio_port - The target port * @property {number} forwarder.audio_stream - The identifier of the forwarder @@ -967,7 +1314,7 @@ class AudioBridgeHandle extends Handle { /** * The response event for audiobridge room create request. * - * @typedef {object} AUDIOBRIDGE_EVENT_CREATED + * @typedef {Object} AUDIOBRIDGE_EVENT_CREATED * @property {number|string} room - The created room * @property {boolean} permanent - True if the room is being persisted in the Janus config file */ @@ -975,14 +1322,14 @@ class AudioBridgeHandle extends Handle { /** * The response event for audiobridge room destroy request. * - * @typedef {object} AUDIOBRIDGE_EVENT_DESTROYED + * @typedef {Object} AUDIOBRIDGE_EVENT_DESTROYED * @property {number|string} room - The destroyed room */ /** * The response event for audiobridge ACL token edit request. * - * @typedef {object} AUDIOBRIDGE_EVENT_RECORDING + * @typedef {Object} AUDIOBRIDGE_EVENT_RECORDING * @property {number|string} room - The involved room * @property {boolean} record - Wheter recording is active or not */ @@ -990,7 +1337,7 @@ class AudioBridgeHandle extends Handle { /** * The response event for audiobridge forwarders list request. * - * @typedef {object} AUDIOBRIDGE_EVENT_FWD_LIST + * @typedef {Object} AUDIOBRIDGE_EVENT_FWD_LIST * @property {number|string} room - The involved room * @property {object[]} forwarders - The list of forwarders * @property {string} forwarders[].host - The target host @@ -1001,44 +1348,128 @@ class AudioBridgeHandle extends Handle { */ /** - * The response event for audiobridge mute request. + * The response event for audiobridge mute participant request. * - * @typedef {object} AUDIOBRIDGE_EVENT_MUTE_ROOM_RESPONSE + * @typedef {Object} AUDIOBRIDGE_EVENT_MUTE_PARTICIPANT_RESPONSE * @property {number|string} room - The involved room + * @property {number|string} feed - The involved feed id */ /** - * The response event for audiobridge unmute request. + * The response event for audiobridge unmute participant request. * - * @typedef {object} AUDIOBRIDGE_EVENT_UNMUTE_ROOM_RESPONSE + * @typedef {Object} AUDIOBRIDGE_EVENT_UNMUTE_PARTICIPANT_RESPONSE * @property {number|string} room - The involved room + * @property {number|string} feed - The involved feed id + */ + +/** + * The response event for audiobridge mute room request. + * + * @typedef {Object} AUDIOBRIDGE_EVENT_MUTE_ROOM_RESPONSE + * @property {number|string} room - The involved room + */ + +/** + * The response event for audiobridge unmute room request. + * + * @typedef {Object} AUDIOBRIDGE_EVENT_UNMUTE_ROOM_RESPONSE + * @property {number|string} room - The involved room + */ + +/** + * The response event for audiobridge suspend request. + * + * @typedef {Object} AUDIOBRIDGE_EVENT_SUSPEND_RESPONSE + * @property {number|string} room - The involved room + * @property {number|string} feed - The involved feed id + */ + +/** + * The response event for audiobridge resume request. + * + * @typedef {Object} AUDIOBRIDGE_EVENT_RESUME_RESPONSE + * @property {number|string} room - The involved room + * @property {number|string} feed - The involved feed id */ /** * The response event for audiobridge ACL token edit request. * - * @typedef {object} AUDIOBRIDGE_EVENT_ALLOWED + * @typedef {Object} AUDIOBRIDGE_EVENT_ALLOWED * @property {number|string} room - The involved room * @property {string[]} list - The updated, complete, list of allowed tokens */ +/** + * The response event for audiobridge play_file request. + * + * @typedef {Object} AUDIOBRIDGE_EVENT_PLAY_FILE_RESPONSE + * @property {number|string} room - The involved room + * @property {string} file_id - The involved file id + */ + +/** + * The response event for audiobridge is_playing request. + * + * @typedef {Object} AUDIOBRIDGE_EVENT_IS_PLAYING_RESPONSE + * @property {number|string} room - The involved room + * @property {string} file_id - The involved file id + * @property {boolean} playing - True if the file is being played + */ + +/** + * The response event for audiobridge stop_file request. + * + * @typedef {Object} AUDIOBRIDGE_EVENT_STOP_FILE_RESPONSE + * @property {number|string} room - The involved room + * @property {string} file_id - The involved file id + */ + +/** + * The response event for audiobridge stop_all_files request. + * + * @typedef {Object} AUDIOBRIDGE_EVENT_STOP_ALL_FILES_RESPONSE + * @property {number|string} room - The involved room + * @property {string[]} file_id_list - The list of file ids that was stopped + */ + +/** + * The response event for audiobridge announcements list request. + * + * @typedef {Object} AUDIOBRIDGE_EVENT_ANNOUNCEMENTS_LIST + * @property {number|string} room - The involved room + * @property {object[]} announcements - The list of announcements + * @property {string} announcements[].file_id - The announcement identifier + * @property {string} announcements[].filename - The path to the Opus file + * @property {boolean} announcements[].playing - True if the announcement is playing + * @property {boolean} announcements[].loop - True if the announcement will be played in a loop + */ + /** * The exported plugin descriptor. * - * @type {object} + * @type {Object} * @property {string} id - The plugin identifier used when attaching to Janus * @property {module:audiobridge-plugin~AudioBridgeHandle} Handle - The custom class implementing the plugin - * @property {object} EVENT - The events emitted by the plugin - * @property {string} EVENT.AUDIOBRIDGE_DESTROYED {@link module:audiobridge-plugin~AUDIOBRIDGE_DESTROYED} - * @property {string} EVENT.AUDIOBRIDGE_KICKED - {@link module:audiobridge-plugin~AUDIOBRIDGE_KICKED} - * @property {string} EVENT.AUDIOBRIDGE_PEER_JOINED {@link module:audiobridge-plugin~AUDIOBRIDGE_PEER_JOINED} - * @property {string} EVENT.AUDIOBRIDGE_PEER_CONFIGURED {@link module:audiobridge-plugin~AUDIOBRIDGE_PEER_CONFIGURED} - * @property {string} EVENT.AUDIOBRIDGE_PEER_KICKED {@link module:audiobridge-plugin~AUDIOBRIDGE_PEER_KICKED} - * @property {string} EVENT.AUDIOBRIDGE_PEER_LEAVING {@link module:audiobridge-plugin~AUDIOBRIDGE_PEER_LEAVING} - * @property {string} EVENT.AUDIOBRIDGE_TALKING {@link module:audiobridge-plugin~AUDIOBRIDGE_TALKING} - * @property {string} EVENT.AUDIOBRIDGE_PEER_TALKING {@link module:audiobridge-plugin~AUDIOBRIDGE_PEER_TALKING} - * @property {string} EVENT.AUDIOBRIDGE_ROOM_MUTED {@link module:audiobridge-plugin~AUDIOBRIDGE_ROOM_MUTED} - * @property {string} EVENT.AUDIOBRIDGE_ERROR {@link module:audiobridge-plugin~AUDIOBRIDGE_ERROR} + * @property {Object} EVENT - The events emitted by the plugin + * @property {string} EVENT.AUDIOBRIDGE_DESTROYED {@link module:audiobridge-plugin~AudioBridgeHandle#event:AUDIOBRIDGE_DESTROYED AUDIOBRIDGE_DESTROYED} + * @property {string} EVENT.AUDIOBRIDGE_CONFIGURED {@link module:audiobridge-plugin~AudioBridgeHandle#event:AUDIOBRIDGE_CONFIGURED AUDIOBRIDGE_CONFIGURED} + * @property {string} EVENT.AUDIOBRIDGE_KICKED - {@link module:audiobridge-plugin~AudioBridgeHandle#event:AUDIOBRIDGE_KICKED AUDIOBRIDGE_KICKED} + * @property {string} EVENT.AUDIOBRIDGE_PEER_JOINED {@link module:audiobridge-plugin~AudioBridgeHandle#event:AUDIOBRIDGE_PEER_JOINED AUDIOBRIDGE_PEER_JOINED} + * @property {string} EVENT.AUDIOBRIDGE_PEER_CONFIGURED {@link module:audiobridge-plugin~AudioBridgeHandle#event:AUDIOBRIDGE_PEER_CONFIGURED AUDIOBRIDGE_PEER_CONFIGURED} + * @property {string} EVENT.AUDIOBRIDGE_PEER_KICKED {@link module:audiobridge-plugin~AudioBridgeHandle#event:AUDIOBRIDGE_PEER_KICKED AUDIOBRIDGE_PEER_KICKED} + * @property {string} EVENT.AUDIOBRIDGE_PEER_LEAVING {@link module:audiobridge-plugin~AudioBridgeHandle#event:AUDIOBRIDGE_PEER_LEAVING AUDIOBRIDGE_PEER_LEAVING} + * @property {string} EVENT.AUDIOBRIDGE_TALKING {@link module:audiobridge-plugin~AudioBridgeHandle#event:AUDIOBRIDGE_TALKING AUDIOBRIDGE_TALKING} + * @property {string} EVENT.AUDIOBRIDGE_PEER_TALKING {@link module:audiobridge-plugin~AudioBridgeHandle#event:AUDIOBRIDGE_PEER_TALKING AUDIOBRIDGE_PEER_TALKING} + * @property {string} EVENT.AUDIOBRIDGE_SUSPENDED {@link module:audiobridge-plugin~AudioBridgeHandle#event:AUDIOBRIDGE_SUSPENDED AUDIOBRIDGE_SUSPENDED} + * @property {string} EVENT.AUDIOBRIDGE_PEER_SUSPENDED {@link module:audiobridge-plugin~AudioBridgeHandle#event:AUDIOBRIDGE_PEER_SUSPENDED AUDIOBRIDGE_PEER_SUSPENDED} + * @property {string} EVENT.AUDIOBRIDGE_RESUMED {@link module:audiobridge-plugin~AudioBridgeHandle#event:AUDIOBRIDGE_RESUMED AUDIOBRIDGE_RESUMED} + * @property {string} EVENT.AUDIOBRIDGE_PEER_RESUMED {@link module:audiobridge-plugin~AudioBridgeHandle#event:AUDIOBRIDGE_PEER_RESUMED AUDIOBRIDGE_PEER_RESUMED} + * @property {string} EVENT.AUDIOBRIDGE_ROOM_MUTED {@link module:audiobridge-plugin~AudioBridgeHandle#event:AUDIOBRIDGE_ROOM_MUTED AUDIOBRIDGE_ROOM_MUTED} + * @property {string} EVENT.AUDIOBRIDGE_ANNOUNCEMENT_STARTED {@link module:audiobridge-plugin~AudioBridgeHandle#event:AUDIOBRIDGE_ANNOUNCEMENT_STARTED AUDIOBRIDGE_ANNOUNCEMENT_STARTED} + * @property {string} EVENT.AUDIOBRIDGE_ANNOUNCEMENT_STOPPED {@link module:audiobridge-plugin~AudioBridgeHandle#event:AUDIOBRIDGE_ANNOUNCEMENT_STOPPED AUDIOBRIDGE_ANNOUNCEMENT_STOPPED} + * @property {string} EVENT.AUDIOBRIDGE_ERROR {@link module:audiobridge-plugin~AudioBridgeHandle#event:AUDIOBRIDGE_ERROR AUDIOBRIDGE_ERROR} */ export default { id: PLUGIN_ID, @@ -1049,17 +1480,31 @@ export default { * The audiobridge room has been destroyed. * * @event module:audiobridge-plugin~AudioBridgeHandle#event:AUDIOBRIDGE_DESTROYED - * @type {module:audiobridge-plugin~AUDIOBRIDGE_EVENT_DESTROYED} + * @type {Object} + * @property {number|string} room - The destroyed room identifier */ AUDIOBRIDGE_DESTROYED: PLUGIN_EVENT.DESTROYED, + /** + * The current user has been configured. + * + * @event module:audiobridge-plugin~AudioBridgeHandle#event:AUDIOBRIDGE_CONFIGURED + * @type {Object} + * @property {number|string} room - The involved room identifier + * @property {number|string} feed - The user feed identifier + * @property {string} [display] - The user display name + * @property {boolean} [muted] - True if the user is muted + * @property {boolean} [setup] - True if the user PeerConnection is up + */ + AUDIOBRIDGE_CONFIGURED: PLUGIN_EVENT.CONFIGURED, + /** * The current user has been kicked out. * * @event module:audiobridge-plugin~AudioBridgeHandle#event:AUDIOBRIDGE_KICKED - * @type {object} - * @property {number|string} room - * @property {number|string} feed + * @type {Object} + * @property {number|string} room - The involved room identifier + * @property {number|string} feed - The user feed identifier */ AUDIOBRIDGE_KICKED: PLUGIN_EVENT.KICKED, @@ -1067,12 +1512,12 @@ export default { * A new participant joined. * * @event module:audiobridge-plugin~AudioBridgeHandle#event:AUDIOBRIDGE_PEER_JOINED - * @type {object} - * @property {number|string} room - * @property {number|string} feed - * @property {string} [display] - * @property {boolean} [muted] - * @property {boolean} [setup] + * @type {Object} + * @property {number|string} room - The involved room identifier + * @property {number|string} feed - The joined participant feed identifier + * @property {string} [display] - The joined participant display name + * @property {boolean} [muted] - True if the participant is muted + * @property {boolean} [setup] - True if the participant PeerConnection is up */ AUDIOBRIDGE_PEER_JOINED: PLUGIN_EVENT.PEER_JOINED, @@ -1080,12 +1525,12 @@ export default { * A participant has been configured. * * @event module:audiobridge-plugin~AudioBridgeHandle#event:AUDIOBRIDGE_PEER_CONFIGURED - * @type {object} - * @property {number|string} room - * @property {number|string} feed - * @property {string} [display] - * @property {boolean} [muted] - * @property {boolean} [setup] + * @type {Object} + * @property {number|string} room - The involved room identifier + * @property {number|string} feed - The configured participant feed identifier + * @property {string} [display] - The configured participant display name + * @property {boolean} [muted] - True if the participant is muted + * @property {boolean} [setup] - True if the participant PeerConnection is up */ AUDIOBRIDGE_PEER_CONFIGURED: PLUGIN_EVENT.PEER_CONFIGURED, @@ -1093,9 +1538,9 @@ export default { * A participant has been kicked out. * * @event module:audiobridge-plugin~AudioBridgeHandle#event:AUDIOBRIDGE_PEER_KICKED - * @type {object} - * @property {number|string} room - * @property {number|string} feed + * @type {Object} + * @property {number|string} room - The involved room identifier + * @property {number|string} feed - The kicked participant feed identifier */ AUDIOBRIDGE_PEER_KICKED: PLUGIN_EVENT.PEER_KICKED, @@ -1103,9 +1548,9 @@ export default { * A participant is leaving. * * @event module:audiobridge-plugin~AudioBridgeHandle#event:AUDIOBRIDGE_PEER_LEAVING - * @type {object} - * @property {number|string} room - * @property {number|string} feed + * @type {Object} + * @property {number|string} room - The involved room identifier + * @property {number|string} feed - The leaving participant feed identifier */ AUDIOBRIDGE_PEER_LEAVING: PLUGIN_EVENT.PEER_LEAVING, @@ -1113,10 +1558,10 @@ export default { * Notify if the current user is talking. * * @event module:audiobridge-plugin~AudioBridgeHandle#event:AUDIOBRIDGE_TALKING - * @type {object} - * @property {number|string} room - * @property {number|string} feed - * @property {boolean} talking + * @type {Object} + * @property {number|string} room - The involved room identifier + * @property {number|string} feed - The user feed identifier + * @property {boolean} talking - True if the user is talking */ AUDIOBRIDGE_TALKING: PLUGIN_EVENT.TALKING, @@ -1124,23 +1569,84 @@ export default { * Notify if a participant is talking. * * @event module:audiobridge-plugin~AudioBridgeHandle#event:AUDIOBRIDGE_PEER_TALKING - * @type {object} - * @property {number|string} room - * @property {number|string} feed - * @property {boolean} talking + * @type {Object} + * @property {number|string} room - The involved room identifier + * @property {number|string} feed - The participant feed identifier + * @property {boolean} talking - True if the participant is talking */ AUDIOBRIDGE_PEER_TALKING: PLUGIN_EVENT.PEER_TALKING, + /** + * The current user has been suspended. + * + * @event module:audiobridge-plugin~AudioBridgeHandle#event:AUDIOBRIDGE_SUSPENDED + * @type {Object} + * @property {number|string} room - The involved room identifier + * @property {number|string} feed - The user feed identifier + */ + AUDIOBRIDGE_SUSPENDED: PLUGIN_EVENT.SUSPENDED, + /** + * Notify if a participant has been suspended. + * + * @event module:audiobridge-plugin~AudioBridgeHandle#event:AUDIOBRIDGE_PEER_SUSPENDED + * @type {Object} + * @property {number|string} room - The involved room identifier + * @property {number|string} feed - The participant feed identifier + * @property {string} display - The participant display name + */ + AUDIOBRIDGE_PEER_SUSPENDED: PLUGIN_EVENT.PEER_SUSPENDED, + + /** + * The current user has been resumed. + * + * @event module:audiobridge-plugin~AudioBridgeHandle#event:AUDIOBRIDGE_RESUMED + * @type {Object} + * @property {number|string} room - The involved room identifier + * @property {number|string} feed - The user feed identifier + */ + AUDIOBRIDGE_RESUMED: PLUGIN_EVENT.RESUMED, + + /** + * Notify if a participant has been resumed. + * + * @event module:audiobridge-plugin~AudioBridgeHandle#event:AUDIOBRIDGE_PEER_RESUMED + * @type {Object} + * @property {number|string} room - The involved room identifier + * @property {number|string} feed - The participant feed identifier + * @property {string} display - The participant display name + */ + AUDIOBRIDGE_PEER_RESUMED: PLUGIN_EVENT.PEER_RESUMED, + /** * The room has been muted or not. * * @event module:audiobridge-plugin~AudioBridgeHandle#event:AUDIOBRIDGE_ROOM_MUTED - * @type {object} - * @property {number|string} room - * @property {boolean} muted + * @type {Object} + * @property {number|string} room - The involved room identifier + * @property {boolean} muted - True if the room is muted */ AUDIOBRIDGE_ROOM_MUTED: PLUGIN_EVENT.ROOM_MUTED, + /** + * The announcement has been started. + * + * @event module:audiobridge-plugin~AudioBridgeHandle#event:AUDIOBRIDGE_ANNOUNCEMENT_STARTED + * @type {Object} + * @property {number|string} room - The involved room identifier + * @property {string} file_id - Unique string ID of the announcement + */ + AUDIOBRIDGE_ANNOUNCEMENT_STARTED: PLUGIN_EVENT.ANNOUNCEMENT_STARTED, + + /** + * The announcement has been stopped. + * + * @event module:audiobridge-plugin~AudioBridgeHandle#event:AUDIOBRIDGE_ANNOUNCEMENT_STOPPED + * @type {Object} + * @property {number|string} room - The involved room identifier + * @property {string} file_id - Unique string ID of the announcement + */ + AUDIOBRIDGE_ANNOUNCEMENT_STOPPED: PLUGIN_EVENT.ANNOUNCEMENT_STOPPED, + /** * Generic audiobridge error. * @@ -1149,4 +1655,4 @@ export default { */ AUDIOBRIDGE_ERROR: PLUGIN_EVENT.ERROR, }, -}; \ No newline at end of file +}; diff --git a/src/plugins/echotest-plugin.js b/src/plugins/echotest-plugin.js index ec3bf86..be62d35 100644 --- a/src/plugins/echotest-plugin.js +++ b/src/plugins/echotest-plugin.js @@ -26,6 +26,7 @@ const PLUGIN_EVENT = { * Moreover it defines some methods to support EchoTest operations.
* * @hideconstructor + * @extends module:handle~Handle */ class EchoTestHandle extends Handle { /** @@ -42,11 +43,11 @@ class EchoTestHandle extends Handle { * The custom "handleMessage" needed for handling EchoTest messages. * * @private - * @param {object} janus_message - * @returns {object} A falsy value for unhandled events, a truthy value for handled events + * @param {Object} janus_message + * @returns {Object} A falsy value for unhandled events, a truthy value for handled events */ handleMessage(janus_message) { - const { plugindata, jsep, transaction } = janus_message; + const { plugindata, transaction } = janus_message; if (plugindata && plugindata.data && plugindata.data.echotest) { /** * @type {EchoTestData} @@ -55,23 +56,12 @@ class EchoTestHandle extends Handle { const { echotest, event, error, error_code, result } = message_data; /* Prepare an object for the output Janode event */ - const janode_event = { - /* The name of the resolved event */ - event: null, - /* The event payload */ - data: {}, - }; - - /* Add JSEP data if available */ - if (jsep) janode_event.data.jsep = jsep; + const janode_event = this._newPluginEvent(janus_message); /* The plugin will emit an event only if the handle does not own the transaction */ /* That means that a transaction has already been closed or this is an async event */ const emit = (this.ownsTransaction(transaction) === false); - /* Use the "janode" property to store the output event */ - janus_message._janode = janode_event; - switch (echotest) { /* Generic event (e.g. result, error) */ @@ -79,7 +69,10 @@ class EchoTestHandle extends Handle { /* EchoTest SlowLink event */ if (event === 'slow_link') { janode_event.event = PLUGIN_EVENT.SLOWLINK; - janode_event.data.bitrate = message_data['current-bitrate']; + janode_event.data.media = message_data.media; + if (typeof message_data['current-bitrate'] !== 'number') { + janode_event.data.bitrate = message_data['current-bitrate']; + } break; } /* EchoTest Result event (ok, done ...) */ @@ -116,13 +109,13 @@ class EchoTestHandle extends Handle { /** * Start/update an echotest session. * - * @param {object} params - * @param {boolean} [audio] - True to request audio in this session - * @param {boolean} [video] - True to request video in this session - * @param {RTCSessionDescription} [jsep=null] - The JSEP offer - * @param {number} [bitrate=0] - The bitrate to force in the session - * @param {boolean} [record=false] - True to record the session - * @param {string} [filename=null] - The filename of the recording + * @param {Object} params + * @param {boolean} [params.audio] - True to request audio in this session + * @param {boolean} [params.video] - True to request video in this session + * @param {RTCSessionDescription} [params.jsep=null] - The JSEP offer + * @param {number} [params.bitrate=0] - The bitrate to force in the session + * @param {boolean} [params.record=false] - True to record the session + * @param {string} [params.filename=null] - The filename of the recording * @returns {Promise} */ async start({ audio, video, jsep = null, bitrate = 0, record = false, filename = null }) { @@ -141,7 +134,7 @@ class EchoTestHandle extends Handle { if (typeof filename === 'string') body.filename = filename; const response = await this.message(body, jsep); - const { event, data: evtdata } = response._janode || {}; + const { event, data: evtdata } = this._getPluginEvent(response); if (event === PLUGIN_EVENT.RESULT && evtdata.result === 'ok') return evtdata; const error = new Error('invalid echotest result'); @@ -155,40 +148,47 @@ class EchoTestHandle extends Handle { * {@link https://janus.conf.meetecho.com/docs/echotest.html} * * @private - * @typedef {object} EchoTestData + * @typedef {Object} EchoTestData */ /** - * @typedef {object} ECHOTEST_EVENT_RESULT + * @typedef {Object} ECHOTEST_EVENT_RESULT * @property {string} result - The result status (ok, done ...) - * @property {RTCSessionDescription} [jsep] - The answer from Janus + * @property {RTCSessionDescription} [jsep] - The JSEP answer from Janus */ /** * The exported plugin descriptor. * - * @type {object} + * @type {Object} * @property {string} id - The plugin identifier used when attaching to Janus - * @property {module:audiobridge-plugin~AudioBridgeHandle} Handle - The custom class implementing the plugin - * @property {object} EVENT - The events emitted by the plugin - * @property {string} EVENT.ECHOTEST_RESULT {@link module:echotest-plugin~ECHOTEST_RESULT} - * @property {string} EVENT.ECHOTEST_SLOWLINK {@link module:echotest-plugin~ECHOTEST_SLOWLINK} - * @property {string} EVENT.ECHOTEST_ERROR {@link module:echotest-plugin~ECHOTEST_ERROR} + * @property {module:echotest-plugin~EchoTestHandle} Handle - The custom class implementing the plugin + * @property {Object} EVENT - The events emitted by the plugin + * @property {string} EVENT.ECHOTEST_RESULT {@link module:echotest-plugin~EchoTestHandle#event:ECHOTEST_RESULT ECHOTEST_RESULT} + * @property {string} EVENT.ECHOTEST_SLOWLINK {@link module:echotest-plugin~EchoTestHandle#event:ECHOTEST_SLOWLINK ECHOTEST_SLOWLINK} + * @property {string} EVENT.ECHOTEST_ERROR {@link module:echotest-plugin~EchoTestHandle#event:ECHOTEST_ERROR ECHOTEST_ERROR} */ export default { id: PLUGIN_ID, Handle: EchoTestHandle, EVENT: { /** + * The result event emitted if the session status changes. + * * @event module:echotest-plugin~EchoTestHandle#event:ECHOTEST_RESULT - * @type {module:echotest-plugin~ECHOTEST_EVENT_RESULT} + * @type {Object} + * @property {string} result - The result status (ok, done ...) + * @property {RTCSessionDescription} [jsep] - The JSEP answer from Janus */ ECHOTEST_RESULT: PLUGIN_EVENT.RESULT, /** + * The slowlink event emitted by the plugin. + * * @event module:echotest-plugin~EchoTestHandle#event:ECHOTEST_SLOWLINK - * @type {object} - * @property {number} bitrate + * @type {Object} + * @property {string} media - The media type (audio, video) + * @property {number} [bitrate] - Current bitrate cap */ ECHOTEST_SLOWLINK: PLUGIN_EVENT.SLOWLINK, diff --git a/src/plugins/recordplay-plugin.js b/src/plugins/recordplay-plugin.js new file mode 100644 index 0000000..7c97777 --- /dev/null +++ b/src/plugins/recordplay-plugin.js @@ -0,0 +1,526 @@ +'use strict'; + +/** + * This module contains the implementation of the Record&Play plugin (ref. {@link https://janus.conf.meetecho.com/docs/recordplay.html}). + * @module recordplay-plugin + */ + +import Handle from '../handle.js'; + +/* The plugin ID exported in the plugin descriptor */ +const PLUGIN_ID = 'janus.plugin.recordplay'; + +/* These are the requests defined for the Janus RecordPlay API */ +const REQUEST_LIST = 'list'; +const REQUEST_UPDATE = 'update'; +const REQUEST_RECORD = 'record'; +const REQUEST_PLAY = 'play'; +const REQUEST_START = 'start'; +const REQUEST_CONFIGURE = 'configure'; +const REQUEST_PAUSE = 'pause'; +const REQUEST_RESUME = 'resume'; +const REQUEST_STOP = 'stop'; + +/* These are the events/responses that the Janode plugin will manage */ +/* Some of them will be exported in the plugin descriptor */ +const PLUGIN_EVENT = { + RECORDINGS_LIST: 'recordplay_list', + RECORDING: 'recordplay_recording', + CONFIGURED: 'recordplay_configured', + PAUSED: 'recordplay_paused', + RESUMED: 'recordplay_resumed', + PREPARING: 'recordplay_preparing', + PLAYING: 'recordplay_playing', + STOPPED: 'recordplay_stopped', + SLOW_LINK: 'recordplay_slowlink', + DONE: 'recordplay_done', + SUCCESS: 'recordplay_success', + ERROR: 'recordplay_error', +}; + +/** + * The class implementing the Record&Play plugin (ref. {@link https://janus.conf.meetecho.com/docs/recordplay.html}).
+ * + * It extends the base Janode Handle class and overrides the base "handleMessage" method.
+ * + * Moreover it defines many methods to support RecordPlay operations. + * + * @hideconstructor + * @extends module:handle~Handle + */ +class RecordPlayHandle extends Handle { + /** + * Create a Janode RecordPlay handle. + * + * @param {module:session~Session} session - A reference to the parent session + * @param {number} id - The handle identifier + */ + constructor(session, id) { + super(session, id); + } + + /** + * The custom "handleMessage" needed for handling RecordPlay messages. + * + * @private + * @param {Object} janus_message + * @returns {Object} A falsy value for unhandled events, a truthy value for handled events + */ + handleMessage(janus_message) { + const { plugindata, transaction } = janus_message; + if (plugindata && plugindata.data && plugindata.data.recordplay) { + /** + * @type {RecordPlayData} + */ + const message_data = plugindata.data; + const { recordplay, error, error_code } = message_data; + + /* Prepare an object for the output Janode event */ + const janode_event = this._newPluginEvent(janus_message); + + /* The plugin will emit an event only if the handle does not own the transaction */ + /* That means that a transaction has already been closed or this is an async event */ + const emit = (this.ownsTransaction(transaction) === false); + + switch (recordplay) { + + /* Got a list of recordings */ + case 'list': + /* Recordings list API */ + janode_event.event = PLUGIN_EVENT.RECORDINGS_LIST; + if (typeof message_data.list !== 'undefined') + janode_event.data.list = message_data.list; + break; + + /* Update success */ + case 'ok': + /* "ok" is treated as "success" */ + janode_event.event = PLUGIN_EVENT.SUCCESS; + break; + + /* Configure success */ + case 'configure': + /* Configure API */ + janode_event.event = PLUGIN_EVENT.CONFIGURED; + if (typeof message_data.result !== 'undefined') + janode_event.data.settings = message_data.result.settings; + break; + + /* Generic event (e.g. errors) */ + case 'event': + /* RecordPlay error */ + if (error) { + janode_event.event = PLUGIN_EVENT.ERROR; + janode_event.data = new Error(`${error_code} ${error}`); + /* In case of error, close a transaction */ + this.closeTransactionWithError(transaction, janode_event.data); + break; + } + /* Update for this handle */ + if (typeof message_data.result !== 'undefined') { + if (typeof message_data.result.status !== 'undefined') { + if (typeof message_data.result.id !== 'undefined') + janode_event.data.id = message_data.result.id; + if (typeof message_data.result.is_private !== 'undefined') + janode_event.data.is_private = message_data.result.is_private; + if (typeof message_data.result.media !== 'undefined') + janode_event.data.media = message_data.result.media; + if (typeof message_data.result.uplink !== 'undefined') + janode_event.data.uplink = message_data.result.uplink; + switch (message_data.result.status) { + case 'recording': + janode_event.event = PLUGIN_EVENT.RECORDING; + break; + case 'paused': + janode_event.event = PLUGIN_EVENT.PAUSED; + break; + case 'resumed': + janode_event.event = PLUGIN_EVENT.RESUMED; + break; + case 'preparing': + janode_event.event = PLUGIN_EVENT.PREPARING; + break; + case 'playing': + janode_event.event = PLUGIN_EVENT.PLAYING; + break; + case 'stopped': + janode_event.event = PLUGIN_EVENT.STOPPED; + break; + case 'slow_link': + janode_event.event = PLUGIN_EVENT.SLOW_LINK; + break; + case 'done': + janode_event.event = PLUGIN_EVENT.DONE; + break; + } + } + break; + } + } + + /* The event has been handled */ + if (janode_event.event) { + /* Try to close the transaction */ + this.closeTransactionWithSuccess(transaction, janus_message); + /* If the transaction was not owned, emit the event */ + if (emit) this.emit(janode_event.event, janode_event.data); + return janode_event; + } + } + + /* The event has not been handled, return a falsy value */ + return null; + } + + /*----------*/ + /* USER API */ + /*----------*/ + + /* These are the APIs that users need to work with the recordplay plugin */ + + /** + * List recordings. + * + * @param {Object} params + * @param {string} [params.admin_key] - The optional admin key needed for invoking the API + * @returns {Promise} + */ + async listRecordings({ admin_key }) { + const body = { + request: REQUEST_LIST, + }; + if (typeof admin_key === 'string') body.admin_key = admin_key; + + const response = await this.message(body); + const { event, data: evtdata } = this._getPluginEvent(response); + if (event === PLUGIN_EVENT.RECORDINGS_LIST) + return evtdata; + const error = new Error(`unexpected response to ${body.request} request`); + throw (error); + } + + /** + * Re-index the list of recordings. + * + * @param {Object} params + * @param {string} [params.admin_key] - The optional admin key needed for invoking the API + * @returns {Promise} + */ + async updateRecordings({ admin_key }) { + const body = { + request: REQUEST_UPDATE, + }; + if (typeof admin_key === 'string') body.admin_key = admin_key; + + const response = await this.message(body); + const { event, data: evtdata } = this._getPluginEvent(response); + if (event === PLUGIN_EVENT.SUCCESS) + return evtdata; + const error = new Error(`unexpected response to ${body.request} request`); + throw (error); + } + + /** + * Configure a recording session. + * + * @param {Object} params + * @param {number} [params.maxBitrate] - The optional bitrate to enforce via REMB + * @param {number} [params.keyframeInterval] - The optional keyframe interval to enforce, in ms + * @returns {Promise} + */ + async configure({ maxBitrate, keyframeInterval }) { + const body = { + request: REQUEST_CONFIGURE, + }; + if (typeof maxBitrate === 'number') body['video-bitrate-max'] = maxBitrate; + if (typeof keyframeInterval === 'number') body['video-keyframe-interval'] = keyframeInterval; + + const response = await this.message(body); + const { event, data: evtdata } = this._getPluginEvent(response); + if (event === PLUGIN_EVENT.CONFIGURED) + return evtdata; + const error = new Error(`unexpected response to ${body.request} request`); + throw (error); + } + + /** + * Start a recording session. + * + * @param {Object} params + * @param {number} [params.id] - The ID to assign to the recording + * @param {string} [params.name] - The short description of the recording + * @param {boolean} [params.is_private] - Flag the recording as private + * @param {string} [params.filename] - Set the base path/filename for the recording + * @param {string} [audiocodec] - Set the audio codec to use in the recording + * @param {string} [videocodec] - Set the video codec to use in the recording + * @param {string} [videoprofile] - Set the video fmtp to use in the recording + * @param {boolean} [params.opusred] - Set whether RED should be negotiated for audio + * @param {boolean} [params.textdata] - In case data channels are negotiated, set whether it should be text (default) or binary data + * @param {boolean} [params.update] - Set to true for renegotiations + * @param {RTCSessionDescription} params.jsep - JSEP offer to be sent to Janus + * @returns {Promise} + */ + async record({ id, name, is_private, filename, audiocodec, videocodec, videoprofile, opusred, textdata, update, jsep }) { + if (!jsep || typeof jsep !== 'object' || jsep && jsep.type !== 'offer') { + const error = new Error('jsep must be an offer'); + return Promise.reject(error); + } + const body = { + request: REQUEST_RECORD, + }; + if (typeof id === 'number') body.id = id; + if (typeof name === 'string') body.name = name; + if (typeof is_private === 'boolean') body.is_private = is_private; + if (typeof filename === 'string') body.filename = filename; + if (typeof audiocodec === 'string') body.audiocodec = audiocodec; + if (typeof videocodec === 'string') body.videocodec = videocodec; + if (typeof videoprofile === 'string') body.videoprofile = videoprofile; + if (typeof opusred === 'boolean') body.opusred = opusred; + if (typeof textdata === 'boolean') body.textdata = textdata; + if (typeof update === 'boolean') body.update = update; + + const response = await this.message(body, jsep); + const { event, data: evtdata } = this._getPluginEvent(response); + if (event === PLUGIN_EVENT.RECORDING) + return evtdata; + const error = new Error(`unexpected response to ${body.request} request`); + throw (error); + } + + /** + * Play an existing recording. + * + * @param {Object} params + * @param {number} params.id - The ID of the recording to replay + * @param {boolean} [params.restart] - Set to true for triggering a renegotiation and an ICE restart + * @returns {Promise} + */ + async play({ id, restart }) { + const body = { + request: REQUEST_PLAY, + id + }; + if (typeof restart === 'boolean') body.restart = restart; + + const response = await this.message(body); + const { event, data: evtdata } = this._getPluginEvent(response); + if (event === PLUGIN_EVENT.PREPARING) + return evtdata; + const error = new Error(`unexpected response to ${body.request} request`); + throw (error); + } + + /** + * Start a playback session. + * + * @param {Object} params + * @param {RTCSessionDescription} params.jsep + * @returns {Promise} + */ + async start({ jsep }) { + if (!jsep || typeof jsep !== 'object' || jsep && jsep.type !== 'answer') { + const error = new Error('jsep must be an answer'); + return Promise.reject(error); + } + + const body = { + request: REQUEST_START, + }; + + const response = await this.message(body, jsep); + const { event, data: evtdata } = this._getPluginEvent(response);; + if (event === PLUGIN_EVENT.PLAYING) + return evtdata; + const error = new Error(`unexpected response to ${body.request} request`); + throw (error); + } + + /** + * Pauses the current recording session. + * + * @returns {Promise} + */ + async pause() { + const body = { + request: REQUEST_PAUSE, + }; + + const response = await this.message(body); + const { event, data: evtdata } = this._getPluginEvent(response);; + if (event === PLUGIN_EVENT.PAUSED) + return evtdata; + const error = new Error(`unexpected response to ${body.request} request`); + throw (error); + } + + /** + * Resumes the current recording session. + * + * @returns {Promise} + */ + async resume() { + const body = { + request: REQUEST_RESUME, + }; + + const response = await this.message(body); + const { event, data: evtdata } = this._getPluginEvent(response);; + if (event === PLUGIN_EVENT.RESUMED) + return evtdata; + const error = new Error(`unexpected response to ${body.request} request`); + throw (error); + } + + /** + * Stop the current recording or playback session. + * + * @returns {Promise} + */ + async stop() { + const body = { + request: REQUEST_STOP, + }; + + const response = await this.message(body); + const { event, data: evtdata } = this._getPluginEvent(response);; + if (event === PLUGIN_EVENT.STOPPED) + return evtdata; + const error = new Error(`unexpected response to ${body.request} request`); + throw (error); + } + +} + +/** + * The payload of the plugin message (cfr. Janus docs). + * {@link https://janus.conf.meetecho.com/docs/recordplay.html} + * + * @private + * @typedef {Object} RecordPlayData + */ + +/** + * The response event for recordplay recordings request. + * + * @typedef {Object} RECORDPLAY_EVENT_RECORDINGS_LIST + * @property {object[]} list - The list of the recordings as returned by Janus + */ + +/** + * The response event for recordplay update request. + * + * @typedef {Object} RECORDPLAY_EVENT_UPDATE_RESPONSE + */ + +/** + * The response event for record request. + * + * @typedef {Object} RECORDPLAY_EVENT_RECORDING + * @property {number} [id] - The involved recording identifier + * @property {boolean} [is_private] - True if the event mentions a private recording + * @property {RTCSessionDescription} [jsep] - Optional JSEP from Janus + */ + +/** + * The response event for configure request. + * + * @typedef {Object} RECORDPLAY_EVENT_CONFIGURED + * @property {object} [settings] - The current settings as returned by Janus + */ + +/** + * The response event for pause request. + * + * @typedef {Object} RECORDPLAY_EVENT_PAUSED + * @property {number} [id] - The involved recording identifier + */ + +/** + * The response event for resume request. + * + * @typedef {Object} RECORDPLAY_EVENT_RESUMED + * @property {number} [id] - The involved recording identifier + */ + +/** + * The response event for play request. + * + * @typedef {Object} RECORDPLAY_EVENT_PREPARING + * @property {number} [id] - The involved recording identifier + * @property {boolean} [is_private] - True if the event mentions a private recording + * @property {RTCSessionDescription} [jsep] - Optional JSEP from Janus + */ + +/** + * The response event for start request. + * + * @typedef {Object} RECORDPLAY_EVENT_PLAYING + * @property {number} [id] - The involved recording identifier + */ + +/** + * The response event for stop request. + * + * @typedef {Object} RECORDPLAY_EVENT_STOPPED + * @property {number} [id] - The involved recording identifier + * @property {boolean} [is_private] - True if the event mentions a private recording + */ + +/** + * A recordplay slow-link event. + * + * @typedef {Object} RECORDPLAY_EVENT_SLOW_LINK + * @property {string} [media] - Audio or video + * @property {number} [current-bitrate] - The current configured max video bitrate + * @property {boolean} [uplink] - Whether this is an uplink or downlink event + */ + +/** + * A recordplay done event. + * + * @typedef {Object} RECORDPLAY_EVENT_DONE + * @property {number} [id] - The involved recording identifier + * @property {boolean} [is_private] - True if the event mentions a private recording + */ + +/** + * The exported plugin descriptor. + * + * @type {Object} + * @property {string} id - The plugin identifier used when attaching to Janus + * @property {module:recordplay-plugin~RecordPlayHandle} Handle - The custom class implementing the plugin + * @property {Object} EVENT - The events emitted by the plugin + * @property {string} EVENT.RECORDPLAY_SLOW_LINK {@link module:recordplay-plugin~RecordPlayHandle#event:RECORDPLAY_STATUS RECORDPLAY_SLOW_LINK} + * @property {string} EVENT.RECORDPLAY_DONE {@link module:recordplay-plugin~RecordPlayHandle#event:RECORDPLAY_STATUS RECORDPLAY_DONE} + * @property {string} EVENT.RECORDPLAY_ERROR {@link module:recordplay-plugin~RecordPlayHandle#event:RECORDPLAY_ERROR RECORDPLAY_ERROR} + */ +export default { + id: PLUGIN_ID, + Handle: RecordPlayHandle, + + EVENT: { + /** + * Trouble on an active stream. + * + * @event module:recordplay-plugin~RecordPlayHandle#event:RECORDPLAY_SLOW_LINK + * @type {module:recordplay-plugin~RECORDPLAY_EVENT_SLOW_LINK} + */ + RECORDPLAY_SLOW_LINK: PLUGIN_EVENT.SLOW_LINK, + + /** + * A recording/playback session is over. + * + * @event module:recordplay-plugin~RecordPlayHandle#event:RECORDPLAY_DONE + * @type {module:recordplay-plugin~RECORDPLAY_EVENT_DONE} + */ + RECORDPLAY_DONE: PLUGIN_EVENT.DONE, + + /** + * Generic recordplay error. + * + * @event module:recordplay-plugin~RecordPlayHandle#event:RECORDPLAY_ERROR + * @type {Error} + */ + RECORDPLAY_ERROR: PLUGIN_EVENT.ERROR, + }, +}; diff --git a/src/plugins/sip-plugin.js b/src/plugins/sip-plugin.js new file mode 100644 index 0000000..2641a21 --- /dev/null +++ b/src/plugins/sip-plugin.js @@ -0,0 +1,612 @@ +'use strict'; + +/** + * This module contains the implementation of the SIP plugin (ref. {@link https://janus.conf.meetecho.com/docs/sip.html}). + * @module sip-plugin + */ + +import Handle from '../handle.js'; +import { JANODE } from '../protocol.js'; + +/* The plugin ID exported in the plugin descriptor */ +const PLUGIN_ID = 'janus.plugin.sip'; + +/* These are the requests defined for the Janus SIP plugin API */ +const REQUEST_REGISTER = 'register'; +const REQUEST_CALL = 'call'; +const REQUEST_ACCEPT = 'accept'; +const REQUEST_HANGUP = 'hangup'; +const REQUEST_DECLINE = 'decline'; + +/* These are the events/responses that the Janode plugin will manage */ +/* Some of them will be exported in the plugin descriptor */ +const PLUGIN_EVENT = { + REGISTERED: 'sip_registered', + REGISTERING: 'sip_registering', + CALLING: 'sip_calling', + RINGING: 'sip_ringing', + PROCEEDING: 'sip_proceeding', + INCOMING: 'sip_incoming', + HANGUP: 'sip_hangup', + HANGINGUP: 'sip_hangingup', + DECLINING: 'declining', + ACCEPTED: 'sip_accepted', + MISSED: 'sip_missed', + INFO: 'sip_info', + DTMF: 'sip_dtmf', + ERROR: 'sip_error', + ERROR_EVENT: 'sip_error_event', +}; + +/** + * The class implementing the SIP plugin (ref. {@link https://janus.conf.meetecho.com/docs/sip.html}).
+ * + * It extends the base Janode Handle class and overrides the base "handleMessage" method.
+ * + * Moreover it defines some methods to support SIP operations.
+ * + * @hideconstructor + * @extends module:handle~Handle + */ +class SipHandle extends Handle { + /** + * Create a Janode SIP handle. + * + * @param {module:session~Session} session - A reference to the parent session + * @param {number} id - The handle identifier + */ + constructor(session, id) { + super(session, id); + this._pendingRegister = null; + this._pendingCalls = {}; + + this.on(JANODE.EVENT.HANDLE_HANGUP, _ => { + this._pendingRegister = null; + this._pendingCalls = {}; + }); + this.on(JANODE.EVENT.HANDLE_DETACHED, _ => { + this._pendingRegister = null; + this._pendingCalls = {}; + }); + } + + /** + * The custom "handleMessage" needed for handling SIP plugin messages. + * + * @private + * @param {Object} janus_message + * @returns {Object} A falsy value for unhandled events, a truthy value for handled events + */ + handleMessage(janus_message) { + const { plugindata, transaction } = janus_message; + if (plugindata && plugindata.data && plugindata.data.sip) { + /** + * @type {SipData} + */ + const message_data = plugindata.data; + const { sip, result, call_id, error, error_code } = message_data; + + /* The event can not be recognized, return a falsy value */ + if (!error && sip !== 'event' && !result.event) + return null; + + /* Prepare an object for the output Janode event */ + const janode_event = this._newPluginEvent(janus_message); + + /* Add call id information if available */ + if (call_id) { + janode_event.data.call_id = call_id; + this._pendingCalls[call_id] = this._pendingCalls[call_id] || {}; + } + + /* Plugin messaging error (not related to SIP requests) */ + if (error) { + janode_event.event = PLUGIN_EVENT.ERROR; + janode_event.data = new Error(`${error_code} ${error}`); + /* In case of error, close a transaction */ + this.closeTransactionWithError(transaction, janode_event.data); + return janode_event; + } + + /* Emit the event to the application */ + let emit = false; + + /* Close the related janus transaction */ + const CLOSE_TX_NO = 0; + const CLOSE_TX_SUCCESS = 1; + const CLOSE_TX_ERROR = -1; + let closeTx = CLOSE_TX_NO; + let txId = transaction; + + switch (result.event) { + + /* Registering event */ + case 'registering': + janode_event.event = PLUGIN_EVENT.REGISTERING; + closeTx = CLOSE_TX_NO; + emit = true; + break; + + case 'registration_failed': + janode_event.event = PLUGIN_EVENT.ERROR_EVENT; + janode_event.data = new Error(`${result.code} ${result.reason}`); + closeTx = CLOSE_TX_ERROR; + txId = transaction || this._pendingRegister; + emit = false; + break; + + case 'registered': + janode_event.event = PLUGIN_EVENT.REGISTERED; + janode_event.data.username = result.username; + janode_event.data.register_sent = result.register_sent; + closeTx = CLOSE_TX_SUCCESS; + txId = transaction || this._pendingRegister; + emit = false; + break; + + case 'calling': + janode_event.event = PLUGIN_EVENT.CALLING; + closeTx = CLOSE_TX_NO; + emit = true; + break; + + case 'ringing': + janode_event.event = PLUGIN_EVENT.RINGING; + closeTx = CLOSE_TX_NO; + emit = true; + break; + + case 'proceeding': + janode_event.event = PLUGIN_EVENT.PROCEEDING; + closeTx = CLOSE_TX_NO; + emit = true; + break; + + /* Inbound call */ + case 'incomingcall': { + janode_event.event = PLUGIN_EVENT.INCOMING; + /* Store the incoming call URI */ + const call = this._pendingCalls[call_id]; + if (call) { + call.incoming = result.username; + } + janode_event.data.username = result.username; + janode_event.data.callee = result.callee; + janode_event.data.display_name = result.displayname || undefined; + closeTx = CLOSE_TX_NO; + emit = true; + break; + } + + case 'hangup': { + /* Is there is a pending call without a reply? */ + const call = this._pendingCalls[call_id]; + if (call && !call.accepted && !call.declined && !call.incoming) { + janode_event.event = PLUGIN_EVENT.ERROR_EVENT; + janode_event.data = new Error(`${result.code} ${result.reason}`); + closeTx = CLOSE_TX_ERROR; + } + /* Async hangup */ + else { + janode_event.event = PLUGIN_EVENT.HANGUP; + closeTx = CLOSE_TX_NO; + emit = true; + } + delete this._pendingCalls[call_id]; + break; + } + + case 'hangingup': + janode_event.event = PLUGIN_EVENT.HANGINGUP; + closeTx = CLOSE_TX_SUCCESS; + emit = false; + break; + + case 'declining': { + janode_event.event = PLUGIN_EVENT.DECLINING; + const call = this._pendingCalls[call_id]; + if (call) { + call.declined = true; + } + closeTx = CLOSE_TX_SUCCESS; + emit = false; + break; + } + + /* A call has been accepted */ + case 'accepted': { + janode_event.event = PLUGIN_EVENT.ACCEPTED; + janode_event.data.username = result.username || this._pendingCalls[call_id].incoming; + const call = this._pendingCalls[call_id]; + if (call) { + call.accepted = true; + } + closeTx = CLOSE_TX_SUCCESS; + emit = false; + break; + } + + /* Call has been missed */ + case 'missed_call': { + delete this._pendingCalls[call_id]; + janode_event.event = PLUGIN_EVENT.MISSED; + janode_event.data.callee = result.callee; + janode_event.data.caller = result.caller; + closeTx = CLOSE_TX_NO; + emit = true; + break; + } + + /* SIP INFO */ + case 'info': { + janode_event.event = PLUGIN_EVENT.INFO; + janode_event.data.sender = result.sender; + if (result.displayname) { + janode_event.data.displayname = result.displayname; + } + janode_event.data.type = result.type; + janode_event.data.content = result.content; + if (result.headers) { + janode_event.data.headers = result.headers; + } + emit = true; + break; + } + + /* RFC2833 DTMF */ + case 'dtmf': { + janode_event.event = PLUGIN_EVENT.DTMF; + janode_event.data.sender = result.sender; + janode_event.data.signal = result.signal; + janode_event.data.duration = result.duration; + emit = true; + break; + } + } + + /* The event has been handled */ + if (janode_event.event) { + if (closeTx === CLOSE_TX_SUCCESS) + this.closeTransactionWithSuccess(txId, janus_message); + if (closeTx === CLOSE_TX_ERROR) + this.closeTransactionWithError(txId, janode_event.data); + if (emit) + this.emit(janode_event.event, janode_event.data); + return janode_event; + } + } + + /* The event has not been handled, return a falsy value */ + return null; + } + + /** + * Register to the SIP plugin (sending of a SIP REGISTER is optional). + * + * @param {Object} params + * @param {string} [params.type] - optional SIP session type, either "guest" or "helper" + * @param {boolean} [params.send_register] - True to send a SIP register + * @param {boolean} [params.force_udp] - True to force UDP for the SIP messaging + * @param {boolean} [params.force_tcp] - True to force TCP for the SIP messaging + * @param {boolean} [params.sips] - True to configure a SIPS URI too when registering + * @param {boolean} [params.rfc2543_cancel] - True to configure sip client to CANCEL pending INVITEs without having received a provisional response + * @param {string} params.username - The SIP URI to register + * @param {string} [params.secret] - The password to use, if any + * @param {string} [params.ha1_secret] - The prehashed password to use, if any + * @param {string} [params.display_name] - The display name to use when sending SIP REGISTER + * @param {string} [params.proxy] - The server to register at (not needed for guests) + * @param {string} [params.outbound_proxy] - The server to register at (not needed for guests) + * @param {number} [params.register_ttl] - The number of seconds after which the registration should expire + * + * @returns {Promise} + */ + async register({ type, send_register, force_udp, force_tcp, sips, rfc2543_cancel, username, secret, ha1_secret, display_name, proxy, outbound_proxy, register_ttl }) { + const body = { + request: REQUEST_REGISTER, + username, + }; + + if (typeof type === 'string') body.type = type; + if (typeof send_register === 'boolean') body.send_register = send_register; + if (typeof force_udp === 'boolean') body.force_udp = force_udp; + if (typeof force_tcp === 'boolean') body.force_tcp = force_tcp; + if (typeof sips === 'boolean') body.sips = sips; + if (typeof rfc2543_cancel === 'boolean') body.rfc2543_cancel = rfc2543_cancel; + if (typeof secret === 'string') body.secret = secret; + if (typeof ha1_secret === 'string') body.ha1_secret = ha1_secret; + if (typeof display_name === 'string') body.display_name = display_name; + if (typeof proxy === 'string') body.proxy = proxy; + if (typeof outbound_proxy === 'string') body.outbound_proxy = outbound_proxy; + if (typeof register_ttl === 'number') body.register_ttl = register_ttl; + + const request = { + janus: 'message', + body, + }; + this.decorateRequest(request); + this._pendingRegister = request.transaction; + + const response = await this.sendRequest(request, 10000); + const { event, data: evtdata } = this._getPluginEvent(response); + if (event === PLUGIN_EVENT.REGISTERED) { + evtdata.username = username; + return evtdata; + } + const error = new Error(`unexpected response to ${body.request} request`); + throw (error); + } + + /** + * Start a SIP call. + * + * @param {Object} params + * @param {string} params.uri - The SIP URI to call + * @param {string} [params.call_id] - The user-defined value of Call-ID SIP header used in all SIP requests throughout the call + * @param {string} [params.authuser] - The username to use to authenticate as to call, only needed in case authentication is needed and no REGISTER was sent + * @param {string} [params.secret] - The password to use for authentication, if any + * @param {string} [params.ha1_secret] - The prehashed password to use for authentication, if any + * @param {RTCSessionDescription} params.jsep - JSEP offer + * @returns {Promise} + */ + async call({ uri, call_id, authuser, secret, ha1_secret, jsep }) { + if (typeof jsep === 'object' && jsep && jsep.type !== 'offer') { + const error = new Error('jsep must be an offer'); + return Promise.reject(error); + } + + const body = { + request: REQUEST_CALL, + uri, + }; + + if (typeof call_id === 'string') body.call_id = call_id; + if (typeof authuser === 'string') body.authuser = authuser; + if (typeof secret === 'string') body.secret = secret; + if (typeof ha1_secret === 'string') body.ha1_secret = ha1_secret; + + const request = { + janus: 'message', + body, + jsep, + }; + this.decorateRequest(request); + + const response = await this.sendRequest(request, 120000); + const { event, data: evtdata } = this._getPluginEvent(response); + if (event === PLUGIN_EVENT.ACCEPTED) + return evtdata; + const error = new Error(`unexpected response to ${body.request} request`); + throw (error); + } + + /** + * Accept an incoming SIP call. + * + * @param {Object} params + * @param {RTCSessionDescription} params.jsep - JSEP answer + * @returns {Promise} + */ + async accept({ jsep }) { + if (typeof jsep === 'object' && jsep && jsep.type !== 'answer') { + const error = new Error('jsep must be an answer'); + return Promise.reject(error); + } + const body = { + request: REQUEST_ACCEPT, + }; + + const request = { + janus: 'message', + body, + jsep, + }; + this.decorateRequest(request); + + const response = await this.sendRequest(request, 10000); + const { event, data: evtdata } = this._getPluginEvent(response); + if (event === PLUGIN_EVENT.ACCEPTED) + return evtdata; + const error = new Error(`unexpected response to ${body.request} request`); + throw (error); + } + + /** + * Hangup a SIP call. + * + * @returns {Promise} + */ + async sip_hangup() { + const body = { + request: REQUEST_HANGUP, + }; + + const request = { + janus: 'message', + body, + }; + this.decorateRequest(request); + + const response = await this.sendRequest(request, 10000); + const { event, data: evtdata } = this._getPluginEvent(response); + if (event === PLUGIN_EVENT.HANGINGUP) + return evtdata; + const error = new Error(`unexpected response to ${body.request} request`); + throw (error); + } + + /** + * Decline an incoming SIP call. + * + * @returns {Promise} + */ + async decline() { + const body = { + request: REQUEST_DECLINE, + }; + + const request = { + janus: 'message', + body, + }; + this.decorateRequest(request); + + const response = await this.sendRequest(request, 10000); + const { event, data: evtdata } = this._getPluginEvent(response); + if (event === PLUGIN_EVENT.DECLINING) + return evtdata; + const error = new Error(`unexpected response to ${body.request} request`); + throw (error); + } +} + +/** + * The payload of the plugin message (cfr. Janus docs). + * {@link https://janus.conf.meetecho.com/docs/sip.html} + * + * @private + * @typedef {Object} SipData + */ + +/** + * The success event for a register request + * + * @typedef {Object} SIP_EVENT_REGISTERED + * @property {string} username - The URI that has been registered + * @property {boolean} register_sent - True is a REGISTER has been sent + */ + +/** + * The success event for an accept request + * + * @typedef {Object} SIP_EVENT_ACCEPTED + * @property {string} call_id + * @property {string} username + * @property {RTCSessionDescription} [jsep] + */ + +/** + * The success event for an hangup request + * + * @typedef {Object} SIP_EVENT_HANGINGUP + * @property {string} call_id + */ + +/** + * The success event for a decline request + * + * @typedef {Object} SIP_EVENT_DECLINING + * @property {string} call_id + */ + +/** + * The exported plugin descriptor. + * + * @type {Object} + * @property {string} id - The plugin identifier used when attaching to Janus + * @property {module:sip-plugin~SipHandle} Handle - The custom class implementing the plugin + * @property {Object} EVENT - The events emitted by the plugin + * @property {string} EVENT.SIP_REGISTERING {@link module:sip-plugin~SipHandle#event:SIP_REGISTERING SIP_REGISTERING} + * @property {string} EVENT.SIP_CALLING {@link module:sip-plugin~SipHandle#event:SIP_CALLING SIP_CALLING} + * @property {string} EVENT.SIP_RINGING {@link module:sip-plugin~SipHandle#event:SIP_RINGING SIP_RINGING} + * @property {string} EVENT.SIP_PROCEEDING {@link module:sip-plugin~SipHandle#event:SIP_PROCEEDING SIP_PROCEEDING} + * @property {string} EVENT.SIP_INCOMING {@link module:sip-plugin~SipHandle#event:SIP_INCOMING SIP_INCOMING} + * @property {string} EVENT.SIP_HANGUP {@link module:sip-plugin~SipHandle#event:SIP_HANGUP SIP_HANGUP} + * @property {string} EVENT.SIP_MISSED {@link module:sip-plugin~SipHandle#event:SIP_MISSED SIP_MISSED} + * @property {string} EVENT.SIP_INFO {@link module:sip-plugin~SipHandle#event:SIP_INFO SIP_INFO} + * @property {string} EVENT.SIP_DTMF {@link module:sip-plugin~SipHandle#event:SIP_DTMF SIP_DTMF} + */ +export default { + id: PLUGIN_ID, + Handle: SipHandle, + EVENT: { + /** + * The event notifying a register is in progress + * + * @event module:sip-plugin~SipHandle#event:SIP_REGISTERING + * @type {Object} + */ + SIP_REGISTERING: PLUGIN_EVENT.REGISTERING, + + /** + * Event for a SIP call in progress + * + * @event module:sip-plugin~SipHandle#event:SIP_CALLING + * @type {Object} + * @property {string} call_id - SIP Call-ID header for related call + */ + SIP_CALLING: PLUGIN_EVENT.CALLING, + + /** + * Event for a SIP call ringing + * + * @event module:sip-plugin~SipHandle#event:SIP_RINGING + * @type {Object} + * @property {string} call_id - SIP Call-ID header for related call + */ + SIP_RINGING: PLUGIN_EVENT.RINGING, + + /** + * Event for a SIP call proceeding + * + * @event module:sip-plugin~SipHandle#event:SIP_PROCEEDING + * @type {Object} + * @property {string} call_id - SIP Call-ID header for related call + */ + SIP_PROCEEDING: PLUGIN_EVENT.PROCEEDING, + + /** + * Event for an incoming SIP call + * + * @event module:sip-plugin~SipHandle#event:SIP_INCOMING + * @type {Object} + * @property {string} call_id - SIP Call-ID header for related call + * @property {string} callee - SIP URI that was called + * @property {string} display_name - Display name of the caller + * @property {string} username - SIP URI of the caller + * @property {RTCSessionDescription} [jsep] - The JSEP offer + */ + SIP_INCOMING: PLUGIN_EVENT.INCOMING, + + /** + * Event for a SIP call hangup + * + * @event module:sip-plugin~SipHandle#event:SIP_HANGUP + * @type {Object} + * @property {string} call_id - SIP Call-ID header for related call + */ + SIP_HANGUP: PLUGIN_EVENT.HANGUP, + + /** + * Event for a missed SIP call + * + * @event module:sip-plugin~SipHandle#event:SIP_MISSED + * @type {Object} + * @property {string} call_id - SIP Call-ID header for related call + * @property {string} callee - SIP URI that was called + * @property {string} caller - SIP URI of the caller + */ + SIP_MISSED: PLUGIN_EVENT.MISSED, + + /** + * @event module:sip-plugin~SipHandle#event:SIP_INFO + * @type {Object} + * @property {string} sender - SIP URI of the message sender + * @property {string} [call_id] - SIP Call-ID header for related call + * @property {string} [displayname] - Display name of the sender + * @property {string} type - Content type of the message + * @property {string} content - Content of the message + * @property {Object} [headers] - Custom headers extracted from SIP event + */ + SIP_INFO: PLUGIN_EVENT.INFO, + + /** + * @event module:sip-plugin~SipHandle#event:SIP_DTMF + * @type {Object} + * @property {string} sender - SIP URI of the DTMF sender + * @property {string} [call_id] - SIP Call-ID header for related call + * @property {string} signal - The DTMF tone signal + * @property {number} duration - The DTMF tone duration + */ + SIP_DTMF: PLUGIN_EVENT.DTMF, + }, +}; \ No newline at end of file diff --git a/src/plugins/streaming-plugin.js b/src/plugins/streaming-plugin.js index f90dfe4..d2af62c 100644 --- a/src/plugins/streaming-plugin.js +++ b/src/plugins/streaming-plugin.js @@ -50,6 +50,7 @@ const PLUGIN_EVENT = { * Moreover it defines many methods to support Streaming operations.
* * @hideconstructor + * @extends module:handle~Handle */ class StreamingHandle extends Handle { /** @@ -72,11 +73,11 @@ class StreamingHandle extends Handle { * The custom "handleMessage" needed for handling Streamiing messages. * * @private - * @param {object} janus_message - * @returns {object} A falsy value for unhandled events, a truthy value for handled events + * @param {Object} janus_message + * @returns {Object} A falsy value for unhandled events, a truthy value for handled events */ handleMessage(janus_message) { - const { plugindata, jsep, transaction } = janus_message; + const { plugindata, transaction } = janus_message; if (plugindata && plugindata.data && plugindata.data.streaming) { /** * @type {StreamingData} @@ -85,23 +86,12 @@ class StreamingHandle extends Handle { const { streaming, error, error_code } = message_data; /* Prepare an object for the output Janode event */ - const janode_event = { - /* The name of the resolved event */ - event: null, - /* The event payload */ - data: {}, - }; - - /* Add JSEP data if available */ - if (jsep) janode_event.data.jsep = jsep; + const janode_event = this._newPluginEvent(janus_message); /* The plugin will emit an event only if the handle does not own the transaction */ /* That means that a transaction has already been closed or this is an async event */ const emit = (this.ownsTransaction(transaction) === false); - /* Use the "janode" property to store the output event */ - janus_message._janode = janode_event; - switch (streaming) { /* Ok is a success response */ @@ -131,6 +121,8 @@ class StreamingHandle extends Handle { janode_event.data.audio_port = (message_data.stream) ? message_data.stream.audio_port : null; janode_event.data.audio_rtcp_port = (message_data.stream) ? message_data.stream.audio_rtcp_port : null; janode_event.data.video_port = (message_data.stream) ? message_data.stream.video_port : null; + janode_event.data.video_port_2 = (message_data.stream) ? message_data.stream.video_port_2 : null; + janode_event.data.video_port_3 = (message_data.stream) ? message_data.stream.video_port_3 : null; janode_event.data.video_rtcp_port = (message_data.stream) ? message_data.stream.video_rtcp_port : null; janode_event.data.data_port = (message_data.stream) ? message_data.stream.data_port : null; break; @@ -211,16 +203,22 @@ class StreamingHandle extends Handle { /** * Subscribe to a mountpoint. * - * @param {object} params + * @param {Object} params * @param {number|string} params.id - The mp id * @param {string} [params.pin] - The optional mp pin * @param {boolean} [params.audio] - True to request audio * @param {boolean} [params.video] - True to request video * @param {boolean} [params.data] - True to request data + * @param {RTCSessionDescription} [params.jsep=null] - JSEP offer + * @param {boolean} [params.e2ee] - True if end-to-end enecryption is used * @param {boolean} [params.restart=false] - True to trigger a restart * @returns {Promise} */ - async watch({ id, pin, audio, video, data, restart = false }) { + async watch({ id, pin, audio, video, data, jsep = null, e2ee, restart = false }) { + if (typeof jsep === 'object' && jsep && jsep.type !== 'offer') { + const error = new Error('jsep must be an offer'); + return Promise.reject(error); + } const body = { request: REQUEST_WATCH, id, @@ -230,10 +228,12 @@ class StreamingHandle extends Handle { if (typeof video === 'boolean') body.offer_video = video; if (typeof data === 'boolean') body.offer_data = data; if (typeof restart === 'boolean') body.restart = restart; + if (jsep) + jsep.e2ee = (typeof e2ee === 'boolean') ? e2ee : jsep.e2ee; - const response = await this.message(body); - const { event, data: evtdata } = response._janode || {}; - if (event === PLUGIN_EVENT.STATUS && (evtdata.status === 'preparing' || evtdata.status === 'updating')) { + const response = await this.message(body, jsep); + const { event, data: evtdata } = this._getPluginEvent(response); + if (event === PLUGIN_EVENT.STATUS && (evtdata.status === 'preparing' || evtdata.status === 'updating' || (jsep && evtdata.status === 'starting'))) { /* Set current mp to subscribed id */ this.mp = id; return evtdata; @@ -245,11 +245,12 @@ class StreamingHandle extends Handle { /** * Start a mountpoint stream. * - * @param {object} params - * @property {RTCSessionDescription} params.jsep + * @param {Object} params + * @param {RTCSessionDescription} params.jsep + * @param {boolean} [params.e2ee] * @returns {Promise} */ - async start({ jsep }) { + async start({ jsep, e2ee }) { if (typeof jsep === 'object' && jsep && jsep.type !== 'answer') { const error = new Error('jsep must be an answer'); return Promise.reject(error); @@ -258,9 +259,10 @@ class StreamingHandle extends Handle { const body = { request: REQUEST_START, }; + jsep.e2ee = (typeof e2ee === 'boolean') ? e2ee : jsep.e2ee; const response = await this.message(body, jsep); - const { event, data: evtdata } = response._janode || {}; + const { event, data: evtdata } = this._getPluginEvent(response);; if (event === PLUGIN_EVENT.STATUS && (evtdata.status === 'starting' || evtdata.status === 'started')) return evtdata; const error = new Error(`unexpected response to ${body.request} request`); @@ -278,7 +280,7 @@ class StreamingHandle extends Handle { }; const response = await this.message(body); - const { event, data: evtdata } = response._janode || {}; + const { event, data: evtdata } = this._getPluginEvent(response);; if (event === PLUGIN_EVENT.STATUS && evtdata.status === 'pausing') return evtdata; const error = new Error(`unexpected response to ${body.request} request`); @@ -296,7 +298,7 @@ class StreamingHandle extends Handle { }; const response = await this.message(body); - const { event, data: evtdata } = response._janode || {}; + const { event, data: evtdata } = this._getPluginEvent(response);; if (event === PLUGIN_EVENT.STATUS && evtdata.status === 'stopping') return evtdata; const error = new Error(`unexpected response to ${body.request} request`); @@ -306,7 +308,7 @@ class StreamingHandle extends Handle { /** * Switch to another mountpoint. * - * @param {object} params + * @param {Object} params * @param {number|string} params.id - The mp id to switch to * @returns {Promise} */ @@ -317,7 +319,7 @@ class StreamingHandle extends Handle { }; const response = await this.message(body); - const { event, data: evtdata } = response._janode || {}; + const { event, data: evtdata } = this._getPluginEvent(response);; if (event === PLUGIN_EVENT.SWITCHED && evtdata.switched === 'ok') { /* Set current mp to the switched id */ this.mp = evtdata.id; @@ -330,7 +332,7 @@ class StreamingHandle extends Handle { /** * Configure an active stream. * - * @param {object} params + * @param {Object} params * @param {boolean} [params.audio] - Enable/disable audio * @param {boolean} [params.video] - Enable/disable video * @param {boolean} [params.data] - Enable/disable data @@ -355,7 +357,7 @@ class StreamingHandle extends Handle { if (typeof temporal_layer === 'number') body.temporal_layer = temporal_layer; const response = await this.message(body); - const { event, data: evtdata } = response._janode || {}; + const { event, data: evtdata } = this._getPluginEvent(response); if (event === PLUGIN_EVENT.CONFIGURED) return evtdata; const error = new Error(`unexpected response to ${body.request} request`); @@ -371,15 +373,18 @@ class StreamingHandle extends Handle { /** * List all the available mountpoints. * + * @param {Object} params + * @param {string} [params.admin_key] - The admin key needed for invoking the API * @returns {Promise} */ - async list() { + async list({ admin_key } = {}) { const body = { request: REQUEST_LIST, }; + if (typeof admin_key === 'string') body.admin_key = admin_key; const response = await this.message(body); - const { event, data: evtdata } = response._janode || {}; + const { event, data: evtdata } = this._getPluginEvent(response); if (event === PLUGIN_EVENT.LIST) return evtdata; const error = new Error(`unexpected response to ${body.request} request`); @@ -389,7 +394,7 @@ class StreamingHandle extends Handle { /** * Get mountpoint info. * - * @param {object} params + * @param {Object} params * @param {number|string} params.id * @param {string} [params.secret] * @returns {Promise} @@ -402,7 +407,7 @@ class StreamingHandle extends Handle { if (typeof secret === 'string') body.secret = '' + secret; const response = await this.message(body); - const { event, data: evtdata } = response._janode || {}; + const { event, data: evtdata } = this._getPluginEvent(response); if (event === PLUGIN_EVENT.INFO) return evtdata; const error = new Error(`unexpected response to ${body.request} request`); @@ -412,27 +417,34 @@ class StreamingHandle extends Handle { /** * Start recording on a mountpoint * - * @param {object} params + * @param {Object} params * @param {number|string} params.id * @param {string} [params.audio] - The filename for audio * @param {string} [params.video] - The filename for video * @param {string} [params.data] - The filename for data + * @param {object[]} [params.media] - [multistream] The media object, each media includes mid, filename * @param {string} [params.secret] * @returns {Promise} */ - async startRecording({ id, audio, video, data, secret }) { + async startRecording({ id, audio, video, data, media, secret }) { const body = { request: REQUEST_RECORDING, action: ACTION_START_REC, id, }; - if (audio) body.audio = audio; - if (video) body.video = video; - if (data) body.data = data; + /* [multistream] */ + if (media && Array.isArray(media)) { + body.media = media; + } + else { + if (audio) body.audio = audio; + if (video) body.video = video; + if (data) body.data = data; + } if (typeof secret === 'string') body.secret = '' + secret; const response = await this.message(body); - const { event, data: evtdata } = response._janode || {}; + const { event, data: evtdata } = this._getPluginEvent(response); if (event === PLUGIN_EVENT.OK) return evtdata; const error = new Error(`unexpected response to ${body.request} request`); @@ -442,27 +454,34 @@ class StreamingHandle extends Handle { /** * Stop recording on a mountpoint. * - * @param {object} params + * @param {Object} params * @param {number|string} params.id * @param {boolean} [params.audio=true] - True to stop recording of audio * @param {boolean} [params.video=true] - True to stop recording of video * @param {boolean} [params.data=true] - True to stop recording of data + * @param {object[]} [params.media] - [multistream] The media object, each media includes mid * @param {string} [params.secret] * @returns {Promise} */ - async stopRecording({ id, audio = true, video = true, data = true, secret }) { + async stopRecording({ id, audio = true, video = true, data = true, media, secret }) { const body = { request: REQUEST_RECORDING, action: ACTION_STOP_REC, id, - audio, - video, - data, }; + /* [multistream] */ + if (media && Array.isArray(media)) { + body.media = media; + } + else { + body.audio = audio; + body.video = video; + body.data = data; + } if (typeof secret === 'string') body.secret = '' + secret; const response = await this.message(body); - const { event, data: evtdata } = response._janode || {}; + const { event, data: evtdata } = this._getPluginEvent(response); if (event === PLUGIN_EVENT.OK) return evtdata; const error = new Error(`unexpected response to ${body.request} request`); @@ -472,7 +491,7 @@ class StreamingHandle extends Handle { /** * Enable a mountpoint. * - * @param {object} params + * @param {Object} params * @param {number|string} params.id * @param {string} [params.secret] * @returns {Promise} @@ -485,7 +504,7 @@ class StreamingHandle extends Handle { if (typeof secret === 'string') body.secret = secret; const response = await this.message(body); - const { event, data: evtdata } = response._janode || {}; + const { event, data: evtdata } = this._getPluginEvent(response); if (event === PLUGIN_EVENT.OK) return evtdata; const error = new Error(`unexpected response to ${body.request} request`); @@ -495,9 +514,9 @@ class StreamingHandle extends Handle { /** * Disable a mountpoint. * - * @param {object} params + * @param {Object} params * @param {number|string} params.id - * @param {boolean} [stop_recording=true] - True if the user wants to also stop the recording of a disabled moutnpoint + * @param {boolean} [params.stop_recording=true] - True if the user wants to also stop the recording of a disabled moutnpoint * @param {string} [params.secret] * @returns {Promise} */ @@ -510,7 +529,7 @@ class StreamingHandle extends Handle { if (typeof secret === 'string') body.secret = secret; const response = await this.message(body); - const { event, data: evtdata } = response._janode || {}; + const { event, data: evtdata } = this._getPluginEvent(response); if (event === PLUGIN_EVENT.OK) return evtdata; const error = new Error(`unexpected response to ${body.request} request`); @@ -520,22 +539,27 @@ class StreamingHandle extends Handle { /** * Create a RTP live mountpoint. * - * @param {object} params + * @param {Object} params * @param {number|string} [params.id=0] - The id for the new mountpoint (if omitted Janus will pick one) + * @param {string} [params.name] - A name for the mp * @param {string} [params.description] - A description for the mp * @param {string} [params.secret] - The secret that'll be needed to edit this mountpoint * @param {string} [params.pin] - The pin that'll be needed to connect to the new mountpoint + * @param {string} [params.admin_key] - The admin key needed for invoking the API * @param {boolean} [params.permanent=false] - True if Janus must persist the mp on a config file * @param {boolean} [params.is_private=false] - Flag the mp as private - * @param {object} [params.audio] - The audio descriptor for the mp + * @param {boolean} [params.e2ee=false] - True to set a a mp as end to end encrypted + * @param {Object} [params.audio] - The audio descriptor for the mp * @param {number} [params.audio.port] - Port used for audio RTP * @param {number} [params.audio.rtcpport] - Port used for audio RTCP * @param {string} [params.audio.mcast] - Multicast address to listen to * @param {number} [params.audio.pt] - Payload type that will be used * @param {string} [params.audio.rtpmap] - rtpmap type that will be used * @param {boolean} [params.audio.skew] - Set skew compensation - * @param {object} [params.video] - The video descriptor for the mp + * @param {Object} [params.video] - The video descriptor for the mp * @param {number} [params.video.port] - Port used for video RTP + * @param {number} [params.video.port2] - Port used for video RTP (simulcast layer) + * @param {number} [params.video.port3] - Port used for video RTP (simulcast layer) * @param {number} [params.video.rtcpport] - Port used for video RTCP * @param {string} [params.video.mcast] - Multicast address to listen to * @param {number} [params.video.pt] - Payload type that will be used @@ -543,59 +567,75 @@ class StreamingHandle extends Handle { * @param {boolean} [params.video.skew] - Set skew compensation * @param {string} [params.video.fmtp] - fmtp that will be used * @param {boolean} [params.video.buffer] - Enable buffering of the keyframes - * @param {object} [params.data] - The datachannel descriptor for the mp + * @param {Object} [params.data] - The datachannel descriptor for the mp * @param {number} [params.data.port] - Port used for datachannels packets * @param {boolean} [params.data.buffer] - Enable buffering of the datachannels + * @param {object[]} [params.media] - [multistream] The media object, each media includes type, mid, port, pt ... * @param {number} [params.threads] - The number of helper threads used in this mp - * @param {object} [params.metadata] - An opaque metadata to add to the mp + * @param {Object} [params.metadata] - An opaque metadata to add to the mp + * @param {number} [params.collision] - The stream collision discarding time in number of milliseconds (0=disabled) * @returns {Promise} */ - async createRtpMountpoint({ id = 0, name, description, secret, pin, permanent = false, is_private = false, audio, video, data, threads, metadata }) { + async createRtpMountpoint({ id = 0, name, description, secret, pin, admin_key, permanent = false, is_private = false, e2ee = false, audio, video, data, media, threads, metadata, collision }) { const body = { request: REQUEST_CREATE, type: 'rtp', id, permanent, is_private, - audio: false, - video: false, - data: false, + e2ee, collision: 2000, }; if (typeof name === 'string') body.name = name; if (typeof description === 'string') body.description = description; if (typeof secret === 'string') body.secret = secret; if (typeof pin === 'string') body.pin = pin; - if (typeof audio === 'object' && audio) { - body.audio = true; - if (typeof audio.port === 'number') body.audioport = audio.port; - if (typeof audio.rtcpport === 'number') body.audiortcpport = audio.rtcppport; - if (typeof audio.mcast === 'string') body.audiomcast = audio.mcast; - if (audio.pt) body.audiopt = audio.pt; - if (audio.rtpmap) body.audiortpmap = audio.rtpmap; - if (audio.skew) body.audioskew = audio.skew || false; + if (typeof admin_key === 'string') body.admin_key = admin_key; + /* [multistream] */ + if (media && Array.isArray(media)) { + body.media = media; } - if (typeof video === 'object' && video) { - body.video = true; - if (typeof video.port === 'number') body.videoport = video.port; - if (typeof video.rtcpport === 'number') body.videortcpport = video.rtcpport; - if (typeof video.mcast === 'string') body.videomcast = video.mcast; - if (video.pt) body.videopt = video.pt; - if (video.rtpmap) body.videortpmap = video.rtpmap; - if (video.fmtp) body.videofmtp = video.fmtp; - if (video.buffer) body.videobufferkf = video.buffer || false; - if (video.skew) body.videoskew = video.skew || false; - } - if (typeof data === 'object' && data) { - body.data = true; - body.dataport = data.port || 0; - if (data.buffer) body.databuffermsg = data.buffer || false; + else { + body.audio = false; + body.video = false; + body.data = false; + if (typeof audio === 'object' && audio) { + body.audio = true; + body.audioport = (typeof audio.port === 'number') ? audio.port : 0; + if (typeof audio.rtcpport === 'number') body.audiortcpport = audio.rtcppport; + if (typeof audio.mcast === 'string') body.audiomcast = audio.mcast; + if (audio.pt) body.audiopt = audio.pt; + if (audio.rtpmap) body.audiortpmap = audio.rtpmap; + if (typeof audio.skew === 'boolean') body.audioskew = audio.skew; + } + if (typeof video === 'object' && video) { + body.video = true; + body.videoport = (typeof video.port === 'number') ? video.port : 0; + if (typeof video.rtcpport === 'number') body.videortcpport = video.rtcpport; + if (typeof video.mcast === 'string') body.videomcast = video.mcast; + if (video.pt) body.videopt = video.pt; + if (video.rtpmap) body.videortpmap = video.rtpmap; + if (video.fmtp) body.videofmtp = video.fmtp; + if (typeof video.buffer === 'boolean') body.videobufferkf = video.buffer; + if (typeof video.skew === 'boolean') body.videoskew = video.skew; + if (typeof video.port2 === 'number' && typeof video.port3 === 'number') { + body.videosimulcast = true; + body.videoport2 = video.port2; + body.videoport3 = video.port3; + } + } + if (typeof data === 'object' && data) { + body.data = true; + body.dataport = (typeof data.port === 'number') ? data.port : 0; + if (typeof data.buffer === 'boolean') body.databuffermsg = data.buffer; + } } if (typeof threads === 'number' && threads > 0) body.threads = threads; if (metadata) body.metadata = metadata; + if (typeof collision === 'number') body.collision = collision; const response = await this.message(body); - const { event, data: evtdata } = response._janode || {}; + const { event, data: evtdata } = this._getPluginEvent(response); if (event === PLUGIN_EVENT.CREATED) return evtdata; const error = new Error(`unexpected response to ${body.request} request`); @@ -605,20 +645,22 @@ class StreamingHandle extends Handle { /** * Destroy a mountpoint. * - * @param {object} params + * @param {Object} params * @param {number|string} params.id + * @param {boolean} [params.permanent] * @param {string} [params.secret] * @returns {Promise} */ - async destroyMountpoint({ id, secret }) { + async destroyMountpoint({ id, permanent, secret }) { const body = { request: REQUEST_DESTROY, id, }; + if (typeof permanent === 'boolean') body.permanent = permanent; if (typeof secret === 'string') body.secret = secret; const response = await this.message(body); - const { event, data: evtdata } = response._janode || {}; + const { event, data: evtdata } = this._getPluginEvent(response); if (event === PLUGIN_EVENT.DESTROYED) return evtdata; const error = new Error(`unexpected response to ${body.request} request`); @@ -632,63 +674,75 @@ class StreamingHandle extends Handle { * {@link https://janus.conf.meetecho.com/docs/streaming.html} * * @private - * @typedef {object} StreamingData + * @typedef {Object} StreamingData */ /** * Success response for streaming requests. * - * @typedef {object} STREAMING_EVENT_OK + * @typedef {Object} STREAMING_EVENT_OK */ /** * Response event for mountpoint info request. * - * @typedef {object} STREAMING_EVENT_INFO + * @typedef {Object} STREAMING_EVENT_INFO */ /** * Response event for mountpoint list request. * - * @typedef {object} STREAMING_EVENT_LIST + * @typedef {Object} STREAMING_EVENT_LIST * @property {object[]} list - The list of mountpoints as returned by Janus */ /** * Response event for mountpoint create request. * - * @typedef {object} STREAMING_EVENT_CREATED + * @typedef {Object} STREAMING_EVENT_CREATED * @property {string} name - The name of the mountpoint * @property {number|string} id - The identifier for the mountpoint * @property {string} description - An optional description * @property {number} [audio_port] - The port for RTP audio * @property {number} [audio_rtcp_port] - The port RTCP audio * @property {number} [video_port] - The port for RTP video + * @property {number} [video_port_2] - The port for RTP video (simulcast) + * @property {number} [video_port_3] - The port for RTP video (simulcast) * @property {number} [video_rtcp_port] - The port for RTCP video * @property {number} [data_port] - The port for datachannels + * @property {object[]} [ports] - [multistream] The ports used by the RTP mountpoint + * @property {string} ports.type - The type of the port (audio, video, data) + * @property {string} ports.mid - The unique mid of the stream + * @property {string} [ports.msid] - The msid of the stream + * @property {string} [ports.host] - The address in use for this stream + * @property {number} [ports.port] - The port on which the plugin listens for this stream's media + * @property {number} [ports.rtcp_port] - The local port for receiving and sending RTCP feedback + * @property {number} [ports.port_2] - The second local port for receiving video frames (only for RTP video, and simulcasting) + * @property {number} [ports.port_3] - The third local port for receiving video frames (only for RTP video, and simulcasting) */ /** * Response event for mountpoint destroy request. * - * @typedef {object} STREAMING_EVENT_DESTROYED + * @typedef {Object} STREAMING_EVENT_DESTROYED * @property {number|string} id - The identifier of the dstroyed mountpoint */ /** * A streaming status update event. * - * @typedef {object} STREAMING_EVENT_STATUS + * @typedef {Object} STREAMING_EVENT_STATUS * @property {string} status - The current status of the stream * @property {number|string} [id] - The involved mountpoint identifier - * @property {boolean} [restart] - True if the request had it true - * @property {RTCSessionDescription} [jsep] - Optional JSEP offer from Janus + * @property {boolean} [restart] - True if the event involves an ICE Restart + * @property {boolean} [e2ee] - True if an offered stream is end to end encrypted + * @property {RTCSessionDescription} [jsep] - Optional JSEP from Janus */ /** * Response event for mountpoint switch request. * - * @typedef {object} STREAMING_EVENT_SWITCHED + * @typedef {Object} STREAMING_EVENT_SWITCHED * @property {string} switched - The string as returned by Janus * @property {number|string} id - The identifier of the mp that has been switched to */ @@ -696,18 +750,18 @@ class StreamingHandle extends Handle { /** * Response event for configure stream request * - * @typedef {object} STREAMING_EVENT_CONFIGURED + * @typedef {Object} STREAMING_EVENT_CONFIGURED */ /** * The exported plugin descriptor. * - * @type {object} + * @type {Object} * @property {string} id - The plugin identifier used when attaching to Janus * @property {module:streaming-plugin~StreamingHandle} Handle - The custom class implementing the plugin - * @property {object} EVENT - The events emitted by the plugin - * @property {string} EVENT.STREAMING_STATUS {@link module:streaming-plugin~STREAMING_STATUS} - * @property {string} EVENT.STREAMING_ERROR {@link module:streaming-plugin~STREAMING_ERROR} + * @property {Object} EVENT - The events emitted by the plugin + * @property {string} EVENT.STREAMING_STATUS {@link module:streaming-plugin~StreamingHandle#event:STREAMING_STATUS STREAMING_STATUS} + * @property {string} EVENT.STREAMING_ERROR {@link module:streaming-plugin~StreamingHandle#event:STREAMING_ERROR STREAMING_ERROR} */ export default { id: PLUGIN_ID, @@ -717,7 +771,12 @@ export default { * Update of the status for the active stream. * * @event module:streaming-plugin~StreamingHandle#event:STREAMING_STATUS - * @type {module:streaming-plugin~STREAMING_EVENT_STATUS} + * @type {Object} + * @property {string} status - The current status of the stream + * @property {number|string} [id] - The involved mountpoint identifier + * @property {boolean} [restart] - True if the event involves an ICE Restart + * @property {boolean} [e2ee] - True if an offered stream is end to end encrypted + * @property {RTCSessionDescription} [jsep] - Optional JSEP from Janus */ STREAMING_STATUS: PLUGIN_EVENT.STATUS, @@ -729,4 +788,4 @@ export default { */ STREAMING_ERROR: PLUGIN_EVENT.ERROR, }, -}; \ No newline at end of file +}; diff --git a/src/plugins/textroom-plugin.js b/src/plugins/textroom-plugin.js new file mode 100644 index 0000000..6cb30d1 --- /dev/null +++ b/src/plugins/textroom-plugin.js @@ -0,0 +1,531 @@ +'use strict'; + +/** + * This module contains the implementation of the TextRoom plugin (ref. {@link https://janus.conf.meetecho.com/docs/textroom.html}). + * Notice this only covers what's possible via the Janus API: messages only sent via datachannels are not covered by this module. + * @module textroom-plugin + */ + +import Handle from '../handle.js'; + +/* The plugin ID exported in the plugin descriptor */ +const PLUGIN_ID = 'janus.plugin.textroom'; + +/* These are the requests defined for the Janus TextRoom API */ +const REQUEST_SETUP = 'setup'; +const REQUEST_ACK = 'ack'; +const REQUEST_RESTART = 'restart'; +const REQUEST_LIST_ROOMS = 'list'; +const REQUEST_LIST_PARTICIPANTS = 'listparticipants'; +const REQUEST_EXISTS = 'exists'; +const REQUEST_CREATE = 'create'; +const REQUEST_ALLOW = 'allowed'; +const REQUEST_ANNOUNCEMENT = 'announcement'; +const REQUEST_KICK = 'kick'; +const REQUEST_DESTROY = 'destroy'; + +/* These are the events/responses that the Janode plugin will manage */ +/* Some of them will be exported in the plugin descriptor */ +const PLUGIN_EVENT = { + ROOMS_LIST: 'textroom_list', + PARTICIPANTS_LIST: 'textroom_participants_list', + EXISTS: 'textroom_exists', + CREATED: 'textroom_created', + DESTROYED: 'textroom_destroyed', + SUCCESS: 'textroom_success', + ERROR: 'textroom_error', +}; + +/** + * The class implementing the TextRoom plugin (ref. {@link https://janus.conf.meetecho.com/docs/textroom.html}). + * Notice this only covers what's possible via the Janus API: messages only sent via datachannels are not covered by this module.
+ * + * It extends the base Janode Handle class and overrides the base "handleMessage" method.
+ * + * Moreover it defines many methods to support TextRoom operations. + * + * @hideconstructor + * @extends module:handle~Handle + */ +class TextRoomHandle extends Handle { + /** + * Create a Janode TextRoom handle. + * + * @param {module:session~Session} session - A reference to the parent session + * @param {number} id - The handle identifier + */ + constructor(session, id) { + super(session, id); + } + + /** + * The custom "handleMessage" needed for handling TextRoom messages. + * + * @private + * @param {Object} janus_message + * @returns {Object} A falsy value for unhandled events, a truthy value for handled events + */ + handleMessage(janus_message) { + const { plugindata, transaction } = janus_message; + if (plugindata && plugindata.data && plugindata.data.textroom) { + /** + * @type {TextRoomData} + */ + const message_data = plugindata.data; + const { textroom, error, error_code, room } = message_data; + + /* Prepare an object for the output Janode event */ + const janode_event = this._newPluginEvent(janus_message); + + /* Add room information if available */ + if (room) janode_event.data.room = room; + + /* The plugin will emit an event only if the handle does not own the transaction */ + /* That means that a transaction has already been closed or this is an async event */ + const emit = (this.ownsTransaction(transaction) === false); + + switch (textroom) { + + /* success response */ + case 'success': + /* Room exists API */ + if (typeof message_data.exists !== 'undefined') { + janode_event.data.exists = message_data.exists; + janode_event.event = PLUGIN_EVENT.EXISTS; + break; + } + /* Room list API */ + if (typeof message_data.list !== 'undefined') { + janode_event.data.list = message_data.list; + janode_event.event = PLUGIN_EVENT.ROOMS_LIST; + break; + } + /* Participants list API */ + if (typeof message_data.participants !== 'undefined') { + janode_event.data.participants = message_data.participants; + janode_event.event = PLUGIN_EVENT.PARTICIPANTS_LIST; + break; + } + + /* Generic success (might be token disable) */ + if (typeof message_data.allowed !== 'undefined') { + janode_event.data.list = message_data.allowed; + } + /* In this case the "event" field of the Janode event is "success" */ + janode_event.event = PLUGIN_EVENT.SUCCESS; + break; + + /* TextRoom room created */ + case 'created': + janode_event.event = PLUGIN_EVENT.CREATED; + janode_event.data.permanent = message_data.permanent; + break; + + /* TextRoom room destroyed */ + case 'destroyed': + janode_event.event = PLUGIN_EVENT.DESTROYED; + janode_event.data.permanent = message_data.permanent; + break; + + /* Generic event (e.g. errors) */ + case 'event': + /* TextRoom error */ + if (error) { + janode_event.event = PLUGIN_EVENT.ERROR; + janode_event.data = new Error(`${error_code} ${error}`); + /* In case of error, close a transaction */ + this.closeTransactionWithError(transaction, janode_event.data); + break; + } + /* Configuration success for this handle */ + if (typeof message_data.result !== 'undefined') { + if (message_data.result === 'ok') { + janode_event.event = PLUGIN_EVENT.SUCCESS; + } + break; + } + } + + /* The event has been handled */ + if (janode_event.event) { + /* Try to close the transaction */ + this.closeTransactionWithSuccess(transaction, janus_message); + /* If the transaction was not owned, emit the event */ + if (emit) this.emit(janode_event.event, janode_event.data); + return janode_event; + } + } + + /* The event has not been handled, return a falsy value */ + return null; + } + + /*----------*/ + /* USER API */ + /*----------*/ + + /* These are the APIs that users need to work with the textroom plugin */ + + /** + * Setup a datachannel connection. + * + * @returns {Promise} + */ + async setup() { + const body = { + request: REQUEST_SETUP, + }; + + const response = await this.message(body); + const { event, data: evtdata } = this._getPluginEvent(response); + if (event === PLUGIN_EVENT.SUCCESS) + return evtdata; + const error = new Error(`unexpected response to ${body.request} request`); + throw (error); + } + + /** + * Complete the setup or restart of a datachannel connection. + * + * @param {Object} params + * @param {RTCSessionDescription} params.jsep + * @returns {Promise} + */ + async ack(jsep) { + const body = { + request: REQUEST_ACK, + }; + + const response = await this.message(body, jsep); + const { event, data: evtdata } = this._getPluginEvent(response); + if (event === PLUGIN_EVENT.SUCCESS) + return evtdata; + const error = new Error(`unexpected response to ${body.request} request`); + throw (error); + } + + /** + * Restart the setup of a datachannel connection. + * + * @returns {Promise} + */ + async restart() { + const body = { + request: REQUEST_RESTART, + }; + + const response = await this.message(body); + const { event, data: evtdata } = this._getPluginEvent(response); + if (event === PLUGIN_EVENT.SUCCESS) + return evtdata; + const error = new Error(`unexpected response to ${body.request} request`); + throw (error); + } + + /*----------------*/ + /* Management API */ + /*----------------*/ + + /* These are the APIs needed to manage textroom resources (rooms, forwarders ...) */ + + /** + * List available textroom rooms. + * + * @param {Object} params + * @param {string} [params.admin_key] - The admin key needed for invoking the API + * @returns {Promise} + */ + async list({ admin_key }) { + const body = { + request: REQUEST_LIST_ROOMS, + }; + if (typeof admin_key === 'string') body.admin_key = admin_key; + + const response = await this.message(body); + const { event, data: evtdata } = this._getPluginEvent(response); + if (event === PLUGIN_EVENT.ROOMS_LIST) + return evtdata; + const error = new Error(`unexpected response to ${body.request} request`); + throw (error); + } + + /** + * List participants inside a room. + * + * @param {Object} params + * @param {number|string} params.room - The room where to execute the list + * @returns {Promise} + */ + async listParticipants({ room, secret }) { + const body = { + request: REQUEST_LIST_PARTICIPANTS, + room, + }; + if (typeof secret === 'string') body.secret = secret; + + const response = await this.message(body); + const { event, data: evtdata } = this._getPluginEvent(response); + if (event === PLUGIN_EVENT.PARTICIPANTS_LIST) + return evtdata; + const error = new Error(`unexpected response to ${body.request} request`); + throw (error); + } + + /** + * Check if a room exists. + * + * @param {Object} params + * @param {number|string} params.room - The involved room + * @returns {Promise} + */ + async exists({ room }) { + const body = { + request: REQUEST_EXISTS, + room, + }; + + const response = await this.message(body); + const { event, data: evtdata } = this._getPluginEvent(response); + if (event === PLUGIN_EVENT.EXISTS) + return evtdata; + const error = new Error(`unexpected response to ${body.request} request`); + throw (error); + } + + /** + * Create a textroom room. + * + * @param {Object} params + * @param {number|string} params.room - The room identifier + * @param {string} [params.admin_key] - The admin key needed for invoking the API + * @param {string} [params.description] - A room description + * @param {string} [params.secret] - The secret to be used when managing the room + * @param {string} [params.pin] - The ping needed for joining the room + * @param {boolean} [params.is_private] - Set room as private (hidden in list) + * @param {boolean} [params.history] - Set number of messages to store as a history + * @param {boolean} [params.post] - Set HTTP backend to forward incoming chat messages to + * @param {boolean} [params.permanent] - Set to true to persist the room in the Janus config file + * @returns {Promise} + */ + async create({ room, admin_key, description, secret, pin, is_private, history, post, permanent }) { + const body = { + request: REQUEST_CREATE, + room, + }; + if (typeof admin_key === 'string') body.admin_key = admin_key; + if (typeof description === 'string') body.description = description; + if (typeof secret === 'string') body.secret = secret; + if (typeof pin === 'string') body.pin = pin; + if (typeof is_private === 'boolean') body.is_private = is_private; + if (typeof history === 'number') body.history = history; + if (typeof post === 'string') body.post = post; + if (typeof permanent === 'boolean') body.permanent = permanent; + + const response = await this.message(body); + const { event, data: evtdata } = this._getPluginEvent(response); + if (event === PLUGIN_EVENT.CREATED) + return evtdata; + const error = new Error(`unexpected response to ${body.request} request`); + throw (error); + } + + /** + * Edit a textroom token list. + * + * @param {Object} params + * @param {number|string} params.room - The involved room + * @param {"enable"|"disable"|"add"|"remove"} params.action - The action to perform + * @param {string[]} params.list - The list of tokens to add/remove + * @param {string} [params.secret] - The optional secret needed to manage the room + * @returns {Promise} + */ + async allow({ room, action, list, secret }) { + const body = { + request: REQUEST_ALLOW, + room, + action, + }; + if (list && list.length > 0) body.allowed = list; + if (typeof secret === 'string') body.secret = secret; + + const response = await this.message(body); + const { event, data: evtdata } = this._getPluginEvent(response); + if (event === PLUGIN_EVENT.SUCCESS) + return evtdata; + const error = new Error(`unexpected response to ${body.request} request`); + throw (error); + } + + /** + * Send an announcement to a textroom room. + * + * @param {Object} params + * @param {number|string} params.room - The involved room + * @param {string} params.text - The content of the announcement, as text + * @param {string} [params.secret] - The optional secret needed to manage the room + * @returns {Promise} + */ + async announcement({ room, text, secret }) { + const body = { + request: REQUEST_ANNOUNCEMENT, + room, + text, + }; + if (typeof secret === 'string') body.secret = secret; + + const response = await this.message(body); + const { event, data: evtdata } = this._getPluginEvent(response); + if (event === PLUGIN_EVENT.SUCCESS) + return evtdata; + const error = new Error(`unexpected response to ${body.request} request`); + throw (error); + } + + /** + * Kick an user out from a room. + * + * @param {Object} params + * @param {number|string} params.room - The involved room + * @param {string} params.username - The user to kick out + * @param {string} [params.secret] - The optional secret needed for managing the room + * @returns {Promise} + */ + async kick({ room, username, secret }) { + const body = { + request: REQUEST_KICK, + room, + username, + }; + if (typeof secret === 'string') body.secret = secret; + + const response = await this.message(body); + const { event, data: evtdata } = this._getPluginEvent(response); + if (event === PLUGIN_EVENT.SUCCESS) { + /* Add data missing from Janus response */ + evtdata.room = body.room; + evtdata.username = body.username; + return evtdata; + } + const error = new Error(`unexpected response to ${body.request} request`); + throw (error); + } + + /** + * Destroy a textroom room. + * + * @param {Object} params + * @param {number|string} params.room - The room to destroy + * @param {boolean} [params.permanent] - Set to true to remove the room from the Janus config file + * @param {string} [params.secret] - The optional secret needed to manage the room + * @returns {Promise} + */ + async destroy({ room, permanent, secret }) { + const body = { + request: REQUEST_DESTROY, + room, + }; + if (typeof permanent === 'boolean') body.permanent = permanent; + if (typeof secret === 'string') body.secret = secret; + + const response = await this.message(body); + const { event, data: evtdata } = this._getPluginEvent(response); + if (event === PLUGIN_EVENT.DESTROYED) + return evtdata; + const error = new Error(`unexpected response to ${body.request} request`); + throw (error); + } + +} + +/** + * The payload of the plugin message (cfr. Janus docs). + * {@link https://janus.conf.meetecho.com/docs/textroom.html} + * + * @private + * @typedef {Object} TextRoomData + */ + +/** + * The response event for textroom WebRTC establishment. + * + * @typedef {Object} TEXTROOM_EVENT_SUCCESS + */ + +/** + * The response event for textroom room list request. + * + * @typedef {Object} TEXTROOM_EVENT_ROOMS_LIST + * @property {object[]} list - The list of the rooms as returned by Janus + */ + +/** + * The response event for textroom participants list request. + * + * @typedef {Object} TEXTROOM_EVENT_PARTICIPANTS_LIST + * @property {number|string} room - The involved room + * @property {object[]} participants - The list of participants as returned by Janus + */ + +/** + * The response event for textroom room exists request. + * + * @typedef {Object} TEXTROOM_EVENT_EXISTS + * @property {number|string} room - The involved room + * @property {boolean} exists - True if the rooms exists + */ + +/** + * The response event for textroom room create request. + * + * @typedef {Object} TEXTROOM_EVENT_CREATED + * @property {number|string} room - The created room + * @property {boolean} permanent - True if the room is being persisted in the Janus config file + */ + +/** + * The response event for textroom room destroy request. + * + * @typedef {Object} TEXTROOM_EVENT_DESTROYED + * @property {number|string} room - The destroyed room + * @property {boolean} permanent - True if the room removal is being persisted in the Janus config file + */ + +/** + * The response event for textroom participant kick request. + * + * @typedef {Object} TEXTROOM_EVENT_KICK_RESPONSE + * @property {number|string} room - The involved room + * @property {string} username - The username that has been kicked out + */ + +/** + * The response event for textroom ACL token edit request. + * + * @typedef {Object} TEXTROOM_EVENT_ALLOWED_RESPONSE + * @property {number|string} room - The involved room + * @property {string[]} list - The updated, complete, list of allowed tokens + */ + +/** + * The exported plugin descriptor. + * + * @type {Object} + * @property {string} id - The plugin identifier used when attaching to Janus + * @property {module:textroom-plugin~TextRoomHandle} Handle - The custom class implementing the plugin + * @property {Object} EVENT - The events emitted by the plugin + * @property {string} EVENT.TEXTROOM_ERROR {@link module:textroom-plugin~TextRoomHandle#event:TEXTROOM_ERROR TEXTROOM_ERROR} + */ +export default { + id: PLUGIN_ID, + Handle: TextRoomHandle, + + EVENT: { + /** + * Generic textroom error. + * + * @event module:textroom-plugin~TextRoomHandle#event:TEXTROOM_ERROR + * @type {Error} + */ + TEXTROOM_ERROR: PLUGIN_EVENT.ERROR, + }, +}; diff --git a/src/plugins/videoroom-plugin.js b/src/plugins/videoroom-plugin.js index 3eda329..a9ebd48 100644 --- a/src/plugins/videoroom-plugin.js +++ b/src/plugins/videoroom-plugin.js @@ -23,6 +23,7 @@ const REQUEST_SWITCH = 'switch'; const REQUEST_PUBLISH = 'publish'; const REQUEST_UNPUBLISH = 'unpublish'; const REQUEST_LEAVE = 'leave'; +const REQUEST_UPDATE = 'update'; const REQUEST_EXISTS = 'exists'; const REQUEST_LIST_ROOMS = 'list'; @@ -79,6 +80,7 @@ const PLUGIN_EVENT = { * Moreover it defines many methods to support VideoRoom operations.
* * @hideconstructor + * @extends module:handle~Handle */ class VideoRoomHandle extends Handle { /** @@ -97,6 +99,14 @@ class VideoRoomHandle extends Handle { */ this.feed = null; + /** + * [multistream] + * Either the streams assigned to this publisher handle or the streams subscribed to in case this handle is a subscriber. + * + * @type {object[]} + */ + this.streams = null; + /** * The identifier of the videoroom the handle has joined. * @@ -109,11 +119,11 @@ class VideoRoomHandle extends Handle { * The custom "handleMessage" needed for handling VideoRoom messages. * * @private - * @param {object} janus_message - * @returns {object} A falsy value for unhandled events, a truthy value for handled events + * @param {Object} janus_message + * @returns {Object} A falsy value for unhandled events, a truthy value for handled events */ handleMessage(janus_message) { - const { plugindata, jsep, transaction } = janus_message; + const { plugindata, transaction } = janus_message; if (plugindata && plugindata.data && plugindata.data.videoroom) { /** * @type {VideoRoomData} @@ -122,15 +132,8 @@ class VideoRoomHandle extends Handle { const { videoroom, error, error_code, room } = message_data; /* Prepare an object for the output Janode event */ - const janode_event = { - /* The name of the resolved event */ - event: null, - /* The event payload */ - data: {}, - }; - - /* Add JSEP data if available */ - if (jsep) janode_event.data.jsep = jsep; + const janode_event = this._newPluginEvent(janus_message); + /* Add room information if available */ if (room) janode_event.data.room = room; @@ -138,9 +141,6 @@ class VideoRoomHandle extends Handle { /* That means that a transaction has already been closed or this is an async event */ const emit = (this.ownsTransaction(transaction) === false); - /* Use the "janode" property to store the output event */ - janus_message._janode = janode_event; - switch (videoroom) { /* Success response */ @@ -182,12 +182,18 @@ class VideoRoomHandle extends Handle { janode_event.data.feed = message_data.id; janode_event.data.description = message_data.description; - janode_event.data.publishers = message_data.publishers.map(({ id, display, talking }) => { + janode_event.data.private_id = message_data.private_id; + janode_event.data.publishers = message_data.publishers.map(({ id, display, talking, audio_codec, video_codec, simulcast, streams }) => { const pub = { feed: id, display, }; if (typeof talking !== 'undefined') pub.talking = talking; + if (typeof audio_codec !== 'undefined') pub.audiocodec = audio_codec; + if (typeof video_codec !== 'undefined') pub.videocodec = video_codec; + if (typeof simulcast !== 'undefined') pub.simulcast = simulcast; + /* [multistream] add streams info for this participant */ + if (typeof streams !== 'undefined') pub.streams = streams; return pub; }); janode_event.event = PLUGIN_EVENT.PUB_JOINED; @@ -197,16 +203,24 @@ class VideoRoomHandle extends Handle { case 'attached': /* Store room and feed id */ this.room = room; - this.feed = message_data.id; + if (typeof message_data.id !== 'undefined') { + this.feed = message_data.id; + janode_event.data.feed = message_data.id; + janode_event.data.display = message_data.display; + } + + /* [multistream] add streams info to the subscriber joined event */ + if (typeof message_data.streams !== 'undefined') { + this.streams = message_data.streams; + janode_event.data.streams = message_data.streams; + } - janode_event.data.feed = message_data.id; - janode_event.data.display = message_data.display; janode_event.event = PLUGIN_EVENT.SUB_JOINED; break; /* Slow-link event */ case 'slow_link': - janode_event.data.feed = this.feed; + if (this.feed) janode_event.data.feed = this.feed; janode_event.data.bitrate = message_data['current-bitrate']; janode_event.event = PLUGIN_EVENT.SLOW_LINK; break; @@ -239,23 +253,81 @@ class VideoRoomHandle extends Handle { /* RTP forwarding started */ case 'rtp_forward': janode_event.data.feed = message_data.publisher_id; - janode_event.data.forwarder = { - host: message_data.rtp_stream.host, - }; - if (message_data.rtp_stream.audio) { - janode_event.data.forwarder.audio_port = message_data.rtp_stream.audio; - janode_event.data.forwarder.audio_rtcp_port = message_data.rtp_stream.audio_rtcp; - janode_event.data.forwarder.audio_stream = message_data.rtp_stream.audio_stream_id; - } - if (message_data.rtp_stream.video) { - janode_event.data.forwarder.video_port = message_data.rtp_stream.video; - janode_event.data.forwarder.video_rtcp_port = message_data.rtp_stream.video_rtcp; - janode_event.data.forwarder.video_stream = message_data.rtp_stream.video_stream_id; + if (message_data.rtp_stream) { + const f = message_data.rtp_stream; + const fwd = { + host: f.host, + }; + if (f.audio_stream_id) { + fwd.audio_stream = f.audio_stream_id; + fwd.audio_port = f.audio; + if (typeof f.audio_rtcp === 'number') { + fwd.audio_rtcp_port = f.audio_rtcp; + } + } + if (f.video_stream_id) { + fwd.video_stream = f.video_stream_id; + fwd.video_port = f.video; + if (typeof f.video_rtcp === 'number') { + fwd.video_rtcp_port = f.video_rtcp; + } + if (f.video_stream_id_2) { + fwd.video_stream_2 = f.video_stream_id_2; + fwd.video_port_2 = f.video_2; + } + if (f.video_stream_id_3) { + fwd.video_stream_3 = f.video_stream_id_3; + fwd.video_port_3 = f.video_3; + } + } + if (f.data_stream_id) { + fwd.data_stream = f.data_stream_id; + fwd.data_port = f.data; + } + + janode_event.data.forwarder = fwd; } - if (message_data.rtp_stream.data) { - janode_event.data.forwarder.data_port = message_data.rtp_stream.data; - janode_event.data.forwarder.data_stream = message_data.rtp_stream.data_stream_id; + /* [multistream] */ + else if (message_data.forwarders) { + janode_event.data.forwarders = message_data.forwarders.map(f => { + const fwd = { + host: f.host, + }; + if (f.type === 'audio') { + fwd.audio_stream = f.stream_id; + fwd.audio_port = f.port; + if (typeof f.remote_rtcp_port === 'number') { + fwd.audio_rtcp_port = f.remote_rtcp_port; + } + } + if (f.type === 'video') { + fwd.video_stream = f.stream_id; + fwd.video_port = f.port; + if (typeof f.remote_rtcp_port === 'number') { + fwd.video_rtcp_port = f.remote_rtcp_port; + } + if (typeof f.substream === 'number') { + fwd.sc_substream_layer = f.substream; + } + } + if (f.type === 'data') { + fwd.data_stream = f.stream_id; + fwd.data_port = f.port; + } + if (typeof f.ssrc === 'number') { + fwd.ssrc = f.ssrc; + } + if (typeof f.pt === 'number') { + fwd.pt = f.pt; + } + if (typeof f.srtp === 'boolean') { + fwd.srtp = f.srtp; + } + + return fwd; + }); } + janode_event.event = PLUGIN_EVENT.RTP_FWD_STARTED; break; @@ -268,75 +340,95 @@ class VideoRoomHandle extends Handle { /* RTP forwarders list */ case 'forwarders': - if (janode_event.data.forwarders) { + if (message_data.rtp_forwarders) { janode_event.data.forwarders = message_data.rtp_forwarders.map(({ publisher_id, rtp_forwarder }) => { const pub = { feed: publisher_id, }; - pub.forwarders = rtp_forwarder.map(forw => { - const forwarder = { - host: forw.ip, + pub.forwarders = rtp_forwarder.map(f => { + const fwd = { + host: f.ip, }; - - if (forw.audio_stream_id) { - forwarder.audio_port = forw.port; - forwarder.audio_rtcp_port = forw.remote_rtcp_port; - forwarder.audio_stream = forw.audio_stream_id; + if (f.audio_stream_id) { + fwd.audio_stream = f.audio_stream_id; + fwd.audio_port = f.port; + if (typeof f.remote_rtcp_port === 'number') { + fwd.audio_rtcp_port = f.remote_rtcp_port; + } + } + if (f.video_stream_id) { + fwd.video_stream = f.video_stream_id; + fwd.video_port = f.port; + if (typeof f.remote_rtcp_port === 'number') { + fwd.video_rtcp_port = f.remote_rtcp_port; + } + if (typeof f.substream === 'number') { + fwd.sc_substream_layer = f.substream; + } + } + if (f.data_stream_id) { + fwd.data_stream = f.data_stream_id; + fwd.data_port = f.port; } - if (forw.video_stream_id) { - forwarder.video_port = forw.port; - forwarder.video_rtcp_port = forw.remote_rtcp_port; - forwarder.video_stream = forw.video_stream_id; + if (typeof f.ssrc === 'number') { + fwd.ssrc = f.ssrc; } - if (forw.data_stream_id) { - forwarder.data_port = forw.port; - forwarder.data_stream = forw.data_stream_id; + if (typeof f.pt === 'number') { + fwd.pt = f.pt; + } + if (typeof f.srtp === 'boolean') { + fwd.srtp = f.srtp; } - return forwarder; + return fwd; }); return pub; }); } - else if (janode_event.data.publishers) { + /* [multistream] */ + else if (message_data.publishers) { janode_event.data.forwarders = message_data.publishers.map(({ publisher_id, forwarders }) => { const pub = { feed: publisher_id, }; - pub.forwarders = forwarders.map(forw => { - const forwarder = { - host: forw.host, + pub.forwarders = forwarders.map(f => { + const fwd = { + host: f.host, }; - - if (forw.type === 'audio') { - forwarder.audio_port = forw.port; - forwarder.audio_rtcp_port = forw.remote_rtcp_port; - forwarder.audio_stream = forw.stream_id; + if (f.type === 'audio') { + fwd.audio_stream = f.stream_id; + fwd.audio_port = f.port; + if (typeof f.remote_rtcp_port === 'number') { + fwd.audio_rtcp_port = f.remote_rtcp_port; + } } - if (forw.type === 'video') { - forwarder.video_port = forw.port; - forwarder.video_rtcp_port = forw.remote_rtcp_port; - forwarder.video_stream = forw.stream_id; - if (typeof forw.substream !== 'undefined') { - forwarder.sc_substream_layer = forw.substream; + if (f.type === 'video') { + fwd.video_stream = f.stream_id; + fwd.video_port = f.port; + if (typeof f.remote_rtcp_port === 'number') { + fwd.video_rtcp_port = f.remote_rtcp_port; + } + if (typeof f.substream === 'number') { + fwd.sc_substream_layer = f.substream; } } - if (forw.type === 'data') { - forwarder.data_port = forw.port; - forwarder.data_stream = forw.stream_id; + if (f.type === 'data') { + fwd.data_stream = f.stream_id; + fwd.data_port = f.port; } - - if (typeof forw.ssrc !== 'undefined') - forwarder.ssrc = forw.ssrc; - if (typeof forw.pt !== 'undefined') - forwarder.pt = forw.pt; - if (typeof forw.srtp !== 'undefined') - forwarder.srtp = forw.srtp; - - return forwarder; + if (typeof f.ssrc === 'number') { + fwd.ssrc = f.ssrc; + } + if (typeof f.pt === 'number') { + fwd.pt = f.pt; + } + if (typeof f.srtp === 'boolean') { + fwd.srtp = f.srtp; + } + return fwd; }); return pub; @@ -351,6 +443,8 @@ class VideoRoomHandle extends Handle { case 'stopped-talking': janode_event.data.feed = message_data.id; janode_event.data.talking = (videoroom === 'talking'); + /* [multistream] */ + if (typeof message_data.mid !== 'undefined') janode_event.data.mid = message_data.mid; janode_event.data.audio_level = message_data['audio-level-dBov-avg']; janode_event.event = PLUGIN_EVENT.TALKING; break; @@ -361,6 +455,12 @@ class VideoRoomHandle extends Handle { janode_event.event = PLUGIN_EVENT.UPDATED; break; + /* [multistream] updating event, sent when janus receives another "update" before getting a JSEP answer for the previous one */ + case 'updating': + janode_event.data.streams = message_data.streams; + janode_event.event = PLUGIN_EVENT.UPDATED; + break; + /* Generic events (error, notifications ...) */ case 'event': /* VideoRoom Error */ @@ -382,12 +482,17 @@ class VideoRoomHandle extends Handle { /* Publisher list notification */ if (message_data.publishers) { janode_event.event = PLUGIN_EVENT.PUB_LIST; - janode_event.data.publishers = message_data.publishers.map(({ id, display, talking }) => { + janode_event.data.publishers = message_data.publishers.map(({ id, display, talking, audio_codec, video_codec, simulcast, streams }) => { const pub = { feed: id, display, }; if (typeof talking !== 'undefined') pub.talking = talking; + if (typeof audio_codec !== 'undefined') pub.audiocodec = audio_codec; + if (typeof video_codec !== 'undefined') pub.videocodec = video_codec; + if (typeof simulcast !== 'undefined') pub.simulcast = simulcast; + /* [multistream] add streams info for this participant */ + if (typeof streams !== 'undefined') pub.streams = streams; return pub; }); break; @@ -395,28 +500,23 @@ class VideoRoomHandle extends Handle { /* Configuration events (publishing, general configuration) */ if (typeof message_data.configured !== 'undefined') { janode_event.event = PLUGIN_EVENT.CONFIGURED; - janode_event.data.feed = this.feed; + if (this.feed) janode_event.data.feed = this.feed; + /* [multistream] add streams info */ + if (typeof message_data.streams !== 'undefined') janode_event.data.streams = message_data.streams; janode_event.data.configured = message_data.configured; break; } - /* Display name changed event */ - if (typeof message_data.display !== 'undefined' && typeof message_data.switched === 'undefined') { - janode_event.event = PLUGIN_EVENT.DISPLAY; - janode_event.data.feed = message_data.id; - janode_event.data.display = message_data.display; - break; - } /* Subscribed feed started */ if (typeof message_data.started !== 'undefined') { janode_event.event = PLUGIN_EVENT.STARTED; - janode_event.data.feed = this.feed; + if (this.feed) janode_event.data.feed = this.feed; janode_event.data.started = message_data.started; break; } /* Subscribed feed paused */ if (typeof message_data.paused !== 'undefined') { janode_event.event = PLUGIN_EVENT.PAUSED; - janode_event.data.feed = this.feed; + if (this.feed) janode_event.data.feed = this.feed; janode_event.data.paused = message_data.paused; break; } @@ -424,11 +524,17 @@ class VideoRoomHandle extends Handle { if (typeof message_data.switched !== 'undefined') { janode_event.event = PLUGIN_EVENT.SWITCHED; janode_event.data.switched = message_data.switched; - if (message_data.switched === 'ok' && typeof message_data.id !== 'undefined') { - janode_event.data.from_feed = this.feed; - this.feed = message_data.id; - janode_event.data.to_feed = this.feed; - janode_event.data.display = message_data.display; + if (message_data.switched === 'ok') { + if (typeof message_data.id !== 'undefined') { + janode_event.data.from_feed = this.feed; + this.feed = message_data.id; + janode_event.data.to_feed = this.feed; + janode_event.data.display = message_data.display; + } + if (typeof message_data.streams != 'undefined') { + this.streams = message_data.streams; + janode_event.data.streams = message_data.streams; + } } break; } @@ -436,6 +542,7 @@ class VideoRoomHandle extends Handle { if (typeof message_data.unpublished !== 'undefined') { janode_event.event = PLUGIN_EVENT.UNPUBLISHED; janode_event.data.feed = (message_data.unpublished === 'ok') ? this.feed : message_data.unpublished; + if (message_data.display) janode_event.data.display = message_data.display; break; } /* Leaving confirmation */ @@ -443,6 +550,14 @@ class VideoRoomHandle extends Handle { janode_event.event = PLUGIN_EVENT.LEAVING; janode_event.data.feed = (message_data.leaving === 'ok') ? this.feed : message_data.leaving; if (message_data.reason) janode_event.data.reason = message_data.reason; + if (message_data.display) janode_event.data.display = message_data.display; + break; + } + /* Display name changed event */ + if (typeof message_data.display !== 'undefined' && typeof message_data.switched === 'undefined') { + janode_event.event = PLUGIN_EVENT.DISPLAY; + janode_event.data.feed = message_data.id; + janode_event.data.display = message_data.display; break; } /* Participant kicked out */ @@ -454,20 +569,24 @@ class VideoRoomHandle extends Handle { /* Participant left (for subscribers "leave") */ if (typeof message_data.left !== 'undefined') { janode_event.event = PLUGIN_EVENT.LEAVING; - janode_event.data.feed = this.feed; + if (this.feed) janode_event.data.feed = this.feed; break; } /* Simulcast substream layer switch */ if (typeof message_data.substream !== 'undefined') { janode_event.event = PLUGIN_EVENT.SC_SUBSTREAM_LAYER; - janode_event.data.feed = this.feed; + if (this.feed) janode_event.data.feed = this.feed; + /* [multistream] */ + if (typeof message_data.mid !== 'undefined') janode_event.data.mid = message_data.mid; janode_event.data.sc_substream_layer = message_data.substream; break; } /* Simulcast temporal layers switch */ if (typeof message_data.temporal !== 'undefined') { janode_event.event = PLUGIN_EVENT.SC_TEMPORAL_LAYERS; - janode_event.data.feed = this.feed; + if (this.feed) janode_event.data.feed = this.feed; + /* [multistream] */ + if (typeof message_data.mid !== 'undefined') janode_event.data.mid = message_data.mid; janode_event.data.sc_temporal_layers = message_data.temporal; break; } @@ -496,7 +615,7 @@ class VideoRoomHandle extends Handle { /** * Join a videoroom as publisher. * - * @param {object} params + * @param {Object} params * @param {number|string} params.room - The room to join to * @param {number|string} [params.feed] - The feed identifier to use, if missing it is picked by Janus * @param {boolean} [params.audio] - True to request audio relaying @@ -508,9 +627,10 @@ class VideoRoomHandle extends Handle { * @param {string} [params.pin] - The optional pin needed to join the room * @param {boolean} [params.record] - Enable the recording * @param {string} [params.filename] - If recording, the base path/file to use for the recording + * @param {object[]} [params.descriptions] - [multistream] The descriptions object, can define a description for the tracks separately e.g. track mid:0 'Video Camera', track mid:1 'Screen' * @returns {Promise} */ - async joinPublisher({ room, feed, audio, video, data, bitrate, record, filename, display, token, pin }) { + async joinPublisher({ room, feed, audio, video, data, bitrate, record, filename, display, token, pin, descriptions }) { const body = { request: REQUEST_JOIN, ptype: PTYPE_PUBLISHER, @@ -527,8 +647,11 @@ class VideoRoomHandle extends Handle { if (typeof token === 'string') body.token = token; if (typeof pin === 'string') body.pin = pin; + /* [multistream] */ + if (descriptions && Array.isArray(descriptions)) body.descriptions = descriptions; + const response = await this.message(body); - const { event, data: evtdata } = response._janode || {}; + const { event, data: evtdata } = this._getPluginEvent(response); if (event === PLUGIN_EVENT.PUB_JOINED) { if (body.display) evtdata.display = body.display; return evtdata; @@ -540,7 +663,7 @@ class VideoRoomHandle extends Handle { /** * Join and configure videoroom handle as publisher. * - * @param {object} params + * @param {Object} params * @param {number|string} params.room - The room to join to * @param {number|string} [params.feed] - The feed identifier to use, if missing it is picked by Janus * @param {boolean} [params.audio] - True to request audio relaying @@ -552,10 +675,12 @@ class VideoRoomHandle extends Handle { * @param {string} [params.pin] - The optional pin needed to join the room * @param {boolean} [params.record] - Enable the recording * @param {string} [params.filename] - If recording, the base path/file to use for the recording + * @param {boolean} [params.e2ee] - True to notify end-to-end encryption for this connection + * @param {object[]} [params.descriptions] - [multistream] The descriptions object, can define a description for the tracks separately e.g. track mid:0 'Video Camera', track mid:1 'Screen' * @param {RTCSessionDescription} [params.jsep] - The JSEP offer * @returns {Promise} */ - async joinConfigurePublisher({ room, feed, audio, video, data, bitrate, record, filename, display, token, pin, jsep }) { + async joinConfigurePublisher({ room, feed, audio, video, data, bitrate, record, filename, display, token, pin, e2ee, descriptions, jsep }) { const body = { request: REQUEST_JOIN_CONFIGURE, ptype: PTYPE_PUBLISHER, @@ -571,6 +696,10 @@ class VideoRoomHandle extends Handle { if (typeof filename === 'string') body.filename = filename; if (typeof token === 'string') body.token = token; if (typeof pin === 'string') body.pin = pin; + if (typeof e2ee === 'boolean' && jsep) jsep.e2ee = e2ee; + + /* [multistream] */ + if (descriptions && Array.isArray(descriptions)) body.descriptions = descriptions; const response = await this.message(body, jsep).catch(e => { /* Cleanup the WebRTC status in Janus in case of errors when publishing */ @@ -593,7 +722,7 @@ class VideoRoomHandle extends Handle { throw e; }); - const { event, data: evtdata } = response._janode || {}; + const { event, data: evtdata } = this._getPluginEvent(response); if (event === PLUGIN_EVENT.PUB_JOINED) { if (body.display) evtdata.display = body.display; return evtdata; @@ -611,7 +740,7 @@ class VideoRoomHandle extends Handle { * restart/update flags, while subscribers need to use them to force * the operation. * - * @param {object} params + * @param {Object} params * @param {boolean} [params.audio] - True to request audio relaying * @param {boolean} [params.video] - True to request video relaying * @param {boolean} [params.data] - True to request datachannel relaying @@ -621,28 +750,45 @@ class VideoRoomHandle extends Handle { * @param {string} [params.filename] - If recording, the base path/file to use for the recording (publishers only) * @param {boolean} [params.restart] - Set to force a ICE restart * @param {boolean} [params.update] - Set to force a renegotiation + * @param {object[]} [params.streams] - [multistream] The streams object, each stream includes mid, keyframe, send, min_delay, max_delay + * @param {object[]} [params.descriptions] - [multistream] The descriptions object, can define a description for the tracks separately e.g. track mid:0 'Video Camera', track mid:1 'Screen' * @param {number} [params.sc_substream_layer] - Substream layer to receive (0-2), in case simulcasting is enabled (subscribers only) * @param {number} [params.sc_substream_fallback_ms] - How much time in ms without receiving packets will make janus drop to the substream below (subscribers only) * @param {number} [params.sc_temporal_layers] - Temporal layers to receive (0-2), in case VP8 simulcasting is enabled (subscribers only) + * @param {boolean} [params.e2ee] - True to notify end-to-end encryption for this connection * @param {RTCSessionDescription} [params.jsep] - The JSEP offer (publishers only) + * @param {boolean} [params.keyframe] - True to request a keyframe (publishers only) * @returns {Promise} */ - async configure({ audio, video, data, bitrate, record, filename, display, restart, update, sc_substream_layer, sc_substream_fallback_ms, sc_temporal_layers, jsep }) { + async configure({ audio, video, data, bitrate, record, filename, display, restart, update, streams, descriptions, sc_substream_layer, sc_substream_fallback_ms, sc_temporal_layers, e2ee, jsep, keyframe }) { const body = { request: REQUEST_CONFIGURE, }; - if (typeof audio === 'boolean') body.audio = audio; - if (typeof video === 'boolean') body.video = video; - if (typeof data === 'boolean') body.data = data; + + /* [multistream] */ + if (streams && Array.isArray(streams)) { + body.streams = streams; + } + else { + if (typeof audio === 'boolean') body.audio = audio; + if (typeof video === 'boolean') body.video = video; + if (typeof data === 'boolean') body.data = data; + if (typeof sc_substream_layer === 'number') body.substream = sc_substream_layer; + if (typeof sc_substream_fallback_ms === 'number') body.fallback = 1000 * sc_substream_fallback_ms; + if (typeof sc_temporal_layers === 'number') body.temporal = sc_temporal_layers; + } + if (typeof bitrate === 'number') body.bitrate = bitrate; if (typeof record === 'boolean') body.record = record; if (typeof filename === 'string') body.filename = filename; if (typeof display === 'string') body.display = display; if (typeof restart === 'boolean') body.restart = restart; if (typeof update === 'boolean') body.update = update; - if (typeof sc_substream_layer === 'number') body.substream = sc_substream_layer; - if (typeof sc_substream_fallback_ms === 'number') body.fallback = 1000 * sc_substream_fallback_ms; - if (typeof sc_temporal_layers === 'number') body.temporal = sc_temporal_layers; + if (typeof e2ee === 'boolean' && jsep) jsep.e2ee = e2ee; + if (typeof keyframe === 'boolean') body.keyframe = keyframe; + + /* [multistream] */ + if (descriptions && Array.isArray(descriptions)) body.descriptions = descriptions; const response = await this.message(body, jsep).catch(e => { /* Cleanup the WebRTC status in Janus in case of errors when publishing */ @@ -665,7 +811,7 @@ class VideoRoomHandle extends Handle { throw e; }); - const { event, data: evtdata } = response._janode || {}; + const { event, data: evtdata } = this._getPluginEvent(response); if (event === PLUGIN_EVENT.CONFIGURED && evtdata.configured === 'ok') { if (body.display) evtdata.display = body.display; if (typeof body.request === 'boolean') evtdata.restart = body.restart; @@ -680,7 +826,7 @@ class VideoRoomHandle extends Handle { * Publish a feed in the room. * Room is detected from the context since a handle must have joined before. * - * @param {object} params + * @param {Object} params * @param {boolean} [params.audio] - True to request audio relaying * @param {boolean} [params.video] - True to request video relaying * @param {boolean} [params.data] - True to request datachannel relaying @@ -688,10 +834,12 @@ class VideoRoomHandle extends Handle { * @param {number} [params.bitrate] - Bitrate cap * @param {boolean} [params.record] - True to record the feed * @param {string} [params.filename] - If recording, the base path/file to use for the recording + * @param {object[]} [params.descriptions] - [multistream] The descriptions object, for each stream you can define description + * @param {boolean} [params.e2ee] - True to notify end-to-end encryption for this connection * @param {RTCSessionDescription} params.jsep - The JSEP offer * @returns {Promise} */ - async publish({ audio, video, data, bitrate, record, filename, display, jsep }) { + async publish({ audio, video, data, bitrate, record, filename, display, descriptions, e2ee, jsep }) { if (typeof jsep === 'object' && jsep && jsep.type !== 'offer') { const error = new Error('jsep must be an offer'); return Promise.reject(error); @@ -699,13 +847,21 @@ class VideoRoomHandle extends Handle { const body = { request: REQUEST_PUBLISH, }; + if (typeof audio === 'boolean') body.audio = audio; if (typeof video === 'boolean') body.video = video; if (typeof data === 'boolean') body.data = data; + if (typeof bitrate === 'number') body.bitrate = bitrate; if (typeof record === 'boolean') body.record = record; if (typeof filename === 'string') body.filename = filename; if (typeof display === 'string') body.display = display; + if (typeof e2ee === 'boolean' && jsep) jsep.e2ee = e2ee; + + /* [multistream] */ + if (descriptions && Array.isArray(descriptions)) { + body.descriptions = descriptions; + } const response = await this.message(body, jsep).catch(e => { /* Cleanup the WebRTC status in Janus in case of errors when publishing */ @@ -728,7 +884,7 @@ class VideoRoomHandle extends Handle { throw e; }); - const { event, data: evtdata } = response._janode || {}; + const { event, data: evtdata } = this._getPluginEvent(response); if (event === PLUGIN_EVENT.CONFIGURED && evtdata.configured === 'ok') { if (body.display) evtdata.display = body.display; return evtdata; @@ -748,7 +904,7 @@ class VideoRoomHandle extends Handle { }; const response = await this.message(body); - const { event, data: evtdata } = response._janode || {}; + const { event, data: evtdata } = this._getPluginEvent(response); if (event === PLUGIN_EVENT.UNPUBLISHED) return evtdata; const error = new Error(`unexpected response to ${body.request} request`); @@ -758,35 +914,59 @@ class VideoRoomHandle extends Handle { /** * Join a room as subscriber. * - * @param {object} params + * @param {Object} params * @param {number|string} params.room - The room to join * @param {number|string} [params.feed=0] - The feed the user wants to subscribe to - * @param {boolean} [params.audio] - True to subscribe to the audio feed - * @param {boolean} [params.video] - True to subscribe to the video feed - * @param {boolean} [params.data] - True to subscribe to the datachannels of the feed + * @param {boolean} [params.audio] - Whether or not audio should be relayed + * @param {boolean} [params.video] - Whether or not video should be relayed + * @param {boolean} [params.data] - Whether or not data should be relayed + * @param {boolean} [params.offer_audio] - Whether or not audio should be negotiated + * @param {boolean} [params.offer_video] - Whether or not video should be negotiated + * @param {boolean} [params.offer_data] - Whether or not data should be negotiated + * @param {number} [params.private_id] - The private id to correlate with publisher * @param {number} [params.sc_substream_layer] - Substream layer to receive (0-2), in case simulcasting is enabled * @param {number} [params.sc_substream_fallback_ms] - How much time in ms without receiving packets will make janus drop to the substream below * @param {number} [params.sc_temporal_layers] - Temporal layers to receive (0-2), in case VP8 simulcasting is enabled + * @param {object[]} [params.streams] - [multistream] The streams object, each stream includes feed, mid, send, ... + * @param {boolean} [params.autoupdate] - [multistream] Whether a new SDP offer is sent automatically when a subscribed publisher leaves + * @param {boolean} [params.use_msid] - [multistream] Whether subscriptions should include an msid that references the publisher * @param {string} [params.token] - The optional token needed + * @param {string} [params.pin] - The optional password required to join the room * @returns {Promise} */ - async joinSubscriber({ room, feed, audio, video, data, sc_substream_layer, sc_substream_fallback_ms, sc_temporal_layers, token }) { + async joinSubscriber({ room, feed, audio, video, data, offer_audio, offer_video, offer_data, private_id, sc_substream_layer, sc_substream_fallback_ms, sc_temporal_layers, streams, autoupdate, use_msid, token, pin }) { const body = { request: REQUEST_JOIN, ptype: PTYPE_LISTENER, room, - feed, }; - if (typeof audio === 'boolean') body.audio = audio; - if (typeof video === 'boolean') body.video = video; - if (typeof data === 'boolean') body.data = data; + + /* [multistream] */ + if (streams && Array.isArray(streams)) { + body.streams = streams; + } + else { + body.feed = feed; + if (typeof audio === 'boolean') body.audio = audio; + if (typeof video === 'boolean') body.video = video; + if (typeof data === 'boolean') body.data = data; + if (typeof offer_audio === 'boolean') body.offer_audio = offer_audio; + if (typeof offer_video === 'boolean') body.offer_video = offer_video; + if (typeof offer_data === 'boolean') body.offer_data = offer_data; + if (typeof sc_substream_layer === 'number') body.substream = sc_substream_layer; + if (typeof sc_substream_fallback_ms === 'number') body.fallback = 1000 * sc_substream_fallback_ms; + if (typeof sc_temporal_layers === 'number') body.temporal = sc_temporal_layers; + } + if (typeof private_id === 'number') body.private_id = private_id; if (typeof token === 'string') body.token = token; - if (typeof sc_substream_layer === 'number') body.substream = sc_substream_layer; - if (typeof sc_substream_fallback_ms === 'number') body.fallback = 1000 * sc_substream_fallback_ms; - if (typeof sc_temporal_layers === 'number') body.temporal = sc_temporal_layers; + if (typeof pin === 'string') body.pin = pin; + + /* [multistream] */ + if (typeof autoupdate === 'boolean') body.autoupdate = autoupdate; + if (typeof use_msid === 'boolean') body.use_msid = use_msid; const response = await this.message(body); - const { event, data: evtdata } = response._janode || {}; + const { event, data: evtdata } = this._getPluginEvent(response); if (event === PLUGIN_EVENT.SUB_JOINED) return evtdata; const error = new Error(`unexpected response to ${body.request} request`); @@ -805,17 +985,20 @@ class VideoRoomHandle extends Handle { /** * Start a subscriber stream. * - * @param {object} params + * @param {Object} params * @param {RTCSessionDescription} params.jsep - The JSEP answer + * @param {boolean} [params.e2ee] - True to hint an end-to-end encrypted negotiation * @returns {Promise} */ - async start({ jsep }) { + async start({ jsep, e2ee }) { const body = { request: REQUEST_START, }; + if (jsep) + jsep.e2ee = (typeof e2ee === 'boolean') ? e2ee : jsep.e2ee; const response = await this.message(body, jsep); - const { event, data: evtdata } = response._janode || {}; + const { event, data: evtdata } = this._getPluginEvent(response); if (event === PLUGIN_EVENT.STARTED && evtdata.started === 'ok') return evtdata; const error = new Error(`unexpected response to ${body.request} request`); @@ -833,7 +1016,7 @@ class VideoRoomHandle extends Handle { }; const response = await this.message(body); - const { event, data: evtdata } = response._janode || {}; + const { event, data: evtdata } = this._getPluginEvent(response); if (event === PLUGIN_EVENT.PAUSED && evtdata.paused === 'ok') return evtdata; const error = new Error(`unexpected response to ${body.request} request`); @@ -843,24 +1026,32 @@ class VideoRoomHandle extends Handle { /** * Switch to another feed. * - * @param {object} params - * @param {number|string} params.to_feed - The feed id of the new publisher to switch to + * @param {Object} params + * @param {number|string} [params.to_feed] - The feed id of the new publisher to switch to * @param {boolean} [params.audio] - True to subscribe to the audio feed * @param {boolean} [params.video] - True to subscribe to the video feed * @param {boolean} [params.data] - True to subscribe to the datachannels of the feed + * @param {object[]} [params.streams] - [multistream] streams array containing feed, mid, sub_mid ... * @returns {Promise} */ - async switch({ to_feed, audio, video, data }) { + async switch({ to_feed, audio, video, data, streams }) { const body = { request: REQUEST_SWITCH, - feed: to_feed, }; - if (typeof audio === 'boolean') body.audio = audio; - if (typeof video === 'boolean') body.video = video; - if (typeof data === 'boolean') body.data = data; + + /* [multistream] */ + if (streams && Array.isArray(streams)) { + body.streams = streams; + } + else { + body.feed = to_feed; + if (typeof audio === 'boolean') body.audio = audio; + if (typeof video === 'boolean') body.video = video; + if (typeof data === 'boolean') body.data = data; + } const response = await this.message(body); - const { event, data: evtdata } = response._janode || {}; + const { event, data: evtdata } = this._getPluginEvent(response); if (event === PLUGIN_EVENT.SWITCHED && evtdata.switched === 'ok') { return evtdata; } @@ -880,13 +1071,38 @@ class VideoRoomHandle extends Handle { }; const response = await this.message(body); - const { event, data: evtdata } = response._janode || {}; + const { event, data: evtdata } = this._getPluginEvent(response); if (event === PLUGIN_EVENT.LEAVING) return evtdata; const error = new Error(`unexpected response to ${body.request} request`); throw (error); } + /** + * [multistream] Update a subscription. + * + * @param {Object} params + * @param {object[]} params.subscribe - The array of streams to subscribe + * @param {object[]} params.unsubscribe - The array of streams to unsubscribe + * + * @returns {Promise} + */ + async update({ subscribe, unsubscribe }) { + const body = { + request: REQUEST_UPDATE, + }; + if (subscribe && Array.isArray(subscribe)) body.subscribe = subscribe; + if (unsubscribe && Array.isArray(unsubscribe)) body.unsubscribe = unsubscribe; + + const response = await this.message(body); + const { event, data: evtdata } = this._getPluginEvent(response); + if (event === PLUGIN_EVENT.UPDATED) { + return evtdata; + } + const error = new Error(`unexpected response to ${body.request} request`); + throw (error); + } + /*----------------*/ /* Management API */ /*----------------*/ @@ -896,7 +1112,7 @@ class VideoRoomHandle extends Handle { /** * List the participants inside a room. * - * @param {object} params + * @param {Object} params * @param {number|string} params.room - The room where the list is being requested * @param {string} params.secret - The optional secret for the operation * @returns {Promise} @@ -909,7 +1125,7 @@ class VideoRoomHandle extends Handle { if (typeof secret === 'string') body.secret = secret; const response = await this.message(body); - const { event, data: evtdata } = response._janode || {}; + const { event, data: evtdata } = this._getPluginEvent(response); if (event === PLUGIN_EVENT.PARTICIPANTS_LIST) return evtdata; const error = new Error(`unexpected response to ${body.request} request`); @@ -919,7 +1135,7 @@ class VideoRoomHandle extends Handle { /** * Enable or disable recording for all participants in a room while the conference is in progress. * - * @param {object} params + * @param {Object} params * @param {number|string} params.room - The room where the change of recording state is being requested * @param {string} params.secret - The optional secret for the operation * @param {boolean} params.record - True starts recording for all participants in an already running conference, false stops the recording @@ -934,7 +1150,7 @@ class VideoRoomHandle extends Handle { if (typeof secret === 'string') body.secret = secret; const response = await this.message(body); - const { event, data: evtdata } = response._janode || {}; + const { event, data: evtdata } = this._getPluginEvent(response); if (event === PLUGIN_EVENT.RECORDING_ENABLED_STATE) { evtdata.room = body.room; return evtdata; @@ -946,7 +1162,7 @@ class VideoRoomHandle extends Handle { /** * Kick a publisher out from a room. * - * @param {object} params + * @param {Object} params * @param {number|string} params.room - The room where the kick is being requested * @param {number|string} params.feed - The identifier of the feed to kick out * @param {string} params.secret - The optional secret for the operation @@ -961,7 +1177,7 @@ class VideoRoomHandle extends Handle { if (typeof secret === 'string') body.secret = secret; const response = await this.message(body); - const { event, data: evtdata } = response._janode || {}; + const { event, data: evtdata } = this._getPluginEvent(response); if (event === PLUGIN_EVENT.SUCCESS) { evtdata.room = body.room; evtdata.feed = body.id; @@ -974,7 +1190,7 @@ class VideoRoomHandle extends Handle { /** * Check if a room exists. * - * @param {object} params + * @param {Object} params * @param {number|string} params.room - The room to check * @returns {Promise} */ @@ -985,7 +1201,7 @@ class VideoRoomHandle extends Handle { }; const response = await this.message(body); - const { event, data: evtdata } = response._janode || {}; + const { event, data: evtdata } = this._getPluginEvent(response); if (event === PLUGIN_EVENT.EXISTS) return evtdata; const error = new Error(`unexpected response to ${body.request} request`); @@ -995,15 +1211,18 @@ class VideoRoomHandle extends Handle { /** * List all the available rooms. * + * @param {Object} params + * @param {string} [params.admin_key] - The admin key needed for invoking the API * @returns {Promise} */ - async list() { + async list({ admin_key } = {}) { const body = { request: REQUEST_LIST_ROOMS, }; + if (typeof admin_key === 'string') body.admin_key = admin_key; const response = await this.message(body); - const { event, data: evtdata } = response._janode || {}; + const { event, data: evtdata } = this._getPluginEvent(response); if (event === PLUGIN_EVENT.ROOMS_LIST) return evtdata; const error = new Error(`unexpected response to ${body.request} request`); @@ -1013,7 +1232,7 @@ class VideoRoomHandle extends Handle { /** * Create a new room. * - * @param {object} params + * @param {Object} params * @param {number|string} [params.room] - The room identifier, if missing picked by janus * @param {string} [params.description] - A textual description of the room * @param {number} [params.max_publishers] - The max number of publishers allowed @@ -1021,6 +1240,7 @@ class VideoRoomHandle extends Handle { * @param {boolean} [params.is_private] - Make the room private (hidden from listing) * @param {string} [params.secret] - The secret that will be used to modify the room * @param {string} [params.pin] - The pin needed to access the room + * @param {string} [params.admin_key] - The admin key needed for invoking the API * @param {number} [params.bitrate] - The bitrate cap that will be used for publishers * @param {boolean} [params.bitrate_cap] - Make the bitrate cap an insormountable limit * @param {number} [params.fir_freq] - The PLI interval in seconds @@ -1029,14 +1249,20 @@ class VideoRoomHandle extends Handle { * @param {boolean} [params.talking_events] - True to enable talking events * @param {number} [params.talking_level_threshold] - Audio level threshold for talking events in the range [0, 127] * @param {number} [params.talking_packets_threshold] - Audio packets threshold for talking events + * @param {boolean} [params.require_pvtid] - Whether subscriptions are required to provide a valid private_id + * @param {boolean} [params.notify_joining] - Whether to notify all participants when a new participant joins the room + * @param {boolean} [params.require_e2ee] - Whether all participants are required to publish and subscribe using e2e encryption * @param {boolean} [params.record] - Wheter to enable recording of any publisher * @param {string} [params.rec_dir] - Folder where recordings should be stored * @param {boolean} [params.videoorient] - Whether the video-orientation RTP extension must be negotiated * @param {string} [params.h264_profile] - H264 specific profile to prefer + * @param {string} [params.vp9_profile] - VP9 specific profile to prefer + * @param {number} [params.threads] - Number of threads to assist with the relaying of publishers in the room * @returns {Promise} */ - async create({ room, description, max_publishers, permanent, is_private, secret, pin, bitrate, - bitrate_cap, fir_freq, audiocodec, videocodec, talking_events, talking_level_threshold, talking_packets_threshold, record, rec_dir, videoorient, h264_profile }) { + async create({ room, description, max_publishers, permanent, is_private, secret, pin, admin_key, bitrate, + bitrate_cap, fir_freq, audiocodec, videocodec, talking_events, talking_level_threshold, talking_packets_threshold, + require_pvtid, notify_joining, require_e2ee, record, rec_dir, videoorient, h264_profile, vp9_profile, threads }) { const body = { request: REQUEST_CREATE, }; @@ -1047,6 +1273,7 @@ class VideoRoomHandle extends Handle { if (typeof is_private === 'boolean') body.is_private = is_private; if (typeof secret === 'string') body.secret = secret; if (typeof pin === 'string') body.pin = pin; + if (typeof admin_key === 'string') body.admin_key = admin_key; if (typeof bitrate === 'number') body.bitrate = bitrate; if (typeof bitrate_cap === 'boolean') body.bitrate_cap = bitrate_cap; if (typeof fir_freq === 'number') body.fir_freq = fir_freq; @@ -1055,13 +1282,18 @@ class VideoRoomHandle extends Handle { if (typeof talking_events === 'boolean') body.audiolevel_event = talking_events; if (typeof talking_level_threshold === 'number' && talking_level_threshold >= 0 && talking_level_threshold <= 127) body.audio_level_average = talking_level_threshold; if (typeof talking_packets_threshold === 'number' && talking_packets_threshold > 0) body.audio_active_packets = talking_packets_threshold; + if (typeof require_pvtid === 'boolean') body.require_pvtid = require_pvtid; + if (typeof notify_joining === 'boolean') body.notify_joining = notify_joining; + if (typeof require_e2ee === 'boolean') body.require_e2ee = require_e2ee; if (typeof record === 'boolean') body.record = record; if (typeof rec_dir === 'string') body.rec_dir = rec_dir; if (typeof videoorient === 'boolean') body.videoorient_ext = videoorient; if (typeof h264_profile === 'string') body.h264_profile = h264_profile; + if (typeof vp9_profile === 'string') body.vp9_profile = vp9_profile; + if (typeof threads === 'number') body.threads = threads; const response = await this.message(body); - const { event, data: evtdata } = response._janode || {}; + const { event, data: evtdata } = this._getPluginEvent(response); if (event === PLUGIN_EVENT.CREATED) return evtdata; const error = new Error(`unexpected response to ${body.request} request`); @@ -1071,7 +1303,7 @@ class VideoRoomHandle extends Handle { /** * Destroy a room. * - * @param {object} params + * @param {Object} params * @param {number|string} params.room - The room to destroy * @param {boolean} [params.permanent] - True to remove the room from the Janus config file * @param {string} [params.secret] - The secret needed to manage the room @@ -1086,7 +1318,7 @@ class VideoRoomHandle extends Handle { if (typeof secret === 'string') body.secret = secret; const response = await this.message(body); - const { event, data: evtdata } = response._janode || {}; + const { event, data: evtdata } = this._getPluginEvent(response); if (event === PLUGIN_EVENT.DESTROYED) return evtdata; const error = new Error(`unexpected response to ${body.request} request`); @@ -1096,7 +1328,7 @@ class VideoRoomHandle extends Handle { /** * Edit the ACL tokens for a room. * - * @param {object} params + * @param {Object} params * @param {number|string} params.room - The room where to change the acl * @param {"enable"|"disable"|"add"|"remove"} params.action - The action to execute on the acl * @param {string[]} params.list - The list of tokens to execute the action onto @@ -1113,7 +1345,7 @@ class VideoRoomHandle extends Handle { if (typeof secret === 'string') body.secret = secret; const response = await this.message(body); - const { event, data: evtdata } = response._janode || {}; + const { event, data: evtdata } = this._getPluginEvent(response); if (event === PLUGIN_EVENT.ALLOWED) return evtdata; const error = new Error(`unexpected response to ${body.request} request`); @@ -1123,40 +1355,56 @@ class VideoRoomHandle extends Handle { /** * Start a RTP forwarding in a room. * - * @param {object} params + * @param {Object} params * @param {number|string} params.room - The room where to start a forwarder * @param {number|string} params.feed - The feed identifier to forward (must be published) * @param {string} params.host - The target host for the forwarder + * @param {object[]} [params.streams] - [multistream] The streams array containing mid, port, rtcp_port, port_2 ... * @param {number} [params.audio_port] - The target audio RTP port, if audio is to be forwarded * @param {number} [params.audio_rtcp_port] - The target audio RTCP port, if audio is to be forwarded * @param {number} [params.audio_ssrc] - The SSRC that will be used for audio RTP * @param {number} [params.video_port] - The target video RTP port, if video is to be forwarded * @param {number} [params.video_rtcp_port] - The target video RTCP port, if video is to be forwarded * @param {number} [params.video_ssrc] - The SSRC that will be used for video RTP + * @param {number} [params.video_port_2] - The target video RTP port for simulcast substream + * @param {number} [params.video_ssrc_2] - The SSRC that will be used for video RTP substream + * @param {number} [params.video_port_3] - The target video RTP port for simulcast substream + * @param {number} [params.video_ssrc_3] - The SSRC that will be used for video RTP substream * @param {number} [params.data_port] - The target datachannels port, if datachannels are to be forwarded * @param {string} [params.secret] - The secret needed for managing the room * @param {string} [params.admin_key] - The admin key needed for invoking the API * @returns {Promise} */ - async startForward({ room, feed, host, audio_port, audio_rtcp_port, audio_ssrc, video_port, video_rtcp_port, video_ssrc, data_port, secret, admin_key }) { + async startForward({ room, feed, host, streams, audio_port, audio_rtcp_port, audio_ssrc, video_port, video_rtcp_port, video_ssrc, video_port_2, video_ssrc_2, video_port_3, video_ssrc_3, data_port, secret, admin_key }) { const body = { request: REQUEST_RTP_FWD_START, room, publisher_id: feed, }; if (typeof host === 'string') body.host = host; - if (typeof audio_port === 'number') body.audio_port = audio_port; - if (typeof audio_rtcp_port === 'number') body.audio_rtcp_port = audio_rtcp_port; - if (typeof audio_ssrc === 'number') body.audio_ssrc = audio_ssrc; - if (typeof video_port === 'number') body.video_port = video_port; - if (typeof video_rtcp_port === 'number') body.video_rtcp_port = video_rtcp_port; - if (typeof video_ssrc === 'number') body.video_ssrc = video_ssrc; - if (typeof data_port === 'number') body.data_port = data_port; + /* [multistream] */ + if (streams && Array.isArray(streams)) { + body.streams = streams; + } + else { + if (typeof audio_port === 'number') body.audio_port = audio_port; + if (typeof audio_rtcp_port === 'number') body.audio_rtcp_port = audio_rtcp_port; + if (typeof audio_ssrc === 'number') body.audio_ssrc = audio_ssrc; + if (typeof video_port === 'number') body.video_port = video_port; + if (typeof video_rtcp_port === 'number') body.video_rtcp_port = video_rtcp_port; + if (typeof video_ssrc === 'number') body.video_ssrc = video_ssrc; + if (typeof video_port_2 === 'number') body.video_port_2 = video_port_2; + if (typeof video_ssrc_2 === 'number') body.video_ssrc_2 = video_ssrc_2; + if (typeof video_port_3 === 'number') body.video_port_3 = video_port_3; + if (typeof video_ssrc_3 === 'number') body.video_ssrc_3 = video_ssrc_3; + if (typeof data_port === 'number') body.data_port = data_port; + } + if (typeof secret === 'string') body.secret = secret; if (typeof admin_key === 'string') body.admin_key = admin_key; const response = await this.message(body); - const { event, data: evtdata } = response._janode || {}; + const { event, data: evtdata } = this._getPluginEvent(response); if (event === PLUGIN_EVENT.RTP_FWD_STARTED) return evtdata; const error = new Error(`unexpected response to ${body.request} request`); @@ -1166,14 +1414,15 @@ class VideoRoomHandle extends Handle { /** * Stop a RTP forwarder in a room. * - * @param {object} params + * @param {Object} params * @param {number|string} params.room - The room where to stop a forwarder * @param {number|string} params.feed - The feed identifier for the forwarder to stop (must be published) * @param {number|string} params.stream - The forwarder identifier as returned by the start forward API * @param {string} [params.secret] - The secret needed for managing the room + * @param {string} [params.admin_key] - The admin key needed for invoking the API * @returns {Promise} */ - async stopForward({ room, feed, stream, secret }) { + async stopForward({ room, feed, stream, secret, admin_key }) { const body = { request: REQUEST_RTP_FWD_STOP, room, @@ -1181,9 +1430,10 @@ class VideoRoomHandle extends Handle { stream_id: stream, }; if (typeof secret === 'string') body.secret = secret; + if (typeof admin_key === 'string') body.admin_key = admin_key; const response = await this.message(body); - const { event, data: evtdata } = response._janode || {}; + const { event, data: evtdata } = this._getPluginEvent(response); if (event === PLUGIN_EVENT.RTP_FWD_STOPPED) return evtdata; const error = new Error(`unexpected response to ${body.request} request`); @@ -1193,7 +1443,7 @@ class VideoRoomHandle extends Handle { /** * List the active forwarders in a room. * - * @param {object} params + * @param {Object} params * @param {number|string} params.room - The room where to list the forwarders * @param {string} [params.secret] - The secret needed for managing the room * @returns {Promise} @@ -1206,7 +1456,7 @@ class VideoRoomHandle extends Handle { if (typeof secret === 'string') body.secret = secret; const response = await this.message(body); - const { event, data: evtdata } = response._janode || {}; + const { event, data: evtdata } = this._getPluginEvent(response); if (event === PLUGIN_EVENT.RTP_FWD_LIST) return evtdata; const error = new Error(`unexpected response to ${body.request} request`); @@ -1220,41 +1470,49 @@ class VideoRoomHandle extends Handle { * {@link https://janus.conf.meetecho.com/docs/videoroom.html} * * @private - * @typedef {object} VideoRoomData + * @typedef {Object} VideoRoomData */ /** * The response event when a publisher has joined. * - * @typedef {object} VIDEOROOM_EVENT_PUB_JOINED + * @typedef {Object} VIDEOROOM_EVENT_PUB_JOINED * @property {number|string} room - The involved room * @property {number|string} feed - The feed identifier * @property {string} [display] - The dsplay name, if available * @property {string} description - A description of the room, if available + * @property {number} private_id - The private id that can be used when subscribing * @property {object[]} publishers - The list of active publishers * @property {number|string} publishers[].feed - The feed of an active publisher - * @property {string} publishers[].display - The display name of an active publisher + * @property {string} [publishers[].display] - The display name of an active publisher + * @property {boolean} [publishers[].talking] - Whether the publisher is talking or not + * @property {string} [publishers[].audiocodec] - The audio codec used by active publisher + * @property {string} [publishers[].videocodec] - The video codec used by active publisher + * @property {boolean} publishers[].simulcast - True if the publisher uses simulcast (VP8 and H.264 only) + * @property {object[]} [publishers[].streams] - [multistream] Streams description as returned by Janus + * @property {boolean} [e2ee] - True if the stream is end-to-end encrypted * @property {RTCSessionDescription} [jsep] - The JSEP answer */ /** * The response event when a subscriber has joined. * - * @typedef {object} VIDEOROOM_EVENT_SUB_JOINED + * @typedef {Object} VIDEOROOM_EVENT_SUB_JOINED * @property {number|string} room - The involved room - * @property {number|string} feed - The published feed identifier - * @property {string} display - The published feed display name + * @property {number|string} [feed] - The published feed identifier + * @property {string} [display] - The published feed display name + * @property {object[]} [streams] - [multistream] Streams description as returned by Janus */ /** * The response event to a participant list request. * - * @typedef {object} VIDEOROOM_EVENT_PARTICIPANTS_LIST + * @typedef {Object} VIDEOROOM_EVENT_PARTICIPANTS_LIST * @property {number|string} room - The involved room * @property {number|string} feed - The current published feed * @property {object[]} participants - The list of current participants * @property {number|string} participants[].feed - Feed identifier of the participant - * @property {string} [participants[].display] - The participant display name, if available + * @property {string} [participants[].display] - The participant's display name, if available * @property {boolean} participants[].publisher - Whether the user is an active publisher in the room * @property {boolean} [participants[].talking] - True if participant is talking */ @@ -1262,7 +1520,7 @@ class VideoRoomHandle extends Handle { /** * The response event for room create request. * - * @typedef {object} VIDEOROOM_EVENT_CREATED + * @typedef {Object} VIDEOROOM_EVENT_CREATED * @property {number|string} room - The created room * @property {boolean} permanent - True if the room has been persisted on the Janus configuratin file */ @@ -1270,7 +1528,7 @@ class VideoRoomHandle extends Handle { /** * The response event for room destroy request. * - * @typedef {object} VIDEOROOM_EVENT_DESTROYED + * @typedef {Object} VIDEOROOM_EVENT_DESTROYED * @property {number|string} room - The destroyed room * @property {boolean} permanent - True if the room has been removed from the Janus configuratin file */ @@ -1278,14 +1536,14 @@ class VideoRoomHandle extends Handle { /** * The response event for room exists request. * - * @typedef {object} VIDEOROOM_EVENT_EXISTS + * @typedef {Object} VIDEOROOM_EVENT_EXISTS * @property {number|string} room - The queried room */ /** * Descriptrion of an active RTP forwarder. * - * @typedef {object} RtpForwarder + * @typedef {Object} RtpForwarder * @property {string} host - The target host * @property {number} [audio_port] - The RTP audio target port * @property {number} [audio_rtcp_port] - The RTCP audio target port @@ -1293,6 +1551,10 @@ class VideoRoomHandle extends Handle { * @property {number} [video_port] - The RTP video target port * @property {number} [video_rtcp_port] - The RTCP video target port * @property {number} [video_stream] - The video forwarder identifier + * @property {number} [video_port_2] - The RTP video target port (simulcast) + * @property {number} [video_stream_2] - The video forwarder identifier (simulcast) + * @property {number} [video_port_3] - The RTP video target port (simulcast) + * @property {number} [video_stream_3] - The video forwarder identifier (simulcast) * @property {number} [data_port] - The datachannels target port * @property {number} [data_stream] - The datachannels forwarder identifier * @property {number} [ssrc] - SSRC this forwarder is using @@ -1304,15 +1566,16 @@ class VideoRoomHandle extends Handle { /** * The response event for RTP forward start request. * - * @typedef {object} VIDEOROOM_EVENT_RTP_FWD_STARTED + * @typedef {Object} VIDEOROOM_EVENT_RTP_FWD_STARTED * @property {number|string} room - The involved room - * @property {RtpForwarder} forwarder - The forwarder object + * @property {RtpForwarder} [forwarder] - The forwarder object + * @property {RtpForwarder[]} [forwarders] - [multistream] The array of forwarders */ /** * The response event for RTP forward stop request. * - * @typedef {object} VIDEOROOM_EVENT_RTP_FWD_STOPPED + * @typedef {Object} VIDEOROOM_EVENT_RTP_FWD_STOPPED * @property {number|string} room - The involved room * @property {number|string} feed - The feed identifier being forwarded * @property {number} stream - The forwarder identifier @@ -1321,7 +1584,7 @@ class VideoRoomHandle extends Handle { /** * The response event for RTP forwarders list request. * - * @typedef {object} VIDEOROOM_EVENT_RTP_FWD_LIST + * @typedef {Object} VIDEOROOM_EVENT_RTP_FWD_LIST * @property {number|string} room - The involved room * @property {object[]} forwarders - The list of forwarders * @property {number|string} forwarders[].feed - The feed that is being forwarded @@ -1331,43 +1594,46 @@ class VideoRoomHandle extends Handle { /** * The response event for videoroom list request. * - * @typedef {object} VIDEOROOM_EVENT_LIST + * @typedef {Object} VIDEOROOM_EVENT_LIST * @property {object[]} list - The list of the room as returned by Janus */ /** * The response event for ACL tokens edit (allowed) request. * - * @typedef {object} VIDEOROOM_EVENT_ALLOWED + * @typedef {Object} VIDEOROOM_EVENT_ALLOWED * @property {string[]} list - The updated, complete, list of allowed tokens */ /** * The response event for publisher/subscriber configure request. * - * @typedef {object} VIDEOROOM_EVENT_CONFIGURED + * @typedef {Object} VIDEOROOM_EVENT_CONFIGURED * @property {number|string} room - The involved room * @property {number|string} feed - The feed identifier * @property {string} [display] - The display name, if available * @property {boolean} [restart] - True if the request had it true * @property {boolean} [update] - True if the request had it true * @property {string} configured - A string with the value returned by Janus + * @property {object[]} [streams] - [multistream] Streams description as returned by Janus + * @property {boolean} [e2ee] - True if the stream is end-to-end encrypted * @property {RTCSessionDescription} [jsep] - The JSEP answer */ /** * The response event for subscriber start request. * - * @typedef {object} VIDEOROOM_EVENT_STARTED + * @typedef {Object} VIDEOROOM_EVENT_STARTED * @property {number|string} room - The involved room - * @property {number|string} feed - The feed that started + * @property {number|string} [feed] - The feed that started + * @property {boolean} [e2ee] - True if started stream is e2ee * @property {string} started - A string with the value returned by Janus */ /** * The response event for subscriber pause request. * - * @typedef {object} VIDEOROOM_EVENT_PAUSED + * @typedef {Object} VIDEOROOM_EVENT_PAUSED * @property {number|string} room - The involved room * @property {number|string} feed - The feed that has been paused * @property {string} paused - A string with the value returned by Janus @@ -1376,18 +1642,19 @@ class VideoRoomHandle extends Handle { /** * The response event for subscriber switch request. * - * @typedef {object} VIDEOROOM_EVENT_SWITCHED + * @typedef {Object} VIDEOROOM_EVENT_SWITCHED * @property {number|string} room - The involved room - * @property {number|string} from_feed - The feed that has been switched from - * @property {number|string} to_feed - The feed that has been switched to + * @property {number|string} [from_feed] - The feed that has been switched from + * @property {number|string} [to_feed] - The feed that has been switched to * @property {string} switched - A string with the value returned by Janus - * @property {string} display - The display name of the new feed + * @property {string} [display] - The display name of the new feed + * @property {object[]} [streams] - [multistream] The updated streams array */ /** * The response event for publisher unpublish request. * - * @typedef {object} VIDEOROOM_EVENT_UNPUBLISHED + * @typedef {Object} VIDEOROOM_EVENT_UNPUBLISHED * @property {number|string} room - The involved room * @property {number|string} feed - The feed that unpublished */ @@ -1395,7 +1662,7 @@ class VideoRoomHandle extends Handle { /** * The response event for publiher/subscriber leave request. * - * @typedef {object} VIDEOROOM_EVENT_LEAVING + * @typedef {Object} VIDEOROOM_EVENT_LEAVING * @property {number|string} room - The involved room * @property {number|string} feed - The feed that left * @property {string} [reason] - An optional string with the reason of the leaving @@ -1404,7 +1671,7 @@ class VideoRoomHandle extends Handle { /** * The response event for the kick request. * - * @typedef {object} VIDEOROOM_EVENT_KICKED + * @typedef {Object} VIDEOROOM_EVENT_KICKED * @property {number|string} room - The involved room * @property {number|string} feed - The feed that has been kicked */ @@ -1412,28 +1679,42 @@ class VideoRoomHandle extends Handle { /** * The response event for the recording enabled request. * - * @typedef {object} VIDEOROOM_EVENT_RECORDING_ENABLED_STATE + * @typedef {Object} VIDEOROOM_EVENT_RECORDING_ENABLED_STATE * @property {number|string} room - The involved room * @property {boolean} recording - Whether or not the room recording is now enabled */ +/** + * [multistream] The response event for update subscriber request. + * + * @typedef {Object} VIDEOROOM_EVENT_UPDATED + * @property {number|string} room - The involved room + * @property {RTCSessionDescription} [jsep] - The updated JSEP offer + * @property {object[]} streams - List of the updated streams in this subscription + */ + /** * The exported plugin descriptor. * - * @type {object} + * @type {Object} * @property {string} id - The plugin identifier used when attaching to Janus * @property {module:videoroom-plugin~VideoRoomHandle} Handle - The custom class implementing the plugin - * @property {object} EVENT - The events emitted by the plugin - * @property {string} EVENT.VIDEOROOM_PUB_PEER_JOINED {@link module:videoroom-plugin~VIDEOROOM_PUB_PEER_JOINED} - * @property {string} EVENT.VIDEOROOM_PUB_LIST {@link module:videoroom-plugin~VIDEOROOM_PUB_LIST} - * @property {string} EVENT.VIDEOROOM_DESTROYED {@link module:videoroom-plugin~VIDEOROOM_DESTROYED} - * @property {string} EVENT.VIDEOROOM_UNPUBLISHED {@link module:videoroom-plugin~VIDEOROOM_UNPUBLISHED} - * @property {string} EVENT.VIDEOROOM_LEAVING {@link module:videoroom-plugin~VIDEOROOM_LEAVING} - * @property {string} EVENT.VIDEOROOM_DISPLAY {@link module:videoroom-plugin~VIDEOROOM_DISPLAY} - * @property {string} EVENT.VIDEOROOM_KICKED {@link module:videoroom-plugin~VIDEOROOM_KICKED} - * @property {string} EVENT.VIDEOROOM_RECORDING_ENABLED_STATE {@link module:videoroom-plugin~VIDEOROOM_RECORDING_ENABLED_STATE} - * @property {string} EVENT.VIDEOROOM_TALKING {@link module:videoroom-plugin~VIDEOROOM_TALKING} - * @property {string} EVENT.VIDEOROOM_ERROR {@link module:videoroom-plugin~VIDEOROOM_ERROR} + * @property {Object} EVENT - The events emitted by the plugin + * @property {string} EVENT.VIDEOROOM_PUB_PEER_JOINED {@link module:videoroom-plugin~VideoRoomHandle#event:VIDEOROOM_PUB_PEER_JOINED VIDEOROOM_PUB_PEER_JOINED} + * @property {string} EVENT.VIDEOROOM_PUB_LIST {@link module:videoroom-plugin~VideoRoomHandle#event:VIDEOROOM_PUB_LIST VIDEOROOM_PUB_LIST} + * @property {string} EVENT.VIDEOROOM_DESTROYED {@link module:videoroom-plugin~VideoRoomHandle#event:VIDEOROOM_DESTROYED VIDEOROOM_DESTROYED} + * @property {string} EVENT.VIDEOROOM_UNPUBLISHED {@link module:videoroom-plugin~VideoRoomHandle#event:VIDEOROOM_UNPUBLISHED VIDEOROOM_UNPUBLISHED} + * @property {string} EVENT.VIDEOROOM_LEAVING {@link module:videoroom-plugin~VideoRoomHandle#event:VIDEOROOM_LEAVING VIDEOROOM_LEAVING} + * @property {string} EVENT.VIDEOROOM_DISPLAY {@link module:videoroom-plugin~VideoRoomHandle#event:VIDEOROOM_DISPLAY VIDEOROOM_DISPLAY} + * @property {string} EVENT.VIDEOROOM_CONFIGURED {@link module:videoroom-plugin~VideoRoomHandle#event:VIDEOROOM_CONFIGURED VIDEOROOM_CONFIGURED} + * @property {string} EVENT.VIDEOROOM_SLOWLINK {@link module:videoroom-plugin~VideoRoomHandle#event:VIDEOROOM_SLOWLINK VIDEOROOM_SLOWLINK} + * @property {string} EVENT.VIDEOROOM_TALKING {@link module:videoroom-plugin~VideoRoomHandle#event:VIDEOROOM_TALKING VIDEOROOM_TALKING} + * @property {string} EVENT.VIDEOROOM_KICKED {@link module:videoroom-plugin~VideoRoomHandle#event:VIDEOROOM_KICKED VIDEOROOM_KICKED} + * @property {string} EVENT.VIDEOROOM_RECORDING_ENABLED_STATE {@link module:videoroom-plugin~VideoRoomHandle#event:VIDEOROOM_RECORDING_ENABLED_STATE VIDEOROOM_RECORDING_ENABLED_STATE} + * @property {string} EVENT.VIDEOROOM_SC_SUBSTREAM_LAYER {@link module:videoroom-plugin~VideoRoomHandle#event:VIDEOROOM_SC_SUBSTREAM_LAYER VIDEOROOM_SC_SUBSTREAM_LAYER} + * @property {string} EVENT.VIDEOROOM_SC_TEMPORAL_LAYERS {@link module:videoroom-plugin~VideoRoomHandle#event:VIDEOROOM_SC_TEMPORAL_LAYERS VIDEOROOM_SC_TEMPORAL_LAYERS} + * @property {string} EVENT.VIDEOROOM_UPDATED {@link module:videoroom-plugin~VideoRoomHandle#event:VIDEOROOM_UPDATED VIDEOROOM_UPDATED} + * @property {string} EVENT.VIDEOROOM_ERROR {@link module:videoroom-plugin~VideoRoomHandle#event:VIDEOROOM_ERROR VIDEOROOM_ERROR} */ export default { id: PLUGIN_ID, @@ -1443,7 +1724,7 @@ export default { * A peer has joined theh room (notify-joining). * * @event module:videoroom-plugin~VideoRoomHandle#event:VIDEOROOM_PUB_PEER_JOINED - * @type {object} + * @type {Object} * @property {number|string} room - The involved room * @property {number|string} feed - The feed identifier that joined * @property {string} display - The display name of the peer @@ -1454,12 +1735,17 @@ export default { * Active publishers list updated. * * @event module:videoroom-plugin~VideoRoomHandle#event:VIDEOROOM_PUB_LIST - * @type {object} + * @type {Object} * @property {number|string} room - The involved room * @property {number|string} feed - The current feed identifier * @property {object[]} publishers - List of the new publishers * @property {number|string} publishers[].feed - Feed identifier of the new publisher * @property {string} publishers[].display - Display name of the new publisher + * @property {boolean} [publishers[].talking] - Whether the publisher is talking or not + * @property {string} [publishers[].audiocodec] - The audio codec used by active publisher + * @property {string} [publishers[].videocodec] - The video codec used by active publisher + * @property {boolean} publishers[].simulcast - True if the publisher uses simulcast (VP8 and H.264 only) + * @property {object[]} [publishers[].streams] - [multistream] Streams description as returned by Janus */ VIDEOROOM_PUB_LIST: PLUGIN_EVENT.PUB_LIST, @@ -1467,7 +1753,9 @@ export default { * The videoroom has been destroyed. * * @event module:videoroom-plugin~VideoRoomHandle#event:VIDEOROOM_DESTROYED - * @type {module:videoroom-plugin~VIDEOROOM_EVENT_DESTROYED} + * @type {Object} + * @property {number|string} room - The destroyed room + * @property {boolean} permanent - True if the room has been removed from the Janus configuratin file */ VIDEOROOM_DESTROYED: PLUGIN_EVENT.DESTROYED, @@ -1475,7 +1763,9 @@ export default { * A feed has been unpublished. * * @event module:videoroom-plugin~VideoRoomHandle#event:VIDEOROOM_UNPUBLISHED - * @type {module:videoroom-plugin~VIDEOROOM_EVENT_UNPUBLISHED} + * @type {Object} + * @property {number|string} room - The involved room + * @property {number|string} feed - The feed that unpublished */ VIDEOROOM_UNPUBLISHED: PLUGIN_EVENT.UNPUBLISHED, @@ -1483,7 +1773,10 @@ export default { * A peer has left the room. * * @event module:videoroom-plugin~VideoRoomHandle#event:VIDEOROOM_LEAVING - * @type {module:videoroom-plugin~VIDEOROOM_EVENT_LEAVING} + * @type {Object} + * @property {number|string} room - The involved room + * @property {number|string} feed - The feed that left + * @property {string} [reason] - An optional string with the reason of the leaving */ VIDEOROOM_LEAVING: PLUGIN_EVENT.LEAVING, @@ -1491,7 +1784,7 @@ export default { * A participant has changed the display name. * * @event module:videoroom-plugin~VideoRoomHandle#event:VIDEOROOM_DISPLAY - * @type {object} + * @type {Object} * @property {number|string} room - The involved room * @property {number|string} feed - The feed of the peer that change display name * @property {string} display - The new display name of the peer @@ -1502,15 +1795,24 @@ export default { * A handle received a configured event. * * @event module:videoroom-plugin~VideoRoomHandle#event:VIDEOROOM_CONFIGURED - * @type {module:videoroom-plugin~VIDEOROOM_EVENT_CONFIGURED} + * @type {Object} + * @property {number|string} room - The involved room + * @property {number|string} feed - The feed identifier + * @property {string} [display] - The display name, if available + * @property {boolean} [restart] - True if the request had it true + * @property {boolean} [update] - True if the request had it true + * @property {string} configured - A string with the value returned by Janus + * @property {object[]} [streams] - [multistream] Streams description as returned by Janus + * @property {boolean} [e2ee] - True if the stream is end-to-end encrypted + * @property {RTCSessionDescription} [jsep] - The JSEP answer */ VIDEOROOM_CONFIGURED: PLUGIN_EVENT.CONFIGURED, /** * A handle received a slow link notification. * - * @event module:videoroom-plugin~VideoRoomHandle#event:VIDEOROOM_DISPLAY - * @type {object} + * @event module:videoroom-plugin~VideoRoomHandle#event:VIDEOROOM_SLOWLINK + * @type {Object} * @property {number|string} room - The involved room * @property {number|string} feed - The feed of the peer that change display name * @property {number} bitrate - The current bitrate cap for the participant @@ -1521,7 +1823,7 @@ export default { * Notify if the current user is talking. * * @event module:videoroom-plugin~VideoRoomHandle#event:VIDEOROOM_TALKING - * @type {object} + * @type {Object} * @property {number|string} room - The involved room * @property {number|string} feed - The feed of the peer this talking notification refers to * @property {boolean} talking - True if the participant is talking @@ -1533,7 +1835,9 @@ export default { * A feed has been kicked out. * * @event module:videoroom-plugin~VideoRoomHandle#event:VIDEOROOM_KICKED - * @type {module:videoroom-plugin~VIDEOROOM_EVENT_KICKED} + * @type {Object} + * @property {number|string} room - The involved room + * @property {number|string} feed - The feed that has been kicked */ VIDEOROOM_KICKED: PLUGIN_EVENT.KICKED, @@ -1541,7 +1845,9 @@ export default { * Conference recording has been enabled or disabled. * * @event module:videoroom-plugin~VideoRoomHandle#event:VIDEOROOM_RECORDING_ENABLED_STATE - * @type {module:videoroom-plugin~VIDEOROOM_EVENT_RECORDING_ENABLED_STATE} + * @type {Object} + * @property {number|string} room - The involved room + * @property {boolean} recording - Whether or not the room recording is now enabled */ VIDEOROOM_RECORDING_ENABLED_STATE: PLUGIN_EVENT.RECORDING_ENABLED_STATE, @@ -1549,7 +1855,7 @@ export default { * A switch to a different simulcast substream has been completed. * * @event module:videoroom-plugin~VideoRoomHandle#event:VIDEOROOM_SC_SUBSTREAM_LAYER - * @type {object} + * @type {Object} * @property {number|string} room - The involved room * @property {number|string} feed - The feed of the peer this notification refers to * @property {number} sc_substream_layer - The new simuclast substream layer relayed @@ -1560,7 +1866,7 @@ export default { * A switch to a different number of simulcast temporal layers has been completed. * * @event module:videoroom-plugin~VideoRoomHandle#event:VIDEOROOM_SC_TEMPORAL_LAYERS - * @type {object} + * @type {Object} * @property {number|string} room - The involved room * @property {number|string} feed - The feed of the peer this switch notification refers to * @property {number} sc_temporal_layers - The new number of simuclast teporal layers relayed @@ -1571,10 +1877,10 @@ export default { * A multistream subscription has been updated. * * @event module:videoroom-plugin~VideoRoomHandle#event:VIDEOROOM_UPDATED - * @type {object} + * @type {Object} * @property {number|string} room - The involved room - * @param {RTCSessionDescription} [jsep] - The updated JSEP offer - * @param {object[]} streams - List of the streams in this subscription + * @property {RTCSessionDescription} [jsep] - The updated JSEP offer + * @property {object[]} streams - List of the updated streams in this subscription */ VIDEOROOM_UPDATED: PLUGIN_EVENT.UPDATED, diff --git a/src/protocol.js b/src/protocol.js index 00a0dde..8e75582 100644 --- a/src/protocol.js +++ b/src/protocol.js @@ -13,7 +13,6 @@ * * Some helper methods related to the protocols are defined here too. * @module protocol - * @private */ /** @@ -57,6 +56,7 @@ export const JANUS = { EVENT: { EVENT: 'event', DETACHED: 'detached', + ICE_FAILED: 'ice-failed', HANGUP: 'hangup', MEDIA: 'media', TIMEOUT: 'timeout', @@ -76,6 +76,20 @@ export const JANUS = { }, }; +/** + * @typedef {Object} JanodeCoreEvents + * @property {string} CONNECTION_CLOSED - {@link module:connection~Connection#event:CONNECTION_CLOSED CONNECTION_CLOSED} + * @property {string} SESSION_DESTROYED - {@link module:session~Session#event:SESSION_DESTROYED SESSION_DESTROYED} + * @property {string} HANDLE_DETACHED - {@link module:handle~Handle#event:HANDLE_DETACHED HANDLE_DETACHED} + * @property {string} HANDLE_ICE_FAILED - {@link module:handle~Handle#event:HANDLE_ICE_FAILED HANDLE_ICE_FAILED} + * @property {string} HANDLE_HANGUP - {@link module:handle~Handle#event:HANDLE_HANGUP HANDLE_HANGUP} + * @property {string} HANDLE_MEDIA - {@link module:handle~Handle#event:HANDLE_MEDIA HANDLE_MEDIA} + * @property {string} HANDLE_WEBRTCUP - {@link module:handle~Handle#event:HANDLE_WEBRTCUP HANDLE_WEBRTCUP} + * @property {string} HANDLE_SLOWLINK - {@link module:handle~Handle#event:HANDLE_SLOWLINK HANDLE_SLOWLINK} + * @property {string} HANDLE_TRICKLE - {@link module:handle~Handle#event:HANDLE_TRICKLE HANDLE_TRICKLE} + * @property {string} CONNECTION_ERROR - {@link module:connection~Connection#event:CONNECTION_ERROR CONNECTION_ERROR} + */ + /** * Janode protocol constants * @@ -84,11 +98,14 @@ export const JANUS = { export const JANODE = { /** * Janode core events. + * + * @type {JanodeCoreEvents} */ EVENT: { CONNECTION_CLOSED: 'connection_closed', SESSION_DESTROYED: 'session_destroyed', HANDLE_DETACHED: 'handle_detached', + HANDLE_ICE_FAILED: 'handle_ice_failed', HANDLE_HANGUP: 'handle_hangup', HANDLE_MEDIA: 'handle_media', HANDLE_WEBRTCUP: 'handle_webrtcup', @@ -102,7 +119,7 @@ export const JANODE = { * Check if a message from Janus is a definitive response. * * @private - * @param {object} data - The data from Janus + * @param {Object} data - The data from Janus * @returns {boolean} True if the check succeeds */ export const isResponseData = data => { @@ -116,7 +133,7 @@ export const isResponseData = data => { * Check if a message from Janus is an event. * * @private - * @param {object} data - The data from Janus + * @param {Object} data - The data from Janus * @returns {boolean} True if the check succeeds */ export const isEventData = data => { @@ -130,7 +147,7 @@ export const isEventData = data => { * Check if a message from Janus is an error. * * @private - * @param {object} data - The data from Janus + * @param {Object} data - The data from Janus * @returns {boolean} True if the check succeeds */ export const isErrorData = data => { @@ -144,7 +161,7 @@ export const isErrorData = data => { * Check if a message from Janus is a timeout notification. * * @private - * @param {object} data - The data from Janus + * @param {Object} data - The data from Janus * @returns {boolean} True if the check succeeds */ export const isTimeoutData = data => { @@ -158,7 +175,7 @@ export const isTimeoutData = data => { * Check if a message from Janus is an ack. * * @private - * @param {object} data - The data from Janus + * @param {Object} data - The data from Janus * @returns {boolean} True if the check succeeds */ export const isAckData = data => { diff --git a/src/session.js b/src/session.js index f908462..3409656 100644 --- a/src/session.js +++ b/src/session.js @@ -3,7 +3,7 @@ /** * This module contains the Session class definition. * @module session - * @access private + * @private */ import { EventEmitter } from 'events'; @@ -38,7 +38,7 @@ class Session extends EventEmitter { * The transaction manager used by this session. * * @private - * @type {TransactionManager} + * @type {module:tmanager~TransactionManager} */ this._tm = connection._tm; @@ -145,7 +145,6 @@ class Session extends EventEmitter { * The session has been destroyed. * * @event module:session~Session#event:SESSION_DESTROYED - * @type {object} * @property {number} id - The session identifier */ this.emit(JANODE.EVENT.SESSION_DESTROYED, { id: this.id }); @@ -240,7 +239,7 @@ class Session extends EventEmitter { * the transaction will be closed. * * @private - * @param {object} janus_message + * @param {Object} janus_message */ _handleMessage(janus_message) { const { sender, janus, transaction } = janus_message; @@ -251,7 +250,7 @@ class Session extends EventEmitter { const handle = this._handles.get(sender); /* If the handle is missing notifies the user */ if (!handle) { - if (janus === JANUS.EVENT.HANDLE_DETACHED_PLUGIN) { + if (janus === JANUS.EVENT.DETACHED) { /* In case of duplicate "detached" try to not pollute the logs */ Logger.verbose(`${LOG_NS} ${this.name} handle ${sender} not found for incoming message "${janus}"`); return; @@ -325,7 +324,7 @@ class Session extends EventEmitter { * Decorate request with session id and transaction (if missing). * * @private - * @param {object} request + * @param {Object} request */ _decorateRequest(request) { request.transaction = request.transaction || getNumericID(); @@ -335,8 +334,8 @@ class Session extends EventEmitter { /** * Send a request from this session. * - * @param {object} request - * @returns {Promise} A promise resolving with the response + * @param {Object} request + * @returns {Promise} A promise resolving with the response */ async sendRequest(request) { /* Input check */ diff --git a/src/tmanager.js b/src/tmanager.js index c6d2a4c..f49629c 100644 --- a/src/tmanager.js +++ b/src/tmanager.js @@ -10,9 +10,9 @@ /** * An object describing a pending transaction stored in the manager. * - * @typedef {object} PendingTransaction + * @typedef {Object} PendingTransaction * @property {string} id - The transaction identifier - * @property {object} owner - A reference to the object that created the transaction + * @property {Object} owner - A reference to the object that created the transaction * @property {string} request - The janus request for the pending transaction * @property {function} done - The success callback * @property {function} error - The error callback @@ -130,13 +130,14 @@ class TransactionManager { * Create a new transaction if id does not exist in the table and add it to the TM. * * @param {string} id - The transaction identifier - * @param {object} owner - A reference to the object that created the transaction + * @param {Object} owner - A reference to the object that created the transaction * @param {string} request - The janus request for the pending transaction * @param {function} done - The success callback * @param {function} error - The error callback + * @param {number} [timeout_ms=0] - The timeout of the transaction * @returns {PendingTransaction|void} The newly created transaction, or nothing if the id already exists */ - createTransaction(id, owner, request, done, error) { + createTransaction(id, owner, request, done, error, timeout_ms = 0) { if (this.has(id)) return; const tx = { id, @@ -145,6 +146,15 @@ class TransactionManager { done, error, }; + if (timeout_ms > 0) { + const timeout = setTimeout(_ => { + this.delete(id); + error(new Error('Transaction timed out!')); + Logger.error(`${LOG_NS} [${owner.id}] closed with timeout transaction ${id}, request "${request}"`); + }, timeout_ms); + tx.timeout = timeout; + } + this.set(id, tx); Logger.verbose(`${LOG_NS} [${tx.owner.id}] created new transaction ${id}, request "${tx.request}"`); return tx; @@ -155,14 +165,15 @@ class TransactionManager { * The closed transaction will be removed from the internal table and the error cb will be invoked with the error string. * * @param {string} id - The transaction identifier - * @param {object} owner - A reference to the transaction owner - * @param {string} error - The error string + * @param {Object} owner - A reference to the transaction owner + * @param {Error} error - The error object * @returns {PendingTransaction|void} The closed transaction, or nothing if the id does not exist or the owner does not match */ closeTransactionWithError(id, owner, error) { const tx = this.get(id); if (!tx) return; if (tx.owner !== owner) return; + clearTimeout(tx.timeout); this.delete(id); tx.error(error); Logger.verbose(`${LOG_NS} [${tx.owner.id}] closed with error transaction ${id}, request "${tx.request}"`); @@ -174,8 +185,8 @@ class TransactionManager { * If an owner is specified only the owner's transaction will be closed. * The closed transactions will be removed from the internal table. * - * @param {object} [owner] - A reference to the transaction owner - * @param {string} error - The error string + * @param {Object} [owner] - A reference to the transaction owner + * @param {Error} error - The error object */ closeAllTransactionsWithError(owner, error) { for (const [_, pendingTx] of this.transactions) { @@ -189,14 +200,15 @@ class TransactionManager { * The closed transaction will be removed from the internal table and the success cb will be invoked with the specified data. * * @param {string} id - The transaction identifier - * @param {object} owner - A reference to the transaction owner - * @param {*} data - The success callback data + * @param {Object} owner - A reference to the transaction owner + * @param {Object} data - The success callback data * @returns {PendingTransaction|void} The closed transaction, or nothing if the id does not exist or the owner does not match */ closeTransactionWithSuccess(id, owner, data) { const tx = this.get(id); if (!tx) return; if (tx.owner !== owner) return; + clearTimeout(tx.timeout); this.delete(id); tx.done(data); Logger.verbose(`${LOG_NS} [${tx.owner.id}] closed with success transaction ${id}, request "${tx.request}"`); diff --git a/src/transport-unix.js b/src/transport-unix.js index 95915f9..eaaf83c 100644 --- a/src/transport-unix.js +++ b/src/transport-unix.js @@ -3,7 +3,7 @@ /** * This module contains the Unix Sockets transport implementation. * @module transport-unix - * @access private + * @private */ import { Buffer } from 'buffer'; @@ -16,6 +16,11 @@ import Logger from './utils/logger.js'; const LOG_NS = '[transport-unix.js]'; import { delayOp } from './utils/utils.js'; +/** + * @typedef {Object} UnixDgramSocket + * @link https://www.npmjs.com/package/unix-dgram#api + */ + /** * Class representing a connection through Unix dgram sockets transport.
* @@ -42,7 +47,8 @@ class TransportUnix { /** * The internal Unix Socket. * - * @type {module:unix-dgram~Socket} + * @type {UnixDgramSocket} + * @link https://www.npmjs.com/package/unix-dgram#api */ this._socket = null; @@ -161,7 +167,7 @@ class TransportUnix { this._socket = socket; - try { unlinkSync(this._local_bind); } catch (error) { } + try { unlinkSync(this._local_bind); } catch (_error) { } socket.bind(this._local_bind); }); } @@ -171,7 +177,7 @@ class TransportUnix { * In case of error retry the connection with another address from the available pool. * If maximum number of attempts is reached, throws an error. * - * @returns {module:unix-dgram~Socket} The unix socket + * @returns {UnixDgramSocket} The unix socket */ async _attemptOpen() { /* Reset status at every attempt */ @@ -189,7 +195,7 @@ class TransportUnix { /* In case of error notifies the user, but try with another address */ this._attempts++; /* Get the max number of attempts from the configuration */ - if (this._attempts >= this._connection._config.getMaxRetries()) { + if (this._attempts >= this._connection._config.getMaxRetries() + 1) { this._opening = false; const err = new Error('attempt limit exceeded'); Logger.error(`${LOG_NS} ${this.name} socket connection failed, ${err.message}`); @@ -291,8 +297,8 @@ class TransportUnix { * Send a request from this connection. * It is called from the parent connection. * - * @param {object} request - The request to be sent - * @returns {Promise} A promise resolving with a response from Janus + * @param {Object} request - The request to be sent + * @returns {Promise} A promise resolving with a response from Janus */ async send(request) { /* Check connection status */ diff --git a/src/transport-ws.js b/src/transport-ws.js index 5e1e7cd..2ed9447 100644 --- a/src/transport-ws.js +++ b/src/transport-ws.js @@ -3,7 +3,7 @@ /** * This module contains the WebSocket transport implementation. * @module transport-ws - * @access private + * @private */ /* Isomorphic implementation of WebSocket */ @@ -94,7 +94,7 @@ class TransportWs { /** * The task of the peridic ws ping. * - * @type {*} + * @type {Object} */ this._ping_task = null; @@ -123,7 +123,7 @@ class TransportWs { Logger.info(`${LOG_NS} ${this.name} trying connection with ${this._connection._address_iterator.currElem().url}`); return new Promise((resolve, reject) => { - const wsOptions = this._connection._config.wsOptions() || { }; + const wsOptions = this._connection._config.wsOptions() || {}; if (!wsOptions.handshakeTimeout) wsOptions.handshakeTimeout = 5000; const ws = new WebSocket( @@ -146,9 +146,10 @@ class TransportWs { /* Start cleanup */ /* Cancel the KA task */ this._unsetPingTask(); - this._connection._signalClose(this._closing); + const wasClosing = this._closing; this._closing = false; this._closed = true; + this._connection._signalClose(wasClosing); /* removeAllListeners is only supported on the node ws module */ if (typeof this._ws.removeAllListeners === 'function') this._ws.removeAllListeners(); }, { once: true }); @@ -196,7 +197,7 @@ class TransportWs { /* In case of error notifies the user, but try with another address */ this._attempts++; /* Get the max number of attempts from the configuration */ - if (this._attempts >= this._connection._config.getMaxRetries()) { + if (this._attempts >= this._connection._config.getMaxRetries() + 1) { this._opening = false; const err = new Error('attempt limit exceeded'); Logger.error(`${LOG_NS} ${this.name} connection failed, ${err.message}`); @@ -377,8 +378,8 @@ class TransportWs { * Wraps with a promise the standard WebSocket API "send". * It is called from the parent connection. * - * @param {object} request - The request to be sent - * @returns {Promise} A promise resolving with a response from Janus + * @param {Object} request - The request to be sent + * @returns {Promise} A promise resolving with a response from Janus */ async send(request) { /* Check connection status */ diff --git a/src/utils/logger.js b/src/utils/logger.js index 22cfb0b..027a19a 100644 --- a/src/utils/logger.js +++ b/src/utils/logger.js @@ -7,30 +7,51 @@ * * Default logging level is "info". * @module logger - * @access private + * @private */ import { getCliArgument } from './utils.js'; -const LEVELS_IDX = ['none', 'error', 'warn', 'info', 'verb', 'debug'].reduce((obj, lvl, idx) => { +const LEVELS = ['none', 'error', 'warning', 'info', 'verbose', 'debug']; +const LEVELS_IDX = LEVELS.reduce((obj, lvl, idx) => { obj[lvl] = idx; return obj; }, {}); const DEFAULT_LEVEL = 'info'; -let log_verbosity = getCliArgument('janode-log', 'string', DEFAULT_LEVEL); - -const printout = (msg_verbosity, console_fn, ...args) => { - if (LEVELS_IDX[msg_verbosity] > LEVELS_IDX[log_verbosity]) return; - const ts = (new Date()).toISOString(); - const prefix = `${ts} - ${msg_verbosity.toUpperCase().padEnd(5, ' ')}:`; - console_fn(prefix, ...args); -}; /** - * The logger used by Janode. + * Class representing a Janode logger.
+ * + * Users are not expected to create Logger instances, but insted use the Janode.Logger instance.
+ * + * @hideconstructor */ -const Logger = { +class Logger { + constructor(lvl = DEFAULT_LEVEL) { + /** + * The current verbosity level of the logger. + * @type {string} + * @private + */ + this._log_verbosity = this.setLevel(lvl); + } + + /** + * @private + */ + _printout(msg_verbosity, console_fn, ...args) { + if (LEVELS_IDX[msg_verbosity] > LEVELS_IDX[this._log_verbosity]) return; + const ts = (new Date()).toISOString(); + const prefix = `${ts} - ${msg_verbosity.toUpperCase().padEnd(8, ' ')}:`; + if (args.length === 1 && typeof args[0] === 'function') { + const msg = (args[0])(); + console_fn(prefix, msg); + } + else + console_fn(prefix, ...args); + } + /** * Debug logging. * It is a wrapper for `console.debug()`. @@ -38,7 +59,9 @@ const Logger = { * @function * @param {...any} args */ - debug: (...args) => printout('debug', console.debug, ...args), + debug(...args) { + this._printout('debug', console.debug, ...args); + } /** * Verbose logging. @@ -47,7 +70,19 @@ const Logger = { * @function * @param {...any} args */ - verbose: (...args) => printout('verb', console.debug, ...args), + verbose(...args) { + this._printout('verbose', console.debug, ...args); + } + + /** + * Alias for verbose. + * + * @function + * @param {...any} args + */ + verb(...args) { + this.verbose(...args); + } /** * Info logging (default). @@ -56,7 +91,9 @@ const Logger = { * @function * @param {...any} args */ - info: (...args) => printout('info', console.info, ...args), + info(...args) { + this._printout('info', console.info, ...args); + } /** * Warning logging. @@ -65,7 +102,19 @@ const Logger = { * @function * @param {...any} args */ - warn: (...args) => printout('warn', console.warn, ...args), + warning(...args) { + this._printout('warning', console.warn, ...args); + } + + /** + * Alias for warning. + * + * @function + * @param {...any} args + */ + warn(...args) { + this.warning(...args); + } /** * Error logging. @@ -74,7 +123,9 @@ const Logger = { * @function * @param {...any} args */ - error: (...args) => printout('error', console.error, ...args), + error(...args) { + this._printout('error', console.error, ...args); + } /** * Set level of logger. @@ -83,13 +134,32 @@ const Logger = { * @param {"debug"|"verb"|"info"|"warn"|"error"|"none"} lvl * @returns {string} The current level */ - setLevel: (lvl = '') => { + setLevel(lvl = '') { lvl = lvl.toLowerCase(); - if (lvl === 'verbose') lvl = 'verb'; - if (lvl === 'warning') lvl = 'warn'; - if (typeof LEVELS_IDX[lvl] === 'number') log_verbosity = lvl; - return log_verbosity; + if (lvl === 'verb') lvl = 'verbose'; + if (lvl === 'warn') lvl = 'warning'; + if (typeof LEVELS_IDX[lvl] === 'number') { + this._log_verbosity = lvl; + } + else { + this._log_verbosity = DEFAULT_LEVEL; + } + return this._log_verbosity; } -}; -export default Logger; \ No newline at end of file + /** + * Alias for setLevel. + * + * @function + * @param {"debug"|"verb"|"info"|"warn"|"error"|"none"} lvl + * @returns {string} The current level + */ + setLogLevel(lvl = '') { + return this.setLevel(lvl); + } +} + +const cli_log_verbosity = getCliArgument('janode-log', 'string', DEFAULT_LEVEL); +const loggerInstance = new Logger(cli_log_verbosity); + +export default loggerInstance; \ No newline at end of file diff --git a/src/utils/utils.js b/src/utils/utils.js index 4d2f1cf..780ba40 100644 --- a/src/utils/utils.js +++ b/src/utils/utils.js @@ -6,13 +6,6 @@ * @private */ - -/** - * @typedef {object} CircularIterator - * @property {function} nextElem - Advance the iterator and get the new element - * @property {function} currElem - Get the current element without advancing - */ - /** * Generate a random alpha-numeric string with a given length. * @@ -48,6 +41,12 @@ export const getNumericID = (_ => { }; })(); +/** + * @typedef {Object} CircularIterator + * @property {function} nextElem - Advance the iterator and get the new element + * @property {function} currElem - Get the current element without advancing + */ + /** * Generate a circular iterator from an array. * @@ -59,10 +58,13 @@ export const newIterator = list => { const len = l.length; var i = 0; - return { + /** @type {CircularIterator} */ + const iterator = { nextElem: _ => l[i++ % len], currElem: _ => l[i % len], }; + + return iterator; }; /** @@ -81,7 +83,7 @@ export const delayOp = ms => { * Check if a url string contains one of the protocols in a white list. * * @param {string} url_string - The url string to be checked - * @param {Array} admitted - The admitted protocols + * @param {Array} admitted - The admitted protocols * @returns {boolean} True if the check succeeds */ export const checkUrl = (url_string, admitted) => { @@ -89,7 +91,7 @@ export const checkUrl = (url_string, admitted) => { /* 'slice(0, -1)' removes the colon at the last position */ const protocol = (new URL(url_string)).protocol.slice(0, -1); return admitted.includes(protocol); - } catch (e) { } + } catch (_error) { } return false; };