diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 00000000..0c40e4ca --- /dev/null +++ b/.eslintignore @@ -0,0 +1,6 @@ +# NOTE: This file and .prettierignore must contain same paths. + +/.cache +/coverage +/doc +/lib diff --git a/.eslintrc.js b/.eslintrc.js index bfec8541..5fcd0c7a 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -1,249 +1,179 @@ -const os = require('os'); - -const isWindows = os.platform() === 'win32'; - -const eslintConfig = -{ - env : - { - browser : true, - es6 : true, - node : true +const eslintConfig = { + env: { + es6: true, + node: true, + browser: true, }, - plugins : [], - settings : {}, - parserOptions : - { - ecmaVersion : 2022, - sourceType : 'module', - ecmaFeatures : - { - impliedStrict : true + plugins: ['prettier'], + settings: {}, + parserOptions: { + ecmaVersion: 2022, + sourceType: 'module', + ecmaFeatures: { + impliedStrict: true, }, - lib : [ 'es2022', 'dom' ], - project : 'tsconfig.json' + lib: ['es2022', 'dom'], }, - rules : - { - 'array-bracket-spacing' : [ 2, 'always', - { - objectsInArrays : true, - arraysInArrays : true - } - ], - 'arrow-parens' : [ 2, 'always' ], - 'arrow-spacing' : 2, - 'block-spacing' : [ 2, 'always' ], - 'brace-style' : [ 2, 'allman', { allowSingleLine: true } ], - 'camelcase' : 2, - 'comma-dangle' : 2, - 'comma-spacing' : [ 2, { before: false, after: true } ], - 'comma-style' : 2, - 'computed-property-spacing' : 2, - 'constructor-super' : 2, - 'curly' : [ 2, 'all' ], + globals: { + NodeJS: 'readonly', + }, + extends: ['eslint:recommended', 'plugin:prettier/recommended'], + rules: { + 'prettier/prettier': 2, + 'constructor-super': 2, + curly: [2, 'all'], // Unfortunatelly `curly` does not apply to blocks in `switch` cases so // this is needed. - 'no-restricted-syntax' : [ 2, - { - 'selector' : 'SwitchCase > *.consequent[type!="BlockStatement"]', - 'message' : 'Switch cases without blocks are disallowed' - } - ], - 'func-call-spacing' : 2, - 'generator-star-spacing' : 2, - 'guard-for-in' : 2, - 'indent' : [ 2, 'tab', { 'SwitchCase': 1 } ], - 'key-spacing' : [ 2, - { - singleLine : - { - beforeColon : false, - afterColon : true - }, - multiLine : - { - beforeColon : true, - afterColon : true, - mode : 'minimum', - align : 'colon' - } - } - ], - 'keyword-spacing' : 2, - 'linebreak-style' : [ 2, isWindows ? 'windows' : 'unix' ], - 'lines-around-comment' : [ 2, - { - allowBlockStart : true, - allowObjectStart : true, - beforeBlockComment : false, - beforeLineComment : false - } - ], - 'max-len' : [ 2, 90, - { - tabWidth : 2, - comments : 90, - ignoreUrls : true, - ignoreStrings : true, - ignoreTemplateLiterals : true, - ignoreRegExpLiterals : true - } - ], - 'newline-after-var' : 2, - 'newline-before-return' : 2, - 'newline-per-chained-call' : 2, - 'no-alert' : 2, - 'no-caller' : 2, - 'no-case-declarations' : 2, - 'no-catch-shadow' : 2, - 'no-class-assign' : 2, - 'no-confusing-arrow' : 2, - 'no-console' : 2, - 'no-const-assign' : 2, - 'no-debugger' : 2, - 'no-dupe-args' : 2, - 'no-dupe-keys' : 2, - 'no-duplicate-case' : 2, - 'no-div-regex' : 2, - 'no-empty' : [ 2, { allowEmptyCatch: true } ], - 'no-empty-pattern' : 2, - 'no-else-return' : 0, - 'no-eval' : 2, - 'no-extend-native' : 2, - 'no-ex-assign' : 2, - 'no-extra-bind' : 2, - 'no-extra-boolean-cast' : 2, - 'no-extra-label' : 2, - 'no-extra-semi' : 2, - 'no-fallthrough' : 2, - 'no-func-assign' : 2, - 'no-global-assign' : 2, - 'no-implicit-coercion' : 2, - 'no-implicit-globals' : 2, - 'no-inner-declarations' : 2, - 'no-invalid-regexp' : 2, - 'no-invalid-this' : 2, - 'no-irregular-whitespace' : 2, - 'no-lonely-if' : 2, - 'no-mixed-operators' : 2, - 'no-mixed-spaces-and-tabs' : 2, - 'no-multi-spaces' : 2, - 'no-multi-str' : 2, - 'no-multiple-empty-lines' : [ 1, { max: 1, maxEOF: 0, maxBOF: 0 } ], - 'no-native-reassign' : 2, - 'no-negated-in-lhs' : 2, - 'no-new' : 2, - 'no-new-func' : 2, - 'no-new-wrappers' : 2, - 'no-obj-calls' : 2, - 'no-proto' : 2, - 'no-prototype-builtins' : 0, - 'no-redeclare' : 2, - 'no-regex-spaces' : 2, - 'no-restricted-imports' : 2, - 'no-return-assign' : 2, - 'no-self-assign' : 2, - 'no-self-compare' : 2, - 'no-sequences' : 2, - 'no-shadow' : 2, - 'no-shadow-restricted-names' : 2, - 'no-spaced-func' : 2, - 'no-sparse-arrays' : 2, - 'no-this-before-super' : 2, - 'no-throw-literal' : 2, - 'no-undef' : 2, - 'no-unexpected-multiline' : 2, - 'no-unmodified-loop-condition' : 2, - 'no-unreachable' : 2, - 'no-unused-vars' : [ 1, { vars: 'all', args: 'after-used' } ], - 'no-use-before-define' : 0, - 'no-useless-call' : 2, - 'no-useless-computed-key' : 2, - 'no-useless-concat' : 2, - 'no-useless-rename' : 2, - 'no-var' : 2, - 'no-whitespace-before-property' : 2, - 'object-curly-newline' : 0, - 'object-curly-spacing' : [ 2, 'always' ], - 'object-property-newline' : [ 2, { allowMultiplePropertiesPerLine: true } ], - 'prefer-const' : 2, - 'prefer-rest-params' : 2, - 'prefer-spread' : 2, - 'prefer-template' : 2, - 'quotes' : [ 2, 'single', { avoidEscape: true } ], - 'semi' : [ 2, 'always' ], - 'semi-spacing' : 2, - 'space-before-blocks' : 2, - 'space-before-function-paren' : [ 2, + 'no-restricted-syntax': [ + 2, { - anonymous : 'never', - named : 'never', - asyncArrow : 'always' - } + selector: 'SwitchCase > *.consequent[type!="BlockStatement"]', + message: 'Switch cases without blocks are disallowed', + }, ], - 'space-in-parens' : [ 2, 'never' ], - 'space-infix-ops' : [ 2, { 'int32Hint': false } ], - 'spaced-comment' : [ 2, 'always' ], - 'strict' : 2, - 'valid-typeof' : 2, - 'yoda' : 2 + 'guard-for-in': 2, + 'newline-after-var': 2, + 'newline-before-return': 2, + 'no-alert': 2, + 'no-caller': 2, + 'no-case-declarations': 2, + 'no-catch-shadow': 2, + 'no-class-assign': 2, + 'no-console': 2, + 'no-const-assign': 2, + 'no-debugger': 2, + 'no-dupe-args': 2, + 'no-dupe-keys': 2, + 'no-duplicate-case': 2, + 'no-div-regex': 2, + 'no-empty': [2, { allowEmptyCatch: true }], + 'no-empty-pattern': 2, + 'no-else-return': 0, + 'no-eval': 2, + 'no-extend-native': 2, + 'no-ex-assign': 2, + 'no-extra-bind': 2, + 'no-extra-boolean-cast': 2, + 'no-extra-label': 2, + 'no-fallthrough': 2, + 'no-func-assign': 2, + 'no-global-assign': 2, + 'no-implicit-coercion': 2, + 'no-implicit-globals': 2, + 'no-inner-declarations': 2, + 'no-invalid-regexp': 2, + 'no-invalid-this': 2, + 'no-irregular-whitespace': 2, + 'no-lonely-if': 2, + 'no-multi-str': 2, + 'no-native-reassign': 2, + 'no-negated-in-lhs': 2, + 'no-new': 2, + 'no-new-func': 2, + 'no-new-wrappers': 2, + 'no-obj-calls': 2, + 'no-proto': 2, + 'no-prototype-builtins': 0, + 'no-redeclare': 2, + 'no-regex-spaces': 2, + 'no-restricted-imports': 2, + 'no-return-assign': 2, + 'no-self-assign': 2, + 'no-self-compare': 2, + 'no-sequences': 2, + 'no-shadow': 2, + 'no-shadow-restricted-names': 2, + 'no-sparse-arrays': 2, + 'no-this-before-super': 2, + 'no-throw-literal': 2, + 'no-undef': 2, + 'no-unmodified-loop-condition': 2, + 'no-unreachable': 2, + 'no-unused-vars': [1, { vars: 'all', args: 'after-used' }], + 'no-use-before-define': 0, + 'no-useless-call': 2, + 'no-useless-computed-key': 2, + 'no-useless-concat': 2, + 'no-useless-rename': 2, + 'no-var': 2, + 'object-curly-newline': 0, + 'prefer-const': 2, + 'prefer-rest-params': 2, + 'prefer-spread': 2, + 'prefer-template': 2, + 'spaced-comment': [2, 'always'], + strict: 2, + 'valid-typeof': 2, + yoda: 2, }, - overrides : [] + overrides: [], }; -eslintConfig.overrides.push( - { - files : [ '*.ts' ], - parser : '@typescript-eslint/parser', - plugins : [ - ...eslintConfig.plugins, - '@typescript-eslint' - ], - extends : [ - 'eslint:recommended', - 'plugin:@typescript-eslint/eslint-recommended', - 'plugin:@typescript-eslint/recommended' - ], - rules : { - ...eslintConfig.rules, - 'no-unused-vars' : 0, - '@typescript-eslint/ban-types' : 0, - '@typescript-eslint/ban-ts-comment' : 0, - '@typescript-eslint/ban-ts-ignore' : 0, - '@typescript-eslint/explicit-module-boundary-types' : 0, - '@typescript-eslint/semi' : 2, - '@typescript-eslint/member-delimiter-style' : [ 2, - { - multiline : { delimiter: 'semi', requireLast: true }, - singleline : { delimiter: 'semi', requireLast: false } - } - ], - '@typescript-eslint/no-explicit-any' : 0, - '@typescript-eslint/no-unused-vars' : [ 2, - { - vars : 'all', - args : 'after-used', - ignoreRestSiblings : false - } - ], - '@typescript-eslint/no-use-before-define' : [ 2, { functions: false } ], - '@typescript-eslint/no-empty-function' : 0, - '@typescript-eslint/no-non-null-assertion' : 0 - } - }); - -eslintConfig.overrides.push( - { - files : [ '*.ts' ], - env : { - ...eslintConfig.env, - 'jest/globals' : true +const tsRules = { + 'no-unused-vars': 0, + '@typescript-eslint/ban-types': 0, + '@typescript-eslint/ban-ts-comment': 0, + '@typescript-eslint/ban-ts-ignore': 0, + '@typescript-eslint/explicit-module-boundary-types': 0, + '@typescript-eslint/semi': 2, + '@typescript-eslint/member-delimiter-style': [ + 2, + { + multiline: { delimiter: 'semi', requireLast: true }, + singleline: { delimiter: 'semi', requireLast: false }, }, - plugins : [ - ...eslintConfig.plugins, - 'jest' - ] - }); + ], + '@typescript-eslint/no-explicit-any': 0, + '@typescript-eslint/no-unused-vars': [ + 2, + { + vars: 'all', + args: 'after-used', + ignoreRestSiblings: false, + }, + ], + '@typescript-eslint/no-use-before-define': [2, { functions: false }], + '@typescript-eslint/no-empty-function': 0, + '@typescript-eslint/no-non-null-assertion': 0, +}; + +eslintConfig.overrides.push({ + files: ['*.ts'], + parser: '@typescript-eslint/parser', + parserOptions: { + ...eslintConfig.parserOptions, + project: 'tsconfig.json', + }, + plugins: [...eslintConfig.plugins, '@typescript-eslint'], + extends: [ + 'plugin:@typescript-eslint/eslint-recommended', + 'plugin:@typescript-eslint/recommended', + ...eslintConfig.extends, + ], + rules: { ...eslintConfig.rules, ...tsRules }, +}); + +eslintConfig.overrides.push({ + files: ['src/test/*.ts'], + parserOptions: { + ...eslintConfig.parserOptions, + project: 'tsconfig.json', + }, + env: { + ...eslintConfig.env, + 'jest/globals': true, + }, + extends: [ + 'plugin:@typescript-eslint/eslint-recommended', + 'plugin:@typescript-eslint/recommended', + ...eslintConfig.extends, + ], + plugins: [...eslintConfig.plugins, '@typescript-eslint', 'jest'], + rules: { + ...eslintConfig.rules, + ...tsRules, + 'jest/no-disabled-tests': 2, + }, +}); module.exports = eslintConfig; diff --git a/.github/ISSUE_TEMPLATE/Bug_Report.md b/.github/ISSUE_TEMPLATE/Bug_Report.md index 88b6f8bc..8a9be7e5 100644 --- a/.github/ISSUE_TEMPLATE/Bug_Report.md +++ b/.github/ISSUE_TEMPLATE/Bug_Report.md @@ -20,5 +20,4 @@ https://mediasoup.discourse.group - mediasoup version: - mediasoup-client version: - ### Issue description diff --git a/.github/ISSUE_TEMPLATE/Support_Question.md b/.github/ISSUE_TEMPLATE/Support_Question.md index fe989f59..2ea1f789 100644 --- a/.github/ISSUE_TEMPLATE/Support_Question.md +++ b/.github/ISSUE_TEMPLATE/Support_Question.md @@ -9,4 +9,4 @@ https://mediasoup.discourse.group Before asking any questions, please check the mediasoup official documentation: -https://mediasoup.org/documentation/ +https://mediasoup.org/documentation diff --git a/.github/workflows/mediasoup-client.yaml b/.github/workflows/mediasoup-client.yaml index 65957b32..23f71691 100644 --- a/.github/workflows/mediasoup-client.yaml +++ b/.github/workflows/mediasoup-client.yaml @@ -1,6 +1,6 @@ name: mediasoup-client -on: [push, pull_request] +on: [pull_request, workflow_dispatch] concurrency: # Cancel a currently running workflow from the same PR, branch or tag when a @@ -12,14 +12,18 @@ jobs: ci: strategy: matrix: - # Different Node versions on Ubuntu, the latest Node on other platforms. ci: - - os: ubuntu-22.04 + - os: ubuntu-20.04 node: 16 - - os: macos-12 + - os: ubuntu-20.04 node: 18 - os: ubuntu-22.04 node: 20 + - os: macos-12 + node: 20 + - os: windows-2022 + node: 20 + runs-on: ${{ matrix.ci.os }} steps: @@ -39,6 +43,12 @@ jobs: key: ${{ matrix.ci.os }}-node-${{ hashFiles('**/package.json') }} restore-keys: | ${{ matrix.ci.os }}-node- - - run: npm ci - - run: npm run lint - - run: npm run test + + - name: npm ci + run: npm ci --foreground-scripts + + - name: npm run lint + run: npm run lint + + - name: npm run test + run: npm run test diff --git a/.gitignore b/.gitignore index 8024be81..21bf7e5e 100644 --- a/.gitignore +++ b/.gitignore @@ -5,11 +5,4 @@ ## Others. /coverage /.cache -/NO_GIT -*.swp -*.swo -.DS_Store -# Vistual Studio Code stuff. -/.vscode -# JetBrains IDE stuff. -/.idea + diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 00000000..c9574e43 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,6 @@ +# NOTE: This file and .eslintignore must contain same paths. + +/.cache +/coverage +/doc +/lib diff --git a/.prettierrc.json b/.prettierrc.json new file mode 100644 index 00000000..7cae653b --- /dev/null +++ b/.prettierrc.json @@ -0,0 +1,10 @@ +{ + "useTabs": true, + "tabWidth": 2, + "arrowParens": "avoid", + "bracketSpacing": true, + "semi": true, + "singleQuote": true, + "trailingComma": "all", + "endOfLine": "auto" +} diff --git a/README.md b/README.md index 8d06a679..d6291d27 100644 --- a/README.md +++ b/README.md @@ -6,16 +6,13 @@ JavaScript client side library for building [mediasoup][mediasoup-website] based applications. - ## Website and Documentation -* [mediasoup.org][mediasoup-website] - +- [mediasoup.org][mediasoup-website] ## Support Forum -* [mediasoup.discourse.group][mediasoup-discourse] - +- [mediasoup.discourse.group][mediasoup-discourse] ## Usage Example @@ -27,119 +24,100 @@ import mySignaling from './my-signaling'; // Our own signaling stuff. const device = new Device(); // Communicate with our server app to retrieve router RTP capabilities. -const routerRtpCapabilities = await mySignaling.request('getRouterCapabilities'); +const routerRtpCapabilities = await mySignaling.request( + 'getRouterCapabilities', +); // Load the device with the router RTP capabilities. await device.load({ routerRtpCapabilities }); // Check whether we can produce video to the router. -if (!device.canProduce('video')) -{ - console.warn('cannot produce video'); +if (!device.canProduce('video')) { + console.warn('cannot produce video'); - // Abort next steps. + // Abort next steps. } // Create a transport in the server for sending our media through it. -const { - id, - iceParameters, - iceCandidates, - dtlsParameters, - sctpParameters -} = await mySignaling.request( - 'createTransport', - { - sctpCapabilities : device.sctpCapabilities - }); +const { id, iceParameters, iceCandidates, dtlsParameters, sctpParameters } = + await mySignaling.request('createTransport', { + sctpCapabilities: device.sctpCapabilities, + }); // Create the local representation of our server-side transport. -const sendTransport = device.createSendTransport( - { - id, - iceParameters, - iceCandidates, - dtlsParameters, - sctpParameters - }); +const sendTransport = device.createSendTransport({ + id, + iceParameters, + iceCandidates, + dtlsParameters, + sctpParameters, +}); // Set transport "connect" event handler. -sendTransport.on('connect', async ({ dtlsParameters }, callback, errback) => -{ - // Here we must communicate our local parameters to our remote transport. - try - { - await mySignaling.request( - 'transport-connect', - { - transportId: sendTransport.id, - dtlsParameters - }); - - // Done in the server, tell our transport. - callback(); - } - catch (error) - { - // Something was wrong in server side. - errback(error); - } +sendTransport.on('connect', async ({ dtlsParameters }, callback, errback) => { + // Here we must communicate our local parameters to our remote transport. + try { + await mySignaling.request('transport-connect', { + transportId: sendTransport.id, + dtlsParameters, + }); + + // Done in the server, tell our transport. + callback(); + } catch (error) { + // Something was wrong in server side. + errback(error); + } }); // Set transport "produce" event handler. sendTransport.on( - 'produce', - async ({ kind, rtpParameters, appData }, callback, errback) => - { - // Here we must communicate our local parameters to our remote transport. - try - { - const { id } = await mySignaling.request( - 'produce', - { - transportId : sendTransport.id, - kind, - rtpParameters, - appData - }); - - // Done in the server, pass the response to our transport. - callback({ id }); - } - catch (error) - { - // Something was wrong in server side. - errback(error); - } - }); + 'produce', + async ({ kind, rtpParameters, appData }, callback, errback) => { + // Here we must communicate our local parameters to our remote transport. + try { + const { id } = await mySignaling.request('produce', { + transportId: sendTransport.id, + kind, + rtpParameters, + appData, + }); + + // Done in the server, pass the response to our transport. + callback({ id }); + } catch (error) { + // Something was wrong in server side. + errback(error); + } + }, +); // Set transport "producedata" event handler. sendTransport.on( - 'producedata', - async ({ sctpStreamParameters, label, protocol, appData }, callback, errback) => - { - // Here we must communicate our local parameters to our remote transport. - try - { - const { id } = await mySignaling.request( - 'produceData', - { - transportId : sendTransport.id, - sctpStreamParameters, - label, - protocol, - appData - }); - - // Done in the server, pass the response to our transport. - callback({ id }); - } - catch (error) - { - // Something was wrong in server side. - errback(error); - } - }); + 'producedata', + async ( + { sctpStreamParameters, label, protocol, appData }, + callback, + errback, + ) => { + // Here we must communicate our local parameters to our remote transport. + try { + const { id } = await mySignaling.request('produceData', { + transportId: sendTransport.id, + sctpStreamParameters, + label, + protocol, + appData, + }); + + // Done in the server, pass the response to our transport. + callback({ id }); + } catch (error) { + // Something was wrong in server side. + errback(error); + } + }, +); // Produce our webcam video. const stream = await navigator.mediaDevices.getUserMedia({ video: true }); @@ -147,34 +125,29 @@ const webcamTrack = stream.getVideoTracks()[0]; const webcamProducer = await sendTransport.produce({ track: webcamTrack }); // Produce data (DataChannel). -const dataProducer = - await sendTransport.produceData({ ordered: true, label: 'foo' }); +const dataProducer = await sendTransport.produceData({ + ordered: true, + label: 'foo', +}); ``` - ## Authors -* Iñaki Baz Castillo [[website](https://inakibaz.me)|[github](https://github.com/ibc/)] -* José Luis Millán [[github](https://github.com/jmillan/)] - +- Iñaki Baz Castillo [[website](https://inakibaz.me)|[github](https://github.com/ibc/)] +- José Luis Millán [[github](https://github.com/jmillan/)] ## Social -* Twitter: [@mediasoup_sfu](https://twitter.com/mediasoup_sfu) - +- Twitter: [@mediasoup_sfu](https://twitter.com/mediasoup_sfu) ## Sponsor You can support mediasoup by [sponsoring][sponsor] it. Thanks! - ## License [ISC](./LICENSE) - - - [mediasoup-website]: https://mediasoup.org [mediasoup-discourse]: https://mediasoup.discourse.group [npm-shield-mediasoup-client]: https://img.shields.io/npm/v/mediasoup-client.svg diff --git a/npm-scripts.mjs b/npm-scripts.mjs index 3b66069f..27ee2c20 100644 --- a/npm-scripts.mjs +++ b/npm-scripts.mjs @@ -8,14 +8,15 @@ const PKG = JSON.parse(fs.readFileSync('./package.json').toString()); const IS_WINDOWS = os.platform() === 'win32'; const MAYOR_VERSION = PKG.version.split('.')[0]; -const task = process.argv.slice(2).join(' '); +const task = process.argv[2]; +const args = process.argv.slice(3).join(' '); run(); -async function run() -{ - switch (task) - { +async function run() { + logInfo(args ? `[args:"${args}"]` : ''); + + switch (task) { // As per NPM documentation (https://docs.npmjs.com/cli/v9/using-npm/scripts) // `prepare` script: // @@ -26,65 +27,63 @@ async function run() // script will be run, before the package is packaged and installed. // // So here we compile TypeScript to JavaScript. - case 'prepare': - { - buildTypescript(/* force */ false); + case 'prepare': { + buildTypescript({ force: false }); break; } - case 'typescript:build': - { + case 'typescript:build': { installDeps(); - buildTypescript(/* force */ true); + buildTypescript({ force: true }); replaceVersion(); break; } - case 'typescript:watch': - { + case 'typescript:watch': { deleteLib(); - executeCmd('tsc --watch'); + executeCmd(`tsc --watch ${args}`); break; } - case 'lint': - { + case 'lint': { lint(); break; } - case 'test': - { - buildTypescript(/* force */ false); + case 'format': { + format(); + + break; + } + + case 'test': { + buildTypescript({ force: false }); replaceVersion(); test(); break; } - case 'coverage': - { - buildTypescript(/* force */ false); + case 'coverage': { + buildTypescript({ force: false }); replaceVersion(); - executeCmd('jest --coverage'); + executeCmd(`jest --coverage ${args}`); executeCmd('open-cli coverage/lcov-report/index.html'); break; } - case 'release:check': - { + case 'release:check': { checkRelease(); break; } - case 'release': - { + case 'release': { checkRelease(); executeCmd(`git commit -am '${PKG.version}'`); executeCmd(`git tag -a ${PKG.version} -m '${PKG.version}'`); @@ -95,8 +94,7 @@ async function run() break; } - default: - { + default: { logError('unknown task'); exitWithError(); @@ -104,20 +102,16 @@ async function run() } } -function replaceVersion() -{ +function replaceVersion() { logInfo('replaceVersion()'); - const files = fs.readdirSync('lib', - { - withFileTypes : true, - recursive : true - }); + const files = fs.readdirSync('lib', { + withFileTypes: true, + recursive: true, + }); - for (const file of files) - { - if (!file.isFile()) - { + for (const file of files) { + if (!file.isFile()) { continue; } @@ -130,30 +124,18 @@ function replaceVersion() } } -function deleteLib() -{ - if (!fs.existsSync('lib')) - { +function deleteLib() { + if (!fs.existsSync('lib')) { return; } logInfo('deleteLib()'); - if (!IS_WINDOWS) - { - executeCmd('rm -rf lib'); - } - else - { - // NOTE: This command fails in Windows if the dir doesn't exist. - executeCmd('rmdir /s /q "lib"', /* exitOnError */ false); - } + fs.rmSync('lib', { recursive: true, force: true }); } -function buildTypescript(force = false) -{ - if (!force && fs.existsSync('lib')) - { +function buildTypescript({ force = false } = { force: false }) { + if (!force && fs.existsSync('lib')) { return; } @@ -163,22 +145,33 @@ function buildTypescript(force = false) executeCmd('tsc'); } -function lint() -{ +function lint() { logInfo('lint()'); - executeCmd('eslint -c .eslintrc.js --max-warnings 0 src .eslintrc.js npm-scripts.mjs'); + executeCmd('prettier . --check'); + + // Ensure there are no rules that are unnecessary or conflict with Prettier + // rules. + executeCmd('eslint-config-prettier .eslintrc.js'); + + executeCmd( + 'eslint -c .eslintrc.js --ignore-path .eslintignore --max-warnings 0 .', + ); } -function test() -{ +function format() { + logInfo('format()'); + + executeCmd('prettier . --write'); +} + +function test() { logInfo('test()'); - executeCmd('jest'); + executeCmd(`jest --silent false --detectOpenHandles ${args}`); } -function installDeps() -{ +function installDeps() { logInfo('installDeps()'); // Install/update deps. @@ -187,60 +180,48 @@ function installDeps() executeCmd('npm install --package-lock-only --ignore-scripts'); } -function checkRelease() -{ +function checkRelease() { logInfo('checkRelease()'); installDeps(); - buildTypescript(/* force */ true); + buildTypescript({ force: true }); replaceVersion(); lint(); test(); } -function executeCmd(command, exitOnError = true) -{ +function executeCmd(command, exitOnError = true) { logInfo(`executeCmd(): ${command}`); - try - { - execSync(command, { stdio: [ 'ignore', process.stdout, process.stderr ] }); - } - catch (error) - { - if (exitOnError) - { + try { + execSync(command, { stdio: ['ignore', process.stdout, process.stderr] }); + } catch (error) { + if (exitOnError) { logError(`executeCmd() failed, exiting: ${error}`); exitWithError(); - } - else - { + } else { logInfo(`executeCmd() failed, ignoring: ${error}`); } } } -function logInfo(message) -{ +function logInfo(message) { // eslint-disable-next-line no-console - console.log(`npm-scripts \x1b[36m[INFO] [${task}]\x1b\[0m`, message); + console.log(`npm-scripts \x1b[36m[INFO] [${task}]\x1b[0m`, message); } // eslint-disable-next-line no-unused-vars -function logWarn(message) -{ +function logWarn(message) { // eslint-disable-next-line no-console - console.warn(`npm-scripts \x1b[33m[WARN] [${task}]\x1b\[0m`, message); + console.warn(`npm-scripts \x1b[33m[WARN] [${task}]\x1b[0m`, message); } -function logError(message) -{ +function logError(message) { // eslint-disable-next-line no-console - console.error(`npm-scripts \x1b[31m[ERROR] [${task}]\x1b\[0m`, message); + console.error(`npm-scripts \x1b[31m[ERROR] [${task}]\x1b[0m`, message); } -function exitWithError() -{ +function exitWithError() { process.exit(1); } diff --git a/package-lock.json b/package-lock.json index 3fe8abda..fe5adfde 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,10185 +1,10491 @@ { - "name": "mediasoup-client", - "version": "3.7.1", - "lockfileVersion": 2, - "requires": true, - "packages": { - "": { - "name": "mediasoup-client", - "version": "3.7.1", - "license": "ISC", - "dependencies": { - "@types/debug": "^4.1.12", - "awaitqueue": "^3.0.2", - "debug": "^4.3.4", - "events": "^3.3.0", - "fake-mediastreamtrack": "^1.2.0", - "h264-profile-level-id": "^2.0.0", - "queue-microtask": "^1.2.3", - "sdp-transform": "^2.14.1", - "supports-color": "^9.4.0", - "ua-parser-js": "^1.0.37" - }, - "devDependencies": { - "@types/events": "^3.0.3", - "@types/jest": "^29.5.11", - "@types/sdp-transform": "^2.4.9", - "@types/ua-parser-js": "^0.7.39", - "@typescript-eslint/eslint-plugin": "^6.15.0", - "@typescript-eslint/parser": "^6.15.0", - "eslint": "^8.56.0", - "eslint-plugin-jest": "^27.6.0", - "jest": "^29.7.0", - "open-cli": "^7.2.0", - "ts-jest": "^29.1.1", - "typescript": "^5.3.3" - }, - "engines": { - "node": ">=16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/mediasoup" - } - }, - "node_modules/@aashutoshrathi/word-wrap": { - "version": "1.2.6", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/@ampproject/remapping": { - "version": "2.2.0", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@jridgewell/gen-mapping": "^0.1.0", - "@jridgewell/trace-mapping": "^0.3.9" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/code-frame": { - "version": "7.18.6", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/highlight": "^7.18.6" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/compat-data": { - "version": "7.20.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/core": { - "version": "7.20.2", - "dev": true, - "license": "MIT", - "dependencies": { - "@ampproject/remapping": "^2.1.0", - "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.20.2", - "@babel/helper-compilation-targets": "^7.20.0", - "@babel/helper-module-transforms": "^7.20.2", - "@babel/helpers": "^7.20.1", - "@babel/parser": "^7.20.2", - "@babel/template": "^7.18.10", - "@babel/traverse": "^7.20.1", - "@babel/types": "^7.20.2", - "convert-source-map": "^1.7.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.2.1", - "semver": "^6.3.0" - }, - "engines": { - "node": ">=6.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/babel" - } - }, - "node_modules/@babel/core/node_modules/convert-source-map": { - "version": "1.9.0", - "dev": true, - "license": "MIT" - }, - "node_modules/@babel/core/node_modules/semver": { - "version": "6.3.0", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/@babel/generator": { - "version": "7.20.4", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.20.2", - "@jridgewell/gen-mapping": "^0.3.2", - "jsesc": "^2.5.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/generator/node_modules/@jridgewell/gen-mapping": { - "version": "0.3.2", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/set-array": "^1.0.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.9" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/helper-compilation-targets": { - "version": "7.20.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/compat-data": "^7.20.0", - "@babel/helper-validator-option": "^7.18.6", - "browserslist": "^4.21.3", - "semver": "^6.3.0" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-compilation-targets/node_modules/semver": { - "version": "6.3.0", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/@babel/helper-environment-visitor": { - "version": "7.18.9", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-function-name": { - "version": "7.19.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/template": "^7.18.10", - "@babel/types": "^7.19.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-hoist-variables": { - "version": "7.18.6", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.18.6" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-imports": { - "version": "7.18.6", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.18.6" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-transforms": { - "version": "7.20.2", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-module-imports": "^7.18.6", - "@babel/helper-simple-access": "^7.20.2", - "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/helper-validator-identifier": "^7.19.1", - "@babel/template": "^7.18.10", - "@babel/traverse": "^7.20.1", - "@babel/types": "^7.20.2" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-plugin-utils": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz", - "integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-simple-access": { - "version": "7.20.2", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.20.2" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-split-export-declaration": { - "version": "7.18.6", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.18.6" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-string-parser": { - "version": "7.19.4", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.19.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-option": { - "version": "7.18.6", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helpers": { - "version": "7.20.1", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/template": "^7.18.10", - "@babel/traverse": "^7.20.1", - "@babel/types": "^7.20.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/highlight": { - "version": "7.18.6", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-validator-identifier": "^7.18.6", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/highlight/node_modules/ansi-styles": { - "version": "3.2.1", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/chalk": { - "version": "2.4.2", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/color-convert": { - "version": "1.9.3", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/@babel/highlight/node_modules/color-name": { - "version": "1.1.3", - "dev": true, - "license": "MIT" - }, - "node_modules/@babel/highlight/node_modules/escape-string-regexp": { - "version": "1.0.5", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/@babel/highlight/node_modules/supports-color": { - "version": "5.5.0", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/parser": { - "version": "7.20.13", - "dev": true, - "license": "MIT", - "bin": { - "parser": "bin/babel-parser.js" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/plugin-syntax-async-generators": { - "version": "7.8.4", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-bigint": { - "version": "7.8.3", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-class-properties": { - "version": "7.12.13", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.12.13" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-import-meta": { - "version": "7.10.4", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-json-strings": { - "version": "7.8.3", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-jsx": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.22.5.tgz", - "integrity": "sha512-gvyP4hZrgrs/wWMaocvxZ44Hw0b3W8Pe+cMxc8V1ULQ07oh8VNbIRaoD1LRZVTvD+0nieDKjfgKg89sD7rrKrg==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-logical-assignment-operators": { - "version": "7.10.4", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { - "version": "7.8.3", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-numeric-separator": { - "version": "7.10.4", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-object-rest-spread": { - "version": "7.8.3", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-optional-catch-binding": { - "version": "7.8.3", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-optional-chaining": { - "version": "7.8.3", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-top-level-await": { - "version": "7.14.5", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-typescript": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.22.5.tgz", - "integrity": "sha512-1mS2o03i7t1c6VzH6fdQ3OA8tcEIxwG18zIPRp+UY1Ihv6W+XZzBCVxExF9upussPXJ0xE9XRHwMoNs1ep/nRQ==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/template": { - "version": "7.18.10", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.18.6", - "@babel/parser": "^7.18.10", - "@babel/types": "^7.18.10" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/traverse": { - "version": "7.20.1", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.20.1", - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-function-name": "^7.19.0", - "@babel/helper-hoist-variables": "^7.18.6", - "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/parser": "^7.20.1", - "@babel/types": "^7.20.0", - "debug": "^4.1.0", - "globals": "^11.1.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/traverse/node_modules/globals": { - "version": "11.12.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/types": { - "version": "7.20.7", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-string-parser": "^7.19.4", - "@babel/helper-validator-identifier": "^7.19.1", - "to-fast-properties": "^2.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@bcoe/v8-coverage": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", - "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", - "dev": true - }, - "node_modules/@eslint-community/eslint-utils": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", - "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", - "dev": true, - "dependencies": { - "eslint-visitor-keys": "^3.3.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" - } - }, - "node_modules/@eslint-community/regexpp": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.6.2.tgz", - "integrity": "sha512-pPTNuaAG3QMH+buKyBIGJs3g/S5y0caxw0ygM3YyE6yJFySwiGGSzA+mM3KJ8QQvzeLh3blwgSonkFjgQdxzMw==", - "dev": true, - "engines": { - "node": "^12.0.0 || ^14.0.0 || >=16.0.0" - } - }, - "node_modules/@eslint/eslintrc": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", - "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", - "dev": true, - "dependencies": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^9.6.0", - "globals": "^13.19.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" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/@eslint/js": { - "version": "8.56.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.56.0.tgz", - "integrity": "sha512-gMsVel9D7f2HLkBma9VbtzZRehRogVRfbr++f06nL2vnCGCNlzOD+/MUov/F4p8myyAHspEhVobgjpX64q5m6A==", - "dev": true, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - } - }, - "node_modules/@humanwhocodes/config-array": { - "version": "0.11.13", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.13.tgz", - "integrity": "sha512-JSBDMiDKSzQVngfRjOdFXgFfklaXI4K9nLF49Auh21lmBWRLIK3+xTErTWD4KU54pb6coM6ESE7Awz/FNU3zgQ==", - "dev": true, - "dependencies": { - "@humanwhocodes/object-schema": "^2.0.1", - "debug": "^4.1.1", - "minimatch": "^3.0.5" - }, - "engines": { - "node": ">=10.10.0" - } - }, - "node_modules/@humanwhocodes/module-importer": { - "version": "1.0.1", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=12.22" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" - } - }, - "node_modules/@humanwhocodes/object-schema": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.1.tgz", - "integrity": "sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw==", - "dev": true - }, - "node_modules/@istanbuljs/load-nyc-config": { - "version": "1.1.0", - "dev": true, - "license": "ISC", - "dependencies": { - "camelcase": "^5.3.1", - "find-up": "^4.1.0", - "get-package-type": "^0.1.0", - "js-yaml": "^3.13.1", - "resolve-from": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/argparse": { - "version": "1.0.10", - "dev": true, - "license": "MIT", - "dependencies": { - "sprintf-js": "~1.0.2" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/find-up": { - "version": "4.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/js-yaml": { - "version": "3.14.1", - "dev": true, - "license": "MIT", - "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/locate-path": { - "version": "5.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/p-limit": { - "version": "2.3.0", - "dev": true, - "license": "MIT", - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/p-locate": { - "version": "4.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/resolve-from": { - "version": "5.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/@istanbuljs/schema": { - "version": "0.1.3", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/@jest/console": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.7.0.tgz", - "integrity": "sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==", - "dev": true, - "dependencies": { - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0", - "slash": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/core": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.7.0.tgz", - "integrity": "sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==", - "dev": true, - "dependencies": { - "@jest/console": "^29.7.0", - "@jest/reporters": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "exit": "^0.1.2", - "graceful-fs": "^4.2.9", - "jest-changed-files": "^29.7.0", - "jest-config": "^29.7.0", - "jest-haste-map": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-regex-util": "^29.6.3", - "jest-resolve": "^29.7.0", - "jest-resolve-dependencies": "^29.7.0", - "jest-runner": "^29.7.0", - "jest-runtime": "^29.7.0", - "jest-snapshot": "^29.7.0", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "jest-watcher": "^29.7.0", - "micromatch": "^4.0.4", - "pretty-format": "^29.7.0", - "slash": "^3.0.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/@jest/environment": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.7.0.tgz", - "integrity": "sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==", - "dev": true, - "dependencies": { - "@jest/fake-timers": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "jest-mock": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/expect": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.7.0.tgz", - "integrity": "sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==", - "dev": true, - "dependencies": { - "expect": "^29.7.0", - "jest-snapshot": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/expect-utils": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.7.0.tgz", - "integrity": "sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==", - "dev": true, - "dependencies": { - "jest-get-type": "^29.6.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/fake-timers": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.7.0.tgz", - "integrity": "sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==", - "dev": true, - "dependencies": { - "@jest/types": "^29.6.3", - "@sinonjs/fake-timers": "^10.0.2", - "@types/node": "*", - "jest-message-util": "^29.7.0", - "jest-mock": "^29.7.0", - "jest-util": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/globals": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.7.0.tgz", - "integrity": "sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==", - "dev": true, - "dependencies": { - "@jest/environment": "^29.7.0", - "@jest/expect": "^29.7.0", - "@jest/types": "^29.6.3", - "jest-mock": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/reporters": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.7.0.tgz", - "integrity": "sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==", - "dev": true, - "dependencies": { - "@bcoe/v8-coverage": "^0.2.3", - "@jest/console": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@jridgewell/trace-mapping": "^0.3.18", - "@types/node": "*", - "chalk": "^4.0.0", - "collect-v8-coverage": "^1.0.0", - "exit": "^0.1.2", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "istanbul-lib-coverage": "^3.0.0", - "istanbul-lib-instrument": "^6.0.0", - "istanbul-lib-report": "^3.0.0", - "istanbul-lib-source-maps": "^4.0.0", - "istanbul-reports": "^3.1.3", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0", - "jest-worker": "^29.7.0", - "slash": "^3.0.0", - "string-length": "^4.0.1", - "strip-ansi": "^6.0.0", - "v8-to-istanbul": "^9.0.1" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/@jest/reporters/node_modules/istanbul-lib-instrument": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.0.tgz", - "integrity": "sha512-x58orMzEVfzPUKqlbLd1hXCnySCxKdDKa6Rjg97CwuLLRI4g3FHTdnExu1OqffVFay6zeMW+T6/DowFLndWnIw==", - "dev": true, - "dependencies": { - "@babel/core": "^7.12.3", - "@babel/parser": "^7.14.7", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-coverage": "^3.2.0", - "semver": "^7.5.4" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@jest/schemas": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", - "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", - "dev": true, - "dependencies": { - "@sinclair/typebox": "^0.27.8" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/source-map": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.6.3.tgz", - "integrity": "sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==", - "dev": true, - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.18", - "callsites": "^3.0.0", - "graceful-fs": "^4.2.9" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/test-result": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.7.0.tgz", - "integrity": "sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==", - "dev": true, - "dependencies": { - "@jest/console": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "collect-v8-coverage": "^1.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/test-sequencer": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz", - "integrity": "sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==", - "dev": true, - "dependencies": { - "@jest/test-result": "^29.7.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "slash": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/transform": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.7.0.tgz", - "integrity": "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==", - "dev": true, - "dependencies": { - "@babel/core": "^7.11.6", - "@jest/types": "^29.6.3", - "@jridgewell/trace-mapping": "^0.3.18", - "babel-plugin-istanbul": "^6.1.1", - "chalk": "^4.0.0", - "convert-source-map": "^2.0.0", - "fast-json-stable-stringify": "^2.1.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "jest-regex-util": "^29.6.3", - "jest-util": "^29.7.0", - "micromatch": "^4.0.4", - "pirates": "^4.0.4", - "slash": "^3.0.0", - "write-file-atomic": "^4.0.2" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/types": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", - "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", - "dev": true, - "dependencies": { - "@jest/schemas": "^29.6.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.1.1", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/set-array": "^1.0.0", - "@jridgewell/sourcemap-codec": "^1.4.10" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/set-array": { - "version": "1.1.2", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.14", - "dev": true, - "license": "MIT" - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.18", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.18.tgz", - "integrity": "sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA==", - "dev": true, - "dependencies": { - "@jridgewell/resolve-uri": "3.1.0", - "@jridgewell/sourcemap-codec": "1.4.14" - } - }, - "node_modules/@nodelib/fs.scandir": { - "version": "2.1.5", - "dev": true, - "license": "MIT", - "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.stat": { - "version": "2.0.5", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.walk": { - "version": "1.2.8", - "dev": true, - "license": "MIT", - "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@sinclair/typebox": { - "version": "0.27.8", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", - "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", - "dev": true - }, - "node_modules/@sinonjs/commons": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.0.tgz", - "integrity": "sha512-jXBtWAF4vmdNmZgD5FoKsVLv3rPgDnLgPbU84LIJ3otV44vJlDRokVng5v8NFJdCf/da9legHcKaRuZs4L7faA==", - "dev": true, - "dependencies": { - "type-detect": "4.0.8" - } - }, - "node_modules/@sinonjs/fake-timers": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", - "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", - "dev": true, - "dependencies": { - "@sinonjs/commons": "^3.0.0" - } - }, - "node_modules/@tokenizer/token": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/@tokenizer/token/-/token-0.3.0.tgz", - "integrity": "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==", - "dev": true - }, - "node_modules/@types/babel__core": { - "version": "7.20.1", - "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.1.tgz", - "integrity": "sha512-aACu/U/omhdk15O4Nfb+fHgH/z3QsfQzpnvRZhYhThms83ZnAOZz7zZAWO7mn2yyNQaA4xTO8GLK3uqFU4bYYw==", - "dev": true, - "dependencies": { - "@babel/parser": "^7.20.7", - "@babel/types": "^7.20.7", - "@types/babel__generator": "*", - "@types/babel__template": "*", - "@types/babel__traverse": "*" - } - }, - "node_modules/@types/babel__generator": { - "version": "7.6.4", - "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.4.tgz", - "integrity": "sha512-tFkciB9j2K755yrTALxD44McOrk+gfpIpvC3sxHjRawj6PfnQxrse4Clq5y/Rq+G3mrBurMax/lG8Qn2t9mSsg==", - "dev": true, - "dependencies": { - "@babel/types": "^7.0.0" - } - }, - "node_modules/@types/babel__template": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.1.tgz", - "integrity": "sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g==", - "dev": true, - "dependencies": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0" - } - }, - "node_modules/@types/babel__traverse": { - "version": "7.20.1", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.1.tgz", - "integrity": "sha512-MitHFXnhtgwsGZWtT68URpOvLN4EREih1u3QtQiN4VdAxWKRVvGCSvw/Qth0M0Qq3pJpnGOu5JaM/ydK7OGbqg==", - "dev": true, - "dependencies": { - "@babel/types": "^7.20.7" - } - }, - "node_modules/@types/debug": { - "version": "4.1.12", - "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz", - "integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==", - "dependencies": { - "@types/ms": "*" - } - }, - "node_modules/@types/events": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/events/-/events-3.0.3.tgz", - "integrity": "sha512-trOc4AAUThEz9hapPtSd7wf5tiQKvTtu5b371UxXdTuqzIh0ArcRspRP0i0Viu+LXstIQ1z96t1nsPxT9ol01g==", - "dev": true - }, - "node_modules/@types/graceful-fs": { - "version": "4.1.6", - "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.6.tgz", - "integrity": "sha512-Sig0SNORX9fdW+bQuTEovKj3uHcUL6LQKbCrrqb1X7J6/ReAbhCXRAhc+SMejhLELFj2QcyuxmUooZ4bt5ReSw==", - "dev": true, - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/istanbul-lib-coverage": { - "version": "2.0.4", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/istanbul-lib-report": { - "version": "3.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/istanbul-lib-coverage": "*" - } - }, - "node_modules/@types/istanbul-reports": { - "version": "3.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/istanbul-lib-report": "*" - } - }, - "node_modules/@types/jest": { - "version": "29.5.11", - "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.11.tgz", - "integrity": "sha512-S2mHmYIVe13vrm6q4kN6fLYYAka15ALQki/vgDC3mIukEOx8WJlv0kQPM+d4w8Gp6u0uSdKND04IlTXBv0rwnQ==", - "dev": true, - "dependencies": { - "expect": "^29.0.0", - "pretty-format": "^29.0.0" - } - }, - "node_modules/@types/json-schema": { - "version": "7.0.12", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.12.tgz", - "integrity": "sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA==", - "dev": true - }, - "node_modules/@types/minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-hov8bUuiLiyFPGyFPE1lwWhmzYbirOXQNNo40+y3zow8aFVTeyn3VWL0VFFfdNddA8S4Vf0Tc062rzyNr7Paag==", - "dev": true - }, - "node_modules/@types/ms": { - "version": "0.7.31", - "license": "MIT" - }, - "node_modules/@types/node": { - "version": "18.11.9", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/normalize-package-data": { - "version": "2.4.4", - "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.4.tgz", - "integrity": "sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==", - "dev": true - }, - "node_modules/@types/sdp-transform": { - "version": "2.4.9", - "resolved": "https://registry.npmjs.org/@types/sdp-transform/-/sdp-transform-2.4.9.tgz", - "integrity": "sha512-bVr+/OoZZy7wrHlNcEAAa6PAgKA4BoXPYVN2EijMC5WnGgQ4ZEuixmKnVs2roiAvr7RhIFVH17QD27cojgIZCg==", - "dev": true - }, - "node_modules/@types/semver": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.0.tgz", - "integrity": "sha512-G8hZ6XJiHnuhQKR7ZmysCeJWE08o8T0AXtk5darsCaTVsYZhhgUrq53jizaR2FvsoeCwJhlmwTjkXBY5Pn/ZHw==", - "dev": true - }, - "node_modules/@types/stack-utils": { - "version": "2.0.1", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/ua-parser-js": { - "version": "0.7.39", - "resolved": "https://registry.npmjs.org/@types/ua-parser-js/-/ua-parser-js-0.7.39.tgz", - "integrity": "sha512-P/oDfpofrdtF5xw433SPALpdSchtJmY7nsJItf8h3KXqOslkbySh8zq4dSWXH2oTjRvJ5PczVEoCZPow6GicLg==", - "dev": true - }, - "node_modules/@types/yargs": { - "version": "17.0.13", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/@types/yargs-parser": { - "version": "21.0.0", - "dev": true, - "license": "MIT" - }, - "node_modules/@typescript-eslint/eslint-plugin": { - "version": "6.15.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.15.0.tgz", - "integrity": "sha512-j5qoikQqPccq9QoBAupOP+CBu8BaJ8BLjaXSioDISeTZkVO3ig7oSIKh3H+rEpee7xCXtWwSB4KIL5l6hWZzpg==", - "dev": true, - "dependencies": { - "@eslint-community/regexpp": "^4.5.1", - "@typescript-eslint/scope-manager": "6.15.0", - "@typescript-eslint/type-utils": "6.15.0", - "@typescript-eslint/utils": "6.15.0", - "@typescript-eslint/visitor-keys": "6.15.0", - "debug": "^4.3.4", - "graphemer": "^1.4.0", - "ignore": "^5.2.4", - "natural-compare": "^1.4.0", - "semver": "^7.5.4", - "ts-api-utils": "^1.0.1" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "@typescript-eslint/parser": "^6.0.0 || ^6.0.0-alpha", - "eslint": "^7.0.0 || ^8.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/scope-manager": { - "version": "6.15.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.15.0.tgz", - "integrity": "sha512-+BdvxYBltqrmgCNu4Li+fGDIkW9n//NrruzG9X1vBzaNK+ExVXPoGB71kneaVw/Jp+4rH/vaMAGC6JfMbHstVg==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "6.15.0", - "@typescript-eslint/visitor-keys": "6.15.0" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/types": { - "version": "6.15.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.15.0.tgz", - "integrity": "sha512-yXjbt//E4T/ee8Ia1b5mGlbNj9fB9lJP4jqLbZualwpP2BCQ5is6BcWwxpIsY4XKAhmdv3hrW92GdtJbatC6dQ==", - "dev": true, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/typescript-estree": { - "version": "6.15.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.15.0.tgz", - "integrity": "sha512-7mVZJN7Hd15OmGuWrp2T9UvqR2Ecg+1j/Bp1jXUEY2GZKV6FXlOIoqVDmLpBiEiq3katvj/2n2mR0SDwtloCew==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "6.15.0", - "@typescript-eslint/visitor-keys": "6.15.0", - "debug": "^4.3.4", - "globby": "^11.1.0", - "is-glob": "^4.0.3", - "semver": "^7.5.4", - "ts-api-utils": "^1.0.1" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/utils": { - "version": "6.15.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.15.0.tgz", - "integrity": "sha512-eF82p0Wrrlt8fQSRL0bGXzK5nWPRV2dYQZdajcfzOD9+cQz9O7ugifrJxclB+xVOvWvagXfqS4Es7vpLP4augw==", - "dev": true, - "dependencies": { - "@eslint-community/eslint-utils": "^4.4.0", - "@types/json-schema": "^7.0.12", - "@types/semver": "^7.5.0", - "@typescript-eslint/scope-manager": "6.15.0", - "@typescript-eslint/types": "6.15.0", - "@typescript-eslint/typescript-estree": "6.15.0", - "semver": "^7.5.4" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^7.0.0 || ^8.0.0" - } - }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/visitor-keys": { - "version": "6.15.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.15.0.tgz", - "integrity": "sha512-1zvtdC1a9h5Tb5jU9x3ADNXO9yjP8rXlaoChu0DQX40vf5ACVpYIVIZhIMZ6d5sDXH7vq4dsZBT1fEGj8D2n2w==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "6.15.0", - "eslint-visitor-keys": "^3.4.1" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/parser": { - "version": "6.15.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.15.0.tgz", - "integrity": "sha512-MkgKNnsjC6QwcMdlNAel24jjkEO/0hQaMDLqP4S9zq5HBAUJNQB6y+3DwLjX7b3l2b37eNAxMPLwb3/kh8VKdA==", - "dev": true, - "dependencies": { - "@typescript-eslint/scope-manager": "6.15.0", - "@typescript-eslint/types": "6.15.0", - "@typescript-eslint/typescript-estree": "6.15.0", - "@typescript-eslint/visitor-keys": "6.15.0", - "debug": "^4.3.4" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^7.0.0 || ^8.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/scope-manager": { - "version": "6.15.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.15.0.tgz", - "integrity": "sha512-+BdvxYBltqrmgCNu4Li+fGDIkW9n//NrruzG9X1vBzaNK+ExVXPoGB71kneaVw/Jp+4rH/vaMAGC6JfMbHstVg==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "6.15.0", - "@typescript-eslint/visitor-keys": "6.15.0" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/types": { - "version": "6.15.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.15.0.tgz", - "integrity": "sha512-yXjbt//E4T/ee8Ia1b5mGlbNj9fB9lJP4jqLbZualwpP2BCQ5is6BcWwxpIsY4XKAhmdv3hrW92GdtJbatC6dQ==", - "dev": true, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/typescript-estree": { - "version": "6.15.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.15.0.tgz", - "integrity": "sha512-7mVZJN7Hd15OmGuWrp2T9UvqR2Ecg+1j/Bp1jXUEY2GZKV6FXlOIoqVDmLpBiEiq3katvj/2n2mR0SDwtloCew==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "6.15.0", - "@typescript-eslint/visitor-keys": "6.15.0", - "debug": "^4.3.4", - "globby": "^11.1.0", - "is-glob": "^4.0.3", - "semver": "^7.5.4", - "ts-api-utils": "^1.0.1" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/visitor-keys": { - "version": "6.15.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.15.0.tgz", - "integrity": "sha512-1zvtdC1a9h5Tb5jU9x3ADNXO9yjP8rXlaoChu0DQX40vf5ACVpYIVIZhIMZ6d5sDXH7vq4dsZBT1fEGj8D2n2w==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "6.15.0", - "eslint-visitor-keys": "^3.4.1" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/scope-manager": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.62.0.tgz", - "integrity": "sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "5.62.0", - "@typescript-eslint/visitor-keys": "5.62.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/type-utils": { - "version": "6.15.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.15.0.tgz", - "integrity": "sha512-CnmHKTfX6450Bo49hPg2OkIm/D/TVYV7jO1MCfPYGwf6x3GO0VU8YMO5AYMn+u3X05lRRxA4fWCz87GFQV6yVQ==", - "dev": true, - "dependencies": { - "@typescript-eslint/typescript-estree": "6.15.0", - "@typescript-eslint/utils": "6.15.0", - "debug": "^4.3.4", - "ts-api-utils": "^1.0.1" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^7.0.0 || ^8.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/scope-manager": { - "version": "6.15.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.15.0.tgz", - "integrity": "sha512-+BdvxYBltqrmgCNu4Li+fGDIkW9n//NrruzG9X1vBzaNK+ExVXPoGB71kneaVw/Jp+4rH/vaMAGC6JfMbHstVg==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "6.15.0", - "@typescript-eslint/visitor-keys": "6.15.0" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/types": { - "version": "6.15.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.15.0.tgz", - "integrity": "sha512-yXjbt//E4T/ee8Ia1b5mGlbNj9fB9lJP4jqLbZualwpP2BCQ5is6BcWwxpIsY4XKAhmdv3hrW92GdtJbatC6dQ==", - "dev": true, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/typescript-estree": { - "version": "6.15.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.15.0.tgz", - "integrity": "sha512-7mVZJN7Hd15OmGuWrp2T9UvqR2Ecg+1j/Bp1jXUEY2GZKV6FXlOIoqVDmLpBiEiq3katvj/2n2mR0SDwtloCew==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "6.15.0", - "@typescript-eslint/visitor-keys": "6.15.0", - "debug": "^4.3.4", - "globby": "^11.1.0", - "is-glob": "^4.0.3", - "semver": "^7.5.4", - "ts-api-utils": "^1.0.1" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/utils": { - "version": "6.15.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.15.0.tgz", - "integrity": "sha512-eF82p0Wrrlt8fQSRL0bGXzK5nWPRV2dYQZdajcfzOD9+cQz9O7ugifrJxclB+xVOvWvagXfqS4Es7vpLP4augw==", - "dev": true, - "dependencies": { - "@eslint-community/eslint-utils": "^4.4.0", - "@types/json-schema": "^7.0.12", - "@types/semver": "^7.5.0", - "@typescript-eslint/scope-manager": "6.15.0", - "@typescript-eslint/types": "6.15.0", - "@typescript-eslint/typescript-estree": "6.15.0", - "semver": "^7.5.4" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^7.0.0 || ^8.0.0" - } - }, - "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/visitor-keys": { - "version": "6.15.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.15.0.tgz", - "integrity": "sha512-1zvtdC1a9h5Tb5jU9x3ADNXO9yjP8rXlaoChu0DQX40vf5ACVpYIVIZhIMZ6d5sDXH7vq4dsZBT1fEGj8D2n2w==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "6.15.0", - "eslint-visitor-keys": "^3.4.1" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/types": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.62.0.tgz", - "integrity": "sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ==", - "dev": true, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/typescript-estree": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.62.0.tgz", - "integrity": "sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "5.62.0", - "@typescript-eslint/visitor-keys": "5.62.0", - "debug": "^4.3.4", - "globby": "^11.1.0", - "is-glob": "^4.0.3", - "semver": "^7.3.7", - "tsutils": "^3.21.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/utils": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.62.0.tgz", - "integrity": "sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ==", - "dev": true, - "dependencies": { - "@eslint-community/eslint-utils": "^4.2.0", - "@types/json-schema": "^7.0.9", - "@types/semver": "^7.3.12", - "@typescript-eslint/scope-manager": "5.62.0", - "@typescript-eslint/types": "5.62.0", - "@typescript-eslint/typescript-estree": "5.62.0", - "eslint-scope": "^5.1.1", - "semver": "^7.3.7" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" - } - }, - "node_modules/@typescript-eslint/visitor-keys": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.62.0.tgz", - "integrity": "sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "5.62.0", - "eslint-visitor-keys": "^3.3.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@ungap/structured-clone": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", - "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", - "dev": true - }, - "node_modules/acorn": { - "version": "8.11.2", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.2.tgz", - "integrity": "sha512-nc0Axzp/0FILLEVsm4fNwLCwMttvhEI263QtVPQcbpfZZ3ts0hLsZGOpE6czNlid7CJ9MlyH8reXkpsf3YUY4w==", - "dev": true, - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/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, - "peerDependencies": { - "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" - } - }, - "node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/ansi-escapes": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", - "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", - "dev": true, - "dependencies": { - "type-fest": "^0.21.3" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ansi-escapes/node_modules/type-fest": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", - "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ansi-regex": { - "version": "5.0.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/ansi-styles": { - "version": "4.3.0", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/anymatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "dev": true, - "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/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 - }, - "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" - } - }, - "node_modules/arrify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", - "integrity": "sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/awaitqueue": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/awaitqueue/-/awaitqueue-3.0.2.tgz", - "integrity": "sha512-AVAtRwmf0DNSesMdyanFKKejTrOnjdKtz5LIDQFu2OTUgXvB/CRTYMrkPAF/2GCF9XBtYVxSwxDORlD41S+RyQ==", - "dependencies": { - "debug": "^4.3.4" - }, - "engines": { - "node": ">=16" - } - }, - "node_modules/babel-jest": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz", - "integrity": "sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==", - "dev": true, - "dependencies": { - "@jest/transform": "^29.7.0", - "@types/babel__core": "^7.1.14", - "babel-plugin-istanbul": "^6.1.1", - "babel-preset-jest": "^29.6.3", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "slash": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "@babel/core": "^7.8.0" - } - }, - "node_modules/babel-plugin-istanbul": { - "version": "6.1.1", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "@babel/helper-plugin-utils": "^7.0.0", - "@istanbuljs/load-nyc-config": "^1.0.0", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-instrument": "^5.0.4", - "test-exclude": "^6.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/babel-plugin-jest-hoist": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz", - "integrity": "sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==", - "dev": true, - "dependencies": { - "@babel/template": "^7.3.3", - "@babel/types": "^7.3.3", - "@types/babel__core": "^7.1.14", - "@types/babel__traverse": "^7.0.6" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/babel-preset-current-node-syntax": { - "version": "1.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/plugin-syntax-async-generators": "^7.8.4", - "@babel/plugin-syntax-bigint": "^7.8.3", - "@babel/plugin-syntax-class-properties": "^7.8.3", - "@babel/plugin-syntax-import-meta": "^7.8.3", - "@babel/plugin-syntax-json-strings": "^7.8.3", - "@babel/plugin-syntax-logical-assignment-operators": "^7.8.3", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", - "@babel/plugin-syntax-numeric-separator": "^7.8.3", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", - "@babel/plugin-syntax-optional-chaining": "^7.8.3", - "@babel/plugin-syntax-top-level-await": "^7.8.3" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/babel-preset-jest": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz", - "integrity": "sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==", - "dev": true, - "dependencies": { - "babel-plugin-jest-hoist": "^29.6.3", - "babel-preset-current-node-syntax": "^1.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/balanced-match": { - "version": "1.0.2", - "dev": true, - "license": "MIT" - }, - "node_modules/big-integer": { - "version": "1.6.52", - "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.52.tgz", - "integrity": "sha512-QxD8cf2eVqJOOz63z6JIN9BzvVs/dlySa5HGSBH5xtR8dPteIRQnBxxKqkNTiT6jbDTF6jAfrd4oMcND9RGbQg==", - "dev": true, - "engines": { - "node": ">=0.6" - } - }, - "node_modules/bplist-parser": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/bplist-parser/-/bplist-parser-0.2.0.tgz", - "integrity": "sha512-z0M+byMThzQmD9NILRniCUXYsYpjwnlO8N5uCFaCqIOpqRsJCrQL9NK3JsD67CN5a08nF5oIL2bD6loTdHOuKw==", - "dev": true, - "dependencies": { - "big-integer": "^1.6.44" - }, - "engines": { - "node": ">= 5.10.0" - } - }, - "node_modules/brace-expansion": { - "version": "1.1.11", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/braces": { - "version": "3.0.2", - "dev": true, - "license": "MIT", - "dependencies": { - "fill-range": "^7.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/browserslist": { - "version": "4.21.4", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - } - ], - "license": "MIT", - "dependencies": { - "caniuse-lite": "^1.0.30001400", - "electron-to-chromium": "^1.4.251", - "node-releases": "^2.0.6", - "update-browserslist-db": "^1.0.9" - }, - "bin": { - "browserslist": "cli.js" - }, - "engines": { - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" - } - }, - "node_modules/bs-logger": { - "version": "0.2.6", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-json-stable-stringify": "2.x" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/bser": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", - "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", - "dev": true, - "dependencies": { - "node-int64": "^0.4.0" - } - }, - "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 - }, - "node_modules/bundle-name": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-3.0.0.tgz", - "integrity": "sha512-PKA4BeSvBpQKQ8iPOGCSiell+N8P+Tf1DlwqmYhpe2gAhKPHn8EYOxVT+ShuGmhg8lN8XiSlS80yiExKXrURlw==", - "dev": true, - "dependencies": { - "run-applescript": "^5.0.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "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, - "engines": { - "node": ">=6" - } - }, - "node_modules/camelcase": { - "version": "5.3.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/camelcase-keys": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-8.0.2.tgz", - "integrity": "sha512-qMKdlOfsjlezMqxkUGGMaWWs17i2HoL15tM+wtx8ld4nLrUwU58TFdvyGOz/piNP842KeO8yXvggVQSdQ828NA==", - "dev": true, - "dependencies": { - "camelcase": "^7.0.0", - "map-obj": "^4.3.0", - "quick-lru": "^6.1.1", - "type-fest": "^2.13.0" - }, - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/camelcase-keys/node_modules/camelcase": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-7.0.1.tgz", - "integrity": "sha512-xlx1yCK2Oc1APsPXDL2LdlNP6+uu8OCDdhOBSVT279M/S+y75O30C2VuD8T2ogdePBBl7PfPF4504tnLgX3zfw==", - "dev": true, - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/camelcase-keys/node_modules/type-fest": { - "version": "2.19.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz", - "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==", - "dev": true, - "engines": { - "node": ">=12.20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/caniuse-lite": { - "version": "1.0.30001431", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/caniuse-lite" - } - ], - "license": "CC-BY-4.0" - }, - "node_modules/chalk": { - "version": "4.1.2", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/chalk/node_modules/has-flag": { - "version": "4.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/chalk/node_modules/supports-color": { - "version": "7.2.0", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/char-regex": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", - "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/ci-info": { - "version": "3.6.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/cjs-module-lexer": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.3.tgz", - "integrity": "sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ==", - "dev": true - }, - "node_modules/cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "dev": true, - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/co": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", - "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", - "dev": true, - "engines": { - "iojs": ">= 1.0.0", - "node": ">= 0.12.0" - } - }, - "node_modules/collect-v8-coverage": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz", - "integrity": "sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==", - "dev": true - }, - "node_modules/color-convert": { - "version": "2.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/color-name": { - "version": "1.1.4", - "dev": true, - "license": "MIT" - }, - "node_modules/concat-map": { - "version": "0.0.1", - "dev": true, - "license": "MIT" - }, - "node_modules/convert-source-map": { - "version": "2.0.0", - "dev": true, - "license": "MIT" - }, - "node_modules/create-jest": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/create-jest/-/create-jest-29.7.0.tgz", - "integrity": "sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==", - "dev": true, - "dependencies": { - "@jest/types": "^29.6.3", - "chalk": "^4.0.0", - "exit": "^0.1.2", - "graceful-fs": "^4.2.9", - "jest-config": "^29.7.0", - "jest-util": "^29.7.0", - "prompts": "^2.0.1" - }, - "bin": { - "create-jest": "bin/create-jest.js" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/cross-spawn": { - "version": "7.0.3", - "dev": true, - "license": "MIT", - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/crypto-random-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-4.0.0.tgz", - "integrity": "sha512-x8dy3RnvYdlUcPOjkEHqozhiwzKNSq7GcPuXFbnyMOCHxX8V3OgIg/pYuabl2sbUPfIJaeAQB7PMOK8DFIdoRA==", - "dev": true, - "dependencies": { - "type-fest": "^1.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/crypto-random-string/node_modules/type-fest": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz", - "integrity": "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/debug": { - "version": "4.3.4", - "license": "MIT", - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/decamelize": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-6.0.0.tgz", - "integrity": "sha512-Fv96DCsdOgB6mdGl67MT5JaTNKRzrzill5OH5s8bjYJXVlcXyPYGyPsUkWyGV5p1TXI5esYIYMMeDJL0hEIwaA==", - "dev": true, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/decamelize-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/decamelize-keys/-/decamelize-keys-1.1.1.tgz", - "integrity": "sha512-WiPxgEirIV0/eIOMcnFBA3/IJZAZqKnwAwWyvvdi4lsr1WCN22nhdf/3db3DoZcUjTV2SqfzIwNyp6y2xs3nmg==", - "dev": true, - "dependencies": { - "decamelize": "^1.1.0", - "map-obj": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/decamelize-keys/node_modules/decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/decamelize-keys/node_modules/map-obj": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", - "integrity": "sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/dedent": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.5.1.tgz", - "integrity": "sha512-+LxW+KLWxu3HW3M2w2ympwtqPrqYRzU8fqi6Fhd18fBALe15blJPI/I4+UHveMVG6lJqB4JNd4UG0S5cnVHwIg==", - "dev": true, - "peerDependencies": { - "babel-plugin-macros": "^3.1.0" - }, - "peerDependenciesMeta": { - "babel-plugin-macros": { - "optional": true - } - } - }, - "node_modules/deep-is": { - "version": "0.1.4", - "dev": true, - "license": "MIT" - }, - "node_modules/deepmerge": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", - "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/default-browser": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-4.0.0.tgz", - "integrity": "sha512-wX5pXO1+BrhMkSbROFsyxUm0i/cJEScyNhA4PPxc41ICuv05ZZB/MX28s8aZx6xjmatvebIapF6hLEKEcpneUA==", - "dev": true, - "dependencies": { - "bundle-name": "^3.0.0", - "default-browser-id": "^3.0.0", - "execa": "^7.1.1", - "titleize": "^3.0.0" - }, - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/default-browser-id": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-3.0.0.tgz", - "integrity": "sha512-OZ1y3y0SqSICtE8DE4S8YOE9UZOJ8wO16fKWVP5J1Qz42kV9jcnMVFrEE/noXb/ss3Q4pZIH79kxofzyNNtUNA==", - "dev": true, - "dependencies": { - "bplist-parser": "^0.2.0", - "untildify": "^4.0.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/default-browser/node_modules/execa": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-7.2.0.tgz", - "integrity": "sha512-UduyVP7TLB5IcAQl+OzLyLcS/l32W/GLg+AhHJ+ow40FOk2U3SAllPwR44v4vmdFwIWqpdwxxpQbF1n5ta9seA==", - "dev": true, - "dependencies": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.1", - "human-signals": "^4.3.0", - "is-stream": "^3.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^5.1.0", - "onetime": "^6.0.0", - "signal-exit": "^3.0.7", - "strip-final-newline": "^3.0.0" - }, - "engines": { - "node": "^14.18.0 || ^16.14.0 || >=18.0.0" - }, - "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" - } - }, - "node_modules/default-browser/node_modules/human-signals": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-4.3.1.tgz", - "integrity": "sha512-nZXjEF2nbo7lIw3mgYjItAfgQXog3OjJogSbKa2CQIIvSGWcKgeJnQlNXip6NglNzYH45nSRiEVimMvYL8DDqQ==", - "dev": true, - "engines": { - "node": ">=14.18.0" - } - }, - "node_modules/default-browser/node_modules/is-stream": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", - "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", - "dev": true, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/default-browser/node_modules/mimic-fn": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", - "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/default-browser/node_modules/npm-run-path": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.2.0.tgz", - "integrity": "sha512-W4/tgAXFqFA0iL7fk0+uQ3g7wkL8xJmx3XdK0VGb4cHW//eZTtKGvFBBoRKVTpY7n6ze4NL9ly7rgXcHufqXKg==", - "dev": true, - "dependencies": { - "path-key": "^4.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/default-browser/node_modules/onetime": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", - "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", - "dev": true, - "dependencies": { - "mimic-fn": "^4.0.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/default-browser/node_modules/path-key": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", - "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/default-browser/node_modules/strip-final-newline": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", - "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/define-lazy-prop": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz", - "integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/detect-newline": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", - "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/diff-sequences": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", - "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", - "dev": true, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "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==", - "dev": true, - "dependencies": { - "path-type": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/doctrine": { - "version": "3.0.0", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "esutils": "^2.0.2" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/electron-to-chromium": { - "version": "1.4.284", - "dev": true, - "license": "ISC" - }, - "node_modules/emittery": { - "version": "0.13.1", - "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz", - "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sindresorhus/emittery?sponsor=1" - } - }, - "node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "node_modules/error-ex": { - "version": "1.3.2", - "dev": true, - "license": "MIT", - "dependencies": { - "is-arrayish": "^0.2.1" - } - }, - "node_modules/escalade": { - "version": "3.1.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/escape-string-regexp": { - "version": "4.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint": { - "version": "8.56.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.56.0.tgz", - "integrity": "sha512-Go19xM6T9puCOWntie1/P997aXxFsOi37JIHRWI514Hc6ZnaHGKY9xFhrU65RT6CcBEzZoGG1e6Nq+DT04ZtZQ==", - "dev": true, - "dependencies": { - "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.6.1", - "@eslint/eslintrc": "^2.1.4", - "@eslint/js": "8.56.0", - "@humanwhocodes/config-array": "^0.11.13", - "@humanwhocodes/module-importer": "^1.0.1", - "@nodelib/fs.walk": "^1.2.8", - "@ungap/structured-clone": "^1.2.0", - "ajv": "^6.12.4", - "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.2.2", - "eslint-visitor-keys": "^3.4.3", - "espree": "^9.6.1", - "esquery": "^1.4.2", - "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.2", - "globals": "^13.19.0", - "graphemer": "^1.4.0", - "ignore": "^5.2.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "is-path-inside": "^3.0.3", - "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.3", - "strip-ansi": "^6.0.1", - "text-table": "^0.2.0" - }, - "bin": { - "eslint": "bin/eslint.js" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint-plugin-jest": { - "version": "27.6.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-27.6.0.tgz", - "integrity": "sha512-MTlusnnDMChbElsszJvrwD1dN3x6nZl//s4JD23BxB6MgR66TZlL064su24xEIS3VACfAoHV1vgyMgPw8nkdng==", - "dev": true, - "dependencies": { - "@typescript-eslint/utils": "^5.10.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "@typescript-eslint/eslint-plugin": "^5.0.0 || ^6.0.0", - "eslint": "^7.0.0 || ^8.0.0", - "jest": "*" - }, - "peerDependenciesMeta": { - "@typescript-eslint/eslint-plugin": { - "optional": true - }, - "jest": { - "optional": true - } - } - }, - "node_modules/eslint-scope": { - "version": "5.1.1", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "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, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint/node_modules/eslint-scope": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", - "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", - "dev": true, - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint/node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/espree": { - "version": "9.6.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", - "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", - "dev": true, - "dependencies": { - "acorn": "^8.9.0", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.4.1" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/esprima": { - "version": "4.0.1", - "dev": true, - "license": "BSD-2-Clause", - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/esquery": { - "version": "1.5.0", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "estraverse": "^5.1.0" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/esquery/node_modules/estraverse": { - "version": "5.3.0", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=4.0" - } - }, - "node_modules/esrecurse": { - "version": "4.3.0", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "estraverse": "^5.2.0" - }, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/esrecurse/node_modules/estraverse": { - "version": "5.3.0", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=4.0" - } - }, - "node_modules/estraverse": { - "version": "4.3.0", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=4.0" - } - }, - "node_modules/esutils": { - "version": "2.0.3", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/event-target-shim": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-6.0.2.tgz", - "integrity": "sha512-8q3LsZjRezbFZ2PN+uP+Q7pnHUMmAOziU2vA2OwoFaKIXxlxl38IylhSSgUorWu/rf4er67w0ikBqjBFk/pomA==", - "engines": { - "node": ">=10.13.0" - }, - "funding": { - "url": "https://github.com/sponsors/mysticatea" - } - }, - "node_modules/events": { - "version": "3.3.0", - "license": "MIT", - "engines": { - "node": ">=0.8.x" - } - }, - "node_modules/execa": { - "version": "5.1.1", - "dev": true, - "license": "MIT", - "dependencies": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.0", - "human-signals": "^2.1.0", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.1", - "onetime": "^5.1.2", - "signal-exit": "^3.0.3", - "strip-final-newline": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" - } - }, - "node_modules/exit": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", - "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", - "dev": true, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/expect": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/expect/-/expect-29.7.0.tgz", - "integrity": "sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==", - "dev": true, - "dependencies": { - "@jest/expect-utils": "^29.7.0", - "jest-get-type": "^29.6.3", - "jest-matcher-utils": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/fake-mediastreamtrack": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/fake-mediastreamtrack/-/fake-mediastreamtrack-1.2.0.tgz", - "integrity": "sha512-AxHtlEmka1sqNoe3Ej1H1hJc9gjjO/6vCbCPm4D4QeEXvzhjYumA+iZ7wOi2WrmkAhGElHhBgWoNgJhFccectA==", - "dependencies": { - "event-target-shim": "^6.0.2", - "uuid": "^9.0.0" - } - }, - "node_modules/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 - }, - "node_modules/fast-glob": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.0.tgz", - "integrity": "sha512-ChDuvbOypPuNjO8yIDf36x7BlZX1smcUMTTcyoIjycexOxd6DFsKsg21qVBzEmr3G7fUKIRy2/psii+CIUt7FA==", - "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" - } - }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "dev": true, - "license": "MIT" - }, - "node_modules/fast-levenshtein": { - "version": "2.0.6", - "dev": true, - "license": "MIT" - }, - "node_modules/fastq": { - "version": "1.13.0", - "dev": true, - "license": "ISC", - "dependencies": { - "reusify": "^1.0.4" - } - }, - "node_modules/fb-watchman": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", - "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", - "dev": true, - "dependencies": { - "bser": "2.1.1" - } - }, - "node_modules/file-entry-cache": { - "version": "6.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "flat-cache": "^3.0.4" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - } - }, - "node_modules/file-type": { - "version": "18.7.0", - "resolved": "https://registry.npmjs.org/file-type/-/file-type-18.7.0.tgz", - "integrity": "sha512-ihHtXRzXEziMrQ56VSgU7wkxh55iNchFkosu7Y9/S+tXHdKyrGjVK0ujbqNnsxzea+78MaLhN6PGmfYSAv1ACw==", - "dev": true, - "dependencies": { - "readable-web-to-node-stream": "^3.0.2", - "strtok3": "^7.0.0", - "token-types": "^5.0.1" - }, - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sindresorhus/file-type?sponsor=1" - } - }, - "node_modules/fill-range": { - "version": "7.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/find-up": { - "version": "5.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/flat-cache": { - "version": "3.0.4", - "dev": true, - "license": "MIT", - "dependencies": { - "flatted": "^3.1.0", - "rimraf": "^3.0.2" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - } - }, - "node_modules/flatted": { - "version": "3.2.7", - "dev": true, - "license": "ISC" - }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "dev": true, - "license": "ISC" - }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/function-bind": { - "version": "1.1.1", - "dev": true, - "license": "MIT" - }, - "node_modules/gensync": { - "version": "1.0.0-beta.2", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true, - "engines": { - "node": "6.* || 8.* || >= 10.*" - } - }, - "node_modules/get-package-type": { - "version": "0.1.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/get-stdin": { - "version": "9.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/get-stream": { - "version": "6.0.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/glob": { - "version": "7.2.3", - "dev": true, - "license": "ISC", - "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" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/glob-parent": { - "version": "6.0.2", - "dev": true, - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.3" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/globals": { - "version": "13.23.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.23.0.tgz", - "integrity": "sha512-XAmF0RjlrjY23MA51q3HltdlGxUpXPvg0GioKiD9X6HD28iMjo2dKC8Vqwm7lne4GNr78+RHTfliktR6ZH09wA==", - "dev": true, - "dependencies": { - "type-fest": "^0.20.2" - }, - "engines": { - "node": ">=8" - }, - "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==", - "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/graceful-fs": { - "version": "4.2.10", - "dev": true, - "license": "ISC" - }, - "node_modules/graphemer": { - "version": "1.4.0", - "dev": true, - "license": "MIT" - }, - "node_modules/h264-profile-level-id": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/h264-profile-level-id/-/h264-profile-level-id-2.0.0.tgz", - "integrity": "sha512-X4CLryVbVA0CtjTExS4G5U1gb2Z4wa32AF8ukVmFuLdw2JRq2aHisor7SY5SYTUUrUSqq0KdPIO18sql6IWIQw==", - "dependencies": { - "@types/debug": "^4.1.12", - "debug": "^4.3.4" - }, - "engines": { - "node": ">=16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/mediasoup" - } - }, - "node_modules/hard-rejection": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/hard-rejection/-/hard-rejection-2.1.0.tgz", - "integrity": "sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/has": { - "version": "1.0.3", - "dev": true, - "license": "MIT", - "dependencies": { - "function-bind": "^1.1.1" - }, - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/has-flag": { - "version": "3.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/hosted-git-info": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-5.2.1.tgz", - "integrity": "sha512-xIcQYMnhcx2Nr4JTjsFmwwnr9vldugPy9uVm0o87bjqqWMv9GaqsTeT+i99wTl0mk1uLxJtHxLb8kymqTENQsw==", - "dev": true, - "dependencies": { - "lru-cache": "^7.5.1" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/hosted-git-info/node_modules/lru-cache": { - "version": "7.18.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", - "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", - "dev": true, - "engines": { - "node": ">=12" - } - }, - "node_modules/html-escaper": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", - "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", - "dev": true - }, - "node_modules/human-signals": { - "version": "2.1.0", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=10.17.0" - } - }, - "node_modules/ieee754": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "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/ignore": { - "version": "5.2.4", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", - "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", - "dev": true, - "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==", - "dev": true, - "dependencies": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/import-local": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz", - "integrity": "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==", - "dev": true, - "dependencies": { - "pkg-dir": "^4.2.0", - "resolve-cwd": "^3.0.0" - }, - "bin": { - "import-local-fixture": "fixtures/cli.js" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.8.19" - } - }, - "node_modules/indent-string": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-5.0.0.tgz", - "integrity": "sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/inflight": { - "version": "1.0.6", - "dev": true, - "license": "ISC", - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "dev": true, - "license": "ISC" - }, - "node_modules/is-arrayish": { - "version": "0.2.1", - "dev": true, - "license": "MIT" - }, - "node_modules/is-core-module": { - "version": "2.13.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.0.tgz", - "integrity": "sha512-Z7dk6Qo8pOCp3l4tsX2C5ZVas4V+UxwQodwZhLopL91TX8UyyHEXafPcyoeeWuLrwzHcr3igO78wNLwHJHsMCQ==", - "dev": true, - "dependencies": { - "has": "^1.0.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-docker": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz", - "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==", - "dev": true, - "bin": { - "is-docker": "cli.js" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-generator-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", - "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/is-glob": { - "version": "4.0.3", - "dev": true, - "license": "MIT", - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-inside-container": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz", - "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==", - "dev": true, - "dependencies": { - "is-docker": "^3.0.0" - }, - "bin": { - "is-inside-container": "cli.js" - }, - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-number": { - "version": "7.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/is-path-inside": { - "version": "3.0.3", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/is-plain-obj": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", - "integrity": "sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-stream": { - "version": "2.0.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-wsl": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", - "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", - "dev": true, - "dependencies": { - "is-docker": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-wsl/node_modules/is-docker": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", - "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", - "dev": true, - "bin": { - "is-docker": "cli.js" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/isexe": { - "version": "2.0.0", - "dev": true, - "license": "ISC" - }, - "node_modules/istanbul-lib-coverage": { - "version": "3.2.0", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-instrument": { - "version": "5.2.1", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "@babel/core": "^7.12.3", - "@babel/parser": "^7.14.7", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-coverage": "^3.2.0", - "semver": "^6.3.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-instrument/node_modules/semver": { - "version": "6.3.0", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/istanbul-lib-report": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", - "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", - "dev": true, - "dependencies": { - "istanbul-lib-coverage": "^3.0.0", - "make-dir": "^4.0.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-lib-report/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, - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-report/node_modules/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, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-source-maps": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", - "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", - "dev": true, - "dependencies": { - "debug": "^4.1.1", - "istanbul-lib-coverage": "^3.0.0", - "source-map": "^0.6.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-reports": { - "version": "3.1.6", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.6.tgz", - "integrity": "sha512-TLgnMkKg3iTDsQ9PbPTdpfAK2DzjF9mqUG7RMgcQl8oFjad8ob4laGxv5XV5U9MAfx8D6tSJiUyuAwzLicaxlg==", - "dev": true, - "dependencies": { - "html-escaper": "^2.0.0", - "istanbul-lib-report": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz", - "integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==", - "dev": true, - "dependencies": { - "@jest/core": "^29.7.0", - "@jest/types": "^29.6.3", - "import-local": "^3.0.2", - "jest-cli": "^29.7.0" - }, - "bin": { - "jest": "bin/jest.js" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/jest-changed-files": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.7.0.tgz", - "integrity": "sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==", - "dev": true, - "dependencies": { - "execa": "^5.0.0", - "jest-util": "^29.7.0", - "p-limit": "^3.1.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-circus": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.7.0.tgz", - "integrity": "sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==", - "dev": true, - "dependencies": { - "@jest/environment": "^29.7.0", - "@jest/expect": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "co": "^4.6.0", - "dedent": "^1.0.0", - "is-generator-fn": "^2.0.0", - "jest-each": "^29.7.0", - "jest-matcher-utils": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-runtime": "^29.7.0", - "jest-snapshot": "^29.7.0", - "jest-util": "^29.7.0", - "p-limit": "^3.1.0", - "pretty-format": "^29.7.0", - "pure-rand": "^6.0.0", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-cli": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.7.0.tgz", - "integrity": "sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==", - "dev": true, - "dependencies": { - "@jest/core": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/types": "^29.6.3", - "chalk": "^4.0.0", - "create-jest": "^29.7.0", - "exit": "^0.1.2", - "import-local": "^3.0.2", - "jest-config": "^29.7.0", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "yargs": "^17.3.1" - }, - "bin": { - "jest": "bin/jest.js" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/jest-config": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.7.0.tgz", - "integrity": "sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==", - "dev": true, - "dependencies": { - "@babel/core": "^7.11.6", - "@jest/test-sequencer": "^29.7.0", - "@jest/types": "^29.6.3", - "babel-jest": "^29.7.0", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "deepmerge": "^4.2.2", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "jest-circus": "^29.7.0", - "jest-environment-node": "^29.7.0", - "jest-get-type": "^29.6.3", - "jest-regex-util": "^29.6.3", - "jest-resolve": "^29.7.0", - "jest-runner": "^29.7.0", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "micromatch": "^4.0.4", - "parse-json": "^5.2.0", - "pretty-format": "^29.7.0", - "slash": "^3.0.0", - "strip-json-comments": "^3.1.1" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "@types/node": "*", - "ts-node": ">=9.0.0" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - }, - "ts-node": { - "optional": true - } - } - }, - "node_modules/jest-diff": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz", - "integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==", - "dev": true, - "dependencies": { - "chalk": "^4.0.0", - "diff-sequences": "^29.6.3", - "jest-get-type": "^29.6.3", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-docblock": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.7.0.tgz", - "integrity": "sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==", - "dev": true, - "dependencies": { - "detect-newline": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-each": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.7.0.tgz", - "integrity": "sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==", - "dev": true, - "dependencies": { - "@jest/types": "^29.6.3", - "chalk": "^4.0.0", - "jest-get-type": "^29.6.3", - "jest-util": "^29.7.0", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-environment-node": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.7.0.tgz", - "integrity": "sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==", - "dev": true, - "dependencies": { - "@jest/environment": "^29.7.0", - "@jest/fake-timers": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "jest-mock": "^29.7.0", - "jest-util": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-get-type": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", - "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", - "dev": true, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-haste-map": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz", - "integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==", - "dev": true, - "dependencies": { - "@jest/types": "^29.6.3", - "@types/graceful-fs": "^4.1.3", - "@types/node": "*", - "anymatch": "^3.0.3", - "fb-watchman": "^2.0.0", - "graceful-fs": "^4.2.9", - "jest-regex-util": "^29.6.3", - "jest-util": "^29.7.0", - "jest-worker": "^29.7.0", - "micromatch": "^4.0.4", - "walker": "^1.0.8" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "optionalDependencies": { - "fsevents": "^2.3.2" - } - }, - "node_modules/jest-leak-detector": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz", - "integrity": "sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==", - "dev": true, - "dependencies": { - "jest-get-type": "^29.6.3", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-matcher-utils": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz", - "integrity": "sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==", - "dev": true, - "dependencies": { - "chalk": "^4.0.0", - "jest-diff": "^29.7.0", - "jest-get-type": "^29.6.3", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-message-util": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.7.0.tgz", - "integrity": "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.12.13", - "@jest/types": "^29.6.3", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "micromatch": "^4.0.4", - "pretty-format": "^29.7.0", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-mock": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.7.0.tgz", - "integrity": "sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==", - "dev": true, - "dependencies": { - "@jest/types": "^29.6.3", - "@types/node": "*", - "jest-util": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-pnp-resolver": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", - "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", - "dev": true, - "engines": { - "node": ">=6" - }, - "peerDependencies": { - "jest-resolve": "*" - }, - "peerDependenciesMeta": { - "jest-resolve": { - "optional": true - } - } - }, - "node_modules/jest-regex-util": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz", - "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==", - "dev": true, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-resolve": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.7.0.tgz", - "integrity": "sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==", - "dev": true, - "dependencies": { - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "jest-pnp-resolver": "^1.2.2", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "resolve": "^1.20.0", - "resolve.exports": "^2.0.0", - "slash": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-resolve-dependencies": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.7.0.tgz", - "integrity": "sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==", - "dev": true, - "dependencies": { - "jest-regex-util": "^29.6.3", - "jest-snapshot": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-runner": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.7.0.tgz", - "integrity": "sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==", - "dev": true, - "dependencies": { - "@jest/console": "^29.7.0", - "@jest/environment": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "emittery": "^0.13.1", - "graceful-fs": "^4.2.9", - "jest-docblock": "^29.7.0", - "jest-environment-node": "^29.7.0", - "jest-haste-map": "^29.7.0", - "jest-leak-detector": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-resolve": "^29.7.0", - "jest-runtime": "^29.7.0", - "jest-util": "^29.7.0", - "jest-watcher": "^29.7.0", - "jest-worker": "^29.7.0", - "p-limit": "^3.1.0", - "source-map-support": "0.5.13" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-runtime": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.7.0.tgz", - "integrity": "sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==", - "dev": true, - "dependencies": { - "@jest/environment": "^29.7.0", - "@jest/fake-timers": "^29.7.0", - "@jest/globals": "^29.7.0", - "@jest/source-map": "^29.6.3", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "cjs-module-lexer": "^1.0.0", - "collect-v8-coverage": "^1.0.0", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-mock": "^29.7.0", - "jest-regex-util": "^29.6.3", - "jest-resolve": "^29.7.0", - "jest-snapshot": "^29.7.0", - "jest-util": "^29.7.0", - "slash": "^3.0.0", - "strip-bom": "^4.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-snapshot": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.7.0.tgz", - "integrity": "sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==", - "dev": true, - "dependencies": { - "@babel/core": "^7.11.6", - "@babel/generator": "^7.7.2", - "@babel/plugin-syntax-jsx": "^7.7.2", - "@babel/plugin-syntax-typescript": "^7.7.2", - "@babel/types": "^7.3.3", - "@jest/expect-utils": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "babel-preset-current-node-syntax": "^1.0.0", - "chalk": "^4.0.0", - "expect": "^29.7.0", - "graceful-fs": "^4.2.9", - "jest-diff": "^29.7.0", - "jest-get-type": "^29.6.3", - "jest-matcher-utils": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0", - "natural-compare": "^1.4.0", - "pretty-format": "^29.7.0", - "semver": "^7.5.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-util": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", - "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", - "dev": true, - "dependencies": { - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-validate": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz", - "integrity": "sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==", - "dev": true, - "dependencies": { - "@jest/types": "^29.6.3", - "camelcase": "^6.2.0", - "chalk": "^4.0.0", - "jest-get-type": "^29.6.3", - "leven": "^3.1.0", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-validate/node_modules/camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/jest-watcher": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.7.0.tgz", - "integrity": "sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==", - "dev": true, - "dependencies": { - "@jest/test-result": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "emittery": "^0.13.1", - "jest-util": "^29.7.0", - "string-length": "^4.0.1" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-worker": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", - "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", - "dev": true, - "dependencies": { - "@types/node": "*", - "jest-util": "^29.7.0", - "merge-stream": "^2.0.0", - "supports-color": "^8.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-worker/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, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-worker/node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, - "node_modules/js-tokens": { - "version": "4.0.0", - "dev": true, - "license": "MIT" - }, - "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, - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/jsesc": { - "version": "2.5.2", - "dev": true, - "license": "MIT", - "bin": { - "jsesc": "bin/jsesc" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/json-parse-even-better-errors": { - "version": "2.3.1", - "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 - }, - "node_modules/json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "dev": true, - "license": "MIT" - }, - "node_modules/json5": { - "version": "2.2.3", - "dev": true, - "license": "MIT", - "bin": { - "json5": "lib/cli.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/kleur": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", - "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/leven": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", - "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/levn": { - "version": "0.4.1", - "dev": true, - "license": "MIT", - "dependencies": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/lines-and-columns": { - "version": "1.2.4", - "dev": true, - "license": "MIT" - }, - "node_modules/locate-path": { - "version": "6.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "p-locate": "^5.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/lodash.memoize": { - "version": "4.1.2", - "dev": true, - "license": "MIT" - }, - "node_modules/lodash.merge": { - "version": "4.6.2", - "dev": true, - "license": "MIT" - }, - "node_modules/lru-cache": { - "version": "6.0.0", - "dev": true, - "license": "ISC", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/make-dir": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", - "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", - "dev": true, - "dependencies": { - "semver": "^7.5.3" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/make-error": { - "version": "1.3.6", - "dev": true, - "license": "ISC" - }, - "node_modules/makeerror": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", - "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", - "dev": true, - "dependencies": { - "tmpl": "1.0.5" - } - }, - "node_modules/map-obj": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.3.0.tgz", - "integrity": "sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==", - "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/meow": { - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/meow/-/meow-11.0.0.tgz", - "integrity": "sha512-Cl0yeeIrko6d94KpUo1M+0X1sB14ikoaqlIGuTH1fW4I+E3+YljL54/hb/BWmVfrV9tTV9zU04+xjw08Fh2WkA==", - "dev": true, - "dependencies": { - "@types/minimist": "^1.2.2", - "camelcase-keys": "^8.0.2", - "decamelize": "^6.0.0", - "decamelize-keys": "^1.1.0", - "hard-rejection": "^2.1.0", - "minimist-options": "4.1.0", - "normalize-package-data": "^4.0.1", - "read-pkg-up": "^9.1.0", - "redent": "^4.0.0", - "trim-newlines": "^4.0.2", - "type-fest": "^3.1.0", - "yargs-parser": "^21.1.1" - }, - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/meow/node_modules/type-fest": { - "version": "3.13.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-3.13.1.tgz", - "integrity": "sha512-tLq3bSNx+xSpwvAJnzrK0Ep5CLNWjvFTOp71URMaAEWBfRb9nnJiBoUe0tF8bI4ZFO3omgBR6NvnbzVUT3Ly4g==", - "dev": true, - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/merge-stream": { - "version": "2.0.0", - "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==", - "dev": true, - "engines": { - "node": ">= 8" - } - }, - "node_modules/micromatch": { - "version": "4.0.5", - "dev": true, - "license": "MIT", - "dependencies": { - "braces": "^3.0.2", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/mimic-fn": { - "version": "2.1.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/min-indent": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", - "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/minimatch": { - "version": "3.1.2", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/minimist-options": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/minimist-options/-/minimist-options-4.1.0.tgz", - "integrity": "sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==", - "dev": true, - "dependencies": { - "arrify": "^1.0.1", - "is-plain-obj": "^1.1.0", - "kind-of": "^6.0.3" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/ms": { - "version": "2.1.2", - "license": "MIT" - }, - "node_modules/natural-compare": { - "version": "1.4.0", - "dev": true, - "license": "MIT" - }, - "node_modules/node-int64": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", - "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", - "dev": true - }, - "node_modules/node-releases": { - "version": "2.0.6", - "dev": true, - "license": "MIT" - }, - "node_modules/normalize-package-data": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-4.0.1.tgz", - "integrity": "sha512-EBk5QKKuocMJhB3BILuKhmaPjI8vNRSpIfO9woLC6NyHVkKKdVEdAO1mrT0ZfxNR1lKwCcTkuZfmGIFdizZ8Pg==", - "dev": true, - "dependencies": { - "hosted-git-info": "^5.0.0", - "is-core-module": "^2.8.1", - "semver": "^7.3.5", - "validate-npm-package-license": "^3.0.4" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/npm-run-path": { - "version": "4.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "path-key": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/once": { - "version": "1.4.0", - "dev": true, - "license": "ISC", - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/onetime": { - "version": "5.1.2", - "dev": true, - "license": "MIT", - "dependencies": { - "mimic-fn": "^2.1.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/open": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/open/-/open-9.1.0.tgz", - "integrity": "sha512-OS+QTnw1/4vrf+9hh1jc1jnYjzSG4ttTBB8UxOwAnInG3Uo4ssetzC1ihqaIHjLJnA5GGlRl6QlZXOTQhRBUvg==", - "dev": true, - "dependencies": { - "default-browser": "^4.0.0", - "define-lazy-prop": "^3.0.0", - "is-inside-container": "^1.0.0", - "is-wsl": "^2.2.0" - }, - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/open-cli": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/open-cli/-/open-cli-7.2.0.tgz", - "integrity": "sha512-1ANJc8oJ92FiaNZ0o2Hw4WBvDJoXs1P74aFMtpAvlbkIPV4uPcQvDz7V6kMOrsZkmB4tglrHVMlLQaafuUuxXg==", - "dev": true, - "dependencies": { - "file-type": "^18.2.1", - "get-stdin": "^9.0.0", - "meow": "^11.0.0", - "open": "^9.0.0", - "tempy": "^3.0.0" - }, - "bin": { - "open-cli": "cli.js" - }, - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/optionator": { - "version": "0.9.3", - "dev": true, - "license": "MIT", - "dependencies": { - "@aashutoshrathi/word-wrap": "^1.2.3", - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/p-limit": { - "version": "3.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "yocto-queue": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-locate": { - "version": "5.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "p-limit": "^3.0.2" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-try": { - "version": "2.2.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "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, - "dependencies": { - "callsites": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/parse-json": { - "version": "5.2.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/path-exists": { - "version": "4.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/path-key": { - "version": "3.1.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true - }, - "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/peek-readable": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/peek-readable/-/peek-readable-5.0.0.tgz", - "integrity": "sha512-YtCKvLUOvwtMGmrniQPdO7MwPjgkFBtFIrmfSbYmYuq3tKDV/mcfAhBth1+C3ru7uXIZasc/pHnb+YDYNkkj4A==", - "dev": true, - "engines": { - "node": ">=14.16" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Borewit" - } - }, - "node_modules/picocolors": { - "version": "1.0.0", - "dev": true, - "license": "ISC" - }, - "node_modules/picomatch": { - "version": "2.3.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/pirates": { - "version": "4.0.5", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 6" - } - }, - "node_modules/pkg-dir": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", - "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", - "dev": true, - "dependencies": { - "find-up": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/pkg-dir/node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/pkg-dir/node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "dependencies": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/pkg-dir/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/pkg-dir/node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/prelude-ls": { - "version": "1.2.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/pretty-format": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", - "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", - "dev": true, - "dependencies": { - "@jest/schemas": "^29.6.3", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/pretty-format/node_modules/ansi-styles": { - "version": "5.2.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/prompts": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", - "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", - "dev": true, - "dependencies": { - "kleur": "^3.0.3", - "sisteransi": "^1.0.5" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/punycode": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/pure-rand": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.0.3.tgz", - "integrity": "sha512-KddyFewCsO0j3+np81IQ+SweXLDnDQTs5s67BOnrYmYe/yNmUhttQyGsYzy8yUnoljGAQ9sl38YB4vH8ur7Y+w==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/dubzzz" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fast-check" - } - ] - }, - "node_modules/queue-microtask": { - "version": "1.2.3", - "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/quick-lru": { - "version": "6.1.2", - "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-6.1.2.tgz", - "integrity": "sha512-AAFUA5O1d83pIHEhJwWCq/RQcRukCkn/NSm2QsTEMle5f2hP0ChI2+3Xb051PZCkLryI/Ir1MVKviT2FIloaTQ==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/react-is": { - "version": "18.2.0", - "dev": true, - "license": "MIT" - }, - "node_modules/read-pkg": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-7.1.0.tgz", - "integrity": "sha512-5iOehe+WF75IccPc30bWTbpdDQLOCc3Uu8bi3Dte3Eueij81yx1Mrufk8qBx/YAbR4uL1FdUr+7BKXDwEtisXg==", - "dev": true, - "dependencies": { - "@types/normalize-package-data": "^2.4.1", - "normalize-package-data": "^3.0.2", - "parse-json": "^5.2.0", - "type-fest": "^2.0.0" - }, - "engines": { - "node": ">=12.20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/read-pkg-up": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-9.1.0.tgz", - "integrity": "sha512-vaMRR1AC1nrd5CQM0PhlRsO5oc2AAigqr7cCrZ/MW/Rsaflz4RlgzkpL4qoU/z1F6wrbd85iFv1OQj/y5RdGvg==", - "dev": true, - "dependencies": { - "find-up": "^6.3.0", - "read-pkg": "^7.1.0", - "type-fest": "^2.5.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/read-pkg-up/node_modules/find-up": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-6.3.0.tgz", - "integrity": "sha512-v2ZsoEuVHYy8ZIlYqwPe/39Cy+cFDzp4dXPaxNvkEuouymu+2Jbz0PxpKarJHYJTmv2HWT3O382qY8l4jMWthw==", - "dev": true, - "dependencies": { - "locate-path": "^7.1.0", - "path-exists": "^5.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/read-pkg-up/node_modules/locate-path": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-7.2.0.tgz", - "integrity": "sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA==", - "dev": true, - "dependencies": { - "p-locate": "^6.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/read-pkg-up/node_modules/p-limit": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-4.0.0.tgz", - "integrity": "sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==", - "dev": true, - "dependencies": { - "yocto-queue": "^1.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/read-pkg-up/node_modules/p-locate": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-6.0.0.tgz", - "integrity": "sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==", - "dev": true, - "dependencies": { - "p-limit": "^4.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/read-pkg-up/node_modules/path-exists": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-5.0.0.tgz", - "integrity": "sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==", - "dev": true, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - } - }, - "node_modules/read-pkg-up/node_modules/type-fest": { - "version": "2.19.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz", - "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==", - "dev": true, - "engines": { - "node": ">=12.20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/read-pkg-up/node_modules/yocto-queue": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.0.0.tgz", - "integrity": "sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==", - "dev": true, - "engines": { - "node": ">=12.20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/read-pkg/node_modules/hosted-git-info": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz", - "integrity": "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==", - "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/read-pkg/node_modules/normalize-package-data": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.3.tgz", - "integrity": "sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA==", - "dev": true, - "dependencies": { - "hosted-git-info": "^4.0.1", - "is-core-module": "^2.5.0", - "semver": "^7.3.4", - "validate-npm-package-license": "^3.0.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/read-pkg/node_modules/type-fest": { - "version": "2.19.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz", - "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==", - "dev": true, - "engines": { - "node": ">=12.20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "dev": true, - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/readable-web-to-node-stream": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/readable-web-to-node-stream/-/readable-web-to-node-stream-3.0.2.tgz", - "integrity": "sha512-ePeK6cc1EcKLEhJFt/AebMCLL+GgSKhuygrZ/GLaKZYEecIgIECf4UaUuaByiGtzckwR4ain9VzUh95T1exYGw==", - "dev": true, - "dependencies": { - "readable-stream": "^3.6.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Borewit" - } - }, - "node_modules/redent": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/redent/-/redent-4.0.0.tgz", - "integrity": "sha512-tYkDkVVtYkSVhuQ4zBgfvciymHaeuel+zFKXShfDnFP5SyVEP7qo70Rf1jTOTCx3vGNAbnEi/xFkcfQVMIBWag==", - "dev": true, - "dependencies": { - "indent-string": "^5.0.0", - "strip-indent": "^4.0.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/resolve": { - "version": "1.22.4", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.4.tgz", - "integrity": "sha512-PXNdCiPqDqeUou+w1C2eTQbNfxKSuMxqTCuvlmmMsk1NWHL5fRrhY6Pl0qEYYc6+QqGClco1Qj8XnjPego4wfg==", - "dev": true, - "dependencies": { - "is-core-module": "^2.13.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/resolve-cwd": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", - "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", - "dev": true, - "dependencies": { - "resolve-from": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/resolve-cwd/node_modules/resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "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, - "engines": { - "node": ">=4" - } - }, - "node_modules/resolve.exports": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.2.tgz", - "integrity": "sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/reusify": { - "version": "1.0.4", - "dev": true, - "license": "MIT", - "engines": { - "iojs": ">=1.0.0", - "node": ">=0.10.0" - } - }, - "node_modules/rimraf": { - "version": "3.0.2", - "dev": true, - "license": "ISC", - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/run-applescript": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-5.0.0.tgz", - "integrity": "sha512-XcT5rBksx1QdIhlFOCtgZkB99ZEouFZ1E2Kc2LHqNW13U3/74YGdkQRmThTwxy4QIyookibDKYZOPqX//6BlAg==", - "dev": true, - "dependencies": { - "execa": "^5.0.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/run-parallel": { - "version": "1.2.0", - "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" - } - ], - "license": "MIT", - "dependencies": { - "queue-microtask": "^1.2.2" - } - }, - "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==", - "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/sdp-transform": { - "version": "2.14.1", - "license": "MIT", - "bin": { - "sdp-verify": "checker.js" - } - }, - "node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/shebang-command": { - "version": "2.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/signal-exit": { - "version": "3.0.7", - "dev": true, - "license": "ISC" - }, - "node_modules/sisteransi": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", - "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", - "dev": true - }, - "node_modules/slash": { - "version": "3.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "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, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/source-map-support": { - "version": "0.5.13", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", - "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", - "dev": true, - "dependencies": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, - "node_modules/spdx-correct": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", - "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", - "dev": true, - "dependencies": { - "spdx-expression-parse": "^3.0.0", - "spdx-license-ids": "^3.0.0" - } - }, - "node_modules/spdx-exceptions": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", - "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", - "dev": true - }, - "node_modules/spdx-expression-parse": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", - "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", - "dev": true, - "dependencies": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" - } - }, - "node_modules/spdx-license-ids": { - "version": "3.0.16", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.16.tgz", - "integrity": "sha512-eWN+LnM3GR6gPu35WxNgbGl8rmY1AEmoMDvL/QD6zYmPWgywxWqJWNdLGT+ke8dKNWrcYgYjPpG5gbTfghP8rw==", - "dev": true - }, - "node_modules/sprintf-js": { - "version": "1.0.3", - "dev": true, - "license": "BSD-3-Clause" - }, - "node_modules/stack-utils": { - "version": "2.0.6", - "dev": true, - "license": "MIT", - "dependencies": { - "escape-string-regexp": "^2.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/stack-utils/node_modules/escape-string-regexp": { - "version": "2.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dev": true, - "dependencies": { - "safe-buffer": "~5.2.0" - } - }, - "node_modules/string-length": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", - "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", - "dev": true, - "dependencies": { - "char-regex": "^1.0.2", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-ansi": { - "version": "6.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-bom": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", - "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-final-newline": { - "version": "2.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/strip-indent": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-4.0.0.tgz", - "integrity": "sha512-mnVSV2l+Zv6BLpSD/8V87CW/y9EmmbYzGCIavsnsI6/nwn26DwffM/yztm30Z/I2DY9wdS3vXVCMnHDgZaVNoA==", - "dev": true, - "dependencies": { - "min-indent": "^1.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/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, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/strtok3": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/strtok3/-/strtok3-7.0.0.tgz", - "integrity": "sha512-pQ+V+nYQdC5H3Q7qBZAz/MO6lwGhoC2gOAjuouGf/VO0m7vQRh8QNMl2Uf6SwAtzZ9bOw3UIeBukEGNJl5dtXQ==", - "dev": true, - "dependencies": { - "@tokenizer/token": "^0.3.0", - "peek-readable": "^5.0.0" - }, - "engines": { - "node": ">=14.16" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Borewit" - } - }, - "node_modules/supports-color": { - "version": "9.4.0", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, - "node_modules/supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/temp-dir": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-3.0.0.tgz", - "integrity": "sha512-nHc6S/bwIilKHNRgK/3jlhDoIHcp45YgyiwcAk46Tr0LfEqGBVpmiAyuiuxeVE44m3mXnEeVhaipLOEWmH+Njw==", - "dev": true, - "engines": { - "node": ">=14.16" - } - }, - "node_modules/tempy": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/tempy/-/tempy-3.1.0.tgz", - "integrity": "sha512-7jDLIdD2Zp0bDe5r3D2qtkd1QOCacylBuL7oa4udvN6v2pqr4+LcCr67C8DR1zkpaZ8XosF5m1yQSabKAW6f2g==", - "dev": true, - "dependencies": { - "is-stream": "^3.0.0", - "temp-dir": "^3.0.0", - "type-fest": "^2.12.2", - "unique-string": "^3.0.0" - }, - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/tempy/node_modules/is-stream": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", - "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", - "dev": true, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/tempy/node_modules/type-fest": { - "version": "2.19.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz", - "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==", - "dev": true, - "engines": { - "node": ">=12.20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/test-exclude": { - "version": "6.0.0", - "dev": true, - "license": "ISC", - "dependencies": { - "@istanbuljs/schema": "^0.1.2", - "glob": "^7.1.4", - "minimatch": "^3.0.4" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/text-table": { - "version": "0.2.0", - "dev": true, - "license": "MIT" - }, - "node_modules/titleize": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/titleize/-/titleize-3.0.0.tgz", - "integrity": "sha512-KxVu8EYHDPBdUYdKZdKtU2aj2XfEx9AfjXxE/Aj0vT06w2icA09Vus1rh6eSu1y01akYg6BjIK/hxyLJINoMLQ==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/tmpl": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", - "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", - "dev": true - }, - "node_modules/to-fast-properties": { - "version": "2.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/token-types": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/token-types/-/token-types-5.0.1.tgz", - "integrity": "sha512-Y2fmSnZjQdDb9W4w4r1tswlMHylzWIeOKpx0aZH9BgGtACHhrk3OkT52AzwcuqTRBZtvvnTjDBh8eynMulu8Vg==", - "dev": true, - "dependencies": { - "@tokenizer/token": "^0.3.0", - "ieee754": "^1.2.1" - }, - "engines": { - "node": ">=14.16" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Borewit" - } - }, - "node_modules/trim-newlines": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-4.1.1.tgz", - "integrity": "sha512-jRKj0n0jXWo6kh62nA5TEh3+4igKDXLvzBJcPpiizP7oOolUrYIxmVBG9TOtHYFHoddUk6YvAkGeGoSVTXfQXQ==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ts-api-utils": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.0.1.tgz", - "integrity": "sha512-lC/RGlPmwdrIBFTX59wwNzqh7aR2otPNPR/5brHZm/XKFYKsfqxihXUe9pU3JI+3vGkl+vyCoNNnPhJn3aLK1A==", - "dev": true, - "engines": { - "node": ">=16.13.0" - }, - "peerDependencies": { - "typescript": ">=4.2.0" - } - }, - "node_modules/ts-jest": { - "version": "29.1.1", - "dev": true, - "license": "MIT", - "dependencies": { - "bs-logger": "0.x", - "fast-json-stable-stringify": "2.x", - "jest-util": "^29.0.0", - "json5": "^2.2.3", - "lodash.memoize": "4.x", - "make-error": "1.x", - "semver": "^7.5.3", - "yargs-parser": "^21.0.1" - }, - "bin": { - "ts-jest": "cli.js" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "@babel/core": ">=7.0.0-beta.0 <8", - "@jest/types": "^29.0.0", - "babel-jest": "^29.0.0", - "jest": "^29.0.0", - "typescript": ">=4.3 <6" - }, - "peerDependenciesMeta": { - "@babel/core": { - "optional": true - }, - "@jest/types": { - "optional": true - }, - "babel-jest": { - "optional": true - }, - "esbuild": { - "optional": true - } - } - }, - "node_modules/tslib": { - "version": "1.14.1", - "dev": true, - "license": "0BSD" - }, - "node_modules/tsutils": { - "version": "3.21.0", - "dev": true, - "license": "MIT", - "dependencies": { - "tslib": "^1.8.1" - }, - "engines": { - "node": ">= 6" - }, - "peerDependencies": { - "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" - } - }, - "node_modules/type-check": { - "version": "0.4.0", - "dev": true, - "license": "MIT", - "dependencies": { - "prelude-ls": "^1.2.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/type-detect": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", - "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "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==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/typescript": { - "version": "5.3.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz", - "integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==", - "dev": true, - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, - "node_modules/ua-parser-js": { - "version": "1.0.37", - "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-1.0.37.tgz", - "integrity": "sha512-bhTyI94tZofjo+Dn8SN6Zv8nBDvyXTymAdM3LDI/0IboIUwTu1rEhW7v2TfiVsoYWgkQ4kOVqnI8APUFbIQIFQ==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/ua-parser-js" - }, - { - "type": "paypal", - "url": "https://paypal.me/faisalman" - }, - { - "type": "github", - "url": "https://github.com/sponsors/faisalman" - } - ], - "engines": { - "node": "*" - } - }, - "node_modules/unique-string": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-3.0.0.tgz", - "integrity": "sha512-VGXBUVwxKMBUznyffQweQABPRRW1vHZAbadFZud4pLFAqRGvv/96vafgjWFqzourzr8YonlQiPgH0YCJfawoGQ==", - "dev": true, - "dependencies": { - "crypto-random-string": "^4.0.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/untildify": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/untildify/-/untildify-4.0.0.tgz", - "integrity": "sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/update-browserslist-db": { - "version": "1.0.10", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - } - ], - "license": "MIT", - "dependencies": { - "escalade": "^3.1.1", - "picocolors": "^1.0.0" - }, - "bin": { - "browserslist-lint": "cli.js" - }, - "peerDependencies": { - "browserslist": ">= 4.21.0" - } - }, - "node_modules/uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, - "dependencies": { - "punycode": "^2.1.0" - } - }, - "node_modules/util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "dev": true - }, - "node_modules/uuid": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz", - "integrity": "sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==", - "bin": { - "uuid": "dist/bin/uuid" - } - }, - "node_modules/v8-to-istanbul": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.1.0.tgz", - "integrity": "sha512-6z3GW9x8G1gd+JIIgQQQxXuiJtCXeAjp6RaPEPLv62mH3iPHPxV6W3robxtCzNErRo6ZwTmzWhsbNvjyEBKzKA==", - "dev": true, - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.12", - "@types/istanbul-lib-coverage": "^2.0.1", - "convert-source-map": "^1.6.0" - }, - "engines": { - "node": ">=10.12.0" - } - }, - "node_modules/v8-to-istanbul/node_modules/convert-source-map": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", - "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", - "dev": true - }, - "node_modules/validate-npm-package-license": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", - "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", - "dev": true, - "dependencies": { - "spdx-correct": "^3.0.0", - "spdx-expression-parse": "^3.0.0" - } - }, - "node_modules/walker": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", - "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", - "dev": true, - "dependencies": { - "makeerror": "1.0.12" - } - }, - "node_modules/which": { - "version": "2.0.2", - "dev": true, - "license": "ISC", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrappy": { - "version": "1.0.2", - "dev": true, - "license": "ISC" - }, - "node_modules/write-file-atomic": { - "version": "4.0.2", - "dev": true, - "license": "ISC", - "dependencies": { - "imurmurhash": "^0.1.4", - "signal-exit": "^3.0.7" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/yallist": { - "version": "4.0.0", - "dev": true, - "license": "ISC" - }, - "node_modules/yargs": { - "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "dev": true, - "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/yargs-parser": { - "version": "21.1.1", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=12" - } - }, - "node_modules/yocto-queue": { - "version": "0.1.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - } - }, - "dependencies": { - "@aashutoshrathi/word-wrap": { - "version": "1.2.6", - "dev": true - }, - "@ampproject/remapping": { - "version": "2.2.0", - "dev": true, - "requires": { - "@jridgewell/gen-mapping": "^0.1.0", - "@jridgewell/trace-mapping": "^0.3.9" - } - }, - "@babel/code-frame": { - "version": "7.18.6", - "dev": true, - "requires": { - "@babel/highlight": "^7.18.6" - } - }, - "@babel/compat-data": { - "version": "7.20.1", - "dev": true - }, - "@babel/core": { - "version": "7.20.2", - "dev": true, - "requires": { - "@ampproject/remapping": "^2.1.0", - "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.20.2", - "@babel/helper-compilation-targets": "^7.20.0", - "@babel/helper-module-transforms": "^7.20.2", - "@babel/helpers": "^7.20.1", - "@babel/parser": "^7.20.2", - "@babel/template": "^7.18.10", - "@babel/traverse": "^7.20.1", - "@babel/types": "^7.20.2", - "convert-source-map": "^1.7.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.2.1", - "semver": "^6.3.0" - }, - "dependencies": { - "convert-source-map": { - "version": "1.9.0", - "dev": true - }, - "semver": { - "version": "6.3.0", - "dev": true - } - } - }, - "@babel/generator": { - "version": "7.20.4", - "dev": true, - "requires": { - "@babel/types": "^7.20.2", - "@jridgewell/gen-mapping": "^0.3.2", - "jsesc": "^2.5.1" - }, - "dependencies": { - "@jridgewell/gen-mapping": { - "version": "0.3.2", - "dev": true, - "requires": { - "@jridgewell/set-array": "^1.0.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.9" - } - } - } - }, - "@babel/helper-compilation-targets": { - "version": "7.20.0", - "dev": true, - "requires": { - "@babel/compat-data": "^7.20.0", - "@babel/helper-validator-option": "^7.18.6", - "browserslist": "^4.21.3", - "semver": "^6.3.0" - }, - "dependencies": { - "semver": { - "version": "6.3.0", - "dev": true - } - } - }, - "@babel/helper-environment-visitor": { - "version": "7.18.9", - "dev": true - }, - "@babel/helper-function-name": { - "version": "7.19.0", - "dev": true, - "requires": { - "@babel/template": "^7.18.10", - "@babel/types": "^7.19.0" - } - }, - "@babel/helper-hoist-variables": { - "version": "7.18.6", - "dev": true, - "requires": { - "@babel/types": "^7.18.6" - } - }, - "@babel/helper-module-imports": { - "version": "7.18.6", - "dev": true, - "requires": { - "@babel/types": "^7.18.6" - } - }, - "@babel/helper-module-transforms": { - "version": "7.20.2", - "dev": true, - "requires": { - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-module-imports": "^7.18.6", - "@babel/helper-simple-access": "^7.20.2", - "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/helper-validator-identifier": "^7.19.1", - "@babel/template": "^7.18.10", - "@babel/traverse": "^7.20.1", - "@babel/types": "^7.20.2" - } - }, - "@babel/helper-plugin-utils": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz", - "integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==", - "dev": true - }, - "@babel/helper-simple-access": { - "version": "7.20.2", - "dev": true, - "requires": { - "@babel/types": "^7.20.2" - } - }, - "@babel/helper-split-export-declaration": { - "version": "7.18.6", - "dev": true, - "requires": { - "@babel/types": "^7.18.6" - } - }, - "@babel/helper-string-parser": { - "version": "7.19.4", - "dev": true - }, - "@babel/helper-validator-identifier": { - "version": "7.19.1", - "dev": true - }, - "@babel/helper-validator-option": { - "version": "7.18.6", - "dev": true - }, - "@babel/helpers": { - "version": "7.20.1", - "dev": true, - "requires": { - "@babel/template": "^7.18.10", - "@babel/traverse": "^7.20.1", - "@babel/types": "^7.20.0" - } - }, - "@babel/highlight": { - "version": "7.18.6", - "dev": true, - "requires": { - "@babel/helper-validator-identifier": "^7.18.6", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.2", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "color-convert": { - "version": "1.9.3", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "dev": true - }, - "escape-string-regexp": { - "version": "1.0.5", - "dev": true - }, - "supports-color": { - "version": "5.5.0", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "@babel/parser": { - "version": "7.20.13", - "dev": true - }, - "@babel/plugin-syntax-async-generators": { - "version": "7.8.4", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-bigint": { - "version": "7.8.3", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-class-properties": { - "version": "7.12.13", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.12.13" - } - }, - "@babel/plugin-syntax-import-meta": { - "version": "7.10.4", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-syntax-json-strings": { - "version": "7.8.3", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-jsx": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.22.5.tgz", - "integrity": "sha512-gvyP4hZrgrs/wWMaocvxZ44Hw0b3W8Pe+cMxc8V1ULQ07oh8VNbIRaoD1LRZVTvD+0nieDKjfgKg89sD7rrKrg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.22.5" - } - }, - "@babel/plugin-syntax-logical-assignment-operators": { - "version": "7.10.4", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-syntax-nullish-coalescing-operator": { - "version": "7.8.3", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-numeric-separator": { - "version": "7.10.4", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-syntax-object-rest-spread": { - "version": "7.8.3", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-optional-catch-binding": { - "version": "7.8.3", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-optional-chaining": { - "version": "7.8.3", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-top-level-await": { - "version": "7.14.5", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.14.5" - } - }, - "@babel/plugin-syntax-typescript": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.22.5.tgz", - "integrity": "sha512-1mS2o03i7t1c6VzH6fdQ3OA8tcEIxwG18zIPRp+UY1Ihv6W+XZzBCVxExF9upussPXJ0xE9XRHwMoNs1ep/nRQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.22.5" - } - }, - "@babel/template": { - "version": "7.18.10", - "dev": true, - "requires": { - "@babel/code-frame": "^7.18.6", - "@babel/parser": "^7.18.10", - "@babel/types": "^7.18.10" - } - }, - "@babel/traverse": { - "version": "7.20.1", - "dev": true, - "requires": { - "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.20.1", - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-function-name": "^7.19.0", - "@babel/helper-hoist-variables": "^7.18.6", - "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/parser": "^7.20.1", - "@babel/types": "^7.20.0", - "debug": "^4.1.0", - "globals": "^11.1.0" - }, - "dependencies": { - "globals": { - "version": "11.12.0", - "dev": true - } - } - }, - "@babel/types": { - "version": "7.20.7", - "dev": true, - "requires": { - "@babel/helper-string-parser": "^7.19.4", - "@babel/helper-validator-identifier": "^7.19.1", - "to-fast-properties": "^2.0.0" - } - }, - "@bcoe/v8-coverage": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", - "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", - "dev": true - }, - "@eslint-community/eslint-utils": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", - "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", - "dev": true, - "requires": { - "eslint-visitor-keys": "^3.3.0" - } - }, - "@eslint-community/regexpp": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.6.2.tgz", - "integrity": "sha512-pPTNuaAG3QMH+buKyBIGJs3g/S5y0caxw0ygM3YyE6yJFySwiGGSzA+mM3KJ8QQvzeLh3blwgSonkFjgQdxzMw==", - "dev": true - }, - "@eslint/eslintrc": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", - "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", - "dev": true, - "requires": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^9.6.0", - "globals": "^13.19.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" - } - }, - "@eslint/js": { - "version": "8.56.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.56.0.tgz", - "integrity": "sha512-gMsVel9D7f2HLkBma9VbtzZRehRogVRfbr++f06nL2vnCGCNlzOD+/MUov/F4p8myyAHspEhVobgjpX64q5m6A==", - "dev": true - }, - "@humanwhocodes/config-array": { - "version": "0.11.13", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.13.tgz", - "integrity": "sha512-JSBDMiDKSzQVngfRjOdFXgFfklaXI4K9nLF49Auh21lmBWRLIK3+xTErTWD4KU54pb6coM6ESE7Awz/FNU3zgQ==", - "dev": true, - "requires": { - "@humanwhocodes/object-schema": "^2.0.1", - "debug": "^4.1.1", - "minimatch": "^3.0.5" - } - }, - "@humanwhocodes/module-importer": { - "version": "1.0.1", - "dev": true - }, - "@humanwhocodes/object-schema": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.1.tgz", - "integrity": "sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw==", - "dev": true - }, - "@istanbuljs/load-nyc-config": { - "version": "1.1.0", - "dev": true, - "requires": { - "camelcase": "^5.3.1", - "find-up": "^4.1.0", - "get-package-type": "^0.1.0", - "js-yaml": "^3.13.1", - "resolve-from": "^5.0.0" - }, - "dependencies": { - "argparse": { - "version": "1.0.10", - "dev": true, - "requires": { - "sprintf-js": "~1.0.2" - } - }, - "find-up": { - "version": "4.1.0", - "dev": true, - "requires": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - } - }, - "js-yaml": { - "version": "3.14.1", - "dev": true, - "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - } - }, - "locate-path": { - "version": "5.0.0", - "dev": true, - "requires": { - "p-locate": "^4.1.0" - } - }, - "p-limit": { - "version": "2.3.0", - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "4.1.0", - "dev": true, - "requires": { - "p-limit": "^2.2.0" - } - }, - "resolve-from": { - "version": "5.0.0", - "dev": true - } - } - }, - "@istanbuljs/schema": { - "version": "0.1.3", - "dev": true - }, - "@jest/console": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.7.0.tgz", - "integrity": "sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==", - "dev": true, - "requires": { - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0", - "slash": "^3.0.0" - } - }, - "@jest/core": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.7.0.tgz", - "integrity": "sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==", - "dev": true, - "requires": { - "@jest/console": "^29.7.0", - "@jest/reporters": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "exit": "^0.1.2", - "graceful-fs": "^4.2.9", - "jest-changed-files": "^29.7.0", - "jest-config": "^29.7.0", - "jest-haste-map": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-regex-util": "^29.6.3", - "jest-resolve": "^29.7.0", - "jest-resolve-dependencies": "^29.7.0", - "jest-runner": "^29.7.0", - "jest-runtime": "^29.7.0", - "jest-snapshot": "^29.7.0", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "jest-watcher": "^29.7.0", - "micromatch": "^4.0.4", - "pretty-format": "^29.7.0", - "slash": "^3.0.0", - "strip-ansi": "^6.0.0" - } - }, - "@jest/environment": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.7.0.tgz", - "integrity": "sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==", - "dev": true, - "requires": { - "@jest/fake-timers": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "jest-mock": "^29.7.0" - } - }, - "@jest/expect": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.7.0.tgz", - "integrity": "sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==", - "dev": true, - "requires": { - "expect": "^29.7.0", - "jest-snapshot": "^29.7.0" - } - }, - "@jest/expect-utils": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.7.0.tgz", - "integrity": "sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==", - "dev": true, - "requires": { - "jest-get-type": "^29.6.3" - } - }, - "@jest/fake-timers": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.7.0.tgz", - "integrity": "sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==", - "dev": true, - "requires": { - "@jest/types": "^29.6.3", - "@sinonjs/fake-timers": "^10.0.2", - "@types/node": "*", - "jest-message-util": "^29.7.0", - "jest-mock": "^29.7.0", - "jest-util": "^29.7.0" - } - }, - "@jest/globals": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.7.0.tgz", - "integrity": "sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==", - "dev": true, - "requires": { - "@jest/environment": "^29.7.0", - "@jest/expect": "^29.7.0", - "@jest/types": "^29.6.3", - "jest-mock": "^29.7.0" - } - }, - "@jest/reporters": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.7.0.tgz", - "integrity": "sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==", - "dev": true, - "requires": { - "@bcoe/v8-coverage": "^0.2.3", - "@jest/console": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@jridgewell/trace-mapping": "^0.3.18", - "@types/node": "*", - "chalk": "^4.0.0", - "collect-v8-coverage": "^1.0.0", - "exit": "^0.1.2", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "istanbul-lib-coverage": "^3.0.0", - "istanbul-lib-instrument": "^6.0.0", - "istanbul-lib-report": "^3.0.0", - "istanbul-lib-source-maps": "^4.0.0", - "istanbul-reports": "^3.1.3", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0", - "jest-worker": "^29.7.0", - "slash": "^3.0.0", - "string-length": "^4.0.1", - "strip-ansi": "^6.0.0", - "v8-to-istanbul": "^9.0.1" - }, - "dependencies": { - "istanbul-lib-instrument": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.0.tgz", - "integrity": "sha512-x58orMzEVfzPUKqlbLd1hXCnySCxKdDKa6Rjg97CwuLLRI4g3FHTdnExu1OqffVFay6zeMW+T6/DowFLndWnIw==", - "dev": true, - "requires": { - "@babel/core": "^7.12.3", - "@babel/parser": "^7.14.7", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-coverage": "^3.2.0", - "semver": "^7.5.4" - } - } - } - }, - "@jest/schemas": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", - "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", - "dev": true, - "requires": { - "@sinclair/typebox": "^0.27.8" - } - }, - "@jest/source-map": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.6.3.tgz", - "integrity": "sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==", - "dev": true, - "requires": { - "@jridgewell/trace-mapping": "^0.3.18", - "callsites": "^3.0.0", - "graceful-fs": "^4.2.9" - } - }, - "@jest/test-result": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.7.0.tgz", - "integrity": "sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==", - "dev": true, - "requires": { - "@jest/console": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "collect-v8-coverage": "^1.0.0" - } - }, - "@jest/test-sequencer": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz", - "integrity": "sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==", - "dev": true, - "requires": { - "@jest/test-result": "^29.7.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "slash": "^3.0.0" - } - }, - "@jest/transform": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.7.0.tgz", - "integrity": "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==", - "dev": true, - "requires": { - "@babel/core": "^7.11.6", - "@jest/types": "^29.6.3", - "@jridgewell/trace-mapping": "^0.3.18", - "babel-plugin-istanbul": "^6.1.1", - "chalk": "^4.0.0", - "convert-source-map": "^2.0.0", - "fast-json-stable-stringify": "^2.1.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "jest-regex-util": "^29.6.3", - "jest-util": "^29.7.0", - "micromatch": "^4.0.4", - "pirates": "^4.0.4", - "slash": "^3.0.0", - "write-file-atomic": "^4.0.2" - } - }, - "@jest/types": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", - "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", - "dev": true, - "requires": { - "@jest/schemas": "^29.6.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - } - }, - "@jridgewell/gen-mapping": { - "version": "0.1.1", - "dev": true, - "requires": { - "@jridgewell/set-array": "^1.0.0", - "@jridgewell/sourcemap-codec": "^1.4.10" - } - }, - "@jridgewell/resolve-uri": { - "version": "3.1.0", - "dev": true - }, - "@jridgewell/set-array": { - "version": "1.1.2", - "dev": true - }, - "@jridgewell/sourcemap-codec": { - "version": "1.4.14", - "dev": true - }, - "@jridgewell/trace-mapping": { - "version": "0.3.18", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.18.tgz", - "integrity": "sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA==", - "dev": true, - "requires": { - "@jridgewell/resolve-uri": "3.1.0", - "@jridgewell/sourcemap-codec": "1.4.14" - } - }, - "@nodelib/fs.scandir": { - "version": "2.1.5", - "dev": true, - "requires": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - } - }, - "@nodelib/fs.stat": { - "version": "2.0.5", - "dev": true - }, - "@nodelib/fs.walk": { - "version": "1.2.8", - "dev": true, - "requires": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - } - }, - "@sinclair/typebox": { - "version": "0.27.8", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", - "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", - "dev": true - }, - "@sinonjs/commons": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.0.tgz", - "integrity": "sha512-jXBtWAF4vmdNmZgD5FoKsVLv3rPgDnLgPbU84LIJ3otV44vJlDRokVng5v8NFJdCf/da9legHcKaRuZs4L7faA==", - "dev": true, - "requires": { - "type-detect": "4.0.8" - } - }, - "@sinonjs/fake-timers": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", - "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", - "dev": true, - "requires": { - "@sinonjs/commons": "^3.0.0" - } - }, - "@tokenizer/token": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/@tokenizer/token/-/token-0.3.0.tgz", - "integrity": "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==", - "dev": true - }, - "@types/babel__core": { - "version": "7.20.1", - "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.1.tgz", - "integrity": "sha512-aACu/U/omhdk15O4Nfb+fHgH/z3QsfQzpnvRZhYhThms83ZnAOZz7zZAWO7mn2yyNQaA4xTO8GLK3uqFU4bYYw==", - "dev": true, - "requires": { - "@babel/parser": "^7.20.7", - "@babel/types": "^7.20.7", - "@types/babel__generator": "*", - "@types/babel__template": "*", - "@types/babel__traverse": "*" - } - }, - "@types/babel__generator": { - "version": "7.6.4", - "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.4.tgz", - "integrity": "sha512-tFkciB9j2K755yrTALxD44McOrk+gfpIpvC3sxHjRawj6PfnQxrse4Clq5y/Rq+G3mrBurMax/lG8Qn2t9mSsg==", - "dev": true, - "requires": { - "@babel/types": "^7.0.0" - } - }, - "@types/babel__template": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.1.tgz", - "integrity": "sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g==", - "dev": true, - "requires": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0" - } - }, - "@types/babel__traverse": { - "version": "7.20.1", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.1.tgz", - "integrity": "sha512-MitHFXnhtgwsGZWtT68URpOvLN4EREih1u3QtQiN4VdAxWKRVvGCSvw/Qth0M0Qq3pJpnGOu5JaM/ydK7OGbqg==", - "dev": true, - "requires": { - "@babel/types": "^7.20.7" - } - }, - "@types/debug": { - "version": "4.1.12", - "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz", - "integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==", - "requires": { - "@types/ms": "*" - } - }, - "@types/events": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/events/-/events-3.0.3.tgz", - "integrity": "sha512-trOc4AAUThEz9hapPtSd7wf5tiQKvTtu5b371UxXdTuqzIh0ArcRspRP0i0Viu+LXstIQ1z96t1nsPxT9ol01g==", - "dev": true - }, - "@types/graceful-fs": { - "version": "4.1.6", - "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.6.tgz", - "integrity": "sha512-Sig0SNORX9fdW+bQuTEovKj3uHcUL6LQKbCrrqb1X7J6/ReAbhCXRAhc+SMejhLELFj2QcyuxmUooZ4bt5ReSw==", - "dev": true, - "requires": { - "@types/node": "*" - } - }, - "@types/istanbul-lib-coverage": { - "version": "2.0.4", - "dev": true - }, - "@types/istanbul-lib-report": { - "version": "3.0.0", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "*" - } - }, - "@types/istanbul-reports": { - "version": "3.0.1", - "dev": true, - "requires": { - "@types/istanbul-lib-report": "*" - } - }, - "@types/jest": { - "version": "29.5.11", - "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.11.tgz", - "integrity": "sha512-S2mHmYIVe13vrm6q4kN6fLYYAka15ALQki/vgDC3mIukEOx8WJlv0kQPM+d4w8Gp6u0uSdKND04IlTXBv0rwnQ==", - "dev": true, - "requires": { - "expect": "^29.0.0", - "pretty-format": "^29.0.0" - } - }, - "@types/json-schema": { - "version": "7.0.12", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.12.tgz", - "integrity": "sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA==", - "dev": true - }, - "@types/minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-hov8bUuiLiyFPGyFPE1lwWhmzYbirOXQNNo40+y3zow8aFVTeyn3VWL0VFFfdNddA8S4Vf0Tc062rzyNr7Paag==", - "dev": true - }, - "@types/ms": { - "version": "0.7.31" - }, - "@types/node": { - "version": "18.11.9", - "dev": true - }, - "@types/normalize-package-data": { - "version": "2.4.4", - "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.4.tgz", - "integrity": "sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==", - "dev": true - }, - "@types/sdp-transform": { - "version": "2.4.9", - "resolved": "https://registry.npmjs.org/@types/sdp-transform/-/sdp-transform-2.4.9.tgz", - "integrity": "sha512-bVr+/OoZZy7wrHlNcEAAa6PAgKA4BoXPYVN2EijMC5WnGgQ4ZEuixmKnVs2roiAvr7RhIFVH17QD27cojgIZCg==", - "dev": true - }, - "@types/semver": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.0.tgz", - "integrity": "sha512-G8hZ6XJiHnuhQKR7ZmysCeJWE08o8T0AXtk5darsCaTVsYZhhgUrq53jizaR2FvsoeCwJhlmwTjkXBY5Pn/ZHw==", - "dev": true - }, - "@types/stack-utils": { - "version": "2.0.1", - "dev": true - }, - "@types/ua-parser-js": { - "version": "0.7.39", - "resolved": "https://registry.npmjs.org/@types/ua-parser-js/-/ua-parser-js-0.7.39.tgz", - "integrity": "sha512-P/oDfpofrdtF5xw433SPALpdSchtJmY7nsJItf8h3KXqOslkbySh8zq4dSWXH2oTjRvJ5PczVEoCZPow6GicLg==", - "dev": true - }, - "@types/yargs": { - "version": "17.0.13", - "dev": true, - "requires": { - "@types/yargs-parser": "*" - } - }, - "@types/yargs-parser": { - "version": "21.0.0", - "dev": true - }, - "@typescript-eslint/eslint-plugin": { - "version": "6.15.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.15.0.tgz", - "integrity": "sha512-j5qoikQqPccq9QoBAupOP+CBu8BaJ8BLjaXSioDISeTZkVO3ig7oSIKh3H+rEpee7xCXtWwSB4KIL5l6hWZzpg==", - "dev": true, - "requires": { - "@eslint-community/regexpp": "^4.5.1", - "@typescript-eslint/scope-manager": "6.15.0", - "@typescript-eslint/type-utils": "6.15.0", - "@typescript-eslint/utils": "6.15.0", - "@typescript-eslint/visitor-keys": "6.15.0", - "debug": "^4.3.4", - "graphemer": "^1.4.0", - "ignore": "^5.2.4", - "natural-compare": "^1.4.0", - "semver": "^7.5.4", - "ts-api-utils": "^1.0.1" - }, - "dependencies": { - "@typescript-eslint/scope-manager": { - "version": "6.15.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.15.0.tgz", - "integrity": "sha512-+BdvxYBltqrmgCNu4Li+fGDIkW9n//NrruzG9X1vBzaNK+ExVXPoGB71kneaVw/Jp+4rH/vaMAGC6JfMbHstVg==", - "dev": true, - "requires": { - "@typescript-eslint/types": "6.15.0", - "@typescript-eslint/visitor-keys": "6.15.0" - } - }, - "@typescript-eslint/types": { - "version": "6.15.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.15.0.tgz", - "integrity": "sha512-yXjbt//E4T/ee8Ia1b5mGlbNj9fB9lJP4jqLbZualwpP2BCQ5is6BcWwxpIsY4XKAhmdv3hrW92GdtJbatC6dQ==", - "dev": true - }, - "@typescript-eslint/typescript-estree": { - "version": "6.15.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.15.0.tgz", - "integrity": "sha512-7mVZJN7Hd15OmGuWrp2T9UvqR2Ecg+1j/Bp1jXUEY2GZKV6FXlOIoqVDmLpBiEiq3katvj/2n2mR0SDwtloCew==", - "dev": true, - "requires": { - "@typescript-eslint/types": "6.15.0", - "@typescript-eslint/visitor-keys": "6.15.0", - "debug": "^4.3.4", - "globby": "^11.1.0", - "is-glob": "^4.0.3", - "semver": "^7.5.4", - "ts-api-utils": "^1.0.1" - } - }, - "@typescript-eslint/utils": { - "version": "6.15.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.15.0.tgz", - "integrity": "sha512-eF82p0Wrrlt8fQSRL0bGXzK5nWPRV2dYQZdajcfzOD9+cQz9O7ugifrJxclB+xVOvWvagXfqS4Es7vpLP4augw==", - "dev": true, - "requires": { - "@eslint-community/eslint-utils": "^4.4.0", - "@types/json-schema": "^7.0.12", - "@types/semver": "^7.5.0", - "@typescript-eslint/scope-manager": "6.15.0", - "@typescript-eslint/types": "6.15.0", - "@typescript-eslint/typescript-estree": "6.15.0", - "semver": "^7.5.4" - } - }, - "@typescript-eslint/visitor-keys": { - "version": "6.15.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.15.0.tgz", - "integrity": "sha512-1zvtdC1a9h5Tb5jU9x3ADNXO9yjP8rXlaoChu0DQX40vf5ACVpYIVIZhIMZ6d5sDXH7vq4dsZBT1fEGj8D2n2w==", - "dev": true, - "requires": { - "@typescript-eslint/types": "6.15.0", - "eslint-visitor-keys": "^3.4.1" - } - } - } - }, - "@typescript-eslint/parser": { - "version": "6.15.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.15.0.tgz", - "integrity": "sha512-MkgKNnsjC6QwcMdlNAel24jjkEO/0hQaMDLqP4S9zq5HBAUJNQB6y+3DwLjX7b3l2b37eNAxMPLwb3/kh8VKdA==", - "dev": true, - "requires": { - "@typescript-eslint/scope-manager": "6.15.0", - "@typescript-eslint/types": "6.15.0", - "@typescript-eslint/typescript-estree": "6.15.0", - "@typescript-eslint/visitor-keys": "6.15.0", - "debug": "^4.3.4" - }, - "dependencies": { - "@typescript-eslint/scope-manager": { - "version": "6.15.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.15.0.tgz", - "integrity": "sha512-+BdvxYBltqrmgCNu4Li+fGDIkW9n//NrruzG9X1vBzaNK+ExVXPoGB71kneaVw/Jp+4rH/vaMAGC6JfMbHstVg==", - "dev": true, - "requires": { - "@typescript-eslint/types": "6.15.0", - "@typescript-eslint/visitor-keys": "6.15.0" - } - }, - "@typescript-eslint/types": { - "version": "6.15.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.15.0.tgz", - "integrity": "sha512-yXjbt//E4T/ee8Ia1b5mGlbNj9fB9lJP4jqLbZualwpP2BCQ5is6BcWwxpIsY4XKAhmdv3hrW92GdtJbatC6dQ==", - "dev": true - }, - "@typescript-eslint/typescript-estree": { - "version": "6.15.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.15.0.tgz", - "integrity": "sha512-7mVZJN7Hd15OmGuWrp2T9UvqR2Ecg+1j/Bp1jXUEY2GZKV6FXlOIoqVDmLpBiEiq3katvj/2n2mR0SDwtloCew==", - "dev": true, - "requires": { - "@typescript-eslint/types": "6.15.0", - "@typescript-eslint/visitor-keys": "6.15.0", - "debug": "^4.3.4", - "globby": "^11.1.0", - "is-glob": "^4.0.3", - "semver": "^7.5.4", - "ts-api-utils": "^1.0.1" - } - }, - "@typescript-eslint/visitor-keys": { - "version": "6.15.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.15.0.tgz", - "integrity": "sha512-1zvtdC1a9h5Tb5jU9x3ADNXO9yjP8rXlaoChu0DQX40vf5ACVpYIVIZhIMZ6d5sDXH7vq4dsZBT1fEGj8D2n2w==", - "dev": true, - "requires": { - "@typescript-eslint/types": "6.15.0", - "eslint-visitor-keys": "^3.4.1" - } - } - } - }, - "@typescript-eslint/scope-manager": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.62.0.tgz", - "integrity": "sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w==", - "dev": true, - "requires": { - "@typescript-eslint/types": "5.62.0", - "@typescript-eslint/visitor-keys": "5.62.0" - } - }, - "@typescript-eslint/type-utils": { - "version": "6.15.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.15.0.tgz", - "integrity": "sha512-CnmHKTfX6450Bo49hPg2OkIm/D/TVYV7jO1MCfPYGwf6x3GO0VU8YMO5AYMn+u3X05lRRxA4fWCz87GFQV6yVQ==", - "dev": true, - "requires": { - "@typescript-eslint/typescript-estree": "6.15.0", - "@typescript-eslint/utils": "6.15.0", - "debug": "^4.3.4", - "ts-api-utils": "^1.0.1" - }, - "dependencies": { - "@typescript-eslint/scope-manager": { - "version": "6.15.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.15.0.tgz", - "integrity": "sha512-+BdvxYBltqrmgCNu4Li+fGDIkW9n//NrruzG9X1vBzaNK+ExVXPoGB71kneaVw/Jp+4rH/vaMAGC6JfMbHstVg==", - "dev": true, - "requires": { - "@typescript-eslint/types": "6.15.0", - "@typescript-eslint/visitor-keys": "6.15.0" - } - }, - "@typescript-eslint/types": { - "version": "6.15.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.15.0.tgz", - "integrity": "sha512-yXjbt//E4T/ee8Ia1b5mGlbNj9fB9lJP4jqLbZualwpP2BCQ5is6BcWwxpIsY4XKAhmdv3hrW92GdtJbatC6dQ==", - "dev": true - }, - "@typescript-eslint/typescript-estree": { - "version": "6.15.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.15.0.tgz", - "integrity": "sha512-7mVZJN7Hd15OmGuWrp2T9UvqR2Ecg+1j/Bp1jXUEY2GZKV6FXlOIoqVDmLpBiEiq3katvj/2n2mR0SDwtloCew==", - "dev": true, - "requires": { - "@typescript-eslint/types": "6.15.0", - "@typescript-eslint/visitor-keys": "6.15.0", - "debug": "^4.3.4", - "globby": "^11.1.0", - "is-glob": "^4.0.3", - "semver": "^7.5.4", - "ts-api-utils": "^1.0.1" - } - }, - "@typescript-eslint/utils": { - "version": "6.15.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.15.0.tgz", - "integrity": "sha512-eF82p0Wrrlt8fQSRL0bGXzK5nWPRV2dYQZdajcfzOD9+cQz9O7ugifrJxclB+xVOvWvagXfqS4Es7vpLP4augw==", - "dev": true, - "requires": { - "@eslint-community/eslint-utils": "^4.4.0", - "@types/json-schema": "^7.0.12", - "@types/semver": "^7.5.0", - "@typescript-eslint/scope-manager": "6.15.0", - "@typescript-eslint/types": "6.15.0", - "@typescript-eslint/typescript-estree": "6.15.0", - "semver": "^7.5.4" - } - }, - "@typescript-eslint/visitor-keys": { - "version": "6.15.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.15.0.tgz", - "integrity": "sha512-1zvtdC1a9h5Tb5jU9x3ADNXO9yjP8rXlaoChu0DQX40vf5ACVpYIVIZhIMZ6d5sDXH7vq4dsZBT1fEGj8D2n2w==", - "dev": true, - "requires": { - "@typescript-eslint/types": "6.15.0", - "eslint-visitor-keys": "^3.4.1" - } - } - } - }, - "@typescript-eslint/types": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.62.0.tgz", - "integrity": "sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ==", - "dev": true - }, - "@typescript-eslint/typescript-estree": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.62.0.tgz", - "integrity": "sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA==", - "dev": true, - "requires": { - "@typescript-eslint/types": "5.62.0", - "@typescript-eslint/visitor-keys": "5.62.0", - "debug": "^4.3.4", - "globby": "^11.1.0", - "is-glob": "^4.0.3", - "semver": "^7.3.7", - "tsutils": "^3.21.0" - } - }, - "@typescript-eslint/utils": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.62.0.tgz", - "integrity": "sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ==", - "dev": true, - "requires": { - "@eslint-community/eslint-utils": "^4.2.0", - "@types/json-schema": "^7.0.9", - "@types/semver": "^7.3.12", - "@typescript-eslint/scope-manager": "5.62.0", - "@typescript-eslint/types": "5.62.0", - "@typescript-eslint/typescript-estree": "5.62.0", - "eslint-scope": "^5.1.1", - "semver": "^7.3.7" - } - }, - "@typescript-eslint/visitor-keys": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.62.0.tgz", - "integrity": "sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw==", - "dev": true, - "requires": { - "@typescript-eslint/types": "5.62.0", - "eslint-visitor-keys": "^3.3.0" - } - }, - "@ungap/structured-clone": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", - "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", - "dev": true - }, - "acorn": { - "version": "8.11.2", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.2.tgz", - "integrity": "sha512-nc0Axzp/0FILLEVsm4fNwLCwMttvhEI263QtVPQcbpfZZ3ts0hLsZGOpE6czNlid7CJ9MlyH8reXkpsf3YUY4w==", - "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-escapes": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", - "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", - "dev": true, - "requires": { - "type-fest": "^0.21.3" - }, - "dependencies": { - "type-fest": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", - "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", - "dev": true - } - } - }, - "ansi-regex": { - "version": "5.0.1", - "dev": true - }, - "ansi-styles": { - "version": "4.3.0", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "anymatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "dev": true, - "requires": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - } - }, - "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 - }, - "arrify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", - "integrity": "sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==", - "dev": true - }, - "awaitqueue": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/awaitqueue/-/awaitqueue-3.0.2.tgz", - "integrity": "sha512-AVAtRwmf0DNSesMdyanFKKejTrOnjdKtz5LIDQFu2OTUgXvB/CRTYMrkPAF/2GCF9XBtYVxSwxDORlD41S+RyQ==", - "requires": { - "debug": "^4.3.4" - } - }, - "babel-jest": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz", - "integrity": "sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==", - "dev": true, - "requires": { - "@jest/transform": "^29.7.0", - "@types/babel__core": "^7.1.14", - "babel-plugin-istanbul": "^6.1.1", - "babel-preset-jest": "^29.6.3", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "slash": "^3.0.0" - } - }, - "babel-plugin-istanbul": { - "version": "6.1.1", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0", - "@istanbuljs/load-nyc-config": "^1.0.0", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-instrument": "^5.0.4", - "test-exclude": "^6.0.0" - } - }, - "babel-plugin-jest-hoist": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz", - "integrity": "sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==", - "dev": true, - "requires": { - "@babel/template": "^7.3.3", - "@babel/types": "^7.3.3", - "@types/babel__core": "^7.1.14", - "@types/babel__traverse": "^7.0.6" - } - }, - "babel-preset-current-node-syntax": { - "version": "1.0.1", - "dev": true, - "requires": { - "@babel/plugin-syntax-async-generators": "^7.8.4", - "@babel/plugin-syntax-bigint": "^7.8.3", - "@babel/plugin-syntax-class-properties": "^7.8.3", - "@babel/plugin-syntax-import-meta": "^7.8.3", - "@babel/plugin-syntax-json-strings": "^7.8.3", - "@babel/plugin-syntax-logical-assignment-operators": "^7.8.3", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", - "@babel/plugin-syntax-numeric-separator": "^7.8.3", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", - "@babel/plugin-syntax-optional-chaining": "^7.8.3", - "@babel/plugin-syntax-top-level-await": "^7.8.3" - } - }, - "babel-preset-jest": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz", - "integrity": "sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==", - "dev": true, - "requires": { - "babel-plugin-jest-hoist": "^29.6.3", - "babel-preset-current-node-syntax": "^1.0.0" - } - }, - "balanced-match": { - "version": "1.0.2", - "dev": true - }, - "big-integer": { - "version": "1.6.52", - "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.52.tgz", - "integrity": "sha512-QxD8cf2eVqJOOz63z6JIN9BzvVs/dlySa5HGSBH5xtR8dPteIRQnBxxKqkNTiT6jbDTF6jAfrd4oMcND9RGbQg==", - "dev": true - }, - "bplist-parser": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/bplist-parser/-/bplist-parser-0.2.0.tgz", - "integrity": "sha512-z0M+byMThzQmD9NILRniCUXYsYpjwnlO8N5uCFaCqIOpqRsJCrQL9NK3JsD67CN5a08nF5oIL2bD6loTdHOuKw==", - "dev": true, - "requires": { - "big-integer": "^1.6.44" - } - }, - "brace-expansion": { - "version": "1.1.11", - "dev": true, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "braces": { - "version": "3.0.2", - "dev": true, - "requires": { - "fill-range": "^7.0.1" - } - }, - "browserslist": { - "version": "4.21.4", - "dev": true, - "requires": { - "caniuse-lite": "^1.0.30001400", - "electron-to-chromium": "^1.4.251", - "node-releases": "^2.0.6", - "update-browserslist-db": "^1.0.9" - } - }, - "bs-logger": { - "version": "0.2.6", - "dev": true, - "requires": { - "fast-json-stable-stringify": "2.x" - } - }, - "bser": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", - "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", - "dev": true, - "requires": { - "node-int64": "^0.4.0" - } - }, - "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 - }, - "bundle-name": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-3.0.0.tgz", - "integrity": "sha512-PKA4BeSvBpQKQ8iPOGCSiell+N8P+Tf1DlwqmYhpe2gAhKPHn8EYOxVT+ShuGmhg8lN8XiSlS80yiExKXrURlw==", - "dev": true, - "requires": { - "run-applescript": "^5.0.0" - } - }, - "callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true - }, - "camelcase": { - "version": "5.3.1", - "dev": true - }, - "camelcase-keys": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-8.0.2.tgz", - "integrity": "sha512-qMKdlOfsjlezMqxkUGGMaWWs17i2HoL15tM+wtx8ld4nLrUwU58TFdvyGOz/piNP842KeO8yXvggVQSdQ828NA==", - "dev": true, - "requires": { - "camelcase": "^7.0.0", - "map-obj": "^4.3.0", - "quick-lru": "^6.1.1", - "type-fest": "^2.13.0" - }, - "dependencies": { - "camelcase": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-7.0.1.tgz", - "integrity": "sha512-xlx1yCK2Oc1APsPXDL2LdlNP6+uu8OCDdhOBSVT279M/S+y75O30C2VuD8T2ogdePBBl7PfPF4504tnLgX3zfw==", - "dev": true - }, - "type-fest": { - "version": "2.19.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz", - "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==", - "dev": true - } - } - }, - "caniuse-lite": { - "version": "1.0.30001431", - "dev": true - }, - "chalk": { - "version": "4.1.2", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "dependencies": { - "has-flag": { - "version": "4.0.0", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "char-regex": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", - "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", - "dev": true - }, - "ci-info": { - "version": "3.6.1", - "dev": true - }, - "cjs-module-lexer": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.3.tgz", - "integrity": "sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ==", - "dev": true - }, - "cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "dev": true, - "requires": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - } - }, - "co": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", - "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", - "dev": true - }, - "collect-v8-coverage": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz", - "integrity": "sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==", - "dev": true - }, - "color-convert": { - "version": "2.0.1", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "dev": true - }, - "concat-map": { - "version": "0.0.1", - "dev": true - }, - "convert-source-map": { - "version": "2.0.0", - "dev": true - }, - "create-jest": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/create-jest/-/create-jest-29.7.0.tgz", - "integrity": "sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==", - "dev": true, - "requires": { - "@jest/types": "^29.6.3", - "chalk": "^4.0.0", - "exit": "^0.1.2", - "graceful-fs": "^4.2.9", - "jest-config": "^29.7.0", - "jest-util": "^29.7.0", - "prompts": "^2.0.1" - } - }, - "cross-spawn": { - "version": "7.0.3", - "dev": true, - "requires": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - } - }, - "crypto-random-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-4.0.0.tgz", - "integrity": "sha512-x8dy3RnvYdlUcPOjkEHqozhiwzKNSq7GcPuXFbnyMOCHxX8V3OgIg/pYuabl2sbUPfIJaeAQB7PMOK8DFIdoRA==", - "dev": true, - "requires": { - "type-fest": "^1.0.1" - }, - "dependencies": { - "type-fest": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz", - "integrity": "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==", - "dev": true - } - } - }, - "debug": { - "version": "4.3.4", - "requires": { - "ms": "2.1.2" - } - }, - "decamelize": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-6.0.0.tgz", - "integrity": "sha512-Fv96DCsdOgB6mdGl67MT5JaTNKRzrzill5OH5s8bjYJXVlcXyPYGyPsUkWyGV5p1TXI5esYIYMMeDJL0hEIwaA==", - "dev": true - }, - "decamelize-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/decamelize-keys/-/decamelize-keys-1.1.1.tgz", - "integrity": "sha512-WiPxgEirIV0/eIOMcnFBA3/IJZAZqKnwAwWyvvdi4lsr1WCN22nhdf/3db3DoZcUjTV2SqfzIwNyp6y2xs3nmg==", - "dev": true, - "requires": { - "decamelize": "^1.1.0", - "map-obj": "^1.0.0" - }, - "dependencies": { - "decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", - "dev": true - }, - "map-obj": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", - "integrity": "sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg==", - "dev": true - } - } - }, - "dedent": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.5.1.tgz", - "integrity": "sha512-+LxW+KLWxu3HW3M2w2ympwtqPrqYRzU8fqi6Fhd18fBALe15blJPI/I4+UHveMVG6lJqB4JNd4UG0S5cnVHwIg==", - "dev": true, - "requires": {} - }, - "deep-is": { - "version": "0.1.4", - "dev": true - }, - "deepmerge": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", - "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", - "dev": true - }, - "default-browser": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-4.0.0.tgz", - "integrity": "sha512-wX5pXO1+BrhMkSbROFsyxUm0i/cJEScyNhA4PPxc41ICuv05ZZB/MX28s8aZx6xjmatvebIapF6hLEKEcpneUA==", - "dev": true, - "requires": { - "bundle-name": "^3.0.0", - "default-browser-id": "^3.0.0", - "execa": "^7.1.1", - "titleize": "^3.0.0" - }, - "dependencies": { - "execa": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-7.2.0.tgz", - "integrity": "sha512-UduyVP7TLB5IcAQl+OzLyLcS/l32W/GLg+AhHJ+ow40FOk2U3SAllPwR44v4vmdFwIWqpdwxxpQbF1n5ta9seA==", - "dev": true, - "requires": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.1", - "human-signals": "^4.3.0", - "is-stream": "^3.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^5.1.0", - "onetime": "^6.0.0", - "signal-exit": "^3.0.7", - "strip-final-newline": "^3.0.0" - } - }, - "human-signals": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-4.3.1.tgz", - "integrity": "sha512-nZXjEF2nbo7lIw3mgYjItAfgQXog3OjJogSbKa2CQIIvSGWcKgeJnQlNXip6NglNzYH45nSRiEVimMvYL8DDqQ==", - "dev": true - }, - "is-stream": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", - "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", - "dev": true - }, - "mimic-fn": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", - "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", - "dev": true - }, - "npm-run-path": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.2.0.tgz", - "integrity": "sha512-W4/tgAXFqFA0iL7fk0+uQ3g7wkL8xJmx3XdK0VGb4cHW//eZTtKGvFBBoRKVTpY7n6ze4NL9ly7rgXcHufqXKg==", - "dev": true, - "requires": { - "path-key": "^4.0.0" - } - }, - "onetime": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", - "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", - "dev": true, - "requires": { - "mimic-fn": "^4.0.0" - } - }, - "path-key": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", - "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", - "dev": true - }, - "strip-final-newline": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", - "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", - "dev": true - } - } - }, - "default-browser-id": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-3.0.0.tgz", - "integrity": "sha512-OZ1y3y0SqSICtE8DE4S8YOE9UZOJ8wO16fKWVP5J1Qz42kV9jcnMVFrEE/noXb/ss3Q4pZIH79kxofzyNNtUNA==", - "dev": true, - "requires": { - "bplist-parser": "^0.2.0", - "untildify": "^4.0.0" - } - }, - "define-lazy-prop": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz", - "integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==", - "dev": true - }, - "detect-newline": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", - "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", - "dev": true - }, - "diff-sequences": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", - "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", - "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", - "dev": true, - "requires": { - "esutils": "^2.0.2" - } - }, - "electron-to-chromium": { - "version": "1.4.284", - "dev": true - }, - "emittery": { - "version": "0.13.1", - "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz", - "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==", - "dev": true - }, - "emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "error-ex": { - "version": "1.3.2", - "dev": true, - "requires": { - "is-arrayish": "^0.2.1" - } - }, - "escalade": { - "version": "3.1.1", - "dev": true - }, - "escape-string-regexp": { - "version": "4.0.0", - "dev": true - }, - "eslint": { - "version": "8.56.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.56.0.tgz", - "integrity": "sha512-Go19xM6T9puCOWntie1/P997aXxFsOi37JIHRWI514Hc6ZnaHGKY9xFhrU65RT6CcBEzZoGG1e6Nq+DT04ZtZQ==", - "dev": true, - "requires": { - "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.6.1", - "@eslint/eslintrc": "^2.1.4", - "@eslint/js": "8.56.0", - "@humanwhocodes/config-array": "^0.11.13", - "@humanwhocodes/module-importer": "^1.0.1", - "@nodelib/fs.walk": "^1.2.8", - "@ungap/structured-clone": "^1.2.0", - "ajv": "^6.12.4", - "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.2.2", - "eslint-visitor-keys": "^3.4.3", - "espree": "^9.6.1", - "esquery": "^1.4.2", - "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.2", - "globals": "^13.19.0", - "graphemer": "^1.4.0", - "ignore": "^5.2.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "is-path-inside": "^3.0.3", - "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.3", - "strip-ansi": "^6.0.1", - "text-table": "^0.2.0" - }, - "dependencies": { - "eslint-scope": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", - "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", - "dev": true, - "requires": { - "esrecurse": "^4.3.0", - "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 - } - } - }, - "eslint-plugin-jest": { - "version": "27.6.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-27.6.0.tgz", - "integrity": "sha512-MTlusnnDMChbElsszJvrwD1dN3x6nZl//s4JD23BxB6MgR66TZlL064su24xEIS3VACfAoHV1vgyMgPw8nkdng==", - "dev": true, - "requires": { - "@typescript-eslint/utils": "^5.10.0" - } - }, - "eslint-scope": { - "version": "5.1.1", - "dev": true, - "requires": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" - } - }, - "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 - }, - "espree": { - "version": "9.6.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", - "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", - "dev": true, - "requires": { - "acorn": "^8.9.0", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.4.1" - } - }, - "esprima": { - "version": "4.0.1", - "dev": true - }, - "esquery": { - "version": "1.5.0", - "dev": true, - "requires": { - "estraverse": "^5.1.0" - }, - "dependencies": { - "estraverse": { - "version": "5.3.0", - "dev": true - } - } - }, - "esrecurse": { - "version": "4.3.0", - "dev": true, - "requires": { - "estraverse": "^5.2.0" - }, - "dependencies": { - "estraverse": { - "version": "5.3.0", - "dev": true - } - } - }, - "estraverse": { - "version": "4.3.0", - "dev": true - }, - "esutils": { - "version": "2.0.3", - "dev": true - }, - "event-target-shim": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-6.0.2.tgz", - "integrity": "sha512-8q3LsZjRezbFZ2PN+uP+Q7pnHUMmAOziU2vA2OwoFaKIXxlxl38IylhSSgUorWu/rf4er67w0ikBqjBFk/pomA==" - }, - "events": { - "version": "3.3.0" - }, - "execa": { - "version": "5.1.1", - "dev": true, - "requires": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.0", - "human-signals": "^2.1.0", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.1", - "onetime": "^5.1.2", - "signal-exit": "^3.0.3", - "strip-final-newline": "^2.0.0" - } - }, - "exit": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", - "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", - "dev": true - }, - "expect": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/expect/-/expect-29.7.0.tgz", - "integrity": "sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==", - "dev": true, - "requires": { - "@jest/expect-utils": "^29.7.0", - "jest-get-type": "^29.6.3", - "jest-matcher-utils": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0" - } - }, - "fake-mediastreamtrack": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/fake-mediastreamtrack/-/fake-mediastreamtrack-1.2.0.tgz", - "integrity": "sha512-AxHtlEmka1sqNoe3Ej1H1hJc9gjjO/6vCbCPm4D4QeEXvzhjYumA+iZ7wOi2WrmkAhGElHhBgWoNgJhFccectA==", - "requires": { - "event-target-shim": "^6.0.2", - "uuid": "^9.0.0" - } - }, - "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.3.0", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.0.tgz", - "integrity": "sha512-ChDuvbOypPuNjO8yIDf36x7BlZX1smcUMTTcyoIjycexOxd6DFsKsg21qVBzEmr3G7fUKIRy2/psii+CIUt7FA==", - "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", - "dev": true - }, - "fast-levenshtein": { - "version": "2.0.6", - "dev": true - }, - "fastq": { - "version": "1.13.0", - "dev": true, - "requires": { - "reusify": "^1.0.4" - } - }, - "fb-watchman": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", - "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", - "dev": true, - "requires": { - "bser": "2.1.1" - } - }, - "file-entry-cache": { - "version": "6.0.1", - "dev": true, - "requires": { - "flat-cache": "^3.0.4" - } - }, - "file-type": { - "version": "18.7.0", - "resolved": "https://registry.npmjs.org/file-type/-/file-type-18.7.0.tgz", - "integrity": "sha512-ihHtXRzXEziMrQ56VSgU7wkxh55iNchFkosu7Y9/S+tXHdKyrGjVK0ujbqNnsxzea+78MaLhN6PGmfYSAv1ACw==", - "dev": true, - "requires": { - "readable-web-to-node-stream": "^3.0.2", - "strtok3": "^7.0.0", - "token-types": "^5.0.1" - } - }, - "fill-range": { - "version": "7.0.1", - "dev": true, - "requires": { - "to-regex-range": "^5.0.1" - } - }, - "find-up": { - "version": "5.0.0", - "dev": true, - "requires": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - } - }, - "flat-cache": { - "version": "3.0.4", - "dev": true, - "requires": { - "flatted": "^3.1.0", - "rimraf": "^3.0.2" - } - }, - "flatted": { - "version": "3.2.7", - "dev": true - }, - "fs.realpath": { - "version": "1.0.0", - "dev": true - }, - "fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, - "optional": true - }, - "function-bind": { - "version": "1.1.1", - "dev": true - }, - "gensync": { - "version": "1.0.0-beta.2", - "dev": true - }, - "get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true - }, - "get-package-type": { - "version": "0.1.0", - "dev": true - }, - "get-stdin": { - "version": "9.0.0", - "dev": true - }, - "get-stream": { - "version": "6.0.1", - "dev": true - }, - "glob": { - "version": "7.2.3", - "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", - "dev": true, - "requires": { - "is-glob": "^4.0.3" - } - }, - "globals": { - "version": "13.23.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.23.0.tgz", - "integrity": "sha512-XAmF0RjlrjY23MA51q3HltdlGxUpXPvg0GioKiD9X6HD28iMjo2dKC8Vqwm7lne4GNr78+RHTfliktR6ZH09wA==", - "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" - } - }, - "graceful-fs": { - "version": "4.2.10", - "dev": true - }, - "graphemer": { - "version": "1.4.0", - "dev": true - }, - "h264-profile-level-id": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/h264-profile-level-id/-/h264-profile-level-id-2.0.0.tgz", - "integrity": "sha512-X4CLryVbVA0CtjTExS4G5U1gb2Z4wa32AF8ukVmFuLdw2JRq2aHisor7SY5SYTUUrUSqq0KdPIO18sql6IWIQw==", - "requires": { - "@types/debug": "^4.1.12", - "debug": "^4.3.4" - } - }, - "hard-rejection": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/hard-rejection/-/hard-rejection-2.1.0.tgz", - "integrity": "sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA==", - "dev": true - }, - "has": { - "version": "1.0.3", - "dev": true, - "requires": { - "function-bind": "^1.1.1" - } - }, - "has-flag": { - "version": "3.0.0", - "dev": true - }, - "hosted-git-info": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-5.2.1.tgz", - "integrity": "sha512-xIcQYMnhcx2Nr4JTjsFmwwnr9vldugPy9uVm0o87bjqqWMv9GaqsTeT+i99wTl0mk1uLxJtHxLb8kymqTENQsw==", - "dev": true, - "requires": { - "lru-cache": "^7.5.1" - }, - "dependencies": { - "lru-cache": { - "version": "7.18.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", - "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", - "dev": true - } - } - }, - "html-escaper": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", - "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", - "dev": true - }, - "human-signals": { - "version": "2.1.0", - "dev": true - }, - "ieee754": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "dev": true - }, - "ignore": { - "version": "5.2.4", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", - "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", - "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" - } - }, - "import-local": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz", - "integrity": "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==", - "dev": true, - "requires": { - "pkg-dir": "^4.2.0", - "resolve-cwd": "^3.0.0" - } - }, - "imurmurhash": { - "version": "0.1.4", - "dev": true - }, - "indent-string": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-5.0.0.tgz", - "integrity": "sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg==", - "dev": true - }, - "inflight": { - "version": "1.0.6", - "dev": true, - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.4", - "dev": true - }, - "is-arrayish": { - "version": "0.2.1", - "dev": true - }, - "is-core-module": { - "version": "2.13.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.0.tgz", - "integrity": "sha512-Z7dk6Qo8pOCp3l4tsX2C5ZVas4V+UxwQodwZhLopL91TX8UyyHEXafPcyoeeWuLrwzHcr3igO78wNLwHJHsMCQ==", - "dev": true, - "requires": { - "has": "^1.0.3" - } - }, - "is-docker": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz", - "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==", - "dev": true - }, - "is-extglob": { - "version": "2.1.1", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true - }, - "is-generator-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", - "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", - "dev": true - }, - "is-glob": { - "version": "4.0.3", - "dev": true, - "requires": { - "is-extglob": "^2.1.1" - } - }, - "is-inside-container": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz", - "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==", - "dev": true, - "requires": { - "is-docker": "^3.0.0" - } - }, - "is-number": { - "version": "7.0.0", - "dev": true - }, - "is-path-inside": { - "version": "3.0.3", - "dev": true - }, - "is-plain-obj": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", - "integrity": "sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==", - "dev": true - }, - "is-stream": { - "version": "2.0.1", - "dev": true - }, - "is-wsl": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", - "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", - "dev": true, - "requires": { - "is-docker": "^2.0.0" - }, - "dependencies": { - "is-docker": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", - "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", - "dev": true - } - } - }, - "isexe": { - "version": "2.0.0", - "dev": true - }, - "istanbul-lib-coverage": { - "version": "3.2.0", - "dev": true - }, - "istanbul-lib-instrument": { - "version": "5.2.1", - "dev": true, - "requires": { - "@babel/core": "^7.12.3", - "@babel/parser": "^7.14.7", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-coverage": "^3.2.0", - "semver": "^6.3.0" - }, - "dependencies": { - "semver": { - "version": "6.3.0", - "dev": true - } - } - }, - "istanbul-lib-report": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", - "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", - "dev": true, - "requires": { - "istanbul-lib-coverage": "^3.0.0", - "make-dir": "^4.0.0", - "supports-color": "^7.1.0" - }, - "dependencies": { - "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 - }, - "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" - } - } - } - }, - "istanbul-lib-source-maps": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", - "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", - "dev": true, - "requires": { - "debug": "^4.1.1", - "istanbul-lib-coverage": "^3.0.0", - "source-map": "^0.6.1" - } - }, - "istanbul-reports": { - "version": "3.1.6", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.6.tgz", - "integrity": "sha512-TLgnMkKg3iTDsQ9PbPTdpfAK2DzjF9mqUG7RMgcQl8oFjad8ob4laGxv5XV5U9MAfx8D6tSJiUyuAwzLicaxlg==", - "dev": true, - "requires": { - "html-escaper": "^2.0.0", - "istanbul-lib-report": "^3.0.0" - } - }, - "jest": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz", - "integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==", - "dev": true, - "requires": { - "@jest/core": "^29.7.0", - "@jest/types": "^29.6.3", - "import-local": "^3.0.2", - "jest-cli": "^29.7.0" - } - }, - "jest-changed-files": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.7.0.tgz", - "integrity": "sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==", - "dev": true, - "requires": { - "execa": "^5.0.0", - "jest-util": "^29.7.0", - "p-limit": "^3.1.0" - } - }, - "jest-circus": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.7.0.tgz", - "integrity": "sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==", - "dev": true, - "requires": { - "@jest/environment": "^29.7.0", - "@jest/expect": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "co": "^4.6.0", - "dedent": "^1.0.0", - "is-generator-fn": "^2.0.0", - "jest-each": "^29.7.0", - "jest-matcher-utils": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-runtime": "^29.7.0", - "jest-snapshot": "^29.7.0", - "jest-util": "^29.7.0", - "p-limit": "^3.1.0", - "pretty-format": "^29.7.0", - "pure-rand": "^6.0.0", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - } - }, - "jest-cli": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.7.0.tgz", - "integrity": "sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==", - "dev": true, - "requires": { - "@jest/core": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/types": "^29.6.3", - "chalk": "^4.0.0", - "create-jest": "^29.7.0", - "exit": "^0.1.2", - "import-local": "^3.0.2", - "jest-config": "^29.7.0", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "yargs": "^17.3.1" - } - }, - "jest-config": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.7.0.tgz", - "integrity": "sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==", - "dev": true, - "requires": { - "@babel/core": "^7.11.6", - "@jest/test-sequencer": "^29.7.0", - "@jest/types": "^29.6.3", - "babel-jest": "^29.7.0", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "deepmerge": "^4.2.2", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "jest-circus": "^29.7.0", - "jest-environment-node": "^29.7.0", - "jest-get-type": "^29.6.3", - "jest-regex-util": "^29.6.3", - "jest-resolve": "^29.7.0", - "jest-runner": "^29.7.0", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "micromatch": "^4.0.4", - "parse-json": "^5.2.0", - "pretty-format": "^29.7.0", - "slash": "^3.0.0", - "strip-json-comments": "^3.1.1" - } - }, - "jest-diff": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz", - "integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==", - "dev": true, - "requires": { - "chalk": "^4.0.0", - "diff-sequences": "^29.6.3", - "jest-get-type": "^29.6.3", - "pretty-format": "^29.7.0" - } - }, - "jest-docblock": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.7.0.tgz", - "integrity": "sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==", - "dev": true, - "requires": { - "detect-newline": "^3.0.0" - } - }, - "jest-each": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.7.0.tgz", - "integrity": "sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==", - "dev": true, - "requires": { - "@jest/types": "^29.6.3", - "chalk": "^4.0.0", - "jest-get-type": "^29.6.3", - "jest-util": "^29.7.0", - "pretty-format": "^29.7.0" - } - }, - "jest-environment-node": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.7.0.tgz", - "integrity": "sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==", - "dev": true, - "requires": { - "@jest/environment": "^29.7.0", - "@jest/fake-timers": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "jest-mock": "^29.7.0", - "jest-util": "^29.7.0" - } - }, - "jest-get-type": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", - "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", - "dev": true - }, - "jest-haste-map": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz", - "integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==", - "dev": true, - "requires": { - "@jest/types": "^29.6.3", - "@types/graceful-fs": "^4.1.3", - "@types/node": "*", - "anymatch": "^3.0.3", - "fb-watchman": "^2.0.0", - "fsevents": "^2.3.2", - "graceful-fs": "^4.2.9", - "jest-regex-util": "^29.6.3", - "jest-util": "^29.7.0", - "jest-worker": "^29.7.0", - "micromatch": "^4.0.4", - "walker": "^1.0.8" - } - }, - "jest-leak-detector": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz", - "integrity": "sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==", - "dev": true, - "requires": { - "jest-get-type": "^29.6.3", - "pretty-format": "^29.7.0" - } - }, - "jest-matcher-utils": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz", - "integrity": "sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==", - "dev": true, - "requires": { - "chalk": "^4.0.0", - "jest-diff": "^29.7.0", - "jest-get-type": "^29.6.3", - "pretty-format": "^29.7.0" - } - }, - "jest-message-util": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.7.0.tgz", - "integrity": "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.12.13", - "@jest/types": "^29.6.3", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "micromatch": "^4.0.4", - "pretty-format": "^29.7.0", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - } - }, - "jest-mock": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.7.0.tgz", - "integrity": "sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==", - "dev": true, - "requires": { - "@jest/types": "^29.6.3", - "@types/node": "*", - "jest-util": "^29.7.0" - } - }, - "jest-pnp-resolver": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", - "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", - "dev": true, - "requires": {} - }, - "jest-regex-util": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz", - "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==", - "dev": true - }, - "jest-resolve": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.7.0.tgz", - "integrity": "sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==", - "dev": true, - "requires": { - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "jest-pnp-resolver": "^1.2.2", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "resolve": "^1.20.0", - "resolve.exports": "^2.0.0", - "slash": "^3.0.0" - } - }, - "jest-resolve-dependencies": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.7.0.tgz", - "integrity": "sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==", - "dev": true, - "requires": { - "jest-regex-util": "^29.6.3", - "jest-snapshot": "^29.7.0" - } - }, - "jest-runner": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.7.0.tgz", - "integrity": "sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==", - "dev": true, - "requires": { - "@jest/console": "^29.7.0", - "@jest/environment": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "emittery": "^0.13.1", - "graceful-fs": "^4.2.9", - "jest-docblock": "^29.7.0", - "jest-environment-node": "^29.7.0", - "jest-haste-map": "^29.7.0", - "jest-leak-detector": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-resolve": "^29.7.0", - "jest-runtime": "^29.7.0", - "jest-util": "^29.7.0", - "jest-watcher": "^29.7.0", - "jest-worker": "^29.7.0", - "p-limit": "^3.1.0", - "source-map-support": "0.5.13" - } - }, - "jest-runtime": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.7.0.tgz", - "integrity": "sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==", - "dev": true, - "requires": { - "@jest/environment": "^29.7.0", - "@jest/fake-timers": "^29.7.0", - "@jest/globals": "^29.7.0", - "@jest/source-map": "^29.6.3", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "cjs-module-lexer": "^1.0.0", - "collect-v8-coverage": "^1.0.0", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-mock": "^29.7.0", - "jest-regex-util": "^29.6.3", - "jest-resolve": "^29.7.0", - "jest-snapshot": "^29.7.0", - "jest-util": "^29.7.0", - "slash": "^3.0.0", - "strip-bom": "^4.0.0" - } - }, - "jest-snapshot": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.7.0.tgz", - "integrity": "sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==", - "dev": true, - "requires": { - "@babel/core": "^7.11.6", - "@babel/generator": "^7.7.2", - "@babel/plugin-syntax-jsx": "^7.7.2", - "@babel/plugin-syntax-typescript": "^7.7.2", - "@babel/types": "^7.3.3", - "@jest/expect-utils": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "babel-preset-current-node-syntax": "^1.0.0", - "chalk": "^4.0.0", - "expect": "^29.7.0", - "graceful-fs": "^4.2.9", - "jest-diff": "^29.7.0", - "jest-get-type": "^29.6.3", - "jest-matcher-utils": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0", - "natural-compare": "^1.4.0", - "pretty-format": "^29.7.0", - "semver": "^7.5.3" - } - }, - "jest-util": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", - "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", - "dev": true, - "requires": { - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - } - }, - "jest-validate": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz", - "integrity": "sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==", - "dev": true, - "requires": { - "@jest/types": "^29.6.3", - "camelcase": "^6.2.0", - "chalk": "^4.0.0", - "jest-get-type": "^29.6.3", - "leven": "^3.1.0", - "pretty-format": "^29.7.0" - }, - "dependencies": { - "camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", - "dev": true - } - } - }, - "jest-watcher": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.7.0.tgz", - "integrity": "sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==", - "dev": true, - "requires": { - "@jest/test-result": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "emittery": "^0.13.1", - "jest-util": "^29.7.0", - "string-length": "^4.0.1" - } - }, - "jest-worker": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", - "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", - "dev": true, - "requires": { - "@types/node": "*", - "jest-util": "^29.7.0", - "merge-stream": "^2.0.0", - "supports-color": "^8.0.0" - }, - "dependencies": { - "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 - }, - "supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "js-tokens": { - "version": "4.0.0", - "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" - } - }, - "jsesc": { - "version": "2.5.2", - "dev": true - }, - "json-parse-even-better-errors": { - "version": "2.3.1", - "dev": true - }, - "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", - "dev": true - }, - "json5": { - "version": "2.2.3", - "dev": true - }, - "kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", - "dev": true - }, - "kleur": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", - "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", - "dev": true - }, - "leven": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", - "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", - "dev": true - }, - "levn": { - "version": "0.4.1", - "dev": true, - "requires": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" - } - }, - "lines-and-columns": { - "version": "1.2.4", - "dev": true - }, - "locate-path": { - "version": "6.0.0", - "dev": true, - "requires": { - "p-locate": "^5.0.0" - } - }, - "lodash.memoize": { - "version": "4.1.2", - "dev": true - }, - "lodash.merge": { - "version": "4.6.2", - "dev": true - }, - "lru-cache": { - "version": "6.0.0", - "dev": true, - "requires": { - "yallist": "^4.0.0" - } - }, - "make-dir": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", - "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", - "dev": true, - "requires": { - "semver": "^7.5.3" - } - }, - "make-error": { - "version": "1.3.6", - "dev": true - }, - "makeerror": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", - "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", - "dev": true, - "requires": { - "tmpl": "1.0.5" - } - }, - "map-obj": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.3.0.tgz", - "integrity": "sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==", - "dev": true - }, - "meow": { - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/meow/-/meow-11.0.0.tgz", - "integrity": "sha512-Cl0yeeIrko6d94KpUo1M+0X1sB14ikoaqlIGuTH1fW4I+E3+YljL54/hb/BWmVfrV9tTV9zU04+xjw08Fh2WkA==", - "dev": true, - "requires": { - "@types/minimist": "^1.2.2", - "camelcase-keys": "^8.0.2", - "decamelize": "^6.0.0", - "decamelize-keys": "^1.1.0", - "hard-rejection": "^2.1.0", - "minimist-options": "4.1.0", - "normalize-package-data": "^4.0.1", - "read-pkg-up": "^9.1.0", - "redent": "^4.0.0", - "trim-newlines": "^4.0.2", - "type-fest": "^3.1.0", - "yargs-parser": "^21.1.1" - }, - "dependencies": { - "type-fest": { - "version": "3.13.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-3.13.1.tgz", - "integrity": "sha512-tLq3bSNx+xSpwvAJnzrK0Ep5CLNWjvFTOp71URMaAEWBfRb9nnJiBoUe0tF8bI4ZFO3omgBR6NvnbzVUT3Ly4g==", - "dev": true - } - } - }, - "merge-stream": { - "version": "2.0.0", - "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", - "dev": true, - "requires": { - "braces": "^3.0.2", - "picomatch": "^2.3.1" - } - }, - "mimic-fn": { - "version": "2.1.0", - "dev": true - }, - "min-indent": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", - "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", - "dev": true - }, - "minimatch": { - "version": "3.1.2", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "minimist-options": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/minimist-options/-/minimist-options-4.1.0.tgz", - "integrity": "sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==", - "dev": true, - "requires": { - "arrify": "^1.0.1", - "is-plain-obj": "^1.1.0", - "kind-of": "^6.0.3" - } - }, - "ms": { - "version": "2.1.2" - }, - "natural-compare": { - "version": "1.4.0", - "dev": true - }, - "node-int64": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", - "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", - "dev": true - }, - "node-releases": { - "version": "2.0.6", - "dev": true - }, - "normalize-package-data": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-4.0.1.tgz", - "integrity": "sha512-EBk5QKKuocMJhB3BILuKhmaPjI8vNRSpIfO9woLC6NyHVkKKdVEdAO1mrT0ZfxNR1lKwCcTkuZfmGIFdizZ8Pg==", - "dev": true, - "requires": { - "hosted-git-info": "^5.0.0", - "is-core-module": "^2.8.1", - "semver": "^7.3.5", - "validate-npm-package-license": "^3.0.4" - } - }, - "normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true - }, - "npm-run-path": { - "version": "4.0.1", - "dev": true, - "requires": { - "path-key": "^3.0.0" - } - }, - "once": { - "version": "1.4.0", - "dev": true, - "requires": { - "wrappy": "1" - } - }, - "onetime": { - "version": "5.1.2", - "dev": true, - "requires": { - "mimic-fn": "^2.1.0" - } - }, - "open": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/open/-/open-9.1.0.tgz", - "integrity": "sha512-OS+QTnw1/4vrf+9hh1jc1jnYjzSG4ttTBB8UxOwAnInG3Uo4ssetzC1ihqaIHjLJnA5GGlRl6QlZXOTQhRBUvg==", - "dev": true, - "requires": { - "default-browser": "^4.0.0", - "define-lazy-prop": "^3.0.0", - "is-inside-container": "^1.0.0", - "is-wsl": "^2.2.0" - } - }, - "open-cli": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/open-cli/-/open-cli-7.2.0.tgz", - "integrity": "sha512-1ANJc8oJ92FiaNZ0o2Hw4WBvDJoXs1P74aFMtpAvlbkIPV4uPcQvDz7V6kMOrsZkmB4tglrHVMlLQaafuUuxXg==", - "dev": true, - "requires": { - "file-type": "^18.2.1", - "get-stdin": "^9.0.0", - "meow": "^11.0.0", - "open": "^9.0.0", - "tempy": "^3.0.0" - } - }, - "optionator": { - "version": "0.9.3", - "dev": true, - "requires": { - "@aashutoshrathi/word-wrap": "^1.2.3", - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0" - } - }, - "p-limit": { - "version": "3.1.0", - "dev": true, - "requires": { - "yocto-queue": "^0.1.0" - } - }, - "p-locate": { - "version": "5.0.0", - "dev": true, - "requires": { - "p-limit": "^3.0.2" - } - }, - "p-try": { - "version": "2.2.0", - "dev": true - }, - "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" - } - }, - "parse-json": { - "version": "5.2.0", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" - } - }, - "path-exists": { - "version": "4.0.0", - "dev": true - }, - "path-is-absolute": { - "version": "1.0.1", - "dev": true - }, - "path-key": { - "version": "3.1.1", - "dev": true - }, - "path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "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 - }, - "peek-readable": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/peek-readable/-/peek-readable-5.0.0.tgz", - "integrity": "sha512-YtCKvLUOvwtMGmrniQPdO7MwPjgkFBtFIrmfSbYmYuq3tKDV/mcfAhBth1+C3ru7uXIZasc/pHnb+YDYNkkj4A==", - "dev": true - }, - "picocolors": { - "version": "1.0.0", - "dev": true - }, - "picomatch": { - "version": "2.3.1", - "dev": true - }, - "pirates": { - "version": "4.0.5", - "dev": true - }, - "pkg-dir": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", - "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", - "dev": true, - "requires": { - "find-up": "^4.0.0" - }, - "dependencies": { - "find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "requires": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - } - }, - "locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "requires": { - "p-locate": "^4.1.0" - } - }, - "p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "requires": { - "p-limit": "^2.2.0" - } - } - } - }, - "prelude-ls": { - "version": "1.2.1", - "dev": true - }, - "pretty-format": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", - "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", - "dev": true, - "requires": { - "@jest/schemas": "^29.6.3", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "5.2.0", - "dev": true - } - } - }, - "prompts": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", - "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", - "dev": true, - "requires": { - "kleur": "^3.0.3", - "sisteransi": "^1.0.5" - } - }, - "punycode": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "dev": true - }, - "pure-rand": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.0.3.tgz", - "integrity": "sha512-KddyFewCsO0j3+np81IQ+SweXLDnDQTs5s67BOnrYmYe/yNmUhttQyGsYzy8yUnoljGAQ9sl38YB4vH8ur7Y+w==", - "dev": true - }, - "queue-microtask": { - "version": "1.2.3" - }, - "quick-lru": { - "version": "6.1.2", - "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-6.1.2.tgz", - "integrity": "sha512-AAFUA5O1d83pIHEhJwWCq/RQcRukCkn/NSm2QsTEMle5f2hP0ChI2+3Xb051PZCkLryI/Ir1MVKviT2FIloaTQ==", - "dev": true - }, - "react-is": { - "version": "18.2.0", - "dev": true - }, - "read-pkg": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-7.1.0.tgz", - "integrity": "sha512-5iOehe+WF75IccPc30bWTbpdDQLOCc3Uu8bi3Dte3Eueij81yx1Mrufk8qBx/YAbR4uL1FdUr+7BKXDwEtisXg==", - "dev": true, - "requires": { - "@types/normalize-package-data": "^2.4.1", - "normalize-package-data": "^3.0.2", - "parse-json": "^5.2.0", - "type-fest": "^2.0.0" - }, - "dependencies": { - "hosted-git-info": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz", - "integrity": "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - }, - "normalize-package-data": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.3.tgz", - "integrity": "sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA==", - "dev": true, - "requires": { - "hosted-git-info": "^4.0.1", - "is-core-module": "^2.5.0", - "semver": "^7.3.4", - "validate-npm-package-license": "^3.0.1" - } - }, - "type-fest": { - "version": "2.19.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz", - "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==", - "dev": true - } - } - }, - "read-pkg-up": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-9.1.0.tgz", - "integrity": "sha512-vaMRR1AC1nrd5CQM0PhlRsO5oc2AAigqr7cCrZ/MW/Rsaflz4RlgzkpL4qoU/z1F6wrbd85iFv1OQj/y5RdGvg==", - "dev": true, - "requires": { - "find-up": "^6.3.0", - "read-pkg": "^7.1.0", - "type-fest": "^2.5.0" - }, - "dependencies": { - "find-up": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-6.3.0.tgz", - "integrity": "sha512-v2ZsoEuVHYy8ZIlYqwPe/39Cy+cFDzp4dXPaxNvkEuouymu+2Jbz0PxpKarJHYJTmv2HWT3O382qY8l4jMWthw==", - "dev": true, - "requires": { - "locate-path": "^7.1.0", - "path-exists": "^5.0.0" - } - }, - "locate-path": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-7.2.0.tgz", - "integrity": "sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA==", - "dev": true, - "requires": { - "p-locate": "^6.0.0" - } - }, - "p-limit": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-4.0.0.tgz", - "integrity": "sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==", - "dev": true, - "requires": { - "yocto-queue": "^1.0.0" - } - }, - "p-locate": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-6.0.0.tgz", - "integrity": "sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==", - "dev": true, - "requires": { - "p-limit": "^4.0.0" - } - }, - "path-exists": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-5.0.0.tgz", - "integrity": "sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==", - "dev": true - }, - "type-fest": { - "version": "2.19.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz", - "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==", - "dev": true - }, - "yocto-queue": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.0.0.tgz", - "integrity": "sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==", - "dev": true - } - } - }, - "readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "dev": true, - "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - } - }, - "readable-web-to-node-stream": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/readable-web-to-node-stream/-/readable-web-to-node-stream-3.0.2.tgz", - "integrity": "sha512-ePeK6cc1EcKLEhJFt/AebMCLL+GgSKhuygrZ/GLaKZYEecIgIECf4UaUuaByiGtzckwR4ain9VzUh95T1exYGw==", - "dev": true, - "requires": { - "readable-stream": "^3.6.0" - } - }, - "redent": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/redent/-/redent-4.0.0.tgz", - "integrity": "sha512-tYkDkVVtYkSVhuQ4zBgfvciymHaeuel+zFKXShfDnFP5SyVEP7qo70Rf1jTOTCx3vGNAbnEi/xFkcfQVMIBWag==", - "dev": true, - "requires": { - "indent-string": "^5.0.0", - "strip-indent": "^4.0.0" - } - }, - "require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", - "dev": true - }, - "resolve": { - "version": "1.22.4", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.4.tgz", - "integrity": "sha512-PXNdCiPqDqeUou+w1C2eTQbNfxKSuMxqTCuvlmmMsk1NWHL5fRrhY6Pl0qEYYc6+QqGClco1Qj8XnjPego4wfg==", - "dev": true, - "requires": { - "is-core-module": "^2.13.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - } - }, - "resolve-cwd": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", - "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", - "dev": true, - "requires": { - "resolve-from": "^5.0.0" - }, - "dependencies": { - "resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "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 - }, - "resolve.exports": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.2.tgz", - "integrity": "sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg==", - "dev": true - }, - "reusify": { - "version": "1.0.4", - "dev": true - }, - "rimraf": { - "version": "3.0.2", - "dev": true, - "requires": { - "glob": "^7.1.3" - } - }, - "run-applescript": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-5.0.0.tgz", - "integrity": "sha512-XcT5rBksx1QdIhlFOCtgZkB99ZEouFZ1E2Kc2LHqNW13U3/74YGdkQRmThTwxy4QIyookibDKYZOPqX//6BlAg==", - "dev": true, - "requires": { - "execa": "^5.0.0" - } - }, - "run-parallel": { - "version": "1.2.0", - "dev": true, - "requires": { - "queue-microtask": "^1.2.2" - } - }, - "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==", - "dev": true - }, - "sdp-transform": { - "version": "2.14.1" - }, - "semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - }, - "shebang-command": { - "version": "2.0.0", - "dev": true, - "requires": { - "shebang-regex": "^3.0.0" - } - }, - "shebang-regex": { - "version": "3.0.0", - "dev": true - }, - "signal-exit": { - "version": "3.0.7", - "dev": true - }, - "sisteransi": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", - "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", - "dev": true - }, - "slash": { - "version": "3.0.0", - "dev": true - }, - "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 - }, - "source-map-support": { - "version": "0.5.13", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", - "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", - "dev": true, - "requires": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, - "spdx-correct": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", - "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", - "dev": true, - "requires": { - "spdx-expression-parse": "^3.0.0", - "spdx-license-ids": "^3.0.0" - } - }, - "spdx-exceptions": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", - "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", - "dev": true - }, - "spdx-expression-parse": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", - "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", - "dev": true, - "requires": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" - } - }, - "spdx-license-ids": { - "version": "3.0.16", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.16.tgz", - "integrity": "sha512-eWN+LnM3GR6gPu35WxNgbGl8rmY1AEmoMDvL/QD6zYmPWgywxWqJWNdLGT+ke8dKNWrcYgYjPpG5gbTfghP8rw==", - "dev": true - }, - "sprintf-js": { - "version": "1.0.3", - "dev": true - }, - "stack-utils": { - "version": "2.0.6", - "dev": true, - "requires": { - "escape-string-regexp": "^2.0.0" - }, - "dependencies": { - "escape-string-regexp": { - "version": "2.0.0", - "dev": true - } - } - }, - "string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dev": true, - "requires": { - "safe-buffer": "~5.2.0" - } - }, - "string-length": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", - "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", - "dev": true, - "requires": { - "char-regex": "^1.0.2", - "strip-ansi": "^6.0.0" - } - }, - "string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - } - }, - "strip-ansi": { - "version": "6.0.1", - "dev": true, - "requires": { - "ansi-regex": "^5.0.1" - } - }, - "strip-bom": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", - "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", - "dev": true - }, - "strip-final-newline": { - "version": "2.0.0", - "dev": true - }, - "strip-indent": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-4.0.0.tgz", - "integrity": "sha512-mnVSV2l+Zv6BLpSD/8V87CW/y9EmmbYzGCIavsnsI6/nwn26DwffM/yztm30Z/I2DY9wdS3vXVCMnHDgZaVNoA==", - "dev": true, - "requires": { - "min-indent": "^1.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 - }, - "strtok3": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/strtok3/-/strtok3-7.0.0.tgz", - "integrity": "sha512-pQ+V+nYQdC5H3Q7qBZAz/MO6lwGhoC2gOAjuouGf/VO0m7vQRh8QNMl2Uf6SwAtzZ9bOw3UIeBukEGNJl5dtXQ==", - "dev": true, - "requires": { - "@tokenizer/token": "^0.3.0", - "peek-readable": "^5.0.0" - } - }, - "supports-color": { - "version": "9.4.0" - }, - "supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "dev": true - }, - "temp-dir": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-3.0.0.tgz", - "integrity": "sha512-nHc6S/bwIilKHNRgK/3jlhDoIHcp45YgyiwcAk46Tr0LfEqGBVpmiAyuiuxeVE44m3mXnEeVhaipLOEWmH+Njw==", - "dev": true - }, - "tempy": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/tempy/-/tempy-3.1.0.tgz", - "integrity": "sha512-7jDLIdD2Zp0bDe5r3D2qtkd1QOCacylBuL7oa4udvN6v2pqr4+LcCr67C8DR1zkpaZ8XosF5m1yQSabKAW6f2g==", - "dev": true, - "requires": { - "is-stream": "^3.0.0", - "temp-dir": "^3.0.0", - "type-fest": "^2.12.2", - "unique-string": "^3.0.0" - }, - "dependencies": { - "is-stream": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", - "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", - "dev": true - }, - "type-fest": { - "version": "2.19.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz", - "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==", - "dev": true - } - } - }, - "test-exclude": { - "version": "6.0.0", - "dev": true, - "requires": { - "@istanbuljs/schema": "^0.1.2", - "glob": "^7.1.4", - "minimatch": "^3.0.4" - } - }, - "text-table": { - "version": "0.2.0", - "dev": true - }, - "titleize": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/titleize/-/titleize-3.0.0.tgz", - "integrity": "sha512-KxVu8EYHDPBdUYdKZdKtU2aj2XfEx9AfjXxE/Aj0vT06w2icA09Vus1rh6eSu1y01akYg6BjIK/hxyLJINoMLQ==", - "dev": true - }, - "tmpl": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", - "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", - "dev": true - }, - "to-fast-properties": { - "version": "2.0.0", - "dev": true - }, - "to-regex-range": { - "version": "5.0.1", - "dev": true, - "requires": { - "is-number": "^7.0.0" - } - }, - "token-types": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/token-types/-/token-types-5.0.1.tgz", - "integrity": "sha512-Y2fmSnZjQdDb9W4w4r1tswlMHylzWIeOKpx0aZH9BgGtACHhrk3OkT52AzwcuqTRBZtvvnTjDBh8eynMulu8Vg==", - "dev": true, - "requires": { - "@tokenizer/token": "^0.3.0", - "ieee754": "^1.2.1" - } - }, - "trim-newlines": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-4.1.1.tgz", - "integrity": "sha512-jRKj0n0jXWo6kh62nA5TEh3+4igKDXLvzBJcPpiizP7oOolUrYIxmVBG9TOtHYFHoddUk6YvAkGeGoSVTXfQXQ==", - "dev": true - }, - "ts-api-utils": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.0.1.tgz", - "integrity": "sha512-lC/RGlPmwdrIBFTX59wwNzqh7aR2otPNPR/5brHZm/XKFYKsfqxihXUe9pU3JI+3vGkl+vyCoNNnPhJn3aLK1A==", - "dev": true, - "requires": {} - }, - "ts-jest": { - "version": "29.1.1", - "dev": true, - "requires": { - "bs-logger": "0.x", - "fast-json-stable-stringify": "2.x", - "jest-util": "^29.0.0", - "json5": "^2.2.3", - "lodash.memoize": "4.x", - "make-error": "1.x", - "semver": "^7.5.3", - "yargs-parser": "^21.0.1" - } - }, - "tslib": { - "version": "1.14.1", - "dev": true - }, - "tsutils": { - "version": "3.21.0", - "dev": true, - "requires": { - "tslib": "^1.8.1" - } - }, - "type-check": { - "version": "0.4.0", - "dev": true, - "requires": { - "prelude-ls": "^1.2.1" - } - }, - "type-detect": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", - "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", - "dev": true - }, - "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 - }, - "typescript": { - "version": "5.3.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz", - "integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==", - "dev": true - }, - "ua-parser-js": { - "version": "1.0.37", - "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-1.0.37.tgz", - "integrity": "sha512-bhTyI94tZofjo+Dn8SN6Zv8nBDvyXTymAdM3LDI/0IboIUwTu1rEhW7v2TfiVsoYWgkQ4kOVqnI8APUFbIQIFQ==" - }, - "unique-string": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-3.0.0.tgz", - "integrity": "sha512-VGXBUVwxKMBUznyffQweQABPRRW1vHZAbadFZud4pLFAqRGvv/96vafgjWFqzourzr8YonlQiPgH0YCJfawoGQ==", - "dev": true, - "requires": { - "crypto-random-string": "^4.0.0" - } - }, - "untildify": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/untildify/-/untildify-4.0.0.tgz", - "integrity": "sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==", - "dev": true - }, - "update-browserslist-db": { - "version": "1.0.10", - "dev": true, - "requires": { - "escalade": "^3.1.1", - "picocolors": "^1.0.0" - } - }, - "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" - } - }, - "util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "dev": true - }, - "uuid": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz", - "integrity": "sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==" - }, - "v8-to-istanbul": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.1.0.tgz", - "integrity": "sha512-6z3GW9x8G1gd+JIIgQQQxXuiJtCXeAjp6RaPEPLv62mH3iPHPxV6W3robxtCzNErRo6ZwTmzWhsbNvjyEBKzKA==", - "dev": true, - "requires": { - "@jridgewell/trace-mapping": "^0.3.12", - "@types/istanbul-lib-coverage": "^2.0.1", - "convert-source-map": "^1.6.0" - }, - "dependencies": { - "convert-source-map": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", - "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", - "dev": true - } - } - }, - "validate-npm-package-license": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", - "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", - "dev": true, - "requires": { - "spdx-correct": "^3.0.0", - "spdx-expression-parse": "^3.0.0" - } - }, - "walker": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", - "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", - "dev": true, - "requires": { - "makeerror": "1.0.12" - } - }, - "which": { - "version": "2.0.2", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - }, - "wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "requires": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - } - }, - "wrappy": { - "version": "1.0.2", - "dev": true - }, - "write-file-atomic": { - "version": "4.0.2", - "dev": true, - "requires": { - "imurmurhash": "^0.1.4", - "signal-exit": "^3.0.7" - } - }, - "y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true - }, - "yallist": { - "version": "4.0.0", - "dev": true - }, - "yargs": { - "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "dev": true, - "requires": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - } - }, - "yargs-parser": { - "version": "21.1.1", - "dev": true - }, - "yocto-queue": { - "version": "0.1.0", - "dev": true - } - } + "name": "mediasoup-client", + "version": "3.7.1", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "mediasoup-client", + "version": "3.7.1", + "license": "ISC", + "dependencies": { + "@types/debug": "^4.1.12", + "awaitqueue": "^3.0.2", + "debug": "^4.3.4", + "events": "^3.3.0", + "fake-mediastreamtrack": "^1.2.0", + "h264-profile-level-id": "^2.0.0", + "queue-microtask": "^1.2.3", + "sdp-transform": "^2.14.1", + "supports-color": "^9.4.0", + "ua-parser-js": "^1.0.37" + }, + "devDependencies": { + "@types/events": "^3.0.3", + "@types/jest": "^29.5.11", + "@types/sdp-transform": "^2.4.9", + "@types/ua-parser-js": "^0.7.39", + "@typescript-eslint/eslint-plugin": "^6.18.1", + "@typescript-eslint/parser": "^6.18.1", + "eslint": "^8.56.0", + "eslint-config-prettier": "^9.1.0", + "eslint-plugin-jest": "^27.6.1", + "eslint-plugin-prettier": "^5.1.2", + "jest": "^29.7.0", + "open-cli": "^7.2.0", + "prettier": "^3.1.1", + "ts-jest": "^29.1.1", + "typescript": "^5.3.3" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mediasoup" + } + }, + "node_modules/@aashutoshrathi/word-wrap": { + "version": "1.2.6", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@ampproject/remapping": { + "version": "2.2.0", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@jridgewell/gen-mapping": "^0.1.0", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.18.6", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/highlight": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.20.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.20.2", + "dev": true, + "license": "MIT", + "dependencies": { + "@ampproject/remapping": "^2.1.0", + "@babel/code-frame": "^7.18.6", + "@babel/generator": "^7.20.2", + "@babel/helper-compilation-targets": "^7.20.0", + "@babel/helper-module-transforms": "^7.20.2", + "@babel/helpers": "^7.20.1", + "@babel/parser": "^7.20.2", + "@babel/template": "^7.18.10", + "@babel/traverse": "^7.20.1", + "@babel/types": "^7.20.2", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.1", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/core/node_modules/convert-source-map": { + "version": "1.9.0", + "dev": true, + "license": "MIT" + }, + "node_modules/@babel/core/node_modules/semver": { + "version": "6.3.0", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/generator": { + "version": "7.20.4", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.20.2", + "@jridgewell/gen-mapping": "^0.3.2", + "jsesc": "^2.5.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/generator/node_modules/@jridgewell/gen-mapping": { + "version": "0.3.2", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.20.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.20.0", + "@babel/helper-validator-option": "^7.18.6", + "browserslist": "^4.21.3", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/semver": { + "version": "6.3.0", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-environment-visitor": { + "version": "7.18.9", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-function-name": { + "version": "7.19.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.18.10", + "@babel/types": "^7.19.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-hoist-variables": { + "version": "7.18.6", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.18.6", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.20.2", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-module-imports": "^7.18.6", + "@babel/helper-simple-access": "^7.20.2", + "@babel/helper-split-export-declaration": "^7.18.6", + "@babel/helper-validator-identifier": "^7.19.1", + "@babel/template": "^7.18.10", + "@babel/traverse": "^7.20.1", + "@babel/types": "^7.20.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz", + "integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-simple-access": { + "version": "7.20.2", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.20.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-split-export-declaration": { + "version": "7.18.6", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.19.4", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.19.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.18.6", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.20.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.18.10", + "@babel/traverse": "^7.20.1", + "@babel/types": "^7.20.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight": { + "version": "7.18.6", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.18.6", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight/node_modules/ansi-styles": { + "version": "3.2.1", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/chalk": { + "version": "2.4.2", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/color-convert": { + "version": "1.9.3", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/@babel/highlight/node_modules/color-name": { + "version": "1.1.3", + "dev": true, + "license": "MIT" + }, + "node_modules/@babel/highlight/node_modules/escape-string-regexp": { + "version": "1.0.5", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@babel/highlight/node_modules/supports-color": { + "version": "5.5.0", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/parser": { + "version": "7.20.13", + "dev": true, + "license": "MIT", + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-syntax-async-generators": { + "version": "7.8.4", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-bigint": { + "version": "7.8.3", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-properties": { + "version": "7.12.13", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-meta": { + "version": "7.10.4", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-json-strings": { + "version": "7.8.3", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-jsx": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.22.5.tgz", + "integrity": "sha512-gvyP4hZrgrs/wWMaocvxZ44Hw0b3W8Pe+cMxc8V1ULQ07oh8VNbIRaoD1LRZVTvD+0nieDKjfgKg89sD7rrKrg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-logical-assignment-operators": { + "version": "7.10.4", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.8.3", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-numeric-separator": { + "version": "7.10.4", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-catch-binding": { + "version": "7.8.3", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-top-level-await": { + "version": "7.14.5", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-typescript": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.22.5.tgz", + "integrity": "sha512-1mS2o03i7t1c6VzH6fdQ3OA8tcEIxwG18zIPRp+UY1Ihv6W+XZzBCVxExF9upussPXJ0xE9XRHwMoNs1ep/nRQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/template": { + "version": "7.18.10", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.18.6", + "@babel/parser": "^7.18.10", + "@babel/types": "^7.18.10" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.20.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.18.6", + "@babel/generator": "^7.20.1", + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-function-name": "^7.19.0", + "@babel/helper-hoist-variables": "^7.18.6", + "@babel/helper-split-export-declaration": "^7.18.6", + "@babel/parser": "^7.20.1", + "@babel/types": "^7.20.0", + "debug": "^4.1.0", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse/node_modules/globals": { + "version": "11.12.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/types": { + "version": "7.20.7", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.19.4", + "@babel/helper-validator-identifier": "^7.19.1", + "to-fast-properties": "^2.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@bcoe/v8-coverage": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", + "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", + "dev": true + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", + "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.6.2.tgz", + "integrity": "sha512-pPTNuaAG3QMH+buKyBIGJs3g/S5y0caxw0ygM3YyE6yJFySwiGGSzA+mM3KJ8QQvzeLh3blwgSonkFjgQdxzMw==", + "dev": true, + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", + "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", + "dev": true, + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.6.0", + "globals": "^13.19.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" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/js": { + "version": "8.56.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.56.0.tgz", + "integrity": "sha512-gMsVel9D7f2HLkBma9VbtzZRehRogVRfbr++f06nL2vnCGCNlzOD+/MUov/F4p8myyAHspEhVobgjpX64q5m6A==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/@humanwhocodes/config-array": { + "version": "0.11.13", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.13.tgz", + "integrity": "sha512-JSBDMiDKSzQVngfRjOdFXgFfklaXI4K9nLF49Auh21lmBWRLIK3+xTErTWD4KU54pb6coM6ESE7Awz/FNU3zgQ==", + "dev": true, + "dependencies": { + "@humanwhocodes/object-schema": "^2.0.1", + "debug": "^4.1.1", + "minimatch": "^3.0.5" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/object-schema": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.1.tgz", + "integrity": "sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw==", + "dev": true + }, + "node_modules/@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "dev": true, + "license": "ISC", + "dependencies": { + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/argparse": { + "version": "1.0.10", + "dev": true, + "license": "MIT", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/find-up": { + "version": "4.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/js-yaml": { + "version": "3.14.1", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/locate-path": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/p-limit": { + "version": "2.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/p-locate": { + "version": "4.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/resolve-from": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/schema": { + "version": "0.1.3", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/console": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.7.0.tgz", + "integrity": "sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/core": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.7.0.tgz", + "integrity": "sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==", + "dev": true, + "dependencies": { + "@jest/console": "^29.7.0", + "@jest/reporters": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "jest-changed-files": "^29.7.0", + "jest-config": "^29.7.0", + "jest-haste-map": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-resolve-dependencies": "^29.7.0", + "jest-runner": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "jest-watcher": "^29.7.0", + "micromatch": "^4.0.4", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/@jest/environment": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.7.0.tgz", + "integrity": "sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==", + "dev": true, + "dependencies": { + "@jest/fake-timers": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-mock": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/expect": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.7.0.tgz", + "integrity": "sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==", + "dev": true, + "dependencies": { + "expect": "^29.7.0", + "jest-snapshot": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/expect-utils": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.7.0.tgz", + "integrity": "sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==", + "dev": true, + "dependencies": { + "jest-get-type": "^29.6.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/fake-timers": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.7.0.tgz", + "integrity": "sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@sinonjs/fake-timers": "^10.0.2", + "@types/node": "*", + "jest-message-util": "^29.7.0", + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/globals": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.7.0.tgz", + "integrity": "sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==", + "dev": true, + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/expect": "^29.7.0", + "@jest/types": "^29.6.3", + "jest-mock": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/reporters": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.7.0.tgz", + "integrity": "sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==", + "dev": true, + "dependencies": { + "@bcoe/v8-coverage": "^0.2.3", + "@jest/console": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@jridgewell/trace-mapping": "^0.3.18", + "@types/node": "*", + "chalk": "^4.0.0", + "collect-v8-coverage": "^1.0.0", + "exit": "^0.1.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-instrument": "^6.0.0", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.0", + "istanbul-reports": "^3.1.3", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "jest-worker": "^29.7.0", + "slash": "^3.0.0", + "string-length": "^4.0.1", + "strip-ansi": "^6.0.0", + "v8-to-istanbul": "^9.0.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/@jest/reporters/node_modules/istanbul-lib-instrument": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.0.tgz", + "integrity": "sha512-x58orMzEVfzPUKqlbLd1hXCnySCxKdDKa6Rjg97CwuLLRI4g3FHTdnExu1OqffVFay6zeMW+T6/DowFLndWnIw==", + "dev": true, + "dependencies": { + "@babel/core": "^7.12.3", + "@babel/parser": "^7.14.7", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@jest/schemas": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", + "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", + "dev": true, + "dependencies": { + "@sinclair/typebox": "^0.27.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/source-map": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.6.3.tgz", + "integrity": "sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.18", + "callsites": "^3.0.0", + "graceful-fs": "^4.2.9" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/test-result": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.7.0.tgz", + "integrity": "sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==", + "dev": true, + "dependencies": { + "@jest/console": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "collect-v8-coverage": "^1.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/test-sequencer": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz", + "integrity": "sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==", + "dev": true, + "dependencies": { + "@jest/test-result": "^29.7.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/transform": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.7.0.tgz", + "integrity": "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==", + "dev": true, + "dependencies": { + "@babel/core": "^7.11.6", + "@jest/types": "^29.6.3", + "@jridgewell/trace-mapping": "^0.3.18", + "babel-plugin-istanbul": "^6.1.1", + "chalk": "^4.0.0", + "convert-source-map": "^2.0.0", + "fast-json-stable-stringify": "^2.1.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", + "micromatch": "^4.0.4", + "pirates": "^4.0.4", + "slash": "^3.0.0", + "write-file-atomic": "^4.0.2" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/set-array": "^1.0.0", + "@jridgewell/sourcemap-codec": "^1.4.10" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.1.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.14", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.18", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.18.tgz", + "integrity": "sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "3.1.0", + "@jridgewell/sourcemap-codec": "1.4.14" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@pkgr/core": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.1.0.tgz", + "integrity": "sha512-Zwq5OCzuwJC2jwqmpEQt7Ds1DTi6BWSwoGkbb1n9pO3hzb35BoJELx7c0T23iDkBGkh2e7tvOtjF3tr3OaQHDQ==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/unts" + } + }, + "node_modules/@sinclair/typebox": { + "version": "0.27.8", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", + "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", + "dev": true + }, + "node_modules/@sinonjs/commons": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.0.tgz", + "integrity": "sha512-jXBtWAF4vmdNmZgD5FoKsVLv3rPgDnLgPbU84LIJ3otV44vJlDRokVng5v8NFJdCf/da9legHcKaRuZs4L7faA==", + "dev": true, + "dependencies": { + "type-detect": "4.0.8" + } + }, + "node_modules/@sinonjs/fake-timers": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", + "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", + "dev": true, + "dependencies": { + "@sinonjs/commons": "^3.0.0" + } + }, + "node_modules/@tokenizer/token": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@tokenizer/token/-/token-0.3.0.tgz", + "integrity": "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==", + "dev": true + }, + "node_modules/@types/babel__core": { + "version": "7.20.1", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.1.tgz", + "integrity": "sha512-aACu/U/omhdk15O4Nfb+fHgH/z3QsfQzpnvRZhYhThms83ZnAOZz7zZAWO7mn2yyNQaA4xTO8GLK3uqFU4bYYw==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.6.4", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.4.tgz", + "integrity": "sha512-tFkciB9j2K755yrTALxD44McOrk+gfpIpvC3sxHjRawj6PfnQxrse4Clq5y/Rq+G3mrBurMax/lG8Qn2t9mSsg==", + "dev": true, + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.1.tgz", + "integrity": "sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.20.1", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.1.tgz", + "integrity": "sha512-MitHFXnhtgwsGZWtT68URpOvLN4EREih1u3QtQiN4VdAxWKRVvGCSvw/Qth0M0Qq3pJpnGOu5JaM/ydK7OGbqg==", + "dev": true, + "dependencies": { + "@babel/types": "^7.20.7" + } + }, + "node_modules/@types/debug": { + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz", + "integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==", + "dependencies": { + "@types/ms": "*" + } + }, + "node_modules/@types/events": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/events/-/events-3.0.3.tgz", + "integrity": "sha512-trOc4AAUThEz9hapPtSd7wf5tiQKvTtu5b371UxXdTuqzIh0ArcRspRP0i0Viu+LXstIQ1z96t1nsPxT9ol01g==", + "dev": true + }, + "node_modules/@types/graceful-fs": { + "version": "4.1.6", + "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.6.tgz", + "integrity": "sha512-Sig0SNORX9fdW+bQuTEovKj3uHcUL6LQKbCrrqb1X7J6/ReAbhCXRAhc+SMejhLELFj2QcyuxmUooZ4bt5ReSw==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/istanbul-lib-coverage": { + "version": "2.0.4", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/istanbul-lib-report": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/istanbul-lib-coverage": "*" + } + }, + "node_modules/@types/istanbul-reports": { + "version": "3.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/istanbul-lib-report": "*" + } + }, + "node_modules/@types/jest": { + "version": "29.5.11", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.11.tgz", + "integrity": "sha512-S2mHmYIVe13vrm6q4kN6fLYYAka15ALQki/vgDC3mIukEOx8WJlv0kQPM+d4w8Gp6u0uSdKND04IlTXBv0rwnQ==", + "dev": true, + "dependencies": { + "expect": "^29.0.0", + "pretty-format": "^29.0.0" + } + }, + "node_modules/@types/json-schema": { + "version": "7.0.12", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.12.tgz", + "integrity": "sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA==", + "dev": true + }, + "node_modules/@types/minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-hov8bUuiLiyFPGyFPE1lwWhmzYbirOXQNNo40+y3zow8aFVTeyn3VWL0VFFfdNddA8S4Vf0Tc062rzyNr7Paag==", + "dev": true + }, + "node_modules/@types/ms": { + "version": "0.7.31", + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "18.11.9", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/normalize-package-data": { + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.4.tgz", + "integrity": "sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==", + "dev": true + }, + "node_modules/@types/sdp-transform": { + "version": "2.4.9", + "resolved": "https://registry.npmjs.org/@types/sdp-transform/-/sdp-transform-2.4.9.tgz", + "integrity": "sha512-bVr+/OoZZy7wrHlNcEAAa6PAgKA4BoXPYVN2EijMC5WnGgQ4ZEuixmKnVs2roiAvr7RhIFVH17QD27cojgIZCg==", + "dev": true + }, + "node_modules/@types/semver": { + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.0.tgz", + "integrity": "sha512-G8hZ6XJiHnuhQKR7ZmysCeJWE08o8T0AXtk5darsCaTVsYZhhgUrq53jizaR2FvsoeCwJhlmwTjkXBY5Pn/ZHw==", + "dev": true + }, + "node_modules/@types/stack-utils": { + "version": "2.0.1", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/ua-parser-js": { + "version": "0.7.39", + "resolved": "https://registry.npmjs.org/@types/ua-parser-js/-/ua-parser-js-0.7.39.tgz", + "integrity": "sha512-P/oDfpofrdtF5xw433SPALpdSchtJmY7nsJItf8h3KXqOslkbySh8zq4dSWXH2oTjRvJ5PczVEoCZPow6GicLg==", + "dev": true + }, + "node_modules/@types/yargs": { + "version": "17.0.13", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@types/yargs-parser": { + "version": "21.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "6.18.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.18.1.tgz", + "integrity": "sha512-nISDRYnnIpk7VCFrGcu1rnZfM1Dh9LRHnfgdkjcbi/l7g16VYRri3TjXi9Ir4lOZSw5N/gnV/3H7jIPQ8Q4daA==", + "dev": true, + "dependencies": { + "@eslint-community/regexpp": "^4.5.1", + "@typescript-eslint/scope-manager": "6.18.1", + "@typescript-eslint/type-utils": "6.18.1", + "@typescript-eslint/utils": "6.18.1", + "@typescript-eslint/visitor-keys": "6.18.1", + "debug": "^4.3.4", + "graphemer": "^1.4.0", + "ignore": "^5.2.4", + "natural-compare": "^1.4.0", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^6.0.0 || ^6.0.0-alpha", + "eslint": "^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/scope-manager": { + "version": "6.18.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.18.1.tgz", + "integrity": "sha512-BgdBwXPFmZzaZUuw6wKiHKIovms97a7eTImjkXCZE04TGHysG+0hDQPmygyvgtkoB/aOQwSM/nWv3LzrOIQOBw==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.18.1", + "@typescript-eslint/visitor-keys": "6.18.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/types": { + "version": "6.18.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.18.1.tgz", + "integrity": "sha512-4TuMAe+tc5oA7wwfqMtB0Y5OrREPF1GeJBAjqwgZh1lEMH5PJQgWgHGfYufVB51LtjD+peZylmeyxUXPfENLCw==", + "dev": true, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/typescript-estree": { + "version": "6.18.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.18.1.tgz", + "integrity": "sha512-fv9B94UAhywPRhUeeV/v+3SBDvcPiLxRZJw/xZeeGgRLQZ6rLMG+8krrJUyIf6s1ecWTzlsbp0rlw7n9sjufHA==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.18.1", + "@typescript-eslint/visitor-keys": "6.18.1", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "minimatch": "9.0.3", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/utils": { + "version": "6.18.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.18.1.tgz", + "integrity": "sha512-zZmTuVZvD1wpoceHvoQpOiewmWu3uP9FuTWo8vqpy2ffsmfCE8mklRPi+vmnIYAIk9t/4kOThri2QCDgor+OpQ==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "@types/json-schema": "^7.0.12", + "@types/semver": "^7.5.0", + "@typescript-eslint/scope-manager": "6.18.1", + "@typescript-eslint/types": "6.18.1", + "@typescript-eslint/typescript-estree": "6.18.1", + "semver": "^7.5.4" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/visitor-keys": { + "version": "6.18.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.18.1.tgz", + "integrity": "sha512-/kvt0C5lRqGoCfsbmm7/CwMqoSkY3zzHLIjdhHZQW3VFrnz7ATecOHR7nb7V+xn4286MBxfnQfQhAmCI0u+bJA==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.18.1", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/minimatch": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "6.18.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.18.1.tgz", + "integrity": "sha512-zct/MdJnVaRRNy9e84XnVtRv9Vf91/qqe+hZJtKanjojud4wAVy/7lXxJmMyX6X6J+xc6c//YEWvpeif8cAhWA==", + "dev": true, + "dependencies": { + "@typescript-eslint/scope-manager": "6.18.1", + "@typescript-eslint/types": "6.18.1", + "@typescript-eslint/typescript-estree": "6.18.1", + "@typescript-eslint/visitor-keys": "6.18.1", + "debug": "^4.3.4" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/scope-manager": { + "version": "6.18.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.18.1.tgz", + "integrity": "sha512-BgdBwXPFmZzaZUuw6wKiHKIovms97a7eTImjkXCZE04TGHysG+0hDQPmygyvgtkoB/aOQwSM/nWv3LzrOIQOBw==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.18.1", + "@typescript-eslint/visitor-keys": "6.18.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/types": { + "version": "6.18.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.18.1.tgz", + "integrity": "sha512-4TuMAe+tc5oA7wwfqMtB0Y5OrREPF1GeJBAjqwgZh1lEMH5PJQgWgHGfYufVB51LtjD+peZylmeyxUXPfENLCw==", + "dev": true, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/typescript-estree": { + "version": "6.18.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.18.1.tgz", + "integrity": "sha512-fv9B94UAhywPRhUeeV/v+3SBDvcPiLxRZJw/xZeeGgRLQZ6rLMG+8krrJUyIf6s1ecWTzlsbp0rlw7n9sjufHA==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.18.1", + "@typescript-eslint/visitor-keys": "6.18.1", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "minimatch": "9.0.3", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/visitor-keys": { + "version": "6.18.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.18.1.tgz", + "integrity": "sha512-/kvt0C5lRqGoCfsbmm7/CwMqoSkY3zzHLIjdhHZQW3VFrnz7ATecOHR7nb7V+xn4286MBxfnQfQhAmCI0u+bJA==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.18.1", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/parser/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@typescript-eslint/parser/node_modules/minimatch": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.62.0.tgz", + "integrity": "sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/visitor-keys": "5.62.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "6.18.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.18.1.tgz", + "integrity": "sha512-wyOSKhuzHeU/5pcRDP2G2Ndci+4g653V43gXTpt4nbyoIOAASkGDA9JIAgbQCdCkcr1MvpSYWzxTz0olCn8+/Q==", + "dev": true, + "dependencies": { + "@typescript-eslint/typescript-estree": "6.18.1", + "@typescript-eslint/utils": "6.18.1", + "debug": "^4.3.4", + "ts-api-utils": "^1.0.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/scope-manager": { + "version": "6.18.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.18.1.tgz", + "integrity": "sha512-BgdBwXPFmZzaZUuw6wKiHKIovms97a7eTImjkXCZE04TGHysG+0hDQPmygyvgtkoB/aOQwSM/nWv3LzrOIQOBw==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.18.1", + "@typescript-eslint/visitor-keys": "6.18.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/types": { + "version": "6.18.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.18.1.tgz", + "integrity": "sha512-4TuMAe+tc5oA7wwfqMtB0Y5OrREPF1GeJBAjqwgZh1lEMH5PJQgWgHGfYufVB51LtjD+peZylmeyxUXPfENLCw==", + "dev": true, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/typescript-estree": { + "version": "6.18.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.18.1.tgz", + "integrity": "sha512-fv9B94UAhywPRhUeeV/v+3SBDvcPiLxRZJw/xZeeGgRLQZ6rLMG+8krrJUyIf6s1ecWTzlsbp0rlw7n9sjufHA==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.18.1", + "@typescript-eslint/visitor-keys": "6.18.1", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "minimatch": "9.0.3", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/utils": { + "version": "6.18.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.18.1.tgz", + "integrity": "sha512-zZmTuVZvD1wpoceHvoQpOiewmWu3uP9FuTWo8vqpy2ffsmfCE8mklRPi+vmnIYAIk9t/4kOThri2QCDgor+OpQ==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "@types/json-schema": "^7.0.12", + "@types/semver": "^7.5.0", + "@typescript-eslint/scope-manager": "6.18.1", + "@typescript-eslint/types": "6.18.1", + "@typescript-eslint/typescript-estree": "6.18.1", + "semver": "^7.5.4" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + } + }, + "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/visitor-keys": { + "version": "6.18.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.18.1.tgz", + "integrity": "sha512-/kvt0C5lRqGoCfsbmm7/CwMqoSkY3zzHLIjdhHZQW3VFrnz7ATecOHR7nb7V+xn4286MBxfnQfQhAmCI0u+bJA==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.18.1", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/type-utils/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@typescript-eslint/type-utils/node_modules/minimatch": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.62.0.tgz", + "integrity": "sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.62.0.tgz", + "integrity": "sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/visitor-keys": "5.62.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.62.0.tgz", + "integrity": "sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@types/json-schema": "^7.0.9", + "@types/semver": "^7.3.12", + "@typescript-eslint/scope-manager": "5.62.0", + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/typescript-estree": "5.62.0", + "eslint-scope": "^5.1.1", + "semver": "^7.3.7" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.62.0.tgz", + "integrity": "sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.62.0", + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@ungap/structured-clone": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", + "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", + "dev": true + }, + "node_modules/acorn": { + "version": "8.11.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.2.tgz", + "integrity": "sha512-nc0Axzp/0FILLEVsm4fNwLCwMttvhEI263QtVPQcbpfZZ3ts0hLsZGOpE6czNlid7CJ9MlyH8reXkpsf3YUY4w==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/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, + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-escapes/node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/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 + }, + "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" + } + }, + "node_modules/arrify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", + "integrity": "sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/awaitqueue": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/awaitqueue/-/awaitqueue-3.0.2.tgz", + "integrity": "sha512-AVAtRwmf0DNSesMdyanFKKejTrOnjdKtz5LIDQFu2OTUgXvB/CRTYMrkPAF/2GCF9XBtYVxSwxDORlD41S+RyQ==", + "dependencies": { + "debug": "^4.3.4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/babel-jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz", + "integrity": "sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==", + "dev": true, + "dependencies": { + "@jest/transform": "^29.7.0", + "@types/babel__core": "^7.1.14", + "babel-plugin-istanbul": "^6.1.1", + "babel-preset-jest": "^29.6.3", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.8.0" + } + }, + "node_modules/babel-plugin-istanbul": { + "version": "6.1.1", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-instrument": "^5.0.4", + "test-exclude": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-plugin-jest-hoist": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz", + "integrity": "sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==", + "dev": true, + "dependencies": { + "@babel/template": "^7.3.3", + "@babel/types": "^7.3.3", + "@types/babel__core": "^7.1.14", + "@types/babel__traverse": "^7.0.6" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/babel-preset-current-node-syntax": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-bigint": "^7.8.3", + "@babel/plugin-syntax-class-properties": "^7.8.3", + "@babel/plugin-syntax-import-meta": "^7.8.3", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.8.3", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.8.3", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-top-level-await": "^7.8.3" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/babel-preset-jest": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz", + "integrity": "sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==", + "dev": true, + "dependencies": { + "babel-plugin-jest-hoist": "^29.6.3", + "babel-preset-current-node-syntax": "^1.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "dev": true, + "license": "MIT" + }, + "node_modules/big-integer": { + "version": "1.6.52", + "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.52.tgz", + "integrity": "sha512-QxD8cf2eVqJOOz63z6JIN9BzvVs/dlySa5HGSBH5xtR8dPteIRQnBxxKqkNTiT6jbDTF6jAfrd4oMcND9RGbQg==", + "dev": true, + "engines": { + "node": ">=0.6" + } + }, + "node_modules/bplist-parser": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/bplist-parser/-/bplist-parser-0.2.0.tgz", + "integrity": "sha512-z0M+byMThzQmD9NILRniCUXYsYpjwnlO8N5uCFaCqIOpqRsJCrQL9NK3JsD67CN5a08nF5oIL2bD6loTdHOuKw==", + "dev": true, + "dependencies": { + "big-integer": "^1.6.44" + }, + "engines": { + "node": ">= 5.10.0" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.21.4", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + } + ], + "license": "MIT", + "dependencies": { + "caniuse-lite": "^1.0.30001400", + "electron-to-chromium": "^1.4.251", + "node-releases": "^2.0.6", + "update-browserslist-db": "^1.0.9" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/bs-logger": { + "version": "0.2.6", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-json-stable-stringify": "2.x" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/bser": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", + "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", + "dev": true, + "dependencies": { + "node-int64": "^0.4.0" + } + }, + "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 + }, + "node_modules/bundle-name": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-3.0.0.tgz", + "integrity": "sha512-PKA4BeSvBpQKQ8iPOGCSiell+N8P+Tf1DlwqmYhpe2gAhKPHn8EYOxVT+ShuGmhg8lN8XiSlS80yiExKXrURlw==", + "dev": true, + "dependencies": { + "run-applescript": "^5.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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, + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase": { + "version": "5.3.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase-keys": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-8.0.2.tgz", + "integrity": "sha512-qMKdlOfsjlezMqxkUGGMaWWs17i2HoL15tM+wtx8ld4nLrUwU58TFdvyGOz/piNP842KeO8yXvggVQSdQ828NA==", + "dev": true, + "dependencies": { + "camelcase": "^7.0.0", + "map-obj": "^4.3.0", + "quick-lru": "^6.1.1", + "type-fest": "^2.13.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/camelcase-keys/node_modules/camelcase": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-7.0.1.tgz", + "integrity": "sha512-xlx1yCK2Oc1APsPXDL2LdlNP6+uu8OCDdhOBSVT279M/S+y75O30C2VuD8T2ogdePBBl7PfPF4504tnLgX3zfw==", + "dev": true, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/camelcase-keys/node_modules/type-fest": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz", + "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==", + "dev": true, + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001431", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/chalk": { + "version": "4.1.2", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chalk/node_modules/has-flag": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/chalk/node_modules/supports-color": { + "version": "7.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/char-regex": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", + "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/ci-info": { + "version": "3.6.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/cjs-module-lexer": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.3.tgz", + "integrity": "sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ==", + "dev": true + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", + "dev": true, + "engines": { + "iojs": ">= 1.0.0", + "node": ">= 0.12.0" + } + }, + "node_modules/collect-v8-coverage": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz", + "integrity": "sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==", + "dev": true + }, + "node_modules/color-convert": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "dev": true, + "license": "MIT" + }, + "node_modules/concat-map": { + "version": "0.0.1", + "dev": true, + "license": "MIT" + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/create-jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/create-jest/-/create-jest-29.7.0.tgz", + "integrity": "sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "jest-config": "^29.7.0", + "jest-util": "^29.7.0", + "prompts": "^2.0.1" + }, + "bin": { + "create-jest": "bin/create-jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/crypto-random-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-4.0.0.tgz", + "integrity": "sha512-x8dy3RnvYdlUcPOjkEHqozhiwzKNSq7GcPuXFbnyMOCHxX8V3OgIg/pYuabl2sbUPfIJaeAQB7PMOK8DFIdoRA==", + "dev": true, + "dependencies": { + "type-fest": "^1.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/crypto-random-string/node_modules/type-fest": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz", + "integrity": "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/debug": { + "version": "4.3.4", + "license": "MIT", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decamelize": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-6.0.0.tgz", + "integrity": "sha512-Fv96DCsdOgB6mdGl67MT5JaTNKRzrzill5OH5s8bjYJXVlcXyPYGyPsUkWyGV5p1TXI5esYIYMMeDJL0hEIwaA==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/decamelize-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/decamelize-keys/-/decamelize-keys-1.1.1.tgz", + "integrity": "sha512-WiPxgEirIV0/eIOMcnFBA3/IJZAZqKnwAwWyvvdi4lsr1WCN22nhdf/3db3DoZcUjTV2SqfzIwNyp6y2xs3nmg==", + "dev": true, + "dependencies": { + "decamelize": "^1.1.0", + "map-obj": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/decamelize-keys/node_modules/decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/decamelize-keys/node_modules/map-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", + "integrity": "sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/dedent": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.5.1.tgz", + "integrity": "sha512-+LxW+KLWxu3HW3M2w2ympwtqPrqYRzU8fqi6Fhd18fBALe15blJPI/I4+UHveMVG6lJqB4JNd4UG0S5cnVHwIg==", + "dev": true, + "peerDependencies": { + "babel-plugin-macros": "^3.1.0" + }, + "peerDependenciesMeta": { + "babel-plugin-macros": { + "optional": true + } + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "dev": true, + "license": "MIT" + }, + "node_modules/deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/default-browser": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-4.0.0.tgz", + "integrity": "sha512-wX5pXO1+BrhMkSbROFsyxUm0i/cJEScyNhA4PPxc41ICuv05ZZB/MX28s8aZx6xjmatvebIapF6hLEKEcpneUA==", + "dev": true, + "dependencies": { + "bundle-name": "^3.0.0", + "default-browser-id": "^3.0.0", + "execa": "^7.1.1", + "titleize": "^3.0.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/default-browser-id": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-3.0.0.tgz", + "integrity": "sha512-OZ1y3y0SqSICtE8DE4S8YOE9UZOJ8wO16fKWVP5J1Qz42kV9jcnMVFrEE/noXb/ss3Q4pZIH79kxofzyNNtUNA==", + "dev": true, + "dependencies": { + "bplist-parser": "^0.2.0", + "untildify": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/default-browser/node_modules/execa": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-7.2.0.tgz", + "integrity": "sha512-UduyVP7TLB5IcAQl+OzLyLcS/l32W/GLg+AhHJ+ow40FOk2U3SAllPwR44v4vmdFwIWqpdwxxpQbF1n5ta9seA==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.1", + "human-signals": "^4.3.0", + "is-stream": "^3.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^5.1.0", + "onetime": "^6.0.0", + "signal-exit": "^3.0.7", + "strip-final-newline": "^3.0.0" + }, + "engines": { + "node": "^14.18.0 || ^16.14.0 || >=18.0.0" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/default-browser/node_modules/human-signals": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-4.3.1.tgz", + "integrity": "sha512-nZXjEF2nbo7lIw3mgYjItAfgQXog3OjJogSbKa2CQIIvSGWcKgeJnQlNXip6NglNzYH45nSRiEVimMvYL8DDqQ==", + "dev": true, + "engines": { + "node": ">=14.18.0" + } + }, + "node_modules/default-browser/node_modules/is-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", + "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/default-browser/node_modules/mimic-fn": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", + "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/default-browser/node_modules/npm-run-path": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.2.0.tgz", + "integrity": "sha512-W4/tgAXFqFA0iL7fk0+uQ3g7wkL8xJmx3XdK0VGb4cHW//eZTtKGvFBBoRKVTpY7n6ze4NL9ly7rgXcHufqXKg==", + "dev": true, + "dependencies": { + "path-key": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/default-browser/node_modules/onetime": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", + "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", + "dev": true, + "dependencies": { + "mimic-fn": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/default-browser/node_modules/path-key": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/default-browser/node_modules/strip-final-newline": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", + "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/define-lazy-prop": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz", + "integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/detect-newline": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", + "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/diff-sequences": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", + "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", + "dev": true, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "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==", + "dev": true, + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/doctrine": { + "version": "3.0.0", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.4.284", + "dev": true, + "license": "ISC" + }, + "node_modules/emittery": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz", + "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sindresorhus/emittery?sponsor=1" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/error-ex": { + "version": "1.3.2", + "dev": true, + "license": "MIT", + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/escalade": { + "version": "3.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "8.56.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.56.0.tgz", + "integrity": "sha512-Go19xM6T9puCOWntie1/P997aXxFsOi37JIHRWI514Hc6ZnaHGKY9xFhrU65RT6CcBEzZoGG1e6Nq+DT04ZtZQ==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.4", + "@eslint/js": "8.56.0", + "@humanwhocodes/config-array": "^0.11.13", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "@ungap/structured-clone": "^1.2.0", + "ajv": "^6.12.4", + "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.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", + "esquery": "^1.4.2", + "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.2", + "globals": "^13.19.0", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "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.3", + "strip-ansi": "^6.0.1", + "text-table": "^0.2.0" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-config-prettier": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.1.0.tgz", + "integrity": "sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==", + "dev": true, + "bin": { + "eslint-config-prettier": "bin/cli.js" + }, + "peerDependencies": { + "eslint": ">=7.0.0" + } + }, + "node_modules/eslint-plugin-jest": { + "version": "27.6.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-27.6.1.tgz", + "integrity": "sha512-WEYkyVXD9NlmFBKvrkmzrC+C9yZoz5pAml2hO19PlS3spJtoiwj4p2u8spd/7zx5IvRsZsCmsoImaAvBB9X93Q==", + "dev": true, + "dependencies": { + "@typescript-eslint/utils": "^5.10.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@typescript-eslint/eslint-plugin": "^5.0.0 || ^6.0.0", + "eslint": "^7.0.0 || ^8.0.0", + "jest": "*" + }, + "peerDependenciesMeta": { + "@typescript-eslint/eslint-plugin": { + "optional": true + }, + "jest": { + "optional": true + } + } + }, + "node_modules/eslint-plugin-prettier": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.1.2.tgz", + "integrity": "sha512-dhlpWc9vOwohcWmClFcA+HjlvUpuyynYs0Rf+L/P6/0iQE6vlHW9l5bkfzN62/Stm9fbq8ku46qzde76T1xlSg==", + "dev": true, + "dependencies": { + "prettier-linter-helpers": "^1.0.0", + "synckit": "^0.8.6" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint-plugin-prettier" + }, + "peerDependencies": { + "@types/eslint": ">=8.0.0", + "eslint": ">=8.0.0", + "eslint-config-prettier": "*", + "prettier": ">=3.0.0" + }, + "peerDependenciesMeta": { + "@types/eslint": { + "optional": true + }, + "eslint-config-prettier": { + "optional": true + } + } + }, + "node_modules/eslint-scope": { + "version": "5.1.1", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "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, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/eslint-scope": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/espree": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "dev": true, + "dependencies": { + "acorn": "^8.9.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "dev": true, + "license": "BSD-2-Clause", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/esquery": { + "version": "1.5.0", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esquery/node_modules/estraverse": { + "version": "5.3.0", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esrecurse/node_modules/estraverse": { + "version": "5.3.0", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "4.3.0", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/event-target-shim": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-6.0.2.tgz", + "integrity": "sha512-8q3LsZjRezbFZ2PN+uP+Q7pnHUMmAOziU2vA2OwoFaKIXxlxl38IylhSSgUorWu/rf4er67w0ikBqjBFk/pomA==", + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + } + }, + "node_modules/events": { + "version": "3.3.0", + "license": "MIT", + "engines": { + "node": ">=0.8.x" + } + }, + "node_modules/execa": { + "version": "5.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/expect": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/expect/-/expect-29.7.0.tgz", + "integrity": "sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==", + "dev": true, + "dependencies": { + "@jest/expect-utils": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/fake-mediastreamtrack": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/fake-mediastreamtrack/-/fake-mediastreamtrack-1.2.0.tgz", + "integrity": "sha512-AxHtlEmka1sqNoe3Ej1H1hJc9gjjO/6vCbCPm4D4QeEXvzhjYumA+iZ7wOi2WrmkAhGElHhBgWoNgJhFccectA==", + "dependencies": { + "event-target-shim": "^6.0.2", + "uuid": "^9.0.0" + } + }, + "node_modules/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 + }, + "node_modules/fast-diff": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz", + "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==", + "dev": true + }, + "node_modules/fast-glob": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.0.tgz", + "integrity": "sha512-ChDuvbOypPuNjO8yIDf36x7BlZX1smcUMTTcyoIjycexOxd6DFsKsg21qVBzEmr3G7fUKIRy2/psii+CIUt7FA==", + "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" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "dev": true, + "license": "MIT" + }, + "node_modules/fastq": { + "version": "1.13.0", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/fb-watchman": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", + "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", + "dev": true, + "dependencies": { + "bser": "2.1.1" + } + }, + "node_modules/file-entry-cache": { + "version": "6.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/file-type": { + "version": "18.7.0", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-18.7.0.tgz", + "integrity": "sha512-ihHtXRzXEziMrQ56VSgU7wkxh55iNchFkosu7Y9/S+tXHdKyrGjVK0ujbqNnsxzea+78MaLhN6PGmfYSAv1ACw==", + "dev": true, + "dependencies": { + "readable-web-to-node-stream": "^3.0.2", + "strtok3": "^7.0.0", + "token-types": "^5.0.1" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sindresorhus/file-type?sponsor=1" + } + }, + "node_modules/fill-range": { + "version": "7.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "3.0.4", + "dev": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.1.0", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/flatted": { + "version": "3.2.7", + "dev": true, + "license": "ISC" + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "dev": true, + "license": "ISC" + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.1", + "dev": true, + "license": "MIT" + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-package-type": { + "version": "0.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/get-stdin": { + "version": "9.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-stream": { + "version": "6.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/glob": { + "version": "7.2.3", + "dev": true, + "license": "ISC", + "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" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/globals": { + "version": "13.23.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.23.0.tgz", + "integrity": "sha512-XAmF0RjlrjY23MA51q3HltdlGxUpXPvg0GioKiD9X6HD28iMjo2dKC8Vqwm7lne4GNr78+RHTfliktR6ZH09wA==", + "dev": true, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "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==", + "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/graceful-fs": { + "version": "4.2.10", + "dev": true, + "license": "ISC" + }, + "node_modules/graphemer": { + "version": "1.4.0", + "dev": true, + "license": "MIT" + }, + "node_modules/h264-profile-level-id": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/h264-profile-level-id/-/h264-profile-level-id-2.0.0.tgz", + "integrity": "sha512-X4CLryVbVA0CtjTExS4G5U1gb2Z4wa32AF8ukVmFuLdw2JRq2aHisor7SY5SYTUUrUSqq0KdPIO18sql6IWIQw==", + "dependencies": { + "@types/debug": "^4.1.12", + "debug": "^4.3.4" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mediasoup" + } + }, + "node_modules/hard-rejection": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/hard-rejection/-/hard-rejection-2.1.0.tgz", + "integrity": "sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/has": { + "version": "1.0.3", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.1" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/has-flag": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/hosted-git-info": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-5.2.1.tgz", + "integrity": "sha512-xIcQYMnhcx2Nr4JTjsFmwwnr9vldugPy9uVm0o87bjqqWMv9GaqsTeT+i99wTl0mk1uLxJtHxLb8kymqTENQsw==", + "dev": true, + "dependencies": { + "lru-cache": "^7.5.1" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/hosted-git-info/node_modules/lru-cache": { + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", + "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true + }, + "node_modules/human-signals": { + "version": "2.1.0", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "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/ignore": { + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", + "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", + "dev": true, + "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==", + "dev": true, + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/import-local": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz", + "integrity": "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==", + "dev": true, + "dependencies": { + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" + }, + "bin": { + "import-local-fixture": "fixtures/cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/indent-string": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-5.0.0.tgz", + "integrity": "sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "dev": true, + "license": "ISC", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "dev": true, + "license": "ISC" + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "dev": true, + "license": "MIT" + }, + "node_modules/is-core-module": { + "version": "2.13.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.0.tgz", + "integrity": "sha512-Z7dk6Qo8pOCp3l4tsX2C5ZVas4V+UxwQodwZhLopL91TX8UyyHEXafPcyoeeWuLrwzHcr3igO78wNLwHJHsMCQ==", + "dev": true, + "dependencies": { + "has": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-docker": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz", + "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==", + "dev": true, + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-generator-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", + "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-inside-container": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz", + "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==", + "dev": true, + "dependencies": { + "is-docker": "^3.0.0" + }, + "bin": { + "is-inside-container": "cli.js" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-path-inside": { + "version": "3.0.3", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-plain-obj": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", + "integrity": "sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-stream": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "dev": true, + "dependencies": { + "is-docker": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-wsl/node_modules/is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "dev": true, + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "dev": true, + "license": "ISC" + }, + "node_modules/istanbul-lib-coverage": { + "version": "3.2.0", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-instrument": { + "version": "5.2.1", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@babel/core": "^7.12.3", + "@babel/parser": "^7.14.7", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-instrument/node_modules/semver": { + "version": "6.3.0", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/istanbul-lib-report": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", + "dev": true, + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^4.0.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-report/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, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-report/node_modules/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, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-source-maps": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", + "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", + "dev": true, + "dependencies": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-reports": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.6.tgz", + "integrity": "sha512-TLgnMkKg3iTDsQ9PbPTdpfAK2DzjF9mqUG7RMgcQl8oFjad8ob4laGxv5XV5U9MAfx8D6tSJiUyuAwzLicaxlg==", + "dev": true, + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz", + "integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==", + "dev": true, + "dependencies": { + "@jest/core": "^29.7.0", + "@jest/types": "^29.6.3", + "import-local": "^3.0.2", + "jest-cli": "^29.7.0" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/jest-changed-files": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.7.0.tgz", + "integrity": "sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==", + "dev": true, + "dependencies": { + "execa": "^5.0.0", + "jest-util": "^29.7.0", + "p-limit": "^3.1.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-circus": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.7.0.tgz", + "integrity": "sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==", + "dev": true, + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/expect": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "co": "^4.6.0", + "dedent": "^1.0.0", + "is-generator-fn": "^2.0.0", + "jest-each": "^29.7.0", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "p-limit": "^3.1.0", + "pretty-format": "^29.7.0", + "pure-rand": "^6.0.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-cli": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.7.0.tgz", + "integrity": "sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==", + "dev": true, + "dependencies": { + "@jest/core": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "create-jest": "^29.7.0", + "exit": "^0.1.2", + "import-local": "^3.0.2", + "jest-config": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "yargs": "^17.3.1" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/jest-config": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.7.0.tgz", + "integrity": "sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==", + "dev": true, + "dependencies": { + "@babel/core": "^7.11.6", + "@jest/test-sequencer": "^29.7.0", + "@jest/types": "^29.6.3", + "babel-jest": "^29.7.0", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "deepmerge": "^4.2.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-circus": "^29.7.0", + "jest-environment-node": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-runner": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "micromatch": "^4.0.4", + "parse-json": "^5.2.0", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@types/node": "*", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "ts-node": { + "optional": true + } + } + }, + "node_modules/jest-diff": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz", + "integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "diff-sequences": "^29.6.3", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-docblock": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.7.0.tgz", + "integrity": "sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==", + "dev": true, + "dependencies": { + "detect-newline": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-each": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.7.0.tgz", + "integrity": "sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "jest-get-type": "^29.6.3", + "jest-util": "^29.7.0", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-environment-node": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.7.0.tgz", + "integrity": "sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==", + "dev": true, + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-get-type": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", + "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", + "dev": true, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-haste-map": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz", + "integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@types/graceful-fs": "^4.1.3", + "@types/node": "*", + "anymatch": "^3.0.3", + "fb-watchman": "^2.0.0", + "graceful-fs": "^4.2.9", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", + "jest-worker": "^29.7.0", + "micromatch": "^4.0.4", + "walker": "^1.0.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "optionalDependencies": { + "fsevents": "^2.3.2" + } + }, + "node_modules/jest-leak-detector": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz", + "integrity": "sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==", + "dev": true, + "dependencies": { + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-matcher-utils": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz", + "integrity": "sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "jest-diff": "^29.7.0", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-message-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.7.0.tgz", + "integrity": "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.12.13", + "@jest/types": "^29.6.3", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-mock": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.7.0.tgz", + "integrity": "sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-pnp-resolver": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", + "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", + "dev": true, + "engines": { + "node": ">=6" + }, + "peerDependencies": { + "jest-resolve": "*" + }, + "peerDependenciesMeta": { + "jest-resolve": { + "optional": true + } + } + }, + "node_modules/jest-regex-util": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz", + "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==", + "dev": true, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-resolve": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.7.0.tgz", + "integrity": "sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-pnp-resolver": "^1.2.2", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "resolve": "^1.20.0", + "resolve.exports": "^2.0.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-resolve-dependencies": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.7.0.tgz", + "integrity": "sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==", + "dev": true, + "dependencies": { + "jest-regex-util": "^29.6.3", + "jest-snapshot": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-runner": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.7.0.tgz", + "integrity": "sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==", + "dev": true, + "dependencies": { + "@jest/console": "^29.7.0", + "@jest/environment": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "emittery": "^0.13.1", + "graceful-fs": "^4.2.9", + "jest-docblock": "^29.7.0", + "jest-environment-node": "^29.7.0", + "jest-haste-map": "^29.7.0", + "jest-leak-detector": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-resolve": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-util": "^29.7.0", + "jest-watcher": "^29.7.0", + "jest-worker": "^29.7.0", + "p-limit": "^3.1.0", + "source-map-support": "0.5.13" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-runtime": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.7.0.tgz", + "integrity": "sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==", + "dev": true, + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", + "@jest/globals": "^29.7.0", + "@jest/source-map": "^29.6.3", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "cjs-module-lexer": "^1.0.0", + "collect-v8-coverage": "^1.0.0", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-mock": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "slash": "^3.0.0", + "strip-bom": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-snapshot": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.7.0.tgz", + "integrity": "sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==", + "dev": true, + "dependencies": { + "@babel/core": "^7.11.6", + "@babel/generator": "^7.7.2", + "@babel/plugin-syntax-jsx": "^7.7.2", + "@babel/plugin-syntax-typescript": "^7.7.2", + "@babel/types": "^7.3.3", + "@jest/expect-utils": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "babel-preset-current-node-syntax": "^1.0.0", + "chalk": "^4.0.0", + "expect": "^29.7.0", + "graceful-fs": "^4.2.9", + "jest-diff": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "natural-compare": "^1.4.0", + "pretty-format": "^29.7.0", + "semver": "^7.5.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", + "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-validate": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz", + "integrity": "sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "camelcase": "^6.2.0", + "chalk": "^4.0.0", + "jest-get-type": "^29.6.3", + "leven": "^3.1.0", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-validate/node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/jest-watcher": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.7.0.tgz", + "integrity": "sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==", + "dev": true, + "dependencies": { + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "emittery": "^0.13.1", + "jest-util": "^29.7.0", + "string-length": "^4.0.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-worker": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", + "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", + "dev": true, + "dependencies": { + "@types/node": "*", + "jest-util": "^29.7.0", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-worker/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, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-worker/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "dev": true, + "license": "MIT" + }, + "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, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsesc": { + "version": "2.5.2", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "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 + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "dev": true, + "license": "MIT" + }, + "node_modules/json5": { + "version": "2.2.3", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/leven": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "dev": true, + "license": "MIT" + }, + "node_modules/locate-path": { + "version": "6.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash.memoize": { + "version": "4.1.2", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "dev": true, + "license": "MIT" + }, + "node_modules/lru-cache": { + "version": "6.0.0", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/make-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", + "dev": true, + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/make-error": { + "version": "1.3.6", + "dev": true, + "license": "ISC" + }, + "node_modules/makeerror": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", + "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", + "dev": true, + "dependencies": { + "tmpl": "1.0.5" + } + }, + "node_modules/map-obj": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.3.0.tgz", + "integrity": "sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/meow": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/meow/-/meow-11.0.0.tgz", + "integrity": "sha512-Cl0yeeIrko6d94KpUo1M+0X1sB14ikoaqlIGuTH1fW4I+E3+YljL54/hb/BWmVfrV9tTV9zU04+xjw08Fh2WkA==", + "dev": true, + "dependencies": { + "@types/minimist": "^1.2.2", + "camelcase-keys": "^8.0.2", + "decamelize": "^6.0.0", + "decamelize-keys": "^1.1.0", + "hard-rejection": "^2.1.0", + "minimist-options": "4.1.0", + "normalize-package-data": "^4.0.1", + "read-pkg-up": "^9.1.0", + "redent": "^4.0.0", + "trim-newlines": "^4.0.2", + "type-fest": "^3.1.0", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/meow/node_modules/type-fest": { + "version": "3.13.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-3.13.1.tgz", + "integrity": "sha512-tLq3bSNx+xSpwvAJnzrK0Ep5CLNWjvFTOp71URMaAEWBfRb9nnJiBoUe0tF8bI4ZFO3omgBR6NvnbzVUT3Ly4g==", + "dev": true, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "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==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.5", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/min-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", + "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist-options": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/minimist-options/-/minimist-options-4.1.0.tgz", + "integrity": "sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==", + "dev": true, + "dependencies": { + "arrify": "^1.0.1", + "is-plain-obj": "^1.1.0", + "kind-of": "^6.0.3" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/ms": { + "version": "2.1.2", + "license": "MIT" + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "dev": true, + "license": "MIT" + }, + "node_modules/node-int64": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", + "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", + "dev": true + }, + "node_modules/node-releases": { + "version": "2.0.6", + "dev": true, + "license": "MIT" + }, + "node_modules/normalize-package-data": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-4.0.1.tgz", + "integrity": "sha512-EBk5QKKuocMJhB3BILuKhmaPjI8vNRSpIfO9woLC6NyHVkKKdVEdAO1mrT0ZfxNR1lKwCcTkuZfmGIFdizZ8Pg==", + "dev": true, + "dependencies": { + "hosted-git-info": "^5.0.0", + "is-core-module": "^2.8.1", + "semver": "^7.3.5", + "validate-npm-package-license": "^3.0.4" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm-run-path": { + "version": "4.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "dev": true, + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/open": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/open/-/open-9.1.0.tgz", + "integrity": "sha512-OS+QTnw1/4vrf+9hh1jc1jnYjzSG4ttTBB8UxOwAnInG3Uo4ssetzC1ihqaIHjLJnA5GGlRl6QlZXOTQhRBUvg==", + "dev": true, + "dependencies": { + "default-browser": "^4.0.0", + "define-lazy-prop": "^3.0.0", + "is-inside-container": "^1.0.0", + "is-wsl": "^2.2.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/open-cli": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/open-cli/-/open-cli-7.2.0.tgz", + "integrity": "sha512-1ANJc8oJ92FiaNZ0o2Hw4WBvDJoXs1P74aFMtpAvlbkIPV4uPcQvDz7V6kMOrsZkmB4tglrHVMlLQaafuUuxXg==", + "dev": true, + "dependencies": { + "file-type": "^18.2.1", + "get-stdin": "^9.0.0", + "meow": "^11.0.0", + "open": "^9.0.0", + "tempy": "^3.0.0" + }, + "bin": { + "open-cli": "cli.js" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/optionator": { + "version": "0.9.3", + "dev": true, + "license": "MIT", + "dependencies": { + "@aashutoshrathi/word-wrap": "^1.2.3", + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "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, + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-json": { + "version": "5.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true + }, + "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/peek-readable": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/peek-readable/-/peek-readable-5.0.0.tgz", + "integrity": "sha512-YtCKvLUOvwtMGmrniQPdO7MwPjgkFBtFIrmfSbYmYuq3tKDV/mcfAhBth1+C3ru7uXIZasc/pHnb+YDYNkkj4A==", + "dev": true, + "engines": { + "node": ">=14.16" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, + "node_modules/picocolors": { + "version": "1.0.0", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "2.3.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pirates": { + "version": "4.0.5", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pkg-dir/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prettier": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.1.1.tgz", + "integrity": "sha512-22UbSzg8luF4UuZtzgiUOfcGM8s4tjBv6dJRT7j275NXsy2jb4aJa4NNveul5x4eqlF1wuhuR2RElK71RvmVaw==", + "dev": true, + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/prettier-linter-helpers": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", + "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", + "dev": true, + "dependencies": { + "fast-diff": "^1.1.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/prompts": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", + "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", + "dev": true, + "dependencies": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/pure-rand": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.0.3.tgz", + "integrity": "sha512-KddyFewCsO0j3+np81IQ+SweXLDnDQTs5s67BOnrYmYe/yNmUhttQyGsYzy8yUnoljGAQ9sl38YB4vH8ur7Y+w==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/dubzzz" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fast-check" + } + ] + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "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/quick-lru": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-6.1.2.tgz", + "integrity": "sha512-AAFUA5O1d83pIHEhJwWCq/RQcRukCkn/NSm2QsTEMle5f2hP0ChI2+3Xb051PZCkLryI/Ir1MVKviT2FIloaTQ==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/react-is": { + "version": "18.2.0", + "dev": true, + "license": "MIT" + }, + "node_modules/read-pkg": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-7.1.0.tgz", + "integrity": "sha512-5iOehe+WF75IccPc30bWTbpdDQLOCc3Uu8bi3Dte3Eueij81yx1Mrufk8qBx/YAbR4uL1FdUr+7BKXDwEtisXg==", + "dev": true, + "dependencies": { + "@types/normalize-package-data": "^2.4.1", + "normalize-package-data": "^3.0.2", + "parse-json": "^5.2.0", + "type-fest": "^2.0.0" + }, + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/read-pkg-up": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-9.1.0.tgz", + "integrity": "sha512-vaMRR1AC1nrd5CQM0PhlRsO5oc2AAigqr7cCrZ/MW/Rsaflz4RlgzkpL4qoU/z1F6wrbd85iFv1OQj/y5RdGvg==", + "dev": true, + "dependencies": { + "find-up": "^6.3.0", + "read-pkg": "^7.1.0", + "type-fest": "^2.5.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/read-pkg-up/node_modules/find-up": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-6.3.0.tgz", + "integrity": "sha512-v2ZsoEuVHYy8ZIlYqwPe/39Cy+cFDzp4dXPaxNvkEuouymu+2Jbz0PxpKarJHYJTmv2HWT3O382qY8l4jMWthw==", + "dev": true, + "dependencies": { + "locate-path": "^7.1.0", + "path-exists": "^5.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/read-pkg-up/node_modules/locate-path": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-7.2.0.tgz", + "integrity": "sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA==", + "dev": true, + "dependencies": { + "p-locate": "^6.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/read-pkg-up/node_modules/p-limit": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-4.0.0.tgz", + "integrity": "sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^1.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/read-pkg-up/node_modules/p-locate": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-6.0.0.tgz", + "integrity": "sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==", + "dev": true, + "dependencies": { + "p-limit": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/read-pkg-up/node_modules/path-exists": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-5.0.0.tgz", + "integrity": "sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + } + }, + "node_modules/read-pkg-up/node_modules/type-fest": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz", + "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==", + "dev": true, + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/read-pkg-up/node_modules/yocto-queue": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.0.0.tgz", + "integrity": "sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==", + "dev": true, + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/read-pkg/node_modules/hosted-git-info": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz", + "integrity": "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/read-pkg/node_modules/normalize-package-data": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.3.tgz", + "integrity": "sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA==", + "dev": true, + "dependencies": { + "hosted-git-info": "^4.0.1", + "is-core-module": "^2.5.0", + "semver": "^7.3.4", + "validate-npm-package-license": "^3.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/read-pkg/node_modules/type-fest": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz", + "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==", + "dev": true, + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/readable-web-to-node-stream": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/readable-web-to-node-stream/-/readable-web-to-node-stream-3.0.2.tgz", + "integrity": "sha512-ePeK6cc1EcKLEhJFt/AebMCLL+GgSKhuygrZ/GLaKZYEecIgIECf4UaUuaByiGtzckwR4ain9VzUh95T1exYGw==", + "dev": true, + "dependencies": { + "readable-stream": "^3.6.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, + "node_modules/redent": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-4.0.0.tgz", + "integrity": "sha512-tYkDkVVtYkSVhuQ4zBgfvciymHaeuel+zFKXShfDnFP5SyVEP7qo70Rf1jTOTCx3vGNAbnEi/xFkcfQVMIBWag==", + "dev": true, + "dependencies": { + "indent-string": "^5.0.0", + "strip-indent": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve": { + "version": "1.22.4", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.4.tgz", + "integrity": "sha512-PXNdCiPqDqeUou+w1C2eTQbNfxKSuMxqTCuvlmmMsk1NWHL5fRrhY6Pl0qEYYc6+QqGClco1Qj8XnjPego4wfg==", + "dev": true, + "dependencies": { + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-cwd": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", + "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", + "dev": true, + "dependencies": { + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-cwd/node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "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, + "engines": { + "node": ">=4" + } + }, + "node_modules/resolve.exports": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.2.tgz", + "integrity": "sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rimraf": { + "version": "3.0.2", + "dev": true, + "license": "ISC", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/run-applescript": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-5.0.0.tgz", + "integrity": "sha512-XcT5rBksx1QdIhlFOCtgZkB99ZEouFZ1E2Kc2LHqNW13U3/74YGdkQRmThTwxy4QIyookibDKYZOPqX//6BlAg==", + "dev": true, + "dependencies": { + "execa": "^5.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "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" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "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==", + "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/sdp-transform": { + "version": "2.14.1", + "license": "MIT", + "bin": { + "sdp-verify": "checker.js" + } + }, + "node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "dev": true, + "license": "ISC" + }, + "node_modules/sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", + "dev": true + }, + "node_modules/slash": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.13", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", + "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", + "dev": true, + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/spdx-correct": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", + "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", + "dev": true, + "dependencies": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-exceptions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", + "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", + "dev": true + }, + "node_modules/spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "dev": true, + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-license-ids": { + "version": "3.0.16", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.16.tgz", + "integrity": "sha512-eWN+LnM3GR6gPu35WxNgbGl8rmY1AEmoMDvL/QD6zYmPWgywxWqJWNdLGT+ke8dKNWrcYgYjPpG5gbTfghP8rw==", + "dev": true + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/stack-utils": { + "version": "2.0.6", + "dev": true, + "license": "MIT", + "dependencies": { + "escape-string-regexp": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/stack-utils/node_modules/escape-string-regexp": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string-length": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", + "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", + "dev": true, + "dependencies": { + "char-regex": "^1.0.2", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-bom": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-final-newline": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/strip-indent": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-4.0.0.tgz", + "integrity": "sha512-mnVSV2l+Zv6BLpSD/8V87CW/y9EmmbYzGCIavsnsI6/nwn26DwffM/yztm30Z/I2DY9wdS3vXVCMnHDgZaVNoA==", + "dev": true, + "dependencies": { + "min-indent": "^1.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/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, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/strtok3": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/strtok3/-/strtok3-7.0.0.tgz", + "integrity": "sha512-pQ+V+nYQdC5H3Q7qBZAz/MO6lwGhoC2gOAjuouGf/VO0m7vQRh8QNMl2Uf6SwAtzZ9bOw3UIeBukEGNJl5dtXQ==", + "dev": true, + "dependencies": { + "@tokenizer/token": "^0.3.0", + "peek-readable": "^5.0.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, + "node_modules/supports-color": { + "version": "9.4.0", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/synckit": { + "version": "0.8.8", + "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.8.8.tgz", + "integrity": "sha512-HwOKAP7Wc5aRGYdKH+dw0PRRpbO841v2DENBtjnR5HFWoiNByAl7vrx3p0G/rCyYXQsrxqtX48TImFtPcIHSpQ==", + "dev": true, + "dependencies": { + "@pkgr/core": "^0.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/unts" + } + }, + "node_modules/synckit/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "dev": true + }, + "node_modules/temp-dir": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-3.0.0.tgz", + "integrity": "sha512-nHc6S/bwIilKHNRgK/3jlhDoIHcp45YgyiwcAk46Tr0LfEqGBVpmiAyuiuxeVE44m3mXnEeVhaipLOEWmH+Njw==", + "dev": true, + "engines": { + "node": ">=14.16" + } + }, + "node_modules/tempy": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/tempy/-/tempy-3.1.0.tgz", + "integrity": "sha512-7jDLIdD2Zp0bDe5r3D2qtkd1QOCacylBuL7oa4udvN6v2pqr4+LcCr67C8DR1zkpaZ8XosF5m1yQSabKAW6f2g==", + "dev": true, + "dependencies": { + "is-stream": "^3.0.0", + "temp-dir": "^3.0.0", + "type-fest": "^2.12.2", + "unique-string": "^3.0.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/tempy/node_modules/is-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", + "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/tempy/node_modules/type-fest": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz", + "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==", + "dev": true, + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/test-exclude": { + "version": "6.0.0", + "dev": true, + "license": "ISC", + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/text-table": { + "version": "0.2.0", + "dev": true, + "license": "MIT" + }, + "node_modules/titleize": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/titleize/-/titleize-3.0.0.tgz", + "integrity": "sha512-KxVu8EYHDPBdUYdKZdKtU2aj2XfEx9AfjXxE/Aj0vT06w2icA09Vus1rh6eSu1y01akYg6BjIK/hxyLJINoMLQ==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/tmpl": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", + "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", + "dev": true + }, + "node_modules/to-fast-properties": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/token-types": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/token-types/-/token-types-5.0.1.tgz", + "integrity": "sha512-Y2fmSnZjQdDb9W4w4r1tswlMHylzWIeOKpx0aZH9BgGtACHhrk3OkT52AzwcuqTRBZtvvnTjDBh8eynMulu8Vg==", + "dev": true, + "dependencies": { + "@tokenizer/token": "^0.3.0", + "ieee754": "^1.2.1" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, + "node_modules/trim-newlines": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-4.1.1.tgz", + "integrity": "sha512-jRKj0n0jXWo6kh62nA5TEh3+4igKDXLvzBJcPpiizP7oOolUrYIxmVBG9TOtHYFHoddUk6YvAkGeGoSVTXfQXQ==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ts-api-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.0.1.tgz", + "integrity": "sha512-lC/RGlPmwdrIBFTX59wwNzqh7aR2otPNPR/5brHZm/XKFYKsfqxihXUe9pU3JI+3vGkl+vyCoNNnPhJn3aLK1A==", + "dev": true, + "engines": { + "node": ">=16.13.0" + }, + "peerDependencies": { + "typescript": ">=4.2.0" + } + }, + "node_modules/ts-jest": { + "version": "29.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "bs-logger": "0.x", + "fast-json-stable-stringify": "2.x", + "jest-util": "^29.0.0", + "json5": "^2.2.3", + "lodash.memoize": "4.x", + "make-error": "1.x", + "semver": "^7.5.3", + "yargs-parser": "^21.0.1" + }, + "bin": { + "ts-jest": "cli.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@babel/core": ">=7.0.0-beta.0 <8", + "@jest/types": "^29.0.0", + "babel-jest": "^29.0.0", + "jest": "^29.0.0", + "typescript": ">=4.3 <6" + }, + "peerDependenciesMeta": { + "@babel/core": { + "optional": true + }, + "@jest/types": { + "optional": true + }, + "babel-jest": { + "optional": true + }, + "esbuild": { + "optional": true + } + } + }, + "node_modules/tslib": { + "version": "1.14.1", + "dev": true, + "license": "0BSD" + }, + "node_modules/tsutils": { + "version": "3.21.0", + "dev": true, + "license": "MIT", + "dependencies": { + "tslib": "^1.8.1" + }, + "engines": { + "node": ">= 6" + }, + "peerDependencies": { + "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" + } + }, + "node_modules/type-check": { + "version": "0.4.0", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "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==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typescript": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz", + "integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/ua-parser-js": { + "version": "1.0.37", + "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-1.0.37.tgz", + "integrity": "sha512-bhTyI94tZofjo+Dn8SN6Zv8nBDvyXTymAdM3LDI/0IboIUwTu1rEhW7v2TfiVsoYWgkQ4kOVqnI8APUFbIQIFQ==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/ua-parser-js" + }, + { + "type": "paypal", + "url": "https://paypal.me/faisalman" + }, + { + "type": "github", + "url": "https://github.com/sponsors/faisalman" + } + ], + "engines": { + "node": "*" + } + }, + "node_modules/unique-string": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-3.0.0.tgz", + "integrity": "sha512-VGXBUVwxKMBUznyffQweQABPRRW1vHZAbadFZud4pLFAqRGvv/96vafgjWFqzourzr8YonlQiPgH0YCJfawoGQ==", + "dev": true, + "dependencies": { + "crypto-random-string": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/untildify": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/untildify/-/untildify-4.0.0.tgz", + "integrity": "sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.0.10", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.1.1", + "picocolors": "^1.0.0" + }, + "bin": { + "browserslist-lint": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true + }, + "node_modules/uuid": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz", + "integrity": "sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/v8-to-istanbul": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.1.0.tgz", + "integrity": "sha512-6z3GW9x8G1gd+JIIgQQQxXuiJtCXeAjp6RaPEPLv62mH3iPHPxV6W3robxtCzNErRo6ZwTmzWhsbNvjyEBKzKA==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.12", + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^1.6.0" + }, + "engines": { + "node": ">=10.12.0" + } + }, + "node_modules/v8-to-istanbul/node_modules/convert-source-map": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", + "dev": true + }, + "node_modules/validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, + "dependencies": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "node_modules/walker": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", + "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", + "dev": true, + "dependencies": { + "makeerror": "1.0.12" + } + }, + "node_modules/which": { + "version": "2.0.2", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "dev": true, + "license": "ISC" + }, + "node_modules/write-file-atomic": { + "version": "4.0.2", + "dev": true, + "license": "ISC", + "dependencies": { + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.7" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "4.0.0", + "dev": true, + "license": "ISC" + }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + }, + "dependencies": { + "@aashutoshrathi/word-wrap": { + "version": "1.2.6", + "dev": true + }, + "@ampproject/remapping": { + "version": "2.2.0", + "dev": true, + "requires": { + "@jridgewell/gen-mapping": "^0.1.0", + "@jridgewell/trace-mapping": "^0.3.9" + } + }, + "@babel/code-frame": { + "version": "7.18.6", + "dev": true, + "requires": { + "@babel/highlight": "^7.18.6" + } + }, + "@babel/compat-data": { + "version": "7.20.1", + "dev": true + }, + "@babel/core": { + "version": "7.20.2", + "dev": true, + "requires": { + "@ampproject/remapping": "^2.1.0", + "@babel/code-frame": "^7.18.6", + "@babel/generator": "^7.20.2", + "@babel/helper-compilation-targets": "^7.20.0", + "@babel/helper-module-transforms": "^7.20.2", + "@babel/helpers": "^7.20.1", + "@babel/parser": "^7.20.2", + "@babel/template": "^7.18.10", + "@babel/traverse": "^7.20.1", + "@babel/types": "^7.20.2", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.1", + "semver": "^6.3.0" + }, + "dependencies": { + "convert-source-map": { + "version": "1.9.0", + "dev": true + }, + "semver": { + "version": "6.3.0", + "dev": true + } + } + }, + "@babel/generator": { + "version": "7.20.4", + "dev": true, + "requires": { + "@babel/types": "^7.20.2", + "@jridgewell/gen-mapping": "^0.3.2", + "jsesc": "^2.5.1" + }, + "dependencies": { + "@jridgewell/gen-mapping": { + "version": "0.3.2", + "dev": true, + "requires": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + } + } + } + }, + "@babel/helper-compilation-targets": { + "version": "7.20.0", + "dev": true, + "requires": { + "@babel/compat-data": "^7.20.0", + "@babel/helper-validator-option": "^7.18.6", + "browserslist": "^4.21.3", + "semver": "^6.3.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "dev": true + } + } + }, + "@babel/helper-environment-visitor": { + "version": "7.18.9", + "dev": true + }, + "@babel/helper-function-name": { + "version": "7.19.0", + "dev": true, + "requires": { + "@babel/template": "^7.18.10", + "@babel/types": "^7.19.0" + } + }, + "@babel/helper-hoist-variables": { + "version": "7.18.6", + "dev": true, + "requires": { + "@babel/types": "^7.18.6" + } + }, + "@babel/helper-module-imports": { + "version": "7.18.6", + "dev": true, + "requires": { + "@babel/types": "^7.18.6" + } + }, + "@babel/helper-module-transforms": { + "version": "7.20.2", + "dev": true, + "requires": { + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-module-imports": "^7.18.6", + "@babel/helper-simple-access": "^7.20.2", + "@babel/helper-split-export-declaration": "^7.18.6", + "@babel/helper-validator-identifier": "^7.19.1", + "@babel/template": "^7.18.10", + "@babel/traverse": "^7.20.1", + "@babel/types": "^7.20.2" + } + }, + "@babel/helper-plugin-utils": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz", + "integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==", + "dev": true + }, + "@babel/helper-simple-access": { + "version": "7.20.2", + "dev": true, + "requires": { + "@babel/types": "^7.20.2" + } + }, + "@babel/helper-split-export-declaration": { + "version": "7.18.6", + "dev": true, + "requires": { + "@babel/types": "^7.18.6" + } + }, + "@babel/helper-string-parser": { + "version": "7.19.4", + "dev": true + }, + "@babel/helper-validator-identifier": { + "version": "7.19.1", + "dev": true + }, + "@babel/helper-validator-option": { + "version": "7.18.6", + "dev": true + }, + "@babel/helpers": { + "version": "7.20.1", + "dev": true, + "requires": { + "@babel/template": "^7.18.10", + "@babel/traverse": "^7.20.1", + "@babel/types": "^7.20.0" + } + }, + "@babel/highlight": { + "version": "7.18.6", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.18.6", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.2", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "color-convert": { + "version": "1.9.3", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "dev": true + }, + "escape-string-regexp": { + "version": "1.0.5", + "dev": true + }, + "supports-color": { + "version": "5.5.0", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "@babel/parser": { + "version": "7.20.13", + "dev": true + }, + "@babel/plugin-syntax-async-generators": { + "version": "7.8.4", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-bigint": { + "version": "7.8.3", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-class-properties": { + "version": "7.12.13", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.12.13" + } + }, + "@babel/plugin-syntax-import-meta": { + "version": "7.10.4", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-syntax-json-strings": { + "version": "7.8.3", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-jsx": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.22.5.tgz", + "integrity": "sha512-gvyP4hZrgrs/wWMaocvxZ44Hw0b3W8Pe+cMxc8V1ULQ07oh8VNbIRaoD1LRZVTvD+0nieDKjfgKg89sD7rrKrg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-syntax-logical-assignment-operators": { + "version": "7.10.4", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.8.3", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-numeric-separator": { + "version": "7.10.4", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-optional-catch-binding": { + "version": "7.8.3", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-top-level-await": { + "version": "7.14.5", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.14.5" + } + }, + "@babel/plugin-syntax-typescript": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.22.5.tgz", + "integrity": "sha512-1mS2o03i7t1c6VzH6fdQ3OA8tcEIxwG18zIPRp+UY1Ihv6W+XZzBCVxExF9upussPXJ0xE9XRHwMoNs1ep/nRQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/template": { + "version": "7.18.10", + "dev": true, + "requires": { + "@babel/code-frame": "^7.18.6", + "@babel/parser": "^7.18.10", + "@babel/types": "^7.18.10" + } + }, + "@babel/traverse": { + "version": "7.20.1", + "dev": true, + "requires": { + "@babel/code-frame": "^7.18.6", + "@babel/generator": "^7.20.1", + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-function-name": "^7.19.0", + "@babel/helper-hoist-variables": "^7.18.6", + "@babel/helper-split-export-declaration": "^7.18.6", + "@babel/parser": "^7.20.1", + "@babel/types": "^7.20.0", + "debug": "^4.1.0", + "globals": "^11.1.0" + }, + "dependencies": { + "globals": { + "version": "11.12.0", + "dev": true + } + } + }, + "@babel/types": { + "version": "7.20.7", + "dev": true, + "requires": { + "@babel/helper-string-parser": "^7.19.4", + "@babel/helper-validator-identifier": "^7.19.1", + "to-fast-properties": "^2.0.0" + } + }, + "@bcoe/v8-coverage": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", + "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", + "dev": true + }, + "@eslint-community/eslint-utils": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", + "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "dev": true, + "requires": { + "eslint-visitor-keys": "^3.3.0" + } + }, + "@eslint-community/regexpp": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.6.2.tgz", + "integrity": "sha512-pPTNuaAG3QMH+buKyBIGJs3g/S5y0caxw0ygM3YyE6yJFySwiGGSzA+mM3KJ8QQvzeLh3blwgSonkFjgQdxzMw==", + "dev": true + }, + "@eslint/eslintrc": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", + "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", + "dev": true, + "requires": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.6.0", + "globals": "^13.19.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" + } + }, + "@eslint/js": { + "version": "8.56.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.56.0.tgz", + "integrity": "sha512-gMsVel9D7f2HLkBma9VbtzZRehRogVRfbr++f06nL2vnCGCNlzOD+/MUov/F4p8myyAHspEhVobgjpX64q5m6A==", + "dev": true + }, + "@humanwhocodes/config-array": { + "version": "0.11.13", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.13.tgz", + "integrity": "sha512-JSBDMiDKSzQVngfRjOdFXgFfklaXI4K9nLF49Auh21lmBWRLIK3+xTErTWD4KU54pb6coM6ESE7Awz/FNU3zgQ==", + "dev": true, + "requires": { + "@humanwhocodes/object-schema": "^2.0.1", + "debug": "^4.1.1", + "minimatch": "^3.0.5" + } + }, + "@humanwhocodes/module-importer": { + "version": "1.0.1", + "dev": true + }, + "@humanwhocodes/object-schema": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.1.tgz", + "integrity": "sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw==", + "dev": true + }, + "@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "dev": true, + "requires": { + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + }, + "dependencies": { + "argparse": { + "version": "1.0.10", + "dev": true, + "requires": { + "sprintf-js": "~1.0.2" + } + }, + "find-up": { + "version": "4.1.0", + "dev": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "js-yaml": { + "version": "3.14.1", + "dev": true, + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, + "locate-path": { + "version": "5.0.0", + "dev": true, + "requires": { + "p-locate": "^4.1.0" + } + }, + "p-limit": { + "version": "2.3.0", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "4.1.0", + "dev": true, + "requires": { + "p-limit": "^2.2.0" + } + }, + "resolve-from": { + "version": "5.0.0", + "dev": true + } + } + }, + "@istanbuljs/schema": { + "version": "0.1.3", + "dev": true + }, + "@jest/console": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.7.0.tgz", + "integrity": "sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==", + "dev": true, + "requires": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "slash": "^3.0.0" + } + }, + "@jest/core": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.7.0.tgz", + "integrity": "sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==", + "dev": true, + "requires": { + "@jest/console": "^29.7.0", + "@jest/reporters": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "jest-changed-files": "^29.7.0", + "jest-config": "^29.7.0", + "jest-haste-map": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-resolve-dependencies": "^29.7.0", + "jest-runner": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "jest-watcher": "^29.7.0", + "micromatch": "^4.0.4", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "strip-ansi": "^6.0.0" + } + }, + "@jest/environment": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.7.0.tgz", + "integrity": "sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==", + "dev": true, + "requires": { + "@jest/fake-timers": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-mock": "^29.7.0" + } + }, + "@jest/expect": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.7.0.tgz", + "integrity": "sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==", + "dev": true, + "requires": { + "expect": "^29.7.0", + "jest-snapshot": "^29.7.0" + } + }, + "@jest/expect-utils": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.7.0.tgz", + "integrity": "sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==", + "dev": true, + "requires": { + "jest-get-type": "^29.6.3" + } + }, + "@jest/fake-timers": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.7.0.tgz", + "integrity": "sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==", + "dev": true, + "requires": { + "@jest/types": "^29.6.3", + "@sinonjs/fake-timers": "^10.0.2", + "@types/node": "*", + "jest-message-util": "^29.7.0", + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0" + } + }, + "@jest/globals": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.7.0.tgz", + "integrity": "sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==", + "dev": true, + "requires": { + "@jest/environment": "^29.7.0", + "@jest/expect": "^29.7.0", + "@jest/types": "^29.6.3", + "jest-mock": "^29.7.0" + } + }, + "@jest/reporters": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.7.0.tgz", + "integrity": "sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==", + "dev": true, + "requires": { + "@bcoe/v8-coverage": "^0.2.3", + "@jest/console": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@jridgewell/trace-mapping": "^0.3.18", + "@types/node": "*", + "chalk": "^4.0.0", + "collect-v8-coverage": "^1.0.0", + "exit": "^0.1.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-instrument": "^6.0.0", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.0", + "istanbul-reports": "^3.1.3", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "jest-worker": "^29.7.0", + "slash": "^3.0.0", + "string-length": "^4.0.1", + "strip-ansi": "^6.0.0", + "v8-to-istanbul": "^9.0.1" + }, + "dependencies": { + "istanbul-lib-instrument": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.0.tgz", + "integrity": "sha512-x58orMzEVfzPUKqlbLd1hXCnySCxKdDKa6Rjg97CwuLLRI4g3FHTdnExu1OqffVFay6zeMW+T6/DowFLndWnIw==", + "dev": true, + "requires": { + "@babel/core": "^7.12.3", + "@babel/parser": "^7.14.7", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^7.5.4" + } + } + } + }, + "@jest/schemas": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", + "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", + "dev": true, + "requires": { + "@sinclair/typebox": "^0.27.8" + } + }, + "@jest/source-map": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.6.3.tgz", + "integrity": "sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==", + "dev": true, + "requires": { + "@jridgewell/trace-mapping": "^0.3.18", + "callsites": "^3.0.0", + "graceful-fs": "^4.2.9" + } + }, + "@jest/test-result": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.7.0.tgz", + "integrity": "sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==", + "dev": true, + "requires": { + "@jest/console": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "collect-v8-coverage": "^1.0.0" + } + }, + "@jest/test-sequencer": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz", + "integrity": "sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==", + "dev": true, + "requires": { + "@jest/test-result": "^29.7.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "slash": "^3.0.0" + } + }, + "@jest/transform": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.7.0.tgz", + "integrity": "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==", + "dev": true, + "requires": { + "@babel/core": "^7.11.6", + "@jest/types": "^29.6.3", + "@jridgewell/trace-mapping": "^0.3.18", + "babel-plugin-istanbul": "^6.1.1", + "chalk": "^4.0.0", + "convert-source-map": "^2.0.0", + "fast-json-stable-stringify": "^2.1.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", + "micromatch": "^4.0.4", + "pirates": "^4.0.4", + "slash": "^3.0.0", + "write-file-atomic": "^4.0.2" + } + }, + "@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "dev": true, + "requires": { + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + } + }, + "@jridgewell/gen-mapping": { + "version": "0.1.1", + "dev": true, + "requires": { + "@jridgewell/set-array": "^1.0.0", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "@jridgewell/resolve-uri": { + "version": "3.1.0", + "dev": true + }, + "@jridgewell/set-array": { + "version": "1.1.2", + "dev": true + }, + "@jridgewell/sourcemap-codec": { + "version": "1.4.14", + "dev": true + }, + "@jridgewell/trace-mapping": { + "version": "0.3.18", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.18.tgz", + "integrity": "sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA==", + "dev": true, + "requires": { + "@jridgewell/resolve-uri": "3.1.0", + "@jridgewell/sourcemap-codec": "1.4.14" + } + }, + "@nodelib/fs.scandir": { + "version": "2.1.5", + "dev": true, + "requires": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + } + }, + "@nodelib/fs.stat": { + "version": "2.0.5", + "dev": true + }, + "@nodelib/fs.walk": { + "version": "1.2.8", + "dev": true, + "requires": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + } + }, + "@pkgr/core": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.1.0.tgz", + "integrity": "sha512-Zwq5OCzuwJC2jwqmpEQt7Ds1DTi6BWSwoGkbb1n9pO3hzb35BoJELx7c0T23iDkBGkh2e7tvOtjF3tr3OaQHDQ==", + "dev": true + }, + "@sinclair/typebox": { + "version": "0.27.8", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", + "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", + "dev": true + }, + "@sinonjs/commons": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.0.tgz", + "integrity": "sha512-jXBtWAF4vmdNmZgD5FoKsVLv3rPgDnLgPbU84LIJ3otV44vJlDRokVng5v8NFJdCf/da9legHcKaRuZs4L7faA==", + "dev": true, + "requires": { + "type-detect": "4.0.8" + } + }, + "@sinonjs/fake-timers": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", + "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", + "dev": true, + "requires": { + "@sinonjs/commons": "^3.0.0" + } + }, + "@tokenizer/token": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@tokenizer/token/-/token-0.3.0.tgz", + "integrity": "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==", + "dev": true + }, + "@types/babel__core": { + "version": "7.20.1", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.1.tgz", + "integrity": "sha512-aACu/U/omhdk15O4Nfb+fHgH/z3QsfQzpnvRZhYhThms83ZnAOZz7zZAWO7mn2yyNQaA4xTO8GLK3uqFU4bYYw==", + "dev": true, + "requires": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "@types/babel__generator": { + "version": "7.6.4", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.4.tgz", + "integrity": "sha512-tFkciB9j2K755yrTALxD44McOrk+gfpIpvC3sxHjRawj6PfnQxrse4Clq5y/Rq+G3mrBurMax/lG8Qn2t9mSsg==", + "dev": true, + "requires": { + "@babel/types": "^7.0.0" + } + }, + "@types/babel__template": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.1.tgz", + "integrity": "sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g==", + "dev": true, + "requires": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "@types/babel__traverse": { + "version": "7.20.1", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.1.tgz", + "integrity": "sha512-MitHFXnhtgwsGZWtT68URpOvLN4EREih1u3QtQiN4VdAxWKRVvGCSvw/Qth0M0Qq3pJpnGOu5JaM/ydK7OGbqg==", + "dev": true, + "requires": { + "@babel/types": "^7.20.7" + } + }, + "@types/debug": { + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz", + "integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==", + "requires": { + "@types/ms": "*" + } + }, + "@types/events": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/events/-/events-3.0.3.tgz", + "integrity": "sha512-trOc4AAUThEz9hapPtSd7wf5tiQKvTtu5b371UxXdTuqzIh0ArcRspRP0i0Viu+LXstIQ1z96t1nsPxT9ol01g==", + "dev": true + }, + "@types/graceful-fs": { + "version": "4.1.6", + "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.6.tgz", + "integrity": "sha512-Sig0SNORX9fdW+bQuTEovKj3uHcUL6LQKbCrrqb1X7J6/ReAbhCXRAhc+SMejhLELFj2QcyuxmUooZ4bt5ReSw==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/istanbul-lib-coverage": { + "version": "2.0.4", + "dev": true + }, + "@types/istanbul-lib-report": { + "version": "3.0.0", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "*" + } + }, + "@types/istanbul-reports": { + "version": "3.0.1", + "dev": true, + "requires": { + "@types/istanbul-lib-report": "*" + } + }, + "@types/jest": { + "version": "29.5.11", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.11.tgz", + "integrity": "sha512-S2mHmYIVe13vrm6q4kN6fLYYAka15ALQki/vgDC3mIukEOx8WJlv0kQPM+d4w8Gp6u0uSdKND04IlTXBv0rwnQ==", + "dev": true, + "requires": { + "expect": "^29.0.0", + "pretty-format": "^29.0.0" + } + }, + "@types/json-schema": { + "version": "7.0.12", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.12.tgz", + "integrity": "sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA==", + "dev": true + }, + "@types/minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-hov8bUuiLiyFPGyFPE1lwWhmzYbirOXQNNo40+y3zow8aFVTeyn3VWL0VFFfdNddA8S4Vf0Tc062rzyNr7Paag==", + "dev": true + }, + "@types/ms": { + "version": "0.7.31" + }, + "@types/node": { + "version": "18.11.9", + "dev": true + }, + "@types/normalize-package-data": { + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.4.tgz", + "integrity": "sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==", + "dev": true + }, + "@types/sdp-transform": { + "version": "2.4.9", + "resolved": "https://registry.npmjs.org/@types/sdp-transform/-/sdp-transform-2.4.9.tgz", + "integrity": "sha512-bVr+/OoZZy7wrHlNcEAAa6PAgKA4BoXPYVN2EijMC5WnGgQ4ZEuixmKnVs2roiAvr7RhIFVH17QD27cojgIZCg==", + "dev": true + }, + "@types/semver": { + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.0.tgz", + "integrity": "sha512-G8hZ6XJiHnuhQKR7ZmysCeJWE08o8T0AXtk5darsCaTVsYZhhgUrq53jizaR2FvsoeCwJhlmwTjkXBY5Pn/ZHw==", + "dev": true + }, + "@types/stack-utils": { + "version": "2.0.1", + "dev": true + }, + "@types/ua-parser-js": { + "version": "0.7.39", + "resolved": "https://registry.npmjs.org/@types/ua-parser-js/-/ua-parser-js-0.7.39.tgz", + "integrity": "sha512-P/oDfpofrdtF5xw433SPALpdSchtJmY7nsJItf8h3KXqOslkbySh8zq4dSWXH2oTjRvJ5PczVEoCZPow6GicLg==", + "dev": true + }, + "@types/yargs": { + "version": "17.0.13", + "dev": true, + "requires": { + "@types/yargs-parser": "*" + } + }, + "@types/yargs-parser": { + "version": "21.0.0", + "dev": true + }, + "@typescript-eslint/eslint-plugin": { + "version": "6.18.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.18.1.tgz", + "integrity": "sha512-nISDRYnnIpk7VCFrGcu1rnZfM1Dh9LRHnfgdkjcbi/l7g16VYRri3TjXi9Ir4lOZSw5N/gnV/3H7jIPQ8Q4daA==", + "dev": true, + "requires": { + "@eslint-community/regexpp": "^4.5.1", + "@typescript-eslint/scope-manager": "6.18.1", + "@typescript-eslint/type-utils": "6.18.1", + "@typescript-eslint/utils": "6.18.1", + "@typescript-eslint/visitor-keys": "6.18.1", + "debug": "^4.3.4", + "graphemer": "^1.4.0", + "ignore": "^5.2.4", + "natural-compare": "^1.4.0", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" + }, + "dependencies": { + "@typescript-eslint/scope-manager": { + "version": "6.18.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.18.1.tgz", + "integrity": "sha512-BgdBwXPFmZzaZUuw6wKiHKIovms97a7eTImjkXCZE04TGHysG+0hDQPmygyvgtkoB/aOQwSM/nWv3LzrOIQOBw==", + "dev": true, + "requires": { + "@typescript-eslint/types": "6.18.1", + "@typescript-eslint/visitor-keys": "6.18.1" + } + }, + "@typescript-eslint/types": { + "version": "6.18.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.18.1.tgz", + "integrity": "sha512-4TuMAe+tc5oA7wwfqMtB0Y5OrREPF1GeJBAjqwgZh1lEMH5PJQgWgHGfYufVB51LtjD+peZylmeyxUXPfENLCw==", + "dev": true + }, + "@typescript-eslint/typescript-estree": { + "version": "6.18.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.18.1.tgz", + "integrity": "sha512-fv9B94UAhywPRhUeeV/v+3SBDvcPiLxRZJw/xZeeGgRLQZ6rLMG+8krrJUyIf6s1ecWTzlsbp0rlw7n9sjufHA==", + "dev": true, + "requires": { + "@typescript-eslint/types": "6.18.1", + "@typescript-eslint/visitor-keys": "6.18.1", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "minimatch": "9.0.3", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" + } + }, + "@typescript-eslint/utils": { + "version": "6.18.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.18.1.tgz", + "integrity": "sha512-zZmTuVZvD1wpoceHvoQpOiewmWu3uP9FuTWo8vqpy2ffsmfCE8mklRPi+vmnIYAIk9t/4kOThri2QCDgor+OpQ==", + "dev": true, + "requires": { + "@eslint-community/eslint-utils": "^4.4.0", + "@types/json-schema": "^7.0.12", + "@types/semver": "^7.5.0", + "@typescript-eslint/scope-manager": "6.18.1", + "@typescript-eslint/types": "6.18.1", + "@typescript-eslint/typescript-estree": "6.18.1", + "semver": "^7.5.4" + } + }, + "@typescript-eslint/visitor-keys": { + "version": "6.18.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.18.1.tgz", + "integrity": "sha512-/kvt0C5lRqGoCfsbmm7/CwMqoSkY3zzHLIjdhHZQW3VFrnz7ATecOHR7nb7V+xn4286MBxfnQfQhAmCI0u+bJA==", + "dev": true, + "requires": { + "@typescript-eslint/types": "6.18.1", + "eslint-visitor-keys": "^3.4.1" + } + }, + "brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0" + } + }, + "minimatch": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "dev": true, + "requires": { + "brace-expansion": "^2.0.1" + } + } + } + }, + "@typescript-eslint/parser": { + "version": "6.18.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.18.1.tgz", + "integrity": "sha512-zct/MdJnVaRRNy9e84XnVtRv9Vf91/qqe+hZJtKanjojud4wAVy/7lXxJmMyX6X6J+xc6c//YEWvpeif8cAhWA==", + "dev": true, + "requires": { + "@typescript-eslint/scope-manager": "6.18.1", + "@typescript-eslint/types": "6.18.1", + "@typescript-eslint/typescript-estree": "6.18.1", + "@typescript-eslint/visitor-keys": "6.18.1", + "debug": "^4.3.4" + }, + "dependencies": { + "@typescript-eslint/scope-manager": { + "version": "6.18.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.18.1.tgz", + "integrity": "sha512-BgdBwXPFmZzaZUuw6wKiHKIovms97a7eTImjkXCZE04TGHysG+0hDQPmygyvgtkoB/aOQwSM/nWv3LzrOIQOBw==", + "dev": true, + "requires": { + "@typescript-eslint/types": "6.18.1", + "@typescript-eslint/visitor-keys": "6.18.1" + } + }, + "@typescript-eslint/types": { + "version": "6.18.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.18.1.tgz", + "integrity": "sha512-4TuMAe+tc5oA7wwfqMtB0Y5OrREPF1GeJBAjqwgZh1lEMH5PJQgWgHGfYufVB51LtjD+peZylmeyxUXPfENLCw==", + "dev": true + }, + "@typescript-eslint/typescript-estree": { + "version": "6.18.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.18.1.tgz", + "integrity": "sha512-fv9B94UAhywPRhUeeV/v+3SBDvcPiLxRZJw/xZeeGgRLQZ6rLMG+8krrJUyIf6s1ecWTzlsbp0rlw7n9sjufHA==", + "dev": true, + "requires": { + "@typescript-eslint/types": "6.18.1", + "@typescript-eslint/visitor-keys": "6.18.1", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "minimatch": "9.0.3", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" + } + }, + "@typescript-eslint/visitor-keys": { + "version": "6.18.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.18.1.tgz", + "integrity": "sha512-/kvt0C5lRqGoCfsbmm7/CwMqoSkY3zzHLIjdhHZQW3VFrnz7ATecOHR7nb7V+xn4286MBxfnQfQhAmCI0u+bJA==", + "dev": true, + "requires": { + "@typescript-eslint/types": "6.18.1", + "eslint-visitor-keys": "^3.4.1" + } + }, + "brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0" + } + }, + "minimatch": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "dev": true, + "requires": { + "brace-expansion": "^2.0.1" + } + } + } + }, + "@typescript-eslint/scope-manager": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.62.0.tgz", + "integrity": "sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w==", + "dev": true, + "requires": { + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/visitor-keys": "5.62.0" + } + }, + "@typescript-eslint/type-utils": { + "version": "6.18.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.18.1.tgz", + "integrity": "sha512-wyOSKhuzHeU/5pcRDP2G2Ndci+4g653V43gXTpt4nbyoIOAASkGDA9JIAgbQCdCkcr1MvpSYWzxTz0olCn8+/Q==", + "dev": true, + "requires": { + "@typescript-eslint/typescript-estree": "6.18.1", + "@typescript-eslint/utils": "6.18.1", + "debug": "^4.3.4", + "ts-api-utils": "^1.0.1" + }, + "dependencies": { + "@typescript-eslint/scope-manager": { + "version": "6.18.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.18.1.tgz", + "integrity": "sha512-BgdBwXPFmZzaZUuw6wKiHKIovms97a7eTImjkXCZE04TGHysG+0hDQPmygyvgtkoB/aOQwSM/nWv3LzrOIQOBw==", + "dev": true, + "requires": { + "@typescript-eslint/types": "6.18.1", + "@typescript-eslint/visitor-keys": "6.18.1" + } + }, + "@typescript-eslint/types": { + "version": "6.18.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.18.1.tgz", + "integrity": "sha512-4TuMAe+tc5oA7wwfqMtB0Y5OrREPF1GeJBAjqwgZh1lEMH5PJQgWgHGfYufVB51LtjD+peZylmeyxUXPfENLCw==", + "dev": true + }, + "@typescript-eslint/typescript-estree": { + "version": "6.18.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.18.1.tgz", + "integrity": "sha512-fv9B94UAhywPRhUeeV/v+3SBDvcPiLxRZJw/xZeeGgRLQZ6rLMG+8krrJUyIf6s1ecWTzlsbp0rlw7n9sjufHA==", + "dev": true, + "requires": { + "@typescript-eslint/types": "6.18.1", + "@typescript-eslint/visitor-keys": "6.18.1", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "minimatch": "9.0.3", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" + } + }, + "@typescript-eslint/utils": { + "version": "6.18.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.18.1.tgz", + "integrity": "sha512-zZmTuVZvD1wpoceHvoQpOiewmWu3uP9FuTWo8vqpy2ffsmfCE8mklRPi+vmnIYAIk9t/4kOThri2QCDgor+OpQ==", + "dev": true, + "requires": { + "@eslint-community/eslint-utils": "^4.4.0", + "@types/json-schema": "^7.0.12", + "@types/semver": "^7.5.0", + "@typescript-eslint/scope-manager": "6.18.1", + "@typescript-eslint/types": "6.18.1", + "@typescript-eslint/typescript-estree": "6.18.1", + "semver": "^7.5.4" + } + }, + "@typescript-eslint/visitor-keys": { + "version": "6.18.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.18.1.tgz", + "integrity": "sha512-/kvt0C5lRqGoCfsbmm7/CwMqoSkY3zzHLIjdhHZQW3VFrnz7ATecOHR7nb7V+xn4286MBxfnQfQhAmCI0u+bJA==", + "dev": true, + "requires": { + "@typescript-eslint/types": "6.18.1", + "eslint-visitor-keys": "^3.4.1" + } + }, + "brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0" + } + }, + "minimatch": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "dev": true, + "requires": { + "brace-expansion": "^2.0.1" + } + } + } + }, + "@typescript-eslint/types": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.62.0.tgz", + "integrity": "sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ==", + "dev": true + }, + "@typescript-eslint/typescript-estree": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.62.0.tgz", + "integrity": "sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA==", + "dev": true, + "requires": { + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/visitor-keys": "5.62.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + } + }, + "@typescript-eslint/utils": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.62.0.tgz", + "integrity": "sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ==", + "dev": true, + "requires": { + "@eslint-community/eslint-utils": "^4.2.0", + "@types/json-schema": "^7.0.9", + "@types/semver": "^7.3.12", + "@typescript-eslint/scope-manager": "5.62.0", + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/typescript-estree": "5.62.0", + "eslint-scope": "^5.1.1", + "semver": "^7.3.7" + } + }, + "@typescript-eslint/visitor-keys": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.62.0.tgz", + "integrity": "sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw==", + "dev": true, + "requires": { + "@typescript-eslint/types": "5.62.0", + "eslint-visitor-keys": "^3.3.0" + } + }, + "@ungap/structured-clone": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", + "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", + "dev": true + }, + "acorn": { + "version": "8.11.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.2.tgz", + "integrity": "sha512-nc0Axzp/0FILLEVsm4fNwLCwMttvhEI263QtVPQcbpfZZ3ts0hLsZGOpE6czNlid7CJ9MlyH8reXkpsf3YUY4w==", + "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-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, + "requires": { + "type-fest": "^0.21.3" + }, + "dependencies": { + "type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dev": true + } + } + }, + "ansi-regex": { + "version": "5.0.1", + "dev": true + }, + "ansi-styles": { + "version": "4.3.0", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + } + }, + "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 + }, + "arrify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", + "integrity": "sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==", + "dev": true + }, + "awaitqueue": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/awaitqueue/-/awaitqueue-3.0.2.tgz", + "integrity": "sha512-AVAtRwmf0DNSesMdyanFKKejTrOnjdKtz5LIDQFu2OTUgXvB/CRTYMrkPAF/2GCF9XBtYVxSwxDORlD41S+RyQ==", + "requires": { + "debug": "^4.3.4" + } + }, + "babel-jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz", + "integrity": "sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==", + "dev": true, + "requires": { + "@jest/transform": "^29.7.0", + "@types/babel__core": "^7.1.14", + "babel-plugin-istanbul": "^6.1.1", + "babel-preset-jest": "^29.6.3", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "slash": "^3.0.0" + } + }, + "babel-plugin-istanbul": { + "version": "6.1.1", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-instrument": "^5.0.4", + "test-exclude": "^6.0.0" + } + }, + "babel-plugin-jest-hoist": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz", + "integrity": "sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==", + "dev": true, + "requires": { + "@babel/template": "^7.3.3", + "@babel/types": "^7.3.3", + "@types/babel__core": "^7.1.14", + "@types/babel__traverse": "^7.0.6" + } + }, + "babel-preset-current-node-syntax": { + "version": "1.0.1", + "dev": true, + "requires": { + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-bigint": "^7.8.3", + "@babel/plugin-syntax-class-properties": "^7.8.3", + "@babel/plugin-syntax-import-meta": "^7.8.3", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.8.3", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.8.3", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-top-level-await": "^7.8.3" + } + }, + "babel-preset-jest": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz", + "integrity": "sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==", + "dev": true, + "requires": { + "babel-plugin-jest-hoist": "^29.6.3", + "babel-preset-current-node-syntax": "^1.0.0" + } + }, + "balanced-match": { + "version": "1.0.2", + "dev": true + }, + "big-integer": { + "version": "1.6.52", + "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.52.tgz", + "integrity": "sha512-QxD8cf2eVqJOOz63z6JIN9BzvVs/dlySa5HGSBH5xtR8dPteIRQnBxxKqkNTiT6jbDTF6jAfrd4oMcND9RGbQg==", + "dev": true + }, + "bplist-parser": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/bplist-parser/-/bplist-parser-0.2.0.tgz", + "integrity": "sha512-z0M+byMThzQmD9NILRniCUXYsYpjwnlO8N5uCFaCqIOpqRsJCrQL9NK3JsD67CN5a08nF5oIL2bD6loTdHOuKw==", + "dev": true, + "requires": { + "big-integer": "^1.6.44" + } + }, + "brace-expansion": { + "version": "1.1.11", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "braces": { + "version": "3.0.2", + "dev": true, + "requires": { + "fill-range": "^7.0.1" + } + }, + "browserslist": { + "version": "4.21.4", + "dev": true, + "requires": { + "caniuse-lite": "^1.0.30001400", + "electron-to-chromium": "^1.4.251", + "node-releases": "^2.0.6", + "update-browserslist-db": "^1.0.9" + } + }, + "bs-logger": { + "version": "0.2.6", + "dev": true, + "requires": { + "fast-json-stable-stringify": "2.x" + } + }, + "bser": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", + "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", + "dev": true, + "requires": { + "node-int64": "^0.4.0" + } + }, + "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 + }, + "bundle-name": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-3.0.0.tgz", + "integrity": "sha512-PKA4BeSvBpQKQ8iPOGCSiell+N8P+Tf1DlwqmYhpe2gAhKPHn8EYOxVT+ShuGmhg8lN8XiSlS80yiExKXrURlw==", + "dev": true, + "requires": { + "run-applescript": "^5.0.0" + } + }, + "callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true + }, + "camelcase": { + "version": "5.3.1", + "dev": true + }, + "camelcase-keys": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-8.0.2.tgz", + "integrity": "sha512-qMKdlOfsjlezMqxkUGGMaWWs17i2HoL15tM+wtx8ld4nLrUwU58TFdvyGOz/piNP842KeO8yXvggVQSdQ828NA==", + "dev": true, + "requires": { + "camelcase": "^7.0.0", + "map-obj": "^4.3.0", + "quick-lru": "^6.1.1", + "type-fest": "^2.13.0" + }, + "dependencies": { + "camelcase": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-7.0.1.tgz", + "integrity": "sha512-xlx1yCK2Oc1APsPXDL2LdlNP6+uu8OCDdhOBSVT279M/S+y75O30C2VuD8T2ogdePBBl7PfPF4504tnLgX3zfw==", + "dev": true + }, + "type-fest": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz", + "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==", + "dev": true + } + } + }, + "caniuse-lite": { + "version": "1.0.30001431", + "dev": true + }, + "chalk": { + "version": "4.1.2", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "dependencies": { + "has-flag": { + "version": "4.0.0", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "char-regex": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", + "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", + "dev": true + }, + "ci-info": { + "version": "3.6.1", + "dev": true + }, + "cjs-module-lexer": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.3.tgz", + "integrity": "sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ==", + "dev": true + }, + "cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + } + }, + "co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", + "dev": true + }, + "collect-v8-coverage": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz", + "integrity": "sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==", + "dev": true + }, + "color-convert": { + "version": "2.0.1", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "dev": true + }, + "concat-map": { + "version": "0.0.1", + "dev": true + }, + "convert-source-map": { + "version": "2.0.0", + "dev": true + }, + "create-jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/create-jest/-/create-jest-29.7.0.tgz", + "integrity": "sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==", + "dev": true, + "requires": { + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "jest-config": "^29.7.0", + "jest-util": "^29.7.0", + "prompts": "^2.0.1" + } + }, + "cross-spawn": { + "version": "7.0.3", + "dev": true, + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + } + }, + "crypto-random-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-4.0.0.tgz", + "integrity": "sha512-x8dy3RnvYdlUcPOjkEHqozhiwzKNSq7GcPuXFbnyMOCHxX8V3OgIg/pYuabl2sbUPfIJaeAQB7PMOK8DFIdoRA==", + "dev": true, + "requires": { + "type-fest": "^1.0.1" + }, + "dependencies": { + "type-fest": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz", + "integrity": "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==", + "dev": true + } + } + }, + "debug": { + "version": "4.3.4", + "requires": { + "ms": "2.1.2" + } + }, + "decamelize": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-6.0.0.tgz", + "integrity": "sha512-Fv96DCsdOgB6mdGl67MT5JaTNKRzrzill5OH5s8bjYJXVlcXyPYGyPsUkWyGV5p1TXI5esYIYMMeDJL0hEIwaA==", + "dev": true + }, + "decamelize-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/decamelize-keys/-/decamelize-keys-1.1.1.tgz", + "integrity": "sha512-WiPxgEirIV0/eIOMcnFBA3/IJZAZqKnwAwWyvvdi4lsr1WCN22nhdf/3db3DoZcUjTV2SqfzIwNyp6y2xs3nmg==", + "dev": true, + "requires": { + "decamelize": "^1.1.0", + "map-obj": "^1.0.0" + }, + "dependencies": { + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", + "dev": true + }, + "map-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", + "integrity": "sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg==", + "dev": true + } + } + }, + "dedent": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.5.1.tgz", + "integrity": "sha512-+LxW+KLWxu3HW3M2w2ympwtqPrqYRzU8fqi6Fhd18fBALe15blJPI/I4+UHveMVG6lJqB4JNd4UG0S5cnVHwIg==", + "dev": true, + "requires": {} + }, + "deep-is": { + "version": "0.1.4", + "dev": true + }, + "deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "dev": true + }, + "default-browser": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-4.0.0.tgz", + "integrity": "sha512-wX5pXO1+BrhMkSbROFsyxUm0i/cJEScyNhA4PPxc41ICuv05ZZB/MX28s8aZx6xjmatvebIapF6hLEKEcpneUA==", + "dev": true, + "requires": { + "bundle-name": "^3.0.0", + "default-browser-id": "^3.0.0", + "execa": "^7.1.1", + "titleize": "^3.0.0" + }, + "dependencies": { + "execa": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-7.2.0.tgz", + "integrity": "sha512-UduyVP7TLB5IcAQl+OzLyLcS/l32W/GLg+AhHJ+ow40FOk2U3SAllPwR44v4vmdFwIWqpdwxxpQbF1n5ta9seA==", + "dev": true, + "requires": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.1", + "human-signals": "^4.3.0", + "is-stream": "^3.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^5.1.0", + "onetime": "^6.0.0", + "signal-exit": "^3.0.7", + "strip-final-newline": "^3.0.0" + } + }, + "human-signals": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-4.3.1.tgz", + "integrity": "sha512-nZXjEF2nbo7lIw3mgYjItAfgQXog3OjJogSbKa2CQIIvSGWcKgeJnQlNXip6NglNzYH45nSRiEVimMvYL8DDqQ==", + "dev": true + }, + "is-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", + "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", + "dev": true + }, + "mimic-fn": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", + "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", + "dev": true + }, + "npm-run-path": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.2.0.tgz", + "integrity": "sha512-W4/tgAXFqFA0iL7fk0+uQ3g7wkL8xJmx3XdK0VGb4cHW//eZTtKGvFBBoRKVTpY7n6ze4NL9ly7rgXcHufqXKg==", + "dev": true, + "requires": { + "path-key": "^4.0.0" + } + }, + "onetime": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", + "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", + "dev": true, + "requires": { + "mimic-fn": "^4.0.0" + } + }, + "path-key": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", + "dev": true + }, + "strip-final-newline": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", + "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", + "dev": true + } + } + }, + "default-browser-id": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-3.0.0.tgz", + "integrity": "sha512-OZ1y3y0SqSICtE8DE4S8YOE9UZOJ8wO16fKWVP5J1Qz42kV9jcnMVFrEE/noXb/ss3Q4pZIH79kxofzyNNtUNA==", + "dev": true, + "requires": { + "bplist-parser": "^0.2.0", + "untildify": "^4.0.0" + } + }, + "define-lazy-prop": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz", + "integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==", + "dev": true + }, + "detect-newline": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", + "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", + "dev": true + }, + "diff-sequences": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", + "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", + "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", + "dev": true, + "requires": { + "esutils": "^2.0.2" + } + }, + "electron-to-chromium": { + "version": "1.4.284", + "dev": true + }, + "emittery": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz", + "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==", + "dev": true + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "error-ex": { + "version": "1.3.2", + "dev": true, + "requires": { + "is-arrayish": "^0.2.1" + } + }, + "escalade": { + "version": "3.1.1", + "dev": true + }, + "escape-string-regexp": { + "version": "4.0.0", + "dev": true + }, + "eslint": { + "version": "8.56.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.56.0.tgz", + "integrity": "sha512-Go19xM6T9puCOWntie1/P997aXxFsOi37JIHRWI514Hc6ZnaHGKY9xFhrU65RT6CcBEzZoGG1e6Nq+DT04ZtZQ==", + "dev": true, + "requires": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.4", + "@eslint/js": "8.56.0", + "@humanwhocodes/config-array": "^0.11.13", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "@ungap/structured-clone": "^1.2.0", + "ajv": "^6.12.4", + "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.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", + "esquery": "^1.4.2", + "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.2", + "globals": "^13.19.0", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "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.3", + "strip-ansi": "^6.0.1", + "text-table": "^0.2.0" + }, + "dependencies": { + "eslint-scope": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "dev": true, + "requires": { + "esrecurse": "^4.3.0", + "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 + } + } + }, + "eslint-config-prettier": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.1.0.tgz", + "integrity": "sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==", + "dev": true, + "requires": {} + }, + "eslint-plugin-jest": { + "version": "27.6.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-27.6.1.tgz", + "integrity": "sha512-WEYkyVXD9NlmFBKvrkmzrC+C9yZoz5pAml2hO19PlS3spJtoiwj4p2u8spd/7zx5IvRsZsCmsoImaAvBB9X93Q==", + "dev": true, + "requires": { + "@typescript-eslint/utils": "^5.10.0" + } + }, + "eslint-plugin-prettier": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.1.2.tgz", + "integrity": "sha512-dhlpWc9vOwohcWmClFcA+HjlvUpuyynYs0Rf+L/P6/0iQE6vlHW9l5bkfzN62/Stm9fbq8ku46qzde76T1xlSg==", + "dev": true, + "requires": { + "prettier-linter-helpers": "^1.0.0", + "synckit": "^0.8.6" + } + }, + "eslint-scope": { + "version": "5.1.1", + "dev": true, + "requires": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + } + }, + "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 + }, + "espree": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "dev": true, + "requires": { + "acorn": "^8.9.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" + } + }, + "esprima": { + "version": "4.0.1", + "dev": true + }, + "esquery": { + "version": "1.5.0", + "dev": true, + "requires": { + "estraverse": "^5.1.0" + }, + "dependencies": { + "estraverse": { + "version": "5.3.0", + "dev": true + } + } + }, + "esrecurse": { + "version": "4.3.0", + "dev": true, + "requires": { + "estraverse": "^5.2.0" + }, + "dependencies": { + "estraverse": { + "version": "5.3.0", + "dev": true + } + } + }, + "estraverse": { + "version": "4.3.0", + "dev": true + }, + "esutils": { + "version": "2.0.3", + "dev": true + }, + "event-target-shim": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-6.0.2.tgz", + "integrity": "sha512-8q3LsZjRezbFZ2PN+uP+Q7pnHUMmAOziU2vA2OwoFaKIXxlxl38IylhSSgUorWu/rf4er67w0ikBqjBFk/pomA==" + }, + "events": { + "version": "3.3.0" + }, + "execa": { + "version": "5.1.1", + "dev": true, + "requires": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + } + }, + "exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", + "dev": true + }, + "expect": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/expect/-/expect-29.7.0.tgz", + "integrity": "sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==", + "dev": true, + "requires": { + "@jest/expect-utils": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0" + } + }, + "fake-mediastreamtrack": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/fake-mediastreamtrack/-/fake-mediastreamtrack-1.2.0.tgz", + "integrity": "sha512-AxHtlEmka1sqNoe3Ej1H1hJc9gjjO/6vCbCPm4D4QeEXvzhjYumA+iZ7wOi2WrmkAhGElHhBgWoNgJhFccectA==", + "requires": { + "event-target-shim": "^6.0.2", + "uuid": "^9.0.0" + } + }, + "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-diff": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz", + "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==", + "dev": true + }, + "fast-glob": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.0.tgz", + "integrity": "sha512-ChDuvbOypPuNjO8yIDf36x7BlZX1smcUMTTcyoIjycexOxd6DFsKsg21qVBzEmr3G7fUKIRy2/psii+CIUt7FA==", + "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", + "dev": true + }, + "fast-levenshtein": { + "version": "2.0.6", + "dev": true + }, + "fastq": { + "version": "1.13.0", + "dev": true, + "requires": { + "reusify": "^1.0.4" + } + }, + "fb-watchman": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", + "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", + "dev": true, + "requires": { + "bser": "2.1.1" + } + }, + "file-entry-cache": { + "version": "6.0.1", + "dev": true, + "requires": { + "flat-cache": "^3.0.4" + } + }, + "file-type": { + "version": "18.7.0", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-18.7.0.tgz", + "integrity": "sha512-ihHtXRzXEziMrQ56VSgU7wkxh55iNchFkosu7Y9/S+tXHdKyrGjVK0ujbqNnsxzea+78MaLhN6PGmfYSAv1ACw==", + "dev": true, + "requires": { + "readable-web-to-node-stream": "^3.0.2", + "strtok3": "^7.0.0", + "token-types": "^5.0.1" + } + }, + "fill-range": { + "version": "7.0.1", + "dev": true, + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "find-up": { + "version": "5.0.0", + "dev": true, + "requires": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + } + }, + "flat-cache": { + "version": "3.0.4", + "dev": true, + "requires": { + "flatted": "^3.1.0", + "rimraf": "^3.0.2" + } + }, + "flatted": { + "version": "3.2.7", + "dev": true + }, + "fs.realpath": { + "version": "1.0.0", + "dev": true + }, + "fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "optional": true + }, + "function-bind": { + "version": "1.1.1", + "dev": true + }, + "gensync": { + "version": "1.0.0-beta.2", + "dev": true + }, + "get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true + }, + "get-package-type": { + "version": "0.1.0", + "dev": true + }, + "get-stdin": { + "version": "9.0.0", + "dev": true + }, + "get-stream": { + "version": "6.0.1", + "dev": true + }, + "glob": { + "version": "7.2.3", + "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", + "dev": true, + "requires": { + "is-glob": "^4.0.3" + } + }, + "globals": { + "version": "13.23.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.23.0.tgz", + "integrity": "sha512-XAmF0RjlrjY23MA51q3HltdlGxUpXPvg0GioKiD9X6HD28iMjo2dKC8Vqwm7lne4GNr78+RHTfliktR6ZH09wA==", + "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" + } + }, + "graceful-fs": { + "version": "4.2.10", + "dev": true + }, + "graphemer": { + "version": "1.4.0", + "dev": true + }, + "h264-profile-level-id": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/h264-profile-level-id/-/h264-profile-level-id-2.0.0.tgz", + "integrity": "sha512-X4CLryVbVA0CtjTExS4G5U1gb2Z4wa32AF8ukVmFuLdw2JRq2aHisor7SY5SYTUUrUSqq0KdPIO18sql6IWIQw==", + "requires": { + "@types/debug": "^4.1.12", + "debug": "^4.3.4" + } + }, + "hard-rejection": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/hard-rejection/-/hard-rejection-2.1.0.tgz", + "integrity": "sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA==", + "dev": true + }, + "has": { + "version": "1.0.3", + "dev": true, + "requires": { + "function-bind": "^1.1.1" + } + }, + "has-flag": { + "version": "3.0.0", + "dev": true + }, + "hosted-git-info": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-5.2.1.tgz", + "integrity": "sha512-xIcQYMnhcx2Nr4JTjsFmwwnr9vldugPy9uVm0o87bjqqWMv9GaqsTeT+i99wTl0mk1uLxJtHxLb8kymqTENQsw==", + "dev": true, + "requires": { + "lru-cache": "^7.5.1" + }, + "dependencies": { + "lru-cache": { + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", + "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", + "dev": true + } + } + }, + "html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true + }, + "human-signals": { + "version": "2.1.0", + "dev": true + }, + "ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "dev": true + }, + "ignore": { + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", + "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", + "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" + } + }, + "import-local": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz", + "integrity": "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==", + "dev": true, + "requires": { + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" + } + }, + "imurmurhash": { + "version": "0.1.4", + "dev": true + }, + "indent-string": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-5.0.0.tgz", + "integrity": "sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg==", + "dev": true + }, + "inflight": { + "version": "1.0.6", + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "dev": true + }, + "is-arrayish": { + "version": "0.2.1", + "dev": true + }, + "is-core-module": { + "version": "2.13.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.0.tgz", + "integrity": "sha512-Z7dk6Qo8pOCp3l4tsX2C5ZVas4V+UxwQodwZhLopL91TX8UyyHEXafPcyoeeWuLrwzHcr3igO78wNLwHJHsMCQ==", + "dev": true, + "requires": { + "has": "^1.0.3" + } + }, + "is-docker": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz", + "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==", + "dev": true + }, + "is-extglob": { + "version": "2.1.1", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "is-generator-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", + "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", + "dev": true + }, + "is-glob": { + "version": "4.0.3", + "dev": true, + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-inside-container": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz", + "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==", + "dev": true, + "requires": { + "is-docker": "^3.0.0" + } + }, + "is-number": { + "version": "7.0.0", + "dev": true + }, + "is-path-inside": { + "version": "3.0.3", + "dev": true + }, + "is-plain-obj": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", + "integrity": "sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==", + "dev": true + }, + "is-stream": { + "version": "2.0.1", + "dev": true + }, + "is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "dev": true, + "requires": { + "is-docker": "^2.0.0" + }, + "dependencies": { + "is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "dev": true + } + } + }, + "isexe": { + "version": "2.0.0", + "dev": true + }, + "istanbul-lib-coverage": { + "version": "3.2.0", + "dev": true + }, + "istanbul-lib-instrument": { + "version": "5.2.1", + "dev": true, + "requires": { + "@babel/core": "^7.12.3", + "@babel/parser": "^7.14.7", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^6.3.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "dev": true + } + } + }, + "istanbul-lib-report": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", + "dev": true, + "requires": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^4.0.0", + "supports-color": "^7.1.0" + }, + "dependencies": { + "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 + }, + "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" + } + } + } + }, + "istanbul-lib-source-maps": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", + "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", + "dev": true, + "requires": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0", + "source-map": "^0.6.1" + } + }, + "istanbul-reports": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.6.tgz", + "integrity": "sha512-TLgnMkKg3iTDsQ9PbPTdpfAK2DzjF9mqUG7RMgcQl8oFjad8ob4laGxv5XV5U9MAfx8D6tSJiUyuAwzLicaxlg==", + "dev": true, + "requires": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + } + }, + "jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz", + "integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==", + "dev": true, + "requires": { + "@jest/core": "^29.7.0", + "@jest/types": "^29.6.3", + "import-local": "^3.0.2", + "jest-cli": "^29.7.0" + } + }, + "jest-changed-files": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.7.0.tgz", + "integrity": "sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==", + "dev": true, + "requires": { + "execa": "^5.0.0", + "jest-util": "^29.7.0", + "p-limit": "^3.1.0" + } + }, + "jest-circus": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.7.0.tgz", + "integrity": "sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==", + "dev": true, + "requires": { + "@jest/environment": "^29.7.0", + "@jest/expect": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "co": "^4.6.0", + "dedent": "^1.0.0", + "is-generator-fn": "^2.0.0", + "jest-each": "^29.7.0", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "p-limit": "^3.1.0", + "pretty-format": "^29.7.0", + "pure-rand": "^6.0.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + } + }, + "jest-cli": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.7.0.tgz", + "integrity": "sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==", + "dev": true, + "requires": { + "@jest/core": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "create-jest": "^29.7.0", + "exit": "^0.1.2", + "import-local": "^3.0.2", + "jest-config": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "yargs": "^17.3.1" + } + }, + "jest-config": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.7.0.tgz", + "integrity": "sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==", + "dev": true, + "requires": { + "@babel/core": "^7.11.6", + "@jest/test-sequencer": "^29.7.0", + "@jest/types": "^29.6.3", + "babel-jest": "^29.7.0", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "deepmerge": "^4.2.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-circus": "^29.7.0", + "jest-environment-node": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-runner": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "micromatch": "^4.0.4", + "parse-json": "^5.2.0", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "strip-json-comments": "^3.1.1" + } + }, + "jest-diff": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz", + "integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==", + "dev": true, + "requires": { + "chalk": "^4.0.0", + "diff-sequences": "^29.6.3", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + } + }, + "jest-docblock": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.7.0.tgz", + "integrity": "sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==", + "dev": true, + "requires": { + "detect-newline": "^3.0.0" + } + }, + "jest-each": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.7.0.tgz", + "integrity": "sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==", + "dev": true, + "requires": { + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "jest-get-type": "^29.6.3", + "jest-util": "^29.7.0", + "pretty-format": "^29.7.0" + } + }, + "jest-environment-node": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.7.0.tgz", + "integrity": "sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==", + "dev": true, + "requires": { + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0" + } + }, + "jest-get-type": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", + "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", + "dev": true + }, + "jest-haste-map": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz", + "integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==", + "dev": true, + "requires": { + "@jest/types": "^29.6.3", + "@types/graceful-fs": "^4.1.3", + "@types/node": "*", + "anymatch": "^3.0.3", + "fb-watchman": "^2.0.0", + "fsevents": "^2.3.2", + "graceful-fs": "^4.2.9", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", + "jest-worker": "^29.7.0", + "micromatch": "^4.0.4", + "walker": "^1.0.8" + } + }, + "jest-leak-detector": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz", + "integrity": "sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==", + "dev": true, + "requires": { + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + } + }, + "jest-matcher-utils": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz", + "integrity": "sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==", + "dev": true, + "requires": { + "chalk": "^4.0.0", + "jest-diff": "^29.7.0", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + } + }, + "jest-message-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.7.0.tgz", + "integrity": "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.12.13", + "@jest/types": "^29.6.3", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + } + }, + "jest-mock": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.7.0.tgz", + "integrity": "sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==", + "dev": true, + "requires": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-util": "^29.7.0" + } + }, + "jest-pnp-resolver": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", + "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", + "dev": true, + "requires": {} + }, + "jest-regex-util": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz", + "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==", + "dev": true + }, + "jest-resolve": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.7.0.tgz", + "integrity": "sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==", + "dev": true, + "requires": { + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-pnp-resolver": "^1.2.2", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "resolve": "^1.20.0", + "resolve.exports": "^2.0.0", + "slash": "^3.0.0" + } + }, + "jest-resolve-dependencies": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.7.0.tgz", + "integrity": "sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==", + "dev": true, + "requires": { + "jest-regex-util": "^29.6.3", + "jest-snapshot": "^29.7.0" + } + }, + "jest-runner": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.7.0.tgz", + "integrity": "sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==", + "dev": true, + "requires": { + "@jest/console": "^29.7.0", + "@jest/environment": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "emittery": "^0.13.1", + "graceful-fs": "^4.2.9", + "jest-docblock": "^29.7.0", + "jest-environment-node": "^29.7.0", + "jest-haste-map": "^29.7.0", + "jest-leak-detector": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-resolve": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-util": "^29.7.0", + "jest-watcher": "^29.7.0", + "jest-worker": "^29.7.0", + "p-limit": "^3.1.0", + "source-map-support": "0.5.13" + } + }, + "jest-runtime": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.7.0.tgz", + "integrity": "sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==", + "dev": true, + "requires": { + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", + "@jest/globals": "^29.7.0", + "@jest/source-map": "^29.6.3", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "cjs-module-lexer": "^1.0.0", + "collect-v8-coverage": "^1.0.0", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-mock": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "slash": "^3.0.0", + "strip-bom": "^4.0.0" + } + }, + "jest-snapshot": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.7.0.tgz", + "integrity": "sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==", + "dev": true, + "requires": { + "@babel/core": "^7.11.6", + "@babel/generator": "^7.7.2", + "@babel/plugin-syntax-jsx": "^7.7.2", + "@babel/plugin-syntax-typescript": "^7.7.2", + "@babel/types": "^7.3.3", + "@jest/expect-utils": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "babel-preset-current-node-syntax": "^1.0.0", + "chalk": "^4.0.0", + "expect": "^29.7.0", + "graceful-fs": "^4.2.9", + "jest-diff": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "natural-compare": "^1.4.0", + "pretty-format": "^29.7.0", + "semver": "^7.5.3" + } + }, + "jest-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", + "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", + "dev": true, + "requires": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + } + }, + "jest-validate": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz", + "integrity": "sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==", + "dev": true, + "requires": { + "@jest/types": "^29.6.3", + "camelcase": "^6.2.0", + "chalk": "^4.0.0", + "jest-get-type": "^29.6.3", + "leven": "^3.1.0", + "pretty-format": "^29.7.0" + }, + "dependencies": { + "camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true + } + } + }, + "jest-watcher": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.7.0.tgz", + "integrity": "sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==", + "dev": true, + "requires": { + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "emittery": "^0.13.1", + "jest-util": "^29.7.0", + "string-length": "^4.0.1" + } + }, + "jest-worker": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", + "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", + "dev": true, + "requires": { + "@types/node": "*", + "jest-util": "^29.7.0", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "dependencies": { + "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 + }, + "supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "js-tokens": { + "version": "4.0.0", + "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" + } + }, + "jsesc": { + "version": "2.5.2", + "dev": true + }, + "json-parse-even-better-errors": { + "version": "2.3.1", + "dev": true + }, + "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", + "dev": true + }, + "json5": { + "version": "2.2.3", + "dev": true + }, + "kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "dev": true + }, + "kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", + "dev": true + }, + "leven": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", + "dev": true + }, + "levn": { + "version": "0.4.1", + "dev": true, + "requires": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + } + }, + "lines-and-columns": { + "version": "1.2.4", + "dev": true + }, + "locate-path": { + "version": "6.0.0", + "dev": true, + "requires": { + "p-locate": "^5.0.0" + } + }, + "lodash.memoize": { + "version": "4.1.2", + "dev": true + }, + "lodash.merge": { + "version": "4.6.2", + "dev": true + }, + "lru-cache": { + "version": "6.0.0", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "make-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", + "dev": true, + "requires": { + "semver": "^7.5.3" + } + }, + "make-error": { + "version": "1.3.6", + "dev": true + }, + "makeerror": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", + "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", + "dev": true, + "requires": { + "tmpl": "1.0.5" + } + }, + "map-obj": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.3.0.tgz", + "integrity": "sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==", + "dev": true + }, + "meow": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/meow/-/meow-11.0.0.tgz", + "integrity": "sha512-Cl0yeeIrko6d94KpUo1M+0X1sB14ikoaqlIGuTH1fW4I+E3+YljL54/hb/BWmVfrV9tTV9zU04+xjw08Fh2WkA==", + "dev": true, + "requires": { + "@types/minimist": "^1.2.2", + "camelcase-keys": "^8.0.2", + "decamelize": "^6.0.0", + "decamelize-keys": "^1.1.0", + "hard-rejection": "^2.1.0", + "minimist-options": "4.1.0", + "normalize-package-data": "^4.0.1", + "read-pkg-up": "^9.1.0", + "redent": "^4.0.0", + "trim-newlines": "^4.0.2", + "type-fest": "^3.1.0", + "yargs-parser": "^21.1.1" + }, + "dependencies": { + "type-fest": { + "version": "3.13.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-3.13.1.tgz", + "integrity": "sha512-tLq3bSNx+xSpwvAJnzrK0Ep5CLNWjvFTOp71URMaAEWBfRb9nnJiBoUe0tF8bI4ZFO3omgBR6NvnbzVUT3Ly4g==", + "dev": true + } + } + }, + "merge-stream": { + "version": "2.0.0", + "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", + "dev": true, + "requires": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + } + }, + "mimic-fn": { + "version": "2.1.0", + "dev": true + }, + "min-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", + "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", + "dev": true + }, + "minimatch": { + "version": "3.1.2", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist-options": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/minimist-options/-/minimist-options-4.1.0.tgz", + "integrity": "sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==", + "dev": true, + "requires": { + "arrify": "^1.0.1", + "is-plain-obj": "^1.1.0", + "kind-of": "^6.0.3" + } + }, + "ms": { + "version": "2.1.2" + }, + "natural-compare": { + "version": "1.4.0", + "dev": true + }, + "node-int64": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", + "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", + "dev": true + }, + "node-releases": { + "version": "2.0.6", + "dev": true + }, + "normalize-package-data": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-4.0.1.tgz", + "integrity": "sha512-EBk5QKKuocMJhB3BILuKhmaPjI8vNRSpIfO9woLC6NyHVkKKdVEdAO1mrT0ZfxNR1lKwCcTkuZfmGIFdizZ8Pg==", + "dev": true, + "requires": { + "hosted-git-info": "^5.0.0", + "is-core-module": "^2.8.1", + "semver": "^7.3.5", + "validate-npm-package-license": "^3.0.4" + } + }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true + }, + "npm-run-path": { + "version": "4.0.1", + "dev": true, + "requires": { + "path-key": "^3.0.0" + } + }, + "once": { + "version": "1.4.0", + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "onetime": { + "version": "5.1.2", + "dev": true, + "requires": { + "mimic-fn": "^2.1.0" + } + }, + "open": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/open/-/open-9.1.0.tgz", + "integrity": "sha512-OS+QTnw1/4vrf+9hh1jc1jnYjzSG4ttTBB8UxOwAnInG3Uo4ssetzC1ihqaIHjLJnA5GGlRl6QlZXOTQhRBUvg==", + "dev": true, + "requires": { + "default-browser": "^4.0.0", + "define-lazy-prop": "^3.0.0", + "is-inside-container": "^1.0.0", + "is-wsl": "^2.2.0" + } + }, + "open-cli": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/open-cli/-/open-cli-7.2.0.tgz", + "integrity": "sha512-1ANJc8oJ92FiaNZ0o2Hw4WBvDJoXs1P74aFMtpAvlbkIPV4uPcQvDz7V6kMOrsZkmB4tglrHVMlLQaafuUuxXg==", + "dev": true, + "requires": { + "file-type": "^18.2.1", + "get-stdin": "^9.0.0", + "meow": "^11.0.0", + "open": "^9.0.0", + "tempy": "^3.0.0" + } + }, + "optionator": { + "version": "0.9.3", + "dev": true, + "requires": { + "@aashutoshrathi/word-wrap": "^1.2.3", + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0" + } + }, + "p-limit": { + "version": "3.1.0", + "dev": true, + "requires": { + "yocto-queue": "^0.1.0" + } + }, + "p-locate": { + "version": "5.0.0", + "dev": true, + "requires": { + "p-limit": "^3.0.2" + } + }, + "p-try": { + "version": "2.2.0", + "dev": true + }, + "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" + } + }, + "parse-json": { + "version": "5.2.0", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + } + }, + "path-exists": { + "version": "4.0.0", + "dev": true + }, + "path-is-absolute": { + "version": "1.0.1", + "dev": true + }, + "path-key": { + "version": "3.1.1", + "dev": true + }, + "path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "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 + }, + "peek-readable": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/peek-readable/-/peek-readable-5.0.0.tgz", + "integrity": "sha512-YtCKvLUOvwtMGmrniQPdO7MwPjgkFBtFIrmfSbYmYuq3tKDV/mcfAhBth1+C3ru7uXIZasc/pHnb+YDYNkkj4A==", + "dev": true + }, + "picocolors": { + "version": "1.0.0", + "dev": true + }, + "picomatch": { + "version": "2.3.1", + "dev": true + }, + "pirates": { + "version": "4.0.5", + "dev": true + }, + "pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "requires": { + "find-up": "^4.0.0" + }, + "dependencies": { + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "requires": { + "p-locate": "^4.1.0" + } + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "requires": { + "p-limit": "^2.2.0" + } + } + } + }, + "prelude-ls": { + "version": "1.2.1", + "dev": true + }, + "prettier": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.1.1.tgz", + "integrity": "sha512-22UbSzg8luF4UuZtzgiUOfcGM8s4tjBv6dJRT7j275NXsy2jb4aJa4NNveul5x4eqlF1wuhuR2RElK71RvmVaw==", + "dev": true + }, + "prettier-linter-helpers": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", + "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", + "dev": true, + "requires": { + "fast-diff": "^1.1.2" + } + }, + "pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dev": true, + "requires": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "5.2.0", + "dev": true + } + } + }, + "prompts": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", + "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", + "dev": true, + "requires": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + } + }, + "punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true + }, + "pure-rand": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.0.3.tgz", + "integrity": "sha512-KddyFewCsO0j3+np81IQ+SweXLDnDQTs5s67BOnrYmYe/yNmUhttQyGsYzy8yUnoljGAQ9sl38YB4vH8ur7Y+w==", + "dev": true + }, + "queue-microtask": { + "version": "1.2.3" + }, + "quick-lru": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-6.1.2.tgz", + "integrity": "sha512-AAFUA5O1d83pIHEhJwWCq/RQcRukCkn/NSm2QsTEMle5f2hP0ChI2+3Xb051PZCkLryI/Ir1MVKviT2FIloaTQ==", + "dev": true + }, + "react-is": { + "version": "18.2.0", + "dev": true + }, + "read-pkg": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-7.1.0.tgz", + "integrity": "sha512-5iOehe+WF75IccPc30bWTbpdDQLOCc3Uu8bi3Dte3Eueij81yx1Mrufk8qBx/YAbR4uL1FdUr+7BKXDwEtisXg==", + "dev": true, + "requires": { + "@types/normalize-package-data": "^2.4.1", + "normalize-package-data": "^3.0.2", + "parse-json": "^5.2.0", + "type-fest": "^2.0.0" + }, + "dependencies": { + "hosted-git-info": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz", + "integrity": "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "normalize-package-data": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.3.tgz", + "integrity": "sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA==", + "dev": true, + "requires": { + "hosted-git-info": "^4.0.1", + "is-core-module": "^2.5.0", + "semver": "^7.3.4", + "validate-npm-package-license": "^3.0.1" + } + }, + "type-fest": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz", + "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==", + "dev": true + } + } + }, + "read-pkg-up": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-9.1.0.tgz", + "integrity": "sha512-vaMRR1AC1nrd5CQM0PhlRsO5oc2AAigqr7cCrZ/MW/Rsaflz4RlgzkpL4qoU/z1F6wrbd85iFv1OQj/y5RdGvg==", + "dev": true, + "requires": { + "find-up": "^6.3.0", + "read-pkg": "^7.1.0", + "type-fest": "^2.5.0" + }, + "dependencies": { + "find-up": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-6.3.0.tgz", + "integrity": "sha512-v2ZsoEuVHYy8ZIlYqwPe/39Cy+cFDzp4dXPaxNvkEuouymu+2Jbz0PxpKarJHYJTmv2HWT3O382qY8l4jMWthw==", + "dev": true, + "requires": { + "locate-path": "^7.1.0", + "path-exists": "^5.0.0" + } + }, + "locate-path": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-7.2.0.tgz", + "integrity": "sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA==", + "dev": true, + "requires": { + "p-locate": "^6.0.0" + } + }, + "p-limit": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-4.0.0.tgz", + "integrity": "sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==", + "dev": true, + "requires": { + "yocto-queue": "^1.0.0" + } + }, + "p-locate": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-6.0.0.tgz", + "integrity": "sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==", + "dev": true, + "requires": { + "p-limit": "^4.0.0" + } + }, + "path-exists": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-5.0.0.tgz", + "integrity": "sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==", + "dev": true + }, + "type-fest": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz", + "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==", + "dev": true + }, + "yocto-queue": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.0.0.tgz", + "integrity": "sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==", + "dev": true + } + } + }, + "readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, + "readable-web-to-node-stream": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/readable-web-to-node-stream/-/readable-web-to-node-stream-3.0.2.tgz", + "integrity": "sha512-ePeK6cc1EcKLEhJFt/AebMCLL+GgSKhuygrZ/GLaKZYEecIgIECf4UaUuaByiGtzckwR4ain9VzUh95T1exYGw==", + "dev": true, + "requires": { + "readable-stream": "^3.6.0" + } + }, + "redent": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-4.0.0.tgz", + "integrity": "sha512-tYkDkVVtYkSVhuQ4zBgfvciymHaeuel+zFKXShfDnFP5SyVEP7qo70Rf1jTOTCx3vGNAbnEi/xFkcfQVMIBWag==", + "dev": true, + "requires": { + "indent-string": "^5.0.0", + "strip-indent": "^4.0.0" + } + }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true + }, + "resolve": { + "version": "1.22.4", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.4.tgz", + "integrity": "sha512-PXNdCiPqDqeUou+w1C2eTQbNfxKSuMxqTCuvlmmMsk1NWHL5fRrhY6Pl0qEYYc6+QqGClco1Qj8XnjPego4wfg==", + "dev": true, + "requires": { + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + } + }, + "resolve-cwd": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", + "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", + "dev": true, + "requires": { + "resolve-from": "^5.0.0" + }, + "dependencies": { + "resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "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 + }, + "resolve.exports": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.2.tgz", + "integrity": "sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg==", + "dev": true + }, + "reusify": { + "version": "1.0.4", + "dev": true + }, + "rimraf": { + "version": "3.0.2", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, + "run-applescript": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-5.0.0.tgz", + "integrity": "sha512-XcT5rBksx1QdIhlFOCtgZkB99ZEouFZ1E2Kc2LHqNW13U3/74YGdkQRmThTwxy4QIyookibDKYZOPqX//6BlAg==", + "dev": true, + "requires": { + "execa": "^5.0.0" + } + }, + "run-parallel": { + "version": "1.2.0", + "dev": true, + "requires": { + "queue-microtask": "^1.2.2" + } + }, + "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==", + "dev": true + }, + "sdp-transform": { + "version": "2.14.1" + }, + "semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "shebang-command": { + "version": "2.0.0", + "dev": true, + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "shebang-regex": { + "version": "3.0.0", + "dev": true + }, + "signal-exit": { + "version": "3.0.7", + "dev": true + }, + "sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", + "dev": true + }, + "slash": { + "version": "3.0.0", + "dev": true + }, + "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 + }, + "source-map-support": { + "version": "0.5.13", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", + "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "spdx-correct": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", + "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", + "dev": true, + "requires": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-exceptions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", + "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", + "dev": true + }, + "spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "dev": true, + "requires": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-license-ids": { + "version": "3.0.16", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.16.tgz", + "integrity": "sha512-eWN+LnM3GR6gPu35WxNgbGl8rmY1AEmoMDvL/QD6zYmPWgywxWqJWNdLGT+ke8dKNWrcYgYjPpG5gbTfghP8rw==", + "dev": true + }, + "sprintf-js": { + "version": "1.0.3", + "dev": true + }, + "stack-utils": { + "version": "2.0.6", + "dev": true, + "requires": { + "escape-string-regexp": "^2.0.0" + }, + "dependencies": { + "escape-string-regexp": { + "version": "2.0.0", + "dev": true + } + } + }, + "string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "requires": { + "safe-buffer": "~5.2.0" + } + }, + "string-length": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", + "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", + "dev": true, + "requires": { + "char-regex": "^1.0.2", + "strip-ansi": "^6.0.0" + } + }, + "string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + }, + "strip-ansi": { + "version": "6.0.1", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1" + } + }, + "strip-bom": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", + "dev": true + }, + "strip-final-newline": { + "version": "2.0.0", + "dev": true + }, + "strip-indent": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-4.0.0.tgz", + "integrity": "sha512-mnVSV2l+Zv6BLpSD/8V87CW/y9EmmbYzGCIavsnsI6/nwn26DwffM/yztm30Z/I2DY9wdS3vXVCMnHDgZaVNoA==", + "dev": true, + "requires": { + "min-indent": "^1.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 + }, + "strtok3": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/strtok3/-/strtok3-7.0.0.tgz", + "integrity": "sha512-pQ+V+nYQdC5H3Q7qBZAz/MO6lwGhoC2gOAjuouGf/VO0m7vQRh8QNMl2Uf6SwAtzZ9bOw3UIeBukEGNJl5dtXQ==", + "dev": true, + "requires": { + "@tokenizer/token": "^0.3.0", + "peek-readable": "^5.0.0" + } + }, + "supports-color": { + "version": "9.4.0" + }, + "supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true + }, + "synckit": { + "version": "0.8.8", + "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.8.8.tgz", + "integrity": "sha512-HwOKAP7Wc5aRGYdKH+dw0PRRpbO841v2DENBtjnR5HFWoiNByAl7vrx3p0G/rCyYXQsrxqtX48TImFtPcIHSpQ==", + "dev": true, + "requires": { + "@pkgr/core": "^0.1.0", + "tslib": "^2.6.2" + }, + "dependencies": { + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "dev": true + } + } + }, + "temp-dir": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-3.0.0.tgz", + "integrity": "sha512-nHc6S/bwIilKHNRgK/3jlhDoIHcp45YgyiwcAk46Tr0LfEqGBVpmiAyuiuxeVE44m3mXnEeVhaipLOEWmH+Njw==", + "dev": true + }, + "tempy": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/tempy/-/tempy-3.1.0.tgz", + "integrity": "sha512-7jDLIdD2Zp0bDe5r3D2qtkd1QOCacylBuL7oa4udvN6v2pqr4+LcCr67C8DR1zkpaZ8XosF5m1yQSabKAW6f2g==", + "dev": true, + "requires": { + "is-stream": "^3.0.0", + "temp-dir": "^3.0.0", + "type-fest": "^2.12.2", + "unique-string": "^3.0.0" + }, + "dependencies": { + "is-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", + "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", + "dev": true + }, + "type-fest": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz", + "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==", + "dev": true + } + } + }, + "test-exclude": { + "version": "6.0.0", + "dev": true, + "requires": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + } + }, + "text-table": { + "version": "0.2.0", + "dev": true + }, + "titleize": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/titleize/-/titleize-3.0.0.tgz", + "integrity": "sha512-KxVu8EYHDPBdUYdKZdKtU2aj2XfEx9AfjXxE/Aj0vT06w2icA09Vus1rh6eSu1y01akYg6BjIK/hxyLJINoMLQ==", + "dev": true + }, + "tmpl": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", + "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", + "dev": true + }, + "to-fast-properties": { + "version": "2.0.0", + "dev": true + }, + "to-regex-range": { + "version": "5.0.1", + "dev": true, + "requires": { + "is-number": "^7.0.0" + } + }, + "token-types": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/token-types/-/token-types-5.0.1.tgz", + "integrity": "sha512-Y2fmSnZjQdDb9W4w4r1tswlMHylzWIeOKpx0aZH9BgGtACHhrk3OkT52AzwcuqTRBZtvvnTjDBh8eynMulu8Vg==", + "dev": true, + "requires": { + "@tokenizer/token": "^0.3.0", + "ieee754": "^1.2.1" + } + }, + "trim-newlines": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-4.1.1.tgz", + "integrity": "sha512-jRKj0n0jXWo6kh62nA5TEh3+4igKDXLvzBJcPpiizP7oOolUrYIxmVBG9TOtHYFHoddUk6YvAkGeGoSVTXfQXQ==", + "dev": true + }, + "ts-api-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.0.1.tgz", + "integrity": "sha512-lC/RGlPmwdrIBFTX59wwNzqh7aR2otPNPR/5brHZm/XKFYKsfqxihXUe9pU3JI+3vGkl+vyCoNNnPhJn3aLK1A==", + "dev": true, + "requires": {} + }, + "ts-jest": { + "version": "29.1.1", + "dev": true, + "requires": { + "bs-logger": "0.x", + "fast-json-stable-stringify": "2.x", + "jest-util": "^29.0.0", + "json5": "^2.2.3", + "lodash.memoize": "4.x", + "make-error": "1.x", + "semver": "^7.5.3", + "yargs-parser": "^21.0.1" + } + }, + "tslib": { + "version": "1.14.1", + "dev": true + }, + "tsutils": { + "version": "3.21.0", + "dev": true, + "requires": { + "tslib": "^1.8.1" + } + }, + "type-check": { + "version": "0.4.0", + "dev": true, + "requires": { + "prelude-ls": "^1.2.1" + } + }, + "type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true + }, + "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 + }, + "typescript": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz", + "integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==", + "dev": true + }, + "ua-parser-js": { + "version": "1.0.37", + "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-1.0.37.tgz", + "integrity": "sha512-bhTyI94tZofjo+Dn8SN6Zv8nBDvyXTymAdM3LDI/0IboIUwTu1rEhW7v2TfiVsoYWgkQ4kOVqnI8APUFbIQIFQ==" + }, + "unique-string": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-3.0.0.tgz", + "integrity": "sha512-VGXBUVwxKMBUznyffQweQABPRRW1vHZAbadFZud4pLFAqRGvv/96vafgjWFqzourzr8YonlQiPgH0YCJfawoGQ==", + "dev": true, + "requires": { + "crypto-random-string": "^4.0.0" + } + }, + "untildify": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/untildify/-/untildify-4.0.0.tgz", + "integrity": "sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==", + "dev": true + }, + "update-browserslist-db": { + "version": "1.0.10", + "dev": true, + "requires": { + "escalade": "^3.1.1", + "picocolors": "^1.0.0" + } + }, + "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" + } + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true + }, + "uuid": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz", + "integrity": "sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==" + }, + "v8-to-istanbul": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.1.0.tgz", + "integrity": "sha512-6z3GW9x8G1gd+JIIgQQQxXuiJtCXeAjp6RaPEPLv62mH3iPHPxV6W3robxtCzNErRo6ZwTmzWhsbNvjyEBKzKA==", + "dev": true, + "requires": { + "@jridgewell/trace-mapping": "^0.3.12", + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^1.6.0" + }, + "dependencies": { + "convert-source-map": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", + "dev": true + } + } + }, + "validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, + "requires": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "walker": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", + "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", + "dev": true, + "requires": { + "makeerror": "1.0.12" + } + }, + "which": { + "version": "2.0.2", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, + "wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + } + }, + "wrappy": { + "version": "1.0.2", + "dev": true + }, + "write-file-atomic": { + "version": "4.0.2", + "dev": true, + "requires": { + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.7" + } + }, + "y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true + }, + "yallist": { + "version": "4.0.0", + "dev": true + }, + "yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "requires": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + } + }, + "yargs-parser": { + "version": "21.1.1", + "dev": true + }, + "yocto-queue": { + "version": "0.1.0", + "dev": true + } + } } diff --git a/package.json b/package.json index b1823a66..dcf25432 100644 --- a/package.json +++ b/package.json @@ -1,91 +1,95 @@ { - "name": "mediasoup-client", - "version": "3.7.1", - "description": "mediasoup client side JavaScript library", - "contributors": [ - "Iñaki Baz Castillo (https://inakibaz.me)", - "José Luis Millán (https://github.com/jmillan)" - ], - "license": "ISC", - "homepage": "https://mediasoup.org", - "repository": { - "type": "git", - "url": "https://github.com/versatica/mediasoup-client.git" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/mediasoup" - }, - "main": "lib/index.js", - "types": "lib/index.d.ts", - "files": [ - "npm-scripts.mjs", - "lib" - ], - "engines": { - "node": ">=16" - }, - "keywords": [ - "webrtc", - "ortc", - "browser", - "nodejs" - ], - "scripts": { - "prepare": "node npm-scripts.mjs prepare", - "typescript:build": "node npm-scripts.mjs typescript:build", - "typescript:watch": "node npm-scripts.mjs typescript:watch", - "lint": "node npm-scripts.mjs lint", - "test": "node npm-scripts.mjs test", - "coverage": "node npm-scripts.mjs coverage", - "release:check": "node npm-scripts.mjs release:check", - "release": "node npm-scripts.mjs release" - }, - "jest": { - "verbose": true, - "testEnvironment": "node", - "testRegex": "src/tests/test.*\\.ts", - "transform": { - "^.*\\.ts$": [ - "ts-jest", - { - "diagnostics": { - "ignoreCodes": [ - "TS151001" - ] - } - } - ] - }, - "coveragePathIgnorePatterns": [ - "src/tests" - ], - "cacheDirectory": ".cache/jest" - }, - "dependencies": { - "@types/debug": "^4.1.12", - "awaitqueue": "^3.0.2", - "debug": "^4.3.4", - "events": "^3.3.0", - "fake-mediastreamtrack": "^1.2.0", - "h264-profile-level-id": "^2.0.0", - "queue-microtask": "^1.2.3", - "sdp-transform": "^2.14.1", - "supports-color": "^9.4.0", - "ua-parser-js": "^1.0.37" - }, - "devDependencies": { - "@types/events": "^3.0.3", - "@types/jest": "^29.5.11", - "@types/sdp-transform": "^2.4.9", - "@types/ua-parser-js": "^0.7.39", - "@typescript-eslint/eslint-plugin": "^6.15.0", - "@typescript-eslint/parser": "^6.15.0", - "eslint": "^8.56.0", - "eslint-plugin-jest": "^27.6.0", - "jest": "^29.7.0", - "open-cli": "^7.2.0", - "ts-jest": "^29.1.1", - "typescript": "^5.3.3" - } + "name": "mediasoup-client", + "version": "3.7.1", + "description": "mediasoup client side JavaScript library", + "contributors": [ + "Iñaki Baz Castillo (https://inakibaz.me)", + "José Luis Millán (https://github.com/jmillan)" + ], + "license": "ISC", + "homepage": "https://mediasoup.org", + "repository": { + "type": "git", + "url": "https://github.com/versatica/mediasoup-client.git" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mediasoup" + }, + "main": "lib/index.js", + "types": "lib/index.d.ts", + "files": [ + "npm-scripts.mjs", + "lib" + ], + "engines": { + "node": ">=16" + }, + "keywords": [ + "webrtc", + "ortc", + "browser", + "nodejs" + ], + "scripts": { + "prepare": "node npm-scripts.mjs prepare", + "typescript:build": "node npm-scripts.mjs typescript:build", + "typescript:watch": "node npm-scripts.mjs typescript:watch", + "lint": "node npm-scripts.mjs lint", + "format": "node npm-scripts.mjs format", + "test": "node npm-scripts.mjs test", + "coverage": "node npm-scripts.mjs coverage", + "release:check": "node npm-scripts.mjs release:check", + "release": "node npm-scripts.mjs release" + }, + "jest": { + "verbose": true, + "testEnvironment": "node", + "testRegex": "src/test/test.*\\.ts", + "transform": { + "^.*\\.ts$": [ + "ts-jest", + { + "diagnostics": { + "ignoreCodes": [ + "TS151001" + ] + } + } + ] + }, + "coveragePathIgnorePatterns": [ + "src/test" + ], + "cacheDirectory": ".cache/jest" + }, + "dependencies": { + "@types/debug": "^4.1.12", + "awaitqueue": "^3.0.2", + "debug": "^4.3.4", + "events": "^3.3.0", + "fake-mediastreamtrack": "^1.2.0", + "h264-profile-level-id": "^2.0.0", + "queue-microtask": "^1.2.3", + "sdp-transform": "^2.14.1", + "supports-color": "^9.4.0", + "ua-parser-js": "^1.0.37" + }, + "devDependencies": { + "@types/events": "^3.0.3", + "@types/jest": "^29.5.11", + "@types/sdp-transform": "^2.4.9", + "@types/ua-parser-js": "^0.7.39", + "@typescript-eslint/eslint-plugin": "^6.18.1", + "@typescript-eslint/parser": "^6.18.1", + "eslint": "^8.56.0", + "eslint-config-prettier": "^9.1.0", + "eslint-plugin-jest": "^27.6.1", + "eslint-plugin-prettier": "^5.1.2", + "jest": "^29.7.0", + "open-cli": "^7.2.0", + "prettier": "^3.1.1", + "ts-jest": "^29.1.1", + "typescript": "^5.3.3" + } } diff --git a/src/Consumer.ts b/src/Consumer.ts index 983bda46..eb0a901a 100644 --- a/src/Consumer.ts +++ b/src/Consumer.ts @@ -6,8 +6,7 @@ import { AppData } from './types'; const logger = new Logger('Consumer'); -export type ConsumerOptions = -{ +export type ConsumerOptions = { id?: string; producerId?: string; kind?: 'audio' | 'video'; @@ -16,8 +15,7 @@ export type ConsumerOptions = appData?: ConsumerAppData; }; -export type ConsumerEvents = -{ +export type ConsumerEvents = { transportclose: []; trackended: []; // Private events. @@ -27,17 +25,16 @@ export type ConsumerEvents = '@resume': []; }; -export type ConsumerObserverEvents = -{ +export type ConsumerObserverEvents = { close: []; pause: []; resume: []; trackended: []; }; -export class Consumer - extends EnhancedEventEmitter -{ +export class Consumer< + ConsumerAppData extends AppData = AppData, +> extends EnhancedEventEmitter { // Id. private readonly _id: string; // Local id. @@ -57,29 +54,26 @@ export class Consumer // App custom data. private _appData: ConsumerAppData; // Observer instance. - protected readonly _observer = new EnhancedEventEmitter(); - - constructor( - { - id, - localId, - producerId, - rtpReceiver, - track, - rtpParameters, - appData - }: - { - id: string; - localId: string; - producerId: string; - rtpReceiver?: RTCRtpReceiver; - track: MediaStreamTrack; - rtpParameters: RtpParameters; - appData?: ConsumerAppData; - } - ) - { + protected readonly _observer = + new EnhancedEventEmitter(); + + constructor({ + id, + localId, + producerId, + rtpReceiver, + track, + rtpParameters, + appData, + }: { + id: string; + localId: string; + producerId: string; + rtpReceiver?: RTCRtpReceiver; + track: MediaStreamTrack; + rtpParameters: RtpParameters; + appData?: ConsumerAppData; + }) { super(); logger.debug('constructor()'); @@ -91,7 +85,7 @@ export class Consumer this._track = track; this._rtpParameters = rtpParameters; this._paused = !track.enabled; - this._appData = appData || {} as ConsumerAppData; + this._appData = appData || ({} as ConsumerAppData); this.onTrackEnded = this.onTrackEnded.bind(this); this.handleTrack(); @@ -100,103 +94,89 @@ export class Consumer /** * Consumer id. */ - get id(): string - { + get id(): string { return this._id; } /** * Local id. */ - get localId(): string - { + get localId(): string { return this._localId; } /** * Associated Producer id. */ - get producerId(): string - { + get producerId(): string { return this._producerId; } /** * Whether the Consumer is closed. */ - get closed(): boolean - { + get closed(): boolean { return this._closed; } /** * Media kind. */ - get kind(): MediaKind - { + get kind(): MediaKind { return this._track.kind as MediaKind; } /** * Associated RTCRtpReceiver. */ - get rtpReceiver(): RTCRtpReceiver | undefined - { + get rtpReceiver(): RTCRtpReceiver | undefined { return this._rtpReceiver; } /** * The associated track. */ - get track(): MediaStreamTrack - { + get track(): MediaStreamTrack { return this._track; } /** * RTP parameters. */ - get rtpParameters(): RtpParameters - { + get rtpParameters(): RtpParameters { return this._rtpParameters; } /** * Whether the Consumer is paused. */ - get paused(): boolean - { + get paused(): boolean { return this._paused; } /** * App custom data. */ - get appData(): ConsumerAppData - { + get appData(): ConsumerAppData { return this._appData; } /** * App custom data setter. */ - set appData(appData: ConsumerAppData) - { + set appData(appData: ConsumerAppData) { this._appData = appData; } - get observer(): EnhancedEventEmitter - { + get observer(): EnhancedEventEmitter { return this._observer; } /** * Closes the Consumer. */ - close(): void - { - if (this._closed) - { + close(): void { + if (this._closed) { return; } @@ -215,10 +195,8 @@ export class Consumer /** * Transport was closed. */ - transportClosed(): void - { - if (this._closed) - { + transportClosed(): void { + if (this._closed) { return; } @@ -237,39 +215,29 @@ export class Consumer /** * Get associated RTCRtpReceiver stats. */ - async getStats(): Promise - { - if (this._closed) - { + async getStats(): Promise { + if (this._closed) { throw new InvalidStateError('closed'); } - return new Promise((resolve, reject) => - { - this.safeEmit( - '@getstats', - resolve, - reject - ); + return new Promise((resolve, reject) => { + this.safeEmit('@getstats', resolve, reject); }); } /** * Pauses receiving media. */ - pause(): void - { + pause(): void { logger.debug('pause()'); - if (this._closed) - { + if (this._closed) { logger.error('pause() | Consumer closed'); return; } - if (this._paused) - { + if (this._paused) { logger.debug('pause() | Consumer is already paused'); return; @@ -287,19 +255,16 @@ export class Consumer /** * Resumes receiving media. */ - resume(): void - { + resume(): void { logger.debug('resume()'); - if (this._closed) - { + if (this._closed) { logger.error('resume() | Consumer closed'); return; } - if (!this._paused) - { + if (!this._paused) { logger.debug('resume() | Consumer is already resumed'); return; @@ -314,8 +279,7 @@ export class Consumer this._observer.safeEmit('resume'); } - private onTrackEnded(): void - { + private onTrackEnded(): void { logger.debug('track "ended" event'); this.safeEmit('trackended'); @@ -324,19 +288,14 @@ export class Consumer this._observer.safeEmit('trackended'); } - private handleTrack(): void - { + private handleTrack(): void { this._track.addEventListener('ended', this.onTrackEnded); } - private destroyTrack(): void - { - try - { + private destroyTrack(): void { + try { this._track.removeEventListener('ended', this.onTrackEnded); this._track.stop(); - } - catch (error) - {} + } catch (error) {} } } diff --git a/src/DataConsumer.ts b/src/DataConsumer.ts index 5193d514..26721063 100644 --- a/src/DataConsumer.ts +++ b/src/DataConsumer.ts @@ -6,17 +6,16 @@ import { AppData } from './types'; const logger = new Logger('DataConsumer'); export type DataConsumerOptions = -{ - id?: string; - dataProducerId?: string; - sctpStreamParameters: SctpStreamParameters; - label?: string; - protocol?: string; - appData?: DataConsumerAppData; -}; - -export type DataConsumerEvents = -{ + { + id?: string; + dataProducerId?: string; + sctpStreamParameters: SctpStreamParameters; + label?: string; + protocol?: string; + appData?: DataConsumerAppData; + }; + +export type DataConsumerEvents = { transportclose: []; open: []; error: [Error]; @@ -26,14 +25,13 @@ export type DataConsumerEvents = '@close': []; }; -export type DataConsumerObserverEvents = -{ +export type DataConsumerObserverEvents = { close: []; }; -export class DataConsumer - extends EnhancedEventEmitter -{ +export class DataConsumer< + DataConsumerAppData extends AppData = AppData, +> extends EnhancedEventEmitter { // Id. private readonly _id: string; // Associated DataProducer Id. @@ -47,25 +45,22 @@ export class DataConsumer // App custom data. private _appData: DataConsumerAppData; // Observer instance. - protected readonly _observer = new EnhancedEventEmitter(); - - constructor( - { - id, - dataProducerId, - dataChannel, - sctpStreamParameters, - appData - }: - { - id: string; - dataProducerId: string; - dataChannel: RTCDataChannel; - sctpStreamParameters: SctpStreamParameters; - appData?: DataConsumerAppData; - } - ) - { + protected readonly _observer = + new EnhancedEventEmitter(); + + constructor({ + id, + dataProducerId, + dataChannel, + sctpStreamParameters, + appData, + }: { + id: string; + dataProducerId: string; + dataChannel: RTCDataChannel; + sctpStreamParameters: SctpStreamParameters; + appData?: DataConsumerAppData; + }) { super(); logger.debug('constructor()'); @@ -74,7 +69,7 @@ export class DataConsumer this._dataProducerId = dataProducerId; this._dataChannel = dataChannel; this._sctpStreamParameters = sctpStreamParameters; - this._appData = appData || {} as DataConsumerAppData; + this._appData = appData || ({} as DataConsumerAppData); this.handleDataChannel(); } @@ -82,103 +77,89 @@ export class DataConsumer /** * DataConsumer id. */ - get id(): string - { + get id(): string { return this._id; } /** * Associated DataProducer id. */ - get dataProducerId(): string - { + get dataProducerId(): string { return this._dataProducerId; } /** * Whether the DataConsumer is closed. */ - get closed(): boolean - { + get closed(): boolean { return this._closed; } /** * SCTP stream parameters. */ - get sctpStreamParameters(): SctpStreamParameters - { + get sctpStreamParameters(): SctpStreamParameters { return this._sctpStreamParameters; } /** * DataChannel readyState. */ - get readyState(): RTCDataChannelState - { + get readyState(): RTCDataChannelState { return this._dataChannel.readyState; } /** * DataChannel label. */ - get label(): string - { + get label(): string { return this._dataChannel.label; } /** * DataChannel protocol. */ - get protocol(): string - { + get protocol(): string { return this._dataChannel.protocol; } /** * DataChannel binaryType. */ - get binaryType(): BinaryType - { + get binaryType(): BinaryType { return this._dataChannel.binaryType; } /** * Set DataChannel binaryType. */ - set binaryType(binaryType: BinaryType) - { + set binaryType(binaryType: BinaryType) { this._dataChannel.binaryType = binaryType; } /** * App custom data. */ - get appData(): DataConsumerAppData - { + get appData(): DataConsumerAppData { return this._appData; } /** * App custom data setter. */ - set appData(appData: DataConsumerAppData) - { + set appData(appData: DataConsumerAppData) { this._appData = appData; } - get observer(): EnhancedEventEmitter - { + get observer(): EnhancedEventEmitter { return this._observer; } /** * Closes the DataConsumer. */ - close(): void - { - if (this._closed) - { + close(): void { + if (this._closed) { return; } @@ -197,10 +178,8 @@ export class DataConsumer /** * Transport was closed. */ - transportClosed(): void - { - if (this._closed) - { + transportClosed(): void { + if (this._closed) { return; } @@ -216,12 +195,9 @@ export class DataConsumer this._observer.safeEmit('close'); } - private handleDataChannel(): void - { - this._dataChannel.addEventListener('open', () => - { - if (this._closed) - { + private handleDataChannel(): void { + this._dataChannel.addEventListener('open', () => { + if (this._closed) { return; } @@ -230,38 +206,32 @@ export class DataConsumer this.safeEmit('open'); }); - this._dataChannel.addEventListener('error', (event: any) => - { - if (this._closed) - { + this._dataChannel.addEventListener('error', (event: any) => { + if (this._closed) { return; } let { error } = event; - if (!error) - { + if (!error) { error = new Error('unknown DataChannel error'); } - if (error.errorDetail === 'sctp-failure') - { + if (error.errorDetail === 'sctp-failure') { logger.error( 'DataChannel SCTP error [sctpCauseCode:%s]: %s', - error.sctpCauseCode, error.message); - } - else - { + error.sctpCauseCode, + error.message, + ); + } else { logger.error('DataChannel "error" event: %o', error); } this.safeEmit('error', error); }); - this._dataChannel.addEventListener('close', () => - { - if (this._closed) - { + this._dataChannel.addEventListener('close', () => { + if (this._closed) { return; } @@ -276,10 +246,8 @@ export class DataConsumer this._observer.safeEmit('close'); }); - this._dataChannel.addEventListener('message', (event: any) => - { - if (this._closed) - { + this._dataChannel.addEventListener('message', (event: any) => { + if (this._closed) { return; } diff --git a/src/DataProducer.ts b/src/DataProducer.ts index 81581143..98e2057f 100644 --- a/src/DataProducer.ts +++ b/src/DataProducer.ts @@ -7,17 +7,16 @@ import { AppData } from './types'; const logger = new Logger('DataProducer'); export type DataProducerOptions = -{ - ordered?: boolean; - maxPacketLifeTime?: number; - maxRetransmits?: number; - label?: string; - protocol?: string; - appData?: DataProducerAppData; -}; - -export type DataProducerEvents = -{ + { + ordered?: boolean; + maxPacketLifeTime?: number; + maxRetransmits?: number; + label?: string; + protocol?: string; + appData?: DataProducerAppData; + }; + +export type DataProducerEvents = { transportclose: []; open: []; error: [Error]; @@ -27,14 +26,13 @@ export type DataProducerEvents = '@close': []; }; -export type DataProducerObserverEvents = -{ +export type DataProducerObserverEvents = { close: []; }; -export class DataProducer - extends EnhancedEventEmitter -{ +export class DataProducer< + DataProducerAppData extends AppData = AppData, +> extends EnhancedEventEmitter { // Id. private readonly _id: string; // The underlying RTCDataChannel instance. @@ -46,23 +44,20 @@ export class DataProducer // App custom data. private _appData: DataProducerAppData; // Observer instance. - protected readonly _observer = new EnhancedEventEmitter(); - - constructor( - { - id, - dataChannel, - sctpStreamParameters, - appData - }: - { - id: string; - dataChannel: RTCDataChannel; - sctpStreamParameters: SctpStreamParameters; - appData?: DataProducerAppData; - } - ) - { + protected readonly _observer = + new EnhancedEventEmitter(); + + constructor({ + id, + dataChannel, + sctpStreamParameters, + appData, + }: { + id: string; + dataChannel: RTCDataChannel; + sctpStreamParameters: SctpStreamParameters; + appData?: DataProducerAppData; + }) { super(); logger.debug('constructor()'); @@ -70,7 +65,7 @@ export class DataProducer this._id = id; this._dataChannel = dataChannel; this._sctpStreamParameters = sctpStreamParameters; - this._appData = appData || {} as DataProducerAppData; + this._appData = appData || ({} as DataProducerAppData); this.handleDataChannel(); } @@ -78,103 +73,89 @@ export class DataProducer /** * DataProducer id. */ - get id(): string - { + get id(): string { return this._id; } /** * Whether the DataProducer is closed. */ - get closed(): boolean - { + get closed(): boolean { return this._closed; } /** * SCTP stream parameters. */ - get sctpStreamParameters(): SctpStreamParameters - { + get sctpStreamParameters(): SctpStreamParameters { return this._sctpStreamParameters; } /** * DataChannel readyState. */ - get readyState(): RTCDataChannelState - { + get readyState(): RTCDataChannelState { return this._dataChannel.readyState; } /** * DataChannel label. */ - get label(): string - { + get label(): string { return this._dataChannel.label; } /** * DataChannel protocol. */ - get protocol(): string - { + get protocol(): string { return this._dataChannel.protocol; } /** * DataChannel bufferedAmount. */ - get bufferedAmount(): number - { + get bufferedAmount(): number { return this._dataChannel.bufferedAmount; } /** * DataChannel bufferedAmountLowThreshold. */ - get bufferedAmountLowThreshold(): number - { + get bufferedAmountLowThreshold(): number { return this._dataChannel.bufferedAmountLowThreshold; } /** * Set DataChannel bufferedAmountLowThreshold. */ - set bufferedAmountLowThreshold(bufferedAmountLowThreshold: number) - { + set bufferedAmountLowThreshold(bufferedAmountLowThreshold: number) { this._dataChannel.bufferedAmountLowThreshold = bufferedAmountLowThreshold; } /** * App custom data. */ - get appData(): DataProducerAppData - { + get appData(): DataProducerAppData { return this._appData; } /** * App custom data setter. */ - set appData(appData: DataProducerAppData) - { + set appData(appData: DataProducerAppData) { this._appData = appData; } - get observer(): EnhancedEventEmitter - { + get observer(): EnhancedEventEmitter { return this._observer; } /** * Closes the DataProducer. */ - close(): void - { - if (this._closed) - { + close(): void { + if (this._closed) { return; } @@ -193,10 +174,8 @@ export class DataProducer /** * Transport was closed. */ - transportClosed(): void - { - if (this._closed) - { + transportClosed(): void { + if (this._closed) { return; } @@ -217,24 +196,19 @@ export class DataProducer * * @param {String|Blob|ArrayBuffer|ArrayBufferView} data. */ - send(data: any): void - { + send(data: any): void { logger.debug('send()'); - if (this._closed) - { + if (this._closed) { throw new InvalidStateError('closed'); } this._dataChannel.send(data); } - private handleDataChannel(): void - { - this._dataChannel.addEventListener('open', () => - { - if (this._closed) - { + private handleDataChannel(): void { + this._dataChannel.addEventListener('open', () => { + if (this._closed) { return; } @@ -243,38 +217,32 @@ export class DataProducer this.safeEmit('open'); }); - this._dataChannel.addEventListener('error', (event: any) => - { - if (this._closed) - { + this._dataChannel.addEventListener('error', (event: any) => { + if (this._closed) { return; } let { error } = event; - if (!error) - { + if (!error) { error = new Error('unknown DataChannel error'); } - if (error.errorDetail === 'sctp-failure') - { + if (error.errorDetail === 'sctp-failure') { logger.error( 'DataChannel SCTP error [sctpCauseCode:%s]: %s', - error.sctpCauseCode, error.message); - } - else - { + error.sctpCauseCode, + error.message, + ); + } else { logger.error('DataChannel "error" event: %o', error); } this.safeEmit('error', error); }); - this._dataChannel.addEventListener('close', () => - { - if (this._closed) - { + this._dataChannel.addEventListener('close', () => { + if (this._closed) { return; } @@ -289,21 +257,18 @@ export class DataProducer this._observer.safeEmit('close'); }); - this._dataChannel.addEventListener('message', () => - { - if (this._closed) - { + this._dataChannel.addEventListener('message', () => { + if (this._closed) { return; } logger.warn( - 'DataChannel "message" event in a DataProducer, message discarded'); + 'DataChannel "message" event in a DataProducer, message discarded', + ); }); - this._dataChannel.addEventListener('bufferedamountlow', () => - { - if (this._closed) - { + this._dataChannel.addEventListener('bufferedamountlow', () => { + if (this._closed) { return; } diff --git a/src/Device.ts b/src/Device.ts index 34806347..cd00c4f3 100644 --- a/src/Device.ts +++ b/src/Device.ts @@ -36,8 +36,7 @@ export type BuiltinHandlerName = | 'ReactNativeUnifiedPlan' | 'ReactNative'; -export type DeviceOptions = -{ +export type DeviceOptions = { /** * The name of one of the builtin handlers. */ @@ -53,46 +52,44 @@ export type DeviceOptions = Handler?: string; }; -export function detectDevice(): BuiltinHandlerName | undefined -{ +export function detectDevice(): BuiltinHandlerName | undefined { // React-Native. // NOTE: react-native-webrtc >= 1.75.0 is required. // NOTE: react-native-webrtc with Unified Plan requires version >= 106.0.0. - if (typeof navigator === 'object' && navigator.product === 'ReactNative') - { + if (typeof navigator === 'object' && navigator.product === 'ReactNative') { logger.debug('detectDevice() | React-Native detected'); - if (typeof RTCPeerConnection === 'undefined') - { + if (typeof RTCPeerConnection === 'undefined') { logger.warn( - 'detectDevice() | unsupported react-native-webrtc without RTCPeerConnection, forgot to call registerGlobals()?'); + 'detectDevice() | unsupported react-native-webrtc without RTCPeerConnection, forgot to call registerGlobals()?', + ); return undefined; } - if (typeof RTCRtpTransceiver !== 'undefined') - { + if (typeof RTCRtpTransceiver !== 'undefined') { logger.debug('detectDevice() | ReactNative UnifiedPlan handler chosen'); return 'ReactNativeUnifiedPlan'; - } - else - { + } else { logger.debug('detectDevice() | ReactNative PlanB handler chosen'); return 'ReactNative'; } } // Browser. - else if (typeof navigator === 'object' && typeof navigator.userAgent === 'string') - { + else if ( + typeof navigator === 'object' && + typeof navigator.userAgent === 'string' + ) { const ua = navigator.userAgent; const uaParser = new UAParser(ua); logger.debug( 'detectDevice() | browser detected [ua:%s, parsed:%o]', - ua, uaParser.getResult() + ua, + uaParser.getResult(), ); const browser = uaParser.getBrowser(); @@ -108,62 +105,46 @@ export function detectDevice(): BuiltinHandlerName | undefined const isIOS = osName === 'ios' || deviceModel === 'ipad'; - const isChrome = browserName && - [ - 'chrome', - 'chromium', - 'mobile chrome', - 'chrome webview', - 'chrome headless' - ].includes(browserName); - - const isFirefox = browserName && - [ - 'firefox', - 'mobile firefox', - 'mobile focus' - ].includes(browserName); - - const isSafari = browserName && - [ - 'safari', - 'mobile safari' - ].includes(browserName); - - const isEdge = browserName && [ 'edge' ].includes(browserName); + const isChrome = + browserName && + [ + 'chrome', + 'chromium', + 'mobile chrome', + 'chrome webview', + 'chrome headless', + ].includes(browserName); + + const isFirefox = + browserName && + ['firefox', 'mobile firefox', 'mobile focus'].includes(browserName); + + const isSafari = + browserName && ['safari', 'mobile safari'].includes(browserName); + + const isEdge = browserName && ['edge'].includes(browserName); // Chrome, Chromium, and Edge. - if ((isChrome || isEdge) && !isIOS && browserVersion >= 111) - { + if ((isChrome || isEdge) && !isIOS && browserVersion >= 111) { return 'Chrome111'; - } - else if ( + } else if ( (isChrome && !isIOS && browserVersion >= 74) || (isEdge && !isIOS && browserVersion >= 88) - ) - { + ) { return 'Chrome74'; - } - else if (isChrome && !isIOS && browserVersion >= 70) - { + } else if (isChrome && !isIOS && browserVersion >= 70) { return 'Chrome70'; - } - else if (isChrome && !isIOS && browserVersion >= 67) - { + } else if (isChrome && !isIOS && browserVersion >= 67) { return 'Chrome67'; - } - else if (isChrome && !isIOS && browserVersion >= 55) - { + } else if (isChrome && !isIOS && browserVersion >= 55) { return 'Chrome55'; } // Firefox. - else if (isFirefox && !isIOS && browserVersion >= 60) - { + else if (isFirefox && !isIOS && browserVersion >= 60) { return 'Firefox60'; } // Firefox on iOS (so Safari). - else if (isFirefox && isIOS && osVersion >= 14.3) - { + else if (isFirefox && isIOS && osVersion >= 14.3) { return 'Safari12'; } // Safari with Unified-Plan support enabled. @@ -172,18 +153,15 @@ export function detectDevice(): BuiltinHandlerName | undefined browserVersion >= 12 && typeof RTCRtpTransceiver !== 'undefined' && RTCRtpTransceiver.prototype.hasOwnProperty('currentDirection') - ) - { + ) { return 'Safari12'; } // Safari with Plab-B support. - else if (isSafari && browserVersion >= 11) - { + else if (isSafari && browserVersion >= 11) { return 'Safari11'; } // Old Edge with ORTC support. - else if (isEdge && !isIOS && browserVersion >= 11 && browserVersion <= 18) - { + else if (isEdge && !isIOS && browserVersion >= 11 && browserVersion <= 18) { return 'Edge11'; } // Best effort for WebKit based browsers in iOS. @@ -191,71 +169,56 @@ export function detectDevice(): BuiltinHandlerName | undefined engineName === 'webkit' && isIOS && typeof RTCRtpTransceiver !== 'undefined' && - RTCRtpTransceiver.prototype.hasOwnProperty('currentDirection')) - { + RTCRtpTransceiver.prototype.hasOwnProperty('currentDirection') + ) { return 'Safari12'; } // Best effort for Chromium based browsers. - else if (engineName === 'blink') - { + else if (engineName === 'blink') { const match = ua.match(/(?:(?:Chrome|Chromium))[ /](\w+)/i); - if (match) - { + if (match) { const version = Number(match[1]); - if (version >= 111) - { + if (version >= 111) { return 'Chrome111'; - } - else if (version >= 74) - { + } else if (version >= 74) { return 'Chrome74'; - } - else if (version >= 70) - { + } else if (version >= 70) { return 'Chrome70'; - } - else if (version >= 67) - { + } else if (version >= 67) { return 'Chrome67'; - } - else - { + } else { return 'Chrome55'; } - } - else - { + } else { return 'Chrome111'; } } // Unsupported browser. - else - { + else { logger.warn( 'detectDevice() | browser not supported [name:%s, version:%s]', - browserName, browserVersion); + browserName, + browserVersion, + ); return undefined; } } // Unknown device. - else - { + else { logger.warn('detectDevice() | unknown device'); return undefined; } } -export type DeviceObserverEvents = -{ +export type DeviceObserverEvents = { newtransport: [Transport]; }; -export class Device -{ +export class Device { // RTC handler factory. private readonly _handlerFactory: HandlerFactory; // Handler name. @@ -272,133 +235,110 @@ export class Device // Local SCTP capabilities. private _sctpCapabilities?: SctpCapabilities; // Observer instance. - protected readonly _observer = new EnhancedEventEmitter(); + protected readonly _observer = + new EnhancedEventEmitter(); /** * Create a new Device to connect to mediasoup server. * * @throws {UnsupportedError} if device is not supported. */ - constructor({ handlerName, handlerFactory, Handler }: DeviceOptions = {}) - { + constructor({ handlerName, handlerFactory, Handler }: DeviceOptions = {}) { logger.debug('constructor()'); // Handle deprecated option. - if (Handler) - { + if (Handler) { logger.warn( - 'constructor() | Handler option is DEPRECATED, use handlerName or handlerFactory instead'); + 'constructor() | Handler option is DEPRECATED, use handlerName or handlerFactory instead', + ); - if (typeof Handler === 'string') - { + if (typeof Handler === 'string') { handlerName = Handler as BuiltinHandlerName; - } - else - { + } else { throw new TypeError( - 'non string Handler option no longer supported, use handlerFactory instead'); + 'non string Handler option no longer supported, use handlerFactory instead', + ); } } - if (handlerName && handlerFactory) - { - throw new TypeError('just one of handlerName or handlerInterface can be given'); + if (handlerName && handlerFactory) { + throw new TypeError( + 'just one of handlerName or handlerInterface can be given', + ); } - if (handlerFactory) - { + if (handlerFactory) { this._handlerFactory = handlerFactory; - } - else - { - if (handlerName) - { + } else { + if (handlerName) { logger.debug('constructor() | handler given: %s', handlerName); - } - else - { + } else { handlerName = detectDevice(); - if (handlerName) - { + if (handlerName) { logger.debug('constructor() | detected handler: %s', handlerName); - } - else - { + } else { throw new UnsupportedError('device not supported'); } } - switch (handlerName) - { - case 'Chrome111': - { + switch (handlerName) { + case 'Chrome111': { this._handlerFactory = Chrome111.createFactory(); break; } - case 'Chrome74': - { + case 'Chrome74': { this._handlerFactory = Chrome74.createFactory(); break; } - case 'Chrome70': - { + case 'Chrome70': { this._handlerFactory = Chrome70.createFactory(); break; } - case 'Chrome67': - { + case 'Chrome67': { this._handlerFactory = Chrome67.createFactory(); break; } - case 'Chrome55': - { + case 'Chrome55': { this._handlerFactory = Chrome55.createFactory(); break; } - case 'Firefox60': - { + case 'Firefox60': { this._handlerFactory = Firefox60.createFactory(); break; } - case 'Safari12': - { + case 'Safari12': { this._handlerFactory = Safari12.createFactory(); break; } - case 'Safari11': - { + case 'Safari11': { this._handlerFactory = Safari11.createFactory(); break; } - case 'Edge11': - { + case 'Edge11': { this._handlerFactory = Edge11.createFactory(); break; } - case 'ReactNativeUnifiedPlan': - { + case 'ReactNativeUnifiedPlan': { this._handlerFactory = ReactNativeUnifiedPlan.createFactory(); break; } - case 'ReactNative': - { + case 'ReactNative': { this._handlerFactory = ReactNative.createFactory(); break; } - default: - { + default: { throw new TypeError(`unknown handlerName "${handlerName}"`); } } @@ -413,10 +353,9 @@ export class Device this._extendedRtpCapabilities = undefined; this._recvRtpCapabilities = undefined; - this._canProduceByKind = - { - audio : false, - video : false + this._canProduceByKind = { + audio: false, + video: false, }; this._sctpCapabilities = undefined; } @@ -424,16 +363,14 @@ export class Device /** * The RTC handler name. */ - get handlerName(): string - { + get handlerName(): string { return this._handlerName; } /** * Whether the Device is loaded. */ - get loaded(): boolean - { + get loaded(): boolean { return this._loaded; } @@ -442,10 +379,8 @@ export class Device * * @throws {InvalidStateError} if not loaded. */ - get rtpCapabilities(): RtpCapabilities - { - if (!this._loaded) - { + get rtpCapabilities(): RtpCapabilities { + if (!this._loaded) { throw new InvalidStateError('not loaded'); } @@ -457,29 +392,26 @@ export class Device * * @throws {InvalidStateError} if not loaded. */ - get sctpCapabilities(): SctpCapabilities - { - if (!this._loaded) - { + get sctpCapabilities(): SctpCapabilities { + if (!this._loaded) { throw new InvalidStateError('not loaded'); } return this._sctpCapabilities!; } - get observer(): EnhancedEventEmitter - { + get observer(): EnhancedEventEmitter { return this._observer; } /** * Initialize the Device. */ - async load( - { routerRtpCapabilities }: - { routerRtpCapabilities: RtpCapabilities } - ): Promise - { + async load({ + routerRtpCapabilities, + }: { + routerRtpCapabilities: RtpCapabilities; + }): Promise { logger.debug('load() [routerRtpCapabilities:%o]', routerRtpCapabilities); routerRtpCapabilities = utils.clone(routerRtpCapabilities); @@ -487,10 +419,8 @@ export class Device // Temporal handler to get its capabilities. let handler: HandlerInterface | undefined; - try - { - if (this._loaded) - { + try { + if (this._loaded) { throw new InvalidStateError('already loaded'); } @@ -502,41 +432,54 @@ export class Device const nativeRtpCapabilities = await handler.getNativeRtpCapabilities(); logger.debug( - 'load() | got native RTP capabilities:%o', nativeRtpCapabilities); + 'load() | got native RTP capabilities:%o', + nativeRtpCapabilities, + ); // This may throw. ortc.validateRtpCapabilities(nativeRtpCapabilities); // Get extended RTP capabilities. this._extendedRtpCapabilities = ortc.getExtendedRtpCapabilities( - nativeRtpCapabilities, routerRtpCapabilities); + nativeRtpCapabilities, + routerRtpCapabilities, + ); logger.debug( 'load() | got extended RTP capabilities:%o', - this._extendedRtpCapabilities); + this._extendedRtpCapabilities, + ); // Check whether we can produce audio/video. - this._canProduceByKind.audio = - ortc.canSend('audio', this._extendedRtpCapabilities); - this._canProduceByKind.video = - ortc.canSend('video', this._extendedRtpCapabilities); + this._canProduceByKind.audio = ortc.canSend( + 'audio', + this._extendedRtpCapabilities, + ); + this._canProduceByKind.video = ortc.canSend( + 'video', + this._extendedRtpCapabilities, + ); // Generate our receiving RTP capabilities for receiving media. - this._recvRtpCapabilities = - ortc.getRecvRtpCapabilities(this._extendedRtpCapabilities); + this._recvRtpCapabilities = ortc.getRecvRtpCapabilities( + this._extendedRtpCapabilities, + ); // This may throw. ortc.validateRtpCapabilities(this._recvRtpCapabilities); logger.debug( 'load() | got receiving RTP capabilities:%o', - this._recvRtpCapabilities); + this._recvRtpCapabilities, + ); // Generate our SCTP capabilities. this._sctpCapabilities = await handler.getNativeSctpCapabilities(); logger.debug( - 'load() | got native SCTP capabilities:%o', this._sctpCapabilities); + 'load() | got native SCTP capabilities:%o', + this._sctpCapabilities, + ); // This may throw. ortc.validateSctpCapabilities(this._sctpCapabilities); @@ -546,11 +489,8 @@ export class Device this._loaded = true; handler.close(); - } - catch (error) - { - if (handler) - { + } catch (error) { + if (handler) { handler.close(); } @@ -564,14 +504,10 @@ export class Device * @throws {InvalidStateError} if not loaded. * @throws {TypeError} if wrong arguments. */ - canProduce(kind: MediaKind): boolean - { - if (!this._loaded) - { + canProduce(kind: MediaKind): boolean { + if (!this._loaded) { throw new InvalidStateError('not loaded'); - } - else if (kind !== 'audio' && kind !== 'video') - { + } else if (kind !== 'audio' && kind !== 'video') { throw new TypeError(`invalid kind "${kind}"`); } @@ -584,37 +520,33 @@ export class Device * @throws {InvalidStateError} if not loaded. * @throws {TypeError} if wrong arguments. */ - createSendTransport( - { - id, - iceParameters, - iceCandidates, - dtlsParameters, - sctpParameters, - iceServers, - iceTransportPolicy, - additionalSettings, - proprietaryConstraints, - appData - }: TransportOptions - ): Transport - { + createSendTransport({ + id, + iceParameters, + iceCandidates, + dtlsParameters, + sctpParameters, + iceServers, + iceTransportPolicy, + additionalSettings, + proprietaryConstraints, + appData, + }: TransportOptions): Transport { logger.debug('createSendTransport()'); - return this.createTransport( - { - direction : 'send', - id : id, - iceParameters : iceParameters, - iceCandidates : iceCandidates, - dtlsParameters : dtlsParameters, - sctpParameters : sctpParameters, - iceServers : iceServers, - iceTransportPolicy : iceTransportPolicy, - additionalSettings : additionalSettings, - proprietaryConstraints : proprietaryConstraints, - appData : appData - }); + return this.createTransport({ + direction: 'send', + id: id, + iceParameters: iceParameters, + iceCandidates: iceCandidates, + dtlsParameters: dtlsParameters, + sctpParameters: sctpParameters, + iceServers: iceServers, + iceTransportPolicy: iceTransportPolicy, + additionalSettings: additionalSettings, + proprietaryConstraints: proprietaryConstraints, + appData: appData, + }); } /** @@ -623,105 +555,83 @@ export class Device * @throws {InvalidStateError} if not loaded. * @throws {TypeError} if wrong arguments. */ - createRecvTransport( - { - id, - iceParameters, - iceCandidates, - dtlsParameters, - sctpParameters, - iceServers, - iceTransportPolicy, - additionalSettings, - proprietaryConstraints, - appData - }: TransportOptions - ): Transport - { + createRecvTransport({ + id, + iceParameters, + iceCandidates, + dtlsParameters, + sctpParameters, + iceServers, + iceTransportPolicy, + additionalSettings, + proprietaryConstraints, + appData, + }: TransportOptions): Transport { logger.debug('createRecvTransport()'); - return this.createTransport( - { - direction : 'recv', - id : id, - iceParameters : iceParameters, - iceCandidates : iceCandidates, - dtlsParameters : dtlsParameters, - sctpParameters : sctpParameters, - iceServers : iceServers, - iceTransportPolicy : iceTransportPolicy, - additionalSettings : additionalSettings, - proprietaryConstraints : proprietaryConstraints, - appData : appData - }); + return this.createTransport({ + direction: 'recv', + id: id, + iceParameters: iceParameters, + iceCandidates: iceCandidates, + dtlsParameters: dtlsParameters, + sctpParameters: sctpParameters, + iceServers: iceServers, + iceTransportPolicy: iceTransportPolicy, + additionalSettings: additionalSettings, + proprietaryConstraints: proprietaryConstraints, + appData: appData, + }); } - private createTransport( - { - direction, - id, - iceParameters, - iceCandidates, - dtlsParameters, - sctpParameters, - iceServers, - iceTransportPolicy, - additionalSettings, - proprietaryConstraints, - appData - }: - { - direction: 'send' | 'recv'; - } & TransportOptions - ): Transport - { - if (!this._loaded) - { + private createTransport({ + direction, + id, + iceParameters, + iceCandidates, + dtlsParameters, + sctpParameters, + iceServers, + iceTransportPolicy, + additionalSettings, + proprietaryConstraints, + appData, + }: { + direction: 'send' | 'recv'; + } & TransportOptions): Transport { + if (!this._loaded) { throw new InvalidStateError('not loaded'); - } - else if (typeof id !== 'string') - { + } else if (typeof id !== 'string') { throw new TypeError('missing id'); - } - else if (typeof iceParameters !== 'object') - { + } else if (typeof iceParameters !== 'object') { throw new TypeError('missing iceParameters'); - } - else if (!Array.isArray(iceCandidates)) - { + } else if (!Array.isArray(iceCandidates)) { throw new TypeError('missing iceCandidates'); - } - else if (typeof dtlsParameters !== 'object') - { + } else if (typeof dtlsParameters !== 'object') { throw new TypeError('missing dtlsParameters'); - } - else if (sctpParameters && typeof sctpParameters !== 'object') - { + } else if (sctpParameters && typeof sctpParameters !== 'object') { throw new TypeError('wrong sctpParameters'); - } - else if (appData && typeof appData !== 'object') - { + } else if (appData && typeof appData !== 'object') { throw new TypeError('if given, appData must be an object'); } // Create a new Transport. - const transport = new Transport( - { - direction, - id, - iceParameters, - iceCandidates, - dtlsParameters, - sctpParameters, - iceServers, - iceTransportPolicy, - additionalSettings, - proprietaryConstraints, - appData, - handlerFactory : this._handlerFactory, - extendedRtpCapabilities : this._extendedRtpCapabilities, - canProduceByKind : this._canProduceByKind - }); + const transport = new Transport({ + direction, + id, + iceParameters, + iceCandidates, + dtlsParameters, + sctpParameters, + iceServers, + iceTransportPolicy, + additionalSettings, + proprietaryConstraints, + appData, + handlerFactory: this._handlerFactory, + extendedRtpCapabilities: this._extendedRtpCapabilities, + canProduceByKind: this._canProduceByKind, + }); // Emit observer event. this._observer.safeEmit('newtransport', transport); diff --git a/src/EnhancedEventEmitter.ts b/src/EnhancedEventEmitter.ts index b6b1dc0e..ebacff8b 100644 --- a/src/EnhancedEventEmitter.ts +++ b/src/EnhancedEventEmitter.ts @@ -5,35 +5,32 @@ const logger = new Logger('EnhancedEventEmitter'); type Events = Record; -export class EnhancedEventEmitter extends EventEmitter -{ - constructor() - { +export class EnhancedEventEmitter< + E extends Events = Events, +> extends EventEmitter { + constructor() { super(); this.setMaxListeners(Infinity); } - emit(eventName: K, ...args: E[K]): boolean - { + emit(eventName: K, ...args: E[K]): boolean { return super.emit(eventName, ...args); } /** * Special addition to the EventEmitter API. */ - safeEmit(eventName: K, ...args: E[K]): boolean - { + safeEmit(eventName: K, ...args: E[K]): boolean { const numListeners = super.listenerCount(eventName); - try - { + try { return super.emit(eventName, ...args); - } - catch (error) - { + } catch (error) { logger.error( 'safeEmit() | event listener threw an error [eventName:%s]:%o', - eventName, error); + eventName, + error, + ); return Boolean(numListeners); } @@ -41,9 +38,8 @@ export class EnhancedEventEmitter extends EventEmitte on( eventName: K, - listener: (...args: E[K]) => void - ): this - { + listener: (...args: E[K]) => void, + ): this { super.on(eventName, listener as (...args: any[]) => void); return this; @@ -51,9 +47,8 @@ export class EnhancedEventEmitter extends EventEmitte off( eventName: K, - listener: (...args: E[K]) => void - ): this - { + listener: (...args: E[K]) => void, + ): this { super.off(eventName, listener as (...args: any[]) => void); return this; @@ -61,9 +56,8 @@ export class EnhancedEventEmitter extends EventEmitte addListener( eventName: K, - listener: (...args: E[K]) => void - ): this - { + listener: (...args: E[K]) => void, + ): this { super.on(eventName, listener as (...args: any[]) => void); return this; @@ -71,9 +65,8 @@ export class EnhancedEventEmitter extends EventEmitte prependListener( eventName: K, - listener: (...args: E[K]) => void - ): this - { + listener: (...args: E[K]) => void, + ): this { super.prependListener(eventName, listener as (...args: any[]) => void); return this; @@ -81,9 +74,8 @@ export class EnhancedEventEmitter extends EventEmitte once( eventName: K, - listener: (...args: E[K]) => void - ): this - { + listener: (...args: E[K]) => void, + ): this { super.once(eventName, listener as (...args: any[]) => void); return this; @@ -91,9 +83,8 @@ export class EnhancedEventEmitter extends EventEmitte prependOnceListener( eventName: K, - listener: (...args: E[K]) => void - ): this - { + listener: (...args: E[K]) => void, + ): this { super.prependOnceListener(eventName, listener as (...args: any[]) => void); return this; @@ -101,33 +92,28 @@ export class EnhancedEventEmitter extends EventEmitte removeListener( eventName: K, - listener: (...args: E[K]) => void - ): this - { + listener: (...args: E[K]) => void, + ): this { super.off(eventName, listener as (...args: any[]) => void); return this; } - removeAllListeners(eventName?: K): this - { + removeAllListeners(eventName?: K): this { super.removeAllListeners(eventName); return this; } - listenerCount(eventName: K): number - { + listenerCount(eventName: K): number { return super.listenerCount(eventName); } - listeners(eventName: K): Function[] - { + listeners(eventName: K): Function[] { return super.listeners(eventName); } - rawListeners(eventName: K): Function[] - { + rawListeners(eventName: K): Function[] { return super.rawListeners(eventName); } } diff --git a/src/Logger.ts b/src/Logger.ts index 395bbe73..5c8a853f 100644 --- a/src/Logger.ts +++ b/src/Logger.ts @@ -2,22 +2,17 @@ import debug from 'debug'; const APP_NAME = 'mediasoup-client'; -export class Logger -{ +export class Logger { private readonly _debug: debug.Debugger; private readonly _warn: debug.Debugger; private readonly _error: debug.Debugger; - constructor(prefix?: string) - { - if (prefix) - { + constructor(prefix?: string) { + if (prefix) { this._debug = debug(`${APP_NAME}:${prefix}`); this._warn = debug(`${APP_NAME}:WARN:${prefix}`); this._error = debug(`${APP_NAME}:ERROR:${prefix}`); - } - else - { + } else { this._debug = debug(APP_NAME); this._warn = debug(`${APP_NAME}:WARN`); this._error = debug(`${APP_NAME}:ERROR`); @@ -30,18 +25,15 @@ export class Logger /* eslint-enable no-console */ } - get debug(): debug.Debugger - { + get debug(): debug.Debugger { return this._debug; } - get warn(): debug.Debugger - { + get warn(): debug.Debugger { return this._warn; } - get error(): debug.Debugger - { + get error(): debug.Debugger { return this._error; } } diff --git a/src/Producer.ts b/src/Producer.ts index e1b1db50..f4b3235f 100644 --- a/src/Producer.ts +++ b/src/Producer.ts @@ -5,14 +5,13 @@ import { MediaKind, RtpCodecCapability, RtpParameters, - RtpEncodingParameters + RtpEncodingParameters, } from './RtpParameters'; import { AppData } from './types'; const logger = new Logger('Producer'); -export type ProducerOptions = -{ +export type ProducerOptions = { track?: MediaStreamTrack; encodings?: RtpEncodingParameters[]; codecOptions?: ProducerCodecOptions; @@ -24,8 +23,7 @@ export type ProducerOptions = }; // https://mediasoup.org/documentation/v3/mediasoup-client/api/#ProducerCodecOptions -export type ProducerCodecOptions = -{ +export type ProducerCodecOptions = { opusStereo?: boolean; opusFec?: boolean; opusDtx?: boolean; @@ -38,52 +36,37 @@ export type ProducerCodecOptions = videoGoogleMinBitrate?: number; }; -export type ProducerEvents = -{ +export type ProducerEvents = { transportclose: []; trackended: []; // Private events. - '@pause': [ - () => void, - (error: Error) => void - ]; - '@resume': [ - () => void, - (error: Error) => void - ]; - '@replacetrack': - [ + '@pause': [() => void, (error: Error) => void]; + '@resume': [() => void, (error: Error) => void]; + '@replacetrack': [ MediaStreamTrack | null, () => void, - (error: Error) => void + (error: Error) => void, ]; - '@setmaxspatiallayer': - [ - number, - () => void, - (error: Error) => void - ]; - '@setrtpencodingparameters': - [ + '@setmaxspatiallayer': [number, () => void, (error: Error) => void]; + '@setrtpencodingparameters': [ RTCRtpEncodingParameters, () => void, - (error: Error) => void + (error: Error) => void, ]; '@getstats': [(stats: RTCStatsReport) => void, (error: Error) => void]; '@close': []; }; -export type ProducerObserverEvents = -{ +export type ProducerObserverEvents = { close: []; pause: []; resume: []; trackended: []; }; -export class Producer - extends EnhancedEventEmitter -{ +export class Producer< + ProducerAppData extends AppData = AppData, +> extends EnhancedEventEmitter { // Id. private readonly _id: string; // Local id. @@ -111,33 +94,30 @@ export class Producer // App custom data. private _appData: ProducerAppData; // Observer instance. - protected readonly _observer = new EnhancedEventEmitter(); - - constructor( - { - id, - localId, - rtpSender, - track, - rtpParameters, - stopTracks, - disableTrackOnPause, - zeroRtpOnPause, - appData - }: - { - id: string; - localId: string; - rtpSender?: RTCRtpSender; - track: MediaStreamTrack; - rtpParameters: RtpParameters; - stopTracks: boolean; - disableTrackOnPause: boolean; - zeroRtpOnPause: boolean; - appData?: ProducerAppData; - } - ) - { + protected readonly _observer = + new EnhancedEventEmitter(); + + constructor({ + id, + localId, + rtpSender, + track, + rtpParameters, + stopTracks, + disableTrackOnPause, + zeroRtpOnPause, + appData, + }: { + id: string; + localId: string; + rtpSender?: RTCRtpSender; + track: MediaStreamTrack; + rtpParameters: RtpParameters; + stopTracks: boolean; + disableTrackOnPause: boolean; + zeroRtpOnPause: boolean; + appData?: ProducerAppData; + }) { super(); logger.debug('constructor()'); @@ -153,7 +133,7 @@ export class Producer this._stopTracks = stopTracks; this._disableTrackOnPause = disableTrackOnPause; this._zeroRtpOnPause = zeroRtpOnPause; - this._appData = appData || {} as ProducerAppData; + this._appData = appData || ({} as ProducerAppData); this.onTrackEnded = this.onTrackEnded.bind(this); // NOTE: Minor issue. If zeroRtpOnPause is true, we cannot emit the @@ -165,64 +145,56 @@ export class Producer /** * Producer id. */ - get id(): string - { + get id(): string { return this._id; } /** * Local id. */ - get localId(): string - { + get localId(): string { return this._localId; } /** * Whether the Producer is closed. */ - get closed(): boolean - { + get closed(): boolean { return this._closed; } /** * Media kind. */ - get kind(): MediaKind - { + get kind(): MediaKind { return this._kind; } /** * Associated RTCRtpSender. */ - get rtpSender(): RTCRtpSender | undefined - { + get rtpSender(): RTCRtpSender | undefined { return this._rtpSender; } /** * The associated track. */ - get track(): MediaStreamTrack | null - { + get track(): MediaStreamTrack | null { return this._track; } /** * RTP parameters. */ - get rtpParameters(): RtpParameters - { + get rtpParameters(): RtpParameters { return this._rtpParameters; } /** * Whether the Producer is paused. */ - get paused(): boolean - { + get paused(): boolean { return this._paused; } @@ -231,39 +203,33 @@ export class Producer * * @type {Number | undefined} */ - get maxSpatialLayer(): number | undefined - { + get maxSpatialLayer(): number | undefined { return this._maxSpatialLayer; } /** * App custom data. */ - get appData(): ProducerAppData - { + get appData(): ProducerAppData { return this._appData; } /** * App custom data setter. */ - set appData(appData: ProducerAppData) - { + set appData(appData: ProducerAppData) { this._appData = appData; } - get observer(): EnhancedEventEmitter - { + get observer(): EnhancedEventEmitter { return this._observer; } /** * Closes the Producer. */ - close(): void - { - if (this._closed) - { + close(): void { + if (this._closed) { return; } @@ -282,10 +248,8 @@ export class Producer /** * Transport was closed. */ - transportClosed(): void - { - if (this._closed) - { + transportClosed(): void { + if (this._closed) { return; } @@ -304,32 +268,23 @@ export class Producer /** * Get associated RTCRtpSender stats. */ - async getStats(): Promise - { - if (this._closed) - { + async getStats(): Promise { + if (this._closed) { throw new InvalidStateError('closed'); } - return new Promise((resolve, reject) => - { - this.safeEmit( - '@getstats', - resolve, - reject - ); + return new Promise((resolve, reject) => { + this.safeEmit('@getstats', resolve, reject); }); } /** * Pauses sending media. */ - pause(): void - { + pause(): void { logger.debug('pause()'); - if (this._closed) - { + if (this._closed) { logger.error('pause() | Producer closed'); return; @@ -337,20 +292,13 @@ export class Producer this._paused = true; - if (this._track && this._disableTrackOnPause) - { + if (this._track && this._disableTrackOnPause) { this._track.enabled = false; } - if (this._zeroRtpOnPause) - { - new Promise((resolve, reject) => - { - this.safeEmit( - '@pause', - resolve, - reject - ); + if (this._zeroRtpOnPause) { + new Promise((resolve, reject) => { + this.safeEmit('@pause', resolve, reject); }).catch(() => {}); } @@ -361,12 +309,10 @@ export class Producer /** * Resumes sending media. */ - resume(): void - { + resume(): void { logger.debug('resume()'); - if (this._closed) - { + if (this._closed) { logger.error('resume() | Producer closed'); return; @@ -374,20 +320,13 @@ export class Producer this._paused = false; - if (this._track && this._disableTrackOnPause) - { + if (this._track && this._disableTrackOnPause) { this._track.enabled = true; } - if (this._zeroRtpOnPause) - { - new Promise((resolve, reject) => - { - this.safeEmit( - '@resume', - resolve, - reject - ); + if (this._zeroRtpOnPause) { + new Promise((resolve, reject) => { + this.safeEmit('@resume', resolve, reject); }).catch(() => {}); } @@ -398,43 +337,36 @@ export class Producer /** * Replaces the current track with a new one or null. */ - async replaceTrack({ track }: { track: MediaStreamTrack | null }): Promise - { + async replaceTrack({ + track, + }: { + track: MediaStreamTrack | null; + }): Promise { logger.debug('replaceTrack() [track:%o]', track); - if (this._closed) - { + if (this._closed) { // This must be done here. Otherwise there is no chance to stop the given // track. - if (track && this._stopTracks) - { - try { track.stop(); } - catch (error) {} + if (track && this._stopTracks) { + try { + track.stop(); + } catch (error) {} } throw new InvalidStateError('closed'); - } - else if (track && track.readyState === 'ended') - { + } else if (track && track.readyState === 'ended') { throw new InvalidStateError('track ended'); } // Do nothing if this is the same track as the current handled one. - if (track === this._track) - { + if (track === this._track) { logger.debug('replaceTrack() | same track, ignored'); return; } - await new Promise((resolve, reject) => - { - this.safeEmit( - '@replacetrack', - track, - resolve, - reject - ); + await new Promise((resolve, reject) => { + this.safeEmit('@replacetrack', track, resolve, reject); }); // Destroy the previous track. @@ -445,14 +377,10 @@ export class Producer // If this Producer was paused/resumed and the state of the new // track does not match, fix it. - if (this._track && this._disableTrackOnPause) - { - if (!this._paused) - { + if (this._track && this._disableTrackOnPause) { + if (!this._paused) { this._track.enabled = true; - } - else if (this._paused) - { + } else if (this._paused) { this._track.enabled = false; } } @@ -464,65 +392,41 @@ export class Producer /** * Sets the video max spatial layer to be sent. */ - async setMaxSpatialLayer(spatialLayer: number): Promise - { - if (this._closed) - { + async setMaxSpatialLayer(spatialLayer: number): Promise { + if (this._closed) { throw new InvalidStateError('closed'); - } - else if (this._kind !== 'video') - { + } else if (this._kind !== 'video') { throw new UnsupportedError('not a video Producer'); - } - else if (typeof spatialLayer !== 'number') - { + } else if (typeof spatialLayer !== 'number') { throw new TypeError('invalid spatialLayer'); } - if (spatialLayer === this._maxSpatialLayer) - { + if (spatialLayer === this._maxSpatialLayer) { return; } - await new Promise((resolve, reject) => - { - this.safeEmit( - '@setmaxspatiallayer', - spatialLayer, - resolve, - reject - ); + await new Promise((resolve, reject) => { + this.safeEmit('@setmaxspatiallayer', spatialLayer, resolve, reject); }).catch(() => {}); this._maxSpatialLayer = spatialLayer; } async setRtpEncodingParameters( - params: RTCRtpEncodingParameters - ): Promise - { - if (this._closed) - { + params: RTCRtpEncodingParameters, + ): Promise { + if (this._closed) { throw new InvalidStateError('closed'); - } - else if (typeof params !== 'object') - { + } else if (typeof params !== 'object') { throw new TypeError('invalid params'); } - await new Promise((resolve, reject) => - { - this.safeEmit( - '@setrtpencodingparameters', - params, - resolve, - reject - ); + await new Promise((resolve, reject) => { + this.safeEmit('@setrtpencodingparameters', params, resolve, reject); }); } - private onTrackEnded(): void - { + private onTrackEnded(): void { logger.debug('track "ended" event'); this.safeEmit('trackended'); @@ -531,34 +435,26 @@ export class Producer this._observer.safeEmit('trackended'); } - private handleTrack(): void - { - if (!this._track) - { + private handleTrack(): void { + if (!this._track) { return; } this._track.addEventListener('ended', this.onTrackEnded); } - private destroyTrack(): void - { - if (!this._track) - { + private destroyTrack(): void { + if (!this._track) { return; } - try - { + try { this._track.removeEventListener('ended', this.onTrackEnded); // Just stop the track unless the app set stopTracks: false. - if (this._stopTracks) - { + if (this._stopTracks) { this._track.stop(); } - } - catch (error) - {} + } catch (error) {} } } diff --git a/src/RtpParameters.ts b/src/RtpParameters.ts index cfcb0884..45edf43d 100644 --- a/src/RtpParameters.ts +++ b/src/RtpParameters.ts @@ -3,8 +3,7 @@ * media level. */ -export type RtpCapabilities = -{ +export type RtpCapabilities = { /** * Supported media and RTX codecs. */ @@ -37,8 +36,7 @@ export type MediaKind = 'audio' | 'video'; * require preferredPayloadType field (if unset, mediasoup will choose a random * one). If given, make sure it's in the 96-127 range. */ -export type RtpCodecCapability = -{ +export type RtpCodecCapability = { /** * Media kind. */ @@ -75,7 +73,11 @@ export type RtpCodecCapability = /** * Direction of RTP header extension. */ -export type RtpHeaderExtensionDirection = 'sendrecv' | 'sendonly' | 'recvonly' | 'inactive'; +export type RtpHeaderExtensionDirection = + | 'sendrecv' + | 'sendonly' + | 'recvonly' + | 'inactive'; /** * Provides information relating to supported header extensions. The list of @@ -87,8 +89,7 @@ export type RtpHeaderExtensionDirection = 'sendrecv' | 'sendonly' | 'recvonly' | * router.rtpCapabilities or mediasoup.getSupportedRtpCapabilities()). It's * ignored if present in endpoints' RTP capabilities. */ -export type RtpHeaderExtension = -{ +export type RtpHeaderExtension = { /** * Media kind. */ @@ -146,8 +147,7 @@ export type RtpHeaderExtension = * the associated producer. This applies even if the producer's encodings have * rid set. */ -export type RtpParameters = -{ +export type RtpParameters = { /** * The MID RTP extension value as defined in the BUNDLE specification. */ @@ -175,8 +175,7 @@ export type RtpParameters = * of media codecs supported by mediasoup and their settings is defined in the * supportedRtpCapabilities.ts file. */ -export type RtpCodecParameters = -{ +export type RtpCodecParameters = { /** * The codec MIME media type/subtype (e.g. 'audio/opus', 'video/VP8'). */ @@ -212,8 +211,7 @@ export type RtpCodecParameters = * messages. The list of RTCP feedbacks supported by mediasoup is defined in the * supportedRtpCapabilities.ts file. */ -export type RtcpFeedback = -{ +export type RtcpFeedback = { /** * RTCP feedback type. */ @@ -228,8 +226,7 @@ export type RtcpFeedback = * Provides information relating to an encoding, which represents a media RTP * stream and its associated RTX stream (if any). */ -export type RtpEncodingParameters = -{ +export type RtpEncodingParameters = { /** * The media SSRC. */ @@ -275,17 +272,17 @@ export type RtpEncodingParameters = * Supported RTP header extension URIs. */ export type RtpHeaderExtensionUri = - 'urn:ietf:params:rtp-hdrext:sdes:mid' | - 'urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id' | - 'urn:ietf:params:rtp-hdrext:sdes:repaired-rtp-stream-id' | - 'http://tools.ietf.org/html/draft-ietf-avtext-framemarking-07' | - 'urn:ietf:params:rtp-hdrext:framemarking' | - 'urn:ietf:params:rtp-hdrext:ssrc-audio-level' | - 'urn:3gpp:video-orientation' | - 'urn:ietf:params:rtp-hdrext:toffset' | - 'http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01' | - 'http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time' | - 'http://www.webrtc.org/experiments/rtp-hdrext/abs-capture-time'; + | 'urn:ietf:params:rtp-hdrext:sdes:mid' + | 'urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id' + | 'urn:ietf:params:rtp-hdrext:sdes:repaired-rtp-stream-id' + | 'http://tools.ietf.org/html/draft-ietf-avtext-framemarking-07' + | 'urn:ietf:params:rtp-hdrext:framemarking' + | 'urn:ietf:params:rtp-hdrext:ssrc-audio-level' + | 'urn:3gpp:video-orientation' + | 'urn:ietf:params:rtp-hdrext:toffset' + | 'http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01' + | 'http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time' + | 'http://www.webrtc.org/experiments/rtp-hdrext/abs-capture-time'; /** * Defines a RTP header extension within the RTP parameters. The list of RTP @@ -295,8 +292,7 @@ export type RtpHeaderExtensionUri = * mediasoup does not currently support encrypted RTP header extensions and no * parameters are currently considered. */ -export type RtpHeaderExtensionParameters = -{ +export type RtpHeaderExtensionParameters = { /** * The URI of the RTP header extension, as defined in RFC 5285. */ @@ -324,8 +320,7 @@ export type RtpHeaderExtensionParameters = * * mediasoup assumes reducedSize to always be true. */ -export type RtcpParameters = -{ +export type RtcpParameters = { /** * The Canonical Name (CNAME) used by RTCP (e.g. in SDES messages). */ diff --git a/src/SctpParameters.ts b/src/SctpParameters.ts index 34289279..7121657a 100644 --- a/src/SctpParameters.ts +++ b/src/SctpParameters.ts @@ -1,10 +1,8 @@ -export type SctpCapabilities = -{ - numStreams: NumSctpStreams; +export type SctpCapabilities = { + numStreams: NumSctpStreams; }; -export type NumSctpStreams = -{ +export type NumSctpStreams = { /** * Initially requested number of outgoing SCTP streams. */ @@ -15,8 +13,7 @@ export type NumSctpStreams = MIS: number; }; -export type SctpParameters = -{ +export type SctpParameters = { /** * Must always equal 5000. */ @@ -42,8 +39,7 @@ export type SctpParameters = * If ordered if false, only one of maxPacketLifeTime or maxRetransmits * can be true. */ -export type SctpStreamParameters = -{ +export type SctpStreamParameters = { /** * SCTP stream id. */ diff --git a/src/Transport.ts b/src/Transport.ts index fa46e038..cd9fd4eb 100644 --- a/src/Transport.ts +++ b/src/Transport.ts @@ -5,7 +5,11 @@ import { EnhancedEventEmitter } from './EnhancedEventEmitter'; import { UnsupportedError, InvalidStateError } from './errors'; import * as utils from './utils'; import * as ortc from './ortc'; -import { HandlerFactory, HandlerInterface, HandlerReceiveOptions } from './handlers/HandlerInterface'; +import { + HandlerFactory, + HandlerInterface, + HandlerReceiveOptions, +} from './handlers/HandlerInterface'; import { Producer, ProducerOptions } from './Producer'; import { Consumer, ConsumerOptions } from './Consumer'; import { DataProducer, DataProducerOptions } from './DataProducer'; @@ -16,8 +20,7 @@ import { AppData } from './types'; const logger = new Logger('Transport'); -export type TransportOptions = -{ +export type TransportOptions = { id: string; iceParameters: IceParameters; iceCandidates: IceCandidate[]; @@ -30,15 +33,13 @@ export type TransportOptions = appData?: TransportAppData; }; -export type CanProduceByKind = -{ +export type CanProduceByKind = { audio: boolean; video: boolean; [key: string]: boolean; }; -export type IceParameters = -{ +export type IceParameters = { /** * ICE username fragment. * */ @@ -53,8 +54,7 @@ export type IceParameters = iceLite?: boolean; }; -export type IceCandidate = -{ +export type IceCandidate = { /** * Unique identifier that allows ICE to correlate candidates that appear on * multiple transports. @@ -86,8 +86,7 @@ export type IceCandidate = tcpType?: 'active' | 'passive' | 'so'; }; -export type DtlsParameters = -{ +export type DtlsParameters = { /** * Server DTLS role. Default 'auto'. */ @@ -102,7 +101,12 @@ export type DtlsParameters = * The hash function algorithm (as defined in the "Hash function Textual Names" * registry initially specified in RFC 4572 Section 8). */ -export type FingerprintAlgorithm = 'sha-1'| 'sha-224'| 'sha-256'| 'sha-384'| 'sha-512'; +export type FingerprintAlgorithm = + | 'sha-1' + | 'sha-224' + | 'sha-256' + | 'sha-384' + | 'sha-512'; /** * The hash function algorithm (as defined in the "Hash function Textual Names" @@ -110,18 +114,14 @@ export type FingerprintAlgorithm = 'sha-1'| 'sha-224'| 'sha-256'| 'sha-384'| 'sh * certificate fingerprint value (in lowercase hex string as expressed utilizing * the syntax of "fingerprint" in RFC 4572 Section 5). */ -export type DtlsFingerprint = -{ +export type DtlsFingerprint = { algorithm: FingerprintAlgorithm; value: string; }; export type DtlsRole = 'auto' | 'client' | 'server'; -export type IceGatheringState = - | 'new' - | 'gathering' - | 'complete'; +export type IceGatheringState = 'new' | 'gathering' | 'complete'; export type ConnectionState = | 'new' @@ -131,30 +131,30 @@ export type ConnectionState = | 'disconnected' | 'closed'; -export type PlainRtpParameters = -{ +export type PlainRtpParameters = { ip: string; ipVersion: 4 | 6; port: number; }; -export type TransportEvents = -{ - connect: [{ dtlsParameters: DtlsParameters }, () => void, (error: Error) => void]; +export type TransportEvents = { + connect: [ + { dtlsParameters: DtlsParameters }, + () => void, + (error: Error) => void, + ]; icegatheringstatechange: [IceGatheringState]; connectionstatechange: [ConnectionState]; - produce: - [ + produce: [ { kind: MediaKind; rtpParameters: RtpParameters; appData: AppData; }, ({ id }: { id: string }) => void, - (error: Error) => void + (error: Error) => void, ]; - producedata: - [ + producedata: [ { sctpStreamParameters: SctpStreamParameters; label?: string; @@ -162,12 +162,11 @@ export type TransportEvents = appData: AppData; }, ({ id }: { id: string }) => void, - (error: Error) => void + (error: Error) => void, ]; }; -export type TransportObserverEvents = -{ +export type TransportObserverEvents = { close: []; newproducer: [Producer]; newconsumer: [Consumer]; @@ -175,27 +174,24 @@ export type TransportObserverEvents = newdataconsumer: [DataConsumer]; }; -class ConsumerCreationTask -{ +class ConsumerCreationTask { consumerOptions: ConsumerOptions; promise: Promise; resolve?: (consumer: Consumer) => void; reject?: (error: Error) => void; - constructor(consumerOptions: ConsumerOptions) - { + constructor(consumerOptions: ConsumerOptions) { this.consumerOptions = consumerOptions; - this.promise = new Promise((resolve, reject) => - { + this.promise = new Promise((resolve, reject) => { this.resolve = resolve; this.reject = reject; }); } } -export class Transport - extends EnhancedEventEmitter -{ +export class Transport< + TransportAppData extends AppData = AppData, +> extends EnhancedEventEmitter { // Id. private readonly _id: string; // Closed flag. @@ -246,33 +242,30 @@ export class Transport // Consumer close in progress flag. private _consumerCloseInProgress = false; // Observer instance. - protected readonly _observer = new EnhancedEventEmitter(); - - constructor( - { - direction, - id, - iceParameters, - iceCandidates, - dtlsParameters, - sctpParameters, - iceServers, - iceTransportPolicy, - additionalSettings, - proprietaryConstraints, - appData, - handlerFactory, - extendedRtpCapabilities, - canProduceByKind - }: - { - direction: 'send' | 'recv'; - handlerFactory: HandlerFactory; - extendedRtpCapabilities: any; - canProduceByKind: CanProduceByKind; - } & TransportOptions - ) - { + protected readonly _observer = + new EnhancedEventEmitter(); + + constructor({ + direction, + id, + iceParameters, + iceCandidates, + dtlsParameters, + sctpParameters, + iceServers, + iceTransportPolicy, + additionalSettings, + proprietaryConstraints, + appData, + handlerFactory, + extendedRtpCapabilities, + canProduceByKind, + }: { + direction: 'send' | 'recv'; + handlerFactory: HandlerFactory; + extendedRtpCapabilities: any; + canProduceByKind: CanProduceByKind; + } & TransportOptions) { super(); logger.debug('constructor() [id:%s, direction:%s]', id, direction); @@ -281,8 +274,9 @@ export class Transport this._direction = direction; this._extendedRtpCapabilities = extendedRtpCapabilities; this._canProduceByKind = canProduceByKind; - this._maxSctpMessageSize = - sctpParameters ? sctpParameters.maxMessageSize : null; + this._maxSctpMessageSize = sctpParameters + ? sctpParameters.maxMessageSize + : null; // Clone and sanitize additionalSettings. additionalSettings = utils.clone(additionalSettings) || {}; @@ -295,21 +289,20 @@ export class Transport this._handler = handlerFactory(); - this._handler.run( - { - direction, - iceParameters, - iceCandidates, - dtlsParameters, - sctpParameters, - iceServers, - iceTransportPolicy, - additionalSettings, - proprietaryConstraints, - extendedRtpCapabilities - }); + this._handler.run({ + direction, + iceParameters, + iceCandidates, + dtlsParameters, + sctpParameters, + iceServers, + iceTransportPolicy, + additionalSettings, + proprietaryConstraints, + extendedRtpCapabilities, + }); - this._appData = appData || {} as TransportAppData; + this._appData = appData || ({} as TransportAppData); this.handleHandler(); } @@ -317,79 +310,68 @@ export class Transport /** * Transport id. */ - get id(): string - { + get id(): string { return this._id; } /** * Whether the Transport is closed. */ - get closed(): boolean - { + get closed(): boolean { return this._closed; } /** * Transport direction. */ - get direction(): 'send' | 'recv' - { + get direction(): 'send' | 'recv' { return this._direction; } /** * RTC handler instance. */ - get handler(): HandlerInterface - { + get handler(): HandlerInterface { return this._handler; } /** * ICE gathering state. */ - get iceGatheringState(): IceGatheringState - { + get iceGatheringState(): IceGatheringState { return this._iceGatheringState; } /** * Connection state. */ - get connectionState(): ConnectionState - { + get connectionState(): ConnectionState { return this._connectionState; } /** * App custom data. */ - get appData(): TransportAppData - { + get appData(): TransportAppData { return this._appData; } /** * App custom data setter. */ - set appData(appData: TransportAppData) - { + set appData(appData: TransportAppData) { this._appData = appData; } - get observer(): EnhancedEventEmitter - { + get observer(): EnhancedEventEmitter { return this._observer; } /** * Close the Transport. */ - close(): void - { - if (this._closed) - { + close(): void { + if (this._closed) { return; } @@ -408,29 +390,25 @@ export class Transport this._connectionState = 'closed'; // Close all Producers. - for (const producer of this._producers.values()) - { + for (const producer of this._producers.values()) { producer.transportClosed(); } this._producers.clear(); // Close all Consumers. - for (const consumer of this._consumers.values()) - { + for (const consumer of this._consumers.values()) { consumer.transportClosed(); } this._consumers.clear(); // Close all DataProducers. - for (const dataProducer of this._dataProducers.values()) - { + for (const dataProducer of this._dataProducers.values()) { dataProducer.transportClosed(); } this._dataProducers.clear(); // Close all DataConsumers. - for (const dataConsumer of this._dataConsumers.values()) - { + for (const dataConsumer of this._dataConsumers.values()) { dataConsumer.transportClosed(); } this._dataConsumers.clear(); @@ -444,10 +422,8 @@ export class Transport * * @returns {RTCStatsReport} */ - async getStats(): Promise - { - if (this._closed) - { + async getStats(): Promise { + if (this._closed) { throw new InvalidStateError('closed'); } @@ -457,195 +433,160 @@ export class Transport /** * Restart ICE connection. */ - async restartIce( - { iceParameters }: - { iceParameters: IceParameters } - ): Promise - { + async restartIce({ + iceParameters, + }: { + iceParameters: IceParameters; + }): Promise { logger.debug('restartIce()'); - if (this._closed) - { + if (this._closed) { throw new InvalidStateError('closed'); - } - else if (!iceParameters) - { + } else if (!iceParameters) { throw new TypeError('missing iceParameters'); } // Enqueue command. return this._awaitQueue.push( async () => await this._handler.restartIce(iceParameters), - 'transport.restartIce()'); + 'transport.restartIce()', + ); } /** * Update ICE servers. */ - async updateIceServers( - { iceServers }: - { iceServers?: RTCIceServer[] } = {} - ): Promise - { + async updateIceServers({ + iceServers, + }: { iceServers?: RTCIceServer[] } = {}): Promise { logger.debug('updateIceServers()'); - if (this._closed) - { + if (this._closed) { throw new InvalidStateError('closed'); - } - else if (!Array.isArray(iceServers)) - { + } else if (!Array.isArray(iceServers)) { throw new TypeError('missing iceServers'); } // Enqueue command. return this._awaitQueue.push( async () => this._handler.updateIceServers(iceServers), - 'transport.updateIceServers()'); + 'transport.updateIceServers()', + ); } /** * Create a Producer. */ - async produce( - { - track, - encodings, - codecOptions, - codec, - stopTracks = true, - disableTrackOnPause = true, - zeroRtpOnPause = false, - appData = {} as ProducerAppData - }: ProducerOptions = {} - ): Promise> - { + async produce({ + track, + encodings, + codecOptions, + codec, + stopTracks = true, + disableTrackOnPause = true, + zeroRtpOnPause = false, + appData = {} as ProducerAppData, + }: ProducerOptions = {}): Promise< + Producer + > { logger.debug('produce() [track:%o]', track); - if (this._closed) - { + if (this._closed) { throw new InvalidStateError('closed'); - } - else if (!track) - { + } else if (!track) { throw new TypeError('missing track'); - } - else if (this._direction !== 'send') - { + } else if (this._direction !== 'send') { throw new UnsupportedError('not a sending Transport'); - } - else if (!this._canProduceByKind[track.kind]) - { + } else if (!this._canProduceByKind[track.kind]) { throw new UnsupportedError(`cannot produce ${track.kind}`); - } - else if (track.readyState === 'ended') - { + } else if (track.readyState === 'ended') { throw new InvalidStateError('track ended'); - } - else if (this.listenerCount('connect') === 0 && this._connectionState === 'new') - { + } else if ( + this.listenerCount('connect') === 0 && + this._connectionState === 'new' + ) { throw new TypeError('no "connect" listener set into this transport'); - } - else if (this.listenerCount('produce') === 0) - { + } else if (this.listenerCount('produce') === 0) { throw new TypeError('no "produce" listener set into this transport'); - } - else if (appData && typeof appData !== 'object') - { + } else if (appData && typeof appData !== 'object') { throw new TypeError('if given, appData must be an object'); } // Enqueue command. - return this._awaitQueue.push( - async () => - { - let normalizedEncodings; - - if (encodings && !Array.isArray(encodings)) - { - throw TypeError('encodings must be an array'); - } - else if (encodings && encodings.length === 0) - { - normalizedEncodings = undefined; - } - else if (encodings) - { - normalizedEncodings = encodings - .map((encoding: any) => - { + return ( + this._awaitQueue + .push(async () => { + let normalizedEncodings; + + if (encodings && !Array.isArray(encodings)) { + throw TypeError('encodings must be an array'); + } else if (encodings && encodings.length === 0) { + normalizedEncodings = undefined; + } else if (encodings) { + normalizedEncodings = encodings.map((encoding: any) => { const normalizedEncoding: any = { active: true }; - if (encoding.active === false) - { + if (encoding.active === false) { normalizedEncoding.active = false; } - if (typeof encoding.dtx === 'boolean') - { + if (typeof encoding.dtx === 'boolean') { normalizedEncoding.dtx = encoding.dtx; } - if (typeof encoding.scalabilityMode === 'string') - { + if (typeof encoding.scalabilityMode === 'string') { normalizedEncoding.scalabilityMode = encoding.scalabilityMode; } - if (typeof encoding.scaleResolutionDownBy === 'number') - { - normalizedEncoding.scaleResolutionDownBy = encoding.scaleResolutionDownBy; + if (typeof encoding.scaleResolutionDownBy === 'number') { + normalizedEncoding.scaleResolutionDownBy = + encoding.scaleResolutionDownBy; } - if (typeof encoding.maxBitrate === 'number') - { + if (typeof encoding.maxBitrate === 'number') { normalizedEncoding.maxBitrate = encoding.maxBitrate; } - if (typeof encoding.maxFramerate === 'number') - { + if (typeof encoding.maxFramerate === 'number') { normalizedEncoding.maxFramerate = encoding.maxFramerate; } - if (typeof encoding.adaptivePtime === 'boolean') - { + if (typeof encoding.adaptivePtime === 'boolean') { normalizedEncoding.adaptivePtime = encoding.adaptivePtime; } - if (typeof encoding.priority === 'string') - { + if (typeof encoding.priority === 'string') { normalizedEncoding.priority = encoding.priority; } - if (typeof encoding.networkPriority === 'string') - { + if (typeof encoding.networkPriority === 'string') { normalizedEncoding.networkPriority = encoding.networkPriority; } return normalizedEncoding; }); - } - - const { localId, rtpParameters, rtpSender } = await this._handler.send( - { - track, - encodings : normalizedEncodings, - codecOptions, - codec - }); + } - try - { - // This will fill rtpParameters's missing fields with default values. - ortc.validateRtpParameters(rtpParameters); + const { localId, rtpParameters, rtpSender } = + await this._handler.send({ + track, + encodings: normalizedEncodings, + codecOptions, + codec, + }); - const { id } = await new Promise<{ id: string }>((resolve, reject) => - { - this.safeEmit( - 'produce', - { - kind : track.kind as MediaKind, - rtpParameters, - appData + try { + // This will fill rtpParameters's missing fields with default values. + ortc.validateRtpParameters(rtpParameters); + + const { id } = await new Promise<{ id: string }>( + (resolve, reject) => { + this.safeEmit( + 'produce', + { + kind: track.kind as MediaKind, + rtpParameters, + appData, + }, + resolve, + reject, + ); }, - resolve, - reject ); - }); - const producer = new Producer( - { + const producer = new Producer({ id, localId, rtpSender, @@ -654,120 +595,99 @@ export class Transport stopTracks, disableTrackOnPause, zeroRtpOnPause, - appData + appData, }); - this._producers.set(producer.id, producer); - this.handleProducer(producer); + this._producers.set(producer.id, producer); + this.handleProducer(producer); - // Emit observer event. - this._observer.safeEmit('newproducer', producer); + // Emit observer event. + this._observer.safeEmit('newproducer', producer); - return producer; - } - catch (error) - { - this._handler.stopSending(localId) - .catch(() => {}); + return producer; + } catch (error) { + this._handler.stopSending(localId).catch(() => {}); - throw error; - } - }, - 'transport.produce()') - // This catch is needed to stop the given track if the command above - // failed due to closed Transport. - .catch((error: Error) => - { - if (stopTracks) - { - try { track.stop(); } - catch (error2) {} - } + throw error; + } + }, 'transport.produce()') + // This catch is needed to stop the given track if the command above + // failed due to closed Transport. + .catch((error: Error) => { + if (stopTracks) { + try { + track.stop(); + } catch (error2) {} + } - throw error; - }); + throw error; + }) + ); } /** * Create a Consumer to consume a remote Producer. */ - async consume( - { - id, - producerId, - kind, - rtpParameters, - streamId, - appData = {} as ConsumerAppData - }: ConsumerOptions - ): Promise> - { + async consume({ + id, + producerId, + kind, + rtpParameters, + streamId, + appData = {} as ConsumerAppData, + }: ConsumerOptions): Promise> { logger.debug('consume()'); rtpParameters = utils.clone(rtpParameters); - if (this._closed) - { + if (this._closed) { throw new InvalidStateError('closed'); - } - else if (this._direction !== 'recv') - { + } else if (this._direction !== 'recv') { throw new UnsupportedError('not a receiving Transport'); - } - else if (typeof id !== 'string') - { + } else if (typeof id !== 'string') { throw new TypeError('missing id'); - } - else if (typeof producerId !== 'string') - { + } else if (typeof producerId !== 'string') { throw new TypeError('missing producerId'); - } - else if (kind !== 'audio' && kind !== 'video') - { + } else if (kind !== 'audio' && kind !== 'video') { throw new TypeError(`invalid kind '${kind}'`); - } - else if (this.listenerCount('connect') === 0 && this._connectionState === 'new') - { + } else if ( + this.listenerCount('connect') === 0 && + this._connectionState === 'new' + ) { throw new TypeError('no "connect" listener set into this transport'); - } - else if (appData && typeof appData !== 'object') - { + } else if (appData && typeof appData !== 'object') { throw new TypeError('if given, appData must be an object'); } // Ensure the device can consume it. const canConsume = ortc.canReceive( - rtpParameters, this._extendedRtpCapabilities); + rtpParameters, + this._extendedRtpCapabilities, + ); - if (!canConsume) - { + if (!canConsume) { throw new UnsupportedError('cannot consume this Producer'); } - const consumerCreationTask = new ConsumerCreationTask( - { - id, - producerId, - kind, - rtpParameters, - streamId, - appData - } - ); + const consumerCreationTask = new ConsumerCreationTask({ + id, + producerId, + kind, + rtpParameters, + streamId, + appData, + }); // Store the Consumer creation task. this._pendingConsumerTasks.push(consumerCreationTask); // There is no Consumer creation in progress, create it now. - queueMicrotask(() => - { - if (this._closed) - { + queueMicrotask(() => { + if (this._closed) { return; } - if (this._consumerCreationInProgress === false) - { + if (this._consumerCreationInProgress === false) { this.createPendingConsumers(); } }); @@ -778,146 +698,117 @@ export class Transport /** * Create a DataProducer */ - async produceData( - { - ordered = true, - maxPacketLifeTime, - maxRetransmits, - label = '', - protocol = '', - appData = {} as DataProducerAppData - }: DataProducerOptions = {} - ): Promise> - { + async produceData({ + ordered = true, + maxPacketLifeTime, + maxRetransmits, + label = '', + protocol = '', + appData = {} as DataProducerAppData, + }: DataProducerOptions = {}): Promise< + DataProducer + > { logger.debug('produceData()'); - if (this._closed) - { + if (this._closed) { throw new InvalidStateError('closed'); - } - else if (this._direction !== 'send') - { + } else if (this._direction !== 'send') { throw new UnsupportedError('not a sending Transport'); - } - else if (!this._maxSctpMessageSize) - { + } else if (!this._maxSctpMessageSize) { throw new UnsupportedError('SCTP not enabled by remote Transport'); - } - else if (this.listenerCount('connect') === 0 && this._connectionState === 'new') - { + } else if ( + this.listenerCount('connect') === 0 && + this._connectionState === 'new' + ) { throw new TypeError('no "connect" listener set into this transport'); - } - else if (this.listenerCount('producedata') === 0) - { + } else if (this.listenerCount('producedata') === 0) { throw new TypeError('no "producedata" listener set into this transport'); - } - else if (appData && typeof appData !== 'object') - { + } else if (appData && typeof appData !== 'object') { throw new TypeError('if given, appData must be an object'); } - if (maxPacketLifeTime || maxRetransmits) - { + if (maxPacketLifeTime || maxRetransmits) { ordered = false; } // Enqueue command. - return this._awaitQueue.push( - async () => - { - const { - dataChannel, - sctpStreamParameters - } = await this._handler.sendDataChannel( - { - ordered, - maxPacketLifeTime, - maxRetransmits, - label, - protocol - }); - - // This will fill sctpStreamParameters's missing fields with default values. - ortc.validateSctpStreamParameters(sctpStreamParameters); - - const { id } = await new Promise<{ id: string }>((resolve, reject) => - { - this.safeEmit( - 'producedata', - { - sctpStreamParameters, - label, - protocol, - appData - }, - resolve, - reject - ); + return this._awaitQueue.push(async () => { + const { dataChannel, sctpStreamParameters } = + await this._handler.sendDataChannel({ + ordered, + maxPacketLifeTime, + maxRetransmits, + label, + protocol, }); - const dataProducer = new DataProducer( + // This will fill sctpStreamParameters's missing fields with default values. + ortc.validateSctpStreamParameters(sctpStreamParameters); + + const { id } = await new Promise<{ id: string }>((resolve, reject) => { + this.safeEmit( + 'producedata', { - id, - dataChannel, sctpStreamParameters, - appData - }); + label, + protocol, + appData, + }, + resolve, + reject, + ); + }); + + const dataProducer = new DataProducer({ + id, + dataChannel, + sctpStreamParameters, + appData, + }); - this._dataProducers.set(dataProducer.id, dataProducer); - this.handleDataProducer(dataProducer); + this._dataProducers.set(dataProducer.id, dataProducer); + this.handleDataProducer(dataProducer); - // Emit observer event. - this._observer.safeEmit('newdataproducer', dataProducer); + // Emit observer event. + this._observer.safeEmit('newdataproducer', dataProducer); - return dataProducer; - }, - 'transport.produceData()'); + return dataProducer; + }, 'transport.produceData()'); } /** * Create a DataConsumer */ - async consumeData( - { - id, - dataProducerId, - sctpStreamParameters, - label = '', - protocol = '', - appData = {} as ConsumerAppData - }: DataConsumerOptions - ): Promise> - { + async consumeData({ + id, + dataProducerId, + sctpStreamParameters, + label = '', + protocol = '', + appData = {} as ConsumerAppData, + }: DataConsumerOptions): Promise< + DataConsumer + > { logger.debug('consumeData()'); sctpStreamParameters = utils.clone(sctpStreamParameters); - if (this._closed) - { + if (this._closed) { throw new InvalidStateError('closed'); - } - else if (this._direction !== 'recv') - { + } else if (this._direction !== 'recv') { throw new UnsupportedError('not a receiving Transport'); - } - else if (!this._maxSctpMessageSize) - { + } else if (!this._maxSctpMessageSize) { throw new UnsupportedError('SCTP not enabled by remote Transport'); - } - else if (typeof id !== 'string') - { + } else if (typeof id !== 'string') { throw new TypeError('missing id'); - } - else if (typeof dataProducerId !== 'string') - { + } else if (typeof dataProducerId !== 'string') { throw new TypeError('missing dataProducerId'); - } - else if (this.listenerCount('connect') === 0 && this._connectionState === 'new') - { + } else if ( + this.listenerCount('connect') === 0 && + this._connectionState === 'new' + ) { throw new TypeError('no "connect" listener set into this transport'); - } - else if (appData && typeof appData !== 'object') - { + } else if (appData && typeof appData !== 'object') { throw new TypeError('if given, appData must be an object'); } @@ -925,54 +816,48 @@ export class Transport ortc.validateSctpStreamParameters(sctpStreamParameters); // Enqueue command. - return this._awaitQueue.push( - async () => - { - const { - dataChannel - } = await this._handler.receiveDataChannel( - { - sctpStreamParameters, - label, - protocol - }); + return this._awaitQueue.push(async () => { + const { dataChannel } = await this._handler.receiveDataChannel({ + sctpStreamParameters, + label, + protocol, + }); - const dataConsumer = new DataConsumer( - { - id, - dataProducerId, - dataChannel, - sctpStreamParameters, - appData - }); + const dataConsumer = new DataConsumer({ + id, + dataProducerId, + dataChannel, + sctpStreamParameters, + appData, + }); - this._dataConsumers.set(dataConsumer.id, dataConsumer); - this.handleDataConsumer(dataConsumer); + this._dataConsumers.set(dataConsumer.id, dataConsumer); + this.handleDataConsumer(dataConsumer); - // Emit observer event. - this._observer.safeEmit('newdataconsumer', dataConsumer); + // Emit observer event. + this._observer.safeEmit('newdataconsumer', dataConsumer); - return dataConsumer; - }, - 'transport.consumeData()'); + return dataConsumer; + }, 'transport.consumeData()'); } // This method is guaranteed to never throw. - private async createPendingConsumers(): Promise - { + private async createPendingConsumers< + ConsumerAppData extends AppData, + >(): Promise { this._consumerCreationInProgress = true; - this._awaitQueue.push( - async () => - { - if (this._pendingConsumerTasks.length === 0) - { - logger.debug('createPendingConsumers() | there is no Consumer to be created'); + this._awaitQueue + .push(async () => { + if (this._pendingConsumerTasks.length === 0) { + logger.debug( + 'createPendingConsumers() | there is no Consumer to be created', + ); return; } - const pendingConsumerTasks = [ ...this._pendingConsumerTasks ]; + const pendingConsumerTasks = [...this._pendingConsumerTasks]; // Clear pending Consumer tasks. this._pendingConsumerTasks = []; @@ -983,39 +868,35 @@ export class Transport // Fill options list. const optionsList: HandlerReceiveOptions[] = []; - for (const task of pendingConsumerTasks) - { + for (const task of pendingConsumerTasks) { const { id, kind, rtpParameters, streamId } = task.consumerOptions; - optionsList.push( - { - trackId : id!, - kind : kind as MediaKind, - rtpParameters, - streamId - }); + optionsList.push({ + trackId: id!, + kind: kind as MediaKind, + rtpParameters, + streamId, + }); } - try - { + try { const results = await this._handler.receive(optionsList); - for (let idx = 0; idx < results.length; ++idx) - { + for (let idx = 0; idx < results.length; ++idx) { const task = pendingConsumerTasks[idx]; const result = results[idx]; - const { id, producerId, kind, rtpParameters, appData } = task.consumerOptions; + const { id, producerId, kind, rtpParameters, appData } = + task.consumerOptions; const { localId, rtpReceiver, track } = result; - const consumer = new Consumer( - { - id : id!, - localId, - producerId : producerId!, - rtpReceiver, - track, - rtpParameters, - appData : appData as ConsumerAppData - }); + const consumer = new Consumer({ + id: id!, + localId, + producerId: producerId!, + rtpReceiver, + track, + rtpParameters, + appData: appData as ConsumerAppData, + }); this._consumers.set(consumer.id, consumer); this.handleConsumer(consumer); @@ -1024,9 +905,9 @@ export class Transport // has not yet been created, it's time to create it. if ( !this._probatorConsumerCreated && - !videoConsumerForProbator && kind === 'video' - ) - { + !videoConsumerForProbator && + kind === 'video' + ) { videoConsumerForProbator = consumer; } @@ -1035,50 +916,45 @@ export class Transport task.resolve!(consumer); } - } - catch (error) - { - for (const task of pendingConsumerTasks) - { + } catch (error) { + for (const task of pendingConsumerTasks) { task.reject!(error as Error); } } // If RTP probation must be handled, do it now. - if (videoConsumerForProbator) - { - try - { - const probatorRtpParameters = - ortc.generateProbatorRtpParameters(videoConsumerForProbator!.rtpParameters); + if (videoConsumerForProbator) { + try { + const probatorRtpParameters = ortc.generateProbatorRtpParameters( + videoConsumerForProbator!.rtpParameters, + ); - await this._handler.receive( - [ { - trackId : 'probator', - kind : 'video', - rtpParameters : probatorRtpParameters - } ]); + await this._handler.receive([ + { + trackId: 'probator', + kind: 'video', + rtpParameters: probatorRtpParameters, + }, + ]); - logger.debug('createPendingConsumers() | Consumer for RTP probation created'); + logger.debug( + 'createPendingConsumers() | Consumer for RTP probation created', + ); this._probatorConsumerCreated = true; - } - catch (error) - { + } catch (error) { logger.error( 'createPendingConsumers() | failed to create Consumer for RTP probation:%o', - error); + error, + ); } } - }, - 'transport.createPendingConsumers()') - .then(() => - { + }, 'transport.createPendingConsumers()') + .then(() => { this._consumerCreationInProgress = false; // There are pending Consumer tasks, enqueue their creation. - if (this._pendingConsumerTasks.length > 0) - { + if (this._pendingConsumerTasks.length > 0) { this.createPendingConsumers(); } }) @@ -1086,184 +962,178 @@ export class Transport .catch(() => {}); } - private pausePendingConsumers() - { + private pausePendingConsumers() { this._consumerPauseInProgress = true; - this._awaitQueue.push( - async () => - { - if (this._pendingPauseConsumers.size === 0) - { - logger.debug('pausePendingConsumers() | there is no Consumer to be paused'); + this._awaitQueue + .push(async () => { + if (this._pendingPauseConsumers.size === 0) { + logger.debug( + 'pausePendingConsumers() | there is no Consumer to be paused', + ); return; } - const pendingPauseConsumers = Array.from(this._pendingPauseConsumers.values()); + const pendingPauseConsumers = Array.from( + this._pendingPauseConsumers.values(), + ); // Clear pending pause Consumer map. this._pendingPauseConsumers.clear(); - try - { - const localIds = pendingPauseConsumers - .map((consumer) => consumer.localId); + try { + const localIds = pendingPauseConsumers.map( + consumer => consumer.localId, + ); await this._handler.pauseReceiving(localIds); + } catch (error) { + logger.error( + 'pausePendingConsumers() | failed to pause Consumers:', + error, + ); } - catch (error) - { - logger.error('pausePendingConsumers() | failed to pause Consumers:', error); - } - }, - 'transport.pausePendingConsumers') - .then(() => - { + }, 'transport.pausePendingConsumers') + .then(() => { this._consumerPauseInProgress = false; // There are pending Consumers to be paused, do it. - if (this._pendingPauseConsumers.size > 0) - { + if (this._pendingPauseConsumers.size > 0) { this.pausePendingConsumers(); } }) // NOTE: We only get here when the await queue is closed. - .catch(() => { }); + .catch(() => {}); } - private resumePendingConsumers() - { + private resumePendingConsumers() { this._consumerResumeInProgress = true; - this._awaitQueue.push( - async () => - { - if (this._pendingResumeConsumers.size === 0) - { - logger.debug('resumePendingConsumers() | there is no Consumer to be resumed'); - + this._awaitQueue + .push(async () => { + if (this._pendingResumeConsumers.size === 0) { + logger.debug( + 'resumePendingConsumers() | there is no Consumer to be resumed', + ); + return; } - const pendingResumeConsumers = Array.from(this._pendingResumeConsumers.values()); + const pendingResumeConsumers = Array.from( + this._pendingResumeConsumers.values(), + ); // Clear pending resume Consumer map. this._pendingResumeConsumers.clear(); - try - { - const localIds = pendingResumeConsumers - .map((consumer) => consumer.localId); + try { + const localIds = pendingResumeConsumers.map( + consumer => consumer.localId, + ); await this._handler.resumeReceiving(localIds); + } catch (error) { + logger.error( + 'resumePendingConsumers() | failed to resume Consumers:', + error, + ); } - catch (error) - { - logger.error('resumePendingConsumers() | failed to resume Consumers:', error); - } - }, - 'transport.resumePendingConsumers') - .then(() => - { + }, 'transport.resumePendingConsumers') + .then(() => { this._consumerResumeInProgress = false; // There are pending Consumer to be resumed, do it. - if (this._pendingResumeConsumers.size > 0) - { + if (this._pendingResumeConsumers.size > 0) { this.resumePendingConsumers(); } }) // NOTE: We only get here when the await queue is closed. - .catch(() => { }); + .catch(() => {}); } - private closePendingConsumers() - { + private closePendingConsumers() { this._consumerCloseInProgress = true; - this._awaitQueue.push( - async () => - { - if (this._pendingCloseConsumers.size === 0) - { - logger.debug('closePendingConsumers() | there is no Consumer to be closed'); - + this._awaitQueue + .push(async () => { + if (this._pendingCloseConsumers.size === 0) { + logger.debug( + 'closePendingConsumers() | there is no Consumer to be closed', + ); + return; } - const pendingCloseConsumers = Array.from(this._pendingCloseConsumers.values()); + const pendingCloseConsumers = Array.from( + this._pendingCloseConsumers.values(), + ); // Clear pending close Consumer map. this._pendingCloseConsumers.clear(); - try - { + try { await this._handler.stopReceiving( - pendingCloseConsumers.map((consumer) => consumer.localId) + pendingCloseConsumers.map(consumer => consumer.localId), + ); + } catch (error) { + logger.error( + 'closePendingConsumers() | failed to close Consumers:', + error, ); } - catch (error) - { - logger.error('closePendingConsumers() | failed to close Consumers:', error); - } - }, - 'transport.closePendingConsumers') - .then(() => - { + }, 'transport.closePendingConsumers') + .then(() => { this._consumerCloseInProgress = false; // There are pending Consumer to be resumed, do it. - if (this._pendingCloseConsumers.size > 0) - { + if (this._pendingCloseConsumers.size > 0) { this.closePendingConsumers(); } }) // NOTE: We only get here when the await queue is closed. - .catch(() => { }); + .catch(() => {}); } - private handleHandler(): void - { + private handleHandler(): void { const handler = this._handler; - handler.on('@connect', ( - { dtlsParameters }: { dtlsParameters: DtlsParameters }, - callback: () => void, - errback: (error: Error) => void - ) => - { - if (this._closed) - { - errback(new InvalidStateError('closed')); + handler.on( + '@connect', + ( + { dtlsParameters }: { dtlsParameters: DtlsParameters }, + callback: () => void, + errback: (error: Error) => void, + ) => { + if (this._closed) { + errback(new InvalidStateError('closed')); - return; - } + return; + } - this.safeEmit('connect', { dtlsParameters }, callback, errback); - }); + this.safeEmit('connect', { dtlsParameters }, callback, errback); + }, + ); - handler.on('@icegatheringstatechange', (iceGatheringState: IceGatheringState) => - { - if (iceGatheringState === this._iceGatheringState) - { - return; - } + handler.on( + '@icegatheringstatechange', + (iceGatheringState: IceGatheringState) => { + if (iceGatheringState === this._iceGatheringState) { + return; + } - logger.debug('ICE gathering state changed to %s', iceGatheringState); + logger.debug('ICE gathering state changed to %s', iceGatheringState); - this._iceGatheringState = iceGatheringState; + this._iceGatheringState = iceGatheringState; - if (!this._closed) - { - this.safeEmit('icegatheringstatechange', iceGatheringState); - } - }); + if (!this._closed) { + this.safeEmit('icegatheringstatechange', iceGatheringState); + } + }, + ); - handler.on('@connectionstatechange', (connectionState: ConnectionState) => - { - if (connectionState === this._connectionState) - { + handler.on('@connectionstatechange', (connectionState: ConnectionState) => { + if (connectionState === this._connectionState) { return; } @@ -1271,100 +1141,107 @@ export class Transport this._connectionState = connectionState; - if (!this._closed) - { + if (!this._closed) { this.safeEmit('connectionstatechange', connectionState); } }); } - private handleProducer(producer: Producer): void - { - producer.on('@close', () => - { + private handleProducer(producer: Producer): void { + producer.on('@close', () => { this._producers.delete(producer.id); - if (this._closed) - { + if (this._closed) { return; } - this._awaitQueue.push( - async () => await this._handler.stopSending(producer.localId), - 'producer @close event') - .catch((error: Error) => logger.warn('producer.close() failed:%o', error)); + this._awaitQueue + .push( + async () => await this._handler.stopSending(producer.localId), + 'producer @close event', + ) + .catch((error: Error) => + logger.warn('producer.close() failed:%o', error), + ); }); - producer.on('@pause', (callback, errback) => - { - this._awaitQueue.push( - async () => await this._handler.pauseSending(producer.localId), - 'producer @pause event') + producer.on('@pause', (callback, errback) => { + this._awaitQueue + .push( + async () => await this._handler.pauseSending(producer.localId), + 'producer @pause event', + ) .then(callback) .catch(errback); }); - producer.on('@resume', (callback, errback) => - { - this._awaitQueue.push( - async () => await this._handler.resumeSending(producer.localId), - 'producer @resume event') + producer.on('@resume', (callback, errback) => { + this._awaitQueue + .push( + async () => await this._handler.resumeSending(producer.localId), + 'producer @resume event', + ) .then(callback) .catch(errback); }); - producer.on('@replacetrack', (track, callback, errback) => - { - this._awaitQueue.push( - async () => await this._handler.replaceTrack(producer.localId, track), - 'producer @replacetrack event') + producer.on('@replacetrack', (track, callback, errback) => { + this._awaitQueue + .push( + async () => await this._handler.replaceTrack(producer.localId, track), + 'producer @replacetrack event', + ) .then(callback) .catch(errback); }); - producer.on('@setmaxspatiallayer', (spatialLayer, callback, errback) => - { - this._awaitQueue.push( - async () => ( - await this._handler.setMaxSpatialLayer(producer.localId, spatialLayer) - ), 'producer @setmaxspatiallayer event') + producer.on('@setmaxspatiallayer', (spatialLayer, callback, errback) => { + this._awaitQueue + .push( + async () => + await this._handler.setMaxSpatialLayer( + producer.localId, + spatialLayer, + ), + 'producer @setmaxspatiallayer event', + ) .then(callback) .catch(errback); }); - producer.on('@setrtpencodingparameters', (params, callback, errback) => - { - this._awaitQueue.push( - async () => ( - await this._handler.setRtpEncodingParameters(producer.localId, params) - ), 'producer @setrtpencodingparameters event') + producer.on('@setrtpencodingparameters', (params, callback, errback) => { + this._awaitQueue + .push( + async () => + await this._handler.setRtpEncodingParameters( + producer.localId, + params, + ), + 'producer @setrtpencodingparameters event', + ) .then(callback) .catch(errback); }); - producer.on('@getstats', (callback, errback) => - { - if (this._closed) - { + producer.on('@getstats', (callback, errback) => { + if (this._closed) { return errback!(new InvalidStateError('closed')); } - this._handler.getSenderStats(producer.localId) + this._handler + .getSenderStats(producer.localId) .then(callback) .catch(errback); }); } - private handleConsumer(consumer: Consumer): void - { - consumer.on('@close', () => - { + private handleConsumer(consumer: Consumer): void { + consumer.on('@close', () => { this._consumers.delete(consumer.id); this._pendingPauseConsumers.delete(consumer.id); this._pendingResumeConsumers.delete(consumer.id); - if (this._closed) - { + if (this._closed) { return; } @@ -1372,17 +1249,14 @@ export class Transport this._pendingCloseConsumers.set(consumer.id, consumer); // There is no Consumer close in progress, do it now. - if (this._consumerCloseInProgress === false) - { + if (this._consumerCloseInProgress === false) { this.closePendingConsumers(); } }); - consumer.on('@pause', () => - { + consumer.on('@pause', () => { // If Consumer is pending to be resumed, remove from pending resume list. - if (this._pendingResumeConsumers.has(consumer.id)) - { + if (this._pendingResumeConsumers.has(consumer.id)) { this._pendingResumeConsumers.delete(consumer.id); } @@ -1390,25 +1264,20 @@ export class Transport this._pendingPauseConsumers.set(consumer.id, consumer); // There is no Consumer pause in progress, do it now. - queueMicrotask(() => - { - if (this._closed) - { + queueMicrotask(() => { + if (this._closed) { return; } - if (this._consumerPauseInProgress === false) - { + if (this._consumerPauseInProgress === false) { this.pausePendingConsumers(); } }); }); - consumer.on('@resume', () => - { + consumer.on('@resume', () => { // If Consumer is pending to be paused, remove from pending pause list. - if (this._pendingPauseConsumers.has(consumer.id)) - { + if (this._pendingPauseConsumers.has(consumer.id)) { this._pendingPauseConsumers.delete(consumer.id); } @@ -1416,45 +1285,37 @@ export class Transport this._pendingResumeConsumers.set(consumer.id, consumer); // There is no Consumer resume in progress, do it now. - queueMicrotask(() => - { - if (this._closed) - { + queueMicrotask(() => { + if (this._closed) { return; } - if (this._consumerResumeInProgress === false) - { + if (this._consumerResumeInProgress === false) { this.resumePendingConsumers(); } }); }); - consumer.on('@getstats', (callback, errback) => - { - if (this._closed) - { + consumer.on('@getstats', (callback, errback) => { + if (this._closed) { return errback!(new InvalidStateError('closed')); } - this._handler.getReceiverStats(consumer.localId) + this._handler + .getReceiverStats(consumer.localId) .then(callback) .catch(errback); }); } - private handleDataProducer(dataProducer: DataProducer): void - { - dataProducer.on('@close', () => - { + private handleDataProducer(dataProducer: DataProducer): void { + dataProducer.on('@close', () => { this._dataProducers.delete(dataProducer.id); }); } - private handleDataConsumer(dataConsumer: DataConsumer): void - { - dataConsumer.on('@close', () => - { + private handleDataConsumer(dataConsumer: DataConsumer): void { + dataConsumer.on('@close', () => { this._dataConsumers.delete(dataConsumer.id); }); } diff --git a/src/errors.ts b/src/errors.ts index 859c5a9d..0f1f0493 100644 --- a/src/errors.ts +++ b/src/errors.ts @@ -1,22 +1,18 @@ /** * Error indicating not support for something. */ -export class UnsupportedError extends Error -{ - constructor(message: string) - { +export class UnsupportedError extends Error { + constructor(message: string) { super(message); this.name = 'UnsupportedError'; - if (Error.hasOwnProperty('captureStackTrace')) // Just in V8. - { + if (Error.hasOwnProperty('captureStackTrace')) { + // Just in V8. // @ts-ignore Error.captureStackTrace(this, UnsupportedError); - } - else - { - this.stack = (new Error(message)).stack; + } else { + this.stack = new Error(message).stack; } } } @@ -24,22 +20,18 @@ export class UnsupportedError extends Error /** * Error produced when calling a method in an invalid state. */ -export class InvalidStateError extends Error -{ - constructor(message: string) - { +export class InvalidStateError extends Error { + constructor(message: string) { super(message); this.name = 'InvalidStateError'; - if (Error.hasOwnProperty('captureStackTrace')) // Just in V8. - { + if (Error.hasOwnProperty('captureStackTrace')) { + // Just in V8. // @ts-ignore Error.captureStackTrace(this, InvalidStateError); - } - else - { - this.stack = (new Error(message)).stack; + } else { + this.stack = new Error(message).stack; } } } diff --git a/src/handlers/Chrome111.ts b/src/handlers/Chrome111.ts index ceb86208..b783d5e2 100644 --- a/src/handlers/Chrome111.ts +++ b/src/handlers/Chrome111.ts @@ -17,7 +17,7 @@ import { HandlerSendDataChannelOptions, HandlerSendDataChannelResult, HandlerReceiveDataChannelOptions, - HandlerReceiveDataChannelResult + HandlerReceiveDataChannelResult, } from './HandlerInterface'; import { RemoteSdp } from './sdp/RemoteSdp'; import { parse as parseScalabilityMode } from '../scalabilityModes'; @@ -29,8 +29,7 @@ const logger = new Logger('Chrome111'); const SCTP_NUM_STREAMS = { OS: 1024, MIS: 1024 }; -export class Chrome111 extends HandlerInterface -{ +export class Chrome111 extends HandlerInterface { // Closed flag. private _closed = false; // Handler direction. @@ -62,203 +61,183 @@ export class Chrome111 extends HandlerInterface /** * Creates a factory function. */ - static createFactory(): HandlerFactory - { + static createFactory(): HandlerFactory { return (): Chrome111 => new Chrome111(); } - constructor() - { + constructor() { super(); } - get name(): string - { + get name(): string { return 'Chrome111'; } - close(): void - { + close(): void { logger.debug('close()'); - if (this._closed) - { + if (this._closed) { return; } this._closed = true; // Close RTCPeerConnection. - if (this._pc) - { - try { this._pc.close(); } - catch (error) {} + if (this._pc) { + try { + this._pc.close(); + } catch (error) {} } this.emit('@close'); } - async getNativeRtpCapabilities(): Promise - { + async getNativeRtpCapabilities(): Promise { logger.debug('getNativeRtpCapabilities()'); - const pc = new (RTCPeerConnection as any)( - { - iceServers : [], - iceTransportPolicy : 'all', - bundlePolicy : 'max-bundle', - rtcpMuxPolicy : 'require', - sdpSemantics : 'unified-plan' - }); + const pc = new (RTCPeerConnection as any)({ + iceServers: [], + iceTransportPolicy: 'all', + bundlePolicy: 'max-bundle', + rtcpMuxPolicy: 'require', + sdpSemantics: 'unified-plan', + }); - try - { + try { pc.addTransceiver('audio'); pc.addTransceiver('video'); const offer = await pc.createOffer(); - try { pc.close(); } - catch (error) {} + try { + pc.close(); + } catch (error) {} const sdpObject = sdpTransform.parse(offer.sdp); - const nativeRtpCapabilities = - sdpCommonUtils.extractRtpCapabilities({ sdpObject }); + const nativeRtpCapabilities = sdpCommonUtils.extractRtpCapabilities({ + sdpObject, + }); // libwebrtc supports NACK for OPUS but doesn't announce it. ortcUtils.addNackSuppportForOpus(nativeRtpCapabilities); return nativeRtpCapabilities; - } - catch (error) - { - try { pc.close(); } - catch (error2) {} + } catch (error) { + try { + pc.close(); + } catch (error2) {} throw error; } } - async getNativeSctpCapabilities(): Promise - { + async getNativeSctpCapabilities(): Promise { logger.debug('getNativeSctpCapabilities()'); return { - numStreams : SCTP_NUM_STREAMS + numStreams: SCTP_NUM_STREAMS, }; } - run( - { - direction, - iceParameters, - iceCandidates, - dtlsParameters, - sctpParameters, - iceServers, - iceTransportPolicy, - additionalSettings, - proprietaryConstraints, - extendedRtpCapabilities - }: HandlerRunOptions - ): void - { + run({ + direction, + iceParameters, + iceCandidates, + dtlsParameters, + sctpParameters, + iceServers, + iceTransportPolicy, + additionalSettings, + proprietaryConstraints, + extendedRtpCapabilities, + }: HandlerRunOptions): void { this.assertNotClosed(); logger.debug('run()'); this._direction = direction; - this._remoteSdp = new RemoteSdp( - { - iceParameters, - iceCandidates, - dtlsParameters, - sctpParameters - }); + this._remoteSdp = new RemoteSdp({ + iceParameters, + iceCandidates, + dtlsParameters, + sctpParameters, + }); - this._sendingRtpParametersByKind = - { - audio : ortc.getSendingRtpParameters('audio', extendedRtpCapabilities), - video : ortc.getSendingRtpParameters('video', extendedRtpCapabilities) + this._sendingRtpParametersByKind = { + audio: ortc.getSendingRtpParameters('audio', extendedRtpCapabilities), + video: ortc.getSendingRtpParameters('video', extendedRtpCapabilities), }; - this._sendingRemoteRtpParametersByKind = - { - audio : ortc.getSendingRemoteRtpParameters('audio', extendedRtpCapabilities), - video : ortc.getSendingRemoteRtpParameters('video', extendedRtpCapabilities) + this._sendingRemoteRtpParametersByKind = { + audio: ortc.getSendingRemoteRtpParameters( + 'audio', + extendedRtpCapabilities, + ), + video: ortc.getSendingRemoteRtpParameters( + 'video', + extendedRtpCapabilities, + ), }; - if (dtlsParameters.role && dtlsParameters.role !== 'auto') - { - this._forcedLocalDtlsRole = dtlsParameters.role === 'server' - ? 'client' - : 'server'; + if (dtlsParameters.role && dtlsParameters.role !== 'auto') { + this._forcedLocalDtlsRole = + dtlsParameters.role === 'server' ? 'client' : 'server'; } this._pc = new (RTCPeerConnection as any)( { - iceServers : iceServers || [], - iceTransportPolicy : iceTransportPolicy || 'all', - bundlePolicy : 'max-bundle', - rtcpMuxPolicy : 'require', - sdpSemantics : 'unified-plan', - ...additionalSettings + iceServers: iceServers || [], + iceTransportPolicy: iceTransportPolicy || 'all', + bundlePolicy: 'max-bundle', + rtcpMuxPolicy: 'require', + sdpSemantics: 'unified-plan', + ...additionalSettings, }, - proprietaryConstraints); + proprietaryConstraints, + ); - this._pc.addEventListener('icegatheringstatechange', () => - { + this._pc.addEventListener('icegatheringstatechange', () => { this.emit('@icegatheringstatechange', this._pc.iceGatheringState); }); - if (this._pc.connectionState) - { - this._pc.addEventListener('connectionstatechange', () => - { + if (this._pc.connectionState) { + this._pc.addEventListener('connectionstatechange', () => { this.emit('@connectionstatechange', this._pc.connectionState); }); - } - else - { + } else { logger.warn( - 'run() | pc.connectionState not supported, using pc.iceConnectionState'); + 'run() | pc.connectionState not supported, using pc.iceConnectionState', + ); - this._pc.addEventListener('iceconnectionstatechange', () => - { - switch (this._pc.iceConnectionState) - { - case 'checking': - { + this._pc.addEventListener('iceconnectionstatechange', () => { + switch (this._pc.iceConnectionState) { + case 'checking': { this.emit('@connectionstatechange', 'connecting'); break; } case 'connected': - case 'completed': - { + case 'completed': { this.emit('@connectionstatechange', 'connected'); break; } - case 'failed': - { + case 'failed': { this.emit('@connectionstatechange', 'failed'); break; } - case 'disconnected': - { + case 'disconnected': { this.emit('@connectionstatechange', 'disconnected'); break; } - case 'closed': - { + case 'closed': { this.emit('@connectionstatechange', 'closed'); break; @@ -268,8 +247,7 @@ export class Chrome111 extends HandlerInterface } } - async updateIceServers(iceServers: RTCIceServer[]): Promise - { + async updateIceServers(iceServers: RTCIceServer[]): Promise { this.assertNotClosed(); logger.debug('updateIceServers()'); @@ -281,8 +259,7 @@ export class Chrome111 extends HandlerInterface this._pc.setConfiguration(configuration); } - async restartIce(iceParameters: IceParameters): Promise - { + async restartIce(iceParameters: IceParameters): Promise { this.assertNotClosed(); logger.debug('restartIce()'); @@ -290,18 +267,17 @@ export class Chrome111 extends HandlerInterface // Provide the remote SDP handler with new remote ICE parameters. this._remoteSdp!.updateIceParameters(iceParameters); - if (!this._transportReady) - { + if (!this._transportReady) { return; } - if (this._direction === 'send') - { + if (this._direction === 'send') { const offer = await this._pc.createOffer({ iceRestart: true }); logger.debug( 'restartIce() | calling pc.setLocalDescription() [offer:%o]', - offer); + offer, + ); await this._pc.setLocalDescription(offer); @@ -309,17 +285,17 @@ export class Chrome111 extends HandlerInterface logger.debug( 'restartIce() | calling pc.setRemoteDescription() [answer:%o]', - answer); + answer, + ); await this._pc.setRemoteDescription(answer); - } - else - { + } else { const offer = { type: 'offer', sdp: this._remoteSdp!.getSdp() }; logger.debug( 'restartIce() | calling pc.setRemoteDescription() [offer:%o]', - offer); + offer, + ); await this._pc.setRemoteDescription(offer); @@ -327,32 +303,32 @@ export class Chrome111 extends HandlerInterface logger.debug( 'restartIce() | calling pc.setLocalDescription() [answer:%o]', - answer); + answer, + ); await this._pc.setLocalDescription(answer); } } - async getTransportStats(): Promise - { + async getTransportStats(): Promise { this.assertNotClosed(); return this._pc.getStats(); } - async send( - { track, encodings, codecOptions, codec }: HandlerSendOptions - ): Promise - { + async send({ + track, + encodings, + codecOptions, + codec, + }: HandlerSendOptions): Promise { this.assertNotClosed(); this.assertSendDirection(); logger.debug('send() [kind:%s, track.id:%s]', track.kind, track.id); - if (encodings && encodings.length > 1) - { - encodings.forEach((encoding, idx: number) => - { + if (encodings && encodings.length > 1) { + encodings.forEach((encoding, idx: number) => { encoding.rid = `r${idx}`; }); @@ -365,62 +341,60 @@ export class Chrome111 extends HandlerInterface let nextRid = 1; let maxTemporalLayers = 1; - for (const encoding of encodings) - { + for (const encoding of encodings) { const temporalLayers = encoding.scalabilityMode ? parseScalabilityMode(encoding.scalabilityMode).temporalLayers : 3; - if (temporalLayers > maxTemporalLayers) - { + if (temporalLayers > maxTemporalLayers) { maxTemporalLayers = temporalLayers; } } - for (const encoding of encodings) - { + for (const encoding of encodings) { encoding.rid = `r${nextRid++}`; encoding.scalabilityMode = `L1T${maxTemporalLayers}`; } } - const sendingRtpParameters: RtpParameters = - utils.clone(this._sendingRtpParametersByKind![track.kind]); + const sendingRtpParameters: RtpParameters = utils.clone( + this._sendingRtpParametersByKind![track.kind], + ); // This may throw. - sendingRtpParameters.codecs = - ortc.reduceCodecs(sendingRtpParameters.codecs, codec); + sendingRtpParameters.codecs = ortc.reduceCodecs( + sendingRtpParameters.codecs, + codec, + ); const sendingRemoteRtpParameters: RtpParameters = - utils.clone(this._sendingRemoteRtpParametersByKind![track.kind]); + utils.clone( + this._sendingRemoteRtpParametersByKind![track.kind], + ); // This may throw. - sendingRemoteRtpParameters.codecs = - ortc.reduceCodecs(sendingRemoteRtpParameters.codecs, codec); + sendingRemoteRtpParameters.codecs = ortc.reduceCodecs( + sendingRemoteRtpParameters.codecs, + codec, + ); const mediaSectionIdx = this._remoteSdp!.getNextMediaSectionIdx(); - const transceiver = this._pc.addTransceiver( - track, - { - direction : 'sendonly', - streams : [ this._sendStream ], - sendEncodings : encodings - }); + const transceiver = this._pc.addTransceiver(track, { + direction: 'sendonly', + streams: [this._sendStream], + sendEncodings: encodings, + }); const offer = await this._pc.createOffer(); let localSdpObject = sdpTransform.parse(offer.sdp); - if (!this._transportReady) - { - await this.setupTransport( - { - localDtlsRole : this._forcedLocalDtlsRole ?? 'client', - localSdpObject - }); + if (!this._transportReady) { + await this.setupTransport({ + localDtlsRole: this._forcedLocalDtlsRole ?? 'client', + localSdpObject, + }); } - logger.debug( - 'send() | calling pc.setLocalDescription() [offer:%o]', - offer); + logger.debug('send() | calling pc.setLocalDescription() [offer:%o]', offer); await this._pc.setLocalDescription(offer); @@ -435,47 +409,47 @@ export class Chrome111 extends HandlerInterface const offerMediaObject = localSdpObject.media[mediaSectionIdx.idx]; // Set RTCP CNAME. - sendingRtpParameters.rtcp!.cname = - sdpCommonUtils.getCname({ offerMediaObject }); + sendingRtpParameters.rtcp!.cname = sdpCommonUtils.getCname({ + offerMediaObject, + }); // Set RTP encodings by parsing the SDP offer if no encodings are given. - if (!encodings) - { - sendingRtpParameters.encodings = - sdpUnifiedPlanUtils.getRtpEncodings({ offerMediaObject }); + if (!encodings) { + sendingRtpParameters.encodings = sdpUnifiedPlanUtils.getRtpEncodings({ + offerMediaObject, + }); } // Set RTP encodings by parsing the SDP offer and complete them with given // one if just a single encoding has been given. - else if (encodings.length === 1) - { - const newEncodings = - sdpUnifiedPlanUtils.getRtpEncodings({ offerMediaObject }); + else if (encodings.length === 1) { + const newEncodings = sdpUnifiedPlanUtils.getRtpEncodings({ + offerMediaObject, + }); Object.assign(newEncodings[0], encodings[0]); sendingRtpParameters.encodings = newEncodings; } // Otherwise if more than 1 encoding are given use them verbatim. - else - { + else { sendingRtpParameters.encodings = encodings; } - this._remoteSdp!.send( - { - offerMediaObject, - reuseMid : mediaSectionIdx.reuseMid, - offerRtpParameters : sendingRtpParameters, - answerRtpParameters : sendingRemoteRtpParameters, - codecOptions, - extmapAllowMixed : true - }); + this._remoteSdp!.send({ + offerMediaObject, + reuseMid: mediaSectionIdx.reuseMid, + offerRtpParameters: sendingRtpParameters, + answerRtpParameters: sendingRemoteRtpParameters, + codecOptions, + extmapAllowMixed: true, + }); const answer = { type: 'answer', sdp: this._remoteSdp!.getSdp() }; logger.debug( 'send() | calling pc.setRemoteDescription() [answer:%o]', - answer); + answer, + ); await this._pc.setRemoteDescription(answer); @@ -484,26 +458,23 @@ export class Chrome111 extends HandlerInterface return { localId, - rtpParameters : sendingRtpParameters, - rtpSender : transceiver.sender + rtpParameters: sendingRtpParameters, + rtpSender: transceiver.sender, }; } - async stopSending(localId: string): Promise - { + async stopSending(localId: string): Promise { this.assertSendDirection(); logger.debug('stopSending() [localId:%s]', localId); - if (this._closed) - { + if (this._closed) { return; } const transceiver = this._mapMidTransceiver.get(localId); - if (!transceiver) - { + if (!transceiver) { throw new Error('associated RTCRtpTransceiver not found'); } @@ -511,24 +482,22 @@ export class Chrome111 extends HandlerInterface this._pc.removeTrack(transceiver.sender); - const mediaSectionClosed = - this._remoteSdp!.closeMediaSection(transceiver.mid!); + const mediaSectionClosed = this._remoteSdp!.closeMediaSection( + transceiver.mid!, + ); - if (mediaSectionClosed) - { - try - { + if (mediaSectionClosed) { + try { transceiver.stop(); - } - catch (error) - {} + } catch (error) {} } const offer = await this._pc.createOffer(); logger.debug( 'stopSending() | calling pc.setLocalDescription() [offer:%o]', - offer); + offer, + ); await this._pc.setLocalDescription(offer); @@ -536,15 +505,15 @@ export class Chrome111 extends HandlerInterface logger.debug( 'stopSending() | calling pc.setRemoteDescription() [answer:%o]', - answer); + answer, + ); await this._pc.setRemoteDescription(answer); this._mapMidTransceiver.delete(localId); } - async pauseSending(localId: string): Promise - { + async pauseSending(localId: string): Promise { this.assertNotClosed(); this.assertSendDirection(); @@ -552,8 +521,7 @@ export class Chrome111 extends HandlerInterface const transceiver = this._mapMidTransceiver.get(localId); - if (!transceiver) - { + if (!transceiver) { throw new Error('associated RTCRtpTransceiver not found'); } @@ -564,7 +532,8 @@ export class Chrome111 extends HandlerInterface logger.debug( 'pauseSending() | calling pc.setLocalDescription() [offer:%o]', - offer); + offer, + ); await this._pc.setLocalDescription(offer); @@ -572,13 +541,13 @@ export class Chrome111 extends HandlerInterface logger.debug( 'pauseSending() | calling pc.setRemoteDescription() [answer:%o]', - answer); + answer, + ); await this._pc.setRemoteDescription(answer); } - async resumeSending(localId: string): Promise - { + async resumeSending(localId: string): Promise { this.assertNotClosed(); this.assertSendDirection(); @@ -588,8 +557,7 @@ export class Chrome111 extends HandlerInterface this._remoteSdp!.resumeSendingMediaSection(localId); - if (!transceiver) - { + if (!transceiver) { throw new Error('associated RTCRtpTransceiver not found'); } @@ -599,7 +567,8 @@ export class Chrome111 extends HandlerInterface logger.debug( 'resumeSending() | calling pc.setLocalDescription() [offer:%o]', - offer); + offer, + ); await this._pc.setLocalDescription(offer); @@ -607,67 +576,68 @@ export class Chrome111 extends HandlerInterface logger.debug( 'resumeSending() | calling pc.setRemoteDescription() [answer:%o]', - answer); + answer, + ); await this._pc.setRemoteDescription(answer); } async replaceTrack( - localId: string, track: MediaStreamTrack | null - ): Promise - { + localId: string, + track: MediaStreamTrack | null, + ): Promise { this.assertNotClosed(); this.assertSendDirection(); - if (track) - { + if (track) { logger.debug( - 'replaceTrack() [localId:%s, track.id:%s]', localId, track.id); - } - else - { + 'replaceTrack() [localId:%s, track.id:%s]', + localId, + track.id, + ); + } else { logger.debug('replaceTrack() [localId:%s, no track]', localId); } const transceiver = this._mapMidTransceiver.get(localId); - if (!transceiver) - { + if (!transceiver) { throw new Error('associated RTCRtpTransceiver not found'); } await transceiver.sender.replaceTrack(track); } - async setMaxSpatialLayer(localId: string, spatialLayer: number): Promise - { + async setMaxSpatialLayer( + localId: string, + spatialLayer: number, + ): Promise { this.assertNotClosed(); this.assertSendDirection(); logger.debug( 'setMaxSpatialLayer() [localId:%s, spatialLayer:%s]', - localId, spatialLayer); + localId, + spatialLayer, + ); const transceiver = this._mapMidTransceiver.get(localId); - if (!transceiver) - { + if (!transceiver) { throw new Error('associated RTCRtpTransceiver not found'); } const parameters = transceiver.sender.getParameters(); - parameters.encodings.forEach((encoding: RTCRtpEncodingParameters, idx: number) => - { - if (idx <= spatialLayer) - { - encoding.active = true; - } - else - { - encoding.active = false; - } - }); + parameters.encodings.forEach( + (encoding: RTCRtpEncodingParameters, idx: number) => { + if (idx <= spatialLayer) { + encoding.active = true; + } else { + encoding.active = false; + } + }, + ); await transceiver.sender.setParameters(parameters); @@ -677,7 +647,8 @@ export class Chrome111 extends HandlerInterface logger.debug( 'setMaxSpatialLayer() | calling pc.setLocalDescription() [offer:%o]', - offer); + offer, + ); await this._pc.setLocalDescription(offer); @@ -685,33 +656,35 @@ export class Chrome111 extends HandlerInterface logger.debug( 'setMaxSpatialLayer() | calling pc.setRemoteDescription() [answer:%o]', - answer); + answer, + ); await this._pc.setRemoteDescription(answer); } - async setRtpEncodingParameters(localId: string, params: any): Promise - { + async setRtpEncodingParameters(localId: string, params: any): Promise { this.assertNotClosed(); this.assertSendDirection(); logger.debug( 'setRtpEncodingParameters() [localId:%s, params:%o]', - localId, params); + localId, + params, + ); const transceiver = this._mapMidTransceiver.get(localId); - if (!transceiver) - { + if (!transceiver) { throw new Error('associated RTCRtpTransceiver not found'); } const parameters = transceiver.sender.getParameters(); - parameters.encodings.forEach((encoding: RTCRtpEncodingParameters, idx: number) => - { - parameters.encodings[idx] = { ...encoding, ...params }; - }); + parameters.encodings.forEach( + (encoding: RTCRtpEncodingParameters, idx: number) => { + parameters.encodings[idx] = { ...encoding, ...params }; + }, + ); await transceiver.sender.setParameters(parameters); @@ -721,7 +694,8 @@ export class Chrome111 extends HandlerInterface logger.debug( 'setRtpEncodingParameters() | calling pc.setLocalDescription() [offer:%o]', - offer); + offer, + ); await this._pc.setLocalDescription(offer); @@ -729,47 +703,42 @@ export class Chrome111 extends HandlerInterface logger.debug( 'setRtpEncodingParameters() | calling pc.setRemoteDescription() [answer:%o]', - answer); + answer, + ); await this._pc.setRemoteDescription(answer); } - async getSenderStats(localId: string): Promise - { + async getSenderStats(localId: string): Promise { this.assertNotClosed(); this.assertSendDirection(); const transceiver = this._mapMidTransceiver.get(localId); - if (!transceiver) - { + if (!transceiver) { throw new Error('associated RTCRtpTransceiver not found'); } return transceiver.sender.getStats(); } - async sendDataChannel( - { - ordered, - maxPacketLifeTime, - maxRetransmits, - label, - protocol - }: HandlerSendDataChannelOptions - ): Promise - { + async sendDataChannel({ + ordered, + maxPacketLifeTime, + maxRetransmits, + label, + protocol, + }: HandlerSendDataChannelOptions): Promise { this.assertNotClosed(); this.assertSendDirection(); - const options = - { - negotiated : true, - id : this._nextSendSctpStreamId, + const options = { + negotiated: true, + id: this._nextSendSctpStreamId, ordered, maxPacketLifeTime, maxRetransmits, - protocol + protocol, }; logger.debug('sendDataChannel() [options:%o]', options); @@ -782,25 +751,24 @@ export class Chrome111 extends HandlerInterface // If this is the first DataChannel we need to create the SDP answer with // m=application section. - if (!this._hasDataChannelMediaSection) - { + if (!this._hasDataChannelMediaSection) { const offer = await this._pc.createOffer(); const localSdpObject = sdpTransform.parse(offer.sdp); - const offerMediaObject = localSdpObject.media - .find((m: any) => m.type === 'application'); + const offerMediaObject = localSdpObject.media.find( + (m: any) => m.type === 'application', + ); - if (!this._transportReady) - { - await this.setupTransport( - { - localDtlsRole : this._forcedLocalDtlsRole ?? 'client', - localSdpObject - }); + if (!this._transportReady) { + await this.setupTransport({ + localDtlsRole: this._forcedLocalDtlsRole ?? 'client', + localSdpObject, + }); } logger.debug( 'sendDataChannel() | calling pc.setLocalDescription() [offer:%o]', - offer); + offer, + ); await this._pc.setLocalDescription(offer); @@ -810,36 +778,34 @@ export class Chrome111 extends HandlerInterface logger.debug( 'sendDataChannel() | calling pc.setRemoteDescription() [answer:%o]', - answer); + answer, + ); await this._pc.setRemoteDescription(answer); this._hasDataChannelMediaSection = true; } - const sctpStreamParameters: SctpStreamParameters = - { - streamId : options.id, - ordered : options.ordered, - maxPacketLifeTime : options.maxPacketLifeTime, - maxRetransmits : options.maxRetransmits + const sctpStreamParameters: SctpStreamParameters = { + streamId: options.id, + ordered: options.ordered, + maxPacketLifeTime: options.maxPacketLifeTime, + maxRetransmits: options.maxRetransmits, }; return { dataChannel, sctpStreamParameters }; } async receive( - optionsList: HandlerReceiveOptions[] - ) : Promise - { + optionsList: HandlerReceiveOptions[], + ): Promise { this.assertNotClosed(); this.assertRecvDirection(); const results: HandlerReceiveResult[] = []; const mapLocalId: Map = new Map(); - for (const options of optionsList) - { + for (const options of optionsList) { const { trackId, kind, rtpParameters, streamId } = options; logger.debug('receive() [trackId:%s, kind:%s]', trackId, kind); @@ -848,80 +814,75 @@ export class Chrome111 extends HandlerInterface mapLocalId.set(trackId, localId); - this._remoteSdp!.receive( - { - mid : localId, - kind, - offerRtpParameters : rtpParameters, - streamId : streamId || rtpParameters.rtcp!.cname!, - trackId - }); + this._remoteSdp!.receive({ + mid: localId, + kind, + offerRtpParameters: rtpParameters, + streamId: streamId || rtpParameters.rtcp!.cname!, + trackId, + }); } const offer = { type: 'offer', sdp: this._remoteSdp!.getSdp() }; logger.debug( 'receive() | calling pc.setRemoteDescription() [offer:%o]', - offer); + offer, + ); await this._pc.setRemoteDescription(offer); let answer = await this._pc.createAnswer(); const localSdpObject = sdpTransform.parse(answer.sdp); - for (const options of optionsList) - { + for (const options of optionsList) { const { trackId, rtpParameters } = options; const localId = mapLocalId.get(trackId); - const answerMediaObject = localSdpObject.media - .find((m: any) => String(m.mid) === localId); + const answerMediaObject = localSdpObject.media.find( + (m: any) => String(m.mid) === localId, + ); // May need to modify codec parameters in the answer based on codec // parameters in the offer. - sdpCommonUtils.applyCodecParameters( - { - offerRtpParameters : rtpParameters, - answerMediaObject - }); + sdpCommonUtils.applyCodecParameters({ + offerRtpParameters: rtpParameters, + answerMediaObject, + }); } answer = { type: 'answer', sdp: sdpTransform.write(localSdpObject) }; - if (!this._transportReady) - { - await this.setupTransport( - { - localDtlsRole : this._forcedLocalDtlsRole ?? 'client', - localSdpObject - }); + if (!this._transportReady) { + await this.setupTransport({ + localDtlsRole: this._forcedLocalDtlsRole ?? 'client', + localSdpObject, + }); } logger.debug( 'receive() | calling pc.setLocalDescription() [answer:%o]', - answer); + answer, + ); await this._pc.setLocalDescription(answer); - for (const options of optionsList) - { + for (const options of optionsList) { const { trackId } = options; const localId = mapLocalId.get(trackId)!; - const transceiver = this._pc.getTransceivers() + const transceiver = this._pc + .getTransceivers() .find((t: RTCRtpTransceiver) => t.mid === localId); - if (!transceiver) - { + if (!transceiver) { throw new Error('new RTCRtpTransceiver not found'); - } - else - { + } else { // Store in the map. this._mapMidTransceiver.set(localId, transceiver); results.push({ localId, - track : transceiver.receiver.track, - rtpReceiver : transceiver.receiver + track: transceiver.receiver.track, + rtpReceiver: transceiver.receiver, }); } } @@ -929,23 +890,19 @@ export class Chrome111 extends HandlerInterface return results; } - async stopReceiving(localIds: string[]): Promise - { + async stopReceiving(localIds: string[]): Promise { this.assertRecvDirection(); - if (this._closed) - { + if (this._closed) { return; } - for (const localId of localIds) - { + for (const localId of localIds) { logger.debug('stopReceiving() [localId:%s]', localId); const transceiver = this._mapMidTransceiver.get(localId); - if (!transceiver) - { + if (!transceiver) { throw new Error('associated RTCRtpTransceiver not found'); } @@ -956,7 +913,8 @@ export class Chrome111 extends HandlerInterface logger.debug( 'stopReceiving() | calling pc.setRemoteDescription() [offer:%o]', - offer); + offer, + ); await this._pc.setRemoteDescription(offer); @@ -964,29 +922,26 @@ export class Chrome111 extends HandlerInterface logger.debug( 'stopReceiving() | calling pc.setLocalDescription() [answer:%o]', - answer); + answer, + ); await this._pc.setLocalDescription(answer); - for (const localId of localIds) - { + for (const localId of localIds) { this._mapMidTransceiver.delete(localId); } } - async pauseReceiving(localIds: string[]): Promise - { + async pauseReceiving(localIds: string[]): Promise { this.assertNotClosed(); this.assertRecvDirection(); - for (const localId of localIds) - { + for (const localId of localIds) { logger.debug('pauseReceiving() [localId:%s]', localId); const transceiver = this._mapMidTransceiver.get(localId); - if (!transceiver) - { + if (!transceiver) { throw new Error('associated RTCRtpTransceiver not found'); } @@ -998,7 +953,8 @@ export class Chrome111 extends HandlerInterface logger.debug( 'pauseReceiving() | calling pc.setRemoteDescription() [offer:%o]', - offer); + offer, + ); await this._pc.setRemoteDescription(offer); @@ -1006,24 +962,22 @@ export class Chrome111 extends HandlerInterface logger.debug( 'pauseReceiving() | calling pc.setLocalDescription() [answer:%o]', - answer); + answer, + ); await this._pc.setLocalDescription(answer); } - async resumeReceiving(localIds: string[]): Promise - { + async resumeReceiving(localIds: string[]): Promise { this.assertNotClosed(); this.assertRecvDirection(); - for (const localId of localIds) - { + for (const localId of localIds) { logger.debug('resumeReceiving() [localId:%s]', localId); const transceiver = this._mapMidTransceiver.get(localId); - if (!transceiver) - { + if (!transceiver) { throw new Error('associated RTCRtpTransceiver not found'); } @@ -1035,7 +989,8 @@ export class Chrome111 extends HandlerInterface logger.debug( 'resumeReceiving() | calling pc.setRemoteDescription() [offer:%o]', - offer); + offer, + ); await this._pc.setRemoteDescription(offer); @@ -1043,30 +998,30 @@ export class Chrome111 extends HandlerInterface logger.debug( 'resumeReceiving() | calling pc.setLocalDescription() [answer:%o]', - answer); + answer, + ); await this._pc.setLocalDescription(answer); } - async getReceiverStats(localId: string): Promise - { + async getReceiverStats(localId: string): Promise { this.assertNotClosed(); this.assertRecvDirection(); const transceiver = this._mapMidTransceiver.get(localId); - if (!transceiver) - { + if (!transceiver) { throw new Error('associated RTCRtpTransceiver not found'); } return transceiver.receiver.getStats(); } - async receiveDataChannel( - { sctpStreamParameters, label, protocol }: HandlerReceiveDataChannelOptions - ): Promise - { + async receiveDataChannel({ + sctpStreamParameters, + label, + protocol, + }: HandlerReceiveDataChannelOptions): Promise { this.assertNotClosed(); this.assertRecvDirection(); @@ -1074,17 +1029,16 @@ export class Chrome111 extends HandlerInterface streamId, ordered, maxPacketLifeTime, - maxRetransmits + maxRetransmits, }: SctpStreamParameters = sctpStreamParameters; - const options = - { - negotiated : true, - id : streamId, + const options = { + negotiated: true, + id: streamId, ordered, maxPacketLifeTime, maxRetransmits, - protocol + protocol, }; logger.debug('receiveDataChannel() [options:%o]', options); @@ -1093,34 +1047,33 @@ export class Chrome111 extends HandlerInterface // If this is the first DataChannel we need to create the SDP offer with // m=application section. - if (!this._hasDataChannelMediaSection) - { + if (!this._hasDataChannelMediaSection) { this._remoteSdp!.receiveSctpAssociation(); const offer = { type: 'offer', sdp: this._remoteSdp!.getSdp() }; logger.debug( 'receiveDataChannel() | calling pc.setRemoteDescription() [offer:%o]', - offer); + offer, + ); await this._pc.setRemoteDescription(offer); const answer = await this._pc.createAnswer(); - if (!this._transportReady) - { + if (!this._transportReady) { const localSdpObject = sdpTransform.parse(answer.sdp); - await this.setupTransport( - { - localDtlsRole : this._forcedLocalDtlsRole ?? 'client', - localSdpObject - }); + await this.setupTransport({ + localDtlsRole: this._forcedLocalDtlsRole ?? 'client', + localSdpObject, + }); } logger.debug( 'receiveDataChannel() | calling pc.setRemoteDescription() [answer:%o]', - answer); + answer, + ); await this._pc.setLocalDescription(answer); @@ -1130,70 +1083,57 @@ export class Chrome111 extends HandlerInterface return { dataChannel }; } - private async setupTransport( - { - localDtlsRole, - localSdpObject - }: - { - localDtlsRole: DtlsRole; - localSdpObject?: any; - } - ): Promise - { - if (!localSdpObject) - { + private async setupTransport({ + localDtlsRole, + localSdpObject, + }: { + localDtlsRole: DtlsRole; + localSdpObject?: any; + }): Promise { + if (!localSdpObject) { localSdpObject = sdpTransform.parse(this._pc.localDescription.sdp); } // Get our local DTLS parameters. - const dtlsParameters = - sdpCommonUtils.extractDtlsParameters({ sdpObject: localSdpObject }); + const dtlsParameters = sdpCommonUtils.extractDtlsParameters({ + sdpObject: localSdpObject, + }); // Set our DTLS role. dtlsParameters.role = localDtlsRole; // Update the remote DTLS role in the SDP. this._remoteSdp!.updateDtlsRole( - localDtlsRole === 'client' ? 'server' : 'client'); + localDtlsRole === 'client' ? 'server' : 'client', + ); // Need to tell the remote transport about our parameters. - await new Promise((resolve, reject) => - { - this.safeEmit( - '@connect', - { dtlsParameters }, - resolve, - reject - ); + await new Promise((resolve, reject) => { + this.safeEmit('@connect', { dtlsParameters }, resolve, reject); }); this._transportReady = true; } - private assertNotClosed(): void - { - if (this._closed) - { + private assertNotClosed(): void { + if (this._closed) { throw new InvalidStateError('method called in a closed handler'); } } - private assertSendDirection(): void - { - if (this._direction !== 'send') - { + private assertSendDirection(): void { + if (this._direction !== 'send') { throw new Error( - 'method can just be called for handlers with "send" direction'); + 'method can just be called for handlers with "send" direction', + ); } } - private assertRecvDirection(): void - { - if (this._direction !== 'recv') - { + private assertRecvDirection(): void { + if (this._direction !== 'recv') { throw new Error( - 'method can just be called for handlers with "recv" direction'); + 'method can just be called for handlers with "recv" direction', + ); } } } diff --git a/src/handlers/Chrome55.ts b/src/handlers/Chrome55.ts index c440acc2..f96c1380 100644 --- a/src/handlers/Chrome55.ts +++ b/src/handlers/Chrome55.ts @@ -16,7 +16,7 @@ import { HandlerSendDataChannelOptions, HandlerSendDataChannelResult, HandlerReceiveDataChannelOptions, - HandlerReceiveDataChannelResult + HandlerReceiveDataChannelResult, } from './HandlerInterface'; import { RemoteSdp } from './sdp/RemoteSdp'; import { IceParameters, DtlsRole } from '../Transport'; @@ -27,8 +27,7 @@ const logger = new Logger('Chrome55'); const SCTP_NUM_STREAMS = { OS: 1024, MIS: 1024 }; -export class Chrome55 extends HandlerInterface -{ +export class Chrome55 extends HandlerInterface { // Handler direction. private _direction?: 'send' | 'recv'; // Remote SDP handler. @@ -46,19 +45,19 @@ export class Chrome55 extends HandlerInterface // Local stream for sending. private readonly _sendStream = new MediaStream(); // Map of sending MediaStreamTracks indexed by localId. - private readonly _mapSendLocalIdTrack: Map = new Map(); + private readonly _mapSendLocalIdTrack: Map = + new Map(); // Next sending localId. private _nextSendLocalId = 0; // Map of MID, RTP parameters and RTCRtpReceiver indexed by local id. // Value is an Object with mid, rtpParameters and rtpReceiver. - private readonly _mapRecvLocalIdInfo: - Map< - string, - { - mid: string; - rtpParameters: RtpParameters; - } - > = new Map(); + private readonly _mapRecvLocalIdInfo: Map< + string, + { + mid: string; + rtpParameters: RtpParameters; + } + > = new Map(); // Whether a DataChannel m=application section has been created. private _hasDataChannelMediaSection = false; // Sending DataChannel id value counter. Incremented for each new DataChannel. @@ -69,193 +68,173 @@ export class Chrome55 extends HandlerInterface /** * Creates a factory function. */ - static createFactory(): HandlerFactory - { + static createFactory(): HandlerFactory { return (): Chrome55 => new Chrome55(); } - constructor() - { + constructor() { super(); } - get name(): string - { + get name(): string { return 'Chrome55'; } - close(): void - { + close(): void { logger.debug('close()'); // Close RTCPeerConnection. - if (this._pc) - { - try { this._pc.close(); } - catch (error) {} + if (this._pc) { + try { + this._pc.close(); + } catch (error) {} } this.emit('@close'); } - async getNativeRtpCapabilities(): Promise - { + async getNativeRtpCapabilities(): Promise { logger.debug('getNativeRtpCapabilities()'); - const pc = new (RTCPeerConnection as any)( - { - iceServers : [], - iceTransportPolicy : 'all', - bundlePolicy : 'max-bundle', - rtcpMuxPolicy : 'require', - sdpSemantics : 'plan-b' - }); + const pc = new (RTCPeerConnection as any)({ + iceServers: [], + iceTransportPolicy: 'all', + bundlePolicy: 'max-bundle', + rtcpMuxPolicy: 'require', + sdpSemantics: 'plan-b', + }); - try - { - const offer = await pc.createOffer( - { - offerToReceiveAudio : true, - offerToReceiveVideo : true - }); + try { + const offer = await pc.createOffer({ + offerToReceiveAudio: true, + offerToReceiveVideo: true, + }); - try { pc.close(); } - catch (error) {} + try { + pc.close(); + } catch (error) {} const sdpObject = sdpTransform.parse(offer.sdp); - const nativeRtpCapabilities = - sdpCommonUtils.extractRtpCapabilities({ sdpObject }); + const nativeRtpCapabilities = sdpCommonUtils.extractRtpCapabilities({ + sdpObject, + }); return nativeRtpCapabilities; - } - catch (error) - { - try { pc.close(); } - catch (error2) {} + } catch (error) { + try { + pc.close(); + } catch (error2) {} throw error; } } - async getNativeSctpCapabilities(): Promise - { + async getNativeSctpCapabilities(): Promise { logger.debug('getNativeSctpCapabilities()'); return { - numStreams : SCTP_NUM_STREAMS + numStreams: SCTP_NUM_STREAMS, }; } - run( - { - direction, - iceParameters, - iceCandidates, - dtlsParameters, - sctpParameters, - iceServers, - iceTransportPolicy, - additionalSettings, - proprietaryConstraints, - extendedRtpCapabilities - }: HandlerRunOptions - ): void - { + run({ + direction, + iceParameters, + iceCandidates, + dtlsParameters, + sctpParameters, + iceServers, + iceTransportPolicy, + additionalSettings, + proprietaryConstraints, + extendedRtpCapabilities, + }: HandlerRunOptions): void { logger.debug('run()'); this._direction = direction; - this._remoteSdp = new RemoteSdp( - { - iceParameters, - iceCandidates, - dtlsParameters, - sctpParameters, - planB : true - }); + this._remoteSdp = new RemoteSdp({ + iceParameters, + iceCandidates, + dtlsParameters, + sctpParameters, + planB: true, + }); - this._sendingRtpParametersByKind = - { - audio : ortc.getSendingRtpParameters('audio', extendedRtpCapabilities), - video : ortc.getSendingRtpParameters('video', extendedRtpCapabilities) + this._sendingRtpParametersByKind = { + audio: ortc.getSendingRtpParameters('audio', extendedRtpCapabilities), + video: ortc.getSendingRtpParameters('video', extendedRtpCapabilities), }; - this._sendingRemoteRtpParametersByKind = - { - audio : ortc.getSendingRemoteRtpParameters('audio', extendedRtpCapabilities), - video : ortc.getSendingRemoteRtpParameters('video', extendedRtpCapabilities) + this._sendingRemoteRtpParametersByKind = { + audio: ortc.getSendingRemoteRtpParameters( + 'audio', + extendedRtpCapabilities, + ), + video: ortc.getSendingRemoteRtpParameters( + 'video', + extendedRtpCapabilities, + ), }; - if (dtlsParameters.role && dtlsParameters.role !== 'auto') - { - this._forcedLocalDtlsRole = dtlsParameters.role === 'server' - ? 'client' - : 'server'; + if (dtlsParameters.role && dtlsParameters.role !== 'auto') { + this._forcedLocalDtlsRole = + dtlsParameters.role === 'server' ? 'client' : 'server'; } this._pc = new (RTCPeerConnection as any)( { - iceServers : iceServers || [], - iceTransportPolicy : iceTransportPolicy || 'all', - bundlePolicy : 'max-bundle', - rtcpMuxPolicy : 'require', - sdpSemantics : 'plan-b', - ...additionalSettings + iceServers: iceServers || [], + iceTransportPolicy: iceTransportPolicy || 'all', + bundlePolicy: 'max-bundle', + rtcpMuxPolicy: 'require', + sdpSemantics: 'plan-b', + ...additionalSettings, }, - proprietaryConstraints); + proprietaryConstraints, + ); - this._pc.addEventListener('icegatheringstatechange', () => - { + this._pc.addEventListener('icegatheringstatechange', () => { this.emit('@icegatheringstatechange', this._pc.iceGatheringState); }); - if (this._pc.connectionState) - { - this._pc.addEventListener('connectionstatechange', () => - { + if (this._pc.connectionState) { + this._pc.addEventListener('connectionstatechange', () => { this.emit('@connectionstatechange', this._pc.connectionState); }); - } - else - { - this._pc.addEventListener('iceconnectionstatechange', () => - { + } else { + this._pc.addEventListener('iceconnectionstatechange', () => { logger.warn( - 'run() | pc.connectionState not supported, using pc.iceConnectionState'); + 'run() | pc.connectionState not supported, using pc.iceConnectionState', + ); - switch (this._pc.iceConnectionState) - { - case 'checking': - { + switch (this._pc.iceConnectionState) { + case 'checking': { this.emit('@connectionstatechange', 'connecting'); break; } case 'connected': - case 'completed': - { + case 'completed': { this.emit('@connectionstatechange', 'connected'); break; } - case 'failed': - { + case 'failed': { this.emit('@connectionstatechange', 'failed'); break; } - case 'disconnected': - { + case 'disconnected': { this.emit('@connectionstatechange', 'disconnected'); break; } - case 'closed': - { + case 'closed': { this.emit('@connectionstatechange', 'closed'); break; @@ -265,8 +244,7 @@ export class Chrome55 extends HandlerInterface } } - async updateIceServers(iceServers: RTCIceServer[]): Promise - { + async updateIceServers(iceServers: RTCIceServer[]): Promise { logger.debug('updateIceServers()'); const configuration = this._pc.getConfiguration(); @@ -276,25 +254,23 @@ export class Chrome55 extends HandlerInterface this._pc.setConfiguration(configuration); } - async restartIce(iceParameters: IceParameters): Promise - { + async restartIce(iceParameters: IceParameters): Promise { logger.debug('restartIce()'); // Provide the remote SDP handler with new remote ICE parameters. this._remoteSdp!.updateIceParameters(iceParameters); - if (!this._transportReady) - { + if (!this._transportReady) { return; } - if (this._direction === 'send') - { + if (this._direction === 'send') { const offer = await this._pc.createOffer({ iceRestart: true }); logger.debug( 'restartIce() | calling pc.setLocalDescription() [offer:%o]', - offer); + offer, + ); await this._pc.setLocalDescription(offer); @@ -302,17 +278,17 @@ export class Chrome55 extends HandlerInterface logger.debug( 'restartIce() | calling pc.setRemoteDescription() [answer:%o]', - answer); + answer, + ); await this._pc.setRemoteDescription(answer); - } - else - { + } else { const offer = { type: 'offer', sdp: this._remoteSdp!.getSdp() }; logger.debug( 'restartIce() | calling pc.setRemoteDescription() [offer:%o]', - offer); + offer, + ); await this._pc.setRemoteDescription(offer); @@ -320,30 +296,32 @@ export class Chrome55 extends HandlerInterface logger.debug( 'restartIce() | calling pc.setLocalDescription() [answer:%o]', - answer); + answer, + ); await this._pc.setLocalDescription(answer); } } - async getTransportStats(): Promise - { + async getTransportStats(): Promise { return this._pc.getStats(); } - async send( - { track, encodings, codecOptions, codec }: HandlerSendOptions - ): Promise - { + async send({ + track, + encodings, + codecOptions, + codec, + }: HandlerSendOptions): Promise { this.assertSendDirection(); logger.debug('send() [kind:%s, track.id:%s]', track.kind, track.id); - if (codec) - { + if (codec) { logger.warn( 'send() | codec selection is not available in %s handler', - this.name); + this.name, + ); } this._sendStream.addTrack(track); @@ -352,70 +330,70 @@ export class Chrome55 extends HandlerInterface let offer = await this._pc.createOffer(); let localSdpObject = sdpTransform.parse(offer.sdp); let offerMediaObject; - const sendingRtpParameters = - utils.clone(this._sendingRtpParametersByKind![track.kind]); - - sendingRtpParameters.codecs = - ortc.reduceCodecs(sendingRtpParameters.codecs); - - const sendingRemoteRtpParameters = - utils.clone(this._sendingRemoteRtpParametersByKind![track.kind]); - - sendingRemoteRtpParameters.codecs = - ortc.reduceCodecs(sendingRemoteRtpParameters.codecs); - - if (!this._transportReady) - { - await this.setupTransport( - { - localDtlsRole : this._forcedLocalDtlsRole ?? 'client', - localSdpObject - }); + const sendingRtpParameters = utils.clone( + this._sendingRtpParametersByKind![track.kind], + ); + + sendingRtpParameters.codecs = ortc.reduceCodecs( + sendingRtpParameters.codecs, + ); + + const sendingRemoteRtpParameters = utils.clone( + this._sendingRemoteRtpParametersByKind![track.kind], + ); + + sendingRemoteRtpParameters.codecs = ortc.reduceCodecs( + sendingRemoteRtpParameters.codecs, + ); + + if (!this._transportReady) { + await this.setupTransport({ + localDtlsRole: this._forcedLocalDtlsRole ?? 'client', + localSdpObject, + }); } - if (track.kind === 'video' && encodings && encodings.length > 1) - { + if (track.kind === 'video' && encodings && encodings.length > 1) { logger.debug('send() | enabling simulcast'); localSdpObject = sdpTransform.parse(offer.sdp); offerMediaObject = localSdpObject.media.find( - (m: any) => m.type === 'video'); + (m: any) => m.type === 'video', + ); - sdpPlanBUtils.addLegacySimulcast( - { - offerMediaObject, - track, - numStreams : encodings.length - }); + sdpPlanBUtils.addLegacySimulcast({ + offerMediaObject, + track, + numStreams: encodings.length, + }); offer = { type: 'offer', sdp: sdpTransform.write(localSdpObject) }; } - logger.debug( - 'send() | calling pc.setLocalDescription() [offer:%o]', - offer); + logger.debug('send() | calling pc.setLocalDescription() [offer:%o]', offer); await this._pc.setLocalDescription(offer); localSdpObject = sdpTransform.parse(this._pc.localDescription.sdp); - offerMediaObject = localSdpObject.media - .find((m: any) => m.type === track.kind); + offerMediaObject = localSdpObject.media.find( + (m: any) => m.type === track.kind, + ); // Set RTCP CNAME. - sendingRtpParameters.rtcp!.cname = - sdpCommonUtils.getCname({ offerMediaObject }); + sendingRtpParameters.rtcp!.cname = sdpCommonUtils.getCname({ + offerMediaObject, + }); // Set RTP encodings. - sendingRtpParameters.encodings = - sdpPlanBUtils.getRtpEncodings({ offerMediaObject, track }); + sendingRtpParameters.encodings = sdpPlanBUtils.getRtpEncodings({ + offerMediaObject, + track, + }); // Complete encodings with given values. - if (encodings) - { - for (let idx = 0; idx < sendingRtpParameters.encodings.length; ++idx) - { - if (encodings[idx]) - { + if (encodings) { + for (let idx = 0; idx < sendingRtpParameters.encodings.length; ++idx) { + if (encodings[idx]) { Object.assign(sendingRtpParameters.encodings[idx], encodings[idx]); } } @@ -426,27 +404,25 @@ export class Chrome55 extends HandlerInterface if ( sendingRtpParameters.encodings.length > 1 && sendingRtpParameters.codecs[0].mimeType.toLowerCase() === 'video/vp8' - ) - { - for (const encoding of sendingRtpParameters.encodings) - { + ) { + for (const encoding of sendingRtpParameters.encodings) { encoding.scalabilityMode = 'L1T3'; } } - this._remoteSdp!.send( - { - offerMediaObject, - offerRtpParameters : sendingRtpParameters, - answerRtpParameters : sendingRemoteRtpParameters, - codecOptions - }); + this._remoteSdp!.send({ + offerMediaObject, + offerRtpParameters: sendingRtpParameters, + answerRtpParameters: sendingRemoteRtpParameters, + codecOptions, + }); const answer = { type: 'answer', sdp: this._remoteSdp!.getSdp() }; logger.debug( 'send() | calling pc.setRemoteDescription() [answer:%o]', - answer); + answer, + ); await this._pc.setRemoteDescription(answer); @@ -458,21 +434,19 @@ export class Chrome55 extends HandlerInterface this._mapSendLocalIdTrack.set(localId, track); return { - localId : localId, - rtpParameters : sendingRtpParameters + localId: localId, + rtpParameters: sendingRtpParameters, }; } - async stopSending(localId: string): Promise - { + async stopSending(localId: string): Promise { this.assertSendDirection(); logger.debug('stopSending() [localId:%s]', localId); const track = this._mapSendLocalIdTrack.get(localId); - if (!track) - { + if (!track) { throw new Error('track not found'); } @@ -484,21 +458,19 @@ export class Chrome55 extends HandlerInterface logger.debug( 'stopSending() | calling pc.setLocalDescription() [offer:%o]', - offer); + offer, + ); - try - { + try { await this._pc.setLocalDescription(offer); - } - catch (error) - { + } catch (error) { // NOTE: If there are no sending tracks, setLocalDescription() will fail with // "Failed to create channels". If so, ignore it. - if (this._sendStream.getTracks().length === 0) - { + if (this._sendStream.getTracks().length === 0) { logger.warn( 'stopSending() | ignoring expected error due no sending tracks: %s', - (error as Error).toString()); + (error as Error).toString(), + ); return; } @@ -506,8 +478,7 @@ export class Chrome55 extends HandlerInterface throw error; } - if (this._pc.signalingState === 'stable') - { + if (this._pc.signalingState === 'stable') { return; } @@ -515,70 +486,67 @@ export class Chrome55 extends HandlerInterface logger.debug( 'stopSending() | calling pc.setRemoteDescription() [answer:%o]', - answer); + answer, + ); await this._pc.setRemoteDescription(answer); } // eslint-disable-next-line @typescript-eslint/no-unused-vars - async pauseSending(localId: string): Promise - { + async pauseSending(localId: string): Promise { // Unimplemented. } // eslint-disable-next-line @typescript-eslint/no-unused-vars - async resumeSending(localId: string): Promise - { + async resumeSending(localId: string): Promise { // Unimplemented. } async replaceTrack( // eslint-disable-next-line @typescript-eslint/no-unused-vars - localId: string, track: MediaStreamTrack | null - ): Promise - { + localId: string, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + track: MediaStreamTrack | null, + ): Promise { throw new UnsupportedError('not implemented'); } - // eslint-disable-next-line @typescript-eslint/no-unused-vars - async setMaxSpatialLayer(localId: string, spatialLayer: number): Promise - { + async setMaxSpatialLayer( + // eslint-disable-next-line @typescript-eslint/no-unused-vars + localId: string, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + spatialLayer: number, + ): Promise { throw new UnsupportedError(' not implemented'); } // eslint-disable-next-line @typescript-eslint/no-unused-vars - async setRtpEncodingParameters(localId: string, params: any): Promise - { + async setRtpEncodingParameters(localId: string, params: any): Promise { throw new UnsupportedError('not supported'); } // eslint-disable-next-line @typescript-eslint/no-unused-vars - async getSenderStats(localId: string): Promise - { + async getSenderStats(localId: string): Promise { throw new UnsupportedError('not implemented'); } - async sendDataChannel( - { - ordered, - maxPacketLifeTime, - maxRetransmits, - label, - protocol - }: HandlerSendDataChannelOptions - ): Promise - { + async sendDataChannel({ + ordered, + maxPacketLifeTime, + maxRetransmits, + label, + protocol, + }: HandlerSendDataChannelOptions): Promise { this.assertSendDirection(); - const options = - { - negotiated : true, - id : this._nextSendSctpStreamId, + const options = { + negotiated: true, + id: this._nextSendSctpStreamId, ordered, maxPacketLifeTime, - maxRetransmitTime : maxPacketLifeTime, // NOTE: Old spec. + maxRetransmitTime: maxPacketLifeTime, // NOTE: Old spec. maxRetransmits, - protocol + protocol, }; logger.debug('sendDataChannel() [options:%o]', options); @@ -591,25 +559,24 @@ export class Chrome55 extends HandlerInterface // If this is the first DataChannel we need to create the SDP answer with // m=application section. - if (!this._hasDataChannelMediaSection) - { + if (!this._hasDataChannelMediaSection) { const offer = await this._pc.createOffer(); const localSdpObject = sdpTransform.parse(offer.sdp); - const offerMediaObject = localSdpObject.media - .find((m: any) => m.type === 'application'); + const offerMediaObject = localSdpObject.media.find( + (m: any) => m.type === 'application', + ); - if (!this._transportReady) - { - await this.setupTransport( - { - localDtlsRole : this._forcedLocalDtlsRole ?? 'client', - localSdpObject - }); + if (!this._transportReady) { + await this.setupTransport({ + localDtlsRole: this._forcedLocalDtlsRole ?? 'client', + localSdpObject, + }); } logger.debug( 'sendDataChannel() | calling pc.setLocalDescription() [offer:%o]', - offer); + offer, + ); await this._pc.setLocalDescription(offer); @@ -619,106 +586,101 @@ export class Chrome55 extends HandlerInterface logger.debug( 'sendDataChannel() | calling pc.setRemoteDescription() [answer:%o]', - answer); + answer, + ); await this._pc.setRemoteDescription(answer); this._hasDataChannelMediaSection = true; } - const sctpStreamParameters: SctpStreamParameters = - { - streamId : options.id, - ordered : options.ordered, - maxPacketLifeTime : options.maxPacketLifeTime, - maxRetransmits : options.maxRetransmits + const sctpStreamParameters: SctpStreamParameters = { + streamId: options.id, + ordered: options.ordered, + maxPacketLifeTime: options.maxPacketLifeTime, + maxRetransmits: options.maxRetransmits, }; return { dataChannel, sctpStreamParameters }; } async receive( - optionsList: HandlerReceiveOptions[] - ) : Promise - { + optionsList: HandlerReceiveOptions[], + ): Promise { this.assertRecvDirection(); const results: HandlerReceiveResult[] = []; - for (const options of optionsList) - { + for (const options of optionsList) { const { trackId, kind, rtpParameters, streamId } = options; logger.debug('receive() [trackId:%s, kind:%s]', trackId, kind); const mid = kind; - this._remoteSdp!.receive( - { - mid, - kind, - offerRtpParameters : rtpParameters, - streamId : streamId || rtpParameters.rtcp!.cname!, - trackId - }); + this._remoteSdp!.receive({ + mid, + kind, + offerRtpParameters: rtpParameters, + streamId: streamId || rtpParameters.rtcp!.cname!, + trackId, + }); } const offer = { type: 'offer', sdp: this._remoteSdp!.getSdp() }; logger.debug( 'receive() | calling pc.setRemoteDescription() [offer:%o]', - offer); + offer, + ); await this._pc.setRemoteDescription(offer); let answer = await this._pc.createAnswer(); const localSdpObject = sdpTransform.parse(answer.sdp); - for (const options of optionsList) - { + for (const options of optionsList) { const { kind, rtpParameters } = options; const mid = kind; - const answerMediaObject = localSdpObject.media - .find((m: any) => String(m.mid) === mid); + const answerMediaObject = localSdpObject.media.find( + (m: any) => String(m.mid) === mid, + ); // May need to modify codec parameters in the answer based on codec // parameters in the offer. - sdpCommonUtils.applyCodecParameters( - { - offerRtpParameters : rtpParameters, - answerMediaObject - }); + sdpCommonUtils.applyCodecParameters({ + offerRtpParameters: rtpParameters, + answerMediaObject, + }); } answer = { type: 'answer', sdp: sdpTransform.write(localSdpObject) }; - if (!this._transportReady) - { - await this.setupTransport( - { - localDtlsRole : this._forcedLocalDtlsRole ?? 'client', - localSdpObject - }); + if (!this._transportReady) { + await this.setupTransport({ + localDtlsRole: this._forcedLocalDtlsRole ?? 'client', + localSdpObject, + }); } logger.debug( 'receive() | calling pc.setLocalDescription() [answer:%o]', - answer); + answer, + ); await this._pc.setLocalDescription(answer); - for (const options of optionsList) - { + for (const options of optionsList) { const { kind, trackId, rtpParameters } = options; const mid = kind; const localId = trackId; const streamId = options.streamId || rtpParameters.rtcp!.cname!; - const stream = this._pc.getRemoteStreams() + const stream = this._pc + .getRemoteStreams() .find((s: any) => s.id === streamId); const track = stream.getTrackById(localId); - if (!track) - { + if (!track) { throw new Error('remote track not found'); } @@ -731,28 +693,30 @@ export class Chrome55 extends HandlerInterface return results; } - async stopReceiving(localIds: string[]): Promise - { + async stopReceiving(localIds: string[]): Promise { this.assertRecvDirection(); - for (const localId of localIds) - { + for (const localId of localIds) { logger.debug('stopReceiving() [localId:%s]', localId); - const { mid, rtpParameters } = this._mapRecvLocalIdInfo.get(localId) || {}; + const { mid, rtpParameters } = + this._mapRecvLocalIdInfo.get(localId) || {}; // Remove from the map. this._mapRecvLocalIdInfo.delete(localId); - this._remoteSdp!.planBStopReceiving( - { mid: mid!, offerRtpParameters: rtpParameters! }); + this._remoteSdp!.planBStopReceiving({ + mid: mid!, + offerRtpParameters: rtpParameters!, + }); } const offer = { type: 'offer', sdp: this._remoteSdp!.getSdp() }; logger.debug( 'stopReceiving() | calling pc.setRemoteDescription() [offer:%o]', - offer); + offer, + ); await this._pc.setRemoteDescription(offer); @@ -760,53 +724,49 @@ export class Chrome55 extends HandlerInterface logger.debug( 'stopReceiving() | calling pc.setLocalDescription() [answer:%o]', - answer); + answer, + ); await this._pc.setLocalDescription(answer); } async pauseReceiving( // eslint-disable-next-line @typescript-eslint/no-unused-vars - localIds: string[]): Promise - { + localIds: string[], + ): Promise { // Unimplemented. } async resumeReceiving( // eslint-disable-next-line @typescript-eslint/no-unused-vars - localIds: string[]): Promise - { + localIds: string[], + ): Promise { // Unimplemented. } // eslint-disable-next-line @typescript-eslint/no-unused-vars - async getReceiverStats(localId: string): Promise - { + async getReceiverStats(localId: string): Promise { throw new UnsupportedError('not implemented'); } - async receiveDataChannel( - { sctpStreamParameters, label, protocol }: HandlerReceiveDataChannelOptions - ): Promise - { + async receiveDataChannel({ + sctpStreamParameters, + label, + protocol, + }: HandlerReceiveDataChannelOptions): Promise { this.assertRecvDirection(); - const { - streamId, - ordered, - maxPacketLifeTime, - maxRetransmits - } = sctpStreamParameters; + const { streamId, ordered, maxPacketLifeTime, maxRetransmits } = + sctpStreamParameters; - const options = - { - negotiated : true, - id : streamId, + const options = { + negotiated: true, + id: streamId, ordered, maxPacketLifeTime, - maxRetransmitTime : maxPacketLifeTime, // NOTE: Old spec. + maxRetransmitTime: maxPacketLifeTime, // NOTE: Old spec. maxRetransmits, - protocol + protocol, }; logger.debug('receiveDataChannel() [options:%o]', options); @@ -815,34 +775,33 @@ export class Chrome55 extends HandlerInterface // If this is the first DataChannel we need to create the SDP offer with // m=application section. - if (!this._hasDataChannelMediaSection) - { + if (!this._hasDataChannelMediaSection) { this._remoteSdp!.receiveSctpAssociation({ oldDataChannelSpec: true }); const offer = { type: 'offer', sdp: this._remoteSdp!.getSdp() }; logger.debug( 'receiveDataChannel() | calling pc.setRemoteDescription() [offer:%o]', - offer); + offer, + ); await this._pc.setRemoteDescription(offer); const answer = await this._pc.createAnswer(); - if (!this._transportReady) - { + if (!this._transportReady) { const localSdpObject = sdpTransform.parse(answer.sdp); - await this.setupTransport( - { - localDtlsRole : this._forcedLocalDtlsRole ?? 'client', - localSdpObject - }); + await this.setupTransport({ + localDtlsRole: this._forcedLocalDtlsRole ?? 'client', + localSdpObject, + }); } logger.debug( 'receiveDataChannel() | calling pc.setRemoteDescription() [answer:%o]', - answer); + answer, + ); await this._pc.setLocalDescription(answer); @@ -852,62 +811,51 @@ export class Chrome55 extends HandlerInterface return { dataChannel }; } - private async setupTransport( - { - localDtlsRole, - localSdpObject - }: - { - localDtlsRole: DtlsRole; - localSdpObject?: any; - } - ): Promise - { - if (!localSdpObject) - { + private async setupTransport({ + localDtlsRole, + localSdpObject, + }: { + localDtlsRole: DtlsRole; + localSdpObject?: any; + }): Promise { + if (!localSdpObject) { localSdpObject = sdpTransform.parse(this._pc.localDescription.sdp); } // Get our local DTLS parameters. - const dtlsParameters = - sdpCommonUtils.extractDtlsParameters({ sdpObject: localSdpObject }); + const dtlsParameters = sdpCommonUtils.extractDtlsParameters({ + sdpObject: localSdpObject, + }); // Set our DTLS role. dtlsParameters.role = localDtlsRole; // Update the remote DTLS role in the SDP. this._remoteSdp!.updateDtlsRole( - localDtlsRole === 'client' ? 'server' : 'client'); + localDtlsRole === 'client' ? 'server' : 'client', + ); // Need to tell the remote transport about our parameters. - await new Promise((resolve, reject) => - { - this.safeEmit( - '@connect', - { dtlsParameters }, - resolve, - reject - ); + await new Promise((resolve, reject) => { + this.safeEmit('@connect', { dtlsParameters }, resolve, reject); }); this._transportReady = true; } - private assertSendDirection(): void - { - if (this._direction !== 'send') - { + private assertSendDirection(): void { + if (this._direction !== 'send') { throw new Error( - 'method can just be called for handlers with "send" direction'); + 'method can just be called for handlers with "send" direction', + ); } } - private assertRecvDirection(): void - { - if (this._direction !== 'recv') - { + private assertRecvDirection(): void { + if (this._direction !== 'recv') { throw new Error( - 'method can just be called for handlers with "recv" direction'); + 'method can just be called for handlers with "recv" direction', + ); } } } diff --git a/src/handlers/Chrome67.ts b/src/handlers/Chrome67.ts index d9abe470..c71877ac 100644 --- a/src/handlers/Chrome67.ts +++ b/src/handlers/Chrome67.ts @@ -15,7 +15,7 @@ import { HandlerSendDataChannelOptions, HandlerSendDataChannelResult, HandlerReceiveDataChannelOptions, - HandlerReceiveDataChannelResult + HandlerReceiveDataChannelResult, } from './HandlerInterface'; import { RemoteSdp } from './sdp/RemoteSdp'; import { IceParameters, DtlsRole } from '../Transport'; @@ -26,8 +26,7 @@ const logger = new Logger('Chrome67'); const SCTP_NUM_STREAMS = { OS: 1024, MIS: 1024 }; -export class Chrome67 extends HandlerInterface -{ +export class Chrome67 extends HandlerInterface { // Handler direction. private _direction?: 'send' | 'recv'; // Remote SDP handler. @@ -51,15 +50,14 @@ export class Chrome67 extends HandlerInterface private _nextSendLocalId = 0; // Map of MID, RTP parameters and RTCRtpReceiver indexed by local id. // Value is an Object with mid, rtpParameters and rtpReceiver. - private readonly _mapRecvLocalIdInfo: - Map< - string, - { - mid: string; - rtpParameters: RtpParameters; - rtpReceiver: RTCRtpReceiver; - } - > = new Map(); + private readonly _mapRecvLocalIdInfo: Map< + string, + { + mid: string; + rtpParameters: RtpParameters; + rtpReceiver: RTCRtpReceiver; + } + > = new Map(); // Whether a DataChannel m=application section has been created. private _hasDataChannelMediaSection = false; // Sending DataChannel id value counter. Incremented for each new DataChannel. @@ -70,193 +68,173 @@ export class Chrome67 extends HandlerInterface /** * Creates a factory function. */ - static createFactory(): HandlerFactory - { + static createFactory(): HandlerFactory { return (): Chrome67 => new Chrome67(); } - constructor() - { + constructor() { super(); } - get name(): string - { + get name(): string { return 'Chrome67'; } - close(): void - { + close(): void { logger.debug('close()'); // Close RTCPeerConnection. - if (this._pc) - { - try { this._pc.close(); } - catch (error) {} + if (this._pc) { + try { + this._pc.close(); + } catch (error) {} } this.emit('@close'); } - async getNativeRtpCapabilities(): Promise - { + async getNativeRtpCapabilities(): Promise { logger.debug('getNativeRtpCapabilities()'); - const pc = new (RTCPeerConnection as any)( - { - iceServers : [], - iceTransportPolicy : 'all', - bundlePolicy : 'max-bundle', - rtcpMuxPolicy : 'require', - sdpSemantics : 'plan-b' - }); + const pc = new (RTCPeerConnection as any)({ + iceServers: [], + iceTransportPolicy: 'all', + bundlePolicy: 'max-bundle', + rtcpMuxPolicy: 'require', + sdpSemantics: 'plan-b', + }); - try - { - const offer = await pc.createOffer( - { - offerToReceiveAudio : true, - offerToReceiveVideo : true - }); + try { + const offer = await pc.createOffer({ + offerToReceiveAudio: true, + offerToReceiveVideo: true, + }); - try { pc.close(); } - catch (error) {} + try { + pc.close(); + } catch (error) {} const sdpObject = sdpTransform.parse(offer.sdp); - const nativeRtpCapabilities = - sdpCommonUtils.extractRtpCapabilities({ sdpObject }); + const nativeRtpCapabilities = sdpCommonUtils.extractRtpCapabilities({ + sdpObject, + }); return nativeRtpCapabilities; - } - catch (error) - { - try { pc.close(); } - catch (error2) {} + } catch (error) { + try { + pc.close(); + } catch (error2) {} throw error; } } - async getNativeSctpCapabilities(): Promise - { + async getNativeSctpCapabilities(): Promise { logger.debug('getNativeSctpCapabilities()'); return { - numStreams : SCTP_NUM_STREAMS + numStreams: SCTP_NUM_STREAMS, }; } - run( - { - direction, - iceParameters, - iceCandidates, - dtlsParameters, - sctpParameters, - iceServers, - iceTransportPolicy, - additionalSettings, - proprietaryConstraints, - extendedRtpCapabilities - }: HandlerRunOptions - ): void - { + run({ + direction, + iceParameters, + iceCandidates, + dtlsParameters, + sctpParameters, + iceServers, + iceTransportPolicy, + additionalSettings, + proprietaryConstraints, + extendedRtpCapabilities, + }: HandlerRunOptions): void { logger.debug('run()'); this._direction = direction; - this._remoteSdp = new RemoteSdp( - { - iceParameters, - iceCandidates, - dtlsParameters, - sctpParameters, - planB : true - }); + this._remoteSdp = new RemoteSdp({ + iceParameters, + iceCandidates, + dtlsParameters, + sctpParameters, + planB: true, + }); - this._sendingRtpParametersByKind = - { - audio : ortc.getSendingRtpParameters('audio', extendedRtpCapabilities), - video : ortc.getSendingRtpParameters('video', extendedRtpCapabilities) + this._sendingRtpParametersByKind = { + audio: ortc.getSendingRtpParameters('audio', extendedRtpCapabilities), + video: ortc.getSendingRtpParameters('video', extendedRtpCapabilities), }; - this._sendingRemoteRtpParametersByKind = - { - audio : ortc.getSendingRemoteRtpParameters('audio', extendedRtpCapabilities), - video : ortc.getSendingRemoteRtpParameters('video', extendedRtpCapabilities) + this._sendingRemoteRtpParametersByKind = { + audio: ortc.getSendingRemoteRtpParameters( + 'audio', + extendedRtpCapabilities, + ), + video: ortc.getSendingRemoteRtpParameters( + 'video', + extendedRtpCapabilities, + ), }; - if (dtlsParameters.role && dtlsParameters.role !== 'auto') - { - this._forcedLocalDtlsRole = dtlsParameters.role === 'server' - ? 'client' - : 'server'; + if (dtlsParameters.role && dtlsParameters.role !== 'auto') { + this._forcedLocalDtlsRole = + dtlsParameters.role === 'server' ? 'client' : 'server'; } this._pc = new (RTCPeerConnection as any)( { - iceServers : iceServers || [], - iceTransportPolicy : iceTransportPolicy || 'all', - bundlePolicy : 'max-bundle', - rtcpMuxPolicy : 'require', - sdpSemantics : 'plan-b', - ...additionalSettings + iceServers: iceServers || [], + iceTransportPolicy: iceTransportPolicy || 'all', + bundlePolicy: 'max-bundle', + rtcpMuxPolicy: 'require', + sdpSemantics: 'plan-b', + ...additionalSettings, }, - proprietaryConstraints); + proprietaryConstraints, + ); - this._pc.addEventListener('icegatheringstatechange', () => - { + this._pc.addEventListener('icegatheringstatechange', () => { this.emit('@icegatheringstatechange', this._pc.iceGatheringState); }); - if (this._pc.connectionState) - { - this._pc.addEventListener('connectionstatechange', () => - { + if (this._pc.connectionState) { + this._pc.addEventListener('connectionstatechange', () => { this.emit('@connectionstatechange', this._pc.connectionState); }); - } - else - { - this._pc.addEventListener('iceconnectionstatechange', () => - { + } else { + this._pc.addEventListener('iceconnectionstatechange', () => { logger.warn( - 'run() | pc.connectionState not supported, using pc.iceConnectionState'); + 'run() | pc.connectionState not supported, using pc.iceConnectionState', + ); - switch (this._pc.iceConnectionState) - { - case 'checking': - { + switch (this._pc.iceConnectionState) { + case 'checking': { this.emit('@connectionstatechange', 'connecting'); break; } case 'connected': - case 'completed': - { + case 'completed': { this.emit('@connectionstatechange', 'connected'); break; } - case 'failed': - { + case 'failed': { this.emit('@connectionstatechange', 'failed'); break; } - case 'disconnected': - { + case 'disconnected': { this.emit('@connectionstatechange', 'disconnected'); break; } - case 'closed': - { + case 'closed': { this.emit('@connectionstatechange', 'closed'); break; @@ -266,8 +244,7 @@ export class Chrome67 extends HandlerInterface } } - async updateIceServers(iceServers: RTCIceServer[]): Promise - { + async updateIceServers(iceServers: RTCIceServer[]): Promise { logger.debug('updateIceServers()'); const configuration = this._pc.getConfiguration(); @@ -277,25 +254,23 @@ export class Chrome67 extends HandlerInterface this._pc.setConfiguration(configuration); } - async restartIce(iceParameters: IceParameters): Promise - { + async restartIce(iceParameters: IceParameters): Promise { logger.debug('restartIce()'); // Provide the remote SDP handler with new remote ICE parameters. this._remoteSdp!.updateIceParameters(iceParameters); - if (!this._transportReady) - { + if (!this._transportReady) { return; } - if (this._direction === 'send') - { + if (this._direction === 'send') { const offer = await this._pc.createOffer({ iceRestart: true }); logger.debug( 'restartIce() | calling pc.setLocalDescription() [offer:%o]', - offer); + offer, + ); await this._pc.setLocalDescription(offer); @@ -303,17 +278,17 @@ export class Chrome67 extends HandlerInterface logger.debug( 'restartIce() | calling pc.setRemoteDescription() [answer:%o]', - answer); + answer, + ); await this._pc.setRemoteDescription(answer); - } - else - { + } else { const offer = { type: 'offer', sdp: this._remoteSdp!.getSdp() }; logger.debug( 'restartIce() | calling pc.setRemoteDescription() [offer:%o]', - offer); + offer, + ); await this._pc.setRemoteDescription(offer); @@ -321,30 +296,32 @@ export class Chrome67 extends HandlerInterface logger.debug( 'restartIce() | calling pc.setLocalDescription() [answer:%o]', - answer); + answer, + ); await this._pc.setLocalDescription(answer); } } - async getTransportStats(): Promise - { + async getTransportStats(): Promise { return this._pc.getStats(); } - async send( - { track, encodings, codecOptions, codec }: HandlerSendOptions - ): Promise - { + async send({ + track, + encodings, + codecOptions, + codec, + }: HandlerSendOptions): Promise { this.assertSendDirection(); logger.debug('send() [kind:%s, track.id:%s]', track.kind, track.id); - if (codec) - { + if (codec) { logger.warn( 'send() | codec selection is not available in %s handler', - this.name); + this.name, + ); } this._sendStream.addTrack(track); @@ -353,70 +330,70 @@ export class Chrome67 extends HandlerInterface let offer = await this._pc.createOffer(); let localSdpObject = sdpTransform.parse(offer.sdp); let offerMediaObject; - const sendingRtpParameters = - utils.clone(this._sendingRtpParametersByKind![track.kind]); - - sendingRtpParameters.codecs = - ortc.reduceCodecs(sendingRtpParameters.codecs); - - const sendingRemoteRtpParameters = - utils.clone(this._sendingRemoteRtpParametersByKind![track.kind]); - - sendingRemoteRtpParameters.codecs = - ortc.reduceCodecs(sendingRemoteRtpParameters.codecs); - - if (!this._transportReady) - { - await this.setupTransport( - { - localDtlsRole : this._forcedLocalDtlsRole ?? 'client', - localSdpObject - }); + const sendingRtpParameters = utils.clone( + this._sendingRtpParametersByKind![track.kind], + ); + + sendingRtpParameters.codecs = ortc.reduceCodecs( + sendingRtpParameters.codecs, + ); + + const sendingRemoteRtpParameters = utils.clone( + this._sendingRemoteRtpParametersByKind![track.kind], + ); + + sendingRemoteRtpParameters.codecs = ortc.reduceCodecs( + sendingRemoteRtpParameters.codecs, + ); + + if (!this._transportReady) { + await this.setupTransport({ + localDtlsRole: this._forcedLocalDtlsRole ?? 'client', + localSdpObject, + }); } - if (track.kind === 'video' && encodings && encodings.length > 1) - { + if (track.kind === 'video' && encodings && encodings.length > 1) { logger.debug('send() | enabling simulcast'); localSdpObject = sdpTransform.parse(offer.sdp); - offerMediaObject = localSdpObject.media - .find((m: any) => m.type === 'video'); - - sdpPlanBUtils.addLegacySimulcast( - { - offerMediaObject, - track, - numStreams : encodings.length - }); + offerMediaObject = localSdpObject.media.find( + (m: any) => m.type === 'video', + ); + + sdpPlanBUtils.addLegacySimulcast({ + offerMediaObject, + track, + numStreams: encodings.length, + }); offer = { type: 'offer', sdp: sdpTransform.write(localSdpObject) }; } - logger.debug( - 'send() | calling pc.setLocalDescription() [offer:%o]', - offer); + logger.debug('send() | calling pc.setLocalDescription() [offer:%o]', offer); await this._pc.setLocalDescription(offer); localSdpObject = sdpTransform.parse(this._pc.localDescription.sdp); - offerMediaObject = localSdpObject.media - .find((m: any) => m.type === track.kind); + offerMediaObject = localSdpObject.media.find( + (m: any) => m.type === track.kind, + ); // Set RTCP CNAME. - sendingRtpParameters.rtcp!.cname = - sdpCommonUtils.getCname({ offerMediaObject }); + sendingRtpParameters.rtcp!.cname = sdpCommonUtils.getCname({ + offerMediaObject, + }); // Set RTP encodings. - sendingRtpParameters.encodings = - sdpPlanBUtils.getRtpEncodings({ offerMediaObject, track }); + sendingRtpParameters.encodings = sdpPlanBUtils.getRtpEncodings({ + offerMediaObject, + track, + }); // Complete encodings with given values. - if (encodings) - { - for (let idx = 0; idx < sendingRtpParameters.encodings.length; ++idx) - { - if (encodings[idx]) - { + if (encodings) { + for (let idx = 0; idx < sendingRtpParameters.encodings.length; ++idx) { + if (encodings[idx]) { Object.assign(sendingRtpParameters.encodings[idx], encodings[idx]); } } @@ -427,27 +404,25 @@ export class Chrome67 extends HandlerInterface if ( sendingRtpParameters.encodings.length > 1 && sendingRtpParameters.codecs[0].mimeType.toLowerCase() === 'video/vp8' - ) - { - for (const encoding of sendingRtpParameters.encodings) - { + ) { + for (const encoding of sendingRtpParameters.encodings) { encoding.scalabilityMode = 'L1T3'; } } - this._remoteSdp!.send( - { - offerMediaObject, - offerRtpParameters : sendingRtpParameters, - answerRtpParameters : sendingRemoteRtpParameters, - codecOptions - }); + this._remoteSdp!.send({ + offerMediaObject, + offerRtpParameters: sendingRtpParameters, + answerRtpParameters: sendingRemoteRtpParameters, + codecOptions, + }); const answer = { type: 'answer', sdp: this._remoteSdp!.getSdp() }; logger.debug( 'send() | calling pc.setRemoteDescription() [answer:%o]', - answer); + answer, + ); await this._pc.setRemoteDescription(answer); @@ -455,36 +430,34 @@ export class Chrome67 extends HandlerInterface this._nextSendLocalId++; - const rtpSender = this._pc.getSenders() + const rtpSender = this._pc + .getSenders() .find((s: RTCRtpSender) => s.track === track); // Insert into the map. this._mapSendLocalIdRtpSender.set(localId, rtpSender); return { - localId : localId, - rtpParameters : sendingRtpParameters, - rtpSender + localId: localId, + rtpParameters: sendingRtpParameters, + rtpSender, }; } - async stopSending(localId: string): Promise - { + async stopSending(localId: string): Promise { this.assertSendDirection(); logger.debug('stopSending() [localId:%s]', localId); const rtpSender = this._mapSendLocalIdRtpSender.get(localId); - if (!rtpSender) - { + if (!rtpSender) { throw new Error('associated RTCRtpSender not found'); } this._pc.removeTrack(rtpSender); - if (rtpSender.track) - { + if (rtpSender.track) { this._sendStream.removeTrack(rtpSender.track); } @@ -494,21 +467,19 @@ export class Chrome67 extends HandlerInterface logger.debug( 'stopSending() | calling pc.setLocalDescription() [offer:%o]', - offer); + offer, + ); - try - { + try { await this._pc.setLocalDescription(offer); - } - catch (error) - { + } catch (error) { // NOTE: If there are no sending tracks, setLocalDescription() will fail with // "Failed to create channels". If so, ignore it. - if (this._sendStream.getTracks().length === 0) - { + if (this._sendStream.getTracks().length === 0) { logger.warn( 'stopSending() | ignoring expected error due no sending tracks: %s', - (error as Error).toString()); + (error as Error).toString(), + ); return; } @@ -516,8 +487,7 @@ export class Chrome67 extends HandlerInterface throw error; } - if (this._pc.signalingState === 'stable') - { + if (this._pc.signalingState === 'stable') { return; } @@ -525,43 +495,41 @@ export class Chrome67 extends HandlerInterface logger.debug( 'stopSending() | calling pc.setRemoteDescription() [answer:%o]', - answer); + answer, + ); await this._pc.setRemoteDescription(answer); } // eslint-disable-next-line @typescript-eslint/no-unused-vars - async pauseSending(localId: string): Promise - { + async pauseSending(localId: string): Promise { // Unimplemented. } // eslint-disable-next-line @typescript-eslint/no-unused-vars - async resumeSending(localId: string): Promise - { + async resumeSending(localId: string): Promise { // Unimplemented. } async replaceTrack( - localId: string, track: MediaStreamTrack | null - ): Promise - { + localId: string, + track: MediaStreamTrack | null, + ): Promise { this.assertSendDirection(); - if (track) - { + if (track) { logger.debug( - 'replaceTrack() [localId:%s, track.id:%s]', localId, track.id); - } - else - { + 'replaceTrack() [localId:%s, track.id:%s]', + localId, + track.id, + ); + } else { logger.debug('replaceTrack() [localId:%s, no track]', localId); } const rtpSender = this._mapSendLocalIdRtpSender.get(localId); - if (!rtpSender) - { + if (!rtpSender) { throw new Error('associated RTCRtpSender not found'); } @@ -570,110 +538,104 @@ export class Chrome67 extends HandlerInterface await rtpSender.replaceTrack(track); // Remove the old track from the local stream. - if (oldTrack) - { + if (oldTrack) { this._sendStream.removeTrack(oldTrack); } // Add the new track to the local stream. - if (track) - { + if (track) { this._sendStream.addTrack(track); } } - async setMaxSpatialLayer(localId: string, spatialLayer: number): Promise - { + async setMaxSpatialLayer( + localId: string, + spatialLayer: number, + ): Promise { this.assertSendDirection(); logger.debug( 'setMaxSpatialLayer() [localId:%s, spatialLayer:%s]', - localId, spatialLayer); + localId, + spatialLayer, + ); const rtpSender = this._mapSendLocalIdRtpSender.get(localId); - if (!rtpSender) - { + if (!rtpSender) { throw new Error('associated RTCRtpSender not found'); } const parameters = rtpSender.getParameters(); - parameters.encodings.forEach((encoding: RTCRtpEncodingParameters, idx: number) => - { - if (idx <= spatialLayer) - { - encoding.active = true; - } - else - { - encoding.active = false; - } - }); + parameters.encodings.forEach( + (encoding: RTCRtpEncodingParameters, idx: number) => { + if (idx <= spatialLayer) { + encoding.active = true; + } else { + encoding.active = false; + } + }, + ); await rtpSender.setParameters(parameters); } - async setRtpEncodingParameters(localId: string, params: any): Promise - { + async setRtpEncodingParameters(localId: string, params: any): Promise { this.assertSendDirection(); logger.debug( 'setRtpEncodingParameters() [localId:%s, params:%o]', - localId, params); + localId, + params, + ); const rtpSender = this._mapSendLocalIdRtpSender.get(localId); - if (!rtpSender) - { + if (!rtpSender) { throw new Error('associated RTCRtpSender not found'); } const parameters = rtpSender.getParameters(); - parameters.encodings.forEach((encoding: RTCRtpEncodingParameters, idx: number) => - { - parameters.encodings[idx] = { ...encoding, ...params }; - }); + parameters.encodings.forEach( + (encoding: RTCRtpEncodingParameters, idx: number) => { + parameters.encodings[idx] = { ...encoding, ...params }; + }, + ); await rtpSender.setParameters(parameters); } - async getSenderStats(localId: string): Promise - { + async getSenderStats(localId: string): Promise { this.assertSendDirection(); const rtpSender = this._mapSendLocalIdRtpSender.get(localId); - if (!rtpSender) - { + if (!rtpSender) { throw new Error('associated RTCRtpSender not found'); } return rtpSender.getStats(); } - async sendDataChannel( - { - ordered, - maxPacketLifeTime, - maxRetransmits, - label, - protocol - }: HandlerSendDataChannelOptions - ): Promise - { + async sendDataChannel({ + ordered, + maxPacketLifeTime, + maxRetransmits, + label, + protocol, + }: HandlerSendDataChannelOptions): Promise { this.assertSendDirection(); - const options = - { - negotiated : true, - id : this._nextSendSctpStreamId, + const options = { + negotiated: true, + id: this._nextSendSctpStreamId, ordered, maxPacketLifeTime, - maxRetransmitTime : maxPacketLifeTime, // NOTE: Old spec. + maxRetransmitTime: maxPacketLifeTime, // NOTE: Old spec. maxRetransmits, - protocol + protocol, }; logger.debug('sendDataChannel() [options:%o]', options); @@ -686,25 +648,24 @@ export class Chrome67 extends HandlerInterface // If this is the first DataChannel we need to create the SDP answer with // m=application section. - if (!this._hasDataChannelMediaSection) - { + if (!this._hasDataChannelMediaSection) { const offer = await this._pc.createOffer(); const localSdpObject = sdpTransform.parse(offer.sdp); - const offerMediaObject = localSdpObject.media - .find((m: any) => m.type === 'application'); + const offerMediaObject = localSdpObject.media.find( + (m: any) => m.type === 'application', + ); - if (!this._transportReady) - { - await this.setupTransport( - { - localDtlsRole : this._forcedLocalDtlsRole ?? 'client', - localSdpObject - }); + if (!this._transportReady) { + await this.setupTransport({ + localDtlsRole: this._forcedLocalDtlsRole ?? 'client', + localSdpObject, + }); } logger.debug( 'sendDataChannel() | calling pc.setLocalDescription() [offer:%o]', - offer); + offer, + ); await this._pc.setLocalDescription(offer); @@ -714,142 +675,143 @@ export class Chrome67 extends HandlerInterface logger.debug( 'sendDataChannel() | calling pc.setRemoteDescription() [answer:%o]', - answer); + answer, + ); await this._pc.setRemoteDescription(answer); this._hasDataChannelMediaSection = true; } - const sctpStreamParameters: SctpStreamParameters = - { - streamId : options.id, - ordered : options.ordered, - maxPacketLifeTime : options.maxPacketLifeTime, - maxRetransmits : options.maxRetransmits + const sctpStreamParameters: SctpStreamParameters = { + streamId: options.id, + ordered: options.ordered, + maxPacketLifeTime: options.maxPacketLifeTime, + maxRetransmits: options.maxRetransmits, }; return { dataChannel, sctpStreamParameters }; } async receive( - optionsList: HandlerReceiveOptions[] - ) : Promise - { + optionsList: HandlerReceiveOptions[], + ): Promise { this.assertRecvDirection(); const results: HandlerReceiveResult[] = []; - for (const options of optionsList) - { + for (const options of optionsList) { const { trackId, kind, rtpParameters, streamId } = options; logger.debug('receive() [trackId:%s, kind:%s]', trackId, kind); const mid = kind; - this._remoteSdp!.receive( - { - mid, - kind, - offerRtpParameters : rtpParameters, - streamId : streamId || rtpParameters.rtcp!.cname!, - trackId - }); + this._remoteSdp!.receive({ + mid, + kind, + offerRtpParameters: rtpParameters, + streamId: streamId || rtpParameters.rtcp!.cname!, + trackId, + }); } const offer = { type: 'offer', sdp: this._remoteSdp!.getSdp() }; logger.debug( 'receive() | calling pc.setRemoteDescription() [offer:%o]', - offer); + offer, + ); await this._pc.setRemoteDescription(offer); let answer = await this._pc.createAnswer(); const localSdpObject = sdpTransform.parse(answer.sdp); - for (const options of optionsList) - { + for (const options of optionsList) { const { kind, rtpParameters } = options; const mid = kind; - const answerMediaObject = localSdpObject.media - .find((m: any) => String(m.mid) === mid); + const answerMediaObject = localSdpObject.media.find( + (m: any) => String(m.mid) === mid, + ); // May need to modify codec parameters in the answer based on codec // parameters in the offer. - sdpCommonUtils.applyCodecParameters( - { - offerRtpParameters : rtpParameters, - answerMediaObject - }); + sdpCommonUtils.applyCodecParameters({ + offerRtpParameters: rtpParameters, + answerMediaObject, + }); } answer = { type: 'answer', sdp: sdpTransform.write(localSdpObject) }; - if (!this._transportReady) - { - await this.setupTransport( - { - localDtlsRole : this._forcedLocalDtlsRole ?? 'client', - localSdpObject - }); + if (!this._transportReady) { + await this.setupTransport({ + localDtlsRole: this._forcedLocalDtlsRole ?? 'client', + localSdpObject, + }); } logger.debug( 'receive() | calling pc.setLocalDescription() [answer:%o]', - answer); + answer, + ); await this._pc.setLocalDescription(answer); - for (const options of optionsList) - { + for (const options of optionsList) { const { kind, trackId, rtpParameters } = options; const localId = trackId; const mid = kind; - const rtpReceiver = this._pc.getReceivers() + const rtpReceiver = this._pc + .getReceivers() .find((r: RTCRtpReceiver) => r.track && r.track.id === localId); - if (!rtpReceiver) - { + if (!rtpReceiver) { throw new Error('new RTCRtpReceiver not'); } // Insert into the map. - this._mapRecvLocalIdInfo.set(localId, { mid, rtpParameters, rtpReceiver }); + this._mapRecvLocalIdInfo.set(localId, { + mid, + rtpParameters, + rtpReceiver, + }); results.push({ localId, - track : rtpReceiver.track, - rtpReceiver + track: rtpReceiver.track, + rtpReceiver, }); } return results; } - async stopReceiving(localIds: string[]): Promise - { + async stopReceiving(localIds: string[]): Promise { this.assertRecvDirection(); - for (const localId of localIds) - { + for (const localId of localIds) { logger.debug('stopReceiving() [localId:%s]', localId); - const { mid, rtpParameters } = this._mapRecvLocalIdInfo.get(localId) || {}; + const { mid, rtpParameters } = + this._mapRecvLocalIdInfo.get(localId) || {}; // Remove from the map. this._mapRecvLocalIdInfo.delete(localId); - this._remoteSdp!.planBStopReceiving( - { mid: mid!, offerRtpParameters: rtpParameters! }); + this._remoteSdp!.planBStopReceiving({ + mid: mid!, + offerRtpParameters: rtpParameters!, + }); } const offer = { type: 'offer', sdp: this._remoteSdp!.getSdp() }; logger.debug( 'stopReceiving() | calling pc.setRemoteDescription() [offer:%o]', - offer); + offer, + ); await this._pc.setRemoteDescription(offer); @@ -857,61 +819,56 @@ export class Chrome67 extends HandlerInterface logger.debug( 'stopReceiving() | calling pc.setLocalDescription() [answer:%o]', - answer); + answer, + ); await this._pc.setLocalDescription(answer); } async pauseReceiving( // eslint-disable-next-line @typescript-eslint/no-unused-vars - localIds: string[]): Promise - { + localIds: string[], + ): Promise { // Unimplemented. } async resumeReceiving( // eslint-disable-next-line @typescript-eslint/no-unused-vars - localIds: string[]): Promise - { + localIds: string[], + ): Promise { // Unimplemented. } - async getReceiverStats(localId: string): Promise - { + async getReceiverStats(localId: string): Promise { this.assertRecvDirection(); const { rtpReceiver } = this._mapRecvLocalIdInfo.get(localId) || {}; - if (!rtpReceiver) - { + if (!rtpReceiver) { throw new Error('associated RTCRtpReceiver not found'); } return rtpReceiver.getStats(); } - async receiveDataChannel( - { sctpStreamParameters, label, protocol }: HandlerReceiveDataChannelOptions - ): Promise - { + async receiveDataChannel({ + sctpStreamParameters, + label, + protocol, + }: HandlerReceiveDataChannelOptions): Promise { this.assertRecvDirection(); - const { - streamId, - ordered, - maxPacketLifeTime, - maxRetransmits - } = sctpStreamParameters; + const { streamId, ordered, maxPacketLifeTime, maxRetransmits } = + sctpStreamParameters; - const options = - { - negotiated : true, - id : streamId, + const options = { + negotiated: true, + id: streamId, ordered, maxPacketLifeTime, - maxRetransmitTime : maxPacketLifeTime, // NOTE: Old spec. + maxRetransmitTime: maxPacketLifeTime, // NOTE: Old spec. maxRetransmits, - protocol + protocol, }; logger.debug('receiveDataChannel() [options:%o]', options); @@ -920,34 +877,33 @@ export class Chrome67 extends HandlerInterface // If this is the first DataChannel we need to create the SDP offer with // m=application section. - if (!this._hasDataChannelMediaSection) - { + if (!this._hasDataChannelMediaSection) { this._remoteSdp!.receiveSctpAssociation({ oldDataChannelSpec: true }); const offer = { type: 'offer', sdp: this._remoteSdp!.getSdp() }; logger.debug( 'receiveDataChannel() | calling pc.setRemoteDescription() [offer:%o]', - offer); + offer, + ); await this._pc.setRemoteDescription(offer); const answer = await this._pc.createAnswer(); - if (!this._transportReady) - { + if (!this._transportReady) { const localSdpObject = sdpTransform.parse(answer.sdp); - await this.setupTransport( - { - localDtlsRole : this._forcedLocalDtlsRole ?? 'client', - localSdpObject - }); + await this.setupTransport({ + localDtlsRole: this._forcedLocalDtlsRole ?? 'client', + localSdpObject, + }); } logger.debug( 'receiveDataChannel() | calling pc.setRemoteDescription() [answer:%o]', - answer); + answer, + ); await this._pc.setLocalDescription(answer); @@ -957,62 +913,51 @@ export class Chrome67 extends HandlerInterface return { dataChannel }; } - private async setupTransport( - { - localDtlsRole, - localSdpObject - }: - { - localDtlsRole: DtlsRole; - localSdpObject?: any; - } - ): Promise - { - if (!localSdpObject) - { + private async setupTransport({ + localDtlsRole, + localSdpObject, + }: { + localDtlsRole: DtlsRole; + localSdpObject?: any; + }): Promise { + if (!localSdpObject) { localSdpObject = sdpTransform.parse(this._pc.localDescription.sdp); } // Get our local DTLS parameters. - const dtlsParameters = - sdpCommonUtils.extractDtlsParameters({ sdpObject: localSdpObject }); + const dtlsParameters = sdpCommonUtils.extractDtlsParameters({ + sdpObject: localSdpObject, + }); // Set our DTLS role. dtlsParameters.role = localDtlsRole; // Update the remote DTLS role in the SDP. this._remoteSdp!.updateDtlsRole( - localDtlsRole === 'client' ? 'server' : 'client'); + localDtlsRole === 'client' ? 'server' : 'client', + ); // Need to tell the remote transport about our parameters. - await new Promise((resolve, reject) => - { - this.safeEmit( - '@connect', - { dtlsParameters }, - resolve, - reject - ); + await new Promise((resolve, reject) => { + this.safeEmit('@connect', { dtlsParameters }, resolve, reject); }); this._transportReady = true; } - private assertSendDirection(): void - { - if (this._direction !== 'send') - { + private assertSendDirection(): void { + if (this._direction !== 'send') { throw new Error( - 'method can just be called for handlers with "send" direction'); + 'method can just be called for handlers with "send" direction', + ); } } - private assertRecvDirection(): void - { - if (this._direction !== 'recv') - { + private assertRecvDirection(): void { + if (this._direction !== 'recv') { throw new Error( - 'method can just be called for handlers with "recv" direction'); + 'method can just be called for handlers with "recv" direction', + ); } } } diff --git a/src/handlers/Chrome70.ts b/src/handlers/Chrome70.ts index a5d61d00..8b2a38ec 100644 --- a/src/handlers/Chrome70.ts +++ b/src/handlers/Chrome70.ts @@ -15,7 +15,7 @@ import { HandlerSendDataChannelOptions, HandlerSendDataChannelResult, HandlerReceiveDataChannelOptions, - HandlerReceiveDataChannelResult + HandlerReceiveDataChannelResult, } from './HandlerInterface'; import { RemoteSdp } from './sdp/RemoteSdp'; import { parse as parseScalabilityMode } from '../scalabilityModes'; @@ -27,8 +27,7 @@ const logger = new Logger('Chrome70'); const SCTP_NUM_STREAMS = { OS: 1024, MIS: 1024 }; -export class Chrome70 extends HandlerInterface -{ +export class Chrome70 extends HandlerInterface { // Handler direction. private _direction?: 'send' | 'recv'; // Remote SDP handler. @@ -58,191 +57,172 @@ export class Chrome70 extends HandlerInterface /** * Creates a factory function. */ - static createFactory(): HandlerFactory - { + static createFactory(): HandlerFactory { return (): Chrome70 => new Chrome70(); } - constructor() - { + constructor() { super(); } - get name(): string - { + get name(): string { return 'Chrome70'; } - close(): void - { + close(): void { logger.debug('close()'); // Close RTCPeerConnection. - if (this._pc) - { - try { this._pc.close(); } - catch (error) {} + if (this._pc) { + try { + this._pc.close(); + } catch (error) {} } this.emit('@close'); } - async getNativeRtpCapabilities(): Promise - { + async getNativeRtpCapabilities(): Promise { logger.debug('getNativeRtpCapabilities()'); - const pc = new (RTCPeerConnection as any)( - { - iceServers : [], - iceTransportPolicy : 'all', - bundlePolicy : 'max-bundle', - rtcpMuxPolicy : 'require', - sdpSemantics : 'unified-plan' - }); + const pc = new (RTCPeerConnection as any)({ + iceServers: [], + iceTransportPolicy: 'all', + bundlePolicy: 'max-bundle', + rtcpMuxPolicy: 'require', + sdpSemantics: 'unified-plan', + }); - try - { + try { pc.addTransceiver('audio'); pc.addTransceiver('video'); const offer = await pc.createOffer(); - try { pc.close(); } - catch (error) {} + try { + pc.close(); + } catch (error) {} const sdpObject = sdpTransform.parse(offer.sdp); - const nativeRtpCapabilities = - sdpCommonUtils.extractRtpCapabilities({ sdpObject }); + const nativeRtpCapabilities = sdpCommonUtils.extractRtpCapabilities({ + sdpObject, + }); return nativeRtpCapabilities; - } - catch (error) - { - try { pc.close(); } - catch (error2) {} + } catch (error) { + try { + pc.close(); + } catch (error2) {} throw error; } } - async getNativeSctpCapabilities(): Promise - { + async getNativeSctpCapabilities(): Promise { logger.debug('getNativeSctpCapabilities()'); return { - numStreams : SCTP_NUM_STREAMS + numStreams: SCTP_NUM_STREAMS, }; } - run( - { - direction, - iceParameters, - iceCandidates, - dtlsParameters, - sctpParameters, - iceServers, - iceTransportPolicy, - additionalSettings, - proprietaryConstraints, - extendedRtpCapabilities - }: HandlerRunOptions - ): void - { + run({ + direction, + iceParameters, + iceCandidates, + dtlsParameters, + sctpParameters, + iceServers, + iceTransportPolicy, + additionalSettings, + proprietaryConstraints, + extendedRtpCapabilities, + }: HandlerRunOptions): void { logger.debug('run()'); this._direction = direction; - this._remoteSdp = new RemoteSdp( - { - iceParameters, - iceCandidates, - dtlsParameters, - sctpParameters - }); + this._remoteSdp = new RemoteSdp({ + iceParameters, + iceCandidates, + dtlsParameters, + sctpParameters, + }); - this._sendingRtpParametersByKind = - { - audio : ortc.getSendingRtpParameters('audio', extendedRtpCapabilities), - video : ortc.getSendingRtpParameters('video', extendedRtpCapabilities) + this._sendingRtpParametersByKind = { + audio: ortc.getSendingRtpParameters('audio', extendedRtpCapabilities), + video: ortc.getSendingRtpParameters('video', extendedRtpCapabilities), }; - this._sendingRemoteRtpParametersByKind = - { - audio : ortc.getSendingRemoteRtpParameters('audio', extendedRtpCapabilities), - video : ortc.getSendingRemoteRtpParameters('video', extendedRtpCapabilities) + this._sendingRemoteRtpParametersByKind = { + audio: ortc.getSendingRemoteRtpParameters( + 'audio', + extendedRtpCapabilities, + ), + video: ortc.getSendingRemoteRtpParameters( + 'video', + extendedRtpCapabilities, + ), }; - if (dtlsParameters.role && dtlsParameters.role !== 'auto') - { - this._forcedLocalDtlsRole = dtlsParameters.role === 'server' - ? 'client' - : 'server'; + if (dtlsParameters.role && dtlsParameters.role !== 'auto') { + this._forcedLocalDtlsRole = + dtlsParameters.role === 'server' ? 'client' : 'server'; } this._pc = new (RTCPeerConnection as any)( { - iceServers : iceServers || [], - iceTransportPolicy : iceTransportPolicy || 'all', - bundlePolicy : 'max-bundle', - rtcpMuxPolicy : 'require', - sdpSemantics : 'unified-plan', - ...additionalSettings + iceServers: iceServers || [], + iceTransportPolicy: iceTransportPolicy || 'all', + bundlePolicy: 'max-bundle', + rtcpMuxPolicy: 'require', + sdpSemantics: 'unified-plan', + ...additionalSettings, }, - proprietaryConstraints); + proprietaryConstraints, + ); - this._pc.addEventListener('icegatheringstatechange', () => - { + this._pc.addEventListener('icegatheringstatechange', () => { this.emit('@icegatheringstatechange', this._pc.iceGatheringState); }); - if (this._pc.connectionState) - { - this._pc.addEventListener('connectionstatechange', () => - { + if (this._pc.connectionState) { + this._pc.addEventListener('connectionstatechange', () => { this.emit('@connectionstatechange', this._pc.connectionState); }); - } - else - { - this._pc.addEventListener('iceconnectionstatechange', () => - { + } else { + this._pc.addEventListener('iceconnectionstatechange', () => { logger.warn( - 'run() | pc.connectionState not supported, using pc.iceConnectionState'); + 'run() | pc.connectionState not supported, using pc.iceConnectionState', + ); - switch (this._pc.iceConnectionState) - { - case 'checking': - { + switch (this._pc.iceConnectionState) { + case 'checking': { this.emit('@connectionstatechange', 'connecting'); break; } case 'connected': - case 'completed': - { + case 'completed': { this.emit('@connectionstatechange', 'connected'); break; } - case 'failed': - { + case 'failed': { this.emit('@connectionstatechange', 'failed'); break; } - case 'disconnected': - { + case 'disconnected': { this.emit('@connectionstatechange', 'disconnected'); break; } - case 'closed': - { + case 'closed': { this.emit('@connectionstatechange', 'closed'); break; @@ -252,8 +232,7 @@ export class Chrome70 extends HandlerInterface } } - async updateIceServers(iceServers: RTCIceServer[]): Promise - { + async updateIceServers(iceServers: RTCIceServer[]): Promise { logger.debug('updateIceServers()'); const configuration = this._pc.getConfiguration(); @@ -263,25 +242,23 @@ export class Chrome70 extends HandlerInterface this._pc.setConfiguration(configuration); } - async restartIce(iceParameters: IceParameters): Promise - { + async restartIce(iceParameters: IceParameters): Promise { logger.debug('restartIce()'); // Provide the remote SDP handler with new remote ICE parameters. this._remoteSdp!.updateIceParameters(iceParameters); - if (!this._transportReady) - { + if (!this._transportReady) { return; } - if (this._direction === 'send') - { + if (this._direction === 'send') { const offer = await this._pc.createOffer({ iceRestart: true }); logger.debug( 'restartIce() | calling pc.setLocalDescription() [offer:%o]', - offer); + offer, + ); await this._pc.setLocalDescription(offer); @@ -289,17 +266,17 @@ export class Chrome70 extends HandlerInterface logger.debug( 'restartIce() | calling pc.setRemoteDescription() [answer:%o]', - answer); + answer, + ); await this._pc.setRemoteDescription(answer); - } - else - { + } else { const offer = { type: 'offer', sdp: this._remoteSdp!.getSdp() }; logger.debug( 'restartIce() | calling pc.setRemoteDescription() [offer:%o]', - offer); + offer, + ); await this._pc.setRemoteDescription(offer); @@ -307,67 +284,73 @@ export class Chrome70 extends HandlerInterface logger.debug( 'restartIce() | calling pc.setLocalDescription() [answer:%o]', - answer); + answer, + ); await this._pc.setLocalDescription(answer); } } - async getTransportStats(): Promise - { + async getTransportStats(): Promise { return this._pc.getStats(); } - async send( - { track, encodings, codecOptions, codec }: HandlerSendOptions - ): Promise - { + async send({ + track, + encodings, + codecOptions, + codec, + }: HandlerSendOptions): Promise { this.assertSendDirection(); logger.debug('send() [kind:%s, track.id:%s]', track.kind, track.id); - const sendingRtpParameters = - utils.clone(this._sendingRtpParametersByKind![track.kind]); + const sendingRtpParameters = utils.clone( + this._sendingRtpParametersByKind![track.kind], + ); // This may throw. - sendingRtpParameters.codecs = - ortc.reduceCodecs(sendingRtpParameters.codecs, codec); + sendingRtpParameters.codecs = ortc.reduceCodecs( + sendingRtpParameters.codecs, + codec, + ); - const sendingRemoteRtpParameters = - utils.clone(this._sendingRemoteRtpParametersByKind![track.kind]); + const sendingRemoteRtpParameters = utils.clone( + this._sendingRemoteRtpParametersByKind![track.kind], + ); // This may throw. - sendingRemoteRtpParameters.codecs = - ortc.reduceCodecs(sendingRemoteRtpParameters.codecs, codec); + sendingRemoteRtpParameters.codecs = ortc.reduceCodecs( + sendingRemoteRtpParameters.codecs, + codec, + ); const mediaSectionIdx = this._remoteSdp!.getNextMediaSectionIdx(); - const transceiver = this._pc.addTransceiver( - track, { direction: 'sendonly', streams: [ this._sendStream ] }); + const transceiver = this._pc.addTransceiver(track, { + direction: 'sendonly', + streams: [this._sendStream], + }); let offer = await this._pc.createOffer(); let localSdpObject = sdpTransform.parse(offer.sdp); let offerMediaObject; - if (!this._transportReady) - { - await this.setupTransport( - { - localDtlsRole : this._forcedLocalDtlsRole ?? 'client', - localSdpObject - }); + if (!this._transportReady) { + await this.setupTransport({ + localDtlsRole: this._forcedLocalDtlsRole ?? 'client', + localSdpObject, + }); } - if (encodings && encodings.length > 1) - { + if (encodings && encodings.length > 1) { logger.debug('send() | enabling legacy simulcast'); localSdpObject = sdpTransform.parse(offer.sdp); offerMediaObject = localSdpObject.media[mediaSectionIdx.idx]; - sdpUnifiedPlanUtils.addLegacySimulcast( - { - offerMediaObject, - numStreams : encodings.length - }); + sdpUnifiedPlanUtils.addLegacySimulcast({ + offerMediaObject, + numStreams: encodings.length, + }); offer = { type: 'offer', sdp: sdpTransform.write(localSdpObject) }; } @@ -375,52 +358,44 @@ export class Chrome70 extends HandlerInterface // Special case for VP9 with SVC. let hackVp9Svc = false; - const layers = - parseScalabilityMode((encodings || [ {} ])[0].scalabilityMode); + const layers = parseScalabilityMode((encodings || [{}])[0].scalabilityMode); if ( encodings && encodings.length === 1 && layers.spatialLayers > 1 && sendingRtpParameters.codecs[0].mimeType.toLowerCase() === 'video/vp9' - ) - { + ) { logger.debug('send() | enabling legacy simulcast for VP9 SVC'); hackVp9Svc = true; localSdpObject = sdpTransform.parse(offer.sdp); offerMediaObject = localSdpObject.media[mediaSectionIdx.idx]; - sdpUnifiedPlanUtils.addLegacySimulcast( - { - offerMediaObject, - numStreams : layers.spatialLayers - }); + sdpUnifiedPlanUtils.addLegacySimulcast({ + offerMediaObject, + numStreams: layers.spatialLayers, + }); offer = { type: 'offer', sdp: sdpTransform.write(localSdpObject) }; } - logger.debug( - 'send() | calling pc.setLocalDescription() [offer:%o]', - offer); + logger.debug('send() | calling pc.setLocalDescription() [offer:%o]', offer); await this._pc.setLocalDescription(offer); // If encodings are given, apply them now. - if (encodings) - { + if (encodings) { logger.debug('send() | applying given encodings'); const parameters = transceiver.sender.getParameters(); - for (let idx = 0; idx < (parameters.encodings || []).length; ++idx) - { + for (let idx = 0; idx < (parameters.encodings || []).length; ++idx) { const encoding = parameters.encodings[idx]; const desiredEncoding = encodings[idx]; // Should not happen but just in case. - if (!desiredEncoding) - { + if (!desiredEncoding) { break; } @@ -440,61 +415,55 @@ export class Chrome70 extends HandlerInterface offerMediaObject = localSdpObject.media[mediaSectionIdx.idx]; // Set RTCP CNAME. - sendingRtpParameters.rtcp!.cname = - sdpCommonUtils.getCname({ offerMediaObject }); + sendingRtpParameters.rtcp!.cname = sdpCommonUtils.getCname({ + offerMediaObject, + }); // Set RTP encodings. - sendingRtpParameters.encodings = - sdpUnifiedPlanUtils.getRtpEncodings({ offerMediaObject }); + sendingRtpParameters.encodings = sdpUnifiedPlanUtils.getRtpEncodings({ + offerMediaObject, + }); // Complete encodings with given values. - if (encodings) - { - for (let idx = 0; idx < sendingRtpParameters.encodings.length; ++idx) - { - if (encodings[idx]) - { + if (encodings) { + for (let idx = 0; idx < sendingRtpParameters.encodings.length; ++idx) { + if (encodings[idx]) { Object.assign(sendingRtpParameters.encodings[idx], encodings[idx]); } } } // Hack for VP9 SVC. - if (hackVp9Svc) - { - sendingRtpParameters.encodings = [ sendingRtpParameters.encodings[0] ]; + if (hackVp9Svc) { + sendingRtpParameters.encodings = [sendingRtpParameters.encodings[0]]; } // If VP8 or H264 and there is effective simulcast, add scalabilityMode to // each encoding. if ( sendingRtpParameters.encodings.length > 1 && - ( - sendingRtpParameters.codecs[0].mimeType.toLowerCase() === 'video/vp8' || - sendingRtpParameters.codecs[0].mimeType.toLowerCase() === 'video/h264' - ) - ) - { - for (const encoding of sendingRtpParameters.encodings) - { + (sendingRtpParameters.codecs[0].mimeType.toLowerCase() === 'video/vp8' || + sendingRtpParameters.codecs[0].mimeType.toLowerCase() === 'video/h264') + ) { + for (const encoding of sendingRtpParameters.encodings) { encoding.scalabilityMode = 'L1T3'; } } - this._remoteSdp!.send( - { - offerMediaObject, - reuseMid : mediaSectionIdx.reuseMid, - offerRtpParameters : sendingRtpParameters, - answerRtpParameters : sendingRemoteRtpParameters, - codecOptions - }); + this._remoteSdp!.send({ + offerMediaObject, + reuseMid: mediaSectionIdx.reuseMid, + offerRtpParameters: sendingRtpParameters, + answerRtpParameters: sendingRemoteRtpParameters, + codecOptions, + }); const answer = { type: 'answer', sdp: this._remoteSdp!.getSdp() }; logger.debug( 'send() | calling pc.setRemoteDescription() [answer:%o]', - answer); + answer, + ); await this._pc.setRemoteDescription(answer); @@ -503,21 +472,19 @@ export class Chrome70 extends HandlerInterface return { localId, - rtpParameters : sendingRtpParameters, - rtpSender : transceiver.sender + rtpParameters: sendingRtpParameters, + rtpSender: transceiver.sender, }; } - async stopSending(localId: string): Promise - { + async stopSending(localId: string): Promise { this.assertSendDirection(); logger.debug('stopSending() [localId:%s]', localId); const transceiver = this._mapMidTransceiver.get(localId); - if (!transceiver) - { + if (!transceiver) { throw new Error('associated RTCRtpTransceiver not found'); } @@ -525,24 +492,22 @@ export class Chrome70 extends HandlerInterface this._pc.removeTrack(transceiver.sender); - const mediaSectionClosed = - this._remoteSdp!.closeMediaSection(transceiver.mid!); + const mediaSectionClosed = this._remoteSdp!.closeMediaSection( + transceiver.mid!, + ); - if (mediaSectionClosed) - { - try - { + if (mediaSectionClosed) { + try { transceiver.stop(); - } - catch (error) - {} + } catch (error) {} } const offer = await this._pc.createOffer(); logger.debug( 'stopSending() | calling pc.setLocalDescription() [offer:%o]', - offer); + offer, + ); await this._pc.setLocalDescription(offer); @@ -550,7 +515,8 @@ export class Chrome70 extends HandlerInterface logger.debug( 'stopSending() | calling pc.setRemoteDescription() [answer:%o]', - answer); + answer, + ); await this._pc.setRemoteDescription(answer); @@ -558,71 +524,69 @@ export class Chrome70 extends HandlerInterface } // eslint-disable-next-line @typescript-eslint/no-unused-vars - async pauseSending(localId: string): Promise - { + async pauseSending(localId: string): Promise { // Unimplemented. } // eslint-disable-next-line @typescript-eslint/no-unused-vars - async resumeSending(localId: string): Promise - { + async resumeSending(localId: string): Promise { // Unimplemented. } async replaceTrack( - localId: string, track: MediaStreamTrack | null - ): Promise - { + localId: string, + track: MediaStreamTrack | null, + ): Promise { this.assertSendDirection(); - if (track) - { + if (track) { logger.debug( - 'replaceTrack() [localId:%s, track.id:%s]', localId, track.id); - } - else - { + 'replaceTrack() [localId:%s, track.id:%s]', + localId, + track.id, + ); + } else { logger.debug('replaceTrack() [localId:%s, no track]', localId); } const transceiver = this._mapMidTransceiver.get(localId); - if (!transceiver) - { + if (!transceiver) { throw new Error('associated RTCRtpTransceiver not found'); } await transceiver.sender.replaceTrack(track); } - async setMaxSpatialLayer(localId: string, spatialLayer: number): Promise - { + async setMaxSpatialLayer( + localId: string, + spatialLayer: number, + ): Promise { this.assertSendDirection(); logger.debug( 'setMaxSpatialLayer() [localId:%s, spatialLayer:%s]', - localId, spatialLayer); + localId, + spatialLayer, + ); const transceiver = this._mapMidTransceiver.get(localId); - if (!transceiver) - { + if (!transceiver) { throw new Error('associated RTCRtpTransceiver not found'); } const parameters = transceiver.sender.getParameters(); - parameters.encodings.forEach((encoding: RTCRtpEncodingParameters, idx: number) => - { - if (idx <= spatialLayer) - { - encoding.active = true; - } - else - { - encoding.active = false; - } - }); + parameters.encodings.forEach( + (encoding: RTCRtpEncodingParameters, idx: number) => { + if (idx <= spatialLayer) { + encoding.active = true; + } else { + encoding.active = false; + } + }, + ); await transceiver.sender.setParameters(parameters); @@ -632,7 +596,8 @@ export class Chrome70 extends HandlerInterface logger.debug( 'setMaxSpatialLayer() | calling pc.setLocalDescription() [offer:%o]', - offer); + offer, + ); await this._pc.setLocalDescription(offer); @@ -640,32 +605,34 @@ export class Chrome70 extends HandlerInterface logger.debug( 'setMaxSpatialLayer() | calling pc.setRemoteDescription() [answer:%o]', - answer); + answer, + ); await this._pc.setRemoteDescription(answer); } - async setRtpEncodingParameters(localId: string, params: any): Promise - { + async setRtpEncodingParameters(localId: string, params: any): Promise { this.assertSendDirection(); logger.debug( 'setRtpEncodingParameters() [localId:%s, params:%o]', - localId, params); + localId, + params, + ); const transceiver = this._mapMidTransceiver.get(localId); - if (!transceiver) - { + if (!transceiver) { throw new Error('associated RTCRtpTransceiver not found'); } const parameters = transceiver.sender.getParameters(); - parameters.encodings.forEach((encoding: RTCRtpEncodingParameters, idx: number) => - { - parameters.encodings[idx] = { ...encoding, ...params }; - }); + parameters.encodings.forEach( + (encoding: RTCRtpEncodingParameters, idx: number) => { + parameters.encodings[idx] = { ...encoding, ...params }; + }, + ); await transceiver.sender.setParameters(parameters); @@ -675,7 +642,8 @@ export class Chrome70 extends HandlerInterface logger.debug( 'setRtpEncodingParameters() | calling pc.setLocalDescription() [offer:%o]', - offer); + offer, + ); await this._pc.setLocalDescription(offer); @@ -683,46 +651,41 @@ export class Chrome70 extends HandlerInterface logger.debug( 'setRtpEncodingParameters() | calling pc.setRemoteDescription() [answer:%o]', - answer); + answer, + ); await this._pc.setRemoteDescription(answer); } - async getSenderStats(localId: string): Promise - { + async getSenderStats(localId: string): Promise { this.assertSendDirection(); const transceiver = this._mapMidTransceiver.get(localId); - if (!transceiver) - { + if (!transceiver) { throw new Error('associated RTCRtpTransceiver not found'); } return transceiver.sender.getStats(); } - async sendDataChannel( - { - ordered, - maxPacketLifeTime, - maxRetransmits, - label, - protocol - }: HandlerSendDataChannelOptions - ): Promise - { + async sendDataChannel({ + ordered, + maxPacketLifeTime, + maxRetransmits, + label, + protocol, + }: HandlerSendDataChannelOptions): Promise { this.assertSendDirection(); - const options = - { - negotiated : true, - id : this._nextSendSctpStreamId, + const options = { + negotiated: true, + id: this._nextSendSctpStreamId, ordered, maxPacketLifeTime, - maxRetransmitTime : maxPacketLifeTime, // NOTE: Old spec. + maxRetransmitTime: maxPacketLifeTime, // NOTE: Old spec. maxRetransmits, - protocol + protocol, }; logger.debug('sendDataChannel() [options:%o]', options); @@ -735,25 +698,24 @@ export class Chrome70 extends HandlerInterface // If this is the first DataChannel we need to create the SDP answer with // m=application section. - if (!this._hasDataChannelMediaSection) - { + if (!this._hasDataChannelMediaSection) { const offer = await this._pc.createOffer(); const localSdpObject = sdpTransform.parse(offer.sdp); - const offerMediaObject = localSdpObject.media - .find((m: any) => m.type === 'application'); + const offerMediaObject = localSdpObject.media.find( + (m: any) => m.type === 'application', + ); - if (!this._transportReady) - { - await this.setupTransport( - { - localDtlsRole : this._forcedLocalDtlsRole ?? 'client', - localSdpObject - }); + if (!this._transportReady) { + await this.setupTransport({ + localDtlsRole: this._forcedLocalDtlsRole ?? 'client', + localSdpObject, + }); } logger.debug( 'sendDataChannel() | calling pc.setLocalDescription() [offer:%o]', - offer); + offer, + ); await this._pc.setLocalDescription(offer); @@ -763,35 +725,33 @@ export class Chrome70 extends HandlerInterface logger.debug( 'sendDataChannel() | calling pc.setRemoteDescription() [answer:%o]', - answer); + answer, + ); await this._pc.setRemoteDescription(answer); this._hasDataChannelMediaSection = true; } - const sctpStreamParameters: SctpStreamParameters = - { - streamId : options.id, - ordered : options.ordered, - maxPacketLifeTime : options.maxPacketLifeTime, - maxRetransmits : options.maxRetransmits + const sctpStreamParameters: SctpStreamParameters = { + streamId: options.id, + ordered: options.ordered, + maxPacketLifeTime: options.maxPacketLifeTime, + maxRetransmits: options.maxRetransmits, }; return { dataChannel, sctpStreamParameters }; } async receive( - optionsList: HandlerReceiveOptions[] - ) : Promise - { + optionsList: HandlerReceiveOptions[], + ): Promise { this.assertRecvDirection(); const results: HandlerReceiveResult[] = []; const mapLocalId: Map = new Map(); - for (const options of optionsList) - { + for (const options of optionsList) { const { trackId, kind, rtpParameters, streamId } = options; logger.debug('receive() [trackId:%s, kind:%s]', trackId, kind); @@ -800,69 +760,66 @@ export class Chrome70 extends HandlerInterface mapLocalId.set(trackId, localId); - this._remoteSdp!.receive( - { - mid : localId, - kind, - offerRtpParameters : rtpParameters, - streamId : streamId || rtpParameters.rtcp!.cname!, - trackId - }); + this._remoteSdp!.receive({ + mid: localId, + kind, + offerRtpParameters: rtpParameters, + streamId: streamId || rtpParameters.rtcp!.cname!, + trackId, + }); } const offer = { type: 'offer', sdp: this._remoteSdp!.getSdp() }; logger.debug( 'receive() | calling pc.setRemoteDescription() [offer:%o]', - offer); + offer, + ); await this._pc.setRemoteDescription(offer); let answer = await this._pc.createAnswer(); const localSdpObject = sdpTransform.parse(answer.sdp); - for (const options of optionsList) - { + for (const options of optionsList) { const { trackId, rtpParameters } = options; const localId = mapLocalId.get(trackId); - const answerMediaObject = localSdpObject.media - .find((m: any) => String(m.mid) === localId); + const answerMediaObject = localSdpObject.media.find( + (m: any) => String(m.mid) === localId, + ); // May need to modify codec parameters in the answer based on codec // parameters in the offer. - sdpCommonUtils.applyCodecParameters( - { - offerRtpParameters : rtpParameters, - answerMediaObject - }); + sdpCommonUtils.applyCodecParameters({ + offerRtpParameters: rtpParameters, + answerMediaObject, + }); } answer = { type: 'answer', sdp: sdpTransform.write(localSdpObject) }; - if (!this._transportReady) - { - await this.setupTransport( - { - localDtlsRole : this._forcedLocalDtlsRole ?? 'client', - localSdpObject - }); + if (!this._transportReady) { + await this.setupTransport({ + localDtlsRole: this._forcedLocalDtlsRole ?? 'client', + localSdpObject, + }); } logger.debug( 'receive() | calling pc.setLocalDescription() [answer:%o]', - answer); + answer, + ); await this._pc.setLocalDescription(answer); - for (const options of optionsList) - { + for (const options of optionsList) { const { trackId } = options; const localId = mapLocalId.get(trackId)!; - const transceiver = this._pc.getTransceivers() + const transceiver = this._pc + .getTransceivers() .find((t: RTCRtpTransceiver) => t.mid === localId); - if (!transceiver) - { + if (!transceiver) { throw new Error('new RTCRtpTransceiver not found'); } @@ -871,26 +828,23 @@ export class Chrome70 extends HandlerInterface results.push({ localId, - track : transceiver.receiver.track, - rtpReceiver : transceiver.receiver + track: transceiver.receiver.track, + rtpReceiver: transceiver.receiver, }); } return results; } - async stopReceiving(localIds: string[]): Promise - { + async stopReceiving(localIds: string[]): Promise { this.assertRecvDirection(); - for (const localId of localIds) - { + for (const localId of localIds) { logger.debug('stopReceiving() [localId:%s]', localId); const transceiver = this._mapMidTransceiver.get(localId); - if (!transceiver) - { + if (!transceiver) { throw new Error('associated RTCRtpTransceiver not found'); } @@ -901,7 +855,8 @@ export class Chrome70 extends HandlerInterface logger.debug( 'stopReceiving() | calling pc.setRemoteDescription() [offer:%o]', - offer); + offer, + ); await this._pc.setRemoteDescription(offer); @@ -909,66 +864,64 @@ export class Chrome70 extends HandlerInterface logger.debug( 'stopReceiving() | calling pc.setLocalDescription() [answer:%o]', - answer); + answer, + ); await this._pc.setLocalDescription(answer); - for (const localId of localIds) - { + for (const localId of localIds) { this._mapMidTransceiver.delete(localId); } } async pauseReceiving( // eslint-disable-next-line @typescript-eslint/no-unused-vars - localIds: string[]): Promise - { + localIds: string[], + ): Promise { // Unimplemented. } async resumeReceiving( // eslint-disable-next-line @typescript-eslint/no-unused-vars - localIds: string[]): Promise - { + localIds: string[], + ): Promise { // Unimplemented. } - async getReceiverStats(localId: string): Promise - { + async getReceiverStats(localId: string): Promise { this.assertRecvDirection(); const transceiver = this._mapMidTransceiver.get(localId); - if (!transceiver) - { + if (!transceiver) { throw new Error('associated RTCRtpTransceiver not found'); } return transceiver.receiver.getStats(); } - async receiveDataChannel( - { sctpStreamParameters, label, protocol }: HandlerReceiveDataChannelOptions - ): Promise - { + async receiveDataChannel({ + sctpStreamParameters, + label, + protocol, + }: HandlerReceiveDataChannelOptions): Promise { this.assertRecvDirection(); const { streamId, ordered, maxPacketLifeTime, - maxRetransmits + maxRetransmits, }: SctpStreamParameters = sctpStreamParameters; - const options = - { - negotiated : true, - id : streamId, + const options = { + negotiated: true, + id: streamId, ordered, maxPacketLifeTime, - maxRetransmitTime : maxPacketLifeTime, // NOTE: Old spec. + maxRetransmitTime: maxPacketLifeTime, // NOTE: Old spec. maxRetransmits, - protocol + protocol, }; logger.debug('receiveDataChannel() [options:%o]', options); @@ -977,34 +930,33 @@ export class Chrome70 extends HandlerInterface // If this is the first DataChannel we need to create the SDP offer with // m=application section. - if (!this._hasDataChannelMediaSection) - { + if (!this._hasDataChannelMediaSection) { this._remoteSdp!.receiveSctpAssociation(); const offer = { type: 'offer', sdp: this._remoteSdp!.getSdp() }; logger.debug( 'receiveDataChannel() | calling pc.setRemoteDescription() [offer:%o]', - offer); + offer, + ); await this._pc.setRemoteDescription(offer); const answer = await this._pc.createAnswer(); - if (!this._transportReady) - { + if (!this._transportReady) { const localSdpObject = sdpTransform.parse(answer.sdp); - await this.setupTransport( - { - localDtlsRole : this._forcedLocalDtlsRole ?? 'client', - localSdpObject - }); + await this.setupTransport({ + localDtlsRole: this._forcedLocalDtlsRole ?? 'client', + localSdpObject, + }); } logger.debug( 'receiveDataChannel() | calling pc.setRemoteDescription() [answer:%o]', - answer); + answer, + ); await this._pc.setLocalDescription(answer); @@ -1014,62 +966,51 @@ export class Chrome70 extends HandlerInterface return { dataChannel }; } - private async setupTransport( - { - localDtlsRole, - localSdpObject - }: - { - localDtlsRole: DtlsRole; - localSdpObject?: any; - } - ): Promise - { - if (!localSdpObject) - { + private async setupTransport({ + localDtlsRole, + localSdpObject, + }: { + localDtlsRole: DtlsRole; + localSdpObject?: any; + }): Promise { + if (!localSdpObject) { localSdpObject = sdpTransform.parse(this._pc.localDescription.sdp); } // Get our local DTLS parameters. - const dtlsParameters = - sdpCommonUtils.extractDtlsParameters({ sdpObject: localSdpObject }); + const dtlsParameters = sdpCommonUtils.extractDtlsParameters({ + sdpObject: localSdpObject, + }); // Set our DTLS role. dtlsParameters.role = localDtlsRole; // Update the remote DTLS role in the SDP. this._remoteSdp!.updateDtlsRole( - localDtlsRole === 'client' ? 'server' : 'client'); + localDtlsRole === 'client' ? 'server' : 'client', + ); // Need to tell the remote transport about our parameters. - await new Promise((resolve, reject) => - { - this.safeEmit( - '@connect', - { dtlsParameters }, - resolve, - reject - ); + await new Promise((resolve, reject) => { + this.safeEmit('@connect', { dtlsParameters }, resolve, reject); }); this._transportReady = true; } - private assertSendDirection(): void - { - if (this._direction !== 'send') - { + private assertSendDirection(): void { + if (this._direction !== 'send') { throw new Error( - 'method can just be called for handlers with "send" direction'); + 'method can just be called for handlers with "send" direction', + ); } } - private assertRecvDirection(): void - { - if (this._direction !== 'recv') - { + private assertRecvDirection(): void { + if (this._direction !== 'recv') { throw new Error( - 'method can just be called for handlers with "recv" direction'); + 'method can just be called for handlers with "recv" direction', + ); } } } diff --git a/src/handlers/Chrome74.ts b/src/handlers/Chrome74.ts index 8ec57985..93dad950 100644 --- a/src/handlers/Chrome74.ts +++ b/src/handlers/Chrome74.ts @@ -17,7 +17,7 @@ import { HandlerSendDataChannelOptions, HandlerSendDataChannelResult, HandlerReceiveDataChannelOptions, - HandlerReceiveDataChannelResult + HandlerReceiveDataChannelResult, } from './HandlerInterface'; import { RemoteSdp } from './sdp/RemoteSdp'; import { parse as parseScalabilityMode } from '../scalabilityModes'; @@ -25,7 +25,7 @@ import { IceParameters, DtlsRole } from '../Transport'; import { RtpCapabilities, RtpParameters, - RtpEncodingParameters + RtpEncodingParameters, } from '../RtpParameters'; import { SctpCapabilities, SctpStreamParameters } from '../SctpParameters'; @@ -33,8 +33,7 @@ const logger = new Logger('Chrome74'); const SCTP_NUM_STREAMS = { OS: 1024, MIS: 1024 }; -export class Chrome74 extends HandlerInterface -{ +export class Chrome74 extends HandlerInterface { // Closed flag. private _closed = false; // Handler direction. @@ -66,201 +65,181 @@ export class Chrome74 extends HandlerInterface /** * Creates a factory function. */ - static createFactory(): HandlerFactory - { + static createFactory(): HandlerFactory { return (): Chrome74 => new Chrome74(); } - constructor() - { + constructor() { super(); } - get name(): string - { + get name(): string { return 'Chrome74'; } - close(): void - { + close(): void { logger.debug('close()'); - if (this._closed) - { + if (this._closed) { return; } this._closed = true; // Close RTCPeerConnection. - if (this._pc) - { - try { this._pc.close(); } - catch (error) {} + if (this._pc) { + try { + this._pc.close(); + } catch (error) {} } this.emit('@close'); } - async getNativeRtpCapabilities(): Promise - { + async getNativeRtpCapabilities(): Promise { logger.debug('getNativeRtpCapabilities()'); - const pc = new (RTCPeerConnection as any)( - { - iceServers : [], - iceTransportPolicy : 'all', - bundlePolicy : 'max-bundle', - rtcpMuxPolicy : 'require', - sdpSemantics : 'unified-plan' - }); + const pc = new (RTCPeerConnection as any)({ + iceServers: [], + iceTransportPolicy: 'all', + bundlePolicy: 'max-bundle', + rtcpMuxPolicy: 'require', + sdpSemantics: 'unified-plan', + }); - try - { + try { pc.addTransceiver('audio'); pc.addTransceiver('video'); const offer = await pc.createOffer(); - try { pc.close(); } - catch (error) {} + try { + pc.close(); + } catch (error) {} const sdpObject = sdpTransform.parse(offer.sdp); - const nativeRtpCapabilities = - sdpCommonUtils.extractRtpCapabilities({ sdpObject }); + const nativeRtpCapabilities = sdpCommonUtils.extractRtpCapabilities({ + sdpObject, + }); // libwebrtc supports NACK for OPUS but doesn't announce it. ortcUtils.addNackSuppportForOpus(nativeRtpCapabilities); return nativeRtpCapabilities; - } - catch (error) - { - try { pc.close(); } - catch (error2) {} + } catch (error) { + try { + pc.close(); + } catch (error2) {} throw error; } } - async getNativeSctpCapabilities(): Promise - { + async getNativeSctpCapabilities(): Promise { logger.debug('getNativeSctpCapabilities()'); return { - numStreams : SCTP_NUM_STREAMS + numStreams: SCTP_NUM_STREAMS, }; } - run( - { - direction, - iceParameters, - iceCandidates, - dtlsParameters, - sctpParameters, - iceServers, - iceTransportPolicy, - additionalSettings, - proprietaryConstraints, - extendedRtpCapabilities - }: HandlerRunOptions - ): void - { + run({ + direction, + iceParameters, + iceCandidates, + dtlsParameters, + sctpParameters, + iceServers, + iceTransportPolicy, + additionalSettings, + proprietaryConstraints, + extendedRtpCapabilities, + }: HandlerRunOptions): void { logger.debug('run()'); this._direction = direction; - this._remoteSdp = new RemoteSdp( - { - iceParameters, - iceCandidates, - dtlsParameters, - sctpParameters - }); + this._remoteSdp = new RemoteSdp({ + iceParameters, + iceCandidates, + dtlsParameters, + sctpParameters, + }); - this._sendingRtpParametersByKind = - { - audio : ortc.getSendingRtpParameters('audio', extendedRtpCapabilities), - video : ortc.getSendingRtpParameters('video', extendedRtpCapabilities) + this._sendingRtpParametersByKind = { + audio: ortc.getSendingRtpParameters('audio', extendedRtpCapabilities), + video: ortc.getSendingRtpParameters('video', extendedRtpCapabilities), }; - this._sendingRemoteRtpParametersByKind = - { - audio : ortc.getSendingRemoteRtpParameters('audio', extendedRtpCapabilities), - video : ortc.getSendingRemoteRtpParameters('video', extendedRtpCapabilities) + this._sendingRemoteRtpParametersByKind = { + audio: ortc.getSendingRemoteRtpParameters( + 'audio', + extendedRtpCapabilities, + ), + video: ortc.getSendingRemoteRtpParameters( + 'video', + extendedRtpCapabilities, + ), }; - if (dtlsParameters.role && dtlsParameters.role !== 'auto') - { - this._forcedLocalDtlsRole = dtlsParameters.role === 'server' - ? 'client' - : 'server'; + if (dtlsParameters.role && dtlsParameters.role !== 'auto') { + this._forcedLocalDtlsRole = + dtlsParameters.role === 'server' ? 'client' : 'server'; } this._pc = new (RTCPeerConnection as any)( { - iceServers : iceServers || [], - iceTransportPolicy : iceTransportPolicy || 'all', - bundlePolicy : 'max-bundle', - rtcpMuxPolicy : 'require', - sdpSemantics : 'unified-plan', - ...additionalSettings + iceServers: iceServers || [], + iceTransportPolicy: iceTransportPolicy || 'all', + bundlePolicy: 'max-bundle', + rtcpMuxPolicy: 'require', + sdpSemantics: 'unified-plan', + ...additionalSettings, }, - proprietaryConstraints); + proprietaryConstraints, + ); - this._pc.addEventListener('icegatheringstatechange', () => - { + this._pc.addEventListener('icegatheringstatechange', () => { this.emit('@icegatheringstatechange', this._pc.iceGatheringState); }); - if (this._pc.connectionState) - { - this._pc.addEventListener('connectionstatechange', () => - { + if (this._pc.connectionState) { + this._pc.addEventListener('connectionstatechange', () => { this.emit('@connectionstatechange', this._pc.connectionState); }); - } - else - { + } else { logger.warn( - 'run() | pc.connectionState not supported, using pc.iceConnectionState'); + 'run() | pc.connectionState not supported, using pc.iceConnectionState', + ); - this._pc.addEventListener('iceconnectionstatechange', () => - { - switch (this._pc.iceConnectionState) - { - case 'checking': - { + this._pc.addEventListener('iceconnectionstatechange', () => { + switch (this._pc.iceConnectionState) { + case 'checking': { this.emit('@connectionstatechange', 'connecting'); break; } case 'connected': - case 'completed': - { + case 'completed': { this.emit('@connectionstatechange', 'connected'); break; } - case 'failed': - { + case 'failed': { this.emit('@connectionstatechange', 'failed'); break; } - case 'disconnected': - { + case 'disconnected': { this.emit('@connectionstatechange', 'disconnected'); break; } - case 'closed': - { + case 'closed': { this.emit('@connectionstatechange', 'closed'); break; @@ -270,8 +249,7 @@ export class Chrome74 extends HandlerInterface } } - async updateIceServers(iceServers: RTCIceServer[]): Promise - { + async updateIceServers(iceServers: RTCIceServer[]): Promise { this.assertNotClosed(); logger.debug('updateIceServers()'); @@ -283,8 +261,7 @@ export class Chrome74 extends HandlerInterface this._pc.setConfiguration(configuration); } - async restartIce(iceParameters: IceParameters): Promise - { + async restartIce(iceParameters: IceParameters): Promise { this.assertNotClosed(); logger.debug('restartIce()'); @@ -292,18 +269,17 @@ export class Chrome74 extends HandlerInterface // Provide the remote SDP handler with new remote ICE parameters. this._remoteSdp!.updateIceParameters(iceParameters); - if (!this._transportReady) - { + if (!this._transportReady) { return; } - if (this._direction === 'send') - { + if (this._direction === 'send') { const offer = await this._pc.createOffer({ iceRestart: true }); logger.debug( 'restartIce() | calling pc.setLocalDescription() [offer:%o]', - offer); + offer, + ); await this._pc.setLocalDescription(offer); @@ -311,17 +287,17 @@ export class Chrome74 extends HandlerInterface logger.debug( 'restartIce() | calling pc.setRemoteDescription() [answer:%o]', - answer); + answer, + ); await this._pc.setRemoteDescription(answer); - } - else - { + } else { const offer = { type: 'offer', sdp: this._remoteSdp!.getSdp() }; logger.debug( 'restartIce() | calling pc.setRemoteDescription() [offer:%o]', - offer); + offer, + ); await this._pc.setRemoteDescription(offer); @@ -329,102 +305,99 @@ export class Chrome74 extends HandlerInterface logger.debug( 'restartIce() | calling pc.setLocalDescription() [answer:%o]', - answer); + answer, + ); await this._pc.setLocalDescription(answer); } } - async getTransportStats(): Promise - { + async getTransportStats(): Promise { this.assertNotClosed(); return this._pc.getStats(); } - async send( - { track, encodings, codecOptions, codec }: HandlerSendOptions - ): Promise - { + async send({ + track, + encodings, + codecOptions, + codec, + }: HandlerSendOptions): Promise { this.assertNotClosed(); this.assertSendDirection(); logger.debug('send() [kind:%s, track.id:%s]', track.kind, track.id); - if (encodings && encodings.length > 1) - { - encodings.forEach((encoding: RtpEncodingParameters, idx: number) => - { + if (encodings && encodings.length > 1) { + encodings.forEach((encoding: RtpEncodingParameters, idx: number) => { encoding.rid = `r${idx}`; }); } - const sendingRtpParameters = - utils.clone(this._sendingRtpParametersByKind![track.kind]); + const sendingRtpParameters = utils.clone( + this._sendingRtpParametersByKind![track.kind], + ); // This may throw. - sendingRtpParameters.codecs = - ortc.reduceCodecs(sendingRtpParameters.codecs, codec); + sendingRtpParameters.codecs = ortc.reduceCodecs( + sendingRtpParameters.codecs, + codec, + ); - const sendingRemoteRtpParameters = - utils.clone(this._sendingRemoteRtpParametersByKind![track.kind]); + const sendingRemoteRtpParameters = utils.clone( + this._sendingRemoteRtpParametersByKind![track.kind], + ); // This may throw. - sendingRemoteRtpParameters.codecs = - ortc.reduceCodecs(sendingRemoteRtpParameters.codecs, codec); + sendingRemoteRtpParameters.codecs = ortc.reduceCodecs( + sendingRemoteRtpParameters.codecs, + codec, + ); const mediaSectionIdx = this._remoteSdp!.getNextMediaSectionIdx(); - const transceiver = this._pc.addTransceiver( - track, - { - direction : 'sendonly', - streams : [ this._sendStream ], - sendEncodings : encodings - }); + const transceiver = this._pc.addTransceiver(track, { + direction: 'sendonly', + streams: [this._sendStream], + sendEncodings: encodings, + }); let offer = await this._pc.createOffer(); let localSdpObject = sdpTransform.parse(offer.sdp); let offerMediaObject; - if (!this._transportReady) - { - await this.setupTransport( - { - localDtlsRole : this._forcedLocalDtlsRole ?? 'client', - localSdpObject - }); + if (!this._transportReady) { + await this.setupTransport({ + localDtlsRole: this._forcedLocalDtlsRole ?? 'client', + localSdpObject, + }); } // Special case for VP9 with SVC. let hackVp9Svc = false; - const layers = - parseScalabilityMode((encodings || [ {} ])[0].scalabilityMode); + const layers = parseScalabilityMode((encodings || [{}])[0].scalabilityMode); if ( encodings && encodings.length === 1 && layers.spatialLayers > 1 && sendingRtpParameters.codecs[0].mimeType.toLowerCase() === 'video/vp9' - ) - { + ) { logger.debug('send() | enabling legacy simulcast for VP9 SVC'); hackVp9Svc = true; localSdpObject = sdpTransform.parse(offer.sdp); offerMediaObject = localSdpObject.media[mediaSectionIdx.idx]; - sdpUnifiedPlanUtils.addLegacySimulcast( - { - offerMediaObject, - numStreams : layers.spatialLayers - }); + sdpUnifiedPlanUtils.addLegacySimulcast({ + offerMediaObject, + numStreams: layers.spatialLayers, + }); offer = { type: 'offer', sdp: sdpTransform.write(localSdpObject) }; } - logger.debug( - 'send() | calling pc.setLocalDescription() [offer:%o]', - offer); + logger.debug('send() | calling pc.setLocalDescription() [offer:%o]', offer); await this._pc.setLocalDescription(offer); @@ -438,35 +411,34 @@ export class Chrome74 extends HandlerInterface offerMediaObject = localSdpObject.media[mediaSectionIdx.idx]; // Set RTCP CNAME. - sendingRtpParameters.rtcp!.cname = - sdpCommonUtils.getCname({ offerMediaObject }); + sendingRtpParameters.rtcp!.cname = sdpCommonUtils.getCname({ + offerMediaObject, + }); // Set RTP encodings by parsing the SDP offer if no encodings are given. - if (!encodings) - { - sendingRtpParameters.encodings = - sdpUnifiedPlanUtils.getRtpEncodings({ offerMediaObject }); + if (!encodings) { + sendingRtpParameters.encodings = sdpUnifiedPlanUtils.getRtpEncodings({ + offerMediaObject, + }); } // Set RTP encodings by parsing the SDP offer and complete them with given // one if just a single encoding has been given. - else if (encodings.length === 1) - { - let newEncodings = - sdpUnifiedPlanUtils.getRtpEncodings({ offerMediaObject }); + else if (encodings.length === 1) { + let newEncodings = sdpUnifiedPlanUtils.getRtpEncodings({ + offerMediaObject, + }); Object.assign(newEncodings[0], encodings[0]); // Hack for VP9 SVC. - if (hackVp9Svc) - { - newEncodings = [ newEncodings[0] ]; + if (hackVp9Svc) { + newEncodings = [newEncodings[0]]; } sendingRtpParameters.encodings = newEncodings; } // Otherwise if more than 1 encoding are given use them verbatim. - else - { + else { sendingRtpParameters.encodings = encodings; } @@ -474,40 +446,33 @@ export class Chrome74 extends HandlerInterface // each encoding. if ( sendingRtpParameters.encodings.length > 1 && - ( - sendingRtpParameters.codecs[0].mimeType.toLowerCase() === 'video/vp8' || - sendingRtpParameters.codecs[0].mimeType.toLowerCase() === 'video/h264' - ) - ) - { - for (const encoding of sendingRtpParameters.encodings) - { - if (encoding.scalabilityMode) - { + (sendingRtpParameters.codecs[0].mimeType.toLowerCase() === 'video/vp8' || + sendingRtpParameters.codecs[0].mimeType.toLowerCase() === 'video/h264') + ) { + for (const encoding of sendingRtpParameters.encodings) { + if (encoding.scalabilityMode) { encoding.scalabilityMode = `L1T${layers.temporalLayers}`; - } - else - { + } else { encoding.scalabilityMode = 'L1T3'; } } } - this._remoteSdp!.send( - { - offerMediaObject, - reuseMid : mediaSectionIdx.reuseMid, - offerRtpParameters : sendingRtpParameters, - answerRtpParameters : sendingRemoteRtpParameters, - codecOptions, - extmapAllowMixed : true - }); + this._remoteSdp!.send({ + offerMediaObject, + reuseMid: mediaSectionIdx.reuseMid, + offerRtpParameters: sendingRtpParameters, + answerRtpParameters: sendingRemoteRtpParameters, + codecOptions, + extmapAllowMixed: true, + }); const answer = { type: 'answer', sdp: this._remoteSdp!.getSdp() }; logger.debug( 'send() | calling pc.setRemoteDescription() [answer:%o]', - answer); + answer, + ); await this._pc.setRemoteDescription(answer); @@ -516,26 +481,23 @@ export class Chrome74 extends HandlerInterface return { localId, - rtpParameters : sendingRtpParameters, - rtpSender : transceiver.sender + rtpParameters: sendingRtpParameters, + rtpSender: transceiver.sender, }; } - async stopSending(localId: string): Promise - { + async stopSending(localId: string): Promise { this.assertSendDirection(); logger.debug('stopSending() [localId:%s]', localId); - if (this._closed) - { + if (this._closed) { return; } const transceiver = this._mapMidTransceiver.get(localId); - if (!transceiver) - { + if (!transceiver) { throw new Error('associated RTCRtpTransceiver not found'); } @@ -543,24 +505,22 @@ export class Chrome74 extends HandlerInterface this._pc.removeTrack(transceiver.sender); - const mediaSectionClosed = - this._remoteSdp!.closeMediaSection(transceiver.mid!); + const mediaSectionClosed = this._remoteSdp!.closeMediaSection( + transceiver.mid!, + ); - if (mediaSectionClosed) - { - try - { + if (mediaSectionClosed) { + try { transceiver.stop(); - } - catch (error) - {} + } catch (error) {} } const offer = await this._pc.createOffer(); logger.debug( 'stopSending() | calling pc.setLocalDescription() [offer:%o]', - offer); + offer, + ); await this._pc.setLocalDescription(offer); @@ -568,15 +528,15 @@ export class Chrome74 extends HandlerInterface logger.debug( 'stopSending() | calling pc.setRemoteDescription() [answer:%o]', - answer); + answer, + ); await this._pc.setRemoteDescription(answer); this._mapMidTransceiver.delete(localId); } - async pauseSending(localId: string): Promise - { + async pauseSending(localId: string): Promise { this.assertNotClosed(); this.assertSendDirection(); @@ -584,8 +544,7 @@ export class Chrome74 extends HandlerInterface const transceiver = this._mapMidTransceiver.get(localId); - if (!transceiver) - { + if (!transceiver) { throw new Error('associated RTCRtpTransceiver not found'); } @@ -596,7 +555,8 @@ export class Chrome74 extends HandlerInterface logger.debug( 'pauseSending() | calling pc.setLocalDescription() [offer:%o]', - offer); + offer, + ); await this._pc.setLocalDescription(offer); @@ -604,13 +564,13 @@ export class Chrome74 extends HandlerInterface logger.debug( 'pauseSending() | calling pc.setRemoteDescription() [answer:%o]', - answer); + answer, + ); await this._pc.setRemoteDescription(answer); } - async resumeSending(localId: string): Promise - { + async resumeSending(localId: string): Promise { this.assertNotClosed(); this.assertSendDirection(); @@ -620,8 +580,7 @@ export class Chrome74 extends HandlerInterface this._remoteSdp!.resumeSendingMediaSection(localId); - if (!transceiver) - { + if (!transceiver) { throw new Error('associated RTCRtpTransceiver not found'); } @@ -631,7 +590,8 @@ export class Chrome74 extends HandlerInterface logger.debug( 'resumeSending() | calling pc.setLocalDescription() [offer:%o]', - offer); + offer, + ); await this._pc.setLocalDescription(offer); @@ -639,67 +599,68 @@ export class Chrome74 extends HandlerInterface logger.debug( 'resumeSending() | calling pc.setRemoteDescription() [answer:%o]', - answer); + answer, + ); await this._pc.setRemoteDescription(answer); } async replaceTrack( - localId: string, track: MediaStreamTrack | null - ): Promise - { + localId: string, + track: MediaStreamTrack | null, + ): Promise { this.assertNotClosed(); this.assertSendDirection(); - if (track) - { + if (track) { logger.debug( - 'replaceTrack() [localId:%s, track.id:%s]', localId, track.id); - } - else - { + 'replaceTrack() [localId:%s, track.id:%s]', + localId, + track.id, + ); + } else { logger.debug('replaceTrack() [localId:%s, no track]', localId); } const transceiver = this._mapMidTransceiver.get(localId); - if (!transceiver) - { + if (!transceiver) { throw new Error('associated RTCRtpTransceiver not found'); } await transceiver.sender.replaceTrack(track); } - async setMaxSpatialLayer(localId: string, spatialLayer: number): Promise - { + async setMaxSpatialLayer( + localId: string, + spatialLayer: number, + ): Promise { this.assertNotClosed(); this.assertSendDirection(); logger.debug( 'setMaxSpatialLayer() [localId:%s, spatialLayer:%s]', - localId, spatialLayer); + localId, + spatialLayer, + ); const transceiver = this._mapMidTransceiver.get(localId); - if (!transceiver) - { + if (!transceiver) { throw new Error('associated RTCRtpTransceiver not found'); } const parameters = transceiver.sender.getParameters(); - parameters.encodings.forEach((encoding: RTCRtpEncodingParameters, idx: number) => - { - if (idx <= spatialLayer) - { - encoding.active = true; - } - else - { - encoding.active = false; - } - }); + parameters.encodings.forEach( + (encoding: RTCRtpEncodingParameters, idx: number) => { + if (idx <= spatialLayer) { + encoding.active = true; + } else { + encoding.active = false; + } + }, + ); await transceiver.sender.setParameters(parameters); @@ -709,7 +670,8 @@ export class Chrome74 extends HandlerInterface logger.debug( 'setMaxSpatialLayer() | calling pc.setLocalDescription() [offer:%o]', - offer); + offer, + ); await this._pc.setLocalDescription(offer); @@ -717,33 +679,35 @@ export class Chrome74 extends HandlerInterface logger.debug( 'setMaxSpatialLayer() | calling pc.setRemoteDescription() [answer:%o]', - answer); + answer, + ); await this._pc.setRemoteDescription(answer); } - async setRtpEncodingParameters(localId: string, params: any): Promise - { + async setRtpEncodingParameters(localId: string, params: any): Promise { this.assertNotClosed(); this.assertSendDirection(); logger.debug( 'setRtpEncodingParameters() [localId:%s, params:%o]', - localId, params); + localId, + params, + ); const transceiver = this._mapMidTransceiver.get(localId); - if (!transceiver) - { + if (!transceiver) { throw new Error('associated RTCRtpTransceiver not found'); } const parameters = transceiver.sender.getParameters(); - parameters.encodings.forEach((encoding: RTCRtpEncodingParameters, idx: number) => - { - parameters.encodings[idx] = { ...encoding, ...params }; - }); + parameters.encodings.forEach( + (encoding: RTCRtpEncodingParameters, idx: number) => { + parameters.encodings[idx] = { ...encoding, ...params }; + }, + ); await transceiver.sender.setParameters(parameters); @@ -753,7 +717,8 @@ export class Chrome74 extends HandlerInterface logger.debug( 'setRtpEncodingParameters() | calling pc.setLocalDescription() [offer:%o]', - offer); + offer, + ); await this._pc.setLocalDescription(offer); @@ -761,47 +726,42 @@ export class Chrome74 extends HandlerInterface logger.debug( 'setRtpEncodingParameters() | calling pc.setRemoteDescription() [answer:%o]', - answer); + answer, + ); await this._pc.setRemoteDescription(answer); } - async getSenderStats(localId: string): Promise - { + async getSenderStats(localId: string): Promise { this.assertNotClosed(); this.assertSendDirection(); const transceiver = this._mapMidTransceiver.get(localId); - if (!transceiver) - { + if (!transceiver) { throw new Error('associated RTCRtpTransceiver not found'); } return transceiver.sender.getStats(); } - async sendDataChannel( - { - ordered, - maxPacketLifeTime, - maxRetransmits, - label, - protocol - }: HandlerSendDataChannelOptions - ): Promise - { + async sendDataChannel({ + ordered, + maxPacketLifeTime, + maxRetransmits, + label, + protocol, + }: HandlerSendDataChannelOptions): Promise { this.assertNotClosed(); this.assertSendDirection(); - const options = - { - negotiated : true, - id : this._nextSendSctpStreamId, + const options = { + negotiated: true, + id: this._nextSendSctpStreamId, ordered, maxPacketLifeTime, maxRetransmits, - protocol + protocol, }; logger.debug('sendDataChannel() [options:%o]', options); @@ -814,25 +774,24 @@ export class Chrome74 extends HandlerInterface // If this is the first DataChannel we need to create the SDP answer with // m=application section. - if (!this._hasDataChannelMediaSection) - { + if (!this._hasDataChannelMediaSection) { const offer = await this._pc.createOffer(); const localSdpObject = sdpTransform.parse(offer.sdp); - const offerMediaObject = localSdpObject.media - .find((m: any) => m.type === 'application'); + const offerMediaObject = localSdpObject.media.find( + (m: any) => m.type === 'application', + ); - if (!this._transportReady) - { - await this.setupTransport( - { - localDtlsRole : this._forcedLocalDtlsRole ?? 'client', - localSdpObject - }); + if (!this._transportReady) { + await this.setupTransport({ + localDtlsRole: this._forcedLocalDtlsRole ?? 'client', + localSdpObject, + }); } logger.debug( 'sendDataChannel() | calling pc.setLocalDescription() [offer:%o]', - offer); + offer, + ); await this._pc.setLocalDescription(offer); @@ -842,36 +801,34 @@ export class Chrome74 extends HandlerInterface logger.debug( 'sendDataChannel() | calling pc.setRemoteDescription() [answer:%o]', - answer); + answer, + ); await this._pc.setRemoteDescription(answer); this._hasDataChannelMediaSection = true; } - const sctpStreamParameters: SctpStreamParameters = - { - streamId : options.id, - ordered : options.ordered, - maxPacketLifeTime : options.maxPacketLifeTime, - maxRetransmits : options.maxRetransmits + const sctpStreamParameters: SctpStreamParameters = { + streamId: options.id, + ordered: options.ordered, + maxPacketLifeTime: options.maxPacketLifeTime, + maxRetransmits: options.maxRetransmits, }; return { dataChannel, sctpStreamParameters }; } async receive( - optionsList: HandlerReceiveOptions[] - ) : Promise - { + optionsList: HandlerReceiveOptions[], + ): Promise { this.assertNotClosed(); this.assertRecvDirection(); const results: HandlerReceiveResult[] = []; const mapLocalId: Map = new Map(); - for (const options of optionsList) - { + for (const options of optionsList) { const { trackId, kind, rtpParameters, streamId } = options; logger.debug('receive() [trackId:%s, kind:%s]', trackId, kind); @@ -880,80 +837,75 @@ export class Chrome74 extends HandlerInterface mapLocalId.set(trackId, localId); - this._remoteSdp!.receive( - { - mid : localId, - kind, - offerRtpParameters : rtpParameters, - streamId : streamId || rtpParameters.rtcp!.cname!, - trackId - }); + this._remoteSdp!.receive({ + mid: localId, + kind, + offerRtpParameters: rtpParameters, + streamId: streamId || rtpParameters.rtcp!.cname!, + trackId, + }); } const offer = { type: 'offer', sdp: this._remoteSdp!.getSdp() }; logger.debug( 'receive() | calling pc.setRemoteDescription() [offer:%o]', - offer); + offer, + ); await this._pc.setRemoteDescription(offer); let answer = await this._pc.createAnswer(); const localSdpObject = sdpTransform.parse(answer.sdp); - for (const options of optionsList) - { + for (const options of optionsList) { const { trackId, rtpParameters } = options; const localId = mapLocalId.get(trackId); - const answerMediaObject = localSdpObject.media - .find((m: any) => String(m.mid) === localId); + const answerMediaObject = localSdpObject.media.find( + (m: any) => String(m.mid) === localId, + ); // May need to modify codec parameters in the answer based on codec // parameters in the offer. - sdpCommonUtils.applyCodecParameters( - { - offerRtpParameters : rtpParameters, - answerMediaObject - }); + sdpCommonUtils.applyCodecParameters({ + offerRtpParameters: rtpParameters, + answerMediaObject, + }); } answer = { type: 'answer', sdp: sdpTransform.write(localSdpObject) }; - if (!this._transportReady) - { - await this.setupTransport( - { - localDtlsRole : this._forcedLocalDtlsRole ?? 'client', - localSdpObject - }); + if (!this._transportReady) { + await this.setupTransport({ + localDtlsRole: this._forcedLocalDtlsRole ?? 'client', + localSdpObject, + }); } logger.debug( 'receive() | calling pc.setLocalDescription() [answer:%o]', - answer); + answer, + ); await this._pc.setLocalDescription(answer); - for (const options of optionsList) - { + for (const options of optionsList) { const { trackId } = options; const localId = mapLocalId.get(trackId)!; - const transceiver = this._pc.getTransceivers() + const transceiver = this._pc + .getTransceivers() .find((t: RTCRtpTransceiver) => t.mid === localId); - if (!transceiver) - { + if (!transceiver) { throw new Error('new RTCRtpTransceiver not found'); - } - else - { + } else { // Store in the map. this._mapMidTransceiver.set(localId, transceiver); results.push({ localId, - track : transceiver.receiver.track, - rtpReceiver : transceiver.receiver + track: transceiver.receiver.track, + rtpReceiver: transceiver.receiver, }); } } @@ -961,23 +913,19 @@ export class Chrome74 extends HandlerInterface return results; } - async stopReceiving(localIds: string[]): Promise - { + async stopReceiving(localIds: string[]): Promise { this.assertRecvDirection(); - if (this._closed) - { + if (this._closed) { return; } - for (const localId of localIds) - { + for (const localId of localIds) { logger.debug('stopReceiving() [localId:%s]', localId); const transceiver = this._mapMidTransceiver.get(localId); - if (!transceiver) - { + if (!transceiver) { throw new Error('associated RTCRtpTransceiver not found'); } @@ -988,7 +936,8 @@ export class Chrome74 extends HandlerInterface logger.debug( 'stopReceiving() | calling pc.setRemoteDescription() [offer:%o]', - offer); + offer, + ); await this._pc.setRemoteDescription(offer); @@ -996,29 +945,26 @@ export class Chrome74 extends HandlerInterface logger.debug( 'stopReceiving() | calling pc.setLocalDescription() [answer:%o]', - answer); + answer, + ); await this._pc.setLocalDescription(answer); - for (const localId of localIds) - { + for (const localId of localIds) { this._mapMidTransceiver.delete(localId); } } - async pauseReceiving(localIds: string[]): Promise - { + async pauseReceiving(localIds: string[]): Promise { this.assertNotClosed(); this.assertRecvDirection(); - for (const localId of localIds) - { + for (const localId of localIds) { logger.debug('pauseReceiving() [localId:%s]', localId); const transceiver = this._mapMidTransceiver.get(localId); - if (!transceiver) - { + if (!transceiver) { throw new Error('associated RTCRtpTransceiver not found'); } @@ -1030,7 +976,8 @@ export class Chrome74 extends HandlerInterface logger.debug( 'pauseReceiving() | calling pc.setRemoteDescription() [offer:%o]', - offer); + offer, + ); await this._pc.setRemoteDescription(offer); @@ -1038,24 +985,22 @@ export class Chrome74 extends HandlerInterface logger.debug( 'pauseReceiving() | calling pc.setLocalDescription() [answer:%o]', - answer); + answer, + ); await this._pc.setLocalDescription(answer); } - async resumeReceiving(localIds: string[]): Promise - { + async resumeReceiving(localIds: string[]): Promise { this.assertNotClosed(); this.assertRecvDirection(); - for (const localId of localIds) - { + for (const localId of localIds) { logger.debug('resumeReceiving() [localId:%s]', localId); const transceiver = this._mapMidTransceiver.get(localId); - if (!transceiver) - { + if (!transceiver) { throw new Error('associated RTCRtpTransceiver not found'); } @@ -1067,7 +1012,8 @@ export class Chrome74 extends HandlerInterface logger.debug( 'resumeReceiving() | calling pc.setRemoteDescription() [offer:%o]', - offer); + offer, + ); await this._pc.setRemoteDescription(offer); @@ -1075,30 +1021,30 @@ export class Chrome74 extends HandlerInterface logger.debug( 'resumeReceiving() | calling pc.setLocalDescription() [answer:%o]', - answer); + answer, + ); await this._pc.setLocalDescription(answer); } - async getReceiverStats(localId: string): Promise - { + async getReceiverStats(localId: string): Promise { this.assertNotClosed(); this.assertRecvDirection(); const transceiver = this._mapMidTransceiver.get(localId); - if (!transceiver) - { + if (!transceiver) { throw new Error('associated RTCRtpTransceiver not found'); } return transceiver.receiver.getStats(); } - async receiveDataChannel( - { sctpStreamParameters, label, protocol }: HandlerReceiveDataChannelOptions - ): Promise - { + async receiveDataChannel({ + sctpStreamParameters, + label, + protocol, + }: HandlerReceiveDataChannelOptions): Promise { this.assertNotClosed(); this.assertRecvDirection(); @@ -1106,17 +1052,16 @@ export class Chrome74 extends HandlerInterface streamId, ordered, maxPacketLifeTime, - maxRetransmits + maxRetransmits, }: SctpStreamParameters = sctpStreamParameters; - const options = - { - negotiated : true, - id : streamId, + const options = { + negotiated: true, + id: streamId, ordered, maxPacketLifeTime, maxRetransmits, - protocol + protocol, }; logger.debug('receiveDataChannel() [options:%o]', options); @@ -1125,34 +1070,33 @@ export class Chrome74 extends HandlerInterface // If this is the first DataChannel we need to create the SDP offer with // m=application section. - if (!this._hasDataChannelMediaSection) - { + if (!this._hasDataChannelMediaSection) { this._remoteSdp!.receiveSctpAssociation(); const offer = { type: 'offer', sdp: this._remoteSdp!.getSdp() }; logger.debug( 'receiveDataChannel() | calling pc.setRemoteDescription() [offer:%o]', - offer); + offer, + ); await this._pc.setRemoteDescription(offer); const answer = await this._pc.createAnswer(); - if (!this._transportReady) - { + if (!this._transportReady) { const localSdpObject = sdpTransform.parse(answer.sdp); - await this.setupTransport( - { - localDtlsRole : this._forcedLocalDtlsRole ?? 'client', - localSdpObject - }); + await this.setupTransport({ + localDtlsRole: this._forcedLocalDtlsRole ?? 'client', + localSdpObject, + }); } logger.debug( 'receiveDataChannel() | calling pc.setRemoteDescription() [answer:%o]', - answer); + answer, + ); await this._pc.setLocalDescription(answer); @@ -1162,70 +1106,57 @@ export class Chrome74 extends HandlerInterface return { dataChannel }; } - private async setupTransport( - { - localDtlsRole, - localSdpObject - }: - { - localDtlsRole: DtlsRole; - localSdpObject?: any; - } - ): Promise - { - if (!localSdpObject) - { + private async setupTransport({ + localDtlsRole, + localSdpObject, + }: { + localDtlsRole: DtlsRole; + localSdpObject?: any; + }): Promise { + if (!localSdpObject) { localSdpObject = sdpTransform.parse(this._pc.localDescription.sdp); } // Get our local DTLS parameters. - const dtlsParameters = - sdpCommonUtils.extractDtlsParameters({ sdpObject: localSdpObject }); + const dtlsParameters = sdpCommonUtils.extractDtlsParameters({ + sdpObject: localSdpObject, + }); // Set our DTLS role. dtlsParameters.role = localDtlsRole; // Update the remote DTLS role in the SDP. this._remoteSdp!.updateDtlsRole( - localDtlsRole === 'client' ? 'server' : 'client'); + localDtlsRole === 'client' ? 'server' : 'client', + ); // Need to tell the remote transport about our parameters. - await new Promise((resolve, reject) => - { - this.safeEmit( - '@connect', - { dtlsParameters }, - resolve, - reject - ); + await new Promise((resolve, reject) => { + this.safeEmit('@connect', { dtlsParameters }, resolve, reject); }); this._transportReady = true; } - private assertNotClosed(): void - { - if (this._closed) - { + private assertNotClosed(): void { + if (this._closed) { throw new InvalidStateError('method called in a closed handler'); } } - private assertSendDirection(): void - { - if (this._direction !== 'send') - { + private assertSendDirection(): void { + if (this._direction !== 'send') { throw new Error( - 'method can just be called for handlers with "send" direction'); + 'method can just be called for handlers with "send" direction', + ); } } - private assertRecvDirection(): void - { - if (this._direction !== 'recv') - { + private assertRecvDirection(): void { + if (this._direction !== 'recv') { throw new Error( - 'method can just be called for handlers with "recv" direction'); + 'method can just be called for handlers with "recv" direction', + ); } } } diff --git a/src/handlers/Edge11.ts b/src/handlers/Edge11.ts index 8d3e5cef..1f51d9a1 100644 --- a/src/handlers/Edge11.ts +++ b/src/handlers/Edge11.ts @@ -14,21 +14,20 @@ import { HandlerSendDataChannelOptions, HandlerSendDataChannelResult, HandlerReceiveDataChannelOptions, - HandlerReceiveDataChannelResult + HandlerReceiveDataChannelResult, } from './HandlerInterface'; import { IceParameters, IceCandidate, DtlsParameters, - DtlsRole + DtlsRole, } from '../Transport'; import { RtpCapabilities, RtpParameters } from '../RtpParameters'; import { SctpCapabilities } from '../SctpParameters'; const logger = new Logger('Edge11'); -export class Edge11 extends HandlerInterface -{ +export class Edge11 extends HandlerInterface { // Generic sending RTP parameters for audio and video. private _sendingRtpParametersByKind?: { [key: string]: RtpParameters }; // Transport remote ICE parameters. @@ -57,92 +56,85 @@ export class Edge11 extends HandlerInterface /** * Creates a factory function. */ - static createFactory(): HandlerFactory - { + static createFactory(): HandlerFactory { return (): Edge11 => new Edge11(); } - constructor() - { + constructor() { super(); } - get name(): string - { + get name(): string { return 'Edge11'; } - close(): void - { + close(): void { logger.debug('close()'); // Close the ICE gatherer. // NOTE: Not yet implemented by Edge. - try { this._iceGatherer.close(); } - catch (error) {} + try { + this._iceGatherer.close(); + } catch (error) {} // Close the ICE transport. - try { this._iceTransport.stop(); } - catch (error) {} + try { + this._iceTransport.stop(); + } catch (error) {} // Close the DTLS transport. - try { this._dtlsTransport.stop(); } - catch (error) {} + try { + this._dtlsTransport.stop(); + } catch (error) {} // Close RTCRtpSenders. - for (const rtpSender of this._rtpSenders.values()) - { - try { (rtpSender as any).stop(); } - catch (error) {} + for (const rtpSender of this._rtpSenders.values()) { + try { + (rtpSender as any).stop(); + } catch (error) {} } // Close RTCRtpReceivers. - for (const rtpReceiver of this._rtpReceivers.values()) - { - try { (rtpReceiver as any).stop(); } - catch (error) {} + for (const rtpReceiver of this._rtpReceivers.values()) { + try { + (rtpReceiver as any).stop(); + } catch (error) {} } this.emit('@close'); } - async getNativeRtpCapabilities(): Promise - { + async getNativeRtpCapabilities(): Promise { logger.debug('getNativeRtpCapabilities()'); return edgeUtils.getCapabilities(); } - async getNativeSctpCapabilities(): Promise - { + async getNativeSctpCapabilities(): Promise { logger.debug('getNativeSctpCapabilities()'); return { - numStreams : { OS: 0, MIS: 0 } + numStreams: { OS: 0, MIS: 0 }, }; } - run( - { - direction, // eslint-disable-line @typescript-eslint/no-unused-vars - iceParameters, - iceCandidates, - dtlsParameters, - sctpParameters, // eslint-disable-line @typescript-eslint/no-unused-vars - iceServers, - iceTransportPolicy, - additionalSettings, // eslint-disable-line @typescript-eslint/no-unused-vars - proprietaryConstraints, // eslint-disable-line @typescript-eslint/no-unused-vars - extendedRtpCapabilities - }: HandlerRunOptions - ): void - { + run({ + direction, // eslint-disable-line @typescript-eslint/no-unused-vars + iceParameters, + iceCandidates, + dtlsParameters, + sctpParameters, // eslint-disable-line @typescript-eslint/no-unused-vars + iceServers, + iceTransportPolicy, + additionalSettings, // eslint-disable-line @typescript-eslint/no-unused-vars + proprietaryConstraints, // eslint-disable-line @typescript-eslint/no-unused-vars + extendedRtpCapabilities, + }: HandlerRunOptions): void { logger.debug('run()'); - this._sendingRtpParametersByKind = - { - audio : ortc.getSendingRtpParameters('audio', extendedRtpCapabilities), - video : ortc.getSendingRtpParameters('video', extendedRtpCapabilities) + this._sendingRtpParametersByKind = { + audio: ortc.getSendingRtpParameters('audio', extendedRtpCapabilities), + video: ortc.getSendingRtpParameters('video', extendedRtpCapabilities), }; this._remoteIceParameters = iceParameters; @@ -156,75 +148,66 @@ export class Edge11 extends HandlerInterface } // eslint-disable-next-line @typescript-eslint/no-unused-vars - async updateIceServers(iceServers: RTCIceServer[]): Promise - { + async updateIceServers(iceServers: RTCIceServer[]): Promise { // NOTE: Edge 11 does not implement iceGatherer.gater(). throw new UnsupportedError('not supported'); } - async restartIce(iceParameters: IceParameters): Promise - { + async restartIce(iceParameters: IceParameters): Promise { logger.debug('restartIce()'); this._remoteIceParameters = iceParameters; - if (!this._transportReady) - { + if (!this._transportReady) { return; } logger.debug('restartIce() | calling iceTransport.start()'); - this._iceTransport.start( - this._iceGatherer, iceParameters, 'controlling'); + this._iceTransport.start(this._iceGatherer, iceParameters, 'controlling'); - for (const candidate of this._remoteIceCandidates!) - { + for (const candidate of this._remoteIceCandidates!) { this._iceTransport.addRemoteCandidate(candidate); } this._iceTransport.addRemoteCandidate({}); } - async getTransportStats(): Promise - { + async getTransportStats(): Promise { return this._iceTransport.getStats(); } async send( // eslint-disable-next-line @typescript-eslint/no-unused-vars - { track, encodings, codecOptions, codec }: HandlerSendOptions - ): Promise - { + { track, encodings, codecOptions, codec }: HandlerSendOptions, + ): Promise { logger.debug('send() [kind:%s, track.id:%s]', track.kind, track.id); - if (!this._transportReady) - { + if (!this._transportReady) { await this.setupTransport({ localDtlsRole: 'server' }); } logger.debug('send() | calling new RTCRtpSender()'); const rtpSender = new (RTCRtpSender as any)(track, this._dtlsTransport); - const rtpParameters = - utils.clone(this._sendingRtpParametersByKind![track.kind]); + const rtpParameters = utils.clone( + this._sendingRtpParametersByKind![track.kind], + ); rtpParameters.codecs = ortc.reduceCodecs(rtpParameters.codecs, codec); - const useRtx = rtpParameters.codecs - .some((_codec: any) => /.+\/rtx$/i.test(_codec.mimeType)); + const useRtx = rtpParameters.codecs.some((_codec: any) => + /.+\/rtx$/i.test(_codec.mimeType), + ); - if (!encodings) - { - encodings = [ {} ]; + if (!encodings) { + encodings = [{}]; } - for (const encoding of encodings) - { + for (const encoding of encodings) { encoding.ssrc = utils.generateRandomNumber(); - if (useRtx) - { + if (useRtx) { encoding.rtx = { ssrc: utils.generateRandomNumber() }; } } @@ -232,11 +215,10 @@ export class Edge11 extends HandlerInterface rtpParameters.encodings = encodings; // Fill RTCRtpParameters.rtcp. - rtpParameters.rtcp = - { - cname : this._cname!, - reducedSize : true, - mux : true + rtpParameters.rtcp = { + cname: this._cname!, + reducedSize: true, + mux: true, }; // NOTE: Convert our standard RTCRtpParameters into those that Edge @@ -245,7 +227,8 @@ export class Edge11 extends HandlerInterface logger.debug( 'send() | calling rtpSender.send() [params:%o]', - edgeRtpParameters); + edgeRtpParameters, + ); await rtpSender.send(edgeRtpParameters); @@ -259,27 +242,22 @@ export class Edge11 extends HandlerInterface return { localId, rtpParameters, rtpSender }; } - async stopSending(localId: string): Promise - { + async stopSending(localId: string): Promise { logger.debug('stopSending() [localId:%s]', localId); const rtpSender = this._rtpSenders.get(localId); - if (!rtpSender) - { + if (!rtpSender) { throw new Error('RTCRtpSender not found'); } this._rtpSenders.delete(localId); - try - { + try { logger.debug('stopSending() | calling rtpSender.stop()'); (rtpSender as any).stop(); - } - catch (error) - { + } catch (error) { logger.warn('stopSending() | rtpSender.stop() failed:%o', error); throw error; @@ -287,101 +265,93 @@ export class Edge11 extends HandlerInterface } // eslint-disable-next-line @typescript-eslint/no-unused-vars - async pauseSending(localId: string): Promise - { + async pauseSending(localId: string): Promise { // Unimplemented. } // eslint-disable-next-line @typescript-eslint/no-unused-vars - async resumeSending(localId: string): Promise - { + async resumeSending(localId: string): Promise { // Unimplemented. } async replaceTrack( - localId: string, track: MediaStreamTrack | null - ): Promise - { - if (track) - { + localId: string, + track: MediaStreamTrack | null, + ): Promise { + if (track) { logger.debug( - 'replaceTrack() [localId:%s, track.id:%s]', localId, track.id); - } - else - { + 'replaceTrack() [localId:%s, track.id:%s]', + localId, + track.id, + ); + } else { logger.debug('replaceTrack() [localId:%s, no track]', localId); } const rtpSender = this._rtpSenders.get(localId); - if (!rtpSender) - { + if (!rtpSender) { throw new Error('RTCRtpSender not found'); } (rtpSender as any).setTrack(track); } - async setMaxSpatialLayer(localId: string, spatialLayer: number): Promise - { + async setMaxSpatialLayer( + localId: string, + spatialLayer: number, + ): Promise { logger.debug( 'setMaxSpatialLayer() [localId:%s, spatialLayer:%s]', - localId, spatialLayer); + localId, + spatialLayer, + ); const rtpSender = this._rtpSenders.get(localId); - if (!rtpSender) - { + if (!rtpSender) { throw new Error('RTCRtpSender not found'); } const parameters = rtpSender.getParameters(); - parameters.encodings - .forEach((encoding, idx) => - { - if (idx <= spatialLayer) - { - encoding.active = true; - } - else - { - encoding.active = false; - } - }); + parameters.encodings.forEach((encoding, idx) => { + if (idx <= spatialLayer) { + encoding.active = true; + } else { + encoding.active = false; + } + }); await rtpSender.setParameters(parameters); } - async setRtpEncodingParameters(localId: string, params: any): Promise - { + async setRtpEncodingParameters(localId: string, params: any): Promise { logger.debug( 'setRtpEncodingParameters() [localId:%s, params:%o]', - localId, params); + localId, + params, + ); const rtpSender = this._rtpSenders.get(localId); - if (!rtpSender) - { + if (!rtpSender) { throw new Error('RTCRtpSender not found'); } const parameters = rtpSender.getParameters(); - parameters.encodings.forEach((encoding: any, idx: number) => - { + parameters.encodings.forEach((encoding: any, idx: number) => { parameters.encodings[idx] = { ...encoding, ...params }; }); await rtpSender.setParameters(parameters); } - async getSenderStats(localId: string): Promise - { + async getSenderStats(localId: string): Promise { const rtpSender = this._rtpSenders.get(localId); - if (!rtpSender) - { + if (!rtpSender) { throw new Error('RTCRtpSender not found'); } @@ -390,51 +360,48 @@ export class Edge11 extends HandlerInterface async sendDataChannel( // eslint-disable-next-line @typescript-eslint/no-unused-vars - options: HandlerSendDataChannelOptions - ): Promise - { + options: HandlerSendDataChannelOptions, + ): Promise { throw new UnsupportedError('not implemented'); } async receive( - optionsList: HandlerReceiveOptions[] - ) : Promise - { + optionsList: HandlerReceiveOptions[], + ): Promise { const results: HandlerReceiveResult[] = []; - for (const options of optionsList) - { + for (const options of optionsList) { const { trackId, kind } = options; logger.debug('receive() [trackId:%s, kind:%s]', trackId, kind); } - if (!this._transportReady) - { + if (!this._transportReady) { await this.setupTransport({ localDtlsRole: 'server' }); } - for (const options of optionsList) - { + for (const options of optionsList) { const { trackId, kind, rtpParameters } = options; logger.debug('receive() | calling new RTCRtpReceiver()'); - const rtpReceiver = new (RTCRtpReceiver as any)(this._dtlsTransport, kind); + const rtpReceiver = new (RTCRtpReceiver as any)( + this._dtlsTransport, + kind, + ); - rtpReceiver.addEventListener('error', (event: any) => - { + rtpReceiver.addEventListener('error', (event: any) => { logger.error('rtpReceiver "error" event [event:%o]', event); }); // NOTE: Convert our standard RTCRtpParameters into those that Edge // expects. - const edgeRtpParameters = - edgeUtils.mangleRtpParameters(rtpParameters); + const edgeRtpParameters = edgeUtils.mangleRtpParameters(rtpParameters); logger.debug( 'receive() | calling rtpReceiver.receive() [params:%o]', - edgeRtpParameters); + edgeRtpParameters, + ); await rtpReceiver.receive(edgeRtpParameters); @@ -445,37 +412,31 @@ export class Edge11 extends HandlerInterface results.push({ localId, - track : rtpReceiver.track, - rtpReceiver + track: rtpReceiver.track, + rtpReceiver, }); } return results; } - async stopReceiving(localIds: string[]): Promise - { - for (const localId of localIds) - { + async stopReceiving(localIds: string[]): Promise { + for (const localId of localIds) { logger.debug('stopReceiving() [localId:%s]', localId); const rtpReceiver = this._rtpReceivers.get(localId); - if (!rtpReceiver) - { + if (!rtpReceiver) { throw new Error('RTCRtpReceiver not found'); } this._rtpReceivers.delete(localId); - try - { + try { logger.debug('stopReceiving() | calling rtpReceiver.stop()'); (rtpReceiver as any).stop(); - } - catch (error) - { + } catch (error) { logger.warn('stopReceiving() | rtpReceiver.stop() failed:%o', error); } } @@ -483,24 +444,22 @@ export class Edge11 extends HandlerInterface async pauseReceiving( // eslint-disable-next-line @typescript-eslint/no-unused-vars - localIds: string[]): Promise - { + localIds: string[], + ): Promise { // Unimplemented. } async resumeReceiving( // eslint-disable-next-line @typescript-eslint/no-unused-vars - localIds: string[]): Promise - { + localIds: string[], + ): Promise { // Unimplemented. } - async getReceiverStats(localId: string): Promise - { + async getReceiverStats(localId: string): Promise { const rtpReceiver = this._rtpReceivers.get(localId); - if (!rtpReceiver) - { + if (!rtpReceiver) { throw new Error('RTCRtpReceiver not found'); } @@ -509,84 +468,73 @@ export class Edge11 extends HandlerInterface async receiveDataChannel( // eslint-disable-next-line @typescript-eslint/no-unused-vars - options: HandlerReceiveDataChannelOptions - ): Promise - { + options: HandlerReceiveDataChannelOptions, + ): Promise { throw new UnsupportedError('not implemented'); } - private setIceGatherer( - { iceServers, iceTransportPolicy }: - { iceServers?: any[]; iceTransportPolicy?: RTCIceTransportPolicy } - ): void - { + private setIceGatherer({ + iceServers, + iceTransportPolicy, + }: { + iceServers?: any[]; + iceTransportPolicy?: RTCIceTransportPolicy; + }): void { // @ts-ignore - const iceGatherer = new (RTCIceGatherer as any)( - { - iceServers : iceServers || [], - gatherPolicy : iceTransportPolicy || 'all' - }); + const iceGatherer = new (RTCIceGatherer as any)({ + iceServers: iceServers || [], + gatherPolicy: iceTransportPolicy || 'all', + }); - iceGatherer.addEventListener('error', (event: any) => - { + iceGatherer.addEventListener('error', (event: any) => { logger.error('iceGatherer "error" event [event:%o]', event); }); // NOTE: Not yet implemented by Edge, which starts gathering automatically. - try - { + try { iceGatherer.gather(); - } - catch (error) - { + } catch (error) { logger.debug( 'setIceGatherer() | iceGatherer.gather() failed: %s', - (error as Error).toString()); + (error as Error).toString(), + ); } this._iceGatherer = iceGatherer; } - private setIceTransport(): void - { + private setIceTransport(): void { const iceTransport = new (RTCIceTransport as any)(this._iceGatherer); // NOTE: Not yet implemented by Edge. - iceTransport.addEventListener('statechange', () => - { - switch (iceTransport.state) - { - case 'checking': - { + iceTransport.addEventListener('statechange', () => { + switch (iceTransport.state) { + case 'checking': { this.emit('@connectionstatechange', 'connecting'); break; } case 'connected': - case 'completed': - { + case 'completed': { this.emit('@connectionstatechange', 'connected'); break; } - case 'failed': - { + case 'failed': { this.emit('@connectionstatechange', 'failed'); break; } - case 'disconnected': - { + case 'disconnected': { this.emit('@connectionstatechange', 'disconnected'); break; } - case 'closed': - { + case 'closed': { this.emit('@connectionstatechange', 'closed'); break; @@ -595,41 +543,34 @@ export class Edge11 extends HandlerInterface }); // NOTE: Not standard, but implemented by Edge. - iceTransport.addEventListener('icestatechange', () => - { - switch (iceTransport.state) - { - case 'checking': - { + iceTransport.addEventListener('icestatechange', () => { + switch (iceTransport.state) { + case 'checking': { this.emit('@connectionstatechange', 'connecting'); break; } case 'connected': - case 'completed': - { + case 'completed': { this.emit('@connectionstatechange', 'connected'); break; } - case 'failed': - { + case 'failed': { this.emit('@connectionstatechange', 'failed'); break; } - case 'disconnected': - { + case 'disconnected': { this.emit('@connectionstatechange', 'disconnected'); break; } - case 'closed': - { + case 'closed': { this.emit('@connectionstatechange', 'closed'); break; @@ -637,51 +578,51 @@ export class Edge11 extends HandlerInterface } }); - iceTransport.addEventListener('candidatepairchange', (event: any) => - { + iceTransport.addEventListener('candidatepairchange', (event: any) => { logger.debug( - 'iceTransport "candidatepairchange" event [pair:%o]', event.pair); + 'iceTransport "candidatepairchange" event [pair:%o]', + event.pair, + ); }); this._iceTransport = iceTransport; } - private setDtlsTransport(): void - { + private setDtlsTransport(): void { const dtlsTransport = new (RTCDtlsTransport as any)(this._iceTransport); // NOTE: Not yet implemented by Edge. - dtlsTransport.addEventListener('statechange', () => - { + dtlsTransport.addEventListener('statechange', () => { logger.debug( - 'dtlsTransport "statechange" event [state:%s]', dtlsTransport.state); + 'dtlsTransport "statechange" event [state:%s]', + dtlsTransport.state, + ); }); // NOTE: Not standard, but implemented by Edge. - dtlsTransport.addEventListener('dtlsstatechange', () => - { + dtlsTransport.addEventListener('dtlsstatechange', () => { logger.debug( - 'dtlsTransport "dtlsstatechange" event [state:%s]', dtlsTransport.state); + 'dtlsTransport "dtlsstatechange" event [state:%s]', + dtlsTransport.state, + ); - if (dtlsTransport.state === 'closed') - { + if (dtlsTransport.state === 'closed') { this.emit('@connectionstatechange', 'closed'); } }); - dtlsTransport.addEventListener('error', (event: any) => - { + dtlsTransport.addEventListener('error', (event: any) => { logger.error('dtlsTransport "error" event [event:%o]', event); }); this._dtlsTransport = dtlsTransport; } - private async setupTransport( - { localDtlsRole }: - { localDtlsRole: DtlsRole } - ): Promise - { + private async setupTransport({ + localDtlsRole, + }: { + localDtlsRole: DtlsRole; + }): Promise { logger.debug('setupTransport()'); // Get our local DTLS parameters. @@ -690,23 +631,19 @@ export class Edge11 extends HandlerInterface dtlsParameters.role = localDtlsRole; // Need to tell the remote transport about our parameters. - await new Promise((resolve, reject) => - { - this.safeEmit( - '@connect', - { dtlsParameters }, - resolve, - reject - ); + await new Promise((resolve, reject) => { + this.safeEmit('@connect', { dtlsParameters }, resolve, reject); }); // Start the RTCIceTransport. this._iceTransport.start( - this._iceGatherer, this._remoteIceParameters, 'controlling'); + this._iceGatherer, + this._remoteIceParameters, + 'controlling', + ); // Add remote ICE candidates. - for (const candidate of this._remoteIceCandidates!) - { + for (const candidate of this._remoteIceCandidates!) { this._iceTransport.addRemoteCandidate(candidate); } @@ -717,9 +654,8 @@ export class Edge11 extends HandlerInterface this._iceTransport.addRemoteCandidate({}); // NOTE: Edge does not like SHA less than 256. - this._remoteDtlsParameters!.fingerprints = this._remoteDtlsParameters!.fingerprints - .filter((fingerprint: any) => - { + this._remoteDtlsParameters!.fingerprints = + this._remoteDtlsParameters!.fingerprints.filter((fingerprint: any) => { return ( fingerprint.algorithm === 'sha-256' || fingerprint.algorithm === 'sha-384' || diff --git a/src/handlers/FakeHandler.ts b/src/handlers/FakeHandler.ts index f9b20cb8..44f20d12 100644 --- a/src/handlers/FakeHandler.ts +++ b/src/handlers/FakeHandler.ts @@ -14,22 +14,21 @@ import { HandlerSendDataChannelOptions, HandlerSendDataChannelResult, HandlerReceiveDataChannelOptions, - HandlerReceiveDataChannelResult + HandlerReceiveDataChannelResult, } from './HandlerInterface'; import { IceParameters, DtlsParameters, DtlsRole, IceGatheringState, - ConnectionState + ConnectionState, } from '../Transport'; import { RtpCapabilities, RtpParameters } from '../RtpParameters'; import { SctpCapabilities } from '../SctpParameters'; const logger = new Logger('FakeHandler'); -class FakeDataChannel extends EnhancedEventEmitter -{ +class FakeDataChannel extends EnhancedEventEmitter { id?: number; ordered?: boolean; maxPacketLifeTime?: number; @@ -37,23 +36,21 @@ class FakeDataChannel extends EnhancedEventEmitter label?: string; protocol?: string; - constructor( - { - id, - ordered, - maxPacketLifeTime, - maxRetransmits, - label, - protocol - }: { - id: number; - ordered?: boolean; - maxPacketLifeTime?: number; - maxRetransmits?: number; - label?: string; - protocol?: string; - }) - { + constructor({ + id, + ordered, + maxPacketLifeTime, + maxRetransmits, + label, + protocol, + }: { + id: number; + ordered?: boolean; + maxPacketLifeTime?: number; + maxRetransmits?: number; + label?: string; + protocol?: string; + }) { super(); this.id = id; @@ -64,32 +61,27 @@ class FakeDataChannel extends EnhancedEventEmitter this.protocol = protocol; } - close(): void - { + close(): void { this.safeEmit('close'); this.emit('@close'); } - send(data: any): void - { + send(data: any): void { this.safeEmit('message', data); } - addEventListener(event: string, fn: () => void): void - { + addEventListener(event: string, fn: () => void): void { this.on(event, fn); } } -export type FakeParameters = -{ +export type FakeParameters = { generateNativeRtpCapabilities: () => RtpCapabilities; generateNativeSctpCapabilities: () => SctpCapabilities; generateLocalDtlsParameters: () => DtlsParameters; }; -export class FakeHandler extends HandlerInterface -{ +export class FakeHandler extends HandlerInterface { // Closed flag. private _closed = false; // Fake parameters source of RTP and SCTP parameters and capabilities. @@ -110,29 +102,24 @@ export class FakeHandler extends HandlerInterface /** * Creates a factory function. */ - static createFactory(fakeParameters: FakeParameters) - { + static createFactory(fakeParameters: FakeParameters) { return (): FakeHandler => new FakeHandler(fakeParameters); } - constructor(fakeParameters: any) - { + constructor(fakeParameters: any) { super(); this.fakeParameters = fakeParameters; } - get name(): string - { + get name(): string { return 'FakeHandler'; } - close(): void - { + close(): void { logger.debug('close()'); - if (this._closed) - { + if (this._closed) { return; } @@ -140,78 +127,67 @@ export class FakeHandler extends HandlerInterface } // NOTE: Custom method for simulation purposes. - setIceGatheringState(iceGatheringState: IceGatheringState): void - { + setIceGatheringState(iceGatheringState: IceGatheringState): void { this.emit('@icegatheringstatechange', iceGatheringState); } // NOTE: Custom method for simulation purposes. - setConnectionState(connectionState: ConnectionState): void - { + setConnectionState(connectionState: ConnectionState): void { this.emit('@connectionstatechange', connectionState); } - async getNativeRtpCapabilities(): Promise - { + async getNativeRtpCapabilities(): Promise { logger.debug('getNativeRtpCapabilities()'); return this.fakeParameters.generateNativeRtpCapabilities(); } - async getNativeSctpCapabilities(): Promise - { + async getNativeSctpCapabilities(): Promise { logger.debug('getNativeSctpCapabilities()'); return this.fakeParameters.generateNativeSctpCapabilities(); } - run( - { - /* eslint-disable @typescript-eslint/no-unused-vars */ - direction, - iceParameters, - iceCandidates, - dtlsParameters, - sctpParameters, - iceServers, - iceTransportPolicy, - proprietaryConstraints, - extendedRtpCapabilities - /* eslint-enable @typescript-eslint/no-unused-vars */ - }: HandlerRunOptions - ): void - { + run({ + /* eslint-disable @typescript-eslint/no-unused-vars */ + direction, + iceParameters, + iceCandidates, + dtlsParameters, + sctpParameters, + iceServers, + iceTransportPolicy, + proprietaryConstraints, + extendedRtpCapabilities, + /* eslint-enable @typescript-eslint/no-unused-vars */ + }: HandlerRunOptions): void { this.assertNotClosed(); logger.debug('run()'); // Generic sending RTP parameters for audio and video. // @type {Object} - this._rtpParametersByKind = - { - audio : ortc.getSendingRtpParameters('audio', extendedRtpCapabilities), - video : ortc.getSendingRtpParameters('video', extendedRtpCapabilities) + this._rtpParametersByKind = { + audio: ortc.getSendingRtpParameters('audio', extendedRtpCapabilities), + video: ortc.getSendingRtpParameters('video', extendedRtpCapabilities), }; } // eslint-disable-next-line @typescript-eslint/no-unused-vars - async updateIceServers(iceServers: RTCIceServer[]): Promise - { + async updateIceServers(iceServers: RTCIceServer[]): Promise { this.assertNotClosed(); logger.debug('updateIceServers()'); } // eslint-disable-next-line @typescript-eslint/no-unused-vars - async restartIce(iceParameters: IceParameters): Promise - { + async restartIce(iceParameters: IceParameters): Promise { this.assertNotClosed(); logger.debug('restartIce()'); } - async getTransportStats(): Promise - { + async getTransportStats(): Promise { this.assertNotClosed(); return new Map(); // NOTE: Whatever. @@ -219,36 +195,33 @@ export class FakeHandler extends HandlerInterface async send( // eslint-disable-next-line @typescript-eslint/no-unused-vars - { track, encodings, codecOptions, codec }: HandlerSendOptions - ): Promise - { + { track, encodings, codecOptions, codec }: HandlerSendOptions, + ): Promise { this.assertNotClosed(); logger.debug('send() [kind:%s, track.id:%s]', track.kind, track.id); - if (!this._transportReady) - { + if (!this._transportReady) { await this.setupTransport({ localDtlsRole: 'server' }); } - const rtpParameters = - utils.clone(this._rtpParametersByKind![track.kind]); - const useRtx = rtpParameters.codecs - .some((_codec: any) => /.+\/rtx$/i.test(_codec.mimeType)); + const rtpParameters = utils.clone( + this._rtpParametersByKind![track.kind], + ); + const useRtx = rtpParameters.codecs.some((_codec: any) => + /.+\/rtx$/i.test(_codec.mimeType), + ); rtpParameters.mid = `mid-${utils.generateRandomNumber()}`; - if (!encodings) - { - encodings = [ {} ]; + if (!encodings) { + encodings = [{}]; } - for (const encoding of encodings) - { + for (const encoding of encodings) { encoding.ssrc = utils.generateRandomNumber(); - if (useRtx) - { + if (useRtx) { encoding.rtx = { ssrc: utils.generateRandomNumber() }; } } @@ -256,11 +229,10 @@ export class FakeHandler extends HandlerInterface rtpParameters.encodings = encodings; // Fill RTCRtpParameters.rtcp. - rtpParameters.rtcp = - { - cname : this._cname, - reducedSize : true, - mux : true + rtpParameters.rtcp = { + cname: this._cname, + reducedSize: true, + mux: true, }; const localId = this._nextLocalId++; @@ -270,17 +242,14 @@ export class FakeHandler extends HandlerInterface return { localId: String(localId), rtpParameters }; } - async stopSending(localId: string): Promise - { + async stopSending(localId: string): Promise { logger.debug('stopSending() [localId:%s]', localId); - if (this._closed) - { + if (this._closed) { return; } - if (!this._tracks.has(Number(localId))) - { + if (!this._tracks.has(Number(localId))) { throw new Error('local track not found'); } @@ -288,34 +257,32 @@ export class FakeHandler extends HandlerInterface } // eslint-disable-next-line @typescript-eslint/no-unused-vars - async pauseSending(localId: string): Promise - { + async pauseSending(localId: string): Promise { this.assertNotClosed(); // Unimplemented. } // eslint-disable-next-line @typescript-eslint/no-unused-vars - async resumeSending(localId: string): Promise - { + async resumeSending(localId: string): Promise { this.assertNotClosed(); // Unimplemented. } async replaceTrack( - localId: string, track: MediaStreamTrack | null - ): Promise - { + localId: string, + track: MediaStreamTrack | null, + ): Promise { this.assertNotClosed(); - if (track) - { + if (track) { logger.debug( - 'replaceTrack() [localId:%s, track.id:%s]', localId, track.id); - } - else - { + 'replaceTrack() [localId:%s, track.id:%s]', + localId, + track.id, + ); + } else { logger.debug('replaceTrack() [localId:%s, no track]', localId); } @@ -323,67 +290,65 @@ export class FakeHandler extends HandlerInterface this._tracks.set(Number(localId), track); } - async setMaxSpatialLayer(localId: string, spatialLayer: number): Promise - { + async setMaxSpatialLayer( + localId: string, + spatialLayer: number, + ): Promise { this.assertNotClosed(); logger.debug( 'setMaxSpatialLayer() [localId:%s, spatialLayer:%s]', - localId, spatialLayer); + localId, + spatialLayer, + ); } - async setRtpEncodingParameters(localId: string, params: any): Promise - { + async setRtpEncodingParameters(localId: string, params: any): Promise { this.assertNotClosed(); logger.debug( 'setRtpEncodingParameters() [localId:%s, params:%o]', - localId, params); + localId, + params, + ); } // eslint-disable-next-line @typescript-eslint/no-unused-vars - async getSenderStats(localId: string): Promise - { + async getSenderStats(localId: string): Promise { this.assertNotClosed(); return new Map(); // NOTE: Whatever. } - async sendDataChannel( - { - ordered, - maxPacketLifeTime, - maxRetransmits, - label, - protocol - }: HandlerSendDataChannelOptions - ): Promise - { + async sendDataChannel({ + ordered, + maxPacketLifeTime, + maxRetransmits, + label, + protocol, + }: HandlerSendDataChannelOptions): Promise { this.assertNotClosed(); - if (!this._transportReady) - { + if (!this._transportReady) { await this.setupTransport({ localDtlsRole: 'server' }); } logger.debug('sendDataChannel()'); - const dataChannel = new FakeDataChannel( - { - id : this._nextSctpStreamId++, - ordered, - maxPacketLifeTime, - maxRetransmits, - label, - protocol - }); - - const sctpStreamParameters = - { - streamId : this._nextSctpStreamId, - ordered : ordered, - maxPacketLifeTime : maxPacketLifeTime, - maxRetransmits : maxRetransmits + const dataChannel = new FakeDataChannel({ + id: this._nextSctpStreamId++, + ordered, + maxPacketLifeTime, + maxRetransmits, + label, + protocol, + }); + + const sctpStreamParameters = { + streamId: this._nextSctpStreamId, + ordered: ordered, + maxPacketLifeTime: maxPacketLifeTime, + maxRetransmits: maxRetransmits, }; // @ts-ignore. @@ -391,19 +356,16 @@ export class FakeHandler extends HandlerInterface } async receive( - optionsList: HandlerReceiveOptions[] - ) : Promise - { + optionsList: HandlerReceiveOptions[], + ): Promise { this.assertNotClosed(); const results: HandlerReceiveResult[] = []; - for (const options of optionsList) - { + for (const options of optionsList) { const { trackId, kind } = options; - if (!this._transportReady) - { + if (!this._transportReady) { await this.setupTransport({ localDtlsRole: 'client' }); } @@ -420,15 +382,12 @@ export class FakeHandler extends HandlerInterface return results; } - async stopReceiving(localIds: string[]): Promise - { - if (this._closed) - { + async stopReceiving(localIds: string[]): Promise { + if (this._closed) { return; } - for (const localId of localIds) - { + for (const localId of localIds) { logger.debug('stopReceiving() [localId:%s]', localId); this._tracks.delete(Number(localId)); @@ -437,8 +396,8 @@ export class FakeHandler extends HandlerInterface async pauseReceiving( // eslint-disable-next-line @typescript-eslint/no-unused-vars - localIds: string[]): Promise - { + localIds: string[], + ): Promise { this.assertNotClosed(); // Unimplemented. @@ -446,66 +405,60 @@ export class FakeHandler extends HandlerInterface async resumeReceiving( // eslint-disable-next-line @typescript-eslint/no-unused-vars - localIds: string[]): Promise - { + localIds: string[], + ): Promise { this.assertNotClosed(); // Unimplemented. } // eslint-disable-next-line @typescript-eslint/no-unused-vars - async getReceiverStats(localId: string): Promise - { + async getReceiverStats(localId: string): Promise { this.assertNotClosed(); return new Map(); // } - async receiveDataChannel( - { sctpStreamParameters, label, protocol }: HandlerReceiveDataChannelOptions - ): Promise - { + async receiveDataChannel({ + sctpStreamParameters, + label, + protocol, + }: HandlerReceiveDataChannelOptions): Promise { this.assertNotClosed(); - if (!this._transportReady) - { + if (!this._transportReady) { await this.setupTransport({ localDtlsRole: 'client' }); } logger.debug('receiveDataChannel()'); - const dataChannel = new FakeDataChannel( - { - id : sctpStreamParameters.streamId!, - ordered : sctpStreamParameters.ordered, - maxPacketLifeTime : sctpStreamParameters.maxPacketLifeTime, - maxRetransmits : sctpStreamParameters.maxRetransmits, - label, - protocol - }); + const dataChannel = new FakeDataChannel({ + id: sctpStreamParameters.streamId!, + ordered: sctpStreamParameters.ordered, + maxPacketLifeTime: sctpStreamParameters.maxPacketLifeTime, + maxRetransmits: sctpStreamParameters.maxRetransmits, + label, + protocol, + }); // @ts-ignore. return { dataChannel }; } - private async setupTransport( - { - localDtlsRole, - // eslint-disable-next-line @typescript-eslint/no-unused-vars - localSdpObject - }: - { - localDtlsRole: DtlsRole; - localSdpObject?: any; - } - ): Promise - { - const dtlsParameters = - utils.clone(this.fakeParameters.generateLocalDtlsParameters()); + private async setupTransport({ + localDtlsRole, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + localSdpObject, + }: { + localDtlsRole: DtlsRole; + localSdpObject?: any; + }): Promise { + const dtlsParameters = utils.clone( + this.fakeParameters.generateLocalDtlsParameters(), + ); // Set our DTLS role. - if (localDtlsRole) - { + if (localDtlsRole) { dtlsParameters.role = localDtlsRole; } @@ -513,17 +466,15 @@ export class FakeHandler extends HandlerInterface this.emit('@connectionstatechange', 'connecting'); // Need to tell the remote transport about our parameters. - await new Promise((resolve, reject) => ( - this.emit('@connect', { dtlsParameters }, resolve, reject) - )); + await new Promise((resolve, reject) => + this.emit('@connect', { dtlsParameters }, resolve, reject), + ); this._transportReady = true; } - private assertNotClosed(): void - { - if (this._closed) - { + private assertNotClosed(): void { + if (this._closed) { throw new InvalidStateError('method called in a closed handler'); } } diff --git a/src/handlers/Firefox60.ts b/src/handlers/Firefox60.ts index 7a93e77a..84c6de26 100644 --- a/src/handlers/Firefox60.ts +++ b/src/handlers/Firefox60.ts @@ -16,7 +16,7 @@ import { HandlerSendDataChannelOptions, HandlerSendDataChannelResult, HandlerReceiveDataChannelOptions, - HandlerReceiveDataChannelResult + HandlerReceiveDataChannelResult, } from './HandlerInterface'; import { RemoteSdp } from './sdp/RemoteSdp'; import { parse as parseScalabilityMode } from '../scalabilityModes'; @@ -24,7 +24,7 @@ import { IceParameters, DtlsRole } from '../Transport'; import { RtpCapabilities, RtpParameters, - RtpEncodingParameters + RtpEncodingParameters, } from '../RtpParameters'; import { SctpCapabilities, SctpStreamParameters } from '../SctpParameters'; @@ -32,8 +32,7 @@ const logger = new Logger('Firefox60'); const SCTP_NUM_STREAMS = { OS: 16, MIS: 2048 }; -export class Firefox60 extends HandlerInterface -{ +export class Firefox60 extends HandlerInterface { // Closed flag. private _closed = false; // Handler direction. @@ -62,53 +61,46 @@ export class Firefox60 extends HandlerInterface /** * Creates a factory function. */ - static createFactory(): HandlerFactory - { + static createFactory(): HandlerFactory { return (): Firefox60 => new Firefox60(); } - constructor() - { + constructor() { super(); } - get name(): string - { + get name(): string { return 'Firefox60'; } - close(): void - { + close(): void { logger.debug('close()'); - if (this._closed) - { + if (this._closed) { return; } this._closed = true; // Close RTCPeerConnection. - if (this._pc) - { - try { this._pc.close(); } - catch (error) {} + if (this._pc) { + try { + this._pc.close(); + } catch (error) {} } this.emit('@close'); } - async getNativeRtpCapabilities(): Promise - { + async getNativeRtpCapabilities(): Promise { logger.debug('getNativeRtpCapabilities()'); - const pc = new (RTCPeerConnection as any)( - { - iceServers : [], - iceTransportPolicy : 'all', - bundlePolicy : 'max-bundle', - rtcpMuxPolicy : 'require' - }); + const pc = new (RTCPeerConnection as any)({ + iceServers: [], + iceTransportPolicy: 'all', + bundlePolicy: 'max-bundle', + rtcpMuxPolicy: 'require', + }); // NOTE: We need to add a real video track to get the RID extension mapping. const canvas = document.createElement('canvas'); @@ -119,17 +111,16 @@ export class Firefox60 extends HandlerInterface const fakeStream = (canvas as any).captureStream(); const fakeVideoTrack = fakeStream.getVideoTracks()[0]; - try - { + try { pc.addTransceiver('audio', { direction: 'sendrecv' }); - const videoTransceiver = - pc.addTransceiver(fakeVideoTrack, { direction: 'sendrecv' }); + const videoTransceiver = pc.addTransceiver(fakeVideoTrack, { + direction: 'sendrecv', + }); const parameters = videoTransceiver.sender.getParameters(); - const encodings = - [ + const encodings = [ { rid: 'r0', maxBitrate: 100000 }, - { rid: 'r1', maxBitrate: 500000 } + { rid: 'r1', maxBitrate: 500000 }, ]; parameters.encodings = encodings; @@ -137,148 +128,142 @@ export class Firefox60 extends HandlerInterface const offer = await pc.createOffer(); - try { canvas.remove(); } - catch (error) {} + try { + canvas.remove(); + } catch (error) {} - try { fakeVideoTrack.stop(); } - catch (error) {} + try { + fakeVideoTrack.stop(); + } catch (error) {} - try { pc.close(); } - catch (error) {} + try { + pc.close(); + } catch (error) {} const sdpObject = sdpTransform.parse(offer.sdp); - const nativeRtpCapabilities = - sdpCommonUtils.extractRtpCapabilities({ sdpObject }); + const nativeRtpCapabilities = sdpCommonUtils.extractRtpCapabilities({ + sdpObject, + }); return nativeRtpCapabilities; - } - catch (error) - { - try { canvas.remove(); } - catch (error2) {} + } catch (error) { + try { + canvas.remove(); + } catch (error2) {} - try { fakeVideoTrack.stop(); } - catch (error2) {} + try { + fakeVideoTrack.stop(); + } catch (error2) {} - try { pc.close(); } - catch (error2) {} + try { + pc.close(); + } catch (error2) {} throw error; } } - async getNativeSctpCapabilities(): Promise - { + async getNativeSctpCapabilities(): Promise { logger.debug('getNativeSctpCapabilities()'); return { - numStreams : SCTP_NUM_STREAMS + numStreams: SCTP_NUM_STREAMS, }; } - run( - { - direction, - iceParameters, - iceCandidates, - dtlsParameters, - sctpParameters, - iceServers, - iceTransportPolicy, - additionalSettings, - proprietaryConstraints, - extendedRtpCapabilities - }: HandlerRunOptions - ): void - { + run({ + direction, + iceParameters, + iceCandidates, + dtlsParameters, + sctpParameters, + iceServers, + iceTransportPolicy, + additionalSettings, + proprietaryConstraints, + extendedRtpCapabilities, + }: HandlerRunOptions): void { this.assertNotClosed(); logger.debug('run()'); this._direction = direction; - this._remoteSdp = new RemoteSdp( - { - iceParameters, - iceCandidates, - dtlsParameters, - sctpParameters - }); + this._remoteSdp = new RemoteSdp({ + iceParameters, + iceCandidates, + dtlsParameters, + sctpParameters, + }); - this._sendingRtpParametersByKind = - { - audio : ortc.getSendingRtpParameters('audio', extendedRtpCapabilities), - video : ortc.getSendingRtpParameters('video', extendedRtpCapabilities) + this._sendingRtpParametersByKind = { + audio: ortc.getSendingRtpParameters('audio', extendedRtpCapabilities), + video: ortc.getSendingRtpParameters('video', extendedRtpCapabilities), }; - this._sendingRemoteRtpParametersByKind = - { - audio : ortc.getSendingRemoteRtpParameters('audio', extendedRtpCapabilities), - video : ortc.getSendingRemoteRtpParameters('video', extendedRtpCapabilities) + this._sendingRemoteRtpParametersByKind = { + audio: ortc.getSendingRemoteRtpParameters( + 'audio', + extendedRtpCapabilities, + ), + video: ortc.getSendingRemoteRtpParameters( + 'video', + extendedRtpCapabilities, + ), }; this._pc = new (RTCPeerConnection as any)( { - iceServers : iceServers || [], - iceTransportPolicy : iceTransportPolicy || 'all', - bundlePolicy : 'max-bundle', - rtcpMuxPolicy : 'require', - ...additionalSettings + iceServers: iceServers || [], + iceTransportPolicy: iceTransportPolicy || 'all', + bundlePolicy: 'max-bundle', + rtcpMuxPolicy: 'require', + ...additionalSettings, }, - proprietaryConstraints); + proprietaryConstraints, + ); - this._pc.addEventListener('icegatheringstatechange', () => - { + this._pc.addEventListener('icegatheringstatechange', () => { this.emit('@icegatheringstatechange', this._pc.iceGatheringState); }); - if (this._pc.connectionState) - { - this._pc.addEventListener('connectionstatechange', () => - { + if (this._pc.connectionState) { + this._pc.addEventListener('connectionstatechange', () => { this.emit('@connectionstatechange', this._pc.connectionState); }); - } - else - { - this._pc.addEventListener('iceconnectionstatechange', () => - { + } else { + this._pc.addEventListener('iceconnectionstatechange', () => { logger.warn( - 'run() | pc.connectionState not supported, using pc.iceConnectionState'); + 'run() | pc.connectionState not supported, using pc.iceConnectionState', + ); - switch (this._pc.iceConnectionState) - { - case 'checking': - { + switch (this._pc.iceConnectionState) { + case 'checking': { this.emit('@connectionstatechange', 'connecting'); break; } case 'connected': - case 'completed': - { + case 'completed': { this.emit('@connectionstatechange', 'connected'); break; } - case 'failed': - { + case 'failed': { this.emit('@connectionstatechange', 'failed'); break; } - case 'disconnected': - { + case 'disconnected': { this.emit('@connectionstatechange', 'disconnected'); break; } - case 'closed': - { + case 'closed': { this.emit('@connectionstatechange', 'closed'); break; @@ -289,16 +274,14 @@ export class Firefox60 extends HandlerInterface } // eslint-disable-next-line @typescript-eslint/no-unused-vars - async updateIceServers(iceServers: RTCIceServer[]): Promise - { + async updateIceServers(iceServers: RTCIceServer[]): Promise { this.assertNotClosed(); // NOTE: Firefox does not implement pc.setConfiguration(). throw new UnsupportedError('not supported'); } - async restartIce(iceParameters: IceParameters): Promise - { + async restartIce(iceParameters: IceParameters): Promise { this.assertNotClosed(); logger.debug('restartIce()'); @@ -306,18 +289,17 @@ export class Firefox60 extends HandlerInterface // Provide the remote SDP handler with new remote ICE parameters. this._remoteSdp!.updateIceParameters(iceParameters); - if (!this._transportReady) - { + if (!this._transportReady) { return; } - if (this._direction === 'send') - { + if (this._direction === 'send') { const offer = await this._pc.createOffer({ iceRestart: true }); logger.debug( 'restartIce() | calling pc.setLocalDescription() [offer:%o]', - offer); + offer, + ); await this._pc.setLocalDescription(offer); @@ -325,17 +307,17 @@ export class Firefox60 extends HandlerInterface logger.debug( 'restartIce() | calling pc.setRemoteDescription() [answer:%o]', - answer); + answer, + ); await this._pc.setRemoteDescription(answer); - } - else - { + } else { const offer = { type: 'offer', sdp: this._remoteSdp!.getSdp() }; logger.debug( 'restartIce() | calling pc.setRemoteDescription() [offer:%o]', - offer); + offer, + ); await this._pc.setRemoteDescription(offer); @@ -343,36 +325,35 @@ export class Firefox60 extends HandlerInterface logger.debug( 'restartIce() | calling pc.setLocalDescription() [answer:%o]', - answer); + answer, + ); await this._pc.setLocalDescription(answer); } } - async getTransportStats(): Promise - { + async getTransportStats(): Promise { this.assertNotClosed(); return this._pc.getStats(); } - async send( - { track, encodings, codecOptions, codec }: HandlerSendOptions - ): Promise - { + async send({ + track, + encodings, + codecOptions, + codec, + }: HandlerSendOptions): Promise { this.assertNotClosed(); this.assertSendDirection(); logger.debug('send() [kind:%s, track.id:%s]', track.kind, track.id); - if (encodings) - { + if (encodings) { encodings = utils.clone(encodings); - if (encodings!.length > 1) - { - encodings!.forEach((encoding, idx) => - { + if (encodings!.length > 1) { + encodings!.forEach((encoding, idx) => { encoding.rid = `r${idx}`; }); @@ -382,32 +363,39 @@ export class Firefox60 extends HandlerInterface } } - const sendingRtpParameters = - utils.clone(this._sendingRtpParametersByKind![track.kind]); + const sendingRtpParameters = utils.clone( + this._sendingRtpParametersByKind![track.kind], + ); // This may throw. - sendingRtpParameters.codecs = - ortc.reduceCodecs(sendingRtpParameters.codecs, codec); + sendingRtpParameters.codecs = ortc.reduceCodecs( + sendingRtpParameters.codecs, + codec, + ); - const sendingRemoteRtpParameters = - utils.clone(this._sendingRemoteRtpParametersByKind![track.kind]); + const sendingRemoteRtpParameters = utils.clone( + this._sendingRemoteRtpParametersByKind![track.kind], + ); // This may throw. - sendingRemoteRtpParameters.codecs = - ortc.reduceCodecs(sendingRemoteRtpParameters.codecs, codec); + sendingRemoteRtpParameters.codecs = ortc.reduceCodecs( + sendingRemoteRtpParameters.codecs, + codec, + ); // NOTE: Firefox fails sometimes to properly anticipate the closed media // section that it should use, so don't reuse closed media sections. // https://github.com/versatica/mediasoup-client/issues/104 // // const mediaSectionIdx = this._remoteSdp!.getNextMediaSectionIdx(); - const transceiver = this._pc.addTransceiver( - track, { direction: 'sendonly', streams: [ this._sendStream ] }); + const transceiver = this._pc.addTransceiver(track, { + direction: 'sendonly', + streams: [this._sendStream], + }); // NOTE: This is not spec compliants. Encodings should be given in addTransceiver // second argument, but Firefox does not support it. - if (encodings) - { + if (encodings) { const parameters = transceiver.sender.getParameters(); parameters.encodings = encodings; @@ -419,17 +407,13 @@ export class Firefox60 extends HandlerInterface // In Firefox use DTLS role client even if we are the "offerer" since // Firefox does not respect ICE-Lite. - if (!this._transportReady) - { + if (!this._transportReady) { await this.setupTransport({ localDtlsRole: 'client', localSdpObject }); } - const layers = - parseScalabilityMode((encodings || [ {} ])[0].scalabilityMode); + const layers = parseScalabilityMode((encodings || [{}])[0].scalabilityMode); - logger.debug( - 'send() | calling pc.setLocalDescription() [offer:%o]', - offer); + logger.debug('send() | calling pc.setLocalDescription() [offer:%o]', offer); await this._pc.setLocalDescription(offer); @@ -441,24 +425,26 @@ export class Firefox60 extends HandlerInterface localSdpObject = sdpTransform.parse(this._pc.localDescription.sdp); - const offerMediaObject = localSdpObject.media[localSdpObject.media.length - 1]; + const offerMediaObject = + localSdpObject.media[localSdpObject.media.length - 1]; // Set RTCP CNAME. - sendingRtpParameters.rtcp!.cname = - sdpCommonUtils.getCname({ offerMediaObject }); + sendingRtpParameters.rtcp!.cname = sdpCommonUtils.getCname({ + offerMediaObject, + }); // Set RTP encodings by parsing the SDP offer if no encodings are given. - if (!encodings) - { - sendingRtpParameters.encodings = - sdpUnifiedPlanUtils.getRtpEncodings({ offerMediaObject }); + if (!encodings) { + sendingRtpParameters.encodings = sdpUnifiedPlanUtils.getRtpEncodings({ + offerMediaObject, + }); } // Set RTP encodings by parsing the SDP offer and complete them with given // one if just a single encoding has been given. - else if (encodings.length === 1) - { - const newEncodings = - sdpUnifiedPlanUtils.getRtpEncodings({ offerMediaObject }); + else if (encodings.length === 1) { + const newEncodings = sdpUnifiedPlanUtils.getRtpEncodings({ + offerMediaObject, + }); Object.assign(newEncodings[0], encodings[0]); @@ -466,8 +452,7 @@ export class Firefox60 extends HandlerInterface } // Otherwise if more than 1 encoding are given use them verbatim (but // reverse them back since we reversed them above to satisfy Firefox). - else - { + else { sendingRtpParameters.encodings = encodings.reverse(); } @@ -475,39 +460,32 @@ export class Firefox60 extends HandlerInterface // each encoding. if ( sendingRtpParameters.encodings.length > 1 && - ( - sendingRtpParameters.codecs[0].mimeType.toLowerCase() === 'video/vp8' || - sendingRtpParameters.codecs[0].mimeType.toLowerCase() === 'video/h264' - ) - ) - { - for (const encoding of sendingRtpParameters.encodings) - { - if (encoding.scalabilityMode) - { + (sendingRtpParameters.codecs[0].mimeType.toLowerCase() === 'video/vp8' || + sendingRtpParameters.codecs[0].mimeType.toLowerCase() === 'video/h264') + ) { + for (const encoding of sendingRtpParameters.encodings) { + if (encoding.scalabilityMode) { encoding.scalabilityMode = `L1T${layers.temporalLayers}`; - } - else - { + } else { encoding.scalabilityMode = 'L1T3'; } } } - this._remoteSdp!.send( - { - offerMediaObject, - offerRtpParameters : sendingRtpParameters, - answerRtpParameters : sendingRemoteRtpParameters, - codecOptions, - extmapAllowMixed : true - }); + this._remoteSdp!.send({ + offerMediaObject, + offerRtpParameters: sendingRtpParameters, + answerRtpParameters: sendingRemoteRtpParameters, + codecOptions, + extmapAllowMixed: true, + }); const answer = { type: 'answer', sdp: this._remoteSdp!.getSdp() }; logger.debug( 'send() | calling pc.setRemoteDescription() [answer:%o]', - answer); + answer, + ); await this._pc.setRemoteDescription(answer); @@ -516,26 +494,23 @@ export class Firefox60 extends HandlerInterface return { localId, - rtpParameters : sendingRtpParameters, - rtpSender : transceiver.sender + rtpParameters: sendingRtpParameters, + rtpSender: transceiver.sender, }; } - async stopSending(localId: string): Promise - { + async stopSending(localId: string): Promise { this.assertSendDirection(); logger.debug('stopSending() [localId:%s]', localId); - if (this._closed) - { + if (this._closed) { return; } const transceiver = this._mapMidTransceiver.get(localId); - if (!transceiver) - { + if (!transceiver) { throw new Error('associated transceiver not found'); } @@ -560,7 +535,8 @@ export class Firefox60 extends HandlerInterface logger.debug( 'stopSending() | calling pc.setLocalDescription() [offer:%o]', - offer); + offer, + ); await this._pc.setLocalDescription(offer); @@ -568,7 +544,8 @@ export class Firefox60 extends HandlerInterface logger.debug( 'stopSending() | calling pc.setRemoteDescription() [answer:%o]', - answer); + answer, + ); await this._pc.setRemoteDescription(answer); @@ -576,8 +553,7 @@ export class Firefox60 extends HandlerInterface } // eslint-disable-next-line @typescript-eslint/no-unused-vars - async pauseSending(localId: string): Promise - { + async pauseSending(localId: string): Promise { this.assertNotClosed(); this.assertSendDirection(); @@ -585,8 +561,7 @@ export class Firefox60 extends HandlerInterface const transceiver = this._mapMidTransceiver.get(localId); - if (!transceiver) - { + if (!transceiver) { throw new Error('associated RTCRtpTransceiver not found'); } @@ -597,7 +572,8 @@ export class Firefox60 extends HandlerInterface logger.debug( 'pauseSending() | calling pc.setLocalDescription() [offer:%o]', - offer); + offer, + ); await this._pc.setLocalDescription(offer); @@ -605,14 +581,14 @@ export class Firefox60 extends HandlerInterface logger.debug( 'pauseSending() | calling pc.setRemoteDescription() [answer:%o]', - answer); + answer, + ); await this._pc.setRemoteDescription(answer); } // eslint-disable-next-line @typescript-eslint/no-unused-vars - async resumeSending(localId: string): Promise - { + async resumeSending(localId: string): Promise { this.assertNotClosed(); this.assertSendDirection(); @@ -620,8 +596,7 @@ export class Firefox60 extends HandlerInterface const transceiver = this._mapMidTransceiver.get(localId); - if (!transceiver) - { + if (!transceiver) { throw new Error('associated RTCRtpTransceiver not found'); } @@ -632,7 +607,8 @@ export class Firefox60 extends HandlerInterface logger.debug( 'resumeSending() | calling pc.setLocalDescription() [offer:%o]', - offer); + offer, + ); await this._pc.setLocalDescription(offer); @@ -640,51 +616,54 @@ export class Firefox60 extends HandlerInterface logger.debug( 'resumeSending() | calling pc.setRemoteDescription() [answer:%o]', - answer); + answer, + ); await this._pc.setRemoteDescription(answer); } async replaceTrack( - localId: string, track: MediaStreamTrack | null - ): Promise - { + localId: string, + track: MediaStreamTrack | null, + ): Promise { this.assertNotClosed(); this.assertSendDirection(); - if (track) - { + if (track) { logger.debug( - 'replaceTrack() [localId:%s, track.id:%s]', localId, track.id); - } - else - { + 'replaceTrack() [localId:%s, track.id:%s]', + localId, + track.id, + ); + } else { logger.debug('replaceTrack() [localId:%s, no track]', localId); } const transceiver = this._mapMidTransceiver.get(localId); - if (!transceiver) - { + if (!transceiver) { throw new Error('associated RTCRtpTransceiver not found'); } await transceiver.sender.replaceTrack(track); } - async setMaxSpatialLayer(localId: string, spatialLayer: number): Promise - { + async setMaxSpatialLayer( + localId: string, + spatialLayer: number, + ): Promise { this.assertNotClosed(); this.assertSendDirection(); logger.debug( 'setMaxSpatialLayer() [localId:%s, spatialLayer:%s]', - localId, spatialLayer); + localId, + spatialLayer, + ); const transceiver = this._mapMidTransceiver.get(localId); - if (!transceiver) - { + if (!transceiver) { throw new Error('associated transceiver not found'); } @@ -694,17 +673,15 @@ export class Firefox60 extends HandlerInterface // requires them in reverse order, so do magic here. spatialLayer = parameters.encodings.length - 1 - spatialLayer; - parameters.encodings.forEach((encoding: RTCRtpEncodingParameters, idx: number) => - { - if (idx >= spatialLayer) - { - encoding.active = true; - } - else - { - encoding.active = false; - } - }); + parameters.encodings.forEach( + (encoding: RTCRtpEncodingParameters, idx: number) => { + if (idx >= spatialLayer) { + encoding.active = true; + } else { + encoding.active = false; + } + }, + ); await transceiver.sender.setParameters(parameters); @@ -714,7 +691,8 @@ export class Firefox60 extends HandlerInterface logger.debug( 'setMaxSpatialLayer() | calling pc.setLocalDescription() [offer:%o]', - offer); + offer, + ); await this._pc.setLocalDescription(offer); @@ -722,33 +700,35 @@ export class Firefox60 extends HandlerInterface logger.debug( 'setMaxSpatialLayer() | calling pc.setRemoteDescription() [answer:%o]', - answer); + answer, + ); await this._pc.setRemoteDescription(answer); } - async setRtpEncodingParameters(localId: string, params: any): Promise - { + async setRtpEncodingParameters(localId: string, params: any): Promise { this.assertNotClosed(); this.assertSendDirection(); logger.debug( 'setRtpEncodingParameters() [localId:%s, params:%o]', - localId, params); + localId, + params, + ); const transceiver = this._mapMidTransceiver.get(localId); - if (!transceiver) - { + if (!transceiver) { throw new Error('associated RTCRtpTransceiver not found'); } const parameters = transceiver.sender.getParameters(); - parameters.encodings.forEach((encoding: RTCRtpEncodingParameters, idx: number) => - { - parameters.encodings[idx] = { ...encoding, ...params }; - }); + parameters.encodings.forEach( + (encoding: RTCRtpEncodingParameters, idx: number) => { + parameters.encodings[idx] = { ...encoding, ...params }; + }, + ); await transceiver.sender.setParameters(parameters); @@ -758,7 +738,8 @@ export class Firefox60 extends HandlerInterface logger.debug( 'setRtpEncodingParameters() | calling pc.setLocalDescription() [offer:%o]', - offer); + offer, + ); await this._pc.setLocalDescription(offer); @@ -766,47 +747,42 @@ export class Firefox60 extends HandlerInterface logger.debug( 'setRtpEncodingParameters() | calling pc.setRemoteDescription() [answer:%o]', - answer); + answer, + ); await this._pc.setRemoteDescription(answer); } - async getSenderStats(localId: string): Promise - { + async getSenderStats(localId: string): Promise { this.assertNotClosed(); this.assertSendDirection(); const transceiver = this._mapMidTransceiver.get(localId); - if (!transceiver) - { + if (!transceiver) { throw new Error('associated RTCRtpTransceiver not found'); } return transceiver.sender.getStats(); } - async sendDataChannel( - { - ordered, - maxPacketLifeTime, - maxRetransmits, - label, - protocol - }: HandlerSendDataChannelOptions - ): Promise - { + async sendDataChannel({ + ordered, + maxPacketLifeTime, + maxRetransmits, + label, + protocol, + }: HandlerSendDataChannelOptions): Promise { this.assertNotClosed(); this.assertSendDirection(); - const options = - { - negotiated : true, - id : this._nextSendSctpStreamId, + const options = { + negotiated: true, + id: this._nextSendSctpStreamId, ordered, maxPacketLifeTime, maxRetransmits, - protocol + protocol, }; logger.debug('sendDataChannel() [options:%o]', options); @@ -819,21 +795,21 @@ export class Firefox60 extends HandlerInterface // If this is the first DataChannel we need to create the SDP answer with // m=application section. - if (!this._hasDataChannelMediaSection) - { + if (!this._hasDataChannelMediaSection) { const offer = await this._pc.createOffer(); const localSdpObject = sdpTransform.parse(offer.sdp); - const offerMediaObject = localSdpObject.media - .find((m: any) => m.type === 'application'); + const offerMediaObject = localSdpObject.media.find( + (m: any) => m.type === 'application', + ); - if (!this._transportReady) - { + if (!this._transportReady) { await this.setupTransport({ localDtlsRole: 'client', localSdpObject }); } logger.debug( 'sendDataChannel() | calling pc.setLocalDescription() [offer:%o]', - offer); + offer, + ); await this._pc.setLocalDescription(offer); @@ -843,19 +819,19 @@ export class Firefox60 extends HandlerInterface logger.debug( 'sendDataChannel() | calling pc.setRemoteDescription() [answer:%o]', - answer); + answer, + ); await this._pc.setRemoteDescription(answer); this._hasDataChannelMediaSection = true; } - const sctpStreamParameters: SctpStreamParameters = - { - streamId : options.id, - ordered : options.ordered, - maxPacketLifeTime : options.maxPacketLifeTime, - maxRetransmits : options.maxRetransmits + const sctpStreamParameters: SctpStreamParameters = { + streamId: options.id, + ordered: options.ordered, + maxPacketLifeTime: options.maxPacketLifeTime, + maxRetransmits: options.maxRetransmits, }; return { dataChannel, sctpStreamParameters }; @@ -863,17 +839,15 @@ export class Firefox60 extends HandlerInterface async receive( // eslint-disable-next-line @typescript-eslint/no-unused-vars - optionsList: HandlerReceiveOptions[] - ) : Promise - { + optionsList: HandlerReceiveOptions[], + ): Promise { this.assertNotClosed(); this.assertRecvDirection(); const results: HandlerReceiveResult[] = []; const mapLocalId: Map = new Map(); - for (const options of optionsList) - { + for (const options of optionsList) { const { trackId, kind, rtpParameters, streamId } = options; logger.debug('receive() [trackId:%s, kind:%s]', trackId, kind); @@ -882,65 +856,63 @@ export class Firefox60 extends HandlerInterface mapLocalId.set(trackId, localId); - this._remoteSdp!.receive( - { - mid : localId, - kind, - offerRtpParameters : rtpParameters, - streamId : streamId || rtpParameters.rtcp!.cname!, - trackId - }); + this._remoteSdp!.receive({ + mid: localId, + kind, + offerRtpParameters: rtpParameters, + streamId: streamId || rtpParameters.rtcp!.cname!, + trackId, + }); } const offer = { type: 'offer', sdp: this._remoteSdp!.getSdp() }; logger.debug( 'receive() | calling pc.setRemoteDescription() [offer:%o]', - offer); + offer, + ); await this._pc.setRemoteDescription(offer); let answer = await this._pc.createAnswer(); const localSdpObject = sdpTransform.parse(answer.sdp); - for (const options of optionsList) - { + for (const options of optionsList) { const { trackId, rtpParameters } = options; const localId = mapLocalId.get(trackId); - const answerMediaObject = localSdpObject.media - .find((m: any) => String(m.mid) === localId); + const answerMediaObject = localSdpObject.media.find( + (m: any) => String(m.mid) === localId, + ); // May need to modify codec parameters in the answer based on codec // parameters in the offer. - sdpCommonUtils.applyCodecParameters( - { - offerRtpParameters : rtpParameters, - answerMediaObject - }); + sdpCommonUtils.applyCodecParameters({ + offerRtpParameters: rtpParameters, + answerMediaObject, + }); answer = { type: 'answer', sdp: sdpTransform.write(localSdpObject) }; } - if (!this._transportReady) - { + if (!this._transportReady) { await this.setupTransport({ localDtlsRole: 'client', localSdpObject }); } logger.debug( 'receive() | calling pc.setLocalDescription() [answer:%o]', - answer); + answer, + ); await this._pc.setLocalDescription(answer); - for (const options of optionsList) - { + for (const options of optionsList) { const { trackId } = options; const localId = mapLocalId.get(trackId)!; - const transceiver = this._pc.getTransceivers() + const transceiver = this._pc + .getTransceivers() .find((t: RTCRtpTransceiver) => t.mid === localId); - if (!transceiver) - { + if (!transceiver) { throw new Error('new RTCRtpTransceiver not found'); } @@ -949,31 +921,27 @@ export class Firefox60 extends HandlerInterface results.push({ localId, - track : transceiver.receiver.track, - rtpReceiver : transceiver.receiver + track: transceiver.receiver.track, + rtpReceiver: transceiver.receiver, }); } return results; } - async stopReceiving(localIds: string[]): Promise - { + async stopReceiving(localIds: string[]): Promise { this.assertRecvDirection(); - if (this._closed) - { + if (this._closed) { return; } - for (const localId of localIds) - { + for (const localId of localIds) { logger.debug('stopReceiving() [localId:%s]', localId); const transceiver = this._mapMidTransceiver.get(localId); - if (!transceiver) - { + if (!transceiver) { throw new Error('associated RTCRtpTransceiver not found'); } @@ -984,7 +952,8 @@ export class Firefox60 extends HandlerInterface logger.debug( 'stopReceiving() | calling pc.setRemoteDescription() [offer:%o]', - offer); + offer, + ); await this._pc.setRemoteDescription(offer); @@ -992,29 +961,26 @@ export class Firefox60 extends HandlerInterface logger.debug( 'stopReceiving() | calling pc.setLocalDescription() [answer:%o]', - answer); + answer, + ); await this._pc.setLocalDescription(answer); - for (const localId of localIds) - { + for (const localId of localIds) { this._mapMidTransceiver.delete(localId); } } - async pauseReceiving(localIds: string[]): Promise - { + async pauseReceiving(localIds: string[]): Promise { this.assertNotClosed(); this.assertRecvDirection(); - for (const localId of localIds) - { + for (const localId of localIds) { logger.debug('pauseReceiving() [localId:%s]', localId); const transceiver = this._mapMidTransceiver.get(localId); - if (!transceiver) - { + if (!transceiver) { throw new Error('associated RTCRtpTransceiver not found'); } @@ -1026,7 +992,8 @@ export class Firefox60 extends HandlerInterface logger.debug( 'pauseReceiving() | calling pc.setRemoteDescription() [offer:%o]', - offer); + offer, + ); await this._pc.setRemoteDescription(offer); @@ -1034,24 +1001,22 @@ export class Firefox60 extends HandlerInterface logger.debug( 'pauseReceiving() | calling pc.setLocalDescription() [answer:%o]', - answer); + answer, + ); await this._pc.setLocalDescription(answer); } - async resumeReceiving(localIds: string[]): Promise - { + async resumeReceiving(localIds: string[]): Promise { this.assertNotClosed(); this.assertRecvDirection(); - for (const localId of localIds) - { + for (const localId of localIds) { logger.debug('resumeReceiving() [localId:%s]', localId); const transceiver = this._mapMidTransceiver.get(localId); - if (!transceiver) - { + if (!transceiver) { throw new Error('associated RTCRtpTransceiver not found'); } @@ -1063,7 +1028,8 @@ export class Firefox60 extends HandlerInterface logger.debug( 'resumeReceiving() | calling pc.setRemoteDescription() [offer:%o]', - offer); + offer, + ); await this._pc.setRemoteDescription(offer); @@ -1071,29 +1037,29 @@ export class Firefox60 extends HandlerInterface logger.debug( 'resumeReceiving() | calling pc.setLocalDescription() [answer:%o]', - answer); + answer, + ); await this._pc.setLocalDescription(answer); } - async getReceiverStats(localId: string): Promise - { + async getReceiverStats(localId: string): Promise { this.assertRecvDirection(); const transceiver = this._mapMidTransceiver.get(localId); - if (!transceiver) - { + if (!transceiver) { throw new Error('associated RTCRtpTransceiver not found'); } return transceiver.receiver.getStats(); } - async receiveDataChannel( - { sctpStreamParameters, label, protocol }: HandlerReceiveDataChannelOptions - ): Promise - { + async receiveDataChannel({ + sctpStreamParameters, + label, + protocol, + }: HandlerReceiveDataChannelOptions): Promise { this.assertNotClosed(); this.assertRecvDirection(); @@ -1101,17 +1067,16 @@ export class Firefox60 extends HandlerInterface streamId, ordered, maxPacketLifeTime, - maxRetransmits + maxRetransmits, }: SctpStreamParameters = sctpStreamParameters; - const options = - { - negotiated : true, - id : streamId, + const options = { + negotiated: true, + id: streamId, ordered, maxPacketLifeTime, maxRetransmits, - protocol + protocol, }; logger.debug('receiveDataChannel() [options:%o]', options); @@ -1120,22 +1085,21 @@ export class Firefox60 extends HandlerInterface // If this is the first DataChannel we need to create the SDP offer with // m=application section. - if (!this._hasDataChannelMediaSection) - { + if (!this._hasDataChannelMediaSection) { this._remoteSdp!.receiveSctpAssociation(); const offer = { type: 'offer', sdp: this._remoteSdp!.getSdp() }; logger.debug( 'receiveDataChannel() | calling pc.setRemoteDescription() [offer:%o]', - offer); + offer, + ); await this._pc.setRemoteDescription(offer); const answer = await this._pc.createAnswer(); - if (!this._transportReady) - { + if (!this._transportReady) { const localSdpObject = sdpTransform.parse(answer.sdp); await this.setupTransport({ localDtlsRole: 'client', localSdpObject }); @@ -1143,7 +1107,8 @@ export class Firefox60 extends HandlerInterface logger.debug( 'receiveDataChannel() | calling pc.setRemoteDescription() [answer:%o]', - answer); + answer, + ); await this._pc.setLocalDescription(answer); @@ -1153,70 +1118,57 @@ export class Firefox60 extends HandlerInterface return { dataChannel }; } - private async setupTransport( - { - localDtlsRole, - localSdpObject - }: - { - localDtlsRole: DtlsRole; - localSdpObject?: any; - } - ): Promise - { - if (!localSdpObject) - { + private async setupTransport({ + localDtlsRole, + localSdpObject, + }: { + localDtlsRole: DtlsRole; + localSdpObject?: any; + }): Promise { + if (!localSdpObject) { localSdpObject = sdpTransform.parse(this._pc.localDescription.sdp); } // Get our local DTLS parameters. - const dtlsParameters = - sdpCommonUtils.extractDtlsParameters({ sdpObject: localSdpObject }); + const dtlsParameters = sdpCommonUtils.extractDtlsParameters({ + sdpObject: localSdpObject, + }); // Set our DTLS role. dtlsParameters.role = localDtlsRole; // Update the remote DTLS role in the SDP. this._remoteSdp!.updateDtlsRole( - localDtlsRole === 'client' ? 'server' : 'client'); + localDtlsRole === 'client' ? 'server' : 'client', + ); // Need to tell the remote transport about our parameters. - await new Promise((resolve, reject) => - { - this.safeEmit( - '@connect', - { dtlsParameters }, - resolve, - reject - ); + await new Promise((resolve, reject) => { + this.safeEmit('@connect', { dtlsParameters }, resolve, reject); }); this._transportReady = true; } - private assertNotClosed(): void - { - if (this._closed) - { + private assertNotClosed(): void { + if (this._closed) { throw new InvalidStateError('method called in a closed handler'); } } - private assertSendDirection(): void - { - if (this._direction !== 'send') - { + private assertSendDirection(): void { + if (this._direction !== 'send') { throw new Error( - 'method can just be called for handlers with "send" direction'); + 'method can just be called for handlers with "send" direction', + ); } } - private assertRecvDirection(): void - { - if (this._direction !== 'recv') - { + private assertRecvDirection(): void { + if (this._direction !== 'recv') { throw new Error( - 'method can just be called for handlers with "recv" direction'); + 'method can just be called for handlers with "recv" direction', + ); } } } diff --git a/src/handlers/HandlerInterface.ts b/src/handlers/HandlerInterface.ts index 21b9710f..15e48b6d 100644 --- a/src/handlers/HandlerInterface.ts +++ b/src/handlers/HandlerInterface.ts @@ -5,24 +5,23 @@ import { IceCandidate, DtlsParameters, IceGatheringState, - ConnectionState + ConnectionState, } from '../Transport'; import { RtpCapabilities, RtpCodecCapability, RtpParameters, - RtpEncodingParameters + RtpEncodingParameters, } from '../RtpParameters'; import { SctpCapabilities, SctpParameters, - SctpStreamParameters + SctpStreamParameters, } from '../SctpParameters'; export type HandlerFactory = () => HandlerInterface; -export type HandlerRunOptions = -{ +export type HandlerRunOptions = { direction: 'send' | 'recv'; iceParameters: IceParameters; iceCandidates: IceCandidate[]; @@ -35,23 +34,20 @@ export type HandlerRunOptions = extendedRtpCapabilities: any; }; -export type HandlerSendOptions = -{ +export type HandlerSendOptions = { track: MediaStreamTrack; encodings?: RtpEncodingParameters[]; codecOptions?: ProducerCodecOptions; codec?: RtpCodecCapability; }; -export type HandlerSendResult = -{ +export type HandlerSendResult = { localId: string; rtpParameters: RtpParameters; rtpSender?: RTCRtpSender; }; -export type HandlerReceiveOptions = -{ +export type HandlerReceiveOptions = { trackId: string; kind: 'audio' | 'video'; rtpParameters: RtpParameters; @@ -64,8 +60,7 @@ export type HandlerReceiveOptions = streamId?: string; }; -export type HandlerReceiveResult = -{ +export type HandlerReceiveResult = { localId: string; track: MediaStreamTrack; rtpReceiver?: RTCRtpReceiver; @@ -73,41 +68,34 @@ export type HandlerReceiveResult = export type HandlerSendDataChannelOptions = SctpStreamParameters; -export type HandlerSendDataChannelResult = -{ +export type HandlerSendDataChannelResult = { dataChannel: RTCDataChannel; sctpStreamParameters: SctpStreamParameters; }; -export type HandlerReceiveDataChannelOptions = -{ +export type HandlerReceiveDataChannelOptions = { sctpStreamParameters: SctpStreamParameters; label?: string; protocol?: string; }; -export type HandlerReceiveDataChannelResult = -{ +export type HandlerReceiveDataChannelResult = { dataChannel: RTCDataChannel; }; -export type HandlerEvents = -{ +export type HandlerEvents = { '@close': []; - '@connect': - [ + '@connect': [ { dtlsParameters: DtlsParameters }, () => void, - (error: Error) => void + (error: Error) => void, ]; '@icegatheringstatechange': [IceGatheringState]; '@connectionstatechange': [ConnectionState]; }; -export abstract class HandlerInterface extends EnhancedEventEmitter -{ - constructor() - { +export abstract class HandlerInterface extends EnhancedEventEmitter { + constructor() { super(); } @@ -136,26 +124,29 @@ export abstract class HandlerInterface extends EnhancedEventEmitter; abstract replaceTrack( - localId: string, track: MediaStreamTrack | null + localId: string, + track: MediaStreamTrack | null, ): Promise; abstract setMaxSpatialLayer( - localId: string, spatialLayer: number + localId: string, + spatialLayer: number, ): Promise; abstract setRtpEncodingParameters( - localId: string, params: any + localId: string, + params: any, ): Promise; abstract getSenderStats(localId: string): Promise; abstract sendDataChannel( - options: HandlerSendDataChannelOptions + options: HandlerSendDataChannelOptions, ): Promise; abstract receive( - optionsList: HandlerReceiveOptions[] - ) : Promise; + optionsList: HandlerReceiveOptions[], + ): Promise; abstract stopReceiving(localIds: string[]): Promise; @@ -166,6 +157,6 @@ export abstract class HandlerInterface extends EnhancedEventEmitter; abstract receiveDataChannel( - options: HandlerReceiveDataChannelOptions + options: HandlerReceiveDataChannelOptions, ): Promise; } diff --git a/src/handlers/ReactNative.ts b/src/handlers/ReactNative.ts index e58fd1a7..ff9b8f1f 100644 --- a/src/handlers/ReactNative.ts +++ b/src/handlers/ReactNative.ts @@ -16,7 +16,7 @@ import { HandlerSendDataChannelOptions, HandlerSendDataChannelResult, HandlerReceiveDataChannelOptions, - HandlerReceiveDataChannelResult + HandlerReceiveDataChannelResult, } from './HandlerInterface'; import { RemoteSdp } from './sdp/RemoteSdp'; import { IceParameters, DtlsRole } from '../Transport'; @@ -27,8 +27,7 @@ const logger = new Logger('ReactNative'); const SCTP_NUM_STREAMS = { OS: 1024, MIS: 1024 }; -export class ReactNative extends HandlerInterface -{ +export class ReactNative extends HandlerInterface { // Handler direction. private _direction?: 'send' | 'recv'; // Remote SDP handler. @@ -46,19 +45,19 @@ export class ReactNative extends HandlerInterface // Local stream for sending. private readonly _sendStream = new MediaStream(); // Map of sending MediaStreamTracks indexed by localId. - private readonly _mapSendLocalIdTrack: Map = new Map(); + private readonly _mapSendLocalIdTrack: Map = + new Map(); // Next sending localId. private _nextSendLocalId = 0; // Map of MID, RTP parameters and RTCRtpReceiver indexed by local id. // Value is an Object with mid, rtpParameters and rtpReceiver. - private readonly _mapRecvLocalIdInfo: - Map< - string, - { - mid: string; - rtpParameters: RtpParameters; - } - > = new Map(); + private readonly _mapRecvLocalIdInfo: Map< + string, + { + mid: string; + rtpParameters: RtpParameters; + } + > = new Map(); // Whether a DataChannel m=application section has been created. private _hasDataChannelMediaSection = false; // Sending DataChannel id value counter. Incremented for each new DataChannel. @@ -69,23 +68,19 @@ export class ReactNative extends HandlerInterface /** * Creates a factory function. */ - static createFactory(): HandlerFactory - { + static createFactory(): HandlerFactory { return (): ReactNative => new ReactNative(); } - constructor() - { + constructor() { super(); } - get name(): string - { + get name(): string { return 'ReactNative'; } - close(): void - { + close(): void { logger.debug('close()'); // Free/dispose native MediaStream but DO NOT free/dispose native @@ -94,173 +89,157 @@ export class ReactNative extends HandlerInterface this._sendStream.release(/* releaseTracks */ false); // Close RTCPeerConnection. - if (this._pc) - { - try { this._pc.close(); } - catch (error) {} + if (this._pc) { + try { + this._pc.close(); + } catch (error) {} } this.emit('@close'); } - async getNativeRtpCapabilities(): Promise - { + async getNativeRtpCapabilities(): Promise { logger.debug('getNativeRtpCapabilities()'); - const pc = new (RTCPeerConnection as any)( - { - iceServers : [], - iceTransportPolicy : 'all', - bundlePolicy : 'max-bundle', - rtcpMuxPolicy : 'require', - sdpSemantics : 'plan-b' - }); + const pc = new (RTCPeerConnection as any)({ + iceServers: [], + iceTransportPolicy: 'all', + bundlePolicy: 'max-bundle', + rtcpMuxPolicy: 'require', + sdpSemantics: 'plan-b', + }); - try - { - const offer = await pc.createOffer( - { - offerToReceiveAudio : true, - offerToReceiveVideo : true - }); + try { + const offer = await pc.createOffer({ + offerToReceiveAudio: true, + offerToReceiveVideo: true, + }); - try { pc.close(); } - catch (error) {} + try { + pc.close(); + } catch (error) {} const sdpObject = sdpTransform.parse(offer.sdp); - const nativeRtpCapabilities = - sdpCommonUtils.extractRtpCapabilities({ sdpObject }); + const nativeRtpCapabilities = sdpCommonUtils.extractRtpCapabilities({ + sdpObject, + }); return nativeRtpCapabilities; - } - catch (error) - { - try { pc.close(); } - catch (error2) {} + } catch (error) { + try { + pc.close(); + } catch (error2) {} throw error; } } - async getNativeSctpCapabilities(): Promise - { + async getNativeSctpCapabilities(): Promise { logger.debug('getNativeSctpCapabilities()'); return { - numStreams : SCTP_NUM_STREAMS + numStreams: SCTP_NUM_STREAMS, }; } - run( - { - direction, - iceParameters, - iceCandidates, - dtlsParameters, - sctpParameters, - iceServers, - iceTransportPolicy, - additionalSettings, - proprietaryConstraints, - extendedRtpCapabilities - }: HandlerRunOptions - ): void - { + run({ + direction, + iceParameters, + iceCandidates, + dtlsParameters, + sctpParameters, + iceServers, + iceTransportPolicy, + additionalSettings, + proprietaryConstraints, + extendedRtpCapabilities, + }: HandlerRunOptions): void { logger.debug('run()'); this._direction = direction; - this._remoteSdp = new RemoteSdp( - { - iceParameters, - iceCandidates, - dtlsParameters, - sctpParameters, - planB : true - }); + this._remoteSdp = new RemoteSdp({ + iceParameters, + iceCandidates, + dtlsParameters, + sctpParameters, + planB: true, + }); - this._sendingRtpParametersByKind = - { - audio : ortc.getSendingRtpParameters('audio', extendedRtpCapabilities), - video : ortc.getSendingRtpParameters('video', extendedRtpCapabilities) + this._sendingRtpParametersByKind = { + audio: ortc.getSendingRtpParameters('audio', extendedRtpCapabilities), + video: ortc.getSendingRtpParameters('video', extendedRtpCapabilities), }; - this._sendingRemoteRtpParametersByKind = - { - audio : ortc.getSendingRemoteRtpParameters('audio', extendedRtpCapabilities), - video : ortc.getSendingRemoteRtpParameters('video', extendedRtpCapabilities) + this._sendingRemoteRtpParametersByKind = { + audio: ortc.getSendingRemoteRtpParameters( + 'audio', + extendedRtpCapabilities, + ), + video: ortc.getSendingRemoteRtpParameters( + 'video', + extendedRtpCapabilities, + ), }; - if (dtlsParameters.role && dtlsParameters.role !== 'auto') - { - this._forcedLocalDtlsRole = dtlsParameters.role === 'server' - ? 'client' - : 'server'; + if (dtlsParameters.role && dtlsParameters.role !== 'auto') { + this._forcedLocalDtlsRole = + dtlsParameters.role === 'server' ? 'client' : 'server'; } this._pc = new (RTCPeerConnection as any)( { - iceServers : iceServers || [], - iceTransportPolicy : iceTransportPolicy || 'all', - bundlePolicy : 'max-bundle', - rtcpMuxPolicy : 'require', - sdpSemantics : 'plan-b', - ...additionalSettings + iceServers: iceServers || [], + iceTransportPolicy: iceTransportPolicy || 'all', + bundlePolicy: 'max-bundle', + rtcpMuxPolicy: 'require', + sdpSemantics: 'plan-b', + ...additionalSettings, }, - proprietaryConstraints); + proprietaryConstraints, + ); - this._pc.addEventListener('icegatheringstatechange', () => - { + this._pc.addEventListener('icegatheringstatechange', () => { this.emit('@icegatheringstatechange', this._pc.iceGatheringState); }); - if (this._pc.connectionState) - { - this._pc.addEventListener('connectionstatechange', () => - { + if (this._pc.connectionState) { + this._pc.addEventListener('connectionstatechange', () => { this.emit('@connectionstatechange', this._pc.connectionState); }); - } - else - { - this._pc.addEventListener('iceconnectionstatechange', () => - { + } else { + this._pc.addEventListener('iceconnectionstatechange', () => { logger.warn( - 'run() | pc.connectionState not supported, using pc.iceConnectionState'); + 'run() | pc.connectionState not supported, using pc.iceConnectionState', + ); - switch (this._pc.iceConnectionState) - { - case 'checking': - { + switch (this._pc.iceConnectionState) { + case 'checking': { this.emit('@connectionstatechange', 'connecting'); break; } case 'connected': - case 'completed': - { + case 'completed': { this.emit('@connectionstatechange', 'connected'); break; } - case 'failed': - { + case 'failed': { this.emit('@connectionstatechange', 'failed'); break; } - case 'disconnected': - { + case 'disconnected': { this.emit('@connectionstatechange', 'disconnected'); break; } - case 'closed': - { + case 'closed': { this.emit('@connectionstatechange', 'closed'); break; @@ -270,8 +249,7 @@ export class ReactNative extends HandlerInterface } } - async updateIceServers(iceServers: RTCIceServer[]): Promise - { + async updateIceServers(iceServers: RTCIceServer[]): Promise { logger.debug('updateIceServers()'); const configuration = this._pc.getConfiguration(); @@ -281,25 +259,23 @@ export class ReactNative extends HandlerInterface this._pc.setConfiguration(configuration); } - async restartIce(iceParameters: IceParameters): Promise - { + async restartIce(iceParameters: IceParameters): Promise { logger.debug('restartIce()'); // Provide the remote SDP handler with new remote ICE parameters. this._remoteSdp!.updateIceParameters(iceParameters); - if (!this._transportReady) - { + if (!this._transportReady) { return; } - if (this._direction === 'send') - { + if (this._direction === 'send') { const offer = await this._pc.createOffer({ iceRestart: true }); logger.debug( 'restartIce() | calling pc.setLocalDescription() [offer:%o]', - offer); + offer, + ); await this._pc.setLocalDescription(offer); @@ -307,17 +283,17 @@ export class ReactNative extends HandlerInterface logger.debug( 'restartIce() | calling pc.setRemoteDescription() [answer:%o]', - answer); + answer, + ); await this._pc.setRemoteDescription(answer); - } - else - { + } else { const offer = { type: 'offer', sdp: this._remoteSdp!.getSdp() }; logger.debug( 'restartIce() | calling pc.setRemoteDescription() [offer:%o]', - offer); + offer, + ); await this._pc.setRemoteDescription(offer); @@ -325,30 +301,32 @@ export class ReactNative extends HandlerInterface logger.debug( 'restartIce() | calling pc.setLocalDescription() [answer:%o]', - answer); + answer, + ); await this._pc.setLocalDescription(answer); } } - async getTransportStats(): Promise - { + async getTransportStats(): Promise { return this._pc.getStats(); } - async send( - { track, encodings, codecOptions, codec }: HandlerSendOptions - ): Promise - { + async send({ + track, + encodings, + codecOptions, + codec, + }: HandlerSendOptions): Promise { this.assertSendDirection(); logger.debug('send() [kind:%s, track.id:%s]', track.kind, track.id); - if (codec) - { + if (codec) { logger.warn( 'send() | codec selection is not available in %s handler', - this.name); + this.name, + ); } this._sendStream.addTrack(track); @@ -357,70 +335,70 @@ export class ReactNative extends HandlerInterface let offer = await this._pc.createOffer(); let localSdpObject = sdpTransform.parse(offer.sdp); let offerMediaObject; - const sendingRtpParameters = - utils.clone(this._sendingRtpParametersByKind![track.kind]); - - sendingRtpParameters.codecs = - ortc.reduceCodecs(sendingRtpParameters.codecs); - - const sendingRemoteRtpParameters = - utils.clone(this._sendingRemoteRtpParametersByKind![track.kind]); - - sendingRemoteRtpParameters.codecs = - ortc.reduceCodecs(sendingRemoteRtpParameters.codecs); - - if (!this._transportReady) - { - await this.setupTransport( - { - localDtlsRole : this._forcedLocalDtlsRole ?? 'client', - localSdpObject - }); + const sendingRtpParameters = utils.clone( + this._sendingRtpParametersByKind![track.kind], + ); + + sendingRtpParameters.codecs = ortc.reduceCodecs( + sendingRtpParameters.codecs, + ); + + const sendingRemoteRtpParameters = utils.clone( + this._sendingRemoteRtpParametersByKind![track.kind], + ); + + sendingRemoteRtpParameters.codecs = ortc.reduceCodecs( + sendingRemoteRtpParameters.codecs, + ); + + if (!this._transportReady) { + await this.setupTransport({ + localDtlsRole: this._forcedLocalDtlsRole ?? 'client', + localSdpObject, + }); } - if (track.kind === 'video' && encodings && encodings.length > 1) - { + if (track.kind === 'video' && encodings && encodings.length > 1) { logger.debug('send() | enabling simulcast'); localSdpObject = sdpTransform.parse(offer.sdp); - offerMediaObject = localSdpObject.media - .find((m: any) => m.type === 'video'); - - sdpPlanBUtils.addLegacySimulcast( - { - offerMediaObject, - track, - numStreams : encodings.length - }); + offerMediaObject = localSdpObject.media.find( + (m: any) => m.type === 'video', + ); + + sdpPlanBUtils.addLegacySimulcast({ + offerMediaObject, + track, + numStreams: encodings.length, + }); offer = { type: 'offer', sdp: sdpTransform.write(localSdpObject) }; } - logger.debug( - 'send() | calling pc.setLocalDescription() [offer:%o]', - offer); + logger.debug('send() | calling pc.setLocalDescription() [offer:%o]', offer); await this._pc.setLocalDescription(offer); localSdpObject = sdpTransform.parse(this._pc.localDescription.sdp); - offerMediaObject = localSdpObject.media - .find((m: any) => m.type === track.kind); + offerMediaObject = localSdpObject.media.find( + (m: any) => m.type === track.kind, + ); // Set RTCP CNAME. - sendingRtpParameters.rtcp!.cname = - sdpCommonUtils.getCname({ offerMediaObject }); + sendingRtpParameters.rtcp!.cname = sdpCommonUtils.getCname({ + offerMediaObject, + }); // Set RTP encodings. - sendingRtpParameters.encodings = - sdpPlanBUtils.getRtpEncodings({ offerMediaObject, track }); + sendingRtpParameters.encodings = sdpPlanBUtils.getRtpEncodings({ + offerMediaObject, + track, + }); // Complete encodings with given values. - if (encodings) - { - for (let idx = 0; idx < sendingRtpParameters.encodings.length; ++idx) - { - if (encodings[idx]) - { + if (encodings) { + for (let idx = 0; idx < sendingRtpParameters.encodings.length; ++idx) { + if (encodings[idx]) { Object.assign(sendingRtpParameters.encodings[idx], encodings[idx]); } } @@ -430,31 +408,27 @@ export class ReactNative extends HandlerInterface // each encoding. if ( sendingRtpParameters.encodings.length > 1 && - ( - sendingRtpParameters.codecs[0].mimeType.toLowerCase() === 'video/vp8' || - sendingRtpParameters.codecs[0].mimeType.toLowerCase() === 'video/h264' - ) - ) - { - for (const encoding of sendingRtpParameters.encodings) - { + (sendingRtpParameters.codecs[0].mimeType.toLowerCase() === 'video/vp8' || + sendingRtpParameters.codecs[0].mimeType.toLowerCase() === 'video/h264') + ) { + for (const encoding of sendingRtpParameters.encodings) { encoding.scalabilityMode = 'L1T3'; } } - this._remoteSdp!.send( - { - offerMediaObject, - offerRtpParameters : sendingRtpParameters, - answerRtpParameters : sendingRemoteRtpParameters, - codecOptions - }); + this._remoteSdp!.send({ + offerMediaObject, + offerRtpParameters: sendingRtpParameters, + answerRtpParameters: sendingRemoteRtpParameters, + codecOptions, + }); const answer = { type: 'answer', sdp: this._remoteSdp!.getSdp() }; logger.debug( 'send() | calling pc.setRemoteDescription() [answer:%o]', - answer); + answer, + ); await this._pc.setRemoteDescription(answer); @@ -466,21 +440,19 @@ export class ReactNative extends HandlerInterface this._mapSendLocalIdTrack.set(localId, track); return { - localId : localId, - rtpParameters : sendingRtpParameters + localId: localId, + rtpParameters: sendingRtpParameters, }; } - async stopSending(localId: string): Promise - { + async stopSending(localId: string): Promise { this.assertSendDirection(); logger.debug('stopSending() [localId:%s]', localId); const track = this._mapSendLocalIdTrack.get(localId); - if (!track) - { + if (!track) { throw new Error('track not found'); } @@ -492,21 +464,19 @@ export class ReactNative extends HandlerInterface logger.debug( 'stopSending() | calling pc.setLocalDescription() [offer:%o]', - offer); + offer, + ); - try - { + try { await this._pc.setLocalDescription(offer); - } - catch (error) - { + } catch (error) { // NOTE: If there are no sending tracks, setLocalDescription() will fail with // "Failed to create channels". If so, ignore it. - if (this._sendStream.getTracks().length === 0) - { + if (this._sendStream.getTracks().length === 0) { logger.warn( 'stopSending() | ignoring expected error due no sending tracks: %s', - (error as Error).toString()); + (error as Error).toString(), + ); return; } @@ -514,8 +484,7 @@ export class ReactNative extends HandlerInterface throw error; } - if (this._pc.signalingState === 'stable') - { + if (this._pc.signalingState === 'stable') { return; } @@ -523,70 +492,67 @@ export class ReactNative extends HandlerInterface logger.debug( 'stopSending() | calling pc.setRemoteDescription() [answer:%o]', - answer); + answer, + ); await this._pc.setRemoteDescription(answer); } // eslint-disable-next-line @typescript-eslint/no-unused-vars - async pauseSending(localId: string): Promise - { + async pauseSending(localId: string): Promise { // Unimplemented. } // eslint-disable-next-line @typescript-eslint/no-unused-vars - async resumeSending(localId: string): Promise - { + async resumeSending(localId: string): Promise { // Unimplemented. } async replaceTrack( // eslint-disable-next-line @typescript-eslint/no-unused-vars - localId: string, track: MediaStreamTrack | null - ): Promise - { + localId: string, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + track: MediaStreamTrack | null, + ): Promise { throw new UnsupportedError('not implemented'); } - // eslint-disable-next-line @typescript-eslint/no-unused-vars - async setMaxSpatialLayer(localId: string, spatialLayer: number): Promise - { + async setMaxSpatialLayer( + // eslint-disable-next-line @typescript-eslint/no-unused-vars + localId: string, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + spatialLayer: number, + ): Promise { throw new UnsupportedError('not implemented'); } // eslint-disable-next-line @typescript-eslint/no-unused-vars - async setRtpEncodingParameters(localId: string, params: any): Promise - { + async setRtpEncodingParameters(localId: string, params: any): Promise { throw new UnsupportedError('not implemented'); } // eslint-disable-next-line @typescript-eslint/no-unused-vars - async getSenderStats(localId: string): Promise - { + async getSenderStats(localId: string): Promise { throw new UnsupportedError('not implemented'); } - async sendDataChannel( - { - ordered, - maxPacketLifeTime, - maxRetransmits, - label, - protocol - }: HandlerSendDataChannelOptions - ): Promise - { + async sendDataChannel({ + ordered, + maxPacketLifeTime, + maxRetransmits, + label, + protocol, + }: HandlerSendDataChannelOptions): Promise { this.assertSendDirection(); - const options = - { - negotiated : true, - id : this._nextSendSctpStreamId, + const options = { + negotiated: true, + id: this._nextSendSctpStreamId, ordered, maxPacketLifeTime, - maxRetransmitTime : maxPacketLifeTime, // NOTE: Old spec. + maxRetransmitTime: maxPacketLifeTime, // NOTE: Old spec. maxRetransmits, - protocol + protocol, }; logger.debug('sendDataChannel() [options:%o]', options); @@ -599,25 +565,24 @@ export class ReactNative extends HandlerInterface // If this is the first DataChannel we need to create the SDP answer with // m=application section. - if (!this._hasDataChannelMediaSection) - { + if (!this._hasDataChannelMediaSection) { const offer = await this._pc.createOffer(); const localSdpObject = sdpTransform.parse(offer.sdp); - const offerMediaObject = localSdpObject.media - .find((m: any) => m.type === 'application'); + const offerMediaObject = localSdpObject.media.find( + (m: any) => m.type === 'application', + ); - if (!this._transportReady) - { - await this.setupTransport( - { - localDtlsRole : this._forcedLocalDtlsRole ?? 'client', - localSdpObject - }); + if (!this._transportReady) { + await this.setupTransport({ + localDtlsRole: this._forcedLocalDtlsRole ?? 'client', + localSdpObject, + }); } logger.debug( 'sendDataChannel() | calling pc.setLocalDescription() [offer:%o]', - offer); + offer, + ); await this._pc.setLocalDescription(offer); @@ -627,35 +592,33 @@ export class ReactNative extends HandlerInterface logger.debug( 'sendDataChannel() | calling pc.setRemoteDescription() [answer:%o]', - answer); + answer, + ); await this._pc.setRemoteDescription(answer); this._hasDataChannelMediaSection = true; } - const sctpStreamParameters: SctpStreamParameters = - { - streamId : options.id, - ordered : options.ordered, - maxPacketLifeTime : options.maxPacketLifeTime, - maxRetransmits : options.maxRetransmits + const sctpStreamParameters: SctpStreamParameters = { + streamId: options.id, + ordered: options.ordered, + maxPacketLifeTime: options.maxPacketLifeTime, + maxRetransmits: options.maxRetransmits, }; return { dataChannel, sctpStreamParameters }; } async receive( - optionsList: HandlerReceiveOptions[] - ) : Promise - { + optionsList: HandlerReceiveOptions[], + ): Promise { this.assertRecvDirection(); const results: HandlerReceiveResult[] = []; const mapStreamId: Map = new Map(); - for (const options of optionsList) - { + for (const options of optionsList) { const { trackId, kind, rtpParameters } = options; logger.debug('receive() [trackId:%s, kind:%s]', trackId, kind); @@ -669,78 +632,76 @@ export class ReactNative extends HandlerInterface // to be different. See: // https://github.com/react-native-webrtc/react-native-webrtc/issues/401 logger.debug( - 'receive() | forcing a random remote streamId to avoid well known bug in react-native-webrtc'); + 'receive() | forcing a random remote streamId to avoid well known bug in react-native-webrtc', + ); streamId += `-hack-${utils.generateRandomNumber()}`; mapStreamId.set(trackId, streamId); - this._remoteSdp!.receive( - { - mid, - kind, - offerRtpParameters : rtpParameters, - streamId, - trackId - }); + this._remoteSdp!.receive({ + mid, + kind, + offerRtpParameters: rtpParameters, + streamId, + trackId, + }); } const offer = { type: 'offer', sdp: this._remoteSdp!.getSdp() }; logger.debug( 'receive() | calling pc.setRemoteDescription() [offer:%o]', - offer); + offer, + ); await this._pc.setRemoteDescription(offer); let answer = await this._pc.createAnswer(); const localSdpObject = sdpTransform.parse(answer.sdp); - for (const options of optionsList) - { + for (const options of optionsList) { const { kind, rtpParameters } = options; const mid = kind; - const answerMediaObject = localSdpObject.media - .find((m: any) => String(m.mid) === mid); + const answerMediaObject = localSdpObject.media.find( + (m: any) => String(m.mid) === mid, + ); // May need to modify codec parameters in the answer based on codec // parameters in the offer. - sdpCommonUtils.applyCodecParameters( - { - offerRtpParameters : rtpParameters, - answerMediaObject - }); + sdpCommonUtils.applyCodecParameters({ + offerRtpParameters: rtpParameters, + answerMediaObject, + }); } answer = { type: 'answer', sdp: sdpTransform.write(localSdpObject) }; - if (!this._transportReady) - { - await this.setupTransport( - { - localDtlsRole : this._forcedLocalDtlsRole ?? 'client', - localSdpObject - }); + if (!this._transportReady) { + await this.setupTransport({ + localDtlsRole: this._forcedLocalDtlsRole ?? 'client', + localSdpObject, + }); } logger.debug( 'receive() | calling pc.setLocalDescription() [answer:%o]', - answer); + answer, + ); await this._pc.setLocalDescription(answer); - for (const options of optionsList) - { + for (const options of optionsList) { const { kind, trackId, rtpParameters } = options; const localId = trackId; const mid = kind; const streamId = mapStreamId.get(trackId); - const stream = this._pc.getRemoteStreams() + const stream = this._pc + .getRemoteStreams() .find((s: MediaStream) => s.id === streamId); const track = stream.getTrackById(localId); - if (!track) - { + if (!track) { throw new Error('remote track not found'); } @@ -753,28 +714,30 @@ export class ReactNative extends HandlerInterface return results; } - async stopReceiving(localIds: string[]): Promise - { + async stopReceiving(localIds: string[]): Promise { this.assertRecvDirection(); - for (const localId of localIds) - { + for (const localId of localIds) { logger.debug('stopReceiving() [localId:%s]', localId); - const { mid, rtpParameters } = this._mapRecvLocalIdInfo.get(localId) || {}; + const { mid, rtpParameters } = + this._mapRecvLocalIdInfo.get(localId) || {}; // Remove from the map. this._mapRecvLocalIdInfo.delete(localId); - this._remoteSdp!.planBStopReceiving( - { mid: mid!, offerRtpParameters: rtpParameters! }); + this._remoteSdp!.planBStopReceiving({ + mid: mid!, + offerRtpParameters: rtpParameters!, + }); } const offer = { type: 'offer', sdp: this._remoteSdp!.getSdp() }; logger.debug( 'stopReceiving() | calling pc.setRemoteDescription() [offer:%o]', - offer); + offer, + ); await this._pc.setRemoteDescription(offer); @@ -782,53 +745,49 @@ export class ReactNative extends HandlerInterface logger.debug( 'stopReceiving() | calling pc.setLocalDescription() [answer:%o]', - answer); + answer, + ); await this._pc.setLocalDescription(answer); } async pauseReceiving( // eslint-disable-next-line @typescript-eslint/no-unused-vars - localIds: string[]): Promise - { + localIds: string[], + ): Promise { // Unimplemented. } async resumeReceiving( // eslint-disable-next-line @typescript-eslint/no-unused-vars - localIds: string[]): Promise - { + localIds: string[], + ): Promise { // Unimplemented. } // eslint-disable-next-line @typescript-eslint/no-unused-vars - async getReceiverStats(localId: string): Promise - { + async getReceiverStats(localId: string): Promise { throw new UnsupportedError('not implemented'); } - async receiveDataChannel( - { sctpStreamParameters, label, protocol }: HandlerReceiveDataChannelOptions - ): Promise - { + async receiveDataChannel({ + sctpStreamParameters, + label, + protocol, + }: HandlerReceiveDataChannelOptions): Promise { this.assertRecvDirection(); - const { - streamId, - ordered, - maxPacketLifeTime, - maxRetransmits - } = sctpStreamParameters; + const { streamId, ordered, maxPacketLifeTime, maxRetransmits } = + sctpStreamParameters; - const options = - { - negotiated : true, - id : streamId, + const options = { + negotiated: true, + id: streamId, ordered, maxPacketLifeTime, - maxRetransmitTime : maxPacketLifeTime, // NOTE: Old spec. + maxRetransmitTime: maxPacketLifeTime, // NOTE: Old spec. maxRetransmits, - protocol + protocol, }; logger.debug('receiveDataChannel() [options:%o]', options); @@ -837,34 +796,33 @@ export class ReactNative extends HandlerInterface // If this is the first DataChannel we need to create the SDP offer with // m=application section. - if (!this._hasDataChannelMediaSection) - { + if (!this._hasDataChannelMediaSection) { this._remoteSdp!.receiveSctpAssociation({ oldDataChannelSpec: true }); const offer = { type: 'offer', sdp: this._remoteSdp!.getSdp() }; logger.debug( 'receiveDataChannel() | calling pc.setRemoteDescription() [offer:%o]', - offer); + offer, + ); await this._pc.setRemoteDescription(offer); const answer = await this._pc.createAnswer(); - if (!this._transportReady) - { + if (!this._transportReady) { const localSdpObject = sdpTransform.parse(answer.sdp); - await this.setupTransport( - { - localDtlsRole : this._forcedLocalDtlsRole ?? 'client', - localSdpObject - }); + await this.setupTransport({ + localDtlsRole: this._forcedLocalDtlsRole ?? 'client', + localSdpObject, + }); } logger.debug( 'receiveDataChannel() | calling pc.setRemoteDescription() [answer:%o]', - answer); + answer, + ); await this._pc.setLocalDescription(answer); @@ -874,62 +832,51 @@ export class ReactNative extends HandlerInterface return { dataChannel }; } - private async setupTransport( - { - localDtlsRole, - localSdpObject - }: - { - localDtlsRole: DtlsRole; - localSdpObject?: any; - } - ): Promise - { - if (!localSdpObject) - { + private async setupTransport({ + localDtlsRole, + localSdpObject, + }: { + localDtlsRole: DtlsRole; + localSdpObject?: any; + }): Promise { + if (!localSdpObject) { localSdpObject = sdpTransform.parse(this._pc.localDescription.sdp); } // Get our local DTLS parameters. - const dtlsParameters = - sdpCommonUtils.extractDtlsParameters({ sdpObject: localSdpObject }); + const dtlsParameters = sdpCommonUtils.extractDtlsParameters({ + sdpObject: localSdpObject, + }); // Set our DTLS role. dtlsParameters.role = localDtlsRole; // Update the remote DTLS role in the SDP. this._remoteSdp!.updateDtlsRole( - localDtlsRole === 'client' ? 'server' : 'client'); + localDtlsRole === 'client' ? 'server' : 'client', + ); // Need to tell the remote transport about our parameters. - await new Promise((resolve, reject) => - { - this.safeEmit( - '@connect', - { dtlsParameters }, - resolve, - reject - ); + await new Promise((resolve, reject) => { + this.safeEmit('@connect', { dtlsParameters }, resolve, reject); }); this._transportReady = true; } - private assertSendDirection(): void - { - if (this._direction !== 'send') - { + private assertSendDirection(): void { + if (this._direction !== 'send') { throw new Error( - 'method can just be called for handlers with "send" direction'); + 'method can just be called for handlers with "send" direction', + ); } } - private assertRecvDirection(): void - { - if (this._direction !== 'recv') - { + private assertRecvDirection(): void { + if (this._direction !== 'recv') { throw new Error( - 'method can just be called for handlers with "recv" direction'); + 'method can just be called for handlers with "recv" direction', + ); } } } diff --git a/src/handlers/ReactNativeUnifiedPlan.ts b/src/handlers/ReactNativeUnifiedPlan.ts index 69159da0..779ebe8d 100644 --- a/src/handlers/ReactNativeUnifiedPlan.ts +++ b/src/handlers/ReactNativeUnifiedPlan.ts @@ -17,7 +17,7 @@ import { HandlerSendDataChannelOptions, HandlerSendDataChannelResult, HandlerReceiveDataChannelOptions, - HandlerReceiveDataChannelResult + HandlerReceiveDataChannelResult, } from './HandlerInterface'; import { RemoteSdp } from './sdp/RemoteSdp'; import { parse as parseScalabilityMode } from '../scalabilityModes'; @@ -25,7 +25,7 @@ import { IceParameters, DtlsRole } from '../Transport'; import { RtpCapabilities, RtpParameters, - RtpEncodingParameters + RtpEncodingParameters, } from '../RtpParameters'; import { SctpCapabilities, SctpStreamParameters } from '../SctpParameters'; @@ -33,8 +33,7 @@ const logger = new Logger('ReactNativeUnifiedPlan'); const SCTP_NUM_STREAMS = { OS: 1024, MIS: 1024 }; -export class ReactNativeUnifiedPlan extends HandlerInterface -{ +export class ReactNativeUnifiedPlan extends HandlerInterface { // Closed flag. private _closed = false; // Handler direction. @@ -66,27 +65,22 @@ export class ReactNativeUnifiedPlan extends HandlerInterface /** * Creates a factory function. */ - static createFactory(): HandlerFactory - { + static createFactory(): HandlerFactory { return (): ReactNativeUnifiedPlan => new ReactNativeUnifiedPlan(); } - constructor() - { + constructor() { super(); } - get name(): string - { + get name(): string { return 'ReactNativeUnifiedPlan'; } - close(): void - { + close(): void { logger.debug('close()'); - if (this._closed) - { + if (this._closed) { return; } @@ -98,176 +92,161 @@ export class ReactNativeUnifiedPlan extends HandlerInterface this._sendStream.release(/* releaseTracks */ false); // Close RTCPeerConnection. - if (this._pc) - { - try { this._pc.close(); } - catch (error) {} + if (this._pc) { + try { + this._pc.close(); + } catch (error) {} } this.emit('@close'); } - async getNativeRtpCapabilities(): Promise - { + async getNativeRtpCapabilities(): Promise { logger.debug('getNativeRtpCapabilities()'); - const pc = new (RTCPeerConnection as any)( - { - iceServers : [], - iceTransportPolicy : 'all', - bundlePolicy : 'max-bundle', - rtcpMuxPolicy : 'require', - sdpSemantics : 'unified-plan' - }); + const pc = new (RTCPeerConnection as any)({ + iceServers: [], + iceTransportPolicy: 'all', + bundlePolicy: 'max-bundle', + rtcpMuxPolicy: 'require', + sdpSemantics: 'unified-plan', + }); - try - { + try { pc.addTransceiver('audio'); pc.addTransceiver('video'); const offer = await pc.createOffer(); - try { pc.close(); } - catch (error) {} + try { + pc.close(); + } catch (error) {} const sdpObject = sdpTransform.parse(offer.sdp); - const nativeRtpCapabilities = - sdpCommonUtils.extractRtpCapabilities({ sdpObject }); + const nativeRtpCapabilities = sdpCommonUtils.extractRtpCapabilities({ + sdpObject, + }); // libwebrtc supports NACK for OPUS but doesn't announce it. ortcUtils.addNackSuppportForOpus(nativeRtpCapabilities); return nativeRtpCapabilities; - } - catch (error) - { - try { pc.close(); } - catch (error2) {} + } catch (error) { + try { + pc.close(); + } catch (error2) {} throw error; } } - async getNativeSctpCapabilities(): Promise - { + async getNativeSctpCapabilities(): Promise { logger.debug('getNativeSctpCapabilities()'); return { - numStreams : SCTP_NUM_STREAMS + numStreams: SCTP_NUM_STREAMS, }; } - run( - { - direction, - iceParameters, - iceCandidates, - dtlsParameters, - sctpParameters, - iceServers, - iceTransportPolicy, - additionalSettings, - proprietaryConstraints, - extendedRtpCapabilities - }: HandlerRunOptions - ): void - { + run({ + direction, + iceParameters, + iceCandidates, + dtlsParameters, + sctpParameters, + iceServers, + iceTransportPolicy, + additionalSettings, + proprietaryConstraints, + extendedRtpCapabilities, + }: HandlerRunOptions): void { this.assertNotClosed(); logger.debug('run()'); this._direction = direction; - this._remoteSdp = new RemoteSdp( - { - iceParameters, - iceCandidates, - dtlsParameters, - sctpParameters - }); + this._remoteSdp = new RemoteSdp({ + iceParameters, + iceCandidates, + dtlsParameters, + sctpParameters, + }); - this._sendingRtpParametersByKind = - { - audio : ortc.getSendingRtpParameters('audio', extendedRtpCapabilities), - video : ortc.getSendingRtpParameters('video', extendedRtpCapabilities) + this._sendingRtpParametersByKind = { + audio: ortc.getSendingRtpParameters('audio', extendedRtpCapabilities), + video: ortc.getSendingRtpParameters('video', extendedRtpCapabilities), }; - this._sendingRemoteRtpParametersByKind = - { - audio : ortc.getSendingRemoteRtpParameters('audio', extendedRtpCapabilities), - video : ortc.getSendingRemoteRtpParameters('video', extendedRtpCapabilities) + this._sendingRemoteRtpParametersByKind = { + audio: ortc.getSendingRemoteRtpParameters( + 'audio', + extendedRtpCapabilities, + ), + video: ortc.getSendingRemoteRtpParameters( + 'video', + extendedRtpCapabilities, + ), }; - if (dtlsParameters.role && dtlsParameters.role !== 'auto') - { - this._forcedLocalDtlsRole = dtlsParameters.role === 'server' - ? 'client' - : 'server'; + if (dtlsParameters.role && dtlsParameters.role !== 'auto') { + this._forcedLocalDtlsRole = + dtlsParameters.role === 'server' ? 'client' : 'server'; } this._pc = new (RTCPeerConnection as any)( { - iceServers : iceServers || [], - iceTransportPolicy : iceTransportPolicy || 'all', - bundlePolicy : 'max-bundle', - rtcpMuxPolicy : 'require', - sdpSemantics : 'unified-plan', - ...additionalSettings + iceServers: iceServers || [], + iceTransportPolicy: iceTransportPolicy || 'all', + bundlePolicy: 'max-bundle', + rtcpMuxPolicy: 'require', + sdpSemantics: 'unified-plan', + ...additionalSettings, }, - proprietaryConstraints); + proprietaryConstraints, + ); - this._pc.addEventListener('icegatheringstatechange', () => - { + this._pc.addEventListener('icegatheringstatechange', () => { this.emit('@icegatheringstatechange', this._pc.iceGatheringState); }); - if (this._pc.connectionState) - { - this._pc.addEventListener('connectionstatechange', () => - { + if (this._pc.connectionState) { + this._pc.addEventListener('connectionstatechange', () => { this.emit('@connectionstatechange', this._pc.connectionState); }); - } - else - { - this._pc.addEventListener('iceconnectionstatechange', () => - { + } else { + this._pc.addEventListener('iceconnectionstatechange', () => { logger.warn( - 'run() | pc.connectionState not supported, using pc.iceConnectionState'); + 'run() | pc.connectionState not supported, using pc.iceConnectionState', + ); - switch (this._pc.iceConnectionState) - { - case 'checking': - { + switch (this._pc.iceConnectionState) { + case 'checking': { this.emit('@connectionstatechange', 'connecting'); break; } case 'connected': - case 'completed': - { + case 'completed': { this.emit('@connectionstatechange', 'connected'); break; } - case 'failed': - { + case 'failed': { this.emit('@connectionstatechange', 'failed'); break; } - case 'disconnected': - { + case 'disconnected': { this.emit('@connectionstatechange', 'disconnected'); break; } - case 'closed': - { + case 'closed': { this.emit('@connectionstatechange', 'closed'); break; @@ -277,8 +256,7 @@ export class ReactNativeUnifiedPlan extends HandlerInterface } } - async updateIceServers(iceServers: RTCIceServer[]): Promise - { + async updateIceServers(iceServers: RTCIceServer[]): Promise { this.assertNotClosed(); logger.debug('updateIceServers()'); @@ -290,8 +268,7 @@ export class ReactNativeUnifiedPlan extends HandlerInterface this._pc.setConfiguration(configuration); } - async restartIce(iceParameters: IceParameters): Promise - { + async restartIce(iceParameters: IceParameters): Promise { this.assertNotClosed(); logger.debug('restartIce()'); @@ -299,18 +276,17 @@ export class ReactNativeUnifiedPlan extends HandlerInterface // Provide the remote SDP handler with new remote ICE parameters. this._remoteSdp!.updateIceParameters(iceParameters); - if (!this._transportReady) - { + if (!this._transportReady) { return; } - if (this._direction === 'send') - { + if (this._direction === 'send') { const offer = await this._pc.createOffer({ iceRestart: true }); logger.debug( 'restartIce() | calling pc.setLocalDescription() [offer:%o]', - offer); + offer, + ); await this._pc.setLocalDescription(offer); @@ -318,17 +294,17 @@ export class ReactNativeUnifiedPlan extends HandlerInterface logger.debug( 'restartIce() | calling pc.setRemoteDescription() [answer:%o]', - answer); + answer, + ); await this._pc.setRemoteDescription(answer); - } - else - { + } else { const offer = { type: 'offer', sdp: this._remoteSdp!.getSdp() }; logger.debug( 'restartIce() | calling pc.setRemoteDescription() [offer:%o]', - offer); + offer, + ); await this._pc.setRemoteDescription(offer); @@ -336,102 +312,99 @@ export class ReactNativeUnifiedPlan extends HandlerInterface logger.debug( 'restartIce() | calling pc.setLocalDescription() [answer:%o]', - answer); + answer, + ); await this._pc.setLocalDescription(answer); } } - async getTransportStats(): Promise - { + async getTransportStats(): Promise { this.assertNotClosed(); return this._pc.getStats(); } - async send( - { track, encodings, codecOptions, codec }: HandlerSendOptions - ): Promise - { + async send({ + track, + encodings, + codecOptions, + codec, + }: HandlerSendOptions): Promise { this.assertNotClosed(); this.assertSendDirection(); logger.debug('send() [kind:%s, track.id:%s]', track.kind, track.id); - if (encodings && encodings.length > 1) - { - encodings.forEach((encoding: RtpEncodingParameters, idx: number) => - { + if (encodings && encodings.length > 1) { + encodings.forEach((encoding: RtpEncodingParameters, idx: number) => { encoding.rid = `r${idx}`; }); } - const sendingRtpParameters = - utils.clone(this._sendingRtpParametersByKind![track.kind]); + const sendingRtpParameters = utils.clone( + this._sendingRtpParametersByKind![track.kind], + ); // This may throw. - sendingRtpParameters.codecs = - ortc.reduceCodecs(sendingRtpParameters.codecs, codec); + sendingRtpParameters.codecs = ortc.reduceCodecs( + sendingRtpParameters.codecs, + codec, + ); - const sendingRemoteRtpParameters = - utils.clone(this._sendingRemoteRtpParametersByKind![track.kind]); + const sendingRemoteRtpParameters = utils.clone( + this._sendingRemoteRtpParametersByKind![track.kind], + ); // This may throw. - sendingRemoteRtpParameters.codecs = - ortc.reduceCodecs(sendingRemoteRtpParameters.codecs, codec); + sendingRemoteRtpParameters.codecs = ortc.reduceCodecs( + sendingRemoteRtpParameters.codecs, + codec, + ); const mediaSectionIdx = this._remoteSdp!.getNextMediaSectionIdx(); - const transceiver = this._pc.addTransceiver( - track, - { - direction : 'sendonly', - streams : [ this._sendStream ], - sendEncodings : encodings - }); + const transceiver = this._pc.addTransceiver(track, { + direction: 'sendonly', + streams: [this._sendStream], + sendEncodings: encodings, + }); let offer = await this._pc.createOffer(); let localSdpObject = sdpTransform.parse(offer.sdp); let offerMediaObject; - if (!this._transportReady) - { - await this.setupTransport( - { - localDtlsRole : this._forcedLocalDtlsRole ?? 'client', - localSdpObject - }); + if (!this._transportReady) { + await this.setupTransport({ + localDtlsRole: this._forcedLocalDtlsRole ?? 'client', + localSdpObject, + }); } // Special case for VP9 with SVC. let hackVp9Svc = false; - const layers = - parseScalabilityMode((encodings || [ {} ])[0].scalabilityMode); + const layers = parseScalabilityMode((encodings || [{}])[0].scalabilityMode); if ( encodings && encodings.length === 1 && layers.spatialLayers > 1 && sendingRtpParameters.codecs[0].mimeType.toLowerCase() === 'video/vp9' - ) - { + ) { logger.debug('send() | enabling legacy simulcast for VP9 SVC'); hackVp9Svc = true; localSdpObject = sdpTransform.parse(offer.sdp); offerMediaObject = localSdpObject.media[mediaSectionIdx.idx]; - sdpUnifiedPlanUtils.addLegacySimulcast( - { - offerMediaObject, - numStreams : layers.spatialLayers - }); + sdpUnifiedPlanUtils.addLegacySimulcast({ + offerMediaObject, + numStreams: layers.spatialLayers, + }); offer = { type: 'offer', sdp: sdpTransform.write(localSdpObject) }; } - logger.debug( - 'send() | calling pc.setLocalDescription() [offer:%o]', - offer); + logger.debug('send() | calling pc.setLocalDescription() [offer:%o]', offer); await this._pc.setLocalDescription(offer); @@ -444,10 +417,9 @@ export class ReactNativeUnifiedPlan extends HandlerInterface // NOTE: This is fixed in react-native-webrtc 111.0.3. let localId = transceiver.mid ?? undefined; - if (!localId) - { + if (!localId) { logger.warn( - 'send() | missing transceiver.mid (bug in react-native-webrtc, using a workaround' + 'send() | missing transceiver.mid (bug in react-native-webrtc, using a workaround', ); } @@ -459,35 +431,34 @@ export class ReactNativeUnifiedPlan extends HandlerInterface offerMediaObject = localSdpObject.media[mediaSectionIdx.idx]; // Set RTCP CNAME. - sendingRtpParameters.rtcp!.cname = - sdpCommonUtils.getCname({ offerMediaObject }); + sendingRtpParameters.rtcp!.cname = sdpCommonUtils.getCname({ + offerMediaObject, + }); // Set RTP encodings by parsing the SDP offer if no encodings are given. - if (!encodings) - { - sendingRtpParameters.encodings = - sdpUnifiedPlanUtils.getRtpEncodings({ offerMediaObject }); + if (!encodings) { + sendingRtpParameters.encodings = sdpUnifiedPlanUtils.getRtpEncodings({ + offerMediaObject, + }); } // Set RTP encodings by parsing the SDP offer and complete them with given // one if just a single encoding has been given. - else if (encodings.length === 1) - { - let newEncodings = - sdpUnifiedPlanUtils.getRtpEncodings({ offerMediaObject }); + else if (encodings.length === 1) { + let newEncodings = sdpUnifiedPlanUtils.getRtpEncodings({ + offerMediaObject, + }); Object.assign(newEncodings[0], encodings[0]); // Hack for VP9 SVC. - if (hackVp9Svc) - { - newEncodings = [ newEncodings[0] ]; + if (hackVp9Svc) { + newEncodings = [newEncodings[0]]; } sendingRtpParameters.encodings = newEncodings; } // Otherwise if more than 1 encoding are given use them verbatim. - else - { + else { sendingRtpParameters.encodings = encodings; } @@ -495,40 +466,33 @@ export class ReactNativeUnifiedPlan extends HandlerInterface // each encoding. if ( sendingRtpParameters.encodings.length > 1 && - ( - sendingRtpParameters.codecs[0].mimeType.toLowerCase() === 'video/vp8' || - sendingRtpParameters.codecs[0].mimeType.toLowerCase() === 'video/h264' - ) - ) - { - for (const encoding of sendingRtpParameters.encodings) - { - if (encoding.scalabilityMode) - { + (sendingRtpParameters.codecs[0].mimeType.toLowerCase() === 'video/vp8' || + sendingRtpParameters.codecs[0].mimeType.toLowerCase() === 'video/h264') + ) { + for (const encoding of sendingRtpParameters.encodings) { + if (encoding.scalabilityMode) { encoding.scalabilityMode = `L1T${layers.temporalLayers}`; - } - else - { + } else { encoding.scalabilityMode = 'L1T3'; } } } - this._remoteSdp!.send( - { - offerMediaObject, - reuseMid : mediaSectionIdx.reuseMid, - offerRtpParameters : sendingRtpParameters, - answerRtpParameters : sendingRemoteRtpParameters, - codecOptions, - extmapAllowMixed : true - }); + this._remoteSdp!.send({ + offerMediaObject, + reuseMid: mediaSectionIdx.reuseMid, + offerRtpParameters: sendingRtpParameters, + answerRtpParameters: sendingRemoteRtpParameters, + codecOptions, + extmapAllowMixed: true, + }); const answer = { type: 'answer', sdp: this._remoteSdp!.getSdp() }; logger.debug( 'send() | calling pc.setRemoteDescription() [answer:%o]', - answer); + answer, + ); await this._pc.setRemoteDescription(answer); @@ -536,8 +500,7 @@ export class ReactNativeUnifiedPlan extends HandlerInterface // we can read generated MID (if not done above) and fill sendingRtpParameters. // NOTE: This is fixed in react-native-webrtc 111.0.3 so this block isn't // needed starting from that version. - if (!localId) - { + if (!localId) { localId = transceiver.mid; sendingRtpParameters.mid = localId; } @@ -547,17 +510,15 @@ export class ReactNativeUnifiedPlan extends HandlerInterface return { localId, - rtpParameters : sendingRtpParameters, - rtpSender : transceiver.sender + rtpParameters: sendingRtpParameters, + rtpSender: transceiver.sender, }; } - async stopSending(localId: string): Promise - { + async stopSending(localId: string): Promise { this.assertSendDirection(); - if (this._closed) - { + if (this._closed) { return; } @@ -565,8 +526,7 @@ export class ReactNativeUnifiedPlan extends HandlerInterface const transceiver = this._mapMidTransceiver.get(localId); - if (!transceiver) - { + if (!transceiver) { throw new Error('associated RTCRtpTransceiver not found'); } @@ -574,24 +534,22 @@ export class ReactNativeUnifiedPlan extends HandlerInterface this._pc.removeTrack(transceiver.sender); - const mediaSectionClosed = - this._remoteSdp!.closeMediaSection(transceiver.mid!); + const mediaSectionClosed = this._remoteSdp!.closeMediaSection( + transceiver.mid!, + ); - if (mediaSectionClosed) - { - try - { + if (mediaSectionClosed) { + try { transceiver.stop(); - } - catch (error) - {} + } catch (error) {} } const offer = await this._pc.createOffer(); logger.debug( 'stopSending() | calling pc.setLocalDescription() [offer:%o]', - offer); + offer, + ); await this._pc.setLocalDescription(offer); @@ -599,15 +557,15 @@ export class ReactNativeUnifiedPlan extends HandlerInterface logger.debug( 'stopSending() | calling pc.setRemoteDescription() [answer:%o]', - answer); + answer, + ); await this._pc.setRemoteDescription(answer); this._mapMidTransceiver.delete(localId); } - async pauseSending(localId: string): Promise - { + async pauseSending(localId: string): Promise { this.assertNotClosed(); this.assertSendDirection(); @@ -615,8 +573,7 @@ export class ReactNativeUnifiedPlan extends HandlerInterface const transceiver = this._mapMidTransceiver.get(localId); - if (!transceiver) - { + if (!transceiver) { throw new Error('associated RTCRtpTransceiver not found'); } @@ -627,7 +584,8 @@ export class ReactNativeUnifiedPlan extends HandlerInterface logger.debug( 'pauseSending() | calling pc.setLocalDescription() [offer:%o]', - offer); + offer, + ); await this._pc.setLocalDescription(offer); @@ -635,13 +593,13 @@ export class ReactNativeUnifiedPlan extends HandlerInterface logger.debug( 'pauseSending() | calling pc.setRemoteDescription() [answer:%o]', - answer); + answer, + ); await this._pc.setRemoteDescription(answer); } - async resumeSending(localId: string): Promise - { + async resumeSending(localId: string): Promise { this.assertNotClosed(); this.assertSendDirection(); @@ -651,8 +609,7 @@ export class ReactNativeUnifiedPlan extends HandlerInterface this._remoteSdp!.resumeSendingMediaSection(localId); - if (!transceiver) - { + if (!transceiver) { throw new Error('associated RTCRtpTransceiver not found'); } @@ -662,7 +619,8 @@ export class ReactNativeUnifiedPlan extends HandlerInterface logger.debug( 'resumeSending() | calling pc.setLocalDescription() [offer:%o]', - offer); + offer, + ); await this._pc.setLocalDescription(offer); @@ -670,67 +628,68 @@ export class ReactNativeUnifiedPlan extends HandlerInterface logger.debug( 'resumeSending() | calling pc.setRemoteDescription() [answer:%o]', - answer); + answer, + ); await this._pc.setRemoteDescription(answer); } async replaceTrack( - localId: string, track: MediaStreamTrack | null - ): Promise - { + localId: string, + track: MediaStreamTrack | null, + ): Promise { this.assertNotClosed(); this.assertSendDirection(); - if (track) - { + if (track) { logger.debug( - 'replaceTrack() [localId:%s, track.id:%s]', localId, track.id); - } - else - { + 'replaceTrack() [localId:%s, track.id:%s]', + localId, + track.id, + ); + } else { logger.debug('replaceTrack() [localId:%s, no track]', localId); } const transceiver = this._mapMidTransceiver.get(localId); - if (!transceiver) - { + if (!transceiver) { throw new Error('associated RTCRtpTransceiver not found'); } await transceiver.sender.replaceTrack(track); } - async setMaxSpatialLayer(localId: string, spatialLayer: number): Promise - { + async setMaxSpatialLayer( + localId: string, + spatialLayer: number, + ): Promise { this.assertNotClosed(); this.assertSendDirection(); logger.debug( 'setMaxSpatialLayer() [localId:%s, spatialLayer:%s]', - localId, spatialLayer); + localId, + spatialLayer, + ); const transceiver = this._mapMidTransceiver.get(localId); - if (!transceiver) - { + if (!transceiver) { throw new Error('associated RTCRtpTransceiver not found'); } const parameters = transceiver.sender.getParameters(); - parameters.encodings.forEach((encoding: RTCRtpEncodingParameters, idx: number) => - { - if (idx <= spatialLayer) - { - encoding.active = true; - } - else - { - encoding.active = false; - } - }); + parameters.encodings.forEach( + (encoding: RTCRtpEncodingParameters, idx: number) => { + if (idx <= spatialLayer) { + encoding.active = true; + } else { + encoding.active = false; + } + }, + ); await transceiver.sender.setParameters(parameters); @@ -740,7 +699,8 @@ export class ReactNativeUnifiedPlan extends HandlerInterface logger.debug( 'setMaxSpatialLayer() | calling pc.setLocalDescription() [offer:%o]', - offer); + offer, + ); await this._pc.setLocalDescription(offer); @@ -748,33 +708,35 @@ export class ReactNativeUnifiedPlan extends HandlerInterface logger.debug( 'setMaxSpatialLayer() | calling pc.setRemoteDescription() [answer:%o]', - answer); + answer, + ); await this._pc.setRemoteDescription(answer); } - async setRtpEncodingParameters(localId: string, params: any): Promise - { + async setRtpEncodingParameters(localId: string, params: any): Promise { this.assertNotClosed(); this.assertSendDirection(); logger.debug( 'setRtpEncodingParameters() [localId:%s, params:%o]', - localId, params); + localId, + params, + ); const transceiver = this._mapMidTransceiver.get(localId); - if (!transceiver) - { + if (!transceiver) { throw new Error('associated RTCRtpTransceiver not found'); } const parameters = transceiver.sender.getParameters(); - parameters.encodings.forEach((encoding: RTCRtpEncodingParameters, idx: number) => - { - parameters.encodings[idx] = { ...encoding, ...params }; - }); + parameters.encodings.forEach( + (encoding: RTCRtpEncodingParameters, idx: number) => { + parameters.encodings[idx] = { ...encoding, ...params }; + }, + ); await transceiver.sender.setParameters(parameters); @@ -784,7 +746,8 @@ export class ReactNativeUnifiedPlan extends HandlerInterface logger.debug( 'setRtpEncodingParameters() | calling pc.setLocalDescription() [offer:%o]', - offer); + offer, + ); await this._pc.setLocalDescription(offer); @@ -792,47 +755,42 @@ export class ReactNativeUnifiedPlan extends HandlerInterface logger.debug( 'setRtpEncodingParameters() | calling pc.setRemoteDescription() [answer:%o]', - answer); + answer, + ); await this._pc.setRemoteDescription(answer); } - async getSenderStats(localId: string): Promise - { + async getSenderStats(localId: string): Promise { this.assertNotClosed(); this.assertSendDirection(); const transceiver = this._mapMidTransceiver.get(localId); - if (!transceiver) - { + if (!transceiver) { throw new Error('associated RTCRtpTransceiver not found'); } return transceiver.sender.getStats(); } - async sendDataChannel( - { - ordered, - maxPacketLifeTime, - maxRetransmits, - label, - protocol - }: HandlerSendDataChannelOptions - ): Promise - { + async sendDataChannel({ + ordered, + maxPacketLifeTime, + maxRetransmits, + label, + protocol, + }: HandlerSendDataChannelOptions): Promise { this.assertNotClosed(); this.assertSendDirection(); - const options = - { - negotiated : true, - id : this._nextSendSctpStreamId, + const options = { + negotiated: true, + id: this._nextSendSctpStreamId, ordered, maxPacketLifeTime, maxRetransmits, - protocol + protocol, }; logger.debug('sendDataChannel() [options:%o]', options); @@ -845,25 +803,24 @@ export class ReactNativeUnifiedPlan extends HandlerInterface // If this is the first DataChannel we need to create the SDP answer with // m=application section. - if (!this._hasDataChannelMediaSection) - { + if (!this._hasDataChannelMediaSection) { const offer = await this._pc.createOffer(); const localSdpObject = sdpTransform.parse(offer.sdp); - const offerMediaObject = localSdpObject.media - .find((m: any) => m.type === 'application'); + const offerMediaObject = localSdpObject.media.find( + (m: any) => m.type === 'application', + ); - if (!this._transportReady) - { - await this.setupTransport( - { - localDtlsRole : this._forcedLocalDtlsRole ?? 'client', - localSdpObject - }); + if (!this._transportReady) { + await this.setupTransport({ + localDtlsRole: this._forcedLocalDtlsRole ?? 'client', + localSdpObject, + }); } logger.debug( 'sendDataChannel() | calling pc.setLocalDescription() [offer:%o]', - offer); + offer, + ); await this._pc.setLocalDescription(offer); @@ -873,36 +830,34 @@ export class ReactNativeUnifiedPlan extends HandlerInterface logger.debug( 'sendDataChannel() | calling pc.setRemoteDescription() [answer:%o]', - answer); + answer, + ); await this._pc.setRemoteDescription(answer); this._hasDataChannelMediaSection = true; } - const sctpStreamParameters: SctpStreamParameters = - { - streamId : options.id, - ordered : options.ordered, - maxPacketLifeTime : options.maxPacketLifeTime, - maxRetransmits : options.maxRetransmits + const sctpStreamParameters: SctpStreamParameters = { + streamId: options.id, + ordered: options.ordered, + maxPacketLifeTime: options.maxPacketLifeTime, + maxRetransmits: options.maxRetransmits, }; return { dataChannel, sctpStreamParameters }; } async receive( - optionsList: HandlerReceiveOptions[] - ) : Promise - { + optionsList: HandlerReceiveOptions[], + ): Promise { this.assertNotClosed(); this.assertRecvDirection(); const results: HandlerReceiveResult[] = []; const mapLocalId: Map = new Map(); - for (const options of optionsList) - { + for (const options of optionsList) { const { trackId, kind, rtpParameters, streamId } = options; logger.debug('receive() [trackId:%s, kind:%s]', trackId, kind); @@ -911,80 +866,75 @@ export class ReactNativeUnifiedPlan extends HandlerInterface mapLocalId.set(trackId, localId); - this._remoteSdp!.receive( - { - mid : localId, - kind, - offerRtpParameters : rtpParameters, - streamId : streamId || rtpParameters.rtcp!.cname!, - trackId - }); + this._remoteSdp!.receive({ + mid: localId, + kind, + offerRtpParameters: rtpParameters, + streamId: streamId || rtpParameters.rtcp!.cname!, + trackId, + }); } const offer = { type: 'offer', sdp: this._remoteSdp!.getSdp() }; logger.debug( 'receive() | calling pc.setRemoteDescription() [offer:%o]', - offer); + offer, + ); await this._pc.setRemoteDescription(offer); let answer = await this._pc.createAnswer(); const localSdpObject = sdpTransform.parse(answer.sdp); - for (const options of optionsList) - { + for (const options of optionsList) { const { trackId, rtpParameters } = options; const localId = mapLocalId.get(trackId); - const answerMediaObject = localSdpObject.media - .find((m: any) => String(m.mid) === localId); + const answerMediaObject = localSdpObject.media.find( + (m: any) => String(m.mid) === localId, + ); // May need to modify codec parameters in the answer based on codec // parameters in the offer. - sdpCommonUtils.applyCodecParameters( - { - offerRtpParameters : rtpParameters, - answerMediaObject - }); + sdpCommonUtils.applyCodecParameters({ + offerRtpParameters: rtpParameters, + answerMediaObject, + }); } answer = { type: 'answer', sdp: sdpTransform.write(localSdpObject) }; - if (!this._transportReady) - { - await this.setupTransport( - { - localDtlsRole : this._forcedLocalDtlsRole ?? 'client', - localSdpObject - }); + if (!this._transportReady) { + await this.setupTransport({ + localDtlsRole: this._forcedLocalDtlsRole ?? 'client', + localSdpObject, + }); } logger.debug( 'receive() | calling pc.setLocalDescription() [answer:%o]', - answer); + answer, + ); await this._pc.setLocalDescription(answer); - for (const options of optionsList) - { + for (const options of optionsList) { const { trackId } = options; const localId = mapLocalId.get(trackId)!; - const transceiver = this._pc.getTransceivers() + const transceiver = this._pc + .getTransceivers() .find((t: RTCRtpTransceiver) => t.mid === localId); - if (!transceiver) - { + if (!transceiver) { throw new Error('new RTCRtpTransceiver not found'); - } - else - { + } else { // Store in the map. this._mapMidTransceiver.set(localId, transceiver); results.push({ localId, - track : transceiver.receiver.track, - rtpReceiver : transceiver.receiver + track: transceiver.receiver.track, + rtpReceiver: transceiver.receiver, }); } } @@ -992,23 +942,19 @@ export class ReactNativeUnifiedPlan extends HandlerInterface return results; } - async stopReceiving(localIds: string[]): Promise - { + async stopReceiving(localIds: string[]): Promise { this.assertRecvDirection(); - if (this._closed) - { + if (this._closed) { return; } - for (const localId of localIds) - { + for (const localId of localIds) { logger.debug('stopReceiving() [localId:%s]', localId); const transceiver = this._mapMidTransceiver.get(localId); - if (!transceiver) - { + if (!transceiver) { throw new Error('associated RTCRtpTransceiver not found'); } @@ -1019,7 +965,8 @@ export class ReactNativeUnifiedPlan extends HandlerInterface logger.debug( 'stopReceiving() | calling pc.setRemoteDescription() [offer:%o]', - offer); + offer, + ); await this._pc.setRemoteDescription(offer); @@ -1027,29 +974,26 @@ export class ReactNativeUnifiedPlan extends HandlerInterface logger.debug( 'stopReceiving() | calling pc.setLocalDescription() [answer:%o]', - answer); + answer, + ); await this._pc.setLocalDescription(answer); - for (const localId of localIds) - { + for (const localId of localIds) { this._mapMidTransceiver.delete(localId); } } - async pauseReceiving(localIds: string[]): Promise - { + async pauseReceiving(localIds: string[]): Promise { this.assertNotClosed(); this.assertRecvDirection(); - for (const localId of localIds) - { + for (const localId of localIds) { logger.debug('pauseReceiving() [localId:%s]', localId); const transceiver = this._mapMidTransceiver.get(localId); - if (!transceiver) - { + if (!transceiver) { throw new Error('associated RTCRtpTransceiver not found'); } @@ -1061,7 +1005,8 @@ export class ReactNativeUnifiedPlan extends HandlerInterface logger.debug( 'pauseReceiving() | calling pc.setRemoteDescription() [offer:%o]', - offer); + offer, + ); await this._pc.setRemoteDescription(offer); @@ -1069,24 +1014,22 @@ export class ReactNativeUnifiedPlan extends HandlerInterface logger.debug( 'pauseReceiving() | calling pc.setLocalDescription() [answer:%o]', - answer); + answer, + ); await this._pc.setLocalDescription(answer); } - async resumeReceiving(localIds: string[]): Promise - { + async resumeReceiving(localIds: string[]): Promise { this.assertNotClosed(); this.assertRecvDirection(); - for (const localId of localIds) - { + for (const localId of localIds) { logger.debug('resumeReceiving() [localId:%s]', localId); const transceiver = this._mapMidTransceiver.get(localId); - if (!transceiver) - { + if (!transceiver) { throw new Error('associated RTCRtpTransceiver not found'); } @@ -1098,7 +1041,8 @@ export class ReactNativeUnifiedPlan extends HandlerInterface logger.debug( 'resumeReceiving() | calling pc.setRemoteDescription() [offer:%o]', - offer); + offer, + ); await this._pc.setRemoteDescription(offer); @@ -1106,30 +1050,30 @@ export class ReactNativeUnifiedPlan extends HandlerInterface logger.debug( 'resumeReceiving() | calling pc.setLocalDescription() [answer:%o]', - answer); + answer, + ); await this._pc.setLocalDescription(answer); } - async getReceiverStats(localId: string): Promise - { + async getReceiverStats(localId: string): Promise { this.assertNotClosed(); this.assertRecvDirection(); const transceiver = this._mapMidTransceiver.get(localId); - if (!transceiver) - { + if (!transceiver) { throw new Error('associated RTCRtpTransceiver not found'); } return transceiver.receiver.getStats(); } - async receiveDataChannel( - { sctpStreamParameters, label, protocol }: HandlerReceiveDataChannelOptions - ): Promise - { + async receiveDataChannel({ + sctpStreamParameters, + label, + protocol, + }: HandlerReceiveDataChannelOptions): Promise { this.assertNotClosed(); this.assertRecvDirection(); @@ -1137,17 +1081,16 @@ export class ReactNativeUnifiedPlan extends HandlerInterface streamId, ordered, maxPacketLifeTime, - maxRetransmits + maxRetransmits, }: SctpStreamParameters = sctpStreamParameters; - const options = - { - negotiated : true, - id : streamId, + const options = { + negotiated: true, + id: streamId, ordered, maxPacketLifeTime, maxRetransmits, - protocol + protocol, }; logger.debug('receiveDataChannel() [options:%o]', options); @@ -1156,34 +1099,33 @@ export class ReactNativeUnifiedPlan extends HandlerInterface // If this is the first DataChannel we need to create the SDP offer with // m=application section. - if (!this._hasDataChannelMediaSection) - { + if (!this._hasDataChannelMediaSection) { this._remoteSdp!.receiveSctpAssociation(); const offer = { type: 'offer', sdp: this._remoteSdp!.getSdp() }; logger.debug( 'receiveDataChannel() | calling pc.setRemoteDescription() [offer:%o]', - offer); + offer, + ); await this._pc.setRemoteDescription(offer); const answer = await this._pc.createAnswer(); - if (!this._transportReady) - { + if (!this._transportReady) { const localSdpObject = sdpTransform.parse(answer.sdp); - await this.setupTransport( - { - localDtlsRole : this._forcedLocalDtlsRole ?? 'client', - localSdpObject - }); + await this.setupTransport({ + localDtlsRole: this._forcedLocalDtlsRole ?? 'client', + localSdpObject, + }); } logger.debug( 'receiveDataChannel() | calling pc.setRemoteDescription() [answer:%o]', - answer); + answer, + ); await this._pc.setLocalDescription(answer); @@ -1193,70 +1135,57 @@ export class ReactNativeUnifiedPlan extends HandlerInterface return { dataChannel }; } - private async setupTransport( - { - localDtlsRole, - localSdpObject - }: - { - localDtlsRole: DtlsRole; - localSdpObject?: any; - } - ): Promise - { - if (!localSdpObject) - { + private async setupTransport({ + localDtlsRole, + localSdpObject, + }: { + localDtlsRole: DtlsRole; + localSdpObject?: any; + }): Promise { + if (!localSdpObject) { localSdpObject = sdpTransform.parse(this._pc.localDescription.sdp); } // Get our local DTLS parameters. - const dtlsParameters = - sdpCommonUtils.extractDtlsParameters({ sdpObject: localSdpObject }); + const dtlsParameters = sdpCommonUtils.extractDtlsParameters({ + sdpObject: localSdpObject, + }); // Set our DTLS role. dtlsParameters.role = localDtlsRole; // Update the remote DTLS role in the SDP. this._remoteSdp!.updateDtlsRole( - localDtlsRole === 'client' ? 'server' : 'client'); + localDtlsRole === 'client' ? 'server' : 'client', + ); // Need to tell the remote transport about our parameters. - await new Promise((resolve, reject) => - { - this.safeEmit( - '@connect', - { dtlsParameters }, - resolve, - reject - ); + await new Promise((resolve, reject) => { + this.safeEmit('@connect', { dtlsParameters }, resolve, reject); }); this._transportReady = true; } - private assertNotClosed(): void - { - if (this._closed) - { + private assertNotClosed(): void { + if (this._closed) { throw new InvalidStateError('method called in a closed handler'); } } - private assertSendDirection(): void - { - if (this._direction !== 'send') - { + private assertSendDirection(): void { + if (this._direction !== 'send') { throw new Error( - 'method can just be called for handlers with "send" direction'); + 'method can just be called for handlers with "send" direction', + ); } } - private assertRecvDirection(): void - { - if (this._direction !== 'recv') - { + private assertRecvDirection(): void { + if (this._direction !== 'recv') { throw new Error( - 'method can just be called for handlers with "recv" direction'); + 'method can just be called for handlers with "recv" direction', + ); } } } diff --git a/src/handlers/Safari11.ts b/src/handlers/Safari11.ts index 6e005f1c..648c68f6 100644 --- a/src/handlers/Safari11.ts +++ b/src/handlers/Safari11.ts @@ -15,7 +15,7 @@ import { HandlerSendDataChannelOptions, HandlerSendDataChannelResult, HandlerReceiveDataChannelOptions, - HandlerReceiveDataChannelResult + HandlerReceiveDataChannelResult, } from './HandlerInterface'; import { RemoteSdp } from './sdp/RemoteSdp'; import { IceParameters, DtlsRole } from '../Transport'; @@ -26,8 +26,7 @@ const logger = new Logger('Safari11'); const SCTP_NUM_STREAMS = { OS: 1024, MIS: 1024 }; -export class Safari11 extends HandlerInterface -{ +export class Safari11 extends HandlerInterface { // Handler direction. private _direction?: 'send' | 'recv'; // Remote SDP handler. @@ -51,15 +50,14 @@ export class Safari11 extends HandlerInterface private _nextSendLocalId = 0; // Map of MID, RTP parameters and RTCRtpReceiver indexed by local id. // Value is an Object with mid, rtpParameters and rtpReceiver. - private readonly _mapRecvLocalIdInfo: - Map< - string, - { - mid: string; - rtpParameters: RtpParameters; - rtpReceiver: RTCRtpReceiver; - } - > = new Map(); + private readonly _mapRecvLocalIdInfo: Map< + string, + { + mid: string; + rtpParameters: RtpParameters; + rtpReceiver: RTCRtpReceiver; + } + > = new Map(); // Whether a DataChannel m=application section has been created. private _hasDataChannelMediaSection = false; // Sending DataChannel id value counter. Incremented for each new DataChannel. @@ -70,192 +68,172 @@ export class Safari11 extends HandlerInterface /** * Creates a factory function. */ - static createFactory(): HandlerFactory - { + static createFactory(): HandlerFactory { return (): Safari11 => new Safari11(); } - constructor() - { + constructor() { super(); } - get name(): string - { + get name(): string { return 'Safari11'; } - close(): void - { + close(): void { logger.debug('close()'); // Close RTCPeerConnection. - if (this._pc) - { - try { this._pc.close(); } - catch (error) {} + if (this._pc) { + try { + this._pc.close(); + } catch (error) {} } this.emit('@close'); } - async getNativeRtpCapabilities(): Promise - { + async getNativeRtpCapabilities(): Promise { logger.debug('getNativeRtpCapabilities()'); - const pc = new (RTCPeerConnection as any)( - { - iceServers : [], - iceTransportPolicy : 'all', - bundlePolicy : 'max-bundle', - rtcpMuxPolicy : 'require', - sdpSemantics : 'plan-b' - }); + const pc = new (RTCPeerConnection as any)({ + iceServers: [], + iceTransportPolicy: 'all', + bundlePolicy: 'max-bundle', + rtcpMuxPolicy: 'require', + sdpSemantics: 'plan-b', + }); - try - { - const offer = await pc.createOffer( - { - offerToReceiveAudio : true, - offerToReceiveVideo : true - }); + try { + const offer = await pc.createOffer({ + offerToReceiveAudio: true, + offerToReceiveVideo: true, + }); - try { pc.close(); } - catch (error) {} + try { + pc.close(); + } catch (error) {} const sdpObject = sdpTransform.parse(offer.sdp); - const nativeRtpCapabilities = - sdpCommonUtils.extractRtpCapabilities({ sdpObject }); + const nativeRtpCapabilities = sdpCommonUtils.extractRtpCapabilities({ + sdpObject, + }); return nativeRtpCapabilities; - } - catch (error) - { - try { pc.close(); } - catch (error2) {} + } catch (error) { + try { + pc.close(); + } catch (error2) {} throw error; } } - async getNativeSctpCapabilities(): Promise - { + async getNativeSctpCapabilities(): Promise { logger.debug('getNativeSctpCapabilities()'); return { - numStreams : SCTP_NUM_STREAMS + numStreams: SCTP_NUM_STREAMS, }; } - run( - { - direction, - iceParameters, - iceCandidates, - dtlsParameters, - sctpParameters, - iceServers, - iceTransportPolicy, - additionalSettings, - proprietaryConstraints, - extendedRtpCapabilities - }: HandlerRunOptions - ): void - { + run({ + direction, + iceParameters, + iceCandidates, + dtlsParameters, + sctpParameters, + iceServers, + iceTransportPolicy, + additionalSettings, + proprietaryConstraints, + extendedRtpCapabilities, + }: HandlerRunOptions): void { logger.debug('run()'); this._direction = direction; - this._remoteSdp = new RemoteSdp( - { - iceParameters, - iceCandidates, - dtlsParameters, - sctpParameters, - planB : true - }); + this._remoteSdp = new RemoteSdp({ + iceParameters, + iceCandidates, + dtlsParameters, + sctpParameters, + planB: true, + }); - this._sendingRtpParametersByKind = - { - audio : ortc.getSendingRtpParameters('audio', extendedRtpCapabilities), - video : ortc.getSendingRtpParameters('video', extendedRtpCapabilities) + this._sendingRtpParametersByKind = { + audio: ortc.getSendingRtpParameters('audio', extendedRtpCapabilities), + video: ortc.getSendingRtpParameters('video', extendedRtpCapabilities), }; - this._sendingRemoteRtpParametersByKind = - { - audio : ortc.getSendingRemoteRtpParameters('audio', extendedRtpCapabilities), - video : ortc.getSendingRemoteRtpParameters('video', extendedRtpCapabilities) + this._sendingRemoteRtpParametersByKind = { + audio: ortc.getSendingRemoteRtpParameters( + 'audio', + extendedRtpCapabilities, + ), + video: ortc.getSendingRemoteRtpParameters( + 'video', + extendedRtpCapabilities, + ), }; - if (dtlsParameters.role && dtlsParameters.role !== 'auto') - { - this._forcedLocalDtlsRole = dtlsParameters.role === 'server' - ? 'client' - : 'server'; + if (dtlsParameters.role && dtlsParameters.role !== 'auto') { + this._forcedLocalDtlsRole = + dtlsParameters.role === 'server' ? 'client' : 'server'; } this._pc = new (RTCPeerConnection as any)( { - iceServers : iceServers || [], - iceTransportPolicy : iceTransportPolicy || 'all', - bundlePolicy : 'max-bundle', - rtcpMuxPolicy : 'require', - ...additionalSettings + iceServers: iceServers || [], + iceTransportPolicy: iceTransportPolicy || 'all', + bundlePolicy: 'max-bundle', + rtcpMuxPolicy: 'require', + ...additionalSettings, }, - proprietaryConstraints); + proprietaryConstraints, + ); - this._pc.addEventListener('icegatheringstatechange', () => - { + this._pc.addEventListener('icegatheringstatechange', () => { this.emit('@icegatheringstatechange', this._pc.iceGatheringState); }); - if (this._pc.connectionState) - { - this._pc.addEventListener('connectionstatechange', () => - { + if (this._pc.connectionState) { + this._pc.addEventListener('connectionstatechange', () => { this.emit('@connectionstatechange', this._pc.connectionState); }); - } - else - { - this._pc.addEventListener('iceconnectionstatechange', () => - { + } else { + this._pc.addEventListener('iceconnectionstatechange', () => { logger.warn( - 'run() | pc.connectionState not supported, using pc.iceConnectionState'); + 'run() | pc.connectionState not supported, using pc.iceConnectionState', + ); - switch (this._pc.iceConnectionState) - { - case 'checking': - { + switch (this._pc.iceConnectionState) { + case 'checking': { this.emit('@connectionstatechange', 'connecting'); break; } case 'connected': - case 'completed': - { + case 'completed': { this.emit('@connectionstatechange', 'connected'); break; } - case 'failed': - { + case 'failed': { this.emit('@connectionstatechange', 'failed'); break; } - case 'disconnected': - { + case 'disconnected': { this.emit('@connectionstatechange', 'disconnected'); break; } - case 'closed': - { + case 'closed': { this.emit('@connectionstatechange', 'closed'); break; @@ -265,8 +243,7 @@ export class Safari11 extends HandlerInterface } } - async updateIceServers(iceServers: RTCIceServer[]): Promise - { + async updateIceServers(iceServers: RTCIceServer[]): Promise { logger.debug('updateIceServers()'); const configuration = this._pc.getConfiguration(); @@ -276,25 +253,23 @@ export class Safari11 extends HandlerInterface this._pc.setConfiguration(configuration); } - async restartIce(iceParameters: IceParameters): Promise - { + async restartIce(iceParameters: IceParameters): Promise { logger.debug('restartIce()'); // Provide the remote SDP handler with new remote ICE parameters. this._remoteSdp!.updateIceParameters(iceParameters); - if (!this._transportReady) - { + if (!this._transportReady) { return; } - if (this._direction === 'send') - { + if (this._direction === 'send') { const offer = await this._pc.createOffer({ iceRestart: true }); logger.debug( 'restartIce() | calling pc.setLocalDescription() [offer:%o]', - offer); + offer, + ); await this._pc.setLocalDescription(offer); @@ -302,17 +277,17 @@ export class Safari11 extends HandlerInterface logger.debug( 'restartIce() | calling pc.setRemoteDescription() [answer:%o]', - answer); + answer, + ); await this._pc.setRemoteDescription(answer); - } - else - { + } else { const offer = { type: 'offer', sdp: this._remoteSdp!.getSdp() }; logger.debug( 'restartIce() | calling pc.setRemoteDescription() [offer:%o]', - offer); + offer, + ); await this._pc.setRemoteDescription(offer); @@ -320,30 +295,32 @@ export class Safari11 extends HandlerInterface logger.debug( 'restartIce() | calling pc.setLocalDescription() [answer:%o]', - answer); + answer, + ); await this._pc.setLocalDescription(answer); } } - async getTransportStats(): Promise - { + async getTransportStats(): Promise { return this._pc.getStats(); } - async send( - { track, encodings, codecOptions, codec }: HandlerSendOptions - ): Promise - { + async send({ + track, + encodings, + codecOptions, + codec, + }: HandlerSendOptions): Promise { this.assertSendDirection(); logger.debug('send() [kind:%s, track.id:%s]', track.kind, track.id); - if (codec) - { + if (codec) { logger.warn( 'send() | codec selection is not available in %s handler', - this.name); + this.name, + ); } this._sendStream.addTrack(track); @@ -352,70 +329,70 @@ export class Safari11 extends HandlerInterface let offer = await this._pc.createOffer(); let localSdpObject = sdpTransform.parse(offer.sdp); let offerMediaObject; - const sendingRtpParameters = - utils.clone(this._sendingRtpParametersByKind![track.kind]); - - sendingRtpParameters.codecs = - ortc.reduceCodecs(sendingRtpParameters.codecs); - - const sendingRemoteRtpParameters = - utils.clone(this._sendingRemoteRtpParametersByKind![track.kind]); - - sendingRemoteRtpParameters.codecs = - ortc.reduceCodecs(sendingRemoteRtpParameters.codecs); - - if (!this._transportReady) - { - await this.setupTransport( - { - localDtlsRole : this._forcedLocalDtlsRole ?? 'client', - localSdpObject - }); + const sendingRtpParameters = utils.clone( + this._sendingRtpParametersByKind![track.kind], + ); + + sendingRtpParameters.codecs = ortc.reduceCodecs( + sendingRtpParameters.codecs, + ); + + const sendingRemoteRtpParameters = utils.clone( + this._sendingRemoteRtpParametersByKind![track.kind], + ); + + sendingRemoteRtpParameters.codecs = ortc.reduceCodecs( + sendingRemoteRtpParameters.codecs, + ); + + if (!this._transportReady) { + await this.setupTransport({ + localDtlsRole: this._forcedLocalDtlsRole ?? 'client', + localSdpObject, + }); } - if (track.kind === 'video' && encodings && encodings.length > 1) - { + if (track.kind === 'video' && encodings && encodings.length > 1) { logger.debug('send() | enabling simulcast'); localSdpObject = sdpTransform.parse(offer.sdp); offerMediaObject = localSdpObject.media.find( - (m: any) => m.type === 'video'); + (m: any) => m.type === 'video', + ); - sdpPlanBUtils.addLegacySimulcast( - { - offerMediaObject, - track, - numStreams : encodings.length - }); + sdpPlanBUtils.addLegacySimulcast({ + offerMediaObject, + track, + numStreams: encodings.length, + }); offer = { type: 'offer', sdp: sdpTransform.write(localSdpObject) }; } - logger.debug( - 'send() | calling pc.setLocalDescription() [offer:%o]', - offer); + logger.debug('send() | calling pc.setLocalDescription() [offer:%o]', offer); await this._pc.setLocalDescription(offer); localSdpObject = sdpTransform.parse(this._pc.localDescription.sdp); - offerMediaObject = localSdpObject.media - .find((m: any) => m.type === track.kind); + offerMediaObject = localSdpObject.media.find( + (m: any) => m.type === track.kind, + ); // Set RTCP CNAME. - sendingRtpParameters.rtcp!.cname = - sdpCommonUtils.getCname({ offerMediaObject }); + sendingRtpParameters.rtcp!.cname = sdpCommonUtils.getCname({ + offerMediaObject, + }); // Set RTP encodings. - sendingRtpParameters.encodings = - sdpPlanBUtils.getRtpEncodings({ offerMediaObject, track }); + sendingRtpParameters.encodings = sdpPlanBUtils.getRtpEncodings({ + offerMediaObject, + track, + }); // Complete encodings with given values. - if (encodings) - { - for (let idx = 0; idx < sendingRtpParameters.encodings.length; ++idx) - { - if (encodings[idx]) - { + if (encodings) { + for (let idx = 0; idx < sendingRtpParameters.encodings.length; ++idx) { + if (encodings[idx]) { Object.assign(sendingRtpParameters.encodings[idx], encodings[idx]); } } @@ -426,27 +403,25 @@ export class Safari11 extends HandlerInterface if ( sendingRtpParameters.encodings.length > 1 && sendingRtpParameters.codecs[0].mimeType.toLowerCase() === 'video/vp8' - ) - { - for (const encoding of sendingRtpParameters.encodings) - { + ) { + for (const encoding of sendingRtpParameters.encodings) { encoding.scalabilityMode = 'L1T3'; } } - this._remoteSdp!.send( - { - offerMediaObject, - offerRtpParameters : sendingRtpParameters, - answerRtpParameters : sendingRemoteRtpParameters, - codecOptions - }); + this._remoteSdp!.send({ + offerMediaObject, + offerRtpParameters: sendingRtpParameters, + answerRtpParameters: sendingRemoteRtpParameters, + codecOptions, + }); const answer = { type: 'answer', sdp: this._remoteSdp!.getSdp() }; logger.debug( 'send() | calling pc.setRemoteDescription() [answer:%o]', - answer); + answer, + ); await this._pc.setRemoteDescription(answer); @@ -454,32 +429,30 @@ export class Safari11 extends HandlerInterface this._nextSendLocalId++; - const rtpSender = this._pc.getSenders() + const rtpSender = this._pc + .getSenders() .find((s: RTCRtpSender) => s.track === track); // Insert into the map. this._mapSendLocalIdRtpSender.set(localId, rtpSender); return { - localId : localId, - rtpParameters : sendingRtpParameters, - rtpSender + localId: localId, + rtpParameters: sendingRtpParameters, + rtpSender, }; } - async stopSending(localId: string): Promise - { + async stopSending(localId: string): Promise { this.assertSendDirection(); const rtpSender = this._mapSendLocalIdRtpSender.get(localId); - if (!rtpSender) - { + if (!rtpSender) { throw new Error('associated RTCRtpSender not found'); } - if (rtpSender.track) - { + if (rtpSender.track) { this._sendStream.removeTrack(rtpSender.track); } @@ -489,21 +462,19 @@ export class Safari11 extends HandlerInterface logger.debug( 'stopSending() | calling pc.setLocalDescription() [offer:%o]', - offer); + offer, + ); - try - { + try { await this._pc.setLocalDescription(offer); - } - catch (error) - { + } catch (error) { // NOTE: If there are no sending tracks, setLocalDescription() will fail with // "Failed to create channels". If so, ignore it. - if (this._sendStream.getTracks().length === 0) - { + if (this._sendStream.getTracks().length === 0) { logger.warn( 'stopSending() | ignoring expected error due no sending tracks: %s', - (error as Error).toString()); + (error as Error).toString(), + ); return; } @@ -511,8 +482,7 @@ export class Safari11 extends HandlerInterface throw error; } - if (this._pc.signalingState === 'stable') - { + if (this._pc.signalingState === 'stable') { return; } @@ -520,43 +490,41 @@ export class Safari11 extends HandlerInterface logger.debug( 'stopSending() | calling pc.setRemoteDescription() [answer:%o]', - answer); + answer, + ); await this._pc.setRemoteDescription(answer); } // eslint-disable-next-line @typescript-eslint/no-unused-vars - async pauseSending(localId: string): Promise - { + async pauseSending(localId: string): Promise { // Unimplemented. } // eslint-disable-next-line @typescript-eslint/no-unused-vars - async resumeSending(localId: string): Promise - { + async resumeSending(localId: string): Promise { // Unimplemented. } async replaceTrack( - localId: string, track: MediaStreamTrack | null - ): Promise - { + localId: string, + track: MediaStreamTrack | null, + ): Promise { this.assertSendDirection(); - if (track) - { + if (track) { logger.debug( - 'replaceTrack() [localId:%s, track.id:%s]', localId, track.id); - } - else - { + 'replaceTrack() [localId:%s, track.id:%s]', + localId, + track.id, + ); + } else { logger.debug('replaceTrack() [localId:%s, no track]', localId); } const rtpSender = this._mapSendLocalIdRtpSender.get(localId); - if (!rtpSender) - { + if (!rtpSender) { throw new Error('associated RTCRtpSender not found'); } @@ -565,109 +533,103 @@ export class Safari11 extends HandlerInterface await rtpSender.replaceTrack(track); // Remove the old track from the local stream. - if (oldTrack) - { + if (oldTrack) { this._sendStream.removeTrack(oldTrack); } // Add the new track to the local stream. - if (track) - { + if (track) { this._sendStream.addTrack(track); } } - async setMaxSpatialLayer(localId: string, spatialLayer: number): Promise - { + async setMaxSpatialLayer( + localId: string, + spatialLayer: number, + ): Promise { this.assertSendDirection(); logger.debug( 'setMaxSpatialLayer() [localId:%s, spatialLayer:%s]', - localId, spatialLayer); + localId, + spatialLayer, + ); const rtpSender = this._mapSendLocalIdRtpSender.get(localId); - if (!rtpSender) - { + if (!rtpSender) { throw new Error('associated RTCRtpSender not found'); } const parameters = rtpSender.getParameters(); - parameters.encodings.forEach((encoding: RTCRtpEncodingParameters, idx: number) => - { - if (idx <= spatialLayer) - { - encoding.active = true; - } - else - { - encoding.active = false; - } - }); + parameters.encodings.forEach( + (encoding: RTCRtpEncodingParameters, idx: number) => { + if (idx <= spatialLayer) { + encoding.active = true; + } else { + encoding.active = false; + } + }, + ); await rtpSender.setParameters(parameters); } - async setRtpEncodingParameters(localId: string, params: any): Promise - { + async setRtpEncodingParameters(localId: string, params: any): Promise { this.assertSendDirection(); logger.debug( 'setRtpEncodingParameters() [localId:%s, params:%o]', - localId, params); + localId, + params, + ); const rtpSender = this._mapSendLocalIdRtpSender.get(localId); - if (!rtpSender) - { + if (!rtpSender) { throw new Error('associated RTCRtpSender not found'); } const parameters = rtpSender.getParameters(); - parameters.encodings.forEach((encoding: RTCRtpEncodingParameters, idx: number) => - { - parameters.encodings[idx] = { ...encoding, ...params }; - }); + parameters.encodings.forEach( + (encoding: RTCRtpEncodingParameters, idx: number) => { + parameters.encodings[idx] = { ...encoding, ...params }; + }, + ); await rtpSender.setParameters(parameters); } - async getSenderStats(localId: string): Promise - { + async getSenderStats(localId: string): Promise { this.assertSendDirection(); const rtpSender = this._mapSendLocalIdRtpSender.get(localId); - if (!rtpSender) - { + if (!rtpSender) { throw new Error('associated RTCRtpSender not found'); } return rtpSender.getStats(); } - async sendDataChannel( - { - ordered, - maxPacketLifeTime, - maxRetransmits, - label, - protocol - }: HandlerSendDataChannelOptions - ): Promise - { + async sendDataChannel({ + ordered, + maxPacketLifeTime, + maxRetransmits, + label, + protocol, + }: HandlerSendDataChannelOptions): Promise { this.assertSendDirection(); - const options = - { - negotiated : true, - id : this._nextSendSctpStreamId, + const options = { + negotiated: true, + id: this._nextSendSctpStreamId, ordered, maxPacketLifeTime, maxRetransmits, - protocol + protocol, }; logger.debug('sendDataChannel() [options:%o]', options); @@ -680,25 +642,24 @@ export class Safari11 extends HandlerInterface // If this is the first DataChannel we need to create the SDP answer with // m=application section. - if (!this._hasDataChannelMediaSection) - { + if (!this._hasDataChannelMediaSection) { const offer = await this._pc.createOffer(); const localSdpObject = sdpTransform.parse(offer.sdp); - const offerMediaObject = localSdpObject.media - .find((m: any) => m.type === 'application'); + const offerMediaObject = localSdpObject.media.find( + (m: any) => m.type === 'application', + ); - if (!this._transportReady) - { - await this.setupTransport( - { - localDtlsRole : this._forcedLocalDtlsRole ?? 'client', - localSdpObject - }); + if (!this._transportReady) { + await this.setupTransport({ + localDtlsRole: this._forcedLocalDtlsRole ?? 'client', + localSdpObject, + }); } logger.debug( 'sendDataChannel() | calling pc.setLocalDescription() [offer:%o]', - offer); + offer, + ); await this._pc.setLocalDescription(offer); @@ -708,143 +669,144 @@ export class Safari11 extends HandlerInterface logger.debug( 'sendDataChannel() | calling pc.setRemoteDescription() [answer:%o]', - answer); + answer, + ); await this._pc.setRemoteDescription(answer); this._hasDataChannelMediaSection = true; } - const sctpStreamParameters: SctpStreamParameters = - { - streamId : options.id, - ordered : options.ordered, - maxPacketLifeTime : options.maxPacketLifeTime, - maxRetransmits : options.maxRetransmits + const sctpStreamParameters: SctpStreamParameters = { + streamId: options.id, + ordered: options.ordered, + maxPacketLifeTime: options.maxPacketLifeTime, + maxRetransmits: options.maxRetransmits, }; return { dataChannel, sctpStreamParameters }; } async receive( - optionsList: HandlerReceiveOptions[] - ) : Promise - { + optionsList: HandlerReceiveOptions[], + ): Promise { this.assertRecvDirection(); const results: HandlerReceiveResult[] = []; - for (const options of optionsList) - { + for (const options of optionsList) { const { trackId, kind, rtpParameters, streamId } = options; logger.debug('receive() [trackId:%s, kind:%s]', trackId, kind); const mid = kind; - this._remoteSdp!.receive( - { - mid, - kind, - offerRtpParameters : rtpParameters, - streamId : streamId || rtpParameters.rtcp!.cname!, - trackId - }); + this._remoteSdp!.receive({ + mid, + kind, + offerRtpParameters: rtpParameters, + streamId: streamId || rtpParameters.rtcp!.cname!, + trackId, + }); } const offer = { type: 'offer', sdp: this._remoteSdp!.getSdp() }; logger.debug( 'receive() | calling pc.setRemoteDescription() [offer:%o]', - offer); + offer, + ); await this._pc.setRemoteDescription(offer); let answer = await this._pc.createAnswer(); const localSdpObject = sdpTransform.parse(answer.sdp); - for (const options of optionsList) - { + for (const options of optionsList) { const { kind, rtpParameters } = options; const mid = kind; - const answerMediaObject = localSdpObject.media - .find((m: any) => String(m.mid) === mid); + const answerMediaObject = localSdpObject.media.find( + (m: any) => String(m.mid) === mid, + ); // May need to modify codec parameters in the answer based on codec // parameters in the offer. - sdpCommonUtils.applyCodecParameters( - { - offerRtpParameters : rtpParameters, - answerMediaObject - }); + sdpCommonUtils.applyCodecParameters({ + offerRtpParameters: rtpParameters, + answerMediaObject, + }); } answer = { type: 'answer', sdp: sdpTransform.write(localSdpObject) }; - if (!this._transportReady) - { - await this.setupTransport( - { - localDtlsRole : this._forcedLocalDtlsRole ?? 'client', - localSdpObject - }); + if (!this._transportReady) { + await this.setupTransport({ + localDtlsRole: this._forcedLocalDtlsRole ?? 'client', + localSdpObject, + }); } logger.debug( 'receive() | calling pc.setLocalDescription() [answer:%o]', - answer); + answer, + ); await this._pc.setLocalDescription(answer); - for (const options of optionsList) - { + for (const options of optionsList) { const { kind, trackId, rtpParameters } = options; const mid = kind; const localId = trackId; - const rtpReceiver = this._pc.getReceivers() + const rtpReceiver = this._pc + .getReceivers() .find((r: RTCRtpReceiver) => r.track && r.track.id === localId); - if (!rtpReceiver) - { + if (!rtpReceiver) { throw new Error('new RTCRtpReceiver not'); } // Insert into the map. - this._mapRecvLocalIdInfo.set(localId, { mid, rtpParameters, rtpReceiver }); + this._mapRecvLocalIdInfo.set(localId, { + mid, + rtpParameters, + rtpReceiver, + }); results.push({ localId, - track : rtpReceiver.track, - rtpReceiver + track: rtpReceiver.track, + rtpReceiver, }); } return results; } - async stopReceiving(localIds: string[]): Promise - { + async stopReceiving(localIds: string[]): Promise { this.assertRecvDirection(); - for (const localId of localIds) - { + for (const localId of localIds) { logger.debug('stopReceiving() [localId:%s]', localId); - const { mid, rtpParameters } = this._mapRecvLocalIdInfo.get(localId) || {}; + const { mid, rtpParameters } = + this._mapRecvLocalIdInfo.get(localId) || {}; // Remove from the map. this._mapRecvLocalIdInfo.delete(localId); - this._remoteSdp!.planBStopReceiving( - { mid: mid!, offerRtpParameters: rtpParameters! }); + this._remoteSdp!.planBStopReceiving({ + mid: mid!, + offerRtpParameters: rtpParameters!, + }); } const offer = { type: 'offer', sdp: this._remoteSdp!.getSdp() }; logger.debug( 'stopReceiving() | calling pc.setRemoteDescription() [offer:%o]', - offer); + offer, + ); await this._pc.setRemoteDescription(offer); @@ -852,19 +814,18 @@ export class Safari11 extends HandlerInterface logger.debug( 'stopReceiving() | calling pc.setLocalDescription() [answer:%o]', - answer); + answer, + ); await this._pc.setLocalDescription(answer); } - async getReceiverStats(localId: string): Promise - { + async getReceiverStats(localId: string): Promise { this.assertRecvDirection(); const { rtpReceiver } = this._mapRecvLocalIdInfo.get(localId) || {}; - if (!rtpReceiver) - { + if (!rtpReceiver) { throw new Error('associated RTCRtpReceiver not found'); } @@ -873,39 +834,35 @@ export class Safari11 extends HandlerInterface async pauseReceiving( // eslint-disable-next-line @typescript-eslint/no-unused-vars - localIds: string[]): Promise - { + localIds: string[], + ): Promise { // Unimplemented. } async resumeReceiving( // eslint-disable-next-line @typescript-eslint/no-unused-vars - localIds: string[]): Promise - { + localIds: string[], + ): Promise { // Unimplemented. } - async receiveDataChannel( - { sctpStreamParameters, label, protocol }: HandlerReceiveDataChannelOptions - ): Promise - { + async receiveDataChannel({ + sctpStreamParameters, + label, + protocol, + }: HandlerReceiveDataChannelOptions): Promise { this.assertRecvDirection(); - const { - streamId, - ordered, - maxPacketLifeTime, - maxRetransmits - } = sctpStreamParameters; + const { streamId, ordered, maxPacketLifeTime, maxRetransmits } = + sctpStreamParameters; - const options = - { - negotiated : true, - id : streamId, + const options = { + negotiated: true, + id: streamId, ordered, maxPacketLifeTime, maxRetransmits, - protocol + protocol, }; logger.debug('receiveDataChannel() [options:%o]', options); @@ -914,34 +871,33 @@ export class Safari11 extends HandlerInterface // If this is the first DataChannel we need to create the SDP offer with // m=application section. - if (!this._hasDataChannelMediaSection) - { + if (!this._hasDataChannelMediaSection) { this._remoteSdp!.receiveSctpAssociation({ oldDataChannelSpec: true }); const offer = { type: 'offer', sdp: this._remoteSdp!.getSdp() }; logger.debug( 'receiveDataChannel() | calling pc.setRemoteDescription() [offer:%o]', - offer); + offer, + ); await this._pc.setRemoteDescription(offer); const answer = await this._pc.createAnswer(); - if (!this._transportReady) - { + if (!this._transportReady) { const localSdpObject = sdpTransform.parse(answer.sdp); - await this.setupTransport( - { - localDtlsRole : this._forcedLocalDtlsRole ?? 'client', - localSdpObject - }); + await this.setupTransport({ + localDtlsRole: this._forcedLocalDtlsRole ?? 'client', + localSdpObject, + }); } logger.debug( 'receiveDataChannel() | calling pc.setRemoteDescription() [answer:%o]', - answer); + answer, + ); await this._pc.setLocalDescription(answer); @@ -951,62 +907,51 @@ export class Safari11 extends HandlerInterface return { dataChannel }; } - private async setupTransport( - { - localDtlsRole, - localSdpObject - }: - { - localDtlsRole: DtlsRole; - localSdpObject?: any; - } - ): Promise - { - if (!localSdpObject) - { + private async setupTransport({ + localDtlsRole, + localSdpObject, + }: { + localDtlsRole: DtlsRole; + localSdpObject?: any; + }): Promise { + if (!localSdpObject) { localSdpObject = sdpTransform.parse(this._pc.localDescription.sdp); } // Get our local DTLS parameters. - const dtlsParameters = - sdpCommonUtils.extractDtlsParameters({ sdpObject: localSdpObject }); + const dtlsParameters = sdpCommonUtils.extractDtlsParameters({ + sdpObject: localSdpObject, + }); // Set our DTLS role. dtlsParameters.role = localDtlsRole; // Update the remote DTLS role in the SDP. this._remoteSdp!.updateDtlsRole( - localDtlsRole === 'client' ? 'server' : 'client'); + localDtlsRole === 'client' ? 'server' : 'client', + ); // Need to tell the remote transport about our parameters. - await new Promise((resolve, reject) => - { - this.safeEmit( - '@connect', - { dtlsParameters }, - resolve, - reject - ); + await new Promise((resolve, reject) => { + this.safeEmit('@connect', { dtlsParameters }, resolve, reject); }); this._transportReady = true; } - private assertSendDirection(): void - { - if (this._direction !== 'send') - { + private assertSendDirection(): void { + if (this._direction !== 'send') { throw new Error( - 'method can just be called for handlers with "send" direction'); + 'method can just be called for handlers with "send" direction', + ); } } - private assertRecvDirection(): void - { - if (this._direction !== 'recv') - { + private assertRecvDirection(): void { + if (this._direction !== 'recv') { throw new Error( - 'method can just be called for handlers with "recv" direction'); + 'method can just be called for handlers with "recv" direction', + ); } } } diff --git a/src/handlers/Safari12.ts b/src/handlers/Safari12.ts index 1bc5359e..0dd0e765 100644 --- a/src/handlers/Safari12.ts +++ b/src/handlers/Safari12.ts @@ -17,7 +17,7 @@ import { HandlerSendDataChannelOptions, HandlerSendDataChannelResult, HandlerReceiveDataChannelOptions, - HandlerReceiveDataChannelResult + HandlerReceiveDataChannelResult, } from './HandlerInterface'; import { RemoteSdp } from './sdp/RemoteSdp'; import { parse as parseScalabilityMode } from '../scalabilityModes'; @@ -29,8 +29,7 @@ const logger = new Logger('Safari12'); const SCTP_NUM_STREAMS = { OS: 1024, MIS: 1024 }; -export class Safari12 extends HandlerInterface -{ +export class Safari12 extends HandlerInterface { // Closed flag. private _closed = false; // Handler direction. @@ -62,201 +61,181 @@ export class Safari12 extends HandlerInterface /** * Creates a factory function. */ - static createFactory(): HandlerFactory - { + static createFactory(): HandlerFactory { return (): Safari12 => new Safari12(); } - constructor() - { + constructor() { super(); } - get name(): string - { + get name(): string { return 'Safari12'; } - close(): void - { + close(): void { logger.debug('close()'); - if (this._closed) - { + if (this._closed) { return; } this._closed = true; // Close RTCPeerConnection. - if (this._pc) - { - try { this._pc.close(); } - catch (error) {} + if (this._pc) { + try { + this._pc.close(); + } catch (error) {} } this.emit('@close'); } - async getNativeRtpCapabilities(): Promise - { + async getNativeRtpCapabilities(): Promise { logger.debug('getNativeRtpCapabilities()'); - const pc = new (RTCPeerConnection as any)( - { - iceServers : [], - iceTransportPolicy : 'all', - bundlePolicy : 'max-bundle', - rtcpMuxPolicy : 'require' - }); + const pc = new (RTCPeerConnection as any)({ + iceServers: [], + iceTransportPolicy: 'all', + bundlePolicy: 'max-bundle', + rtcpMuxPolicy: 'require', + }); - try - { + try { pc.addTransceiver('audio'); pc.addTransceiver('video'); const offer = await pc.createOffer(); - try { pc.close(); } - catch (error) {} + try { + pc.close(); + } catch (error) {} const sdpObject = sdpTransform.parse(offer.sdp); - const nativeRtpCapabilities = - sdpCommonUtils.extractRtpCapabilities({ sdpObject }); + const nativeRtpCapabilities = sdpCommonUtils.extractRtpCapabilities({ + sdpObject, + }); // libwebrtc supports NACK for OPUS but doesn't announce it. ortcUtils.addNackSuppportForOpus(nativeRtpCapabilities); return nativeRtpCapabilities; - } - catch (error) - { - try { pc.close(); } - catch (error2) {} + } catch (error) { + try { + pc.close(); + } catch (error2) {} throw error; } } - async getNativeSctpCapabilities(): Promise - { + async getNativeSctpCapabilities(): Promise { logger.debug('getNativeSctpCapabilities()'); return { - numStreams : SCTP_NUM_STREAMS + numStreams: SCTP_NUM_STREAMS, }; } - run( - { - direction, - iceParameters, - iceCandidates, - dtlsParameters, - sctpParameters, - iceServers, - iceTransportPolicy, - additionalSettings, - proprietaryConstraints, - extendedRtpCapabilities - }: HandlerRunOptions - ): void - { + run({ + direction, + iceParameters, + iceCandidates, + dtlsParameters, + sctpParameters, + iceServers, + iceTransportPolicy, + additionalSettings, + proprietaryConstraints, + extendedRtpCapabilities, + }: HandlerRunOptions): void { this.assertNotClosed(); logger.debug('run()'); this._direction = direction; - this._remoteSdp = new RemoteSdp( - { - iceParameters, - iceCandidates, - dtlsParameters, - sctpParameters - }); + this._remoteSdp = new RemoteSdp({ + iceParameters, + iceCandidates, + dtlsParameters, + sctpParameters, + }); - this._sendingRtpParametersByKind = - { - audio : ortc.getSendingRtpParameters('audio', extendedRtpCapabilities), - video : ortc.getSendingRtpParameters('video', extendedRtpCapabilities) + this._sendingRtpParametersByKind = { + audio: ortc.getSendingRtpParameters('audio', extendedRtpCapabilities), + video: ortc.getSendingRtpParameters('video', extendedRtpCapabilities), }; - this._sendingRemoteRtpParametersByKind = - { - audio : ortc.getSendingRemoteRtpParameters('audio', extendedRtpCapabilities), - video : ortc.getSendingRemoteRtpParameters('video', extendedRtpCapabilities) + this._sendingRemoteRtpParametersByKind = { + audio: ortc.getSendingRemoteRtpParameters( + 'audio', + extendedRtpCapabilities, + ), + video: ortc.getSendingRemoteRtpParameters( + 'video', + extendedRtpCapabilities, + ), }; - if (dtlsParameters.role && dtlsParameters.role !== 'auto') - { - this._forcedLocalDtlsRole = dtlsParameters.role === 'server' - ? 'client' - : 'server'; + if (dtlsParameters.role && dtlsParameters.role !== 'auto') { + this._forcedLocalDtlsRole = + dtlsParameters.role === 'server' ? 'client' : 'server'; } this._pc = new (RTCPeerConnection as any)( { - iceServers : iceServers || [], - iceTransportPolicy : iceTransportPolicy || 'all', - bundlePolicy : 'max-bundle', - rtcpMuxPolicy : 'require', - ...additionalSettings + iceServers: iceServers || [], + iceTransportPolicy: iceTransportPolicy || 'all', + bundlePolicy: 'max-bundle', + rtcpMuxPolicy: 'require', + ...additionalSettings, }, - proprietaryConstraints); + proprietaryConstraints, + ); - this._pc.addEventListener('icegatheringstatechange', () => - { + this._pc.addEventListener('icegatheringstatechange', () => { this.emit('@icegatheringstatechange', this._pc.iceGatheringState); }); - if (this._pc.connectionState) - { - this._pc.addEventListener('connectionstatechange', () => - { + if (this._pc.connectionState) { + this._pc.addEventListener('connectionstatechange', () => { this.emit('@connectionstatechange', this._pc.connectionState); }); - } - else - { - this._pc.addEventListener('iceconnectionstatechange', () => - { + } else { + this._pc.addEventListener('iceconnectionstatechange', () => { logger.warn( - 'run() | pc.connectionState not supported, using pc.iceConnectionState'); + 'run() | pc.connectionState not supported, using pc.iceConnectionState', + ); - switch (this._pc.iceConnectionState) - { - case 'checking': - { + switch (this._pc.iceConnectionState) { + case 'checking': { this.emit('@connectionstatechange', 'connecting'); break; } case 'connected': - case 'completed': - { + case 'completed': { this.emit('@connectionstatechange', 'connected'); break; } - case 'failed': - { + case 'failed': { this.emit('@connectionstatechange', 'failed'); break; } - case 'disconnected': - { + case 'disconnected': { this.emit('@connectionstatechange', 'disconnected'); break; } - case 'closed': - { + case 'closed': { this.emit('@connectionstatechange', 'closed'); break; @@ -266,8 +245,7 @@ export class Safari12 extends HandlerInterface } } - async updateIceServers(iceServers: RTCIceServer[]): Promise - { + async updateIceServers(iceServers: RTCIceServer[]): Promise { this.assertNotClosed(); logger.debug('updateIceServers()'); @@ -279,8 +257,7 @@ export class Safari12 extends HandlerInterface this._pc.setConfiguration(configuration); } - async restartIce(iceParameters: IceParameters): Promise - { + async restartIce(iceParameters: IceParameters): Promise { this.assertNotClosed(); logger.debug('restartIce()'); @@ -288,18 +265,17 @@ export class Safari12 extends HandlerInterface // Provide the remote SDP handler with new remote ICE parameters. this._remoteSdp!.updateIceParameters(iceParameters); - if (!this._transportReady) - { + if (!this._transportReady) { return; } - if (this._direction === 'send') - { + if (this._direction === 'send') { const offer = await this._pc.createOffer({ iceRestart: true }); logger.debug( 'restartIce() | calling pc.setLocalDescription() [offer:%o]', - offer); + offer, + ); await this._pc.setLocalDescription(offer); @@ -307,17 +283,17 @@ export class Safari12 extends HandlerInterface logger.debug( 'restartIce() | calling pc.setRemoteDescription() [answer:%o]', - answer); + answer, + ); await this._pc.setRemoteDescription(answer); - } - else - { + } else { const offer = { type: 'offer', sdp: this._remoteSdp!.getSdp() }; logger.debug( 'restartIce() | calling pc.setRemoteDescription() [offer:%o]', - offer); + offer, + ); await this._pc.setRemoteDescription(offer); @@ -325,80 +301,83 @@ export class Safari12 extends HandlerInterface logger.debug( 'restartIce() | calling pc.setLocalDescription() [answer:%o]', - answer); + answer, + ); await this._pc.setLocalDescription(answer); } } - async getTransportStats(): Promise - { + async getTransportStats(): Promise { this.assertNotClosed(); return this._pc.getStats(); } - async send( - { track, encodings, codecOptions, codec }: HandlerSendOptions - ): Promise - { + async send({ + track, + encodings, + codecOptions, + codec, + }: HandlerSendOptions): Promise { this.assertNotClosed(); this.assertSendDirection(); logger.debug('send() [kind:%s, track.id:%s]', track.kind, track.id); - const sendingRtpParameters = - utils.clone(this._sendingRtpParametersByKind![track.kind]); + const sendingRtpParameters = utils.clone( + this._sendingRtpParametersByKind![track.kind], + ); // This may throw. - sendingRtpParameters.codecs = - ortc.reduceCodecs(sendingRtpParameters.codecs, codec); + sendingRtpParameters.codecs = ortc.reduceCodecs( + sendingRtpParameters.codecs, + codec, + ); - const sendingRemoteRtpParameters = - utils.clone(this._sendingRemoteRtpParametersByKind![track.kind]); + const sendingRemoteRtpParameters = utils.clone( + this._sendingRemoteRtpParametersByKind![track.kind], + ); // This may throw. - sendingRemoteRtpParameters.codecs = - ortc.reduceCodecs(sendingRemoteRtpParameters.codecs, codec); + sendingRemoteRtpParameters.codecs = ortc.reduceCodecs( + sendingRemoteRtpParameters.codecs, + codec, + ); const mediaSectionIdx = this._remoteSdp!.getNextMediaSectionIdx(); - const transceiver = this._pc.addTransceiver( - track, { direction: 'sendonly', streams: [ this._sendStream ] }); + const transceiver = this._pc.addTransceiver(track, { + direction: 'sendonly', + streams: [this._sendStream], + }); let offer = await this._pc.createOffer(); let localSdpObject = sdpTransform.parse(offer.sdp); let offerMediaObject; - if (!this._transportReady) - { - await this.setupTransport( - { - localDtlsRole : this._forcedLocalDtlsRole ?? 'client', - localSdpObject - }); + if (!this._transportReady) { + await this.setupTransport({ + localDtlsRole: this._forcedLocalDtlsRole ?? 'client', + localSdpObject, + }); } - const layers = - parseScalabilityMode((encodings || [ {} ])[0].scalabilityMode); + const layers = parseScalabilityMode((encodings || [{}])[0].scalabilityMode); - if (encodings && encodings.length > 1) - { + if (encodings && encodings.length > 1) { logger.debug('send() | enabling legacy simulcast'); localSdpObject = sdpTransform.parse(offer.sdp); offerMediaObject = localSdpObject.media[mediaSectionIdx.idx]; - sdpUnifiedPlanUtils.addLegacySimulcast( - { - offerMediaObject, - numStreams : encodings.length - }); + sdpUnifiedPlanUtils.addLegacySimulcast({ + offerMediaObject, + numStreams: encodings.length, + }); offer = { type: 'offer', sdp: sdpTransform.write(localSdpObject) }; } - logger.debug( - 'send() | calling pc.setLocalDescription() [offer:%o]', - offer); + logger.debug('send() | calling pc.setLocalDescription() [offer:%o]', offer); await this._pc.setLocalDescription(offer); @@ -412,20 +391,19 @@ export class Safari12 extends HandlerInterface offerMediaObject = localSdpObject.media[mediaSectionIdx.idx]; // Set RTCP CNAME. - sendingRtpParameters.rtcp!.cname = - sdpCommonUtils.getCname({ offerMediaObject }); + sendingRtpParameters.rtcp!.cname = sdpCommonUtils.getCname({ + offerMediaObject, + }); // Set RTP encodings. - sendingRtpParameters.encodings = - sdpUnifiedPlanUtils.getRtpEncodings({ offerMediaObject }); + sendingRtpParameters.encodings = sdpUnifiedPlanUtils.getRtpEncodings({ + offerMediaObject, + }); // Complete encodings with given values. - if (encodings) - { - for (let idx = 0; idx < sendingRtpParameters.encodings.length; ++idx) - { - if (encodings[idx]) - { + if (encodings) { + for (let idx = 0; idx < sendingRtpParameters.encodings.length; ++idx) { + if (encodings[idx]) { Object.assign(sendingRtpParameters.encodings[idx], encodings[idx]); } } @@ -435,39 +413,32 @@ export class Safari12 extends HandlerInterface // each encoding. if ( sendingRtpParameters.encodings.length > 1 && - ( - sendingRtpParameters.codecs[0].mimeType.toLowerCase() === 'video/vp8' || - sendingRtpParameters.codecs[0].mimeType.toLowerCase() === 'video/h264' - ) - ) - { - for (const encoding of sendingRtpParameters.encodings) - { - if (encoding.scalabilityMode) - { + (sendingRtpParameters.codecs[0].mimeType.toLowerCase() === 'video/vp8' || + sendingRtpParameters.codecs[0].mimeType.toLowerCase() === 'video/h264') + ) { + for (const encoding of sendingRtpParameters.encodings) { + if (encoding.scalabilityMode) { encoding.scalabilityMode = `L1T${layers.temporalLayers}`; - } - else - { + } else { encoding.scalabilityMode = 'L1T3'; } } } - this._remoteSdp!.send( - { - offerMediaObject, - reuseMid : mediaSectionIdx.reuseMid, - offerRtpParameters : sendingRtpParameters, - answerRtpParameters : sendingRemoteRtpParameters, - codecOptions - }); + this._remoteSdp!.send({ + offerMediaObject, + reuseMid: mediaSectionIdx.reuseMid, + offerRtpParameters: sendingRtpParameters, + answerRtpParameters: sendingRemoteRtpParameters, + codecOptions, + }); const answer = { type: 'answer', sdp: this._remoteSdp!.getSdp() }; logger.debug( 'send() | calling pc.setRemoteDescription() [answer:%o]', - answer); + answer, + ); await this._pc.setRemoteDescription(answer); @@ -476,17 +447,15 @@ export class Safari12 extends HandlerInterface return { localId, - rtpParameters : sendingRtpParameters, - rtpSender : transceiver.sender + rtpParameters: sendingRtpParameters, + rtpSender: transceiver.sender, }; } - async stopSending(localId: string): Promise - { + async stopSending(localId: string): Promise { this.assertSendDirection(); - if (this._closed) - { + if (this._closed) { return; } @@ -494,8 +463,7 @@ export class Safari12 extends HandlerInterface const transceiver = this._mapMidTransceiver.get(localId); - if (!transceiver) - { + if (!transceiver) { throw new Error('associated RTCRtpTransceiver not found'); } @@ -503,24 +471,22 @@ export class Safari12 extends HandlerInterface this._pc.removeTrack(transceiver.sender); - const mediaSectionClosed = - this._remoteSdp!.closeMediaSection(transceiver.mid!); + const mediaSectionClosed = this._remoteSdp!.closeMediaSection( + transceiver.mid!, + ); - if (mediaSectionClosed) - { - try - { + if (mediaSectionClosed) { + try { transceiver.stop(); - } - catch (error) - {} + } catch (error) {} } const offer = await this._pc.createOffer(); logger.debug( 'stopSending() | calling pc.setLocalDescription() [offer:%o]', - offer); + offer, + ); await this._pc.setLocalDescription(offer); @@ -528,7 +494,8 @@ export class Safari12 extends HandlerInterface logger.debug( 'stopSending() | calling pc.setRemoteDescription() [answer:%o]', - answer); + answer, + ); await this._pc.setRemoteDescription(answer); @@ -536,8 +503,7 @@ export class Safari12 extends HandlerInterface } // eslint-disable-next-line @typescript-eslint/no-unused-vars - async pauseSending(localId: string): Promise - { + async pauseSending(localId: string): Promise { this.assertNotClosed(); this.assertSendDirection(); @@ -545,8 +511,7 @@ export class Safari12 extends HandlerInterface const transceiver = this._mapMidTransceiver.get(localId); - if (!transceiver) - { + if (!transceiver) { throw new Error('associated RTCRtpTransceiver not found'); } @@ -557,7 +522,8 @@ export class Safari12 extends HandlerInterface logger.debug( 'pauseSending() | calling pc.setLocalDescription() [offer:%o]', - offer); + offer, + ); await this._pc.setLocalDescription(offer); @@ -565,14 +531,14 @@ export class Safari12 extends HandlerInterface logger.debug( 'pauseSending() | calling pc.setRemoteDescription() [answer:%o]', - answer); + answer, + ); await this._pc.setRemoteDescription(answer); } // eslint-disable-next-line @typescript-eslint/no-unused-vars - async resumeSending(localId: string): Promise - { + async resumeSending(localId: string): Promise { this.assertNotClosed(); this.assertSendDirection(); @@ -580,8 +546,7 @@ export class Safari12 extends HandlerInterface const transceiver = this._mapMidTransceiver.get(localId); - if (!transceiver) - { + if (!transceiver) { throw new Error('associated RTCRtpTransceiver not found'); } @@ -592,7 +557,8 @@ export class Safari12 extends HandlerInterface logger.debug( 'resumeSending() | calling pc.setLocalDescription() [offer:%o]', - offer); + offer, + ); await this._pc.setLocalDescription(offer); @@ -600,67 +566,68 @@ export class Safari12 extends HandlerInterface logger.debug( 'resumeSending() | calling pc.setRemoteDescription() [answer:%o]', - answer); + answer, + ); await this._pc.setRemoteDescription(answer); } async replaceTrack( - localId: string, track: MediaStreamTrack | null - ): Promise - { + localId: string, + track: MediaStreamTrack | null, + ): Promise { this.assertNotClosed(); this.assertSendDirection(); - if (track) - { + if (track) { logger.debug( - 'replaceTrack() [localId:%s, track.id:%s]', localId, track.id); - } - else - { + 'replaceTrack() [localId:%s, track.id:%s]', + localId, + track.id, + ); + } else { logger.debug('replaceTrack() [localId:%s, no track]', localId); } const transceiver = this._mapMidTransceiver.get(localId); - if (!transceiver) - { + if (!transceiver) { throw new Error('associated RTCRtpTransceiver not found'); } await transceiver.sender.replaceTrack(track); } - async setMaxSpatialLayer(localId: string, spatialLayer: number): Promise - { + async setMaxSpatialLayer( + localId: string, + spatialLayer: number, + ): Promise { this.assertNotClosed(); this.assertSendDirection(); logger.debug( 'setMaxSpatialLayer() [localId:%s, spatialLayer:%s]', - localId, spatialLayer); + localId, + spatialLayer, + ); const transceiver = this._mapMidTransceiver.get(localId); - if (!transceiver) - { + if (!transceiver) { throw new Error('associated RTCRtpTransceiver not found'); } const parameters = transceiver.sender.getParameters(); - parameters.encodings.forEach((encoding: RTCRtpEncodingParameters, idx: number) => - { - if (idx <= spatialLayer) - { - encoding.active = true; - } - else - { - encoding.active = false; - } - }); + parameters.encodings.forEach( + (encoding: RTCRtpEncodingParameters, idx: number) => { + if (idx <= spatialLayer) { + encoding.active = true; + } else { + encoding.active = false; + } + }, + ); await transceiver.sender.setParameters(parameters); @@ -670,7 +637,8 @@ export class Safari12 extends HandlerInterface logger.debug( 'setMaxSpatialLayer() | calling pc.setLocalDescription() [offer:%o]', - offer); + offer, + ); await this._pc.setLocalDescription(offer); @@ -678,33 +646,35 @@ export class Safari12 extends HandlerInterface logger.debug( 'setMaxSpatialLayer() | calling pc.setRemoteDescription() [answer:%o]', - answer); + answer, + ); await this._pc.setRemoteDescription(answer); } - async setRtpEncodingParameters(localId: string, params: any): Promise - { + async setRtpEncodingParameters(localId: string, params: any): Promise { this.assertNotClosed(); this.assertSendDirection(); logger.debug( 'setRtpEncodingParameters() [localId:%s, params:%o]', - localId, params); + localId, + params, + ); const transceiver = this._mapMidTransceiver.get(localId); - if (!transceiver) - { + if (!transceiver) { throw new Error('associated RTCRtpTransceiver not found'); } const parameters = transceiver.sender.getParameters(); - parameters.encodings.forEach((encoding: RTCRtpEncodingParameters, idx: number) => - { - parameters.encodings[idx] = { ...encoding, ...params }; - }); + parameters.encodings.forEach( + (encoding: RTCRtpEncodingParameters, idx: number) => { + parameters.encodings[idx] = { ...encoding, ...params }; + }, + ); await transceiver.sender.setParameters(parameters); @@ -714,7 +684,8 @@ export class Safari12 extends HandlerInterface logger.debug( 'setRtpEncodingParameters() | calling pc.setLocalDescription() [offer:%o]', - offer); + offer, + ); await this._pc.setLocalDescription(offer); @@ -722,47 +693,42 @@ export class Safari12 extends HandlerInterface logger.debug( 'setRtpEncodingParameters() | calling pc.setRemoteDescription() [answer:%o]', - answer); + answer, + ); await this._pc.setRemoteDescription(answer); } - async getSenderStats(localId: string): Promise - { + async getSenderStats(localId: string): Promise { this.assertNotClosed(); this.assertSendDirection(); const transceiver = this._mapMidTransceiver.get(localId); - if (!transceiver) - { + if (!transceiver) { throw new Error('associated RTCRtpTransceiver not found'); } return transceiver.sender.getStats(); } - async sendDataChannel( - { - ordered, - maxPacketLifeTime, - maxRetransmits, - label, - protocol - }: HandlerSendDataChannelOptions - ): Promise - { + async sendDataChannel({ + ordered, + maxPacketLifeTime, + maxRetransmits, + label, + protocol, + }: HandlerSendDataChannelOptions): Promise { this.assertNotClosed(); this.assertSendDirection(); - const options = - { - negotiated : true, - id : this._nextSendSctpStreamId, + const options = { + negotiated: true, + id: this._nextSendSctpStreamId, ordered, maxPacketLifeTime, maxRetransmits, - protocol + protocol, }; logger.debug('sendDataChannel() [options:%o]', options); @@ -775,25 +741,24 @@ export class Safari12 extends HandlerInterface // If this is the first DataChannel we need to create the SDP answer with // m=application section. - if (!this._hasDataChannelMediaSection) - { + if (!this._hasDataChannelMediaSection) { const offer = await this._pc.createOffer(); const localSdpObject = sdpTransform.parse(offer.sdp); - const offerMediaObject = localSdpObject.media - .find((m: any) => m.type === 'application'); + const offerMediaObject = localSdpObject.media.find( + (m: any) => m.type === 'application', + ); - if (!this._transportReady) - { - await this.setupTransport( - { - localDtlsRole : this._forcedLocalDtlsRole ?? 'client', - localSdpObject - }); + if (!this._transportReady) { + await this.setupTransport({ + localDtlsRole: this._forcedLocalDtlsRole ?? 'client', + localSdpObject, + }); } logger.debug( 'sendDataChannel() | calling pc.setLocalDescription() [offer:%o]', - offer); + offer, + ); await this._pc.setLocalDescription(offer); @@ -803,36 +768,34 @@ export class Safari12 extends HandlerInterface logger.debug( 'sendDataChannel() | calling pc.setRemoteDescription() [answer:%o]', - answer); + answer, + ); await this._pc.setRemoteDescription(answer); this._hasDataChannelMediaSection = true; } - const sctpStreamParameters: SctpStreamParameters = - { - streamId : options.id, - ordered : options.ordered, - maxPacketLifeTime : options.maxPacketLifeTime, - maxRetransmits : options.maxRetransmits + const sctpStreamParameters: SctpStreamParameters = { + streamId: options.id, + ordered: options.ordered, + maxPacketLifeTime: options.maxPacketLifeTime, + maxRetransmits: options.maxRetransmits, }; return { dataChannel, sctpStreamParameters }; } async receive( - optionsList: HandlerReceiveOptions[] - ) : Promise - { + optionsList: HandlerReceiveOptions[], + ): Promise { this.assertNotClosed(); this.assertRecvDirection(); const results: HandlerReceiveResult[] = []; const mapLocalId: Map = new Map(); - for (const options of optionsList) - { + for (const options of optionsList) { const { trackId, kind, rtpParameters, streamId } = options; logger.debug('receive() [trackId:%s, kind:%s]', trackId, kind); @@ -841,69 +804,66 @@ export class Safari12 extends HandlerInterface mapLocalId.set(trackId, localId); - this._remoteSdp!.receive( - { - mid : localId, - kind, - offerRtpParameters : rtpParameters, - streamId : streamId || rtpParameters.rtcp!.cname!, - trackId - }); + this._remoteSdp!.receive({ + mid: localId, + kind, + offerRtpParameters: rtpParameters, + streamId: streamId || rtpParameters.rtcp!.cname!, + trackId, + }); } const offer = { type: 'offer', sdp: this._remoteSdp!.getSdp() }; logger.debug( 'receive() | calling pc.setRemoteDescription() [offer:%o]', - offer); + offer, + ); await this._pc.setRemoteDescription(offer); let answer = await this._pc.createAnswer(); const localSdpObject = sdpTransform.parse(answer.sdp); - for (const options of optionsList) - { + for (const options of optionsList) { const { trackId, rtpParameters } = options; const localId = mapLocalId.get(trackId); - const answerMediaObject = localSdpObject.media - .find((m: any) => String(m.mid) === localId); + const answerMediaObject = localSdpObject.media.find( + (m: any) => String(m.mid) === localId, + ); // May need to modify codec parameters in the answer based on codec // parameters in the offer. - sdpCommonUtils.applyCodecParameters( - { - offerRtpParameters : rtpParameters, - answerMediaObject - }); + sdpCommonUtils.applyCodecParameters({ + offerRtpParameters: rtpParameters, + answerMediaObject, + }); } answer = { type: 'answer', sdp: sdpTransform.write(localSdpObject) }; - if (!this._transportReady) - { - await this.setupTransport( - { - localDtlsRole : this._forcedLocalDtlsRole ?? 'client', - localSdpObject - }); + if (!this._transportReady) { + await this.setupTransport({ + localDtlsRole: this._forcedLocalDtlsRole ?? 'client', + localSdpObject, + }); } logger.debug( 'receive() | calling pc.setLocalDescription() [answer:%o]', - answer); + answer, + ); await this._pc.setLocalDescription(answer); - for (const options of optionsList) - { + for (const options of optionsList) { const { trackId } = options; const localId = mapLocalId.get(trackId)!; - const transceiver = this._pc.getTransceivers() + const transceiver = this._pc + .getTransceivers() .find((t: RTCRtpTransceiver) => t.mid === localId); - if (!transceiver) - { + if (!transceiver) { throw new Error('new RTCRtpTransceiver not found'); } @@ -912,31 +872,27 @@ export class Safari12 extends HandlerInterface results.push({ localId, - track : transceiver.receiver.track, - rtpReceiver : transceiver.receiver + track: transceiver.receiver.track, + rtpReceiver: transceiver.receiver, }); } return results; } - async stopReceiving(localIds: string[]): Promise - { + async stopReceiving(localIds: string[]): Promise { this.assertRecvDirection(); - if (this._closed) - { + if (this._closed) { return; } - for (const localId of localIds) - { + for (const localId of localIds) { logger.debug('stopReceiving() [localId:%s]', localId); const transceiver = this._mapMidTransceiver.get(localId); - if (!transceiver) - { + if (!transceiver) { throw new Error('associated RTCRtpTransceiver not found'); } @@ -947,7 +903,8 @@ export class Safari12 extends HandlerInterface logger.debug( 'stopReceiving() | calling pc.setRemoteDescription() [offer:%o]', - offer); + offer, + ); await this._pc.setRemoteDescription(offer); @@ -955,29 +912,26 @@ export class Safari12 extends HandlerInterface logger.debug( 'stopReceiving() | calling pc.setLocalDescription() [answer:%o]', - answer); + answer, + ); await this._pc.setLocalDescription(answer); - for (const localId of localIds) - { + for (const localId of localIds) { this._mapMidTransceiver.delete(localId); } } - async pauseReceiving(localIds: string[]): Promise - { + async pauseReceiving(localIds: string[]): Promise { this.assertNotClosed(); this.assertRecvDirection(); - for (const localId of localIds) - { + for (const localId of localIds) { logger.debug('pauseReceiving() [localId:%s]', localId); const transceiver = this._mapMidTransceiver.get(localId); - if (!transceiver) - { + if (!transceiver) { throw new Error('associated RTCRtpTransceiver not found'); } @@ -989,7 +943,8 @@ export class Safari12 extends HandlerInterface logger.debug( 'pauseReceiving() | calling pc.setRemoteDescription() [offer:%o]', - offer); + offer, + ); await this._pc.setRemoteDescription(offer); @@ -997,24 +952,22 @@ export class Safari12 extends HandlerInterface logger.debug( 'pauseReceiving() | calling pc.setLocalDescription() [answer:%o]', - answer); + answer, + ); await this._pc.setLocalDescription(answer); } - async resumeReceiving(localIds: string[]): Promise - { + async resumeReceiving(localIds: string[]): Promise { this.assertNotClosed(); this.assertRecvDirection(); - for (const localId of localIds) - { + for (const localId of localIds) { logger.debug('resumeReceiving() [localId:%s]', localId); const transceiver = this._mapMidTransceiver.get(localId); - if (!transceiver) - { + if (!transceiver) { throw new Error('associated RTCRtpTransceiver not found'); } @@ -1026,7 +979,8 @@ export class Safari12 extends HandlerInterface logger.debug( 'resumeReceiving() | calling pc.setRemoteDescription() [offer:%o]', - offer); + offer, + ); await this._pc.setRemoteDescription(offer); @@ -1034,30 +988,30 @@ export class Safari12 extends HandlerInterface logger.debug( 'resumeReceiving() | calling pc.setLocalDescription() [answer:%o]', - answer); + answer, + ); await this._pc.setLocalDescription(answer); } - async getReceiverStats(localId: string): Promise - { + async getReceiverStats(localId: string): Promise { this.assertNotClosed(); this.assertRecvDirection(); const transceiver = this._mapMidTransceiver.get(localId); - if (!transceiver) - { + if (!transceiver) { throw new Error('associated RTCRtpTransceiver not found'); } return transceiver.receiver.getStats(); } - async receiveDataChannel( - { sctpStreamParameters, label, protocol }: HandlerReceiveDataChannelOptions - ): Promise - { + async receiveDataChannel({ + sctpStreamParameters, + label, + protocol, + }: HandlerReceiveDataChannelOptions): Promise { this.assertNotClosed(); this.assertRecvDirection(); @@ -1065,17 +1019,16 @@ export class Safari12 extends HandlerInterface streamId, ordered, maxPacketLifeTime, - maxRetransmits + maxRetransmits, }: SctpStreamParameters = sctpStreamParameters; - const options = - { - negotiated : true, - id : streamId, + const options = { + negotiated: true, + id: streamId, ordered, maxPacketLifeTime, maxRetransmits, - protocol + protocol, }; logger.debug('receiveDataChannel() [options:%o]', options); @@ -1084,34 +1037,33 @@ export class Safari12 extends HandlerInterface // If this is the first DataChannel we need to create the SDP offer with // m=application section. - if (!this._hasDataChannelMediaSection) - { + if (!this._hasDataChannelMediaSection) { this._remoteSdp!.receiveSctpAssociation(); const offer = { type: 'offer', sdp: this._remoteSdp!.getSdp() }; logger.debug( 'receiveDataChannel() | calling pc.setRemoteDescription() [offer:%o]', - offer); + offer, + ); await this._pc.setRemoteDescription(offer); const answer = await this._pc.createAnswer(); - if (!this._transportReady) - { + if (!this._transportReady) { const localSdpObject = sdpTransform.parse(answer.sdp); - await this.setupTransport( - { - localDtlsRole : this._forcedLocalDtlsRole ?? 'client', - localSdpObject - }); + await this.setupTransport({ + localDtlsRole: this._forcedLocalDtlsRole ?? 'client', + localSdpObject, + }); } logger.debug( 'receiveDataChannel() | calling pc.setRemoteDescription() [answer:%o]', - answer); + answer, + ); await this._pc.setLocalDescription(answer); @@ -1121,70 +1073,57 @@ export class Safari12 extends HandlerInterface return { dataChannel }; } - private async setupTransport( - { - localDtlsRole, - localSdpObject - }: - { - localDtlsRole: DtlsRole; - localSdpObject?: any; - } - ): Promise - { - if (!localSdpObject) - { + private async setupTransport({ + localDtlsRole, + localSdpObject, + }: { + localDtlsRole: DtlsRole; + localSdpObject?: any; + }): Promise { + if (!localSdpObject) { localSdpObject = sdpTransform.parse(this._pc.localDescription.sdp); } // Get our local DTLS parameters. - const dtlsParameters = - sdpCommonUtils.extractDtlsParameters({ sdpObject: localSdpObject }); + const dtlsParameters = sdpCommonUtils.extractDtlsParameters({ + sdpObject: localSdpObject, + }); // Set our DTLS role. dtlsParameters.role = localDtlsRole; // Update the remote DTLS role in the SDP. this._remoteSdp!.updateDtlsRole( - localDtlsRole === 'client' ? 'server' : 'client'); + localDtlsRole === 'client' ? 'server' : 'client', + ); // Need to tell the remote transport about our parameters. - await new Promise((resolve, reject) => - { - this.safeEmit( - '@connect', - { dtlsParameters }, - resolve, - reject - ); + await new Promise((resolve, reject) => { + this.safeEmit('@connect', { dtlsParameters }, resolve, reject); }); this._transportReady = true; } - private assertNotClosed(): void - { - if (this._closed) - { + private assertNotClosed(): void { + if (this._closed) { throw new InvalidStateError('method called in a closed handler'); } } - private assertSendDirection(): void - { - if (this._direction !== 'send') - { + private assertSendDirection(): void { + if (this._direction !== 'send') { throw new Error( - 'method can just be called for handlers with "send" direction'); + 'method can just be called for handlers with "send" direction', + ); } } - private assertRecvDirection(): void - { - if (this._direction !== 'recv') - { + private assertRecvDirection(): void { + if (this._direction !== 'recv') { throw new Error( - 'method can just be called for handlers with "recv" direction'); + 'method can just be called for handlers with "recv" direction', + ); } } } diff --git a/src/handlers/ortc/edgeUtils.ts b/src/handlers/ortc/edgeUtils.ts index 571acda3..5af11246 100644 --- a/src/handlers/ortc/edgeUtils.ts +++ b/src/handlers/ortc/edgeUtils.ts @@ -5,13 +5,11 @@ import { RtpCapabilities, RtpParameters } from '../../RtpParameters'; * Normalize ORTC based Edge's RTCRtpReceiver.getCapabilities() to produce a full * compliant ORTC RTCRtpCapabilities. */ -export function getCapabilities(): RtpCapabilities -{ +export function getCapabilities(): RtpCapabilities { const nativeCaps = (RTCRtpReceiver as any).getCapabilities(); const caps = utils.clone(nativeCaps); - for (const codec of caps.codecs ?? []) - { + for (const codec of caps.codecs ?? []) { // Rename numChannels to channels. // @ts-ignore codec.channels = codec.numChannels; @@ -23,26 +21,23 @@ export function getCapabilities(): RtpCapabilities codec.mimeType = codec.mimeType || `${codec.kind}/${codec.name}`; // NOTE: Edge sets some numeric parameters as string rather than number. Fix them. - if (codec.parameters) - { + if (codec.parameters) { const parameters = codec.parameters; - if (parameters.apt) - { + if (parameters.apt) { parameters.apt = Number(parameters.apt); } - if (parameters['packetization-mode']) - { - parameters['packetization-mode'] = Number(parameters['packetization-mode']); + if (parameters['packetization-mode']) { + parameters['packetization-mode'] = Number( + parameters['packetization-mode'], + ); } } // Delete emty parameter String in rtcpFeedback. - for (const feedback of codec.rtcpFeedback || []) - { - if (!feedback.parameter) - { + for (const feedback of codec.rtcpFeedback || []) { + if (!feedback.parameter) { feedback.parameter = ''; } } @@ -54,23 +49,21 @@ export function getCapabilities(): RtpCapabilities /** * Generate RTCRtpParameters as ORTC based Edge likes. */ -export function mangleRtpParameters(rtpParameters: RtpParameters): RtpParameters -{ +export function mangleRtpParameters( + rtpParameters: RtpParameters, +): RtpParameters { const params = utils.clone(rtpParameters); // Rename mid to muxId. - if (params.mid) - { + if (params.mid) { // @ts-ignore (due to muxId). params.muxId = params.mid; delete params.mid; } - for (const codec of params.codecs) - { + for (const codec of params.codecs) { // Rename channels to numChannels. - if (codec.channels) - { + if (codec.channels) { // @ts-ignore. codec.numChannels = codec.channels; delete codec.channels; @@ -78,8 +71,7 @@ export function mangleRtpParameters(rtpParameters: RtpParameters): RtpParameters // Add codec.name (requried by Edge). // @ts-ignore (due to name). - if (codec.mimeType && !codec.name) - { + if (codec.mimeType && !codec.name) { // @ts-ignore (due to name). codec.name = codec.mimeType.split('/')[1]; } diff --git a/src/handlers/ortc/utils.ts b/src/handlers/ortc/utils.ts index 7bee29a7..a6366bc1 100644 --- a/src/handlers/ortc/utils.ts +++ b/src/handlers/ortc/utils.ts @@ -3,20 +3,14 @@ import { RtpCapabilities } from '../../RtpParameters'; /** * This function adds RTCP NACK support for OPUS codec in given capabilities. */ -export function addNackSuppportForOpus(rtpCapabilities: RtpCapabilities): void -{ - for (const codec of (rtpCapabilities.codecs || [])) - { +export function addNackSuppportForOpus(rtpCapabilities: RtpCapabilities): void { + for (const codec of rtpCapabilities.codecs || []) { if ( - ( - codec.mimeType.toLowerCase() === 'audio/opus' || - codec.mimeType.toLowerCase() === 'audio/multiopus' - ) && - !codec.rtcpFeedback?.some((fb) => fb.type === 'nack' && !fb.parameter) - ) - { - if (!codec.rtcpFeedback) - { + (codec.mimeType.toLowerCase() === 'audio/opus' || + codec.mimeType.toLowerCase() === 'audio/multiopus') && + !codec.rtcpFeedback?.some(fb => fb.type === 'nack' && !fb.parameter) + ) { + if (!codec.rtcpFeedback) { codec.rtcpFeedback = []; } diff --git a/src/handlers/sdp/MediaSection.ts b/src/handlers/sdp/MediaSection.ts index 8f91e32e..89661e75 100644 --- a/src/handlers/sdp/MediaSection.ts +++ b/src/handlers/sdp/MediaSection.ts @@ -5,7 +5,7 @@ import { IceCandidate, DtlsParameters, DtlsRole, - PlainRtpParameters + PlainRtpParameters, } from '../../Transport'; import { ProducerCodecOptions } from '../../Producer'; import { @@ -13,46 +13,38 @@ import { RtpParameters, RtpCodecParameters, RtcpFeedback, - RtpHeaderExtensionParameters + RtpHeaderExtensionParameters, } from '../../RtpParameters'; import { SctpParameters } from '../../SctpParameters'; -export abstract class MediaSection -{ +export abstract class MediaSection { // SDP media object. protected readonly _mediaObject: any; // Whether this is Plan-B SDP. protected readonly _planB: boolean; - constructor( - { - iceParameters, - iceCandidates, - dtlsParameters, - planB = false - }: - { - iceParameters?: IceParameters; - iceCandidates?: IceCandidate[]; - dtlsParameters?: DtlsParameters; - planB: boolean; - } - ) - { + constructor({ + iceParameters, + iceCandidates, + dtlsParameters, + planB = false, + }: { + iceParameters?: IceParameters; + iceCandidates?: IceCandidate[]; + dtlsParameters?: DtlsParameters; + planB: boolean; + }) { this._mediaObject = {}; this._planB = planB; - if (iceParameters) - { + if (iceParameters) { this.setIceParameters(iceParameters); } - if (iceCandidates) - { + if (iceCandidates) { this._mediaObject.candidates = []; - for (const candidate of iceCandidates) - { + for (const candidate of iceCandidates) { const candidateObject: any = {}; // mediasoup does mandates rtcp-mux so candidates component is always @@ -65,8 +57,7 @@ export abstract class MediaSection candidateObject.transport = candidate.protocol; candidateObject.type = candidate.type; - if (candidate.tcpType) - { + if (candidate.tcpType) { candidateObject.tcptype = candidate.tcpType; } @@ -77,44 +68,37 @@ export abstract class MediaSection this._mediaObject.iceOptions = 'renomination'; } - if (dtlsParameters) - { + if (dtlsParameters) { this.setDtlsRole(dtlsParameters.role!); } } abstract setDtlsRole(role: DtlsRole): void; - get mid(): string - { + get mid(): string { return String(this._mediaObject.mid); } - get closed(): boolean - { + get closed(): boolean { return this._mediaObject.port === 0; } - getObject(): object - { + getObject(): object { return this._mediaObject; } - setIceParameters(iceParameters: IceParameters): void - { + setIceParameters(iceParameters: IceParameters): void { this._mediaObject.iceUfrag = iceParameters.usernameFragment; this._mediaObject.icePwd = iceParameters.password; } - pause(): void - { + pause(): void { this._mediaObject.direction = 'inactive'; } abstract resume(): void; - disable(): void - { + disable(): void { this.pause(); delete this._mediaObject.ext; @@ -126,97 +110,82 @@ export abstract class MediaSection delete this._mediaObject.extmapAllowMixed; } - close(): void - { + close(): void { this.disable(); this._mediaObject.port = 0; } } -export class AnswerMediaSection extends MediaSection -{ - constructor( - { - iceParameters, - iceCandidates, - dtlsParameters, - sctpParameters, - plainRtpParameters, - planB = false, - offerMediaObject, - offerRtpParameters, - answerRtpParameters, - codecOptions, - extmapAllowMixed = false - }: - { - iceParameters?: IceParameters; - iceCandidates?: IceCandidate[]; - dtlsParameters?: DtlsParameters; - sctpParameters?: SctpParameters; - plainRtpParameters?: PlainRtpParameters; - planB?: boolean; - offerMediaObject: any; - offerRtpParameters?: RtpParameters; - answerRtpParameters?: RtpParameters; - codecOptions?: ProducerCodecOptions; - extmapAllowMixed?: boolean; - } - ) - { +export class AnswerMediaSection extends MediaSection { + constructor({ + iceParameters, + iceCandidates, + dtlsParameters, + sctpParameters, + plainRtpParameters, + planB = false, + offerMediaObject, + offerRtpParameters, + answerRtpParameters, + codecOptions, + extmapAllowMixed = false, + }: { + iceParameters?: IceParameters; + iceCandidates?: IceCandidate[]; + dtlsParameters?: DtlsParameters; + sctpParameters?: SctpParameters; + plainRtpParameters?: PlainRtpParameters; + planB?: boolean; + offerMediaObject: any; + offerRtpParameters?: RtpParameters; + answerRtpParameters?: RtpParameters; + codecOptions?: ProducerCodecOptions; + extmapAllowMixed?: boolean; + }) { super({ iceParameters, iceCandidates, dtlsParameters, planB }); this._mediaObject.mid = String(offerMediaObject.mid); this._mediaObject.type = offerMediaObject.type; this._mediaObject.protocol = offerMediaObject.protocol; - if (!plainRtpParameters) - { + if (!plainRtpParameters) { this._mediaObject.connection = { ip: '127.0.0.1', version: 4 }; this._mediaObject.port = 7; - } - else - { - this._mediaObject.connection = - { - ip : plainRtpParameters.ip, - version : plainRtpParameters.ipVersion + } else { + this._mediaObject.connection = { + ip: plainRtpParameters.ip, + version: plainRtpParameters.ipVersion, }; this._mediaObject.port = plainRtpParameters.port; } - switch (offerMediaObject.type) - { + switch (offerMediaObject.type) { case 'audio': - case 'video': - { + case 'video': { this._mediaObject.direction = 'recvonly'; this._mediaObject.rtp = []; this._mediaObject.rtcpFb = []; this._mediaObject.fmtp = []; - for (const codec of answerRtpParameters!.codecs) - { - const rtp: any = - { - payload : codec.payloadType, - codec : getCodecName(codec), - rate : codec.clockRate + for (const codec of answerRtpParameters!.codecs) { + const rtp: any = { + payload: codec.payloadType, + codec: getCodecName(codec), + rate: codec.clockRate, }; - if (codec.channels! > 1) - { + if (codec.channels! > 1) { rtp.encoding = codec.channels; } this._mediaObject.rtp.push(rtp); const codecParameters = utils.clone(codec.parameters) ?? {}; - let codecRtcpFeedback: RtcpFeedback[] = utils.clone(codec.rtcpFeedback) ?? []; + let codecRtcpFeedback: RtcpFeedback[] = + utils.clone(codec.rtcpFeedback) ?? []; - if (codecOptions) - { + if (codecOptions) { const { opusStereo, opusFec, @@ -227,49 +196,40 @@ export class AnswerMediaSection extends MediaSection opusNack, videoGoogleStartBitrate, videoGoogleMaxBitrate, - videoGoogleMinBitrate + videoGoogleMinBitrate, } = codecOptions; - const offerCodec = offerRtpParameters!.codecs - .find((c: RtpCodecParameters) => ( - c.payloadType === codec.payloadType - )); + const offerCodec = offerRtpParameters!.codecs.find( + (c: RtpCodecParameters) => c.payloadType === codec.payloadType, + ); - switch (codec.mimeType.toLowerCase()) - { + switch (codec.mimeType.toLowerCase()) { case 'audio/opus': - case 'audio/multiopus': - { - if (opusStereo !== undefined) - { + case 'audio/multiopus': { + if (opusStereo !== undefined) { offerCodec!.parameters['sprop-stereo'] = opusStereo ? 1 : 0; codecParameters.stereo = opusStereo ? 1 : 0; } - if (opusFec !== undefined) - { + if (opusFec !== undefined) { offerCodec!.parameters.useinbandfec = opusFec ? 1 : 0; codecParameters.useinbandfec = opusFec ? 1 : 0; } - if (opusDtx !== undefined) - { + if (opusDtx !== undefined) { offerCodec!.parameters.usedtx = opusDtx ? 1 : 0; codecParameters.usedtx = opusDtx ? 1 : 0; } - if (opusMaxPlaybackRate !== undefined) - { + if (opusMaxPlaybackRate !== undefined) { codecParameters.maxplaybackrate = opusMaxPlaybackRate; } - if (opusMaxAverageBitrate !== undefined) - { + if (opusMaxAverageBitrate !== undefined) { codecParameters.maxaveragebitrate = opusMaxAverageBitrate; } - if (opusPtime !== undefined) - { + if (opusPtime !== undefined) { offerCodec!.parameters.ptime = opusPtime; codecParameters.ptime = opusPtime; } @@ -277,14 +237,14 @@ export class AnswerMediaSection extends MediaSection // If opusNack is not set, we must remove NACK support for OPUS. // Otherwise it would be enabled for those handlers that artificially // announce it in their RTP capabilities. - if (!opusNack) - { - offerCodec!.rtcpFeedback = offerCodec! - .rtcpFeedback! - .filter((fb) => fb.type !== 'nack' || fb.parameter); - - codecRtcpFeedback = codecRtcpFeedback - .filter((fb) => fb.type !== 'nack' || fb.parameter); + if (!opusNack) { + offerCodec!.rtcpFeedback = offerCodec!.rtcpFeedback!.filter( + fb => fb.type !== 'nack' || fb.parameter, + ); + + codecRtcpFeedback = codecRtcpFeedback.filter( + fb => fb.type !== 'nack' || fb.parameter, + ); } break; @@ -293,21 +253,20 @@ export class AnswerMediaSection extends MediaSection case 'video/vp8': case 'video/vp9': case 'video/h264': - case 'video/h265': - { - if (videoGoogleStartBitrate !== undefined) - { - codecParameters['x-google-start-bitrate'] = videoGoogleStartBitrate; + case 'video/h265': { + if (videoGoogleStartBitrate !== undefined) { + codecParameters['x-google-start-bitrate'] = + videoGoogleStartBitrate; } - if (videoGoogleMaxBitrate !== undefined) - { - codecParameters['x-google-max-bitrate'] = videoGoogleMaxBitrate; + if (videoGoogleMaxBitrate !== undefined) { + codecParameters['x-google-max-bitrate'] = + videoGoogleMaxBitrate; } - if (videoGoogleMinBitrate !== undefined) - { - codecParameters['x-google-min-bitrate'] = videoGoogleMinBitrate; + if (videoGoogleMinBitrate !== undefined) { + codecParameters['x-google-min-bitrate'] = + videoGoogleMinBitrate; } break; @@ -315,35 +274,29 @@ export class AnswerMediaSection extends MediaSection } } - const fmtp = - { - payload : codec.payloadType, - config : '' + const fmtp = { + payload: codec.payloadType, + config: '', }; - for (const key of Object.keys(codecParameters)) - { - if (fmtp.config) - { + for (const key of Object.keys(codecParameters)) { + if (fmtp.config) { fmtp.config += ';'; } fmtp.config += `${key}=${codecParameters[key]}`; } - if (fmtp.config) - { + if (fmtp.config) { this._mediaObject.fmtp.push(fmtp); } - for (const fb of codecRtcpFeedback) - { - this._mediaObject.rtcpFb.push( - { - payload : codec.payloadType, - type : fb.type, - subtype : fb.parameter - }); + for (const fb of codecRtcpFeedback) { + this._mediaObject.rtcpFb.push({ + payload: codec.payloadType, + type: fb.type, + subtype: fb.parameter, + }); } } @@ -353,113 +306,96 @@ export class AnswerMediaSection extends MediaSection this._mediaObject.ext = []; - for (const ext of answerRtpParameters!.headerExtensions!) - { + for (const ext of answerRtpParameters!.headerExtensions!) { // Don't add a header extension if not present in the offer. - const found = (offerMediaObject.ext || []) - .some((localExt: RtpHeaderExtensionParameters) => localExt.uri === ext.uri); + const found = (offerMediaObject.ext || []).some( + (localExt: RtpHeaderExtensionParameters) => + localExt.uri === ext.uri, + ); - if (!found) - { + if (!found) { continue; } - this._mediaObject.ext.push( - { - uri : ext.uri, - value : ext.id - }); + this._mediaObject.ext.push({ + uri: ext.uri, + value: ext.id, + }); } // Allow both 1 byte and 2 bytes length header extensions. if ( extmapAllowMixed && offerMediaObject.extmapAllowMixed === 'extmap-allow-mixed' - ) - { + ) { this._mediaObject.extmapAllowMixed = 'extmap-allow-mixed'; } // Simulcast. - if (offerMediaObject.simulcast) - { - this._mediaObject.simulcast = - { - dir1 : 'recv', - list1 : offerMediaObject.simulcast.list1 + if (offerMediaObject.simulcast) { + this._mediaObject.simulcast = { + dir1: 'recv', + list1: offerMediaObject.simulcast.list1, }; this._mediaObject.rids = []; - for (const rid of offerMediaObject.rids || []) - { - if (rid.direction !== 'send') - { + for (const rid of offerMediaObject.rids || []) { + if (rid.direction !== 'send') { continue; } - this._mediaObject.rids.push( - { - id : rid.id, - direction : 'recv' - }); + this._mediaObject.rids.push({ + id: rid.id, + direction: 'recv', + }); } } // Simulcast (draft version 03). - else if (offerMediaObject.simulcast_03) - { + else if (offerMediaObject.simulcast_03) { // eslint-disable-next-line camelcase - this._mediaObject.simulcast_03 = - { - value : offerMediaObject.simulcast_03.value.replace(/send/g, 'recv') + this._mediaObject.simulcast_03 = { + value: offerMediaObject.simulcast_03.value.replace(/send/g, 'recv'), }; this._mediaObject.rids = []; - for (const rid of offerMediaObject.rids || []) - { - if (rid.direction !== 'send') - { + for (const rid of offerMediaObject.rids || []) { + if (rid.direction !== 'send') { continue; } - this._mediaObject.rids.push( - { - id : rid.id, - direction : 'recv' - }); + this._mediaObject.rids.push({ + id: rid.id, + direction: 'recv', + }); } } this._mediaObject.rtcpMux = 'rtcp-mux'; this._mediaObject.rtcpRsize = 'rtcp-rsize'; - if (this._planB && this._mediaObject.type === 'video') - { + if (this._planB && this._mediaObject.type === 'video') { this._mediaObject.xGoogleFlag = 'conference'; } break; } - case 'application': - { + case 'application': { // New spec. - if (typeof offerMediaObject.sctpPort === 'number') - { + if (typeof offerMediaObject.sctpPort === 'number') { this._mediaObject.payloads = 'webrtc-datachannel'; this._mediaObject.sctpPort = sctpParameters!.port; this._mediaObject.maxMessageSize = sctpParameters!.maxMessageSize; } // Old spec. - else if (offerMediaObject.sctpmap) - { + else if (offerMediaObject.sctpmap) { this._mediaObject.payloads = sctpParameters!.port; - this._mediaObject.sctpmap = - { - app : 'webrtc-datachannel', - sctpmapNumber : sctpParameters!.port, - maxMessageSize : sctpParameters!.maxMessageSize + this._mediaObject.sctpmap = { + app: 'webrtc-datachannel', + sctpmapNumber: sctpParameters!.port, + maxMessageSize: sctpParameters!.maxMessageSize, }; } @@ -468,26 +404,21 @@ export class AnswerMediaSection extends MediaSection } } - setDtlsRole(role: DtlsRole): void - { - switch (role) - { - case 'client': - { + setDtlsRole(role: DtlsRole): void { + switch (role) { + case 'client': { this._mediaObject.setup = 'active'; break; } - case 'server': - { + case 'server': { this._mediaObject.setup = 'passive'; break; } - case 'auto': - { + case 'auto': { this._mediaObject.setup = 'actpass'; break; @@ -495,173 +426,141 @@ export class AnswerMediaSection extends MediaSection } } - resume(): void - { + resume(): void { this._mediaObject.direction = 'recvonly'; } - muxSimulcastStreams(encodings: RTCRtpEncodingParameters[]): void - { - if (!this._mediaObject.simulcast || !this._mediaObject.simulcast.list1) - { + muxSimulcastStreams(encodings: RTCRtpEncodingParameters[]): void { + if (!this._mediaObject.simulcast || !this._mediaObject.simulcast.list1) { return; } - const layers: {[rid: string | number]: RTCRtpEncodingParameters} = {}; + const layers: { [rid: string | number]: RTCRtpEncodingParameters } = {}; - for (const encoding of encodings) - { - if (encoding.rid) - { + for (const encoding of encodings) { + if (encoding.rid) { layers[encoding.rid] = encoding; } } const raw = this._mediaObject.simulcast.list1; - const simulcastStreams = - sdpTransform.parseSimulcastStreamList(raw); + const simulcastStreams = sdpTransform.parseSimulcastStreamList(raw); - for (const simulcastStream of simulcastStreams) - { - for (const simulcastFormat of simulcastStream) - { + for (const simulcastStream of simulcastStreams) { + for (const simulcastFormat of simulcastStream) { simulcastFormat.paused = !layers[simulcastFormat.scid]?.active; } } - this._mediaObject.simulcast.list1 = simulcastStreams.map((simulcastFormats) => - simulcastFormats.map((f) => - `${f.paused ? '~' : ''}${f.scid}` - ).join(',') - ).join(';'); + this._mediaObject.simulcast.list1 = simulcastStreams + .map(simulcastFormats => + simulcastFormats.map(f => `${f.paused ? '~' : ''}${f.scid}`).join(','), + ) + .join(';'); } } -export class OfferMediaSection extends MediaSection -{ - constructor( - { - iceParameters, - iceCandidates, - dtlsParameters, - sctpParameters, - plainRtpParameters, - planB = false, - mid, - kind, - offerRtpParameters, - streamId, - trackId, - oldDataChannelSpec = false - }: - { - iceParameters?: IceParameters; - iceCandidates?: IceCandidate[]; - dtlsParameters?: DtlsParameters; - sctpParameters?: SctpParameters; - plainRtpParameters?: PlainRtpParameters; - planB?: boolean; - mid: string; - kind: MediaKind | 'application'; - offerRtpParameters?: RtpParameters; - streamId?: string; - trackId?: string; - oldDataChannelSpec?: boolean; - } - ) - { +export class OfferMediaSection extends MediaSection { + constructor({ + iceParameters, + iceCandidates, + dtlsParameters, + sctpParameters, + plainRtpParameters, + planB = false, + mid, + kind, + offerRtpParameters, + streamId, + trackId, + oldDataChannelSpec = false, + }: { + iceParameters?: IceParameters; + iceCandidates?: IceCandidate[]; + dtlsParameters?: DtlsParameters; + sctpParameters?: SctpParameters; + plainRtpParameters?: PlainRtpParameters; + planB?: boolean; + mid: string; + kind: MediaKind | 'application'; + offerRtpParameters?: RtpParameters; + streamId?: string; + trackId?: string; + oldDataChannelSpec?: boolean; + }) { super({ iceParameters, iceCandidates, dtlsParameters, planB }); this._mediaObject.mid = String(mid); this._mediaObject.type = kind; - if (!plainRtpParameters) - { + if (!plainRtpParameters) { this._mediaObject.connection = { ip: '127.0.0.1', version: 4 }; - if (!sctpParameters) - { + if (!sctpParameters) { this._mediaObject.protocol = 'UDP/TLS/RTP/SAVPF'; - } - else - { + } else { this._mediaObject.protocol = 'UDP/DTLS/SCTP'; } this._mediaObject.port = 7; - } - else - { - this._mediaObject.connection = - { - ip : plainRtpParameters.ip, - version : plainRtpParameters.ipVersion + } else { + this._mediaObject.connection = { + ip: plainRtpParameters.ip, + version: plainRtpParameters.ipVersion, }; this._mediaObject.protocol = 'RTP/AVP'; this._mediaObject.port = plainRtpParameters.port; } - switch (kind) - { + switch (kind) { case 'audio': - case 'video': - { + case 'video': { this._mediaObject.direction = 'sendonly'; this._mediaObject.rtp = []; this._mediaObject.rtcpFb = []; this._mediaObject.fmtp = []; - if (!this._planB) - { + if (!this._planB) { this._mediaObject.msid = `${streamId || '-'} ${trackId}`; } - for (const codec of offerRtpParameters!.codecs) - { - const rtp: any = - { - payload : codec.payloadType, - codec : getCodecName(codec), - rate : codec.clockRate + for (const codec of offerRtpParameters!.codecs) { + const rtp: any = { + payload: codec.payloadType, + codec: getCodecName(codec), + rate: codec.clockRate, }; - if (codec.channels! > 1) - { + if (codec.channels! > 1) { rtp.encoding = codec.channels; } this._mediaObject.rtp.push(rtp); - const fmtp = - { - payload : codec.payloadType, - config : '' + const fmtp = { + payload: codec.payloadType, + config: '', }; - for (const key of Object.keys(codec.parameters)) - { - if (fmtp.config) - { + for (const key of Object.keys(codec.parameters)) { + if (fmtp.config) { fmtp.config += ';'; } fmtp.config += `${key}=${codec.parameters[key]}`; } - if (fmtp.config) - { + if (fmtp.config) { this._mediaObject.fmtp.push(fmtp); } - for (const fb of codec.rtcpFeedback!) - { - this._mediaObject.rtcpFb.push( - { - payload : codec.payloadType, - type : fb.type, - subtype : fb.parameter - }); + for (const fb of codec.rtcpFeedback!) { + this._mediaObject.rtcpFb.push({ + payload: codec.payloadType, + type: fb.type, + subtype: fb.parameter, + }); } } @@ -671,13 +570,11 @@ export class OfferMediaSection extends MediaSection this._mediaObject.ext = []; - for (const ext of offerRtpParameters!.headerExtensions!) - { - this._mediaObject.ext.push( - { - uri : ext.uri, - value : ext.id - }); + for (const ext of offerRtpParameters!.headerExtensions!) { + this._mediaObject.ext.push({ + uri: ext.uri, + value: ext.id, + }); } this._mediaObject.rtcpMux = 'rtcp-mux'; @@ -685,84 +582,69 @@ export class OfferMediaSection extends MediaSection const encoding = offerRtpParameters!.encodings![0]; const ssrc = encoding.ssrc; - const rtxSsrc = (encoding.rtx && encoding.rtx.ssrc) - ? encoding.rtx.ssrc - : undefined; + const rtxSsrc = + encoding.rtx && encoding.rtx.ssrc ? encoding.rtx.ssrc : undefined; this._mediaObject.ssrcs = []; this._mediaObject.ssrcGroups = []; - if (offerRtpParameters!.rtcp!.cname) - { - this._mediaObject.ssrcs.push( - { - id : ssrc, - attribute : 'cname', - value : offerRtpParameters!.rtcp!.cname - }); + if (offerRtpParameters!.rtcp!.cname) { + this._mediaObject.ssrcs.push({ + id: ssrc, + attribute: 'cname', + value: offerRtpParameters!.rtcp!.cname, + }); } - if (this._planB) - { - this._mediaObject.ssrcs.push( - { - id : ssrc, - attribute : 'msid', - value : `${streamId || '-'} ${trackId}` - }); + if (this._planB) { + this._mediaObject.ssrcs.push({ + id: ssrc, + attribute: 'msid', + value: `${streamId || '-'} ${trackId}`, + }); } - if (rtxSsrc) - { - if (offerRtpParameters!.rtcp!.cname) - { - this._mediaObject.ssrcs.push( - { - id : rtxSsrc, - attribute : 'cname', - value : offerRtpParameters!.rtcp!.cname - }); + if (rtxSsrc) { + if (offerRtpParameters!.rtcp!.cname) { + this._mediaObject.ssrcs.push({ + id: rtxSsrc, + attribute: 'cname', + value: offerRtpParameters!.rtcp!.cname, + }); } - if (this._planB) - { - this._mediaObject.ssrcs.push( - { - id : rtxSsrc, - attribute : 'msid', - value : `${streamId || '-'} ${trackId}` - }); + if (this._planB) { + this._mediaObject.ssrcs.push({ + id: rtxSsrc, + attribute: 'msid', + value: `${streamId || '-'} ${trackId}`, + }); } // Associate original and retransmission SSRCs. - this._mediaObject.ssrcGroups.push( - { - semantics : 'FID', - ssrcs : `${ssrc} ${rtxSsrc}` - }); + this._mediaObject.ssrcGroups.push({ + semantics: 'FID', + ssrcs: `${ssrc} ${rtxSsrc}`, + }); } break; } - case 'application': - { + case 'application': { // New spec. - if (!oldDataChannelSpec) - { + if (!oldDataChannelSpec) { this._mediaObject.payloads = 'webrtc-datachannel'; this._mediaObject.sctpPort = sctpParameters!.port; this._mediaObject.maxMessageSize = sctpParameters!.maxMessageSize; } // Old spec. - else - { + else { this._mediaObject.payloads = sctpParameters!.port; - this._mediaObject.sctpmap = - { - app : 'webrtc-datachannel', - sctpmapNumber : sctpParameters!.port, - maxMessageSize : sctpParameters!.maxMessageSize + this._mediaObject.sctpmap = { + app: 'webrtc-datachannel', + sctpmapNumber: sctpParameters!.port, + maxMessageSize: sctpParameters!.maxMessageSize, }; } @@ -772,172 +654,147 @@ export class OfferMediaSection extends MediaSection } // eslint-disable-next-line @typescript-eslint/no-unused-vars - setDtlsRole(role: DtlsRole): void - { + setDtlsRole(role: DtlsRole): void { // Always 'actpass'. this._mediaObject.setup = 'actpass'; } - resume(): void - { + resume(): void { this._mediaObject.direction = 'sendonly'; } - planBReceive( - { - offerRtpParameters, - streamId, - trackId - }: - { - offerRtpParameters: RtpParameters; - streamId: string; - trackId: string; - } - ): void - { + planBReceive({ + offerRtpParameters, + streamId, + trackId, + }: { + offerRtpParameters: RtpParameters; + streamId: string; + trackId: string; + }): void { const encoding = offerRtpParameters.encodings![0]; const ssrc = encoding.ssrc; - const rtxSsrc = (encoding.rtx && encoding.rtx.ssrc) - ? encoding.rtx.ssrc - : undefined; + const rtxSsrc = + encoding.rtx && encoding.rtx.ssrc ? encoding.rtx.ssrc : undefined; const payloads = this._mediaObject.payloads.split(' '); - for (const codec of offerRtpParameters.codecs) - { - if (payloads.includes(String(codec.payloadType))) - { + for (const codec of offerRtpParameters.codecs) { + if (payloads.includes(String(codec.payloadType))) { continue; } - const rtp: any = - { - payload : codec.payloadType, - codec : getCodecName(codec), - rate : codec.clockRate + const rtp: any = { + payload: codec.payloadType, + codec: getCodecName(codec), + rate: codec.clockRate, }; - if (codec.channels! > 1) - { + if (codec.channels! > 1) { rtp.encoding = codec.channels; } this._mediaObject.rtp.push(rtp); - const fmtp = - { - payload : codec.payloadType, - config : '' + const fmtp = { + payload: codec.payloadType, + config: '', }; - for (const key of Object.keys(codec.parameters)) - { - if (fmtp.config) - { + for (const key of Object.keys(codec.parameters)) { + if (fmtp.config) { fmtp.config += ';'; } fmtp.config += `${key}=${codec.parameters[key]}`; } - if (fmtp.config) - { + if (fmtp.config) { this._mediaObject.fmtp.push(fmtp); } - for (const fb of codec.rtcpFeedback!) - { - this._mediaObject.rtcpFb.push( - { - payload : codec.payloadType, - type : fb.type, - subtype : fb.parameter - }); + for (const fb of codec.rtcpFeedback!) { + this._mediaObject.rtcpFb.push({ + payload: codec.payloadType, + type: fb.type, + subtype: fb.parameter, + }); } } - this._mediaObject.payloads += ` ${offerRtpParameters - .codecs - .filter((codec: RtpCodecParameters) => - !this._mediaObject.payloads.includes(codec.payloadType)) + this._mediaObject.payloads += ` ${offerRtpParameters.codecs + .filter( + (codec: RtpCodecParameters) => + !this._mediaObject.payloads.includes(codec.payloadType), + ) .map((codec: RtpCodecParameters) => codec.payloadType) .join(' ')}`; this._mediaObject.payloads = this._mediaObject.payloads.trim(); - if (offerRtpParameters.rtcp!.cname) - { - this._mediaObject.ssrcs.push( - { - id : ssrc, - attribute : 'cname', - value : offerRtpParameters.rtcp!.cname - }); - } - - this._mediaObject.ssrcs.push( - { - id : ssrc, - attribute : 'msid', - value : `${streamId || '-'} ${trackId}` + if (offerRtpParameters.rtcp!.cname) { + this._mediaObject.ssrcs.push({ + id: ssrc, + attribute: 'cname', + value: offerRtpParameters.rtcp!.cname, }); + } - if (rtxSsrc) - { - if (offerRtpParameters.rtcp!.cname) - { - this._mediaObject.ssrcs.push( - { - id : rtxSsrc, - attribute : 'cname', - value : offerRtpParameters.rtcp!.cname - }); + this._mediaObject.ssrcs.push({ + id: ssrc, + attribute: 'msid', + value: `${streamId || '-'} ${trackId}`, + }); + + if (rtxSsrc) { + if (offerRtpParameters.rtcp!.cname) { + this._mediaObject.ssrcs.push({ + id: rtxSsrc, + attribute: 'cname', + value: offerRtpParameters.rtcp!.cname, + }); } - this._mediaObject.ssrcs.push( - { - id : rtxSsrc, - attribute : 'msid', - value : `${streamId || '-'} ${trackId}` - }); + this._mediaObject.ssrcs.push({ + id: rtxSsrc, + attribute: 'msid', + value: `${streamId || '-'} ${trackId}`, + }); // Associate original and retransmission SSRCs. - this._mediaObject.ssrcGroups.push( - { - semantics : 'FID', - ssrcs : `${ssrc} ${rtxSsrc}` - }); + this._mediaObject.ssrcGroups.push({ + semantics: 'FID', + ssrcs: `${ssrc} ${rtxSsrc}`, + }); } } - planBStopReceiving( - { offerRtpParameters }: { offerRtpParameters: RtpParameters } - ): void - { + planBStopReceiving({ + offerRtpParameters, + }: { + offerRtpParameters: RtpParameters; + }): void { const encoding = offerRtpParameters.encodings![0]; const ssrc = encoding.ssrc; - const rtxSsrc = (encoding.rtx && encoding.rtx.ssrc) - ? encoding.rtx.ssrc - : undefined; + const rtxSsrc = + encoding.rtx && encoding.rtx.ssrc ? encoding.rtx.ssrc : undefined; - this._mediaObject.ssrcs = this._mediaObject.ssrcs - .filter((s: any) => s.id !== ssrc && s.id !== rtxSsrc); + this._mediaObject.ssrcs = this._mediaObject.ssrcs.filter( + (s: any) => s.id !== ssrc && s.id !== rtxSsrc, + ); - if (rtxSsrc) - { - this._mediaObject.ssrcGroups = this._mediaObject.ssrcGroups - .filter((group: any) => group.ssrcs !== `${ssrc} ${rtxSsrc}`); + if (rtxSsrc) { + this._mediaObject.ssrcGroups = this._mediaObject.ssrcGroups.filter( + (group: any) => group.ssrcs !== `${ssrc} ${rtxSsrc}`, + ); } } } -function getCodecName(codec: RtpCodecParameters): string -{ +function getCodecName(codec: RtpCodecParameters): string { const MimeTypeRegex = new RegExp('^(audio|video)/(.+)', 'i'); const mimeTypeMatch = MimeTypeRegex.exec(codec.mimeType); - if (!mimeTypeMatch) - { + if (!mimeTypeMatch) { throw new TypeError('invalid codec.mimeType'); } diff --git a/src/handlers/sdp/RemoteSdp.ts b/src/handlers/sdp/RemoteSdp.ts index a728dee4..18207a7e 100644 --- a/src/handlers/sdp/RemoteSdp.ts +++ b/src/handlers/sdp/RemoteSdp.ts @@ -3,14 +3,14 @@ import { Logger } from '../../Logger'; import { MediaSection, AnswerMediaSection, - OfferMediaSection + OfferMediaSection, } from './MediaSection'; import { IceParameters, IceCandidate, DtlsParameters, DtlsRole, - PlainRtpParameters + PlainRtpParameters, } from '../../Transport'; import { ProducerCodecOptions } from '../../Producer'; import { MediaKind, RtpParameters } from '../../RtpParameters'; @@ -18,8 +18,7 @@ import { SctpParameters } from '../../SctpParameters'; const logger = new Logger('RemoteSdp'); -export class RemoteSdp -{ +export class RemoteSdp { // Remote ICE parameters. private _iceParameters?: IceParameters; // Remote ICE candidates. @@ -41,115 +40,96 @@ export class RemoteSdp // SDP object. private readonly _sdpObject: any; - constructor( - { - iceParameters, - iceCandidates, - dtlsParameters, - sctpParameters, - plainRtpParameters, - planB = false - }: - { - iceParameters?: IceParameters; - iceCandidates?: IceCandidate[]; - dtlsParameters?: DtlsParameters; - sctpParameters?: SctpParameters; - plainRtpParameters?: PlainRtpParameters; - planB?: boolean; - } - ) - { + constructor({ + iceParameters, + iceCandidates, + dtlsParameters, + sctpParameters, + plainRtpParameters, + planB = false, + }: { + iceParameters?: IceParameters; + iceCandidates?: IceCandidate[]; + dtlsParameters?: DtlsParameters; + sctpParameters?: SctpParameters; + plainRtpParameters?: PlainRtpParameters; + planB?: boolean; + }) { this._iceParameters = iceParameters; this._iceCandidates = iceCandidates; this._dtlsParameters = dtlsParameters; this._sctpParameters = sctpParameters; this._plainRtpParameters = plainRtpParameters; this._planB = planB; - this._sdpObject = - { - version : 0, - origin : - { - address : '0.0.0.0', - ipVer : 4, - netType : 'IN', - sessionId : 10000, - sessionVersion : 0, - username : 'mediasoup-client' + this._sdpObject = { + version: 0, + origin: { + address: '0.0.0.0', + ipVer: 4, + netType: 'IN', + sessionId: 10000, + sessionVersion: 0, + username: 'mediasoup-client', }, - name : '-', - timing : { start: 0, stop: 0 }, - media : [] + name: '-', + timing: { start: 0, stop: 0 }, + media: [], }; // If ICE parameters are given, add ICE-Lite indicator. - if (iceParameters && iceParameters.iceLite) - { + if (iceParameters && iceParameters.iceLite) { this._sdpObject.icelite = 'ice-lite'; } // If DTLS parameters are given, assume WebRTC and BUNDLE. - if (dtlsParameters) - { + if (dtlsParameters) { this._sdpObject.msidSemantic = { semantic: 'WMS', token: '*' }; // NOTE: We take the latest fingerprint. const numFingerprints = this._dtlsParameters!.fingerprints.length; - this._sdpObject.fingerprint = - { - type : dtlsParameters.fingerprints[numFingerprints - 1].algorithm, - hash : dtlsParameters.fingerprints[numFingerprints - 1].value + this._sdpObject.fingerprint = { + type: dtlsParameters.fingerprints[numFingerprints - 1].algorithm, + hash: dtlsParameters.fingerprints[numFingerprints - 1].value, }; - this._sdpObject.groups = [ { type: 'BUNDLE', mids: '' } ]; + this._sdpObject.groups = [{ type: 'BUNDLE', mids: '' }]; } // If there are plain RPT parameters, override SDP origin. - if (plainRtpParameters) - { + if (plainRtpParameters) { this._sdpObject.origin.address = plainRtpParameters.ip; this._sdpObject.origin.ipVer = plainRtpParameters.ipVersion; } } - updateIceParameters(iceParameters: IceParameters): void - { - logger.debug( - 'updateIceParameters() [iceParameters:%o]', - iceParameters); + updateIceParameters(iceParameters: IceParameters): void { + logger.debug('updateIceParameters() [iceParameters:%o]', iceParameters); this._iceParameters = iceParameters; this._sdpObject.icelite = iceParameters.iceLite ? 'ice-lite' : undefined; - for (const mediaSection of this._mediaSections) - { + for (const mediaSection of this._mediaSections) { mediaSection.setIceParameters(iceParameters); } } - updateDtlsRole(role: DtlsRole): void - { + updateDtlsRole(role: DtlsRole): void { logger.debug('updateDtlsRole() [role:%s]', role); this._dtlsParameters!.role = role; - for (const mediaSection of this._mediaSections) - { + for (const mediaSection of this._mediaSections) { mediaSection.setDtlsRole(role); } } - getNextMediaSectionIdx(): { idx: number; reuseMid?: string } - { + getNextMediaSectionIdx(): { idx: number; reuseMid?: string } { // If a closed media section is found, return its index. - for (let idx = 0; idx < this._mediaSections.length; ++idx) - { + for (let idx = 0; idx < this._mediaSections.length; ++idx) { const mediaSection = this._mediaSections[idx]; - if (mediaSection.closed) - { + if (mediaSection.closed) { return { idx, reuseMid: mediaSection.mid }; } } @@ -158,143 +138,120 @@ export class RemoteSdp return { idx: this._mediaSections.length }; } - send( - { + send({ + offerMediaObject, + reuseMid, + offerRtpParameters, + answerRtpParameters, + codecOptions, + extmapAllowMixed = false, + }: { + offerMediaObject: any; + reuseMid?: string; + offerRtpParameters: RtpParameters; + answerRtpParameters: RtpParameters; + codecOptions?: ProducerCodecOptions; + extmapAllowMixed?: boolean; + }): void { + const mediaSection = new AnswerMediaSection({ + iceParameters: this._iceParameters, + iceCandidates: this._iceCandidates, + dtlsParameters: this._dtlsParameters, + plainRtpParameters: this._plainRtpParameters, + planB: this._planB, offerMediaObject, - reuseMid, offerRtpParameters, answerRtpParameters, codecOptions, - extmapAllowMixed = false - }: - { - offerMediaObject: any; - reuseMid?: string; - offerRtpParameters: RtpParameters; - answerRtpParameters: RtpParameters; - codecOptions?: ProducerCodecOptions; - extmapAllowMixed? : boolean; - } - ): void - { - const mediaSection = new AnswerMediaSection( - { - iceParameters : this._iceParameters, - iceCandidates : this._iceCandidates, - dtlsParameters : this._dtlsParameters, - plainRtpParameters : this._plainRtpParameters, - planB : this._planB, - offerMediaObject, - offerRtpParameters, - answerRtpParameters, - codecOptions, - extmapAllowMixed - }); + extmapAllowMixed, + }); // Unified-Plan with closed media section replacement. - if (reuseMid) - { + if (reuseMid) { this._replaceMediaSection(mediaSection, reuseMid); } // Unified-Plan or Plan-B with different media kind. - else if (!this._midToIndex.has(mediaSection.mid)) - { + else if (!this._midToIndex.has(mediaSection.mid)) { this._addMediaSection(mediaSection); } // Plan-B with same media kind. - else - { + else { this._replaceMediaSection(mediaSection); } } - receive( - { - mid, - kind, - offerRtpParameters, - streamId, - trackId - }: - { - mid: string; - kind: MediaKind; - offerRtpParameters: RtpParameters; - streamId: string; - trackId: string; - } - ): void - { + receive({ + mid, + kind, + offerRtpParameters, + streamId, + trackId, + }: { + mid: string; + kind: MediaKind; + offerRtpParameters: RtpParameters; + streamId: string; + trackId: string; + }): void { const idx = this._midToIndex.get(mid); let mediaSection: OfferMediaSection | undefined; - if (idx !== undefined) - { + if (idx !== undefined) { mediaSection = this._mediaSections[idx] as OfferMediaSection; } // Unified-Plan or different media kind. - if (!mediaSection) - { - mediaSection = new OfferMediaSection( - { - iceParameters : this._iceParameters, - iceCandidates : this._iceCandidates, - dtlsParameters : this._dtlsParameters, - plainRtpParameters : this._plainRtpParameters, - planB : this._planB, - mid, - kind, - offerRtpParameters, - streamId, - trackId - }); + if (!mediaSection) { + mediaSection = new OfferMediaSection({ + iceParameters: this._iceParameters, + iceCandidates: this._iceCandidates, + dtlsParameters: this._dtlsParameters, + plainRtpParameters: this._plainRtpParameters, + planB: this._planB, + mid, + kind, + offerRtpParameters, + streamId, + trackId, + }); // Let's try to recycle a closed media section (if any). // NOTE: Yes, we can recycle a closed m=audio section with a new m=video. - const oldMediaSection = this._mediaSections.find((m) => (m.closed)); + const oldMediaSection = this._mediaSections.find(m => m.closed); - if (oldMediaSection) - { + if (oldMediaSection) { this._replaceMediaSection(mediaSection, oldMediaSection.mid); - } - else - { + } else { this._addMediaSection(mediaSection); } } // Plan-B. - else - { + else { mediaSection.planBReceive({ offerRtpParameters, streamId, trackId }); this._replaceMediaSection(mediaSection); } } - pauseMediaSection(mid: string): void - { + pauseMediaSection(mid: string): void { const mediaSection = this._findMediaSection(mid); mediaSection.pause(); } - resumeSendingMediaSection(mid: string): void - { + resumeSendingMediaSection(mid: string): void { const mediaSection = this._findMediaSection(mid); mediaSection.resume(); } - resumeReceivingMediaSection(mid: string): void - { + resumeReceivingMediaSection(mid: string): void { const mediaSection = this._findMediaSection(mid); mediaSection.resume(); } - disableMediaSection(mid: string): void - { + disableMediaSection(mid: string): void { const mediaSection = this._findMediaSection(mid); mediaSection.disable(); @@ -307,17 +264,16 @@ export class RemoteSdp * NOTE: Closing the first m section is a pain since it invalidates the bundled * transport, so instead closing it we just disable it. */ - closeMediaSection(mid: string): boolean - { + closeMediaSection(mid: string): boolean { const mediaSection = this._findMediaSection(mid); // NOTE: Closing the first m section is a pain since it invalidates the // bundled transport, so let's avoid it. - if (mid === this._firstMid) - { + if (mid === this._firstMid) { logger.debug( 'closeMediaSection() | cannot close first media section, disabling it instead [mid:%s]', - mid); + mid, + ); this.disableMediaSection(mid); @@ -333,9 +289,9 @@ export class RemoteSdp } muxMediaSectionSimulcast( - mid: string, encodings: RTCRtpEncodingParameters[] - ): void - { + mid: string, + encodings: RTCRtpEncodingParameters[], + ): void { const mediaSection = this._findMediaSection(mid) as AnswerMediaSection; mediaSection.muxSimulcastStreams(encodings); @@ -343,17 +299,13 @@ export class RemoteSdp this._replaceMediaSection(mediaSection); } - planBStopReceiving( - { - mid, - offerRtpParameters - }: - { - mid: string; - offerRtpParameters: RtpParameters; - } - ): void - { + planBStopReceiving({ + mid, + offerRtpParameters, + }: { + mid: string; + offerRtpParameters: RtpParameters; + }): void { const mediaSection = this._findMediaSection(mid) as OfferMediaSection; mediaSection.planBStopReceiving({ offerRtpParameters }); @@ -361,53 +313,45 @@ export class RemoteSdp this._replaceMediaSection(mediaSection); } - sendSctpAssociation({ offerMediaObject }: { offerMediaObject: any }): void - { - const mediaSection = new AnswerMediaSection( - { - iceParameters : this._iceParameters, - iceCandidates : this._iceCandidates, - dtlsParameters : this._dtlsParameters, - sctpParameters : this._sctpParameters, - plainRtpParameters : this._plainRtpParameters, - offerMediaObject - }); + sendSctpAssociation({ offerMediaObject }: { offerMediaObject: any }): void { + const mediaSection = new AnswerMediaSection({ + iceParameters: this._iceParameters, + iceCandidates: this._iceCandidates, + dtlsParameters: this._dtlsParameters, + sctpParameters: this._sctpParameters, + plainRtpParameters: this._plainRtpParameters, + offerMediaObject, + }); this._addMediaSection(mediaSection); } - receiveSctpAssociation( - { oldDataChannelSpec = false }: - { oldDataChannelSpec?: boolean } = {} - ): void - { - const mediaSection = new OfferMediaSection( - { - iceParameters : this._iceParameters, - iceCandidates : this._iceCandidates, - dtlsParameters : this._dtlsParameters, - sctpParameters : this._sctpParameters, - plainRtpParameters : this._plainRtpParameters, - mid : 'datachannel', - kind : 'application', - oldDataChannelSpec - }); + receiveSctpAssociation({ + oldDataChannelSpec = false, + }: { oldDataChannelSpec?: boolean } = {}): void { + const mediaSection = new OfferMediaSection({ + iceParameters: this._iceParameters, + iceCandidates: this._iceCandidates, + dtlsParameters: this._dtlsParameters, + sctpParameters: this._sctpParameters, + plainRtpParameters: this._plainRtpParameters, + mid: 'datachannel', + kind: 'application', + oldDataChannelSpec, + }); this._addMediaSection(mediaSection); } - getSdp(): string - { + getSdp(): string { // Increase SDP version. this._sdpObject.origin.sessionVersion++; return sdpTransform.write(this._sdpObject); } - _addMediaSection(newMediaSection: MediaSection): void - { - if (!this._firstMid) - { + _addMediaSection(newMediaSection: MediaSection): void { + if (!this._firstMid) { this._firstMid = newMediaSection.mid; } @@ -424,15 +368,12 @@ export class RemoteSdp this._regenerateBundleMids(); } - _replaceMediaSection(newMediaSection: MediaSection, reuseMid?: string): void - { + _replaceMediaSection(newMediaSection: MediaSection, reuseMid?: string): void { // Store it in the map. - if (typeof reuseMid === 'string') - { + if (typeof reuseMid === 'string') { const idx = this._midToIndex.get(reuseMid); - if (idx === undefined) - { + if (idx === undefined) { throw new Error(`no media section found for reuseMid '${reuseMid}'`); } @@ -450,15 +391,13 @@ export class RemoteSdp // Regenerate BUNDLE mids. this._regenerateBundleMids(); - } - else - { + } else { const idx = this._midToIndex.get(newMediaSection.mid); - if (idx === undefined) - { + if (idx === undefined) { throw new Error( - `no media section found with mid '${newMediaSection.mid}'`); + `no media section found with mid '${newMediaSection.mid}'`, + ); } // Replace the index in the vector with the new media section. @@ -469,22 +408,18 @@ export class RemoteSdp } } - _findMediaSection(mid: string): MediaSection - { + _findMediaSection(mid: string): MediaSection { const idx = this._midToIndex.get(mid); - if (idx === undefined) - { + if (idx === undefined) { throw new Error(`no media section found with mid '${mid}'`); } return this._mediaSections[idx]; } - _regenerateBundleMids(): void - { - if (!this._dtlsParameters) - { + _regenerateBundleMids(): void { + if (!this._dtlsParameters) { return; } diff --git a/src/handlers/sdp/commonUtils.ts b/src/handlers/sdp/commonUtils.ts index 6a5dde08..918c0e24 100644 --- a/src/handlers/sdp/commonUtils.ts +++ b/src/handlers/sdp/commonUtils.ts @@ -5,18 +5,18 @@ import { RtpCodecCapability, RtpHeaderExtension, RtpParameters, - RtcpFeedback + RtcpFeedback, } from '../../RtpParameters'; /** * This function must be called with an SDP with 1 m=audio and 1 m=video * sections. */ -export function extractRtpCapabilities( - { sdpObject }: - { sdpObject: any } -): RtpCapabilities -{ +export function extractRtpCapabilities({ + sdpObject, +}: { + sdpObject: any; +}): RtpCapabilities { // Map of RtpCodecParameters indexed by payload type. const codecsMap: Map = new Map(); // Array of RtpHeaderExtensions. @@ -25,16 +25,12 @@ export function extractRtpCapabilities( let gotAudio = false; let gotVideo = false; - for (const m of sdpObject.media) - { + for (const m of sdpObject.media) { const kind = m.type; - switch (kind) - { - case 'audio': - { - if (gotAudio) - { + switch (kind) { + case 'audio': { + if (gotAudio) { continue; } @@ -43,10 +39,8 @@ export function extractRtpCapabilities( break; } - case 'video': - { - if (gotVideo) - { + case 'video': { + if (gotVideo) { continue; } @@ -55,43 +49,37 @@ export function extractRtpCapabilities( break; } - default: - { + default: { continue; } } // Get codecs. - for (const rtp of m.rtp) - { - const codec: RtpCodecCapability = - { - kind : kind, - mimeType : `${kind}/${rtp.codec}`, - preferredPayloadType : rtp.payload, - clockRate : rtp.rate, - channels : rtp.encoding, - parameters : {}, - rtcpFeedback : [] + for (const rtp of m.rtp) { + const codec: RtpCodecCapability = { + kind: kind, + mimeType: `${kind}/${rtp.codec}`, + preferredPayloadType: rtp.payload, + clockRate: rtp.rate, + channels: rtp.encoding, + parameters: {}, + rtcpFeedback: [], }; codecsMap.set(codec.preferredPayloadType!, codec); } // Get codec parameters. - for (const fmtp of m.fmtp || []) - { + for (const fmtp of m.fmtp || []) { const parameters = sdpTransform.parseParams(fmtp.config); const codec = codecsMap.get(fmtp.payload); - if (!codec) - { + if (!codec) { continue; } // Specials case to convert parameter value to string. - if (parameters && parameters.hasOwnProperty('profile-level-id')) - { + if (parameters && parameters.hasOwnProperty('profile-level-id')) { parameters['profile-level-id'] = String(parameters['profile-level-id']); } @@ -99,27 +87,22 @@ export function extractRtpCapabilities( } // Get RTCP feedback for each codec. - for (const fb of m.rtcpFb || []) - { - const feedback: RtcpFeedback = - { - type : fb.type, - parameter : fb.subtype + for (const fb of m.rtcpFb || []) { + const feedback: RtcpFeedback = { + type: fb.type, + parameter: fb.subtype, }; - if (!feedback.parameter) - { + if (!feedback.parameter) { delete feedback.parameter; } // rtcp-fb payload is not '*', so just apply it to its corresponding // codec. - if (fb.payload !== '*') - { + if (fb.payload !== '*') { const codec = codecsMap.get(fb.payload); - if (!codec) - { + if (!codec) { continue; } @@ -127,12 +110,9 @@ export function extractRtpCapabilities( } // If rtcp-fb payload is '*' it must be applied to all codecs with same // kind (with some exceptions such as RTX codec). - else - { - for (const codec of codecsMap.values()) - { - if (codec.kind === kind && !/.+\/rtx$/i.test(codec.mimeType)) - { + else { + for (const codec of codecsMap.values()) { + if (codec.kind === kind && !/.+\/rtx$/i.test(codec.mimeType)) { codec.rtcpFeedback!.push(feedback); } } @@ -140,114 +120,100 @@ export function extractRtpCapabilities( } // Get RTP header extensions. - for (const ext of m.ext || []) - { + for (const ext of m.ext || []) { // Ignore encrypted extensions (not yet supported in mediasoup). - if (ext['encrypt-uri']) - { + if (ext['encrypt-uri']) { continue; } - const headerExtension: RtpHeaderExtension = - { - kind : kind, - uri : ext.uri, - preferredId : ext.value + const headerExtension: RtpHeaderExtension = { + kind: kind, + uri: ext.uri, + preferredId: ext.value, }; headerExtensions.push(headerExtension); } } - const rtpCapabilities: RtpCapabilities = - { - codecs : Array.from(codecsMap.values()), - headerExtensions : headerExtensions + const rtpCapabilities: RtpCapabilities = { + codecs: Array.from(codecsMap.values()), + headerExtensions: headerExtensions, }; return rtpCapabilities; } -export function extractDtlsParameters( - { sdpObject }: - { sdpObject: any } -): DtlsParameters -{ +export function extractDtlsParameters({ + sdpObject, +}: { + sdpObject: any; +}): DtlsParameters { let setup = sdpObject.setup; let fingerprint = sdpObject.fingerprint; - if (!setup || !fingerprint) - { - const mediaObject = (sdpObject.media || []) - .find((m: { port: number }) => (m.port !== 0)); + if (!setup || !fingerprint) { + const mediaObject = (sdpObject.media || []).find( + (m: { port: number }) => m.port !== 0, + ); - if (mediaObject) - { + if (mediaObject) { setup ??= mediaObject.setup; fingerprint ??= mediaObject.fingerprint; } } - if (!setup) - { + if (!setup) { throw new Error('no a=setup found at SDP session or media level'); - } - else if (!fingerprint) - { + } else if (!fingerprint) { throw new Error('no a=fingerprint found at SDP session or media level'); } let role: DtlsRole | undefined; - switch (setup) - { - case 'active': - { + switch (setup) { + case 'active': { role = 'client'; break; } - case 'passive': - { + case 'passive': { role = 'server'; break; } - case 'actpass': - { + case 'actpass': { role = 'auto'; break; } } - const dtlsParameters: DtlsParameters = - { + const dtlsParameters: DtlsParameters = { role, - fingerprints : - [ + fingerprints: [ { - algorithm : fingerprint.type, - value : fingerprint.hash - } - ] + algorithm: fingerprint.type, + value: fingerprint.hash, + }, + ], }; return dtlsParameters; } -export function getCname( - { offerMediaObject }: - { offerMediaObject: any } -): string -{ - const ssrcCnameLine = (offerMediaObject.ssrcs || []) - .find((line: { attribute: string }) => line.attribute === 'cname'); +export function getCname({ + offerMediaObject, +}: { + offerMediaObject: any; +}): string { + const ssrcCnameLine = (offerMediaObject.ssrcs || []).find( + (line: { attribute: string }) => line.attribute === 'cname', + ); - if (!ssrcCnameLine) - { + if (!ssrcCnameLine) { return ''; } @@ -258,57 +224,48 @@ export function getCname( * Apply codec parameters in the given SDP m= section answer based on the * given RTP parameters of an offer. */ -export function applyCodecParameters( - { - offerRtpParameters, - answerMediaObject - }: - { - offerRtpParameters: RtpParameters; - answerMediaObject: any; - } -): void -{ - for (const codec of offerRtpParameters.codecs) - { +export function applyCodecParameters({ + offerRtpParameters, + answerMediaObject, +}: { + offerRtpParameters: RtpParameters; + answerMediaObject: any; +}): void { + for (const codec of offerRtpParameters.codecs) { const mimeType = codec.mimeType.toLowerCase(); // Avoid parsing codec parameters for unhandled codecs. - if (mimeType !== 'audio/opus') - { + if (mimeType !== 'audio/opus') { continue; } - const rtp = (answerMediaObject.rtp || []) - .find((r: { payload: number }) => r.payload === codec.payloadType); + const rtp = (answerMediaObject.rtp || []).find( + (r: { payload: number }) => r.payload === codec.payloadType, + ); - if (!rtp) - { + if (!rtp) { continue; } // Just in case. answerMediaObject.fmtp = answerMediaObject.fmtp || []; - let fmtp = answerMediaObject.fmtp - .find((f: { payload: number }) => f.payload === codec.payloadType); + let fmtp = answerMediaObject.fmtp.find( + (f: { payload: number }) => f.payload === codec.payloadType, + ); - if (!fmtp) - { + if (!fmtp) { fmtp = { payload: codec.payloadType, config: '' }; answerMediaObject.fmtp.push(fmtp); } const parameters = sdpTransform.parseParams(fmtp.config); - switch (mimeType) - { - case 'audio/opus': - { + switch (mimeType) { + case 'audio/opus': { const spropStereo = codec.parameters['sprop-stereo']; - if (spropStereo !== undefined) - { + if (spropStereo !== undefined) { parameters.stereo = spropStereo ? 1 : 0; } @@ -319,10 +276,8 @@ export function applyCodecParameters( // Write the codec fmtp.config back. fmtp.config = ''; - for (const key of Object.keys(parameters)) - { - if (fmtp.config) - { + for (const key of Object.keys(parameters)) { + if (fmtp.config) { fmtp.config += ';'; } diff --git a/src/handlers/sdp/plainRtpUtils.ts b/src/handlers/sdp/plainRtpUtils.ts index 25dfcaa2..c568a646 100644 --- a/src/handlers/sdp/plainRtpUtils.ts +++ b/src/handlers/sdp/plainRtpUtils.ts @@ -1,66 +1,50 @@ import { MediaKind, RtpEncodingParameters } from '../../RtpParameters'; -export function extractPlainRtpParameters( - { - sdpObject, - kind - }: - { - sdpObject: any; - kind: MediaKind; - } -): -{ +export function extractPlainRtpParameters({ + sdpObject, + kind, +}: { + sdpObject: any; + kind: MediaKind; +}): { ip: string; ipVersion: 4 | 6; port: number; -} -{ - const mediaObject = (sdpObject.media || []) - .find((m: any) => m.type === kind); +} { + const mediaObject = (sdpObject.media || []).find((m: any) => m.type === kind); - if (!mediaObject) - { + if (!mediaObject) { throw new Error(`m=${kind} section not found`); } const connectionObject = mediaObject.connection || sdpObject.connection; return { - ip : connectionObject.ip, - ipVersion : connectionObject.version, - port : mediaObject.port + ip: connectionObject.ip, + ipVersion: connectionObject.version, + port: mediaObject.port, }; } -export function getRtpEncodings( - { - sdpObject, - kind - }: - { - sdpObject: any; - kind: MediaKind; - } -): RtpEncodingParameters[] -{ - const mediaObject = (sdpObject.media || []) - .find((m: any) => m.type === kind); +export function getRtpEncodings({ + sdpObject, + kind, +}: { + sdpObject: any; + kind: MediaKind; +}): RtpEncodingParameters[] { + const mediaObject = (sdpObject.media || []).find((m: any) => m.type === kind); - if (!mediaObject) - { + if (!mediaObject) { throw new Error(`m=${kind} section not found`); } const ssrcCnameLine = (mediaObject.ssrcs || [])[0]; const ssrc = ssrcCnameLine ? ssrcCnameLine.id : null; - if (ssrc) - { - return [ { ssrc } ]; - } - else - { + if (ssrc) { + return [{ ssrc }]; + } else { return []; } } diff --git a/src/handlers/sdp/planBUtils.ts b/src/handlers/sdp/planBUtils.ts index c9066c5b..2522366d 100644 --- a/src/handlers/sdp/planBUtils.ts +++ b/src/handlers/sdp/planBUtils.ts @@ -1,64 +1,54 @@ import { RtpEncodingParameters } from '../../RtpParameters'; -export function getRtpEncodings( - { - offerMediaObject, - track - }: - { - offerMediaObject: any; - track: MediaStreamTrack; - } -): RtpEncodingParameters[] -{ +export function getRtpEncodings({ + offerMediaObject, + track, +}: { + offerMediaObject: any; + track: MediaStreamTrack; +}): RtpEncodingParameters[] { // First media SSRC (or the only one). let firstSsrc; const ssrcs = new Set(); - for (const line of offerMediaObject.ssrcs || []) - { - if (line.attribute !== 'msid') - { + for (const line of offerMediaObject.ssrcs || []) { + if (line.attribute !== 'msid') { continue; } const trackId = line.value.split(' ')[1]; - if (trackId === track.id) - { + if (trackId === track.id) { const ssrc = line.id; ssrcs.add(ssrc); - if (!firstSsrc) - { + if (!firstSsrc) { firstSsrc = ssrc; } } } - if (ssrcs.size === 0) - { - throw new Error(`a=ssrc line with msid information not found [track.id:${track.id}]`); + if (ssrcs.size === 0) { + throw new Error( + `a=ssrc line with msid information not found [track.id:${track.id}]`, + ); } const ssrcToRtxSsrc = new Map(); // First assume RTX is used. - for (const line of offerMediaObject.ssrcGroups || []) - { - if (line.semantics !== 'FID') - { + for (const line of offerMediaObject.ssrcGroups || []) { + if (line.semantics !== 'FID') { continue; } - let [ ssrc, rtxSsrc ] = line.ssrcs.split(/\s+/); + let [ssrc, rtxSsrc] = line.ssrcs.split(/\s+/); ssrc = Number(ssrc); rtxSsrc = Number(rtxSsrc); - if (ssrcs.has(ssrc)) - { + if (ssrcs.has(ssrc)) { // Remove both the SSRC and RTX SSRC from the set so later we know that they // are already handled. ssrcs.delete(ssrc); @@ -71,20 +61,17 @@ export function getRtpEncodings( // If the set of SSRCs is not empty it means that RTX is not being used, so take // media SSRCs from there. - for (const ssrc of ssrcs) - { + for (const ssrc of ssrcs) { // Add to the map. ssrcToRtxSsrc.set(ssrc, null); } const encodings: RtpEncodingParameters[] = []; - for (const [ ssrc, rtxSsrc ] of ssrcToRtxSsrc) - { + for (const [ssrc, rtxSsrc] of ssrcToRtxSsrc) { const encoding: any = { ssrc }; - if (rtxSsrc) - { + if (rtxSsrc) { encoding.rtx = { ssrc: rtxSsrc }; } @@ -97,21 +84,16 @@ export function getRtpEncodings( /** * Adds multi-ssrc based simulcast into the given SDP media section offer. */ -export function addLegacySimulcast( - { - offerMediaObject, - track, - numStreams - }: - { - offerMediaObject: any; - track: MediaStreamTrack; - numStreams: number; - } -): void -{ - if (numStreams <= 1) - { +export function addLegacySimulcast({ + offerMediaObject, + track, + numStreams, +}: { + offerMediaObject: any; + track: MediaStreamTrack; + numStreams: number; +}): void { + if (numStreams <= 1) { throw new TypeError('numStreams must be greater than 1'); } @@ -120,75 +102,64 @@ export function addLegacySimulcast( let streamId: any; // Get the SSRC. - const ssrcMsidLine = (offerMediaObject.ssrcs || []) - .find((line: any) => - { - if (line.attribute !== 'msid') - { - return false; - } + const ssrcMsidLine = (offerMediaObject.ssrcs || []).find((line: any) => { + if (line.attribute !== 'msid') { + return false; + } - const trackId = line.value.split(' ')[1]; + const trackId = line.value.split(' ')[1]; - if (trackId === track.id) - { - firstSsrc = line.id; - streamId = line.value.split(' ')[0]; + if (trackId === track.id) { + firstSsrc = line.id; + streamId = line.value.split(' ')[0]; - return true; - } - else - { - return false; - } - }); + return true; + } else { + return false; + } + }); - if (!ssrcMsidLine) - { - throw new Error(`a=ssrc line with msid information not found [track.id:${track.id}]`); + if (!ssrcMsidLine) { + throw new Error( + `a=ssrc line with msid information not found [track.id:${track.id}]`, + ); } // Get the SSRC for RTX. - (offerMediaObject.ssrcGroups || []) - .some((line: any) => - { - if (line.semantics !== 'FID') - { - return false; - } + (offerMediaObject.ssrcGroups || []).some((line: any) => { + if (line.semantics !== 'FID') { + return false; + } - const ssrcs = line.ssrcs.split(/\s+/); + const ssrcs = line.ssrcs.split(/\s+/); - if (Number(ssrcs[0]) === firstSsrc) - { - firstRtxSsrc = Number(ssrcs[1]); + if (Number(ssrcs[0]) === firstSsrc) { + firstRtxSsrc = Number(ssrcs[1]); - return true; - } - else - { - return false; - } - }); + return true; + } else { + return false; + } + }); - const ssrcCnameLine = offerMediaObject.ssrcs - .find((line: any) => (line.attribute === 'cname' && line.id === firstSsrc)); + const ssrcCnameLine = offerMediaObject.ssrcs.find( + (line: any) => line.attribute === 'cname' && line.id === firstSsrc, + ); - if (!ssrcCnameLine) - { - throw new Error(`a=ssrc line with cname information not found [track.id:${track.id}]`); + if (!ssrcCnameLine) { + throw new Error( + `a=ssrc line with cname information not found [track.id:${track.id}]`, + ); } const cname = ssrcCnameLine.value; const ssrcs = []; const rtxSsrcs = []; - for (let i = 0; i < numStreams; ++i) - { + for (let i = 0; i < numStreams; ++i) { ssrcs.push(firstSsrc + i); - if (firstRtxSsrc) - { + if (firstRtxSsrc) { rtxSsrcs.push(firstRtxSsrc + i); } } @@ -196,54 +167,46 @@ export function addLegacySimulcast( offerMediaObject.ssrcGroups = offerMediaObject.ssrcGroups || []; offerMediaObject.ssrcs = offerMediaObject.ssrcs || []; - offerMediaObject.ssrcGroups.push( - { - semantics : 'SIM', - ssrcs : ssrcs.join(' ') - }); + offerMediaObject.ssrcGroups.push({ + semantics: 'SIM', + ssrcs: ssrcs.join(' '), + }); - for (let i = 0; i < ssrcs.length; ++i) - { + for (let i = 0; i < ssrcs.length; ++i) { const ssrc = ssrcs[i]; - offerMediaObject.ssrcs.push( - { - id : ssrc, - attribute : 'cname', - value : cname - }); - - offerMediaObject.ssrcs.push( - { - id : ssrc, - attribute : 'msid', - value : `${streamId} ${track.id}` - }); + offerMediaObject.ssrcs.push({ + id: ssrc, + attribute: 'cname', + value: cname, + }); + + offerMediaObject.ssrcs.push({ + id: ssrc, + attribute: 'msid', + value: `${streamId} ${track.id}`, + }); } - for (let i = 0; i < rtxSsrcs.length; ++i) - { + for (let i = 0; i < rtxSsrcs.length; ++i) { const ssrc = ssrcs[i]; const rtxSsrc = rtxSsrcs[i]; - offerMediaObject.ssrcs.push( - { - id : rtxSsrc, - attribute : 'cname', - value : cname - }); - - offerMediaObject.ssrcs.push( - { - id : rtxSsrc, - attribute : 'msid', - value : `${streamId} ${track.id}` - }); - - offerMediaObject.ssrcGroups.push( - { - semantics : 'FID', - ssrcs : `${ssrc} ${rtxSsrc}` - }); + offerMediaObject.ssrcs.push({ + id: rtxSsrc, + attribute: 'cname', + value: cname, + }); + + offerMediaObject.ssrcs.push({ + id: rtxSsrc, + attribute: 'msid', + value: `${streamId} ${track.id}`, + }); + + offerMediaObject.ssrcGroups.push({ + semantics: 'FID', + ssrcs: `${ssrc} ${rtxSsrc}`, + }); } } diff --git a/src/handlers/sdp/unifiedPlanUtils.ts b/src/handlers/sdp/unifiedPlanUtils.ts index 395b7013..2b4955e6 100644 --- a/src/handlers/sdp/unifiedPlanUtils.ts +++ b/src/handlers/sdp/unifiedPlanUtils.ts @@ -1,41 +1,36 @@ import { RtpEncodingParameters } from '../../RtpParameters'; -export function getRtpEncodings( - { offerMediaObject }: - { offerMediaObject: any } -): RtpEncodingParameters[] -{ +export function getRtpEncodings({ + offerMediaObject, +}: { + offerMediaObject: any; +}): RtpEncodingParameters[] { const ssrcs = new Set(); - for (const line of offerMediaObject.ssrcs || []) - { + for (const line of offerMediaObject.ssrcs || []) { const ssrc = line.id; ssrcs.add(ssrc); } - if (ssrcs.size === 0) - { + if (ssrcs.size === 0) { throw new Error('no a=ssrc lines found'); } const ssrcToRtxSsrc = new Map(); // First assume RTX is used. - for (const line of offerMediaObject.ssrcGroups || []) - { - if (line.semantics !== 'FID') - { + for (const line of offerMediaObject.ssrcGroups || []) { + if (line.semantics !== 'FID') { continue; } - let [ ssrc, rtxSsrc ] = line.ssrcs.split(/\s+/); + let [ssrc, rtxSsrc] = line.ssrcs.split(/\s+/); ssrc = Number(ssrc); rtxSsrc = Number(rtxSsrc); - if (ssrcs.has(ssrc)) - { + if (ssrcs.has(ssrc)) { // Remove both the SSRC and RTX SSRC from the set so later we know // that they are already handled. ssrcs.delete(ssrc); @@ -48,20 +43,17 @@ export function getRtpEncodings( // If the set of SSRCs is not empty it means that RTX is not being used, so // take media SSRCs from there. - for (const ssrc of ssrcs) - { + for (const ssrc of ssrcs) { // Add to the map. ssrcToRtxSsrc.set(ssrc, null); } const encodings: RtpEncodingParameters[] = []; - for (const [ ssrc, rtxSsrc ] of ssrcToRtxSsrc) - { + for (const [ssrc, rtxSsrc] of ssrcToRtxSsrc) { const encoding: RtpEncodingParameters = { ssrc }; - if (rtxSsrc) - { + if (rtxSsrc) { encoding.rtx = { ssrc: rtxSsrc }; } @@ -74,63 +66,52 @@ export function getRtpEncodings( /** * Adds multi-ssrc based simulcast into the given SDP media section offer. */ -export function addLegacySimulcast( - { - offerMediaObject, - numStreams - }: - { - offerMediaObject: any; - numStreams: number; - } -): void -{ - if (numStreams <= 1) - { +export function addLegacySimulcast({ + offerMediaObject, + numStreams, +}: { + offerMediaObject: any; + numStreams: number; +}): void { + if (numStreams <= 1) { throw new TypeError('numStreams must be greater than 1'); } // Get the SSRC. - const ssrcMsidLine = (offerMediaObject.ssrcs || []) - .find((line: any) => line.attribute === 'msid'); + const ssrcMsidLine = (offerMediaObject.ssrcs || []).find( + (line: any) => line.attribute === 'msid', + ); - if (!ssrcMsidLine) - { + if (!ssrcMsidLine) { throw new Error('a=ssrc line with msid information not found'); } - const [ streamId, trackId ] = ssrcMsidLine.value.split(' '); + const [streamId, trackId] = ssrcMsidLine.value.split(' '); const firstSsrc = ssrcMsidLine.id; let firstRtxSsrc; // Get the SSRC for RTX. - (offerMediaObject.ssrcGroups || []) - .some((line: any) => - { - if (line.semantics !== 'FID') - { - return false; - } - - const ssrcs = line.ssrcs.split(/\s+/); - - if (Number(ssrcs[0]) === firstSsrc) - { - firstRtxSsrc = Number(ssrcs[1]); - - return true; - } - else - { - return false; - } - }); + (offerMediaObject.ssrcGroups || []).some((line: any) => { + if (line.semantics !== 'FID') { + return false; + } + + const ssrcs = line.ssrcs.split(/\s+/); - const ssrcCnameLine = offerMediaObject.ssrcs - .find((line: any) => line.attribute === 'cname'); + if (Number(ssrcs[0]) === firstSsrc) { + firstRtxSsrc = Number(ssrcs[1]); - if (!ssrcCnameLine) - { + return true; + } else { + return false; + } + }); + + const ssrcCnameLine = offerMediaObject.ssrcs.find( + (line: any) => line.attribute === 'cname', + ); + + if (!ssrcCnameLine) { throw new Error('a=ssrc line with cname information not found'); } @@ -138,12 +119,10 @@ export function addLegacySimulcast( const ssrcs = []; const rtxSsrcs = []; - for (let i = 0; i < numStreams; ++i) - { + for (let i = 0; i < numStreams; ++i) { ssrcs.push(firstSsrc + i); - if (firstRtxSsrc) - { + if (firstRtxSsrc) { rtxSsrcs.push(firstRtxSsrc + i); } } @@ -151,54 +130,46 @@ export function addLegacySimulcast( offerMediaObject.ssrcGroups = []; offerMediaObject.ssrcs = []; - offerMediaObject.ssrcGroups.push( - { - semantics : 'SIM', - ssrcs : ssrcs.join(' ') - }); + offerMediaObject.ssrcGroups.push({ + semantics: 'SIM', + ssrcs: ssrcs.join(' '), + }); - for (let i = 0; i < ssrcs.length; ++i) - { + for (let i = 0; i < ssrcs.length; ++i) { const ssrc = ssrcs[i]; - offerMediaObject.ssrcs.push( - { - id : ssrc, - attribute : 'cname', - value : cname - }); - - offerMediaObject.ssrcs.push( - { - id : ssrc, - attribute : 'msid', - value : `${streamId} ${trackId}` - }); + offerMediaObject.ssrcs.push({ + id: ssrc, + attribute: 'cname', + value: cname, + }); + + offerMediaObject.ssrcs.push({ + id: ssrc, + attribute: 'msid', + value: `${streamId} ${trackId}`, + }); } - for (let i = 0; i < rtxSsrcs.length; ++i) - { + for (let i = 0; i < rtxSsrcs.length; ++i) { const ssrc = ssrcs[i]; const rtxSsrc = rtxSsrcs[i]; - offerMediaObject.ssrcs.push( - { - id : rtxSsrc, - attribute : 'cname', - value : cname - }); - - offerMediaObject.ssrcs.push( - { - id : rtxSsrc, - attribute : 'msid', - value : `${streamId} ${trackId}` - }); - - offerMediaObject.ssrcGroups.push( - { - semantics : 'FID', - ssrcs : `${ssrc} ${rtxSsrc}` - }); + offerMediaObject.ssrcs.push({ + id: rtxSsrc, + attribute: 'cname', + value: cname, + }); + + offerMediaObject.ssrcs.push({ + id: rtxSsrc, + attribute: 'msid', + value: `${streamId} ${trackId}`, + }); + + offerMediaObject.ssrcGroups.push({ + semantics: 'FID', + ssrcs: `${ssrc} ${rtxSsrc}`, + }); } } diff --git a/src/ortc.ts b/src/ortc.ts index 8feb024d..4dff3dd3 100644 --- a/src/ortc.ts +++ b/src/ortc.ts @@ -9,13 +9,13 @@ import { RtcpFeedback, RtpEncodingParameters, RtpHeaderExtensionParameters, - RtcpParameters + RtcpParameters, } from './RtpParameters'; import { SctpCapabilities, NumSctpStreams, SctpParameters, - SctpStreamParameters + SctpStreamParameters, } from './SctpParameters'; import * as utils from './utils'; @@ -28,40 +28,30 @@ const RTP_PROBATOR_CODEC_PAYLOAD_TYPE = 127; * fields with default values. * It throws if invalid. */ -export function validateRtpCapabilities(caps: RtpCapabilities): void -{ - if (typeof caps !== 'object') - { +export function validateRtpCapabilities(caps: RtpCapabilities): void { + if (typeof caps !== 'object') { throw new TypeError('caps is not an object'); } // codecs is optional. If unset, fill with an empty array. - if (caps.codecs && !Array.isArray(caps.codecs)) - { + if (caps.codecs && !Array.isArray(caps.codecs)) { throw new TypeError('caps.codecs is not an array'); - } - else if (!caps.codecs) - { + } else if (!caps.codecs) { caps.codecs = []; } - for (const codec of caps.codecs) - { + for (const codec of caps.codecs) { validateRtpCodecCapability(codec); } // headerExtensions is optional. If unset, fill with an empty array. - if (caps.headerExtensions && !Array.isArray(caps.headerExtensions)) - { + if (caps.headerExtensions && !Array.isArray(caps.headerExtensions)) { throw new TypeError('caps.headerExtensions is not an array'); - } - else if (!caps.headerExtensions) - { + } else if (!caps.headerExtensions) { caps.headerExtensions = []; } - for (const ext of caps.headerExtensions) - { + for (const ext of caps.headerExtensions) { validateRtpHeaderExtension(ext); } } @@ -71,25 +61,21 @@ export function validateRtpCapabilities(caps: RtpCapabilities): void * fields with default values. * It throws if invalid. */ -export function validateRtpCodecCapability(codec: RtpCodecCapability): void -{ +export function validateRtpCodecCapability(codec: RtpCodecCapability): void { const MimeTypeRegex = new RegExp('^(audio|video)/(.+)', 'i'); - if (typeof codec !== 'object') - { + if (typeof codec !== 'object') { throw new TypeError('codec is not an object'); } // mimeType is mandatory. - if (!codec.mimeType || typeof codec.mimeType !== 'string') - { + if (!codec.mimeType || typeof codec.mimeType !== 'string') { throw new TypeError('missing codec.mimeType'); } const mimeTypeMatch = MimeTypeRegex.exec(codec.mimeType); - if (!mimeTypeMatch) - { + if (!mimeTypeMatch) { throw new TypeError('invalid codec.mimeType'); } @@ -97,70 +83,60 @@ export function validateRtpCodecCapability(codec: RtpCodecCapability): void codec.kind = mimeTypeMatch[1].toLowerCase() as MediaKind; // preferredPayloadType is optional. - if (codec.preferredPayloadType && typeof codec.preferredPayloadType !== 'number') - { + if ( + codec.preferredPayloadType && + typeof codec.preferredPayloadType !== 'number' + ) { throw new TypeError('invalid codec.preferredPayloadType'); } // clockRate is mandatory. - if (typeof codec.clockRate !== 'number') - { + if (typeof codec.clockRate !== 'number') { throw new TypeError('missing codec.clockRate'); } // channels is optional. If unset, set it to 1 (just if audio). - if (codec.kind === 'audio') - { - if (typeof codec.channels !== 'number') - { + if (codec.kind === 'audio') { + if (typeof codec.channels !== 'number') { codec.channels = 1; } - } - else - { + } else { delete codec.channels; } // parameters is optional. If unset, set it to an empty object. - if (!codec.parameters || typeof codec.parameters !== 'object') - { + if (!codec.parameters || typeof codec.parameters !== 'object') { codec.parameters = {}; } - for (const key of Object.keys(codec.parameters)) - { + for (const key of Object.keys(codec.parameters)) { let value = codec.parameters[key]; - if (value === undefined) - { + if (value === undefined) { codec.parameters[key] = ''; value = ''; } - if (typeof value !== 'string' && typeof value !== 'number') - { + if (typeof value !== 'string' && typeof value !== 'number') { throw new TypeError( - `invalid codec parameter [key:${key}s, value:${value}]`); + `invalid codec parameter [key:${key}s, value:${value}]`, + ); } // Specific parameters validation. - if (key === 'apt') - { - if (typeof value !== 'number') - { + if (key === 'apt') { + if (typeof value !== 'number') { throw new TypeError('invalid codec apt parameter'); } } } // rtcpFeedback is optional. If unset, set it to an empty array. - if (!codec.rtcpFeedback || !Array.isArray(codec.rtcpFeedback)) - { + if (!codec.rtcpFeedback || !Array.isArray(codec.rtcpFeedback)) { codec.rtcpFeedback = []; } - for (const fb of codec.rtcpFeedback) - { + for (const fb of codec.rtcpFeedback) { validateRtcpFeedback(fb); } } @@ -170,22 +146,18 @@ export function validateRtpCodecCapability(codec: RtpCodecCapability): void * fields with default values. * It throws if invalid. */ -export function validateRtcpFeedback(fb: RtcpFeedback): void -{ - if (typeof fb !== 'object') - { +export function validateRtcpFeedback(fb: RtcpFeedback): void { + if (typeof fb !== 'object') { throw new TypeError('fb is not an object'); } // type is mandatory. - if (!fb.type || typeof fb.type !== 'string') - { + if (!fb.type || typeof fb.type !== 'string') { throw new TypeError('missing fb.type'); } // parameter is optional. If unset set it to an empty string. - if (!fb.parameter || typeof fb.parameter !== 'string') - { + if (!fb.parameter || typeof fb.parameter !== 'string') { fb.parameter = ''; } } @@ -195,49 +167,37 @@ export function validateRtcpFeedback(fb: RtcpFeedback): void * fields with default values. * It throws if invalid. */ -export function validateRtpHeaderExtension(ext: RtpHeaderExtension): void -{ - - if (typeof ext !== 'object') - { +export function validateRtpHeaderExtension(ext: RtpHeaderExtension): void { + if (typeof ext !== 'object') { throw new TypeError('ext is not an object'); } // kind is mandatory. - if (ext.kind !== 'audio' && ext.kind !== 'video') - { + if (ext.kind !== 'audio' && ext.kind !== 'video') { throw new TypeError('invalid ext.kind'); } // uri is mandatory. - if (!ext.uri || typeof ext.uri !== 'string') - { + if (!ext.uri || typeof ext.uri !== 'string') { throw new TypeError('missing ext.uri'); } // preferredId is mandatory. - if (typeof ext.preferredId !== 'number') - { + if (typeof ext.preferredId !== 'number') { throw new TypeError('missing ext.preferredId'); } // preferredEncrypt is optional. If unset set it to false. - if (ext.preferredEncrypt && typeof ext.preferredEncrypt !== 'boolean') - { + if (ext.preferredEncrypt && typeof ext.preferredEncrypt !== 'boolean') { throw new TypeError('invalid ext.preferredEncrypt'); - } - else if (!ext.preferredEncrypt) - { + } else if (!ext.preferredEncrypt) { ext.preferredEncrypt = false; } // direction is optional. If unset set it to sendrecv. - if (ext.direction && typeof ext.direction !== 'string') - { + if (ext.direction && typeof ext.direction !== 'string') { throw new TypeError('invalid ext.direction'); - } - else if (!ext.direction) - { + } else if (!ext.direction) { ext.direction = 'sendrecv'; } } @@ -247,67 +207,51 @@ export function validateRtpHeaderExtension(ext: RtpHeaderExtension): void * fields with default values. * It throws if invalid. */ -export function validateRtpParameters(params: RtpParameters): void -{ - if (typeof params !== 'object') - { +export function validateRtpParameters(params: RtpParameters): void { + if (typeof params !== 'object') { throw new TypeError('params is not an object'); } // mid is optional. - if (params.mid && typeof params.mid !== 'string') - { + if (params.mid && typeof params.mid !== 'string') { throw new TypeError('params.mid is not a string'); } // codecs is mandatory. - if (!Array.isArray(params.codecs)) - { + if (!Array.isArray(params.codecs)) { throw new TypeError('missing params.codecs'); } - for (const codec of params.codecs) - { + for (const codec of params.codecs) { validateRtpCodecParameters(codec); } // headerExtensions is optional. If unset, fill with an empty array. - if (params.headerExtensions && !Array.isArray(params.headerExtensions)) - { + if (params.headerExtensions && !Array.isArray(params.headerExtensions)) { throw new TypeError('params.headerExtensions is not an array'); - } - else if (!params.headerExtensions) - { + } else if (!params.headerExtensions) { params.headerExtensions = []; } - for (const ext of params.headerExtensions) - { + for (const ext of params.headerExtensions) { validateRtpHeaderExtensionParameters(ext); } // encodings is optional. If unset, fill with an empty array. - if (params.encodings && !Array.isArray(params.encodings)) - { + if (params.encodings && !Array.isArray(params.encodings)) { throw new TypeError('params.encodings is not an array'); - } - else if (!params.encodings) - { + } else if (!params.encodings) { params.encodings = []; } - for (const encoding of params.encodings) - { + for (const encoding of params.encodings) { validateRtpEncodingParameters(encoding); } // rtcp is optional. If unset, fill with an empty object. - if (params.rtcp && typeof params.rtcp !== 'object') - { + if (params.rtcp && typeof params.rtcp !== 'object') { throw new TypeError('params.rtcp is not an object'); - } - else if (!params.rtcp) - { + } else if (!params.rtcp) { params.rtcp = {}; } @@ -319,95 +263,78 @@ export function validateRtpParameters(params: RtpParameters): void * fields with default values. * It throws if invalid. */ -export function validateRtpCodecParameters(codec: RtpCodecParameters): void -{ +export function validateRtpCodecParameters(codec: RtpCodecParameters): void { const MimeTypeRegex = new RegExp('^(audio|video)/(.+)', 'i'); - if (typeof codec !== 'object') - { + if (typeof codec !== 'object') { throw new TypeError('codec is not an object'); } // mimeType is mandatory. - if (!codec.mimeType || typeof codec.mimeType !== 'string') - { + if (!codec.mimeType || typeof codec.mimeType !== 'string') { throw new TypeError('missing codec.mimeType'); } const mimeTypeMatch = MimeTypeRegex.exec(codec.mimeType); - if (!mimeTypeMatch) - { + if (!mimeTypeMatch) { throw new TypeError('invalid codec.mimeType'); } // payloadType is mandatory. - if (typeof codec.payloadType !== 'number') - { + if (typeof codec.payloadType !== 'number') { throw new TypeError('missing codec.payloadType'); } // clockRate is mandatory. - if (typeof codec.clockRate !== 'number') - { + if (typeof codec.clockRate !== 'number') { throw new TypeError('missing codec.clockRate'); } const kind = mimeTypeMatch[1].toLowerCase() as MediaKind; // channels is optional. If unset, set it to 1 (just if audio). - if (kind === 'audio') - { - if (typeof codec.channels !== 'number') - { + if (kind === 'audio') { + if (typeof codec.channels !== 'number') { codec.channels = 1; } - } - else - { + } else { delete codec.channels; } // parameters is optional. If unset, set it to an empty object. - if (!codec.parameters || typeof codec.parameters !== 'object') - { + if (!codec.parameters || typeof codec.parameters !== 'object') { codec.parameters = {}; } - for (const key of Object.keys(codec.parameters)) - { + for (const key of Object.keys(codec.parameters)) { let value = codec.parameters[key]; - if (value === undefined) - { + if (value === undefined) { codec.parameters[key] = ''; value = ''; } - if (typeof value !== 'string' && typeof value !== 'number') - { + if (typeof value !== 'string' && typeof value !== 'number') { throw new TypeError( - `invalid codec parameter [key:${key}s, value:${value}]`); + `invalid codec parameter [key:${key}s, value:${value}]`, + ); } // Specific parameters validation. - if (key === 'apt') - { - if (typeof value !== 'number') - { + if (key === 'apt') { + if (typeof value !== 'number') { throw new TypeError('invalid codec apt parameter'); } } } // rtcpFeedback is optional. If unset, set it to an empty array. - if (!codec.rtcpFeedback || !Array.isArray(codec.rtcpFeedback)) - { + if (!codec.rtcpFeedback || !Array.isArray(codec.rtcpFeedback)) { codec.rtcpFeedback = []; } - for (const fb of codec.rtcpFeedback) - { + for (const fb of codec.rtcpFeedback) { validateRtcpFeedback(fb); } } @@ -418,55 +345,43 @@ export function validateRtpCodecParameters(codec: RtpCodecParameters): void * It throws if invalid. */ export function validateRtpHeaderExtensionParameters( - ext: RtpHeaderExtensionParameters -): void -{ - - if (typeof ext !== 'object') - { + ext: RtpHeaderExtensionParameters, +): void { + if (typeof ext !== 'object') { throw new TypeError('ext is not an object'); } // uri is mandatory. - if (!ext.uri || typeof ext.uri !== 'string') - { + if (!ext.uri || typeof ext.uri !== 'string') { throw new TypeError('missing ext.uri'); } // id is mandatory. - if (typeof ext.id !== 'number') - { + if (typeof ext.id !== 'number') { throw new TypeError('missing ext.id'); } // encrypt is optional. If unset set it to false. - if (ext.encrypt && typeof ext.encrypt !== 'boolean') - { + if (ext.encrypt && typeof ext.encrypt !== 'boolean') { throw new TypeError('invalid ext.encrypt'); - } - else if (!ext.encrypt) - { + } else if (!ext.encrypt) { ext.encrypt = false; } // parameters is optional. If unset, set it to an empty object. - if (!ext.parameters || typeof ext.parameters !== 'object') - { + if (!ext.parameters || typeof ext.parameters !== 'object') { ext.parameters = {}; } - for (const key of Object.keys(ext.parameters)) - { + for (const key of Object.keys(ext.parameters)) { let value = ext.parameters[key]; - if (value === undefined) - { + if (value === undefined) { ext.parameters[key] = ''; value = ''; } - if (typeof value !== 'string' && typeof value !== 'number') - { + if (typeof value !== 'string' && typeof value !== 'number') { throw new TypeError('invalid header extension parameter'); } } @@ -477,48 +392,43 @@ export function validateRtpHeaderExtensionParameters( * fields with default values. * It throws if invalid. */ -export function validateRtpEncodingParameters(encoding: RtpEncodingParameters): void -{ - if (typeof encoding !== 'object') - { +export function validateRtpEncodingParameters( + encoding: RtpEncodingParameters, +): void { + if (typeof encoding !== 'object') { throw new TypeError('encoding is not an object'); } // ssrc is optional. - if (encoding.ssrc && typeof encoding.ssrc !== 'number') - { + if (encoding.ssrc && typeof encoding.ssrc !== 'number') { throw new TypeError('invalid encoding.ssrc'); } // rid is optional. - if (encoding.rid && typeof encoding.rid !== 'string') - { + if (encoding.rid && typeof encoding.rid !== 'string') { throw new TypeError('invalid encoding.rid'); } // rtx is optional. - if (encoding.rtx && typeof encoding.rtx !== 'object') - { + if (encoding.rtx && typeof encoding.rtx !== 'object') { throw new TypeError('invalid encoding.rtx'); - } - else if (encoding.rtx) - { + } else if (encoding.rtx) { // RTX ssrc is mandatory if rtx is present. - if (typeof encoding.rtx.ssrc !== 'number') - { + if (typeof encoding.rtx.ssrc !== 'number') { throw new TypeError('missing encoding.rtx.ssrc'); } } // dtx is optional. If unset set it to false. - if (!encoding.dtx || typeof encoding.dtx !== 'boolean') - { + if (!encoding.dtx || typeof encoding.dtx !== 'boolean') { encoding.dtx = false; } // scalabilityMode is optional. - if (encoding.scalabilityMode && typeof encoding.scalabilityMode !== 'string') - { + if ( + encoding.scalabilityMode && + typeof encoding.scalabilityMode !== 'string' + ) { throw new TypeError('invalid encoding.scalabilityMode'); } } @@ -528,22 +438,18 @@ export function validateRtpEncodingParameters(encoding: RtpEncodingParameters): * fields with default values. * It throws if invalid. */ -export function validateRtcpParameters(rtcp: RtcpParameters): void -{ - if (typeof rtcp !== 'object') - { +export function validateRtcpParameters(rtcp: RtcpParameters): void { + if (typeof rtcp !== 'object') { throw new TypeError('rtcp is not an object'); } // cname is optional. - if (rtcp.cname && typeof rtcp.cname !== 'string') - { + if (rtcp.cname && typeof rtcp.cname !== 'string') { throw new TypeError('invalid rtcp.cname'); } // reducedSize is optional. If unset set it to true. - if (!rtcp.reducedSize || typeof rtcp.reducedSize !== 'boolean') - { + if (!rtcp.reducedSize || typeof rtcp.reducedSize !== 'boolean') { rtcp.reducedSize = true; } } @@ -553,16 +459,13 @@ export function validateRtcpParameters(rtcp: RtcpParameters): void * fields with default values. * It throws if invalid. */ -export function validateSctpCapabilities(caps: SctpCapabilities): void -{ - if (typeof caps !== 'object') - { +export function validateSctpCapabilities(caps: SctpCapabilities): void { + if (typeof caps !== 'object') { throw new TypeError('caps is not an object'); } // numStreams is mandatory. - if (!caps.numStreams || typeof caps.numStreams !== 'object') - { + if (!caps.numStreams || typeof caps.numStreams !== 'object') { throw new TypeError('missing caps.numStreams'); } @@ -574,22 +477,18 @@ export function validateSctpCapabilities(caps: SctpCapabilities): void * fields with default values. * It throws if invalid. */ -export function validateNumSctpStreams(numStreams: NumSctpStreams): void -{ - if (typeof numStreams !== 'object') - { +export function validateNumSctpStreams(numStreams: NumSctpStreams): void { + if (typeof numStreams !== 'object') { throw new TypeError('numStreams is not an object'); } // OS is mandatory. - if (typeof numStreams.OS !== 'number') - { + if (typeof numStreams.OS !== 'number') { throw new TypeError('missing numStreams.OS'); } // MIS is mandatory. - if (typeof numStreams.MIS !== 'number') - { + if (typeof numStreams.MIS !== 'number') { throw new TypeError('missing numStreams.MIS'); } } @@ -599,34 +498,28 @@ export function validateNumSctpStreams(numStreams: NumSctpStreams): void * fields with default values. * It throws if invalid. */ -export function validateSctpParameters(params: SctpParameters): void -{ - if (typeof params !== 'object') - { +export function validateSctpParameters(params: SctpParameters): void { + if (typeof params !== 'object') { throw new TypeError('params is not an object'); } // port is mandatory. - if (typeof params.port !== 'number') - { + if (typeof params.port !== 'number') { throw new TypeError('missing params.port'); } // OS is mandatory. - if (typeof params.OS !== 'number') - { + if (typeof params.OS !== 'number') { throw new TypeError('missing params.OS'); } // MIS is mandatory. - if (typeof params.MIS !== 'number') - { + if (typeof params.MIS !== 'number') { throw new TypeError('missing params.MIS'); } // maxMessageSize is mandatory. - if (typeof params.maxMessageSize !== 'number') - { + if (typeof params.maxMessageSize !== 'number') { throw new TypeError('missing params.maxMessageSize'); } } @@ -636,70 +529,68 @@ export function validateSctpParameters(params: SctpParameters): void * fields with default values. * It throws if invalid. */ -export function validateSctpStreamParameters(params: SctpStreamParameters): void -{ - if (typeof params !== 'object') - { +export function validateSctpStreamParameters( + params: SctpStreamParameters, +): void { + if (typeof params !== 'object') { throw new TypeError('params is not an object'); } // streamId is mandatory. - if (typeof params.streamId !== 'number') - { + if (typeof params.streamId !== 'number') { throw new TypeError('missing params.streamId'); } // ordered is optional. let orderedGiven = false; - if (typeof params.ordered === 'boolean') - { + if (typeof params.ordered === 'boolean') { orderedGiven = true; - } - else - { + } else { params.ordered = true; } // maxPacketLifeTime is optional. - if (params.maxPacketLifeTime && typeof params.maxPacketLifeTime !== 'number') - { + if ( + params.maxPacketLifeTime && + typeof params.maxPacketLifeTime !== 'number' + ) { throw new TypeError('invalid params.maxPacketLifeTime'); } // maxRetransmits is optional. - if (params.maxRetransmits && typeof params.maxRetransmits !== 'number') - { + if (params.maxRetransmits && typeof params.maxRetransmits !== 'number') { throw new TypeError('invalid params.maxRetransmits'); } - if (params.maxPacketLifeTime && params.maxRetransmits) - { - throw new TypeError('cannot provide both maxPacketLifeTime and maxRetransmits'); + if (params.maxPacketLifeTime && params.maxRetransmits) { + throw new TypeError( + 'cannot provide both maxPacketLifeTime and maxRetransmits', + ); } if ( orderedGiven && params.ordered && (params.maxPacketLifeTime || params.maxRetransmits) - ) - { - throw new TypeError('cannot be ordered with maxPacketLifeTime or maxRetransmits'); - } - else if (!orderedGiven && (params.maxPacketLifeTime || params.maxRetransmits)) - { + ) { + throw new TypeError( + 'cannot be ordered with maxPacketLifeTime or maxRetransmits', + ); + } else if ( + !orderedGiven && + (params.maxPacketLifeTime || params.maxRetransmits) + ) { params.ordered = false; } // label is optional. - if (params.label && typeof params.label !== 'string') - { + if (params.label && typeof params.label !== 'string') { throw new TypeError('invalid params.label'); } // protocol is optional. - if (params.protocol && typeof params.protocol !== 'string') - { + if (params.protocol && typeof params.protocol !== 'string') { throw new TypeError('invalid params.protocol'); } } @@ -709,126 +600,111 @@ export function validateSctpStreamParameters(params: SctpStreamParameters): void */ export function getExtendedRtpCapabilities( localCaps: RtpCapabilities, - remoteCaps: RtpCapabilities -): any -{ - const extendedRtpCapabilities: any = - { - codecs : [], - headerExtensions : [] + remoteCaps: RtpCapabilities, +): any { + const extendedRtpCapabilities: any = { + codecs: [], + headerExtensions: [], }; // Match media codecs and keep the order preferred by remoteCaps. - for (const remoteCodec of remoteCaps.codecs || []) - { - if (isRtxCodec(remoteCodec)) - { + for (const remoteCodec of remoteCaps.codecs || []) { + if (isRtxCodec(remoteCodec)) { continue; } - const matchingLocalCodec = (localCaps.codecs || []) - .find((localCodec: RtpCodecCapability) => ( - matchCodecs(localCodec, remoteCodec, { strict: true, modify: true })) - ); + const matchingLocalCodec = (localCaps.codecs || []).find( + (localCodec: RtpCodecCapability) => + matchCodecs(localCodec, remoteCodec, { strict: true, modify: true }), + ); - if (!matchingLocalCodec) - { + if (!matchingLocalCodec) { continue; } - const extendedCodec: any = - { - mimeType : matchingLocalCodec.mimeType, - kind : matchingLocalCodec.kind, - clockRate : matchingLocalCodec.clockRate, - channels : matchingLocalCodec.channels, - localPayloadType : matchingLocalCodec.preferredPayloadType, - localRtxPayloadType : undefined, - remotePayloadType : remoteCodec.preferredPayloadType, - remoteRtxPayloadType : undefined, - localParameters : matchingLocalCodec.parameters, - remoteParameters : remoteCodec.parameters, - rtcpFeedback : reduceRtcpFeedback(matchingLocalCodec, remoteCodec) + const extendedCodec: any = { + mimeType: matchingLocalCodec.mimeType, + kind: matchingLocalCodec.kind, + clockRate: matchingLocalCodec.clockRate, + channels: matchingLocalCodec.channels, + localPayloadType: matchingLocalCodec.preferredPayloadType, + localRtxPayloadType: undefined, + remotePayloadType: remoteCodec.preferredPayloadType, + remoteRtxPayloadType: undefined, + localParameters: matchingLocalCodec.parameters, + remoteParameters: remoteCodec.parameters, + rtcpFeedback: reduceRtcpFeedback(matchingLocalCodec, remoteCodec), }; extendedRtpCapabilities.codecs.push(extendedCodec); } // Match RTX codecs. - for (const extendedCodec of extendedRtpCapabilities.codecs) - { - const matchingLocalRtxCodec = localCaps.codecs! - .find((localCodec: RtpCodecCapability) => ( + for (const extendedCodec of extendedRtpCapabilities.codecs) { + const matchingLocalRtxCodec = localCaps.codecs!.find( + (localCodec: RtpCodecCapability) => isRtxCodec(localCodec) && - localCodec.parameters.apt === extendedCodec.localPayloadType - )); + localCodec.parameters.apt === extendedCodec.localPayloadType, + ); - const matchingRemoteRtxCodec = remoteCaps.codecs! - .find((remoteCodec: RtpCodecCapability) => ( + const matchingRemoteRtxCodec = remoteCaps.codecs!.find( + (remoteCodec: RtpCodecCapability) => isRtxCodec(remoteCodec) && - remoteCodec.parameters.apt === extendedCodec.remotePayloadType - )); - - if (matchingLocalRtxCodec && matchingRemoteRtxCodec) - { - extendedCodec.localRtxPayloadType = matchingLocalRtxCodec.preferredPayloadType; - extendedCodec.remoteRtxPayloadType = matchingRemoteRtxCodec.preferredPayloadType; + remoteCodec.parameters.apt === extendedCodec.remotePayloadType, + ); + + if (matchingLocalRtxCodec && matchingRemoteRtxCodec) { + extendedCodec.localRtxPayloadType = + matchingLocalRtxCodec.preferredPayloadType; + extendedCodec.remoteRtxPayloadType = + matchingRemoteRtxCodec.preferredPayloadType; } } // Match header extensions. - for (const remoteExt of remoteCaps.headerExtensions!) - { - const matchingLocalExt = localCaps.headerExtensions! - .find((localExt: RtpHeaderExtension) => ( - matchHeaderExtensions(localExt, remoteExt) - )); - - if (!matchingLocalExt) - { + for (const remoteExt of remoteCaps.headerExtensions!) { + const matchingLocalExt = localCaps.headerExtensions!.find( + (localExt: RtpHeaderExtension) => + matchHeaderExtensions(localExt, remoteExt), + ); + + if (!matchingLocalExt) { continue; } - const extendedExt = - { - kind : remoteExt.kind, - uri : remoteExt.uri, - sendId : matchingLocalExt.preferredId, - recvId : remoteExt.preferredId, - encrypt : matchingLocalExt.preferredEncrypt, - direction : 'sendrecv' + const extendedExt = { + kind: remoteExt.kind, + uri: remoteExt.uri, + sendId: matchingLocalExt.preferredId, + recvId: remoteExt.preferredId, + encrypt: matchingLocalExt.preferredEncrypt, + direction: 'sendrecv', }; - switch (remoteExt.direction) - { - case 'sendrecv': - { + switch (remoteExt.direction) { + case 'sendrecv': { extendedExt.direction = 'sendrecv'; break; } - case 'recvonly': - { + case 'recvonly': { extendedExt.direction = 'sendonly'; break; } - case 'sendonly': - { + case 'sendonly': { extendedExt.direction = 'recvonly'; break; } - case 'inactive': - { + case 'inactive': { extendedExt.direction = 'inactive'; break; } - } extendedRtpCapabilities.headerExtensions.push(extendedExt); @@ -841,46 +717,41 @@ export function getExtendedRtpCapabilities( * Generate RTP capabilities for receiving media based on the given extended * RTP capabilities. */ -export function getRecvRtpCapabilities(extendedRtpCapabilities: any): RtpCapabilities -{ - const rtpCapabilities: RtpCapabilities = - { - codecs : [], - headerExtensions : [] +export function getRecvRtpCapabilities( + extendedRtpCapabilities: any, +): RtpCapabilities { + const rtpCapabilities: RtpCapabilities = { + codecs: [], + headerExtensions: [], }; - for (const extendedCodec of extendedRtpCapabilities.codecs) - { - const codec = - { - mimeType : extendedCodec.mimeType, - kind : extendedCodec.kind, - preferredPayloadType : extendedCodec.remotePayloadType, - clockRate : extendedCodec.clockRate, - channels : extendedCodec.channels, - parameters : extendedCodec.localParameters, - rtcpFeedback : extendedCodec.rtcpFeedback + for (const extendedCodec of extendedRtpCapabilities.codecs) { + const codec = { + mimeType: extendedCodec.mimeType, + kind: extendedCodec.kind, + preferredPayloadType: extendedCodec.remotePayloadType, + clockRate: extendedCodec.clockRate, + channels: extendedCodec.channels, + parameters: extendedCodec.localParameters, + rtcpFeedback: extendedCodec.rtcpFeedback, }; rtpCapabilities.codecs!.push(codec); // Add RTX codec. - if (!extendedCodec.remoteRtxPayloadType) - { + if (!extendedCodec.remoteRtxPayloadType) { continue; } - const rtxCodec: RtpCodecCapability = - { - mimeType : `${extendedCodec.kind}/rtx`, - kind : extendedCodec.kind, - preferredPayloadType : extendedCodec.remoteRtxPayloadType, - clockRate : extendedCodec.clockRate, - parameters : - { - apt : extendedCodec.remotePayloadType + const rtxCodec: RtpCodecCapability = { + mimeType: `${extendedCodec.kind}/rtx`, + kind: extendedCodec.kind, + preferredPayloadType: extendedCodec.remoteRtxPayloadType, + clockRate: extendedCodec.clockRate, + parameters: { + apt: extendedCodec.remotePayloadType, }, - rtcpFeedback : [] + rtcpFeedback: [], }; rtpCapabilities.codecs!.push(rtxCodec); @@ -888,24 +759,21 @@ export function getRecvRtpCapabilities(extendedRtpCapabilities: any): RtpCapabil // TODO: In the future, we need to add FEC, CN, etc, codecs. } - for (const extendedExtension of extendedRtpCapabilities.headerExtensions) - { + for (const extendedExtension of extendedRtpCapabilities.headerExtensions) { // Ignore RTP extensions not valid for receiving. if ( extendedExtension.direction !== 'sendrecv' && extendedExtension.direction !== 'recvonly' - ) - { + ) { continue; } - const ext: RtpHeaderExtension = - { - kind : extendedExtension.kind, - uri : extendedExtension.uri, - preferredId : extendedExtension.recvId, - preferredEncrypt : extendedExtension.encrypt, - direction : extendedExtension.direction + const ext: RtpHeaderExtension = { + kind: extendedExtension.kind, + uri: extendedExtension.uri, + preferredId: extendedExtension.recvId, + preferredEncrypt: extendedExtension.encrypt, + direction: extendedExtension.direction, }; rtpCapabilities.headerExtensions!.push(ext); @@ -920,76 +788,63 @@ export function getRecvRtpCapabilities(extendedRtpCapabilities: any): RtpCapabil */ export function getSendingRtpParameters( kind: MediaKind, - extendedRtpCapabilities: any -): RtpParameters -{ - const rtpParameters: RtpParameters = - { - mid : undefined, - codecs : [], - headerExtensions : [], - encodings : [], - rtcp : {} + extendedRtpCapabilities: any, +): RtpParameters { + const rtpParameters: RtpParameters = { + mid: undefined, + codecs: [], + headerExtensions: [], + encodings: [], + rtcp: {}, }; - for (const extendedCodec of extendedRtpCapabilities.codecs) - { - if (extendedCodec.kind !== kind) - { + for (const extendedCodec of extendedRtpCapabilities.codecs) { + if (extendedCodec.kind !== kind) { continue; } - const codec: RtpCodecParameters = - { - mimeType : extendedCodec.mimeType, - payloadType : extendedCodec.localPayloadType, - clockRate : extendedCodec.clockRate, - channels : extendedCodec.channels, - parameters : extendedCodec.localParameters, - rtcpFeedback : extendedCodec.rtcpFeedback + const codec: RtpCodecParameters = { + mimeType: extendedCodec.mimeType, + payloadType: extendedCodec.localPayloadType, + clockRate: extendedCodec.clockRate, + channels: extendedCodec.channels, + parameters: extendedCodec.localParameters, + rtcpFeedback: extendedCodec.rtcpFeedback, }; rtpParameters.codecs.push(codec); // Add RTX codec. - if (extendedCodec.localRtxPayloadType) - { - const rtxCodec: RtpCodecParameters = - { - mimeType : `${extendedCodec.kind}/rtx`, - payloadType : extendedCodec.localRtxPayloadType, - clockRate : extendedCodec.clockRate, - parameters : - { - apt : extendedCodec.localPayloadType + if (extendedCodec.localRtxPayloadType) { + const rtxCodec: RtpCodecParameters = { + mimeType: `${extendedCodec.kind}/rtx`, + payloadType: extendedCodec.localRtxPayloadType, + clockRate: extendedCodec.clockRate, + parameters: { + apt: extendedCodec.localPayloadType, }, - rtcpFeedback : [] + rtcpFeedback: [], }; rtpParameters.codecs.push(rtxCodec); } } - for (const extendedExtension of extendedRtpCapabilities.headerExtensions) - { + for (const extendedExtension of extendedRtpCapabilities.headerExtensions) { // Ignore RTP extensions of a different kind and those not valid for sending. if ( (extendedExtension.kind && extendedExtension.kind !== kind) || - ( - extendedExtension.direction !== 'sendrecv' && - extendedExtension.direction !== 'sendonly' - ) - ) - { + (extendedExtension.direction !== 'sendrecv' && + extendedExtension.direction !== 'sendonly') + ) { continue; } - const ext: RtpHeaderExtensionParameters = - { - uri : extendedExtension.uri, - id : extendedExtension.sendId, - encrypt : extendedExtension.encrypt, - parameters : {} + const ext: RtpHeaderExtensionParameters = { + uri: extendedExtension.uri, + id: extendedExtension.sendId, + encrypt: extendedExtension.encrypt, + parameters: {}, }; rtpParameters.headerExtensions!.push(ext); @@ -1003,77 +858,63 @@ export function getSendingRtpParameters( */ export function getSendingRemoteRtpParameters( kind: MediaKind, - extendedRtpCapabilities: any -): RtpParameters -{ - const rtpParameters: RtpParameters = - { - mid : undefined, - codecs : [], - headerExtensions : [], - encodings : [], - rtcp : {} + extendedRtpCapabilities: any, +): RtpParameters { + const rtpParameters: RtpParameters = { + mid: undefined, + codecs: [], + headerExtensions: [], + encodings: [], + rtcp: {}, }; - for (const extendedCodec of extendedRtpCapabilities.codecs) - { - if (extendedCodec.kind !== kind) - { + for (const extendedCodec of extendedRtpCapabilities.codecs) { + if (extendedCodec.kind !== kind) { continue; } - const codec = - { - mimeType : extendedCodec.mimeType, - payloadType : extendedCodec.localPayloadType, - clockRate : extendedCodec.clockRate, - channels : extendedCodec.channels, - parameters : extendedCodec.remoteParameters, - rtcpFeedback : extendedCodec.rtcpFeedback + const codec = { + mimeType: extendedCodec.mimeType, + payloadType: extendedCodec.localPayloadType, + clockRate: extendedCodec.clockRate, + channels: extendedCodec.channels, + parameters: extendedCodec.remoteParameters, + rtcpFeedback: extendedCodec.rtcpFeedback, }; rtpParameters.codecs.push(codec); // Add RTX codec. - if (extendedCodec.localRtxPayloadType) - { - const rtxCodec: RtpCodecParameters = - { - mimeType : `${extendedCodec.kind}/rtx`, - payloadType : extendedCodec.localRtxPayloadType, - clockRate : extendedCodec.clockRate, - parameters : - { - apt : extendedCodec.localPayloadType + if (extendedCodec.localRtxPayloadType) { + const rtxCodec: RtpCodecParameters = { + mimeType: `${extendedCodec.kind}/rtx`, + payloadType: extendedCodec.localRtxPayloadType, + clockRate: extendedCodec.clockRate, + parameters: { + apt: extendedCodec.localPayloadType, }, - rtcpFeedback : [] + rtcpFeedback: [], }; rtpParameters.codecs.push(rtxCodec); } } - for (const extendedExtension of extendedRtpCapabilities.headerExtensions) - { + for (const extendedExtension of extendedRtpCapabilities.headerExtensions) { // Ignore RTP extensions of a different kind and those not valid for sending. if ( (extendedExtension.kind && extendedExtension.kind !== kind) || - ( - extendedExtension.direction !== 'sendrecv' && - extendedExtension.direction !== 'sendonly' - ) - ) - { + (extendedExtension.direction !== 'sendrecv' && + extendedExtension.direction !== 'sendonly') + ) { continue; } - const ext: RtpHeaderExtensionParameters = - { - uri : extendedExtension.uri, - id : extendedExtension.sendId, - encrypt : extendedExtension.encrypt, - parameters : {} - + const ext: RtpHeaderExtensionParameters = { + uri: extendedExtension.uri, + id: extendedExtension.sendId, + encrypt: extendedExtension.encrypt, + parameters: {}, }; rtpParameters.headerExtensions!.push(ext); @@ -1081,38 +922,35 @@ export function getSendingRemoteRtpParameters( // Reduce codecs' RTCP feedback. Use Transport-CC if available, REMB otherwise. if ( - rtpParameters.headerExtensions!.some((ext) => ( - ext.uri === 'http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01' - )) - ) - { - for (const codec of rtpParameters.codecs) - { - codec.rtcpFeedback = (codec.rtcpFeedback || []) - .filter((fb: RtcpFeedback) => fb.type !== 'goog-remb'); + rtpParameters.headerExtensions!.some( + ext => + ext.uri === + 'http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01', + ) + ) { + for (const codec of rtpParameters.codecs) { + codec.rtcpFeedback = (codec.rtcpFeedback || []).filter( + (fb: RtcpFeedback) => fb.type !== 'goog-remb', + ); } - } - else if ( - rtpParameters.headerExtensions!.some((ext) => ( - ext.uri === 'http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time' - )) - ) - { - for (const codec of rtpParameters.codecs) - { - codec.rtcpFeedback = (codec.rtcpFeedback || []) - .filter((fb) => fb.type !== 'transport-cc'); + } else if ( + rtpParameters.headerExtensions!.some( + ext => + ext.uri === + 'http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time', + ) + ) { + for (const codec of rtpParameters.codecs) { + codec.rtcpFeedback = (codec.rtcpFeedback || []).filter( + fb => fb.type !== 'transport-cc', + ); } - } - else - { - for (const codec of rtpParameters.codecs) - { - codec.rtcpFeedback = (codec.rtcpFeedback || []) - .filter((fb: RtcpFeedback) => ( - fb.type !== 'transport-cc' && - fb.type !== 'goog-remb' - )); + } else { + for (const codec of rtpParameters.codecs) { + codec.rtcpFeedback = (codec.rtcpFeedback || []).filter( + (fb: RtcpFeedback) => + fb.type !== 'transport-cc' && fb.type !== 'goog-remb', + ); } } @@ -1131,32 +969,25 @@ export function getSendingRemoteRtpParameters( */ export function reduceCodecs( codecs: RtpCodecParameters[], - capCodec?: RtpCodecCapability -): RtpCodecParameters[] -{ + capCodec?: RtpCodecCapability, +): RtpCodecParameters[] { const filteredCodecs: RtpCodecParameters[] = []; // If no capability codec is given, take the first one (and RTX). - if (!capCodec) - { + if (!capCodec) { filteredCodecs.push(codecs[0]); - if (isRtxCodec(codecs[1])) - { + if (isRtxCodec(codecs[1])) { filteredCodecs.push(codecs[1]); } } // Otherwise look for a compatible set of codecs. - else - { - for (let idx = 0; idx < codecs.length; ++idx) - { - if (matchCodecs(codecs[idx], capCodec)) - { + else { + for (let idx = 0; idx < codecs.length; ++idx) { + if (matchCodecs(codecs[idx], capCodec)) { filteredCodecs.push(codecs[idx]); - if (isRtxCodec(codecs[idx + 1])) - { + if (isRtxCodec(codecs[idx + 1])) { filteredCodecs.push(codecs[idx + 1]); } @@ -1164,8 +995,7 @@ export function reduceCodecs( } } - if (filteredCodecs.length === 0) - { + if (filteredCodecs.length === 0) { throw new TypeError('no matching codec found'); } } @@ -1177,22 +1007,20 @@ export function reduceCodecs( * Create RTP parameters for a Consumer for the RTP probator. */ export function generateProbatorRtpParameters( - videoRtpParameters: RtpParameters -): RtpParameters -{ + videoRtpParameters: RtpParameters, +): RtpParameters { // Clone given reference video RTP parameters. videoRtpParameters = utils.clone(videoRtpParameters); // This may throw. validateRtpParameters(videoRtpParameters); - const rtpParameters: RtpParameters = - { - mid : RTP_PROBATOR_MID, - codecs : [], - headerExtensions : [], - encodings : [ { ssrc: RTP_PROBATOR_SSRC } ], - rtcp : { cname: 'probator' } + const rtpParameters: RtpParameters = { + mid: RTP_PROBATOR_MID, + codecs: [], + headerExtensions: [], + encodings: [{ ssrc: RTP_PROBATOR_SSRC }], + rtcp: { cname: 'probator' }, }; rtpParameters.codecs.push(videoRtpParameters.codecs[0]); @@ -1205,10 +1033,13 @@ export function generateProbatorRtpParameters( /** * Whether media can be sent based on the given RTP capabilities. */ -export function canSend(kind: MediaKind, extendedRtpCapabilities: any): boolean -{ - return extendedRtpCapabilities.codecs. - some((codec: any) => codec.kind === kind); +export function canSend( + kind: MediaKind, + extendedRtpCapabilities: any, +): boolean { + return extendedRtpCapabilities.codecs.some( + (codec: any) => codec.kind === kind, + ); } /** @@ -1217,27 +1048,24 @@ export function canSend(kind: MediaKind, extendedRtpCapabilities: any): boolean */ export function canReceive( rtpParameters: RtpParameters, - extendedRtpCapabilities: any -): boolean -{ + extendedRtpCapabilities: any, +): boolean { // This may throw. validateRtpParameters(rtpParameters); - if (rtpParameters.codecs.length === 0) - { + if (rtpParameters.codecs.length === 0) { return false; } const firstMediaCodec = rtpParameters.codecs[0]; - return extendedRtpCapabilities.codecs - .some((codec: any) => codec.remotePayloadType === firstMediaCodec.payloadType); + return extendedRtpCapabilities.codecs.some( + (codec: any) => codec.remotePayloadType === firstMediaCodec.payloadType, + ); } -function isRtxCodec(codec?: RtpCodecCapability | RtpCodecParameters): boolean -{ - if (!codec) - { +function isRtxCodec(codec?: RtpCodecCapability | RtpCodecParameters): boolean { + if (!codec) { return false; } @@ -1247,70 +1075,54 @@ function isRtxCodec(codec?: RtpCodecCapability | RtpCodecParameters): boolean function matchCodecs( aCodec: RtpCodecCapability | RtpCodecParameters, bCodec: RtpCodecCapability | RtpCodecParameters, - { strict = false, modify = false } = {} -): boolean -{ + { strict = false, modify = false } = {}, +): boolean { const aMimeType = aCodec.mimeType.toLowerCase(); const bMimeType = bCodec.mimeType.toLowerCase(); - if (aMimeType !== bMimeType) - { + if (aMimeType !== bMimeType) { return false; } - if (aCodec.clockRate !== bCodec.clockRate) - { + if (aCodec.clockRate !== bCodec.clockRate) { return false; } - if (aCodec.channels !== bCodec.channels) - { + if (aCodec.channels !== bCodec.channels) { return false; } // Per codec special checks. - switch (aMimeType) - { - case 'video/h264': - { - if (strict) - { + switch (aMimeType) { + case 'video/h264': { + if (strict) { const aPacketizationMode = aCodec.parameters['packetization-mode'] || 0; const bPacketizationMode = bCodec.parameters['packetization-mode'] || 0; - if (aPacketizationMode !== bPacketizationMode) - { + if (aPacketizationMode !== bPacketizationMode) { return false; } - if (!h264.isSameProfile(aCodec.parameters, bCodec.parameters)) - { + if (!h264.isSameProfile(aCodec.parameters, bCodec.parameters)) { return false; } let selectedProfileLevelId; - try - { - selectedProfileLevelId = - h264.generateProfileLevelIdStringForAnswer( - aCodec.parameters, bCodec.parameters - ); - } - catch (error) - { + try { + selectedProfileLevelId = h264.generateProfileLevelIdStringForAnswer( + aCodec.parameters, + bCodec.parameters, + ); + } catch (error) { return false; } - if (modify) - { - if (selectedProfileLevelId) - { + if (modify) { + if (selectedProfileLevelId) { aCodec.parameters['profile-level-id'] = selectedProfileLevelId; bCodec.parameters['profile-level-id'] = selectedProfileLevelId; - } - else - { + } else { delete aCodec.parameters['profile-level-id']; delete bCodec.parameters['profile-level-id']; } @@ -1320,15 +1132,12 @@ function matchCodecs( break; } - case 'video/vp9': - { - if (strict) - { + case 'video/vp9': { + if (strict) { const aProfileId = aCodec.parameters['profile-id'] || 0; const bProfileId = bCodec.parameters['profile-id'] || 0; - if (aProfileId !== bProfileId) - { + if (aProfileId !== bProfileId) { return false; } } @@ -1342,16 +1151,13 @@ function matchCodecs( function matchHeaderExtensions( aExt: RtpHeaderExtension, - bExt: RtpHeaderExtension -): boolean -{ - if (aExt.kind && bExt.kind && aExt.kind !== bExt.kind) - { + bExt: RtpHeaderExtension, +): boolean { + if (aExt.kind && bExt.kind && aExt.kind !== bExt.kind) { return false; } - if (aExt.uri !== bExt.uri) - { + if (aExt.uri !== bExt.uri) { return false; } @@ -1360,21 +1166,18 @@ function matchHeaderExtensions( function reduceRtcpFeedback( codecA: RtpCodecCapability | RtpCodecParameters, - codecB: RtpCodecCapability | RtpCodecParameters -): RtcpFeedback[] -{ + codecB: RtpCodecCapability | RtpCodecParameters, +): RtcpFeedback[] { const reducedRtcpFeedback: RtcpFeedback[] = []; - for (const aFb of codecA.rtcpFeedback || []) - { - const matchingBFb = (codecB.rtcpFeedback || []) - .find((bFb: RtcpFeedback) => ( + for (const aFb of codecA.rtcpFeedback || []) { + const matchingBFb = (codecB.rtcpFeedback || []).find( + (bFb: RtcpFeedback) => bFb.type === aFb.type && - (bFb.parameter === aFb.parameter || (!bFb.parameter && !aFb.parameter)) - )); + (bFb.parameter === aFb.parameter || (!bFb.parameter && !aFb.parameter)), + ); - if (matchingBFb) - { + if (matchingBFb) { reducedRtcpFeedback.push(matchingBFb); } } diff --git a/src/scalabilityModes.ts b/src/scalabilityModes.ts index 7eef60f5..3f31a1b5 100644 --- a/src/scalabilityModes.ts +++ b/src/scalabilityModes.ts @@ -1,27 +1,22 @@ const ScalabilityModeRegex = new RegExp('^[LS]([1-9]\\d{0,1})T([1-9]\\d{0,1})'); -export type ScalabilityMode = -{ +export type ScalabilityMode = { spatialLayers: number; temporalLayers: number; }; -export function parse(scalabilityMode?: string): ScalabilityMode -{ +export function parse(scalabilityMode?: string): ScalabilityMode { const match = ScalabilityModeRegex.exec(scalabilityMode || ''); - if (match) - { + if (match) { return { - spatialLayers : Number(match[1]), - temporalLayers : Number(match[2]) + spatialLayers: Number(match[1]), + temporalLayers: Number(match[2]), }; - } - else - { + } else { return { - spatialLayers : 1, - temporalLayers : 1 + spatialLayers: 1, + temporalLayers: 1, }; } } diff --git a/src/test/fakeParameters.ts b/src/test/fakeParameters.ts new file mode 100644 index 00000000..ccbfc4b4 --- /dev/null +++ b/src/test/fakeParameters.ts @@ -0,0 +1,637 @@ +import * as mediasoupClient from '../'; +import * as utils from '../utils'; + +function generateFakeUuid(): string { + return String(utils.generateRandomNumber()); +} + +export function generateRouterRtpCapabilities(): mediasoupClient.types.RtpCapabilities { + return { + codecs: [ + { + mimeType: 'audio/opus', + kind: 'audio', + preferredPayloadType: 100, + clockRate: 48000, + channels: 2, + rtcpFeedback: [{ type: 'transport-cc' }], + parameters: { + useinbandfec: 1, + foo: 'bar', + }, + }, + { + mimeType: 'video/VP8', + kind: 'video', + preferredPayloadType: 101, + clockRate: 90000, + rtcpFeedback: [ + { type: 'nack' }, + { type: 'nack', parameter: 'pli' }, + { type: 'ccm', parameter: 'fir' }, + { type: 'goog-remb' }, + { type: 'transport-cc' }, + ], + parameters: { + 'x-google-start-bitrate': 1500, + }, + }, + { + mimeType: 'video/rtx', + kind: 'video', + preferredPayloadType: 102, + clockRate: 90000, + rtcpFeedback: [], + parameters: { + apt: 101, + }, + }, + { + mimeType: 'video/H264', + kind: 'video', + preferredPayloadType: 103, + clockRate: 90000, + rtcpFeedback: [ + { type: 'nack' }, + { type: 'nack', parameter: 'pli' }, + { type: 'ccm', parameter: 'fir' }, + { type: 'goog-remb' }, + { type: 'transport-cc' }, + ], + parameters: { + 'level-asymmetry-allowed': 1, + 'packetization-mode': 1, + 'profile-level-id': '42e01f', + }, + }, + { + mimeType: 'video/rtx', + kind: 'video', + preferredPayloadType: 104, + clockRate: 90000, + rtcpFeedback: [], + parameters: { + apt: 103, + }, + }, + ], + headerExtensions: [ + { + kind: 'audio', + uri: 'urn:ietf:params:rtp-hdrext:sdes:mid', + preferredId: 1, + preferredEncrypt: false, + direction: 'sendrecv', + }, + { + kind: 'video', + uri: 'urn:ietf:params:rtp-hdrext:sdes:mid', + preferredId: 1, + preferredEncrypt: false, + direction: 'sendrecv', + }, + { + kind: 'video', + uri: 'urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id', + preferredId: 2, + preferredEncrypt: false, + direction: 'recvonly', + }, + { + kind: 'video', + uri: 'urn:ietf:params:rtp-hdrext:sdes:repaired-rtp-stream-id', + preferredId: 3, + preferredEncrypt: false, + direction: 'recvonly', + }, + { + kind: 'audio', + uri: 'http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time', + preferredId: 4, + preferredEncrypt: false, + direction: 'sendrecv', + }, + { + kind: 'video', + uri: 'http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time', + preferredId: 4, + preferredEncrypt: false, + direction: 'sendrecv', + }, + { + kind: 'audio', + uri: 'http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01', + preferredId: 5, + preferredEncrypt: false, + direction: 'recvonly', + }, + { + kind: 'video', + uri: 'http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01', + preferredId: 5, + preferredEncrypt: false, + direction: 'sendrecv', + }, + { + kind: 'video', + uri: 'http://tools.ietf.org/html/draft-ietf-avtext-framemarking-07', + preferredId: 6, + preferredEncrypt: false, + direction: 'sendrecv', + }, + { + kind: 'video', + uri: 'urn:ietf:params:rtp-hdrext:framemarking', + preferredId: 7, + preferredEncrypt: false, + direction: 'sendrecv', + }, + { + kind: 'audio', + uri: 'urn:ietf:params:rtp-hdrext:ssrc-audio-level', + preferredId: 10, + preferredEncrypt: false, + direction: 'sendrecv', + }, + { + kind: 'video', + uri: 'urn:3gpp:video-orientation', + preferredId: 11, + preferredEncrypt: false, + direction: 'sendrecv', + }, + { + kind: 'video', + uri: 'urn:ietf:params:rtp-hdrext:toffset', + preferredId: 12, + preferredEncrypt: false, + direction: 'sendrecv', + }, + ], + }; +} + +export function generateNativeRtpCapabilities(): mediasoupClient.types.RtpCapabilities { + return { + codecs: [ + { + mimeType: 'audio/opus', + kind: 'audio', + preferredPayloadType: 111, + clockRate: 48000, + channels: 2, + rtcpFeedback: [{ type: 'transport-cc' }], + parameters: { + minptime: 10, + useinbandfec: 1, + }, + }, + { + mimeType: 'audio/ISAC', + kind: 'audio', + preferredPayloadType: 103, + clockRate: 16000, + channels: 1, + rtcpFeedback: [{ type: 'transport-cc' }], + parameters: {}, + }, + { + mimeType: 'audio/CN', + kind: 'audio', + preferredPayloadType: 106, + clockRate: 32000, + channels: 1, + rtcpFeedback: [{ type: 'transport-cc' }], + parameters: {}, + }, + { + mimeType: 'video/VP8', + kind: 'video', + preferredPayloadType: 96, + clockRate: 90000, + rtcpFeedback: [ + { type: 'goog-remb' }, + { type: 'transport-cc' }, + { type: 'ccm', parameter: 'fir' }, + { type: 'nack' }, + { type: 'nack', parameter: 'pli' }, + ], + parameters: { + baz: '1234abcd', + }, + }, + { + mimeType: 'video/rtx', + kind: 'video', + preferredPayloadType: 97, + clockRate: 90000, + rtcpFeedback: [], + parameters: { + apt: 96, + }, + }, + ], + headerExtensions: [ + { + kind: 'audio', + uri: 'urn:ietf:params:rtp-hdrext:sdes:mid', + preferredId: 1, + }, + { + kind: 'video', + uri: 'urn:ietf:params:rtp-hdrext:sdes:mid', + preferredId: 1, + }, + { + kind: 'video', + uri: 'urn:ietf:params:rtp-hdrext:toffset', + preferredId: 2, + }, + { + kind: 'video', + uri: 'http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time', + preferredId: 3, + }, + { + kind: 'video', + uri: 'urn:3gpp:video-orientation', + preferredId: 4, + }, + { + kind: 'video', + uri: 'http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01', + preferredId: 5, + }, + { + kind: 'video', + // @ts-ignore + uri: 'http://www.webrtc.org/experiments/rtp-hdrext/playout-delay', + preferredId: 6, + }, + { + kind: 'video', + // @ts-ignore + uri: 'http://www.webrtc.org/experiments/rtp-hdrext/video-content-type', + preferredId: 7, + }, + { + kind: 'video', + // @ts-ignore + uri: 'http://www.webrtc.org/experiments/rtp-hdrext/video-timing', + preferredId: 8, + }, + { + kind: 'audio', + uri: 'urn:ietf:params:rtp-hdrext:ssrc-audio-level', + preferredId: 10, + }, + ], + }; +} + +export function generateNativeSctpCapabilities(): mediasoupClient.types.SctpCapabilities { + return { + numStreams: { OS: 2048, MIS: 2048 }, + }; +} + +export function generateLocalDtlsParameters(): mediasoupClient.types.DtlsParameters { + return { + fingerprints: [ + { + algorithm: 'sha-256', + value: + '82:5A:68:3D:36:C3:0A:DE:AF:E7:32:43:D2:88:83:57:AC:2D:65:E5:80:C4:B6:FB:AF:1A:A0:21:9F:6D:0C:AD', + }, + ], + role: 'auto', + }; +} + +export function generateTransportRemoteParameters(): mediasoupClient.types.TransportOptions { + return { + id: generateFakeUuid(), + iceParameters: { + iceLite: true, + password: 'yku5ej8nvfaor28lvtrabcx0wkrpkztz', + usernameFragment: 'h3hk1iz6qqlnqlne', + }, + iceCandidates: [ + { + foundation: 'udpcandidate', + ip: '9.9.9.9', + port: 40533, + priority: 1078862079, + protocol: 'udp', + type: 'host', + tcpType: 'passive', + }, + { + foundation: 'udpcandidate', + ip: '9:9:9:9:9:9', + port: 41333, + priority: 1078862089, + protocol: 'udp', + type: 'host', + tcpType: 'passive', + }, + ], + dtlsParameters: { + fingerprints: [ + { + algorithm: 'sha-256', + value: + 'A9:F4:E0:D2:74:D3:0F:D9:CA:A5:2F:9F:7F:47:FA:F0:C4:72:DD:73:49:D0:3B:14:90:20:51:30:1B:90:8E:71', + }, + { + algorithm: 'sha-384', + value: + '03:D9:0B:87:13:98:F6:6D:BC:FC:92:2E:39:D4:E1:97:32:61:30:56:84:70:81:6E:D1:82:97:EA:D9:C1:21:0F:6B:C5:E7:7F:E1:97:0C:17:97:6E:CF:B3:EF:2E:74:B0', + }, + { + algorithm: 'sha-512', + value: + '84:27:A4:28:A4:73:AF:43:02:2A:44:68:FF:2F:29:5C:3B:11:9A:60:F4:A8:F0:F5:AC:A0:E3:49:3E:B1:34:53:A9:85:CE:51:9B:ED:87:5E:B8:F4:8E:3D:FA:20:51:B8:96:EE:DA:56:DC:2F:5C:62:79:15:23:E0:21:82:2B:2C', + }, + ], + role: 'auto', + }, + sctpParameters: { + port: 5000, + OS: 2048, + MIS: 2048, + maxMessageSize: 2000000, + }, + }; +} + +export function generateProducerRemoteParameters(): { id: string } { + return { + id: generateFakeUuid(), + }; +} + +export function generateConsumerRemoteParameters({ + id, + codecMimeType, +}: { + id?: string; + codecMimeType?: string; +} = {}): mediasoupClient.types.ConsumerOptions { + switch (codecMimeType) { + case 'audio/opus': { + return { + id: id || generateFakeUuid(), + producerId: generateFakeUuid(), + kind: 'audio', + rtpParameters: { + codecs: [ + { + mimeType: 'audio/opus', + payloadType: 100, + clockRate: 48000, + channels: 2, + rtcpFeedback: [{ type: 'transport-cc' }], + parameters: { + useinbandfec: 1, + foo: 'bar', + }, + }, + ], + encodings: [ + { + ssrc: 46687003, + }, + ], + headerExtensions: [ + { + uri: 'urn:ietf:params:rtp-hdrext:sdes:mid', + id: 1, + }, + { + uri: 'http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01', + id: 5, + }, + { + uri: 'urn:ietf:params:rtp-hdrext:ssrc-audio-level', + id: 10, + }, + ], + rtcp: { + cname: 'wB4Ql4lrsxYLjzuN', + reducedSize: true, + mux: true, + }, + }, + }; + } + + case 'audio/ISAC': { + return { + id: id || generateFakeUuid(), + producerId: generateFakeUuid(), + kind: 'audio', + rtpParameters: { + codecs: [ + { + mimeType: 'audio/ISAC', + payloadType: 111, + clockRate: 16000, + channels: 1, + rtcpFeedback: [{ type: 'transport-cc' }], + parameters: {}, + }, + ], + encodings: [ + { + ssrc: 46687004, + }, + ], + headerExtensions: [ + { + uri: 'urn:ietf:params:rtp-hdrext:sdes:mid', + id: 1, + }, + { + uri: 'http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01', + id: 5, + }, + ], + rtcp: { + cname: 'wB4Ql4lrsxYLjzuN', + reducedSize: true, + mux: true, + }, + }, + }; + } + + case 'video/VP8': { + return { + id: id || generateFakeUuid(), + producerId: generateFakeUuid(), + kind: 'video', + rtpParameters: { + codecs: [ + { + mimeType: 'video/VP8', + payloadType: 101, + clockRate: 90000, + rtcpFeedback: [ + { type: 'nack' }, + { type: 'nack', parameter: 'pli' }, + { type: 'ccm', parameter: 'fir' }, + { type: 'goog-remb' }, + { type: 'transport-cc' }, + ], + parameters: { + 'x-google-start-bitrate': 1500, + }, + }, + { + mimeType: 'video/rtx', + payloadType: 102, + clockRate: 90000, + rtcpFeedback: [], + parameters: { + apt: 101, + }, + }, + ], + encodings: [ + { + ssrc: 99991111, + rtx: { + ssrc: 99991112, + }, + }, + ], + headerExtensions: [ + { + uri: 'urn:ietf:params:rtp-hdrext:sdes:mid', + id: 1, + }, + { + uri: 'http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time', + id: 4, + }, + { + uri: 'http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01', + id: 5, + }, + { + uri: 'urn:3gpp:video-orientation', + id: 11, + }, + { + uri: 'urn:ietf:params:rtp-hdrext:toffset', + id: 12, + }, + ], + rtcp: { + cname: 'wB4Ql4lrsxYLjzuN', + reducedSize: true, + mux: true, + }, + }, + }; + } + + case 'video/H264': { + return { + id: id || generateFakeUuid(), + producerId: generateFakeUuid(), + kind: 'video', + rtpParameters: { + codecs: [ + { + mimeType: 'video/H264', + payloadType: 103, + clockRate: 90000, + rtcpFeedback: [ + { type: 'nack' }, + { type: 'nack', parameter: 'pli' }, + { type: 'ccm', parameter: 'fir' }, + { type: 'goog-remb' }, + { type: 'transport-cc' }, + ], + parameters: { + 'level-asymmetry-allowed': 1, + 'packetization-mode': 1, + 'profile-level-id': '42e01f', + }, + }, + { + mimeType: 'video/rtx', + payloadType: 104, + clockRate: 90000, + rtcpFeedback: [], + parameters: { + apt: 103, + }, + }, + ], + encodings: [ + { + ssrc: 99991113, + rtx: { + ssrc: 99991114, + }, + }, + ], + headerExtensions: [ + { + uri: 'urn:ietf:params:rtp-hdrext:sdes:mid', + id: 1, + }, + { + uri: 'http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time', + id: 4, + }, + { + uri: 'http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01', + id: 5, + }, + { + uri: 'urn:3gpp:video-orientation', + id: 11, + }, + { + uri: 'urn:ietf:params:rtp-hdrext:toffset', + id: 12, + }, + ], + rtcp: { + cname: 'wB4Ql4lrsxYLjzuN', + reducedSize: true, + mux: true, + }, + }, + }; + } + + default: { + throw new TypeError(`unknown codecMimeType "${codecMimeType}"`); + } + } +} + +export function generateDataProducerRemoteParameters(): { id: string } { + return { + id: generateFakeUuid(), + }; +} + +export function generateDataConsumerRemoteParameters({ + id, +}: { id?: string } = {}): mediasoupClient.types.DataConsumerOptions { + return { + id: id || generateFakeUuid(), + dataProducerId: generateFakeUuid(), + sctpStreamParameters: { + streamId: 666, + maxPacketLifeTime: 5000, + maxRetransmits: undefined, + }, + }; +} diff --git a/src/tests/test.ts b/src/test/test.ts similarity index 56% rename from src/tests/test.ts rename to src/test/test.ts index 24361824..5a101626 100644 --- a/src/tests/test.ts +++ b/src/test/test.ts @@ -15,12 +15,7 @@ import { RtpCapabilities } from '../RtpParameters'; import * as fakeParameters from './fakeParameters'; import { uaTestCases } from './uaTestCases'; -const { - Device, - detectDevice, - parseScalabilityMode, - debug -} = mediasoupClient; +const { Device, detectDevice, parseScalabilityMode, debug } = mediasoupClient; let device: mediasoupClient.types.Device; let sendTransport: mediasoupClient.types.Transport; @@ -32,171 +27,132 @@ let videoConsumer: mediasoupClient.types.Consumer; let dataProducer: mediasoupClient.types.DataProducer; let dataConsumer: mediasoupClient.types.DataConsumer; -test('mediasoup-client exposes debug dependency', () => -{ +test('mediasoup-client exposes debug dependency', () => { expect(typeof debug).toBe('function'); }, 500); -test('detectDevice() returns nothing in Node', () => -{ +test('detectDevice() returns nothing in Node', () => { expect(detectDevice()).toBe(undefined); }); -test('create a Device in Node without custom handlerName/handlerFactory throws UnsupportedError', () => -{ - expect(() => new Device()) - .toThrow(UnsupportedError); +test('create a Device in Node without custom handlerName/handlerFactory throws UnsupportedError', () => { + expect(() => new Device()).toThrow(UnsupportedError); }); -test('create a Device with an unknown handlerName string throws TypeError', () => -{ +test('create a Device with an unknown handlerName string throws TypeError', () => { // @ts-ignore - expect(() => new Device({ handlerName: 'FooBrowser666' })) - .toThrow(TypeError); + expect(() => new Device({ handlerName: 'FooBrowser666' })).toThrow(TypeError); }); -test('create a Device in Node with a valid handlerFactory succeeds', () => -{ - device = new Device({ handlerFactory: FakeHandler.createFactory(fakeParameters) }); +test('create a Device in Node with a valid handlerFactory succeeds', () => { + device = new Device({ + handlerFactory: FakeHandler.createFactory(fakeParameters), + }); expect(typeof device).toBe('object'); expect(device.handlerName).toBe('FakeHandler'); expect(device.loaded).toBe(false); }); -test('device.rtpCapabilities getter throws InvalidStateError if not loaded', () => -{ - expect(() => device.rtpCapabilities) - .toThrow(InvalidStateError); +test('device.rtpCapabilities getter throws InvalidStateError if not loaded', () => { + expect(() => device.rtpCapabilities).toThrow(InvalidStateError); }); -test('device.sctpCapabilities getter throws InvalidStateError if not loaded', () => -{ - expect(() => device.sctpCapabilities) - .toThrow(InvalidStateError); +test('device.sctpCapabilities getter throws InvalidStateError if not loaded', () => { + expect(() => device.sctpCapabilities).toThrow(InvalidStateError); }); -test('device.canProduce() throws InvalidStateError if not loaded', () => -{ - expect(() => device.canProduce('audio')) - .toThrow(InvalidStateError); +test('device.canProduce() throws InvalidStateError if not loaded', () => { + expect(() => device.canProduce('audio')).toThrow(InvalidStateError); }); -test('device.createSendTransport() throws InvalidStateError if not loaded', () => -{ - const { - id, - iceParameters, - iceCandidates, - dtlsParameters, - sctpParameters - } = fakeParameters.generateTransportRemoteParameters(); +test('device.createSendTransport() throws InvalidStateError if not loaded', () => { + const { id, iceParameters, iceCandidates, dtlsParameters, sctpParameters } = + fakeParameters.generateTransportRemoteParameters(); - expect(() => device.createSendTransport( - { + expect(() => + device.createSendTransport({ id, iceParameters, iceCandidates, dtlsParameters, - sctpParameters - })) - .toThrow(InvalidStateError); + sctpParameters, + }), + ).toThrow(InvalidStateError); }); -test('device.load() without routerRtpCapabilities rejects with TypeError', async () => -{ +test('device.load() without routerRtpCapabilities rejects with TypeError', async () => { // @ts-ignore - await expect(device.load({})) - .rejects - .toThrow(TypeError); + await expect(device.load({})).rejects.toThrow(TypeError); expect(device.loaded).toBe(false); }, 500); -test('device.load() with invalid routerRtpCapabilities rejects with TypeError', async () => -{ +test('device.load() with invalid routerRtpCapabilities rejects with TypeError', async () => { // Clonse fake router RTP capabilities to make them invalid. - const routerRtpCapabilities = - utils.clone(fakeParameters.generateRouterRtpCapabilities()); + const routerRtpCapabilities = utils.clone( + fakeParameters.generateRouterRtpCapabilities(), + ); - for (const codec of routerRtpCapabilities.codecs!) - { + for (const codec of routerRtpCapabilities.codecs!) { // @ts-ignore delete codec!.mimeType; } - await expect(device.load({ routerRtpCapabilities })) - .rejects - .toThrow(TypeError); + await expect(device.load({ routerRtpCapabilities })).rejects.toThrow( + TypeError, + ); expect(device.loaded).toBe(false); }, 500); -test('device.load() succeeds', async () => -{ +test('device.load() succeeds', async () => { // Assume we get the router RTP capabilities. const routerRtpCapabilities = fakeParameters.generateRouterRtpCapabilities(); - await expect(device.load({ routerRtpCapabilities })) - .resolves - .toBe(undefined); + await expect(device.load({ routerRtpCapabilities })).resolves.toBe(undefined); expect(device.loaded).toBe(true); }, 500); -test('device.load() rejects with InvalidStateError if already loaded', async () => -{ +test('device.load() rejects with InvalidStateError if already loaded', async () => { // @ts-ignore - await expect(device.load({})) - .rejects - .toThrow(InvalidStateError); + await expect(device.load({})).rejects.toThrow(InvalidStateError); expect(device.loaded).toBe(true); }, 500); -test('device.rtpCapabilities getter succeeds', () => -{ +test('device.rtpCapabilities getter succeeds', () => { expect(typeof device.rtpCapabilities).toBe('object'); }); -test('device.sctpCapabilities getter succeeds', () => -{ +test('device.sctpCapabilities getter succeeds', () => { expect(typeof device.sctpCapabilities).toBe('object'); }); -test('device.canProduce() with "audio"/"video" kind returns true', () => -{ +test('device.canProduce() with "audio"/"video" kind returns true', () => { expect(device.canProduce('audio')).toBe(true); expect(device.canProduce('video')).toBe(true); }); -test('device.canProduce() with invalid kind throws TypeError', () => -{ +test('device.canProduce() with invalid kind throws TypeError', () => { // @ts-ignore - expect(() => device.canProduce('chicken')) - .toThrow(TypeError); + expect(() => device.canProduce('chicken')).toThrow(TypeError); }); -test('device.createSendTransport() for sending media succeeds', () => -{ +test('device.createSendTransport() for sending media succeeds', () => { // Assume we create a transport in the server and get its remote parameters. - const { + const { id, iceParameters, iceCandidates, dtlsParameters, sctpParameters } = + fakeParameters.generateTransportRemoteParameters(); + + sendTransport = device.createSendTransport<{ foo: number }>({ id, iceParameters, iceCandidates, dtlsParameters, - sctpParameters - } = fakeParameters.generateTransportRemoteParameters(); - - sendTransport = device.createSendTransport<{ foo: number }>( - { - id, - iceParameters, - iceCandidates, - dtlsParameters, - sctpParameters, - appData : { foo: 123 } - }); + sctpParameters, + appData: { foo: 123 }, + }); expect(typeof sendTransport).toBe('object'); expect(sendTransport.id).toBe(id); @@ -208,25 +164,18 @@ test('device.createSendTransport() for sending media succeeds', () => expect(sendTransport.appData).toEqual({ foo: 123 }); }); -test('device.createRecvTransport() for receiving media succeeds', () => -{ +test('device.createRecvTransport() for receiving media succeeds', () => { // Assume we create a transport in the server and get its remote parameters. - const { + const { id, iceParameters, iceCandidates, dtlsParameters, sctpParameters } = + fakeParameters.generateTransportRemoteParameters(); + + recvTransport = device.createRecvTransport({ id, iceParameters, iceCandidates, dtlsParameters, - sctpParameters - } = fakeParameters.generateTransportRemoteParameters(); - - recvTransport = device.createRecvTransport( - { - id, - iceParameters, - iceCandidates, - dtlsParameters, - sctpParameters - }); + sctpParameters, + }); expect(typeof recvTransport).toBe('object'); expect(recvTransport.id).toBe(id); @@ -238,60 +187,51 @@ test('device.createRecvTransport() for receiving media succeeds', () => expect(recvTransport.appData).toEqual({}); }); -test('device.createSendTransport() with missing remote Transport parameters throws TypeError', () => -{ +test('device.createSendTransport() with missing remote Transport parameters throws TypeError', () => { // @ts-ignore - expect(() => device.createSendTransport({ id: '1234' })) - .toThrow(TypeError); + expect(() => device.createSendTransport({ id: '1234' })).toThrow(TypeError); - // @ts-ignore - expect(() => device.createSendTransport({ id: '1234', iceParameters: {} })) - .toThrow(TypeError); + expect(() => + // @ts-ignore + device.createSendTransport({ id: '1234', iceParameters: {} }), + ).toThrow(TypeError); - expect(() => device.createSendTransport( - { - id : '1234', + expect(() => + device.createSendTransport({ + id: '1234', // @ts-ignore - iceParameters : {}, - iceCandidates : [] - })) - .toThrow(TypeError); + iceParameters: {}, + iceCandidates: [], + }), + ).toThrow(TypeError); }); -test('device.createRecvTransport() with a non object appData throws TypeError', () => -{ - const { - id, - iceParameters, - iceCandidates, - dtlsParameters, - sctpParameters - } = fakeParameters.generateTransportRemoteParameters(); +test('device.createRecvTransport() with a non object appData throws TypeError', () => { + const { id, iceParameters, iceCandidates, dtlsParameters, sctpParameters } = + fakeParameters.generateTransportRemoteParameters(); - expect(() => device.createRecvTransport( - { + expect(() => + device.createRecvTransport({ id, iceParameters, iceCandidates, dtlsParameters, sctpParameters, // @ts-ignore - appData : 1234 - })) - .toThrow(TypeError); + appData: 1234, + }), + ).toThrow(TypeError); }); -test('transport.produce() without "connect" listener rejects', async () => -{ +test('transport.produce() without "connect" listener rejects', async () => { const audioTrack = new FakeMediaStreamTrack({ kind: 'audio' }); - await expect(sendTransport.produce({ track: audioTrack })) - .rejects - .toThrow(Error); + await expect(sendTransport.produce({ track: audioTrack })).rejects.toThrow( + Error, + ); }, 500); -test('transport.produce() succeeds', async () => -{ +test('transport.produce() succeeds', async () => { const audioTrack = new FakeMediaStreamTrack({ kind: 'audio' }); const videoTrack = new FakeMediaStreamTrack({ kind: 'video' }); let audioProducerId; @@ -300,8 +240,7 @@ test('transport.produce() succeeds', async () => let produceEventNumTimesCalled = 0; // eslint-disable-next-line no-unused-vars - sendTransport.on('connect', ({ dtlsParameters }, callback /* errback */) => - { + sendTransport.on('connect', ({ dtlsParameters }, callback /* errback */) => { connectEventNumTimesCalled++; expect(typeof dtlsParameters).toBe('object'); @@ -312,47 +251,45 @@ test('transport.produce() succeeds', async () => }); // eslint-disable-next-line no-unused-vars - sendTransport.on('produce', ({ kind, rtpParameters, appData }, callback /* errback */) => - { - produceEventNumTimesCalled++; + sendTransport.on( + 'produce', + ({ kind, rtpParameters, appData }, callback /* errback */) => { + produceEventNumTimesCalled++; - expect(typeof kind).toBe('string'); - expect(typeof rtpParameters).toBe('object'); + expect(typeof kind).toBe('string'); + expect(typeof rtpParameters).toBe('object'); - let id: string; + let id: string; - switch (kind) - { - case 'audio': - { - expect(appData).toEqual({ foo: 'FOO' }); + switch (kind) { + case 'audio': { + expect(appData).toEqual({ foo: 'FOO' }); - id = fakeParameters.generateProducerRemoteParameters().id; - audioProducerId = id; + id = fakeParameters.generateProducerRemoteParameters().id; + audioProducerId = id; - break; - } + break; + } - case 'video': - { - expect(appData).toEqual({}); + case 'video': { + expect(appData).toEqual({}); - id = fakeParameters.generateProducerRemoteParameters().id; - videoProducerId = id; + id = fakeParameters.generateProducerRemoteParameters().id; + videoProducerId = id; - break; - } + break; + } - default: - { - throw new Error('unknown kind'); + default: { + throw new Error('unknown kind'); + } } - } - // Emulate communication with the server and success response with Producer - // remote parameters. - setTimeout(() => callback({ id })); - }); + // Emulate communication with the server and success response with Producer + // remote parameters. + setTimeout(() => callback({ id })); + }, + ); let codecs; let headerExtensions; @@ -363,8 +300,11 @@ test('transport.produce() succeeds', async () => audioTrack.enabled = false; // Use stopTracks: false. - audioProducer = await sendTransport.produce<{ foo: string }>( - { track: audioTrack, stopTracks: false, appData: { foo: 'FOO' } }); + audioProducer = await sendTransport.produce<{ foo: string }>({ + track: audioTrack, + stopTracks: false, + appData: { foo: 'FOO' }, + }); expect(connectEventNumTimesCalled).toBe(1); expect(produceEventNumTimesCalled).toBe(1); @@ -379,47 +319,41 @@ test('transport.produce() succeeds', async () => codecs = audioProducer.rtpParameters.codecs; - expect(codecs[0]).toEqual( - { - mimeType : 'audio/opus', - payloadType : 111, - clockRate : 48000, - channels : 2, - rtcpFeedback : - [ - { type: 'transport-cc', parameter: '' } - ], - parameters : - { - minptime : 10, - useinbandfec : 1 - } - }); + expect(codecs[0]).toEqual({ + mimeType: 'audio/opus', + payloadType: 111, + clockRate: 48000, + channels: 2, + rtcpFeedback: [{ type: 'transport-cc', parameter: '' }], + parameters: { + minptime: 10, + useinbandfec: 1, + }, + }); headerExtensions = audioProducer.rtpParameters.headerExtensions; - expect(headerExtensions).toEqual( - [ - { - uri : 'urn:ietf:params:rtp-hdrext:sdes:mid', - id : 1, - encrypt : false, - parameters : {} - }, - { - uri : 'urn:ietf:params:rtp-hdrext:ssrc-audio-level', - id : 10, - encrypt : false, - parameters : {} - } - ]); + expect(headerExtensions).toEqual([ + { + uri: 'urn:ietf:params:rtp-hdrext:sdes:mid', + id: 1, + encrypt: false, + parameters: {}, + }, + { + uri: 'urn:ietf:params:rtp-hdrext:ssrc-audio-level', + id: 10, + encrypt: false, + parameters: {}, + }, + ]); encodings = audioProducer.rtpParameters.encodings; expect(Array.isArray(encodings)).toBe(true); expect(encodings!.length).toBe(1); expect(typeof encodings?.[0]).toBe('object'); - expect(Object.keys(encodings![0])).toEqual([ 'ssrc', 'dtx' ]); + expect(Object.keys(encodings![0])).toEqual(['ssrc', 'dtx']); expect(typeof encodings?.[0].ssrc).toBe('number'); rtcp = audioProducer.rtpParameters.rtcp; @@ -434,21 +368,16 @@ test('transport.produce() succeeds', async () => // Reset the audio paused state. audioProducer.resume(); - const videoEncodings = - [ - { maxBitrate: 100000 }, - { maxBitrate: 500000 } - ]; + const videoEncodings = [{ maxBitrate: 100000 }, { maxBitrate: 500000 }]; // Note that stopTracks is not give so it's true by default. // Use disableTrackOnPause: false and zeroRtpOnPause: true - videoProducer = await sendTransport.produce( - { - track : videoTrack, - encodings : videoEncodings, - disableTrackOnPause : false, - zeroRtpOnPause : true - }); + videoProducer = await sendTransport.produce({ + track: videoTrack, + encodings: videoEncodings, + disableTrackOnPause: false, + zeroRtpOnPause: true, + }); expect(connectEventNumTimesCalled).toBe(1); expect(produceEventNumTimesCalled).toBe(2); @@ -463,72 +392,66 @@ test('transport.produce() succeeds', async () => codecs = videoProducer.rtpParameters.codecs; - expect(codecs[0]).toEqual( - { - mimeType : 'video/VP8', - payloadType : 96, - clockRate : 90000, - rtcpFeedback : - [ - { type: 'goog-remb', parameter: '' }, - { type: 'transport-cc', parameter: '' }, - { type: 'ccm', parameter: 'fir' }, - { type: 'nack', parameter: '' }, - { type: 'nack', parameter: 'pli' } - ], - parameters : - { - baz : '1234abcd' - } - }); + expect(codecs[0]).toEqual({ + mimeType: 'video/VP8', + payloadType: 96, + clockRate: 90000, + rtcpFeedback: [ + { type: 'goog-remb', parameter: '' }, + { type: 'transport-cc', parameter: '' }, + { type: 'ccm', parameter: 'fir' }, + { type: 'nack', parameter: '' }, + { type: 'nack', parameter: 'pli' }, + ], + parameters: { + baz: '1234abcd', + }, + }); - expect(codecs[1]).toEqual( - { - mimeType : 'video/rtx', - payloadType : 97, - clockRate : 90000, - rtcpFeedback : [], - parameters : - { - apt : 96 - } - }); + expect(codecs[1]).toEqual({ + mimeType: 'video/rtx', + payloadType: 97, + clockRate: 90000, + rtcpFeedback: [], + parameters: { + apt: 96, + }, + }); headerExtensions = videoProducer.rtpParameters.headerExtensions; - expect(headerExtensions).toEqual( - [ - { - uri : 'urn:ietf:params:rtp-hdrext:sdes:mid', - id : 1, - encrypt : false, - parameters : {} - }, - { - uri : 'http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time', - id : 3, - encrypt : false, - parameters : {} - }, - { - uri : 'http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01', - id : 5, - encrypt : false, - parameters : {} - }, - { - uri : 'urn:3gpp:video-orientation', - id : 4, - encrypt : false, - parameters : {} - }, - { - uri : 'urn:ietf:params:rtp-hdrext:toffset', - id : 2, - encrypt : false, - parameters : {} - } - ]); + expect(headerExtensions).toEqual([ + { + uri: 'urn:ietf:params:rtp-hdrext:sdes:mid', + id: 1, + encrypt: false, + parameters: {}, + }, + { + uri: 'http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time', + id: 3, + encrypt: false, + parameters: {}, + }, + { + uri: 'http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01', + id: 5, + encrypt: false, + parameters: {}, + }, + { + uri: 'urn:3gpp:video-orientation', + id: 4, + encrypt: false, + parameters: {}, + }, + { + uri: 'urn:ietf:params:rtp-hdrext:toffset', + id: 2, + encrypt: false, + parameters: {}, + }, + ]); encodings = videoProducer.rtpParameters.encodings; @@ -537,12 +460,12 @@ test('transport.produce() succeeds', async () => expect(typeof encodings?.[0]).toBe('object'); expect(typeof encodings?.[0].ssrc).toBe('number'); expect(typeof encodings?.[0].rtx).toBe('object'); - expect(Object.keys(encodings![0].rtx!)).toEqual([ 'ssrc' ]); + expect(Object.keys(encodings![0].rtx!)).toEqual(['ssrc']); expect(typeof encodings?.[0].rtx?.ssrc).toBe('number'); expect(typeof encodings?.[1]).toBe('object'); expect(typeof encodings?.[1].ssrc).toBe('number'); expect(typeof encodings?.[1].rtx).toBe('object'); - expect(Object.keys(encodings![1].rtx!)).toEqual([ 'ssrc' ]); + expect(Object.keys(encodings![1].rtx!)).toEqual(['ssrc']); expect(typeof encodings?.[1].rtx?.ssrc).toBe('number'); rtcp = videoProducer.rtpParameters.rtcp; @@ -558,54 +481,50 @@ test('transport.produce() succeeds', async () => sendTransport.removeAllListeners('produce'); }, 500); -test('transport.produce() without track rejects with TypeError', async () => -{ - await expect(sendTransport.produce({})) - .rejects - .toThrow(TypeError); +test('transport.produce() without track rejects with TypeError', async () => { + await expect(sendTransport.produce({})).rejects.toThrow(TypeError); }, 500); -test('transport.produce() in a receiving Transport rejects with UnsupportedError', async () => -{ +test('transport.produce() in a receiving Transport rejects with UnsupportedError', async () => { const track = new FakeMediaStreamTrack({ kind: 'audio' }); - await expect(recvTransport.produce({ track })) - .rejects - .toThrow(UnsupportedError); + await expect(recvTransport.produce({ track })).rejects.toThrow( + UnsupportedError, + ); }, 500); -test('transport.produce() with an ended track rejects with InvalidStateError', async () => -{ +test('transport.produce() with an ended track rejects with InvalidStateError', async () => { const track = new FakeMediaStreamTrack({ kind: 'audio' }); track.stop(); - await expect(sendTransport.produce({ track })) - .rejects - .toThrow(InvalidStateError); + await expect(sendTransport.produce({ track })).rejects.toThrow( + InvalidStateError, + ); }, 500); -test('transport.produce() with a non object appData rejects with TypeError', async () => -{ +test('transport.produce() with a non object appData rejects with TypeError', async () => { const track = new FakeMediaStreamTrack({ kind: 'audio' }); // @ts-ignore - await expect(sendTransport.produce({ track, appData: true })) - .rejects - .toThrow(TypeError); + await expect(sendTransport.produce({ track, appData: true })).rejects.toThrow( + TypeError, + ); }, 500); -test('transport.consume() succeeds', async () => -{ +test('transport.consume() succeeds', async () => { const audioConsumerRemoteParameters = - fakeParameters.generateConsumerRemoteParameters({ codecMimeType: 'audio/opus' }); + fakeParameters.generateConsumerRemoteParameters({ + codecMimeType: 'audio/opus', + }); const videoConsumerRemoteParameters = - fakeParameters.generateConsumerRemoteParameters({ codecMimeType: 'video/VP8' }); + fakeParameters.generateConsumerRemoteParameters({ + codecMimeType: 'video/VP8', + }); let connectEventNumTimesCalled = 0; // eslint-disable-next-line no-unused-vars - recvTransport.on('connect', ({ dtlsParameters }, callback /* errback */) => - { + recvTransport.on('connect', ({ dtlsParameters }, callback /* errback */) => { connectEventNumTimesCalled++; expect(typeof dtlsParameters).toBe('object'); @@ -620,19 +539,20 @@ test('transport.consume() succeeds', async () => let encodings; let rtcp; - audioConsumer = await recvTransport.consume<{ bar: string }>( - { - id : audioConsumerRemoteParameters.id, - producerId : audioConsumerRemoteParameters.producerId, - kind : audioConsumerRemoteParameters.kind, - rtpParameters : audioConsumerRemoteParameters.rtpParameters, - appData : { bar: 'BAR' } - }); + audioConsumer = await recvTransport.consume<{ bar: string }>({ + id: audioConsumerRemoteParameters.id, + producerId: audioConsumerRemoteParameters.producerId, + kind: audioConsumerRemoteParameters.kind, + rtpParameters: audioConsumerRemoteParameters.rtpParameters, + appData: { bar: 'BAR' }, + }); expect(connectEventNumTimesCalled).toBe(1); expect(typeof audioConsumer).toBe('object'); expect(audioConsumer.id).toBe(audioConsumerRemoteParameters.id); - expect(audioConsumer.producerId).toBe(audioConsumerRemoteParameters.producerId); + expect(audioConsumer.producerId).toBe( + audioConsumerRemoteParameters.producerId, + ); expect(audioConsumer.closed).toBe(false); expect(audioConsumer.kind).toBe('audio'); expect(typeof audioConsumer.track).toBe('object'); @@ -642,53 +562,47 @@ test('transport.consume() succeeds', async () => codecs = audioConsumer.rtpParameters.codecs; - expect(codecs[0]).toEqual( - { - mimeType : 'audio/opus', - payloadType : 100, - clockRate : 48000, - channels : 2, - rtcpFeedback : - [ - { type: 'transport-cc', parameter: '' } - ], - parameters : - { - useinbandfec : 1, - foo : 'bar' - } - }); + expect(codecs[0]).toEqual({ + mimeType: 'audio/opus', + payloadType: 100, + clockRate: 48000, + channels: 2, + rtcpFeedback: [{ type: 'transport-cc', parameter: '' }], + parameters: { + useinbandfec: 1, + foo: 'bar', + }, + }); headerExtensions = audioConsumer.rtpParameters.headerExtensions; - expect(headerExtensions).toEqual( - [ - { - uri : 'urn:ietf:params:rtp-hdrext:sdes:mid', - id : 1, - encrypt : false, - parameters : {} - }, - { - uri : 'http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01', - id : 5, - encrypt : false, - parameters : {} - }, - { - uri : 'urn:ietf:params:rtp-hdrext:ssrc-audio-level', - id : 10, - encrypt : false, - parameters : {} - } - ]); + expect(headerExtensions).toEqual([ + { + uri: 'urn:ietf:params:rtp-hdrext:sdes:mid', + id: 1, + encrypt: false, + parameters: {}, + }, + { + uri: 'http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01', + id: 5, + encrypt: false, + parameters: {}, + }, + { + uri: 'urn:ietf:params:rtp-hdrext:ssrc-audio-level', + id: 10, + encrypt: false, + parameters: {}, + }, + ]); encodings = audioConsumer.rtpParameters.encodings; expect(Array.isArray(encodings)).toBe(true); expect(encodings!.length).toBe(1); expect(typeof encodings?.[0]).toBe('object'); - expect(Object.keys(encodings![0])).toEqual([ 'ssrc', 'dtx' ]); + expect(Object.keys(encodings![0])).toEqual(['ssrc', 'dtx']); expect(typeof encodings![0].ssrc).toBe('number'); rtcp = audioProducer.rtpParameters.rtcp; @@ -699,18 +613,19 @@ test('transport.consume() succeeds', async () => expect(audioConsumer.paused).toBe(false); expect(audioConsumer.appData).toEqual({ bar: 'BAR' }); - videoConsumer = await recvTransport.consume( - { - id : videoConsumerRemoteParameters.id, - producerId : videoConsumerRemoteParameters.producerId, - kind : videoConsumerRemoteParameters.kind, - rtpParameters : videoConsumerRemoteParameters.rtpParameters - }); + videoConsumer = await recvTransport.consume({ + id: videoConsumerRemoteParameters.id, + producerId: videoConsumerRemoteParameters.producerId, + kind: videoConsumerRemoteParameters.kind, + rtpParameters: videoConsumerRemoteParameters.rtpParameters, + }); expect(connectEventNumTimesCalled).toBe(1); expect(typeof videoConsumer).toBe('object'); expect(videoConsumer.id).toBe(videoConsumerRemoteParameters.id); - expect(videoConsumer.producerId).toBe(videoConsumerRemoteParameters.producerId); + expect(videoConsumer.producerId).toBe( + videoConsumerRemoteParameters.producerId, + ); expect(videoConsumer.closed).toBe(false); expect(videoConsumer.kind).toBe('video'); expect(typeof videoConsumer.track).toBe('object'); @@ -720,82 +635,76 @@ test('transport.consume() succeeds', async () => codecs = videoConsumer.rtpParameters.codecs; - expect(codecs[0]).toEqual( - { - mimeType : 'video/VP8', - payloadType : 101, - clockRate : 90000, - rtcpFeedback : - [ - { type: 'nack', parameter: '' }, - { type: 'nack', parameter: 'pli' }, - { type: 'ccm', parameter: 'fir' }, - { type: 'goog-remb', parameter: '' }, - { type: 'transport-cc', parameter: '' } - ], - parameters : - { - 'x-google-start-bitrate' : 1500 - } - }); + expect(codecs[0]).toEqual({ + mimeType: 'video/VP8', + payloadType: 101, + clockRate: 90000, + rtcpFeedback: [ + { type: 'nack', parameter: '' }, + { type: 'nack', parameter: 'pli' }, + { type: 'ccm', parameter: 'fir' }, + { type: 'goog-remb', parameter: '' }, + { type: 'transport-cc', parameter: '' }, + ], + parameters: { + 'x-google-start-bitrate': 1500, + }, + }); - expect(codecs[1]).toEqual( - { - mimeType : 'video/rtx', - payloadType : 102, - clockRate : 90000, - rtcpFeedback : [], - parameters : - { - apt : 101 - } - }); + expect(codecs[1]).toEqual({ + mimeType: 'video/rtx', + payloadType: 102, + clockRate: 90000, + rtcpFeedback: [], + parameters: { + apt: 101, + }, + }); headerExtensions = videoConsumer.rtpParameters.headerExtensions; - expect(headerExtensions).toEqual( - [ - { - uri : 'urn:ietf:params:rtp-hdrext:sdes:mid', - id : 1, - encrypt : false, - parameters : {} - }, - { - uri : 'http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time', - id : 4, - encrypt : false, - parameters : {} - }, - { - uri : 'http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01', - id : 5, - encrypt : false, - parameters : {} - }, - { - uri : 'urn:3gpp:video-orientation', - id : 11, - encrypt : false, - parameters : {} - }, - { - uri : 'urn:ietf:params:rtp-hdrext:toffset', - id : 12, - encrypt : false, - parameters : {} - } - ]); + expect(headerExtensions).toEqual([ + { + uri: 'urn:ietf:params:rtp-hdrext:sdes:mid', + id: 1, + encrypt: false, + parameters: {}, + }, + { + uri: 'http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time', + id: 4, + encrypt: false, + parameters: {}, + }, + { + uri: 'http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01', + id: 5, + encrypt: false, + parameters: {}, + }, + { + uri: 'urn:3gpp:video-orientation', + id: 11, + encrypt: false, + parameters: {}, + }, + { + uri: 'urn:ietf:params:rtp-hdrext:toffset', + id: 12, + encrypt: false, + parameters: {}, + }, + ]); encodings = videoConsumer.rtpParameters.encodings; expect(Array.isArray(encodings)).toBe(true); expect(encodings!.length).toBe(1); expect(typeof encodings?.[0]).toBe('object'); - expect(Object.keys(encodings![0])).toEqual([ 'ssrc', 'rtx', 'dtx' ]); + expect(Object.keys(encodings![0])).toEqual(['ssrc', 'rtx', 'dtx']); expect(typeof encodings?.[0].ssrc).toBe('number'); expect(typeof encodings?.[0].rtx).toBe('object'); - expect(Object.keys(encodings![0].rtx!)).toEqual([ 'ssrc' ]); + expect(Object.keys(encodings![0].rtx!)).toEqual(['ssrc']); expect(typeof encodings?.[0].rtx?.ssrc).toBe('number'); rtcp = videoConsumer.rtpParameters.rtcp; @@ -809,50 +718,49 @@ test('transport.consume() succeeds', async () => recvTransport.removeAllListeners('connect'); }, 500); -test('transport.consume() batches consumers created in same macrotask into the same task', async () => -{ +test('transport.consume() batches consumers created in same macrotask into the same task', async () => { const videoConsumerRemoteParameters1 = - fakeParameters.generateConsumerRemoteParameters({ codecMimeType: 'video/VP8' }); + fakeParameters.generateConsumerRemoteParameters({ + codecMimeType: 'video/VP8', + }); const videoConsumerRemoteParameters2 = - fakeParameters.generateConsumerRemoteParameters({ codecMimeType: 'video/VP8' }); + fakeParameters.generateConsumerRemoteParameters({ + codecMimeType: 'video/VP8', + }); - const pushSpy = jest.spyOn((recvTransport as unknown as { _awaitQueue: AwaitQueue })._awaitQueue, 'push'); + const pushSpy = jest.spyOn( + (recvTransport as unknown as { _awaitQueue: AwaitQueue })._awaitQueue, + 'push', + ); - const waitForConsumer = (id: string | undefined): Promise => - { - return new Promise((resolve) => - { - recvTransport.observer.on('newconsumer', (consumer) => - { - if (consumer.id === id) - { + const waitForConsumer = (id: string | undefined): Promise => { + return new Promise(resolve => { + recvTransport.observer.on('newconsumer', consumer => { + if (consumer.id === id) { resolve(); } }); }); }; - const allConsumersCreated = Promise.all( - [ - waitForConsumer(videoConsumerRemoteParameters1.id), - waitForConsumer(videoConsumerRemoteParameters2.id) - ]); + const allConsumersCreated = Promise.all([ + waitForConsumer(videoConsumerRemoteParameters1.id), + waitForConsumer(videoConsumerRemoteParameters2.id), + ]); await Promise.all([ - recvTransport.consume( - { - id : videoConsumerRemoteParameters1.id, - producerId : videoConsumerRemoteParameters1.producerId, - kind : videoConsumerRemoteParameters1.kind, - rtpParameters : videoConsumerRemoteParameters1.rtpParameters - }), - recvTransport.consume( - { - id : videoConsumerRemoteParameters2.id, - producerId : videoConsumerRemoteParameters2.producerId, - kind : videoConsumerRemoteParameters2.kind, - rtpParameters : videoConsumerRemoteParameters2.rtpParameters - }) + recvTransport.consume({ + id: videoConsumerRemoteParameters1.id, + producerId: videoConsumerRemoteParameters1.producerId, + kind: videoConsumerRemoteParameters1.kind, + rtpParameters: videoConsumerRemoteParameters1.rtpParameters, + }), + recvTransport.consume({ + id: videoConsumerRemoteParameters2.id, + producerId: videoConsumerRemoteParameters2.producerId, + kind: videoConsumerRemoteParameters2.kind, + rtpParameters: videoConsumerRemoteParameters2.rtpParameters, + }), ]); await allConsumersCreated; @@ -860,131 +768,125 @@ test('transport.consume() batches consumers created in same macrotask into the s expect(pushSpy).toBeCalledTimes(1); }, 500); -test('transport.consume() without remote Consumer parameters rejects with TypeError', async () => -{ +test('transport.consume() without remote Consumer parameters rejects with TypeError', async () => { // @ts-ignore - await expect(recvTransport.consume({})) - .rejects - .toThrow(TypeError); + await expect(recvTransport.consume({})).rejects.toThrow(TypeError); }, 500); -test('transport.consume() with missing remote Consumer parameters rejects with TypeError', async () => -{ - // @ts-ignore - await expect(recvTransport.consume({ id: '1234' })) - .rejects - .toThrow(TypeError); - +test('transport.consume() with missing remote Consumer parameters rejects with TypeError', async () => { // @ts-ignore - await expect(recvTransport.consume({ id: '1234', producerId: '4444' })) - .rejects - .toThrow(TypeError); + await expect(recvTransport.consume({ id: '1234' })).rejects.toThrow( + TypeError, + ); - await expect(recvTransport.consume( + await expect( // @ts-ignore - { - id : '1234', - producerId : '4444', - kind : 'audio' - })) - .rejects - .toThrow(TypeError); - - await expect(recvTransport.consume( - // @ts-ignore - { - id : '1234', - producerId : '4444', - kind : 'audio' - })) - .rejects - .toThrow(TypeError); + recvTransport.consume({ id: '1234', producerId: '4444' }), + ).rejects.toThrow(TypeError); + + await expect( + recvTransport.consume( + // @ts-ignore + { + id: '1234', + producerId: '4444', + kind: 'audio', + }, + ), + ).rejects.toThrow(TypeError); + + await expect( + recvTransport.consume( + // @ts-ignore + { + id: '1234', + producerId: '4444', + kind: 'audio', + }, + ), + ).rejects.toThrow(TypeError); }, 500); -test('transport.consume() in a sending Transport rejects with UnsupportedError', async () => -{ - const { - id, - producerId, - kind, - rtpParameters - } = fakeParameters.generateConsumerRemoteParameters({ codecMimeType: 'audio/opus' }); +test('transport.consume() in a sending Transport rejects with UnsupportedError', async () => { + const { id, producerId, kind, rtpParameters } = + fakeParameters.generateConsumerRemoteParameters({ + codecMimeType: 'audio/opus', + }); - await expect(sendTransport.consume( - { + await expect( + sendTransport.consume({ id, producerId, kind, - rtpParameters - })) - .rejects - .toThrow(UnsupportedError); + rtpParameters, + }), + ).rejects.toThrow(UnsupportedError); }, 500); -test('transport.consume() with unsupported rtpParameters rejects with UnsupportedError', async () => -{ - const { - id, - producerId, - kind, - rtpParameters - } = fakeParameters.generateConsumerRemoteParameters({ codecMimeType: 'audio/ISAC' }); +test('transport.consume() with unsupported rtpParameters rejects with UnsupportedError', async () => { + const { id, producerId, kind, rtpParameters } = + fakeParameters.generateConsumerRemoteParameters({ + codecMimeType: 'audio/ISAC', + }); - await expect(sendTransport.consume( - { + await expect( + sendTransport.consume({ id, producerId, kind, - rtpParameters - })) - .rejects - .toThrow(UnsupportedError); + rtpParameters, + }), + ).rejects.toThrow(UnsupportedError); }, 500); -test('transport.consume() with a non object appData rejects with TypeError', async () => -{ +test('transport.consume() with a non object appData rejects with TypeError', async () => { const consumerRemoteParameters = - fakeParameters.generateConsumerRemoteParameters({ codecMimeType: 'audio/opus' }); + fakeParameters.generateConsumerRemoteParameters({ + codecMimeType: 'audio/opus', + }); - // @ts-ignore - await expect(recvTransport.consume({ consumerRemoteParameters, appData: true })) - .rejects - .toThrow(TypeError); + await expect( + // @ts-ignore + recvTransport.consume({ consumerRemoteParameters, appData: true }), + ).rejects.toThrow(TypeError); }, 500); -test('transport.produceData() succeeds', async () => -{ +test('transport.produceData() succeeds', async () => { let dataProducerId; let produceDataEventNumTimesCalled = 0; // eslint-disable-next-line no-unused-vars - sendTransport.on('producedata', ({ sctpStreamParameters, label, protocol, appData }, callback /* errback */) => - { - produceDataEventNumTimesCalled++; - - expect(typeof sctpStreamParameters).toBe('object'); - expect(label).toBe('FOO'); - expect(protocol).toBe('BAR'); - expect(appData).toEqual({ foo: 'FOO' }); - - const id = fakeParameters.generateDataProducerRemoteParameters().id; - - dataProducerId = id; - - // Emulate communication with the server and success response with Producer - // remote parameters. - setTimeout(() => callback({ id })); + sendTransport.on( + 'producedata', + ( + { sctpStreamParameters, label, protocol, appData }, + callback /* errback */, + ) => { + produceDataEventNumTimesCalled++; + + expect(typeof sctpStreamParameters).toBe('object'); + expect(label).toBe('FOO'); + expect(protocol).toBe('BAR'); + expect(appData).toEqual({ foo: 'FOO' }); + + const id = fakeParameters.generateDataProducerRemoteParameters().id; + + dataProducerId = id; + + // Emulate communication with the server and success response with Producer + // remote parameters. + setTimeout(() => callback({ id })); + }, + ); + + dataProducer = await sendTransport.produceData<{ foo: string }>({ + ordered: false, + maxPacketLifeTime: 5555, + label: 'FOO', + protocol: 'BAR', + appData: { foo: 'FOO' }, }); - dataProducer = await sendTransport.produceData<{ foo: string }>( - { - ordered : false, - maxPacketLifeTime : 5555, - label : 'FOO', - protocol : 'BAR', - appData : { foo: 'FOO' } - }); - expect(produceDataEventNumTimesCalled).toBe(1); expect(typeof dataProducer).toBe('object'); expect(dataProducer.id).toBe(dataProducerId); @@ -1000,39 +902,35 @@ test('transport.produceData() succeeds', async () => sendTransport.removeAllListeners('producedata'); }, 500); -test('transport.produceData() in a receiving Transport rejects with UnsupportedError', async () => -{ - await expect(recvTransport.produceData({})) - .rejects - .toThrow(UnsupportedError); +test('transport.produceData() in a receiving Transport rejects with UnsupportedError', async () => { + await expect(recvTransport.produceData({})).rejects.toThrow(UnsupportedError); }, 500); -test('transport.produceData() with a non object appData rejects with TypeError', async () => -{ +test('transport.produceData() with a non object appData rejects with TypeError', async () => { // @ts-ignore - await expect(sendTransport.produceData({ appData: true })) - .rejects - .toThrow(TypeError); + await expect(sendTransport.produceData({ appData: true })).rejects.toThrow( + TypeError, + ); }, 500); -test('transport.consumeData() succeeds', async () => -{ +test('transport.consumeData() succeeds', async () => { const dataConsumerRemoteParameters = fakeParameters.generateDataConsumerRemoteParameters(); - dataConsumer = await recvTransport.consumeData<{ bar: string }>( - { - id : dataConsumerRemoteParameters.id, - dataProducerId : dataConsumerRemoteParameters.dataProducerId, - sctpStreamParameters : dataConsumerRemoteParameters.sctpStreamParameters, - label : 'FOO', - protocol : 'BAR', - appData : { bar: 'BAR' } - }); + dataConsumer = await recvTransport.consumeData<{ bar: string }>({ + id: dataConsumerRemoteParameters.id, + dataProducerId: dataConsumerRemoteParameters.dataProducerId, + sctpStreamParameters: dataConsumerRemoteParameters.sctpStreamParameters, + label: 'FOO', + protocol: 'BAR', + appData: { bar: 'BAR' }, + }); expect(typeof dataConsumer).toBe('object'); expect(dataConsumer.id).toBe(dataConsumerRemoteParameters.id); - expect(dataConsumer.dataProducerId).toBe(dataConsumerRemoteParameters.dataProducerId); + expect(dataConsumer.dataProducerId).toBe( + dataConsumerRemoteParameters.dataProducerId, + ); expect(dataConsumer.closed).toBe(false); expect(typeof dataConsumer.sctpStreamParameters).toBe('object'); expect(typeof dataConsumer.sctpStreamParameters.streamId).toBe('number'); @@ -1040,101 +938,79 @@ test('transport.consumeData() succeeds', async () => expect(dataConsumer.protocol).toBe('BAR'); }, 500); -test('transport.consumeData() without remote DataConsumer parameters rejects with TypeError', async () => -{ +test('transport.consumeData() without remote DataConsumer parameters rejects with TypeError', async () => { // @ts-ignore - await expect(recvTransport.consumeData({})) - .rejects - .toThrow(TypeError); + await expect(recvTransport.consumeData({})).rejects.toThrow(TypeError); }, 500); -test('transport.consumeData() with missing remote DataConsumer parameters rejects with TypeError', async () => -{ +test('transport.consumeData() with missing remote DataConsumer parameters rejects with TypeError', async () => { // @ts-ignore - await expect(recvTransport.consumeData({ id: '1234' })) - .rejects - .toThrow(TypeError); + await expect(recvTransport.consumeData({ id: '1234' })).rejects.toThrow( + TypeError, + ); - // @ts-ignore - await expect(recvTransport.consumeData({ id: '1234', dataProducerId: '4444' })) - .rejects - .toThrow(TypeError); + await expect( + // @ts-ignore + recvTransport.consumeData({ id: '1234', dataProducerId: '4444' }), + ).rejects.toThrow(TypeError); }, 500); -test('transport.consumeData() in a sending Transport rejects with UnsupportedError', async () => -{ - const { - id, - dataProducerId, - sctpStreamParameters - } = fakeParameters.generateDataConsumerRemoteParameters(); +test('transport.consumeData() in a sending Transport rejects with UnsupportedError', async () => { + const { id, dataProducerId, sctpStreamParameters } = + fakeParameters.generateDataConsumerRemoteParameters(); - await expect(sendTransport.consumeData( - { + await expect( + sendTransport.consumeData({ id, dataProducerId, - sctpStreamParameters - })) - .rejects - .toThrow(UnsupportedError); + sctpStreamParameters, + }), + ).rejects.toThrow(UnsupportedError); }, 500); -test('transport.consumeData() with a non object appData rejects with TypeError', async () => -{ +test('transport.consumeData() with a non object appData rejects with TypeError', async () => { const dataConsumerRemoteParameters = fakeParameters.generateDataConsumerRemoteParameters(); - // @ts-ignore - await expect(recvTransport.consumeData({ dataConsumerRemoteParameters, appData: true })) - .rejects - .toThrow(TypeError); + await expect( + // @ts-ignore + recvTransport.consumeData({ dataConsumerRemoteParameters, appData: true }), + ).rejects.toThrow(TypeError); }, 500); -test('transport.getStats() succeeds', async () => -{ +test('transport.getStats() succeeds', async () => { const stats = await sendTransport.getStats(); expect(typeof stats).toBe('object'); }, 500); -test('transport.restartIce() succeeds', async () => -{ - await expect(sendTransport.restartIce( - { - iceParameters : - { - usernameFragment : 'foo', - password : 'xxx' - } - })) - .resolves - .toBe(undefined); +test('transport.restartIce() succeeds', async () => { + await expect( + sendTransport.restartIce({ + iceParameters: { + usernameFragment: 'foo', + password: 'xxx', + }, + }), + ).resolves.toBe(undefined); }, 500); -test('transport.restartIce() without remote iceParameters rejects with TypeError', async () => -{ +test('transport.restartIce() without remote iceParameters rejects with TypeError', async () => { // @ts-ignore - await expect(sendTransport.restartIce({})) - .rejects - .toThrow(TypeError); + await expect(sendTransport.restartIce({})).rejects.toThrow(TypeError); }, 500); -test('transport.updateIceServers() succeeds', async () => -{ - await expect(sendTransport.updateIceServers({ iceServers: [] })) - .resolves - .toBe(undefined); +test('transport.updateIceServers() succeeds', async () => { + await expect( + sendTransport.updateIceServers({ iceServers: [] }), + ).resolves.toBe(undefined); }, 500); -test('transport.updateIceServers() without iceServers rejects with TypeError', async () => -{ - await expect(sendTransport.updateIceServers({})) - .rejects - .toThrow(TypeError); +test('transport.updateIceServers() without iceServers rejects with TypeError', async () => { + await expect(sendTransport.updateIceServers({})).rejects.toThrow(TypeError); }, 500); -test('ICE gathering state change fires "icegatheringstatechange" in live Transport', () => -{ +test('ICE gathering state change fires "icegatheringstatechange" in live Transport', () => { // NOTE: These tests are a bit flaky and we should isolate them. FakeHandler // emits '@connectionstatechange' with value 'connecting' as soon as its // private setupTransport() method is called (which has happens many times in @@ -1148,8 +1024,7 @@ test('ICE gathering state change fires "icegatheringstatechange" in live Transpo let iceGatheringStateChangeEventNumTimesCalled = 0; let connectionStateChangeEventNumTimesCalled = 0; - sendTransport.on('icegatheringstatechange', (iceGatheringState) => - { + sendTransport.on('icegatheringstatechange', iceGatheringState => { iceGatheringStateChangeEventNumTimesCalled++; expect(iceGatheringState).toBe('complete'); @@ -1158,8 +1033,7 @@ test('ICE gathering state change fires "icegatheringstatechange" in live Transpo }); // eslint-disable-next-line @typescript-eslint/no-unused-vars - sendTransport.on('connectionstatechange', (connectionState) => - { + sendTransport.on('connectionstatechange', connectionState => { connectionStateChangeEventNumTimesCalled++; }); @@ -1175,12 +1049,10 @@ test('ICE gathering state change fires "icegatheringstatechange" in live Transpo sendTransport.removeAllListeners('connectionstatechange'); }); -test('connection state change fires "connectionstatechange" in live Transport', () => -{ +test('connection state change fires "connectionstatechange" in live Transport', () => { let connectionStateChangeEventNumTimesCalled = 0; - sendTransport.on('connectionstatechange', (connectionState) => - { + sendTransport.on('connectionstatechange', connectionState => { connectionStateChangeEventNumTimesCalled++; expect(connectionState).toBe('completed'); @@ -1195,8 +1067,7 @@ test('connection state change fires "connectionstatechange" in live Transport', sendTransport.removeAllListeners('connectionstatechange'); }); -test('producer.pause() succeeds', () => -{ +test('producer.pause() succeeds', () => { videoProducer.pause(); expect(videoProducer.paused).toBe(true); @@ -1204,25 +1075,23 @@ test('producer.pause() succeeds', () => expect(videoProducer.track?.enabled).toBe(true); }); -test('producer.resume() succeeds', () => -{ +test('producer.resume() succeeds', () => { videoProducer.resume(); expect(videoProducer.paused).toBe(false); expect(videoProducer.track?.enabled).toBe(true); }); -test('producer.replaceTrack() with a new track succeeds', async () => -{ +test('producer.replaceTrack() with a new track succeeds', async () => { // Have the audio Producer paused. audioProducer.pause(); const audioProducerPreviousTrack = audioProducer.track; const newAudioTrack = new FakeMediaStreamTrack({ kind: 'audio' }); - await expect(audioProducer.replaceTrack({ track: newAudioTrack })) - .resolves - .toBe(undefined); + await expect( + audioProducer.replaceTrack({ track: newAudioTrack }), + ).resolves.toBe(undefined); // Previous track must be 'live' due to stopTracks: false. expect(audioProducerPreviousTrack?.readyState).toBe('live'); @@ -1238,9 +1107,9 @@ test('producer.replaceTrack() with a new track succeeds', async () => const videoProducerPreviousTrack = videoProducer.track; const newVideoTrack = new FakeMediaStreamTrack({ kind: 'video' }); - await expect(videoProducer.replaceTrack({ track: newVideoTrack })) - .resolves - .toBe(undefined); + await expect( + videoProducer.replaceTrack({ track: newVideoTrack }), + ).resolves.toBe(undefined); // Previous track must be 'ended' due to stopTracks: true. expect(videoProducerPreviousTrack?.readyState).toBe('ended'); @@ -1249,16 +1118,15 @@ test('producer.replaceTrack() with a new track succeeds', async () => expect(videoProducer.paused).toBe(false); }, 500); -test('producer.replaceTrack() with null succeeds', async () => -{ +test('producer.replaceTrack() with null succeeds', async () => { // Have the audio Producer paused. audioProducer.pause(); const audioProducerPreviousTrack = audioProducer.track; - await expect(audioProducer.replaceTrack({ track: null })) - .resolves - .toBe(undefined); + await expect(audioProducer.replaceTrack({ track: null })).resolves.toBe( + undefined, + ); // Previous track must be 'live' due to stopTracks: false. expect(audioProducerPreviousTrack?.readyState).toBe('live'); @@ -1275,9 +1143,9 @@ test('producer.replaceTrack() with null succeeds', async () => audioProducerPreviousTrack!.enabled = false; // Set the original audio track back. - await expect(audioProducer.replaceTrack({ track: audioProducerPreviousTrack })) - .resolves - .toBe(undefined); + await expect( + audioProducer.replaceTrack({ track: audioProducerPreviousTrack }), + ).resolves.toBe(undefined); // The given audio track was muted but the Producer was not, so the track // must not be muted now. @@ -1288,102 +1156,86 @@ test('producer.replaceTrack() with null succeeds', async () => audioProducer.resume(); }, 500); -test('producer.replaceTrack() with an ended track rejects with InvalidStateError', async () => -{ +test('producer.replaceTrack() with an ended track rejects with InvalidStateError', async () => { const track = new FakeMediaStreamTrack({ kind: 'audio' }); track.stop(); - await expect(videoProducer.replaceTrack({ track })) - .rejects - .toThrow(InvalidStateError); + await expect(videoProducer.replaceTrack({ track })).rejects.toThrow( + InvalidStateError, + ); expect(track.readyState).toBe('ended'); expect(videoProducer.track?.readyState).toBe('live'); }, 500); -test('producer.replaceTrack() with the same track succeeds', async () => -{ - await expect(audioProducer.replaceTrack({ track: audioProducer.track })) - .resolves - .toBe(undefined); +test('producer.replaceTrack() with the same track succeeds', async () => { + await expect( + audioProducer.replaceTrack({ track: audioProducer.track }), + ).resolves.toBe(undefined); expect(audioProducer.track?.readyState).toBe('live'); }, 500); -test('producer.setMaxSpatialLayer() succeeds', async () => -{ - await expect(videoProducer.setMaxSpatialLayer(0)) - .resolves - .toBe(undefined); +test('producer.setMaxSpatialLayer() succeeds', async () => { + await expect(videoProducer.setMaxSpatialLayer(0)).resolves.toBe(undefined); expect(videoProducer.maxSpatialLayer).toBe(0); }, 500); -test('producer.setMaxSpatialLayer() in an audio Producer rejects with UnsupportedError', async () => -{ - await expect(audioProducer.setMaxSpatialLayer(1)) - .rejects - .toThrow(UnsupportedError); +test('producer.setMaxSpatialLayer() in an audio Producer rejects with UnsupportedError', async () => { + await expect(audioProducer.setMaxSpatialLayer(1)).rejects.toThrow( + UnsupportedError, + ); expect(audioProducer.maxSpatialLayer).toBe(undefined); }, 500); -test('producer.setMaxSpatialLayer() with invalid spatialLayer rejects with TypeError', async () => -{ +test('producer.setMaxSpatialLayer() with invalid spatialLayer rejects with TypeError', async () => { // @ts-ignore - await expect(videoProducer.setMaxSpatialLayer('chicken')) - .rejects - .toThrow(TypeError); + await expect(videoProducer.setMaxSpatialLayer('chicken')).rejects.toThrow( + TypeError, + ); }, 500); -test('producer.setMaxSpatialLayer() without spatialLayer rejects with TypeError', async () => -{ +test('producer.setMaxSpatialLayer() without spatialLayer rejects with TypeError', async () => { // @ts-ignore - await expect(videoProducer.setMaxSpatialLayer()) - .rejects - .toThrow(TypeError); + await expect(videoProducer.setMaxSpatialLayer()).rejects.toThrow(TypeError); }, 500); -test('producer.setRtpEncodingParameters() succeeds', async () => -{ - await expect(videoProducer.setRtpEncodingParameters({ scaleResolutionDownBy: 2 })) - .resolves - .toBe(undefined); +test('producer.setRtpEncodingParameters() succeeds', async () => { + await expect( + videoProducer.setRtpEncodingParameters({ scaleResolutionDownBy: 2 }), + ).resolves.toBe(undefined); expect(videoProducer.maxSpatialLayer).toBe(0); }, 500); -test('producer.getStats() succeeds', async () => -{ +test('producer.getStats() succeeds', async () => { const stats = await videoProducer.getStats(); expect(typeof stats).toBe('object'); }, 500); -test('consumer.resume() succeeds', () => -{ +test('consumer.resume() succeeds', () => { videoConsumer.resume(); expect(videoConsumer.paused).toBe(false); }); -test('consumer.pause() succeeds', () => -{ +test('consumer.pause() succeeds', () => { videoConsumer.pause(); expect(videoConsumer.paused).toBe(true); }); -test('consumer.getStats() succeeds', async () => -{ +test('consumer.getStats() succeeds', async () => { const stats = await videoConsumer.getStats(); expect(typeof stats).toBe('object'); }, 500); -test('producer.close() succeed', () => -{ +test('producer.close() succeed', () => { audioProducer.close(); expect(audioProducer.closed).toBe(true); @@ -1391,77 +1243,62 @@ test('producer.close() succeed', () => expect(audioProducer.track?.readyState).toBe('live'); }); -test('producer.replaceTrack() rejects with InvalidStateError if closed', async () => -{ +test('producer.replaceTrack() rejects with InvalidStateError if closed', async () => { const audioTrack = new FakeMediaStreamTrack({ kind: 'audio' }); - await expect(audioProducer.replaceTrack({ track: audioTrack })) - .rejects - .toThrow(InvalidStateError); + await expect( + audioProducer.replaceTrack({ track: audioTrack }), + ).rejects.toThrow(InvalidStateError); expect(audioTrack.readyState).toBe('live'); }, 500); -test('producer.getStats() rejects with InvalidStateError if closed', async () => -{ - await expect(audioProducer.getStats()) - .rejects - .toThrow(InvalidStateError); +test('producer.getStats() rejects with InvalidStateError if closed', async () => { + await expect(audioProducer.getStats()).rejects.toThrow(InvalidStateError); }, 500); -test('consumer.close() succeed', () => -{ +test('consumer.close() succeed', () => { audioConsumer.close(); expect(audioConsumer.closed).toBe(true); expect(audioConsumer.track.readyState).toBe('ended'); }); -test('consumer.getStats() rejects with InvalidStateError if closed', async () => -{ - await expect(audioConsumer.getStats()) - .rejects - .toThrow(InvalidStateError); +test('consumer.getStats() rejects with InvalidStateError if closed', async () => { + await expect(audioConsumer.getStats()).rejects.toThrow(InvalidStateError); }, 500); -test('dataProducer.close() succeed', () => -{ +test('dataProducer.close() succeed', () => { dataProducer.close(); expect(dataProducer.closed).toBe(true); }); -test('dataConsumer.close() succeed', () => -{ +test('dataConsumer.close() succeed', () => { dataConsumer.close(); expect(dataConsumer.closed).toBe(true); }); -test('remotetely stopped track fires "trackended" in live Producers/Consumers', () => -{ +test('remotetely stopped track fires "trackended" in live Producers/Consumers', () => { let audioProducerTrackendedEventCalled = false; let videoProducerTrackendedEventCalled = false; let audiosConsumerTrackendedEventCalled = false; let videoConsumerTrackendedEventCalled = false; - audioProducer.on('trackended', () => - { + audioProducer.on('trackended', () => { audioProducerTrackendedEventCalled = true; }); - videoProducer.on('trackended', () => - { + videoProducer.on('trackended', () => { videoProducerTrackendedEventCalled = true; }); - audioConsumer.on('trackended', () => - { + audioConsumer.on('trackended', () => { audiosConsumerTrackendedEventCalled = true; }); - videoConsumer.on('trackended', () => - { + videoConsumer.on('trackended', () => { videoConsumerTrackendedEventCalled = true; }); @@ -1493,30 +1330,25 @@ test('remotetely stopped track fires "trackended" in live Producers/Consumers', videoConsumer.removeAllListeners(); }); -test('transport.close() fires "transportclose" in live Producers/Consumers', () => -{ +test('transport.close() fires "transportclose" in live Producers/Consumers', () => { let audioProducerTransportcloseEventCalled = false; let videoProducerTransportcloseEventCalled = false; let audioConsumerTransportcloseEventCalled = false; let videoConsumerTransportcloseEventCalled = false; - audioProducer.on('transportclose', () => - { + audioProducer.on('transportclose', () => { audioProducerTransportcloseEventCalled = true; }); - videoProducer.on('transportclose', () => - { + videoProducer.on('transportclose', () => { videoProducerTransportcloseEventCalled = true; }); - audioConsumer.on('transportclose', () => - { + audioConsumer.on('transportclose', () => { audioConsumerTransportcloseEventCalled = true; }); - videoConsumer.on('transportclose', () => - { + videoConsumer.on('transportclose', () => { videoConsumerTransportcloseEventCalled = true; }); @@ -1550,16 +1382,15 @@ test('transport.close() fires "transportclose" in live Producers/Consumers', () videoConsumer.removeAllListeners(); }); -test('transport.produce() rejects with InvalidStateError if closed', async () => -{ +test('transport.produce() rejects with InvalidStateError if closed', async () => { const track = new FakeMediaStreamTrack({ kind: 'audio' }); // Add noop listener to avoid the method fail. sendTransport.on('produce', () => {}); - await expect(sendTransport.produce({ track, stopTracks: false })) - .rejects - .toThrow(InvalidStateError); + await expect( + sendTransport.produce({ track, stopTracks: false }), + ).rejects.toThrow(InvalidStateError); // The track must be 'live' due to stopTracks: false. expect(track.readyState).toBe('live'); @@ -1567,65 +1398,53 @@ test('transport.produce() rejects with InvalidStateError if closed', async () => sendTransport.removeAllListeners('produce'); }, 500); -test('transport.consume() rejects with InvalidStateError if closed', async () => -{ +test('transport.consume() rejects with InvalidStateError if closed', async () => { // @ts-ignore - await expect(recvTransport.consume({})) - .rejects - .toThrow(InvalidStateError); + await expect(recvTransport.consume({})).rejects.toThrow(InvalidStateError); recvTransport.removeAllListeners(); }, 500); -test('transport.produceData() rejects with InvalidStateError if closed', async () => -{ +test('transport.produceData() rejects with InvalidStateError if closed', async () => { // Add noop listener to avoid the method fail. sendTransport.on('producedata', () => {}); - await expect(sendTransport.produceData({})) - .rejects - .toThrow(InvalidStateError); + await expect(sendTransport.produceData({})).rejects.toThrow( + InvalidStateError, + ); sendTransport.removeAllListeners('producedata'); }, 500); -test('transport.consumeData() rejects with InvalidStateError if closed', async () => -{ +test('transport.consumeData() rejects with InvalidStateError if closed', async () => { // @ts-ignore - await expect(recvTransport.consumeData({})) - .rejects - .toThrow(InvalidStateError); + await expect(recvTransport.consumeData({})).rejects.toThrow( + InvalidStateError, + ); }, 500); -test('transport.getStats() rejects with InvalidStateError if closed', async () => -{ - await expect(sendTransport.getStats()) - .rejects - .toThrow(InvalidStateError); +test('transport.getStats() rejects with InvalidStateError if closed', async () => { + await expect(sendTransport.getStats()).rejects.toThrow(InvalidStateError); }, 500); -test('transport.restartIce() rejects with InvalidStateError if closed', async () => -{ +test('transport.restartIce() rejects with InvalidStateError if closed', async () => { // @ts-ignore - await expect(sendTransport.restartIce({ ieParameters: {} })) - .rejects - .toThrow(InvalidStateError); + await expect(sendTransport.restartIce({ ieParameters: {} })).rejects.toThrow( + InvalidStateError, + ); }, 500); -test('transport.updateIceServers() rejects with InvalidStateError if closed', async () => -{ - await expect(sendTransport.updateIceServers({ iceServers: [] })) - .rejects - .toThrow(InvalidStateError); +test('transport.updateIceServers() rejects with InvalidStateError if closed', async () => { + await expect( + sendTransport.updateIceServers({ iceServers: [] }), + ).rejects.toThrow(InvalidStateError); }, 500); -test('connection state change does not fire "connectionstatechange" in closed Transport', () => -{ +test('connection state change does not fire "connectionstatechange" in closed Transport', () => { let connectionStateChangeEventNumTimesCalled = 0; // eslint-disable-next-line no-unused-vars - sendTransport.on('connectionstatechange', (/* connectionState */) => - { + sendTransport.on('connectionstatechange', (/* connectionState */) => { connectionStateChangeEventNumTimesCalled++; }); @@ -1638,21 +1457,21 @@ test('connection state change does not fire "connectionstatechange" in closed Tr sendTransport.removeAllListeners('connectionstatechange'); }); -test('RemoteSdp properly handles multiple streams of the same type in planB', async () => -{ +test('RemoteSdp properly handles multiple streams of the same type in planB', async () => { let sdp = undefined; let sdpObject = undefined; const remoteSdp = new RemoteSdp({ planB: true }); - await remoteSdp.receive( - { - mid : 'video', - kind : 'video', - offerRtpParameters : fakeParameters.generateConsumerRemoteParameters({ codecMimeType: 'video/VP8' }).rtpParameters, - streamId : 'streamId-1', - trackId : 'trackId-1' - }); + await remoteSdp.receive({ + mid: 'video', + kind: 'video', + offerRtpParameters: fakeParameters.generateConsumerRemoteParameters({ + codecMimeType: 'video/VP8', + }).rtpParameters, + streamId: 'streamId-1', + trackId: 'trackId-1', + }); sdp = remoteSdp.getSdp(); sdpObject = sdpTransform.parse(sdp); @@ -1666,14 +1485,15 @@ test('RemoteSdp properly handles multiple streams of the same type in planB', as expect(sdpObject.media[0].rtp[1].codec).toBe('rtx'); expect(sdpObject.media[0].ssrcs?.length).toBe(4); - await remoteSdp.receive( - { - mid : 'video', - kind : 'video', - offerRtpParameters : fakeParameters.generateConsumerRemoteParameters({ codecMimeType: 'video/H264' }).rtpParameters, - streamId : 'streamId-2', - trackId : 'trackId-2' - }); + await remoteSdp.receive({ + mid: 'video', + kind: 'video', + offerRtpParameters: fakeParameters.generateConsumerRemoteParameters({ + codecMimeType: 'video/H264', + }).rtpParameters, + streamId: 'streamId-2', + trackId: 'trackId-2', + }); sdp = remoteSdp.getSdp(); sdpObject = sdpTransform.parse(sdp); @@ -1691,11 +1511,12 @@ test('RemoteSdp properly handles multiple streams of the same type in planB', as expect(sdpObject.media[0].rtp[3].codec).toBe('rtx'); expect(sdpObject.media[0].ssrcs?.length).toBe(8); - await remoteSdp.planBStopReceiving( - { - mid : 'video', - offerRtpParameters : fakeParameters.generateConsumerRemoteParameters({ codecMimeType: 'video/H264' }).rtpParameters - }); + await remoteSdp.planBStopReceiving({ + mid: 'video', + offerRtpParameters: fakeParameters.generateConsumerRemoteParameters({ + codecMimeType: 'video/H264', + }).rtpParameters, + }); sdp = remoteSdp.getSdp(); sdpObject = sdpTransform.parse(sdp); @@ -1714,21 +1535,21 @@ test('RemoteSdp properly handles multiple streams of the same type in planB', as expect(sdpObject.media[0].ssrcs?.length).toBe(4); }, 500); -test('RemoteSdp does not duplicate codec descriptions', async () => -{ +test('RemoteSdp does not duplicate codec descriptions', async () => { let sdp = undefined; let sdpObject = undefined; const remoteSdp = new RemoteSdp({ planB: true }); - await remoteSdp.receive( - { - mid : 'video', - kind : 'video', - offerRtpParameters : fakeParameters.generateConsumerRemoteParameters({ codecMimeType: 'video/VP8' }).rtpParameters, - streamId : 'streamId-1', - trackId : 'trackId-1' - }); + await remoteSdp.receive({ + mid: 'video', + kind: 'video', + offerRtpParameters: fakeParameters.generateConsumerRemoteParameters({ + codecMimeType: 'video/VP8', + }).rtpParameters, + streamId: 'streamId-1', + trackId: 'trackId-1', + }); sdp = remoteSdp.getSdp(); sdpObject = sdpTransform.parse(sdp); @@ -1742,14 +1563,15 @@ test('RemoteSdp does not duplicate codec descriptions', async () => expect(sdpObject.media[0].rtp[1].codec).toBe('rtx'); expect(sdpObject.media[0].ssrcs?.length).toBe(4); - await remoteSdp.receive( - { - mid : 'video', - kind : 'video', - offerRtpParameters : fakeParameters.generateConsumerRemoteParameters({ codecMimeType: 'video/VP8' }).rtpParameters, - streamId : 'streamId-1', - trackId : 'trackId-1' - }); + await remoteSdp.receive({ + mid: 'video', + kind: 'video', + offerRtpParameters: fakeParameters.generateConsumerRemoteParameters({ + codecMimeType: 'video/VP8', + }).rtpParameters, + streamId: 'streamId-1', + trackId: 'trackId-1', + }); sdp = remoteSdp.getSdp(); sdpObject = sdpTransform.parse(sdp); @@ -1764,49 +1586,72 @@ test('RemoteSdp does not duplicate codec descriptions', async () => expect(sdpObject.media[0].ssrcs?.length).toBe(8); }, 500); -test('parseScalabilityMode() works', () => -{ - expect(parseScalabilityMode('L1T3')).toEqual({ spatialLayers: 1, temporalLayers: 3 }); - expect(parseScalabilityMode('L3T2_KEY')).toEqual({ spatialLayers: 3, temporalLayers: 2 }); - expect(parseScalabilityMode('S2T3')).toEqual({ spatialLayers: 2, temporalLayers: 3 }); - expect(parseScalabilityMode('foo')).toEqual({ spatialLayers: 1, temporalLayers: 1 }); - expect(parseScalabilityMode()).toEqual({ spatialLayers: 1, temporalLayers: 1 }); - expect(parseScalabilityMode('S0T3')).toEqual({ spatialLayers: 1, temporalLayers: 1 }); - expect(parseScalabilityMode('S1T0')).toEqual({ spatialLayers: 1, temporalLayers: 1 }); - expect(parseScalabilityMode('L20T3')).toEqual({ spatialLayers: 20, temporalLayers: 3 }); - expect(parseScalabilityMode('S200T3')).toEqual({ spatialLayers: 1, temporalLayers: 1 }); +test('parseScalabilityMode() works', () => { + expect(parseScalabilityMode('L1T3')).toEqual({ + spatialLayers: 1, + temporalLayers: 3, + }); + expect(parseScalabilityMode('L3T2_KEY')).toEqual({ + spatialLayers: 3, + temporalLayers: 2, + }); + expect(parseScalabilityMode('S2T3')).toEqual({ + spatialLayers: 2, + temporalLayers: 3, + }); + expect(parseScalabilityMode('foo')).toEqual({ + spatialLayers: 1, + temporalLayers: 1, + }); + expect(parseScalabilityMode()).toEqual({ + spatialLayers: 1, + temporalLayers: 1, + }); + expect(parseScalabilityMode('S0T3')).toEqual({ + spatialLayers: 1, + temporalLayers: 1, + }); + expect(parseScalabilityMode('S1T0')).toEqual({ + spatialLayers: 1, + temporalLayers: 1, + }); + expect(parseScalabilityMode('L20T3')).toEqual({ + spatialLayers: 20, + temporalLayers: 3, + }); + expect(parseScalabilityMode('S200T3')).toEqual({ + spatialLayers: 1, + temporalLayers: 1, + }); }); -describe('detectDevice() assigns proper handler based on UserAgent', () => -{ +describe('detectDevice() assigns proper handler based on UserAgent', () => { const originalNavigator = global.navigator; - for (const uaTestCase of uaTestCases) - { - test(uaTestCase.desc, () => - { - // @ts-ignore - global.navigator = - { - userAgent : uaTestCase.ua - }; - - const originalRTCRtpTransceiver = global.RTCRtpTransceiver; - - if (uaTestCase.expect === 'Safari12') - { - global.RTCRtpTransceiver = class Dummy - { - currentDirection() - {} - } as any; - } + for (const uaTestCase of uaTestCases) { + test( + uaTestCase.desc, + () => { + // @ts-ignore + global.navigator = { + userAgent: uaTestCase.ua, + }; + + const originalRTCRtpTransceiver = global.RTCRtpTransceiver; + + if (uaTestCase.expect === 'Safari12') { + global.RTCRtpTransceiver = class Dummy { + currentDirection() {} + } as any; + } - expect(detectDevice()).toBe(uaTestCase.expect); + expect(detectDevice()).toBe(uaTestCase.expect); - // Cleanup. - global.RTCRtpTransceiver = originalRTCRtpTransceiver; - }, 100); + // Cleanup. + global.RTCRtpTransceiver = originalRTCRtpTransceiver; + }, + 100, + ); } // Cleanup. diff --git a/src/test/uaTestCases.ts b/src/test/uaTestCases.ts new file mode 100644 index 00000000..68cb5aee --- /dev/null +++ b/src/test/uaTestCases.ts @@ -0,0 +1,90 @@ +import { BuiltinHandlerName } from '../Device'; + +type UATestCase = { + desc: string; + ua: string; + expect?: BuiltinHandlerName; +}; + +export const uaTestCases: UATestCase[] = [ + { + desc: 'Microsoft Edge 100', + ua: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.1108.55 Safari/537.36 Edg/100.0.1108.55', + expect: 'Chrome74', + }, + { + desc: 'Mac (Intel) Chrome 112', + ua: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36', + expect: 'Chrome111', + }, + { + desc: 'Generic Android Chrome 112', + ua: 'Mozilla/5.0 (Linux; Android 13; M2012K11AG) AppleWebKit/537.36 (KHTML, like Gecko) Soul/4.0 Chrome/112.0.5615.135 Mobile Safari/537.36', + expect: 'Chrome111', + }, + { + desc: 'Motorola Edge Chrome 104', + ua: 'Mozilla/5.0 (Linux; Android 10; motorola edge) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.0.0 Mobile Safari/537.36', + expect: 'Chrome74', + }, + { + desc: 'Microsoft Edge 44', + ua: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.140 Safari/537.36 Edge/18.17763', + expect: 'Edge11', + }, + { + desc: 'Firefox 68 (Android)', + ua: 'Mozilla/5.0 (Android 10; Mobile; rv:68.10.0) Gecko/68.10.0 Firefox/68.10.0', + expect: 'Firefox60', + }, + { + desc: 'In-app WebView (Android)', + ua: 'Mozilla/5.0 (Linux; Android 11; G91 Pro Build/RP1A.200720.011; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/114.0.5735.130 Mobile Safari/537.36', + expect: 'Chrome111', + }, + { + desc: 'Safari 11', + ua: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 11_13) AppleWebKit/537.36 (KHTML, like Gecko) Version/11.0.92 Safari/619.28', + expect: 'Safari11', + }, + { + desc: 'Safari 11 (iPad)', + ua: 'Mozilla/5.0 (iPad; CPU OS 14_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/11.0 Mobile/15E148 Safari/604.1', + expect: 'Safari11', + }, + { + desc: 'Brave', + ua: 'Mozilla/5.0 (Windows NT 10.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.4044.113 Safari/5370.36 Brave/9085', + expect: 'Chrome111', + }, + { + desc: 'In-app WebView (Android) (Facebook)', + ua: 'Mozilla/5.0 (Linux; Android 12; SM-S908U1 Build/SP1A.210812.016; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/100.0.4896.88 Mobile Safari/537.36 [FB_IAB/FB4A;FBAV/377.0.0.22.107;]', + expect: 'Chrome74', + }, + { + desc: 'Firefox (Linux)', + ua: 'Mozilla/5.0 (X11; Ubuntu; Linux i686; rv:109.0) Gecko/20100101 Firefox/114.0', + expect: 'Firefox60', + }, + { + desc: 'Firefox (iOS) - Unsupported', + ua: 'Mozilla/5.0 (iPhone; CPU iPhone OS 13_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) FxiOS/114.0 Mobile/15E148 Safari/605.1.15', + expect: undefined, + }, + { + desc: 'In-app WKWebView (iOS) (TikTok)', + ua: 'Mozilla/5.0 (iPhone; CPU iPhone OS 14_8 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 musical_ly_21.1.0 JsSdk/2.0 NetType/4G Channel/App Store ByteLocale/ru Region/RU ByteFullLocale/ru-RU isDarkMode/1 WKWebView/1 BytedanceWebview/d8a21c6', + expect: 'Safari12', + }, + { + desc: 'In-app WkWebView (iOS) (WeChat)', + ua: 'Mozilla/5.0 (iPhone; CPU iPhone OS 16_0_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 MicroMessenger/8.0.37(0x1800252f) NetType/WIFI Language/zh_CN', + expect: 'Safari12', + }, + { + desc: 'Chrome Mobile (iOS)', + ua: 'Mozilla/5.0 (iPhone; CPU iPhone OS 16_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) CriOS/114.0.5735.124 Mobile/15E148 Safari/604.1', + expect: 'Safari12', + }, +]; diff --git a/src/tests/fakeParameters.ts b/src/tests/fakeParameters.ts deleted file mode 100644 index 09842990..00000000 --- a/src/tests/fakeParameters.ts +++ /dev/null @@ -1,717 +0,0 @@ -import * as mediasoupClient from '../'; -import * as utils from '../utils'; - -function generateFakeUuid(): string -{ - return String(utils.generateRandomNumber()); -} - -export function generateRouterRtpCapabilities(): mediasoupClient.types.RtpCapabilities -{ - return { - codecs : - [ - { - mimeType : 'audio/opus', - kind : 'audio', - preferredPayloadType : 100, - clockRate : 48000, - channels : 2, - rtcpFeedback : - [ - { type: 'transport-cc' } - ], - parameters : - { - useinbandfec : 1, - foo : 'bar' - } - }, - { - mimeType : 'video/VP8', - kind : 'video', - preferredPayloadType : 101, - clockRate : 90000, - rtcpFeedback : - [ - { type: 'nack' }, - { type: 'nack', parameter: 'pli' }, - { type: 'ccm', parameter: 'fir' }, - { type: 'goog-remb' }, - { type: 'transport-cc' } - ], - parameters : - { - 'x-google-start-bitrate' : 1500 - } - }, - { - mimeType : 'video/rtx', - kind : 'video', - preferredPayloadType : 102, - clockRate : 90000, - rtcpFeedback : [], - parameters : - { - apt : 101 - } - }, - { - mimeType : 'video/H264', - kind : 'video', - preferredPayloadType : 103, - clockRate : 90000, - rtcpFeedback : - [ - { type: 'nack' }, - { type: 'nack', parameter: 'pli' }, - { type: 'ccm', parameter: 'fir' }, - { type: 'goog-remb' }, - { type: 'transport-cc' } - ], - parameters : - { - 'level-asymmetry-allowed' : 1, - 'packetization-mode' : 1, - 'profile-level-id' : '42e01f' - } - }, - { - mimeType : 'video/rtx', - kind : 'video', - preferredPayloadType : 104, - clockRate : 90000, - rtcpFeedback : [], - parameters : - { - apt : 103 - } - } - ], - headerExtensions : - [ - { - kind : 'audio', - uri : 'urn:ietf:params:rtp-hdrext:sdes:mid', - preferredId : 1, - preferredEncrypt : false, - direction : 'sendrecv' - }, - { - kind : 'video', - uri : 'urn:ietf:params:rtp-hdrext:sdes:mid', - preferredId : 1, - preferredEncrypt : false, - direction : 'sendrecv' - }, - { - kind : 'video', - uri : 'urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id', - preferredId : 2, - preferredEncrypt : false, - direction : 'recvonly' - }, - { - kind : 'video', - uri : 'urn:ietf:params:rtp-hdrext:sdes:repaired-rtp-stream-id', - preferredId : 3, - preferredEncrypt : false, - direction : 'recvonly' - }, - { - kind : 'audio', - uri : 'http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time', - preferredId : 4, - preferredEncrypt : false, - direction : 'sendrecv' - }, - { - kind : 'video', - uri : 'http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time', - preferredId : 4, - preferredEncrypt : false, - direction : 'sendrecv' - }, - { - kind : 'audio', - uri : 'http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01', - preferredId : 5, - preferredEncrypt : false, - direction : 'recvonly' - }, - { - kind : 'video', - uri : 'http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01', - preferredId : 5, - preferredEncrypt : false, - direction : 'sendrecv' - }, - { - kind : 'video', - uri : 'http://tools.ietf.org/html/draft-ietf-avtext-framemarking-07', - preferredId : 6, - preferredEncrypt : false, - direction : 'sendrecv' - }, - { - kind : 'video', - uri : 'urn:ietf:params:rtp-hdrext:framemarking', - preferredId : 7, - preferredEncrypt : false, - direction : 'sendrecv' - }, - { - kind : 'audio', - uri : 'urn:ietf:params:rtp-hdrext:ssrc-audio-level', - preferredId : 10, - preferredEncrypt : false, - direction : 'sendrecv' - }, - { - kind : 'video', - uri : 'urn:3gpp:video-orientation', - preferredId : 11, - preferredEncrypt : false, - direction : 'sendrecv' - }, - { - kind : 'video', - uri : 'urn:ietf:params:rtp-hdrext:toffset', - preferredId : 12, - preferredEncrypt : false, - direction : 'sendrecv' - } - ] - }; -} - -export function generateNativeRtpCapabilities(): mediasoupClient.types.RtpCapabilities -{ - return { - codecs : - [ - { - mimeType : 'audio/opus', - kind : 'audio', - preferredPayloadType : 111, - clockRate : 48000, - channels : 2, - rtcpFeedback : - [ - { type: 'transport-cc' } - ], - parameters : - { - minptime : 10, - useinbandfec : 1 - } - }, - { - mimeType : 'audio/ISAC', - kind : 'audio', - preferredPayloadType : 103, - clockRate : 16000, - channels : 1, - rtcpFeedback : - [ - { type: 'transport-cc' } - ], - parameters : {} - }, - { - mimeType : 'audio/CN', - kind : 'audio', - preferredPayloadType : 106, - clockRate : 32000, - channels : 1, - rtcpFeedback : - [ - { type: 'transport-cc' } - ], - parameters : {} - }, - { - mimeType : 'video/VP8', - kind : 'video', - preferredPayloadType : 96, - clockRate : 90000, - rtcpFeedback : - [ - { type: 'goog-remb' }, - { type: 'transport-cc' }, - { type: 'ccm', parameter: 'fir' }, - { type: 'nack' }, - { type: 'nack', parameter: 'pli' } - ], - parameters : - { - baz : '1234abcd' - } - }, - { - mimeType : 'video/rtx', - kind : 'video', - preferredPayloadType : 97, - clockRate : 90000, - rtcpFeedback : [], - parameters : - { - apt : 96 - } - } - ], - headerExtensions : - [ - { - kind : 'audio', - uri : 'urn:ietf:params:rtp-hdrext:sdes:mid', - preferredId : 1 - }, - { - kind : 'video', - uri : 'urn:ietf:params:rtp-hdrext:sdes:mid', - preferredId : 1 - }, - { - kind : 'video', - uri : 'urn:ietf:params:rtp-hdrext:toffset', - preferredId : 2 - }, - { - kind : 'video', - uri : 'http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time', - preferredId : 3 - }, - { - kind : 'video', - uri : 'urn:3gpp:video-orientation', - preferredId : 4 - }, - { - kind : 'video', - uri : 'http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01', - preferredId : 5 - }, - { - kind : 'video', - // @ts-ignore - uri : 'http://www.webrtc.org/experiments/rtp-hdrext/playout-delay', - preferredId : 6 - }, - { - kind : 'video', - // @ts-ignore - uri : 'http://www.webrtc.org/experiments/rtp-hdrext/video-content-type', - preferredId : 7 - }, - { - kind : 'video', - // @ts-ignore - uri : 'http://www.webrtc.org/experiments/rtp-hdrext/video-timing', - preferredId : 8 - }, - { - kind : 'audio', - uri : 'urn:ietf:params:rtp-hdrext:ssrc-audio-level', - preferredId : 10 - } - ] - }; -} - -export function generateNativeSctpCapabilities(): mediasoupClient.types.SctpCapabilities -{ - return { - numStreams : { OS: 2048, MIS: 2048 } - }; -} - -export function generateLocalDtlsParameters(): mediasoupClient.types.DtlsParameters -{ - return { - fingerprints : - [ - { - algorithm : 'sha-256', - value : '82:5A:68:3D:36:C3:0A:DE:AF:E7:32:43:D2:88:83:57:AC:2D:65:E5:80:C4:B6:FB:AF:1A:A0:21:9F:6D:0C:AD' - } - ], - role : 'auto' - }; -} - -export function generateTransportRemoteParameters(): - mediasoupClient.types.TransportOptions -{ - return { - id : generateFakeUuid(), - iceParameters : - { - iceLite : true, - password : 'yku5ej8nvfaor28lvtrabcx0wkrpkztz', - usernameFragment : 'h3hk1iz6qqlnqlne' - }, - iceCandidates : - [ - { - foundation : 'udpcandidate', - ip : '9.9.9.9', - port : 40533, - priority : 1078862079, - protocol : 'udp', - type : 'host', - tcpType : 'passive' - }, - { - foundation : 'udpcandidate', - ip : '9:9:9:9:9:9', - port : 41333, - priority : 1078862089, - protocol : 'udp', - type : 'host', - tcpType : 'passive' - } - ], - dtlsParameters : - { - fingerprints : - [ - { - algorithm : 'sha-256', - value : 'A9:F4:E0:D2:74:D3:0F:D9:CA:A5:2F:9F:7F:47:FA:F0:C4:72:DD:73:49:D0:3B:14:90:20:51:30:1B:90:8E:71' - }, - { - algorithm : 'sha-384', - value : '03:D9:0B:87:13:98:F6:6D:BC:FC:92:2E:39:D4:E1:97:32:61:30:56:84:70:81:6E:D1:82:97:EA:D9:C1:21:0F:6B:C5:E7:7F:E1:97:0C:17:97:6E:CF:B3:EF:2E:74:B0' - }, - { - algorithm : 'sha-512', - value : '84:27:A4:28:A4:73:AF:43:02:2A:44:68:FF:2F:29:5C:3B:11:9A:60:F4:A8:F0:F5:AC:A0:E3:49:3E:B1:34:53:A9:85:CE:51:9B:ED:87:5E:B8:F4:8E:3D:FA:20:51:B8:96:EE:DA:56:DC:2F:5C:62:79:15:23:E0:21:82:2B:2C' - } - ], - role : 'auto' - }, - sctpParameters : - { - port : 5000, - OS : 2048, - MIS : 2048, - maxMessageSize : 2000000 - } - }; -} - -export function generateProducerRemoteParameters(): { id: string } -{ - return { - id : generateFakeUuid() - }; -} - -export function generateConsumerRemoteParameters( - { id, codecMimeType }: - { id?: string; codecMimeType?: string } = {} -): mediasoupClient.types.ConsumerOptions -{ - switch (codecMimeType) - { - case 'audio/opus': - { - return { - id : id || generateFakeUuid(), - producerId : generateFakeUuid(), - kind : 'audio', - rtpParameters : - { - codecs : - [ - { - mimeType : 'audio/opus', - payloadType : 100, - clockRate : 48000, - channels : 2, - rtcpFeedback : - [ - { type: 'transport-cc' } - ], - parameters : - { - useinbandfec : 1, - foo : 'bar' - } - } - ], - encodings : - [ - { - ssrc : 46687003 - } - ], - headerExtensions : - [ - { - uri : 'urn:ietf:params:rtp-hdrext:sdes:mid', - id : 1 - }, - { - uri : 'http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01', - id : 5 - }, - { - uri : 'urn:ietf:params:rtp-hdrext:ssrc-audio-level', - id : 10 - } - ], - rtcp : - { - cname : 'wB4Ql4lrsxYLjzuN', - reducedSize : true, - mux : true - } - } - }; - } - - case 'audio/ISAC': - { - return { - id : id || generateFakeUuid(), - producerId : generateFakeUuid(), - kind : 'audio', - rtpParameters : - { - codecs : - [ - { - mimeType : 'audio/ISAC', - payloadType : 111, - clockRate : 16000, - channels : 1, - rtcpFeedback : - [ - { type: 'transport-cc' } - ], - parameters : {} - } - ], - encodings : - [ - { - ssrc : 46687004 - } - ], - headerExtensions : - [ - { - uri : 'urn:ietf:params:rtp-hdrext:sdes:mid', - id : 1 - }, - { - uri : 'http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01', - id : 5 - } - ], - rtcp : - { - cname : 'wB4Ql4lrsxYLjzuN', - reducedSize : true, - mux : true - } - } - }; - } - - case 'video/VP8': - { - return { - id : id || generateFakeUuid(), - producerId : generateFakeUuid(), - kind : 'video', - rtpParameters : - { - codecs : - [ - { - mimeType : 'video/VP8', - payloadType : 101, - clockRate : 90000, - rtcpFeedback : - [ - { type: 'nack' }, - { type: 'nack', parameter: 'pli' }, - { type: 'ccm', parameter: 'fir' }, - { type: 'goog-remb' }, - { type: 'transport-cc' } - ], - parameters : - { - 'x-google-start-bitrate' : 1500 - } - }, - { - mimeType : 'video/rtx', - payloadType : 102, - clockRate : 90000, - rtcpFeedback : [], - parameters : - { - apt : 101 - } - } - ], - encodings : - [ - { - ssrc : 99991111, - rtx : - { - ssrc : 99991112 - } - } - ], - headerExtensions : - [ - { - uri : 'urn:ietf:params:rtp-hdrext:sdes:mid', - id : 1 - }, - { - uri : 'http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time', - id : 4 - }, - { - uri : 'http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01', - id : 5 - }, - { - uri : 'urn:3gpp:video-orientation', - id : 11 - }, - { - uri : 'urn:ietf:params:rtp-hdrext:toffset', - id : 12 - } - ], - rtcp : - { - cname : 'wB4Ql4lrsxYLjzuN', - reducedSize : true, - mux : true - } - } - }; - } - - case 'video/H264': - { - return { - id : id || generateFakeUuid(), - producerId : generateFakeUuid(), - kind : 'video', - rtpParameters : - { - codecs : - [ - { - mimeType : 'video/H264', - payloadType : 103, - clockRate : 90000, - rtcpFeedback : - [ - { type: 'nack' }, - { type: 'nack', parameter: 'pli' }, - { type: 'ccm', parameter: 'fir' }, - { type: 'goog-remb' }, - { type: 'transport-cc' } - ], - parameters : - { - 'level-asymmetry-allowed' : 1, - 'packetization-mode' : 1, - 'profile-level-id' : '42e01f' - } - }, - { - mimeType : 'video/rtx', - payloadType : 104, - clockRate : 90000, - rtcpFeedback : [], - parameters : - { - apt : 103 - } - } - ], - encodings : - [ - { - ssrc : 99991113, - rtx : - { - ssrc : 99991114 - } - } - ], - headerExtensions : - [ - { - uri : 'urn:ietf:params:rtp-hdrext:sdes:mid', - id : 1 - }, - { - uri : 'http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time', - id : 4 - }, - { - uri : 'http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01', - id : 5 - }, - { - uri : 'urn:3gpp:video-orientation', - id : 11 - }, - { - uri : 'urn:ietf:params:rtp-hdrext:toffset', - id : 12 - } - ], - rtcp : - { - cname : 'wB4Ql4lrsxYLjzuN', - reducedSize : true, - mux : true - } - } - }; - } - - default: - { - throw new TypeError(`unknown codecMimeType "${codecMimeType}"`); - } - } -} - -export function generateDataProducerRemoteParameters(): { id: string } -{ - return { - id : generateFakeUuid() - }; -} - -export function generateDataConsumerRemoteParameters( - { id }: - { id?: string } = {} -): mediasoupClient.types.DataConsumerOptions -{ - return { - id : id || generateFakeUuid(), - dataProducerId : generateFakeUuid(), - sctpStreamParameters : - { - streamId : 666, - maxPacketLifeTime : 5000, - maxRetransmits : undefined - } - }; -} diff --git a/src/tests/uaTestCases.ts b/src/tests/uaTestCases.ts deleted file mode 100644 index 1527ff1d..00000000 --- a/src/tests/uaTestCases.ts +++ /dev/null @@ -1,92 +0,0 @@ -import { BuiltinHandlerName } from '../Device'; - -type UATestCase = -{ - desc: string; - ua: string; - expect?: BuiltinHandlerName; -}; - -export const uaTestCases: UATestCase[] = -[ - { - desc : 'Microsoft Edge 100', - ua : 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.1108.55 Safari/537.36 Edg/100.0.1108.55', - expect : 'Chrome74' - }, - { - desc : 'Mac (Intel) Chrome 112', - ua : 'Mozilla/5.0 (Macintosh; Intel Mac OS X 12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36', - expect : 'Chrome111' - }, - { - desc : 'Generic Android Chrome 112', - ua : 'Mozilla/5.0 (Linux; Android 13; M2012K11AG) AppleWebKit/537.36 (KHTML, like Gecko) Soul/4.0 Chrome/112.0.5615.135 Mobile Safari/537.36', - expect : 'Chrome111' - }, - { - desc : 'Motorola Edge Chrome 104', - ua : 'Mozilla/5.0 (Linux; Android 10; motorola edge) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.0.0 Mobile Safari/537.36', - expect : 'Chrome74' - }, - { - desc : 'Microsoft Edge 44', - ua : 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.140 Safari/537.36 Edge/18.17763', - expect : 'Edge11' - }, - { - desc : 'Firefox 68 (Android)', - ua : 'Mozilla/5.0 (Android 10; Mobile; rv:68.10.0) Gecko/68.10.0 Firefox/68.10.0', - expect : 'Firefox60' - }, - { - desc : 'In-app WebView (Android)', - ua : 'Mozilla/5.0 (Linux; Android 11; G91 Pro Build/RP1A.200720.011; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/114.0.5735.130 Mobile Safari/537.36', - expect : 'Chrome111' - }, - { - desc : 'Safari 11', - ua : 'Mozilla/5.0 (Macintosh; Intel Mac OS X 11_13) AppleWebKit/537.36 (KHTML, like Gecko) Version/11.0.92 Safari/619.28', - expect : 'Safari11' - }, - { - desc : 'Safari 11 (iPad)', - ua : 'Mozilla/5.0 (iPad; CPU OS 14_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/11.0 Mobile/15E148 Safari/604.1', - expect : 'Safari11' - }, - { - desc : 'Brave', - ua : 'Mozilla/5.0 (Windows NT 10.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.4044.113 Safari/5370.36 Brave/9085', - expect : 'Chrome111' - }, - { - desc : 'In-app WebView (Android) (Facebook)', - ua : 'Mozilla/5.0 (Linux; Android 12; SM-S908U1 Build/SP1A.210812.016; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/100.0.4896.88 Mobile Safari/537.36 [FB_IAB/FB4A;FBAV/377.0.0.22.107;]', - expect : 'Chrome74' - }, - { - desc : 'Firefox (Linux)', - ua : 'Mozilla/5.0 (X11; Ubuntu; Linux i686; rv:109.0) Gecko/20100101 Firefox/114.0', - expect : 'Firefox60' - }, - { - desc : 'Firefox (iOS) - Unsupported', - ua : 'Mozilla/5.0 (iPhone; CPU iPhone OS 13_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) FxiOS/114.0 Mobile/15E148 Safari/605.1.15', - expect : undefined - }, - { - desc : 'In-app WKWebView (iOS) (TikTok)', - ua : 'Mozilla/5.0 (iPhone; CPU iPhone OS 14_8 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 musical_ly_21.1.0 JsSdk/2.0 NetType/4G Channel/App Store ByteLocale/ru Region/RU ByteFullLocale/ru-RU isDarkMode/1 WKWebView/1 BytedanceWebview/d8a21c6', - expect : 'Safari12' - }, - { - desc : 'In-app WkWebView (iOS) (WeChat)', - ua : 'Mozilla/5.0 (iPhone; CPU iPhone OS 16_0_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 MicroMessenger/8.0.37(0x1800252f) NetType/WIFI Language/zh_CN', - expect : 'Safari12' - }, - { - desc : 'Chrome Mobile (iOS)', - ua : 'Mozilla/5.0 (iPhone; CPU iPhone OS 16_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) CriOS/114.0.5735.124 Mobile/15E148 Safari/604.1', - expect : 'Safari12' - } -]; diff --git a/src/types.ts b/src/types.ts index 4086b24c..4ef4abf6 100644 --- a/src/types.ts +++ b/src/types.ts @@ -10,7 +10,6 @@ export * from './handlers/HandlerInterface'; export * from './errors'; export type { ScalabilityMode } from './scalabilityModes'; -export type AppData = -{ +export type AppData = { [key: string]: unknown; }; diff --git a/src/utils.ts b/src/utils.ts index 02e6792c..08c0c814 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,23 +1,15 @@ /** * Clones the given value. */ -export function clone(value: T): T -{ - if (value === undefined) - { +export function clone(value: T): T { + if (value === undefined) { return undefined as unknown as T; - } - else if (Number.isNaN(value)) - { + } else if (Number.isNaN(value)) { return NaN as unknown as T; - } - else if (typeof structuredClone === 'function') - { + } else if (typeof structuredClone === 'function') { // Available in Node >= 18. return structuredClone(value); - } - else - { + } else { return JSON.parse(JSON.stringify(value)); } } @@ -25,7 +17,6 @@ export function clone(value: T): T /** * Generates a random positive integer. */ -export function generateRandomNumber(): number -{ +export function generateRandomNumber(): number { return Math.round(Math.random() * 10000000); } diff --git a/tsconfig.json b/tsconfig.json index 5c0f3ff9..173a0078 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,8 +1,7 @@ { "compileOnSave": true, - "compilerOptions": - { - "lib": [ "es2020", "dom" ], + "compilerOptions": { + "lib": ["es2020", "dom"], "target": "es2020", "module": "commonjs", "moduleResolution": "node", @@ -13,11 +12,11 @@ "declaration": true, "declarationMap": true }, - "include": [ "src" ], + "include": ["src"], "watchOptions": { - "watchFile": "useFsEvents", - "watchDirectory": "useFsEvents", - "fallbackPolling": "dynamicPriority", - "synchronousWatchDirectory": true + "watchFile": "useFsEvents", + "watchDirectory": "useFsEvents", + "fallbackPolling": "dynamicPriority", + "synchronousWatchDirectory": true } }