├── .dockerignore ├── .eslintrc ├── .gitignore ├── .npmignore ├── .travis.yml ├── Dockerfile ├── README.md ├── bin └── npm-proxy-cache ├── cert ├── README.md ├── dummy.crt ├── dummy.csr └── dummy.key ├── docker-compose.yml ├── index.js ├── lib ├── cache.js ├── logger.js └── proxy.js ├── package-lock.json ├── package.json ├── scripts ├── lint ├── release └── test └── test ├── cache.js ├── dummy.data └── proxy.js /.dockerignore: -------------------------------------------------------------------------------- 1 | .git 2 | node_modules 3 | *.md 4 | test 5 | .git* 6 | cache 7 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | # borrowed from https://github.com/nodejs/node/blob/master/.eslintrc 2 | env: 3 | node: true 4 | mocha: true 5 | es6: true 6 | 7 | rules: 8 | # Possible Errors 9 | # http://eslint.org/docs/rules/#possible-errors 10 | comma-dangle: [2, "only-multiline"] 11 | no-control-regex: 2 12 | no-debugger: 2 13 | no-dupe-args: 2 14 | no-dupe-keys: 2 15 | no-duplicate-case: 2 16 | no-empty-character-class: 2 17 | no-ex-assign: 2 18 | no-extra-boolean-cast: 2 19 | no-extra-parens: [2, "functions"] 20 | no-extra-semi: 2 21 | no-func-assign: 2 22 | no-invalid-regexp: 2 23 | no-irregular-whitespace: 2 24 | no-negated-in-lhs: 2 25 | no-obj-calls: 2 26 | no-proto: 2 27 | no-unexpected-multiline: 2 28 | no-unreachable: 2 29 | use-isnan: 2 30 | valid-typeof: 2 31 | 32 | # Best Practices 33 | # http://eslint.org/docs/rules/#best-practices 34 | no-fallthrough: 2 35 | no-multi-spaces: 2 36 | no-octal: 2 37 | no-redeclare: 2 38 | no-self-assign: 2 39 | no-unused-labels: 2 40 | 41 | # Strict Mode 42 | # http://eslint.org/docs/rules/#strict-mode 43 | strict: [2, "global"] 44 | 45 | # Variables 46 | # http://eslint.org/docs/rules/#variables 47 | no-delete-var: 2 48 | no-undef: 2 49 | no-unused-vars: [2, {"args": "none"}] 50 | 51 | # Node.js and CommonJS 52 | # http://eslint.org/docs/rules/#nodejs-and-commonjs 53 | no-mixed-requires: 2 54 | no-new-require: 2 55 | no-path-concat: 2 56 | no-restricted-modules: [2, "sys", "_linklist"] 57 | 58 | # Stylistic Issues 59 | # http://eslint.org/docs/rules/#stylistic-issues 60 | comma-spacing: 2 61 | eol-last: 2 62 | indent: [2, 2, {SwitchCase: 1}] 63 | key-spacing: [2, {mode: "minimum"}] 64 | keyword-spacing: 2 65 | linebreak-style: [2, "unix"] 66 | max-len: [2, 120, 2] 67 | new-parens: 2 68 | no-mixed-spaces-and-tabs: 2 69 | no-multiple-empty-lines: [2, {max: 2}] 70 | no-trailing-spaces: 2 71 | quotes: [2, "single", "avoid-escape"] 72 | semi: 2 73 | space-before-blocks: [2, "always"] 74 | space-before-function-paren: [2, "never"] 75 | space-in-parens: [2, "never"] 76 | space-infix-ops: 2 77 | space-unary-ops: 2 78 | 79 | # ECMAScript 6 80 | # http://eslint.org/docs/rules/#ecmascript-6 81 | arrow-parens: [2, "always"] 82 | arrow-spacing: [2, {"before": true, "after": true}] 83 | constructor-super: 2 84 | no-class-assign: 2 85 | no-confusing-arrow: 2 86 | no-const-assign: 2 87 | no-dupe-class-members: 2 88 | no-new-symbol: 2 89 | no-this-before-super: 2 90 | prefer-const: 2 91 | template-curly-spacing: 2 92 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | cache/* 3 | *.log 4 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | cache/* 3 | bk.js 4 | test.js 5 | bench.js 6 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "6.9" 4 | - "8.4" 5 | - "10" 6 | scripts: 7 | - npm run lint 8 | - npm test 9 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM mhart/alpine-node:8 2 | 3 | MAINTAINER Dmitry Shirokov 4 | 5 | ADD package.json /tmp/package.json 6 | 7 | RUN cd /tmp && \ 8 | npm install --production && \ 9 | mkdir -p /opt/npm-proxy-cache && \ 10 | cp -a /tmp/node_modules /opt/npm-proxy-cache && \ 11 | mkdir -p /opt/npm-proxy-cache/cache 12 | 13 | VOLUME /opt/npm-proxy-cache/cache 14 | 15 | WORKDIR /opt/npm-proxy-cache 16 | ADD . /opt/npm-proxy-cache 17 | 18 | # Expose API port 19 | EXPOSE 8080 20 | 21 | ENTRYPOINT ["node", "/opt/npm-proxy-cache/bin/npm-proxy-cache"] 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | npm-proxy-cache [![Build Status](https://travis-ci.org/runk/npm-proxy-cache.svg?branch=master)](https://travis-ci.org/runk/npm-proxy-cache) 3 | ======== 4 | 5 | [![Greenkeeper badge](https://badges.greenkeeper.io/runk/npm-proxy-cache.svg)](https://greenkeeper.io/) 6 | 7 | HTTP/HTTPS caching proxy for work with `npm` utility. This is **not** a reverse proxy. 8 | 9 | You may find this tool useful if you are experiencing huge network lags / latency 10 | problems. Other solutions such as local CoachDB mirror of npm registry require much 11 | more work and maintenance. 12 | 13 | 14 | ## Installation 15 | 16 | ### npm 17 | npm install npm-proxy-cache -g 18 | 19 | ### Docker 20 | The docker image of this repository is not hosted on Docker Hub (yet) 21 | 22 | To run npm-proxy-cache as a Docker container, you need to build the image first: 23 | 24 | ```shell 25 | docker build -t npm-proxy-cache . 26 | ``` 27 | 28 | After building the image successfully, you can run the Docker container. To pass parameters, simply append them to the `docker run` command, like so: 29 | 30 | ```shell 31 | docker run -t npm-proxy-cache --port 8080 --host 0.0.0.0 --expired 32 | ``` 33 | 34 | 35 | ## Usage 36 | 37 | First of all, you need to configure `npm` to use proxy 38 | 39 | ```shell 40 | npm config set proxy http://localhost:8080/ 41 | npm config set https-proxy http://localhost:8080/ 42 | npm config set strict-ssl false 43 | ``` 44 | 45 | Another way is to use it explicitly with `npm install` command, like this: 46 | 47 | ```shell 48 | npm --proxy http://localhost:8080 --https-proxy http://localhost:8080 --strict-ssl false install 49 | ``` 50 | 51 | The `strict-ssl false` option is required since it's impossible to auth cached response 52 | from https proxy, which actully acts as a MITM (man in the middle). All other than `GET` 53 | requests *are not cached*, so you still be able to publish your modules to npm registry without 54 | switching cache on and off. 55 | 56 | Once you have `npm` configured, start the proxy: 57 | 58 | ```shell 59 | npm-proxy-cache 60 | ``` 61 | 62 | By default proxy starts on `localhost:8080` and have cache ttl 30 mins. These values might be 63 | overriden using command line options: 64 | 65 | ```text 66 | npm-proxy-cache --help 67 | 68 | Usage: npm-proxy-cache [options] 69 | 70 | Options: 71 | 72 | -h, --host [name] Hostname [localhost] 73 | -p, --port [number] An integer argument [8080] 74 | -t, --ttl [seconds] Cache lifetime in seconds [1800] 75 | -s, --storage [path] Storage path 76 | -x, --proxy HTTP proxy to be used, e.g. http://user:pass@example.com:8888/ 77 | -e, --expired Use expired cache when npm registry unavailable 78 | -f, --friendly-names Use actual file names instead of hashes in the cache 79 | -v, --verbose Verbose mode 80 | -n, --metadata-excluded Exclude metadata requests from caching 81 | -l, --log-path Log path 82 | -m, --internal-port HTTPs port to use for internal proxying "MITM" server (necessary for running on Windows systems) 83 | --help This help 84 | ``` 85 | 86 | ## Why can't I use the built-in npm cache? 87 | 88 | Well, for some reason npm cache works not as expected and cache hits are low. Additionally, 89 | CI servers which run on multiply machines may utilize one cache storage which you can provide 90 | via caching proxy. 91 | 92 | ## Docker 93 | 94 | To use a docker container, run: 95 | 96 | ```shell 97 | curl -sSL https://get.docker.com/ | sh 98 | docker pull folha/npm-proxy-cache 99 | docker run --restart=always --net=host -p 8080:8080 -t folha/npm-proxy-cache --name=npm-proxy-cache 100 | npm --proxy http://npm-proxy-cache:8080 --https-proxy http://npm-proxy-cache:8080 --strict-ssl false install 101 | ``` 102 | 103 | ## Limitations 104 | 105 | - Works only with node `6` and above. 106 | 107 | 108 | ---- 109 | 110 | Any feedback is welcome 111 | -------------------------------------------------------------------------------- /bin/npm-proxy-cache: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | var fs = require('fs'), 4 | program = require('commander'); 5 | 6 | var numeric = function (v) { 7 | return parseInt(v, 10); 8 | }; 9 | 10 | program 11 | .version(JSON.parse(fs.readFileSync(__dirname + '/../package.json', 'utf8')).version) 12 | 13 | .option('-h, --host [name]', 'Hostname [localhost]', 'localhost') 14 | .option('-p, --port [number]', 'An integer argument [8080]', numeric, 8080) 15 | .option('-t, --ttl [seconds]', 'Cache lifetime in seconds [1800]', numeric, 1800) 16 | .option('-s, --storage [path]', 'Storage path', __dirname + '/../cache') 17 | .option('-x, --proxy [address]', 'HTTP proxy to be used, e.g. http://user:pass@example.com:8888/') 18 | .option('-e, --expired', 'Use expired cache when npm registry unavailable') 19 | .option('-f, --friendly-names', 'Use actual file names instead of hashes in the cache') 20 | .option('-v, --verbose', 'Verbose mode') 21 | .option('-n, --metadata-excluded', 'Exclude metadata requests from caching') 22 | .option('-l, --log-path [path]', 'Log path') 23 | .option('-m, --internal-port [port]', 24 | 'HTTPs port to use for internal proxying "MITM" server (mandatory on Windows systems)') 25 | .parse(process.argv); 26 | 27 | require('../lib/proxy').powerup(program); 28 | -------------------------------------------------------------------------------- /cert/README.md: -------------------------------------------------------------------------------- 1 | ### Generate self-signed certificate 2 | 3 | Detailed instructions: http://www.akadia.com/services/ssh_test_certificate.html 4 | 5 | 6 | #### Step 1: Generate a Private Key 7 | 8 | openssl genrsa -des -out dummy.key 2048 9 | 10 | 11 | #### Step 2: Generate a CSR (Certificate Signing Request) 12 | 13 | openssl req -new -key dummy.key -out dummy.csr 14 | 15 | 16 | #### Step 3: Remove Passphrase from Key 17 | 18 | openssl rsa -in dummy.key -out dummy.key 19 | 20 | 21 | #### Step 4: Generating a Self-Signed Certificate 22 | 23 | openssl x509 -req -days 3650 -in dummy.csr -signkey dummy.key -out dummy.crt 24 | -------------------------------------------------------------------------------- /cert/dummy.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDBjCCAe4CCQD+KU2x2RIYfjANBgkqhkiG9w0BAQUFADBFMQswCQYDVQQGEwJB 3 | VTETMBEGA1UECBMKU29tZS1TdGF0ZTEhMB8GA1UEChMYSW50ZXJuZXQgV2lkZ2l0 4 | cyBQdHkgTHRkMB4XDTE1MTIzMDIyNTAyNloXDTI1MTIyNzIyNTAyNlowRTELMAkG 5 | A1UEBhMCQVUxEzARBgNVBAgTClNvbWUtU3RhdGUxITAfBgNVBAoTGEludGVybmV0 6 | IFdpZGdpdHMgUHR5IEx0ZDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB 7 | AJ5EiQqEkvhPBOViH4sHJ/kv7a4+t/Wp9JQrIALca9ToEsxme3P6Tp8i9Ln3Y6q8 8 | VxzcE5Bh5fRa2nJ5Dnf7+EJIG0X0IcCaAJRUyO6DMz5RFTqCHmk6kO+3HIoZvCCT 9 | 1vzoP79C6PZCS7s1T4/uhLkrAJxmYsuvUkzgtZg+79pdZLTU+nHpnGOkWyIYtLZo 10 | JIGo64SPniWFVhwoLtPm/1sy8tzJx4ORyEDW8eCN/tCuYQt3Y/N5KInGpja633B1 11 | Z03QsAmQ9ir0xooxBtRv9CRQVRNA9fc08GTQp8L3kB6/6LbEH+67QIOYTs+Uq5uH 12 | qYtO4Lavdx8GKJPuOFXAB40CAwEAATANBgkqhkiG9w0BAQUFAAOCAQEAnjyVOcXu 13 | DMLFIrWGCqqVXlkgbarAD9nTOA4roajmIiFay6Of0TO5wLI1dXE96FUJdEeNiKnr 14 | XjTAPdLMzdQbEqYwdzWylsHw2VZY6yEAsc66xlufCMSTMZDAKiLp1GRTmiWNGbER 15 | qnynKdkn+WuE46y9k8WyXXA1QBq+dqLeHxASHDqwyKQnezYJk4wE/q30Bcojd+qG 16 | KC1Flz3dfeBGDd2CVal3cPo3teiudrjQalH3zdNitAbUBZ1Y7YK3JACGsazXiOwi 17 | N0eQqQm4qsItF5hSh1cs8OK12qzTv+495ac4j0i/v8JvJaMinIhUej+VpEDd8hoc 18 | eFu3cSpxcJb0ZQ== 19 | -----END CERTIFICATE----- 20 | -------------------------------------------------------------------------------- /cert/dummy.csr: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE REQUEST----- 2 | MIICijCCAXICAQAwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgTClNvbWUtU3RhdGUx 3 | ITAfBgNVBAoTGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDCCASIwDQYJKoZIhvcN 4 | AQEBBQADggEPADCCAQoCggEBAJ5EiQqEkvhPBOViH4sHJ/kv7a4+t/Wp9JQrIALc 5 | a9ToEsxme3P6Tp8i9Ln3Y6q8VxzcE5Bh5fRa2nJ5Dnf7+EJIG0X0IcCaAJRUyO6D 6 | Mz5RFTqCHmk6kO+3HIoZvCCT1vzoP79C6PZCS7s1T4/uhLkrAJxmYsuvUkzgtZg+ 7 | 79pdZLTU+nHpnGOkWyIYtLZoJIGo64SPniWFVhwoLtPm/1sy8tzJx4ORyEDW8eCN 8 | /tCuYQt3Y/N5KInGpja633B1Z03QsAmQ9ir0xooxBtRv9CRQVRNA9fc08GTQp8L3 9 | kB6/6LbEH+67QIOYTs+Uq5uHqYtO4Lavdx8GKJPuOFXAB40CAwEAAaAAMA0GCSqG 10 | SIb3DQEBBQUAA4IBAQBIgzrjOa1c8nntwBjc08Ym6d8Nww26DS/KvoILFVVIGwtJ 11 | GcaqEYd2MuujFzv+OQPaMmXuPBft/SFeheMJ0QtrFN3W9KGGx92FWkj2h7G5bNyS 12 | LomDv8jmJxelMx5IkmlPhT++2QBWxHTIsJFPvxwTZ6+75QZX/PIiL/V3WNnfeTWc 13 | Z0/TRg33/v7e+FbEmniAmk8n4MxPGN25viz+XfZpYd7AhVfnmjuMW4siIro4sVJ2 14 | 7tBwj92dTk0bZKBK+vCiAKmMdbheL3EYNn2jan5kM48JweFv505+QoV8sNnHojNx 15 | IAurwdW7BciNPmSn3NTLHRWbc8HYvuC6LektM0Ms 16 | -----END CERTIFICATE REQUEST----- 17 | -------------------------------------------------------------------------------- /cert/dummy.key: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEowIBAAKCAQEAnkSJCoSS+E8E5WIfiwcn+S/trj639an0lCsgAtxr1OgSzGZ7 3 | c/pOnyL0ufdjqrxXHNwTkGHl9FracnkOd/v4QkgbRfQhwJoAlFTI7oMzPlEVOoIe 4 | aTqQ77ccihm8IJPW/Og/v0Lo9kJLuzVPj+6EuSsAnGZiy69STOC1mD7v2l1ktNT6 5 | cemcY6RbIhi0tmgkgajrhI+eJYVWHCgu0+b/WzLy3MnHg5HIQNbx4I3+0K5hC3dj 6 | 83koicamNrrfcHVnTdCwCZD2KvTGijEG1G/0JFBVE0D19zTwZNCnwveQHr/otsQf 7 | 7rtAg5hOz5Srm4epi07gtq93HwYok+44VcAHjQIDAQABAoIBADROumw7fMeMdjXJ 8 | xFsStmdpK8jW+b27uhXuWtoXN5xaRzw+8rZWF/4Ic5Gy7cHUFYZ4Rg8GDBZCoMgM 9 | snd9U4O0iS8JjM43F9HuY6pQunLyIW4A60QkExrnDgumjcLGmdtkdw0jdBNPve/V 10 | osITNtJsH/uQ6z4eZ7j424vB4Uvt5PZsfUs+OoFvZyuHPBT15SdW4fj/kw/J4HJ2 11 | nEaYUHS6nt37zwgksqMhiaoEx/E1xubrAE1KQCiNwv6Xbfma8AAKSMLMt5mzezzV 12 | 7pLmPKEHEBHTty+dAJUuIw45J3CKnEmSP9hnVUjzRNepsHeD2gbgMY/9ELS0cf4w 13 | u48iyJECgYEAy+SV2BGorgH29zlU34LswmlrrtTKUu/PkBD8EwnknLLGnfoov8+4 14 | e+FitKPJC1lnjzlEpgwuvKCVDUofZn1/R7JPp5Y0YNrFSyqk/ZdmPIX37voBgZIv 15 | 2pOZpSLQrJ1BCXLmqadQZWlHma2dIJWJNaCTJ5JiIAqchrtYOmSWX2cCgYEAxrb7 16 | 6Tjs8rwkJAEtpCxqGjhvDPBtE256QA8nlb9FStGEWkSBlFl1y+z/IH+u8cmoldHM 17 | KXQAXrwqCdzV8j9NL+HrHG+Dy0YgY8dZ+koTmoFSSYiJztd5GTIBEMHDQiHtKE3z 18 | wnToHhbzvdgXgAqHWJs8Z04EDdQCTVUgTRVxbOsCgYEAt0dYBu0AwsITXPIVQAFx 19 | j+Sxnmu7H0hgPTF3VinbbZwqe9FmFd3ECN92GcjsrJ4GQDGLavOig36Z3WNWzItd 20 | AXbgsVWM6c9d8A56V1Zj2V3Xm+LxvaZKC+IIB/A+whttXfACNlinJTG44gS1LJge 21 | yaz4+mZUfFbtkxMO4RaQMVsCgYB/41Ao/wvwBIm16lQ3bqYyjdNannl0ogtnotk1 22 | FM5Boqntdl123kRjo/lmLwamccRf94/PtepvDeZ4Rnoi1/6TLHCl8lb9wfoifTA5 23 | Vuda7pPI42jdh+tV/cqVwD4sF62L3O9eXOLuSpM/wQ0OyLfmnLG5Qop1Y9a/cmPB 24 | eGVxVQKBgAhw1GkO2kzDnycEu3RNLQqIGZWU8Buu07UvxITdNuMmeP2FI0o5hiNl 25 | e2Fy4Ujd2hWFJGVdvMZdeJRo7NQMWRkSfRhBiH8AS1nk6Cy3MclT38V89v/QYYc5 26 | nV77ioEnVVPwuFYEm5ZuA/7hANf8yzj3frYk/mgHJeRx7kateIcB 27 | -----END RSA PRIVATE KEY----- 28 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | npmproxycache: 2 | build: . 3 | ports: 4 | - "8080:8080" 5 | # You can pass parameters here 6 | command: "--host 0.0.0.0 --port 8080 --verbose --expired" 7 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | module.exports = require('./lib/proxy'); 3 | -------------------------------------------------------------------------------- /lib/cache.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const path = require('path'); 3 | const fs = require('fs'); 4 | const os = require('os'); 5 | const crypto = require('crypto'); 6 | const mkdirp = require('mkdirp'); 7 | const mv = require('mv'); 8 | 9 | function Cache(opts) { 10 | this.opts = opts || {}; 11 | this.opts.ttl = (opts.ttl || 1800) * 1000; 12 | this.opts.friendlyNames = opts.friendlyNames; 13 | this.opts.path = opts.path || path.join(__dirname, '/../cache'); 14 | 15 | this.locks = {}; 16 | const nop = function() {}; 17 | 18 | this.stat = function(fullpath) { 19 | if (!fs.existsSync(fullpath)) 20 | return {status: Cache.NOT_FOUND}; 21 | 22 | const stat = fs.lstatSync(fullpath); 23 | stat.type = path.extname(fullpath) ? 'application/octet-stream' : 'application/json'; 24 | stat.status = (Date.now() < stat.ctime.valueOf() + this.opts.ttl) 25 | ? Cache.FRESH 26 | : Cache.EXPIRED; 27 | 28 | return stat; 29 | }; 30 | 31 | 32 | this.meta = function(key, cb) { 33 | const self = this; 34 | const fullpath = this.getPath(key).full; 35 | const stat = this.stat(fullpath); 36 | 37 | if (stat.status === Cache.NOT_FOUND || stat.status === Cache.EXPIRED) 38 | return cb(null, stat); 39 | 40 | if (!this.locks[key]) 41 | return cb(null, stat); 42 | 43 | // wait until lock releases 44 | // generally when file is locked means that process is writing to file right now 45 | (function wait() { 46 | if (self.locks[key]) 47 | return setTimeout(wait, 100); 48 | 49 | // need to acquire new fstat, since file has been changed 50 | cb(null, self.stat(fullpath)); 51 | })(); 52 | }; 53 | 54 | 55 | this.read = function(key) { 56 | const pathInfo = this.getPath(key); 57 | const file = fs.createReadStream(pathInfo.full); 58 | 59 | file.on('finish', function() { 60 | file.close(nop); 61 | }); 62 | 63 | return file; 64 | }; 65 | 66 | 67 | this.write = function(key, readStream, cb) { 68 | const locks = this.locks; 69 | const pathInfo = this.getPath(key); 70 | const self = this; 71 | 72 | // Create a lock 73 | locks[key] = true; 74 | 75 | mkdirp.sync(pathInfo.dir, 511); // 511 is decimal equvivalent of 0777 76 | 77 | // On top of locking mechanism, doing write to a temp location, and 78 | // when it's finish moving the data file to its final destination. 79 | const tmpPath = path.join(os.tmpdir(), pathInfo.file + '-' + Math.round(Math.random() * 1e9).toString(36)); 80 | 81 | const writeStream = fs.createWriteStream(tmpPath); 82 | readStream.pipe(writeStream); 83 | 84 | writeStream.on('finish', function() { 85 | writeStream.close(nop); 86 | 87 | // Release the lock, move data file to final destination. 88 | delete (locks[key]); 89 | mv(tmpPath, pathInfo.full, function(err) { 90 | if (err) { return cb(err); } 91 | self.meta(key, cb); 92 | }); 93 | }); 94 | }; 95 | 96 | 97 | this.getPath = function(key) { 98 | let file; 99 | let base; 100 | 101 | if (this.opts.friendlyNames) { 102 | // The key is the URL; the last part is the module name and if 103 | // the last version is requested, it lacks the file extension 104 | file = path.basename(key); 105 | // Cut the version suffix and file extension; only module name 106 | // should make the directory, make sure that there is no dot as 107 | // directory name coming from the first characters of the file name 108 | base = file.replace(/(-\d\.\d.\d)?\.tgz/, '').replace(/\./g, '-'); 109 | } else { 110 | file = crypto.createHash('md5').update(key).digest('hex') 111 | .substring(0, 8) + path.extname(key); 112 | 113 | base = file; 114 | } 115 | // Make sure that there are always 3 nested directories to avoid 116 | // both file and folder at the same level (/q/q, /q/q/qq) 117 | const chunks = base.split('').splice(0, 3); 118 | while (chunks.length < 3) 119 | chunks.push('-'); 120 | const dir = chunks.join('/'); 121 | 122 | return { 123 | dir: path.join(this.opts.path, dir), 124 | full: path.join(this.opts.path, dir, file), 125 | file: file, 126 | rel: path.join(dir, file) 127 | }; 128 | }; 129 | 130 | 131 | this.unlink = function(key) { 132 | delete (this.locks[key]); 133 | fs.unlinkSync(this.getPath(key).full); 134 | }; 135 | 136 | } 137 | 138 | Cache.NOT_FOUND = 0; 139 | Cache.EXPIRED = 2; 140 | Cache.FRESH = 4; 141 | 142 | module.exports = Cache; 143 | -------------------------------------------------------------------------------- /lib/logger.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const log4js = require('log4js'); 3 | 4 | module.exports = function(opts) { 5 | const appenders = { stdout: { type: 'stdout' } }; 6 | if (opts.logPath) { 7 | appenders.file = { type: 'file', filename: opts.logPath }; 8 | } 9 | 10 | const categories = { 11 | default: { 12 | appenders: Object.keys(appenders), 13 | level: opts.verbose ? 'debug' : 'info' 14 | } 15 | }; 16 | 17 | log4js.configure({ appenders, categories }); 18 | return log4js.getLogger(); 19 | }; 20 | -------------------------------------------------------------------------------- /lib/proxy.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const http = require('http'); 3 | const net = require('net'); 4 | const https = require('https'); 5 | const fs = require('fs'); 6 | const os = require('os'); 7 | const url = require('url'); 8 | const path = require('path'); 9 | const request = require('request'); 10 | const Cache = require('./cache'); 11 | const getLogger = require('./logger'); 12 | 13 | 14 | // To avoid 'DEPTH_ZERO_SELF_SIGNED_CERT' error on some setups 15 | process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0'; 16 | 17 | exports.log = null; 18 | 19 | exports.cache = null; 20 | 21 | exports.opts = {}; 22 | 23 | // Port or socket path of internal MITM server. 24 | let mitmAddress; 25 | 26 | // Random header to prevent sending requests in a cycle 27 | const cycleCheckHeader = 'x-npm-proxy-cache-' + Math.round(Math.random() * 10000); 28 | 29 | exports.powerup = function(opts) { 30 | 31 | exports.opts = opts || {}; 32 | 33 | const options = { 34 | key: fs.readFileSync(path.join(__dirname, '/../cert/dummy.key'), 'utf8'), 35 | cert: fs.readFileSync(path.join(__dirname, '/../cert/dummy.crt'), 'utf8') 36 | }; 37 | 38 | this.cache = new Cache({ 39 | path: opts.storage, ttl: opts.ttl, friendlyNames: opts.friendlyNames 40 | }); 41 | 42 | 43 | this.log = getLogger(opts); 44 | 45 | // Fake https server aka MITM 46 | const mitm = https.createServer(options, exports.httpHandler); 47 | 48 | // NOTE: for windows platform user has to specify port, since 49 | // it does not support unix sockets. 50 | if (/^win/i.test(process.platform) && !isNumeric(opts.internalPort)) { 51 | console.error('Error: On Windows platform you have to specify internal port,\n' 52 | + 'for example `--internal-port 8081`.'); 53 | process.exit(1); 54 | } 55 | if (opts.internalPort) { 56 | mitmAddress = opts.internalPort; 57 | 58 | } else { 59 | mitmAddress = path.join(os.tmpdir(), 'mitm.sock'); 60 | 61 | // Cleanup MITM socket for unix platforms 62 | if (fs.existsSync(mitmAddress)) 63 | fs.unlinkSync(mitmAddress); 64 | } 65 | 66 | mitm.listen(mitmAddress); 67 | 68 | // start HTTP server with custom request handler callback function 69 | const server = http.createServer(exports.httpHandler).listen(opts.port, opts.host, function(err) { 70 | if (err) throw err; 71 | exports.log.info('Listening on %s:%s [%d]', opts.host, opts.port, process.pid); 72 | }); 73 | 74 | // add handler for HTTPS (which issues a CONNECT to the proxy) 75 | server.addListener('connect', this.httpsHandler); 76 | 77 | return { httpsServer: mitm, httpServer: server }; 78 | }; 79 | 80 | 81 | exports.httpHandler = function(req, res) { 82 | const cache = exports.cache, 83 | log = exports.log, 84 | path = url.parse(req.url).path, 85 | schema = req.client.pair || req.connection.encrypted ? 'https' : 'http', 86 | dest = schema + '://' + req.headers['host'] + path; 87 | 88 | if (req.headers[cycleCheckHeader]) { 89 | res.writeHead(502); 90 | res.end('Sending requests to myself. Stopping to prevent cycles.'); 91 | return; 92 | } 93 | 94 | const params = { 95 | headers: {}, 96 | rejectUnauthorized: false, 97 | url: dest 98 | }; 99 | 100 | params.headers[cycleCheckHeader] = 1; 101 | 102 | // Carry following headers down to dest npm repository. 103 | const carryHeaders = ['authorization', 'version', 'referer', 'npm-session', 'user-agent']; 104 | carryHeaders.forEach(function(name) { 105 | params.headers[name] = req.headers[name]; 106 | }); 107 | 108 | if (exports.opts.proxy) 109 | params.proxy = exports.opts.proxy; 110 | 111 | 112 | // Skipping other than GET methods 113 | // Skipping metadata requests when configured 114 | if ( 115 | req.method !== 'GET' || 116 | (exports.opts.metadataExcluded && req.headers['accept'] === 'application/json') 117 | ) 118 | return bypass(req, res, params); 119 | 120 | 121 | cache.meta(dest, function(err, meta) { 122 | if (err) 123 | throw err; 124 | 125 | if (meta.status === Cache.FRESH) 126 | return respondWithCache(dest, cache, meta, res); 127 | 128 | const p = cache.getPath(dest); 129 | log.debug('Cache file:', p.rel); 130 | 131 | log.warn('direct', dest); 132 | 133 | const onResponse = function(err, response) { 134 | // don't save responses with codes other than 200 135 | if (!err && response.statusCode === 200) { 136 | cache.write(dest, r, function(err, meta) { 137 | if (err) 138 | throw err; 139 | 140 | respondWithCache(dest, cache, meta, res); 141 | }); 142 | 143 | } else { 144 | // serve expired cache if user wants so 145 | if (exports.opts.expired && meta.status === Cache.EXPIRED) 146 | return respondWithCache(dest, cache, meta, res); 147 | 148 | log.error('An error occcured: "%s", status code "%s"', 149 | err ? err.message : 'Unknown', 150 | response ? response.statusCode : 0 151 | ); 152 | 153 | // clean old cache 154 | if (meta.status !== Cache.NOT_FOUND) 155 | cache.unlink(dest); 156 | 157 | res.end(err ? err.toString() : 'Status ' + response.statusCode + ' returned'); 158 | } 159 | }; 160 | 161 | const r = request(params); 162 | r.on('response', onResponse.bind(null, null)); 163 | r.on('error', onResponse.bind(null)); 164 | r.on('end', function() { 165 | log.debug('end'); 166 | }); 167 | }); 168 | }; 169 | 170 | 171 | exports.httpsHandler = function(request, socketRequest, bodyhead) { 172 | const log = exports.log, 173 | httpVersion = request['httpVersion']; 174 | 175 | log.debug(' = will connect to socket (or port) "%s"', mitmAddress); 176 | 177 | // set up TCP connection 178 | const proxySocket = new net.Socket(); 179 | proxySocket.connect(mitmAddress, function() { 180 | log.debug('< connected to socket (or port) "%s"', mitmAddress); 181 | log.debug('> writing head of length %d', bodyhead.length); 182 | 183 | proxySocket.write(bodyhead); 184 | 185 | // tell the caller the connection was successfully established 186 | socketRequest.write('HTTP/' + httpVersion + ' 200 Connection established\r\n\r\n'); 187 | }); 188 | 189 | proxySocket.on('data', function(chunk) { 190 | log.debug('< data length = %d', chunk.length); 191 | socketRequest.write(chunk); 192 | }); 193 | 194 | proxySocket.on('end', function() { 195 | log.debug('< end'); 196 | socketRequest.end(); 197 | }); 198 | 199 | socketRequest.on('data', function(chunk) { 200 | log.debug('> data length = %d', chunk.length); 201 | proxySocket.write(chunk); 202 | }); 203 | 204 | socketRequest.on('end', function() { 205 | log.debug('> end'); 206 | proxySocket.end(); 207 | }); 208 | 209 | proxySocket.on('error', function(err) { 210 | socketRequest.write('HTTP/' + httpVersion + ' 500 Connection error\r\n\r\n'); 211 | log.error('< ERR: %s', err.toString()); 212 | socketRequest.end(); 213 | }); 214 | 215 | socketRequest.on('error', function(err) { 216 | log.error('> ERR: %s', err.toString()); 217 | proxySocket.end(); 218 | }); 219 | }; 220 | 221 | 222 | function bypass(req, res, params) { 223 | const length = parseInt(req.headers['content-length']); 224 | const log = exports.log; 225 | 226 | const onEnd = function(params, res) { 227 | return request(params) 228 | .on('error', function(err) { 229 | log.error('bypass', err); 230 | }) 231 | .pipe(res, { end: false }); 232 | }; 233 | 234 | if (!isFinite(length)) { 235 | onEnd(params, res); 236 | return; 237 | } 238 | 239 | const raw = new Buffer(length); 240 | let pointer = 0; 241 | 242 | req.on('data', function(chunk) { 243 | chunk.copy(raw, pointer); 244 | pointer += chunk.length; 245 | }); 246 | 247 | req.on('end', function() { 248 | params.method = req.method; 249 | if (raw.length > 0) { 250 | params.body = raw; 251 | } 252 | params.headers = { 253 | 'Content-Type': req.headers['content-type'] 254 | }; 255 | onEnd(params, res); 256 | }); 257 | } 258 | 259 | 260 | function respondWithCache(dest, cache, meta, res) { 261 | const log = exports.log; 262 | log.info('cache', dest); 263 | log.debug('size: %s, type: "%s", ctime: %d', meta.size, meta.type, meta.ctime.valueOf()); 264 | res.setHeader('Content-Length', meta.size); 265 | res.setHeader('Content-Type', meta.type); 266 | res.setHeader('Connection', 'keep-alive'); 267 | res.setHeader('X-Cache-Hit', 'true'); 268 | return cache.read(dest).pipe(res); 269 | } 270 | 271 | function isNumeric(n) { 272 | return !isNaN(parseFloat(n)) && isFinite(n); 273 | } 274 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "npm-proxy-cache", 3 | "version": "2.2.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "@gimenete/type-writer": { 8 | "version": "0.1.3", 9 | "resolved": "https://registry.npmjs.org/@gimenete/type-writer/-/type-writer-0.1.3.tgz", 10 | "integrity": "sha512-vhpvVfM/fYqb1aAnkgOvtDKoOgU3ZYIvDnKSDAFSoBvallmGURMlHOE0/VG/gqunUZVXGCFBGHxI8swjBh+sIA==", 11 | "dev": true, 12 | "requires": { 13 | "camelcase": "^5.0.0", 14 | "prettier": "^1.13.7" 15 | } 16 | }, 17 | "@octokit/rest": { 18 | "version": "15.10.0", 19 | "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-15.10.0.tgz", 20 | "integrity": "sha512-xZ4ejCZoqvKrIN3tQOKZlJ6nDQxaOdLcjRsamDnbckU7V5YTn2xheIqFXnQ2vLvxqVwyI8+2dfsODYbHxtwtSw==", 21 | "dev": true, 22 | "requires": { 23 | "@gimenete/type-writer": "^0.1.3", 24 | "before-after-hook": "^1.1.0", 25 | "btoa-lite": "^1.0.0", 26 | "debug": "^3.1.0", 27 | "http-proxy-agent": "^2.1.0", 28 | "https-proxy-agent": "^2.2.0", 29 | "lodash": "^4.17.4", 30 | "node-fetch": "^2.1.1", 31 | "url-template": "^2.0.8" 32 | } 33 | }, 34 | "acorn": { 35 | "version": "5.7.2", 36 | "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.2.tgz", 37 | "integrity": "sha512-cJrKCNcr2kv8dlDnbw+JPUGjHZzo4myaxOLmpOX8a+rgX94YeTcTMv/LFJUSByRpc+i4GgVnnhLxvMu/2Y+rqw==", 38 | "dev": true 39 | }, 40 | "acorn-jsx": { 41 | "version": "4.1.1", 42 | "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-4.1.1.tgz", 43 | "integrity": "sha512-JY+iV6r+cO21KtntVvFkD+iqjtdpRUpGqKWgfkCdZq1R+kbreEl8EcdcJR4SmiIgsIQT33s6QzheQ9a275Q8xw==", 44 | "dev": true, 45 | "requires": { 46 | "acorn": "^5.0.3" 47 | } 48 | }, 49 | "agent-base": { 50 | "version": "4.2.1", 51 | "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.2.1.tgz", 52 | "integrity": "sha512-JVwXMr9nHYTUXsBFKUqhJwvlcYU/blreOEUkhNR2eXZIvwd+c+o5V4MgDPKWnMS/56awN3TRzIP+KoPn+roQtg==", 53 | "dev": true, 54 | "requires": { 55 | "es6-promisify": "^5.0.0" 56 | } 57 | }, 58 | "ajv": { 59 | "version": "5.5.2", 60 | "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz", 61 | "integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=", 62 | "requires": { 63 | "co": "^4.6.0", 64 | "fast-deep-equal": "^1.0.0", 65 | "fast-json-stable-stringify": "^2.0.0", 66 | "json-schema-traverse": "^0.3.0" 67 | } 68 | }, 69 | "ajv-keywords": { 70 | "version": "3.2.0", 71 | "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.2.0.tgz", 72 | "integrity": "sha1-6GuBnGAs+IIa1jdBNpjx3sAhhHo=", 73 | "dev": true 74 | }, 75 | "ansi-escapes": { 76 | "version": "3.1.0", 77 | "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.1.0.tgz", 78 | "integrity": "sha512-UgAb8H9D41AQnu/PbWlCofQVcnV4Gs2bBJi9eZPxfU/hgglFh3SMDMENRIqdr7H6XFnXdoknctFByVsCOotTVw==", 79 | "dev": true 80 | }, 81 | "ansi-regex": { 82 | "version": "2.1.1", 83 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", 84 | "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", 85 | "dev": true 86 | }, 87 | "ansi-styles": { 88 | "version": "2.2.1", 89 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", 90 | "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", 91 | "dev": true 92 | }, 93 | "argparse": { 94 | "version": "1.0.10", 95 | "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", 96 | "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", 97 | "dev": true, 98 | "requires": { 99 | "sprintf-js": "~1.0.2" 100 | } 101 | }, 102 | "array-union": { 103 | "version": "1.0.2", 104 | "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", 105 | "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", 106 | "dev": true, 107 | "requires": { 108 | "array-uniq": "^1.0.1" 109 | } 110 | }, 111 | "array-uniq": { 112 | "version": "1.0.3", 113 | "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", 114 | "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=", 115 | "dev": true 116 | }, 117 | "arrify": { 118 | "version": "1.0.1", 119 | "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", 120 | "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=", 121 | "dev": true 122 | }, 123 | "asn1": { 124 | "version": "0.2.4", 125 | "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", 126 | "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", 127 | "requires": { 128 | "safer-buffer": "~2.1.0" 129 | } 130 | }, 131 | "assert-plus": { 132 | "version": "1.0.0", 133 | "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", 134 | "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" 135 | }, 136 | "asynckit": { 137 | "version": "0.4.0", 138 | "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", 139 | "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" 140 | }, 141 | "aws-sign2": { 142 | "version": "0.7.0", 143 | "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", 144 | "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=" 145 | }, 146 | "aws4": { 147 | "version": "1.8.0", 148 | "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz", 149 | "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==" 150 | }, 151 | "babel-code-frame": { 152 | "version": "6.26.0", 153 | "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", 154 | "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=", 155 | "dev": true, 156 | "requires": { 157 | "chalk": "^1.1.3", 158 | "esutils": "^2.0.2", 159 | "js-tokens": "^3.0.2" 160 | }, 161 | "dependencies": { 162 | "chalk": { 163 | "version": "1.1.3", 164 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", 165 | "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", 166 | "dev": true, 167 | "requires": { 168 | "ansi-styles": "^2.2.1", 169 | "escape-string-regexp": "^1.0.2", 170 | "has-ansi": "^2.0.0", 171 | "strip-ansi": "^3.0.0", 172 | "supports-color": "^2.0.0" 173 | } 174 | }, 175 | "strip-ansi": { 176 | "version": "3.0.1", 177 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", 178 | "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", 179 | "dev": true, 180 | "requires": { 181 | "ansi-regex": "^2.0.0" 182 | } 183 | } 184 | } 185 | }, 186 | "balanced-match": { 187 | "version": "1.0.0", 188 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", 189 | "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" 190 | }, 191 | "bcrypt-pbkdf": { 192 | "version": "1.0.2", 193 | "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", 194 | "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", 195 | "optional": true, 196 | "requires": { 197 | "tweetnacl": "^0.14.3" 198 | } 199 | }, 200 | "before-after-hook": { 201 | "version": "1.1.0", 202 | "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-1.1.0.tgz", 203 | "integrity": "sha512-VOMDtYPwLbIncTxNoSzRyvaMxtXmLWLUqr8k5AfC1BzLk34HvBXaQX8snOwQZ4c0aX8aSERqtJSiI9/m2u5kuA==", 204 | "dev": true 205 | }, 206 | "brace-expansion": { 207 | "version": "1.1.11", 208 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 209 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 210 | "requires": { 211 | "balanced-match": "^1.0.0", 212 | "concat-map": "0.0.1" 213 | } 214 | }, 215 | "browser-stdout": { 216 | "version": "1.3.1", 217 | "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", 218 | "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", 219 | "dev": true 220 | }, 221 | "btoa-lite": { 222 | "version": "1.0.0", 223 | "resolved": "https://registry.npmjs.org/btoa-lite/-/btoa-lite-1.0.0.tgz", 224 | "integrity": "sha1-M3dm2hWAEhD92VbCLpxokaudAzc=", 225 | "dev": true 226 | }, 227 | "caller-path": { 228 | "version": "0.1.0", 229 | "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-0.1.0.tgz", 230 | "integrity": "sha1-lAhe9jWB7NPaqSREqP6U6CV3dR8=", 231 | "dev": true, 232 | "requires": { 233 | "callsites": "^0.2.0" 234 | } 235 | }, 236 | "callsites": { 237 | "version": "0.2.0", 238 | "resolved": "https://registry.npmjs.org/callsites/-/callsites-0.2.0.tgz", 239 | "integrity": "sha1-r6uWJikQp/M8GaV3WCXGnzTjUMo=", 240 | "dev": true 241 | }, 242 | "camelcase": { 243 | "version": "5.0.0", 244 | "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.0.0.tgz", 245 | "integrity": "sha512-faqwZqnWxbxn+F1d399ygeamQNy3lPp/H9H6rNrqYh4FSVCtcY+3cub1MxA8o9mDd55mM8Aghuu/kuyYA6VTsA==", 246 | "dev": true 247 | }, 248 | "caseless": { 249 | "version": "0.12.0", 250 | "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", 251 | "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" 252 | }, 253 | "chalk": { 254 | "version": "2.4.1", 255 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz", 256 | "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", 257 | "dev": true, 258 | "requires": { 259 | "ansi-styles": "^3.2.1", 260 | "escape-string-regexp": "^1.0.5", 261 | "supports-color": "^5.3.0" 262 | }, 263 | "dependencies": { 264 | "ansi-styles": { 265 | "version": "3.2.1", 266 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", 267 | "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", 268 | "dev": true, 269 | "requires": { 270 | "color-convert": "^1.9.0" 271 | } 272 | }, 273 | "supports-color": { 274 | "version": "5.4.0", 275 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz", 276 | "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", 277 | "dev": true, 278 | "requires": { 279 | "has-flag": "^3.0.0" 280 | } 281 | } 282 | } 283 | }, 284 | "chardet": { 285 | "version": "0.4.2", 286 | "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.4.2.tgz", 287 | "integrity": "sha1-tUc7M9yXxCTl2Y3IfVXU2KKci/I=", 288 | "dev": true 289 | }, 290 | "circular-json": { 291 | "version": "0.5.5", 292 | "resolved": "https://registry.npmjs.org/circular-json/-/circular-json-0.5.5.tgz", 293 | "integrity": "sha512-13YaR6kiz0kBNmIVM87Io8Hp7bWOo4r61vkEANy8iH9R9bc6avud/1FT0SBpqR1RpIQADOh/Q+yHZDA1iL6ysA==" 294 | }, 295 | "cli-cursor": { 296 | "version": "2.1.0", 297 | "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", 298 | "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=", 299 | "dev": true, 300 | "requires": { 301 | "restore-cursor": "^2.0.0" 302 | } 303 | }, 304 | "cli-width": { 305 | "version": "2.2.0", 306 | "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.0.tgz", 307 | "integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=", 308 | "dev": true 309 | }, 310 | "co": { 311 | "version": "4.6.0", 312 | "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", 313 | "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=" 314 | }, 315 | "color-convert": { 316 | "version": "1.9.2", 317 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.2.tgz", 318 | "integrity": "sha512-3NUJZdhMhcdPn8vJ9v2UQJoH0qqoGUkYTgFEPZaPjEtwmmKUfNV46zZmgB2M5M4DCEQHMaCfWHCxiBflLm04Tg==", 319 | "dev": true, 320 | "requires": { 321 | "color-name": "1.1.1" 322 | } 323 | }, 324 | "color-name": { 325 | "version": "1.1.1", 326 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.1.tgz", 327 | "integrity": "sha1-SxQVMEz1ACjqgWQ2Q72C6gWANok=", 328 | "dev": true 329 | }, 330 | "combined-stream": { 331 | "version": "1.0.6", 332 | "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.6.tgz", 333 | "integrity": "sha1-cj599ugBrFYTETp+RFqbactjKBg=", 334 | "requires": { 335 | "delayed-stream": "~1.0.0" 336 | } 337 | }, 338 | "commander": { 339 | "version": "2.17.1", 340 | "resolved": "https://registry.npmjs.org/commander/-/commander-2.17.1.tgz", 341 | "integrity": "sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg==" 342 | }, 343 | "concat-map": { 344 | "version": "0.0.1", 345 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 346 | "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" 347 | }, 348 | "core-util-is": { 349 | "version": "1.0.2", 350 | "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", 351 | "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" 352 | }, 353 | "cross-spawn": { 354 | "version": "6.0.5", 355 | "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", 356 | "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", 357 | "dev": true, 358 | "requires": { 359 | "nice-try": "^1.0.4", 360 | "path-key": "^2.0.1", 361 | "semver": "^5.5.0", 362 | "shebang-command": "^1.2.0", 363 | "which": "^1.2.9" 364 | } 365 | }, 366 | "dashdash": { 367 | "version": "1.14.1", 368 | "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", 369 | "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", 370 | "requires": { 371 | "assert-plus": "^1.0.0" 372 | } 373 | }, 374 | "date-format": { 375 | "version": "1.2.0", 376 | "resolved": "https://registry.npmjs.org/date-format/-/date-format-1.2.0.tgz", 377 | "integrity": "sha1-YV6CjiM90aubua4JUODOzPpuytg=" 378 | }, 379 | "debug": { 380 | "version": "3.1.0", 381 | "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", 382 | "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", 383 | "requires": { 384 | "ms": "2.0.0" 385 | } 386 | }, 387 | "deep-is": { 388 | "version": "0.1.3", 389 | "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", 390 | "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", 391 | "dev": true 392 | }, 393 | "del": { 394 | "version": "2.2.2", 395 | "resolved": "https://registry.npmjs.org/del/-/del-2.2.2.tgz", 396 | "integrity": "sha1-wSyYHQZ4RshLyvhiz/kw2Qf/0ag=", 397 | "dev": true, 398 | "requires": { 399 | "globby": "^5.0.0", 400 | "is-path-cwd": "^1.0.0", 401 | "is-path-in-cwd": "^1.0.0", 402 | "object-assign": "^4.0.1", 403 | "pify": "^2.0.0", 404 | "pinkie-promise": "^2.0.0", 405 | "rimraf": "^2.2.8" 406 | } 407 | }, 408 | "delayed-stream": { 409 | "version": "1.0.0", 410 | "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", 411 | "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" 412 | }, 413 | "diff": { 414 | "version": "3.5.0", 415 | "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", 416 | "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", 417 | "dev": true 418 | }, 419 | "doctrine": { 420 | "version": "2.1.0", 421 | "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", 422 | "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", 423 | "dev": true, 424 | "requires": { 425 | "esutils": "^2.0.2" 426 | } 427 | }, 428 | "ecc-jsbn": { 429 | "version": "0.1.2", 430 | "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", 431 | "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", 432 | "optional": true, 433 | "requires": { 434 | "jsbn": "~0.1.0", 435 | "safer-buffer": "^2.1.0" 436 | } 437 | }, 438 | "es6-promise": { 439 | "version": "4.2.4", 440 | "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.4.tgz", 441 | "integrity": "sha512-/NdNZVJg+uZgtm9eS3O6lrOLYmQag2DjdEXuPaHlZ6RuVqgqaVZfgYCepEIKsLqwdQArOPtC3XzRLqGGfT8KQQ==", 442 | "dev": true 443 | }, 444 | "es6-promisify": { 445 | "version": "5.0.0", 446 | "resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz", 447 | "integrity": "sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=", 448 | "dev": true, 449 | "requires": { 450 | "es6-promise": "^4.0.3" 451 | } 452 | }, 453 | "escape-string-regexp": { 454 | "version": "1.0.5", 455 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", 456 | "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", 457 | "dev": true 458 | }, 459 | "eslint": { 460 | "version": "5.4.0", 461 | "resolved": "https://registry.npmjs.org/eslint/-/eslint-5.4.0.tgz", 462 | "integrity": "sha512-UIpL91XGex3qtL6qwyCQJar2j3osKxK9e3ano3OcGEIRM4oWIpCkDg9x95AXEC2wMs7PnxzOkPZ2gq+tsMS9yg==", 463 | "dev": true, 464 | "requires": { 465 | "ajv": "^6.5.0", 466 | "babel-code-frame": "^6.26.0", 467 | "chalk": "^2.1.0", 468 | "cross-spawn": "^6.0.5", 469 | "debug": "^3.1.0", 470 | "doctrine": "^2.1.0", 471 | "eslint-scope": "^4.0.0", 472 | "eslint-utils": "^1.3.1", 473 | "eslint-visitor-keys": "^1.0.0", 474 | "espree": "^4.0.0", 475 | "esquery": "^1.0.1", 476 | "esutils": "^2.0.2", 477 | "file-entry-cache": "^2.0.0", 478 | "functional-red-black-tree": "^1.0.1", 479 | "glob": "^7.1.2", 480 | "globals": "^11.7.0", 481 | "ignore": "^4.0.2", 482 | "imurmurhash": "^0.1.4", 483 | "inquirer": "^5.2.0", 484 | "is-resolvable": "^1.1.0", 485 | "js-yaml": "^3.11.0", 486 | "json-stable-stringify-without-jsonify": "^1.0.1", 487 | "levn": "^0.3.0", 488 | "lodash": "^4.17.5", 489 | "minimatch": "^3.0.4", 490 | "mkdirp": "^0.5.1", 491 | "natural-compare": "^1.4.0", 492 | "optionator": "^0.8.2", 493 | "path-is-inside": "^1.0.2", 494 | "pluralize": "^7.0.0", 495 | "progress": "^2.0.0", 496 | "regexpp": "^2.0.0", 497 | "require-uncached": "^1.0.3", 498 | "semver": "^5.5.0", 499 | "strip-ansi": "^4.0.0", 500 | "strip-json-comments": "^2.0.1", 501 | "table": "^4.0.3", 502 | "text-table": "^0.2.0" 503 | }, 504 | "dependencies": { 505 | "ajv": { 506 | "version": "6.5.3", 507 | "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.5.3.tgz", 508 | "integrity": "sha512-LqZ9wY+fx3UMiiPd741yB2pj3hhil+hQc8taf4o2QGRFpWgZ2V5C8HA165DY9sS3fJwsk7uT7ZlFEyC3Ig3lLg==", 509 | "dev": true, 510 | "requires": { 511 | "fast-deep-equal": "^2.0.1", 512 | "fast-json-stable-stringify": "^2.0.0", 513 | "json-schema-traverse": "^0.4.1", 514 | "uri-js": "^4.2.2" 515 | } 516 | }, 517 | "fast-deep-equal": { 518 | "version": "2.0.1", 519 | "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", 520 | "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=", 521 | "dev": true 522 | }, 523 | "glob": { 524 | "version": "7.1.2", 525 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", 526 | "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", 527 | "dev": true, 528 | "requires": { 529 | "fs.realpath": "^1.0.0", 530 | "inflight": "^1.0.4", 531 | "inherits": "2", 532 | "minimatch": "^3.0.4", 533 | "once": "^1.3.0", 534 | "path-is-absolute": "^1.0.0" 535 | } 536 | }, 537 | "json-schema-traverse": { 538 | "version": "0.4.1", 539 | "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", 540 | "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", 541 | "dev": true 542 | } 543 | } 544 | }, 545 | "eslint-scope": { 546 | "version": "4.0.0", 547 | "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.0.tgz", 548 | "integrity": "sha512-1G6UTDi7Jc1ELFwnR58HV4fK9OQK4S6N985f166xqXxpjU6plxFISJa2Ba9KCQuFa8RCnj/lSFJbHo7UFDBnUA==", 549 | "dev": true, 550 | "requires": { 551 | "esrecurse": "^4.1.0", 552 | "estraverse": "^4.1.1" 553 | } 554 | }, 555 | "eslint-utils": { 556 | "version": "1.3.1", 557 | "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-1.3.1.tgz", 558 | "integrity": "sha512-Z7YjnIldX+2XMcjr7ZkgEsOj/bREONV60qYeB/bjMAqqqZ4zxKyWX+BOUkdmRmA9riiIPVvo5x86m5elviOk0Q==", 559 | "dev": true 560 | }, 561 | "eslint-visitor-keys": { 562 | "version": "1.0.0", 563 | "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz", 564 | "integrity": "sha512-qzm/XxIbxm/FHyH341ZrbnMUpe+5Bocte9xkmFMzPMjRaZMcXww+MpBptFvtU+79L362nqiLhekCxCxDPaUMBQ==", 565 | "dev": true 566 | }, 567 | "espree": { 568 | "version": "4.0.0", 569 | "resolved": "https://registry.npmjs.org/espree/-/espree-4.0.0.tgz", 570 | "integrity": "sha512-kapdTCt1bjmspxStVKX6huolXVV5ZfyZguY1lcfhVVZstce3bqxH9mcLzNn3/mlgW6wQ732+0fuG9v7h0ZQoKg==", 571 | "dev": true, 572 | "requires": { 573 | "acorn": "^5.6.0", 574 | "acorn-jsx": "^4.1.1" 575 | } 576 | }, 577 | "esprima": { 578 | "version": "4.0.1", 579 | "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", 580 | "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", 581 | "dev": true 582 | }, 583 | "esquery": { 584 | "version": "1.0.1", 585 | "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.0.1.tgz", 586 | "integrity": "sha512-SmiyZ5zIWH9VM+SRUReLS5Q8a7GxtRdxEBVZpm98rJM7Sb+A9DVCndXfkeFUd3byderg+EbDkfnevfCwynWaNA==", 587 | "dev": true, 588 | "requires": { 589 | "estraverse": "^4.0.0" 590 | } 591 | }, 592 | "esrecurse": { 593 | "version": "4.2.1", 594 | "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz", 595 | "integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==", 596 | "dev": true, 597 | "requires": { 598 | "estraverse": "^4.1.0" 599 | } 600 | }, 601 | "estraverse": { 602 | "version": "4.2.0", 603 | "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz", 604 | "integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=", 605 | "dev": true 606 | }, 607 | "esutils": { 608 | "version": "2.0.2", 609 | "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", 610 | "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=", 611 | "dev": true 612 | }, 613 | "extend": { 614 | "version": "3.0.2", 615 | "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", 616 | "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" 617 | }, 618 | "external-editor": { 619 | "version": "2.2.0", 620 | "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-2.2.0.tgz", 621 | "integrity": "sha512-bSn6gvGxKt+b7+6TKEv1ZycHleA7aHhRHyAqJyp5pbUFuYYNIzpZnQDk7AsYckyWdEnTeAnay0aCy2aV6iTk9A==", 622 | "dev": true, 623 | "requires": { 624 | "chardet": "^0.4.0", 625 | "iconv-lite": "^0.4.17", 626 | "tmp": "^0.0.33" 627 | } 628 | }, 629 | "extsprintf": { 630 | "version": "1.3.0", 631 | "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", 632 | "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" 633 | }, 634 | "fast-deep-equal": { 635 | "version": "1.1.0", 636 | "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz", 637 | "integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=" 638 | }, 639 | "fast-json-stable-stringify": { 640 | "version": "2.0.0", 641 | "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", 642 | "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=" 643 | }, 644 | "fast-levenshtein": { 645 | "version": "2.0.6", 646 | "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", 647 | "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", 648 | "dev": true 649 | }, 650 | "figures": { 651 | "version": "2.0.0", 652 | "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", 653 | "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=", 654 | "dev": true, 655 | "requires": { 656 | "escape-string-regexp": "^1.0.5" 657 | } 658 | }, 659 | "file-entry-cache": { 660 | "version": "2.0.0", 661 | "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-2.0.0.tgz", 662 | "integrity": "sha1-w5KZDD5oR4PYOLjISkXYoEhFg2E=", 663 | "dev": true, 664 | "requires": { 665 | "flat-cache": "^1.2.1", 666 | "object-assign": "^4.0.1" 667 | } 668 | }, 669 | "flat-cache": { 670 | "version": "1.3.0", 671 | "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-1.3.0.tgz", 672 | "integrity": "sha1-0wMLMrOBVPTjt+nHCfSQ9++XxIE=", 673 | "dev": true, 674 | "requires": { 675 | "circular-json": "^0.3.1", 676 | "del": "^2.0.2", 677 | "graceful-fs": "^4.1.2", 678 | "write": "^0.2.1" 679 | }, 680 | "dependencies": { 681 | "circular-json": { 682 | "version": "0.3.3", 683 | "resolved": "https://registry.npmjs.org/circular-json/-/circular-json-0.3.3.tgz", 684 | "integrity": "sha512-UZK3NBx2Mca+b5LsG7bY183pHWt5Y1xts4P3Pz7ENTwGVnJOUWbRb3ocjvX7hx9tq/yTAdclXm9sZ38gNuem4A==", 685 | "dev": true 686 | } 687 | } 688 | }, 689 | "forever-agent": { 690 | "version": "0.6.1", 691 | "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", 692 | "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=" 693 | }, 694 | "form-data": { 695 | "version": "2.3.2", 696 | "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.2.tgz", 697 | "integrity": "sha1-SXBJi+YEwgwAXU9cI67NIda0kJk=", 698 | "requires": { 699 | "asynckit": "^0.4.0", 700 | "combined-stream": "1.0.6", 701 | "mime-types": "^2.1.12" 702 | } 703 | }, 704 | "fs.realpath": { 705 | "version": "1.0.0", 706 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", 707 | "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", 708 | "dev": true 709 | }, 710 | "functional-red-black-tree": { 711 | "version": "1.0.1", 712 | "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", 713 | "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", 714 | "dev": true 715 | }, 716 | "getpass": { 717 | "version": "0.1.7", 718 | "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", 719 | "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", 720 | "requires": { 721 | "assert-plus": "^1.0.0" 722 | } 723 | }, 724 | "github-publish-release": { 725 | "version": "5.0.0", 726 | "resolved": "https://registry.npmjs.org/github-publish-release/-/github-publish-release-5.0.0.tgz", 727 | "integrity": "sha512-LLE++vKy9mIUHJIln1xgmQoC33jgc0gjz9FujX0Pbl+L14feFYn18XBEBC5Ho4qmvsxROW9awShoO93uRPt0GA==", 728 | "dev": true, 729 | "requires": { 730 | "@octokit/rest": "^15.10.0", 731 | "lodash": "^4.17.10" 732 | } 733 | }, 734 | "glob": { 735 | "version": "6.0.4", 736 | "resolved": "https://registry.npmjs.org/glob/-/glob-6.0.4.tgz", 737 | "integrity": "sha1-DwiGD2oVUSey+t1PnOJLGqtuTSI=", 738 | "requires": { 739 | "inflight": "^1.0.4", 740 | "inherits": "2", 741 | "minimatch": "2 || 3", 742 | "once": "^1.3.0", 743 | "path-is-absolute": "^1.0.0" 744 | } 745 | }, 746 | "globals": { 747 | "version": "11.7.0", 748 | "resolved": "https://registry.npmjs.org/globals/-/globals-11.7.0.tgz", 749 | "integrity": "sha512-K8BNSPySfeShBQXsahYB/AbbWruVOTyVpgoIDnl8odPpeSfP2J5QO2oLFFdl2j7GfDCtZj2bMKar2T49itTPCg==", 750 | "dev": true 751 | }, 752 | "globby": { 753 | "version": "5.0.0", 754 | "resolved": "https://registry.npmjs.org/globby/-/globby-5.0.0.tgz", 755 | "integrity": "sha1-69hGZ8oNuzMLmbz8aOrCvFQ3Dg0=", 756 | "dev": true, 757 | "requires": { 758 | "array-union": "^1.0.1", 759 | "arrify": "^1.0.0", 760 | "glob": "^7.0.3", 761 | "object-assign": "^4.0.1", 762 | "pify": "^2.0.0", 763 | "pinkie-promise": "^2.0.0" 764 | }, 765 | "dependencies": { 766 | "glob": { 767 | "version": "7.1.2", 768 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", 769 | "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", 770 | "dev": true, 771 | "requires": { 772 | "fs.realpath": "^1.0.0", 773 | "inflight": "^1.0.4", 774 | "inherits": "2", 775 | "minimatch": "^3.0.4", 776 | "once": "^1.3.0", 777 | "path-is-absolute": "^1.0.0" 778 | } 779 | } 780 | } 781 | }, 782 | "graceful-fs": { 783 | "version": "4.1.11", 784 | "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", 785 | "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=", 786 | "dev": true 787 | }, 788 | "growl": { 789 | "version": "1.10.5", 790 | "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", 791 | "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", 792 | "dev": true 793 | }, 794 | "har-schema": { 795 | "version": "2.0.0", 796 | "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", 797 | "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=" 798 | }, 799 | "har-validator": { 800 | "version": "5.1.0", 801 | "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.0.tgz", 802 | "integrity": "sha512-+qnmNjI4OfH2ipQ9VQOw23bBd/ibtfbVdK2fYbY4acTDqKTW/YDp9McimZdDbG8iV9fZizUqQMD5xvriB146TA==", 803 | "requires": { 804 | "ajv": "^5.3.0", 805 | "har-schema": "^2.0.0" 806 | } 807 | }, 808 | "has-ansi": { 809 | "version": "2.0.0", 810 | "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", 811 | "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", 812 | "dev": true, 813 | "requires": { 814 | "ansi-regex": "^2.0.0" 815 | } 816 | }, 817 | "has-flag": { 818 | "version": "3.0.0", 819 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", 820 | "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", 821 | "dev": true 822 | }, 823 | "he": { 824 | "version": "1.1.1", 825 | "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz", 826 | "integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=", 827 | "dev": true 828 | }, 829 | "http-proxy-agent": { 830 | "version": "2.1.0", 831 | "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-2.1.0.tgz", 832 | "integrity": "sha512-qwHbBLV7WviBl0rQsOzH6o5lwyOIvwp/BdFnvVxXORldu5TmjFfjzBcWUWS5kWAZhmv+JtiDhSuQCp4sBfbIgg==", 833 | "dev": true, 834 | "requires": { 835 | "agent-base": "4", 836 | "debug": "3.1.0" 837 | } 838 | }, 839 | "http-signature": { 840 | "version": "1.2.0", 841 | "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", 842 | "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", 843 | "requires": { 844 | "assert-plus": "^1.0.0", 845 | "jsprim": "^1.2.2", 846 | "sshpk": "^1.7.0" 847 | } 848 | }, 849 | "https-proxy-agent": { 850 | "version": "2.2.1", 851 | "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.1.tgz", 852 | "integrity": "sha512-HPCTS1LW51bcyMYbxUIOO4HEOlQ1/1qRaFWcyxvwaqUS9TY88aoEuHUY33kuAh1YhVVaDQhLZsnPd+XNARWZlQ==", 853 | "dev": true, 854 | "requires": { 855 | "agent-base": "^4.1.0", 856 | "debug": "^3.1.0" 857 | } 858 | }, 859 | "iconv-lite": { 860 | "version": "0.4.23", 861 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz", 862 | "integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==", 863 | "dev": true, 864 | "requires": { 865 | "safer-buffer": ">= 2.1.2 < 3" 866 | } 867 | }, 868 | "ignore": { 869 | "version": "4.0.6", 870 | "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", 871 | "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", 872 | "dev": true 873 | }, 874 | "imurmurhash": { 875 | "version": "0.1.4", 876 | "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", 877 | "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", 878 | "dev": true 879 | }, 880 | "inflight": { 881 | "version": "1.0.6", 882 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", 883 | "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", 884 | "requires": { 885 | "once": "^1.3.0", 886 | "wrappy": "1" 887 | } 888 | }, 889 | "inherits": { 890 | "version": "2.0.3", 891 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", 892 | "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" 893 | }, 894 | "inquirer": { 895 | "version": "5.2.0", 896 | "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-5.2.0.tgz", 897 | "integrity": "sha512-E9BmnJbAKLPGonz0HeWHtbKf+EeSP93paWO3ZYoUpq/aowXvYGjjCSuashhXPpzbArIjBbji39THkxTz9ZeEUQ==", 898 | "dev": true, 899 | "requires": { 900 | "ansi-escapes": "^3.0.0", 901 | "chalk": "^2.0.0", 902 | "cli-cursor": "^2.1.0", 903 | "cli-width": "^2.0.0", 904 | "external-editor": "^2.1.0", 905 | "figures": "^2.0.0", 906 | "lodash": "^4.3.0", 907 | "mute-stream": "0.0.7", 908 | "run-async": "^2.2.0", 909 | "rxjs": "^5.5.2", 910 | "string-width": "^2.1.0", 911 | "strip-ansi": "^4.0.0", 912 | "through": "^2.3.6" 913 | } 914 | }, 915 | "is-fullwidth-code-point": { 916 | "version": "2.0.0", 917 | "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", 918 | "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", 919 | "dev": true 920 | }, 921 | "is-path-cwd": { 922 | "version": "1.0.0", 923 | "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-1.0.0.tgz", 924 | "integrity": "sha1-0iXsIxMuie3Tj9p2dHLmLmXxEG0=", 925 | "dev": true 926 | }, 927 | "is-path-in-cwd": { 928 | "version": "1.0.1", 929 | "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-1.0.1.tgz", 930 | "integrity": "sha512-FjV1RTW48E7CWM7eE/J2NJvAEEVektecDBVBE5Hh3nM1Jd0kvhHtX68Pr3xsDf857xt3Y4AkwVULK1Vku62aaQ==", 931 | "dev": true, 932 | "requires": { 933 | "is-path-inside": "^1.0.0" 934 | } 935 | }, 936 | "is-path-inside": { 937 | "version": "1.0.1", 938 | "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.1.tgz", 939 | "integrity": "sha1-jvW33lBDej/cprToZe96pVy0gDY=", 940 | "dev": true, 941 | "requires": { 942 | "path-is-inside": "^1.0.1" 943 | } 944 | }, 945 | "is-promise": { 946 | "version": "2.1.0", 947 | "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz", 948 | "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=", 949 | "dev": true 950 | }, 951 | "is-resolvable": { 952 | "version": "1.1.0", 953 | "resolved": "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.1.0.tgz", 954 | "integrity": "sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg==", 955 | "dev": true 956 | }, 957 | "is-typedarray": { 958 | "version": "1.0.0", 959 | "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", 960 | "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" 961 | }, 962 | "isarray": { 963 | "version": "1.0.0", 964 | "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", 965 | "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" 966 | }, 967 | "isexe": { 968 | "version": "2.0.0", 969 | "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", 970 | "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", 971 | "dev": true 972 | }, 973 | "isstream": { 974 | "version": "0.1.2", 975 | "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", 976 | "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" 977 | }, 978 | "js-tokens": { 979 | "version": "3.0.2", 980 | "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", 981 | "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=", 982 | "dev": true 983 | }, 984 | "js-yaml": { 985 | "version": "3.12.0", 986 | "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.12.0.tgz", 987 | "integrity": "sha512-PIt2cnwmPfL4hKNwqeiuz4bKfnzHTBv6HyVgjahA6mPLwPDzjDWrplJBMjHUFxku/N3FlmrbyPclad+I+4mJ3A==", 988 | "dev": true, 989 | "requires": { 990 | "argparse": "^1.0.7", 991 | "esprima": "^4.0.0" 992 | } 993 | }, 994 | "jsbn": { 995 | "version": "0.1.1", 996 | "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", 997 | "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", 998 | "optional": true 999 | }, 1000 | "json-schema": { 1001 | "version": "0.2.3", 1002 | "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", 1003 | "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=" 1004 | }, 1005 | "json-schema-traverse": { 1006 | "version": "0.3.1", 1007 | "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz", 1008 | "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=" 1009 | }, 1010 | "json-stable-stringify-without-jsonify": { 1011 | "version": "1.0.1", 1012 | "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", 1013 | "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", 1014 | "dev": true 1015 | }, 1016 | "json-stringify-safe": { 1017 | "version": "5.0.1", 1018 | "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", 1019 | "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" 1020 | }, 1021 | "jsprim": { 1022 | "version": "1.4.1", 1023 | "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", 1024 | "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", 1025 | "requires": { 1026 | "assert-plus": "1.0.0", 1027 | "extsprintf": "1.3.0", 1028 | "json-schema": "0.2.3", 1029 | "verror": "1.10.0" 1030 | } 1031 | }, 1032 | "levn": { 1033 | "version": "0.3.0", 1034 | "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", 1035 | "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", 1036 | "dev": true, 1037 | "requires": { 1038 | "prelude-ls": "~1.1.2", 1039 | "type-check": "~0.3.2" 1040 | } 1041 | }, 1042 | "lodash": { 1043 | "version": "4.17.10", 1044 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.10.tgz", 1045 | "integrity": "sha512-UejweD1pDoXu+AD825lWwp4ZGtSwgnpZxb3JDViD7StjQz+Nb/6l093lx4OQ0foGWNRoc19mWy7BzL+UAK2iVg==", 1046 | "dev": true 1047 | }, 1048 | "log4js": { 1049 | "version": "3.0.6", 1050 | "resolved": "https://registry.npmjs.org/log4js/-/log4js-3.0.6.tgz", 1051 | "integrity": "sha512-ezXZk6oPJCWL483zj64pNkMuY/NcRX5MPiB0zE6tjZM137aeusrOnW1ecxgF9cmwMWkBMhjteQxBPoZBh9FDxQ==", 1052 | "requires": { 1053 | "circular-json": "^0.5.5", 1054 | "date-format": "^1.2.0", 1055 | "debug": "^3.1.0", 1056 | "rfdc": "^1.1.2", 1057 | "streamroller": "0.7.0" 1058 | } 1059 | }, 1060 | "mime-db": { 1061 | "version": "1.35.0", 1062 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.35.0.tgz", 1063 | "integrity": "sha512-JWT/IcCTsB0Io3AhWUMjRqucrHSPsSf2xKLaRldJVULioggvkJvggZ3VXNNSRkCddE6D+BUI4HEIZIA2OjwIvg==" 1064 | }, 1065 | "mime-types": { 1066 | "version": "2.1.19", 1067 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.19.tgz", 1068 | "integrity": "sha512-P1tKYHVSZ6uFo26mtnve4HQFE3koh1UWVkp8YUC+ESBHe945xWSoXuHHiGarDqcEZ+whpCDnlNw5LON0kLo+sw==", 1069 | "requires": { 1070 | "mime-db": "~1.35.0" 1071 | } 1072 | }, 1073 | "mimic-fn": { 1074 | "version": "1.2.0", 1075 | "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", 1076 | "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", 1077 | "dev": true 1078 | }, 1079 | "minimatch": { 1080 | "version": "3.0.4", 1081 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", 1082 | "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", 1083 | "requires": { 1084 | "brace-expansion": "^1.1.7" 1085 | } 1086 | }, 1087 | "minimist": { 1088 | "version": "0.0.8", 1089 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", 1090 | "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" 1091 | }, 1092 | "mkdirp": { 1093 | "version": "0.5.1", 1094 | "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", 1095 | "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", 1096 | "requires": { 1097 | "minimist": "0.0.8" 1098 | } 1099 | }, 1100 | "mocha": { 1101 | "version": "5.2.0", 1102 | "resolved": "https://registry.npmjs.org/mocha/-/mocha-5.2.0.tgz", 1103 | "integrity": "sha512-2IUgKDhc3J7Uug+FxMXuqIyYzH7gJjXECKe/w43IGgQHTSj3InJi+yAA7T24L9bQMRKiUEHxEX37G5JpVUGLcQ==", 1104 | "dev": true, 1105 | "requires": { 1106 | "browser-stdout": "1.3.1", 1107 | "commander": "2.15.1", 1108 | "debug": "3.1.0", 1109 | "diff": "3.5.0", 1110 | "escape-string-regexp": "1.0.5", 1111 | "glob": "7.1.2", 1112 | "growl": "1.10.5", 1113 | "he": "1.1.1", 1114 | "minimatch": "3.0.4", 1115 | "mkdirp": "0.5.1", 1116 | "supports-color": "5.4.0" 1117 | }, 1118 | "dependencies": { 1119 | "commander": { 1120 | "version": "2.15.1", 1121 | "resolved": "https://registry.npmjs.org/commander/-/commander-2.15.1.tgz", 1122 | "integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==", 1123 | "dev": true 1124 | }, 1125 | "glob": { 1126 | "version": "7.1.2", 1127 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", 1128 | "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", 1129 | "dev": true, 1130 | "requires": { 1131 | "fs.realpath": "^1.0.0", 1132 | "inflight": "^1.0.4", 1133 | "inherits": "2", 1134 | "minimatch": "^3.0.4", 1135 | "once": "^1.3.0", 1136 | "path-is-absolute": "^1.0.0" 1137 | } 1138 | }, 1139 | "supports-color": { 1140 | "version": "5.4.0", 1141 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz", 1142 | "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", 1143 | "dev": true, 1144 | "requires": { 1145 | "has-flag": "^3.0.0" 1146 | } 1147 | } 1148 | } 1149 | }, 1150 | "ms": { 1151 | "version": "2.0.0", 1152 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", 1153 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" 1154 | }, 1155 | "mute-stream": { 1156 | "version": "0.0.7", 1157 | "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz", 1158 | "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=", 1159 | "dev": true 1160 | }, 1161 | "mv": { 1162 | "version": "2.1.1", 1163 | "resolved": "https://registry.npmjs.org/mv/-/mv-2.1.1.tgz", 1164 | "integrity": "sha1-rmzg1vbV4KT32JN5jQPB6pVZtqI=", 1165 | "requires": { 1166 | "mkdirp": "~0.5.1", 1167 | "ncp": "~2.0.0", 1168 | "rimraf": "~2.4.0" 1169 | }, 1170 | "dependencies": { 1171 | "rimraf": { 1172 | "version": "2.4.5", 1173 | "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.4.5.tgz", 1174 | "integrity": "sha1-7nEM5dk6j9uFb7Xqj/Di11k0sto=", 1175 | "requires": { 1176 | "glob": "^6.0.1" 1177 | } 1178 | } 1179 | } 1180 | }, 1181 | "natural-compare": { 1182 | "version": "1.4.0", 1183 | "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", 1184 | "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", 1185 | "dev": true 1186 | }, 1187 | "ncp": { 1188 | "version": "2.0.0", 1189 | "resolved": "https://registry.npmjs.org/ncp/-/ncp-2.0.0.tgz", 1190 | "integrity": "sha1-GVoh1sRuNh0vsSgbo4uR6d9727M=" 1191 | }, 1192 | "nice-try": { 1193 | "version": "1.0.5", 1194 | "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", 1195 | "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", 1196 | "dev": true 1197 | }, 1198 | "node-fetch": { 1199 | "version": "2.2.0", 1200 | "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.2.0.tgz", 1201 | "integrity": "sha512-OayFWziIxiHY8bCUyLX6sTpDH8Jsbp4FfYd1j1f7vZyfgkcOnAyM4oQR16f8a0s7Gl/viMGRey8eScYk4V4EZA==", 1202 | "dev": true 1203 | }, 1204 | "oauth-sign": { 1205 | "version": "0.9.0", 1206 | "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", 1207 | "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==" 1208 | }, 1209 | "object-assign": { 1210 | "version": "4.1.1", 1211 | "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", 1212 | "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", 1213 | "dev": true 1214 | }, 1215 | "once": { 1216 | "version": "1.4.0", 1217 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 1218 | "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", 1219 | "requires": { 1220 | "wrappy": "1" 1221 | } 1222 | }, 1223 | "onetime": { 1224 | "version": "2.0.1", 1225 | "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", 1226 | "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=", 1227 | "dev": true, 1228 | "requires": { 1229 | "mimic-fn": "^1.0.0" 1230 | } 1231 | }, 1232 | "optionator": { 1233 | "version": "0.8.2", 1234 | "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz", 1235 | "integrity": "sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=", 1236 | "dev": true, 1237 | "requires": { 1238 | "deep-is": "~0.1.3", 1239 | "fast-levenshtein": "~2.0.4", 1240 | "levn": "~0.3.0", 1241 | "prelude-ls": "~1.1.2", 1242 | "type-check": "~0.3.2", 1243 | "wordwrap": "~1.0.0" 1244 | } 1245 | }, 1246 | "os-tmpdir": { 1247 | "version": "1.0.2", 1248 | "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", 1249 | "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", 1250 | "dev": true 1251 | }, 1252 | "path-is-absolute": { 1253 | "version": "1.0.1", 1254 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", 1255 | "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" 1256 | }, 1257 | "path-is-inside": { 1258 | "version": "1.0.2", 1259 | "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", 1260 | "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=", 1261 | "dev": true 1262 | }, 1263 | "path-key": { 1264 | "version": "2.0.1", 1265 | "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", 1266 | "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", 1267 | "dev": true 1268 | }, 1269 | "performance-now": { 1270 | "version": "2.1.0", 1271 | "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", 1272 | "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" 1273 | }, 1274 | "pify": { 1275 | "version": "2.3.0", 1276 | "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", 1277 | "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", 1278 | "dev": true 1279 | }, 1280 | "pinkie": { 1281 | "version": "2.0.4", 1282 | "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", 1283 | "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=", 1284 | "dev": true 1285 | }, 1286 | "pinkie-promise": { 1287 | "version": "2.0.1", 1288 | "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", 1289 | "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", 1290 | "dev": true, 1291 | "requires": { 1292 | "pinkie": "^2.0.0" 1293 | } 1294 | }, 1295 | "pluralize": { 1296 | "version": "7.0.0", 1297 | "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-7.0.0.tgz", 1298 | "integrity": "sha512-ARhBOdzS3e41FbkW/XWrTEtukqqLoK5+Z/4UeDaLuSW+39JPeFgs4gCGqsrJHVZX0fUrx//4OF0K1CUGwlIFow==", 1299 | "dev": true 1300 | }, 1301 | "prelude-ls": { 1302 | "version": "1.1.2", 1303 | "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", 1304 | "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", 1305 | "dev": true 1306 | }, 1307 | "prettier": { 1308 | "version": "1.14.2", 1309 | "resolved": "https://registry.npmjs.org/prettier/-/prettier-1.14.2.tgz", 1310 | "integrity": "sha512-McHPg0n1pIke+A/4VcaS2en+pTNjy4xF+Uuq86u/5dyDO59/TtFZtQ708QIRkEZ3qwKz3GVkVa6mpxK/CpB8Rg==", 1311 | "dev": true 1312 | }, 1313 | "process-nextick-args": { 1314 | "version": "2.0.0", 1315 | "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", 1316 | "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==" 1317 | }, 1318 | "progress": { 1319 | "version": "2.0.0", 1320 | "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.0.tgz", 1321 | "integrity": "sha1-ihvjZr+Pwj2yvSPxDG/pILQ4nR8=", 1322 | "dev": true 1323 | }, 1324 | "psl": { 1325 | "version": "1.1.29", 1326 | "resolved": "https://registry.npmjs.org/psl/-/psl-1.1.29.tgz", 1327 | "integrity": "sha512-AeUmQ0oLN02flVHXWh9sSJF7mcdFq0ppid/JkErufc3hGIV/AMa8Fo9VgDo/cT2jFdOWoFvHp90qqBH54W+gjQ==" 1328 | }, 1329 | "punycode": { 1330 | "version": "1.4.1", 1331 | "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", 1332 | "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" 1333 | }, 1334 | "qs": { 1335 | "version": "6.5.2", 1336 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", 1337 | "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" 1338 | }, 1339 | "readable-stream": { 1340 | "version": "2.3.6", 1341 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", 1342 | "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", 1343 | "requires": { 1344 | "core-util-is": "~1.0.0", 1345 | "inherits": "~2.0.3", 1346 | "isarray": "~1.0.0", 1347 | "process-nextick-args": "~2.0.0", 1348 | "safe-buffer": "~5.1.1", 1349 | "string_decoder": "~1.1.1", 1350 | "util-deprecate": "~1.0.1" 1351 | } 1352 | }, 1353 | "regexpp": { 1354 | "version": "2.0.0", 1355 | "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-2.0.0.tgz", 1356 | "integrity": "sha512-g2FAVtR8Uh8GO1Nv5wpxW7VFVwHcCEr4wyA8/MHiRkO8uHoR5ntAA8Uq3P1vvMTX/BeQiRVSpDGLd+Wn5HNOTA==", 1357 | "dev": true 1358 | }, 1359 | "request": { 1360 | "version": "2.88.0", 1361 | "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz", 1362 | "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==", 1363 | "requires": { 1364 | "aws-sign2": "~0.7.0", 1365 | "aws4": "^1.8.0", 1366 | "caseless": "~0.12.0", 1367 | "combined-stream": "~1.0.6", 1368 | "extend": "~3.0.2", 1369 | "forever-agent": "~0.6.1", 1370 | "form-data": "~2.3.2", 1371 | "har-validator": "~5.1.0", 1372 | "http-signature": "~1.2.0", 1373 | "is-typedarray": "~1.0.0", 1374 | "isstream": "~0.1.2", 1375 | "json-stringify-safe": "~5.0.1", 1376 | "mime-types": "~2.1.19", 1377 | "oauth-sign": "~0.9.0", 1378 | "performance-now": "^2.1.0", 1379 | "qs": "~6.5.2", 1380 | "safe-buffer": "^5.1.2", 1381 | "tough-cookie": "~2.4.3", 1382 | "tunnel-agent": "^0.6.0", 1383 | "uuid": "^3.3.2" 1384 | } 1385 | }, 1386 | "require-uncached": { 1387 | "version": "1.0.3", 1388 | "resolved": "https://registry.npmjs.org/require-uncached/-/require-uncached-1.0.3.tgz", 1389 | "integrity": "sha1-Tg1W1slmL9MeQwEcS5WqSZVUIdM=", 1390 | "dev": true, 1391 | "requires": { 1392 | "caller-path": "^0.1.0", 1393 | "resolve-from": "^1.0.0" 1394 | } 1395 | }, 1396 | "resolve-from": { 1397 | "version": "1.0.1", 1398 | "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-1.0.1.tgz", 1399 | "integrity": "sha1-Jsv+k10a7uq7Kbw/5a6wHpPUQiY=", 1400 | "dev": true 1401 | }, 1402 | "restore-cursor": { 1403 | "version": "2.0.0", 1404 | "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", 1405 | "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=", 1406 | "dev": true, 1407 | "requires": { 1408 | "onetime": "^2.0.0", 1409 | "signal-exit": "^3.0.2" 1410 | } 1411 | }, 1412 | "rfdc": { 1413 | "version": "1.1.2", 1414 | "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.1.2.tgz", 1415 | "integrity": "sha512-92ktAgvZhBzYTIK0Mja9uen5q5J3NRVMoDkJL2VMwq6SXjVCgqvQeVP2XAaUY6HT+XpQYeLSjb3UoitBryKmdA==" 1416 | }, 1417 | "rimraf": { 1418 | "version": "2.6.2", 1419 | "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz", 1420 | "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==", 1421 | "dev": true, 1422 | "requires": { 1423 | "glob": "^7.0.5" 1424 | }, 1425 | "dependencies": { 1426 | "glob": { 1427 | "version": "7.1.2", 1428 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", 1429 | "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", 1430 | "dev": true, 1431 | "requires": { 1432 | "fs.realpath": "^1.0.0", 1433 | "inflight": "^1.0.4", 1434 | "inherits": "2", 1435 | "minimatch": "^3.0.4", 1436 | "once": "^1.3.0", 1437 | "path-is-absolute": "^1.0.0" 1438 | } 1439 | } 1440 | } 1441 | }, 1442 | "run-async": { 1443 | "version": "2.3.0", 1444 | "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.3.0.tgz", 1445 | "integrity": "sha1-A3GrSuC91yDUFm19/aZP96RFpsA=", 1446 | "dev": true, 1447 | "requires": { 1448 | "is-promise": "^2.1.0" 1449 | } 1450 | }, 1451 | "rxjs": { 1452 | "version": "5.5.11", 1453 | "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-5.5.11.tgz", 1454 | "integrity": "sha512-3bjO7UwWfA2CV7lmwYMBzj4fQ6Cq+ftHc2MvUe+WMS7wcdJ1LosDWmdjPQanYp2dBRj572p7PeU81JUxHKOcBA==", 1455 | "dev": true, 1456 | "requires": { 1457 | "symbol-observable": "1.0.1" 1458 | } 1459 | }, 1460 | "safe-buffer": { 1461 | "version": "5.1.2", 1462 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", 1463 | "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" 1464 | }, 1465 | "safer-buffer": { 1466 | "version": "2.1.2", 1467 | "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", 1468 | "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" 1469 | }, 1470 | "semver": { 1471 | "version": "5.5.0", 1472 | "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz", 1473 | "integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==", 1474 | "dev": true 1475 | }, 1476 | "shebang-command": { 1477 | "version": "1.2.0", 1478 | "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", 1479 | "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", 1480 | "dev": true, 1481 | "requires": { 1482 | "shebang-regex": "^1.0.0" 1483 | } 1484 | }, 1485 | "shebang-regex": { 1486 | "version": "1.0.0", 1487 | "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", 1488 | "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", 1489 | "dev": true 1490 | }, 1491 | "signal-exit": { 1492 | "version": "3.0.2", 1493 | "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", 1494 | "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", 1495 | "dev": true 1496 | }, 1497 | "slice-ansi": { 1498 | "version": "1.0.0", 1499 | "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-1.0.0.tgz", 1500 | "integrity": "sha512-POqxBK6Lb3q6s047D/XsDVNPnF9Dl8JSaqe9h9lURl0OdNqy/ujDrOiIHtsqXMGbWWTIomRzAMaTyawAU//Reg==", 1501 | "dev": true, 1502 | "requires": { 1503 | "is-fullwidth-code-point": "^2.0.0" 1504 | } 1505 | }, 1506 | "sprintf-js": { 1507 | "version": "1.0.3", 1508 | "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", 1509 | "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", 1510 | "dev": true 1511 | }, 1512 | "sshpk": { 1513 | "version": "1.14.2", 1514 | "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.14.2.tgz", 1515 | "integrity": "sha1-xvxhZIo9nE52T9P8306hBeSSupg=", 1516 | "requires": { 1517 | "asn1": "~0.2.3", 1518 | "assert-plus": "^1.0.0", 1519 | "bcrypt-pbkdf": "^1.0.0", 1520 | "dashdash": "^1.12.0", 1521 | "ecc-jsbn": "~0.1.1", 1522 | "getpass": "^0.1.1", 1523 | "jsbn": "~0.1.0", 1524 | "safer-buffer": "^2.0.2", 1525 | "tweetnacl": "~0.14.0" 1526 | } 1527 | }, 1528 | "streamroller": { 1529 | "version": "0.7.0", 1530 | "resolved": "https://registry.npmjs.org/streamroller/-/streamroller-0.7.0.tgz", 1531 | "integrity": "sha512-WREzfy0r0zUqp3lGO096wRuUp7ho1X6uo/7DJfTlEi0Iv/4gT7YHqXDjKC2ioVGBZtE8QzsQD9nx1nIuoZ57jQ==", 1532 | "requires": { 1533 | "date-format": "^1.2.0", 1534 | "debug": "^3.1.0", 1535 | "mkdirp": "^0.5.1", 1536 | "readable-stream": "^2.3.0" 1537 | } 1538 | }, 1539 | "string-width": { 1540 | "version": "2.1.1", 1541 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", 1542 | "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", 1543 | "dev": true, 1544 | "requires": { 1545 | "is-fullwidth-code-point": "^2.0.0", 1546 | "strip-ansi": "^4.0.0" 1547 | } 1548 | }, 1549 | "string_decoder": { 1550 | "version": "1.1.1", 1551 | "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", 1552 | "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", 1553 | "requires": { 1554 | "safe-buffer": "~5.1.0" 1555 | } 1556 | }, 1557 | "strip-ansi": { 1558 | "version": "4.0.0", 1559 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", 1560 | "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", 1561 | "dev": true, 1562 | "requires": { 1563 | "ansi-regex": "^3.0.0" 1564 | }, 1565 | "dependencies": { 1566 | "ansi-regex": { 1567 | "version": "3.0.0", 1568 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", 1569 | "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", 1570 | "dev": true 1571 | } 1572 | } 1573 | }, 1574 | "strip-json-comments": { 1575 | "version": "2.0.1", 1576 | "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", 1577 | "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", 1578 | "dev": true 1579 | }, 1580 | "supports-color": { 1581 | "version": "2.0.0", 1582 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", 1583 | "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", 1584 | "dev": true 1585 | }, 1586 | "symbol-observable": { 1587 | "version": "1.0.1", 1588 | "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.0.1.tgz", 1589 | "integrity": "sha1-g0D8RwLDEi310iKI+IKD9RPT/dQ=", 1590 | "dev": true 1591 | }, 1592 | "table": { 1593 | "version": "4.0.3", 1594 | "resolved": "https://registry.npmjs.org/table/-/table-4.0.3.tgz", 1595 | "integrity": "sha512-S7rnFITmBH1EnyKcvxBh1LjYeQMmnZtCXSEbHcH6S0NoKit24ZuFO/T1vDcLdYsLQkM188PVVhQmzKIuThNkKg==", 1596 | "dev": true, 1597 | "requires": { 1598 | "ajv": "^6.0.1", 1599 | "ajv-keywords": "^3.0.0", 1600 | "chalk": "^2.1.0", 1601 | "lodash": "^4.17.4", 1602 | "slice-ansi": "1.0.0", 1603 | "string-width": "^2.1.1" 1604 | }, 1605 | "dependencies": { 1606 | "ajv": { 1607 | "version": "6.5.3", 1608 | "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.5.3.tgz", 1609 | "integrity": "sha512-LqZ9wY+fx3UMiiPd741yB2pj3hhil+hQc8taf4o2QGRFpWgZ2V5C8HA165DY9sS3fJwsk7uT7ZlFEyC3Ig3lLg==", 1610 | "dev": true, 1611 | "requires": { 1612 | "fast-deep-equal": "^2.0.1", 1613 | "fast-json-stable-stringify": "^2.0.0", 1614 | "json-schema-traverse": "^0.4.1", 1615 | "uri-js": "^4.2.2" 1616 | } 1617 | }, 1618 | "fast-deep-equal": { 1619 | "version": "2.0.1", 1620 | "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", 1621 | "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=", 1622 | "dev": true 1623 | }, 1624 | "json-schema-traverse": { 1625 | "version": "0.4.1", 1626 | "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", 1627 | "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", 1628 | "dev": true 1629 | } 1630 | } 1631 | }, 1632 | "text-table": { 1633 | "version": "0.2.0", 1634 | "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", 1635 | "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", 1636 | "dev": true 1637 | }, 1638 | "through": { 1639 | "version": "2.3.8", 1640 | "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", 1641 | "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", 1642 | "dev": true 1643 | }, 1644 | "tmp": { 1645 | "version": "0.0.33", 1646 | "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", 1647 | "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", 1648 | "dev": true, 1649 | "requires": { 1650 | "os-tmpdir": "~1.0.2" 1651 | } 1652 | }, 1653 | "tough-cookie": { 1654 | "version": "2.4.3", 1655 | "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz", 1656 | "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==", 1657 | "requires": { 1658 | "psl": "^1.1.24", 1659 | "punycode": "^1.4.1" 1660 | } 1661 | }, 1662 | "tunnel-agent": { 1663 | "version": "0.6.0", 1664 | "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", 1665 | "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", 1666 | "requires": { 1667 | "safe-buffer": "^5.0.1" 1668 | } 1669 | }, 1670 | "tweetnacl": { 1671 | "version": "0.14.5", 1672 | "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", 1673 | "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", 1674 | "optional": true 1675 | }, 1676 | "type-check": { 1677 | "version": "0.3.2", 1678 | "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", 1679 | "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", 1680 | "dev": true, 1681 | "requires": { 1682 | "prelude-ls": "~1.1.2" 1683 | } 1684 | }, 1685 | "uri-js": { 1686 | "version": "4.2.2", 1687 | "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", 1688 | "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", 1689 | "dev": true, 1690 | "requires": { 1691 | "punycode": "^2.1.0" 1692 | }, 1693 | "dependencies": { 1694 | "punycode": { 1695 | "version": "2.1.1", 1696 | "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", 1697 | "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", 1698 | "dev": true 1699 | } 1700 | } 1701 | }, 1702 | "url-template": { 1703 | "version": "2.0.8", 1704 | "resolved": "https://registry.npmjs.org/url-template/-/url-template-2.0.8.tgz", 1705 | "integrity": "sha1-/FZaPMy/93MMd19WQflVV5FDnyE=", 1706 | "dev": true 1707 | }, 1708 | "util-deprecate": { 1709 | "version": "1.0.2", 1710 | "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", 1711 | "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" 1712 | }, 1713 | "uuid": { 1714 | "version": "3.3.2", 1715 | "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", 1716 | "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==" 1717 | }, 1718 | "verror": { 1719 | "version": "1.10.0", 1720 | "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", 1721 | "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", 1722 | "requires": { 1723 | "assert-plus": "^1.0.0", 1724 | "core-util-is": "1.0.2", 1725 | "extsprintf": "^1.2.0" 1726 | } 1727 | }, 1728 | "which": { 1729 | "version": "1.3.1", 1730 | "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", 1731 | "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", 1732 | "dev": true, 1733 | "requires": { 1734 | "isexe": "^2.0.0" 1735 | } 1736 | }, 1737 | "wordwrap": { 1738 | "version": "1.0.0", 1739 | "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", 1740 | "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=", 1741 | "dev": true 1742 | }, 1743 | "wrappy": { 1744 | "version": "1.0.2", 1745 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 1746 | "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" 1747 | }, 1748 | "write": { 1749 | "version": "0.2.1", 1750 | "resolved": "https://registry.npmjs.org/write/-/write-0.2.1.tgz", 1751 | "integrity": "sha1-X8A4KOJkzqP+kUVUdvejxWbLB1c=", 1752 | "dev": true, 1753 | "requires": { 1754 | "mkdirp": "^0.5.1" 1755 | } 1756 | } 1757 | } 1758 | } 1759 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "npm-proxy-cache", 3 | "version": "2.2.0", 4 | "publishConfig": { 5 | "registry": "https://registry.npmjs.org/" 6 | }, 7 | "dependencies": { 8 | "commander": "^2.17.1", 9 | "log4js": "^3.0.6", 10 | "mkdirp": "^0.5.1", 11 | "mv": "^2.1.1", 12 | "request": "^2.88.0" 13 | }, 14 | "description": "HTTP/HTTPS caching proxy for work with `npm` utility / registry", 15 | "main": "index.js", 16 | "devDependencies": { 17 | "eslint": "^5.4.0", 18 | "github-publish-release": "^5.0.0", 19 | "mocha": "^5.2.0", 20 | "rimraf": "^2.6.2" 21 | }, 22 | "scripts": { 23 | "lint": "scripts/lint", 24 | "release": "scripts/release", 25 | "test": "scripts/test" 26 | }, 27 | "repository": { 28 | "type": "git", 29 | "url": "git@github.com:runk/npm-proxy-cache.git" 30 | }, 31 | "bin": { 32 | "npm-proxy-cache": "./bin/npm-proxy-cache" 33 | }, 34 | "keywords": [ 35 | "npm", 36 | "proxy", 37 | "registry", 38 | "cache", 39 | "proxy server", 40 | "forward proxy" 41 | ], 42 | "author": "Dmitry Shirokov ", 43 | "contributors": [ 44 | "@someuser77", 45 | "@FrankZZ" 46 | ], 47 | "license": "MIT", 48 | "bugs": { 49 | "url": "https://github.com/runk/npm-proxy-cache/issues" 50 | }, 51 | "engines": { 52 | "node": ">=6" 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /scripts/lint: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Exit immediately if a pipeline returns non-zero status 4 | # http://www.gnu.org/software/bash/manual/bashref.html#The-Set-Builtin 5 | set -e 6 | 7 | LIST="." 8 | 9 | if [ -n "$npm_config_staged" ]; then 10 | LIST=`git diff --cached --name-only | grep -E '\\.(js|json)$'` 11 | fi 12 | 13 | FIX="" 14 | if [ -n "$npm_config_fix" ]; then 15 | FIX="--fix" 16 | fi 17 | 18 | if [ "$LIST" ]; then eslint $FIX --config .eslintrc $LIST; fi 19 | -------------------------------------------------------------------------------- /scripts/release: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Exit immediately if a pipeline returns non-zero status 4 | # http://www.gnu.org/software/bash/manual/bashref.html#The-Set-Builtin 5 | set -e 6 | 7 | npm run lint 8 | npm test 9 | 10 | if [ -n "$npm_config_major" ]; then SEMVER="major" 11 | elif [ -n "$npm_config_patch" ]; then SEMVER="patch" 12 | else SEMVER="minor"; fi 13 | 14 | npm version $SEMVER 15 | github-publish-release 16 | git push --follow-tags 17 | npm publish 18 | -------------------------------------------------------------------------------- /scripts/test: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Exit immediately if a pipeline returns non-zero status 4 | # http://www.gnu.org/software/bash/manual/bashref.html#The-Set-Builtin 5 | set -e 6 | 7 | export NODE_ENV=test 8 | 9 | # --watch option 10 | if [ -n "$npm_config_watch" ]; then WATCH="--watch"; else WATCH=""; fi 11 | 12 | # --coverage option 13 | if [ -n "$npm_config_coverage" ]; then RUNNER="nyc mocha"; else RUNNER="mocha"; fi 14 | 15 | 16 | $RUNNER \ 17 | --check-leaks \ 18 | --bail \ 19 | --grep ${npm_config_grep:-''} \ 20 | --recursive \ 21 | --timeout 1s \ 22 | --inline-diffs \ 23 | $WATCH \ 24 | ${npm_config_file:-'test'} 25 | -------------------------------------------------------------------------------- /test/cache.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const assert = require('assert'); 3 | const path = require('path'); 4 | const fs = require('fs'); 5 | const rimraf = require('rimraf'); 6 | const Cache = require('../lib/cache'); 7 | 8 | describe('cache', function() { 9 | 10 | let opts; 11 | const filepath = path.join(__dirname, '/cache'); 12 | 13 | const dummy = 'Lorem ipsum dolor sit amet ...\n'; 14 | 15 | beforeEach(function() { 16 | opts = { path: filepath, ttl: 10 }; 17 | }); 18 | 19 | before(function(done) { 20 | rimraf(filepath, done); 21 | }); 22 | 23 | after(function(done) { 24 | rimraf(filepath, done); 25 | }); 26 | 27 | 28 | describe('constructor()', function() { 29 | it('should create new instance of Cache', function() { 30 | const cache = new Cache(opts); 31 | assert(cache instanceof Cache); 32 | }); 33 | }); 34 | 35 | 36 | describe('write()', function() { 37 | const readStream = fs.createReadStream(path.join(__dirname, 'dummy.data')); 38 | 39 | it('should write the file', function(done) { 40 | const cache = new Cache(opts); 41 | const key = '/-/foo/bar.dat'; 42 | const pathInfo = cache.getPath(key); 43 | cache.write(key, readStream, function(err, meta) { 44 | assert.equal(meta.size, 31); 45 | assert.equal(meta.status, 4); 46 | assert.equal(fs.readFileSync(pathInfo.full, 'utf8'), dummy); 47 | done(); 48 | }); 49 | }); 50 | 51 | it('should handle locks', function(done) { 52 | const cache = new Cache(opts); 53 | const readStream = fs.createReadStream(path.join(__dirname, 'dummy.data')); 54 | const key = '/-/foo/baz.dat'; 55 | const pathInfo = cache.getPath(key); 56 | cache.write(key, readStream, function(err, meta) { 57 | assert(!cache.locks[key], 'Lock should be released'); 58 | assert(fs.existsSync(pathInfo.full)); 59 | done(); 60 | }); 61 | 62 | assert(cache.locks[key], 'Lock should be set'); 63 | assert(!fs.existsSync(pathInfo.full)); 64 | }); 65 | }); 66 | 67 | 68 | describe('read()', function() { 69 | it('should create new read stream', function(done) { 70 | const cache = new Cache(opts); 71 | const readable = cache.read('/-/foo/bar.dat'); 72 | 73 | readable.setEncoding('utf8'); 74 | readable.on('data', function(data) { 75 | assert.equal(typeof data, 'string'); 76 | assert.equal(data.toString(), dummy); 77 | done(); 78 | }); 79 | 80 | readable.read(); 81 | }); 82 | }); 83 | 84 | 85 | describe('meta()', function() { 86 | it('should return meta', function(done) { 87 | const cache = new Cache(opts); 88 | cache.meta('/-/foo/bar.dat', function(err, meta) { 89 | if (err) return done(err); 90 | assert.equal(meta.size, 31); 91 | assert.equal(meta.type, 'application/octet-stream'); 92 | assert.equal(meta.status, Cache.FRESH); 93 | done(); 94 | }); 95 | }); 96 | 97 | it('should return NOT_FOUND status', function(done) { 98 | const cache = new Cache(opts); 99 | cache.meta('/la/la', function(err, meta) { 100 | if (err) return done(err); 101 | assert.deepEqual(meta, {status: Cache.NOT_FOUND}); 102 | done(); 103 | }); 104 | }); 105 | }); 106 | 107 | 108 | describe('getPath()', function() { 109 | it('return path info', function() { 110 | const cache = new Cache(opts); 111 | const filepath = cache.getPath('/foo/bar/-/../baz.tgz'); 112 | assert.equal(filepath.dir, opts.path + '/f/a/7'); 113 | assert.equal(filepath.file, 'fa7bf9eb.tgz'); 114 | assert.equal(filepath.full, opts.path + '/f/a/7/fa7bf9eb.tgz'); 115 | assert.equal(filepath.rel, 'f/a/7/fa7bf9eb.tgz'); 116 | }); 117 | 118 | describe ('given the friendlyNames option is set', function() { 119 | let cache; 120 | 121 | beforeEach(function() { 122 | opts.friendlyNames = true; 123 | cache = new Cache(opts); 124 | }); 125 | 126 | it('uses just the module name from the URL', function() { 127 | const filepath = cache.getPath('http://registry/test'); 128 | assert.equal(filepath.dir, opts.path + '/t/e/s'); 129 | assert.equal(filepath.file, 'test'); 130 | assert.equal(filepath.full, opts.path + '/t/e/s/test'); 131 | assert.equal(filepath.rel, 't/e/s/test'); 132 | }); 133 | 134 | it('cuts the file extension from the module URL', function() { 135 | const filepath = cache.getPath('http://registry/test.tgz'); 136 | assert.equal(filepath.dir, opts.path + '/t/e/s'); 137 | assert.equal(filepath.file, 'test.tgz'); 138 | assert.equal(filepath.full, opts.path + '/t/e/s/test.tgz'); 139 | assert.equal(filepath.rel, 't/e/s/test.tgz'); 140 | }); 141 | 142 | it('cuts the version suffix from the module URL', function() { 143 | const filepath = cache.getPath('http://registry/test-1.2.3.tgz'); 144 | assert.equal(filepath.dir, opts.path + '/t/e/s'); 145 | assert.equal(filepath.file, 'test-1.2.3.tgz'); 146 | assert.equal(filepath.full, opts.path + '/t/e/s/test-1.2.3.tgz'); 147 | assert.equal(filepath.rel, 't/e/s/test-1.2.3.tgz'); 148 | }); 149 | 150 | it('uses hyphens instead of dots in the directory structure', function() { 151 | const filepath = cache.getPath('http://registry/te.st'); 152 | assert.equal(filepath.dir, opts.path + '/t/e/-'); 153 | assert.equal(filepath.file, 'te.st'); 154 | assert.equal(filepath.full, opts.path + '/t/e/-/te.st'); 155 | assert.equal(filepath.rel, 't/e/-/te.st'); 156 | }); 157 | 158 | it('uses short direcory structure for short module name', function() { 159 | const filepath = cache.getPath('http://registry/q'); 160 | assert.equal(filepath.dir, opts.path + '/q/-/-'); 161 | assert.equal(filepath.file, 'q'); 162 | assert.equal(filepath.full, opts.path + '/q/-/-/q'); 163 | assert.equal(filepath.rel, 'q/-/-/q'); 164 | }); 165 | 166 | it('cuts the version suffix and file extension from short module names', function() { 167 | const filepath = cache.getPath('http://registry/q-1.2.3.tgz'); 168 | assert.equal(filepath.dir, opts.path + '/q/-/-'); 169 | assert.equal(filepath.file, 'q-1.2.3.tgz'); 170 | assert.equal(filepath.full, opts.path + '/q/-/-/q-1.2.3.tgz'); 171 | assert.equal(filepath.rel, 'q/-/-/q-1.2.3.tgz'); 172 | }); 173 | }); 174 | }); 175 | 176 | }); 177 | -------------------------------------------------------------------------------- /test/dummy.data: -------------------------------------------------------------------------------- 1 | Lorem ipsum dolor sit amet ... 2 | -------------------------------------------------------------------------------- /test/proxy.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const assert = require('assert'); 3 | const http = require('http'); 4 | const request = require('request'); 5 | 6 | 7 | describe('proxy', function() { 8 | 9 | const MOCK_PORT = 5000; 10 | const PROXY_PORT = 32403; 11 | const calledMock = {}; 12 | let proxy; 13 | 14 | const server = http.createServer(function(req, res) { 15 | calledMock[req.method + req.url] = calledMock[req.method + req.url] + 1 || 1; 16 | res.setHeader('Content-Type', 'application/json'); 17 | res.end(JSON.stringify({ random: Math.random() })); 18 | }); 19 | 20 | before(function(done) { 21 | server.listen(MOCK_PORT, function(err) { 22 | if (err) return done(err); 23 | 24 | proxy = require('../lib/proxy').powerup({ 25 | port: PROXY_PORT, 26 | metadataExcluded: true, 27 | ttl: 1800 28 | }); 29 | done(); 30 | }); 31 | }); 32 | 33 | after(function() { 34 | proxy.httpServer.close(); 35 | proxy.httpsServer.close(); 36 | server.close(); 37 | }); 38 | 39 | function performNpmRequest(fn) { 40 | request({ 41 | baseUrl: 'http://127.0.0.1:' + PROXY_PORT, 42 | uri: '/test', 43 | headers: { 44 | accept: 'application/json', 45 | host: '127.0.0.1:' + MOCK_PORT 46 | } 47 | }, fn); 48 | } 49 | 50 | it('should not cache metadata requests', function(done) { 51 | performNpmRequest(function(err, res, resBody) { 52 | if (err) return done(err); 53 | 54 | const originalBody = resBody; 55 | 56 | performNpmRequest(function(err, res, resBody) { 57 | if (err) return done(err); 58 | assert.notDeepEqual(resBody, originalBody); 59 | assert.equal(calledMock['GET/test'], 2, 'Mock is not called twice'); 60 | done(); 61 | }); 62 | }); 63 | }); 64 | }); 65 | --------------------------------------------------------------------------------