├── .eslintrc.json ├── .github └── stale.yml ├── .gitignore ├── LICENSE ├── README.md ├── cert.cer ├── cert.pfx ├── examples └── logger │ ├── index.js │ ├── package-lock.json │ └── package.json ├── index.d.ts ├── index.js ├── package-lock.json ├── package.json ├── tcp-proxy-cli.js └── tcp-proxy.js /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "parserOptions": { 3 | "ecmaVersion": 6 4 | }, 5 | "plugins": [ 6 | "security" 7 | ], 8 | "extends": [ 9 | "google", 10 | "plugin:security/recommended" 11 | ], 12 | "rules": { 13 | "quotes": "off", 14 | "require-jsdoc": "off", 15 | "no-var": "off", 16 | "guard-for-in": "off", 17 | "comma-dangle": "off", 18 | "indent": ["error", 4] 19 | } 20 | } -------------------------------------------------------------------------------- /.github/stale.yml: -------------------------------------------------------------------------------- 1 | # Number of days of inactivity before an issue becomes stale 2 | daysUntilStale: 60 3 | # Number of days of inactivity before a stale issue is closed 4 | daysUntilClose: 7 5 | # Issues with these labels will never be considered stale 6 | exemptLabels: 7 | - pinned 8 | - security 9 | # Label to use when marking an issue as stale 10 | staleLabel: wontfix 11 | # Comment to post when marking an issue as stale. Set to `false` to disable 12 | markComment: > 13 | This issue has been automatically marked as stale because it has not had 14 | recent activity. It will be closed if no further activity occurs. Thank you 15 | for your contributions. 16 | # Comment to post when closing a stale issue. Set to `false` to disable 17 | closeComment: false 18 | 19 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .vscode -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (C) 2011-2016 Devendra Tewari 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # node-tcp-proxy [![Codacy Badge](https://app.codacy.com/project/badge/Grade/a882a604851b494caf65b3913592da4c)](https://www.codacy.com/gh/tewarid/node-tcp-proxy/dashboard?utm_source=github.com&utm_medium=referral&utm_content=tewarid/node-tcp-proxy&utm_campaign=Badge_Grade) [![Maintainability](https://api.codeclimate.com/v1/badges/119038e281e93a7d5d05/maintainability)](https://codeclimate.com/github/tewarid/node-tcp-proxy/maintainability) 2 | 3 | A [classical](https://tools.ietf.org/html/rfc1919) TCP proxy that may be used to access a service on another network. An extensible replacement for socat when used thus 4 | 5 | ```bash 6 | socat TCP-LISTEN:port,fork TCP:host:port 7 | ``` 8 | 9 | `port` is where socat listens for incoming requests. `host:port` are the host and port where the actual service is listening at. 10 | 11 | To achieve the same with node-tcp-proxy 12 | 13 | ```bash 14 | tcpproxy --proxyPort port [--hostname ] --serviceHost host1,host2 --servicePort port1,port2 [--q] [--tls [both]] [--pfx file] [--passphrase secret] 15 | ``` 16 | 17 | Optionally, use `--hostname` to specify host or IP address to listen at. Node.js listens on unspecified IPv6 address `::` by default. If `--serviceHost` and `--servicePort` specify a comma separated list, the proxy will perform load balancing on a round-robin basis. 18 | 19 | TLS can be enabled at the proxy port using `--tls`. Use `--pfx` followed by path to specify server certificate, and `--passphrase` to provide the password required to access it. Use `--tls both`, to also enable TLS with the service. 20 | 21 | ## npm 22 | 23 | Install node-tcp-proxy using [npm](https://www.npmjs.com/package/node-tcp-proxy) 24 | 25 | ```bash 26 | sudo npm install -g node-tcp-proxy 27 | ``` 28 | 29 | ## Programming Interface 30 | 31 | To create a proxy in your own code 32 | 33 | ```javascript 34 | var proxy = require("node-tcp-proxy"); 35 | var newProxy = proxy.createProxy(8080, "host", 10080); 36 | ``` 37 | 38 | To end the proxy 39 | 40 | ```javascript 41 | newProxy.end(); 42 | ``` 43 | 44 | `hostname` can be provided through an optional fourth parameter e.g. `{hostname: 0.0.0.0}` to `createProxy`. Console output may be silenced by adding `quiet: true` e.g. `{hostname: 0.0.0.0, quiet: true}`. 45 | 46 | If you specify more than one service host and port pair, the proxy will perform round-robin load balancing 47 | 48 | ```javascript 49 | var hosts = ["host1", "host2"]; 50 | var ports = [10080, 10080]; 51 | var newProxy = proxy.createProxy(8080, hosts, ports); 52 | // or var newProxy = proxy.createProxy(8080, "host1,host2", "10080,10080"); 53 | ``` 54 | 55 | You can intercept and modify data sent in either direction, and modify the service host selection strategy 56 | 57 | ```javascript 58 | var proxy = require("node-tcp-proxy"); 59 | var util = require("util"); 60 | var serviceHosts = ["www.google.com", "www.bing.com"]; 61 | var servicePorts = [80, 80]; 62 | var newProxy = proxy.createProxy(8080, serviceHosts, servicePorts, { 63 | upstream: function(context, data) { 64 | console.log(util.format("Client %s:%s sent:", 65 | context.proxySocket.remoteAddress, 66 | context.proxySocket.remotePort)); 67 | // do something with the data and return modified data 68 | return data; 69 | }, 70 | downstream: function(context, data) { 71 | console.log(util.format("Service %s:%s sent:", 72 | context.serviceSocket.remoteAddress, 73 | context.serviceSocket.remotePort)); 74 | // do something with the data and return modified data 75 | return data; 76 | }, 77 | serviceHostSelected: function(proxySocket, i) { 78 | console.log(util.format("Service host %s:%s selected for client %s:%s.", 79 | serviceHosts[i], 80 | servicePorts[i], 81 | proxySocket.remoteAddress, 82 | proxySocket.remotePort)); 83 | // use your own strategy to calculate i 84 | return i; 85 | } 86 | }); 87 | ``` 88 | 89 | ## Alternatives 90 | 91 | You may want to check out these interesting alternatives 92 | 93 | * [http-proxy](https://github.com/http-party/node-http-proxy) - programmable proxying library that supports websockets. It is suitable for implementing components such as reverse proxies and load balancers. 94 | 95 | * [sslh](https://github.com/yrutschle/sslh) - accepts connections on specified ports, and forwards them further based on tests performed on the first data packet sent by the remote client. 96 | 97 | * [socat](http://www.dest-unreach.org/socat/) - socat is a relay for bidirectional data transfer between two independent data channels. Each of these data channels may be a file, pipe, device (serial line etc. or a pseudo terminal), a socket (UNIX, IP4, IP6 - raw, UDP, TCP), an SSL socket, proxy CONNECT connection, a file descriptor (stdin etc.), the GNU line editor (readline), a program, or a combination of two of these. 98 | -------------------------------------------------------------------------------- /cert.cer: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIICujCCAaKgAwIBAgIEWy6HKjANBgkqhkiG9w0BAQsFADAPMQ0wCwYDVQQDDARU 3 | ZXN0MB4XDTE4MDYyMzE3NDUxNFoXDTM4MDYyMzE3NDUxNFowDzENMAsGA1UEAwwE 4 | VGVzdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMjCaahrNXupv5gk 5 | mY+unEqusINlsy/WX1QU3lH5Lctdh7/sPGh8zqqlUevZhdKMfNFLk+FTEvxh13k8 6 | sjkgX6+pzqW6Hi0fn5Nc0nizrHVLX6cHBVS9dwEi/M0hwk7DEUsYwPxFTvL2BCXf 7 | Xxotb32JUC6Y1UVGNXYZQ0R9XXOecoDjwDpVJNbK3fTrrE/wNB2+ppZk8sqd4Gyb 8 | 9+PicYI9oidcNXQWOFqVejXC7M635CxGzPopalDJB3NLw+8iBzq84NwosVBjqzmh 9 | 0JGrNSXfXrQa0EjCI22B5MnVlNsG5DiNLjALO/REoGmsKxMQXAWw+to0muXqZaUo 10 | tjftvg8CAwEAAaMeMBwwGgYDVR0RBBMwEYIJbG9jYWxob3N0hwR/AAABMA0GCSqG 11 | SIb3DQEBCwUAA4IBAQDEMnfRxCuu/1dbCl2v9iLp5mqPx2tXD1mUsrmdehJmYGi8 12 | gGfyk3X7CPkHZUNY8etRwM8ghG2j5Kx5e0ecV6A6YW/dfVuyiX/1ZrCNExI5IIAh 13 | fVTPF2ZrI93kG/eGNheCqAOSrnqaCWYeM/v57pG+M4R36Vefj8+fz4uppurXeXkq 14 | i0H5oG/R7pi/kRSaquY9FMAwHmHUgeGY/n6I709PZGDvbXwWOqJnH2KmWkj6krs4 15 | neTelyBkFiKkgPzN4t5LLm/NaXXHQYYC05tpw1IVZALrZX0dFDZfTExw6Yya2j9i 16 | Pm+N/y0kK8PGEf9BGP6rTHkVSk3YqsBdrU0gG1cO 17 | -----END CERTIFICATE----- 18 | -------------------------------------------------------------------------------- /cert.pfx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tewarid/node-tcp-proxy/9fd5e7538a7bbc01edef23300c073e995a1d5bcb/cert.pfx -------------------------------------------------------------------------------- /examples/logger/index.js: -------------------------------------------------------------------------------- 1 | var proxy = require("node-tcp-proxy"); 2 | var util = require("util"); 3 | var replace = require('buffer-replace'); 4 | var argv = require("commander"); 5 | 6 | argv 7 | .usage("[options]") 8 | .option("--tls", "Use TLS 1.2; ", false) 9 | .parse(process.argv); 10 | 11 | var PROXY_HOST = "localhost"; 12 | var PROXY_PORT = "8080"; 13 | var SERVICE_HOST = "www.baidu.com"; 14 | var SERVICE_PORT = argv.tls ? 443: 80; 15 | 16 | var serviceHosts = [SERVICE_HOST, SERVICE_HOST]; 17 | var servicePorts = [SERVICE_PORT, SERVICE_PORT]; 18 | 19 | var options = { 20 | upstream: function(context, data) { 21 | log(context.proxySocket, data); 22 | data = replace(data, `${PROXY_HOST}:${PROXY_PORT}`, SERVICE_HOST); 23 | return data; 24 | }, 25 | downstream: function(context, data) { 26 | log(context.serviceSocket, data); 27 | data = replace(data, SERVICE_HOST, `${PROXY_HOST}:${PROXY_PORT}`); 28 | return data; 29 | }, 30 | serviceHostSelected: function(proxySocket, i) { 31 | console.log(util.format("Service host %s:%s selected for client %s:%s.", 32 | serviceHosts[parseInt(i, 10)], 33 | servicePorts[parseInt(i, 10)], 34 | proxySocket.remoteAddress, 35 | proxySocket.remotePort)); 36 | // use your own strategy to calculate i 37 | return i; 38 | } 39 | }; 40 | 41 | if (argv.tls) { 42 | options.tls = "both"; 43 | } 44 | 45 | var newProxy = proxy 46 | .createProxy(PROXY_PORT, serviceHosts, servicePorts, options); 47 | 48 | function log(socket, data) { 49 | console.log(util.format("%s:%s sent:", 50 | socket.remoteAddress, 51 | socket.remotePort)); 52 | console.log(data.toString('hex')); 53 | } 54 | 55 | console.log("Open http://localhost:8080 in the browser."); 56 | 57 | if (argv.tls) { 58 | console.log("TLS 1.2 is enabled."); 59 | } else { 60 | console.log("Specify --tls in command line if you want to use TLS 1.2."); 61 | } 62 | 63 | console.log("press Enter key to quit..."); 64 | setTimeout(handleTimeout, 1000); 65 | function handleTimeout() { 66 | var data = process.stdin.read(1); 67 | if (data) { 68 | console.log("bye."); 69 | newProxy.end(); 70 | process.exit(0); 71 | } 72 | setTimeout(handleTimeout, 1000); 73 | } 74 | -------------------------------------------------------------------------------- /examples/logger/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "logger", 3 | "version": "1.0.0", 4 | "lockfileVersion": 2, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "logger", 9 | "version": "1.0.0", 10 | "license": "MIT", 11 | "dependencies": { 12 | "buffer-replace": "^1.0.0", 13 | "node-tcp-proxy": "^0.0.27" 14 | } 15 | }, 16 | "../..": { 17 | "version": "0.0.23", 18 | "extraneous": true, 19 | "license": "MIT", 20 | "dependencies": { 21 | "commander": "^2.19.0" 22 | }, 23 | "bin": { 24 | "tcpproxy": "tcp-proxy-cli.js" 25 | }, 26 | "devDependencies": { 27 | "eslint": "^4.19.1", 28 | "eslint-config-google": "^0.8.1", 29 | "eslint-plugin-security": "^1.4.0" 30 | } 31 | }, 32 | "node_modules/buffer-replace": { 33 | "version": "1.0.0", 34 | "resolved": "https://registry.npmjs.org/buffer-replace/-/buffer-replace-1.0.0.tgz", 35 | "integrity": "sha1-vEJ8QK9MHwbWkz3t5XEQrLqK3lQ=" 36 | }, 37 | "node_modules/commander": { 38 | "version": "2.20.3", 39 | "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", 40 | "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" 41 | }, 42 | "node_modules/node-tcp-proxy": { 43 | "version": "0.0.26", 44 | "resolved": "https://registry.npmjs.org/node-tcp-proxy/-/node-tcp-proxy-0.0.26.tgz", 45 | "integrity": "sha512-MwWLo/007Rx1wKnVOx+qB4F2dNc6PqJEat0i0cjCedXxaZ/SZ4urMEVATQXQ/Pn62BFjtllrm4VCwHUhJMhV/Q==", 46 | "dependencies": { 47 | "commander": "^2.19.0" 48 | }, 49 | "bin": { 50 | "tcpproxy": "tcp-proxy-cli.js" 51 | } 52 | } 53 | }, 54 | "dependencies": { 55 | "buffer-replace": { 56 | "version": "1.0.0", 57 | "resolved": "https://registry.npmjs.org/buffer-replace/-/buffer-replace-1.0.0.tgz", 58 | "integrity": "sha1-vEJ8QK9MHwbWkz3t5XEQrLqK3lQ=" 59 | }, 60 | "commander": { 61 | "version": "2.20.3", 62 | "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", 63 | "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" 64 | }, 65 | "node-tcp-proxy": { 66 | "version": "0.0.26", 67 | "resolved": "https://registry.npmjs.org/node-tcp-proxy/-/node-tcp-proxy-0.0.26.tgz", 68 | "integrity": "sha512-MwWLo/007Rx1wKnVOx+qB4F2dNc6PqJEat0i0cjCedXxaZ/SZ4urMEVATQXQ/Pn62BFjtllrm4VCwHUhJMhV/Q==", 69 | "requires": { 70 | "commander": "^2.19.0" 71 | } 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /examples/logger/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "logger", 3 | "version": "1.0.0", 4 | "description": "Logger example", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "start": "node index.js" 9 | }, 10 | "author": "", 11 | "license": "MIT", 12 | "dependencies": { 13 | "buffer-replace": "^1.0.0", 14 | "node-tcp-proxy": "^0.0.27" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /index.d.ts: -------------------------------------------------------------------------------- 1 | import { Socket } from 'net'; 2 | import { TLSSocket ,TlsOptions} from 'tls'; 3 | 4 | // Name or IP address of service host(s); if this is a list, performs round-robin load balancing 5 | type ServiceHost = string | string[]; 6 | 7 | // Service port number(s); if this a list, it should have as many entries as serviceHost 8 | type ServicePort = string | number | number[]; 9 | 10 | interface ContextBase { 11 | buffers: Buffer[]; 12 | connect: boolean; 13 | } 14 | 15 | interface UpstreamContext extends ContextBase { 16 | proxySocket: Socket | TLSSocket; 17 | } 18 | 19 | interface DownstreamContext extends ContextBase { 20 | serviceSocket: Socket | TLSSocket; 21 | } 22 | 23 | interface TcpProxyOptions { 24 | // Name or IP address of host 25 | hostname: string; 26 | // IP address of interface to use to connect to service 27 | localAddress: string; 28 | // Port number to use to connect to service 29 | localPort: number; 30 | // Be quiet, default: true 31 | quiet: boolean; 32 | // Use TLS 1.2 with clients; specify both to also use TLS 1.2 with service 33 | tls: boolean; 34 | // Do not accept invalid certificate, default: true 35 | rejectUnauthorized: boolean; 36 | // Private key file path, for example: ./cert.pfx 37 | pfx: string; 38 | // Passphrase to access private key file 39 | passphrase: string; 40 | // List of authorized users 41 | identUsers: string[]; 42 | // List of allowed IPs 43 | allowedIPs: string[]; 44 | // Custom tls server options 45 | customTlsOptions: TlsOptions; 46 | serviceHostSelected: (proxySocket: Socket | TLSSocket, i: number) => number; 47 | upstream: (context: UpstreamContext, data: Buffer) => Buffer; 48 | downstream: (context: DownstreamContext, data: Buffer) => Buffer; 49 | } 50 | 51 | declare class TcpProxy { 52 | constructor( 53 | proxyPort: number, 54 | serviceHost: ServiceHost, 55 | servicePort: ServicePort, 56 | options?: Partial 57 | ); 58 | end(): void; 59 | } 60 | 61 | export function createProxy( 62 | proxyPort: number, 63 | serviceHost: ServiceHost, 64 | servicePort: ServicePort, 65 | options?: Partial 66 | ): TcpProxy; 67 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | var proxy = require("./tcp-proxy"); 2 | exports.createProxy = proxy.createProxy; 3 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "node-tcp-proxy", 3 | "version": "0.0.28", 4 | "lockfileVersion": 2, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "node-tcp-proxy", 9 | "version": "0.0.28", 10 | "license": "MIT", 11 | "dependencies": { 12 | "commander": "^2.19.0" 13 | }, 14 | "bin": { 15 | "tcpproxy": "tcp-proxy-cli.js" 16 | }, 17 | "devDependencies": { 18 | "eslint": "^8.9.0", 19 | "eslint-config-google": "^0.14.0", 20 | "eslint-plugin-security": "^1.4.0" 21 | } 22 | }, 23 | "node_modules/@eslint/eslintrc": { 24 | "version": "1.1.0", 25 | "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.1.0.tgz", 26 | "integrity": "sha512-C1DfL7XX4nPqGd6jcP01W9pVM1HYCuUkFk1432D7F0v3JSlUIeOYn9oCoi3eoLZ+iwBSb29BMFxxny0YrrEZqg==", 27 | "dev": true, 28 | "dependencies": { 29 | "ajv": "^6.12.4", 30 | "debug": "^4.3.2", 31 | "espree": "^9.3.1", 32 | "globals": "^13.9.0", 33 | "ignore": "^4.0.6", 34 | "import-fresh": "^3.2.1", 35 | "js-yaml": "^4.1.0", 36 | "minimatch": "^3.0.4", 37 | "strip-json-comments": "^3.1.1" 38 | }, 39 | "engines": { 40 | "node": "^12.22.0 || ^14.17.0 || >=16.0.0" 41 | } 42 | }, 43 | "node_modules/@eslint/eslintrc/node_modules/ignore": { 44 | "version": "4.0.6", 45 | "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", 46 | "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", 47 | "dev": true, 48 | "engines": { 49 | "node": ">= 4" 50 | } 51 | }, 52 | "node_modules/@humanwhocodes/config-array": { 53 | "version": "0.9.3", 54 | "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.9.3.tgz", 55 | "integrity": "sha512-3xSMlXHh03hCcCmFc0rbKp3Ivt2PFEJnQUJDDMTJQ2wkECZWdq4GePs2ctc5H8zV+cHPaq8k2vU8mrQjA6iHdQ==", 56 | "dev": true, 57 | "dependencies": { 58 | "@humanwhocodes/object-schema": "^1.2.1", 59 | "debug": "^4.1.1", 60 | "minimatch": "^3.0.4" 61 | }, 62 | "engines": { 63 | "node": ">=10.10.0" 64 | } 65 | }, 66 | "node_modules/@humanwhocodes/object-schema": { 67 | "version": "1.2.1", 68 | "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", 69 | "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", 70 | "dev": true 71 | }, 72 | "node_modules/acorn": { 73 | "version": "8.7.0", 74 | "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.0.tgz", 75 | "integrity": "sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ==", 76 | "dev": true, 77 | "bin": { 78 | "acorn": "bin/acorn" 79 | }, 80 | "engines": { 81 | "node": ">=0.4.0" 82 | } 83 | }, 84 | "node_modules/acorn-jsx": { 85 | "version": "5.3.2", 86 | "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", 87 | "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", 88 | "dev": true, 89 | "peerDependencies": { 90 | "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" 91 | } 92 | }, 93 | "node_modules/ajv": { 94 | "version": "6.12.6", 95 | "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", 96 | "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", 97 | "dev": true, 98 | "dependencies": { 99 | "fast-deep-equal": "^3.1.1", 100 | "fast-json-stable-stringify": "^2.0.0", 101 | "json-schema-traverse": "^0.4.1", 102 | "uri-js": "^4.2.2" 103 | }, 104 | "funding": { 105 | "type": "github", 106 | "url": "https://github.com/sponsors/epoberezkin" 107 | } 108 | }, 109 | "node_modules/ansi-regex": { 110 | "version": "5.0.1", 111 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", 112 | "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", 113 | "dev": true, 114 | "engines": { 115 | "node": ">=8" 116 | } 117 | }, 118 | "node_modules/ansi-styles": { 119 | "version": "4.3.0", 120 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", 121 | "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", 122 | "dev": true, 123 | "dependencies": { 124 | "color-convert": "^2.0.1" 125 | }, 126 | "engines": { 127 | "node": ">=8" 128 | }, 129 | "funding": { 130 | "url": "https://github.com/chalk/ansi-styles?sponsor=1" 131 | } 132 | }, 133 | "node_modules/argparse": { 134 | "version": "2.0.1", 135 | "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", 136 | "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", 137 | "dev": true 138 | }, 139 | "node_modules/balanced-match": { 140 | "version": "1.0.2", 141 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", 142 | "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", 143 | "dev": true 144 | }, 145 | "node_modules/brace-expansion": { 146 | "version": "1.1.11", 147 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 148 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 149 | "dev": true, 150 | "dependencies": { 151 | "balanced-match": "^1.0.0", 152 | "concat-map": "0.0.1" 153 | } 154 | }, 155 | "node_modules/callsites": { 156 | "version": "3.1.0", 157 | "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", 158 | "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", 159 | "dev": true, 160 | "engines": { 161 | "node": ">=6" 162 | } 163 | }, 164 | "node_modules/chalk": { 165 | "version": "4.1.2", 166 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", 167 | "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", 168 | "dev": true, 169 | "dependencies": { 170 | "ansi-styles": "^4.1.0", 171 | "supports-color": "^7.1.0" 172 | }, 173 | "engines": { 174 | "node": ">=10" 175 | }, 176 | "funding": { 177 | "url": "https://github.com/chalk/chalk?sponsor=1" 178 | } 179 | }, 180 | "node_modules/color-convert": { 181 | "version": "2.0.1", 182 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", 183 | "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", 184 | "dev": true, 185 | "dependencies": { 186 | "color-name": "~1.1.4" 187 | }, 188 | "engines": { 189 | "node": ">=7.0.0" 190 | } 191 | }, 192 | "node_modules/color-name": { 193 | "version": "1.1.4", 194 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", 195 | "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", 196 | "dev": true 197 | }, 198 | "node_modules/commander": { 199 | "version": "2.19.0", 200 | "resolved": "https://registry.npmjs.org/commander/-/commander-2.19.0.tgz", 201 | "integrity": "sha512-6tvAOO+D6OENvRAh524Dh9jcfKTYDQAqvqezbCW82xj5X0pSrcpxtvRKHLG0yBY6SD7PSDrJaj+0AiOcKVd1Xg==" 202 | }, 203 | "node_modules/concat-map": { 204 | "version": "0.0.1", 205 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 206 | "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", 207 | "dev": true 208 | }, 209 | "node_modules/cross-spawn": { 210 | "version": "7.0.3", 211 | "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", 212 | "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", 213 | "dev": true, 214 | "dependencies": { 215 | "path-key": "^3.1.0", 216 | "shebang-command": "^2.0.0", 217 | "which": "^2.0.1" 218 | }, 219 | "engines": { 220 | "node": ">= 8" 221 | } 222 | }, 223 | "node_modules/debug": { 224 | "version": "4.3.3", 225 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", 226 | "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", 227 | "dev": true, 228 | "dependencies": { 229 | "ms": "2.1.2" 230 | }, 231 | "engines": { 232 | "node": ">=6.0" 233 | }, 234 | "peerDependenciesMeta": { 235 | "supports-color": { 236 | "optional": true 237 | } 238 | } 239 | }, 240 | "node_modules/deep-is": { 241 | "version": "0.1.4", 242 | "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", 243 | "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", 244 | "dev": true 245 | }, 246 | "node_modules/doctrine": { 247 | "version": "3.0.0", 248 | "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", 249 | "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", 250 | "dev": true, 251 | "dependencies": { 252 | "esutils": "^2.0.2" 253 | }, 254 | "engines": { 255 | "node": ">=6.0.0" 256 | } 257 | }, 258 | "node_modules/escape-string-regexp": { 259 | "version": "4.0.0", 260 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", 261 | "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", 262 | "dev": true, 263 | "engines": { 264 | "node": ">=10" 265 | }, 266 | "funding": { 267 | "url": "https://github.com/sponsors/sindresorhus" 268 | } 269 | }, 270 | "node_modules/eslint": { 271 | "version": "8.9.0", 272 | "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.9.0.tgz", 273 | "integrity": "sha512-PB09IGwv4F4b0/atrbcMFboF/giawbBLVC7fyDamk5Wtey4Jh2K+rYaBhCAbUyEI4QzB1ly09Uglc9iCtFaG2Q==", 274 | "dev": true, 275 | "dependencies": { 276 | "@eslint/eslintrc": "^1.1.0", 277 | "@humanwhocodes/config-array": "^0.9.2", 278 | "ajv": "^6.10.0", 279 | "chalk": "^4.0.0", 280 | "cross-spawn": "^7.0.2", 281 | "debug": "^4.3.2", 282 | "doctrine": "^3.0.0", 283 | "escape-string-regexp": "^4.0.0", 284 | "eslint-scope": "^7.1.1", 285 | "eslint-utils": "^3.0.0", 286 | "eslint-visitor-keys": "^3.3.0", 287 | "espree": "^9.3.1", 288 | "esquery": "^1.4.0", 289 | "esutils": "^2.0.2", 290 | "fast-deep-equal": "^3.1.3", 291 | "file-entry-cache": "^6.0.1", 292 | "functional-red-black-tree": "^1.0.1", 293 | "glob-parent": "^6.0.1", 294 | "globals": "^13.6.0", 295 | "ignore": "^5.2.0", 296 | "import-fresh": "^3.0.0", 297 | "imurmurhash": "^0.1.4", 298 | "is-glob": "^4.0.0", 299 | "js-yaml": "^4.1.0", 300 | "json-stable-stringify-without-jsonify": "^1.0.1", 301 | "levn": "^0.4.1", 302 | "lodash.merge": "^4.6.2", 303 | "minimatch": "^3.0.4", 304 | "natural-compare": "^1.4.0", 305 | "optionator": "^0.9.1", 306 | "regexpp": "^3.2.0", 307 | "strip-ansi": "^6.0.1", 308 | "strip-json-comments": "^3.1.0", 309 | "text-table": "^0.2.0", 310 | "v8-compile-cache": "^2.0.3" 311 | }, 312 | "bin": { 313 | "eslint": "bin/eslint.js" 314 | }, 315 | "engines": { 316 | "node": "^12.22.0 || ^14.17.0 || >=16.0.0" 317 | }, 318 | "funding": { 319 | "url": "https://opencollective.com/eslint" 320 | } 321 | }, 322 | "node_modules/eslint-config-google": { 323 | "version": "0.14.0", 324 | "resolved": "https://registry.npmjs.org/eslint-config-google/-/eslint-config-google-0.14.0.tgz", 325 | "integrity": "sha512-WsbX4WbjuMvTdeVL6+J3rK1RGhCTqjsFjX7UMSMgZiyxxaNLkoJENbrGExzERFeoTpGw3F3FypTiWAP9ZXzkEw==", 326 | "dev": true, 327 | "engines": { 328 | "node": ">=0.10.0" 329 | }, 330 | "peerDependencies": { 331 | "eslint": ">=5.16.0" 332 | } 333 | }, 334 | "node_modules/eslint-plugin-security": { 335 | "version": "1.4.0", 336 | "resolved": "https://registry.npmjs.org/eslint-plugin-security/-/eslint-plugin-security-1.4.0.tgz", 337 | "integrity": "sha512-xlS7P2PLMXeqfhyf3NpqbvbnW04kN8M9NtmhpR3XGyOvt/vNKS7XPXT5EDbwKW9vCjWH4PpfQvgD/+JgN0VJKA==", 338 | "dev": true, 339 | "dependencies": { 340 | "safe-regex": "^1.1.0" 341 | } 342 | }, 343 | "node_modules/eslint-scope": { 344 | "version": "7.1.1", 345 | "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz", 346 | "integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==", 347 | "dev": true, 348 | "dependencies": { 349 | "esrecurse": "^4.3.0", 350 | "estraverse": "^5.2.0" 351 | }, 352 | "engines": { 353 | "node": "^12.22.0 || ^14.17.0 || >=16.0.0" 354 | } 355 | }, 356 | "node_modules/eslint-utils": { 357 | "version": "3.0.0", 358 | "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", 359 | "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", 360 | "dev": true, 361 | "dependencies": { 362 | "eslint-visitor-keys": "^2.0.0" 363 | }, 364 | "engines": { 365 | "node": "^10.0.0 || ^12.0.0 || >= 14.0.0" 366 | }, 367 | "funding": { 368 | "url": "https://github.com/sponsors/mysticatea" 369 | }, 370 | "peerDependencies": { 371 | "eslint": ">=5" 372 | } 373 | }, 374 | "node_modules/eslint-utils/node_modules/eslint-visitor-keys": { 375 | "version": "2.1.0", 376 | "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", 377 | "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", 378 | "dev": true, 379 | "engines": { 380 | "node": ">=10" 381 | } 382 | }, 383 | "node_modules/eslint-visitor-keys": { 384 | "version": "3.3.0", 385 | "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", 386 | "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", 387 | "dev": true, 388 | "engines": { 389 | "node": "^12.22.0 || ^14.17.0 || >=16.0.0" 390 | } 391 | }, 392 | "node_modules/espree": { 393 | "version": "9.3.1", 394 | "resolved": "https://registry.npmjs.org/espree/-/espree-9.3.1.tgz", 395 | "integrity": "sha512-bvdyLmJMfwkV3NCRl5ZhJf22zBFo1y8bYh3VYb+bfzqNB4Je68P2sSuXyuFquzWLebHpNd2/d5uv7yoP9ISnGQ==", 396 | "dev": true, 397 | "dependencies": { 398 | "acorn": "^8.7.0", 399 | "acorn-jsx": "^5.3.1", 400 | "eslint-visitor-keys": "^3.3.0" 401 | }, 402 | "engines": { 403 | "node": "^12.22.0 || ^14.17.0 || >=16.0.0" 404 | } 405 | }, 406 | "node_modules/esquery": { 407 | "version": "1.4.0", 408 | "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", 409 | "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", 410 | "dev": true, 411 | "dependencies": { 412 | "estraverse": "^5.1.0" 413 | }, 414 | "engines": { 415 | "node": ">=0.10" 416 | } 417 | }, 418 | "node_modules/esrecurse": { 419 | "version": "4.3.0", 420 | "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", 421 | "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", 422 | "dev": true, 423 | "dependencies": { 424 | "estraverse": "^5.2.0" 425 | }, 426 | "engines": { 427 | "node": ">=4.0" 428 | } 429 | }, 430 | "node_modules/estraverse": { 431 | "version": "5.3.0", 432 | "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", 433 | "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", 434 | "dev": true, 435 | "engines": { 436 | "node": ">=4.0" 437 | } 438 | }, 439 | "node_modules/esutils": { 440 | "version": "2.0.3", 441 | "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", 442 | "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", 443 | "dev": true, 444 | "engines": { 445 | "node": ">=0.10.0" 446 | } 447 | }, 448 | "node_modules/fast-deep-equal": { 449 | "version": "3.1.3", 450 | "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", 451 | "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", 452 | "dev": true 453 | }, 454 | "node_modules/fast-json-stable-stringify": { 455 | "version": "2.1.0", 456 | "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", 457 | "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", 458 | "dev": true 459 | }, 460 | "node_modules/fast-levenshtein": { 461 | "version": "2.0.6", 462 | "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", 463 | "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", 464 | "dev": true 465 | }, 466 | "node_modules/file-entry-cache": { 467 | "version": "6.0.1", 468 | "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", 469 | "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", 470 | "dev": true, 471 | "dependencies": { 472 | "flat-cache": "^3.0.4" 473 | }, 474 | "engines": { 475 | "node": "^10.12.0 || >=12.0.0" 476 | } 477 | }, 478 | "node_modules/flat-cache": { 479 | "version": "3.0.4", 480 | "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", 481 | "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", 482 | "dev": true, 483 | "dependencies": { 484 | "flatted": "^3.1.0", 485 | "rimraf": "^3.0.2" 486 | }, 487 | "engines": { 488 | "node": "^10.12.0 || >=12.0.0" 489 | } 490 | }, 491 | "node_modules/flatted": { 492 | "version": "3.2.5", 493 | "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.5.tgz", 494 | "integrity": "sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg==", 495 | "dev": true 496 | }, 497 | "node_modules/fs.realpath": { 498 | "version": "1.0.0", 499 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", 500 | "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", 501 | "dev": true 502 | }, 503 | "node_modules/functional-red-black-tree": { 504 | "version": "1.0.1", 505 | "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", 506 | "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", 507 | "dev": true 508 | }, 509 | "node_modules/glob": { 510 | "version": "7.2.0", 511 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", 512 | "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", 513 | "dev": true, 514 | "dependencies": { 515 | "fs.realpath": "^1.0.0", 516 | "inflight": "^1.0.4", 517 | "inherits": "2", 518 | "minimatch": "^3.0.4", 519 | "once": "^1.3.0", 520 | "path-is-absolute": "^1.0.0" 521 | }, 522 | "engines": { 523 | "node": "*" 524 | }, 525 | "funding": { 526 | "url": "https://github.com/sponsors/isaacs" 527 | } 528 | }, 529 | "node_modules/glob-parent": { 530 | "version": "6.0.2", 531 | "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", 532 | "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", 533 | "dev": true, 534 | "dependencies": { 535 | "is-glob": "^4.0.3" 536 | }, 537 | "engines": { 538 | "node": ">=10.13.0" 539 | } 540 | }, 541 | "node_modules/globals": { 542 | "version": "13.12.1", 543 | "resolved": "https://registry.npmjs.org/globals/-/globals-13.12.1.tgz", 544 | "integrity": "sha512-317dFlgY2pdJZ9rspXDks7073GpDmXdfbM3vYYp0HAMKGDh1FfWPleI2ljVNLQX5M5lXcAslTcPTrOrMEFOjyw==", 545 | "dev": true, 546 | "dependencies": { 547 | "type-fest": "^0.20.2" 548 | }, 549 | "engines": { 550 | "node": ">=8" 551 | }, 552 | "funding": { 553 | "url": "https://github.com/sponsors/sindresorhus" 554 | } 555 | }, 556 | "node_modules/has-flag": { 557 | "version": "4.0.0", 558 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", 559 | "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", 560 | "dev": true, 561 | "engines": { 562 | "node": ">=8" 563 | } 564 | }, 565 | "node_modules/ignore": { 566 | "version": "5.2.0", 567 | "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", 568 | "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", 569 | "dev": true, 570 | "engines": { 571 | "node": ">= 4" 572 | } 573 | }, 574 | "node_modules/import-fresh": { 575 | "version": "3.3.0", 576 | "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", 577 | "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", 578 | "dev": true, 579 | "dependencies": { 580 | "parent-module": "^1.0.0", 581 | "resolve-from": "^4.0.0" 582 | }, 583 | "engines": { 584 | "node": ">=6" 585 | }, 586 | "funding": { 587 | "url": "https://github.com/sponsors/sindresorhus" 588 | } 589 | }, 590 | "node_modules/imurmurhash": { 591 | "version": "0.1.4", 592 | "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", 593 | "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", 594 | "dev": true, 595 | "engines": { 596 | "node": ">=0.8.19" 597 | } 598 | }, 599 | "node_modules/inflight": { 600 | "version": "1.0.6", 601 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", 602 | "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", 603 | "dev": true, 604 | "dependencies": { 605 | "once": "^1.3.0", 606 | "wrappy": "1" 607 | } 608 | }, 609 | "node_modules/inherits": { 610 | "version": "2.0.4", 611 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 612 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", 613 | "dev": true 614 | }, 615 | "node_modules/is-extglob": { 616 | "version": "2.1.1", 617 | "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", 618 | "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", 619 | "dev": true, 620 | "engines": { 621 | "node": ">=0.10.0" 622 | } 623 | }, 624 | "node_modules/is-glob": { 625 | "version": "4.0.3", 626 | "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", 627 | "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", 628 | "dev": true, 629 | "dependencies": { 630 | "is-extglob": "^2.1.1" 631 | }, 632 | "engines": { 633 | "node": ">=0.10.0" 634 | } 635 | }, 636 | "node_modules/isexe": { 637 | "version": "2.0.0", 638 | "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", 639 | "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", 640 | "dev": true 641 | }, 642 | "node_modules/js-yaml": { 643 | "version": "4.1.0", 644 | "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", 645 | "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", 646 | "dev": true, 647 | "dependencies": { 648 | "argparse": "^2.0.1" 649 | }, 650 | "bin": { 651 | "js-yaml": "bin/js-yaml.js" 652 | } 653 | }, 654 | "node_modules/json-schema-traverse": { 655 | "version": "0.4.1", 656 | "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", 657 | "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", 658 | "dev": true 659 | }, 660 | "node_modules/json-stable-stringify-without-jsonify": { 661 | "version": "1.0.1", 662 | "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", 663 | "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", 664 | "dev": true 665 | }, 666 | "node_modules/levn": { 667 | "version": "0.4.1", 668 | "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", 669 | "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", 670 | "dev": true, 671 | "dependencies": { 672 | "prelude-ls": "^1.2.1", 673 | "type-check": "~0.4.0" 674 | }, 675 | "engines": { 676 | "node": ">= 0.8.0" 677 | } 678 | }, 679 | "node_modules/lodash.merge": { 680 | "version": "4.6.2", 681 | "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", 682 | "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", 683 | "dev": true 684 | }, 685 | "node_modules/minimatch": { 686 | "version": "3.1.1", 687 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.1.tgz", 688 | "integrity": "sha512-reLxBcKUPNBnc/sVtAbxgRVFSegoGeLaSjmphNhcwcolhYLRgtJscn5mRl6YRZNQv40Y7P6JM2YhSIsbL9OB5A==", 689 | "dev": true, 690 | "dependencies": { 691 | "brace-expansion": "^1.1.7" 692 | }, 693 | "engines": { 694 | "node": "*" 695 | } 696 | }, 697 | "node_modules/ms": { 698 | "version": "2.1.2", 699 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", 700 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", 701 | "dev": true 702 | }, 703 | "node_modules/natural-compare": { 704 | "version": "1.4.0", 705 | "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", 706 | "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", 707 | "dev": true 708 | }, 709 | "node_modules/once": { 710 | "version": "1.4.0", 711 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 712 | "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", 713 | "dev": true, 714 | "dependencies": { 715 | "wrappy": "1" 716 | } 717 | }, 718 | "node_modules/optionator": { 719 | "version": "0.9.1", 720 | "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", 721 | "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", 722 | "dev": true, 723 | "dependencies": { 724 | "deep-is": "^0.1.3", 725 | "fast-levenshtein": "^2.0.6", 726 | "levn": "^0.4.1", 727 | "prelude-ls": "^1.2.1", 728 | "type-check": "^0.4.0", 729 | "word-wrap": "^1.2.3" 730 | }, 731 | "engines": { 732 | "node": ">= 0.8.0" 733 | } 734 | }, 735 | "node_modules/parent-module": { 736 | "version": "1.0.1", 737 | "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", 738 | "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", 739 | "dev": true, 740 | "dependencies": { 741 | "callsites": "^3.0.0" 742 | }, 743 | "engines": { 744 | "node": ">=6" 745 | } 746 | }, 747 | "node_modules/path-is-absolute": { 748 | "version": "1.0.1", 749 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", 750 | "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", 751 | "dev": true, 752 | "engines": { 753 | "node": ">=0.10.0" 754 | } 755 | }, 756 | "node_modules/path-key": { 757 | "version": "3.1.1", 758 | "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", 759 | "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", 760 | "dev": true, 761 | "engines": { 762 | "node": ">=8" 763 | } 764 | }, 765 | "node_modules/prelude-ls": { 766 | "version": "1.2.1", 767 | "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", 768 | "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", 769 | "dev": true, 770 | "engines": { 771 | "node": ">= 0.8.0" 772 | } 773 | }, 774 | "node_modules/punycode": { 775 | "version": "2.1.1", 776 | "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", 777 | "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", 778 | "dev": true, 779 | "engines": { 780 | "node": ">=6" 781 | } 782 | }, 783 | "node_modules/regexpp": { 784 | "version": "3.2.0", 785 | "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", 786 | "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", 787 | "dev": true, 788 | "engines": { 789 | "node": ">=8" 790 | }, 791 | "funding": { 792 | "url": "https://github.com/sponsors/mysticatea" 793 | } 794 | }, 795 | "node_modules/resolve-from": { 796 | "version": "4.0.0", 797 | "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", 798 | "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", 799 | "dev": true, 800 | "engines": { 801 | "node": ">=4" 802 | } 803 | }, 804 | "node_modules/ret": { 805 | "version": "0.1.14", 806 | "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.14.tgz", 807 | "integrity": "sha1-WMY2g3sS4WH4o4DPCBxqIw/RZk4=", 808 | "dev": true, 809 | "engines": { 810 | "node": ">=0.12" 811 | } 812 | }, 813 | "node_modules/rimraf": { 814 | "version": "3.0.2", 815 | "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", 816 | "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", 817 | "dev": true, 818 | "dependencies": { 819 | "glob": "^7.1.3" 820 | }, 821 | "bin": { 822 | "rimraf": "bin.js" 823 | }, 824 | "funding": { 825 | "url": "https://github.com/sponsors/isaacs" 826 | } 827 | }, 828 | "node_modules/safe-regex": { 829 | "version": "1.1.0", 830 | "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", 831 | "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", 832 | "dev": true, 833 | "dependencies": { 834 | "ret": "~0.1.10" 835 | } 836 | }, 837 | "node_modules/shebang-command": { 838 | "version": "2.0.0", 839 | "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", 840 | "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", 841 | "dev": true, 842 | "dependencies": { 843 | "shebang-regex": "^3.0.0" 844 | }, 845 | "engines": { 846 | "node": ">=8" 847 | } 848 | }, 849 | "node_modules/shebang-regex": { 850 | "version": "3.0.0", 851 | "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", 852 | "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", 853 | "dev": true, 854 | "engines": { 855 | "node": ">=8" 856 | } 857 | }, 858 | "node_modules/strip-ansi": { 859 | "version": "6.0.1", 860 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", 861 | "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", 862 | "dev": true, 863 | "dependencies": { 864 | "ansi-regex": "^5.0.1" 865 | }, 866 | "engines": { 867 | "node": ">=8" 868 | } 869 | }, 870 | "node_modules/strip-json-comments": { 871 | "version": "3.1.1", 872 | "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", 873 | "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", 874 | "dev": true, 875 | "engines": { 876 | "node": ">=8" 877 | }, 878 | "funding": { 879 | "url": "https://github.com/sponsors/sindresorhus" 880 | } 881 | }, 882 | "node_modules/supports-color": { 883 | "version": "7.2.0", 884 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", 885 | "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", 886 | "dev": true, 887 | "dependencies": { 888 | "has-flag": "^4.0.0" 889 | }, 890 | "engines": { 891 | "node": ">=8" 892 | } 893 | }, 894 | "node_modules/text-table": { 895 | "version": "0.2.0", 896 | "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", 897 | "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", 898 | "dev": true 899 | }, 900 | "node_modules/type-check": { 901 | "version": "0.4.0", 902 | "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", 903 | "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", 904 | "dev": true, 905 | "dependencies": { 906 | "prelude-ls": "^1.2.1" 907 | }, 908 | "engines": { 909 | "node": ">= 0.8.0" 910 | } 911 | }, 912 | "node_modules/type-fest": { 913 | "version": "0.20.2", 914 | "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", 915 | "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", 916 | "dev": true, 917 | "engines": { 918 | "node": ">=10" 919 | }, 920 | "funding": { 921 | "url": "https://github.com/sponsors/sindresorhus" 922 | } 923 | }, 924 | "node_modules/uri-js": { 925 | "version": "4.4.1", 926 | "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", 927 | "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", 928 | "dev": true, 929 | "dependencies": { 930 | "punycode": "^2.1.0" 931 | } 932 | }, 933 | "node_modules/v8-compile-cache": { 934 | "version": "2.3.0", 935 | "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", 936 | "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", 937 | "dev": true 938 | }, 939 | "node_modules/which": { 940 | "version": "2.0.2", 941 | "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", 942 | "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", 943 | "dev": true, 944 | "dependencies": { 945 | "isexe": "^2.0.0" 946 | }, 947 | "bin": { 948 | "node-which": "bin/node-which" 949 | }, 950 | "engines": { 951 | "node": ">= 8" 952 | } 953 | }, 954 | "node_modules/word-wrap": { 955 | "version": "1.2.4", 956 | "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.4.tgz", 957 | "integrity": "sha512-2V81OA4ugVo5pRo46hAoD2ivUJx8jXmWXfUkY4KFNw0hEptvN0QfH3K4nHiwzGeKl5rFKedV48QVoqYavy4YpA==", 958 | "dev": true, 959 | "engines": { 960 | "node": ">=0.10.0" 961 | } 962 | }, 963 | "node_modules/wrappy": { 964 | "version": "1.0.2", 965 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 966 | "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", 967 | "dev": true 968 | } 969 | }, 970 | "dependencies": { 971 | "@eslint/eslintrc": { 972 | "version": "1.1.0", 973 | "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.1.0.tgz", 974 | "integrity": "sha512-C1DfL7XX4nPqGd6jcP01W9pVM1HYCuUkFk1432D7F0v3JSlUIeOYn9oCoi3eoLZ+iwBSb29BMFxxny0YrrEZqg==", 975 | "dev": true, 976 | "requires": { 977 | "ajv": "^6.12.4", 978 | "debug": "^4.3.2", 979 | "espree": "^9.3.1", 980 | "globals": "^13.9.0", 981 | "ignore": "^4.0.6", 982 | "import-fresh": "^3.2.1", 983 | "js-yaml": "^4.1.0", 984 | "minimatch": "^3.0.4", 985 | "strip-json-comments": "^3.1.1" 986 | }, 987 | "dependencies": { 988 | "ignore": { 989 | "version": "4.0.6", 990 | "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", 991 | "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", 992 | "dev": true 993 | } 994 | } 995 | }, 996 | "@humanwhocodes/config-array": { 997 | "version": "0.9.3", 998 | "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.9.3.tgz", 999 | "integrity": "sha512-3xSMlXHh03hCcCmFc0rbKp3Ivt2PFEJnQUJDDMTJQ2wkECZWdq4GePs2ctc5H8zV+cHPaq8k2vU8mrQjA6iHdQ==", 1000 | "dev": true, 1001 | "requires": { 1002 | "@humanwhocodes/object-schema": "^1.2.1", 1003 | "debug": "^4.1.1", 1004 | "minimatch": "^3.0.4" 1005 | } 1006 | }, 1007 | "@humanwhocodes/object-schema": { 1008 | "version": "1.2.1", 1009 | "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", 1010 | "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", 1011 | "dev": true 1012 | }, 1013 | "acorn": { 1014 | "version": "8.7.0", 1015 | "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.0.tgz", 1016 | "integrity": "sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ==", 1017 | "dev": true 1018 | }, 1019 | "acorn-jsx": { 1020 | "version": "5.3.2", 1021 | "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", 1022 | "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", 1023 | "dev": true, 1024 | "requires": {} 1025 | }, 1026 | "ajv": { 1027 | "version": "6.12.6", 1028 | "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", 1029 | "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", 1030 | "dev": true, 1031 | "requires": { 1032 | "fast-deep-equal": "^3.1.1", 1033 | "fast-json-stable-stringify": "^2.0.0", 1034 | "json-schema-traverse": "^0.4.1", 1035 | "uri-js": "^4.2.2" 1036 | } 1037 | }, 1038 | "ansi-regex": { 1039 | "version": "5.0.1", 1040 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", 1041 | "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", 1042 | "dev": true 1043 | }, 1044 | "ansi-styles": { 1045 | "version": "4.3.0", 1046 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", 1047 | "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", 1048 | "dev": true, 1049 | "requires": { 1050 | "color-convert": "^2.0.1" 1051 | } 1052 | }, 1053 | "argparse": { 1054 | "version": "2.0.1", 1055 | "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", 1056 | "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", 1057 | "dev": true 1058 | }, 1059 | "balanced-match": { 1060 | "version": "1.0.2", 1061 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", 1062 | "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", 1063 | "dev": true 1064 | }, 1065 | "brace-expansion": { 1066 | "version": "1.1.11", 1067 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 1068 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 1069 | "dev": true, 1070 | "requires": { 1071 | "balanced-match": "^1.0.0", 1072 | "concat-map": "0.0.1" 1073 | } 1074 | }, 1075 | "callsites": { 1076 | "version": "3.1.0", 1077 | "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", 1078 | "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", 1079 | "dev": true 1080 | }, 1081 | "chalk": { 1082 | "version": "4.1.2", 1083 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", 1084 | "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", 1085 | "dev": true, 1086 | "requires": { 1087 | "ansi-styles": "^4.1.0", 1088 | "supports-color": "^7.1.0" 1089 | } 1090 | }, 1091 | "color-convert": { 1092 | "version": "2.0.1", 1093 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", 1094 | "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", 1095 | "dev": true, 1096 | "requires": { 1097 | "color-name": "~1.1.4" 1098 | } 1099 | }, 1100 | "color-name": { 1101 | "version": "1.1.4", 1102 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", 1103 | "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", 1104 | "dev": true 1105 | }, 1106 | "commander": { 1107 | "version": "2.19.0", 1108 | "resolved": "https://registry.npmjs.org/commander/-/commander-2.19.0.tgz", 1109 | "integrity": "sha512-6tvAOO+D6OENvRAh524Dh9jcfKTYDQAqvqezbCW82xj5X0pSrcpxtvRKHLG0yBY6SD7PSDrJaj+0AiOcKVd1Xg==" 1110 | }, 1111 | "concat-map": { 1112 | "version": "0.0.1", 1113 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 1114 | "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", 1115 | "dev": true 1116 | }, 1117 | "cross-spawn": { 1118 | "version": "7.0.3", 1119 | "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", 1120 | "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", 1121 | "dev": true, 1122 | "requires": { 1123 | "path-key": "^3.1.0", 1124 | "shebang-command": "^2.0.0", 1125 | "which": "^2.0.1" 1126 | } 1127 | }, 1128 | "debug": { 1129 | "version": "4.3.3", 1130 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", 1131 | "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", 1132 | "dev": true, 1133 | "requires": { 1134 | "ms": "2.1.2" 1135 | } 1136 | }, 1137 | "deep-is": { 1138 | "version": "0.1.4", 1139 | "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", 1140 | "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", 1141 | "dev": true 1142 | }, 1143 | "doctrine": { 1144 | "version": "3.0.0", 1145 | "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", 1146 | "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", 1147 | "dev": true, 1148 | "requires": { 1149 | "esutils": "^2.0.2" 1150 | } 1151 | }, 1152 | "escape-string-regexp": { 1153 | "version": "4.0.0", 1154 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", 1155 | "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", 1156 | "dev": true 1157 | }, 1158 | "eslint": { 1159 | "version": "8.9.0", 1160 | "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.9.0.tgz", 1161 | "integrity": "sha512-PB09IGwv4F4b0/atrbcMFboF/giawbBLVC7fyDamk5Wtey4Jh2K+rYaBhCAbUyEI4QzB1ly09Uglc9iCtFaG2Q==", 1162 | "dev": true, 1163 | "requires": { 1164 | "@eslint/eslintrc": "^1.1.0", 1165 | "@humanwhocodes/config-array": "^0.9.2", 1166 | "ajv": "^6.10.0", 1167 | "chalk": "^4.0.0", 1168 | "cross-spawn": "^7.0.2", 1169 | "debug": "^4.3.2", 1170 | "doctrine": "^3.0.0", 1171 | "escape-string-regexp": "^4.0.0", 1172 | "eslint-scope": "^7.1.1", 1173 | "eslint-utils": "^3.0.0", 1174 | "eslint-visitor-keys": "^3.3.0", 1175 | "espree": "^9.3.1", 1176 | "esquery": "^1.4.0", 1177 | "esutils": "^2.0.2", 1178 | "fast-deep-equal": "^3.1.3", 1179 | "file-entry-cache": "^6.0.1", 1180 | "functional-red-black-tree": "^1.0.1", 1181 | "glob-parent": "^6.0.1", 1182 | "globals": "^13.6.0", 1183 | "ignore": "^5.2.0", 1184 | "import-fresh": "^3.0.0", 1185 | "imurmurhash": "^0.1.4", 1186 | "is-glob": "^4.0.0", 1187 | "js-yaml": "^4.1.0", 1188 | "json-stable-stringify-without-jsonify": "^1.0.1", 1189 | "levn": "^0.4.1", 1190 | "lodash.merge": "^4.6.2", 1191 | "minimatch": "^3.0.4", 1192 | "natural-compare": "^1.4.0", 1193 | "optionator": "^0.9.1", 1194 | "regexpp": "^3.2.0", 1195 | "strip-ansi": "^6.0.1", 1196 | "strip-json-comments": "^3.1.0", 1197 | "text-table": "^0.2.0", 1198 | "v8-compile-cache": "^2.0.3" 1199 | } 1200 | }, 1201 | "eslint-config-google": { 1202 | "version": "0.14.0", 1203 | "resolved": "https://registry.npmjs.org/eslint-config-google/-/eslint-config-google-0.14.0.tgz", 1204 | "integrity": "sha512-WsbX4WbjuMvTdeVL6+J3rK1RGhCTqjsFjX7UMSMgZiyxxaNLkoJENbrGExzERFeoTpGw3F3FypTiWAP9ZXzkEw==", 1205 | "dev": true, 1206 | "requires": {} 1207 | }, 1208 | "eslint-plugin-security": { 1209 | "version": "1.4.0", 1210 | "resolved": "https://registry.npmjs.org/eslint-plugin-security/-/eslint-plugin-security-1.4.0.tgz", 1211 | "integrity": "sha512-xlS7P2PLMXeqfhyf3NpqbvbnW04kN8M9NtmhpR3XGyOvt/vNKS7XPXT5EDbwKW9vCjWH4PpfQvgD/+JgN0VJKA==", 1212 | "dev": true, 1213 | "requires": { 1214 | "safe-regex": "^1.1.0" 1215 | } 1216 | }, 1217 | "eslint-scope": { 1218 | "version": "7.1.1", 1219 | "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz", 1220 | "integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==", 1221 | "dev": true, 1222 | "requires": { 1223 | "esrecurse": "^4.3.0", 1224 | "estraverse": "^5.2.0" 1225 | } 1226 | }, 1227 | "eslint-utils": { 1228 | "version": "3.0.0", 1229 | "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", 1230 | "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", 1231 | "dev": true, 1232 | "requires": { 1233 | "eslint-visitor-keys": "^2.0.0" 1234 | }, 1235 | "dependencies": { 1236 | "eslint-visitor-keys": { 1237 | "version": "2.1.0", 1238 | "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", 1239 | "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", 1240 | "dev": true 1241 | } 1242 | } 1243 | }, 1244 | "eslint-visitor-keys": { 1245 | "version": "3.3.0", 1246 | "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", 1247 | "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", 1248 | "dev": true 1249 | }, 1250 | "espree": { 1251 | "version": "9.3.1", 1252 | "resolved": "https://registry.npmjs.org/espree/-/espree-9.3.1.tgz", 1253 | "integrity": "sha512-bvdyLmJMfwkV3NCRl5ZhJf22zBFo1y8bYh3VYb+bfzqNB4Je68P2sSuXyuFquzWLebHpNd2/d5uv7yoP9ISnGQ==", 1254 | "dev": true, 1255 | "requires": { 1256 | "acorn": "^8.7.0", 1257 | "acorn-jsx": "^5.3.1", 1258 | "eslint-visitor-keys": "^3.3.0" 1259 | } 1260 | }, 1261 | "esquery": { 1262 | "version": "1.4.0", 1263 | "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", 1264 | "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", 1265 | "dev": true, 1266 | "requires": { 1267 | "estraverse": "^5.1.0" 1268 | } 1269 | }, 1270 | "esrecurse": { 1271 | "version": "4.3.0", 1272 | "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", 1273 | "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", 1274 | "dev": true, 1275 | "requires": { 1276 | "estraverse": "^5.2.0" 1277 | } 1278 | }, 1279 | "estraverse": { 1280 | "version": "5.3.0", 1281 | "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", 1282 | "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", 1283 | "dev": true 1284 | }, 1285 | "esutils": { 1286 | "version": "2.0.3", 1287 | "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", 1288 | "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", 1289 | "dev": true 1290 | }, 1291 | "fast-deep-equal": { 1292 | "version": "3.1.3", 1293 | "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", 1294 | "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", 1295 | "dev": true 1296 | }, 1297 | "fast-json-stable-stringify": { 1298 | "version": "2.1.0", 1299 | "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", 1300 | "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", 1301 | "dev": true 1302 | }, 1303 | "fast-levenshtein": { 1304 | "version": "2.0.6", 1305 | "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", 1306 | "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", 1307 | "dev": true 1308 | }, 1309 | "file-entry-cache": { 1310 | "version": "6.0.1", 1311 | "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", 1312 | "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", 1313 | "dev": true, 1314 | "requires": { 1315 | "flat-cache": "^3.0.4" 1316 | } 1317 | }, 1318 | "flat-cache": { 1319 | "version": "3.0.4", 1320 | "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", 1321 | "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", 1322 | "dev": true, 1323 | "requires": { 1324 | "flatted": "^3.1.0", 1325 | "rimraf": "^3.0.2" 1326 | } 1327 | }, 1328 | "flatted": { 1329 | "version": "3.2.5", 1330 | "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.5.tgz", 1331 | "integrity": "sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg==", 1332 | "dev": true 1333 | }, 1334 | "fs.realpath": { 1335 | "version": "1.0.0", 1336 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", 1337 | "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", 1338 | "dev": true 1339 | }, 1340 | "functional-red-black-tree": { 1341 | "version": "1.0.1", 1342 | "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", 1343 | "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", 1344 | "dev": true 1345 | }, 1346 | "glob": { 1347 | "version": "7.2.0", 1348 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", 1349 | "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", 1350 | "dev": true, 1351 | "requires": { 1352 | "fs.realpath": "^1.0.0", 1353 | "inflight": "^1.0.4", 1354 | "inherits": "2", 1355 | "minimatch": "^3.0.4", 1356 | "once": "^1.3.0", 1357 | "path-is-absolute": "^1.0.0" 1358 | } 1359 | }, 1360 | "glob-parent": { 1361 | "version": "6.0.2", 1362 | "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", 1363 | "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", 1364 | "dev": true, 1365 | "requires": { 1366 | "is-glob": "^4.0.3" 1367 | } 1368 | }, 1369 | "globals": { 1370 | "version": "13.12.1", 1371 | "resolved": "https://registry.npmjs.org/globals/-/globals-13.12.1.tgz", 1372 | "integrity": "sha512-317dFlgY2pdJZ9rspXDks7073GpDmXdfbM3vYYp0HAMKGDh1FfWPleI2ljVNLQX5M5lXcAslTcPTrOrMEFOjyw==", 1373 | "dev": true, 1374 | "requires": { 1375 | "type-fest": "^0.20.2" 1376 | } 1377 | }, 1378 | "has-flag": { 1379 | "version": "4.0.0", 1380 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", 1381 | "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", 1382 | "dev": true 1383 | }, 1384 | "ignore": { 1385 | "version": "5.2.0", 1386 | "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", 1387 | "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", 1388 | "dev": true 1389 | }, 1390 | "import-fresh": { 1391 | "version": "3.3.0", 1392 | "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", 1393 | "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", 1394 | "dev": true, 1395 | "requires": { 1396 | "parent-module": "^1.0.0", 1397 | "resolve-from": "^4.0.0" 1398 | } 1399 | }, 1400 | "imurmurhash": { 1401 | "version": "0.1.4", 1402 | "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", 1403 | "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", 1404 | "dev": true 1405 | }, 1406 | "inflight": { 1407 | "version": "1.0.6", 1408 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", 1409 | "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", 1410 | "dev": true, 1411 | "requires": { 1412 | "once": "^1.3.0", 1413 | "wrappy": "1" 1414 | } 1415 | }, 1416 | "inherits": { 1417 | "version": "2.0.4", 1418 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 1419 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", 1420 | "dev": true 1421 | }, 1422 | "is-extglob": { 1423 | "version": "2.1.1", 1424 | "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", 1425 | "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", 1426 | "dev": true 1427 | }, 1428 | "is-glob": { 1429 | "version": "4.0.3", 1430 | "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", 1431 | "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", 1432 | "dev": true, 1433 | "requires": { 1434 | "is-extglob": "^2.1.1" 1435 | } 1436 | }, 1437 | "isexe": { 1438 | "version": "2.0.0", 1439 | "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", 1440 | "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", 1441 | "dev": true 1442 | }, 1443 | "js-yaml": { 1444 | "version": "4.1.0", 1445 | "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", 1446 | "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", 1447 | "dev": true, 1448 | "requires": { 1449 | "argparse": "^2.0.1" 1450 | } 1451 | }, 1452 | "json-schema-traverse": { 1453 | "version": "0.4.1", 1454 | "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", 1455 | "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", 1456 | "dev": true 1457 | }, 1458 | "json-stable-stringify-without-jsonify": { 1459 | "version": "1.0.1", 1460 | "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", 1461 | "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", 1462 | "dev": true 1463 | }, 1464 | "levn": { 1465 | "version": "0.4.1", 1466 | "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", 1467 | "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", 1468 | "dev": true, 1469 | "requires": { 1470 | "prelude-ls": "^1.2.1", 1471 | "type-check": "~0.4.0" 1472 | } 1473 | }, 1474 | "lodash.merge": { 1475 | "version": "4.6.2", 1476 | "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", 1477 | "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", 1478 | "dev": true 1479 | }, 1480 | "minimatch": { 1481 | "version": "3.1.1", 1482 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.1.tgz", 1483 | "integrity": "sha512-reLxBcKUPNBnc/sVtAbxgRVFSegoGeLaSjmphNhcwcolhYLRgtJscn5mRl6YRZNQv40Y7P6JM2YhSIsbL9OB5A==", 1484 | "dev": true, 1485 | "requires": { 1486 | "brace-expansion": "^1.1.7" 1487 | } 1488 | }, 1489 | "ms": { 1490 | "version": "2.1.2", 1491 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", 1492 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", 1493 | "dev": true 1494 | }, 1495 | "natural-compare": { 1496 | "version": "1.4.0", 1497 | "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", 1498 | "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", 1499 | "dev": true 1500 | }, 1501 | "once": { 1502 | "version": "1.4.0", 1503 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 1504 | "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", 1505 | "dev": true, 1506 | "requires": { 1507 | "wrappy": "1" 1508 | } 1509 | }, 1510 | "optionator": { 1511 | "version": "0.9.1", 1512 | "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", 1513 | "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", 1514 | "dev": true, 1515 | "requires": { 1516 | "deep-is": "^0.1.3", 1517 | "fast-levenshtein": "^2.0.6", 1518 | "levn": "^0.4.1", 1519 | "prelude-ls": "^1.2.1", 1520 | "type-check": "^0.4.0", 1521 | "word-wrap": "^1.2.3" 1522 | } 1523 | }, 1524 | "parent-module": { 1525 | "version": "1.0.1", 1526 | "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", 1527 | "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", 1528 | "dev": true, 1529 | "requires": { 1530 | "callsites": "^3.0.0" 1531 | } 1532 | }, 1533 | "path-is-absolute": { 1534 | "version": "1.0.1", 1535 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", 1536 | "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", 1537 | "dev": true 1538 | }, 1539 | "path-key": { 1540 | "version": "3.1.1", 1541 | "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", 1542 | "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", 1543 | "dev": true 1544 | }, 1545 | "prelude-ls": { 1546 | "version": "1.2.1", 1547 | "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", 1548 | "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", 1549 | "dev": true 1550 | }, 1551 | "punycode": { 1552 | "version": "2.1.1", 1553 | "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", 1554 | "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", 1555 | "dev": true 1556 | }, 1557 | "regexpp": { 1558 | "version": "3.2.0", 1559 | "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", 1560 | "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", 1561 | "dev": true 1562 | }, 1563 | "resolve-from": { 1564 | "version": "4.0.0", 1565 | "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", 1566 | "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", 1567 | "dev": true 1568 | }, 1569 | "ret": { 1570 | "version": "0.1.14", 1571 | "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.14.tgz", 1572 | "integrity": "sha1-WMY2g3sS4WH4o4DPCBxqIw/RZk4=", 1573 | "dev": true 1574 | }, 1575 | "rimraf": { 1576 | "version": "3.0.2", 1577 | "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", 1578 | "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", 1579 | "dev": true, 1580 | "requires": { 1581 | "glob": "^7.1.3" 1582 | } 1583 | }, 1584 | "safe-regex": { 1585 | "version": "1.1.0", 1586 | "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", 1587 | "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", 1588 | "dev": true, 1589 | "requires": { 1590 | "ret": "~0.1.10" 1591 | } 1592 | }, 1593 | "shebang-command": { 1594 | "version": "2.0.0", 1595 | "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", 1596 | "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", 1597 | "dev": true, 1598 | "requires": { 1599 | "shebang-regex": "^3.0.0" 1600 | } 1601 | }, 1602 | "shebang-regex": { 1603 | "version": "3.0.0", 1604 | "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", 1605 | "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", 1606 | "dev": true 1607 | }, 1608 | "strip-ansi": { 1609 | "version": "6.0.1", 1610 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", 1611 | "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", 1612 | "dev": true, 1613 | "requires": { 1614 | "ansi-regex": "^5.0.1" 1615 | } 1616 | }, 1617 | "strip-json-comments": { 1618 | "version": "3.1.1", 1619 | "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", 1620 | "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", 1621 | "dev": true 1622 | }, 1623 | "supports-color": { 1624 | "version": "7.2.0", 1625 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", 1626 | "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", 1627 | "dev": true, 1628 | "requires": { 1629 | "has-flag": "^4.0.0" 1630 | } 1631 | }, 1632 | "text-table": { 1633 | "version": "0.2.0", 1634 | "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", 1635 | "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", 1636 | "dev": true 1637 | }, 1638 | "type-check": { 1639 | "version": "0.4.0", 1640 | "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", 1641 | "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", 1642 | "dev": true, 1643 | "requires": { 1644 | "prelude-ls": "^1.2.1" 1645 | } 1646 | }, 1647 | "type-fest": { 1648 | "version": "0.20.2", 1649 | "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", 1650 | "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", 1651 | "dev": true 1652 | }, 1653 | "uri-js": { 1654 | "version": "4.4.1", 1655 | "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", 1656 | "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", 1657 | "dev": true, 1658 | "requires": { 1659 | "punycode": "^2.1.0" 1660 | } 1661 | }, 1662 | "v8-compile-cache": { 1663 | "version": "2.3.0", 1664 | "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", 1665 | "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", 1666 | "dev": true 1667 | }, 1668 | "which": { 1669 | "version": "2.0.2", 1670 | "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", 1671 | "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", 1672 | "dev": true, 1673 | "requires": { 1674 | "isexe": "^2.0.0" 1675 | } 1676 | }, 1677 | "word-wrap": { 1678 | "version": "1.2.4", 1679 | "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.4.tgz", 1680 | "integrity": "sha512-2V81OA4ugVo5pRo46hAoD2ivUJx8jXmWXfUkY4KFNw0hEptvN0QfH3K4nHiwzGeKl5rFKedV48QVoqYavy4YpA==", 1681 | "dev": true 1682 | }, 1683 | "wrappy": { 1684 | "version": "1.0.2", 1685 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 1686 | "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", 1687 | "dev": true 1688 | } 1689 | } 1690 | } 1691 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "node-tcp-proxy", 3 | "description": "A simple TCP proxy built using Node.js", 4 | "license": "MIT", 5 | "version": "0.0.28", 6 | "main": "index.js", 7 | "types": "./index.d.ts", 8 | "repository": { 9 | "type": "git", 10 | "url": "https://github.com/tewarid/node-tcp-proxy.git" 11 | }, 12 | "bin": { 13 | "tcpproxy": "./tcp-proxy-cli.js" 14 | }, 15 | "dependencies": { 16 | "commander": "^2.19.0" 17 | }, 18 | "devDependencies": { 19 | "eslint": "^8.9.0", 20 | "eslint-config-google": "^0.14.0", 21 | "eslint-plugin-security": "^1.4.0" 22 | }, 23 | "false": {} 24 | } 25 | -------------------------------------------------------------------------------- /tcp-proxy-cli.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | var argv = require("commander"); 3 | var packageConfig = require('./package.json'); 4 | 5 | argv 6 | .usage("[options]") 7 | .version(packageConfig.version) 8 | .option("-p, --proxyPort ", 9 | "Proxy port number (required)", parseInt) 10 | .option("-h, --hostname [name]", "Name or IP address of host") 11 | .option("-n, --serviceHost ", 12 | "Name or IP address of service host(s); " + 13 | "if this is a comma separated list, " + 14 | "performs round-robin load balancing (required)") 15 | .option("-s, --servicePort ", "Service port number(s); " + 16 | "if this a comma separated list," + 17 | "it should have as many entries as serviceHost (required)") 18 | .option("-m, --localAddress
", 19 | "IP address of interface to use to connect to service") 20 | .option("-l, --localPort ", 21 | "Port number to use to connect to service") 22 | .option("-q, --q", "Be quiet") 23 | .option("-t, --tls [both]", "Use TLS 1.2 with clients; " + 24 | "specify both to also use TLS 1.2 with service", false) 25 | .option("-u, --rejectUnauthorized [value]", 26 | "Do not accept invalid certificate", false) 27 | .option("-c, --pfx [file]", "Private key file", 28 | require.resolve("./cert.pfx")) 29 | .option("-a, --passphrase [value]", 30 | "Passphrase to access private key file", "abcd") 31 | .option("-i, --identUsers [user[,...]]", 32 | "Comma-separated list of authorized users", "") 33 | .option("-A, --allowedIPs [ip1[,...]]", 34 | "Comma-separated list of allowed IPs, overrides -i", "") 35 | .parse(process.argv); 36 | 37 | var options = Object.assign(argv, { 38 | quiet: argv.q === true, 39 | rejectUnauthorized: argv.rejectUnauthorized !== "false", 40 | identUsers: argv.identUsers === '' ? [] : argv.identUsers.split(','), 41 | allowedIps: argv.allowedIPs === '' ? [] : argv.allowedIPs.split(',') 42 | }); 43 | 44 | if (!argv.proxyPort || !argv.serviceHost || !argv.servicePort) { 45 | argv.help(); 46 | } 47 | 48 | var proxy = require("./tcp-proxy.js").createProxy(argv.proxyPort, 49 | argv.serviceHost, argv.servicePort, options); 50 | 51 | process.on("uncaughtException", function(err) { 52 | console.error(err); 53 | proxy.end(); 54 | }); 55 | 56 | process.on("SIGINT", function() { 57 | proxy.end(); 58 | }); 59 | -------------------------------------------------------------------------------- /tcp-proxy.js: -------------------------------------------------------------------------------- 1 | var net = require("net"); 2 | var tls = require('tls'); 3 | var fs = require('fs'); 4 | var util = require('util'); 5 | 6 | module.exports.createProxy = function(proxyPort, 7 | serviceHost, servicePort, options) { 8 | return new TcpProxy(proxyPort, serviceHost, servicePort, options); 9 | }; 10 | 11 | function uniqueKey(socket) { 12 | var key = socket.remoteAddress + ":" + socket.remotePort; 13 | return key; 14 | } 15 | 16 | function parse(o) { 17 | if (typeof o === "string") { 18 | return o.split(","); 19 | } else if (typeof o === "number") { 20 | return parse(o.toString()); 21 | } else if (Array.isArray(o)) { 22 | return o; 23 | } else { 24 | throw new Error("cannot parse object: " + o); 25 | } 26 | } 27 | 28 | function TcpProxy(proxyPort, serviceHost, servicePort, options) { 29 | this.proxyPort = proxyPort; 30 | this.serviceHosts = parse(serviceHost); 31 | this.servicePorts = parse(servicePort); 32 | this.serviceHostIndex = -1; 33 | this.options = this.parseOptions(options); 34 | this.proxyTlsOptions = { 35 | passphrase: this.options.passphrase, 36 | secureProtocol: "TLSv1_2_method" 37 | }; 38 | if (this.options.tls) { 39 | // eslint-disable-next-line security/detect-non-literal-fs-filename 40 | this.proxyTlsOptions.pfx = fs.readFileSync(this.options.pfx); 41 | } 42 | this.serviceTlsOptions = { 43 | rejectUnauthorized: this.options.rejectUnauthorized, 44 | secureProtocol: "TLSv1_2_method" 45 | }; 46 | this.proxySockets = {}; 47 | if (this.options.identUsers.length !== 0) { 48 | this.users = this.options.identUsers; 49 | this.log('Will only allow these users: '.concat(this.users.join(', '))); 50 | } else { 51 | this.log('Will allow all users'); 52 | } 53 | if (this.options.allowedIPs.length !== 0) { 54 | this.allowedIPs = this.options.allowedIPs; 55 | } 56 | this.createListener(); 57 | } 58 | 59 | TcpProxy.prototype.parseOptions = function(options) { 60 | return Object.assign({ 61 | quiet: true, 62 | pfx: require.resolve('./cert.pfx'), 63 | passphrase: 'abcd', 64 | rejectUnauthorized: true, 65 | identUsers: [], 66 | allowedIPs: [] 67 | }, options); 68 | }; 69 | 70 | TcpProxy.prototype.createListener = function() { 71 | var self = this; 72 | if (self.options.tls) { 73 | self.server = tls.createServer(self.options.customTlsOptions || self.proxyTlsOptions, function(socket) { 74 | self.handleClientConnection(socket); 75 | }); 76 | } else { 77 | self.server = net.createServer(function(socket) { 78 | self.handleClientConnection(socket); 79 | }); 80 | } 81 | self.server.listen(self.proxyPort, self.options.hostname); 82 | }; 83 | 84 | TcpProxy.prototype.handleClientConnection = function(socket) { 85 | var self = this; 86 | if (self.users) { 87 | self.handleAuth(socket); 88 | } else { 89 | self.handleClient(socket); 90 | } 91 | }; 92 | 93 | // RFC 1413 authentication 94 | TcpProxy.prototype.handleAuth = function(proxySocket) { 95 | var self = this; 96 | if (self.allowedIPs.includes(proxySocket.remoteAddress)) { 97 | self.handleClient(proxySocket); 98 | return; 99 | } 100 | var query = util.format("%d, %d", proxySocket.remotePort, this.proxyPort); 101 | var ident = new net.Socket(); 102 | var resp = undefined; 103 | ident.on('error', function(e) { 104 | resp = false; 105 | ident.destroy(); 106 | }); 107 | ident.on('data', function(data) { 108 | resp = data.toString().trim(); 109 | ident.destroy(); 110 | }); 111 | ident.on('close', function(data) { 112 | if (!resp) { 113 | self.log('No identd'); 114 | proxySocket.destroy(); 115 | return; 116 | } 117 | var user = resp.split(':').pop(); 118 | if (!self.users.includes(user)) { 119 | self.log(util.format('User "%s" unauthorized', user)); 120 | proxySocket.destroy(); 121 | } else { 122 | self.handleClient(proxySocket); 123 | } 124 | }); 125 | ident.connect(113, proxySocket.remoteAddress, function() { 126 | ident.write(query); 127 | ident.end(); 128 | }); 129 | }; 130 | 131 | TcpProxy.prototype.handleClient = function(proxySocket) { 132 | var self = this; 133 | var key = uniqueKey(proxySocket); 134 | self.proxySockets[`${key}`] = proxySocket; 135 | var context = { 136 | buffers: [], 137 | connected: false, 138 | proxySocket: proxySocket 139 | }; 140 | proxySocket.on("data", function(data) { 141 | self.handleUpstreamData(context, data); 142 | }); 143 | proxySocket.on("close", function(hadError) { 144 | delete self.proxySockets[uniqueKey(proxySocket)]; 145 | if (context.serviceSocket !== undefined) { 146 | context.serviceSocket.destroy(); 147 | } 148 | }); 149 | proxySocket.on("error", function(e) { 150 | if (context.serviceSocket !== undefined) { 151 | context.serviceSocket.destroy(); 152 | } 153 | }); 154 | }; 155 | 156 | TcpProxy.prototype.handleUpstreamData = function(context, data) { 157 | var self = this; 158 | Promise.resolve(self.intercept(self.options.upstream, context, data)) 159 | .then((processedData) => { 160 | if (context.connected) { 161 | context.serviceSocket.write(processedData); 162 | } else { 163 | context.buffers[context.buffers.length] = processedData; 164 | if (context.serviceSocket === undefined) { 165 | self.createServiceSocket(context); 166 | } 167 | } 168 | }); 169 | }; 170 | 171 | TcpProxy.prototype.createServiceSocket = function(context) { 172 | var self = this; 173 | var options = self.parseServiceOptions(context); 174 | if (self.options.tls === "both") { 175 | context.serviceSocket = tls.connect(options, function() { 176 | self.writeBuffer(context); 177 | }); 178 | } else { 179 | context.serviceSocket = new net.Socket(); 180 | context.serviceSocket.connect(options, function() { 181 | self.writeBuffer(context); 182 | }); 183 | } 184 | context.serviceSocket.on("data", function(data) { 185 | Promise.resolve(self.intercept(self.options.downstream, context, data)) 186 | .then((processedData) => context.proxySocket.write(processedData)); 187 | }); 188 | context.serviceSocket.on("close", function(hadError) { 189 | if (context.proxySocket !== undefined) { 190 | context.proxySocket.destroy(); 191 | } 192 | }); 193 | context.serviceSocket.on("error", function(e) { 194 | if (context.proxySocket !== undefined) { 195 | context.proxySocket.destroy(); 196 | } 197 | }); 198 | }; 199 | 200 | TcpProxy.prototype.parseServiceOptions = function(context) { 201 | var self = this; 202 | var i = self.getServiceHostIndex(context.proxySocket); 203 | return Object.assign({ 204 | port: self.servicePorts[parseInt(i, 10)], 205 | host: self.serviceHosts[parseInt(i, 10)], 206 | localAddress: self.options.localAddress, 207 | localPort: self.options.localPort 208 | }, self.serviceTlsOptions); 209 | }; 210 | 211 | TcpProxy.prototype.getServiceHostIndex = function(proxySocket) { 212 | this.serviceHostIndex++; 213 | if (this.serviceHostIndex == this.serviceHosts.length) { 214 | this.serviceHostIndex = 0; 215 | } 216 | var index = this.serviceHostIndex; 217 | if (this.options.serviceHostSelected) { 218 | index = this.options.serviceHostSelected(proxySocket, index); 219 | } 220 | return index; 221 | }; 222 | 223 | TcpProxy.prototype.writeBuffer = function(context) { 224 | context.connected = true; 225 | if (context.buffers.length > 0) { 226 | for (var i = 0; i < context.buffers.length; i++) { 227 | context.serviceSocket.write(context.buffers[parseInt(i, 10)]); 228 | } 229 | } 230 | }; 231 | 232 | TcpProxy.prototype.end = function() { 233 | this.server.close(); 234 | for (var key in this.proxySockets) { 235 | this.proxySockets[`${key}`].destroy(); 236 | } 237 | this.server.unref(); 238 | }; 239 | 240 | TcpProxy.prototype.log = function(msg) { 241 | if (!this.options.quiet) { 242 | console.log(msg); 243 | } 244 | }; 245 | 246 | TcpProxy.prototype.intercept = function(interceptor, context, data) { 247 | if (interceptor) { 248 | return interceptor(context, data); 249 | } 250 | return data; 251 | }; 252 | --------------------------------------------------------------------------------