├── .eslintignore ├── .eslintrc.json ├── .github └── workflows │ ├── ci.yml │ └── publish.yml ├── .gitignore ├── .npmignore ├── .travis.yml ├── LICENSE ├── README.md ├── examples └── websocket.js ├── index.js ├── lib ├── BaseClient.js ├── Client.js ├── TcpClient.js ├── WsClient.js ├── constants.js └── error.js ├── package-lock.json ├── package.json ├── test ├── data │ ├── ._nosplit.txt │ ├── combine.txt │ ├── nosplit.txt │ ├── split-part1.txt │ └── split-part2.txt ├── fakeserver.js ├── index.js └── unit.js └── yarn.lock /.eslintignore: -------------------------------------------------------------------------------- 1 | test/* 2 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "node": true, 4 | "es6": true 5 | }, 6 | "parserOptions": { 7 | "ecmaFeatures": { 8 | "jsx": false, 9 | "modules": false 10 | }, 11 | "ecmaVersion": 2020 12 | }, 13 | "plugins": ["promise"], 14 | "rules": { 15 | "promise/always-return": "error", 16 | "promise/no-return-wrap": "error", 17 | "promise/param-names": "error", 18 | "promise/catch-or-return": "error", 19 | "promise/no-native": "off", 20 | "promise/no-nesting": "warn", 21 | "promise/no-promise-in-callback": "warn", 22 | "promise/no-callback-in-promise": "warn", 23 | "promise/no-return-in-finally": "warn", 24 | 25 | // Possible Errors 26 | // http://eslint.org/docs/rules/#possible-errors 27 | "comma-dangle": [2, "only-multiline"], 28 | "no-control-regex": 2, 29 | "no-debugger": 2, 30 | "no-dupe-args": 2, 31 | "no-dupe-keys": 2, 32 | "no-duplicate-case": 2, 33 | "no-empty-character-class": 2, 34 | "no-ex-assign": 2, 35 | "no-extra-boolean-cast" : 2, 36 | "no-extra-parens": [2, "functions"], 37 | "no-extra-semi": 2, 38 | "no-func-assign": 2, 39 | "no-invalid-regexp": 2, 40 | "no-irregular-whitespace": 2, 41 | "no-negated-in-lhs": 2, 42 | "no-obj-calls": 2, 43 | "no-proto": 2, 44 | "no-unexpected-multiline": 2, 45 | "no-unreachable": 2, 46 | "use-isnan": 2, 47 | "valid-typeof": 2, 48 | 49 | // Best Practices 50 | // http://eslint.org/docs/rules/#best-practices 51 | "no-fallthrough": 2, 52 | "no-octal": 2, 53 | "no-redeclare": 2, 54 | "no-self-assign": 2, 55 | "no-unused-labels": 2, 56 | 57 | // Strict Mode 58 | // http://eslint.org/docs/rules/#strict-mode 59 | "strict": [2, "never"], 60 | 61 | // Variables 62 | // http://eslint.org/docs/rules/#variables 63 | "no-delete-var": 2, 64 | "no-undef": 2, 65 | "no-unused-vars": [2, {"args": "none"}], 66 | 67 | // Node.js and CommonJS 68 | // http://eslint.org/docs/rules/#nodejs-and-commonjs 69 | "no-mixed-requires": 2, 70 | "no-new-require": 2, 71 | "no-path-concat": 2, 72 | "no-restricted-modules": [2, "sys", "_linklist"], 73 | 74 | // Stylistic Issues 75 | // http://eslint.org/docs/rules/#stylistic-issues 76 | "comma-spacing": 2, 77 | "eol-last": 2, 78 | "indent": [2, 2, {"SwitchCase": 1}], 79 | "keyword-spacing": 2, 80 | "max-len": [2, 120, 2], 81 | "new-parens": 2, 82 | "no-mixed-spaces-and-tabs": 2, 83 | "no-multiple-empty-lines": [2, {"max": 2}], 84 | "no-trailing-spaces": [2, {"skipBlankLines": false }], 85 | "quotes": [2, "single", "avoid-escape"], 86 | "semi": 2, 87 | "space-before-blocks": [2, "always"], 88 | "space-before-function-paren": [2, "never"], 89 | "space-in-parens": [2, "never"], 90 | "space-infix-ops": 2, 91 | "space-unary-ops": 2, 92 | 93 | // ECMAScript 6 94 | // http://eslint.org/docs/rules/#ecmascript-6 95 | "arrow-parens": [2, "always"], 96 | "arrow-spacing": [2, {"before": true, "after": true}], 97 | "constructor-super": 2, 98 | "no-class-assign": 2, 99 | "no-confusing-arrow": 2, 100 | "no-const-assign": 2, 101 | "no-dupe-class-members": 2, 102 | "no-new-symbol": 2, 103 | "no-this-before-super": 2, 104 | "prefer-const": 2 105 | }, 106 | "globals": { 107 | "DTRACE_HTTP_CLIENT_REQUEST" : false, 108 | "LTTNG_HTTP_CLIENT_REQUEST" : false, 109 | "COUNTER_HTTP_CLIENT_REQUEST" : false, 110 | "DTRACE_HTTP_CLIENT_RESPONSE" : false, 111 | "LTTNG_HTTP_CLIENT_RESPONSE" : false, 112 | "COUNTER_HTTP_CLIENT_RESPONSE" : false, 113 | "DTRACE_HTTP_SERVER_REQUEST" : false, 114 | "LTTNG_HTTP_SERVER_REQUEST" : false, 115 | "COUNTER_HTTP_SERVER_REQUEST" : false, 116 | "DTRACE_HTTP_SERVER_RESPONSE" : false, 117 | "LTTNG_HTTP_SERVER_RESPONSE" : false, 118 | "COUNTER_HTTP_SERVER_RESPONSE" : false, 119 | "DTRACE_NET_STREAM_END" : false, 120 | "LTTNG_NET_STREAM_END" : false, 121 | "COUNTER_NET_SERVER_CONNECTION_CLOSE" : false, 122 | "DTRACE_NET_SERVER_CONNECTION" : false, 123 | "LTTNG_NET_SERVER_CONNECTION" : false, 124 | "COUNTER_NET_SERVER_CONNECTION" : false 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | workflow_dispatch: 6 | 7 | jobs: 8 | build: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@v3 12 | - uses: actions/setup-node@v3 13 | with: 14 | node-version: 14 15 | - run: npm ci 16 | - run: npm run jslint 17 | - run: npm test 18 | 19 | -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | name: npm-publish 2 | 3 | # run when a tag is pushed or kick off manually 4 | on: 5 | push: 6 | tags: 7 | - '*' 8 | workflow_dispatch: 9 | 10 | jobs: 11 | build: 12 | runs-on: ubuntu-latest 13 | 14 | steps: 15 | - uses: actions/checkout@v2 16 | - uses: actions/setup-node@v1 17 | with: 18 | node-version: '14.x' 19 | registry-url: 'https://registry.npmjs.org' 20 | - run: npm install 21 | - run: npm publish --access public 22 | env: 23 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} 24 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | .DS_Store 6 | .coveralls.yml 7 | 8 | config.json 9 | 10 | # Runtime data 11 | pids 12 | *.pid 13 | *.seed 14 | 15 | # Directory for instrumented libs generated by jscoverage/JSCover 16 | lib-cov 17 | 18 | # Coverage directory used by tools like istanbul 19 | coverage 20 | 21 | # nyc test coverage 22 | .nyc_output 23 | 24 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 25 | .grunt 26 | 27 | # node-waf configuration 28 | .lock-wscript 29 | 30 | # Compiled binary addons (http://nodejs.org/api/addons.html) 31 | build/Release 32 | 33 | # Dependency directories 34 | node_modules 35 | jspm_packages 36 | 37 | # Optional npm cache directory 38 | .npm 39 | 40 | # Optional REPL history 41 | .node_repl_history 42 | 43 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | examples -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "10" 4 | after_success: npm run coveralls -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2018 Dave Horton 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # rtpengine-client [![Build Status](https://travis-ci.org/davehorton/rtpengine-client.svg?branch=master)](http://travis-ci.org/davehorton/rtpengine-client) [![NPM version](https://badge.fury.io/js/rtpengine-client.svg)](http://badge.fury.io/js/rtpengine-client) [![Coverage Status](https://coveralls.io/repos/github/davehorton/rtpengine-client/badge.svg?branch=master)](https://coveralls.io/github/davehorton/rtpengine-client?branch=master) 2 | 3 | A Promises-based nodejs client for accessing rtpengine via ng protocol 4 | 5 | ## Usage 6 | 7 | ```js 8 | const Client = require('rtpengine-client').Client; 9 | const client = new Client(); 10 | 11 | client.ping(22222, '39.194.250.246') 12 | .then((res) => { 13 | console.log(`received ${JSON.stringify(res)}`); // {result: 'pong'} 14 | }) 15 | .catch((err) => { 16 | console.log(`Error: ${err}`); 17 | }); 18 | ``` 19 | 20 | ## Constructing a client 21 | ```js 22 | client = new Client(); // listen on any port and default address 23 | // or.. 24 | client = new Client(9055); // listen on a specific port 25 | // or.. 26 | client = new Client(9055, '192.168.1.10'); // listen on a specific port and address 27 | // or.. 28 | client = new Client({port: 9055, host: '192.168.1.10'}); // listen on a specific port and address 29 | // or.. 30 | client = new Client({timeout: 1500}); // wait a max of 1500 ms for each command reply, throw error on timeout 31 | // or.. 32 | client = new Client({rejectOnFailure: true}); 33 | // reject promise on any command if response from rtpengine has error 34 | // default behavior is to resolve with any response from rtpengine, even errors 35 | ``` 36 | 37 | ## Websocket support 38 | 39 | ```js 40 | const client = new Client('ws://:8080'); 41 | 42 | client.on('listening', () => { 43 | client.statistics() 44 | .then((res) => { 45 | console.log('received data', res); 46 | }) 47 | .catch((err) => { 48 | console.log(`Error: ${err}`); 49 | }); 50 | }); 51 | ``` 52 | 53 | ## Making requests 54 | The ng request verbs (`ping`, `offer`, `answer`, `delete`, `query`, `start recording`, `stop recording`, `block DTMF`, `unblock DTMF`, `block media`, `unblock media`) are available as methods on the `client` object. The sytax for each is the same: 55 | + the destination of the request comes first, either as `port, host` or `{port, host}` 56 | + following that, if any options are required for the request, those come next in an object. 57 | 58 | The function call returns a promise that is resolved when the response is received. 59 | 60 | Function names are as follows: 61 | 62 | | ng verb | function name | 63 | |------------------|------------------| 64 | |ping | ping | 65 | |offer | offer | 66 | |answer | answer | 67 | |delete | delete | 68 | |query | query | 69 | |start recording | startRecording | 70 | |stop recording | stopRecording | 71 | |block DTMF | blockDTMF | 72 | |unblock DTMF | unblockDTMF | 73 | |play DTMF | playDTMF | 74 | |block media | blockMedia | 75 | |unblock media | unblockMedia | 76 | |silence media | silenceMedia | 77 | |unsilence media | unsilenceMedia | 78 | |start forwarding | startForwarding | 79 | |stop forwarding | stopForwarding | 80 | |play media | playMedia | 81 | |stop media | stopMedia | 82 | |statistics | statistics | 83 | |publish | publish | 84 | |subscribe request | subscribeRequest | 85 | |subscribe answer | subscribeAnswer | 86 | |unsubscribe | unsubscribe | 87 | 88 | For instance 89 | ``` 90 | client.offer(22222, '35.195.250.243', { 91 | 'sdp': .. 92 | 'call-id': .. 93 | 'from-tag': .. 94 | }) 95 | .then((res) => { 96 | console.log(res); // { "result": "ok", "sdp": "v=0\r\no=..." } 97 | }) 98 | .catch((err) => { 99 | 100 | }); 101 | 102 | // or.. 103 | client.offer({port: 22222, host: '35.195.250.243}, { 104 | 'sdp': .. 105 | 'call-id': .. 106 | 'from-tag': .. 107 | }) // ...etc 108 | ``` -------------------------------------------------------------------------------- /examples/websocket.js: -------------------------------------------------------------------------------- 1 | const Client = require('../').WsClient; 2 | const client = new Client('ws://:8080'); 3 | 4 | client.on('listening', () => { 5 | client.statistics() 6 | .then((res) => { 7 | console.log('received data', res); 8 | }) 9 | .catch((err) => { 10 | console.log(`Error: ${err}`); 11 | }); 12 | }); 13 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | const Client = require('./lib/Client'); 2 | const WsClient = require('./lib/WsClient'); 3 | const TcpClient = require('./lib/TcpClient'); 4 | const RtpEngineError = require('./lib/error'); 5 | 6 | module.exports = { Client, RtpEngineError, WsClient, TcpClient }; 7 | -------------------------------------------------------------------------------- /lib/BaseClient.js: -------------------------------------------------------------------------------- 1 | const bencode = require('bencode'); 2 | const v4 = require('uuid-random'); 3 | const Emitter = require('events').EventEmitter; 4 | const RtpEngineError = require('./error'); 5 | const { getRtpEngineNameForCommand } = require('./constants'); 6 | const debug = require('debug')('rtpengine:baseClient'); 7 | 8 | class BaseClient extends Emitter { 9 | 10 | constructor(opts) { 11 | super(); 12 | this.type = opts.type; 13 | this.connected = false; 14 | this.timers = new Map(); 15 | this.timeout = opts.timeout || 0; 16 | 17 | this.messages = new Map(); 18 | this.incomingMsgs = []; 19 | if (this.timeout) { 20 | this.timers = new Map(); 21 | } 22 | } 23 | 24 | get connectionBased() { 25 | return ['tcp', 'websocket'].includes(this.type); 26 | } 27 | 28 | send_internal(name, opts, callback) { 29 | if (typeof opts === 'function') { 30 | callback = opts; 31 | opts = {}; 32 | } 33 | opts = { 34 | ...opts, 35 | command: getRtpEngineNameForCommand(name) 36 | }; 37 | 38 | debug(`RtpEngine: ${name} ${JSON.stringify(opts)}, callback: ${typeof callback}}`); 39 | 40 | const __x = (opts, callback) => { 41 | const cookie = v4(); 42 | const message = BaseClient.encodeMessage(cookie, opts); 43 | this.messages.set(cookie, callback); 44 | 45 | debug(`RtpEngine: sending command: ${cookie}: ${JSON.stringify(opts)}`); 46 | 47 | if (this.timeout) { 48 | debug(`setting timeout: ${this.timeout}ms`); 49 | this.timers.set(cookie, setTimeout(this._onMessageTimeout.bind(this, cookie), this.timeout)); 50 | } 51 | 52 | if (this.type === 'udp') { 53 | this.socket.send(message, this.remotePort, this.remoteHost, (err) => { 54 | if (err) { 55 | debug(`error sending command to rtpengine over ws at ${this.remoteHost}:${this.remotePort}`); 56 | this.messages.delete(cookie); 57 | return callback(err); 58 | } 59 | }); 60 | } else if (this.type === 'websocket') { 61 | this.socket.send(message, (err) => { 62 | if (err) { 63 | debug(`error sending command to rtpengine at ${this.url}`); 64 | this.messages.delete(cookie); 65 | return callback(err); 66 | } 67 | }); 68 | } 69 | else if (this.type === 'tcp') { 70 | if (!this.connected) return callback('socket to rtpengine is not connected'); 71 | this.socket.write(message, (err) => { 72 | if (err) { 73 | debug(`error sending command to rtpengine over tcp at ${this.hostport}`); 74 | this.messages.delete(cookie); 75 | return callback(err); 76 | } 77 | }); 78 | } 79 | }; 80 | 81 | if (callback) { 82 | __x(opts, callback) ; 83 | return this ; 84 | } 85 | 86 | return new Promise((resolve, reject) => { 87 | __x(opts, (err, data) => { 88 | if (err) return reject(err); 89 | resolve(data); 90 | }); 91 | }); 92 | } 93 | 94 | _handleIncomingMessages() { 95 | while (this.incomingMsgs.length) { 96 | const msg = this.incomingMsgs.shift(); 97 | try { 98 | const obj = BaseClient.decodeMessage(msg); 99 | this._onParsedMessage(obj); 100 | } catch (err) { 101 | console.log({err}, 'error decoding message from rtpengine'); 102 | debug(`Error decoding message from rtpengine ${err.message}`); 103 | this.emit('error', new RtpEngineError(`malformed/unexpected message format ${msg}`)); 104 | } 105 | } 106 | } 107 | 108 | _onMessage(msg) { 109 | this.incomingMsgs.push(msg); 110 | setImmediate(this._handleIncomingMessages.bind(this)); 111 | } 112 | 113 | _onParsedMessage(obj) { 114 | if (!this.messages.has(obj.id)) { 115 | console.log({data: obj.data}, `received a response from rtpengine with unknown msg id: '${obj.id}'`); 116 | this.emit('error', new RtpEngineError( 117 | `received a response that can not be correlated to a request: ${obj.id}: ${obj.data}`)); 118 | return; 119 | } 120 | 121 | const callback = this.messages.get(obj.id); 122 | if (this.timers) { 123 | const timer = this.timers.get(obj.id); 124 | clearTimeout(timer); 125 | this.timers.delete(obj.id); 126 | } 127 | 128 | this.messages.delete(obj.id); 129 | if (this.rejectOnError && obj.data.result === 'error') { 130 | return callback(obj.data['error-reason']); 131 | } 132 | setImmediate(callback.bind(null, null, obj.data)); 133 | } 134 | 135 | _onMessageTimeout(id) { 136 | this.timers.delete(id); 137 | const callback = this.messages.get(id); 138 | if (!callback) { 139 | this.emit('error', new RtpEngineError( 140 | `received a timeout that can not be correlated to a request: ${id}`)); 141 | return; 142 | } 143 | this.messages.delete(id); 144 | 145 | let connDetails = ''; 146 | if (this.type === 'udp') { 147 | connDetails = `host:${this.remoteHost} port:${this.remotePort}`; 148 | } else if (this.type === 'websocket') { 149 | connDetails = `url:${this.url}`; 150 | } else if (this.type === 'tcp') { 151 | connDetails = `hostport:${this.hostport}`; 152 | } 153 | 154 | callback(new RtpEngineError(`rtpengine timeout ${connDetails}`)); 155 | } 156 | 157 | _onError(err) { 158 | debug(`RtpEngine#_onError: ${JSON.stringify(err)}`); 159 | this.emit('error', err); 160 | } 161 | 162 | _onListening() { 163 | this.emit('listening'); 164 | if (this.connectionBased) this.connected = true; 165 | } 166 | 167 | _onEnd() { 168 | this.emit('end'); 169 | if (this.connectionBased) this.connected = false; 170 | } 171 | 172 | close() { 173 | if ('tcp' === this.type) this.socket.destroy(); 174 | else this.socket.close(); 175 | if (this.connectionBased) this.connected = false; 176 | } 177 | } 178 | 179 | 180 | BaseClient.decodeMessage = function(msg) { 181 | const idx = msg.indexOf(' '); 182 | if (idx === 36) { 183 | const buf1 = msg.subarray(0, idx); 184 | const buf2 = msg.subarray(idx + 1); 185 | const data = bencode.decode(buf2, 'utf8'); 186 | const obj = { id: buf1.toString(), data }; 187 | return obj; 188 | } 189 | debug(`no data returned from parsing ${msg}`); 190 | throw new Error('Error parsing message'); 191 | }; 192 | 193 | BaseClient.encodeMessage = function(id, data) { 194 | const message = new Buffer.from( 195 | [id, bencode.encode(data)].join(' ') 196 | ); 197 | return message; 198 | }; 199 | 200 | module.exports = BaseClient; 201 | -------------------------------------------------------------------------------- /lib/Client.js: -------------------------------------------------------------------------------- 1 | const dgram = require('dgram'); 2 | const assert = require('assert'); 3 | const BaseClient = require('./BaseClient'); 4 | const RtpEngineError = require('./error'); 5 | const { COMMANDS } = require('./constants'); 6 | const debug = require('debug')('rtpengine:Client'); 7 | 8 | class Client extends BaseClient { 9 | 10 | constructor(...args) { 11 | super({type: 'udp', ...args}); 12 | 13 | this.socket = dgram.createSocket('udp4'); 14 | if (typeof args[args.length - 1] === 'function') { 15 | this.addListener('listening', args.pop()); 16 | } 17 | 18 | this.socket.on('message', this._onMessage.bind(this)); 19 | this.socket.on('error', this._onError.bind(this)); 20 | this.socket.on('listening', this._onListening.bind(this)); 21 | 22 | if (typeof args[0] === 'object') { 23 | const localPort = args[0].localPort || 0; 24 | if (!args[0].localAddress) { 25 | this.socket.bind(localPort); 26 | } 27 | else { 28 | this.socket.bind(localPort, args[0].localAddress); 29 | } 30 | if (args[0].timeout) this.timeout = args[0].timeout; 31 | this.rejectOnError = args[0].rejectOnError; 32 | } 33 | else { 34 | switch (args.length) { 35 | case 0 : 36 | this.socket.bind(); 37 | break; 38 | case 1: 39 | this.socket.bind(args[0]); 40 | break; 41 | case 2: 42 | this.socket.bind(args[0], args[1]); 43 | break; 44 | default: 45 | throw new RtpEngineError('invalid number of arguments to rtpengine-client constructor'); 46 | } 47 | } 48 | 49 | this.messages = new Map(); 50 | if (this.timeout) { 51 | this.timers = new Map(); 52 | } 53 | } 54 | 55 | _onListening() { 56 | const r = process.env.UDP_RECV_BUFSIZE || process.env.UDP_BUFSIZE; 57 | const s = process.env.UDP_SEND_BUFSIZE || process.env.UDP_BUFSIZE; 58 | 59 | try { 60 | if (r) { 61 | const recvBufSize = parseInt(r, 10); 62 | if (recvBufSize > 0) { 63 | this.socket.setRecvBufferSize(recvBufSize); 64 | } 65 | } 66 | if (s) { 67 | const sendBufSize = parseInt(s, 10); 68 | if (sendBufSize > 0) { 69 | this.socket.setSendBufferSize(sendBufSize); 70 | } 71 | } 72 | } catch (err) { 73 | console.log({err, r, s}, 'rtpengine-client: error setting udp buffer size'); 74 | } 75 | super._onListening(); 76 | } 77 | } 78 | 79 | // add commands 80 | COMMANDS.forEach((method) => { 81 | Client.prototype[method] = function(...args) { 82 | assert.ok(args.length, `must supply destination port and address in call to Client#${method}`); 83 | 84 | debug(args); 85 | let idx = 1; 86 | if (typeof args[0] === 'object') { 87 | assert(typeof args[0].port === 'number', `must supply 'port' in call to Client#${method}`); 88 | assert(typeof args[0].host === 'string', `must supply 'host' in call to Client#${method}`); 89 | this.remotePort = args[0].port; 90 | this.remoteHost = args[0].host; 91 | 92 | } 93 | else { 94 | assert(typeof args[0] === 'number', `must supply port in call to Client#${method}`); 95 | assert(typeof args[1] === 'string', `must supply host in call to Client#${method}`); 96 | this.remotePort = args[0]; 97 | this.remoteHost = args[1]; 98 | idx = 2; 99 | } 100 | 101 | const a = [method].concat(args.slice(idx)); 102 | debug(a); 103 | return this.send_internal(...[method].concat(args.slice(idx))); 104 | }; 105 | }); 106 | 107 | module.exports = Client; 108 | -------------------------------------------------------------------------------- /lib/TcpClient.js: -------------------------------------------------------------------------------- 1 | const BaseClient = require('./BaseClient'); 2 | const net = require('net'); 3 | const RtpEngineError = require('./error'); 4 | const { COMMANDS } = require('./constants'); 5 | const debug = require('debug')('rtpengine:baseClient'); 6 | 7 | 8 | const MAX_BUFLEN = 16384; 9 | 10 | class TcpClient extends BaseClient { 11 | 12 | constructor(...args) { 13 | super({type: 'tcp', ...args}); 14 | 15 | if (typeof args[0] === 'object') { 16 | if (args[0].timeout) this.timeout = args[0].timeout; 17 | this.hostport = args[0].hostport; 18 | } 19 | else { 20 | this.hostport = args[0]; 21 | } 22 | 23 | const arr = /^(.*):(\d+)$/.exec(this.hostport); 24 | if (!arr) throw new RtpEngineError(`rtpengine-client: invalid hostport for tcp connection: ${this.hostport}`); 25 | 26 | this.host = arr[1]; 27 | this.port = arr[2]; 28 | 29 | const socket = this.socket = new net.Socket(); 30 | socket.setKeepAlive(true) ; 31 | 32 | socket.on('connect', () => { 33 | this.connected = true; 34 | this.emit('connect'); 35 | }); 36 | socket.on('data', this._onData.bind(this)); 37 | socket.on('end', this._onEnd.bind(this)); 38 | socket.on('ready', this._onListening.bind(this)); 39 | socket.on('error', this._onError.bind(this)); 40 | 41 | debug(`connecting tcp client to ${this.host}:${this.port}`); 42 | socket.connect(this.port, this.host); 43 | } 44 | 45 | _onData(msg) { 46 | let res; 47 | this.buffer = !this.buffer ? msg : Buffer.concat([this.buffer, msg]); 48 | if (this.buffer.length > MAX_BUFLEN) { 49 | this.emit('error', new RtpEngineError(`malformed/unexpected message format ${this.buffer.slice(0, 64)}...`)); 50 | this.buffer = null; 51 | return; 52 | } 53 | try { 54 | console.log(`received ${msg.length} bytes`); 55 | res = BaseClient.decodeMessage(this.buffer); 56 | } catch (err) { 57 | return; 58 | } 59 | this._onParsedMessage(res); 60 | this.buffer = null; 61 | } 62 | } 63 | 64 | // add commands 65 | COMMANDS.forEach((method) => { 66 | TcpClient.prototype[method] = function(...args) { 67 | return this.send_internal(...[method].concat(args)); 68 | }; 69 | }); 70 | 71 | module.exports = TcpClient; 72 | -------------------------------------------------------------------------------- /lib/WsClient.js: -------------------------------------------------------------------------------- 1 | const WebSocket = require('ws'); 2 | const BaseClient = require('./BaseClient'); 3 | const { COMMANDS } = require('./constants'); 4 | 5 | class WsClient extends BaseClient { 6 | 7 | constructor(...args) { 8 | super({type: 'websocket', ...args}); 9 | this.connectionCount = 0; 10 | 11 | if (typeof args[0] === 'object') { 12 | if (args[0].timeout) this.timeout = args[0].timeout; 13 | this.url = args[0].url; 14 | } 15 | else { 16 | this.url = args[0]; 17 | } 18 | this._connect(this.url, 'ng.rtpengine.com'); 19 | } 20 | 21 | _connect(opts, protocol) { 22 | this.socket = new WebSocket(opts, protocol); 23 | this._attachHandlers(this.socket, opts, protocol); 24 | } 25 | 26 | _attachHandlers(socket, opts, protocol) { 27 | socket.on('message', this._onMessage.bind(this)); 28 | socket.on('error', this._onError.bind(this)); 29 | socket.on('open', () => { 30 | if (this.connectionCount++ === 0) this._onListening(); 31 | else this.emit('reconnected'); 32 | }); 33 | socket.on('close', (code) => { 34 | this.emit('close'); 35 | setTimeout(this._connect.bind(this, opts, protocol), 2000); 36 | }); 37 | } 38 | } 39 | 40 | // add commands 41 | COMMANDS.forEach((method) => { 42 | WsClient.prototype[method] = function(...args) { 43 | return this.send_internal(...[method].concat(args)); 44 | }; 45 | }); 46 | 47 | module.exports = WsClient; 48 | -------------------------------------------------------------------------------- /lib/constants.js: -------------------------------------------------------------------------------- 1 | const _commands = { 2 | 'answer': 'answer', 3 | 'delete': 'delete', 4 | 'list': 'list', 5 | 'offer': 'offer', 6 | 'ping': 'ping', 7 | 'query': 'query', 8 | 'startRecording': 'start recording', 9 | 'stopRecording': 'stop recording', 10 | 'blockDTMF': 'block DTMF', 11 | 'unblockDTMF': 'unblock DTMF', 12 | 'playDTMF': 'play DTMF', 13 | 'blockMedia': 'block media', 14 | 'unblockMedia': 'unblock media', 15 | 'silenceMedia': 'silence media', 16 | 'unsilenceMedia': 'unsilence media', 17 | 'startForwarding': 'start forwarding', 18 | 'stopForwarding': 'stop forwarding', 19 | 'playMedia': 'play media', 20 | 'stopMedia': 'stop media', 21 | 'statistics': 'statistics', 22 | 'publish': 'publish', 23 | 'subscribeRequest': 'subscribe request', 24 | 'subscribeAnswer': 'subscribe answer', 25 | 'unsubscribe': 'unsubscribe' 26 | }; 27 | 28 | const COMMANDS = Object.keys(_commands); 29 | 30 | const getRtpEngineNameForCommand = (name) => _commands[name]; 31 | 32 | module.exports = { 33 | COMMANDS, 34 | getRtpEngineNameForCommand 35 | }; 36 | -------------------------------------------------------------------------------- /lib/error.js: -------------------------------------------------------------------------------- 1 | class RtpEngineError extends Error { 2 | constructor(...args) { 3 | super(...args); 4 | Error.captureStackTrace(this, RtpEngineError); 5 | } 6 | } 7 | 8 | module.exports = RtpEngineError; 9 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "rtpengine-client", 3 | "version": "0.4.12", 4 | "description": "node client for rtpengine daemon", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "NODE_ENV=test node test/ ", 8 | "coverage": "./node_modules/.bin/nyc --reporter html --report-dir ./coverage npm run test", 9 | "jslint": "eslint lib" 10 | }, 11 | "author": "Dave Horton", 12 | "license": "MIT", 13 | "devDependencies": { 14 | "debug": "^4.3.1", 15 | "eslint": "^7.4.0", 16 | "eslint-plugin-promise": "^6.0.0", 17 | "sinon": "^9.0.2", 18 | "tape": "^5.6.1" 19 | }, 20 | "dependencies": { 21 | "bencode": "^2.0.3", 22 | "uuid-random": "^1.3.2", 23 | "ws": "^7.4.4" 24 | }, 25 | "keywords": [ 26 | "rtpengine" 27 | ], 28 | "repository": { 29 | "type": "git", 30 | "url": "https://github.com/davehorton/rtpengine-client" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /test/data/._nosplit.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drachtio/rtpengine-client/a00c2cb08c5f55bd3454c08d8a151747b39cbce2/test/data/._nosplit.txt -------------------------------------------------------------------------------- /test/data/combine.txt: -------------------------------------------------------------------------------- 1 | d10:statisticsd17:currentstatisticsd8:byteratei34282e9:errorratei0e10:packetratei200e15:sessionsforeigni0e11:sessionsowni2e13:sessionstotali2e15:transcodedmediai0eeeed10:statisticsd17:currentstatisticsd8:byteratei34282e9:errorratei0e10:packetratei200e15:sessionsforeigni0e11:sessionsowni2e13:sessionstotali2e15:transcodedmediai0eeee -------------------------------------------------------------------------------- /test/data/nosplit.txt: -------------------------------------------------------------------------------- 1 | d10:statisticsd17:controlstatisticsd7:proxiesld11:answercounti0e14:answerduration8:0.00000012:blkdtmfcounti0e15:blkdtmfduration8:0.00000013:blkmediacounti0e16:blkmediaduration8:0.00000011:deletecounti0e14:deleteduration8:0.00000010:errorcounti0e9:listcounti0e12:listduration8:0.00000010:offercounti0e13:offerduration8:0.0000009:pingcounti0e12:pingduration8:0.00000013:playdtmfcounti0e16:playdtmfduration8:0.00000014:playmediacounti0e17:playmediaduration8:0.0000005:proxy9:127.0.0.110:querycounti0e13:queryduration8:0.00000013:startfwdcounti0e16:startfwdduration8:0.00000013:startreccounti0e16:startrecduration8:0.00000010:statscounti1e13:statsduration8:0.00035612:stopfwdcounti0e15:stopfwdduration8:0.00000014:stopmediacounti0e17:stopmediaduration8:0.00000012:stopreccounti0e15:stoprecduration8:0.00000014:unblkdtmfcounti0e17:unblkdtmfduration8:0.00000015:unblkmediacounti0e18:unblkmediaduration8:0.000000ed11:answercounti318e14:answerduration8:0.04118512:blkdtmfcounti0e15:blkdtmfduration8:0.00000013:blkmediacounti0e16:blkmediaduration8:0.00000011:deletecounti360e14:deleteduration8:0.02628310:errorcounti0e9:listcounti0e12:listduration8:0.00000010:offercounti381e13:offerduration8:0.1236819:pingcounti0e12:pingduration8:0.00000013:playdtmfcounti0e16:playdtmfduration8:0.00000014:playmediacounti0e17:playmediaduration8:0.0000005:proxy13:172.32.32.12910:querycounti0e13:queryduration8:0.00000013:startfwdcounti0e16:startfwdduration8:0.00000013:startreccounti0e16:startrecduration8:0.00000010:statscounti80046e13:statsduration9:26.04823312:stopfwdcounti0e15:stopfwdduration8:0.00000014:stopmediacounti0e17:stopmediaduration8:0.00000012:stopreccounti0e15:stoprecduration8:0.00000014:unblkdtmfcounti0e17:unblkdtmfduration8:0.00000015:unblkmediacounti0e18:unblkmediaduration8:0.000000ed11:answercounti388e14:answerduration8:0.05103612:blkdtmfcounti0e15:blkdtmfduration8:0.00000013:blkmediacounti0e16:blkmediaduration8:0.00000011:deletecounti474e14:deleteduration8:0.03314610:errorcounti0e9:listcounti0e12:listduration8:0.00000010:offercounti496e13:offerduration8:0.1646159:pingcounti0e12:pingduration8:0.00000013:playdtmfcounti0e16:playdtmfduration8:0.00000014:playmediacounti0e17:playmediaduration8:0.0000005:proxy12:172.32.32.7910:querycounti0e13:queryduration8:0.00000013:startfwdcounti0e16:startfwdduration8:0.00000013:startreccounti0e16:startrecduration8:0.00000010:statscounti80046e13:statsduration9:26.19857412:stopfwdcounti0e15:stopfwdduration8:0.00000014:stopmediacounti0e17:stopmediaduration8:0.00000012:stopreccounti0e15:stoprecduration8:0.00000014:unblkdtmfcounti0e17:unblkdtmfduration8:0.00000015:unblkmediacounti0e18:unblkmediaduration8:0.000000ee16:totalanswercounti706e17:totalblkdtmfcounti0e18:totalblkmediacounti0e16:totaldeletecounti834e14:totallistcounti0e15:totaloffercounti877e14:totalpingcounti0e18:totalplaydtmfcounti0e19:totalplaymediacounti0e15:totalquerycounti0e18:totalstartfwdcounti0e18:totalstartreccounti0e15:totalstatscounti160093e17:totalstopfwdcounti0e19:totalstopmediacounti0e17:totalstopreccounti0e19:totalunblkdtmfcounti0e20:totalunblkmediacounti0ee17:currentstatisticsd8:byteratei34282e9:errorratei0e10:packetratei200e15:sessionsforeigni0e11:sessionsowni2e13:sessionstotali2e15:transcodedmediai0ee10:interfacesld7:address13:172.32.32.2154:name7:private5:portsd4:freei19993e4:lasti44978e3:maxi60000e3:mini40000e6:totalsi20001e4:usedi8e8:used_pct4:0.04eed7:address13:172.32.32.2154:name6:public5:portsd4:freei19993e4:lasti44978e3:maxi60000e3:mini40000e6:totalsi20001e4:usedi8e8:used_pct4:0.04eee18:intervalstatisticsd14:avganswerdelay8:0.00000020:avganswerrequestratei0e14:avgdeletedelay8:0.00000020:avgdeleterequestratei0e13:avgofferdelay8:0.00000019:avgofferrequestratei0e14:maxanswerdelay8:0.00000020:maxanswerrequestratei0e14:maxdeletedelay8:0.00000020:maxdeleterequestratei0e18:maxmanagedsessionsi0e13:maxofferdelay8:0.00000019:maxofferrequestratei0e14:minanswerdelay8:0.00000020:minanswerrequestratei0e14:mindeletedelay8:0.00000020:mindeleterequestratei0e18:minmanagedsessionsi0e13:minofferdelay8:0.00000019:minofferrequestratei0e18:totalcallsduration8:0.000000e15:totalstatisticsd15:avgcallduration9:23.11061720:finaltimeoutsessionsi0e24:forcedterminatedsessionsi0e15:managedsessionsi835e20:offertimeoutsessionsi0e13:onewaystreamsi152e25:regularterminatedsessionsi832e16:rejectedsessionsi0e12:relayedbytesi117276710e19:relayedpacketerrorsi5631e14:relayedpacketsi685615e21:silenttimeoutsessionsi0e15:timeoutsessionsi3e6:uptime6:96058914:zerowaystreamsi244ee11:transcodersleee -------------------------------------------------------------------------------- /test/data/split-part1.txt: -------------------------------------------------------------------------------- 1 | d10:statisticsd17:controlstatisticsd7:proxiesld11:answercounti0e14:answerduration8:0.00000012:blkdtmfcounti0e15:blkdtmfduration8:0.00000013:blkmediacounti0e16:blkmediaduration8:0.00000011:deletecounti0e14:deleteduration8:0.00000010:errorcounti0e9:listcounti0e12:listduration8:0.00000010:offercounti0e13:offerduration8:0.0000009:pingcounti0e12:pingduration8:0.00000013:playdtmfcounti0e16:playdtmfduration8:0.00000014:playmediacounti0e17:playmediaduration8:0.0000005:proxy9:127.0.0.110:querycounti0e13:queryduration8:0.00000013:startfwdcounti0e16:startfwdduration8:0.00000013:startreccounti0e16:startrecduration8:0.00000010:statscounti1e13:statsduration8:0.00035612:stopfwdcounti0e15:stopfwdduration8:0.00000014:stopmediacounti0e17:stopmediaduration8:0.00000012:stopreccounti0e15:stoprecduration8:0.00000014:unblkdtmfcounti0e17:unblkdtmfdura -------------------------------------------------------------------------------- /test/data/split-part2.txt: -------------------------------------------------------------------------------- 1 | tion8:0.00000015:unblkmediacounti0e18:unblkmediaduration8:0.000000ed11:answercounti318e14:answerduration8:0.04118512:blkdtmfcounti0e15:blkdtmfduration8:0.00000013:blkmediacounti0e16:blkmediaduration8:0.00000011:deletecounti360e14:deleteduration8:0.02628310:errorcounti0e9:listcounti0e12:listduration8:0.00000010:offercounti381e13:offerduration8:0.1236819:pingcounti0e12:pingduration8:0.00000013:playdtmfcounti0e16:playdtmfduration8:0.00000014:playmediacounti0e17:playmediaduration8:0.0000005:proxy13:172.32.32.12910:querycounti0e13:queryduration8:0.00000013:startfwdcounti0e16:startfwdduration8:0.00000013:startreccounti0e16:startrecduration8:0.00000010:statscounti80046e13:statsduration9:26.04823312:stopfwdcounti0e15:stopfwdduration8:0.00000014:stopmediacounti0e17:stopmediaduration8:0.00000012:stopreccounti0e15:stoprecduration8:0.00000014:unblkdtmfcounti0e17:unblkdtmfduration8:0.00000015:unblkmediacounti0e18:unblkmediaduration8:0.000000ed11:answercounti388e14:answerduration8:0.05103612:blkdtmfcounti0e15:blkdtmfduration8:0.00000013:blkmediacounti0e16:blkmediaduration8:0.00000011:deletecounti474e14:deleteduration8:0.03314610:errorcounti0e9:listcounti0e12:listduration8:0.00000010:offercounti496e13:offerduration8:0.1646159:pingcounti0e12:pingduration8:0.00000013:playdtmfcounti0e16:playdtmfduration8:0.00000014:playmediacounti0e17:playmediaduration8:0.0000005:proxy12:172.32.32.7910:querycounti0e13:queryduration8:0.00000013:startfwdcounti0e16:startfwdduration8:0.00000013:startreccounti0e16:startrecduration8:0.00000010:statscounti80046e13:statsduration9:26.19857412:stopfwdcounti0e15:stopfwdduration8:0.00000014:stopmediacounti0e17:stopmediaduration8:0.00000012:stopreccounti0e15:stoprecduration8:0.00000014:unblkdtmfcounti0e17:unblkdtmfduration8:0.00000015:unblkmediacounti0e18:unblkmediaduration8:0.000000ee16:totalanswercounti706e17:totalblkdtmfcounti0e18:totalblkmediacounti0e16:totaldeletecounti834e14:totallistcounti0e15:totaloffercounti877e14:totalpingcounti0e18:totalplaydtmfcounti0e19:totalplaymediacounti0e15:totalquerycounti0e18:totalstartfwdcounti0e18:totalstartreccounti0e15:totalstatscounti160093e17:totalstopfwdcounti0e19:totalstopmediacounti0e17:totalstopreccounti0e19:totalunblkdtmfcounti0e20:totalunblkmediacounti0ee17:currentstatisticsd8:byteratei34282e9:errorratei0e10:packetratei200e15:sessionsforeigni0e11:sessionsowni2e13:sessionstotali2e15:transcodedmediai0ee10:interfacesld7:address13:172.32.32.2154:name7:private5:portsd4:freei19993e4:lasti44978e3:maxi60000e3:mini40000e6:totalsi20001e4:usedi8e8:used_pct4:0.04eed7:address13:172.32.32.2154:name6:public5:portsd4:freei19993e4:lasti44978e3:maxi60000e3:mini40000e6:totalsi20001e4:usedi8e8:used_pct4:0.04eee18:intervalstatisticsd14:avganswerdelay8:0.00000020:avganswerrequestratei0e14:avgdeletedelay8:0.00000020:avgdeleterequestratei0e13:avgofferdelay8:0.00000019:avgofferrequestratei0e14:maxanswerdelay8:0.00000020:maxanswerrequestratei0e14:maxdeletedelay8:0.00000020:maxdeleterequestratei0e18:maxmanagedsessionsi0e13:maxofferdelay8:0.00000019:maxofferrequestratei0e14:minanswerdelay8:0.00000020:minanswerrequestratei0e14:mindeletedelay8:0.00000020:mindeleterequestratei0e18:minmanagedsessionsi0e13:minofferdelay8:0.00000019:minofferrequestratei0e18:totalcallsduration8:0.000000e15:totalstatisticsd15:avgcallduration9:23.11061720:finaltimeoutsessionsi0e24:forcedterminatedsessionsi0e15:managedsessionsi835e20:offertimeoutsessionsi0e13:onewaystreamsi152e25:regularterminatedsessionsi832e16:rejectedsessionsi0e12:relayedbytesi117276710e19:relayedpacketerrorsi5631e14:relayedpacketsi685615e21:silenttimeoutsessionsi0e15:timeoutsessionsi3e6:uptime6:96058914:zerowaystreamsi244ee11:transcodersleee -------------------------------------------------------------------------------- /test/fakeserver.js: -------------------------------------------------------------------------------- 1 | const net = require('net'); 2 | const assert = require('assert'); 3 | const fs = require('fs'); 4 | const data = { 5 | nosplit: fs.readFileSync(`${__dirname}/data/nosplit.txt`), 6 | split1: fs.readFileSync(`${__dirname}/data/split-part1.txt`), 7 | split2: fs.readFileSync(`${__dirname}/data/split-part2.txt`), 8 | nonmessage: 'this is not a message', 9 | sample: 'd3:bar4:spam3:fooi42ee' 10 | }; 11 | const debug = require('debug')('rtpengine:test'); 12 | 13 | const waitFor = (ms) => new Promise((resolve) => setTimeout(resolve, ms)); 14 | 15 | class FakeServer { 16 | constructor({port, scenario}) { 17 | assert.ok(['nosplit', 'split', 'combine', 'nonmessage'].includes(scenario), `invalid scenario: ${scenario}`); 18 | this.scenario = scenario; 19 | this.server = net.createServer(); 20 | this.server.listen(port); 21 | 22 | this.server.on('connection', this._onConnection.bind(this)); 23 | this.server.on('error', this._onError.bind(this)); 24 | 25 | debug(`FakeServer - listening on port ${port}`); 26 | 27 | } 28 | 29 | close() { 30 | debug('FakeServer - close'); 31 | this.server.close(); 32 | } 33 | 34 | _onConnection(socket) { 35 | socket.on('error', (err) => {}); 36 | socket.on('data', (d) => { 37 | const arr = /^(.*)\s/.exec(d.toString()); 38 | const msgId = arr[1]; 39 | debug({data: d.toString()}, `got data with msg id ${msgId}`); 40 | switch (this.scenario) { 41 | case 'nosplit': 42 | socket.write(`${msgId} ${data.nosplit}`); 43 | break; 44 | case 'split': 45 | socket.write(`${msgId} ${data.split1}`, 'utf8'); 46 | waitFor(500) 47 | .then(() => { 48 | socket.write(data.split2, 'utf8'); 49 | }) 50 | break; 51 | case 'nonmessage': 52 | for (let i = 0; i < 10000; i++) { 53 | if (!socket.destroyed) socket.write(`${msgId} this is not bencoded `, 'utf8'); 54 | } 55 | break; 56 | default: 57 | socket.write(`${msgId} ${data.combine}`, 'utf8'); 58 | break; 59 | } 60 | }); 61 | } 62 | 63 | _onError(err) { 64 | console.error('Fakserver error', err); 65 | throw(err); 66 | } 67 | } 68 | 69 | module.exports = FakeServer; 70 | -------------------------------------------------------------------------------- /test/index.js: -------------------------------------------------------------------------------- 1 | require('./unit'); 2 | -------------------------------------------------------------------------------- /test/unit.js: -------------------------------------------------------------------------------- 1 | const test = require('tape').test; 2 | const {Client, TcpClient} = require('..'); 3 | const RtpEngineError = require('..').RtpEngineError; 4 | const sinon = require('sinon'); 5 | const decode = Client.decodeMessage; 6 | const encode = Client.encodeMessage; 7 | const debug = require('debug')('rtpengine:test'); 8 | const fs = require('fs'); 9 | const data = { 10 | nosplit: fs.readFileSync(`${__dirname}/data/nosplit.txt`), 11 | split1: fs.readFileSync(`${__dirname}/data/split-part1.txt`), 12 | split2: fs.readFileSync(`${__dirname}/data/split-part2.txt`), 13 | nonmessage: 'this is not a message', 14 | sample: 'd3:bar4:spam3:fooi42ee' 15 | }; 16 | 17 | const statResponse = Buffer.from(fs.readFileSync(`${__dirname}/data/nosplit.txt`)); 18 | 19 | const roundTripTime = (startAt) => { 20 | const diff = process.hrtime(startAt); 21 | const time = diff[0] * 1e3 + diff[1] * 1e-6; 22 | return time.toFixed(0); 23 | }; 24 | 25 | function fakeRtpEngine(client, message, port, host, callback) { 26 | const obj = decode(message); 27 | 28 | callback(null); 29 | 30 | switch (obj.data.command) { 31 | case 'ping': 32 | this.emit('message', encode(obj.id, {result: 'pong'})); 33 | break; 34 | case 'statistics': 35 | this.emit('message', Buffer.from([obj.id, statResponse].join(' '))); 36 | break; 37 | } 38 | } 39 | function fakeRtpEngineFail(client, message, port, host, callback) { 40 | callback(new Error('error sending')); 41 | } 42 | 43 | function fakeRtpEngineFail2(client, message, port, host, callback) { 44 | setImmediate(() => { this.emit('error', 'unexpected error of some kind'); }); 45 | } 46 | 47 | function fakeRtpEngineFail3(client, message, port, host, callback) { 48 | setImmediate(() => { this.emit('message', 'unparseable message'); }); 49 | } 50 | function fakeRtpEngineFail4(client, message, port, host, callback) { 51 | setImmediate(() => { 52 | const obj = decode(message); 53 | client.messages.delete(obj.id); 54 | 55 | callback(null); 56 | 57 | switch (obj.data.command) { 58 | case 'ping': 59 | this.emit('message', encode(obj.id, {result: 'pong'})); 60 | break; 61 | } 62 | }); 63 | } 64 | function fakeRtpEngineFail5(client, message, port, host, callback) { 65 | debug('got ping message'); 66 | setImmediate(() => { 67 | callback(null); 68 | }); 69 | } 70 | 71 | test('new Client()', (t) => { 72 | t.plan(1); 73 | let client; 74 | t.doesNotThrow(() => { client = new Client(); }); 75 | client.close(); 76 | }); 77 | 78 | test('new Client(port)', (t) => { 79 | t.plan(1); 80 | let client; 81 | t.doesNotThrow(() => { client = new Client(6066); }); 82 | client.close(); 83 | }); 84 | 85 | test('new Client(port, address)', (t) => { 86 | t.plan(1); 87 | let client; 88 | t.doesNotThrow(() => { client = new Client(6066, '127.0.0.1'); }); 89 | client.close(); 90 | }); 91 | 92 | test('new Client(obj)', (t) => { 93 | t.plan(1); 94 | let client; 95 | t.doesNotThrow(() => { client = new Client({localPort: 9099, localAddress: '127.0.0.1'}); }); 96 | client.close(); 97 | }); 98 | 99 | test('new Client({}})', (t) => { 100 | t.plan(1); 101 | let client; 102 | t.doesNotThrow(() => { client = new Client({}); }); 103 | client.close(); 104 | }); 105 | 106 | test('new Client(p1, p2, p3)', (t) => { 107 | t.plan(1); 108 | t.throws(() => { new Client(9099, '127.0.0.1', 'foobar'); }); 109 | }); 110 | 111 | test('new Client(callback)', (t) => { 112 | t.plan(1); 113 | const client = new Client(() => { 114 | t.pass('listening event emitted, if callback supplied'); 115 | client.close(); 116 | }); 117 | }); 118 | 119 | test('new Client(port, callback)', (t) => { 120 | t.plan(1); 121 | const client = new Client(6060, () => { 122 | t.pass('listening event emitted, if callback supplied'); 123 | client.close(); 124 | }); 125 | }); 126 | 127 | test('new Client(obj, callback)', (t) => { 128 | t.plan(1); 129 | const client = new Client({localPort: 9099, localAddress: '127.0.0.1'}, () => { 130 | t.pass('listening event emitted, if callback supplied'); 131 | client.close(); 132 | }); 133 | }); 134 | 135 | test('ping({port, host})', (t) => { 136 | t.plan(1); 137 | const client = new Client(); 138 | sinon.stub(client.socket, 'send') 139 | .callsFake(fakeRtpEngine.bind(client.socket, client)); 140 | 141 | client.ping({port: 22222, host: '35.195.250.243'}) 142 | .then((res) => { 143 | t.equal(res.result, 'pong', 'received \'pong\''); 144 | client.close(); 145 | }) 146 | .catch((err) => { 147 | client.close(); 148 | t.fail(err); 149 | }); 150 | }); 151 | 152 | test('ping(port, host)', (t) => { 153 | t.plan(1); 154 | const client = new Client(); 155 | sinon.stub(client.socket, 'send') 156 | .callsFake(fakeRtpEngine.bind(client.socket, client)); 157 | 158 | client.ping(22222, '35.195.250.243') 159 | .then((res) => { 160 | t.equal(res.result, 'pong', 'received \'pong\''); 161 | client.close(); 162 | }) 163 | .catch((err) => { 164 | client.close(); 165 | t.fail(err); 166 | }); 167 | }); 168 | 169 | test('ping using callback', (t) => { 170 | t.plan(1); 171 | const client = new Client(); 172 | sinon.stub(client.socket, 'send') 173 | .callsFake(fakeRtpEngine.bind(client.socket, client)); 174 | 175 | client.ping(22222, '35.195.250.243', (err, res) => { 176 | if (err) { 177 | client.close(); 178 | return t.fail(err); 179 | } 180 | t.equal(res.result, 'pong', 'received \'pong\' when sending using callback'); 181 | client.close(); 182 | }); 183 | }); 184 | 185 | test('error sending', (t) => { 186 | t.plan(1); 187 | const client = new Client(); 188 | sinon.stub(client.socket, 'send') 189 | .callsFake(fakeRtpEngineFail.bind(client.socket, client)); 190 | 191 | client.ping(22222, '35.195.250.243') 192 | .then((res) => { 193 | t.fail('expected send failure'); 194 | client.close(); 195 | }) 196 | .catch((err) => { 197 | client.close(); 198 | t.pass('rejects Promise when send fails'); 199 | }); 200 | }); 201 | 202 | test('socket error', (t) => { 203 | t.plan(1); 204 | const client = new Client(); 205 | sinon.stub(client.socket, 'send') 206 | .callsFake(fakeRtpEngineFail2.bind(client.socket, client)); 207 | 208 | client.ping(22222, '35.195.250.243'); 209 | client.on('error', (err) => { 210 | t.pass('error is emitted by client'); 211 | client.close(); 212 | }); 213 | }); 214 | 215 | test('message parsing error', (t) => { 216 | t.plan(1); 217 | const client = new Client(); 218 | sinon.stub(client.socket, 'send') 219 | .callsFake(fakeRtpEngineFail3.bind(client.socket, client)); 220 | 221 | client.ping(22222, '35.195.250.243'); 222 | client.on('error', (err) => { 223 | t.ok(err instanceof RtpEngineError, 'RtpEngineError emitted by client'); 224 | client.close(); 225 | }); 226 | }); 227 | 228 | test('timeout', (t) => { 229 | t.plan(1); 230 | const client = new Client({timeout: 10}); 231 | sinon.stub(client.socket, 'send') 232 | .callsFake(fakeRtpEngineFail5.bind(client.socket, client)); 233 | 234 | client.ping({port: 22222, host: '35.195.250.243'}) 235 | .then((res) => { 236 | t.fail('expected send failure'); 237 | client.close(); 238 | }) 239 | .catch((err) => { 240 | client.close(); 241 | t.equals(err.message, 'rtpengine timeout host:35.195.250.243 port:22222', 'rtpengine timeout is emitted by client'); 242 | }); 243 | }); 244 | 245 | test.skip('message correlation error', (t) => { 246 | t.plan(1); 247 | const client = new Client(); 248 | sinon.stub(client.socket, 'send') 249 | .callsFake(fakeRtpEngineFail4.bind(client.socket, client)); 250 | 251 | client.ping(22222, '35.195.250.243'); 252 | client.on('error', (err) => { 253 | t.pass(); 254 | client.close(); 255 | }); 256 | }); 257 | 258 | 259 | // tcp tests 260 | const FakeRtpEngine = require('./fakeserver'); 261 | test('tcp - single message', (t) => { 262 | t.plan(1); 263 | const server = new FakeRtpEngine({port: 3457, scenario: 'nosplit'}); 264 | const client = new TcpClient('localhost:3457'); 265 | client.on('connect', () => { 266 | client.statistics() 267 | .then((res) => { 268 | t.pass(); 269 | client.close(); 270 | server.close(); 271 | }); 272 | }); 273 | }); 274 | 275 | test('tcp - message broken into two frames', (t) => { 276 | t.plan(1); 277 | const server = new FakeRtpEngine({port: 3457, scenario: 'split'}); 278 | const client = new TcpClient('localhost:3457'); 279 | client.on('connect', () => { 280 | client.statistics() 281 | .then((res) => { 282 | t.pass(); 283 | client.close(); 284 | server.close(); 285 | }); 286 | }); 287 | }); 288 | 289 | test('tcp - not a message', (t) => { 290 | t.plan(1); 291 | const server = new FakeRtpEngine({port: 3457, scenario: 'nonmessage'}); 292 | const client = new TcpClient('localhost:3457'); 293 | client.on('connect', () => { 294 | client.statistics() 295 | .then((res) => { 296 | t.pass(); 297 | client.close(); 298 | server.close(); 299 | }); 300 | }); 301 | client.on('error', (err) => { 302 | console.log(`msg: ${err.message}`); 303 | t.pass(); 304 | client.close(); 305 | server.close(); 306 | }); 307 | }); 308 | 309 | test('benchmark', (t) => { 310 | const total = 50000; 311 | let responses = 0; 312 | t.plan(1); 313 | const client = new Client(); 314 | sinon.stub(client.socket, 'send') 315 | .callsFake(fakeRtpEngine.bind(client.socket, client)); 316 | 317 | console.log(`starting benchmark: ${total} statistics requests...`); 318 | const startAt = process.hrtime(); 319 | for (let i = 0; i < total; i++) { 320 | const even = i % 2 === 0; 321 | client[even ? 'ping': 'statistics'](22222, '35.195.250.243') 322 | .then((res) => { 323 | if (++responses === total) { 324 | const rtt = roundTripTime(startAt); 325 | t.pass(`time to send/receive ${total} requests: ${rtt}ms`); 326 | client.close(); 327 | } 328 | //else console.log({res}, `responses: ${res}`); 329 | }) 330 | .catch((err) => { 331 | client.close(); 332 | t.fail(err); 333 | }); 334 | } 335 | }); 336 | -------------------------------------------------------------------------------- /yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | "@babel/code-frame@7.12.11": 6 | "integrity" "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==" 7 | "resolved" "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz" 8 | "version" "7.12.11" 9 | dependencies: 10 | "@babel/highlight" "^7.10.4" 11 | 12 | "@babel/helper-validator-identifier@^7.12.11": 13 | "integrity" "sha512-np/lG3uARFybkoHokJUmf1QfEvRVCPbmQeUQpKow5cQ3xWrV9i3rUHodKDJPQfTVX61qKi+UdYk8kik84n7XOw==" 14 | "resolved" "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.12.11.tgz" 15 | "version" "7.12.11" 16 | 17 | "@babel/highlight@^7.10.4": 18 | "integrity" "sha512-5aPpe5XQPzflQrFwL1/QoeHkP2MsA4JCntcXHRhEsdsfPVkvPi2w7Qix4iV7t5S/oC9OodGrggd8aco1g3SZFg==" 19 | "resolved" "https://registry.npmjs.org/@babel/highlight/-/highlight-7.13.10.tgz" 20 | "version" "7.13.10" 21 | dependencies: 22 | "@babel/helper-validator-identifier" "^7.12.11" 23 | "chalk" "^2.0.0" 24 | "js-tokens" "^4.0.0" 25 | 26 | "@eslint/eslintrc@^0.4.0": 27 | "integrity" "sha512-2ZPCc+uNbjV5ERJr+aKSPRwZgKd2z11x0EgLvb1PURmUrn9QNRXFqje0Ldq454PfAVyaJYyrDvvIKSFP4NnBog==" 28 | "resolved" "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.0.tgz" 29 | "version" "0.4.0" 30 | dependencies: 31 | "ajv" "^6.12.4" 32 | "debug" "^4.1.1" 33 | "espree" "^7.3.0" 34 | "globals" "^12.1.0" 35 | "ignore" "^4.0.6" 36 | "import-fresh" "^3.2.1" 37 | "js-yaml" "^3.13.1" 38 | "minimatch" "^3.0.4" 39 | "strip-json-comments" "^3.1.1" 40 | 41 | "@sinonjs/commons@^1.6.0", "@sinonjs/commons@^1.7.0", "@sinonjs/commons@^1.8.1": 42 | "integrity" "sha512-sruwd86RJHdsVf/AtBoijDmUqJp3B6hF/DGC23C+JaegnDHaZyewCjoVGTdg3J0uz3Zs7NnIT05OBOmML72lQw==" 43 | "resolved" "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.2.tgz" 44 | "version" "1.8.2" 45 | dependencies: 46 | "type-detect" "4.0.8" 47 | 48 | "@sinonjs/fake-timers@^6.0.0", "@sinonjs/fake-timers@^6.0.1": 49 | "integrity" "sha512-MZPUxrmFubI36XS1DI3qmI0YdN1gks62JtFZvxR67ljjSNCeK6U08Zx4msEWOXuofgqUt6zPHSi1H9fbjR/NRA==" 50 | "resolved" "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-6.0.1.tgz" 51 | "version" "6.0.1" 52 | dependencies: 53 | "@sinonjs/commons" "^1.7.0" 54 | 55 | "@sinonjs/samsam@^5.3.1": 56 | "integrity" "sha512-1Hc0b1TtyfBu8ixF/tpfSHTVWKwCBLY4QJbkgnE7HcwyvT2xArDxb4K7dMgqRm3szI+LJbzmW/s4xxEhv6hwDg==" 57 | "resolved" "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-5.3.1.tgz" 58 | "version" "5.3.1" 59 | dependencies: 60 | "@sinonjs/commons" "^1.6.0" 61 | "lodash.get" "^4.4.2" 62 | "type-detect" "^4.0.8" 63 | 64 | "@sinonjs/text-encoding@^0.7.1": 65 | "integrity" "sha512-+iTbntw2IZPb/anVDbypzfQa+ay64MW0Zo8aJ8gZPWMMK6/OubMVb6lUPMagqjOPnmtauXnFCACVl3O7ogjeqQ==" 66 | "resolved" "https://registry.npmjs.org/@sinonjs/text-encoding/-/text-encoding-0.7.1.tgz" 67 | "version" "0.7.1" 68 | 69 | "acorn-jsx@^5.3.1": 70 | "integrity" "sha512-K0Ptm/47OKfQRpNQ2J/oIN/3QYiK6FwW+eJbILhsdxh2WTLdl+30o8aGdTbm5JbffpFFAg/g+zi1E+jvJha5ng==" 71 | "resolved" "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.1.tgz" 72 | "version" "5.3.1" 73 | 74 | "acorn@^6.0.0 || ^7.0.0 || ^8.0.0", "acorn@^7.4.0": 75 | "integrity" "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==" 76 | "resolved" "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz" 77 | "version" "7.4.1" 78 | 79 | "ajv@^6.10.0", "ajv@^6.12.4": 80 | "integrity" "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==" 81 | "resolved" "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz" 82 | "version" "6.12.6" 83 | dependencies: 84 | "fast-deep-equal" "^3.1.1" 85 | "fast-json-stable-stringify" "^2.0.0" 86 | "json-schema-traverse" "^0.4.1" 87 | "uri-js" "^4.2.2" 88 | 89 | "ajv@^7.0.2": 90 | "integrity" "sha512-+nu0HDv7kNSOua9apAVc979qd932rrZeb3WOvoiD31A/p1mIE5/9bN2027pE2rOPYEdS3UHzsvof4hY+lM9/WQ==" 91 | "resolved" "https://registry.npmjs.org/ajv/-/ajv-7.2.1.tgz" 92 | "version" "7.2.1" 93 | dependencies: 94 | "fast-deep-equal" "^3.1.1" 95 | "json-schema-traverse" "^1.0.0" 96 | "require-from-string" "^2.0.2" 97 | "uri-js" "^4.2.2" 98 | 99 | "ansi-colors@^4.1.1": 100 | "integrity" "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==" 101 | "resolved" "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz" 102 | "version" "4.1.1" 103 | 104 | "ansi-regex@^5.0.0": 105 | "integrity" "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==" 106 | "resolved" "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz" 107 | "version" "5.0.1" 108 | 109 | "ansi-styles@^3.2.1": 110 | "integrity" "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==" 111 | "resolved" "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz" 112 | "version" "3.2.1" 113 | dependencies: 114 | "color-convert" "^1.9.0" 115 | 116 | "ansi-styles@^4.0.0", "ansi-styles@^4.1.0": 117 | "integrity" "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==" 118 | "resolved" "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz" 119 | "version" "4.3.0" 120 | dependencies: 121 | "color-convert" "^2.0.1" 122 | 123 | "argparse@^1.0.7": 124 | "integrity" "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==" 125 | "resolved" "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz" 126 | "version" "1.0.10" 127 | dependencies: 128 | "sprintf-js" "~1.0.2" 129 | 130 | "array-filter@^1.0.0": 131 | "integrity" "sha1-uveeYubvTCpMC4MSMtr/7CUfnYM=" 132 | "resolved" "https://registry.npmjs.org/array-filter/-/array-filter-1.0.0.tgz" 133 | "version" "1.0.0" 134 | 135 | "array.prototype.every@^1.1.3": 136 | "integrity" "sha512-vWnriJI//SOMOWtXbU/VXhJ/InfnNHPF6BLKn5WfY8xXy+NWql0fUy20GO3sdqBhCAO+qw8S/E5nJiZX+QFdCA==" 137 | "resolved" "https://registry.npmjs.org/array.prototype.every/-/array.prototype.every-1.1.3.tgz" 138 | "version" "1.1.3" 139 | dependencies: 140 | "call-bind" "^1.0.2" 141 | "define-properties" "^1.1.3" 142 | "es-abstract" "^1.19.0" 143 | "is-string" "^1.0.7" 144 | 145 | "astral-regex@^2.0.0": 146 | "integrity" "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==" 147 | "resolved" "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz" 148 | "version" "2.0.0" 149 | 150 | "available-typed-arrays@^1.0.2": 151 | "integrity" "sha512-XWX3OX8Onv97LMk/ftVyBibpGwY5a8SmuxZPzeOxqmuEqUCOM9ZE+uIaD1VNJ5QnvU2UQusvmKbuM1FR8QWGfQ==" 152 | "resolved" "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.2.tgz" 153 | "version" "1.0.2" 154 | dependencies: 155 | "array-filter" "^1.0.0" 156 | 157 | "balanced-match@^1.0.0": 158 | "integrity" "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" 159 | "resolved" "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz" 160 | "version" "1.0.0" 161 | 162 | "bencode@^2.0.3": 163 | "integrity" "sha512-D/vrAD4dLVX23NalHwb8dSvsUsxeRPO8Y7ToKA015JQYq69MLDOMkC0uGZYA/MPpltLO8rt8eqFC2j8DxjTZ/w==" 164 | "resolved" "https://registry.npmjs.org/bencode/-/bencode-2.0.3.tgz" 165 | "version" "2.0.3" 166 | 167 | "brace-expansion@^1.1.7": 168 | "integrity" "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==" 169 | "resolved" "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz" 170 | "version" "1.1.11" 171 | dependencies: 172 | "balanced-match" "^1.0.0" 173 | "concat-map" "0.0.1" 174 | 175 | "call-bind@^1.0.0", "call-bind@^1.0.2": 176 | "integrity" "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==" 177 | "resolved" "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz" 178 | "version" "1.0.2" 179 | dependencies: 180 | "function-bind" "^1.1.1" 181 | "get-intrinsic" "^1.0.2" 182 | 183 | "callsites@^3.0.0": 184 | "integrity" "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==" 185 | "resolved" "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz" 186 | "version" "3.1.0" 187 | 188 | "chalk@^2.0.0": 189 | "integrity" "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==" 190 | "resolved" "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz" 191 | "version" "2.4.2" 192 | dependencies: 193 | "ansi-styles" "^3.2.1" 194 | "escape-string-regexp" "^1.0.5" 195 | "supports-color" "^5.3.0" 196 | 197 | "chalk@^4.0.0": 198 | "integrity" "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==" 199 | "resolved" "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz" 200 | "version" "4.1.0" 201 | dependencies: 202 | "ansi-styles" "^4.1.0" 203 | "supports-color" "^7.1.0" 204 | 205 | "color-convert@^1.9.0": 206 | "integrity" "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==" 207 | "resolved" "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz" 208 | "version" "1.9.3" 209 | dependencies: 210 | "color-name" "1.1.3" 211 | 212 | "color-convert@^2.0.1": 213 | "integrity" "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==" 214 | "resolved" "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz" 215 | "version" "2.0.1" 216 | dependencies: 217 | "color-name" "~1.1.4" 218 | 219 | "color-name@~1.1.4": 220 | "integrity" "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" 221 | "resolved" "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz" 222 | "version" "1.1.4" 223 | 224 | "color-name@1.1.3": 225 | "integrity" "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" 226 | "resolved" "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz" 227 | "version" "1.1.3" 228 | 229 | "concat-map@0.0.1": 230 | "integrity" "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" 231 | "resolved" "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz" 232 | "version" "0.0.1" 233 | 234 | "cross-spawn@^7.0.2": 235 | "integrity" "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==" 236 | "resolved" "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz" 237 | "version" "7.0.3" 238 | dependencies: 239 | "path-key" "^3.1.0" 240 | "shebang-command" "^2.0.0" 241 | "which" "^2.0.1" 242 | 243 | "debug@^4.0.1", "debug@^4.1.1", "debug@^4.3.1": 244 | "integrity" "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==" 245 | "resolved" "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz" 246 | "version" "4.3.1" 247 | dependencies: 248 | "ms" "2.1.2" 249 | 250 | "deep-equal@^2.0.5": 251 | "integrity" "sha512-nPiRgmbAtm1a3JsnLCf6/SLfXcjyN5v8L1TXzdCmHrXJ4hx+gW/w1YCcn7z8gJtSiDArZCgYtbao3QqLm/N1Sw==" 252 | "resolved" "https://registry.npmjs.org/deep-equal/-/deep-equal-2.0.5.tgz" 253 | "version" "2.0.5" 254 | dependencies: 255 | "call-bind" "^1.0.0" 256 | "es-get-iterator" "^1.1.1" 257 | "get-intrinsic" "^1.0.1" 258 | "is-arguments" "^1.0.4" 259 | "is-date-object" "^1.0.2" 260 | "is-regex" "^1.1.1" 261 | "isarray" "^2.0.5" 262 | "object-is" "^1.1.4" 263 | "object-keys" "^1.1.1" 264 | "object.assign" "^4.1.2" 265 | "regexp.prototype.flags" "^1.3.0" 266 | "side-channel" "^1.0.3" 267 | "which-boxed-primitive" "^1.0.1" 268 | "which-collection" "^1.0.1" 269 | "which-typed-array" "^1.1.2" 270 | 271 | "deep-is@^0.1.3": 272 | "integrity" "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=" 273 | "resolved" "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz" 274 | "version" "0.1.3" 275 | 276 | "define-properties@^1.1.3", "define-properties@^1.1.4": 277 | "integrity" "sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==" 278 | "resolved" "https://registry.npmjs.org/define-properties/-/define-properties-1.1.4.tgz" 279 | "version" "1.1.4" 280 | dependencies: 281 | "has-property-descriptors" "^1.0.0" 282 | "object-keys" "^1.1.1" 283 | 284 | "defined@^1.0.0": 285 | "integrity" "sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM=" 286 | "resolved" "https://registry.npmjs.org/defined/-/defined-1.0.0.tgz" 287 | "version" "1.0.0" 288 | 289 | "diff@^4.0.2": 290 | "integrity" "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==" 291 | "resolved" "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz" 292 | "version" "4.0.2" 293 | 294 | "doctrine@^3.0.0": 295 | "integrity" "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==" 296 | "resolved" "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz" 297 | "version" "3.0.0" 298 | dependencies: 299 | "esutils" "^2.0.2" 300 | 301 | "dotignore@^0.1.2": 302 | "integrity" "sha512-UGGGWfSauusaVJC+8fgV+NVvBXkCTmVv7sk6nojDZZvuOUNGUy0Zk4UpHQD6EDjS0jpBwcACvH4eofvyzBcRDw==" 303 | "resolved" "https://registry.npmjs.org/dotignore/-/dotignore-0.1.2.tgz" 304 | "version" "0.1.2" 305 | dependencies: 306 | "minimatch" "^3.0.4" 307 | 308 | "emoji-regex@^8.0.0": 309 | "integrity" "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" 310 | "resolved" "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz" 311 | "version" "8.0.0" 312 | 313 | "enquirer@^2.3.5": 314 | "integrity" "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==" 315 | "resolved" "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz" 316 | "version" "2.3.6" 317 | dependencies: 318 | "ansi-colors" "^4.1.1" 319 | 320 | "es-abstract@^1.18.0-next.1", "es-abstract@^1.18.0-next.2", "es-abstract@^1.19.0", "es-abstract@^1.19.5": 321 | "integrity" "sha512-AyrnaKVpMzljIdwjzrj+LxGmj8ik2LckwXacHqrJJ/jxz6dDDBcZ7I7nlHM0FvEW8MfbWJwOd+yT2XzYW49Frw==" 322 | "resolved" "https://registry.npmjs.org/es-abstract/-/es-abstract-1.20.3.tgz" 323 | "version" "1.20.3" 324 | dependencies: 325 | "call-bind" "^1.0.2" 326 | "es-to-primitive" "^1.2.1" 327 | "function-bind" "^1.1.1" 328 | "function.prototype.name" "^1.1.5" 329 | "get-intrinsic" "^1.1.3" 330 | "get-symbol-description" "^1.0.0" 331 | "has" "^1.0.3" 332 | "has-property-descriptors" "^1.0.0" 333 | "has-symbols" "^1.0.3" 334 | "internal-slot" "^1.0.3" 335 | "is-callable" "^1.2.6" 336 | "is-negative-zero" "^2.0.2" 337 | "is-regex" "^1.1.4" 338 | "is-shared-array-buffer" "^1.0.2" 339 | "is-string" "^1.0.7" 340 | "is-weakref" "^1.0.2" 341 | "object-inspect" "^1.12.2" 342 | "object-keys" "^1.1.1" 343 | "object.assign" "^4.1.4" 344 | "regexp.prototype.flags" "^1.4.3" 345 | "safe-regex-test" "^1.0.0" 346 | "string.prototype.trimend" "^1.0.5" 347 | "string.prototype.trimstart" "^1.0.5" 348 | "unbox-primitive" "^1.0.2" 349 | 350 | "es-get-iterator@^1.1.1": 351 | "integrity" "sha512-+DTO8GYwbMCwbywjimwZMHp8AuYXOS2JZFWoi2AlPOS3ebnII9w/NLpNZtA7A0YLaVDw+O7KFCeoIV7OPvM7hQ==" 352 | "resolved" "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.2.tgz" 353 | "version" "1.1.2" 354 | dependencies: 355 | "call-bind" "^1.0.2" 356 | "get-intrinsic" "^1.1.0" 357 | "has-symbols" "^1.0.1" 358 | "is-arguments" "^1.1.0" 359 | "is-map" "^2.0.2" 360 | "is-set" "^2.0.2" 361 | "is-string" "^1.0.5" 362 | "isarray" "^2.0.5" 363 | 364 | "es-to-primitive@^1.2.1": 365 | "integrity" "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==" 366 | "resolved" "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz" 367 | "version" "1.2.1" 368 | dependencies: 369 | "is-callable" "^1.1.4" 370 | "is-date-object" "^1.0.1" 371 | "is-symbol" "^1.0.2" 372 | 373 | "escape-string-regexp@^1.0.5": 374 | "integrity" "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" 375 | "resolved" "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz" 376 | "version" "1.0.5" 377 | 378 | "eslint-plugin-promise@^6.0.0": 379 | "integrity" "sha512-7GPezalm5Bfi/E22PnQxDWH2iW9GTvAlUNTztemeHb6c1BniSyoeTrM87JkC0wYdi6aQrZX9p2qEiAno8aTcbw==" 380 | "resolved" "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-6.0.0.tgz" 381 | "version" "6.0.0" 382 | 383 | "eslint-scope@^5.1.1": 384 | "integrity" "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==" 385 | "resolved" "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz" 386 | "version" "5.1.1" 387 | dependencies: 388 | "esrecurse" "^4.3.0" 389 | "estraverse" "^4.1.1" 390 | 391 | "eslint-utils@^2.1.0": 392 | "integrity" "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==" 393 | "resolved" "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz" 394 | "version" "2.1.0" 395 | dependencies: 396 | "eslint-visitor-keys" "^1.1.0" 397 | 398 | "eslint-visitor-keys@^1.1.0": 399 | "integrity" "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==" 400 | "resolved" "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz" 401 | "version" "1.3.0" 402 | 403 | "eslint-visitor-keys@^1.3.0": 404 | "integrity" "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==" 405 | "resolved" "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz" 406 | "version" "1.3.0" 407 | 408 | "eslint-visitor-keys@^2.0.0": 409 | "integrity" "sha512-QudtT6av5WXels9WjIM7qz1XD1cWGvX4gGXvp/zBn9nXG02D0utdU3Em2m/QjTnrsk6bBjmCygl3rmj118msQQ==" 410 | "resolved" "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.0.0.tgz" 411 | "version" "2.0.0" 412 | 413 | "eslint@^7.0.0 || ^8.0.0", "eslint@^7.4.0": 414 | "integrity" "sha512-W2aJbXpMNofUp0ztQaF40fveSsJBjlSCSWpy//gzfTvwC+USs/nceBrKmlJOiM8r1bLwP2EuYkCqArn/6QTIgg==" 415 | "resolved" "https://registry.npmjs.org/eslint/-/eslint-7.21.0.tgz" 416 | "version" "7.21.0" 417 | dependencies: 418 | "@babel/code-frame" "7.12.11" 419 | "@eslint/eslintrc" "^0.4.0" 420 | "ajv" "^6.10.0" 421 | "chalk" "^4.0.0" 422 | "cross-spawn" "^7.0.2" 423 | "debug" "^4.0.1" 424 | "doctrine" "^3.0.0" 425 | "enquirer" "^2.3.5" 426 | "eslint-scope" "^5.1.1" 427 | "eslint-utils" "^2.1.0" 428 | "eslint-visitor-keys" "^2.0.0" 429 | "espree" "^7.3.1" 430 | "esquery" "^1.4.0" 431 | "esutils" "^2.0.2" 432 | "file-entry-cache" "^6.0.1" 433 | "functional-red-black-tree" "^1.0.1" 434 | "glob-parent" "^5.0.0" 435 | "globals" "^12.1.0" 436 | "ignore" "^4.0.6" 437 | "import-fresh" "^3.0.0" 438 | "imurmurhash" "^0.1.4" 439 | "is-glob" "^4.0.0" 440 | "js-yaml" "^3.13.1" 441 | "json-stable-stringify-without-jsonify" "^1.0.1" 442 | "levn" "^0.4.1" 443 | "lodash" "^4.17.20" 444 | "minimatch" "^3.0.4" 445 | "natural-compare" "^1.4.0" 446 | "optionator" "^0.9.1" 447 | "progress" "^2.0.0" 448 | "regexpp" "^3.1.0" 449 | "semver" "^7.2.1" 450 | "strip-ansi" "^6.0.0" 451 | "strip-json-comments" "^3.1.0" 452 | "table" "^6.0.4" 453 | "text-table" "^0.2.0" 454 | "v8-compile-cache" "^2.0.3" 455 | 456 | "espree@^7.3.0", "espree@^7.3.1": 457 | "integrity" "sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g==" 458 | "resolved" "https://registry.npmjs.org/espree/-/espree-7.3.1.tgz" 459 | "version" "7.3.1" 460 | dependencies: 461 | "acorn" "^7.4.0" 462 | "acorn-jsx" "^5.3.1" 463 | "eslint-visitor-keys" "^1.3.0" 464 | 465 | "esprima@^4.0.0": 466 | "integrity" "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==" 467 | "resolved" "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz" 468 | "version" "4.0.1" 469 | 470 | "esquery@^1.4.0": 471 | "integrity" "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==" 472 | "resolved" "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz" 473 | "version" "1.4.0" 474 | dependencies: 475 | "estraverse" "^5.1.0" 476 | 477 | "esrecurse@^4.3.0": 478 | "integrity" "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==" 479 | "resolved" "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz" 480 | "version" "4.3.0" 481 | dependencies: 482 | "estraverse" "^5.2.0" 483 | 484 | "estraverse@^4.1.1": 485 | "integrity" "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==" 486 | "resolved" "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz" 487 | "version" "4.3.0" 488 | 489 | "estraverse@^5.1.0": 490 | "integrity" "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==" 491 | "resolved" "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz" 492 | "version" "5.2.0" 493 | 494 | "estraverse@^5.2.0": 495 | "integrity" "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==" 496 | "resolved" "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz" 497 | "version" "5.2.0" 498 | 499 | "esutils@^2.0.2": 500 | "integrity" "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==" 501 | "resolved" "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz" 502 | "version" "2.0.3" 503 | 504 | "fast-deep-equal@^3.1.1": 505 | "integrity" "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" 506 | "resolved" "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz" 507 | "version" "3.1.3" 508 | 509 | "fast-json-stable-stringify@^2.0.0": 510 | "integrity" "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" 511 | "resolved" "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz" 512 | "version" "2.1.0" 513 | 514 | "fast-levenshtein@^2.0.6": 515 | "integrity" "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=" 516 | "resolved" "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz" 517 | "version" "2.0.6" 518 | 519 | "file-entry-cache@^6.0.1": 520 | "integrity" "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==" 521 | "resolved" "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz" 522 | "version" "6.0.1" 523 | dependencies: 524 | "flat-cache" "^3.0.4" 525 | 526 | "flat-cache@^3.0.4": 527 | "integrity" "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==" 528 | "resolved" "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz" 529 | "version" "3.0.4" 530 | dependencies: 531 | "flatted" "^3.1.0" 532 | "rimraf" "^3.0.2" 533 | 534 | "flatted@^3.1.0": 535 | "integrity" "sha512-zAoAQiudy+r5SvnSw3KJy5os/oRJYHzrzja/tBDqrZtNhUw8bt6y8OBzMWcjWr+8liV8Eb6yOhw8WZ7VFZ5ZzA==" 536 | "resolved" "https://registry.npmjs.org/flatted/-/flatted-3.1.1.tgz" 537 | "version" "3.1.1" 538 | 539 | "for-each@^0.3.3": 540 | "integrity" "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==" 541 | "resolved" "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz" 542 | "version" "0.3.3" 543 | dependencies: 544 | "is-callable" "^1.1.3" 545 | 546 | "foreach@^2.0.5": 547 | "integrity" "sha1-C+4AUBiusmDQo6865ljdATbsG5k=" 548 | "resolved" "https://registry.npmjs.org/foreach/-/foreach-2.0.5.tgz" 549 | "version" "2.0.5" 550 | 551 | "fs.realpath@^1.0.0": 552 | "integrity" "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" 553 | "resolved" "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz" 554 | "version" "1.0.0" 555 | 556 | "function-bind@^1.1.1": 557 | "integrity" "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" 558 | "resolved" "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz" 559 | "version" "1.1.1" 560 | 561 | "function.prototype.name@^1.1.5": 562 | "integrity" "sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==" 563 | "resolved" "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.5.tgz" 564 | "version" "1.1.5" 565 | dependencies: 566 | "call-bind" "^1.0.2" 567 | "define-properties" "^1.1.3" 568 | "es-abstract" "^1.19.0" 569 | "functions-have-names" "^1.2.2" 570 | 571 | "functional-red-black-tree@^1.0.1": 572 | "integrity" "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=" 573 | "resolved" "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz" 574 | "version" "1.0.1" 575 | 576 | "functions-have-names@^1.2.2": 577 | "integrity" "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==" 578 | "resolved" "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz" 579 | "version" "1.2.3" 580 | 581 | "get-intrinsic@^1.0.1", "get-intrinsic@^1.0.2", "get-intrinsic@^1.1.0", "get-intrinsic@^1.1.1", "get-intrinsic@^1.1.3": 582 | "integrity" "sha512-QJVz1Tj7MS099PevUG5jvnt9tSkXN8K14dxQlikJuPt4uD9hHAHjLyLBiLR5zELelBdD9QNRAXZzsJx0WaDL9A==" 583 | "resolved" "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.3.tgz" 584 | "version" "1.1.3" 585 | dependencies: 586 | "function-bind" "^1.1.1" 587 | "has" "^1.0.3" 588 | "has-symbols" "^1.0.3" 589 | 590 | "get-package-type@^0.1.0": 591 | "integrity" "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==" 592 | "resolved" "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz" 593 | "version" "0.1.0" 594 | 595 | "get-symbol-description@^1.0.0": 596 | "integrity" "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==" 597 | "resolved" "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz" 598 | "version" "1.0.0" 599 | dependencies: 600 | "call-bind" "^1.0.2" 601 | "get-intrinsic" "^1.1.1" 602 | 603 | "glob-parent@^5.0.0": 604 | "integrity" "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==" 605 | "resolved" "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz" 606 | "version" "5.1.2" 607 | dependencies: 608 | "is-glob" "^4.0.1" 609 | 610 | "glob@^7.1.3", "glob@^7.2.3": 611 | "integrity" "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==" 612 | "resolved" "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz" 613 | "version" "7.2.3" 614 | dependencies: 615 | "fs.realpath" "^1.0.0" 616 | "inflight" "^1.0.4" 617 | "inherits" "2" 618 | "minimatch" "^3.1.1" 619 | "once" "^1.3.0" 620 | "path-is-absolute" "^1.0.0" 621 | 622 | "globals@^12.1.0": 623 | "integrity" "sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg==" 624 | "resolved" "https://registry.npmjs.org/globals/-/globals-12.4.0.tgz" 625 | "version" "12.4.0" 626 | dependencies: 627 | "type-fest" "^0.8.1" 628 | 629 | "has-bigints@^1.0.2": 630 | "integrity" "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==" 631 | "resolved" "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz" 632 | "version" "1.0.2" 633 | 634 | "has-dynamic-import@^2.0.1": 635 | "integrity" "sha512-X3fbtsZmwb6W7fJGR9o7x65fZoodygCrZ3TVycvghP62yYQfS0t4RS0Qcz+j5tQYUKeSWS09tHkWW6WhFV3XhQ==" 636 | "resolved" "https://registry.npmjs.org/has-dynamic-import/-/has-dynamic-import-2.0.1.tgz" 637 | "version" "2.0.1" 638 | dependencies: 639 | "call-bind" "^1.0.2" 640 | "get-intrinsic" "^1.1.1" 641 | 642 | "has-flag@^3.0.0": 643 | "integrity" "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" 644 | "resolved" "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz" 645 | "version" "3.0.0" 646 | 647 | "has-flag@^4.0.0": 648 | "integrity" "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" 649 | "resolved" "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz" 650 | "version" "4.0.0" 651 | 652 | "has-property-descriptors@^1.0.0": 653 | "integrity" "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==" 654 | "resolved" "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz" 655 | "version" "1.0.0" 656 | dependencies: 657 | "get-intrinsic" "^1.1.1" 658 | 659 | "has-symbols@^1.0.1", "has-symbols@^1.0.2", "has-symbols@^1.0.3": 660 | "integrity" "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==" 661 | "resolved" "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz" 662 | "version" "1.0.3" 663 | 664 | "has-tostringtag@^1.0.0": 665 | "integrity" "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==" 666 | "resolved" "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz" 667 | "version" "1.0.0" 668 | dependencies: 669 | "has-symbols" "^1.0.2" 670 | 671 | "has@^1.0.3": 672 | "integrity" "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==" 673 | "resolved" "https://registry.npmjs.org/has/-/has-1.0.3.tgz" 674 | "version" "1.0.3" 675 | dependencies: 676 | "function-bind" "^1.1.1" 677 | 678 | "ignore@^4.0.6": 679 | "integrity" "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==" 680 | "resolved" "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz" 681 | "version" "4.0.6" 682 | 683 | "import-fresh@^3.0.0", "import-fresh@^3.2.1": 684 | "integrity" "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==" 685 | "resolved" "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz" 686 | "version" "3.3.0" 687 | dependencies: 688 | "parent-module" "^1.0.0" 689 | "resolve-from" "^4.0.0" 690 | 691 | "imurmurhash@^0.1.4": 692 | "integrity" "sha1-khi5srkoojixPcT7a21XbyMUU+o=" 693 | "resolved" "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz" 694 | "version" "0.1.4" 695 | 696 | "inflight@^1.0.4": 697 | "integrity" "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=" 698 | "resolved" "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz" 699 | "version" "1.0.6" 700 | dependencies: 701 | "once" "^1.3.0" 702 | "wrappy" "1" 703 | 704 | "inherits@^2.0.4", "inherits@2": 705 | "integrity" "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" 706 | "resolved" "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz" 707 | "version" "2.0.4" 708 | 709 | "internal-slot@^1.0.3": 710 | "integrity" "sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==" 711 | "resolved" "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz" 712 | "version" "1.0.3" 713 | dependencies: 714 | "get-intrinsic" "^1.1.0" 715 | "has" "^1.0.3" 716 | "side-channel" "^1.0.4" 717 | 718 | "is-arguments@^1.0.4", "is-arguments@^1.1.0": 719 | "integrity" "sha512-1Ij4lOMPl/xB5kBDn7I+b2ttPMKa8szhEIrXDuXQD/oe3HJLTLhqhgGspwgyGd6MOywBUqVvYicF72lkgDnIHg==" 720 | "resolved" "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.0.tgz" 721 | "version" "1.1.0" 722 | dependencies: 723 | "call-bind" "^1.0.0" 724 | 725 | "is-bigint@^1.0.1": 726 | "integrity" "sha512-J0ELF4yHFxHy0cmSxZuheDOz2luOdVvqjwmEcj8H/L1JHeuEDSDbeRP+Dk9kFVk5RTFzbucJ2Kb9F7ixY2QaCg==" 727 | "resolved" "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.1.tgz" 728 | "version" "1.0.1" 729 | 730 | "is-boolean-object@^1.1.0": 731 | "integrity" "sha512-a7Uprx8UtD+HWdyYwnD1+ExtTgqQtD2k/1yJgtXP6wnMm8byhkoTZRl+95LLThpzNZJ5aEvi46cdH+ayMFRwmA==" 732 | "resolved" "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.0.tgz" 733 | "version" "1.1.0" 734 | dependencies: 735 | "call-bind" "^1.0.0" 736 | 737 | "is-callable@^1.1.3", "is-callable@^1.1.4", "is-callable@^1.2.6": 738 | "integrity" "sha512-krO72EO2NptOGAX2KYyqbP9vYMlNAXdB53rq6f8LXY6RY7JdSR/3BD6wLUlPHSAesmY9vstNrjvqGaCiRK/91Q==" 739 | "resolved" "https://registry.npmjs.org/is-callable/-/is-callable-1.2.6.tgz" 740 | "version" "1.2.6" 741 | 742 | "is-core-module@^2.2.0": 743 | "integrity" "sha512-XRAfAdyyY5F5cOXn7hYQDqh2Xmii+DEfIcQGxK/uNwMHhIkPWO0g8msXcbzLe+MpGoR951MlqM/2iIlU4vKDdQ==" 744 | "resolved" "https://registry.npmjs.org/is-core-module/-/is-core-module-2.2.0.tgz" 745 | "version" "2.2.0" 746 | dependencies: 747 | "has" "^1.0.3" 748 | 749 | "is-date-object@^1.0.1", "is-date-object@^1.0.2": 750 | "integrity" "sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==" 751 | "resolved" "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.2.tgz" 752 | "version" "1.0.2" 753 | 754 | "is-extglob@^2.1.1": 755 | "integrity" "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=" 756 | "resolved" "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz" 757 | "version" "2.1.1" 758 | 759 | "is-fullwidth-code-point@^3.0.0": 760 | "integrity" "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" 761 | "resolved" "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz" 762 | "version" "3.0.0" 763 | 764 | "is-glob@^4.0.0", "is-glob@^4.0.1": 765 | "integrity" "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==" 766 | "resolved" "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz" 767 | "version" "4.0.1" 768 | dependencies: 769 | "is-extglob" "^2.1.1" 770 | 771 | "is-map@^2.0.1", "is-map@^2.0.2": 772 | "integrity" "sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg==" 773 | "resolved" "https://registry.npmjs.org/is-map/-/is-map-2.0.2.tgz" 774 | "version" "2.0.2" 775 | 776 | "is-negative-zero@^2.0.2": 777 | "integrity" "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==" 778 | "resolved" "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz" 779 | "version" "2.0.2" 780 | 781 | "is-number-object@^1.0.4": 782 | "integrity" "sha512-zohwelOAur+5uXtk8O3GPQ1eAcu4ZX3UwxQhUlfFFMNpUd83gXgjbhJh6HmB6LUNV/ieOLQuDwJO3dWJosUeMw==" 783 | "resolved" "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.4.tgz" 784 | "version" "1.0.4" 785 | 786 | "is-regex@^1.1.1", "is-regex@^1.1.4": 787 | "integrity" "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==" 788 | "resolved" "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz" 789 | "version" "1.1.4" 790 | dependencies: 791 | "call-bind" "^1.0.2" 792 | "has-tostringtag" "^1.0.0" 793 | 794 | "is-set@^2.0.1", "is-set@^2.0.2": 795 | "integrity" "sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g==" 796 | "resolved" "https://registry.npmjs.org/is-set/-/is-set-2.0.2.tgz" 797 | "version" "2.0.2" 798 | 799 | "is-shared-array-buffer@^1.0.2": 800 | "integrity" "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==" 801 | "resolved" "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz" 802 | "version" "1.0.2" 803 | dependencies: 804 | "call-bind" "^1.0.2" 805 | 806 | "is-string@^1.0.5", "is-string@^1.0.7": 807 | "integrity" "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==" 808 | "resolved" "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz" 809 | "version" "1.0.7" 810 | dependencies: 811 | "has-tostringtag" "^1.0.0" 812 | 813 | "is-symbol@^1.0.2", "is-symbol@^1.0.3": 814 | "integrity" "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==" 815 | "resolved" "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz" 816 | "version" "1.0.3" 817 | dependencies: 818 | "has-symbols" "^1.0.1" 819 | 820 | "is-typed-array@^1.1.3": 821 | "integrity" "sha512-S+GRDgJlR3PyEbsX/Fobd9cqpZBuvUS+8asRqYDMLCb2qMzt1oz5m5oxQCxOgUDxiWsOVNi4yaF+/uvdlHlYug==" 822 | "resolved" "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.5.tgz" 823 | "version" "1.1.5" 824 | dependencies: 825 | "available-typed-arrays" "^1.0.2" 826 | "call-bind" "^1.0.2" 827 | "es-abstract" "^1.18.0-next.2" 828 | "foreach" "^2.0.5" 829 | "has-symbols" "^1.0.1" 830 | 831 | "is-weakmap@^2.0.1": 832 | "integrity" "sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA==" 833 | "resolved" "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.1.tgz" 834 | "version" "2.0.1" 835 | 836 | "is-weakref@^1.0.2": 837 | "integrity" "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==" 838 | "resolved" "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz" 839 | "version" "1.0.2" 840 | dependencies: 841 | "call-bind" "^1.0.2" 842 | 843 | "is-weakset@^2.0.1": 844 | "integrity" "sha512-pi4vhbhVHGLxohUw7PhGsueT4vRGFoXhP7+RGN0jKIv9+8PWYCQTqtADngrxOm2g46hoH0+g8uZZBzMrvVGDmw==" 845 | "resolved" "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.1.tgz" 846 | "version" "2.0.1" 847 | 848 | "isarray@^2.0.5": 849 | "integrity" "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==" 850 | "resolved" "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz" 851 | "version" "2.0.5" 852 | 853 | "isarray@0.0.1": 854 | "integrity" "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" 855 | "resolved" "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz" 856 | "version" "0.0.1" 857 | 858 | "isexe@^2.0.0": 859 | "integrity" "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" 860 | "resolved" "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz" 861 | "version" "2.0.0" 862 | 863 | "js-tokens@^4.0.0": 864 | "integrity" "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" 865 | "resolved" "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz" 866 | "version" "4.0.0" 867 | 868 | "js-yaml@^3.13.1": 869 | "integrity" "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==" 870 | "resolved" "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz" 871 | "version" "3.14.1" 872 | dependencies: 873 | "argparse" "^1.0.7" 874 | "esprima" "^4.0.0" 875 | 876 | "json-schema-traverse@^0.4.1": 877 | "integrity" "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" 878 | "resolved" "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz" 879 | "version" "0.4.1" 880 | 881 | "json-schema-traverse@^1.0.0": 882 | "integrity" "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" 883 | "resolved" "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz" 884 | "version" "1.0.0" 885 | 886 | "json-stable-stringify-without-jsonify@^1.0.1": 887 | "integrity" "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=" 888 | "resolved" "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz" 889 | "version" "1.0.1" 890 | 891 | "just-extend@^4.0.2": 892 | "integrity" "sha512-aWgeGFW67BP3e5181Ep1Fv2v8z//iBJfrvyTnq8wG86vEESwmonn1zPBJ0VfmT9CJq2FIT0VsETtrNFm2a+SHA==" 893 | "resolved" "https://registry.npmjs.org/just-extend/-/just-extend-4.1.1.tgz" 894 | "version" "4.1.1" 895 | 896 | "levn@^0.4.1": 897 | "integrity" "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==" 898 | "resolved" "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz" 899 | "version" "0.4.1" 900 | dependencies: 901 | "prelude-ls" "^1.2.1" 902 | "type-check" "~0.4.0" 903 | 904 | "lodash.get@^4.4.2": 905 | "integrity" "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=" 906 | "resolved" "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz" 907 | "version" "4.4.2" 908 | 909 | "lodash@^4.17.20": 910 | "integrity" "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" 911 | "resolved" "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz" 912 | "version" "4.17.21" 913 | 914 | "lru-cache@^6.0.0": 915 | "integrity" "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==" 916 | "resolved" "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz" 917 | "version" "6.0.0" 918 | dependencies: 919 | "yallist" "^4.0.0" 920 | 921 | "minimatch@^3.0.4", "minimatch@^3.1.1": 922 | "integrity" "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==" 923 | "resolved" "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz" 924 | "version" "3.1.2" 925 | dependencies: 926 | "brace-expansion" "^1.1.7" 927 | 928 | "minimist@^1.2.6": 929 | "integrity" "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==" 930 | "resolved" "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz" 931 | "version" "1.2.6" 932 | 933 | "ms@2.1.2": 934 | "integrity" "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" 935 | "resolved" "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz" 936 | "version" "2.1.2" 937 | 938 | "natural-compare@^1.4.0": 939 | "integrity" "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=" 940 | "resolved" "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz" 941 | "version" "1.4.0" 942 | 943 | "nise@^4.0.4": 944 | "integrity" "sha512-eQMEmGN/8arp0xsvGoQ+B1qvSkR73B1nWSCh7nOt5neMCtwcQVYQGdzQMhcNscktTsWB54xnlSQFzOAPJD8nXA==" 945 | "resolved" "https://registry.npmjs.org/nise/-/nise-4.1.0.tgz" 946 | "version" "4.1.0" 947 | dependencies: 948 | "@sinonjs/commons" "^1.7.0" 949 | "@sinonjs/fake-timers" "^6.0.0" 950 | "@sinonjs/text-encoding" "^0.7.1" 951 | "just-extend" "^4.0.2" 952 | "path-to-regexp" "^1.7.0" 953 | 954 | "object-inspect@^1.12.2", "object-inspect@^1.9.0": 955 | "integrity" "sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==" 956 | "resolved" "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz" 957 | "version" "1.12.2" 958 | 959 | "object-is@^1.1.4", "object-is@^1.1.5": 960 | "integrity" "sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==" 961 | "resolved" "https://registry.npmjs.org/object-is/-/object-is-1.1.5.tgz" 962 | "version" "1.1.5" 963 | dependencies: 964 | "call-bind" "^1.0.2" 965 | "define-properties" "^1.1.3" 966 | 967 | "object-keys@^1.1.1": 968 | "integrity" "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==" 969 | "resolved" "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz" 970 | "version" "1.1.1" 971 | 972 | "object.assign@^4.1.2", "object.assign@^4.1.4": 973 | "integrity" "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==" 974 | "resolved" "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz" 975 | "version" "4.1.4" 976 | dependencies: 977 | "call-bind" "^1.0.2" 978 | "define-properties" "^1.1.4" 979 | "has-symbols" "^1.0.3" 980 | "object-keys" "^1.1.1" 981 | 982 | "once@^1.3.0": 983 | "integrity" "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=" 984 | "resolved" "https://registry.npmjs.org/once/-/once-1.4.0.tgz" 985 | "version" "1.4.0" 986 | dependencies: 987 | "wrappy" "1" 988 | 989 | "optionator@^0.9.1": 990 | "integrity" "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==" 991 | "resolved" "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz" 992 | "version" "0.9.1" 993 | dependencies: 994 | "deep-is" "^0.1.3" 995 | "fast-levenshtein" "^2.0.6" 996 | "levn" "^0.4.1" 997 | "prelude-ls" "^1.2.1" 998 | "type-check" "^0.4.0" 999 | "word-wrap" "^1.2.3" 1000 | 1001 | "parent-module@^1.0.0": 1002 | "integrity" "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==" 1003 | "resolved" "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz" 1004 | "version" "1.0.1" 1005 | dependencies: 1006 | "callsites" "^3.0.0" 1007 | 1008 | "path-is-absolute@^1.0.0": 1009 | "integrity" "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" 1010 | "resolved" "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz" 1011 | "version" "1.0.1" 1012 | 1013 | "path-key@^3.1.0": 1014 | "integrity" "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==" 1015 | "resolved" "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz" 1016 | "version" "3.1.1" 1017 | 1018 | "path-parse@^1.0.6": 1019 | "integrity" "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" 1020 | "resolved" "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz" 1021 | "version" "1.0.7" 1022 | 1023 | "path-to-regexp@^1.7.0": 1024 | "integrity" "sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==" 1025 | "resolved" "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz" 1026 | "version" "1.8.0" 1027 | dependencies: 1028 | "isarray" "0.0.1" 1029 | 1030 | "prelude-ls@^1.2.1": 1031 | "integrity" "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==" 1032 | "resolved" "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz" 1033 | "version" "1.2.1" 1034 | 1035 | "progress@^2.0.0": 1036 | "integrity" "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==" 1037 | "resolved" "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz" 1038 | "version" "2.0.3" 1039 | 1040 | "punycode@^2.1.0": 1041 | "integrity" "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" 1042 | "resolved" "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz" 1043 | "version" "2.1.1" 1044 | 1045 | "regexp.prototype.flags@^1.3.0", "regexp.prototype.flags@^1.4.3": 1046 | "integrity" "sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==" 1047 | "resolved" "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz" 1048 | "version" "1.4.3" 1049 | dependencies: 1050 | "call-bind" "^1.0.2" 1051 | "define-properties" "^1.1.3" 1052 | "functions-have-names" "^1.2.2" 1053 | 1054 | "regexpp@^3.1.0": 1055 | "integrity" "sha512-ZOIzd8yVsQQA7j8GCSlPGXwg5PfmA1mrq0JP4nGhh54LaKN3xdai/vHUDu74pKwV8OxseMS65u2NImosQcSD0Q==" 1056 | "resolved" "https://registry.npmjs.org/regexpp/-/regexpp-3.1.0.tgz" 1057 | "version" "3.1.0" 1058 | 1059 | "require-from-string@^2.0.2": 1060 | "integrity" "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==" 1061 | "resolved" "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz" 1062 | "version" "2.0.2" 1063 | 1064 | "resolve-from@^4.0.0": 1065 | "integrity" "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==" 1066 | "resolved" "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz" 1067 | "version" "4.0.0" 1068 | 1069 | "resolve@^2.0.0-next.3": 1070 | "integrity" "sha512-W8LucSynKUIDu9ylraa7ueVZ7hc0uAgJBxVsQSKOXOyle8a93qXhcz+XAXZ8bIq2d6i4Ehddn6Evt+0/UwKk6Q==" 1071 | "resolved" "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.3.tgz" 1072 | "version" "2.0.0-next.3" 1073 | dependencies: 1074 | "is-core-module" "^2.2.0" 1075 | "path-parse" "^1.0.6" 1076 | 1077 | "resumer@^0.0.0": 1078 | "integrity" "sha1-8ej0YeQGS6Oegq883CqMiT0HZ1k=" 1079 | "resolved" "https://registry.npmjs.org/resumer/-/resumer-0.0.0.tgz" 1080 | "version" "0.0.0" 1081 | dependencies: 1082 | "through" "~2.3.4" 1083 | 1084 | "rimraf@^3.0.2": 1085 | "integrity" "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==" 1086 | "resolved" "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz" 1087 | "version" "3.0.2" 1088 | dependencies: 1089 | "glob" "^7.1.3" 1090 | 1091 | "safe-regex-test@^1.0.0": 1092 | "integrity" "sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==" 1093 | "resolved" "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.0.tgz" 1094 | "version" "1.0.0" 1095 | dependencies: 1096 | "call-bind" "^1.0.2" 1097 | "get-intrinsic" "^1.1.3" 1098 | "is-regex" "^1.1.4" 1099 | 1100 | "semver@^7.2.1": 1101 | "integrity" "sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==" 1102 | "resolved" "https://registry.npmjs.org/semver/-/semver-7.3.4.tgz" 1103 | "version" "7.3.4" 1104 | dependencies: 1105 | "lru-cache" "^6.0.0" 1106 | 1107 | "shebang-command@^2.0.0": 1108 | "integrity" "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==" 1109 | "resolved" "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz" 1110 | "version" "2.0.0" 1111 | dependencies: 1112 | "shebang-regex" "^3.0.0" 1113 | 1114 | "shebang-regex@^3.0.0": 1115 | "integrity" "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==" 1116 | "resolved" "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz" 1117 | "version" "3.0.0" 1118 | 1119 | "side-channel@^1.0.3", "side-channel@^1.0.4": 1120 | "integrity" "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==" 1121 | "resolved" "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz" 1122 | "version" "1.0.4" 1123 | dependencies: 1124 | "call-bind" "^1.0.0" 1125 | "get-intrinsic" "^1.0.2" 1126 | "object-inspect" "^1.9.0" 1127 | 1128 | "sinon@^9.0.2": 1129 | "integrity" "sha512-zljcULZQsJxVra28qIAL6ow1Z9tpattkCTEJR4RBP3TGc00FcttsP5pK284Nas5WjMZU5Yzy3kAIp3B3KRf5Yg==" 1130 | "resolved" "https://registry.npmjs.org/sinon/-/sinon-9.2.4.tgz" 1131 | "version" "9.2.4" 1132 | dependencies: 1133 | "@sinonjs/commons" "^1.8.1" 1134 | "@sinonjs/fake-timers" "^6.0.1" 1135 | "@sinonjs/samsam" "^5.3.1" 1136 | "diff" "^4.0.2" 1137 | "nise" "^4.0.4" 1138 | "supports-color" "^7.1.0" 1139 | 1140 | "slice-ansi@^4.0.0": 1141 | "integrity" "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==" 1142 | "resolved" "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz" 1143 | "version" "4.0.0" 1144 | dependencies: 1145 | "ansi-styles" "^4.0.0" 1146 | "astral-regex" "^2.0.0" 1147 | "is-fullwidth-code-point" "^3.0.0" 1148 | 1149 | "sprintf-js@~1.0.2": 1150 | "integrity" "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=" 1151 | "resolved" "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz" 1152 | "version" "1.0.3" 1153 | 1154 | "string-width@^4.2.0": 1155 | "integrity" "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==" 1156 | "resolved" "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz" 1157 | "version" "4.2.2" 1158 | dependencies: 1159 | "emoji-regex" "^8.0.0" 1160 | "is-fullwidth-code-point" "^3.0.0" 1161 | "strip-ansi" "^6.0.0" 1162 | 1163 | "string.prototype.trim@^1.2.6": 1164 | "integrity" "sha512-8lMR2m+U0VJTPp6JjvJTtGyc4FIGq9CdRt7O9p6T0e6K4vjU+OP+SQJpbe/SBmRcCUIvNUnjsbmY6lnMp8MhsQ==" 1165 | "resolved" "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.6.tgz" 1166 | "version" "1.2.6" 1167 | dependencies: 1168 | "call-bind" "^1.0.2" 1169 | "define-properties" "^1.1.4" 1170 | "es-abstract" "^1.19.5" 1171 | 1172 | "string.prototype.trimend@^1.0.5": 1173 | "integrity" "sha512-I7RGvmjV4pJ7O3kdf+LXFpVfdNOxtCW/2C8f6jNiW4+PQchwxkCDzlk1/7p+Wl4bqFIZeF47qAHXLuHHWKAxog==" 1174 | "resolved" "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.5.tgz" 1175 | "version" "1.0.5" 1176 | dependencies: 1177 | "call-bind" "^1.0.2" 1178 | "define-properties" "^1.1.4" 1179 | "es-abstract" "^1.19.5" 1180 | 1181 | "string.prototype.trimstart@^1.0.5": 1182 | "integrity" "sha512-THx16TJCGlsN0o6dl2o6ncWUsdgnLRSA23rRE5pyGBw/mLr3Ej/R2LaqCtgP8VNMGZsvMWnf9ooZPyY2bHvUFg==" 1183 | "resolved" "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.5.tgz" 1184 | "version" "1.0.5" 1185 | dependencies: 1186 | "call-bind" "^1.0.2" 1187 | "define-properties" "^1.1.4" 1188 | "es-abstract" "^1.19.5" 1189 | 1190 | "strip-ansi@^6.0.0": 1191 | "integrity" "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==" 1192 | "resolved" "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz" 1193 | "version" "6.0.0" 1194 | dependencies: 1195 | "ansi-regex" "^5.0.0" 1196 | 1197 | "strip-json-comments@^3.1.0", "strip-json-comments@^3.1.1": 1198 | "integrity" "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==" 1199 | "resolved" "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz" 1200 | "version" "3.1.1" 1201 | 1202 | "supports-color@^5.3.0": 1203 | "integrity" "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==" 1204 | "resolved" "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz" 1205 | "version" "5.5.0" 1206 | dependencies: 1207 | "has-flag" "^3.0.0" 1208 | 1209 | "supports-color@^7.1.0": 1210 | "integrity" "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==" 1211 | "resolved" "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz" 1212 | "version" "7.2.0" 1213 | dependencies: 1214 | "has-flag" "^4.0.0" 1215 | 1216 | "table@^6.0.4": 1217 | "integrity" "sha512-rxZevLGTUzWna/qBLObOe16kB2RTnnbhciwgPbMMlazz1yZGVEgnZK762xyVdVznhqxrfCeBMmMkgOOaPwjH7g==" 1218 | "resolved" "https://registry.npmjs.org/table/-/table-6.0.7.tgz" 1219 | "version" "6.0.7" 1220 | dependencies: 1221 | "ajv" "^7.0.2" 1222 | "lodash" "^4.17.20" 1223 | "slice-ansi" "^4.0.0" 1224 | "string-width" "^4.2.0" 1225 | 1226 | "tape@^5.6.1": 1227 | "integrity" "sha512-reNzS3rzsJtKk0f+zJx2XlzIsjJXlIcOIrIxk5shHAG/DzW3BKyMg8UfN79oluYlcWo4lIt56ahLqwgpRT4idg==" 1228 | "resolved" "https://registry.npmjs.org/tape/-/tape-5.6.1.tgz" 1229 | "version" "5.6.1" 1230 | dependencies: 1231 | "array.prototype.every" "^1.1.3" 1232 | "call-bind" "^1.0.2" 1233 | "deep-equal" "^2.0.5" 1234 | "defined" "^1.0.0" 1235 | "dotignore" "^0.1.2" 1236 | "for-each" "^0.3.3" 1237 | "get-package-type" "^0.1.0" 1238 | "glob" "^7.2.3" 1239 | "has" "^1.0.3" 1240 | "has-dynamic-import" "^2.0.1" 1241 | "inherits" "^2.0.4" 1242 | "is-regex" "^1.1.4" 1243 | "minimist" "^1.2.6" 1244 | "object-inspect" "^1.12.2" 1245 | "object-is" "^1.1.5" 1246 | "object-keys" "^1.1.1" 1247 | "object.assign" "^4.1.4" 1248 | "resolve" "^2.0.0-next.3" 1249 | "resumer" "^0.0.0" 1250 | "string.prototype.trim" "^1.2.6" 1251 | "through" "^2.3.8" 1252 | 1253 | "text-table@^0.2.0": 1254 | "integrity" "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=" 1255 | "resolved" "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz" 1256 | "version" "0.2.0" 1257 | 1258 | "through@^2.3.8", "through@~2.3.4": 1259 | "integrity" "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=" 1260 | "resolved" "https://registry.npmjs.org/through/-/through-2.3.8.tgz" 1261 | "version" "2.3.8" 1262 | 1263 | "type-check@^0.4.0", "type-check@~0.4.0": 1264 | "integrity" "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==" 1265 | "resolved" "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz" 1266 | "version" "0.4.0" 1267 | dependencies: 1268 | "prelude-ls" "^1.2.1" 1269 | 1270 | "type-detect@^4.0.8", "type-detect@4.0.8": 1271 | "integrity" "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==" 1272 | "resolved" "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz" 1273 | "version" "4.0.8" 1274 | 1275 | "type-fest@^0.8.1": 1276 | "integrity" "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==" 1277 | "resolved" "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz" 1278 | "version" "0.8.1" 1279 | 1280 | "unbox-primitive@^1.0.2": 1281 | "integrity" "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==" 1282 | "resolved" "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz" 1283 | "version" "1.0.2" 1284 | dependencies: 1285 | "call-bind" "^1.0.2" 1286 | "has-bigints" "^1.0.2" 1287 | "has-symbols" "^1.0.3" 1288 | "which-boxed-primitive" "^1.0.2" 1289 | 1290 | "uri-js@^4.2.2": 1291 | "integrity" "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==" 1292 | "resolved" "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz" 1293 | "version" "4.4.1" 1294 | dependencies: 1295 | "punycode" "^2.1.0" 1296 | 1297 | "uuid-random@^1.3.2": 1298 | "integrity" "sha512-UOzej0Le/UgkbWEO8flm+0y+G+ljUon1QWTEZOq1rnMAsxo2+SckbiZdKzAHHlVh6gJqI1TjC/xwgR50MuCrBQ==" 1299 | "resolved" "https://registry.npmjs.org/uuid-random/-/uuid-random-1.3.2.tgz" 1300 | "version" "1.3.2" 1301 | 1302 | "v8-compile-cache@^2.0.3": 1303 | "integrity" "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==" 1304 | "resolved" "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz" 1305 | "version" "2.3.0" 1306 | 1307 | "which-boxed-primitive@^1.0.1", "which-boxed-primitive@^1.0.2": 1308 | "integrity" "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==" 1309 | "resolved" "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz" 1310 | "version" "1.0.2" 1311 | dependencies: 1312 | "is-bigint" "^1.0.1" 1313 | "is-boolean-object" "^1.1.0" 1314 | "is-number-object" "^1.0.4" 1315 | "is-string" "^1.0.5" 1316 | "is-symbol" "^1.0.3" 1317 | 1318 | "which-collection@^1.0.1": 1319 | "integrity" "sha512-W8xeTUwaln8i3K/cY1nGXzdnVZlidBcagyNFtBdD5kxnb4TvGKR7FfSIS3mYpwWS1QUCutfKz8IY8RjftB0+1A==" 1320 | "resolved" "https://registry.npmjs.org/which-collection/-/which-collection-1.0.1.tgz" 1321 | "version" "1.0.1" 1322 | dependencies: 1323 | "is-map" "^2.0.1" 1324 | "is-set" "^2.0.1" 1325 | "is-weakmap" "^2.0.1" 1326 | "is-weakset" "^2.0.1" 1327 | 1328 | "which-typed-array@^1.1.2": 1329 | "integrity" "sha512-49E0SpUe90cjpoc7BOJwyPHRqSAd12c10Qm2amdEZrJPCY2NDxaW01zHITrem+rnETY3dwrbH3UUrUwagfCYDA==" 1330 | "resolved" "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.4.tgz" 1331 | "version" "1.1.4" 1332 | dependencies: 1333 | "available-typed-arrays" "^1.0.2" 1334 | "call-bind" "^1.0.0" 1335 | "es-abstract" "^1.18.0-next.1" 1336 | "foreach" "^2.0.5" 1337 | "function-bind" "^1.1.1" 1338 | "has-symbols" "^1.0.1" 1339 | "is-typed-array" "^1.1.3" 1340 | 1341 | "which@^2.0.1": 1342 | "integrity" "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==" 1343 | "resolved" "https://registry.npmjs.org/which/-/which-2.0.2.tgz" 1344 | "version" "2.0.2" 1345 | dependencies: 1346 | "isexe" "^2.0.0" 1347 | 1348 | "word-wrap@^1.2.3": 1349 | "integrity" "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==" 1350 | "resolved" "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz" 1351 | "version" "1.2.3" 1352 | 1353 | "wrappy@1": 1354 | "integrity" "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" 1355 | "resolved" "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz" 1356 | "version" "1.0.2" 1357 | 1358 | "ws@^7.4.4": 1359 | "integrity" "sha512-6GLgCqo2cy2A2rjCNFlxQS6ZljG/coZfZXclldI8FB/1G3CCI36Zd8xy2HrFVACi8tfk5XrgLQEk+P0Tnz9UcA==" 1360 | "resolved" "https://registry.npmjs.org/ws/-/ws-7.5.6.tgz" 1361 | "version" "7.5.6" 1362 | 1363 | "yallist@^4.0.0": 1364 | "integrity" "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" 1365 | "resolved" "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz" 1366 | "version" "4.0.0" 1367 | --------------------------------------------------------------------------------