http-proxy-middleware
Advanced tools
Comparing version 0.17.4 to 0.18.0
# Changelog | ||
## [v0.18.0](https://github.com/chimurai/http-proxy-middleware/releases/tag/v0.18.0) | ||
- fix(vulnerability): update micromatch to v3.x ([npm:braces:20180219](https://snyk.io/test/npm/http-proxy-middleware?tab=issues&severity=high&severity=medium&severity=low#npm:braces:20180219 | ||
)) | ||
- test(node): drop node 0.x support ([#212](https://github.com/chimurai/http-proxy-middleware/pull/212)) | ||
## [v0.17.4](https://github.com/chimurai/http-proxy-middleware/releases/tag/v0.17.4) | ||
@@ -4,0 +10,0 @@ - fix(ntlm authentication): fixed bug preventing proxying with ntlm authentication. ([#132](https://github.com/chimurai/http-proxy-middleware/pull/149)) (Thanks: [EladBezalel](https://github.com/EladBezalel), [oshri551](https://github.com/oshri551)) |
@@ -1,5 +0,5 @@ | ||
var HPM = require('./lib'); | ||
var HPM = require('./lib') | ||
module.exports = function(context, opts) { | ||
return new HPM(context, opts); | ||
}; | ||
module.exports = function (context, opts) { | ||
return new HPM(context, opts) | ||
} |
@@ -1,52 +0,53 @@ | ||
var _ = require('lodash'); | ||
var url = require('url'); | ||
var logger = require('./logger').getInstance(); | ||
var _ = require('lodash') | ||
var url = require('url') | ||
var ERRORS = require('./errors') | ||
var logger = require('./logger').getInstance() | ||
module.exports = { | ||
createConfig: createConfig | ||
}; | ||
createConfig: createConfig | ||
} | ||
function createConfig(context, opts) { | ||
// structure of config object to be returned | ||
var config = { | ||
context: undefined, | ||
options: {} | ||
}; | ||
function createConfig (context, opts) { | ||
// structure of config object to be returned | ||
var config = { | ||
context: undefined, | ||
options: {} | ||
} | ||
// app.use('/api', proxy({target:'http://localhost:9000'})); | ||
if (isContextless(context, opts)) { | ||
config.context = '/'; | ||
config.options = _.assign(config.options, context); | ||
} | ||
// app.use('/api', proxy('http://localhost:9000')); | ||
// app.use(proxy('http://localhost:9000/api')); | ||
else if (isStringShortHand(context)) { | ||
var oUrl = url.parse(context); | ||
var target = [oUrl.protocol, '//', oUrl.host].join(''); | ||
// app.use('/api', proxy({target:'http://localhost:9000'})); | ||
if (isContextless(context, opts)) { | ||
config.context = '/' | ||
config.options = _.assign(config.options, context) | ||
config.context = oUrl.pathname || '/'; | ||
config.options = _.assign(config.options, {target: target}, opts); | ||
// app.use('/api', proxy('http://localhost:9000')); | ||
// app.use(proxy('http://localhost:9000/api')); | ||
} else if (isStringShortHand(context)) { | ||
var oUrl = url.parse(context) | ||
var target = [oUrl.protocol, '//', oUrl.host].join('') | ||
if (oUrl.protocol === 'ws:' || oUrl.protocol === 'wss:') { | ||
config.options.ws = true; | ||
} | ||
// app.use('/api', proxy({target:'http://localhost:9000'})); | ||
} else { | ||
config.context = context; | ||
config.options = _.assign(config.options, opts); | ||
config.context = oUrl.pathname || '/' | ||
config.options = _.assign(config.options, {target: target}, opts) | ||
if (oUrl.protocol === 'ws:' || oUrl.protocol === 'wss:') { | ||
config.options.ws = true | ||
} | ||
// app.use('/api', proxy({target:'http://localhost:9000'})); | ||
} else { | ||
config.context = context | ||
config.options = _.assign(config.options, opts) | ||
} | ||
configureLogger(config.options); | ||
configureLogger(config.options) | ||
if (!config.options.target) { | ||
throw new Error('[HPM] Missing "target" option. Example: {target: "http://www.example.org"}'); | ||
} | ||
if (!config.options.target) { | ||
throw new Error(ERRORS.ERR_CONFIG_FACTORY_TARGET_MISSING) | ||
} | ||
// Legacy option.proxyHost | ||
config.options = mapLegacyProxyHostOption(config.options); | ||
// Legacy option.proxyHost | ||
config.options = mapLegacyProxyHostOption(config.options) | ||
// Legacy option.proxyTable > option.router | ||
config.options = mapLegacyProxyTableOption(config.options); | ||
// Legacy option.proxyTable > option.router | ||
config.options = mapLegacyProxyTableOption(config.options) | ||
return config; | ||
return config | ||
} | ||
@@ -65,6 +66,6 @@ | ||
*/ | ||
function isStringShortHand(context) { | ||
if (_.isString(context)) { | ||
return (url.parse(context).host) ? true : false; | ||
} | ||
function isStringShortHand (context) { | ||
if (_.isString(context)) { | ||
return !!(url.parse(context).host) | ||
} | ||
} | ||
@@ -83,46 +84,46 @@ | ||
*/ | ||
function isContextless(context, opts) { | ||
return (_.isPlainObject(context) && _.isEmpty(opts)); | ||
function isContextless (context, opts) { | ||
return (_.isPlainObject(context) && _.isEmpty(opts)) | ||
} | ||
function mapLegacyProxyHostOption(options) { | ||
// set options.headers.host when option.proxyHost is provided | ||
if (options.proxyHost) { | ||
logger.warn('*************************************'); | ||
logger.warn('[HPM] Deprecated "option.proxyHost"'); | ||
logger.warn(' Use "option.changeOrigin" or "option.headers.host" instead'); | ||
logger.warn(' "option.proxyHost" will be removed in future release.'); | ||
logger.warn('*************************************'); | ||
function mapLegacyProxyHostOption (options) { | ||
// set options.headers.host when option.proxyHost is provided | ||
if (options.proxyHost) { | ||
logger.warn('*************************************') | ||
logger.warn('[HPM] Deprecated "option.proxyHost"') | ||
logger.warn(' Use "option.changeOrigin" or "option.headers.host" instead') | ||
logger.warn(' "option.proxyHost" will be removed in future release.') | ||
logger.warn('*************************************') | ||
options.headers = options.headers || {}; | ||
options.headers.host = options.proxyHost; | ||
} | ||
options.headers = options.headers || {} | ||
options.headers.host = options.proxyHost | ||
} | ||
return options; | ||
return options | ||
} | ||
// Warn deprecated proxyTable api usage | ||
function mapLegacyProxyTableOption(options) { | ||
if (options.proxyTable) { | ||
logger.warn('*************************************'); | ||
logger.warn('[HPM] Deprecated "option.proxyTable"'); | ||
logger.warn(' Use "option.router" instead'); | ||
logger.warn(' "option.proxyTable" will be removed in future release.'); | ||
logger.warn('*************************************'); | ||
function mapLegacyProxyTableOption (options) { | ||
if (options.proxyTable) { | ||
logger.warn('*************************************') | ||
logger.warn('[HPM] Deprecated "option.proxyTable"') | ||
logger.warn(' Use "option.router" instead') | ||
logger.warn(' "option.proxyTable" will be removed in future release.') | ||
logger.warn('*************************************') | ||
options.router = _.clone(options.proxyTable); | ||
_.omit(options, 'proxyTable'); | ||
} | ||
options.router = _.clone(options.proxyTable) | ||
_.omit(options, 'proxyTable') | ||
} | ||
return options; | ||
return options | ||
} | ||
function configureLogger(options) { | ||
if (options.logLevel) { | ||
logger.setLevel(options.logLevel); | ||
} | ||
function configureLogger (options) { | ||
if (options.logLevel) { | ||
logger.setLevel(options.logLevel) | ||
} | ||
if (options.logProvider) { | ||
logger.setProvider(options.logProvider); | ||
} | ||
if (options.logProvider) { | ||
logger.setProvider(options.logProvider) | ||
} | ||
} |
@@ -1,41 +0,41 @@ | ||
var _ = require('lodash'); | ||
var url = require('url'); | ||
var isGlob = require('is-glob'); | ||
var micromatch = require('micromatch'); | ||
var _ = require('lodash') | ||
var url = require('url') | ||
var isGlob = require('is-glob') | ||
var micromatch = require('micromatch') | ||
var ERRORS = require('./errors') | ||
module.exports = { | ||
match: matchContext | ||
}; | ||
match: matchContext | ||
} | ||
function matchContext(context, uri, req) { | ||
function matchContext (context, uri, req) { | ||
// single path | ||
if (isStringPath(context)) { | ||
return matchSingleStringPath(context, uri) | ||
} | ||
// single path | ||
if (isStringPath(context)) { | ||
return matchSingleStringPath(context, uri); | ||
} | ||
// single glob path | ||
if (isGlobPath(context)) { | ||
return matchSingleGlobPath(context, uri) | ||
} | ||
// single glob path | ||
if (isGlobPath(context)) { | ||
return matchSingleGlobPath(context, uri); | ||
// multi path | ||
if (Array.isArray(context)) { | ||
if (context.every(isStringPath)) { | ||
return matchMultiPath(context, uri) | ||
} | ||
if (context.every(isGlobPath)) { | ||
return matchMultiGlobPath(context, uri) | ||
} | ||
// multi path | ||
if (Array.isArray(context)) { | ||
if (context.every(isStringPath)) { | ||
return matchMultiPath(context, uri); | ||
} | ||
if (context.every(isGlobPath)) { | ||
return matchMultiGlobPath(context, uri); | ||
} | ||
throw new Error(ERRORS.ERR_CONTEXT_MATCHER_INVALID_ARRAY) | ||
} | ||
throw new Error('[HPM] Invalid context. Expecting something like: ["/api", "/ajax"] or ["/api/**", "!**.html"]'); | ||
} | ||
// custom matching | ||
if (_.isFunction(context)) { | ||
var pathname = getUrlPathName(uri) | ||
return context(pathname, req) | ||
} | ||
// custom matching | ||
if (_.isFunction(context)) { | ||
var pathname = getUrlPathName(uri); | ||
return context(pathname, req); | ||
} | ||
throw new Error('[HPM] Invalid context. Expecting something like: "/api" or ["/api", "/ajax"]'); | ||
throw new Error(ERRORS.ERR_CONTEXT_MATCHER_GENERIC) | ||
} | ||
@@ -48,30 +48,30 @@ | ||
*/ | ||
function matchSingleStringPath(context, uri) { | ||
var pathname = getUrlPathName(uri); | ||
return pathname.indexOf(context) === 0; | ||
function matchSingleStringPath (context, uri) { | ||
var pathname = getUrlPathName(uri) | ||
return pathname.indexOf(context) === 0 | ||
} | ||
function matchSingleGlobPath(pattern, uri) { | ||
var pathname = getUrlPathName(uri); | ||
var matches = micromatch(pathname, pattern); | ||
return matches && (matches.length > 0); | ||
function matchSingleGlobPath (pattern, uri) { | ||
var pathname = getUrlPathName(uri) | ||
var matches = micromatch(pathname, pattern) | ||
return matches && (matches.length > 0) | ||
} | ||
function matchMultiGlobPath(patternList, uri) { | ||
return matchSingleGlobPath(patternList, uri); | ||
function matchMultiGlobPath (patternList, uri) { | ||
return matchSingleGlobPath(patternList, uri) | ||
} | ||
/** | ||
* @param {String} context ['/api', '/ajax'] | ||
* @param {String} contextList ['/api', '/ajax'] | ||
* @param {String} uri 'http://example.org/api/b/c/d.html' | ||
* @return {Boolean} | ||
*/ | ||
function matchMultiPath(contextList, uri) { | ||
for (var i = 0; i < contextList.length; i++) { | ||
var context = contextList[i]; | ||
if (matchSingleStringPath(context, uri)) { | ||
return true; | ||
} | ||
function matchMultiPath (contextList, uri) { | ||
for (var i = 0; i < contextList.length; i++) { | ||
var context = contextList[i] | ||
if (matchSingleStringPath(context, uri)) { | ||
return true | ||
} | ||
return false; | ||
} | ||
return false | ||
} | ||
@@ -85,12 +85,12 @@ | ||
*/ | ||
function getUrlPathName(uri) { | ||
return uri && url.parse(uri).pathname; | ||
function getUrlPathName (uri) { | ||
return uri && url.parse(uri).pathname | ||
} | ||
function isStringPath(context) { | ||
return _.isString(context) && !isGlob(context); | ||
function isStringPath (context) { | ||
return _.isString(context) && !isGlob(context) | ||
} | ||
function isGlobPath(context) { | ||
return isGlob(context); | ||
function isGlobPath (context) { | ||
return isGlob(context) | ||
} |
@@ -1,74 +0,74 @@ | ||
var _ = require('lodash'); | ||
var logger = require('./logger').getInstance(); | ||
var _ = require('lodash') | ||
var logger = require('./logger').getInstance() | ||
module.exports = { | ||
init: init, | ||
getHandlers: getProxyEventHandlers | ||
}; | ||
init: init, | ||
getHandlers: getProxyEventHandlers | ||
} | ||
function init(proxy, opts) { | ||
var handlers = getProxyEventHandlers(opts); | ||
function init (proxy, opts) { | ||
var handlers = getProxyEventHandlers(opts) | ||
_.forIn(handlers, function(handler, eventName) { | ||
proxy.on(eventName, handlers[eventName]); | ||
}); | ||
_.forIn(handlers, function (handler, eventName) { | ||
proxy.on(eventName, handlers[eventName]) | ||
}) | ||
logger.debug('[HPM] Subscribed to http-proxy events: ', _.keys(handlers)); | ||
logger.debug('[HPM] Subscribed to http-proxy events: ', _.keys(handlers)) | ||
} | ||
function getProxyEventHandlers(opts) { | ||
// https://github.com/nodejitsu/node-http-proxy#listening-for-proxy-events | ||
var proxyEvents = ['error', 'proxyReq', 'proxyReqWs', 'proxyRes', 'open', 'close']; | ||
var handlers = {}; | ||
function getProxyEventHandlers (opts) { | ||
// https://github.com/nodejitsu/node-http-proxy#listening-for-proxy-events | ||
var proxyEvents = ['error', 'proxyReq', 'proxyReqWs', 'proxyRes', 'open', 'close'] | ||
var handlers = {} | ||
_.forEach(proxyEvents, function(event) { | ||
// all handlers for the http-proxy events are prefixed with 'on'. | ||
// loop through options and try to find these handlers | ||
// and add them to the handlers object for subscription in init(). | ||
var eventName = _.camelCase('on ' + event); | ||
var fnHandler = _.get(opts, eventName); | ||
_.forEach(proxyEvents, function (event) { | ||
// all handlers for the http-proxy events are prefixed with 'on'. | ||
// loop through options and try to find these handlers | ||
// and add them to the handlers object for subscription in init(). | ||
var eventName = _.camelCase('on ' + event) | ||
var fnHandler = _.get(opts, eventName) | ||
if (_.isFunction(fnHandler)) { | ||
handlers[event] = fnHandler; | ||
} | ||
}); | ||
// add default error handler in absence of error handler | ||
if (!_.isFunction(handlers.error)) { | ||
handlers.error = defaultErrorHandler; | ||
if (_.isFunction(fnHandler)) { | ||
handlers[event] = fnHandler | ||
} | ||
}) | ||
// add default close handler in absence of close handler | ||
if (!_.isFunction(handlers.close)) { | ||
handlers.close = logClose; | ||
} | ||
// add default error handler in absence of error handler | ||
if (!_.isFunction(handlers.error)) { | ||
handlers.error = defaultErrorHandler | ||
} | ||
return handlers; | ||
}; | ||
// add default close handler in absence of close handler | ||
if (!_.isFunction(handlers.close)) { | ||
handlers.close = logClose | ||
} | ||
function defaultErrorHandler(err, req, res) { | ||
var host = (req.headers && req.headers.host); | ||
var code = err.code; | ||
return handlers | ||
} | ||
if (res.writeHead && !res.headersSent) { | ||
if (/HPE_INVALID/.test(code)) { | ||
res.writeHead(502); | ||
} else { | ||
switch(code) { | ||
case 'ECONNRESET': | ||
case 'ENOTFOUND': | ||
case 'ECONNREFUSED': | ||
res.writeHead(504); | ||
break; | ||
default: res.writeHead(500); | ||
} | ||
} | ||
function defaultErrorHandler (err, req, res) { | ||
var host = (req.headers && req.headers.host) | ||
var code = err.code | ||
if (res.writeHead && !res.headersSent) { | ||
if (/HPE_INVALID/.test(code)) { | ||
res.writeHead(502) | ||
} else { | ||
switch (code) { | ||
case 'ECONNRESET': | ||
case 'ENOTFOUND': | ||
case 'ECONNREFUSED': | ||
res.writeHead(504) | ||
break | ||
default: res.writeHead(500) | ||
} | ||
} | ||
} | ||
res.end('Error occured while trying to proxy to: ' + host + req.url); | ||
res.end('Error occured while trying to proxy to: ' + host + req.url) | ||
} | ||
function logClose(req, socket, head) { | ||
// view disconnected websocket connections | ||
logger.info('[HPM] Client disconnected'); | ||
function logClose (req, socket, head) { | ||
// view disconnected websocket connections | ||
logger.info('[HPM] Client disconnected') | ||
} |
237
lib/index.js
@@ -1,147 +0,152 @@ | ||
var _ = require('lodash'); | ||
var httpProxy = require('http-proxy'); | ||
var configFactory = require('./config-factory'); | ||
var handlers = require('./handlers'); | ||
var contextMatcher = require('./context-matcher'); | ||
var PathRewriter = require('./path-rewriter'); | ||
var Router = require('./router'); | ||
var logger = require('./logger').getInstance(); | ||
var getArrow = require('./logger').getArrow; | ||
var _ = require('lodash') | ||
var httpProxy = require('http-proxy') | ||
var configFactory = require('./config-factory') | ||
var handlers = require('./handlers') | ||
var contextMatcher = require('./context-matcher') | ||
var PathRewriter = require('./path-rewriter') | ||
var Router = require('./router') | ||
var logger = require('./logger').getInstance() | ||
var getArrow = require('./logger').getArrow | ||
module.exports = HttpProxyMiddleware; | ||
module.exports = HttpProxyMiddleware | ||
function HttpProxyMiddleware(context, opts) { | ||
// https://github.com/chimurai/http-proxy-middleware/issues/57 | ||
var wsUpgradeDebounced = _.debounce(handleUpgrade); | ||
var wsInitialized = false; | ||
var config = configFactory.createConfig(context, opts); | ||
var proxyOptions = config.options; | ||
function HttpProxyMiddleware (context, opts) { | ||
// https://github.com/chimurai/http-proxy-middleware/issues/57 | ||
var wsUpgradeDebounced = _.debounce(handleUpgrade) | ||
var wsInitialized = false | ||
var config = configFactory.createConfig(context, opts) | ||
var proxyOptions = config.options | ||
// create proxy | ||
var proxy = httpProxy.createProxyServer({}); | ||
logger.info('[HPM] Proxy created:', config.context, ' -> ', proxyOptions.target); | ||
// create proxy | ||
var proxy = httpProxy.createProxyServer({}) | ||
logger.info('[HPM] Proxy created:', config.context, ' -> ', proxyOptions.target) | ||
var pathRewriter = PathRewriter.create(proxyOptions.pathRewrite); // returns undefined when "pathRewrite" is not provided | ||
var pathRewriter = PathRewriter.create(proxyOptions.pathRewrite) // returns undefined when "pathRewrite" is not provided | ||
// attach handler to http-proxy events | ||
handlers.init(proxy, proxyOptions); | ||
// attach handler to http-proxy events | ||
handlers.init(proxy, proxyOptions) | ||
// log errors for debug purpose | ||
proxy.on('error', logError); | ||
// log errors for debug purpose | ||
proxy.on('error', logError) | ||
// https://github.com/chimurai/http-proxy-middleware/issues/19 | ||
// expose function to upgrade externally | ||
middleware.upgrade = wsUpgradeDebounced; | ||
// https://github.com/chimurai/http-proxy-middleware/issues/19 | ||
// expose function to upgrade externally | ||
middleware.upgrade = wsUpgradeDebounced | ||
return middleware; | ||
return middleware | ||
function middleware(req, res, next) { | ||
if (shouldProxy(config.context, req)) { | ||
var activeProxyOptions = prepareProxyRequest(req); | ||
proxy.web(req, res, activeProxyOptions); | ||
} else { | ||
next(); | ||
} | ||
function middleware (req, res, next) { | ||
if (shouldProxy(config.context, req)) { | ||
var activeProxyOptions = prepareProxyRequest(req) | ||
proxy.web(req, res, activeProxyOptions) | ||
} else { | ||
next() | ||
} | ||
if (proxyOptions.ws === true) { | ||
// use initial request to access the server object to subscribe to http upgrade event | ||
catchUpgradeRequest(req.connection.server); | ||
} | ||
if (proxyOptions.ws === true) { | ||
// use initial request to access the server object to subscribe to http upgrade event | ||
catchUpgradeRequest(req.connection.server) | ||
} | ||
} | ||
function catchUpgradeRequest(server) { | ||
// subscribe once; don't subscribe on every request... | ||
// https://github.com/chimurai/http-proxy-middleware/issues/113 | ||
if (!wsInitialized) { | ||
server.on('upgrade', wsUpgradeDebounced); | ||
wsInitialized = true; | ||
} | ||
function catchUpgradeRequest (server) { | ||
// subscribe once; don't subscribe on every request... | ||
// https://github.com/chimurai/http-proxy-middleware/issues/113 | ||
if (!wsInitialized) { | ||
server.on('upgrade', wsUpgradeDebounced) | ||
wsInitialized = true | ||
} | ||
} | ||
function handleUpgrade(req, socket, head) { | ||
// set to initialized when used externally | ||
wsInitialized = true; | ||
function handleUpgrade (req, socket, head) { | ||
// set to initialized when used externally | ||
wsInitialized = true | ||
if (shouldProxy(config.context, req)) { | ||
var activeProxyOptions = prepareProxyRequest(req); | ||
proxy.ws(req, socket, head, activeProxyOptions); | ||
logger.info('[HPM] Upgrading to WebSocket'); | ||
} | ||
if (shouldProxy(config.context, req)) { | ||
var activeProxyOptions = prepareProxyRequest(req) | ||
proxy.ws(req, socket, head, activeProxyOptions) | ||
logger.info('[HPM] Upgrading to WebSocket') | ||
} | ||
} | ||
/** | ||
* Determine whether request should be proxied. | ||
* | ||
* @private | ||
* @return {Boolean} | ||
*/ | ||
function shouldProxy(context, req) { | ||
var path = (req.originalUrl || req.url); | ||
return contextMatcher.match(context, path, req); | ||
} | ||
/** | ||
* Determine whether request should be proxied. | ||
* | ||
* @private | ||
* @param {String} context [description] | ||
* @param {Object} req [description] | ||
* @return {Boolean} | ||
*/ | ||
function shouldProxy (context, req) { | ||
var path = (req.originalUrl || req.url) | ||
return contextMatcher.match(context, path, req) | ||
} | ||
/** | ||
* Apply option.router and option.pathRewrite | ||
* Order matters: | ||
Router uses original path for routing; | ||
NOT the modified path, after it has been rewritten by pathRewrite | ||
*/ | ||
function prepareProxyRequest(req) { | ||
// https://github.com/chimurai/http-proxy-middleware/issues/17 | ||
// https://github.com/chimurai/http-proxy-middleware/issues/94 | ||
req.url = (req.originalUrl || req.url); | ||
/** | ||
* Apply option.router and option.pathRewrite | ||
* Order matters: | ||
* Router uses original path for routing; | ||
* NOT the modified path, after it has been rewritten by pathRewrite | ||
* @param {Object} req | ||
* @return {Object} proxy options | ||
*/ | ||
function prepareProxyRequest (req) { | ||
// https://github.com/chimurai/http-proxy-middleware/issues/17 | ||
// https://github.com/chimurai/http-proxy-middleware/issues/94 | ||
req.url = (req.originalUrl || req.url) | ||
// store uri before it gets rewritten for logging | ||
var originalPath = req.url; | ||
var newProxyOptions = _.assign({}, proxyOptions); | ||
// store uri before it gets rewritten for logging | ||
var originalPath = req.url | ||
var newProxyOptions = _.assign({}, proxyOptions) | ||
// Apply in order: | ||
// 1. option.router | ||
// 2. option.pathRewrite | ||
__applyRouter(req, newProxyOptions); | ||
__applyPathRewrite(req, pathRewriter); | ||
// Apply in order: | ||
// 1. option.router | ||
// 2. option.pathRewrite | ||
__applyRouter(req, newProxyOptions) | ||
__applyPathRewrite(req, pathRewriter) | ||
// debug logging for both http(s) and websockets | ||
if (proxyOptions.logLevel === 'debug') { | ||
var arrow = getArrow(originalPath, req.url, proxyOptions.target, newProxyOptions.target); | ||
logger.debug('[HPM] %s %s %s %s', req.method, originalPath, arrow, newProxyOptions.target); | ||
} | ||
return newProxyOptions; | ||
// debug logging for both http(s) and websockets | ||
if (proxyOptions.logLevel === 'debug') { | ||
var arrow = getArrow(originalPath, req.url, proxyOptions.target, newProxyOptions.target) | ||
logger.debug('[HPM] %s %s %s %s', req.method, originalPath, arrow, newProxyOptions.target) | ||
} | ||
// Modify option.target when router present. | ||
function __applyRouter(req, options) { | ||
var newTarget; | ||
return newProxyOptions | ||
} | ||
if (options.router) { | ||
newTarget = Router.getTarget(req, options); | ||
// Modify option.target when router present. | ||
function __applyRouter (req, options) { | ||
var newTarget | ||
if (newTarget) { | ||
logger.debug('[HPM] Router new target: %s -> "%s"', options.target, newTarget); | ||
options.target = newTarget; | ||
} | ||
} | ||
if (options.router) { | ||
newTarget = Router.getTarget(req, options) | ||
if (newTarget) { | ||
logger.debug('[HPM] Router new target: %s -> "%s"', options.target, newTarget) | ||
options.target = newTarget | ||
} | ||
} | ||
} | ||
// rewrite path | ||
function __applyPathRewrite(req, pathRewriter) { | ||
if (pathRewriter) { | ||
var path = pathRewriter(req.url, req); | ||
// rewrite path | ||
function __applyPathRewrite (req, pathRewriter) { | ||
if (pathRewriter) { | ||
var path = pathRewriter(req.url, req) | ||
if (typeof path === 'string') { | ||
req.url = path; | ||
} else { | ||
logger.info('[HPM] pathRewrite: No rewritten path found. (%s)', req.url); | ||
} | ||
} | ||
if (typeof path === 'string') { | ||
req.url = path | ||
} else { | ||
logger.info('[HPM] pathRewrite: No rewritten path found. (%s)', req.url) | ||
} | ||
} | ||
} | ||
function logError(err, req, res) { | ||
var hostname = (req.headers && req.headers.host) || (req.hostname || req.host); // (websocket) || (node0.10 || node 4/5) | ||
var target = proxyOptions.target.host || proxyOptions.target; | ||
var errReference = 'https://nodejs.org/api/errors.html#errors_common_system_errors'; // link to Node Common Systems Errors page | ||
function logError (err, req, res) { | ||
var hostname = (req.headers && req.headers.host) || (req.hostname || req.host) // (websocket) || (node0.10 || node 4/5) | ||
var target = proxyOptions.target.host || proxyOptions.target | ||
var errorMessage = '[HPM] Error occurred while trying to proxy request %s from %s to %s (%s) (%s)' | ||
var errReference = 'https://nodejs.org/api/errors.html#errors_common_system_errors' // link to Node Common Systems Errors page | ||
logger.error('[HPM] Error occurred while trying to proxy request %s from %s to %s (%s) (%s)', req.url, hostname, target, err.code, errReference); | ||
} | ||
}; | ||
logger.error(errorMessage, req.url, hostname, target, err.code, errReference) | ||
} | ||
} |
@@ -1,142 +0,142 @@ | ||
var util = require('util'); | ||
var _ = require('lodash'); | ||
var util = require('util') | ||
var _ = require('lodash') | ||
var loggerInstance; | ||
var loggerInstance | ||
var defaultProvider = { | ||
log: console.log, | ||
debug: console.log, // use .log(); since console does not have .debug() | ||
info: console.info, | ||
warn: console.warn, | ||
error: console.error | ||
}; | ||
log: console.log, | ||
debug: console.log, // use .log(); since console does not have .debug() | ||
info: console.info, | ||
warn: console.warn, | ||
error: console.error | ||
} | ||
// log level 'weight' | ||
var LEVELS = { | ||
debug: 10, | ||
info: 20, | ||
warn: 30, | ||
error: 50, | ||
silent: 80 | ||
}; | ||
debug: 10, | ||
info: 20, | ||
warn: 30, | ||
error: 50, | ||
silent: 80 | ||
} | ||
module.exports = { | ||
// singleton | ||
getInstance: function() { | ||
if (!loggerInstance) { | ||
loggerInstance = new Logger(); | ||
} | ||
// singleton | ||
getInstance: function () { | ||
if (!loggerInstance) { | ||
loggerInstance = new Logger() | ||
} | ||
return loggerInstance; | ||
return loggerInstance | ||
}, | ||
getArrow: getArrow | ||
} | ||
function Logger () { | ||
var logLevel | ||
var provider | ||
var api = { | ||
log: log, | ||
debug: debug, | ||
info: info, | ||
warn: warn, | ||
error: error, | ||
setLevel: function (v) { | ||
if (isValidLevel(v)) { | ||
logLevel = v | ||
} | ||
}, | ||
getArrow: getArrow | ||
}; | ||
setProvider: function (fn) { | ||
if (fn && isValidProvider(fn)) { | ||
provider = fn(defaultProvider) | ||
} | ||
} | ||
} | ||
function Logger() { | ||
var logLevel; | ||
var provider; | ||
init() | ||
var api = { | ||
log: log, | ||
debug: debug, | ||
info: info, | ||
warn: warn, | ||
error: error, | ||
setLevel: function(v) { | ||
if (isValidLevel(v)) { | ||
logLevel = v; | ||
} | ||
}, | ||
setProvider: function(fn) { | ||
if (fn && isValidProvider(fn)) { | ||
provider = fn(defaultProvider); | ||
} | ||
} | ||
}; | ||
return api | ||
init(); | ||
function init () { | ||
api.setLevel('info') | ||
api.setProvider(function () { | ||
return defaultProvider | ||
}) | ||
} | ||
return api; | ||
// log will log messages, regardless of logLevels | ||
function log () { | ||
provider.log(_interpolate.apply(null, arguments)) | ||
} | ||
function init() { | ||
api.setLevel('info'); | ||
api.setProvider(function() { | ||
return defaultProvider; | ||
}); | ||
function debug () { | ||
if (_showLevel('debug')) { | ||
provider.debug(_interpolate.apply(null, arguments)) | ||
} | ||
} | ||
// log will log messages, regardless of logLevels | ||
function log() { | ||
provider.log(_interpolate.apply(null, arguments)); | ||
function info () { | ||
if (_showLevel('info')) { | ||
provider.info(_interpolate.apply(null, arguments)) | ||
} | ||
} | ||
function debug() { | ||
if (_showLevel('debug')) { | ||
provider.debug(_interpolate.apply(null, arguments)); | ||
} | ||
function warn () { | ||
if (_showLevel('warn')) { | ||
provider.warn(_interpolate.apply(null, arguments)) | ||
} | ||
} | ||
function info() { | ||
if (_showLevel('info')) { | ||
provider.info(_interpolate.apply(null, arguments)); | ||
} | ||
function error () { | ||
if (_showLevel('error')) { | ||
provider.error(_interpolate.apply(null, arguments)) | ||
} | ||
} | ||
function warn() { | ||
if (_showLevel('warn')) { | ||
provider.warn(_interpolate.apply(null, arguments)); | ||
} | ||
} | ||
/** | ||
* Decide to log or not to log, based on the log levels 'weight' | ||
* @param {String} showLevel [debug, info, warn, error, silent] | ||
* @return {Boolean} | ||
*/ | ||
function _showLevel (showLevel) { | ||
var result = false | ||
var currentLogLevel = LEVELS[logLevel] | ||
function error() { | ||
if (_showLevel('error')) { | ||
provider.error(_interpolate.apply(null, arguments)); | ||
} | ||
if (currentLogLevel && (currentLogLevel <= LEVELS[showLevel])) { | ||
result = true | ||
} | ||
/** | ||
* Decide to log or not to log, based on the log levels 'weight' | ||
* @param {String} showLevel [debug, info, warn, error, silent] | ||
* @return {Boolean} | ||
*/ | ||
function _showLevel(showLevel) { | ||
var result = false; | ||
var currentLogLevel = LEVELS[logLevel]; | ||
return result | ||
} | ||
if (currentLogLevel && (currentLogLevel <= LEVELS[showLevel])) { | ||
result = true; | ||
} | ||
// make sure logged messages and its data are return interpolated | ||
// make it possible for additional log data, such date/time or custom prefix. | ||
function _interpolate () { | ||
var fn = _.spread(util.format) | ||
var result = fn(_.slice(arguments)) | ||
return result; | ||
} | ||
return result | ||
} | ||
// make sure logged messages and its data are return interpolated | ||
// make it possible for additional log data, such date/time or custom prefix. | ||
function _interpolate() { | ||
var fn = _.spread(util.format); | ||
var result = fn(_.slice(arguments)); | ||
function isValidProvider (fnProvider) { | ||
var result = true | ||
return result; | ||
if (fnProvider && !_.isFunction(fnProvider)) { | ||
throw new Error('[HPM] Log provider config error. Expecting a function.') | ||
} | ||
function isValidProvider(fnProvider) { | ||
var result = true; | ||
return result | ||
} | ||
if (fnProvider && !_.isFunction(fnProvider)) { | ||
throw new Error('[HPM] Log provider config error. Expecting a function.'); | ||
} | ||
function isValidLevel (levelName) { | ||
var validLevels = _.keys(LEVELS) | ||
var isValid = _.includes(validLevels, levelName) | ||
return result; | ||
if (!isValid) { | ||
throw new Error('[HPM] Log level error. Invalid logLevel.') | ||
} | ||
function isValidLevel(levelName) { | ||
var validLevels = _.keys(LEVELS); | ||
var isValid = _.includes(validLevels, levelName); | ||
if (!isValid) { | ||
throw new Error('[HPM] Log level error. Invalid logLevel.'); | ||
} | ||
return isValid; | ||
} | ||
return isValid | ||
} | ||
} | ||
@@ -149,11 +149,25 @@ | ||
* ≈> router + pathRewrite | ||
* | ||
* @param {String} originalPath | ||
* @param {String} newPath | ||
* @param {String} originalTarget | ||
* @param {String} newTarget | ||
* @return {String} | ||
*/ | ||
function getArrow(originalPath, newPath, originalTarget, newTarget) { | ||
var arrow = ['>']; | ||
var isNewTarget = (originalTarget !== newTarget); // router | ||
var isNewPath = (originalPath !== newPath); // pathRewrite | ||
function getArrow (originalPath, newPath, originalTarget, newTarget) { | ||
var arrow = ['>'] | ||
var isNewTarget = (originalTarget !== newTarget) // router | ||
var isNewPath = (originalPath !== newPath) // pathRewrite | ||
if (isNewPath && !isNewTarget) {arrow.unshift('~');} else if (!isNewPath && isNewTarget) {arrow.unshift('=');} else if (isNewPath && isNewTarget) {arrow.unshift('≈');} else {arrow.unshift('-');} | ||
if (isNewPath && !isNewTarget) { | ||
arrow.unshift('~') | ||
} else if (!isNewPath && isNewTarget) { | ||
arrow.unshift('=') | ||
} else if (isNewPath && isNewTarget) { | ||
arrow.unshift('≈') | ||
} else { | ||
arrow.unshift('-') | ||
} | ||
return arrow.join(''); | ||
return arrow.join('') | ||
} |
@@ -1,7 +0,8 @@ | ||
var _ = require('lodash'); | ||
var logger = require('./logger').getInstance(); | ||
var _ = require('lodash') | ||
var logger = require('./logger').getInstance() | ||
var ERRORS = require('./errors') | ||
module.exports = { | ||
create: createPathRewriter | ||
}; | ||
create: createPathRewriter | ||
} | ||
@@ -11,63 +12,63 @@ /** | ||
* | ||
* @returns {function} Function to rewrite paths; This function should accept `path` (request.url) as parameter | ||
* @param {Object} rewriteConfig | ||
* @return {Function} Function to rewrite paths; This function should accept `path` (request.url) as parameter | ||
*/ | ||
function createPathRewriter(rewriteConfig) { | ||
var rulesCache; | ||
function createPathRewriter (rewriteConfig) { | ||
var rulesCache | ||
if (!isValidRewriteConfig(rewriteConfig)) { | ||
return; | ||
} | ||
if (!isValidRewriteConfig(rewriteConfig)) { | ||
return | ||
} | ||
if (_.isFunction(rewriteConfig)) { | ||
var customRewriteFn = rewriteConfig; | ||
return customRewriteFn; | ||
} else { | ||
rulesCache = parsePathRewriteRules(rewriteConfig); | ||
return rewritePath; | ||
} | ||
if (_.isFunction(rewriteConfig)) { | ||
var customRewriteFn = rewriteConfig | ||
return customRewriteFn | ||
} else { | ||
rulesCache = parsePathRewriteRules(rewriteConfig) | ||
return rewritePath | ||
} | ||
function rewritePath(path) { | ||
var result = path; | ||
function rewritePath (path) { | ||
var result = path | ||
_.forEach(rulesCache, function(rule) { | ||
if (rule.regex.test(path)) { | ||
result = result.replace(rule.regex, rule.value); | ||
logger.debug('[HPM] Rewriting path from "%s" to "%s"', path, result); | ||
return false; | ||
} | ||
}); | ||
_.forEach(rulesCache, function (rule) { | ||
if (rule.regex.test(path)) { | ||
result = result.replace(rule.regex, rule.value) | ||
logger.debug('[HPM] Rewriting path from "%s" to "%s"', path, result) | ||
return false | ||
} | ||
}) | ||
return result; | ||
} | ||
return result | ||
} | ||
} | ||
function isValidRewriteConfig(rewriteConfig) { | ||
if (_.isFunction(rewriteConfig)) { | ||
return true; | ||
} else if (!_.isEmpty(rewriteConfig) && _.isPlainObject(rewriteConfig)) { | ||
return true; | ||
} else if (_.isUndefined(rewriteConfig) || | ||
function isValidRewriteConfig (rewriteConfig) { | ||
if (_.isFunction(rewriteConfig)) { | ||
return true | ||
} else if (!_.isEmpty(rewriteConfig) && _.isPlainObject(rewriteConfig)) { | ||
return true | ||
} else if (_.isUndefined(rewriteConfig) || | ||
_.isNull(rewriteConfig) || | ||
_.isEqual(rewriteConfig, {})) { | ||
return false; | ||
} else { | ||
throw new Error('[HPM] Invalid pathRewrite config. Expecting object with pathRewrite config or a rewrite function'); | ||
} | ||
return false | ||
} else { | ||
throw new Error(ERRORS.ERR_PATH_REWRITER_CONFIG) | ||
} | ||
} | ||
function parsePathRewriteRules(rewriteConfig) { | ||
var rules = []; | ||
function parsePathRewriteRules (rewriteConfig) { | ||
var rules = [] | ||
if (_.isPlainObject(rewriteConfig)) { | ||
_.forIn(rewriteConfig, function(value, key) { | ||
rules.push({ | ||
regex: new RegExp(key), | ||
value: rewriteConfig[key] | ||
}); | ||
logger.info('[HPM] Proxy rewrite rule created: "%s" ~> "%s"', key, rewriteConfig[key]); | ||
}); | ||
} | ||
if (_.isPlainObject(rewriteConfig)) { | ||
_.forIn(rewriteConfig, function (value, key) { | ||
rules.push({ | ||
regex: new RegExp(key), | ||
value: rewriteConfig[key] | ||
}) | ||
logger.info('[HPM] Proxy rewrite rule created: "%s" ~> "%s"', key, rewriteConfig[key]) | ||
}) | ||
} | ||
return rules; | ||
return rules | ||
} | ||
@@ -1,53 +0,49 @@ | ||
var _ = require('lodash'); | ||
var logger = require('./logger.js').getInstance(); | ||
var _ = require('lodash') | ||
var logger = require('./logger.js').getInstance() | ||
module.exports = { | ||
getTarget: getTarget | ||
}; | ||
getTarget: getTarget | ||
} | ||
function getTarget(req, config) { | ||
var newTarget; | ||
var router = config.router; | ||
function getTarget (req, config) { | ||
var newTarget | ||
var router = config.router | ||
if (_.isPlainObject(router)) { | ||
newTarget = getTargetFromProxyTable(req, router); | ||
} else if (_.isFunction(router)) { | ||
newTarget = router(req); | ||
} | ||
if (_.isPlainObject(router)) { | ||
newTarget = getTargetFromProxyTable(req, router) | ||
} else if (_.isFunction(router)) { | ||
newTarget = router(req) | ||
} | ||
return newTarget; | ||
return newTarget | ||
} | ||
function getTargetFromProxyTable(req, table) { | ||
var result; | ||
var host = req.headers.host; | ||
var path = req.url; | ||
function getTargetFromProxyTable (req, table) { | ||
var result | ||
var host = req.headers.host | ||
var path = req.url | ||
var hostAndPath = host + path; | ||
var hostAndPath = host + path | ||
_.forIn(table, function(value, key) { | ||
if (containsPath(key)) { | ||
_.forIn(table, function (value, key) { | ||
if (containsPath(key)) { | ||
if (hostAndPath.indexOf(key) > -1) { // match 'localhost:3000/api' | ||
result = table[key] | ||
logger.debug('[HPM] Router table match: "%s"', key) | ||
return false | ||
} | ||
} else { | ||
if (key === host) { // match 'localhost:3000' | ||
result = table[key] | ||
logger.debug('[HPM] Router table match: "%s"', host) | ||
return false | ||
} | ||
} | ||
}) | ||
if (hostAndPath.indexOf(key) > -1) { // match 'localhost:3000/api' | ||
result = table[key]; | ||
logger.debug('[HPM] Router table match: "%s"', key); | ||
return false; | ||
} | ||
} else { | ||
if (key === host) { // match 'localhost:3000' | ||
result = table[key]; | ||
logger.debug('[HPM] Router table match: "%s"', host); | ||
return false; | ||
} | ||
} | ||
}); | ||
return result; | ||
return result | ||
} | ||
function containsPath(v) { | ||
return v.indexOf('/') > -1; | ||
function containsPath (v) { | ||
return v.indexOf('/') > -1 | ||
} |
{ | ||
"name": "http-proxy-middleware", | ||
"version": "0.17.4", | ||
"version": "0.18.0", | ||
"description": "The one-liner node.js proxy middleware for connect, express and browser-sync", | ||
@@ -12,5 +12,7 @@ "main": "index.js", | ||
"clean": "rm -rf coverage", | ||
"test": "mocha --recursive --colors --reporter spec", | ||
"lint": "standard --verbose | snazzy --colors", | ||
"test": "npm run lint && mocha --recursive --colors --reporter spec", | ||
"cover": "npm run clean && istanbul cover ./node_modules/mocha/bin/_mocha -- --recursive", | ||
"coveralls": "istanbul cover ./node_modules/mocha/bin/_mocha --report lcovonly -- --recursive --reporter spec && istanbul-coveralls && npm run clean" | ||
"coveralls": "istanbul cover ./node_modules/mocha/bin/_mocha --report lcovonly -- --recursive --reporter spec && istanbul-coveralls && npm run clean", | ||
"commitmsg": "commitlint -e $GIT_PARAMS" | ||
}, | ||
@@ -43,20 +45,38 @@ "repository": { | ||
"devDependencies": { | ||
"browser-sync": "^2.18.2", | ||
"chai": "^3.5.0", | ||
"connect": "^3.5.0", | ||
"coveralls": "^2.11.15", | ||
"express": "^4.14.0", | ||
"@commitlint/cli": "^6.1.3", | ||
"@commitlint/config-conventional": "^6.1.3", | ||
"browser-sync": "^2.23.6", | ||
"chai": "^4.1.2", | ||
"connect": "^3.6.6", | ||
"coveralls": "^3.0.0", | ||
"express": "^4.16.3", | ||
"husky": "^0.14.3", | ||
"istanbul": "^0.4.5", | ||
"istanbul-coveralls": "^1.0.3", | ||
"mocha": "^3.2.0", | ||
"mocha-lcov-reporter": "1.2.0", | ||
"opn": "^4.0.2", | ||
"ws": "^1.1.1" | ||
"mocha": "^5.0.4", | ||
"mocha-lcov-reporter": "1.3.0", | ||
"opn": "^5.2.0", | ||
"snazzy": "^7.1.1", | ||
"standard": "^11.0.0", | ||
"ws": "^5.0.0" | ||
}, | ||
"dependencies": { | ||
"http-proxy": "^1.16.2", | ||
"is-glob": "^3.1.0", | ||
"lodash": "^4.17.2", | ||
"micromatch": "^2.3.11" | ||
"is-glob": "^4.0.0", | ||
"lodash": "^4.17.5", | ||
"micromatch": "^3.1.9" | ||
}, | ||
"engines": { | ||
"node": ">=4.0.0" | ||
}, | ||
"standard": { | ||
"env": [ | ||
"mocha" | ||
] | ||
}, | ||
"commitlint": { | ||
"extends": [ | ||
"@commitlint/config-conventional" | ||
] | ||
} | ||
} |
@@ -7,2 +7,3 @@ # http-proxy-middleware | ||
[![dependency Status](https://snyk.io/test/npm/http-proxy-middleware/badge.svg)](https://snyk.io/test/npm/http-proxy-middleware) | ||
[![JavaScript Style Guide](https://img.shields.io/badge/codestyle-standard-brightgreen.svg)](https://standardjs.com) | ||
@@ -134,3 +135,3 @@ Node.js proxying made simple. Configure proxy middleware with ease for [connect](https://github.com/senchalabs/connect), [express](https://github.com/strongloop/express), [browser-sync](https://github.com/BrowserSync/browser-sync) and [many more](#compatible-servers). | ||
The [RFC 3986 `path`](https://tools.ietf.org/html/rfc3986#section-3.3) is be used for context matching. | ||
[RFC 3986 `path`](https://tools.ietf.org/html/rfc3986#section-3.3) is used for context matching. | ||
@@ -424,14 +425,9 @@ ``` | ||
$ npm install | ||
``` | ||
unit testing | ||
# linting | ||
$ npm run lint | ||
```bash | ||
# unit tests | ||
$ npm test | ||
``` | ||
coverage | ||
```bash | ||
# code coverage | ||
@@ -438,0 +434,0 @@ $ npm run cover |
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
13
628
45653
16
444
2
+ Addedarr-diff@4.0.0(transitive)
+ Addedarr-union@3.1.0(transitive)
+ Addedarray-unique@0.3.2(transitive)
+ Addedassign-symbols@1.0.0(transitive)
+ Addedatob@2.1.2(transitive)
+ Addedbase@0.11.2(transitive)
+ Addedbraces@2.3.2(transitive)
+ Addedcache-base@1.0.1(transitive)
+ Addedclass-utils@0.3.6(transitive)
+ Addedcollection-visit@1.0.0(transitive)
+ Addedcomponent-emitter@1.3.1(transitive)
+ Addedcopy-descriptor@0.1.1(transitive)
+ Addeddebug@2.6.9(transitive)
+ Addeddecode-uri-component@0.2.2(transitive)
+ Addeddefine-property@0.2.51.0.02.0.2(transitive)
+ Addedexpand-brackets@2.1.4(transitive)
+ Addedextend-shallow@2.0.13.0.2(transitive)
+ Addedextglob@2.0.4(transitive)
+ Addedfill-range@4.0.0(transitive)
+ Addedfragment-cache@0.2.1(transitive)
+ Addedfunction-bind@1.1.2(transitive)
+ Addedget-value@2.0.6(transitive)
+ Addedhas-value@0.3.11.0.0(transitive)
+ Addedhas-values@0.1.41.0.0(transitive)
+ Addedhasown@2.0.2(transitive)
+ Addedis-accessor-descriptor@1.0.1(transitive)
+ Addedis-data-descriptor@1.0.1(transitive)
+ Addedis-descriptor@0.1.71.0.3(transitive)
+ Addedis-extendable@1.0.1(transitive)
+ Addedis-glob@4.0.3(transitive)
+ Addedis-number@3.0.0(transitive)
+ Addedis-plain-object@2.0.4(transitive)
+ Addedis-windows@1.0.2(transitive)
+ Addedisobject@3.0.1(transitive)
+ Addedkind-of@4.0.0(transitive)
+ Addedmap-cache@0.2.2(transitive)
+ Addedmap-visit@1.0.0(transitive)
+ Addedmicromatch@3.1.10(transitive)
+ Addedmixin-deep@1.3.2(transitive)
+ Addedms@2.0.0(transitive)
+ Addednanomatch@1.2.13(transitive)
+ Addedobject-copy@0.1.0(transitive)
+ Addedobject-visit@1.0.1(transitive)
+ Addedobject.pick@1.3.0(transitive)
+ Addedpascalcase@0.1.1(transitive)
+ Addedposix-character-classes@0.1.1(transitive)
+ Addedregex-not@1.0.2(transitive)
+ Addedresolve-url@0.2.1(transitive)
+ Addedret@0.1.15(transitive)
+ Addedsafe-regex@1.1.0(transitive)
+ Addedset-value@2.0.1(transitive)
+ Addedsnapdragon@0.8.2(transitive)
+ Addedsnapdragon-node@2.1.1(transitive)
+ Addedsnapdragon-util@3.0.1(transitive)
+ Addedsource-map@0.5.7(transitive)
+ Addedsource-map-resolve@0.5.3(transitive)
+ Addedsource-map-url@0.4.1(transitive)
+ Addedsplit-string@3.1.0(transitive)
+ Addedstatic-extend@0.1.2(transitive)
+ Addedto-object-path@0.3.0(transitive)
+ Addedto-regex@3.0.2(transitive)
+ Addedto-regex-range@2.1.1(transitive)
+ Addedunion-value@1.0.1(transitive)
+ Addedunset-value@1.0.0(transitive)
+ Addedurix@0.1.0(transitive)
+ Addeduse@3.1.1(transitive)
- Removedarr-diff@2.0.0(transitive)
- Removedarray-unique@0.2.1(transitive)
- Removedbraces@1.8.5(transitive)
- Removedexpand-brackets@0.1.5(transitive)
- Removedexpand-range@1.8.2(transitive)
- Removedextglob@0.3.2(transitive)
- Removedfilename-regex@2.0.1(transitive)
- Removedfill-range@2.2.4(transitive)
- Removedfor-own@0.1.5(transitive)
- Removedglob-base@0.3.0(transitive)
- Removedglob-parent@2.0.0(transitive)
- Removedis-dotfile@1.0.3(transitive)
- Removedis-equal-shallow@0.1.3(transitive)
- Removedis-extglob@1.0.0(transitive)
- Removedis-glob@2.0.13.1.0(transitive)
- Removedis-number@2.1.04.0.0(transitive)
- Removedis-posix-bracket@0.1.1(transitive)
- Removedis-primitive@2.0.0(transitive)
- Removedmath-random@1.0.4(transitive)
- Removedmicromatch@2.3.11(transitive)
- Removednormalize-path@2.1.1(transitive)
- Removedobject.omit@2.0.1(transitive)
- Removedparse-glob@3.0.4(transitive)
- Removedpreserve@0.2.0(transitive)
- Removedrandomatic@3.1.1(transitive)
- Removedregex-cache@0.4.4(transitive)
- Removedremove-trailing-separator@1.1.0(transitive)
Updatedis-glob@^4.0.0
Updatedlodash@^4.17.5
Updatedmicromatch@^3.1.9