diff --git a/capacitor/capacitor.config.js b/capacitor/capacitor.config.js index a1e4760..13cb504 100644 --- a/capacitor/capacitor.config.js +++ b/capacitor/capacitor.config.js @@ -8,7 +8,7 @@ const config = { launchShowDuration: 0 }, CapacitorHttp: { - enabled: false + enabled: true } }, // remove server section before making production build diff --git a/capacitor/package.json b/capacitor/package.json index d8eeff6..9eb7409 100644 --- a/capacitor/package.json +++ b/capacitor/package.json @@ -5,13 +5,14 @@ "build:app": "vite build", "build:android": "run-s build:app cap-run:android", "build:ios": "run-s build:app cap-run:ios", - "cap-run:android": "cap run android --target=Pixel_XL_API_33 --external --public-host=10.5.0.2", - "cap-run:ios": "cap sync ios && cap open ios", + "cap:android-emulator": "cap run android --target=Pixel_XL_API_33 --external --public-host=10.5.0.2 && dev:localhost-bind", + "cap:localhost-bind": "adb reverse tcp:5001 tcp:5001", + "cap:android": "run-s cap:android-emulator cap:localhost-bind", + "cap:ios": "cap sync ios && cap open ios", "dev:ios": "run-p dev:start cap-run:ios", "dev:preview": "vite preview", - "dev:start": "run-p dev:webpack cap-run:android", - "dev:webpack": "webpack serve", - "dev:localhost-bind": "adb reverse tcp:5001 tcp:5001" + "dev:start": "run-p dev:webpack cap:android", + "dev:webpack": "webpack serve" }, "devDependencies": { "assert": "^2.1.0", @@ -21,6 +22,7 @@ "cordova-res": "^0.15.4", "crypto-browserify": "^3.12.0", "hybrid-chunk-store": "^1.2.2", + "npm-run-all": "^4.1.5", "path-esm": "^1.0.0", "querystring": "^0.2.1", "stream-browserify": "^3.0.0", diff --git a/capacitor/src/chrome-dgram.js b/capacitor/src/chrome-dgram.js index bddd101..aade1d9 100644 --- a/capacitor/src/chrome-dgram.js +++ b/capacitor/src/chrome-dgram.js @@ -1,3 +1,4 @@ +// @ts-nocheck /*! chrome-dgram. MIT License. Feross Aboukhadijeh */ /* global chrome */ @@ -7,11 +8,7 @@ * * Datagram sockets are available through require('chrome-dgram'). */ - -exports.Socket = Socket - -const EventEmitter = require('events').EventEmitter -const inherits = require('inherits') +const EventEmitter = require('events') const series = require('run-series') const BIND_STATE_UNBOUND = 0 @@ -21,12 +18,10 @@ const BIND_STATE_BOUND = 2 // Track open sockets to route incoming data (via onReceive) to the right handlers. const sockets = {} +const noop = () => {} + // Thorough check for Chrome App since both Edge and Chrome implement dummy chrome object -if ( - typeof chrome === 'object' && - typeof chrome.sockets === 'object' && - typeof chrome.sockets.udp === 'object' -) { +if (typeof chrome?.sockets?.udp === 'object') { chrome.sockets.udp.onReceive.addListener(onReceive) chrome.sockets.udp.onReceiveError.addListener(onReceiveError) } @@ -69,8 +64,6 @@ exports.createSocket = function (type, listener) { return new Socket(type, listener) } -inherits(Socket, EventEmitter) - /** * Class: dgram.Socket * @@ -96,402 +89,377 @@ inherits(Socket, EventEmitter) * - exception Error object * Emitted when an error occurs. */ -function Socket (options, listener) { - const self = this - EventEmitter.call(self) - if (typeof options === 'string') options = { type: options } - if (options.type !== 'udp4') throw new Error('Bad socket type specified. Valid types are: udp4') +class Socket extends EventEmitter { + constructor (options, listener) { + super() + if (typeof options === 'string') options = { type: options } + if (options.type !== 'udp4') throw new Error('Bad socket type specified. Valid types are: udp4') - if (typeof listener === 'function') self.on('message', listener) + if (typeof listener === 'function') this.on('message', listener) - self._destroyed = false - self._bindState = BIND_STATE_UNBOUND - self._bindTasks = [] -} - -/** - * socket.bind(port, [address], [callback]) - * - * For UDP sockets, listen for datagrams on a named port and optional address. - * If address is not specified, the OS will try to listen on all addresses. - * After binding is done, a "listening" event is emitted and the callback(if - * specified) is called. Specifying both a "listening" event listener and - * callback is not harmful but not very useful. - * - * A bound datagram socket keeps the node process running to receive - * datagrams. - * - * If binding fails, an "error" event is generated. In rare case (e.g. binding - * a closed socket), an Error may be thrown by this method. - * - * @param {number} port - * @param {string} address Optional - * @param {function} callback Function with no parameters, Optional. Callback - * when binding is done. - */ -Socket.prototype.bind = function (port, address, callback) { - const self = this - if (typeof address === 'function') { - callback = address - address = undefined + this._destroyed = false + this._bindState = BIND_STATE_UNBOUND + this._bindTasks = [] } - if (!address) address = '0.0.0.0' + /** + * socket.bind(port, [address], [callback]) + * + * For UDP sockets, listen for datagrams on a named port and optional address. + * If address is not specified, the OS will try to listen on all addresses. + * After binding is done, a "listening" event is emitted and the callback(if + * specified) is called. Specifying both a "listening" event listener and + * callback is not harmful but not very useful. + * + * A bound datagram socket keeps the node process running to receive + * datagrams. + * + * If binding fails, an "error" event is generated. In rare case (e.g. binding + * a closed socket), an Error may be thrown by this method. + * + * @param {number} port + * @param {string} address Optional + * @param {function} callback Function with no parameters, Optional. Callback + * when binding is done. + */ + bind (port, address, callback) { + if (typeof address === 'function') { + callback = address + address = undefined + } - if (!port) port = 0 + if (!address) address = '0.0.0.0' - if (self._bindState !== BIND_STATE_UNBOUND) throw new Error('Socket is already bound') + if (!port) port = 0 - self._bindState = BIND_STATE_BINDING + if (this._bindState !== BIND_STATE_UNBOUND) throw new Error('Socket is already bound') - if (typeof callback === 'function') self.once('listening', callback) + this._bindState = BIND_STATE_BINDING - chrome.sockets.udp.create(function (createInfo) { - self.id = createInfo.socketId + if (typeof callback === 'function') this.once('listening', callback) - sockets[self.id] = self + chrome.sockets.udp.create(createInfo => { + this.id = createInfo.socketId - const bindFns = self._bindTasks.map(function (t) { return t.fn }) + sockets[this.id] = this - series(bindFns, function (err) { - if (err) return self.emit('error', err) - chrome.sockets.udp.bind(self.id, address, port, function (result) { - if (result < 0) { - self.emit('error', new Error('Socket ' + self.id + ' failed to bind. ' + - chrome.runtime.lastError.message)) - return - } - chrome.sockets.udp.getInfo(self.id, function (socketInfo) { - if (!socketInfo.localPort || !socketInfo.localAddress) { - self.emit('error', new Error('Cannot get local port/address for Socket ' + self.id)) + const bindFns = this._bindTasks.map(({ fn }) => fn) + + series(bindFns, err => { + if (err) return this.emit('error', err) + chrome.sockets.udp.bind(this.id, address, port, result => { + if (result < 0) { + this.emit('error', new Error('Socket ' + this.id + ' failed to bind. ' + + chrome.runtime.lastError.message)) return } + chrome.sockets.udp.getInfo(this.id, socketInfo => { + if (!socketInfo.localPort || !socketInfo.localAddress) { + this.emit('error', new Error('Cannot get local port/address for Socket ' + this.id)) + return + } - self._port = socketInfo.localPort - self._address = socketInfo.localAddress + this._port = socketInfo.localPort + this._address = socketInfo.localAddress - self._bindState = BIND_STATE_BOUND - self.emit('listening') + this._bindState = BIND_STATE_BOUND + this.emit('listening') - self._bindTasks.map(function (t) { - t.callback() + for (const t of this._bindTasks) { + t.callback() + } }) }) }) }) - }) -} - -/** - * Internal function to receive new messages and emit `message` events. - */ -Socket.prototype._onReceive = function (info) { - const self = this - - const buf = Buffer.from(new Uint8Array(info.data)) - const rinfo = { - address: info.remoteAddress, - family: 'IPv4', - port: info.remotePort, - size: buf.length - } - self.emit('message', buf, rinfo) -} - -Socket.prototype._onReceiveError = function (resultCode) { - const self = this - self.emit('error', new Error('Socket ' + self.id + ' receive error ' + resultCode)) -} - -/** - * socket.send(buf, offset, length, port, address, [callback]) - * - * For UDP sockets, the destination port and IP address must be - * specified. A string may be supplied for the address parameter, and it will - * be resolved with DNS. An optional callback may be specified to detect any - * DNS errors and when buf may be re-used. Note that DNS lookups will delay - * the time that a send takes place, at least until the next tick. The only - * way to know for sure that a send has taken place is to use the callback. - * - * If the socket has not been previously bound with a call to bind, it's - * assigned a random port number and bound to the "all interfaces" address - * (0.0.0.0 for udp4 sockets, ::0 for udp6 sockets). - * - * @param {Buffer|Arrayish|string} buf Message to be sent - * @param {number} offset Offset in the buffer where the message starts. Optional. - * @param {number} length Number of bytes in the message. Optional. - * @param {number} port destination port - * @param {string} address destination IP - * @param {function} callback Callback when message is done being delivered. - * Optional. - * - * Valid combinations: - * send(buffer, offset, length, port, address, callback) - * send(buffer, offset, length, port, address) - * send(buffer, offset, length, port) - * send(bufferOrList, port, address, callback) - * send(bufferOrList, port, address) - * send(bufferOrList, port) - * - */ -Socket.prototype.send = function (buffer, offset, length, port, address, callback) { - const self = this - - let list - - if (address || (port && typeof port !== 'function')) { - buffer = sliceBuffer(buffer, offset, length) - } else { - callback = port - port = offset - address = length } - if (!Array.isArray(buffer)) { - if (typeof buffer === 'string') { - list = [Buffer.from(buffer)] - } else if (!(buffer instanceof Uint8Array)) { - throw new TypeError('First argument must be a buffer or a string') - } else { - list = [Buffer.from(buffer)] + /** + * Internal function to receive new messages and emit `message` events. + */ + _onReceive (info) { + const buf = new Uint8Array(info.data) + const rinfo = { + address: info.remoteAddress, + family: 'IPv4', + port: info.remotePort, + size: buf.length } - } else if (!(list = fixBufferList(buffer))) { - throw new TypeError('Buffer list arguments must be buffers or strings') + this.emit('message', buf, rinfo) } - port = port >>> 0 - if (port === 0 || port > 65535) { - throw new RangeError('Port should be > 0 and < 65536') + _onReceiveError (resultCode) { + this.emit('error', new Error('Socket ' + this.id + ' receive error ' + resultCode)) } - // Normalize callback so it's always a function - if (typeof callback !== 'function') { - callback = function () {} + /** + * socket.send(buf, offset, length, port, address, [callback]) + * + * For UDP sockets, the destination port and IP address must be + * specified. A string may be supplied for the address parameter, and it will + * be resolved with DNS. An optional callback may be specified to detect any + * DNS errors and when buf may be re-used. Note that DNS lookups will delay + * the time that a send takes place, at least until the next tick. The only + * way to know for sure that a send has taken place is to use the callback. + * + * If the socket has not been previously bound with a call to bind, it's + * assigned a random port number and bound to the "all interfaces" address + * (0.0.0.0 for udp4 sockets, ::0 for udp6 sockets). + * + * @param {Buffer|Arrayish|string} buf Message to be sent + * @param {number} offset Offset in the buffer where the message starts. Optional. + * @param {number} length Number of bytes in the message. Optional. + * @param {number} port destination port + * @param {string} address destination IP + * @param {function} callback Callback when message is done being delivered. + * Optional. + * + * Valid combinations: + * send(buffer, offset, length, port, address, callback) + * send(buffer, offset, length, port, address) + * send(buffer, offset, length, port) + * send(bufferOrList, port, address, callback) + * send(bufferOrList, port, address) + * send(bufferOrList, port) + * + */ + send (buffer, offset, length, port, address, callback = noop) { + let list + + if (address || (port && typeof port !== 'function')) { + buffer = this.sliceBuffer(buffer, offset, length) + } else { + callback = port + port = offset + address = length + } + + if (!Array.isArray(buffer)) { + if (typeof buffer === 'string') { + list = [Buffer.from(buffer)] + } else if (!(buffer instanceof Uint8Array)) { + throw new TypeError('First argument must be a buffer or a string') + } else { + list = [Buffer.from(buffer)] + } + } else if (!(list = this.fixBufferList(buffer))) { + throw new TypeError('Buffer list arguments must be buffers or strings') + } + + port = port >>> 0 + if (port === 0 || port > 65535) { + throw new RangeError('Port should be > 0 and < 65536') + } + + if (this._bindState === BIND_STATE_UNBOUND) this.bind(0) + + // If the socket hasn't been bound yet, push the outbound packet onto the + // send queue and send after binding is complete. + if (this._bindState !== BIND_STATE_BOUND) { + // If the send queue hasn't been initialized yet, do it, and install an + // event handler that flishes the send queue after binding is done. + if (!this._sendQueue) { + this._sendQueue = [] + this.once('listening', () => { + // Flush the send queue. + for (let i = 0; i < this._sendQueue.length; i++) { + this.send.apply(this, this._sendQueue[i]) + } + this._sendQueue = undefined + }) + } + this._sendQueue.push([buffer, offset, length, port, address, callback]) + return + } + + const ab = Buffer.concat(list).buffer + + chrome.sockets.udp.send(this.id, ab, address, port, sendInfo => { + if (sendInfo.resultCode < 0) { + const err = new Error('Socket ' + this.id + ' send error ' + sendInfo.resultCode) + callback(err) + this.emit('error', err) + } else { + callback(null) + } + }) } - if (self._bindState === BIND_STATE_UNBOUND) self.bind(0) + sliceBuffer (buffer, offset, length) { + buffer = Buffer.from(buffer) - // If the socket hasn't been bound yet, push the outbound packet onto the - // send queue and send after binding is complete. - if (self._bindState !== BIND_STATE_BOUND) { - // If the send queue hasn't been initialized yet, do it, and install an - // event handler that flishes the send queue after binding is done. - if (!self._sendQueue) { - self._sendQueue = [] - self.once('listening', function () { - // Flush the send queue. - for (let i = 0; i < self._sendQueue.length; i++) { - self.send.apply(self, self._sendQueue[i]) - } - self._sendQueue = undefined + offset = offset >>> 0 + length = length >>> 0 + + // assuming buffer is browser implementation (`buffer` package on npm) + let buf = buffer.buffer + if (buffer.byteOffset || buffer.byteLength !== buf.byteLength) { + buf = buf.slice(buffer.byteOffset, buffer.byteOffset + buffer.byteLength) + } + if (offset || length !== buffer.length) { + buf = buf.slice(offset, length) + } + + return Buffer.from(buf) + } + + fixBufferList (list) { + const newlist = new Array(list.length) + + for (let i = 0, l = list.length; i < l; i++) { + const buf = list[i] + if (typeof buf === 'string') { + newlist[i] = Buffer.from(buf) + } else if (!(buf instanceof Uint8Array)) { + return null + } else { + newlist[i] = Buffer.from(buf) + } + } + + return newlist + } + + /** + * Close the underlying socket and stop listening for data on it. + */ + close () { + if (this._destroyed) return + + delete sockets[this.id] + chrome.sockets.udp.close(this.id) + this._destroyed = true + + this.emit('close') + } + + /** + * Returns an object containing the address information for a socket. For UDP + * sockets, this object will contain address, family and port. + * + * @return {Object} information + */ + address () { + return { + address: this._address, + port: this._port, + family: 'IPv4' + } + } + + setBroadcast (flag) { + // No chrome.sockets equivalent + } + + setTTL (ttl) { + // No chrome.sockets equivalent + } + + // NOTE: Multicast code is untested. Pull requests accepted for bug fixes and to + // add tests! + /** + * Sets the IP_MULTICAST_TTL socket option. TTL stands for "Time to Live," but + * in this context it specifies the number of IP hops that a packet is allowed + * to go through, specifically for multicast traffic. Each router or gateway + * that forwards a packet decrements the TTL. If the TTL is decremented to 0 + * by a router, it will not be forwarded. + * + * The argument to setMulticastTTL() is a number of hops between 0 and 255. + * The default on most systems is 1. + * + * NOTE: The Chrome version of this function is async, whereas the node + * version is sync. Keep this in mind. + * + * @param {number} ttl + * @param {function} callback CHROME-SPECIFIC: Called when the configuration + * operation is done. + */ + setMulticastTTL (ttl, callback = noop) { + const setMulticastTTL = callback => { + chrome.sockets.udp.setMulticastTimeToLive(this.id, ttl, callback) + } + if (this._bindState === BIND_STATE_BOUND) { + setMulticastTTL(callback) + } else { + this._bindTasks.push({ + fn: setMulticastTTL, + callback }) } - self._sendQueue.push([buffer, offset, length, port, address, callback]) - return } - const ab = Buffer.concat(list).buffer - - chrome.sockets.udp.send(self.id, ab, address, port, function (sendInfo) { - if (sendInfo.resultCode < 0) { - const err = new Error('Socket ' + self.id + ' send error ' + sendInfo.resultCode) - callback(err) - self.emit('error', err) - } else { - callback(null) + /** + * Sets or clears the IP_MULTICAST_LOOP socket option. When this option is + * set, multicast packets will also be received on the local interface. + * + * NOTE: The Chrome version of this function is async, whereas the node + * version is sync. Keep this in mind. + * + * @param {boolean} flag + * @param {function} callback CHROME-SPECIFIC: Called when the configuration + * operation is done. + */ + setMulticastLoopback (flag, callback = noop) { + const setMulticastLoopback = callback => { + chrome.sockets.udp.setMulticastLoopbackMode(this.id, flag, callback) } - }) -} - -function sliceBuffer (buffer, offset, length) { - buffer = Buffer.from(buffer) - - offset = offset >>> 0 - length = length >>> 0 - - // assuming buffer is browser implementation (`buffer` package on npm) - let buf = buffer.buffer - if (buffer.byteOffset || buffer.byteLength !== buf.byteLength) { - buf = buf.slice(buffer.byteOffset, buffer.byteOffset + buffer.byteLength) - } - if (offset || length !== buffer.length) { - buf = buf.slice(offset, length) - } - - return Buffer.from(buf) -} - -function fixBufferList (list) { - const newlist = new Array(list.length) - - for (let i = 0, l = list.length; i < l; i++) { - const buf = list[i] - if (typeof buf === 'string') { - newlist[i] = Buffer.from(buf) - } else if (!(buf instanceof Uint8Array)) { - return null + if (this._bindState === BIND_STATE_BOUND) { + setMulticastLoopback(callback) } else { - newlist[i] = Buffer.from(buf) + this._bindTasks.push({ + fn: setMulticastLoopback, + callback + }) } } - return newlist -} + /** + * Tells the kernel to join a multicast group with IP_ADD_MEMBERSHIP socket + * option. + * + * If multicastInterface is not specified, the OS will try to add membership + * to all valid interfaces. + * + * NOTE: The Chrome version of this function is async, whereas the node + * version is sync. Keep this in mind. + * + * @param {string} multicastAddress + * @param {string} [multicastInterface] Optional + * @param {function} callback CHROME-SPECIFIC: Called when the configuration + * operation is done. + */ + addMembership (multicastAddress, multicastInterface, callback = noop) { + chrome.sockets.udp.joinGroup(this.id, multicastAddress, callback) + } -/** - * Close the underlying socket and stop listening for data on it. - */ -Socket.prototype.close = function () { - const self = this - if (self._destroyed) return + /** + * Opposite of addMembership - tells the kernel to leave a multicast group + * with IP_DROP_MEMBERSHIP socket option. This is automatically called by the + * kernel when the socket is closed or process terminates, so most apps will + * never need to call this. + * + * NOTE: The Chrome version of this function is async, whereas the node + * version is sync. Keep this in mind. + * + * If multicastInterface is not specified, the OS will try to drop membership + * to all valid interfaces. + * + * @param {[type]} multicastAddress + * @param {[type]} multicastInterface Optional + * @param {function} callback CHROME-SPECIFIC: Called when the configuration + * operation is done. + */ + dropMembership (multicastAddress, multicastInterface, callback = noop) { + chrome.sockets.udp.leaveGroup(this.id, multicastAddress, callback) + } - delete sockets[self.id] - chrome.sockets.udp.close(self.id) - self._destroyed = true + unref () { + // No chrome.sockets equivalent + } - self.emit('close') -} - -/** - * Returns an object containing the address information for a socket. For UDP - * sockets, this object will contain address, family and port. - * - * @return {Object} information - */ -Socket.prototype.address = function () { - const self = this - return { - address: self._address, - port: self._port, - family: 'IPv4' + ref () { + // No chrome.sockets equivalent } } -Socket.prototype.setBroadcast = function (flag) { - // No chrome.sockets equivalent -} - -Socket.prototype.setTTL = function (ttl) { - // No chrome.sockets equivalent -} - -// NOTE: Multicast code is untested. Pull requests accepted for bug fixes and to -// add tests! - -/** - * Sets the IP_MULTICAST_TTL socket option. TTL stands for "Time to Live," but - * in this context it specifies the number of IP hops that a packet is allowed - * to go through, specifically for multicast traffic. Each router or gateway - * that forwards a packet decrements the TTL. If the TTL is decremented to 0 - * by a router, it will not be forwarded. - * - * The argument to setMulticastTTL() is a number of hops between 0 and 255. - * The default on most systems is 1. - * - * NOTE: The Chrome version of this function is async, whereas the node - * version is sync. Keep this in mind. - * - * @param {number} ttl - * @param {function} callback CHROME-SPECIFIC: Called when the configuration - * operation is done. - */ -Socket.prototype.setMulticastTTL = function (ttl, callback) { - const self = this - if (!callback) callback = function () {} - if (self._bindState === BIND_STATE_BOUND) { - setMulticastTTL(callback) - } else { - self._bindTasks.push({ - fn: setMulticastTTL, - callback - }) - } - - function setMulticastTTL (callback) { - chrome.sockets.udp.setMulticastTimeToLive(self.id, ttl, callback) - } -} - -/** - * Sets or clears the IP_MULTICAST_LOOP socket option. When this option is - * set, multicast packets will also be received on the local interface. - * - * NOTE: The Chrome version of this function is async, whereas the node - * version is sync. Keep this in mind. - * - * @param {boolean} flag - * @param {function} callback CHROME-SPECIFIC: Called when the configuration - * operation is done. - */ -Socket.prototype.setMulticastLoopback = function (flag, callback) { - const self = this - if (!callback) callback = function () {} - if (self._bindState === BIND_STATE_BOUND) { - setMulticastLoopback(callback) - } else { - self._bindTasks.push({ - fn: setMulticastLoopback, - callback - }) - } - - function setMulticastLoopback (callback) { - chrome.sockets.udp.setMulticastLoopbackMode(self.id, flag, callback) - } -} - -/** - * Tells the kernel to join a multicast group with IP_ADD_MEMBERSHIP socket - * option. - * - * If multicastInterface is not specified, the OS will try to add membership - * to all valid interfaces. - * - * NOTE: The Chrome version of this function is async, whereas the node - * version is sync. Keep this in mind. - * - * @param {string} multicastAddress - * @param {string} [multicastInterface] Optional - * @param {function} callback CHROME-SPECIFIC: Called when the configuration - * operation is done. - */ -Socket.prototype.addMembership = function (multicastAddress, - multicastInterface, - callback) { - const self = this - if (!callback) callback = function () {} - chrome.sockets.udp.joinGroup(self.id, multicastAddress, callback) -} - -/** - * Opposite of addMembership - tells the kernel to leave a multicast group - * with IP_DROP_MEMBERSHIP socket option. This is automatically called by the - * kernel when the socket is closed or process terminates, so most apps will - * never need to call this. - * - * NOTE: The Chrome version of this function is async, whereas the node - * version is sync. Keep this in mind. - * - * If multicastInterface is not specified, the OS will try to drop membership - * to all valid interfaces. - * - * @param {[type]} multicastAddress - * @param {[type]} multicastInterface Optional - * @param {function} callback CHROME-SPECIFIC: Called when the configuration - * operation is done. - */ -Socket.prototype.dropMembership = function (multicastAddress, - multicastInterface, - callback) { - const self = this - if (!callback) callback = function () {} - chrome.sockets.udp.leaveGroup(self.id, multicastAddress, callback) -} - -Socket.prototype.unref = function () { - // No chrome.sockets equivalent -} - -Socket.prototype.ref = function () { - // No chrome.sockets equivalent -} +exports.Socket = Socket diff --git a/capacitor/src/chrome-net.js b/capacitor/src/chrome-net.js index ac77cef..235778e 100644 --- a/capacitor/src/chrome-net.js +++ b/capacitor/src/chrome-net.js @@ -1,3 +1,4 @@ +// @ts-nocheck /*! chrome-net. MIT License. Feross Aboukhadijeh */ /* global chrome */ 'use strict' @@ -12,11 +13,9 @@ */ const EventEmitter = require('events') -const inherits = require('inherits') const stream = require('stream') -const deprecate = require('util').deprecate const timers = require('timers') -const Buffer = require('buffer').Buffer +const { Buffer } = require('buffer') // Track open servers and sockets to route incoming sockets (via onAccept and onReceive) // to the right handlers. @@ -24,30 +23,26 @@ const servers = {} const sockets = {} // Thorough check for Chrome App since both Edge and Chrome implement dummy chrome object -if ( - typeof chrome === 'object' && - typeof chrome.sockets === 'object' && - typeof chrome.sockets.tcp === 'object' -) { +if (typeof chrome?.sockets?.tcp === 'object') { chrome.sockets.tcp.onReceive.addListener(onReceive) chrome.sockets.tcp.onReceiveError.addListener(onReceiveError) } -function onAccept (info) { - if (info.socketId in servers) { - servers[info.socketId]._onAccept(info.clientSocketId) - } else { - console.error('Unknown server socket id: ' + info.socketId) - } -} +// function onAccept (info) { +// if (info.socketId in servers) { +// servers[info.socketId]._onAccept(info.clientSocketId) +// } else { +// console.error('Unknown server socket id: ' + info.socketId) +// } +// } -function onAcceptError (info) { - if (info.socketId in servers) { - servers[info.socketId]._onAcceptError(info.resultCode) - } else { - console.error('Unknown server socket id: ' + info.socketId) - } -} +// function onAcceptError (info) { +// if (info.socketId in servers) { +// servers[info.socketId]._onAcceptError(info.resultCode) +// } else { +// console.error('Unknown server socket id: ' + info.socketId) +// } +// } function onReceive (info) { if (info.socketId in sockets) { @@ -104,17 +99,12 @@ exports.createServer = function (options, connectionListener) { * @param {function} listener * @return {Socket} */ -exports.connect = exports.createConnection = function () { - const argsLen = arguments.length - let args = new Array(argsLen) - for (let i = 0; i < argsLen; i++) args[i] = arguments[i] +exports.connect = exports.createConnection = function (...args) { args = normalizeConnectArgs(args) const s = new Socket(args[0]) return Socket.prototype.connect.apply(s, args) } -inherits(Server, EventEmitter) - /** * Class: net.Server * ================= @@ -137,278 +127,313 @@ inherits(Server, EventEmitter) * Emitted when an error occurs. The 'close' event will be called directly * following this event. See example in discussion of server.listen. */ -function Server (options, connectionListener) { - if (!(this instanceof Server)) return new Server(options, connectionListener) - EventEmitter.call(this) +class Server extends EventEmitter { + constructor (options, connectionListener) { + super() - if (typeof options === 'function') { - connectionListener = options - options = {} - this.on('connection', connectionListener) - } else if (options == null || typeof options === 'object') { - options = options || {} - - if (typeof connectionListener === 'function') { + if (typeof options === 'function') { + connectionListener = options + options = {} this.on('connection', connectionListener) - } - } else { - throw new TypeError('options must be an object') - } + } else if (options == null || typeof options === 'object') { + options = options || {} - this._connections = 0 - - Object.defineProperty(this, 'connections', { - get: deprecate(() => this._connections, - 'Server.connections property is deprecated. ' + - 'Use Server.getConnections method instead.'), - set: deprecate((val) => (this._connections = val), - 'Server.connections property is deprecated.'), - configurable: true, - enumerable: false - }) - - this.id = null // a number > 0 - this.connecting = false - - this.allowHalfOpen = options.allowHalfOpen || false - this.pauseOnConnect = !!options.pauseOnConnect - this._address = null - - this._host = null - this._port = null - this._backlog = null -} -exports.Server = Server - -Server.prototype._usingSlaves = false // not used - -/** - * server.listen(port, [host], [backlog], [callback]) - * - * Begin accepting connections on the specified port and host. If the host is - * omitted, the server will accept connections directed to any IPv4 address - * (INADDR_ANY). A port value of zero will assign a random port. - * - * Backlog is the maximum length of the queue of pending connections. The - * actual length will be determined by your OS through sysctl settings such as - * tcp_max_syn_backlog and somaxconn on linux. The default value of this - * parameter is 511 (not 512). - * - * This function is asynchronous. When the server has been bound, 'listening' - * event will be emitted. The last parameter callback will be added as an - * listener for the 'listening' event. - * - * @return {Socket} - */ -Server.prototype.listen = function (/* variable arguments... */) { - const lastArg = arguments[arguments.length - 1] - if (typeof lastArg === 'function') { - this.once('listening', lastArg) - } - - let port = toNumber(arguments[0]) - - let address - - // The third optional argument is the backlog size. - // When the ip is omitted it can be the second argument. - let backlog = toNumber(arguments[1]) || toNumber(arguments[2]) || undefined - - if (arguments[0] !== null && typeof arguments[0] === 'object') { - const h = arguments[0] - - if (h._handle || h.handle) { - throw new Error('handle is not supported in Chrome Apps.') - } - if (typeof h.fd === 'number' && h.fd >= 0) { - throw new Error('fd is not supported in Chrome Apps.') - } - - // The first argument is a configuration object - if (h.backlog) { - backlog = h.backlog - } - - if (typeof h.port === 'number' || typeof h.port === 'string' || - (typeof h.port === 'undefined' && 'port' in h)) { - // Undefined is interpreted as zero (random port) for consistency - // with net.connect(). - address = h.host || null - port = h.port - } else if (h.path && isPipeName(h.path)) { - throw new Error('Pipes are not supported in Chrome Apps.') + if (typeof connectionListener === 'function') { + this.on('connection', connectionListener) + } } else { - throw new Error('Invalid listen argument: ' + h) + throw new TypeError('options must be an object') } - } else if (isPipeName(arguments[0])) { - // UNIX socket or Windows pipe. - throw new Error('Pipes are not supported in Chrome Apps.') - } else if (arguments[1] === undefined || - typeof arguments[1] === 'function' || - typeof arguments[1] === 'number') { - // The first argument is the port, no IP given. - address = null - } else { - // The first argument is the port, the second an IP. - address = arguments[1] + + this._connections = 0 + + this.id = null // a number > 0 + this.connecting = false + + this.allowHalfOpen = options.allowHalfOpen || false + this.pauseOnConnect = !!options.pauseOnConnect + this._address = null + + this._host = null + this._port = null + this._backlog = null } - // now do something with port, address, backlog + get connections () { + return this._connections + } - if (this.id) { + set connections (val) { + this._connections = val + } + + _usingSlaves = false + + /** + * server.listen(port, [host], [backlog], [callback]) + * + * Begin accepting connections on the specified port and host. If the host is + * omitted, the server will accept connections directed to any IPv4 address + * (INADDR_ANY). A port value of zero will assign a random port. + * + * Backlog is the maximum length of the queue of pending connections. The + * actual length will be determined by your OS through sysctl settings such as + * tcp_max_syn_backlog and somaxconn on linux. The default value of this + * parameter is 511 (not 512). + * + * This function is asynchronous. When the server has been bound, 'listening' + * event will be emitted. The last parameter callback will be added as an + * listener for the 'listening' event. + * + * @return {Socket} + */ + listen (/* variable arguments... */) { + const lastArg = arguments[arguments.length - 1] + if (typeof lastArg === 'function') { + this.once('listening', lastArg) + } + + let port = toNumber(arguments[0]) + + let address + + // The third optional argument is the backlog size. + // When the ip is omitted it can be the second argument. + let backlog = toNumber(arguments[1]) || toNumber(arguments[2]) || undefined + + if (arguments[0] !== null && typeof arguments[0] === 'object') { + const h = arguments[0] + + if (h._handle || h.handle) { + throw new Error('handle is not supported in Chrome Apps.') + } + if (typeof h.fd === 'number' && h.fd >= 0) { + throw new Error('fd is not supported in Chrome Apps.') + } + + // The first argument is a configuration object + if (h.backlog) { + backlog = h.backlog + } + + if (typeof h.port === 'number' || typeof h.port === 'string' || + (typeof h.port === 'undefined' && 'port' in h)) { + // Undefined is interpreted as zero (random port) for consistency + // with net.connect(). + address = h.host || null + port = h.port + } else if (h.path && isPipeName(h.path)) { + throw new Error('Pipes are not supported in Chrome Apps.') + } else { + throw new Error('Invalid listen argument: ' + h) + } + } else if (isPipeName(arguments[0])) { + // UNIX socket or Windows pipe. + throw new Error('Pipes are not supported in Chrome Apps.') + } else if (arguments[1] === undefined || + typeof arguments[1] === 'function' || + typeof arguments[1] === 'number') { + // The first argument is the port, no IP given. + address = null + } else { + // The first argument is the port, the second an IP. + address = arguments[1] + } + + // now do something with port, address, backlog + if (this.id) { + this.close() + } + + // If port is invalid or undefined, bind to a random port. + assertPort(port) + this._port = port | 0 + + this._host = address + + let isAny6 = !this._host + if (isAny6) { + this._host = '::' + } + + this._backlog = typeof backlog === 'number' ? backlog : undefined + + this.connecting = true + + if (chrome.sockets?.tcpServer?.create) { + chrome.sockets.tcpServer.create((createInfo) => { + if (!this.connecting || this.id) { + ignoreLastError() + chrome.sockets.tcpServer.close(createInfo.socketId) + return + } + if (chrome.runtime.lastError) { + this.emit('error', new Error(chrome.runtime.lastError.message)) + return + } + const socketId = this.id = createInfo.socketId + servers[this.id] = this + const listen = () => chrome.sockets.tcpServer.listen(this.id, this._host, + this._port, this._backlog, + (result) => { + // callback may be after close + if (this.id !== socketId) { + ignoreLastError() + return + } + if (result !== 0 && isAny6) { + ignoreLastError() + this._host = '0.0.0.0' // try IPv4 + isAny6 = false + return listen() + } + this._onListen(result) + }) + listen() + }) + } else { + this._address = {} + this.emit('listening') + } + + return this + } + + _onListen (result) { + this.connecting = false + + if (result === 0) { + const idBefore = this.id + chrome.sockets.tcpServer.getInfo(this.id, (info) => { + if (this.id !== idBefore) { + ignoreLastError() + return + } + if (chrome.runtime.lastError) { + this._onListen(-2) // net::ERR_FAILED + return + } + + this._address = { + port: info.localPort, + family: info.localAddress && + info.localAddress.indexOf(':') !== -1 + ? 'IPv6' + : 'IPv4', + address: info.localAddress + } + this.emit('listening') + }) + } else { + this.emit('error', exceptionWithHostPort(result, 'listen', this._host, this._port)) + if (this.id) { + chrome.sockets.tcpServer.close(this.id) + delete servers[this.id] + this.id = null + } + } + } + + _onAccept (clientSocketId) { + // Set the `maxConnections` property to reject connections when the server's + // connection count gets high. + if (this.maxConnections && this._connections >= this.maxConnections) { + chrome.sockets.tcp.close(clientSocketId) + console.warn('Rejected connection - hit `maxConnections` limit') + return + } + + this._connections += 1 + + const acceptedSocket = new Socket({ + server: this, + id: clientSocketId, + allowHalfOpen: this.allowHalfOpen, + pauseOnCreate: this.pauseOnConnect + }) + acceptedSocket.on('connect', () => this.emit('connection', acceptedSocket)) + } + + _onAcceptError (resultCode) { + this.emit('error', errnoException(resultCode, 'accept')) this.close() } - // If port is invalid or undefined, bind to a random port. - assertPort(port) - this._port = port | 0 - - this._host = address - - const isAny6 = !this._host - if (isAny6) { - this._host = '::' - } - - this._backlog = typeof backlog === 'number' ? backlog : undefined - - this.connecting = true - - // chrome.sockets.tcpServer.create((createInfo) => { - // if (!this.connecting || this.id) { - // ignoreLastError() - // chrome.sockets.tcpServer.close(createInfo.socketId) - // return - // } - // if (chrome.runtime.lastError) { - // this.emit('error', new Error(chrome.runtime.lastError.message)) - // return - // } - - // const socketId = this.id = createInfo.socketId - // servers[this.id] = this - - // const listen = () => chrome.sockets.tcpServer.listen(this.id, this._host, - // this._port, this._backlog, - // (result) => { - // // callback may be after close - // if (this.id !== socketId) { - // ignoreLastError() - // return - // } - // if (result !== 0 && isAny6) { - // ignoreLastError() - // this._host = '0.0.0.0' // try IPv4 - // isAny6 = false - // return listen() - // } - - // this._onListen(result) - // }) - // listen() - // }) - this._address = {} - this.emit('listening') - - return this -} - -Server.prototype._onListen = function (result) { - this.connecting = false - - if (result === 0) { - const idBefore = this.id - chrome.sockets.tcpServer.getInfo(this.id, (info) => { - if (this.id !== idBefore) { - ignoreLastError() - return - } - if (chrome.runtime.lastError) { - this._onListen(-2) // net::ERR_FAILED - return + /** + * Stops the server from accepting new connections and keeps existing + * connections. This function is asynchronous, the server is finally closed + * when all connections are ended and the server emits a 'close' event. + * Optionally, you can pass a callback to listen for the 'close' event. + * @param {function} callback + */ + close (callback) { + if (typeof callback === 'function') { + if (!this.id) { + this.once('close', () => callback(new Error('Not running'))) + } else { + this.once('close', callback) } + } - this._address = { - port: info.localPort, - family: info.localAddress && - info.localAddress.indexOf(':') !== -1 - ? 'IPv6' - : 'IPv4', - address: info.localAddress - } - this.emit('listening') - }) - } else { - this.emit('error', exceptionWithHostPort(result, 'listen', this._host, this._port)) if (this.id) { chrome.sockets.tcpServer.close(this.id) delete servers[this.id] this.id = null } - } -} + this._address = null + this.connecting = false -Server.prototype._onAccept = function (clientSocketId) { - // Set the `maxConnections` property to reject connections when the server's - // connection count gets high. - if (this.maxConnections && this._connections >= this.maxConnections) { - chrome.sockets.tcp.close(clientSocketId) - console.warn('Rejected connection - hit `maxConnections` limit') - return + this._emitCloseIfDrained() + + return this } - this._connections += 1 - - const acceptedSocket = new Socket({ - server: this, - id: clientSocketId, - allowHalfOpen: this.allowHalfOpen, - pauseOnCreate: this.pauseOnConnect - }) - acceptedSocket.on('connect', () => this.emit('connection', acceptedSocket)) -} - -Server.prototype._onAcceptError = function (resultCode) { - this.emit('error', errnoException(resultCode, 'accept')) - this.close() -} - -/** - * Stops the server from accepting new connections and keeps existing - * connections. This function is asynchronous, the server is finally closed - * when all connections are ended and the server emits a 'close' event. - * Optionally, you can pass a callback to listen for the 'close' event. - * @param {function} callback - */ -Server.prototype.close = function (callback) { - if (typeof callback === 'function') { - if (!this.id) { - this.once('close', () => callback(new Error('Not running'))) - } else { - this.once('close', callback) + _emitCloseIfDrained () { + if (this.id || this.connecting || this._connections) { + return } + + process.nextTick(emitCloseNT, this) } - if (this.id) { - chrome.sockets.tcpServer.close(this.id) - delete servers[this.id] - this.id = null + get listening () { + return !!this._address } - this._address = null - this.connecting = false - this._emitCloseIfDrained() + /** + * Returns the bound address, the address family name and port of the socket + * as reported by the operating system. Returns an object with three + * properties, e.g. { port: 12346, family: 'IPv4', address: '127.0.0.1' } + * + * @return {Object} information + */ + address () { + return this._address + } - return this -} - -Server.prototype._emitCloseIfDrained = function () { - if (this.id || this.connecting || this._connections) { - return - } - - process.nextTick(emitCloseNT, this) + ref () { + // No chrome.socket equivalent + return this + } + + unref () { + // No chrome.socket equivalent + return this + } + + /** + * Asynchronously get the number of concurrent connections on the server. + * Works when sockets were sent to forks. + * + * Callback should take two arguments err and count. + * + * @param {function} callback + */ + getConnections (callback) { + process.nextTick(callback, null, this._connections) + } } +exports.Server = Server function emitCloseNT (self) { if (self.id || self.connecting || self._connections) { @@ -417,45 +442,6 @@ function emitCloseNT (self) { self.emit('close') } -Object.defineProperty(Server.prototype, 'listening', { - get: function () { - return !!this._address - }, - configurable: true, - enumerable: true -}) - -/** - * Returns the bound address, the address family name and port of the socket - * as reported by the operating system. Returns an object with three - * properties, e.g. { port: 12346, family: 'IPv4', address: '127.0.0.1' } - * - * @return {Object} information - */ -Server.prototype.address = function () { - return this._address -} - -Server.prototype.unref = -Server.prototype.ref = function () { - // No chrome.socket equivalent - return this -} - -/** - * Asynchronously get the number of concurrent connections on the server. - * Works when sockets were sent to forks. - * - * Callback should take two arguments err and count. - * - * @param {function} callback - */ -Server.prototype.getConnections = function (callback) { - process.nextTick(callback, null, this._connections) -} - -inherits(Socket, stream.Duplex) - /** * Class: net.Socket * ================= @@ -520,526 +506,511 @@ inherits(Socket, stream.Duplex) * Emitted once the socket is fully closed. The argument had_error is a * boolean which says if the socket was closed due to a transmission error. */ -function Socket (options) { - if (!(this instanceof Socket)) return new Socket(options) - - if (typeof options === 'number') { - options = { fd: options } // Legacy interface. - } else if (options === undefined) { - options = {} - } - - if (options.handle) { - throw new Error('handle is not supported in Chrome Apps.') - } else if (options.fd !== undefined) { - throw new Error('fd is not supported in Chrome Apps.') - } - - options.decodeStrings = true - options.objectMode = false - stream.Duplex.call(this, options) - - this.destroyed = false - this._hadError = false // Used by _http_client.js - this.id = null // a number > 0 - this._parent = null - this._host = null - this._port = null - this._pendingData = null - - this.ondata = null - this.onend = null - - this._init() - this._reset() - - // default to *not* allowing half open sockets - // Note: this is not possible in Chrome Apps, see https://crbug.com/124952 - this.allowHalfOpen = options.allowHalfOpen || false - - // shut down the socket when we're finished with it. - this.on('finish', this.destroy) - - if (options.server) { - this.server = this._server = options.server - this.id = options.id - sockets[this.id] = this - - if (options.pauseOnCreate) { - // stop the handle from reading and pause the stream - // (Already paused in Chrome version) - this._readableState.flowing = false +class Socket extends stream.Duplex { + constructor (options) { + if (typeof options === 'number') { + options = { fd: options } // Legacy interface. + } else if (options === undefined) { + options = {} + } + + if (options.handle) { + throw new Error('handle is not supported in Chrome Apps.') + } else if (options.fd !== undefined) { + throw new Error('fd is not supported in Chrome Apps.') + } + + options.decodeStrings = true + options.objectMode = false + super(options) + + this.destroyed = false + this._hadError = false // Used by _http_client.js + this.id = null // a number > 0 + this._parent = null + this._host = null + this._port = null + this._pendingData = null + + this.ondata = null + this.onend = null + + this._init() + this._reset() + + // default to *not* allowing half open sockets + // Note: this is not possible in Chrome Apps, see https://crbug.com/124952 + this.allowHalfOpen = options.allowHalfOpen || false + + // shut down the socket when we're finished with it. + this.on('finish', this.destroy) + + if (options.server) { + this.server = this._server = options.server + this.id = options.id + sockets[this.id] = this + + if (options.pauseOnCreate) { + // stop the handle from reading and pause the stream + // (Already paused in Chrome version) + this._readableState.flowing = false + } + + // For incoming sockets (from server), it's already connected. + this.connecting = true + this.writable = true + this._onConnect() + } + } + + // called when creating new Socket, or when re-using a closed Socket + _init () { + // The amount of received bytes. + this.bytesRead = 0 + + this._bytesDispatched = 0 + + // Reserve properties + this.server = null + this._server = null + } + + // called when creating new Socket, or when closing a Socket + _reset () { + this.remoteAddress = this.remotePort = + this.localAddress = this.localPort = null + this.remoteFamily = 'IPv4' + this.readable = this.writable = false + this.connecting = false + } + + /** + * socket.connect(port, [host], [connectListener]) + * socket.connect(options, [connectListener]) + * + * Opens the connection for a given socket. If port and host are given, then + * the socket will be opened as a TCP socket, if host is omitted, localhost + * will be assumed. If a path is given, the socket will be opened as a unix + * socket to that path. + * + * Normally this method is not needed, as net.createConnection opens the + * socket. Use this only if you are implementing a custom Socket. + * + * This function is asynchronous. When the 'connect' event is emitted the + * socket is established. If there is a problem connecting, the 'connect' + * event will not be emitted, the 'error' event will be emitted with the + * exception. + * + * The connectListener parameter will be added as an listener for the + * 'connect' event. + * + * @param {Object} options + * @param {function} cb + * @return {Socket} this socket (for chaining) + */ + connect () { + const argsLen = arguments.length + let args = new Array(argsLen) + for (let i = 0; i < argsLen; i++) args[i] = arguments[i] + args = normalizeConnectArgs(args) + const options = args[0] + const cb = args[1] + + if (options.path) { + throw new Error('Pipes are not supported in Chrome Apps.') + } + + if (this.id) { + // already connected, destroy and connect again + this.destroy() + } + + if (this.destroyed) { + this._readableState.reading = false + this._readableState.ended = false + this._readableState.endEmitted = false + this._writableState.ended = false + this._writableState.ending = false + this._writableState.finished = false + this._writableState.errorEmitted = false + this._writableState.length = 0 + this.destroyed = false } - // For incoming sockets (from server), it's already connected. this.connecting = true this.writable = true - this._onConnect() - } -} -exports.Socket = Socket -// called when creating new Socket, or when re-using a closed Socket -Socket.prototype._init = function () { - // The amount of received bytes. - this.bytesRead = 0 + this._host = options.host || 'localhost' + this._port = options.port - this._bytesDispatched = 0 - - // Reserve properties - this.server = null - this._server = null -} - -// called when creating new Socket, or when closing a Socket -Socket.prototype._reset = function () { - this.remoteAddress = this.remotePort = - this.localAddress = this.localPort = null - this.remoteFamily = 'IPv4' - this.readable = this.writable = false - this.connecting = false -} - -/** - * socket.connect(port, [host], [connectListener]) - * socket.connect(options, [connectListener]) - * - * Opens the connection for a given socket. If port and host are given, then - * the socket will be opened as a TCP socket, if host is omitted, localhost - * will be assumed. If a path is given, the socket will be opened as a unix - * socket to that path. - * - * Normally this method is not needed, as net.createConnection opens the - * socket. Use this only if you are implementing a custom Socket. - * - * This function is asynchronous. When the 'connect' event is emitted the - * socket is established. If there is a problem connecting, the 'connect' - * event will not be emitted, the 'error' event will be emitted with the - * exception. - * - * The connectListener parameter will be added as an listener for the - * 'connect' event. - * - * @param {Object} options - * @param {function} cb - * @return {Socket} this socket (for chaining) - */ -Socket.prototype.connect = function () { - const argsLen = arguments.length - let args = new Array(argsLen) - for (let i = 0; i < argsLen; i++) args[i] = arguments[i] - args = normalizeConnectArgs(args) - const options = args[0] - const cb = args[1] - - if (options.path) { - throw new Error('Pipes are not supported in Chrome Apps.') - } - - if (this.id) { - // already connected, destroy and connect again - this.destroy() - } - - if (this.destroyed) { - this._readableState.reading = false - this._readableState.ended = false - this._readableState.endEmitted = false - this._writableState.ended = false - this._writableState.ending = false - this._writableState.finished = false - this._writableState.errorEmitted = false - this._writableState.length = 0 - this.destroyed = false - } - - this.connecting = true - this.writable = true - - this._host = options.host || 'localhost' - this._port = options.port - - if (typeof this._port !== 'undefined') { - if (typeof this._port !== 'number' && typeof this._port !== 'string') { - throw new TypeError('"port" option should be a number or string: ' + this._port) + if (typeof this._port !== 'undefined') { + if (typeof this._port !== 'number' && typeof this._port !== 'string') { + throw new TypeError('"port" option should be a number or string: ' + this._port) + } + if (!isLegalPort(this._port)) { + throw new RangeError('"port" option should be >= 0 and < 65536: ' + this._port) + } } - if (!isLegalPort(this._port)) { - throw new RangeError('"port" option should be >= 0 and < 65536: ' + this._port) - } - } - this._port |= 0 + this._port |= 0 - this._init() + this._init() - this._unrefTimer() + this._unrefTimer() - if (typeof cb === 'function') { - this.once('connect', cb) - } - - chrome.sockets.tcp.create((createInfo) => { - if (!this.connecting || this.id) { - ignoreLastError() - chrome.sockets.tcp.close(createInfo.socketId) - return - } - if (chrome.runtime.lastError) { - this.destroy(new Error(chrome.runtime.lastError.message)) - return + if (typeof cb === 'function') { + this.once('connect', cb) } - this.id = createInfo.socketId - sockets[this.id] = this + chrome.sockets.tcp.create((createInfo) => { + if (!this.connecting || this.id) { + ignoreLastError() + chrome.sockets.tcp.close(createInfo.socketId) + return + } + if (chrome.runtime.lastError) { + this.destroy(new Error(chrome.runtime.lastError.message)) + return + } - chrome.sockets.tcp.setPaused(this.id, true) + this.id = createInfo.socketId + sockets[this.id] = this - chrome.sockets.tcp.connect(this.id, this._host, this._port, (result) => { - // callback may come after call to destroy - if (this.id !== createInfo.socketId) { + chrome.sockets.tcp.setPaused(this.id, true) + + chrome.sockets.tcp.connect(this.id, this._host, this._port, (result) => { + // callback may come after call to destroy + if (this.id !== createInfo.socketId) { + ignoreLastError() + return + } + if (result !== 0) { + this.destroy(exceptionWithHostPort(result, 'connect', this._host, this._port)) + return + } + + this._unrefTimer() + this._onConnect() + }) + }) + + return this + } + + _onConnect () { + const idBefore = this.id + chrome.sockets.tcp.getInfo(this.id, (result) => { + if (this.id !== idBefore) { ignoreLastError() return } - if (result !== 0) { - this.destroy(exceptionWithHostPort(result, 'connect', this._host, this._port)) + if (chrome.runtime.lastError) { + this.destroy(new Error(chrome.runtime.lastError.message)) return } - this._unrefTimer() - this._onConnect() - }) - }) - - return this -} - -Socket.prototype._onConnect = function () { - const idBefore = this.id - chrome.sockets.tcp.getInfo(this.id, (result) => { - if (this.id !== idBefore) { - ignoreLastError() - return - } - if (chrome.runtime.lastError) { - this.destroy(new Error(chrome.runtime.lastError.message)) - return - } - - this.remoteAddress = result.peerAddress - this.remoteFamily = result.peerAddress && + this.remoteAddress = result.peerAddress + this.remoteFamily = result.peerAddress && result.peerAddress.indexOf(':') !== -1 - ? 'IPv6' - : 'IPv4' - this.remotePort = result.peerPort - this.localAddress = result.localAddress - this.localPort = result.localPort + ? 'IPv6' + : 'IPv4' + this.remotePort = result.peerPort + this.localAddress = result.localAddress + this.localPort = result.localPort - this.connecting = false - this.readable = true + this.connecting = false + this.readable = true - this.emit('connect') - // start the first read, or get an immediate EOF. - // this doesn't actually consume any bytes, because len=0 - if (!this.isPaused()) this.read(0) - }) -} + this.emit('connect') + // start the first read, or get an immediate EOF. + // this doesn't actually consume any bytes, because len=0 + if (!this.isPaused()) this.read(0) + }) + } -/** - * The number of characters currently buffered to be written. - * @type {number} - */ -Object.defineProperty(Socket.prototype, 'bufferSize', { - get: function () { + /** + * The number of characters currently buffered to be written. + * @type {number} + */ + get bufferSize () { if (this.id) { let bytes = this._writableState.length if (this._pendingData) bytes += this._pendingData.length return bytes } } -}) -Socket.prototype.end = function (data, encoding) { - stream.Duplex.prototype.end.call(this, data, encoding) - this.writable = false -} - -Socket.prototype._write = function (chunk, encoding, callback) { - if (!callback) callback = () => {} - - if (this.connecting) { - this._pendingData = chunk - this.once('connect', () => this._write(chunk, encoding, callback)) - return - } - this._pendingData = null - - if (!this.id) { - callback(new Error('This socket is closed')) - return + end (data, encoding) { + stream.Duplex.prototype.end.call(this, data, encoding) + this.writable = false } - // assuming buffer is browser implementation (`buffer` package on npm) - let buffer = chunk.buffer - if (chunk.byteLength !== buffer.byteLength) { - buffer = buffer.slice(chunk.byteOffset, chunk.byteOffset + chunk.byteLength) - } + _write (chunk, encoding, callback) { + if (!callback) callback = () => { } - const idBefore = this.id - chrome.sockets.tcp.send(this.id, buffer, (sendInfo) => { - if (this.id !== idBefore) { - ignoreLastError() + if (this.connecting) { + this._pendingData = chunk + this.once('connect', () => this._write(chunk, encoding, callback)) + return + } + this._pendingData = null + + if (!this.id) { + callback(new Error('This socket is closed')) return } - if (sendInfo.resultCode < 0) { - this._destroy(exceptionWithHostPort(sendInfo.resultCode, 'write', this.remoteAddress, this.remotePort), callback) - } else { - this._unrefTimer() - callback(null) + // assuming buffer is browser implementation (`buffer` package on npm) + let buffer = chunk.buffer + if (chunk.byteLength !== buffer.byteLength) { + buffer = buffer.slice(chunk.byteOffset, chunk.byteOffset + chunk.byteLength) } - }) - this._bytesDispatched += chunk.length -} + const idBefore = this.id + chrome.sockets.tcp.send(this.id, buffer, (sendInfo) => { + if (this.id !== idBefore) { + ignoreLastError() + return + } -Socket.prototype._read = function (bufferSize) { - if (this.connecting || !this.id) { - this.once('connect', () => this._read(bufferSize)) - return - } - - chrome.sockets.tcp.setPaused(this.id, false) - - const idBefore = this.id - chrome.sockets.tcp.getInfo(this.id, (result) => { - if (this.id !== idBefore) { - ignoreLastError() - return - } - if (chrome.runtime.lastError || !result.connected) { - this._onReceiveError(-15) // workaround for https://crbug.com/518161 - } - }) -} - -Socket.prototype._onReceive = function (data) { - const buffer = Buffer.from(data) - const offset = this.bytesRead - - this.bytesRead += buffer.length - this._unrefTimer() - - if (this.ondata) { - console.error('socket.ondata = func is non-standard, use socket.on(\'data\', func)') - this.ondata(buffer, offset, this.bytesRead) - } - if (!this.push(buffer)) { // if returns false, then apply backpressure - chrome.sockets.tcp.setPaused(this.id, true) - } -} - -Socket.prototype._onReceiveError = function (resultCode) { - if (resultCode === -100) { // net::ERR_CONNECTION_CLOSED - if (this.onend) { - console.error('socket.onend = func is non-standard, use socket.on(\'end\', func)') - this.once('end', this.onend) - } - this.push(null) - this.destroy() - } else if (resultCode < 0) { - this.destroy(errnoException(resultCode, 'read')) - } -} - -function protoGetter (name, callback) { - Object.defineProperty(Socket.prototype, name, { - configurable: false, - enumerable: true, - get: callback - }) -} - -/** - * The amount of bytes sent. - * @return {number} - */ -protoGetter('bytesWritten', function bytesWritten () { - if (this.id) return this._bytesDispatched + this.bufferSize -}) - -Socket.prototype.destroy = function (exception) { - this._destroy(exception) -} - -Socket.prototype._destroy = function (exception, cb) { - const fireErrorCallbacks = () => { - if (cb) cb(exception) - if (exception && !this._writableState.errorEmitted) { - process.nextTick(emitErrorNT, this, exception) - this._writableState.errorEmitted = true - } - } - - if (this.destroyed) { - // already destroyed, fire error callbacks - fireErrorCallbacks() - return - } - - if (this._server) { - this._server._connections -= 1 - if (this._server._emitCloseIfDrained) this._server._emitCloseIfDrained() - } - - this._reset() - - for (let s = this; s !== null; s = s._parent) timers.unenroll(s) // eslint-disable-line node/no-deprecated-api - - this.destroyed = true - - // If _destroy() has been called before chrome.sockets.tcp.create() - // callback, we don't have an id. Therefore we don't need to close - // or disconnect - if (this.id) { - delete sockets[this.id] - chrome.sockets.tcp.close(this.id, () => { - if (this.destroyed) { - this.emit('close', !!exception) + if (sendInfo.resultCode < 0) { + this._destroy(exceptionWithHostPort(sendInfo.resultCode, 'write', this.remoteAddress, this.remotePort), callback) + } else { + this._unrefTimer() + callback(null) } }) - this.id = null + + this._bytesDispatched += chunk.length } - fireErrorCallbacks() -} - -Socket.prototype.destroySoon = function () { - if (this.writable) this.end() - - if (this._writableState.finished) this.destroy() -} - -/** - * Sets the socket to timeout after timeout milliseconds of inactivity on the socket. - * By default net.Socket do not have a timeout. When an idle timeout is triggered the - * socket will receive a 'timeout' event but the connection will not be severed. The - * user must manually end() or destroy() the socket. - * - * If timeout is 0, then the existing idle timeout is disabled. - * - * The optional callback parameter will be added as a one time listener for the 'timeout' event. - * - * @param {number} timeout - * @param {function} callback - */ -Socket.prototype.setTimeout = function (timeout, callback) { - if (timeout === 0) { - timers.unenroll(this) // eslint-disable-line node/no-deprecated-api - if (callback) { - this.removeListener('timeout', callback) + _read (bufferSize) { + if (this.connecting || !this.id) { + this.once('connect', () => this._read(bufferSize)) + return } - } else { - timers.enroll(this, timeout) // eslint-disable-line node/no-deprecated-api - timers._unrefActive(this) - if (callback) { - this.once('timeout', callback) + + chrome.sockets.tcp.setPaused(this.id, false) + + const idBefore = this.id + chrome.sockets.tcp.getInfo(this.id, (result) => { + if (this.id !== idBefore) { + ignoreLastError() + return + } + if (chrome.runtime.lastError || !result.connected) { + this._onReceiveError(-15) // workaround for https://crbug.com/518161 + } + }) + } + + _onReceive (data) { + const buffer = Buffer.from(data) + const offset = this.bytesRead + + this.bytesRead += buffer.length + this._unrefTimer() + + if (this.ondata) { + console.error('socket.ondata = func is non-standard, use socket.on(\'data\', func)') + this.ondata(buffer, offset, this.bytesRead) + } + if (!this.push(buffer)) { // if returns false, then apply backpressure + chrome.sockets.tcp.setPaused(this.id, true) } } - return this -} - -Socket.prototype._onTimeout = function () { - this.emit('timeout') -} - -Socket.prototype._unrefTimer = function unrefTimer () { - for (let s = this; s !== null; s = s._parent) { - timers._unrefActive(s) + _onReceiveError (resultCode) { + if (resultCode === -100) { // net::ERR_CONNECTION_CLOSED + if (this.onend) { + console.error('socket.onend = func is non-standard, use socket.on(\'end\', func)') + this.once('end', this.onend) + } + this.push(null) + this.destroy() + } else if (resultCode < 0) { + this.destroy(errnoException(resultCode, 'read')) + } } -} -/** - * Disables the Nagle algorithm. By default TCP connections use the Nagle - * algorithm, they buffer data before sending it off. Setting true for noDelay - * will immediately fire off data each time socket.write() is called. noDelay - * defaults to true. - * - * NOTE: The Chrome version of this function is async, whereas the node - * version is sync. Keep this in mind. - * - * @param {boolean} [noDelay] Optional - * @param {function} callback CHROME-SPECIFIC: Called when the configuration - * operation is done. - */ -Socket.prototype.setNoDelay = function (noDelay, callback) { - if (!this.id) { - this.once('connect', () => this.setNoDelay(noDelay, callback)) + /** + * The amount of bytes sent. + * @return {number} + */ + get bytesWritten () { + if (this.id) return this._bytesDispatched + this.bufferSize + } + + destroy (exception) { + this._destroy(exception) + } + + _destroy (exception, cb) { + const fireErrorCallbacks = () => { + if (cb) cb(exception) + if (exception && !this._writableState.errorEmitted) { + process.nextTick(emitErrorNT, this, exception) + this._writableState.errorEmitted = true + } + } + + if (this.destroyed) { + // already destroyed, fire error callbacks + fireErrorCallbacks() + return + } + + if (this._server) { + this._server._connections -= 1 + if (this._server._emitCloseIfDrained) this._server._emitCloseIfDrained() + } + + this._reset() + + for (let s = this; s !== null; s = s._parent) timers.unenroll(s) // eslint-disable-line n/no-deprecated-api + + this.destroyed = true + + // If _destroy() has been called before chrome.sockets.tcp.create() + // callback, we don't have an id. Therefore we don't need to close + // or disconnect + if (this.id) { + delete sockets[this.id] + chrome.sockets.tcp.close(this.id, () => { + if (this.destroyed) { + this.emit('close', !!exception) + } + }) + this.id = null + } + + fireErrorCallbacks() + } + + destroySoon () { + if (this.writable) this.end() + + if (this._writableState.finished) this.destroy() + } + + /** + * Sets the socket to timeout after timeout milliseconds of inactivity on the socket. + * By default net.Socket do not have a timeout. When an idle timeout is triggered the + * socket will receive a 'timeout' event but the connection will not be severed. The + * user must manually end() or destroy() the socket. + * + * If timeout is 0, then the existing idle timeout is disabled. + * + * The optional callback parameter will be added as a one time listener for the 'timeout' event. + * + * @param {number} timeout + * @param {function} callback + */ + setTimeout (timeout, callback) { + if (timeout === 0) { + timers.unenroll(this) // eslint-disable-line n/no-deprecated-api + if (callback) { + this.removeListener('timeout', callback) + } + } else { + timers.enroll(this, timeout) // eslint-disable-line n/no-deprecated-api + timers._unrefActive(this) + if (callback) { + this.once('timeout', callback) + } + } + return this } - // backwards compatibility: assume true when `noDelay` is omitted - noDelay = noDelay === undefined ? true : !!noDelay - chrome.sockets.tcp.setNoDelay(this.id, noDelay, chromeCallbackWrap(callback)) + _onTimeout () { + this.emit('timeout') + } - return this -} + _unrefTimer () { + for (let s = this; s !== null; s = s._parent) { + timers._unrefActive(s) + } + } + + /** + * Disables the Nagle algorithm. By default TCP connections use the Nagle + * algorithm, they buffer data before sending it off. Setting true for noDelay + * will immediately fire off data each time socket.write() is called. noDelay + * defaults to true. + * + * NOTE: The Chrome version of this function is async, whereas the node + * version is sync. Keep this in mind. + * + * @param {boolean} [noDelay] Optional + * @param {function} callback CHROME-SPECIFIC: Called when the configuration + * operation is done. + */ + setNoDelay (noDelay, callback) { + if (!this.id) { + this.once('connect', () => this.setNoDelay(noDelay, callback)) + return this + } + + // backwards compatibility: assume true when `noDelay` is omitted + noDelay = noDelay === undefined ? true : !!noDelay + chrome.sockets.tcp.setNoDelay(this.id, noDelay, chromeCallbackWrap(callback)) -/** - * Enable/disable keep-alive functionality, and optionally set the initial - * delay before the first keepalive probe is sent on an idle socket. enable - * defaults to false. - * - * Set initialDelay (in milliseconds) to set the delay between the last data - * packet received and the first keepalive probe. Setting 0 for initialDelay - * will leave the value unchanged from the default (or previous) setting. - * Defaults to 0. - * - * NOTE: The Chrome version of this function is async, whereas the node - * version is sync. Keep this in mind. - * - * @param {boolean} [enable] Optional - * @param {number} [initialDelay] - * @param {function} callback CHROME-SPECIFIC: Called when the configuration - * operation is done. - */ -Socket.prototype.setKeepAlive = function (enable, initialDelay, callback) { - if (!this.id) { - this.once('connect', () => this.setKeepAlive(enable, initialDelay, callback)) return this } - chrome.sockets.tcp.setKeepAlive(this.id, !!enable, ~~(initialDelay / 1000), - chromeCallbackWrap(callback)) + /** + * Enable/disable keep-alive functionality, and optionally set the initial + * delay before the first keepalive probe is sent on an idle socket. enable + * defaults to false. + * + * Set initialDelay (in milliseconds) to set the delay between the last data + * packet received and the first keepalive probe. Setting 0 for initialDelay + * will leave the value unchanged from the default (or previous) setting. + * Defaults to 0. + * + * NOTE: The Chrome version of this function is async, whereas the node + * version is sync. Keep this in mind. + * + * @param {boolean} [enable] Optional + * @param {number} [initialDelay] + * @param {function} callback CHROME-SPECIFIC: Called when the configuration + * operation is done. + */ + setKeepAlive (enable, initialDelay, callback) { + if (!this.id) { + this.once('connect', () => this.setKeepAlive(enable, initialDelay, callback)) + return this + } - return this -} + chrome.sockets.tcp.setKeepAlive(this.id, !!enable, ~~(initialDelay / 1000), + chromeCallbackWrap(callback)) -/** - * Returns the bound address, the address family name and port of the socket - * as reported by the operating system. Returns an object with three - * properties, e.g. { port: 12346, family: 'IPv4', address: '127.0.0.1' } - * - * @return {Object} information - */ -Socket.prototype.address = function () { - return { - address: this.localAddress, - port: this.localPort, - family: this.localAddress && - this.localAddress.indexOf(':') !== -1 - ? 'IPv6' - : 'IPv4' + return this } -} -Object.defineProperty(Socket.prototype, '_connecting', { - get: function () { + /** + * Returns the bound address, the address family name and port of the socket + * as reported by the operating system. Returns an object with three + * properties, e.g. { port: 12346, family: 'IPv4', address: '127.0.0.1' } + * + * @return {Object} information + */ + address () { + return { + address: this.localAddress, + port: this.localPort, + family: this.localAddress && + this.localAddress.indexOf(':') !== -1 + ? 'IPv6' + : 'IPv4' + } + } + + get _connecting () { return this.connecting } -}) -Object.defineProperty(Socket.prototype, 'readyState', { - get: function () { + get readyState () { if (this.connecting) { return 'opening' } else if (this.readable && this.writable) { @@ -1048,13 +1019,18 @@ Object.defineProperty(Socket.prototype, 'readyState', { return 'closed' } } -}) -Socket.prototype.unref = -Socket.prototype.ref = function () { - // No chrome.socket equivalent - return this + ref () { + // No chrome.socket equivalent + return this + } + + unref () { + // No chrome.socket equivalent + return this + } } +exports.Socket = Socket // // EXPORTED HELPERS diff --git a/capacitor/src/cors-fetch.js b/capacitor/src/cors-fetch.js new file mode 100644 index 0000000..5406b8e --- /dev/null +++ b/capacitor/src/cors-fetch.js @@ -0,0 +1,15 @@ +const fetch = globalThis.fetch + +if (globalThis.CapacitorWebFetch) { + globalThis.fetch = globalThis.CapacitorWebFetch +} + +export const Blob = self.Blob +export const File = self.File +export const FormData = self.FormData +export const Headers = self.Headers +export const Request = self.Request +export const Response = self.Response +export const AbortController = self.AbortController +export const AbortSignal = self.AbortSignal +export default fetch diff --git a/capacitor/src/ipc.js b/capacitor/src/ipc.js index c4d764c..b6eb68f 100644 --- a/capacitor/src/ipc.js +++ b/capacitor/src/ipc.js @@ -38,7 +38,7 @@ export const ipcRendererWebTorrent = new EventEmitter() const [_platform, arch] = navigator.platform.split(' ') window.version = { - platform: globalThis.cordova.platformId, + platform: globalThis.cordova?.platformId, arch, - version: globalThis.cordova.version + version: globalThis.cordova?.version } diff --git a/capacitor/src/webtorrent.js b/capacitor/src/webtorrent.js index 8ef8166..ae44a91 100644 --- a/capacitor/src/webtorrent.js +++ b/capacitor/src/webtorrent.js @@ -7,6 +7,7 @@ const controller = (async () => { const reg = await navigator.serviceWorker.register('./sw.js', { scope: './' }) const worker = reg.active || reg.waiting || reg.installing + if (!worker) throw new Error('No worker registration') return new Promise(resolve => { function checkState ({ state }) { if (state === 'activated') { @@ -20,6 +21,6 @@ const controller = (async () => { } }) })() -window.controller = controller +globalThis.controller = controller -window.client = new TorrentClient(ipcRendererWebTorrent, () => ({ bsize: Infinity, bavail: Infinity }), 'browser', controller) +globalThis.client = new TorrentClient(ipcRendererWebTorrent, () => ({ bsize: Infinity, bavail: Infinity }), 'browser', controller) diff --git a/capacitor/webpack.config.cjs b/capacitor/webpack.config.cjs index 847858f..d0d1fa9 100644 --- a/capacitor/webpack.config.cjs +++ b/capacitor/webpack.config.cjs @@ -27,21 +27,21 @@ const capacitorConfig = { } } const alias = { - '@/modules/ipc.js': join(__dirname, 'src', 'ipc.js'), - 'webtorrent/lib/utp.cjs': false, - '@silentbot1/nat-api': false, fs: false, - http: 'stream-http', - https: 'stream-http', - 'load-ip-set': false, - net: join(__dirname, 'src', 'chrome-net.js'), - dgram: join(__dirname, 'src', 'chrome-dgram.js'), - util: 'util', - assert: 'assert', os: false, ws: false, - ut_pex: 'ut_pex', + '@silentbot1/nat-api': false, + 'load-ip-set': false, 'bittorrent-dht': false, + 'webtorrent/lib/utp.cjs': false, + '@/modules/ipc.js': join(__dirname, 'src', 'ipc.js'), + net: join(__dirname, 'src', 'chrome-net.js'), + dgram: join(__dirname, 'src', 'chrome-dgram.js'), + 'cross-fetch-ponyfill': join(__dirname, 'src', 'cors-fetch.js'), + http: 'stream-http', + https: 'stream-http', + assert: 'assert', + ut_pex: 'ut_pex', path: 'path-esm', 'fs-chunk-store': 'hybrid-chunk-store', stream: 'stream-browserify', diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a2eb3ee..e4e473a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -105,6 +105,9 @@ importers: hybrid-chunk-store: specifier: ^1.2.2 version: 1.2.2 + npm-run-all: + specifier: ^4.1.5 + version: 4.1.5 path-esm: specifier: ^1.0.0 version: 1.0.0 @@ -1187,6 +1190,13 @@ packages: resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} engines: {node: '>=8'} + /ansi-styles@3.2.1: + resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==} + engines: {node: '>=4'} + dependencies: + color-convert: 1.9.3 + dev: true + /ansi-styles@4.3.0: resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} engines: {node: '>=8'} @@ -1254,7 +1264,6 @@ packages: dependencies: call-bind: 1.0.5 is-array-buffer: 3.0.2 - dev: false /array-flatten@1.1.1: resolution: {integrity: sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==} @@ -1320,7 +1329,6 @@ packages: get-intrinsic: 1.2.2 is-array-buffer: 3.0.2 is-shared-array-buffer: 1.0.2 - dev: false /asn1.js@5.4.1: resolution: {integrity: sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA==} @@ -1850,6 +1858,15 @@ packages: /caniuse-lite@1.0.30001551: resolution: {integrity: sha512-vtBAez47BoGMMzlbYhfXrMV1kvRF2WP/lqiMuDu1Sb4EE4LKEgjopFDSRtZfdVnslNRpOqV/woE+Xgrwj6VQlg==} + /chalk@2.4.2: + resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} + engines: {node: '>=4'} + dependencies: + ansi-styles: 3.2.1 + escape-string-regexp: 1.0.5 + supports-color: 5.5.0 + dev: true + /chalk@4.1.2: resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} engines: {node: '>=10'} @@ -1976,6 +1993,12 @@ packages: periscopic: 3.1.0 dev: false + /color-convert@1.9.3: + resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} + dependencies: + color-name: 1.1.3 + dev: true + /color-convert@2.0.1: resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} engines: {node: '>=7.0.0'} @@ -1983,6 +2006,10 @@ packages: dependencies: color-name: 1.1.4 + /color-name@1.1.3: + resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==} + dev: true + /color-name@1.1.4: resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} requiresBuild: true @@ -2230,6 +2257,17 @@ packages: node-fetch: 3.3.2 dev: false + /cross-spawn@6.0.5: + resolution: {integrity: sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==} + engines: {node: '>=4.8'} + dependencies: + nice-try: 1.0.5 + path-key: 2.0.1 + semver: 5.7.2 + shebang-command: 1.2.0 + which: 1.3.1 + dev: true + /cross-spawn@7.0.3: resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} engines: {node: '>= 8'} @@ -2729,6 +2767,12 @@ packages: resolution: {integrity: sha512-GiaH0KJUewYok+eeY05IIgjtAe4Yltygk9Wqp1V5yVWLdhf0hYZchRjNIT9bb0mSwRcIusT3cx7PJUf3zEIfUA==} dev: false + /error-ex@1.3.2: + resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==} + dependencies: + is-arrayish: 0.2.1 + dev: true + /es-abstract@1.22.3: resolution: {integrity: sha512-eiiY8HQeYfYH2Con2berK+To6GrK2RxbPawDkGq4UiCQQfZHb6wX9qQqkbpPqaxQFcl8d9QzZqo0tGE0VcrdwA==} engines: {node: '>= 0.4'} @@ -2772,7 +2816,6 @@ packages: typed-array-length: 1.0.4 unbox-primitive: 1.0.2 which-typed-array: 1.1.13 - dev: false /es-module-lexer@1.3.1: resolution: {integrity: sha512-JUFAyicQV9mXc3YRxPnDlrfBKpqt6hUYzz9/boprUJHs4e4KVr3XwOF70doO6gwXUor6EWZJAyWAfKki84t20Q==} @@ -2784,7 +2827,6 @@ packages: get-intrinsic: 1.2.2 has-tostringtag: 1.0.0 hasown: 2.0.0 - dev: false /es-shim-unscopables@1.0.2: resolution: {integrity: sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==} @@ -2799,7 +2841,6 @@ packages: is-callable: 1.2.7 is-date-object: 1.0.5 is-symbol: 1.0.4 - dev: false /es6-error@4.1.1: resolution: {integrity: sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==} @@ -2813,6 +2854,11 @@ packages: /escape-html@1.0.3: resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==} + /escape-string-regexp@1.0.5: + resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} + engines: {node: '>=0.8.0'} + dev: true + /escape-string-regexp@4.0.0: resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} engines: {node: '>=10'} @@ -3451,11 +3497,9 @@ packages: define-properties: 1.2.1 es-abstract: 1.22.3 functions-have-names: 1.2.3 - dev: false /functions-have-names@1.2.3: resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} - dev: false /get-browser-rtc@1.1.0: resolution: {integrity: sha512-MghbMJ61EJrRsDe7w1Bvqt3ZsBuqhce5nrn/XAwgwOXhcsz53/ltdxOse1h/8eKXj5slzxdsz56g5rzOFSGwfQ==} @@ -3495,7 +3539,6 @@ packages: dependencies: call-bind: 1.0.5 get-intrinsic: 1.2.2 - dev: false /get-tsconfig@4.7.2: resolution: {integrity: sha512-wuMsz4leaj5hbGgg4IvDU0bqJagpftG5l5cXIAvo8uZrqn0NJqwtfupTN00VnkQJPcIRrxYrm1Ue24btpCha2A==} @@ -3627,7 +3670,11 @@ packages: /has-bigints@1.0.2: resolution: {integrity: sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==} - dev: false + + /has-flag@3.0.0: + resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==} + engines: {node: '>=4'} + dev: true /has-flag@4.0.0: resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} @@ -3692,6 +3739,10 @@ packages: minimalistic-crypto-utils: 1.0.1 dev: true + /hosted-git-info@2.8.9: + resolution: {integrity: sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==} + dev: true + /hosted-git-info@4.1.0: resolution: {integrity: sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==} engines: {node: '>=10'} @@ -3950,7 +4001,6 @@ packages: get-intrinsic: 1.2.2 hasown: 2.0.0 side-channel: 1.0.4 - dev: false /interpret@3.1.1: resolution: {integrity: sha512-6xwYfHbajpoF0xLW+iwLkhwgvLoZDfjYfoFNu8ftMoXINzwuymNLd9u/KmwtdT2GbR+/Cz66otEGEVVUHX9QLQ==} @@ -3992,7 +4042,10 @@ packages: call-bind: 1.0.5 get-intrinsic: 1.2.2 is-typed-array: 1.1.12 - dev: false + + /is-arrayish@0.2.1: + resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} + dev: true /is-arrayish@0.3.2: resolution: {integrity: sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==} @@ -4002,7 +4055,6 @@ packages: resolution: {integrity: sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==} dependencies: has-bigints: 1.0.2 - dev: false /is-binary-path@2.1.0: resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} @@ -4016,7 +4068,6 @@ packages: dependencies: call-bind: 1.0.5 has-tostringtag: 1.0.0 - dev: false /is-callable@1.2.7: resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} @@ -4039,7 +4090,6 @@ packages: engines: {node: '>= 0.4'} dependencies: has-tostringtag: 1.0.0 - dev: false /is-docker@2.2.1: resolution: {integrity: sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==} @@ -4083,14 +4133,12 @@ packages: /is-negative-zero@2.0.2: resolution: {integrity: sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==} engines: {node: '>= 0.4'} - dev: false /is-number-object@1.0.7: resolution: {integrity: sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==} engines: {node: '>= 0.4'} dependencies: has-tostringtag: 1.0.0 - dev: false /is-number@7.0.0: resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} @@ -4123,13 +4171,11 @@ packages: dependencies: call-bind: 1.0.5 has-tostringtag: 1.0.0 - dev: false /is-shared-array-buffer@1.0.2: resolution: {integrity: sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==} dependencies: call-bind: 1.0.5 - dev: false /is-stream@2.0.1: resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} @@ -4140,14 +4186,12 @@ packages: engines: {node: '>= 0.4'} dependencies: has-tostringtag: 1.0.0 - dev: false /is-symbol@1.0.4: resolution: {integrity: sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==} engines: {node: '>= 0.4'} dependencies: has-symbols: 1.0.3 - dev: false /is-typed-array@1.1.12: resolution: {integrity: sha512-Z14TF2JNG8Lss5/HMqt0//T9JeHXttXy5pH/DBU4vi98ozO2btxzq9MwYDZYnKwU8nRsz/+GVFVRDq3DkVuSPg==} @@ -4159,7 +4203,6 @@ packages: resolution: {integrity: sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==} dependencies: call-bind: 1.0.5 - dev: false /is-wsl@2.2.0: resolution: {integrity: sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==} @@ -4172,7 +4215,6 @@ packages: /isarray@2.0.5: resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==} - dev: false /isbinaryfile@4.0.10: resolution: {integrity: sha512-iHrqe5shvBUcFbmZq9zOQHBoeOhZJu6RQGrDpBgenUm/Am+F3JM2MgQj+rK3Z601fzrL5gLZWtAPH2OBaSVcyw==} @@ -4234,6 +4276,10 @@ packages: /json-buffer@3.0.1: resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} + /json-parse-better-errors@1.0.2: + resolution: {integrity: sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==} + dev: true + /json-parse-even-better-errors@2.3.1: resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} @@ -4371,6 +4417,16 @@ packages: split: 1.0.1 dev: false + /load-json-file@4.0.0: + resolution: {integrity: sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw==} + engines: {node: '>=4'} + dependencies: + graceful-fs: 4.2.11 + parse-json: 4.0.0 + pify: 3.0.0 + strip-bom: 3.0.0 + dev: true + /loader-runner@4.3.0: resolution: {integrity: sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==} engines: {node: '>=6.11.5'} @@ -4516,6 +4572,11 @@ packages: dependencies: queue-microtask: 1.2.3 + /memorystream@0.3.1: + resolution: {integrity: sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw==} + engines: {node: '>= 0.10.0'} + dev: true + /merge-descriptors@1.0.1: resolution: {integrity: sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==} @@ -4722,6 +4783,10 @@ packages: engines: {node: '>= 0.4.0'} dev: false + /nice-try@1.0.5: + resolution: {integrity: sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==} + dev: true + /no-case@3.0.4: resolution: {integrity: sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==} dependencies: @@ -4782,6 +4847,15 @@ packages: /node-releases@2.0.13: resolution: {integrity: sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==} + /normalize-package-data@2.5.0: + resolution: {integrity: sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==} + dependencies: + hosted-git-info: 2.8.9 + resolve: 1.22.8 + semver: 5.7.2 + validate-npm-package-license: 3.0.4 + dev: true + /normalize-path@3.0.0: resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} engines: {node: '>=0.10.0'} @@ -4791,6 +4865,22 @@ packages: engines: {node: '>=10'} dev: true + /npm-run-all@4.1.5: + resolution: {integrity: sha512-Oo82gJDAVcaMdi3nuoKFavkIHBRVqQ1qvMb+9LHk/cF4P6B2m8aP04hGf7oL6wZ9BuGwX1onlLhpuoofSyoQDQ==} + engines: {node: '>= 4'} + hasBin: true + dependencies: + ansi-styles: 3.2.1 + chalk: 2.4.2 + cross-spawn: 6.0.5 + memorystream: 0.3.1 + minimatch: 3.1.2 + pidtree: 0.3.1 + read-pkg: 3.0.0 + shell-quote: 1.8.1 + string.prototype.padend: 3.1.5 + dev: true + /npm-run-path@4.0.1: resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==} engines: {node: '>=8'} @@ -4968,6 +5058,14 @@ packages: safe-buffer: 5.2.1 dev: true + /parse-json@4.0.0: + resolution: {integrity: sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==} + engines: {node: '>=4'} + dependencies: + error-ex: 1.3.2 + json-parse-better-errors: 1.0.2 + dev: true + /parse-torrent@11.0.14: resolution: {integrity: sha512-/9Yp1FAx5Qd7JX4/0LPiJ73bxVoUgymRDlyB2xAkJ1r9tKrtc1L4Dr024TZDwp1qzDa+oITsYn0i/SbR+fWm4w==} engines: {node: '>=12.20.0'} @@ -5004,6 +5102,11 @@ packages: resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} engines: {node: '>=0.10.0'} + /path-key@2.0.1: + resolution: {integrity: sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==} + engines: {node: '>=4'} + dev: true + /path-key@3.1.1: resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} engines: {node: '>=8'} @@ -5022,6 +5125,13 @@ packages: /path-to-regexp@0.1.7: resolution: {integrity: sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==} + /path-type@3.0.0: + resolution: {integrity: sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==} + engines: {node: '>=4'} + dependencies: + pify: 3.0.0 + dev: true + /path-type@4.0.0: resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} engines: {node: '>=8'} @@ -5060,10 +5170,21 @@ packages: resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} engines: {node: '>=8.6'} + /pidtree@0.3.1: + resolution: {integrity: sha512-qQbW94hLHEqCg7nhby4yRC7G2+jYHY4Rguc2bjw7Uug4GIJuu1tvf2uHaZv5Q8zdt+WKJ6qK1FOI6amaWUo5FA==} + engines: {node: '>=0.10'} + hasBin: true + dev: true + /piece-length@2.0.1: resolution: {integrity: sha512-dBILiDmm43y0JPISWEmVGKBETQjwJe6mSU9GND+P9KW0SJGUwoU/odyH1nbalOP9i8WSYuqf1lQnaj92Bhw+Ug==} dev: false + /pify@3.0.0: + resolution: {integrity: sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==} + engines: {node: '>=4'} + dev: true + /pkg-dir@4.2.0: resolution: {integrity: sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==} engines: {node: '>=8'} @@ -5357,6 +5478,15 @@ packages: lazy-val: 1.0.5 dev: true + /read-pkg@3.0.0: + resolution: {integrity: sha512-BLq/cCO9two+lBgiTYNqD6GdtK8s4NpaWrl6/rCO9w0TUS8oJl7cmToOZfRYllKTISY6nt1U7jQ53brmKqY6BA==} + engines: {node: '>=4'} + dependencies: + load-json-file: 4.0.0 + normalize-package-data: 2.5.0 + path-type: 3.0.0 + dev: true + /readable-stream@2.3.8: resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==} dependencies: @@ -5405,7 +5535,6 @@ packages: call-bind: 1.0.5 define-properties: 1.2.1 set-function-name: 2.0.1 - dev: false /relateurl@0.2.7: resolution: {integrity: sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog==} @@ -5551,7 +5680,6 @@ packages: get-intrinsic: 1.2.2 has-symbols: 1.0.3 isarray: 2.0.5 - dev: false /safe-buffer@5.1.2: resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} @@ -5565,7 +5693,6 @@ packages: call-bind: 1.0.5 get-intrinsic: 1.2.2 is-regex: 1.1.4 - dev: false /safer-buffer@2.1.2: resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} @@ -5613,6 +5740,11 @@ packages: dev: true optional: true + /semver@5.7.2: + resolution: {integrity: sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==} + hasBin: true + dev: true + /semver@6.3.1: resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} hasBin: true @@ -5698,7 +5830,6 @@ packages: define-data-property: 1.1.1 functions-have-names: 1.2.3 has-property-descriptors: 1.0.1 - dev: false /setimmediate@1.0.5: resolution: {integrity: sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==} @@ -5739,12 +5870,24 @@ packages: tunnel-agent: 0.6.0 dev: true + /shebang-command@1.2.0: + resolution: {integrity: sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==} + engines: {node: '>=0.10.0'} + dependencies: + shebang-regex: 1.0.0 + dev: true + /shebang-command@2.0.0: resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} engines: {node: '>=8'} dependencies: shebang-regex: 3.0.0 + /shebang-regex@1.0.0: + resolution: {integrity: sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==} + engines: {node: '>=0.10.0'} + dev: true + /shebang-regex@3.0.0: resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} engines: {node: '>=8'} @@ -5861,6 +6004,28 @@ packages: resolution: {integrity: sha512-zC8zGoGkmc8J9ndvml8Xksr1Amk9qBujgbF0JAIWO7kXr43w0h/0GJNM/Vustixu+YE8N/MTrQ7N31FvHUACxQ==} dev: false + /spdx-correct@3.2.0: + resolution: {integrity: sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==} + dependencies: + spdx-expression-parse: 3.0.1 + spdx-license-ids: 3.0.16 + dev: true + + /spdx-exceptions@2.3.0: + resolution: {integrity: sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==} + dev: true + + /spdx-expression-parse@3.0.1: + resolution: {integrity: sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==} + dependencies: + spdx-exceptions: 2.3.0 + spdx-license-ids: 3.0.16 + dev: true + + /spdx-license-ids@3.0.16: + resolution: {integrity: sha512-eWN+LnM3GR6gPu35WxNgbGl8rmY1AEmoMDvL/QD6zYmPWgywxWqJWNdLGT+ke8dKNWrcYgYjPpG5gbTfghP8rw==} + dev: true + /spdy-transport@3.0.0: resolution: {integrity: sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==} dependencies: @@ -5952,6 +6117,15 @@ packages: is-fullwidth-code-point: 3.0.0 strip-ansi: 6.0.1 + /string.prototype.padend@3.1.5: + resolution: {integrity: sha512-DOB27b/2UTTD+4myKUFh+/fXWcu/UDyASIXfg+7VzoCNNGOfWvoyU/x5pvVHr++ztyt/oSYI1BcWBBG/hmlNjA==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.5 + define-properties: 1.2.1 + es-abstract: 1.22.3 + dev: true + /string.prototype.trim@1.2.8: resolution: {integrity: sha512-lfjY4HcixfQXOfaqCvcBuOIapyaroTXhbkfJN3gcB1OtyupngWK4sEET9Knd0cXd28kTUqu/kHoV4HKSJdnjiQ==} engines: {node: '>= 0.4'} @@ -5959,7 +6133,6 @@ packages: call-bind: 1.0.5 define-properties: 1.2.1 es-abstract: 1.22.3 - dev: false /string.prototype.trimend@1.0.7: resolution: {integrity: sha512-Ni79DqeB72ZFq1uH/L6zJ+DKZTkOtPIHovb3YZHQViE+HDouuU4mBrLOLDn5Dde3RF8qw5qVETEjhu9locMLvA==} @@ -5967,7 +6140,6 @@ packages: call-bind: 1.0.5 define-properties: 1.2.1 es-abstract: 1.22.3 - dev: false /string.prototype.trimstart@1.0.7: resolution: {integrity: sha512-NGhtDFu3jCEm7B4Fy0DpLewdJQOZcQ0rGbwQ/+stjnrp2i+rlKeCvos9hOIeCmqwratM47OBxY7uFZzjxHXmrg==} @@ -5975,7 +6147,6 @@ packages: call-bind: 1.0.5 define-properties: 1.2.1 es-abstract: 1.22.3 - dev: false /string2compact@2.0.1: resolution: {integrity: sha512-Bm/T8lHMTRXw+u83LE+OW7fXmC/wM+Mbccfdo533ajSBNxddDHlRrvxE49NdciGHgXkUQM5WYskJ7uTkbBUI0A==} @@ -6004,7 +6175,6 @@ packages: /strip-bom@3.0.0: resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} engines: {node: '>=4'} - dev: false /strip-final-newline@2.0.0: resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==} @@ -6029,6 +6199,13 @@ packages: - supports-color dev: true + /supports-color@5.5.0: + resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} + engines: {node: '>=4'} + dependencies: + has-flag: 3.0.0 + dev: true + /supports-color@7.2.0: resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} engines: {node: '>=8'} @@ -6348,7 +6525,6 @@ packages: call-bind: 1.0.5 get-intrinsic: 1.2.2 is-typed-array: 1.1.12 - dev: false /typed-array-byte-length@1.0.0: resolution: {integrity: sha512-Or/+kvLxNpeQ9DtSydonMxCx+9ZXOswtwJn17SNLvhptaXYDJvkFFP5zbfU/uLmvnBJlI4yrnXRxpdWH/M5tNA==} @@ -6358,7 +6534,6 @@ packages: for-each: 0.3.3 has-proto: 1.0.1 is-typed-array: 1.1.12 - dev: false /typed-array-byte-offset@1.0.0: resolution: {integrity: sha512-RD97prjEt9EL8YgAgpOkf3O4IF9lhJFr9g0htQkm0rchFp/Vx7LW5Q8fSXXub7BXAODyUQohRMyOc3faCPd0hg==} @@ -6369,7 +6544,6 @@ packages: for-each: 0.3.3 has-proto: 1.0.1 is-typed-array: 1.1.12 - dev: false /typed-array-length@1.0.4: resolution: {integrity: sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==} @@ -6377,7 +6551,6 @@ packages: call-bind: 1.0.5 for-each: 0.3.3 is-typed-array: 1.1.12 - dev: false /typescript@4.9.5: resolution: {integrity: sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==} @@ -6397,7 +6570,6 @@ packages: has-bigints: 1.0.2 has-symbols: 1.0.3 which-boxed-primitive: 1.0.2 - dev: false /undici-types@5.25.3: resolution: {integrity: sha512-Ga1jfYwRn7+cP9v8auvEXN1rX3sWqlayd4HP7OKk4mZWylEmu3KzXDUGrQUN6Ol7qo1gPvB2e5gX6udnyEPgdA==} @@ -6513,6 +6685,13 @@ packages: resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==} hasBin: true + /validate-npm-package-license@3.0.4: + resolution: {integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==} + dependencies: + spdx-correct: 3.2.0 + spdx-expression-parse: 3.0.1 + dev: true + /vary@1.1.2: resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} engines: {node: '>= 0.8'} @@ -6779,7 +6958,6 @@ packages: is-number-object: 1.0.7 is-string: 1.0.7 is-symbol: 1.0.4 - dev: false /which-typed-array@1.1.13: resolution: {integrity: sha512-P5Nra0qjSncduVPEAr7xhoF5guty49ArDTwzJ/yNuPIbZppyRxFQsRCWrocxIY+CnMVG+qfbU2FmDKyvSGClow==} @@ -6791,6 +6969,13 @@ packages: gopd: 1.0.1 has-tostringtag: 1.0.0 + /which@1.3.1: + resolution: {integrity: sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==} + hasBin: true + dependencies: + isexe: 2.0.0 + dev: true + /which@2.0.2: resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} engines: {node: '>= 8'}