├── .gitignore ├── .jscsrc ├── .jshintrc ├── .npmignore ├── .nvmrc ├── .travis.yml ├── LICENSE-MIT ├── README.md ├── app └── steps │ ├── buildProxyReq.js │ ├── copyProxyResHeadersToUserRes.js │ ├── decorateProxyReqBody.js │ ├── decorateProxyReqOpts.js │ ├── decorateUserRes.js │ ├── prepareProxyReq.js │ ├── resolveProxyHost.js │ ├── resolveProxyReqPath.js │ ├── sendProxyRequest.js │ └── sendUserRes.js ├── index.js ├── lib ├── as.js ├── chunkLength.js ├── isUnset.js ├── mockHTTP.js ├── requestOptions.js ├── resolveOptions.js └── scopeContainer.js ├── package-lock.json ├── package.json ├── test ├── agent.js ├── bodyEncoding.js ├── connectTimeout.js ├── cookies.js ├── decorateRequest.js ├── headers.js ├── host.js ├── https.js ├── integration │ └── proxyReqPathResolver.js ├── port.js ├── session.js ├── status.js ├── support │ └── proxyTarget.js ├── timeout.js ├── unit │ └── resolveProxyReqPath.js ├── urlParsing.js ├── userResDecorator.js └── verbs.js └── types.d.ts /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | *.log 4 | 5 | # no Webstorm info at all 6 | .idea/* 7 | 8 | # no VSCode info either 9 | .vscode/* -------------------------------------------------------------------------------- /.jscsrc: -------------------------------------------------------------------------------- 1 | { 2 | "preset": "node-style-guide", 3 | "requireCapitalizedComments": null, 4 | "maximumLineLength": 120, 5 | "requireTrailingComma": null, 6 | "disallowQuotedKeysInObjects":false, 7 | "requireSpaceAfterLineComment":false, 8 | "validateIndentation":false, 9 | "requireEarlyReturn": false 10 | } 11 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | // JSHint Default Configuration File (as on JSHint website) 3 | // See http://jshint.com/docs/ for more details 4 | 5 | "maxerr" : 50, // {int} Maximum error before stopping 6 | 7 | // Enforcing 8 | "bitwise" : true, // true: Prohibit bitwise operators (&, |, ^, etc.) 9 | "camelcase" : false, // true: Identifiers must be in camelCase 10 | "curly" : true, // true: Require {} for every new block or scope 11 | "eqeqeq" : true, // true: Require triple equals (===) for comparison 12 | "forin" : true, // true: Require filtering for..in loops with obj.hasOwnProperty() 13 | "freeze" : true, // true: prohibits overwriting prototypes of native objects such as Array, Date etc. 14 | "immed" : false, // true: Require immediate invocations to be wrapped in parens e.g. `(function () { } ());` 15 | "latedef" : false, // true: Require variables/functions to be defined before being used 16 | "newcap" : false, // true: Require capitalization of all constructor functions e.g. `new F()` 17 | "noarg" : true, // true: Prohibit use of `arguments.caller` and `arguments.callee` 18 | "noempty" : true, // true: Prohibit use of empty blocks 19 | "nonbsp" : true, // true: Prohibit "non-breaking whitespace" characters. 20 | "nonew" : false, // true: Prohibit use of constructors for side-effects (without assignment) 21 | "plusplus" : false, // true: Prohibit use of `++` and `--` 22 | "quotmark" : false, // Quotation mark consistency: 23 | // false : do nothing (default) 24 | // true : ensure whatever is used is consistent 25 | // "single" : require single quotes 26 | // "double" : require double quotes 27 | "undef" : true, // true: Require all non-global variables to be declared (prevents global leaks) 28 | "unused" : true, // Unused variables: 29 | // true : all variables, last function parameter 30 | // "vars" : all variables only 31 | // "strict" : all variables, all function parameters 32 | "strict" : true, // true: Requires all functions run in ES5 Strict Mode 33 | "maxparams" : false, // {int} Max number of formal params allowed per function 34 | "maxdepth" : false, // {int} Max depth of nested blocks (within functions) 35 | "maxstatements" : false, // {int} Max number statements per function 36 | "maxcomplexity" : false, // {int} Max cyclomatic complexity per function 37 | "maxlen" : false, // {int} Max number of characters per line 38 | "varstmt" : false, // true: Disallow any var statements. Only `let` and `const` are allowed. 39 | 40 | // Relaxing 41 | "asi" : false, // true: Tolerate Automatic Semicolon Insertion (no semicolons) 42 | "boss" : false, // true: Tolerate assignments where comparisons would be expected 43 | "debug" : false, // true: Allow debugger statements e.g. browser breakpoints. 44 | "eqnull" : false, // true: Tolerate use of `== null` 45 | "esversion" : 9, // {int} Specify the ECMAScript version to which the code must adhere. 46 | "moz" : false, // true: Allow Mozilla specific syntax (extends and overrides esnext features) 47 | // (ex: `for each`, multiple try/catch, function expression…) 48 | "evil" : false, // true: Tolerate use of `eval` and `new Function()` 49 | "expr" : false, // true: Tolerate `ExpressionStatement` as Programs 50 | "funcscope" : false, // true: Tolerate defining variables inside control statements 51 | "globalstrict" : false, // true: Allow global "use strict" (also enables 'strict') 52 | "iterator" : false, // true: Tolerate using the `__iterator__` property 53 | "lastsemic" : false, // true: Tolerate omitting a semicolon for the last statement of a 1-line block 54 | "laxbreak" : false, // true: Tolerate possibly unsafe line breakings 55 | "laxcomma" : false, // true: Tolerate comma-first style coding 56 | "loopfunc" : false, // true: Tolerate functions being defined in loops 57 | "multistr" : false, // true: Tolerate multi-line strings 58 | "noyield" : false, // true: Tolerate generator functions with no yield statement in them. 59 | "notypeof" : false, // true: Tolerate invalid typeof operator values 60 | "proto" : false, // true: Tolerate using the `__proto__` property 61 | "scripturl" : false, // true: Tolerate script-targeted URLs 62 | "shadow" : false, // true: Allows re-define variables later in code e.g. `var x=1; x=2;` 63 | "sub" : false, // true: Tolerate using `[]` notation when it can still be expressed in dot notation 64 | "supernew" : false, // true: Tolerate `new function () { ... };` and `new Object;` 65 | "validthis" : false, // true: Tolerate using this in a non-constructor function 66 | 67 | // Environments 68 | "browser" : true, // Web Browser (window, document, etc) 69 | "browserify" : false, // Browserify (node.js code in the browser) 70 | "couch" : false, // CouchDB 71 | "devel" : true, // Development/debugging (alert, confirm, etc) 72 | "dojo" : false, // Dojo Toolkit 73 | "jasmine" : false, // Jasmine 74 | "jquery" : false, // jQuery 75 | "mocha" : true, // Mocha 76 | "mootools" : false, // MooTools 77 | "node" : true, // Node.js 78 | "nonstandard" : false, // Widely adopted globals (escape, unescape, etc) 79 | "phantom" : false, // PhantomJS 80 | "prototypejs" : false, // Prototype and Scriptaculous 81 | "qunit" : false, // QUnit 82 | "rhino" : false, // Rhino 83 | "shelljs" : false, // ShellJS 84 | "typed" : false, // Globals for typed array constructions 85 | "worker" : false, // Web Workers 86 | "wsh" : false, // Windows Scripting Host 87 | "yui" : false, // Yahoo User Interface 88 | 89 | // Custom Globals 90 | "globals" : { // additional predefined global variables 91 | "Promise" : false 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | # Numerous always-ignore extensions 2 | *.bak 3 | *.patch 4 | *.diff 5 | *.err 6 | *.orig 7 | *.log 8 | *.rej 9 | *.swo 10 | *.swp 11 | *.zip 12 | *.vi 13 | *~ 14 | *.sass-cache 15 | 16 | # OS or Editor folders 17 | .DS_Store 18 | ._* 19 | .cache 20 | .project 21 | .settings 22 | .tmproj 23 | *.esproj 24 | *.sublime-project 25 | *.sublime-workspace 26 | nbproject 27 | thumbs.db 28 | 29 | # Folders to ignore 30 | .hg 31 | .svn 32 | .CVS 33 | .idea 34 | node_modules 35 | old/ 36 | *-old/ 37 | *-notrack/ 38 | build/ 39 | combo/ 40 | reference/ 41 | jscoverage_lib/ 42 | temp/ 43 | tmp/ 44 | 45 | 46 | -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | v14.15.4 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "10.23.1" 4 | - "12.20.1" 5 | - "14.15.4" 6 | - "15.6.0" 7 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2017 nsimmons 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | 22 | 23 | express-http-proxy (Original work which this project derives from) 24 | 25 | Copyright (c) 2013 villadora , contributors 26 | http://kael.me/ 27 | 28 | Permission is hereby granted, free of charge, to any person obtaining 29 | a copy of this software and associated documentation files (the 30 | "Software"), to deal in the Software without restriction, including 31 | without limitation the rights to use, copy, modify, merge, publish, 32 | distribute, sublicense, and/or sell copies of the Software, and to 33 | permit persons to whom the Software is furnished to do so, subject to 34 | the following conditions: 35 | 36 | The above copyright notice and this permission notice shall be 37 | included in all copies or substantial portions of the Software. 38 | 39 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 40 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 41 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 42 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 43 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 44 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 45 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # koa-better-http-proxy [![Build Status](https://travis-ci.org/nsimmons/koa-better-http-proxy.svg?branch=master)](https://travis-ci.org/nsimmons/koa-better-http-proxy) [![Downloads](https://img.shields.io/npm/dm/koa-better-http-proxy.png?style=flat-square)](https://www.npmjs.com/package/koa-better-http-proxy) 2 | ![KoaJs Slack](https://img.shields.io/badge/Koa.Js-Slack%20Channel-Slack.svg?longCache=true&style=for-the-badge) 3 | 4 | Koa middleware to proxy request to another host and pass response back. Based on [express-http-proxy](https://github.com/villadora/express-http-proxy). 5 | 6 | ## Install 7 | 8 | ```bash 9 | $ npm install koa-better-http-proxy --save 10 | ``` 11 | 12 | ## Usage 13 | ```js 14 | proxy(host, options); 15 | ``` 16 | 17 | To proxy URLS to the host 'www.google.com': 18 | 19 | ```js 20 | var proxy = require('koa-better-http-proxy'); 21 | var Koa = require('koa'); 22 | 23 | var app = new Koa(); 24 | app.use(proxy('www.google.com')); 25 | ``` 26 | 27 | If you wish to proxy only specific paths, you can use a router middleware to accomplish this. See [Koa routing middlewares](https://github.com/koajs/koa/wiki#routing-and-mounting). 28 | 29 | ### Options 30 | 31 | #### agent 32 | 33 | Use a custom `http.Agent` for proxy requests. 34 | 35 | ```js 36 | var agent = new http.Agent(options); 37 | app.use(proxy('www.google.com', { 38 | agent: agent, 39 | })); 40 | ``` 41 | 42 | #### port 43 | 44 | The port to use for the proxied host. 45 | 46 | ```js 47 | app.use(proxy('www.google.com', { 48 | port: 443 49 | })); 50 | ``` 51 | 52 | #### headers 53 | 54 | Additional headers to send to the proxied host. 55 | 56 | ```js 57 | app.use(proxy('www.google.com', { 58 | headers: { 59 | 'X-Special-Header': 'true' 60 | } 61 | })); 62 | ``` 63 | 64 | 65 | #### strippedHeaders 66 | 67 | Headers to remove from proxy response. 68 | 69 | ```js 70 | app.use(proxy('www.google.com', { 71 | strippedHeaders: [ 72 | 'set-cookie' 73 | ] 74 | })); 75 | ``` 76 | 77 | #### preserveReqSession 78 | 79 | Pass the session along to the proxied request 80 | 81 | ```js 82 | app.use(proxy('www.google.com', { 83 | preserveReqSession: true 84 | })); 85 | ``` 86 | 87 | #### proxyReqPathResolver (supports Promises) 88 | 89 | Provide a proxyReqPathResolver function if you'd like to 90 | operate on the path before issuing the proxy request. Use a Promise for async 91 | operations. 92 | 93 | ```js 94 | app.use(proxy('localhost:12345', { 95 | proxyReqPathResolver: function(ctx) { 96 | return require('url').parse(ctx.url).path; 97 | } 98 | })); 99 | ``` 100 | 101 | Promise form 102 | 103 | ```js 104 | app.use(proxy('localhost:12345', { 105 | proxyReqPathResolver: function(ctx) { 106 | return new Promise(function (resolve, reject) { 107 | setTimeout(function () { // do asyncness 108 | resolve(fancyResults); 109 | }, 200); 110 | }); 111 | } 112 | })); 113 | ``` 114 | 115 | #### filter 116 | 117 | The ```filter``` option can be used to limit what requests are proxied. Return ```true``` to execute proxy. 118 | 119 | For example, if you only want to proxy get request: 120 | 121 | ```js 122 | app.use(proxy('www.google.com', { 123 | filter: function(ctx) { 124 | return ctx.method === 'GET'; 125 | } 126 | })); 127 | ``` 128 | 129 | #### userResDecorator (supports Promise) 130 | 131 | You can modify the proxy's response before sending it to the client. 132 | 133 | ##### exploiting references 134 | The intent is that this be used to modify the proxy response data only. 135 | 136 | Note: 137 | The other arguments (proxyRes, ctx) are passed by reference, so 138 | you *can* currently exploit this to modify either response's headers, for 139 | instance, but this is not a reliable interface. I expect to close this 140 | exploit in a future release, while providing an additional hook for mutating 141 | the userRes before sending. 142 | 143 | #### userResHeadersDecorator (supports Promise) 144 | 145 | You can modify the proxy's headers before sending it to the client. 146 | 147 | ##### gzip responses 148 | 149 | If your proxy response is gzipped, this program will automatically unzip 150 | it before passing to your function, then zip it back up before piping it to the 151 | user response. There is currently no way to short-circuit this behavior. 152 | 153 | ```js 154 | app.use(proxy('www.google.com', { 155 | userResDecorator: function(proxyRes, proxyResData, ctx) { 156 | data = JSON.parse(proxyResData.toString('utf8')); 157 | data.newProperty = 'exciting data'; 158 | return JSON.stringify(data); 159 | } 160 | })); 161 | ``` 162 | 163 | ```js 164 | app.use(proxy('httpbin.org', { 165 | userResDecorator: function(proxyRes, proxyResData) { 166 | return new Promise(function(resolve) { 167 | proxyResData.funkyMessage = 'oi io oo ii'; 168 | setTimeout(function() { 169 | resolve(proxyResData); 170 | }, 200); 171 | }); 172 | } 173 | })); 174 | ``` 175 | 176 | #### limit 177 | 178 | This sets the body size limit (default: `1mb`). If the body size is larger than the specified (or default) limit, 179 | a `413 Request Entity Too Large` error will be returned. See [bytes.js](https://www.npmjs.com/package/bytes) for 180 | a list of supported formats. 181 | 182 | ```js 183 | app.use(proxy('www.google.com', { 184 | limit: '5mb' 185 | })); 186 | ``` 187 | 188 | #### proxyReqOptDecorator (supports Promise form) 189 | 190 | You can mutate the request options before sending the proxyRequest. 191 | proxyReqOpt represents the options argument passed to the (http|https).request 192 | module. 193 | 194 | NOTE: req.path cannot be changed via this method; use ```proxyReqPathResolver``` instead. 195 | 196 | ```js 197 | app.use(proxy('www.google.com', { 198 | proxyReqOptDecorator: function(proxyReqOpts, ctx) { 199 | // you can update headers 200 | proxyReqOpts.headers['content-type'] = 'text/html'; 201 | // you can change the method 202 | proxyReqOpts.method = 'GET'; 203 | return proxyReqOpts; 204 | } 205 | })); 206 | ``` 207 | 208 | You can use a Promise for async style. 209 | 210 | ```js 211 | app.use(proxy('www.google.com', { 212 | proxyReqOptDecorator: function(proxyReqOpts, ctx) { 213 | return new Promise(function(resolve, reject) { 214 | proxyReqOpts.headers['content-type'] = 'text/html'; 215 | resolve(proxyReqOpts); 216 | }) 217 | } 218 | })); 219 | ``` 220 | 221 | #### proxyReqBodyDecorator (supports Promise form) 222 | 223 | You can mutate the body content before sending the proxyRequest. 224 | 225 | ```js 226 | app.use(proxy('www.google.com', { 227 | proxyReqBodyDecorator: function(bodyContent, ctx) { 228 | return bodyContent.split('').reverse().join(''); 229 | } 230 | })); 231 | ``` 232 | 233 | You can use a Promise for async style. 234 | 235 | ```js 236 | app.use(proxy('www.google.com', { 237 | proxyReqBodyDecorator: function(proxyReq, ctx) { 238 | return new Promise(function(resolve, reject) { 239 | http.get('http://dev/null', function (err, res) { 240 | if (err) { reject(err); } 241 | resolve(res); 242 | }); 243 | }) 244 | } 245 | })); 246 | ``` 247 | 248 | #### https 249 | 250 | Normally, your proxy request will be made on the same protocol as the original 251 | request. If you'd like to force the proxy request to be https, use this 252 | option. 253 | 254 | ```js 255 | app.use(proxy('www.google.com', { 256 | https: true 257 | })); 258 | ``` 259 | 260 | #### preserveHostHdr 261 | 262 | You can copy the host HTTP header to the proxied express server using the `preserveHostHdr` option. 263 | 264 | ```js 265 | app.use(proxy('www.google.com', { 266 | preserveHostHdr: true 267 | })); 268 | ``` 269 | 270 | #### parseReqBody 271 | 272 | The ```parseReqBody``` option allows you to control parsing the request body. 273 | For example, disabling body parsing is useful for large uploads where it would be inefficient 274 | to hold the data in memory. 275 | 276 | This defaults to true in order to preserve legacy behavior. 277 | 278 | When false, no action will be taken on the body and accordingly ```req.body``` will no longer be set. 279 | 280 | Note that setting this to false overrides ```reqAsBuffer``` and ```reqBodyEncoding``` below. 281 | 282 | ```js 283 | app.use(proxy('www.google.com', { 284 | parseReqBody: false 285 | })); 286 | ``` 287 | 288 | #### reqAsBuffer 289 | 290 | Note: this is an experimental feature. ymmv 291 | 292 | The ```reqAsBuffer``` option allows you to ensure the req body is encoded as a Node 293 | ```Buffer``` when sending a proxied request. Any value for this is truthy. 294 | 295 | This defaults to to false in order to preserve legacy behavior. Note that 296 | the value of ```reqBodyEnconding``` is used as the encoding when coercing strings 297 | (and stringified JSON) to Buffer. 298 | 299 | Ignored if ```parseReqBody``` is set to false. 300 | 301 | ```js 302 | app.use(proxy('www.google.com', { 303 | reqAsBuffer: true 304 | })); 305 | ``` 306 | 307 | #### reqBodyEncoding 308 | 309 | Encoding used to decode request body. Defaults to ```utf-8```. 310 | 311 | Use ```null``` to preserve as Buffer when proxied request body is a Buffer. (e.g image upload) 312 | Accept any values supported by [raw-body](https://www.npmjs.com/package/raw-body#readme). 313 | 314 | The same encoding is used in the userResDecorator method. 315 | 316 | Ignored if ```parseReqBody``` is set to false. 317 | 318 | ```js 319 | app.use(proxy('httpbin.org', { 320 | reqBodyEncoding: null 321 | })); 322 | ``` 323 | 324 | #### connectTimeout 325 | 326 | By default, node does not express a timeout on connections. 327 | Use connectTimeout option to impose a specific timeout on the inital connection. (`connect` for http requests and `secureConnect` for https) 328 | This is useful if there are dns, network issues, or if you are uncertain if the destination is reachable. 329 | Timed-out requests will respond with 504 status code and a X-Timeout-Reason header. 330 | 331 | ```js 332 | app.use(proxy('httpbin.org', { 333 | connectTimeout: 2000 // in milliseconds, two seconds 334 | })); 335 | ``` 336 | 337 | 338 | #### timeout 339 | 340 | By default, node does not express a timeout on connections. 341 | Use timeout option to impose a specific timeout. This includes the time taken to make the connection and can be used with or without `connectTimeout`. 342 | Timed-out requests will respond with 504 status code and a X-Timeout-Reason header. 343 | 344 | ```js 345 | app.use(proxy('httpbin.org', { 346 | timeout: 2000 // in milliseconds, two seconds 347 | })); 348 | ``` 349 | -------------------------------------------------------------------------------- /app/steps/buildProxyReq.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var requestOptions = require('../../lib/requestOptions'); 4 | 5 | function buildProxyReq(Container) { 6 | var ctx = Container.user.ctx; 7 | var options = Container.options; 8 | var host = Container.proxy.host; 9 | 10 | var parseBody = (!options.parseReqBody) ? Promise.resolve(null) : requestOptions.bodyContent(ctx, options); 11 | var createReqOptions = requestOptions.create(ctx, options, host); 12 | 13 | return Promise 14 | .all([parseBody, createReqOptions]) 15 | .then(function(responseArray) { 16 | Container.proxy.bodyContent = responseArray[0]; 17 | Container.proxy.reqBuilder = responseArray[1]; 18 | return Container; 19 | }); 20 | } 21 | 22 | module.exports = buildProxyReq; 23 | -------------------------------------------------------------------------------- /app/steps/copyProxyResHeadersToUserRes.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | function copyProxyResHeadersToUserRes(container) { 4 | return new Promise(function(resolve) { 5 | var ctx = container.user.ctx; 6 | var rsp = container.proxy.res; 7 | var strippedHeaders = container.options.strippedHeaders || []; 8 | var userResHeadersDecorator = container.options.userResHeadersDecorator || function(headers) { return headers; }; 9 | 10 | if (ctx.headerSent || ctx.status === 504) { 11 | return resolve(container); 12 | } 13 | ctx.status = rsp.statusCode; 14 | Promise.resolve(userResHeadersDecorator(rsp.headers)).then(function(decoratedHeaders) { 15 | Object.keys(decoratedHeaders) 16 | .filter(function(item) { return strippedHeaders.indexOf(item) < 0; }) 17 | .filter(function(item) { return item !== 'transfer-encoding'; }) 18 | .forEach(function(item) { 19 | ctx.set(item, decoratedHeaders[item]); 20 | }); 21 | resolve(container); 22 | }); 23 | }); 24 | } 25 | 26 | module.exports = copyProxyResHeadersToUserRes; 27 | -------------------------------------------------------------------------------- /app/steps/decorateProxyReqBody.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | function defaultDecorator(proxyReqOptBody/*, userReq */) { 4 | return proxyReqOptBody; 5 | } 6 | 7 | function decorateProxyReqBody(container) { 8 | var resolverFn = container.options.proxyReqBodyDecorator || defaultDecorator; 9 | 10 | return Promise 11 | .resolve(resolverFn(container.proxy.bodyContent, container.user.ctx)) 12 | .then(function(bodyContent) { 13 | container.proxy.bodyContent = bodyContent; 14 | return Promise.resolve(container); 15 | }); 16 | } 17 | 18 | module.exports = decorateProxyReqBody; 19 | -------------------------------------------------------------------------------- /app/steps/decorateProxyReqOpts.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | function defaultDecorator(proxyReqOptBuilder /*, userReq */) { 4 | return proxyReqOptBuilder; 5 | } 6 | 7 | function decorateProxyReqOpt(container) { 8 | var resolverFn = container.options.proxyReqOptDecorator || defaultDecorator; 9 | 10 | return Promise 11 | .resolve(resolverFn(container.proxy.reqBuilder, container.user.ctx)) 12 | .then(function(processedReqOpts) { 13 | delete processedReqOpts.params; 14 | container.proxy.reqBuilder = processedReqOpts; 15 | return Promise.resolve(container); 16 | }); 17 | } 18 | 19 | module.exports = decorateProxyReqOpt; 20 | -------------------------------------------------------------------------------- /app/steps/decorateUserRes.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var as = require('../../lib/as.js'); 4 | var zlib = require('zlib'); 5 | 6 | function isResGzipped(res) { 7 | return res.headers['content-encoding'] === 'gzip'; 8 | } 9 | 10 | function zipOrUnzip(method) { 11 | return function(rspData, res) { 12 | return (isResGzipped(res)) ? zlib[method](rspData) : rspData; 13 | }; 14 | } 15 | 16 | var maybeUnzipResponse = zipOrUnzip('gunzipSync'); 17 | var maybeZipResponse = zipOrUnzip('gzipSync'); 18 | 19 | function verifyBuffer(rspd, reject) { 20 | if (!Buffer.isBuffer(rspd)) { 21 | return reject(new Error('userResDecorator should return string or buffer as data')); 22 | } 23 | } 24 | 25 | function updateHeaders(ctx, rspdBefore, rspdAfter, reject) { 26 | if (!ctx.headerSent) { 27 | ctx.set('content-length', rspdAfter.length); 28 | } else if (rspdAfter.length !== rspdBefore.length) { 29 | var error = '"Content-Length" is already sent,' + 30 | 'the length of response data can not be changed'; 31 | return reject(new Error(error)); 32 | } 33 | } 34 | 35 | function decorateProxyResBody(container) { 36 | if (container.user.ctx.status === 504) { 37 | return Promise.resolve(container); 38 | } 39 | var resolverFn = container.options.userResDecorator; 40 | if (!resolverFn) { 41 | return Promise.resolve(container); 42 | } 43 | 44 | var proxyResData = maybeUnzipResponse(container.proxy.resData, container.proxy.res); 45 | var proxyRes = container.proxy.res; 46 | var ctx = container.user.ctx; 47 | 48 | return Promise 49 | .resolve(resolverFn(proxyRes, proxyResData, ctx)) 50 | .then(function(modifiedResData) { 51 | return new Promise(function(resolve, reject) { 52 | var rspd = as.buffer(modifiedResData, container.options); 53 | verifyBuffer(rspd, reject); 54 | updateHeaders(ctx, proxyResData, rspd, reject); 55 | container.proxy.resData = maybeZipResponse(rspd, container.proxy.res); 56 | resolve(container); 57 | }); 58 | }); 59 | } 60 | 61 | module.exports = decorateProxyResBody; 62 | -------------------------------------------------------------------------------- /app/steps/prepareProxyReq.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var as = require('../../lib/as'); 4 | 5 | function getContentLength(body) { 6 | 7 | var result; 8 | if (Buffer.isBuffer(body)) { // Buffer 9 | result = body.length; 10 | } else if (typeof body === 'string') { 11 | result = Buffer.byteLength(body); 12 | } 13 | return result; 14 | } 15 | 16 | 17 | function prepareProxyReq(container) { 18 | return new Promise(function(resolve) { 19 | var bodyContent = container.proxy.bodyContent; 20 | var reqOpt = container.proxy.reqBuilder; 21 | 22 | if (bodyContent) { 23 | bodyContent = container.options.reqAsBuffer ? 24 | as.buffer(bodyContent, container.options) : 25 | as.bufferOrString(bodyContent); 26 | 27 | reqOpt.headers['content-length'] = getContentLength(bodyContent); 28 | 29 | if (container.options.reqBodyEncoding) { 30 | reqOpt.headers['accept-charset'] = container.options.reqBodyEncoding; 31 | } 32 | } 33 | 34 | container.proxy.bodyContent = bodyContent; 35 | resolve(container); 36 | }); 37 | } 38 | 39 | module.exports = prepareProxyReq; 40 | 41 | -------------------------------------------------------------------------------- /app/steps/resolveProxyHost.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var requestOptions = require('../../lib/requestOptions'); 3 | 4 | function resolveProxyHost(container) { 5 | var parsedHost = requestOptions.parseHost(container); 6 | 7 | container.proxy.reqBuilder.host = parsedHost.host; 8 | container.proxy.reqBuilder.port = container.options.port || parsedHost.port; 9 | container.proxy.requestModule = parsedHost.module; 10 | return Promise.resolve(container); 11 | } 12 | 13 | module.exports = resolveProxyHost; 14 | -------------------------------------------------------------------------------- /app/steps/resolveProxyReqPath.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var url = require('url'); 4 | 5 | function defaultProxyReqPathResolver(ctx) { 6 | return url.parse(ctx.url).path; 7 | } 8 | 9 | function resolveProxyReqPath(container) { 10 | var resolverFn = container.options.proxyReqPathResolver || defaultProxyReqPathResolver; 11 | 12 | return Promise 13 | .resolve(resolverFn(container.user.ctx)) 14 | .then(function(resolvedPath) { 15 | container.proxy.reqBuilder.path = resolvedPath; 16 | return Promise.resolve(container); 17 | }); 18 | } 19 | 20 | module.exports = resolveProxyReqPath; 21 | -------------------------------------------------------------------------------- /app/steps/sendProxyRequest.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var chunkLength = require('../../lib/chunkLength'); 4 | 5 | function sendProxyRequest(Container) { 6 | var ctx = Container.user.ctx; 7 | var bodyContent = Container.proxy.bodyContent; 8 | var reqOpt = Container.proxy.reqBuilder; 9 | var options = Container.options; 10 | 11 | return new Promise(function(resolve, reject) { 12 | var protocol = Container.proxy.requestModule; 13 | var proxyReq = protocol.request(reqOpt, function(rsp) { 14 | var chunks = []; 15 | rsp.on('data', function(chunk) { chunks.push(chunk); }); 16 | rsp.on('end', function() { 17 | Container.proxy.res = rsp; 18 | Container.proxy.resData = Buffer.concat(chunks, chunkLength(chunks)); 19 | resolve(Container); 20 | }); 21 | rsp.on('error', reject); 22 | }); 23 | var abortRequest = function() { 24 | proxyReq.abort(); 25 | }; 26 | var timeoutDuration = null; 27 | 28 | proxyReq.on('socket', function(socket) { 29 | var isSecure = Container.proxy.isSecure; 30 | var timeout = options.timeout; 31 | var connectTimeout = options.connectTimeout; 32 | // "secureConnect" includes time taken for "lookup" (dns), "connect" and "ready" events, as well as tls handshake. 33 | var eventListener = isSecure ? 'secureConnect' : 'connect'; 34 | 35 | if (connectTimeout) { 36 | timeoutDuration = connectTimeout; 37 | socket.setTimeout(connectTimeout, abortRequest); 38 | 39 | socket.on(eventListener, function() { 40 | if (timeout) { 41 | timeoutDuration = timeout; 42 | socket.setTimeout(timeout, abortRequest); 43 | } else { 44 | // 0 to reset to the default of no timeout for the rest of the request 45 | socket.setTimeout(0); 46 | } 47 | }); 48 | } else if (timeout) { 49 | timeoutDuration = timeout; 50 | socket.setTimeout(timeout, abortRequest); 51 | } 52 | }); 53 | 54 | // TODO: do reject here and handle this later on 55 | proxyReq.on('error', function(err) { 56 | // reject(error); 57 | if (err.code === 'ECONNRESET') { 58 | ctx.set('X-Timout-Reason', 'koa-better-http-proxy timed out your request after ' + timeoutDuration + 'ms.'); 59 | ctx.set('Content-Type', 'text/plain'); 60 | ctx.status = 504; 61 | resolve(Container); 62 | } else { 63 | reject(err); 64 | } 65 | }); 66 | 67 | // this guy should go elsewhere, down the chain 68 | if (options.parseReqBody) { 69 | // We are parsing the body ourselves so we need to write the body content 70 | // and then manually end the request. 71 | 72 | //if (bodyContent instanceof Object) { 73 | //throw new Error 74 | //debugger; 75 | //bodyContent = JSON.stringify(bodyContent); 76 | //} 77 | 78 | if (bodyContent.length) { 79 | proxyReq.write(bodyContent); 80 | } 81 | proxyReq.end(); 82 | } else { 83 | // Pipe will call end when it has completely read from the request. 84 | ctx.req.pipe(proxyReq); 85 | } 86 | 87 | ctx.req.on('aborted', function() { 88 | // reject? 89 | proxyReq.abort(); 90 | }); 91 | }); 92 | } 93 | 94 | 95 | module.exports = sendProxyRequest; 96 | -------------------------------------------------------------------------------- /app/steps/sendUserRes.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | function sendUserRes(Container) { 4 | if (!Container.user.ctx.headerSent && Container.user.ctx.status !== 504) { 5 | Container.user.ctx.body = Container.proxy.resData; 6 | } 7 | return Promise.resolve(Container); 8 | } 9 | 10 | module.exports = sendUserRes; 11 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var ScopeContainer = require('./lib/scopeContainer'); 4 | var assert = require('assert'); 5 | 6 | var buildProxyReq = require('./app/steps/buildProxyReq'); 7 | var copyProxyResHeadersToUserRes = require('./app/steps/copyProxyResHeadersToUserRes'); 8 | var decorateProxyReqBody = require('./app/steps/decorateProxyReqBody'); 9 | var decorateProxyReqOpts = require('./app/steps/decorateProxyReqOpts'); 10 | var decorateUserRes = require('./app/steps/decorateUserRes'); 11 | var prepareProxyReq = require('./app/steps/prepareProxyReq'); 12 | var resolveProxyHost = require('./app/steps/resolveProxyHost'); 13 | var resolveProxyReqPath = require('./app/steps/resolveProxyReqPath'); 14 | var sendProxyRequest = require('./app/steps/sendProxyRequest'); 15 | var sendUserRes = require('./app/steps/sendUserRes'); 16 | 17 | module.exports = function proxy(host, userOptions) { 18 | assert(host, 'Host should not be empty'); 19 | return function(ctx, next) { 20 | var container = new ScopeContainer(ctx, host, userOptions); 21 | 22 | // Skip proxy if filter is falsey. Loose equality so filters can return 23 | // false, null, undefined, etc. 24 | if (!container.options.filter(ctx)) { 25 | return Promise.resolve(null).then(next); 26 | } 27 | 28 | return buildProxyReq(container) 29 | .then(resolveProxyHost) 30 | .then(decorateProxyReqOpts) 31 | .then(resolveProxyReqPath) 32 | .then(decorateProxyReqBody) 33 | .then(prepareProxyReq) 34 | .then(sendProxyRequest) 35 | .then(copyProxyResHeadersToUserRes) 36 | .then(decorateUserRes) 37 | .then(sendUserRes) 38 | .then(next); 39 | }; 40 | }; 41 | -------------------------------------------------------------------------------- /lib/as.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /* 4 | * Trivial convenience methods for parsing Buffers 5 | */ 6 | 7 | function asBuffer(body, options) { 8 | 9 | var ret; 10 | if (Buffer.isBuffer(body)) { 11 | ret = body; 12 | } else if (typeof body === 'object') { 13 | ret = Buffer.from(JSON.stringify(body), options.reqBodyEncoding); 14 | } else if (typeof body === 'string') { 15 | ret = Buffer.from(body, options.reqBodyEncodeing); 16 | } 17 | return ret; 18 | } 19 | 20 | function asBufferOrString(body) { 21 | 22 | var ret; 23 | if (Buffer.isBuffer(body)) { 24 | ret = body; 25 | } else if (typeof body === 'object') { 26 | ret = JSON.stringify(body); 27 | } else if (typeof body === 'string') { 28 | ret = body; 29 | } 30 | return ret; 31 | } 32 | 33 | module.exports = { 34 | buffer: asBuffer, 35 | bufferOrString: asBufferOrString 36 | }; 37 | -------------------------------------------------------------------------------- /lib/chunkLength.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | function chunkLength(chunks) { 4 | return chunks.reduce(function(len, buf) { 5 | return len + buf.length; 6 | }, 0); 7 | } 8 | 9 | module.exports = chunkLength; 10 | -------------------------------------------------------------------------------- /lib/isUnset.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function(val) { 4 | return (typeof val === 'undefined' || val === '' || val === null); 5 | }; 6 | -------------------------------------------------------------------------------- /lib/mockHTTP.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var app = require('express')(); 4 | 5 | app.use('/status/:status', function(req, res) { 6 | res.status(Number(req.params.status)); 7 | res.send(); 8 | }); 9 | 10 | module.exports = app; 11 | -------------------------------------------------------------------------------- /lib/requestOptions.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var http = require('http'); 3 | var https = require('https'); 4 | var url = require('url'); 5 | var getRawBody = require('raw-body'); 6 | var isUnset = require('./isUnset'); 7 | 8 | function extend(obj, source, skips) { 9 | if (!source) { 10 | return obj; 11 | } 12 | 13 | for (var prop in source) { 14 | if (!skips || skips.indexOf(prop) === -1) { 15 | obj[prop] = source[prop]; 16 | } 17 | } 18 | 19 | return obj; 20 | } 21 | 22 | function parseHost(Container) { 23 | var host = Container.params.host; 24 | var ctx = Container.user.ctx; 25 | var options = Container.options; 26 | host = (typeof host === 'function') ? host(ctx) : host.toString(); 27 | 28 | if (!host) { 29 | return new Error('Empty host parameter'); 30 | } 31 | 32 | if (!/http(s)?:\/\//.test(host)) { 33 | host = 'http://' + host; 34 | } 35 | 36 | var parsed = url.parse(host); 37 | 38 | if (!parsed.hostname) { 39 | return new Error('Unable to parse hostname, possibly missing protocol://?'); 40 | } 41 | 42 | var ishttps = options.https || parsed.protocol === 'https:'; 43 | 44 | return { 45 | host: parsed.hostname, 46 | port: parsed.port || (ishttps ? 443 : 80), 47 | isSecure: ishttps, 48 | module: ishttps ? https : http, 49 | }; 50 | } 51 | 52 | function reqHeaders(ctx, options) { 53 | var headers = options.headers || {}; 54 | 55 | var skipHdrs = [ 'connection', 'content-length' ]; 56 | if (!options.preserveHostHdr) { 57 | skipHdrs.push('host'); 58 | } 59 | return extend(headers, ctx.headers, skipHdrs); 60 | } 61 | 62 | function createRequestOptions(ctx, options) { 63 | // prepare proxyRequest 64 | var reqOpt = { 65 | headers: reqHeaders(ctx, options), 66 | method: ctx.method, 67 | path: ctx.path, 68 | // params: req.params, 69 | }; 70 | 71 | if (options.preserveReqSession) { 72 | reqOpt.session = ctx.session; 73 | } 74 | 75 | if (options.agent) { 76 | reqOpt.agent = options.agent; 77 | } 78 | 79 | return Promise.resolve(reqOpt); 80 | } 81 | 82 | // extract to bodyContent object 83 | function bodyContent(ctx, options) { 84 | var parseReqBody = isUnset(options.parseReqBody) ? true : options.parseReqBody; 85 | 86 | function maybeParseBody(ctx, limit) { 87 | if (ctx.request.body) { 88 | return Promise.resolve(ctx.request.body); 89 | } else { 90 | // Returns a promise if no callback specified and global Promise exists. 91 | return getRawBody(ctx.req, { 92 | length: ctx.headers['content-length'], 93 | limit: limit, 94 | }); 95 | } 96 | } 97 | 98 | if (parseReqBody) { 99 | return maybeParseBody(ctx, options.limit || '1mb'); 100 | } 101 | } 102 | 103 | 104 | module.exports = { 105 | create: createRequestOptions, 106 | bodyContent: bodyContent, 107 | parseHost: parseHost 108 | }; 109 | -------------------------------------------------------------------------------- /lib/resolveOptions.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var isUnset = require('../lib/isUnset'); 4 | 5 | function resolveBodyEncoding(reqBodyEncoding) { 6 | /* For reqBodyEncoding, these is a meaningful difference between null and 7 | * undefined. null should be passed forward as the value of reqBodyEncoding, 8 | * and undefined should result in utf-8. 9 | */ 10 | return reqBodyEncoding !== undefined ? reqBodyEncoding: 'utf-8'; 11 | } 12 | 13 | function resolveOptions(options) { 14 | // resolve user argument to program usable options 15 | options = options || {}; 16 | 17 | return { 18 | agent: options.agent, 19 | proxyReqPathResolver: options.proxyReqPathResolver, 20 | proxyReqOptDecorator: options.proxyReqOptDecorator, 21 | proxyReqBodyDecorator: options.proxyReqBodyDecorator, 22 | userResDecorator: options.userResDecorator, 23 | userResHeadersDecorator: options.userResHeadersDecorator, 24 | filter: options.filter || defaultFilter, 25 | // For backwards compatability, we default to legacy behavior for newly added settings. 26 | parseReqBody: isUnset(options.parseReqBody) ? true : options.parseReqBody, 27 | reqBodyEncoding: resolveBodyEncoding(options.reqBodyEncoding), 28 | headers: {...options.headers || {}}, 29 | strippedHeaders: options.strippedHeaders, 30 | preserveReqSession: options.preserveReqSession, 31 | https: options.https, 32 | port: options.port, 33 | reqAsBuffer: options.reqAsBuffer, 34 | connectTimeout: options.connectTimeout, 35 | timeout: options.timeout, 36 | limit: options.limit, 37 | }; 38 | } 39 | 40 | function defaultFilter() { 41 | // No-op version of filter. Allows everything! 42 | return true; 43 | } 44 | 45 | module.exports = resolveOptions; 46 | -------------------------------------------------------------------------------- /lib/scopeContainer.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var resolveOptions = require('../lib/resolveOptions'); 3 | 4 | // The Container object is passed down the chain of Promises, with each one 5 | // of them mutating and returning Container. The goal is that (eventually) 6 | // author using this library // could hook into/override any of these 7 | // workflow steps with a Promise or simple function. 8 | // Container for scoped arguments in a promise chain. Each promise recieves 9 | // this as its argument, and returns it. 10 | // 11 | // Do not expose the details of this to hooks/user functions. 12 | 13 | function Container(ctx, host, userOptions) { 14 | return { 15 | user: { 16 | ctx: ctx, 17 | }, 18 | proxy: { 19 | req: {}, 20 | res: {}, 21 | resData: undefined, // from proxy res 22 | bodyContent: undefined, // for proxy req 23 | reqBuilder: {}, // reqOpt, intended as first arg to http(s)?.request 24 | }, 25 | options: resolveOptions(userOptions), 26 | params: { 27 | host: host, 28 | userOptions: userOptions 29 | } 30 | }; 31 | } 32 | 33 | module.exports = Container; 34 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "koa-better-http-proxy", 3 | "version": "0.2.9", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "JSV": { 8 | "version": "4.0.2", 9 | "resolved": "https://registry.npmjs.org/JSV/-/JSV-4.0.2.tgz", 10 | "integrity": "sha1-0Hf2glVx+CEy+d/67Vh7QCn+/1c=", 11 | "dev": true 12 | }, 13 | "accepts": { 14 | "version": "1.3.7", 15 | "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", 16 | "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", 17 | "dev": true, 18 | "requires": { 19 | "mime-types": "~2.1.24", 20 | "negotiator": "0.6.2" 21 | } 22 | }, 23 | "ansi-regex": { 24 | "version": "2.1.1", 25 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", 26 | "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", 27 | "dev": true 28 | }, 29 | "ansi-styles": { 30 | "version": "2.2.1", 31 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", 32 | "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", 33 | "dev": true 34 | }, 35 | "any-promise": { 36 | "version": "1.3.0", 37 | "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", 38 | "integrity": "sha1-q8av7tzqUugJzcA3au0845Y10X8=", 39 | "dev": true 40 | }, 41 | "argparse": { 42 | "version": "1.0.10", 43 | "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", 44 | "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", 45 | "dev": true, 46 | "requires": { 47 | "sprintf-js": "~1.0.2" 48 | } 49 | }, 50 | "array-filter": { 51 | "version": "1.0.0", 52 | "resolved": "https://registry.npmjs.org/array-filter/-/array-filter-1.0.0.tgz", 53 | "integrity": "sha1-uveeYubvTCpMC4MSMtr/7CUfnYM=", 54 | "dev": true 55 | }, 56 | "array-flatten": { 57 | "version": "1.1.1", 58 | "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", 59 | "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=", 60 | "dev": true 61 | }, 62 | "assertion-error": { 63 | "version": "1.1.0", 64 | "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", 65 | "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", 66 | "dev": true 67 | }, 68 | "async": { 69 | "version": "0.2.10", 70 | "resolved": "https://registry.npmjs.org/async/-/async-0.2.10.tgz", 71 | "integrity": "sha1-trvgsGdLnXGXCMo43owjfLUmw9E=", 72 | "dev": true 73 | }, 74 | "available-typed-arrays": { 75 | "version": "1.0.2", 76 | "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.2.tgz", 77 | "integrity": "sha512-XWX3OX8Onv97LMk/ftVyBibpGwY5a8SmuxZPzeOxqmuEqUCOM9ZE+uIaD1VNJ5QnvU2UQusvmKbuM1FR8QWGfQ==", 78 | "dev": true, 79 | "requires": { 80 | "array-filter": "^1.0.0" 81 | } 82 | }, 83 | "babel-runtime": { 84 | "version": "6.26.0", 85 | "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", 86 | "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", 87 | "dev": true, 88 | "requires": { 89 | "core-js": "^2.4.0", 90 | "regenerator-runtime": "^0.11.0" 91 | } 92 | }, 93 | "babylon": { 94 | "version": "6.18.0", 95 | "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz", 96 | "integrity": "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==", 97 | "dev": true 98 | }, 99 | "balanced-match": { 100 | "version": "1.0.0", 101 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", 102 | "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", 103 | "dev": true 104 | }, 105 | "body-parser": { 106 | "version": "1.19.0", 107 | "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", 108 | "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", 109 | "dev": true, 110 | "requires": { 111 | "bytes": "3.1.0", 112 | "content-type": "~1.0.4", 113 | "debug": "2.6.9", 114 | "depd": "~1.1.2", 115 | "http-errors": "1.7.2", 116 | "iconv-lite": "0.4.24", 117 | "on-finished": "~2.3.0", 118 | "qs": "6.7.0", 119 | "raw-body": "2.4.0", 120 | "type-is": "~1.6.17" 121 | }, 122 | "dependencies": { 123 | "raw-body": { 124 | "version": "2.4.0", 125 | "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", 126 | "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", 127 | "dev": true, 128 | "requires": { 129 | "bytes": "3.1.0", 130 | "http-errors": "1.7.2", 131 | "iconv-lite": "0.4.24", 132 | "unpipe": "1.0.0" 133 | } 134 | } 135 | } 136 | }, 137 | "brace-expansion": { 138 | "version": "1.1.11", 139 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 140 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 141 | "dev": true, 142 | "requires": { 143 | "balanced-match": "^1.0.0", 144 | "concat-map": "0.0.1" 145 | } 146 | }, 147 | "bytes": { 148 | "version": "3.1.0", 149 | "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", 150 | "integrity": "sha1-9s95M6Ng4FiPqf3oVlHNx/gF0fY=" 151 | }, 152 | "cache-content-type": { 153 | "version": "1.0.1", 154 | "resolved": "https://registry.npmjs.org/cache-content-type/-/cache-content-type-1.0.1.tgz", 155 | "integrity": "sha512-IKufZ1o4Ut42YUrZSo8+qnMTrFuKkvyoLXUywKz9GJ5BrhOFGhLdkx9sG4KAnVvbY6kEcSFjLQul+DVmBm2bgA==", 156 | "dev": true, 157 | "requires": { 158 | "mime-types": "^2.1.18", 159 | "ylru": "^1.2.0" 160 | } 161 | }, 162 | "call-bind": { 163 | "version": "1.0.2", 164 | "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", 165 | "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", 166 | "dev": true, 167 | "requires": { 168 | "function-bind": "^1.1.1", 169 | "get-intrinsic": "^1.0.2" 170 | } 171 | }, 172 | "chai": { 173 | "version": "3.5.0", 174 | "resolved": "https://registry.npmjs.org/chai/-/chai-3.5.0.tgz", 175 | "integrity": "sha1-TQJjewZ/6Vi9v906QOxW/vc3Mkc=", 176 | "dev": true, 177 | "requires": { 178 | "assertion-error": "^1.0.1", 179 | "deep-eql": "^0.1.3", 180 | "type-detect": "^1.0.0" 181 | } 182 | }, 183 | "chalk": { 184 | "version": "1.1.3", 185 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", 186 | "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", 187 | "dev": true, 188 | "requires": { 189 | "ansi-styles": "^2.2.1", 190 | "escape-string-regexp": "^1.0.2", 191 | "has-ansi": "^2.0.0", 192 | "strip-ansi": "^3.0.0", 193 | "supports-color": "^2.0.0" 194 | } 195 | }, 196 | "cli": { 197 | "version": "1.0.1", 198 | "resolved": "https://registry.npmjs.org/cli/-/cli-1.0.1.tgz", 199 | "integrity": "sha1-IoF1NPJL+klQw01TLUjsvGIbjBQ=", 200 | "dev": true, 201 | "requires": { 202 | "exit": "0.1.2", 203 | "glob": "^7.1.1" 204 | }, 205 | "dependencies": { 206 | "glob": { 207 | "version": "7.1.6", 208 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", 209 | "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", 210 | "dev": true, 211 | "requires": { 212 | "fs.realpath": "^1.0.0", 213 | "inflight": "^1.0.4", 214 | "inherits": "2", 215 | "minimatch": "^3.0.4", 216 | "once": "^1.3.0", 217 | "path-is-absolute": "^1.0.0" 218 | } 219 | } 220 | } 221 | }, 222 | "cli-table": { 223 | "version": "0.3.4", 224 | "resolved": "https://registry.npmjs.org/cli-table/-/cli-table-0.3.4.tgz", 225 | "integrity": "sha512-1vinpnX/ZERcmE443i3SZTmU5DF0rPO9DrL4I2iVAllhxzCM9SzPlHnz19fsZB78htkKZvYBvj6SZ6vXnaxmTA==", 226 | "dev": true, 227 | "requires": { 228 | "chalk": "^2.4.1", 229 | "string-width": "^4.2.0" 230 | }, 231 | "dependencies": { 232 | "ansi-styles": { 233 | "version": "3.2.1", 234 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", 235 | "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", 236 | "dev": true, 237 | "requires": { 238 | "color-convert": "^1.9.0" 239 | } 240 | }, 241 | "chalk": { 242 | "version": "2.4.2", 243 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", 244 | "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", 245 | "dev": true, 246 | "requires": { 247 | "ansi-styles": "^3.2.1", 248 | "escape-string-regexp": "^1.0.5", 249 | "supports-color": "^5.3.0" 250 | } 251 | }, 252 | "supports-color": { 253 | "version": "5.5.0", 254 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", 255 | "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", 256 | "dev": true, 257 | "requires": { 258 | "has-flag": "^3.0.0" 259 | } 260 | } 261 | } 262 | }, 263 | "co": { 264 | "version": "4.6.0", 265 | "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", 266 | "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", 267 | "dev": true 268 | }, 269 | "color-convert": { 270 | "version": "1.9.3", 271 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", 272 | "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", 273 | "dev": true, 274 | "requires": { 275 | "color-name": "1.1.3" 276 | } 277 | }, 278 | "color-name": { 279 | "version": "1.1.3", 280 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", 281 | "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", 282 | "dev": true 283 | }, 284 | "combined-stream": { 285 | "version": "1.0.8", 286 | "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", 287 | "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", 288 | "dev": true, 289 | "requires": { 290 | "delayed-stream": "~1.0.0" 291 | } 292 | }, 293 | "commander": { 294 | "version": "2.9.0", 295 | "resolved": "https://registry.npmjs.org/commander/-/commander-2.9.0.tgz", 296 | "integrity": "sha1-nJkJQXbhIkDLItbFFGCYQA/g99Q=", 297 | "dev": true, 298 | "requires": { 299 | "graceful-readlink": ">= 1.0.0" 300 | } 301 | }, 302 | "comment-parser": { 303 | "version": "0.3.2", 304 | "resolved": "https://registry.npmjs.org/comment-parser/-/comment-parser-0.3.2.tgz", 305 | "integrity": "sha1-PAPwd2uGo239mgosl8YwfzMggv4=", 306 | "dev": true, 307 | "requires": { 308 | "readable-stream": "^2.0.4" 309 | }, 310 | "dependencies": { 311 | "isarray": { 312 | "version": "1.0.0", 313 | "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", 314 | "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", 315 | "dev": true 316 | }, 317 | "readable-stream": { 318 | "version": "2.3.7", 319 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", 320 | "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", 321 | "dev": true, 322 | "requires": { 323 | "core-util-is": "~1.0.0", 324 | "inherits": "~2.0.3", 325 | "isarray": "~1.0.0", 326 | "process-nextick-args": "~2.0.0", 327 | "safe-buffer": "~5.1.1", 328 | "string_decoder": "~1.1.1", 329 | "util-deprecate": "~1.0.1" 330 | } 331 | }, 332 | "string_decoder": { 333 | "version": "1.1.1", 334 | "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", 335 | "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", 336 | "dev": true, 337 | "requires": { 338 | "safe-buffer": "~5.1.0" 339 | } 340 | } 341 | } 342 | }, 343 | "component-emitter": { 344 | "version": "1.2.1", 345 | "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", 346 | "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=", 347 | "dev": true 348 | }, 349 | "concat-map": { 350 | "version": "0.0.1", 351 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 352 | "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", 353 | "dev": true 354 | }, 355 | "console-browserify": { 356 | "version": "1.1.0", 357 | "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.1.0.tgz", 358 | "integrity": "sha1-8CQcRXMKn8YyOyBtvzjtx0HQuxA=", 359 | "dev": true, 360 | "requires": { 361 | "date-now": "^0.1.4" 362 | } 363 | }, 364 | "content-disposition": { 365 | "version": "0.5.3", 366 | "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", 367 | "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", 368 | "dev": true, 369 | "requires": { 370 | "safe-buffer": "5.1.2" 371 | } 372 | }, 373 | "content-type": { 374 | "version": "1.0.4", 375 | "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", 376 | "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", 377 | "dev": true 378 | }, 379 | "cookie": { 380 | "version": "0.4.0", 381 | "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", 382 | "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==", 383 | "dev": true 384 | }, 385 | "cookie-parser": { 386 | "version": "1.4.5", 387 | "resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.5.tgz", 388 | "integrity": "sha512-f13bPUj/gG/5mDr+xLmSxxDsB9DQiTIfhJS/sqjrmfAWiAN+x2O4i/XguTL9yDZ+/IFDanJ+5x7hC4CXT9Tdzw==", 389 | "dev": true, 390 | "requires": { 391 | "cookie": "0.4.0", 392 | "cookie-signature": "1.0.6" 393 | } 394 | }, 395 | "cookie-signature": { 396 | "version": "1.0.6", 397 | "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", 398 | "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=", 399 | "dev": true 400 | }, 401 | "cookiejar": { 402 | "version": "2.0.6", 403 | "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.0.6.tgz", 404 | "integrity": "sha1-Cr81atANHFohnYjURRgEbdAmrP4=", 405 | "dev": true 406 | }, 407 | "cookies": { 408 | "version": "0.8.0", 409 | "resolved": "https://registry.npmjs.org/cookies/-/cookies-0.8.0.tgz", 410 | "integrity": "sha512-8aPsApQfebXnuI+537McwYsDtjVxGm8gTIzQI3FDW6t5t/DAhERxtnbEPN/8RX+uZthoz4eCOgloXaE5cYyNow==", 411 | "dev": true, 412 | "requires": { 413 | "depd": "~2.0.0", 414 | "keygrip": "~1.1.0" 415 | }, 416 | "dependencies": { 417 | "depd": { 418 | "version": "2.0.0", 419 | "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", 420 | "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", 421 | "dev": true 422 | } 423 | } 424 | }, 425 | "core-js": { 426 | "version": "2.6.12", 427 | "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.12.tgz", 428 | "integrity": "sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==", 429 | "dev": true 430 | }, 431 | "core-util-is": { 432 | "version": "1.0.2", 433 | "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", 434 | "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", 435 | "dev": true 436 | }, 437 | "cst": { 438 | "version": "0.4.10", 439 | "resolved": "https://registry.npmjs.org/cst/-/cst-0.4.10.tgz", 440 | "integrity": "sha512-U5ETe1IOjq2h56ZcBE3oe9rT7XryCH6IKgPMv0L7sSk6w29yR3p5egCK0T3BDNHHV95OoUBgXsqiVG+3a900Ag==", 441 | "dev": true, 442 | "requires": { 443 | "babel-runtime": "^6.9.2", 444 | "babylon": "^6.8.1", 445 | "source-map-support": "^0.4.0" 446 | } 447 | }, 448 | "cycle": { 449 | "version": "1.0.3", 450 | "resolved": "https://registry.npmjs.org/cycle/-/cycle-1.0.3.tgz", 451 | "integrity": "sha1-IegLK+hYD5i0aPN5QwZisEbDStI=", 452 | "dev": true 453 | }, 454 | "date-now": { 455 | "version": "0.1.4", 456 | "resolved": "https://registry.npmjs.org/date-now/-/date-now-0.1.4.tgz", 457 | "integrity": "sha1-6vQ5/U1ISK105cx9vvIAZyueNFs=", 458 | "dev": true 459 | }, 460 | "debug": { 461 | "version": "2.6.9", 462 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", 463 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", 464 | "dev": true, 465 | "requires": { 466 | "ms": "2.0.0" 467 | } 468 | }, 469 | "deep-eql": { 470 | "version": "0.1.3", 471 | "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-0.1.3.tgz", 472 | "integrity": "sha1-71WKyrjeJSBs1xOQbXTlaTDrafI=", 473 | "dev": true, 474 | "requires": { 475 | "type-detect": "0.1.1" 476 | }, 477 | "dependencies": { 478 | "type-detect": { 479 | "version": "0.1.1", 480 | "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-0.1.1.tgz", 481 | "integrity": "sha1-C6XsKohWQORw6k6FBZcZANrFiCI=", 482 | "dev": true 483 | } 484 | } 485 | }, 486 | "deep-equal": { 487 | "version": "2.0.5", 488 | "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-2.0.5.tgz", 489 | "integrity": "sha512-nPiRgmbAtm1a3JsnLCf6/SLfXcjyN5v8L1TXzdCmHrXJ4hx+gW/w1YCcn7z8gJtSiDArZCgYtbao3QqLm/N1Sw==", 490 | "dev": true, 491 | "requires": { 492 | "call-bind": "^1.0.0", 493 | "es-get-iterator": "^1.1.1", 494 | "get-intrinsic": "^1.0.1", 495 | "is-arguments": "^1.0.4", 496 | "is-date-object": "^1.0.2", 497 | "is-regex": "^1.1.1", 498 | "isarray": "^2.0.5", 499 | "object-is": "^1.1.4", 500 | "object-keys": "^1.1.1", 501 | "object.assign": "^4.1.2", 502 | "regexp.prototype.flags": "^1.3.0", 503 | "side-channel": "^1.0.3", 504 | "which-boxed-primitive": "^1.0.1", 505 | "which-collection": "^1.0.1", 506 | "which-typed-array": "^1.1.2" 507 | }, 508 | "dependencies": { 509 | "isarray": { 510 | "version": "2.0.5", 511 | "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", 512 | "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", 513 | "dev": true 514 | } 515 | } 516 | }, 517 | "define-properties": { 518 | "version": "1.1.3", 519 | "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", 520 | "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", 521 | "dev": true, 522 | "requires": { 523 | "object-keys": "^1.0.12" 524 | } 525 | }, 526 | "delayed-stream": { 527 | "version": "1.0.0", 528 | "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", 529 | "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", 530 | "dev": true 531 | }, 532 | "delegates": { 533 | "version": "1.0.0", 534 | "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", 535 | "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=", 536 | "dev": true 537 | }, 538 | "depd": { 539 | "version": "1.1.2", 540 | "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", 541 | "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" 542 | }, 543 | "destroy": { 544 | "version": "1.0.4", 545 | "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", 546 | "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=", 547 | "dev": true 548 | }, 549 | "diff": { 550 | "version": "1.4.0", 551 | "resolved": "https://registry.npmjs.org/diff/-/diff-1.4.0.tgz", 552 | "integrity": "sha1-fyjS657nsVqX79ic5j3P2qPMur8=", 553 | "dev": true 554 | }, 555 | "dom-serializer": { 556 | "version": "0.2.2", 557 | "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.2.2.tgz", 558 | "integrity": "sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g==", 559 | "dev": true, 560 | "requires": { 561 | "domelementtype": "^2.0.1", 562 | "entities": "^2.0.0" 563 | }, 564 | "dependencies": { 565 | "domelementtype": { 566 | "version": "2.1.0", 567 | "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.1.0.tgz", 568 | "integrity": "sha512-LsTgx/L5VpD+Q8lmsXSHW2WpA+eBlZ9HPf3erD1IoPF00/3JKHZ3BknUVA2QGDNu69ZNmyFmCWBSO45XjYKC5w==", 569 | "dev": true 570 | }, 571 | "entities": { 572 | "version": "2.1.0", 573 | "resolved": "https://registry.npmjs.org/entities/-/entities-2.1.0.tgz", 574 | "integrity": "sha512-hCx1oky9PFrJ611mf0ifBLBRW8lUUVRlFolb5gWRfIELabBlbp9xZvrqZLZAs+NxFnbfQoeGd8wDkygjg7U85w==", 575 | "dev": true 576 | } 577 | } 578 | }, 579 | "domelementtype": { 580 | "version": "1.3.1", 581 | "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz", 582 | "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==", 583 | "dev": true 584 | }, 585 | "domhandler": { 586 | "version": "2.3.0", 587 | "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.3.0.tgz", 588 | "integrity": "sha1-LeWaCCLVAn+r/28DLCsloqir5zg=", 589 | "dev": true, 590 | "requires": { 591 | "domelementtype": "1" 592 | } 593 | }, 594 | "domutils": { 595 | "version": "1.5.1", 596 | "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz", 597 | "integrity": "sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8=", 598 | "dev": true, 599 | "requires": { 600 | "dom-serializer": "0", 601 | "domelementtype": "1" 602 | } 603 | }, 604 | "ee-first": { 605 | "version": "1.1.1", 606 | "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", 607 | "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=", 608 | "dev": true 609 | }, 610 | "emoji-regex": { 611 | "version": "8.0.0", 612 | "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", 613 | "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", 614 | "dev": true 615 | }, 616 | "encodeurl": { 617 | "version": "1.0.2", 618 | "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", 619 | "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=", 620 | "dev": true 621 | }, 622 | "entities": { 623 | "version": "1.0.0", 624 | "resolved": "https://registry.npmjs.org/entities/-/entities-1.0.0.tgz", 625 | "integrity": "sha1-sph6o4ITR/zeZCsk/fyeT7cSvyY=", 626 | "dev": true 627 | }, 628 | "es-abstract": { 629 | "version": "1.18.0-next.2", 630 | "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0-next.2.tgz", 631 | "integrity": "sha512-Ih4ZMFHEtZupnUh6497zEL4y2+w8+1ljnCyaTa+adcoafI1GOvMwFlDjBLfWR7y9VLfrjRJe9ocuHY1PSR9jjw==", 632 | "dev": true, 633 | "requires": { 634 | "call-bind": "^1.0.2", 635 | "es-to-primitive": "^1.2.1", 636 | "function-bind": "^1.1.1", 637 | "get-intrinsic": "^1.0.2", 638 | "has": "^1.0.3", 639 | "has-symbols": "^1.0.1", 640 | "is-callable": "^1.2.2", 641 | "is-negative-zero": "^2.0.1", 642 | "is-regex": "^1.1.1", 643 | "object-inspect": "^1.9.0", 644 | "object-keys": "^1.1.1", 645 | "object.assign": "^4.1.2", 646 | "string.prototype.trimend": "^1.0.3", 647 | "string.prototype.trimstart": "^1.0.3" 648 | } 649 | }, 650 | "es-get-iterator": { 651 | "version": "1.1.1", 652 | "resolved": "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.1.tgz", 653 | "integrity": "sha512-qorBw8Y7B15DVLaJWy6WdEV/ZkieBcu6QCq/xzWzGOKJqgG1j754vXRfZ3NY7HSShneqU43mPB4OkQBTkvHhFw==", 654 | "dev": true, 655 | "requires": { 656 | "call-bind": "^1.0.0", 657 | "get-intrinsic": "^1.0.1", 658 | "has-symbols": "^1.0.1", 659 | "is-arguments": "^1.0.4", 660 | "is-map": "^2.0.1", 661 | "is-set": "^2.0.1", 662 | "is-string": "^1.0.5", 663 | "isarray": "^2.0.5" 664 | }, 665 | "dependencies": { 666 | "isarray": { 667 | "version": "2.0.5", 668 | "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", 669 | "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", 670 | "dev": true 671 | } 672 | } 673 | }, 674 | "es-to-primitive": { 675 | "version": "1.2.1", 676 | "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", 677 | "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", 678 | "dev": true, 679 | "requires": { 680 | "is-callable": "^1.1.4", 681 | "is-date-object": "^1.0.1", 682 | "is-symbol": "^1.0.2" 683 | } 684 | }, 685 | "es6-promise": { 686 | "version": "3.3.1", 687 | "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.3.1.tgz", 688 | "integrity": "sha1-oIzd6EzNvzTQJ6FFG8kdS80ophM=" 689 | }, 690 | "escape-html": { 691 | "version": "1.0.3", 692 | "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", 693 | "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=", 694 | "dev": true 695 | }, 696 | "escape-string-regexp": { 697 | "version": "1.0.5", 698 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", 699 | "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", 700 | "dev": true 701 | }, 702 | "esprima": { 703 | "version": "2.7.3", 704 | "resolved": "https://registry.npmjs.org/esprima/-/esprima-2.7.3.tgz", 705 | "integrity": "sha1-luO3DVd59q1JzQMmc9HDEnZ7pYE=", 706 | "dev": true 707 | }, 708 | "estraverse": { 709 | "version": "4.3.0", 710 | "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", 711 | "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", 712 | "dev": true 713 | }, 714 | "etag": { 715 | "version": "1.8.1", 716 | "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", 717 | "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=", 718 | "dev": true 719 | }, 720 | "exit": { 721 | "version": "0.1.2", 722 | "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", 723 | "integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=", 724 | "dev": true 725 | }, 726 | "express": { 727 | "version": "4.17.1", 728 | "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", 729 | "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", 730 | "dev": true, 731 | "requires": { 732 | "accepts": "~1.3.7", 733 | "array-flatten": "1.1.1", 734 | "body-parser": "1.19.0", 735 | "content-disposition": "0.5.3", 736 | "content-type": "~1.0.4", 737 | "cookie": "0.4.0", 738 | "cookie-signature": "1.0.6", 739 | "debug": "2.6.9", 740 | "depd": "~1.1.2", 741 | "encodeurl": "~1.0.2", 742 | "escape-html": "~1.0.3", 743 | "etag": "~1.8.1", 744 | "finalhandler": "~1.1.2", 745 | "fresh": "0.5.2", 746 | "merge-descriptors": "1.0.1", 747 | "methods": "~1.1.2", 748 | "on-finished": "~2.3.0", 749 | "parseurl": "~1.3.3", 750 | "path-to-regexp": "0.1.7", 751 | "proxy-addr": "~2.0.5", 752 | "qs": "6.7.0", 753 | "range-parser": "~1.2.1", 754 | "safe-buffer": "5.1.2", 755 | "send": "0.17.1", 756 | "serve-static": "1.14.1", 757 | "setprototypeof": "1.1.1", 758 | "statuses": "~1.5.0", 759 | "type-is": "~1.6.18", 760 | "utils-merge": "1.0.1", 761 | "vary": "~1.1.2" 762 | } 763 | }, 764 | "extend": { 765 | "version": "3.0.0", 766 | "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.0.tgz", 767 | "integrity": "sha1-WkdDU7nzNT3dgXbf03uRyDpG8dQ=", 768 | "dev": true 769 | }, 770 | "eyes": { 771 | "version": "0.1.8", 772 | "resolved": "https://registry.npmjs.org/eyes/-/eyes-0.1.8.tgz", 773 | "integrity": "sha1-Ys8SAjTGg3hdkCNIqADvPgzCC8A=", 774 | "dev": true 775 | }, 776 | "finalhandler": { 777 | "version": "1.1.2", 778 | "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", 779 | "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", 780 | "dev": true, 781 | "requires": { 782 | "debug": "2.6.9", 783 | "encodeurl": "~1.0.2", 784 | "escape-html": "~1.0.3", 785 | "on-finished": "~2.3.0", 786 | "parseurl": "~1.3.3", 787 | "statuses": "~1.5.0", 788 | "unpipe": "~1.0.0" 789 | } 790 | }, 791 | "foreach": { 792 | "version": "2.0.5", 793 | "resolved": "https://registry.npmjs.org/foreach/-/foreach-2.0.5.tgz", 794 | "integrity": "sha1-C+4AUBiusmDQo6865ljdATbsG5k=", 795 | "dev": true 796 | }, 797 | "form-data": { 798 | "version": "1.0.0-rc3", 799 | "resolved": "https://registry.npmjs.org/form-data/-/form-data-1.0.0-rc3.tgz", 800 | "integrity": "sha1-01vGLn+8KTeuePlIqqDTjZBgdXc=", 801 | "dev": true, 802 | "requires": { 803 | "async": "^1.4.0", 804 | "combined-stream": "^1.0.5", 805 | "mime-types": "^2.1.3" 806 | }, 807 | "dependencies": { 808 | "async": { 809 | "version": "1.5.2", 810 | "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", 811 | "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=", 812 | "dev": true 813 | } 814 | } 815 | }, 816 | "formidable": { 817 | "version": "1.0.16", 818 | "resolved": "https://registry.npmjs.org/formidable/-/formidable-1.0.16.tgz", 819 | "integrity": "sha1-SRbP38TL7QILJXpqlQWpqzjCzQ4=", 820 | "dev": true 821 | }, 822 | "forwarded": { 823 | "version": "0.1.2", 824 | "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", 825 | "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=", 826 | "dev": true 827 | }, 828 | "fresh": { 829 | "version": "0.5.2", 830 | "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", 831 | "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=", 832 | "dev": true 833 | }, 834 | "fs.realpath": { 835 | "version": "1.0.0", 836 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", 837 | "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", 838 | "dev": true 839 | }, 840 | "function-bind": { 841 | "version": "1.1.1", 842 | "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", 843 | "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", 844 | "dev": true 845 | }, 846 | "get-intrinsic": { 847 | "version": "1.0.2", 848 | "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.0.2.tgz", 849 | "integrity": "sha512-aeX0vrFm21ILl3+JpFFRNe9aUvp6VFZb2/CTbgLb8j75kOhvoNYjt9d8KA/tJG4gSo8nzEDedRl0h7vDmBYRVg==", 850 | "dev": true, 851 | "requires": { 852 | "function-bind": "^1.1.1", 853 | "has": "^1.0.3", 854 | "has-symbols": "^1.0.1" 855 | } 856 | }, 857 | "glob": { 858 | "version": "5.0.15", 859 | "resolved": "https://registry.npmjs.org/glob/-/glob-5.0.15.tgz", 860 | "integrity": "sha1-G8k2ueAvSmA/zCIuz3Yz0wuLk7E=", 861 | "dev": true, 862 | "requires": { 863 | "inflight": "^1.0.4", 864 | "inherits": "2", 865 | "minimatch": "2 || 3", 866 | "once": "^1.3.0", 867 | "path-is-absolute": "^1.0.0" 868 | } 869 | }, 870 | "graceful-readlink": { 871 | "version": "1.0.1", 872 | "resolved": "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz", 873 | "integrity": "sha1-TK+tdrxi8C+gObL5Tpo906ORpyU=", 874 | "dev": true 875 | }, 876 | "growl": { 877 | "version": "1.9.2", 878 | "resolved": "https://registry.npmjs.org/growl/-/growl-1.9.2.tgz", 879 | "integrity": "sha1-Dqd0NxXbjY3ixe3hd14bRayFwC8=", 880 | "dev": true 881 | }, 882 | "has": { 883 | "version": "1.0.3", 884 | "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", 885 | "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", 886 | "dev": true, 887 | "requires": { 888 | "function-bind": "^1.1.1" 889 | } 890 | }, 891 | "has-ansi": { 892 | "version": "2.0.0", 893 | "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", 894 | "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", 895 | "dev": true, 896 | "requires": { 897 | "ansi-regex": "^2.0.0" 898 | } 899 | }, 900 | "has-color": { 901 | "version": "0.1.7", 902 | "resolved": "https://registry.npmjs.org/has-color/-/has-color-0.1.7.tgz", 903 | "integrity": "sha1-ZxRKUmDDT8PMpnfQQdr1L+e3iy8=", 904 | "dev": true 905 | }, 906 | "has-flag": { 907 | "version": "3.0.0", 908 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", 909 | "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", 910 | "dev": true 911 | }, 912 | "has-symbols": { 913 | "version": "1.0.1", 914 | "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", 915 | "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==", 916 | "dev": true 917 | }, 918 | "htmlparser2": { 919 | "version": "3.8.3", 920 | "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.8.3.tgz", 921 | "integrity": "sha1-mWwosZFRaovoZQGn15dX5ccMEGg=", 922 | "dev": true, 923 | "requires": { 924 | "domelementtype": "1", 925 | "domhandler": "2.3", 926 | "domutils": "1.5", 927 | "entities": "1.0", 928 | "readable-stream": "1.1" 929 | } 930 | }, 931 | "http-assert": { 932 | "version": "1.4.1", 933 | "resolved": "https://registry.npmjs.org/http-assert/-/http-assert-1.4.1.tgz", 934 | "integrity": "sha512-rdw7q6GTlibqVVbXr0CKelfV5iY8G2HqEUkhSk297BMbSpSL8crXC+9rjKoMcZZEsksX30le6f/4ul4E28gegw==", 935 | "dev": true, 936 | "requires": { 937 | "deep-equal": "~1.0.1", 938 | "http-errors": "~1.7.2" 939 | }, 940 | "dependencies": { 941 | "deep-equal": { 942 | "version": "1.0.1", 943 | "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.0.1.tgz", 944 | "integrity": "sha1-9dJgKStmDghO/0zbyfCK0yR0SLU=", 945 | "dev": true 946 | } 947 | } 948 | }, 949 | "http-errors": { 950 | "version": "1.7.2", 951 | "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", 952 | "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", 953 | "dev": true, 954 | "requires": { 955 | "depd": "~1.1.2", 956 | "inherits": "2.0.3", 957 | "setprototypeof": "1.1.1", 958 | "statuses": ">= 1.5.0 < 2", 959 | "toidentifier": "1.0.0" 960 | } 961 | }, 962 | "i": { 963 | "version": "0.3.7", 964 | "resolved": "https://registry.npmjs.org/i/-/i-0.3.7.tgz", 965 | "integrity": "sha512-FYz4wlXgkQwIPqhzC5TdNMLSE5+GS1IIDJZY/1ZiEPCT2S3COUVZeT5OW4BmW4r5LHLQuOosSwsvnroG9GR59Q==", 966 | "dev": true 967 | }, 968 | "iconv-lite": { 969 | "version": "0.4.24", 970 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", 971 | "integrity": "sha1-ICK0sl+93CHS9SSXSkdKr+czkIs=", 972 | "requires": { 973 | "safer-buffer": ">= 2.1.2 < 3" 974 | } 975 | }, 976 | "inflight": { 977 | "version": "1.0.6", 978 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", 979 | "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", 980 | "dev": true, 981 | "requires": { 982 | "once": "^1.3.0", 983 | "wrappy": "1" 984 | } 985 | }, 986 | "inherit": { 987 | "version": "2.2.7", 988 | "resolved": "https://registry.npmjs.org/inherit/-/inherit-2.2.7.tgz", 989 | "integrity": "sha512-dxJmC1j0Q32NFAjvbd6g3lXYLZ49HgzotgbSMwMkoiTXGhC9412Oc24g7A7M9cPPkw/vDsF2cSII+2zJwocUtQ==", 990 | "dev": true 991 | }, 992 | "inherits": { 993 | "version": "2.0.3", 994 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", 995 | "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", 996 | "dev": true 997 | }, 998 | "ipaddr.js": { 999 | "version": "1.9.1", 1000 | "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", 1001 | "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", 1002 | "dev": true 1003 | }, 1004 | "is-arguments": { 1005 | "version": "1.1.0", 1006 | "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.0.tgz", 1007 | "integrity": "sha512-1Ij4lOMPl/xB5kBDn7I+b2ttPMKa8szhEIrXDuXQD/oe3HJLTLhqhgGspwgyGd6MOywBUqVvYicF72lkgDnIHg==", 1008 | "dev": true, 1009 | "requires": { 1010 | "call-bind": "^1.0.0" 1011 | } 1012 | }, 1013 | "is-bigint": { 1014 | "version": "1.0.1", 1015 | "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.1.tgz", 1016 | "integrity": "sha512-J0ELF4yHFxHy0cmSxZuheDOz2luOdVvqjwmEcj8H/L1JHeuEDSDbeRP+Dk9kFVk5RTFzbucJ2Kb9F7ixY2QaCg==", 1017 | "dev": true 1018 | }, 1019 | "is-boolean-object": { 1020 | "version": "1.1.0", 1021 | "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.0.tgz", 1022 | "integrity": "sha512-a7Uprx8UtD+HWdyYwnD1+ExtTgqQtD2k/1yJgtXP6wnMm8byhkoTZRl+95LLThpzNZJ5aEvi46cdH+ayMFRwmA==", 1023 | "dev": true, 1024 | "requires": { 1025 | "call-bind": "^1.0.0" 1026 | } 1027 | }, 1028 | "is-callable": { 1029 | "version": "1.2.2", 1030 | "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.2.tgz", 1031 | "integrity": "sha512-dnMqspv5nU3LoewK2N/y7KLtxtakvTuaCsU9FU50/QDmdbHNy/4/JuRtMHqRU22o3q+W89YQndQEeCVwK+3qrA==", 1032 | "dev": true 1033 | }, 1034 | "is-core-module": { 1035 | "version": "2.2.0", 1036 | "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.2.0.tgz", 1037 | "integrity": "sha512-XRAfAdyyY5F5cOXn7hYQDqh2Xmii+DEfIcQGxK/uNwMHhIkPWO0g8msXcbzLe+MpGoR951MlqM/2iIlU4vKDdQ==", 1038 | "dev": true, 1039 | "requires": { 1040 | "has": "^1.0.3" 1041 | } 1042 | }, 1043 | "is-date-object": { 1044 | "version": "1.0.2", 1045 | "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.2.tgz", 1046 | "integrity": "sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==", 1047 | "dev": true 1048 | }, 1049 | "is-fullwidth-code-point": { 1050 | "version": "3.0.0", 1051 | "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", 1052 | "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", 1053 | "dev": true 1054 | }, 1055 | "is-generator-function": { 1056 | "version": "1.0.8", 1057 | "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.8.tgz", 1058 | "integrity": "sha512-2Omr/twNtufVZFr1GhxjOMFPAj2sjc/dKaIqBhvo4qciXfJmITGH6ZGd8eZYNHza8t1y0e01AuqRhJwfWp26WQ==", 1059 | "dev": true 1060 | }, 1061 | "is-map": { 1062 | "version": "2.0.2", 1063 | "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.2.tgz", 1064 | "integrity": "sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg==", 1065 | "dev": true 1066 | }, 1067 | "is-negative-zero": { 1068 | "version": "2.0.1", 1069 | "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.1.tgz", 1070 | "integrity": "sha512-2z6JzQvZRa9A2Y7xC6dQQm4FSTSTNWjKIYYTt4246eMTJmIo0Q+ZyOsU66X8lxK1AbB92dFeglPLrhwpeRKO6w==", 1071 | "dev": true 1072 | }, 1073 | "is-number-object": { 1074 | "version": "1.0.4", 1075 | "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.4.tgz", 1076 | "integrity": "sha512-zohwelOAur+5uXtk8O3GPQ1eAcu4ZX3UwxQhUlfFFMNpUd83gXgjbhJh6HmB6LUNV/ieOLQuDwJO3dWJosUeMw==", 1077 | "dev": true 1078 | }, 1079 | "is-regex": { 1080 | "version": "1.1.1", 1081 | "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.1.tgz", 1082 | "integrity": "sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg==", 1083 | "dev": true, 1084 | "requires": { 1085 | "has-symbols": "^1.0.1" 1086 | } 1087 | }, 1088 | "is-set": { 1089 | "version": "2.0.2", 1090 | "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.2.tgz", 1091 | "integrity": "sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g==", 1092 | "dev": true 1093 | }, 1094 | "is-string": { 1095 | "version": "1.0.5", 1096 | "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.5.tgz", 1097 | "integrity": "sha512-buY6VNRjhQMiF1qWDouloZlQbRhDPCebwxSjxMjxgemYT46YMd2NR0/H+fBhEfWX4A/w9TBJ+ol+okqJKFE6vQ==", 1098 | "dev": true 1099 | }, 1100 | "is-symbol": { 1101 | "version": "1.0.3", 1102 | "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz", 1103 | "integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==", 1104 | "dev": true, 1105 | "requires": { 1106 | "has-symbols": "^1.0.1" 1107 | } 1108 | }, 1109 | "is-typed-array": { 1110 | "version": "1.1.4", 1111 | "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.4.tgz", 1112 | "integrity": "sha512-ILaRgn4zaSrVNXNGtON6iFNotXW3hAPF3+0fB1usg2jFlWqo5fEDdmJkz0zBfoi7Dgskr8Khi2xZ8cXqZEfXNA==", 1113 | "dev": true, 1114 | "requires": { 1115 | "available-typed-arrays": "^1.0.2", 1116 | "call-bind": "^1.0.0", 1117 | "es-abstract": "^1.18.0-next.1", 1118 | "foreach": "^2.0.5", 1119 | "has-symbols": "^1.0.1" 1120 | } 1121 | }, 1122 | "is-utf8": { 1123 | "version": "0.2.1", 1124 | "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", 1125 | "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=", 1126 | "dev": true 1127 | }, 1128 | "is-weakmap": { 1129 | "version": "2.0.1", 1130 | "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.1.tgz", 1131 | "integrity": "sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA==", 1132 | "dev": true 1133 | }, 1134 | "is-weakset": { 1135 | "version": "2.0.1", 1136 | "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.1.tgz", 1137 | "integrity": "sha512-pi4vhbhVHGLxohUw7PhGsueT4vRGFoXhP7+RGN0jKIv9+8PWYCQTqtADngrxOm2g46hoH0+g8uZZBzMrvVGDmw==", 1138 | "dev": true 1139 | }, 1140 | "isarray": { 1141 | "version": "0.0.1", 1142 | "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", 1143 | "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", 1144 | "dev": true 1145 | }, 1146 | "isstream": { 1147 | "version": "0.1.2", 1148 | "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", 1149 | "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", 1150 | "dev": true 1151 | }, 1152 | "jade": { 1153 | "version": "0.26.3", 1154 | "resolved": "https://registry.npmjs.org/jade/-/jade-0.26.3.tgz", 1155 | "integrity": "sha1-jxDXl32NefL2/4YqgbBRPMslaGw=", 1156 | "dev": true, 1157 | "requires": { 1158 | "commander": "0.6.1", 1159 | "mkdirp": "0.3.0" 1160 | }, 1161 | "dependencies": { 1162 | "commander": { 1163 | "version": "0.6.1", 1164 | "resolved": "https://registry.npmjs.org/commander/-/commander-0.6.1.tgz", 1165 | "integrity": "sha1-+mihT2qUXVTbvlDYzbMyDp47GgY=", 1166 | "dev": true 1167 | }, 1168 | "mkdirp": { 1169 | "version": "0.3.0", 1170 | "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.0.tgz", 1171 | "integrity": "sha1-G79asbqCevI1dRQ0kEJkVfSB/h4=", 1172 | "dev": true 1173 | } 1174 | } 1175 | }, 1176 | "js-yaml": { 1177 | "version": "3.4.6", 1178 | "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.4.6.tgz", 1179 | "integrity": "sha1-a+GyP2JJ9T0pM3D9TRqqY84bTrA=", 1180 | "dev": true, 1181 | "requires": { 1182 | "argparse": "^1.0.2", 1183 | "esprima": "^2.6.0", 1184 | "inherit": "^2.2.2" 1185 | } 1186 | }, 1187 | "jscs": { 1188 | "version": "3.0.7", 1189 | "resolved": "https://registry.npmjs.org/jscs/-/jscs-3.0.7.tgz", 1190 | "integrity": "sha1-cUG03/W4bjLQ6Z12S4NnZ8MNIBo=", 1191 | "dev": true, 1192 | "requires": { 1193 | "chalk": "~1.1.0", 1194 | "cli-table": "~0.3.1", 1195 | "commander": "~2.9.0", 1196 | "cst": "^0.4.3", 1197 | "estraverse": "^4.1.0", 1198 | "exit": "~0.1.2", 1199 | "glob": "^5.0.1", 1200 | "htmlparser2": "3.8.3", 1201 | "js-yaml": "~3.4.0", 1202 | "jscs-jsdoc": "^2.0.0", 1203 | "jscs-preset-wikimedia": "~1.0.0", 1204 | "jsonlint": "~1.6.2", 1205 | "lodash": "~3.10.0", 1206 | "minimatch": "~3.0.0", 1207 | "natural-compare": "~1.2.2", 1208 | "pathval": "~0.1.1", 1209 | "prompt": "~0.2.14", 1210 | "reserved-words": "^0.1.1", 1211 | "resolve": "^1.1.6", 1212 | "strip-bom": "^2.0.0", 1213 | "strip-json-comments": "~1.0.2", 1214 | "to-double-quotes": "^2.0.0", 1215 | "to-single-quotes": "^2.0.0", 1216 | "vow": "~0.4.8", 1217 | "vow-fs": "~0.3.4", 1218 | "xmlbuilder": "^3.1.0" 1219 | } 1220 | }, 1221 | "jscs-jsdoc": { 1222 | "version": "2.0.0", 1223 | "resolved": "https://registry.npmjs.org/jscs-jsdoc/-/jscs-jsdoc-2.0.0.tgz", 1224 | "integrity": "sha1-9T684CmqMSW9iCkLpQ1k1FEKSHE=", 1225 | "dev": true, 1226 | "requires": { 1227 | "comment-parser": "^0.3.1", 1228 | "jsdoctypeparser": "~1.2.0" 1229 | } 1230 | }, 1231 | "jscs-preset-wikimedia": { 1232 | "version": "1.0.1", 1233 | "resolved": "https://registry.npmjs.org/jscs-preset-wikimedia/-/jscs-preset-wikimedia-1.0.1.tgz", 1234 | "integrity": "sha512-RWqu6IYSUlnYuCRCF0obCOHjJV0vhpLcvKbauwxmLQoZ0PiXDTWBYlfpsEfdhg7pmREAEwrARfDRz5qWD6qknA==", 1235 | "dev": true 1236 | }, 1237 | "jsdoctypeparser": { 1238 | "version": "1.2.0", 1239 | "resolved": "https://registry.npmjs.org/jsdoctypeparser/-/jsdoctypeparser-1.2.0.tgz", 1240 | "integrity": "sha1-597cFToRhJ/8UUEUSuhqfvDCU5I=", 1241 | "dev": true, 1242 | "requires": { 1243 | "lodash": "^3.7.0" 1244 | } 1245 | }, 1246 | "jshint": { 1247 | "version": "2.12.0", 1248 | "resolved": "https://registry.npmjs.org/jshint/-/jshint-2.12.0.tgz", 1249 | "integrity": "sha512-TwuuaUDmra0JMkuqvqy+WGo2xGHSNjv1BA1nTIgtH2K5z1jHuAEeAgp7laaR+hLRmajRjcrM71+vByBDanCyYA==", 1250 | "dev": true, 1251 | "requires": { 1252 | "cli": "~1.0.0", 1253 | "console-browserify": "1.1.x", 1254 | "exit": "0.1.x", 1255 | "htmlparser2": "3.8.x", 1256 | "lodash": "~4.17.19", 1257 | "minimatch": "~3.0.2", 1258 | "shelljs": "0.3.x", 1259 | "strip-json-comments": "1.0.x" 1260 | }, 1261 | "dependencies": { 1262 | "lodash": { 1263 | "version": "4.17.20", 1264 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", 1265 | "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==", 1266 | "dev": true 1267 | } 1268 | } 1269 | }, 1270 | "jsonlint": { 1271 | "version": "1.6.3", 1272 | "resolved": "https://registry.npmjs.org/jsonlint/-/jsonlint-1.6.3.tgz", 1273 | "integrity": "sha512-jMVTMzP+7gU/IyC6hvKyWpUU8tmTkK5b3BPNuMI9U8Sit+YAWLlZwB6Y6YrdCxfg2kNz05p3XY3Bmm4m26Nv3A==", 1274 | "dev": true, 1275 | "requires": { 1276 | "JSV": "^4.0.x", 1277 | "nomnom": "^1.5.x" 1278 | } 1279 | }, 1280 | "keygrip": { 1281 | "version": "1.1.0", 1282 | "resolved": "https://registry.npmjs.org/keygrip/-/keygrip-1.1.0.tgz", 1283 | "integrity": "sha512-iYSchDJ+liQ8iwbSI2QqsQOvqv58eJCEanyJPJi+Khyu8smkcKSFUCbPwzFcL7YVtZ6eONjqRX/38caJ7QjRAQ==", 1284 | "dev": true, 1285 | "requires": { 1286 | "tsscmp": "1.0.6" 1287 | } 1288 | }, 1289 | "koa": { 1290 | "version": "2.13.1", 1291 | "resolved": "https://registry.npmjs.org/koa/-/koa-2.13.1.tgz", 1292 | "integrity": "sha512-Lb2Dloc72auj5vK4X4qqL7B5jyDPQaZucc9sR/71byg7ryoD1NCaCm63CShk9ID9quQvDEi1bGR/iGjCG7As3w==", 1293 | "dev": true, 1294 | "requires": { 1295 | "accepts": "^1.3.5", 1296 | "cache-content-type": "^1.0.0", 1297 | "content-disposition": "~0.5.2", 1298 | "content-type": "^1.0.4", 1299 | "cookies": "~0.8.0", 1300 | "debug": "~3.1.0", 1301 | "delegates": "^1.0.0", 1302 | "depd": "^2.0.0", 1303 | "destroy": "^1.0.4", 1304 | "encodeurl": "^1.0.2", 1305 | "escape-html": "^1.0.3", 1306 | "fresh": "~0.5.2", 1307 | "http-assert": "^1.3.0", 1308 | "http-errors": "^1.6.3", 1309 | "is-generator-function": "^1.0.7", 1310 | "koa-compose": "^4.1.0", 1311 | "koa-convert": "^1.2.0", 1312 | "on-finished": "^2.3.0", 1313 | "only": "~0.0.2", 1314 | "parseurl": "^1.3.2", 1315 | "statuses": "^1.5.0", 1316 | "type-is": "^1.6.16", 1317 | "vary": "^1.1.2" 1318 | }, 1319 | "dependencies": { 1320 | "debug": { 1321 | "version": "3.1.0", 1322 | "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", 1323 | "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", 1324 | "dev": true, 1325 | "requires": { 1326 | "ms": "2.0.0" 1327 | } 1328 | }, 1329 | "depd": { 1330 | "version": "2.0.0", 1331 | "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", 1332 | "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", 1333 | "dev": true 1334 | } 1335 | } 1336 | }, 1337 | "koa-compose": { 1338 | "version": "4.1.0", 1339 | "resolved": "https://registry.npmjs.org/koa-compose/-/koa-compose-4.1.0.tgz", 1340 | "integrity": "sha512-8ODW8TrDuMYvXRwra/Kh7/rJo9BtOfPc6qO8eAfC80CnCvSjSl0bkRM24X6/XBBEyj0v1nRUQ1LyOy3dbqOWXw==", 1341 | "dev": true 1342 | }, 1343 | "koa-convert": { 1344 | "version": "1.2.0", 1345 | "resolved": "https://registry.npmjs.org/koa-convert/-/koa-convert-1.2.0.tgz", 1346 | "integrity": "sha1-2kCHXfSd4FOQmNFwC1CCDOvNIdA=", 1347 | "dev": true, 1348 | "requires": { 1349 | "co": "^4.6.0", 1350 | "koa-compose": "^3.0.0" 1351 | }, 1352 | "dependencies": { 1353 | "koa-compose": { 1354 | "version": "3.2.1", 1355 | "resolved": "https://registry.npmjs.org/koa-compose/-/koa-compose-3.2.1.tgz", 1356 | "integrity": "sha1-qFzLQLfZhtjlo0Wzoazo6rz1Tec=", 1357 | "dev": true, 1358 | "requires": { 1359 | "any-promise": "^1.1.0" 1360 | } 1361 | } 1362 | } 1363 | }, 1364 | "lodash": { 1365 | "version": "3.10.1", 1366 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-3.10.1.tgz", 1367 | "integrity": "sha1-W/Rejkm6QYnhfUgnid/RW9FAt7Y=", 1368 | "dev": true 1369 | }, 1370 | "lru-cache": { 1371 | "version": "2.7.3", 1372 | "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.7.3.tgz", 1373 | "integrity": "sha1-bUUk6LlV+V1PW1iFHOId1y+06VI=", 1374 | "dev": true 1375 | }, 1376 | "media-typer": { 1377 | "version": "0.3.0", 1378 | "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", 1379 | "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=", 1380 | "dev": true 1381 | }, 1382 | "merge-descriptors": { 1383 | "version": "1.0.1", 1384 | "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", 1385 | "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=", 1386 | "dev": true 1387 | }, 1388 | "methods": { 1389 | "version": "1.1.2", 1390 | "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", 1391 | "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=", 1392 | "dev": true 1393 | }, 1394 | "mime": { 1395 | "version": "1.6.0", 1396 | "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", 1397 | "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", 1398 | "dev": true 1399 | }, 1400 | "mime-db": { 1401 | "version": "1.45.0", 1402 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.45.0.tgz", 1403 | "integrity": "sha512-CkqLUxUk15hofLoLyljJSrukZi8mAtgd+yE5uO4tqRZsdsAJKv0O+rFMhVDRJgozy+yG6md5KwuXhD4ocIoP+w==", 1404 | "dev": true 1405 | }, 1406 | "mime-types": { 1407 | "version": "2.1.28", 1408 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.28.tgz", 1409 | "integrity": "sha512-0TO2yJ5YHYr7M2zzT7gDU1tbwHxEUWBCLt0lscSNpcdAfFyJOVEpRYNS7EXVcTLNj/25QO8gulHC5JtTzSE2UQ==", 1410 | "dev": true, 1411 | "requires": { 1412 | "mime-db": "1.45.0" 1413 | } 1414 | }, 1415 | "minimatch": { 1416 | "version": "3.0.4", 1417 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", 1418 | "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", 1419 | "dev": true, 1420 | "requires": { 1421 | "brace-expansion": "^1.1.7" 1422 | } 1423 | }, 1424 | "minimist": { 1425 | "version": "1.2.5", 1426 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", 1427 | "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", 1428 | "dev": true 1429 | }, 1430 | "mkdirp": { 1431 | "version": "0.5.5", 1432 | "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", 1433 | "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", 1434 | "dev": true, 1435 | "requires": { 1436 | "minimist": "^1.2.5" 1437 | } 1438 | }, 1439 | "mocha": { 1440 | "version": "2.5.3", 1441 | "resolved": "https://registry.npmjs.org/mocha/-/mocha-2.5.3.tgz", 1442 | "integrity": "sha1-FhvlvetJZ3HrmzV0UFC2IrWu/Fg=", 1443 | "dev": true, 1444 | "requires": { 1445 | "commander": "2.3.0", 1446 | "debug": "2.2.0", 1447 | "diff": "1.4.0", 1448 | "escape-string-regexp": "1.0.2", 1449 | "glob": "3.2.11", 1450 | "growl": "1.9.2", 1451 | "jade": "0.26.3", 1452 | "mkdirp": "0.5.1", 1453 | "supports-color": "1.2.0", 1454 | "to-iso-string": "0.0.2" 1455 | }, 1456 | "dependencies": { 1457 | "commander": { 1458 | "version": "2.3.0", 1459 | "resolved": "https://registry.npmjs.org/commander/-/commander-2.3.0.tgz", 1460 | "integrity": "sha1-/UMOiJgy7DU7ms0d4hfBHLPu+HM=", 1461 | "dev": true 1462 | }, 1463 | "debug": { 1464 | "version": "2.2.0", 1465 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz", 1466 | "integrity": "sha1-+HBX6ZWxofauaklgZkE3vFbwOdo=", 1467 | "dev": true, 1468 | "requires": { 1469 | "ms": "0.7.1" 1470 | } 1471 | }, 1472 | "escape-string-regexp": { 1473 | "version": "1.0.2", 1474 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.2.tgz", 1475 | "integrity": "sha1-Tbwv5nTnGUnK8/smlc5/LcHZqNE=", 1476 | "dev": true 1477 | }, 1478 | "glob": { 1479 | "version": "3.2.11", 1480 | "resolved": "https://registry.npmjs.org/glob/-/glob-3.2.11.tgz", 1481 | "integrity": "sha1-Spc/Y1uRkPcV0QmH1cAP0oFevj0=", 1482 | "dev": true, 1483 | "requires": { 1484 | "inherits": "2", 1485 | "minimatch": "0.3" 1486 | } 1487 | }, 1488 | "minimatch": { 1489 | "version": "0.3.0", 1490 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.3.0.tgz", 1491 | "integrity": "sha1-J12O2qxPG7MyZHIInnlJyDlGmd0=", 1492 | "dev": true, 1493 | "requires": { 1494 | "lru-cache": "2", 1495 | "sigmund": "~1.0.0" 1496 | } 1497 | }, 1498 | "minimist": { 1499 | "version": "0.0.8", 1500 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", 1501 | "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", 1502 | "dev": true 1503 | }, 1504 | "mkdirp": { 1505 | "version": "0.5.1", 1506 | "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", 1507 | "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", 1508 | "dev": true, 1509 | "requires": { 1510 | "minimist": "0.0.8" 1511 | } 1512 | }, 1513 | "ms": { 1514 | "version": "0.7.1", 1515 | "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz", 1516 | "integrity": "sha1-nNE8A62/8ltl7/3nzoZO6VIBcJg=", 1517 | "dev": true 1518 | }, 1519 | "supports-color": { 1520 | "version": "1.2.0", 1521 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-1.2.0.tgz", 1522 | "integrity": "sha1-/x7R5hFp0Gs88tWI4YixjYhH4X4=", 1523 | "dev": true 1524 | } 1525 | } 1526 | }, 1527 | "ms": { 1528 | "version": "2.0.0", 1529 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", 1530 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", 1531 | "dev": true 1532 | }, 1533 | "mute-stream": { 1534 | "version": "0.0.8", 1535 | "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", 1536 | "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", 1537 | "dev": true 1538 | }, 1539 | "natural-compare": { 1540 | "version": "1.2.2", 1541 | "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.2.2.tgz", 1542 | "integrity": "sha1-H5bWDjFBysG20FZTzg2urHY69qo=", 1543 | "dev": true 1544 | }, 1545 | "ncp": { 1546 | "version": "0.4.2", 1547 | "resolved": "https://registry.npmjs.org/ncp/-/ncp-0.4.2.tgz", 1548 | "integrity": "sha1-q8xsvT7C7Spyn/bnwfqPAXhKhXQ=", 1549 | "dev": true 1550 | }, 1551 | "negotiator": { 1552 | "version": "0.6.2", 1553 | "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", 1554 | "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==", 1555 | "dev": true 1556 | }, 1557 | "nomnom": { 1558 | "version": "1.8.1", 1559 | "resolved": "https://registry.npmjs.org/nomnom/-/nomnom-1.8.1.tgz", 1560 | "integrity": "sha1-IVH3Ikcrp55Qp2/BJbuMjy5Nwqc=", 1561 | "dev": true, 1562 | "requires": { 1563 | "chalk": "~0.4.0", 1564 | "underscore": "~1.6.0" 1565 | }, 1566 | "dependencies": { 1567 | "ansi-styles": { 1568 | "version": "1.0.0", 1569 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-1.0.0.tgz", 1570 | "integrity": "sha1-yxAt8cVvUSPquLZ817mAJ6AnkXg=", 1571 | "dev": true 1572 | }, 1573 | "chalk": { 1574 | "version": "0.4.0", 1575 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-0.4.0.tgz", 1576 | "integrity": "sha1-UZmj3c0MHv4jvAjBsCewYXbgxk8=", 1577 | "dev": true, 1578 | "requires": { 1579 | "ansi-styles": "~1.0.0", 1580 | "has-color": "~0.1.0", 1581 | "strip-ansi": "~0.1.0" 1582 | } 1583 | }, 1584 | "strip-ansi": { 1585 | "version": "0.1.1", 1586 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-0.1.1.tgz", 1587 | "integrity": "sha1-OeipjQRNFQZgq+SmgIrPcLt7yZE=", 1588 | "dev": true 1589 | } 1590 | } 1591 | }, 1592 | "object-inspect": { 1593 | "version": "1.9.0", 1594 | "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.9.0.tgz", 1595 | "integrity": "sha512-i3Bp9iTqwhaLZBxGkRfo5ZbE07BQRT7MGu8+nNgwW9ItGp1TzCTw2DLEoWwjClxBjOFI/hWljTAmYGCEwmtnOw==", 1596 | "dev": true 1597 | }, 1598 | "object-is": { 1599 | "version": "1.1.4", 1600 | "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.4.tgz", 1601 | "integrity": "sha512-1ZvAZ4wlF7IyPVOcE1Omikt7UpaFlOQq0HlSti+ZvDH3UiD2brwGMwDbyV43jao2bKJ+4+WdPJHSd7kgzKYVqg==", 1602 | "dev": true, 1603 | "requires": { 1604 | "call-bind": "^1.0.0", 1605 | "define-properties": "^1.1.3" 1606 | } 1607 | }, 1608 | "object-keys": { 1609 | "version": "1.1.1", 1610 | "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", 1611 | "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", 1612 | "dev": true 1613 | }, 1614 | "object.assign": { 1615 | "version": "4.1.2", 1616 | "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", 1617 | "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", 1618 | "dev": true, 1619 | "requires": { 1620 | "call-bind": "^1.0.0", 1621 | "define-properties": "^1.1.3", 1622 | "has-symbols": "^1.0.1", 1623 | "object-keys": "^1.1.1" 1624 | } 1625 | }, 1626 | "on-finished": { 1627 | "version": "2.3.0", 1628 | "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", 1629 | "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", 1630 | "dev": true, 1631 | "requires": { 1632 | "ee-first": "1.1.1" 1633 | } 1634 | }, 1635 | "once": { 1636 | "version": "1.4.0", 1637 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 1638 | "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", 1639 | "dev": true, 1640 | "requires": { 1641 | "wrappy": "1" 1642 | } 1643 | }, 1644 | "only": { 1645 | "version": "0.0.2", 1646 | "resolved": "https://registry.npmjs.org/only/-/only-0.0.2.tgz", 1647 | "integrity": "sha1-Kv3oTQPlC5qO3EROMGEKcCle37Q=", 1648 | "dev": true 1649 | }, 1650 | "parseurl": { 1651 | "version": "1.3.3", 1652 | "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", 1653 | "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", 1654 | "dev": true 1655 | }, 1656 | "path-is-absolute": { 1657 | "version": "1.0.1", 1658 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", 1659 | "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", 1660 | "dev": true 1661 | }, 1662 | "path-parse": { 1663 | "version": "1.0.7", 1664 | "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", 1665 | "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", 1666 | "dev": true 1667 | }, 1668 | "path-to-regexp": { 1669 | "version": "0.1.7", 1670 | "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", 1671 | "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=", 1672 | "dev": true 1673 | }, 1674 | "pathval": { 1675 | "version": "0.1.1", 1676 | "resolved": "https://registry.npmjs.org/pathval/-/pathval-0.1.1.tgz", 1677 | "integrity": "sha1-CPkRzcqczllCiA2ngXvAtyO2bYI=", 1678 | "dev": true 1679 | }, 1680 | "pkginfo": { 1681 | "version": "0.4.1", 1682 | "resolved": "https://registry.npmjs.org/pkginfo/-/pkginfo-0.4.1.tgz", 1683 | "integrity": "sha1-tUGO8EOd5UJfxJlQQtztFPsqhP8=", 1684 | "dev": true 1685 | }, 1686 | "process-nextick-args": { 1687 | "version": "2.0.1", 1688 | "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", 1689 | "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", 1690 | "dev": true 1691 | }, 1692 | "prompt": { 1693 | "version": "0.2.14", 1694 | "resolved": "https://registry.npmjs.org/prompt/-/prompt-0.2.14.tgz", 1695 | "integrity": "sha1-V3VPZPVD/XsIRXB8gY7OYY8F/9w=", 1696 | "dev": true, 1697 | "requires": { 1698 | "pkginfo": "0.x.x", 1699 | "read": "1.0.x", 1700 | "revalidator": "0.1.x", 1701 | "utile": "0.2.x", 1702 | "winston": "0.8.x" 1703 | }, 1704 | "dependencies": { 1705 | "colors": { 1706 | "version": "0.6.2", 1707 | "resolved": "https://registry.npmjs.org/colors/-/colors-0.6.2.tgz", 1708 | "integrity": "sha1-JCP+ZnisDF2uiFLl0OW+CMmXq8w=", 1709 | "dev": true 1710 | }, 1711 | "winston": { 1712 | "version": "0.8.3", 1713 | "resolved": "https://registry.npmjs.org/winston/-/winston-0.8.3.tgz", 1714 | "integrity": "sha1-ZLar9M0Brcrv1QCTk7HY6L7BnbA=", 1715 | "dev": true, 1716 | "requires": { 1717 | "async": "0.2.x", 1718 | "colors": "0.6.x", 1719 | "cycle": "1.0.x", 1720 | "eyes": "0.1.x", 1721 | "isstream": "0.1.x", 1722 | "pkginfo": "0.3.x", 1723 | "stack-trace": "0.0.x" 1724 | }, 1725 | "dependencies": { 1726 | "pkginfo": { 1727 | "version": "0.3.1", 1728 | "resolved": "https://registry.npmjs.org/pkginfo/-/pkginfo-0.3.1.tgz", 1729 | "integrity": "sha1-Wyn2qB9wcXFC4J52W76rl7T4HiE=", 1730 | "dev": true 1731 | } 1732 | } 1733 | } 1734 | } 1735 | }, 1736 | "proxy-addr": { 1737 | "version": "2.0.6", 1738 | "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz", 1739 | "integrity": "sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw==", 1740 | "dev": true, 1741 | "requires": { 1742 | "forwarded": "~0.1.2", 1743 | "ipaddr.js": "1.9.1" 1744 | } 1745 | }, 1746 | "qs": { 1747 | "version": "6.7.0", 1748 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", 1749 | "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==", 1750 | "dev": true 1751 | }, 1752 | "range-parser": { 1753 | "version": "1.2.1", 1754 | "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", 1755 | "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", 1756 | "dev": true 1757 | }, 1758 | "raw-body": { 1759 | "version": "2.4.1", 1760 | "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.1.tgz", 1761 | "integrity": "sha1-MKyC+Yu1rowVLmcUnayNVRU7Fow=", 1762 | "requires": { 1763 | "bytes": "3.1.0", 1764 | "http-errors": "1.7.3", 1765 | "iconv-lite": "0.4.24", 1766 | "unpipe": "1.0.0" 1767 | }, 1768 | "dependencies": { 1769 | "http-errors": { 1770 | "version": "1.7.3", 1771 | "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.3.tgz", 1772 | "integrity": "sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw==", 1773 | "requires": { 1774 | "depd": "~1.1.2", 1775 | "inherits": "2.0.4", 1776 | "setprototypeof": "1.1.1", 1777 | "statuses": ">= 1.5.0 < 2", 1778 | "toidentifier": "1.0.0" 1779 | } 1780 | }, 1781 | "inherits": { 1782 | "version": "2.0.4", 1783 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 1784 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" 1785 | } 1786 | } 1787 | }, 1788 | "read": { 1789 | "version": "1.0.7", 1790 | "resolved": "https://registry.npmjs.org/read/-/read-1.0.7.tgz", 1791 | "integrity": "sha1-s9oZvQUkMal2cdRKQmNK33ELQMQ=", 1792 | "dev": true, 1793 | "requires": { 1794 | "mute-stream": "~0.0.4" 1795 | } 1796 | }, 1797 | "readable-stream": { 1798 | "version": "1.1.14", 1799 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", 1800 | "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", 1801 | "dev": true, 1802 | "requires": { 1803 | "core-util-is": "~1.0.0", 1804 | "inherits": "~2.0.1", 1805 | "isarray": "0.0.1", 1806 | "string_decoder": "~0.10.x" 1807 | } 1808 | }, 1809 | "reduce-component": { 1810 | "version": "1.0.1", 1811 | "resolved": "https://registry.npmjs.org/reduce-component/-/reduce-component-1.0.1.tgz", 1812 | "integrity": "sha1-4Mk1QsV0UhvqE98PlIjtgqt3xdo=", 1813 | "dev": true 1814 | }, 1815 | "regenerator-runtime": { 1816 | "version": "0.11.1", 1817 | "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz", 1818 | "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==", 1819 | "dev": true 1820 | }, 1821 | "regexp.prototype.flags": { 1822 | "version": "1.3.1", 1823 | "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.3.1.tgz", 1824 | "integrity": "sha512-JiBdRBq91WlY7uRJ0ds7R+dU02i6LKi8r3BuQhNXn+kmeLN+EfHhfjqMRis1zJxnlu88hq/4dx0P2OP3APRTOA==", 1825 | "dev": true, 1826 | "requires": { 1827 | "call-bind": "^1.0.2", 1828 | "define-properties": "^1.1.3" 1829 | } 1830 | }, 1831 | "reserved-words": { 1832 | "version": "0.1.2", 1833 | "resolved": "https://registry.npmjs.org/reserved-words/-/reserved-words-0.1.2.tgz", 1834 | "integrity": "sha1-AKCUD5jNUBrqqsMWQR2a3FKzGrE=", 1835 | "dev": true 1836 | }, 1837 | "resolve": { 1838 | "version": "1.19.0", 1839 | "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.19.0.tgz", 1840 | "integrity": "sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg==", 1841 | "dev": true, 1842 | "requires": { 1843 | "is-core-module": "^2.1.0", 1844 | "path-parse": "^1.0.6" 1845 | } 1846 | }, 1847 | "revalidator": { 1848 | "version": "0.1.8", 1849 | "resolved": "https://registry.npmjs.org/revalidator/-/revalidator-0.1.8.tgz", 1850 | "integrity": "sha1-/s5hv6DBtSoga9axgZgYS91SOjs=", 1851 | "dev": true 1852 | }, 1853 | "rimraf": { 1854 | "version": "2.7.1", 1855 | "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", 1856 | "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", 1857 | "dev": true, 1858 | "requires": { 1859 | "glob": "^7.1.3" 1860 | }, 1861 | "dependencies": { 1862 | "glob": { 1863 | "version": "7.1.6", 1864 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", 1865 | "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", 1866 | "dev": true, 1867 | "requires": { 1868 | "fs.realpath": "^1.0.0", 1869 | "inflight": "^1.0.4", 1870 | "inherits": "2", 1871 | "minimatch": "^3.0.4", 1872 | "once": "^1.3.0", 1873 | "path-is-absolute": "^1.0.0" 1874 | } 1875 | } 1876 | } 1877 | }, 1878 | "safe-buffer": { 1879 | "version": "5.1.2", 1880 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", 1881 | "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", 1882 | "dev": true 1883 | }, 1884 | "safer-buffer": { 1885 | "version": "2.1.2", 1886 | "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", 1887 | "integrity": "sha1-RPoWGwGHuVSd2Eu5GAL5vYOFzWo=" 1888 | }, 1889 | "send": { 1890 | "version": "0.17.1", 1891 | "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", 1892 | "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==", 1893 | "dev": true, 1894 | "requires": { 1895 | "debug": "2.6.9", 1896 | "depd": "~1.1.2", 1897 | "destroy": "~1.0.4", 1898 | "encodeurl": "~1.0.2", 1899 | "escape-html": "~1.0.3", 1900 | "etag": "~1.8.1", 1901 | "fresh": "0.5.2", 1902 | "http-errors": "~1.7.2", 1903 | "mime": "1.6.0", 1904 | "ms": "2.1.1", 1905 | "on-finished": "~2.3.0", 1906 | "range-parser": "~1.2.1", 1907 | "statuses": "~1.5.0" 1908 | }, 1909 | "dependencies": { 1910 | "ms": { 1911 | "version": "2.1.1", 1912 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", 1913 | "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", 1914 | "dev": true 1915 | } 1916 | } 1917 | }, 1918 | "serve-static": { 1919 | "version": "1.14.1", 1920 | "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", 1921 | "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==", 1922 | "dev": true, 1923 | "requires": { 1924 | "encodeurl": "~1.0.2", 1925 | "escape-html": "~1.0.3", 1926 | "parseurl": "~1.3.3", 1927 | "send": "0.17.1" 1928 | } 1929 | }, 1930 | "setprototypeof": { 1931 | "version": "1.1.1", 1932 | "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", 1933 | "integrity": "sha1-fpWsskqpL1iF4KvvW6ExMw1K5oM=" 1934 | }, 1935 | "shelljs": { 1936 | "version": "0.3.0", 1937 | "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.3.0.tgz", 1938 | "integrity": "sha1-NZbmMHp4FUT1kfN9phg2DzHbV7E=", 1939 | "dev": true 1940 | }, 1941 | "side-channel": { 1942 | "version": "1.0.4", 1943 | "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", 1944 | "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", 1945 | "dev": true, 1946 | "requires": { 1947 | "call-bind": "^1.0.0", 1948 | "get-intrinsic": "^1.0.2", 1949 | "object-inspect": "^1.9.0" 1950 | } 1951 | }, 1952 | "sigmund": { 1953 | "version": "1.0.1", 1954 | "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz", 1955 | "integrity": "sha1-P/IfGYytIXX587eBhT/ZTQ0ZtZA=", 1956 | "dev": true 1957 | }, 1958 | "source-map": { 1959 | "version": "0.5.7", 1960 | "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", 1961 | "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", 1962 | "dev": true 1963 | }, 1964 | "source-map-support": { 1965 | "version": "0.4.18", 1966 | "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.18.tgz", 1967 | "integrity": "sha512-try0/JqxPLF9nOjvSta7tVondkP5dwgyLDjVoyMDlmjugT2lRZ1OfsrYTkCd2hkDnJTKRbO/Rl3orm8vlsUzbA==", 1968 | "dev": true, 1969 | "requires": { 1970 | "source-map": "^0.5.6" 1971 | } 1972 | }, 1973 | "sprintf-js": { 1974 | "version": "1.0.3", 1975 | "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", 1976 | "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", 1977 | "dev": true 1978 | }, 1979 | "stack-trace": { 1980 | "version": "0.0.10", 1981 | "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", 1982 | "integrity": "sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA=", 1983 | "dev": true 1984 | }, 1985 | "statuses": { 1986 | "version": "1.5.0", 1987 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", 1988 | "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" 1989 | }, 1990 | "string-width": { 1991 | "version": "4.2.0", 1992 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", 1993 | "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", 1994 | "dev": true, 1995 | "requires": { 1996 | "emoji-regex": "^8.0.0", 1997 | "is-fullwidth-code-point": "^3.0.0", 1998 | "strip-ansi": "^6.0.0" 1999 | }, 2000 | "dependencies": { 2001 | "ansi-regex": { 2002 | "version": "5.0.0", 2003 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", 2004 | "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", 2005 | "dev": true 2006 | }, 2007 | "strip-ansi": { 2008 | "version": "6.0.0", 2009 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", 2010 | "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", 2011 | "dev": true, 2012 | "requires": { 2013 | "ansi-regex": "^5.0.0" 2014 | } 2015 | } 2016 | } 2017 | }, 2018 | "string.prototype.trimend": { 2019 | "version": "1.0.3", 2020 | "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.3.tgz", 2021 | "integrity": "sha512-ayH0pB+uf0U28CtjlLvL7NaohvR1amUvVZk+y3DYb0Ey2PUV5zPkkKy9+U1ndVEIXO8hNg18eIv9Jntbii+dKw==", 2022 | "dev": true, 2023 | "requires": { 2024 | "call-bind": "^1.0.0", 2025 | "define-properties": "^1.1.3" 2026 | } 2027 | }, 2028 | "string.prototype.trimstart": { 2029 | "version": "1.0.3", 2030 | "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.3.tgz", 2031 | "integrity": "sha512-oBIBUy5lea5tt0ovtOFiEQaBkoBBkyJhZXzJYrSmDo5IUUqbOPvVezuRs/agBIdZ2p2Eo1FD6bD9USyBLfl3xg==", 2032 | "dev": true, 2033 | "requires": { 2034 | "call-bind": "^1.0.0", 2035 | "define-properties": "^1.1.3" 2036 | } 2037 | }, 2038 | "string_decoder": { 2039 | "version": "0.10.31", 2040 | "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", 2041 | "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", 2042 | "dev": true 2043 | }, 2044 | "strip-ansi": { 2045 | "version": "3.0.1", 2046 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", 2047 | "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", 2048 | "dev": true, 2049 | "requires": { 2050 | "ansi-regex": "^2.0.0" 2051 | } 2052 | }, 2053 | "strip-bom": { 2054 | "version": "2.0.0", 2055 | "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", 2056 | "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", 2057 | "dev": true, 2058 | "requires": { 2059 | "is-utf8": "^0.2.0" 2060 | } 2061 | }, 2062 | "strip-json-comments": { 2063 | "version": "1.0.4", 2064 | "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-1.0.4.tgz", 2065 | "integrity": "sha1-HhX7ysl9Pumb8tc7TGVrCCu6+5E=", 2066 | "dev": true 2067 | }, 2068 | "superagent": { 2069 | "version": "1.8.5", 2070 | "resolved": "https://registry.npmjs.org/superagent/-/superagent-1.8.5.tgz", 2071 | "integrity": "sha1-HA3cOvMOgOuE68BcshItqP6UC1U=", 2072 | "dev": true, 2073 | "requires": { 2074 | "component-emitter": "~1.2.0", 2075 | "cookiejar": "2.0.6", 2076 | "debug": "2", 2077 | "extend": "3.0.0", 2078 | "form-data": "1.0.0-rc3", 2079 | "formidable": "~1.0.14", 2080 | "methods": "~1.1.1", 2081 | "mime": "1.3.4", 2082 | "qs": "2.3.3", 2083 | "readable-stream": "1.0.27-1", 2084 | "reduce-component": "1.0.1" 2085 | }, 2086 | "dependencies": { 2087 | "mime": { 2088 | "version": "1.3.4", 2089 | "resolved": "https://registry.npmjs.org/mime/-/mime-1.3.4.tgz", 2090 | "integrity": "sha1-EV+eO2s9rylZmDyzjxSaLUDrXVM=", 2091 | "dev": true 2092 | }, 2093 | "qs": { 2094 | "version": "2.3.3", 2095 | "resolved": "https://registry.npmjs.org/qs/-/qs-2.3.3.tgz", 2096 | "integrity": "sha1-6eha2+ddoLvkyOBHaghikPhjtAQ=", 2097 | "dev": true 2098 | }, 2099 | "readable-stream": { 2100 | "version": "1.0.27-1", 2101 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.27-1.tgz", 2102 | "integrity": "sha1-a2eYPCA1fO/QfwFlABoW1xDZEHg=", 2103 | "dev": true, 2104 | "requires": { 2105 | "core-util-is": "~1.0.0", 2106 | "inherits": "~2.0.1", 2107 | "isarray": "0.0.1", 2108 | "string_decoder": "~0.10.x" 2109 | } 2110 | } 2111 | } 2112 | }, 2113 | "supertest": { 2114 | "version": "1.2.0", 2115 | "resolved": "https://registry.npmjs.org/supertest/-/supertest-1.2.0.tgz", 2116 | "integrity": "sha1-hQp5X5Bo0vrxngF5n/CZYuDOQ74=", 2117 | "dev": true, 2118 | "requires": { 2119 | "methods": "1.x", 2120 | "superagent": "^1.7.2" 2121 | } 2122 | }, 2123 | "supports-color": { 2124 | "version": "2.0.0", 2125 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", 2126 | "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", 2127 | "dev": true 2128 | }, 2129 | "to-double-quotes": { 2130 | "version": "2.0.0", 2131 | "resolved": "https://registry.npmjs.org/to-double-quotes/-/to-double-quotes-2.0.0.tgz", 2132 | "integrity": "sha1-qvIx1vqUiUn4GTAburRITYWI5Kc=", 2133 | "dev": true 2134 | }, 2135 | "to-iso-string": { 2136 | "version": "0.0.2", 2137 | "resolved": "https://registry.npmjs.org/to-iso-string/-/to-iso-string-0.0.2.tgz", 2138 | "integrity": "sha1-TcGeZk38y+Jb2NtQiwDG2hWCVdE=", 2139 | "dev": true 2140 | }, 2141 | "to-single-quotes": { 2142 | "version": "2.0.1", 2143 | "resolved": "https://registry.npmjs.org/to-single-quotes/-/to-single-quotes-2.0.1.tgz", 2144 | "integrity": "sha1-fMKRUfD18sQZRvEZ9ZMv5VQXASU=", 2145 | "dev": true 2146 | }, 2147 | "toidentifier": { 2148 | "version": "1.0.0", 2149 | "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", 2150 | "integrity": "sha1-fhvjRw8ed5SLxD2Uo8j013UrpVM=" 2151 | }, 2152 | "tsscmp": { 2153 | "version": "1.0.6", 2154 | "resolved": "https://registry.npmjs.org/tsscmp/-/tsscmp-1.0.6.tgz", 2155 | "integrity": "sha512-LxhtAkPDTkVCMQjt2h6eBVY28KCjikZqZfMcC15YBeNjkgUpdCfBu5HoiOTDu86v6smE8yOjyEktJ8hlbANHQA==", 2156 | "dev": true 2157 | }, 2158 | "type-detect": { 2159 | "version": "1.0.0", 2160 | "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-1.0.0.tgz", 2161 | "integrity": "sha1-diIXzAbbJY7EiQihKY6LlRIejqI=", 2162 | "dev": true 2163 | }, 2164 | "type-is": { 2165 | "version": "1.6.18", 2166 | "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", 2167 | "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", 2168 | "dev": true, 2169 | "requires": { 2170 | "media-typer": "0.3.0", 2171 | "mime-types": "~2.1.24" 2172 | } 2173 | }, 2174 | "underscore": { 2175 | "version": "1.6.0", 2176 | "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.6.0.tgz", 2177 | "integrity": "sha1-izixDKze9jM3uLJOT/htRa6lKag=", 2178 | "dev": true 2179 | }, 2180 | "unpipe": { 2181 | "version": "1.0.0", 2182 | "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", 2183 | "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" 2184 | }, 2185 | "util-deprecate": { 2186 | "version": "1.0.2", 2187 | "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", 2188 | "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", 2189 | "dev": true 2190 | }, 2191 | "utile": { 2192 | "version": "0.2.1", 2193 | "resolved": "https://registry.npmjs.org/utile/-/utile-0.2.1.tgz", 2194 | "integrity": "sha1-kwyI6ZCY1iIINMNWy9mncFItkNc=", 2195 | "dev": true, 2196 | "requires": { 2197 | "async": "~0.2.9", 2198 | "deep-equal": "*", 2199 | "i": "0.3.x", 2200 | "mkdirp": "0.x.x", 2201 | "ncp": "0.4.x", 2202 | "rimraf": "2.x.x" 2203 | } 2204 | }, 2205 | "utils-merge": { 2206 | "version": "1.0.1", 2207 | "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", 2208 | "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=", 2209 | "dev": true 2210 | }, 2211 | "uuid": { 2212 | "version": "2.0.3", 2213 | "resolved": "https://registry.npmjs.org/uuid/-/uuid-2.0.3.tgz", 2214 | "integrity": "sha1-Z+LoY3lyFVMN/zGOW/nc6/1Hsho=", 2215 | "dev": true 2216 | }, 2217 | "vary": { 2218 | "version": "1.1.2", 2219 | "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", 2220 | "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=", 2221 | "dev": true 2222 | }, 2223 | "vow": { 2224 | "version": "0.4.20", 2225 | "resolved": "https://registry.npmjs.org/vow/-/vow-0.4.20.tgz", 2226 | "integrity": "sha512-YYoSYXUYABqY08D/WrjcWJxJSErcILRRTQpcPyUc0SFfgIPKSUFzVt7u1HC3TXGJZM/qhsSjCLNQstxqf7asgQ==", 2227 | "dev": true 2228 | }, 2229 | "vow-fs": { 2230 | "version": "0.3.6", 2231 | "resolved": "https://registry.npmjs.org/vow-fs/-/vow-fs-0.3.6.tgz", 2232 | "integrity": "sha1-LUxZviLivyYY3fWXq0uqkjvnIA0=", 2233 | "dev": true, 2234 | "requires": { 2235 | "glob": "^7.0.5", 2236 | "uuid": "^2.0.2", 2237 | "vow": "^0.4.7", 2238 | "vow-queue": "^0.4.1" 2239 | }, 2240 | "dependencies": { 2241 | "glob": { 2242 | "version": "7.1.6", 2243 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", 2244 | "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", 2245 | "dev": true, 2246 | "requires": { 2247 | "fs.realpath": "^1.0.0", 2248 | "inflight": "^1.0.4", 2249 | "inherits": "2", 2250 | "minimatch": "^3.0.4", 2251 | "once": "^1.3.0", 2252 | "path-is-absolute": "^1.0.0" 2253 | } 2254 | } 2255 | } 2256 | }, 2257 | "vow-queue": { 2258 | "version": "0.4.3", 2259 | "resolved": "https://registry.npmjs.org/vow-queue/-/vow-queue-0.4.3.tgz", 2260 | "integrity": "sha512-/poAKDTFL3zYbeQg7cl4BGcfP4sGgXKrHnRFSKj97dteUFu8oyXMwIcdwu8NSx/RmPGIuYx1Bik/y5vU4H/VKw==", 2261 | "dev": true, 2262 | "requires": { 2263 | "vow": "^0.4.17" 2264 | } 2265 | }, 2266 | "which-boxed-primitive": { 2267 | "version": "1.0.2", 2268 | "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", 2269 | "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", 2270 | "dev": true, 2271 | "requires": { 2272 | "is-bigint": "^1.0.1", 2273 | "is-boolean-object": "^1.1.0", 2274 | "is-number-object": "^1.0.4", 2275 | "is-string": "^1.0.5", 2276 | "is-symbol": "^1.0.3" 2277 | } 2278 | }, 2279 | "which-collection": { 2280 | "version": "1.0.1", 2281 | "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.1.tgz", 2282 | "integrity": "sha512-W8xeTUwaln8i3K/cY1nGXzdnVZlidBcagyNFtBdD5kxnb4TvGKR7FfSIS3mYpwWS1QUCutfKz8IY8RjftB0+1A==", 2283 | "dev": true, 2284 | "requires": { 2285 | "is-map": "^2.0.1", 2286 | "is-set": "^2.0.1", 2287 | "is-weakmap": "^2.0.1", 2288 | "is-weakset": "^2.0.1" 2289 | } 2290 | }, 2291 | "which-typed-array": { 2292 | "version": "1.1.4", 2293 | "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.4.tgz", 2294 | "integrity": "sha512-49E0SpUe90cjpoc7BOJwyPHRqSAd12c10Qm2amdEZrJPCY2NDxaW01zHITrem+rnETY3dwrbH3UUrUwagfCYDA==", 2295 | "dev": true, 2296 | "requires": { 2297 | "available-typed-arrays": "^1.0.2", 2298 | "call-bind": "^1.0.0", 2299 | "es-abstract": "^1.18.0-next.1", 2300 | "foreach": "^2.0.5", 2301 | "function-bind": "^1.1.1", 2302 | "has-symbols": "^1.0.1", 2303 | "is-typed-array": "^1.1.3" 2304 | } 2305 | }, 2306 | "wrappy": { 2307 | "version": "1.0.2", 2308 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 2309 | "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", 2310 | "dev": true 2311 | }, 2312 | "xmlbuilder": { 2313 | "version": "3.1.0", 2314 | "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-3.1.0.tgz", 2315 | "integrity": "sha1-LIaIjy1OrehQ+jjKf3Ij9yCVFuE=", 2316 | "dev": true, 2317 | "requires": { 2318 | "lodash": "^3.5.0" 2319 | } 2320 | }, 2321 | "ylru": { 2322 | "version": "1.2.1", 2323 | "resolved": "https://registry.npmjs.org/ylru/-/ylru-1.2.1.tgz", 2324 | "integrity": "sha512-faQrqNMzcPCHGVC2aaOINk13K+aaBDUPjGWl0teOXywElLjyVAB6Oe2jj62jHYtwsU49jXhScYbvPENK+6zAvQ==", 2325 | "dev": true 2326 | } 2327 | } 2328 | } 2329 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "koa-better-http-proxy", 3 | "version": "0.2.10", 4 | "description": "http proxy middleware for koa", 5 | "engines": { 6 | "node": ">=10.0.0" 7 | }, 8 | "engineStrict": true, 9 | "main": "index.js", 10 | "scripts": { 11 | "test": "npm -s run mocha && npm run -s lint && npm run -s jscs", 12 | "test:debug": "mocha debug -R spec test --recursive", 13 | "mocha": "mocha -R spec test --recursive", 14 | "lint": "jshint index.js test/*.js test/**/*js lib/*js app/**/*js ", 15 | "jscs": "jscs index.js test/*.js test/**/*js lib/*js app/**/*js" 16 | }, 17 | "repository": { 18 | "type": "git", 19 | "url": "git://github.com/nsimmons/koa-better-http-proxy.git" 20 | }, 21 | "types": "types.d.ts", 22 | "keywords": [ 23 | "koa-better-http-proxy" 24 | ], 25 | "author": { 26 | "name": "nsimmons", 27 | "email": "nick.simmons.514@gmail.com" 28 | }, 29 | "license": "MIT", 30 | "bugs": { 31 | "url": "https://github.com/nsimmons/koa-better-http-proxy/issues" 32 | }, 33 | "devDependencies": { 34 | "body-parser": "^1.17.1", 35 | "chai": "^3.5.0", 36 | "cookie-parser": "^1.4.3", 37 | "express": "^4.15.2", 38 | "jscs": "^3.0.7", 39 | "jshint": "^2.9.4", 40 | "koa": "^2.2.0", 41 | "mocha": "^2.1.0", 42 | "supertest": "^1.2.0" 43 | }, 44 | "dependencies": { 45 | "es6-promise": "^3.3.1", 46 | "raw-body": "^2.2.0" 47 | }, 48 | "contributors": [] 49 | } 50 | -------------------------------------------------------------------------------- /test/agent.js: -------------------------------------------------------------------------------- 1 | var assert = require('assert'); 2 | var Koa = require('koa'); 3 | var agent = require('supertest').agent; 4 | var proxy = require('../'); 5 | var http = require('http'); 6 | 7 | describe('agent', function() { 8 | 'use strict'; 9 | 10 | this.timeout(10000); 11 | 12 | it('agent', function(done) { 13 | var httpAgent = new http.Agent(); 14 | var app = new Koa(); 15 | app.use(proxy('httpbin.org', { 16 | agent: httpAgent, 17 | proxyReqOptDecorator: function(reqOpts, ctx) { 18 | assert(reqOpts.agent, httpAgent); 19 | return ctx; 20 | } 21 | })); 22 | 23 | agent(app.callback()) 24 | .get('/user-agent') 25 | .end(function(err) { 26 | if (err) { return done(err); } 27 | done(); 28 | }); 29 | }); 30 | }); 31 | -------------------------------------------------------------------------------- /test/bodyEncoding.js: -------------------------------------------------------------------------------- 1 | var assert = require('assert'); 2 | var Koa = require('koa'); 3 | var agent = require('supertest').agent; 4 | var fs = require('fs'); 5 | var os = require('os'); 6 | var proxy = require('../'); 7 | var startProxyTarget = require('./support/proxyTarget'); 8 | 9 | startProxyTarget(8109, 1000); 10 | 11 | describe('body encoding', function() { 12 | 'use strict'; 13 | this.timeout(10000); 14 | 15 | var pngHex = '89504e470d0a1a0a0' + 16 | '000000d4948445200' + 17 | '00000100000001080' + 18 | '60000001f15c48900' + 19 | '00000a49444154789' + 20 | 'c6300010000050001' + 21 | '0d0a2db4000000004' + 22 | '9454e44ae426082'; 23 | var pngData = Buffer.from(pngHex, 'hex'); 24 | var largePngData = Buffer.from(pngHex.repeat(100000), 'hex'); // 6.7MB 25 | 26 | it('allow raw data', function(done) { 27 | var filename = os.tmpdir() + '/koa-better-http-proxy-test-' + (new Date()).getTime() + '-png-transparent.png'; 28 | var app = new Koa(); 29 | 30 | app.use(proxy('localhost:8109', { 31 | reqBodyEncoding: null, 32 | proxyReqBodyDecorator: function(bodyContent) { 33 | assert((Buffer.from(bodyContent).toString('hex')).indexOf(pngData.toString('hex')) >= 0, 34 | 'body should contain same data'); 35 | return bodyContent; 36 | } 37 | })); 38 | 39 | fs.writeFile(filename, pngData, function(err) { 40 | if (err) { throw err; } 41 | agent(app.callback()) 42 | .post('/post') 43 | .attach('image', filename) 44 | .end(function(err) { 45 | fs.unlink(filename, function() {}); 46 | // This test is both broken and I think unnecessary. 47 | // Its broken because http.bin no longer supports /post, but this test assertion is based on the old 48 | // httpbin behavior. 49 | // The assertion in the proxyReqOptDecorator above verifies the test title. 50 | //var response = Buffer.from(res.body.attachment.data).toString('base64'); 51 | //assert(response.indexOf(pngData.toString('base64')) >= 0, 'response should include original raw data'); 52 | done(err); 53 | }); 54 | }); 55 | 56 | }); 57 | 58 | // In this case, the package `raw-body` will print error stack which does not mater. 59 | it('should get 413 by posting file which is larger than 1mb without setting limit', function(done) { 60 | var filename = os.tmpdir() + '/koa-better-http-proxy-test-' + (new Date()).getTime() + '-png-transparent.png'; 61 | var app = new Koa(); 62 | app.use(proxy('localhost:8109', { 63 | })); 64 | fs.writeFile(filename, largePngData, function(err) { 65 | if (err) { throw err; } 66 | agent(app.callback()) 67 | .post('/post') 68 | .attach('image', filename) 69 | .expect(413) 70 | .end(function(err) { 71 | fs.unlink(filename, function() {}); 72 | assert(err === null); 73 | if (err) { return done(err); } 74 | done(); 75 | }); 76 | }); 77 | }); 78 | 79 | it('should not fail on large limit', function(done) { 80 | var filename = os.tmpdir() + '/koa-better-http-proxy-test-' + (new Date()).getTime() + '-png-transparent.png'; 81 | var app = new Koa(); 82 | app.use(proxy('localhost:8109', { 83 | // This case `parseReqBody` should not be set to false, 84 | limit: '20gb', 85 | })); 86 | fs.writeFile(filename, largePngData, function(err) { 87 | if (err) { throw err; } 88 | agent(app.callback()) 89 | .post('/post') 90 | .attach('image', filename) 91 | .expect(200) 92 | .end(function(err) { 93 | fs.unlink(filename, function() {}); 94 | assert(err === null); 95 | if (err) { return done(err); } 96 | done(); 97 | }); 98 | }); 99 | }); 100 | 101 | describe('when user sets parseReqBody', function() { 102 | 103 | it('should not parse body', function(done) { 104 | var filename = os.tmpdir() + '/koa-better-http-proxy-test-' + (new Date()).getTime() + '-png-transparent.png'; 105 | var app = new Koa(); 106 | app.use(proxy('localhost:8109', { 107 | parseReqBody: false, 108 | proxyReqBodyDecorator: function(bodyContent) { 109 | assert(!bodyContent, 'body content should not be parsed.'); 110 | return bodyContent; 111 | } 112 | })); 113 | 114 | fs.writeFile(filename, pngData, function(err) { 115 | if (err) { throw err; } 116 | agent(app.callback()) 117 | .post('/post') 118 | .attach('image', filename) 119 | .end(function(err) { 120 | fs.unlink(filename, function() {}); 121 | // This test is both broken and I think unnecessary. 122 | // Its broken because http.bin no longer supports /post, but this test assertion is based on the old 123 | // httpbin behavior. 124 | // The assertion in the proxyReqOptDecorator above verifies the test title. 125 | // var response = new Buffer(res.body.attachment.data).toString('base64'); 126 | // assert(response.indexOf(pngData.toString('base64')) >= 0, 'response should include original raw data'); 127 | done(err); 128 | }); 129 | }); 130 | }); 131 | 132 | }); 133 | 134 | describe('when user sets reqBodyEncoding', function() { 135 | 136 | it('should set the accepts-charset header', function(done) { 137 | var app = new Koa(); 138 | app.use(proxy('httpbin.org', { 139 | reqBodyEncoding: 'utf-16' 140 | })); 141 | agent(app.callback()) 142 | .get('/headers') 143 | .end(function(err, res) { 144 | if (err) { throw err; } 145 | assert.equal(res.body.headers['Accept-Charset'], 'utf-16'); 146 | done(err); 147 | }); 148 | }); 149 | 150 | }); 151 | 152 | }); 153 | -------------------------------------------------------------------------------- /test/connectTimeout.js: -------------------------------------------------------------------------------- 1 | var Koa = require('koa'); 2 | var agent = require('supertest').agent; 3 | var proxy = require('../'); 4 | var proxyTarget = require('./support/proxyTarget'); 5 | 6 | describe('honors connectTimeout option', function() { 7 | 'use strict'; 8 | 9 | var other, http; 10 | beforeEach(function() { 11 | http = new Koa(); 12 | other = proxyTarget(8080, 1000, [{ 13 | method: 'get', 14 | path: '/', 15 | fn: function(_, res) { res.sendStatus(200); } 16 | }]); 17 | }); 18 | 19 | afterEach(function() { 20 | other.close(); 21 | }); 22 | 23 | function assertSuccess(server, done) { 24 | agent(server.callback()) 25 | .get('/') 26 | .expect(200) 27 | .end(function(err) { 28 | if (err) { 29 | return done(err); 30 | } 31 | done(); 32 | }); 33 | } 34 | 35 | function assertConnectionTimeout(server, time, done) { 36 | agent(server.callback()) 37 | .get('/') 38 | .expect(504) 39 | .expect('X-Timout-Reason', 'koa-better-http-proxy timed out your request after ' + time + 'ms.') 40 | .end(function(err) { 41 | if (err) { 42 | return done(err); 43 | } 44 | done(); 45 | }); 46 | } 47 | 48 | describe('when connectTimeout option is set lower than server connect time', function() { 49 | it.skip('should fail with CONNECTION TIMEOUT', function(done) { 50 | http.use(proxy('http://127.0.0.0', { 51 | connectTimeout: 50, 52 | })); 53 | 54 | assertConnectionTimeout(http, 50, done); 55 | }); 56 | }); 57 | 58 | describe('when connectTimeout option is set higher than server connect time', function() { 59 | it('should succeed', function(done) { 60 | http.use(proxy('http://localhost:8080', { 61 | connectTimeout: 50, 62 | })); 63 | 64 | assertSuccess(http, done); 65 | }); 66 | }); 67 | 68 | describe('when timeout option is also used', function() { 69 | it('should fail with CONNECTION TIMEOUT when timeout is set lower than server response time', function(done) { 70 | http.use(proxy('http://localhost:8080', { 71 | connectTimeout: 100, 72 | timeout: 300, 73 | })); 74 | 75 | assertConnectionTimeout(http, 300, done); 76 | }); 77 | 78 | it.skip('should fail with CONNECTION TIMEOUT based on connectTimeout when a connection cannot be made', 79 | function(done) { 80 | http.use(proxy('http://127.0.0.0', { 81 | connectTimeout: 100, 82 | timeout: 300, 83 | })); 84 | 85 | assertConnectionTimeout(http, 100, done); 86 | } 87 | ); 88 | 89 | it('should succeed when timeout is higher than server response time', function(done) { 90 | http.use(proxy('http://localhost:8080', { 91 | connectTimeout: 100, 92 | timeout: 1200, 93 | })); 94 | 95 | assertSuccess(http, done); 96 | }); 97 | }); 98 | 99 | }); 100 | -------------------------------------------------------------------------------- /test/cookies.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var assert = require('assert'); 4 | var Koa = require('koa'); 5 | var agent = require('supertest').agent; 6 | var proxy = require('../'); 7 | 8 | var proxyTarget = require('../test/support/proxyTarget'); 9 | var proxyRouteFn = [{ 10 | method: 'get', 11 | path: '/cookieTest', 12 | fn: function(req, res) { 13 | Object.keys(req.cookies).forEach(function(key) { 14 | res.cookie(key, req.cookies[key]); 15 | }); 16 | res.sendStatus(200); 17 | } 18 | }]; 19 | 20 | describe('proxies cookie', function() { 21 | this.timeout(10000); 22 | 23 | var app; 24 | var proxyServer; 25 | 26 | beforeEach(function() { 27 | proxyServer = proxyTarget(12346, 100, proxyRouteFn); 28 | app = new Koa(); 29 | app.use(proxy('localhost:12346')); 30 | }); 31 | 32 | afterEach(function() { 33 | proxyServer.close(); 34 | }); 35 | 36 | it('set cookie', function(done) { 37 | agent(app.callback()) 38 | .get('/cookieTest') 39 | .set('Cookie', 'myApp-token=12345667') 40 | .end(function(err, res) { 41 | var cookiesMatch = res.headers['set-cookie'].filter(function(item) { 42 | return item.match(/myApp-token=12345667/); 43 | }); 44 | assert(cookiesMatch); 45 | done(err); 46 | }); 47 | }); 48 | }); 49 | -------------------------------------------------------------------------------- /test/decorateRequest.js: -------------------------------------------------------------------------------- 1 | var assert = require('assert'); 2 | var Koa = require('koa'); 3 | var agent = require('supertest').agent; 4 | var proxy = require('../'); 5 | 6 | describe('proxyReqOptDecorator', function() { 7 | 'use strict'; 8 | 9 | this.timeout(10000); 10 | 11 | describe('Supports Promise and non-Promise forms', function() { 12 | 13 | describe('when proxyReqOptDecorator is a simple function (non Promise)', function() { 14 | it('should mutate the proxied request', function(done) { 15 | var app = new Koa(); 16 | app.use(proxy('httpbin.org', { 17 | proxyReqOptDecorator: function(reqOpt) { 18 | reqOpt.headers['user-agent'] = 'test user agent'; 19 | return reqOpt; 20 | } 21 | })); 22 | 23 | agent(app.callback()) 24 | .get('/user-agent') 25 | .end(function(err, res) { 26 | if (err) { return done(err); } 27 | assert.equal(res.body['user-agent'], 'test user agent'); 28 | done(); 29 | }); 30 | }); 31 | }); 32 | 33 | describe('when proxyReqOptDecorator is a Promise', function() { 34 | it('should mutate the proxied request', function(done) { 35 | var app = new Koa(); 36 | app.use(proxy('httpbin.org', { 37 | proxyReqOptDecorator: function(reqOpt) { 38 | return new Promise(function(resolve) { 39 | reqOpt.headers['user-agent'] = 'test user agent'; 40 | resolve(reqOpt); 41 | }); 42 | } 43 | })); 44 | 45 | agent(app.callback()) 46 | .get('/user-agent') 47 | .end(function(err, res) { 48 | if (err) { return done(err); } 49 | assert.equal(res.body['user-agent'], 'test user agent'); 50 | done(); 51 | }); 52 | }); 53 | }); 54 | }); 55 | 56 | describe('proxyReqOptDecorator has access to the source request\'s data', function() { 57 | it('should have access to ip', function(done) { 58 | var app = new Koa(); 59 | app.use(proxy('httpbin.org', { 60 | proxyReqOptDecorator: function(reqOpts, ctx) { 61 | assert(ctx.ip); 62 | return reqOpts; 63 | } 64 | })); 65 | 66 | agent(app.callback()) 67 | .get('/') 68 | .end(function(err) { 69 | if (err) { return done(err); } 70 | done(); 71 | }); 72 | 73 | }); 74 | }); 75 | }); 76 | -------------------------------------------------------------------------------- /test/headers.js: -------------------------------------------------------------------------------- 1 | var assert = require('assert'); 2 | var Koa = require('koa'); 3 | var agent = require('supertest').agent; 4 | var proxy = require('../'); 5 | 6 | describe('proxies headers', function() { 7 | 'use strict'; 8 | this.timeout(10000); 9 | 10 | var http; 11 | 12 | beforeEach(function() { 13 | http = new Koa(); 14 | http.use(proxy('http://httpbin.org', { 15 | headers: { 16 | 'X-Current-president': 'taft' 17 | } 18 | })); 19 | }); 20 | 21 | it('does not include connection header by default', function(done) { 22 | var app = new Koa(); 23 | app.use(proxy('httpbin.org', { 24 | proxyReqOptDecorator: function(reqOpts, ctx) { 25 | try { 26 | assert(!reqOpts.headers.connection); 27 | } catch (err) { 28 | done(err); 29 | } 30 | return ctx; 31 | } 32 | })); 33 | 34 | agent(app.callback()) 35 | .get('/user-agent') 36 | .end(function(err) { 37 | if (err) { return done(err); } 38 | done(); 39 | }); 40 | }); 41 | 42 | it('passed as options', function(done) { 43 | agent(http.callback()) 44 | .get('/headers') 45 | .expect(200) 46 | .end(function(err, res) { 47 | if (err) { return done(err); } 48 | assert(res.body.headers['X-Current-President'] === 'taft'); 49 | done(); 50 | }); 51 | }); 52 | 53 | it('passed as on request', function(done) { 54 | agent(http.callback()) 55 | .get('/headers') 56 | .set('X-Powerererer', 'XTYORG') 57 | .expect(200) 58 | .end(function(err, res) { 59 | if (err) { return done(err); } 60 | assert(res.body.headers['X-Powerererer']); 61 | done(); 62 | }); 63 | }); 64 | 65 | }); 66 | -------------------------------------------------------------------------------- /test/host.js: -------------------------------------------------------------------------------- 1 | var Koa = require('koa'); 2 | var agent = require('supertest').agent; 3 | var proxy = require('../'); 4 | 5 | describe('host can be a dynamic function', function() { 6 | 'use strict'; 7 | 8 | this.timeout(10000); 9 | 10 | var app = new Koa(); 11 | var firstProxyApp = new Koa(); 12 | var secondProxyApp = new Koa(); 13 | var firstPort = 10001; 14 | var secondPort = 10002; 15 | 16 | app.use(proxy(function(ctx) { 17 | return 'localhost:' + ctx.url.replace('/proxy/', ''); 18 | })); 19 | 20 | firstProxyApp.use(function(ctx) { 21 | ctx.status = 204; 22 | }); 23 | firstProxyApp.listen(firstPort); 24 | 25 | secondProxyApp.use(function(ctx) { 26 | ctx.status = 200; 27 | }); 28 | secondProxyApp.listen(secondPort); 29 | 30 | it('can proxy with session value', function(done) { 31 | agent(app.callback()) 32 | .get('/proxy/' + firstPort) 33 | .expect(204) 34 | .end(function(err) { 35 | if (err) { 36 | return done(err); 37 | } 38 | agent(app.callback()) 39 | .get('/proxy/' + secondPort) 40 | .expect(200, done); 41 | }); 42 | }); 43 | }); 44 | -------------------------------------------------------------------------------- /test/https.js: -------------------------------------------------------------------------------- 1 | var assert = require('assert'); 2 | var Koa = require('koa'); 3 | var agent = require('supertest').agent; 4 | var proxy = require('../'); 5 | 6 | describe('proxies https', function() { 7 | 'use strict'; 8 | 9 | this.timeout(10000); 10 | 11 | var app; 12 | 13 | beforeEach(function() { 14 | app = new Koa(); 15 | }); 16 | 17 | function assertSecureRequest(app, done) { 18 | agent(app.callback()) 19 | .get('/get?show_env=1') 20 | .end(function(err, res) { 21 | if (err) { return done(err); } 22 | assert(res.body.headers['X-Forwarded-Port'] === '443', 'Expects forwarded 443 Port'); 23 | assert(res.body.headers['X-Forwarded-Proto'] === 'https', 'Expects forwarded protocol to be https'); 24 | done(); 25 | }); 26 | } 27 | 28 | describe('when host is a String', function() { 29 | describe('and includes "https" as protocol', function() { 30 | it('proxys via https', function(done) { 31 | app.use(proxy('https://httpbin.org')); 32 | assertSecureRequest(app, done); 33 | }); 34 | }); 35 | describe('option https is set to "true"', function() { 36 | it('proxys via https', function(done) { 37 | app.use(proxy('http://httpbin.org', {https: true})); 38 | assertSecureRequest(app, done); 39 | }); 40 | }); 41 | }); 42 | 43 | describe('when host is a Function', function() { 44 | describe('returned value includes "https" as protocol', function() { 45 | it('proxys via https', function(done) { 46 | app.use(proxy(function() { return 'https://httpbin.org'; })); 47 | assertSecureRequest(app, done); 48 | }); 49 | }); 50 | describe('option https is set to "true"', function() { 51 | it('proxys via https', function(done) { 52 | app.use(proxy(function() { return 'http://httpbin.org'; }, {https: true})); 53 | assertSecureRequest(app, done); 54 | }); 55 | }); 56 | }); 57 | 58 | }); 59 | -------------------------------------------------------------------------------- /test/integration/proxyReqPathResolver.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var assert = require('assert'); 4 | var Koa = require('koa'); 5 | var agent = require('supertest').agent; 6 | var http = require('http'); 7 | var proxy = require('../../'); 8 | var proxyTarget = require('../../test/support/proxyTarget'); 9 | 10 | describe('resolveProxyReqPath', function() { 11 | var server; 12 | 13 | this.timeout(10000); 14 | 15 | before(function() { 16 | var handlers = [{ 17 | method: 'get', 18 | path: '/working', 19 | fn: function(req, res) { 20 | res.sendStatus(200); 21 | } 22 | }]; 23 | 24 | server = proxyTarget(12345, 100, handlers); 25 | }); 26 | 27 | after(function() { 28 | server.close(); 29 | }); 30 | 31 | it('does not pollute global proxy headers with individual request headers', function(done) { 32 | var app = new Koa(); 33 | var opts = {headers: {}}; 34 | app.use(proxy('localhost:12345', opts)); 35 | 36 | agent(app.callback()) 37 | .get('/working') 38 | .end(function() { 39 | assert.deepStrictEqual(opts.headers, {}); 40 | done(); 41 | }); 42 | }); 43 | 44 | describe('when author uses option proxyReqPathResolver', function() { 45 | it('the proxy request path is the result of the function', function(done) { 46 | var app = new Koa(); 47 | var opts = {}; 48 | opts.proxyReqPathResolver = function() { return '/working'; }; 49 | app.use(proxy('localhost:12345', opts)); 50 | 51 | agent(app.callback()) 52 | .get('/failing') 53 | .expect(200) 54 | .end(done); 55 | }); 56 | 57 | it('the proxyReqPathResolver method has access to request object', function(done) { 58 | var app = new Koa(); 59 | var opts = {}; 60 | opts.proxyReqPathResolver = function(ctx) { 61 | assert.ok(ctx.req instanceof http.IncomingMessage); 62 | return '/working'; 63 | }; 64 | app.use(proxy('localhost:12345', opts)); 65 | 66 | agent(app.callback()) 67 | .get('/foobar') 68 | .expect(200) 69 | .end(done); 70 | }); 71 | 72 | }); 73 | }); 74 | -------------------------------------------------------------------------------- /test/port.js: -------------------------------------------------------------------------------- 1 | var assert = require('assert'); 2 | var Koa = require('koa'); 3 | var agent = require('supertest').agent; 4 | var proxy = require('../'); 5 | 6 | function proxyTarget(port) { 7 | 'use strict'; 8 | 9 | var other = new Koa(); 10 | other.use(function(ctx) { 11 | ctx.status = 200; 12 | ctx.body = 'Success'; 13 | }); 14 | return other.listen(port); 15 | } 16 | 17 | describe('proxies to requested port', function() { 18 | 'use strict'; 19 | 20 | var other, http; 21 | beforeEach(function() { 22 | http = new Koa(); 23 | other = proxyTarget(8080); 24 | }); 25 | 26 | afterEach(function() { 27 | other.close(); 28 | }); 29 | 30 | function assertSuccess(server, done) { 31 | agent(http.callback()) 32 | .get('/') 33 | .expect(200) 34 | .end(function(err, res) { 35 | if (err) { return done(err); } 36 | assert(res.text === 'Success'); 37 | done(); 38 | }); 39 | } 40 | 41 | describe('when host is a String', function() { 42 | it('when passed as an option', function(done) { 43 | http.use(proxy('http://localhost', { 44 | port: 8080 45 | })); 46 | assertSuccess(http, done); 47 | }); 48 | 49 | it('when passed on the host string', function(done) { 50 | http.use(proxy('http://localhost:8080')); 51 | assertSuccess(http, done); 52 | }); 53 | 54 | }); 55 | 56 | describe('when host is a function', function() { 57 | it('and port is on the String returned', function(done) { 58 | http.use(proxy( 59 | function() { return 'http://localhost:8080'; } 60 | )); 61 | assertSuccess(http, done); 62 | }); 63 | 64 | it('and port passed as an option', function(done) { 65 | http.use(proxy( 66 | function() { return 'http://localhost'; }, 67 | { port: 8080 } 68 | )); 69 | assertSuccess(http, done); 70 | }); 71 | }); 72 | 73 | }); 74 | -------------------------------------------------------------------------------- /test/session.js: -------------------------------------------------------------------------------- 1 | var assert = require('assert'); 2 | var Koa = require('koa'); 3 | var agent = require('supertest').agent; 4 | var proxy = require('../'); 5 | 6 | describe('preserveReqSession', function() { 7 | 'use strict'; 8 | 9 | this.timeout(10000); 10 | 11 | it('preserveReqSession', function(done) { 12 | var app = new Koa(); 13 | app.use(function(ctx, next) { 14 | ctx.session = 'hola'; 15 | return Promise.resolve(null).then(next); 16 | }); 17 | app.use(proxy('httpbin.org', { 18 | preserveReqSession: true, 19 | proxyReqOptDecorator: function(reqOpts, ctx) { 20 | assert(reqOpts.session, 'hola'); 21 | return ctx; 22 | } 23 | })); 24 | 25 | agent(app.callback()) 26 | .get('/user-agent') 27 | .end(function(err) { 28 | if (err) { return done(err); } 29 | done(); 30 | }); 31 | }); 32 | }); 33 | -------------------------------------------------------------------------------- /test/status.js: -------------------------------------------------------------------------------- 1 | var Koa = require('koa'); 2 | var agent = require('supertest').agent; 3 | var proxy = require('../'); 4 | var mockEndpoint = require('../lib/mockHTTP.js'); 5 | 6 | describe('proxies status code', function() { 7 | 'use strict'; 8 | 9 | var proxyServer = new Koa(); 10 | var port = 21239; 11 | var proxiedEndpoint = 'http://localhost:' + port; 12 | var server; 13 | 14 | proxyServer.use(proxy(proxiedEndpoint)); 15 | 16 | beforeEach(function() { 17 | server = mockEndpoint.listen(21239); 18 | }); 19 | 20 | afterEach(function() { 21 | server.close(); 22 | }); 23 | 24 | [304, 404, 200, 401, 500].forEach(function(status) { 25 | it('on ' + status, function(done) { 26 | agent(proxyServer.callback()) 27 | .get('/status/' + status) 28 | .expect(status, done); 29 | }); 30 | }); 31 | }); 32 | -------------------------------------------------------------------------------- /test/support/proxyTarget.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var express = require('express'); 4 | var bodyParser = require('body-parser'); 5 | var cookieParser = require('cookie-parser'); 6 | 7 | function proxyTarget(port, timeout, handlers) { 8 | var target = express(); 9 | 10 | timeout = timeout || 100; 11 | 12 | // parse application/x-www-form-urlencoded 13 | target.use(bodyParser.urlencoded({ extended: false })); 14 | 15 | // parse application/json 16 | target.use(bodyParser.json()); 17 | target.use(cookieParser()); 18 | 19 | target.use(function(req, res, next) { 20 | setTimeout(function() { 21 | next(); 22 | },timeout); 23 | }); 24 | 25 | if (handlers) { 26 | handlers.forEach(function(handler) { 27 | target[handler.method](handler.path, handler.fn); 28 | }); 29 | } 30 | 31 | target.post('/post', function(req, res) { 32 | req.pipe(res); 33 | }); 34 | 35 | target.use(function(err, req, res, next) { 36 | res.send(err); 37 | next(); 38 | }); 39 | 40 | target.use(function(req, res) { 41 | res.sendStatus(404); 42 | }); 43 | 44 | return target.listen(port); 45 | } 46 | 47 | module.exports = proxyTarget; 48 | -------------------------------------------------------------------------------- /test/timeout.js: -------------------------------------------------------------------------------- 1 | var Koa = require('koa'); 2 | var agent = require('supertest').agent; 3 | var proxy = require('../'); 4 | var proxyTarget = require('./support/proxyTarget'); 5 | 6 | describe('honors timeout option', function() { 7 | 'use strict'; 8 | 9 | var other, http; 10 | beforeEach(function() { 11 | http = new Koa(); 12 | other = proxyTarget(8080, 1000, [{ 13 | method: 'get', 14 | path: '/', 15 | fn: function(req, res) { res.sendStatus(200); } 16 | }]); 17 | }); 18 | 19 | afterEach(function() { 20 | other.close(); 21 | }); 22 | 23 | function assertSuccess(server, done) { 24 | agent(http.callback()) 25 | .get('/') 26 | .expect(200) 27 | .end(function(err) { 28 | if (err) { 29 | return done(err); 30 | } 31 | done(); 32 | }); 33 | } 34 | 35 | function assertConnectionTimeout(server, done) { 36 | agent(http.callback()) 37 | .get('/') 38 | .expect(504) 39 | .expect('X-Timout-Reason', 'koa-better-http-proxy timed out your request after 100ms.') 40 | .end(function(err) { 41 | if (err) { 42 | return done(err); 43 | } 44 | done(); 45 | }); 46 | } 47 | 48 | describe('when timeout option is set lower than server response time', function() { 49 | it('should fail with CONNECTION TIMEOUT', function(done) { 50 | 51 | http.use(proxy('http://localhost:8080', { 52 | timeout: 100, 53 | })); 54 | 55 | assertConnectionTimeout(http, done); 56 | }); 57 | }); 58 | 59 | describe('when timeout option is set higher than server response time', function() { 60 | it('should succeed', function(done) { 61 | 62 | http.use(proxy('http://localhost:8080', { 63 | timeout: 1200, 64 | })); 65 | 66 | assertSuccess(http, done); 67 | }); 68 | }); 69 | 70 | }); 71 | -------------------------------------------------------------------------------- /test/unit/resolveProxyReqPath.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var assert = require('assert'); 4 | var ScopeContainer = require('../../lib/scopeContainer'); 5 | var resolveProxyReqPath = require('../../app/steps/resolveProxyReqPath'); 6 | var expect = require('chai').expect; 7 | 8 | 9 | describe('resolveProxyReqPath', function() { 10 | var container; 11 | 12 | beforeEach(function() { 13 | container = new ScopeContainer(); 14 | }); 15 | 16 | var tests = [ 17 | { 18 | resolverType: 'undefined', 19 | resolverFn: undefined, 20 | data: [ 21 | { url: 'http://localhost:12345', parsed: '/' }, 22 | { url: 'http://g.com/123?45=67', parsed: '/123?45=67' } 23 | ] 24 | }, 25 | { 26 | resolverType: 'a syncronous function', 27 | resolverFn: function() { return 'the craziest thing'; }, 28 | data: [ 29 | { url: 'http://localhost:12345', parsed: 'the craziest thing' }, 30 | { url: 'http://g.com/123?45=67', parsed: 'the craziest thing' } 31 | ] 32 | }, 33 | { 34 | resolverType: 'a Promise', 35 | resolverFn: function() { 36 | return new Promise(function(resolve) { 37 | resolve('the craziest think'); 38 | }); 39 | }, 40 | data: [ 41 | { url: 'http://localhost:12345', parsed: 'the craziest think' }, 42 | { url: 'http://g.com/123?45=67', parsed: 'the craziest think' } 43 | ] 44 | } 45 | ]; 46 | 47 | describe('when proxyReqPathResolver', function() { 48 | 49 | tests.forEach(function(test) { 50 | describe('is ' + test.resolverType, function() { 51 | describe('it returns a promise which resolves a container with expected url', function() { 52 | test.data.forEach(function(data) { 53 | it(data.url, function(done) { 54 | container.user.ctx = { url: data.url }; 55 | container.options.proxyReqPathResolver = test.resolverFn; 56 | var r = resolveProxyReqPath(container); 57 | 58 | assert(r instanceof Promise, 'Expect resolver to return a thennable'); 59 | 60 | r.then(function(container) { 61 | var response; 62 | try { 63 | response = container.proxy.reqBuilder.path; 64 | if (!response) { 65 | throw new Error('reqBuilder.url is undefined'); 66 | } 67 | } catch (e) { 68 | done(e); 69 | } 70 | expect(response).to.equal(data.parsed); 71 | done(); 72 | }); 73 | }); 74 | }); 75 | }); 76 | }); 77 | }); 78 | 79 | }); 80 | }); 81 | -------------------------------------------------------------------------------- /test/urlParsing.js: -------------------------------------------------------------------------------- 1 | var assert = require('assert'); 2 | var Koa = require('koa'); 3 | var agent = require('supertest').agent; 4 | var proxy = require('../'); 5 | 6 | describe('url parsing', function() { 7 | 'use strict'; 8 | 9 | this.timeout(10000); 10 | 11 | it('can parse a url with a port', function(done) { 12 | var app = new Koa(); 13 | app.use(proxy('http://httpbin.org:80')); 14 | agent(app.callback()) 15 | .get('/') 16 | .end(function(err) { 17 | if (err) { return done(err); } 18 | assert(true); 19 | done(); 20 | }); 21 | }); 22 | 23 | it('does not throw `Uncaught RangeError` if you have both a port and a trailing slash', function(done) { 24 | var app = new Koa(); 25 | app.use(proxy('http://httpbin.org:80/')); 26 | agent(app.callback()) 27 | .get('/') 28 | .end(function(err) { 29 | if (err) { return done(err); } 30 | assert(true); 31 | done(); 32 | }); 33 | }); 34 | }); 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /test/userResDecorator.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var assert = require('assert'); 4 | var Koa = require('koa'); 5 | var agent = require('supertest').agent; 6 | var proxy = require('../'); 7 | 8 | function proxyTarget(port) { 9 | var other = new Koa(); 10 | other.use(function(ctx, next) { 11 | if (ctx.request.url !== '/json') { 12 | return next(); 13 | } 14 | ctx.set('content-type', 'app,lication/json'); 15 | ctx.body = JSON.stringify({foo: 'bar'}); 16 | }); 17 | other.use(function(ctx) { 18 | ctx.status = 200; 19 | ctx.set('x-wombat-alliance', 'mammels'); 20 | ctx.set('x-custom-header', 'something'); 21 | ctx.body = 'Success'; 22 | }); 23 | return other.listen(port); 24 | } 25 | 26 | describe('userResDecorator', function() { 27 | var other; 28 | 29 | beforeEach(function() { 30 | other = proxyTarget(8080); 31 | }); 32 | 33 | afterEach(function() { 34 | other.close(); 35 | }); 36 | 37 | it('has access to original response', function(done) { 38 | var app = new Koa(); 39 | app.use(proxy('http://localhost', { 40 | port: 8080, 41 | userResDecorator: function(proxyRes, proxyResData) { 42 | assert(proxyRes.connection); 43 | assert(proxyRes.socket); 44 | assert(proxyRes.headers); 45 | assert(proxyRes.headers['content-type']); 46 | return proxyResData; 47 | } 48 | })); 49 | 50 | agent(app.callback()).get('/').end(done); 51 | }); 52 | 53 | it('works with promises', function(done) { 54 | var app = new Koa(); 55 | app.use(proxy('http://localhost', { 56 | port: 8080, 57 | userResDecorator: function(proxyRes, proxyResData) { 58 | return new Promise(function(resolve) { 59 | proxyResData.funkyMessage = 'oi io oo ii'; 60 | setTimeout(function() { 61 | resolve(proxyResData); 62 | }, 200); 63 | }); 64 | } 65 | })); 66 | 67 | agent(app.callback()) 68 | .get('/') 69 | .end(function(err, res) { 70 | if (err) { return done(err); } 71 | 72 | assert(res.body.funkyMessage = 'oi io oo ii'); 73 | done(); 74 | }); 75 | 76 | }); 77 | 78 | it('can modify the response data', function(done) { 79 | var app = new Koa(); 80 | app.use(proxy('http://localhost', { 81 | port: 8080, 82 | userResDecorator: function(proxyRes, proxyResData) { 83 | proxyResData = JSON.parse(proxyResData.toString('utf8')); 84 | proxyResData.intercepted = true; 85 | return JSON.stringify(proxyResData); 86 | } 87 | })); 88 | 89 | agent(app.callback()) 90 | .get('/json') 91 | .end(function(err, res) { 92 | if (err) { return done(err); } 93 | 94 | assert(res.body.intercepted); 95 | done(); 96 | }); 97 | }); 98 | 99 | it('can filter response headers', function(done) { 100 | var proxiedApp = new Koa(); 101 | var app = new Koa(); 102 | var p1Done, p2Done; 103 | var p1 = new Promise(function(resolve) { p1Done = resolve; }); 104 | var p2 = new Promise(function(resolve) { p2Done = resolve; }); 105 | app.use(proxy('http://localhost', { 106 | port: 8080 107 | })); 108 | proxiedApp.use(proxy('http://localhost', { 109 | port: 8080, 110 | strippedHeaders: ['x-wombat-alliance', 'x-custom-header'] 111 | })); 112 | 113 | agent(app.callback()) 114 | .get('/') 115 | .end(function(err, res) { 116 | if (err) { return done(err); } 117 | assert(typeof res.headers['x-custom-header'] === 'string'); 118 | assert(typeof res.headers['x-wombat-alliance'] === 'string'); 119 | p1Done(); 120 | }); 121 | 122 | agent(proxiedApp.callback()) 123 | .get('/') 124 | .end(function(err, res) { 125 | if (err) { return done(err); } 126 | assert(typeof res.headers['x-custom-header'] !== 'string'); 127 | assert(typeof res.headers['x-wombat-alliance'] !== 'string'); 128 | p2Done(); 129 | }); 130 | 131 | Promise.all([p1, p2]).then(function() { done(); }); 132 | }); 133 | 134 | it('can modify the response headers', function(done) { 135 | var app = new Koa(); 136 | app.use(proxy('http://localhost', { 137 | port: 8080, 138 | userResHeadersDecorator: function(headers) { 139 | var newHeaders = Object.keys(headers) 140 | .reduce(function(result, key) { 141 | result[key] = headers[key]; 142 | return result; 143 | }, {}); 144 | newHeaders['x-transaction-id'] = '12345'; 145 | newHeaders['x-entity-id'] = 'abcdef'; 146 | return newHeaders; 147 | } 148 | })); 149 | 150 | agent(app.callback()) 151 | .get('/ip') 152 | .end(function(err, res) { 153 | if (err) { return done(err); } 154 | assert(res.headers['x-transaction-id'] === '12345'); 155 | assert(res.headers['x-entity-id'] === 'abcdef'); 156 | done(); 157 | }); 158 | }); 159 | 160 | it('can mutuate an html response', function(done) { 161 | var app = new Koa(); 162 | app.use(proxy('http://localhost', { 163 | port: 8080, 164 | userResDecorator: function(rsp, data) { 165 | data = data.toString().replace('Success', 'Hey'); 166 | assert(data !== ''); 167 | return data; 168 | } 169 | })); 170 | 171 | agent(app.callback()) 172 | .get('/') 173 | .end(function(err, res) { 174 | if (err) { return done(err); } 175 | assert(res.text.indexOf('Hey') > -1); 176 | done(); 177 | }); 178 | }); 179 | 180 | it('can change the location of a redirect', function(done) { 181 | 182 | function redirectingServer(port, origin) { 183 | var app = new Koa(); 184 | app.use(function(ctx) { 185 | ctx.redirect(origin + '/proxied/redirect/url'); 186 | }); 187 | return app.listen(port); 188 | } 189 | 190 | var redirectingServerPort = 8012; 191 | var redirectingServerOrigin = ['http://localhost', redirectingServerPort].join(':'); 192 | 193 | var server = redirectingServer(redirectingServerPort, redirectingServerOrigin); 194 | 195 | var proxyApp = new Koa(); 196 | var preferredPort = 3000; 197 | 198 | proxyApp.use(proxy(redirectingServerOrigin, { 199 | userResDecorator: function(rsp, data, ctx) { 200 | var proxyReturnedLocation = ctx.response.headers.location; 201 | ctx.set('location', proxyReturnedLocation.replace(redirectingServerPort, preferredPort)); 202 | return data; 203 | } 204 | })); 205 | 206 | agent(proxyApp.callback()) 207 | .get('/') 208 | .expect(function(res) { 209 | res.headers.location.match(/localhost:3000/); 210 | }) 211 | .end(function() { 212 | server.close(); 213 | done(); 214 | }); 215 | }); 216 | }); 217 | 218 | 219 | describe('test userResDecorator on html response from github',function() { 220 | /* 221 | Github provided a unique situation where the encoding was different than 222 | utf-8 when we didn't explicitly ask for utf-8. This test helped sort out 223 | the issue, and even though its a little too on the nose for a specific 224 | case, it seems worth keeping around to ensure we don't regress on this 225 | issue. 226 | */ 227 | 228 | it('is able to read and manipulate the response', function(done) { 229 | this.timeout(15000); // give it some extra time to get response 230 | var app = new Koa(); 231 | app.use(proxy('https://github.com/villadora/express-http-proxy', { 232 | userResDecorator: function(targetResponse, data) { 233 | data = data.toString().replace('DOCTYPE','WINNING'); 234 | assert(data !== ''); 235 | return data; 236 | } 237 | })); 238 | 239 | agent(app.callback()) 240 | .get('/html') 241 | .end(function(err, res) { 242 | if (err) { return done(err); } 243 | assert(res.text.indexOf('WINNING') > -1); 244 | done(); 245 | }); 246 | 247 | }); 248 | }); 249 | 250 | -------------------------------------------------------------------------------- /test/verbs.js: -------------------------------------------------------------------------------- 1 | var assert = require('assert'); 2 | var Koa = require('koa'); 3 | var agent = require('supertest').agent; 4 | var proxy = require('../'); 5 | 6 | describe('http verbs', function() { 7 | 'use strict'; 8 | this.timeout(10000); 9 | 10 | var app; 11 | 12 | beforeEach(function() { 13 | app = new Koa(); 14 | app.use(proxy('httpbin.org')); 15 | }); 16 | 17 | it('test proxy get', function(done) { 18 | agent(app.callback()) 19 | .get('/get') 20 | .end(function(err, res) { 21 | if (err) { return done(err); } 22 | assert(/node-superagent/.test(res.body.headers['User-Agent'])); 23 | assert.equal(res.body.url, 'http://httpbin.org/get'); 24 | done(err); 25 | }); 26 | }); 27 | 28 | it('test proxy post', function(done) { 29 | agent(app.callback()) 30 | .post('/post') 31 | .send({ 32 | mypost: 'hello' 33 | }) 34 | .end(function(err, res) { 35 | assert.equal(res.body.data, '{"mypost":"hello"}'); 36 | done(err); 37 | }); 38 | }); 39 | 40 | it('test proxy put', function(done) { 41 | agent(app.callback()) 42 | .put('/put') 43 | .send({ 44 | mypost: 'hello' 45 | }) 46 | .end(function(err, res) { 47 | assert.equal(res.body.data, '{"mypost":"hello"}'); 48 | done(err); 49 | }); 50 | }); 51 | 52 | it('test proxy patch', function(done) { 53 | agent(app.callback()) 54 | .patch('/patch') 55 | .send({ 56 | mypost: 'hello' 57 | }) 58 | .end(function(err, res) { 59 | assert.equal(res.body.data, '{"mypost":"hello"}'); 60 | done(err); 61 | }); 62 | }); 63 | 64 | it('test proxy delete', function(done) { 65 | agent(app.callback()) 66 | .del('/delete') 67 | .send({ 68 | mypost: 'hello' 69 | }) 70 | .end(function(err, res) { 71 | assert.equal(res.body.data, '{"mypost":"hello"}'); 72 | done(err); 73 | }); 74 | }); 75 | }); 76 | -------------------------------------------------------------------------------- /types.d.ts: -------------------------------------------------------------------------------- 1 | import * as koa from 'koa'; 2 | import * as http from 'http'; 3 | 4 | declare function koaHttpProxy(host: string, options: koaHttpProxy.IOptions): koa.Middleware; 5 | 6 | declare namespace koaHttpProxy { 7 | export interface IOptions { 8 | agent?: http.Agent, 9 | headers?: { [key: string]: any }, 10 | strippedHeaders?: [string], 11 | https?: boolean, 12 | limit?: string, 13 | parseReqBody?: boolean, 14 | port?: number, 15 | preserveHostHdr?: boolean, 16 | preserveReqSession?: boolean, 17 | reqAsBuffer?: boolean, 18 | reqBodyEncoding?: string | null, 19 | connectTimeout?: number, 20 | timeout?: number, 21 | filter?(ctx: koa.Context): boolean, 22 | proxyReqBodyDecorator?(bodyContent: string, ctx: koa.Context): string | Promise, 23 | proxyReqOptDecorator?(proxyReqOpts: IRequestOption, ctx: koa.Context): IRequestOption | Promise, 24 | proxyReqPathResolver?(ctx: koa.Context): string | Promise, 25 | userResDecorator?(proxyRes: http.IncomingMessage, proxyResData: string | Buffer, ctx: koa.Context): string | Buffer | Promise | Promise, 26 | userResHeadersDecorator?(headers: {[key: string]: string}): Promise<{[key: string]: string}> | {[key: string]: string}, 27 | } 28 | 29 | export interface IRequestOption { 30 | hostname: string, 31 | port: number, 32 | headers: { [key: string]: any }, 33 | method: string, 34 | path: string, 35 | bodyContent: string | Buffer, 36 | params: any, 37 | } 38 | } 39 | 40 | export = koaHttpProxy 41 | --------------------------------------------------------------------------------