From c0d39055f2b5bc2fb6bf2b46c7f80980846fe73a Mon Sep 17 00:00:00 2001 From: brianc Date: Tue, 7 Jun 2016 19:16:19 -0500 Subject: [PATCH 01/95] Initial commit --- .gitignore | 1 + index.js | 118 ++++++++++++++++++++++++++++++++++++++++++++++++ package.json | 40 ++++++++++++++++ test/index.js | 111 +++++++++++++++++++++++++++++++++++++++++++++ test/mocha.opts | 2 + 5 files changed, 272 insertions(+) create mode 100644 .gitignore create mode 100644 index.js create mode 100644 package.json create mode 100644 test/index.js create mode 100644 test/mocha.opts diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..3c3629e6 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +node_modules diff --git a/index.js b/index.js new file mode 100644 index 00000000..7c102d8a --- /dev/null +++ b/index.js @@ -0,0 +1,118 @@ +var genericPool = require('generic-pool') +var util = require('util') +var EventEmitter = require('events').EventEmitter +var debug = require('debug') + +var Pool = module.exports = function(options) { + EventEmitter.call(this) + this.options = options || {} + this.log = this.options.log || debug('pg:pool') + this.Client = this.options.Client || require('pg').Client + this.Promise = this.options.Promise || Promise + + this.options.max = this.options.max || this.options.poolSize || 10 + this.options.create = this.options.create || this._create.bind(this) + this.options.destroy = this.options.destroy || this._destroy.bind(this) + this.pool = new genericPool.Pool(this.options) +} + +util.inherits(Pool, EventEmitter) + +Pool.prototype._destroy = function(client) { + if (client._destroying) return + client._destroying = true + client.end() +} + +Pool.prototype._create = function(cb) { + this.log('connecting new client') + var client = new this.Client(this.options) + + client.on('error', function(e) { + this.log('connected client error:', e) + this.pool.destroy(client) + e.client = client + this.emit('error', e) + }.bind(this)) + + client.connect(function(err) { + this.log('client connected') + if (err) { + this.log('client connection error:', e) + cb(err) + } + + client.queryAsync = function(text, values) { + return new this.Promise((resolve, reject) => { + client.query(text, values, function(err, res) { + err ? reject(err) : resolve(res) + }) + }) + }.bind(this) + + cb(err, err ? null : client) + }.bind(this)) +} + +Pool.prototype.connect = function(cb) { + return new this.Promise(function(resolve, reject) { + this.log('acquire client begin') + this.pool.acquire(function(err, client) { + if (err) { + this.log('acquire client. error:', err) + if (cb) { + cb(err, null, function() { }) + } + return reject(err) + } + + this.log('acquire client') + + client.release = function(err) { + if (err) { + this.log('release client. error:', err) + this.pool.destroy(client) + } + this.log('release client') + delete client.release + this.pool.release(client) + }.bind(this) + + if (cb) { + cb(null, client, client.release) + } + + return resolve(client) + }.bind(this)) + }.bind(this)) +} + +Pool.prototype.take = Pool.prototype.connect + +Pool.prototype.query = function(text, values) { + return this.take().then(function(client) { + return client.queryAsync(text, values) + .then(function(res) { + client.release() + return res + }).catch(function(error) { + client.release(error) + throw error + }) + }) +} + +Pool.prototype.end = function(cb) { + this.log('draining pool') + return new this.Promise(function(resolve, reject) { + this.pool.drain(function() { + this.log('pool drained, calling destroy all now') + this.pool.destroyAllNow(function() { + if(cb) { + cb() + } + resolve() + }) + }.bind(this)) + }.bind(this)) +} diff --git a/package.json b/package.json new file mode 100644 index 00000000..7d8b8f09 --- /dev/null +++ b/package.json @@ -0,0 +1,40 @@ +{ + "name": "pg-pool", + "version": "0.0.1", + "description": "Connection pool for node-postgres", + "main": "index.js", + "directories": { + "test": "test" + }, + "scripts": { + "test": "node_modules/.bin/mocha" + }, + "repository": { + "type": "git", + "url": "git://github.com/brianc/node-pg-pool.git" + }, + "keywords": [ + "pg", + "postgres", + "pool", + "database" + ], + "author": "Brian M. Carlson", + "license": "MIT", + "bugs": { + "url": "https://github.com/brianc/node-pg-pool/issues" + }, + "homepage": "https://github.com/brianc/node-pg-pool#readme", + "devDependencies": { + "bluebird": "3.4.0", + "co": "4.6.0", + "expect.js": "0.3.1", + "lodash": "4.13.1", + "mocha": "^2.3.3", + "pg": "4.5.6" + }, + "dependencies": { + "debug": "^2.2.0", + "generic-pool": "2.4.2" + } +} diff --git a/test/index.js b/test/index.js new file mode 100644 index 00000000..4ea43cd0 --- /dev/null +++ b/test/index.js @@ -0,0 +1,111 @@ +var expect = require('expect.js') +var Client = require('pg').Client +var co = require('co') +var Promise = require('bluebird') +var _ = require('lodash') + +var Pool = require('../') + +describe('pool', function() { + + describe('with callbacks', function() { + it('works totally unconfigured', function(done) { + const pool = new Pool() + pool.connect(function(err, client, release) { + if (err) return done(err) + client.query('SELECT NOW()', function(err, res) { + release() + if (err) return done(err) + expect(res.rows).to.have.length(1) + pool.end(done) + }) + }) + }) + + it('passes props to clients', function(done) { + const pool = new Pool({ binary: true }) + pool.connect(function(err, client, release) { + release() + expect(client.binary).to.eql(true) + pool.end(done) + }) + }) + + it('removes client if it errors in background', function(done) { + const pool = new Pool() + pool.connect(function(err, client, release) { + release() + client.testString = 'foo' + setTimeout(function() { + client.emit('error', new Error('on purpose')) + }, 10) + }) + pool.on('error', function(err) { + expect(err.message).to.be('on purpose') + expect(err.client).to.not.be(undefined) + expect(err.client.testString).to.be('foo') + err.client.connection.stream.on('end', function() { + pool.end(done) + }) + }) + }) + }) + + describe('with promises', function() { + it('connects and disconnects', co.wrap(function*() { + var pool = new Pool() + var client = yield pool.connect() + expect(pool.pool.availableObjectsCount()).to.be(0) + var res = yield client.queryAsync('select $1::text as name', ['hi']) + expect(res.rows).to.eql([{ name: 'hi' }]) + client.release() + expect(pool.pool.getPoolSize()).to.be(1) + expect(pool.pool.availableObjectsCount()).to.be(1) + return yield pool.end() + })) + + it('properly pools clients', co.wrap(function*() { + var pool = new Pool({ poolSize: 9 }) + var count = 0 + while (count < 30) { + count++ + pool.connect().then(function(client) { + client.queryAsync('select $1::text as name', ['hi']).then(function(res) { + client.release() + }) + }) + } + yield Promise.delay(100) + expect(pool.pool.getPoolSize()).to.be(9) + return yield pool.end() + })) + + it('supports just running queries', co.wrap(function*() { + var pool = new Pool({ poolSize: 9 }) + var count = 0 + var queries = _.times(30).map(function() { + return pool.query('SELECT $1::text as name', ['hi']) + }) + console.log('executing') + yield queries + expect(pool.pool.getPoolSize()).to.be(9) + expect(pool.pool.availableObjectsCount()).to.be(9) + return yield pool.end() + })) + + it('recovers from all errors', co.wrap(function*() { + var pool = new Pool({ poolSize: 9 }) + var count = 0 + + while(count++ < 30) { + try { + yield pool.query('SELECT lksjdfd') + } catch(e) { + } + } + var res = yield pool.query('SELECT $1::text as name', ['hi']) + expect(res.rows).to.eql([{ name: 'hi' }]) + return yield pool.end() + })) + }) +}) diff --git a/test/mocha.opts b/test/mocha.opts new file mode 100644 index 00000000..46e8e69d --- /dev/null +++ b/test/mocha.opts @@ -0,0 +1,2 @@ +--no-exit +--bail From ad73407aad29d1fcc7cf9adb0ca3c0c33523f312 Mon Sep 17 00:00:00 2001 From: brianc Date: Tue, 7 Jun 2016 19:37:07 -0500 Subject: [PATCH 02/95] Initial commit --- .gitignore | 1 + Makefile | 14 +++++++++++++ index.js | 46 +++++++++++++++++++++---------------------- package.json | 8 +++++--- test/index.js | 54 +++++++++++++++++++++++++-------------------------- 5 files changed, 70 insertions(+), 53 deletions(-) create mode 100644 Makefile diff --git a/.gitignore b/.gitignore index 3c3629e6..93f13619 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ node_modules +npm-debug.log diff --git a/Makefile b/Makefile new file mode 100644 index 00000000..8a763142 --- /dev/null +++ b/Makefile @@ -0,0 +1,14 @@ +.PHONY: jshint test publish-patch test + +test: + npm test + +patch: test + npm version patch -m "Bump version" + git push origin master --tags + npm publish + +minor: test + npm version minor -m "Bump version" + git push origin master --tags + npm publish diff --git a/index.js b/index.js index 7c102d8a..a21e2c0d 100644 --- a/index.js +++ b/index.js @@ -3,11 +3,11 @@ var util = require('util') var EventEmitter = require('events').EventEmitter var debug = require('debug') -var Pool = module.exports = function(options) { +var Pool = module.exports = function (options, Client) { EventEmitter.call(this) this.options = options || {} this.log = this.options.log || debug('pg:pool') - this.Client = this.options.Client || require('pg').Client + this.Client = this.options.Client || Client || require('pg').Client this.Promise = this.options.Promise || Promise this.options.max = this.options.max || this.options.poolSize || 10 @@ -18,33 +18,33 @@ var Pool = module.exports = function(options) { util.inherits(Pool, EventEmitter) -Pool.prototype._destroy = function(client) { +Pool.prototype._destroy = function (client) { if (client._destroying) return client._destroying = true client.end() } -Pool.prototype._create = function(cb) { +Pool.prototype._create = function (cb) { this.log('connecting new client') var client = new this.Client(this.options) - client.on('error', function(e) { + client.on('error', function (e) { this.log('connected client error:', e) this.pool.destroy(client) e.client = client this.emit('error', e) }.bind(this)) - client.connect(function(err) { + client.connect(function (err) { this.log('client connected') if (err) { - this.log('client connection error:', e) + this.log('client connection error:', err) cb(err) } - client.queryAsync = function(text, values) { + client.queryAsync = function (text, values) { return new this.Promise((resolve, reject) => { - client.query(text, values, function(err, res) { + client.query(text, values, function (err, res) { err ? reject(err) : resolve(res) }) }) @@ -54,21 +54,21 @@ Pool.prototype._create = function(cb) { }.bind(this)) } -Pool.prototype.connect = function(cb) { - return new this.Promise(function(resolve, reject) { +Pool.prototype.connect = function (cb) { + return new this.Promise(function (resolve, reject) { this.log('acquire client begin') - this.pool.acquire(function(err, client) { + this.pool.acquire(function (err, client) { if (err) { this.log('acquire client. error:', err) if (cb) { - cb(err, null, function() { }) + cb(err, null, function () {}) } return reject(err) } this.log('acquire client') - client.release = function(err) { + client.release = function (err) { if (err) { this.log('release client. error:', err) this.pool.destroy(client) @@ -89,26 +89,26 @@ Pool.prototype.connect = function(cb) { Pool.prototype.take = Pool.prototype.connect -Pool.prototype.query = function(text, values) { - return this.take().then(function(client) { +Pool.prototype.query = function (text, values) { + return this.take().then(function (client) { return client.queryAsync(text, values) - .then(function(res) { + .then(function (res) { client.release() return res - }).catch(function(error) { + }).catch(function (error) { client.release(error) throw error }) }) } -Pool.prototype.end = function(cb) { +Pool.prototype.end = function (cb) { this.log('draining pool') - return new this.Promise(function(resolve, reject) { - this.pool.drain(function() { + return new this.Promise(function (resolve, reject) { + this.pool.drain(function () { this.log('pool drained, calling destroy all now') - this.pool.destroyAllNow(function() { - if(cb) { + this.pool.destroyAllNow(function () { + if (cb) { cb() } resolve() diff --git a/package.json b/package.json index 7d8b8f09..1c5f6ed4 100644 --- a/package.json +++ b/package.json @@ -1,13 +1,13 @@ { "name": "pg-pool", - "version": "0.0.1", + "version": "1.0.0", "description": "Connection pool for node-postgres", "main": "index.js", "directories": { "test": "test" }, "scripts": { - "test": "node_modules/.bin/mocha" + "test": "node_modules/.bin/standard && node_modules/.bin/mocha" }, "repository": { "type": "git", @@ -31,7 +31,9 @@ "expect.js": "0.3.1", "lodash": "4.13.1", "mocha": "^2.3.3", - "pg": "4.5.6" + "pg": "4.5.6", + "standard": "7.1.2", + "standard-format": "2.2.1" }, "dependencies": { "debug": "^2.2.0", diff --git a/test/index.js b/test/index.js index 4ea43cd0..bc547cc8 100644 --- a/test/index.js +++ b/test/index.js @@ -1,19 +1,20 @@ var expect = require('expect.js') -var Client = require('pg').Client var co = require('co') var Promise = require('bluebird') var _ = require('lodash') +var describe = require('mocha').describe +var it = require('mocha').it + var Pool = require('../') -describe('pool', function() { - - describe('with callbacks', function() { - it('works totally unconfigured', function(done) { +describe('pool', function () { + describe('with callbacks', function () { + it('works totally unconfigured', function (done) { const pool = new Pool() - pool.connect(function(err, client, release) { + pool.connect(function (err, client, release) { if (err) return done(err) - client.query('SELECT NOW()', function(err, res) { + client.query('SELECT NOW()', function (err, res) { release() if (err) return done(err) expect(res.rows).to.have.length(1) @@ -22,37 +23,39 @@ describe('pool', function() { }) }) - it('passes props to clients', function(done) { + it('passes props to clients', function (done) { const pool = new Pool({ binary: true }) - pool.connect(function(err, client, release) { + pool.connect(function (err, client, release) { release() + if (err) return done(err) expect(client.binary).to.eql(true) pool.end(done) }) }) - it('removes client if it errors in background', function(done) { + it('removes client if it errors in background', function (done) { const pool = new Pool() - pool.connect(function(err, client, release) { + pool.connect(function (err, client, release) { release() + if (err) return done(err) client.testString = 'foo' - setTimeout(function() { + setTimeout(function () { client.emit('error', new Error('on purpose')) }, 10) }) - pool.on('error', function(err) { + pool.on('error', function (err) { expect(err.message).to.be('on purpose') expect(err.client).to.not.be(undefined) expect(err.client.testString).to.be('foo') - err.client.connection.stream.on('end', function() { + err.client.connection.stream.on('end', function () { pool.end(done) }) }) }) }) - describe('with promises', function() { - it('connects and disconnects', co.wrap(function*() { + describe('with promises', function () { + it('connects and disconnects', co.wrap(function * () { var pool = new Pool() var client = yield pool.connect() expect(pool.pool.availableObjectsCount()).to.be(0) @@ -64,13 +67,13 @@ describe('pool', function() { return yield pool.end() })) - it('properly pools clients', co.wrap(function*() { + it('properly pools clients', co.wrap(function * () { var pool = new Pool({ poolSize: 9 }) var count = 0 while (count < 30) { count++ - pool.connect().then(function(client) { - client.queryAsync('select $1::text as name', ['hi']).then(function(res) { + pool.connect().then(function (client) { + client.queryAsync('select $1::text as name', ['hi']).then(function (res) { client.release() }) }) @@ -80,28 +83,25 @@ describe('pool', function() { return yield pool.end() })) - it('supports just running queries', co.wrap(function*() { + it('supports just running queries', co.wrap(function * () { var pool = new Pool({ poolSize: 9 }) - var count = 0 - var queries = _.times(30).map(function() { + var queries = _.times(30).map(function () { return pool.query('SELECT $1::text as name', ['hi']) }) - console.log('executing') yield queries expect(pool.pool.getPoolSize()).to.be(9) expect(pool.pool.availableObjectsCount()).to.be(9) return yield pool.end() })) - it('recovers from all errors', co.wrap(function*() { + it('recovers from all errors', co.wrap(function * () { var pool = new Pool({ poolSize: 9 }) var count = 0 - while(count++ < 30) { + while (count++ < 30) { try { yield pool.query('SELECT lksjdfd') - } catch(e) { - } + } catch (e) {} } var res = yield pool.query('SELECT $1::text as name', ['hi']) expect(res.rows).to.eql([{ name: 'hi' }]) From 2aa207eea64d4bf9840dcdc9e6145c3585514dc2 Mon Sep 17 00:00:00 2001 From: brianc Date: Fri, 10 Jun 2016 16:58:23 -0500 Subject: [PATCH 03/95] Update test semantics --- test/index.js | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/test/index.js b/test/index.js index bc547cc8..a26f4656 100644 --- a/test/index.js +++ b/test/index.js @@ -70,15 +70,11 @@ describe('pool', function () { it('properly pools clients', co.wrap(function * () { var pool = new Pool({ poolSize: 9 }) var count = 0 - while (count < 30) { - count++ - pool.connect().then(function (client) { - client.queryAsync('select $1::text as name', ['hi']).then(function (res) { - client.release() - }) - }) - } - yield Promise.delay(100) + yield _.times(30).map(function * () { + var client = yield pool.connect() + var result = yield client.queryAsync('select $1::text as name', ['hi']) + client.release() + }) expect(pool.pool.getPoolSize()).to.be(9) return yield pool.end() })) From 1decf9693afe3ceb205a5dc4a2e9919d12030c80 Mon Sep 17 00:00:00 2001 From: Brian C Date: Fri, 10 Jun 2016 17:16:14 -0500 Subject: [PATCH 04/95] Create README.md --- README.md | 100 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 100 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 00000000..a6ef5ca1 --- /dev/null +++ b/README.md @@ -0,0 +1,100 @@ +# node-pg-pool +A connection pool for node-postgres + +## install +```sh +npm i pg-pool pg +``` + +## use + +to use pg-pool you must first create an instance of a pool + +```js +//by default the pool uses the same +//configuration as whatever `pg` version you have installed +const pool = new Pool() + +//you can pass properties to the pool +//these properties are passed unchanged to both the node-postgres Client constructor +//and the node-pool (https://github.com/coopernurse/node-pool) constructor +//allowing you to fully configure the behavior of both +const pool2 = new Pool({ + database: 'postgres', + user: 'brianc', + password: 'secret!', + port: 5432, + ssl: true, + max: 20, //set pool max size to 20 + min: 4, //set min pool size to 4 + idleTimeoutMillis: 1000 //close idle clients after 1 second +}) + +//you can supply a custom client constructor +//if you want to use the native postgres client +const NativeClient = require('pg').native.Client +const nativePool = new Pool({ Client: NativeClient }) + +//you can even pool pg-native clients directly +const PgNativeClient = require('pg-native') +const pgNativePool = new Pool({ Client: PgNativeClient }) +``` + +pg-pool supports a fully promise-based api for acquiring clients + +```js +const pool = new Pool() +pool.connect().then(client => { + client.query('select $1::text as name', ['pg-pool']).then(res => { + client.release() + console.log('hello from', res.rows[0].name) + }) + .catch(e => { + client.release() + console.error('query error', e.message, e.stack) + }) +}) +``` + +pg-pool supports the traditional callback api for acquiring a client that node-postgres has shipped with internally for years: + +```js +const pool = new Pool() +pool.connect((err, client, done) => { + if (err) return done(err) + + client.query('SELECT $1::text as name', ['pg-pool'], (err, res) => { + done() + if (err) { + return console.error('query error', e.message, e.stack) + } + console.log('hello from', res.rows[0].name) + }) +}) +``` + +When you are finished with the pool if all the clients are idle the pool will close them after `config.idleTimeoutMillis` and your app +will shutdown gracefully. If you don't want to wait for the timeout you can end the pool as follows: + +```js +const pool = new Pool() +const client = await pool.connect() +console.log(await client.query('select now()')) +client.release() +await pool.end() +``` + +## tests + +To run tests clone the repo, `npm i` in the working dir, and then run `npm test` + +## license + +The MIT License (MIT) +Copyright (c) 2016 Brian M. Carlson + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. From da6a2b0b0cce3b644755c1112943671472fc26a1 Mon Sep 17 00:00:00 2001 From: Brian C Date: Fri, 10 Jun 2016 17:17:35 -0500 Subject: [PATCH 05/95] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a6ef5ca1..e0e9a9bd 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# node-pg-pool +# pg-pool A connection pool for node-postgres ## install From 5c88bd696513169aa640f33c061f2d3d84d01ec5 Mon Sep 17 00:00:00 2001 From: brianc Date: Fri, 10 Jun 2016 17:40:42 -0500 Subject: [PATCH 06/95] Update readme --- README.md | 36 ++++++++++++++++++++++++++++++++++-- index.js | 2 ++ 2 files changed, 36 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index e0e9a9bd..84fdae74 100644 --- a/README.md +++ b/README.md @@ -56,13 +56,39 @@ pool.connect().then(client => { }) ``` -pg-pool supports the traditional callback api for acquiring a client that node-postgres has shipped with internally for years: +this ends up looking much nicer if you're using [co](https://github.com/tj/co) or async/await: + +```js +const pool = new Pool() +const client = await pool.connect() +try { + const result = await client.query('select $1::text as name', ['brianc']) + console.log('hello from', result.rows[0]) +} finally { + client.release() +} +``` + +because its so common to just run a query and return the client to the pool afterward pg-pool has this built-in: + +```js +const pool = new Pool() +const time = await pool.query('SELECT NOW()') +const name = await pool.query('select $1::text as name', ['brianc']) +console.log(name.rows[0].name, 'says hello at', time.rows[0].name) +``` +__pro tip:__ unless you need to run a transaction (which requires a single client for multiple queries) or you +have some other edge case like [streaming rows](https://github.com/brianc/node-pg-query-stream) or using a [cursor](https://github.com/brianc/node-pg-cursor) +you should almost always just use `pool.query`. Its easy, it does the right thing :tm:, and wont ever forget to return +clients back to the pool after the query is done. + +pg-pool still and will always support the traditional callback api for acquiring a client that node-postgres has shipped with internally for years: ```js const pool = new Pool() pool.connect((err, client, done) => { if (err) return done(err) - + client.query('SELECT $1::text as name', ['pg-pool'], (err, res) => { done() if (err) { @@ -73,6 +99,8 @@ pool.connect((err, client, done) => { }) ``` +That means you can drop pg-pool into your app and 99% of the cases you wont even notice a difference. In fact, very soon I will be using pg-pool internally within node-postgres itself! + When you are finished with the pool if all the clients are idle the pool will close them after `config.idleTimeoutMillis` and your app will shutdown gracefully. If you don't want to wait for the timeout you can end the pool as follows: @@ -88,6 +116,10 @@ await pool.end() To run tests clone the repo, `npm i` in the working dir, and then run `npm test` +## contributions + +I love contributions. Please make sure they have tests, and submit a PR. If you're not sure if the issue is worth it or will be accepted it never hurts to open an issue to begin the conversation. Don't forget to follow me on twitter at [@briancarlson](https://twitter.com/briancarlson) - I generally announce any noteworthy updates there. + ## license The MIT License (MIT) diff --git a/index.js b/index.js index a21e2c0d..1586947a 100644 --- a/index.js +++ b/index.js @@ -42,6 +42,8 @@ Pool.prototype._create = function (cb) { cb(err) } + var query = client.query; + client.queryAsync = function (text, values) { return new this.Promise((resolve, reject) => { client.query(text, values, function (err, res) { From d73538550cba65f681ca73690ef97d1b1acd6865 Mon Sep 17 00:00:00 2001 From: Brian C Date: Fri, 10 Jun 2016 17:44:48 -0500 Subject: [PATCH 07/95] Update README.md --- README.md | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 84fdae74..18a4b524 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,8 @@ npm i pg-pool pg ## use +### create + to use pg-pool you must first create an instance of a pool ```js @@ -40,6 +42,8 @@ const PgNativeClient = require('pg-native') const pgNativePool = new Pool({ Client: PgNativeClient }) ``` +### acquire clients with a promise + pg-pool supports a fully promise-based api for acquiring clients ```js @@ -56,6 +60,8 @@ pool.connect().then(client => { }) ``` +### plays nice with async/await + this ends up looking much nicer if you're using [co](https://github.com/tj/co) or async/await: ```js @@ -69,6 +75,8 @@ try { } ``` +### your new favorite helper method + because its so common to just run a query and return the client to the pool afterward pg-pool has this built-in: ```js @@ -82,7 +90,9 @@ have some other edge case like [streaming rows](https://github.com/brianc/node-p you should almost always just use `pool.query`. Its easy, it does the right thing :tm:, and wont ever forget to return clients back to the pool after the query is done. -pg-pool still and will always support the traditional callback api for acquiring a client that node-postgres has shipped with internally for years: +### drop-in backwards compatible + +pg-pool still and will always support the traditional callback api for acquiring a client. This is the exact API node-postgres has shipped with internally for years: ```js const pool = new Pool() @@ -101,6 +111,8 @@ pool.connect((err, client, done) => { That means you can drop pg-pool into your app and 99% of the cases you wont even notice a difference. In fact, very soon I will be using pg-pool internally within node-postgres itself! +### shut it down + When you are finished with the pool if all the clients are idle the pool will close them after `config.idleTimeoutMillis` and your app will shutdown gracefully. If you don't want to wait for the timeout you can end the pool as follows: From d42770ae2ca4229d3ac5da0fbb2a5a9709a51537 Mon Sep 17 00:00:00 2001 From: John Fawcett Date: Fri, 10 Jun 2016 21:25:56 -0500 Subject: [PATCH 08/95] Demonstrate that pg-pool exports Pool constructor --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 18a4b524..edcb79d3 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,8 @@ npm i pg-pool pg to use pg-pool you must first create an instance of a pool ```js +const Pool = require('pg-pool') + //by default the pool uses the same //configuration as whatever `pg` version you have installed const pool = new Pool() From cb21c2a2e7d6a3bd6d3244c7d09ab97f43ac4728 Mon Sep 17 00:00:00 2001 From: brianc Date: Fri, 10 Jun 2016 22:52:07 -0500 Subject: [PATCH 09/95] Better compatibility with pg --- index.js | 28 ++++++++-------------------- package.json | 2 +- test/index.js | 6 ++---- 3 files changed, 11 insertions(+), 25 deletions(-) diff --git a/index.js b/index.js index 1586947a..b477e225 100644 --- a/index.js +++ b/index.js @@ -41,17 +41,6 @@ Pool.prototype._create = function (cb) { this.log('client connection error:', err) cb(err) } - - var query = client.query; - - client.queryAsync = function (text, values) { - return new this.Promise((resolve, reject) => { - client.query(text, values, function (err, res) { - err ? reject(err) : resolve(res) - }) - }) - }.bind(this) - cb(err, err ? null : client) }.bind(this)) } @@ -92,16 +81,15 @@ Pool.prototype.connect = function (cb) { Pool.prototype.take = Pool.prototype.connect Pool.prototype.query = function (text, values) { - return this.take().then(function (client) { - return client.queryAsync(text, values) - .then(function (res) { - client.release() - return res - }).catch(function (error) { - client.release(error) - throw error + return new this.Promise(function (resolve, reject) { + this.connect(function (err, client, done) { + if (err) return reject(err) + client.query(text, values, function (err, res) { + done(err) + err ? reject(err) : resolve(res) }) - }) + }) + }.bind(this)) } Pool.prototype.end = function (cb) { diff --git a/package.json b/package.json index 1c5f6ed4..fd31c79b 100644 --- a/package.json +++ b/package.json @@ -31,7 +31,7 @@ "expect.js": "0.3.1", "lodash": "4.13.1", "mocha": "^2.3.3", - "pg": "4.5.6", + "pg": "5.1.0", "standard": "7.1.2", "standard-format": "2.2.1" }, diff --git a/test/index.js b/test/index.js index a26f4656..364d62f2 100644 --- a/test/index.js +++ b/test/index.js @@ -1,6 +1,5 @@ var expect = require('expect.js') var co = require('co') -var Promise = require('bluebird') var _ = require('lodash') var describe = require('mocha').describe @@ -59,7 +58,7 @@ describe('pool', function () { var pool = new Pool() var client = yield pool.connect() expect(pool.pool.availableObjectsCount()).to.be(0) - var res = yield client.queryAsync('select $1::text as name', ['hi']) + var res = yield client.query('select $1::text as name', ['hi']) expect(res.rows).to.eql([{ name: 'hi' }]) client.release() expect(pool.pool.getPoolSize()).to.be(1) @@ -69,10 +68,9 @@ describe('pool', function () { it('properly pools clients', co.wrap(function * () { var pool = new Pool({ poolSize: 9 }) - var count = 0 yield _.times(30).map(function * () { var client = yield pool.connect() - var result = yield client.queryAsync('select $1::text as name', ['hi']) + yield client.query('select $1::text as name', ['hi']) client.release() }) expect(pool.pool.getPoolSize()).to.be(9) From e38cfe078c8db3208a323fed279b002aaee2b6df Mon Sep 17 00:00:00 2001 From: brianc Date: Fri, 10 Jun 2016 22:52:21 -0500 Subject: [PATCH 10/95] Bump version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index fd31c79b..0f12e96f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "pg-pool", - "version": "1.0.0", + "version": "1.0.1", "description": "Connection pool for node-postgres", "main": "index.js", "directories": { From a6f641eb2cf45638a3152b7d84b9f99325aa7df2 Mon Sep 17 00:00:00 2001 From: Lee Symes Date: Tue, 21 Jun 2016 12:56:12 +1200 Subject: [PATCH 11/95] Only release client once on an error. (#3) Prevent `generic-pool` error when releasing a client with an error. Fixes #2 --- index.js | 13 +++++++------ test/index.js | 13 ++++++++++++- 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/index.js b/index.js index b477e225..67cfc19f 100644 --- a/index.js +++ b/index.js @@ -60,13 +60,14 @@ Pool.prototype.connect = function (cb) { this.log('acquire client') client.release = function (err) { - if (err) { - this.log('release client. error:', err) - this.pool.destroy(client) - } - this.log('release client') delete client.release - this.pool.release(client) + if (err) { + this.log('destroy client. error:', err) + this.pool.destroy(client) + } else { + this.log('release client') + this.pool.release(client) + } }.bind(this) if (cb) { diff --git a/test/index.js b/test/index.js index 364d62f2..e03c5f23 100644 --- a/test/index.js +++ b/test/index.js @@ -89,7 +89,18 @@ describe('pool', function () { })) it('recovers from all errors', co.wrap(function * () { - var pool = new Pool({ poolSize: 9 }) + var pool = new Pool({ + poolSize: 9, + log: function (str, level) { + // Custom logging function to ensure we are not causing errors or warnings + // inside the `generic-pool` library. + if (level === 'error' || level === 'warn') { + expect().fail('An error or warning was logged from the generic pool library.\n' + + 'Level: ' + level + '\n' + + 'Message: ' + str + '\n') + } + } + }) var count = 0 while (count++ < 30) { From d09cf3b9c33d00b1f059c974ac127f0a4fd051cb Mon Sep 17 00:00:00 2001 From: brianc Date: Mon, 20 Jun 2016 19:57:44 -0500 Subject: [PATCH 12/95] Bump version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 0f12e96f..a3d554c4 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "pg-pool", - "version": "1.0.1", + "version": "1.0.2", "description": "Connection pool for node-postgres", "main": "index.js", "directories": { From ef8530aeb7d4e0ee155d3652c11cc1e2e4d74657 Mon Sep 17 00:00:00 2001 From: Brian C Date: Tue, 21 Jun 2016 22:38:31 -0500 Subject: [PATCH 13/95] Update README.md --- README.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/README.md b/README.md index edcb79d3..9ad5e20d 100644 --- a/README.md +++ b/README.md @@ -126,6 +126,20 @@ client.release() await pool.end() ``` +### environment variables + +pg-pool & node-postgres support some of the same environment variables as `psql` supports. The most common are: + +``` +PGDATABASE=my_db +PGUSER=username +PGPASSWORD="my awesome password" +PGPORT=5432 +PGSSLMODE=require +``` + +Usually I will export these into my local environment via a `.env` file with environment settings or expor them in `~/.bash_profile` or something similar. This way I get configurability which works with both the postgres suite of tools (`psql`, `pg_dump`, `pg_restore`) and node, I can vary the environment variables locally and in production, and it supports the concept of a [12-factor app](http://12factor.net/) out of the box. + ## tests To run tests clone the repo, `npm i` in the working dir, and then run `npm test` From d21ed42fc6f634704e2edc21410840cca52dfd07 Mon Sep 17 00:00:00 2001 From: Brian C Date: Wed, 22 Jun 2016 10:03:14 -0500 Subject: [PATCH 14/95] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 9ad5e20d..61a26180 100644 --- a/README.md +++ b/README.md @@ -138,7 +138,7 @@ PGPORT=5432 PGSSLMODE=require ``` -Usually I will export these into my local environment via a `.env` file with environment settings or expor them in `~/.bash_profile` or something similar. This way I get configurability which works with both the postgres suite of tools (`psql`, `pg_dump`, `pg_restore`) and node, I can vary the environment variables locally and in production, and it supports the concept of a [12-factor app](http://12factor.net/) out of the box. +Usually I will export these into my local environment via a `.env` file with environment settings or export them in `~/.bash_profile` or something similar. This way I get configurability which works with both the postgres suite of tools (`psql`, `pg_dump`, `pg_restore`) and node, I can vary the environment variables locally and in production, and it supports the concept of a [12-factor app](http://12factor.net/) out of the box. ## tests From cc20f8b747ecb85b19496750494e58eb495d4ceb Mon Sep 17 00:00:00 2001 From: ikokostya Date: Thu, 23 Jun 2016 00:34:17 +0300 Subject: [PATCH 15/95] Clone options in Pool constructor (fixes #4) (#5) --- index.js | 3 ++- package.json | 3 ++- test/index.js | 11 +++++++++++ 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/index.js b/index.js index 67cfc19f..6e5fdbbd 100644 --- a/index.js +++ b/index.js @@ -2,10 +2,11 @@ var genericPool = require('generic-pool') var util = require('util') var EventEmitter = require('events').EventEmitter var debug = require('debug') +var objectAssign = require('object-assign') var Pool = module.exports = function (options, Client) { EventEmitter.call(this) - this.options = options || {} + this.options = objectAssign({}, options) this.log = this.options.log || debug('pg:pool') this.Client = this.options.Client || Client || require('pg').Client this.Promise = this.options.Promise || Promise diff --git a/package.json b/package.json index a3d554c4..7ddc16ea 100644 --- a/package.json +++ b/package.json @@ -37,6 +37,7 @@ }, "dependencies": { "debug": "^2.2.0", - "generic-pool": "2.4.2" + "generic-pool": "2.4.2", + "object-assign": "4.1.0" } } diff --git a/test/index.js b/test/index.js index e03c5f23..956f15cc 100644 --- a/test/index.js +++ b/test/index.js @@ -51,6 +51,17 @@ describe('pool', function () { }) }) }) + + it('should not change given options', function (done) { + var options = { max: 10 } + var pool = new Pool(options) + pool.connect(function (err, client, release) { + release() + if (err) return done(err) + expect(options).to.eql({ max: 10 }) + pool.end(done) + }) + }) }) describe('with promises', function () { From 276b50d69f24651270a6103c3c5e33242cc8a451 Mon Sep 17 00:00:00 2001 From: brianc Date: Wed, 22 Jun 2016 17:17:11 -0500 Subject: [PATCH 16/95] Bump version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 7ddc16ea..34ece77c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "pg-pool", - "version": "1.0.2", + "version": "1.0.3", "description": "Connection pool for node-postgres", "main": "index.js", "directories": { From 7ef08fd8611641057c1a6115a8ffac712d133b4b Mon Sep 17 00:00:00 2001 From: Brian C Date: Wed, 22 Jun 2016 23:29:09 -0500 Subject: [PATCH 17/95] Remove dependency on debug (#6) Accept a `log: (message, other...) => { }` parameter as a config option, but by default use a no-op function instead of debug. --- index.js | 3 +-- package.json | 1 - test/logging.js | 20 ++++++++++++++++++++ 3 files changed, 21 insertions(+), 3 deletions(-) create mode 100644 test/logging.js diff --git a/index.js b/index.js index 6e5fdbbd..687e410f 100644 --- a/index.js +++ b/index.js @@ -1,13 +1,12 @@ var genericPool = require('generic-pool') var util = require('util') var EventEmitter = require('events').EventEmitter -var debug = require('debug') var objectAssign = require('object-assign') var Pool = module.exports = function (options, Client) { EventEmitter.call(this) this.options = objectAssign({}, options) - this.log = this.options.log || debug('pg:pool') + this.log = this.options.log || function () { } this.Client = this.options.Client || Client || require('pg').Client this.Promise = this.options.Promise || Promise diff --git a/package.json b/package.json index 34ece77c..d298b813 100644 --- a/package.json +++ b/package.json @@ -36,7 +36,6 @@ "standard-format": "2.2.1" }, "dependencies": { - "debug": "^2.2.0", "generic-pool": "2.4.2", "object-assign": "4.1.0" } diff --git a/test/logging.js b/test/logging.js new file mode 100644 index 00000000..e03fa519 --- /dev/null +++ b/test/logging.js @@ -0,0 +1,20 @@ +var expect = require('expect.js') +var co = require('co') + +var describe = require('mocha').describe +var it = require('mocha').it + +var Pool = require('../') + +describe('logging', function () { + it('logs to supplied log function if given', co.wrap(function * () { + var messages = [] + var log = function (msg) { + messages.push(msg) + } + var pool = new Pool({ log: log }) + yield pool.query('SELECT NOW()') + expect(messages.length).to.be.greaterThan(0) + return pool.end() + })) +}) From 63caf7cd4c2255537b063274a1142b9a52148a4b Mon Sep 17 00:00:00 2001 From: Brian C Date: Wed, 22 Jun 2016 23:29:35 -0500 Subject: [PATCH 18/95] Add 'connect' event to pool (#7) * Have pool emit 'connect' callback with client * Ensure pool emits client on connect event --- index.js | 2 ++ test/events.js | 24 ++++++++++++++++++++++++ 2 files changed, 26 insertions(+) create mode 100644 test/events.js diff --git a/index.js b/index.js index 687e410f..ee09dc81 100644 --- a/index.js +++ b/index.js @@ -14,6 +14,7 @@ var Pool = module.exports = function (options, Client) { this.options.create = this.options.create || this._create.bind(this) this.options.destroy = this.options.destroy || this._destroy.bind(this) this.pool = new genericPool.Pool(this.options) + this.onCreate = this.options.onCreate } util.inherits(Pool, EventEmitter) @@ -37,6 +38,7 @@ Pool.prototype._create = function (cb) { client.connect(function (err) { this.log('client connected') + this.emit('connect', client) if (err) { this.log('client connection error:', err) cb(err) diff --git a/test/events.js b/test/events.js new file mode 100644 index 00000000..3b7225b7 --- /dev/null +++ b/test/events.js @@ -0,0 +1,24 @@ +var expect = require('expect.js') + +var describe = require('mocha').describe +var it = require('mocha').it + +var Pool = require('../') + +describe('events', function () { + it('emits connect before callback', function (done) { + var pool = new Pool() + var emittedClient = false + pool.on('connect', function (client) { + emittedClient = client + }) + + pool.connect(function (err, client, release) { + if (err) return done(err) + release() + pool.end() + expect(client).to.be(emittedClient) + done() + }) + }) +}) From d2775fc02354c1ca31e91fe1342e7f1a820599d2 Mon Sep 17 00:00:00 2001 From: Brian C Date: Wed, 22 Jun 2016 23:55:17 -0500 Subject: [PATCH 19/95] Add travis.yml file (#9) * Add travis.yml file * Remove test on pg@9.5 since travis does not support it --- .travis.yml | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 00000000..12f9f47a --- /dev/null +++ b/.travis.yml @@ -0,0 +1,13 @@ +language: node_js + +matrix: + include: + - node_js: "4" + addons: + postgresql: "9.1" + - node_js: "5" + addons: + postgresql: "9.4" + - node_js: "6" + addons: + postgresql: "9.4" From 8c058a300a2b46c0214f2a27d15b20a8bf0b360f Mon Sep 17 00:00:00 2001 From: Brian C Date: Thu, 23 Jun 2016 00:09:50 -0500 Subject: [PATCH 20/95] Update README.md --- README.md | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/README.md b/README.md index 61a26180..9a18e91f 100644 --- a/README.md +++ b/README.md @@ -126,6 +126,54 @@ client.release() await pool.end() ``` +### events + +Every instance of a `Pool` is an event emitter. These instances emit the following events: + +#### error + +Emitted whenever an idle client in the pool encounters an error. This is common when your PostgreSQL server shuts down, reboots, or a network partition otherwise causes it to become unavailable while your pool has connected clients. + +Example: + +```js +var pg = require('pg') +var pool = new pg.Pool() + +// attach an error handler to the pool for when a connected, idle client +// receives an error by being disconnected, etc +pool.on('error', function(error, client) { + // handle this in the same way you would treat process.on('uncaughtException') + // it is supplied the error as well as the idle client which received the error +}) +``` + +#### connect + +Fired whenever the pool creates a __new__ `pg.Client` instance and successfully connects it to the backend. + +Example: + +```js +var pg = require('pg') +var pool = new pg.Pool() + +var count = 0 + +pool.on('connect', client => { + client.count = count++ +}) + +pool.connect() + .then(client => { + client.query('SELECT $1::int AS "clientCount', [client.count]) + .then(res => console.log(res.rows[0].clientCount)) // outputs 0 + })) + +`` + +This allows you to do custom bootstrapping and manipulation of clients after they have been successfully connected to the PostgreSQL backend, but before any queries have been issued. + ### environment variables pg-pool & node-postgres support some of the same environment variables as `psql` supports. The most common are: From c95036c362f75f7bf0ef0c75e45b8c37eb7bab31 Mon Sep 17 00:00:00 2001 From: Brian C Date: Thu, 23 Jun 2016 00:21:01 -0500 Subject: [PATCH 21/95] Update README.md Add travis badge --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 9a18e91f..1b6c8a0c 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,6 @@ # pg-pool +[![Build Status](https://travis-ci.org/brianc/node-pg-pool.svg?branch=master)](https://travis-ci.org/brianc/node-pg-pool) + A connection pool for node-postgres ## install From 955d6ba79747d954f203879885a133d80a3e8cda Mon Sep 17 00:00:00 2001 From: brianc Date: Thu, 23 Jun 2016 00:21:16 -0500 Subject: [PATCH 22/95] Bump version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index d298b813..9e74dc50 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "pg-pool", - "version": "1.0.3", + "version": "1.1.0", "description": "Connection pool for node-postgres", "main": "index.js", "directories": { From cc40403de9760b92c599fefc27ff079c03598f6f Mon Sep 17 00:00:00 2001 From: Brian C Date: Thu, 23 Jun 2016 00:22:07 -0500 Subject: [PATCH 23/95] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 1b6c8a0c..895140dc 100644 --- a/README.md +++ b/README.md @@ -172,7 +172,7 @@ pool.connect() .then(res => console.log(res.rows[0].clientCount)) // outputs 0 })) -`` +``` This allows you to do custom bootstrapping and manipulation of clients after they have been successfully connected to the PostgreSQL backend, but before any queries have been issued. From 4758ea660e44f34259a9828a4fd6bbd958cdac17 Mon Sep 17 00:00:00 2001 From: brianc Date: Thu, 23 Jun 2016 14:34:41 -0500 Subject: [PATCH 24/95] Update documentation --- README.md | 47 ++++++++++++++++++++++++++++++++--------------- 1 file changed, 32 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index 895140dc..dbb5b1f7 100644 --- a/README.md +++ b/README.md @@ -69,14 +69,28 @@ pool.connect().then(client => { this ends up looking much nicer if you're using [co](https://github.com/tj/co) or async/await: ```js -const pool = new Pool() -const client = await pool.connect() -try { - const result = await client.query('select $1::text as name', ['brianc']) - console.log('hello from', result.rows[0]) -} finally { - client.release() -} +// with async/await +(async () => { + const pool = new Pool() + const client = await pool.connect() + try { + const result = await client.query('select $1::text as name', ['brianc']) + console.log('hello from', result.rows[0]) + } finally { + client.release() + } +})().catch(e => console.error(e.message, e.stack)) + +// with co +co(function * () { + const client = yield pool.connect() + try { + const result = yield client.query('select $1::text as name', ['brianc']) + console.log('hello from', result.rows[0]) + } finally { + client.release() + } +}).catch(e => console.error(e.message, e.stack)) ``` ### your new favorite helper method @@ -89,6 +103,7 @@ const time = await pool.query('SELECT NOW()') const name = await pool.query('select $1::text as name', ['brianc']) console.log(name.rows[0].name, 'says hello at', time.rows[0].name) ``` + __pro tip:__ unless you need to run a transaction (which requires a single client for multiple queries) or you have some other edge case like [streaming rows](https://github.com/brianc/node-pg-query-stream) or using a [cursor](https://github.com/brianc/node-pg-cursor) you should almost always just use `pool.query`. Its easy, it does the right thing :tm:, and wont ever forget to return @@ -96,7 +111,7 @@ clients back to the pool after the query is done. ### drop-in backwards compatible -pg-pool still and will always support the traditional callback api for acquiring a client. This is the exact API node-postgres has shipped with internally for years: +pg-pool still and will always support the traditional callback api for acquiring a client. This is the exact API node-postgres has shipped with for years: ```js const pool = new Pool() @@ -113,8 +128,6 @@ pool.connect((err, client, done) => { }) ``` -That means you can drop pg-pool into your app and 99% of the cases you wont even notice a difference. In fact, very soon I will be using pg-pool internally within node-postgres itself! - ### shut it down When you are finished with the pool if all the clients are idle the pool will close them after `config.idleTimeoutMillis` and your app @@ -142,7 +155,7 @@ Example: var pg = require('pg') var pool = new pg.Pool() -// attach an error handler to the pool for when a connected, idle client +// attach an error handler to the pool for when a connected, idle client // receives an error by being disconnected, etc pool.on('error', function(error, client) { // handle this in the same way you would treat process.on('uncaughtException') @@ -166,11 +179,15 @@ pool.on('connect', client => { client.count = count++ }) -pool.connect() +pool + .connect() .then(client => { - client.query('SELECT $1::int AS "clientCount', [client.count]) + return client + .query('SELECT $1::int AS "clientCount', [client.count]) .then(res => console.log(res.rows[0].clientCount)) // outputs 0 + .then(() => client) })) + .then(client => client.release()) ``` @@ -196,7 +213,7 @@ To run tests clone the repo, `npm i` in the working dir, and then run `npm test` ## contributions -I love contributions. Please make sure they have tests, and submit a PR. If you're not sure if the issue is worth it or will be accepted it never hurts to open an issue to begin the conversation. Don't forget to follow me on twitter at [@briancarlson](https://twitter.com/briancarlson) - I generally announce any noteworthy updates there. +I love contributions. Please make sure they have tests, and submit a PR. If you're not sure if the issue is worth it or will be accepted it never hurts to open an issue to begin the conversation. If you're interested in keeping up with node-postgres releated stuff, you can follow me on twitter at [@briancarlson](https://twitter.com/briancarlson) - I generally announce any noteworthy updates there. ## license From 8b45ea1e7d9d73f971245ebb063c684bf36f9486 Mon Sep 17 00:00:00 2001 From: Brian C Date: Fri, 24 Jun 2016 10:48:23 -0500 Subject: [PATCH 25/95] Update README.md Add a section with instructions on where to instantiate your pool --- README.md | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/README.md b/README.md index dbb5b1f7..51381c85 100644 --- a/README.md +++ b/README.md @@ -141,6 +141,34 @@ client.release() await pool.end() ``` +### a note on instances + +The pool should be a __long-lived object__ in your application. Generally you'll want to instantiate one pool when your app starts up and use the same instance of the pool throughout the lifetime of your application. If you are frequently creating a new pool within your code you likely don't have your pool initialization code in the correct place. Example: + +``` +// assume this is a file in your program at ./your-app/lib/db.js + +// correct usage: create the pool and let it live +// 'globally' here, controlling access to it through exported methods +const pool = new pg.Pool() + +// this is the right way to export the query method +module.exports.query = (text, values) => { + console.log('query:', text, values) + return pool.query(text, values) +} + +// this would be the WRONG way to export the connect method +module.exports.connect = () => { + // notice how we would be creating a pool instance here + // every time we called 'connect' to get a new client? + // that's a bad thing & results in creating an unbounded + // number of pools & therefore connections + const aPool = new pg.Pool() + return aPool.connect() +} +``` + ### events Every instance of a `Pool` is an event emitter. These instances emit the following events: From ce59164ba1bc4932594be7f4cae5ab52d4576f2d Mon Sep 17 00:00:00 2001 From: Brian C Date: Fri, 24 Jun 2016 11:35:16 -0500 Subject: [PATCH 26/95] Add callback interface to pool#query (#11) * Add callback interface to pool#query * Fix linting errors --- index.js | 5 ++++- test/index.js | 10 ++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/index.js b/index.js index ee09dc81..042d697b 100644 --- a/index.js +++ b/index.js @@ -83,13 +83,16 @@ Pool.prototype.connect = function (cb) { Pool.prototype.take = Pool.prototype.connect -Pool.prototype.query = function (text, values) { +Pool.prototype.query = function (text, values, cb) { return new this.Promise(function (resolve, reject) { this.connect(function (err, client, done) { if (err) return reject(err) client.query(text, values, function (err, res) { done(err) err ? reject(err) : resolve(res) + if (cb) { + cb(err, res) + } }) }) }.bind(this)) diff --git a/test/index.js b/test/index.js index 956f15cc..a7772b61 100644 --- a/test/index.js +++ b/test/index.js @@ -32,6 +32,16 @@ describe('pool', function () { }) }) + it('can run a query with a callback', function (done) { + const pool = new Pool() + pool.query('SELECT $1::text as name', ['brianc'], function (err, res) { + expect(res.rows[0]).to.eql({ name: 'brianc' }) + pool.end(function () { + done(err) + }) + }) + }) + it('removes client if it errors in background', function (done) { const pool = new Pool() pool.connect(function (err, client, release) { From f47bc5f23b5b4d68f0c2d7eaa2904998615c38d3 Mon Sep 17 00:00:00 2001 From: brianc Date: Fri, 24 Jun 2016 11:35:11 -0500 Subject: [PATCH 27/95] Update documentation --- README.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/README.md b/README.md index 51381c85..4b494efc 100644 --- a/README.md +++ b/README.md @@ -104,6 +104,15 @@ const name = await pool.query('select $1::text as name', ['brianc']) console.log(name.rows[0].name, 'says hello at', time.rows[0].name) ``` +you can also use a callback here if you'd like: + +```js +const pool = new Pool() +pool.query('SELECT $1::text as name', ['brianc'], function (err, res) { + console.log(res.rows[0].name) // brianc +}) +``` + __pro tip:__ unless you need to run a transaction (which requires a single client for multiple queries) or you have some other edge case like [streaming rows](https://github.com/brianc/node-pg-query-stream) or using a [cursor](https://github.com/brianc/node-pg-cursor) you should almost always just use `pool.query`. Its easy, it does the right thing :tm:, and wont ever forget to return From 2d446d49533b1f31cadf76210b3d56c72f83a10f Mon Sep 17 00:00:00 2001 From: brianc Date: Fri, 24 Jun 2016 11:36:36 -0500 Subject: [PATCH 28/95] Bump version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 9e74dc50..afc1cd6d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "pg-pool", - "version": "1.1.0", + "version": "1.2.0", "description": "Connection pool for node-postgres", "main": "index.js", "directories": { From aa1f10b0c04bbd3e04851c3ea8501414e634bdc1 Mon Sep 17 00:00:00 2001 From: Brian C Date: Fri, 24 Jun 2016 11:52:32 -0500 Subject: [PATCH 29/95] Add support for pool#query without params (#12) --- index.js | 4 ++++ test/index.js | 10 ++++++++++ 2 files changed, 14 insertions(+) diff --git a/index.js b/index.js index 042d697b..9f4bbf2b 100644 --- a/index.js +++ b/index.js @@ -84,6 +84,10 @@ Pool.prototype.connect = function (cb) { Pool.prototype.take = Pool.prototype.connect Pool.prototype.query = function (text, values, cb) { + if (typeof values === 'function') { + cb = values + values = undefined + } return new this.Promise(function (resolve, reject) { this.connect(function (err, client, done) { if (err) return reject(err) diff --git a/test/index.js b/test/index.js index a7772b61..ce4f5ef8 100644 --- a/test/index.js +++ b/test/index.js @@ -32,6 +32,16 @@ describe('pool', function () { }) }) + it('can run a query with a callback without parameters', function (done) { + const pool = new Pool() + pool.query('SELECT 1 as num', function (err, res) { + expect(res.rows[0]).to.eql({ num: 1 }) + pool.end(function () { + done(err) + }) + }) + }) + it('can run a query with a callback', function (done) { const pool = new Pool() pool.query('SELECT $1::text as name', ['brianc'], function (err, res) { From baa5800a70381c9eb3b75accb426b0b02414ae42 Mon Sep 17 00:00:00 2001 From: brianc Date: Fri, 24 Jun 2016 09:53:46 -0700 Subject: [PATCH 30/95] Bump version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index afc1cd6d..ed226ad9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "pg-pool", - "version": "1.2.0", + "version": "1.2.1", "description": "Connection pool for node-postgres", "main": "index.js", "directories": { From d316ef55243c472e37a3084bb99657ac2774a274 Mon Sep 17 00:00:00 2001 From: Peter W Date: Sun, 26 Jun 2016 04:21:40 +1000 Subject: [PATCH 31/95] Fix example code for connect event (#14) --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 4b494efc..a6482659 100644 --- a/README.md +++ b/README.md @@ -207,8 +207,8 @@ Fired whenever the pool creates a __new__ `pg.Client` instance and successfully Example: ```js -var pg = require('pg') -var pool = new pg.Pool() +const Pool = require('pg-pool') +const pool = new Pool() var count = 0 @@ -220,10 +220,10 @@ pool .connect() .then(client => { return client - .query('SELECT $1::int AS "clientCount', [client.count]) + .query('SELECT $1::int AS "clientCount"', [client.count]) .then(res => console.log(res.rows[0].clientCount)) // outputs 0 .then(() => client) - })) + }) .then(client => client.release()) ``` From ce173f8c280d1d2aaf2f1a1f6fb356d3f667325f Mon Sep 17 00:00:00 2001 From: Peter W Date: Mon, 27 Jun 2016 14:39:36 +1000 Subject: [PATCH 32/95] Fix error event doc in README (#15) - making error event example code start by acquiring `pool` in the same way it is done in the example at the top for `create` --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index a6482659..7bf558f6 100644 --- a/README.md +++ b/README.md @@ -189,8 +189,8 @@ Emitted whenever an idle client in the pool encounters an error. This is common Example: ```js -var pg = require('pg') -var pool = new pg.Pool() +const Pool = require('pg-pool') +const pool = new Pool() // attach an error handler to the pool for when a connected, idle client // receives an error by being disconnected, etc From d653234a0c504941a3046f86632bfe42c3ad921a Mon Sep 17 00:00:00 2001 From: Brian C Date: Sun, 26 Jun 2016 22:00:16 -0700 Subject: [PATCH 33/95] Add acquire event (#16) * Add acquire event Add acquire event which fires every time a client is acquired from the pool. * Update README.md --- README.md | 67 +++++++++++++++++++++++++++++++++----------------- index.js | 1 + test/events.js | 21 ++++++++++++++++ 3 files changed, 66 insertions(+), 23 deletions(-) diff --git a/README.md b/README.md index 7bf558f6..4a4fa02e 100644 --- a/README.md +++ b/README.md @@ -15,17 +15,17 @@ npm i pg-pool pg to use pg-pool you must first create an instance of a pool ```js -const Pool = require('pg-pool') +var Pool = require('pg-pool') //by default the pool uses the same //configuration as whatever `pg` version you have installed -const pool = new Pool() +var pool = new Pool() //you can pass properties to the pool //these properties are passed unchanged to both the node-postgres Client constructor //and the node-pool (https://github.com/coopernurse/node-pool) constructor //allowing you to fully configure the behavior of both -const pool2 = new Pool({ +var pool2 = new Pool({ database: 'postgres', user: 'brianc', password: 'secret!', @@ -38,12 +38,12 @@ const pool2 = new Pool({ //you can supply a custom client constructor //if you want to use the native postgres client -const NativeClient = require('pg').native.Client -const nativePool = new Pool({ Client: NativeClient }) +var NativeClient = require('pg').native.Client +var nativePool = new Pool({ Client: NativeClient }) //you can even pool pg-native clients directly -const PgNativeClient = require('pg-native') -const pgNativePool = new Pool({ Client: PgNativeClient }) +var PgNativeClient = require('pg-native') +var pgNativePool = new Pool({ Client: PgNativeClient }) ``` ### acquire clients with a promise @@ -51,7 +51,7 @@ const pgNativePool = new Pool({ Client: PgNativeClient }) pg-pool supports a fully promise-based api for acquiring clients ```js -const pool = new Pool() +var pool = new Pool() pool.connect().then(client => { client.query('select $1::text as name', ['pg-pool']).then(res => { client.release() @@ -71,10 +71,10 @@ this ends up looking much nicer if you're using [co](https://github.com/tj/co) o ```js // with async/await (async () => { - const pool = new Pool() - const client = await pool.connect() + var pool = new Pool() + var client = await pool.connect() try { - const result = await client.query('select $1::text as name', ['brianc']) + var result = await client.query('select $1::text as name', ['brianc']) console.log('hello from', result.rows[0]) } finally { client.release() @@ -83,9 +83,9 @@ this ends up looking much nicer if you're using [co](https://github.com/tj/co) o // with co co(function * () { - const client = yield pool.connect() + var client = yield pool.connect() try { - const result = yield client.query('select $1::text as name', ['brianc']) + var result = yield client.query('select $1::text as name', ['brianc']) console.log('hello from', result.rows[0]) } finally { client.release() @@ -98,16 +98,16 @@ co(function * () { because its so common to just run a query and return the client to the pool afterward pg-pool has this built-in: ```js -const pool = new Pool() -const time = await pool.query('SELECT NOW()') -const name = await pool.query('select $1::text as name', ['brianc']) +var pool = new Pool() +var time = await pool.query('SELECT NOW()') +var name = await pool.query('select $1::text as name', ['brianc']) console.log(name.rows[0].name, 'says hello at', time.rows[0].name) ``` you can also use a callback here if you'd like: ```js -const pool = new Pool() +var pool = new Pool() pool.query('SELECT $1::text as name', ['brianc'], function (err, res) { console.log(res.rows[0].name) // brianc }) @@ -123,7 +123,7 @@ clients back to the pool after the query is done. pg-pool still and will always support the traditional callback api for acquiring a client. This is the exact API node-postgres has shipped with for years: ```js -const pool = new Pool() +var pool = new Pool() pool.connect((err, client, done) => { if (err) return done(err) @@ -143,8 +143,8 @@ When you are finished with the pool if all the clients are idle the pool will cl will shutdown gracefully. If you don't want to wait for the timeout you can end the pool as follows: ```js -const pool = new Pool() -const client = await pool.connect() +var pool = new Pool() +var client = await pool.connect() console.log(await client.query('select now()')) client.release() await pool.end() @@ -159,7 +159,7 @@ The pool should be a __long-lived object__ in your application. Generally you'l // correct usage: create the pool and let it live // 'globally' here, controlling access to it through exported methods -const pool = new pg.Pool() +var pool = new pg.Pool() // this is the right way to export the query method module.exports.query = (text, values) => { @@ -173,7 +173,7 @@ module.exports.connect = () => { // every time we called 'connect' to get a new client? // that's a bad thing & results in creating an unbounded // number of pools & therefore connections - const aPool = new pg.Pool() + var aPool = new pg.Pool() return aPool.connect() } ``` @@ -228,7 +228,28 @@ pool ``` -This allows you to do custom bootstrapping and manipulation of clients after they have been successfully connected to the PostgreSQL backend, but before any queries have been issued. +#### acquire + +Fired whenever the a client is acquired from the pool + +Example: + +This allows you to count the number of clients which have ever been acquired from the pool. + +```js +var Pool = require('pg-pool') +var pool = new Pool() + +var acquireCount = 0 +pool.on('acquire', function (client) { + console.log('acquired', (++acquireCount), 'clients') +}) + +for (var i = 0; i < 200; i++) { + pool.query('SELECT NOW()') +} + +``` ### environment variables diff --git a/index.js b/index.js index 9f4bbf2b..1fe22a14 100644 --- a/index.js +++ b/index.js @@ -60,6 +60,7 @@ Pool.prototype.connect = function (cb) { } this.log('acquire client') + this.emit('acquire', client) client.release = function (err) { delete client.release diff --git a/test/events.js b/test/events.js index 3b7225b7..671d1218 100644 --- a/test/events.js +++ b/test/events.js @@ -21,4 +21,25 @@ describe('events', function () { done() }) }) + + it('emits acquire every time a client is acquired', function (done) { + var pool = new Pool() + var acquireCount = 0 + pool.on('acquire', function (client) { + expect(client).to.be.ok() + acquireCount++ + }) + for (var i = 0; i < 10; i++) { + pool.connect(function (err, client, release) { + err ? done(err) : release() + release() + if (err) return done(err) + }) + pool.query('SELECT now()') + } + setTimeout(function () { + expect(acquireCount).to.be(20) + pool.end(done) + }, 40) + }) }) From d1c70ec9c1c8ec44a1ffccd99c5c57a9e96e3ef0 Mon Sep 17 00:00:00 2001 From: brianc Date: Sun, 26 Jun 2016 22:02:49 -0700 Subject: [PATCH 34/95] Update documentation --- README.md | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 4a4fa02e..d904d76b 100644 --- a/README.md +++ b/README.md @@ -242,13 +242,23 @@ var pool = new Pool() var acquireCount = 0 pool.on('acquire', function (client) { - console.log('acquired', (++acquireCount), 'clients') + acquireCount++ +}) + +var connectCount = 0 +pool.on('connect', function () { + connectCount++ }) for (var i = 0; i < 200; i++) { pool.query('SELECT NOW()') } +setTimeout(function () { + console.log('connect count:', connectCount) // output: connect count: 10 + console.log('acquire count:', acquireCount) // output: acquire count: 200 +}, 100) + ``` ### environment variables From 22a76ddd1dc910975914cd29e8c4efc8d7cbdeb1 Mon Sep 17 00:00:00 2001 From: brianc Date: Sun, 26 Jun 2016 22:05:46 -0700 Subject: [PATCH 35/95] Bump version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index ed226ad9..eebf42ca 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "pg-pool", - "version": "1.2.1", + "version": "1.3.0", "description": "Connection pool for node-postgres", "main": "index.js", "directories": { From ef1b15e13aae78a8928d37f7aad89bd86e921189 Mon Sep 17 00:00:00 2001 From: Brian C Date: Thu, 30 Jun 2016 15:22:49 -0700 Subject: [PATCH 36/95] Update README.md Add note on "bring your own promise" --- README.md | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/README.md b/README.md index d904d76b..1616d77a 100644 --- a/README.md +++ b/README.md @@ -275,6 +275,27 @@ PGSSLMODE=require Usually I will export these into my local environment via a `.env` file with environment settings or export them in `~/.bash_profile` or something similar. This way I get configurability which works with both the postgres suite of tools (`psql`, `pg_dump`, `pg_restore`) and node, I can vary the environment variables locally and in production, and it supports the concept of a [12-factor app](http://12factor.net/) out of the box. +## bring your own promise + +In versions of node `<=0.12.x` there is no native promise implementation available globally. You can polyfill the promise globally like this: + +```js +// first run `npm install promise-polyfill --save +if (typeof Promise == 'undefined') { + global.Promise = require('promise-polyfill') +} +``` + +You can use any other promise implementation you'd like. The pool also allows you to configure the promise implementation on a per-pool level: + +```js +var bluebirdPool = new Pool({ + Promise: require('bluebird') +}) +``` + +__please note:__ in node `<=0.12.x` the pool will throw if you do not provide a promise constructor in one of the two ways mentioned above. In node `>=4.0.0` the pool will use the native promise implementation by default; however, the two methods above still allow you to "bring your own." + ## tests To run tests clone the repo, `npm i` in the working dir, and then run `npm test` From 9ab7aff029aa9c879e3e04197935074f032b6b1c Mon Sep 17 00:00:00 2001 From: Brian C Date: Sun, 3 Jul 2016 18:17:34 -0500 Subject: [PATCH 37/95] Update code to run on >=0.10.0 (#17) * Replace const with var * Update code to run on 0.10.x + * Lint --- .travis.yml | 6 +++ index.js | 1 + package.json | 3 +- test/index.js | 124 ++++++++++++++++++++++++++---------------------- test/logging.js | 12 ++--- 5 files changed, 82 insertions(+), 64 deletions(-) diff --git a/.travis.yml b/.travis.yml index 12f9f47a..8a1126aa 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,6 +2,12 @@ language: node_js matrix: include: + - node_js: "0.10" + addons: + postgresql: "9.1" + - node_js: "0.12" + addons: + postgresql: "9.1" - node_js: "4" addons: postgresql: "9.1" diff --git a/index.js b/index.js index 1fe22a14..f126306e 100644 --- a/index.js +++ b/index.js @@ -89,6 +89,7 @@ Pool.prototype.query = function (text, values, cb) { cb = values values = undefined } + return new this.Promise(function (resolve, reject) { this.connect(function (err, client, done) { if (err) return reject(err) diff --git a/package.json b/package.json index eebf42ca..b4daf484 100644 --- a/package.json +++ b/package.json @@ -26,8 +26,7 @@ }, "homepage": "https://github.com/brianc/node-pg-pool#readme", "devDependencies": { - "bluebird": "3.4.0", - "co": "4.6.0", + "bluebird": "3.4.1", "expect.js": "0.3.1", "lodash": "4.13.1", "mocha": "^2.3.3", diff --git a/test/index.js b/test/index.js index ce4f5ef8..a7103af6 100644 --- a/test/index.js +++ b/test/index.js @@ -1,16 +1,20 @@ var expect = require('expect.js') -var co = require('co') var _ = require('lodash') var describe = require('mocha').describe var it = require('mocha').it +var Promise = require('bluebird') var Pool = require('../') +if (typeof global.Promise === 'undefined') { + global.Promise = Promise +} + describe('pool', function () { describe('with callbacks', function () { it('works totally unconfigured', function (done) { - const pool = new Pool() + var pool = new Pool() pool.connect(function (err, client, release) { if (err) return done(err) client.query('SELECT NOW()', function (err, res) { @@ -23,7 +27,7 @@ describe('pool', function () { }) it('passes props to clients', function (done) { - const pool = new Pool({ binary: true }) + var pool = new Pool({ binary: true }) pool.connect(function (err, client, release) { release() if (err) return done(err) @@ -33,7 +37,7 @@ describe('pool', function () { }) it('can run a query with a callback without parameters', function (done) { - const pool = new Pool() + var pool = new Pool() pool.query('SELECT 1 as num', function (err, res) { expect(res.rows[0]).to.eql({ num: 1 }) pool.end(function () { @@ -43,7 +47,7 @@ describe('pool', function () { }) it('can run a query with a callback', function (done) { - const pool = new Pool() + var pool = new Pool() pool.query('SELECT $1::text as name', ['brianc'], function (err, res) { expect(res.rows[0]).to.eql({ name: 'brianc' }) pool.end(function () { @@ -53,7 +57,7 @@ describe('pool', function () { }) it('removes client if it errors in background', function (done) { - const pool = new Pool() + var pool = new Pool() pool.connect(function (err, client, release) { release() if (err) return done(err) @@ -85,63 +89,71 @@ describe('pool', function () { }) describe('with promises', function () { - it('connects and disconnects', co.wrap(function * () { + it('connects and disconnects', function () { var pool = new Pool() - var client = yield pool.connect() - expect(pool.pool.availableObjectsCount()).to.be(0) - var res = yield client.query('select $1::text as name', ['hi']) - expect(res.rows).to.eql([{ name: 'hi' }]) - client.release() - expect(pool.pool.getPoolSize()).to.be(1) - expect(pool.pool.availableObjectsCount()).to.be(1) - return yield pool.end() - })) - - it('properly pools clients', co.wrap(function * () { - var pool = new Pool({ poolSize: 9 }) - yield _.times(30).map(function * () { - var client = yield pool.connect() - yield client.query('select $1::text as name', ['hi']) - client.release() + return pool.connect().then(function (client) { + expect(pool.pool.availableObjectsCount()).to.be(0) + return client.query('select $1::text as name', ['hi']).then(function (res) { + expect(res.rows).to.eql([{ name: 'hi' }]) + client.release() + expect(pool.pool.getPoolSize()).to.be(1) + expect(pool.pool.availableObjectsCount()).to.be(1) + return pool.end() + }) }) - expect(pool.pool.getPoolSize()).to.be(9) - return yield pool.end() - })) + }) - it('supports just running queries', co.wrap(function * () { + it('properly pools clients', function () { var pool = new Pool({ poolSize: 9 }) - var queries = _.times(30).map(function () { + return Promise.map(_.times(30), function () { + return pool.connect().then(function (client) { + return client.query('select $1::text as name', ['hi']).then(function (res) { + client.release() + return res + }) + }) + }).then(function (res) { + expect(res).to.have.length(30) + expect(pool.pool.getPoolSize()).to.be(9) + return pool.end() + }) + }) + + it('supports just running queries', function () { + var pool = new Pool({ poolSize: 9 }) + return Promise.map(_.times(30), function () { return pool.query('SELECT $1::text as name', ['hi']) + }).then(function (queries) { + expect(queries).to.have.length(30) + expect(pool.pool.getPoolSize()).to.be(9) + expect(pool.pool.availableObjectsCount()).to.be(9) + return pool.end() }) - yield queries - expect(pool.pool.getPoolSize()).to.be(9) - expect(pool.pool.availableObjectsCount()).to.be(9) - return yield pool.end() - })) + }) - it('recovers from all errors', co.wrap(function * () { - var pool = new Pool({ - poolSize: 9, - log: function (str, level) { - // Custom logging function to ensure we are not causing errors or warnings - // inside the `generic-pool` library. - if (level === 'error' || level === 'warn') { - expect().fail('An error or warning was logged from the generic pool library.\n' + - 'Level: ' + level + '\n' + - 'Message: ' + str + '\n') - } - } + it('recovers from all errors', function () { + var pool = new Pool() + + var errors = [] + return Promise.mapSeries(_.times(30), function () { + return pool.query('SELECT asldkfjasldkf') + .catch(function (e) { + errors.push(e) + }) + }).then(function () { + return pool.query('SELECT $1::text as name', ['hi']).then(function (res) { + expect(errors).to.have.length(30) + expect(res.rows).to.eql([{ name: 'hi' }]) + return pool.end() + }) }) - var count = 0 - - while (count++ < 30) { - try { - yield pool.query('SELECT lksjdfd') - } catch (e) {} - } - var res = yield pool.query('SELECT $1::text as name', ['hi']) - expect(res.rows).to.eql([{ name: 'hi' }]) - return yield pool.end() - })) + }) + }) +}) + +process.on('unhandledRejection', function (e) { + console.error(e.message, e.stack) + setImmediate(function () { + throw e }) }) diff --git a/test/logging.js b/test/logging.js index e03fa519..47389a5a 100644 --- a/test/logging.js +++ b/test/logging.js @@ -1,5 +1,4 @@ var expect = require('expect.js') -var co = require('co') var describe = require('mocha').describe var it = require('mocha').it @@ -7,14 +6,15 @@ var it = require('mocha').it var Pool = require('../') describe('logging', function () { - it('logs to supplied log function if given', co.wrap(function * () { + it('logs to supplied log function if given', function () { var messages = [] var log = function (msg) { messages.push(msg) } var pool = new Pool({ log: log }) - yield pool.query('SELECT NOW()') - expect(messages.length).to.be.greaterThan(0) - return pool.end() - })) + return pool.query('SELECT NOW()').then(function () { + expect(messages.length).to.be.greaterThan(0) + return pool.end() + }) + }) }) From 8c42c4172bc6ea4de78c540b19e219d37a30b984 Mon Sep 17 00:00:00 2001 From: brianc Date: Sun, 3 Jul 2016 16:17:55 -0700 Subject: [PATCH 38/95] Bump version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index b4daf484..d7772d04 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "pg-pool", - "version": "1.3.0", + "version": "1.3.1", "description": "Connection pool for node-postgres", "main": "index.js", "directories": { From e2d77719e7c63521b52b46397c000ec4ffa933df Mon Sep 17 00:00:00 2001 From: Timothy Haley Date: Mon, 18 Jul 2016 16:54:57 -0400 Subject: [PATCH 39/95] Url parsing example (#19) * Added URL parsing example * Typos and cleanup * Last typo --- README.md | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/README.md b/README.md index 1616d77a..f0dfe2b7 100644 --- a/README.md +++ b/README.md @@ -46,6 +46,40 @@ var PgNativeClient = require('pg-native') var pgNativePool = new Pool({ Client: PgNativeClient }) ``` +##### Note: +The Pool constructor does not support passing a Database URL as the parameter. To use pg-pool on heroku, for example, you need to parse the URL into a config object. Here is an example of how to parse a Database URL. + +```js +const Pool = require('pg-pool'); +const url = require('url') + +const params = url.parse(process.env.DATABASE_URL); +const auth = params.auth.split(':'); + +const config = { + user: auth[0], + password: auth[1], + host: params.hostname, + port: params.port, + database: params.pathname.split('/')[1], + ssl: true +}; + +const pool = Pool(config); + +/* + Transforms, 'progres://DBuser:secret@DBHost:#####/myDB', into + config = { + user: 'DBuser', + password: 'secret', + host: 'DBHost', + port: '#####', + database: 'myDB', + ssl: true + } +*/ +``` + ### acquire clients with a promise pg-pool supports a fully promise-based api for acquiring clients From 1d89029ba7f4eb45bb82c2e4263def6ce3c5728c Mon Sep 17 00:00:00 2001 From: Risto Novik Date: Tue, 26 Jul 2016 18:18:14 +0300 Subject: [PATCH 40/95] Added missing new keyword to Heroku example. (#21) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f0dfe2b7..39711ab3 100644 --- a/README.md +++ b/README.md @@ -65,7 +65,7 @@ const config = { ssl: true }; -const pool = Pool(config); +const pool = new Pool(config); /* Transforms, 'progres://DBuser:secret@DBHost:#####/myDB', into From 51fb7db8fad8bc83f95121d06af72752513aea61 Mon Sep 17 00:00:00 2001 From: Cody Greene Date: Fri, 5 Aug 2016 14:21:51 -0700 Subject: [PATCH 41/95] Support function-like construction (plus test) (#23) * Support function-like construction Remove the necessity of using `new` in front of the `Pool`, i.e. allow it to be used as a regular function. This is following https://github.com/brianc/node-postgres/issues/1077 * add Pool factory test --- index.js | 3 +++ test/index.js | 6 ++++++ 2 files changed, 9 insertions(+) diff --git a/index.js b/index.js index f126306e..6f622bac 100644 --- a/index.js +++ b/index.js @@ -4,6 +4,9 @@ var EventEmitter = require('events').EventEmitter var objectAssign = require('object-assign') var Pool = module.exports = function (options, Client) { + if (!(this instanceof Pool)) { + return new Pool(options, Client) + } EventEmitter.call(this) this.options = objectAssign({}, options) this.log = this.options.log || function () { } diff --git a/test/index.js b/test/index.js index a7103af6..e4616a43 100644 --- a/test/index.js +++ b/test/index.js @@ -12,6 +12,12 @@ if (typeof global.Promise === 'undefined') { } describe('pool', function () { + it('can be used as a factory function', function () { + var pool = Pool() + expect(pool instanceof Pool).to.be.ok() + expect(typeof pool.connect).to.be('function') + }) + describe('with callbacks', function () { it('works totally unconfigured', function (done) { var pool = new Pool() From eca2ea0ede3431b6f92ed09122638b7b792a8029 Mon Sep 17 00:00:00 2001 From: Cody Greene Date: Fri, 5 Aug 2016 14:22:50 -0700 Subject: [PATCH 42/95] emit "connect" event only on success and avoid double callback (#22) * fail: "connect" event only on success Double callback invocation will also cause this to fail. * avoid double callback: _create If `client.connect` returns an error, then the callback for `Pool#_create` is only invoked once. Also the `connect` event is only emitted on a successful connection, the client is otherwise rather useless. * legacy compat; don't use Object.assign * legacy compat; events.EventEmitter --- index.js | 7 ++++--- test/events.js | 30 ++++++++++++++++++++++++++++-- 2 files changed, 32 insertions(+), 5 deletions(-) diff --git a/index.js b/index.js index 6f622bac..80279db2 100644 --- a/index.js +++ b/index.js @@ -40,13 +40,14 @@ Pool.prototype._create = function (cb) { }.bind(this)) client.connect(function (err) { - this.log('client connected') - this.emit('connect', client) if (err) { this.log('client connection error:', err) cb(err) + } else { + this.log('client connected') + this.emit('connect', client) + cb(null, client) } - cb(err, err ? null : client) }.bind(this)) } diff --git a/test/events.js b/test/events.js index 671d1218..bcb523f0 100644 --- a/test/events.js +++ b/test/events.js @@ -1,8 +1,8 @@ var expect = require('expect.js') - +var EventEmitter = require('events').EventEmitter var describe = require('mocha').describe var it = require('mocha').it - +var objectAssign = require('object-assign') var Pool = require('../') describe('events', function () { @@ -22,6 +22,24 @@ describe('events', function () { }) }) + it('emits "connect" only with a successful connection', function (done) { + var pool = new Pool({ + // This client will always fail to connect + Client: mockClient({ + connect: function (cb) { + process.nextTick(function () { cb(new Error('bad news')) }) + } + }) + }) + pool.on('connect', function () { + throw new Error('should never get here') + }) + pool._create(function (err) { + if (err) done() + else done(new Error('expected failure')) + }) + }) + it('emits acquire every time a client is acquired', function (done) { var pool = new Pool() var acquireCount = 0 @@ -43,3 +61,11 @@ describe('events', function () { }, 40) }) }) + +function mockClient (methods) { + return function () { + var client = new EventEmitter() + objectAssign(client, methods) + return client + } +} From 9964208fe8ab996782cd5ee4f4bd2f334e8cbd16 Mon Sep 17 00:00:00 2001 From: brianc Date: Fri, 5 Aug 2016 16:23:05 -0500 Subject: [PATCH 43/95] Bump version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index d7772d04..2f9926bf 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "pg-pool", - "version": "1.3.1", + "version": "1.4.0", "description": "Connection pool for node-postgres", "main": "index.js", "directories": { From afce7ed6e377f619860a6ea7b4e9416693002836 Mon Sep 17 00:00:00 2001 From: "Brian M. Carlson" Date: Fri, 26 Aug 2016 09:05:46 -0500 Subject: [PATCH 44/95] Bump version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 2f9926bf..e373fb0e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "pg-pool", - "version": "1.4.0", + "version": "1.4.1", "description": "Connection pool for node-postgres", "main": "index.js", "directories": { From f2221a404022949e79587670eaf51b3f0596a4a8 Mon Sep 17 00:00:00 2001 From: "Brian M. Carlson" Date: Fri, 26 Aug 2016 09:07:54 -0500 Subject: [PATCH 45/95] Bump version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index e373fb0e..f4ad72fe 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "pg-pool", - "version": "1.4.1", + "version": "1.4.2", "description": "Connection pool for node-postgres", "main": "index.js", "directories": { From b091cc0d057d1a146333ee6d5e5e8ca376ef9f2b Mon Sep 17 00:00:00 2001 From: jphaas Date: Fri, 26 Aug 2016 10:08:15 -0400 Subject: [PATCH 46/95] Bug fix: Pool.query now calls cb if connect() fails (#25) * Pool.query calls cb if connect() fails Old behavior was that if connect called back with an error, the promise would get rejected but the cb function would never get called. * Test that Pool.query passes connection errors to callback * Fixes to standardjs compliance --- index.js | 7 ++++++- test/index.js | 11 +++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/index.js b/index.js index 80279db2..10d8dd01 100644 --- a/index.js +++ b/index.js @@ -96,7 +96,12 @@ Pool.prototype.query = function (text, values, cb) { return new this.Promise(function (resolve, reject) { this.connect(function (err, client, done) { - if (err) return reject(err) + if (err) { + if (cb) { + cb(err) + } + return reject(err) + } client.query(text, values, function (err, res) { done(err) err ? reject(err) : resolve(res) diff --git a/test/index.js b/test/index.js index e4616a43..d7752fce 100644 --- a/test/index.js +++ b/test/index.js @@ -62,6 +62,17 @@ describe('pool', function () { }) }) + it('passes connection errors to callback', function (done) { + var pool = new Pool({host: 'no-postgres-server-here.com'}) + pool.query('SELECT $1::text as name', ['brianc'], function (err, res) { + expect(res).to.be(undefined) + expect(err).to.be.an(Error) + pool.end(function (err) { + done(err) + }) + }) + }) + it('removes client if it errors in background', function (done) { var pool = new Pool() pool.connect(function (err, client, release) { From cf28f9357fa476b11a9257df8fbd1d7c8f939fbd Mon Sep 17 00:00:00 2001 From: "Brian M. Carlson" Date: Fri, 26 Aug 2016 09:11:18 -0500 Subject: [PATCH 47/95] Bump version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index f4ad72fe..8c1cd7fa 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "pg-pool", - "version": "1.4.2", + "version": "1.4.3", "description": "Connection pool for node-postgres", "main": "index.js", "directories": { From 6a7edabc22e36db7386c97ee93f08f957364f37d Mon Sep 17 00:00:00 2001 From: Yanlong Wang Date: Wed, 14 Sep 2016 10:26:03 +0800 Subject: [PATCH 48/95] When connection fail, emit the error. (#28) * When connection fail, emit the error. If client connect failed, emit the connection error rather than swallowing it. * Add test for connection error. --- index.js | 1 + test/events.js | 26 ++++++++++++++++++++++++-- 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/index.js b/index.js index 10d8dd01..f62f667d 100644 --- a/index.js +++ b/index.js @@ -43,6 +43,7 @@ Pool.prototype._create = function (cb) { if (err) { this.log('client connection error:', err) cb(err) + this.emit('error', err) } else { this.log('client connected') this.emit('connect', client) diff --git a/test/events.js b/test/events.js index bcb523f0..5045cc2c 100644 --- a/test/events.js +++ b/test/events.js @@ -22,7 +22,7 @@ describe('events', function () { }) }) - it('emits "connect" only with a successful connection', function (done) { + it('emits "error" with a failed connection', function (done) { var pool = new Pool({ // This client will always fail to connect Client: mockClient({ @@ -34,7 +34,29 @@ describe('events', function () { pool.on('connect', function () { throw new Error('should never get here') }) - pool._create(function (err) { + pool.on('error', function (err) { + if (err) done() + else done(new Error('expected failure')) + }) + pool.connect() + }) + + it('callback err with a failed connection', function (done) { + var pool = new Pool({ + // This client will always fail to connect + Client: mockClient({ + connect: function (cb) { + process.nextTick(function () { cb(new Error('bad news')) }) + } + }) + }) + pool.on('connect', function () { + throw new Error('should never get here') + }) + pool.on('error', function (err) { + if (!err) done(new Error('expected failure')) + }) + pool.connect(function (err) { if (err) done() else done(new Error('expected failure')) }) From fbdfc15b89caa0f45e782bf173bf95f63d3a472c Mon Sep 17 00:00:00 2001 From: "Brian M. Carlson" Date: Tue, 13 Sep 2016 21:29:56 -0500 Subject: [PATCH 49/95] Bump version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 8c1cd7fa..f8d7e63c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "pg-pool", - "version": "1.4.3", + "version": "1.5.0", "description": "Connection pool for node-postgres", "main": "index.js", "directories": { From fd802a385c25ad588350845485ab13e40ccf9752 Mon Sep 17 00:00:00 2001 From: Charmander <~@charmander.me> Date: Sun, 4 Dec 2016 15:20:24 -0800 Subject: [PATCH 50/95] =?UTF-8?q?Don=E2=80=99t=20create=20promises=20when?= =?UTF-8?q?=20callbacks=20are=20provided=20(#31)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Revert "When connection fail, emit the error. (#28)" This reverts commit 6a7edabc22e36db7386c97ee93f08f957364f37d. The callback passed to `Pool.prototype.connect` should be responsible for handling connection errors. The `error` event is documented to be: > Emitted whenever an idle client in the pool encounters an error. This isn’t the case of an idle client in the pool; it never makes it into the pool. It also breaks tests on pg’s master because of nonspecific dependencies. * Don’t create promises when callbacks are provided It’s incorrect to do so. One consequence is that a rejected promise will be unhandled, which is currently annoying, but also dangerous in the future: > DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code. The way callbacks are used currently also causes #24 (hiding of errors thrown synchronously from the callback). One fix for that would be to call them asynchronously from inside the `new Promise()` executor: process.nextTick(cb, error); I don’t think it’s worth implementing, though, since it would still be backwards-incompatible – just less obvious about it. Also fixes a bug where the `Pool.prototype.connect` callback would be called twice if there was an error. * Use Node-0.10-compatible `process.nextTick` --- index.js | 54 ++++++++++++++++++++++++++++++++------------------ test/events.js | 26 ++---------------------- test/index.js | 26 ++++++++++++++++++++++++ 3 files changed, 63 insertions(+), 43 deletions(-) diff --git a/index.js b/index.js index f62f667d..d3551868 100644 --- a/index.js +++ b/index.js @@ -22,6 +22,32 @@ var Pool = module.exports = function (options, Client) { util.inherits(Pool, EventEmitter) +Pool.prototype._promise = function (cb, executor) { + if (!cb) { + return new this.Promise(executor) + } + + function resolved (value) { + process.nextTick(function () { + cb(null, value) + }) + } + + function rejected (error) { + process.nextTick(function () { + cb(error) + }) + } + + executor(resolved, rejected) +} + +Pool.prototype._promiseNoCallback = function (callback, executor) { + return callback + ? executor() + : new this.Promise(executor) +} + Pool.prototype._destroy = function (client) { if (client._destroying) return client._destroying = true @@ -43,7 +69,6 @@ Pool.prototype._create = function (cb) { if (err) { this.log('client connection error:', err) cb(err) - this.emit('error', err) } else { this.log('client connected') this.emit('connect', client) @@ -53,15 +78,17 @@ Pool.prototype._create = function (cb) { } Pool.prototype.connect = function (cb) { - return new this.Promise(function (resolve, reject) { + return this._promiseNoCallback(cb, function (resolve, reject) { this.log('acquire client begin') this.pool.acquire(function (err, client) { if (err) { this.log('acquire client. error:', err) if (cb) { cb(err, null, function () {}) + } else { + reject(err) } - return reject(err) + return } this.log('acquire client') @@ -80,9 +107,9 @@ Pool.prototype.connect = function (cb) { if (cb) { cb(null, client, client.release) + } else { + resolve(client) } - - return resolve(client) }.bind(this)) }.bind(this)) } @@ -95,20 +122,14 @@ Pool.prototype.query = function (text, values, cb) { values = undefined } - return new this.Promise(function (resolve, reject) { + return this._promise(cb, function (resolve, reject) { this.connect(function (err, client, done) { if (err) { - if (cb) { - cb(err) - } return reject(err) } client.query(text, values, function (err, res) { done(err) err ? reject(err) : resolve(res) - if (cb) { - cb(err, res) - } }) }) }.bind(this)) @@ -116,15 +137,10 @@ Pool.prototype.query = function (text, values, cb) { Pool.prototype.end = function (cb) { this.log('draining pool') - return new this.Promise(function (resolve, reject) { + return this._promise(cb, function (resolve, reject) { this.pool.drain(function () { this.log('pool drained, calling destroy all now') - this.pool.destroyAllNow(function () { - if (cb) { - cb() - } - resolve() - }) + this.pool.destroyAllNow(resolve) }.bind(this)) }.bind(this)) } diff --git a/test/events.js b/test/events.js index 5045cc2c..bcb523f0 100644 --- a/test/events.js +++ b/test/events.js @@ -22,7 +22,7 @@ describe('events', function () { }) }) - it('emits "error" with a failed connection', function (done) { + it('emits "connect" only with a successful connection', function (done) { var pool = new Pool({ // This client will always fail to connect Client: mockClient({ @@ -34,29 +34,7 @@ describe('events', function () { pool.on('connect', function () { throw new Error('should never get here') }) - pool.on('error', function (err) { - if (err) done() - else done(new Error('expected failure')) - }) - pool.connect() - }) - - it('callback err with a failed connection', function (done) { - var pool = new Pool({ - // This client will always fail to connect - Client: mockClient({ - connect: function (cb) { - process.nextTick(function () { cb(new Error('bad news')) }) - } - }) - }) - pool.on('connect', function () { - throw new Error('should never get here') - }) - pool.on('error', function (err) { - if (!err) done(new Error('expected failure')) - }) - pool.connect(function (err) { + pool._create(function (err) { if (err) done() else done(new Error('expected failure')) }) diff --git a/test/index.js b/test/index.js index d7752fce..5f6d0ad5 100644 --- a/test/index.js +++ b/test/index.js @@ -103,6 +103,32 @@ describe('pool', function () { pool.end(done) }) }) + + it('does not create promises when connecting', function (done) { + var pool = new Pool() + var returnValue = pool.connect(function (err, client, release) { + release() + if (err) return done(err) + pool.end(done) + }) + expect(returnValue).to.be(undefined) + }) + + it('does not create promises when querying', function (done) { + var pool = new Pool() + var returnValue = pool.query('SELECT 1 as num', function (err) { + pool.end(function () { + done(err) + }) + }) + expect(returnValue).to.be(undefined) + }) + + it('does not create promises when ending', function (done) { + var pool = new Pool() + var returnValue = pool.end(done) + expect(returnValue).to.be(undefined) + }) }) describe('with promises', function () { From ab70f579234cb51f7508cb18d09ff6d4e6944948 Mon Sep 17 00:00:00 2001 From: Brian Carlson Date: Sun, 4 Dec 2016 17:27:10 -0600 Subject: [PATCH 51/95] Bump version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index f8d7e63c..0e01513c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "pg-pool", - "version": "1.5.0", + "version": "1.6.0", "description": "Connection pool for node-postgres", "main": "index.js", "directories": { From 2aed8bf7b3a03917b14336f5048c2092109f40b9 Mon Sep 17 00:00:00 2001 From: Shakeel Mohamed Date: Mon, 6 Mar 2017 09:47:30 -0800 Subject: [PATCH 52/95] Add syntax highlighting to code block in README (#42) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 39711ab3..b86447b4 100644 --- a/README.md +++ b/README.md @@ -188,7 +188,7 @@ await pool.end() The pool should be a __long-lived object__ in your application. Generally you'll want to instantiate one pool when your app starts up and use the same instance of the pool throughout the lifetime of your application. If you are frequently creating a new pool within your code you likely don't have your pool initialization code in the correct place. Example: -``` +```js // assume this is a file in your program at ./your-app/lib/db.js // correct usage: create the pool and let it live From 0f323999fc071ee471a34f0a937d64087ae23f6a Mon Sep 17 00:00:00 2001 From: Raul Ochoa Date: Sat, 1 Apr 2017 18:28:27 +0200 Subject: [PATCH 53/95] Access `Promise` through global (#39) This matches the proposed way in "bring your own promise". --- index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.js b/index.js index d3551868..43cccae3 100644 --- a/index.js +++ b/index.js @@ -11,7 +11,7 @@ var Pool = module.exports = function (options, Client) { this.options = objectAssign({}, options) this.log = this.options.log || function () { } this.Client = this.options.Client || Client || require('pg').Client - this.Promise = this.options.Promise || Promise + this.Promise = this.options.Promise || global.Promise this.options.max = this.options.max || this.options.poolSize || 10 this.options.create = this.options.create || this._create.bind(this) From 0b3d68ef31e4f315214be7f150123fe7088f2815 Mon Sep 17 00:00:00 2001 From: Lewis J Ellis Date: Sat, 1 Apr 2017 09:31:33 -0700 Subject: [PATCH 54/95] Bump generic-pool dep to 2.4.3 (#35) --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 0e01513c..41514287 100644 --- a/package.json +++ b/package.json @@ -35,7 +35,7 @@ "standard-format": "2.2.1" }, "dependencies": { - "generic-pool": "2.4.2", + "generic-pool": "2.4.3", "object-assign": "4.1.0" } } From 5918a9e10547a96d8b059714bc2140a296ad3656 Mon Sep 17 00:00:00 2001 From: "Brian M. Carlson" Date: Sat, 1 Apr 2017 18:43:04 -0500 Subject: [PATCH 55/95] Bump version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 41514287..f6e76a67 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "pg-pool", - "version": "1.6.0", + "version": "1.7.0", "description": "Connection pool for node-postgres", "main": "index.js", "directories": { From c89b74bb5d4ccac201414bf3c6f4860cbb7b74c5 Mon Sep 17 00:00:00 2001 From: Russ Tyndall Date: Thu, 13 Apr 2017 09:26:53 -0400 Subject: [PATCH 56/95] Make a test case and fix for errors drainging the pool (#49) * this bug leaves the pool empty even if there is work to be done, if there are enough consecutive errors to empty the pool re brianc/node-pg-pool#48 --- index.js | 28 ++++++++++++++++++++++++++++ test/index.js | 30 ++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+) diff --git a/index.js b/index.js index 43cccae3..aec3a1ec 100644 --- a/index.js +++ b/index.js @@ -3,6 +3,26 @@ var util = require('util') var EventEmitter = require('events').EventEmitter var objectAssign = require('object-assign') +// there is a bug in the generic pool where it will not recreate +// destroyed workers (even if there is waiting work to do) unless +// there is a min specified. Make sure we keep some connections +// SEE: https://github.com/coopernurse/node-pool/pull/186 +// SEE: https://github.com/brianc/node-pg-pool/issues/48 +// SEE: https://github.com/strongloop/loopback-connector-postgresql/issues/231 +function _ensureMinimum () { + var i, diff, waiting + if (this._draining) return + waiting = this._waitingClients.size() + if (this._factory.min > 0) { // we have positive specified minimum + diff = this._factory.min - this._count + } else if (waiting > 0) { // we have no minimum, but we do have work to do + diff = Math.min(waiting, this._factory.max - this._count) + } + for (i = 0; i < diff; i++) { + this._createResource() + } +}; + var Pool = module.exports = function (options, Client) { if (!(this instanceof Pool)) { return new Pool(options, Client) @@ -17,6 +37,14 @@ var Pool = module.exports = function (options, Client) { this.options.create = this.options.create || this._create.bind(this) this.options.destroy = this.options.destroy || this._destroy.bind(this) this.pool = new genericPool.Pool(this.options) + // Monkey patch to ensure we always finish our work + // - There is a bug where callbacks go uncalled if min is not set + // - We might still not want a connection to *always* exist + // - but we do want to create up to max connections if we have work + // - still waiting + // This should be safe till the version of pg-pool is upgraded + // SEE: https://github.com/coopernurse/node-pool/pull/186 + this.pool._ensureMinimum = _ensureMinimum this.onCreate = this.options.onCreate } diff --git a/test/index.js b/test/index.js index 5f6d0ad5..3f2b99d9 100644 --- a/test/index.js +++ b/test/index.js @@ -194,6 +194,36 @@ describe('pool', function () { }) }) +describe('pool error handling', function () { + it('Should complete these queries without dying', function (done) { + var pgPool = new Pool() + var pool = pgPool.pool + pool._factory.max = 1 + pool._factory.min = null + var errors = 0 + var shouldGet = 0 + function runErrorQuery () { + shouldGet++ + return new Promise(function (resolve, reject) { + pgPool.query("SELECT 'asd'+1 ").then(function (res) { + reject(res) // this should always error + }).catch(function (err) { + errors++ + resolve(err) + }) + }) + } + var ps = [] + for (var i = 0; i < 5; i++) { + ps.push(runErrorQuery()) + } + Promise.all(ps).then(function () { + expect(shouldGet).to.eql(errors) + done() + }) + }) +}) + process.on('unhandledRejection', function (e) { console.error(e.message, e.stack) setImmediate(function () { From 659a448fabe7e4426666748cb6daf2350c33e840 Mon Sep 17 00:00:00 2001 From: brianc Date: Thu, 13 Apr 2017 10:50:00 -0500 Subject: [PATCH 57/95] Bump version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index f6e76a67..763b273c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "pg-pool", - "version": "1.7.0", + "version": "1.7.1", "description": "Connection pool for node-postgres", "main": "index.js", "directories": { From 52c96a4b2e516d069a96ab8435b9a9395830fca7 Mon Sep 17 00:00:00 2001 From: Amila Welihinda Date: Mon, 29 May 2017 08:48:58 -0700 Subject: [PATCH 58/95] Create LICENSE (#54) * Create LICENSE * Update LICENSE --- LICENSE | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 LICENSE diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..4e905814 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2017 Brian M. Carlson + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. From a51fe56bc11ce8c5370db51cb3f5b171f95d38e4 Mon Sep 17 00:00:00 2001 From: Brian C Date: Fri, 2 Jun 2017 09:40:11 -0500 Subject: [PATCH 59/95] Update .travis.yml Drop node@0.10 and node@0.12 from the test matrix. --- .travis.yml | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/.travis.yml b/.travis.yml index 8a1126aa..155d5062 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,18 +2,12 @@ language: node_js matrix: include: - - node_js: "0.10" - addons: - postgresql: "9.1" - - node_js: "0.12" - addons: - postgresql: "9.1" - node_js: "4" addons: postgresql: "9.1" - - node_js: "5" - addons: - postgresql: "9.4" - node_js: "6" addons: postgresql: "9.4" + - node_js: "8" + addons: + postgresql: "9.6" From f93385284df62d8aec3882721f288ba0f30b0727 Mon Sep 17 00:00:00 2001 From: Brian C Date: Fri, 2 Jun 2017 13:00:42 -0500 Subject: [PATCH 60/95] Update .travis.yml --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 155d5062..47358a12 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,4 +10,4 @@ matrix: postgresql: "9.4" - node_js: "8" addons: - postgresql: "9.6" + postgresql: "9.4" From 959d89e043bfba0fd3e16e48483dbd10d25e29fb Mon Sep 17 00:00:00 2001 From: brianc Date: Wed, 7 Jun 2017 22:35:55 -0500 Subject: [PATCH 61/95] Add test for connectionString property delegation --- index.js | 2 +- test/connection-strings.js | 22 ++++++++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) create mode 100644 test/connection-strings.js diff --git a/index.js b/index.js index aec3a1ec..ccad9686 100644 --- a/index.js +++ b/index.js @@ -96,7 +96,7 @@ Pool.prototype._create = function (cb) { client.connect(function (err) { if (err) { this.log('client connection error:', err) - cb(err) + cb(err, client) } else { this.log('client connected') this.emit('connect', client) diff --git a/test/connection-strings.js b/test/connection-strings.js new file mode 100644 index 00000000..cc624a14 --- /dev/null +++ b/test/connection-strings.js @@ -0,0 +1,22 @@ +var expect = require('expect.js') +var describe = require('mocha').describe +var it = require('mocha').it +var Pool = require('../') + +describe('Connection strings', function () { + it('pool delegates connectionString property to client', function () { + var pool = new Pool({ + connectionString: 'postgres://foo:bar@baz:1234/xur' + }) + pool.connect(function (err, client) { + expect(err).to.not.be(undefined) + expect(client).to.not.be(undefined) + expect(client.username).to.equal('foo') + expect(client.password).to.equal('bar') + expect(client.database).to.equal('baz') + expect(client.port).to.equal(1234) + expect(client.database).to.equal('xur') + }) + }) +}) + From 5061068b0455ba77f2d317cb17543911796c9d25 Mon Sep 17 00:00:00 2001 From: "Brian M. Carlson" Date: Thu, 8 Jun 2017 18:18:49 -0500 Subject: [PATCH 62/95] Fix test --- index.js | 2 +- test/connection-strings.js | 24 ++++++++++++++++-------- 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/index.js b/index.js index ccad9686..2e30ac6e 100644 --- a/index.js +++ b/index.js @@ -96,7 +96,7 @@ Pool.prototype._create = function (cb) { client.connect(function (err) { if (err) { this.log('client connection error:', err) - cb(err, client) + cb(err, null) } else { this.log('client connected') this.emit('connect', client) diff --git a/test/connection-strings.js b/test/connection-strings.js index cc624a14..8ee3d074 100644 --- a/test/connection-strings.js +++ b/test/connection-strings.js @@ -4,18 +4,26 @@ var it = require('mocha').it var Pool = require('../') describe('Connection strings', function () { - it('pool delegates connectionString property to client', function () { + it('pool delegates connectionString property to client', function (done) { + var connectionString = 'postgres://foo:bar@baz:1234/xur' + var pool = new Pool({ - connectionString: 'postgres://foo:bar@baz:1234/xur' + // use a fake client so we can check we're passed the connectionString + Client: function (args) { + expect(args.connectionString).to.equal(connectionString) + return { + connect: function (cb) { + cb(new Error('testing')) + }, + on: function () { } + } + }, + connectionString: connectionString }) + pool.connect(function (err, client) { expect(err).to.not.be(undefined) - expect(client).to.not.be(undefined) - expect(client.username).to.equal('foo') - expect(client.password).to.equal('bar') - expect(client.database).to.equal('baz') - expect(client.port).to.equal(1234) - expect(client.database).to.equal('xur') + done() }) }) }) From f7b1edc7bbc994427b59cae57237cc52c8d51950 Mon Sep 17 00:00:00 2001 From: Brian C Date: Sun, 18 Jun 2017 14:09:18 -0500 Subject: [PATCH 63/95] Add client to error event emitter (#65) When the pool emits an error pass the client as the 2nd parameter to the `on('error')` handler. --- index.js | 2 +- test/events.js | 16 ++++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/index.js b/index.js index 2e30ac6e..7e0f0ce5 100644 --- a/index.js +++ b/index.js @@ -90,7 +90,7 @@ Pool.prototype._create = function (cb) { this.log('connected client error:', e) this.pool.destroy(client) e.client = client - this.emit('error', e) + this.emit('error', e, client) }.bind(this)) client.connect(function (err) { diff --git a/test/events.js b/test/events.js index bcb523f0..759dff02 100644 --- a/test/events.js +++ b/test/events.js @@ -60,6 +60,22 @@ describe('events', function () { pool.end(done) }, 40) }) + + it('emits error and client if an idle client in the pool hits an error', function (done) { + var pool = new Pool() + pool.connect(function (err, client) { + expect(err).to.equal(null) + client.release() + setImmediate(function () { + client.emit('error', new Error('problem')) + }) + pool.once('error', function (err, errClient) { + expect(err.message).to.equal('problem') + expect(errClient).to.equal(client) + done() + }) + }) + }) }) function mockClient (methods) { From 0c32c57e0e00f695e650fb1c8b9aadac7d901cd3 Mon Sep 17 00:00:00 2001 From: "Brian M. Carlson" Date: Sun, 18 Jun 2017 14:09:54 -0500 Subject: [PATCH 64/95] Bump version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 763b273c..b3424f1e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "pg-pool", - "version": "1.7.1", + "version": "1.8.0", "description": "Connection pool for node-postgres", "main": "index.js", "directories": { From a0eb36d81938e488b3bc5369faee74fe22f36949 Mon Sep 17 00:00:00 2001 From: Brian C Date: Thu, 13 Jul 2017 22:37:08 -0500 Subject: [PATCH 65/95] 2.0 (#67) * Initial work * Make progress on custom pool * Make all original tests pass * Fix test race * Fix test when DNS is missing * Test more error conditions * Add test for byop * Add BYOP tests for errors * Add test for idle client error expunging * Fix typo * Replace var with const/let * Remove var usage * Fix linting * Work on connection timeout * Work on error condition tests * Remove logging * Add connection timeout * Add idle timeout * Test for returning to client to pool after error fixes #48 * Add idleTimeout support to native client * Add pg as peer dependency fixes #45 * Rename properties * Fix lint * use strict * Add draining to pool.end * Ensure ending pools drain properly * Remove yarn.lock * Remove object-assign * Remove node 8 * Remove closure for waiter construction * Ensure client.connect is never sync * Fix lint * Change to es6 class * Code cleanup & lint fixes --- .travis.yml | 3 - index.js | 378 ++++++++++++++++++++------------- package.json | 9 +- test/bring-your-own-promise.js | 36 ++++ test/connection-strings.js | 12 +- test/connection-timeout.js | 62 ++++++ test/ending.js | 34 +++ test/error-handling.js | 135 ++++++++++++ test/events.js | 48 ++--- test/idle-timeout.js | 54 +++++ test/index.js | 173 ++++++++------- test/logging.js | 14 +- test/mocha.opts | 2 + test/setup.js | 10 + test/sizing.js | 44 ++++ test/timeout.js | 0 test/verify.js | 25 +++ 17 files changed, 755 insertions(+), 284 deletions(-) create mode 100644 test/bring-your-own-promise.js create mode 100644 test/connection-timeout.js create mode 100644 test/ending.js create mode 100644 test/error-handling.js create mode 100644 test/idle-timeout.js create mode 100644 test/setup.js create mode 100644 test/sizing.js create mode 100644 test/timeout.js create mode 100644 test/verify.js diff --git a/.travis.yml b/.travis.yml index 47358a12..e1a0ce70 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,6 +8,3 @@ matrix: - node_js: "6" addons: postgresql: "9.4" - - node_js: "8" - addons: - postgresql: "9.4" diff --git a/index.js b/index.js index 7e0f0ce5..c317e531 100644 --- a/index.js +++ b/index.js @@ -1,174 +1,248 @@ -var genericPool = require('generic-pool') -var util = require('util') -var EventEmitter = require('events').EventEmitter -var objectAssign = require('object-assign') +'use strict' +const EventEmitter = require('events').EventEmitter -// there is a bug in the generic pool where it will not recreate -// destroyed workers (even if there is waiting work to do) unless -// there is a min specified. Make sure we keep some connections -// SEE: https://github.com/coopernurse/node-pool/pull/186 -// SEE: https://github.com/brianc/node-pg-pool/issues/48 -// SEE: https://github.com/strongloop/loopback-connector-postgresql/issues/231 -function _ensureMinimum () { - var i, diff, waiting - if (this._draining) return - waiting = this._waitingClients.size() - if (this._factory.min > 0) { // we have positive specified minimum - diff = this._factory.min - this._count - } else if (waiting > 0) { // we have no minimum, but we do have work to do - diff = Math.min(waiting, this._factory.max - this._count) - } - for (i = 0; i < diff; i++) { - this._createResource() - } -}; +const NOOP = function () { } -var Pool = module.exports = function (options, Client) { - if (!(this instanceof Pool)) { - return new Pool(options, Client) +class IdleItem { + constructor (client, timeoutId) { + this.client = client + this.timeoutId = timeoutId } - EventEmitter.call(this) - this.options = objectAssign({}, options) - this.log = this.options.log || function () { } - this.Client = this.options.Client || Client || require('pg').Client - this.Promise = this.options.Promise || global.Promise - - this.options.max = this.options.max || this.options.poolSize || 10 - this.options.create = this.options.create || this._create.bind(this) - this.options.destroy = this.options.destroy || this._destroy.bind(this) - this.pool = new genericPool.Pool(this.options) - // Monkey patch to ensure we always finish our work - // - There is a bug where callbacks go uncalled if min is not set - // - We might still not want a connection to *always* exist - // - but we do want to create up to max connections if we have work - // - still waiting - // This should be safe till the version of pg-pool is upgraded - // SEE: https://github.com/coopernurse/node-pool/pull/186 - this.pool._ensureMinimum = _ensureMinimum - this.onCreate = this.options.onCreate } -util.inherits(Pool, EventEmitter) - -Pool.prototype._promise = function (cb, executor) { - if (!cb) { - return new this.Promise(executor) - } - - function resolved (value) { - process.nextTick(function () { - cb(null, value) - }) - } - - function rejected (error) { - process.nextTick(function () { - cb(error) - }) - } - - executor(resolved, rejected) +function throwOnRelease () { + throw new Error('Release called on client which has already been released to the pool.') } -Pool.prototype._promiseNoCallback = function (callback, executor) { - return callback - ? executor() - : new this.Promise(executor) +function release (client, err) { + client.release = throwOnRelease + if (err) { + this._remove(client) + this._pulseQueue() + return + } + + // idle timeout + let tid + if (this.options.idleTimeoutMillis) { + tid = setTimeout(() => { + this.log('remove idle client') + this._remove(client) + }, this.idleTimeoutMillis) + } + + if (this.ending) { + this._remove(client) + } else { + this._idle.push(new IdleItem(client, tid)) + } + this._pulseQueue() } -Pool.prototype._destroy = function (client) { - if (client._destroying) return - client._destroying = true - client.end() +function promisify (Promise, callback) { + if (callback) { + return { callback: callback, result: undefined } + } + let rej + let res + const cb = function (err, client) { + err ? rej(err) : res(client) + } + const result = new Promise(function (resolve, reject) { + res = resolve + rej = reject + }) + return { callback: cb, result: result } } -Pool.prototype._create = function (cb) { - this.log('connecting new client') - var client = new this.Client(this.options) +class Pool extends EventEmitter { + constructor (options, Client) { + super() + this.options = Object.assign({}, options) + this.options.max = this.options.max || this.options.poolSize || 10 + this.log = this.options.log || function () { } + this.Client = this.options.Client || Client || require('pg').Client + this.Promise = this.options.Promise || global.Promise - client.on('error', function (e) { - this.log('connected client error:', e) - this.pool.destroy(client) - e.client = client - this.emit('error', e, client) - }.bind(this)) + this._clients = [] + this._idle = [] + this._pendingQueue = [] + this._endCallback = undefined + this.ending = false + } - client.connect(function (err) { - if (err) { - this.log('client connection error:', err) - cb(err, null) - } else { - this.log('client connected') - this.emit('connect', client) - cb(null, client) + _isFull () { + return this._clients.length >= this.options.max + } + + _pulseQueue () { + this.log('pulse queue') + if (this.ending) { + this.log('pulse queue on ending') + if (this._idle.length) { + this._idle.map(item => { + this._remove(item.client) + }) + } + if (!this._clients.length) { + this._endCallback() + } + return } - }.bind(this)) -} - -Pool.prototype.connect = function (cb) { - return this._promiseNoCallback(cb, function (resolve, reject) { - this.log('acquire client begin') - this.pool.acquire(function (err, client) { - if (err) { - this.log('acquire client. error:', err) - if (cb) { - cb(err, null, function () {}) - } else { - reject(err) - } - return - } - - this.log('acquire client') + // if we don't have any waiting, do nothing + if (!this._pendingQueue.length) { + this.log('no queued requests') + return + } + // if we don't have any idle clients and we have no more room do nothing + if (!this._idle.length && this._isFull()) { + return + } + const waiter = this._pendingQueue.shift() + if (this._idle.length) { + const idleItem = this._idle.pop() + clearTimeout(idleItem.timeoutId) + const client = idleItem.client + client.release = release.bind(this, client) this.emit('acquire', client) - - client.release = function (err) { - delete client.release - if (err) { - this.log('destroy client. error:', err) - this.pool.destroy(client) - } else { - this.log('release client') - this.pool.release(client) - } - }.bind(this) - - if (cb) { - cb(null, client, client.release) - } else { - resolve(client) - } - }.bind(this)) - }.bind(this)) -} - -Pool.prototype.take = Pool.prototype.connect - -Pool.prototype.query = function (text, values, cb) { - if (typeof values === 'function') { - cb = values - values = undefined + return waiter(undefined, client, client.release) + } + if (!this._isFull()) { + return this.connect(waiter) + } + throw new Error('unexpected condition') } - return this._promise(cb, function (resolve, reject) { - this.connect(function (err, client, done) { - if (err) { - return reject(err) + _remove (client) { + this._idle = this._idle.filter(item => item.client !== client) + this._clients = this._clients.filter(c => c !== client) + client.end() + this.emit('remove', client) + } + + connect (cb) { + if (this.ending) { + const err = new Error('Cannot use a pool after calling end on the pool') + return cb ? cb(err) : this.Promise.reject(err) + } + if (this._clients.length >= this.options.max || this._idle.length) { + const response = promisify(this.Promise, cb) + const result = response.result + this._pendingQueue.push(response.callback) + // if we have idle clients schedule a pulse immediately + if (this._idle.length) { + process.nextTick(() => this._pulseQueue()) } - client.query(text, values, function (err, res) { - done(err) - err ? reject(err) : resolve(res) + return result + } + + const client = new this.Client(this.options) + this._clients.push(client) + const idleListener = (err) => { + err.client = client + client.removeListener('error', idleListener) + client.on('error', () => { + this.log('additional client error after disconnection due to error', err) + }) + this._remove(client) + // TODO - document that once the pool emits an error + // the client has already been closed & purged and is unusable + this.emit('error', err, client) + } + + this.log('connecting new client') + + // connection timeout logic + let tid + let timeoutHit = false + if (this.options.connectionTimeoutMillis) { + tid = setTimeout(() => { + this.log('ending client due to timeout') + timeoutHit = true + // force kill the node driver, and let libpq do its teardown + client.connection ? client.connection.stream.destroy() : client.end() + }, this.options.connectionTimeoutMillis) + } + + const response = promisify(this.Promise, cb) + cb = response.callback + + this.log('connecting new client') + client.connect((err) => { + this.log('new client connected') + if (tid) { + clearTimeout(tid) + } + client.on('error', idleListener) + if (err) { + // remove the dead client from our list of clients + this._clients = this._clients.filter(c => c !== client) + if (timeoutHit) { + err.message = 'Connection terminiated due to connection timeout' + } + cb(err, undefined, NOOP) + } else { + client.release = release.bind(this, client) + this.emit('connect', client) + this.emit('acquire', client) + if (this.options.verify) { + this.options.verify(client, cb) + } else { + cb(undefined, client, client.release) + } + } + }) + return response.result + } + + query (text, values, cb) { + if (typeof values === 'function') { + cb = values + values = undefined + } + const response = promisify(this.Promise, cb) + cb = response.callback + this.connect((err, client) => { + if (err) { + return cb(err) + } + this.log('dispatching query') + client.query(text, values, (err, res) => { + this.log('query dispatched') + client.release(err) + if (err) { + return cb(err) + } else { + return cb(undefined, res) + } }) }) - }.bind(this)) -} + return response.result + } -Pool.prototype.end = function (cb) { - this.log('draining pool') - return this._promise(cb, function (resolve, reject) { - this.pool.drain(function () { - this.log('pool drained, calling destroy all now') - this.pool.destroyAllNow(resolve) - }.bind(this)) - }.bind(this)) + end (cb) { + this.log('ending') + if (this.ending) { + const err = new Error('Called end on pool more than once') + return cb ? cb(err) : this.Promise.reject(err) + } + this.ending = true + const promised = promisify(this.Promise, cb) + this._endCallback = promised.callback + this._pulseQueue() + return promised.result + } + + get waitingCount () { + return this._pendingQueue.length + } + + get idleCount () { + return this._idle.length + } + + get totalCount () { + return this._clients.length + } } +module.exports = Pool diff --git a/package.json b/package.json index b3424f1e..392375bb 100644 --- a/package.json +++ b/package.json @@ -27,15 +27,16 @@ "homepage": "https://github.com/brianc/node-pg-pool#readme", "devDependencies": { "bluebird": "3.4.1", + "co": "4.6.0", "expect.js": "0.3.1", "lodash": "4.13.1", "mocha": "^2.3.3", - "pg": "5.1.0", + "pg": "*", "standard": "7.1.2", "standard-format": "2.2.1" }, - "dependencies": { - "generic-pool": "2.4.3", - "object-assign": "4.1.0" + "dependencies": {}, + "peerDependencies": { + "pg": ">5.0" } } diff --git a/test/bring-your-own-promise.js b/test/bring-your-own-promise.js new file mode 100644 index 00000000..f7fe3bde --- /dev/null +++ b/test/bring-your-own-promise.js @@ -0,0 +1,36 @@ +'use strict' +const co = require('co') +const expect = require('expect.js') + +const describe = require('mocha').describe +const it = require('mocha').it +const BluebirdPromise = require('bluebird') + +const Pool = require('../') + +const checkType = promise => { + expect(promise).to.be.a(BluebirdPromise) + return promise.catch(e => undefined) +} + +describe('Bring your own promise', function () { + it('uses supplied promise for operations', co.wrap(function * () { + const pool = new Pool({ Promise: BluebirdPromise }) + const client1 = yield checkType(pool.connect()) + client1.release() + yield checkType(pool.query('SELECT NOW()')) + const client2 = yield checkType(pool.connect()) + // TODO - make sure pg supports BYOP as well + client2.release() + yield checkType(pool.end()) + })) + + it('uses promises in errors', co.wrap(function * () { + const pool = new Pool({ Promise: BluebirdPromise, port: 48484 }) + yield checkType(pool.connect()) + yield checkType(pool.end()) + yield checkType(pool.connect()) + yield checkType(pool.query()) + yield checkType(pool.end()) + })) +}) diff --git a/test/connection-strings.js b/test/connection-strings.js index 8ee3d074..7013f28d 100644 --- a/test/connection-strings.js +++ b/test/connection-strings.js @@ -1,13 +1,13 @@ -var expect = require('expect.js') -var describe = require('mocha').describe -var it = require('mocha').it -var Pool = require('../') +const expect = require('expect.js') +const describe = require('mocha').describe +const it = require('mocha').it +const Pool = require('../') describe('Connection strings', function () { it('pool delegates connectionString property to client', function (done) { - var connectionString = 'postgres://foo:bar@baz:1234/xur' + const connectionString = 'postgres://foo:bar@baz:1234/xur' - var pool = new Pool({ + const pool = new Pool({ // use a fake client so we can check we're passed the connectionString Client: function (args) { expect(args.connectionString).to.equal(connectionString) diff --git a/test/connection-timeout.js b/test/connection-timeout.js new file mode 100644 index 00000000..0671b112 --- /dev/null +++ b/test/connection-timeout.js @@ -0,0 +1,62 @@ +'use strict' +const net = require('net') +const co = require('co') +const expect = require('expect.js') + +const describe = require('mocha').describe +const it = require('mocha').it +const before = require('mocha').before +const after = require('mocha').after + +const Pool = require('../') + +describe('connection timeout', () => { + before((done) => { + this.server = net.createServer((socket) => { + }) + + this.server.listen(() => { + this.port = this.server.address().port + done() + }) + }) + + after((done) => { + this.server.close(done) + }) + + it('should callback with an error if timeout is passed', (done) => { + const pool = new Pool({ connectionTimeoutMillis: 10, port: this.port }) + pool.connect((err, client, release) => { + expect(err).to.be.an(Error) + expect(err.message).to.contain('timeout') + expect(client).to.equal(undefined) + expect(pool.idleCount).to.equal(0) + done() + }) + }) + + it('should reject promise with an error if timeout is passed', (done) => { + const pool = new Pool({ connectionTimeoutMillis: 10, port: this.port }) + pool.connect().catch(err => { + expect(err).to.be.an(Error) + expect(err.message).to.contain('timeout') + expect(pool.idleCount).to.equal(0) + done() + }) + }) + + it('should handle multiple timeouts', co.wrap(function * () { + const errors = [] + const pool = new Pool({ connectionTimeoutMillis: 1, port: this.port }) + for (var i = 0; i < 15; i++) { + try { + yield pool.connect() + } catch (e) { + errors.push(e) + } + } + expect(errors).to.have.length(15) + }.bind(this))) +}) + diff --git a/test/ending.js b/test/ending.js new file mode 100644 index 00000000..1956b13f --- /dev/null +++ b/test/ending.js @@ -0,0 +1,34 @@ +'use strict' +const co = require('co') +const expect = require('expect.js') + +const describe = require('mocha').describe +const it = require('mocha').it + +const Pool = require('../') + +describe('pool ending', () => { + it('ends without being used', (done) => { + const pool = new Pool() + pool.end(done) + }) + + it('ends with a promise', () => { + return new Pool().end() + }) + + it('ends with clients', co.wrap(function * () { + const pool = new Pool() + const res = yield pool.query('SELECT $1::text as name', ['brianc']) + expect(res.rows[0].name).to.equal('brianc') + return pool.end() + })) + + it('allows client to finish', co.wrap(function * () { + const pool = new Pool() + const query = pool.query('SELECT $1::text as name', ['brianc']) + yield pool.end() + const res = yield query + expect(res.rows[0].name).to.equal('brianc') + })) +}) diff --git a/test/error-handling.js b/test/error-handling.js new file mode 100644 index 00000000..de68fad3 --- /dev/null +++ b/test/error-handling.js @@ -0,0 +1,135 @@ +'use strict' +const co = require('co') +const expect = require('expect.js') + +const describe = require('mocha').describe +const it = require('mocha').it + +const Pool = require('../') + +describe('pool error handling', function () { + it('Should complete these queries without dying', function (done) { + const pool = new Pool() + let errors = 0 + let shouldGet = 0 + function runErrorQuery () { + shouldGet++ + return new Promise(function (resolve, reject) { + pool.query("SELECT 'asd'+1 ").then(function (res) { + reject(res) // this should always error + }).catch(function (err) { + errors++ + resolve(err) + }) + }) + } + const ps = [] + for (let i = 0; i < 5; i++) { + ps.push(runErrorQuery()) + } + Promise.all(ps).then(function () { + expect(shouldGet).to.eql(errors) + pool.end(done) + }) + }) + + describe('calling release more than once', () => { + it('should throw each time', co.wrap(function * () { + const pool = new Pool() + const client = yield pool.connect() + client.release() + expect(() => client.release()).to.throwError() + expect(() => client.release()).to.throwError() + return yield pool.end() + })) + }) + + describe('calling connect after end', () => { + it('should return an error', function * () { + const pool = new Pool() + const res = yield pool.query('SELECT $1::text as name', ['hi']) + expect(res.rows[0].name).to.equal('hi') + const wait = pool.end() + pool.query('select now()') + yield wait + expect(() => pool.query('select now()')).to.reject() + }) + }) + + describe('using an ended pool', () => { + it('rejects all additional promises', (done) => { + const pool = new Pool() + const promises = [] + pool.end() + .then(() => { + const squash = promise => promise.catch(e => 'okay!') + promises.push(squash(pool.connect())) + promises.push(squash(pool.query('SELECT NOW()'))) + promises.push(squash(pool.end())) + Promise.all(promises).then(res => { + expect(res).to.eql(['okay!', 'okay!', 'okay!']) + done() + }) + }) + }) + + it('returns an error on all additional callbacks', (done) => { + const pool = new Pool() + pool.end(() => { + pool.query('SELECT *', (err) => { + expect(err).to.be.an(Error) + pool.connect((err) => { + expect(err).to.be.an(Error) + pool.end((err) => { + expect(err).to.be.an(Error) + done() + }) + }) + }) + }) + }) + }) + + describe('error from idle client', () => { + it('removes client from pool', co.wrap(function * () { + const pool = new Pool() + const client = yield pool.connect() + expect(pool.totalCount).to.equal(1) + expect(pool.waitingCount).to.equal(0) + expect(pool.idleCount).to.equal(0) + client.release() + yield new Promise((resolve, reject) => { + process.nextTick(() => { + pool.once('error', (err) => { + expect(err.message).to.equal('expected') + expect(pool.idleCount).to.equal(0) + expect(pool.totalCount).to.equal(0) + pool.end().then(resolve, reject) + }) + client.emit('error', new Error('expected')) + }) + }) + })) + }) + + describe('pool with lots of errors', () => { + it('continues to work and provide new clients', co.wrap(function * () { + const pool = new Pool({ max: 1 }) + const errors = [] + for (var i = 0; i < 20; i++) { + try { + yield pool.query('invalid sql') + } catch (err) { + errors.push(err) + } + } + expect(errors).to.have.length(20) + expect(pool.idleCount).to.equal(0) + expect(pool.query).to.be.a(Function) + const res = yield pool.query('SELECT $1::text as name', ['brianc']) + expect(res.rows).to.have.length(1) + expect(res.rows[0].name).to.equal('brianc') + return pool.end() + })) + }) +}) diff --git a/test/events.js b/test/events.js index 759dff02..3d4405e2 100644 --- a/test/events.js +++ b/test/events.js @@ -1,14 +1,15 @@ -var expect = require('expect.js') -var EventEmitter = require('events').EventEmitter -var describe = require('mocha').describe -var it = require('mocha').it -var objectAssign = require('object-assign') -var Pool = require('../') +'use strict' + +const expect = require('expect.js') +const EventEmitter = require('events').EventEmitter +const describe = require('mocha').describe +const it = require('mocha').it +const Pool = require('../') describe('events', function () { it('emits connect before callback', function (done) { - var pool = new Pool() - var emittedClient = false + const pool = new Pool() + let emittedClient = false pool.on('connect', function (client) { emittedClient = client }) @@ -23,48 +24,47 @@ describe('events', function () { }) it('emits "connect" only with a successful connection', function (done) { - var pool = new Pool({ + const pool = new Pool({ // This client will always fail to connect Client: mockClient({ connect: function (cb) { - process.nextTick(function () { cb(new Error('bad news')) }) + process.nextTick(() => { + cb(new Error('bad news')) + setImmediate(done) + }) } }) }) pool.on('connect', function () { throw new Error('should never get here') }) - pool._create(function (err) { - if (err) done() - else done(new Error('expected failure')) - }) + return pool.connect().catch(e => expect(e.message).to.equal('bad news')) }) it('emits acquire every time a client is acquired', function (done) { - var pool = new Pool() - var acquireCount = 0 + const pool = new Pool() + let acquireCount = 0 pool.on('acquire', function (client) { expect(client).to.be.ok() acquireCount++ }) - for (var i = 0; i < 10; i++) { + for (let i = 0; i < 10; i++) { pool.connect(function (err, client, release) { - err ? done(err) : release() - release() if (err) return done(err) + release() }) pool.query('SELECT now()') } setTimeout(function () { expect(acquireCount).to.be(20) pool.end(done) - }, 40) + }, 100) }) it('emits error and client if an idle client in the pool hits an error', function (done) { - var pool = new Pool() + const pool = new Pool() pool.connect(function (err, client) { - expect(err).to.equal(null) + expect(err).to.equal(undefined) client.release() setImmediate(function () { client.emit('error', new Error('problem')) @@ -80,8 +80,8 @@ describe('events', function () { function mockClient (methods) { return function () { - var client = new EventEmitter() - objectAssign(client, methods) + const client = new EventEmitter() + Object.assign(client, methods) return client } } diff --git a/test/idle-timeout.js b/test/idle-timeout.js new file mode 100644 index 00000000..68a2b78a --- /dev/null +++ b/test/idle-timeout.js @@ -0,0 +1,54 @@ +'use strict' +const co = require('co') +const expect = require('expect.js') + +const describe = require('mocha').describe +const it = require('mocha').it + +const Pool = require('../') + +const wait = time => new Promise((resolve) => setTimeout(resolve, time)) + +describe('idle timeout', () => { + it('should timeout and remove the client', (done) => { + const pool = new Pool({ idleTimeoutMillis: 10 }) + pool.query('SELECT NOW()') + pool.on('remove', () => { + expect(pool.idleCount).to.equal(0) + expect(pool.totalCount).to.equal(0) + done() + }) + }) + + it('can remove idle clients and recreate them', co.wrap(function * () { + const pool = new Pool({ idleTimeoutMillis: 1 }) + const results = [] + for (var i = 0; i < 20; i++) { + let query = pool.query('SELECT NOW()') + expect(pool.idleCount).to.equal(0) + expect(pool.totalCount).to.equal(1) + results.push(yield query) + yield wait(2) + expect(pool.idleCount).to.equal(0) + expect(pool.totalCount).to.equal(0) + } + expect(results).to.have.length(20) + })) + + it('does not time out clients which are used', co.wrap(function * () { + const pool = new Pool({ idleTimeoutMillis: 1 }) + const results = [] + for (var i = 0; i < 20; i++) { + let client = yield pool.connect() + expect(pool.totalCount).to.equal(1) + expect(pool.idleCount).to.equal(0) + yield wait(10) + results.push(yield client.query('SELECT NOW()')) + client.release() + expect(pool.idleCount).to.equal(1) + expect(pool.totalCount).to.equal(1) + } + expect(results).to.have.length(20) + return pool.end() + })) +}) diff --git a/test/index.js b/test/index.js index 3f2b99d9..010d99c5 100644 --- a/test/index.js +++ b/test/index.js @@ -1,26 +1,16 @@ -var expect = require('expect.js') -var _ = require('lodash') +'use strict' +const expect = require('expect.js') +const _ = require('lodash') -var describe = require('mocha').describe -var it = require('mocha').it -var Promise = require('bluebird') +const describe = require('mocha').describe +const it = require('mocha').it -var Pool = require('../') - -if (typeof global.Promise === 'undefined') { - global.Promise = Promise -} +const Pool = require('../') describe('pool', function () { - it('can be used as a factory function', function () { - var pool = Pool() - expect(pool instanceof Pool).to.be.ok() - expect(typeof pool.connect).to.be('function') - }) - describe('with callbacks', function () { it('works totally unconfigured', function (done) { - var pool = new Pool() + const pool = new Pool() pool.connect(function (err, client, release) { if (err) return done(err) client.query('SELECT NOW()', function (err, res) { @@ -33,7 +23,7 @@ describe('pool', function () { }) it('passes props to clients', function (done) { - var pool = new Pool({ binary: true }) + const pool = new Pool({ binary: true }) pool.connect(function (err, client, release) { release() if (err) return done(err) @@ -43,7 +33,7 @@ describe('pool', function () { }) it('can run a query with a callback without parameters', function (done) { - var pool = new Pool() + const pool = new Pool() pool.query('SELECT 1 as num', function (err, res) { expect(res.rows[0]).to.eql({ num: 1 }) pool.end(function () { @@ -53,7 +43,7 @@ describe('pool', function () { }) it('can run a query with a callback', function (done) { - var pool = new Pool() + const pool = new Pool() pool.query('SELECT $1::text as name', ['brianc'], function (err, res) { expect(res.rows[0]).to.eql({ name: 'brianc' }) pool.end(function () { @@ -63,18 +53,30 @@ describe('pool', function () { }) it('passes connection errors to callback', function (done) { - var pool = new Pool({host: 'no-postgres-server-here.com'}) + const pool = new Pool({ port: 53922 }) pool.query('SELECT $1::text as name', ['brianc'], function (err, res) { expect(res).to.be(undefined) expect(err).to.be.an(Error) + // a connection error should not polute the pool with a dead client + expect(pool.totalCount).to.equal(0) pool.end(function (err) { done(err) }) }) }) + it('does not pass client to error callback', function (done) { + const pool = new Pool({ port: 58242 }) + pool.connect(function (err, client, release) { + expect(err).to.be.an(Error) + expect(client).to.be(undefined) + expect(release).to.be.a(Function) + pool.end(done) + }) + }) + it('removes client if it errors in background', function (done) { - var pool = new Pool() + const pool = new Pool() pool.connect(function (err, client, release) { release() if (err) return done(err) @@ -94,8 +96,8 @@ describe('pool', function () { }) it('should not change given options', function (done) { - var options = { max: 10 } - var pool = new Pool(options) + const options = { max: 10 } + const pool = new Pool(options) pool.connect(function (err, client, release) { release() if (err) return done(err) @@ -105,8 +107,8 @@ describe('pool', function () { }) it('does not create promises when connecting', function (done) { - var pool = new Pool() - var returnValue = pool.connect(function (err, client, release) { + const pool = new Pool() + const returnValue = pool.connect(function (err, client, release) { release() if (err) return done(err) pool.end(done) @@ -115,8 +117,8 @@ describe('pool', function () { }) it('does not create promises when querying', function (done) { - var pool = new Pool() - var returnValue = pool.query('SELECT 1 as num', function (err) { + const pool = new Pool() + const returnValue = pool.query('SELECT 1 as num', function (err) { pool.end(function () { done(err) }) @@ -125,67 +127,99 @@ describe('pool', function () { }) it('does not create promises when ending', function (done) { - var pool = new Pool() - var returnValue = pool.end(done) + const pool = new Pool() + const returnValue = pool.end(done) expect(returnValue).to.be(undefined) }) + + it('never calls callback syncronously', function (done) { + const pool = new Pool() + pool.connect((err, client) => { + if (err) throw err + client.release() + setImmediate(() => { + let called = false + pool.connect((err, client) => { + if (err) throw err + called = true + client.release() + setImmediate(() => { + pool.end(done) + }) + }) + expect(called).to.equal(false) + }) + }) + }) }) describe('with promises', function () { - it('connects and disconnects', function () { - var pool = new Pool() + it('connects, queries, and disconnects', function () { + const pool = new Pool() return pool.connect().then(function (client) { - expect(pool.pool.availableObjectsCount()).to.be(0) return client.query('select $1::text as name', ['hi']).then(function (res) { expect(res.rows).to.eql([{ name: 'hi' }]) client.release() - expect(pool.pool.getPoolSize()).to.be(1) - expect(pool.pool.availableObjectsCount()).to.be(1) return pool.end() }) }) }) + it('executes a query directly', () => { + const pool = new Pool() + return pool + .query('SELECT $1::text as name', ['hi']) + .then(res => { + expect(res.rows).to.have.length(1) + expect(res.rows[0].name).to.equal('hi') + return pool.end() + }) + }) + it('properly pools clients', function () { - var pool = new Pool({ poolSize: 9 }) - return Promise.map(_.times(30), function () { + const pool = new Pool({ poolSize: 9 }) + const promises = _.times(30, function () { return pool.connect().then(function (client) { return client.query('select $1::text as name', ['hi']).then(function (res) { client.release() return res }) }) - }).then(function (res) { + }) + return Promise.all(promises).then(function (res) { expect(res).to.have.length(30) - expect(pool.pool.getPoolSize()).to.be(9) + expect(pool.totalCount).to.be(9) return pool.end() }) }) it('supports just running queries', function () { - var pool = new Pool({ poolSize: 9 }) - return Promise.map(_.times(30), function () { - return pool.query('SELECT $1::text as name', ['hi']) - }).then(function (queries) { + const pool = new Pool({ poolSize: 9 }) + const text = 'select $1::text as name' + const values = ['hi'] + const query = { text: text, values: values } + const promises = _.times(30, () => pool.query(query)) + return Promise.all(promises).then(function (queries) { expect(queries).to.have.length(30) - expect(pool.pool.getPoolSize()).to.be(9) - expect(pool.pool.availableObjectsCount()).to.be(9) return pool.end() }) }) - it('recovers from all errors', function () { - var pool = new Pool() + it('recovers from query errors', function () { + const pool = new Pool() - var errors = [] - return Promise.mapSeries(_.times(30), function () { + const errors = [] + const promises = _.times(30, () => { return pool.query('SELECT asldkfjasldkf') .catch(function (e) { errors.push(e) }) - }).then(function () { + }) + return Promise.all(promises).then(() => { + expect(errors).to.have.length(30) + expect(pool.totalCount).to.equal(0) + expect(pool.idleCount).to.equal(0) return pool.query('SELECT $1::text as name', ['hi']).then(function (res) { - expect(errors).to.have.length(30) expect(res.rows).to.eql([{ name: 'hi' }]) return pool.end() }) @@ -193,40 +227,3 @@ describe('pool', function () { }) }) }) - -describe('pool error handling', function () { - it('Should complete these queries without dying', function (done) { - var pgPool = new Pool() - var pool = pgPool.pool - pool._factory.max = 1 - pool._factory.min = null - var errors = 0 - var shouldGet = 0 - function runErrorQuery () { - shouldGet++ - return new Promise(function (resolve, reject) { - pgPool.query("SELECT 'asd'+1 ").then(function (res) { - reject(res) // this should always error - }).catch(function (err) { - errors++ - resolve(err) - }) - }) - } - var ps = [] - for (var i = 0; i < 5; i++) { - ps.push(runErrorQuery()) - } - Promise.all(ps).then(function () { - expect(shouldGet).to.eql(errors) - done() - }) - }) -}) - -process.on('unhandledRejection', function (e) { - console.error(e.message, e.stack) - setImmediate(function () { - throw e - }) -}) diff --git a/test/logging.js b/test/logging.js index 47389a5a..839603b7 100644 --- a/test/logging.js +++ b/test/logging.js @@ -1,17 +1,17 @@ -var expect = require('expect.js') +const expect = require('expect.js') -var describe = require('mocha').describe -var it = require('mocha').it +const describe = require('mocha').describe +const it = require('mocha').it -var Pool = require('../') +const Pool = require('../') describe('logging', function () { it('logs to supplied log function if given', function () { - var messages = [] - var log = function (msg) { + const messages = [] + const log = function (msg) { messages.push(msg) } - var pool = new Pool({ log: log }) + const pool = new Pool({ log: log }) return pool.query('SELECT NOW()').then(function () { expect(messages.length).to.be.greaterThan(0) return pool.end() diff --git a/test/mocha.opts b/test/mocha.opts index 46e8e69d..590fb862 100644 --- a/test/mocha.opts +++ b/test/mocha.opts @@ -1,2 +1,4 @@ +--require test/setup.js --no-exit --bail +--timeout 10000 diff --git a/test/setup.js b/test/setup.js new file mode 100644 index 00000000..cf75b7a6 --- /dev/null +++ b/test/setup.js @@ -0,0 +1,10 @@ +const crash = reason => { + process.on(reason, err => { + console.error(reason, err.stack) + process.exit(-1) + }) +} + +crash('unhandledRejection') +crash('uncaughtError') +crash('warning') diff --git a/test/sizing.js b/test/sizing.js new file mode 100644 index 00000000..9e926877 --- /dev/null +++ b/test/sizing.js @@ -0,0 +1,44 @@ +const expect = require('expect.js') +const co = require('co') +const _ = require('lodash') + +const describe = require('mocha').describe +const it = require('mocha').it + +const Pool = require('../') + +describe('pool size of 1', () => { + it('can create a single client and use it once', co.wrap(function * () { + const pool = new Pool({ max: 1 }) + expect(pool.waitingCount).to.equal(0) + const client = yield pool.connect() + const res = yield client.query('SELECT $1::text as name', ['hi']) + expect(res.rows[0].name).to.equal('hi') + client.release() + pool.end() + })) + + it('can create a single client and use it multiple times', co.wrap(function * () { + const pool = new Pool({ max: 1 }) + expect(pool.waitingCount).to.equal(0) + const client = yield pool.connect() + const wait = pool.connect() + expect(pool.waitingCount).to.equal(1) + client.release() + const client2 = yield wait + expect(client).to.equal(client2) + client2.release() + return yield pool.end() + })) + + it('can only send 1 query at a time', co.wrap(function * () { + const pool = new Pool({ max: 1 }) + const queries = _.times(20, (i) => { + return pool.query('SELECT COUNT(*) as counts FROM pg_stat_activity') + }) + const results = yield Promise.all(queries) + const counts = results.map(res => parseInt(res.rows[0].counts), 10) + expect(counts).to.eql(_.times(20, i => 1)) + return yield pool.end() + })) +}) diff --git a/test/timeout.js b/test/timeout.js new file mode 100644 index 00000000..e69de29b diff --git a/test/verify.js b/test/verify.js new file mode 100644 index 00000000..667dea9f --- /dev/null +++ b/test/verify.js @@ -0,0 +1,25 @@ +'use strict' +const expect = require('expect.js') + +const describe = require('mocha').describe +const it = require('mocha').it + +const Pool = require('../') + +describe('verify', () => { + it('verifies a client with a callback', false, (done) => { + const pool = new Pool({ + verify: (client, cb) => { + client.release() + cb(new Error('nope')) + } + }) + + pool.connect((err, client) => { + expect(err).to.be.an(Error) + expect(err.message).to.be('nope') + pool.end() + done() + }) + }) +}) From 139cbdea16e5b54c2e8b65a971710965d143ea3e Mon Sep 17 00:00:00 2001 From: Brian Carlson Date: Thu, 13 Jul 2017 22:37:45 -0500 Subject: [PATCH 66/95] Bump version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 392375bb..be1eb142 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "pg-pool", - "version": "1.8.0", + "version": "2.0.0", "description": "Connection pool for node-postgres", "main": "index.js", "directories": { From 2421a769cb03b13430706d884a13cd5dc86b98fd Mon Sep 17 00:00:00 2001 From: Brian C Date: Thu, 13 Jul 2017 22:40:26 -0500 Subject: [PATCH 67/95] Update README.md --- README.md | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index b86447b4..a02fa32c 100644 --- a/README.md +++ b/README.md @@ -17,23 +17,24 @@ to use pg-pool you must first create an instance of a pool ```js var Pool = require('pg-pool') -//by default the pool uses the same -//configuration as whatever `pg` version you have installed +// by default the pool uses the same +// configuration as whatever `pg` version you have installed var pool = new Pool() -//you can pass properties to the pool -//these properties are passed unchanged to both the node-postgres Client constructor -//and the node-pool (https://github.com/coopernurse/node-pool) constructor -//allowing you to fully configure the behavior of both +// you can pass properties to the pool +// these properties are passed unchanged to both the node-postgres Client constructor +// and the node-pool (https://github.com/coopernurse/node-pool) constructor +// allowing you to fully configure the behavior of both var pool2 = new Pool({ database: 'postgres', user: 'brianc', password: 'secret!', port: 5432, ssl: true, - max: 20, //set pool max size to 20 - min: 4, //set min pool size to 4 - idleTimeoutMillis: 1000 //close idle clients after 1 second + max: 20, // set pool max size to 20 + min: 4, // set min pool size to 4 + idleTimeoutMillis: 1000, // close idle clients after 1 second + connectionTimeoutMillis: 1000, // return an error after 1 second if connection could not be established }) //you can supply a custom client constructor From 40f5126b6ef16d80c03a3001b0802824dbbe2af5 Mon Sep 17 00:00:00 2001 From: Brian C Date: Fri, 14 Jul 2017 14:07:07 -0500 Subject: [PATCH 68/95] Fix idle client teardown on error (#68) - Re-add default idleTimeoutMillis = 10000 by default - Fix idle timeout clearing on shutdown - Ensure idleTimeoutMillis is used in timeout --- index.js | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/index.js b/index.js index c317e531..49d797be 100644 --- a/index.js +++ b/index.js @@ -16,7 +16,7 @@ function throwOnRelease () { function release (client, err) { client.release = throwOnRelease - if (err) { + if (err || this.ending) { this._remove(client) this._pulseQueue() return @@ -28,14 +28,10 @@ function release (client, err) { tid = setTimeout(() => { this.log('remove idle client') this._remove(client) - }, this.idleTimeoutMillis) + }, this.options.idleTimeoutMillis) } - if (this.ending) { - this._remove(client) - } else { - this._idle.push(new IdleItem(client, tid)) - } + this._idle.push(new IdleItem(client, tid)) this._pulseQueue() } @@ -64,6 +60,10 @@ class Pool extends EventEmitter { this.Client = this.options.Client || Client || require('pg').Client this.Promise = this.options.Promise || global.Promise + if (typeof this.options.idleTimeoutMillis === 'undefined') { + this.options.idleTimeoutMillis = 10000 + } + this._clients = [] this._idle = [] this._pendingQueue = [] @@ -114,7 +114,10 @@ class Pool extends EventEmitter { } _remove (client) { - this._idle = this._idle.filter(item => item.client !== client) + this._idle = this._idle.filter(item => { + clearTimeout(item.timeoutId) + return item.client !== client + }) this._clients = this._clients.filter(c => c !== client) client.end() this.emit('remove', client) From a446537377d7157be60f59f4684b69632051d501 Mon Sep 17 00:00:00 2001 From: Brian Carlson Date: Fri, 14 Jul 2017 14:07:19 -0500 Subject: [PATCH 69/95] Bump version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index be1eb142..a0ebd92b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "pg-pool", - "version": "2.0.0", + "version": "2.0.1", "description": "Connection pool for node-postgres", "main": "index.js", "directories": { From 53584b704ac63466876ecec4a4bb9dead3835d6e Mon Sep 17 00:00:00 2001 From: Brian C Date: Thu, 10 Aug 2017 00:20:56 -0500 Subject: [PATCH 70/95] Add connection & query timeout if all clients are checked out (#70) * Add connection & query timeout if all clients are checked out This addresses [pg#1390](https://github.com/brianc/node-postgres/issues/1390). Ensure connection timeout applies both for new connections and on an exhuasted pool. I also made the library return an error when passing a function as the first param to `pool.query` - previosuly this threw a sync type error. * Add pg-cursor to dev deps --- index.js | 32 +++++++++++++++++++++++++- package.json | 3 ++- test/connection-timeout.js | 47 +++++++++++++++++++++++++++++++++++++- test/error-handling.js | 11 +++++++++ test/submittable.js | 19 +++++++++++++++ 5 files changed, 109 insertions(+), 3 deletions(-) create mode 100644 test/submittable.js diff --git a/index.js b/index.js index 49d797be..46c399f9 100644 --- a/index.js +++ b/index.js @@ -128,14 +128,34 @@ class Pool extends EventEmitter { const err = new Error('Cannot use a pool after calling end on the pool') return cb ? cb(err) : this.Promise.reject(err) } + + // if we don't have to connect a new client, don't do so if (this._clients.length >= this.options.max || this._idle.length) { const response = promisify(this.Promise, cb) const result = response.result - this._pendingQueue.push(response.callback) + // if we have idle clients schedule a pulse immediately if (this._idle.length) { process.nextTick(() => this._pulseQueue()) } + + if (!this.options.connectionTimeoutMillis) { + this._pendingQueue.push(response.callback) + return result + } + + // set connection timeout on checking out an existing client + const tid = setTimeout(() => { + // remove the callback from pending waiters because + // we're going to call it with a timeout error + this._pendingQueue = this._pendingQueue.filter(cb => cb === response.callback) + response.callback(new Error('timeout exceeded when trying to connect')) + }, this.options.connectionTimeoutMillis) + + this._pendingQueue.push(function (err, res, done) { + clearTimeout(tid) + response.callback(err, res, done) + }) return result } @@ -199,6 +219,16 @@ class Pool extends EventEmitter { } query (text, values, cb) { + // guard clause against passing a function as the first parameter + if (typeof text === 'function') { + const response = promisify(this.Promise, text) + setImmediate(function () { + return response.callback(new Error('Passing a function as the first parameter to pool.query is not supported')) + }) + return response.result + } + + // allow plain text query without values if (typeof values === 'function') { cb = values values = undefined diff --git a/package.json b/package.json index a0ebd92b..192a64ed 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,7 @@ "test": "test" }, "scripts": { - "test": "node_modules/.bin/standard && node_modules/.bin/mocha" + "test": " node_modules/.bin/mocha && node_modules/.bin/standard" }, "repository": { "type": "git", @@ -32,6 +32,7 @@ "lodash": "4.13.1", "mocha": "^2.3.3", "pg": "*", + "pg-cursor": "^1.3.0", "standard": "7.1.2", "standard-format": "2.2.1" }, diff --git a/test/connection-timeout.js b/test/connection-timeout.js index 0671b112..8f3239b3 100644 --- a/test/connection-timeout.js +++ b/test/connection-timeout.js @@ -58,5 +58,50 @@ describe('connection timeout', () => { } expect(errors).to.have.length(15) }.bind(this))) -}) + it('should timeout on checkout of used connection', (done) => { + const pool = new Pool({ connectionTimeoutMillis: 100, max: 1 }) + pool.connect((err, client, release) => { + expect(err).to.be(undefined) + expect(client).to.not.be(undefined) + pool.connect((err, client) => { + expect(err).to.be.an(Error) + expect(client).to.be(undefined) + release() + pool.end(done) + }) + }) + }) + + it('should timeout on query if all clients are busy', (done) => { + const pool = new Pool({ connectionTimeoutMillis: 100, max: 1 }) + pool.connect((err, client, release) => { + expect(err).to.be(undefined) + expect(client).to.not.be(undefined) + pool.query('select now()', (err, result) => { + expect(err).to.be.an(Error) + expect(result).to.be(undefined) + release() + pool.end(done) + }) + }) + }) + + it('should recover from timeout errors', (done) => { + const pool = new Pool({ connectionTimeoutMillis: 100, max: 1 }) + pool.connect((err, client, release) => { + expect(err).to.be(undefined) + expect(client).to.not.be(undefined) + pool.query('select now()', (err, result) => { + expect(err).to.be.an(Error) + expect(result).to.be(undefined) + release() + pool.query('select $1::text as name', ['brianc'], (err, res) => { + expect(err).to.be(undefined) + expect(res.rows).to.have.length(1) + pool.end(done) + }) + }) + }) + }) +}) diff --git a/test/error-handling.js b/test/error-handling.js index de68fad3..9435dd7f 100644 --- a/test/error-handling.js +++ b/test/error-handling.js @@ -112,6 +112,17 @@ describe('pool error handling', function () { })) }) + describe('passing a function to pool.query', () => { + it('calls back with error', (done) => { + const pool = new Pool() + console.log('passing fn to query') + pool.query((err) => { + expect(err).to.be.an(Error) + pool.end(done) + }) + }) + }) + describe('pool with lots of errors', () => { it('continues to work and provide new clients', co.wrap(function * () { const pool = new Pool({ max: 1 }) diff --git a/test/submittable.js b/test/submittable.js new file mode 100644 index 00000000..7a1574d4 --- /dev/null +++ b/test/submittable.js @@ -0,0 +1,19 @@ +'use strict' +const Cursor = require('pg-cursor') +const expect = require('expect.js') +const describe = require('mocha').describe +const it = require('mocha').it + +const Pool = require('../') + +describe('submittle', () => { + it('is returned from the query method', false, (done) => { + const pool = new Pool() + const cursor = pool.query(new Cursor('SELECT * from generate_series(0, 1000)')) + cursor.read((err, rows) => { + expect(err).to.be(undefined) + expect(!!rows).to.be.ok() + cursor.close(done) + }) + }) +}) From c3417e95ebf669bd8617a1382f2eb5f09e0239c4 Mon Sep 17 00:00:00 2001 From: Brian Carlson Date: Thu, 10 Aug 2017 00:21:10 -0500 Subject: [PATCH 71/95] Bump version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 192a64ed..9711973e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "pg-pool", - "version": "2.0.1", + "version": "2.0.2", "description": "Connection pool for node-postgres", "main": "index.js", "directories": { From 4e3522634034ed954b8521b528088e4610101421 Mon Sep 17 00:00:00 2001 From: Charmander <~@charmander.me> Date: Thu, 10 Aug 2017 14:00:49 +0000 Subject: [PATCH 72/95] Fix client remove clearing unrelated idle timers (#71) * Add failing test for idle timer continuation after removal * Clear idle timeout only for removed client * Copy list of idle clients for modification during iteration --- index.js | 23 ++++++++++++++++++----- test/idle-timeout.js | 25 +++++++++++++++++++++++++ 2 files changed, 43 insertions(+), 5 deletions(-) diff --git a/index.js b/index.js index 46c399f9..af86134b 100644 --- a/index.js +++ b/index.js @@ -3,6 +3,14 @@ const EventEmitter = require('events').EventEmitter const NOOP = function () { } +const removeWhere = (list, predicate) => { + const i = list.findIndex(predicate) + + return i === -1 + ? undefined + : list.splice(i, 1)[0] +} + class IdleItem { constructor (client, timeoutId) { this.client = client @@ -80,7 +88,7 @@ class Pool extends EventEmitter { if (this.ending) { this.log('pulse queue on ending') if (this._idle.length) { - this._idle.map(item => { + this._idle.slice().map(item => { this._remove(item.client) }) } @@ -114,10 +122,15 @@ class Pool extends EventEmitter { } _remove (client) { - this._idle = this._idle.filter(item => { - clearTimeout(item.timeoutId) - return item.client !== client - }) + const removed = removeWhere( + this._idle, + item => item.client === client + ) + + if (removed !== undefined) { + clearTimeout(removed.timeoutId) + } + this._clients = this._clients.filter(c => c !== client) client.end() this.emit('remove', client) diff --git a/test/idle-timeout.js b/test/idle-timeout.js index 68a2b78a..a24ab7b0 100644 --- a/test/idle-timeout.js +++ b/test/idle-timeout.js @@ -20,6 +20,31 @@ describe('idle timeout', () => { }) }) + it('times out and removes clients when others are also removed', co.wrap(function * () { + const pool = new Pool({ idleTimeoutMillis: 10 }) + const clientA = yield pool.connect() + const clientB = yield pool.connect() + clientA.release() + clientB.release(new Error()) + + const removal = new Promise((resolve) => { + pool.on('remove', () => { + expect(pool.idleCount).to.equal(0) + expect(pool.totalCount).to.equal(0) + resolve() + }) + }) + + const timeout = wait(100).then(() => + Promise.reject(new Error('Idle timeout failed to occur'))) + + try { + yield Promise.race([removal, timeout]) + } finally { + pool.end() + } + })) + it('can remove idle clients and recreate them', co.wrap(function * () { const pool = new Pool({ idleTimeoutMillis: 1 }) const results = [] From 4d7734a71122cbbe1bb81bb466430e96f3405394 Mon Sep 17 00:00:00 2001 From: Brian Carlson Date: Thu, 10 Aug 2017 09:01:31 -0500 Subject: [PATCH 73/95] Bump version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 9711973e..17740db2 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "pg-pool", - "version": "2.0.2", + "version": "2.0.3", "description": "Connection pool for node-postgres", "main": "index.js", "directories": { From 2f14cb1a0f0cc6a6d35466edd939a24ff9a0e22d Mon Sep 17 00:00:00 2001 From: Charmander <~@charmander.me> Date: Fri, 6 Apr 2018 16:28:03 +0000 Subject: [PATCH 74/95] Update CI versions (#88) * Update CI versions PostgreSQL 9.1 is no longer available on 14.04. * Add Node 9 to CI --- .travis.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.travis.yml b/.travis.yml index e1a0ce70..cd2db492 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,6 +5,13 @@ matrix: - node_js: "4" addons: postgresql: "9.1" + dist: precise - node_js: "6" addons: postgresql: "9.4" + - node_js: "8" + addons: + postgresql: "9.6" + - node_js: "9" + addons: + postgresql: "9.6" From fabf39c6063d01fe76c61b2d80d221a7c9410604 Mon Sep 17 00:00:00 2001 From: Charmander <~@charmander.me> Date: Fri, 4 May 2018 17:59:39 +0000 Subject: [PATCH 75/95] Count only test query itself (#87) * Count only test query itself This breaks more obviously in PostgreSQL 10 (https://wiki.postgresql.org/wiki/New_in_postgres_10#Significant_Expansion_of_Wait_Events_in_pg_stat_activity). * Fix query counting for PostgreSQL 9.1 --- test/sizing.js | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/test/sizing.js b/test/sizing.js index 9e926877..b310b3d3 100644 --- a/test/sizing.js +++ b/test/sizing.js @@ -33,11 +33,17 @@ describe('pool size of 1', () => { it('can only send 1 query at a time', co.wrap(function * () { const pool = new Pool({ max: 1 }) - const queries = _.times(20, (i) => { - return pool.query('SELECT COUNT(*) as counts FROM pg_stat_activity') - }) + + // the query text column name changed in PostgreSQL 9.2 + const versionResult = yield pool.query('SHOW server_version_num') + const version = parseInt(versionResult.rows[0].server_version_num, 10) + const queryColumn = version < 90200 ? 'current_query' : 'query' + + const queryText = 'SELECT COUNT(*) as counts FROM pg_stat_activity WHERE ' + queryColumn + ' = $1' + const queries = _.times(20, () => + pool.query(queryText, [queryText])) const results = yield Promise.all(queries) - const counts = results.map(res => parseInt(res.rows[0].counts), 10) + const counts = results.map(res => parseInt(res.rows[0].counts, 10)) expect(counts).to.eql(_.times(20, i => 1)) return yield pool.end() })) From 1871d0f9e18330afe8149ff5df8b3e54ac71469a Mon Sep 17 00:00:00 2001 From: Charmander <~@charmander.me> Date: Fri, 4 May 2018 18:04:42 +0000 Subject: [PATCH 76/95] Remove timed-out checkouts from queue correctly (#86) * Add failing test for correct removal from checkout queue on timeout * Remove timed-out checkouts from queue correctly Fixes #85. --- index.js | 20 +++++++++++++++----- test/connection-timeout.js | 22 ++++++++++++++++++++++ 2 files changed, 37 insertions(+), 5 deletions(-) diff --git a/index.js b/index.js index af86134b..85f36c96 100644 --- a/index.js +++ b/index.js @@ -3,6 +3,14 @@ const EventEmitter = require('events').EventEmitter const NOOP = function () { } +const remove = (list, value) => { + const i = list.indexOf(value) + + if (i !== -1) { + list.splice(i, 1) + } +} + const removeWhere = (list, predicate) => { const i = list.findIndex(predicate) @@ -157,18 +165,20 @@ class Pool extends EventEmitter { return result } + const queueCallback = (err, res, done) => { + clearTimeout(tid) + response.callback(err, res, done) + } + // set connection timeout on checking out an existing client const tid = setTimeout(() => { // remove the callback from pending waiters because // we're going to call it with a timeout error - this._pendingQueue = this._pendingQueue.filter(cb => cb === response.callback) + remove(this._pendingQueue, queueCallback) response.callback(new Error('timeout exceeded when trying to connect')) }, this.options.connectionTimeoutMillis) - this._pendingQueue.push(function (err, res, done) { - clearTimeout(tid) - response.callback(err, res, done) - }) + this._pendingQueue.push(queueCallback) return result } diff --git a/test/connection-timeout.js b/test/connection-timeout.js index 8f3239b3..f7a2fd8e 100644 --- a/test/connection-timeout.js +++ b/test/connection-timeout.js @@ -73,6 +73,28 @@ describe('connection timeout', () => { }) }) + it('should not break further pending checkouts on a timeout', (done) => { + const pool = new Pool({ connectionTimeoutMillis: 200, max: 1 }) + pool.connect((err, client, releaseOuter) => { + expect(err).to.be(undefined) + + pool.connect((err, client) => { + expect(err).to.be.an(Error) + expect(client).to.be(undefined) + releaseOuter() + }) + + setTimeout(() => { + pool.connect((err, client, releaseInner) => { + expect(err).to.be(undefined) + expect(client).to.not.be(undefined) + releaseInner() + pool.end(done) + }) + }, 100) + }) + }) + it('should timeout on query if all clients are busy', (done) => { const pool = new Pool({ connectionTimeoutMillis: 100, max: 1 }) pool.connect((err, client, release) => { From 6b2883d2904debeb5ece69865647fa913278f8b2 Mon Sep 17 00:00:00 2001 From: Yuval Greenfield Date: Fri, 4 May 2018 11:06:32 -0700 Subject: [PATCH 77/95] terminiated -> terminated (#78) --- index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.js b/index.js index 85f36c96..02c66f2d 100644 --- a/index.js +++ b/index.js @@ -224,7 +224,7 @@ class Pool extends EventEmitter { // remove the dead client from our list of clients this._clients = this._clients.filter(c => c !== client) if (timeoutHit) { - err.message = 'Connection terminiated due to connection timeout' + err.message = 'Connection terminated due to connection timeout' } cb(err, undefined, NOOP) } else { From 277dc508daea03a8f6c0bcc3c534cab5b2501b12 Mon Sep 17 00:00:00 2001 From: Yuval Greenfield Date: Fri, 4 May 2018 11:06:49 -0700 Subject: [PATCH 78/95] Clarifying pool connect logging (#73) Existing log code was outputting 'connecting new client' twice and saying 'new client connected', creating a false impression when an error (like a timeout) was present. --- index.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/index.js b/index.js index 02c66f2d..ab74209f 100644 --- a/index.js +++ b/index.js @@ -196,7 +196,7 @@ class Pool extends EventEmitter { this.emit('error', err, client) } - this.log('connecting new client') + this.log('checking client timeout') // connection timeout logic let tid @@ -215,12 +215,12 @@ class Pool extends EventEmitter { this.log('connecting new client') client.connect((err) => { - this.log('new client connected') if (tid) { clearTimeout(tid) } client.on('error', idleListener) if (err) { + this.log('client failed to connect', err) // remove the dead client from our list of clients this._clients = this._clients.filter(c => c !== client) if (timeoutHit) { @@ -228,6 +228,7 @@ class Pool extends EventEmitter { } cb(err, undefined, NOOP) } else { + this.log('new client connected') client.release = release.bind(this, client) this.emit('connect', client) this.emit('acquire', client) From 4b9669eaa764126135f0bbf64af9be1e9d5092a9 Mon Sep 17 00:00:00 2001 From: "Brian M. Carlson" Date: Mon, 12 Nov 2018 12:32:19 -0600 Subject: [PATCH 79/95] Bump version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 17740db2..b15b4a53 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "pg-pool", - "version": "2.0.3", + "version": "2.0.4", "description": "Connection pool for node-postgres", "main": "index.js", "directories": { From 7ef3f4aa4a6404abae289c3bcab5a00a50a03199 Mon Sep 17 00:00:00 2001 From: Charmander <~@charmander.me> Date: Tue, 11 Dec 2018 10:53:00 -0800 Subject: [PATCH 80/95] Fix queued checkout after a connection failure (#111) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Test queued checkout after a connection failure Co-authored-by: Johannes Würbach * Fix queued checkout after a connection failure Co-authored-by: Johannes Würbach --- index.js | 4 ++++ test/error-handling.js | 22 ++++++++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/index.js b/index.js index ab74209f..2ccbbe7f 100644 --- a/index.js +++ b/index.js @@ -226,6 +226,10 @@ class Pool extends EventEmitter { if (timeoutHit) { err.message = 'Connection terminated due to connection timeout' } + + // this client won’t be released, so move on immediately + this._pulseQueue() + cb(err, undefined, NOOP) } else { this.log('new client connected') diff --git a/test/error-handling.js b/test/error-handling.js index 9435dd7f..1c84889c 100644 --- a/test/error-handling.js +++ b/test/error-handling.js @@ -1,4 +1,5 @@ 'use strict' +const net = require('net') const co = require('co') const expect = require('expect.js') @@ -143,4 +144,25 @@ describe('pool error handling', function () { return pool.end() })) }) + + it('should continue with queued items after a connection failure', (done) => { + const closeServer = net.createServer((socket) => { + socket.destroy() + }).unref() + + closeServer.listen(() => { + const pool = new Pool({ max: 1, port: closeServer.address().port }) + pool.connect((err) => { + expect(err).to.be.an(Error) + expect(err.message).to.be('Connection terminated unexpectedly') + }) + pool.connect((err) => { + expect(err).to.be.an(Error) + expect(err.message).to.be('Connection terminated unexpectedly') + closeServer.close(() => { + pool.end(done) + }) + }) + }) + }) }) From 140f9a1242e94c09fc780d8e4bcc82b91a787d39 Mon Sep 17 00:00:00 2001 From: "Brian M. Carlson" Date: Tue, 11 Dec 2018 12:53:22 -0600 Subject: [PATCH 81/95] Bump version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index b15b4a53..c9334457 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "pg-pool", - "version": "2.0.4", + "version": "2.0.5", "description": "Connection pool for node-postgres", "main": "index.js", "directories": { From 35a285c9a7a5815b0bac9e6a2274e3a47976c11e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20W=C3=BCrbach?= Date: Fri, 14 Dec 2018 21:15:35 +0100 Subject: [PATCH 82/95] Fix two timeout races (#109) --- index.js | 67 +++++++++++++++++---------- test/connection-timeout.js | 92 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 135 insertions(+), 24 deletions(-) diff --git a/index.js b/index.js index 2ccbbe7f..fe921cbb 100644 --- a/index.js +++ b/index.js @@ -3,14 +3,6 @@ const EventEmitter = require('events').EventEmitter const NOOP = function () { } -const remove = (list, value) => { - const i = list.indexOf(value) - - if (i !== -1) { - list.splice(i, 1) - } -} - const removeWhere = (list, predicate) => { const i = list.findIndex(predicate) @@ -26,6 +18,12 @@ class IdleItem { } } +class PendingItem { + constructor (callback) { + this.callback = callback + } +} + function throwOnRelease () { throw new Error('Release called on client which has already been released to the pool.') } @@ -85,6 +83,7 @@ class Pool extends EventEmitter { this._pendingQueue = [] this._endCallback = undefined this.ending = false + this.ended = false } _isFull () { @@ -93,6 +92,10 @@ class Pool extends EventEmitter { _pulseQueue () { this.log('pulse queue') + if (this.ended) { + this.log('pulse queue ended') + return + } if (this.ending) { this.log('pulse queue on ending') if (this._idle.length) { @@ -101,6 +104,7 @@ class Pool extends EventEmitter { }) } if (!this._clients.length) { + this.ended = true this._endCallback() } return @@ -121,10 +125,10 @@ class Pool extends EventEmitter { const client = idleItem.client client.release = release.bind(this, client) this.emit('acquire', client) - return waiter(undefined, client, client.release) + return waiter.callback(undefined, client, client.release) } if (!this._isFull()) { - return this.connect(waiter) + return this.newClient(waiter) } throw new Error('unexpected condition') } @@ -150,18 +154,18 @@ class Pool extends EventEmitter { return cb ? cb(err) : this.Promise.reject(err) } + const response = promisify(this.Promise, cb) + const result = response.result + // if we don't have to connect a new client, don't do so if (this._clients.length >= this.options.max || this._idle.length) { - const response = promisify(this.Promise, cb) - const result = response.result - // if we have idle clients schedule a pulse immediately if (this._idle.length) { process.nextTick(() => this._pulseQueue()) } if (!this.options.connectionTimeoutMillis) { - this._pendingQueue.push(response.callback) + this._pendingQueue.push(new PendingItem(response.callback)) return result } @@ -170,18 +174,27 @@ class Pool extends EventEmitter { response.callback(err, res, done) } + const pendingItem = new PendingItem(queueCallback) + // set connection timeout on checking out an existing client const tid = setTimeout(() => { // remove the callback from pending waiters because // we're going to call it with a timeout error - remove(this._pendingQueue, queueCallback) + removeWhere(this._pendingQueue, (i) => i.callback === queueCallback) + pendingItem.timedOut = true response.callback(new Error('timeout exceeded when trying to connect')) }, this.options.connectionTimeoutMillis) - this._pendingQueue.push(queueCallback) + this._pendingQueue.push(pendingItem) return result } + this.newClient(new PendingItem(response.callback)) + + return result + } + + newClient (pendingItem) { const client = new this.Client(this.options) this._clients.push(client) const idleListener = (err) => { @@ -210,9 +223,6 @@ class Pool extends EventEmitter { }, this.options.connectionTimeoutMillis) } - const response = promisify(this.Promise, cb) - cb = response.callback - this.log('connecting new client') client.connect((err) => { if (tid) { @@ -230,20 +240,29 @@ class Pool extends EventEmitter { // this client won’t be released, so move on immediately this._pulseQueue() - cb(err, undefined, NOOP) + if (!pendingItem.timedOut) { + pendingItem.callback(err, undefined, NOOP) + } } else { this.log('new client connected') client.release = release.bind(this, client) this.emit('connect', client) this.emit('acquire', client) - if (this.options.verify) { - this.options.verify(client, cb) + if (!pendingItem.timedOut) { + if (this.options.verify) { + this.options.verify(client, pendingItem.callback) + } else { + pendingItem.callback(undefined, client, client.release) + } } else { - cb(undefined, client, client.release) + if (this.options.verify) { + this.options.verify(client, client.release) + } else { + client.release() + } } } }) - return response.result } query (text, values, cb) { diff --git a/test/connection-timeout.js b/test/connection-timeout.js index f7a2fd8e..2c4d68f7 100644 --- a/test/connection-timeout.js +++ b/test/connection-timeout.js @@ -11,6 +11,8 @@ const after = require('mocha').after const Pool = require('../') describe('connection timeout', () => { + const connectionFailure = new Error('Temporary connection failure') + before((done) => { this.server = net.createServer((socket) => { }) @@ -126,4 +128,94 @@ describe('connection timeout', () => { }) }) }) + + it('continues processing after a connection failure', (done) => { + const Client = require('pg').Client + const orgConnect = Client.prototype.connect + let called = false + + Client.prototype.connect = function (cb) { + // Simulate a failure on first call + if (!called) { + called = true + + return setTimeout(() => { + cb(connectionFailure) + }, 100) + } + // And pass-through the second call + orgConnect.call(this, cb) + } + + const pool = new Pool({ + Client: Client, + connectionTimeoutMillis: 1000, + max: 1 + }) + + pool.connect((err, client, release) => { + expect(err).to.be(connectionFailure) + + pool.query('select $1::text as name', ['brianc'], (err, res) => { + expect(err).to.be(undefined) + expect(res.rows).to.have.length(1) + pool.end(done) + }) + }) + }) + + it('releases newly connected clients if the queued already timed out', (done) => { + const Client = require('pg').Client + + const orgConnect = Client.prototype.connect + + let connection = 0 + + Client.prototype.connect = function (cb) { + // Simulate a failure on first call + if (connection === 0) { + connection++ + + return setTimeout(() => { + cb(connectionFailure) + }, 300) + } + + // And second connect taking > connection timeout + if (connection === 1) { + connection++ + + return setTimeout(() => { + orgConnect.call(this, cb) + }, 1000) + } + + orgConnect.call(this, cb) + } + + const pool = new Pool({ + Client: Client, + connectionTimeoutMillis: 1000, + max: 1 + }) + + // Direct connect + pool.connect((err, client, release) => { + expect(err).to.be(connectionFailure) + }) + + // Queued + let called = 0 + pool.connect((err, client, release) => { + // Verify the callback is only called once + expect(called++).to.be(0) + expect(err).to.be.an(Error) + + pool.query('select $1::text as name', ['brianc'], (err, res) => { + expect(err).to.be(undefined) + expect(res.rows).to.have.length(1) + pool.end(done) + }) + }) + }) }) From f91769538d8036efbf394277d95d9471b9b64d6f Mon Sep 17 00:00:00 2001 From: Bryan Clement Date: Wed, 2 Jan 2019 10:10:42 -0800 Subject: [PATCH 83/95] idleListener no longer grabs references to things it doesn't need (#83) --- index.js | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/index.js b/index.js index fe921cbb..cfe377c3 100644 --- a/index.js +++ b/index.js @@ -65,6 +65,20 @@ function promisify (Promise, callback) { return { callback: cb, result: result } } +function makeIdleListener (pool, client) { + return function idleListener (err) { + err.client = client + client.removeListener('error', idleListener) + client.on('error', () => { + pool.log('additional client error after disconnection due to error', err) + }) + pool._remove(client) + // TODO - document that once the pool emits an error + // the client has already been closed & purged and is unusable + pool.emit('error', err, client) + } +} + class Pool extends EventEmitter { constructor (options, Client) { super() @@ -197,17 +211,7 @@ class Pool extends EventEmitter { newClient (pendingItem) { const client = new this.Client(this.options) this._clients.push(client) - const idleListener = (err) => { - err.client = client - client.removeListener('error', idleListener) - client.on('error', () => { - this.log('additional client error after disconnection due to error', err) - }) - this._remove(client) - // TODO - document that once the pool emits an error - // the client has already been closed & purged and is unusable - this.emit('error', err, client) - } + const idleListener = makeIdleListener(this, client) this.log('checking client timeout') From 4d2ad3695192279f658d08d8cd5054661140def7 Mon Sep 17 00:00:00 2001 From: Brian C Date: Wed, 2 Jan 2019 12:11:21 -0600 Subject: [PATCH 84/95] Upgrade to test on node 10 (#114) * Upgrade to test on node 10 * Use errno instead of code * Only check errno if it exists --- .travis.yml | 2 +- package-lock.json | 2106 ++++++++++++++++++++++++++++++++++++ package.json | 6 +- test/connection-timeout.js | 3 + test/error-handling.js | 8 +- test/events.js | 3 +- test/mocha.opts | 1 - 7 files changed, 2120 insertions(+), 9 deletions(-) create mode 100644 package-lock.json diff --git a/.travis.yml b/.travis.yml index cd2db492..0deb0216 100644 --- a/.travis.yml +++ b/.travis.yml @@ -12,6 +12,6 @@ matrix: - node_js: "8" addons: postgresql: "9.6" - - node_js: "9" + - node_js: "10" addons: postgresql: "9.6" diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 00000000..a05e4c50 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,2106 @@ +{ + "name": "pg-pool", + "version": "2.0.5", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", + "dev": true + }, + "acorn": { + "version": "3.3.0", + "resolved": "http://registry.npmjs.org/acorn/-/acorn-3.3.0.tgz", + "integrity": "sha1-ReN/s56No/JbruP/U2niu18iAXo=", + "dev": true + }, + "acorn-jsx": { + "version": "3.0.1", + "resolved": "http://registry.npmjs.org/acorn-jsx/-/acorn-jsx-3.0.1.tgz", + "integrity": "sha1-r9+UiPsezvyDSPb7IvRk4ypYs2s=", + "dev": true, + "requires": { + "acorn": "^3.0.4" + } + }, + "acorn-to-esprima": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/acorn-to-esprima/-/acorn-to-esprima-2.0.8.tgz", + "integrity": "sha1-AD8MZC65ITL0F9NwjxStqCrfLrE=", + "dev": true + }, + "ajv": { + "version": "4.11.8", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-4.11.8.tgz", + "integrity": "sha1-gv+wKynmYq5TvcIK8VlHcGc5xTY=", + "dev": true, + "requires": { + "co": "^4.6.0", + "json-stable-stringify": "^1.0.1" + } + }, + "ajv-keywords": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-1.5.1.tgz", + "integrity": "sha1-MU3QpLM2j609/NxU7eYXG4htrzw=", + "dev": true + }, + "ansi-escapes": { + "version": "1.4.0", + "resolved": "http://registry.npmjs.org/ansi-escapes/-/ansi-escapes-1.4.0.tgz", + "integrity": "sha1-06ioOzGapneTZisT52HHkRQiMG4=", + "dev": true + }, + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + }, + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true + }, + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "requires": { + "sprintf-js": "~1.0.2" + } + }, + "babel-code-frame": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", + "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=", + "dev": true, + "requires": { + "chalk": "^1.1.3", + "esutils": "^2.0.2", + "js-tokens": "^3.0.2" + } + }, + "babel-messages": { + "version": "6.23.0", + "resolved": "https://registry.npmjs.org/babel-messages/-/babel-messages-6.23.0.tgz", + "integrity": "sha1-8830cDhYA1sqKVHG7F7fbGLyYw4=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-runtime": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", + "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", + "dev": true, + "requires": { + "core-js": "^2.4.0", + "regenerator-runtime": "^0.11.0" + } + }, + "babel-traverse": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-traverse/-/babel-traverse-6.26.0.tgz", + "integrity": "sha1-RqnL1+3MYsjlwGTi0tjQ9ANXZu4=", + "dev": true, + "requires": { + "babel-code-frame": "^6.26.0", + "babel-messages": "^6.23.0", + "babel-runtime": "^6.26.0", + "babel-types": "^6.26.0", + "babylon": "^6.18.0", + "debug": "^2.6.8", + "globals": "^9.18.0", + "invariant": "^2.2.2", + "lodash": "^4.17.4" + } + }, + "babel-types": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-types/-/babel-types-6.26.0.tgz", + "integrity": "sha1-o7Bz+Uq0nrb6Vc1lInozQ4BjJJc=", + "dev": true, + "requires": { + "babel-runtime": "^6.26.0", + "esutils": "^2.0.2", + "lodash": "^4.17.4", + "to-fast-properties": "^1.0.3" + } + }, + "babylon": { + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz", + "integrity": "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==", + "dev": true + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "dev": true + }, + "bluebird": { + "version": "3.4.1", + "resolved": "http://registry.npmjs.org/bluebird/-/bluebird-3.4.1.tgz", + "integrity": "sha1-tzHd9I4t077awudeEhWhG8uR+gc=", + "dev": true + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "browser-stdout": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", + "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", + "dev": true + }, + "buffer-from": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", + "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", + "dev": true + }, + "buffer-writer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/buffer-writer/-/buffer-writer-2.0.0.tgz", + "integrity": "sha512-a7ZpuTZU1TRtnwyCNW3I5dc0wWNC3VR9S++Ewyk2HHZdrO3CQJqSpd+95Us590V6AL7JqUAH2IwZ/398PmNFgw==", + "dev": true + }, + "caller-path": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-0.1.0.tgz", + "integrity": "sha1-lAhe9jWB7NPaqSREqP6U6CV3dR8=", + "dev": true, + "requires": { + "callsites": "^0.2.0" + } + }, + "callsites": { + "version": "0.2.0", + "resolved": "http://registry.npmjs.org/callsites/-/callsites-0.2.0.tgz", + "integrity": "sha1-r6uWJikQp/M8GaV3WCXGnzTjUMo=", + "dev": true + }, + "chalk": { + "version": "1.1.3", + "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "requires": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + }, + "dependencies": { + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true + } + } + }, + "circular-json": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/circular-json/-/circular-json-0.3.3.tgz", + "integrity": "sha512-UZK3NBx2Mca+b5LsG7bY183pHWt5Y1xts4P3Pz7ENTwGVnJOUWbRb3ocjvX7hx9tq/yTAdclXm9sZ38gNuem4A==", + "dev": true + }, + "cli-cursor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-1.0.2.tgz", + "integrity": "sha1-ZNo/fValRBLll5S9Ytw1KV6PKYc=", + "dev": true, + "requires": { + "restore-cursor": "^1.0.1" + } + }, + "cli-width": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.0.tgz", + "integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=", + "dev": true + }, + "clone": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", + "integrity": "sha1-2jCcwmPfFZlMaIypAheco8fNfH4=", + "dev": true + }, + "co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", + "dev": true + }, + "code-point-at": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", + "dev": true + }, + "commander": { + "version": "2.15.1", + "resolved": "http://registry.npmjs.org/commander/-/commander-2.15.1.tgz", + "integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==", + "dev": true + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "concat-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" + } + }, + "config-chain": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.12.tgz", + "integrity": "sha512-a1eOIcu8+7lUInge4Rpf/n4Krkf3Dd9lqhljRzII1/Zno/kRtUWnznPO3jOKBmTEktkt3fkxisUcivoj0ebzoA==", + "dev": true, + "requires": { + "ini": "^1.3.4", + "proto-list": "~1.2.1" + } + }, + "core-js": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.0.tgz", + "integrity": "sha512-kLRC6ncVpuEW/1kwrOXYX6KQASCVtrh1gQr/UiaVgFlf9WE5Vp+lNe5+h3LuMr5PAucWnnEXwH0nQHRH/gpGtw==", + "dev": true + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", + "dev": true + }, + "d": { + "version": "1.0.0", + "resolved": "http://registry.npmjs.org/d/-/d-1.0.0.tgz", + "integrity": "sha1-dUu1v+VUUdpppYuU1F9MWwRi1Y8=", + "dev": true, + "requires": { + "es5-ext": "^0.10.9" + } + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "debug-log": { + "version": "1.0.1", + "resolved": "http://registry.npmjs.org/debug-log/-/debug-log-1.0.1.tgz", + "integrity": "sha1-IwdjLUwEOCuN+KMvcLiVBG1SdF8=", + "dev": true + }, + "deep-is": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", + "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", + "dev": true + }, + "defaults": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.3.tgz", + "integrity": "sha1-xlYFHpgX2f8I7YgUd/P+QBnz730=", + "dev": true, + "requires": { + "clone": "^1.0.2" + } + }, + "deglob": { + "version": "1.1.2", + "resolved": "http://registry.npmjs.org/deglob/-/deglob-1.1.2.tgz", + "integrity": "sha1-dtV3wl/j9zKUEqK1nq3qV6xQDj8=", + "dev": true, + "requires": { + "find-root": "^1.0.0", + "glob": "^7.0.5", + "ignore": "^3.0.9", + "pkg-config": "^1.1.0", + "run-parallel": "^1.1.2", + "uniq": "^1.0.1", + "xtend": "^4.0.0" + }, + "dependencies": { + "glob": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", + "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + } + } + }, + "diff": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", + "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", + "dev": true + }, + "disparity": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/disparity/-/disparity-2.0.0.tgz", + "integrity": "sha1-V92stHMkrl9Y0swNqIbbTOnutxg=", + "dev": true, + "requires": { + "ansi-styles": "^2.0.1", + "diff": "^1.3.2" + }, + "dependencies": { + "diff": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-1.4.0.tgz", + "integrity": "sha1-fyjS657nsVqX79ic5j3P2qPMur8=", + "dev": true + } + } + }, + "doctrine": { + "version": "1.5.0", + "resolved": "http://registry.npmjs.org/doctrine/-/doctrine-1.5.0.tgz", + "integrity": "sha1-N53Ocw9hZvds76TmcHoVmwLFpvo=", + "dev": true, + "requires": { + "esutils": "^2.0.2", + "isarray": "^1.0.0" + } + }, + "editorconfig": { + "version": "0.13.3", + "resolved": "https://registry.npmjs.org/editorconfig/-/editorconfig-0.13.3.tgz", + "integrity": "sha512-WkjsUNVCu+ITKDj73QDvi0trvpdDWdkDyHybDGSXPfekLCqwmpD7CP7iPbvBgosNuLcI96XTDwNa75JyFl7tEQ==", + "dev": true, + "requires": { + "bluebird": "^3.0.5", + "commander": "^2.9.0", + "lru-cache": "^3.2.0", + "semver": "^5.1.0", + "sigmund": "^1.0.1" + }, + "dependencies": { + "semver": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz", + "integrity": "sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==", + "dev": true + } + } + }, + "es5-ext": { + "version": "0.10.46", + "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.46.tgz", + "integrity": "sha512-24XxRvJXNFwEMpJb3nOkiRJKRoupmjYmOPVlI65Qy2SrtxwOTB+g6ODjBKOtwEHbYrhWRty9xxOWLNdClT2djw==", + "dev": true, + "requires": { + "es6-iterator": "~2.0.3", + "es6-symbol": "~3.1.1", + "next-tick": "1" + } + }, + "es6-iterator": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", + "integrity": "sha1-p96IkUGgWpSwhUQDstCg+/qY87c=", + "dev": true, + "requires": { + "d": "1", + "es5-ext": "^0.10.35", + "es6-symbol": "^3.1.1" + } + }, + "es6-map": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/es6-map/-/es6-map-0.1.5.tgz", + "integrity": "sha1-kTbgUD3MBqMBaQ8LsU/042TpSfA=", + "dev": true, + "requires": { + "d": "1", + "es5-ext": "~0.10.14", + "es6-iterator": "~2.0.1", + "es6-set": "~0.1.5", + "es6-symbol": "~3.1.1", + "event-emitter": "~0.3.5" + } + }, + "es6-set": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/es6-set/-/es6-set-0.1.5.tgz", + "integrity": "sha1-0rPsXU2ADO2BjbU40ol02wpzzLE=", + "dev": true, + "requires": { + "d": "1", + "es5-ext": "~0.10.14", + "es6-iterator": "~2.0.1", + "es6-symbol": "3.1.1", + "event-emitter": "~0.3.5" + } + }, + "es6-symbol": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.1.tgz", + "integrity": "sha1-vwDvT9q2uhtG7Le2KbTH7VcVzHc=", + "dev": true, + "requires": { + "d": "1", + "es5-ext": "~0.10.14" + } + }, + "es6-weak-map": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/es6-weak-map/-/es6-weak-map-2.0.2.tgz", + "integrity": "sha1-XjqzIlH/0VOKH45f+hNXdy+S2W8=", + "dev": true, + "requires": { + "d": "1", + "es5-ext": "^0.10.14", + "es6-iterator": "^2.0.1", + "es6-symbol": "^3.1.1" + } + }, + "escape-string-regexp": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.2.tgz", + "integrity": "sha1-Tbwv5nTnGUnK8/smlc5/LcHZqNE=", + "dev": true + }, + "escope": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/escope/-/escope-3.6.0.tgz", + "integrity": "sha1-4Bl16BJ4GhY6ba392AOY3GTIicM=", + "dev": true, + "requires": { + "es6-map": "^0.1.3", + "es6-weak-map": "^2.0.1", + "esrecurse": "^4.1.0", + "estraverse": "^4.1.1" + } + }, + "esformatter": { + "version": "0.9.6", + "resolved": "https://registry.npmjs.org/esformatter/-/esformatter-0.9.6.tgz", + "integrity": "sha1-Ngiux4KN7uPNP0bhGSrrRyaKlX8=", + "dev": true, + "requires": { + "acorn-to-esprima": "^2.0.6", + "babel-traverse": "^6.4.5", + "debug": "^0.7.4", + "disparity": "^2.0.0", + "esformatter-parser": "^1.0.0", + "glob": "^5.0.3", + "minimist": "^1.1.1", + "mout": ">=0.9 <2.0", + "npm-run": "^2.0.0", + "resolve": "^1.1.5", + "rocambole": ">=0.7 <2.0", + "rocambole-indent": "^2.0.4", + "rocambole-linebreak": "^1.0.2", + "rocambole-node": "~1.0", + "rocambole-token": "^1.1.2", + "rocambole-whitespace": "^1.0.0", + "stdin": "*", + "strip-json-comments": "~0.1.1", + "supports-color": "^1.3.1", + "user-home": "^2.0.0" + }, + "dependencies": { + "debug": { + "version": "0.7.4", + "resolved": "http://registry.npmjs.org/debug/-/debug-0.7.4.tgz", + "integrity": "sha1-BuHqgILCyxTjmAbiLi9vdX+Srzk=", + "dev": true + }, + "glob": { + "version": "5.0.15", + "resolved": "https://registry.npmjs.org/glob/-/glob-5.0.15.tgz", + "integrity": "sha1-G8k2ueAvSmA/zCIuz3Yz0wuLk7E=", + "dev": true, + "requires": { + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "2 || 3", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "minimist": { + "version": "1.2.0", + "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "dev": true + }, + "strip-json-comments": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-0.1.3.tgz", + "integrity": "sha1-Fkxk43Coo8wAyeAbU55WmCPw7lQ=", + "dev": true + }, + "supports-color": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-1.3.1.tgz", + "integrity": "sha1-FXWN8J2P87SswwdTn6vicJXhBC0=", + "dev": true + } + } + }, + "esformatter-eol-last": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/esformatter-eol-last/-/esformatter-eol-last-1.0.0.tgz", + "integrity": "sha1-RaeP9GIrHUnkT1a0mQV2amMpDAc=", + "dev": true, + "requires": { + "string.prototype.endswith": "^0.2.0" + } + }, + "esformatter-ignore": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/esformatter-ignore/-/esformatter-ignore-0.1.3.tgz", + "integrity": "sha1-BNO4db+knd4ATMWN9va7w8BWfx4=", + "dev": true + }, + "esformatter-jsx": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/esformatter-jsx/-/esformatter-jsx-7.4.1.tgz", + "integrity": "sha1-siCa4JCPQTp0exIFcny/S6QklgI=", + "dev": true, + "requires": { + "babylon": "6.14.1", + "esformatter-ignore": "^0.1.3", + "extend": "3.0.0", + "js-beautify": "1.6.4" + }, + "dependencies": { + "babylon": { + "version": "6.14.1", + "resolved": "http://registry.npmjs.org/babylon/-/babylon-6.14.1.tgz", + "integrity": "sha1-lWJ1+rcnU62bNDXXr+WPi/CimBU=", + "dev": true + } + } + }, + "esformatter-literal-notation": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/esformatter-literal-notation/-/esformatter-literal-notation-1.0.1.tgz", + "integrity": "sha1-cQ57QgF1/j9+WvrVu60ykQOELi8=", + "dev": true, + "requires": { + "rocambole": "^0.3.6", + "rocambole-token": "^1.2.1" + }, + "dependencies": { + "esprima": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-1.0.4.tgz", + "integrity": "sha1-n1V+CPw7TSbs6d00+Pv0drYlha0=", + "dev": true + }, + "rocambole": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/rocambole/-/rocambole-0.3.6.tgz", + "integrity": "sha1-Teu/WUMUS8e2AG2Vvo+swLdDUqc=", + "dev": true, + "requires": { + "esprima": "~1.0" + } + } + } + }, + "esformatter-parser": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/esformatter-parser/-/esformatter-parser-1.0.0.tgz", + "integrity": "sha1-CFQHLQSHU57TnK442KVDLBfsEdM=", + "dev": true, + "requires": { + "acorn-to-esprima": "^2.0.8", + "babel-traverse": "^6.9.0", + "babylon": "^6.8.0", + "rocambole": "^0.7.0" + } + }, + "esformatter-quotes": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/esformatter-quotes/-/esformatter-quotes-1.1.0.tgz", + "integrity": "sha1-4ixsRFx/MGBB2BybnlH8psv6yoI=", + "dev": true + }, + "esformatter-remove-trailing-commas": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/esformatter-remove-trailing-commas/-/esformatter-remove-trailing-commas-1.0.1.tgz", + "integrity": "sha1-k5diTB+qmA/E7Mfl6YE+tPK1gqc=", + "dev": true, + "requires": { + "rocambole-token": "^1.2.1" + } + }, + "esformatter-semicolon-first": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/esformatter-semicolon-first/-/esformatter-semicolon-first-1.2.0.tgz", + "integrity": "sha1-47US0dTgcxDqvKv1cnfqfIpW4kI=", + "dev": true, + "requires": { + "esformatter-parser": "^1.0", + "rocambole": ">=0.6.0 <2.0", + "rocambole-linebreak": "^1.0.2", + "rocambole-token": "^1.2.1" + } + }, + "esformatter-spaced-lined-comment": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/esformatter-spaced-lined-comment/-/esformatter-spaced-lined-comment-2.0.1.tgz", + "integrity": "sha1-3F80B/k8KV4eVkRr00RWDaXm3Kw=", + "dev": true + }, + "eslint": { + "version": "2.10.2", + "resolved": "http://registry.npmjs.org/eslint/-/eslint-2.10.2.tgz", + "integrity": "sha1-sjCUgv7wQ9MgM2WjIShebM4Bw9c=", + "dev": true, + "requires": { + "chalk": "^1.1.3", + "concat-stream": "^1.4.6", + "debug": "^2.1.1", + "doctrine": "^1.2.1", + "es6-map": "^0.1.3", + "escope": "^3.6.0", + "espree": "3.1.4", + "estraverse": "^4.2.0", + "esutils": "^2.0.2", + "file-entry-cache": "^1.1.1", + "glob": "^7.0.3", + "globals": "^9.2.0", + "ignore": "^3.1.2", + "imurmurhash": "^0.1.4", + "inquirer": "^0.12.0", + "is-my-json-valid": "^2.10.0", + "is-resolvable": "^1.0.0", + "js-yaml": "^3.5.1", + "json-stable-stringify": "^1.0.0", + "lodash": "^4.0.0", + "mkdirp": "^0.5.0", + "optionator": "^0.8.1", + "path-is-absolute": "^1.0.0", + "path-is-inside": "^1.0.1", + "pluralize": "^1.2.1", + "progress": "^1.1.8", + "require-uncached": "^1.0.2", + "shelljs": "^0.6.0", + "strip-json-comments": "~1.0.1", + "table": "^3.7.8", + "text-table": "~0.2.0", + "user-home": "^2.0.0" + }, + "dependencies": { + "glob": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", + "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + } + } + }, + "eslint-config-standard-jsx": { + "version": "1.2.1", + "resolved": "http://registry.npmjs.org/eslint-config-standard-jsx/-/eslint-config-standard-jsx-1.2.1.tgz", + "integrity": "sha1-DRmxcF8K1INj7yqLv6cd8BLZibM=", + "dev": true + }, + "eslint-plugin-promise": { + "version": "1.3.2", + "resolved": "http://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-1.3.2.tgz", + "integrity": "sha1-/OMy1vX/UjIApTdwSGPsPCQiunw=", + "dev": true + }, + "eslint-plugin-react": { + "version": "5.2.2", + "resolved": "http://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-5.2.2.tgz", + "integrity": "sha1-fbBo4fVIf2hx5N7vNqOBwwPqwWE=", + "dev": true, + "requires": { + "doctrine": "^1.2.2", + "jsx-ast-utils": "^1.2.1" + } + }, + "eslint-plugin-standard": { + "version": "1.3.3", + "resolved": "http://registry.npmjs.org/eslint-plugin-standard/-/eslint-plugin-standard-1.3.3.tgz", + "integrity": "sha1-owhUUVI0MedvQJxwy4+U4yvw7H8=", + "dev": true + }, + "espree": { + "version": "3.1.4", + "resolved": "http://registry.npmjs.org/espree/-/espree-3.1.4.tgz", + "integrity": "sha1-BybXrIOvl6fISY2ps2OjYJ0qaKE=", + "dev": true, + "requires": { + "acorn": "^3.1.0", + "acorn-jsx": "^3.0.0" + } + }, + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true + }, + "esrecurse": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz", + "integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==", + "dev": true, + "requires": { + "estraverse": "^4.1.0" + } + }, + "estraverse": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz", + "integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=", + "dev": true + }, + "esutils": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", + "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=", + "dev": true + }, + "event-emitter": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz", + "integrity": "sha1-34xp7vFkeSPHFXuc6DhAYQsCzDk=", + "dev": true, + "requires": { + "d": "1", + "es5-ext": "~0.10.14" + } + }, + "exit-hook": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/exit-hook/-/exit-hook-1.1.1.tgz", + "integrity": "sha1-8FyiM7SMBdVP/wd2XfhQfpXAL/g=", + "dev": true + }, + "expect.js": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/expect.js/-/expect.js-0.3.1.tgz", + "integrity": "sha1-sKWaDS7/VDdUTr8M6qYBWEHQm1s=", + "dev": true + }, + "extend": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.0.tgz", + "integrity": "sha1-WkdDU7nzNT3dgXbf03uRyDpG8dQ=", + "dev": true + }, + "fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", + "dev": true + }, + "figures": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-1.7.0.tgz", + "integrity": "sha1-y+Hjr/zxzUS4DK3+0o3Hk6lwHS4=", + "dev": true, + "requires": { + "escape-string-regexp": "^1.0.5", + "object-assign": "^4.1.0" + }, + "dependencies": { + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true + } + } + }, + "file-entry-cache": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-1.3.1.tgz", + "integrity": "sha1-RMYepgeuS+nBQC9B9EJwy/4zT/g=", + "dev": true, + "requires": { + "flat-cache": "^1.2.1", + "object-assign": "^4.0.1" + } + }, + "find-root": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", + "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==", + "dev": true + }, + "flat-cache": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-1.3.4.tgz", + "integrity": "sha512-VwyB3Lkgacfik2vhqR4uv2rvebqmDvFu4jlN/C1RzWoJEo8I7z4Q404oiqYCkq41mni8EzQnm95emU9seckwtg==", + "dev": true, + "requires": { + "circular-json": "^0.3.1", + "graceful-fs": "^4.1.2", + "rimraf": "~2.6.2", + "write": "^0.2.1" + } + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "generate-function": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.3.1.tgz", + "integrity": "sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ==", + "dev": true, + "requires": { + "is-property": "^1.0.2" + } + }, + "generate-object-property": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/generate-object-property/-/generate-object-property-1.2.0.tgz", + "integrity": "sha1-nA4cQDCM6AT0eDYYuTf6iPmdUNA=", + "dev": true, + "requires": { + "is-property": "^1.0.0" + } + }, + "get-stdin": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-5.0.1.tgz", + "integrity": "sha1-Ei4WFZHiH/TFJTAwVpPyDmOTo5g=", + "dev": true + }, + "glob": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", + "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "globals": { + "version": "9.18.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-9.18.0.tgz", + "integrity": "sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ==", + "dev": true + }, + "graceful-fs": { + "version": "4.1.15", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz", + "integrity": "sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==", + "dev": true + }, + "growl": { + "version": "1.10.5", + "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", + "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", + "dev": true + }, + "has-ansi": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", + "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "he": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz", + "integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=", + "dev": true + }, + "ignore": { + "version": "3.3.10", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-3.3.10.tgz", + "integrity": "sha512-Pgs951kaMm5GXP7MOvxERINe3gsaVjUWFm+UZPSq9xYriQAksyhg0csnS0KXSNRD5NmNdapXEpjxG49+AKh/ug==", + "dev": true + }, + "imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "dev": true + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "dev": true + }, + "ini": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", + "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", + "dev": true + }, + "inquirer": { + "version": "0.12.0", + "resolved": "http://registry.npmjs.org/inquirer/-/inquirer-0.12.0.tgz", + "integrity": "sha1-HvK/1jUE3wvHV4X/+MLEHfEvB34=", + "dev": true, + "requires": { + "ansi-escapes": "^1.1.0", + "ansi-regex": "^2.0.0", + "chalk": "^1.0.0", + "cli-cursor": "^1.0.1", + "cli-width": "^2.0.0", + "figures": "^1.3.5", + "lodash": "^4.3.0", + "readline2": "^1.0.1", + "run-async": "^0.1.0", + "rx-lite": "^3.1.2", + "string-width": "^1.0.1", + "strip-ansi": "^3.0.0", + "through": "^2.3.6" + } + }, + "invariant": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", + "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", + "dev": true, + "requires": { + "loose-envify": "^1.0.0" + } + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "dev": true, + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "is-my-ip-valid": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-my-ip-valid/-/is-my-ip-valid-1.0.0.tgz", + "integrity": "sha512-gmh/eWXROncUzRnIa1Ubrt5b8ep/MGSnfAUI3aRp+sqTCs1tv1Isl8d8F6JmkN3dXKc3ehZMrtiPN9eL03NuaQ==", + "dev": true + }, + "is-my-json-valid": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/is-my-json-valid/-/is-my-json-valid-2.19.0.tgz", + "integrity": "sha512-mG0f/unGX1HZ5ep4uhRaPOS8EkAY8/j6mDRMJrutq4CqhoJWYp7qAlonIPy3TV7p3ju4TK9fo/PbnoksWmsp5Q==", + "dev": true, + "requires": { + "generate-function": "^2.0.0", + "generate-object-property": "^1.1.0", + "is-my-ip-valid": "^1.0.0", + "jsonpointer": "^4.0.0", + "xtend": "^4.0.0" + } + }, + "is-property": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz", + "integrity": "sha1-V/4cTkhHTt1lsJkR8msc1Ald2oQ=", + "dev": true + }, + "is-resolvable": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.1.0.tgz", + "integrity": "sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg==", + "dev": true + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true + }, + "js-beautify": { + "version": "1.6.4", + "resolved": "http://registry.npmjs.org/js-beautify/-/js-beautify-1.6.4.tgz", + "integrity": "sha1-qa95aZdCrJobb93B/bx4vE1RX8M=", + "dev": true, + "requires": { + "config-chain": "~1.1.5", + "editorconfig": "^0.13.2", + "mkdirp": "~0.5.0", + "nopt": "~3.0.1" + } + }, + "js-tokens": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", + "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=", + "dev": true + }, + "js-yaml": { + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.12.0.tgz", + "integrity": "sha512-PIt2cnwmPfL4hKNwqeiuz4bKfnzHTBv6HyVgjahA6mPLwPDzjDWrplJBMjHUFxku/N3FlmrbyPclad+I+4mJ3A==", + "dev": true, + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, + "json-stable-stringify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz", + "integrity": "sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8=", + "dev": true, + "requires": { + "jsonify": "~0.0.0" + } + }, + "jsonify": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz", + "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=", + "dev": true + }, + "jsonpointer": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-4.0.1.tgz", + "integrity": "sha1-T9kss04OnbPInIYi7PUfm5eMbLk=", + "dev": true + }, + "jsx-ast-utils": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-1.4.1.tgz", + "integrity": "sha1-OGchPo3Xm/Ho8jAMDPwe+xgsDfE=", + "dev": true + }, + "levn": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", + "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", + "dev": true, + "requires": { + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2" + } + }, + "lodash": { + "version": "4.17.11", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", + "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==", + "dev": true + }, + "loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "dev": true, + "requires": { + "js-tokens": "^3.0.0 || ^4.0.0" + } + }, + "lru-cache": { + "version": "3.2.0", + "resolved": "http://registry.npmjs.org/lru-cache/-/lru-cache-3.2.0.tgz", + "integrity": "sha1-cXibO39Tmb7IVl3aOKow0qCX7+4=", + "dev": true, + "requires": { + "pseudomap": "^1.0.1" + } + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "0.0.8", + "resolved": "http://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", + "dev": true + }, + "mkdirp": { + "version": "0.5.1", + "resolved": "http://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "dev": true, + "requires": { + "minimist": "0.0.8" + } + }, + "mocha": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-5.2.0.tgz", + "integrity": "sha512-2IUgKDhc3J7Uug+FxMXuqIyYzH7gJjXECKe/w43IGgQHTSj3InJi+yAA7T24L9bQMRKiUEHxEX37G5JpVUGLcQ==", + "dev": true, + "requires": { + "browser-stdout": "1.3.1", + "commander": "2.15.1", + "debug": "3.1.0", + "diff": "3.5.0", + "escape-string-regexp": "1.0.5", + "glob": "7.1.2", + "growl": "1.10.5", + "he": "1.1.1", + "minimatch": "3.0.4", + "mkdirp": "0.5.1", + "supports-color": "5.4.0" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true + } + } + }, + "mout": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/mout/-/mout-1.1.0.tgz", + "integrity": "sha512-XsP0vf4As6BfqglxZqbqQ8SR6KQot2AgxvR0gG+WtUkf90vUXchMOZQtPf/Hml1rEffJupqL/tIrU6EYhsUQjw==", + "dev": true + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "multiline": { + "version": "1.0.2", + "resolved": "http://registry.npmjs.org/multiline/-/multiline-1.0.2.tgz", + "integrity": "sha1-abHyX/B00oKJBPJE3dBrfZbvbJM=", + "dev": true, + "requires": { + "strip-indent": "^1.0.0" + } + }, + "mute-stream": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.5.tgz", + "integrity": "sha1-j7+rsKmKJT0xhDMfno3rc3L6xsA=", + "dev": true + }, + "next-tick": { + "version": "1.0.0", + "resolved": "http://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz", + "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=", + "dev": true + }, + "nopt": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz", + "integrity": "sha1-xkZdvwirzU2zWTF/eaxopkayj/k=", + "dev": true, + "requires": { + "abbrev": "1" + } + }, + "npm-path": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/npm-path/-/npm-path-1.1.0.tgz", + "integrity": "sha1-BHSuAEGcMn1UcBt88s0F3Ii+EUA=", + "dev": true, + "requires": { + "which": "^1.2.4" + } + }, + "npm-run": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/npm-run/-/npm-run-2.0.0.tgz", + "integrity": "sha1-KN/ArV4uRv4ISOK9WN3wAue3PBU=", + "dev": true, + "requires": { + "minimist": "^1.1.1", + "npm-path": "^1.0.1", + "npm-which": "^2.0.0", + "serializerr": "^1.0.1", + "spawn-sync": "^1.0.5", + "sync-exec": "^0.5.0" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "dev": true + } + } + }, + "npm-which": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/npm-which/-/npm-which-2.0.0.tgz", + "integrity": "sha1-DEaYIWC3gwk2YdHQG9RJbS/qu6w=", + "dev": true, + "requires": { + "commander": "^2.2.0", + "npm-path": "^1.0.0", + "which": "^1.0.5" + } + }, + "number-is-nan": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", + "dev": true + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "dev": true + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "onetime": { + "version": "1.1.0", + "resolved": "http://registry.npmjs.org/onetime/-/onetime-1.1.0.tgz", + "integrity": "sha1-ofeDj4MUxRbwXs78vEzP4EtO14k=", + "dev": true + }, + "optionator": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz", + "integrity": "sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=", + "dev": true, + "requires": { + "deep-is": "~0.1.3", + "fast-levenshtein": "~2.0.4", + "levn": "~0.3.0", + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2", + "wordwrap": "~1.0.0" + } + }, + "os-homedir": { + "version": "1.0.2", + "resolved": "http://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", + "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", + "dev": true + }, + "os-shim": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/os-shim/-/os-shim-0.1.3.tgz", + "integrity": "sha1-a2LDeRz3kJ6jXtRuF2WLtBfLORc=", + "dev": true + }, + "packet-reader": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/packet-reader/-/packet-reader-0.3.1.tgz", + "integrity": "sha1-zWLmCvjX/qinBexP+ZCHHEaHHyc=", + "dev": true + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "http://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true + }, + "path-is-inside": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", + "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=", + "dev": true + }, + "path-parse": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", + "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", + "dev": true + }, + "pg": { + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/pg/-/pg-7.7.1.tgz", + "integrity": "sha512-p3I0mXOmUvCoVlCMFW6iYSrnguPol6q8He15NGgSIdM3sPGjFc+8JGCeKclw8ZR4ETd+Jxy2KNiaPUcocHZeMw==", + "dev": true, + "requires": { + "buffer-writer": "2.0.0", + "packet-reader": "0.3.1", + "pg-connection-string": "0.1.3", + "pg-pool": "^2.0.4", + "pg-types": "~1.12.1", + "pgpass": "1.x", + "semver": "4.3.2" + } + }, + "pg-connection-string": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-0.1.3.tgz", + "integrity": "sha1-2hhHsglA5C7hSSvq9l1J2RskXfc=", + "dev": true + }, + "pg-cursor": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/pg-cursor/-/pg-cursor-1.3.0.tgz", + "integrity": "sha1-siDxkIl2t7QNqjc8etpfyoI6sNk=", + "dev": true + }, + "pg-pool": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-2.0.5.tgz", + "integrity": "sha512-T4W9qzP2LjItXuXbW6jgAF2AY0jHp6IoTxRhM3GB7yzfBxzDnA3GCm0sAduzmmiCybMqD0+V1HiqIG5X2YWqlQ==", + "dev": true + }, + "pg-types": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-1.12.1.tgz", + "integrity": "sha1-1kCH45A7WP+q0nnnWVxSIIoUw9I=", + "dev": true, + "requires": { + "postgres-array": "~1.0.0", + "postgres-bytea": "~1.0.0", + "postgres-date": "~1.0.0", + "postgres-interval": "^1.1.0" + } + }, + "pgpass": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.2.tgz", + "integrity": "sha1-Knu0G2BltnkH6R2hsHwYR8h3swY=", + "dev": true, + "requires": { + "split": "^1.0.0" + } + }, + "pkg-config": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/pkg-config/-/pkg-config-1.1.1.tgz", + "integrity": "sha1-VX7yLXPaPIg3EHdmxS6tq94pj+Q=", + "dev": true, + "requires": { + "debug-log": "^1.0.0", + "find-root": "^1.0.0", + "xtend": "^4.0.1" + } + }, + "pluralize": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-1.2.1.tgz", + "integrity": "sha1-0aIUg/0iu0HlihL6NCGCMUCJfEU=", + "dev": true + }, + "postgres-array": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-1.0.3.tgz", + "integrity": "sha512-5wClXrAP0+78mcsNX3/ithQ5exKvCyK5lr5NEEEeGwwM6NJdQgzIJBVxLvRW+huFpX92F2QnZ5CcokH0VhK2qQ==", + "dev": true + }, + "postgres-bytea": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz", + "integrity": "sha1-AntTPAqokOJtFy1Hz5zOzFIazTU=", + "dev": true + }, + "postgres-date": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.3.tgz", + "integrity": "sha1-4tiXAu/bJY/52c7g/pG9BpdSV6g=", + "dev": true + }, + "postgres-interval": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.1.2.tgz", + "integrity": "sha512-fC3xNHeTskCxL1dC8KOtxXt7YeFmlbTYtn7ul8MkVERuTmf7pI4DrkAxcw3kh1fQ9uz4wQmd03a1mRiXUZChfQ==", + "dev": true, + "requires": { + "xtend": "^4.0.0" + } + }, + "prelude-ls": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", + "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", + "dev": true + }, + "process-nextick-args": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", + "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==", + "dev": true + }, + "progress": { + "version": "1.1.8", + "resolved": "http://registry.npmjs.org/progress/-/progress-1.1.8.tgz", + "integrity": "sha1-4mDHj2Fhzdmw5WzD4Khd4Xx6V74=", + "dev": true + }, + "proto-list": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz", + "integrity": "sha1-IS1b/hMYMGpCD2QCuOJv85ZHqEk=", + "dev": true + }, + "protochain": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/protochain/-/protochain-1.0.5.tgz", + "integrity": "sha1-mRxAfpneJkqt+PgVBLXn+ve/omA=", + "dev": true + }, + "pseudomap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", + "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=", + "dev": true + }, + "readable-stream": { + "version": "2.3.6", + "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "readline2": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/readline2/-/readline2-1.0.1.tgz", + "integrity": "sha1-QQWWCP/BVHV7cV2ZidGZ/783LjU=", + "dev": true, + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "mute-stream": "0.0.5" + } + }, + "regenerator-runtime": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz", + "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==", + "dev": true + }, + "repeat-string": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", + "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", + "dev": true + }, + "require-uncached": { + "version": "1.0.3", + "resolved": "http://registry.npmjs.org/require-uncached/-/require-uncached-1.0.3.tgz", + "integrity": "sha1-Tg1W1slmL9MeQwEcS5WqSZVUIdM=", + "dev": true, + "requires": { + "caller-path": "^0.1.0", + "resolve-from": "^1.0.0" + } + }, + "resolve": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.8.1.tgz", + "integrity": "sha512-AicPrAC7Qu1JxPCZ9ZgCZlY35QgFnNqc+0LtbRNxnVw4TXvjQ72wnuL9JQcEBgXkI9JM8MsT9kaQoHcpCRJOYA==", + "dev": true, + "requires": { + "path-parse": "^1.0.5" + } + }, + "resolve-from": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-1.0.1.tgz", + "integrity": "sha1-Jsv+k10a7uq7Kbw/5a6wHpPUQiY=", + "dev": true + }, + "restore-cursor": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-1.0.1.tgz", + "integrity": "sha1-NGYfRohjJ/7SmRR5FSJS35LapUE=", + "dev": true, + "requires": { + "exit-hook": "^1.0.0", + "onetime": "^1.0.0" + } + }, + "rimraf": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz", + "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==", + "dev": true, + "requires": { + "glob": "^7.0.5" + }, + "dependencies": { + "glob": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", + "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + } + } + }, + "rocambole": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/rocambole/-/rocambole-0.7.0.tgz", + "integrity": "sha1-9seVBVF9xCtvuECEK4uVOw+WhYU=", + "dev": true, + "requires": { + "esprima": "^2.1" + }, + "dependencies": { + "esprima": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-2.7.3.tgz", + "integrity": "sha1-luO3DVd59q1JzQMmc9HDEnZ7pYE=", + "dev": true + } + } + }, + "rocambole-indent": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/rocambole-indent/-/rocambole-indent-2.0.4.tgz", + "integrity": "sha1-oYokl3ygQAuGHapGMehh3LUtCFw=", + "dev": true, + "requires": { + "debug": "^2.1.3", + "mout": "^0.11.0", + "rocambole-token": "^1.2.1" + }, + "dependencies": { + "mout": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/mout/-/mout-0.11.1.tgz", + "integrity": "sha1-ujYR318OWx/7/QEWa48C0fX6K5k=", + "dev": true + } + } + }, + "rocambole-linebreak": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/rocambole-linebreak/-/rocambole-linebreak-1.0.2.tgz", + "integrity": "sha1-A2IVFbQ7RyHJflocG8paA2Y2jy8=", + "dev": true, + "requires": { + "debug": "^2.1.3", + "rocambole-token": "^1.2.1", + "semver": "^4.3.1" + } + }, + "rocambole-node": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/rocambole-node/-/rocambole-node-1.0.0.tgz", + "integrity": "sha1-21tJ3nQHsAgN1RSHLyjjk9D3/z8=", + "dev": true + }, + "rocambole-token": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/rocambole-token/-/rocambole-token-1.2.1.tgz", + "integrity": "sha1-x4XfdCjcPLJ614lwR71SOMwHDTU=", + "dev": true + }, + "rocambole-whitespace": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/rocambole-whitespace/-/rocambole-whitespace-1.0.0.tgz", + "integrity": "sha1-YzMJSSVrKZQfWbGQRZ+ZnGsdO/k=", + "dev": true, + "requires": { + "debug": "^2.1.3", + "repeat-string": "^1.5.0", + "rocambole-token": "^1.2.1" + } + }, + "run-async": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-0.1.0.tgz", + "integrity": "sha1-yK1KXhEGYeQCp9IbUw4AnyX444k=", + "dev": true, + "requires": { + "once": "^1.3.0" + } + }, + "run-parallel": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.1.9.tgz", + "integrity": "sha512-DEqnSRTDw/Tc3FXf49zedI638Z9onwUotBMiUFKmrO2sdFKIbXamXGQ3Axd4qgphxKB4kw/qP1w5kTxnfU1B9Q==", + "dev": true + }, + "rx-lite": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/rx-lite/-/rx-lite-3.1.2.tgz", + "integrity": "sha1-Gc5QLKVyZl87ZHsQk5+X/RYV8QI=", + "dev": true + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "semver": { + "version": "4.3.2", + "resolved": "http://registry.npmjs.org/semver/-/semver-4.3.2.tgz", + "integrity": "sha1-x6BxWKgL7dBSNVt3DYLWZA+AO+c=", + "dev": true + }, + "serializerr": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/serializerr/-/serializerr-1.0.3.tgz", + "integrity": "sha1-EtTFqhw/+49tHcXzlaqUVVacP5E=", + "dev": true, + "requires": { + "protochain": "^1.0.5" + } + }, + "shelljs": { + "version": "0.6.1", + "resolved": "http://registry.npmjs.org/shelljs/-/shelljs-0.6.1.tgz", + "integrity": "sha1-7GIRvtGSBEIIj+D3Cyg3Iy7SyKg=", + "dev": true + }, + "sigmund": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz", + "integrity": "sha1-P/IfGYytIXX587eBhT/ZTQ0ZtZA=", + "dev": true + }, + "slice-ansi": { + "version": "0.0.4", + "resolved": "http://registry.npmjs.org/slice-ansi/-/slice-ansi-0.0.4.tgz", + "integrity": "sha1-7b+JA/ZvfOL46v1s7tZeJkyDGzU=", + "dev": true + }, + "spawn-sync": { + "version": "1.0.15", + "resolved": "https://registry.npmjs.org/spawn-sync/-/spawn-sync-1.0.15.tgz", + "integrity": "sha1-sAeZVX63+wyDdsKdROih6mfldHY=", + "dev": true, + "requires": { + "concat-stream": "^1.4.7", + "os-shim": "^0.1.2" + } + }, + "split": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/split/-/split-1.0.1.tgz", + "integrity": "sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg==", + "dev": true, + "requires": { + "through": "2" + } + }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "http://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "dev": true + }, + "standard": { + "version": "7.1.2", + "resolved": "http://registry.npmjs.org/standard/-/standard-7.1.2.tgz", + "integrity": "sha1-QBZu7sJAUGXRpPDj8VurxuJ0YH4=", + "dev": true, + "requires": { + "eslint": "~2.10.2", + "eslint-config-standard": "5.3.1", + "eslint-config-standard-jsx": "1.2.1", + "eslint-plugin-promise": "^1.0.8", + "eslint-plugin-react": "^5.0.1", + "eslint-plugin-standard": "^1.3.1", + "standard-engine": "^4.0.0" + }, + "dependencies": { + "eslint-config-standard": { + "version": "5.3.1", + "resolved": "http://registry.npmjs.org/eslint-config-standard/-/eslint-config-standard-5.3.1.tgz", + "integrity": "sha1-WRyWkVF0QTL1YdO5FagS6kE/5JA=", + "dev": true + } + } + }, + "standard-engine": { + "version": "4.1.3", + "resolved": "http://registry.npmjs.org/standard-engine/-/standard-engine-4.1.3.tgz", + "integrity": "sha1-ejGq1U8D2fOTVfQzic4GlPQJQVU=", + "dev": true, + "requires": { + "defaults": "^1.0.2", + "deglob": "^1.0.0", + "find-root": "^1.0.0", + "get-stdin": "^5.0.1", + "minimist": "^1.1.0", + "multiline": "^1.0.2", + "pkg-config": "^1.0.1", + "xtend": "^4.0.0" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "dev": true + } + } + }, + "standard-format": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/standard-format/-/standard-format-2.2.4.tgz", + "integrity": "sha1-uQ+zmmNfdJzU/RF/5HMNMRearu8=", + "dev": true, + "requires": { + "deglob": "^1.0.0", + "esformatter": "^0.9.0", + "esformatter-eol-last": "^1.0.0", + "esformatter-jsx": "^7.0.0", + "esformatter-literal-notation": "^1.0.0", + "esformatter-quotes": "^1.0.0", + "esformatter-remove-trailing-commas": "^1.0.1", + "esformatter-semicolon-first": "^1.1.0", + "esformatter-spaced-lined-comment": "^2.0.0", + "minimist": "^1.1.0", + "stdin": "0.0.1" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "dev": true + } + } + }, + "stdin": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/stdin/-/stdin-0.0.1.tgz", + "integrity": "sha1-0wQZgarsPf28d6GzjWNy449ftx4=", + "dev": true + }, + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "dev": true, + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + }, + "string.prototype.endswith": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/string.prototype.endswith/-/string.prototype.endswith-0.2.0.tgz", + "integrity": "sha1-oZwg3uUamHd+mkfhDwm+OTubunU=", + "dev": true + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "strip-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-1.0.1.tgz", + "integrity": "sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI=", + "dev": true, + "requires": { + "get-stdin": "^4.0.1" + }, + "dependencies": { + "get-stdin": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz", + "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=", + "dev": true + } + } + }, + "strip-json-comments": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-1.0.4.tgz", + "integrity": "sha1-HhX7ysl9Pumb8tc7TGVrCCu6+5E=", + "dev": true + }, + "supports-color": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz", + "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + }, + "sync-exec": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/sync-exec/-/sync-exec-0.5.0.tgz", + "integrity": "sha1-P3JY5KW6FyRTgZCfpqb2z1BuFmE=", + "dev": true + }, + "table": { + "version": "3.8.3", + "resolved": "http://registry.npmjs.org/table/-/table-3.8.3.tgz", + "integrity": "sha1-K7xULw/amGGnVdOUf+/Ys/UThV8=", + "dev": true, + "requires": { + "ajv": "^4.7.0", + "ajv-keywords": "^1.0.0", + "chalk": "^1.1.1", + "lodash": "^4.0.0", + "slice-ansi": "0.0.4", + "string-width": "^2.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + }, + "string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "dev": true, + "requires": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + } + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "requires": { + "ansi-regex": "^3.0.0" + } + } + } + }, + "text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", + "dev": true + }, + "through": { + "version": "2.3.8", + "resolved": "http://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", + "dev": true + }, + "to-fast-properties": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.3.tgz", + "integrity": "sha1-uDVx+k2MJbguIxsG46MFXeTKGkc=", + "dev": true + }, + "type-check": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", + "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", + "dev": true, + "requires": { + "prelude-ls": "~1.1.2" + } + }, + "typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", + "dev": true + }, + "uniq": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz", + "integrity": "sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8=", + "dev": true + }, + "user-home": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/user-home/-/user-home-2.0.0.tgz", + "integrity": "sha1-nHC/2Babwdy/SGBODwS4tJzenp8=", + "dev": true, + "requires": { + "os-homedir": "^1.0.0" + } + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "dev": true + }, + "which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, + "wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=", + "dev": true + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + }, + "write": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/write/-/write-0.2.1.tgz", + "integrity": "sha1-X8A4KOJkzqP+kUVUdvejxWbLB1c=", + "dev": true, + "requires": { + "mkdirp": "^0.5.1" + } + }, + "xtend": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", + "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=", + "dev": true + } + } +} diff --git a/package.json b/package.json index c9334457..cf7a14a1 100644 --- a/package.json +++ b/package.json @@ -29,12 +29,12 @@ "bluebird": "3.4.1", "co": "4.6.0", "expect.js": "0.3.1", - "lodash": "4.13.1", - "mocha": "^2.3.3", + "lodash": "^4.17.11", + "mocha": "^5.2.0", "pg": "*", "pg-cursor": "^1.3.0", "standard": "7.1.2", - "standard-format": "2.2.1" + "standard-format": "^2.2.4" }, "dependencies": {}, "peerDependencies": { diff --git a/test/connection-timeout.js b/test/connection-timeout.js index 2c4d68f7..8151354c 100644 --- a/test/connection-timeout.js +++ b/test/connection-timeout.js @@ -15,6 +15,9 @@ describe('connection timeout', () => { before((done) => { this.server = net.createServer((socket) => { + socket.on('data', () => { + // discard any buffered data or the server wont terminate + }) }) this.server.listen(() => { diff --git a/test/error-handling.js b/test/error-handling.js index 1c84889c..e899c350 100644 --- a/test/error-handling.js +++ b/test/error-handling.js @@ -154,11 +154,15 @@ describe('pool error handling', function () { const pool = new Pool({ max: 1, port: closeServer.address().port }) pool.connect((err) => { expect(err).to.be.an(Error) - expect(err.message).to.be('Connection terminated unexpectedly') + if (err.errno) { + expect(err.errno).to.be('ECONNRESET') + } }) pool.connect((err) => { expect(err).to.be.an(Error) - expect(err.message).to.be('Connection terminated unexpectedly') + if (err.errno) { + expect(err.errno).to.be('ECONNRESET') + } closeServer.close(() => { pool.end(done) }) diff --git a/test/events.js b/test/events.js index 3d4405e2..a2da4810 100644 --- a/test/events.js +++ b/test/events.js @@ -23,14 +23,13 @@ describe('events', function () { }) }) - it('emits "connect" only with a successful connection', function (done) { + it('emits "connect" only with a successful connection', function () { const pool = new Pool({ // This client will always fail to connect Client: mockClient({ connect: function (cb) { process.nextTick(() => { cb(new Error('bad news')) - setImmediate(done) }) } }) diff --git a/test/mocha.opts b/test/mocha.opts index 590fb862..eb0ba600 100644 --- a/test/mocha.opts +++ b/test/mocha.opts @@ -1,4 +1,3 @@ --require test/setup.js ---no-exit --bail --timeout 10000 From d7f6ed0c7cb7f546211ae3c90e6f1d50b7bcd383 Mon Sep 17 00:00:00 2001 From: "Brian M. Carlson" Date: Wed, 2 Jan 2019 12:14:50 -0600 Subject: [PATCH 85/95] Bump version --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index a05e4c50..f977e264 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "pg-pool", - "version": "2.0.5", + "version": "2.0.6", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index cf7a14a1..c4583b3b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "pg-pool", - "version": "2.0.5", + "version": "2.0.6", "description": "Connection pool for node-postgres", "main": "index.js", "directories": { From f9fc232db36a09633425d16aeb46099ac6a1a3c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20W=C3=BCrbach?= Date: Thu, 25 Jul 2019 19:38:21 +0200 Subject: [PATCH 86/95] Remove idleListener when client is in-use (#123) * Prevent double release with callback When using the callback instead of client.release, double releasing a client was possible causing clients to be re-added multiple times. * Remove idleListener when client is in-use When a client is in-use, the error handling should be done by the consumer and not by the pool itself as this otherwise might cause errors to be handled multiple times. * Handle verify failures --- index.js | 120 ++++++++++++++++++++++++++--------------- test/error-handling.js | 65 ++++++++++++++++++++-- 2 files changed, 138 insertions(+), 47 deletions(-) diff --git a/index.js b/index.js index cfe377c3..e93cc96c 100644 --- a/index.js +++ b/index.js @@ -12,8 +12,9 @@ const removeWhere = (list, predicate) => { } class IdleItem { - constructor (client, timeoutId) { + constructor (client, idleListener, timeoutId) { this.client = client + this.idleListener = idleListener this.timeoutId = timeoutId } } @@ -28,27 +29,6 @@ function throwOnRelease () { throw new Error('Release called on client which has already been released to the pool.') } -function release (client, err) { - client.release = throwOnRelease - if (err || this.ending) { - this._remove(client) - this._pulseQueue() - return - } - - // idle timeout - let tid - if (this.options.idleTimeoutMillis) { - tid = setTimeout(() => { - this.log('remove idle client') - this._remove(client) - }, this.options.idleTimeoutMillis) - } - - this._idle.push(new IdleItem(client, tid)) - this._pulseQueue() -} - function promisify (Promise, callback) { if (callback) { return { callback: callback, result: undefined } @@ -68,6 +48,7 @@ function promisify (Promise, callback) { function makeIdleListener (pool, client) { return function idleListener (err) { err.client = client + client.removeListener('error', idleListener) client.on('error', () => { pool.log('additional client error after disconnection due to error', err) @@ -132,17 +113,17 @@ class Pool extends EventEmitter { if (!this._idle.length && this._isFull()) { return } - const waiter = this._pendingQueue.shift() + const pendingItem = this._pendingQueue.shift() if (this._idle.length) { const idleItem = this._idle.pop() clearTimeout(idleItem.timeoutId) const client = idleItem.client - client.release = release.bind(this, client) - this.emit('acquire', client) - return waiter.callback(undefined, client, client.release) + const idleListener = idleItem.idleListener + + return this._acquireClient(client, pendingItem, idleListener, false) } if (!this._isFull()) { - return this.newClient(waiter) + return this.newClient(pendingItem) } throw new Error('unexpected condition') } @@ -249,26 +230,79 @@ class Pool extends EventEmitter { } } else { this.log('new client connected') - client.release = release.bind(this, client) - this.emit('connect', client) - this.emit('acquire', client) - if (!pendingItem.timedOut) { - if (this.options.verify) { - this.options.verify(client, pendingItem.callback) - } else { - pendingItem.callback(undefined, client, client.release) - } - } else { - if (this.options.verify) { - this.options.verify(client, client.release) - } else { - client.release() - } - } + + return this._acquireClient(client, pendingItem, idleListener, true) } }) } + // acquire a client for a pending work item + _acquireClient (client, pendingItem, idleListener, isNew) { + if (isNew) { + this.emit('connect', client) + } + + this.emit('acquire', client) + + let released = false + + client.release = (err) => { + if (released) { + throwOnRelease() + } + + released = true + this._release(client, idleListener, err) + } + + client.removeListener('error', idleListener) + + if (!pendingItem.timedOut) { + if (isNew && this.options.verify) { + this.options.verify(client, (err) => { + if (err) { + client.release(err) + return pendingItem.callback(err, undefined, NOOP) + } + + pendingItem.callback(undefined, client, client.release) + }) + } else { + pendingItem.callback(undefined, client, client.release) + } + } else { + if (isNew && this.options.verify) { + this.options.verify(client, client.release) + } else { + client.release() + } + } + } + + // release a client back to the poll, include an error + // to remove it from the pool + _release (client, idleListener, err) { + client.on('error', idleListener) + + if (err || this.ending) { + this._remove(client) + this._pulseQueue() + return + } + + // idle timeout + let tid + if (this.options.idleTimeoutMillis) { + tid = setTimeout(() => { + this.log('remove idle client') + this._remove(client) + }, this.options.idleTimeoutMillis) + } + + this._idle.push(new IdleItem(client, idleListener, tid)) + this._pulseQueue() + } + query (text, values, cb) { // guard clause against passing a function as the first parameter if (typeof text === 'function') { diff --git a/test/error-handling.js b/test/error-handling.js index e899c350..1e416683 100644 --- a/test/error-handling.js +++ b/test/error-handling.js @@ -43,6 +43,20 @@ describe('pool error handling', function () { expect(() => client.release()).to.throwError() return yield pool.end() })) + + it('should throw each time with callbacks', function (done) { + const pool = new Pool() + + pool.connect(function (err, client, clientDone) { + expect(err).not.to.be.an(Error) + clientDone() + + expect(() => clientDone()).to.throwError() + expect(() => clientDone()).to.throwError() + + pool.end(done) + }) + }) }) describe('calling connect after end', () => { @@ -101,13 +115,56 @@ describe('pool error handling', function () { client.release() yield new Promise((resolve, reject) => { process.nextTick(() => { + let poolError pool.once('error', (err) => { - expect(err.message).to.equal('expected') - expect(pool.idleCount).to.equal(0) - expect(pool.totalCount).to.equal(0) - pool.end().then(resolve, reject) + poolError = err }) + + let clientError + client.once('error', (err) => { + clientError = err + }) + client.emit('error', new Error('expected')) + + expect(clientError.message).to.equal('expected') + expect(poolError.message).to.equal('expected') + expect(pool.idleCount).to.equal(0) + expect(pool.totalCount).to.equal(0) + pool.end().then(resolve, reject) + }) + }) + })) + }) + + describe('error from in-use client', () => { + it('keeps the client in the pool', co.wrap(function * () { + const pool = new Pool() + const client = yield pool.connect() + expect(pool.totalCount).to.equal(1) + expect(pool.waitingCount).to.equal(0) + expect(pool.idleCount).to.equal(0) + + yield new Promise((resolve, reject) => { + process.nextTick(() => { + let poolError + pool.once('error', (err) => { + poolError = err + }) + + let clientError + client.once('error', (err) => { + clientError = err + }) + + client.emit('error', new Error('expected')) + + expect(clientError.message).to.equal('expected') + expect(poolError).not.to.be.ok() + expect(pool.idleCount).to.equal(0) + expect(pool.totalCount).to.equal(1) + client.release() + pool.end().then(resolve, reject) }) }) })) From e59a7667ffd4ae20c75a057ad4beadfbe830e6ce Mon Sep 17 00:00:00 2001 From: "Brian M. Carlson" Date: Thu, 25 Jul 2019 12:39:44 -0500 Subject: [PATCH 87/95] Bump version --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index f977e264..4099fbfd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "pg-pool", - "version": "2.0.6", + "version": "2.0.7", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index c4583b3b..0609ddbd 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "pg-pool", - "version": "2.0.6", + "version": "2.0.7", "description": "Connection pool for node-postgres", "main": "index.js", "directories": { From 70d5c0995852fba68391a5d4a19e234043d86198 Mon Sep 17 00:00:00 2001 From: Jonathan Baudanza Date: Thu, 25 Jul 2019 10:41:28 -0700 Subject: [PATCH 88/95] Remove reference to "min" config option (#126) Because it no longer exists. --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index a02fa32c..b77b65d8 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,6 @@ var pool2 = new Pool({ port: 5432, ssl: true, max: 20, // set pool max size to 20 - min: 4, // set min pool size to 4 idleTimeoutMillis: 1000, // close idle clients after 1 second connectionTimeoutMillis: 1000, // return an error after 1 second if connection could not be established }) From 2d2a87392ceaf73ba0d2ed13ed9f6857e1d72ddd Mon Sep 17 00:00:00 2001 From: Yuri Astrakhan Date: Wed, 4 Sep 2019 01:45:45 -0400 Subject: [PATCH 89/95] Minor cleanup - inline throwOnRelease() (#129) The throwOnRelease() function does not appear to be exposed anywhere, and it does not appear to make any sense to have it as a standalone func, as it ovecomplicates things and makes function call as non-returning. Inlined it. --- index.js | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/index.js b/index.js index e93cc96c..e517f76f 100644 --- a/index.js +++ b/index.js @@ -25,10 +25,6 @@ class PendingItem { } } -function throwOnRelease () { - throw new Error('Release called on client which has already been released to the pool.') -} - function promisify (Promise, callback) { if (callback) { return { callback: callback, result: undefined } @@ -248,7 +244,7 @@ class Pool extends EventEmitter { client.release = (err) => { if (released) { - throwOnRelease() + throw new Error('Release called on client which has already been released to the pool.') } released = true From c8c41c5b6557c8919d53a9cf7d33d4ca6287e1c9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 19 Nov 2019 10:02:08 -0600 Subject: [PATCH 90/95] Bump lodash from 4.17.11 to 4.17.13 (#136) Bumps [lodash](https://github.com/lodash/lodash) from 4.17.11 to 4.17.13. - [Release notes](https://github.com/lodash/lodash/releases) - [Commits](https://github.com/lodash/lodash/compare/4.17.11...4.17.13) Signed-off-by: dependabot[bot] --- package-lock.json | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/package-lock.json b/package-lock.json index 4099fbfd..08410f56 100644 --- a/package-lock.json +++ b/package-lock.json @@ -553,7 +553,7 @@ "dependencies": { "debug": { "version": "0.7.4", - "resolved": "http://registry.npmjs.org/debug/-/debug-0.7.4.tgz", + "resolved": "https://registry.npmjs.org/debug/-/debug-0.7.4.tgz", "integrity": "sha1-BuHqgILCyxTjmAbiLi9vdX+Srzk=", "dev": true }, @@ -619,7 +619,7 @@ "dependencies": { "babylon": { "version": "6.14.1", - "resolved": "http://registry.npmjs.org/babylon/-/babylon-6.14.1.tgz", + "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.14.1.tgz", "integrity": "sha1-lWJ1+rcnU62bNDXXr+WPi/CimBU=", "dev": true } @@ -1172,9 +1172,9 @@ } }, "lodash": { - "version": "4.17.11", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", - "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==", + "version": "4.17.13", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.13.tgz", + "integrity": "sha512-vm3/XWXfWtRua0FkUyEHBZy8kCPjErNBT9fJx8Zvs+U6zjqPbTUOpkaoum3O5uiA8sm+yNMHXfYkTUHFoMxFNA==", "dev": true }, "loose-envify": { @@ -1756,7 +1756,7 @@ }, "semver": { "version": "4.3.2", - "resolved": "http://registry.npmjs.org/semver/-/semver-4.3.2.tgz", + "resolved": "https://registry.npmjs.org/semver/-/semver-4.3.2.tgz", "integrity": "sha1-x6BxWKgL7dBSNVt3DYLWZA+AO+c=", "dev": true }, @@ -1920,7 +1920,7 @@ }, "strip-ansi": { "version": "3.0.1", - "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "dev": true, "requires": { From 236db3813d1602ed32a36b016326550bc3007c98 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20W=C3=BCrbach?= Date: Wed, 18 Dec 2019 17:08:30 +0100 Subject: [PATCH 91/95] Handle client errors in pool.query (#131) If an error not related to the query occurs, the client is emitting an error event. Forward this event to the callback. --- index.js | 18 ++++++++++++++++++ test/error-handling.js | 15 +++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/index.js b/index.js index e517f76f..1c7faf21 100644 --- a/index.js +++ b/index.js @@ -316,13 +316,31 @@ class Pool extends EventEmitter { } const response = promisify(this.Promise, cb) cb = response.callback + this.connect((err, client) => { if (err) { return cb(err) } + + let clientReleased = false + const onError = (err) => { + if (clientReleased) { + return + } + clientReleased = true + client.release(err) + cb(err) + } + + client.once('error', onError) this.log('dispatching query') client.query(text, values, (err, res) => { this.log('query dispatched') + client.removeListener('error', onError) + if (clientReleased) { + return + } + clientReleased = true client.release(err) if (err) { return cb(err) diff --git a/test/error-handling.js b/test/error-handling.js index 1e416683..9ef8848e 100644 --- a/test/error-handling.js +++ b/test/error-handling.js @@ -226,4 +226,19 @@ describe('pool error handling', function () { }) }) }) + + it('handles post-checkout client failures in pool.query', (done) => { + const pool = new Pool({ max: 1 }) + pool.on('error', () => { + // We double close the connection in this test, prevent exception caused by that + }) + pool.query('SELECT pg_sleep(5)', [], (err) => { + expect(err).to.be.an(Error) + done() + }) + + setTimeout(() => { + pool._clients[0].end() + }, 1000) + }) }) From 8f819a0e8d11522e96246635a9da4371b8b87ce8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 18 Dec 2019 10:08:42 -0600 Subject: [PATCH 92/95] Bump js-yaml from 3.12.0 to 3.13.1 (#137) Bumps [js-yaml](https://github.com/nodeca/js-yaml) from 3.12.0 to 3.13.1. - [Release notes](https://github.com/nodeca/js-yaml/releases) - [Changelog](https://github.com/nodeca/js-yaml/blob/master/CHANGELOG.md) - [Commits](https://github.com/nodeca/js-yaml/compare/3.12.0...3.13.1) Signed-off-by: dependabot[bot] --- package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 08410f56..c1251e74 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1125,9 +1125,9 @@ "dev": true }, "js-yaml": { - "version": "3.12.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.12.0.tgz", - "integrity": "sha512-PIt2cnwmPfL4hKNwqeiuz4bKfnzHTBv6HyVgjahA6mPLwPDzjDWrplJBMjHUFxku/N3FlmrbyPclad+I+4mJ3A==", + "version": "3.13.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", + "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", "dev": true, "requires": { "argparse": "^1.0.7", From 5edcfcb68d62ad2784f981e39de287ae06b5c069 Mon Sep 17 00:00:00 2001 From: "Brian M. Carlson" Date: Thu, 19 Dec 2019 08:13:17 -0600 Subject: [PATCH 93/95] Add yarn.lock file --- yarn.lock | 1688 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1688 insertions(+) create mode 100644 yarn.lock diff --git a/yarn.lock b/yarn.lock new file mode 100644 index 00000000..5bdbcbac --- /dev/null +++ b/yarn.lock @@ -0,0 +1,1688 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +abbrev@1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" + integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q== + +acorn-jsx@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-3.0.1.tgz#afdf9488fb1ecefc8348f6fb22f464e32a58b36b" + integrity sha1-r9+UiPsezvyDSPb7IvRk4ypYs2s= + dependencies: + acorn "^3.0.4" + +acorn-to-esprima@^2.0.6, acorn-to-esprima@^2.0.8: + version "2.0.8" + resolved "https://registry.yarnpkg.com/acorn-to-esprima/-/acorn-to-esprima-2.0.8.tgz#003f0c642eb92132f417d3708f14ada82adf2eb1" + integrity sha1-AD8MZC65ITL0F9NwjxStqCrfLrE= + +acorn@^3.0.4, acorn@^3.1.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-3.3.0.tgz#45e37fb39e8da3f25baee3ff5369e2bb5f22017a" + integrity sha1-ReN/s56No/JbruP/U2niu18iAXo= + +ajv-keywords@^1.0.0: + version "1.5.1" + resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-1.5.1.tgz#314dd0a4b3368fad3dfcdc54ede6171b886daf3c" + integrity sha1-MU3QpLM2j609/NxU7eYXG4htrzw= + +ajv@^4.7.0: + version "4.11.8" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-4.11.8.tgz#82ffb02b29e662ae53bdc20af15947706739c536" + integrity sha1-gv+wKynmYq5TvcIK8VlHcGc5xTY= + dependencies: + co "^4.6.0" + json-stable-stringify "^1.0.1" + +ansi-escapes@^1.1.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-1.4.0.tgz#d3a8a83b319aa67793662b13e761c7911422306e" + integrity sha1-06ioOzGapneTZisT52HHkRQiMG4= + +ansi-regex@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" + integrity sha1-w7M6te42DYbg5ijwRorn7yfWVN8= + +ansi-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" + integrity sha1-7QMXwyIGT3lGbAKWa922Bas32Zg= + +ansi-styles@^2.0.1, ansi-styles@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" + integrity sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4= + +argparse@^1.0.7: + version "1.0.10" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" + integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== + dependencies: + sprintf-js "~1.0.2" + +babel-code-frame@^6.26.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.26.0.tgz#63fd43f7dc1e3bb7ce35947db8fe369a3f58c74b" + integrity sha1-Y/1D99weO7fONZR9uP42mj9Yx0s= + dependencies: + chalk "^1.1.3" + esutils "^2.0.2" + js-tokens "^3.0.2" + +babel-messages@^6.23.0: + version "6.23.0" + resolved "https://registry.yarnpkg.com/babel-messages/-/babel-messages-6.23.0.tgz#f3cdf4703858035b2a2951c6ec5edf6c62f2630e" + integrity sha1-8830cDhYA1sqKVHG7F7fbGLyYw4= + dependencies: + babel-runtime "^6.22.0" + +babel-runtime@^6.22.0, babel-runtime@^6.26.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.26.0.tgz#965c7058668e82b55d7bfe04ff2337bc8b5647fe" + integrity sha1-llxwWGaOgrVde/4E/yM3vItWR/4= + dependencies: + core-js "^2.4.0" + regenerator-runtime "^0.11.0" + +babel-traverse@^6.4.5, babel-traverse@^6.9.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-traverse/-/babel-traverse-6.26.0.tgz#46a9cbd7edcc62c8e5c064e2d2d8d0f4035766ee" + integrity sha1-RqnL1+3MYsjlwGTi0tjQ9ANXZu4= + dependencies: + babel-code-frame "^6.26.0" + babel-messages "^6.23.0" + babel-runtime "^6.26.0" + babel-types "^6.26.0" + babylon "^6.18.0" + debug "^2.6.8" + globals "^9.18.0" + invariant "^2.2.2" + lodash "^4.17.4" + +babel-types@^6.26.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-types/-/babel-types-6.26.0.tgz#a3b073f94ab49eb6fa55cd65227a334380632497" + integrity sha1-o7Bz+Uq0nrb6Vc1lInozQ4BjJJc= + dependencies: + babel-runtime "^6.26.0" + esutils "^2.0.2" + lodash "^4.17.4" + to-fast-properties "^1.0.3" + +babylon@6.14.1: + version "6.14.1" + resolved "https://registry.yarnpkg.com/babylon/-/babylon-6.14.1.tgz#956275fab72753ad9b3435d7afe58f8bf0a29815" + integrity sha1-lWJ1+rcnU62bNDXXr+WPi/CimBU= + +babylon@^6.18.0, babylon@^6.8.0: + version "6.18.0" + resolved "https://registry.yarnpkg.com/babylon/-/babylon-6.18.0.tgz#af2f3b88fa6f5c1e4c634d1a0f8eac4f55b395e3" + integrity sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ== + +balanced-match@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" + integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c= + +bluebird@3.4.1: + version "3.4.1" + resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.4.1.tgz#b731ddf48e2dd3bedac2e75e1215a11bcb91fa07" + integrity sha1-tzHd9I4t077awudeEhWhG8uR+gc= + +bluebird@^3.0.5: + version "3.7.2" + resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f" + integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg== + +brace-expansion@^1.1.7: + version "1.1.11" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" + integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.1" + +browser-stdout@1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.1.tgz#baa559ee14ced73452229bad7326467c61fabd60" + integrity sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw== + +buffer-from@^1.0.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef" + integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A== + +buffer-writer@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/buffer-writer/-/buffer-writer-2.0.0.tgz#ce7eb81a38f7829db09c873f2fbb792c0c98ec04" + integrity sha512-a7ZpuTZU1TRtnwyCNW3I5dc0wWNC3VR9S++Ewyk2HHZdrO3CQJqSpd+95Us590V6AL7JqUAH2IwZ/398PmNFgw== + +caller-path@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/caller-path/-/caller-path-0.1.0.tgz#94085ef63581ecd3daa92444a8fe94e82577751f" + integrity sha1-lAhe9jWB7NPaqSREqP6U6CV3dR8= + dependencies: + callsites "^0.2.0" + +callsites@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/callsites/-/callsites-0.2.0.tgz#afab96262910a7f33c19a5775825c69f34e350ca" + integrity sha1-r6uWJikQp/M8GaV3WCXGnzTjUMo= + +chalk@^1.0.0, chalk@^1.1.1, chalk@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" + integrity sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg= + dependencies: + ansi-styles "^2.2.1" + escape-string-regexp "^1.0.2" + has-ansi "^2.0.0" + strip-ansi "^3.0.0" + supports-color "^2.0.0" + +circular-json@^0.3.1: + version "0.3.3" + resolved "https://registry.yarnpkg.com/circular-json/-/circular-json-0.3.3.tgz#815c99ea84f6809529d2f45791bdf82711352d66" + integrity sha512-UZK3NBx2Mca+b5LsG7bY183pHWt5Y1xts4P3Pz7ENTwGVnJOUWbRb3ocjvX7hx9tq/yTAdclXm9sZ38gNuem4A== + +cli-cursor@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-1.0.2.tgz#64da3f7d56a54412e59794bd62dc35295e8f2987" + integrity sha1-ZNo/fValRBLll5S9Ytw1KV6PKYc= + dependencies: + restore-cursor "^1.0.1" + +cli-width@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.2.0.tgz#ff19ede8a9a5e579324147b0c11f0fbcbabed639" + integrity sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk= + +clone@^1.0.2: + version "1.0.4" + resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e" + integrity sha1-2jCcwmPfFZlMaIypAheco8fNfH4= + +co@4.6.0, co@^4.6.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" + integrity sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ= + +code-point-at@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" + integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c= + +commander@2.15.1: + version "2.15.1" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.15.1.tgz#df46e867d0fc2aec66a34662b406a9ccafff5b0f" + integrity sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag== + +commander@^2.2.0, commander@^2.9.0: + version "2.20.3" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" + integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== + +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= + +concat-stream@^1.4.6, concat-stream@^1.4.7: + version "1.6.2" + resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34" + integrity sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw== + dependencies: + buffer-from "^1.0.0" + inherits "^2.0.3" + readable-stream "^2.2.2" + typedarray "^0.0.6" + +config-chain@~1.1.5: + version "1.1.12" + resolved "https://registry.yarnpkg.com/config-chain/-/config-chain-1.1.12.tgz#0fde8d091200eb5e808caf25fe618c02f48e4efa" + integrity sha512-a1eOIcu8+7lUInge4Rpf/n4Krkf3Dd9lqhljRzII1/Zno/kRtUWnznPO3jOKBmTEktkt3fkxisUcivoj0ebzoA== + dependencies: + ini "^1.3.4" + proto-list "~1.2.1" + +core-js@^2.4.0: + version "2.6.11" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.11.tgz#38831469f9922bded8ee21c9dc46985e0399308c" + integrity sha512-5wjnpaT/3dV+XB4borEsnAYQchn00XSgTAWKDkEqv+K8KevjbzmofK6hfJ9TZIlpj2N0xQpazy7PiRQiWHqzWg== + +core-util-is@~1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" + integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= + +d@1, d@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/d/-/d-1.0.1.tgz#8698095372d58dbee346ffd0c7093f99f8f9eb5a" + integrity sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA== + dependencies: + es5-ext "^0.10.50" + type "^1.0.1" + +debug-log@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/debug-log/-/debug-log-1.0.1.tgz#2307632d4c04382b8df8a32f70b895046d52745f" + integrity sha1-IwdjLUwEOCuN+KMvcLiVBG1SdF8= + +debug@3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261" + integrity sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g== + dependencies: + ms "2.0.0" + +debug@^0.7.4: + version "0.7.4" + resolved "https://registry.yarnpkg.com/debug/-/debug-0.7.4.tgz#06e1ea8082c2cb14e39806e22e2f6f757f92af39" + integrity sha1-BuHqgILCyxTjmAbiLi9vdX+Srzk= + +debug@^2.1.1, debug@^2.1.3, debug@^2.6.8: + version "2.6.9" + resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" + integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== + dependencies: + ms "2.0.0" + +deep-is@~0.1.3: + version "0.1.3" + resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" + integrity sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ= + +defaults@^1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/defaults/-/defaults-1.0.3.tgz#c656051e9817d9ff08ed881477f3fe4019f3ef7d" + integrity sha1-xlYFHpgX2f8I7YgUd/P+QBnz730= + dependencies: + clone "^1.0.2" + +deglob@^1.0.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/deglob/-/deglob-1.1.2.tgz#76d577c25fe3f7329412a2b59eadea57ac500e3f" + integrity sha1-dtV3wl/j9zKUEqK1nq3qV6xQDj8= + dependencies: + find-root "^1.0.0" + glob "^7.0.5" + ignore "^3.0.9" + pkg-config "^1.1.0" + run-parallel "^1.1.2" + uniq "^1.0.1" + xtend "^4.0.0" + +diff@3.5.0: + version "3.5.0" + resolved "https://registry.yarnpkg.com/diff/-/diff-3.5.0.tgz#800c0dd1e0a8bfbc95835c202ad220fe317e5a12" + integrity sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA== + +diff@^1.3.2: + version "1.4.0" + resolved "https://registry.yarnpkg.com/diff/-/diff-1.4.0.tgz#7f28d2eb9ee7b15a97efd89ce63dcfdaa3ccbabf" + integrity sha1-fyjS657nsVqX79ic5j3P2qPMur8= + +disparity@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/disparity/-/disparity-2.0.0.tgz#57ddacb47324ae5f58d2cc0da886db4ce9eeb718" + integrity sha1-V92stHMkrl9Y0swNqIbbTOnutxg= + dependencies: + ansi-styles "^2.0.1" + diff "^1.3.2" + +doctrine@^1.2.1, doctrine@^1.2.2: + version "1.5.0" + resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-1.5.0.tgz#379dce730f6166f76cefa4e6707a159b02c5a6fa" + integrity sha1-N53Ocw9hZvds76TmcHoVmwLFpvo= + dependencies: + esutils "^2.0.2" + isarray "^1.0.0" + +editorconfig@^0.13.2: + version "0.13.3" + resolved "https://registry.yarnpkg.com/editorconfig/-/editorconfig-0.13.3.tgz#e5219e587951d60958fd94ea9a9a008cdeff1b34" + integrity sha512-WkjsUNVCu+ITKDj73QDvi0trvpdDWdkDyHybDGSXPfekLCqwmpD7CP7iPbvBgosNuLcI96XTDwNa75JyFl7tEQ== + dependencies: + bluebird "^3.0.5" + commander "^2.9.0" + lru-cache "^3.2.0" + semver "^5.1.0" + sigmund "^1.0.1" + +es5-ext@^0.10.35, es5-ext@^0.10.46, es5-ext@^0.10.50, es5-ext@~0.10.14: + version "0.10.53" + resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.53.tgz#93c5a3acfdbef275220ad72644ad02ee18368de1" + integrity sha512-Xs2Stw6NiNHWypzRTY1MtaG/uJlwCk8kH81920ma8mvN8Xq1gsfhZvpkImLQArw8AHnv8MT2I45J3c0R8slE+Q== + dependencies: + es6-iterator "~2.0.3" + es6-symbol "~3.1.3" + next-tick "~1.0.0" + +es6-iterator@^2.0.3, es6-iterator@~2.0.1, es6-iterator@~2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/es6-iterator/-/es6-iterator-2.0.3.tgz#a7de889141a05a94b0854403b2d0a0fbfa98f3b7" + integrity sha1-p96IkUGgWpSwhUQDstCg+/qY87c= + dependencies: + d "1" + es5-ext "^0.10.35" + es6-symbol "^3.1.1" + +es6-map@^0.1.3: + version "0.1.5" + resolved "https://registry.yarnpkg.com/es6-map/-/es6-map-0.1.5.tgz#9136e0503dcc06a301690f0bb14ff4e364e949f0" + integrity sha1-kTbgUD3MBqMBaQ8LsU/042TpSfA= + dependencies: + d "1" + es5-ext "~0.10.14" + es6-iterator "~2.0.1" + es6-set "~0.1.5" + es6-symbol "~3.1.1" + event-emitter "~0.3.5" + +es6-set@~0.1.5: + version "0.1.5" + resolved "https://registry.yarnpkg.com/es6-set/-/es6-set-0.1.5.tgz#d2b3ec5d4d800ced818db538d28974db0a73ccb1" + integrity sha1-0rPsXU2ADO2BjbU40ol02wpzzLE= + dependencies: + d "1" + es5-ext "~0.10.14" + es6-iterator "~2.0.1" + es6-symbol "3.1.1" + event-emitter "~0.3.5" + +es6-symbol@3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/es6-symbol/-/es6-symbol-3.1.1.tgz#bf00ef4fdab6ba1b46ecb7b629b4c7ed5715cc77" + integrity sha1-vwDvT9q2uhtG7Le2KbTH7VcVzHc= + dependencies: + d "1" + es5-ext "~0.10.14" + +es6-symbol@^3.1.1, es6-symbol@~3.1.1, es6-symbol@~3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/es6-symbol/-/es6-symbol-3.1.3.tgz#bad5d3c1bcdac28269f4cb331e431c78ac705d18" + integrity sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA== + dependencies: + d "^1.0.1" + ext "^1.1.2" + +es6-weak-map@^2.0.1: + version "2.0.3" + resolved "https://registry.yarnpkg.com/es6-weak-map/-/es6-weak-map-2.0.3.tgz#b6da1f16cc2cc0d9be43e6bdbfc5e7dfcdf31d53" + integrity sha512-p5um32HOTO1kP+w7PRnB+5lQ43Z6muuMuIMffvDN8ZB4GcnjLBV6zGStpbASIMk4DCAvEaamhe2zhyCb/QXXsA== + dependencies: + d "1" + es5-ext "^0.10.46" + es6-iterator "^2.0.3" + es6-symbol "^3.1.1" + +escape-string-regexp@1.0.5, escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" + integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= + +escope@^3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/escope/-/escope-3.6.0.tgz#e01975e812781a163a6dadfdd80398dc64c889c3" + integrity sha1-4Bl16BJ4GhY6ba392AOY3GTIicM= + dependencies: + es6-map "^0.1.3" + es6-weak-map "^2.0.1" + esrecurse "^4.1.0" + estraverse "^4.1.1" + +esformatter-eol-last@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/esformatter-eol-last/-/esformatter-eol-last-1.0.0.tgz#45a78ff4622b1d49e44f56b49905766a63290c07" + integrity sha1-RaeP9GIrHUnkT1a0mQV2amMpDAc= + dependencies: + string.prototype.endswith "^0.2.0" + +esformatter-ignore@^0.1.3: + version "0.1.3" + resolved "https://registry.yarnpkg.com/esformatter-ignore/-/esformatter-ignore-0.1.3.tgz#04d3b875bfa49dde004cc58df6f6bbc3c0567f1e" + integrity sha1-BNO4db+knd4ATMWN9va7w8BWfx4= + +esformatter-jsx@^7.0.0: + version "7.4.1" + resolved "https://registry.yarnpkg.com/esformatter-jsx/-/esformatter-jsx-7.4.1.tgz#b2209ae0908f413a747b1205727cbf4ba4249602" + integrity sha1-siCa4JCPQTp0exIFcny/S6QklgI= + dependencies: + babylon "6.14.1" + esformatter-ignore "^0.1.3" + extend "3.0.0" + js-beautify "1.6.4" + +esformatter-literal-notation@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/esformatter-literal-notation/-/esformatter-literal-notation-1.0.1.tgz#710e7b420175fe3f7e5afad5bbad329103842e2f" + integrity sha1-cQ57QgF1/j9+WvrVu60ykQOELi8= + dependencies: + rocambole "^0.3.6" + rocambole-token "^1.2.1" + +esformatter-parser@^1.0, esformatter-parser@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/esformatter-parser/-/esformatter-parser-1.0.0.tgz#0854072d0487539ed39cae38d8a5432c17ec11d3" + integrity sha1-CFQHLQSHU57TnK442KVDLBfsEdM= + dependencies: + acorn-to-esprima "^2.0.8" + babel-traverse "^6.9.0" + babylon "^6.8.0" + rocambole "^0.7.0" + +esformatter-quotes@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/esformatter-quotes/-/esformatter-quotes-1.1.0.tgz#e22c6c445c7f306041d81c9b9e51fca6cbfaca82" + integrity sha1-4ixsRFx/MGBB2BybnlH8psv6yoI= + +esformatter-remove-trailing-commas@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/esformatter-remove-trailing-commas/-/esformatter-remove-trailing-commas-1.0.1.tgz#9397624c1faa980fc4ecc7e5e9813eb4f2b582a7" + integrity sha1-k5diTB+qmA/E7Mfl6YE+tPK1gqc= + dependencies: + rocambole-token "^1.2.1" + +esformatter-semicolon-first@^1.1.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/esformatter-semicolon-first/-/esformatter-semicolon-first-1.2.0.tgz#e3b512d1d4e07310eabcabf57277ea7c8a56e242" + integrity sha1-47US0dTgcxDqvKv1cnfqfIpW4kI= + dependencies: + esformatter-parser "^1.0" + rocambole ">=0.6.0 <2.0" + rocambole-linebreak "^1.0.2" + rocambole-token "^1.2.1" + +esformatter-spaced-lined-comment@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/esformatter-spaced-lined-comment/-/esformatter-spaced-lined-comment-2.0.1.tgz#dc5f3407f93c295e1e56446bd344560da5e6dcac" + integrity sha1-3F80B/k8KV4eVkRr00RWDaXm3Kw= + +esformatter@^0.9.0: + version "0.9.6" + resolved "https://registry.yarnpkg.com/esformatter/-/esformatter-0.9.6.tgz#3608aec7828deee3cd3f46e1192aeb47268a957f" + integrity sha1-Ngiux4KN7uPNP0bhGSrrRyaKlX8= + dependencies: + acorn-to-esprima "^2.0.6" + babel-traverse "^6.4.5" + debug "^0.7.4" + disparity "^2.0.0" + esformatter-parser "^1.0.0" + glob "^5.0.3" + minimist "^1.1.1" + mout ">=0.9 <2.0" + npm-run "^2.0.0" + resolve "^1.1.5" + rocambole ">=0.7 <2.0" + rocambole-indent "^2.0.4" + rocambole-linebreak "^1.0.2" + rocambole-node "~1.0" + rocambole-token "^1.1.2" + rocambole-whitespace "^1.0.0" + stdin "*" + strip-json-comments "~0.1.1" + supports-color "^1.3.1" + user-home "^2.0.0" + +eslint-config-standard-jsx@1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/eslint-config-standard-jsx/-/eslint-config-standard-jsx-1.2.1.tgz#0d19b1705f0ad48363ef2a8bbfa71df012d989b3" + integrity sha1-DRmxcF8K1INj7yqLv6cd8BLZibM= + +eslint-config-standard@5.3.1: + version "5.3.1" + resolved "https://registry.yarnpkg.com/eslint-config-standard/-/eslint-config-standard-5.3.1.tgz#591c969151744132f561d3b915a812ea413fe490" + integrity sha1-WRyWkVF0QTL1YdO5FagS6kE/5JA= + +eslint-plugin-promise@^1.0.8: + version "1.3.2" + resolved "https://registry.yarnpkg.com/eslint-plugin-promise/-/eslint-plugin-promise-1.3.2.tgz#fce332d6f5ff523200a537704863ec3c2422ba7c" + integrity sha1-/OMy1vX/UjIApTdwSGPsPCQiunw= + +eslint-plugin-react@^5.0.1: + version "5.2.2" + resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-5.2.2.tgz#7db068e1f5487f6871e4deef36a381c303eac161" + integrity sha1-fbBo4fVIf2hx5N7vNqOBwwPqwWE= + dependencies: + doctrine "^1.2.2" + jsx-ast-utils "^1.2.1" + +eslint-plugin-standard@^1.3.1: + version "1.3.3" + resolved "https://registry.yarnpkg.com/eslint-plugin-standard/-/eslint-plugin-standard-1.3.3.tgz#a3085451523431e76f409c70cb8f94e32bf0ec7f" + integrity sha1-owhUUVI0MedvQJxwy4+U4yvw7H8= + +eslint@~2.10.2: + version "2.10.2" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-2.10.2.tgz#b2309482fef043d3203365a321285e6cce01c3d7" + integrity sha1-sjCUgv7wQ9MgM2WjIShebM4Bw9c= + dependencies: + chalk "^1.1.3" + concat-stream "^1.4.6" + debug "^2.1.1" + doctrine "^1.2.1" + es6-map "^0.1.3" + escope "^3.6.0" + espree "3.1.4" + estraverse "^4.2.0" + esutils "^2.0.2" + file-entry-cache "^1.1.1" + glob "^7.0.3" + globals "^9.2.0" + ignore "^3.1.2" + imurmurhash "^0.1.4" + inquirer "^0.12.0" + is-my-json-valid "^2.10.0" + is-resolvable "^1.0.0" + js-yaml "^3.5.1" + json-stable-stringify "^1.0.0" + lodash "^4.0.0" + mkdirp "^0.5.0" + optionator "^0.8.1" + path-is-absolute "^1.0.0" + path-is-inside "^1.0.1" + pluralize "^1.2.1" + progress "^1.1.8" + require-uncached "^1.0.2" + shelljs "^0.6.0" + strip-json-comments "~1.0.1" + table "^3.7.8" + text-table "~0.2.0" + user-home "^2.0.0" + +espree@3.1.4: + version "3.1.4" + resolved "https://registry.yarnpkg.com/espree/-/espree-3.1.4.tgz#0726d7ac83af97a7c8498da9b363a3609d2a68a1" + integrity sha1-BybXrIOvl6fISY2ps2OjYJ0qaKE= + dependencies: + acorn "^3.1.0" + acorn-jsx "^3.0.0" + +esprima@^2.1: + version "2.7.3" + resolved "https://registry.yarnpkg.com/esprima/-/esprima-2.7.3.tgz#96e3b70d5779f6ad49cd032673d1c312767ba581" + integrity sha1-luO3DVd59q1JzQMmc9HDEnZ7pYE= + +esprima@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" + integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== + +esprima@~1.0: + version "1.0.4" + resolved "https://registry.yarnpkg.com/esprima/-/esprima-1.0.4.tgz#9f557e08fc3b4d26ece9dd34f8fbf476b62585ad" + integrity sha1-n1V+CPw7TSbs6d00+Pv0drYlha0= + +esrecurse@^4.1.0: + version "4.2.1" + resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.2.1.tgz#007a3b9fdbc2b3bb87e4879ea19c92fdbd3942cf" + integrity sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ== + dependencies: + estraverse "^4.1.0" + +estraverse@^4.1.0, estraverse@^4.1.1, estraverse@^4.2.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" + integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== + +esutils@^2.0.2: + version "2.0.3" + resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" + integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== + +event-emitter@~0.3.5: + version "0.3.5" + resolved "https://registry.yarnpkg.com/event-emitter/-/event-emitter-0.3.5.tgz#df8c69eef1647923c7157b9ce83840610b02cc39" + integrity sha1-34xp7vFkeSPHFXuc6DhAYQsCzDk= + dependencies: + d "1" + es5-ext "~0.10.14" + +exit-hook@^1.0.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/exit-hook/-/exit-hook-1.1.1.tgz#f05ca233b48c05d54fff07765df8507e95c02ff8" + integrity sha1-8FyiM7SMBdVP/wd2XfhQfpXAL/g= + +expect.js@0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/expect.js/-/expect.js-0.3.1.tgz#b0a59a0d2eff5437544ebf0ceaa6015841d09b5b" + integrity sha1-sKWaDS7/VDdUTr8M6qYBWEHQm1s= + +ext@^1.1.2: + version "1.4.0" + resolved "https://registry.yarnpkg.com/ext/-/ext-1.4.0.tgz#89ae7a07158f79d35517882904324077e4379244" + integrity sha512-Key5NIsUxdqKg3vIsdw9dSuXpPCQ297y6wBjL30edxwPgt2E44WcWBZey/ZvUc6sERLTxKdyCu4gZFmUbk1Q7A== + dependencies: + type "^2.0.0" + +extend@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.0.tgz#5a474353b9f3353ddd8176dfd37b91c83a46f1d4" + integrity sha1-WkdDU7nzNT3dgXbf03uRyDpG8dQ= + +fast-levenshtein@~2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" + integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc= + +figures@^1.3.5: + version "1.7.0" + resolved "https://registry.yarnpkg.com/figures/-/figures-1.7.0.tgz#cbe1e3affcf1cd44b80cadfed28dc793a9701d2e" + integrity sha1-y+Hjr/zxzUS4DK3+0o3Hk6lwHS4= + dependencies: + escape-string-regexp "^1.0.5" + object-assign "^4.1.0" + +file-entry-cache@^1.1.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-1.3.1.tgz#44c61ea607ae4be9c1402f41f44270cbfe334ff8" + integrity sha1-RMYepgeuS+nBQC9B9EJwy/4zT/g= + dependencies: + flat-cache "^1.2.1" + object-assign "^4.0.1" + +find-root@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/find-root/-/find-root-1.1.0.tgz#abcfc8ba76f708c42a97b3d685b7e9450bfb9ce4" + integrity sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng== + +flat-cache@^1.2.1: + version "1.3.4" + resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-1.3.4.tgz#2c2ef77525cc2929007dfffa1dd314aa9c9dee6f" + integrity sha512-VwyB3Lkgacfik2vhqR4uv2rvebqmDvFu4jlN/C1RzWoJEo8I7z4Q404oiqYCkq41mni8EzQnm95emU9seckwtg== + dependencies: + circular-json "^0.3.1" + graceful-fs "^4.1.2" + rimraf "~2.6.2" + write "^0.2.1" + +fs.realpath@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= + +generate-function@^2.0.0: + version "2.3.1" + resolved "https://registry.yarnpkg.com/generate-function/-/generate-function-2.3.1.tgz#f069617690c10c868e73b8465746764f97c3479f" + integrity sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ== + dependencies: + is-property "^1.0.2" + +generate-object-property@^1.1.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/generate-object-property/-/generate-object-property-1.2.0.tgz#9c0e1c40308ce804f4783618b937fa88f99d50d0" + integrity sha1-nA4cQDCM6AT0eDYYuTf6iPmdUNA= + dependencies: + is-property "^1.0.0" + +get-stdin@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-4.0.1.tgz#b968c6b0a04384324902e8bf1a5df32579a450fe" + integrity sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4= + +get-stdin@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-5.0.1.tgz#122e161591e21ff4c52530305693f20e6393a398" + integrity sha1-Ei4WFZHiH/TFJTAwVpPyDmOTo5g= + +glob@7.1.2: + version "7.1.2" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.2.tgz#c19c9df9a028702d678612384a6552404c636d15" + integrity sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.4" + once "^1.3.0" + path-is-absolute "^1.0.0" + +glob@^5.0.3: + version "5.0.15" + resolved "https://registry.yarnpkg.com/glob/-/glob-5.0.15.tgz#1bc936b9e02f4a603fcc222ecf7633d30b8b93b1" + integrity sha1-G8k2ueAvSmA/zCIuz3Yz0wuLk7E= + dependencies: + inflight "^1.0.4" + inherits "2" + minimatch "2 || 3" + once "^1.3.0" + path-is-absolute "^1.0.0" + +glob@^7.0.3, glob@^7.0.5, glob@^7.1.3: + version "7.1.6" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6" + integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.4" + once "^1.3.0" + path-is-absolute "^1.0.0" + +globals@^9.18.0, globals@^9.2.0: + version "9.18.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-9.18.0.tgz#aa3896b3e69b487f17e31ed2143d69a8e30c2d8a" + integrity sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ== + +graceful-fs@^4.1.2: + version "4.2.3" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.3.tgz#4a12ff1b60376ef09862c2093edd908328be8423" + integrity sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ== + +growl@1.10.5: + version "1.10.5" + resolved "https://registry.yarnpkg.com/growl/-/growl-1.10.5.tgz#f2735dc2283674fa67478b10181059355c369e5e" + integrity sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA== + +has-ansi@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91" + integrity sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE= + dependencies: + ansi-regex "^2.0.0" + +has-flag@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" + integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0= + +he@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/he/-/he-1.1.1.tgz#93410fd21b009735151f8868c2f271f3427e23fd" + integrity sha1-k0EP0hsAlzUVH4howvJx80J+I/0= + +ignore@^3.0.9, ignore@^3.1.2: + version "3.3.10" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-3.3.10.tgz#0a97fb876986e8081c631160f8f9f389157f0043" + integrity sha512-Pgs951kaMm5GXP7MOvxERINe3gsaVjUWFm+UZPSq9xYriQAksyhg0csnS0KXSNRD5NmNdapXEpjxG49+AKh/ug== + +imurmurhash@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" + integrity sha1-khi5srkoojixPcT7a21XbyMUU+o= + +inflight@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= + dependencies: + once "^1.3.0" + wrappy "1" + +inherits@2, inherits@^2.0.3, inherits@~2.0.3: + version "2.0.4" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== + +ini@^1.3.4: + version "1.3.5" + resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927" + integrity sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw== + +inquirer@^0.12.0: + version "0.12.0" + resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-0.12.0.tgz#1ef2bfd63504df0bc75785fff8c2c41df12f077e" + integrity sha1-HvK/1jUE3wvHV4X/+MLEHfEvB34= + dependencies: + ansi-escapes "^1.1.0" + ansi-regex "^2.0.0" + chalk "^1.0.0" + cli-cursor "^1.0.1" + cli-width "^2.0.0" + figures "^1.3.5" + lodash "^4.3.0" + readline2 "^1.0.1" + run-async "^0.1.0" + rx-lite "^3.1.2" + string-width "^1.0.1" + strip-ansi "^3.0.0" + through "^2.3.6" + +invariant@^2.2.2: + version "2.2.4" + resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6" + integrity sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA== + dependencies: + loose-envify "^1.0.0" + +is-fullwidth-code-point@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb" + integrity sha1-754xOG8DGn8NZDr4L95QxFfvAMs= + dependencies: + number-is-nan "^1.0.0" + +is-fullwidth-code-point@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" + integrity sha1-o7MKXE8ZkYMWeqq5O+764937ZU8= + +is-my-ip-valid@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-my-ip-valid/-/is-my-ip-valid-1.0.0.tgz#7b351b8e8edd4d3995d4d066680e664d94696824" + integrity sha512-gmh/eWXROncUzRnIa1Ubrt5b8ep/MGSnfAUI3aRp+sqTCs1tv1Isl8d8F6JmkN3dXKc3ehZMrtiPN9eL03NuaQ== + +is-my-json-valid@^2.10.0: + version "2.20.0" + resolved "https://registry.yarnpkg.com/is-my-json-valid/-/is-my-json-valid-2.20.0.tgz#1345a6fca3e8daefc10d0fa77067f54cedafd59a" + integrity sha512-XTHBZSIIxNsIsZXg7XB5l8z/OBFosl1Wao4tXLpeC7eKU4Vm/kdop2azkPqULwnfGQjmeDIyey9g7afMMtdWAA== + dependencies: + generate-function "^2.0.0" + generate-object-property "^1.1.0" + is-my-ip-valid "^1.0.0" + jsonpointer "^4.0.0" + xtend "^4.0.0" + +is-property@^1.0.0, is-property@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-property/-/is-property-1.0.2.tgz#57fe1c4e48474edd65b09911f26b1cd4095dda84" + integrity sha1-V/4cTkhHTt1lsJkR8msc1Ald2oQ= + +is-resolvable@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-resolvable/-/is-resolvable-1.1.0.tgz#fb18f87ce1feb925169c9a407c19318a3206ed88" + integrity sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg== + +isarray@^1.0.0, isarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" + integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= + +isexe@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= + +js-beautify@1.6.4: + version "1.6.4" + resolved "https://registry.yarnpkg.com/js-beautify/-/js-beautify-1.6.4.tgz#a9af79699742ac9a1b6fddc1fdbc78bc4d515fc3" + integrity sha1-qa95aZdCrJobb93B/bx4vE1RX8M= + dependencies: + config-chain "~1.1.5" + editorconfig "^0.13.2" + mkdirp "~0.5.0" + nopt "~3.0.1" + +"js-tokens@^3.0.0 || ^4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" + integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== + +js-tokens@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b" + integrity sha1-mGbfOVECEw449/mWvOtlRDIJwls= + +js-yaml@^3.5.1: + version "3.13.1" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.13.1.tgz#aff151b30bfdfa8e49e05da22e7415e9dfa37847" + integrity sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw== + dependencies: + argparse "^1.0.7" + esprima "^4.0.0" + +json-stable-stringify@^1.0.0, json-stable-stringify@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz#9a759d39c5f2ff503fd5300646ed445f88c4f9af" + integrity sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8= + dependencies: + jsonify "~0.0.0" + +jsonify@~0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.0.tgz#2c74b6ee41d93ca51b7b5aaee8f503631d252a73" + integrity sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM= + +jsonpointer@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/jsonpointer/-/jsonpointer-4.0.1.tgz#4fd92cb34e0e9db3c89c8622ecf51f9b978c6cb9" + integrity sha1-T9kss04OnbPInIYi7PUfm5eMbLk= + +jsx-ast-utils@^1.2.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-1.4.1.tgz#3867213e8dd79bf1e8f2300c0cfc1efb182c0df1" + integrity sha1-OGchPo3Xm/Ho8jAMDPwe+xgsDfE= + +levn@~0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" + integrity sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4= + dependencies: + prelude-ls "~1.1.2" + type-check "~0.3.2" + +lodash@^4.0.0, lodash@^4.17.11, lodash@^4.17.4, lodash@^4.3.0: + version "4.17.15" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548" + integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A== + +loose-envify@^1.0.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" + integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q== + dependencies: + js-tokens "^3.0.0 || ^4.0.0" + +lru-cache@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-3.2.0.tgz#71789b3b7f5399bec8565dda38aa30d2a097efee" + integrity sha1-cXibO39Tmb7IVl3aOKow0qCX7+4= + dependencies: + pseudomap "^1.0.1" + +"minimatch@2 || 3", minimatch@3.0.4, minimatch@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" + integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== + dependencies: + brace-expansion "^1.1.7" + +minimist@0.0.8: + version "0.0.8" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" + integrity sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0= + +minimist@^1.1.0, minimist@^1.1.1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" + integrity sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ= + +mkdirp@0.5.1, mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.0: + version "0.5.1" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" + integrity sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM= + dependencies: + minimist "0.0.8" + +mocha@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/mocha/-/mocha-5.2.0.tgz#6d8ae508f59167f940f2b5b3c4a612ae50c90ae6" + integrity sha512-2IUgKDhc3J7Uug+FxMXuqIyYzH7gJjXECKe/w43IGgQHTSj3InJi+yAA7T24L9bQMRKiUEHxEX37G5JpVUGLcQ== + dependencies: + browser-stdout "1.3.1" + commander "2.15.1" + debug "3.1.0" + diff "3.5.0" + escape-string-regexp "1.0.5" + glob "7.1.2" + growl "1.10.5" + he "1.1.1" + minimatch "3.0.4" + mkdirp "0.5.1" + supports-color "5.4.0" + +"mout@>=0.9 <2.0": + version "1.2.2" + resolved "https://registry.yarnpkg.com/mout/-/mout-1.2.2.tgz#c9b718a499806a0632cede178e80f436259e777d" + integrity sha512-w0OUxFEla6z3d7sVpMZGBCpQvYh8PHS1wZ6Wu9GNKHMpAHWJ0if0LsQZh3DlOqw55HlhJEOMLpFnwtxp99Y5GA== + +mout@^0.11.0: + version "0.11.1" + resolved "https://registry.yarnpkg.com/mout/-/mout-0.11.1.tgz#ba3611df5f0e5b1ffbfd01166b8f02d1f5fa2b99" + integrity sha1-ujYR318OWx/7/QEWa48C0fX6K5k= + +ms@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" + integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= + +multiline@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/multiline/-/multiline-1.0.2.tgz#69b1f25ff074d2828904f244ddd06b7d96ef6c93" + integrity sha1-abHyX/B00oKJBPJE3dBrfZbvbJM= + dependencies: + strip-indent "^1.0.0" + +mute-stream@0.0.5: + version "0.0.5" + resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.5.tgz#8fbfabb0a98a253d3184331f9e8deb7372fac6c0" + integrity sha1-j7+rsKmKJT0xhDMfno3rc3L6xsA= + +next-tick@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-1.0.0.tgz#ca86d1fe8828169b0120208e3dc8424b9db8342c" + integrity sha1-yobR/ogoFpsBICCOPchCS524NCw= + +nopt@~3.0.1: + version "3.0.6" + resolved "https://registry.yarnpkg.com/nopt/-/nopt-3.0.6.tgz#c6465dbf08abcd4db359317f79ac68a646b28ff9" + integrity sha1-xkZdvwirzU2zWTF/eaxopkayj/k= + dependencies: + abbrev "1" + +npm-path@^1.0.0, npm-path@^1.0.1: + version "1.1.0" + resolved "https://registry.yarnpkg.com/npm-path/-/npm-path-1.1.0.tgz#0474ae00419c327d54701b7cf2cd05dc88be1140" + integrity sha1-BHSuAEGcMn1UcBt88s0F3Ii+EUA= + dependencies: + which "^1.2.4" + +npm-run@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/npm-run/-/npm-run-2.0.0.tgz#28dfc0ad5e2e46fe0848e2bd58ddf002e7b73c15" + integrity sha1-KN/ArV4uRv4ISOK9WN3wAue3PBU= + dependencies: + minimist "^1.1.1" + npm-path "^1.0.1" + npm-which "^2.0.0" + serializerr "^1.0.1" + spawn-sync "^1.0.5" + sync-exec "^0.5.0" + +npm-which@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/npm-which/-/npm-which-2.0.0.tgz#0c46982160b783093661d1d01bd4496d2feabbac" + integrity sha1-DEaYIWC3gwk2YdHQG9RJbS/qu6w= + dependencies: + commander "^2.2.0" + npm-path "^1.0.0" + which "^1.0.5" + +number-is-nan@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" + integrity sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0= + +object-assign@^4.0.1, object-assign@^4.1.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" + integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= + +once@^1.3.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= + dependencies: + wrappy "1" + +onetime@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/onetime/-/onetime-1.1.0.tgz#a1f7838f8314c516f05ecefcbc4ccfe04b4ed789" + integrity sha1-ofeDj4MUxRbwXs78vEzP4EtO14k= + +optionator@^0.8.1: + version "0.8.3" + resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.3.tgz#84fa1d036fe9d3c7e21d99884b601167ec8fb495" + integrity sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA== + dependencies: + deep-is "~0.1.3" + fast-levenshtein "~2.0.6" + levn "~0.3.0" + prelude-ls "~1.1.2" + type-check "~0.3.2" + word-wrap "~1.2.3" + +os-homedir@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3" + integrity sha1-/7xJiDNuDoM94MFox+8VISGqf7M= + +os-shim@^0.1.2: + version "0.1.3" + resolved "https://registry.yarnpkg.com/os-shim/-/os-shim-0.1.3.tgz#6b62c3791cf7909ea35ed46e17658bb417cb3917" + integrity sha1-a2LDeRz3kJ6jXtRuF2WLtBfLORc= + +packet-reader@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/packet-reader/-/packet-reader-1.0.0.tgz#9238e5480dedabacfe1fe3f2771063f164157d74" + integrity sha512-HAKu/fG3HpHFO0AA8WE8q2g+gBJaZ9MG7fcKk+IJPLTGAD6Psw4443l+9DGRbOIh3/aXr7Phy0TjilYivJo5XQ== + +path-is-absolute@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= + +path-is-inside@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53" + integrity sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM= + +path-parse@^1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c" + integrity sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw== + +pg-connection-string@0.1.3: + version "0.1.3" + resolved "https://registry.yarnpkg.com/pg-connection-string/-/pg-connection-string-0.1.3.tgz#da1847b20940e42ee1492beaf65d49d91b245df7" + integrity sha1-2hhHsglA5C7hSSvq9l1J2RskXfc= + +pg-cursor@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/pg-cursor/-/pg-cursor-1.3.0.tgz#b220f1908976b7b40daa373c7ada5fca823ab0d9" + integrity sha1-siDxkIl2t7QNqjc8etpfyoI6sNk= + +pg-int8@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/pg-int8/-/pg-int8-1.0.1.tgz#943bd463bf5b71b4170115f80f8efc9a0c0eb78c" + integrity sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw== + +pg-pool@^2.0.7: + version "2.0.7" + resolved "https://registry.yarnpkg.com/pg-pool/-/pg-pool-2.0.7.tgz#f14ecab83507941062c313df23f6adcd9fd0ce54" + integrity sha512-UiJyO5B9zZpu32GSlP0tXy8J2NsJ9EFGFfz5v6PSbdz/1hBLX1rNiiy5+mAm5iJJYwfCv4A0EBcQLGWwjbpzZw== + +pg-types@^2.1.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/pg-types/-/pg-types-2.2.0.tgz#2d0250d636454f7cfa3b6ae0382fdfa8063254a3" + integrity sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA== + dependencies: + pg-int8 "1.0.1" + postgres-array "~2.0.0" + postgres-bytea "~1.0.0" + postgres-date "~1.0.4" + postgres-interval "^1.1.0" + +pg@*: + version "7.15.1" + resolved "https://registry.yarnpkg.com/pg/-/pg-7.15.1.tgz#a0bac84ebaeb809f3a369fb695ae89b314b08b22" + integrity sha512-o293Pxx5bNRpTv3Dh4+IIhPbTw19Bo4zvppLgR+MAV2I7AF3sMr9gPB4JPvBffWb24pDfC+7Ghe6xh2VxVMBpQ== + dependencies: + buffer-writer "2.0.0" + packet-reader "1.0.0" + pg-connection-string "0.1.3" + pg-pool "^2.0.7" + pg-types "^2.1.0" + pgpass "1.x" + semver "4.3.2" + +pgpass@1.x: + version "1.0.2" + resolved "https://registry.yarnpkg.com/pgpass/-/pgpass-1.0.2.tgz#2a7bb41b6065b67907e91da1b07c1847c877b306" + integrity sha1-Knu0G2BltnkH6R2hsHwYR8h3swY= + dependencies: + split "^1.0.0" + +pkg-config@^1.0.1, pkg-config@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/pkg-config/-/pkg-config-1.1.1.tgz#557ef22d73da3c8837107766c52eadabde298fe4" + integrity sha1-VX7yLXPaPIg3EHdmxS6tq94pj+Q= + dependencies: + debug-log "^1.0.0" + find-root "^1.0.0" + xtend "^4.0.1" + +pluralize@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-1.2.1.tgz#d1a21483fd22bb41e58a12fa3421823140897c45" + integrity sha1-0aIUg/0iu0HlihL6NCGCMUCJfEU= + +postgres-array@~2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/postgres-array/-/postgres-array-2.0.0.tgz#48f8fce054fbc69671999329b8834b772652d82e" + integrity sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA== + +postgres-bytea@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/postgres-bytea/-/postgres-bytea-1.0.0.tgz#027b533c0aa890e26d172d47cf9ccecc521acd35" + integrity sha1-AntTPAqokOJtFy1Hz5zOzFIazTU= + +postgres-date@~1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/postgres-date/-/postgres-date-1.0.4.tgz#1c2728d62ef1bff49abdd35c1f86d4bdf118a728" + integrity sha512-bESRvKVuTrjoBluEcpv2346+6kgB7UlnqWZsnbnCccTNq/pqfj1j6oBaN5+b/NrDXepYUT/HKadqv3iS9lJuVA== + +postgres-interval@^1.1.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/postgres-interval/-/postgres-interval-1.2.0.tgz#b460c82cb1587507788819a06aa0fffdb3544695" + integrity sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ== + dependencies: + xtend "^4.0.0" + +prelude-ls@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" + integrity sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ= + +process-nextick-args@~2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" + integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== + +progress@^1.1.8: + version "1.1.8" + resolved "https://registry.yarnpkg.com/progress/-/progress-1.1.8.tgz#e260c78f6161cdd9b0e56cc3e0a85de17c7a57be" + integrity sha1-4mDHj2Fhzdmw5WzD4Khd4Xx6V74= + +proto-list@~1.2.1: + version "1.2.4" + resolved "https://registry.yarnpkg.com/proto-list/-/proto-list-1.2.4.tgz#212d5bfe1318306a420f6402b8e26ff39647a849" + integrity sha1-IS1b/hMYMGpCD2QCuOJv85ZHqEk= + +protochain@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/protochain/-/protochain-1.0.5.tgz#991c407e99de264aadf8f81504b5e7faf7bfa260" + integrity sha1-mRxAfpneJkqt+PgVBLXn+ve/omA= + +pseudomap@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3" + integrity sha1-8FKijacOYYkX7wqKw0wa5aaChrM= + +readable-stream@^2.2.2: + version "2.3.6" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.6.tgz#b11c27d88b8ff1fbe070643cf94b0c79ae1b0aaf" + integrity sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw== + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.3" + isarray "~1.0.0" + process-nextick-args "~2.0.0" + safe-buffer "~5.1.1" + string_decoder "~1.1.1" + util-deprecate "~1.0.1" + +readline2@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/readline2/-/readline2-1.0.1.tgz#41059608ffc154757b715d9989d199ffbf372e35" + integrity sha1-QQWWCP/BVHV7cV2ZidGZ/783LjU= + dependencies: + code-point-at "^1.0.0" + is-fullwidth-code-point "^1.0.0" + mute-stream "0.0.5" + +regenerator-runtime@^0.11.0: + version "0.11.1" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz#be05ad7f9bf7d22e056f9726cee5017fbf19e2e9" + integrity sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg== + +repeat-string@^1.5.0: + version "1.6.1" + resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" + integrity sha1-jcrkcOHIirwtYA//Sndihtp15jc= + +require-uncached@^1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/require-uncached/-/require-uncached-1.0.3.tgz#4e0d56d6c9662fd31e43011c4b95aa49955421d3" + integrity sha1-Tg1W1slmL9MeQwEcS5WqSZVUIdM= + dependencies: + caller-path "^0.1.0" + resolve-from "^1.0.0" + +resolve-from@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-1.0.1.tgz#26cbfe935d1aeeeabb29bc3fe5aeb01e93d44226" + integrity sha1-Jsv+k10a7uq7Kbw/5a6wHpPUQiY= + +resolve@^1.1.5: + version "1.14.1" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.14.1.tgz#9e018c540fcf0c427d678b9931cbf45e984bcaff" + integrity sha512-fn5Wobh4cxbLzuHaE+nphztHy43/b++4M6SsGFC2gB8uYwf0C8LcarfCz1un7UTW8OFQg9iNjZ4xpcFVGebDPg== + dependencies: + path-parse "^1.0.6" + +restore-cursor@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-1.0.1.tgz#34661f46886327fed2991479152252df92daa541" + integrity sha1-NGYfRohjJ/7SmRR5FSJS35LapUE= + dependencies: + exit-hook "^1.0.0" + onetime "^1.0.0" + +rimraf@~2.6.2: + version "2.6.3" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab" + integrity sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA== + dependencies: + glob "^7.1.3" + +rocambole-indent@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/rocambole-indent/-/rocambole-indent-2.0.4.tgz#a18a24977ca0400b861daa4631e861dcb52d085c" + integrity sha1-oYokl3ygQAuGHapGMehh3LUtCFw= + dependencies: + debug "^2.1.3" + mout "^0.11.0" + rocambole-token "^1.2.1" + +rocambole-linebreak@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/rocambole-linebreak/-/rocambole-linebreak-1.0.2.tgz#03621515b43b4721c97e5a1c1bca5a0366368f2f" + integrity sha1-A2IVFbQ7RyHJflocG8paA2Y2jy8= + dependencies: + debug "^2.1.3" + rocambole-token "^1.2.1" + semver "^4.3.1" + +rocambole-node@~1.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/rocambole-node/-/rocambole-node-1.0.0.tgz#db5b49de7407b0080dd514872f28e393d0f7ff3f" + integrity sha1-21tJ3nQHsAgN1RSHLyjjk9D3/z8= + +rocambole-token@^1.1.2, rocambole-token@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/rocambole-token/-/rocambole-token-1.2.1.tgz#c785df7428dc3cb27ad7897047bd5238cc070d35" + integrity sha1-x4XfdCjcPLJ614lwR71SOMwHDTU= + +rocambole-whitespace@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/rocambole-whitespace/-/rocambole-whitespace-1.0.0.tgz#63330949256b29941f59b190459f999c6b1d3bf9" + integrity sha1-YzMJSSVrKZQfWbGQRZ+ZnGsdO/k= + dependencies: + debug "^2.1.3" + repeat-string "^1.5.0" + rocambole-token "^1.2.1" + +"rocambole@>=0.6.0 <2.0", "rocambole@>=0.7 <2.0", rocambole@^0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/rocambole/-/rocambole-0.7.0.tgz#f6c79505517dc42b6fb840842b8b953b0f968585" + integrity sha1-9seVBVF9xCtvuECEK4uVOw+WhYU= + dependencies: + esprima "^2.1" + +rocambole@^0.3.6: + version "0.3.6" + resolved "https://registry.yarnpkg.com/rocambole/-/rocambole-0.3.6.tgz#4debbf5943144bc7b6006d95be8facc0b74352a7" + integrity sha1-Teu/WUMUS8e2AG2Vvo+swLdDUqc= + dependencies: + esprima "~1.0" + +run-async@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/run-async/-/run-async-0.1.0.tgz#c8ad4a5e110661e402a7d21b530e009f25f8e389" + integrity sha1-yK1KXhEGYeQCp9IbUw4AnyX444k= + dependencies: + once "^1.3.0" + +run-parallel@^1.1.2: + version "1.1.9" + resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.1.9.tgz#c9dd3a7cf9f4b2c4b6244e173a6ed866e61dd679" + integrity sha512-DEqnSRTDw/Tc3FXf49zedI638Z9onwUotBMiUFKmrO2sdFKIbXamXGQ3Axd4qgphxKB4kw/qP1w5kTxnfU1B9Q== + +rx-lite@^3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/rx-lite/-/rx-lite-3.1.2.tgz#19ce502ca572665f3b647b10939f97fd1615f102" + integrity sha1-Gc5QLKVyZl87ZHsQk5+X/RYV8QI= + +safe-buffer@~5.1.0, safe-buffer@~5.1.1: + version "5.1.2" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" + integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== + +semver@4.3.2: + version "4.3.2" + resolved "https://registry.yarnpkg.com/semver/-/semver-4.3.2.tgz#c7a07158a80bedd052355b770d82d6640f803be7" + integrity sha1-x6BxWKgL7dBSNVt3DYLWZA+AO+c= + +semver@^4.3.1: + version "4.3.6" + resolved "https://registry.yarnpkg.com/semver/-/semver-4.3.6.tgz#300bc6e0e86374f7ba61068b5b1ecd57fc6532da" + integrity sha1-MAvG4OhjdPe6YQaLWx7NV/xlMto= + +semver@^5.1.0: + version "5.7.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" + integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== + +serializerr@^1.0.1: + version "1.0.3" + resolved "https://registry.yarnpkg.com/serializerr/-/serializerr-1.0.3.tgz#12d4c5aa1c3ffb8f6d1dc5f395aa9455569c3f91" + integrity sha1-EtTFqhw/+49tHcXzlaqUVVacP5E= + dependencies: + protochain "^1.0.5" + +shelljs@^0.6.0: + version "0.6.1" + resolved "https://registry.yarnpkg.com/shelljs/-/shelljs-0.6.1.tgz#ec6211bed1920442088fe0f70b2837232ed2c8a8" + integrity sha1-7GIRvtGSBEIIj+D3Cyg3Iy7SyKg= + +sigmund@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/sigmund/-/sigmund-1.0.1.tgz#3ff21f198cad2175f9f3b781853fd94d0d19b590" + integrity sha1-P/IfGYytIXX587eBhT/ZTQ0ZtZA= + +slice-ansi@0.0.4: + version "0.0.4" + resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-0.0.4.tgz#edbf8903f66f7ce2f8eafd6ceed65e264c831b35" + integrity sha1-7b+JA/ZvfOL46v1s7tZeJkyDGzU= + +spawn-sync@^1.0.5: + version "1.0.15" + resolved "https://registry.yarnpkg.com/spawn-sync/-/spawn-sync-1.0.15.tgz#b00799557eb7fb0c8376c29d44e8a1ea67e57476" + integrity sha1-sAeZVX63+wyDdsKdROih6mfldHY= + dependencies: + concat-stream "^1.4.7" + os-shim "^0.1.2" + +split@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/split/-/split-1.0.1.tgz#605bd9be303aa59fb35f9229fbea0ddec9ea07d9" + integrity sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg== + dependencies: + through "2" + +sprintf-js@~1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" + integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw= + +standard-engine@^4.0.0: + version "4.1.3" + resolved "https://registry.yarnpkg.com/standard-engine/-/standard-engine-4.1.3.tgz#7a31aad54f03d9f39355f43389ce0694f4094155" + integrity sha1-ejGq1U8D2fOTVfQzic4GlPQJQVU= + dependencies: + defaults "^1.0.2" + deglob "^1.0.0" + find-root "^1.0.0" + get-stdin "^5.0.1" + minimist "^1.1.0" + multiline "^1.0.2" + pkg-config "^1.0.1" + xtend "^4.0.0" + +standard-format@^2.2.4: + version "2.2.4" + resolved "https://registry.yarnpkg.com/standard-format/-/standard-format-2.2.4.tgz#b90fb39a635f749cd4fd117fe4730d31179aaeef" + integrity sha1-uQ+zmmNfdJzU/RF/5HMNMRearu8= + dependencies: + deglob "^1.0.0" + esformatter "^0.9.0" + esformatter-eol-last "^1.0.0" + esformatter-jsx "^7.0.0" + esformatter-literal-notation "^1.0.0" + esformatter-quotes "^1.0.0" + esformatter-remove-trailing-commas "^1.0.1" + esformatter-semicolon-first "^1.1.0" + esformatter-spaced-lined-comment "^2.0.0" + minimist "^1.1.0" + stdin "0.0.1" + +standard@7.1.2: + version "7.1.2" + resolved "https://registry.yarnpkg.com/standard/-/standard-7.1.2.tgz#40166eeec2405065d1a4f0e3f15babc6e274607e" + integrity sha1-QBZu7sJAUGXRpPDj8VurxuJ0YH4= + dependencies: + eslint "~2.10.2" + eslint-config-standard "5.3.1" + eslint-config-standard-jsx "1.2.1" + eslint-plugin-promise "^1.0.8" + eslint-plugin-react "^5.0.1" + eslint-plugin-standard "^1.3.1" + standard-engine "^4.0.0" + +stdin@*, stdin@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/stdin/-/stdin-0.0.1.tgz#d3041981aaec3dfdbc77a1b38d6372e38f5fb71e" + integrity sha1-0wQZgarsPf28d6GzjWNy449ftx4= + +string-width@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" + integrity sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M= + dependencies: + code-point-at "^1.0.0" + is-fullwidth-code-point "^1.0.0" + strip-ansi "^3.0.0" + +string-width@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" + integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw== + dependencies: + is-fullwidth-code-point "^2.0.0" + strip-ansi "^4.0.0" + +string.prototype.endswith@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/string.prototype.endswith/-/string.prototype.endswith-0.2.0.tgz#a19c20dee51a98777e9a47e10f09be393b9bba75" + integrity sha1-oZwg3uUamHd+mkfhDwm+OTubunU= + +string_decoder@~1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" + integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== + dependencies: + safe-buffer "~5.1.0" + +strip-ansi@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" + integrity sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8= + dependencies: + ansi-regex "^2.0.0" + +strip-ansi@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f" + integrity sha1-qEeQIusaw2iocTibY1JixQXuNo8= + dependencies: + ansi-regex "^3.0.0" + +strip-indent@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-1.0.1.tgz#0c7962a6adefa7bbd4ac366460a638552ae1a0a2" + integrity sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI= + dependencies: + get-stdin "^4.0.1" + +strip-json-comments@~0.1.1: + version "0.1.3" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-0.1.3.tgz#164c64e370a8a3cc00c9e01b539e569823f0ee54" + integrity sha1-Fkxk43Coo8wAyeAbU55WmCPw7lQ= + +strip-json-comments@~1.0.1: + version "1.0.4" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-1.0.4.tgz#1e15fbcac97d3ee99bf2d73b4c656b082bbafb91" + integrity sha1-HhX7ysl9Pumb8tc7TGVrCCu6+5E= + +supports-color@5.4.0: + version "5.4.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.4.0.tgz#1c6b337402c2137605efe19f10fec390f6faab54" + integrity sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w== + dependencies: + has-flag "^3.0.0" + +supports-color@^1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-1.3.1.tgz#15758df09d8ff3b4acc307539fabe27095e1042d" + integrity sha1-FXWN8J2P87SswwdTn6vicJXhBC0= + +supports-color@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" + integrity sha1-U10EXOa2Nj+kARcIRimZXp3zJMc= + +sync-exec@^0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/sync-exec/-/sync-exec-0.5.0.tgz#3f7258e4a5ba17245381909fa6a6f6cf506e1661" + integrity sha1-P3JY5KW6FyRTgZCfpqb2z1BuFmE= + +table@^3.7.8: + version "3.8.3" + resolved "https://registry.yarnpkg.com/table/-/table-3.8.3.tgz#2bbc542f0fda9861a755d3947fefd8b3f513855f" + integrity sha1-K7xULw/amGGnVdOUf+/Ys/UThV8= + dependencies: + ajv "^4.7.0" + ajv-keywords "^1.0.0" + chalk "^1.1.1" + lodash "^4.0.0" + slice-ansi "0.0.4" + string-width "^2.0.0" + +text-table@~0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" + integrity sha1-f17oI66AUgfACvLfSoTsP8+lcLQ= + +through@2, through@^2.3.6: + version "2.3.8" + resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" + integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU= + +to-fast-properties@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-1.0.3.tgz#b83571fa4d8c25b82e231b06e3a3055de4ca1a47" + integrity sha1-uDVx+k2MJbguIxsG46MFXeTKGkc= + +type-check@~0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72" + integrity sha1-WITKtRLPHTVeP7eE8wgEsrUg23I= + dependencies: + prelude-ls "~1.1.2" + +type@^1.0.1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/type/-/type-1.2.0.tgz#848dd7698dafa3e54a6c479e759c4bc3f18847a0" + integrity sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg== + +type@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/type/-/type-2.0.0.tgz#5f16ff6ef2eb44f260494dae271033b29c09a9c3" + integrity sha512-KBt58xCHry4Cejnc2ISQAF7QY+ORngsWfxezO68+12hKV6lQY8P/psIkcbjeHWn7MqcgciWJyCCevFMJdIXpow== + +typedarray@^0.0.6: + version "0.0.6" + resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" + integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= + +uniq@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/uniq/-/uniq-1.0.1.tgz#b31c5ae8254844a3a8281541ce2b04b865a734ff" + integrity sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8= + +user-home@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/user-home/-/user-home-2.0.0.tgz#9c70bfd8169bc1dcbf48604e0f04b8b49cde9e9f" + integrity sha1-nHC/2Babwdy/SGBODwS4tJzenp8= + dependencies: + os-homedir "^1.0.0" + +util-deprecate@~1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= + +which@^1.0.5, which@^1.2.4: + version "1.3.1" + resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" + integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== + dependencies: + isexe "^2.0.0" + +word-wrap@~1.2.3: + version "1.2.3" + resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" + integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ== + +wrappy@1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= + +write@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/write/-/write-0.2.1.tgz#5fc03828e264cea3fe91455476f7a3c566cb0757" + integrity sha1-X8A4KOJkzqP+kUVUdvejxWbLB1c= + dependencies: + mkdirp "^0.5.1" + +xtend@^4.0.0, xtend@^4.0.1: + version "4.0.2" + resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" + integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== From e302c4c6ffc709292e8bbac3d4286a147faf8ed3 Mon Sep 17 00:00:00 2001 From: "Brian M. Carlson" Date: Thu, 19 Dec 2019 08:13:27 -0600 Subject: [PATCH 94/95] Bump version --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index c1251e74..e1f089f9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "pg-pool", - "version": "2.0.7", + "version": "2.0.8", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 0609ddbd..30f2bb2d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "pg-pool", - "version": "2.0.7", + "version": "2.0.8", "description": "Connection pool for node-postgres", "main": "index.js", "directories": { From cb96ae2d6e37b1414df405d80258e0e2bafeaba0 Mon Sep 17 00:00:00 2001 From: "Brian M. Carlson" Date: Thu, 19 Dec 2019 08:16:52 -0600 Subject: [PATCH 95/95] Drop node 4.0 from test matrix --- .travis.yml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index 0deb0216..db2833db 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,10 +2,6 @@ language: node_js matrix: include: - - node_js: "4" - addons: - postgresql: "9.1" - dist: precise - node_js: "6" addons: postgresql: "9.4"