restify-enroute
Advanced tools
Comparing version 2.0.1 to 3.0.1
285
lib/index.js
@@ -1,249 +0,50 @@ | ||
/* | ||
* restify-enroute | ||
* dynamically inject routes into restify app from configuration file | ||
* | ||
* Copyright (c) 2015 Netflix, Inc. | ||
* Licensed under the MIT license. | ||
*/ | ||
'use strict'; | ||
var path = require('path'); | ||
var assert = require('assert-plus'); | ||
var Verror = require('verror'); | ||
var shell = require('shelljs'); | ||
var restify = require('restify'); | ||
var vasync = require('vasync'); | ||
/** | ||
* checks if the path is device rooted, that is, if it has [drive letter]:\ at its beggining. | ||
* @private | ||
* @param {String} fpath | ||
* The file path to check | ||
* @returns {Boolean} | ||
* Whether the path is rooted or not. | ||
*/ | ||
function isWin32DeviceRoot(fpath) { | ||
// Only for win32 | ||
if (process.platform !== 'win32') { | ||
return false; | ||
} | ||
var install = require('./install'); | ||
var parser = require('./parser'); | ||
// Any drive letter followed by :\ | ||
var deviceRoot = /^[A-Za-z]:\\/; | ||
return deviceRoot.test(fpath); | ||
} | ||
function requireHandlerFunc(filePath, log) { | ||
var scriptPath = filePath; | ||
var handlerFunc; | ||
if (!scriptPath.endsWith('.js')) { | ||
scriptPath += '.js'; | ||
} | ||
try { | ||
handlerFunc = require(scriptPath); | ||
} catch (err) { | ||
var errObj = new Verror(err, 'Fail to load %s', | ||
scriptPath); | ||
log.error(errObj); | ||
throw errObj; | ||
} | ||
return handlerFunc; | ||
} | ||
/** | ||
* insert restify expiry middleware after each | ||
* handler function to prevent expired request | ||
* to continue process | ||
* @public | ||
* @param {Array} middlewareArr middleware handler array | ||
* @param {Object} expiry expiry plugin middleware config | ||
* @returns {Array} middleware handler array that has expiry plugin | ||
*/ | ||
function appendExpiryCheck(middlewareArr, expiry) { | ||
var finalHdlList = []; | ||
if (Array.isArray(middlewareArr)) { | ||
middlewareArr.forEach(function (fn) { | ||
finalHdlList.push(fn); | ||
finalHdlList.push(restify.requestExpiry(expiry)); | ||
module.exports = { | ||
/** | ||
* Installs configuration driven routes onto a restify server. Note only | ||
* one of opts.config or opts.configPath is needed. | ||
* | ||
* exports | ||
* | ||
* @param {object} opts Options object. | ||
* @param {string} [opts.config] The POJO of the config you want to | ||
* validate. | ||
* @param {string} [opts.configPath] The path to the data on disk to | ||
* validate. | ||
* @param {string} opts.server The restify server to install the routes | ||
* onto. | ||
* @param {function} cb The callback f(err, result) | ||
* @returns {undefined} | ||
*/ | ||
install: function (opts, cb) { | ||
assert.object(opts, 'opts'); | ||
assert.func(cb, 'cb'); | ||
// asserts of other inputs done by parse and installRoutes repectively | ||
vasync.pipeline({arg: {}, funcs: [ | ||
function parse(ctx, _cb) { | ||
parser.parse(opts, function (err, config) { | ||
ctx.config = config; | ||
return _cb(err); | ||
}); | ||
}, | ||
function installRoutes(ctx, _cb) { | ||
install({ | ||
enroute: ctx.config, | ||
server: opts.server | ||
}, function (err) { | ||
return _cb(err); | ||
}); | ||
} | ||
]}, function (err, res) { | ||
return cb(err); | ||
}); | ||
} else { | ||
finalHdlList = middlewareArr; | ||
finalHdlList.push(restify.requestExpiry(expiry)); | ||
} | ||
return finalHdlList; | ||
} | ||
/** | ||
* getRouteConfigObject description] | ||
* @param {Object} routeConf an array of route config file path routeConf.filePath or | ||
* an array of route definition object routeConf.routeDefinitionObj | ||
* @param {Object} log bunyan log object | ||
* @returns {Object} route definition object | ||
*/ | ||
function getRouteConfigObject(routeConf, log) { | ||
assert.optionalString(routeConf.filePath, 'file path need to be a string'); | ||
assert.optionalObject(routeConf.routeDefinitionObj, | ||
'route definition is an object'); | ||
assert.optionalString(routeConf.baseUrl, 'base url needs to be a string'); | ||
var configFilePath; | ||
var baseurl = routeConf.baseUrl || ''; | ||
var routeConfObj = {baseUrl : baseurl}; | ||
var appRoot = shell.pwd(); | ||
if (routeConf.filePath) { | ||
configFilePath = | ||
path.normalize(path.join(appRoot, routeConf.filePath)); | ||
try { | ||
//route injection is at server start up time, therefore it's | ||
//needs to be synchronize load | ||
/*eslint-disable global-require*/ | ||
routeConfObj.routeDefinitionObj = require(configFilePath); | ||
/*eslint-enable */ | ||
} catch (err) { | ||
var errObj = new Verror(err, 'problem load route configuration file at %s', | ||
configFilePath); | ||
log.error(errObj); | ||
throw errObj; | ||
} | ||
} else { | ||
//if user didn't provide route configuration file path | ||
//here we should have a rout definition object | ||
routeConfObj.routeDefinitionObj = routeConf.routeDefinitionObj; | ||
} | ||
return routeConfObj; | ||
} | ||
/** | ||
* take the handlers from a route config file and setup routes into | ||
* restify server | ||
* @public | ||
* @function createRoute | ||
* @param {Object} options | ||
* @param {Object} options.server required options restify server | ||
* @param {Object} options.log required bunyan log object | ||
* @param {Object} options.preMiddleware optional, | ||
* options.preMiddleware | ||
* a list of middleware called before route handler | ||
* @param {Object} options.postMiddleware optional, | ||
* a list of middleware called after route handler | ||
* @param {Array of Object} options.routeConf required, | ||
* array of object contains route configuration file relative path to app root | ||
* and optional base url path | ||
* for example, one set of route configuration | ||
* [{filePath : ./etc/routes/route.json, baseUrl : 'v1/myaccount/'} | ||
* or route definition object, for example | ||
* [{routeDefinitionObj : routeObj, baseUrl : 'v1/myaccount/'} | ||
* user should provide either configuration file path or an route definition path | ||
* @param {Function} cb optional callback function | ||
* @returns {undefined} | ||
*/ | ||
function createRoute(options, cb) { | ||
assert.object(options.server, 'options.server restify server'); | ||
assert.object(options.log, 'options.log bunyan log'); | ||
assert.arrayOfObject(options.routeConf, | ||
'options.routeConf route conifguration files'); | ||
assert.optionalString(options.scriptPath, | ||
'options.scriptPath, optionally for test'); | ||
assert.optionalObject(options.expiry, | ||
'options.expiry, optionally check expiry after each middleware'); | ||
var server = options.server; | ||
var log = options.log; | ||
var scriptRoot = options.scriptPath; | ||
var preMiddleware = options.preMiddleware; | ||
var postMiddleware = options.postMiddleware; | ||
var expiry = options.expiry; | ||
options.routeConf.forEach(function (routeConf) { | ||
var routeConfObj = getRouteConfigObject(routeConf, options.log); | ||
assert.object(routeConfObj, 'Missing route config object'); | ||
assert.object(routeConfObj.routeDefinitionObj, 'Missing route definition object'); | ||
assert.optionalString(routeConfObj.baseUrl, 'base url needs to be a string'); | ||
var baseUrl = routeConfObj.baseUrl; | ||
var routes; | ||
var routelist = {}; | ||
routes = routeConfObj.routeDefinitionObj.routes; | ||
Object.keys(routes).forEach(function (rpath) { | ||
//deliminate route path and method with } | ||
var routePath = baseUrl + '/' + rpath; | ||
Object.keys(routes[rpath]).forEach(function (method) { | ||
var actions = []; | ||
//var routeKey; | ||
var scriptFpath = routes[rpath][method].source; | ||
var handlerFunc; | ||
if (scriptRoot) { | ||
scriptFpath = path.normalize(path.join(scriptRoot, | ||
scriptFpath)); | ||
} else { | ||
scriptFpath = path.resolve(scriptFpath); | ||
// make sure the path casing obeys the casing of the parent | ||
if (isWin32DeviceRoot(scriptFpath) && | ||
scriptFpath.substr(0, 2).toLowerCase() === module.filename.substr(0, 2).toLowerCase()) { | ||
scriptFpath = module.filename.substr(0, 2) + scriptFpath.substr(2); | ||
} | ||
} | ||
//routeKey = routePath + '}' + method; | ||
if (typeof routelist[routePath] !== 'undefined') { | ||
throw new Error('Route path exist' + rpath | ||
+ ' method ' + method); | ||
} | ||
handlerFunc = requireHandlerFunc(scriptFpath, log); | ||
//routelist[routeKey] = handlerFunc; | ||
if (preMiddleware) { | ||
if (Array.isArray(preMiddleware)) { | ||
actions = actions.concat(preMiddleware); | ||
} else { | ||
actions.push(preMiddleware); | ||
} | ||
} | ||
if (handlerFunc) { | ||
if (Array.isArray(handlerFunc)) { | ||
actions = actions.concat(handlerFunc); | ||
} else { | ||
actions.push(handlerFunc); | ||
} | ||
} | ||
if (postMiddleware) { | ||
if (Array.isArray(postMiddleware)) { | ||
actions = actions.concat(postMiddleware); | ||
} else { | ||
actions.push(postMiddleware); | ||
} | ||
} | ||
if (expiry) { | ||
actions = appendExpiryCheck(actions, expiry); | ||
} | ||
server[method.toLowerCase()](routePath, actions); | ||
}); | ||
}); | ||
}); | ||
if (cb && typeof cb === 'function') { | ||
cb(); | ||
} | ||
} | ||
module.exports = { | ||
createRoute : createRoute | ||
}, | ||
validate: parser.parse | ||
}; | ||
{ | ||
"name": "restify-enroute", | ||
"version": "2.0.1", | ||
"version": "3.0.1", | ||
"main": "./lib/index.js", | ||
"description": "dynamically inject routes into restify app from configuration file", | ||
"description": "Config driven restify route creation", | ||
"homepage": "http://www.restify.com", | ||
"author": { | ||
"name": "Tsubomi Imamura", | ||
"email": "tsubomi.imamura@gmail.com" | ||
"name": "Yunong Xiao" | ||
}, | ||
@@ -34,19 +33,20 @@ "repository": { | ||
"devDependencies": { | ||
"chai": "^3.2.0", | ||
"eslint": "^0.24.1", | ||
"jscs": "^1.13.1", | ||
"mocha": "^2.2.5", | ||
"nsp": "^2.1.0" | ||
"chai": "^3.5.0", | ||
"coveralls": "^2.11.14", | ||
"eslint": "^3.8.1", | ||
"istanbul": "^0.4.5", | ||
"jscs": "^3.0.7", | ||
"mocha": "^3.1.2", | ||
"nsp": "^2.6.2", | ||
"restify": "^4.0.0", | ||
"restify-clients": "^1.4.0", | ||
"uuid": "^2.0.3" | ||
}, | ||
"dependencies": { | ||
"app-root-path": "^1.0.0", | ||
"assert-plus": "^0.1.5", | ||
"body-parser": "^1.14.1", | ||
"bunyan": "^1.5.1", | ||
"dtrace-provider": "^0.6.0", | ||
"restify": "^4.1.1", | ||
"restify-clients": "^1.1.1", | ||
"shelljs": "^0.5.3", | ||
"ajv": "^4.8.0", | ||
"assert-plus": "^1.0.0", | ||
"lodash": "^4.16.4", | ||
"vasync": "^1.6.4", | ||
"verror": "^1.6.0" | ||
} | ||
} |
250
README.md
@@ -1,29 +0,48 @@ | ||
# enroute | ||
This module provides convenience way to specify restify route in a declarative JSON format. | ||
## Route Configuration file Example | ||
# restify-enroute | ||
[](https://npmjs.org/package/restify-enroute) | ||
[](https://travis-ci.org/restify/enroute) | ||
[](https://coveralls.io/r/restify/enroute?branch=master) | ||
[](https://david-dm.org/restify/enroute) | ||
[](https://david-dm.org/restify/enroute#info=devDependencies) | ||
[](https://www.bithound.io/github/restify/enroute/master) | ||
[](https://travis-ci.org/restify/enroute) | ||
This module provides configuration driven route installation for restify. | ||
Instead of having to declare routes in code, you can create a confiuration file | ||
like this: | ||
```json | ||
{ | ||
"schemaVersion": 1, | ||
"routes": { | ||
"acceptcookies": { | ||
"foo": { | ||
"get": { | ||
"source": "./test/etc/fooGet.js" | ||
}, | ||
"post": { | ||
"source": "./test/etc/fooPost.js" | ||
}, | ||
"put": { | ||
"source": "./routes/putAcceptCookies.js" | ||
"source": "./test/etc/fooPut.js" | ||
}, | ||
"get": { | ||
"source": "./routes/getAcceptCookies.js" | ||
"delete": { | ||
"source": "./test/etc/fooDelete.js" | ||
}, | ||
"head": { | ||
"source": "./test/etc/fooHead.js" | ||
}, | ||
"patch": { | ||
"source": "./test/etc/fooPatch.js" | ||
}, | ||
"options": { | ||
"source": "./test/etc/fooOptions.js" | ||
} | ||
}, | ||
"accountInfo": { | ||
"bar": { | ||
"get": { | ||
"source": "./routes/getAccountInfo.js" | ||
} | ||
}, | ||
"cancelPlan": { | ||
"source": "./test/etc/barGet.js" | ||
}, | ||
"post": { | ||
"source": "./routes/postCancelPlan.js" | ||
"source": "./test/etc/barPost.js" | ||
} | ||
}, | ||
"accountInfo/old": { | ||
"get": { | ||
"source": "./routes/getAccountInfoOld.js" | ||
} | ||
} | ||
@@ -33,117 +52,114 @@ } | ||
``` | ||
The second level keys "acceptcookies", "accountInfo", "cancelPlan", "accountInfo/old" is url path. | ||
The third level keys are HTTP method names. | ||
The value of "source" is the route handler script's relative path from app root. | ||
This declares the route name, http method, and handler file on disk. this | ||
module will install these routes onto a restify server for you. The | ||
corresponding handler file would look like: | ||
## Usage Example | ||
```javascript | ||
enroute.createRoute({ | ||
//restify server object | ||
server: server, | ||
//bunyan log object | ||
log: log, | ||
//array of route config object, the object includes route configuration file path | ||
//and an optional baseUrl String, it will be prepend | ||
//for every route path specified in route configuration file. user can also provide | ||
//route definition object directly by doing | ||
//routeConf: [{routeDefinitionObj : routeObj}], | ||
routeConf: [{filePath : routeScriptRootPath + '/route.json'}], | ||
//user can specify an array of universal middleware function before all routes | ||
preMiddleware : preHandlerArr, | ||
//user can specify an array of universal middleware function after all routes | ||
postMiddleware : postMiddleware, | ||
//the root path of all route scripts, it can be different from route.json path | ||
scriptPath : routeScriptRootPath | ||
}, function() { | ||
//user can specify optional callback function | ||
client.get('/hello', | ||
function(err, req, res, obj) { | ||
if (err) { | ||
done(err); | ||
} | ||
assert.equal(obj.data, 'Hello world!', | ||
'Hello World endpoint response.'); | ||
done(); | ||
}); | ||
}); | ||
}); | ||
module.exports = function handler(req, res, next) { | ||
res.send(200, 'Hello World'); | ||
next() | ||
}; | ||
``` | ||
Routes can be injected into Restify server either before or after server start. | ||
## API | ||
Synopsis: `install(opts, cb)` | ||
## Route Handler Script Example | ||
```javascript | ||
'use strict'; | ||
// Node core modules | ||
var url = require('url'); | ||
Installs routes as defined in opts into a restify server, invokes the callback | ||
when done. | ||
* `opts`: The options object containing | ||
* `opts.server` The restify server to install the routes on to. | ||
* `[opts.config]` The POJO of the enroute config. | ||
* `[opts.configPath]` The path to the enroute config on disk. | ||
* `cb` The callback. Returns `Error` if there's an error installing the routes. | ||
Note only one of `opts.config` or `opts.configPath` is needed. The module will | ||
either read in the file from disk, or use a pre-populated POJO. | ||
// Third party modules specified in package.json | ||
var _ = require('lodash'); | ||
var falcor = require('falcor'); | ||
var verror = require('verror'); | ||
### Example | ||
```javascript | ||
const enroute = require('restify-enroute'); | ||
const restify = require('restify'); | ||
function accountInfo(req, res, next) { | ||
var queryparams = req.queryParams; | ||
// bunyan logger | ||
var log = req.log; | ||
// metrics client; | ||
var metrics = req.metrics; | ||
// api client | ||
var apiClient = req.apiClient; | ||
const CONFIG = { | ||
schemaVersion: 1, | ||
routes: { | ||
foo: { | ||
get: { | ||
source: './test/etc/fooGet.js' | ||
}, | ||
post: { | ||
source: './test/etc/fooPost.js' | ||
}, | ||
delete: { | ||
source: './test/etc/fooDelete.js' | ||
}, | ||
head: { | ||
source: './test/etc/fooHead.js' | ||
}, | ||
} | ||
} | ||
}; | ||
// Make some api requests | ||
const server = restify.createServer(); | ||
// install routes with enroute | ||
enroute.install({ | ||
config: CONFIG, | ||
server: server | ||
}, function (err) { | ||
if (err) { | ||
console.error('unable to install routes'); | ||
} else { | ||
console.log('routes installed'); | ||
SERVER.listen(1337); | ||
} | ||
}); | ||
``` | ||
// send back response | ||
res.send(200); | ||
Synopsis: `validate(opts, cb)` | ||
return next(); | ||
} | ||
Parse and validate a enroute config. This will verify that the config | ||
is valid and return a POJO with the properties. Note only one of opts.config | ||
or opts.configPath is needed. | ||
module.exports = accountInfo; | ||
``` | ||
Route definitions must export as single function or an array of functions where the signature conforms to f(req, res, next). This is identical to writing a restify handler function. The req object contains request state, the api client, logging and metric clients that can be leveraged by the user land code in the endpoint script. The res object is used to send back the response to the client, and next() must be invoked when the script has completed. | ||
* `opts` The options object containing | ||
* `[opts.config]` The POJO of the config you want to validate. | ||
* `[opts.configPath]` The path to the config on disk to validate. | ||
* `cb` The callback f(err, validatedConfig). Returns `Error` if there's an | ||
* error parsing or validating the config | ||
Instead of a single function, route definitions can export an array of chained function handlers; increasing the modularity of their endpoint scripts. | ||
### Example | ||
```javascript | ||
'use strict'; | ||
// Node core modules | ||
var url = require('url'); | ||
const enroute = require('restify-enroute'); | ||
const CONFIG = { | ||
schemaVersion: 1, | ||
routes: { | ||
foo: { | ||
get: { | ||
source: './test/etc/fooGet.js' | ||
}, | ||
post: { | ||
source: './test/etc/fooPost.js' | ||
}, | ||
delete: { | ||
source: './test/etc/fooDelete.js' | ||
}, | ||
head: { | ||
source: './test/etc/fooHead.js' | ||
}, | ||
} | ||
} | ||
}; | ||
// Third party modules specified in metadata dependencies/floatDependencies fields | ||
var _ = require('lodash'); | ||
var falcor = require('falcor'); | ||
var verror = require('verror'); | ||
var postHandler = require('somePostHandler'); | ||
function somePreHandler(req, res, next) { | ||
return next(); | ||
} | ||
function accountInfo(req, res, next) { | ||
var queryparams = req.queryParams; | ||
// bunyan logger | ||
var log = req.log; | ||
// metrics client; | ||
var metrics = req.metrics; | ||
// api client | ||
var apiClient = req.apiClient; | ||
// Make some api requests | ||
// send back response | ||
res.send(200); | ||
return next(); | ||
} | ||
module.exports = [somePreHandler, accountInfo, postHandler]; | ||
const server = restify.createServer(); | ||
// install routes with enroute | ||
enroute.validate({ | ||
config: CONFIG | ||
}, function (err) { | ||
if (err) { | ||
console.error('unable to install routes'); | ||
} else { | ||
console.log('config successfully validated'); | ||
} | ||
}); | ||
``` | ||
Notice that the functions can be both defined within the script itself, but can also be a module. Critically, every function in the chain must invoke ```next()```, otherwise the request will hang. | ||
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
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
Uses eval
Supply chain riskPackage uses dynamic code execution (e.g., eval()), which is a dangerous practice. This can prevent the code from running in certain environments and increases the risk that the code may contain exploits or malicious behavior.
Found 1 instance in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
21641
5
7
376
165
10
3
+ Addedajv@^4.8.0
+ Addedlodash@^4.16.4
+ Addedvasync@^1.6.4
+ Addedajv@4.11.8(transitive)
+ Addedcall-bind@1.0.8(transitive)
+ Addedco@4.6.0(transitive)
+ Addeddefine-data-property@1.1.4(transitive)
+ Addedhas-property-descriptors@1.0.2(transitive)
+ Addedisarray@2.0.5(transitive)
+ Addedjson-stable-stringify@1.2.1(transitive)
+ Addedjsonify@0.0.1(transitive)
+ Addedobject-keys@1.1.1(transitive)
+ Addedset-function-length@1.2.2(transitive)
+ Addedvasync@1.6.4(transitive)
- Removedapp-root-path@^1.0.0
- Removedbody-parser@^1.14.1
- Removedbunyan@^1.5.1
- Removeddtrace-provider@^0.6.0
- Removedrestify@^4.1.1
- Removedrestify-clients@^1.1.1
- Removedshelljs@^0.5.3
- Removedapp-root-path@1.4.0(transitive)
- Removedasn1@0.1.11(transitive)
- Removedassert-plus@0.1.50.2.0(transitive)
- Removedbackoff@2.5.0(transitive)
- Removedbalanced-match@1.0.2(transitive)
- Removedbody-parser@1.20.3(transitive)
- Removedbrace-expansion@1.1.11(transitive)
- Removedbunyan@1.8.15(transitive)
- Removedbytes@3.1.2(transitive)
- Removedconcat-map@0.0.1(transitive)
- Removedcontent-type@1.0.5(transitive)
- Removedcore-util-is@1.0.3(transitive)
- Removedcsv@0.4.6(transitive)
- Removedcsv-generate@0.0.6(transitive)
- Removedcsv-parse@1.3.3(transitive)
- Removedcsv-stringify@0.0.8(transitive)
- Removedctype@0.5.3(transitive)
- Removeddebug@2.6.9(transitive)
- Removeddepd@2.0.0(transitive)
- Removeddestroy@1.2.0(transitive)
- Removeddetect-node@2.1.0(transitive)
- Removeddtrace-provider@0.6.00.8.8(transitive)
- Removedee-first@1.1.1(transitive)
- Removedescape-regexp-component@1.0.2(transitive)
- Removedfast-safe-stringify@1.2.3(transitive)
- Removedformidable@1.2.6(transitive)
- Removedglob@6.0.4(transitive)
- Removedhandle-thing@1.2.5(transitive)
- Removedhpack.js@2.1.6(transitive)
- Removedhttp-deceiver@1.2.7(transitive)
- Removedhttp-errors@2.0.0(transitive)
- Removedhttp-signature@0.11.0(transitive)
- Removediconv-lite@0.4.24(transitive)
- Removedinflight@1.0.6(transitive)
- Removedinherits@2.0.4(transitive)
- Removedisarray@1.0.0(transitive)
- Removedkeep-alive-agent@0.0.1(transitive)
- Removedlodash@3.10.1(transitive)
- Removedlru-cache@4.1.5(transitive)
- Removedmedia-typer@0.3.0(transitive)
- Removedmime@1.6.0(transitive)
- Removedmime-db@1.52.0(transitive)
- Removedmime-types@2.1.35(transitive)
- Removedminimalistic-assert@1.0.1(transitive)
- Removedminimatch@3.1.2(transitive)
- Removedminimist@1.2.8(transitive)
- Removedmkdirp@0.5.6(transitive)
- Removedmoment@2.30.1(transitive)
- Removedms@2.0.0(transitive)
- Removedmv@2.1.1(transitive)
- Removednan@2.22.2(transitive)
- Removedncp@2.0.0(transitive)
- Removednegotiator@0.6.4(transitive)
- Removedobject-inspect@1.13.4(transitive)
- Removedobuf@1.1.2(transitive)
- Removedon-finished@2.4.1(transitive)
- Removedonce@1.4.0(transitive)
- Removedpath-is-absolute@1.0.1(transitive)
- Removedprecond@0.2.3(transitive)
- Removedprocess-nextick-args@2.0.1(transitive)
- Removedpseudomap@1.0.2(transitive)
- Removedqs@6.13.0(transitive)
- Removedraw-body@2.5.2(transitive)
- Removedreadable-stream@2.3.8(transitive)
- Removedrestify@4.3.4(transitive)
- Removedrestify-clients@1.6.0(transitive)
- Removedrestify-errors@3.1.0(transitive)
- Removedrimraf@2.4.5(transitive)
- Removedsafe-buffer@5.1.25.2.1(transitive)
- Removedsafe-json-stringify@1.2.0(transitive)
- Removedsafer-buffer@2.1.2(transitive)
- Removedselect-hose@2.0.0(transitive)
- Removedsemver@4.3.65.7.2(transitive)
- Removedsetprototypeof@1.2.0(transitive)
- Removedshelljs@0.5.3(transitive)
- Removedside-channel@1.1.0(transitive)
- Removedside-channel-list@1.0.0(transitive)
- Removedside-channel-map@1.0.1(transitive)
- Removedside-channel-weakmap@1.0.2(transitive)
- Removedspdy@3.4.7(transitive)
- Removedspdy-transport@2.1.1(transitive)
- Removedstatuses@2.0.1(transitive)
- Removedstream-transform@0.1.2(transitive)
- Removedstring_decoder@1.1.1(transitive)
- Removedtoidentifier@1.0.1(transitive)
- Removedtunnel-agent@0.6.0(transitive)
- Removedtype-is@1.6.18(transitive)
- Removedunpipe@1.0.0(transitive)
- Removedutil-deprecate@1.0.2(transitive)
- Removeduuid@3.4.0(transitive)
- Removedvasync@1.6.3(transitive)
- Removedwbuf@1.7.3(transitive)
- Removedwrappy@1.0.2(transitive)
- Removedyallist@2.1.2(transitive)
Updatedassert-plus@^1.0.0