├── .gitattributes ├── .github ├── dependabot.yml └── workflows │ └── push.yml ├── .gitignore ├── LICENSE ├── README.md ├── package-lock.json ├── package.json ├── src ├── agent.js ├── http.js └── socks.js └── test ├── agent.test.js ├── http-proxy.js ├── server.js └── socks-proxy.js /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: npm 4 | directory: "/" 5 | schedule: 6 | interval: daily 7 | time: "04:00" 8 | open-pull-requests-limit: 10 9 | ignore: 10 | - dependency-name: y18n 11 | versions: 12 | - 4.0.1 13 | - dependency-name: mocha 14 | versions: 15 | - 8.3.0 16 | - 8.3.1 17 | - dependency-name: socks 18 | versions: 19 | - 2.5.1 20 | -------------------------------------------------------------------------------- /.github/workflows/push.yml: -------------------------------------------------------------------------------- 1 | name: Node CI 2 | on: [push, pull_request] 3 | jobs: 4 | test: 5 | runs-on: ubuntu-latest 6 | strategy: 7 | matrix: 8 | node-version: [10.x, 12.x] 9 | steps: 10 | - uses: actions/checkout@master 11 | - name: Use Node.js ${{ matrix.node-version }} 12 | uses: actions/setup-node@v1 13 | with: 14 | node-version: ${{ matrix.node-version }} 15 | - name: npm install, build, and test 16 | run: | 17 | npm install 18 | npm run build --if-present 19 | npm test 20 | env: 21 | CI: true -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # nyc test coverage 21 | .nyc_output 22 | 23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 24 | .grunt 25 | 26 | # Bower dependency directory (https://bower.io/) 27 | bower_components 28 | 29 | # node-waf configuration 30 | .lock-wscript 31 | 32 | # Compiled binary addons (https://nodejs.org/api/addons.html) 33 | build/Release 34 | 35 | # Dependency directories 36 | node_modules/ 37 | jspm_packages/ 38 | 39 | # TypeScript v1 declaration files 40 | typings/ 41 | 42 | # Optional npm cache directory 43 | .npm 44 | 45 | # Optional eslint cache 46 | .eslintcache 47 | 48 | # Optional REPL history 49 | .node_repl_history 50 | 51 | # Output of 'npm pack' 52 | *.tgz 53 | 54 | # Yarn Integrity file 55 | .yarn-integrity 56 | 57 | # dotenv environment variables file 58 | .env 59 | 60 | # next.js build output 61 | .next 62 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Jakob Sjælland 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # simple-proxy-agent 2 | 3 | [![Build status](https://github.com/zjael/simple-proxy-agent/workflows/Node%20CI/badge.svg)](https://github.com/zjael/simple-proxy-agent/actions) 4 | [![Package version](https://img.shields.io/npm/v/simple-proxy-agent.svg)](https://npmjs.org/package/simple-proxy-agent) 5 | [![NPM downloads](https://img.shields.io/npm/dm/simple-proxy-agent)](https://npmjs.org/package/simple-proxy-agent) 6 | [![Make a pull request](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](http://makeapullrequest.com) 7 | [![License: MIT](https://img.shields.io/badge/License-MIT-brightgreen.svg)](https://opensource.org/licenses/MIT) 8 | 9 | > An simple agent for HTTP and HTTPS through HTTP and SOCKS proxies 10 | 11 | ## Table of Contents 12 | 13 | * [Install](#install) 14 | * [Usage](#usage) 15 | * [License](#license) 16 | 17 | ## Install 18 | 19 | ```shell script 20 | npm install simple-proxy-agent 21 | ``` 22 | 23 | ## Usage 24 | 25 | ### Proxy Request 26 | 27 | ```js 28 | const fetch = require('node-fetch'); 29 | const ProxyAgent = require('simple-proxy-agent'); 30 | 31 | fetch('https://example.com', { 32 | agent: new ProxyAgent('http://0.0.0.0:8080', { 33 | // Options, with all defaults 34 | tunnel: true, // If true, will tunnel all HTTPS using CONNECT method 35 | timeout: 5000, // Time in milli-seconds, to maximum wait for proxy connection to establish 36 | }) 37 | }) 38 | .then(res => res.text()) 39 | .then(body => console.log(body)) 40 | .catch(err => console.error(err)) 41 | ``` 42 | 43 | ### Basic Authentication 44 | 45 | ```js 46 | const fetch = require('node-fetch'); 47 | const ProxyAgent = require('simple-proxy-agent'); 48 | 49 | fetch('https://example.com', { 50 | agent: new ProxyAgent('http://user:password@0.0.0.0:8080') 51 | }) 52 | .then(res => res.text()) 53 | .then(body => console.log(body)) 54 | .catch(err => console.error(err)) 55 | ``` 56 | 57 | ## License 58 | 59 | MIT 60 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "simple-proxy-agent", 3 | "version": "1.1.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "@ungap/promise-all-settled": { 8 | "version": "1.1.2", 9 | "resolved": "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz", 10 | "integrity": "sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==", 11 | "dev": true 12 | }, 13 | "ansi-colors": { 14 | "version": "4.1.1", 15 | "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", 16 | "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", 17 | "dev": true 18 | }, 19 | "ansi-regex": { 20 | "version": "5.0.1", 21 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", 22 | "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", 23 | "dev": true 24 | }, 25 | "ansi-styles": { 26 | "version": "4.3.0", 27 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", 28 | "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", 29 | "dev": true, 30 | "requires": { 31 | "color-convert": "^2.0.1" 32 | } 33 | }, 34 | "anymatch": { 35 | "version": "3.1.2", 36 | "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", 37 | "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", 38 | "dev": true, 39 | "requires": { 40 | "normalize-path": "^3.0.0", 41 | "picomatch": "^2.0.4" 42 | } 43 | }, 44 | "argparse": { 45 | "version": "2.0.1", 46 | "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", 47 | "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", 48 | "dev": true 49 | }, 50 | "balanced-match": { 51 | "version": "1.0.2", 52 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", 53 | "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", 54 | "dev": true 55 | }, 56 | "binary-extensions": { 57 | "version": "2.2.0", 58 | "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", 59 | "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", 60 | "dev": true 61 | }, 62 | "brace-expansion": { 63 | "version": "1.1.11", 64 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 65 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 66 | "dev": true, 67 | "requires": { 68 | "balanced-match": "^1.0.0", 69 | "concat-map": "0.0.1" 70 | } 71 | }, 72 | "braces": { 73 | "version": "3.0.2", 74 | "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", 75 | "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", 76 | "dev": true, 77 | "requires": { 78 | "fill-range": "^7.0.1" 79 | } 80 | }, 81 | "browser-stdout": { 82 | "version": "1.3.1", 83 | "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", 84 | "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", 85 | "dev": true 86 | }, 87 | "camelcase": { 88 | "version": "6.3.0", 89 | "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", 90 | "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", 91 | "dev": true 92 | }, 93 | "chalk": { 94 | "version": "4.1.2", 95 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", 96 | "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", 97 | "dev": true, 98 | "requires": { 99 | "ansi-styles": "^4.1.0", 100 | "supports-color": "^7.1.0" 101 | }, 102 | "dependencies": { 103 | "supports-color": { 104 | "version": "7.2.0", 105 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", 106 | "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", 107 | "dev": true, 108 | "requires": { 109 | "has-flag": "^4.0.0" 110 | } 111 | } 112 | } 113 | }, 114 | "charenc": { 115 | "version": "0.0.2", 116 | "resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz", 117 | "integrity": "sha1-wKHS86cJLgN3S/qD8UwPxXkKhmc=", 118 | "dev": true 119 | }, 120 | "chokidar": { 121 | "version": "3.5.3", 122 | "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", 123 | "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", 124 | "dev": true, 125 | "requires": { 126 | "anymatch": "~3.1.2", 127 | "braces": "~3.0.2", 128 | "fsevents": "~2.3.2", 129 | "glob-parent": "~5.1.2", 130 | "is-binary-path": "~2.1.0", 131 | "is-glob": "~4.0.1", 132 | "normalize-path": "~3.0.0", 133 | "readdirp": "~3.6.0" 134 | } 135 | }, 136 | "cliui": { 137 | "version": "7.0.4", 138 | "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", 139 | "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", 140 | "dev": true, 141 | "requires": { 142 | "string-width": "^4.2.0", 143 | "strip-ansi": "^6.0.0", 144 | "wrap-ansi": "^7.0.0" 145 | } 146 | }, 147 | "color-convert": { 148 | "version": "2.0.1", 149 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", 150 | "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", 151 | "dev": true, 152 | "requires": { 153 | "color-name": "~1.1.4" 154 | } 155 | }, 156 | "color-name": { 157 | "version": "1.1.4", 158 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", 159 | "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", 160 | "dev": true 161 | }, 162 | "concat-map": { 163 | "version": "0.0.1", 164 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 165 | "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", 166 | "dev": true 167 | }, 168 | "crypt": { 169 | "version": "0.0.2", 170 | "resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz", 171 | "integrity": "sha1-iNf/fsDfuG9xPch7u0LQRNPmxBs=", 172 | "dev": true 173 | }, 174 | "debug": { 175 | "version": "4.3.3", 176 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", 177 | "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", 178 | "dev": true, 179 | "requires": { 180 | "ms": "2.1.2" 181 | }, 182 | "dependencies": { 183 | "ms": { 184 | "version": "2.1.2", 185 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", 186 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", 187 | "dev": true 188 | } 189 | } 190 | }, 191 | "decamelize": { 192 | "version": "4.0.0", 193 | "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", 194 | "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", 195 | "dev": true 196 | }, 197 | "diff": { 198 | "version": "5.0.0", 199 | "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", 200 | "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", 201 | "dev": true 202 | }, 203 | "emoji-regex": { 204 | "version": "8.0.0", 205 | "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", 206 | "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", 207 | "dev": true 208 | }, 209 | "es6-promisify": { 210 | "version": "6.1.1", 211 | "resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-6.1.1.tgz", 212 | "integrity": "sha512-HBL8I3mIki5C1Cc9QjKUenHtnG0A5/xA8Q/AllRcfiwl2CZFXGK7ddBiCoRwAix4i2KxcQfjtIVcrVbB3vbmwg==", 213 | "dev": true 214 | }, 215 | "escalade": { 216 | "version": "3.1.1", 217 | "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", 218 | "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", 219 | "dev": true 220 | }, 221 | "escape-string-regexp": { 222 | "version": "4.0.0", 223 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", 224 | "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", 225 | "dev": true 226 | }, 227 | "fill-range": { 228 | "version": "7.0.1", 229 | "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", 230 | "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", 231 | "dev": true, 232 | "requires": { 233 | "to-regex-range": "^5.0.1" 234 | } 235 | }, 236 | "find-up": { 237 | "version": "5.0.0", 238 | "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", 239 | "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", 240 | "dev": true, 241 | "requires": { 242 | "locate-path": "^6.0.0", 243 | "path-exists": "^4.0.0" 244 | } 245 | }, 246 | "flat": { 247 | "version": "5.0.2", 248 | "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", 249 | "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", 250 | "dev": true 251 | }, 252 | "fs.realpath": { 253 | "version": "1.0.0", 254 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", 255 | "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", 256 | "dev": true 257 | }, 258 | "fsevents": { 259 | "version": "2.3.2", 260 | "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", 261 | "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", 262 | "dev": true, 263 | "optional": true 264 | }, 265 | "get-caller-file": { 266 | "version": "2.0.5", 267 | "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", 268 | "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", 269 | "dev": true 270 | }, 271 | "glob": { 272 | "version": "7.2.0", 273 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", 274 | "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", 275 | "dev": true, 276 | "requires": { 277 | "fs.realpath": "^1.0.0", 278 | "inflight": "^1.0.4", 279 | "inherits": "2", 280 | "minimatch": "^3.0.4", 281 | "once": "^1.3.0", 282 | "path-is-absolute": "^1.0.0" 283 | } 284 | }, 285 | "glob-parent": { 286 | "version": "5.1.2", 287 | "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", 288 | "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", 289 | "dev": true, 290 | "requires": { 291 | "is-glob": "^4.0.1" 292 | } 293 | }, 294 | "growl": { 295 | "version": "1.10.5", 296 | "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", 297 | "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", 298 | "dev": true 299 | }, 300 | "has-flag": { 301 | "version": "4.0.0", 302 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", 303 | "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", 304 | "dev": true 305 | }, 306 | "he": { 307 | "version": "1.2.0", 308 | "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", 309 | "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", 310 | "dev": true 311 | }, 312 | "inflight": { 313 | "version": "1.0.6", 314 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", 315 | "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", 316 | "dev": true, 317 | "requires": { 318 | "once": "^1.3.0", 319 | "wrappy": "1" 320 | } 321 | }, 322 | "inherits": { 323 | "version": "2.0.4", 324 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 325 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", 326 | "dev": true 327 | }, 328 | "ip": { 329 | "version": "1.1.5", 330 | "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz", 331 | "integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=" 332 | }, 333 | "is-binary-path": { 334 | "version": "2.1.0", 335 | "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", 336 | "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", 337 | "dev": true, 338 | "requires": { 339 | "binary-extensions": "^2.0.0" 340 | } 341 | }, 342 | "is-buffer": { 343 | "version": "1.1.6", 344 | "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", 345 | "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", 346 | "dev": true 347 | }, 348 | "is-extglob": { 349 | "version": "2.1.1", 350 | "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", 351 | "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", 352 | "dev": true 353 | }, 354 | "is-fullwidth-code-point": { 355 | "version": "3.0.0", 356 | "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", 357 | "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", 358 | "dev": true 359 | }, 360 | "is-glob": { 361 | "version": "4.0.3", 362 | "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", 363 | "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", 364 | "dev": true, 365 | "requires": { 366 | "is-extglob": "^2.1.1" 367 | } 368 | }, 369 | "is-number": { 370 | "version": "7.0.0", 371 | "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", 372 | "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", 373 | "dev": true 374 | }, 375 | "is-plain-obj": { 376 | "version": "2.1.0", 377 | "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", 378 | "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", 379 | "dev": true 380 | }, 381 | "is-unicode-supported": { 382 | "version": "0.1.0", 383 | "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", 384 | "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", 385 | "dev": true 386 | }, 387 | "isexe": { 388 | "version": "2.0.0", 389 | "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", 390 | "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", 391 | "dev": true 392 | }, 393 | "js-yaml": { 394 | "version": "4.1.0", 395 | "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", 396 | "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", 397 | "dev": true, 398 | "requires": { 399 | "argparse": "^2.0.1" 400 | } 401 | }, 402 | "locate-path": { 403 | "version": "6.0.0", 404 | "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", 405 | "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", 406 | "dev": true, 407 | "requires": { 408 | "p-locate": "^5.0.0" 409 | } 410 | }, 411 | "log-symbols": { 412 | "version": "4.1.0", 413 | "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", 414 | "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", 415 | "dev": true, 416 | "requires": { 417 | "chalk": "^4.1.0", 418 | "is-unicode-supported": "^0.1.0" 419 | } 420 | }, 421 | "md5": { 422 | "version": "2.3.0", 423 | "resolved": "https://registry.npmjs.org/md5/-/md5-2.3.0.tgz", 424 | "integrity": "sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g==", 425 | "dev": true, 426 | "requires": { 427 | "charenc": "0.0.2", 428 | "crypt": "0.0.2", 429 | "is-buffer": "~1.1.6" 430 | } 431 | }, 432 | "minimatch": { 433 | "version": "3.0.4", 434 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", 435 | "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", 436 | "dev": true, 437 | "requires": { 438 | "brace-expansion": "^1.1.7" 439 | } 440 | }, 441 | "mocha": { 442 | "version": "9.2.0", 443 | "resolved": "https://registry.npmjs.org/mocha/-/mocha-9.2.0.tgz", 444 | "integrity": "sha512-kNn7E8g2SzVcq0a77dkphPsDSN7P+iYkqE0ZsGCYWRsoiKjOt+NvXfaagik8vuDa6W5Zw3qxe8Jfpt5qKf+6/Q==", 445 | "dev": true, 446 | "requires": { 447 | "@ungap/promise-all-settled": "1.1.2", 448 | "ansi-colors": "4.1.1", 449 | "browser-stdout": "1.3.1", 450 | "chokidar": "3.5.3", 451 | "debug": "4.3.3", 452 | "diff": "5.0.0", 453 | "escape-string-regexp": "4.0.0", 454 | "find-up": "5.0.0", 455 | "glob": "7.2.0", 456 | "growl": "1.10.5", 457 | "he": "1.2.0", 458 | "js-yaml": "4.1.0", 459 | "log-symbols": "4.1.0", 460 | "minimatch": "3.0.4", 461 | "ms": "2.1.3", 462 | "nanoid": "3.2.0", 463 | "serialize-javascript": "6.0.0", 464 | "strip-json-comments": "3.1.1", 465 | "supports-color": "8.1.1", 466 | "which": "2.0.2", 467 | "workerpool": "6.2.0", 468 | "yargs": "16.2.0", 469 | "yargs-parser": "20.2.4", 470 | "yargs-unparser": "2.0.0" 471 | } 472 | }, 473 | "ms": { 474 | "version": "2.1.3", 475 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", 476 | "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", 477 | "dev": true 478 | }, 479 | "nanoid": { 480 | "version": "3.2.0", 481 | "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.2.0.tgz", 482 | "integrity": "sha512-fmsZYa9lpn69Ad5eDn7FMcnnSR+8R34W9qJEijxYhTbfOWzr22n1QxCMzXLK+ODyW2973V3Fux959iQoUxzUIA==", 483 | "dev": true 484 | }, 485 | "node-fetch": { 486 | "version": "2.6.1", 487 | "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", 488 | "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==", 489 | "dev": true 490 | }, 491 | "normalize-path": { 492 | "version": "3.0.0", 493 | "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", 494 | "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", 495 | "dev": true 496 | }, 497 | "once": { 498 | "version": "1.4.0", 499 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 500 | "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", 501 | "dev": true, 502 | "requires": { 503 | "wrappy": "1" 504 | } 505 | }, 506 | "os-tmpdir": { 507 | "version": "1.0.2", 508 | "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", 509 | "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", 510 | "dev": true 511 | }, 512 | "p-limit": { 513 | "version": "3.1.0", 514 | "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", 515 | "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", 516 | "dev": true, 517 | "requires": { 518 | "yocto-queue": "^0.1.0" 519 | } 520 | }, 521 | "p-locate": { 522 | "version": "5.0.0", 523 | "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", 524 | "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", 525 | "dev": true, 526 | "requires": { 527 | "p-limit": "^3.0.2" 528 | } 529 | }, 530 | "path-exists": { 531 | "version": "4.0.0", 532 | "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", 533 | "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", 534 | "dev": true 535 | }, 536 | "path-is-absolute": { 537 | "version": "1.0.1", 538 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", 539 | "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", 540 | "dev": true 541 | }, 542 | "pem": { 543 | "version": "1.14.6", 544 | "resolved": "https://registry.npmjs.org/pem/-/pem-1.14.6.tgz", 545 | "integrity": "sha512-I5GKUer2PPv5qzUfxaZ6IGRkhp+357Kyv2t1JJg9vP8hGGI13qU34N2QupmggbpIZGPuudH0jn8KU5hjFpPk3g==", 546 | "dev": true, 547 | "requires": { 548 | "es6-promisify": "^6.0.0", 549 | "md5": "^2.2.1", 550 | "os-tmpdir": "^1.0.1", 551 | "which": "^2.0.2" 552 | } 553 | }, 554 | "picomatch": { 555 | "version": "2.3.1", 556 | "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", 557 | "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", 558 | "dev": true 559 | }, 560 | "randombytes": { 561 | "version": "2.1.0", 562 | "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", 563 | "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", 564 | "dev": true, 565 | "requires": { 566 | "safe-buffer": "^5.1.0" 567 | } 568 | }, 569 | "readdirp": { 570 | "version": "3.6.0", 571 | "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", 572 | "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", 573 | "dev": true, 574 | "requires": { 575 | "picomatch": "^2.2.1" 576 | } 577 | }, 578 | "require-directory": { 579 | "version": "2.1.1", 580 | "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", 581 | "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", 582 | "dev": true 583 | }, 584 | "safe-buffer": { 585 | "version": "5.2.1", 586 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", 587 | "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", 588 | "dev": true 589 | }, 590 | "serialize-javascript": { 591 | "version": "6.0.0", 592 | "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", 593 | "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", 594 | "dev": true, 595 | "requires": { 596 | "randombytes": "^2.1.0" 597 | } 598 | }, 599 | "smart-buffer": { 600 | "version": "4.2.0", 601 | "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", 602 | "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==" 603 | }, 604 | "socks": { 605 | "version": "2.6.2", 606 | "resolved": "https://registry.npmjs.org/socks/-/socks-2.6.2.tgz", 607 | "integrity": "sha512-zDZhHhZRY9PxRruRMR7kMhnf3I8hDs4S3f9RecfnGxvcBHQcKcIH/oUcEWffsfl1XxdYlA7nnlGbbTvPz9D8gA==", 608 | "requires": { 609 | "ip": "^1.1.5", 610 | "smart-buffer": "^4.2.0" 611 | } 612 | }, 613 | "string-width": { 614 | "version": "4.2.3", 615 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", 616 | "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", 617 | "dev": true, 618 | "requires": { 619 | "emoji-regex": "^8.0.0", 620 | "is-fullwidth-code-point": "^3.0.0", 621 | "strip-ansi": "^6.0.1" 622 | } 623 | }, 624 | "strip-ansi": { 625 | "version": "6.0.1", 626 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", 627 | "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", 628 | "dev": true, 629 | "requires": { 630 | "ansi-regex": "^5.0.1" 631 | } 632 | }, 633 | "strip-json-comments": { 634 | "version": "3.1.1", 635 | "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", 636 | "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", 637 | "dev": true 638 | }, 639 | "supports-color": { 640 | "version": "8.1.1", 641 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", 642 | "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", 643 | "dev": true, 644 | "requires": { 645 | "has-flag": "^4.0.0" 646 | } 647 | }, 648 | "to-regex-range": { 649 | "version": "5.0.1", 650 | "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", 651 | "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", 652 | "dev": true, 653 | "requires": { 654 | "is-number": "^7.0.0" 655 | } 656 | }, 657 | "which": { 658 | "version": "2.0.2", 659 | "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", 660 | "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", 661 | "dev": true, 662 | "requires": { 663 | "isexe": "^2.0.0" 664 | } 665 | }, 666 | "workerpool": { 667 | "version": "6.2.0", 668 | "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.0.tgz", 669 | "integrity": "sha512-Rsk5qQHJ9eowMH28Jwhe8HEbmdYDX4lwoMWshiCXugjtHqMD9ZbiqSDLxcsfdqsETPzVUtX5s1Z5kStiIM6l4A==", 670 | "dev": true 671 | }, 672 | "wrap-ansi": { 673 | "version": "7.0.0", 674 | "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", 675 | "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", 676 | "dev": true, 677 | "requires": { 678 | "ansi-styles": "^4.0.0", 679 | "string-width": "^4.1.0", 680 | "strip-ansi": "^6.0.0" 681 | } 682 | }, 683 | "wrappy": { 684 | "version": "1.0.2", 685 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 686 | "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", 687 | "dev": true 688 | }, 689 | "y18n": { 690 | "version": "5.0.8", 691 | "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", 692 | "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", 693 | "dev": true 694 | }, 695 | "yargs": { 696 | "version": "16.2.0", 697 | "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", 698 | "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", 699 | "dev": true, 700 | "requires": { 701 | "cliui": "^7.0.2", 702 | "escalade": "^3.1.1", 703 | "get-caller-file": "^2.0.5", 704 | "require-directory": "^2.1.1", 705 | "string-width": "^4.2.0", 706 | "y18n": "^5.0.5", 707 | "yargs-parser": "^20.2.2" 708 | } 709 | }, 710 | "yargs-parser": { 711 | "version": "20.2.4", 712 | "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", 713 | "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", 714 | "dev": true 715 | }, 716 | "yargs-unparser": { 717 | "version": "2.0.0", 718 | "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", 719 | "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", 720 | "dev": true, 721 | "requires": { 722 | "camelcase": "^6.0.0", 723 | "decamelize": "^4.0.0", 724 | "flat": "^5.0.2", 725 | "is-plain-obj": "^2.1.0" 726 | } 727 | }, 728 | "yocto-queue": { 729 | "version": "0.1.0", 730 | "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", 731 | "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", 732 | "dev": true 733 | } 734 | } 735 | } 736 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "simple-proxy-agent", 3 | "version": "1.1.0", 4 | "description": "An simple agent for HTTP and HTTPS through HTTP and SOCKS proxies", 5 | "main": "./src/agent.js", 6 | "engines": { 7 | "node": ">=10.0" 8 | }, 9 | "scripts": { 10 | "test": "mocha --timeout 10000 --exit" 11 | }, 12 | "repository": { 13 | "type": "git", 14 | "url": "git+https://github.com/zjael/simple-proxy-agent.git" 15 | }, 16 | "dependencies": { 17 | "socks": "^2.3.2" 18 | }, 19 | "devDependencies": { 20 | "mocha": "^9.0.2", 21 | "node-fetch": "^2.6.0", 22 | "pem": "^1.14.3" 23 | }, 24 | "keywords": [ 25 | "proxy", 26 | "agent", 27 | "http", 28 | "https", 29 | "socks", 30 | "socks4", 31 | "socks5" 32 | ], 33 | "author": "Jakob Sjælland", 34 | "license": "MIT", 35 | "bugs": { 36 | "url": "https://github.com/zjael/simple-proxy-agent/issues" 37 | }, 38 | "homepage": "https://github.com/zjael/simple-proxy-agent#readme" 39 | } 40 | -------------------------------------------------------------------------------- /src/agent.js: -------------------------------------------------------------------------------- 1 | const url = require('url'); 2 | const HTTP = require('./http.js'); 3 | const SOCKS = require('./socks.js'); 4 | 5 | function agent(proxy, options = {}) { 6 | if (!proxy) throw new Error('a proxy must be specified!'); 7 | 8 | options = Object.assign({ 9 | tunnel: true, 10 | timeout: 5000 11 | }, options) 12 | 13 | const { protocol } = url.parse(proxy); 14 | switch (protocol) { 15 | case 'http:': 16 | return new HTTP(proxy, options); 17 | case 'https:': 18 | return new HTTP(proxy, options); 19 | case 'socks:': 20 | case 'socks4:': 21 | case 'socks4a:': 22 | case 'socks5:': 23 | case 'socks5h:': 24 | return new SOCKS(proxy, options); 25 | default: 26 | throw new Error('Unsupported protocol: ' + protocol); 27 | } 28 | } 29 | 30 | module.exports = agent; -------------------------------------------------------------------------------- /src/http.js: -------------------------------------------------------------------------------- 1 | const http = require('http'); 2 | const url = require('url'); 3 | const tls = require('tls'); 4 | const net = require('net'); 5 | 6 | class HTTP { 7 | constructor(proxy, options) { 8 | this.proxy = proxy; 9 | this.options = options; 10 | this.init(); 11 | } 12 | 13 | init() { 14 | const proxy = url.parse(this.proxy); 15 | proxy.host = proxy.hostname || proxy.host; 16 | proxy.port = +proxy.port || (proxy.protocol.toLowerCase() === 'https:' ? 443 : 80); 17 | this.proxy = proxy; 18 | } 19 | } 20 | 21 | HTTP.prototype.addRequest = function(req, options) { 22 | if(!options.protocol) options = options.uri; 23 | const absolute = url.format({ 24 | protocol: options.protocol || 'http:', 25 | hostname: options.hostname || options.host, 26 | port: options.port, 27 | pathname: req.path 28 | }); 29 | req.path = decodeURIComponent(absolute); 30 | req.shouldKeepAlive = false; 31 | 32 | this.createConnection(options) 33 | .then(socket => { 34 | req.onSocket(socket); 35 | }) 36 | .catch(err => { 37 | req.emit('error', err); 38 | }) 39 | }; 40 | 41 | HTTP.prototype.createConnection = function(options) { 42 | return new Promise((resolve, reject) => { 43 | const ssl = options.protocol ? options.protocol.toLowerCase() === 'https:' : false; 44 | if(ssl && this.options.tunnel === true) { 45 | if(options.port === 80) options.port = 443; 46 | // CONNECT Method 47 | const req = http.request({ 48 | host: this.proxy.hostname, 49 | port: this.proxy.port, 50 | auth: this.proxy.auth, 51 | method: 'CONNECT', 52 | path: (options.hostname || options.host) + ":" + options.port, 53 | headers: { 54 | host: options.host 55 | }, 56 | timeout: this.options.timeout 57 | }); 58 | 59 | req.once('connect', (res, socket, head) => { 60 | const tunnel = tls.connect({ 61 | socket: socket, 62 | host: options.hostname || options.host, 63 | port: +options.port, 64 | servername: options.servername || options.host 65 | }); 66 | resolve(tunnel); 67 | }); 68 | 69 | req.once('timeout', () => { 70 | req.abort(); 71 | reject(new Error('HTTP CONNECT request timed out')) 72 | }) 73 | 74 | req.once('error', (err) => { 75 | reject(err); 76 | }); 77 | 78 | req.once('close', () => { 79 | reject(new Error('Tunnel failed. Socket closed prematurely')); 80 | }); 81 | 82 | req.end(); 83 | } else { 84 | const socket = net.connect({ 85 | host: this.proxy.host, 86 | port: this.proxy.port, 87 | auth: this.proxy.auth, 88 | }); 89 | resolve(socket); 90 | } 91 | }) 92 | }; 93 | 94 | module.exports = HTTP; -------------------------------------------------------------------------------- /src/socks.js: -------------------------------------------------------------------------------- 1 | const url = require('url'); 2 | const tls = require('tls'); 3 | const dns = require('dns'); 4 | const net = require('net'); 5 | const { SocksClient } = require('socks'); 6 | 7 | class SOCKS { 8 | constructor(proxy, options) { 9 | this.proxy = proxy; 10 | this.options = options; 11 | this.init(); 12 | } 13 | 14 | init() { 15 | const proxy = url.parse(this.proxy); 16 | proxy.host = proxy.hostname || proxy.host; 17 | proxy.port = +proxy.port || 1080; 18 | 19 | switch (proxy.protocol) { 20 | case 'socks4:': 21 | case 'socks4a:': 22 | proxy.type = 4; 23 | break; 24 | case 'socks:': 25 | case 'socks5:': 26 | case 'socks5h:': 27 | proxy.type = 5; 28 | break; 29 | } 30 | 31 | this.proxy = proxy; 32 | } 33 | } 34 | 35 | SOCKS.prototype.addRequest = function(req, options) { 36 | if(!options.protocol) options = options.uri; 37 | req.shouldKeepAlive = false; 38 | 39 | this.createConnection(options) 40 | .then(socket => { 41 | req.onSocket(socket); 42 | }) 43 | .catch(err => { 44 | req.emit('error', err); 45 | }) 46 | }; 47 | 48 | SOCKS.prototype.createConnection = async function(options) { 49 | try { 50 | let lookup = false; 51 | switch (this.proxy.protocol) { 52 | case 'socks4:': 53 | case 'socks5:': 54 | lookup = true; 55 | break; 56 | } 57 | 58 | let ip = options.hostname; 59 | if(lookup && !net.isIP(ip)) { 60 | ip = await new Promise((resolve, reject) => { 61 | dns.lookup(ip, (err, address) => { 62 | if(err) reject(err); 63 | resolve(address); 64 | }); 65 | }) 66 | } 67 | 68 | const ssl = options.protocol ? options.protocol.toLowerCase() === 'https:' : false; 69 | if(ssl && this.options.tunnel === true && options.port === 80) options.port = 443; 70 | 71 | const auth = this.proxy.auth && this.proxy.auth.split(':'); 72 | const { socket } = await SocksClient.createConnection({ 73 | proxy: { 74 | host: this.proxy.hostname || this.proxy.host, 75 | port: +this.proxy.port, 76 | type: this.proxy.type, 77 | ...(auth && { 78 | userId: auth[0], 79 | password: auth[1] 80 | }) 81 | }, 82 | command: 'connect', 83 | destination: { 84 | host: ip, 85 | port: +options.port 86 | }, 87 | timeout: this.options.timeout 88 | }) 89 | 90 | if(ssl && this.options.tunnel === true) { 91 | return tls.connect({ 92 | socket: socket, 93 | host: options.hostname || options.host, 94 | port: +options.port, 95 | servername: options.servername || options.host 96 | }); 97 | } 98 | 99 | return socket; 100 | } catch (err) { 101 | throw err; 102 | } 103 | }; 104 | module.exports = SOCKS; -------------------------------------------------------------------------------- /test/agent.test.js: -------------------------------------------------------------------------------- 1 | process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0"; 2 | 3 | const assert = require('assert'); 4 | const ProxyAgent = require('../src/agent.js'); 5 | const fetch = require('node-fetch'); 6 | const server = require('./server.js'); 7 | const httpProxy = require('./http-proxy.js'); 8 | const socksProxy = require('./socks-proxy'); 9 | const pem = require('pem'); 10 | 11 | const serverPorts = {}; 12 | const proxyPorts = {}; 13 | 14 | before(async () => { 15 | try { 16 | const options = await new Promise(resolve => { 17 | pem.createCertificate({ days: 1, selfSigned: true }, (err, keys) => { 18 | if(err) throw err; 19 | resolve({ 20 | key: keys.serviceKey, 21 | cert: keys.certificate 22 | }); 23 | }); 24 | }) 25 | 26 | await Promise.all([ 27 | new Promise(resolve => { 28 | server.createHTTP().then(port => { 29 | serverPorts.HTTP = port; 30 | resolve(); 31 | }) 32 | }), 33 | new Promise(resolve => { 34 | server.createHTTPS(options).then(port => { 35 | serverPorts.HTTPS = port; 36 | resolve(); 37 | }) 38 | }), 39 | new Promise(resolve => { 40 | httpProxy.create().then(port => { 41 | proxyPorts.HTTP = port; 42 | proxyPorts.HTTPS = port; 43 | resolve(); 44 | }) 45 | }), 46 | new Promise(resolve => { 47 | socksProxy.create().then(port => { 48 | proxyPorts.SOCKS = port; 49 | proxyPorts.SOCKS4 = port; 50 | proxyPorts.SOCKS5 = port; 51 | resolve(); 52 | }) 53 | }) 54 | ]) 55 | 56 | return; 57 | } catch (err) { 58 | throw err; 59 | } 60 | }); 61 | 62 | after(done => { 63 | server.closeAll(); 64 | httpProxy.close(); 65 | socksProxy.close(); 66 | done(); 67 | }) 68 | 69 | describe('ProxyAgent', () => { 70 | for(const protocol of ["HTTP", "HTTPS", "SOCKS4", "SOCKS5"]) { 71 | for(const server of ["HTTP", "HTTPS"]) { 72 | it(`should work over an ${protocol} proxy to an ${server} server without auth`, () => { 73 | const proxy = process.env[`${protocol}_PROXY`] || `${protocol.toLowerCase()}://127.0.0.1:` + proxyPorts[protocol]; 74 | const target = process.env.HTTP_TARGET_URL || `${server.toLowerCase()}://127.0.0.1:` + serverPorts[server]; 75 | const match = target.match(/:\/\/(.*)/)[1]; 76 | return fetch(target, { agent: new ProxyAgent(proxy) }) 77 | .then(res => res.json()) 78 | .then(json => { 79 | assert.strictEqual(match, json.host); 80 | }) 81 | .catch(err => { 82 | throw err; 83 | }).done; 84 | }); 85 | } 86 | 87 | for(const server of ["HTTP", "HTTPS"]) { 88 | it(`should work over an ${protocol} proxy to an ${server} server with auth`, () => { 89 | const proxy = process.env[`${protocol}_PROXY`] || `${protocol.toLowerCase()}://toto:tata@127.0.0.1:` + proxyPorts[protocol]; 90 | const target = process.env.HTTPS_TARGET_URL || `${server.toLowerCase()}://127.0.0.1:` + serverPorts[server]; 91 | const match = target.match(/:\/\/(.*)/)[1]; 92 | return fetch(target, { agent: new ProxyAgent(proxy) }) 93 | .then(res => res.json()) 94 | .then(json => { 95 | assert.strictEqual(match, json.host); 96 | }) 97 | .catch(err => { 98 | throw err; 99 | }).done; 100 | }); 101 | } 102 | } 103 | }); -------------------------------------------------------------------------------- /test/http-proxy.js: -------------------------------------------------------------------------------- 1 | const http = require('http'); 2 | const net = require('net'); 3 | const url = require('url'); 4 | 5 | const server = http.createServer((req, res) => { 6 | const options = { 7 | method: req.method, 8 | headers: req.headers 9 | }; 10 | 11 | let body = ''; 12 | req.on('data', (chunk) => { 13 | body += chunk.toString(); 14 | }); 15 | 16 | const data = http.request(req.url, options, (response) => { 17 | response.setEncoding('utf8'); 18 | res.writeHead(response.statusCode, response.headers); 19 | 20 | response.on('data', (chunk) => { 21 | res.write(chunk); 22 | }); 23 | response.on('close', () => { 24 | res.end(); 25 | }); 26 | response.on('end', () => { 27 | res.end(); 28 | }); 29 | }).on('error', (err) => { 30 | res.writeHead(500); 31 | res.end(); 32 | }); 33 | 34 | req.on('end', () => { 35 | data.write(body); 36 | data.end(); 37 | }) 38 | }); 39 | 40 | server.on('connect', (req, clientSocket, head) => { 41 | let parsed = url.parse(`http://${req.url}`); 42 | if (+parsed.port === 443) parsed = url.parse(`https://${req.url}`); 43 | //check authorization 44 | let authorizationOk = true 45 | const authorization = req.headers.authorization; 46 | if (authorization !== undefined) { 47 | authorizationOk = authorization === 'Basic ' + Buffer.from('toto:tata', 'utf-8').toString('base64'); 48 | } 49 | 50 | const serverSocket = net.connect(parsed.port, parsed.hostname, () => { 51 | if (authorizationOk) { 52 | clientSocket.write('HTTP/1.1 200 Connection Established\r\n' + 53 | 'Proxy-agent: NodeJS-Proxy\r\n' + 54 | '\r\n'); 55 | } else { 56 | clientSocket.write('HTTP/1.1 401 Unauthorized\r\n' + 57 | 'Proxy-agent: NodeJS-Proxy\r\n' + 58 | '\r\n'); 59 | throw new Error("unauthorized"); 60 | } 61 | serverSocket.write(head); 62 | serverSocket.pipe(clientSocket); 63 | clientSocket.pipe(serverSocket); 64 | }); 65 | 66 | const errorHandler = (err) => { 67 | if (serverSocket) serverSocket.end(); 68 | } 69 | 70 | clientSocket.on('error', errorHandler) 71 | clientSocket.on('end', errorHandler) 72 | serverSocket.on('error', errorHandler) 73 | serverSocket.on('end', errorHandler) 74 | }); 75 | 76 | function create() { 77 | return new Promise(resolve => { 78 | server.listen(() => { 79 | resolve(server.address().port); 80 | }) 81 | }) 82 | } 83 | 84 | function close() { 85 | server.close(); 86 | } 87 | 88 | module.exports = { 89 | create, 90 | close 91 | } -------------------------------------------------------------------------------- /test/server.js: -------------------------------------------------------------------------------- 1 | const http = require('http'); 2 | const https = require('https'); 3 | 4 | const servers = []; 5 | 6 | function createHTTP() { 7 | return new Promise(resolve => { 8 | const server = http.createServer(); 9 | server.on('request', (req, res) => { 10 | res.end(JSON.stringify(req.headers)); 11 | }); 12 | servers.push(server); 13 | server.listen(() => { 14 | resolve(server.address().port); 15 | }) 16 | }) 17 | } 18 | 19 | function createHTTPS(options) { 20 | return new Promise(resolve => { 21 | const server = https.createServer(options); 22 | server.on('request', (req, res) => { 23 | res.end(JSON.stringify(req.headers)); 24 | }); 25 | servers.push(server); 26 | server.listen(() => { 27 | resolve(server.address().port); 28 | }) 29 | }) 30 | } 31 | 32 | function closeAll() { 33 | for(const server of servers) { 34 | server.close(); 35 | } 36 | } 37 | 38 | module.exports = { 39 | createHTTP, 40 | createHTTPS, 41 | closeAll 42 | } -------------------------------------------------------------------------------- /test/socks-proxy.js: -------------------------------------------------------------------------------- 1 | // Credits to chbrown @ https://github.com/chbrown/socks-server 2 | 3 | const net = require('net'); 4 | 5 | function formatIPv4(buffer) { 6 | // buffer.length == 4 7 | return buffer[0] + '.' + buffer[1] + '.' + buffer[2] + '.' + buffer[3]; 8 | } 9 | 10 | function formatIPv6(buffer) { 11 | // buffer.length == 16 12 | var parts = []; 13 | for (var i = 0; i < 16; i += 2) { 14 | parts.push(buffer.readUInt16BE(i).toString(16)); 15 | } 16 | return parts.join(':'); 17 | } 18 | 19 | /** 20 | Returns an object with three properties designed to look like the address 21 | returned from socket.address(), e.g.: 22 | 23 | { family: 'IPv4', address: '127.0.0.1', port: 12346 } 24 | { family: 'IPv6', address: '1404:abf0:c984:ed7d:110e:ea59:69b6:4490', port: 8090 } 25 | { family: 'domain', address: '1404:abf0:c984:ed7d:110e:ea59:69b6:4490', port: 8090 } 26 | 27 | The given `type` should be either 1, 3, or 4, and the `buffer` should be 28 | formatted according to the SOCKS5 specification. 29 | */ 30 | function readAddress(type, buffer) { 31 | if (type == 1) { 32 | // IPv4 address 33 | return { 34 | family: 'IPv4', 35 | address: formatIPv4(buffer), 36 | port: buffer.readUInt16BE(4), 37 | }; 38 | } 39 | else if (type == 3) { 40 | // Domain name 41 | var length = buffer[0]; 42 | return { 43 | family: 'domain', 44 | address: buffer.slice(1, length + 1).toString(), 45 | port: buffer.readUInt16BE(length + 1), 46 | }; 47 | } 48 | else if (type == 4) { 49 | // IPv6 address 50 | return { 51 | family: 'IPv6', 52 | address: formatIPv6(buffer), 53 | port: buffer.readUInt16BE(16), 54 | }; 55 | } 56 | } 57 | 58 | const authMethod = { 59 | NO_AUTHENT: 0x00, 60 | USERNAME_PASSWORD: 0x02 61 | } 62 | 63 | const server = net.createServer((socket) => { 64 | socket.once('data', (greeting) => { 65 | // greeting = [socks_version, supported_authentication_methods, 66 | // ...supported_authentication_method_ids] 67 | const socks_version = greeting[0]; 68 | if (socks_version === 4) { 69 | let response = 0x5a; 70 | // username verification only 71 | if(greeting.length > 9) { 72 | const username = greeting.slice(8, greeting.length).toString(); 73 | response = username === 'toto' ? 0x5a : 0x5b; 74 | } 75 | const address = { 76 | port: greeting.slice(2, 4).readUInt16BE(0), 77 | address: formatIPv4(greeting.slice(4)), 78 | }; 79 | net.connect(address.port, address.address, function () { 80 | let socket = this; 81 | // the socks response must be made after the remote connection has been 82 | // established 83 | //socket.pipe(this).pipe(socket); 84 | socket.write(Buffer.from([0, response, 0, 0, 0, 0, 0, 0])); 85 | }); 86 | } 87 | else if (socks_version === 5) { 88 | const methods = greeting.slice(2, greeting.length); 89 | const authent = methods.length >= 2 90 | if (authent) { 91 | socket.write(Buffer.from([5, authMethod.USERNAME_PASSWORD])); 92 | socket.once('data', authentSocksV5); 93 | } else { 94 | socket.write(Buffer.from([5, authMethod.NO_AUTHENT])); 95 | socket.once('data', requestHandlerSocksV5); 96 | } 97 | } 98 | }) 99 | .on('error', function (err) { 100 | console.error('socket error: %s', err.message); 101 | }); 102 | }) 103 | .on('error', function (err) { 104 | console.error('server error: %j', err); 105 | }); 106 | 107 | const authentSocksV5 = function (userPass) { 108 | let socket = this; 109 | const username = userPass.slice(2, userPass[1] + 2).toString(); 110 | const password = userPass.slice(userPass[1] + 3).toString(); 111 | if (username === 'toto' && password === 'tata') { 112 | socket.write(Buffer.from([5, 0x00])); 113 | } else { 114 | socket.write(Buffer.from([5, 0x01])); 115 | } 116 | socket.once('data', requestHandlerSocksV5); 117 | }; 118 | 119 | const requestHandlerSocksV5 = function (connection) { 120 | let socket = this; 121 | const address_type = connection[3]; 122 | const address = readAddress(address_type, connection.slice(4)); 123 | net.connect(address.port, address.address, function () { 124 | socket.pipe(this).pipe(socket); 125 | const response = Buffer.from(connection); 126 | response[1] = 0; 127 | socket.write(response); 128 | }); 129 | } 130 | 131 | function create() { 132 | return new Promise(resolve => { 133 | server.listen(() => { 134 | resolve(server.address().port); 135 | }) 136 | }) 137 | } 138 | 139 | function close() { 140 | server.close(); 141 | } 142 | 143 | module.exports = { 144 | create, 145 | close 146 | } --------------------------------------------------------------------------------