winston-middleware 0.2.3 → 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,7 +20,12 @@
20
20
  //
21
21
  var winston = require('winston');
22
22
  var util = require('util');
23
+ var chalk = require('chalk');
24
+
25
+ //Allow this file to get an exclusive copy of underscore so it can change the template settings without affecting others
26
+ delete require.cache[require.resolve('underscore')];
23
27
  var _ = require('underscore');
28
+ delete require.cache[require.resolve('underscore')];
24
29
 
25
30
  /**
26
31
  * A default list of properties in the request object that are allowed to be logged.
@@ -38,6 +43,12 @@ var requestWhitelist = ['url', 'headers', 'method', 'httpVersion', 'originalUrl'
38
43
  */
39
44
  var bodyWhitelist = [];
40
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
+
41
52
  /**
42
53
  * A default list of properties in the response object that are allowed to be logged.
43
54
  * These properties will be safely included in the meta of the log.
@@ -45,6 +56,13 @@ var bodyWhitelist = [];
45
56
  */
46
57
  var responseWhitelist = ['statusCode'];
47
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
+
48
66
  /**
49
67
  * A default function to filter the properties of the req object.
50
68
  * @param req
@@ -61,23 +79,33 @@ var defaultRequestFilter = function (req, propName) {
61
79
  * @param propName
62
80
  * @return {*}
63
81
  */
64
- var defaultResponseFilter = function (req, propName) {
65
- 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;
66
92
  };
67
93
 
68
94
  function filterObject(originalObj, whiteList, initialFilter) {
69
95
 
70
96
  var obj = {};
97
+ var fieldsSet = false;
71
98
 
72
99
  [].concat(whiteList).forEach(function (propName) {
73
100
  var value = initialFilter(originalObj, propName);
74
101
 
75
102
  if(typeof (value) !== 'undefined') {
76
103
  obj[propName] = value;
104
+ fieldsSet = true;
77
105
  };
78
106
  });
79
107
 
80
- return obj;
108
+ return fieldsSet?obj:undefined;
81
109
  }
82
110
 
83
111
  //
@@ -91,6 +119,7 @@ function errorLogger(options) {
91
119
  ensureValidOptions(options);
92
120
 
93
121
  options.requestFilter = options.requestFilter || defaultRequestFilter;
122
+ options.winstonInstance = options.winstonInstance || (new winston.Logger ({ transports: options.transports }));
94
123
 
95
124
  return function (err, req, res, next) {
96
125
 
@@ -99,12 +128,7 @@ function errorLogger(options) {
99
128
  exceptionMeta.req = filterObject(req, requestWhitelist, options.requestFilter);
100
129
 
101
130
  // This is fire and forget, we don't want logging to hold up the request so don't wait for the callback
102
- for(var i = 0; i < options.transports.length; i++) {
103
- var transport = options.transports[i];
104
- transport.logException('middlewareError', exceptionMeta, function () {
105
- // Nothing to do here
106
- });
107
- }
131
+ options.winstonInstance.log('error', 'middlewareError', exceptionMeta);
108
132
 
109
133
  next(err);
110
134
  };
@@ -119,13 +143,28 @@ function errorLogger(options) {
119
143
  function logger(options) {
120
144
 
121
145
  ensureValidOptions(options);
146
+ ensureValidLoggerOptions(options);
122
147
 
123
148
  options.requestFilter = options.requestFilter || defaultRequestFilter;
124
149
  options.responseFilter = options.responseFilter || defaultResponseFilter;
150
+ options.winstonInstance = options.winstonInstance || (new winston.Logger ({ transports: options.transports }));
125
151
  options.level = options.level || "info";
152
+ options.statusLevels = options.statusLevels || false;
126
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
+ });
127
163
 
128
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();
129
168
 
130
169
  req._startTime = (new Date);
131
170
 
@@ -135,6 +174,10 @@ function logger(options) {
135
174
  body: []
136
175
  };
137
176
 
177
+ req._routeBlacklists = {
178
+ body: []
179
+ };
180
+
138
181
  // Manage to get information from the response too, just like Connect.logger does:
139
182
  var end = res.end;
140
183
  res.end = function(chunk, encoding) {
@@ -143,10 +186,27 @@ function logger(options) {
143
186
  res.end = end;
144
187
  res.end(chunk, encoding);
145
188
 
146
- if(options.meta !== false) {
147
- 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
+ }
148
205
 
149
- var bodyWhitelist;
206
+ var meta = {};
207
+
208
+ if(options.meta !== false) {
209
+ var bodyWhitelist, blacklist;
150
210
 
151
211
  requestWhitelist = requestWhitelist.concat(req._routeWhitelists.req || []);
152
212
  responseWhitelist = responseWhitelist.concat(req._routeWhitelists.res || []);
@@ -154,28 +214,44 @@ function logger(options) {
154
214
  meta.req = filterObject(req, requestWhitelist, options.requestFilter);
155
215
  meta.res = filterObject(res, responseWhitelist, options.responseFilter);
156
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
+
157
226
  bodyWhitelist = req._routeWhitelists.body || [];
227
+ blacklist = _.union(bodyBlacklist, (req._routeBlacklists.body || []));
158
228
 
159
- if (bodyWhitelist) {
160
- meta.req.body = filterObject(req.body, bodyWhitelist, options.requestFilter);
161
- };
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;
162
241
 
163
242
  meta.responseTime = res.responseTime;
164
243
  }
165
244
 
166
- // Using mustache style templating
167
- _.templateSettings = {
168
- interpolate: /\{\{(.+?)\}\}/g
169
- };
170
- var template = _.template(options.msg);
171
- var msg = template({req: req, res: res});
172
-
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
+ }
173
252
  // This is fire and forget, we don't want logging to hold up the request so don't wait for the callback
174
- for(var i = 0; i < options.transports.length; i++) {
175
- var transport = options.transports[i];
176
- transport.log(options.level, msg, meta, function () {
177
- // Nothing to do here
178
- });
253
+ if (!options.skip(req, res)) {
254
+ options.winstonInstance.log(options.level, msg, meta);
179
255
  }
180
256
  };
181
257
 
@@ -185,13 +261,23 @@ function logger(options) {
185
261
 
186
262
  function ensureValidOptions(options) {
187
263
  if(!options) throw new Error("options are required by winston-middleware middleware");
188
- if(!options.transports || !(options.transports.length > 0)) throw new Error("transports are required by winston-middleware middleware");
189
- };
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
+ }
190
273
 
191
274
  module.exports.errorLogger = errorLogger;
192
275
  module.exports.logger = logger;
193
276
  module.exports.requestWhitelist = requestWhitelist;
194
277
  module.exports.bodyWhitelist = bodyWhitelist;
278
+ module.exports.bodyBlacklist = bodyBlacklist;
195
279
  module.exports.responseWhitelist = responseWhitelist;
196
280
  module.exports.defaultRequestFilter = defaultRequestFilter;
197
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.3",
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
  }