diff --git a/.travis.yml b/.travis.yml index 1409c12..cd584de 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,7 +5,5 @@ node_js: - "8" - "7" - "6" - - "5" - - "4" after_success: - npm run codecov diff --git a/docs/appenders.md b/docs/appenders.md index 50ff252..c511ca4 100644 --- a/docs/appenders.md +++ b/docs/appenders.md @@ -30,10 +30,12 @@ The following appenders are included with log4js. Some require extra dependencie * [recording](recording.md) * [stderr](stderr.md) * [stdout](stdout.md) +* [tcp](tcp.md) +* [tcp-server](tcp-server.md) ## Optional Appenders -The following appenders are supported by log4js, but will issue deprecation warnings from version 2.6 onwards - they will be removed from the log4js core in version 3. If you are using these appenders, you should alter your dependencies to include them explicitly. +The following appenders are supported by log4js, but are no longer distributed with log4js core from version 3 onwards. * [gelf](https://github.com/log4js-node/gelf) * [hipchat](https://github.com/log4js-node/hipchat) @@ -50,8 +52,6 @@ The following appenders are supported by log4js, but will issue deprecation warn For example, if you were previously using the gelf appender (`type: 'gelf'`) then you should add `@log4js-node/gelf` to your dependencies and change the type to `type: '@log4js-node/gelf'`. -To turn off the deprecation warnings, add `deprecationWarnings: false` to your log4js config. The core version of the appender will still work. But note that you will have to install the external appenders when version 3 is released as they will not be included at all. - ## Other Appenders Log4js can load appenders from outside the core appenders. The `type` config value is used as a require path if no matching appender can be found. For example, the following configuration will attempt to load an appender from the module 'cheese/appender', passing the rest of the config for the appender to that module: diff --git a/docs/clustering.md b/docs/clustering.md new file mode 100644 index 0000000..31cb9e7 --- /dev/null +++ b/docs/clustering.md @@ -0,0 +1,28 @@ +# Clustering / Multi-process Logging + +If you're running log4js in an application that uses [node's core cluster](https://nodejs.org/dist/latest-v8.x/docs/api/cluster.html) then log4js will transparently handle making sure the processes don't try to log at the same time. All logging is done on the master process, with the worker processes sending their log messages to the master via `process.send`. This ensures that you don't get multiple processes trying to write to the same file (or rotate the log files) at the same time. + +This can cause problems in some rare circumstances, if you're experiencing weird logging problems, then use the `disableClustering: true` option in your log4js configuration to have every process behave as if it were the master process. Be careful if you're logging to files. + +## I'm using PM2, but I'm not getting any logs! +To get log4js working with [PM2](http://pm2.keymetrics.io), you'll need to install the [pm2-intercom](https://www.npmjs.com/package/pm2-intercom) module. +```bash +pm2 install pm2-intercom +``` +Then add the value `pm2: true` to your log4js configuration. If you're also using `node-config`, then you'll probably have renamed your `NODE_APP_INSTANCE` environment variable. If so, you'll also need to add `pm2InstanceVar: ''` where `` should be replaced with the new name you gave the instance environment variable. +```javascript +log4js.configure({ + appenders: { out: { type: 'stdout'}}, + categories: { default: { appenders: ['out'], level: 'info'}}, + pm2: true, + pm2InstanceVar: 'INSTANCE_ID' +}); +``` + +## I'm using Passenger, but I'm not getting any logs! + +[Passenger](https://www.phusionpassenger.com/library/) replaces the node.js core cluster module with a non-functional stub, so you won't see any output using log4js. To fix this, add `disableClustering: true` to your configuration. Again, be careful if you're logging to files. + +## I'm not using clustering/pm2/passenger but I do have multiple processes that I'd like to all log to the same place + +Ok, you probably want to look at the [tcp-server](tcp-server.md) and [tcp appender](tcp.md) documentation. diff --git a/docs/faq.md b/docs/faq.md index 2e33348..a7348dc 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -35,23 +35,9 @@ const logger = log4js.getLogger('console'); console.log = logger.info.bind(logger); // do the same for others - console.debug, etc. ``` -## I'm using PM2, but I'm not getting any logs! -To get log4js working with PM2, you'll need to install the [pm2-intercom](https://www.npmjs.com/package/pm2-intercom) module. -```bash -pm2 install pm2-intercom -``` -Then add the value `pm2: true` to your log4js configuration. If you're also using `node-config`, then you'll probably have renamed your `NODE_APP_INSTANCE` environment variable. If so, you'll also need to add `pm2InstanceVar: ''` where `` should be replaced with the new name you gave the instance environment variable. -```javascript -log4js.configure({ - appenders: { out: { type: 'stdout'}}, - categories: { default: { appenders: ['out'], level: 'info'}}, - pm2: true, - pm2InstanceVar: 'INSTANCE_ID' -}); -``` +## I'm using pm2/passenger/some other third thing and I'm not getting any logs! -## FFS, why did you mess with the PM2 stuff? It was working fine for me! -You can turn off the clustering support, with the `disableClustering: true` option in your config. This will make log4js behave more like it did before version 2.x. Each worker process will log its own output, instead of sending it all to the master process. Be careful if you're logging to files though, this could result in weird behaviour. +Take a look at the [clustering](clustering.md) docs, they should help you out. ## NPM complains about nodemailer being deprecated, what should I do? diff --git a/docs/index.md b/docs/index.md index b133b93..99e6f58 100644 --- a/docs/index.md +++ b/docs/index.md @@ -14,9 +14,9 @@ There have been a few changes between log4js 1.x and 2.x (and 0.x too). You shou * [SMTP appender](https://github.com/log4js-node/smtp) * [GELF appender](https://github.com/log4js-node/gelf) * [Loggly appender](https://github.com/log4js-node/loggly) -* [Logstash UDP appender](logstashUDP.md) +* [Logstash UDP appender](https://github.com/log4js-node/logstashUDP) * logFaces ([UDP](logFaces-UDP.md) and [HTTP](logFaces-HTTP.md)) appender -* [multiprocess appender](multiprocess.md) (useful when you've got multiple servers but want to centralise logging) +* [TCP appender](tcp.md) (useful when you've got multiple servers but want to centralise logging) * a [logger for connect/express](connect-logger.md) servers * configurable log message [layout/patterns](layouts.md) * different log levels for different log categories (make some parts of your app log as DEBUG, others only ERRORS, etc.) @@ -38,6 +38,9 @@ logger.level = 'debug'; // default level is OFF - which means no logs at all. logger.debug("Some debug messages"); ``` +## Clustering +If you use node's cluster, or passenger, or pm2, then you should read this [clustering guide](clustering.md) + ## Note for library makers If you're writing a library and would like to include support for log4js, without introducing a dependency headache for your users, take a look at [log4js-api](https://github.com/log4js-node/log4js-api). diff --git a/docs/multiprocess.md b/docs/multiprocess.md index 821361d..a3f31e8 100644 --- a/docs/multiprocess.md +++ b/docs/multiprocess.md @@ -1,8 +1,11 @@ # Multiprocess Appender +*You probably want to use the [tcp server](tcp-server.md) or [tcp appender](tcp.md) instead of this - they are more flexible* + +*Note that if you're just using node core's `cluster` module then you don't need to use this appender - log4js will handle logging within the cluster transparently.* + The multiprocess appender sends log events to a master server over TCP sockets. It can be used as a simple way to centralise logging when you have multiple servers or processes. It uses the node.js core networking modules, and so does not require any extra dependencies. Remember to call `log4js.shutdown` when your application terminates, so that the sockets get closed cleanly. -Note that if you're just using node core's `cluster` module then you don't need to use this appender - log4js will handle logging within the cluster transparently. ## Configuration diff --git a/docs/slack.md b/docs/slack.md deleted file mode 100644 index f1cfbf4..0000000 --- a/docs/slack.md +++ /dev/null @@ -1,31 +0,0 @@ -# Slack Appender - -Sends log events to a [slack](https://slack.com) channel. To use this appender you will need to include [slack-node](https://www.npmjs.com/package/slack-node) in your application's dependencies. - -## Configuration - -* `type` - `slack` -* `token` - `string` - your Slack API token (see the slack and slack-node docs) -* `channel_id` - `string` - the channel to send log messages -* `icon_url` - `string` (optional) - the icon to use for the message -* `username` - `string` - the username to display with the message -* `layout` - `object` (optional, defaults to `basicLayout`) - the layout to use for the message (see [layouts](layouts.md)). - -## Example - -```javascript -log4js.configure({ - appenders: { - alerts: { - type: 'slack', - token: 'abc123def', - channel_id: 'prod-alerts', - username: 'our_application' - } - }, - categories: { - default: { appenders: ['alerts'], level: 'error' } - } -}); -``` -This configuration will send all error (and above) messages to the `prod-alerts` slack channel, with the username `our_application`. diff --git a/docs/tcp-server.md b/docs/tcp-server.md new file mode 100644 index 0000000..62a90d9 --- /dev/null +++ b/docs/tcp-server.md @@ -0,0 +1,23 @@ +# TCP Server Appender + +Strictly speaking, this is not an appender - but it is configured as one. The TCP server listens for log messages on a port, taking JSON-encoded log events and then forwarding them to the other appenders. It can be used as a simple way to centralise logging when you have multiple servers or processes. It uses the node.js core networking modules, and so does not require any extra dependencies. Remember to call `log4js.shutdown` when your application terminates, so that the sockets get closed cleanly. It is designed to work with the [tcp appender](tcp.md), but could work with anything that sends correctly formatted JSON log events. + +## Configuration + +* `type` - `tcp-server` +* `port` - `integer` (optional, defaults to `5000`) - the port to listen on +* `host` - `string` (optional, defaults to `localhost`) - the host/IP address to listen on + +## Example (master) +```javascript +log4js.configure({ + appenders: { + file: { type: 'file', filename: 'all-the-logs.log' }, + server: { type: 'tcp-server', host: '0.0.0.0' } + }, + categories: { + default: { appenders: ['file'], level: 'info' } + } +}); +``` +This creates a log server listening on port 5000, on all IP addresses the host has assigned to it. Note that the appender is not included in the appenders listed for the categories. All events received on the socket will be forwarded to the other appenders, as if they had originated on the same server. diff --git a/docs/tcp.md b/docs/tcp.md new file mode 100644 index 0000000..95438b8 --- /dev/null +++ b/docs/tcp.md @@ -0,0 +1,22 @@ +# TCP Appender + +The TCP appender sends log events to a master server over TCP sockets. It can be used as a simple way to centralise logging when you have multiple servers or processes. It uses the node.js core networking modules, and so does not require any extra dependencies. Remember to call `log4js.shutdown` when your application terminates, so that the sockets get closed cleanly. It's designed to work with the [tcp-server](tcp-server.md), but it doesn't necessarily have to, just make sure whatever is listening at the other end is expecting JSON objects as strings. + +## Configuration + +* `type` - `tcp` +* `port` - `integer` (optional, defaults to `5000`) - the port to send to +* `host` - `string` (optional, defaults to `localhost`) - the host/IP address to send to + +## Example +```javascript +log4js.configure({ + appenders: { + network: { type: 'tcp', host: 'log.server' } + }, + categories: { + default: { appenders: ['network'], level: 'error' } + } +}); +``` +This will send all error messages to `log.server:5000`. diff --git a/examples/layouts.js b/examples/layouts.js new file mode 100644 index 0000000..0d47444 --- /dev/null +++ b/examples/layouts.js @@ -0,0 +1,13 @@ +const log4js = require('../lib/log4js'); + +log4js.configure({ + appenders: { + out: { type: 'stdout', layout: { type: 'messagePassThrough' } } + }, + categories: { + default: { appenders: ['out'], level: 'info' } + } +}); + +const logger = log4js.getLogger('thing'); +logger.info('This should not have a timestamp'); diff --git a/lib/LoggingEvent.js b/lib/LoggingEvent.js new file mode 100644 index 0000000..0166de7 --- /dev/null +++ b/lib/LoggingEvent.js @@ -0,0 +1,72 @@ +const CircularJSON = require('circular-json'); +const levels = require('./levels'); + +/** + * @name LoggingEvent + * @namespace Log4js + */ +class LoggingEvent { + /** + * Models a logging event. + * @constructor + * @param {String} categoryName name of category + * @param {Log4js.Level} level level of message + * @param {Array} data objects to log + * @author Seth Chisamore + */ + constructor(categoryName, level, data, context) { + this.startTime = new Date(); + this.categoryName = categoryName; + this.data = data; + this.level = level; + this.context = Object.assign({}, context); + this.pid = process.pid; + } + + serialise() { + const logData = this.data.map((e) => { + // JSON.stringify(new Error('test')) returns {}, which is not really useful for us. + // The following allows us to serialize errors correctly. + if (e && e.message && e.stack) { + e = Object.assign({ message: e.message, stack: e.stack }, e); + } + return e; + }); + this.data = logData; + return CircularJSON.stringify(this); + } + + static deserialise(serialised) { + let event; + try { + const rehydratedEvent = CircularJSON.parse(serialised); + rehydratedEvent.data = rehydratedEvent.data.map((e) => { + if (e && e.message && e.stack) { + const fakeError = new Error(e); + Object.keys(e).forEach((key) => { fakeError[key] = e[key]; }); + e = fakeError; + } + return e; + }); + event = new LoggingEvent( + rehydratedEvent.categoryName, + levels.getLevel(rehydratedEvent.level.levelStr), + rehydratedEvent.data, + rehydratedEvent.context + ); + event.startTime = new Date(rehydratedEvent.startTime); + event.pid = rehydratedEvent.pid; + event.cluster = rehydratedEvent.cluster; + } catch (e) { + event = new LoggingEvent( + 'log4js', + levels.ERROR, + ['Unable to parse log:', serialised, 'because: ', e] + ); + } + + return event; + } +} + +module.exports = LoggingEvent; diff --git a/lib/appender-adapter.js b/lib/appenders/adapters.js similarity index 70% rename from lib/appender-adapter.js rename to lib/appenders/adapters.js index c6b3eb1..ff038e8 100644 --- a/lib/appender-adapter.js +++ b/lib/appenders/adapters.js @@ -1,3 +1,5 @@ +'use strict'; + function maxFileSizeUnitTransform(maxLogSize) { if (typeof maxLogSize === 'number' && Number.isInteger(maxLogSize)) { return maxLogSize; @@ -20,30 +22,25 @@ function maxFileSizeUnitTransform(maxLogSize) { } function adapter(configAdapter, config) { + const newConfig = Object.assign({}, config); Object.keys(configAdapter).forEach((key) => { - if (config[key]) { - config[key] = configAdapter[key](config[key]); + if (newConfig[key]) { + newConfig[key] = configAdapter[key](config[key]); } }); + return newConfig; } function fileAppenderAdapter(config) { const configAdapter = { maxLogSize: maxFileSizeUnitTransform }; - adapter(configAdapter, config); + return adapter(configAdapter, config); } -function fileSyncAppenderAdapter(config) { - const configAdapter = { - maxLogSize: maxFileSizeUnitTransform - }; - adapter(configAdapter, config); -} - -const appenderAdapter = { +const adapters = { file: fileAppenderAdapter, - fileSync: fileSyncAppenderAdapter + fileSync: fileAppenderAdapter }; -module.exports = appenderAdapter; +module.exports.modifyConfig = config => (adapters[config.type] ? adapters[config.type](config) : config); diff --git a/lib/appenders/categoryFilter.js b/lib/appenders/categoryFilter.js index 263970b..4ec8327 100644 --- a/lib/appenders/categoryFilter.js +++ b/lib/appenders/categoryFilter.js @@ -1,9 +1,13 @@ 'use strict'; +const debug = require('debug')('log4js:categoryFilter'); + function categoryFilter(excludes, appender) { if (typeof excludes === 'string') excludes = [excludes]; return (logEvent) => { + debug(`Checking ${logEvent.categoryName} against ${excludes}`); if (excludes.indexOf(logEvent.categoryName) === -1) { + debug('Not excluded, sending to appender'); appender(logEvent); } }; diff --git a/lib/appenders/gelf.js b/lib/appenders/gelf.js deleted file mode 100644 index aa4d566..0000000 --- a/lib/appenders/gelf.js +++ /dev/null @@ -1,152 +0,0 @@ -'use strict'; - -/** - * This appender has been deprecated. - * Updates and bug fixes should be made against https://github.com/lgo4js-node/gelf - */ -const zlib = require('zlib'); -// const levels = require('../levels'); -const dgram = require('dgram'); -const util = require('util'); -const OS = require('os'); -const debug = require('debug')('log4js:gelf'); - -/* eslint no-unused-vars:0 */ -const LOG_EMERG = 0; // system is unusable(unused) -const LOG_ALERT = 1; // action must be taken immediately(unused) -const LOG_CRIT = 2; // critical conditions -const LOG_ERROR = 3; // error conditions -const LOG_WARNING = 4; // warning conditions -const LOG_NOTICE = 5; // normal, but significant, condition(unused) -const LOG_INFO = 6; // informational message -const LOG_DEBUG = 7; // debug-level message - -/** - * GELF appender that supports sending UDP packets to a GELF compatible server such as Graylog - * - * @param layout a function that takes a logevent and returns a string (defaults to none). - * @param config.host - host to which to send logs (default:localhost) - * @param config.port - port at which to send logs to (default:12201) - * @param config.hostname - hostname of the current host (default:OS hostname) - * @param config.facility - facility to log to (default:nodejs-server) - */ -/* eslint no-underscore-dangle:0 */ -function gelfAppender(layout, config, levels) { - const levelMapping = {}; - levelMapping[levels.ALL] = LOG_DEBUG; - levelMapping[levels.TRACE] = LOG_DEBUG; - levelMapping[levels.DEBUG] = LOG_DEBUG; - levelMapping[levels.INFO] = LOG_INFO; - levelMapping[levels.WARN] = LOG_WARNING; - levelMapping[levels.ERROR] = LOG_ERROR; - levelMapping[levels.FATAL] = LOG_CRIT; - - const host = config.host || 'localhost'; - const port = config.port || 12201; - const hostname = config.hostname || OS.hostname(); - const facility = config.facility; - const customFields = config.customFields; - - const defaultCustomFields = customFields || {}; - - if (facility) { - defaultCustomFields._facility = facility; - } - - const client = dgram.createSocket('udp4'); - - process.on('exit', () => { - if (client) client.close(); - }); - - /** - * Add custom fields (start with underscore ) - * - if the first object passed to the logger contains 'GELF' field, - * copy the underscore fields to the message - * @param loggingEvent - * @param msg - */ - function addCustomFields(loggingEvent, msg) { - /* append defaultCustomFields firsts */ - Object.keys(defaultCustomFields).forEach((key) => { - // skip _id field for graylog2, skip keys not starts with UNDERSCORE - if (key.match(/^_/) && key !== '_id') { - msg[key] = defaultCustomFields[key]; - } - }); - - /* append custom fields per message */ - const data = loggingEvent.data; - if (!Array.isArray(data) || data.length === 0) return; - const firstData = data[0]; - if (firstData) { - if (!firstData.GELF) return; // identify with GELF field defined - // Remove the GELF key, some gelf supported logging systems drop the message with it - delete firstData.GELF; - Object.keys(firstData).forEach((key) => { - // skip _id field for graylog2, skip keys not starts with UNDERSCORE - if (key.match(/^_/) || key !== '_id') { - msg[key] = firstData[key]; - } - }); - - /* the custom field object should be removed, so it will not be looged by the later appenders */ - loggingEvent.data.shift(); - } - } - - function preparePacket(loggingEvent) { - const msg = {}; - addCustomFields(loggingEvent, msg); - msg.short_message = layout(loggingEvent); - - msg.version = '1.1'; - msg.timestamp = msg.timestamp || new Date().getTime() / 1000; // log should use millisecond - msg.host = hostname; - msg.level = levelMapping[loggingEvent.level || levels.DEBUG]; - return msg; - } - - function sendPacket(packet) { - client.send(packet, 0, packet.length, port, host, (err) => { - if (err) { - console.error(err); - } - }); - } - - const app = (loggingEvent) => { - const message = preparePacket(loggingEvent); - zlib.gzip(Buffer.from(JSON.stringify(message)), (err, packet) => { - if (err) { - console.error(err.stack); - } else { - if (packet.length > 8192) { // eslint-disable-line - debug(`Message packet length (${packet.length}) is larger than 8k. Not sending`); - } else { - sendPacket(packet); - } - } - }); - }; - app.shutdown = function (cb) { - if (client) { - client.close(cb); - } - }; - - // trigger a deprecation warning, with a pointer to the replacement lib - app.deprecated = '@log4js-node/gelf'; - - return app; -} - -function configure(config, layouts, findAppender, levels) { - let layout = layouts.messagePassThroughLayout; - if (config.layout) { - layout = layouts.layout(config.layout.type, config.layout); - } - return gelfAppender(layout, config, levels); -} - -module.exports.configure = configure; diff --git a/lib/appenders/hipchat.js b/lib/appenders/hipchat.js deleted file mode 100644 index 6e60790..0000000 --- a/lib/appenders/hipchat.js +++ /dev/null @@ -1,87 +0,0 @@ -'use strict'; - -const hipchat = require('hipchat-notifier'); - -/** - @invoke as - - log4js.configure({ - 'appenders': { 'hipchat': - { - 'type' : 'hipchat', - 'hipchat_token': '< User token with Notification Privileges >', - 'hipchat_room': '< Room ID or Name >', - // optionl - 'hipchat_from': '[ additional from label ]', - 'hipchat_notify': '[ notify boolean to bug people ]', - 'hipchat_host' : 'api.hipchat.com' - } - }, - categories: { default: { appenders: ['hipchat'], level: 'debug' }} - }); - - var logger = log4js.getLogger('hipchat'); - logger.warn('Test Warn message'); - - @invoke - */ - -function hipchatNotifierResponseCallback(err) { - if (err) { - throw err; - } -} - -function hipchatAppender(config, layout) { - const notifier = hipchat.make(config.hipchat_room, config.hipchat_token); - - return (loggingEvent) => { - let notifierFn; - - notifier.setRoom(config.hipchat_room); - notifier.setFrom(config.hipchat_from || ''); - notifier.setNotify(config.hipchat_notify || false); - - if (config.hipchat_host) { - notifier.setHost(config.hipchat_host); - } - - switch (loggingEvent.level.toString()) { - case 'TRACE': - case 'DEBUG': - notifierFn = 'info'; - break; - case 'WARN': - notifierFn = 'warning'; - break; - case 'ERROR': - case 'FATAL': - notifierFn = 'failure'; - break; - default: - notifierFn = 'success'; - } - - // @TODO, re-work in timezoneOffset ? - const layoutMessage = layout(loggingEvent); - - // dispatch hipchat api request, do not return anything - // [overide hipchatNotifierResponseCallback] - notifier[notifierFn](layoutMessage, config.hipchat_response_callback || - hipchatNotifierResponseCallback); - }; -} - -function hipchatConfigure(config, layouts) { - let layout = layouts.messagePassThroughLayout; - - if (config.layout) { - layout = layouts.layout(config.layout.type, config.layout); - } - - const appender = hipchatAppender(config, layout); - appender.deprecated = '@log4js-node/hipchat'; - return appender; -} - -module.exports.configure = hipchatConfigure; diff --git a/lib/appenders/index.js b/lib/appenders/index.js new file mode 100644 index 0000000..d7408b8 --- /dev/null +++ b/lib/appenders/index.js @@ -0,0 +1,103 @@ +const path = require('path'); +const debug = require('debug')('log4js:appenders'); +const configuration = require('../configuration'); +const clustering = require('../clustering'); +const levels = require('../levels'); +const layouts = require('../layouts'); +const adapters = require('./adapters'); + +// pre-load the core appenders so that webpack can find them +const coreAppenders = new Map(); +coreAppenders.set('console', require('./console')); +coreAppenders.set('stdout', require('./stdout')); +coreAppenders.set('stderr', require('./stderr')); +coreAppenders.set('file', require('./file')); +coreAppenders.set('dateFile', require('./dateFile')); + +const appenders = new Map(); + +const tryLoading = (modulePath, config) => { + debug('Loading module from ', modulePath); + try { + return require(modulePath); //eslint-disable-line + } catch (e) { + // if the module was found, and we still got an error, then raise it + configuration.throwExceptionIf( + config, + e.code !== 'MODULE_NOT_FOUND', + `appender "${modulePath}" could not be loaded (error was: ${e})` + ); + return undefined; + } +}; + +const loadAppenderModule = (type, config) => coreAppenders.get(type) || + tryLoading(`./${type}`, config) || + tryLoading(type, config) || + tryLoading(path.join(path.dirname(require.main.filename), type), config) || + tryLoading(path.join(process.cwd(), type), config); + +const createAppender = (name, config) => { + const appenderConfig = config.appenders[name]; + const appenderModule = loadAppenderModule(appenderConfig.type, config); + configuration.throwExceptionIf( + config, + configuration.not(appenderModule), + `appender "${name}" is not valid (type "${appenderConfig.type}" could not be found)` + ); + if (appenderModule.appender) { + debug(`DEPRECATION: Appender ${appenderConfig.type} exports an appender function.`); + } + if (appenderModule.shutdown) { + debug(`DEPRECATION: Appender ${appenderConfig.type} exports a shutdown function.`); + } + + debug(`${name}: clustering.isMaster ? ${clustering.isMaster()}`); + debug(`${name}: appenderModule is ${require('util').inspect(appenderModule)}`); // eslint-disable-line + return clustering.onlyOnMaster(() => { + debug(`calling appenderModule.configure for ${name} / ${appenderConfig.type}`); + return appenderModule.configure( + adapters.modifyConfig(appenderConfig), + layouts, + appender => appenders.get(appender), + levels + ); + }, () => {}); +}; + +const setup = (config) => { + appenders.clear(); + + Object.keys(config.appenders).forEach((name) => { + debug(`Creating appender ${name}`); + appenders.set(name, createAppender(name, config)); + }); +}; + +setup({ appenders: { out: { type: 'stdout' } } }); + +configuration.addListener((config) => { + configuration.throwExceptionIf( + config, + configuration.not(configuration.anObject(config.appenders)), + 'must have a property "appenders" of type object.' + ); + const appenderNames = Object.keys(config.appenders); + configuration.throwExceptionIf( + config, + configuration.not(appenderNames.length), + 'must define at least one appender.' + ); + + appenderNames.forEach((name) => { + configuration.throwExceptionIf( + config, + configuration.not(config.appenders[name].type), + `appender "${name}" is not valid (must be an object with property "type")` + ); + }); +}); + +configuration.addListener(setup); + +module.exports = appenders; diff --git a/lib/appenders/logFaces-HTTP.js b/lib/appenders/logFaces-HTTP.js deleted file mode 100644 index 397a02c..0000000 --- a/lib/appenders/logFaces-HTTP.js +++ /dev/null @@ -1,94 +0,0 @@ -/** - * This appender is deprecated. All bugfixes and improvements should be made in - * https://github.com/log4js-node/logFaces-HTTP - * - * logFaces appender sends JSON formatted log events to logFaces receivers. - * There are two types of receivers supported - raw UDP sockets (for server side apps), - * and HTTP (for client side apps). Depending on the usage, this appender - * requires either of the two: - * - * For UDP require 'dgram', see 'https://nodejs.org/api/dgram.html' - * For HTTP require 'axios', see 'https://www.npmjs.com/package/axios' - * - * Make sure your project have relevant dependancy installed before using this appender. - */ -/* eslint global-require:0 */ - -'use strict'; - -const util = require('util'); -const axios = require('axios'); - -/** - * - * For HTTP (browsers or node.js) use the following configuration params: - * { - * "type": "logFaces-HTTP", // must be present for instantiation - * "application": "LFS-TEST", // name of the application (domain) - * "url": "http://lfs-server/logs", // logFaces receiver servlet URL - * } - */ -function logFacesAppender(config) { - const sender = axios.create({ - baseURL: config.url, - timeout: config.timeout || 5000, - headers: { 'Content-Type': 'application/json' }, - withCredentials: true - }); - - const appender = function log(event) { - // convert to logFaces compact json format - const lfsEvent = { - a: config.application || '', // application name - t: event.startTime.getTime(), // time stamp - p: event.level.levelStr, // level (priority) - g: event.categoryName, // logger name - m: format(event.data) // message text - }; - - // add context variables if exist - Object.keys(event.context).forEach((key) => { - lfsEvent[`p_${key}`] = event.context[key]; - }); - - // send to server - sender.post('', lfsEvent) - .catch((error) => { - if (error.response) { - console.error(`log4js.logFaces-HTTP Appender error posting to ${config.url}: ${error.response.status} - ${error.response.data}`); - return; - } - console.error(`log4js.logFaces-HTTP Appender error: ${error.message}`); - }); - }; - - appender.deprecated = '@log4js-node/logfaces-http'; - return appender; -} - -function configure(config) { - return logFacesAppender(config); -} - -function format(logData) { - const data = Array.isArray(logData) - ? logData - : Array.prototype.slice.call(arguments); - return util.format.apply(util, wrapErrorsWithInspect(data)); -} - -function wrapErrorsWithInspect(items) { - return items.map((item) => { - if ((item instanceof Error) && item.stack) { - return { - inspect: function () { - return `${util.format(item)}\n${item.stack}`; - } - }; - } - - return item; - }); -} - -module.exports.configure = configure; diff --git a/lib/appenders/logFaces-UDP.js b/lib/appenders/logFaces-UDP.js deleted file mode 100644 index edafe2e..0000000 --- a/lib/appenders/logFaces-UDP.js +++ /dev/null @@ -1,97 +0,0 @@ -/** - * This appender has been deprecated. Any bug fixes or improvements should be - * made against https://github.com/log4js-node/logFaces-UDP - * - * logFaces appender sends JSON formatted log events to logFaces receivers. - * There are two types of receivers supported - raw UDP sockets (for server side apps), - * and HTTP (for client side apps). Depending on the usage, this appender - * requires either of the two: - * - * For UDP require 'dgram', see 'https://nodejs.org/api/dgram.html' - * For HTTP require 'axios', see 'https://www.npmjs.com/package/axios' - * - * Make sure your project have relevant dependancy installed before using this appender. - */ - -'use strict'; - -const util = require('util'); -const dgram = require('dgram'); - -function datagram(config) { - const sock = dgram.createSocket('udp4'); - const host = config.remoteHost || '127.0.0.1'; - const port = config.port || 55201; - - return function (event) { - const buff = Buffer.from(JSON.stringify(event)); - sock.send(buff, 0, buff.length, port, host, (err) => { - if (err) { - console.error(`log4js.logFacesUDPAppender error sending to ${host}:${port}, error: `, err); - } - }); - }; -} - -/** - * For UDP (node.js) use the following configuration params: - * { - * "type": "logFaces-UDP", // must be present for instantiation - * "application": "LFS-TEST", // name of the application (domain) - * "remoteHost": "127.0.0.1", // logFaces server address (hostname) - * "port": 55201 // UDP receiver listening port - * } - * - */ -function logFacesUDPAppender(config) { - const send = datagram(config); - - const appender = function log(event) { - // convert to logFaces compact json format - const lfsEvent = { - a: config.application || '', // application name - t: event.startTime.getTime(), // time stamp - p: event.level.levelStr, // level (priority) - g: event.categoryName, // logger name - m: format(event.data) // message text - }; - - // add context variables if exist - Object.keys(event.context).forEach((key) => { - lfsEvent[`p_${key}`] = event.context[key]; - }); - - // send to server - send(lfsEvent); - }; - - appender.deprecated = '@log4js-node/logfaces-udp'; - return appender; -} - -function configure(config) { - return logFacesUDPAppender(config); -} - -function wrapErrorsWithInspect(items) { - return items.map((item) => { - if ((item instanceof Error) && item.stack) { - return { - inspect: function () { - return `${util.format(item)}\n${item.stack}`; - } - }; - } - - return item; - }); -} - -function format(logData) { - const data = Array.isArray(logData) - ? logData - : Array.prototype.slice.call(arguments); - return util.format.apply(util, wrapErrorsWithInspect(data)); -} - -module.exports.configure = configure; diff --git a/lib/appenders/loggly.js b/lib/appenders/loggly.js deleted file mode 100644 index 285888d..0000000 --- a/lib/appenders/loggly.js +++ /dev/null @@ -1,127 +0,0 @@ -/* eslint no-prototype-builtins:1,no-restricted-syntax:[1, "ForInStatement"] */ - -'use strict'; - -/** - * This appender has been deprecated. - * Updates and bug fixes should be made against https://github.com/log4js-node/loggly - */ -const debug = require('debug')('log4js:loggly'); -const loggly = require('loggly'); -const os = require('os'); - -function isAnyObject(value) { - return value !== null && (typeof value === 'object' || typeof value === 'function'); -} - -function numKeys(obj) { - return Object.keys(obj).length; -} - -/** - * @param msgListArgs - * @returns Object{ deTaggedMsg: [...], additionalTags: [...] } - */ -function processTags(msgListArgs) { - const msgList = (msgListArgs.length === 1 ? [msgListArgs[0]] : msgListArgs); - - return msgList.reduce((accumulate, element) => { - if (isAnyObject(element) && Array.isArray(element.tags) && numKeys(element) === 1) { - accumulate.additionalTags = accumulate.additionalTags.concat(element.tags); - } else { - accumulate.deTaggedData.push(element); - } - return accumulate; - }, { deTaggedData: [], additionalTags: [] }); -} - -/** - * Loggly Appender. Sends logging events to Loggly using node-loggly, optionally adding tags. - * - * This appender will scan the msg from the logging event, and pull out any argument of the - * shape `{ tags: [] }` so that it's possible to add tags in a normal logging call. - * - * For example: - * - * logger.info({ tags: ['my-tag-1', 'my-tag-2'] }, 'Some message', someObj, ...) - * - * And then this appender will remove the tags param and append it to the config.tags. - * - * @param config object with loggly configuration data - * { - * token: 'your-really-long-input-token', - * subdomain: 'your-subdomain', - * tags: ['loggly-tag1', 'loggly-tag2', .., 'loggly-tagn'] - * } - * @param layout a function that takes a logevent and returns a string (defaults to objectLayout). - */ -function logglyAppender(config, layout) { - const client = loggly.createClient(config); - let openRequests = 0; - let shutdownCB; - - debug('creating appender.'); - - function app(loggingEvent) { - const result = processTags(loggingEvent.data); - const deTaggedData = result.deTaggedData; - const additionalTags = result.additionalTags; - - // Replace the data property with the deTaggedData - loggingEvent.data = deTaggedData; - - const msg = layout(loggingEvent); - - openRequests += 1; - debug('sending log event to loggly'); - client.log( - { - msg: msg, - level: loggingEvent.level.levelStr, - category: loggingEvent.categoryName, - hostname: os.hostname().toString(), - }, - additionalTags, - (error) => { - if (error) { - console.error('log4js.logglyAppender - error occurred: ', error); - } - - debug('log event received by loggly.'); - - openRequests -= 1; - - if (shutdownCB && openRequests === 0) { - shutdownCB(); - - shutdownCB = undefined; - } - } - ); - } - - app.shutdown = function (cb) { - debug('shutdown called'); - if (openRequests === 0) { - cb(); - } else { - shutdownCB = cb; - } - }; - - // trigger a deprecation warning, with a pointer to the replacement lib - app.deprecated = '@log4js-node/loggly'; - - return app; -} - -function configure(config, layouts) { - let layout = layouts.messagePassThroughLayout; - if (config.layout) { - layout = layouts.layout(config.layout.type, config.layout); - } - debug('configuring new appender'); - return logglyAppender(config, layout); -} - -module.exports.configure = configure; diff --git a/lib/appenders/logstashHTTP.js b/lib/appenders/logstashHTTP.js deleted file mode 100644 index b04862c..0000000 --- a/lib/appenders/logstashHTTP.js +++ /dev/null @@ -1,97 +0,0 @@ -/** - * This appender is deprecated, please apply any bugfixes or changes - * to https://github.com/log4js-node/logstash-http - * logstashHTTP appender sends JSON formatted log events to logstashHTTP receivers. - * - * HTTP require 'axios', see 'https://www.npmjs.com/package/axios' - * - * Make sure your project have relevant dependancy installed before using this appender. - */ -/* eslint global-require:0 */ - -'use strict'; - -const util = require('util'); -const axios = require('axios'); - -/** - * - * For HTTP (browsers or node.js) use the following configuration params: - * { - * "type": "logstashHTTP", // must be present for instantiation - * "application": "logstash-test", // name of the application - * "logType": "application", // type of the application - * "logChannel": "test", // channel of the application - * "url": "http://lfs-server/_bulk", // logstash receiver servlet URL - * } - */ -function logstashHTTPAppender(config) { - const sender = axios.create({ - baseURL: config.url, - timeout: config.timeout || 5000, - headers: { 'Content-Type': 'application/x-ndjson' }, - withCredentials: true, - }); - - const appender = function log(event) { - const logstashEvent = [ - { - index: { - _index: config.application, - _type: config.logType, - }, - }, - { - message: format(event.data), // eslint-disable-line - context: event.context, - level: event.level.level / 100, - level_name: event.level.levelStr, - channel: config.logChannel, - datetime: (new Date(event.startTime)).toISOString(), - extra: {}, - }, - ]; - const logstashJSON = `${JSON.stringify(logstashEvent[0])}\n${JSON.stringify(logstashEvent[1])}\n`; - - // send to server - sender.post('', logstashJSON) - .catch((error) => { - if (error.response) { - console.error(`log4js.logstashHTTP Appender error posting to ${config.url}: ${error.response.status} - ${error.response.data}`); - return; - } - console.error(`log4js.logstashHTTP Appender error: ${error.message}`); - }); - }; - - appender.deprecated = '@log4js-node/logstash-http'; - - return appender; -} - -function configure(config) { - return logstashHTTPAppender(config); -} - -function format(logData) { - const data = Array.isArray(logData) - ? logData - : Array.prototype.slice.call(arguments); - return util.format.apply(util, wrapErrorsWithInspect(data)); -} - -function wrapErrorsWithInspect(items) { - return items.map((item) => { - if ((item instanceof Error) && item.stack) { - return { - inspect: function () { - return `${util.format(item)}\n${item.stack}`; - } - }; - } - - return item; - }); -} - -module.exports.configure = configure; diff --git a/lib/appenders/logstashUDP.js b/lib/appenders/logstashUDP.js deleted file mode 100755 index aefbca3..0000000 --- a/lib/appenders/logstashUDP.js +++ /dev/null @@ -1,111 +0,0 @@ -'use strict'; - -const dgram = require('dgram'); -const util = require('util'); - -function sendLog(udp, host, port, logObject) { - const buffer = Buffer.from(JSON.stringify(logObject)); - - /* eslint no-unused-vars:0 */ - udp.send(buffer, 0, buffer.length, port, host, (err, bytes) => { - if (err) { - console.error('log4js.logstashUDP - %s:%p Error: %s', host, port, util.inspect(err)); - } - }); -} - - -function logstashUDP(config, layout) { - const udp = dgram.createSocket('udp4'); - const type = config.logType ? config.logType : config.category; - - if (!config.fields) { - config.fields = {}; - } - - function checkArgs(argsValue, logUnderFields) { - if ((!argsValue) || (argsValue === 'both')) { - return true; - } - - if (logUnderFields && (argsValue === 'fields')) { - return true; - } - - if ((!logUnderFields) && (argsValue === 'direct')) { - return true; - } - - return false; - } - - function log(loggingEvent) { - /* - https://gist.github.com/jordansissel/2996677 - { - 'message' => 'hello world', - '@version' => '1', - '@timestamp' => '2014-04-22T23:03:14.111Z', - 'type' => 'stdin', - 'host' => 'hello.local' - } - @timestamp is the ISO8601 high-precision timestamp for the event. - @version is the version number of this json schema - Every other field is valid and fine. - */ - - const fields = {}; - Object.keys(config.fields).forEach((key) => { - fields[key] = typeof config.fields[key] === 'function' ? config.fields[key](loggingEvent) : config.fields[key]; - }); - - /* eslint no-prototype-builtins:1,no-restricted-syntax:[1, "ForInStatement"] */ - if (loggingEvent.data.length > 1) { - const secondEvData = loggingEvent.data[1]; - if ((secondEvData !== undefined) && (secondEvData !== null)) { - Object.keys(secondEvData).forEach((key) => { - fields[key] = secondEvData[key]; - }); - } - } - fields.level = loggingEvent.level.levelStr; - fields.category = loggingEvent.categoryName; - - const logObject = { - '@version': '1', - '@timestamp': (new Date(loggingEvent.startTime)).toISOString(), - type: type, - message: layout(loggingEvent) - }; - - if (checkArgs(config.args, true)) { - logObject.fields = fields; - } - - if (checkArgs(config.args, false)) { - Object.keys(fields).forEach((key) => { - logObject[key] = fields[key]; - }); - } - - sendLog(udp, config.host, config.port, logObject); - } - - log.shutdown = function (cb) { - udp.close(cb); - }; - - log.deprecated = '@log4js-node/logstashudp'; - return log; -} - -function configure(config, layouts) { - let layout = layouts.dummyLayout; - if (config.layout) { - layout = layouts.layout(config.layout.type, config.layout); - } - - return logstashUDP(config, layout); -} - -module.exports.configure = configure; diff --git a/lib/appenders/mailgun.js b/lib/appenders/mailgun.js deleted file mode 100644 index 5fe2c17..0000000 --- a/lib/appenders/mailgun.js +++ /dev/null @@ -1,44 +0,0 @@ -'use strict'; - -/** - * This appender has been deprecated. - * Updates and bug fixes should be made against https://github.com/log4js-node/mailgun - */ -const mailgunFactory = require('mailgun-js'); - -function mailgunAppender(config, layout) { - const mailgun = mailgunFactory({ - apiKey: config.apikey, - domain: config.domain - }); - - const appender = (loggingEvent) => { - const data = { - from: config.from, - to: config.to, - subject: config.subject, - text: layout(loggingEvent, config.timezoneOffset) - }; - - /* eslint no-unused-vars:0 */ - mailgun.messages().send(data, (error, body) => { - if (error !== null) console.error('log4js.mailgunAppender - Error happened', error); - }); - }; - - // trigger a deprecation warning. - appender.deprecated = '@logj4s-node/mailgun'; - - return appender; -} - -function configure(config, layouts) { - let layout = layouts.basicLayout; - if (config.layout) { - layout = layouts.layout(config.layout.type, config.layout); - } - - return mailgunAppender(config, layout); -} - -module.exports.configure = configure; diff --git a/lib/appenders/rabbitmq.js b/lib/appenders/rabbitmq.js deleted file mode 100644 index e5de5c6..0000000 --- a/lib/appenders/rabbitmq.js +++ /dev/null @@ -1,67 +0,0 @@ -'use strict'; - -/** - * This appender is deprecated and will be removed in version 3.x - * Please make any bug fixes or improvements to https://github.com/log4js-node/rabbitmq - */ -const amqplib = require('amqplib'); - -function rabbitmqAppender(config, layout) { - const host = config.host || '127.0.0.1'; - const port = config.port || 5672; - const username = config.username || 'guest'; - const password = config.password || 'guest'; - const exchange = config.exchange || ''; - const type = config.mq_type || ''; - const durable = config.durable || false; - const routingKey = config.routing_key || 'logstash'; - const con = { - protocol: 'amqp', - hostname: host, - port: port, - username: username, - password: password, - locale: 'en_US', - frameMax: 0, - heartbeat: 0, - vhost: '/', - routing_key: routingKey, - exchange: exchange, - mq_type: type, - durable: durable, - }; - const clientconn = amqplib.connect(con); - clientconn.publish = amqplib.connect(con).publish ? amqplib.connect(con).publish : (client, message) => { - client.then((conn) => { - const rn = conn.createChannel().then((ch) => { - const ok = ch.assertExchange(exchange, type, { durable: durable }); - return ok.then(() => { - ch.publish(exchange, routingKey, Buffer.from(message)); - return ch.close(); - }); - }); - return rn; - }).catch(console.error); - }; - function log(loggingEvent) { - const message = layout(loggingEvent); - clientconn.publish(clientconn, message); - } - log.shutdown = function () { - clientconn.close(); - }; - - log.deprecated = '@log4js-node/rabbitmq'; - return log; -} - -function configure(config, layouts) { - let layout = layouts.messagePassThroughLayout; - if (config.layout) { - layout = layouts.layout(config.layout.type, config.layout); - } - - return rabbitmqAppender(config, layout); -} - -module.exports.configure = configure; diff --git a/lib/appenders/recording.js b/lib/appenders/recording.js index f0fa2f2..13576c0 100644 --- a/lib/appenders/recording.js +++ b/lib/appenders/recording.js @@ -7,6 +7,7 @@ const recordedEvents = []; function configure() { return function (logEvent) { debug(`received logEvent, number of events now ${recordedEvents.length + 1}`); + debug('log event was ', logEvent); recordedEvents.push(logEvent); }; } diff --git a/lib/appenders/redis.js b/lib/appenders/redis.js deleted file mode 100644 index 93f1637..0000000 --- a/lib/appenders/redis.js +++ /dev/null @@ -1,51 +0,0 @@ -'use strict'; - -/** - * This appender has been deprecated, and will be removed in version 3. - * Any bug fixes or improvements should be made against @log4js-node/redis - */ -const redis = require('redis'); -const util = require('util'); - -function redisAppender(config, layout) { - const host = config.host || '127.0.0.1'; - const port = config.port || 6379; - const auth = config.pass ? { auth_pass: config.pass } : {}; - const redisClient = redis.createClient(port, host, auth); - - redisClient.on('error', (err) => { - if (err) { - console.error(`log4js.redisAppender - ${host}:${port} Error: ${util.inspect(err)}`); - } - }); - - const appender = function (loggingEvent) { - const message = layout(loggingEvent); - redisClient.publish(config.channel, message, (err) => { - if (err) { - console.error(`log4js.redisAppender - ${host}:${port} Error: ${util.inspect(err)}`); - } - }); - }; - - appender.shutdown = (cb) => { - redisClient.quit(); - if (cb) cb(); - }; - - // trigger a deprecation warning. - appender.deprecated = '@logj4s-node/redis'; - - return appender; -} - -function configure(config, layouts) { - let layout = layouts.messagePassThroughLayout; - if (config.layout) { - layout = layouts.layout(config.layout.type, config.layout); - } - - return redisAppender(config, layout); -} - -module.exports.configure = configure; diff --git a/lib/appenders/slack.js b/lib/appenders/slack.js deleted file mode 100644 index e348b93..0000000 --- a/lib/appenders/slack.js +++ /dev/null @@ -1,47 +0,0 @@ -'use strict'; - -/** - * This appender has been deprecated. - * Updates and bug fixes should be made against https://github.com/log4js-node/slack - */ -const Slack = require('slack-node'); - -function slackAppender(_config, layout, slack) { - const appender = (loggingEvent) => { - const data = { - channel_id: _config.channel_id, - text: layout(loggingEvent, _config.timezoneOffset), - icon_url: _config.icon_url, - username: _config.username - }; - - /* eslint no-unused-vars:0 */ - slack.api('chat.postMessage', { - channel: data.channel_id, - text: data.text, - icon_url: data.icon_url, - username: data.username - }, (err, response) => { - if (err) { - throw err; - } - }); - }; - - // trigger a deprecation warning. - appender.deprecated = '@logj4s-node/slack'; - return appender; -} - -function configure(_config, layouts) { - const slack = new Slack(_config.token); - - let layout = layouts.basicLayout; - if (_config.layout) { - layout = layouts.layout(_config.layout.type, _config.layout); - } - - return slackAppender(_config, layout, slack); -} - -module.exports.configure = configure; diff --git a/lib/appenders/smtp.js b/lib/appenders/smtp.js deleted file mode 100644 index d2fe8f4..0000000 --- a/lib/appenders/smtp.js +++ /dev/null @@ -1,148 +0,0 @@ -'use strict'; - -/** - * This appender has been deprecated. - * Updates and bug fixes should be made against https://github.com/log4js-node/smtp - */ - -const mailer = require('nodemailer'); -const os = require('os'); - -/** - * SMTP Appender. Sends logging events using SMTP protocol. - * It can either send an email on each event or group several - * logging events gathered during specified interval. - * - * @param _config appender configuration data - * config.sendInterval time between log emails (in seconds), if 0 - * then every event sends an email - * config.shutdownTimeout time to give up remaining emails (in seconds; defaults to 5). - * @param _layout a function that takes a logevent and returns a string (defaults to basicLayout). - */ -function smtpAppender(config, layout, subjectLayout) { - if (!config.attachment) { - config.attachment = {}; - } - - config.attachment.enable = !!config.attachment.enable; - config.attachment.message = config.attachment.message || 'See logs as attachment'; - config.attachment.filename = config.attachment.filename || 'default.log'; - - const sendInterval = config.sendInterval * 1000 || 0; - const shutdownTimeout = ('shutdownTimeout' in config ? config.shutdownTimeout : 5) * 1000; - const transport = mailer.createTransport(getTransportOptions()); - const logEventBuffer = []; - - let unsentCount = 0; - let sendTimer; - - function sendBuffer() { - if (logEventBuffer.length > 0) { - const firstEvent = logEventBuffer[0]; - let body = ''; - const count = logEventBuffer.length; - while (logEventBuffer.length > 0) { - body += `${layout(logEventBuffer.shift(), config.timezoneOffset)}\n`; - } - - const msg = { - to: config.recipients, - subject: config.subject || subjectLayout(firstEvent), - headers: { Hostname: os.hostname() } - }; - - if (config.attachment.enable === true) { - msg[config.html ? 'html' : 'text'] = config.attachment.message; - msg.attachments = [ - { - filename: config.attachment.filename, - contentType: 'text/x-log', - content: body - } - ]; - } else { - msg[config.html ? 'html' : 'text'] = body; - } - - if (config.sender) { - msg.from = config.sender; - } - transport.sendMail(msg, (error) => { - if (error) { - console.error('log4js.smtpAppender - Error happened', error); - } - transport.close(); - unsentCount -= count; - }); - } - } - - function getTransportOptions() { - let options = null; - if (config.SMTP) { - options = config.SMTP; - } else if (config.transport) { - options = config.transport.options || {}; - options.transport = config.transport.plugin || 'smtp'; - } - return options; - } - - function scheduleSend() { - if (!sendTimer) { - sendTimer = setTimeout(() => { - sendTimer = null; - sendBuffer(); - }, sendInterval); - } - } - - function shutdown(cb) { - if (shutdownTimeout > 0) { - setTimeout(() => { - if (sendTimer) { - clearTimeout(sendTimer); - } - - sendBuffer(); - }, shutdownTimeout); - } - - (function checkDone() { - if (unsentCount > 0) { - setTimeout(checkDone, 100); - } else { - cb(); - } - }()); - } - - const appender = (loggingEvent) => { - unsentCount++; // eslint-disable-line no-plusplus - logEventBuffer.push(loggingEvent); - if (sendInterval > 0) { - scheduleSend(); - } else { - sendBuffer(); - } - }; - - appender.shutdown = shutdown; - - // trigger a deprecation warning. - appender.deprecated = '@logj4s-node/smtp'; - - return appender; -} - -function configure(config, layouts) { - const subjectLayout = layouts.messagePassThroughLayout; - let layout = layouts.basicLayout; - if (config.layout) { - layout = layouts.layout(config.layout.type, config.layout); - } - return smtpAppender(config, layout, subjectLayout); -} - - -module.exports.configure = configure; diff --git a/lib/appenders/tcp-server.js b/lib/appenders/tcp-server.js new file mode 100644 index 0000000..6a9d1fb --- /dev/null +++ b/lib/appenders/tcp-server.js @@ -0,0 +1,39 @@ +const debug = require('debug')('log4js:tcp-server'); +const net = require('net'); +const clustering = require('../clustering'); +const LoggingEvent = require('../LoggingEvent'); + +const send = (data) => { + if (data) { + const event = LoggingEvent.deserialise(data); + clustering.send(event); + } +}; + +exports.configure = (config) => { + debug('configure called with ', config); + // dummy shutdown if we're not master + let shutdown = (cb) => { cb(); }; + + clustering.onlyOnMaster(() => { + const server = net.createServer((socket) => { + socket.setEncoding('utf8'); + socket.on('data', send); + socket.on('end', send); + }); + + server.listen(config.port || 5000, config.host || 'localhost', () => { + debug(`listening on ${config.host || 'localhost'}:${config.port || 5000}`); + server.unref(); + }); + + shutdown = (cb) => { + debug('shutdown called.'); + server.close(cb); + }; + }); + + return { + shutdown + }; +}; diff --git a/lib/appenders/tcp.js b/lib/appenders/tcp.js new file mode 100644 index 0000000..968f075 --- /dev/null +++ b/lib/appenders/tcp.js @@ -0,0 +1,76 @@ +'use strict'; + +const debug = require('debug')('log4js:tcp'); +const net = require('net'); + +function appender(config) { + let canWrite = false; + const buffer = []; + let socket; + let shutdownAttempts = 3; + + function write(loggingEvent) { + debug('Writing log event to socket'); + canWrite = socket.write(loggingEvent.serialise(), 'utf8'); + } + + function emptyBuffer() { + let evt; + debug('emptying buffer'); + /* eslint no-cond-assign:0 */ + while ((evt = buffer.shift())) { + write(evt); + } + } + + function createSocket() { + debug(`appender creating socket to ${config.host || 'localhost'}:${config.port || 5000}`); + socket = net.createConnection(config.port || 5000, config.host || 'localhost'); + socket.on('connect', () => { + debug('socket connected'); + emptyBuffer(); + canWrite = true; + }); + socket.on('drain', () => { + debug('drain event received, emptying buffer'); + canWrite = true; + emptyBuffer(); + }); + socket.on('timeout', socket.end.bind(socket)); + // don't bother listening for 'error', 'close' gets called after that anyway + socket.on('close', createSocket); + } + + createSocket(); + + function log(loggingEvent) { + if (canWrite) { + write(loggingEvent); + } else { + debug('buffering log event because it cannot write at the moment'); + buffer.push(loggingEvent); + } + } + + log.shutdown = function (cb) { + debug('shutdown called'); + if (buffer.length && shutdownAttempts) { + debug('buffer has items, waiting 100ms to empty'); + shutdownAttempts -= 1; + setTimeout(() => { + log.shutdown(cb); + }, 100); + } else { + socket.removeAllListeners('close'); + socket.end(cb); + } + }; + return log; +} + +function configure(config) { + debug(`configure with config = ${config}`); + return appender(config); +} + +module.exports.configure = configure; diff --git a/lib/categories.js b/lib/categories.js new file mode 100644 index 0000000..37fcb8b --- /dev/null +++ b/lib/categories.js @@ -0,0 +1,123 @@ +const configuration = require('./configuration'); +const levels = require('./levels'); +const appenders = require('./appenders'); +const debug = require('debug')('log4js:categories'); + +const categories = new Map(); + +configuration.addListener((config) => { + configuration.throwExceptionIf( + config, + configuration.not(configuration.anObject(config.categories)), + 'must have a property "categories" of type object.' + ); + + const categoryNames = Object.keys(config.categories); + configuration.throwExceptionIf( + config, + configuration.not(categoryNames.length), + 'must define at least one category.' + ); + + categoryNames.forEach((name) => { + const category = config.categories[name]; + configuration.throwExceptionIf( + config, + [ + configuration.not(category.appenders), + configuration.not(category.level) + ], + `category "${name}" is not valid (must be an object with properties "appenders" and "level")` + ); + + configuration.throwExceptionIf( + config, + configuration.not(Array.isArray(category.appenders)), + `category "${name}" is not valid (appenders must be an array of appender names)` + ); + + configuration.throwExceptionIf( + config, + configuration.not(category.appenders.length), + `category "${name}" is not valid (appenders must contain at least one appender name)` + ); + + category.appenders.forEach((appender) => { + configuration.throwExceptionIf( + config, + configuration.not(appenders.get(appender)), + `category "${name}" is not valid (appender "${appender}" is not defined)` + ); + }); + + configuration.throwExceptionIf( + config, + configuration.not(levels.getLevel(category.level)), + `category "${name}" is not valid (level "${category.level}" not recognised;` + + ` valid levels are ${levels.levels.join(', ')})` + ); + }); + + configuration.throwExceptionIf( + config, + configuration.not(config.categories.default), + 'must define a "default" category.' + ); +}); + +const setup = (config) => { + categories.clear(); + + const categoryNames = Object.keys(config.categories); + categoryNames.forEach((name) => { + const category = config.categories[name]; + const categoryAppenders = []; + category.appenders.forEach((appender) => { + categoryAppenders.push(appenders.get(appender)); + debug(`Creating category ${name}`); + categories.set( + name, + { appenders: categoryAppenders, level: levels.getLevel(category.level) } + ); + }); + }); +}; + +setup({ categories: { default: { appenders: ['out'], level: 'OFF' } } }); +configuration.addListener(setup); + +const configForCategory = (category) => { + debug(`configForCategory: searching for config for ${category}`); + if (categories.has(category)) { + debug(`configForCategory: ${category} exists in config, returning it`); + return categories.get(category); + } + if (category.indexOf('.') > 0) { + debug(`configForCategory: ${category} has hierarchy, searching for parents`); + return configForCategory(category.substring(0, category.lastIndexOf('.'))); + } + debug('configForCategory: returning config for default category'); + return configForCategory('default'); +}; + +const appendersForCategory = category => configForCategory(category).appenders; +const getLevelForCategory = category => configForCategory(category).level; + +const setLevelForCategory = (category, level) => { + let categoryConfig = categories.get(category); + debug(`setLevelForCategory: found ${categoryConfig} for ${category}`); + if (!categoryConfig) { + const sourceCategoryConfig = configForCategory(category); + debug('setLevelForCategory: no config found for category, ' + + `found ${sourceCategoryConfig} for parents of ${category}`); + categoryConfig = { appenders: sourceCategoryConfig.appenders }; + } + categoryConfig.level = level; + categories.set(category, categoryConfig); +}; + +module.exports = { + appendersForCategory, + getLevelForCategory, + setLevelForCategory +}; diff --git a/lib/clustering.js b/lib/clustering.js new file mode 100644 index 0000000..e8ede70 --- /dev/null +++ b/lib/clustering.js @@ -0,0 +1,92 @@ +const debug = require('debug')('log4js:clustering'); +const LoggingEvent = require('./LoggingEvent'); +const configuration = require('./configuration'); +const cluster = require('cluster'); + +const listeners = []; + +let disabled = false; +let pm2 = false; +let pm2InstanceVar = 'NODE_APP_INSTANCE'; + +const isPM2Master = () => pm2 && process.env[pm2InstanceVar] === '0'; +const isMaster = () => disabled || cluster.isMaster || isPM2Master(); + +const sendToListeners = (logEvent) => { + listeners.forEach(l => l(logEvent)); +}; + +// in a multi-process node environment, worker loggers will use +// process.send +const receiver = (worker, message) => { + // prior to node v6, the worker parameter was not passed (args were message, handle) + debug('cluster message received from worker ', worker, ': ', message); + if (worker.topic && worker.data) { + message = worker; + worker = undefined; + } + if (message && message.topic && message.topic === 'log4js:message') { + debug('received message: ', message.data); + const logEvent = LoggingEvent.deserialise(message.data); + sendToListeners(logEvent); + } +}; + +configuration.addListener((config) => { + // clear out the listeners, because configure has been called. + listeners.length = 0; + + disabled = config.disableClustering; + pm2 = config.pm2; + pm2InstanceVar = config.pm2InstanceVar || 'NODE_APP_INSTANCE'; + + debug(`clustering disabled ? ${disabled}`); + debug(`cluster.isMaster ? ${cluster.isMaster}`); + debug(`pm2 enabled ? ${pm2}`); + debug(`pm2InstanceVar = ${pm2InstanceVar}`); + debug(`process.env[${pm2InstanceVar}] = ${process.env[pm2InstanceVar]}`); + + // just in case configure is called after shutdown + if (pm2) { + process.removeListener('message', receiver); + } + if (cluster.removeListener) { + cluster.removeListener('message', receiver); + } + + if (config.disableClustering) { + debug('Not listening for cluster messages, because clustering disabled.'); + } else if (isPM2Master()) { + // PM2 cluster support + // PM2 runs everything as workers - install pm2-intercom for this to work. + // we only want one of the app instances to write logs + debug('listening for PM2 broadcast messages'); + process.on('message', receiver); + } else if (cluster.isMaster) { + debug('listening for cluster messages'); + cluster.on('message', receiver); + } else { + debug('not listening for messages, because we are not a master process'); + } +}); + +module.exports = { + onlyOnMaster: (fn, notMaster) => (isMaster() ? fn() : notMaster), + isMaster: isMaster, + send: (msg) => { + if (isMaster()) { + sendToListeners(msg); + } else { + if (!pm2) { + msg.cluster = { + workerId: cluster.worker.id, + worker: process.pid + }; + } + process.send({ topic: 'log4js:message', data: msg.serialise() }); + } + }, + onMessage: (listener) => { + listeners.push(listener); + } +}; diff --git a/lib/configuration.js b/lib/configuration.js index e73b5f6..86cea04 100644 --- a/lib/configuration.js +++ b/lib/configuration.js @@ -1,237 +1,48 @@ 'use strict'; const util = require('util'); -const path = require('path'); -const levels = require('./levels'); -const layouts = require('./layouts'); -const appenderAdapter = require('./appender-adapter'); const debug = require('debug')('log4js:configuration'); -let cluster; -try { - cluster = require('cluster'); // eslint-disable-line global-require -} catch (e) { - debug('Clustering support disabled because require(cluster) threw an error: ', e); -} +const listeners = []; -const validColours = [ - 'white', 'grey', 'black', - 'blue', 'cyan', 'green', - 'magenta', 'red', 'yellow' -]; +const not = thing => !thing; -function not(thing) { - return !thing; -} +const anObject = thing => thing && typeof thing === 'object' && !Array.isArray(thing); -function anObject(thing) { - return thing && typeof thing === 'object' && !Array.isArray(thing); -} +const validIdentifier = thing => /^[A-Za-z][A-Za-z0-9_]*$/g.test(thing); -function validIdentifier(thing) { - return /^[A-Za-z][A-Za-z0-9_]*$/g.test(thing); -} +const anInteger = thing => thing && typeof thing === 'number' && Number.isInteger(thing); -function anInteger(thing) { - return thing && typeof thing === 'number' && Number.isInteger(thing); -} +const addListener = (fn) => { + listeners.push(fn); + debug(`Added listener, listeners now ${listeners.length}`); +}; -class Configuration { - throwExceptionIf(checks, message) { - const tests = Array.isArray(checks) ? checks : [checks]; - tests.forEach((test) => { - if (test) { - throw new Error(`Problem with log4js configuration: (${util.inspect(this.candidate, { depth: 5 })})` + - ` - ${message}`); - } - }); - } - - tryLoading(modulePath) { - debug('Loading module from ', modulePath); - try { - return require(modulePath); //eslint-disable-line - } catch (e) { - // if the module was found, and we still got an error, then raise it - this.throwExceptionIf( - e.code !== 'MODULE_NOT_FOUND', - `appender "${path}" could not be loaded (error was: ${e})` - ); - return undefined; +const throwExceptionIf = (config, checks, message) => { + const tests = Array.isArray(checks) ? checks : [checks]; + tests.forEach((test) => { + if (test) { + throw new Error(`Problem with log4js configuration: (${util.inspect(config, { depth: 5 })})` + + ` - ${message}`); } - } + }); +}; - loadAppenderModule(type) { - return this.tryLoading(`./appenders/${type}`) || - this.tryLoading(type) || - this.tryLoading(path.join(path.dirname(require.main.filename), type)) || - this.tryLoading(path.join(process.cwd(), type)); - } +const configure = (candidate) => { + debug('New configuration to be validated: ', candidate); + throwExceptionIf(candidate, not(anObject(candidate)), 'must be an object.'); - createAppender(name, config) { - const appenderModule = this.loadAppenderModule(config.type); + debug(`Calling configuration listeners (${listeners.length})`); + listeners.forEach(listener => listener(candidate)); + debug('Configuration finished.'); +}; - if (appenderAdapter[config.type]) { - appenderAdapter[config.type](config); - } - - this.throwExceptionIf( - not(appenderModule), - `appender "${name}" is not valid (type "${config.type}" could not be found)` - ); - if (appenderModule.appender) { - debug(`DEPRECATION: Appender ${config.type} exports an appender function.`); - } - if (appenderModule.shutdown) { - debug(`DEPRECATION: Appender ${config.type} exports a shutdown function.`); - } - - if (this.disableClustering || cluster.isMaster || (this.pm2 && process.env[this.pm2InstanceVar] === '0')) { - debug(`cluster.isMaster ? ${cluster.isMaster}`); - debug(`pm2 enabled ? ${this.pm2}`); - debug(`pm2InstanceVar = ${this.pm2InstanceVar}`); - debug(`process.env[${this.pm2InstanceVar}] = ${process.env[this.pm2InstanceVar]}`); - - const appender = appenderModule.configure( - config, - layouts, - this.configuredAppenders.get.bind(this.configuredAppenders), - this.configuredLevels - ); - - if (appender.deprecated && this.deprecationWarnings) { - console.error(`Appender "${name}" uses a deprecated type "${config.type}", ` + // eslint-disable-line - 'which will be removed in log4js v3. ' + - `You should change it to use "${appender.deprecated}". ` + - 'To turn off this warning add "deprecationWarnings: false" to your config.'); - } - - return appender; - } - return () => {}; - } - - get appenders() { - return this.configuredAppenders; - } - - set appenders(appenderConfig) { - const appenderNames = Object.keys(appenderConfig); - this.throwExceptionIf(not(appenderNames.length), 'must define at least one appender.'); - - this.configuredAppenders = new Map(); - appenderNames.forEach((name) => { - this.throwExceptionIf( - not(appenderConfig[name].type), - `appender "${name}" is not valid (must be an object with property "type")` - ); - - debug(`Creating appender ${name}`); - this.configuredAppenders.set(name, this.createAppender(name, appenderConfig[name])); - }); - } - - get categories() { - return this.configuredCategories; - } - - set categories(categoryConfig) { - const categoryNames = Object.keys(categoryConfig); - this.throwExceptionIf(not(categoryNames.length), 'must define at least one category.'); - - this.configuredCategories = new Map(); - categoryNames.forEach((name) => { - const category = categoryConfig[name]; - this.throwExceptionIf( - [ - not(category.appenders), - not(category.level) - ], - `category "${name}" is not valid (must be an object with properties "appenders" and "level")` - ); - - this.throwExceptionIf( - not(Array.isArray(category.appenders)), - `category "${name}" is not valid (appenders must be an array of appender names)` - ); - - this.throwExceptionIf( - not(category.appenders.length), - `category "${name}" is not valid (appenders must contain at least one appender name)` - ); - - const appenders = []; - category.appenders.forEach((appender) => { - this.throwExceptionIf( - not(this.configuredAppenders.get(appender)), - `category "${name}" is not valid (appender "${appender}" is not defined)` - ); - appenders.push(this.appenders.get(appender)); - }); - - this.throwExceptionIf( - not(this.configuredLevels.getLevel(category.level)), - `category "${name}" is not valid (level "${category.level}" not recognised;` + - ` valid levels are ${this.configuredLevels.levels.join(', ')})` - ); - - debug(`Creating category ${name}`); - this.configuredCategories.set( - name, - { appenders: appenders, level: this.configuredLevels.getLevel(category.level) } - ); - }); - - this.throwExceptionIf(not(categoryConfig.default), 'must define a "default" category.'); - } - - get levels() { - return this.configuredLevels; - } - - set levels(levelConfig) { - // levels are optional - if (levelConfig) { - this.throwExceptionIf(not(anObject(levelConfig)), 'levels must be an object'); - const newLevels = Object.keys(levelConfig); - newLevels.forEach((l) => { - this.throwExceptionIf( - not(validIdentifier(l)), - `level name "${l}" is not a valid identifier (must start with a letter, only contain A-Z,a-z,0-9,_)` - ); - this.throwExceptionIf(not(anObject(levelConfig[l])), `level "${l}" must be an object`); - this.throwExceptionIf(not(levelConfig[l].value), `level "${l}" must have a 'value' property`); - this.throwExceptionIf(not(anInteger(levelConfig[l].value)), `level "${l}".value must have an integer value`); - this.throwExceptionIf(not(levelConfig[l].colour), `level "${l}" must have a 'colour' property`); - this.throwExceptionIf( - not(validColours.indexOf(levelConfig[l].colour) > -1), - `level "${l}".colour must be one of ${validColours.join(', ')}` - ); - }); - } - this.configuredLevels = levels(levelConfig); - } - - constructor(candidate) { - this.candidate = candidate; - - this.throwExceptionIf(not(anObject(candidate)), 'must be an object.'); - this.throwExceptionIf(not(anObject(candidate.appenders)), 'must have a property "appenders" of type object.'); - this.throwExceptionIf(not(anObject(candidate.categories)), 'must have a property "categories" of type object.'); - - this.disableClustering = this.candidate.disableClustering || !cluster; - this.deprecationWarnings = true; - if ('deprecationWarnings' in this.candidate) { - this.deprecationWarnings = this.candidate.deprecationWarnings; - } - - this.pm2 = this.candidate.pm2; - this.pm2InstanceVar = this.candidate.pm2InstanceVar || 'NODE_APP_INSTANCE'; - - this.levels = candidate.levels; - this.appenders = candidate.appenders; - this.categories = candidate.categories; - } -} - -module.exports = Configuration; +module.exports = { + configure, + addListener, + throwExceptionIf, + anObject, + anInteger, + validIdentifier, + not +}; diff --git a/lib/connect-logger.js b/lib/connect-logger.js index 9306ac1..3a314de 100755 --- a/lib/connect-logger.js +++ b/lib/connect-logger.js @@ -2,6 +2,8 @@ 'use strict'; +const levels = require('./levels'); + const DEFAULT_FORMAT = ':remote-addr - -' + ' ":method :url HTTP/:http-version"' + ' :status :content-length ":referrer"' + @@ -163,8 +165,7 @@ function createNoLogCondition(nolog) { return regexp; } -module.exports = function (levels) { - /** +/** * Log requests with the given `options` or a `format` string. * * Options: @@ -192,80 +193,77 @@ module.exports = function (levels) { * @param options * @api public */ - function getLogger(logger4js, options) { - /* eslint no-underscore-dangle:0 */ - if (typeof options === 'object') { - options = options || {}; - } else if (options) { - options = { format: options }; - } else { - options = {}; - } - - const thisLogger = logger4js; - let level = levels.getLevel(options.level, levels.INFO); - const fmt = options.format || DEFAULT_FORMAT; - const nolog = options.nolog ? createNoLogCondition(options.nolog) : null; - - return (req, res, next) => { - // mount safety - if (req._logging) return next(); - - // nologs - if (nolog && nolog.test(req.originalUrl)) return next(); - - if (thisLogger.isLevelEnabled(level) || options.level === 'auto') { - const start = new Date(); - const writeHead = res.writeHead; - - // flag as logging - req._logging = true; - - // proxy for statusCode. - res.writeHead = (code, headers) => { - res.writeHead = writeHead; - res.writeHead(code, headers); - - res.__statusCode = code; - res.__headers = headers || {}; - - // status code response level handling - if (options.level === 'auto') { - level = levels.INFO; - if (code >= 300) level = levels.WARN; - if (code >= 400) level = levels.ERROR; - } else { - level = levels.getLevel(options.level, levels.INFO); - } - }; - - // hook on end request to emit the log entry of the HTTP request. - res.on('finish', () => { - res.responseTime = new Date() - start; - // status code response level handling - if (res.statusCode && options.level === 'auto') { - level = levels.INFO; - if (res.statusCode >= 300) level = levels.WARN; - if (res.statusCode >= 400) level = levels.ERROR; - } - - if (thisLogger.isLevelEnabled(level)) { - const combinedTokens = assembleTokens(req, res, options.tokens || []); - - if (typeof fmt === 'function') { - const line = fmt(req, res, str => format(str, combinedTokens)); - if (line) thisLogger.log(level, line); - } else { - thisLogger.log(level, format(fmt, combinedTokens)); - } - } - }); - } - - // ensure next gets always called - return next(); - }; +module.exports = function getLogger(logger4js, options) { + /* eslint no-underscore-dangle:0 */ + if (typeof options === 'object') { + options = options || {}; + } else if (options) { + options = { format: options }; + } else { + options = {}; } - return { connectLogger: getLogger }; + const thisLogger = logger4js; + let level = levels.getLevel(options.level, levels.INFO); + const fmt = options.format || DEFAULT_FORMAT; + const nolog = options.nolog ? createNoLogCondition(options.nolog) : null; + + return (req, res, next) => { + // mount safety + if (req._logging) return next(); + + // nologs + if (nolog && nolog.test(req.originalUrl)) return next(); + + if (thisLogger.isLevelEnabled(level) || options.level === 'auto') { + const start = new Date(); + const writeHead = res.writeHead; + + // flag as logging + req._logging = true; + + // proxy for statusCode. + res.writeHead = (code, headers) => { + res.writeHead = writeHead; + res.writeHead(code, headers); + + res.__statusCode = code; + res.__headers = headers || {}; + + // status code response level handling + if (options.level === 'auto') { + level = levels.INFO; + if (code >= 300) level = levels.WARN; + if (code >= 400) level = levels.ERROR; + } else { + level = levels.getLevel(options.level, levels.INFO); + } + }; + + // hook on end request to emit the log entry of the HTTP request. + res.on('finish', () => { + res.responseTime = new Date() - start; + // status code response level handling + if (res.statusCode && options.level === 'auto') { + level = levels.INFO; + if (res.statusCode >= 300) level = levels.WARN; + if (res.statusCode >= 400) level = levels.ERROR; + } + + if (thisLogger.isLevelEnabled(level)) { + const combinedTokens = assembleTokens(req, res, options.tokens || []); + + if (typeof fmt === 'function') { + const line = fmt(req, res, str => format(str, combinedTokens)); + if (line) thisLogger.log(level, line); + } else { + thisLogger.log(level, format(fmt, combinedTokens)); + } + } + }); + } + + // ensure next gets always called + return next(); + }; }; diff --git a/lib/levels.js b/lib/levels.js index 0ca70c8..99ac0ea 100644 --- a/lib/levels.js +++ b/lib/levels.js @@ -1,60 +1,22 @@ 'use strict'; -module.exports = function (customLevels) { - /** - * @name Level - * @namespace Log4js - */ - class Level { - constructor(level, levelStr, colour) { - this.level = level; - this.levelStr = levelStr; - this.colour = colour; - } +const configuration = require('./configuration'); - toString() { - return this.levelStr; - } +const validColours = [ + 'white', 'grey', 'black', + 'blue', 'cyan', 'green', + 'magenta', 'red', 'yellow' +]; - isLessThanOrEqualTo(otherLevel) { - if (typeof otherLevel === 'string') { - otherLevel = getLevel(otherLevel); - } - return this.level <= otherLevel.level; - } - - isGreaterThanOrEqualTo(otherLevel) { - if (typeof otherLevel === 'string') { - otherLevel = getLevel(otherLevel); - } - return this.level >= otherLevel.level; - } - - isEqualTo(otherLevel) { - if (typeof otherLevel === 'string') { - otherLevel = getLevel(otherLevel); - } - return this.level === otherLevel.level; - } +class Level { + constructor(level, levelStr, colour) { + this.level = level; + this.levelStr = levelStr; + this.colour = colour; } - const defaultLevels = { - ALL: new Level(Number.MIN_VALUE, 'ALL', 'grey'), - TRACE: new Level(5000, 'TRACE', 'blue'), - DEBUG: new Level(10000, 'DEBUG', 'cyan'), - INFO: new Level(20000, 'INFO', 'green'), - WARN: new Level(30000, 'WARN', 'yellow'), - ERROR: new Level(40000, 'ERROR', 'red'), - FATAL: new Level(50000, 'FATAL', 'magenta'), - MARK: new Level(9007199254740992, 'MARK', 'grey'), // 2^53 - OFF: new Level(Number.MAX_VALUE, 'OFF', 'grey') - }; - - if (customLevels) { - const levels = Object.keys(customLevels); - levels.forEach((l) => { - defaultLevels[l.toUpperCase()] = new Level(customLevels[l].value, l.toUpperCase(), customLevels[l].colour); - }); + toString() { + return this.levelStr; } /** @@ -63,7 +25,7 @@ module.exports = function (customLevels) { * @param {Level} [defaultLevel] -- default Level, if no String representation * @return {Level} */ - function getLevel(sArg, defaultLevel) { + static getLevel(sArg, defaultLevel) { if (!sArg) { return defaultLevel; } @@ -73,15 +35,109 @@ module.exports = function (customLevels) { } if (typeof sArg === 'string') { - return defaultLevels[sArg.toUpperCase()] || defaultLevel; + return Level[sArg.toUpperCase()] || defaultLevel; } - return getLevel(sArg.toString()); + return Level.getLevel(sArg.toString()); } - const orderedLevels = Object.keys(defaultLevels).sort((a, b) => b.level - a.level); - defaultLevels.getLevel = getLevel; - defaultLevels.levels = orderedLevels; + static addLevels(customLevels) { + if (customLevels) { + const levels = Object.keys(customLevels); + levels.forEach((l) => { + Level[l.toUpperCase()] = new Level( + customLevels[l].value, + l.toUpperCase(), + customLevels[l].colour + ); + Level.levels.push(Level[l.toUpperCase()]); + }); + Level.levels.sort((a, b) => a.level - b.level); + } + } - return defaultLevels; -}; + + isLessThanOrEqualTo(otherLevel) { + if (typeof otherLevel === 'string') { + otherLevel = Level.getLevel(otherLevel); + } + return this.level <= otherLevel.level; + } + + isGreaterThanOrEqualTo(otherLevel) { + if (typeof otherLevel === 'string') { + otherLevel = Level.getLevel(otherLevel); + } + return this.level >= otherLevel.level; + } + + isEqualTo(otherLevel) { + if (typeof otherLevel === 'string') { + otherLevel = Level.getLevel(otherLevel); + } + return this.level === otherLevel.level; + } +} + +Level.levels = []; +Level.addLevels({ + ALL: { value: Number.MIN_VALUE, colour: 'grey' }, + TRACE: { value: 5000, colour: 'blue' }, + DEBUG: { value: 10000, colour: 'cyan' }, + INFO: { value: 20000, colour: 'green' }, + WARN: { value: 30000, colour: 'yellow' }, + ERROR: { value: 40000, colour: 'red' }, + FATAL: { value: 50000, colour: 'magenta' }, + MARK: { value: 9007199254740992, colour: 'grey' }, // 2^53 + OFF: { value: Number.MAX_VALUE, colour: 'grey' } +}); + +configuration.addListener((config) => { + const levelConfig = config.levels; + if (levelConfig) { + configuration.throwExceptionIf( + config, + configuration.not(configuration.anObject(levelConfig)), + 'levels must be an object' + ); + const newLevels = Object.keys(levelConfig); + newLevels.forEach((l) => { + configuration.throwExceptionIf( + config, + configuration.not(configuration.validIdentifier(l)), + `level name "${l}" is not a valid identifier (must start with a letter, only contain A-Z,a-z,0-9,_)` + ); + configuration.throwExceptionIf( + config, + configuration.not(configuration.anObject(levelConfig[l])), + `level "${l}" must be an object` + ); + configuration.throwExceptionIf( + config, + configuration.not(levelConfig[l].value), + `level "${l}" must have a 'value' property` + ); + configuration.throwExceptionIf( + config, + configuration.not(configuration.anInteger(levelConfig[l].value)), + `level "${l}".value must have an integer value` + ); + configuration.throwExceptionIf( + config, + configuration.not(levelConfig[l].colour), + `level "${l}" must have a 'colour' property` + ); + configuration.throwExceptionIf( + config, + configuration.not(validColours.indexOf(levelConfig[l].colour) > -1), + `level "${l}".colour must be one of ${validColours.join(', ')}` + ); + }); + } +}); + +configuration.addListener((config) => { + Level.addLevels(config.levels); +}); + +module.exports = Level; diff --git a/lib/log4js.js b/lib/log4js.js index 7bce895..072330d 100644 --- a/lib/log4js.js +++ b/lib/log4js.js @@ -15,163 +15,33 @@ * * NOTE: the authors below are the original browser-based log4js authors * don't try to contact them about bugs in this version :) - * @version 1.0 * @author Stephan Strittmatter - http://jroller.com/page/stritti * @author Seth Chisamore - http://www.chisamore.com * @since 2005-05-20 - * @static * Website: http://log4js.berlios.de */ const debug = require('debug')('log4js:main'); const fs = require('fs'); -const CircularJSON = require('circular-json'); -const Configuration = require('./configuration'); -const connectModule = require('./connect-logger'); -const logger = require('./logger'); +const configuration = require('./configuration'); const layouts = require('./layouts'); const levels = require('./levels'); +const appenders = require('./appenders'); +const categories = require('./categories'); +const Logger = require('./logger'); +const clustering = require('./clustering'); +const connectLogger = require('./connect-logger'); -let cluster; -try { - cluster = require('cluster'); // eslint-disable-line global-require -} catch (e) { - debug('Clustering support disabled because require(cluster) threw an error: ', e); -} - -const defaultConfig = { - appenders: { - stdout: { type: 'stdout' } - }, - categories: { - default: { appenders: ['stdout'], level: 'OFF' } - } -}; - -let Logger; -let LoggingEvent; -let config; let enabled = false; -function configForCategory(category) { - debug(`configForCategory: searching for config for ${category}`); - if (config.categories.has(category)) { - debug(`configForCategory: ${category} exists in config, returning it`); - return config.categories.get(category); - } - if (category.indexOf('.') > 0) { - debug(`configForCategory: ${category} has hierarchy, searching for parents`); - return configForCategory(category.substring(0, category.lastIndexOf('.'))); - } - debug('configForCategory: returning config for default category'); - return configForCategory('default'); -} - -function appendersForCategory(category) { - return configForCategory(category).appenders; -} - -function levelForCategory(category) { - return configForCategory(category).level; -} - -function setLevelForCategory(category, level) { - let categoryConfig = config.categories.get(category); - debug(`setLevelForCategory: found ${categoryConfig} for ${category}`); - if (!categoryConfig) { - const sourceCategoryConfig = configForCategory(category); - debug('setLevelForCategory: no config found for category, ' + - `found ${sourceCategoryConfig} for parents of ${category}`); - categoryConfig = { appenders: sourceCategoryConfig.appenders }; - } - categoryConfig.level = level; - config.categories.set(category, categoryConfig); -} - -function serialise(logEvent) { - // JSON.stringify(new Error('test')) returns {}, which is not really useful for us. - // The following allows us to serialize errors correctly. - // Validate that we really are in this case - try { - const logData = logEvent.data.map((e) => { - if (e && e.message && e.stack) { - e = Object.assign({ message: e.message, stack: e.stack }, e); - } - return e; - }); - logEvent.data = logData; - return CircularJSON.stringify(logEvent); - } catch (e) { - return serialise(new LoggingEvent( - 'log4js', - config.levels.ERROR, - ['Unable to serialise log event due to :', e] - )); - } -} - -function deserialise(serialised) { - let event; - try { - event = CircularJSON.parse(serialised); - event.startTime = new Date(event.startTime); - event.level = config.levels.getLevel(event.level.levelStr); - event.data = event.data.map((e) => { - if (e && e.message && e.stack) { - const fakeError = new Error(e); - Object.keys(e).forEach((key) => { fakeError[key] = e[key]; }); - e = fakeError; - } - return e; - }); - } catch (e) { - event = new LoggingEvent( - 'log4js', - config.levels.ERROR, - ['Unable to parse log:', serialised, 'because: ', e] - ); - } - - return event; -} - function sendLogEventToAppender(logEvent) { if (!enabled) return; debug('Received log event ', logEvent); - const appenders = appendersForCategory(logEvent.categoryName); - appenders.forEach((appender) => { + const categoryAppenders = categories.appendersForCategory(logEvent.categoryName); + categoryAppenders.forEach((appender) => { appender(logEvent); }); } -function workerDispatch(logEvent) { - debug(`sending message to master from worker ${process.pid}`); - process.send({ topic: 'log4js:message', data: serialise(logEvent) }); -} - -function isPM2Master() { - return config.pm2 && process.env[config.pm2InstanceVar] === '0'; -} - -function isMaster() { - return config.disableClustering || cluster.isMaster || isPM2Master(); -} - -/** - * Get a logger instance. - * @static - * @param loggerCategoryName - * @return {Logger} instance of logger for the category - */ -function getLogger(category) { - if (!enabled) { - configure(process.env.LOG4JS_CONFIG || defaultConfig); - } - - const cat = category || 'default'; - debug(`creating logger as ${isMaster() ? 'master' : 'worker'}`); - return new Logger((isMaster() ? sendLogEventToAppender : workerDispatch), cat); -} - function loadConfigurationFile(filename) { if (filename) { debug(`Loading configuration from ${filename}`); @@ -180,21 +50,6 @@ function loadConfigurationFile(filename) { return filename; } -// in a multi-process node environment, worker loggers will use -// process.send -const receiver = (worker, message) => { - // prior to node v6, the worker parameter was not passed (args were message, handle) - debug('cluster message received from worker ', worker, ': ', message); - if (worker.topic && worker.data) { - message = worker; - worker = undefined; - } - if (message && message.topic && message.topic === 'log4js:message') { - debug('received message: ', message.data); - sendLogEventToAppender(deserialise(message.data)); - } -}; - function configure(configurationFileOrObject) { let configObject = configurationFileOrObject; @@ -202,32 +57,9 @@ function configure(configurationFileOrObject) { configObject = loadConfigurationFile(configurationFileOrObject); } debug(`Configuration is ${configObject}`); - config = new Configuration(configObject); - module.exports.levels = config.levels; - const loggerModule = logger(config.levels, levelForCategory, setLevelForCategory); - Logger = loggerModule.Logger; - LoggingEvent = loggerModule.LoggingEvent; - module.exports.connectLogger = connectModule(config.levels).connectLogger; + configuration.configure(configObject); - // just in case configure is called after shutdown - process.removeListener('message', receiver); - if (cluster && !config.disableClustering) { - cluster.removeListener('message', receiver); - } - if (config.disableClustering) { - debug('Not listening for cluster messages, because clustering disabled.'); - } else if (isPM2Master()) { - // PM2 cluster support - // PM2 runs everything as workers - install pm2-intercom for this to work. - // we only want one of the app instances to write logs - debug('listening for PM2 broadcast messages'); - process.on('message', receiver); - } else if (cluster.isMaster) { - debug('listening for cluster messages'); - cluster.on('message', receiver); - } else { - debug('not listening for messages, because we are not a master process'); - } + clustering.onMessage(sendLogEventToAppender); enabled = true; @@ -249,8 +81,8 @@ function shutdown(cb) { enabled = false; // Call each of the shutdown functions in parallel - const appenders = Array.from(config.appenders.values()); - const shutdownFunctions = appenders.reduceRight((accum, next) => (next.shutdown ? accum + 1 : accum), 0); + const appendersToCheck = Array.from(appenders.values()); + const shutdownFunctions = appendersToCheck.reduceRight((accum, next) => (next.shutdown ? accum + 1 : accum), 0); let completed = 0; let error; @@ -270,11 +102,28 @@ function shutdown(cb) { return cb(); } - appenders.filter(a => a.shutdown).forEach(a => a.shutdown(complete)); + appendersToCheck.filter(a => a.shutdown).forEach(a => a.shutdown(complete)); return null; } +/** + * Get a logger instance. + * @static + * @param loggerCategoryName + * @return {Logger} instance of logger for the category + */ +function getLogger(category) { + if (!enabled) { + configure(process.env.LOG4JS_CONFIG || { + appenders: { out: { type: 'stdout' } }, + categories: { default: { appenders: ['out'], level: 'OFF' } } + }); + } + return new Logger(category || 'default'); +} + + /** * @name log4js * @namespace Log4js @@ -286,8 +135,8 @@ const log4js = { getLogger, configure, shutdown, - connectLogger: connectModule(levels()).connectLogger, - levels: levels(), + connectLogger, + levels, addLayout: layouts.addLayout }; diff --git a/lib/logger.js b/lib/logger.js index bd3ccb5..55bdedf 100644 --- a/lib/logger.js +++ b/lib/logger.js @@ -3,136 +3,92 @@ 'use strict'; const debug = require('debug')('log4js:logger'); - -let cluster; -try { - cluster = require('cluster'); // eslint-disable-line global-require -} catch (e) { - debug('Clustering support disabled because require(cluster) threw an error: ', e); -} +const LoggingEvent = require('./LoggingEvent'); +const levels = require('./levels'); +const clustering = require('./clustering'); +const categories = require('./categories'); +const configuration = require('./configuration'); /** - * @name LoggingEvent + * Logger to log messages. + * use {@see log4js#getLogger(String)} to get an instance. + * + * @name Logger * @namespace Log4js + * @param name name of category to log to + * @param level - the loglevel for the category + * @param dispatch - the function which will receive the logevents + * + * @author Stephan Strittmatter */ -class LoggingEvent { - /** - * Models a logging event. - * @constructor - * @param {String} categoryName name of category - * @param {Log4js.Level} level level of message - * @param {Array} data objects to log - * @author Seth Chisamore - */ - constructor(categoryName, level, data, context) { - this.startTime = new Date(); - this.categoryName = categoryName; - this.data = data; - this.level = level; - this.context = Object.assign({}, context); - this.pid = process.pid; - if (cluster && cluster.isWorker) { - this.cluster = { - workerId: cluster.worker.id, - worker: process.pid - }; +class Logger { + constructor(name) { + if (!name) { + throw new Error('No category provided.'); } + this.category = name; + this.context = {}; + debug(`Logger created (${this.category}, ${this.level})`); + } + + get level() { + return levels.getLevel(categories.getLevelForCategory(this.category), levels.TRACE); + } + + set level(level) { + categories.setLevelForCategory(this.category, levels.getLevel(level, this.level)); + } + + log(level, ...args) { + const logLevel = levels.getLevel(level, levels.INFO); + if (this.isLevelEnabled(logLevel)) { + this._log(logLevel, args); + } + } + + isLevelEnabled(otherLevel) { + return this.level.isLessThanOrEqualTo(otherLevel); + } + + _log(level, data) { + debug(`sending log data (${level}) to appenders`); + const loggingEvent = new LoggingEvent(this.category, level, data, this.context); + clustering.send(loggingEvent); + } + + addContext(key, value) { + this.context[key] = value; + } + + removeContext(key) { + delete this.context[key]; + } + + clearContext() { + this.context = {}; } } -module.exports = function (levels, getLevelForCategory, setLevelForCategory) { - /** - * Logger to log messages. - * use {@see log4js#getLogger(String)} to get an instance. - * - * @name Logger - * @namespace Log4js - * @param name name of category to log to - * @param level - the loglevel for the category - * @param dispatch - the function which will receive the logevents - * - * @author Stephan Strittmatter - */ - class Logger { - constructor(dispatch, name) { - if (typeof dispatch !== 'function') { - throw new Error('No dispatch function provided.'); - } - if (!name) { - throw new Error('No category provided.'); - } - this.category = name; - this.dispatch = dispatch; - this.context = {}; - debug(`Logger created (${this.category}, ${this.level}, ${this.dispatch})`); - } +function addLevelMethods(target) { + const level = levels.getLevel(target); - get level() { - return levels.getLevel(getLevelForCategory(this.category), levels.TRACE); - } + const levelStrLower = level.toString().toLowerCase(); + const levelMethod = levelStrLower.replace(/_([a-z])/g, g => g[1].toUpperCase()); + const isLevelMethod = levelMethod[0].toUpperCase() + levelMethod.slice(1); - set level(level) { - setLevelForCategory(this.category, levels.getLevel(level, this.level)); - } - - log() { - /* eslint prefer-rest-params:0 */ - // todo: once node v4 support dropped, use rest parameter instead - const args = Array.from(arguments); - const logLevel = levels.getLevel(args[0], levels.INFO); - if (this.isLevelEnabled(logLevel)) { - this._log(logLevel, args.slice(1)); - } - } - - isLevelEnabled(otherLevel) { - return this.level.isLessThanOrEqualTo(otherLevel); - } - - _log(level, data) { - debug(`sending log data (${level}) to appenders`); - const loggingEvent = new LoggingEvent(this.category, level, data, this.context); - this.dispatch(loggingEvent); - } - - addContext(key, value) { - this.context[key] = value; - } - - removeContext(key) { - delete this.context[key]; - } - - clearContext() { - this.context = {}; - } - } - - function addLevelMethods(target) { - const level = levels.getLevel(target); - - const levelStrLower = level.toString().toLowerCase(); - const levelMethod = levelStrLower.replace(/_([a-z])/g, g => g[1].toUpperCase()); - const isLevelMethod = levelMethod[0].toUpperCase() + levelMethod.slice(1); - - Logger.prototype[`is${isLevelMethod}Enabled`] = function () { - return this.isLevelEnabled(level); - }; - - Logger.prototype[levelMethod] = function () { - /* eslint prefer-rest-params:0 */ - // todo: once node v4 support dropped, use rest parameter instead - const args = Array.from(arguments); - if (this.isLevelEnabled(level)) { - this._log(level, args); - } - }; - } - - levels.levels.forEach(addLevelMethods); - - return { - LoggingEvent: LoggingEvent, - Logger: Logger + Logger.prototype[`is${isLevelMethod}Enabled`] = function () { + return this.isLevelEnabled(level); }; -}; + + Logger.prototype[levelMethod] = function (...args) { + this.log(level, ...args); + }; +} + +levels.levels.forEach(addLevelMethods); + +configuration.addListener(() => { + levels.levels.forEach(addLevelMethods); +}); + +module.exports = Logger; diff --git a/package-lock.json b/package-lock.json index 0cd26a9..b947684 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4,6 +4,16 @@ "lockfileVersion": 1, "requires": true, "dependencies": { + "@log4js-node/sandboxed-module": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@log4js-node/sandboxed-module/-/sandboxed-module-2.1.1.tgz", + "integrity": "sha512-qzxAUfNvchwy+Z3Mx6N5zdtgps95z3t1ynwKrHtLbe4mwG2mZtfGRCWPKXtdSW9tnKT6DZdKPgJoHiU7cWlzSw==", + "dev": true, + "requires": { + "require-like": "0.1.2", + "stack-trace": "0.0.9" + } + }, "JSONStream": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.3.tgz", @@ -37,24 +47,11 @@ } } }, - "addressparser": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/addressparser/-/addressparser-1.0.1.tgz", - "integrity": "sha1-R6++GiqSYhkdtoOOT9HTm0CCF0Y=", - "optional": true - }, - "agent-base": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.2.0.tgz", - "integrity": "sha512-c+R/U5X+2zz2+UCrCFv6odQzJdoqI+YecuhnAJLa1zYaMc13zPfwMwZrr91Pd1DYNo/yPRbiM4WVf9whgwFsIg==", - "requires": { - "es6-promisify": "^5.0.0" - } - }, "ajv": { "version": "5.5.2", "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz", "integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=", + "dev": true, "requires": { "co": "^4.6.0", "fast-deep-equal": "^1.0.0", @@ -85,19 +82,6 @@ "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=", "dev": true }, - "amqplib": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/amqplib/-/amqplib-0.5.2.tgz", - "integrity": "sha512-l9mCs6LbydtHqRniRwYkKdqxVa6XMz3Vw1fh+2gJaaVgTM6Jk3o8RccAKWKtlhT1US5sWrFh+KKxsVUALURSIA==", - "optional": true, - "requires": { - "bitsyntax": "~0.0.4", - "bluebird": "^3.4.6", - "buffer-more-ints": "0.0.2", - "readable-stream": "1.x >=1.1.9", - "safe-buffer": "^5.0.1" - } - }, "ansi-escapes": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.1.0.tgz", @@ -107,12 +91,14 @@ "ansi-regex": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true }, "ansi-styles": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=" + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true }, "argparse": { "version": "1.0.10", @@ -165,51 +151,32 @@ "asn1": { "version": "0.2.3", "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz", - "integrity": "sha1-2sh4dxPJlmhJ/IGAd36+nB3fO4Y=" + "integrity": "sha1-2sh4dxPJlmhJ/IGAd36+nB3fO4Y=", + "dev": true }, "assert-plus": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" - }, - "ast-types": { - "version": "0.11.5", - "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.11.5.tgz", - "integrity": "sha512-oJjo+5e7/vEc2FBK8gUalV0pba4L3VdBIs2EKhOLHLcOd2FgQIVQN9xb0eZ9IjEWyAL7vq6fGJxOvVvdCHNyMw==", - "optional": true - }, - "async": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/async/-/async-2.6.1.tgz", - "integrity": "sha512-fNEiL2+AZt6AlAw/29Cr0UDe4sRAHCpEHh54WMz+Bb7QfNcFw4h3loofyJpLeQs4Yx7yuqu/2dLgM5hKOs6HlQ==", - "optional": true, - "requires": { - "lodash": "^4.17.10" - } + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", + "dev": true }, "asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", + "dev": true }, "aws-sign2": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", - "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=" + "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=", + "dev": true }, "aws4": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.7.0.tgz", - "integrity": "sha512-32NDda82rhwD9/JBCCkB+MRYDp0oSvlo2IL6rQWA10PQi7tDUM3eqMSltXmY+Oyl/7N3P3qNtAlv7X0d9bI28w==" - }, - "axios": { - "version": "0.15.3", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.15.3.tgz", - "integrity": "sha1-LJ1jiy4ZGgjqHWzJiOrda6W9wFM=", - "optional": true, - "requires": { - "follow-redirects": "1.0.0" - } + "integrity": "sha512-32NDda82rhwD9/JBCCkB+MRYDp0oSvlo2IL6rQWA10PQi7tDUM3eqMSltXmY+Oyl/7N3P3qNtAlv7X0d9bI28w==", + "dev": true }, "babel-code-frame": { "version": "6.26.0", @@ -232,6 +199,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz", "integrity": "sha1-Y7xdy2EzG5K8Bf1SiVPDNGKgb40=", + "dev": true, "optional": true, "requires": { "tweetnacl": "^0.14.3" @@ -243,58 +211,11 @@ "integrity": "sha512-3/qRXczDi2Cdbz6jE+W3IflJOutRVica8frpBn14de1mBOkzDo+6tY33kNhvkw54Kn3PzRRD2VnGbGPcTAk4sw==", "dev": true }, - "bitsyntax": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/bitsyntax/-/bitsyntax-0.0.4.tgz", - "integrity": "sha1-6xDMb4K4xJDj6FaY8H6D1G4MuoI=", - "optional": true, - "requires": { - "buffer-more-ints": "0.0.2" - } - }, - "bl": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/bl/-/bl-1.1.2.tgz", - "integrity": "sha1-/cqHGplxOqANGeO7ukHER4emU5g=", - "optional": true, - "requires": { - "readable-stream": "~2.0.5" - }, - "dependencies": { - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "optional": true - }, - "readable-stream": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.0.6.tgz", - "integrity": "sha1-j5A0HmilPMySh4jaz80Rs265t44=", - "optional": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.1", - "isarray": "~1.0.0", - "process-nextick-args": "~1.0.6", - "string_decoder": "~0.10.x", - "util-deprecate": "~1.0.1" - } - } - } - }, "bluebird": { "version": "3.5.1", "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.1.tgz", - "integrity": "sha512-MKiLiV+I1AA596t9w1sQJ8jkiSr5+ZKi0WKrYGUn6d1Fx+Ij4tIj+m2WMQSGczs5jZVxV339chE8iwk6F64wjA==" - }, - "boom": { - "version": "2.10.1", - "resolved": "https://registry.npmjs.org/boom/-/boom-2.10.1.tgz", - "integrity": "sha1-OciRjO/1eZ+D+UkqhI9iWt0Mdm8=", - "requires": { - "hoek": "2.x.x" - } + "integrity": "sha512-MKiLiV+I1AA596t9w1sQJ8jkiSr5+ZKi0WKrYGUn6d1Fx+Ij4tIj+m2WMQSGczs5jZVxV339chE8iwk6F64wjA==", + "dev": true }, "brace-expansion": { "version": "1.1.11", @@ -312,38 +233,12 @@ "integrity": "sha512-c5mRlguI/Pe2dSZmpER62rSCu0ryKmWddzRYsuXc50U2/g8jMOulc31VZMa4mYx31U5xsmSOpDCgH88Vl9cDGQ==", "dev": true }, - "buffer-more-ints": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/buffer-more-ints/-/buffer-more-ints-0.0.2.tgz", - "integrity": "sha1-JrOIXRD6E9t/wBquOquHAZngEkw=" - }, - "buildmail": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/buildmail/-/buildmail-4.0.1.tgz", - "integrity": "sha1-h393OLeHKYccmhBeO4N9K+EaenI=", - "optional": true, - "requires": { - "addressparser": "1.0.1", - "libbase64": "0.1.0", - "libmime": "3.0.0", - "libqp": "1.1.0", - "nodemailer-fetch": "1.6.0", - "nodemailer-shared": "1.1.0", - "punycode": "1.4.1" - } - }, "builtin-modules": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=", "dev": true }, - "bytes": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", - "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=", - "optional": true - }, "caller-path": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-0.1.0.tgz", @@ -388,7 +283,8 @@ "caseless": { "version": "0.12.0", "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" + "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", + "dev": true }, "center-align": { "version": "0.1.3", @@ -405,6 +301,7 @@ "version": "1.1.3", "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, "requires": { "ansi-styles": "^2.2.1", "escape-string-regexp": "^1.0.2", @@ -475,7 +372,8 @@ "co": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", - "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=" + "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", + "dev": true }, "codecov": { "version": "3.0.2", @@ -519,16 +417,11 @@ "version": "1.0.6", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.6.tgz", "integrity": "sha1-cj599ugBrFYTETp+RFqbactjKBg=", + "dev": true, "requires": { "delayed-stream": "~1.0.0" } }, - "commander": { - "version": "2.15.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.15.1.tgz", - "integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==", - "optional": true - }, "compare-func": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/compare-func/-/compare-func-1.3.2.tgz", @@ -816,15 +709,6 @@ "which": "^1.2.9" } }, - "cryptiles": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-2.0.5.tgz", - "integrity": "sha1-O9/s3GCBR8HGcgL6KR59ylnqo7g=", - "optional": true, - "requires": { - "boom": "2.x.x" - } - }, "currently-unhandled": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz", @@ -847,16 +731,11 @@ "version": "1.14.1", "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", + "dev": true, "requires": { "assert-plus": "^1.0.0" } }, - "data-uri-to-buffer": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-1.2.0.tgz", - "integrity": "sha512-vKQ9DTQPN1FLYiiEEOQ6IBGFqvjCa5rSK3cWMy/Nespm5d/x3dGFT9UBZnkLxCwua/IXBi2TYnwTEpsOvhC4UQ==", - "optional": true - }, "date-format": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/date-format/-/date-format-1.2.0.tgz", @@ -903,18 +782,8 @@ "deep-is": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", - "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=" - }, - "degenerator": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/degenerator/-/degenerator-1.0.4.tgz", - "integrity": "sha1-/PSQo37OJmRk2cxDGrmMWBnO0JU=", - "optional": true, - "requires": { - "ast-types": "0.x.x", - "escodegen": "1.x.x", - "esprima": "3.x.x" - } + "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", + "dev": true }, "del": { "version": "2.2.2", @@ -942,13 +811,8 @@ "delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" - }, - "depd": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", - "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", - "optional": true + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", + "dev": true }, "diff": { "version": "1.4.0", @@ -974,16 +838,11 @@ "is-obj": "^1.0.0" } }, - "double-ended-queue": { - "version": "2.1.0-0", - "resolved": "https://registry.npmjs.org/double-ended-queue/-/double-ended-queue-2.1.0-0.tgz", - "integrity": "sha1-ED01J/0xUo9AGIEwyEHv3XgmTlw=", - "optional": true - }, "ecc-jsbn": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz", "integrity": "sha1-D8c6ntXw1Tw4GTOYUj735UN3dQU=", + "dev": true, "optional": true, "requires": { "jsbn": "~0.1.0" @@ -998,36 +857,11 @@ "is-arrayish": "^0.2.1" } }, - "es6-promise": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.4.tgz", - "integrity": "sha512-/NdNZVJg+uZgtm9eS3O6lrOLYmQag2DjdEXuPaHlZ6RuVqgqaVZfgYCepEIKsLqwdQArOPtC3XzRLqGGfT8KQQ==" - }, - "es6-promisify": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz", - "integrity": "sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=", - "requires": { - "es6-promise": "^4.0.3" - } - }, "escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" - }, - "escodegen": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.10.0.tgz", - "integrity": "sha512-fjUOf8johsv23WuIKdNQU4P9t9jhQ4Qzx6pC2uW890OloK3Zs1ZAoCNpg/2larNF501jLl3UNy0kIRcF6VI22g==", - "optional": true, - "requires": { - "esprima": "^3.1.3", - "estraverse": "^4.2.0", - "esutils": "^2.0.2", - "optionator": "^0.8.1", - "source-map": "~0.6.1" - } + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true }, "eslint": { "version": "4.19.1", @@ -1306,11 +1140,6 @@ "acorn-jsx": "^3.0.0" } }, - "esprima": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-3.1.3.tgz", - "integrity": "sha1-/cpRzuYTOJXjyI1TXOSdv/YqRjM=" - }, "esquery": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.0.1.tgz", @@ -1332,12 +1161,14 @@ "estraverse": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz", - "integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=" + "integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=", + "dev": true }, "esutils": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", - "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=" + "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=", + "dev": true }, "events-to-array": { "version": "1.1.2", @@ -1348,7 +1179,8 @@ "extend": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.1.tgz", - "integrity": "sha1-p1Xqe8Gt/MWjHOfnYtuq3F5jZEQ=" + "integrity": "sha1-p1Xqe8Gt/MWjHOfnYtuq3F5jZEQ=", + "dev": true }, "external-editor": { "version": "2.2.0", @@ -1364,22 +1196,26 @@ "extsprintf": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", - "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" + "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", + "dev": true }, "fast-deep-equal": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz", - "integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=" + "integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=", + "dev": true }, "fast-json-stable-stringify": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", - "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=" + "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=", + "dev": true }, "fast-levenshtein": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=" + "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", + "dev": true }, "figures": { "version": "2.0.0", @@ -1400,12 +1236,6 @@ "object-assign": "^4.0.1" } }, - "file-uri-to-path": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", - "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", - "optional": true - }, "find-parent-dir": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/find-parent-dir/-/find-parent-dir-0.3.0.tgz", @@ -1459,26 +1289,6 @@ } } }, - "follow-redirects": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.0.0.tgz", - "integrity": "sha1-jjQpjL0uF28lTv/sdaHHjMhJ/Tc=", - "optional": true, - "requires": { - "debug": "^2.2.0" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "optional": true, - "requires": { - "ms": "2.0.0" - } - } - } - }, "foreground-child": { "version": "1.5.6", "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-1.5.6.tgz", @@ -1504,12 +1314,14 @@ "forever-agent": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", - "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=" + "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", + "dev": true }, "form-data": { "version": "2.3.2", "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.2.tgz", "integrity": "sha1-SXBJi+YEwgwAXU9cI67NIda0kJk=", + "dev": true, "requires": { "asynckit": "^0.4.0", "combined-stream": "1.0.6", @@ -1528,16 +1340,6 @@ "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", "dev": true }, - "ftp": { - "version": "0.3.10", - "resolved": "https://registry.npmjs.org/ftp/-/ftp-0.3.10.tgz", - "integrity": "sha1-kZfYYa2BQvPmPVqDv+TFn3MwiF0=", - "optional": true, - "requires": { - "readable-stream": "1.1.x", - "xregexp": "2.0.0" - } - }, "function-bind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", @@ -1556,21 +1358,6 @@ "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", "dev": true }, - "generate-function": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.0.0.tgz", - "integrity": "sha1-aFj+fAlpt9TpCTM3ZHrHn2DfvnQ=", - "optional": true - }, - "generate-object-property": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/generate-object-property/-/generate-object-property-1.2.0.tgz", - "integrity": "sha1-nA4cQDCM6AT0eDYYuTf6iPmdUNA=", - "optional": true, - "requires": { - "is-property": "^1.0.0" - } - }, "get-pkg-repo": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/get-pkg-repo/-/get-pkg-repo-1.4.0.tgz", @@ -1672,71 +1459,11 @@ "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=", "dev": true }, - "get-uri": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/get-uri/-/get-uri-2.0.2.tgz", - "integrity": "sha512-ZD325dMZOgerGqF/rF6vZXyFGTAay62svjQIT+X/oU2PtxYpFxvSkbsdi+oxIrsNxlZVd4y8wUDqkaExWTI/Cw==", - "optional": true, - "requires": { - "data-uri-to-buffer": "1", - "debug": "2", - "extend": "3", - "file-uri-to-path": "1", - "ftp": "~0.3.10", - "readable-stream": "2" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "optional": true, - "requires": { - "ms": "2.0.0" - } - }, - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "optional": true - }, - "process-nextick-args": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", - "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==", - "optional": true - }, - "readable-stream": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", - "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", - "optional": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "optional": true, - "requires": { - "safe-buffer": "~5.1.0" - } - } - } - }, "getpass": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "dev": true, "requires": { "assert-plus": "^1.0.0" } @@ -1871,12 +1598,14 @@ "har-schema": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", - "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=" + "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=", + "dev": true }, "har-validator": { "version": "5.0.3", "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.0.3.tgz", "integrity": "sha1-ukAsJmGU8VlW7xXg/PJCmT9qff0=", + "dev": true, "requires": { "ajv": "^5.1.0", "har-schema": "^2.0.0" @@ -1895,6 +1624,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", + "dev": true, "requires": { "ansi-regex": "^2.0.0" } @@ -1905,93 +1635,23 @@ "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", "dev": true }, - "hawk": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/hawk/-/hawk-3.1.3.tgz", - "integrity": "sha1-B4REvXwWQLD+VA0sm3PVlnjo4cQ=", - "optional": true, - "requires": { - "boom": "2.x.x", - "cryptiles": "2.x.x", - "hoek": "2.x.x", - "sntp": "1.x.x" - } - }, - "hipchat-notifier": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/hipchat-notifier/-/hipchat-notifier-1.1.0.tgz", - "integrity": "sha1-ttJJdVQ3wZEII2d5nTupoPI7Ix4=", - "optional": true, - "requires": { - "lodash": "^4.0.0", - "request": "^2.0.0" - } - }, - "hoek": { - "version": "2.16.3", - "resolved": "https://registry.npmjs.org/hoek/-/hoek-2.16.3.tgz", - "integrity": "sha1-ILt0A9POo5jpHcRxCo/xuCdKJe0=" - }, "hosted-git-info": { "version": "2.6.0", "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.6.0.tgz", "integrity": "sha512-lIbgIIQA3lz5XaB6vxakj6sDHADJiZadYEJB+FgA+C4nubM1NwcuvUr9EJPmnH1skZqpqUzWborWo8EIUi0Sdw==", "dev": true }, - "http-errors": { - "version": "1.6.3", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", - "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", - "optional": true, - "requires": { - "depd": "~1.1.2", - "inherits": "2.0.3", - "setprototypeof": "1.1.0", - "statuses": ">= 1.4.0 < 2" - } - }, - "http-proxy-agent": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-2.1.0.tgz", - "integrity": "sha512-qwHbBLV7WviBl0rQsOzH6o5lwyOIvwp/BdFnvVxXORldu5TmjFfjzBcWUWS5kWAZhmv+JtiDhSuQCp4sBfbIgg==", - "requires": { - "agent-base": "4", - "debug": "3.1.0" - } - }, "http-signature": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", + "dev": true, "requires": { "assert-plus": "^1.0.0", "jsprim": "^1.2.2", "sshpk": "^1.7.0" } }, - "httpntlm": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/httpntlm/-/httpntlm-1.6.1.tgz", - "integrity": "sha1-rQFScUOi6Hc8+uapb1hla7UqNLI=", - "requires": { - "httpreq": ">=0.4.22", - "underscore": "~1.7.0" - } - }, - "httpreq": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/httpreq/-/httpreq-0.4.24.tgz", - "integrity": "sha1-QzX/2CzZaWaKOUZckprGHWOTYn8=" - }, - "https-proxy-agent": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.1.tgz", - "integrity": "sha512-HPCTS1LW51bcyMYbxUIOO4HEOlQ1/1qRaFWcyxvwaqUS9TY88aoEuHUY33kuAh1YhVVaDQhLZsnPd+XNARWZlQ==", - "requires": { - "agent-base": "^4.1.0", - "debug": "^3.1.0" - } - }, "husky": { "version": "0.14.3", "resolved": "https://registry.npmjs.org/husky/-/husky-0.14.3.tgz", @@ -2007,6 +1667,7 @@ "version": "0.4.23", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz", "integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==", + "dev": true, "requires": { "safer-buffer": ">= 2.1.2 < 3" } @@ -2029,12 +1690,6 @@ "integrity": "sha1-Sl/W0nzDMvN+VBmlBNu4NxBckok=", "dev": true }, - "inflection": { - "version": "1.12.0", - "resolved": "https://registry.npmjs.org/inflection/-/inflection-1.12.0.tgz", - "integrity": "sha1-ogCTVlbW9fa8TcdQLhrstwMihBY=", - "optional": true - }, "inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", @@ -2124,11 +1779,6 @@ } } }, - "ip": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz", - "integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=" - }, "is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", @@ -2174,25 +1824,6 @@ "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", "dev": true }, - "is-my-ip-valid": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-my-ip-valid/-/is-my-ip-valid-1.0.0.tgz", - "integrity": "sha512-gmh/eWXROncUzRnIa1Ubrt5b8ep/MGSnfAUI3aRp+sqTCs1tv1Isl8d8F6JmkN3dXKc3ehZMrtiPN9eL03NuaQ==", - "optional": true - }, - "is-my-json-valid": { - "version": "2.17.2", - "resolved": "https://registry.npmjs.org/is-my-json-valid/-/is-my-json-valid-2.17.2.tgz", - "integrity": "sha512-IBhBslgngMQN8DDSppmgDv7RNrlFotuuDsKcrCP3+HbFaVivIBU7u9oiiErw8sH4ynx3+gOGQ3q2otkgiSi6kg==", - "optional": true, - "requires": { - "generate-function": "^2.0.0", - "generate-object-property": "^1.1.0", - "is-my-ip-valid": "^1.0.0", - "jsonpointer": "^4.0.0", - "xtend": "^4.0.0" - } - }, "is-obj": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", @@ -2235,24 +1866,12 @@ "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=", "dev": true }, - "is-property": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz", - "integrity": "sha1-V/4cTkhHTt1lsJkR8msc1Ald2oQ=", - "optional": true - }, "is-resolvable": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.1.0.tgz", "integrity": "sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg==", "dev": true }, - "is-stream": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", - "optional": true - }, "is-subset": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/is-subset/-/is-subset-0.1.1.tgz", @@ -2271,7 +1890,8 @@ "is-typedarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", + "dev": true }, "is-utf8": { "version": "0.2.1", @@ -2279,12 +1899,6 @@ "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=", "dev": true }, - "isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", - "optional": true - }, "isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", @@ -2294,7 +1908,8 @@ "isstream": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" + "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", + "dev": true }, "js-tokens": { "version": "3.0.2", @@ -2324,6 +1939,7 @@ "version": "0.1.1", "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", + "dev": true, "optional": true }, "json-parse-better-errors": { @@ -2335,12 +1951,14 @@ "json-schema": { "version": "0.2.3", "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", - "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=" + "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=", + "dev": true }, "json-schema-traverse": { "version": "0.3.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz", - "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=" + "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=", + "dev": true }, "json-stable-stringify-without-jsonify": { "version": "1.0.1", @@ -2351,7 +1969,8 @@ "json-stringify-safe": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", + "dev": true }, "jsonparse": { "version": "1.3.1", @@ -2359,16 +1978,11 @@ "integrity": "sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA=", "dev": true }, - "jsonpointer": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-4.0.1.tgz", - "integrity": "sha1-T9kss04OnbPInIYi7PUfm5eMbLk=", - "optional": true - }, "jsprim": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", + "dev": true, "requires": { "assert-plus": "1.0.0", "extsprintf": "1.3.0", @@ -2402,38 +2016,12 @@ "version": "0.3.0", "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", + "dev": true, "requires": { "prelude-ls": "~1.1.2", "type-check": "~0.3.2" } }, - "libbase64": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/libbase64/-/libbase64-0.1.0.tgz", - "integrity": "sha1-YjUag5VjrF/1vSbxL2Dpgwu3UeY=" - }, - "libmime": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/libmime/-/libmime-3.0.0.tgz", - "integrity": "sha1-UaGp50SOy9Ms2lRCFnW7IbwJPaY=", - "requires": { - "iconv-lite": "0.4.15", - "libbase64": "0.1.0", - "libqp": "1.1.0" - }, - "dependencies": { - "iconv-lite": { - "version": "0.4.15", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.15.tgz", - "integrity": "sha1-/iZaIYrGpXz+hUkn6dBMGYJe3es=" - } - } - }, - "libqp": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/libqp/-/libqp-1.1.0.tgz", - "integrity": "sha1-9ebgatdLeU+1tbZpiL9yjvHe2+g=" - }, "load-json-file": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", @@ -2459,7 +2047,8 @@ "lodash": { "version": "4.17.10", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.10.tgz", - "integrity": "sha512-UejweD1pDoXu+AD825lWwp4ZGtSwgnpZxb3JDViD7StjQz+Nb/6l093lx4OQ0foGWNRoc19mWy7BzL+UAK2iVg==" + "integrity": "sha512-UejweD1pDoXu+AD825lWwp4ZGtSwgnpZxb3JDViD7StjQz+Nb/6l093lx4OQ0foGWNRoc19mWy7BzL+UAK2iVg==", + "dev": true }, "lodash._reinterpolate": { "version": "3.0.0", @@ -2492,118 +2081,6 @@ "integrity": "sha512-U7KCmLdqsGHBLeWqYlFA0V0Sl6P08EE1ZrmA9cxjUE0WVqT9qnyVDPz1kzpFEP0jdJuFnasWIfSd7fsaNXkpbg==", "dev": true }, - "loggly": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/loggly/-/loggly-1.1.1.tgz", - "integrity": "sha1-Cg/B0/o6XsRP3HuJe+uipGlc6+4=", - "optional": true, - "requires": { - "json-stringify-safe": "5.0.x", - "request": "2.75.x", - "timespan": "2.3.x" - }, - "dependencies": { - "assert-plus": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.2.0.tgz", - "integrity": "sha1-104bh+ev/A24qttwIfP+SBAasjQ=", - "optional": true - }, - "aws-sign2": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.6.0.tgz", - "integrity": "sha1-FDQt0428yU0OW4fXY81jYSwOeU8=", - "optional": true - }, - "caseless": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.11.0.tgz", - "integrity": "sha1-cVuW6phBWTzDMGeSP17GDr2k99c=", - "optional": true - }, - "form-data": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.0.0.tgz", - "integrity": "sha1-bwrrrcxdoWwT4ezBETfYX5uIOyU=", - "optional": true, - "requires": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.5", - "mime-types": "^2.1.11" - } - }, - "har-validator": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-2.0.6.tgz", - "integrity": "sha1-zcvAgYgmWtEZtqWnyKtw7s+10n0=", - "optional": true, - "requires": { - "chalk": "^1.1.1", - "commander": "^2.9.0", - "is-my-json-valid": "^2.12.4", - "pinkie-promise": "^2.0.0" - } - }, - "http-signature": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.1.1.tgz", - "integrity": "sha1-33LiZwZs0Kxn+3at+OE0qPvPkb8=", - "optional": true, - "requires": { - "assert-plus": "^0.2.0", - "jsprim": "^1.2.2", - "sshpk": "^1.7.0" - } - }, - "node-uuid": { - "version": "1.4.8", - "resolved": "https://registry.npmjs.org/node-uuid/-/node-uuid-1.4.8.tgz", - "integrity": "sha1-sEDrCSOWivq/jTL7HxfxFn/auQc=", - "optional": true - }, - "qs": { - "version": "6.2.3", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.2.3.tgz", - "integrity": "sha1-HPyyXBCpsrSDBT/zn138kjOQjP4=", - "optional": true - }, - "request": { - "version": "2.75.0", - "resolved": "https://registry.npmjs.org/request/-/request-2.75.0.tgz", - "integrity": "sha1-0rgmiihtoT6qXQGt9dGMyQ9lfZM=", - "optional": true, - "requires": { - "aws-sign2": "~0.6.0", - "aws4": "^1.2.1", - "bl": "~1.1.2", - "caseless": "~0.11.0", - "combined-stream": "~1.0.5", - "extend": "~3.0.0", - "forever-agent": "~0.6.1", - "form-data": "~2.0.0", - "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", - "node-uuid": "~1.4.7", - "oauth-sign": "~0.8.1", - "qs": "~6.2.0", - "stringstream": "~0.0.4", - "tough-cookie": "~2.3.0", - "tunnel-agent": "~0.4.1" - } - }, - "tunnel-agent": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.4.3.tgz", - "integrity": "sha1-Y3PbdpCf5XDgjXNYM2Xtgop07us=", - "optional": true - } - } - }, "longest": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz", @@ -2624,38 +2101,12 @@ "version": "4.1.3", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.3.tgz", "integrity": "sha512-fFEhvcgzuIoJVUF8fYr5KR0YqxD238zgObTps31YdADwPPAp82a4M8TrckkWyx7ekNlf9aBcVn81cFwwXngrJA==", + "dev": true, "requires": { "pseudomap": "^1.0.2", "yallist": "^2.1.2" } }, - "mailcomposer": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/mailcomposer/-/mailcomposer-4.0.1.tgz", - "integrity": "sha1-DhxEsqB890DuF9wUm6AJ8Zyt/rQ=", - "optional": true, - "requires": { - "buildmail": "4.0.1", - "libmime": "3.0.0" - } - }, - "mailgun-js": { - "version": "0.18.0", - "resolved": "https://registry.npmjs.org/mailgun-js/-/mailgun-js-0.18.0.tgz", - "integrity": "sha512-o0P6jjZlx5CQj12tvVgDTbgjTqVN0+5h6/6P1+3c6xmozVKBwniQ6Qt3MkCSF0+ueVTbobAfWyGpWRZMJu8t1g==", - "optional": true, - "requires": { - "async": "~2.6.0", - "debug": "~3.1.0", - "form-data": "~2.3.0", - "inflection": "~1.12.0", - "is-stream": "^1.1.0", - "path-proxy": "~1.0.0", - "promisify-call": "^2.0.2", - "proxy-agent": "~3.0.0", - "tsscmp": "~1.0.0" - } - }, "map-obj": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-2.0.0.tgz", @@ -2711,12 +2162,14 @@ "mime-db": { "version": "1.33.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.33.0.tgz", - "integrity": "sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ==" + "integrity": "sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ==", + "dev": true }, "mime-types": { "version": "2.1.18", "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.18.tgz", "integrity": "sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ==", + "dev": true, "requires": { "mime-db": "~1.33.0" } @@ -2800,89 +2253,6 @@ "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", "dev": true }, - "netmask": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/netmask/-/netmask-1.0.6.tgz", - "integrity": "sha1-ICl+idhvb2QA8lDZ9Pa0wZRfzTU=", - "optional": true - }, - "nodemailer": { - "version": "2.7.2", - "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-2.7.2.tgz", - "integrity": "sha1-8kLmSa7q45tsftdA73sGHEBNMPk=", - "optional": true, - "requires": { - "libmime": "3.0.0", - "mailcomposer": "4.0.1", - "nodemailer-direct-transport": "3.3.2", - "nodemailer-shared": "1.1.0", - "nodemailer-smtp-pool": "2.8.2", - "nodemailer-smtp-transport": "2.7.2", - "socks": "1.1.9" - }, - "dependencies": { - "socks": { - "version": "1.1.9", - "resolved": "https://registry.npmjs.org/socks/-/socks-1.1.9.tgz", - "integrity": "sha1-Yo1+TQSRJDVEWsC25Fk3bLPm1pE=", - "optional": true, - "requires": { - "ip": "^1.1.2", - "smart-buffer": "^1.0.4" - } - } - } - }, - "nodemailer-direct-transport": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/nodemailer-direct-transport/-/nodemailer-direct-transport-3.3.2.tgz", - "integrity": "sha1-6W+vuQNYVglH5WkBfZfmBzilCoY=", - "optional": true, - "requires": { - "nodemailer-shared": "1.1.0", - "smtp-connection": "2.12.0" - } - }, - "nodemailer-fetch": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/nodemailer-fetch/-/nodemailer-fetch-1.6.0.tgz", - "integrity": "sha1-ecSQihwPXzdbc/6IjamCj23JY6Q=" - }, - "nodemailer-shared": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/nodemailer-shared/-/nodemailer-shared-1.1.0.tgz", - "integrity": "sha1-z1mU4v0mjQD1zw+nZ6CBae2wfsA=", - "requires": { - "nodemailer-fetch": "1.6.0" - } - }, - "nodemailer-smtp-pool": { - "version": "2.8.2", - "resolved": "https://registry.npmjs.org/nodemailer-smtp-pool/-/nodemailer-smtp-pool-2.8.2.tgz", - "integrity": "sha1-LrlNbPhXgLG0clzoU7nL1ejajHI=", - "optional": true, - "requires": { - "nodemailer-shared": "1.1.0", - "nodemailer-wellknown": "0.1.10", - "smtp-connection": "2.12.0" - } - }, - "nodemailer-smtp-transport": { - "version": "2.7.2", - "resolved": "https://registry.npmjs.org/nodemailer-smtp-transport/-/nodemailer-smtp-transport-2.7.2.tgz", - "integrity": "sha1-A9ccdjFPFKx9vHvwM6am0W1n+3c=", - "optional": true, - "requires": { - "nodemailer-shared": "1.1.0", - "nodemailer-wellknown": "0.1.10", - "smtp-connection": "2.12.0" - } - }, - "nodemailer-wellknown": { - "version": "0.1.10", - "resolved": "https://registry.npmjs.org/nodemailer-wellknown/-/nodemailer-wellknown-0.1.10.tgz", - "integrity": "sha1-WG24EB2zDLRDjrVGc3pBqtDPE9U=" - }, "normalize-package-data": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.4.0.tgz", @@ -2944,7 +2314,8 @@ "dependencies": { "align-text": { "version": "0.1.4", - "bundled": true, + "resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz", + "integrity": "sha1-DNkKVhCT810KmSVsIrcGlDP60Rc=", "dev": true, "requires": { "kind-of": "^3.0.2", @@ -2954,22 +2325,26 @@ }, "amdefine": { "version": "1.0.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", + "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=", "dev": true }, "ansi-regex": { "version": "2.1.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", "dev": true }, "ansi-styles": { "version": "2.2.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", "dev": true }, "append-transform": { "version": "0.4.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/append-transform/-/append-transform-0.4.0.tgz", + "integrity": "sha1-126/jKlNJ24keja61EpLdKthGZE=", "dev": true, "requires": { "default-require-extensions": "^1.0.0" @@ -2977,52 +2352,62 @@ }, "archy": { "version": "1.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz", + "integrity": "sha1-+cjBN1fMHde8N5rHeyxipcKGjEA=", "dev": true }, "arr-diff": { "version": "4.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", + "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", "dev": true }, "arr-flatten": { "version": "1.1.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", + "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", "dev": true }, "arr-union": { "version": "3.1.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", + "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=", "dev": true }, "array-unique": { "version": "0.3.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", + "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", "dev": true }, "arrify": { "version": "1.0.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", + "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=", "dev": true }, "assign-symbols": { "version": "1.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", + "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=", "dev": true }, "async": { "version": "1.5.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", + "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=", "dev": true }, "atob": { "version": "2.1.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.1.tgz", + "integrity": "sha1-ri1acpR38onWDdf5amMUoi3Wwio=", "dev": true }, "babel-code-frame": { "version": "6.26.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", + "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=", "dev": true, "requires": { "chalk": "^1.1.3", @@ -3032,7 +2417,8 @@ }, "babel-generator": { "version": "6.26.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/babel-generator/-/babel-generator-6.26.1.tgz", + "integrity": "sha512-HyfwY6ApZj7BYTcJURpM5tznulaBvyio7/0d4zFOeMPUmfxkCjHocCuoLa2SAGzBI8AREcH3eP3758F672DppA==", "dev": true, "requires": { "babel-messages": "^6.23.0", @@ -3047,7 +2433,8 @@ }, "babel-messages": { "version": "6.23.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/babel-messages/-/babel-messages-6.23.0.tgz", + "integrity": "sha1-8830cDhYA1sqKVHG7F7fbGLyYw4=", "dev": true, "requires": { "babel-runtime": "^6.22.0" @@ -3055,7 +2442,8 @@ }, "babel-runtime": { "version": "6.26.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", + "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", "dev": true, "requires": { "core-js": "^2.4.0", @@ -3064,7 +2452,8 @@ }, "babel-template": { "version": "6.26.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/babel-template/-/babel-template-6.26.0.tgz", + "integrity": "sha1-3gPi0WOWsGn0bdn/+FIfsaDjXgI=", "dev": true, "requires": { "babel-runtime": "^6.26.0", @@ -3076,7 +2465,8 @@ }, "babel-traverse": { "version": "6.26.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/babel-traverse/-/babel-traverse-6.26.0.tgz", + "integrity": "sha1-RqnL1+3MYsjlwGTi0tjQ9ANXZu4=", "dev": true, "requires": { "babel-code-frame": "^6.26.0", @@ -3092,7 +2482,8 @@ }, "babel-types": { "version": "6.26.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/babel-types/-/babel-types-6.26.0.tgz", + "integrity": "sha1-o7Bz+Uq0nrb6Vc1lInozQ4BjJJc=", "dev": true, "requires": { "babel-runtime": "^6.26.0", @@ -3103,17 +2494,20 @@ }, "babylon": { "version": "6.18.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz", + "integrity": "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==", "dev": true }, "balanced-match": { "version": "1.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", "dev": true }, "base": { "version": "0.11.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", + "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", "dev": true, "requires": { "cache-base": "^1.0.1", @@ -3127,7 +2521,8 @@ "dependencies": { "define-property": { "version": "1.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", "dev": true, "requires": { "is-descriptor": "^1.0.0" @@ -3135,7 +2530,8 @@ }, "is-accessor-descriptor": { "version": "1.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", "dev": true, "requires": { "kind-of": "^6.0.0" @@ -3143,7 +2539,8 @@ }, "is-data-descriptor": { "version": "1.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", "dev": true, "requires": { "kind-of": "^6.0.0" @@ -3151,7 +2548,8 @@ }, "is-descriptor": { "version": "1.0.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", "dev": true, "requires": { "is-accessor-descriptor": "^1.0.0", @@ -3161,19 +2559,22 @@ }, "isobject": { "version": "3.0.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", "dev": true }, "kind-of": { "version": "6.0.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", + "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", "dev": true } } }, "brace-expansion": { "version": "1.1.11", - "bundled": true, + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, "requires": { "balanced-match": "^1.0.0", @@ -3182,7 +2583,8 @@ }, "braces": { "version": "2.3.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", "dev": true, "requires": { "arr-flatten": "^1.1.0", @@ -3199,7 +2601,8 @@ "dependencies": { "extend-shallow": { "version": "2.0.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { "is-extendable": "^0.1.0" @@ -3209,12 +2612,14 @@ }, "builtin-modules": { "version": "1.1.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", + "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=", "dev": true }, "cache-base": { "version": "1.0.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", + "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", "dev": true, "requires": { "collection-visit": "^1.0.0", @@ -3230,14 +2635,16 @@ "dependencies": { "isobject": { "version": "3.0.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", "dev": true } } }, "caching-transform": { "version": "1.0.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/caching-transform/-/caching-transform-1.0.1.tgz", + "integrity": "sha1-bb2y8g+Nj7znnz6U6dF0Lc31wKE=", "dev": true, "requires": { "md5-hex": "^1.2.0", @@ -3247,13 +2654,15 @@ }, "camelcase": { "version": "1.2.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz", + "integrity": "sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk=", "dev": true, "optional": true }, "center-align": { "version": "0.1.3", - "bundled": true, + "resolved": "https://registry.npmjs.org/center-align/-/center-align-0.1.3.tgz", + "integrity": "sha1-qg0yYptu6XIgBBHL1EYckHvCt60=", "dev": true, "optional": true, "requires": { @@ -3263,7 +2672,8 @@ }, "chalk": { "version": "1.1.3", - "bundled": true, + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", "dev": true, "requires": { "ansi-styles": "^2.2.1", @@ -3275,7 +2685,8 @@ }, "class-utils": { "version": "0.3.6", - "bundled": true, + "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", + "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", "dev": true, "requires": { "arr-union": "^3.1.0", @@ -3286,7 +2697,8 @@ "dependencies": { "define-property": { "version": "0.2.5", - "bundled": true, + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", "dev": true, "requires": { "is-descriptor": "^0.1.0" @@ -3294,14 +2706,16 @@ }, "isobject": { "version": "3.0.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", "dev": true } } }, "cliui": { "version": "2.1.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz", + "integrity": "sha1-S0dXYP+AJkx2LDoXGQMukcf+oNE=", "dev": true, "optional": true, "requires": { @@ -3312,7 +2726,8 @@ "dependencies": { "wordwrap": { "version": "0.0.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz", + "integrity": "sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8=", "dev": true, "optional": true } @@ -3320,12 +2735,14 @@ }, "code-point-at": { "version": "1.1.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", "dev": true }, "collection-visit": { "version": "1.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", + "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", "dev": true, "requires": { "map-visit": "^1.0.0", @@ -3334,37 +2751,44 @@ }, "commondir": { "version": "1.0.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", "dev": true }, "component-emitter": { "version": "1.2.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", + "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=", "dev": true }, "concat-map": { "version": "0.0.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", "dev": true }, "convert-source-map": { "version": "1.5.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.5.1.tgz", + "integrity": "sha1-uCeAl7m8IpNl3lxiz1/K7YtVmeU=", "dev": true }, "copy-descriptor": { "version": "0.1.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", + "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=", "dev": true }, "core-js": { "version": "2.5.6", - "bundled": true, + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.5.6.tgz", + "integrity": "sha512-lQUVfQi0aLix2xpyjrrJEvfuYCqPc/HwmTKsC/VNf8q0zsjX7SQZtp4+oRONN5Tsur9GDETPjj+Ub2iDiGZfSQ==", "dev": true }, "cross-spawn": { "version": "4.0.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-4.0.2.tgz", + "integrity": "sha1-e5JHYhwjrf3ThWAEqCPL45dCTUE=", "dev": true, "requires": { "lru-cache": "^4.0.1", @@ -3373,7 +2797,8 @@ }, "debug": { "version": "2.6.9", - "bundled": true, + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "dev": true, "requires": { "ms": "2.0.0" @@ -3381,22 +2806,26 @@ }, "debug-log": { "version": "1.0.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/debug-log/-/debug-log-1.0.1.tgz", + "integrity": "sha1-IwdjLUwEOCuN+KMvcLiVBG1SdF8=", "dev": true }, "decamelize": { "version": "1.2.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", "dev": true }, "decode-uri-component": { "version": "0.2.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", + "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", "dev": true }, "default-require-extensions": { "version": "1.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-1.0.0.tgz", + "integrity": "sha1-836hXT4T/9m0N9M+GnW1+5eHTLg=", "dev": true, "requires": { "strip-bom": "^2.0.0" @@ -3404,7 +2833,8 @@ }, "define-property": { "version": "2.0.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", + "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", "dev": true, "requires": { "is-descriptor": "^1.0.2", @@ -3413,7 +2843,8 @@ "dependencies": { "is-accessor-descriptor": { "version": "1.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", "dev": true, "requires": { "kind-of": "^6.0.0" @@ -3421,7 +2852,8 @@ }, "is-data-descriptor": { "version": "1.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", "dev": true, "requires": { "kind-of": "^6.0.0" @@ -3429,7 +2861,8 @@ }, "is-descriptor": { "version": "1.0.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", "dev": true, "requires": { "is-accessor-descriptor": "^1.0.0", @@ -3439,19 +2872,22 @@ }, "isobject": { "version": "3.0.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", "dev": true }, "kind-of": { "version": "6.0.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", + "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", "dev": true } } }, "detect-indent": { "version": "4.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-4.0.0.tgz", + "integrity": "sha1-920GQ1LN9Docts5hnE7jqUdd4gg=", "dev": true, "requires": { "repeating": "^2.0.0" @@ -3459,7 +2895,8 @@ }, "error-ex": { "version": "1.3.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.1.tgz", + "integrity": "sha1-+FWobOYa3E6GIcPNoh56dhLDqNw=", "dev": true, "requires": { "is-arrayish": "^0.2.1" @@ -3467,17 +2904,20 @@ }, "escape-string-regexp": { "version": "1.0.5", - "bundled": true, + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", "dev": true }, "esutils": { "version": "2.0.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", + "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=", "dev": true }, "execa": { "version": "0.7.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz", + "integrity": "sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=", "dev": true, "requires": { "cross-spawn": "^5.0.1", @@ -3491,7 +2931,8 @@ "dependencies": { "cross-spawn": { "version": "5.1.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", + "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", "dev": true, "requires": { "lru-cache": "^4.0.1", @@ -3503,7 +2944,8 @@ }, "expand-brackets": { "version": "2.1.4", - "bundled": true, + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", + "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", "dev": true, "requires": { "debug": "^2.3.3", @@ -3517,7 +2959,8 @@ "dependencies": { "define-property": { "version": "0.2.5", - "bundled": true, + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", "dev": true, "requires": { "is-descriptor": "^0.1.0" @@ -3525,7 +2968,8 @@ }, "extend-shallow": { "version": "2.0.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { "is-extendable": "^0.1.0" @@ -3535,7 +2979,8 @@ }, "extend-shallow": { "version": "3.0.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", "dev": true, "requires": { "assign-symbols": "^1.0.0", @@ -3544,7 +2989,8 @@ "dependencies": { "is-extendable": { "version": "1.0.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", "dev": true, "requires": { "is-plain-object": "^2.0.4" @@ -3554,7 +3000,8 @@ }, "extglob": { "version": "2.0.4", - "bundled": true, + "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", + "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", "dev": true, "requires": { "array-unique": "^0.3.2", @@ -3569,7 +3016,8 @@ "dependencies": { "define-property": { "version": "1.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", "dev": true, "requires": { "is-descriptor": "^1.0.0" @@ -3577,7 +3025,8 @@ }, "extend-shallow": { "version": "2.0.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { "is-extendable": "^0.1.0" @@ -3585,7 +3034,8 @@ }, "is-accessor-descriptor": { "version": "1.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", "dev": true, "requires": { "kind-of": "^6.0.0" @@ -3593,7 +3043,8 @@ }, "is-data-descriptor": { "version": "1.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", "dev": true, "requires": { "kind-of": "^6.0.0" @@ -3601,7 +3052,8 @@ }, "is-descriptor": { "version": "1.0.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", "dev": true, "requires": { "is-accessor-descriptor": "^1.0.0", @@ -3611,14 +3063,16 @@ }, "kind-of": { "version": "6.0.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", + "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", "dev": true } } }, "fill-range": { "version": "4.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", "dev": true, "requires": { "extend-shallow": "^2.0.1", @@ -3629,7 +3083,8 @@ "dependencies": { "extend-shallow": { "version": "2.0.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { "is-extendable": "^0.1.0" @@ -3639,7 +3094,8 @@ }, "find-cache-dir": { "version": "0.1.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-0.1.1.tgz", + "integrity": "sha1-yN765XyKUqinhPnjHFfHQumToLk=", "dev": true, "requires": { "commondir": "^1.0.1", @@ -3649,7 +3105,8 @@ }, "find-up": { "version": "2.1.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", "dev": true, "requires": { "locate-path": "^2.0.0" @@ -3657,12 +3114,14 @@ }, "for-in": { "version": "1.0.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", + "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", "dev": true }, "foreground-child": { "version": "1.5.6", - "bundled": true, + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-1.5.6.tgz", + "integrity": "sha1-T9ca0t/elnibmApcCilZN8svXOk=", "dev": true, "requires": { "cross-spawn": "^4", @@ -3671,7 +3130,8 @@ }, "fragment-cache": { "version": "0.2.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", + "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", "dev": true, "requires": { "map-cache": "^0.2.2" @@ -3679,27 +3139,32 @@ }, "fs.realpath": { "version": "1.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", "dev": true }, "get-caller-file": { "version": "1.0.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.2.tgz", + "integrity": "sha1-9wLmMSfn4jHBYKgMFVSstw1QR+U=", "dev": true }, "get-stream": { "version": "3.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", + "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=", "dev": true }, "get-value": { "version": "2.0.6", - "bundled": true, + "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", + "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=", "dev": true }, "glob": { "version": "7.1.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", + "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", "dev": true, "requires": { "fs.realpath": "^1.0.0", @@ -3712,17 +3177,20 @@ }, "globals": { "version": "9.18.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/globals/-/globals-9.18.0.tgz", + "integrity": "sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ==", "dev": true }, "graceful-fs": { "version": "4.1.11", - "bundled": true, + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", + "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=", "dev": true }, "handlebars": { "version": "4.0.11", - "bundled": true, + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.0.11.tgz", + "integrity": "sha1-Ywo13+ApS8KB7a5v/F0yn8eYLcw=", "dev": true, "requires": { "async": "^1.4.0", @@ -3733,7 +3201,8 @@ "dependencies": { "source-map": { "version": "0.4.4", - "bundled": true, + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz", + "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=", "dev": true, "requires": { "amdefine": ">=0.0.4" @@ -3743,7 +3212,8 @@ }, "has-ansi": { "version": "2.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", + "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", "dev": true, "requires": { "ansi-regex": "^2.0.0" @@ -3751,12 +3221,14 @@ }, "has-flag": { "version": "1.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", "dev": true }, "has-value": { "version": "1.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", + "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", "dev": true, "requires": { "get-value": "^2.0.6", @@ -3766,14 +3238,16 @@ "dependencies": { "isobject": { "version": "3.0.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", "dev": true } } }, "has-values": { "version": "1.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", + "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", "dev": true, "requires": { "is-number": "^3.0.0", @@ -3782,7 +3256,8 @@ "dependencies": { "is-number": { "version": "3.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", "dev": true, "requires": { "kind-of": "^3.0.2" @@ -3790,7 +3265,8 @@ "dependencies": { "kind-of": { "version": "3.2.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, "requires": { "is-buffer": "^1.1.5" @@ -3800,7 +3276,8 @@ }, "kind-of": { "version": "4.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", + "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", "dev": true, "requires": { "is-buffer": "^1.1.5" @@ -3810,17 +3287,20 @@ }, "hosted-git-info": { "version": "2.6.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.6.0.tgz", + "integrity": "sha512-lIbgIIQA3lz5XaB6vxakj6sDHADJiZadYEJB+FgA+C4nubM1NwcuvUr9EJPmnH1skZqpqUzWborWo8EIUi0Sdw==", "dev": true }, "imurmurhash": { "version": "0.1.4", - "bundled": true, + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", "dev": true }, "inflight": { "version": "1.0.6", - "bundled": true, + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", "dev": true, "requires": { "once": "^1.3.0", @@ -3829,12 +3309,14 @@ }, "inherits": { "version": "2.0.3", - "bundled": true, + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", "dev": true }, "invariant": { "version": "2.2.4", - "bundled": true, + "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", + "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", "dev": true, "requires": { "loose-envify": "^1.0.0" @@ -3842,12 +3324,14 @@ }, "invert-kv": { "version": "1.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz", + "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=", "dev": true }, "is-accessor-descriptor": { "version": "0.1.6", - "bundled": true, + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", "dev": true, "requires": { "kind-of": "^3.0.2" @@ -3855,17 +3339,20 @@ }, "is-arrayish": { "version": "0.2.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", "dev": true }, "is-buffer": { "version": "1.1.6", - "bundled": true, + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", "dev": true }, "is-builtin-module": { "version": "1.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-1.0.0.tgz", + "integrity": "sha1-VAVy0096wxGfj3bDDLwbHgN6/74=", "dev": true, "requires": { "builtin-modules": "^1.0.0" @@ -3873,7 +3360,8 @@ }, "is-data-descriptor": { "version": "0.1.4", - "bundled": true, + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", "dev": true, "requires": { "kind-of": "^3.0.2" @@ -3881,7 +3369,8 @@ }, "is-descriptor": { "version": "0.1.6", - "bundled": true, + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", "dev": true, "requires": { "is-accessor-descriptor": "^0.1.6", @@ -3891,19 +3380,22 @@ "dependencies": { "kind-of": { "version": "5.1.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", "dev": true } } }, "is-extendable": { "version": "0.1.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", "dev": true }, "is-finite": { "version": "1.0.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.0.2.tgz", + "integrity": "sha1-zGZ3aVYCvlUO8R6LSqYwU0K20Ko=", "dev": true, "requires": { "number-is-nan": "^1.0.0" @@ -3911,12 +3403,14 @@ }, "is-fullwidth-code-point": { "version": "2.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", "dev": true }, "is-number": { "version": "3.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", "dev": true, "requires": { "kind-of": "^3.0.2" @@ -3924,7 +3418,8 @@ }, "is-odd": { "version": "2.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/is-odd/-/is-odd-2.0.0.tgz", + "integrity": "sha512-OTiixgpZAT1M4NHgS5IguFp/Vz2VI3U7Goh4/HA1adtwyLtSBrxYlcSYkhpAE07s4fKEcjrFxyvtQBND4vFQyQ==", "dev": true, "requires": { "is-number": "^4.0.0" @@ -3932,14 +3427,16 @@ "dependencies": { "is-number": { "version": "4.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz", + "integrity": "sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==", "dev": true } } }, "is-plain-object": { "version": "2.0.4", - "bundled": true, + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", "dev": true, "requires": { "isobject": "^3.0.1" @@ -3947,49 +3444,58 @@ "dependencies": { "isobject": { "version": "3.0.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", "dev": true } } }, "is-stream": { "version": "1.1.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", "dev": true }, "is-utf8": { "version": "0.2.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", + "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=", "dev": true }, "is-windows": { "version": "1.0.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", + "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", "dev": true }, "isarray": { "version": "1.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", "dev": true }, "isexe": { "version": "2.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", "dev": true }, "isobject": { "version": "3.0.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", "dev": true }, "istanbul-lib-coverage": { "version": "1.2.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-1.2.0.tgz", + "integrity": "sha512-GvgM/uXRwm+gLlvkWHTjDAvwynZkL9ns15calTrmhGgowlwJBbWMYzWbKqE2DT6JDP1AFXKa+Zi0EkqNCUqY0A==", "dev": true }, "istanbul-lib-hook": { "version": "1.1.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/istanbul-lib-hook/-/istanbul-lib-hook-1.1.0.tgz", + "integrity": "sha512-U3qEgwVDUerZ0bt8cfl3dSP3S6opBoOtk3ROO5f2EfBr/SRiD9FQqzwaZBqFORu8W7O0EXpai+k7kxHK13beRg==", "dev": true, "requires": { "append-transform": "^0.4.0" @@ -3997,7 +3503,8 @@ }, "istanbul-lib-instrument": { "version": "1.10.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-1.10.1.tgz", + "integrity": "sha512-1dYuzkOCbuR5GRJqySuZdsmsNKPL3PTuyPevQfoCXJePT9C8y1ga75neU+Tuy9+yS3G/dgx8wgOmp2KLpgdoeQ==", "dev": true, "requires": { "babel-generator": "^6.18.0", @@ -4011,7 +3518,8 @@ }, "istanbul-lib-report": { "version": "1.1.3", - "bundled": true, + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-1.1.3.tgz", + "integrity": "sha512-D4jVbMDtT2dPmloPJS/rmeP626N5Pr3Rp+SovrPn1+zPChGHcggd/0sL29jnbm4oK9W0wHjCRsdch9oLd7cm6g==", "dev": true, "requires": { "istanbul-lib-coverage": "^1.1.2", @@ -4022,7 +3530,8 @@ "dependencies": { "supports-color": { "version": "3.2.3", - "bundled": true, + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", + "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", "dev": true, "requires": { "has-flag": "^1.0.0" @@ -4032,7 +3541,8 @@ }, "istanbul-lib-source-maps": { "version": "1.2.3", - "bundled": true, + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-1.2.3.tgz", + "integrity": "sha512-fDa0hwU/5sDXwAklXgAoCJCOsFsBplVQ6WBldz5UwaqOzmDhUK4nfuR7/G//G2lERlblUNJB8P6e8cXq3a7MlA==", "dev": true, "requires": { "debug": "^3.1.0", @@ -4044,7 +3554,8 @@ "dependencies": { "debug": { "version": "3.1.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", "dev": true, "requires": { "ms": "2.0.0" @@ -4054,7 +3565,8 @@ }, "istanbul-reports": { "version": "1.4.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-1.4.0.tgz", + "integrity": "sha512-OPzVo1fPZ2H+owr8q/LYKLD+vquv9Pj4F+dj808MdHbuQLD7S4ACRjcX+0Tne5Vxt2lxXvdZaL7v+FOOAV281w==", "dev": true, "requires": { "handlebars": "^4.0.3" @@ -4062,17 +3574,20 @@ }, "js-tokens": { "version": "3.0.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", + "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=", "dev": true }, "jsesc": { "version": "1.3.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-1.3.0.tgz", + "integrity": "sha1-RsP+yMGJKxKwgz25vHYiF226s0s=", "dev": true }, "kind-of": { "version": "3.2.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, "requires": { "is-buffer": "^1.1.5" @@ -4080,13 +3595,15 @@ }, "lazy-cache": { "version": "1.0.4", - "bundled": true, + "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz", + "integrity": "sha1-odePw6UEdMuAhF07O24dpJpEbo4=", "dev": true, "optional": true }, "lcid": { "version": "1.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", + "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=", "dev": true, "requires": { "invert-kv": "^1.0.0" @@ -4094,7 +3611,8 @@ }, "load-json-file": { "version": "1.1.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", + "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", "dev": true, "requires": { "graceful-fs": "^4.1.2", @@ -4106,7 +3624,8 @@ }, "locate-path": { "version": "2.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", "dev": true, "requires": { "p-locate": "^2.0.0", @@ -4115,24 +3634,28 @@ "dependencies": { "path-exists": { "version": "3.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", "dev": true } } }, "lodash": { "version": "4.17.10", - "bundled": true, + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.10.tgz", + "integrity": "sha512-UejweD1pDoXu+AD825lWwp4ZGtSwgnpZxb3JDViD7StjQz+Nb/6l093lx4OQ0foGWNRoc19mWy7BzL+UAK2iVg==", "dev": true }, "longest": { "version": "1.0.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz", + "integrity": "sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc=", "dev": true }, "loose-envify": { "version": "1.3.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.3.1.tgz", + "integrity": "sha1-0aitM/qc4OcT1l/dCsi3SNR4yEg=", "dev": true, "requires": { "js-tokens": "^3.0.0" @@ -4140,7 +3663,8 @@ }, "lru-cache": { "version": "4.1.3", - "bundled": true, + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.3.tgz", + "integrity": "sha512-fFEhvcgzuIoJVUF8fYr5KR0YqxD238zgObTps31YdADwPPAp82a4M8TrckkWyx7ekNlf9aBcVn81cFwwXngrJA==", "dev": true, "requires": { "pseudomap": "^1.0.2", @@ -4149,12 +3673,14 @@ }, "map-cache": { "version": "0.2.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", + "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=", "dev": true }, "map-visit": { "version": "1.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", + "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", "dev": true, "requires": { "object-visit": "^1.0.0" @@ -4162,7 +3688,8 @@ }, "md5-hex": { "version": "1.3.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/md5-hex/-/md5-hex-1.3.0.tgz", + "integrity": "sha1-0sSv6YPENwZiF5uMrRRSGRNQRsQ=", "dev": true, "requires": { "md5-o-matic": "^0.1.1" @@ -4170,12 +3697,14 @@ }, "md5-o-matic": { "version": "0.1.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/md5-o-matic/-/md5-o-matic-0.1.1.tgz", + "integrity": "sha1-givM1l4RfFFPqxdrJZRdVBAKA8M=", "dev": true }, "mem": { "version": "1.1.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/mem/-/mem-1.1.0.tgz", + "integrity": "sha1-Xt1StIXKHZAP5kiVUFOZoN+kX3Y=", "dev": true, "requires": { "mimic-fn": "^1.0.0" @@ -4183,7 +3712,8 @@ }, "merge-source-map": { "version": "1.1.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/merge-source-map/-/merge-source-map-1.1.0.tgz", + "integrity": "sha512-Qkcp7P2ygktpMPh2mCQZaf3jhN6D3Z/qVZHSdWvQ+2Ef5HgRAPBO57A77+ENm0CPx2+1Ce/MYKi3ymqdfuqibw==", "dev": true, "requires": { "source-map": "^0.6.1" @@ -4191,14 +3721,16 @@ "dependencies": { "source-map": { "version": "0.6.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true } } }, "micromatch": { "version": "3.1.10", - "bundled": true, + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", "dev": true, "requires": { "arr-diff": "^4.0.0", @@ -4218,19 +3750,22 @@ "dependencies": { "kind-of": { "version": "6.0.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", + "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", "dev": true } } }, "mimic-fn": { "version": "1.2.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", + "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", "dev": true }, "minimatch": { "version": "3.0.4", - "bundled": true, + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", "dev": true, "requires": { "brace-expansion": "^1.1.7" @@ -4238,12 +3773,14 @@ }, "minimist": { "version": "0.0.8", - "bundled": true, + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", "dev": true }, "mixin-deep": { "version": "1.3.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.1.tgz", + "integrity": "sha512-8ZItLHeEgaqEvd5lYBXfm4EZSFCX29Jb9K+lAHhDKzReKBQKj3R+7NOF6tjqYi9t4oI8VUfaWITJQm86wnXGNQ==", "dev": true, "requires": { "for-in": "^1.0.2", @@ -4252,7 +3789,8 @@ "dependencies": { "is-extendable": { "version": "1.0.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", "dev": true, "requires": { "is-plain-object": "^2.0.4" @@ -4262,7 +3800,8 @@ }, "mkdirp": { "version": "0.5.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", "dev": true, "requires": { "minimist": "0.0.8" @@ -4270,12 +3809,14 @@ }, "ms": { "version": "2.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", "dev": true }, "nanomatch": { "version": "1.2.9", - "bundled": true, + "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.9.tgz", + "integrity": "sha512-n8R9bS8yQ6eSXaV6jHUpKzD8gLsin02w1HSFiegwrs9E098Ylhw5jdyKPaYqvHknHaSCKTPp7C8dGCQ0q9koXA==", "dev": true, "requires": { "arr-diff": "^4.0.0", @@ -4294,24 +3835,28 @@ "dependencies": { "arr-diff": { "version": "4.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", + "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", "dev": true }, "array-unique": { "version": "0.3.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", + "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", "dev": true }, "kind-of": { "version": "6.0.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", + "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", "dev": true } } }, "normalize-package-data": { "version": "2.4.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.4.0.tgz", + "integrity": "sha512-9jjUFbTPfEy3R/ad/2oNbKtW9Hgovl5O1FvFWKkKblNXoN/Oou6+9+KKohPK13Yc3/TyunyWhJp6gvRNR/PPAw==", "dev": true, "requires": { "hosted-git-info": "^2.1.4", @@ -4322,7 +3867,8 @@ }, "npm-run-path": { "version": "2.0.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", + "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", "dev": true, "requires": { "path-key": "^2.0.0" @@ -4330,17 +3876,20 @@ }, "number-is-nan": { "version": "1.0.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", "dev": true }, "object-assign": { "version": "4.1.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", "dev": true }, "object-copy": { "version": "0.1.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", + "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", "dev": true, "requires": { "copy-descriptor": "^0.1.0", @@ -4350,7 +3899,8 @@ "dependencies": { "define-property": { "version": "0.2.5", - "bundled": true, + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", "dev": true, "requires": { "is-descriptor": "^0.1.0" @@ -4360,7 +3910,8 @@ }, "object-visit": { "version": "1.0.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", + "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", "dev": true, "requires": { "isobject": "^3.0.0" @@ -4368,14 +3919,16 @@ "dependencies": { "isobject": { "version": "3.0.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", "dev": true } } }, "object.pick": { "version": "1.3.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", + "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", "dev": true, "requires": { "isobject": "^3.0.1" @@ -4383,14 +3936,16 @@ "dependencies": { "isobject": { "version": "3.0.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", "dev": true } } }, "once": { "version": "1.4.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", "dev": true, "requires": { "wrappy": "1" @@ -4398,7 +3953,8 @@ }, "optimist": { "version": "0.6.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz", + "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=", "dev": true, "requires": { "minimist": "~0.0.1", @@ -4407,12 +3963,14 @@ }, "os-homedir": { "version": "1.0.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", + "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", "dev": true }, "os-locale": { "version": "2.1.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-2.1.0.tgz", + "integrity": "sha512-3sslG3zJbEYcaC4YVAvDorjGxc7tv6KVATnLPZONiljsUncvihe9BQoVCEs0RZ1kmf4Hk9OBqlZfJZWI4GanKA==", "dev": true, "requires": { "execa": "^0.7.0", @@ -4422,12 +3980,14 @@ }, "p-finally": { "version": "1.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", "dev": true }, "p-limit": { "version": "1.2.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.2.0.tgz", + "integrity": "sha512-Y/OtIaXtUPr4/YpMv1pCL5L5ed0rumAaAeBSj12F+bSlMdys7i8oQF/GUJmfpTS/QoaRrS/k6pma29haJpsMng==", "dev": true, "requires": { "p-try": "^1.0.0" @@ -4435,7 +3995,8 @@ }, "p-locate": { "version": "2.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", "dev": true, "requires": { "p-limit": "^1.1.0" @@ -4443,12 +4004,14 @@ }, "p-try": { "version": "1.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", "dev": true }, "parse-json": { "version": "2.2.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", + "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", "dev": true, "requires": { "error-ex": "^1.2.0" @@ -4456,12 +4019,14 @@ }, "pascalcase": { "version": "0.1.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", + "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=", "dev": true }, "path-exists": { "version": "2.1.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", + "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", "dev": true, "requires": { "pinkie-promise": "^2.0.0" @@ -4469,22 +4034,26 @@ }, "path-is-absolute": { "version": "1.0.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", "dev": true }, "path-key": { "version": "2.0.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", "dev": true }, "path-parse": { "version": "1.0.5", - "bundled": true, + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.5.tgz", + "integrity": "sha1-PBrfhx6pzWyUMbbqK9dKD/BVxME=", "dev": true }, "path-type": { "version": "1.1.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", + "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", "dev": true, "requires": { "graceful-fs": "^4.1.2", @@ -4494,17 +4063,20 @@ }, "pify": { "version": "2.3.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", "dev": true }, "pinkie": { "version": "2.0.4", - "bundled": true, + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", + "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=", "dev": true }, "pinkie-promise": { "version": "2.0.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", "dev": true, "requires": { "pinkie": "^2.0.0" @@ -4512,7 +4084,8 @@ }, "pkg-dir": { "version": "1.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-1.0.0.tgz", + "integrity": "sha1-ektQio1bstYp1EcFb/TpyTFM89Q=", "dev": true, "requires": { "find-up": "^1.0.0" @@ -4520,7 +4093,8 @@ "dependencies": { "find-up": { "version": "1.1.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", + "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", "dev": true, "requires": { "path-exists": "^2.0.0", @@ -4531,17 +4105,20 @@ }, "posix-character-classes": { "version": "0.1.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", + "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=", "dev": true }, "pseudomap": { "version": "1.0.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", + "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=", "dev": true }, "read-pkg": { "version": "1.1.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", + "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", "dev": true, "requires": { "load-json-file": "^1.0.0", @@ -4551,7 +4128,8 @@ }, "read-pkg-up": { "version": "1.0.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", + "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=", "dev": true, "requires": { "find-up": "^1.0.0", @@ -4560,7 +4138,8 @@ "dependencies": { "find-up": { "version": "1.1.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", + "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", "dev": true, "requires": { "path-exists": "^2.0.0", @@ -4571,12 +4150,14 @@ }, "regenerator-runtime": { "version": "0.11.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz", + "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==", "dev": true }, "regex-not": { "version": "1.0.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", + "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", "dev": true, "requires": { "extend-shallow": "^3.0.2", @@ -4585,17 +4166,20 @@ }, "repeat-element": { "version": "1.1.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.2.tgz", + "integrity": "sha1-7wiaF40Ug7quTZPrmLT55OEdmQo=", "dev": true }, "repeat-string": { "version": "1.6.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", + "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", "dev": true }, "repeating": { "version": "2.0.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz", + "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=", "dev": true, "requires": { "is-finite": "^1.0.0" @@ -4603,32 +4187,38 @@ }, "require-directory": { "version": "2.1.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", "dev": true }, "require-main-filename": { "version": "1.0.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", + "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=", "dev": true }, "resolve-from": { "version": "2.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-2.0.0.tgz", + "integrity": "sha1-lICrIOlP+h2egKgEx+oUdhGWa1c=", "dev": true }, "resolve-url": { "version": "0.2.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", + "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=", "dev": true }, "ret": { "version": "0.1.15", - "bundled": true, + "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", + "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", "dev": true }, "right-align": { "version": "0.1.3", - "bundled": true, + "resolved": "https://registry.npmjs.org/right-align/-/right-align-0.1.3.tgz", + "integrity": "sha1-YTObci/mo1FWiSENJOFMlhSGE+8=", "dev": true, "optional": true, "requires": { @@ -4637,7 +4227,8 @@ }, "rimraf": { "version": "2.6.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz", + "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==", "dev": true, "requires": { "glob": "^7.0.5" @@ -4645,7 +4236,8 @@ }, "safe-regex": { "version": "1.1.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", + "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", "dev": true, "requires": { "ret": "~0.1.10" @@ -4653,17 +4245,20 @@ }, "semver": { "version": "5.5.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz", + "integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==", "dev": true }, "set-blocking": { "version": "2.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", "dev": true }, "set-value": { "version": "2.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.0.tgz", + "integrity": "sha512-hw0yxk9GT/Hr5yJEYnHNKYXkIA8mVJgd9ditYZCe16ZczcaELYYcfvaXesNACk2O8O0nTiPQcQhGUQj8JLzeeg==", "dev": true, "requires": { "extend-shallow": "^2.0.1", @@ -4674,7 +4269,8 @@ "dependencies": { "extend-shallow": { "version": "2.0.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { "is-extendable": "^0.1.0" @@ -4684,7 +4280,8 @@ }, "shebang-command": { "version": "1.2.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", "dev": true, "requires": { "shebang-regex": "^1.0.0" @@ -4692,22 +4289,26 @@ }, "shebang-regex": { "version": "1.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", "dev": true }, "signal-exit": { "version": "3.0.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", + "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", "dev": true }, "slide": { "version": "1.1.6", - "bundled": true, + "resolved": "https://registry.npmjs.org/slide/-/slide-1.1.6.tgz", + "integrity": "sha1-VusCfWW00tzmyy4tMsTUr8nh1wc=", "dev": true }, "snapdragon": { "version": "0.8.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", + "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", "dev": true, "requires": { "base": "^0.11.1", @@ -4722,7 +4323,8 @@ "dependencies": { "define-property": { "version": "0.2.5", - "bundled": true, + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", "dev": true, "requires": { "is-descriptor": "^0.1.0" @@ -4730,7 +4332,8 @@ }, "extend-shallow": { "version": "2.0.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { "is-extendable": "^0.1.0" @@ -4740,7 +4343,8 @@ }, "snapdragon-node": { "version": "2.1.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", + "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", "dev": true, "requires": { "define-property": "^1.0.0", @@ -4750,7 +4354,8 @@ "dependencies": { "define-property": { "version": "1.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", "dev": true, "requires": { "is-descriptor": "^1.0.0" @@ -4758,7 +4363,8 @@ }, "is-accessor-descriptor": { "version": "1.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", "dev": true, "requires": { "kind-of": "^6.0.0" @@ -4766,7 +4372,8 @@ }, "is-data-descriptor": { "version": "1.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", "dev": true, "requires": { "kind-of": "^6.0.0" @@ -4774,7 +4381,8 @@ }, "is-descriptor": { "version": "1.0.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", "dev": true, "requires": { "is-accessor-descriptor": "^1.0.0", @@ -4784,19 +4392,22 @@ }, "isobject": { "version": "3.0.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", "dev": true }, "kind-of": { "version": "6.0.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", + "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", "dev": true } } }, "snapdragon-util": { "version": "3.0.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", + "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", "dev": true, "requires": { "kind-of": "^3.2.0" @@ -4804,12 +4415,14 @@ }, "source-map": { "version": "0.5.7", - "bundled": true, + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", "dev": true }, "source-map-resolve": { "version": "0.5.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.1.tgz", + "integrity": "sha512-0KW2wvzfxm8NCTb30z0LMNyPqWCdDGE2viwzUaucqJdkTRXtZiSY3I+2A6nVAjmdOy0I4gU8DwnVVGsk9jvP2A==", "dev": true, "requires": { "atob": "^2.0.0", @@ -4821,12 +4434,14 @@ }, "source-map-url": { "version": "0.4.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz", + "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=", "dev": true }, "spawn-wrap": { "version": "1.4.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/spawn-wrap/-/spawn-wrap-1.4.2.tgz", + "integrity": "sha512-vMwR3OmmDhnxCVxM8M+xO/FtIp6Ju/mNaDfCMMW7FDcLRTPFWUswec4LXJHTJE2hwTI9O0YBfygu4DalFl7Ylg==", "dev": true, "requires": { "foreground-child": "^1.5.6", @@ -4839,7 +4454,8 @@ }, "spdx-correct": { "version": "3.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.0.0.tgz", + "integrity": "sha512-N19o9z5cEyc8yQQPukRCZ9EUmb4HUpnrmaL/fxS2pBo2jbfcFRVuFZ/oFC+vZz0MNNk0h80iMn5/S6qGZOL5+g==", "dev": true, "requires": { "spdx-expression-parse": "^3.0.0", @@ -4848,12 +4464,14 @@ }, "spdx-exceptions": { "version": "2.1.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.1.0.tgz", + "integrity": "sha512-4K1NsmrlCU1JJgUrtgEeTVyfx8VaYea9J9LvARxhbHtVtohPs/gFGG5yy49beySjlIMhhXZ4QqujIZEfS4l6Cg==", "dev": true }, "spdx-expression-parse": { "version": "3.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz", + "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==", "dev": true, "requires": { "spdx-exceptions": "^2.1.0", @@ -4862,12 +4480,14 @@ }, "spdx-license-ids": { "version": "3.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.0.tgz", + "integrity": "sha512-2+EPwgbnmOIl8HjGBXXMd9NAu02vLjOO1nWw4kmeRDFyHn+M/ETfHxQUK0oXg8ctgVnl9t3rosNVsZ1jG61nDA==", "dev": true }, "split-string": { "version": "3.1.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", + "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", "dev": true, "requires": { "extend-shallow": "^3.0.0" @@ -4875,7 +4495,8 @@ }, "static-extend": { "version": "0.1.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", + "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", "dev": true, "requires": { "define-property": "^0.2.5", @@ -4884,7 +4505,8 @@ "dependencies": { "define-property": { "version": "0.2.5", - "bundled": true, + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", "dev": true, "requires": { "is-descriptor": "^0.1.0" @@ -4894,7 +4516,8 @@ }, "string-width": { "version": "2.1.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", "dev": true, "requires": { "is-fullwidth-code-point": "^2.0.0", @@ -4903,12 +4526,14 @@ "dependencies": { "ansi-regex": { "version": "3.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", "dev": true }, "strip-ansi": { "version": "4.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", "dev": true, "requires": { "ansi-regex": "^3.0.0" @@ -4918,7 +4543,8 @@ }, "strip-ansi": { "version": "3.0.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "dev": true, "requires": { "ansi-regex": "^2.0.0" @@ -4926,7 +4552,8 @@ }, "strip-bom": { "version": "2.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", + "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", "dev": true, "requires": { "is-utf8": "^0.2.0" @@ -4934,17 +4561,20 @@ }, "strip-eof": { "version": "1.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", + "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", "dev": true }, "supports-color": { "version": "2.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", "dev": true }, "test-exclude": { "version": "4.2.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-4.2.1.tgz", + "integrity": "sha512-qpqlP/8Zl+sosLxBcVKl9vYy26T9NPalxSzzCP/OY6K7j938ui2oKgo+kRZYfxAeIpLqpbVnsHq1tyV70E4lWQ==", "dev": true, "requires": { "arrify": "^1.0.1", @@ -4956,17 +4586,20 @@ "dependencies": { "arr-diff": { "version": "4.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", + "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", "dev": true }, "array-unique": { "version": "0.3.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", + "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", "dev": true }, "braces": { "version": "2.3.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", "dev": true, "requires": { "arr-flatten": "^1.1.0", @@ -4983,7 +4616,8 @@ "dependencies": { "extend-shallow": { "version": "2.0.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { "is-extendable": "^0.1.0" @@ -4993,7 +4627,8 @@ }, "expand-brackets": { "version": "2.1.4", - "bundled": true, + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", + "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", "dev": true, "requires": { "debug": "^2.3.3", @@ -5007,7 +4642,8 @@ "dependencies": { "define-property": { "version": "0.2.5", - "bundled": true, + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", "dev": true, "requires": { "is-descriptor": "^0.1.0" @@ -5015,7 +4651,8 @@ }, "extend-shallow": { "version": "2.0.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { "is-extendable": "^0.1.0" @@ -5023,7 +4660,8 @@ }, "is-accessor-descriptor": { "version": "0.1.6", - "bundled": true, + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", "dev": true, "requires": { "kind-of": "^3.0.2" @@ -5031,7 +4669,8 @@ "dependencies": { "kind-of": { "version": "3.2.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, "requires": { "is-buffer": "^1.1.5" @@ -5041,7 +4680,8 @@ }, "is-data-descriptor": { "version": "0.1.4", - "bundled": true, + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", "dev": true, "requires": { "kind-of": "^3.0.2" @@ -5049,7 +4689,8 @@ "dependencies": { "kind-of": { "version": "3.2.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, "requires": { "is-buffer": "^1.1.5" @@ -5059,7 +4700,8 @@ }, "is-descriptor": { "version": "0.1.6", - "bundled": true, + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", "dev": true, "requires": { "is-accessor-descriptor": "^0.1.6", @@ -5069,14 +4711,16 @@ }, "kind-of": { "version": "5.1.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", "dev": true } } }, "extglob": { "version": "2.0.4", - "bundled": true, + "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", + "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", "dev": true, "requires": { "array-unique": "^0.3.2", @@ -5091,7 +4735,8 @@ "dependencies": { "define-property": { "version": "1.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", "dev": true, "requires": { "is-descriptor": "^1.0.0" @@ -5099,7 +4744,8 @@ }, "extend-shallow": { "version": "2.0.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { "is-extendable": "^0.1.0" @@ -5109,7 +4755,8 @@ }, "fill-range": { "version": "4.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", "dev": true, "requires": { "extend-shallow": "^2.0.1", @@ -5120,7 +4767,8 @@ "dependencies": { "extend-shallow": { "version": "2.0.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { "is-extendable": "^0.1.0" @@ -5130,7 +4778,8 @@ }, "is-accessor-descriptor": { "version": "1.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", "dev": true, "requires": { "kind-of": "^6.0.0" @@ -5138,7 +4787,8 @@ }, "is-data-descriptor": { "version": "1.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", "dev": true, "requires": { "kind-of": "^6.0.0" @@ -5146,7 +4796,8 @@ }, "is-descriptor": { "version": "1.0.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", "dev": true, "requires": { "is-accessor-descriptor": "^1.0.0", @@ -5156,7 +4807,8 @@ }, "is-number": { "version": "3.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", "dev": true, "requires": { "kind-of": "^3.0.2" @@ -5164,7 +4816,8 @@ "dependencies": { "kind-of": { "version": "3.2.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, "requires": { "is-buffer": "^1.1.5" @@ -5174,17 +4827,20 @@ }, "isobject": { "version": "3.0.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", "dev": true }, "kind-of": { "version": "6.0.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", + "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", "dev": true }, "micromatch": { "version": "3.1.10", - "bundled": true, + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", "dev": true, "requires": { "arr-diff": "^4.0.0", @@ -5206,12 +4862,14 @@ }, "to-fast-properties": { "version": "1.0.3", - "bundled": true, + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.3.tgz", + "integrity": "sha1-uDVx+k2MJbguIxsG46MFXeTKGkc=", "dev": true }, "to-object-path": { "version": "0.3.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", + "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", "dev": true, "requires": { "kind-of": "^3.0.2" @@ -5219,7 +4877,8 @@ }, "to-regex": { "version": "3.0.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", + "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", "dev": true, "requires": { "define-property": "^2.0.2", @@ -5230,7 +4889,8 @@ }, "to-regex-range": { "version": "2.1.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", "dev": true, "requires": { "is-number": "^3.0.0", @@ -5239,7 +4899,8 @@ "dependencies": { "is-number": { "version": "3.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", "dev": true, "requires": { "kind-of": "^3.0.2" @@ -5249,12 +4910,14 @@ }, "trim-right": { "version": "1.0.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/trim-right/-/trim-right-1.0.1.tgz", + "integrity": "sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM=", "dev": true }, "uglify-js": { "version": "2.8.29", - "bundled": true, + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz", + "integrity": "sha1-KcVzMUgFe7Th913zW3qcty5qWd0=", "dev": true, "optional": true, "requires": { @@ -5265,7 +4928,8 @@ "dependencies": { "yargs": { "version": "3.10.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz", + "integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=", "dev": true, "optional": true, "requires": { @@ -5279,13 +4943,15 @@ }, "uglify-to-browserify": { "version": "1.0.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz", + "integrity": "sha1-bgkk1r2mta/jSeOabWMoUKD4grc=", "dev": true, "optional": true }, "union-value": { "version": "1.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.0.tgz", + "integrity": "sha1-XHHDTLW61dzr4+oM0IIHulqhrqQ=", "dev": true, "requires": { "arr-union": "^3.1.0", @@ -5296,7 +4962,8 @@ "dependencies": { "extend-shallow": { "version": "2.0.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { "is-extendable": "^0.1.0" @@ -5304,7 +4971,8 @@ }, "set-value": { "version": "0.4.3", - "bundled": true, + "resolved": "https://registry.npmjs.org/set-value/-/set-value-0.4.3.tgz", + "integrity": "sha1-fbCPnT0i3H945Trzw79GZuzfzPE=", "dev": true, "requires": { "extend-shallow": "^2.0.1", @@ -5317,7 +4985,8 @@ }, "unset-value": { "version": "1.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", + "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", "dev": true, "requires": { "has-value": "^0.3.1", @@ -5326,7 +4995,8 @@ "dependencies": { "has-value": { "version": "0.3.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", + "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", "dev": true, "requires": { "get-value": "^2.0.3", @@ -5336,7 +5006,8 @@ "dependencies": { "isobject": { "version": "2.1.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", + "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", "dev": true, "requires": { "isarray": "1.0.0" @@ -5346,24 +5017,28 @@ }, "has-values": { "version": "0.1.4", - "bundled": true, + "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", + "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=", "dev": true }, "isobject": { "version": "3.0.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", "dev": true } } }, "urix": { "version": "0.1.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", + "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", "dev": true }, "use": { "version": "3.1.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/use/-/use-3.1.0.tgz", + "integrity": "sha512-6UJEQM/L+mzC3ZJNM56Q4DFGLX/evKGRg15UJHGB9X5j5Z3AFbgZvjUh2yq/UJUY4U5dh7Fal++XbNg1uzpRAw==", "dev": true, "requires": { "kind-of": "^6.0.2" @@ -5371,14 +5046,16 @@ "dependencies": { "kind-of": { "version": "6.0.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", + "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", "dev": true } } }, "validate-npm-package-license": { "version": "3.0.3", - "bundled": true, + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.3.tgz", + "integrity": "sha512-63ZOUnL4SIXj4L0NixR3L1lcjO38crAbgrTpl28t8jjrfuiOBL5Iygm+60qPs/KsZGzPNg6Smnc/oY16QTjF0g==", "dev": true, "requires": { "spdx-correct": "^3.0.0", @@ -5387,7 +5064,8 @@ }, "which": { "version": "1.3.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/which/-/which-1.3.0.tgz", + "integrity": "sha512-xcJpopdamTuY5duC/KnTTNBraPK54YwpenP4lzxU8H91GudWpFv38u0CKjclE1Wi2EH2EDz5LRcHcKbCIzqGyg==", "dev": true, "requires": { "isexe": "^2.0.0" @@ -5395,23 +5073,27 @@ }, "which-module": { "version": "2.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", + "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", "dev": true }, "window-size": { "version": "0.1.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz", + "integrity": "sha1-VDjNLqk7IC76Ohn+iIeu58lPnJ0=", "dev": true, "optional": true }, "wordwrap": { "version": "0.0.3", - "bundled": true, + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz", + "integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc=", "dev": true }, "wrap-ansi": { "version": "2.1.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", + "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", "dev": true, "requires": { "string-width": "^1.0.1", @@ -5420,7 +5102,8 @@ "dependencies": { "is-fullwidth-code-point": { "version": "1.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", "dev": true, "requires": { "number-is-nan": "^1.0.0" @@ -5428,7 +5111,8 @@ }, "string-width": { "version": "1.0.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", "dev": true, "requires": { "code-point-at": "^1.0.0", @@ -5440,12 +5124,14 @@ }, "wrappy": { "version": "1.0.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", "dev": true }, "write-file-atomic": { "version": "1.3.4", - "bundled": true, + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-1.3.4.tgz", + "integrity": "sha1-+Aek8LHZ6ROuekgRLmzDrxmRtF8=", "dev": true, "requires": { "graceful-fs": "^4.1.11", @@ -5455,17 +5141,20 @@ }, "y18n": { "version": "3.2.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz", + "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=", "dev": true }, "yallist": { "version": "2.1.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", + "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", "dev": true }, "yargs": { "version": "11.1.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/yargs/-/yargs-11.1.0.tgz", + "integrity": "sha512-NwW69J42EsCSanF8kyn5upxvjp5ds+t3+udGBeTbFnERA+lF541DDpMawzo4z6W/QrzNM18D+BPMiOBibnFV5A==", "dev": true, "requires": { "cliui": "^4.0.0", @@ -5484,17 +5173,20 @@ "dependencies": { "ansi-regex": { "version": "3.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", "dev": true }, "camelcase": { "version": "4.1.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", + "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=", "dev": true }, "cliui": { "version": "4.1.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/cliui/-/cliui-4.1.0.tgz", + "integrity": "sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ==", "dev": true, "requires": { "string-width": "^2.1.1", @@ -5504,7 +5196,8 @@ }, "strip-ansi": { "version": "4.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", "dev": true, "requires": { "ansi-regex": "^3.0.0" @@ -5512,7 +5205,8 @@ }, "yargs-parser": { "version": "9.0.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-9.0.2.tgz", + "integrity": "sha1-nM9qQ0YP5O1Aqbto9I1DuKaMwHc=", "dev": true, "requires": { "camelcase": "^4.1.0" @@ -5522,7 +5216,8 @@ }, "yargs-parser": { "version": "8.1.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-8.1.0.tgz", + "integrity": "sha512-yP+6QqN8BmrgW2ggLtTbdrOyBNSI7zBa4IykmiV5R1wl1JWNxQvWhMfMdmzIYtKU7oP3OOInY/tl2ov3BDjnJQ==", "dev": true, "requires": { "camelcase": "^4.1.0" @@ -5530,7 +5225,8 @@ "dependencies": { "camelcase": { "version": "4.1.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", + "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=", "dev": true } } @@ -5540,7 +5236,8 @@ "oauth-sign": { "version": "0.8.2", "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz", - "integrity": "sha1-Rqarfwrq2N6unsBWV4C31O/rnUM=" + "integrity": "sha1-Rqarfwrq2N6unsBWV4C31O/rnUM=", + "dev": true }, "object-assign": { "version": "4.1.1", @@ -5594,6 +5291,7 @@ "version": "0.8.2", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz", "integrity": "sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=", + "dev": true, "requires": { "deep-is": "~0.1.3", "fast-levenshtein": "~2.0.4", @@ -5654,35 +5352,6 @@ "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", "dev": true }, - "pac-proxy-agent": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/pac-proxy-agent/-/pac-proxy-agent-2.0.2.tgz", - "integrity": "sha512-cDNAN1Ehjbf5EHkNY5qnRhGPUCp6SnpyVof5fRzN800QV1Y2OkzbH9rmjZkbBRa8igof903yOnjIl6z0SlAhxA==", - "optional": true, - "requires": { - "agent-base": "^4.2.0", - "debug": "^3.1.0", - "get-uri": "^2.0.0", - "http-proxy-agent": "^2.1.0", - "https-proxy-agent": "^2.2.1", - "pac-resolver": "^3.0.0", - "raw-body": "^2.2.0", - "socks-proxy-agent": "^3.0.0" - } - }, - "pac-resolver": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pac-resolver/-/pac-resolver-3.0.0.tgz", - "integrity": "sha512-tcc38bsjuE3XZ5+4vP96OfhOugrX+JcnpUbhfuc4LuXBLQhoTthOstZeoQJBDnQUDYzYmdImKsbz0xSl1/9qeA==", - "optional": true, - "requires": { - "co": "^4.6.0", - "degenerator": "^1.0.4", - "ip": "^1.1.5", - "netmask": "^1.0.6", - "thunkify": "^2.1.2" - } - }, "parse-github-repo-url": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/parse-github-repo-url/-/parse-github-repo-url-1.4.1.tgz", @@ -5723,23 +5392,6 @@ "integrity": "sha1-PBrfhx6pzWyUMbbqK9dKD/BVxME=", "dev": true }, - "path-proxy": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/path-proxy/-/path-proxy-1.0.0.tgz", - "integrity": "sha1-GOijaFn8nS8aU7SN7hOFQ8Ag3l4=", - "optional": true, - "requires": { - "inflection": "~1.3.0" - }, - "dependencies": { - "inflection": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/inflection/-/inflection-1.3.8.tgz", - "integrity": "sha1-y9Fg2p91sUw8xjV41POWeEvzAU4=", - "optional": true - } - } - }, "path-type": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", @@ -5752,7 +5404,8 @@ "performance-now": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" + "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", + "dev": true }, "pify": { "version": "3.0.0", @@ -5763,12 +5416,14 @@ "pinkie": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", - "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=" + "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=", + "dev": true }, "pinkie-promise": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", + "dev": true, "requires": { "pinkie": "^2.0.0" } @@ -5812,13 +5467,8 @@ "prelude-ls": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", - "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=" - }, - "process-nextick-args": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", - "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=", - "optional": true + "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", + "dev": true }, "progress": { "version": "2.0.0", @@ -5826,46 +5476,17 @@ "integrity": "sha1-ihvjZr+Pwj2yvSPxDG/pILQ4nR8=", "dev": true }, - "promisify-call": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/promisify-call/-/promisify-call-2.0.4.tgz", - "integrity": "sha1-1IwtRWUszM1SgB3ey9UzptS9X7o=", - "optional": true, - "requires": { - "with-callback": "^1.0.2" - } - }, - "proxy-agent": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-3.0.0.tgz", - "integrity": "sha512-g6n6vnk8fRf705ShN+FEXFG/SDJaW++lSs0d9KaJh4uBWW/wi7en4Cpo5VYQW3SZzAE121lhB/KLQrbURoubZw==", - "optional": true, - "requires": { - "agent-base": "^4.2.0", - "debug": "^3.1.0", - "http-proxy-agent": "^2.1.0", - "https-proxy-agent": "^2.2.1", - "lru-cache": "^4.1.2", - "pac-proxy-agent": "^2.0.1", - "proxy-from-env": "^1.0.0", - "socks-proxy-agent": "^3.0.0" - } - }, - "proxy-from-env": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.0.0.tgz", - "integrity": "sha1-M8UDmPcOp+uW0h97gXYwpVeRx+4=", - "optional": true - }, "pseudomap": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", - "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=" + "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=", + "dev": true }, "punycode": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", - "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", + "dev": true }, "q": { "version": "1.5.1", @@ -5876,7 +5497,8 @@ "qs": { "version": "6.5.2", "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", - "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" + "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", + "dev": true }, "quick-lru": { "version": "1.1.0", @@ -5884,18 +5506,6 @@ "integrity": "sha1-Q2CxfGETatOAeDl/8RQW4Ybc+7g=", "dev": true }, - "raw-body": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.3.tgz", - "integrity": "sha512-9esiElv1BrZoI3rCDuOuKCBRbuApGGaDPQfjSflGxdy4oyzqghxu6klEkkVIvBje+FF0BX9coEv8KqW6X/7njw==", - "optional": true, - "requires": { - "bytes": "3.0.0", - "http-errors": "1.6.3", - "iconv-lite": "0.4.23", - "unpipe": "1.0.0" - } - }, "read-pkg": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", @@ -5988,18 +5598,6 @@ } } }, - "readable-stream": { - "version": "1.1.14", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", - "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", - "optional": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.1", - "isarray": "0.0.1", - "string_decoder": "~0.10.x" - } - }, "redent": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/redent/-/redent-2.0.0.tgz", @@ -6010,29 +5608,6 @@ "strip-indent": "^2.0.0" } }, - "redis": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/redis/-/redis-2.8.0.tgz", - "integrity": "sha512-M1OkonEQwtRmZv4tEWF2VgpG0JWJ8Fv1PhlgT5+B+uNq2cA3Rt1Yt/ryoR+vQNOQcIEgdCdfH0jr3bDpihAw1A==", - "optional": true, - "requires": { - "double-ended-queue": "^2.1.0-0", - "redis-commands": "^1.2.0", - "redis-parser": "^2.6.0" - } - }, - "redis-commands": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/redis-commands/-/redis-commands-1.3.5.tgz", - "integrity": "sha512-foGF8u6MXGFF++1TZVC6icGXuMYPftKXt1FBT2vrfU9ZATNtZJ8duRC5d1lEfE8hyVe3jhelHGB91oB7I6qLsA==", - "optional": true - }, - "redis-parser": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-2.6.0.tgz", - "integrity": "sha1-Uu0J2srBCPGmMcB+m2mUHnoZUEs=", - "optional": true - }, "regexpp": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-1.1.0.tgz", @@ -6058,6 +5633,7 @@ "version": "2.87.0", "resolved": "https://registry.npmjs.org/request/-/request-2.87.0.tgz", "integrity": "sha512-fcogkm7Az5bsS6Sl0sibkbhcKsnyon/jV1kF3ajGmF0c8HrttdKTPRT9hieOaQHA5HEq6r8OyWOo/o781C1tNw==", + "dev": true, "requires": { "aws-sign2": "~0.7.0", "aws4": "^1.6.0", @@ -6081,18 +5657,6 @@ "uuid": "^3.1.0" } }, - "requestretry": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/requestretry/-/requestretry-1.13.0.tgz", - "integrity": "sha512-Lmh9qMvnQXADGAQxsXHP4rbgO6pffCfuR8XUBdP9aitJcLQJxhp7YZK4xAVYXnPJ5E52mwrfiKQtKonPL8xsmg==", - "optional": true, - "requires": { - "extend": "^3.0.0", - "lodash": "^4.15.0", - "request": "^2.74.0", - "when": "^3.7.7" - } - }, "require-like": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/require-like/-/require-like-0.1.2.tgz", @@ -6185,17 +5749,8 @@ "safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" - }, - "sandboxed-module": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/sandboxed-module/-/sandboxed-module-2.0.3.tgz", - "integrity": "sha1-x+VFkzm7y6KMUwPusz9ug4e/upY=", - "dev": true, - "requires": { - "require-like": "0.1.2", - "stack-trace": "0.0.9" - } + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true }, "semver": { "version": "5.5.0", @@ -6208,12 +5763,6 @@ "integrity": "sha1-kqSWkGX5xwxpR1PVUkj8aPj2Usk=", "dev": true }, - "setprototypeof": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", - "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==", - "optional": true - }, "shebang-command": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", @@ -6235,15 +5784,6 @@ "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", "dev": true }, - "slack-node": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/slack-node/-/slack-node-0.2.0.tgz", - "integrity": "sha1-3kuN3aqLeT9h29KTgQT9q/N9+jA=", - "optional": true, - "requires": { - "requestretry": "^1.2.2" - } - }, "slice-ansi": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-1.0.0.tgz", @@ -6253,51 +5793,11 @@ "is-fullwidth-code-point": "^2.0.0" } }, - "smart-buffer": { - "version": "1.1.15", - "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-1.1.15.tgz", - "integrity": "sha1-fxFLW2X6s+KjWqd1uxLw0cZJvxY=" - }, - "smtp-connection": { - "version": "2.12.0", - "resolved": "https://registry.npmjs.org/smtp-connection/-/smtp-connection-2.12.0.tgz", - "integrity": "sha1-1275EnyyPCJZ7bHoNJwujV4tdME=", - "requires": { - "httpntlm": "1.6.1", - "nodemailer-shared": "1.1.0" - } - }, - "sntp": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/sntp/-/sntp-1.0.9.tgz", - "integrity": "sha1-ZUEYTMkK7qbG57NeJlkIJEPGYZg=", - "optional": true, - "requires": { - "hoek": "2.x.x" - } - }, - "socks": { - "version": "1.1.10", - "resolved": "https://registry.npmjs.org/socks/-/socks-1.1.10.tgz", - "integrity": "sha1-W4t/x8jzQcU+0FbpKbe/Tei6e1o=", - "requires": { - "ip": "^1.1.4", - "smart-buffer": "^1.0.13" - } - }, - "socks-proxy-agent": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-3.0.1.tgz", - "integrity": "sha512-ZwEDymm204mTzvdqyUqOdovVr2YRd2NYskrYrF2LXyZ9qDiMAoFESGK8CRphiO7rtbo2Y757k2Nia3x2hGtalA==", - "requires": { - "agent-base": "^4.1.0", - "socks": "^1.1.10" - } - }, "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true }, "source-map-support": { "version": "0.5.6", @@ -6369,6 +5869,7 @@ "version": "1.14.2", "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.14.2.tgz", "integrity": "sha1-xvxhZIo9nE52T9P8306hBeSSupg=", + "dev": true, "requires": { "asn1": "~0.2.3", "assert-plus": "^1.0.0", @@ -6393,12 +5894,6 @@ "integrity": "sha1-1PM6tU6OOHeLDKXP07OvsS22hiA=", "dev": true }, - "statuses": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", - "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=", - "optional": true - }, "streamroller": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/streamroller/-/streamroller-0.7.0.tgz", @@ -6471,22 +5966,11 @@ } } }, - "string_decoder": { - "version": "0.10.31", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", - "optional": true - }, - "stringstream": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.6.tgz", - "integrity": "sha512-87GEBAkegbBcweToUrdzf3eLhWNg06FJTebl4BVJz/JgWy8CvEr9dRtX5qWphiynMSQlxxi+QqN0z5T32SLlhA==", - "optional": true - }, "strip-ansi": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, "requires": { "ansi-regex": "^2.0.0" } @@ -6512,7 +5996,8 @@ "supports-color": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=" + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true }, "table": { "version": "4.0.2", @@ -6752,18 +6237,6 @@ } } }, - "thunkify": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/thunkify/-/thunkify-2.1.2.tgz", - "integrity": "sha1-+qDp0jDFGsyVyhOjYawFyn4EVT0=", - "optional": true - }, - "timespan": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/timespan/-/timespan-2.3.0.tgz", - "integrity": "sha1-SQLOBAvRPYRcj1myfp1ZutbzmSk=", - "optional": true - }, "tmatch": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/tmatch/-/tmatch-3.1.0.tgz", @@ -6783,6 +6256,7 @@ "version": "2.3.4", "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.4.tgz", "integrity": "sha512-TZ6TTfI5NtZnuyy/Kecv+CnoROnyXn2DN97LontgQpCwsX2XyLYCC0ENhYkehSOwAp8rTQKc/NUIF7BkQ5rKLA==", + "dev": true, "requires": { "punycode": "^1.4.1" } @@ -6811,16 +6285,11 @@ "integrity": "sha512-ovCs24PGjmByVPr9tSIOs/yjUX9sJl0grEmOsj9dZA/UknQkgPOKcUqM84aSCvt9awHuhc/boMzTg3BHFalxWw==", "dev": true }, - "tsscmp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/tsscmp/-/tsscmp-1.0.5.tgz", - "integrity": "sha1-fcSjOvcVgatDN9qR2FylQn69mpc=", - "optional": true - }, "tunnel-agent": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", + "dev": true, "requires": { "safe-buffer": "^5.0.1" } @@ -6829,12 +6298,14 @@ "version": "0.14.5", "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", + "dev": true, "optional": true }, "type-check": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", + "dev": true, "requires": { "prelude-ls": "~1.1.2" } @@ -6879,11 +6350,6 @@ "dev": true, "optional": true }, - "underscore": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.7.0.tgz", - "integrity": "sha1-a7rwh3UA02vjTsqlhODbn+8DUgk=" - }, "unicode-length": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/unicode-length/-/unicode-length-1.0.3.tgz", @@ -6894,12 +6360,6 @@ "strip-ansi": "^3.0.1" } }, - "unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=", - "optional": true - }, "urlgrey": { "version": "0.4.4", "resolved": "https://registry.npmjs.org/urlgrey/-/urlgrey-0.4.4.tgz", @@ -6914,7 +6374,8 @@ "uuid": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.2.1.tgz", - "integrity": "sha512-jZnMwlb9Iku/O3smGWvZhauCf6cvvpKi4BKRiliS3cxnI+Gz9j5MEpTz2UFuXiKPJocb7gnsLHwiS05ige5BEA==" + "integrity": "sha512-jZnMwlb9Iku/O3smGWvZhauCf6cvvpKi4BKRiliS3cxnI+Gz9j5MEpTz2UFuXiKPJocb7gnsLHwiS05ige5BEA==", + "dev": true }, "validate-commit-msg": { "version": "2.14.0", @@ -6942,18 +6403,13 @@ "version": "1.10.0", "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", + "dev": true, "requires": { "assert-plus": "^1.0.0", "core-util-is": "1.0.2", "extsprintf": "^1.2.0" } }, - "when": { - "version": "3.7.8", - "resolved": "https://registry.npmjs.org/when/-/when-3.7.8.tgz", - "integrity": "sha1-xxMLan6gRpPoQs3J56Hyqjmjn4I=", - "optional": true - }, "which": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", @@ -6970,16 +6426,11 @@ "dev": true, "optional": true }, - "with-callback": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/with-callback/-/with-callback-1.0.2.tgz", - "integrity": "sha1-oJYpuakgAo1yFAT7Q1vc/1yRvCE=", - "optional": true - }, "wordwrap": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", - "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=" + "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=", + "dev": true }, "wrappy": { "version": "1.0.2", @@ -7007,21 +6458,17 @@ "signal-exit": "^3.0.2" } }, - "xregexp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/xregexp/-/xregexp-2.0.0.tgz", - "integrity": "sha1-UqY+VsoLhKfzpfPWGHLxJq16WUM=", - "optional": true - }, "xtend": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", - "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=" + "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=", + "dev": true }, "yallist": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", - "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=" + "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", + "dev": true }, "yapool": { "version": "1.0.0", diff --git a/package.json b/package.json index 662e066..2b30a41 100644 --- a/package.json +++ b/package.json @@ -25,7 +25,7 @@ "url": "http://github.com/log4js-node/log4js-node/issues" }, "engines": { - "node": ">=4.0" + "node": ">=6.0" }, "scripts": { "clean": "find test -type f ! -name '*.json' ! -name '*.js' ! -name '.eslintrc' -delete && rm *.log", @@ -33,9 +33,8 @@ "commitmsg": "validate-commit-msg", "posttest": "npm run clean", "pretest": "eslint 'lib/**/*.js' 'test/**/*.js'", - "test": "tap 'test/tap/**/*.js'", + "test": "tap 'test/tap/**/*.js' --cov", "typings": "tsc -p types/tsconfig.json", - "coverage": "tap 'test/tap/**/*.js' --cov", "codecov": "tap 'test/tap/**/*.js' --cov --coverage-report=lcov && codecov" }, "directories": { @@ -58,21 +57,11 @@ "eslint-plugin-import": "^2.11.0", "husky": "^0.14.3", "nyc": "^11.7.3", - "sandboxed-module": "^2.0.3", + "@log4js-node/sandboxed-module": "^2.1.0", "tap": "^11.1.5", "typescript": "^2.8.3", "validate-commit-msg": "^2.14.0" }, - "optionalDependencies": { - "hipchat-notifier": "^1.1.0", - "loggly": "^1.1.0", - "mailgun-js": "^0.18.0", - "nodemailer": "^2.5.0", - "redis": "^2.7.1", - "slack-node": "~0.2.0", - "axios": "^0.15.3", - "amqplib": "^0.5.2" - }, "browser": { "os": false }, diff --git a/test/sandbox-coverage.js b/test/sandbox-coverage.js index 8be97c8..7604fab 100644 --- a/test/sandbox-coverage.js +++ b/test/sandbox-coverage.js @@ -1,6 +1,6 @@ 'use strict'; -const sandbox = require('sandboxed-module'); +const sandbox = require('@log4js-node/sandboxed-module'); sandbox.configure({ sourceTransformers: { diff --git a/test/tap/LoggingEvent-test.js b/test/tap/LoggingEvent-test.js new file mode 100644 index 0000000..a30c186 --- /dev/null +++ b/test/tap/LoggingEvent-test.js @@ -0,0 +1,42 @@ +const test = require('tap').test; +const LoggingEvent = require('../../lib/LoggingEvent'); +const levels = require('../../lib/levels'); + +test('LoggingEvent', (batch) => { + batch.test('should serialise to JSON', (t) => { + const event = new LoggingEvent('cheese', levels.DEBUG, ['log message'], { user: 'bob' }); + // set the event date to a known value + event.startTime = new Date(Date.UTC(2018, 1, 4, 18, 30, 23, 10)); + const rehydratedEvent = JSON.parse(event.serialise()); + t.equal(rehydratedEvent.startTime, '2018-02-04T18:30:23.010Z'); + t.equal(rehydratedEvent.categoryName, 'cheese'); + t.equal(rehydratedEvent.level.levelStr, 'DEBUG'); + t.equal(rehydratedEvent.data.length, 1); + t.equal(rehydratedEvent.data[0], 'log message'); + t.equal(rehydratedEvent.context.user, 'bob'); + t.end(); + }); + + batch.test('should deserialise from JSON', (t) => { + const dehydratedEvent = `{ + "startTime": "2018-02-04T10:25:23.010Z", + "categoryName": "biscuits", + "level": { + "levelStr": "INFO" + }, + "data": [ "some log message", { "x": 1 } ], + "context": { "thing": "otherThing" } + }`; + const event = LoggingEvent.deserialise(dehydratedEvent); + t.type(event, LoggingEvent); + t.same(event.startTime, new Date(Date.UTC(2018, 1, 4, 10, 25, 23, 10))); + t.equal(event.categoryName, 'biscuits'); + t.same(event.level, levels.INFO); + t.equal(event.data[0], 'some log message'); + t.equal(event.data[1].x, 1); + t.equal(event.context.thing, 'otherThing'); + t.end(); + }); + + batch.end(); +}); diff --git a/test/tap/configuration-test.js b/test/tap/configuration-test.js index e3645c0..2eef979 100644 --- a/test/tap/configuration-test.js +++ b/test/tap/configuration-test.js @@ -1,7 +1,7 @@ 'use strict'; const test = require('tap').test; -const sandbox = require('sandboxed-module'); +const sandbox = require('@log4js-node/sandboxed-module'); const modulePath = 'some/path/to/mylog4js.json'; const pathsChecked = []; diff --git a/test/tap/configuration-validation-test.js b/test/tap/configuration-validation-test.js index 4b674fc..b4fc1c4 100644 --- a/test/tap/configuration-validation-test.js +++ b/test/tap/configuration-validation-test.js @@ -1,26 +1,25 @@ 'use strict'; const test = require('tap').test; -const Configuration = require('../../lib/configuration'); const util = require('util'); const path = require('path'); -const sandbox = require('sandboxed-module'); +const sandbox = require('@log4js-node/sandboxed-module'); +const log4js = require('../../lib/log4js'); +const configuration = require('../../lib/configuration'); +const debug = require('debug')('log4js:test.configuration-validation'); -function testAppender(label, deprecated) { - return { - configure: function (config, layouts, findAppender) { - return { - configureCalled: true, - type: config.type, - label: label, - config: config, - layouts: layouts, - findAppender: findAppender, - deprecated: deprecated - }; - } - }; -} +const testAppender = (label, result) => ({ + configure: function (config, layouts, findAppender) { + debug(`testAppender(${label}).configure called, with config: ${util.inspect(config)}`); + result.configureCalled = true; + result.type = config.type; + result.label = label; + result.config = config; + result.layouts = layouts; + result.findAppender = findAppender; + return { }; + } +}); test('log4js configuration validation', (batch) => { batch.test('should give error if config is just plain silly', (t) => { @@ -28,7 +27,7 @@ test('log4js configuration validation', (batch) => { const expectedError = new Error(`Problem with log4js configuration: (${util.inspect(config)}) - must be an object.`); t.throws( - () => new Configuration(config), + () => configuration.configure(config), expectedError ); }); @@ -39,7 +38,7 @@ test('log4js configuration validation', (batch) => { batch.test('should give error if config is an empty object', (t) => { const expectedError = new Error('Problem with log4js configuration: ({}) - must have a property "appenders" of type object.'); - t.throws(() => new Configuration({}), expectedError); + t.throws(() => log4js.configure({}), expectedError); t.end(); }); @@ -47,15 +46,15 @@ test('log4js configuration validation', (batch) => { const expectedError = new Error('Problem with log4js configuration: ({ categories: {} }) ' + '- must have a property "appenders" of type object.'); - t.throws(() => new Configuration({ categories: {} }), expectedError); + t.throws(() => log4js.configure({ categories: {} }), expectedError); t.end(); }); batch.test('should give error if config has no categories', (t) => { const expectedError = - new Error('Problem with log4js configuration: ({ appenders: {} }) ' + + new Error('Problem with log4js configuration: ({ appenders: { out: { type: \'stdout\' } } }) ' + '- must have a property "categories" of type object.'); - t.throws(() => new Configuration({ appenders: {} }), expectedError); + t.throws(() => log4js.configure({ appenders: { out: { type: 'stdout' } } }), expectedError); t.end(); }); @@ -64,7 +63,7 @@ test('log4js configuration validation', (batch) => { new Error('Problem with log4js configuration: ({ appenders: [], categories: [] })' + ' - must have a property "appenders" of type object.'); t.throws( - () => new Configuration({ appenders: [], categories: [] }), + () => log4js.configure({ appenders: [], categories: [] }), error ); t.end(); @@ -75,7 +74,7 @@ test('log4js configuration validation', (batch) => { new Error('Problem with log4js configuration: ({ appenders: { thing: \'cheese\' }, categories: {} })' + ' - appender "thing" is not valid (must be an object with property "type")'); t.throws( - () => new Configuration({ appenders: { thing: 'cheese' }, categories: {} }), + () => log4js.configure({ appenders: { thing: 'cheese' }, categories: {} }), error ); t.end(); @@ -85,7 +84,7 @@ test('log4js configuration validation', (batch) => { const error = new Error('Problem with log4js configuration: ({ appenders: {}, categories: {} })' + ' - must define at least one appender.'); t.throws( - () => new Configuration({ appenders: {}, categories: {} }), + () => log4js.configure({ appenders: {}, categories: {} }), error ); t.end(); @@ -96,7 +95,7 @@ test('log4js configuration validation', (batch) => { '({ appenders: { stdout: { type: \'stdout\' } },\n categories: { thing: \'cheese\' } })' + ' - category "thing" is not valid (must be an object with properties "appenders" and "level")'); t.throws( - () => new Configuration({ appenders: { stdout: { type: 'stdout' } }, categories: { thing: 'cheese' } }), + () => log4js.configure({ appenders: { stdout: { type: 'stdout' } }, categories: { thing: 'cheese' } }), error ); t.end(); @@ -108,7 +107,7 @@ test('log4js configuration validation', (batch) => { ' categories: { thing: { appenders: [ \'stdout\' ], level: \'ERROR\' } } })' + ' - must define a "default" category.'); t.throws( - () => new Configuration({ + () => log4js.configure({ appenders: { stdout: { type: 'stdout' } }, categories: { thing: { appenders: ['stdout'], level: 'ERROR' } } }), @@ -122,7 +121,7 @@ test('log4js configuration validation', (batch) => { new Error('Problem with log4js configuration: ({ appenders: { stdout: { type: \'stdout\' } }, categories: {} })' + ' - must define at least one category.'); t.throws( - () => new Configuration({ appenders: { stdout: { type: 'stdout' } }, categories: {} }), + () => log4js.configure({ appenders: { stdout: { type: 'stdout' } }, categories: {} }), error ); t.end(); @@ -134,7 +133,7 @@ test('log4js configuration validation', (batch) => { ' categories: { thing: { appenders: {}, level: \'ERROR\' } } })' + ' - category "thing" is not valid (appenders must be an array of appender names)'); t.throws( - () => new Configuration({ + () => log4js.configure({ appenders: { stdout: { type: 'stdout' } }, categories: { thing: { appenders: {}, level: 'ERROR' } } }), @@ -149,7 +148,7 @@ test('log4js configuration validation', (batch) => { ' categories: { thing: { appenders: [], level: \'ERROR\' } } })' + ' - category "thing" is not valid (appenders must contain at least one appender name)'); t.throws( - () => new Configuration({ + () => log4js.configure({ appenders: { stdout: { type: 'stdout' } }, categories: { thing: { appenders: [], level: 'ERROR' } } }), @@ -164,7 +163,7 @@ test('log4js configuration validation', (batch) => { ' categories: { thing: { appenders: [ \'cheese\' ], level: \'ERROR\' } } })' + ' - category "thing" is not valid (appender "cheese" is not defined)'); t.throws( - () => new Configuration({ + () => log4js.configure({ appenders: { stdout: { type: 'stdout' } }, categories: { thing: { appenders: ['cheese'], level: 'ERROR' } } }), @@ -180,7 +179,7 @@ test('log4js configuration validation', (batch) => { ' - category "default" is not valid (level "Biscuits" not recognised; ' + 'valid levels are ALL, TRACE, DEBUG, INFO, WARN, ERROR, FATAL, MARK, OFF)'); t.throws( - () => new Configuration({ + () => log4js.configure({ appenders: { stdout: { type: 'stdout' } }, categories: { default: { appenders: ['stdout'], level: 'Biscuits' } } }), @@ -195,7 +194,7 @@ test('log4js configuration validation', (batch) => { ' categories: { default: { appenders: [ \'thing\' ], level: \'ERROR\' } } })' + ' - appender "thing" is not valid (type "cheese" could not be found)'); t.throws( - () => new Configuration({ + () => log4js.configure({ appenders: { thing: { type: 'cheese' } }, categories: { default: { appenders: ['thing'], level: 'ERROR' } } }), @@ -205,207 +204,136 @@ test('log4js configuration validation', (batch) => { }); batch.test('should create appender instances', (t) => { - const SandboxedConfiguration = sandbox.require( - '../../lib/configuration', + const thing = {}; + const sandboxedLog4js = sandbox.require( + '../../lib/log4js', { - singleOnly: true, requires: { - cheese: testAppender('cheesy') - } + cheese: testAppender('cheesy', thing) + }, + ignoreMissing: true } ); - const config = new SandboxedConfiguration({ + sandboxedLog4js.configure({ appenders: { thing: { type: 'cheese' } }, categories: { default: { appenders: ['thing'], level: 'ERROR' } } }); - const thing = config.appenders.get('thing'); t.ok(thing.configureCalled); t.equal(thing.type, 'cheese'); t.end(); }); batch.test('should load appenders from core first', (t) => { - const SandboxedConfiguration = sandbox.require( - '../../lib/configuration', + const result = {}; + const sandboxedLog4js = sandbox.require( + '../../lib/log4js', { - singleOnly: true, requires: { - './appenders/cheese': testAppender('correct'), - cheese: testAppender('wrong') - } + './cheese': testAppender('correct', result), + cheese: testAppender('wrong', result) + }, + ignoreMissing: true } ); - const config = new SandboxedConfiguration({ + sandboxedLog4js.configure({ appenders: { thing: { type: 'cheese' } }, categories: { default: { appenders: ['thing'], level: 'ERROR' } } }); - const thing = config.appenders.get('thing'); - t.ok(thing.configureCalled); - t.equal(thing.type, 'cheese'); - t.equal(thing.label, 'correct'); + t.ok(result.configureCalled); + t.equal(result.type, 'cheese'); + t.equal(result.label, 'correct'); t.end(); }); batch.test('should load appenders relative to main file if not in core, or node_modules', (t) => { + const result = {}; const mainPath = path.dirname(require.main.filename); - const sandboxConfig = { singleOnly: true, requires: {} }; - sandboxConfig.requires[`${mainPath}/cheese`] = testAppender('correct'); + const sandboxConfig = { + ignoreMissing: true, + requires: {} + }; + sandboxConfig.requires[`${mainPath}/cheese`] = testAppender('correct', result); // add this one, because when we're running coverage the main path is a bit different sandboxConfig.requires[ `${path.join(mainPath, '../../node_modules/nyc/bin/cheese')}` - ] = testAppender('correct'); - const SandboxedConfiguration = sandbox.require('../../lib/configuration', sandboxConfig); + ] = testAppender('correct', result); + const sandboxedLog4js = sandbox.require('../../lib/log4js', sandboxConfig); - const config = new SandboxedConfiguration({ + sandboxedLog4js.configure({ appenders: { thing: { type: 'cheese' } }, categories: { default: { appenders: ['thing'], level: 'ERROR' } } }); - const thing = config.appenders.get('thing'); - t.ok(thing.configureCalled); - t.equal(thing.type, 'cheese'); - t.equal(thing.label, 'correct'); + t.ok(result.configureCalled); + t.equal(result.type, 'cheese'); + t.equal(result.label, 'correct'); t.end(); }); batch.test('should load appenders relative to process.cwd if not found in core, node_modules', (t) => { - const SandboxedConfiguration = sandbox.require( - '../../lib/configuration', + const result = {}; + const fakeProcess = new Proxy(process, { + get(target, key) { + if (key === 'cwd') { + return () => '/var/lib/cheese'; + } + + return target[key]; + } + }); + const sandboxedLog4js = sandbox.require( + '../../lib/log4js', { - singleOnly: true, + ignoreMissing: true, requires: { - '/var/lib/cheese/cheese': testAppender('correct'), + '/var/lib/cheese/cheese': testAppender('correct', result), }, globals: { - process: { cwd: () => '/var/lib/cheese', env: {} } + process: fakeProcess } } ); - const config = new SandboxedConfiguration({ + sandboxedLog4js.configure({ appenders: { thing: { type: 'cheese' } }, categories: { default: { appenders: ['thing'], level: 'ERROR' } } }); - const thing = config.appenders.get('thing'); - t.ok(thing.configureCalled); - t.equal(thing.type, 'cheese'); - t.equal(thing.label, 'correct'); + t.ok(result.configureCalled); + t.equal(result.type, 'cheese'); + t.equal(result.label, 'correct'); t.end(); }); batch.test('should pass config, layout, findAppender to appenders', (t) => { - const SandboxedConfiguration = sandbox.require( - '../../lib/configuration', + const result = {}; + const sandboxedLog4js = sandbox.require( + '../../lib/log4js', { - singleOnly: true, + ignoreMissing: true, requires: { - cheese: testAppender('cheesy') + cheese: testAppender('cheesy', result), + notCheese: testAppender('notCheesy', {}) } } ); - const config = new SandboxedConfiguration({ - appenders: { thing: { type: 'cheese', foo: 'bar' }, thing2: { type: 'cheese' } }, + sandboxedLog4js.configure({ + appenders: { thing: { type: 'cheese', foo: 'bar' }, thing2: { type: 'notCheese' } }, categories: { default: { appenders: ['thing'], level: 'ERROR' } } }); - const thing = config.appenders.get('thing'); - t.ok(thing.configureCalled); - t.equal(thing.type, 'cheese'); - t.equal(thing.config.foo, 'bar'); - t.type(thing.layouts, 'object'); - t.type(thing.layouts.basicLayout, 'function'); - t.type(thing.findAppender, 'function'); - t.type(thing.findAppender('thing2'), 'object'); - t.end(); - }); - - batch.test('should display deprecation warning if needed', (t) => { - let deprecationMessage; - const SandboxedConfiguration = sandbox.require( - '../../lib/configuration', - { - singleOnly: true, - requires: { - './appenders/gelf': testAppender('gelf', '@some/lib') - }, - globals: { - console: { - error: (msg) => { - deprecationMessage = msg; - } - } - } - } - ); - - const config = new SandboxedConfiguration({ - appenders: { thing: { type: 'gelf' } }, - categories: { default: { appenders: ['thing'], level: 'debug' } } - }); - - t.test('should output warning on console.error', (assert) => { - assert.equal( - deprecationMessage, - 'Appender "thing" uses a deprecated type "gelf", which will be removed in log4js v3. ' + - 'You should change it to use "@some/lib". ' + - 'To turn off this warning add "deprecationWarnings: false" to your config.' - ); - assert.end(); - }); - - t.test('should still return an appender', (assert) => { - const thing = config.appenders.get('thing'); - assert.ok(thing.configureCalled); - assert.equal(thing.type, 'gelf'); - assert.end(); - }); - - t.end(); - }); - - batch.test('should not display deprecation warning if turned off', (t) => { - let deprecationMessage; - const SandboxedConfiguration = sandbox.require( - '../../lib/configuration', - { - singleOnly: true, - requires: { - './appenders/gelf': testAppender('gelf', '@some/lib') - }, - globals: { - console: { - error: (msg) => { - deprecationMessage = msg; - } - } - } - } - ); - - const config = new SandboxedConfiguration({ - appenders: { thing: { type: 'gelf' } }, - categories: { default: { appenders: ['thing'], level: 'debug' } }, - deprecationWarnings: false - }); - - t.test('should not output warning on console.error', (assert) => { - assert.notOk(deprecationMessage); - assert.end(); - }); - - t.test('should still return an appender', (assert) => { - const thing = config.appenders.get('thing'); - assert.ok(thing.configureCalled); - assert.equal(thing.type, 'gelf'); - assert.end(); - }); - + t.ok(result.configureCalled); + t.equal(result.type, 'cheese'); + t.equal(result.config.foo, 'bar'); + t.type(result.layouts, 'object'); + t.type(result.layouts.basicLayout, 'function'); + t.type(result.findAppender, 'function'); + t.type(result.findAppender('thing2'), 'object'); t.end(); }); diff --git a/test/tap/connect-logger-test.js b/test/tap/connect-logger-test.js index 6c79d07..ead71de 100644 --- a/test/tap/connect-logger-test.js +++ b/test/tap/connect-logger-test.js @@ -4,7 +4,7 @@ const test = require('tap').test; const EE = require('events').EventEmitter; -const levels = require('../../lib/levels')(); +const levels = require('../../lib/levels'); class MockLogger { constructor() { @@ -58,13 +58,13 @@ function request(cl, method, url, code, reqHeaders, resHeaders) { } test('log4js connect logger', (batch) => { - const clm = require('../../lib/connect-logger')(levels); + const clm = require('../../lib/connect-logger'); batch.test('getConnectLoggerModule', (t) => { - t.type(clm, 'object', 'should return a connect logger factory'); + t.type(clm, 'function', 'should return a connect logger factory'); t.test('should take a log4js logger and return a "connect logger"', (assert) => { const ml = new MockLogger(); - const cl = clm.connectLogger(ml); + const cl = clm(ml); assert.type(cl, 'function'); assert.end(); @@ -72,7 +72,7 @@ test('log4js connect logger', (batch) => { t.test('log events', (assert) => { const ml = new MockLogger(); - const cl = clm.connectLogger(ml); + const cl = clm(ml); request(cl, 'GET', 'http://url', 200); const messages = ml.messages; @@ -89,7 +89,7 @@ test('log4js connect logger', (batch) => { t.test('log events with level below logging level', (assert) => { const ml = new MockLogger(); ml.level = levels.FATAL; - const cl = clm.connectLogger(ml); + const cl = clm(ml); request(cl, 'GET', 'http://url', 200); assert.type(ml.messages, 'Array'); @@ -100,7 +100,7 @@ test('log4js connect logger', (batch) => { t.test('log events with non-default level and custom format', (assert) => { const ml = new MockLogger(); ml.level = levels.INFO; - const cl = clm.connectLogger(ml, { level: levels.INFO, format: ':method :url' }); + const cl = clm(ml, { level: levels.INFO, format: ':method :url' }); request(cl, 'GET', 'http://url', 200); const messages = ml.messages; @@ -116,7 +116,7 @@ test('log4js connect logger', (batch) => { batch.test('logger with options as string', (t) => { const ml = new MockLogger(); ml.level = levels.INFO; - const cl = clm.connectLogger(ml, ':method :url'); + const cl = clm(ml, ':method :url'); request(cl, 'POST', 'http://meh', 200); const messages = ml.messages; @@ -127,7 +127,7 @@ test('log4js connect logger', (batch) => { batch.test('auto log levels', (t) => { const ml = new MockLogger(); ml.level = levels.INFO; - const cl = clm.connectLogger(ml, { level: 'auto', format: ':method :url' }); + const cl = clm(ml, { level: 'auto', format: ':method :url' }); request(cl, 'GET', 'http://meh', 200); request(cl, 'GET', 'http://meh', 201); request(cl, 'GET', 'http://meh', 302); @@ -161,7 +161,7 @@ test('log4js connect logger', (batch) => { batch.test('format using a function', (t) => { const ml = new MockLogger(); ml.level = levels.INFO; - const cl = clm.connectLogger(ml, () => 'I was called'); + const cl = clm(ml, () => 'I was called'); request(cl, 'GET', 'http://blah', 200); t.equal(ml.messages[0].message, 'I was called'); @@ -171,7 +171,7 @@ test('log4js connect logger', (batch) => { batch.test('format that includes request headers', (t) => { const ml = new MockLogger(); ml.level = levels.INFO; - const cl = clm.connectLogger(ml, ':req[Content-Type]'); + const cl = clm(ml, ':req[Content-Type]'); request( cl, 'GET', 'http://blah', 200, @@ -185,7 +185,7 @@ test('log4js connect logger', (batch) => { batch.test('format that includes response headers', (t) => { const ml = new MockLogger(); ml.level = levels.INFO; - const cl = clm.connectLogger(ml, ':res[Content-Type]'); + const cl = clm(ml, ':res[Content-Type]'); request( cl, 'GET', 'http://blah', 200, @@ -200,7 +200,7 @@ test('log4js connect logger', (batch) => { batch.test('log events with custom token', (t) => { const ml = new MockLogger(); ml.level = levels.INFO; - const cl = clm.connectLogger(ml, { + const cl = clm(ml, { level: levels.INFO, format: ':method :url :custom_string', tokens: [ @@ -221,7 +221,7 @@ test('log4js connect logger', (batch) => { batch.test('log events with custom override token', (t) => { const ml = new MockLogger(); ml.level = levels.INFO; - const cl = clm.connectLogger(ml, { + const cl = clm(ml, { level: levels.INFO, format: ':method :url :date', tokens: [ diff --git a/test/tap/connect-nolog-test.js b/test/tap/connect-nolog-test.js index 5404c34..1c81208 100644 --- a/test/tap/connect-nolog-test.js +++ b/test/tap/connect-nolog-test.js @@ -2,7 +2,7 @@ const test = require('tap').test; const EE = require('events').EventEmitter; -const levels = require('../../lib/levels')(); +const levels = require('../../lib/levels'); class MockLogger { constructor() { @@ -41,11 +41,11 @@ class MockResponse extends EE { } test('log4js connect logger', (batch) => { - const clm = require('../../lib/connect-logger')(levels); + const clm = require('../../lib/connect-logger'); batch.test('with nolog config', (t) => { const ml = new MockLogger(); - const cl = clm.connectLogger(ml, { nolog: '\\.gif' }); + const cl = clm(ml, { nolog: '\\.gif' }); t.beforeEach((done) => { ml.messages = []; done(); }); @@ -82,7 +82,7 @@ test('log4js connect logger', (batch) => { batch.test('nolog Strings', (t) => { const ml = new MockLogger(); - const cl = clm.connectLogger(ml, { nolog: '\\.gif|\\.jpe?g' }); + const cl = clm(ml, { nolog: '\\.gif|\\.jpe?g' }); t.beforeEach((done) => { ml.messages = []; done(); }); @@ -129,7 +129,7 @@ test('log4js connect logger', (batch) => { batch.test('nolog Array', (t) => { const ml = new MockLogger(); - const cl = clm.connectLogger(ml, { nolog: ['\\.gif', '\\.jpe?g'] }); + const cl = clm(ml, { nolog: ['\\.gif', '\\.jpe?g'] }); t.beforeEach((done) => { ml.messages = []; done(); }); @@ -176,7 +176,7 @@ test('log4js connect logger', (batch) => { batch.test('nolog RegExp', (t) => { const ml = new MockLogger(); - const cl = clm.connectLogger(ml, { nolog: /\.gif|\.jpe?g/ }); + const cl = clm(ml, { nolog: /\.gif|\.jpe?g/ }); t.beforeEach((done) => { ml.messages = []; done(); }); diff --git a/test/tap/consoleAppender-test.js b/test/tap/consoleAppender-test.js index 3e977b2..12c46d7 100644 --- a/test/tap/consoleAppender-test.js +++ b/test/tap/consoleAppender-test.js @@ -1,7 +1,7 @@ 'use strict'; const test = require('tap').test; -const sandbox = require('sandboxed-module'); +const sandbox = require('@log4js-node/sandboxed-module'); const consoleAppender = require('../../lib/appenders/console'); test('log4js console appender', (batch) => { diff --git a/test/tap/default-settings-test.js b/test/tap/default-settings-test.js index ee1c275..3538721 100644 --- a/test/tap/default-settings-test.js +++ b/test/tap/default-settings-test.js @@ -1,7 +1,7 @@ 'use strict'; const test = require('tap').test; -const sandbox = require('sandboxed-module'); +const sandbox = require('@log4js-node/sandboxed-module'); test('default settings', (t) => { const output = []; diff --git a/test/tap/file-sighup-test.js b/test/tap/file-sighup-test.js index b0a0a3c..b96d486 100644 --- a/test/tap/file-sighup-test.js +++ b/test/tap/file-sighup-test.js @@ -1,7 +1,7 @@ 'use strict'; const test = require('tap').test; -const sandbox = require('sandboxed-module'); +const sandbox = require('@log4js-node/sandboxed-module'); test('file appender SIGHUP', (t) => { let closeCalled = 0; diff --git a/test/tap/fileAppender-test.js b/test/tap/fileAppender-test.js index cb933a9..ad8439c 100644 --- a/test/tap/fileAppender-test.js +++ b/test/tap/fileAppender-test.js @@ -3,7 +3,7 @@ const test = require('tap').test; const fs = require('fs'); const path = require('path'); -const sandbox = require('sandboxed-module'); +const sandbox = require('@log4js-node/sandboxed-module'); const log4js = require('../../lib/log4js'); const zlib = require('zlib'); const EOL = require('os').EOL || '\n'; diff --git a/test/tap/gelfAppender-test.js b/test/tap/gelfAppender-test.js deleted file mode 100644 index b473ee4..0000000 --- a/test/tap/gelfAppender-test.js +++ /dev/null @@ -1,261 +0,0 @@ -'use strict'; - -const test = require('tap').test; -const sandbox = require('sandboxed-module'); -const realLayouts = require('../../lib/layouts'); - -const setupLogging = function (options, category, compressedLength) { - const fakeDgram = { - sent: false, - socket: { - packetLength: 0, - closed: false, - close: function (cb) { - this.closed = true; - if (cb) cb(); - }, - send: function (pkt, offset, pktLength, port, host) { - fakeDgram.sent = true; - this.packet = pkt; - this.offset = offset; - this.packetLength = pktLength; - this.port = port; - this.host = host; - } - }, - createSocket: function (type) { - this.type = type; - return this.socket; - } - }; - - const fakeZlib = { - gzip: function (objectToCompress, callback) { - fakeZlib.uncompressed = objectToCompress; - if (this.shouldError) { - callback({ stack: 'oh noes' }); - return; - } - - if (compressedLength) { - callback(null, { length: compressedLength }); - } else { - callback(null, "I've been compressed"); - } - } - }; - - let exitHandler; - - const fakeConsole = { - error: function (message) { - this.message = message; - } - }; - - const fakeLayouts = { - layout: function (type, opt) { - this.type = type; - this.options = opt; - return realLayouts.messagePassThroughLayout; - }, - messagePassThroughLayout: realLayouts.messagePassThroughLayout - }; - - const log4js = sandbox.require('../../lib/log4js', { - // singleOnly: true, - requires: { - dgram: fakeDgram, - zlib: fakeZlib, - './layouts': fakeLayouts - }, - globals: { - process: { - on: function (evt, handler) { - if (evt === 'exit') { - exitHandler = handler; - } - }, - removeListener: () => {}, - env: {}, - stderr: process.stderr - }, - console: fakeConsole - } - }); - - options = options || {}; - options.type = 'gelf'; - - log4js.configure({ - appenders: { gelf: options }, - categories: { default: { appenders: ['gelf'], level: 'debug' } } - }); - - return { - dgram: fakeDgram, - compress: fakeZlib, - exitHandler: exitHandler, - console: fakeConsole, - layouts: fakeLayouts, - logger: log4js.getLogger(category || 'gelf-test'), - log4js: log4js - }; -}; - -test('log4js gelfAppender', (batch) => { - batch.test('with default gelfAppender settings', (t) => { - const setup = setupLogging(); - setup.logger.info('This is a test'); - - const dgram = setup.dgram; - - t.test('dgram packet should be sent via udp to the localhost gelf server', (assert) => { - assert.equal(dgram.type, 'udp4'); - assert.equal(dgram.socket.host, 'localhost'); - assert.equal(dgram.socket.port, 12201); - assert.equal(dgram.socket.offset, 0); - assert.ok(dgram.socket.packetLength > 0, 'Received blank message'); - assert.equal(dgram.socket.packet, "I've been compressed"); - assert.end(); - }); - - const message = JSON.parse(setup.compress.uncompressed); - t.test('the uncompressed log message should be in the gelf format', (assert) => { - assert.equal(message.version, '1.1'); - assert.equal(message.host, require('os').hostname()); - assert.equal(message.level, 6); // INFO - assert.equal(message.short_message, 'This is a test'); - assert.end(); - }); - t.end(); - }); - - batch.test('with a message longer than 8k', (t) => { - const setup = setupLogging(undefined, undefined, 10240); - setup.logger.info('Blah.'); - - t.equal(setup.dgram.sent, false, 'the dgram packet should not be sent'); - t.end(); - }); - - batch.test('with a null log message', (t) => { - const setup = setupLogging(); - setup.logger.info(null); - - t.ok(setup.dgram.sent); - - const msg = JSON.parse(setup.compress.uncompressed); - t.equal(msg.level, 6); - t.equal(msg.short_message, 'null'); - t.end(); - }); - - batch.test('with non-default options', (t) => { - const setup = setupLogging({ - host: 'somewhere', - port: 12345, - hostname: 'cheese', - facility: 'nonsense' - }); - setup.logger.debug('Just testing.'); - - const dgram = setup.dgram; - t.test('the dgram packet should pick up the options', (assert) => { - assert.equal(dgram.socket.host, 'somewhere'); - assert.equal(dgram.socket.port, 12345); - assert.end(); - }); - - const message = JSON.parse(setup.compress.uncompressed); - t.test('the uncompressed packet should pick up the options', (assert) => { - assert.equal(message.host, 'cheese'); - assert.equal(message._facility, 'nonsense'); - assert.end(); - }); - - t.end(); - }); - - batch.test('on process.exit should close open sockets', (t) => { - const setup = setupLogging(); - setup.exitHandler(); - - t.ok(setup.dgram.socket.closed); - t.end(); - }); - - batch.test('on shutdown should close open sockets', (t) => { - const setup = setupLogging(); - setup.log4js.shutdown(() => { - t.ok(setup.dgram.socket.closed); - t.end(); - }); - }); - - batch.test('on zlib error should output to console.error', (t) => { - const setup = setupLogging(); - setup.compress.shouldError = true; - setup.logger.info('whatever'); - - t.equal(setup.console.message, 'oh noes'); - t.end(); - }); - - batch.test('with layout in configuration', (t) => { - const setup = setupLogging({ - layout: { - type: 'madeuplayout', - earlgrey: 'yes, please' - } - }); - - t.test('should pass options to layout', (assert) => { - assert.equal(setup.layouts.type, 'madeuplayout'); - assert.equal(setup.layouts.options.earlgrey, 'yes, please'); - assert.end(); - }); - t.end(); - }); - - batch.test('with custom fields options', (t) => { - const setup = setupLogging({ - host: 'somewhere', - port: 12345, - hostname: 'cheese', - facility: 'nonsense', - customFields: { - _every1: 'Hello every one', - _every2: 'Hello every two' - } - }); - const myFields = { - GELF: true, - _every2: 'Overwritten!', - _myField: 'This is my field!' - }; - setup.logger.debug(myFields, 'Just testing.'); - - const dgram = setup.dgram; - t.test('the dgram packet should pick up the options', (assert) => { - assert.equal(dgram.socket.host, 'somewhere'); - assert.equal(dgram.socket.port, 12345); - assert.end(); - }); - - const message = JSON.parse(setup.compress.uncompressed); - t.test('the uncompressed packet should pick up the options', (assert) => { - assert.equal(message.host, 'cheese'); - assert.notOk(message.GELF); // make sure flag was removed - assert.equal(message._facility, 'nonsense'); - assert.equal(message._every1, 'Hello every one'); // the default value - assert.equal(message._every2, 'Overwritten!'); // the overwritten value - assert.equal(message._myField, 'This is my field!'); // the value for this message only - assert.equal(message.short_message, 'Just testing.'); // skip the field object - assert.end(); - }); - t.end(); - }); - - batch.end(); -}); diff --git a/test/tap/hipchatAppender-test.js b/test/tap/hipchatAppender-test.js deleted file mode 100644 index 24c7c7d..0000000 --- a/test/tap/hipchatAppender-test.js +++ /dev/null @@ -1,142 +0,0 @@ -'use strict'; - -const test = require('tap').test; -const sandbox = require('sandboxed-module'); -const hipchatAppender = require('../../lib/appenders/hipchat'); - -function setupLogging(category, options) { - const lastRequest = {}; - - const fakeRequest = function (args, level) { - lastRequest.notifier = this; - lastRequest.body = args[0]; - lastRequest.callback = args[1]; - lastRequest.level = level; - }; - - const fakeHipchatNotifier = { - make: function (room, token, from, host, notify) { - return { - room: room, - token: token, - from: from || '', - host: host || 'api.hipchat.com', - notify: notify || false, - setRoom: function (val) { - this.room = val; - }, - setFrom: function (val) { - this.from = val; - }, - setHost: function (val) { - this.host = val; - }, - setNotify: function (val) { - this.notify = val; - }, - info: function () { - fakeRequest.call(this, arguments, 'info'); - }, - warning: function () { - fakeRequest.call(this, arguments, 'warning'); - }, - failure: function () { - fakeRequest.call(this, arguments, 'failure'); - }, - success: function () { - fakeRequest.call(this, arguments, 'success'); - } - }; - } - }; - - const log4js = sandbox.require('../../lib/log4js', { - requires: { - 'hipchat-notifier': fakeHipchatNotifier - } - }); - - options = options || {}; - options.type = 'hipchat'; - - log4js.configure({ - appenders: { hipchat: options }, - categories: { default: { appenders: ['hipchat'], level: 'debug' } } - }); - - return { - logger: log4js.getLogger(category), - lastRequest: lastRequest - }; -} - -test('HipChat appender', (batch) => { - batch.test('should export a configure function', (t) => { - t.type(hipchatAppender.configure, 'function'); - t.end(); - }); - - batch.test('when logging to HipChat v2 API', (t) => { - const customCallback = function () { - return 'works'; - }; - - const topic = setupLogging('myCategory', { - type: 'hipchat', - hipchat_token: 'User_Token_With_Notification_Privs', - hipchat_room: 'Room_ID_Or_Name', - hipchat_from: 'Log4js_Test', - hipchat_notify: true, - hipchat_host: 'hipchat.your-company.tld', - hipchat_response_callback: customCallback - }); - topic.logger.warn('Log event #1'); - - t.test('a request to hipchat_host should be sent', (assert) => { - assert.equal(topic.lastRequest.notifier.host, 'hipchat.your-company.tld'); - assert.equal(topic.lastRequest.notifier.notify, true); - assert.equal(topic.lastRequest.body, 'Log event #1'); - assert.equal(topic.lastRequest.level, 'warning'); - assert.end(); - }); - - t.equal(topic.lastRequest.callback(), 'works', 'a custom callback to the HipChat response is supported'); - t.end(); - }); - - batch.test('when missing options', (t) => { - const topic = setupLogging('myLogger', { - type: 'hipchat', - }); - topic.logger.error('Log event #2'); - - t.test('it sets some defaults', (assert) => { - assert.equal(topic.lastRequest.notifier.host, 'api.hipchat.com'); - assert.equal(topic.lastRequest.notifier.notify, false); - assert.equal(topic.lastRequest.body, 'Log event #2'); - assert.equal(topic.lastRequest.level, 'failure'); - assert.end(); - }); - t.end(); - }); - - batch.test('when basicLayout is provided', (t) => { - const topic = setupLogging('myLogger', { - type: 'hipchat', - layout: { type: 'basic' } - }); - topic.logger.debug('Log event #3'); - - t.test('it should include the timestamp', (assert) => { - // basicLayout adds [TIMESTAMP] [LEVEL] category - message - // e.g. [2016-06-10 11:50:53.819] [DEBUG] myLogger - Log event #23 - - assert.match(topic.lastRequest.body, /^\[[^\]]+] \[[^\]]+].*Log event #3$/); - assert.equal(topic.lastRequest.level, 'info'); - assert.end(); - }); - t.end(); - }); - - batch.end(); -}); diff --git a/test/tap/levels-test.js b/test/tap/levels-test.js index 6815da3..13e13ce 100644 --- a/test/tap/levels-test.js +++ b/test/tap/levels-test.js @@ -1,7 +1,7 @@ 'use strict'; const test = require('tap').test; -const levels = require('../../lib/levels')(); +const levels = require('../../lib/levels'); function assertThat(assert, level) { function assertForEach(assertion, testFn, otherLevels) { diff --git a/test/tap/logFaces-HTTP-test.js b/test/tap/logFaces-HTTP-test.js deleted file mode 100644 index 7cc879b..0000000 --- a/test/tap/logFaces-HTTP-test.js +++ /dev/null @@ -1,108 +0,0 @@ -'use strict'; - -const test = require('tap').test; -const sandbox = require('sandboxed-module'); -const appender = require('../../lib/appenders/logFaces-HTTP'); - -function setupLogging(category, options) { - const fakeAxios = { - create: function (config) { - this.config = config; - return { - post: function (emptyString, event) { - fakeAxios.args = [emptyString, event]; - return { - catch: function (cb) { - fakeAxios.errorCb = cb; - } - }; - } - }; - } - }; - - const fakeConsole = { - error: function (msg) { - this.msg = msg; - } - }; - - const log4js = sandbox.require('../../lib/log4js', { - requires: { - axios: fakeAxios - }, - globals: { - console: fakeConsole - } - }); - - options.type = 'logFaces-HTTP'; - log4js.configure({ - appenders: { http: options }, - categories: { default: { appenders: ['http'], level: 'trace' } } - }); - - return { - logger: log4js.getLogger(category), - fakeAxios: fakeAxios, - fakeConsole: fakeConsole - }; -} - -test('logFaces appender', (batch) => { - batch.test('should export a configure function', (t) => { - t.type(appender.configure, 'function'); - t.end(); - }); - - batch.test('when using HTTP receivers', (t) => { - const setup = setupLogging('myCategory', { - application: 'LFS-HTTP', - url: 'http://localhost/receivers/rx1' - }); - - t.test('axios should be configured', (assert) => { - assert.equal(setup.fakeAxios.config.baseURL, 'http://localhost/receivers/rx1'); - assert.equal(setup.fakeAxios.config.timeout, 5000); - assert.equal(setup.fakeAxios.config.withCredentials, true); - assert.same(setup.fakeAxios.config.headers, { 'Content-Type': 'application/json' }); - assert.end(); - }); - - setup.logger.addContext('foo', 'bar'); - setup.logger.addContext('bar', 'foo'); - setup.logger.warn('Log event #1'); - - t.test('an event should be sent', (assert) => { - const event = setup.fakeAxios.args[1]; - assert.equal(event.a, 'LFS-HTTP'); - assert.equal(event.m, 'Log event #1'); - assert.equal(event.g, 'myCategory'); - assert.equal(event.p, 'WARN'); - assert.equal(event.p_foo, 'bar'); - assert.equal(event.p_bar, 'foo'); - - // Assert timestamp, up to hours resolution. - const date = new Date(event.t); - assert.equal( - date.toISOString().substring(0, 14), - new Date().toISOString().substring(0, 14) - ); - assert.end(); - }); - - t.test('errors should be sent to console.error', (assert) => { - setup.fakeAxios.errorCb({ response: { status: 500, data: 'oh no' } }); - assert.equal( - setup.fakeConsole.msg, - 'log4js.logFaces-HTTP Appender error posting to http://localhost/receivers/rx1: 500 - oh no' - ); - setup.fakeAxios.errorCb(new Error('oh dear')); - assert.equal(setup.fakeConsole.msg, 'log4js.logFaces-HTTP Appender error: oh dear'); - assert.end(); - }); - t.end(); - }); - - batch.end(); -}); diff --git a/test/tap/logFaces-UDP-test.js b/test/tap/logFaces-UDP-test.js deleted file mode 100644 index 907ec2e..0000000 --- a/test/tap/logFaces-UDP-test.js +++ /dev/null @@ -1,100 +0,0 @@ -'use strict'; - -const test = require('tap').test; -const sandbox = require('sandboxed-module'); -const appender = require('../../lib/appenders/logFaces-UDP'); - -function setupLogging(category, options) { - const fakeDgram = { - createSocket: function (type) { - fakeDgram.type = type; - return { - send: function (buffer, start, end, port, host, cb) { - fakeDgram.buffer = buffer; - fakeDgram.start = start; - fakeDgram.end = end; - fakeDgram.port = port; - fakeDgram.host = host; - fakeDgram.cb = cb; - } - }; - } - }; - - const fakeConsole = { - error: function (msg, err) { - this.msg = msg; - this.err = err; - } - }; - - const log4js = sandbox.require('../../lib/log4js', { - requires: { - dgram: fakeDgram - }, - globals: { - console: fakeConsole - } - }); - - options.type = 'logFaces-UDP'; - log4js.configure({ - appenders: { - udp: options - }, - categories: { default: { appenders: ['udp'], level: 'trace' } } - }); - - return { - logger: log4js.getLogger(category), - dgram: fakeDgram, - console: fakeConsole - }; -} - -test('logFaces appender', (batch) => { - batch.test('should export a configure function', (t) => { - t.type(appender.configure, 'function'); - t.end(); - }); - - batch.test('when using UDP receivers', (t) => { - const setup = setupLogging('udpCategory', { - application: 'LFS-UDP', - remoteHost: '127.0.0.1', - port: 55201 - }); - - setup.logger.addContext('foo', 'bar'); - setup.logger.addContext('bar', 'foo'); - setup.logger.error('Log event #2'); - - t.test('an event should be sent', (assert) => { - const event = JSON.parse(setup.dgram.buffer.toString()); - assert.equal(event.a, 'LFS-UDP'); - assert.equal(event.m, 'Log event #2'); - assert.equal(event.g, 'udpCategory'); - assert.equal(event.p, 'ERROR'); - assert.equal(event.p_foo, 'bar'); - assert.equal(event.p_bar, 'foo'); - - // Assert timestamp, up to hours resolution. - const date = new Date(event.t); - assert.equal( - date.toISOString().substring(0, 14), - new Date().toISOString().substring(0, 14) - ); - assert.end(); - }); - - t.test('dgram errors should be sent to console.error', (assert) => { - setup.dgram.cb('something went wrong'); - assert.equal(setup.console.msg, 'log4js.logFacesUDPAppender error sending to 127.0.0.1:55201, error: '); - assert.equal(setup.console.err, 'something went wrong'); - assert.end(); - }); - t.end(); - }); - - batch.end(); -}); diff --git a/test/tap/logger-test.js b/test/tap/logger-test.js index c4ff6ed..69dfa8f 100644 --- a/test/tap/logger-test.js +++ b/test/tap/logger-test.js @@ -1,30 +1,35 @@ 'use strict'; const test = require('tap').test; -const levels = require('../../lib/levels')(); +const debug = require('debug')('log4js:test.logger'); +const levels = require('../../lib/levels'); +const sandbox = require('@log4js-node/sandboxed-module'); + +const events = []; +const Logger = sandbox.require( + '../../lib/logger', + { + requires: { + './levels': levels, + './clustering': { + isMaster: () => true, + onlyOnMaster: fn => fn(), + send: (evt) => { + debug('fake clustering got event:', evt); + events.push(evt); + } + } + } + } +); const testConfig = { level: levels.TRACE }; -const loggerModule = require('../../lib/logger')( - levels, - () => testConfig.level, - (category, level) => { testConfig.level = level; } -); - -const Logger = loggerModule.Logger; -const testDispatcher = { - events: [], - dispatch: function (evt) { - this.events.push(evt); - } -}; -const dispatch = testDispatcher.dispatch.bind(testDispatcher); - test('../../lib/logger', (batch) => { batch.beforeEach((done) => { - testDispatcher.events = []; + events.length = 0; testConfig.level = levels.TRACE; done(); }); @@ -32,28 +37,20 @@ test('../../lib/logger', (batch) => { batch.test('constructor with no parameters', (t) => { t.throws( () => new Logger(), - new Error('No dispatch function provided.') - ); - t.end(); - }); - - batch.test('constructor with only dispatch', (t) => { - t.throws( - () => new Logger(dispatch), new Error('No category provided.') ); t.end(); }); batch.test('constructor with category', (t) => { - const logger = new Logger(dispatch, 'cheese'); + const logger = new Logger('cheese'); t.equal(logger.category, 'cheese', 'should use category'); - t.equal(logger.level, levels.TRACE, 'should use TRACE log level'); + t.equal(logger.level, levels.OFF, 'should use OFF log level'); t.end(); }); batch.test('set level should delegate', (t) => { - const logger = new Logger(dispatch, 'cheese'); + const logger = new Logger('cheese'); logger.level = 'debug'; t.equal(logger.category, 'cheese', 'should use category'); t.equal(logger.level, levels.DEBUG, 'should use level'); @@ -61,7 +58,7 @@ test('../../lib/logger', (batch) => { }); batch.test('isLevelEnabled', (t) => { - const logger = new Logger(dispatch, 'cheese'); + const logger = new Logger('cheese'); const functions = [ 'isTraceEnabled', 'isDebugEnabled', 'isInfoEnabled', 'isWarnEnabled', 'isErrorEnabled', 'isFatalEnabled' @@ -83,11 +80,11 @@ test('../../lib/logger', (batch) => { }); batch.test('should send log events to dispatch function', (t) => { - const logger = new Logger(dispatch, 'cheese'); + const logger = new Logger('cheese'); + logger.level = 'debug'; logger.debug('Event 1'); logger.debug('Event 2'); logger.debug('Event 3'); - const events = testDispatcher.events; t.equal(events.length, 3); t.equal(events[0].data[0], 'Event 1'); @@ -97,7 +94,8 @@ test('../../lib/logger', (batch) => { }); batch.test('should add context values to every event', (t) => { - const logger = new Logger(dispatch, 'fromage'); + const logger = new Logger('fromage'); + logger.level = 'debug'; logger.debug('Event 1'); logger.addContext('cheese', 'edam'); logger.debug('Event 2'); @@ -108,7 +106,6 @@ test('../../lib/logger', (batch) => { logger.debug('Event 5'); logger.clearContext(); logger.debug('Event 6'); - const events = testDispatcher.events; t.equal(events.length, 6); t.same(events[0].context, {}); @@ -121,10 +118,10 @@ test('../../lib/logger', (batch) => { }); batch.test('should not break when log data has no toString', (t) => { - const logger = new Logger(dispatch, 'thing'); + const logger = new Logger('thing'); + logger.level = 'debug'; logger.info('Just testing ', Object.create(null)); - const events = testDispatcher.events; t.equal(events.length, 1); t.end(); }); diff --git a/test/tap/logging-test.js b/test/tap/logging-test.js index 554f9d1..30c8215 100644 --- a/test/tap/logging-test.js +++ b/test/tap/logging-test.js @@ -1,7 +1,7 @@ 'use strict'; const test = require('tap').test; -const sandbox = require('sandboxed-module'); +const sandbox = require('@log4js-node/sandboxed-module'); const recording = require('../../lib/appenders/recording'); test('log4js', (batch) => { diff --git a/test/tap/logglyAppender-test.js b/test/tap/logglyAppender-test.js deleted file mode 100644 index 400a4b8..0000000 --- a/test/tap/logglyAppender-test.js +++ /dev/null @@ -1,117 +0,0 @@ -'use strict'; - -const test = require('tap').test; -const sandbox = require('sandboxed-module'); -const layouts = require('../../lib/layouts'); - -function setupLogging(category, options) { - const msgs = []; - - const fakeLoggly = { - createClient: function (opts) { - return { - config: opts, - log: function (msg, tags, cb) { - msgs.push({ - msg: msg, - tags: tags, - cb: cb - }); - } - }; - } - }; - - const fakeLayouts = { - layout: function (type, config) { - this.type = type; - this.config = config; - return layouts.messagePassThroughLayout; - }, - basicLayout: layouts.basicLayout, - messagePassThroughLayout: layouts.messagePassThroughLayout - }; - - const fakeConsole = { - errors: [], - error: function (msg, value) { - this.errors.push({ msg: msg, value: value }); - } - }; - - const log4js = sandbox.require('../../lib/log4js', { - requires: { - loggly: fakeLoggly, - './layouts': fakeLayouts - }, - globals: { - console: fakeConsole - } - }); - - options = options || {}; - options.type = 'loggly'; - - log4js.configure({ - appenders: { loggly: options }, - categories: { default: { appenders: ['loggly'], level: 'trace' } } - }); - - return { - log4js: log4js, - logger: log4js.getLogger(category), - loggly: fakeLoggly, - layouts: fakeLayouts, - console: fakeConsole, - results: msgs - }; -} - -function setupTaggedLogging() { - return setupLogging('loggly', { - token: 'your-really-long-input-token', - subdomain: 'your-subdomain', - tags: ['loggly-tag1', 'loggly-tag2', 'loggly-tagn'] - }); -} - -test('log4js logglyAppender', (batch) => { - batch.test('with minimal config', (t) => { - const setup = setupTaggedLogging(); - setup.logger.log('trace', 'Log event #1', 'Log 2', { tags: ['tag1', 'tag2'] }); - - t.equal(setup.results.length, 1, 'has a results.length of 1'); - t.equal(setup.results[0].msg.msg, 'Log event #1 Log 2', 'has a result msg with both args concatenated'); - t.same(setup.results[0].tags, ['tag1', 'tag2'], 'has the correct result tags'); - t.end(); - }); - - batch.test('config with object with tags and other keys', (t) => { - const setup = setupTaggedLogging(); - // ignore this tags object b/c there are 2 keys - setup.logger.log('trace', 'Log event #1', { other: 'other', tags: ['tag1', 'tag2'] }); - - t.equal(setup.results.length, 1, 'has a results.length of 1'); - t.equal( - setup.results[0].msg.msg, - 'Log event #1 { other: \'other\', tags: [ \'tag1\', \'tag2\' ] }', - 'has a result msg with the args concatenated' - ); - t.same(setup.results[0].tags, [], 'has a result tags with the arg that contains no tags'); - t.end(); - }); - - batch.test('with shutdown callback', (t) => { - const setup = setupTaggedLogging(); - setup.logger.log('trace', 'Log event #1', 'Log 2', { - tags: ['tag1', 'tag2'] - }); - - setup.log4js.shutdown(() => { t.end(); }); - - // shutdown will wait until after the last message has been sent to loggly - setup.results[0].cb(); - }); - - batch.end(); -}); diff --git a/test/tap/logstashHTTP-test.js b/test/tap/logstashHTTP-test.js deleted file mode 100644 index d5333db..0000000 --- a/test/tap/logstashHTTP-test.js +++ /dev/null @@ -1,114 +0,0 @@ -'use strict'; - -const test = require('tap').test; -const sandbox = require('sandboxed-module'); -const appender = require('../../lib/appenders/logstashHTTP'); - -function setupLogging(category, options) { - const fakeAxios = { - create: function (config) { - this.config = config; - return { - post: function (emptyString, event) { - fakeAxios.args = [emptyString, event]; - return { - catch: function (cb) { - fakeAxios.errorCb = cb; - } - }; - } - }; - } - }; - - const fakeConsole = { - error: function (msg) { - this.msg = msg; - } - }; - - const log4js = sandbox.require('../../lib/log4js', { - requires: { - axios: fakeAxios - }, - globals: { - console: fakeConsole - } - }); - - options.type = 'logstashHTTP'; - log4js.configure({ - appenders: { http: options }, - categories: { default: { appenders: ['http'], level: 'trace' } } - }); - - return { - logger: log4js.getLogger(category), - fakeAxios: fakeAxios, - fakeConsole: fakeConsole - }; -} - -test('logstashappender', (batch) => { - batch.test('should export a configure function', (t) => { - t.type(appender.configure, 'function'); - t.end(); - }); - - batch.test('when using HTTP receivers', (t) => { - const setup = setupLogging('myCategory', { - application: 'logstash-sample', - logType: 'application', - logChannel: 'sample', - url: 'http://localhost/receivers/rx1' - }); - - t.test('axios should be configured', (assert) => { - assert.equal(setup.fakeAxios.config.baseURL, 'http://localhost/receivers/rx1'); - assert.equal(setup.fakeAxios.config.timeout, 5000); - assert.equal(setup.fakeAxios.config.withCredentials, true); - assert.same(setup.fakeAxios.config.headers, { 'Content-Type': 'application/x-ndjson' }); - assert.end(); - }); - - setup.logger.addContext('foo', 'bar'); - setup.logger.addContext('bar', 'foo'); - setup.logger.warn('Log event #1'); - - t.test('an event should be sent', (assert) => { - const packet = setup.fakeAxios.args[1].split('\n'); - const eventHeader = JSON.parse(packet[0]); - const eventBody = JSON.parse(packet[1]); - assert.equal(eventHeader.index._index, 'logstash-sample'); - assert.equal(eventHeader.index._type, 'application'); - - assert.equal(eventBody.channel, 'sample'); - assert.equal(eventBody.message, 'Log event #1'); - assert.equal(eventBody.level_name, 'WARN'); - assert.equal(eventBody.context.foo, 'bar'); - assert.equal(eventBody.context.bar, 'foo'); - - // Assert timestamp, up to hours resolution. - const date = new Date(eventBody.datetime); - assert.equal( - date.toISOString().substring(0, 14), - new Date().toISOString().substring(0, 14) - ); - assert.end(); - }); - - t.test('errors should be sent to console.error', (assert) => { - setup.fakeAxios.errorCb({ response: { status: 500, data: 'oh no' } }); - assert.equal( - setup.fakeConsole.msg, - 'log4js.logstashHTTP Appender error posting to http://localhost/receivers/rx1: 500 - oh no' - ); - setup.fakeAxios.errorCb(new Error('oh dear')); - assert.equal(setup.fakeConsole.msg, 'log4js.logstashHTTP Appender error: oh dear'); - assert.end(); - }); - t.end(); - }); - - batch.end(); -}); diff --git a/test/tap/logstashUDP-test.js b/test/tap/logstashUDP-test.js deleted file mode 100644 index d05d880..0000000 --- a/test/tap/logstashUDP-test.js +++ /dev/null @@ -1,277 +0,0 @@ -'use strict'; - -const test = require('tap').test; -const sandbox = require('sandboxed-module'); -const appender = require('../../lib/appenders/logstashUDP'); - -function setupLogging(category, options) { - const udpSent = {}; - const socket = { closed: false }; - - const fakeDgram = { - createSocket: function () { - return { - send: function (buffer, offset, length, port, host, callback) { - udpSent.date = new Date(); - udpSent.host = host; - udpSent.port = port; - udpSent.length = length; - udpSent.offset = 0; - udpSent.buffer = buffer; - callback(undefined, length); - }, - close: function (cb) { - socket.closed = true; - cb(); - } - }; - } - }; - - const log4js = sandbox.require('../../lib/log4js', { - requires: { - dgram: fakeDgram - } - }); - - options = options || {}; - options.type = 'logstashUDP'; - log4js.configure({ - appenders: { logstash: options }, - categories: { default: { appenders: ['logstash'], level: 'trace' } } - }); - - return { - logger: log4js.getLogger(category), - log4js: log4js, - results: udpSent, - socket: socket - }; -} - -test('logstashUDP appender', (batch) => { - batch.test('should export a configure function', (t) => { - t.type(appender.configure, 'function'); - t.end(); - }); - - batch.test('a UDP packet should be sent', (t) => { - const setup = setupLogging('myCategory', { - host: '127.0.0.1', - port: 10001, - type: 'logstashUDP', - logType: 'myAppType', - category: 'myLogger', - fields: { - field1: 'value1', - field2: 'value2' - }, - layout: { - type: 'pattern', - pattern: '%m' - } - }); - setup.logger.log('trace', 'Log event #1'); - - t.equal(setup.results.host, '127.0.0.1'); - t.equal(setup.results.port, 10001); - t.equal(setup.results.offset, 0); - - const json = JSON.parse(setup.results.buffer.toString()); - t.equal(json.type, 'myAppType'); - const fields = { - field1: 'value1', - field2: 'value2', - level: 'TRACE', - category: 'myCategory' - }; - - const keys = Object.keys(fields); - for (let i = 0, length = keys.length; i < length; i += 1) { - t.equal(json[keys[i]], fields[keys[i]]); - } - - t.equal(JSON.stringify(json.fields), JSON.stringify(fields)); - t.equal(json.message, 'Log event #1'); - // Assert timestamp, up to hours resolution. - const date = new Date(json['@timestamp']); - t.equal( - date.toISOString().substring(0, 14), - setup.results.date.toISOString().substring(0, 14) - ); - - t.end(); - }); - - batch.test('default options', (t) => { - const setup = setupLogging('myLogger', { - host: '127.0.0.1', - port: 10001, - type: 'logstashUDP', - category: 'myLogger', - layout: { - type: 'pattern', - pattern: '%m' - } - }); - setup.logger.log('trace', 'Log event #1'); - - const json = JSON.parse(setup.results.buffer.toString()); - t.equal(json.type, 'myLogger'); - t.equal( - JSON.stringify(json.fields), - JSON.stringify({ level: 'TRACE', category: 'myLogger' }) - ); - - t.end(); - }); - - batch.test('configuration can include functions to generate field values at run-time', (t) => { - const setup = setupLogging('myCategory', { - host: '127.0.0.1', - port: 10001, - type: 'logstashUDP', - logType: 'myAppType', - category: 'myLogger', - fields: { - field1: 'value1', - field2: function () { - return 'evaluated at runtime'; - } - }, - layout: { - type: 'pattern', - pattern: '%m' - } - }); - setup.logger.log('trace', 'Log event #1'); - - const json = JSON.parse(setup.results.buffer.toString()); - t.equal(json.fields.field1, 'value1'); - t.equal(json.fields.field2, 'evaluated at runtime'); - - t.end(); - }); - - batch.test('extra fields should be added to the fields structure', (t) => { - const setup = setupLogging('myLogger', { - host: '127.0.0.1', - port: 10001, - type: 'logstashUDP', - category: 'myLogger', - layout: { - type: 'dummy' - } - }); - setup.logger.log('trace', 'Log event #1', { extra1: 'value1', extra2: 'value2' }); - - const json = JSON.parse(setup.results.buffer.toString()); - const fields = { - extra1: 'value1', - extra2: 'value2', - level: 'TRACE', - category: 'myLogger' - }; - t.equal(JSON.stringify(json.fields), JSON.stringify(fields)); - t.end(); - }); - - batch.test('use direct args', (t) => { - const setup = setupLogging('myLogger', { - host: '127.0.0.1', - port: 10001, - type: 'logstashUDP', - category: 'myLogger', - args: 'direct', - layout: { - type: 'dummy' - } - }); - - setup.logger.log('info', 'Log event with fields', { extra1: 'value1', extra2: 'value2' }); - const json = JSON.parse(setup.results.buffer.toString()); - - t.equal(json.extra1, 'value1'); - t.equal(json.extra2, 'value2'); - t.equal(json.fields, undefined); - t.end(); - }); - - batch.test('use fields args', (t) => { - const setup = setupLogging('myLogger', { - host: '127.0.0.1', - port: 10001, - type: 'logstashUDP', - category: 'myLogger', - args: 'fields', - layout: { - type: 'dummy' - } - }); - - setup.logger.log('info', 'Log event with fields', { extra1: 'value1', extra2: 'value2' }); - const json = JSON.parse(setup.results.buffer.toString()); - - t.equal(json.extra1, undefined); - t.equal(json.extra2, undefined); - t.equal(json.fields.extra1, 'value1'); - t.equal(json.fields.extra2, 'value2'); - t.end(); - }); - - batch.test('Send null as argument', (t) => { - const setup = setupLogging('myLogger', { - host: '127.0.0.1', - port: 10001, - type: 'logstashUDP', - category: 'myLogger', - layout: { - type: 'dummy' - } - }); - - const msg = 'test message with null'; - setup.logger.info(msg, null); - const json = JSON.parse(setup.results.buffer.toString()); - - t.equal(json.message, msg); - t.end(); - }); - - batch.test('Send undefined as argument', (t) => { - const setup = setupLogging('myLogger', { - host: '127.0.0.1', - port: 10001, - type: 'logstashUDP', - category: 'myLogger', - layout: { - type: 'dummy' - } - }); - - const msg = 'test message with undefined'; - setup.logger.info(msg, undefined); - const json = JSON.parse(setup.results.buffer.toString()); - - t.equal(json.message, msg); - t.end(); - }); - - batch.test('shutdown should close sockets', (t) => { - const setup = setupLogging('myLogger', { - host: '127.0.0.1', - port: 10001, - type: 'logstashUDP', - category: 'myLogger', - layout: { - type: 'dummy' - } - }); - setup.log4js.shutdown(() => { - t.ok(setup.socket.closed); - t.end(); - }); - }); - - batch.end(); -}); diff --git a/test/tap/mailgunAppender-test.js b/test/tap/mailgunAppender-test.js deleted file mode 100644 index 0b5740b..0000000 --- a/test/tap/mailgunAppender-test.js +++ /dev/null @@ -1,183 +0,0 @@ -'use strict'; - -const test = require('tap').test; -const layouts = require('../../lib/layouts'); -const sandbox = require('sandboxed-module'); - -function setupLogging(category, options) { - const msgs = []; - - const mailgunCredentials = { - apiKey: options.apikey, - domain: options.domain - }; - - const fakeMailgun = function () { - return { - messages: function () { - return { - config: options, - send: function (data, callback) { - msgs.push(data); - callback(false, { status: 'OK' }); - } - }; - } - }; - }; - - const fakeLayouts = { - layout: function (type, config) { - this.type = type; - this.config = config; - return layouts.messagePassThroughLayout; - }, - basicLayout: layouts.basicLayout, - messagePassThroughLayout: layouts.messagePassThroughLayout - }; - - const fakeConsole = { - errors: [], - logs: [], - error: function (msg, value) { - this.errors.push({ msg: msg, value: value }); - }, - log: function (msg, value) { - this.logs.push({ msg: msg, value: value }); - } - }; - - const log4js = sandbox.require('../../lib/log4js', { - requires: { - 'mailgun-js': fakeMailgun, - './layouts': fakeLayouts - }, - globals: { - console: fakeConsole - } - }); - options = options || {}; - options.type = 'mailgun'; - log4js.configure({ - appenders: { mailgun: options }, - categories: { default: { appenders: ['mailgun'], level: 'trace' } } - }); - - return { - logger: log4js.getLogger(category), - mailer: fakeMailgun, - layouts: fakeLayouts, - console: fakeConsole, - mails: msgs, - credentials: mailgunCredentials - }; -} - -function checkMessages(assert, result) { - for (let i = 0; i < result.mails.length; ++i) { - assert.equal(result.mails[i].from, 'sender@domain.com'); - assert.equal(result.mails[i].to, 'recepient@domain.com'); - assert.equal(result.mails[i].subject, 'This is subject'); - assert.ok(new RegExp(`.+Log event #${i + 1}`).test(result.mails[i].text)); - } -} - -test('log4js mailgunAppender', (batch) => { - batch.test('mailgun setup', (t) => { - const result = setupLogging('mailgun setup', { - apikey: 'APIKEY', - domain: 'DOMAIN', - from: 'sender@domain.com', - to: 'recepient@domain.com', - subject: 'This is subject' - }); - - t.test('mailgun credentials should match', (assert) => { - assert.equal(result.credentials.apiKey, 'APIKEY'); - assert.equal(result.credentials.domain, 'DOMAIN'); - assert.end(); - }); - t.end(); - }); - - batch.test('basic usage', (t) => { - const result = setupLogging('basic usage', { - apikey: 'APIKEY', - domain: 'DOMAIN', - from: 'sender@domain.com', - to: 'recepient@domain.com', - subject: 'This is subject' - }); - - result.logger.info('Log event #1'); - - t.equal(result.mails.length, 1, 'should be one message only'); - checkMessages(t, result); - t.end(); - }); - - batch.test('config with layout', (t) => { - const result = setupLogging('config with layout', { - layout: { - type: 'tester' - } - }); - t.equal(result.layouts.type, 'tester', 'should configure layout'); - t.end(); - }); - - batch.test('error when sending email', (t) => { - const setup = setupLogging('separate email for each event', { - apikey: 'APIKEY', - domain: 'DOMAIN', - from: 'sender@domain.com', - to: 'recepient@domain.com', - subject: 'This is subject' - }); - - setup.mailer.messages = function () { - return { - send: function (msg, cb) { - cb({ msg: 'log4js.mailgunAppender - Error happened' }, null); - } - }; - }; - - setup.logger.info('This will break'); - const cons = setup.console; - - t.test('should be logged to console', (assert) => { - assert.equal(cons.errors.length, 2); - // errors[0] is the deprecation warning - assert.equal(cons.errors[1].msg, 'log4js.mailgunAppender - Error happened'); - assert.end(); - }); - t.end(); - }); - - batch.test('separate email for each event', (t) => { - const setup = setupLogging('separate email for each event', { - apikey: 'APIKEY', - domain: 'DOMAIN', - from: 'sender@domain.com', - to: 'recepient@domain.com', - subject: 'This is subject' - }); - setTimeout(() => { - setup.logger.info('Log event #1'); - }, 0); - setTimeout(() => { - setup.logger.info('Log event #2'); - }, 500); - setTimeout(() => { - setup.logger.info('Log event #3'); - }, 1100); - setTimeout(() => { - t.equal(setup.mails.length, 3, 'should be three messages'); - checkMessages(t, setup); - t.end(); - }, 3000); - }); - - batch.end(); -}); diff --git a/test/tap/multiprocess-shutdown-test.js b/test/tap/multiprocess-shutdown-test.js index 568ba6a..eb169f7 100644 --- a/test/tap/multiprocess-shutdown-test.js +++ b/test/tap/multiprocess-shutdown-test.js @@ -4,7 +4,7 @@ const test = require('tap').test; const log4js = require('../../lib/log4js'); const net = require('net'); const childProcess = require('child_process'); -const sandbox = require('sandboxed-module'); +const sandbox = require('@log4js-node/sandboxed-module'); test('multiprocess appender shutdown (master)', { timeout: 2000 }, (t) => { log4js.configure({ diff --git a/test/tap/multiprocess-test.js b/test/tap/multiprocess-test.js index 7a71acd..b6ccabb 100644 --- a/test/tap/multiprocess-test.js +++ b/test/tap/multiprocess-test.js @@ -1,7 +1,7 @@ 'use strict'; const test = require('tap').test; -const sandbox = require('sandboxed-module'); +const sandbox = require('@log4js-node/sandboxed-module'); const recording = require('../../lib/appenders/recording'); function makeFakeNet() { diff --git a/test/tap/passenger-test.js b/test/tap/passenger-test.js index c1842b4..bf69837 100644 --- a/test/tap/passenger-test.js +++ b/test/tap/passenger-test.js @@ -1,5 +1,5 @@ const test = require('tap').test; -const sandbox = require('sandboxed-module'); +const sandbox = require('@log4js-node/sandboxed-module'); // passenger provides a non-functional cluster module, // but it does not implement the event emitter functions diff --git a/test/tap/rabbitmqAppender-test.js b/test/tap/rabbitmqAppender-test.js deleted file mode 100644 index 12fdd8d..0000000 --- a/test/tap/rabbitmqAppender-test.js +++ /dev/null @@ -1,125 +0,0 @@ -'use strict'; - -const test = require('tap').test; -const sandbox = require('sandboxed-module'); -const appender = require('../../lib/appenders/rabbitmq'); - -function setupLogging(category, options) { - const fakeRabbitmq = { - msgs: [], - connect: function (conn) { - this.port = conn.port; - this.host = conn.hostname; - this.username = conn.username; - this.password = conn.password; - this.routing_key = conn.routing_key; - this.exchange = conn.exchange; - this.mq_type = conn.mq_type; - this.durable = conn.durable; - return { - publish: function (client, message) { - fakeRabbitmq.msgs.push(message); - } - }; - } - }; - - const fakeConsole = { - errors: [], - error: function (msg) { - this.errors.push(msg); - } - }; - - const log4js = sandbox.require('../../lib/log4js', { - requires: { - amqplib: fakeRabbitmq, - }, - globals: { - console: fakeConsole - } - }); - log4js.configure({ - appenders: { rabbitmq: options }, - categories: { default: { appenders: ['rabbitmq'], level: 'trace' } } - }); - - return { - logger: log4js.getLogger(category), - fakeRabbitmq: fakeRabbitmq, - fakeConsole: fakeConsole - }; -} - -test('log4js rabbitmqAppender', (batch) => { - batch.test('should export a configure function', (t) => { - t.type(appender.configure, 'function'); - t.end(); - }); - - batch.test('rabbitmq setup', (t) => { - const result = setupLogging('rabbitmq setup', { - host: '123.123.123.123', - port: 5672, - username: 'guest', - password: 'guest', - routing_key: 'logstash', - exchange: 'exchange_logs', - mq_type: 'direct', - durable: true, - type: 'rabbitmq', - layout: { - type: 'pattern', - pattern: 'cheese %m' - } - }); - - result.logger.info('Log event #1'); - - t.test('rabbitmq credentials should match', (assert) => { - assert.equal(result.fakeRabbitmq.host, '123.123.123.123'); - assert.equal(result.fakeRabbitmq.port, 5672); - assert.equal(result.fakeRabbitmq.username, 'guest'); - assert.equal(result.fakeRabbitmq.password, 'guest'); - assert.equal(result.fakeRabbitmq.routing_key, 'logstash'); - assert.equal(result.fakeRabbitmq.exchange, 'exchange_logs'); - assert.equal(result.fakeRabbitmq.mq_type, 'direct'); - assert.equal(result.fakeRabbitmq.durable, true); - assert.equal(result.fakeRabbitmq.msgs.length, 1, 'should be one message only'); - assert.equal(result.fakeRabbitmq.msgs[0], 'cheese Log event #1'); - assert.end(); - }); - - t.end(); - }); - - batch.test('default values', (t) => { - const setup = setupLogging('defaults', { - type: 'rabbitmq' - }); - - setup.logger.info('just testing'); - - t.test('should use localhost', (assert) => { - assert.equal(setup.fakeRabbitmq.host, '127.0.0.1'); - assert.equal(setup.fakeRabbitmq.port, 5672); - assert.equal(setup.fakeRabbitmq.username, 'guest'); - assert.equal(setup.fakeRabbitmq.password, 'guest'); - assert.equal(setup.fakeRabbitmq.exchange, ''); - assert.equal(setup.fakeRabbitmq.mq_type, ''); - assert.equal(setup.fakeRabbitmq.durable, false); - assert.equal(setup.fakeRabbitmq.routing_key, 'logstash'); - assert.end(); - }); - - t.test('should use message pass through layout', (assert) => { - assert.equal(setup.fakeRabbitmq.msgs.length, 1); - assert.equal(setup.fakeRabbitmq.msgs[0], 'just testing'); - assert.end(); - }); - - t.end(); - }); - - batch.end(); -}); diff --git a/test/tap/redisAppender-test.js b/test/tap/redisAppender-test.js deleted file mode 100644 index 583ee64..0000000 --- a/test/tap/redisAppender-test.js +++ /dev/null @@ -1,151 +0,0 @@ -'use strict'; - -const test = require('tap').test; -const sandbox = require('sandboxed-module'); -const appender = require('../../lib/appenders/redis'); - -function setupLogging(category, options) { - const fakeRedis = { - msgs: [], - createClient: function (port, host, optionR) { - this.port = port; - this.host = host; - this.optionR = optionR; - - return { - on: function (event, callback) { - fakeRedis.errorCb = callback; - }, - publish: function (channel, message, callback) { - fakeRedis.msgs.push({ channel: channel, message: message }); - fakeRedis.publishCb = callback; - }, - quit: function () { - fakeRedis.quitCalled = true; - }, - }; - } - }; - - const fakeConsole = { - errors: [], - error: function (msg) { - this.errors.push(msg); - } - }; - - const log4js = sandbox.require('../../lib/log4js', { - requires: { - redis: fakeRedis - }, - globals: { - console: fakeConsole - } - }); - log4js.configure({ - appenders: { redis: options }, - categories: { default: { appenders: ['redis'], level: 'trace' } } - }); - - return { - logger: log4js.getLogger(category), - log4js: log4js, - fakeRedis: fakeRedis, - fakeConsole: fakeConsole - }; -} - -test('log4js redisAppender', (batch) => { - batch.test('should export a configure function', (t) => { - t.type(appender.configure, 'function'); - t.end(); - }); - - batch.test('redis setup', (t) => { - const result = setupLogging('redis setup', { - host: '123.123.123.123', - port: 1234, - pass: '123456', - channel: 'log', - type: 'redis', - layout: { - type: 'pattern', - pattern: 'cheese %m' - } - }); - - result.logger.info('Log event #1'); - result.fakeRedis.publishCb(); - - t.test('redis credentials should match', (assert) => { - assert.equal(result.fakeRedis.host, '123.123.123.123'); - assert.equal(result.fakeRedis.port, 1234); - assert.equal(result.fakeRedis.optionR.auth_pass, '123456'); - assert.equal(result.fakeRedis.msgs.length, 1, 'should be one message only'); - assert.equal(result.fakeRedis.msgs[0].channel, 'log'); - assert.equal(result.fakeRedis.msgs[0].message, 'cheese Log event #1'); - assert.end(); - }); - - t.end(); - }); - - batch.test('default values', (t) => { - const setup = setupLogging('defaults', { - type: 'redis', - channel: 'thing' - }); - - setup.logger.info('just testing'); - setup.fakeRedis.publishCb(); - - t.test('should use localhost', (assert) => { - assert.equal(setup.fakeRedis.host, '127.0.0.1'); - assert.equal(setup.fakeRedis.port, 6379); - assert.same(setup.fakeRedis.optionR, {}); - assert.end(); - }); - - t.test('should use message pass through layout', (assert) => { - assert.equal(setup.fakeRedis.msgs.length, 1); - assert.equal(setup.fakeRedis.msgs[0].channel, 'thing'); - assert.equal(setup.fakeRedis.msgs[0].message, 'just testing'); - assert.end(); - }); - - t.end(); - }); - - batch.test('redis errors', (t) => { - const setup = setupLogging('errors', { type: 'redis', channel: 'testing' }); - - setup.fakeRedis.errorCb('oh no, error on connect'); - setup.logger.info('something something'); - setup.fakeRedis.publishCb('oh no, error on publish'); - - t.test('should go to the console', (assert) => { - assert.equal(setup.fakeConsole.errors.length, 3); - assert.equal( - setup.fakeConsole.errors[1], - 'log4js.redisAppender - 127.0.0.1:6379 Error: \'oh no, error on connect\'' - ); - assert.equal( - setup.fakeConsole.errors[2], - 'log4js.redisAppender - 127.0.0.1:6379 Error: \'oh no, error on publish\'' - ); - assert.end(); - }); - t.end(); - }); - - batch.test('shutdown', (t) => { - const setup = setupLogging('shutdown', { type: 'redis', channel: 'testing' }); - - setup.log4js.shutdown(() => { - t.ok(setup.fakeRedis.quitCalled); - t.end(); - }); - }); - - batch.end(); -}); diff --git a/test/tap/server-test.js b/test/tap/server-test.js new file mode 100644 index 0000000..3be4f98 --- /dev/null +++ b/test/tap/server-test.js @@ -0,0 +1,47 @@ +const test = require('tap').test; +const net = require('net'); +const log4js = require('../../lib/log4js'); +const vcr = require('../../lib/appenders/recording'); +const levels = require('../../lib/levels'); +const LoggingEvent = require('../../lib/LoggingEvent'); + +log4js.configure({ + appenders: { + vcr: { type: 'recording' }, + tcp: { type: 'tcp-server', port: 5678 } + }, + categories: { + default: { appenders: ['vcr'], level: 'debug' } + } +}); + +test('TCP Server', (batch) => { + batch.test('should listen for TCP messages and re-send via process.send', (t) => { + // give the socket a chance to start up + setTimeout(() => { + const socket = net.connect(5678, () => { + socket.write( + (new LoggingEvent('test-category', levels.INFO, ['something'], {})).serialise(), + () => { + socket.end(); + setTimeout(() => { + log4js.shutdown(() => { + const logs = vcr.replay(); + t.equal(logs.length, 1); + t.match(logs[0], { + data: ['something'], + categoryName: 'test-category', + level: { levelStr: 'INFO' }, + context: {} + }); + t.end(); + }); + }, 100); + } + ); + }); + socket.unref(); + }, 100); + }); + batch.end(); +}); diff --git a/test/tap/slackAppender-test.js b/test/tap/slackAppender-test.js deleted file mode 100644 index 75048d6..0000000 --- a/test/tap/slackAppender-test.js +++ /dev/null @@ -1,163 +0,0 @@ -'use strict'; - -const test = require('tap').test; -const sandbox = require('sandboxed-module'); -const realLayouts = require('../../lib/layouts'); - -function setupLogging(category, options) { - const msgs = []; - - const slackCredentials = { - token: options.token, - channel_id: options.channel_id, - username: options.username, - format: options.format, - icon_url: options.icon_url - }; - const fakeSlack = (function (key) { - function constructor() { - return { - options: key, - api: function (action, data, callback) { - msgs.push(data); - callback(false, { status: 'sent' }); - } - }; - } - - return constructor(key); - }); - - const fakeLayouts = { - layout: function (type, config) { - this.type = type; - this.config = config; - return realLayouts.messagePassThroughLayout; - }, - basicLayout: realLayouts.basicLayout, - coloredLayout: realLayouts.coloredLayout, - messagePassThroughLayout: realLayouts.messagePassThroughLayout - }; - - const fakeConsole = { - errors: [], - logs: [], - error: function (msg, value) { - this.errors.push({ msg: msg, value: value }); - }, - log: function (msg, value) { - this.logs.push({ msg: msg, value: value }); - } - }; - - const log4js = sandbox.require('../../lib/log4js', { - requires: { - 'slack-node': fakeSlack, - './layouts': fakeLayouts - }, - globals: { - console: fakeConsole - } - }); - - options.type = 'slack'; - log4js.configure({ - appenders: { - slack: options - }, - categories: { - default: { appenders: ['slack'], level: 'trace' } - } - }); - - return { - logger: log4js.getLogger(category), - mailer: fakeSlack, - layouts: fakeLayouts, - console: fakeConsole, - messages: msgs, - credentials: slackCredentials - }; -} - -function checkMessages(assert, result) { - for (let i = 0; i < result.messages.length; ++i) { - assert.equal(result.messages[i].channel, '#CHANNEL'); - assert.equal(result.messages[i].username, 'USERNAME'); - assert.ok(new RegExp(`.+Log event #${i + 1}`).test(result.messages[i].text)); - } -} - -test('log4js slackAppender', (batch) => { - batch.test('slack setup', (t) => { - const result = setupLogging('slack setup', { - token: 'TOKEN', - channel_id: '#CHANNEL', - username: 'USERNAME', - format: 'FORMAT', - icon_url: 'ICON_URL' - }); - - t.test('slack credentials should match', (assert) => { - assert.equal(result.credentials.token, 'TOKEN'); - assert.equal(result.credentials.channel_id, '#CHANNEL'); - assert.equal(result.credentials.username, 'USERNAME'); - assert.equal(result.credentials.format, 'FORMAT'); - assert.equal(result.credentials.icon_url, 'ICON_URL'); - assert.end(); - }); - t.end(); - }); - - batch.test('basic usage', (t) => { - const setup = setupLogging('basic usage', { - token: 'TOKEN', - channel_id: '#CHANNEL', - username: 'USERNAME', - format: 'FORMAT', - icon_url: 'ICON_URL', - }); - - setup.logger.info('Log event #1'); - - t.equal(setup.messages.length, 1, 'should be one message only'); - checkMessages(t, setup); - t.end(); - }); - - batch.test('config with layout', (t) => { - const result = setupLogging('config with layout', { - layout: { - type: 'tester' - } - }); - t.equal(result.layouts.type, 'tester', 'should configure layout'); - t.end(); - }); - - batch.test('separate notification for each event', (t) => { - const setup = setupLogging('separate notification for each event', { - token: 'TOKEN', - channel_id: '#CHANNEL', - username: 'USERNAME', - format: 'FORMAT', - icon_url: 'ICON_URL', - }); - setTimeout(() => { - setup.logger.info('Log event #1'); - }, 0); - setTimeout(() => { - setup.logger.info('Log event #2'); - }, 500); - setTimeout(() => { - setup.logger.info('Log event #3'); - }, 1100); - setTimeout(() => { - t.equal(setup.messages.length, 3, 'should be three messages'); - checkMessages(t, setup); - t.end(); - }, 3000); - }); - - batch.end(); -}); diff --git a/test/tap/smtpAppender-test.js b/test/tap/smtpAppender-test.js deleted file mode 100644 index b8bf463..0000000 --- a/test/tap/smtpAppender-test.js +++ /dev/null @@ -1,280 +0,0 @@ -'use strict'; - -const test = require('tap').test; -const realLayouts = require('../../lib/layouts'); -const sandbox = require('sandboxed-module'); - -function setupLogging(category, options, errorOnSend) { - const msgs = []; - - const fakeMailer = { - createTransport: function (name, opts) { - return { - config: opts, - sendMail: function (msg, callback) { - if (errorOnSend) { - callback({ message: errorOnSend }); - return; - } - msgs.push(msg); - callback(null, true); - }, - close: function () { - } - }; - } - }; - - const fakeLayouts = { - layout: function (type, config) { - this.type = type; - this.config = config; - return realLayouts.messagePassThroughLayout; - }, - basicLayout: realLayouts.basicLayout, - messagePassThroughLayout: realLayouts.messagePassThroughLayout - }; - - const fakeConsole = { - errors: [], - error: function (msg, value) { - this.errors.push({ msg: msg, value: value }); - } - }; - - const log4js = sandbox.require('../../lib/log4js', { - requires: { - nodemailer: fakeMailer, - './layouts': fakeLayouts - }, - globals: { - console: fakeConsole - } - }); - - options.type = 'smtp'; - log4js.configure({ - appenders: { - smtp: options - }, - categories: { default: { appenders: ['smtp'], level: 'trace' } } - }); - - return { - logger: log4js.getLogger(category), - mailer: fakeMailer, - layouts: fakeLayouts, - console: fakeConsole, - results: msgs - }; -} - -function checkMessages(assert, result, sender, subject) { - for (let i = 0; i < result.results.length; ++i) { - assert.equal(result.results[i].from, sender); - assert.equal(result.results[i].to, 'recipient@domain.com'); - assert.equal(result.results[i].subject, subject ? subject : `Log event #${i + 1}`); // eslint-disable-line - assert.ok(new RegExp(`.+Log event #${i + 1}\n$`).test(result.results[i].text)); - } -} - -test('log4js smtpAppender', (batch) => { - batch.test('minimal config', (t) => { - const setup = setupLogging('minimal config', { - recipients: 'recipient@domain.com', - SMTP: { - port: 25, - auth: { - user: 'user@domain.com' - } - } - }); - setup.logger.info('Log event #1'); - - t.equal(setup.results.length, 1, 'should be one message only'); - checkMessages(t, setup); - t.end(); - }); - - batch.test('fancy config', (t) => { - const setup = setupLogging('fancy config', { - recipients: 'recipient@domain.com', - sender: 'sender@domain.com', - subject: 'This is subject', - SMTP: { - port: 25, - auth: { - user: 'user@domain.com' - } - } - }); - setup.logger.info('Log event #1'); - - t.equal(setup.results.length, 1, 'should be one message only'); - checkMessages(t, setup, 'sender@domain.com', 'This is subject'); - t.end(); - }); - - batch.test('config with layout', (t) => { - const setup = setupLogging('config with layout', { - layout: { - type: 'tester' - } - }); - t.equal(setup.layouts.type, 'tester', 'should configure layout'); - t.end(); - }); - - batch.test('separate email for each event', (t) => { - const setup = setupLogging('separate email for each event', { - recipients: 'recipient@domain.com', - SMTP: { - port: 25, - auth: { - user: 'user@domain.com' - } - } - }); - setTimeout(() => { - setup.logger.info('Log event #1'); - }, 0); - setTimeout(() => { - setup.logger.info('Log event #2'); - }, 500); - setTimeout(() => { - setup.logger.info('Log event #3'); - }, 1100); - setTimeout(() => { - t.equal(setup.results.length, 3, 'there should be three messages'); - checkMessages(t, setup); - t.end(); - }, 3000); - }); - - batch.test('multiple events in one email', (t) => { - const setup = setupLogging('multiple events in one email', { - recipients: 'recipient@domain.com', - sendInterval: 1, - SMTP: { - port: 25, - auth: { - user: 'user@domain.com' - } - } - }); - setTimeout(() => { - setup.logger.info('Log event #1'); - }, 0); - setTimeout(() => { - setup.logger.info('Log event #2'); - }, 100); - setTimeout(() => { - setup.logger.info('Log event #3'); - }, 1500); - setTimeout(() => { - t.equal(setup.results.length, 2, 'there should be two messages'); - t.equal(setup.results[0].to, 'recipient@domain.com'); - t.equal(setup.results[0].subject, 'Log event #1'); - t.equal( - setup.results[0].text.match(new RegExp('.+Log event #[1-2]$', 'gm')).length, - 2 - ); - t.equal(setup.results[1].to, 'recipient@domain.com'); - t.equal(setup.results[1].subject, 'Log event #3'); - t.ok(/.+Log event #3\n$/.test(setup.results[1].text)); - t.end(); - }, 3000); - }); - - batch.test('error when sending email', (t) => { - const setup = setupLogging('error when sending email', { - recipients: 'recipient@domain.com', - sendInterval: 0, - SMTP: { port: 25, auth: { user: 'user@domain.com' } } - }, 'oh noes'); - - setup.logger.info('This will break'); - - t.test('should be logged to console', (assert) => { - assert.equal(setup.console.errors.length, 2); - // first error will be deprecation warning, ignore it - assert.equal(setup.console.errors[1].msg, 'log4js.smtpAppender - Error happened'); - assert.equal(setup.console.errors[1].value.message, 'oh noes'); - assert.end(); - }); - t.end(); - }); - - batch.test('transport full config', (t) => { - const setup = setupLogging('transport full config', { - recipients: 'recipient@domain.com', - transport: { - plugin: 'sendmail', - options: { - path: '/usr/sbin/sendmail' - } - } - }); - setup.logger.info('Log event #1'); - - t.equal(setup.results.length, 1, 'should be one message only'); - checkMessages(t, setup); - t.end(); - }); - - batch.test('transport no-options config', (t) => { - const setup = setupLogging('transport no-options config', { - recipients: 'recipient@domain.com', - transport: { - plugin: 'sendmail' - } - }); - setup.logger.info('Log event #1'); - - t.equal(setup.results.length, 1, 'should be one message only'); - checkMessages(t, setup); - t.end(); - }); - - batch.test('transport no-plugin config', (t) => { - const setup = setupLogging('transport no-plugin config', { - recipients: 'recipient@domain.com', - transport: {} - }); - setup.logger.info('Log event #1'); - - t.equal(setup.results.length, 1, 'should be one message only'); - checkMessages(t, setup); - t.end(); - }); - - batch.test('attachment config', (t) => { - const setup = setupLogging('attachment config', { - recipients: 'recipient@domain.com', - attachment: { - enable: true - }, - SMTP: { - port: 25, - auth: { - user: 'user@domain.com' - } - } - }); - setup.logger.info('Log event #1'); - - t.test('message should contain proper data', (assert) => { - assert.equal(setup.results.length, 1); - assert.equal(setup.results[0].attachments.length, 1); - const attachment = setup.results[0].attachments[0]; - assert.equal(setup.results[0].text, 'See logs as attachment'); - assert.equal(attachment.filename, 'default.log'); - assert.equal(attachment.contentType, 'text/x-log'); - assert.ok(new RegExp(`.+Log event #${1}\n$`).test(attachment.content)); - assert.end(); - }); - t.end(); - }); - - batch.end(); -}); diff --git a/test/tap/stderrAppender-test.js b/test/tap/stderrAppender-test.js index ee61e9a..f52856f 100644 --- a/test/tap/stderrAppender-test.js +++ b/test/tap/stderrAppender-test.js @@ -2,7 +2,7 @@ const test = require('tap').test; const layouts = require('../../lib/layouts'); -const sandbox = require('sandboxed-module'); +const sandbox = require('@log4js-node/sandboxed-module'); test('stderr appender', (t) => { const output = []; diff --git a/test/tap/stdoutAppender-test.js b/test/tap/stdoutAppender-test.js index a3b0ce4..c82b57e 100644 --- a/test/tap/stdoutAppender-test.js +++ b/test/tap/stdoutAppender-test.js @@ -2,7 +2,7 @@ const test = require('tap').test; const layouts = require('../../lib/layouts'); -const sandbox = require('sandboxed-module'); +const sandbox = require('@log4js-node/sandboxed-module'); test('stdout appender', (t) => { const output = []; diff --git a/test/tap/tcp-appender-test.js b/test/tap/tcp-appender-test.js new file mode 100644 index 0000000..20d4316 --- /dev/null +++ b/test/tap/tcp-appender-test.js @@ -0,0 +1,45 @@ +const test = require('tap').test; +const net = require('net'); +const log4js = require('../../lib/log4js'); + +const messages = []; +const server = net.createServer((socket) => { + socket.setEncoding('utf8'); + socket.on('data', (data) => { + messages.push(JSON.parse(data)); + }); +}); + +server.unref(); + +server.listen(() => { + const port = server.address().port; + log4js.configure({ + appenders: { + tcp: { type: 'tcp', port: port } + }, + categories: { + default: { appenders: ['tcp'], level: 'debug' } + } + }); + + const logger = log4js.getLogger(); + logger.info('This should be sent via TCP.'); + log4js.shutdown(() => { + server.close(() => { + test('TCP Appender', (batch) => { + batch.test('should send log messages as JSON over TCP', (t) => { + t.equal(messages.length, 1); + t.match(messages[0], { + data: ['This should be sent via TCP.'], + categoryName: 'default', + context: {}, + level: { levelStr: 'INFO' } + }); + t.end(); + }); + batch.end(); + }); + }); + }); +}); diff --git a/types/log4js.d.ts b/types/log4js.d.ts index 88ade72..5b9aec6 100644 --- a/types/log4js.d.ts +++ b/types/log4js.d.ts @@ -168,67 +168,6 @@ export interface DateFileAppender { daysToKeep?: number; } -export interface GELFAppender { - 'type': 'gelf'; - // (defaults to localhost) - the gelf server hostname - host?: string; - // (defaults to 12201) - the port the gelf server is listening on - port?: number; - // (defaults to OS.hostname()) - the hostname used to identify the origin of the log messages. - hostname?: string; - facility?: string; - // fields to be added to each log message; custom fields must start with an underscore. - customFields?: { [field: string]: any }; -} - -export interface HipchatAppender { - type: 'hipchat'; - // User token with notification privileges - hipchat_token: string; - // Room ID or name - hipchat_room: string; - // (defaults to empty string) - a label to say where the message is from - hipchat_from?: string; - // (defaults to false) - make hipchat annoy people - hipchat_notify?: boolean; - // (defaults to api.hipchat.com) - set this if you have your own hipchat server - hipchat_host?: string; - // (defaults to only throwing errors) - implement this function if you want intercept the responses from hipchat - hipchat_response_callback?(err: Error, response: any): any; - // (defaults to messagePassThroughLayout) - layout?: Layout; -} - -export interface LogFacesHTTPAppender { - type: 'logFaces-HTTP'; - // logFaces receiver servlet URL - url: string; - // (defaults to empty string) - used to identify your application’s logs - application?: string; - // (defaults to 5000ms) - the timeout for the HTTP request. - timeout?: number; -} - -export interface LogFacesUDPAppender { - type: 'logFaces-UDP'; - // (defaults to ‘127.0.0.1’)- hostname or IP address of the logFaces receiver - remoteHost?: string; - // (defaults to 55201) - port the logFaces receiver is listening on - port?: number; - // (defaults to empty string) - used to identify your application’s logs - application?: string; -} - -export interface LogglyAppender { - type: 'loggly'; - // your really long input token - token: string; - // your subdomain - subdomain: string; - // tags to include in every log message - tags?: string[]; -} - export interface LogLevelFilterAppender { type: 'logLevelFilter'; // the name of an appender, defined in the same configuration, that you want to filter @@ -239,35 +178,6 @@ export interface LogLevelFilterAppender { maxLevel?: string; } -export interface LogstashUDPAppender { - type: 'logstashUDP'; - // hostname (or IP-address) of the logstash server - host: string; - // port of the logstash server - port: number; - // used for the type field in the logstash data - logType?: string; - // used for the type field of the logstash data if logType is not defined - category?: string; - // extra fields to log with each event - fields?: { [fieldname: string]: any }; - // (defaults to dummyLayout) used for the message field of the logstash data - layout?: Layout; -} - -export interface MailgunAppender { - type: 'mailgun'; - // your mailgun API key - apiKey: string; - // your domain - domain: string; - from: string; - to: string; - subject: string; - // (defaults to basicLayout) - layout?: Layout; -} - export interface MultiFileAppender { type: 'multiFile'; // the base part of the generated log filename @@ -290,84 +200,10 @@ export interface MultiprocessAppender { loggerHost?: string; } -export interface RedisAppender { - type: 'redis'; - // (defaults to 127.0.0.1) - the location of the redis server - host?: string; - // (defaults to 6379) - the port the redis server is listening on - port?: number; - // password to use when authenticating connection to redis - pass?: string; - // the redis channel that log events will be published to - channel: string; - // (defaults to messagePassThroughLayout) - the layout to use for log events. - layout?: Layout; -} - -export interface SlackAppender { - type: 'slack'; - // your Slack API token (see the slack and slack-node docs) - token: string; - // the channel to send log messages - channel_id: string; - // the icon to use for the message - icon_url?: string; - // the username to display with the message - username: string; - // (defaults to basicLayout) - the layout to use for the message. - layout?: Layout; -} - export interface RecordingAppender { type: 'recording'; } -export interface SmtpAppender { - type: 'smtp'; - // (if not present will use transport field) - SMTP?: { - // (defaults to localhost) - host?: string; - // (defaults to 25) - port?: number; - // authentication details - auth?: { - user: string; - pass: string; - }; - }; - // (if not present will use SMTP) - see nodemailer docs for transport options - transport?: { - // (defaults to smtp) - the nodemailer transport plugin to use - plugin?: string; - // configuration for the transport plugin - options?: any; - } | string; - // send logs as email attachment - attachment?: { - // (defaults to false) - enable?: boolean; - // (defaults to See logs as attachment) - message to put in body of email - message: string; - // (defaults to default.log) - attachment filename - filename: string; - }; - // integer(defaults to 0) - batch emails and send in one email every sendInterval seconds, if 0 then every log message will send an email. - sendInterval?: number; - // (defaults to 5) - time in seconds to wait for emails to be sent during shutdown - shutdownTimeout?: number; - // email addresses to send the logs to - recipients: string; - // (defaults to message from first log event in batch) - subject for email - subject?: string; - // who the logs should be sent as - sender?: string; - // (defaults to false) - send the email as HTML instead of plain text - html?: boolean; - // (defaults to basicLayout) - layout?: Layout; -} - export interface StandardErrorAppender { type: 'stderr'; // (defaults to colouredLayout) @@ -390,20 +226,10 @@ export type Appender = CategoryFilterAppender | FileAppender | SyncfileAppender | DateFileAppender - | GELFAppender - | HipchatAppender - | LogFacesHTTPAppender - | LogFacesUDPAppender - | LogglyAppender | LogLevelFilterAppender - | LogstashUDPAppender - | MailgunAppender | MultiFileAppender | MultiprocessAppender - | RedisAppender - | SlackAppender | RecordingAppender - | SmtpAppender | StandardErrorAppender | StandardOutputAppender | CustomAppender;