diff --git a/README.md b/README.md index 412daec..dc8593e 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,45 @@ # less-tedious -Helper package for the node.js tedious SQL Server connectivity module. +Helper package for the node.js tedious SQL Server library, and accompanying mssql client library. This package provides the following... -- easy to use `query` and `modify` methods that simplifies common SQL calls. +- easy to use `query` and `modify` methods that simplifies common SQL calls - automatic connection management and pool initialisation -- promise support. No more wrapping requests in callbacks - verbose debugging info, enabled by `winston` +- retry logic which tolerates timeouts and other custom conditions of your choosing + + +## Async Retry mechanism + +The async retry handler was created to support the retry of database calls under certain conditions. +For example, when all database connections are saturated, you may want to wait 5 seconds before re-attempting. + +### Example + +```javascript +const retry = require('./retry-async') +// actual call to database wrapped in an async function... +const callDatabase = async () => { + const data = await sql.query('SELECT * FROM [SomeTable]') + return data +} +// we only want to retry timeout errors... +const onlyRetryTimeoutsPredicate = (error) => { + return error.message.contains('timeout') +} + +const retryPolicy = { + attempts: 3, + pauseTimeMs: 5000, + pauseMultiplier: 1.5 +} + +const attemptCount = 3 +try { + const data = await retry(callDatabase, retryPolicy, onlyRetryTimeoutsPredicate) + console.log(`we got some data:${data}`) +} catch (error) { + console.error(`attempted to query database ${retryPolicy.attempts} times, but all calls were unsuccessful`) +} +``` diff --git a/date.service.js b/date.service.js index b7826b9..dca3b14 100644 --- a/date.service.js +++ b/date.service.js @@ -1,6 +1,5 @@ 'use strict' const moment = require('moment') -const logger = require('winston') const gdsFullFormat = 'D MMMM YYYY' const gdsShortFormat = 'D MMM YYYY' @@ -63,7 +62,6 @@ const dateService = { checkAndFormat: function (date, format) { if (!(date instanceof Date || moment.isMoment(date))) { - logger.warn(`Date parameter is not a Date or Moment object: ${date}`) return '' } const m = moment(date) diff --git a/example-config.js b/example-config.js index 3ce071f..b1ad7b1 100644 --- a/example-config.js +++ b/example-config.js @@ -1,4 +1,6 @@ 'use strict' + +require('dotenv').config() const oneMinuteInMilliseconds = 60000 module.exports = { diff --git a/http-example.js b/http-example.js new file mode 100644 index 0000000..6623fcc --- /dev/null +++ b/http-example.js @@ -0,0 +1,20 @@ +'use strict' + +require('dotenv').config() +const express = require('express') +const sqlService = require('./index') +const sqlConfig = require('./example-config') + +sqlService.initPool(sqlConfig) + +const app = express() +const port = 3000 + +const callDatabase = async (res) => { + const sql = 'SELECT TOP 1 * FROM [mtc_admin].[settings]' + await sqlService.queryWithRetry(sql) + res.sendStatus(200) +} + +app.get('/', (req, res) => callDatabase(res)) +app.listen(port, () => console.log(`express listening on port ${port}`)) diff --git a/index.js b/index.js index 52f6cb9..ac0e397 100644 --- a/index.js +++ b/index.js @@ -4,7 +4,6 @@ const R = require('ramda') const moment = require('moment') let cache = {} const mssql = require('mssql') -const logger = require('winston') const dateService = require('./date.service') let pool @@ -18,7 +17,6 @@ let pool * @return {string | undefined} */ const findDataType = (type) => Object.keys(sqlService.TYPES).find(k => { - logger.debug(`findDataType('${type}'): called`) if (type.toUpperCase() === k.toUpperCase()) { return k } @@ -150,7 +148,6 @@ async function generateParams (tableName, data) { options.length = cacheData.maxLength } - logger.debug(`sql.service: generateParams: options set for [${column}]`, options) params.push({ name: column, value, @@ -203,7 +200,6 @@ sqlService.initPool = async (sqlConfig) => { throw new Error('sqlConfig is required') } if (pool) { - logger.warn('The connection pool has already been initialised') return } validateSqlConfig(sqlConfig) @@ -218,19 +214,21 @@ sqlService.initPool = async (sqlConfig) => { max: sqlConfig.Pooling.MaxCount || 5, min: sqlConfig.Pooling.MinCount || 0, idleTimeoutMillis: sqlConfig.Pooling.IdleTimeout || 30000 + }, + options: { + encrypt: sqlConfig.Encrypt } } + pool = new mssql.ConnectionPool(config) - pool.on('error', err => { - logger.error('SQL Pool Error:', err) - }) + // TODO emit error + pool.on('error', () => {}) return pool.connect() } sqlService.drainPool = async () => { await pool if (!pool) { - logger.warn('The connection pool is not initialised') return } return pool.close() @@ -262,6 +260,19 @@ function addParamsToRequestSimple (params, request) { } } +const retry = require('./retry-async') +const retryConfig = { + attempts: 3, + pauseTimeMs: 5000, + pauseMultiplier: 1.5 +} +const connectionLimitReachedErrorCode = 10928 + +const dbLimitReached = (error) => { + // https://docs.microsoft.com/en-us/azure/sql-database/sql-database-develop-error-messages + return error.number === connectionLimitReachedErrorCode // || error.message.indexOf('request limit') !== -1 +} + /** * Query data from SQL Server via mssql * @param {string} sql - The SELECT statement to execute @@ -269,32 +280,16 @@ function addParamsToRequestSimple (params, request) { * @return {Promise<*>} */ sqlService.query = async (sql, params = []) => { - logger.debug(`sql.service.query(): ${sql}`) - logger.debug('sql.service.query(): Params ', R.map(R.pick(['name', 'value']), params)) await pool - const request = new mssql.Request(pool) - addParamsToRequestSimple(params, request) - - let result - try { - result = await request.query(sql) - } catch (error) { - logger.error('sqlService.query(): SQL Query threw an error', error) - if (error.code && (error.code === 'ECONNCLOSED' || error.code === 'ESOCKET')) { - logger.error('sqlService.query(): An SQL request was attempted but the connection is closed', error) - } - try { - logger.error('sqlService.query(): SQL RETRY', error) - const retryRequest = new mssql.Request(pool) - addParamsToRequestSimple(params, retryRequest) - result = await retryRequest.query(sql) - } catch (error2) { - logger.error('sqlService.query(): SQL RETRY FAILED', error2) - throw error2 - } + const query = async () => { + const request = new mssql.Request(pool) + addParamsToRequestSimple(params, request) + const result = await request.query(sql) + return sqlService.transformResult(result) } - return sqlService.transformResult(result) + + return retry(query, retryConfig, dbLimitReached) } /** @@ -317,9 +312,6 @@ function addParamsToRequest (params, request) { options.scale = param.scale || 5 } const opts = param.options ? param.options : options - if (opts && Object.keys(opts).length) { - logger.debug('sql.service: addParamsToRequest(): opts to addParameter are: ', opts) - } if (opts.precision) { request.input(param.name, param.type(opts.precision, opts.scale), param.value) @@ -339,39 +331,24 @@ function addParamsToRequest (params, request) { * @return {Promise} */ sqlService.modify = async (sql, params = []) => { - logger.debug('sql.service.modify(): SQL: ' + sql) - logger.debug('sql.service.modify(): Params ', R.map(R.pick(['name', 'value']), params)) await pool - const request = new mssql.Request(pool) - addParamsToRequest(params, request) + const modify = async () => { + const request = new mssql.Request(pool) + addParamsToRequest(params, request) + return request.query(sql) + } + const returnValue = {} const insertIds = [] let rawResponse - try { - rawResponse = await request.query(sql) - logger.debug('sql.service.modify(): result:', rawResponse) - } catch (error) { - logger.error('sqlService.modify(): SQL Query threw an error', error) - if (error.code && (error.code === 'ECONNCLOSED' || error.code === 'ESOCKET')) { - logger.error('sqlService.modify(): An SQL request was attempted but the connection is closed', error) - } - try { - logger.error('sqlService.modify(): attempting SQL retry', error) - const retryRequest = new mssql.Request(pool) - addParamsToRequest(params, retryRequest) - rawResponse = await retryRequest.query(sql) - logger.error('sqlService.modify(): SQL retry success', error) - logger.debug('sql.service: modify: result:', rawResponse) - } catch (error2) { - logger.error('sqlService.modify(): SQL RETRY FAILED', error2) - throw error2 - } - } + rawResponse = await retry(modify, retryConfig, dbLimitReached) if (rawResponse && rawResponse.recordset) { for (let obj of rawResponse.recordset) { + /* TODO remove this strict column name limitation and + extract column value regardless of name */ if (obj && obj.SCOPE_IDENTITY) { insertIds.push(obj.SCOPE_IDENTITY) } @@ -404,7 +381,12 @@ sqlService.findOneById = async (table, id, schema = '[mtc_admin]') => { FROM ${schema}.${table} WHERE id = @id ` - const rows = await sqlService.query(sql, [paramId]) + + const query = async () => { + return sqlService.query(sql, [paramId]) + } + + const rows = await retry(query, retryConfig, dbLimitReached) return R.head(rows) } @@ -423,12 +405,9 @@ sqlService.getCacheEntryForColumn = async function (table, column) { await sqlService.updateDataTypeCache() } if (!cache.hasOwnProperty(key)) { - logger.debug(`sql.service: cache miss for ${key}`) return undefined } const cacheData = cache[key] - logger.debug(`sql.service: cache hit for ${key}`) - logger.debug('sql.service: cache', cacheData) return cacheData } @@ -440,7 +419,6 @@ sqlService.getCacheEntryForColumn = async function (table, column) { */ sqlService.generateInsertStatement = async (table, data, schema = '[mtc_admin]') => { const params = await generateParams(table, data) - logger.debug('sql.service: Params ', R.compose(R.map(R.pick(['name', 'value'])))(params)) const sql = ` INSERT INTO ${schema}.${table} ( ${extractColumns(data)} ) VALUES ( ${createParamIdentifiers(data)} ); SELECT SCOPE_IDENTITY() AS [SCOPE_IDENTITY]` @@ -476,7 +454,6 @@ sqlService.generateMultipleInsertStatements = async (table, data, schema = '[mtc ) }) params = R.flatten(params) - logger.debug('sql.service: Params ', R.compose(R.map(R.pick(['name', 'value'])))(params)) const sql = ` INSERT INTO ${schema}.${table} ( ${headers} ) VALUES ( ${values} ); SELECT SCOPE_IDENTITY()` @@ -520,13 +497,11 @@ sqlService.create = async (tableName, data) => { params, outputParams } = await sqlService.generateInsertStatement(tableName, preparedData) - try { - const res = await sqlService.modify(sql, params, outputParams) - return res - } catch (error) { - logger.warn('sql.service: Failed to INSERT', error) - throw error + + const create = async () => { + return sqlService.modify(sql, params, outputParams) } + return retry(create, retryConfig, dbLimitReached) } /** @@ -563,7 +538,6 @@ sqlService.updateDataTypeCache = async function () { maxLength: row.CHARACTER_MAXIMUM_LENGTH && row.CHARACTER_MAXIMUM_LENGTH > 0 ? row.CHARACTER_MAXIMUM_LENGTH : undefined } }) - logger.debug('sql.service: updateDataTypeCache() complete') } /** @@ -584,13 +558,12 @@ sqlService.update = async function (tableName, data) { sql, params } = await sqlService.generateUpdateStatement(tableName, preparedData) - try { - const res = await sqlService.modify(sql, params) - return res - } catch (error) { - logger.warn('sql.service: Failed to UPDATE', error) - throw error + + const update = async () => { + return sqlService.modify(sql, params) } + + return retry(update, retryConfig, dbLimitReached) } /** @@ -647,7 +620,8 @@ BEGIN CATCH ); END CATCH ` - return sqlService.modify(wrappedSQL, params) + const modify = async () => sqlService.modify(wrappedSQL, params) + return retry(modify, retryConfig, dbLimitReached) } module.exports = sqlService diff --git a/package.json b/package.json index 3a25edf..e2f64e5 100644 --- a/package.json +++ b/package.json @@ -10,17 +10,19 @@ "author": "Guy Harwood, Jon Shipley, George Chatzigiannis", "license": "GPL-3.0", "dependencies": { + "dotenv": "^7.0.0", + "express": "^4.16.4", "moment": "^2.22.2", "mssql": "^5.0.3", "ramda": "^0.25.0", - "uuid": "^3.3.2", - "winston": "^3.0.0" + "uuid": "^3.3.2" }, "devDependencies": { "jasmine-console-reporter": "^3.0.0", "standard": "^12.0.1" }, "scripts": { - "lint": "standard" + "lint": "standard", + "http": "node ./http-example.js" } } diff --git a/retry-async.js b/retry-async.js new file mode 100644 index 0000000..345ba90 --- /dev/null +++ b/retry-async.js @@ -0,0 +1,53 @@ +'use strict' + +const pause = (duration) => new Promise(res => setTimeout(res, duration), noReject => undefined) +const defaultRetryCondition = () => true +const defaultConfiguration = { + attempts: 3, + pauseTimeMs: 5000, + pauseMultiplier: 1.5 +} + +/** + * @param {function} asyncRetryableFunction - the function to execute with retry behaviour + * @param {object} retryConfiguration - the behaviour of the retry policy. Default settings are provided + * @param {function(Error):Boolean} retryCondition - predicate function to determine if the function should be retried. Defaults to true + */ +const asyncRetryHandler = async (asyncRetryableFunction, retryConfiguration = defaultConfiguration, retryCondition = defaultRetryCondition) => { + let retryPolicy = {} + try { + Object.assign(retryPolicy, retryConfiguration) + const result = await asyncRetryableFunction() + return result + } catch (error) { + if (retryPolicy.attempts > 1 && retryCondition(error)) { + await pause(retryPolicy.pauseTimeMs) + retryPolicy.attempts -= 1 + retryPolicy.pauseTimeMs *= retryConfiguration.pauseMultiplier + await asyncRetryHandler(asyncRetryableFunction, retryPolicy, retryCondition) + } else { + throw error + } + } +} + +/** + * @param {number} attempts - the total number of attempts to make at calling the function successfully + * @param {function} asyncFn - the async function to attempt + * @param {function(error:Error)} condition - a predicate that allows you to retry only in certain conditions. Receives the error argument. For example - this is useful when you only want to retry a database timeout error, but not a syntax error. + */ +const asyncRetryHandlerV1 = async (attempts, asyncFn, condition = defaultRetryCondition) => { + try { + const result = await asyncFn() + return result + } catch (error) { + if (attempts > 1 && condition(error)) { + await pause(defaultConfiguration.pauseTimeMs) + await asyncRetryHandler(attempts - 1, asyncFn, condition) + } else { + throw error + } + } +} + +module.exports = asyncRetryHandler diff --git a/spec/integration/retry-async.spec.js b/spec/integration/retry-async.spec.js new file mode 100644 index 0000000..6c5fb7d --- /dev/null +++ b/spec/integration/retry-async.spec.js @@ -0,0 +1,99 @@ +'use strict' +/* global describe beforeAll it expect fail xit spyOn */ + +const retry = require('../../retry-async') +const sqlConfig = require('../../example-config') +const sql = require('mssql') + +const retryConfiguration = { + attempts: 3, + pauseTimeMs: 100, + pauseMultiplier: 1.5 +} + +describe('retry:integration', () => { + beforeAll(async () => { + const config = { + user: sqlConfig.Application.Username, + password: sqlConfig.Application.Password, + server: sqlConfig.Server, + database: sqlConfig.Database, + connectionTimeout: sqlConfig.connectionTimeout || 30000, + requestTimeout: sqlConfig.requestTimeout || 15000, + pool: { + max: sqlConfig.Pooling.MaxCount || 5, + min: sqlConfig.Pooling.MinCount || 0, + idleTimeoutMillis: sqlConfig.Pooling.IdleTimeout || 30000 + }, + options: { + encrypt: sqlConfig.Encrypt + } + } + await sql.connect(config) + }) + + it('should be transparent when handled method works correctly', async () => { + const handledFn = async () => { + return sql.query('SELECT * FROM Settings') + } + let settingsRows = await retry(handledFn, retryConfiguration) + expect(settingsRows).toBeDefined() + expect(settingsRows.recordset.length).toBe(1) + const record = settingsRows.recordset[0] + expect(record.loadingTimeLimit).toBe(3) + }) + + it('should retry 3 times when handled method fails', async () => { + const handledFn = async () => { + return sql.query('SELECT * FROM TableThatDoesNotExist') + } + const spy = jasmine.createSpy().and.callFake(handledFn) + try { + await retry(spy, retryConfiguration) + fail('error should have been thrown') + } catch (error) { + expect(error.message).toContain(`Invalid object name 'TableThatDoesNotExist'`) + } + expect(spy).toHaveBeenCalledTimes(3) + }) + + it('it does not retry if predicate condition is not met', async () => { + const handledFn = async () => { + return sql.query('SELECT * FROM TableThatDoesNotExist') + } + const predicate = (error) => { + // will always fail + return error.message === 'sql server is busy' + } + const spy = jasmine.createSpy().and.callFake(handledFn) + try { + await retry(spy, retryConfiguration, predicate) + fail('error should have been thrown') + } catch (error) { + expect(error.message).toContain(`Invalid object name 'TableThatDoesNotExist'`) + } + expect(spy).toHaveBeenCalledTimes(1) + }) + + it('it retries specified number of times when predicate met', async () => { + const handledFn = async () => { + return sql.query('SELECT * FROM TableThatDoesNotExist') + } + const predicate = () => { + return true + } + const spyObject = { + myFunc: handledFn + } + spyOn(spyObject, 'myFunc').and.callThrough() + try { + await retry(spyObject.myFunc, retryConfiguration, predicate) + fail('error should have been thrown') + } catch (error) { + expect(error.message).toContain(`Invalid object name 'TableThatDoesNotExist'`) + expect(spyObject.myFunc).toHaveBeenCalledTimes(3) + return + } + fail('error should have been caught') + }) +}) diff --git a/spec/integration/sql-config.js b/spec/integration/sql-config.js deleted file mode 100644 index beb448f..0000000 --- a/spec/integration/sql-config.js +++ /dev/null @@ -1,17 +0,0 @@ -'use strict' - -module.exports = { - database: process.env.SQL_DATABASE || 'mtc', - server: process.env.SQL_SERVER || 'localhost', - port: process.env.SQL_PORT || 1433, - user: process.env.SQL_APP_USER || 'mtcAdminUser', // docker default - password: process.env.SQL_APP_USER_PASSWORD || 'your-chosen*P4ssw0rd_for_dev_env!', // docker default - pool: { - min: process.env.SQL_POOL_MIN_COUNT || 0, - max: process.env.SQL_POOL_MAX_COUNT || 5 - }, - options: { - appName: process.env.SQL_APP_NAME || 'less-tedious-integration-tests', // docker default - encrypt: process.env.SQL_ENCRYPT || true - } -} diff --git a/spec/integration/sql.service.spec.js b/spec/integration/sql.service.spec.js index c655611..acdf658 100644 --- a/spec/integration/sql.service.spec.js +++ b/spec/integration/sql.service.spec.js @@ -3,7 +3,6 @@ const moment = require('moment') const R = require('ramda') -const winston = require('winston') const uuid = require('uuid/v4') const sqlConfig = require('../../example-config') @@ -59,6 +58,7 @@ describe('sql.service:integration', () => { expect(row.id).toBe(1) expect(row.loadingTimeLimit).toBeDefined() expect(row.loadingTimeLimit).toBe(3) + expect(row.questionTimeLimit).toBeDefined() expect(row.questionTimeLimit).toBe(5) }) @@ -353,7 +353,6 @@ describe('sql.service:integration', () => { tNvarchar: 'the quick brown fox' } // This will generate a warning because of the error, we can shut that up for this test - spyOn(winston, 'warn') try { await sql.create(table, param) fail('expected to throw') diff --git a/yarn.lock b/yarn.lock index 32cff96..b80e545 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7,6 +7,14 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-8.10.44.tgz#b00cf3595c6a3d75740af9768739a8125053a5a9" integrity sha512-HY3SK7egERHGUfY8p6ztXIEQWcIPHouYhCGcLAPQin7gE2G/fALFz+epnMwcxKUS6aKqTVoAFdi+t1llQd3xcw== +accepts@~1.3.5: + version "1.3.5" + resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.5.tgz#eb777df6011723a3b14e8a72c0805c8e86746bd2" + integrity sha1-63d99gEXI6OxTopywIBcjoZ0a9I= + dependencies: + mime-types "~2.1.18" + negotiator "0.6.1" + acorn-jsx@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-4.1.1.tgz#e8e41e48ea2fe0c896740610ab6a4ffd8add225e" @@ -83,6 +91,11 @@ argparse@^1.0.7: dependencies: sprintf-js "~1.0.2" +array-flatten@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" + integrity sha1-ml9pkFGx5wczKPKgCJaLZOopVdI= + array-includes@^3.0.3: version "3.0.3" resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.0.3.tgz#184b48f62d92d7452bb31b323165c7f8bd02266d" @@ -123,12 +136,6 @@ async@>=0.6.0: dependencies: lodash "^4.17.11" -async@^2.6.0: - version "2.6.1" - resolved "https://registry.yarnpkg.com/async/-/async-2.6.1.tgz#b245a23ca71930044ec53fa46aa00a3e87c6a610" - dependencies: - lodash "^4.17.10" - asynckit@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" @@ -176,6 +183,22 @@ bl@^2.0.1: readable-stream "^2.3.5" safe-buffer "^5.1.1" +body-parser@1.18.3: + version "1.18.3" + resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.18.3.tgz#5b292198ffdd553b3a0f20ded0592b956955c8b4" + integrity sha1-WykhmP/dVTs6DyDe0FkrlWlVyLQ= + dependencies: + bytes "3.0.0" + content-type "~1.0.4" + debug "2.6.9" + depd "~1.1.2" + http-errors "~1.6.3" + iconv-lite "0.4.23" + on-finished "~2.3.0" + qs "6.5.2" + raw-body "2.3.3" + type-is "~1.6.16" + brace-expansion@^1.1.7: version "1.1.11" resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" @@ -192,6 +215,11 @@ builtin-modules@^1.0.0: version "1.1.1" resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-1.1.1.tgz#270f076c5a72c02f5b65a47df94c5fe3a278892f" +bytes@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.0.0.tgz#d32815404d689699f85a4ea4fa8755dd13a96048" + integrity sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg= + caller-path@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/caller-path/-/caller-path-0.1.0.tgz#94085ef63581ecd3daa92444a8fe94e82577751f" @@ -261,45 +289,10 @@ color-convert@^1.9.0: dependencies: color-name "^1.1.1" -color-convert@^1.9.1: - version "1.9.3" - resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" - dependencies: - color-name "1.1.3" - -color-name@1.1.3, color-name@^1.0.0, color-name@^1.1.1: +color-name@^1.1.1: version "1.1.3" resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" -color-string@^1.5.2: - version "1.5.3" - resolved "https://registry.yarnpkg.com/color-string/-/color-string-1.5.3.tgz#c9bbc5f01b58b5492f3d6857459cb6590ce204cc" - dependencies: - color-name "^1.0.0" - simple-swizzle "^0.2.2" - -color@3.0.x: - version "3.0.0" - resolved "https://registry.yarnpkg.com/color/-/color-3.0.0.tgz#d920b4328d534a3ac8295d68f7bd4ba6c427be9a" - dependencies: - color-convert "^1.9.1" - color-string "^1.5.2" - -colornames@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/colornames/-/colornames-1.1.1.tgz#f8889030685c7c4ff9e2a559f5077eb76a816f96" - -colors@^1.2.1: - version "1.3.2" - resolved "https://registry.yarnpkg.com/colors/-/colors-1.3.2.tgz#2df8ff573dfbf255af562f8ce7181d6b971a359b" - -colorspace@1.1.x: - version "1.1.1" - resolved "https://registry.yarnpkg.com/colorspace/-/colorspace-1.1.1.tgz#9ac2491e1bc6f8fb690e2176814f8d091636d972" - dependencies: - color "3.0.x" - text-hex "1.0.x" - combined-stream@^1.0.6, combined-stream@~1.0.6: version "1.0.7" resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.7.tgz#2d1d24317afb8abe95d6d2c0b07b57813539d828" @@ -315,6 +308,26 @@ contains-path@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/contains-path/-/contains-path-0.1.0.tgz#fe8cf184ff6670b6baef01a9d4861a5cbec4120a" +content-disposition@0.5.2: + version "0.5.2" + resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.2.tgz#0cf68bb9ddf5f2be7961c3a85178cb85dba78cb4" + integrity sha1-DPaLud318r55YcOoUXjLhdunjLQ= + +content-type@~1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b" + integrity sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA== + +cookie-signature@1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" + integrity sha1-4wOogrNCzD7oylE6eZmXNNqzriw= + +cookie@0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.3.1.tgz#e7e0a1f9ef43b4c8ba925c5c5a96e806d16873bb" + integrity sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s= + core-util-is@1.0.2, 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" @@ -345,7 +358,7 @@ 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.6.8, debug@^2.6.9: +debug@2.6.9, debug@^2.6.8, debug@^2.6.9: version "2.6.9" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" dependencies: @@ -408,17 +421,14 @@ delayed-stream@~1.0.0: resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk= -depd@^1.1.2: +depd@^1.1.2, depd@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" -diagnostics@^1.0.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/diagnostics/-/diagnostics-1.1.1.tgz#cab6ac33df70c9d9a727490ae43ac995a769b22a" - dependencies: - colorspace "1.1.x" - enabled "1.0.x" - kuler "1.0.x" +destroy@~1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80" + integrity sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA= doctrine@1.5.0: version "1.5.0" @@ -433,6 +443,11 @@ doctrine@^2.1.0: dependencies: esutils "^2.0.2" +dotenv@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-7.0.0.tgz#a2be3cd52736673206e8a85fb5210eea29628e7c" + integrity sha512-M3NhsLbV1i6HuGzBUH8vXrtxOk+tWmzWKDMbAVSUp3Zsjm7ywFeuwrUXhmhQyRK1q5B5GGy7hcXPbj3bnfZg2g== + ecc-jsbn@~0.1.1: version "0.1.2" resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9" @@ -448,15 +463,15 @@ ecdsa-sig-formatter@1.0.11: dependencies: safe-buffer "^5.0.1" -enabled@1.0.x: - version "1.0.2" - resolved "https://registry.yarnpkg.com/enabled/-/enabled-1.0.2.tgz#965f6513d2c2d1c5f4652b64a2e3396467fc2f93" - dependencies: - env-variable "0.0.x" +ee-first@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" + integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0= -env-variable@0.0.x: - version "0.0.4" - resolved "https://registry.yarnpkg.com/env-variable/-/env-variable-0.0.4.tgz#0d6280cf507d84242befe35a512b5ae4be77c54e" +encodeurl@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" + integrity sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k= error-ex@^1.2.0, error-ex@^1.3.1: version "1.3.2" @@ -482,6 +497,11 @@ es-to-primitive@^1.1.1: is-date-object "^1.0.1" is-symbol "^1.0.1" +escape-html@~1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" + integrity sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg= + escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" @@ -648,6 +668,47 @@ esutils@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.2.tgz#0abf4f1caa5bcb1f7a9d8acc6dea4faaa04bac9b" +etag@~1.8.1: + version "1.8.1" + resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" + integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc= + +express@^4.16.4: + version "4.16.4" + resolved "https://registry.yarnpkg.com/express/-/express-4.16.4.tgz#fddef61926109e24c515ea97fd2f1bdbf62df12e" + integrity sha512-j12Uuyb4FMrd/qQAm6uCHAkPtO8FDTRJZBDd5D2KOL2eLaz1yUNdUB/NOIyq0iU4q4cFarsUCrnFDPBcnksuOg== + dependencies: + accepts "~1.3.5" + array-flatten "1.1.1" + body-parser "1.18.3" + content-disposition "0.5.2" + content-type "~1.0.4" + cookie "0.3.1" + cookie-signature "1.0.6" + debug "2.6.9" + depd "~1.1.2" + encodeurl "~1.0.2" + escape-html "~1.0.3" + etag "~1.8.1" + finalhandler "1.1.1" + fresh "0.5.2" + merge-descriptors "1.0.1" + methods "~1.1.2" + on-finished "~2.3.0" + parseurl "~1.3.2" + path-to-regexp "0.1.7" + proxy-addr "~2.0.4" + qs "6.5.2" + range-parser "~1.2.0" + safe-buffer "5.1.2" + send "0.16.2" + serve-static "1.13.2" + setprototypeof "1.1.0" + statuses "~1.4.0" + type-is "~1.6.16" + utils-merge "1.0.1" + vary "~1.1.2" + extend@~3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" @@ -683,14 +744,6 @@ fast-levenshtein@~2.0.4: version "2.0.6" resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" -fast-safe-stringify@^2.0.4: - version "2.0.6" - resolved "https://registry.yarnpkg.com/fast-safe-stringify/-/fast-safe-stringify-2.0.6.tgz#04b26106cc56681f51a044cfc0d76cf0008ac2c2" - -fecha@^2.3.3: - version "2.3.3" - resolved "https://registry.yarnpkg.com/fecha/-/fecha-2.3.3.tgz#948e74157df1a32fd1b12c3a3c3cdcb6ec9d96cd" - figures@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/figures/-/figures-2.0.0.tgz#3ab1a2d2a62c8bfb431a0c94cb797a2fce27c962" @@ -704,6 +757,19 @@ file-entry-cache@^2.0.0: flat-cache "^1.2.1" object-assign "^4.0.1" +finalhandler@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.1.tgz#eebf4ed840079c83f4249038c9d703008301b105" + integrity sha512-Y1GUDo39ez4aHAw7MysnUD5JzYX+WaIj8I57kO3aEPT1fFRL4sr7mjei97FgnwhAyyzRYmQZaTHb2+9uZ1dPtg== + dependencies: + debug "2.6.9" + encodeurl "~1.0.2" + escape-html "~1.0.3" + on-finished "~2.3.0" + parseurl "~1.3.2" + statuses "~1.4.0" + unpipe "~1.0.0" + find-root@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/find-root/-/find-root-1.1.0.tgz#abcfc8ba76f708c42a97b3d685b7e9450bfb9ce4" @@ -744,6 +810,16 @@ form-data@~2.3.2: combined-stream "^1.0.6" mime-types "^2.1.12" +forwarded@~0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.1.2.tgz#98c23dab1175657b8c0573e8ceccd91b0ff18c84" + integrity sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ= + +fresh@0.5.2: + version "0.5.2" + resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" + integrity sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac= + fs.realpath@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" @@ -835,6 +911,16 @@ hosted-git-info@^2.1.4: version "2.7.1" resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.7.1.tgz#97f236977bd6e125408930ff6de3eec6281ec047" +http-errors@1.6.3, http-errors@~1.6.2, http-errors@~1.6.3: + version "1.6.3" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.6.3.tgz#8b55680bb4be283a0b5bf4ea2e38580be1d9320d" + integrity sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0= + dependencies: + depd "~1.1.2" + inherits "2.0.3" + setprototypeof "1.1.0" + statuses ">= 1.4.0 < 2" + http-signature@~1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1" @@ -844,6 +930,13 @@ http-signature@~1.2.0: jsprim "^1.2.2" sshpk "^1.7.0" +iconv-lite@0.4.23: + version "0.4.23" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.23.tgz#297871f63be507adcfbfca715d0cd0eed84e9a63" + integrity sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA== + dependencies: + safer-buffer ">= 2.1.2 < 3" + iconv-lite@^0.4.17, iconv-lite@^0.4.23: version "0.4.24" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" @@ -869,7 +962,7 @@ inflight@^1.0.4: once "^1.3.0" wrappy "1" -inherits@2, inherits@^2.0.3, inherits@~2.0.3: +inherits@2, inherits@2.0.3, inherits@^2.0.3, inherits@~2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" @@ -891,14 +984,15 @@ inquirer@^5.2.0: strip-ansi "^4.0.0" through "^2.3.6" +ipaddr.js@1.8.0: + version "1.8.0" + resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.8.0.tgz#eaa33d6ddd7ace8f7f6fe0c9ca0440e706738b1e" + integrity sha1-6qM9bd16zo9/b+DJygRA5wZzix4= + is-arrayish@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" -is-arrayish@^0.3.1: - version "0.3.2" - resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.3.2.tgz#4574a2ae56f7ab206896fb431eaeed066fdf8f03" - is-builtin-module@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-builtin-module/-/is-builtin-module-1.0.0.tgz#540572d34f7ac3119f8f76c30cbc1b1e037affbe" @@ -947,10 +1041,6 @@ is-resolvable@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/is-resolvable/-/is-resolvable-1.1.0.tgz#fb18f87ce1feb925169c9a407c19318a3206ed88" -is-stream@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" - is-symbol@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.1.tgz#3cc59f00025194b6ab2e38dbae6689256b660572" @@ -1058,12 +1148,6 @@ jws@3.x.x: jwa "^1.4.1" safe-buffer "^5.0.1" -kuler@1.0.x: - version "1.0.0" - resolved "https://registry.yarnpkg.com/kuler/-/kuler-1.0.0.tgz#904ad31c373b781695854d32be33818bf1d60250" - dependencies: - colornames "^1.1.1" - levn@^0.3.0, levn@~0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" @@ -1100,49 +1184,59 @@ lodash.toarray@^4.4.0: version "4.4.0" resolved "https://registry.yarnpkg.com/lodash.toarray/-/lodash.toarray-4.4.0.tgz#24c4bfcd6b2fba38bfd0594db1179d8e9b656561" -lodash@^4.17.10, lodash@^4.17.4, lodash@^4.17.5, lodash@^4.3.0: - version "4.17.10" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.10.tgz#1b7793cf7259ea38fb3661d4d38b3260af8ae4e7" - lodash@^4.17.11: version "4.17.11" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.11.tgz#b39ea6229ef607ecd89e2c8df12536891cac9b8d" integrity sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg== +lodash@^4.17.4, lodash@^4.17.5, lodash@^4.3.0: + version "4.17.10" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.10.tgz#1b7793cf7259ea38fb3661d4d38b3260af8ae4e7" + log-symbols@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-2.2.0.tgz#5740e1c5d6f0dfda4ad9323b5332107ef6b4c40a" dependencies: chalk "^2.0.1" -logform@^1.9.0: - version "1.9.1" - resolved "https://registry.yarnpkg.com/logform/-/logform-1.9.1.tgz#58b29d7b11c332456d7a217e17b48a13ad69d60a" - dependencies: - colors "^1.2.1" - fast-safe-stringify "^2.0.4" - fecha "^2.3.3" - ms "^2.1.1" - triple-beam "^1.2.0" - loose-envify@^1.3.1: version "1.4.0" resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" dependencies: js-tokens "^3.0.0 || ^4.0.0" +media-typer@0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" + integrity sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g= + +merge-descriptors@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" + integrity sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E= + +methods@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" + integrity sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4= + mime-db@~1.38.0: version "1.38.0" resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.38.0.tgz#1a2aab16da9eb167b49c6e4df2d9c68d63d8e2ad" integrity sha512-bqVioMFFzc2awcdJZIzR3HjZFX20QhilVS7hytkKrv7xFAn8bM1gzc/FOX2awLISvWe0PV8ptFKcon+wZ5qYkg== -mime-types@^2.1.12, mime-types@~2.1.19: +mime-types@^2.1.12, mime-types@~2.1.18, mime-types@~2.1.19: version "2.1.22" resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.22.tgz#fe6b355a190926ab7698c9a0556a11199b2199bd" integrity sha512-aGl6TZGnhm/li6F7yx82bJiBZwgiEa4Hf6CNr8YO+r5UHr53tSTYZb102zyU50DOWWKeOv0uQLRL0/9EiKWCog== dependencies: mime-db "~1.38.0" +mime@1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/mime/-/mime-1.4.1.tgz#121f9ebc49e3766f311a76e1fa1c8003c4b03aa6" + integrity sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ== + mimic-fn@^1.0.0: version "1.2.0" resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.2.0.tgz#820c86a39334640e99516928bd03fca88057d022" @@ -1200,6 +1294,11 @@ natural-compare@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" +negotiator@0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.1.tgz#2b327184e8992101177b28563fb5e7102acd0ca9" + integrity sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk= + nice-try@^1.0.4: version "1.0.5" resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" @@ -1232,16 +1331,19 @@ object-keys@^1.0.12: version "1.0.12" resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.0.12.tgz#09c53855377575310cca62f55bb334abff7b3ed2" +on-finished@~2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" + integrity sha1-IPEzZIGwg811M3mSoWlxqi2QaUc= + dependencies: + ee-first "1.1.1" + once@^1.3.0: version "1.4.0" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" dependencies: wrappy "1" -one-time@0.0.4: - version "0.0.4" - resolved "https://registry.yarnpkg.com/one-time/-/one-time-0.0.4.tgz#f8cdf77884826fe4dff93e3a9cc37b1e4480742e" - onetime@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/onetime/-/onetime-2.0.1.tgz#067428230fd67443b2794b22bba528b6867962d4" @@ -1303,6 +1405,11 @@ parse-json@^4.0.0: error-ex "^1.3.1" json-parse-better-errors "^1.0.1" +parseurl@~1.3.2: + version "1.3.2" + resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.2.tgz#fc289d4ed8993119460c156253262cdc8de65bf3" + integrity sha1-/CidTtiZMRlGDBViUyYs3I3mW/M= + path-exists@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-2.1.0.tgz#0feb6c64f0fc518d9a754dd5efb62c7022761f4b" @@ -1329,6 +1436,11 @@ path-parse@^1.0.5: version "1.0.6" resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c" +path-to-regexp@0.1.7: + version "0.1.7" + resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" + integrity sha1-32BBeABfUi8V60SQ5yR6G/qmf4w= + path-type@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/path-type/-/path-type-2.0.0.tgz#f012ccb8415b7096fc2daa1054c3d72389594c73" @@ -1402,6 +1514,14 @@ prop-types@^15.6.2: loose-envify "^1.3.1" object-assign "^4.1.1" +proxy-addr@~2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.4.tgz#ecfc733bf22ff8c6f407fa275327b9ab67e48b93" + integrity sha512-5erio2h9jp5CHGwcybmxmVqHmnCBZeewlfJ0pex+UW7Qny7OOZXTtH56TGNyBizkgiOwhJtMKrVzDTeKcySZwA== + dependencies: + forwarded "~0.1.2" + ipaddr.js "1.8.0" + psl@^1.1.24: version "1.1.31" resolved "https://registry.yarnpkg.com/psl/-/psl-1.1.31.tgz#e9aa86d0101b5b105cbe93ac6b784cd547276184" @@ -1416,7 +1536,7 @@ punycode@^2.1.0: version "2.1.1" resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" -qs@~6.5.2: +qs@6.5.2, qs@~6.5.2: version "6.5.2" resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36" integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA== @@ -1425,6 +1545,21 @@ ramda@^0.25.0: version "0.25.0" resolved "https://registry.yarnpkg.com/ramda/-/ramda-0.25.0.tgz#8fdf68231cffa90bc2f9460390a0cb74a29b29a9" +range-parser@~1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.0.tgz#f49be6b487894ddc40dcc94a322f611092e00d5e" + integrity sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4= + +raw-body@2.3.3: + version "2.3.3" + resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.3.3.tgz#1b324ece6b5706e153855bc1148c65bb7f6ea0c3" + integrity sha512-9esiElv1BrZoI3rCDuOuKCBRbuApGGaDPQfjSflGxdy4oyzqghxu6klEkkVIvBje+FF0BX9coEv8KqW6X/7njw== + dependencies: + bytes "3.0.0" + http-errors "1.6.3" + iconv-lite "0.4.23" + unpipe "1.0.0" + read-pkg-up@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-2.0.0.tgz#6b72a8048984e0c41e79510fd5e9fa99b3b549be" @@ -1440,7 +1575,7 @@ read-pkg@^2.0.0: normalize-package-data "^2.3.2" path-type "^2.0.0" -readable-stream@^2.3.5, readable-stream@^2.3.6: +readable-stream@^2.3.5: version "2.3.6" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.6.tgz#b11c27d88b8ff1fbe070643cf94b0c79ae1b0aaf" dependencies: @@ -1537,7 +1672,7 @@ rxjs@^5.5.2: dependencies: symbol-observable "1.0.1" -safe-buffer@^5.0.1, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: +safe-buffer@5.1.2, safe-buffer@^5.0.1, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: version "5.1.2" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" @@ -1549,6 +1684,40 @@ safe-buffer@^5.0.1, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@~5.1.0, version "5.5.1" resolved "https://registry.yarnpkg.com/semver/-/semver-5.5.1.tgz#7dfdd8814bdb7cabc7be0fb1d734cfb66c940477" +send@0.16.2: + version "0.16.2" + resolved "https://registry.yarnpkg.com/send/-/send-0.16.2.tgz#6ecca1e0f8c156d141597559848df64730a6bbc1" + integrity sha512-E64YFPUssFHEFBvpbbjr44NCLtI1AohxQ8ZSiJjQLskAdKuriYEP6VyGEsRDH8ScozGpkaX1BGvhanqCwkcEZw== + dependencies: + debug "2.6.9" + depd "~1.1.2" + destroy "~1.0.4" + encodeurl "~1.0.2" + escape-html "~1.0.3" + etag "~1.8.1" + fresh "0.5.2" + http-errors "~1.6.2" + mime "1.4.1" + ms "2.0.0" + on-finished "~2.3.0" + range-parser "~1.2.0" + statuses "~1.4.0" + +serve-static@1.13.2: + version "1.13.2" + resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.13.2.tgz#095e8472fd5b46237db50ce486a43f4b86c6cec1" + integrity sha512-p/tdJrO4U387R9oMjb1oj7qSMaMfmOyd4j9hOFoxZe2baQszgHcSWjuya/CiT5kgZZKRudHNOA0pYXOl8rQ5nw== + dependencies: + encodeurl "~1.0.2" + escape-html "~1.0.3" + parseurl "~1.3.2" + send "0.16.2" + +setprototypeof@1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.0.tgz#d0bd85536887b6fe7c0d818cb962d9d91c54e656" + integrity sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ== + shebang-command@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" @@ -1563,12 +1732,6 @@ signal-exit@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d" -simple-swizzle@^0.2.2: - version "0.2.2" - resolved "https://registry.yarnpkg.com/simple-swizzle/-/simple-swizzle-0.2.2.tgz#a4da6b635ffcccca33f70d17cb92592de95e557a" - dependencies: - is-arrayish "^0.3.1" - slice-ansi@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-1.0.0.tgz#044f1a49d8842ff307aad6b505ed178bd950134d" @@ -1620,10 +1783,6 @@ sshpk@^1.7.0: safer-buffer "^2.0.2" tweetnacl "~0.14.0" -stack-trace@0.0.x: - version "0.0.10" - resolved "https://registry.yarnpkg.com/stack-trace/-/stack-trace-0.0.10.tgz#547c70b347e8d32b4e108ea1a2a159e5fdde19c0" - standard-engine@~9.0.0: version "9.0.0" resolved "https://registry.yarnpkg.com/standard-engine/-/standard-engine-9.0.0.tgz#d3a3d74c4c1b91f51a1e66362465261ca7610316" @@ -1647,6 +1806,16 @@ standard@^12.0.1: eslint-plugin-standard "~4.0.0" standard-engine "~9.0.0" +"statuses@>= 1.4.0 < 2": + version "1.5.0" + resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" + integrity sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow= + +statuses@~1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.4.0.tgz#bb73d446da2796106efcc1b601a253d6c46bd087" + integrity sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew== + string-width@^2.1.0, string-width@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" @@ -1727,10 +1896,6 @@ tedious@^4.2.0: readable-stream "^3.0.3" sprintf-js "^1.1.1" -text-hex@1.0.x: - version "1.0.0" - resolved "https://registry.yarnpkg.com/text-hex/-/text-hex-1.0.0.tgz#69dc9c1b17446ee79a92bf5b884bb4b9127506f5" - text-table@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" @@ -1753,10 +1918,6 @@ tough-cookie@~2.4.3: psl "^1.1.24" punycode "^1.4.1" -triple-beam@^1.2.0, triple-beam@^1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/triple-beam/-/triple-beam-1.3.0.tgz#a595214c7298db8339eeeee083e4d10bd8cb8dd9" - tunnel-agent@^0.6.0: version "0.6.0" resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" @@ -1775,6 +1936,14 @@ type-check@~0.3.2: dependencies: prelude-ls "~1.1.2" +type-is@~1.6.16: + version "1.6.16" + resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.16.tgz#f89ce341541c672b25ee7ae3c73dee3b2be50194" + integrity sha512-HRkVv/5qY2G6I8iab9cI7v1bOIdhm94dVjQCPFElW9W+3GeDOSHmy2EBYe4VTApuzolPcmgFTN3ftVJRKR2J9Q== + dependencies: + media-typer "0.3.0" + mime-types "~2.1.18" + "underscore@>= 1.3.1": version "1.9.1" resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.9.1.tgz#06dce34a0e68a7babc29b365b8e74b8925203961" @@ -1784,6 +1953,11 @@ uniq@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/uniq/-/uniq-1.0.1.tgz#b31c5ae8254844a3a8281541ce2b04b865a734ff" +unpipe@1.0.0, unpipe@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" + integrity sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw= + uri-js@^4.2.2: version "4.2.2" resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.2.2.tgz#94c540e1ff772956e2299507c010aea6c8838eb0" @@ -1794,6 +1968,11 @@ util-deprecate@^1.0.1, util-deprecate@~1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" +utils-merge@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" + integrity sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM= + uuid@^3.1.0, uuid@^3.3.2: version "3.3.2" resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.3.2.tgz#1b4af4955eb3077c501c23872fc6513811587131" @@ -1806,6 +1985,11 @@ validate-npm-package-license@^3.0.1: spdx-correct "^3.0.0" spdx-expression-parse "^3.0.0" +vary@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" + integrity sha1-IpnwLG3tMNSllhsLn3RSShj2NPw= + verror@1.10.0: version "1.10.0" resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400" @@ -1827,27 +2011,6 @@ which@^1.2.9: dependencies: isexe "^2.0.0" -winston-transport@^4.2.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/winston-transport/-/winston-transport-4.2.0.tgz#a20be89edf2ea2ca39ba25f3e50344d73e6520e5" - dependencies: - readable-stream "^2.3.6" - triple-beam "^1.2.0" - -winston@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/winston/-/winston-3.0.0.tgz#1f0b24a96586798bcf0cd149fb07ed47cb01a1b2" - dependencies: - async "^2.6.0" - diagnostics "^1.0.1" - is-stream "^1.1.0" - logform "^1.9.0" - one-time "0.0.4" - readable-stream "^2.3.6" - stack-trace "0.0.x" - triple-beam "^1.3.0" - winston-transport "^4.2.0" - wordwrap@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb"