winston-middleware 0.2.4 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/.travis.yml CHANGED
@@ -2,4 +2,11 @@ language: node_js
2
2
  node_js:
3
3
  - "0.6"
4
4
  - "0.8"
5
- script: "npm test"
5
+ - "0.10"
6
+ - "0.11"
7
+ - "0.12"
8
+ - "iojs"
9
+
10
+ script:
11
+ - "test $TRAVIS_NODE_VERSION = '0.6' || npm test"
12
+ - "test $TRAVIS_NODE_VERSION != '0.6' || npm run-script test-coverage"
package/AUTHORS CHANGED
@@ -1,4 +1,7 @@
1
1
  Heapsource <npm@heapsource.com> (http://www.heapsource.com)
2
2
  Lars Jacob (http://jaclar.net)
3
- Jonathan Lomas (http://feedbackular.com)
3
+ Jonathan Lomas (http://floatinglomas.ca)
4
4
  Xavier Damman (http://xdamman.com)
5
+ Quentin Rossetti <quentin.rossetti@gmail.com>
6
+ Damian Kaczmarek <rush@rushbase.net>
7
+ Robbie Trencheny <me@robbiet.us> (http://robbie.io)
package/LICENSE CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2012-2013 Heapsource.com - http://www.heapsource.com
1
+ Copyright (c) 2012-2014 Bithavoc.io - http://bithavoc.io
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining a copy
4
4
  of this software and associated documentation files (the "Software"), to deal
package/Readme.md CHANGED
@@ -1,5 +1,5 @@
1
1
  # winston-middleware
2
- [![Build Status](https://secure.travis-ci.org/firebaseco/winston-middleware.png)](http://travis-ci.org/firebaseco/winston-middleware)
2
+ [![Build Status](https://secure.travis-ci.org/bithavoc/winston-middleware.png)](http://travis-ci.org/bithavoc/winston-middleware)
3
3
 
4
4
  > [winston](https://github.com/flatiron/winston) middleware for express.js
5
5
 
@@ -38,7 +38,9 @@ var winston = require('winston'),
38
38
  Use `expressWinston.errorLogger(options)` to create a middleware that log the errors of the pipeline.
39
39
 
40
40
  ``` js
41
- app.use(app.router); // notice how the router goes first.
41
+ var router = require('./my-express-router');
42
+
43
+ app.use(router); // notice how the router goes first.
42
44
  app.use(expressWinston.errorLogger({
43
45
  transports: [
44
46
  new winston.transports.Console({
@@ -55,14 +57,22 @@ The logger needs to be added AFTER the express router(`app.router)`) and BEFORE
55
57
 
56
58
  ``` js
57
59
  transports: [<WinstonTransport>], // list of all winston transports instances to use.
58
- level: String // log level to use, the default is "info".
60
+ winstonInstance: <WinstonLogger>, // a winston logger instance. If this is provided the transports option is ignored
61
+ level: String, // log level to use, the default is "info".
62
+ statusLevels: Boolean // different HTTP status codes caused log messages to be logged at different levels (info/warn/error), the default is false
63
+ skip: function(req, res) // function to determine if logging is skipped, defaults to false
59
64
  ```
60
65
 
66
+ To use winston's existing transports, set `transports` to the values (as in key-value) of the `winston.default.transports` object. This may be done, for example, by using underscorejs: `transports: _.values(winston.default.transports)`.
67
+
68
+ Alternatively, if you're using a winston logger instance elsewhere and have already set up levels and transports, pass the instance into expressWinston with the `winstonInstance` option. The `transports` option is then ignored.
69
+
61
70
  ### Request Logging
62
71
 
63
72
  Use `expressWinston.logger(options)` to create a middleware to log your HTTP requests.
64
73
 
65
74
  ``` js
75
+ var router = require('./my-express-router');
66
76
 
67
77
  app.use(expressWinston.logger({
68
78
  transports: [
@@ -72,9 +82,13 @@ Use `expressWinston.logger(options)` to create a middleware to log your HTTP req
72
82
  })
73
83
  ],
74
84
  meta: true, // optional: control whether you want to log the meta data about the request (default to true)
75
- msg: "HTTP {{req.method}} {{req.url}}" // optional: customize the default logging message. E.g. "{{res.statusCode}} {{req.method}} {{res.responseTime}}ms {{req.url}}"
85
+ msg: "HTTP {{req.method}} {{req.url}}", // optional: customize the default logging message. E.g. "{{res.statusCode}} {{req.method}} {{res.responseTime}}ms {{req.url}}"
86
+ expressFormat: true, // Use the default Express/morgan request formatting, with the same colors. Enabling this will override any msg and colorStatus if true. Will only output colors on transports with colorize set to true
87
+ colorStatus: true, // Color the status code, using the Express/morgan color palette (default green, 3XX cyan, 4XX yellow, 5XX red). Will not be recognized if expressFormat is true
88
+ ignoreRoute: function (req, res) { return false; } // optional: allows to skip some log messages based on request and/or response
76
89
  }));
77
- app.use(app.router); // notice how the router goes after the logger.
90
+
91
+ app.use(router); // notice how the router goes after the logger.
78
92
  ```
79
93
 
80
94
  ## Examples
@@ -88,6 +102,18 @@ Use `expressWinston.logger(options)` to create a middleware to log your HTTP req
88
102
  app.use(express.bodyParser());
89
103
  app.use(express.methodOverride());
90
104
 
105
+ // Let's make our express `Router` first.
106
+ var router = express.Router();
107
+ router.get('/error', function(req, res, next) {
108
+ // here we cause an error in the pipeline so we see winston-middleware in action.
109
+ return next(new Error("This is an error and it should be logged to the console"));
110
+ });
111
+
112
+ app.get('/', function(req, res, next) {
113
+ res.write('This is a normal request, it should be logged to the console too');
114
+ res.end();
115
+ });
116
+
91
117
  // winston-middleware logger makes sense BEFORE the router.
92
118
  app.use(expressWinston.logger({
93
119
  transports: [
@@ -98,7 +124,8 @@ Use `expressWinston.logger(options)` to create a middleware to log your HTTP req
98
124
  ]
99
125
  }));
100
126
 
101
- app.use(app.router);
127
+ // Now we can tell the app to use our routing code:
128
+ app.use(router);
102
129
 
103
130
  // winston-middleware errorLogger makes sense AFTER the router.
104
131
  app.use(expressWinston.errorLogger({
@@ -116,18 +143,8 @@ Use `expressWinston.logger(options)` to create a middleware to log your HTTP req
116
143
  showStack: true
117
144
  }));
118
145
 
119
- app.get('/error', function(req, res, next) {
120
- // here we cause an error in the pipeline so we see winston-middleware in action.
121
- return next(new Error("This is an error and it should be logged to the console"));
122
- });
123
-
124
- app.get('/', function(req, res, next) {
125
- res.write('This is a normal request, it should be logged to the console too');
126
- res.end();
127
- });
128
-
129
146
  app.listen(3000, function(){
130
- console.log("winston-middleware demo listening on port %d in %s mode", app.address().port, app.settings.env);
147
+ console.log("winston-middleware demo listening on port %d in %s mode", this.address().port, app.settings.env);
131
148
  });
132
149
  ```
133
150
 
@@ -234,11 +251,42 @@ Browse `/error` will show you how winston-middleware handles and logs the errors
234
251
  "message": "middlewareError"
235
252
  }
236
253
 
237
- ## Whitelists
254
+ ## Global Whitelists and Blacklists
255
+
256
+ winston-middleware exposes three whitelists that control which properties of the `request`, `body`, and `response` are logged:
257
+
258
+ * `requestWhitelist`
259
+ * `bodyWhitelist`, `bodyBlacklist`
260
+ * `responseWhitelist`
261
+
262
+ For example, `requestWhitelist` defaults to:
263
+
264
+ ['url', 'headers', 'method', 'httpVersion', 'originalUrl', 'query'];
265
+
266
+ Only those properties of the request object will be logged. Set or modify the whitelist as necessary.
267
+
268
+ For example, to include the session property (the session data), add the following during logger setup:
269
+
270
+ expressWinston.requestWhitelist.push('session');
271
+
272
+ The blacklisting excludes certain properties and keeps all others. If both `bodyWhitelist` and `bodyBlacklist` are set
273
+ the properties excluded by the blacklist are not included even if they are listed in the whitelist!
274
+
275
+ Example:
276
+
277
+ expressWinston.bodyBlacklist.push('secretid', 'secretproperty');
278
+
279
+ Note that you can log the whole request and/or response body:
280
+
281
+ expressWinston.requestWhitelist.push('body');
282
+ expressWinston.responseWhitelist.push('body');
283
+
284
+ ## Route-Specific Whitelists and Blacklists
285
+
238
286
  New in version 0.2.x is the ability to add whitelist elements in a route. winston-middleware adds a `_routeWhitelists` object to the `req`uest, containing `.body`, `.req` and .res` properties, to which you can set an array of 'whitelist' parameters to include in the log, specific to the route in question:
239
287
 
240
288
  ``` js
241
- app.post('/user/register', function(req, res, next) {
289
+ router.post('/user/register', function(req, res, next) {
242
290
  req._routeWhitelists.body = ['username', 'email', 'age']; // But not 'password' or 'confirm-password' or 'top-secret'
243
291
  req._routeWhitelists.res = ['_headers'];
244
292
  });
@@ -277,21 +325,42 @@ Post to `/user/register` would give you something like the following:
277
325
  "message": "HTTP GET /favicon.ico"
278
326
  }
279
327
 
328
+ Blacklisting supports only the `body` property.
329
+
330
+
331
+ ``` js
332
+ router.post('/user/register', function(req, res, next) {
333
+ req._routeWhitelists.body = ['username', 'email', 'age']; // But not 'password' or 'confirm-password' or 'top-secret'
334
+ req._routeBlacklists.body = ['username', 'password', 'confirm-password', 'top-secret'];
335
+ req._routeWhitelists.res = ['_headers'];
336
+ });
337
+ ```
338
+
339
+ If both `req._bodyWhitelist.body` and `req._bodyBlacklist.body` are set the result will be the white listed properties
340
+ excluding any black listed ones. In the above example, only 'email' and 'age' would be included.
341
+
342
+
280
343
  ## Tests
281
344
 
345
+ Run the basic Mocha tests:
346
+
282
347
  npm test
283
348
 
284
- ## Issues and Collaboration
349
+ Run the Travis-CI tests (which will fail with < 100% coverage):
285
350
 
286
- * Implement a chain of requestFilters. Currently only one requestFilter is allowed in the options.
351
+ npm test-travis
287
352
 
288
- We are accepting pull-request for these features.
353
+ Generate the `coverage.html` coverage report:
354
+
355
+ npm test-coverage
356
+
357
+ ## Issues and Collaboration
289
358
 
290
- If you ran into any problems, please use the project [Issues section](https://github.com/firebaseco/winston-middleware/issues) to search or post any bug.
359
+ If you ran into any problems, please use the project [Issues section](https://github.com/bithavoc/winston-middleware/issues) to search or post any bug.
291
360
 
292
361
  ## Contributors
293
362
 
294
- * [Johan Hernandez](https://github.com/thepumpkin1979) (https://github.com/thepumpkin1979)
363
+ * [Johan Hernandez](https://github.com/bithavoc) (https://github.com/bithavoc)
295
364
  * [Lars Jacob](https://github.com/jaclar) (https://github.com/jaclar)
296
365
  * [Jonathan Lomas](https://github.com/floatingLomas) (https://github.com/floatingLomas)
297
366
 
@@ -299,7 +368,7 @@ Also see AUTHORS file, add yourself if you are missing.
299
368
 
300
369
  ## MIT License
301
370
 
302
- Copyright (c) 2012-2014 Heapsource.com and Contributors - http://www.heapsource.com
371
+ Copyright (c) 2012-2014 Bithavoc.io and Contributors - http://bithavoc.io
303
372
 
304
373
  Permission is hereby granted, free of charge, to any person obtaining a copy
305
374
  of this software and associated documentation files (the "Software"), to deal
package/index.js CHANGED
@@ -20,6 +20,7 @@
20
20
  //
21
21
  var winston = require('winston');
22
22
  var util = require('util');
23
+ var chalk = require('chalk');
23
24
 
24
25
  //Allow this file to get an exclusive copy of underscore so it can change the template settings without affecting others
25
26
  delete require.cache[require.resolve('underscore')];
@@ -42,6 +43,12 @@ var requestWhitelist = ['url', 'headers', 'method', 'httpVersion', 'originalUrl'
42
43
  */
43
44
  var bodyWhitelist = [];
44
45
 
46
+ /**
47
+ * A default list of properties in the request body that are not allowed to be logged.
48
+ * @type {Array}
49
+ */
50
+ var bodyBlacklist = [];
51
+
45
52
  /**
46
53
  * A default list of properties in the response object that are allowed to be logged.
47
54
  * These properties will be safely included in the meta of the log.
@@ -49,6 +56,13 @@ var bodyWhitelist = [];
49
56
  */
50
57
  var responseWhitelist = ['statusCode'];
51
58
 
59
+ /**
60
+ * A list of request routes that will be skipped instead of being logged. This would be useful if routes for health checks or pings would otherwise pollute
61
+ * your log files.
62
+ * @type {Array}
63
+ */
64
+ var ignoredRoutes = [];
65
+
52
66
  /**
53
67
  * A default function to filter the properties of the req object.
54
68
  * @param req
@@ -65,23 +79,33 @@ var defaultRequestFilter = function (req, propName) {
65
79
  * @param propName
66
80
  * @return {*}
67
81
  */
68
- var defaultResponseFilter = function (req, propName) {
69
- return req[propName];
82
+ var defaultResponseFilter = function (res, propName) {
83
+ return res[propName];
84
+ };
85
+
86
+ /**
87
+ * A default function to decide whether skip logging of particular request. Doesn't skip anything (i.e. log all requests).
88
+ * @return always false
89
+ */
90
+ var defaultSkip = function() {
91
+ return false;
70
92
  };
71
93
 
72
94
  function filterObject(originalObj, whiteList, initialFilter) {
73
95
 
74
96
  var obj = {};
97
+ var fieldsSet = false;
75
98
 
76
99
  [].concat(whiteList).forEach(function (propName) {
77
100
  var value = initialFilter(originalObj, propName);
78
101
 
79
102
  if(typeof (value) !== 'undefined') {
80
103
  obj[propName] = value;
104
+ fieldsSet = true;
81
105
  };
82
106
  });
83
107
 
84
- return obj;
108
+ return fieldsSet?obj:undefined;
85
109
  }
86
110
 
87
111
  //
@@ -95,6 +119,7 @@ function errorLogger(options) {
95
119
  ensureValidOptions(options);
96
120
 
97
121
  options.requestFilter = options.requestFilter || defaultRequestFilter;
122
+ options.winstonInstance = options.winstonInstance || (new winston.Logger ({ transports: options.transports }));
98
123
 
99
124
  return function (err, req, res, next) {
100
125
 
@@ -103,12 +128,7 @@ function errorLogger(options) {
103
128
  exceptionMeta.req = filterObject(req, requestWhitelist, options.requestFilter);
104
129
 
105
130
  // This is fire and forget, we don't want logging to hold up the request so don't wait for the callback
106
- for(var i = 0; i < options.transports.length; i++) {
107
- var transport = options.transports[i];
108
- transport.logException('middlewareError', exceptionMeta, function () {
109
- // Nothing to do here
110
- });
111
- }
131
+ options.winstonInstance.log('error', 'middlewareError', exceptionMeta);
112
132
 
113
133
  next(err);
114
134
  };
@@ -123,13 +143,28 @@ function errorLogger(options) {
123
143
  function logger(options) {
124
144
 
125
145
  ensureValidOptions(options);
146
+ ensureValidLoggerOptions(options);
126
147
 
127
148
  options.requestFilter = options.requestFilter || defaultRequestFilter;
128
149
  options.responseFilter = options.responseFilter || defaultResponseFilter;
150
+ options.winstonInstance = options.winstonInstance || (new winston.Logger ({ transports: options.transports }));
129
151
  options.level = options.level || "info";
152
+ options.statusLevels = options.statusLevels || false;
130
153
  options.msg = options.msg || "HTTP {{req.method}} {{req.url}}";
154
+ options.colorStatus = options.colorStatus || false;
155
+ options.expressFormat = options.expressFormat || false;
156
+ options.ignoreRoute = options.ignoreRoute || function () { return false; };
157
+ options.skip = options.skip || defaultSkip;
158
+
159
+ // Using mustache style templating
160
+ var template = _.template(options.msg, null, {
161
+ interpolate: /\{\{(.+?)\}\}/g
162
+ });
131
163
 
132
164
  return function (req, res, next) {
165
+ var currentUrl = req.originalUrl || req.url;
166
+ if (currentUrl && _.contains(ignoredRoutes, currentUrl)) return next();
167
+ if (options.ignoreRoute(req, res)) return next();
133
168
 
134
169
  req._startTime = (new Date);
135
170
 
@@ -139,6 +174,10 @@ function logger(options) {
139
174
  body: []
140
175
  };
141
176
 
177
+ req._routeBlacklists = {
178
+ body: []
179
+ };
180
+
142
181
  // Manage to get information from the response too, just like Connect.logger does:
143
182
  var end = res.end;
144
183
  res.end = function(chunk, encoding) {
@@ -147,10 +186,27 @@ function logger(options) {
147
186
  res.end = end;
148
187
  res.end(chunk, encoding);
149
188
 
150
- if(options.meta !== false) {
151
- var meta = {};
189
+ req.url = req.originalUrl || req.url;
190
+
191
+ if (options.statusLevels) {
192
+ if (res.statusCode >= 100) { options.level = "info"; }
193
+ if (res.statusCode >= 400) { options.level = "warn"; }
194
+ if (res.statusCode >= 500) { options.level = "error"; }
195
+ };
196
+
197
+ if (options.colorStatus || options.expressFormat) {
198
+ // Palette from https://github.com/expressjs/morgan/blob/master/index.js#L205
199
+ var statusColor = 'green';
200
+ if (res.statusCode >= 500) statusColor = 'red';
201
+ else if (res.statusCode >= 400) statusColor = 'yellow';
202
+ else if (res.statusCode >= 300) statusColor = 'cyan';
203
+ var coloredStatusCode = chalk[statusColor](res.statusCode);
204
+ }
152
205
 
153
- var bodyWhitelist;
206
+ var meta = {};
207
+
208
+ if(options.meta !== false) {
209
+ var bodyWhitelist, blacklist;
154
210
 
155
211
  requestWhitelist = requestWhitelist.concat(req._routeWhitelists.req || []);
156
212
  responseWhitelist = responseWhitelist.concat(req._routeWhitelists.res || []);
@@ -158,28 +214,44 @@ function logger(options) {
158
214
  meta.req = filterObject(req, requestWhitelist, options.requestFilter);
159
215
  meta.res = filterObject(res, responseWhitelist, options.responseFilter);
160
216
 
217
+ if (_.contains(responseWhitelist, 'body')) {
218
+ if (chunk) {
219
+ var isJson = (res._headers && res._headers['content-type']
220
+ && res._headers['content-type'].indexOf('json') >= 0);
221
+
222
+ meta.res.body = isJson ? JSON.parse(chunk) : chunk.toString();
223
+ }
224
+ }
225
+
161
226
  bodyWhitelist = req._routeWhitelists.body || [];
227
+ blacklist = _.union(bodyBlacklist, (req._routeBlacklists.body || []));
162
228
 
163
- if (bodyWhitelist) {
164
- meta.req.body = filterObject(req.body, bodyWhitelist, options.requestFilter);
165
- };
229
+ var filteredBody = null;
230
+
231
+ if ( req.body !== undefined ) {
232
+ if (blacklist.length > 0 && bodyWhitelist.length === 0) {
233
+ var whitelist = _.difference(_.keys(req.body), blacklist);
234
+ filteredBody = filterObject(req.body, whitelist, options.requestFilter);
235
+ } else {
236
+ filteredBody = filterObject(req.body, bodyWhitelist, options.requestFilter);
237
+ }
238
+ }
239
+
240
+ if (filteredBody) meta.req.body = filteredBody;
166
241
 
167
242
  meta.responseTime = res.responseTime;
168
243
  }
169
244
 
170
- // Using mustache style templating
171
- _.templateSettings = {
172
- interpolate: /\{\{(.+?)\}\}/g
173
- };
174
- var template = _.template(options.msg);
175
- var msg = template({req: req, res: res});
176
-
245
+ if(options.expressFormat) {
246
+ var msg = chalk.grey(req.method + " " + req.url || req.url)
247
+ + " " + chalk[statusColor](res.statusCode)
248
+ + " " + chalk.grey(res.responseTime+"ms");
249
+ } else {
250
+ var msg = template({req: req, res: res});
251
+ }
177
252
  // This is fire and forget, we don't want logging to hold up the request so don't wait for the callback
178
- for(var i = 0; i < options.transports.length; i++) {
179
- var transport = options.transports[i];
180
- transport.log(options.level, msg, meta, function () {
181
- // Nothing to do here
182
- });
253
+ if (!options.skip(req, res)) {
254
+ options.winstonInstance.log(options.level, msg, meta);
183
255
  }
184
256
  };
185
257
 
@@ -189,13 +261,23 @@ function logger(options) {
189
261
 
190
262
  function ensureValidOptions(options) {
191
263
  if(!options) throw new Error("options are required by winston-middleware middleware");
192
- if(!options.transports || !(options.transports.length > 0)) throw new Error("transports are required by winston-middleware middleware");
193
- };
264
+ if(!((options.transports && (options.transports.length > 0)) || options.winstonInstance))
265
+ throw new Error("transports or a winstonInstance are required by winston-middleware middleware");
266
+ }
267
+
268
+ function ensureValidLoggerOptions(options) {
269
+ if (options.ignoreRoute && !_.isFunction(options.ignoreRoute)) {
270
+ throw new Error("`ignoreRoute` winston-middleware option should be a function");
271
+ }
272
+ }
194
273
 
195
274
  module.exports.errorLogger = errorLogger;
196
275
  module.exports.logger = logger;
197
276
  module.exports.requestWhitelist = requestWhitelist;
198
277
  module.exports.bodyWhitelist = bodyWhitelist;
278
+ module.exports.bodyBlacklist = bodyBlacklist;
199
279
  module.exports.responseWhitelist = responseWhitelist;
200
280
  module.exports.defaultRequestFilter = defaultRequestFilter;
201
281
  module.exports.defaultResponseFilter = defaultResponseFilter;
282
+ module.exports.defaultSkip = defaultSkip;
283
+ module.exports.ignoredRoutes = ignoredRoutes;
package/package.json CHANGED
@@ -1,5 +1,9 @@
1
1
  {
2
- "author": "Heapsource.com <npm@heapsource.com> (http://www.heapsource.com)",
2
+ "author": {
3
+ "name": "bithavoc",
4
+ "email": "im@bithavoc.io",
5
+ "url": "http://bithavoc.io"
6
+ },
3
7
  "name": "winston-middleware",
4
8
  "description": "express.js middleware for flatiron/winston",
5
9
  "keywords": [
@@ -10,25 +14,75 @@
10
14
  "log",
11
15
  "error",
12
16
  "handler",
13
- "middleware"
17
+ "middleware",
18
+ "colors"
14
19
  ],
15
- "version": "0.2.4",
20
+ "version": "0.3.1",
16
21
  "repository": {
17
- "url": "https://github.com/heapsource/winston-middleware.git"
22
+ "type": "git",
23
+ "url": "https://github.com/bithavoc/winston-middleware.git"
24
+ },
25
+ "bugs": {
26
+ "url": "http://github.com/bithavoc/winston-middleware/issues",
27
+ "email": "im@bithavoc.io"
18
28
  },
19
29
  "main": "index.js",
20
30
  "scripts": {
21
- "test": "vows --spec"
31
+ "test": "node_modules/.bin/mocha --reporter spec",
32
+ "test-travis": "node_modules/.bin/mocha --require blanket --reporter travis-cov",
33
+ "test-coverage": "node_modules/.bin/mocha --require blanket --reporter html-cov >| coverage.html || true"
34
+ },
35
+ "config": {
36
+ "travis-cov": {
37
+ "threshold": 100
38
+ },
39
+ "blanket": {
40
+ "pattern": [
41
+ "index.js"
42
+ ],
43
+ "data-cover-never": [
44
+ "node_modules",
45
+ "test"
46
+ ]
47
+ }
22
48
  },
23
49
  "dependencies": {
24
- "winston": "0.6.x",
25
- "underscore": "~1.5.2"
50
+ "chalk": "~0.4.0",
51
+ "underscore": "~1.5.2",
52
+ "winston": "~0.9.0"
26
53
  },
27
54
  "devDependencies": {
28
- "vows": "0.7.x"
55
+ "blanket": "~1.1.6",
56
+ "mocha": "~2.1.0",
57
+ "node-mocks-http": "~1.2.3",
58
+ "should": "~4.6.0",
59
+ "travis-cov": "~0.2.5"
29
60
  },
30
61
  "engines": {
31
- "node": "*"
62
+ "node": ">=0.6.0"
32
63
  },
33
- "license": "MIT"
64
+ "license": "MIT",
65
+ "contributors": [
66
+ {
67
+ "name": "Lars Jacob",
68
+ "url": "http://jaclar.net"
69
+ },
70
+ {
71
+ "name": "Jonathan Lomas",
72
+ "url": "http://floatinglomas.ca"
73
+ },
74
+ {
75
+ "name": "Xavier Damman",
76
+ "url": "http://xdamman.com"
77
+ },
78
+ {
79
+ "name": "Quentin Rossetti",
80
+ "email": "quentin.rossetti@gmail.com"
81
+ },
82
+ {
83
+ "name": "Robbie Trencheny",
84
+ "email": "me@robbiet.us",
85
+ "url": "http://robbie.io"
86
+ }
87
+ ]
34
88
  }