diff --git a/lib/domain/Functions.js b/lib/domain/Functions.js new file mode 100644 index 0000000..48c3259 --- /dev/null +++ b/lib/domain/Functions.js @@ -0,0 +1,235 @@ +const crypto = require('crypto'); +const semver = require('semver'); + +const log = require('../support/log'); +const { StdoutLogStorage, DefaultLogStorage } = require('./LogStorage'); + +const Metric = require('./Metric'); + +const Pipeline = require('./Pipeline'); +const ErrorTracker = require('./ErrorTracker'); + +function codeFileName(namespace, codeId, version) { + if (version === undefined || version === 'latest') { + return `${namespace}/${codeId}.js`; + } + return `${namespace}/${codeId}/${version}.js`; +} + +class Functions { + constructor(storage, sandbox, req) { + this.storage = storage; + this.sandbox = sandbox; + this.req = req; + } + + async updateVersion(namespace, id, version) { + if (!version) { + return; + } + + // Latest versions are saved to namespace + let latest = {}; + const ns = await this.storage.getNamespace(namespace) || { namespace }; + + if (ns.latest) { + latest = ns.latest; + } + + if (Object.prototype.hasOwnProperty.call(latest, id)) { + const curVersion = latest[id]; + if (semver.gt(version, curVersion)) { + latest[id] = version; + } + } else { + latest[id] = version; + } + + ns.latest = latest; + await this.storage.putNamespace(namespace, ns); + } + + async create(namespace, id, version, code, env) { + let v = version; + const filename = codeFileName(namespace, id, version); + + if (version === 'latest') { + v = null; + } + + const invalid = this.sandbox.testSyntaxError(filename, code, { + console: new StdoutLogStorage(namespace, id, v).console, + }); + if (invalid) { + this.req.log.error(`Failed to post code: ${invalid.error}`); + return { + status: 400, + body: invalid, + }; + } + + const hash = crypto.createHash('sha1').update(code).digest('hex'); + const data = { id, version, code, hash }; + + if (env) { + data.env = env; + } + + try { + await this.storage.putCode(namespace, id, v, data); + + await this.updateVersion(namespace, id, v); + + return { + status: 200, + body: data, + }; + } catch (err) { + log.error(`[${namespace}:${id}:${version}] ${err}`); + return { + status: 500, + body: { error: err.message }, + }; + } + } + + async run(namespace, id, version) { + let v = version; + const filename = codeFileName(namespace, id, version); + const metric = new Metric('function-run'); + + const ns = await this.storage.getNamespace(namespace); + if (!ns) { + v = null; + } else if (ns.latest && version === 'latest') { + v = ns.latest[id]; + } + + const logStorage = new DefaultLogStorage(namespace, id, v, this.req); + + let code; + try { + code = await this.storage.getCodeByCache(namespace, id, v, { + preCache: (preCode) => { + preCode.script = this.sandbox.compileCode(filename, preCode.code); + return preCode; + }, + }); + + if (!code) { + const errMsg = v ? `Code '${namespace}/${id}/${v}' was not found` : `Code '${namespace}/${id}' was not found`; + return { + status: 404, + body: { error: errMsg }, + }; + } + } catch (err) { + return { + status: err.statusCode || 500, + body: { error: err.message }, + }; + } + + try { + const options = { + console: logStorage.console, + env: code.env, + }; + const result = await this.sandbox.runScript(code.script, this.req, options); + + const spent = metric.observeFunctionRun({ namespace, id, version, status: result.status }); + logStorage.flush({ + status: result.status, + requestTime: spent, + }); + return result; + } catch (err) { + logStorage.console.error(`Failed to run function: ${err}`); + logStorage.console.error(err.stack); + const status = err.statusCode || 500; + const errResult = { + status, + body: { error: err.message }, + }; + + const spent = metric.observeFunctionRun({ namespace, id, version, status }); + + const logResult = logStorage.flush({ + status, + requestTime: spent, + }); + + const { sentryDSN } = code; + + const extra = Object.assign({ body: this.req.body }, logResult || {}); + const errTracker = new ErrorTracker({ + sentryDSN, + filename, + extra, + tags: { codeHash: code.hash }, + code: code.code, + }); + errTracker.notify(err); + return errResult; + } + } + + async runPipeline(stepsInput) { + const metric = new Metric('pipeline-run'); + + const stepsPromises = stepsInput.map(async (step) => { + const [namespace, id, version] = step.split('/', 3); + const ns = await this.storage.getNamespace(namespace); + + // Return versioned function + if (version !== undefined || version !== 'latest') { + return { namespace, id, version }; + } + + // Handle latest and unversioned functions + let v = version; + if (!ns) { + v = null; + } else if (ns.latest && version === 'latest') { + v = ns.latest[id]; + } + return { namespace, id, version: v }; + }); + + const steps = await Promise.all(stepsPromises); + + try { + const codes = await this.storage.getCodesByCache(steps, { + preCache: (code) => { + const filename = codeFileName(code.namespace, code.id, code.version); + code.script = this.sandbox.compileCode(filename, code.code); + return code; + }, + }); + + for (let i = 0; i < codes.length; i += 1) { + if (!codes[i]) { + const { namespace, id, version } = steps[i]; + const codeName = version ? `${namespace}/${id}/${version}` : `${namespace}/${id}`; + const e = new Error(`Code '${codeName}' was not found`); + e.statusCode = 404; + throw e; + } + } + + const result = await new Pipeline(this.sandbox, this.req, codes).run(); + metric.observePipelineRun(result.status); + return result; + } catch (err) { + const result = { + status: err.statusCode || 500, + body: { error: err.message }, + }; + + metric.observePipelineRun(result.status); + return result; + } + } +} + +module.exports = Functions; diff --git a/lib/domain/LogStorage.js b/lib/domain/LogStorage.js index 69dcbf2..e6d4294 100644 --- a/lib/domain/LogStorage.js +++ b/lib/domain/LogStorage.js @@ -19,18 +19,18 @@ function extractHTTPFields(req) { class StdoutLogStorage { /* eslint class-methods-use-this: ["error", { "exceptMethods": ["flush"] }] */ - constructor(namespace, id) { - this.console = new PrefixLog(`namespace:${namespace}, id:${id}`); + constructor(namespace, id, version) { + this.console = new PrefixLog(`namespace:${namespace}, id:${id}, version:${version}`); } flush() {} } class GelfLogStorage { - constructor(namespace, id, req) { + constructor(namespace, id, version, req) { this.stream = new MemoryStream(config.log.maxFullMessage); this.console = new PrefixLog(null, this.stream, this.stream); - this.file = `${namespace}/${id}.js`; + this.file = `${namespace}/${id}/${version}.js`; this.namespace = namespace; this.fields = extractHTTPFields(req); this.gelfClients = GelfLogStorage.prepareGelfClients(); diff --git a/lib/domain/Metric.js b/lib/domain/Metric.js index f9aa667..260b3a4 100644 --- a/lib/domain/Metric.js +++ b/lib/domain/Metric.js @@ -24,7 +24,7 @@ const functionRunHistogram = new prometheusClient.Histogram({ name: 'backstage_functions_function_run_duration_seconds', help: 'How many time spent to run a function in seconds', buckets, - labelNames: ['namespace', 'id'], + labelNames: ['namespace', 'id', 'version'], }); const functionOverviewRunHistogram = new prometheusClient.Histogram({ @@ -61,7 +61,7 @@ const functionOverviewRunCounter = new prometheusClient.Counter({ const functionRunCounter = new prometheusClient.Counter({ name: 'backstage_functions_function_run_total', help: 'What is the status code of a function', - labelNames: ['namespace', 'id', 'status'], + labelNames: ['namespace', 'id', 'version', 'status'], }); const functionPipelineCounter = new prometheusClient.Counter({ @@ -81,15 +81,15 @@ class Metric { this.start = Date.now(); } - observeFunctionRun({ namespace, id, status }) { + observeFunctionRun({ namespace, id, version, status }) { const spent = (Date.now() - this.start) / 1000; const normalizedStatusCode = normalizeStatusCode(status); functionOverviewRunHistogram.observe(spent); functionOverviewRunCounter.labels(normalizedStatusCode).inc(); - functionRunHistogram.labels(namespace, id).observe(spent); - functionRunCounter.labels(namespace, id, normalizedStatusCode).inc(); + functionRunHistogram.labels(namespace, id, version).observe(spent); + functionRunCounter.labels(namespace, id, version, normalizedStatusCode).inc(); return spent; } diff --git a/lib/domain/Pipeline.js b/lib/domain/Pipeline.js index 7515b62..72299f7 100644 --- a/lib/domain/Pipeline.js +++ b/lib/domain/Pipeline.js @@ -4,8 +4,8 @@ const Metric = require('./Metric'); const { DefaultLogStorage } = require('./LogStorage'); const ErrorTracker = require('./ErrorTracker'); -function codeFileName(namespace, codeId) { - return `${namespace}/${codeId}.js`; +function codeFileName(namespace, codeId, version) { + return `${namespace}/${codeId}/${version}.js`; } class Pipeline { @@ -25,8 +25,8 @@ class Pipeline { this.steps = nextSteps; const metric = new Metric('function-run'); - const filename = codeFileName(step.namespace, step.id); - const logStorage = new DefaultLogStorage(step.namespace, step.id, this.req); + const filename = codeFileName(step.namespace, step.id, step.version); + const logStorage = new DefaultLogStorage(step.namespace, step.id, step.version, this.req); const options = { console: logStorage.console, @@ -38,6 +38,7 @@ class Pipeline { const spent = metric.observeFunctionRun({ namespace: step.namespace, id: step.id, + version: step.version, status: result.status, }); @@ -59,6 +60,7 @@ class Pipeline { } else { result.body.namespace = step.namespace; result.body.functionId = step.id; + result.body.version = step.version; return result; } @@ -74,6 +76,7 @@ class Pipeline { const spent = metric.observeFunctionRun({ namespace: step.namespace, id: step.id, + version: step.version, status, }); diff --git a/lib/domain/storage/InMemory.js b/lib/domain/storage/InMemory.js index 574f079..d8042d0 100644 --- a/lib/domain/storage/InMemory.js +++ b/lib/domain/storage/InMemory.js @@ -9,24 +9,31 @@ ERR_FUNCTION_NOT_FOUND.statusCode = 404; ERR_ENV_NOT_FOUND.statusCode = 404; -function storageKey(namespace, codeId) { - return `code:${namespace}/${codeId}`; +function storageKey(namespace, codeId, version) { + if (!version) { + return `code:${namespace}/${codeId}`; + } + return `code:${namespace}/${codeId}/${version}`; } -function functionInMemoryKey(namespace, id) { - return `${namespace}:${id}`; +function functionInMemoryKey(namespace, id, version) { + if (version === '' || version === undefined) { + return `${namespace}:${id}`; + } + return `${namespace}:${id}:${version}`; } function getFunctionInMemoryKey(functionStorageKey) { - const items = functionStorageKey.split(':'); + const items = functionStorageKey.split(':', 3); return { namespace: items[0], id: items[1], + version: items[2], }; } async function getMultiCodes(storage, codes, preCache) { - const keys = codes.map(({ namespace, id }) => storageKey(namespace, id)); + const keys = codes.map(({ namespace, id, version }) => storageKey(namespace, id, version)); return keys.map((key) => { if (key in storage.storage) { @@ -45,13 +52,14 @@ class StorageInMemory extends Storage { this.rangeNamespaces = []; } - async putCode(namespace, id, code) { - const key = storageKey(namespace, id); + async putCode(namespace, id, version, code) { + const key = storageKey(namespace, id, version); const storage = this.storage[key]; const updated = new Date().toISOString(); const data = { id, namespace, + version, created: (storage && storage.created) ? storage.created : updated, versionID: uuidV4(), updated, @@ -73,21 +81,20 @@ class StorageInMemory extends Storage { } this.storage[key] = data; - this.setNamespaceMember(namespace, id); + this.setNamespaceMember(namespace, id, version); return 'OK'; } - setNamespaceMember(namespace, id) { - const key = functionInMemoryKey(namespace, id); + setNamespaceMember(namespace, id, version) { + const key = functionInMemoryKey(namespace, id, version); if (!this.rangeNamespaces.includes(key)) { this.rangeNamespaces.push(key); } } - async getCode(namespace, id) { - const key = storageKey(namespace, id); - + async getCode(namespace, id, version) { + const key = storageKey(namespace, id, version); const data = this.storage[key]; if (!data || !data.code) { return null; @@ -101,6 +108,7 @@ class StorageInMemory extends Storage { const result = { id, namespace, + version, code: data.code, hash: data.hash, created: data.created, @@ -114,18 +122,17 @@ class StorageInMemory extends Storage { } result.sentryDSN = Storage.getSentryByEnvOrNamespace(result); - return result; } - async deleteCode(namespace, id) { - const key = storageKey(namespace, id); + async deleteCode(namespace, id, version) { + const key = storageKey(namespace, id, version); delete this.storage[key]; return 1; } - async getCodeByCache(namespace, id, { preCache }) { - const item = await this.getCode(namespace, id); + async getCodeByCache(namespace, id, version, { preCache }) { + const item = await this.getCode(namespace, id, version); return preCache(item); } @@ -154,7 +161,7 @@ class StorageInMemory extends Storage { return result; } - async search(namespace, id, page = 1, perPage = 10) { + async search(namespace, id, version, page = 1, perPage = 10) { const total = this.rangeNamespaces.length; const paginator = new Paginator(page, perPage, total); @@ -162,7 +169,7 @@ class StorageInMemory extends Storage { throw new Error(paginator.error); } - const functionKey = functionInMemoryKey(namespace, id || ''); + const functionKey = functionInMemoryKey(namespace, id || '', version || ''); const items = []; this.rangeNamespaces.forEach((key) => { if (key.includes(functionKey)) { @@ -199,8 +206,8 @@ class StorageInMemory extends Storage { delete this.namespaces[namespace]; } - async deleteCodeEnviromentVariable(namespace, id, env) { - const code = await this.getCode(namespace, id); + async deleteCodeEnviromentVariable(namespace, id, version, env) { + const code = await this.getCode(namespace, id, version); if (!code) { throw ERR_FUNCTION_NOT_FOUND; @@ -212,11 +219,11 @@ class StorageInMemory extends Storage { delete code.env[env]; - return this.putCode(namespace, id, { env: code.env }); + return this.putCode(namespace, id, version, { env: code.env }); } - async putCodeEnviromentVariable(namespace, id, env, value) { - const code = await this.getCode(namespace, id); + async putCodeEnviromentVariable(namespace, id, version, env, value) { + const code = await this.getCode(namespace, id, version); if (!code) { throw ERR_FUNCTION_NOT_FOUND; @@ -228,7 +235,7 @@ class StorageInMemory extends Storage { code.env[env] = value; - return this.putCode(namespace, id, { env: code.env }); + return this.putCode(namespace, id, version, { env: code.env }); } async getCodesByCache(codes, { preCache }) { diff --git a/lib/domain/storage/redis.js b/lib/domain/storage/redis.js index ff93bca..d3526dc 100644 --- a/lib/domain/storage/redis.js +++ b/lib/domain/storage/redis.js @@ -14,44 +14,50 @@ const ERR_ENV_NOT_FOUND = new Error('Env variable not found'); ERR_FUNCTION_NOT_FOUND.statusCode = 404; ERR_ENV_NOT_FOUND.statusCode = 404; -function redisKey(namespace, codeId) { - return `code:${namespace}/${codeId}`; +function redisKey(namespace, codeId, version) { + if (!version) { + return `code:${namespace}/${codeId}`; + } + return `code:${namespace}/${codeId}/${version}`; } function namespaceRedisKey(namespace) { return `namespace:${namespace}`; } -function functionRedisKey(namespace, id) { - return `${namespace}:${id}`; +function functionRedisKey(namespace, id, version) { + if (!version) { + return `${namespace}:${id}`; + } + return `${namespace}:${id}:${version}`; } function getFunctionRedisKey(functionKey) { - const items = functionKey.split(':'); + const items = functionKey.split(':', 3); return { namespace: items[0], id: items[1], + version: items[2], }; } -async function getCodeAndPopulateCache(storage, namespace, id, preCache) { - log.info('Get code from database, namespace:', namespace, - 'codeId:', id, 'and store on cache'); +async function getCodeAndPopulateCache(storage, namespace, id, version, preCache) { + log.info(`Get code from database, namespace: ${namespace} codeId: ${id} version: ${version} and store on cache`); - const code = await storage.getCode(namespace, id); + const code = await storage.getCode(namespace, id, version); if (!code) { return null; } const cacheItem = preCache(code); - const key = redisKey(namespace, id); + const key = redisKey(namespace, id, version); storage.cache[key] = cacheItem; return cacheItem; } async function getMultiCodes(storage, codes, preCache) { - const keys = codes.map(({ namespace, id }) => redisKey(namespace, id)); + const keys = codes.map(({ namespace, id, version }) => redisKey(namespace, id, version)); const pipeline = storage.client.pipeline(); for (const key of keys) { @@ -64,7 +70,7 @@ async function getMultiCodes(storage, codes, preCache) { return Promise.reject(err); } const key = keys[i]; - const { namespace, id } = codes[i]; + const { namespace, id, version } = codes[i]; const cacheItem = storage.cache[key]; if (cacheItem && cacheItem.versionID === versionID) { @@ -72,7 +78,7 @@ async function getMultiCodes(storage, codes, preCache) { } // populate the cache - return getCodeAndPopulateCache(storage, namespace, id, preCache); + return getCodeAndPopulateCache(storage, namespace, id, version, preCache); }); } @@ -158,7 +164,7 @@ class StorageRedis extends Storage { return result; } - async search(namespace, id, page = 1, perPage = 10) { + async search(namespace, id, version, page = 1, perPage = 10) { const total = await this.client.zcount('namespaces', '-inf', '+inf'); const paginator = new Paginator(page, perPage, total); @@ -167,8 +173,8 @@ class StorageRedis extends Storage { throw new Error(paginator.error); } - const minRange = `[${functionRedisKey(namespace, id || '*')}`; - const maxRange = `[${functionRedisKey(namespace, id || '\xff')}`; + const minRange = `[${functionRedisKey(namespace, id || '*', version || '*')}`; + const maxRange = `[${functionRedisKey(namespace, id || '\xff', version || '\xff')}`; const keys = await this.client.zrangebylex( 'namespaces', minRange, @@ -197,16 +203,16 @@ class StorageRedis extends Storage { return result; } - setNamespaceMember(namespace, id) { - return this.client.zadd('namespaces', 0, functionRedisKey(namespace, id)); + setNamespaceMember(namespace, id, version) { + return this.client.zadd('namespaces', 0, functionRedisKey(namespace, id, version)); } - deleteNamespaceMember(namespace, id) { - return this.client.zrem('namespaces', 0, functionRedisKey(namespace, id)); + deleteNamespaceMember(namespace, id, version) { + return this.client.zrem('namespaces', 0, functionRedisKey(namespace, id, version)); } - async putCode(namespace, id, code) { - const key = redisKey(namespace, id); + async putCode(namespace, id, version, code) { + const key = redisKey(namespace, id, version); const data = { versionID: uuidV4(), updated: new Date().toISOString() }; const functionCode = await this.client.hget(key, 'code'); @@ -223,12 +229,12 @@ class StorageRedis extends Storage { data.env = JSON.stringify(code.env); } - this.setNamespaceMember(namespace, id); + this.setNamespaceMember(namespace, id, version); return this.client.hmset(key, data); } - async getCode(namespace, id) { - const key = redisKey(namespace, id); + async getCode(namespace, id, version) { + const key = redisKey(namespace, id, version); const data = await this.client.hgetall(key); if (!data.code) { return null; @@ -242,6 +248,7 @@ class StorageRedis extends Storage { const result = { id, namespace, + version, code: data.code, hash: data.hash, created: data.created, @@ -255,20 +262,19 @@ class StorageRedis extends Storage { } result.sentryDSN = Storage.getSentryByEnvOrNamespace(result); - return result; } - deleteCode(namespace, id) { - const key = redisKey(namespace, id); + deleteCode(namespace, id, version) { + const key = redisKey(namespace, id, version); - this.deleteNamespaceMember(namespace, id); + this.deleteNamespaceMember(namespace, id, version); return this.client.del(key); } - async getCodeByCache(namespace, id, { preCache }) { - const key = redisKey(namespace, id); + async getCodeByCache(namespace, id, version, { preCache }) { + const key = redisKey(namespace, id, version); const versionID = await this.client.hget(key, 'versionID'); const cacheItem = this.cache[key]; @@ -276,15 +282,15 @@ class StorageRedis extends Storage { return cacheItem; } - return getCodeAndPopulateCache(this, namespace, id, preCache); + return getCodeAndPopulateCache(this, namespace, id, version, preCache); } async getCodesByCache(codes, { preCache }) { return Promise.all(await getMultiCodes(this, codes, preCache)); } - async putCodeEnviromentVariable(namespace, id, env, value) { - const code = await this.getCode(namespace, id); + async putCodeEnviromentVariable(namespace, id, version, env, value) { + const code = await this.getCode(namespace, id, version); if (!code) { throw ERR_FUNCTION_NOT_FOUND; } @@ -292,11 +298,11 @@ class StorageRedis extends Storage { code.env = {}; } code.env[env] = value; - return this.putCode(namespace, id, { env: code.env }); + return this.putCode(namespace, id, version, { env: code.env }); } - async deleteCodeEnviromentVariable(namespace, id, env) { - const code = await this.getCode(namespace, id); + async deleteCodeEnviromentVariable(namespace, id, version, env) { + const code = await this.getCode(namespace, id, version); if (!code) { throw ERR_FUNCTION_NOT_FOUND; } @@ -304,7 +310,7 @@ class StorageRedis extends Storage { throw ERR_ENV_NOT_FOUND; } delete code.env[env]; - return this.putCode(namespace, id, { env: code.env }); + return this.putCode(namespace, id, version, { env: code.env }); } async getNamespace(namespace) { @@ -313,11 +319,25 @@ class StorageRedis extends Storage { if (!data.namespace) { return null; } + + if (data.latest) { + try { + const latest = JSON.parse(data.latest); + data.latest = latest; + } catch (err) { + log.debug('Failed to deserialize latest info'); + } + } + return data; } putNamespace(namespace, data) { const key = namespaceRedisKey(namespace); + if (data.latest) { + const latest = JSON.stringify(data.latest); + data.latest = latest; + } return this.client.hmset(key, data); } diff --git a/lib/http/routers/FunctionsRouter.js b/lib/http/routers/FunctionsRouter.js index 3e0e2fe..25ad288 100644 --- a/lib/http/routers/FunctionsRouter.js +++ b/lib/http/routers/FunctionsRouter.js @@ -1,25 +1,17 @@ -const crypto = require('crypto'); - const Router = require('express').Router; const bodyParser = require('body-parser'); const Validator = require('jsonschema').Validator; const log = require('../../support/log'); const schemas = require('../../domain/schemas'); -const Pipeline = require('../../domain/Pipeline'); -const ErrorTracker = require('../../domain/ErrorTracker'); -const { StdoutLogStorage, DefaultLogStorage } = require('../../domain/LogStorage'); +const Functions = require('../../domain/Functions'); +const { StdoutLogStorage } = require('../../domain/LogStorage'); const FunctionsRequest = require('../FunctionsRequest'); -const Metric = require('../../domain/Metric'); const SchemaResponse = require('../SchemaResponse'); const router = new Router(); const { bodyParserLimit } = require('../../support/config'); -function codeFileName(namespace, codeId) { - return `${namespace}/${codeId}.js`; -} - router.get('/', async (req, res) => { const memoryStorage = req.app.get('memoryStorage'); const page = parseInt(req.query.page || '1', 10); @@ -30,7 +22,7 @@ router.get('/', async (req, res) => { try { let list = {}; if (namespace) { - list = await memoryStorage.search(namespace, id, page, perPage); + list = await memoryStorage.search(namespace, id, 'latest', page, perPage); } else { list = await memoryStorage.listNamespaces(page, perPage); } @@ -49,6 +41,7 @@ router.all('/:namespace/:id*', (req, res, next) => { router.put('/:namespace/:id', bodyParser.json({ limit: bodyParserLimit }), async (req, res) => { const validationResult = new Validator().validate(req.body, schemas['functions/item']); const memoryStorage = req.app.get('memoryStorage'); + const sandbox = req.app.get('sandbox'); if (!validationResult.valid) { const error = 'Invalid instance'; @@ -57,42 +50,21 @@ router.put('/:namespace/:id', bodyParser.json({ limit: bodyParserLimit }), async res.status(400).json({ error, details }); return; } - const { - namespace, - id, - } = req.params; - const { - code, - env, - } = req.body; - const filename = codeFileName(namespace, id); - const sandbox = req.app.get('sandbox'); - - const invalid = sandbox.testSyntaxError(filename, code, { - console: new StdoutLogStorage(namespace, id).console, - }); - if (invalid) { - req.log.error(`Failed to post code: ${invalid.error}`); - res.status(400).json(invalid); - return; - } + const { namespace, id } = req.params; + const { code, env } = req.body; - const hash = crypto.createHash('sha1').update(code).digest('hex'); - const data = { id, code, hash }; + const f = new Functions(memoryStorage, sandbox, req); + const result = await f.create(namespace, id, 'latest', code, env); - if (env) { - data.env = env; - } - - try { - await memoryStorage.putCode(namespace, id, data); - res.set({ ETag: data.hash }); + if (result.status === 200) { + res.status(result.status); + res.set({ ETag: result.body.hash }); const functionsRequest = new FunctionsRequest(req); - new SchemaResponse(functionsRequest, res, 'functions/item').json(data); - } catch (err) { - log.error(`[${namespace}:${id}] ${err}`); - res.status(500).json({ error: err.message }); + new SchemaResponse(functionsRequest, res, 'functions/item').json(result.body); + } else { + res.status(result.status); + res.json(result.body); } }); @@ -108,15 +80,11 @@ router.put('/:namespace/:id/env/:env', bodyParser.json({ strict: false, limit: b return; } - const { - namespace, - id, - env, - } = req.params; + const { namespace, id, env } = req.params; try { await memoryStorage - .putCodeEnviromentVariable(namespace, id, env, req.body); + .putCodeEnviromentVariable(namespace, id, 'latest', env, req.body); res.status(204).end(); } catch (err) { log.error(`[${namespace}:${id}] Failed to set enviroment variable ${env}, error: ${err}`); @@ -125,16 +93,12 @@ router.put('/:namespace/:id/env/:env', bodyParser.json({ strict: false, limit: b }); router.delete('/:namespace/:id/env/:env', async (req, res) => { - const { - namespace, - id, - env, - } = req.params; + const { namespace, id, env } = req.params; const memoryStorage = req.app.get('memoryStorage'); try { await memoryStorage - .deleteCodeEnviromentVariable(namespace, id, env); + .deleteCodeEnviromentVariable(namespace, id, 'latest', env); res.status(204).end(); } catch (err) { log.error(`[${namespace}:${id}] Failed to unset enviroment variable ${env}, error: ${err}`); @@ -143,14 +107,19 @@ router.delete('/:namespace/:id/env/:env', async (req, res) => { }); router.get('/:namespace/:id', async (req, res) => { - const { - namespace, - id, - } = req.params; + const { namespace, id } = req.params; const memoryStorage = req.app.get('memoryStorage'); try { - const code = await memoryStorage.getCode(namespace, id); + let v; + const ns = await memoryStorage.getNamespace(namespace); + if (!ns) { + v = null; + } else if (ns.latest) { + v = ns.latest[id]; + } + + const code = await memoryStorage.getCode(namespace, id, v); if (!code) { const error = 'Code not found'; req.log.error(error); @@ -171,12 +140,11 @@ router.get('/:namespace/:id', async (req, res) => { }); router.delete('/:namespace/:id', async (req, res) => { - const namespace = req.params.namespace; - const id = req.params.id; + const { namespace, id } = req.params; const memoryStorage = req.app.get('memoryStorage'); try { - await memoryStorage.deleteCode(namespace, id); + await memoryStorage.deleteCode(namespace, id, 'latest'); res.status(204).end(); } catch (err) { req.log.error(`Failed to delete code id: ${err}`); @@ -189,72 +157,15 @@ router.all('/:namespace/:id/run', bodyParser.json({ limit: bodyParserLimit }), a const { namespace, id } = req.params; const memoryStorage = req.app.get('memoryStorage'); const sandbox = req.app.get('sandbox'); - const filename = codeFileName(namespace, id); - const metric = new Metric('function-run'); - const logStorage = new DefaultLogStorage(namespace, id, req); - - let code; - - try { - code = await memoryStorage.getCodeByCache(namespace, id, { - preCache: (preCode) => { - preCode.script = sandbox.compileCode(filename, preCode.code); - return preCode; - }, - }); - if (!code) { - const error = new Error(`Code '${namespace}/${id}' is not found`); - error.statusCode = 404; - throw error; - } - } catch (err) { - res.status(err.statusCode || 500).json({ error: err.message }); - return; - } - - try { - const options = { - console: logStorage.console, - env: code.env, - }; - const result = await sandbox.runScript(code.script, req, options); + const f = new Functions(memoryStorage, sandbox, req); + const result = await f.run(namespace, id, 'latest'); + if (result.headers) { res.set(result.headers); - res.status(result.status); - res.json(result.body); - - const spent = metric.observeFunctionRun({ namespace, id, status: result.status }); - - logStorage.flush({ - status: result.status, - requestTime: spent, - }); - } catch (err) { - logStorage.console.error(`Failed to run function: ${err}`); - logStorage.console.error(err.stack); - const status = err.statusCode || 500; - res.status(status).json({ error: err.message }); - - const spent = metric.observeFunctionRun({ namespace, id, status }); - - const logResult = logStorage.flush({ - status, - requestTime: spent, - }); - - const { sentryDSN } = code; - - const extra = Object.assign({ body: req.body }, logResult || {}); - const errTracker = new ErrorTracker({ - sentryDSN, - filename, - extra, - tags: { codeHash: code.hash }, - code: code.code, - }); - errTracker.notify(err); } + res.status(result.status); + res.json(result.body); }); @@ -262,48 +173,20 @@ router.put('/pipeline', bodyParser.json({ limit: bodyParserLimit }), async (req, const memoryStorage = req.app.get('memoryStorage'); const sandbox = req.app.get('sandbox'); - let { steps } = req.query; - + const { steps } = req.query; if (!steps) { - res.status(400).json({ error: 'Pass step by querystring is required' }); + res.status(400).json({ error: 'Step query param is required' }); return; } - steps = steps.map((step) => { - const [namespace, id] = step.split('/', 2); - return { namespace, id }; - }); - const metric = new Metric('pipeline-run'); - - try { - const codes = await memoryStorage.getCodesByCache(steps, { - preCache: (code) => { - const filename = codeFileName(code.namespace, code.id); - code.script = sandbox.compileCode(filename, code.code); - return code; - }, - }); - - for (let i = 0; i < codes.length; i += 1) { - if (!codes[i]) { - const { namespace, id } = steps[i]; - const e = new Error(`Code '${namespace}/${id}' is not found`); - e.statusCode = 404; - throw e; - } - } - - const result = await new Pipeline(sandbox, req, codes).run(); + const f = new Functions(memoryStorage, sandbox, req); + const result = await f.runPipeline(steps); + if (result.headers) { res.set(result.headers); - res.status(result.status); - metric.observePipelineRun(result.status); - res.json(result.body); - } catch (err) { - const status = err.statusCode || 500; - metric.observePipelineRun(status); - res.status(status).json({ error: err.message }); } + res.status(result.status); + res.json(result.body); }); module.exports = router; diff --git a/lib/http/routers/FunctionsV2Router.js b/lib/http/routers/FunctionsV2Router.js new file mode 100644 index 0000000..c74c5b7 --- /dev/null +++ b/lib/http/routers/FunctionsV2Router.js @@ -0,0 +1,207 @@ +const Router = require('express').Router; +const bodyParser = require('body-parser'); +const Validator = require('jsonschema').Validator; + +const log = require('../../support/log'); +const schemas = require('../../domain/schemas'); +const { StdoutLogStorage } = require('../../domain/LogStorage'); +const FunctionsRequest = require('../FunctionsRequest'); +const SchemaResponse = require('../SchemaResponse'); +const Functions = require('../../domain/Functions'); + +const router = new Router(); +const { bodyParserLimit } = require('../../support/config'); + +// V2 API +// +// {version} => semantic version or `latest` +// `latest` => pointer to most recent version +// +// - /api/v2/functions/{namespace}/{name} => List versions +// - /api/v2/functions/{namespace}/{name}/{version} +// - /api/v2/functions/{namespace}/{name}/{version}/env +// - /api/v2/functions/{namespace}/{name}/{version}/run +// - /api/v2/functions/pipeline +// +// Migration V1 => V2 +// => V1 always access V2 with version `latest`. +// If not found: +// - fallback to old key format +// - migrate the function, initial version 0.0.0, `latest` => 0.0.0 +// + +router.get('/', async (req, res) => { + const memoryStorage = req.app.get('memoryStorage'); + const page = parseInt(req.query.page || '1', 10); + const perPage = parseInt(req.query.perPage || '10', 10); + const { namespace, id, version } = req.query; + const functionsRequest = new FunctionsRequest(req); + + try { + let list = {}; + if (namespace) { + list = await memoryStorage.search(namespace, id, version, page, perPage); + } else { + list = await memoryStorage.listNamespaces(page, perPage); + } + new SchemaResponse(functionsRequest, res, 'functions/list').json(list); + } catch (err) { + log.error(`Error listing namespaces and its functions: ${err}`); + res.status(500).json({ error: err.message }); + } +}); + +router.all('/:namespace/:id/:version*', (req, res, next) => { + req.log = new StdoutLogStorage(req.params.namespace, req.params.id, req.params.version).console; + next(); +}); + +router.put('/:namespace/:id/:version', bodyParser.json({ limit: bodyParserLimit }), async (req, res) => { + const validationResult = new Validator().validate(req.body, schemas['functions/item']); + const memoryStorage = req.app.get('memoryStorage'); + const sandbox = req.app.get('sandbox'); + + if (!validationResult.valid) { + const error = 'Invalid instance'; + const details = validationResult.errors.map(e => e.toString()); + + res.status(400).json({ error, details }); + return; + } + const { namespace, id, version } = req.params; + const { code, env } = req.body; + + const f = new Functions(memoryStorage, sandbox, req); + const result = await f.create(namespace, id, version, code, env); + + if (result.status === 200) { + res.set({ ETag: result.body.hash }); + + const functionsRequest = new FunctionsRequest(req); + new SchemaResponse(functionsRequest, res, 'functions/item').json(result.body); + } else { + res.status(result.status); + res.json(result.body); + } +}); + +router.put('/:namespace/:id/:version/env/:env', bodyParser.json({ strict: false, limit: bodyParserLimit }), async (req, res) => { + const validationResult = new Validator().validate(req.body, schemas['functions/env']); + const memoryStorage = req.app.get('memoryStorage'); + + if (!validationResult.valid) { + const error = 'Invalid instance'; + const details = validationResult.errors.map(e => e.toString()); + + res.status(400).json({ error, details }); + return; + } + + const { namespace, id, version, env } = req.params; + + try { + await memoryStorage + .putCodeEnviromentVariable(namespace, id, version, env, req.body); + res.status(204).end(); + } catch (err) { + log.error(`[${namespace}:${id}] Failed to set enviroment variable ${env}, error: ${err}`); + res.status(err.statusCode || 500).json({ error: err.message }); + } +}); + +router.delete('/:namespace/:id/:version/env/:env', async (req, res) => { + const { namespace, id, version, env } = req.params; + const memoryStorage = req.app.get('memoryStorage'); + + try { + await memoryStorage + .deleteCodeEnviromentVariable(namespace, id, version, env); + res.status(204).end(); + } catch (err) { + log.error(`[${namespace}:${id}] Failed to unset enviroment variable ${env}, error: ${err}`); + res.status(err.statusCode || 500).json({ error: err.message }); + } +}); + +router.get('/:namespace/:id/:version', async (req, res) => { + const { namespace, id, version } = req.params; + const memoryStorage = req.app.get('memoryStorage'); + + try { + let v = version; + const ns = await memoryStorage.getNamespace(namespace); + if (!ns) { + v = null; + } else if (ns.latest && version === 'latest') { + v = ns.latest[id]; + } + + const code = await memoryStorage.getCode(namespace, id, v); + if (!code) { + const error = 'Code not found'; + req.log.error(error); + res.status(404).json({ error }); + return; + } + + res.set({ ETag: code.hash }); + const functionsRequest = new FunctionsRequest(req); + new SchemaResponse(functionsRequest, res, 'functions/item').json(code); + } catch (err) { + req.log.error(`${err}`); + req.log.error(`${err.stack}`); + res.status(500).json({ error: err.message }); + } +}); + +router.delete('/:namespace/:id/:version', async (req, res) => { + const { namespace, id, version } = req.params; + const memoryStorage = req.app.get('memoryStorage'); + + try { + await memoryStorage.deleteCode(namespace, id, version); + res.status(204).end(); + } catch (err) { + req.log.error(`Failed to delete code id: ${err}`); + res.status(500).json({ error: err.message }); + } +}); + + +router.all('/:namespace/:id/:version/run', bodyParser.json({ limit: bodyParserLimit }), async (req, res) => { + const { namespace, id, version } = req.params; + const memoryStorage = req.app.get('memoryStorage'); + const sandbox = req.app.get('sandbox'); + + const f = new Functions(memoryStorage, sandbox, req); + const result = await f.run(namespace, id, version); + + if (result.headers) { + res.set(result.headers); + } + res.status(result.status); + res.json(result.body); +}); + + +router.put('/pipeline', bodyParser.json({ limit: bodyParserLimit }), async (req, res) => { + const memoryStorage = req.app.get('memoryStorage'); + const sandbox = req.app.get('sandbox'); + + const { steps } = req.query; + if (!steps) { + res.status(400).json({ error: 'Step query param is required' }); + return; + } + + const f = new Functions(memoryStorage, sandbox, req); + const result = await f.runPipeline(steps); + + if (result.headers) { + res.set(result.headers); + } + res.status(result.status); + res.json(result.body); +}); + +module.exports = router; diff --git a/lib/http/routes.js b/lib/http/routes.js index f96338f..ac51a70 100644 --- a/lib/http/routes.js +++ b/lib/http/routes.js @@ -10,6 +10,7 @@ const StatusRouter = require('./routers/StatusRouter'); const DebugRouter = require('./routers/DebugRouter'); const NamespacesRouter = require('./routers/NamespacesRouter'); const FunctionsRouter = require('./routers/FunctionsRouter'); +const FunctionsV2Router = require('./routers/FunctionsV2Router'); const parseExposeEnv = require('../support/parseExposeEnv'); const config = require('../support/config'); @@ -58,5 +59,7 @@ app.use('/_schemas', SchemasRouter); app.use('/functions', FunctionsRouter); app.use('/namespaces', NamespacesRouter); +// V2 Routes +app.use('/v2/functions', FunctionsV2Router); module.exports = app; diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json index 660c6fa..90f853c 100644 --- a/npm-shrinkwrap.json +++ b/npm-shrinkwrap.json @@ -1,6 +1,6 @@ { "name": "@globocom/backstage-functions", - "version": "0.2.15", + "version": "0.2.16", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -9,8 +9,8 @@ "resolved": "https://registry.npmjs.org/@globocom/backstage-functions-sandbox/-/backstage-functions-sandbox-0.5.0.tgz", "integrity": "sha512-rwr/rGv6sbdYfzi9X6dNlNFz2EqByxsShp8WwRGof1bwVS7YDnFjmwbq71JlOXFe5UAvjIGkTm1VtOkSBtJf9Q==", "requires": { - "once": "^1.4.0", - "stack-trace": "^0.0.9" + "once": "1.4.0", + "stack-trace": "0.0.9" } }, "abbrev": { @@ -24,7 +24,7 @@ "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.3.tgz", "integrity": "sha1-w8p0NJOGSMPg2cHjKN1otiLChMo=", "requires": { - "mime-types": "~2.1.11", + "mime-types": "2.1.12", "negotiator": "0.6.1" } }, @@ -40,7 +40,7 @@ "integrity": "sha1-r9+UiPsezvyDSPb7IvRk4ypYs2s=", "dev": true, "requires": { - "acorn": "^3.0.4" + "acorn": "3.3.0" }, "dependencies": { "acorn": { @@ -56,8 +56,8 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-4.11.8.tgz", "integrity": "sha1-gv+wKynmYq5TvcIK8VlHcGc5xTY=", "requires": { - "co": "^4.6.0", - "json-stable-stringify": "^1.0.1" + "co": "4.6.0", + "json-stable-stringify": "1.0.1" } }, "ajv-keywords": { @@ -71,11 +71,10 @@ "resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz", "integrity": "sha1-DNkKVhCT810KmSVsIrcGlDP60Rc=", "dev": true, - "optional": true, "requires": { - "kind-of": "^3.0.2", - "longest": "^1.0.1", - "repeat-string": "^1.5.2" + "kind-of": "3.2.2", + "longest": "1.0.1", + "repeat-string": "1.6.1" } }, "amdefine": { @@ -90,7 +89,7 @@ "integrity": "sha1-w2rsy6VjuJzrVW82kPCx2eNUf38=", "dev": true, "requires": { - "string-width": "^2.0.0" + "string-width": "2.1.1" }, "dependencies": { "ansi-regex": { @@ -111,8 +110,8 @@ "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", "dev": true, "requires": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" + "is-fullwidth-code-point": "2.0.0", + "strip-ansi": "4.0.0" } }, "strip-ansi": { @@ -121,7 +120,7 @@ "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", "dev": true, "requires": { - "ansi-regex": "^3.0.0" + "ansi-regex": "3.0.0" } } } @@ -150,8 +149,8 @@ "integrity": "sha512-0XNayC8lTHQ2OI8aljNCN3sSx6hsr/1+rlcDAotXJR7C1oZZHCNsfpbKwMjRA3Uqb5tF1Rae2oloTr4xpq+WjA==", "dev": true, "requires": { - "micromatch": "^2.1.5", - "normalize-path": "^2.0.0" + "micromatch": "2.3.11", + "normalize-path": "2.1.1" } }, "argparse": { @@ -160,7 +159,7 @@ "integrity": "sha1-c9g7wmP4bpf4zE9rrhsOkKfSLIY=", "dev": true, "requires": { - "sprintf-js": "~1.0.2" + "sprintf-js": "1.0.3" } }, "arr-diff": { @@ -169,7 +168,7 @@ "integrity": "sha1-jzuCf5Vai9ZpaX5KQlasPOrjVs8=", "dev": true, "requires": { - "arr-flatten": "^1.0.1" + "arr-flatten": "1.1.0" } }, "arr-flatten": { @@ -207,7 +206,7 @@ "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", "dev": true, "requires": { - "array-uniq": "^1.0.1" + "array-uniq": "1.0.3" } }, "array-uniq": { @@ -228,8 +227,8 @@ "integrity": "sha1-VWpcU2LAhkgyPdrrnenRS8GGTJA=", "dev": true, "requires": { - "define-properties": "^1.1.2", - "es-abstract": "^1.7.0" + "define-properties": "1.1.2", + "es-abstract": "1.9.0" } }, "arrify": { @@ -259,7 +258,7 @@ "resolved": "https://registry.npmjs.org/async/-/async-2.1.2.tgz", "integrity": "sha1-YSpKtF70KnDN6Aa62G7m2wR+g4U=", "requires": { - "lodash": "^4.14.0" + "lodash": "4.16.4" } }, "async-each": { @@ -289,9 +288,9 @@ "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=", "dev": true, "requires": { - "chalk": "^1.1.3", - "esutils": "^2.0.2", - "js-tokens": "^3.0.2" + "chalk": "1.1.3", + "esutils": "2.0.2", + "js-tokens": "3.0.2" } }, "balanced-match": { @@ -311,7 +310,7 @@ "integrity": "sha1-PKdrhSQccXC/fZcD57mqdGMAQNQ=", "optional": true, "requires": { - "tweetnacl": "^0.14.3" + "tweetnacl": "0.14.3" } }, "binary-extensions": { @@ -336,15 +335,15 @@ "integrity": "sha1-11eM9PHRHV9uqATO813Hp/9trmc=", "requires": { "bytes": "2.4.0", - "content-type": "~1.0.2", - "debug": "~2.2.0", - "depd": "~1.1.0", - "http-errors": "~1.5.0", + "content-type": "1.0.2", + "debug": "2.2.0", + "depd": "1.1.0", + "http-errors": "1.5.0", "iconv-lite": "0.4.13", - "on-finished": "~2.3.0", + "on-finished": "2.3.0", "qs": "6.2.0", - "raw-body": "~2.1.7", - "type-is": "~1.6.13" + "raw-body": "2.1.7", + "type-is": "1.6.13" } }, "boom": { @@ -352,7 +351,7 @@ "resolved": "https://registry.npmjs.org/boom/-/boom-2.10.1.tgz", "integrity": "sha1-OciRjO/1eZ+D+UkqhI9iWt0Mdm8=", "requires": { - "hoek": "2.x.x" + "hoek": "2.16.3" } }, "boxen": { @@ -361,13 +360,13 @@ "integrity": "sha1-Px1AMsMP/qnUsCwyLq8up0HcvOU=", "dev": true, "requires": { - "ansi-align": "^2.0.0", - "camelcase": "^4.0.0", - "chalk": "^2.0.1", - "cli-boxes": "^1.0.0", - "string-width": "^2.0.0", - "term-size": "^1.2.0", - "widest-line": "^1.0.0" + "ansi-align": "2.0.0", + "camelcase": "4.1.0", + "chalk": "2.3.0", + "cli-boxes": "1.0.0", + "string-width": "2.1.1", + "term-size": "1.2.0", + "widest-line": "1.0.0" }, "dependencies": { "ansi-regex": { @@ -382,7 +381,7 @@ "integrity": "sha512-NnSOmMEYtVR2JVMIGTzynRkkaxtiq1xnFBcdQD/DnNCYPoEPsVJhM98BDyaoNOQIi7p4okdi3E27eN7GQbsUug==", "dev": true, "requires": { - "color-convert": "^1.9.0" + "color-convert": "1.9.1" } }, "camelcase": { @@ -397,9 +396,9 @@ "integrity": "sha512-Az5zJR2CBujap2rqXGaJKaPHyJ0IrUimvYNX+ncCy8PJP4ltOGTrHUIo097ZaL2zMeKYpiCdqDvS6zdrTFok3Q==", "dev": true, "requires": { - "ansi-styles": "^3.1.0", - "escape-string-regexp": "^1.0.5", - "supports-color": "^4.0.0" + "ansi-styles": "3.2.0", + "escape-string-regexp": "1.0.5", + "supports-color": "4.5.0" } }, "has-flag": { @@ -420,8 +419,8 @@ "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", "dev": true, "requires": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" + "is-fullwidth-code-point": "2.0.0", + "strip-ansi": "4.0.0" } }, "strip-ansi": { @@ -430,7 +429,7 @@ "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", "dev": true, "requires": { - "ansi-regex": "^3.0.0" + "ansi-regex": "3.0.0" } }, "supports-color": { @@ -439,7 +438,7 @@ "integrity": "sha1-vnoN5ITexcXN34s9WRJQRJEvY1s=", "dev": true, "requires": { - "has-flag": "^2.0.0" + "has-flag": "2.0.0" } } } @@ -450,7 +449,7 @@ "integrity": "sha1-wHshHHyVLsH479Uad+8NHTmQopI=", "dev": true, "requires": { - "balanced-match": "^1.0.0", + "balanced-match": "1.0.0", "concat-map": "0.0.1" } }, @@ -460,9 +459,9 @@ "integrity": "sha1-uneWLhLf+WnWt2cR6RS3N4V79qc=", "dev": true, "requires": { - "expand-range": "^1.8.1", - "preserve": "^0.2.0", - "repeat-element": "^1.1.2" + "expand-range": "1.8.2", + "preserve": "0.2.0", + "repeat-element": "1.1.2" } }, "browser-stdout": { @@ -488,7 +487,7 @@ "integrity": "sha1-lAhe9jWB7NPaqSREqP6U6CV3dR8=", "dev": true, "requires": { - "callsites": "^0.2.0" + "callsites": "0.2.0" } }, "callsites": { @@ -523,8 +522,8 @@ "dev": true, "optional": true, "requires": { - "align-text": "^0.1.3", - "lazy-cache": "^1.0.3" + "align-text": "0.1.4", + "lazy-cache": "1.0.4" } }, "chai": { @@ -533,9 +532,9 @@ "integrity": "sha1-TQJjewZ/6Vi9v906QOxW/vc3Mkc=", "dev": true, "requires": { - "assertion-error": "^1.0.1", - "deep-eql": "^0.1.3", - "type-detect": "^1.0.0" + "assertion-error": "1.0.2", + "deep-eql": "0.1.3", + "type-detect": "1.0.0" } }, "chai-string": { @@ -550,11 +549,11 @@ "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", "dev": true, "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" } }, "chokidar": { @@ -563,15 +562,15 @@ "integrity": "sha1-eY5ol3gVHIB2tLNg5e3SjNortGg=", "dev": true, "requires": { - "anymatch": "^1.3.0", - "async-each": "^1.0.0", - "fsevents": "^1.0.0", - "glob-parent": "^2.0.0", - "inherits": "^2.0.1", - "is-binary-path": "^1.0.0", - "is-glob": "^2.0.0", - "path-is-absolute": "^1.0.0", - "readdirp": "^2.0.0" + "anymatch": "1.3.2", + "async-each": "1.0.1", + "fsevents": "1.1.3", + "glob-parent": "2.0.0", + "inherits": "2.0.1", + "is-binary-path": "1.0.1", + "is-glob": "2.0.1", + "path-is-absolute": "1.0.1", + "readdirp": "2.1.0" } }, "circular-json": { @@ -592,7 +591,7 @@ "integrity": "sha1-ZNo/fValRBLll5S9Ytw1KV6PKYc=", "dev": true, "requires": { - "restore-cursor": "^1.0.1" + "restore-cursor": "1.0.1" } }, "cli-width": { @@ -608,8 +607,8 @@ "dev": true, "optional": true, "requires": { - "center-align": "^0.1.1", - "right-align": "^0.1.1", + "center-align": "0.1.3", + "right-align": "0.1.3", "wordwrap": "0.0.2" }, "dependencies": { @@ -644,7 +643,7 @@ "integrity": "sha512-mjGanIiwQJskCC18rPR6OmrZ6fm2Lc7PeGFYwCmy5J34wC6F1PzdGL6xeMfmgicfYcNLGuVFA3WzXtIDCQSZxQ==", "dev": true, "requires": { - "color-name": "^1.1.1" + "color-name": "1.1.3" } }, "color-name": { @@ -663,7 +662,7 @@ "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.5.tgz", "integrity": "sha1-k4NwpXtKUd6ix3wV1cX9+JUWQAk=", "requires": { - "delayed-stream": "~1.0.0" + "delayed-stream": "1.0.0" } }, "commander": { @@ -672,7 +671,7 @@ "integrity": "sha1-nJkJQXbhIkDLItbFFGCYQA/g99Q=", "dev": true, "requires": { - "graceful-readlink": ">= 1.0.0" + "graceful-readlink": "1.0.1" } }, "component-emitter": { @@ -693,9 +692,9 @@ "integrity": "sha1-CqxmL9Ur54lk1VMvaUeE5wEQrPc=", "dev": true, "requires": { - "inherits": "^2.0.3", - "readable-stream": "^2.2.2", - "typedarray": "^0.0.6" + "inherits": "2.0.3", + "readable-stream": "2.3.3", + "typedarray": "0.0.6" }, "dependencies": { "inherits": { @@ -712,12 +711,12 @@ "integrity": "sha512-5oNkD/L++l0O6xGXxb1EWS7SivtjfGQlRyxJsYgE0Z495/L81e2h4/d3r969hoPXuFItzNOKMtsXgYG4c7dYvw==", "dev": true, "requires": { - "dot-prop": "^4.1.0", - "graceful-fs": "^4.1.2", - "make-dir": "^1.0.0", - "unique-string": "^1.0.0", - "write-file-atomic": "^2.0.0", - "xdg-basedir": "^3.0.0" + "dot-prop": "4.2.0", + "graceful-fs": "4.1.11", + "make-dir": "1.1.0", + "unique-string": "1.0.0", + "write-file-atomic": "2.3.0", + "xdg-basedir": "3.0.0" } }, "contains-path": { @@ -783,8 +782,8 @@ "integrity": "sha1-bl/mfYsgXOTSL60Ft3geja3MSzA=", "dev": true, "requires": { - "argparse": "^1.0.7", - "esprima": "^2.6.0" + "argparse": "1.0.9", + "esprima": "2.7.3" } }, "minimist": { @@ -805,26 +804,26 @@ "integrity": "sha1-Tf5b9r6LjNw3/Pk+BLZVd3InEN4=", "dev": true, "requires": { - "aws-sign2": "~0.6.0", - "aws4": "^1.2.1", - "caseless": "~0.11.0", - "combined-stream": "~1.0.5", - "extend": "~3.0.0", - "forever-agent": "~0.6.1", - "form-data": "~2.1.1", - "har-validator": "~2.0.6", - "hawk": "~3.1.3", - "http-signature": "~1.1.0", - "is-typedarray": "~1.0.0", - "isstream": "~0.1.2", - "json-stringify-safe": "~5.0.1", - "mime-types": "~2.1.7", - "oauth-sign": "~0.8.1", - "qs": "~6.3.0", - "stringstream": "~0.0.4", - "tough-cookie": "~2.3.0", - "tunnel-agent": "~0.4.1", - "uuid": "^3.0.0" + "aws-sign2": "0.6.0", + "aws4": "1.5.0", + "caseless": "0.11.0", + "combined-stream": "1.0.5", + "extend": "3.0.0", + "forever-agent": "0.6.1", + "form-data": "2.1.1", + "har-validator": "2.0.6", + "hawk": "3.1.3", + "http-signature": "1.1.1", + "is-typedarray": "1.0.0", + "isstream": "0.1.2", + "json-stringify-safe": "5.0.1", + "mime-types": "2.1.12", + "oauth-sign": "0.8.2", + "qs": "6.3.2", + "stringstream": "0.0.5", + "tough-cookie": "2.3.2", + "tunnel-agent": "0.4.3", + "uuid": "3.0.1" } } } @@ -835,7 +834,7 @@ "integrity": "sha1-Br56vvlHo/FKMP1hBnHUAbyot7Y=", "dev": true, "requires": { - "capture-stack-trace": "^1.0.0" + "capture-stack-trace": "1.0.0" } }, "cross-spawn": { @@ -844,9 +843,9 @@ "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", "dev": true, "requires": { - "lru-cache": "^4.0.1", - "shebang-command": "^1.2.0", - "which": "^1.2.9" + "lru-cache": "4.1.1", + "shebang-command": "1.2.0", + "which": "1.3.0" } }, "cryptiles": { @@ -854,7 +853,7 @@ "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-2.0.5.tgz", "integrity": "sha1-O9/s3GCBR8HGcgL6KR59ylnqo7g=", "requires": { - "boom": "2.x.x" + "boom": "2.10.1" } }, "crypto-random-string": { @@ -874,7 +873,7 @@ "integrity": "sha1-dUu1v+VUUdpppYuU1F9MWwRi1Y8=", "dev": true, "requires": { - "es5-ext": "^0.10.9" + "es5-ext": "0.10.35" } }, "damerau-levenshtein": { @@ -888,7 +887,7 @@ "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.0.tgz", "integrity": "sha1-KeSGxUGL8PNWA0qZPVFoajPoQUE=", "requires": { - "assert-plus": "^1.0.0" + "assert-plus": "1.0.0" }, "dependencies": { "assert-plus": { @@ -953,8 +952,8 @@ "integrity": "sha1-g6c/L+pWmJj7c3GTyPhzyvbUXJQ=", "dev": true, "requires": { - "foreach": "^2.0.5", - "object-keys": "^1.0.8" + "foreach": "2.0.5", + "object-keys": "1.0.11" } }, "del": { @@ -963,13 +962,13 @@ "integrity": "sha1-wSyYHQZ4RshLyvhiz/kw2Qf/0ag=", "dev": true, "requires": { - "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" + "globby": "5.0.0", + "is-path-cwd": "1.0.0", + "is-path-in-cwd": "1.0.0", + "object-assign": "4.1.1", + "pify": "2.3.0", + "pinkie-promise": "2.0.1", + "rimraf": "2.6.2" } }, "delayed-stream": { @@ -1004,8 +1003,8 @@ "integrity": "sha1-xz2NKQnSIpHhoAejlYBNqLZl/mM=", "dev": true, "requires": { - "esutils": "^2.0.2", - "isarray": "^1.0.0" + "esutils": "2.0.2", + "isarray": "1.0.0" } }, "dot-prop": { @@ -1014,7 +1013,7 @@ "integrity": "sha512-tUMXrxlExSW6U2EXiiKGSBVdYgtV8qlHL+C10TsW4PURY/ic+eaysnSkwB4kA/mBlCyy/IKDJ+Lc3wbWeaXtuQ==", "dev": true, "requires": { - "is-obj": "^1.0.0" + "is-obj": "1.0.1" } }, "duplexer": { @@ -1035,7 +1034,7 @@ "integrity": "sha1-D8c6ntXw1Tw4GTOYUj735UN3dQU=", "optional": true, "requires": { - "jsbn": "~0.1.0" + "jsbn": "0.1.0" } }, "ee-first": { @@ -1054,11 +1053,11 @@ "integrity": "sha512-kk3IJoKo7A3pWJc0OV8yZ/VEX2oSUytfekrJiqoxBlKJMFAJVJVpGdHClCCTdv+Fn2zHfpDHHIelMFhZVfef3Q==", "dev": true, "requires": { - "es-to-primitive": "^1.1.1", - "function-bind": "^1.1.1", - "has": "^1.0.1", - "is-callable": "^1.1.3", - "is-regex": "^1.0.4" + "es-to-primitive": "1.1.1", + "function-bind": "1.1.1", + "has": "1.0.1", + "is-callable": "1.1.3", + "is-regex": "1.0.4" } }, "es-to-primitive": { @@ -1067,9 +1066,9 @@ "integrity": "sha1-RTVSSKiJeQNLZ5Lhm7gfK3l13Q0=", "dev": true, "requires": { - "is-callable": "^1.1.1", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.1" + "is-callable": "1.1.3", + "is-date-object": "1.0.1", + "is-symbol": "1.0.1" } }, "es5-ext": { @@ -1078,8 +1077,8 @@ "integrity": "sha1-GO6FjOajxFx9eekcFfzKnsVoSU8=", "dev": true, "requires": { - "es6-iterator": "~2.0.1", - "es6-symbol": "~3.1.1" + "es6-iterator": "2.0.3", + "es6-symbol": "3.1.1" } }, "es6-iterator": { @@ -1088,9 +1087,9 @@ "integrity": "sha1-p96IkUGgWpSwhUQDstCg+/qY87c=", "dev": true, "requires": { - "d": "1", - "es5-ext": "^0.10.35", - "es6-symbol": "^3.1.1" + "d": "1.0.0", + "es5-ext": "0.10.35", + "es6-symbol": "3.1.1" } }, "es6-map": { @@ -1099,12 +1098,12 @@ "integrity": "sha1-kTbgUD3MBqMBaQ8LsU/042TpSfA=", "dev": true, "requires": { - "d": "1", - "es5-ext": "~0.10.14", - "es6-iterator": "~2.0.1", - "es6-set": "~0.1.5", - "es6-symbol": "~3.1.1", - "event-emitter": "~0.3.5" + "d": "1.0.0", + "es5-ext": "0.10.35", + "es6-iterator": "2.0.3", + "es6-set": "0.1.5", + "es6-symbol": "3.1.1", + "event-emitter": "0.3.5" } }, "es6-promise": { @@ -1119,11 +1118,11 @@ "integrity": "sha1-0rPsXU2ADO2BjbU40ol02wpzzLE=", "dev": true, "requires": { - "d": "1", - "es5-ext": "~0.10.14", - "es6-iterator": "~2.0.1", + "d": "1.0.0", + "es5-ext": "0.10.35", + "es6-iterator": "2.0.3", "es6-symbol": "3.1.1", - "event-emitter": "~0.3.5" + "event-emitter": "0.3.5" } }, "es6-symbol": { @@ -1132,8 +1131,8 @@ "integrity": "sha1-vwDvT9q2uhtG7Le2KbTH7VcVzHc=", "dev": true, "requires": { - "d": "1", - "es5-ext": "~0.10.14" + "d": "1.0.0", + "es5-ext": "0.10.35" } }, "es6-weak-map": { @@ -1142,10 +1141,10 @@ "integrity": "sha1-XjqzIlH/0VOKH45f+hNXdy+S2W8=", "dev": true, "requires": { - "d": "1", - "es5-ext": "^0.10.14", - "es6-iterator": "^2.0.1", - "es6-symbol": "^3.1.1" + "d": "1.0.0", + "es5-ext": "0.10.35", + "es6-iterator": "2.0.3", + "es6-symbol": "3.1.1" } }, "escape-html": { @@ -1165,11 +1164,11 @@ "integrity": "sha1-WltTr0aTEQvrsIZ6o0MN07cKEBg=", "dev": true, "requires": { - "esprima": "^2.7.1", - "estraverse": "^1.9.1", - "esutils": "^2.0.2", - "optionator": "^0.8.1", - "source-map": "~0.2.0" + "esprima": "2.7.3", + "estraverse": "1.9.3", + "esutils": "2.0.2", + "optionator": "0.8.2", + "source-map": "0.2.0" }, "dependencies": { "esprima": { @@ -1192,10 +1191,10 @@ "integrity": "sha1-4Bl16BJ4GhY6ba392AOY3GTIicM=", "dev": true, "requires": { - "es6-map": "^0.1.3", - "es6-weak-map": "^2.0.1", - "esrecurse": "^4.1.0", - "estraverse": "^4.1.1" + "es6-map": "0.1.5", + "es6-weak-map": "2.0.2", + "esrecurse": "4.2.0", + "estraverse": "4.2.0" } }, "eslint": { @@ -1204,41 +1203,41 @@ "integrity": "sha1-yPxiAcf0DdCJQbh8CFdnOGpnmsw=", "dev": true, "requires": { - "babel-code-frame": "^6.16.0", - "chalk": "^1.1.3", - "concat-stream": "^1.5.2", - "debug": "^2.1.1", - "doctrine": "^2.0.0", - "escope": "^3.6.0", - "espree": "^3.4.0", - "esquery": "^1.0.0", - "estraverse": "^4.2.0", - "esutils": "^2.0.2", - "file-entry-cache": "^2.0.0", - "glob": "^7.0.3", - "globals": "^9.14.0", - "ignore": "^3.2.0", - "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", - "levn": "^0.3.0", - "lodash": "^4.0.0", - "mkdirp": "^0.5.0", - "natural-compare": "^1.4.0", - "optionator": "^0.8.2", - "path-is-inside": "^1.0.1", - "pluralize": "^1.2.1", - "progress": "^1.1.8", - "require-uncached": "^1.0.2", - "shelljs": "^0.7.5", - "strip-bom": "^3.0.0", - "strip-json-comments": "~2.0.1", - "table": "^3.7.8", - "text-table": "~0.2.0", - "user-home": "^2.0.0" + "babel-code-frame": "6.26.0", + "chalk": "1.1.3", + "concat-stream": "1.6.0", + "debug": "2.2.0", + "doctrine": "2.0.0", + "escope": "3.6.0", + "espree": "3.5.2", + "esquery": "1.0.0", + "estraverse": "4.2.0", + "esutils": "2.0.2", + "file-entry-cache": "2.0.0", + "glob": "7.1.2", + "globals": "9.18.0", + "ignore": "3.3.7", + "imurmurhash": "0.1.4", + "inquirer": "0.12.0", + "is-my-json-valid": "2.16.1", + "is-resolvable": "1.0.0", + "js-yaml": "3.10.0", + "json-stable-stringify": "1.0.1", + "levn": "0.3.0", + "lodash": "4.16.4", + "mkdirp": "0.5.1", + "natural-compare": "1.4.0", + "optionator": "0.8.2", + "path-is-inside": "1.0.2", + "pluralize": "1.2.1", + "progress": "1.1.8", + "require-uncached": "1.0.3", + "shelljs": "0.7.8", + "strip-bom": "3.0.0", + "strip-json-comments": "2.0.1", + "table": "3.8.3", + "text-table": "0.2.0", + "user-home": "2.0.0" } }, "eslint-config-airbnb": { @@ -1247,7 +1246,7 @@ "integrity": "sha1-qygrdWol8D0ErCZMJNZzoIqAMnA=", "dev": true, "requires": { - "eslint-config-airbnb-base": "^8.0.0" + "eslint-config-airbnb-base": "8.0.0" } }, "eslint-config-airbnb-base": { @@ -1262,9 +1261,9 @@ "integrity": "sha1-Wt2BBujJKNssuiMrzZ76hG49oWw=", "dev": true, "requires": { - "debug": "^2.2.0", - "object-assign": "^4.0.1", - "resolve": "^1.1.6" + "debug": "2.2.0", + "object-assign": "4.1.1", + "resolve": "1.5.0" } }, "eslint-plugin-import": { @@ -1273,22 +1272,22 @@ "integrity": "sha1-svoH68xTUE0PKkR3WC7Iv/GHG58=", "dev": true, "requires": { - "builtin-modules": "^1.1.1", - "contains-path": "^0.1.0", - "debug": "^2.2.0", - "doctrine": "1.3.x", - "es6-map": "^0.1.3", - "es6-set": "^0.1.4", - "eslint-import-resolver-node": "^0.2.0", - "has": "^1.0.1", - "lodash.cond": "^4.3.0", - "lodash.endswith": "^4.0.1", - "lodash.find": "^4.3.0", - "lodash.findindex": "^4.3.0", - "minimatch": "^3.0.3", - "object-assign": "^4.0.1", - "pkg-dir": "^1.0.0", - "pkg-up": "^1.0.0" + "builtin-modules": "1.1.1", + "contains-path": "0.1.0", + "debug": "2.2.0", + "doctrine": "1.3.0", + "es6-map": "0.1.5", + "es6-set": "0.1.5", + "eslint-import-resolver-node": "0.2.3", + "has": "1.0.1", + "lodash.cond": "4.5.2", + "lodash.endswith": "4.2.1", + "lodash.find": "4.6.0", + "lodash.findindex": "4.6.0", + "minimatch": "3.0.4", + "object-assign": "4.1.1", + "pkg-dir": "1.0.0", + "pkg-up": "1.0.0" }, "dependencies": { "doctrine": { @@ -1297,8 +1296,8 @@ "integrity": "sha1-E+dWgrVVGEJCdvfBc3g0Vu+RPSY=", "dev": true, "requires": { - "esutils": "^2.0.2", - "isarray": "^1.0.0" + "esutils": "2.0.2", + "isarray": "1.0.0" } } } @@ -1309,9 +1308,9 @@ "integrity": "sha1-TjXLcbin23AqxBXIBuuOjZ6mxl0=", "dev": true, "requires": { - "damerau-levenshtein": "^1.0.0", - "jsx-ast-utils": "^1.0.0", - "object-assign": "^4.0.1" + "damerau-levenshtein": "1.0.4", + "jsx-ast-utils": "1.4.1", + "object-assign": "4.1.1" } }, "eslint-plugin-react": { @@ -1320,11 +1319,11 @@ "integrity": "sha1-xUNb6wZ3ThLH2y9qut3L+QDNP3g=", "dev": true, "requires": { - "array.prototype.find": "^2.0.1", - "doctrine": "^1.2.2", - "has": "^1.0.1", - "jsx-ast-utils": "^1.3.4", - "object.assign": "^4.0.4" + "array.prototype.find": "2.0.4", + "doctrine": "1.5.0", + "has": "1.0.1", + "jsx-ast-utils": "1.4.1", + "object.assign": "4.0.4" }, "dependencies": { "doctrine": { @@ -1333,8 +1332,8 @@ "integrity": "sha1-N53Ocw9hZvds76TmcHoVmwLFpvo=", "dev": true, "requires": { - "esutils": "^2.0.2", - "isarray": "^1.0.0" + "esutils": "2.0.2", + "isarray": "1.0.0" } } } @@ -1345,8 +1344,8 @@ "integrity": "sha512-sadKeYwaR/aJ3stC2CdvgXu1T16TdYN+qwCpcWbMnGJ8s0zNWemzrvb2GbD4OhmJ/fwpJjudThAlLobGbWZbCQ==", "dev": true, "requires": { - "acorn": "^5.2.1", - "acorn-jsx": "^3.0.0" + "acorn": "5.2.1", + "acorn-jsx": "3.0.1" } }, "esprima": { @@ -1361,7 +1360,7 @@ "integrity": "sha1-z7qLV9f7qT8XKYqKAGoEzaE9gPo=", "dev": true, "requires": { - "estraverse": "^4.0.0" + "estraverse": "4.2.0" } }, "esrecurse": { @@ -1370,8 +1369,8 @@ "integrity": "sha1-+pVo2Y04I/mkHZHpAtyrnqblsWM=", "dev": true, "requires": { - "estraverse": "^4.1.0", - "object-assign": "^4.0.1" + "estraverse": "4.2.0", + "object-assign": "4.1.1" } }, "estraverse": { @@ -1397,8 +1396,8 @@ "integrity": "sha1-34xp7vFkeSPHFXuc6DhAYQsCzDk=", "dev": true, "requires": { - "d": "1", - "es5-ext": "~0.10.14" + "d": "1.0.0", + "es5-ext": "0.10.35" } }, "event-stream": { @@ -1407,13 +1406,13 @@ "integrity": "sha1-SrTJoPWlTbkzi0w02Gv86PSzVXE=", "dev": true, "requires": { - "duplexer": "~0.1.1", - "from": "~0", - "map-stream": "~0.1.0", + "duplexer": "0.1.1", + "from": "0.1.7", + "map-stream": "0.1.0", "pause-stream": "0.0.11", - "split": "0.3", - "stream-combiner": "~0.0.4", - "through": "~2.3.1" + "split": "0.3.3", + "stream-combiner": "0.0.4", + "through": "2.3.8" } }, "eventemitter3": { @@ -1428,13 +1427,13 @@ "integrity": "sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=", "dev": true, "requires": { - "cross-spawn": "^5.0.1", - "get-stream": "^3.0.0", - "is-stream": "^1.1.0", - "npm-run-path": "^2.0.0", - "p-finally": "^1.0.0", - "signal-exit": "^3.0.0", - "strip-eof": "^1.0.0" + "cross-spawn": "5.1.0", + "get-stream": "3.0.0", + "is-stream": "1.1.0", + "npm-run-path": "2.0.2", + "p-finally": "1.0.0", + "signal-exit": "3.0.2", + "strip-eof": "1.0.0" } }, "exit-hook": { @@ -1449,7 +1448,7 @@ "integrity": "sha1-3wcoTjQqgHzXM6xa9yQR5YHRF3s=", "dev": true, "requires": { - "is-posix-bracket": "^0.1.0" + "is-posix-bracket": "0.1.1" } }, "expand-range": { @@ -1458,7 +1457,7 @@ "integrity": "sha1-opnv/TNf4nIeuujiV+x5ZE/IUzc=", "dev": true, "requires": { - "fill-range": "^2.1.0" + "fill-range": "2.2.3" } }, "express": { @@ -1466,32 +1465,32 @@ "resolved": "https://registry.npmjs.org/express/-/express-4.14.0.tgz", "integrity": "sha1-we4/Qs3Ikfs9xlCoki1R7IR9DWY=", "requires": { - "accepts": "~1.3.3", + "accepts": "1.3.3", "array-flatten": "1.1.1", "content-disposition": "0.5.1", - "content-type": "~1.0.2", + "content-type": "1.0.2", "cookie": "0.3.1", "cookie-signature": "1.0.6", - "debug": "~2.2.0", - "depd": "~1.1.0", - "encodeurl": "~1.0.1", - "escape-html": "~1.0.3", - "etag": "~1.7.0", + "debug": "2.2.0", + "depd": "1.1.0", + "encodeurl": "1.0.1", + "escape-html": "1.0.3", + "etag": "1.7.0", "finalhandler": "0.5.0", "fresh": "0.3.0", "merge-descriptors": "1.0.1", - "methods": "~1.1.2", - "on-finished": "~2.3.0", - "parseurl": "~1.3.1", + "methods": "1.1.2", + "on-finished": "2.3.0", + "parseurl": "1.3.1", "path-to-regexp": "0.1.7", - "proxy-addr": "~1.1.2", + "proxy-addr": "1.1.2", "qs": "6.2.0", - "range-parser": "~1.2.0", + "range-parser": "1.2.0", "send": "0.14.1", - "serve-static": "~1.11.1", - "type-is": "~1.6.13", + "serve-static": "1.11.1", + "type-is": "1.6.13", "utils-merge": "1.0.0", - "vary": "~1.1.0" + "vary": "1.1.0" } }, "extend": { @@ -1505,7 +1504,7 @@ "integrity": "sha1-Lhj/PS9JqydlzskCPwEdqo2DSaE=", "dev": true, "requires": { - "is-extglob": "^1.0.0" + "is-extglob": "1.0.0" } }, "extsprintf": { @@ -1530,8 +1529,8 @@ "integrity": "sha1-y+Hjr/zxzUS4DK3+0o3Hk6lwHS4=", "dev": true, "requires": { - "escape-string-regexp": "^1.0.5", - "object-assign": "^4.1.0" + "escape-string-regexp": "1.0.5", + "object-assign": "4.1.1" } }, "file-entry-cache": { @@ -1540,8 +1539,8 @@ "integrity": "sha1-w5KZDD5oR4PYOLjISkXYoEhFg2E=", "dev": true, "requires": { - "flat-cache": "^1.2.1", - "object-assign": "^4.0.1" + "flat-cache": "1.3.0", + "object-assign": "4.1.1" } }, "filename-regex": { @@ -1556,11 +1555,11 @@ "integrity": "sha1-ULd9/X5Gm8dJJHCWNpn+eoSFpyM=", "dev": true, "requires": { - "is-number": "^2.1.0", - "isobject": "^2.0.0", - "randomatic": "^1.1.3", - "repeat-element": "^1.1.2", - "repeat-string": "^1.5.2" + "is-number": "2.1.0", + "isobject": "2.1.0", + "randomatic": "1.1.7", + "repeat-element": "1.1.2", + "repeat-string": "1.6.1" } }, "finalhandler": { @@ -1568,11 +1567,11 @@ "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-0.5.0.tgz", "integrity": "sha1-6VCKvs6bbbqHGmlCodeRG5GRGsc=", "requires": { - "debug": "~2.2.0", - "escape-html": "~1.0.3", - "on-finished": "~2.3.0", - "statuses": "~1.3.0", - "unpipe": "~1.0.0" + "debug": "2.2.0", + "escape-html": "1.0.3", + "on-finished": "2.3.0", + "statuses": "1.3.0", + "unpipe": "1.0.0" } }, "find-up": { @@ -1581,8 +1580,8 @@ "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", "dev": true, "requires": { - "path-exists": "^2.0.0", - "pinkie-promise": "^2.0.0" + "path-exists": "2.1.0", + "pinkie-promise": "2.0.1" } }, "flat-cache": { @@ -1591,10 +1590,10 @@ "integrity": "sha1-0wMLMrOBVPTjt+nHCfSQ9++XxIE=", "dev": true, "requires": { - "circular-json": "^0.3.1", - "del": "^2.0.2", - "graceful-fs": "^4.1.2", - "write": "^0.2.1" + "circular-json": "0.3.3", + "del": "2.2.2", + "graceful-fs": "4.1.11", + "write": "0.2.1" } }, "flexbuffer": { @@ -1614,7 +1613,7 @@ "integrity": "sha1-UmXGgaTylNq78XyVCbZ2OqhFEM4=", "dev": true, "requires": { - "for-in": "^1.0.1" + "for-in": "1.0.2" } }, "foreach": { @@ -1629,10 +1628,10 @@ "integrity": "sha1-AKzSD5274vedBGl7zKKnfuAO4DE=", "dev": true, "requires": { - "commander": "~2.9.0", - "http-proxy": "~1.11.1", - "mustache": "^2.2.1", - "shell-quote": "~1.4.2" + "commander": "2.9.0", + "http-proxy": "1.11.3", + "mustache": "2.3.0", + "shell-quote": "1.4.3" } }, "forever-agent": { @@ -1645,9 +1644,9 @@ "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.1.1.tgz", "integrity": "sha1-St8DQuGnmvoehMjDIKn/yCOSofM=", "requires": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.5", - "mime-types": "^2.1.12" + "asynckit": "0.4.0", + "combined-stream": "1.0.5", + "mime-types": "2.1.12" } }, "formatio": { @@ -1656,7 +1655,7 @@ "integrity": "sha1-87IWfZBoxGmKjVH092CjmlTYGOs=", "dev": true, "requires": { - "samsam": "1.x" + "samsam": "1.3.0" } }, "formidable": { @@ -1694,8 +1693,8 @@ "dev": true, "optional": true, "requires": { - "nan": "^2.3.0", - "node-pre-gyp": "^0.6.39" + "nan": "2.8.0", + "node-pre-gyp": "0.6.39" }, "dependencies": { "abbrev": { @@ -1712,16 +1711,15 @@ "dev": true, "optional": true, "requires": { - "co": "^4.6.0", - "json-stable-stringify": "^1.0.1" + "co": "4.6.0", + "json-stable-stringify": "1.0.1" } }, "ansi-regex": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "dev": true, - "optional": true + "dev": true }, "aproba": { "version": "1.1.1", @@ -1737,8 +1735,8 @@ "dev": true, "optional": true, "requires": { - "delegates": "^1.0.0", - "readable-stream": "^2.0.6" + "delegates": "1.0.0", + "readable-stream": "2.2.9" } }, "asn1": { @@ -1780,8 +1778,7 @@ "version": "0.4.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.2.tgz", "integrity": "sha1-yz8+PHMtwPAe5wtAPzAuYddwmDg=", - "dev": true, - "optional": true + "dev": true }, "bcrypt-pbkdf": { "version": "1.0.1", @@ -1790,7 +1787,7 @@ "dev": true, "optional": true, "requires": { - "tweetnacl": "^0.14.3" + "tweetnacl": "0.14.5" } }, "block-stream": { @@ -1798,9 +1795,8 @@ "resolved": "https://registry.npmjs.org/block-stream/-/block-stream-0.0.9.tgz", "integrity": "sha1-E+v+d4oDIFz+A3UUgeu0szAMEmo=", "dev": true, - "optional": true, "requires": { - "inherits": "~2.0.0" + "inherits": "2.0.3" } }, "boom": { @@ -1808,9 +1804,8 @@ "resolved": "https://registry.npmjs.org/boom/-/boom-2.10.1.tgz", "integrity": "sha1-OciRjO/1eZ+D+UkqhI9iWt0Mdm8=", "dev": true, - "optional": true, "requires": { - "hoek": "2.x.x" + "hoek": "2.16.3" } }, "brace-expansion": { @@ -1818,9 +1813,8 @@ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.7.tgz", "integrity": "sha1-Pv/DxQ4ABTH7cg6v+A8K6O8jz1k=", "dev": true, - "optional": true, "requires": { - "balanced-match": "^0.4.1", + "balanced-match": "0.4.2", "concat-map": "0.0.1" } }, @@ -1828,8 +1822,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/buffer-shims/-/buffer-shims-1.0.0.tgz", "integrity": "sha1-mXjOMXOIxkmth5MCjDR37wRKi1E=", - "dev": true, - "optional": true + "dev": true }, "caseless": { "version": "0.12.0", @@ -1849,48 +1842,42 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", - "dev": true, - "optional": true + "dev": true }, "combined-stream": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.5.tgz", "integrity": "sha1-k4NwpXtKUd6ix3wV1cX9+JUWQAk=", "dev": true, - "optional": true, "requires": { - "delayed-stream": "~1.0.0" + "delayed-stream": "1.0.0" } }, "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "dev": true, - "optional": true + "dev": true }, "console-control-strings": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", - "dev": true, - "optional": true + "dev": true }, "core-util-is": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", - "dev": true, - "optional": true + "dev": true }, "cryptiles": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-2.0.5.tgz", "integrity": "sha1-O9/s3GCBR8HGcgL6KR59ylnqo7g=", "dev": true, - "optional": true, "requires": { - "boom": "2.x.x" + "boom": "2.10.1" } }, "dashdash": { @@ -1900,7 +1887,7 @@ "dev": true, "optional": true, "requires": { - "assert-plus": "^1.0.0" + "assert-plus": "1.0.0" }, "dependencies": { "assert-plus": { @@ -1933,8 +1920,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", - "dev": true, - "optional": true + "dev": true }, "delegates": { "version": "1.0.0", @@ -1957,7 +1943,7 @@ "dev": true, "optional": true, "requires": { - "jsbn": "~0.1.0" + "jsbn": "0.1.1" } }, "extend": { @@ -1971,8 +1957,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.0.2.tgz", "integrity": "sha1-4QgOBljjALBilJkMxw4VAiNf1VA=", - "dev": true, - "optional": true + "dev": true }, "forever-agent": { "version": "0.6.1", @@ -1997,20 +1982,18 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", - "dev": true, - "optional": true + "dev": true }, "fstream": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.11.tgz", "integrity": "sha1-XB+x8RdHcRTwYyoOtLcbPLD9MXE=", "dev": true, - "optional": true, "requires": { - "graceful-fs": "^4.1.2", - "inherits": "~2.0.0", - "mkdirp": ">=0.5 0", - "rimraf": "2" + "graceful-fs": "4.1.11", + "inherits": "2.0.3", + "mkdirp": "0.5.1", + "rimraf": "2.6.1" } }, "fstream-ignore": { @@ -2020,9 +2003,9 @@ "dev": true, "optional": true, "requires": { - "fstream": "^1.0.0", - "inherits": "2", - "minimatch": "^3.0.0" + "fstream": "1.0.11", + "inherits": "2.0.3", + "minimatch": "3.0.4" } }, "gauge": { @@ -2032,14 +2015,14 @@ "dev": true, "optional": true, "requires": { - "aproba": "^1.0.3", - "console-control-strings": "^1.0.0", - "has-unicode": "^2.0.0", - "object-assign": "^4.1.0", - "signal-exit": "^3.0.0", - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1", - "wide-align": "^1.1.0" + "aproba": "1.1.1", + "console-control-strings": "1.1.0", + "has-unicode": "2.0.1", + "object-assign": "4.1.1", + "signal-exit": "3.0.2", + "string-width": "1.0.2", + "strip-ansi": "3.0.1", + "wide-align": "1.1.2" } }, "getpass": { @@ -2049,7 +2032,7 @@ "dev": true, "optional": true, "requires": { - "assert-plus": "^1.0.0" + "assert-plus": "1.0.0" }, "dependencies": { "assert-plus": { @@ -2066,22 +2049,20 @@ "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", "dev": true, - "optional": true, "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "fs.realpath": "1.0.0", + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.4.0", + "path-is-absolute": "1.0.1" } }, "graceful-fs": { "version": "4.1.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=", - "dev": true, - "optional": true + "dev": true }, "har-schema": { "version": "1.0.5", @@ -2113,20 +2094,18 @@ "resolved": "https://registry.npmjs.org/hawk/-/hawk-3.1.3.tgz", "integrity": "sha1-B4REvXwWQLD+VA0sm3PVlnjo4cQ=", "dev": true, - "optional": true, "requires": { - "boom": "2.x.x", - "cryptiles": "2.x.x", - "hoek": "2.x.x", - "sntp": "1.x.x" + "boom": "2.10.1", + "cryptiles": "2.0.5", + "hoek": "2.16.3", + "sntp": "1.0.9" } }, "hoek": { "version": "2.16.3", "resolved": "https://registry.npmjs.org/hoek/-/hoek-2.16.3.tgz", "integrity": "sha1-ILt0A9POo5jpHcRxCo/xuCdKJe0=", - "dev": true, - "optional": true + "dev": true }, "http-signature": { "version": "1.1.1", @@ -2135,9 +2114,9 @@ "dev": true, "optional": true, "requires": { - "assert-plus": "^0.2.0", - "jsprim": "^1.2.2", - "sshpk": "^1.7.0" + "assert-plus": "0.2.0", + "jsprim": "1.4.0", + "sshpk": "1.13.0" } }, "inflight": { @@ -2145,18 +2124,16 @@ "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", "dev": true, - "optional": true, "requires": { - "once": "^1.3.0", - "wrappy": "1" + "once": "1.4.0", + "wrappy": "1.0.2" } }, "inherits": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", - "dev": true, - "optional": true + "dev": true }, "ini": { "version": "1.3.4", @@ -2170,7 +2147,6 @@ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", "dev": true, - "optional": true, "requires": { "number-is-nan": "1.0.1" } @@ -2186,8 +2162,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true, - "optional": true + "dev": true }, "isstream": { "version": "0.1.2", @@ -2203,7 +2178,7 @@ "dev": true, "optional": true, "requires": { - "jsbn": "~0.1.0" + "jsbn": "0.1.1" } }, "jsbn": { @@ -2227,7 +2202,7 @@ "dev": true, "optional": true, "requires": { - "jsonify": "~0.0.0" + "jsonify": "0.0.0" } }, "json-stringify-safe": { @@ -2270,15 +2245,13 @@ "version": "1.27.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.27.0.tgz", "integrity": "sha1-gg9XIpa70g7CXtVeW13oaeVDbrE=", - "dev": true, - "optional": true + "dev": true }, "mime-types": { "version": "2.1.15", "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.15.tgz", "integrity": "sha1-pOv1BkCUVpI3uM9wBGd20J/JKu0=", "dev": true, - "optional": true, "requires": { "mime-db": "1.27.0" } @@ -2288,7 +2261,6 @@ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", "dev": true, - "optional": true, "requires": { "brace-expansion": "1.1.7" } @@ -2297,15 +2269,13 @@ "version": "0.0.8", "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", - "dev": true, - "optional": true + "dev": true }, "mkdirp": { "version": "0.5.1", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", "dev": true, - "optional": true, "requires": { "minimist": "0.0.8" } @@ -2324,17 +2294,17 @@ "dev": true, "optional": true, "requires": { - "detect-libc": "^1.0.2", + "detect-libc": "1.0.2", "hawk": "3.1.3", - "mkdirp": "^0.5.1", - "nopt": "^4.0.1", - "npmlog": "^4.0.2", - "rc": "^1.1.7", + "mkdirp": "0.5.1", + "nopt": "4.0.1", + "npmlog": "4.1.0", + "rc": "1.2.1", "request": "2.81.0", - "rimraf": "^2.6.1", - "semver": "^5.3.0", - "tar": "^2.2.1", - "tar-pack": "^3.4.0" + "rimraf": "2.6.1", + "semver": "5.3.0", + "tar": "2.2.1", + "tar-pack": "3.4.0" } }, "nopt": { @@ -2344,8 +2314,8 @@ "dev": true, "optional": true, "requires": { - "abbrev": "1", - "osenv": "^0.1.4" + "abbrev": "1.1.0", + "osenv": "0.1.4" } }, "npmlog": { @@ -2355,18 +2325,17 @@ "dev": true, "optional": true, "requires": { - "are-we-there-yet": "~1.1.2", - "console-control-strings": "~1.1.0", - "gauge": "~2.7.3", - "set-blocking": "~2.0.0" + "are-we-there-yet": "1.1.4", + "console-control-strings": "1.1.0", + "gauge": "2.7.4", + "set-blocking": "2.0.0" } }, "number-is-nan": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", - "dev": true, - "optional": true + "dev": true }, "oauth-sign": { "version": "0.8.2", @@ -2387,9 +2356,8 @@ "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", "dev": true, - "optional": true, "requires": { - "wrappy": "1" + "wrappy": "1.0.2" } }, "os-homedir": { @@ -2421,8 +2389,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "dev": true, - "optional": true + "dev": true }, "performance-now": { "version": "0.2.0", @@ -2435,8 +2402,7 @@ "version": "1.0.7", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=", - "dev": true, - "optional": true + "dev": true }, "punycode": { "version": "1.4.1", @@ -2459,10 +2425,10 @@ "dev": true, "optional": true, "requires": { - "deep-extend": "~0.4.0", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" + "deep-extend": "0.4.2", + "ini": "1.3.4", + "minimist": "1.2.0", + "strip-json-comments": "2.0.1" }, "dependencies": { "minimist": { @@ -2479,15 +2445,14 @@ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.2.9.tgz", "integrity": "sha1-z3jsb0ptHrQ9JkiMrJfwQudLf8g=", "dev": true, - "optional": true, "requires": { - "buffer-shims": "~1.0.0", - "core-util-is": "~1.0.0", - "inherits": "~2.0.1", - "isarray": "~1.0.0", - "process-nextick-args": "~1.0.6", - "string_decoder": "~1.0.0", - "util-deprecate": "~1.0.1" + "buffer-shims": "1.0.0", + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "1.0.7", + "string_decoder": "1.0.1", + "util-deprecate": "1.0.2" } }, "request": { @@ -2497,28 +2462,28 @@ "dev": true, "optional": true, "requires": { - "aws-sign2": "~0.6.0", - "aws4": "^1.2.1", - "caseless": "~0.12.0", - "combined-stream": "~1.0.5", - "extend": "~3.0.0", - "forever-agent": "~0.6.1", - "form-data": "~2.1.1", - "har-validator": "~4.2.1", - "hawk": "~3.1.3", - "http-signature": "~1.1.0", - "is-typedarray": "~1.0.0", - "isstream": "~0.1.2", - "json-stringify-safe": "~5.0.1", - "mime-types": "~2.1.7", - "oauth-sign": "~0.8.1", - "performance-now": "^0.2.0", - "qs": "~6.4.0", - "safe-buffer": "^5.0.1", - "stringstream": "~0.0.4", - "tough-cookie": "~2.3.0", - "tunnel-agent": "^0.6.0", - "uuid": "^3.0.0" + "aws-sign2": "0.6.0", + "aws4": "1.6.0", + "caseless": "0.12.0", + "combined-stream": "1.0.5", + "extend": "3.0.1", + "forever-agent": "0.6.1", + "form-data": "2.1.4", + "har-validator": "4.2.1", + "hawk": "3.1.3", + "http-signature": "1.1.1", + "is-typedarray": "1.0.0", + "isstream": "0.1.2", + "json-stringify-safe": "5.0.1", + "mime-types": "2.1.15", + "oauth-sign": "0.8.2", + "performance-now": "0.2.0", + "qs": "6.4.0", + "safe-buffer": "5.0.1", + "stringstream": "0.0.5", + "tough-cookie": "2.3.2", + "tunnel-agent": "0.6.0", + "uuid": "3.0.1" } }, "rimraf": { @@ -2526,17 +2491,15 @@ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.1.tgz", "integrity": "sha1-wjOOxkPfeht/5cVPqG9XQopV8z0=", "dev": true, - "optional": true, "requires": { - "glob": "^7.0.5" + "glob": "7.1.2" } }, "safe-buffer": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.0.1.tgz", "integrity": "sha1-0mPKVGls2KMGtcplUekt5XkY++c=", - "dev": true, - "optional": true + "dev": true }, "semver": { "version": "5.3.0", @@ -2564,9 +2527,8 @@ "resolved": "https://registry.npmjs.org/sntp/-/sntp-1.0.9.tgz", "integrity": "sha1-ZUEYTMkK7qbG57NeJlkIJEPGYZg=", "dev": true, - "optional": true, "requires": { - "hoek": "2.x.x" + "hoek": "2.16.3" } }, "sshpk": { @@ -2576,15 +2538,15 @@ "dev": true, "optional": true, "requires": { - "asn1": "~0.2.3", - "assert-plus": "^1.0.0", - "bcrypt-pbkdf": "^1.0.0", - "dashdash": "^1.12.0", - "ecc-jsbn": "~0.1.1", - "getpass": "^0.1.1", - "jodid25519": "^1.0.0", - "jsbn": "~0.1.0", - "tweetnacl": "~0.14.0" + "asn1": "0.2.3", + "assert-plus": "1.0.0", + "bcrypt-pbkdf": "1.0.1", + "dashdash": "1.14.1", + "ecc-jsbn": "0.1.1", + "getpass": "0.1.7", + "jodid25519": "1.0.2", + "jsbn": "0.1.1", + "tweetnacl": "0.14.5" }, "dependencies": { "assert-plus": { @@ -2601,11 +2563,10 @@ "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", "dev": true, - "optional": true, "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" + "code-point-at": "1.1.0", + "is-fullwidth-code-point": "1.0.0", + "strip-ansi": "3.0.1" } }, "string_decoder": { @@ -2613,9 +2574,8 @@ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.1.tgz", "integrity": "sha1-YuIA8DmVWmgQ2N8KM//A8BNmLZg=", "dev": true, - "optional": true, "requires": { - "safe-buffer": "^5.0.1" + "safe-buffer": "5.0.1" } }, "stringstream": { @@ -2630,9 +2590,8 @@ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "dev": true, - "optional": true, "requires": { - "ansi-regex": "^2.0.0" + "ansi-regex": "2.1.1" } }, "strip-json-comments": { @@ -2647,11 +2606,10 @@ "resolved": "https://registry.npmjs.org/tar/-/tar-2.2.1.tgz", "integrity": "sha1-jk0qJWwOIYXGsYrWlK7JaLg8sdE=", "dev": true, - "optional": true, "requires": { - "block-stream": "*", - "fstream": "^1.0.2", - "inherits": "2" + "block-stream": "0.0.9", + "fstream": "1.0.11", + "inherits": "2.0.3" } }, "tar-pack": { @@ -2661,14 +2619,14 @@ "dev": true, "optional": true, "requires": { - "debug": "^2.2.0", - "fstream": "^1.0.10", - "fstream-ignore": "^1.0.5", - "once": "^1.3.3", - "readable-stream": "^2.1.4", - "rimraf": "^2.5.1", - "tar": "^2.2.1", - "uid-number": "^0.0.6" + "debug": "2.6.8", + "fstream": "1.0.11", + "fstream-ignore": "1.0.5", + "once": "1.4.0", + "readable-stream": "2.2.9", + "rimraf": "2.6.1", + "tar": "2.2.1", + "uid-number": "0.0.6" } }, "tough-cookie": { @@ -2678,7 +2636,7 @@ "dev": true, "optional": true, "requires": { - "punycode": "^1.4.1" + "punycode": "1.4.1" } }, "tunnel-agent": { @@ -2688,7 +2646,7 @@ "dev": true, "optional": true, "requires": { - "safe-buffer": "^5.0.1" + "safe-buffer": "5.0.1" } }, "tweetnacl": { @@ -2709,8 +2667,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", - "dev": true, - "optional": true + "dev": true }, "uuid": { "version": "3.0.1", @@ -2736,15 +2693,14 @@ "dev": true, "optional": true, "requires": { - "string-width": "^1.0.2" + "string-width": "1.0.2" } }, "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true, - "optional": true + "dev": true } } }, @@ -2759,7 +2715,7 @@ "resolved": "https://registry.npmjs.org/gelf/-/gelf-2.0.1.tgz", "integrity": "sha512-77duTNPkUgcwFRl+kbmPx2YGMF4e030xBVVsKix5mruTAjuROW36WxmpVSk8MkiZC1D68JYNNb+kfJeZG/D5VQ==", "requires": { - "async": "^2.5.0" + "async": "2.6.2" }, "dependencies": { "async": { @@ -2767,7 +2723,7 @@ "resolved": "https://registry.npmjs.org/async/-/async-2.6.2.tgz", "integrity": "sha512-H1qVYh1MYhEEFLsP97cVKqCGo7KfCyTt6uEWqsTBr9SO84oK9Uwbyd/yCW+6rKJLHksBNUVWZDAjfS+Ccx0Bbg==", "requires": { - "lodash": "^4.17.11" + "lodash": "4.17.11" } }, "lodash": { @@ -2789,7 +2745,7 @@ "integrity": "sha1-nA4cQDCM6AT0eDYYuTf6iPmdUNA=", "dev": true, "requires": { - "is-property": "^1.0.0" + "is-property": "1.0.2" } }, "get-stream": { @@ -2803,7 +2759,7 @@ "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.6.tgz", "integrity": "sha1-KD/9n8ElaECHUxHBtg6MQBhxEOY=", "requires": { - "assert-plus": "^1.0.0" + "assert-plus": "1.0.0" }, "dependencies": { "assert-plus": { @@ -2819,12 +2775,12 @@ "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", "dev": true, "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "fs.realpath": "1.0.0", + "inflight": "1.0.6", + "inherits": "2.0.1", + "minimatch": "3.0.4", + "once": "1.4.0", + "path-is-absolute": "1.0.1" } }, "glob-base": { @@ -2833,8 +2789,8 @@ "integrity": "sha1-27Fk9iIbHAscz4Kuoyi0l98Oo8Q=", "dev": true, "requires": { - "glob-parent": "^2.0.0", - "is-glob": "^2.0.0" + "glob-parent": "2.0.0", + "is-glob": "2.0.1" } }, "glob-parent": { @@ -2843,7 +2799,7 @@ "integrity": "sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg=", "dev": true, "requires": { - "is-glob": "^2.0.0" + "is-glob": "2.0.1" } }, "global-dirs": { @@ -2852,7 +2808,7 @@ "integrity": "sha1-sxnA3UYH81PzvpzKTHL8FIxJ9EU=", "dev": true, "requires": { - "ini": "^1.3.4" + "ini": "1.3.5" } }, "globals": { @@ -2867,12 +2823,12 @@ "integrity": "sha1-69hGZ8oNuzMLmbz8aOrCvFQ3Dg0=", "dev": true, "requires": { - "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" + "array-union": "1.0.2", + "arrify": "1.0.1", + "glob": "7.1.2", + "object-assign": "4.1.1", + "pify": "2.3.0", + "pinkie-promise": "2.0.1" } }, "got": { @@ -2881,17 +2837,17 @@ "integrity": "sha1-JAzQV4WpoY5WHcG0S0HHY+8ejbA=", "dev": true, "requires": { - "create-error-class": "^3.0.0", - "duplexer3": "^0.1.4", - "get-stream": "^3.0.0", - "is-redirect": "^1.0.0", - "is-retry-allowed": "^1.0.0", - "is-stream": "^1.0.0", - "lowercase-keys": "^1.0.0", - "safe-buffer": "^5.0.1", - "timed-out": "^4.0.0", - "unzip-response": "^2.0.1", - "url-parse-lax": "^1.0.0" + "create-error-class": "3.0.2", + "duplexer3": "0.1.4", + "get-stream": "3.0.0", + "is-redirect": "1.0.0", + "is-retry-allowed": "1.1.0", + "is-stream": "1.1.0", + "lowercase-keys": "1.0.0", + "safe-buffer": "5.1.1", + "timed-out": "4.0.1", + "unzip-response": "2.0.1", + "url-parse-lax": "1.0.0" } }, "graceful-fs": { @@ -2918,10 +2874,10 @@ "integrity": "sha1-Ywo13+ApS8KB7a5v/F0yn8eYLcw=", "dev": true, "requires": { - "async": "^1.4.0", - "optimist": "^0.6.1", - "source-map": "^0.4.4", - "uglify-js": "^2.6" + "async": "1.5.2", + "optimist": "0.6.1", + "source-map": "0.4.4", + "uglify-js": "2.8.29" }, "dependencies": { "async": { @@ -2936,7 +2892,7 @@ "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=", "dev": true, "requires": { - "amdefine": ">=0.0.4" + "amdefine": "1.0.1" } } } @@ -2952,10 +2908,10 @@ "integrity": "sha1-zcvAgYgmWtEZtqWnyKtw7s+10n0=", "dev": true, "requires": { - "chalk": "^1.1.1", - "commander": "^2.9.0", - "is-my-json-valid": "^2.12.4", - "pinkie-promise": "^2.0.0" + "chalk": "1.1.3", + "commander": "2.9.0", + "is-my-json-valid": "2.16.1", + "pinkie-promise": "2.0.1" } }, "has": { @@ -2964,7 +2920,7 @@ "integrity": "sha1-hGFzP1OLCDfJNh45qauelwTcLyg=", "dev": true, "requires": { - "function-bind": "^1.0.2" + "function-bind": "1.1.1" } }, "has-ansi": { @@ -2973,7 +2929,7 @@ "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", "dev": true, "requires": { - "ansi-regex": "^2.0.0" + "ansi-regex": "2.1.1" } }, "has-flag": { @@ -2987,10 +2943,10 @@ "resolved": "https://registry.npmjs.org/hawk/-/hawk-3.1.3.tgz", "integrity": "sha1-B4REvXwWQLD+VA0sm3PVlnjo4cQ=", "requires": { - "boom": "2.x.x", - "cryptiles": "2.x.x", - "hoek": "2.x.x", - "sntp": "1.x.x" + "boom": "2.10.1", + "cryptiles": "2.0.5", + "hoek": "2.16.3", + "sntp": "1.0.9" } }, "he": { @@ -3011,7 +2967,7 @@ "requires": { "inherits": "2.0.1", "setprototypeof": "1.0.1", - "statuses": ">= 1.3.0 < 2" + "statuses": "1.3.0" } }, "http-proxy": { @@ -3020,8 +2976,8 @@ "integrity": "sha1-GRXciIdR4qa/PCq/yxgI+obHI1M=", "dev": true, "requires": { - "eventemitter3": "1.x.x", - "requires-port": "0.x.x" + "eventemitter3": "1.2.0", + "requires-port": "0.0.1" } }, "http-signature": { @@ -3029,9 +2985,9 @@ "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.1.1.tgz", "integrity": "sha1-33LiZwZs0Kxn+3at+OE0qPvPkb8=", "requires": { - "assert-plus": "^0.2.0", - "jsprim": "^1.2.2", - "sshpk": "^1.7.0" + "assert-plus": "0.2.0", + "jsprim": "1.3.1", + "sshpk": "1.10.1" } }, "iconv-lite": { @@ -3069,8 +3025,8 @@ "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", "dev": true, "requires": { - "once": "^1.3.0", - "wrappy": "1" + "once": "1.4.0", + "wrappy": "1.0.2" } }, "inherits": { @@ -3090,19 +3046,19 @@ "integrity": "sha1-HvK/1jUE3wvHV4X/+MLEHfEvB34=", "dev": true, "requires": { - "ansi-escapes": "^1.1.0", - "ansi-regex": "^2.0.0", - "chalk": "^1.0.0", - "cli-cursor": "^1.0.1", - "cli-width": "^2.0.0", - "figures": "^1.3.5", - "lodash": "^4.3.0", - "readline2": "^1.0.1", - "run-async": "^0.1.0", - "rx-lite": "^3.1.2", - "string-width": "^1.0.1", - "strip-ansi": "^3.0.0", - "through": "^2.3.6" + "ansi-escapes": "1.4.0", + "ansi-regex": "2.1.1", + "chalk": "1.1.3", + "cli-cursor": "1.0.2", + "cli-width": "2.2.0", + "figures": "1.7.0", + "lodash": "4.16.4", + "readline2": "1.0.1", + "run-async": "0.1.0", + "rx-lite": "3.1.2", + "string-width": "1.0.2", + "strip-ansi": "3.0.1", + "through": "2.3.8" } }, "interpret": { @@ -3116,29 +3072,29 @@ "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-3.1.4.tgz", "integrity": "sha512-gz9h5BVDh5DVfzGAzyUsE/NBABLuDN5inIFKKRq66h9mqtpBFSM5HaOxKgkJLwMhQihZPkadq/xaZP4dlrHwXA==", "requires": { - "bluebird": "^3.3.4", - "cluster-key-slot": "^1.0.6", - "debug": "^2.2.0", - "denque": "^1.1.0", + "bluebird": "3.5.1", + "cluster-key-slot": "1.0.8", + "debug": "2.2.0", + "denque": "1.2.2", "flexbuffer": "0.0.6", - "lodash.assign": "^4.2.0", - "lodash.bind": "^4.2.1", - "lodash.clone": "^4.5.0", - "lodash.clonedeep": "^4.5.0", - "lodash.defaults": "^4.2.0", - "lodash.difference": "^4.5.0", - "lodash.flatten": "^4.4.0", - "lodash.foreach": "^4.5.0", - "lodash.isempty": "^4.4.0", - "lodash.keys": "^4.2.0", - "lodash.noop": "^3.0.1", - "lodash.partial": "^4.2.1", - "lodash.pick": "^4.4.0", - "lodash.sample": "^4.2.1", - "lodash.shuffle": "^4.2.0", - "lodash.values": "^4.3.0", - "redis-commands": "^1.2.0", - "redis-parser": "^2.4.0" + "lodash.assign": "4.2.0", + "lodash.bind": "4.2.1", + "lodash.clone": "4.5.0", + "lodash.clonedeep": "4.5.0", + "lodash.defaults": "4.2.0", + "lodash.difference": "4.5.0", + "lodash.flatten": "4.4.0", + "lodash.foreach": "4.5.0", + "lodash.isempty": "4.4.0", + "lodash.keys": "4.2.0", + "lodash.noop": "3.0.1", + "lodash.partial": "4.2.1", + "lodash.pick": "4.4.0", + "lodash.sample": "4.2.1", + "lodash.shuffle": "4.2.0", + "lodash.values": "4.3.0", + "redis-commands": "1.3.1", + "redis-parser": "2.6.0" }, "dependencies": { "lodash.assign": { @@ -3169,7 +3125,7 @@ "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", "dev": true, "requires": { - "binary-extensions": "^1.0.0" + "binary-extensions": "1.11.0" } }, "is-buffer": { @@ -3202,7 +3158,7 @@ "integrity": "sha1-IjgJj8Ih3gvPpdnqxMRdY4qhxTQ=", "dev": true, "requires": { - "is-primitive": "^2.0.0" + "is-primitive": "2.0.0" } }, "is-extendable": { @@ -3223,7 +3179,7 @@ "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", "dev": true, "requires": { - "number-is-nan": "^1.0.0" + "number-is-nan": "1.0.1" } }, "is-glob": { @@ -3232,7 +3188,7 @@ "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", "dev": true, "requires": { - "is-extglob": "^1.0.0" + "is-extglob": "1.0.0" } }, "is-installed-globally": { @@ -3241,8 +3197,8 @@ "integrity": "sha1-Df2Y9akRFxbdU13aZJL2e/PSWoA=", "dev": true, "requires": { - "global-dirs": "^0.1.0", - "is-path-inside": "^1.0.0" + "global-dirs": "0.1.1", + "is-path-inside": "1.0.0" } }, "is-my-json-valid": { @@ -3251,10 +3207,10 @@ "integrity": "sha512-ochPsqWS1WXj8ZnMIV0vnNXooaMhp7cyL4FMSIPKTtnV0Ha/T19G2b9kkhcNsabV9bxYkze7/aLZJb/bYuFduQ==", "dev": true, "requires": { - "generate-function": "^2.0.0", - "generate-object-property": "^1.1.0", - "jsonpointer": "^4.0.0", - "xtend": "^4.0.0" + "generate-function": "2.0.0", + "generate-object-property": "1.2.0", + "jsonpointer": "4.0.1", + "xtend": "4.0.1" } }, "is-npm": { @@ -3269,7 +3225,7 @@ "integrity": "sha1-Afy7s5NGOlSPL0ZszhbezknbkI8=", "dev": true, "requires": { - "kind-of": "^3.0.2" + "kind-of": "3.2.2" } }, "is-obj": { @@ -3290,7 +3246,7 @@ "integrity": "sha1-ZHdYK4IU1gI0YJRWcAO+ip6sBNw=", "dev": true, "requires": { - "is-path-inside": "^1.0.0" + "is-path-inside": "1.0.0" } }, "is-path-inside": { @@ -3299,7 +3255,7 @@ "integrity": "sha1-/AbloWg/vaE95mev9xe7wQpI838=", "dev": true, "requires": { - "path-is-inside": "^1.0.1" + "path-is-inside": "1.0.2" } }, "is-posix-bracket": { @@ -3332,7 +3288,7 @@ "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=", "dev": true, "requires": { - "has": "^1.0.1" + "has": "1.0.1" } }, "is-resolvable": { @@ -3341,7 +3297,7 @@ "integrity": "sha1-jfV8YeouPFAUCNEA+wE8+NbgzGI=", "dev": true, "requires": { - "tryit": "^1.0.1" + "tryit": "1.0.3" } }, "is-retry-allowed": { @@ -3399,20 +3355,20 @@ "integrity": "sha1-ZcfXPUxNqE1POsMQuRj7C4Azczs=", "dev": true, "requires": { - "abbrev": "1.0.x", - "async": "1.x", - "escodegen": "1.8.x", - "esprima": "2.7.x", - "glob": "^5.0.15", - "handlebars": "^4.0.1", - "js-yaml": "3.x", - "mkdirp": "0.5.x", - "nopt": "3.x", - "once": "1.x", - "resolve": "1.1.x", - "supports-color": "^3.1.0", - "which": "^1.1.1", - "wordwrap": "^1.0.0" + "abbrev": "1.0.9", + "async": "1.5.2", + "escodegen": "1.8.1", + "esprima": "2.7.3", + "glob": "5.0.15", + "handlebars": "4.0.11", + "js-yaml": "3.10.0", + "mkdirp": "0.5.1", + "nopt": "3.0.6", + "once": "1.4.0", + "resolve": "1.1.7", + "supports-color": "3.2.3", + "which": "1.3.0", + "wordwrap": "1.0.0" }, "dependencies": { "async": { @@ -3433,11 +3389,11 @@ "integrity": "sha1-G8k2ueAvSmA/zCIuz3Yz0wuLk7E=", "dev": true, "requires": { - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "2 || 3", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "inflight": "1.0.6", + "inherits": "2.0.1", + "minimatch": "3.0.4", + "once": "1.4.0", + "path-is-absolute": "1.0.1" } }, "resolve": { @@ -3452,7 +3408,7 @@ "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", "dev": true, "requires": { - "has-flag": "^1.0.0" + "has-flag": "1.0.0" } } } @@ -3463,11 +3419,11 @@ "integrity": "sha1-TxwVkr4QTVkfkzy/nA8vUoStzwA=", "dev": true, "requires": { - "chalk": "^1.0.0", - "coveralls": "^2.11.2", - "minimist": "^1.1.1", - "rimraf": "^2.3.4", - "sum-up": "^1.0.1" + "chalk": "1.1.3", + "coveralls": "2.13.3", + "minimist": "1.2.0", + "rimraf": "2.6.2", + "sum-up": "1.0.3" }, "dependencies": { "minimist": { @@ -3484,7 +3440,7 @@ "integrity": "sha1-BtSRIlUJNBlHfUJWM2BuDpB4KWc=", "optional": true, "requires": { - "jsbn": "~0.1.0" + "jsbn": "0.1.0" } }, "js-tokens": { @@ -3499,8 +3455,8 @@ "integrity": "sha512-O2v52ffjLa9VeM43J4XocZE//WT9N0IiwDa3KSHH7Tu8CtH+1qM8SIZvnsTh6v+4yFy5KUY3BHUVwjpfAWsjIA==", "dev": true, "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" + "argparse": "1.0.9", + "esprima": "4.0.0" } }, "jsbn": { @@ -3519,7 +3475,7 @@ "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz", "integrity": "sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8=", "requires": { - "jsonify": "~0.0.0" + "jsonify": "0.0.0" } }, "json-stringify-safe": { @@ -3577,7 +3533,7 @@ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, "requires": { - "is-buffer": "^1.1.5" + "is-buffer": "1.1.6" } }, "latest-version": { @@ -3586,7 +3542,7 @@ "integrity": "sha1-ogU4P+oyKzO1rjsYq+4NwvNW7hU=", "dev": true, "requires": { - "package-json": "^4.0.0" + "package-json": "4.0.1" } }, "lazy-cache": { @@ -3608,8 +3564,8 @@ "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", "dev": true, "requires": { - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2" + "prelude-ls": "1.1.2", + "type-check": "0.3.2" } }, "lodash": { @@ -3623,8 +3579,8 @@ "integrity": "sha1-jDigmVAPIVrQnlnxci/QxSv+Ck4=", "dev": true, "requires": { - "lodash._basecopy": "^3.0.0", - "lodash.keys": "^3.0.0" + "lodash._basecopy": "3.0.1", + "lodash.keys": "3.1.2" } }, "lodash._basecopy": { @@ -3651,9 +3607,9 @@ "integrity": "sha1-g4pbri/aymOsIt7o4Z+k5taXCxE=", "dev": true, "requires": { - "lodash._bindcallback": "^3.0.0", - "lodash._isiterateecall": "^3.0.0", - "lodash.restparam": "^3.0.0" + "lodash._bindcallback": "3.0.1", + "lodash._isiterateecall": "3.0.9", + "lodash.restparam": "3.6.1" } }, "lodash._getnative": { @@ -3674,9 +3630,9 @@ "integrity": "sha1-POnwI0tLIiPilrj6CsH+6OvKZPo=", "dev": true, "requires": { - "lodash._baseassign": "^3.0.0", - "lodash._createassigner": "^3.0.0", - "lodash.keys": "^3.0.0" + "lodash._baseassign": "3.2.0", + "lodash._createassigner": "3.1.1", + "lodash.keys": "3.1.2" } }, "lodash.bind": { @@ -3706,9 +3662,9 @@ "integrity": "sha1-1/KEnw29p+BGgruM1yqwIkYd6+c=", "dev": true, "requires": { - "lodash._baseassign": "^3.0.0", - "lodash._basecreate": "^3.0.0", - "lodash._isiterateecall": "^3.0.0" + "lodash._baseassign": "3.2.0", + "lodash._basecreate": "3.0.3", + "lodash._isiterateecall": "3.0.9" } }, "lodash.defaults": { @@ -3717,8 +3673,8 @@ "integrity": "sha1-xzCLGNv4vJNy1wGnNJPGEZK9Liw=", "dev": true, "requires": { - "lodash.assign": "^3.0.0", - "lodash.restparam": "^3.0.0" + "lodash.assign": "3.2.0", + "lodash.restparam": "3.6.1" } }, "lodash.difference": { @@ -3783,9 +3739,9 @@ "integrity": "sha1-TbwEcrFWvlCgsoaFXRvQsMZWCYo=", "dev": true, "requires": { - "lodash._getnative": "^3.0.0", - "lodash.isarguments": "^3.0.0", - "lodash.isarray": "^3.0.0" + "lodash._getnative": "3.9.1", + "lodash.isarguments": "3.1.0", + "lodash.isarray": "3.0.4" } }, "lodash.noop": { @@ -3840,8 +3796,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz", "integrity": "sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc=", - "dev": true, - "optional": true + "dev": true }, "lowercase-keys": { "version": "1.0.0", @@ -3855,8 +3810,8 @@ "integrity": "sha512-q4spe4KTfsAS1SUHLO0wz8Qiyf1+vMIAgpRYioFYDMNqKfHQbg+AVDH3i4fvpl71/P1L0dBl+fQi+P37UYf0ew==", "dev": true, "requires": { - "pseudomap": "^1.0.2", - "yallist": "^2.1.2" + "pseudomap": "1.0.2", + "yallist": "2.1.2" } }, "lsmod": { @@ -3870,7 +3825,7 @@ "integrity": "sha512-0Pkui4wLJ7rxvmfUvs87skoEaxmu0hCUApF8nonzpl7q//FWp9zu8W61Scz4sd/kUiqDxvUhtoam2efDyiBzcA==", "dev": true, "requires": { - "pify": "^3.0.0" + "pify": "3.0.0" }, "dependencies": { "pify": { @@ -3908,19 +3863,19 @@ "integrity": "sha1-hmd8l9FyCzY0MdBNDRUpO9OMFWU=", "dev": true, "requires": { - "arr-diff": "^2.0.0", - "array-unique": "^0.2.1", - "braces": "^1.8.2", - "expand-brackets": "^0.1.4", - "extglob": "^0.3.1", - "filename-regex": "^2.0.0", - "is-extglob": "^1.0.0", - "is-glob": "^2.0.1", - "kind-of": "^3.0.2", - "normalize-path": "^2.0.1", - "object.omit": "^2.0.0", - "parse-glob": "^3.0.4", - "regex-cache": "^0.4.2" + "arr-diff": "2.0.0", + "array-unique": "0.2.1", + "braces": "1.8.5", + "expand-brackets": "0.1.5", + "extglob": "0.3.2", + "filename-regex": "2.0.1", + "is-extglob": "1.0.0", + "is-glob": "2.0.1", + "kind-of": "3.2.2", + "normalize-path": "2.1.1", + "object.omit": "2.0.1", + "parse-glob": "3.0.4", + "regex-cache": "0.4.4" } }, "mime": { @@ -3938,7 +3893,7 @@ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.12.tgz", "integrity": "sha1-FSuiVndwIN1GY/VMLnvCY4HnFyk=", "requires": { - "mime-db": "~1.24.0" + "mime-db": "1.24.0" } }, "minimatch": { @@ -3947,7 +3902,7 @@ "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", "dev": true, "requires": { - "brace-expansion": "^1.1.7" + "brace-expansion": "1.1.8" } }, "minimist": { @@ -4000,12 +3955,12 @@ "integrity": "sha1-gFIR3wT6rxxjo2ADBs31reULLsg=", "dev": true, "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.2", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "fs.realpath": "1.0.0", + "inflight": "1.0.6", + "inherits": "2.0.1", + "minimatch": "3.0.4", + "once": "1.4.0", + "path-is-absolute": "1.0.1" } }, "ms": { @@ -4020,7 +3975,7 @@ "integrity": "sha1-cqJiiU2dQIuVbKBf83su2KbiotU=", "dev": true, "requires": { - "has-flag": "^1.0.0" + "has-flag": "1.0.0" } } } @@ -4030,11 +3985,11 @@ "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.7.0.tgz", "integrity": "sha1-6xDKjlDRq+D409rVwCAdBS2YHGI=", "requires": { - "basic-auth": "~1.0.3", - "debug": "~2.2.0", - "depd": "~1.1.0", - "on-finished": "~2.3.0", - "on-headers": "~1.0.1" + "basic-auth": "1.0.4", + "debug": "2.2.0", + "depd": "1.1.0", + "on-finished": "2.3.0", + "on-headers": "1.0.1" } }, "ms": { @@ -4078,11 +4033,11 @@ "integrity": "sha512-q9jXh3UNsMV28KeqI43ILz5+c3l+RiNW8mhurEwCKckuHQbL+hTJIKKTiUlCPKlgQ/OukFvSnKB/Jk3+sFbkGA==", "dev": true, "requires": { - "formatio": "^1.2.0", - "just-extend": "^1.1.26", - "lolex": "^1.6.0", - "path-to-regexp": "^1.7.0", - "text-encoding": "^0.6.4" + "formatio": "1.2.0", + "just-extend": "1.1.27", + "lolex": "1.6.0", + "path-to-regexp": "1.7.0", + "text-encoding": "0.6.4" }, "dependencies": { "isarray": { @@ -4119,16 +4074,16 @@ "integrity": "sha1-mWpW3EnZ8Wu/G3ik3gjxNjSzh40=", "dev": true, "requires": { - "chokidar": "^1.7.0", - "debug": "^2.6.8", - "es6-promise": "^3.3.1", - "ignore-by-default": "^1.0.1", - "lodash.defaults": "^3.1.2", - "minimatch": "^3.0.4", - "ps-tree": "^1.1.0", - "touch": "^3.1.0", + "chokidar": "1.7.0", + "debug": "2.6.9", + "es6-promise": "3.3.1", + "ignore-by-default": "1.0.1", + "lodash.defaults": "3.1.2", + "minimatch": "3.0.4", + "ps-tree": "1.1.0", + "touch": "3.1.0", "undefsafe": "0.0.3", - "update-notifier": "^2.2.0" + "update-notifier": "2.3.0" }, "dependencies": { "debug": { @@ -4154,7 +4109,7 @@ "integrity": "sha1-xkZdvwirzU2zWTF/eaxopkayj/k=", "dev": true, "requires": { - "abbrev": "1" + "abbrev": "1.0.9" } }, "normalize-path": { @@ -4163,7 +4118,7 @@ "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", "dev": true, "requires": { - "remove-trailing-separator": "^1.0.1" + "remove-trailing-separator": "1.1.0" } }, "npm-run-path": { @@ -4172,7 +4127,7 @@ "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", "dev": true, "requires": { - "path-key": "^2.0.0" + "path-key": "2.0.1" } }, "number-is-nan": { @@ -4187,33 +4142,33 @@ "integrity": "sha512-oUu0WHt1k/JMIODvAYXX6C50Mupw2GO34P/Jdg2ty9xrLufBthHiKR2gf08aF+9S0abW1fl24R7iKRBXzibZmg==", "dev": true, "requires": { - "archy": "^1.0.0", - "arrify": "^1.0.1", - "caching-transform": "^1.0.0", - "convert-source-map": "^1.3.0", - "debug-log": "^1.0.1", - "default-require-extensions": "^1.0.0", - "find-cache-dir": "^0.1.1", - "find-up": "^2.1.0", - "foreground-child": "^1.5.3", - "glob": "^7.0.6", - "istanbul-lib-coverage": "^1.1.1", - "istanbul-lib-hook": "^1.1.0", - "istanbul-lib-instrument": "^1.9.1", - "istanbul-lib-report": "^1.1.2", - "istanbul-lib-source-maps": "^1.2.2", - "istanbul-reports": "^1.1.3", - "md5-hex": "^1.2.0", - "merge-source-map": "^1.0.2", - "micromatch": "^2.3.11", - "mkdirp": "^0.5.0", - "resolve-from": "^2.0.0", - "rimraf": "^2.5.4", - "signal-exit": "^3.0.1", - "spawn-wrap": "=1.3.8", - "test-exclude": "^4.1.1", - "yargs": "^10.0.3", - "yargs-parser": "^8.0.0" + "archy": "1.0.0", + "arrify": "1.0.1", + "caching-transform": "1.0.1", + "convert-source-map": "1.5.0", + "debug-log": "1.0.1", + "default-require-extensions": "1.0.0", + "find-cache-dir": "0.1.1", + "find-up": "2.1.0", + "foreground-child": "1.5.6", + "glob": "7.1.2", + "istanbul-lib-coverage": "1.1.1", + "istanbul-lib-hook": "1.1.0", + "istanbul-lib-instrument": "1.9.1", + "istanbul-lib-report": "1.1.2", + "istanbul-lib-source-maps": "1.2.2", + "istanbul-reports": "1.1.3", + "md5-hex": "1.3.0", + "merge-source-map": "1.0.4", + "micromatch": "2.3.11", + "mkdirp": "0.5.1", + "resolve-from": "2.0.0", + "rimraf": "2.6.2", + "signal-exit": "3.0.2", + "spawn-wrap": "1.3.8", + "test-exclude": "4.1.1", + "yargs": "10.0.3", + "yargs-parser": "8.0.0" }, "dependencies": { "align-text": { @@ -4221,11 +4176,10 @@ "resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz", "integrity": "sha1-DNkKVhCT810KmSVsIrcGlDP60Rc=", "dev": true, - "optional": true, "requires": { - "kind-of": "^3.0.2", - "longest": "^1.0.1", - "repeat-string": "^1.5.2" + "kind-of": "3.2.2", + "longest": "1.0.1", + "repeat-string": "1.6.1" } }, "amdefine": { @@ -4252,7 +4206,7 @@ "integrity": "sha1-126/jKlNJ24keja61EpLdKthGZE=", "dev": true, "requires": { - "default-require-extensions": "^1.0.0" + "default-require-extensions": "1.0.0" } }, "archy": { @@ -4267,7 +4221,7 @@ "integrity": "sha1-jzuCf5Vai9ZpaX5KQlasPOrjVs8=", "dev": true, "requires": { - "arr-flatten": "^1.0.1" + "arr-flatten": "1.1.0" } }, "arr-flatten": { @@ -4300,9 +4254,9 @@ "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=", "dev": true, "requires": { - "chalk": "^1.1.3", - "esutils": "^2.0.2", - "js-tokens": "^3.0.2" + "chalk": "1.1.3", + "esutils": "2.0.2", + "js-tokens": "3.0.2" } }, "babel-generator": { @@ -4311,14 +4265,14 @@ "integrity": "sha1-rBriAHC3n248odMmlhMFN3TyDcU=", "dev": true, "requires": { - "babel-messages": "^6.23.0", - "babel-runtime": "^6.26.0", - "babel-types": "^6.26.0", - "detect-indent": "^4.0.0", - "jsesc": "^1.3.0", - "lodash": "^4.17.4", - "source-map": "^0.5.6", - "trim-right": "^1.0.1" + "babel-messages": "6.23.0", + "babel-runtime": "6.26.0", + "babel-types": "6.26.0", + "detect-indent": "4.0.0", + "jsesc": "1.3.0", + "lodash": "4.17.4", + "source-map": "0.5.7", + "trim-right": "1.0.1" } }, "babel-messages": { @@ -4327,7 +4281,7 @@ "integrity": "sha1-8830cDhYA1sqKVHG7F7fbGLyYw4=", "dev": true, "requires": { - "babel-runtime": "^6.22.0" + "babel-runtime": "6.26.0" } }, "babel-runtime": { @@ -4336,8 +4290,8 @@ "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", "dev": true, "requires": { - "core-js": "^2.4.0", - "regenerator-runtime": "^0.11.0" + "core-js": "2.5.1", + "regenerator-runtime": "0.11.0" } }, "babel-template": { @@ -4346,11 +4300,11 @@ "integrity": "sha1-3gPi0WOWsGn0bdn/+FIfsaDjXgI=", "dev": true, "requires": { - "babel-runtime": "^6.26.0", - "babel-traverse": "^6.26.0", - "babel-types": "^6.26.0", - "babylon": "^6.18.0", - "lodash": "^4.17.4" + "babel-runtime": "6.26.0", + "babel-traverse": "6.26.0", + "babel-types": "6.26.0", + "babylon": "6.18.0", + "lodash": "4.17.4" } }, "babel-traverse": { @@ -4359,15 +4313,15 @@ "integrity": "sha1-RqnL1+3MYsjlwGTi0tjQ9ANXZu4=", "dev": true, "requires": { - "babel-code-frame": "^6.26.0", - "babel-messages": "^6.23.0", - "babel-runtime": "^6.26.0", - "babel-types": "^6.26.0", - "babylon": "^6.18.0", - "debug": "^2.6.8", - "globals": "^9.18.0", - "invariant": "^2.2.2", - "lodash": "^4.17.4" + "babel-code-frame": "6.26.0", + "babel-messages": "6.23.0", + "babel-runtime": "6.26.0", + "babel-types": "6.26.0", + "babylon": "6.18.0", + "debug": "2.6.9", + "globals": "9.18.0", + "invariant": "2.2.2", + "lodash": "4.17.4" } }, "babel-types": { @@ -4376,10 +4330,10 @@ "integrity": "sha1-o7Bz+Uq0nrb6Vc1lInozQ4BjJJc=", "dev": true, "requires": { - "babel-runtime": "^6.26.0", - "esutils": "^2.0.2", - "lodash": "^4.17.4", - "to-fast-properties": "^1.0.3" + "babel-runtime": "6.26.0", + "esutils": "2.0.2", + "lodash": "4.17.4", + "to-fast-properties": "1.0.3" } }, "babylon": { @@ -4400,7 +4354,7 @@ "integrity": "sha1-wHshHHyVLsH479Uad+8NHTmQopI=", "dev": true, "requires": { - "balanced-match": "^1.0.0", + "balanced-match": "1.0.0", "concat-map": "0.0.1" } }, @@ -4410,9 +4364,9 @@ "integrity": "sha1-uneWLhLf+WnWt2cR6RS3N4V79qc=", "dev": true, "requires": { - "expand-range": "^1.8.1", - "preserve": "^0.2.0", - "repeat-element": "^1.1.2" + "expand-range": "1.8.2", + "preserve": "0.2.0", + "repeat-element": "1.1.2" } }, "builtin-modules": { @@ -4427,9 +4381,9 @@ "integrity": "sha1-bb2y8g+Nj7znnz6U6dF0Lc31wKE=", "dev": true, "requires": { - "md5-hex": "^1.2.0", - "mkdirp": "^0.5.1", - "write-file-atomic": "^1.1.4" + "md5-hex": "1.3.0", + "mkdirp": "0.5.1", + "write-file-atomic": "1.3.4" } }, "camelcase": { @@ -4446,8 +4400,8 @@ "dev": true, "optional": true, "requires": { - "align-text": "^0.1.3", - "lazy-cache": "^1.0.3" + "align-text": "0.1.4", + "lazy-cache": "1.0.4" } }, "chalk": { @@ -4456,11 +4410,11 @@ "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", "dev": true, "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" } }, "cliui": { @@ -4470,8 +4424,8 @@ "dev": true, "optional": true, "requires": { - "center-align": "^0.1.1", - "right-align": "^0.1.1", + "center-align": "0.1.3", + "right-align": "0.1.3", "wordwrap": "0.0.2" }, "dependencies": { @@ -4520,8 +4474,8 @@ "integrity": "sha1-e5JHYhwjrf3ThWAEqCPL45dCTUE=", "dev": true, "requires": { - "lru-cache": "^4.0.1", - "which": "^1.2.9" + "lru-cache": "4.1.1", + "which": "1.3.0" } }, "debug": { @@ -4551,7 +4505,7 @@ "integrity": "sha1-836hXT4T/9m0N9M+GnW1+5eHTLg=", "dev": true, "requires": { - "strip-bom": "^2.0.0" + "strip-bom": "2.0.0" } }, "detect-indent": { @@ -4560,7 +4514,7 @@ "integrity": "sha1-920GQ1LN9Docts5hnE7jqUdd4gg=", "dev": true, "requires": { - "repeating": "^2.0.0" + "repeating": "2.0.1" } }, "error-ex": { @@ -4569,7 +4523,7 @@ "integrity": "sha1-+FWobOYa3E6GIcPNoh56dhLDqNw=", "dev": true, "requires": { - "is-arrayish": "^0.2.1" + "is-arrayish": "0.2.1" } }, "escape-string-regexp": { @@ -4590,13 +4544,13 @@ "integrity": "sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=", "dev": true, "requires": { - "cross-spawn": "^5.0.1", - "get-stream": "^3.0.0", - "is-stream": "^1.1.0", - "npm-run-path": "^2.0.0", - "p-finally": "^1.0.0", - "signal-exit": "^3.0.0", - "strip-eof": "^1.0.0" + "cross-spawn": "5.1.0", + "get-stream": "3.0.0", + "is-stream": "1.1.0", + "npm-run-path": "2.0.2", + "p-finally": "1.0.0", + "signal-exit": "3.0.2", + "strip-eof": "1.0.0" }, "dependencies": { "cross-spawn": { @@ -4605,9 +4559,9 @@ "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", "dev": true, "requires": { - "lru-cache": "^4.0.1", - "shebang-command": "^1.2.0", - "which": "^1.2.9" + "lru-cache": "4.1.1", + "shebang-command": "1.2.0", + "which": "1.3.0" } } } @@ -4618,7 +4572,7 @@ "integrity": "sha1-3wcoTjQqgHzXM6xa9yQR5YHRF3s=", "dev": true, "requires": { - "is-posix-bracket": "^0.1.0" + "is-posix-bracket": "0.1.1" } }, "expand-range": { @@ -4627,7 +4581,7 @@ "integrity": "sha1-opnv/TNf4nIeuujiV+x5ZE/IUzc=", "dev": true, "requires": { - "fill-range": "^2.1.0" + "fill-range": "2.2.3" } }, "extglob": { @@ -4636,7 +4590,7 @@ "integrity": "sha1-Lhj/PS9JqydlzskCPwEdqo2DSaE=", "dev": true, "requires": { - "is-extglob": "^1.0.0" + "is-extglob": "1.0.0" } }, "filename-regex": { @@ -4651,11 +4605,11 @@ "integrity": "sha1-ULd9/X5Gm8dJJHCWNpn+eoSFpyM=", "dev": true, "requires": { - "is-number": "^2.1.0", - "isobject": "^2.0.0", - "randomatic": "^1.1.3", - "repeat-element": "^1.1.2", - "repeat-string": "^1.5.2" + "is-number": "2.1.0", + "isobject": "2.1.0", + "randomatic": "1.1.7", + "repeat-element": "1.1.2", + "repeat-string": "1.6.1" } }, "find-cache-dir": { @@ -4664,9 +4618,9 @@ "integrity": "sha1-yN765XyKUqinhPnjHFfHQumToLk=", "dev": true, "requires": { - "commondir": "^1.0.1", - "mkdirp": "^0.5.1", - "pkg-dir": "^1.0.0" + "commondir": "1.0.1", + "mkdirp": "0.5.1", + "pkg-dir": "1.0.0" } }, "find-up": { @@ -4675,7 +4629,7 @@ "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", "dev": true, "requires": { - "locate-path": "^2.0.0" + "locate-path": "2.0.0" } }, "for-in": { @@ -4690,7 +4644,7 @@ "integrity": "sha1-UmXGgaTylNq78XyVCbZ2OqhFEM4=", "dev": true, "requires": { - "for-in": "^1.0.1" + "for-in": "1.0.2" } }, "foreground-child": { @@ -4699,8 +4653,8 @@ "integrity": "sha1-T9ca0t/elnibmApcCilZN8svXOk=", "dev": true, "requires": { - "cross-spawn": "^4", - "signal-exit": "^3.0.0" + "cross-spawn": "4.0.2", + "signal-exit": "3.0.2" } }, "fs.realpath": { @@ -4727,12 +4681,12 @@ "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", "dev": true, "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "fs.realpath": "1.0.0", + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.4.0", + "path-is-absolute": "1.0.1" } }, "glob-base": { @@ -4741,8 +4695,8 @@ "integrity": "sha1-27Fk9iIbHAscz4Kuoyi0l98Oo8Q=", "dev": true, "requires": { - "glob-parent": "^2.0.0", - "is-glob": "^2.0.0" + "glob-parent": "2.0.0", + "is-glob": "2.0.1" } }, "glob-parent": { @@ -4751,7 +4705,7 @@ "integrity": "sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg=", "dev": true, "requires": { - "is-glob": "^2.0.0" + "is-glob": "2.0.1" } }, "globals": { @@ -4772,10 +4726,10 @@ "integrity": "sha1-Ywo13+ApS8KB7a5v/F0yn8eYLcw=", "dev": true, "requires": { - "async": "^1.4.0", - "optimist": "^0.6.1", - "source-map": "^0.4.4", - "uglify-js": "^2.6" + "async": "1.5.2", + "optimist": "0.6.1", + "source-map": "0.4.4", + "uglify-js": "2.8.29" }, "dependencies": { "source-map": { @@ -4784,7 +4738,7 @@ "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=", "dev": true, "requires": { - "amdefine": ">=0.0.4" + "amdefine": "1.0.1" } } } @@ -4795,7 +4749,7 @@ "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", "dev": true, "requires": { - "ansi-regex": "^2.0.0" + "ansi-regex": "2.1.1" } }, "has-flag": { @@ -4822,8 +4776,8 @@ "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", "dev": true, "requires": { - "once": "^1.3.0", - "wrappy": "1" + "once": "1.4.0", + "wrappy": "1.0.2" } }, "inherits": { @@ -4838,7 +4792,7 @@ "integrity": "sha1-nh9WrArNtr8wMwbzOL47IErmA2A=", "dev": true, "requires": { - "loose-envify": "^1.0.0" + "loose-envify": "1.3.1" } }, "invert-kv": { @@ -4865,7 +4819,7 @@ "integrity": "sha1-VAVy0096wxGfj3bDDLwbHgN6/74=", "dev": true, "requires": { - "builtin-modules": "^1.0.0" + "builtin-modules": "1.1.1" } }, "is-dotfile": { @@ -4880,7 +4834,7 @@ "integrity": "sha1-IjgJj8Ih3gvPpdnqxMRdY4qhxTQ=", "dev": true, "requires": { - "is-primitive": "^2.0.0" + "is-primitive": "2.0.0" } }, "is-extendable": { @@ -4901,7 +4855,7 @@ "integrity": "sha1-zGZ3aVYCvlUO8R6LSqYwU0K20Ko=", "dev": true, "requires": { - "number-is-nan": "^1.0.0" + "number-is-nan": "1.0.1" } }, "is-fullwidth-code-point": { @@ -4910,7 +4864,7 @@ "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", "dev": true, "requires": { - "number-is-nan": "^1.0.0" + "number-is-nan": "1.0.1" } }, "is-glob": { @@ -4919,7 +4873,7 @@ "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", "dev": true, "requires": { - "is-extglob": "^1.0.0" + "is-extglob": "1.0.0" } }, "is-number": { @@ -4928,7 +4882,7 @@ "integrity": "sha1-Afy7s5NGOlSPL0ZszhbezknbkI8=", "dev": true, "requires": { - "kind-of": "^3.0.2" + "kind-of": "3.2.2" } }, "is-posix-bracket": { @@ -4988,7 +4942,7 @@ "integrity": "sha512-U3qEgwVDUerZ0bt8cfl3dSP3S6opBoOtk3ROO5f2EfBr/SRiD9FQqzwaZBqFORu8W7O0EXpai+k7kxHK13beRg==", "dev": true, "requires": { - "append-transform": "^0.4.0" + "append-transform": "0.4.0" } }, "istanbul-lib-instrument": { @@ -4997,13 +4951,13 @@ "integrity": "sha512-RQmXeQ7sphar7k7O1wTNzVczF9igKpaeGQAG9qR2L+BS4DCJNTI9nytRmIVYevwO0bbq+2CXvJmYDuz0gMrywA==", "dev": true, "requires": { - "babel-generator": "^6.18.0", - "babel-template": "^6.16.0", - "babel-traverse": "^6.18.0", - "babel-types": "^6.18.0", - "babylon": "^6.18.0", - "istanbul-lib-coverage": "^1.1.1", - "semver": "^5.3.0" + "babel-generator": "6.26.0", + "babel-template": "6.26.0", + "babel-traverse": "6.26.0", + "babel-types": "6.26.0", + "babylon": "6.18.0", + "istanbul-lib-coverage": "1.1.1", + "semver": "5.4.1" } }, "istanbul-lib-report": { @@ -5012,10 +4966,10 @@ "integrity": "sha512-UTv4VGx+HZivJQwAo1wnRwe1KTvFpfi/NYwN7DcsrdzMXwpRT/Yb6r4SBPoHWj4VuQPakR32g4PUUeyKkdDkBA==", "dev": true, "requires": { - "istanbul-lib-coverage": "^1.1.1", - "mkdirp": "^0.5.1", - "path-parse": "^1.0.5", - "supports-color": "^3.1.2" + "istanbul-lib-coverage": "1.1.1", + "mkdirp": "0.5.1", + "path-parse": "1.0.5", + "supports-color": "3.2.3" }, "dependencies": { "supports-color": { @@ -5024,7 +4978,7 @@ "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", "dev": true, "requires": { - "has-flag": "^1.0.0" + "has-flag": "1.0.0" } } } @@ -5035,11 +4989,11 @@ "integrity": "sha512-8BfdqSfEdtip7/wo1RnrvLpHVEd8zMZEDmOFEnpC6dg0vXflHt9nvoAyQUzig2uMSXfF2OBEYBV3CVjIL9JvaQ==", "dev": true, "requires": { - "debug": "^3.1.0", - "istanbul-lib-coverage": "^1.1.1", - "mkdirp": "^0.5.1", - "rimraf": "^2.6.1", - "source-map": "^0.5.3" + "debug": "3.1.0", + "istanbul-lib-coverage": "1.1.1", + "mkdirp": "0.5.1", + "rimraf": "2.6.2", + "source-map": "0.5.7" }, "dependencies": { "debug": { @@ -5059,7 +5013,7 @@ "integrity": "sha512-ZEelkHh8hrZNI5xDaKwPMFwDsUf5wIEI2bXAFGp1e6deR2mnEKBPhLJEgr4ZBt8Gi6Mj38E/C8kcy9XLggVO2Q==", "dev": true, "requires": { - "handlebars": "^4.0.3" + "handlebars": "4.0.11" } }, "js-tokens": { @@ -5080,7 +5034,7 @@ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, "requires": { - "is-buffer": "^1.1.5" + "is-buffer": "1.1.5" } }, "lazy-cache": { @@ -5096,7 +5050,7 @@ "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=", "dev": true, "requires": { - "invert-kv": "^1.0.0" + "invert-kv": "1.0.0" } }, "load-json-file": { @@ -5105,11 +5059,11 @@ "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", "dev": true, "requires": { - "graceful-fs": "^4.1.2", - "parse-json": "^2.2.0", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0", - "strip-bom": "^2.0.0" + "graceful-fs": "4.1.11", + "parse-json": "2.2.0", + "pify": "2.3.0", + "pinkie-promise": "2.0.1", + "strip-bom": "2.0.0" } }, "locate-path": { @@ -5118,8 +5072,8 @@ "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", "dev": true, "requires": { - "p-locate": "^2.0.0", - "path-exists": "^3.0.0" + "p-locate": "2.0.0", + "path-exists": "3.0.0" }, "dependencies": { "path-exists": { @@ -5140,8 +5094,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz", "integrity": "sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc=", - "dev": true, - "optional": true + "dev": true }, "loose-envify": { "version": "1.3.1", @@ -5149,7 +5102,7 @@ "integrity": "sha1-0aitM/qc4OcT1l/dCsi3SNR4yEg=", "dev": true, "requires": { - "js-tokens": "^3.0.0" + "js-tokens": "3.0.2" } }, "lru-cache": { @@ -5158,8 +5111,8 @@ "integrity": "sha512-q4spe4KTfsAS1SUHLO0wz8Qiyf1+vMIAgpRYioFYDMNqKfHQbg+AVDH3i4fvpl71/P1L0dBl+fQi+P37UYf0ew==", "dev": true, "requires": { - "pseudomap": "^1.0.2", - "yallist": "^2.1.2" + "pseudomap": "1.0.2", + "yallist": "2.1.2" } }, "md5-hex": { @@ -5168,7 +5121,7 @@ "integrity": "sha1-0sSv6YPENwZiF5uMrRRSGRNQRsQ=", "dev": true, "requires": { - "md5-o-matic": "^0.1.1" + "md5-o-matic": "0.1.1" } }, "md5-o-matic": { @@ -5183,7 +5136,7 @@ "integrity": "sha1-Xt1StIXKHZAP5kiVUFOZoN+kX3Y=", "dev": true, "requires": { - "mimic-fn": "^1.0.0" + "mimic-fn": "1.1.0" } }, "merge-source-map": { @@ -5192,7 +5145,7 @@ "integrity": "sha1-pd5GU42uhNQRTMXqArR3KmNGcB8=", "dev": true, "requires": { - "source-map": "^0.5.6" + "source-map": "0.5.7" } }, "micromatch": { @@ -5201,19 +5154,19 @@ "integrity": "sha1-hmd8l9FyCzY0MdBNDRUpO9OMFWU=", "dev": true, "requires": { - "arr-diff": "^2.0.0", - "array-unique": "^0.2.1", - "braces": "^1.8.2", - "expand-brackets": "^0.1.4", - "extglob": "^0.3.1", - "filename-regex": "^2.0.0", - "is-extglob": "^1.0.0", - "is-glob": "^2.0.1", - "kind-of": "^3.0.2", - "normalize-path": "^2.0.1", - "object.omit": "^2.0.0", - "parse-glob": "^3.0.4", - "regex-cache": "^0.4.2" + "arr-diff": "2.0.0", + "array-unique": "0.2.1", + "braces": "1.8.5", + "expand-brackets": "0.1.5", + "extglob": "0.3.2", + "filename-regex": "2.0.1", + "is-extglob": "1.0.0", + "is-glob": "2.0.1", + "kind-of": "3.2.2", + "normalize-path": "2.1.1", + "object.omit": "2.0.1", + "parse-glob": "3.0.4", + "regex-cache": "0.4.4" } }, "mimic-fn": { @@ -5228,7 +5181,7 @@ "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", "dev": true, "requires": { - "brace-expansion": "^1.1.7" + "brace-expansion": "1.1.8" } }, "minimist": { @@ -5258,10 +5211,10 @@ "integrity": "sha512-9jjUFbTPfEy3R/ad/2oNbKtW9Hgovl5O1FvFWKkKblNXoN/Oou6+9+KKohPK13Yc3/TyunyWhJp6gvRNR/PPAw==", "dev": true, "requires": { - "hosted-git-info": "^2.1.4", - "is-builtin-module": "^1.0.0", - "semver": "2 || 3 || 4 || 5", - "validate-npm-package-license": "^3.0.1" + "hosted-git-info": "2.5.0", + "is-builtin-module": "1.0.0", + "semver": "5.4.1", + "validate-npm-package-license": "3.0.1" } }, "normalize-path": { @@ -5270,7 +5223,7 @@ "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", "dev": true, "requires": { - "remove-trailing-separator": "^1.0.1" + "remove-trailing-separator": "1.1.0" } }, "npm-run-path": { @@ -5279,7 +5232,7 @@ "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", "dev": true, "requires": { - "path-key": "^2.0.0" + "path-key": "2.0.1" } }, "number-is-nan": { @@ -5300,8 +5253,8 @@ "integrity": "sha1-Gpx0SCnznbuFjHbKNXmuKlTr0fo=", "dev": true, "requires": { - "for-own": "^0.1.4", - "is-extendable": "^0.1.1" + "for-own": "0.1.5", + "is-extendable": "0.1.1" } }, "once": { @@ -5310,7 +5263,7 @@ "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", "dev": true, "requires": { - "wrappy": "1" + "wrappy": "1.0.2" } }, "optimist": { @@ -5319,8 +5272,8 @@ "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=", "dev": true, "requires": { - "minimist": "~0.0.1", - "wordwrap": "~0.0.2" + "minimist": "0.0.8", + "wordwrap": "0.0.3" } }, "os-homedir": { @@ -5335,9 +5288,9 @@ "integrity": "sha512-3sslG3zJbEYcaC4YVAvDorjGxc7tv6KVATnLPZONiljsUncvihe9BQoVCEs0RZ1kmf4Hk9OBqlZfJZWI4GanKA==", "dev": true, "requires": { - "execa": "^0.7.0", - "lcid": "^1.0.0", - "mem": "^1.1.0" + "execa": "0.7.0", + "lcid": "1.0.0", + "mem": "1.1.0" } }, "p-finally": { @@ -5358,7 +5311,7 @@ "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", "dev": true, "requires": { - "p-limit": "^1.1.0" + "p-limit": "1.1.0" } }, "parse-glob": { @@ -5367,10 +5320,10 @@ "integrity": "sha1-ssN2z7EfNVE7rdFz7wu246OIORw=", "dev": true, "requires": { - "glob-base": "^0.3.0", - "is-dotfile": "^1.0.0", - "is-extglob": "^1.0.0", - "is-glob": "^2.0.0" + "glob-base": "0.3.0", + "is-dotfile": "1.0.3", + "is-extglob": "1.0.0", + "is-glob": "2.0.1" } }, "parse-json": { @@ -5379,7 +5332,7 @@ "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", "dev": true, "requires": { - "error-ex": "^1.2.0" + "error-ex": "1.3.1" } }, "path-exists": { @@ -5388,7 +5341,7 @@ "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", "dev": true, "requires": { - "pinkie-promise": "^2.0.0" + "pinkie-promise": "2.0.1" } }, "path-is-absolute": { @@ -5415,9 +5368,9 @@ "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", "dev": true, "requires": { - "graceful-fs": "^4.1.2", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0" + "graceful-fs": "4.1.11", + "pify": "2.3.0", + "pinkie-promise": "2.0.1" } }, "pify": { @@ -5438,7 +5391,7 @@ "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", "dev": true, "requires": { - "pinkie": "^2.0.0" + "pinkie": "2.0.4" } }, "pkg-dir": { @@ -5447,7 +5400,7 @@ "integrity": "sha1-ektQio1bstYp1EcFb/TpyTFM89Q=", "dev": true, "requires": { - "find-up": "^1.0.0" + "find-up": "1.1.2" }, "dependencies": { "find-up": { @@ -5456,8 +5409,8 @@ "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", "dev": true, "requires": { - "path-exists": "^2.0.0", - "pinkie-promise": "^2.0.0" + "path-exists": "2.1.0", + "pinkie-promise": "2.0.1" } } } @@ -5480,8 +5433,8 @@ "integrity": "sha512-D5JUjPyJbaJDkuAazpVnSfVkLlpeO3wDlPROTMLGKG1zMFNFRgrciKo1ltz/AzNTkqE0HzDx655QOL51N06how==", "dev": true, "requires": { - "is-number": "^3.0.0", - "kind-of": "^4.0.0" + "is-number": "3.0.0", + "kind-of": "4.0.0" }, "dependencies": { "is-number": { @@ -5490,7 +5443,7 @@ "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", "dev": true, "requires": { - "kind-of": "^3.0.2" + "kind-of": "3.2.2" }, "dependencies": { "kind-of": { @@ -5499,7 +5452,7 @@ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, "requires": { - "is-buffer": "^1.1.5" + "is-buffer": "1.1.5" } } } @@ -5510,7 +5463,7 @@ "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", "dev": true, "requires": { - "is-buffer": "^1.1.5" + "is-buffer": "1.1.5" } } } @@ -5521,9 +5474,9 @@ "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", "dev": true, "requires": { - "load-json-file": "^1.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^1.0.0" + "load-json-file": "1.1.0", + "normalize-package-data": "2.4.0", + "path-type": "1.1.0" } }, "read-pkg-up": { @@ -5532,8 +5485,8 @@ "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=", "dev": true, "requires": { - "find-up": "^1.0.0", - "read-pkg": "^1.0.0" + "find-up": "1.1.2", + "read-pkg": "1.1.0" }, "dependencies": { "find-up": { @@ -5542,8 +5495,8 @@ "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", "dev": true, "requires": { - "path-exists": "^2.0.0", - "pinkie-promise": "^2.0.0" + "path-exists": "2.1.0", + "pinkie-promise": "2.0.1" } } } @@ -5560,7 +5513,7 @@ "integrity": "sha512-nVIZwtCjkC9YgvWkpM55B5rBhBYRZhAaJbgcFYXXsHnbZ9UZI9nnVWYZpBlCqv9ho2eZryPnWrZGsOdPwVWXWQ==", "dev": true, "requires": { - "is-equal-shallow": "^0.1.3" + "is-equal-shallow": "0.1.3" } }, "remove-trailing-separator": { @@ -5587,7 +5540,7 @@ "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=", "dev": true, "requires": { - "is-finite": "^1.0.0" + "is-finite": "1.0.2" } }, "require-directory": { @@ -5615,7 +5568,7 @@ "dev": true, "optional": true, "requires": { - "align-text": "^0.1.1" + "align-text": "0.1.4" } }, "rimraf": { @@ -5624,7 +5577,7 @@ "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==", "dev": true, "requires": { - "glob": "^7.0.5" + "glob": "7.1.2" } }, "semver": { @@ -5645,7 +5598,7 @@ "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", "dev": true, "requires": { - "shebang-regex": "^1.0.0" + "shebang-regex": "1.0.0" } }, "shebang-regex": { @@ -5678,12 +5631,12 @@ "integrity": "sha512-Yfkd7Yiwz4RcBPrDWzvhnTzQINBHNqOEhUzOdWZ67Y9b4wzs3Gz6ymuptQmRBpzlpOzroM7jwzmBdRec7JJ0UA==", "dev": true, "requires": { - "foreground-child": "^1.5.6", - "mkdirp": "^0.5.0", - "os-homedir": "^1.0.1", - "rimraf": "^2.3.3", - "signal-exit": "^3.0.2", - "which": "^1.2.4" + "foreground-child": "1.5.6", + "mkdirp": "0.5.1", + "os-homedir": "1.0.2", + "rimraf": "2.6.2", + "signal-exit": "3.0.2", + "which": "1.3.0" } }, "spdx-correct": { @@ -5692,7 +5645,7 @@ "integrity": "sha1-SzBz2TP/UfORLwOsVRlJikFQ20A=", "dev": true, "requires": { - "spdx-license-ids": "^1.0.2" + "spdx-license-ids": "1.2.2" } }, "spdx-expression-parse": { @@ -5713,8 +5666,8 @@ "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", "dev": true, "requires": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" + "is-fullwidth-code-point": "2.0.0", + "strip-ansi": "4.0.0" }, "dependencies": { "ansi-regex": { @@ -5735,7 +5688,7 @@ "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", "dev": true, "requires": { - "ansi-regex": "^3.0.0" + "ansi-regex": "3.0.0" } } } @@ -5746,7 +5699,7 @@ "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "dev": true, "requires": { - "ansi-regex": "^2.0.0" + "ansi-regex": "2.1.1" } }, "strip-bom": { @@ -5755,7 +5708,7 @@ "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", "dev": true, "requires": { - "is-utf8": "^0.2.0" + "is-utf8": "0.2.1" } }, "strip-eof": { @@ -5776,11 +5729,11 @@ "integrity": "sha512-35+Asrsk3XHJDBgf/VRFexPgh3UyETv8IAn/LRTiZjVy6rjPVqdEk8dJcJYBzl1w0XCJM48lvTy8SfEsCWS4nA==", "dev": true, "requires": { - "arrify": "^1.0.1", - "micromatch": "^2.3.11", - "object-assign": "^4.1.0", - "read-pkg-up": "^1.0.1", - "require-main-filename": "^1.0.1" + "arrify": "1.0.1", + "micromatch": "2.3.11", + "object-assign": "4.1.1", + "read-pkg-up": "1.0.1", + "require-main-filename": "1.0.1" } }, "to-fast-properties": { @@ -5802,9 +5755,9 @@ "dev": true, "optional": true, "requires": { - "source-map": "~0.5.1", - "uglify-to-browserify": "~1.0.0", - "yargs": "~3.10.0" + "source-map": "0.5.7", + "uglify-to-browserify": "1.0.2", + "yargs": "3.10.0" }, "dependencies": { "yargs": { @@ -5814,9 +5767,9 @@ "dev": true, "optional": true, "requires": { - "camelcase": "^1.0.2", - "cliui": "^2.1.0", - "decamelize": "^1.0.0", + "camelcase": "1.2.1", + "cliui": "2.1.0", + "decamelize": "1.2.0", "window-size": "0.1.0" } } @@ -5835,8 +5788,8 @@ "integrity": "sha1-KAS6vnEq0zeUWaz74kdGqywwP7w=", "dev": true, "requires": { - "spdx-correct": "~1.0.0", - "spdx-expression-parse": "~1.0.0" + "spdx-correct": "1.0.2", + "spdx-expression-parse": "1.0.4" } }, "which": { @@ -5845,7 +5798,7 @@ "integrity": "sha512-xcJpopdamTuY5duC/KnTTNBraPK54YwpenP4lzxU8H91GudWpFv38u0CKjclE1Wi2EH2EDz5LRcHcKbCIzqGyg==", "dev": true, "requires": { - "isexe": "^2.0.0" + "isexe": "2.0.0" } }, "which-module": { @@ -5873,8 +5826,8 @@ "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", "dev": true, "requires": { - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1" + "string-width": "1.0.2", + "strip-ansi": "3.0.1" }, "dependencies": { "string-width": { @@ -5883,9 +5836,9 @@ "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", "dev": true, "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" + "code-point-at": "1.1.0", + "is-fullwidth-code-point": "1.0.0", + "strip-ansi": "3.0.1" } } } @@ -5902,9 +5855,9 @@ "integrity": "sha1-+Aek8LHZ6ROuekgRLmzDrxmRtF8=", "dev": true, "requires": { - "graceful-fs": "^4.1.11", - "imurmurhash": "^0.1.4", - "slide": "^1.1.5" + "graceful-fs": "4.1.11", + "imurmurhash": "0.1.4", + "slide": "1.1.6" } }, "y18n": { @@ -5925,18 +5878,18 @@ "integrity": "sha512-DqBpQ8NAUX4GyPP/ijDGHsJya4tYqLQrjPr95HNsr1YwL3+daCfvBwg7+gIC6IdJhR2kATh3hb61vjzMWEtjdw==", "dev": true, "requires": { - "cliui": "^3.2.0", - "decamelize": "^1.1.1", - "find-up": "^2.1.0", - "get-caller-file": "^1.0.1", - "os-locale": "^2.0.0", - "require-directory": "^2.1.1", - "require-main-filename": "^1.0.1", - "set-blocking": "^2.0.0", - "string-width": "^2.0.0", - "which-module": "^2.0.0", - "y18n": "^3.2.1", - "yargs-parser": "^8.0.0" + "cliui": "3.2.0", + "decamelize": "1.2.0", + "find-up": "2.1.0", + "get-caller-file": "1.0.2", + "os-locale": "2.1.0", + "require-directory": "2.1.1", + "require-main-filename": "1.0.1", + "set-blocking": "2.0.0", + "string-width": "2.1.1", + "which-module": "2.0.0", + "y18n": "3.2.1", + "yargs-parser": "8.0.0" }, "dependencies": { "cliui": { @@ -5945,9 +5898,9 @@ "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=", "dev": true, "requires": { - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1", - "wrap-ansi": "^2.0.0" + "string-width": "1.0.2", + "strip-ansi": "3.0.1", + "wrap-ansi": "2.1.0" }, "dependencies": { "string-width": { @@ -5956,9 +5909,9 @@ "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", "dev": true, "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" + "code-point-at": "1.1.0", + "is-fullwidth-code-point": "1.0.0", + "strip-ansi": "3.0.1" } } } @@ -5971,7 +5924,7 @@ "integrity": "sha1-IdR2Mw5agieaS4gTRb8GYQLiGcY=", "dev": true, "requires": { - "camelcase": "^4.1.0" + "camelcase": "4.1.0" }, "dependencies": { "camelcase": { @@ -6007,9 +5960,9 @@ "integrity": "sha1-scnMBE7xuf5jYG/BQau7MuFHMMw=", "dev": true, "requires": { - "define-properties": "^1.1.2", - "function-bind": "^1.1.0", - "object-keys": "^1.0.10" + "define-properties": "1.1.2", + "function-bind": "1.1.1", + "object-keys": "1.0.11" } }, "object.omit": { @@ -6018,8 +5971,8 @@ "integrity": "sha1-Gpx0SCnznbuFjHbKNXmuKlTr0fo=", "dev": true, "requires": { - "for-own": "^0.1.4", - "is-extendable": "^0.1.1" + "for-own": "0.1.5", + "is-extendable": "0.1.1" } }, "on-finished": { @@ -6040,7 +5993,7 @@ "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", "requires": { - "wrappy": "1" + "wrappy": "1.0.2" } }, "onetime": { @@ -6055,8 +6008,8 @@ "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=", "dev": true, "requires": { - "minimist": "~0.0.1", - "wordwrap": "~0.0.2" + "minimist": "0.0.8", + "wordwrap": "0.0.3" }, "dependencies": { "wordwrap": { @@ -6073,12 +6026,12 @@ "integrity": "sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=", "dev": true, "requires": { - "deep-is": "~0.1.3", - "fast-levenshtein": "~2.0.4", - "levn": "~0.3.0", - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2", - "wordwrap": "~1.0.0" + "deep-is": "0.1.3", + "fast-levenshtein": "2.0.6", + "levn": "0.3.0", + "prelude-ls": "1.1.2", + "type-check": "0.3.2", + "wordwrap": "1.0.0" } }, "os-homedir": { @@ -6099,10 +6052,18 @@ "integrity": "sha1-iGmgQBJTZhxMTKPabCEh7VVfXu0=", "dev": true, "requires": { - "got": "^6.7.1", - "registry-auth-token": "^3.0.1", - "registry-url": "^3.0.3", - "semver": "^5.1.0" + "got": "6.7.1", + "registry-auth-token": "3.3.1", + "registry-url": "3.1.0", + "semver": "5.7.1" + }, + "dependencies": { + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + } } }, "parse-glob": { @@ -6111,10 +6072,10 @@ "integrity": "sha1-ssN2z7EfNVE7rdFz7wu246OIORw=", "dev": true, "requires": { - "glob-base": "^0.3.0", - "is-dotfile": "^1.0.0", - "is-extglob": "^1.0.0", - "is-glob": "^2.0.0" + "glob-base": "0.3.0", + "is-dotfile": "1.0.3", + "is-extglob": "1.0.0", + "is-glob": "2.0.1" } }, "parseurl": { @@ -6128,7 +6089,7 @@ "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", "dev": true, "requires": { - "pinkie-promise": "^2.0.0" + "pinkie-promise": "2.0.1" } }, "path-is-absolute": { @@ -6166,7 +6127,7 @@ "integrity": "sha1-/lo0sMvOErWqaitAPuLnO2AvFEU=", "dev": true, "requires": { - "through": "~2.3" + "through": "2.3.8" } }, "performance-now": { @@ -6192,7 +6153,7 @@ "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", "dev": true, "requires": { - "pinkie": "^2.0.0" + "pinkie": "2.0.4" } }, "pkg-dir": { @@ -6201,7 +6162,7 @@ "integrity": "sha1-ektQio1bstYp1EcFb/TpyTFM89Q=", "dev": true, "requires": { - "find-up": "^1.0.0" + "find-up": "1.1.2" } }, "pkg-up": { @@ -6210,7 +6171,7 @@ "integrity": "sha1-Pgj7RhUlxEIWJKM7n35tCvWwWiY=", "dev": true, "requires": { - "find-up": "^1.0.0" + "find-up": "1.1.2" } }, "pkginfo": { @@ -6259,7 +6220,7 @@ "resolved": "https://registry.npmjs.org/prom-client/-/prom-client-11.5.3.tgz", "integrity": "sha512-iz22FmTbtkyL2vt0MdDFY+kWof+S9UB/NACxSn2aJcewtw+EERsen0urSkZ2WrHseNdydsvcxCTAnPcSMZZv4Q==", "requires": { - "tdigest": "^0.1.1" + "tdigest": "0.1.1" } }, "proxy-addr": { @@ -6267,7 +6228,7 @@ "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-1.1.2.tgz", "integrity": "sha1-tMxfImENlTWCTBI675089zxAujc=", "requires": { - "forwarded": "~0.1.0", + "forwarded": "0.1.0", "ipaddr.js": "1.1.1" } }, @@ -6277,7 +6238,7 @@ "integrity": "sha1-tCGyQUDWID8e08dplrRCewjowBQ=", "dev": true, "requires": { - "event-stream": "~3.3.0" + "event-stream": "3.3.4" } }, "pseudomap": { @@ -6302,8 +6263,8 @@ "integrity": "sha512-D5JUjPyJbaJDkuAazpVnSfVkLlpeO3wDlPROTMLGKG1zMFNFRgrciKo1ltz/AzNTkqE0HzDx655QOL51N06how==", "dev": true, "requires": { - "is-number": "^3.0.0", - "kind-of": "^4.0.0" + "is-number": "3.0.0", + "kind-of": "4.0.0" }, "dependencies": { "is-number": { @@ -6312,7 +6273,7 @@ "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", "dev": true, "requires": { - "kind-of": "^3.0.2" + "kind-of": "3.2.2" }, "dependencies": { "kind-of": { @@ -6321,7 +6282,7 @@ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, "requires": { - "is-buffer": "^1.1.5" + "is-buffer": "1.1.6" } } } @@ -6332,7 +6293,7 @@ "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", "dev": true, "requires": { - "is-buffer": "^1.1.5" + "is-buffer": "1.1.6" } } } @@ -6377,10 +6338,10 @@ "integrity": "sha1-2M6ctX6NZNnHut2YdsfDTL48cHc=", "dev": true, "requires": { - "deep-extend": "~0.4.0", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" + "deep-extend": "0.4.2", + "ini": "1.3.5", + "minimist": "1.2.0", + "strip-json-comments": "2.0.1" }, "dependencies": { "minimist": { @@ -6397,13 +6358,13 @@ "integrity": "sha512-m+qzzcn7KUxEmd1gMbchF+Y2eIUbieUaxkWtptyHywrX0rE8QEYqPC07Vuy4Wm32/xE16NcdBctb8S0Xe/5IeQ==", "dev": true, "requires": { - "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" + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "1.0.7", + "safe-buffer": "5.1.1", + "string_decoder": "1.0.3", + "util-deprecate": "1.0.2" }, "dependencies": { "inherits": { @@ -6420,10 +6381,10 @@ "integrity": "sha1-TtCtBg3zBzMAxIRANz9y0cxkLXg=", "dev": true, "requires": { - "graceful-fs": "^4.1.2", - "minimatch": "^3.0.2", - "readable-stream": "^2.0.2", - "set-immediate-shim": "^1.0.1" + "graceful-fs": "4.1.11", + "minimatch": "3.0.4", + "readable-stream": "2.3.3", + "set-immediate-shim": "1.0.1" } }, "readline2": { @@ -6432,8 +6393,8 @@ "integrity": "sha1-QQWWCP/BVHV7cV2ZidGZ/783LjU=", "dev": true, "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", + "code-point-at": "1.1.0", + "is-fullwidth-code-point": "1.0.0", "mute-stream": "0.0.5" } }, @@ -6443,7 +6404,7 @@ "integrity": "sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q=", "dev": true, "requires": { - "resolve": "^1.1.6" + "resolve": "1.5.0" } }, "redis-commands": { @@ -6462,7 +6423,7 @@ "integrity": "sha512-nVIZwtCjkC9YgvWkpM55B5rBhBYRZhAaJbgcFYXXsHnbZ9UZI9nnVWYZpBlCqv9ho2eZryPnWrZGsOdPwVWXWQ==", "dev": true, "requires": { - "is-equal-shallow": "^0.1.3" + "is-equal-shallow": "0.1.3" } }, "registry-auth-token": { @@ -6471,8 +6432,8 @@ "integrity": "sha1-+w0yie4Nmtosu1KvXf5mywcNMAY=", "dev": true, "requires": { - "rc": "^1.1.6", - "safe-buffer": "^5.0.1" + "rc": "1.2.2", + "safe-buffer": "5.1.1" } }, "registry-url": { @@ -6481,7 +6442,7 @@ "integrity": "sha1-PU74cPc93h138M+aOBQyRE4XSUI=", "dev": true, "requires": { - "rc": "^1.0.1" + "rc": "1.2.2" } }, "remove-trailing-separator": { @@ -6507,28 +6468,28 @@ "resolved": "https://registry.npmjs.org/request/-/request-2.81.0.tgz", "integrity": "sha1-xpKJRqDgbF+Nb4qTM0af/aRimKA=", "requires": { - "aws-sign2": "~0.6.0", - "aws4": "^1.2.1", - "caseless": "~0.12.0", - "combined-stream": "~1.0.5", - "extend": "~3.0.0", - "forever-agent": "~0.6.1", - "form-data": "~2.1.1", - "har-validator": "~4.2.1", - "hawk": "~3.1.3", - "http-signature": "~1.1.0", - "is-typedarray": "~1.0.0", - "isstream": "~0.1.2", - "json-stringify-safe": "~5.0.1", - "mime-types": "~2.1.7", - "oauth-sign": "~0.8.1", - "performance-now": "^0.2.0", - "qs": "~6.4.0", - "safe-buffer": "^5.0.1", - "stringstream": "~0.0.4", - "tough-cookie": "~2.3.0", - "tunnel-agent": "^0.6.0", - "uuid": "^3.0.0" + "aws-sign2": "0.6.0", + "aws4": "1.5.0", + "caseless": "0.12.0", + "combined-stream": "1.0.5", + "extend": "3.0.0", + "forever-agent": "0.6.1", + "form-data": "2.1.1", + "har-validator": "4.2.1", + "hawk": "3.1.3", + "http-signature": "1.1.1", + "is-typedarray": "1.0.0", + "isstream": "0.1.2", + "json-stringify-safe": "5.0.1", + "mime-types": "2.1.12", + "oauth-sign": "0.8.2", + "performance-now": "0.2.0", + "qs": "6.4.0", + "safe-buffer": "5.1.1", + "stringstream": "0.0.5", + "tough-cookie": "2.3.2", + "tunnel-agent": "0.6.0", + "uuid": "3.0.1" }, "dependencies": { "caseless": { @@ -6541,8 +6502,8 @@ "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-4.2.1.tgz", "integrity": "sha1-M0gdDxu/9gDdID11gSpqX7oALio=", "requires": { - "ajv": "^4.9.1", - "har-schema": "^1.0.5" + "ajv": "4.11.8", + "har-schema": "1.0.5" } }, "qs": { @@ -6555,7 +6516,7 @@ "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", "requires": { - "safe-buffer": "^5.0.1" + "safe-buffer": "5.1.1" } } } @@ -6566,8 +6527,8 @@ "integrity": "sha1-Tg1W1slmL9MeQwEcS5WqSZVUIdM=", "dev": true, "requires": { - "caller-path": "^0.1.0", - "resolve-from": "^1.0.0" + "caller-path": "0.1.0", + "resolve-from": "1.0.1" } }, "requires-port": { @@ -6582,7 +6543,7 @@ "integrity": "sha512-hgoSGrc3pjzAPHNBg+KnFcK2HwlHTs/YrAGUr6qgTVUZmXv1UEXXl0bZNBKMA9fud6lRYFdPGz0xXxycPzmmiw==", "dev": true, "requires": { - "path-parse": "^1.0.5" + "path-parse": "1.0.5" } }, "resolve-from": { @@ -6597,8 +6558,8 @@ "integrity": "sha1-NGYfRohjJ/7SmRR5FSJS35LapUE=", "dev": true, "requires": { - "exit-hook": "^1.0.0", - "onetime": "^1.0.0" + "exit-hook": "1.1.1", + "onetime": "1.1.0" } }, "right-align": { @@ -6608,7 +6569,7 @@ "dev": true, "optional": true, "requires": { - "align-text": "^0.1.1" + "align-text": "0.1.4" } }, "rimraf": { @@ -6617,7 +6578,7 @@ "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==", "dev": true, "requires": { - "glob": "^7.0.5" + "glob": "7.1.2" } }, "run-async": { @@ -6626,7 +6587,7 @@ "integrity": "sha1-yK1KXhEGYeQCp9IbUw4AnyX444k=", "dev": true, "requires": { - "once": "^1.3.0" + "once": "1.4.0" } }, "rx-lite": { @@ -6647,10 +6608,9 @@ "dev": true }, "semver": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.4.1.tgz", - "integrity": "sha512-WfG/X9+oATh81XtllIo/I8gOiY9EXRdv1cQdyykeXK17YcUW3EXUAi2To4pcH6nZtJPr7ZOpM5OMyWJZm+8Rsg==", - "dev": true + "version": "7.3.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz", + "integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==" }, "semver-diff": { "version": "2.1.0", @@ -6658,7 +6618,15 @@ "integrity": "sha1-S7uEN8jTfksM8aaP1ybsbWRdbTY=", "dev": true, "requires": { - "semver": "^5.0.3" + "semver": "5.7.1" + }, + "dependencies": { + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + } } }, "send": { @@ -6666,19 +6634,19 @@ "resolved": "https://registry.npmjs.org/send/-/send-0.14.1.tgz", "integrity": "sha1-qVSYQyU5L1FTKndgdg5FlZjIn3o=", "requires": { - "debug": "~2.2.0", - "depd": "~1.1.0", - "destroy": "~1.0.4", - "encodeurl": "~1.0.1", - "escape-html": "~1.0.3", - "etag": "~1.7.0", + "debug": "2.2.0", + "depd": "1.1.0", + "destroy": "1.0.4", + "encodeurl": "1.0.1", + "escape-html": "1.0.3", + "etag": "1.7.0", "fresh": "0.3.0", - "http-errors": "~1.5.0", + "http-errors": "1.5.0", "mime": "1.3.4", "ms": "0.7.1", - "on-finished": "~2.3.0", - "range-parser": "~1.2.0", - "statuses": "~1.3.0" + "on-finished": "2.3.0", + "range-parser": "1.2.0", + "statuses": "1.3.0" } }, "serve-static": { @@ -6686,9 +6654,9 @@ "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.11.1.tgz", "integrity": "sha1-1sznaTUF9zPHWd5Xvvwa92wPCAU=", "requires": { - "encodeurl": "~1.0.1", - "escape-html": "~1.0.3", - "parseurl": "~1.3.1", + "encodeurl": "1.0.1", + "escape-html": "1.0.3", + "parseurl": "1.3.1", "send": "0.14.1" } }, @@ -6709,7 +6677,7 @@ "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", "dev": true, "requires": { - "shebang-regex": "^1.0.0" + "shebang-regex": "1.0.0" } }, "shebang-regex": { @@ -6724,10 +6692,10 @@ "integrity": "sha1-lSxE4LHtkBPvU5WBecxkPod3Rms=", "dev": true, "requires": { - "array-filter": "~0.0.0", - "array-map": "~0.0.0", - "array-reduce": "~0.0.0", - "jsonify": "~0.0.0" + "array-filter": "0.0.1", + "array-map": "0.0.0", + "array-reduce": "0.0.0", + "jsonify": "0.0.0" } }, "shelljs": { @@ -6736,9 +6704,9 @@ "integrity": "sha1-3svPh0sNHl+3LhSxZKloMEjprLM=", "dev": true, "requires": { - "glob": "^7.0.0", - "interpret": "^1.0.0", - "rechoir": "^0.6.2" + "glob": "7.1.2", + "interpret": "1.0.4", + "rechoir": "0.6.2" } }, "signal-exit": { @@ -6753,13 +6721,13 @@ "integrity": "sha512-c7u0ZuvBRX1eXuB4jN3BRCAOGiUTlM8SE3TxbJHrNiHUKL7wonujMOB6Fi1gQc00U91IscFORQHDga/eccqpbw==", "dev": true, "requires": { - "diff": "^3.1.0", + "diff": "3.2.0", "formatio": "1.2.0", - "lodash.get": "^4.4.2", - "lolex": "^2.2.0", - "nise": "^1.2.0", - "supports-color": "^4.4.0", - "type-detect": "^4.0.5" + "lodash.get": "4.4.2", + "lolex": "2.3.1", + "nise": "1.2.0", + "supports-color": "4.5.0", + "type-detect": "4.0.5" }, "dependencies": { "has-flag": { @@ -6774,7 +6742,7 @@ "integrity": "sha1-vnoN5ITexcXN34s9WRJQRJEvY1s=", "dev": true, "requires": { - "has-flag": "^2.0.0" + "has-flag": "2.0.0" } }, "type-detect": { @@ -6796,7 +6764,7 @@ "resolved": "https://registry.npmjs.org/sntp/-/sntp-1.0.9.tgz", "integrity": "sha1-ZUEYTMkK7qbG57NeJlkIJEPGYZg=", "requires": { - "hoek": "2.x.x" + "hoek": "2.16.3" } }, "source-map": { @@ -6806,7 +6774,7 @@ "dev": true, "optional": true, "requires": { - "amdefine": ">=0.0.4" + "amdefine": "1.0.1" } }, "split": { @@ -6815,7 +6783,7 @@ "integrity": "sha1-zQ7qXmOiEd//frDwkcQTPi0N0o8=", "dev": true, "requires": { - "through": "2" + "through": "2.3.8" } }, "sprintf-js": { @@ -6829,15 +6797,15 @@ "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.10.1.tgz", "integrity": "sha1-MOGl0ykkSXShr2FREznVla9mOLA=", "requires": { - "asn1": "~0.2.3", - "assert-plus": "^1.0.0", - "bcrypt-pbkdf": "^1.0.0", - "dashdash": "^1.12.0", - "ecc-jsbn": "~0.1.1", - "getpass": "^0.1.1", - "jodid25519": "^1.0.0", - "jsbn": "~0.1.0", - "tweetnacl": "~0.14.0" + "asn1": "0.2.3", + "assert-plus": "1.0.0", + "bcrypt-pbkdf": "1.0.0", + "dashdash": "1.14.0", + "ecc-jsbn": "0.1.1", + "getpass": "0.1.6", + "jodid25519": "1.0.2", + "jsbn": "0.1.0", + "tweetnacl": "0.14.3" }, "dependencies": { "assert-plus": { @@ -6863,7 +6831,7 @@ "integrity": "sha1-TV5DPBhSYd3mI8o/RMWGvPXErRQ=", "dev": true, "requires": { - "duplexer": "~0.1.1" + "duplexer": "0.1.1" } }, "string-width": { @@ -6872,9 +6840,9 @@ "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", "dev": true, "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" + "code-point-at": "1.1.0", + "is-fullwidth-code-point": "1.0.0", + "strip-ansi": "3.0.1" } }, "string_decoder": { @@ -6883,7 +6851,7 @@ "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==", "dev": true, "requires": { - "safe-buffer": "~5.1.0" + "safe-buffer": "5.1.1" } }, "stringstream": { @@ -6897,7 +6865,7 @@ "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "dev": true, "requires": { - "ansi-regex": "^2.0.0" + "ansi-regex": "2.1.1" } }, "strip-bom": { @@ -6924,7 +6892,7 @@ "integrity": "sha1-HGYfZnBX9jvLeHWqFDi8FiUlFW4=", "dev": true, "requires": { - "chalk": "^1.0.0" + "chalk": "1.1.3" } }, "superagent": { @@ -6933,16 +6901,16 @@ "integrity": "sha1-cDUpoHFOV+EjlZ3e+84ZOy5Q0RU=", "dev": true, "requires": { - "component-emitter": "^1.2.0", - "cookiejar": "^2.0.6", - "debug": "^2.2.0", - "extend": "^3.0.0", + "component-emitter": "1.2.1", + "cookiejar": "2.1.1", + "debug": "2.2.0", + "extend": "3.0.0", "form-data": "1.0.0-rc4", - "formidable": "^1.0.17", - "methods": "^1.1.1", - "mime": "^1.3.4", - "qs": "^6.1.0", - "readable-stream": "^2.0.5" + "formidable": "1.1.1", + "methods": "1.1.2", + "mime": "1.3.4", + "qs": "6.2.0", + "readable-stream": "2.3.3" }, "dependencies": { "async": { @@ -6957,9 +6925,9 @@ "integrity": "sha1-BaxrwiIntD5EYfSIFhVUaZ1Pi14=", "dev": true, "requires": { - "async": "^1.5.2", - "combined-stream": "^1.0.5", - "mime-types": "^2.1.10" + "async": "1.5.2", + "combined-stream": "1.0.5", + "mime-types": "2.1.12" } } } @@ -6970,8 +6938,8 @@ "integrity": "sha1-oFgIHXiPFRXUcA11Aogea3WeRM0=", "dev": true, "requires": { - "methods": "1.x", - "superagent": "^2.0.0" + "methods": "1.1.2", + "superagent": "2.3.0" } }, "supports-color": { @@ -6986,12 +6954,12 @@ "integrity": "sha1-K7xULw/amGGnVdOUf+/Ys/UThV8=", "dev": true, "requires": { - "ajv": "^4.7.0", - "ajv-keywords": "^1.0.0", - "chalk": "^1.1.1", - "lodash": "^4.0.0", + "ajv": "4.11.8", + "ajv-keywords": "1.5.1", + "chalk": "1.1.3", + "lodash": "4.16.4", "slice-ansi": "0.0.4", - "string-width": "^2.0.0" + "string-width": "2.1.1" }, "dependencies": { "ansi-regex": { @@ -7012,8 +6980,8 @@ "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", "dev": true, "requires": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" + "is-fullwidth-code-point": "2.0.0", + "strip-ansi": "4.0.0" } }, "strip-ansi": { @@ -7022,7 +6990,7 @@ "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", "dev": true, "requires": { - "ansi-regex": "^3.0.0" + "ansi-regex": "3.0.0" } } } @@ -7041,7 +7009,7 @@ "integrity": "sha1-RYuDiH8oj8Vtb/+/rSYuJmOO+mk=", "dev": true, "requires": { - "execa": "^0.7.0" + "execa": "0.7.0" } }, "text-encoding": { @@ -7073,7 +7041,7 @@ "integrity": "sha512-WBx8Uy5TLtOSRtIq+M03/sKDrXCLHxwDcquSP2c43Le03/9serjQBIztjRz6FkJez9D/hleyAXTBGLwwZUw9lA==", "dev": true, "requires": { - "nopt": "~1.0.10" + "nopt": "1.0.10" }, "dependencies": { "nopt": { @@ -7082,7 +7050,7 @@ "integrity": "sha1-bd0hvSoxQXuScn3Vhfim83YI6+4=", "dev": true, "requires": { - "abbrev": "1" + "abbrev": "1.0.9" } } } @@ -7092,7 +7060,7 @@ "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.2.tgz", "integrity": "sha1-8IH3bkyFcg5sN6X6ztc3FQ2EByo=", "requires": { - "punycode": "^1.4.1" + "punycode": "1.4.1" } }, "tryit": { @@ -7119,7 +7087,7 @@ "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", "dev": true, "requires": { - "prelude-ls": "~1.1.2" + "prelude-ls": "1.1.2" } }, "type-detect": { @@ -7134,7 +7102,7 @@ "integrity": "sha1-boO6e8MM0zp7sLf7AHN6IIW/nQg=", "requires": { "media-typer": "0.3.0", - "mime-types": "~2.1.11" + "mime-types": "2.1.12" } }, "typedarray": { @@ -7150,9 +7118,9 @@ "dev": true, "optional": true, "requires": { - "source-map": "~0.5.1", - "uglify-to-browserify": "~1.0.0", - "yargs": "~3.10.0" + "source-map": "0.5.7", + "uglify-to-browserify": "1.0.2", + "yargs": "3.10.0" }, "dependencies": { "source-map": { @@ -7183,7 +7151,7 @@ "integrity": "sha1-nhBXzKhRq7kzmPizOuGHuZyuwRo=", "dev": true, "requires": { - "crypto-random-string": "^1.0.0" + "crypto-random-string": "1.0.0" } }, "unpipe": { @@ -7203,15 +7171,15 @@ "integrity": "sha1-TognpruRUUCrCTVZ1wFOPruDdFE=", "dev": true, "requires": { - "boxen": "^1.2.1", - "chalk": "^2.0.1", - "configstore": "^3.0.0", - "import-lazy": "^2.1.0", - "is-installed-globally": "^0.1.0", - "is-npm": "^1.0.0", - "latest-version": "^3.0.0", - "semver-diff": "^2.0.0", - "xdg-basedir": "^3.0.0" + "boxen": "1.2.2", + "chalk": "2.3.0", + "configstore": "3.1.1", + "import-lazy": "2.1.0", + "is-installed-globally": "0.1.0", + "is-npm": "1.0.0", + "latest-version": "3.1.0", + "semver-diff": "2.1.0", + "xdg-basedir": "3.0.0" }, "dependencies": { "ansi-styles": { @@ -7220,7 +7188,7 @@ "integrity": "sha512-NnSOmMEYtVR2JVMIGTzynRkkaxtiq1xnFBcdQD/DnNCYPoEPsVJhM98BDyaoNOQIi7p4okdi3E27eN7GQbsUug==", "dev": true, "requires": { - "color-convert": "^1.9.0" + "color-convert": "1.9.1" } }, "chalk": { @@ -7229,9 +7197,9 @@ "integrity": "sha512-Az5zJR2CBujap2rqXGaJKaPHyJ0IrUimvYNX+ncCy8PJP4ltOGTrHUIo097ZaL2zMeKYpiCdqDvS6zdrTFok3Q==", "dev": true, "requires": { - "ansi-styles": "^3.1.0", - "escape-string-regexp": "^1.0.5", - "supports-color": "^4.0.0" + "ansi-styles": "3.2.0", + "escape-string-regexp": "1.0.5", + "supports-color": "4.5.0" } }, "has-flag": { @@ -7246,7 +7214,7 @@ "integrity": "sha1-vnoN5ITexcXN34s9WRJQRJEvY1s=", "dev": true, "requires": { - "has-flag": "^2.0.0" + "has-flag": "2.0.0" } } } @@ -7257,7 +7225,7 @@ "integrity": "sha1-evjzA2Rem9eaJy56FKxovAYJ2nM=", "dev": true, "requires": { - "prepend-http": "^1.0.1" + "prepend-http": "1.0.4" } }, "user-home": { @@ -7266,7 +7234,7 @@ "integrity": "sha1-nHC/2Babwdy/SGBODwS4tJzenp8=", "dev": true, "requires": { - "os-homedir": "^1.0.0" + "os-homedir": "1.0.2" } }, "util-deprecate": { @@ -7304,7 +7272,7 @@ "integrity": "sha512-xcJpopdamTuY5duC/KnTTNBraPK54YwpenP4lzxU8H91GudWpFv38u0CKjclE1Wi2EH2EDz5LRcHcKbCIzqGyg==", "dev": true, "requires": { - "isexe": "^2.0.0" + "isexe": "2.0.0" } }, "widest-line": { @@ -7313,7 +7281,7 @@ "integrity": "sha1-DAnIXCqUaD0Nfq+O4JfVZL8OEFw=", "dev": true, "requires": { - "string-width": "^1.0.1" + "string-width": "1.0.2" } }, "window-size": { @@ -7328,13 +7296,13 @@ "resolved": "https://registry.npmjs.org/winston/-/winston-2.2.0.tgz", "integrity": "sha1-LIU92Hq1UqjoSF1yy7+aIobwKbc=", "requires": { - "async": "~1.0.0", - "colors": "1.0.x", - "cycle": "1.0.x", - "eyes": "0.1.x", - "isstream": "0.1.x", - "pkginfo": "0.3.x", - "stack-trace": "0.0.x" + "async": "1.0.0", + "colors": "1.0.3", + "cycle": "1.0.3", + "eyes": "0.1.8", + "isstream": "0.1.2", + "pkginfo": "0.3.1", + "stack-trace": "0.0.9" }, "dependencies": { "async": { @@ -7361,7 +7329,7 @@ "integrity": "sha1-X8A4KOJkzqP+kUVUdvejxWbLB1c=", "dev": true, "requires": { - "mkdirp": "^0.5.1" + "mkdirp": "0.5.1" } }, "write-file-atomic": { @@ -7370,9 +7338,9 @@ "integrity": "sha512-xuPeK4OdjWqtfi59ylvVL0Yn35SF3zgcAcv7rBPFHVaEapaDr4GdGgm3j7ckTwH9wHL7fGmgfAnb0+THrHb8tA==", "dev": true, "requires": { - "graceful-fs": "^4.1.11", - "imurmurhash": "^0.1.4", - "signal-exit": "^3.0.2" + "graceful-fs": "4.1.11", + "imurmurhash": "0.1.4", + "signal-exit": "3.0.2" } }, "xdg-basedir": { @@ -7400,9 +7368,9 @@ "dev": true, "optional": true, "requires": { - "camelcase": "^1.0.2", - "cliui": "^2.1.0", - "decamelize": "^1.0.0", + "camelcase": "1.2.1", + "cliui": "2.1.0", + "decamelize": "1.2.0", "window-size": "0.1.0" } } diff --git a/package.json b/package.json index e63eede..9f78157 100644 --- a/package.json +++ b/package.json @@ -40,6 +40,7 @@ "prom-client": "^11.5.3", "raven": "^2.2.1", "request": "^2.81.0", + "semver": "^7.3.2", "stack-trace": "^0.0.9", "uuid": "^3.0.1", "winston": "^2.2.0" diff --git a/test/fakes/FakeStorage.js b/test/fakes/FakeStorage.js index 34920dd..4ab4de3 100644 --- a/test/fakes/FakeStorage.js +++ b/test/fakes/FakeStorage.js @@ -58,7 +58,7 @@ class FakeStorage extends Storage { }; } - async putCode(namespace, id, code) { + async putCode(namespace, id, version, code) { this.lastPutCode = code; if (id === 'error') { throw new Error('Storage error'); @@ -74,7 +74,7 @@ class FakeStorage extends Storage { return null; } - async getCodeByCache(namespace, id, { preCache }) { + async getCodeByCache(namespace, id, version, { preCache }) { if (id === 'cached') { const script = new Sandbox({}).compileCode('cached.js', ` function main(req, res) { @@ -119,24 +119,26 @@ class FakeStorage extends Storage { } getCodesByCache(codes, { preCache }) { - const promises = codes.map(c => this.getCodeByCache(c.namespace, c.id, { preCache })); + const promises = codes.map(c => ( + this.getCodeByCache(c.namespace, c.id, c.version, { preCache }) + )); return Promise.all(promises); } - async putCodeEnviromentVariable(namespace, id, env, value) { - const code = await this.getCode(namespace, id); + async putCodeEnviromentVariable(namespace, id, version, env, value) { + const code = await this.getCode(namespace, id, version); if (!code) { const err = new Error('Function not found'); err.statusCode = 404; throw err; } - this.lastEnvSet = { namespace, id, env, value }; + this.lastEnvSet = { namespace, id, version, env, value }; return null; } - async deleteCodeEnviromentVariable(namespace, id, env) { - const code = await this.getCode(namespace, id); + async deleteCodeEnviromentVariable(namespace, id, version, env) { + const code = await this.getCode(namespace, id, version); if (!code) { const err = new Error('Function not found'); err.statusCode = 404; diff --git a/test/fakes/FakeStorageV2.js b/test/fakes/FakeStorageV2.js new file mode 100644 index 0000000..4a82fd8 --- /dev/null +++ b/test/fakes/FakeStorageV2.js @@ -0,0 +1,179 @@ +/* eslint class-methods-use-this: ['error', { "exceptMethods": [ + 'listNamespaces', 'getCode', 'deleteCode', 'getCodeByCache', 'compileCode', 'runScript', + 'getNamespace', 'deleteNamespace', 'search' + ]}]*/ + +const Sandbox = require('@globocom/backstage-functions-sandbox'); +const Storage = require('../../lib/domain/storage'); + +class FakeStorage extends Storage { + constructor() { + super(); + this.lastPutCode = null; + this.lastEnvSet = null; + this.lastEnvUnset = null; + } + + async listNamespaces() { + return { + items: [ + { namespace: 'namespace1', id: 'function', version: '0.0.1' }, + { namespace: 'namespace2', id: 'function', version: '0.0.1' }, + { namespace: 'namespace3', id: 'function', version: '0.0.1' }, + ], + }; + } + + async search(namespace, id) { + if (id === 'function1' && namespace === 'namespace1') { + return { + items: [ + { namespace: 'namespace1', id: 'function1', version: '0.0.1' }, + { namespace: 'namespace1', id: 'function1', version: '0.0.2' }, + ], + }; + } + if (namespace === 'namespace1') { + return { + items: [ + { namespace: 'namespace1', id: 'function1', version: '0.0.1' }, + { namespace: 'namespace1', id: 'function2', version: '0.0.1' }, + ], + }; + } + if (namespace === 'error' || id === 'error') { + throw new Error('Storage error'); + } + return { items: [] }; + } + + async getCode(namespace, id) { + if (id === 'not-found') { + return null; + } else if (id === 'error') { + throw new Error('Failed to get code'); + } + return { + hash: 'my-hash-123', + code: 'function main() {}', + }; + } + + async putCode(namespace, id, version, code) { + this.lastPutCode = code; + if (id === 'error') { + throw new Error('Storage error'); + } + + return null; + } + + async deleteCode(namespace, id) { + if (id === 'error') { + throw new Error('Storage error'); + } + return null; + } + + async getCodeByCache(namespace, id, version, { preCache }) { + if (id === 'cached') { + const script = new Sandbox({}).compileCode('cached.js', ` + function main(req, res) { + res.send({ result: 'cached', body: req.body }) + }`); + return ({ script }); + } else if (id === 'fresh') { + const code = ` + function main(req, res) { + res.send({ result: 'fresh', body: req.body, env: Backstage.env }) + }`; + const env = { MY_FOO: 'bar' }; + return preCache({ code, env }); + } else if (id === 'send-string') { + const code = ` + function main(req, res) { + res.send('this is an alert'); + }`; + return preCache({ code }); + } else if (id === 'step1') { + const code = ` + function main(req, res) { + res.send({ x: req.body.x * 10 }) + }`; + return preCache({ code }); + } else if (id === 'step2') { + const code = ` + function main(req, res) { + res.send({ x: req.body.x * 20 }) + }`; + return preCache({ code }); + } else if (id === 'error') { + throw new Error('Storage error'); + } else if (id === 'customError') { + const err = new Error('Custom error'); + err.statusCode = 422; + throw err; + } else if (id === 'not-found') { + return null; + } + throw new Error('Unexpected id'); + } + + getCodesByCache(codes, { preCache }) { + const promises = codes.map(c => ( + this.getCodeByCache(c.namespace, c.id, c.version, { preCache }) + )); + return Promise.all(promises); + } + + async putCodeEnviromentVariable(namespace, id, version, env, value) { + const code = await this.getCode(namespace, id, version); + if (!code) { + const err = new Error('Function not found'); + err.statusCode = 404; + throw err; + } + + this.lastEnvSet = { namespace, id, version, env, value }; + return null; + } + + async deleteCodeEnviromentVariable(namespace, id, version, env) { + const code = await this.getCode(namespace, id, version); + if (!code) { + const err = new Error('Function not found'); + err.statusCode = 404; + throw err; + } + + this.lastEnvUnset = { namespace, id, env }; + return null; + } + + async putNamespace(namespace, data) { + if (namespace === 'error') { + throw new Error('Storage error'); + } + + this.lastPutNamespace = Object.assign({}, data, { namespace }); + } + + async getNamespace(namespace) { + if (namespace === 'found') { + return { namespace, sentryDSN: 'http://sentry.io/project' }; + } else if (namespace === 'error') { + throw new Error('Storage error'); + } + + return null; + } + + async deleteNamespace(namespace) { + if (namespace === 'error') { + throw new Error('Storage error'); + } + return null; + } +} + +module.exports = FakeStorage; diff --git a/test/integration/domain/http/routers/FunctionsRouter.test.js b/test/integration/domain/http/routers/FunctionsRouter.test.js index a43ec4d..1ad8fd4 100644 --- a/test/integration/domain/http/routers/FunctionsRouter.test.js +++ b/test/integration/domain/http/routers/FunctionsRouter.test.js @@ -45,7 +45,7 @@ describe('FunctionRouter integration', () => { .expect('content-type', /json/, done); }); - it('should put the code at the function', (done) => { + it('should get the code at the function', (done) => { request(routes) .get('/functions/function-router-get/test2') .expect(200) diff --git a/test/integration/domain/http/routers/FunctionsV2Router.test.js b/test/integration/domain/http/routers/FunctionsV2Router.test.js new file mode 100644 index 0000000..bcef365 --- /dev/null +++ b/test/integration/domain/http/routers/FunctionsV2Router.test.js @@ -0,0 +1,342 @@ +const request = require('supertest'); +const expect = require('chai').expect; +const routes = require('../../../../../lib/http/routes'); + +describe('FunctionV2Router integration', () => { + describe('PUT /v2/functions/:namespace/:id/:version', () => { + describe('when code is correct', () => { + const code = ` + function main(req, res) { + res.send({ foo: 'bar' }); + } + `; + + it('should put the code at the function', (done) => { + request(routes) + .put('/v2/functions/functionv2-router-test/test1/0.0.1') + .send({ code }) + .expect('content-type', /json/) + .expect(200) + .end((err, res) => { + if (err) { + done(err); + } + expect(res.body.code).to.be.eql(code); + expect(res.body.id).to.be.eql('test1'); + expect(res.body.version).to.be.eql('0.0.1'); + done(); + }); + }); + }); + }); + + describe('PUT v1 and GET v2 with latest', () => { + let code; + before((done) => { + code = ` + function main(req, res) { + res.send({ foo: 'bar' }); + } + `; + + request(routes) + .put('/functions/function-v1/test1') + .send({ code }) + .expect(200) + .expect('content-type', /json/, done); + }); + + it('should return the code at latest version', (done) => { + request(routes) + .get('/v2/functions/function-v1/test1/latest') + .expect(200) + .expect('content-type', /json/) + .end((err, res) => { + if (err) { + done(err); + } + + expect(res.body.code).to.be.eql(code); + expect(res.body.id).to.be.eql('test1'); + expect(res.body.version).to.be.eql(null); + done(); + }); + }); + }); + + describe('PUT v2 and GET v1', () => { + let code; + before((done) => { + code = ` + function main(req, res) { + res.send({ foo: 'bar' }); + } + `; + + request(routes) + .put('/v2/functions/function-v2/test1/0.0.1') + .send({ code }) + .expect(200) + .expect('content-type', /json/); + + request(routes) + .put('/v2/functions/function-v2/test1/0.0.2') + .send({ code }) + .expect(200) + .expect('content-type', /json/, done); + }); + + it('should return the code at latest version', (done) => { + request(routes) + .get('/functions/function-v2/test1') + .expect(200) + .expect('content-type', /json/) + .end((err, res) => { + if (err) { + done(err); + } + + expect(res.body.code).to.be.eql(code); + expect(res.body.id).to.be.eql('test1'); + expect(res.body.version).to.be.eql('0.0.2'); + done(); + }); + }); + }); + + describe('PUT v2 old version and GET v1 latest', () => { + let code; + before((done) => { + code = ` + function main(req, res) { + res.send({ foo: 'bar' }); + } + `; + + // newer version + request(routes) + .put('/v2/functions/function-v2/test2/0.2.1') + .send({ code }) + .expect(200) + .expect('content-type', /json/, done); + }); + + // patch fix for 0.1.1 + before((done) => { + request(routes) + .put('/v2/functions/function-v2/test2/0.1.2') + .send({ code }) + .expect(200) + .expect('content-type', /json/, done); + }); + + it('should return the code at latest version', (done) => { + request(routes) + .get('/functions/function-v2/test2') + .expect(200) + .expect('content-type', /json/) + .end((err, res) => { + if (err) { + done(err); + } + + expect(res.body.code).to.be.eql(code); + expect(res.body.id).to.be.eql('test2'); + expect(res.body.version).to.be.eql('0.2.1'); + done(); + }); + }); + }); + + describe('GET /v2/functions/:namespace/:id/:version', () => { + let code; + before((done) => { + code = ` + function main(req, res) { + res.send({ foo: 'bar' }); + } + `; + + request(routes) + .put('/v2/functions/functionv2-router-get/test2/0.0.1') + .send({ code }) + .expect(200) + .expect('content-type', /json/, done); + }); + + it('should get the code at the function', (done) => { + request(routes) + .get('/v2/functions/functionv2-router-get/test2/0.0.1') + .expect(200) + .expect('content-type', /json/) + .end((err, res) => { + if (err) { + done(err); + } + + expect(res.body.code).to.be.eql(code); + expect(res.body.id).to.be.eql('test2'); + expect(res.body.version).to.be.eql('0.0.1'); + expect(res.body.hash).to.exists; + done(); + }); + }); + }); + + describe('GET /v2/functions/:namespace/:id/:version/run', () => { + describe('simple run with json body', () => { + before((done) => { + const code = ` + function main(req, res) { + res.send({ hey: req.method }); + } + `; + + request(routes) + .put('/v2/functions/functionv2-router-run/test2/0.0.1') + .send({ code }) + .expect(200) + .expect('content-type', /json/, done); + }); + + it('should runs the code and return properlly', (done) => { + request(routes) + .get('/v2/functions/functionv2-router-run/test2/0.0.1/run') + .expect(200) + .expect('content-type', /json/) + .expect({ hey: 'GET' }, done); + }); + }); + }); + + describe('PUT /v2/functions/:namespace/:id/:version/run', () => { + describe('simple run with json body', () => { + before((done) => { + const code = ` + function main(req, res) { + res.send({ foo: 'bar' }); + } + `; + + request(routes) + .put('/v2/functions/functionv2-router-run/test1/0.0.1') + .send({ code }) + .expect(200) + .expect('content-type', /json/, done); + }); + + it('should runs the code and return properlly', (done) => { + request(routes) + .put('/v2/functions/functionv2-router-run/test1/0.0.1/run') + .expect(200) + .expect('content-type', /json/) + .expect({ foo: 'bar' }, done); + }); + }); + + describe('500 status code', () => { + before((done) => { + const code = ` + function main(req, res) { + res.internalServerError('My server is crashed'); + } + `; + + request(routes) + .put('/v2/functions/functionv2-router-run/test2/0.0.1') + .send({ code }) + .expect(200) + .expect('content-type', /json/, done); + }); + + it('should returns the status 500 with text plain content', (done) => { + request(routes) + .put('/v2/functions/functionv2-router-run/test2/0.0.1/run') + .expect(500) + .expect('content-type', /json/) + .expect('{"error":"My server is crashed"}', done); + }); + }); + + describe('304 status code', () => { + before((done) => { + const code = ` + function main(req, res) { + res.notModified(); + } + `; + + request(routes) + .put('/v2/functions/functionv2-router-run/test2/0.0.1') + .send({ code }) + .expect(200) + .expect('content-type', /json/, done); + }); + + it('should returns the status 500 with text plain content', (done) => { + request(routes) + .put('/v2/functions/functionv2-router-run/test2/0.0.1/run') + .expect(304) + .expect('', done); + }); + }); + + describe('body and query string to the code and combine then', () => { + before((done) => { + const code = ` + function main(req, res) { + const query = req.query; + const body = req.body; + res.send({ query, body }); + } + `; + + request(routes) + .put('/v2/functions/functionv2-router-run/test3/0.0.1') + .send({ code }) + .expect(200) + .expect('content-type', /json/, done); + }); + + it('should returns the status 403 with text plain content', (done) => { + const person = { name: 'John Doe' }; + + request(routes) + .put('/v2/functions/functionv2-router-run/test3/0.0.1/run?where[name]=John') + .send({ person }) + .expect(200) + .expect('content-type', /json/) + .expect({ + body: { person }, + query: { where: { name: 'John' } }, + }, done); + }); + }); + + describe('require arbitrary library inside function', () => { + before((done) => { + const code = ` + const _ = require('lodash'); + const people = [{name: 'John'}, {name: 'Doe'}]; + function main(req, res) { + const names = _.map(people, 'name'); + res.send({ names }); + } + `; + + request(routes) + .put('/v2/functions/functionv2-router-run/test4/0.0.1').send({ code }) + .expect(200) + .expect('content-type', /json/, done); + }); + + it('should uses the arbitrary library properly', (done) => { + request(routes) + .put('/v2/functions/functionv2-router-run/test4/0.0.1/run') + .expect(200) + .expect('content-type', /json/) + .expect({ names: ['John', 'Doe'] }, done); + }); + }); + }); +}); diff --git a/test/integration/domain/storage/storage-redis.test.js b/test/integration/domain/storage/storage-redis.test.js index 269b8d4..59157c6 100644 --- a/test/integration/domain/storage/storage-redis.test.js +++ b/test/integration/domain/storage/storage-redis.test.js @@ -41,10 +41,10 @@ describe('StorageRedis', () => { MY_VAR: 'my var', }, }; - const x = await storage.putCode('backstage', 'test', code); + const x = await storage.putCode('backstage', 'test', 'latest', code); expect(x).to.be.eql('OK'); - const code2 = await storage.getCode('backstage', 'test'); + const code2 = await storage.getCode('backstage', 'test', 'latest'); expect(code2.id).to.be.eql('test'); expect(code2.namespace).to.be.eql('backstage'); @@ -62,10 +62,10 @@ describe('StorageRedis', () => { hash: '123', }; - const x = await storage.putCode('backstage', code.id, code); + const x = await storage.putCode('backstage', code.id, 'latest', code); expect(x).to.be.eql('OK'); - const code2 = await storage.getCode('backstage', code.id); + const code2 = await storage.getCode('backstage', code.id, 'latest'); expect(code2.created).to.be.eql(code2.updated); }); }); @@ -74,7 +74,7 @@ describe('StorageRedis', () => { describe('#getCode()', () => { describe('when code id is not found', () => { it('should yield a null', async () => { - const code = await storage.getCode('backstage', 'not-found'); + const code = await storage.getCode('backstage', 'not-found', 'latest'); expect(code).to.be.null; }); }); @@ -90,18 +90,18 @@ describe('StorageRedis', () => { hash: '123', }; await storage.deleteNamespace(this.namespace); - await storage.deleteCode(this.namespace, this.code.id); + await storage.deleteCode(this.namespace, this.code.id, 'latest'); }); it('when is configured in the namespace', async () => { - const x = await storage.putCode(this.namespace, this.code.id, this.code); + const x = await storage.putCode(this.namespace, this.code.id, 'latest', this.code); expect(x).to.be.eql('OK'); await storage.putNamespace(this.namespace, { namespace: this.namespace, sentryDSN: 'http://my-sentry.io/foo', }); - const result = await storage.getCode(this.namespace, this.code.id); + const result = await storage.getCode(this.namespace, this.code.id, 'latest'); expect(result.sentryDSN).to.be.eql('http://my-sentry.io/foo'); }); @@ -109,18 +109,18 @@ describe('StorageRedis', () => { this.code.env = { sentryDSN: 'http://my-sentry.io/fooenv', }; - const x = await storage.putCode(this.namespace, this.code.id, this.code); + const x = await storage.putCode(this.namespace, this.code.id, 'latest', this.code); expect(x).to.be.eql('OK'); - const result = await storage.getCode(this.namespace, this.code.id); + const result = await storage.getCode(this.namespace, this.code.id, 'latest'); expect(result.sentryDSN).to.be.eql('http://my-sentry.io/fooenv'); }); it('when isn`t configured', async () => { - const x = await storage.putCode(this.namespace, this.code.id, this.code); + const x = await storage.putCode(this.namespace, this.code.id, 'latest', this.code); expect(x).to.be.eql('OK'); - const result = await storage.getCode(this.namespace, this.code.id); + const result = await storage.getCode(this.namespace, this.code.id, 'latest'); expect(result.sentryDSN).to.be.null; }); }); @@ -135,13 +135,13 @@ describe('StorageRedis', () => { code: 'a = 1;', hash: '123', }; - const putResponse = await storage.putCode(namespace, id, code); + const putResponse = await storage.putCode(namespace, id, 'latest', code); expect(putResponse).to.be.eql('OK'); - const deleteResponse = await storage.deleteCode(namespace, id); + const deleteResponse = await storage.deleteCode(namespace, id, 'latest'); expect(deleteResponse).to.be.eql(1); - const code2 = await storage.getCode(namespace, id); + const code2 = await storage.getCode(namespace, id, 'latest'); expect(code2).to.be.null; }); }); @@ -157,7 +157,7 @@ describe('StorageRedis', () => { const namespace = 'backstage'; const id = 'cache-000'; - const cacheResponse = await storage.getCodeByCache(namespace, id, { preCache }); + const cacheResponse = await storage.getCodeByCache(namespace, id, 'latest', { preCache }); expect(cacheResponse).to.be.null; }); }); @@ -178,10 +178,10 @@ describe('StorageRedis', () => { hash: '123', }; - const putResponse = await storage.putCode(namespace, id, code); + const putResponse = await storage.putCode(namespace, id, 'latest', code); expect(putResponse).to.be.eql('OK'); - const cacheResponse = await storage.getCodeByCache(namespace, id, { preCache }); + const cacheResponse = await storage.getCodeByCache(namespace, id, 'latest', { preCache }); expect(cacheResponse.preCached).to.be.true; expect(preCache.called).to.be.true; @@ -203,16 +203,16 @@ describe('StorageRedis', () => { code: 'b = 1;', hash: '123a', }; - const putResponse = await storage.putCode(namespace, id, code); + const putResponse = await storage.putCode(namespace, id, 'latest', code); expect(putResponse).to.be.eql('OK'); - let cacheResponse = await storage.getCodeByCache(namespace, id, { preCache }); + let cacheResponse = await storage.getCodeByCache(namespace, id, 'latest', { preCache }); expect(cacheResponse.preCached).to.be.true; preCache.called = false; const lastVersionID = cacheResponse.versionID; - cacheResponse = await storage.getCodeByCache(namespace, id, { preCache }); + cacheResponse = await storage.getCodeByCache(namespace, id, 'latest', { preCache }); expect(cacheResponse.id).to.be.eql(id); expect(cacheResponse.code).to.be.eql('b = 1;'); expect(cacheResponse.hash).to.be.eql('123a'); @@ -238,21 +238,21 @@ describe('StorageRedis', () => { hash: '123a', }; - const putResponse = await storage.putCode(namespace, id, code); + const putResponse = await storage.putCode(namespace, id, 'latest', code); expect(putResponse).to.be.eql('OK'); // populate the cache - let cacheResponse = await storage.getCodeByCache(namespace, id, { preCache }); + let cacheResponse = await storage.getCodeByCache(namespace, id, 'latest', { preCache }); expect(cacheResponse.preCachedByHash).to.be.eql('123a'); // change item in database code.code = 'd = 2;'; code.hash = '123b'; const lastVersionID = cacheResponse.versionID; - await storage.putCode(namespace, id, code); + await storage.putCode(namespace, id, 'latest', code); preCache.called = false; - cacheResponse = await storage.getCodeByCache(namespace, id, { preCache }); + cacheResponse = await storage.getCodeByCache(namespace, id, 'latest', { preCache }); expect(cacheResponse.id).to.be.eql(id); expect(cacheResponse.code).to.be.eql('d = 2;'); @@ -279,6 +279,7 @@ describe('StorageRedis', () => { code1 = { namespace: 'backstage', id: 'code1', + version: 'latest', code: 'c = 1;', hash: '123a', }; @@ -286,6 +287,7 @@ describe('StorageRedis', () => { code2 = { namespace: 'backstage', id: 'code2', + version: 'latest', code: 'c = 1;', hash: '123b', }; @@ -304,8 +306,8 @@ describe('StorageRedis', () => { describe('when all codes are found', () => { it('should return all codes', async () => { const [putResponse1, putResponse2] = await Promise.all([ - storage.putCode(code1.namespace, code1.id, code1), - storage.putCode(code2.namespace, code2.id, code2), + storage.putCode(code1.namespace, code1.id, 'latest', code1), + storage.putCode(code2.namespace, code2.id, 'latest', code2), ]); expect(putResponse1).to.be.eql('OK'); @@ -320,16 +322,16 @@ describe('StorageRedis', () => { describe('when some code are updated', () => { it('should return all codes', async () => { const [putResponse1, putResponse2] = await Promise.all([ - storage.putCode(code1.namespace, code1.id, code1), - storage.putCode(code2.namespace, code2.id, code2), + storage.putCode(code1.namespace, code1.id, 'latest', code1), + storage.putCode(code2.namespace, code2.id, 'latest', code2), ]); expect(putResponse1).to.be.eql('OK'); expect(putResponse2).to.be.eql('OK'); const [savedCode1, savedCode2] = await Promise.all([ - storage.getCode(code1.namespace, code1.id), - storage.getCode(code2.namespace, code2.id), + storage.getCode(code1.namespace, code1.id, 'latest'), + storage.getCode(code2.namespace, code2.id, 'latest'), ]); const code1VersionID = savedCode1.versionID; @@ -342,7 +344,7 @@ describe('StorageRedis', () => { expect(result2.preCached).to.be.true; code2.code = 'console.info("changed");'; - const putResponse = await storage.putCode(code2.namespace, code2.id, code2); + const putResponse = await storage.putCode(code2.namespace, code2.id, 'latest', code2); expect(putResponse).to.be.eql('OK'); [result1, result2] = await storage.getCodesByCache([code1, code2], { preCache }); @@ -359,15 +361,16 @@ describe('StorageRedis', () => { const code = { namespace: 'backstage', id: 'test-env', + version: 'latest', code: 'a = 1;', hash: '123', }; - const x = await storage.putCode(code.namespace, code.id, code); + const x = await storage.putCode(code.namespace, code.id, 'latest', code); expect(x).to.be.eql('OK'); - await storage.putCodeEnviromentVariable(code.namespace, code.id, varName, 'true'); + await storage.putCodeEnviromentVariable(code.namespace, code.id, 'latest', varName, 'true'); - const { env, code: source } = await storage.getCode(code.namespace, code.id); + const { env, code: source } = await storage.getCode(code.namespace, code.id, 'latest'); expect(source).to.be.eql(code.code); expect(env).to.be.eql({ MY_SKIP: 'true', @@ -378,7 +381,7 @@ describe('StorageRedis', () => { describe('when target function is not found', () => { it('should raise an error', async () => { try { - await storage.putCodeEnviromentVariable('backstage', 'test-env-not-found', 'MY_SKIP', 'true'); + await storage.putCodeEnviromentVariable('backstage', 'test-env-not-found', 'latest', 'MY_SKIP', 'true'); } catch (err) { expect(err.message).to.be.eql('Function not found'); expect(err.statusCode).to.be.eql(404); @@ -398,17 +401,18 @@ describe('StorageRedis', () => { namespace: 'backstage', id: 'test-env-unset', code: 'a = 1;', + version: 'latest', hash: '123', env: {}, }; code.env[varName] = 'me'; - const x = await storage.putCode(code.namespace, code.id, code); + const x = await storage.putCode(code.namespace, code.id, 'latest', code); expect(x).to.be.eql('OK'); - await storage.deleteCodeEnviromentVariable(code.namespace, code.id, varName); + await storage.deleteCodeEnviromentVariable(code.namespace, code.id, 'latest', varName); - const { env, code: source } = await storage.getCode(code.namespace, code.id); + const { env, code: source } = await storage.getCode(code.namespace, code.id, 'latest'); expect(source).to.be.eql(code.code); expect(env).to.be.eql({}); }); @@ -420,6 +424,7 @@ describe('StorageRedis', () => { const code = { namespace: 'backstage', id: 'test-env-unset', + version: 'latest', code: 'a = 1;', hash: '123', env: { @@ -428,16 +433,16 @@ describe('StorageRedis', () => { }; try { - const x = await storage.putCode(code.namespace, code.id, code); + const x = await storage.putCode(code.namespace, code.id, 'latest', code); expect(x).to.be.eql('OK'); - await storage.deleteCodeEnviromentVariable(code.namespace, code.id, varName); + await storage.deleteCodeEnviromentVariable(code.namespace, code.id, 'latest', varName); throw new Error('Not raised an expection'); } catch (err) { expect(err.message).to.be.eql('Env variable not found'); expect(err.statusCode).to.be.eql(404); } - const { env, code: source } = await storage.getCode(code.namespace, code.id); + const { env, code: source } = await storage.getCode(code.namespace, code.id, 'latest'); expect(source).to.be.eql(code.code); expect(env).to.be.eql({ TO_MAINTAIN: 'true', @@ -454,7 +459,7 @@ describe('StorageRedis', () => { }; try { - await storage.deleteCodeEnviromentVariable(code.namespace, code.id, varName); + await storage.deleteCodeEnviromentVariable(code.namespace, code.id, 'latest', varName); } catch (err) { expect(err.message).to.be.eql('Function not found'); expect(err.statusCode).to.be.eql(404); @@ -546,11 +551,11 @@ describe('StorageRedis', () => { describe('#search()', () => { describe('with valid values', () => { before(() => { - storage.setNamespaceMember('namespace1', 'function1'); - storage.setNamespaceMember('namespace1', 'function2'); - storage.setNamespaceMember('namespace2', 'function1'); + storage.setNamespaceMember('namespace1', 'function1', 'latest'); + storage.setNamespaceMember('namespace1', 'function2', 'latest'); + storage.setNamespaceMember('namespace2', 'function1', 'latest'); for (let i = 1; i <= 25; i += 1) { - storage.setNamespaceMember('namespace3', `function${i}`); + storage.setNamespaceMember('namespace3', `function${i}`, 'latest'); } }); @@ -561,10 +566,12 @@ describe('StorageRedis', () => { { id: 'function1', namespace: 'namespace1', + version: 'latest', }, { id: 'function2', namespace: 'namespace1', + version: 'latest', }, ], nextPage: 2, @@ -580,6 +587,7 @@ describe('StorageRedis', () => { { id: 'function1', namespace: 'namespace1', + version: 'latest', }, ], nextPage: 2, @@ -595,19 +603,19 @@ describe('StorageRedis', () => { it('with page 2', async () => { const page = 2; - const list = await storage.search('namespace3', '', page); + const list = await storage.search('namespace3', '', '', page); expect(list.items).to.have.lengthOf(10); }); it('with page 3', async () => { const page = 3; - const list = await storage.search('namespace3', '', page); + const list = await storage.search('namespace3', '', '', page); expect(list.items).to.have.lengthOf(5); }); it('with perPage 5', async () => { const perPage = 5; - const list = await storage.search('namespace3', '', 1, perPage); + const list = await storage.search('namespace3', '', '', 1, perPage); expect(list.items).to.have.lengthOf(perPage); }); }); diff --git a/test/unit/domain/LogStorage.test.js b/test/unit/domain/LogStorage.test.js index a9ff230..86cb631 100644 --- a/test/unit/domain/LogStorage.test.js +++ b/test/unit/domain/LogStorage.test.js @@ -13,17 +13,17 @@ const Graygelf = require('../../GelfServer'); describe('StdoutLogStorage', () => { /* eslint no-underscore-dangle: ["error", { "allow": ["_stdout", "_stderr"] }] */ - const logStorage = new StdoutLogStorage('test-namespace', 'test-id'); + const logStorage = new StdoutLogStorage('test-namespace', 'test-id', 'latest'); it('should create prefixed stdout console', () => { const stdout = logStorage.console._stdout; - expect(stdout.prefix).to.be.eql('info: [namespace:test-namespace, id:test-id]'); + expect(stdout.prefix).to.be.eql('info: [namespace:test-namespace, id:test-id, version:latest]'); expect(stdout.buf).to.be.eql(process.stdout); }); it('should create prefixed stderr console', () => { const stderr = logStorage.console._stderr; - expect(stderr.prefix).to.be.eql('error: [namespace:test-namespace, id:test-id]'); + expect(stderr.prefix).to.be.eql('error: [namespace:test-namespace, id:test-id, version:latest]'); expect(stderr.buf).to.be.eql(process.stderr); }); }); @@ -57,7 +57,7 @@ describe('GelfLogStorage', () => { }); config.log.hosts = ['localhost', '127.0.0.1']; - logStorage = new GelfLogStorage('test-namespace', 'test-id', req); + logStorage = new GelfLogStorage('test-namespace', 'test-id', 'latest', req); }); after(() => { @@ -84,7 +84,7 @@ describe('GelfLogStorage', () => { describe('when has more than one host in config.log.hosts', () => { before(() => { - newLogStorage = new GelfLogStorage('test-namespace', 'test-id', req); + newLogStorage = new GelfLogStorage('test-namespace', 'test-id', 'latest', req); }); after(() => { @@ -129,7 +129,7 @@ describe('GelfLogStorage', () => { it('should receive short_message attribute', () => { expect(receivedMsg.short_message).to.be.eql( - 'Function "test-namespace/test-id.js" was executed with status 200 "OK"' + 'Function "test-namespace/test-id/latest.js" was executed with status 200 "OK"' ); }); @@ -153,7 +153,7 @@ describe('GelfLogStorage', () => { }); it('should receive file attribute', () => { - expect(receivedMsg._file).to.be.eql('test-namespace/test-id.js'); + expect(receivedMsg._file).to.be.eql('test-namespace/test-id/latest.js'); }); it('should receive rid attribute extracted from HTTP header setting', () => { diff --git a/test/unit/domain/Metric.test.js b/test/unit/domain/Metric.test.js index 587ea1a..61f88c0 100644 --- a/test/unit/domain/Metric.test.js +++ b/test/unit/domain/Metric.test.js @@ -11,10 +11,10 @@ describe('Metric', () => { }); it('should increment metrics in registry', () => { - new Metric().observeFunctionRun({ namespace: 'xpto', id: 'blah', status: 403 }); + new Metric().observeFunctionRun({ namespace: 'xpto', id: 'blah', version: 'latest', status: 403 }); const data = prometheusClient.register.metrics(); - expect(data).to.be.include('backstage_functions_function_run_total{namespace="xpto",id="blah",status="4xx"} 1'); - expect(data).to.be.include('backstage_functions_function_run_duration_seconds_bucket{le="0.05",namespace="xpto",id="blah"} 1'); + expect(data).to.be.include('backstage_functions_function_run_total{namespace="xpto",id="blah",version="latest",status="4xx"} 1'); + expect(data).to.be.include('backstage_functions_function_run_duration_seconds_bucket{le="0.05",namespace="xpto",id="blah",version="latest"} 1'); expect(data).to.be.include('backstage_functions_overview_run_total{status="4xx"} 1'); expect(data).to.be.include('backstage_functions_overview_run_duration_seconds_bucket{le="0.05"} 1'); diff --git a/test/unit/domain/storageInMemory.test.js b/test/unit/domain/storageInMemory.test.js index 274b92f..e2ff222 100644 --- a/test/unit/domain/storageInMemory.test.js +++ b/test/unit/domain/storageInMemory.test.js @@ -23,10 +23,10 @@ describe('StorageInMemory', () => { MY_VAR: 'my var', }, }; - const x = await storage.putCode('backstage', 'test', code); + const x = await storage.putCode('backstage', 'test', 'latest', code); expect(x).to.be.eql('OK'); - const code2 = await storage.getCode('backstage', 'test'); + const code2 = await storage.getCode('backstage', 'test', 'latest'); expect(code2.id).to.be.eql('test'); expect(code2.namespace).to.be.eql('backstage'); @@ -44,10 +44,10 @@ describe('StorageInMemory', () => { hash: '123', }; - const x = await storage.putCode('backstage', code.id, code); + const x = await storage.putCode('backstage', code.id, 'latest', code); expect(x).to.be.eql('OK'); - const code2 = await storage.getCode('backstage', code.id); + const code2 = await storage.getCode('backstage', code.id, 'latest'); expect(code2.created).to.be.eql(code2.updated); }); }); @@ -56,7 +56,7 @@ describe('StorageInMemory', () => { describe('#getCode()', () => { describe('when code id is not found', () => { it('should yield a null', async () => { - const code = await storage.getCode('backstage', 'not-found'); + const code = await storage.getCode('backstage', 'not-found', 'latest'); expect(code).to.be.null; }); }); @@ -70,21 +70,22 @@ describe('StorageInMemory', () => { this.code = { id: 'test', code: 'a = 1;', + version: 'latest', hash: '123', }; await storage.deleteNamespace(this.namespace); - await storage.deleteCode(this.namespace, this.code.id); + await storage.deleteCode(this.namespace, this.code.id, 'latest'); }); it('when is configured in the namespace', async () => { - const x = await storage.putCode(this.namespace, this.code.id, this.code); + const x = await storage.putCode(this.namespace, this.code.id, 'latest', this.code); expect(x).to.be.eql('OK'); await storage.putNamespace(this.namespace, { namespace: this.namespace, sentryDSN: 'http://my-sentry.io/foo', }); - const result = await storage.getCode(this.namespace, this.code.id); + const result = await storage.getCode(this.namespace, this.code.id, 'latest'); expect(result.sentryDSN).to.be.eql('http://my-sentry.io/foo'); }); @@ -92,18 +93,18 @@ describe('StorageInMemory', () => { this.code.env = { sentryDSN: 'http://my-sentry.io/fooenv', }; - const x = await storage.putCode(this.namespace, this.code.id, this.code); + const x = await storage.putCode(this.namespace, this.code.id, 'latest', this.code); expect(x).to.be.eql('OK'); - const result = await storage.getCode(this.namespace, this.code.id); + const result = await storage.getCode(this.namespace, this.code.id, 'latest'); expect(result.sentryDSN).to.be.eql('http://my-sentry.io/fooenv'); }); it('when isn`t configured', async () => { - const x = await storage.putCode(this.namespace, this.code.id, this.code); + const x = await storage.putCode(this.namespace, this.code.id, 'latest', this.code); expect(x).to.be.eql('OK'); - const result = await storage.getCode(this.namespace, this.code.id); + const result = await storage.getCode(this.namespace, this.code.id, 'latest'); expect(result.sentryDSN).to.be.null; }); }); @@ -117,13 +118,13 @@ describe('StorageInMemory', () => { code: 'a = 1;', hash: '123', }; - const putResponse = await storage.putCode(namespace, id, code); + const putResponse = await storage.putCode(namespace, id, 'latest', code); expect(putResponse).to.be.eql('OK'); - const deleteResponse = await storage.deleteCode(namespace, id); + const deleteResponse = await storage.deleteCode(namespace, id, 'latest'); expect(deleteResponse).to.be.eql(1); - const code2 = await storage.getCode(namespace, id); + const code2 = await storage.getCode(namespace, id, 'latest'); expect(code2).to.be.null; }); }); @@ -134,7 +135,7 @@ describe('StorageInMemory', () => { const preCache = code => code; const namespace = 'backstage'; const id = 'cache-000'; - const cacheResponse = await storage.getCodeByCache(namespace, id, { preCache }); + const cacheResponse = await storage.getCodeByCache(namespace, id, 'latest', { preCache }); expect(cacheResponse).to.be.null; }); }); @@ -155,10 +156,10 @@ describe('StorageInMemory', () => { hash: '123', }; - const putResponse = await storage.putCode(namespace, id, code); + const putResponse = await storage.putCode(namespace, id, 'latest', code); expect(putResponse).to.be.eql('OK'); - const cacheResponse = await storage.getCodeByCache(namespace, id, { preCache }); + const cacheResponse = await storage.getCodeByCache(namespace, id, 'latest', { preCache }); expect(cacheResponse.preCached).to.be.true; expect(preCache.called).to.be.true; @@ -181,21 +182,21 @@ describe('StorageInMemory', () => { hash: '123a', }; - const putResponse = await storage.putCode(namespace, id, code); + const putResponse = await storage.putCode(namespace, id, 'latest', code); expect(putResponse).to.be.eql('OK'); // populate the cache - let cacheResponse = await storage.getCodeByCache(namespace, id, { preCache }); + let cacheResponse = await storage.getCodeByCache(namespace, id, 'latest', { preCache }); expect(cacheResponse.preCachedByHash).to.be.eql('123a'); // change item in database code.code = 'd = 2;'; code.hash = '123b'; const lastVersionID = cacheResponse.versionID; - await storage.putCode(namespace, id, code); + await storage.putCode(namespace, id, 'latest', code); preCache.called = false; - cacheResponse = await storage.getCodeByCache(namespace, id, { preCache }); + cacheResponse = await storage.getCodeByCache(namespace, id, 'latest', { preCache }); expect(cacheResponse.id).to.be.eql(id); expect(cacheResponse.code).to.be.eql('d = 2;'); @@ -222,6 +223,7 @@ describe('StorageInMemory', () => { code1 = { namespace: 'backstage', id: 'code1', + version: 'latest', code: 'c = 1;', hash: '123a', }; @@ -229,6 +231,7 @@ describe('StorageInMemory', () => { code2 = { namespace: 'backstage', id: 'code2', + version: 'latest', code: 'c = 1;', hash: '123b', }; @@ -247,8 +250,8 @@ describe('StorageInMemory', () => { describe('when all codes are found', () => { it('should return all codes', async () => { const [putResponse1, putResponse2] = await Promise.all([ - storage.putCode(code1.namespace, code1.id, code1), - storage.putCode(code2.namespace, code2.id, code2), + storage.putCode(code1.namespace, code1.id, 'latest', code1), + storage.putCode(code2.namespace, code2.id, 'latest', code2), ]); expect(putResponse1).to.be.eql('OK'); @@ -263,16 +266,16 @@ describe('StorageInMemory', () => { describe('when some code are updated', () => { it('should return all codes', async () => { const [putResponse1, putResponse2] = await Promise.all([ - storage.putCode(code1.namespace, code1.id, code1), - storage.putCode(code2.namespace, code2.id, code2), + storage.putCode(code1.namespace, code1.id, 'latest', code1), + storage.putCode(code2.namespace, code2.id, 'latest', code2), ]); expect(putResponse1).to.be.eql('OK'); expect(putResponse2).to.be.eql('OK'); const [savedCode1, savedCode2] = await Promise.all([ - storage.getCode(code1.namespace, code1.id), - storage.getCode(code2.namespace, code2.id), + storage.getCode(code1.namespace, code1.id, 'latest'), + storage.getCode(code2.namespace, code2.id, 'latest'), ]); const code1VersionID = savedCode1.versionID; @@ -285,7 +288,7 @@ describe('StorageInMemory', () => { expect(result2.preCached).to.be.true; code2.code = 'console.info("changed");'; - const putResponse = await storage.putCode(code2.namespace, code2.id, code2); + const putResponse = await storage.putCode(code2.namespace, code2.id, 'latest', code2); expect(putResponse).to.be.eql('OK'); [result1, result2] = await storage.getCodesByCache([code1, code2], { preCache }); @@ -302,15 +305,16 @@ describe('StorageInMemory', () => { const code = { namespace: 'backstage', id: 'test-env', + version: 'latest', code: 'a = 1;', hash: '123', }; - const x = await storage.putCode(code.namespace, code.id, code); + const x = await storage.putCode(code.namespace, code.id, 'latest', code); expect(x).to.be.eql('OK'); - await storage.putCodeEnviromentVariable(code.namespace, code.id, varName, 'true'); + await storage.putCodeEnviromentVariable(code.namespace, code.id, 'latest', varName, 'true'); - const { env, code: source } = await storage.getCode(code.namespace, code.id); + const { env, code: source } = await storage.getCode(code.namespace, code.id, 'latest'); expect(source).to.be.eql(code.code); expect(env).to.be.eql({ MY_SKIP: 'true', @@ -321,7 +325,7 @@ describe('StorageInMemory', () => { describe('when target function is not found', () => { it('should raise an error', async () => { try { - await storage.putCodeEnviromentVariable('backstage', 'test-env-not-found', 'MY_SKIP', 'true'); + await storage.putCodeEnviromentVariable('backstage', 'test-env-not-found', 'latest', 'MY_SKIP', 'true'); } catch (err) { expect(err.message).to.be.eql('Function not found'); expect(err.statusCode).to.be.eql(404); @@ -346,12 +350,12 @@ describe('StorageInMemory', () => { }; code.env[varName] = 'me'; - const x = await storage.putCode(code.namespace, code.id, code); + const x = await storage.putCode(code.namespace, code.id, 'latest', code); expect(x).to.be.eql('OK'); - await storage.deleteCodeEnviromentVariable(code.namespace, code.id, varName); + await storage.deleteCodeEnviromentVariable(code.namespace, code.id, 'latest', varName); - const { env, code: source } = await storage.getCode(code.namespace, code.id); + const { env, code: source } = await storage.getCode(code.namespace, code.id, 'latest'); expect(source).to.be.eql(code.code); expect(env).to.be.eql({}); }); @@ -371,16 +375,16 @@ describe('StorageInMemory', () => { }; try { - const x = await storage.putCode(code.namespace, code.id, code); + const x = await storage.putCode(code.namespace, code.id, 'latest', code); expect(x).to.be.eql('OK'); - await storage.deleteCodeEnviromentVariable(code.namespace, code.id, varName); + await storage.deleteCodeEnviromentVariable(code.namespace, code.id, 'latest', varName); throw new Error('Not raised an expection'); } catch (err) { expect(err.message).to.be.eql('Env variable not found'); expect(err.statusCode).to.be.eql(404); } - const { env, code: source } = await storage.getCode(code.namespace, code.id); + const { env, code: source } = await storage.getCode(code.namespace, code.id, 'latest'); expect(source).to.be.eql(code.code); expect(env).to.be.eql({ TO_MAINTAIN: 'true', @@ -397,7 +401,7 @@ describe('StorageInMemory', () => { }; try { - await storage.deleteCodeEnviromentVariable(code.namespace, code.id, varName); + await storage.deleteCodeEnviromentVariable(code.namespace, code.id, 'latest', varName); } catch (err) { expect(err.message).to.be.eql('Function not found'); expect(err.statusCode).to.be.eql(404); @@ -451,9 +455,9 @@ describe('StorageInMemory', () => { describe('#search', () => { describe('with valid values', () => { beforeEach(() => { - storage.setNamespaceMember('namespace1', 'function1'); - storage.setNamespaceMember('namespace1', 'function2'); - storage.setNamespaceMember('namespace2', 'function1'); + storage.setNamespaceMember('namespace1', 'function1', 'latest'); + storage.setNamespaceMember('namespace1', 'function2', 'latest'); + storage.setNamespaceMember('namespace2', 'function1', 'latest'); }); it('with namespace', async () => { @@ -463,10 +467,12 @@ describe('StorageInMemory', () => { { id: 'function1', namespace: 'namespace1', + version: 'latest', }, { id: 'function2', namespace: 'namespace1', + version: 'latest', }, ], page: 1, @@ -481,6 +487,7 @@ describe('StorageInMemory', () => { { id: 'function1', namespace: 'namespace1', + version: 'latest', }, ], page: 1, diff --git a/test/unit/http/routers/FunctionsRouter.test.js b/test/unit/http/routers/FunctionsRouter.test.js index 3affa87..d46995a 100644 --- a/test/unit/http/routers/FunctionsRouter.test.js +++ b/test/unit/http/routers/FunctionsRouter.test.js @@ -8,7 +8,6 @@ const expect = chai.expect; const routes = require('../../../../lib/http/routes'); const FakeStorage = require('../../../fakes/FakeStorage'); - describe('GET /functions', () => { before(() => { routes.set('memoryStorage', new FakeStorage()); @@ -79,6 +78,7 @@ describe('PUT /functions/:namespace/:id', () => { expect(memoryStorage.lastPutCode).to.be.eql({ id: 'correct', hash: 'c177063dc3780c2fe9b4fdc913650e8147c9b8b0', + version: 'latest', code, }); }) @@ -86,6 +86,7 @@ describe('PUT /functions/:namespace/:id', () => { id: 'correct', code: 'function main() {}', hash: 'c177063dc3780c2fe9b4fdc913650e8147c9b8b0', + version: 'latest', }, done); }); }); @@ -273,7 +274,7 @@ describe('PUT /functions/:namespace/:id/run', () => { .put('/functions/backstage/not-found/run') .send({ args: [] }) .expect(404, { - error: 'Code \'backstage/not-found\' is not found', + error: 'Code \'backstage/not-found\' was not found', }, done); }); }); @@ -313,7 +314,7 @@ describe('PUT /functions/pipeline', () => { request(routes) .put('/functions/pipeline') .expect(400, { - error: 'Pass step by querystring is required', + error: 'Step query param is required', }, done); }); }); @@ -323,7 +324,7 @@ describe('PUT /functions/pipeline', () => { request(routes) .put('/functions/pipeline?steps[0]=backstage/not-found') .expect(404, { - error: 'Code \'backstage/not-found\' is not found', + error: 'Code \'backstage/not-found\' was not found', }, done); }); }); @@ -355,6 +356,7 @@ describe('PUT /functions/:namespace/:id/env/:env', () => { expect(memoryStorage.lastEnvSet).to.be.eql({ namespace: 'backstage', id: 'correct', + version: 'latest', env: 'MY_VAR', value: 'MY VALUE', }); diff --git a/test/unit/http/routers/FunctionsV2Router.test.js b/test/unit/http/routers/FunctionsV2Router.test.js new file mode 100644 index 0000000..4237a55 --- /dev/null +++ b/test/unit/http/routers/FunctionsV2Router.test.js @@ -0,0 +1,428 @@ +const request = require('supertest'); +const chai = require('chai'); +chai.use(require('chai-string')); + +const Sandbox = require('@globocom/backstage-functions-sandbox'); + +const expect = chai.expect; +const routes = require('../../../../lib/http/routes'); +const FakeStorage = require('../../../fakes/FakeStorageV2'); + +describe('GET /v2/functions', () => { + before(() => { + routes.set('memoryStorage', new FakeStorage()); + }); + + it('should return namespaces with their functions', (done) => { + request(routes) + .get('/v2/functions') + .expect((res) => { + expect(res.body.items[0].namespace).to.be.eql('namespace1'); + expect(res.body.items[0].id).to.be.eql('function'); + expect(res.body.items[1].namespace).to.be.eql('namespace2'); + expect(res.body.items[1].id).to.be.eql('function'); + expect(res.body.items[2].namespace).to.be.eql('namespace3'); + expect(res.body.items[2].id).to.be.eql('function'); + expect(res.profile).to.endsWith('/_schemas/functions/list'); + }) + .expect(200, done); + }); + + it('search by namespace', (done) => { + request(routes) + .get('/v2/functions?namespace=namespace1') + .expect('Content-Type', /json/) + .expect(200, { + items: [ + { namespace: 'namespace1', id: 'function1', version: '0.0.1' }, + { namespace: 'namespace1', id: 'function2', version: '0.0.1' }, + ], + }, done); + }); + + it('search by namespace and function id', (done) => { + request(routes) + .get('/v2/functions?namespace=namespace1&id=function1') + .expect('Content-Type', /json/) + .expect(200, { + items: [ + { namespace: 'namespace1', id: 'function1', version: '0.0.1' }, + { namespace: 'namespace1', id: 'function1', version: '0.0.2' }, + ], + }, done); + }); + + it('should fail the request with 500 error', (done) => { + request(routes) + .get('/v2/functions?namespace=error') + .expect(500, { + error: 'Storage error', + }, done); + }); +}); + +describe('PUT /v2/functions/:namespace/:id/:version', () => { + before(() => { + routes.set('memoryStorage', new FakeStorage()); + }); + + describe('when code is correct', () => { + it('should return the code', (done) => { + const code = 'function main() {}'; + + request(routes) + .put('/v2/functions/backstage/correct/0.0.1') + .send({ code }) + .expect('Content-Type', /json/) + .expect(() => { + const memoryStorage = routes.get('memoryStorage'); + expect(memoryStorage.lastPutCode).to.be.eql({ + id: 'correct', + hash: 'c177063dc3780c2fe9b4fdc913650e8147c9b8b0', + version: '0.0.1', + code, + }); + }) + .expect(200, { + id: 'correct', + code: 'function main() {}', + hash: 'c177063dc3780c2fe9b4fdc913650e8147c9b8b0', + version: '0.0.1', + }, done); + }); + }); + + describe('when return any error from storage', () => { + it('should return the code', (done) => { + const code = 'function main() {}'; + + request(routes) + .put('/v2/functions/backstage/error/0.0.1') + .send({ code }) + .expect('Content-Type', /json/) + .expect(500, { + error: 'Storage error', + }, done); + }); + }); + + describe('when code has a syntax error', () => { + it('should return a error', (done) => { + request(routes) + .put('/v2/functions/backstage/invalid/0.0.1') + .send({ code: '{)' }) + .expect('Content-Type', /application\/json/) + .expect((res) => { + expect(res.body.error).to.deep.include('Unexpected token'); + expect(res.body.stack).to.be.equal(''); + }) + .expect(400, done); + }); + }); + + describe('when code has a logic error', () => { + it('should return a error', (done) => { + const code = `let a = {}; + function c() { + a.b(); + }; + c()`; + + request(routes) + .put('/v2/functions/codes/crazy/0.0.1') + .send({ code }) + .expect('Content-Type', /json/) + .expect(400, { + error: 'TypeError: a.b is not a function', + stack: 'at c (codes/crazy/0.0.1.js:3)\nat codes/crazy/0.0.1.js:5\nat codes/crazy/0.0.1.js:6', + }, done); + }); + }); + + describe('when code has a timeout error', () => { + it('should return a error', (done) => { + const code = 'while(1) {};'; + + request(routes) + .put('/v2/functions/codes/timeout/0.0.1') + .send({ code }) + .expect('Content-Type', /json/) + .expect((res) => { + expect(res.body.error).to.include('Error: Script execution timed out'); + }) + .expect(400, done); + }); + }); + + describe('when code is not a string', () => { + it('should return a error', (done) => { + const code = { wrong: 'yes' }; + + request(routes) + .put('/v2/functions/codes/invalid/0.0.1') + .send({ code }) + .expect('Content-Type', /^application\/json/) + .expect(400, { + error: 'Invalid instance', + details: [ + 'instance.code is not of a type(s) string', + ], + }, done); + }); + }); +}); + + +describe('GET /v2/functions/:namespace/:id/:version', () => { + before(() => { + routes.set('memoryStorage', new FakeStorage()); + }); + + describe('when code is not found', () => { + it('should return 404 error', (done) => { + request(routes) + .get('/v2/functions/backstage/not-found/0.0.1') + .expect((res) => { + expect(res.body.error).to.be.eql('Code not found'); + }) + .expect(404, done); + }); + }); + + describe('when code is found', () => { + it('should return the code', (done) => { + request(routes) + .get('/v2/functions/backstage/found/0.0.1') + .expect('ETag', 'my-hash-123') + .expect((res) => { + expect(res.body.hash).to.be.eql('my-hash-123'); + }) + .expect(200, done); + }); + }); +}); + + +describe('DELETE /v2/functions/:namespace/:id/:version', () => { + before(() => { + routes.set('memoryStorage', new FakeStorage()); + }); + + describe('when delete is not sucessfully', () => { + it('should return 500 error', (done) => { + request(routes) + .delete('/v2/functions/backstage/error/0.0.1') + .expect((res) => { + expect(res.body.error).to.be.eql('Storage error'); + }) + .expect(500, done); + }); + }); + + describe('when delete is successfully', () => { + it('should return 204', (done) => { + request(routes) + .delete('/v2/functions/backstage/found/0.0.1') + .expect(204, done); + }); + }); +}); + + +describe('PUT /v2/functions/:namespace/:id/:version/run', () => { + before(() => { + routes.set('memoryStorage', new FakeStorage()); + routes.set('sandbox', new Sandbox({})); + }); + + describe('when code is found in cache', () => { + it('should reuse compiled code from storage cache', (done) => { + request(routes) + .put('/v2/functions/backstage/cached/0.0.1/run') + .send({ args: [1, 2] }) + .expect(200, { + result: 'cached', + body: { args: [1, 2] }, + }, done); + }); + }); + + describe('when code is found in cache', () => { + it('should reuse compiled code from storage cache', (done) => { + request(routes) + .put('/v2/functions/backstage/send-string/0.0.1/run') + .send({ args: [1, 2] }) + .expect('content-type', /json/) + .expect(200, '"this is an alert"', done); + }); + }); + + describe('when code is not found in cache', () => { + it('should compile code from storage', (done) => { + request(routes) + .put('/v2/functions/backstage/fresh/0.0.1/run') + .send({ args: [3, 4] }) + .expect(200, { + result: 'fresh', + body: { args: [3, 4] }, + env: { MY_FOO: 'bar' }, + }, done); + }); + }); + describe('when code is not found in storage', () => { + it('should return a 404 error', (done) => { + request(routes) + .put('/v2/functions/backstage/not-found/0.0.1/run') + .send({ args: [] }) + .expect(404, { + error: 'Code \'backstage/not-found\' was not found', + }, done); + }); + }); + + describe('when error is found', () => { + it('should return a 500 error', (done) => { + request(routes) + .put('/v2/functions/backstage/error/0.0.1/run') + .send({ args: [] }) + .expect(500, { + error: 'Storage error', + }, done); + }); + }); + + describe('when error with custom status code is found', () => { + it('should return a custom status code', (done) => { + request(routes) + .put('/v2/functions/backstage/customError/0.0.1/run') + .send({ args: [] }) + .expect(422, { + error: 'Custom error', + }, done); + }); + }); +}); + + +describe('PUT /v2/functions/pipeline', () => { + before(() => { + routes.set('memoryStorage', new FakeStorage()); + routes.set('sandbox', new Sandbox({})); + }); + + describe('when there are no steps', () => { + it('should return a bad request', (done) => { + request(routes) + .put('/v2/functions/pipeline') + .expect(400, { + error: 'Step query param is required', + }, done); + }); + }); + + describe('when step does not exists', () => { + it('should return a not found request', (done) => { + request(routes) + .put('/v2/functions/pipeline?steps[0]=backstage/not-found/0.1.0') + .expect(404, { + error: 'Code \'backstage/not-found/0.1.0\' was not found', + }, done); + }); + }); + + describe('when step use two steps', () => { + it('should return a result', (done) => { + request(routes) + .put('/v2/functions/pipeline?steps[0]=backstage/step1/latest&steps[1]=backstage/step2/0.1.0') + .send({ x: 1 }) + .expect(200, { x: 200 }, done); + }); + }); +}); + + +describe('PUT /v2/functions/:namespace/:id/:version/env/:env', () => { + before(() => { + routes.set('memoryStorage', new FakeStorage()); + }); + + describe('when pass a json string on body', () => { + it('should create an enviroment variable', (done) => { + request(routes) + .put('/v2/functions/backstage/correct/0.0.1/env/MY_VAR') + .set('content-type', 'application/json') + .send('"MY VALUE"') + .expect(() => { + const memoryStorage = routes.get('memoryStorage'); + expect(memoryStorage.lastEnvSet).to.be.eql({ + namespace: 'backstage', + id: 'correct', + version: '0.0.1', + env: 'MY_VAR', + value: 'MY VALUE', + }); + }) + .expect(204, done); + }); + }); + + describe('when the target function it\'s not exist', () => { + it('should fail the request with 404 error', (done) => { + request(routes) + .put('/v2/functions/backstage/not-found/0.0.1/env/MY_VAR') + .set('content-type', 'application/json') + .send('"MY VALUE"') + .expect(404, { + error: 'Function not found', + }, done); + }); + }); + + describe('when not pass a json string on body', () => { + it('should validate', (done) => { + request(routes) + .put('/v2/functions/backstage/correct/0.0.1/env/MY_VAR') + .send('wrong string') + .expect('Content-Type', /json/) + .expect(400, { + error: 'Invalid instance', + details: [ + 'instance is not of a type(s) string', + ], + }, done); + }); + }); +}); + +describe('DELETE /v2/functions/:namespace/:id/:version/env/:env', () => { + before(() => { + routes.set('memoryStorage', new FakeStorage()); + }); + + describe('when sucessfully', () => { + it('should delete an enviroment variable', (done) => { + request(routes) + .delete('/v2/functions/backstage/correct/0.0.1/env/MY_VAR') + .expect(() => { + const memoryStorage = routes.get('memoryStorage'); + expect(memoryStorage.lastEnvUnset).to.be.eql({ + namespace: 'backstage', + id: 'correct', + env: 'MY_VAR', + }); + }) + .expect(204, {}, done); + }); + }); + + describe('when the target function it\'s not exist', () => { + it('should fail the request with 404 error', (done) => { + request(routes) + .delete('/v2/functions/backstage/not-found/0.0.1/env/MY_VAR') + .expect(404, { + error: 'Function not found', + }, done); + }); + }); +}); +