From e2bf705e76964a71bd3bdb44a676d9e8449017ec Mon Sep 17 00:00:00 2001 From: "Brian M. Carlson" Date: Fri, 30 Jun 2017 17:00:30 -0400 Subject: [PATCH 01/34] Initial work --- index.js | 210 ++--- package.json | 4 +- test/connection-strings.js | 4 +- test/error-handling.js | 51 ++ test/index.js | 63 +- yarn.lock | 1494 ++++++++++++++++++++++++++++++++++++ 6 files changed, 1643 insertions(+), 183 deletions(-) create mode 100644 test/error-handling.js create mode 100644 yarn.lock diff --git a/index.js b/index.js index 7e0f0ce..94a21af 100644 --- a/index.js +++ b/index.js @@ -1,174 +1,122 @@ -var genericPool = require('generic-pool') 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) } EventEmitter.call(this) - this.options = objectAssign({}, options) + this.options = Object.assign({}, Pool.defaults, options) this.log = this.options.log || function () { } this.Client = this.options.Client || Client || require('pg').Client this.Promise = this.options.Promise || global.Promise + this._clients = [] 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) + if (this.options.create) { + throw new Error('support custom create') } - function resolved (value) { - process.nextTick(function () { - cb(null, value) - }) - } - - function rejected (error) { - process.nextTick(function () { - cb(error) - }) + if (this.options.destroy) { + throw new Error('support custom destroy') } +} - executor(resolved, rejected) +Pool.defaults = { } +util.inherits(Pool, EventEmitter) + 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 - 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, client) - }.bind(this)) - +Pool.prototype.connect = function (cb) { + if (this._clients.length >= this.options.max) { + throw new Error('pool is full') + } + const client = new this.Client(this.options) + this._clients.push(client) + const idleListener = (err) => { + err.client = client + this.emit('error', err, client) + client.removeListener('error', idleListener) + client.on('error', () => { + this.log('additional client error after disconnection due to error', err) + }) + client.end() + } + let result = undefined + if (!cb) { + let reject = undefined + let resolve = undefined + cb = function (err, client) { + err ? reject(err) : resolve(client) + } + result = new Promise(function (res, rej) { + resolve = res + reject = rej + }) + } client.connect(function (err) { + const release = function release() { + client.on('error', idleListener) + } if (err) { - this.log('client connection error:', err) - cb(err, null) + cb(err, undefined, function() { }) } else { - this.log('client connected') - this.emit('connect', client) - cb(null, client) + client.release = release + cb(undefined, client, release) } - }.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') - 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)) + }) + return result } -Pool.prototype.take = Pool.prototype.connect - Pool.prototype.query = function (text, values, cb) { - if (typeof values === 'function') { + if (typeof values == 'function') { cb = values values = undefined } - - return this._promise(cb, function (resolve, reject) { - this.connect(function (err, client, done) { + let result = undefined + if (!cb) { + let reject = undefined + let resolve = undefined + cb = function (err, res) { + err ? reject(err) : resolve(res) + } + result = new Promise(function (res, rej) { + resolve = res + reject = rej + }) + } + this.connect(function (err, client) { + if (err) { + return cb(err) + } + client.query(text, values, function (err, res) { + client.release() if (err) { - return reject(err) + return cb(err) + } else { + return cb(undefined, res) } - client.query(text, values, function (err, res) { - done(err) - err ? reject(err) : resolve(res) - }) }) - }.bind(this)) + }) + return 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)) + const promises = this._clients.map(client => client.end()) + if (!cb) { + return Promise.all(promises) + } + Promise.all(promises) + .then(() => cb ? cb() : undefined) + .catch(err => { + console.log('end error', err) + cb(err) + }) } diff --git a/package.json b/package.json index b3424f1..fda3e12 100644 --- a/package.json +++ b/package.json @@ -30,12 +30,10 @@ "expect.js": "0.3.1", "lodash": "4.13.1", "mocha": "^2.3.3", - "pg": "5.1.0", + "pg": "7.*", "standard": "7.1.2", "standard-format": "2.2.1" }, "dependencies": { - "generic-pool": "2.4.3", - "object-assign": "4.1.0" } } diff --git a/test/connection-strings.js b/test/connection-strings.js index 8ee3d07..d2029d9 100644 --- a/test/connection-strings.js +++ b/test/connection-strings.js @@ -12,8 +12,8 @@ describe('Connection strings', function () { Client: function (args) { expect(args.connectionString).to.equal(connectionString) return { - connect: function (cb) { - cb(new Error('testing')) + connect: function () { + return Promise.reject(new Error('testing')) }, on: function () { } } diff --git a/test/error-handling.js b/test/error-handling.js new file mode 100644 index 0000000..ef5d19f --- /dev/null +++ b/test/error-handling.js @@ -0,0 +1,51 @@ + +var expect = require('expect.js') +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 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/index.js b/test/index.js index 3f2b99d..98f9ffe 100644 --- a/test/index.js +++ b/test/index.js @@ -63,7 +63,7 @@ describe('pool', function () { }) it('passes connection errors to callback', function (done) { - var pool = new Pool({host: 'no-postgres-server-here.com'}) + var pool = new Pool({ host: 'alsdkfjlaskd0808fj' }) pool.query('SELECT $1::text as name', ['brianc'], function (err, res) { expect(res).to.be(undefined) expect(err).to.be.an(Error) @@ -132,21 +132,29 @@ describe('pool', function () { }) describe('with promises', function () { - it('connects and disconnects', function () { + it('connects, queries, and disconnects', function () { var 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('properly pools clients', function () { + 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.skip('properly pools clients', function () { var pool = new Pool({ poolSize: 9 }) return Promise.map(_.times(30), function () { return pool.connect().then(function (client) { @@ -162,19 +170,17 @@ describe('pool', function () { }) }) - it('supports just running queries', function () { + it.skip('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() }) }) - it('recovers from all errors', function () { + it.skip('recovers from all errors', function () { var pool = new Pool() var errors = [] @@ -193,40 +199,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/yarn.lock b/yarn.lock new file mode 100644 index 0000000..cb9ad38 --- /dev/null +++ b/yarn.lock @@ -0,0 +1,1494 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +abbrev@1: + version "1.1.0" + resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.0.tgz#d0554c2256636e2f56e7c2e5ad183f859428d81f" + +acorn-jsx@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-3.0.1.tgz#afdf9488fb1ecefc8348f6fb22f464e32a58b36b" + 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" + +acorn@^1.0.3: + version "1.2.2" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-1.2.2.tgz#c8ce27de0acc76d896d2b1fad3df588d9e82f014" + +acorn@^3.0.4, acorn@^3.1.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-3.3.0.tgz#45e37fb39e8da3f25baee3ff5369e2bb5f22017a" + +ajv-keywords@^1.0.0: + version "1.5.1" + resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-1.5.1.tgz#314dd0a4b3368fad3dfcdc54ede6171b886daf3c" + +ajv@^4.7.0: + version "4.11.8" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-4.11.8.tgz#82ffb02b29e662ae53bdc20af15947706739c536" + 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" + +ansi-regex@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" + +ansi-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" + +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" + +ap@~0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/ap/-/ap-0.2.0.tgz#ae0942600b29912f0d2b14ec60c45e8f330b6110" + +argparse@^1.0.7: + version "1.0.9" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.9.tgz#73d83bc263f86e97f8cc4f6bae1b0e90a7d22c86" + dependencies: + sprintf-js "~1.0.2" + +array-union@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/array-union/-/array-union-1.0.2.tgz#9a34410e4f4e3da23dea375be5be70f24778ec39" + dependencies: + array-uniq "^1.0.1" + +array-uniq@^1.0.1: + version "1.0.3" + resolved "https://registry.yarnpkg.com/array-uniq/-/array-uniq-1.0.3.tgz#af6ac877a25cc7f74e058894753858dfdb24fdb6" + +arrify@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d" + +babel-code-frame@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.22.0.tgz#027620bee567a88c32561574e7fd0801d33118e4" + dependencies: + chalk "^1.1.0" + esutils "^2.0.2" + js-tokens "^3.0.0" + +babel-messages@^6.23.0: + version "6.23.0" + resolved "https://registry.yarnpkg.com/babel-messages/-/babel-messages-6.23.0.tgz#f3cdf4703858035b2a2951c6ec5edf6c62f2630e" + dependencies: + babel-runtime "^6.22.0" + +babel-runtime@^6.22.0: + version "6.23.0" + resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.23.0.tgz#0a9489f144de70efb3ce4300accdb329e2fc543b" + dependencies: + core-js "^2.4.0" + regenerator-runtime "^0.10.0" + +babel-traverse@^6.4.5, babel-traverse@^6.9.0: + version "6.25.0" + resolved "https://registry.yarnpkg.com/babel-traverse/-/babel-traverse-6.25.0.tgz#2257497e2fcd19b89edc13c4c91381f9512496f1" + dependencies: + babel-code-frame "^6.22.0" + babel-messages "^6.23.0" + babel-runtime "^6.22.0" + babel-types "^6.25.0" + babylon "^6.17.2" + debug "^2.2.0" + globals "^9.0.0" + invariant "^2.2.0" + lodash "^4.2.0" + +babel-types@^6.25.0: + version "6.25.0" + resolved "https://registry.yarnpkg.com/babel-types/-/babel-types-6.25.0.tgz#70afb248d5660e5d18f811d91c8303b54134a18e" + dependencies: + babel-runtime "^6.22.0" + esutils "^2.0.2" + lodash "^4.2.0" + to-fast-properties "^1.0.1" + +babylon@^6.1.21, babylon@^6.17.2, babylon@^6.8.0: + version "6.17.4" + resolved "https://registry.yarnpkg.com/babylon/-/babylon-6.17.4.tgz#3e8b7402b88d22c3423e137a1577883b15ff869a" + +balanced-match@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" + +bluebird@3.4.1, bluebird@^3.0.5: + version "3.4.1" + resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.4.1.tgz#b731ddf48e2dd3bedac2e75e1215a11bcb91fa07" + +brace-expansion@^1.1.7: + version "1.1.8" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.8.tgz#c07b211c7c952ec1f8efd51a77ef0d1d3990a292" + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.1" + +buffer-writer@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/buffer-writer/-/buffer-writer-1.0.1.tgz#22a936901e3029afcd7547eb4487ceb697a3bf08" + +caller-path@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/caller-path/-/caller-path-0.1.0.tgz#94085ef63581ecd3daa92444a8fe94e82577751f" + 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" + +chalk@^1.0.0, chalk@^1.1.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" + 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.1" + resolved "https://registry.yarnpkg.com/circular-json/-/circular-json-0.3.1.tgz#be8b36aefccde8b3ca7aa2d6afc07a37242c0d2d" + +cli-cursor@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-1.0.2.tgz#64da3f7d56a54412e59794bd62dc35295e8f2987" + dependencies: + restore-cursor "^1.0.1" + +cli-width@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.1.0.tgz#b234ca209b29ef66fc518d9b98d5847b00edf00a" + +clone@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.2.tgz#260b7a99ebb1edfe247538175f783243cb19d149" + +co@^4.6.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" + +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" + +commander@0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/commander/-/commander-0.6.1.tgz#fa68a14f6a945d54dbbe50d8cdb3320e9e3b1a06" + +commander@2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.3.0.tgz#fd430e889832ec353b9acd1de217c11cb3eef873" + +commander@^2.2.0, commander@^2.9.0: + version "2.10.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.10.0.tgz#e1f5d3245de246d1a5ca04702fa1ad1bd7e405fe" + dependencies: + graceful-readlink ">= 1.0.0" + +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + +concat-stream@^1.4.6, concat-stream@^1.4.7: + version "1.6.0" + resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.0.tgz#0aac662fd52be78964d5532f694784e70110acf7" + dependencies: + inherits "^2.0.3" + readable-stream "^2.2.2" + typedarray "^0.0.6" + +config-chain@~1.1.5: + version "1.1.11" + resolved "https://registry.yarnpkg.com/config-chain/-/config-chain-1.1.11.tgz#aba09747dfbe4c3e70e766a6e41586e1859fc6f2" + dependencies: + ini "^1.3.4" + proto-list "~1.2.1" + +core-js@^2.4.0: + version "2.4.1" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.4.1.tgz#4de911e667b0eae9124e34254b53aea6fc618d3e" + +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" + +d@1: + version "1.0.0" + resolved "https://registry.yarnpkg.com/d/-/d-1.0.0.tgz#754bb5bfe55451da69a58b94d45f4c5b0462d58f" + dependencies: + es5-ext "^0.10.9" + +debug-log@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/debug-log/-/debug-log-1.0.1.tgz#2307632d4c04382b8df8a32f70b895046d52745f" + +debug@2.2.0, debug@^2.1.1, debug@^2.1.3, debug@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/debug/-/debug-2.2.0.tgz#f87057e995b1a1f6ae6a4960664137bc56f039da" + dependencies: + ms "0.7.1" + +debug@^0.7.4: + version "0.7.4" + resolved "https://registry.yarnpkg.com/debug/-/debug-0.7.4.tgz#06e1ea8082c2cb14e39806e22e2f6f757f92af39" + +deep-is@~0.1.3: + version "0.1.3" + resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" + +defaults@^1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/defaults/-/defaults-1.0.3.tgz#c656051e9817d9ff08ed881477f3fe4019f3ef7d" + 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" + 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" + +del@^2.0.2: + version "2.2.2" + resolved "https://registry.yarnpkg.com/del/-/del-2.2.2.tgz#c12c981d067846c84bcaf862cff930d907ffd1a8" + dependencies: + globby "^5.0.0" + is-path-cwd "^1.0.0" + is-path-in-cwd "^1.0.0" + object-assign "^4.0.1" + pify "^2.0.0" + pinkie-promise "^2.0.0" + rimraf "^2.2.8" + +diff@1.4.0, diff@^1.3.2: + version "1.4.0" + resolved "https://registry.yarnpkg.com/diff/-/diff-1.4.0.tgz#7f28d2eb9ee7b15a97efd89ce63dcfdaa3ccbabf" + +disparity@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/disparity/-/disparity-2.0.0.tgz#57ddacb47324ae5f58d2cc0da886db4ce9eeb718" + 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" + dependencies: + esutils "^2.0.2" + isarray "^1.0.0" + +editorconfig@^0.13.2: + version "0.13.2" + resolved "https://registry.yarnpkg.com/editorconfig/-/editorconfig-0.13.2.tgz#8e57926d9ee69ab6cb999f027c2171467acceb35" + dependencies: + bluebird "^3.0.5" + commander "^2.9.0" + lru-cache "^3.2.0" + sigmund "^1.0.1" + +es5-ext@^0.10.14, es5-ext@^0.10.9, es5-ext@~0.10.14: + version "0.10.23" + resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.23.tgz#7578b51be974207a5487821b56538c224e4e7b38" + dependencies: + es6-iterator "2" + es6-symbol "~3.1" + +es6-iterator@2, es6-iterator@^2.0.1, es6-iterator@~2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/es6-iterator/-/es6-iterator-2.0.1.tgz#8e319c9f0453bf575d374940a655920e59ca5512" + dependencies: + d "1" + es5-ext "^0.10.14" + es6-symbol "^3.1" + +es6-map@^0.1.3: + version "0.1.5" + resolved "https://registry.yarnpkg.com/es6-map/-/es6-map-0.1.5.tgz#9136e0503dcc06a301690f0bb14ff4e364e949f0" + 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" + 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, es6-symbol@^3.1, es6-symbol@^3.1.1, es6-symbol@~3.1, es6-symbol@~3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/es6-symbol/-/es6-symbol-3.1.1.tgz#bf00ef4fdab6ba1b46ecb7b629b4c7ed5715cc77" + dependencies: + d "1" + es5-ext "~0.10.14" + +es6-weak-map@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/es6-weak-map/-/es6-weak-map-2.0.2.tgz#5e3ab32251ffd1538a1f8e5ffa1357772f92d96f" + dependencies: + d "1" + es5-ext "^0.10.14" + es6-iterator "^2.0.1" + es6-symbol "^3.1.1" + +escape-string-regexp@1.0.2, escape-string-regexp@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.2.tgz#4dbc2fe674e71949caf3fb2695ce7f2dc1d9a8d1" + +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" + +escope@^3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/escope/-/escope-3.6.0.tgz#e01975e812781a163a6dadfdd80398dc64c889c3" + 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" + 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" + +esformatter-jsx@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/esformatter-jsx/-/esformatter-jsx-3.0.0.tgz#e2201e83d7988ecf4b9d95ea5174bc1db869bae6" + dependencies: + babylon "^6.1.21" + esformatter-ignore "^0.1.3" + extend "^2.0.1" + fresh-falafel "^1.2.0" + js-beautify "^1.5.10" + +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" + 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" + 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" + +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" + 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" + 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" + +esformatter@^0.9.0: + version "0.9.6" + resolved "https://registry.yarnpkg.com/esformatter/-/esformatter-0.9.6.tgz#3608aec7828deee3cd3f46e1192aeb47268a957f" + 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" + +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" + +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" + +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" + 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" + +eslint@~2.10.2: + version "2.10.2" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-2.10.2.tgz#b2309482fef043d3203365a321285e6cce01c3d7" + 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" + 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" + +esprima@^3.1.1: + version "3.1.3" + resolved "https://registry.yarnpkg.com/esprima/-/esprima-3.1.3.tgz#fdca51cee6133895e3c88d535ce49dbff62a4633" + +esprima@~1.0: + version "1.0.4" + resolved "https://registry.yarnpkg.com/esprima/-/esprima-1.0.4.tgz#9f557e08fc3b4d26ece9dd34f8fbf476b62585ad" + +esrecurse@^4.1.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.2.0.tgz#fa9568d98d3823f9a41d91e902dcab9ea6e5b163" + dependencies: + estraverse "^4.1.0" + object-assign "^4.0.1" + +estraverse@^4.1.0, estraverse@^4.1.1, estraverse@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.2.0.tgz#0dee3fed31fcd469618ce7342099fc1afa0bdb13" + +esutils@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.2.tgz#0abf4f1caa5bcb1f7a9d8acc6dea4faaa04bac9b" + +event-emitter@~0.3.5: + version "0.3.5" + resolved "https://registry.yarnpkg.com/event-emitter/-/event-emitter-0.3.5.tgz#df8c69eef1647923c7157b9ce83840610b02cc39" + 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" + +expect.js@0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/expect.js/-/expect.js-0.3.1.tgz#b0a59a0d2eff5437544ebf0ceaa6015841d09b5b" + +extend@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/extend/-/extend-2.0.1.tgz#1ee8010689e7395ff9448241c98652bc759a8260" + +fast-levenshtein@~2.0.4: + version "2.0.6" + resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" + +figures@^1.3.5: + version "1.7.0" + resolved "https://registry.yarnpkg.com/figures/-/figures-1.7.0.tgz#cbe1e3affcf1cd44b80cadfed28dc793a9701d2e" + 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" + 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" + +flat-cache@^1.2.1: + version "1.2.2" + resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-1.2.2.tgz#fa86714e72c21db88601761ecf2f555d1abc6b96" + dependencies: + circular-json "^0.3.1" + del "^2.0.2" + graceful-fs "^4.1.2" + write "^0.2.1" + +foreach@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/foreach/-/foreach-2.0.5.tgz#0bee005018aeb260d0a3af3ae658dd0136ec1b99" + +fresh-falafel@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/fresh-falafel/-/fresh-falafel-1.2.0.tgz#5966dee95fb35d2a29b12d2f25168b17225e4b6c" + dependencies: + acorn "^1.0.3" + foreach "^2.0.5" + isarray "0.0.1" + object-keys "^1.0.6" + +fs.realpath@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + +generate-function@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/generate-function/-/generate-function-2.0.0.tgz#6858fe7c0969b7d4e9093337647ac79f60dfbe74" + +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" + dependencies: + is-property "^1.0.0" + +generic-pool@2.4.2: + version "2.4.2" + resolved "https://registry.yarnpkg.com/generic-pool/-/generic-pool-2.4.2.tgz#886bc5bf0beb7db96e81bcbba078818de5a62683" + +generic-pool@2.4.3: + version "2.4.3" + resolved "https://registry.yarnpkg.com/generic-pool/-/generic-pool-2.4.3.tgz#780c36f69dfad05a5a045dd37be7adca11a4f6ff" + +get-stdin@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-4.0.1.tgz#b968c6b0a04384324902e8bf1a5df32579a450fe" + +get-stdin@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-5.0.1.tgz#122e161591e21ff4c52530305693f20e6393a398" + +glob@3.2.11: + version "3.2.11" + resolved "https://registry.yarnpkg.com/glob/-/glob-3.2.11.tgz#4a973f635b9190f715d10987d5c00fd2815ebe3d" + dependencies: + inherits "2" + minimatch "0.3" + +glob@^5.0.3: + version "5.0.15" + resolved "https://registry.yarnpkg.com/glob/-/glob-5.0.15.tgz#1bc936b9e02f4a603fcc222ecf7633d30b8b93b1" + 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: + version "7.1.2" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.2.tgz#c19c9df9a028702d678612384a6552404c636d15" + 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.0.0, globals@^9.2.0: + version "9.18.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-9.18.0.tgz#aa3896b3e69b487f17e31ed2143d69a8e30c2d8a" + +globby@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/globby/-/globby-5.0.0.tgz#ebd84667ca0dbb330b99bcfc68eac2bc54370e0d" + dependencies: + array-union "^1.0.1" + arrify "^1.0.0" + glob "^7.0.3" + object-assign "^4.0.1" + pify "^2.0.0" + pinkie-promise "^2.0.0" + +graceful-fs@^4.1.2: + version "4.1.11" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.11.tgz#0e8bdfe4d1ddb8854d64e04ea7c00e2a026e5658" + +"graceful-readlink@>= 1.0.0": + version "1.0.1" + resolved "https://registry.yarnpkg.com/graceful-readlink/-/graceful-readlink-1.0.1.tgz#4cafad76bc62f02fa039b2f94e9a3dd3a391a725" + +growl@1.9.2: + version "1.9.2" + resolved "https://registry.yarnpkg.com/growl/-/growl-1.9.2.tgz#0ea7743715db8d8de2c5ede1775e1b45ac85c02f" + +has-ansi@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91" + dependencies: + ansi-regex "^2.0.0" + +ignore@^3.0.9, ignore@^3.1.2: + version "3.3.3" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-3.3.3.tgz#432352e57accd87ab3110e82d3fea0e47812156d" + +imurmurhash@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" + +inflight@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + dependencies: + once "^1.3.0" + wrappy "1" + +inherits@2, inherits@^2.0.3, inherits@~2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" + +ini@^1.3.4: + version "1.3.4" + resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.4.tgz#0537cb79daf59b59a1a517dff706c86ec039162e" + +inquirer@^0.12.0: + version "0.12.0" + resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-0.12.0.tgz#1ef2bfd63504df0bc75785fff8c2c41df12f077e" + 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.0: + version "2.2.2" + resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.2.tgz#9e1f56ac0acdb6bf303306f338be3b204ae60360" + 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" + 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" + +is-my-json-valid@^2.10.0: + version "2.16.0" + resolved "https://registry.yarnpkg.com/is-my-json-valid/-/is-my-json-valid-2.16.0.tgz#f079dd9bfdae65ee2038aae8acbc86ab109e3693" + dependencies: + generate-function "^2.0.0" + generate-object-property "^1.1.0" + jsonpointer "^4.0.0" + xtend "^4.0.0" + +is-path-cwd@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-path-cwd/-/is-path-cwd-1.0.0.tgz#d225ec23132e89edd38fda767472e62e65f1106d" + +is-path-in-cwd@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-path-in-cwd/-/is-path-in-cwd-1.0.0.tgz#6477582b8214d602346094567003be8a9eac04dc" + dependencies: + is-path-inside "^1.0.0" + +is-path-inside@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-1.0.0.tgz#fc06e5a1683fbda13de667aff717bbc10a48f37f" + dependencies: + path-is-inside "^1.0.1" + +is-property@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-property/-/is-property-1.0.2.tgz#57fe1c4e48474edd65b09911f26b1cd4095dda84" + +is-resolvable@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-resolvable/-/is-resolvable-1.0.0.tgz#8df57c61ea2e3c501408d100fb013cf8d6e0cc62" + dependencies: + tryit "^1.0.1" + +isarray@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" + +isarray@^1.0.0, isarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" + +isexe@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + +jade@0.26.3: + version "0.26.3" + resolved "https://registry.yarnpkg.com/jade/-/jade-0.26.3.tgz#8f10d7977d8d79f2f6ff862a81b0513ccb25686c" + dependencies: + commander "0.6.1" + mkdirp "0.3.0" + +js-beautify@^1.5.10: + version "1.6.14" + resolved "https://registry.yarnpkg.com/js-beautify/-/js-beautify-1.6.14.tgz#d3b8f7322d02b9277d58bd238264c327e58044cd" + dependencies: + config-chain "~1.1.5" + editorconfig "^0.13.2" + mkdirp "~0.5.0" + nopt "~3.0.1" + +js-tokens@^3.0.0: + version "3.0.2" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b" + +js-yaml@^3.5.1: + version "3.8.4" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.8.4.tgz#520b4564f86573ba96662af85a8cafa7b4b5a6f6" + dependencies: + argparse "^1.0.7" + esprima "^3.1.1" + +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" + 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" + +jsonpointer@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/jsonpointer/-/jsonpointer-4.0.1.tgz#4fd92cb34e0e9db3c89c8622ecf51f9b978c6cb9" + +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" + +levn@~0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" + dependencies: + prelude-ls "~1.1.2" + type-check "~0.3.2" + +lodash@4.13.1, lodash@^4.0.0, lodash@^4.2.0, lodash@^4.3.0: + version "4.13.1" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.13.1.tgz#83e4b10913f48496d4d16fec4a560af2ee744b68" + +loose-envify@^1.0.0: + version "1.3.1" + resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.3.1.tgz#d1a8ad33fa9ce0e713d65fdd0ac8b748d478c848" + dependencies: + js-tokens "^3.0.0" + +lru-cache@2: + version "2.7.3" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-2.7.3.tgz#6d4524e8b955f95d4f5b58851ce21dd72fb4e952" + +lru-cache@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-3.2.0.tgz#71789b3b7f5399bec8565dda38aa30d2a097efee" + dependencies: + pseudomap "^1.0.1" + +minimatch@0.3: + version "0.3.0" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-0.3.0.tgz#275d8edaac4f1bb3326472089e7949c8394699dd" + dependencies: + lru-cache "2" + sigmund "~1.0.0" + +"minimatch@2 || 3", minimatch@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" + 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" + +minimist@^1.1.0, minimist@^1.1.1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" + +mkdirp@0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.3.0.tgz#1bbf5ab1ba827af23575143490426455f481fe1e" + +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" + dependencies: + minimist "0.0.8" + +mocha@^2.3.3: + version "2.5.3" + resolved "https://registry.yarnpkg.com/mocha/-/mocha-2.5.3.tgz#161be5bdeb496771eb9b35745050b622b5aefc58" + dependencies: + commander "2.3.0" + debug "2.2.0" + diff "1.4.0" + escape-string-regexp "1.0.2" + glob "3.2.11" + growl "1.9.2" + jade "0.26.3" + mkdirp "0.5.1" + supports-color "1.2.0" + to-iso-string "0.0.2" + +"mout@>=0.9 <2.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/mout/-/mout-1.0.0.tgz#9bdf1d4af57d66d47cb353a6335a3281098e1501" + +mout@^0.11.0: + version "0.11.1" + resolved "https://registry.yarnpkg.com/mout/-/mout-0.11.1.tgz#ba3611df5f0e5b1ffbfd01166b8f02d1f5fa2b99" + +ms@0.7.1: + version "0.7.1" + resolved "https://registry.yarnpkg.com/ms/-/ms-0.7.1.tgz#9cd13c03adbff25b65effde7ce864ee952017098" + +multiline@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/multiline/-/multiline-1.0.2.tgz#69b1f25ff074d2828904f244ddd06b7d96ef6c93" + 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" + +nopt@~3.0.1: + version "3.0.6" + resolved "https://registry.yarnpkg.com/nopt/-/nopt-3.0.6.tgz#c6465dbf08abcd4db359317f79ac68a646b28ff9" + 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" + 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" + 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" + 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" + +object-assign@4.1.0, object-assign@^4.0.1, object-assign@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.0.tgz#7a3b3d0e98063d43f4c03f2e8ae6cd51a86883a0" + +object-keys@^1.0.6: + version "1.0.11" + resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.0.11.tgz#c54601778ad560f1142ce0e01bcca8b56d13426d" + +once@^1.3.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + dependencies: + wrappy "1" + +onetime@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/onetime/-/onetime-1.1.0.tgz#a1f7838f8314c516f05ecefcbc4ccfe04b4ed789" + +optionator@^0.8.1: + version "0.8.2" + resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.2.tgz#364c5e409d3f4d6301d6c0b4c05bba50180aeb64" + dependencies: + 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@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3" + +os-shim@^0.1.2: + version "0.1.3" + resolved "https://registry.yarnpkg.com/os-shim/-/os-shim-0.1.3.tgz#6b62c3791cf7909ea35ed46e17658bb417cb3917" + +packet-reader@0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/packet-reader/-/packet-reader-0.2.0.tgz#819df4d010b82d5ea5671f8a1a3acf039bcd7700" + +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" + +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" + +path-parse@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.5.tgz#3c1adf871ea9cd6c9431b6ea2bd74a0ff055c4c1" + +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" + +pg-types@1.*: + version "1.12.0" + resolved "https://registry.yarnpkg.com/pg-types/-/pg-types-1.12.0.tgz#8ad3b7b897e3fd463e62de241ad5fc640b4a66f0" + dependencies: + ap "~0.2.0" + postgres-array "~1.0.0" + postgres-bytea "~1.0.0" + postgres-date "~1.0.0" + postgres-interval "^1.1.0" + +pg@5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/pg/-/pg-5.1.0.tgz#073b9b36763ad8a5478dbb85effef45e739ba9d8" + dependencies: + buffer-writer "1.0.1" + generic-pool "2.4.2" + packet-reader "0.2.0" + pg-connection-string "0.1.3" + pg-types "1.*" + pgpass "0.0.6" + semver "4.3.2" + +pgpass@0.0.6: + version "0.0.6" + resolved "https://registry.yarnpkg.com/pgpass/-/pgpass-0.0.6.tgz#f6276200d01739da1eea63138bdca35ff4bd7280" + dependencies: + split "^1.0.0" + +pify@^2.0.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" + +pinkie-promise@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa" + dependencies: + pinkie "^2.0.0" + +pinkie@^2.0.0: + version "2.0.4" + resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870" + +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" + 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" + +postgres-array@~1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/postgres-array/-/postgres-array-1.0.2.tgz#8e0b32eb03bf77a5c0a7851e0441c169a256a238" + +postgres-bytea@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/postgres-bytea/-/postgres-bytea-1.0.0.tgz#027b533c0aa890e26d172d47cf9ccecc521acd35" + +postgres-date@~1.0.0: + version "1.0.3" + resolved "https://registry.yarnpkg.com/postgres-date/-/postgres-date-1.0.3.tgz#e2d89702efdb258ff9d9cee0fe91bd06975257a8" + +postgres-interval@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/postgres-interval/-/postgres-interval-1.1.0.tgz#1031e7bac34564132862adc9eb6c6d2f3aa75bb4" + 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" + +process-nextick-args@~1.0.6: + version "1.0.7" + resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-1.0.7.tgz#150e20b756590ad3f91093f25a4f2ad8bff30ba3" + +progress@^1.1.8: + version "1.1.8" + resolved "https://registry.yarnpkg.com/progress/-/progress-1.1.8.tgz#e260c78f6161cdd9b0e56cc3e0a85de17c7a57be" + +proto-list@~1.2.1: + version "1.2.4" + resolved "https://registry.yarnpkg.com/proto-list/-/proto-list-1.2.4.tgz#212d5bfe1318306a420f6402b8e26ff39647a849" + +protochain@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/protochain/-/protochain-1.0.5.tgz#991c407e99de264aadf8f81504b5e7faf7bfa260" + +pseudomap@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3" + +readable-stream@^2.2.2: + version "2.3.3" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.3.tgz#368f2512d79f9d46fdfc71349ae7878bbc1eb95c" + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.3" + isarray "~1.0.0" + process-nextick-args "~1.0.6" + safe-buffer "~5.1.1" + string_decoder "~1.0.3" + 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" + dependencies: + code-point-at "^1.0.0" + is-fullwidth-code-point "^1.0.0" + mute-stream "0.0.5" + +regenerator-runtime@^0.10.0: + version "0.10.5" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.10.5.tgz#336c3efc1220adcedda2c9fab67b5a7955a33658" + +repeat-string@^1.5.0: + version "1.6.1" + resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" + +require-uncached@^1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/require-uncached/-/require-uncached-1.0.3.tgz#4e0d56d6c9662fd31e43011c4b95aa49955421d3" + 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" + +resolve@^1.1.5: + version "1.3.3" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.3.3.tgz#655907c3469a8680dc2de3a275a8fdd69691f0e5" + dependencies: + path-parse "^1.0.5" + +restore-cursor@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-1.0.1.tgz#34661f46886327fed2991479152252df92daa541" + dependencies: + exit-hook "^1.0.0" + onetime "^1.0.0" + +rimraf@^2.2.8: + version "2.6.1" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.1.tgz#c2338ec643df7a1b7fe5c54fa86f57428a55f33d" + dependencies: + glob "^7.0.5" + +rocambole-indent@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/rocambole-indent/-/rocambole-indent-2.0.4.tgz#a18a24977ca0400b861daa4631e861dcb52d085c" + 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" + 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" + +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" + +rocambole-whitespace@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/rocambole-whitespace/-/rocambole-whitespace-1.0.0.tgz#63330949256b29941f59b190459f999c6b1d3bf9" + 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" + dependencies: + esprima "^2.1" + +rocambole@^0.3.6: + version "0.3.6" + resolved "https://registry.yarnpkg.com/rocambole/-/rocambole-0.3.6.tgz#4debbf5943144bc7b6006d95be8facc0b74352a7" + 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" + dependencies: + once "^1.3.0" + +run-parallel@^1.1.2: + version "1.1.6" + resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.1.6.tgz#29003c9a2163e01e2d2dfc90575f2c6c1d61a039" + +rx-lite@^3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/rx-lite/-/rx-lite-3.1.2.tgz#19ce502ca572665f3b647b10939f97fd1615f102" + +safe-buffer@~5.1.0, safe-buffer@~5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.1.tgz#893312af69b2123def71f57889001671eeb2c853" + +semver@4.3.2, semver@^4.3.1: + version "4.3.2" + resolved "https://registry.yarnpkg.com/semver/-/semver-4.3.2.tgz#c7a07158a80bedd052355b770d82d6640f803be7" + +serializerr@^1.0.1: + version "1.0.3" + resolved "https://registry.yarnpkg.com/serializerr/-/serializerr-1.0.3.tgz#12d4c5aa1c3ffb8f6d1dc5f395aa9455569c3f91" + 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" + +sigmund@^1.0.1, sigmund@~1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/sigmund/-/sigmund-1.0.1.tgz#3ff21f198cad2175f9f3b781853fd94d0d19b590" + +slice-ansi@0.0.4: + version "0.0.4" + resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-0.0.4.tgz#edbf8903f66f7ce2f8eafd6ceed65e264c831b35" + +spawn-sync@^1.0.5: + version "1.0.15" + resolved "https://registry.yarnpkg.com/spawn-sync/-/spawn-sync-1.0.15.tgz#b00799557eb7fb0c8376c29d44e8a1ea67e57476" + dependencies: + concat-stream "^1.4.7" + os-shim "^0.1.2" + +split@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/split/-/split-1.0.0.tgz#c4395ce683abcd254bc28fe1dabb6e5c27dcffae" + 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" + +standard-engine@^4.0.0: + version "4.1.3" + resolved "https://registry.yarnpkg.com/standard-engine/-/standard-engine-4.1.3.tgz#7a31aad54f03d9f39355f43389ce0694f4094155" + 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.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/standard-format/-/standard-format-2.2.1.tgz#249ffdf7ae89c7941d357c134ac3bdf8e8cd19b0" + dependencies: + deglob "^1.0.0" + esformatter "^0.9.0" + esformatter-eol-last "^1.0.0" + esformatter-jsx "^3.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" + 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" + +string-width@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" + 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.0" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.0.tgz#030664561fc146c9423ec7d978fe2457437fe6d0" + 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" + +string_decoder@~1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.0.3.tgz#0fc67d7c141825de94282dd536bec6b9bce860ab" + 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" + 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" + 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" + 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" + +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" + +supports-color@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-1.2.0.tgz#ff1ed1e61169d06b3cf2d588e188b18d8847e17e" + +supports-color@^1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-1.3.1.tgz#15758df09d8ff3b4acc307539fabe27095e1042d" + +supports-color@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" + +sync-exec@^0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/sync-exec/-/sync-exec-0.5.0.tgz#3f7258e4a5ba17245381909fa6a6f6cf506e1661" + +table@^3.7.8: + version "3.8.3" + resolved "https://registry.yarnpkg.com/table/-/table-3.8.3.tgz#2bbc542f0fda9861a755d3947fefd8b3f513855f" + 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" + +through@2, through@^2.3.6: + version "2.3.8" + resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" + +to-fast-properties@^1.0.1: + version "1.0.3" + resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-1.0.3.tgz#b83571fa4d8c25b82e231b06e3a3055de4ca1a47" + +to-iso-string@0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/to-iso-string/-/to-iso-string-0.0.2.tgz#4dc19e664dfccbe25bd8db508b00c6da158255d1" + +tryit@^1.0.1: + version "1.0.3" + resolved "https://registry.yarnpkg.com/tryit/-/tryit-1.0.3.tgz#393be730a9446fd1ead6da59a014308f36c289cb" + +type-check@~0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72" + dependencies: + prelude-ls "~1.1.2" + +typedarray@^0.0.6: + version "0.0.6" + resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" + +uniq@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/uniq/-/uniq-1.0.1.tgz#b31c5ae8254844a3a8281541ce2b04b865a734ff" + +user-home@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/user-home/-/user-home-2.0.0.tgz#9c70bfd8169bc1dcbf48604e0f04b8b49cde9e9f" + 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" + +which@^1.0.5, which@^1.2.4: + version "1.2.14" + resolved "https://registry.yarnpkg.com/which/-/which-1.2.14.tgz#9a87c4378f03e827cecaf1acdf56c736c01c14e5" + dependencies: + isexe "^2.0.0" + +wordwrap@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" + +wrappy@1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + +write@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/write/-/write-0.2.1.tgz#5fc03828e264cea3fe91455476f7a3c566cb0757" + dependencies: + mkdirp "^0.5.1" + +xtend@^4.0.0, xtend@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af" From d3c86fe9f71cf5e97bf460598cb553b2d06e6432 Mon Sep 17 00:00:00 2001 From: "Brian M. Carlson" Date: Fri, 30 Jun 2017 19:30:54 -0400 Subject: [PATCH 02/34] Make progress on custom pool --- index.js | 79 ++++++++++++++++++++++++++++++++++++++++++--- package.json | 4 +-- test/index.js | 86 ++++++++++++++++++++++++------------------------- test/mocha.opts | 1 + test/setup.js | 9 ++++++ test/sizing.js | 44 +++++++++++++++++++++++++ 6 files changed, 173 insertions(+), 50 deletions(-) create mode 100644 test/setup.js create mode 100644 test/sizing.js diff --git a/index.js b/index.js index 94a21af..202d7fa 100644 --- a/index.js +++ b/index.js @@ -1,3 +1,4 @@ +"use strict" var util = require('util') var EventEmitter = require('events').EventEmitter @@ -11,6 +12,8 @@ var Pool = module.exports = function (options, Client) { this.Client = this.options.Client || Client || require('pg').Client this.Promise = this.options.Promise || global.Promise this._clients = [] + this._idle = [] + this._pendingQueue = [] this.options.max = this.options.max || this.options.poolSize || 10 @@ -34,9 +37,54 @@ Pool.prototype._promiseNoCallback = function (callback, executor) { : new this.Promise(executor) } +Pool.prototype._pulseQueue = function () { + if (!this._pendingQueue.length) { + return + } + if (!this._idle.length) { + return + } + const client = this._idle.pop() + const waiter = this._pendingQueue.shift() + waiter(null, client) +} + Pool.prototype.connect = function (cb) { if (this._clients.length >= this.options.max) { - throw new Error('pool is full') + let result = undefined + if (!cb) { + let reject = undefined + let resolve = undefined + cb = function (err, client) { + err ? reject(err) : resolve(client) + } + result = new Promise(function (res, rej) { + resolve = res + reject = rej + }) + } + this._pendingQueue.push((err, client) => { + const idleListener = (err) => { + err.client = client + this.emit('error', err, client) + client.removeListener('error', idleListener) + client.on('error', () => { + this.log('additional client error after disconnection due to error', err) + }) + client.end() + } + const release = () => { + delete client.release + client.on('error', idleListener) + client.removeListener('error', idleListener) + this._idle.push(client) + this._pulseQueue() + } + client.release = release + cb(err, client) + }) + this._pulseQueue() + return result } const client = new this.Client(this.options) this._clients.push(client) @@ -61,9 +109,13 @@ Pool.prototype.connect = function (cb) { reject = rej }) } - client.connect(function (err) { - const release = function release() { + client.connect((err) => { + const release = () => { + delete client.release client.on('error', idleListener) + client.removeListener('error', idleListener) + this._idle.push(client) + this._pulseQueue() } if (err) { cb(err, undefined, function() { }) @@ -97,7 +149,7 @@ Pool.prototype.query = function (text, values, cb) { return cb(err) } client.query(text, values, function (err, res) { - client.release() + client.release(err) if (err) { return cb(err) } else { @@ -116,7 +168,24 @@ Pool.prototype.end = function (cb) { Promise.all(promises) .then(() => cb ? cb() : undefined) .catch(err => { - console.log('end error', err) cb(err) }) } + +Object.defineProperty(Pool.prototype, 'waitingCount', { + get: function () { + return this._pendingQueue.length + } +}) + +Object.defineProperty(Pool.prototype, 'idleCount', { + get: function () { + return this._idle.length + } +}) + +Object.defineProperty(Pool.prototype, 'totalCount', { + get: function () { + return this._clients.length + } +}) diff --git a/package.json b/package.json index fda3e12..6274a43 100644 --- a/package.json +++ b/package.json @@ -27,6 +27,7 @@ "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", @@ -34,6 +35,5 @@ "standard": "7.1.2", "standard-format": "2.2.1" }, - "dependencies": { - } + "dependencies": {} } diff --git a/test/index.js b/test/index.js index 98f9ffe..ee967e7 100644 --- a/test/index.js +++ b/test/index.js @@ -1,26 +1,21 @@ -var expect = require('expect.js') -var _ = require('lodash') +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() + const 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 +28,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 +38,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 +48,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,7 +58,7 @@ describe('pool', function () { }) it('passes connection errors to callback', function (done) { - var pool = new Pool({ host: 'alsdkfjlaskd0808fj' }) + const pool = new Pool({ host: 'alsdkfjlaskd0808fj' }) pool.query('SELECT $1::text as name', ['brianc'], function (err, res) { expect(res).to.be(undefined) expect(err).to.be.an(Error) @@ -73,8 +68,8 @@ describe('pool', function () { }) }) - it('removes client if it errors in background', function (done) { - var pool = new Pool() + it.skip('removes client if it errors in background', function (done) { + const pool = new Pool() pool.connect(function (err, client, release) { release() if (err) return done(err) @@ -94,8 +89,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 +100,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 +110,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,15 +120,15 @@ 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) }) }) describe('with promises', function () { it('connects, queries, and disconnects', function () { - var pool = new Pool() + const pool = new Pool() return pool.connect().then(function (client) { return client.query('select $1::text as name', ['hi']).then(function (res) { expect(res.rows).to.eql([{ name: 'hi' }]) @@ -154,44 +149,49 @@ describe('pool', function () { }) }) - it.skip('properly pools clients', function () { - var pool = new Pool({ poolSize: 9 }) - return Promise.map(_.times(30), function () { + it('properly pools clients', 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.skip('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) { + it('supports just running queries', function () { + 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) return pool.end() }) }) - it.skip('recovers from all errors', function () { - var pool = new Pool() + it('recovers from all 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.idleCount).to.equal(10) 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() }) diff --git a/test/mocha.opts b/test/mocha.opts index 46e8e69..9ffe54f 100644 --- a/test/mocha.opts +++ b/test/mocha.opts @@ -1,2 +1,3 @@ +--require test/setup.js --no-exit --bail diff --git a/test/setup.js b/test/setup.js new file mode 100644 index 0000000..89beece --- /dev/null +++ b/test/setup.js @@ -0,0 +1,9 @@ +const crash = reason => { + process.on(reason, err => { + console.error(err.message, err.stack) + process.exit(-1) + }) +} + +crash('unhandledRejection') +crash('uncaughtError') diff --git a/test/sizing.js b/test/sizing.js new file mode 100644 index 0000000..b9b6135 --- /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() + })) +}) From 0f6e85db2108e65bca328a584548ffe9f661239a Mon Sep 17 00:00:00 2001 From: "Brian M. Carlson" Date: Fri, 30 Jun 2017 20:09:34 -0400 Subject: [PATCH 03/34] Make all original tests pass --- index.js | 24 ++++++++---------------- test/connection-strings.js | 4 ++-- test/error-handling.js | 21 +++------------------ test/events.js | 15 +++++++-------- test/index.js | 2 +- test/setup.js | 1 + 6 files changed, 22 insertions(+), 45 deletions(-) diff --git a/index.js b/index.js index 202d7fa..fc158f5 100644 --- a/index.js +++ b/index.js @@ -64,24 +64,14 @@ Pool.prototype.connect = function (cb) { }) } this._pendingQueue.push((err, client) => { - const idleListener = (err) => { - err.client = client - this.emit('error', err, client) - client.removeListener('error', idleListener) - client.on('error', () => { - this.log('additional client error after disconnection due to error', err) - }) - client.end() - } const release = () => { - delete client.release - client.on('error', idleListener) - client.removeListener('error', idleListener) + client.release = function () { throw new Error('called release twice') } this._idle.push(client) this._pulseQueue() } client.release = release - cb(err, client) + this.emit('acquire', client) + cb(err, client, release) }) this._pulseQueue() return result @@ -109,18 +99,20 @@ Pool.prototype.connect = function (cb) { reject = rej }) } + this.log('creating new client') client.connect((err) => { const release = () => { - delete client.release - client.on('error', idleListener) - client.removeListener('error', idleListener) + client.release = function () { throw new Error('called release twice') } this._idle.push(client) this._pulseQueue() } + client.on('error', idleListener) if (err) { cb(err, undefined, function() { }) } else { client.release = release + this.emit('connect', client) + this.emit('acquire', client) cb(undefined, client, release) } }) diff --git a/test/connection-strings.js b/test/connection-strings.js index d2029d9..8ee3d07 100644 --- a/test/connection-strings.js +++ b/test/connection-strings.js @@ -12,8 +12,8 @@ describe('Connection strings', function () { Client: function (args) { expect(args.connectionString).to.equal(connectionString) return { - connect: function () { - return Promise.reject(new Error('testing')) + connect: function (cb) { + cb(new Error('testing')) }, on: function () { } } diff --git a/test/error-handling.js b/test/error-handling.js index ef5d19f..6df5ca3 100644 --- a/test/error-handling.js +++ b/test/error-handling.js @@ -8,23 +8,15 @@ var Promise = require('bluebird') var Pool = require('../') -if (typeof global.Promise === 'undefined') { - global.Promise = Promise -} - - 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 pool = new Pool() var errors = 0 var shouldGet = 0 function runErrorQuery () { shouldGet++ return new Promise(function (resolve, reject) { - pgPool.query("SELECT 'asd'+1 ").then(function (res) { + pool.query("SELECT 'asd'+1 ").then(function (res) { reject(res) // this should always error }).catch(function (err) { errors++ @@ -38,14 +30,7 @@ describe('pool error handling', function () { } Promise.all(ps).then(function () { expect(shouldGet).to.eql(errors) - done() + pool.end(done) }) }) }) - -process.on('unhandledRejection', function (e) { - console.error(e.message, e.stack) - setImmediate(function () { - throw e - }) -}) diff --git a/test/events.js b/test/events.js index 759dff0..aae7937 100644 --- a/test/events.js +++ b/test/events.js @@ -27,17 +27,17 @@ describe('events', function () { // 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) { @@ -49,9 +49,8 @@ describe('events', function () { }) for (var 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()') } @@ -64,7 +63,7 @@ describe('events', function () { 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) + expect(err).to.equal(undefined) client.release() setImmediate(function () { client.emit('error', new Error('problem')) diff --git a/test/index.js b/test/index.js index ee967e7..b05068b 100644 --- a/test/index.js +++ b/test/index.js @@ -68,7 +68,7 @@ describe('pool', function () { }) }) - it.skip('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) { release() diff --git a/test/setup.js b/test/setup.js index 89beece..77bd142 100644 --- a/test/setup.js +++ b/test/setup.js @@ -7,3 +7,4 @@ const crash = reason => { crash('unhandledRejection') crash('uncaughtError') +crash('warning') From 3cc72ce5da442321a6fbffe04e1edb63c33be05c Mon Sep 17 00:00:00 2001 From: "Brian M. Carlson" Date: Fri, 30 Jun 2017 20:43:22 -0400 Subject: [PATCH 04/34] Fix test race --- index.js | 19 +------------------ test/error-handling.js | 25 ++++++++++++++++++++++++- test/events.js | 2 +- 3 files changed, 26 insertions(+), 20 deletions(-) diff --git a/index.js b/index.js index fc158f5..6fe2db9 100644 --- a/index.js +++ b/index.js @@ -7,7 +7,7 @@ var Pool = module.exports = function (options, Client) { return new Pool(options, Client) } EventEmitter.call(this) - this.options = Object.assign({}, Pool.defaults, options) + this.options = Object.assign({}, options) this.log = this.options.log || function () { } this.Client = this.options.Client || Client || require('pg').Client this.Promise = this.options.Promise || global.Promise @@ -16,27 +16,10 @@ var Pool = module.exports = function (options, Client) { this._pendingQueue = [] this.options.max = this.options.max || this.options.poolSize || 10 - - if (this.options.create) { - throw new Error('support custom create') - } - - if (this.options.destroy) { - throw new Error('support custom destroy') - } -} - -Pool.defaults = { } util.inherits(Pool, EventEmitter) -Pool.prototype._promiseNoCallback = function (callback, executor) { - return callback - ? executor() - : new this.Promise(executor) -} - Pool.prototype._pulseQueue = function () { if (!this._pendingQueue.length) { return diff --git a/test/error-handling.js b/test/error-handling.js index 6df5ca3..e4e2c75 100644 --- a/test/error-handling.js +++ b/test/error-handling.js @@ -1,4 +1,4 @@ - +var co = require('co') var expect = require('expect.js') var _ = require('lodash') @@ -33,4 +33,27 @@ describe('pool error handling', function () { 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() + }) + }) }) diff --git a/test/events.js b/test/events.js index aae7937..44a40c6 100644 --- a/test/events.js +++ b/test/events.js @@ -57,7 +57,7 @@ describe('events', function () { 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) { From 1581df34fe7b52de0168052dd64d452bd8b158c5 Mon Sep 17 00:00:00 2001 From: "Brian M. Carlson" Date: Sat, 1 Jul 2017 00:17:01 -0400 Subject: [PATCH 05/34] Fix test when DNS is missing --- index.js | 3 ++- test/index.js | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/index.js b/index.js index 6fe2db9..e5150da 100644 --- a/index.js +++ b/index.js @@ -82,8 +82,9 @@ Pool.prototype.connect = function (cb) { reject = rej }) } - this.log('creating new client') + this.log('connecting new client', this.options) client.connect((err) => { + this.log('new client connected') const release = () => { client.release = function () { throw new Error('called release twice') } this._idle.push(client) diff --git a/test/index.js b/test/index.js index b05068b..0e8b7dc 100644 --- a/test/index.js +++ b/test/index.js @@ -58,7 +58,7 @@ describe('pool', function () { }) it('passes connection errors to callback', function (done) { - const pool = new Pool({ host: 'alsdkfjlaskd0808fj' }) + 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) From 2205dd8f46f31d35d5bcfe07a3efdc6e879d2c7a Mon Sep 17 00:00:00 2001 From: "Brian M. Carlson" Date: Sat, 1 Jul 2017 01:13:33 -0400 Subject: [PATCH 06/34] Test more error conditions --- index.js | 74 +++++++++++++++++++++--------------------- test/error-handling.js | 37 +++++++++++++++++++++ test/setup.js | 2 +- 3 files changed, 75 insertions(+), 38 deletions(-) diff --git a/index.js b/index.js index e5150da..b4eb1f6 100644 --- a/index.js +++ b/index.js @@ -14,6 +14,7 @@ var Pool = module.exports = function (options, Client) { this._clients = [] this._idle = [] this._pendingQueue = [] + this._ending = false this.options.max = this.options.max || this.options.poolSize || 10 } @@ -32,20 +33,31 @@ Pool.prototype._pulseQueue = function () { waiter(null, client) } +const promisify = callback => { + if (callback) { + return { callback: callback, result: undefined } + } + let reject = undefined + let resolve = undefined + const cb = function (err, client) { + err ? reject(err) : resolve(client) + } + const result = new Promise(function (res, rej) { + resolve = res + reject = rej + }) + return { callback: cb, result: result } +} + Pool.prototype.connect = function (cb) { + if (this._ending) { + const err = new Error('Cannot use a pool after calling end on the pool') + return cb ? cb(err) : Promise.reject(err) + } if (this._clients.length >= this.options.max) { - let result = undefined - if (!cb) { - let reject = undefined - let resolve = undefined - cb = function (err, client) { - err ? reject(err) : resolve(client) - } - result = new Promise(function (res, rej) { - resolve = res - reject = rej - }) - } + const response = promisify(cb) + const result = response.result + cb = response.callback this._pendingQueue.push((err, client) => { const release = () => { client.release = function () { throw new Error('called release twice') } @@ -59,6 +71,7 @@ Pool.prototype.connect = function (cb) { this._pulseQueue() return result } + const client = new this.Client(this.options) this._clients.push(client) const idleListener = (err) => { @@ -70,18 +83,10 @@ Pool.prototype.connect = function (cb) { }) client.end() } - let result = undefined - if (!cb) { - let reject = undefined - let resolve = undefined - cb = function (err, client) { - err ? reject(err) : resolve(client) - } - result = new Promise(function (res, rej) { - resolve = res - reject = rej - }) - } + + const response = promisify(cb) + const result = response.result + cb = response.callback this.log('connecting new client', this.options) client.connect((err) => { this.log('new client connected') @@ -108,18 +113,8 @@ Pool.prototype.query = function (text, values, cb) { cb = values values = undefined } - let result = undefined - if (!cb) { - let reject = undefined - let resolve = undefined - cb = function (err, res) { - err ? reject(err) : resolve(res) - } - result = new Promise(function (res, rej) { - resolve = res - reject = rej - }) - } + const response = promisify(cb) + cb = response.callback this.connect(function (err, client) { if (err) { return cb(err) @@ -133,10 +128,15 @@ Pool.prototype.query = function (text, values, cb) { } }) }) - return result + return response.result } Pool.prototype.end = function (cb) { + if (this._ending) { + const err = new Error('Called end on pool more than once') + return cb ? cb(err) : Promise.reject(err) + } + this._ending = true const promises = this._clients.map(client => client.end()) if (!cb) { return Promise.all(promises) diff --git a/test/error-handling.js b/test/error-handling.js index e4e2c75..c5864eb 100644 --- a/test/error-handling.js +++ b/test/error-handling.js @@ -56,4 +56,41 @@ describe('pool error handling', function () { expect(() => pool.query('select now()')).to.reject() }) }) + + describe('using an ended pool', () => { + it('rejects all additional promises', (done) => { + const pool = new Pool() + const errors = [] + 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() + const errors = [] + 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() + }) + }) + }) + }) + }) + + }) }) diff --git a/test/setup.js b/test/setup.js index 77bd142..cf75b7a 100644 --- a/test/setup.js +++ b/test/setup.js @@ -1,6 +1,6 @@ const crash = reason => { process.on(reason, err => { - console.error(err.message, err.stack) + console.error(reason, err.stack) process.exit(-1) }) } From 3d8a9dae0234253e1df28271a00c0a6a51d0cbd7 Mon Sep 17 00:00:00 2001 From: "Brian M. Carlson" Date: Sat, 1 Jul 2017 01:29:15 -0400 Subject: [PATCH 07/34] Add test for byop --- index.js | 18 +++++++++--------- test/bring-your-own-promise.js | 27 +++++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 9 deletions(-) create mode 100644 test/bring-your-own-promise.js diff --git a/index.js b/index.js index b4eb1f6..787318d 100644 --- a/index.js +++ b/index.js @@ -33,7 +33,7 @@ Pool.prototype._pulseQueue = function () { waiter(null, client) } -const promisify = callback => { +Pool.prototype._promisify = function(callback) { if (callback) { return { callback: callback, result: undefined } } @@ -42,7 +42,7 @@ const promisify = callback => { const cb = function (err, client) { err ? reject(err) : resolve(client) } - const result = new Promise(function (res, rej) { + const result = new this.Promise(function (res, rej) { resolve = res reject = rej }) @@ -52,10 +52,10 @@ const promisify = callback => { Pool.prototype.connect = function (cb) { if (this._ending) { const err = new Error('Cannot use a pool after calling end on the pool') - return cb ? cb(err) : Promise.reject(err) + return cb ? cb(err) : this.Promise.reject(err) } if (this._clients.length >= this.options.max) { - const response = promisify(cb) + const response = this._promisify(cb) const result = response.result cb = response.callback this._pendingQueue.push((err, client) => { @@ -84,7 +84,7 @@ Pool.prototype.connect = function (cb) { client.end() } - const response = promisify(cb) + const response = this._promisify(cb) const result = response.result cb = response.callback this.log('connecting new client', this.options) @@ -113,7 +113,7 @@ Pool.prototype.query = function (text, values, cb) { cb = values values = undefined } - const response = promisify(cb) + const response = this._promisify(cb) cb = response.callback this.connect(function (err, client) { if (err) { @@ -134,14 +134,14 @@ Pool.prototype.query = function (text, values, cb) { Pool.prototype.end = function (cb) { if (this._ending) { const err = new Error('Called end on pool more than once') - return cb ? cb(err) : Promise.reject(err) + return cb ? cb(err) : this.Promise.reject(err) } this._ending = true const promises = this._clients.map(client => client.end()) if (!cb) { - return Promise.all(promises) + return this.Promise.all(promises) } - Promise.all(promises) + this.Promise.all(promises) .then(() => cb ? cb() : undefined) .catch(err => { cb(err) diff --git a/test/bring-your-own-promise.js b/test/bring-your-own-promise.js new file mode 100644 index 0000000..631ff60 --- /dev/null +++ b/test/bring-your-own-promise.js @@ -0,0 +1,27 @@ +var expect = require('expect.js') +var _ = require('lodash') + +var describe = require('mocha').describe +var it = require('mocha').it +var BluebirdPromise = require('bluebird') + +var Pool = require('../') + +describe('Bring your own promise', function () { + it('uses supplied promise for operations', (done) => { + const pool = new Pool({ Promise: BluebirdPromise }) + const promise = pool.connect() + expect(promise).to.be.a(BluebirdPromise) + promise.then(() => { + const queryPromise = pool.query('SELECT NOW()') + expect(queryPromise).to.be.a(BluebirdPromise) + queryPromise.then(() => { + const connectPromise = pool.connect() + expect(connectPromise).to.be.a(BluebirdPromise) + connectPromise.then(client => { + pool.end(done) + }) + }) + }) + }) +}) From a3de64e482c8dc464600b32ce101c2668b178c52 Mon Sep 17 00:00:00 2001 From: "Brian M. Carlson" Date: Sat, 1 Jul 2017 01:36:31 -0400 Subject: [PATCH 08/34] Add BYOP tests for errors --- test/bring-your-own-promise.js | 49 +++++++++++++++++++--------------- 1 file changed, 28 insertions(+), 21 deletions(-) diff --git a/test/bring-your-own-promise.js b/test/bring-your-own-promise.js index 631ff60..6520de0 100644 --- a/test/bring-your-own-promise.js +++ b/test/bring-your-own-promise.js @@ -1,27 +1,34 @@ -var expect = require('expect.js') -var _ = require('lodash') +'use strict' +const co = require('co') +const expect = require('expect.js') +const _ = require('lodash') -var describe = require('mocha').describe -var it = require('mocha').it -var BluebirdPromise = require('bluebird') +const describe = require('mocha').describe +const it = require('mocha').it +const BluebirdPromise = require('bluebird') -var Pool = require('../') +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', (done) => { + it('uses supplied promise for operations', co.wrap(function * () { + const pool = new Pool({ Promise: BluebirdPromise }) + yield checkType(pool.connect()) + yield checkType(pool.query('SELECT NOW()')) + yield checkType(pool.connect()) + yield checkType(pool.end()) + })) + + it('uses promises in errors', co.wrap(function * () { const pool = new Pool({ Promise: BluebirdPromise }) - const promise = pool.connect() - expect(promise).to.be.a(BluebirdPromise) - promise.then(() => { - const queryPromise = pool.query('SELECT NOW()') - expect(queryPromise).to.be.a(BluebirdPromise) - queryPromise.then(() => { - const connectPromise = pool.connect() - expect(connectPromise).to.be.a(BluebirdPromise) - connectPromise.then(client => { - pool.end(done) - }) - }) - }) - }) + yield checkType(pool.connect()) + yield checkType(pool.end()) + yield checkType(pool.connect()) + yield checkType(pool.query()) + yield checkType(pool.end()) + })) }) From 7d6a1890ffcad40f358a3083ff6676484dac4073 Mon Sep 17 00:00:00 2001 From: "Brian M. Carlson" Date: Sat, 1 Jul 2017 01:56:35 -0400 Subject: [PATCH 09/34] Add test for idle client error expunging --- index.js | 12 ++++++++++-- test/error-handling.js | 21 +++++++++++++++++++++ 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/index.js b/index.js index 787318d..b5378f3 100644 --- a/index.js +++ b/index.js @@ -49,6 +49,12 @@ Pool.prototype._promisify = function(callback) { return { callback: cb, result: result } } +Pool.prototype._remove = function (client) { + this._idle = this._idle.filter(c => c != client) + this._clients = this._clients.filter(c => c != client) + client.end() +} + Pool.prototype.connect = function (cb) { if (this._ending) { const err = new Error('Cannot use a pool after calling end on the pool') @@ -76,12 +82,14 @@ Pool.prototype.connect = function (cb) { this._clients.push(client) const idleListener = (err) => { err.client = client - this.emit('error', err, client) client.removeListener('error', idleListener) client.on('error', () => { this.log('additional client error after disconnection due to error', err) }) - client.end() + 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 response = this._promisify(cb) diff --git a/test/error-handling.js b/test/error-handling.js index c5864eb..72d0394 100644 --- a/test/error-handling.js +++ b/test/error-handling.js @@ -91,6 +91,27 @@ describe('pool error handling', function () { }) }) }) + }) + describe('error on idel 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')) + }) + }) + })) }) }) From a85e9ecc645c38e42dd99396338f7f4e54c5fbf2 Mon Sep 17 00:00:00 2001 From: "Brian M. Carlson" Date: Sat, 1 Jul 2017 01:57:41 -0400 Subject: [PATCH 10/34] Fix typo --- test/error-handling.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/error-handling.js b/test/error-handling.js index 72d0394..305dc31 100644 --- a/test/error-handling.js +++ b/test/error-handling.js @@ -93,7 +93,7 @@ describe('pool error handling', function () { }) }) - describe('error on idel client', () => { + describe('error from idle client', () => { it('removes client from pool', co.wrap(function * () { const pool = new Pool() const client = yield pool.connect() From c9700765bda089e9e75a1f9c3d97656558d4601e Mon Sep 17 00:00:00 2001 From: "Brian M. Carlson" Date: Sat, 1 Jul 2017 02:00:34 -0400 Subject: [PATCH 11/34] Replace var with const/let --- test/error-handling.js | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/test/error-handling.js b/test/error-handling.js index 305dc31..c1bdb42 100644 --- a/test/error-handling.js +++ b/test/error-handling.js @@ -1,18 +1,17 @@ -var co = require('co') -var expect = require('expect.js') -var _ = require('lodash') +"use strict" +const co = require('co') +const expect = require('expect.js') -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('../') +const Pool = require('../') describe('pool error handling', function () { it('Should complete these queries without dying', function (done) { - var pool = new Pool() - var errors = 0 - var shouldGet = 0 + const pool = new Pool() + let errors = 0 + let shouldGet = 0 function runErrorQuery () { shouldGet++ return new Promise(function (resolve, reject) { @@ -24,8 +23,8 @@ describe('pool error handling', function () { }) }) } - var ps = [] - for (var i = 0; i < 5; i++) { + const ps = [] + for (let i = 0; i < 5; i++) { ps.push(runErrorQuery()) } Promise.all(ps).then(function () { From 0b421c078d79cf6b2233ccddf019352f56d34908 Mon Sep 17 00:00:00 2001 From: "Brian M. Carlson" Date: Sat, 1 Jul 2017 02:03:42 -0400 Subject: [PATCH 12/34] Remove var usage --- index.js | 6 +++--- test/connection-strings.js | 12 ++++++------ test/events.js | 28 ++++++++++++++-------------- test/logging.js | 14 +++++++------- 4 files changed, 30 insertions(+), 30 deletions(-) diff --git a/index.js b/index.js index b5378f3..5354e89 100644 --- a/index.js +++ b/index.js @@ -1,8 +1,8 @@ "use strict" -var util = require('util') -var EventEmitter = require('events').EventEmitter +const util = require('util') +const EventEmitter = require('events').EventEmitter -var Pool = module.exports = function (options, Client) { +const Pool = module.exports = function (options, Client) { if (!(this instanceof Pool)) { return new Pool(options, Client) } diff --git a/test/connection-strings.js b/test/connection-strings.js index 8ee3d07..7013f28 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/events.js b/test/events.js index 44a40c6..28f3707 100644 --- a/test/events.js +++ b/test/events.js @@ -1,14 +1,14 @@ -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('../') +const expect = require('expect.js') +const EventEmitter = require('events').EventEmitter +const describe = require('mocha').describe +const it = require('mocha').it +const objectAssign = require('object-assign') +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,7 +23,7 @@ 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) { @@ -41,13 +41,13 @@ describe('events', function () { }) 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) { if (err) return done(err) release() @@ -61,7 +61,7 @@ describe('events', function () { }) 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(undefined) client.release() @@ -79,7 +79,7 @@ describe('events', function () { function mockClient (methods) { return function () { - var client = new EventEmitter() + const client = new EventEmitter() objectAssign(client, methods) return client } diff --git a/test/logging.js b/test/logging.js index 47389a5..839603b 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() From 20c4c3463424d9998dc95cee6286b73b67a98b0f Mon Sep 17 00:00:00 2001 From: "Brian M. Carlson" Date: Sat, 1 Jul 2017 02:06:09 -0400 Subject: [PATCH 13/34] Fix linting --- index.js | 16 ++++++++-------- test/bring-your-own-promise.js | 1 - test/error-handling.js | 6 ++---- test/sizing.js | 2 +- test/timeout-tests.js | 0 5 files changed, 11 insertions(+), 14 deletions(-) create mode 100644 test/timeout-tests.js diff --git a/index.js b/index.js index 5354e89..f1de052 100644 --- a/index.js +++ b/index.js @@ -1,4 +1,4 @@ -"use strict" +'use strict' const util = require('util') const EventEmitter = require('events').EventEmitter @@ -33,12 +33,12 @@ Pool.prototype._pulseQueue = function () { waiter(null, client) } -Pool.prototype._promisify = function(callback) { +Pool.prototype._promisify = function (callback) { if (callback) { return { callback: callback, result: undefined } } - let reject = undefined - let resolve = undefined + let reject + let resolve const cb = function (err, client) { err ? reject(err) : resolve(client) } @@ -50,8 +50,8 @@ Pool.prototype._promisify = function(callback) { } Pool.prototype._remove = function (client) { - this._idle = this._idle.filter(c => c != client) - this._clients = this._clients.filter(c => c != client) + this._idle = this._idle.filter(c => c !== client) + this._clients = this._clients.filter(c => c !== client) client.end() } @@ -105,7 +105,7 @@ Pool.prototype.connect = function (cb) { } client.on('error', idleListener) if (err) { - cb(err, undefined, function() { }) + cb(err, undefined, function () { }) } else { client.release = release this.emit('connect', client) @@ -117,7 +117,7 @@ Pool.prototype.connect = function (cb) { } Pool.prototype.query = function (text, values, cb) { - if (typeof values == 'function') { + if (typeof values === 'function') { cb = values values = undefined } diff --git a/test/bring-your-own-promise.js b/test/bring-your-own-promise.js index 6520de0..9682978 100644 --- a/test/bring-your-own-promise.js +++ b/test/bring-your-own-promise.js @@ -1,7 +1,6 @@ 'use strict' const co = require('co') const expect = require('expect.js') -const _ = require('lodash') const describe = require('mocha').describe const it = require('mocha').it diff --git a/test/error-handling.js b/test/error-handling.js index c1bdb42..18e8525 100644 --- a/test/error-handling.js +++ b/test/error-handling.js @@ -1,4 +1,4 @@ -"use strict" +'use strict' const co = require('co') const expect = require('expect.js') @@ -34,7 +34,7 @@ describe('pool error handling', function () { }) describe('calling release more than once', () => { - it('should throw each time', co.wrap(function* () { + it('should throw each time', co.wrap(function * () { const pool = new Pool() const client = yield pool.connect() client.release() @@ -59,7 +59,6 @@ describe('pool error handling', function () { describe('using an ended pool', () => { it('rejects all additional promises', (done) => { const pool = new Pool() - const errors = [] const promises = [] pool.end() .then(() => { @@ -76,7 +75,6 @@ describe('pool error handling', function () { it('returns an error on all additional callbacks', (done) => { const pool = new Pool() - const errors = [] pool.end(() => { pool.query('SELECT *', (err) => { expect(err).to.be.an(Error) diff --git a/test/sizing.js b/test/sizing.js index b9b6135..9e92687 100644 --- a/test/sizing.js +++ b/test/sizing.js @@ -8,7 +8,7 @@ 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 * () { + 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() diff --git a/test/timeout-tests.js b/test/timeout-tests.js new file mode 100644 index 0000000..e69de29 From cb7b8e6f080c00bcbded6399cdc088f14a81b1b2 Mon Sep 17 00:00:00 2001 From: "Brian M. Carlson" Date: Mon, 3 Jul 2017 10:27:06 -0400 Subject: [PATCH 14/34] Work on connection timeout --- test/connection-timeout.js | 15 +++++++++++++++ test/idle-timeout.js | 15 +++++++++++++++ 2 files changed, 30 insertions(+) create mode 100644 test/connection-timeout.js create mode 100644 test/idle-timeout.js diff --git a/test/connection-timeout.js b/test/connection-timeout.js new file mode 100644 index 0000000..c1dd0a0 --- /dev/null +++ b/test/connection-timeout.js @@ -0,0 +1,15 @@ +'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('connection timeout', () => { + it('should be a thing', () => { + return Promise.reject(new Error('Should support idle timeout')) + }) +}) + diff --git a/test/idle-timeout.js b/test/idle-timeout.js new file mode 100644 index 0000000..24298f4 --- /dev/null +++ b/test/idle-timeout.js @@ -0,0 +1,15 @@ +'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('idle timeout', () => { + it('should be a thing', () => { + return Promise.reject(new Error('Should support idle timeout')) + }) +}) From 8b9e1fb0a04c48181e40bc95ff71a7bd64713e83 Mon Sep 17 00:00:00 2001 From: Brian Carlson Date: Sat, 8 Jul 2017 09:46:59 -0500 Subject: [PATCH 15/34] Work on error condition tests --- index.js | 48 +++++++++++++++++++++++++++++++------- test/connection-timeout.js | 2 +- test/idle-timeout.js | 2 +- test/index.js | 17 ++++++++++++-- test/mocha.opts | 1 + 5 files changed, 58 insertions(+), 12 deletions(-) diff --git a/index.js b/index.js index f1de052..1e3c040 100644 --- a/index.js +++ b/index.js @@ -2,6 +2,8 @@ const util = require('util') const EventEmitter = require('events').EventEmitter +const NOOP = function() { } + const Pool = module.exports = function (options, Client) { if (!(this instanceof Pool)) { return new Pool(options, Client) @@ -21,16 +23,28 @@ const Pool = module.exports = function (options, Client) { util.inherits(Pool, EventEmitter) +Pool.prototype._isFull = function () { + return this._clients.length >= this.options.max +} + Pool.prototype._pulseQueue = function () { + // if we don't have any waiting, do nothing if (!this._pendingQueue.length) { return } - if (!this._idle.length) { + // if we don't have any idle clients and we have no more room do nothing + if (!this._idle.length && this._isFull()) { return } - const client = this._idle.pop() const waiter = this._pendingQueue.shift() - waiter(null, client) + if (this._idle.length) { + const client = this._idle.pop() + return waiter(null, client) + } + if (!this._isFull()) { + return this.connect(waiter) + } + throw new Error('unexpected condition') } Pool.prototype._promisify = function (callback) { @@ -52,9 +66,14 @@ Pool.prototype._promisify = function (callback) { Pool.prototype._remove = function (client) { this._idle = this._idle.filter(c => c !== client) this._clients = this._clients.filter(c => c !== client) + console.log('remove', this._clients.length) client.end() } +function release (err) { + +} + Pool.prototype.connect = function (cb) { if (this._ending) { const err = new Error('Cannot use a pool after calling end on the pool') @@ -65,9 +84,16 @@ Pool.prototype.connect = function (cb) { const result = response.result cb = response.callback this._pendingQueue.push((err, client) => { - const release = () => { + if (err) { + return cb(err, undefined, function () { }) + } + const release = (err) => { client.release = function () { throw new Error('called release twice') } - this._idle.push(client) + if (err) { + this._remove(client) + } else { + this._idle.push(client) + } this._pulseQueue() } client.release = release @@ -98,14 +124,20 @@ Pool.prototype.connect = function (cb) { this.log('connecting new client', this.options) client.connect((err) => { this.log('new client connected') - const release = () => { + const release = (err) => { client.release = function () { throw new Error('called release twice') } - this._idle.push(client) + if (err) { + this._remove(client) + } else { + this._idle.push(client) + } this._pulseQueue() } client.on('error', idleListener) if (err) { - cb(err, undefined, function () { }) + // remove the dead client from our list of clients + this._clients = this._clients.filter(c => c !== client) + cb(err, undefined, NOOP) } else { client.release = release this.emit('connect', client) diff --git a/test/connection-timeout.js b/test/connection-timeout.js index c1dd0a0..4ae5dd3 100644 --- a/test/connection-timeout.js +++ b/test/connection-timeout.js @@ -8,7 +8,7 @@ const it = require('mocha').it const Pool = require('../') describe('connection timeout', () => { - it('should be a thing', () => { + it('should be a thing', false, () => { return Promise.reject(new Error('Should support idle timeout')) }) }) diff --git a/test/idle-timeout.js b/test/idle-timeout.js index 24298f4..a387a81 100644 --- a/test/idle-timeout.js +++ b/test/idle-timeout.js @@ -9,7 +9,7 @@ const Pool = require('../') describe('idle timeout', () => { - it('should be a thing', () => { + it('should be a thing', false, () => { return Promise.reject(new Error('Should support idle timeout')) }) }) diff --git a/test/index.js b/test/index.js index 0e8b7dc..3468417 100644 --- a/test/index.js +++ b/test/index.js @@ -62,12 +62,24 @@ describe('pool', function () { 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) { const pool = new Pool() pool.connect(function (err, client, release) { @@ -178,7 +190,7 @@ describe('pool', function () { }) }) - it('recovers from all errors', function () { + it.only('recovers from query errors', function () { const pool = new Pool() const errors = [] @@ -190,7 +202,8 @@ describe('pool', function () { }) return Promise.all(promises).then(() => { expect(errors).to.have.length(30) - expect(pool.idleCount).to.equal(10) + 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(res.rows).to.eql([{ name: 'hi' }]) return pool.end() diff --git a/test/mocha.opts b/test/mocha.opts index 9ffe54f..590fb86 100644 --- a/test/mocha.opts +++ b/test/mocha.opts @@ -1,3 +1,4 @@ --require test/setup.js --no-exit --bail +--timeout 10000 From 93dcadb78811d578b716fa2311a3eeac933e3e2f Mon Sep 17 00:00:00 2001 From: Brian Carlson Date: Sat, 8 Jul 2017 10:06:27 -0500 Subject: [PATCH 16/34] Remove logging --- index.js | 1 - test/index.js | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/index.js b/index.js index 1e3c040..975c305 100644 --- a/index.js +++ b/index.js @@ -66,7 +66,6 @@ Pool.prototype._promisify = function (callback) { Pool.prototype._remove = function (client) { this._idle = this._idle.filter(c => c !== client) this._clients = this._clients.filter(c => c !== client) - console.log('remove', this._clients.length) client.end() } diff --git a/test/index.js b/test/index.js index 3468417..8ece1cd 100644 --- a/test/index.js +++ b/test/index.js @@ -190,7 +190,7 @@ describe('pool', function () { }) }) - it.only('recovers from query errors', function () { + it('recovers from query errors', function () { const pool = new Pool() const errors = [] From 4d5bac8ebf55f050d509b4775c51d4998381c6ef Mon Sep 17 00:00:00 2001 From: "Brian M. Carlson" Date: Sat, 8 Jul 2017 11:15:03 -0500 Subject: [PATCH 17/34] Add connection timeout --- index.js | 51 ++++++++++++++++++++++---------------- package.json | 2 +- test/connection-timeout.js | 51 ++++++++++++++++++++++++++++++++++++-- 3 files changed, 79 insertions(+), 25 deletions(-) diff --git a/index.js b/index.js index 975c305..e08ddb6 100644 --- a/index.js +++ b/index.js @@ -69,7 +69,14 @@ Pool.prototype._remove = function (client) { client.end() } -function release (err) { +function release(client, err) { + client.release = function () { throw new Error('called release twice') } + if (err) { + this._remove(client) + } else { + this._idle.push(client) + } + this._pulseQueue() } @@ -86,18 +93,9 @@ Pool.prototype.connect = function (cb) { if (err) { return cb(err, undefined, function () { }) } - const release = (err) => { - client.release = function () { throw new Error('called release twice') } - if (err) { - this._remove(client) - } else { - this._idle.push(client) - } - this._pulseQueue() - } - client.release = release + client.release = release.bind(this, client) this.emit('acquire', client) - cb(err, client, release) + cb(err, client, client.release) }) this._pulseQueue() return result @@ -121,27 +119,36 @@ Pool.prototype.connect = function (cb) { const result = response.result cb = response.callback this.log('connecting new client', this.options) + + // connection timeout logic + let tid = undefined + let timeoutHit = false + if (this.options.connectionTimeout) { + tid = setTimeout(() => { + this.log('ending client due to timeout') + timeoutHit = true + client.connection.stream.destroy() + }, this.options.connectionTimeout) + } + client.connect((err) => { this.log('new client connected') - const release = (err) => { - client.release = function () { throw new Error('called release twice') } - if (err) { - this._remove(client) - } else { - this._idle.push(client) - } - this._pulseQueue() + 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 + client.release = release.bind(this, client) this.emit('connect', client) this.emit('acquire', client) - cb(undefined, client, release) + cb(undefined, client, client.release) } }) return result diff --git a/package.json b/package.json index 6274a43..4989885 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": "7.*", + "pg": "*", "standard": "7.1.2", "standard-format": "2.2.1" }, diff --git a/test/connection-timeout.js b/test/connection-timeout.js index 4ae5dd3..237eb1f 100644 --- a/test/connection-timeout.js +++ b/test/connection-timeout.js @@ -1,4 +1,5 @@ 'use strict' +const net = require('net') const co = require('co') const expect = require('expect.js') @@ -7,9 +8,55 @@ const it = require('mocha').it const Pool = require('../') + + describe('connection timeout', () => { - it('should be a thing', false, () => { - return Promise.reject(new Error('Should support idle 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({ connectionTimeout: 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({ connectionTimeout: 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({ connectionTimeout: 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))) }) From 947d7a8c7f5057ba66ce8d7d7b593013a28b5a5b Mon Sep 17 00:00:00 2001 From: "Brian M. Carlson" Date: Sat, 8 Jul 2017 16:51:16 -0500 Subject: [PATCH 18/34] Add idle timeout --- index.js | 31 +++++++++++++++++++++++++------ test/idle-timeout.js | 26 ++++++++++++++++++++++++-- 2 files changed, 49 insertions(+), 8 deletions(-) diff --git a/index.js b/index.js index e08ddb6..9e0d6ef 100644 --- a/index.js +++ b/index.js @@ -38,8 +38,9 @@ Pool.prototype._pulseQueue = function () { } const waiter = this._pendingQueue.shift() if (this._idle.length) { - const client = this._idle.pop() - return waiter(null, client) + const idleItem = this._idle.pop() + clearTimeout(idleItem.timeoutId) + return waiter(null, idleItem.client) } if (!this._isFull()) { return this.connect(waiter) @@ -64,20 +65,38 @@ Pool.prototype._promisify = function (callback) { } Pool.prototype._remove = function (client) { - this._idle = this._idle.filter(c => c !== client) + this._idle = this._idle.filter(item => item.client !== client) this._clients = this._clients.filter(c => c !== client) client.end() + this.emit('remove', client) +} + +class IdleItem { + constructor(client, timeoutId) { + this.client = client + this.timeoutId = timeoutId + } } function release(client, err) { client.release = function () { throw new Error('called release twice') } if (err) { this._remove(client) - } else { - this._idle.push(client) + this._pulseQueue() + return } - this._pulseQueue() + // idle timeout + let tid = undefined + if (this.options.idleTimeout) { + tid = setTimeout(() => { + this.log('remove idle client') + this._remove(client) + }, this.idleTimeout) + } + + this._idle.push(new IdleItem(client, tid)) + this._pulseQueue() } Pool.prototype.connect = function (cb) { diff --git a/test/idle-timeout.js b/test/idle-timeout.js index a387a81..f14815a 100644 --- a/test/idle-timeout.js +++ b/test/idle-timeout.js @@ -7,9 +7,31 @@ const it = require('mocha').it const Pool = require('../') +const wait = time => new Promise((resolve) => setTimeout(resolve, time)) describe('idle timeout', () => { - it('should be a thing', false, () => { - return Promise.reject(new Error('Should support idle timeout')) + it('should timeout and remove the client', (done) => { + const pool = new Pool({ idleTimeout: 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({ idleTimeout: 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) + })) }) From 87d657ddc35f00ea1674a8a47a97c941f7c32a4d Mon Sep 17 00:00:00 2001 From: "Brian M. Carlson" Date: Sat, 8 Jul 2017 17:19:44 -0500 Subject: [PATCH 19/34] Test for returning to client to pool after error fixes #48 --- index.js | 4 ++-- test/error-handling.js | 21 +++++++++++++++++++++ test/idle-timeout.js | 16 ++++++++++++++++ 3 files changed, 39 insertions(+), 2 deletions(-) diff --git a/index.js b/index.js index 9e0d6ef..810981c 100644 --- a/index.js +++ b/index.js @@ -104,7 +104,7 @@ Pool.prototype.connect = function (cb) { 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) { + if (this._clients.length >= this.options.max || this._idle.length) { const response = this._promisify(cb) const result = response.result cb = response.callback @@ -137,7 +137,7 @@ Pool.prototype.connect = function (cb) { const response = this._promisify(cb) const result = response.result cb = response.callback - this.log('connecting new client', this.options) + this.log('connecting new client') // connection timeout logic let tid = undefined diff --git a/test/error-handling.js b/test/error-handling.js index 18e8525..de68fad 100644 --- a/test/error-handling.js +++ b/test/error-handling.js @@ -111,4 +111,25 @@ describe('pool error handling', function () { }) })) }) + + 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/idle-timeout.js b/test/idle-timeout.js index f14815a..0c95936 100644 --- a/test/idle-timeout.js +++ b/test/idle-timeout.js @@ -34,4 +34,20 @@ describe('idle timeout', () => { } expect(results).to.have.length(20) })) + + it('does not time out clients which are used', co.wrap(function * () { + const pool = new Pool() + 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(1) + expect(pool.totalCount).to.equal(1) + } + expect(results).to.have.length(20) + return pool.end() + })) }) From 7d9b22a5c356557a1cf785a334c97ce8b99ecdfb Mon Sep 17 00:00:00 2001 From: "Brian M. Carlson" Date: Sat, 8 Jul 2017 20:43:37 -0500 Subject: [PATCH 20/34] Add idleTimeout support to native client --- index.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/index.js b/index.js index 810981c..cbdcd80 100644 --- a/index.js +++ b/index.js @@ -146,7 +146,8 @@ Pool.prototype.connect = function (cb) { tid = setTimeout(() => { this.log('ending client due to timeout') timeoutHit = true - client.connection.stream.destroy() + // force kill the node driver, and let libpq do its teardown + client.connection ? client.connection.stream.destroy() : client.end() }, this.options.connectionTimeout) } From 2085a6d7418c258638b5df00ac296d7f38b024f8 Mon Sep 17 00:00:00 2001 From: "Brian M. Carlson" Date: Sat, 8 Jul 2017 20:51:37 -0500 Subject: [PATCH 21/34] Add pg as peer dependency fixes #45 --- package.json | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 4989885..392375b 100644 --- a/package.json +++ b/package.json @@ -35,5 +35,8 @@ "standard": "7.1.2", "standard-format": "2.2.1" }, - "dependencies": {} + "dependencies": {}, + "peerDependencies": { + "pg": ">5.0" + } } From f356baeb353e3da113cb343732caa1c25d2a35b7 Mon Sep 17 00:00:00 2001 From: "Brian M. Carlson" Date: Sat, 8 Jul 2017 21:16:15 -0500 Subject: [PATCH 22/34] Rename properties --- .travis.yml | 2 +- index.js | 16 ++++++++-------- test/connection-timeout.js | 6 +++--- test/idle-timeout.js | 4 ++-- 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/.travis.yml b/.travis.yml index 47358a1..155d506 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,4 +10,4 @@ matrix: postgresql: "9.4" - node_js: "8" addons: - postgresql: "9.4" + postgresql: "9.6" diff --git a/index.js b/index.js index cbdcd80..00cef07 100644 --- a/index.js +++ b/index.js @@ -16,7 +16,7 @@ const Pool = module.exports = function (options, Client) { this._clients = [] this._idle = [] this._pendingQueue = [] - this._ending = false + this.ending = false this.options.max = this.options.max || this.options.poolSize || 10 } @@ -88,11 +88,11 @@ function release(client, err) { // idle timeout let tid = undefined - if (this.options.idleTimeout) { + if (this.options.idleTimeoutMillis) { tid = setTimeout(() => { this.log('remove idle client') this._remove(client) - }, this.idleTimeout) + }, this.idleTimeoutMillis) } this._idle.push(new IdleItem(client, tid)) @@ -100,7 +100,7 @@ function release(client, err) { } Pool.prototype.connect = function (cb) { - if (this._ending) { + 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) } @@ -142,13 +142,13 @@ Pool.prototype.connect = function (cb) { // connection timeout logic let tid = undefined let timeoutHit = false - if (this.options.connectionTimeout) { + 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.connectionTimeout) + }, this.options.connectionTimeoutMillis) } client.connect((err) => { @@ -198,11 +198,11 @@ Pool.prototype.query = function (text, values, cb) { } Pool.prototype.end = function (cb) { - if (this._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 + this.ending = true const promises = this._clients.map(client => client.end()) if (!cb) { return this.Promise.all(promises) diff --git a/test/connection-timeout.js b/test/connection-timeout.js index 237eb1f..68dfeb9 100644 --- a/test/connection-timeout.js +++ b/test/connection-timeout.js @@ -26,7 +26,7 @@ describe('connection timeout', () => { }) it('should callback with an error if timeout is passed', (done) => { - const pool = new Pool({ connectionTimeout: 10, port: this.port }) + 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') @@ -37,7 +37,7 @@ describe('connection timeout', () => { }) it('should reject promise with an error if timeout is passed', (done) => { - const pool = new Pool({ connectionTimeout: 10, port: this.port }) + 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') @@ -48,7 +48,7 @@ describe('connection timeout', () => { it('should handle multiple timeouts', co.wrap(function * () { const errors = [] - const pool = new Pool({ connectionTimeout: 1, port: this.port }) + const pool = new Pool({ connectionTimeoutMillis: 1, port: this.port }) for (var i = 0; i < 15; i++) { try { yield pool.connect() diff --git a/test/idle-timeout.js b/test/idle-timeout.js index 0c95936..0d90591 100644 --- a/test/idle-timeout.js +++ b/test/idle-timeout.js @@ -11,7 +11,7 @@ const wait = time => new Promise((resolve) => setTimeout(resolve, time)) describe('idle timeout', () => { it('should timeout and remove the client', (done) => { - const pool = new Pool({ idleTimeout: 10 }) + const pool = new Pool({ idleTimeoutMillis: 10 }) pool.query('SELECT NOW()') pool.on('remove', () => { expect(pool.idleCount).to.equal(0) @@ -21,7 +21,7 @@ describe('idle timeout', () => { }) it('can remove idle clients and recreate them', co.wrap(function * () { - const pool = new Pool({ idleTimeout: 1 }) + const pool = new Pool({ idleTimeoutMillis: 1 }) const results = [] for (var i = 0; i < 20; i++) { let query = pool.query('SELECT NOW()') From e1b6ea9a8d4f55c75767817d7e7ff4f7232932b7 Mon Sep 17 00:00:00 2001 From: "Brian M. Carlson" Date: Sat, 8 Jul 2017 21:17:49 -0500 Subject: [PATCH 23/34] Fix lint --- index.js | 10 +++++----- test/connection-timeout.js | 6 +++--- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/index.js b/index.js index 00cef07..cb72e27 100644 --- a/index.js +++ b/index.js @@ -2,7 +2,7 @@ const util = require('util') const EventEmitter = require('events').EventEmitter -const NOOP = function() { } +const NOOP = function () { } const Pool = module.exports = function (options, Client) { if (!(this instanceof Pool)) { @@ -72,13 +72,13 @@ Pool.prototype._remove = function (client) { } class IdleItem { - constructor(client, timeoutId) { + constructor (client, timeoutId) { this.client = client this.timeoutId = timeoutId } } -function release(client, err) { +function release (client, err) { client.release = function () { throw new Error('called release twice') } if (err) { this._remove(client) @@ -87,7 +87,7 @@ function release(client, err) { } // idle timeout - let tid = undefined + let tid if (this.options.idleTimeoutMillis) { tid = setTimeout(() => { this.log('remove idle client') @@ -140,7 +140,7 @@ Pool.prototype.connect = function (cb) { this.log('connecting new client') // connection timeout logic - let tid = undefined + let tid let timeoutHit = false if (this.options.connectionTimeoutMillis) { tid = setTimeout(() => { diff --git a/test/connection-timeout.js b/test/connection-timeout.js index 68dfeb9..0671b11 100644 --- a/test/connection-timeout.js +++ b/test/connection-timeout.js @@ -5,11 +5,11 @@ 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) => { @@ -52,7 +52,7 @@ describe('connection timeout', () => { for (var i = 0; i < 15; i++) { try { yield pool.connect() - } catch(e) { + } catch (e) { errors.push(e) } } From be8d58ba7abed54324d7f29d071708873f7be20b Mon Sep 17 00:00:00 2001 From: Brian Carlson Date: Sun, 9 Jul 2017 00:38:40 -0500 Subject: [PATCH 24/34] use strict --- test/events.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/events.js b/test/events.js index 28f3707..6b30a29 100644 --- a/test/events.js +++ b/test/events.js @@ -1,3 +1,5 @@ +'use strict' + const expect = require('expect.js') const EventEmitter = require('events').EventEmitter const describe = require('mocha').describe From 36f1c9ac5dbd94732addb719a2da46aa7bb77af4 Mon Sep 17 00:00:00 2001 From: Brian Carlson Date: Sun, 9 Jul 2017 01:10:40 -0500 Subject: [PATCH 25/34] Add draining to pool.end --- index.js | 42 ++++++++++++++++++++++++---------- test/bring-your-own-promise.js | 9 +++++--- test/ending-tests.js | 34 +++++++++++++++++++++++++++ 3 files changed, 70 insertions(+), 15 deletions(-) create mode 100644 test/ending-tests.js diff --git a/index.js b/index.js index cb72e27..f255620 100644 --- a/index.js +++ b/index.js @@ -16,6 +16,7 @@ const Pool = module.exports = function (options, Client) { this._clients = [] this._idle = [] this._pendingQueue = [] + this._endCallback = undefined this.ending = false this.options.max = this.options.max || this.options.poolSize || 10 @@ -28,8 +29,22 @@ Pool.prototype._isFull = function () { } Pool.prototype._pulseQueue = function () { + 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 + } // 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 @@ -95,7 +110,11 @@ function release (client, err) { }, this.idleTimeoutMillis) } - this._idle.push(new IdleItem(client, tid)) + if (this.ending) { + this._remove(client) + } else { + this._idle.push(new IdleItem(client, tid)) + } this._pulseQueue() } @@ -151,6 +170,7 @@ Pool.prototype.connect = function (cb) { }, this.options.connectionTimeoutMillis) } + this.log('connecting new client') client.connect((err) => { this.log('new client connected') if (tid) { @@ -181,11 +201,13 @@ Pool.prototype.query = function (text, values, cb) { } const response = this._promisify(cb) cb = response.callback - this.connect(function (err, client) { + this.connect((err, client) => { if (err) { return cb(err) } - client.query(text, values, function (err, res) { + this.log('dispatching query') + client.query(text, values, (err, res) => { + this.log('query dispatched') client.release(err) if (err) { return cb(err) @@ -198,20 +220,16 @@ Pool.prototype.query = function (text, values, cb) { } Pool.prototype.end = function (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 promises = this._clients.map(client => client.end()) - if (!cb) { - return this.Promise.all(promises) - } - this.Promise.all(promises) - .then(() => cb ? cb() : undefined) - .catch(err => { - cb(err) - }) + const promised = this._promisify(cb) + this._endCallback = promised.callback + this._pulseQueue() + return promised.result } Object.defineProperty(Pool.prototype, 'waitingCount', { diff --git a/test/bring-your-own-promise.js b/test/bring-your-own-promise.js index 9682978..f7fe3bd 100644 --- a/test/bring-your-own-promise.js +++ b/test/bring-your-own-promise.js @@ -16,14 +16,17 @@ const checkType = promise => { describe('Bring your own promise', function () { it('uses supplied promise for operations', co.wrap(function * () { const pool = new Pool({ Promise: BluebirdPromise }) - yield checkType(pool.connect()) + const client1 = yield checkType(pool.connect()) + client1.release() yield checkType(pool.query('SELECT NOW()')) - yield checkType(pool.connect()) + 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 }) + const pool = new Pool({ Promise: BluebirdPromise, port: 48484 }) yield checkType(pool.connect()) yield checkType(pool.end()) yield checkType(pool.connect()) diff --git a/test/ending-tests.js b/test/ending-tests.js new file mode 100644 index 0000000..1956b13 --- /dev/null +++ b/test/ending-tests.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') + })) +}) From bfd4f375c49a52092131b1640594183f282e951f Mon Sep 17 00:00:00 2001 From: Brian Carlson Date: Sun, 9 Jul 2017 01:30:34 -0500 Subject: [PATCH 26/34] Ensure ending pools drain properly --- index.js | 6 +++++- test/{ending-tests.js => ending.js} | 0 test/{timeout-tests.js => timeout.js} | 0 test/verify.js | 25 +++++++++++++++++++++++++ 4 files changed, 30 insertions(+), 1 deletion(-) rename test/{ending-tests.js => ending.js} (100%) rename test/{timeout-tests.js => timeout.js} (100%) create mode 100644 test/verify.js diff --git a/index.js b/index.js index f255620..7ce4a01 100644 --- a/index.js +++ b/index.js @@ -188,7 +188,11 @@ Pool.prototype.connect = function (cb) { client.release = release.bind(this, client) this.emit('connect', client) this.emit('acquire', client) - cb(undefined, client, client.release) + if (this.options.verify) { + this.options.verify(client, cb) + } else { + cb(undefined, client, client.release) + } } }) return result diff --git a/test/ending-tests.js b/test/ending.js similarity index 100% rename from test/ending-tests.js rename to test/ending.js diff --git a/test/timeout-tests.js b/test/timeout.js similarity index 100% rename from test/timeout-tests.js rename to test/timeout.js diff --git a/test/verify.js b/test/verify.js new file mode 100644 index 0000000..667dea9 --- /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 4af9ad4c251ae4fb51269b7cf52cccb65bb2e71e Mon Sep 17 00:00:00 2001 From: Brian Carlson Date: Sun, 9 Jul 2017 01:46:11 -0500 Subject: [PATCH 27/34] Remove yarn.lock --- yarn.lock | 1494 ----------------------------------------------------- 1 file changed, 1494 deletions(-) delete mode 100644 yarn.lock diff --git a/yarn.lock b/yarn.lock deleted file mode 100644 index cb9ad38..0000000 --- a/yarn.lock +++ /dev/null @@ -1,1494 +0,0 @@ -# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. -# yarn lockfile v1 - - -abbrev@1: - version "1.1.0" - resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.0.tgz#d0554c2256636e2f56e7c2e5ad183f859428d81f" - -acorn-jsx@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-3.0.1.tgz#afdf9488fb1ecefc8348f6fb22f464e32a58b36b" - 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" - -acorn@^1.0.3: - version "1.2.2" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-1.2.2.tgz#c8ce27de0acc76d896d2b1fad3df588d9e82f014" - -acorn@^3.0.4, acorn@^3.1.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-3.3.0.tgz#45e37fb39e8da3f25baee3ff5369e2bb5f22017a" - -ajv-keywords@^1.0.0: - version "1.5.1" - resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-1.5.1.tgz#314dd0a4b3368fad3dfcdc54ede6171b886daf3c" - -ajv@^4.7.0: - version "4.11.8" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-4.11.8.tgz#82ffb02b29e662ae53bdc20af15947706739c536" - 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" - -ansi-regex@^2.0.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" - -ansi-regex@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" - -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" - -ap@~0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/ap/-/ap-0.2.0.tgz#ae0942600b29912f0d2b14ec60c45e8f330b6110" - -argparse@^1.0.7: - version "1.0.9" - resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.9.tgz#73d83bc263f86e97f8cc4f6bae1b0e90a7d22c86" - dependencies: - sprintf-js "~1.0.2" - -array-union@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/array-union/-/array-union-1.0.2.tgz#9a34410e4f4e3da23dea375be5be70f24778ec39" - dependencies: - array-uniq "^1.0.1" - -array-uniq@^1.0.1: - version "1.0.3" - resolved "https://registry.yarnpkg.com/array-uniq/-/array-uniq-1.0.3.tgz#af6ac877a25cc7f74e058894753858dfdb24fdb6" - -arrify@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d" - -babel-code-frame@^6.22.0: - version "6.22.0" - resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.22.0.tgz#027620bee567a88c32561574e7fd0801d33118e4" - dependencies: - chalk "^1.1.0" - esutils "^2.0.2" - js-tokens "^3.0.0" - -babel-messages@^6.23.0: - version "6.23.0" - resolved "https://registry.yarnpkg.com/babel-messages/-/babel-messages-6.23.0.tgz#f3cdf4703858035b2a2951c6ec5edf6c62f2630e" - dependencies: - babel-runtime "^6.22.0" - -babel-runtime@^6.22.0: - version "6.23.0" - resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.23.0.tgz#0a9489f144de70efb3ce4300accdb329e2fc543b" - dependencies: - core-js "^2.4.0" - regenerator-runtime "^0.10.0" - -babel-traverse@^6.4.5, babel-traverse@^6.9.0: - version "6.25.0" - resolved "https://registry.yarnpkg.com/babel-traverse/-/babel-traverse-6.25.0.tgz#2257497e2fcd19b89edc13c4c91381f9512496f1" - dependencies: - babel-code-frame "^6.22.0" - babel-messages "^6.23.0" - babel-runtime "^6.22.0" - babel-types "^6.25.0" - babylon "^6.17.2" - debug "^2.2.0" - globals "^9.0.0" - invariant "^2.2.0" - lodash "^4.2.0" - -babel-types@^6.25.0: - version "6.25.0" - resolved "https://registry.yarnpkg.com/babel-types/-/babel-types-6.25.0.tgz#70afb248d5660e5d18f811d91c8303b54134a18e" - dependencies: - babel-runtime "^6.22.0" - esutils "^2.0.2" - lodash "^4.2.0" - to-fast-properties "^1.0.1" - -babylon@^6.1.21, babylon@^6.17.2, babylon@^6.8.0: - version "6.17.4" - resolved "https://registry.yarnpkg.com/babylon/-/babylon-6.17.4.tgz#3e8b7402b88d22c3423e137a1577883b15ff869a" - -balanced-match@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" - -bluebird@3.4.1, bluebird@^3.0.5: - version "3.4.1" - resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.4.1.tgz#b731ddf48e2dd3bedac2e75e1215a11bcb91fa07" - -brace-expansion@^1.1.7: - version "1.1.8" - resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.8.tgz#c07b211c7c952ec1f8efd51a77ef0d1d3990a292" - dependencies: - balanced-match "^1.0.0" - concat-map "0.0.1" - -buffer-writer@1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/buffer-writer/-/buffer-writer-1.0.1.tgz#22a936901e3029afcd7547eb4487ceb697a3bf08" - -caller-path@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/caller-path/-/caller-path-0.1.0.tgz#94085ef63581ecd3daa92444a8fe94e82577751f" - 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" - -chalk@^1.0.0, chalk@^1.1.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" - 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.1" - resolved "https://registry.yarnpkg.com/circular-json/-/circular-json-0.3.1.tgz#be8b36aefccde8b3ca7aa2d6afc07a37242c0d2d" - -cli-cursor@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-1.0.2.tgz#64da3f7d56a54412e59794bd62dc35295e8f2987" - dependencies: - restore-cursor "^1.0.1" - -cli-width@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.1.0.tgz#b234ca209b29ef66fc518d9b98d5847b00edf00a" - -clone@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.2.tgz#260b7a99ebb1edfe247538175f783243cb19d149" - -co@^4.6.0: - version "4.6.0" - resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" - -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" - -commander@0.6.1: - version "0.6.1" - resolved "https://registry.yarnpkg.com/commander/-/commander-0.6.1.tgz#fa68a14f6a945d54dbbe50d8cdb3320e9e3b1a06" - -commander@2.3.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/commander/-/commander-2.3.0.tgz#fd430e889832ec353b9acd1de217c11cb3eef873" - -commander@^2.2.0, commander@^2.9.0: - version "2.10.0" - resolved "https://registry.yarnpkg.com/commander/-/commander-2.10.0.tgz#e1f5d3245de246d1a5ca04702fa1ad1bd7e405fe" - dependencies: - graceful-readlink ">= 1.0.0" - -concat-map@0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" - -concat-stream@^1.4.6, concat-stream@^1.4.7: - version "1.6.0" - resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.0.tgz#0aac662fd52be78964d5532f694784e70110acf7" - dependencies: - inherits "^2.0.3" - readable-stream "^2.2.2" - typedarray "^0.0.6" - -config-chain@~1.1.5: - version "1.1.11" - resolved "https://registry.yarnpkg.com/config-chain/-/config-chain-1.1.11.tgz#aba09747dfbe4c3e70e766a6e41586e1859fc6f2" - dependencies: - ini "^1.3.4" - proto-list "~1.2.1" - -core-js@^2.4.0: - version "2.4.1" - resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.4.1.tgz#4de911e667b0eae9124e34254b53aea6fc618d3e" - -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" - -d@1: - version "1.0.0" - resolved "https://registry.yarnpkg.com/d/-/d-1.0.0.tgz#754bb5bfe55451da69a58b94d45f4c5b0462d58f" - dependencies: - es5-ext "^0.10.9" - -debug-log@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/debug-log/-/debug-log-1.0.1.tgz#2307632d4c04382b8df8a32f70b895046d52745f" - -debug@2.2.0, debug@^2.1.1, debug@^2.1.3, debug@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/debug/-/debug-2.2.0.tgz#f87057e995b1a1f6ae6a4960664137bc56f039da" - dependencies: - ms "0.7.1" - -debug@^0.7.4: - version "0.7.4" - resolved "https://registry.yarnpkg.com/debug/-/debug-0.7.4.tgz#06e1ea8082c2cb14e39806e22e2f6f757f92af39" - -deep-is@~0.1.3: - version "0.1.3" - resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" - -defaults@^1.0.2: - version "1.0.3" - resolved "https://registry.yarnpkg.com/defaults/-/defaults-1.0.3.tgz#c656051e9817d9ff08ed881477f3fe4019f3ef7d" - 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" - 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" - -del@^2.0.2: - version "2.2.2" - resolved "https://registry.yarnpkg.com/del/-/del-2.2.2.tgz#c12c981d067846c84bcaf862cff930d907ffd1a8" - dependencies: - globby "^5.0.0" - is-path-cwd "^1.0.0" - is-path-in-cwd "^1.0.0" - object-assign "^4.0.1" - pify "^2.0.0" - pinkie-promise "^2.0.0" - rimraf "^2.2.8" - -diff@1.4.0, diff@^1.3.2: - version "1.4.0" - resolved "https://registry.yarnpkg.com/diff/-/diff-1.4.0.tgz#7f28d2eb9ee7b15a97efd89ce63dcfdaa3ccbabf" - -disparity@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/disparity/-/disparity-2.0.0.tgz#57ddacb47324ae5f58d2cc0da886db4ce9eeb718" - 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" - dependencies: - esutils "^2.0.2" - isarray "^1.0.0" - -editorconfig@^0.13.2: - version "0.13.2" - resolved "https://registry.yarnpkg.com/editorconfig/-/editorconfig-0.13.2.tgz#8e57926d9ee69ab6cb999f027c2171467acceb35" - dependencies: - bluebird "^3.0.5" - commander "^2.9.0" - lru-cache "^3.2.0" - sigmund "^1.0.1" - -es5-ext@^0.10.14, es5-ext@^0.10.9, es5-ext@~0.10.14: - version "0.10.23" - resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.23.tgz#7578b51be974207a5487821b56538c224e4e7b38" - dependencies: - es6-iterator "2" - es6-symbol "~3.1" - -es6-iterator@2, es6-iterator@^2.0.1, es6-iterator@~2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/es6-iterator/-/es6-iterator-2.0.1.tgz#8e319c9f0453bf575d374940a655920e59ca5512" - dependencies: - d "1" - es5-ext "^0.10.14" - es6-symbol "^3.1" - -es6-map@^0.1.3: - version "0.1.5" - resolved "https://registry.yarnpkg.com/es6-map/-/es6-map-0.1.5.tgz#9136e0503dcc06a301690f0bb14ff4e364e949f0" - 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" - 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, es6-symbol@^3.1, es6-symbol@^3.1.1, es6-symbol@~3.1, es6-symbol@~3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/es6-symbol/-/es6-symbol-3.1.1.tgz#bf00ef4fdab6ba1b46ecb7b629b4c7ed5715cc77" - dependencies: - d "1" - es5-ext "~0.10.14" - -es6-weak-map@^2.0.1: - version "2.0.2" - resolved "https://registry.yarnpkg.com/es6-weak-map/-/es6-weak-map-2.0.2.tgz#5e3ab32251ffd1538a1f8e5ffa1357772f92d96f" - dependencies: - d "1" - es5-ext "^0.10.14" - es6-iterator "^2.0.1" - es6-symbol "^3.1.1" - -escape-string-regexp@1.0.2, escape-string-regexp@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.2.tgz#4dbc2fe674e71949caf3fb2695ce7f2dc1d9a8d1" - -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" - -escope@^3.6.0: - version "3.6.0" - resolved "https://registry.yarnpkg.com/escope/-/escope-3.6.0.tgz#e01975e812781a163a6dadfdd80398dc64c889c3" - 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" - 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" - -esformatter-jsx@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/esformatter-jsx/-/esformatter-jsx-3.0.0.tgz#e2201e83d7988ecf4b9d95ea5174bc1db869bae6" - dependencies: - babylon "^6.1.21" - esformatter-ignore "^0.1.3" - extend "^2.0.1" - fresh-falafel "^1.2.0" - js-beautify "^1.5.10" - -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" - 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" - 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" - -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" - 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" - 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" - -esformatter@^0.9.0: - version "0.9.6" - resolved "https://registry.yarnpkg.com/esformatter/-/esformatter-0.9.6.tgz#3608aec7828deee3cd3f46e1192aeb47268a957f" - 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" - -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" - -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" - -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" - 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" - -eslint@~2.10.2: - version "2.10.2" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-2.10.2.tgz#b2309482fef043d3203365a321285e6cce01c3d7" - 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" - 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" - -esprima@^3.1.1: - version "3.1.3" - resolved "https://registry.yarnpkg.com/esprima/-/esprima-3.1.3.tgz#fdca51cee6133895e3c88d535ce49dbff62a4633" - -esprima@~1.0: - version "1.0.4" - resolved "https://registry.yarnpkg.com/esprima/-/esprima-1.0.4.tgz#9f557e08fc3b4d26ece9dd34f8fbf476b62585ad" - -esrecurse@^4.1.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.2.0.tgz#fa9568d98d3823f9a41d91e902dcab9ea6e5b163" - dependencies: - estraverse "^4.1.0" - object-assign "^4.0.1" - -estraverse@^4.1.0, estraverse@^4.1.1, estraverse@^4.2.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.2.0.tgz#0dee3fed31fcd469618ce7342099fc1afa0bdb13" - -esutils@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.2.tgz#0abf4f1caa5bcb1f7a9d8acc6dea4faaa04bac9b" - -event-emitter@~0.3.5: - version "0.3.5" - resolved "https://registry.yarnpkg.com/event-emitter/-/event-emitter-0.3.5.tgz#df8c69eef1647923c7157b9ce83840610b02cc39" - 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" - -expect.js@0.3.1: - version "0.3.1" - resolved "https://registry.yarnpkg.com/expect.js/-/expect.js-0.3.1.tgz#b0a59a0d2eff5437544ebf0ceaa6015841d09b5b" - -extend@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/extend/-/extend-2.0.1.tgz#1ee8010689e7395ff9448241c98652bc759a8260" - -fast-levenshtein@~2.0.4: - version "2.0.6" - resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" - -figures@^1.3.5: - version "1.7.0" - resolved "https://registry.yarnpkg.com/figures/-/figures-1.7.0.tgz#cbe1e3affcf1cd44b80cadfed28dc793a9701d2e" - 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" - 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" - -flat-cache@^1.2.1: - version "1.2.2" - resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-1.2.2.tgz#fa86714e72c21db88601761ecf2f555d1abc6b96" - dependencies: - circular-json "^0.3.1" - del "^2.0.2" - graceful-fs "^4.1.2" - write "^0.2.1" - -foreach@^2.0.5: - version "2.0.5" - resolved "https://registry.yarnpkg.com/foreach/-/foreach-2.0.5.tgz#0bee005018aeb260d0a3af3ae658dd0136ec1b99" - -fresh-falafel@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/fresh-falafel/-/fresh-falafel-1.2.0.tgz#5966dee95fb35d2a29b12d2f25168b17225e4b6c" - dependencies: - acorn "^1.0.3" - foreach "^2.0.5" - isarray "0.0.1" - object-keys "^1.0.6" - -fs.realpath@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" - -generate-function@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/generate-function/-/generate-function-2.0.0.tgz#6858fe7c0969b7d4e9093337647ac79f60dfbe74" - -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" - dependencies: - is-property "^1.0.0" - -generic-pool@2.4.2: - version "2.4.2" - resolved "https://registry.yarnpkg.com/generic-pool/-/generic-pool-2.4.2.tgz#886bc5bf0beb7db96e81bcbba078818de5a62683" - -generic-pool@2.4.3: - version "2.4.3" - resolved "https://registry.yarnpkg.com/generic-pool/-/generic-pool-2.4.3.tgz#780c36f69dfad05a5a045dd37be7adca11a4f6ff" - -get-stdin@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-4.0.1.tgz#b968c6b0a04384324902e8bf1a5df32579a450fe" - -get-stdin@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-5.0.1.tgz#122e161591e21ff4c52530305693f20e6393a398" - -glob@3.2.11: - version "3.2.11" - resolved "https://registry.yarnpkg.com/glob/-/glob-3.2.11.tgz#4a973f635b9190f715d10987d5c00fd2815ebe3d" - dependencies: - inherits "2" - minimatch "0.3" - -glob@^5.0.3: - version "5.0.15" - resolved "https://registry.yarnpkg.com/glob/-/glob-5.0.15.tgz#1bc936b9e02f4a603fcc222ecf7633d30b8b93b1" - 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: - version "7.1.2" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.2.tgz#c19c9df9a028702d678612384a6552404c636d15" - 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.0.0, globals@^9.2.0: - version "9.18.0" - resolved "https://registry.yarnpkg.com/globals/-/globals-9.18.0.tgz#aa3896b3e69b487f17e31ed2143d69a8e30c2d8a" - -globby@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/globby/-/globby-5.0.0.tgz#ebd84667ca0dbb330b99bcfc68eac2bc54370e0d" - dependencies: - array-union "^1.0.1" - arrify "^1.0.0" - glob "^7.0.3" - object-assign "^4.0.1" - pify "^2.0.0" - pinkie-promise "^2.0.0" - -graceful-fs@^4.1.2: - version "4.1.11" - resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.11.tgz#0e8bdfe4d1ddb8854d64e04ea7c00e2a026e5658" - -"graceful-readlink@>= 1.0.0": - version "1.0.1" - resolved "https://registry.yarnpkg.com/graceful-readlink/-/graceful-readlink-1.0.1.tgz#4cafad76bc62f02fa039b2f94e9a3dd3a391a725" - -growl@1.9.2: - version "1.9.2" - resolved "https://registry.yarnpkg.com/growl/-/growl-1.9.2.tgz#0ea7743715db8d8de2c5ede1775e1b45ac85c02f" - -has-ansi@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91" - dependencies: - ansi-regex "^2.0.0" - -ignore@^3.0.9, ignore@^3.1.2: - version "3.3.3" - resolved "https://registry.yarnpkg.com/ignore/-/ignore-3.3.3.tgz#432352e57accd87ab3110e82d3fea0e47812156d" - -imurmurhash@^0.1.4: - version "0.1.4" - resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" - -inflight@^1.0.4: - version "1.0.6" - resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" - dependencies: - once "^1.3.0" - wrappy "1" - -inherits@2, inherits@^2.0.3, inherits@~2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" - -ini@^1.3.4: - version "1.3.4" - resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.4.tgz#0537cb79daf59b59a1a517dff706c86ec039162e" - -inquirer@^0.12.0: - version "0.12.0" - resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-0.12.0.tgz#1ef2bfd63504df0bc75785fff8c2c41df12f077e" - 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.0: - version "2.2.2" - resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.2.tgz#9e1f56ac0acdb6bf303306f338be3b204ae60360" - 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" - 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" - -is-my-json-valid@^2.10.0: - version "2.16.0" - resolved "https://registry.yarnpkg.com/is-my-json-valid/-/is-my-json-valid-2.16.0.tgz#f079dd9bfdae65ee2038aae8acbc86ab109e3693" - dependencies: - generate-function "^2.0.0" - generate-object-property "^1.1.0" - jsonpointer "^4.0.0" - xtend "^4.0.0" - -is-path-cwd@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-path-cwd/-/is-path-cwd-1.0.0.tgz#d225ec23132e89edd38fda767472e62e65f1106d" - -is-path-in-cwd@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-path-in-cwd/-/is-path-in-cwd-1.0.0.tgz#6477582b8214d602346094567003be8a9eac04dc" - dependencies: - is-path-inside "^1.0.0" - -is-path-inside@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-1.0.0.tgz#fc06e5a1683fbda13de667aff717bbc10a48f37f" - dependencies: - path-is-inside "^1.0.1" - -is-property@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/is-property/-/is-property-1.0.2.tgz#57fe1c4e48474edd65b09911f26b1cd4095dda84" - -is-resolvable@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-resolvable/-/is-resolvable-1.0.0.tgz#8df57c61ea2e3c501408d100fb013cf8d6e0cc62" - dependencies: - tryit "^1.0.1" - -isarray@0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" - -isarray@^1.0.0, isarray@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" - -isexe@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" - -jade@0.26.3: - version "0.26.3" - resolved "https://registry.yarnpkg.com/jade/-/jade-0.26.3.tgz#8f10d7977d8d79f2f6ff862a81b0513ccb25686c" - dependencies: - commander "0.6.1" - mkdirp "0.3.0" - -js-beautify@^1.5.10: - version "1.6.14" - resolved "https://registry.yarnpkg.com/js-beautify/-/js-beautify-1.6.14.tgz#d3b8f7322d02b9277d58bd238264c327e58044cd" - dependencies: - config-chain "~1.1.5" - editorconfig "^0.13.2" - mkdirp "~0.5.0" - nopt "~3.0.1" - -js-tokens@^3.0.0: - version "3.0.2" - resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b" - -js-yaml@^3.5.1: - version "3.8.4" - resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.8.4.tgz#520b4564f86573ba96662af85a8cafa7b4b5a6f6" - dependencies: - argparse "^1.0.7" - esprima "^3.1.1" - -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" - 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" - -jsonpointer@^4.0.0: - version "4.0.1" - resolved "https://registry.yarnpkg.com/jsonpointer/-/jsonpointer-4.0.1.tgz#4fd92cb34e0e9db3c89c8622ecf51f9b978c6cb9" - -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" - -levn@~0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" - dependencies: - prelude-ls "~1.1.2" - type-check "~0.3.2" - -lodash@4.13.1, lodash@^4.0.0, lodash@^4.2.0, lodash@^4.3.0: - version "4.13.1" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.13.1.tgz#83e4b10913f48496d4d16fec4a560af2ee744b68" - -loose-envify@^1.0.0: - version "1.3.1" - resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.3.1.tgz#d1a8ad33fa9ce0e713d65fdd0ac8b748d478c848" - dependencies: - js-tokens "^3.0.0" - -lru-cache@2: - version "2.7.3" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-2.7.3.tgz#6d4524e8b955f95d4f5b58851ce21dd72fb4e952" - -lru-cache@^3.2.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-3.2.0.tgz#71789b3b7f5399bec8565dda38aa30d2a097efee" - dependencies: - pseudomap "^1.0.1" - -minimatch@0.3: - version "0.3.0" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-0.3.0.tgz#275d8edaac4f1bb3326472089e7949c8394699dd" - dependencies: - lru-cache "2" - sigmund "~1.0.0" - -"minimatch@2 || 3", minimatch@^3.0.4: - version "3.0.4" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" - 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" - -minimist@^1.1.0, minimist@^1.1.1: - version "1.2.0" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" - -mkdirp@0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.3.0.tgz#1bbf5ab1ba827af23575143490426455f481fe1e" - -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" - dependencies: - minimist "0.0.8" - -mocha@^2.3.3: - version "2.5.3" - resolved "https://registry.yarnpkg.com/mocha/-/mocha-2.5.3.tgz#161be5bdeb496771eb9b35745050b622b5aefc58" - dependencies: - commander "2.3.0" - debug "2.2.0" - diff "1.4.0" - escape-string-regexp "1.0.2" - glob "3.2.11" - growl "1.9.2" - jade "0.26.3" - mkdirp "0.5.1" - supports-color "1.2.0" - to-iso-string "0.0.2" - -"mout@>=0.9 <2.0": - version "1.0.0" - resolved "https://registry.yarnpkg.com/mout/-/mout-1.0.0.tgz#9bdf1d4af57d66d47cb353a6335a3281098e1501" - -mout@^0.11.0: - version "0.11.1" - resolved "https://registry.yarnpkg.com/mout/-/mout-0.11.1.tgz#ba3611df5f0e5b1ffbfd01166b8f02d1f5fa2b99" - -ms@0.7.1: - version "0.7.1" - resolved "https://registry.yarnpkg.com/ms/-/ms-0.7.1.tgz#9cd13c03adbff25b65effde7ce864ee952017098" - -multiline@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/multiline/-/multiline-1.0.2.tgz#69b1f25ff074d2828904f244ddd06b7d96ef6c93" - 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" - -nopt@~3.0.1: - version "3.0.6" - resolved "https://registry.yarnpkg.com/nopt/-/nopt-3.0.6.tgz#c6465dbf08abcd4db359317f79ac68a646b28ff9" - 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" - 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" - 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" - 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" - -object-assign@4.1.0, object-assign@^4.0.1, object-assign@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.0.tgz#7a3b3d0e98063d43f4c03f2e8ae6cd51a86883a0" - -object-keys@^1.0.6: - version "1.0.11" - resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.0.11.tgz#c54601778ad560f1142ce0e01bcca8b56d13426d" - -once@^1.3.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" - dependencies: - wrappy "1" - -onetime@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/onetime/-/onetime-1.1.0.tgz#a1f7838f8314c516f05ecefcbc4ccfe04b4ed789" - -optionator@^0.8.1: - version "0.8.2" - resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.2.tgz#364c5e409d3f4d6301d6c0b4c05bba50180aeb64" - dependencies: - 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@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3" - -os-shim@^0.1.2: - version "0.1.3" - resolved "https://registry.yarnpkg.com/os-shim/-/os-shim-0.1.3.tgz#6b62c3791cf7909ea35ed46e17658bb417cb3917" - -packet-reader@0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/packet-reader/-/packet-reader-0.2.0.tgz#819df4d010b82d5ea5671f8a1a3acf039bcd7700" - -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" - -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" - -path-parse@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.5.tgz#3c1adf871ea9cd6c9431b6ea2bd74a0ff055c4c1" - -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" - -pg-types@1.*: - version "1.12.0" - resolved "https://registry.yarnpkg.com/pg-types/-/pg-types-1.12.0.tgz#8ad3b7b897e3fd463e62de241ad5fc640b4a66f0" - dependencies: - ap "~0.2.0" - postgres-array "~1.0.0" - postgres-bytea "~1.0.0" - postgres-date "~1.0.0" - postgres-interval "^1.1.0" - -pg@5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/pg/-/pg-5.1.0.tgz#073b9b36763ad8a5478dbb85effef45e739ba9d8" - dependencies: - buffer-writer "1.0.1" - generic-pool "2.4.2" - packet-reader "0.2.0" - pg-connection-string "0.1.3" - pg-types "1.*" - pgpass "0.0.6" - semver "4.3.2" - -pgpass@0.0.6: - version "0.0.6" - resolved "https://registry.yarnpkg.com/pgpass/-/pgpass-0.0.6.tgz#f6276200d01739da1eea63138bdca35ff4bd7280" - dependencies: - split "^1.0.0" - -pify@^2.0.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" - -pinkie-promise@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa" - dependencies: - pinkie "^2.0.0" - -pinkie@^2.0.0: - version "2.0.4" - resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870" - -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" - 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" - -postgres-array@~1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/postgres-array/-/postgres-array-1.0.2.tgz#8e0b32eb03bf77a5c0a7851e0441c169a256a238" - -postgres-bytea@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/postgres-bytea/-/postgres-bytea-1.0.0.tgz#027b533c0aa890e26d172d47cf9ccecc521acd35" - -postgres-date@~1.0.0: - version "1.0.3" - resolved "https://registry.yarnpkg.com/postgres-date/-/postgres-date-1.0.3.tgz#e2d89702efdb258ff9d9cee0fe91bd06975257a8" - -postgres-interval@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/postgres-interval/-/postgres-interval-1.1.0.tgz#1031e7bac34564132862adc9eb6c6d2f3aa75bb4" - 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" - -process-nextick-args@~1.0.6: - version "1.0.7" - resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-1.0.7.tgz#150e20b756590ad3f91093f25a4f2ad8bff30ba3" - -progress@^1.1.8: - version "1.1.8" - resolved "https://registry.yarnpkg.com/progress/-/progress-1.1.8.tgz#e260c78f6161cdd9b0e56cc3e0a85de17c7a57be" - -proto-list@~1.2.1: - version "1.2.4" - resolved "https://registry.yarnpkg.com/proto-list/-/proto-list-1.2.4.tgz#212d5bfe1318306a420f6402b8e26ff39647a849" - -protochain@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/protochain/-/protochain-1.0.5.tgz#991c407e99de264aadf8f81504b5e7faf7bfa260" - -pseudomap@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3" - -readable-stream@^2.2.2: - version "2.3.3" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.3.tgz#368f2512d79f9d46fdfc71349ae7878bbc1eb95c" - dependencies: - core-util-is "~1.0.0" - inherits "~2.0.3" - isarray "~1.0.0" - process-nextick-args "~1.0.6" - safe-buffer "~5.1.1" - string_decoder "~1.0.3" - 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" - dependencies: - code-point-at "^1.0.0" - is-fullwidth-code-point "^1.0.0" - mute-stream "0.0.5" - -regenerator-runtime@^0.10.0: - version "0.10.5" - resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.10.5.tgz#336c3efc1220adcedda2c9fab67b5a7955a33658" - -repeat-string@^1.5.0: - version "1.6.1" - resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" - -require-uncached@^1.0.2: - version "1.0.3" - resolved "https://registry.yarnpkg.com/require-uncached/-/require-uncached-1.0.3.tgz#4e0d56d6c9662fd31e43011c4b95aa49955421d3" - 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" - -resolve@^1.1.5: - version "1.3.3" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.3.3.tgz#655907c3469a8680dc2de3a275a8fdd69691f0e5" - dependencies: - path-parse "^1.0.5" - -restore-cursor@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-1.0.1.tgz#34661f46886327fed2991479152252df92daa541" - dependencies: - exit-hook "^1.0.0" - onetime "^1.0.0" - -rimraf@^2.2.8: - version "2.6.1" - resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.1.tgz#c2338ec643df7a1b7fe5c54fa86f57428a55f33d" - dependencies: - glob "^7.0.5" - -rocambole-indent@^2.0.4: - version "2.0.4" - resolved "https://registry.yarnpkg.com/rocambole-indent/-/rocambole-indent-2.0.4.tgz#a18a24977ca0400b861daa4631e861dcb52d085c" - 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" - 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" - -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" - -rocambole-whitespace@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/rocambole-whitespace/-/rocambole-whitespace-1.0.0.tgz#63330949256b29941f59b190459f999c6b1d3bf9" - 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" - dependencies: - esprima "^2.1" - -rocambole@^0.3.6: - version "0.3.6" - resolved "https://registry.yarnpkg.com/rocambole/-/rocambole-0.3.6.tgz#4debbf5943144bc7b6006d95be8facc0b74352a7" - 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" - dependencies: - once "^1.3.0" - -run-parallel@^1.1.2: - version "1.1.6" - resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.1.6.tgz#29003c9a2163e01e2d2dfc90575f2c6c1d61a039" - -rx-lite@^3.1.2: - version "3.1.2" - resolved "https://registry.yarnpkg.com/rx-lite/-/rx-lite-3.1.2.tgz#19ce502ca572665f3b647b10939f97fd1615f102" - -safe-buffer@~5.1.0, safe-buffer@~5.1.1: - version "5.1.1" - resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.1.tgz#893312af69b2123def71f57889001671eeb2c853" - -semver@4.3.2, semver@^4.3.1: - version "4.3.2" - resolved "https://registry.yarnpkg.com/semver/-/semver-4.3.2.tgz#c7a07158a80bedd052355b770d82d6640f803be7" - -serializerr@^1.0.1: - version "1.0.3" - resolved "https://registry.yarnpkg.com/serializerr/-/serializerr-1.0.3.tgz#12d4c5aa1c3ffb8f6d1dc5f395aa9455569c3f91" - 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" - -sigmund@^1.0.1, sigmund@~1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/sigmund/-/sigmund-1.0.1.tgz#3ff21f198cad2175f9f3b781853fd94d0d19b590" - -slice-ansi@0.0.4: - version "0.0.4" - resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-0.0.4.tgz#edbf8903f66f7ce2f8eafd6ceed65e264c831b35" - -spawn-sync@^1.0.5: - version "1.0.15" - resolved "https://registry.yarnpkg.com/spawn-sync/-/spawn-sync-1.0.15.tgz#b00799557eb7fb0c8376c29d44e8a1ea67e57476" - dependencies: - concat-stream "^1.4.7" - os-shim "^0.1.2" - -split@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/split/-/split-1.0.0.tgz#c4395ce683abcd254bc28fe1dabb6e5c27dcffae" - 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" - -standard-engine@^4.0.0: - version "4.1.3" - resolved "https://registry.yarnpkg.com/standard-engine/-/standard-engine-4.1.3.tgz#7a31aad54f03d9f39355f43389ce0694f4094155" - 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.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/standard-format/-/standard-format-2.2.1.tgz#249ffdf7ae89c7941d357c134ac3bdf8e8cd19b0" - dependencies: - deglob "^1.0.0" - esformatter "^0.9.0" - esformatter-eol-last "^1.0.0" - esformatter-jsx "^3.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" - 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" - -string-width@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" - 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.0" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.0.tgz#030664561fc146c9423ec7d978fe2457437fe6d0" - 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" - -string_decoder@~1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.0.3.tgz#0fc67d7c141825de94282dd536bec6b9bce860ab" - 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" - 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" - 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" - 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" - -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" - -supports-color@1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-1.2.0.tgz#ff1ed1e61169d06b3cf2d588e188b18d8847e17e" - -supports-color@^1.3.1: - version "1.3.1" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-1.3.1.tgz#15758df09d8ff3b4acc307539fabe27095e1042d" - -supports-color@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" - -sync-exec@^0.5.0: - version "0.5.0" - resolved "https://registry.yarnpkg.com/sync-exec/-/sync-exec-0.5.0.tgz#3f7258e4a5ba17245381909fa6a6f6cf506e1661" - -table@^3.7.8: - version "3.8.3" - resolved "https://registry.yarnpkg.com/table/-/table-3.8.3.tgz#2bbc542f0fda9861a755d3947fefd8b3f513855f" - 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" - -through@2, through@^2.3.6: - version "2.3.8" - resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" - -to-fast-properties@^1.0.1: - version "1.0.3" - resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-1.0.3.tgz#b83571fa4d8c25b82e231b06e3a3055de4ca1a47" - -to-iso-string@0.0.2: - version "0.0.2" - resolved "https://registry.yarnpkg.com/to-iso-string/-/to-iso-string-0.0.2.tgz#4dc19e664dfccbe25bd8db508b00c6da158255d1" - -tryit@^1.0.1: - version "1.0.3" - resolved "https://registry.yarnpkg.com/tryit/-/tryit-1.0.3.tgz#393be730a9446fd1ead6da59a014308f36c289cb" - -type-check@~0.3.2: - version "0.3.2" - resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72" - dependencies: - prelude-ls "~1.1.2" - -typedarray@^0.0.6: - version "0.0.6" - resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" - -uniq@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/uniq/-/uniq-1.0.1.tgz#b31c5ae8254844a3a8281541ce2b04b865a734ff" - -user-home@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/user-home/-/user-home-2.0.0.tgz#9c70bfd8169bc1dcbf48604e0f04b8b49cde9e9f" - 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" - -which@^1.0.5, which@^1.2.4: - version "1.2.14" - resolved "https://registry.yarnpkg.com/which/-/which-1.2.14.tgz#9a87c4378f03e827cecaf1acdf56c736c01c14e5" - dependencies: - isexe "^2.0.0" - -wordwrap@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" - -wrappy@1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" - -write@^0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/write/-/write-0.2.1.tgz#5fc03828e264cea3fe91455476f7a3c566cb0757" - dependencies: - mkdirp "^0.5.1" - -xtend@^4.0.0, xtend@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af" From bfa7ae7c295efafb329eec09b8c4f9eeb7428340 Mon Sep 17 00:00:00 2001 From: Brian Carlson Date: Sun, 9 Jul 2017 01:51:03 -0500 Subject: [PATCH 28/34] Remove object-assign --- test/events.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/test/events.js b/test/events.js index 6b30a29..3d4405e 100644 --- a/test/events.js +++ b/test/events.js @@ -4,7 +4,6 @@ const expect = require('expect.js') const EventEmitter = require('events').EventEmitter const describe = require('mocha').describe const it = require('mocha').it -const objectAssign = require('object-assign') const Pool = require('../') describe('events', function () { @@ -82,7 +81,7 @@ describe('events', function () { function mockClient (methods) { return function () { const client = new EventEmitter() - objectAssign(client, methods) + Object.assign(client, methods) return client } } From 024795e6bd9f0d6cc2b87edbea44551a1d8552ee Mon Sep 17 00:00:00 2001 From: Brian Carlson Date: Sun, 9 Jul 2017 01:55:13 -0500 Subject: [PATCH 29/34] Remove node 8 --- .travis.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 155d506..e1a0ce7 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.6" From 6a2a302571e7e85a8f3013d9aa9274c8a4c6bda1 Mon Sep 17 00:00:00 2001 From: Brian Carlson Date: Tue, 11 Jul 2017 17:19:11 -0500 Subject: [PATCH 30/34] Remove closure for waiter construction --- index.js | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/index.js b/index.js index 7ce4a01..f90e5e1 100644 --- a/index.js +++ b/index.js @@ -55,7 +55,10 @@ Pool.prototype._pulseQueue = function () { if (this._idle.length) { const idleItem = this._idle.pop() clearTimeout(idleItem.timeoutId) - return waiter(null, idleItem.client) + const client = idleItem.client + client.release = release.bind(this, client) + this.emit('acquire', client) + return waiter(undefined, client, client.release) } if (!this._isFull()) { return this.connect(waiter) @@ -93,8 +96,12 @@ class IdleItem { } } +function throwOnRelease() { + throw new Error('Release called on client which has already been released to the pool.') +} + function release (client, err) { - client.release = function () { throw new Error('called release twice') } + client.release = throwOnRelease if (err) { this._remove(client) this._pulseQueue() @@ -126,15 +133,7 @@ Pool.prototype.connect = function (cb) { if (this._clients.length >= this.options.max || this._idle.length) { const response = this._promisify(cb) const result = response.result - cb = response.callback - this._pendingQueue.push((err, client) => { - if (err) { - return cb(err, undefined, function () { }) - } - client.release = release.bind(this, client) - this.emit('acquire', client) - cb(err, client, client.release) - }) + this._pendingQueue.push(response.callback) this._pulseQueue() return result } From 0692b9661d2ca0b0eacd1223f9163faa11b1c579 Mon Sep 17 00:00:00 2001 From: Brian Carlson Date: Thu, 13 Jul 2017 12:53:06 -0500 Subject: [PATCH 31/34] Ensure client.connect is never sync --- index.js | 13 ++++++++----- test/idle-timeout.js | 11 ++++++----- test/index.js | 20 ++++++++++++++++++++ 3 files changed, 34 insertions(+), 10 deletions(-) diff --git a/index.js b/index.js index f90e5e1..7c04872 100644 --- a/index.js +++ b/index.js @@ -134,7 +134,10 @@ Pool.prototype.connect = function (cb) { const response = this._promisify(cb) const result = response.result this._pendingQueue.push(response.callback) - this._pulseQueue() + // if we have idle clients schedule a pulse immediately + if (this._idle.length) { + process.nextTick(() => this._pulseQueue()) + } return result } @@ -152,9 +155,6 @@ Pool.prototype.connect = function (cb) { this.emit('error', err, client) } - const response = this._promisify(cb) - const result = response.result - cb = response.callback this.log('connecting new client') // connection timeout logic @@ -169,6 +169,9 @@ Pool.prototype.connect = function (cb) { }, this.options.connectionTimeoutMillis) } + const response = this._promisify(cb) + cb = response.callback + this.log('connecting new client') client.connect((err) => { this.log('new client connected') @@ -194,7 +197,7 @@ Pool.prototype.connect = function (cb) { } } }) - return result + return response.result } Pool.prototype.query = function (text, values, cb) { diff --git a/test/idle-timeout.js b/test/idle-timeout.js index 0d90591..68a2b78 100644 --- a/test/idle-timeout.js +++ b/test/idle-timeout.js @@ -36,14 +36,15 @@ describe('idle timeout', () => { })) it('does not time out clients which are used', co.wrap(function * () { - const pool = new Pool() + 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) + let client = yield pool.connect() expect(pool.totalCount).to.equal(1) - results.push(yield query) - yield wait(2) + 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) } diff --git a/test/index.js b/test/index.js index 8ece1cd..938b923 100644 --- a/test/index.js +++ b/test/index.js @@ -136,6 +136,26 @@ describe('pool', function () { 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 () { From e444519a097455990e2e59ebc5253174ea1d697f Mon Sep 17 00:00:00 2001 From: Brian Carlson Date: Thu, 13 Jul 2017 12:55:44 -0500 Subject: [PATCH 32/34] Fix lint --- index.js | 72 +++++++++++++++++++++++++-------------------------- test/index.js | 1 + 2 files changed, 37 insertions(+), 36 deletions(-) diff --git a/index.js b/index.js index 7c04872..9838810 100644 --- a/index.js +++ b/index.js @@ -4,6 +4,42 @@ const EventEmitter = require('events').EventEmitter const NOOP = function () { } +class IdleItem { + constructor (client, timeoutId) { + this.client = client + this.timeoutId = timeoutId + } +} + +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._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() +} + const Pool = module.exports = function (options, Client) { if (!(this instanceof Pool)) { return new Pool(options, Client) @@ -89,42 +125,6 @@ Pool.prototype._remove = function (client) { this.emit('remove', client) } -class IdleItem { - constructor (client, timeoutId) { - this.client = client - this.timeoutId = timeoutId - } -} - -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._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.connect = function (cb) { if (this.ending) { const err = new Error('Cannot use a pool after calling end on the pool') diff --git a/test/index.js b/test/index.js index 938b923..5b6147e 100644 --- a/test/index.js +++ b/test/index.js @@ -1,3 +1,4 @@ +'use strict' const expect = require('expect.js') const _ = require('lodash') From fc6c2de23d5eb8a9b481f90caa7ed75edbb5b63f Mon Sep 17 00:00:00 2001 From: Brian Carlson Date: Thu, 13 Jul 2017 13:04:13 -0500 Subject: [PATCH 33/34] Change to es6 class --- index.js | 341 +++++++++++++++++++++++++------------------------- test/index.js | 6 - 2 files changed, 170 insertions(+), 177 deletions(-) diff --git a/index.js b/index.js index 9838810..61406bb 100644 --- a/index.js +++ b/index.js @@ -5,17 +5,17 @@ const EventEmitter = require('events').EventEmitter const NOOP = function () { } class IdleItem { - constructor (client, timeoutId) { + constructor(client, timeoutId) { this.client = client this.timeoutId = timeoutId } } -function throwOnRelease () { +function throwOnRelease() { throw new Error('Release called on client which has already been released to the pool.') } -function release (client, err) { +function release(client, err) { client.release = throwOnRelease if (err) { this._remove(client) @@ -40,69 +40,7 @@ function release (client, err) { this._pulseQueue() } -const Pool = module.exports = function (options, Client) { - if (!(this instanceof Pool)) { - return new Pool(options, Client) - } - EventEmitter.call(this) - this.options = Object.assign({}, options) - this.log = this.options.log || function () { } - this.Client = this.options.Client || Client || require('pg').Client - this.Promise = this.options.Promise || global.Promise - this._clients = [] - this._idle = [] - this._pendingQueue = [] - this._endCallback = undefined - this.ending = false - - this.options.max = this.options.max || this.options.poolSize || 10 -} - -util.inherits(Pool, EventEmitter) - -Pool.prototype._isFull = function () { - return this._clients.length >= this.options.max -} - -Pool.prototype._pulseQueue = function () { - 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 - } - // 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) - return waiter(undefined, client, client.release) - } - if (!this._isFull()) { - return this.connect(waiter) - } - throw new Error('unexpected condition') -} - -Pool.prototype._promisify = function (callback) { +function promisify(Promise, callback) { if (callback) { return { callback: callback, result: undefined } } @@ -111,147 +49,208 @@ Pool.prototype._promisify = function (callback) { const cb = function (err, client) { err ? reject(err) : resolve(client) } - const result = new this.Promise(function (res, rej) { + const result = new Promise(function (res, rej) { resolve = res reject = rej }) return { callback: cb, result: result } -} -Pool.prototype._remove = function (client) { - this._idle = this._idle.filter(item => item.client !== client) - this._clients = this._clients.filter(c => c !== client) - client.end() - this.emit('remove', client) } -Pool.prototype.connect = function (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) +class Pool extends EventEmitter { + constructor(options, Client) { + super() + this.options = Object.assign({}, options) + this.log = this.options.log || function () { } + this.Client = this.options.Client || Client || require('pg').Client + this.Promise = this.options.Promise || global.Promise + this._clients = [] + this._idle = [] + this._pendingQueue = [] + this._endCallback = undefined + this.ending = false + + this.options.max = this.options.max || this.options.poolSize || 10 + } + + _isFull() { + return this._clients.length >= this.options.max } - if (this._clients.length >= this.options.max || this._idle.length) { - const response = this._promisify(cb) - const result = response.result - this._pendingQueue.push(response.callback) - // if we have idle clients schedule a pulse immediately + + _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 + } + // 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) { - process.nextTick(() => this._pulseQueue()) + const idleItem = this._idle.pop() + clearTimeout(idleItem.timeoutId) + const client = idleItem.client + client.release = release.bind(this, client) + this.emit('acquire', client) + return waiter(undefined, client, client.release) } - return result + if (!this._isFull()) { + return this.connect(waiter) + } + throw new Error('unexpected condition') } - 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) + _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) + } - this.log('connecting new 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()) + } + return result + } - // 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 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 response = this._promisify(cb) - cb = response.callback + this.log('connecting new client') - this.log('connecting new client') - client.connect((err) => { - this.log('new client connected') - if (tid) { - clearTimeout(tid) + // 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) } - 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' + + 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) } - 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) + 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 { - cb(undefined, client, client.release) + 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 -} + }) + return response.result + -Pool.prototype.query = function (text, values, cb) { - if (typeof values === 'function') { - cb = values - values = undefined } - const response = this._promisify(cb) - cb = response.callback - this.connect((err, client) => { - if (err) { - return cb(err) + + query(text, values, cb) { + if (typeof values === 'function') { + cb = values + values = undefined } - this.log('dispatching query') - client.query(text, values, (err, res) => { - this.log('query dispatched') - client.release(err) + const response = promisify(this.Promise, cb) + cb = response.callback + this.connect((err, client) => { if (err) { return cb(err) - } else { - return cb(undefined, res) } + 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) + } + }) }) - }) - return response.result -} + return response.result -Pool.prototype.end = function (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 = this._promisify(cb) - this._endCallback = promised.callback - this._pulseQueue() - return promised.result -} -Object.defineProperty(Pool.prototype, 'waitingCount', { - get: function () { + 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 } -}) -Object.defineProperty(Pool.prototype, 'idleCount', { - get: function () { + get idleCount() { return this._idle.length } -}) -Object.defineProperty(Pool.prototype, 'totalCount', { - get: function () { + get totalCount() { return this._clients.length } -}) +} +module.exports = Pool diff --git a/test/index.js b/test/index.js index 5b6147e..010d99c 100644 --- a/test/index.js +++ b/test/index.js @@ -8,12 +8,6 @@ const it = require('mocha').it const Pool = require('../') describe('pool', function () { - it('can be used as a factory function', function () { - const 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) { const pool = new Pool() From cbdbccc87412139c4efbb384ff0881df63689b88 Mon Sep 17 00:00:00 2001 From: Brian Carlson Date: Thu, 13 Jul 2017 13:07:17 -0500 Subject: [PATCH 34/34] Code cleanup & lint fixes --- index.js | 52 ++++++++++++++++++++++------------------------------ 1 file changed, 22 insertions(+), 30 deletions(-) diff --git a/index.js b/index.js index 61406bb..c317e53 100644 --- a/index.js +++ b/index.js @@ -1,21 +1,20 @@ 'use strict' -const util = require('util') const EventEmitter = require('events').EventEmitter const NOOP = function () { } class IdleItem { - constructor(client, timeoutId) { + constructor (client, timeoutId) { this.client = client this.timeoutId = timeoutId } } -function throwOnRelease() { +function throwOnRelease () { throw new Error('Release called on client which has already been released to the pool.') } -function release(client, err) { +function release (client, err) { client.release = throwOnRelease if (err) { this._remove(client) @@ -40,44 +39,43 @@ function release(client, err) { this._pulseQueue() } -function promisify(Promise, callback) { +function promisify (Promise, callback) { if (callback) { return { callback: callback, result: undefined } } - let reject - let resolve + let rej + let res const cb = function (err, client) { - err ? reject(err) : resolve(client) + err ? rej(err) : res(client) } - const result = new Promise(function (res, rej) { - resolve = res - reject = rej + const result = new Promise(function (resolve, reject) { + res = resolve + rej = reject }) return { callback: cb, result: result } - } class Pool extends EventEmitter { - constructor(options, Client) { + 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 + this._clients = [] this._idle = [] this._pendingQueue = [] this._endCallback = undefined this.ending = false - - this.options.max = this.options.max || this.options.poolSize || 10 } - _isFull() { + _isFull () { return this._clients.length >= this.options.max } - _pulseQueue() { + _pulseQueue () { this.log('pulse queue') if (this.ending) { this.log('pulse queue on ending') @@ -115,15 +113,14 @@ class Pool extends EventEmitter { throw new Error('unexpected condition') } - _remove(client) { + _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) { + 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) @@ -196,11 +193,9 @@ class Pool extends EventEmitter { } }) return response.result - - } - query(text, values, cb) { + query (text, values, cb) { if (typeof values === 'function') { cb = values values = undefined @@ -223,10 +218,9 @@ class Pool extends EventEmitter { }) }) return response.result - } - end(cb) { + end (cb) { this.log('ending') if (this.ending) { const err = new Error('Called end on pool more than once') @@ -237,19 +231,17 @@ class Pool extends EventEmitter { this._endCallback = promised.callback this._pulseQueue() return promised.result - - } - get waitingCount() { + get waitingCount () { return this._pendingQueue.length } - get idleCount() { + get idleCount () { return this._idle.length } - get totalCount() { + get totalCount () { return this._clients.length } }