Commit 77a29a30 authored by Leo Iannacone's avatar Leo Iannacone

update internal libraries

parent 0a65407c
...@@ -29,5 +29,5 @@ ...@@ -29,5 +29,5 @@
"url": "https://github.com/visionmedia/ejs/issues" "url": "https://github.com/visionmedia/ejs/issues"
}, },
"_id": "ejs@1.0.0", "_id": "ejs@1.0.0",
"_from": "ejs@>= 0.0.1" "_from": "ejs@1.*"
} }
...@@ -35,5 +35,5 @@ ...@@ -35,5 +35,5 @@
"url": "https://github.com/expressjs/accepts/issues" "url": "https://github.com/expressjs/accepts/issues"
}, },
"_id": "accepts@1.0.7", "_id": "accepts@1.0.7",
"_from": "accepts@~1.0.4" "_from": "accepts@~1.0.5"
} }
4.6.1 / 2014-07-12
==================
* fix `subapp.mountpath` regression for `app.use(subapp)`
4.6.0 / 2014-07-11
==================
* accept multiple callbacks to `app.use()`
* add explicit "Rosetta Flash JSONP abuse" protection
- previous versions are not vulnerable; this is just explicit protection
* catch errors in multiple `req.param(name, fn)` handlers
* deprecate `res.redirect(url, status)` -- use `res.redirect(status, url)` instead
* fix `res.send(status, num)` to send `num` as json (not error)
* remove unnecessary escaping when `res.jsonp` returns JSON response
* support non-string `path` in `app.use(path, fn)`
- supports array of paths
- supports `RegExp`
* router: fix optimization on router exit
* router: refactor location of `try` blocks
* router: speed up standard `app.use(fn)`
* deps: debug@1.0.3
- Add support for multiple wildcards in namespaces
* deps: finalhandler@0.0.3
- deps: debug@1.0.3
* deps: methods@1.1.0
- add `CONNECT`
* deps: parseurl@~1.1.3
- faster parsing of href-only URLs
* deps: path-to-regexp@0.1.3
* deps: send@0.6.0
- deps: debug@1.0.3
* deps: serve-static@~1.3.2
- deps: parseurl@~1.1.3
- deps: send@0.6.0
* perf: fix arguments reassign deopt in some `res` methods
4.5.1 / 2014-07-06 4.5.1 / 2014-07-06
================== ==================
...@@ -237,6 +274,30 @@ ...@@ -237,6 +274,30 @@
- `app.route()` - Proxy to the app's `Router#route()` method to create a new route - `app.route()` - Proxy to the app's `Router#route()` method to create a new route
- Router & Route - public API - Router & Route - public API
3.14.0 / 2014-07-11
===================
* add explicit "Rosetta Flash JSONP abuse" protection
- previous versions are not vulnerable; this is just explicit protection
* deprecate `res.redirect(url, status)` -- use `res.redirect(status, url)` instead
* fix `res.send(status, num)` to send `num` as json (not error)
* remove unnecessary escaping when `res.jsonp` returns JSON response
* deps: basic-auth@1.0.0
- support empty password
- support empty username
* deps: connect@2.23.0
- deps: debug@1.0.3
- deps: express-session@~1.6.4
- deps: method-override@~2.1.0
- deps: parseurl@~1.1.3
- deps: serve-static@~1.3.1
* deps: debug@1.0.3
- Add support for multiple wildcards in namespaces
* deps: methods@1.1.0
- add `CONNECT`
* deps: parseurl@~1.1.3
- faster parsing of href-only URLs
3.13.0 / 2014-07-03 3.13.0 / 2014-07-03
=================== ===================
......
[![Express Logo](https://i.cloudup.com/zfY6lL7eFa-3000x3000.png)](https://expressjs.com/) [![Express Logo](https://i.cloudup.com/zfY6lL7eFa-3000x3000.png)](https://expressjs.com/)
Fast, unopinionated, minimalist web framework for [node](https://nodejs.org). Fast, unopinionated, minimalist web framework for [node](http://nodejs.org).
[![NPM Version](https://badge.fury.io/js/express.svg)](https://badge.fury.io/js/express) [![NPM Version](https://badge.fury.io/js/express.svg)](https://badge.fury.io/js/express)
[![Build Status](https://travis-ci.org/visionmedia/express.svg?branch=master)](https://travis-ci.org/visionmedia/express) [![Build Status](https://travis-ci.org/visionmedia/express.svg?branch=master)](https://travis-ci.org/visionmedia/express)
......
...@@ -147,38 +147,49 @@ app.handle = function(req, res, done) { ...@@ -147,38 +147,49 @@ app.handle = function(req, res, done) {
* @api public * @api public
*/ */
app.use = function(route, fn){ app.use = function use(path, fn) {
var mount_app; var mount_app;
var mount_path;
// check for .use(path, app) or .use(app) signature
if (arguments.length <= 2) {
mount_path = typeof path === 'string'
? path
: '/';
mount_app = typeof path === 'function'
? path
: fn;
}
// default route to '/' // setup router
if ('string' != typeof route) fn = route, route = '/'; this.lazyrouter();
var router = this._router;
// express app // express app
if (fn.handle && fn.set) mount_app = fn; if (mount_app && mount_app.handle && mount_app.set) {
debug('.use app under %s', mount_path);
mount_app.mountpath = mount_path;
mount_app.parent = this;
// restore .app property on req and res // restore .app property on req and res
if (mount_app) { router.use(mount_path, function mounted_app(req, res, next) {
debug('.use app under %s', route);
mount_app.mountpath = route;
fn = function(req, res, next) {
var orig = req.app; var orig = req.app;
mount_app.handle(req, res, function(err) { mount_app.handle(req, res, function(err) {
req.__proto__ = orig.request; req.__proto__ = orig.request;
res.__proto__ = orig.response; res.__proto__ = orig.response;
next(err); next(err);
}); });
}; });
}
this.lazyrouter();
this._router.use(route, fn);
// mounted an app // mounted an app
if (mount_app) {
mount_app.parent = this;
mount_app.emit('mount', this); mount_app.emit('mount', this);
return this;
} }
// pass-through use
router.use.apply(router, arguments);
return this; return this;
}; };
......
...@@ -78,67 +78,77 @@ res.links = function(links){ ...@@ -78,67 +78,77 @@ res.links = function(links){
* @api public * @api public
*/ */
res.send = function(body){ res.send = function send(body) {
var req = this.req; var chunk = body;
var head = 'HEAD' == req.method;
var type;
var encoding; var encoding;
var len; var len;
var req = this.req;
var type;
// settings // settings
var app = this.app; var app = this.app;
// allow status / body // allow status / body
if (2 == arguments.length) { if (arguments.length === 2) {
// res.send(body, status) backwards compat // res.send(body, status) backwards compat
if ('number' != typeof body && 'number' == typeof arguments[1]) { if (typeof arguments[0] !== 'number' && typeof arguments[1] === 'number') {
deprecate('res.send(body, status): Use res.send(status, body) instead'); deprecate('res.send(body, status): Use res.send(status, body) instead');
this.statusCode = arguments[1]; this.statusCode = arguments[1];
} else { } else {
this.statusCode = body; this.statusCode = arguments[0];
body = arguments[1]; chunk = arguments[1];
} }
} }
switch (typeof body) { // disambiguate res.send(status) and res.send(status, num)
// response status if (typeof chunk === 'number' && arguments.length === 1) {
case 'number': // res.send(status) will set status message as text string
this.get('Content-Type') || this.type('txt'); if (!this.get('Content-Type')) {
this.statusCode = body; this.type('txt');
body = http.STATUS_CODES[body]; }
break;
this.statusCode = chunk;
chunk = http.STATUS_CODES[chunk];
}
switch (typeof chunk) {
// string defaulting to html // string defaulting to html
case 'string': case 'string':
if (!this.get('Content-Type')) this.type('html'); if (!this.get('Content-Type')) {
this.type('html');
}
break; break;
case 'boolean': case 'boolean':
case 'number':
case 'object': case 'object':
if (null == body) { if (chunk === null) {
body = ''; chunk = '';
} else if (Buffer.isBuffer(body)) { } else if (Buffer.isBuffer(chunk)) {
this.get('Content-Type') || this.type('bin'); if (!this.get('Content-Type')) {
this.type('bin');
}
} else { } else {
return this.json(body); return this.json(chunk);
} }
break; break;
} }
// write strings in utf-8 // write strings in utf-8
if ('string' === typeof body) { if (typeof chunk === 'string') {
encoding = 'utf8'; encoding = 'utf8';
type = this.get('Content-Type'); type = this.get('Content-Type');
// reflect this in content-type // reflect this in content-type
if ('string' === typeof type) { if (typeof type === 'string') {
this.set('Content-Type', setCharset(type, 'utf-8')); this.set('Content-Type', setCharset(type, 'utf-8'));
} }
} }
// populate Content-Length // populate Content-Length
if (undefined !== body && !this.get('Content-Length')) { if (chunk !== undefined && !this.get('Content-Length')) {
len = Buffer.isBuffer(body) len = Buffer.isBuffer(chunk)
? body.length ? chunk.length
: Buffer.byteLength(body, encoding); : Buffer.byteLength(chunk, encoding);
this.set('Content-Length', len); this.set('Content-Length', len);
} }
...@@ -146,7 +156,7 @@ res.send = function(body){ ...@@ -146,7 +156,7 @@ res.send = function(body){
var etag = len !== undefined && app.get('etag fn'); var etag = len !== undefined && app.get('etag fn');
if (etag && ('GET' === req.method || 'HEAD' === req.method)) { if (etag && ('GET' === req.method || 'HEAD' === req.method)) {
if (!this.get('ETag')) { if (!this.get('ETag')) {
etag = etag(body, encoding); etag = etag(chunk, encoding);
etag && this.set('ETag', etag); etag && this.set('ETag', etag);
} }
} }
...@@ -159,11 +169,16 @@ res.send = function(body){ ...@@ -159,11 +169,16 @@ res.send = function(body){
this.removeHeader('Content-Type'); this.removeHeader('Content-Type');
this.removeHeader('Content-Length'); this.removeHeader('Content-Length');
this.removeHeader('Transfer-Encoding'); this.removeHeader('Transfer-Encoding');
body = ''; chunk = '';
}
// skip body for HEAD
if (req.method === 'HEAD') {
this.end();
} }
// respond // respond
this.end((head ? null : body), encoding); this.end(chunk, encoding);
return this; return this;
}; };
...@@ -181,20 +196,22 @@ res.send = function(body){ ...@@ -181,20 +196,22 @@ res.send = function(body){
* @api public * @api public
*/ */
res.json = function(obj){ res.json = function json(obj) {
var val = obj;
// allow status / body // allow status / body
if (2 == arguments.length) { if (arguments.length === 2) {
// res.json(body, status) backwards compat // res.json(body, status) backwards compat
if ('number' == typeof arguments[1]) { if (typeof arguments[1] === 'number') {
this.statusCode = arguments[1]; this.statusCode = arguments[1];
if (typeof obj === 'number') { if (typeof arguments[0] === 'number') {
deprecate('res.json(obj, status): Use res.json(status, obj) instead'); deprecate('res.json(obj, status): Use res.json(status, obj) instead');
} else { } else {
deprecate('res.json(num, status): Use res.status(status).json(num) instead'); deprecate('res.json(num, status): Use res.status(status).json(num) instead');
} }
} else { } else {
this.statusCode = obj; this.statusCode = arguments[0];
obj = arguments[1]; val = arguments[1];
} }
} }
...@@ -202,10 +219,12 @@ res.json = function(obj){ ...@@ -202,10 +219,12 @@ res.json = function(obj){
var app = this.app; var app = this.app;
var replacer = app.get('json replacer'); var replacer = app.get('json replacer');
var spaces = app.get('json spaces'); var spaces = app.get('json spaces');
var body = JSON.stringify(obj, replacer, spaces); var body = JSON.stringify(val, replacer, spaces);
// content-type // content-type
this.get('Content-Type') || this.set('Content-Type', 'application/json'); if (!this.get('Content-Type')) {
this.set('Content-Type', 'application/json');
}
return this.send(body); return this.send(body);
}; };
...@@ -223,20 +242,22 @@ res.json = function(obj){ ...@@ -223,20 +242,22 @@ res.json = function(obj){
* @api public * @api public
*/ */
res.jsonp = function(obj){ res.jsonp = function jsonp(obj) {
var val = obj;
// allow status / body // allow status / body
if (2 == arguments.length) { if (arguments.length === 2) {
// res.json(body, status) backwards compat // res.json(body, status) backwards compat
if ('number' == typeof arguments[1]) { if (typeof arguments[1] === 'number') {
this.statusCode = arguments[1]; this.statusCode = arguments[1];
if (typeof obj === 'number') { if (typeof arguments[0] === 'number') {
deprecate('res.jsonp(obj, status): Use res.jsonp(status, obj) instead'); deprecate('res.jsonp(obj, status): Use res.jsonp(status, obj) instead');
} else { } else {
deprecate('res.jsonp(num, status): Use res.status(status).jsonp(num) instead'); deprecate('res.jsonp(num, status): Use res.status(status).jsonp(num) instead');
} }
} else { } else {
this.statusCode = obj; this.statusCode = arguments[0];
obj = arguments[1]; val = arguments[1];
} }
} }
...@@ -244,13 +265,14 @@ res.jsonp = function(obj){ ...@@ -244,13 +265,14 @@ res.jsonp = function(obj){
var app = this.app; var app = this.app;
var replacer = app.get('json replacer'); var replacer = app.get('json replacer');
var spaces = app.get('json spaces'); var spaces = app.get('json spaces');
var body = JSON.stringify(obj, replacer, spaces) var body = JSON.stringify(val, replacer, spaces);
.replace(/\u2028/g, '\\u2028')
.replace(/\u2029/g, '\\u2029');
var callback = this.req.query[app.get('jsonp callback name')]; var callback = this.req.query[app.get('jsonp callback name')];
// content-type // content-type
this.get('Content-Type') || this.set('Content-Type', 'application/json'); if (!this.get('Content-Type')) {
this.set('X-Content-Type-Options', 'nosniff');
this.set('Content-Type', 'application/json');
}
// fixup callback // fixup callback
if (Array.isArray(callback)) { if (Array.isArray(callback)) {
...@@ -258,10 +280,22 @@ res.jsonp = function(obj){ ...@@ -258,10 +280,22 @@ res.jsonp = function(obj){
} }
// jsonp // jsonp
if (callback && 'string' === typeof callback) { if (typeof callback === 'string' && callback.length !== 0) {
this.charset = 'utf-8';
this.set('X-Content-Type-Options', 'nosniff');
this.set('Content-Type', 'text/javascript'); this.set('Content-Type', 'text/javascript');
var cb = callback.replace(/[^\[\]\w$.]/g, '');
body = 'typeof ' + cb + ' === \'function\' && ' + cb + '(' + body + ');'; // restrict callback charset
callback = callback.replace(/[^\[\]\w$.]/g, '');
// replace chars not allowed in JavaScript that are in JSON
body = body
.replace(/\u2028/g, '\\u2028')
.replace(/\u2029/g, '\\u2029');
// the /**/ is a specific security mitigation for "Rosetta Flash JSONP abuse"
// the typeof check is just to reduce client error noise
body = '/**/ typeof ' + callback + ' === \'function\' && ' + callback + '(' + body + ');';
} }
return this.send(body); return this.send(body);
...@@ -550,8 +584,8 @@ res.attachment = function(filename){ ...@@ -550,8 +584,8 @@ res.attachment = function(filename){
*/ */
res.set = res.set =
res.header = function(field, val){ res.header = function header(field, val) {
if (2 == arguments.length) { if (arguments.length === 2) {
if (Array.isArray(val)) val = val.map(String); if (Array.isArray(val)) val = val.map(String);
else val = String(val); else val = String(val);
if ('content-type' == field.toLowerCase() && !/;\s*charset\s*=/.test(val)) { if ('content-type' == field.toLowerCase() && !/;\s*charset\s*=/.test(val)) {
...@@ -687,32 +721,30 @@ res.location = function(url){ ...@@ -687,32 +721,30 @@ res.location = function(url){
* res.redirect('/foo/bar'); * res.redirect('/foo/bar');
* res.redirect('http://example.com'); * res.redirect('http://example.com');
* res.redirect(301, 'http://example.com'); * res.redirect(301, 'http://example.com');
* res.redirect('http://example.com', 301);
* res.redirect('../login'); // /blog/post/1 -> /blog/login * res.redirect('../login'); // /blog/post/1 -> /blog/login
* *
* @param {String} url
* @param {Number} code
* @api public * @api public
*/ */
res.redirect = function(url){ res.redirect = function redirect(url) {
var head = 'HEAD' == this.req.method; var address = url;
var status = 302;
var body; var body;
var status = 302;
// allow status / url // allow status / url
if (2 == arguments.length) { if (arguments.length === 2) {
if ('number' == typeof url) { if (typeof arguments[0] === 'number') {
status = url; status = arguments[0];
url = arguments[1]; address = arguments[1];
} else { } else {
deprecate('res.redirect(ur, status): Use res.redirect(status, url) instead');
status = arguments[1]; status = arguments[1];
} }
} }
// Set location header // Set location header
this.location(url); this.location(address);
url = this.get('Location'); address = this.get('Location');
// Support text/{plain,html} by default // Support text/{plain,html} by default
this.format({ this.format({
...@@ -733,7 +765,12 @@ res.redirect = function(url){ ...@@ -733,7 +765,12 @@ res.redirect = function(url){
// Respond // Respond
this.statusCode = status; this.statusCode = status;
this.set('Content-Length', Buffer.byteLength(body)); this.set('Content-Length', Buffer.byteLength(body));
this.end(head ? null : body);
if (this.req.method === 'HEAD') {
this.end();
}
this.end(body);
}; };
/** /**
......
...@@ -9,6 +9,8 @@ var mixin = require('utils-merge'); ...@@ -9,6 +9,8 @@ var mixin = require('utils-merge');
var debug = require('debug')('express:router'); var debug = require('debug')('express:router');
var parseUrl = require('parseurl'); var parseUrl = require('parseurl');
var slice = Array.prototype.slice; var slice = Array.prototype.slice;
var toString = Object.prototype.toString;
var utils = require('../utils');
/** /**
* Initialize a new `Router` with the given `options`. * Initialize a new `Router` with the given `options`.
...@@ -149,35 +151,38 @@ proto.handle = function(req, res, done) { ...@@ -149,35 +151,38 @@ proto.handle = function(req, res, done) {
}); });
} }
// setup basic req values
req.baseUrl = parentUrl;
req.originalUrl = req.originalUrl || req.url;
next(); next();
function next(err) { function next(err) {
if (err === 'route') { var layerError = err === 'route'
err = undefined; ? null
} : err;
var layer = stack[idx++]; var layer = stack[idx++];
var layerPath;
if (!layer) {
return done(err);
}
if (slashAdded) { if (slashAdded) {
req.url = req.url.substr(1); req.url = req.url.substr(1);
slashAdded = false; slashAdded = false;
} }
if (removed.length !== 0) {
req.baseUrl = parentUrl; req.baseUrl = parentUrl;
req.url = protohost + removed + req.url.substr(protohost.length); req.url = protohost + removed + req.url.substr(protohost.length);
req.originalUrl = req.originalUrl || req.url;
removed = ''; removed = '';
}
try { if (!layer) {
var path = parseUrl(req).pathname; return done(layerError);
if (undefined == path) path = '/'; }
if (!layer.match(path)) return next(err); self.match_layer(layer, req, res, function (err, path) {
if (err || path === undefined) {
return next(layerError || err);
}
// route object and not middleware // route object and not middleware
var route = layer.route; var route = layer.route;
...@@ -185,8 +190,8 @@ proto.handle = function(req, res, done) { ...@@ -185,8 +190,8 @@ proto.handle = function(req, res, done) {
// if final route, then we support options // if final route, then we support options
if (route) { if (route) {
// we don't run any routes with error first // we don't run any routes with error first
if (err) { if (layerError) {
return next(err); return next(layerError);
} }
var method = req.method; var method = req.method;
...@@ -210,36 +215,33 @@ proto.handle = function(req, res, done) { ...@@ -210,36 +215,33 @@ proto.handle = function(req, res, done) {
req.params = self.mergeParams req.params = self.mergeParams
? mergeParams(layer.params, parentParams) ? mergeParams(layer.params, parentParams)
: layer.params; : layer.params;
layerPath = layer.path; var layerPath = layer.path;
// this should be done for the layer // this should be done for the layer
return self.process_params(layer, paramcalled, req, res, function(err) { self.process_params(layer, paramcalled, req, res, function (err) {
if (err) { if (err) {
return next(err); return next(layerError || err);
} }
if (route) { if (route) {
return layer.handle(req, res, next); return layer.handle_request(req, res, next);
} }
trim_prefix(); trim_prefix(layer, layerError, layerPath, path);
});
}); });
} catch (err) {
next(err);
} }
function trim_prefix() { function trim_prefix(layer, layerError, layerPath, path) {
var c = path[layerPath.length]; var c = path[layerPath.length];
if (c && '/' != c && '.' != c) return next(err); if (c && '/' !== c && '.' !== c) return next(layerError);
// Trim off the part of the url that matches the route // Trim off the part of the url that matches the route
// middleware (.use stuff) needs to have the path stripped // middleware (.use stuff) needs to have the path stripped
removed = layerPath; if (layerPath.length !== 0) {
if (removed.length) {
debug('trim prefix (%s) from url %s', layerPath, req.url); debug('trim prefix (%s) from url %s', layerPath, req.url);
removed = layerPath;
req.url = protohost + req.url.substr(protohost.length + removed.length); req.url = protohost + req.url.substr(protohost.length + removed.length);
}
// Ensure leading slash // Ensure leading slash
if (!fqdn && req.url[0] !== '/') { if (!fqdn && req.url[0] !== '/') {
...@@ -248,27 +250,42 @@ proto.handle = function(req, res, done) { ...@@ -248,27 +250,42 @@ proto.handle = function(req, res, done) {
} }
// Setup base URL (no trailing slash) // Setup base URL (no trailing slash)
if (removed.length && removed.substr(-1) === '/') { req.baseUrl = parentUrl + (removed[removed.length - 1] === '/'
req.baseUrl = parentUrl + removed.substring(0, removed.length - 1); ? removed.substring(0, removed.length - 1)
} else { : removed);
req.baseUrl = parentUrl + removed;
} }
debug('%s %s : %s', layer.handle.name || 'anonymous', layerPath, req.originalUrl); debug('%s %s : %s', layer.name, layerPath, req.originalUrl);
var arity = layer.handle.length;
try { if (layerError) {
if (err && arity === 4) { layer.handle_error(layerError, req, res, next);
layer.handle(err, req, res, next);
} else if (!err && arity < 4) {
layer.handle(req, res, next);
} else { } else {
next(err); layer.handle_request(req, res, next);
} }
} catch (err) {
next(err);
} }
};
/**
* Match request to a layer.
*
* @api private
*/
proto.match_layer = function match_layer(layer, req, res, done) {
var error = null;
var path;
try {
path = parseUrl(req).pathname;
if (!layer.match(path)) {
path = undefined;
} }
} catch (err) {
error = err;
} }
done(error, path);
}; };
/** /**
...@@ -338,11 +355,7 @@ proto.process_params = function(layer, called, req, res, done) { ...@@ -338,11 +355,7 @@ proto.process_params = function(layer, called, req, res, done) {
value: paramVal value: paramVal
}; };
try { paramCallback();
return paramCallback();
} catch (err) {
return done(err);
}
} }
// single param callbacks // single param callbacks
...@@ -361,7 +374,11 @@ proto.process_params = function(layer, called, req, res, done) { ...@@ -361,7 +374,11 @@ proto.process_params = function(layer, called, req, res, done) {
if (!fn) return param(); if (!fn) return param();
try {
fn(req, res, paramCallback, paramVal, key.name); fn(req, res, paramCallback, paramVal, key.name);
} catch (e) {
paramCallback(e);
}
} }
param(); param();
...@@ -379,40 +396,47 @@ proto.process_params = function(layer, called, req, res, done) { ...@@ -379,40 +396,47 @@ proto.process_params = function(layer, called, req, res, done) {
* handlers can operate without any code changes regardless of the "prefix" * handlers can operate without any code changes regardless of the "prefix"
* pathname. * pathname.
* *
* @param {String|Function} route
* @param {Function} fn
* @return {app} for chaining
* @api public * @api public
*/ */
proto.use = function(route, fn){ proto.use = function use(fn) {
// default route to '/' var offset = 0;
if ('string' != typeof route) { var path = '/';
fn = route; var self = this;
route = '/';
}
// default path to '/'
if (typeof fn !== 'function') { if (typeof fn !== 'function') {
var type = {}.toString.call(fn); offset = 1;
var msg = 'Router.use() requires callback functions but got a ' + type; path = fn;
throw new Error(msg);
} }
// strip trailing slash var callbacks = utils.flatten(slice.call(arguments, offset));
if ('/' == route[route.length - 1]) {
route = route.slice(0, -1); if (callbacks.length === 0) {
throw new TypeError('Router.use() requires callback function');
} }
var layer = new Layer(route, { callbacks.forEach(function (fn) {
sensitive: this.caseSensitive, if (typeof fn !== 'function') {
strict: this.strict, var type = toString.call(fn);
var msg = 'Router.use() requires callback function but got a ' + type;
throw new TypeError(msg);
}
// add the middleware
debug('use %s %s', path, fn.name || '<anonymous>');
var layer = new Layer(path, {
sensitive: self.caseSensitive,
strict: false,
end: false end: false
}, fn); }, fn);
// add the middleware layer.route = undefined;
debug('use %s %s', route || '/', fn.name || 'anonymous');
self.stack.push(layer);
});
this.stack.push(layer);
return this; return this;
}; };
...@@ -448,7 +472,7 @@ proto.route = function(path){ ...@@ -448,7 +472,7 @@ proto.route = function(path){
methods.concat('all').forEach(function(method){ methods.concat('all').forEach(function(method){
proto[method] = function(path){ proto[method] = function(path){
var route = this.route(path) var route = this.route(path)
route[method].apply(route, [].slice.call(arguments, 1)); route[method].apply(route, slice.call(arguments, 1));
return this; return this;
}; };
}); });
...@@ -511,8 +535,14 @@ function restore(fn, obj) { ...@@ -511,8 +535,14 @@ function restore(fn, obj) {
// wrap a function // wrap a function
function wrap(old, fn) { function wrap(old, fn) {
return function () { return function proxy() {
var args = [old].concat(slice.call(arguments)); var args = new Array(arguments.length + 1);
args[0] = old;
for (var i = 0, len = arguments.length; i < len; i++) {
args[i + 1] = arguments[i];
}
fn.apply(this, args); fn.apply(this, args);
}; };
} }
...@@ -18,10 +18,67 @@ function Layer(path, options, fn) { ...@@ -18,10 +18,67 @@ function Layer(path, options, fn) {
debug('new %s', path); debug('new %s', path);
options = options || {}; options = options || {};
this.regexp = pathRegexp(path, this.keys = [], options);
this.handle = fn; this.handle = fn;
this.name = fn.name || '<anonymous>';
this.params = undefined;
this.path = undefined;
this.regexp = pathRegexp(path, this.keys = [], options);
if (path === '/' && options.end === false) {
this.regexp.fast_slash = true;
}
} }
/**
* Handle the error for the layer.
*
* @param {Error} error
* @param {Request} req
* @param {Response} res
* @param {function} next
* @api private
*/
Layer.prototype.handle_error = function handle_error(error, req, res, next) {
var fn = this.handle;
if (fn.length !== 4) {
// not a standard error handler
return next(error);
}
try {
fn(error, req, res, next);
} catch (err) {
next(err);
}
};
/**
* Handle the request for the layer.
*
* @param {Request} req
* @param {Response} res
* @param {function} next
* @api private
*/
Layer.prototype.handle_request = function handle(req, res, next) {
var fn = this.handle;
if (fn.length > 3) {
// not a standard request handler
return next();
}
try {
fn(req, res, next);
} catch (err) {
next(err);
}
};
/** /**
* Check if this route matches `path`, if so * Check if this route matches `path`, if so
* populate `.params`. * populate `.params`.
...@@ -31,18 +88,32 @@ function Layer(path, options, fn) { ...@@ -31,18 +88,32 @@ function Layer(path, options, fn) {
* @api private * @api private
*/ */
Layer.prototype.match = function(path){ Layer.prototype.match = function match(path) {
var keys = this.keys; if (this.regexp.fast_slash) {
var params = this.params = {}; // fast path non-ending match for / (everything matches)
this.params = {};
this.path = '';
return true;
}
var m = this.regexp.exec(path); var m = this.regexp.exec(path);
var n = 0;
var key;
var val;
if (!m) return false; if (!m) {
this.params = undefined;
this.path = undefined;
return false;
}
// store values
this.params = {};
this.path = m[0]; this.path = m[0];
var keys = this.keys;
var params = this.params;
var n = 0;
var key;
var val;
for (var i = 1, len = m.length; i < len; ++i) { for (var i = 1, len = m.length; i < len; ++i) {
key = keys[i - 1]; key = keys[i - 1];
val = decode_param(m[i]); val = decode_param(m[i]);
......
/**
* Module dependencies.
*/
var pathRegexp = require('path-to-regexp');
/**
* Expose `Layer`.
*/
module.exports = Match;
function Match(layer, path, params) {
this.layer = layer;
this.params = {};
this.path = path || '';
if (!params) {
return this;
}
var keys = layer.keys;
var n = 0;
var prop;
var key;
var val;
for (var i = 0; i < params.length; i++) {
key = keys[i];
val = decode_param(params[i]);
prop = key
? key.name
: n++;
this.params[prop] = val;
}
return this;
};
/**
* Decode param value.
*
* @param {string} val
* @return {string}
* @api private
*/
function decode_param(val){
if (typeof val !== 'string') {
return val;
}
try {
return decodeURIComponent(val);
} catch (e) {
var err = new TypeError("Failed to decode param '" + val + "'");
err.status = 400;
throw err;
}
}
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
*/ */
var debug = require('debug')('express:router:route'); var debug = require('debug')('express:router:route');
var Layer = require('./layer');
var methods = require('methods'); var methods = require('methods');
var utils = require('../utils'); var utils = require('../utils');
...@@ -64,6 +65,7 @@ Route.prototype._options = function(){ ...@@ -64,6 +65,7 @@ Route.prototype._options = function(){
*/ */
Route.prototype.dispatch = function(req, res, done){ Route.prototype.dispatch = function(req, res, done){
var idx = 0;
var stack = this.stack; var stack = this.stack;
if (stack.length === 0) { if (stack.length === 0) {
return done(); return done();
...@@ -76,8 +78,9 @@ Route.prototype.dispatch = function(req, res, done){ ...@@ -76,8 +78,9 @@ Route.prototype.dispatch = function(req, res, done){
req.route = this; req.route = this;
var idx = 0; next();
(function next_layer(err) {
function next(err) {
if (err && err === 'route') { if (err && err === 'route') {
return done(); return done();
} }
...@@ -88,33 +91,15 @@ Route.prototype.dispatch = function(req, res, done){ ...@@ -88,33 +91,15 @@ Route.prototype.dispatch = function(req, res, done){
} }
if (layer.method && layer.method !== method) { if (layer.method && layer.method !== method) {
return next_layer(err); return next(err);
} }
var arity = layer.handle.length;
if (err) { if (err) {
if (arity < 4) { layer.handle_error(err, req, res, next);
return next_layer(err); } else {
layer.handle_request(req, res, next);
} }
try {
layer.handle(err, req, res, next_layer);
} catch (err) {
next_layer(err);
}
return;
}
if (arity > 3) {
return next_layer();
} }
try {
layer.handle(req, res, next_layer);
} catch (err) {
next_layer(err);
}
})();
}; };
/** /**
...@@ -155,8 +140,11 @@ Route.prototype.all = function(){ ...@@ -155,8 +140,11 @@ Route.prototype.all = function(){
throw new Error(msg); throw new Error(msg);
} }
var layer = Layer('/', {}, fn);
layer.method = undefined;
self.methods._all = true; self.methods._all = true;
self.stack.push({ handle: fn }); self.stack.push(layer);
}); });
return self; return self;
...@@ -176,11 +164,11 @@ methods.forEach(function(method){ ...@@ -176,11 +164,11 @@ methods.forEach(function(method){
debug('%s %s', method, self.path); debug('%s %s', method, self.path);
if (!self.methods[method]) { var layer = Layer('/', {}, fn);
self.methods[method] = true; layer.method = method;
}
self.stack.push({ method: method, handle: fn }); self.methods[method] = true;
self.stack.push(layer);
}); });
return self; return self;
}; };
......
...@@ -35,5 +35,5 @@ ...@@ -35,5 +35,5 @@
"url": "https://github.com/expressjs/accepts/issues" "url": "https://github.com/expressjs/accepts/issues"
}, },
"_id": "accepts@1.0.7", "_id": "accepts@1.0.7",
"_from": "accepts@~1.0.7" "_from": "accepts@~1.0.5"
} }
1.0.3 / 2014-07-09
==================
* Add support for multiple wildcards in namespaces (#122, @seegno)
* browser: fix lint
1.0.2 / 2014-06-10 1.0.2 / 2014-06-10
================== ==================
......
...@@ -66,7 +66,7 @@ function formatArgs() { ...@@ -66,7 +66,7 @@ function formatArgs() {
+ (useColors ? '%c ' : ' ') + (useColors ? '%c ' : ' ')
+ '+' + exports.humanize(this.diff); + '+' + exports.humanize(this.diff);
if (!useColors) return args if (!useColors) return args;
var c = 'color: ' + this.color; var c = 'color: ' + this.color;
args = [args[0], c, ''].concat(Array.prototype.slice.call(args, 1)); args = [args[0], c, ''].concat(Array.prototype.slice.call(args, 1));
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
"name": "debug", "name": "debug",
"repo": "visionmedia/debug", "repo": "visionmedia/debug",
"description": "small debugging utility", "description": "small debugging utility",
"version": "1.0.2", "version": "1.0.3",
"keywords": [ "keywords": [
"debug", "debug",
"log", "log",
......
...@@ -141,7 +141,7 @@ function enable(namespaces) { ...@@ -141,7 +141,7 @@ function enable(namespaces) {
for (var i = 0; i < len; i++) { for (var i = 0; i < len; i++) {
if (!split[i]) continue; // ignore empty strings if (!split[i]) continue; // ignore empty strings
namespaces = split[i].replace('*', '.*?'); namespaces = split[i].replace(/\*/g, '.*?');
if (namespaces[0] === '-') { if (namespaces[0] === '-') {
exports.skips.push(new RegExp('^' + namespaces.substr(1) + '$')); exports.skips.push(new RegExp('^' + namespaces.substr(1) + '$'));
} else { } else {
......
{ {
"name": "debug", "name": "debug",
"version": "1.0.2", "version": "1.0.3",
"repository": { "repository": {
"type": "git", "type": "git",
"url": "git://github.com/visionmedia/debug.git" "url": "git://github.com/visionmedia/debug.git"
...@@ -42,6 +42,6 @@ ...@@ -42,6 +42,6 @@
"bugs": { "bugs": {
"url": "https://github.com/visionmedia/debug/issues" "url": "https://github.com/visionmedia/debug/issues"
}, },
"_id": "debug@1.0.2", "_id": "debug@1.0.3",
"_from": "debug@1.0.2" "_from": "debug@1.0.3"
} }
0.0.3 / 2014-07-11
==================
* deps: debug@1.0.3
- Add support for multiple wildcards in namespaces
0.0.2 / 2014-06-19 0.0.2 / 2014-06-19
================== ==================
......
{ {
"name": "finalhandler", "name": "finalhandler",
"description": "Node.js final http responder", "description": "Node.js final http responder",
"version": "0.0.2", "version": "0.0.3",
"author": { "author": {
"name": "Douglas Christopher Wilson", "name": "Douglas Christopher Wilson",
"email": "doug@somethingdoug.com" "email": "doug@somethingdoug.com"
...@@ -12,11 +12,11 @@ ...@@ -12,11 +12,11 @@
"url": "git://github.com/expressjs/finalhandler" "url": "git://github.com/expressjs/finalhandler"
}, },
"dependencies": { "dependencies": {
"debug": "1.0.2", "debug": "1.0.3",
"escape-html": "1.0.1" "escape-html": "1.0.1"
}, },
"devDependencies": { "devDependencies": {
"istanbul": "0.2.10", "istanbul": "0.3.0",
"mocha": "~1.20.1", "mocha": "~1.20.1",
"should": "~4.0.1", "should": "~4.0.1",
"supertest": "~0.13.0" "supertest": "~0.13.0"
...@@ -34,6 +34,6 @@ ...@@ -34,6 +34,6 @@
"bugs": { "bugs": {
"url": "https://github.com/expressjs/finalhandler/issues" "url": "https://github.com/expressjs/finalhandler/issues"
}, },
"_id": "finalhandler@0.0.2", "_id": "finalhandler@0.0.3",
"_from": "finalhandler@0.0.2" "_from": "finalhandler@0.0.3"
} }
...@@ -29,5 +29,6 @@ ...@@ -29,5 +29,6 @@
"url": "https://github.com/visionmedia/node-fresh/issues" "url": "https://github.com/visionmedia/node-fresh/issues"
}, },
"_id": "fresh@0.2.2", "_id": "fresh@0.2.2",
"_from": "fresh@0.2.2" "_from": "fresh@0.2.2",
"scripts": {}
} }
1.1.0 / 2014-07-05
==================
* add CONNECT
1.0.1 / 2014-06-02 1.0.1 / 2014-06-02
================== ==================
......
...@@ -34,7 +34,8 @@ if (http.METHODS) { ...@@ -34,7 +34,8 @@ if (http.METHODS) {
'subscribe', 'subscribe',
'unsubscribe', 'unsubscribe',
'patch', 'patch',
'search' 'search',
'connect'
]; ];
} }
{ {
"name": "methods", "name": "methods",
"version": "1.0.1", "version": "1.1.0",
"description": "HTTP methods that node supports", "description": "HTTP methods that node supports",
"main": "index.js", "main": "index.js",
"scripts": { "scripts": {
...@@ -26,6 +26,6 @@ ...@@ -26,6 +26,6 @@
"bugs": { "bugs": {
"url": "https://github.com/visionmedia/node-methods/issues" "url": "https://github.com/visionmedia/node-methods/issues"
}, },
"_id": "methods@1.0.1", "_id": "methods@1.1.0",
"_from": "methods@1.0.1" "_from": "methods@1.1.0"
} }
1.1.3 / 2014-07-08
==================
* Fix typo
1.1.2 / 2014-07-08
==================
* Seriously fix Node.js 0.8 compatibility
1.1.1 / 2014-07-08
==================
* Fix Node.js 0.8 compatibility
1.1.0 / 2014-07-08
==================
* Incorporate URL href-only parse fast-path
1.0.1 / 2014-03-08
==================
* Add missing `require`
1.0.0 / 2014-03-08
==================
* Genesis from `connect`
...@@ -4,9 +4,9 @@ Parse a URL with memoization. ...@@ -4,9 +4,9 @@ Parse a URL with memoization.
## API ## API
### var pathname = parseurl(req) ### var parsedUrl = parseurl(req)
`pathname` can then be passed to a router or something. `parsedUrl` is basically a `url.parse()` object.
## LICENSE ## LICENSE
......
var parse = require('url').parse; /**
* Module dependencies.
*/
var url = require('url')
var parse = url.parse
var Url = url.Url
/**
* Pattern for a simple path case.
* See: https://github.com/joyent/node/pull/7878
*/
var simplePathRegExp = /^(\/\/?(?!\/)[^\?\s]*)(\?[^\s]*)?$/
var tryFastRegExp = /^\/[^\\#]*$/
/** /**
* Parse the `req` url with memoization. * Parse the `req` url with memoization.
* *
* @param {ServerRequest} req * @param {ServerRequest} req
* @return {Object} * @return {Object}
* @api private * @api public
*/ */
module.exports = function parseUrl(req){ module.exports = function parseUrl(req){
var parsed = req._parsedUrl; var parsed = req._parsedUrl
if (parsed && parsed.href == req.url) {
return parsed;
} else {
parsed = parse(req.url);
if (parsed.auth && !parsed.protocol && ~parsed.href.indexOf('//')) { if (fresh(req, parsed)) {
// This parses pathnames, and a strange pathname like //r@e should work return parsed
parsed = parse(req.url.replace(/@/g, '%40'));
} }
return req._parsedUrl = parsed; parsed = fastparse(req.url)
if (parsed.auth && !parsed.protocol && parsed.href.indexOf('//') !== -1) {
// This parses pathnames, and a strange pathname like //r@e should work
parsed = fastparse(req.url.replace(/@/g, '%40'))
} }
return req._parsedUrl = parsed
}; };
/**
* Parse the `str` url with fast-path short-cut.
*
* @param {string} str
* @return {Object}
* @api private
*/
function fastparse(str) {
if (typeof str === 'string' && tryFastRegExp.test(str)) {
// Try fast path regexp
// See: https://github.com/joyent/node/pull/7878
var simplePath = simplePathRegExp.exec(str)
// Construct simple URL
if (simplePath) {
var url = Url !== undefined
? new Url()
: {}
url.path = str
url.href = str
url.pathname = simplePath[1]
if (simplePath[2]) {
url.search = simplePath[2];
url.query = url.search.substr(1);
}
return url
}
}
return parse(str)
}
/**
* Determine if parsed is still fresh for req.
*
* @param {ServerRequest} req
* @param {object} parsedUrl
* @return {boolean}
* @api private
*/
function fresh(req, parsedUrl) {
return typeof parsedUrl === 'object'
&& parsedUrl !== null
&& (Url === undefined || parsedUrl instanceof Url)
&& parsedUrl.href === req.url
}
{ {
"name": "parseurl", "name": "parseurl",
"description": "parse a url with memoization", "description": "parse a url with memoization",
"version": "1.0.1", "version": "1.1.3",
"author": { "author": {
"name": "Jonathan Ong", "name": "Jonathan Ong",
"email": "me@jongleberry.com", "email": "me@jongleberry.com",
...@@ -16,8 +16,8 @@ ...@@ -16,8 +16,8 @@
"email": "me@jongleberry.com" "email": "me@jongleberry.com"
}, },
"license": "MIT", "license": "MIT",
"readme": "# parseurl\n\nParse a URL with memoization.\n\n## API\n\n### var pathname = parseurl(req)\n\n`pathname` can then be passed to a router or something.\n\n## LICENSE\n\n(The MIT License)\n\nCopyright (c) 2014 Jonathan Ong <me@jongleberry.com>\n\nPermission is hereby granted, free of charge, to any person obtaining\na copy of this software and associated documentation files (the\n'Software'), to deal in the Software without restriction, including\nwithout limitation the rights to use, copy, modify, merge, publish,\ndistribute, sublicense, and/or sell copies of the Software, and to\npermit persons to whom the Software is furnished to do so, subject to\nthe following conditions:\n\nThe above copyright notice and this permission notice shall be\nincluded in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\nIN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\nTORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\nSOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.", "readme": "# parseurl\n\nParse a URL with memoization.\n\n## API\n\n### var parsedUrl = parseurl(req)\n\n`parsedUrl` is basically a `url.parse()` object.\n\n## LICENSE\n\n(The MIT License)\n\nCopyright (c) 2014 Jonathan Ong <me@jongleberry.com>\n\nPermission is hereby granted, free of charge, to any person obtaining\na copy of this software and associated documentation files (the\n'Software'), to deal in the Software without restriction, including\nwithout limitation the rights to use, copy, modify, merge, publish,\ndistribute, sublicense, and/or sell copies of the Software, and to\npermit persons to whom the Software is furnished to do so, subject to\nthe following conditions:\n\nThe above copyright notice and this permission notice shall be\nincluded in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\nIN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\nTORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\nSOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n",
"readmeFilename": "README.md", "readmeFilename": "README.md",
"_id": "parseurl@1.0.1", "_id": "parseurl@1.1.3",
"_from": "parseurl@1.0.1" "_from": "parseurl@~1.1.3"
} }
0.1.3 / 2014-07-06
==================
* Better array support
* Improved support for trailing slash in non-ending mode
0.1.0 / 2014-03-06 0.1.0 / 2014-03-06
================== ==================
......
{ {
"name": "path-to-regexp", "name": "path-to-regexp",
"description": "Express style path to RegExp utility", "description": "Express style path to RegExp utility",
"version": "0.1.0", "version": "0.1.3",
"keywords": [ "keywords": [
"express", "express",
"regexp", "regexp",
......
...@@ -22,22 +22,33 @@ module.exports = pathtoRegexp; ...@@ -22,22 +22,33 @@ module.exports = pathtoRegexp;
function pathtoRegexp(path, keys, options) { function pathtoRegexp(path, keys, options) {
options = options || {}; options = options || {};
var sensitive = options.sensitive;
var strict = options.strict; var strict = options.strict;
var end = options.end !== false; var end = options.end !== false;
var flags = options.sensitive ? '' : 'i';
keys = keys || []; keys = keys || [];
if (path instanceof RegExp) return path; if (path instanceof RegExp) {
if (path instanceof Array) path = '(' + path.join('|') + ')'; return path;
}
path = path if (Array.isArray(path)) {
.concat(strict ? '' : '/?') // Map array parts into regexps and return their source. We also pass
// the same keys and options instance into every generation to get
// consistent matching groups before we join the sources together.
path = path.map(function (value) {
return pathtoRegexp(value, keys, options).source;
});
return new RegExp('(?:' + path.join('|') + ')', flags);
}
path = ('^' + path + (strict ? '' : path[path.length - 1] === '/' ? '?' : '/?'))
.replace(/\/\(/g, '/(?:') .replace(/\/\(/g, '/(?:')
.replace(/([\/\.])/g, '\\$1') .replace(/([\/\.])/g, '\\$1')
.replace(/(\\\/)?(\\\.)?:(\w+)(\(.*?\))?(\*)?(\?)?/g, function (match, slash, format, key, capture, star, optional) { .replace(/(\\\/)?(\\\.)?:(\w+)(\(.*?\))?(\*)?(\?)?/g, function (match, slash, format, key, capture, star, optional) {
slash = slash || ''; slash = slash || '';
format = format || ''; format = format || '';
capture = capture || '([^/' + format + ']+?)'; capture = capture || '([^\\/' + format + ']+?)';
optional = optional || ''; optional = optional || '';
keys.push({ name: key, optional: !!optional }); keys.push({ name: key, optional: !!optional });
...@@ -52,5 +63,8 @@ function pathtoRegexp(path, keys, options) { ...@@ -52,5 +63,8 @@ function pathtoRegexp(path, keys, options) {
}) })
.replace(/\*/g, '(.*)'); .replace(/\*/g, '(.*)');
return new RegExp('^' + path + (end ? '$' : '(?=\/|$)'), sensitive ? '' : 'i'); // If the path is non-ending, match until the end or a slash.
path += (end ? '$' : (path[path.length - 1] === '/' ? '' : '(?=\\/|$)'));
return new RegExp(path, flags);
}; };
{ {
"name": "path-to-regexp", "name": "path-to-regexp",
"description": "Express style path to RegExp utility", "description": "Express style path to RegExp utility",
"version": "0.1.2", "version": "0.1.3",
"scripts": { "scripts": {
"test": "istanbul cover _mocha -- -R spec" "test": "istanbul cover _mocha -- -R spec"
}, },
...@@ -27,6 +27,6 @@ ...@@ -27,6 +27,6 @@
"bugs": { "bugs": {
"url": "https://github.com/component/path-to-regexp/issues" "url": "https://github.com/component/path-to-regexp/issues"
}, },
"_id": "path-to-regexp@0.1.2", "_id": "path-to-regexp@0.1.3",
"_from": "path-to-regexp@0.1.2" "_from": "path-to-regexp@0.1.3"
} }
...@@ -46,6 +46,30 @@ describe('path-to-regexp', function () { ...@@ -46,6 +46,30 @@ describe('path-to-regexp', function () {
assert.ok(!m); assert.ok(!m);
}); });
it('should do strict matches with trailing slashes', function () {
var params = [];
var re = pathToRegExp('/:test/', params, { strict: true });
var m;
assert.equal(params.length, 1);
assert.equal(params[0].name, 'test');
assert.equal(params[0].optional, false);
m = re.exec('/route');
assert.ok(!m);
m = re.exec('/route/');
assert.equal(m.length, 2);
assert.equal(m[0], '/route/');
assert.equal(m[1], 'route');
m = re.exec('/route//');
assert.ok(!m);
});
it('should allow optional express format params', function () { it('should allow optional express format params', function () {
var params = []; var params = [];
var re = pathToRegExp('/:test?', params); var re = pathToRegExp('/:test?', params);
...@@ -388,19 +412,75 @@ describe('path-to-regexp', function () { ...@@ -388,19 +412,75 @@ describe('path-to-regexp', function () {
assert.equal(m[1], 'test'); assert.equal(m[1], 'test');
}); });
it('should match trailing slashes in non-ending non-strict mode', function () {
var params = [];
var re = pathToRegExp('/route/', params, { end: false });
var m;
assert.equal(params.length, 0);
m = re.exec('/route/');
assert.equal(m.length, 1);
assert.equal(m[0], '/route/');
m = re.exec('/route/test');
assert.equal(m.length, 1);
assert.equal(m[0], '/route');
m = re.exec('/route');
assert.equal(m.length, 1);
assert.equal(m[0], '/route');
m = re.exec('/route//');
assert.equal(m.length, 1);
assert.equal(m[0], '/route/');
});
it('should match trailing slashing in non-ending strict mode', function () {
var params = [];
var re = pathToRegExp('/route/', params, { end: false, strict: true });
assert.equal(params.length, 0);
m = re.exec('/route/');
assert.equal(m.length, 1);
assert.equal(m[0], '/route/');
m = re.exec('/route/test');
assert.equal(m.length, 1);
assert.equal(m[0], '/route/');
m = re.exec('/route');
assert.ok(!m);
m = re.exec('/route//');
assert.equal(m.length, 1);
assert.equal(m[0], '/route/');
});
it('should not match trailing slashes in non-ending strict mode', function () { it('should not match trailing slashes in non-ending strict mode', function () {
var params = []; var params = [];
var re = pathToRegExp('/:test', params, { end: false, strict: true }); var re = pathToRegExp('/route', params, { end: false, strict: true });
assert.equal(params.length, 1); assert.equal(params.length, 0);
assert.equal(params[0].name, 'test');
assert.equal(params[0].optional, false);
m = re.exec('/test/'); m = re.exec('/route');
assert.equal(m.length, 2); assert.equal(m.length, 1);
assert.equal(m[0], '/test'); assert.equal(m[0], '/route');
assert.equal(m[1], 'test');
m = re.exec('/route/');
assert.ok(m.length, 1);
assert.equal(m[0], '/route');
}); });
it('should match text after an express param', function () { it('should match text after an express param', function () {
...@@ -502,9 +582,35 @@ describe('path-to-regexp', function () { ...@@ -502,9 +582,35 @@ describe('path-to-regexp', function () {
it('should join arrays parts', function () { it('should join arrays parts', function () {
var re = pathToRegExp(['/test', '/route']); var re = pathToRegExp(['/test', '/route']);
assert.ok(re.exec('/test')); assert.ok(re.test('/test'));
assert.ok(re.exec('/route')); assert.ok(re.test('/route'));
assert.ok(!re.exec('/else')); assert.ok(!re.test('/else'));
});
it('should match parts properly', function () {
var params = [];
var re = pathToRegExp(['/:test', '/test/:route'], params);
var m;
assert.equal(params.length, 2);
assert.equal(params[0].name, 'test');
assert.equal(params[0].optional, false);
assert.equal(params[1].name, 'route');
assert.equal(params[1].optional, false);
m = re.exec('/route');
assert.equal(m.length, 3);
assert.equal(m[0], '/route');
assert.equal(m[1], 'route');
assert.equal(m[2], undefined);
m = re.exec('/test/path');
assert.equal(m.length, 3);
assert.equal(m[0], '/test/path');
assert.equal(m[1], undefined);
assert.equal(m[2], 'path');
}); });
}); });
}); });
...@@ -29,5 +29,6 @@ ...@@ -29,5 +29,6 @@
"url": "https://github.com/visionmedia/node-range-parser/issues" "url": "https://github.com/visionmedia/node-range-parser/issues"
}, },
"_id": "range-parser@1.0.0", "_id": "range-parser@1.0.0",
"_from": "range-parser@~1.0.0" "_from": "range-parser@~1.0.0",
"scripts": {}
} }
0.6.0 / 2014-07-11
==================
* Deprecate `from` option; use `root` option
* Deprecate `send.etag()` -- use `etag` in `options`
* Deprecate `send.hidden()` -- use `hidden` in `options`
* Deprecate `send.index()` -- use `index` in `options`
* Deprecate `send.maxage()` -- use `maxAge` in `options`
* Deprecate `send.root()` -- use `root` in `options`
* deps: debug@1.0.3
- Add support for multiple wildcards in namespaces
0.5.0 / 2014-06-28 0.5.0 / 2014-06-28
================== ==================
......
...@@ -79,7 +79,7 @@ var app = http.createServer(function(req, res){ ...@@ -79,7 +79,7 @@ var app = http.createServer(function(req, res){
set `false` or to supply a new index pass a string or an array set `false` or to supply a new index pass a string or an array
in preferred order. in preferred order.
#### maxage #### maxAge
Provide a max-age in milliseconds for http caching, defaults to 0. Provide a max-age in milliseconds for http caching, defaults to 0.
This can also be a string accepted by the This can also be a string accepted by the
...@@ -98,30 +98,6 @@ var app = http.createServer(function(req, res){ ...@@ -98,30 +98,6 @@ var app = http.createServer(function(req, res){
- `stream` file streaming has started `(stream)` - `stream` file streaming has started `(stream)`
- `end` streaming has completed - `end` streaming has completed
### .etag(bool)
Enable or disable etag generation, defaults to true.
### .root(dir)
Serve files relative to `path`. Aliased as `.from(dir)`.
### .index(paths)
By default send supports "index.html" files, to disable this
invoke `.index(false)` or to supply a new index pass a string
or an array in preferred order.
### .maxage(ms)
Provide a max-age in milliseconds for http caching, defaults to 0.
This can also be a string accepted by the
[ms](https://www.npmjs.org/package/ms#readme) module.
### .hidden(bool)
Enable or disable transfer of hidden files, defaults to false.
## Error-handling ## Error-handling
By default when no `error` listeners are present an automatic response will be made, otherwise you have full control over the response, aka you may show a 5xx page etc. By default when no `error` listeners are present an automatic response will be made, otherwise you have full control over the response, aka you may show a 5xx page etc.
......
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
*/ */
var debug = require('debug')('send') var debug = require('debug')('send')
var deprecate = require('depd')('send')
var escapeHtml = require('escape-html') var escapeHtml = require('escape-html')
, parseRange = require('range-parser') , parseRange = require('range-parser')
, Stream = require('stream') , Stream = require('stream')
...@@ -20,6 +21,10 @@ var escapeHtml = require('escape-html') ...@@ -20,6 +21,10 @@ var escapeHtml = require('escape-html')
var EventEmitter = require('events').EventEmitter; var EventEmitter = require('events').EventEmitter;
var ms = require('ms'); var ms = require('ms');
/**
* Variables.
*/
var maxMaxAge = 60 * 60 * 24 * 365 * 1000; // 1 year
var upPathRegexp = /(?:^|[\\\/])\.\.(?:[\\\/]|$)/; var upPathRegexp = /(?:^|[\\\/])\.\.(?:[\\\/]|$)/;
/** /**
...@@ -78,11 +83,32 @@ function SendStream(req, path, options) { ...@@ -78,11 +83,32 @@ function SendStream(req, path, options) {
this.req = req; this.req = req;
this.path = path; this.path = path;
this.options = options; this.options = options;
this.etag(('etag' in options) ? options.etag : true);
this.maxage(options.maxage); this._etag = options.etag !== undefined
this.hidden(options.hidden); ? Boolean(options.etag)
this.index(('index' in options) ? options.index : 'index.html'); : true
if (options.root || options.from) this.root(options.root || options.from);
this._hidden = Boolean(options.hidden)
this._index = options.index !== undefined
? normalizeIndex(options.index)
: ['index.html']
this._maxage = options.maxAge || options.maxage
this._maxage = typeof this._maxage === 'string'
? ms(this._maxage)
: Number(this._maxage)
this._maxage = !isNaN(this._maxage)
? Math.min(Math.max(0, this._maxage), maxMaxAge)
: 0
this._root = options.root
? normalize(options.root)
: null
if (!this._root && options.from) {
this.from(options.from);
}
} }
/** /**
...@@ -99,12 +125,12 @@ SendStream.prototype.__proto__ = Stream.prototype; ...@@ -99,12 +125,12 @@ SendStream.prototype.__proto__ = Stream.prototype;
* @api public * @api public
*/ */
SendStream.prototype.etag = function(val){ SendStream.prototype.etag = deprecate.function(function etag(val) {
val = Boolean(val); val = Boolean(val);
debug('etag %s', val); debug('etag %s', val);
this._etag = val; this._etag = val;
return this; return this;
}; }, 'send.etag: pass etag as option');
/** /**
* Enable or disable "hidden" (dot) files. * Enable or disable "hidden" (dot) files.
...@@ -114,12 +140,12 @@ SendStream.prototype.etag = function(val){ ...@@ -114,12 +140,12 @@ SendStream.prototype.etag = function(val){
* @api public * @api public
*/ */
SendStream.prototype.hidden = function(val){ SendStream.prototype.hidden = deprecate.function(function hidden(val) {
val = Boolean(val); val = Boolean(val);
debug('hidden %s', val); debug('hidden %s', val);
this._hidden = val; this._hidden = val;
return this; return this;
}; }, 'send.hidden: pass hidden as option');
/** /**
* Set index `paths`, set to a falsy * Set index `paths`, set to a falsy
...@@ -130,12 +156,12 @@ SendStream.prototype.hidden = function(val){ ...@@ -130,12 +156,12 @@ SendStream.prototype.hidden = function(val){
* @api public * @api public
*/ */
SendStream.prototype.index = function index(paths){ SendStream.prototype.index = deprecate.function(function index(paths) {
var index = !paths ? [] : Array.isArray(paths) ? paths : [paths]; var index = !paths ? [] : normalizeIndex(paths);
debug('index %o', paths); debug('index %o', paths);
this._index = index; this._index = index;
return this; return this;
}; }, 'send.index: pass index as option');
/** /**
* Set root `path`. * Set root `path`.
...@@ -145,13 +171,18 @@ SendStream.prototype.index = function index(paths){ ...@@ -145,13 +171,18 @@ SendStream.prototype.index = function index(paths){
* @api public * @api public
*/ */
SendStream.prototype.root = SendStream.prototype.root = function(path){
SendStream.prototype.from = function(path){
path = String(path); path = String(path);
this._root = normalize(path); this._root = normalize(path);
return this; return this;
}; };
SendStream.prototype.from = deprecate.function(SendStream.prototype.root,
'send.from: pass root as option');
SendStream.prototype.root = deprecate.function(SendStream.prototype.root,
'send.root: pass root as option');
/** /**
* Set max-age to `maxAge`. * Set max-age to `maxAge`.
* *
...@@ -160,7 +191,7 @@ SendStream.prototype.from = function(path){ ...@@ -160,7 +191,7 @@ SendStream.prototype.from = function(path){
* @api public * @api public
*/ */
SendStream.prototype.maxage = function maxage(maxAge){ SendStream.prototype.maxage = deprecate.function(function maxage(maxAge) {
maxAge = typeof maxAge === 'string' maxAge = typeof maxAge === 'string'
? ms(maxAge) ? ms(maxAge)
: Number(maxAge); : Number(maxAge);
...@@ -169,7 +200,7 @@ SendStream.prototype.maxage = function maxage(maxAge){ ...@@ -169,7 +200,7 @@ SendStream.prototype.maxage = function maxage(maxAge){
debug('max-age %d', maxAge); debug('max-age %d', maxAge);
this._maxage = maxAge; this._maxage = maxAge;
return this; return this;
}; }, 'send.maxage: pass maxAge as option');
/** /**
* Emit error with `status`. * Emit error with `status`.
...@@ -431,6 +462,8 @@ SendStream.prototype.send = function(path, stat){ ...@@ -431,6 +462,8 @@ SendStream.prototype.send = function(path, stat){
return this.headersAlreadySent(); return this.headersAlreadySent();
} }
debug('options %o', options);
// set header fields // set header fields
this.setHeader(path, stat); this.setHeader(path, stat);
...@@ -622,3 +655,14 @@ SendStream.prototype.setHeader = function setHeader(path, stat){ ...@@ -622,3 +655,14 @@ SendStream.prototype.setHeader = function setHeader(path, stat){
res.setHeader('ETag', etag); res.setHeader('ETag', etag);
} }
}; };
/**
* Normalize the index option into an array.
*
* @param {boolean|string|array} val
* @api private
*/
function normalizeIndex(val){
return [].concat(val || [])
}
...@@ -31,5 +31,6 @@ ...@@ -31,5 +31,6 @@
"url": "https://github.com/broofa/node-mime/issues" "url": "https://github.com/broofa/node-mime/issues"
}, },
"_id": "mime@1.2.11", "_id": "mime@1.2.11",
"_from": "mime@1.2.11" "_from": "mime@1.2.11",
"scripts": {}
} }
{ {
"name": "send", "name": "send",
"description": "Better streaming static file server with Range and conditional-GET support", "description": "Better streaming static file server with Range and conditional-GET support",
"version": "0.5.0", "version": "0.6.0",
"author": { "author": {
"name": "TJ Holowaychuk", "name": "TJ Holowaychuk",
"email": "tj@vision-media.ca" "email": "tj@vision-media.ca"
...@@ -23,7 +23,8 @@ ...@@ -23,7 +23,8 @@
"server" "server"
], ],
"dependencies": { "dependencies": {
"debug": "1.0.2", "debug": "1.0.3",
"depd": "0.3.0",
"escape-html": "1.0.1", "escape-html": "1.0.1",
"finished": "1.2.2", "finished": "1.2.2",
"fresh": "0.2.2", "fresh": "0.2.2",
...@@ -32,7 +33,7 @@ ...@@ -32,7 +33,7 @@
"range-parser": "~1.0.0" "range-parser": "~1.0.0"
}, },
"devDependencies": { "devDependencies": {
"istanbul": "0.2.10", "istanbul": "0.3.0",
"mocha": "~1.20.0", "mocha": "~1.20.0",
"should": "~4.0.0", "should": "~4.0.0",
"supertest": "~0.13.0" "supertest": "~0.13.0"
...@@ -45,11 +46,11 @@ ...@@ -45,11 +46,11 @@
"test-cov": "istanbul cover node_modules/mocha/bin/_mocha -- --reporter dot", "test-cov": "istanbul cover node_modules/mocha/bin/_mocha -- --reporter dot",
"test-travis": "istanbul cover node_modules/mocha/bin/_mocha --report lcovonly -- --reporter spec" "test-travis": "istanbul cover node_modules/mocha/bin/_mocha --report lcovonly -- --reporter spec"
}, },
"readme": "# send\n\n[![NPM version](https://badge.fury.io/js/send.svg)](https://badge.fury.io/js/send)\n[![Build Status](https://travis-ci.org/visionmedia/send.svg?branch=master)](https://travis-ci.org/visionmedia/send)\n[![Coverage Status](https://img.shields.io/coveralls/visionmedia/send.svg?branch=master)](https://coveralls.io/r/visionmedia/send)\n\n Send is Connect's `static()` extracted for generalized use, a streaming static file\n server supporting partial responses (Ranges), conditional-GET negotiation, high test coverage, and granular events which may be leveraged to take appropriate actions in your application or framework.\n\n## Installation\n\n $ npm install send\n\n## Examples\n\n Small:\n\n```js\nvar http = require('http');\nvar send = require('send');\n\nvar app = http.createServer(function(req, res){\n send(req, req.url).pipe(res);\n}).listen(3000);\n```\n\n Serving from a root directory with custom error-handling:\n\n```js\nvar http = require('http');\nvar send = require('send');\nvar url = require('url');\n\nvar app = http.createServer(function(req, res){\n // your custom error-handling logic:\n function error(err) {\n res.statusCode = err.status || 500;\n res.end(err.message);\n }\n\n // your custom headers\n function headers(res, path, stat) {\n // serve all files for download\n res.setHeader('Content-Disposition', 'attachment');\n }\n\n // your custom directory handling logic:\n function redirect() {\n res.statusCode = 301;\n res.setHeader('Location', req.url + '/');\n res.end('Redirecting to ' + req.url + '/');\n }\n\n // transfer arbitrary files from within\n // /www/example.com/public/*\n send(req, url.parse(req.url).pathname, {root: '/www/example.com/public'})\n .on('error', error)\n .on('directory', redirect)\n .on('headers', headers)\n .pipe(res);\n}).listen(3000);\n```\n\n## API\n\n### Options\n\n#### etag\n\n Enable or disable etag generation, defaults to true.\n\n#### hidden\n\n Enable or disable transfer of hidden files, defaults to false.\n\n#### index\n\n By default send supports \"index.html\" files, to disable this\n set `false` or to supply a new index pass a string or an array\n in preferred order.\n\n#### maxage\n\n Provide a max-age in milliseconds for http caching, defaults to 0.\n This can also be a string accepted by the\n [ms](https://www.npmjs.org/package/ms#readme) module.\n\n#### root\n\n Serve files relative to `path`.\n\n### Events\n\n - `error` an error occurred `(err)`\n - `directory` a directory was requested\n - `file` a file was requested `(path, stat)`\n - `headers` the headers are about to be set on a file `(res, path, stat)`\n - `stream` file streaming has started `(stream)`\n - `end` streaming has completed\n\n### .etag(bool)\n\n Enable or disable etag generation, defaults to true.\n\n### .root(dir)\n\n Serve files relative to `path`. Aliased as `.from(dir)`.\n\n### .index(paths)\n\n By default send supports \"index.html\" files, to disable this\n invoke `.index(false)` or to supply a new index pass a string\n or an array in preferred order.\n\n### .maxage(ms)\n\n Provide a max-age in milliseconds for http caching, defaults to 0.\n This can also be a string accepted by the\n [ms](https://www.npmjs.org/package/ms#readme) module.\n\n### .hidden(bool)\n\n Enable or disable transfer of hidden files, defaults to false.\n\n## Error-handling\n\n By default when no `error` listeners are present an automatic response will be made, otherwise you have full control over the response, aka you may show a 5xx page etc.\n\n## Caching\n\n It does _not_ perform internal caching, you should use a reverse proxy cache such\n as Varnish for this, or those fancy things called CDNs. If your application is small enough that it would benefit from single-node memory caching, it's small enough that it does not need caching at all ;).\n\n## Debugging\n\n To enable `debug()` instrumentation output export __DEBUG__:\n\n```\n$ DEBUG=send node app\n```\n\n## Running tests\n\n```\n$ npm install\n$ npm test\n```\n\n## License \n\n(The MIT License)\n\nCopyright (c) 2012 TJ Holowaychuk &lt;tj@vision-media.ca&gt;\n\nPermission is hereby granted, free of charge, to any person obtaining\na copy of this software and associated documentation files (the\n'Software'), to deal in the Software without restriction, including\nwithout limitation the rights to use, copy, modify, merge, publish,\ndistribute, sublicense, and/or sell copies of the Software, and to\npermit persons to whom the Software is furnished to do so, subject to\nthe following conditions:\n\nThe above copyright notice and this permission notice shall be\nincluded in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\nIN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\nTORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\nSOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n", "readme": "# send\n\n[![NPM version](https://badge.fury.io/js/send.svg)](https://badge.fury.io/js/send)\n[![Build Status](https://travis-ci.org/visionmedia/send.svg?branch=master)](https://travis-ci.org/visionmedia/send)\n[![Coverage Status](https://img.shields.io/coveralls/visionmedia/send.svg?branch=master)](https://coveralls.io/r/visionmedia/send)\n\n Send is Connect's `static()` extracted for generalized use, a streaming static file\n server supporting partial responses (Ranges), conditional-GET negotiation, high test coverage, and granular events which may be leveraged to take appropriate actions in your application or framework.\n\n## Installation\n\n $ npm install send\n\n## Examples\n\n Small:\n\n```js\nvar http = require('http');\nvar send = require('send');\n\nvar app = http.createServer(function(req, res){\n send(req, req.url).pipe(res);\n}).listen(3000);\n```\n\n Serving from a root directory with custom error-handling:\n\n```js\nvar http = require('http');\nvar send = require('send');\nvar url = require('url');\n\nvar app = http.createServer(function(req, res){\n // your custom error-handling logic:\n function error(err) {\n res.statusCode = err.status || 500;\n res.end(err.message);\n }\n\n // your custom headers\n function headers(res, path, stat) {\n // serve all files for download\n res.setHeader('Content-Disposition', 'attachment');\n }\n\n // your custom directory handling logic:\n function redirect() {\n res.statusCode = 301;\n res.setHeader('Location', req.url + '/');\n res.end('Redirecting to ' + req.url + '/');\n }\n\n // transfer arbitrary files from within\n // /www/example.com/public/*\n send(req, url.parse(req.url).pathname, {root: '/www/example.com/public'})\n .on('error', error)\n .on('directory', redirect)\n .on('headers', headers)\n .pipe(res);\n}).listen(3000);\n```\n\n## API\n\n### Options\n\n#### etag\n\n Enable or disable etag generation, defaults to true.\n\n#### hidden\n\n Enable or disable transfer of hidden files, defaults to false.\n\n#### index\n\n By default send supports \"index.html\" files, to disable this\n set `false` or to supply a new index pass a string or an array\n in preferred order.\n\n#### maxAge\n\n Provide a max-age in milliseconds for http caching, defaults to 0.\n This can also be a string accepted by the\n [ms](https://www.npmjs.org/package/ms#readme) module.\n\n#### root\n\n Serve files relative to `path`.\n\n### Events\n\n - `error` an error occurred `(err)`\n - `directory` a directory was requested\n - `file` a file was requested `(path, stat)`\n - `headers` the headers are about to be set on a file `(res, path, stat)`\n - `stream` file streaming has started `(stream)`\n - `end` streaming has completed\n\n## Error-handling\n\n By default when no `error` listeners are present an automatic response will be made, otherwise you have full control over the response, aka you may show a 5xx page etc.\n\n## Caching\n\n It does _not_ perform internal caching, you should use a reverse proxy cache such\n as Varnish for this, or those fancy things called CDNs. If your application is small enough that it would benefit from single-node memory caching, it's small enough that it does not need caching at all ;).\n\n## Debugging\n\n To enable `debug()` instrumentation output export __DEBUG__:\n\n```\n$ DEBUG=send node app\n```\n\n## Running tests\n\n```\n$ npm install\n$ npm test\n```\n\n## License \n\n(The MIT License)\n\nCopyright (c) 2012 TJ Holowaychuk &lt;tj@vision-media.ca&gt;\n\nPermission is hereby granted, free of charge, to any person obtaining\na copy of this software and associated documentation files (the\n'Software'), to deal in the Software without restriction, including\nwithout limitation the rights to use, copy, modify, merge, publish,\ndistribute, sublicense, and/or sell copies of the Software, and to\npermit persons to whom the Software is furnished to do so, subject to\nthe following conditions:\n\nThe above copyright notice and this permission notice shall be\nincluded in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\nIN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\nTORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\nSOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n",
"readmeFilename": "Readme.md", "readmeFilename": "Readme.md",
"bugs": { "bugs": {
"url": "https://github.com/visionmedia/send/issues" "url": "https://github.com/visionmedia/send/issues"
}, },
"_id": "send@0.5.0", "_id": "send@0.6.0",
"_from": "send@0.5.0" "_from": "send@0.6.0"
} }
1.3.2 / 2014-07-11
==================
* deps: send@0.6.0
- deps: debug@1.0.3
1.3.1 / 2014-07-09
==================
* deps: parseurl@~1.1.3
- faster parsing of href-only URLs
1.3.0 / 2014-06-28
==================
* Add `setHeaders` option
* Include HTML link in redirect response
* deps: send@0.5.0
- Accept string for `maxAge` (converted by `ms`)
1.2.3 / 2014-06-11
==================
* deps: send@0.4.3
- Do not throw un-catchable error on file open race condition
- Use `escape-html` for HTML escaping
- deps: debug@1.0.2
- deps: finished@1.2.2
- deps: fresh@0.2.2
1.2.2 / 2014-06-09
==================
* deps: send@0.4.2
- fix "event emitter leak" warnings
- deps: debug@1.0.1
- deps: finished@1.2.1
1.2.1 / 2014-06-02
==================
* use `escape-html` for escaping
* deps: send@0.4.1
- Send `max-age` in `Cache-Control` in correct format
1.2.0 / 2014-05-29
==================
* deps: send@0.4.0
- Calculate ETag with md5 for reduced collisions
- Fix wrong behavior when index file matches directory
- Ignore stream errors after request ends
- Skip directories in index file search
- deps: debug@0.8.1
1.1.0 / 2014-04-24
==================
* Accept options directly to `send` module
* deps: send@0.3.0
1.0.4 / 2014-04-07
==================
* Resolve relative paths at middleware setup
* Use parseurl to parse the URL from request
1.0.3 / 2014-03-20
==================
* Do not rely on connect-like environments
1.0.2 / 2014-03-06
==================
* deps: send@0.2.0
1.0.1 / 2014-03-05
==================
* Add mime export for back-compat
1.0.0 / 2014-03-05
==================
* Genesis from `connect`
(The MIT License)
Copyright (c) 2010 Sencha Inc.
Copyright (c) 2011 LearnBoost
Copyright (c) 2011 TJ Holowaychuk
Copyright (c) 2014 Douglas Christopher Wilson
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
'Software'), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
# serve-static
[![NPM version](https://badge.fury.io/js/serve-static.svg)](http://badge.fury.io/js/serve-static)
[![Build Status](https://travis-ci.org/expressjs/serve-static.svg?branch=master)](https://travis-ci.org/expressjs/serve-static)
[![Coverage Status](https://img.shields.io/coveralls/expressjs/serve-static.svg?branch=master)](https://coveralls.io/r/expressjs/serve-static)
Previously `connect.static()`.
## Install
```sh
$ npm install serve-static
```
## API
```js
var serveStatic = require('serve-static')
```
### serveStatic(root, options)
Create a new middleware function to serve files from within a given root
directory. The file to serve will be determined by combining `req.url`
with the provided root directory.
Options:
- `hidden` Allow transfer of hidden files. defaults to `false`
- `index` Default file name, defaults to `'index.html'`
- `maxAge` Browser cache maxAge in milliseconds. This can also be a string accepted by the [ms](https://www.npmjs.org/package/ms#readme) module. defaults to `0`
- `redirect` Redirect to trailing "/" when the pathname is a dir. defaults to `true`
- `setHeaders` Function to set custom headers on response.
## Examples
### Serve files with vanilla node.js http server
```js
var finalhandler = require('finalhandler')
var http = require('http')
var serveStatic = require('serve-static')
// Serve up public/ftp folder
var serve = serveStatic('public/ftp', {'index': ['index.html', 'index.htm']})
// Create server
var server = http.createServer(function(req, res){
var done = finalhandler(req, res)
serve(req, res, done)
})
// Listen
server.listen(3000)
```
### Serve all files from ftp folder
```js
var connect = require('connect')
var serveStatic = require('serve-static')
var app = connect()
app.use(serveStatic('public/ftp', {'index': ['default.html', 'default.htm']}))
app.listen(3000)
```
### Serve all files as downloads
```js
var express = require('express')
var serveStatic = require('serve-static')
var app = express()
app.use(serveStatic('public/ftp', {
'index': false,
'setHeaders': setHeaders
}))
app.listen(3000)
function setHeaders(res, path) {
res.attachment(path)
}
```
## License
The MIT License (MIT)
Copyright (c) 2014 Douglas Christopher Wilson
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
/*!
* serve-static
* Copyright(c) 2010 Sencha Inc.
* Copyright(c) 2011 TJ Holowaychuk
* Copyright(c) 2014 Douglas Christopher Wilson
* MIT Licensed
*/
/**
* Module dependencies.
*/
var escapeHtml = require('escape-html');
var parseurl = require('parseurl');
var resolve = require('path').resolve;
var send = require('send');
var url = require('url');
/**
* @param {String} root
* @param {Object} options
* @return {Function}
* @api public
*/
exports = module.exports = function(root, options){
options = extend({}, options);
// root required
if (!root) throw new TypeError('root path required');
// resolve root to absolute
root = resolve(root);
// default redirect
var redirect = false !== options.redirect;
// headers listener
var setHeaders = options.setHeaders
delete options.setHeaders
if (setHeaders && typeof setHeaders !== 'function') {
throw new TypeError('option setHeaders must be function')
}
// setup options for send
options.maxage = options.maxage || options.maxAge || 0;
options.root = root;
return function staticMiddleware(req, res, next) {
if ('GET' != req.method && 'HEAD' != req.method) return next();
var opts = extend({}, options);
var originalUrl = url.parse(req.originalUrl || req.url);
var path = parseurl(req).pathname;
if (path === '/' && originalUrl.pathname[originalUrl.pathname.length - 1] !== '/') {
// make sure redirect occurs at mount
path = ''
}
// create send stream
var stream = send(req, path, opts)
if (redirect) {
// redirect relative to originalUrl
stream.on('directory', function redirect() {
originalUrl.pathname += '/'
var target = url.format(originalUrl)
res.statusCode = 303
res.setHeader('Content-Type', 'text/html; charset=utf-8')
res.setHeader('Location', target)
res.end('Redirecting to <a href="' + escapeHtml(target) + '">' + escapeHtml(target) + '</a>\n')
})
} else {
// forward to next middleware on directory
stream.on('directory', next)
}
// add headers listener
if (setHeaders) {
stream.on('headers', setHeaders)
}
// forward non-404 errors
stream.on('error', function error(err) {
next(err.status === 404 ? null : err)
})
// pipe
stream.pipe(res)
};
};
/**
* Expose mime module.
*
* If you wish to extend the mime table use this
* reference to the "mime" module in the npm registry.
*/
exports.mime = send.mime;
/**
* Shallow clone a single object.
*
* @param {Object} obj
* @param {Object} source
* @return {Object}
* @api private
*/
function extend(obj, source) {
if (!source) return obj;
for (var prop in source) {
obj[prop] = source[prop];
}
return obj;
};
{
"name": "serve-static",
"description": "Serve static files",
"version": "1.3.2",
"author": {
"name": "Douglas Christopher Wilson",
"email": "doug@somethingdoug.com"
},
"license": "MIT",
"repository": {
"type": "git",
"url": "git://github.com/expressjs/serve-static"
},
"dependencies": {
"escape-html": "1.0.1",
"parseurl": "~1.1.3",
"send": "0.6.0"
},
"devDependencies": {
"istanbul": "0.3.0",
"mocha": "~1.20.0",
"should": "~4.0.0",
"supertest": "~0.13.0"
},
"engines": {
"node": ">= 0.8.0"
},
"scripts": {
"test": "mocha --reporter dot --require should test/",
"test-cov": "istanbul cover node_modules/mocha/bin/_mocha -- --reporter dot --require should test/",
"test-travis": "istanbul cover node_modules/mocha/bin/_mocha --report lcovonly -- --reporter spec --require should test/"
},
"readme": "# serve-static\n\n[![NPM version](https://badge.fury.io/js/serve-static.svg)](http://badge.fury.io/js/serve-static)\n[![Build Status](https://travis-ci.org/expressjs/serve-static.svg?branch=master)](https://travis-ci.org/expressjs/serve-static)\n[![Coverage Status](https://img.shields.io/coveralls/expressjs/serve-static.svg?branch=master)](https://coveralls.io/r/expressjs/serve-static)\n\nPreviously `connect.static()`.\n\n## Install\n\n```sh\n$ npm install serve-static\n```\n\n## API\n\n```js\nvar serveStatic = require('serve-static')\n```\n\n### serveStatic(root, options)\n\nCreate a new middleware function to serve files from within a given root\ndirectory. The file to serve will be determined by combining `req.url`\nwith the provided root directory.\n\nOptions:\n\n- `hidden` Allow transfer of hidden files. defaults to `false`\n- `index` Default file name, defaults to `'index.html'`\n- `maxAge` Browser cache maxAge in milliseconds. This can also be a string accepted by the [ms](https://www.npmjs.org/package/ms#readme) module. defaults to `0`\n- `redirect` Redirect to trailing \"/\" when the pathname is a dir. defaults to `true`\n- `setHeaders` Function to set custom headers on response.\n\n## Examples\n\n### Serve files with vanilla node.js http server\n\n```js\nvar finalhandler = require('finalhandler')\nvar http = require('http')\nvar serveStatic = require('serve-static')\n\n// Serve up public/ftp folder\nvar serve = serveStatic('public/ftp', {'index': ['index.html', 'index.htm']})\n\n// Create server\nvar server = http.createServer(function(req, res){\n var done = finalhandler(req, res)\n serve(req, res, done)\n})\n\n// Listen\nserver.listen(3000)\n```\n\n### Serve all files from ftp folder\n\n```js\nvar connect = require('connect')\nvar serveStatic = require('serve-static')\n\nvar app = connect()\n\napp.use(serveStatic('public/ftp', {'index': ['default.html', 'default.htm']}))\napp.listen(3000)\n```\n\n### Serve all files as downloads\n\n```js\nvar express = require('express')\nvar serveStatic = require('serve-static')\n\nvar app = express()\n\napp.use(serveStatic('public/ftp', {\n 'index': false,\n 'setHeaders': setHeaders\n}))\napp.listen(3000)\n\nfunction setHeaders(res, path) {\n res.attachment(path)\n}\n```\n\n## License\n\nThe MIT License (MIT)\n\nCopyright (c) 2014 Douglas Christopher Wilson\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n",
"readmeFilename": "Readme.md",
"bugs": {
"url": "https://github.com/expressjs/serve-static/issues"
},
"_id": "serve-static@1.3.2",
"_from": "serve-static@~1.3.2"
}
{ {
"name": "express", "name": "express",
"description": "Fast, unopinionated, minimalist web framework", "description": "Fast, unopinionated, minimalist web framework",
"version": "4.5.1", "version": "4.6.1",
"author": { "author": {
"name": "TJ Holowaychuk", "name": "TJ Holowaychuk",
"email": "tj@vision-media.ca" "email": "tj@vision-media.ca"
...@@ -50,30 +50,30 @@ ...@@ -50,30 +50,30 @@
"dependencies": { "dependencies": {
"accepts": "~1.0.7", "accepts": "~1.0.7",
"buffer-crc32": "0.2.3", "buffer-crc32": "0.2.3",
"debug": "1.0.2", "debug": "1.0.3",
"depd": "0.3.0", "depd": "0.3.0",
"escape-html": "1.0.1", "escape-html": "1.0.1",
"finalhandler": "0.0.2", "finalhandler": "0.0.3",
"media-typer": "0.2.0", "media-typer": "0.2.0",
"methods": "1.0.1", "methods": "1.1.0",
"parseurl": "1.0.1", "parseurl": "~1.1.3",
"path-to-regexp": "0.1.3",
"proxy-addr": "1.0.1", "proxy-addr": "1.0.1",
"range-parser": "1.0.0", "range-parser": "1.0.0",
"send": "0.5.0", "send": "0.6.0",
"serve-static": "~1.3.0", "serve-static": "~1.3.2",
"type-is": "~1.3.2", "type-is": "~1.3.2",
"vary": "0.1.0", "vary": "0.1.0",
"cookie": "0.1.2", "cookie": "0.1.2",
"fresh": "0.2.2", "fresh": "0.2.2",
"cookie-signature": "1.0.4", "cookie-signature": "1.0.4",
"merge-descriptors": "0.0.2", "merge-descriptors": "0.0.2",
"utils-merge": "1.0.0",
"qs": "0.6.6", "qs": "0.6.6",
"path-to-regexp": "0.1.2" "utils-merge": "1.0.0"
}, },
"devDependencies": { "devDependencies": {
"after": "0.8.1", "after": "0.8.1",
"istanbul": "0.2.14", "istanbul": "0.3.0",
"mocha": "~1.20.1", "mocha": "~1.20.1",
"should": "~4.0.4", "should": "~4.0.4",
"supertest": "~0.13.0", "supertest": "~0.13.0",
...@@ -84,8 +84,8 @@ ...@@ -84,8 +84,8 @@
"hjs": "~0.0.6", "hjs": "~0.0.6",
"body-parser": "~1.4.3", "body-parser": "~1.4.3",
"cookie-parser": "~1.3.1", "cookie-parser": "~1.3.1",
"express-session": "~1.6.1", "express-session": "~1.6.5",
"method-override": "2.0.2", "method-override": "~2.1.1",
"multiparty": "~3.3.0", "multiparty": "~3.3.0",
"morgan": "1.1.1", "morgan": "1.1.1",
"vhost": "2.0.0" "vhost": "2.0.0"
...@@ -99,11 +99,11 @@ ...@@ -99,11 +99,11 @@
"test-cov": "istanbul cover node_modules/mocha/bin/_mocha -- --require test/support/env --reporter dot --check-leaks test/ test/acceptance/", "test-cov": "istanbul cover node_modules/mocha/bin/_mocha -- --require test/support/env --reporter dot --check-leaks test/ test/acceptance/",
"test-travis": "istanbul cover node_modules/mocha/bin/_mocha --report lcovonly -- --require test/support/env --reporter spec --check-leaks test/ test/acceptance/" "test-travis": "istanbul cover node_modules/mocha/bin/_mocha --report lcovonly -- --require test/support/env --reporter spec --check-leaks test/ test/acceptance/"
}, },
"readme": "[![Express Logo](https://i.cloudup.com/zfY6lL7eFa-3000x3000.png)](https://expressjs.com/)\n\n Fast, unopinionated, minimalist web framework for [node](https://nodejs.org).\n\n [![NPM Version](https://badge.fury.io/js/express.svg)](https://badge.fury.io/js/express)\n [![Build Status](https://travis-ci.org/visionmedia/express.svg?branch=master)](https://travis-ci.org/visionmedia/express)\n [![Coverage Status](https://img.shields.io/coveralls/visionmedia/express.svg)](https://coveralls.io/r/visionmedia/express)\n [![Gittip](https://img.shields.io/gittip/visionmedia.svg)](https://www.gittip.com/visionmedia/)\n\n```js\nvar express = require('express')\nvar app = express()\n\napp.get('/', function (req, res) {\n res.send('Hello World')\n})\n\napp.listen(3000)\n```\n\n **PROTIP** Be sure to read [Migrating from 3.x to 4.x](https://github.com/visionmedia/express/wiki/Migrating-from-3.x-to-4.x) as well as [New features in 4.x](https://github.com/visionmedia/express/wiki/New-features-in-4.x).\n\n### Installation\n\n```bash\n$ npm install express\n```\n\n## Quick Start\n\n The quickest way to get started with express is to utilize the executable [`express(1)`](https://github.com/expressjs/generator) to generate an application as shown below:\n\n Install the executable. The executable's major version will match Express's:\n\n```bash\n$ npm install -g express-generator@4\n```\n\n Create the app:\n\n```bash\n$ express /tmp/foo && cd /tmp/foo\n```\n\n Install dependencies:\n\n```bash\n$ npm install\n```\n\n Start the server:\n\n```bash\n$ npm start\n```\n\n## Features\n\n * Robust routing\n * HTTP helpers (redirection, caching, etc)\n * View system supporting 14+ template engines\n * Content negotiation\n * Focus on high performance\n * Executable for generating applications quickly\n * High test coverage\n\n## Philosophy\n\n The Express philosophy is to provide small, robust tooling for HTTP servers, making\n it a great solution for single page applications, web sites, hybrids, or public\n HTTP APIs.\n\n Express does not force you to use any specific ORM or template engine. With support for over\n 14 template engines via [Consolidate.js](https://github.com/visionmedia/consolidate.js),\n you can quickly craft your perfect framework.\n\n## More Information\n\n * [Website and Documentation](http://expressjs.com/) - [[website repo](https://github.com/visionmedia/expressjs.com)]\n * [Github Organization](https://github.com/expressjs) for Official Middleware & Modules\n * [#express](https://webchat.freenode.net/?channels=express) on freenode IRC\n * Visit the [Wiki](https://github.com/visionmedia/express/wiki)\n * [Google Group](https://groups.google.com/group/express-js) for discussion\n * [Русскоязычная документация](http://jsman.ru/express/)\n * Run express examples [online](https://runnable.com/express)\n\n## Viewing Examples\n\n Clone the Express repo, then install the dev dependencies to install all the example / test suite dependencies:\n\n```bash\n$ git clone git://github.com/visionmedia/express.git --depth 1\n$ cd express\n$ npm install\n```\n\n Then run whichever example you want:\n\n $ node examples/content-negotiation\n\n You can also view live examples here:\n\n <a href=\"https://runnable.com/express\" target=\"_blank\"><img src=\"https://runnable.com/external/styles/assets/runnablebtn.png\" style=\"width:67px;height:25px;\"></a>\n\n## Running Tests\n\n To run the test suite, first invoke the following command within the repo, installing the development dependencies:\n\n```bash\n$ npm install\n```\n\n Then run the tests:\n\n```bash\n$ npm test\n```\n\n### Contributors\n\n * Author: [TJ Holowaychuk](https://github.com/visionmedia)\n * Lead Maintainer: [Douglas Christopher Wilson](https://github.com/dougwilson)\n * [All Contributors](https://github.com/visionmedia/express/graphs/contributors)\n\n### License\n\n [MIT](LICENSE)\n", "readme": "[![Express Logo](https://i.cloudup.com/zfY6lL7eFa-3000x3000.png)](https://expressjs.com/)\n\n Fast, unopinionated, minimalist web framework for [node](http://nodejs.org).\n\n [![NPM Version](https://badge.fury.io/js/express.svg)](https://badge.fury.io/js/express)\n [![Build Status](https://travis-ci.org/visionmedia/express.svg?branch=master)](https://travis-ci.org/visionmedia/express)\n [![Coverage Status](https://img.shields.io/coveralls/visionmedia/express.svg)](https://coveralls.io/r/visionmedia/express)\n [![Gittip](https://img.shields.io/gittip/visionmedia.svg)](https://www.gittip.com/visionmedia/)\n\n```js\nvar express = require('express')\nvar app = express()\n\napp.get('/', function (req, res) {\n res.send('Hello World')\n})\n\napp.listen(3000)\n```\n\n **PROTIP** Be sure to read [Migrating from 3.x to 4.x](https://github.com/visionmedia/express/wiki/Migrating-from-3.x-to-4.x) as well as [New features in 4.x](https://github.com/visionmedia/express/wiki/New-features-in-4.x).\n\n### Installation\n\n```bash\n$ npm install express\n```\n\n## Quick Start\n\n The quickest way to get started with express is to utilize the executable [`express(1)`](https://github.com/expressjs/generator) to generate an application as shown below:\n\n Install the executable. The executable's major version will match Express's:\n\n```bash\n$ npm install -g express-generator@4\n```\n\n Create the app:\n\n```bash\n$ express /tmp/foo && cd /tmp/foo\n```\n\n Install dependencies:\n\n```bash\n$ npm install\n```\n\n Start the server:\n\n```bash\n$ npm start\n```\n\n## Features\n\n * Robust routing\n * HTTP helpers (redirection, caching, etc)\n * View system supporting 14+ template engines\n * Content negotiation\n * Focus on high performance\n * Executable for generating applications quickly\n * High test coverage\n\n## Philosophy\n\n The Express philosophy is to provide small, robust tooling for HTTP servers, making\n it a great solution for single page applications, web sites, hybrids, or public\n HTTP APIs.\n\n Express does not force you to use any specific ORM or template engine. With support for over\n 14 template engines via [Consolidate.js](https://github.com/visionmedia/consolidate.js),\n you can quickly craft your perfect framework.\n\n## More Information\n\n * [Website and Documentation](http://expressjs.com/) - [[website repo](https://github.com/visionmedia/expressjs.com)]\n * [Github Organization](https://github.com/expressjs) for Official Middleware & Modules\n * [#express](https://webchat.freenode.net/?channels=express) on freenode IRC\n * Visit the [Wiki](https://github.com/visionmedia/express/wiki)\n * [Google Group](https://groups.google.com/group/express-js) for discussion\n * [Русскоязычная документация](http://jsman.ru/express/)\n * Run express examples [online](https://runnable.com/express)\n\n## Viewing Examples\n\n Clone the Express repo, then install the dev dependencies to install all the example / test suite dependencies:\n\n```bash\n$ git clone git://github.com/visionmedia/express.git --depth 1\n$ cd express\n$ npm install\n```\n\n Then run whichever example you want:\n\n $ node examples/content-negotiation\n\n You can also view live examples here:\n\n <a href=\"https://runnable.com/express\" target=\"_blank\"><img src=\"https://runnable.com/external/styles/assets/runnablebtn.png\" style=\"width:67px;height:25px;\"></a>\n\n## Running Tests\n\n To run the test suite, first invoke the following command within the repo, installing the development dependencies:\n\n```bash\n$ npm install\n```\n\n Then run the tests:\n\n```bash\n$ npm test\n```\n\n### Contributors\n\n * Author: [TJ Holowaychuk](https://github.com/visionmedia)\n * Lead Maintainer: [Douglas Christopher Wilson](https://github.com/dougwilson)\n * [All Contributors](https://github.com/visionmedia/express/graphs/contributors)\n\n### License\n\n [MIT](LICENSE)\n",
"readmeFilename": "Readme.md", "readmeFilename": "Readme.md",
"bugs": { "bugs": {
"url": "https://github.com/visionmedia/express/issues" "url": "https://github.com/visionmedia/express/issues"
}, },
"_id": "express@4.5.1", "_id": "express@4.6.1",
"_from": "express@4.x" "_from": "express@4.x"
} }
test
\ No newline at end of file
language: node_js
node_js:
- "0.11"
- "0.10"
- "0.9"
- "0.8"
- "0.6"
- "0.4"
before_install:
- '[ "${TRAVIS_NODE_VERSION}" == "0.6" ] || npm install -g npm@~1.4.6'
matrix:
fast_finish: true
allow_failures:
- node_js: "0.11"
- node_js: "0.9"
- node_js: "0.6"
- node_js: "0.4"
The MIT License (MIT)
Copyright (c) 2014 Stefan Thomas
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
[![Build Status][1]][2] [![dependency status][9]][10] [![dev dependency status][11]][12]
# extend() for Node.js <sup>[![Version Badge][8]][3]</sup>
`node-extend` is a port of the classic extend() method from jQuery. It behaves as you expect. It is simple, tried and true.
## Installation
This package is available on [npm][3] as: `extend`
``` sh
npm install extend
```
## Usage
**Syntax:** extend **(** [`deep`], `target`, `object1`, [`objectN`] **)**
*Extend one object with one or more others, returning the modified object.*
Keep in mind that the target object will be modified, and will be returned from extend().
If a boolean true is specified as the first argument, extend performs a deep copy, recursively copying any objects it finds. Otherwise, the copy will share structure with the original object(s).
Undefined properties are not copied. However, properties inherited from the object's prototype will be copied over.
Warning: passing `false` as the first argument is not supported.
### Arguments
* `deep` *Boolean* (optional)
If set, the merge becomes recursive (i.e. deep copy).
* `target` *Object*
The object to extend.
* `object1` *Object*
The object that will be merged into the first.
* `objectN` *Object* (Optional)
More objects to merge into the first.
## License
`node-extend` is licensed under the [MIT License][4].
## Acknowledgements
All credit to the jQuery authors for perfecting this amazing utility.
Ported to Node.js by [Stefan Thomas][5] with contributions by [Jonathan Buchanan][6] and [Jordan Harband][7].
[1]: https://travis-ci.org/justmoon/node-extend.svg
[2]: https://travis-ci.org/justmoon/node-extend
[3]: https://npmjs.org/package/extend
[4]: http://opensource.org/licenses/MIT
[5]: https://github.com/justmoon
[6]: https://github.com/insin
[7]: https://github.com/ljharb
[8]: http://vb.teelaun.ch/justmoon/node-extend.svg
[9]: https://david-dm.org/justmoon/node-extend.svg
[10]: https://david-dm.org/justmoon/node-extend
[11]: https://david-dm.org/justmoon/node-extend/dev-status.svg
[12]: https://david-dm.org/justmoon/node-extend#info=devDependencies
{
"name": "extend",
"author": "Stefan Thomas <justmoon@members.fsf.org> (http://www.justmoon.net)",
"version": "1.3.0",
"description": "Port of jQuery.extend for node.js and the browser.",
"scripts": [
"index.js"
],
"contributors": [
{
"name": "Jordan Harband",
"url": "https://github.com/ljharb"
}
],
"keywords": [
"extend",
"clone",
"merge"
],
"repository" : {
"type": "git",
"url": "https://github.com/justmoon/node-extend.git"
},
"dependencies": {
},
"devDependencies": {
"tape" : "~2.12.3",
"covert": "~0.4.0"
}
}
var hasOwn = Object.prototype.hasOwnProperty;
var toString = Object.prototype.toString;
var undefined;
var isPlainObject = function isPlainObject(obj) {
"use strict";
if (!obj || toString.call(obj) !== '[object Object]' || obj.nodeType || obj.setInterval) {
return false;
}
var has_own_constructor = hasOwn.call(obj, 'constructor');
var has_is_property_of_method = obj.constructor && obj.constructor.prototype && hasOwn.call(obj.constructor.prototype, 'isPrototypeOf');
// Not own constructor property must be Object
if (obj.constructor && !has_own_constructor && !has_is_property_of_method) {
return false;
}
// Own properties are enumerated firstly, so to speed up,
// if last one is own, then all properties are own.
var key;
for (key in obj) {}
return key === undefined || hasOwn.call(obj, key);
};
module.exports = function extend() {
"use strict";
var options, name, src, copy, copyIsArray, clone,
target = arguments[0],
i = 1,
length = arguments.length,
deep = false;
// Handle a deep copy situation
if (typeof target === "boolean") {
deep = target;
target = arguments[1] || {};
// skip the boolean and the target
i = 2;
} else if (typeof target !== "object" && typeof target !== "function" || target == undefined) {
target = {};
}
for (; i < length; ++i) {
// Only deal with non-null/undefined values
if ((options = arguments[i]) != null) {
// Extend the base object
for (name in options) {
src = target[name];
copy = options[name];
// Prevent never-ending loop
if (target === copy) {
continue;
}
// Recurse if we're merging plain objects or arrays
if (deep && copy && (isPlainObject(copy) || (copyIsArray = Array.isArray(copy)))) {
if (copyIsArray) {
copyIsArray = false;
clone = src && Array.isArray(src) ? src : [];
} else {
clone = src && isPlainObject(src) ? src : {};
}
// Never move original objects, clone them
target[name] = extend(deep, clone, copy);
// Don't bring in undefined values
} else if (copy !== undefined) {
target[name] = copy;
}
}
}
}
// Return the modified object
return target;
};
{
"name": "extend",
"author": {
"name": "Stefan Thomas",
"email": "justmoon@members.fsf.org",
"url": "http://www.justmoon.net"
},
"version": "1.3.0",
"description": "Port of jQuery.extend for node.js and the browser",
"main": "index",
"scripts": {
"test": "node test/index.js",
"coverage": "covert test/index.js",
"coverage-quiet": "covert test/index.js --quiet"
},
"contributors": [
{
"name": "Jordan Harband",
"url": "https://github.com/ljharb"
}
],
"keywords": [
"extend",
"clone",
"merge"
],
"repository": {
"type": "git",
"url": "https://github.com/justmoon/node-extend.git"
},
"dependencies": {},
"devDependencies": {
"tape": "~2.13.2",
"covert": "~0.4.0"
},
"readme": "[![Build Status][1]][2] [![dependency status][9]][10] [![dev dependency status][11]][12]\n\n# extend() for Node.js <sup>[![Version Badge][8]][3]</sup>\n\n`node-extend` is a port of the classic extend() method from jQuery. It behaves as you expect. It is simple, tried and true.\n\n## Installation\n\nThis package is available on [npm][3] as: `extend`\n\n``` sh\nnpm install extend\n```\n\n## Usage\n\n**Syntax:** extend **(** [`deep`], `target`, `object1`, [`objectN`] **)**\n\n*Extend one object with one or more others, returning the modified object.*\n\nKeep in mind that the target object will be modified, and will be returned from extend().\n\nIf a boolean true is specified as the first argument, extend performs a deep copy, recursively copying any objects it finds. Otherwise, the copy will share structure with the original object(s).\nUndefined properties are not copied. However, properties inherited from the object's prototype will be copied over.\nWarning: passing `false` as the first argument is not supported.\n\n### Arguments\n\n* `deep` *Boolean* (optional)\nIf set, the merge becomes recursive (i.e. deep copy).\n* `target`\t*Object*\nThe object to extend.\n* `object1`\t*Object*\nThe object that will be merged into the first.\n* `objectN` *Object* (Optional)\nMore objects to merge into the first.\n\n## License\n\n`node-extend` is licensed under the [MIT License][4].\n\n## Acknowledgements\n\nAll credit to the jQuery authors for perfecting this amazing utility.\n\nPorted to Node.js by [Stefan Thomas][5] with contributions by [Jonathan Buchanan][6] and [Jordan Harband][7].\n\n[1]: https://travis-ci.org/justmoon/node-extend.svg\n[2]: https://travis-ci.org/justmoon/node-extend\n[3]: https://npmjs.org/package/extend\n[4]: http://opensource.org/licenses/MIT\n[5]: https://github.com/justmoon\n[6]: https://github.com/insin\n[7]: https://github.com/ljharb\n[8]: http://vb.teelaun.ch/justmoon/node-extend.svg\n[9]: https://david-dm.org/justmoon/node-extend.svg\n[10]: https://david-dm.org/justmoon/node-extend\n[11]: https://david-dm.org/justmoon/node-extend/dev-status.svg\n[12]: https://david-dm.org/justmoon/node-extend#info=devDependencies\n\n",
"readmeFilename": "README.md",
"bugs": {
"url": "https://github.com/justmoon/node-extend/issues"
},
"_id": "extend@1.3.0",
"_from": "extend@*"
}
1.4.0 / 2014-07-21
==================
* deps: parseurl@~1.2.0
- Cache URLs based on original value
- Remove no-longer-needed URL mis-parse work-around
- Simplify the "fast-path" `RegExp`
* deps: send@0.7.0
- Add `dotfiles` option
- deps: debug@1.0.4
- deps: depd@0.4.2
1.3.2 / 2014-07-11
==================
* deps: send@0.6.0
- Cap `maxAge` value to 1 year
- deps: debug@1.0.3
1.3.1 / 2014-07-09 1.3.1 / 2014-07-09
================== ==================
......
...@@ -4,8 +4,6 @@ ...@@ -4,8 +4,6 @@
[![Build Status](https://travis-ci.org/expressjs/serve-static.svg?branch=master)](https://travis-ci.org/expressjs/serve-static) [![Build Status](https://travis-ci.org/expressjs/serve-static.svg?branch=master)](https://travis-ci.org/expressjs/serve-static)
[![Coverage Status](https://img.shields.io/coveralls/expressjs/serve-static.svg?branch=master)](https://coveralls.io/r/expressjs/serve-static) [![Coverage Status](https://img.shields.io/coveralls/expressjs/serve-static.svg?branch=master)](https://coveralls.io/r/expressjs/serve-static)
Previously `connect.static()`.
## Install ## Install
```sh ```sh
...@@ -22,15 +20,50 @@ var serveStatic = require('serve-static') ...@@ -22,15 +20,50 @@ var serveStatic = require('serve-static')
Create a new middleware function to serve files from within a given root Create a new middleware function to serve files from within a given root
directory. The file to serve will be determined by combining `req.url` directory. The file to serve will be determined by combining `req.url`
with the provided root directory. with the provided root directory. When a file is not found, instead of
sending a 404 response, this module will instead call `next()` to move on
to the next middleware, allowing for stacking and fall-backs.
#### Options
##### dotfiles
Set how "dotfiles" are treated when encountered. A dotfile is a file
or directory that begins with a dot ("."). Note this check is done on
the path itself without checking if the path actually exists on the
disk. If `root` is specified, only the dotfiles above the root are
checked (i.e. the root itself can be within a dotfile when when set
to "deny").
The default value is `'ignore'`.
- `'allow'` No special treatment for dotfiles.
- `'deny'` Send a 403 for any request for a dotfile.
- `'ignore'` Pretend like the dotfile does not exist and call `next()`.
##### etag
Enable or disable etag generation, defaults to true.
##### index
By default this module will send "index.html" files in response to a request
on a directory. To disable this set `false` or to supply a new index pass a
string or an array in preferred order.
##### maxAge
Provide a max-age in milliseconds for http caching, defaults to 0. This
can also be a string accepted by the [ms](https://www.npmjs.org/package/ms#readme)
module.
##### redirect
Redirect to trailing "/" when the pathname is a dir. Defaults to `true`.
Options: ##### setHeaders
- `hidden` Allow transfer of hidden files. defaults to `false` Function to set custom headers on response.
- `index` Default file name, defaults to `'index.html'`
- `maxAge` Browser cache maxAge in milliseconds. This can also be a string accepted by the [ms](https://www.npmjs.org/package/ms#readme) module. defaults to `0`
- `redirect` Redirect to trailing "/" when the pathname is a dir. defaults to `true`
- `setHeaders` Function to set custom headers on response.
## Examples ## Examples
......
# Compiled source # benchmark/
################### coverage/
*.com test/
*.class .travis.yml
*.dll
*.exe
*.o
*.so
# Packages #
############
# it's better to unpack these files and commit the raw source
# git has its own built in compression methods
*.7z
*.dmg
*.gz
*.iso
*.jar
*.rar
*.tar
*.zip
# Logs and databases #
######################
*.log
*.sql
*.sqlite
# OS generated files #
######################
.DS_Store*
# Icon?
ehthumbs.db
Thumbs.db
# Node.js #
###########
lib-cov
*.seed
*.log
*.csv
*.dat
*.out
*.pid
*.gz
pids
logs
results
node_modules
npm-debug.log
# Components #
##############
/build
/components
/public
\ No newline at end of file
1.2.0 / 2014-07-21
==================
* Cache URLs based on original value
* Remove no-longer-needed URL mis-parse work-around
* Simplify the "fast-path" `RegExp`
1.1.3 / 2014-07-08 1.1.3 / 2014-07-08
================== ==================
......
(The MIT License)
Copyright (c) 2014 Jonathan Ong <me@jongleberry.com>
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
'Software'), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
# parseurl # parseurl
[![NPM version](https://badge.fury.io/js/parseurl.svg)](http://badge.fury.io/js/parseurl)
[![Build Status](https://travis-ci.org/expressjs/parseurl.svg?branch=master)](https://travis-ci.org/expressjs/parseurl)
[![Coverage Status](https://img.shields.io/coveralls/expressjs/parseurl.svg?branch=master)](https://coveralls.io/r/expressjs/parseurl)
Parse a URL with memoization. Parse a URL with memoization.
## Install
```bash
$ npm install parseurl
```
## API ## API
### var parsedUrl = parseurl(req) ```js
var parseurl = require('parseurl')
```
### parseurl(req)
Parse the URL of the given request object (looks at the `req.url` property)
and return the result. The result is the same as `url.parse` in Node.js core.
Calling this function multiple times on the same `req` where `req.url` does
not change will return a cached parsed object, rather than parsing again.
## Benchmark
```bash
$ npm run-script bench
> parseurl@1.2.0 bench nodejs-parseurl
> node benchmark/index.js
> node benchmark/fullurl.js
Parsing URL "http://localhost:8888/foo/bar?user=tj&pet=fluffy"
1 test completed.
2 tests completed.
3 tests completed.
fasturl x 1,290,780 ops/sec ±0.46% (195 runs sampled)
nativeurl x 56,401 ops/sec ±0.22% (196 runs sampled)
parseurl x 55,231 ops/sec ±0.22% (194 runs sampled)
> node benchmark/pathquery.js
Parsing URL "/foo/bar?user=tj&pet=fluffy"
1 test completed.
2 tests completed.
3 tests completed.
fasturl x 1,986,668 ops/sec ±0.27% (190 runs sampled)
nativeurl x 98,740 ops/sec ±0.21% (195 runs sampled)
parseurl x 2,628,171 ops/sec ±0.36% (195 runs sampled)
> node benchmark/samerequest.js
Parsing URL "/foo/bar?user=tj&pet=fluffy" on same request object
1 test completed.
2 tests completed.
3 tests completed.
fasturl x 2,184,468 ops/sec ±0.40% (194 runs sampled)
nativeurl x 99,437 ops/sec ±0.71% (194 runs sampled)
parseurl x 10,498,005 ops/sec ±0.61% (186 runs sampled)
> node benchmark/simplepath.js
Parsing URL "/foo/bar"
1 test completed.
2 tests completed.
3 tests completed.
`parsedUrl` is basically a `url.parse()` object. fasturl x 4,535,825 ops/sec ±0.27% (191 runs sampled)
nativeurl x 98,769 ops/sec ±0.54% (191 runs sampled)
parseurl x 4,164,865 ops/sec ±0.34% (192 runs sampled)
## LICENSE > node benchmark/slash.js
(The MIT License) Parsing URL "/"
Copyright (c) 2014 Jonathan Ong <me@jongleberry.com> 1 test completed.
2 tests completed.
3 tests completed.
Permission is hereby granted, free of charge, to any person obtaining fasturl x 4,908,405 ops/sec ±0.42% (191 runs sampled)
a copy of this software and associated documentation files (the nativeurl x 100,945 ops/sec ±0.59% (188 runs sampled)
'Software'), to deal in the Software without restriction, including parseurl x 4,333,208 ops/sec ±0.27% (194 runs sampled)
without limitation the rights to use, copy, modify, merge, publish, ```
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be ## License
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, [MIT](LICENSE)
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
...@@ -12,8 +12,7 @@ var Url = url.Url ...@@ -12,8 +12,7 @@ var Url = url.Url
* See: https://github.com/joyent/node/pull/7878 * See: https://github.com/joyent/node/pull/7878
*/ */
var simplePathRegExp = /^(\/\/?(?!\/)[^\?\s]*)(\?[^\s]*)?$/ var simplePathRegExp = /^(\/\/?(?!\/)[^\?#\s]*)(\?[^#\s]*)?$/
var tryFastRegExp = /^\/[^\\#]*$/
/** /**
* Parse the `req` url with memoization. * Parse the `req` url with memoization.
...@@ -25,17 +24,16 @@ var tryFastRegExp = /^\/[^\\#]*$/ ...@@ -25,17 +24,16 @@ var tryFastRegExp = /^\/[^\\#]*$/
module.exports = function parseUrl(req){ module.exports = function parseUrl(req){
var parsed = req._parsedUrl var parsed = req._parsedUrl
var url = req.url
if (fresh(req, parsed)) { if (fresh(url, parsed)) {
// Return cached URL parse
return parsed return parsed
} }
parsed = fastparse(req.url) // Parse the URL
parsed = fastparse(url)
if (parsed.auth && !parsed.protocol && parsed.href.indexOf('//') !== -1) { parsed._raw = url
// This parses pathnames, and a strange pathname like //r@e should work
parsed = fastparse(req.url.replace(/@/g, '%40'))
}
return req._parsedUrl = parsed return req._parsedUrl = parsed
}; };
...@@ -49,44 +47,41 @@ module.exports = function parseUrl(req){ ...@@ -49,44 +47,41 @@ module.exports = function parseUrl(req){
*/ */
function fastparse(str) { function fastparse(str) {
if (typeof str === 'string' && tryFastRegExp.test(str)) {
// Try fast path regexp // Try fast path regexp
// See: https://github.com/joyent/node/pull/7878 // See: https://github.com/joyent/node/pull/7878
var simplePath = simplePathRegExp.exec(str) var simplePath = typeof str === 'string' && simplePathRegExp.exec(str)
// Construct simple URL // Construct simple URL
if (simplePath) { if (simplePath) {
var pathname = simplePath[1]
var search = simplePath[2] || null
var url = Url !== undefined var url = Url !== undefined
? new Url() ? new Url()
: {} : {}
url.path = str url.path = str
url.href = str url.href = str
url.pathname = simplePath[1] url.pathname = pathname
url.search = search
if (simplePath[2]) { url.query = search && search.substr(1)
url.search = simplePath[2];
url.query = url.search.substr(1);
}
return url return url
} }
}
return parse(str) return parse(str)
} }
/** /**
* Determine if parsed is still fresh for req. * Determine if parsed is still fresh for url.
* *
* @param {ServerRequest} req * @param {string} url
* @param {object} parsedUrl * @param {object} parsedUrl
* @return {boolean} * @return {boolean}
* @api private * @api private
*/ */
function fresh(req, parsedUrl) { function fresh(url, parsedUrl) {
return typeof parsedUrl === 'object' return typeof parsedUrl === 'object'
&& parsedUrl !== null && parsedUrl !== null
&& (Url === undefined || parsedUrl instanceof Url) && (Url === undefined || parsedUrl instanceof Url)
&& parsedUrl.href === req.url && parsedUrl._raw === url
} }
{ {
"name": "parseurl", "name": "parseurl",
"description": "parse a url with memoization", "description": "parse a url with memoization",
"version": "1.1.3", "version": "1.2.0",
"author": { "author": {
"name": "Jonathan Ong", "name": "Jonathan Ong",
"email": "me@jongleberry.com", "email": "me@jongleberry.com",
"url": "http://jongleberry.com" "url": "http://jongleberry.com"
}, },
"contributors": [
{
"name": "Douglas Christopher Wilson",
"email": "doug@somethingdoug.com"
}
],
"repository": { "repository": {
"type": "git", "type": "git",
"url": "https://github.com/expressjs/parseurl.git" "url": "git://github.com/expressjs/parseurl"
},
"bugs": {
"url": "https://github.com/expressjs/parseurl/issues",
"email": "me@jongleberry.com"
}, },
"license": "MIT", "license": "MIT",
"readme": "# parseurl\n\nParse a URL with memoization.\n\n## API\n\n### var parsedUrl = parseurl(req)\n\n`parsedUrl` is basically a `url.parse()` object.\n\n## LICENSE\n\n(The MIT License)\n\nCopyright (c) 2014 Jonathan Ong <me@jongleberry.com>\n\nPermission is hereby granted, free of charge, to any person obtaining\na copy of this software and associated documentation files (the\n'Software'), to deal in the Software without restriction, including\nwithout limitation the rights to use, copy, modify, merge, publish,\ndistribute, sublicense, and/or sell copies of the Software, and to\npermit persons to whom the Software is furnished to do so, subject to\nthe following conditions:\n\nThe above copyright notice and this permission notice shall be\nincluded in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\nIN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\nTORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\nSOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n", "devDependencies": {
"benchmark": "1.0.0",
"beautify-benchmark": "0.2.4",
"fast-url-parser": "~1.0.0",
"istanbul": "0.3.0",
"mocha": "~1.20.0"
},
"scripts": {
"bench": "node benchmark/index.js",
"test": "mocha --check-leaks --bail --reporter spec test/",
"test-cov": "istanbul cover node_modules/mocha/bin/_mocha -- --check-leaks --reporter dot test/",
"test-travis": "istanbul cover node_modules/mocha/bin/_mocha --report lcovonly -- --check-leaks --reporter spec test/"
},
"readme": "# parseurl\n\n[![NPM version](https://badge.fury.io/js/parseurl.svg)](http://badge.fury.io/js/parseurl)\n[![Build Status](https://travis-ci.org/expressjs/parseurl.svg?branch=master)](https://travis-ci.org/expressjs/parseurl)\n[![Coverage Status](https://img.shields.io/coveralls/expressjs/parseurl.svg?branch=master)](https://coveralls.io/r/expressjs/parseurl)\n\nParse a URL with memoization.\n\n## Install\n\n```bash\n$ npm install parseurl\n```\n\n## API\n\n```js\nvar parseurl = require('parseurl')\n```\n\n### parseurl(req)\n\nParse the URL of the given request object (looks at the `req.url` property)\nand return the result. The result is the same as `url.parse` in Node.js core.\nCalling this function multiple times on the same `req` where `req.url` does\nnot change will return a cached parsed object, rather than parsing again.\n\n## Benchmark\n\n```bash\n$ npm run-script bench\n\n> parseurl@1.2.0 bench nodejs-parseurl\n> node benchmark/index.js\n\n> node benchmark/fullurl.js\n\n Parsing URL \"http://localhost:8888/foo/bar?user=tj&pet=fluffy\"\n\n 1 test completed.\n 2 tests completed.\n 3 tests completed.\n\n fasturl x 1,290,780 ops/sec ±0.46% (195 runs sampled)\n nativeurl x 56,401 ops/sec ±0.22% (196 runs sampled)\n parseurl x 55,231 ops/sec ±0.22% (194 runs sampled)\n\n> node benchmark/pathquery.js\n\n Parsing URL \"/foo/bar?user=tj&pet=fluffy\"\n\n 1 test completed.\n 2 tests completed.\n 3 tests completed.\n\n fasturl x 1,986,668 ops/sec ±0.27% (190 runs sampled)\n nativeurl x 98,740 ops/sec ±0.21% (195 runs sampled)\n parseurl x 2,628,171 ops/sec ±0.36% (195 runs sampled)\n\n> node benchmark/samerequest.js\n\n Parsing URL \"/foo/bar?user=tj&pet=fluffy\" on same request object\n\n 1 test completed.\n 2 tests completed.\n 3 tests completed.\n\n fasturl x 2,184,468 ops/sec ±0.40% (194 runs sampled)\n nativeurl x 99,437 ops/sec ±0.71% (194 runs sampled)\n parseurl x 10,498,005 ops/sec ±0.61% (186 runs sampled)\n\n> node benchmark/simplepath.js\n\n Parsing URL \"/foo/bar\"\n\n 1 test completed.\n 2 tests completed.\n 3 tests completed.\n\n fasturl x 4,535,825 ops/sec ±0.27% (191 runs sampled)\n nativeurl x 98,769 ops/sec ±0.54% (191 runs sampled)\n parseurl x 4,164,865 ops/sec ±0.34% (192 runs sampled)\n\n> node benchmark/slash.js\n\n Parsing URL \"/\"\n\n 1 test completed.\n 2 tests completed.\n 3 tests completed.\n\n fasturl x 4,908,405 ops/sec ±0.42% (191 runs sampled)\n nativeurl x 100,945 ops/sec ±0.59% (188 runs sampled)\n parseurl x 4,333,208 ops/sec ±0.27% (194 runs sampled)\n```\n\n## License\n\n [MIT](LICENSE)\n",
"readmeFilename": "README.md", "readmeFilename": "README.md",
"_id": "parseurl@1.1.3", "bugs": {
"_from": "parseurl@~1.1.3" "url": "https://github.com/expressjs/parseurl/issues"
},
"_id": "parseurl@1.2.0",
"_from": "parseurl@~1.2.0"
} }
0.7.0 / 2014-07-20
==================
* Deprecate `hidden` option; use `dotfiles` option
* Add `dotfiles` option
* deps: debug@1.0.4
* deps: depd@0.4.2
- Add `TRACE_DEPRECATION` environment variable
- Remove non-standard grey color from color output
- Support `--no-deprecation` argument
- Support `--trace-deprecation` argument
0.6.0 / 2014-07-11
==================
* Deprecate `from` option; use `root` option
* Deprecate `send.etag()` -- use `etag` in `options`
* Deprecate `send.hidden()` -- use `hidden` in `options`
* Deprecate `send.index()` -- use `index` in `options`
* Deprecate `send.maxage()` -- use `maxAge` in `options`
* Deprecate `send.root()` -- use `root` in `options`
* Cap `maxAge` value to 1 year
* deps: debug@1.0.3
- Add support for multiple wildcards in namespaces
0.5.0 / 2014-06-28 0.5.0 / 2014-06-28
================== ==================
......
...@@ -69,9 +69,20 @@ var app = http.createServer(function(req, res){ ...@@ -69,9 +69,20 @@ var app = http.createServer(function(req, res){
Enable or disable etag generation, defaults to true. Enable or disable etag generation, defaults to true.
#### hidden #### dotfiles
Enable or disable transfer of hidden files, defaults to false. Set how "dotfiles" are treated when encountered. A dotfile is a file
or directory that begins with a dot ("."). Note this check is done on
the path itself without checking if the path actually exists on the
disk. If `root` is specified, only the dotfiles above the root are
checked (i.e. the root itself can be within a dotfile when when set
to "deny").
The default value is `'ignore'`.
- `'allow'` No special treatment for dotfiles.
- `'deny'` Send a 403 for any request for a dotfile.
- `'ignore'` Pretend like the dotfile does not exist and 404.
#### index #### index
...@@ -79,7 +90,7 @@ var app = http.createServer(function(req, res){ ...@@ -79,7 +90,7 @@ var app = http.createServer(function(req, res){
set `false` or to supply a new index pass a string or an array set `false` or to supply a new index pass a string or an array
in preferred order. in preferred order.
#### maxage #### maxAge
Provide a max-age in milliseconds for http caching, defaults to 0. Provide a max-age in milliseconds for http caching, defaults to 0.
This can also be a string accepted by the This can also be a string accepted by the
...@@ -98,30 +109,6 @@ var app = http.createServer(function(req, res){ ...@@ -98,30 +109,6 @@ var app = http.createServer(function(req, res){
- `stream` file streaming has started `(stream)` - `stream` file streaming has started `(stream)`
- `end` streaming has completed - `end` streaming has completed
### .etag(bool)
Enable or disable etag generation, defaults to true.
### .root(dir)
Serve files relative to `path`. Aliased as `.from(dir)`.
### .index(paths)
By default send supports "index.html" files, to disable this
invoke `.index(false)` or to supply a new index pass a string
or an array in preferred order.
### .maxage(ms)
Provide a max-age in milliseconds for http caching, defaults to 0.
This can also be a string accepted by the
[ms](https://www.npmjs.org/package/ms#readme) module.
### .hidden(bool)
Enable or disable transfer of hidden files, defaults to false.
## Error-handling ## Error-handling
By default when no `error` listeners are present an automatic response will be made, otherwise you have full control over the response, aka you may show a 5xx page etc. By default when no `error` listeners are present an automatic response will be made, otherwise you have full control over the response, aka you may show a 5xx page etc.
......
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
*/ */
var debug = require('debug')('send') var debug = require('debug')('send')
var deprecate = require('depd')('send')
var escapeHtml = require('escape-html') var escapeHtml = require('escape-html')
, parseRange = require('range-parser') , parseRange = require('range-parser')
, Stream = require('stream') , Stream = require('stream')
...@@ -13,14 +14,19 @@ var escapeHtml = require('escape-html') ...@@ -13,14 +14,19 @@ var escapeHtml = require('escape-html')
, http = require('http') , http = require('http')
, onFinished = require('finished') , onFinished = require('finished')
, fs = require('fs') , fs = require('fs')
, basename = path.basename
, normalize = path.normalize , normalize = path.normalize
, join = path.join , join = path.join
, utils = require('./utils'); , utils = require('./utils');
var EventEmitter = require('events').EventEmitter; var EventEmitter = require('events').EventEmitter;
var ms = require('ms'); var ms = require('ms');
var upPathRegexp = /(?:^|[\\\/])\.\.(?:[\\\/]|$)/; /**
* Variables.
*/
var maxMaxAge = 60 * 60 * 24 * 365 * 1000; // 1 year
var resolve = path.resolve
var sep = path.sep
var upPathRegexp = /(?:^|[\\\/])\.\.(?:[\\\/]|$)/
/** /**
* Expose `send`. * Expose `send`.
...@@ -59,13 +65,6 @@ function send(req, path, options) { ...@@ -59,13 +65,6 @@ function send(req, path, options) {
/** /**
* Initialize a `SendStream` with the given `path`. * Initialize a `SendStream` with the given `path`.
* *
* Events:
*
* - `error` an error occurred
* - `stream` file streaming has started
* - `end` streaming has completed
* - `directory` a directory was requested
*
* @param {Request} req * @param {Request} req
* @param {String} path * @param {String} path
* @param {Object} options * @param {Object} options
...@@ -78,11 +77,49 @@ function SendStream(req, path, options) { ...@@ -78,11 +77,49 @@ function SendStream(req, path, options) {
this.req = req; this.req = req;
this.path = path; this.path = path;
this.options = options; this.options = options;
this.etag(('etag' in options) ? options.etag : true);
this.maxage(options.maxage); this._etag = options.etag !== undefined
this.hidden(options.hidden); ? Boolean(options.etag)
this.index(('index' in options) ? options.index : 'index.html'); : true
if (options.root || options.from) this.root(options.root || options.from);
this._dotfiles = options.dotfiles !== undefined
? options.dotfiles
: 'ignore'
if (['allow', 'deny', 'ignore'].indexOf(this._dotfiles) === -1) {
throw new TypeError('dotfiles option must be "allow", "deny", or "ignore"')
}
this._hidden = Boolean(options.hidden)
if ('hidden' in options) {
deprecate('hidden: use dotfiles: \'' + (this._hidden ? 'allow' : 'ignore') + '\' instead')
}
// legacy support
if (!('dotfiles' in options)) {
this._dotfiles = undefined
}
this._index = options.index !== undefined
? normalizeIndex(options.index)
: ['index.html']
this._maxage = options.maxAge || options.maxage
this._maxage = typeof this._maxage === 'string'
? ms(this._maxage)
: Number(this._maxage)
this._maxage = !isNaN(this._maxage)
? Math.min(Math.max(0, this._maxage), maxMaxAge)
: 0
this._root = options.root
? resolve(options.root)
: null
if (!this._root && options.from) {
this.from(options.from);
}
} }
/** /**
...@@ -99,12 +136,12 @@ SendStream.prototype.__proto__ = Stream.prototype; ...@@ -99,12 +136,12 @@ SendStream.prototype.__proto__ = Stream.prototype;
* @api public * @api public
*/ */
SendStream.prototype.etag = function(val){ SendStream.prototype.etag = deprecate.function(function etag(val) {
val = Boolean(val); val = Boolean(val);
debug('etag %s', val); debug('etag %s', val);
this._etag = val; this._etag = val;
return this; return this;
}; }, 'send.etag: pass etag as option');
/** /**
* Enable or disable "hidden" (dot) files. * Enable or disable "hidden" (dot) files.
...@@ -114,12 +151,13 @@ SendStream.prototype.etag = function(val){ ...@@ -114,12 +151,13 @@ SendStream.prototype.etag = function(val){
* @api public * @api public
*/ */
SendStream.prototype.hidden = function(val){ SendStream.prototype.hidden = deprecate.function(function hidden(val) {
val = Boolean(val); val = Boolean(val);
debug('hidden %s', val); debug('hidden %s', val);
this._hidden = val; this._hidden = val;
this._dotfiles = undefined
return this; return this;
}; }, 'send.hidden: use dotfiles option');
/** /**
* Set index `paths`, set to a falsy * Set index `paths`, set to a falsy
...@@ -130,12 +168,12 @@ SendStream.prototype.hidden = function(val){ ...@@ -130,12 +168,12 @@ SendStream.prototype.hidden = function(val){
* @api public * @api public
*/ */
SendStream.prototype.index = function index(paths){ SendStream.prototype.index = deprecate.function(function index(paths) {
var index = !paths ? [] : Array.isArray(paths) ? paths : [paths]; var index = !paths ? [] : normalizeIndex(paths);
debug('index %o', paths); debug('index %o', paths);
this._index = index; this._index = index;
return this; return this;
}; }, 'send.index: pass index as option');
/** /**
* Set root `path`. * Set root `path`.
...@@ -145,13 +183,18 @@ SendStream.prototype.index = function index(paths){ ...@@ -145,13 +183,18 @@ SendStream.prototype.index = function index(paths){
* @api public * @api public
*/ */
SendStream.prototype.root = SendStream.prototype.root = function(path){
SendStream.prototype.from = function(path){
path = String(path); path = String(path);
this._root = normalize(path); this._root = resolve(path)
return this; return this;
}; };
SendStream.prototype.from = deprecate.function(SendStream.prototype.root,
'send.from: pass root as option');
SendStream.prototype.root = deprecate.function(SendStream.prototype.root,
'send.root: pass root as option');
/** /**
* Set max-age to `maxAge`. * Set max-age to `maxAge`.
* *
...@@ -160,7 +203,7 @@ SendStream.prototype.from = function(path){ ...@@ -160,7 +203,7 @@ SendStream.prototype.from = function(path){
* @api public * @api public
*/ */
SendStream.prototype.maxage = function maxage(maxAge){ SendStream.prototype.maxage = deprecate.function(function maxage(maxAge) {
maxAge = typeof maxAge === 'string' maxAge = typeof maxAge === 'string'
? ms(maxAge) ? ms(maxAge)
: Number(maxAge); : Number(maxAge);
...@@ -169,7 +212,7 @@ SendStream.prototype.maxage = function maxage(maxAge){ ...@@ -169,7 +212,7 @@ SendStream.prototype.maxage = function maxage(maxAge){
debug('max-age %d', maxAge); debug('max-age %d', maxAge);
this._maxage = maxAge; this._maxage = maxAge;
return this; return this;
}; }, 'send.maxage: pass maxAge as option');
/** /**
* Emit error with `status`. * Emit error with `status`.
...@@ -197,17 +240,6 @@ SendStream.prototype.error = function(status, err){ ...@@ -197,17 +240,6 @@ SendStream.prototype.error = function(status, err){
res.end(msg); res.end(msg);
}; };
/**
* Check if the pathname is potentially malicious.
*
* @return {Boolean}
* @api private
*/
SendStream.prototype.isMalicious = function(){
return !this._root && ~this.path.indexOf('..') && upPathRegexp.test(this.path);
};
/** /**
* Check if the pathname ends with "/". * Check if the pathname ends with "/".
* *
...@@ -219,17 +251,6 @@ SendStream.prototype.hasTrailingSlash = function(){ ...@@ -219,17 +251,6 @@ SendStream.prototype.hasTrailingSlash = function(){
return '/' == this.path[this.path.length - 1]; return '/' == this.path[this.path.length - 1];
}; };
/**
* Check if the basename leads with ".".
*
* @return {Boolean}
* @api private
*/
SendStream.prototype.hasLeadingDot = function(){
return '.' == basename(this.path)[0];
};
/** /**
* Check if this is a conditional GET request. * Check if this is a conditional GET request.
* *
...@@ -369,30 +390,67 @@ SendStream.prototype.redirect = function(path){ ...@@ -369,30 +390,67 @@ SendStream.prototype.redirect = function(path){
SendStream.prototype.pipe = function(res){ SendStream.prototype.pipe = function(res){
var self = this var self = this
, args = arguments , args = arguments
, path = this.path
, root = this._root; , root = this._root;
// references // references
this.res = res; this.res = res;
// invalid request uri // decode the path
path = utils.decode(path); var path = utils.decode(this.path)
if (-1 == path) return this.error(400); if (path === -1) return this.error(400)
// null byte(s) // null byte(s)
if (~path.indexOf('\0')) return this.error(400); if (~path.indexOf('\0')) return this.error(400);
var parts
if (root !== null) {
// join / normalize from optional root dir // join / normalize from optional root dir
if (root) path = normalize(join(this._root, path)); path = normalize(join(root, path))
// malicious path
if (path.substr(0, root.length) !== root) {
debug('malicious path "%s"', path)
return this.error(403)
}
// explode path parts
parts = path.substr(root.length + 1).split(sep)
} else {
// ".." is malicious without "root" // ".." is malicious without "root"
if (this.isMalicious()) return this.error(403); if (upPathRegexp.test(path)) {
debug('malicious path "%s"', path)
return this.error(403)
}
// malicious path // explode path parts
if (root && 0 != path.indexOf(root)) return this.error(403); parts = normalize(path).split(sep)
// resolve the path
path = resolve(path)
}
// hidden file support // dotfile handling
if (!this._hidden && this.hasLeadingDot()) return this.error(404); if (containsDotFile(parts)) {
var access = this._dotfiles
// legacy support
if (access === undefined) {
access = parts[parts.length - 1][0] === '.'
? (this._hidden ? 'allow' : 'ignore')
: 'allow'
}
debug('%s dotfile "%s"', access, path)
switch (access) {
case 'allow':
break
case 'deny':
return this.error(403)
case 'ignore':
default:
return this.error(404)
}
}
// index file support // index file support
if (this._index.length && this.hasTrailingSlash()) { if (this._index.length && this.hasTrailingSlash()) {
...@@ -431,6 +489,8 @@ SendStream.prototype.send = function(path, stat){ ...@@ -431,6 +489,8 @@ SendStream.prototype.send = function(path, stat){
return this.headersAlreadySent(); return this.headersAlreadySent();
} }
debug('pipe "%s"', path)
// set header fields // set header fields
this.setHeader(path, stat); this.setHeader(path, stat);
...@@ -622,3 +682,30 @@ SendStream.prototype.setHeader = function setHeader(path, stat){ ...@@ -622,3 +682,30 @@ SendStream.prototype.setHeader = function setHeader(path, stat){
res.setHeader('ETag', etag); res.setHeader('ETag', etag);
} }
}; };
/**
* Determine if path parts contain a dotfile.
*
* @api private
*/
function containsDotFile(parts) {
for (var i = 0; i < parts.length; i++) {
if (parts[i][0] === '.') {
return true
}
}
return false
}
/**
* Normalize the index option into an array.
*
* @param {boolean|string|array} val
* @api private
*/
function normalizeIndex(val){
return [].concat(val || [])
}
1.0.4 / 2014-07-15
==================
* dist: recompile
* example: remove `console.info()` log usage
* example: add "Content-Type" UTF-8 header to browser example
* browser: place %c marker after the space character
* browser: reset the "content" color via `color: inherit`
* browser: add colors support for Firefox >= v31
* debug: prefer an instance `log()` function over the global one (#119)
* Readme: update documentation about styled console logs for FF v31 (#116, @wryk)
1.0.3 / 2014-07-09
==================
* Add support for multiple wildcards in namespaces (#122, @seegno)
* browser: fix lint
1.0.2 / 2014-06-10 1.0.2 / 2014-06-10
================== ==================
......
...@@ -91,7 +91,10 @@ setInterval(function(){ ...@@ -91,7 +91,10 @@ setInterval(function(){
#### Web Inspector Colors #### Web Inspector Colors
Colors are also enabled on "Web Inspectors" that understand the `%c` formatting Colors are also enabled on "Web Inspectors" that understand the `%c` formatting
option. These are WebKit web inspectors, and the Firebug plugin for Firefox. option. These are WebKit web inspectors, Firefox ([since version
31](https://hacks.mozilla.org/2014/05/editable-box-model-multiple-selection-sublime-text-keys-much-more-firefox-developer-tools-episode-31/))
and the Firebug plugin for Firefox (any version).
Colored output looks something like: Colored output looks something like:
![](https://cloud.githubusercontent.com/assets/71256/3139768/b98c5fd8-e8ef-11e3-862a-f7253b6f47c6.png) ![](https://cloud.githubusercontent.com/assets/71256/3139768/b98c5fd8-e8ef-11e3-862a-f7253b6f47c6.png)
......
...@@ -26,9 +26,9 @@ exports.colors = [ ...@@ -26,9 +26,9 @@ exports.colors = [
]; ];
/** /**
* Currently only WebKit-based Web Inspectors and the Firebug * Currently only WebKit-based Web Inspectors, Firefox >= v31,
* extension (*not* the built-in Firefox web inpector) are * and the Firebug extension (any Firefox version) are known
* known to support "%c" CSS customizations. * to support "%c" CSS customizations.
* *
* TODO: add a `localStorage` variable to explicitly enable/disable colors * TODO: add a `localStorage` variable to explicitly enable/disable colors
*/ */
...@@ -37,7 +37,10 @@ function useColors() { ...@@ -37,7 +37,10 @@ function useColors() {
// is webkit? http://stackoverflow.com/a/16459606/376773 // is webkit? http://stackoverflow.com/a/16459606/376773
return ('WebkitAppearance' in document.documentElement.style) || return ('WebkitAppearance' in document.documentElement.style) ||
// is firebug? http://stackoverflow.com/a/398120/376773 // is firebug? http://stackoverflow.com/a/398120/376773
(window.console && (console.firebug || (console.exception && console.table))); (window.console && (console.firebug || (console.exception && console.table))) ||
// is firefox >= v31?
// https://developer.mozilla.org/en-US/docs/Tools/Web_Console#Styling_messages
(navigator.userAgent.toLowerCase().match(/firefox\/(\d+)/) && parseInt(RegExp.$1, 10) >= 31);
} }
/** /**
...@@ -61,15 +64,15 @@ function formatArgs() { ...@@ -61,15 +64,15 @@ function formatArgs() {
args[0] = (useColors ? '%c' : '') args[0] = (useColors ? '%c' : '')
+ this.namespace + this.namespace
+ (useColors ? '%c ' : ' ') + (useColors ? ' %c' : ' ')
+ args[0] + args[0]
+ (useColors ? '%c ' : ' ') + (useColors ? '%c ' : ' ')
+ '+' + exports.humanize(this.diff); + '+' + exports.humanize(this.diff);
if (!useColors) return args if (!useColors) return args;
var c = 'color: ' + this.color; var c = 'color: ' + this.color;
args = [args[0], c, ''].concat(Array.prototype.slice.call(args, 1)); args = [args[0], c, 'color: inherit'].concat(Array.prototype.slice.call(args, 1));
// the final "%c" is somewhat tricky, because there could be other // the final "%c" is somewhat tricky, because there could be other
// arguments passed either before or after the %c, so we need to // arguments passed either before or after the %c, so we need to
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
"name": "debug", "name": "debug",
"repo": "visionmedia/debug", "repo": "visionmedia/debug",
"description": "small debugging utility", "description": "small debugging utility",
"version": "1.0.2", "version": "1.0.4",
"keywords": [ "keywords": [
"debug", "debug",
"log", "log",
......
...@@ -113,7 +113,7 @@ function debug(namespace) { ...@@ -113,7 +113,7 @@ function debug(namespace) {
if ('function' === typeof exports.formatArgs) { if ('function' === typeof exports.formatArgs) {
args = exports.formatArgs.apply(self, args); args = exports.formatArgs.apply(self, args);
} }
var logFn = exports.log || enabled.log || console.log.bind(console); var logFn = enabled.log || exports.log || console.log.bind(console);
logFn.apply(self, args); logFn.apply(self, args);
} }
enabled.enabled = true; enabled.enabled = true;
...@@ -141,7 +141,7 @@ function enable(namespaces) { ...@@ -141,7 +141,7 @@ function enable(namespaces) {
for (var i = 0; i < len; i++) { for (var i = 0; i < len; i++) {
if (!split[i]) continue; // ignore empty strings if (!split[i]) continue; // ignore empty strings
namespaces = split[i].replace('*', '.*?'); namespaces = split[i].replace(/\*/g, '.*?');
if (namespaces[0] === '-') { if (namespaces[0] === '-') {
exports.skips.push(new RegExp('^' + namespaces.substr(1) + '$')); exports.skips.push(new RegExp('^' + namespaces.substr(1) + '$'));
} else { } else {
......
{ {
"name": "debug", "name": "debug",
"version": "1.0.2", "version": "1.0.4",
"repository": { "repository": {
"type": "git", "type": "git",
"url": "git://github.com/visionmedia/debug.git" "url": "git://github.com/visionmedia/debug.git"
...@@ -37,11 +37,11 @@ ...@@ -37,11 +37,11 @@
"debug/debug.js": "debug.js" "debug/debug.js": "debug.js"
} }
}, },
"readme": "# debug\n\n tiny node.js debugging utility modelled after node core's debugging technique.\n\n## Installation\n\n```bash\n$ npm install debug\n```\n\n## Usage\n\n With `debug` you simply invoke the exported function to generate your debug function, passing it a name which will determine if a noop function is returned, or a decorated `console.error`, so all of the `console` format string goodies you're used to work fine. A unique color is selected per-function for visibility.\n\nExample _app.js_:\n\n```js\nvar debug = require('debug')('http')\n , http = require('http')\n , name = 'My App';\n\n// fake app\n\ndebug('booting %s', name);\n\nhttp.createServer(function(req, res){\n debug(req.method + ' ' + req.url);\n res.end('hello\\n');\n}).listen(3000, function(){\n debug('listening');\n});\n\n// fake worker of some kind\n\nrequire('./worker');\n```\n\nExample _worker.js_:\n\n```js\nvar debug = require('debug')('worker');\n\nsetInterval(function(){\n debug('doing some work');\n}, 1000);\n```\n\n The __DEBUG__ environment variable is then used to enable these based on space or comma-delimited names. Here are some examples:\n\n ![debug http and worker](http://f.cl.ly/items/18471z1H402O24072r1J/Screenshot.png)\n\n ![debug worker](http://f.cl.ly/items/1X413v1a3M0d3C2c1E0i/Screenshot.png)\n\n## Millisecond diff\n\n When actively developing an application it can be useful to see when the time spent between one `debug()` call and the next. Suppose for example you invoke `debug()` before requesting a resource, and after as well, the \"+NNNms\" will show you how much time was spent between calls.\n\n ![](http://f.cl.ly/items/2i3h1d3t121M2Z1A3Q0N/Screenshot.png)\n\n When stdout is not a TTY, `Date#toUTCString()` is used, making it more useful for logging the debug information as shown below:\n\n ![](http://f.cl.ly/items/112H3i0e0o0P0a2Q2r11/Screenshot.png)\n\n## Conventions\n\n If you're using this in one or more of your libraries, you _should_ use the name of your library so that developers may toggle debugging as desired without guessing names. If you have more than one debuggers you _should_ prefix them with your library name and use \":\" to separate features. For example \"bodyParser\" from Connect would then be \"connect:bodyParser\".\n\n## Wildcards\n\n The `*` character may be used as a wildcard. Suppose for example your library has debuggers named \"connect:bodyParser\", \"connect:compress\", \"connect:session\", instead of listing all three with `DEBUG=connect:bodyParser,connect.compress,connect:session`, you may simply do `DEBUG=connect:*`, or to run everything using this module simply use `DEBUG=*`.\n\n You can also exclude specific debuggers by prefixing them with a \"-\" character. For example, `DEBUG=*,-connect:*` would include all debuggers except those starting with \"connect:\".\n\n## Browser support\n\n Debug works in the browser as well, currently persisted by `localStorage`. For example if you have `worker:a` and `worker:b` as shown below, and wish to debug both type `debug.enable('worker:*')` in the console and refresh the page, this will remain until you disable with `debug.disable()`.\n\n```js\na = debug('worker:a');\nb = debug('worker:b');\n\nsetInterval(function(){\n a('doing some work');\n}, 1000);\n\nsetInterval(function(){\n b('doing some work');\n}, 1200);\n```\n\n#### Web Inspector Colors\n\n Colors are also enabled on \"Web Inspectors\" that understand the `%c` formatting\n option. These are WebKit web inspectors, and the Firebug plugin for Firefox.\n Colored output looks something like:\n\n ![](https://cloud.githubusercontent.com/assets/71256/3139768/b98c5fd8-e8ef-11e3-862a-f7253b6f47c6.png)\n\n### stderr vs stdout\n\nYou can set an alternative logging method per-namespace by overriding the `log` method on a per-namespace or globally:\n\nExample _stderr.js_:\n\n```js\nvar debug = require('../');\nvar log = debug('app:log');\n\n// by default console.log is used\nlog('goes to stdout!');\n\nvar error = debug('app:error');\n// set this namespace to log via console.error\nerror.log = console.error.bind(console); // don't forget to bind to console!\nerror('goes to stderr');\nlog('still goes to stdout!');\n\n// set all output to go via console.warn\n// overrides all per-namespace log settings\ndebug.log = console.warn.bind(console);\nlog('now goes to stderr via console.warn');\nerror('still goes to stderr, but via console.warn now');\n```\n\n## Authors\n\n - TJ Holowaychuk\n - Nathan Rajlich\n\n## License\n\n(The MIT License)\n\nCopyright (c) 2014 TJ Holowaychuk &lt;tj@vision-media.ca&gt;\n\nPermission is hereby granted, free of charge, to any person obtaining\na copy of this software and associated documentation files (the\n'Software'), to deal in the Software without restriction, including\nwithout limitation the rights to use, copy, modify, merge, publish,\ndistribute, sublicense, and/or sell copies of the Software, and to\npermit persons to whom the Software is furnished to do so, subject to\nthe following conditions:\n\nThe above copyright notice and this permission notice shall be\nincluded in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\nIN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\nTORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\nSOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n", "readme": "# debug\n\n tiny node.js debugging utility modelled after node core's debugging technique.\n\n## Installation\n\n```bash\n$ npm install debug\n```\n\n## Usage\n\n With `debug` you simply invoke the exported function to generate your debug function, passing it a name which will determine if a noop function is returned, or a decorated `console.error`, so all of the `console` format string goodies you're used to work fine. A unique color is selected per-function for visibility.\n\nExample _app.js_:\n\n```js\nvar debug = require('debug')('http')\n , http = require('http')\n , name = 'My App';\n\n// fake app\n\ndebug('booting %s', name);\n\nhttp.createServer(function(req, res){\n debug(req.method + ' ' + req.url);\n res.end('hello\\n');\n}).listen(3000, function(){\n debug('listening');\n});\n\n// fake worker of some kind\n\nrequire('./worker');\n```\n\nExample _worker.js_:\n\n```js\nvar debug = require('debug')('worker');\n\nsetInterval(function(){\n debug('doing some work');\n}, 1000);\n```\n\n The __DEBUG__ environment variable is then used to enable these based on space or comma-delimited names. Here are some examples:\n\n ![debug http and worker](http://f.cl.ly/items/18471z1H402O24072r1J/Screenshot.png)\n\n ![debug worker](http://f.cl.ly/items/1X413v1a3M0d3C2c1E0i/Screenshot.png)\n\n## Millisecond diff\n\n When actively developing an application it can be useful to see when the time spent between one `debug()` call and the next. Suppose for example you invoke `debug()` before requesting a resource, and after as well, the \"+NNNms\" will show you how much time was spent between calls.\n\n ![](http://f.cl.ly/items/2i3h1d3t121M2Z1A3Q0N/Screenshot.png)\n\n When stdout is not a TTY, `Date#toUTCString()` is used, making it more useful for logging the debug information as shown below:\n\n ![](http://f.cl.ly/items/112H3i0e0o0P0a2Q2r11/Screenshot.png)\n\n## Conventions\n\n If you're using this in one or more of your libraries, you _should_ use the name of your library so that developers may toggle debugging as desired without guessing names. If you have more than one debuggers you _should_ prefix them with your library name and use \":\" to separate features. For example \"bodyParser\" from Connect would then be \"connect:bodyParser\".\n\n## Wildcards\n\n The `*` character may be used as a wildcard. Suppose for example your library has debuggers named \"connect:bodyParser\", \"connect:compress\", \"connect:session\", instead of listing all three with `DEBUG=connect:bodyParser,connect.compress,connect:session`, you may simply do `DEBUG=connect:*`, or to run everything using this module simply use `DEBUG=*`.\n\n You can also exclude specific debuggers by prefixing them with a \"-\" character. For example, `DEBUG=*,-connect:*` would include all debuggers except those starting with \"connect:\".\n\n## Browser support\n\n Debug works in the browser as well, currently persisted by `localStorage`. For example if you have `worker:a` and `worker:b` as shown below, and wish to debug both type `debug.enable('worker:*')` in the console and refresh the page, this will remain until you disable with `debug.disable()`.\n\n```js\na = debug('worker:a');\nb = debug('worker:b');\n\nsetInterval(function(){\n a('doing some work');\n}, 1000);\n\nsetInterval(function(){\n b('doing some work');\n}, 1200);\n```\n\n#### Web Inspector Colors\n\n Colors are also enabled on \"Web Inspectors\" that understand the `%c` formatting\n option. These are WebKit web inspectors, Firefox ([since version\n 31](https://hacks.mozilla.org/2014/05/editable-box-model-multiple-selection-sublime-text-keys-much-more-firefox-developer-tools-episode-31/))\n and the Firebug plugin for Firefox (any version).\n\n Colored output looks something like:\n\n ![](https://cloud.githubusercontent.com/assets/71256/3139768/b98c5fd8-e8ef-11e3-862a-f7253b6f47c6.png)\n\n### stderr vs stdout\n\nYou can set an alternative logging method per-namespace by overriding the `log` method on a per-namespace or globally:\n\nExample _stderr.js_:\n\n```js\nvar debug = require('../');\nvar log = debug('app:log');\n\n// by default console.log is used\nlog('goes to stdout!');\n\nvar error = debug('app:error');\n// set this namespace to log via console.error\nerror.log = console.error.bind(console); // don't forget to bind to console!\nerror('goes to stderr');\nlog('still goes to stdout!');\n\n// set all output to go via console.warn\n// overrides all per-namespace log settings\ndebug.log = console.warn.bind(console);\nlog('now goes to stderr via console.warn');\nerror('still goes to stderr, but via console.warn now');\n```\n\n## Authors\n\n - TJ Holowaychuk\n - Nathan Rajlich\n\n## License\n\n(The MIT License)\n\nCopyright (c) 2014 TJ Holowaychuk &lt;tj@vision-media.ca&gt;\n\nPermission is hereby granted, free of charge, to any person obtaining\na copy of this software and associated documentation files (the\n'Software'), to deal in the Software without restriction, including\nwithout limitation the rights to use, copy, modify, merge, publish,\ndistribute, sublicense, and/or sell copies of the Software, and to\npermit persons to whom the Software is furnished to do so, subject to\nthe following conditions:\n\nThe above copyright notice and this permission notice shall be\nincluded in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\nIN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\nTORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\nSOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n",
"readmeFilename": "Readme.md", "readmeFilename": "Readme.md",
"bugs": { "bugs": {
"url": "https://github.com/visionmedia/debug/issues" "url": "https://github.com/visionmedia/debug/issues"
}, },
"_id": "debug@1.0.2", "_id": "debug@1.0.4",
"_from": "debug@1.0.2" "_from": "debug@1.0.4"
} }
0.4.2 / 2014-07-19
==================
* Correct call site for wrapped functions and properties
0.4.1 / 2014-07-19
==================
* Improve automatic message generation for function properties
0.4.0 / 2014-07-19
==================
* Add `TRACE_DEPRECATION` environment variable
* Remove non-standard grey color from color output
* Support `--no-deprecation` argument
* Support `--trace-deprecation` argument
* Support `deprecate.property(fn, prop, message)`
0.3.0 / 2014-06-16
==================
* Add `NO_DEPRECATION` environment variable
0.2.0 / 2014-06-15
==================
* Add `deprecate.property(obj, prop, message)`
* Remove `supports-color` dependency for node.js 0.8
0.1.0 / 2014-06-15
==================
* Add `deprecate.function(fn, message)`
* Add `process.on('deprecation', fn)` emitter
* Automatically generate message when omitted from `deprecate()`
0.0.1 / 2014-06-15
==================
* Fix warning for dynamic calls at singe call site
0.0.0 / 2014-06-15
==================
* Initial implementation
(The MIT License)
Copyright (c) 2014 Douglas Christopher Wilson
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
'Software'), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
# depd
[![NPM version](https://badge.fury.io/js/depd.svg)](http://badge.fury.io/js/depd)
[![Build Status](https://travis-ci.org/dougwilson/nodejs-depd.svg?branch=master)](https://travis-ci.org/dougwilson/nodejs-depd)
[![Coverage Status](https://img.shields.io/coveralls/dougwilson/nodejs-depd.svg?branch=master)](https://coveralls.io/r/dougwilson/nodejs-depd)
Deprecate all the things
> With great modules comes great responsibility; mark things deprecated!
## Install
```sh
$ npm install depd
```
## API
```js
var deprecate = require('depd')('my-module')
```
This library allows you to display deprecation messages to your users.
This library goes above and beyond with deprecation warnings by
introspecting the call stack (but only the bits that it is interested
in).
Instead of just warning on the first invocation of a deprecated
function and never again, this module will warn on the first invocation
of a deprecated function per unique call site, making it ideal to alert
users of all deprecated uses across the code base, rather than just
whatever happens to execute first.
The deprecation warnings from this module also include the file and line
information for the call into the module that the deprecated function was
in.
### depd(namespace)
Create a new deprecate function that uses the given namespace name in the
messages and will display the call site prior to the stack entering the
file this function was called from. It is highly suggested you use the
name of your module as the namespace.
### deprecate(message)
Call this function from deprecated code to display a deprecation message.
This message will appear once per unique caller site. Caller site is the
first call site in the stack in a different file from the caller of this
function.
If the message is omitted, a message is generated for you based on the site
of the `deprecate()` call and will display the name of the function called,
similar to the name displayed in a stack trace.
### deprecate.function(fn, message)
Call this function to wrap a given function in a deprecation message on any
call to the function. An optional message can be supplied to provide a custom
message.
### deprecate.property(obj, prop, message)
Call this function to wrap a given property on object in a deprecation message
on any accessing or setting of the property. An optional message can be supplied
to provide a custom message.
The method must be called on the object where the property belongs (not
inherited from the prototype).
If the property is a data descriptor, it will be converted to an accessor
descriptor in order to display the deprecation message.
### process.on('deprecation', fn)
This module will allow easy capturing of deprecation errors by emitting the
errors as the type "deprecation" on the global `process`. If there are no
listeners for this type, the errors are written to STDERR as normal, but if
there are any listeners, nothing will be written to STDERR and instead only
emitted. From there, you can write the errors in a different format or to a
logging source.
The error represents the deprecation and is emitted only once with the same
rules as writing to STDERR. The error has the following properties:
- `message` - This is the message given by the library
- `name` - This is always `'DeprecationError'`
- `namespace` - This is the namespace the deprecation came from
- `stack` - This is the stack of the call to the deprecated thing
Example `error.stack` output:
```
DeprecationError: my-cool-module deprecated oldfunction
at Object.<anonymous> ([eval]-wrapper:6:22)
at Module._compile (module.js:456:26)
at evalScript (node.js:532:25)
at startup (node.js:80:7)
at node.js:902:3
```
### process.env.NO_DEPRECATION
As a user of modules that are deprecated, the environment variable `NO_DEPRECATION`
is provided as a quick solution to silencing deprecation warnings from being
output. The format of this is similar to that of `DEBUG`:
```sh
$ NO_DEPRECATION=my-module,othermod node app.js
```
This will suppress deprecations from being output for "my-module" and "othermod".
The value is a list of comma-separated namespaces. To suppress every warning
across all namespaces, use the value `*` for a namespace.
Providing the argument `--no-deprecation` to the `node` executable will suppress
all deprecations.
**NOTE** This will not suppress the deperecations given to any "deprecation"
event listeners, just the output to STDERR.
### process.env.TRACE_DEPRECATION
As a user of modules that are deprecated, the environment variable `TRACE_DEPRECATION`
is provided as a solution to getting more detailed location information in deprecation
warnings by including the entire stack trace. The format of this is the same as
`NO_DEPRECATION`:
```sh
$ TRACE_DEPRECATION=my-module,othermod node app.js
```
This will include stack traces for deprecations being output for "my-module" and
"othermod". The value is a list of comma-separated namespaces. To trace every
warning across all namespaces, use the value `*` for a namespace.
Providing the argument `--trace-deprecation` to the `node` executable will trace
all deprecations.
**NOTE** This will not trace the deperecations silenced by `NO_DEPRECATION`.
## Display
![message](files/message.png)
When a user calls a function in your library that you mark deprecated, they
will see the following written to STDERR (in the given colors, similar colors
and layout to the `debug` module):
```
bright cyan bright yellow
| | reset cyan
| | | |
▼ ▼ ▼ ▼
my-cool-module deprecated oldfunction [eval]-wrapper:6:22
▲ ▲ ▲ ▲
| | | |
namespace | | location of mycoolmod.oldfunction() call
| deprecation message
the word "deprecated"
```
If the user redirects their STDERR to a file or somewhere that does not support
colors, they see (similar layout to the `debug` module):
```
Sun, 15 Jun 2014 05:21:37 GMT my-cool-module deprecated oldfunction at [eval]-wrapper:6:22
▲ ▲ ▲ ▲ ▲
| | | | |
timestamp of message namespace | | location of mycoolmod.oldfunction() call
| deprecation message
the word "deprecated"
```
## Examples
### Deprecating all calls to a function
This will display a deprecated message about "oldfunction" being deprecated
from "my-module" on STDERR.
```js
var deprecate = require('depd')('my-cool-module')
// message automatically derived from function name
// Object.oldfunction
exports.oldfunction = deprecate.function(function oldfunction() {
// all calls to function are deprecated
})
// specific message
exports.oldfunction = deprecate.function(function () {
// all calls to function are deprecated
}, 'oldfunction')
```
### Conditionally deprecating a function call
This will display a deprecated message about "weirdfunction" being deprecated
from "my-module" on STDERR when called with less than 2 arguments.
```js
var deprecate = require('depd')('my-cool-module')
exports.weirdfunction = function () {
if (arguments.length < 2) {
// calls with 0 or 1 args are deprecated
deprecate('weirdfunction args < 2')
}
}
```
When calling `deprecate` as a function, the warning is counted per call site
within your own module, so you can display different deprecations depending
on different situations and the users will still get all the warnings:
```js
var deprecate = require('depd')('my-cool-module')
exports.weirdfunction = function () {
if (arguments.length < 2) {
// calls with 0 or 1 args are deprecated
deprecate('weirdfunction args < 2')
} else if (typeof arguments[0] !== 'string') {
// calls with non-string first argument are deprecated
deprecate('weirdfunction non-string first arg')
}
}
```
### Deprecating property access
This will display a deprecated message about "oldprop" being deprecated
from "my-module" on STDERR when accessed. A deprecation will be displayed
when setting the value and when getting the value.
```js
var deprecate = require('depd')('my-cool-module')
exports.oldprop = 'something'
// message automatically derives from property name
deprecate.property(exports, 'oldprop')
// explicit message
deprecate.property(exports, 'oldprop', 'oldprop >= 0.10')
```
## License
The MIT License (MIT)
Copyright (c) 2014 Douglas Christopher Wilson
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
/*!
* depd
* Copyright(c) 2014 Douglas Christopher Wilson
* MIT Licensed
*/
/**
* Module dependencies.
*/
var EventEmitter = require('events').EventEmitter
var relative = require('path').relative
/**
* Module exports.
*/
module.exports = depd
/**
* Get the path to base files on.
*/
var basePath = process.cwd()
/**
* Get listener count on event emitter.
*/
/*istanbul ignore next*/
var eventListenerCount = EventEmitter.listenerCount
|| function (emitter, type) { return emitter.listeners(type).length }
/**
* Determine if namespace is contained in the string.
*/
function containsNamespace(str, namespace) {
var val = str.split(/[ ,]+/)
namespace = String(namespace).toLowerCase()
for (var i = 0 ; i < val.length; i++) {
if (!(str = val[i])) continue;
// namespace contained
if (str === '*' || str.toLowerCase() === namespace) {
return true
}
}
return false
}
/**
* Convert a data descriptor to accessor descriptor.
*/
function convertDataDescriptorToAccessor(obj, prop, message) {
var descriptor = Object.getOwnPropertyDescriptor(obj, prop)
var value = descriptor.value
descriptor.get = function getter() { return value }
if (descriptor.writable) {
descriptor.set = function setter(val) { return value = val }
}
delete descriptor.value
delete descriptor.writable
Object.defineProperty(obj, prop, descriptor)
return descriptor
}
/**
* Create arguments string to keep arity.
*/
function createArgumentsString(arity) {
var str = ''
for (var i = 0; i < arity; i++) {
str += ', arg' + i
}
return str.substr(2)
}
/**
* Create stack string from stack.
*/
function createStackString(stack) {
var str = this.name + ': ' + this.namespace
if (this.message) {
str += ' deprecated ' + this.message
}
for (var i = 0; i < stack.length; i++) {
str += '\n at ' + stack[i].toString()
}
return str
}
/**
* Create deprecate for namespace in caller.
*/
function depd(namespace) {
if (!namespace) {
throw new TypeError('argument namespace is required')
}
var stack = getStack()
var site = callSiteLocation(stack[1])
var file = site[0]
function deprecate(message) {
// call to self as log
log.call(deprecate, message)
}
deprecate._file = file
deprecate._ignored = isignored(namespace)
deprecate._namespace = namespace
deprecate._traced = istraced(namespace)
deprecate._warned = Object.create(null)
deprecate.function = wrapfunction
deprecate.property = wrapproperty
return deprecate
}
/**
* Determine if namespace is ignored.
*/
function isignored(namespace) {
/* istanbul ignore next: tested in a child processs */
if (process.noDeprecation) {
// --no-deprecation support
return true
}
var str = process.env.NO_DEPRECATION || ''
// namespace ignored
return containsNamespace(str, namespace)
}
/**
* Determine if namespace is traced.
*/
function istraced(namespace) {
/* istanbul ignore next: tested in a child processs */
if (process.traceDeprecation) {
// --trace-deprecation support
return true
}
var str = process.env.TRACE_DEPRECATION || ''
// namespace traced
return containsNamespace(str, namespace)
}
/**
* Display deprecation message.
*/
function log(message, site) {
var haslisteners = eventListenerCount(process, 'deprecation') !== 0
// abort early if no destination
if (!haslisteners && this._ignored) {
return
}
var caller
var callFile
var callSite
var i = 0
var seen = false
var stack = getStack()
var file = this._file
if (site) {
// provided site
callSite = callSiteLocation(stack[1])
callSite.name = site.name
file = callSite[0]
} else {
// get call site
i = 2
site = callSiteLocation(stack[i])
callSite = site
}
// get caller of deprecated thing in relation to file
for (; i < stack.length; i++) {
caller = callSiteLocation(stack[i])
callFile = caller[0]
if (callFile === file) {
seen = true
} else if (callFile === this._file) {
file = this._file
} else if (seen) {
break
}
}
var key = caller
? site.join(':') + '__' + caller.join(':')
: undefined
if (key !== undefined && key in this._warned) {
// already warned
return
}
this._warned[key] = true
// generate automatic message from call site
if (!message) {
message = callSite === site || !callSite.name
? defaultMessage(site)
: defaultMessage(callSite)
}
// emit deprecation if listeners exist
if (haslisteners) {
var err = DeprecationError(this._namespace, message, stack.slice(i))
process.emit('deprecation', err)
return
}
// format and write message
var format = process.stderr.isTTY
? formatColor
: formatPlain
var msg = format.call(this, message, caller, stack.slice(i))
process.stderr.write(msg + '\n', 'utf8')
return
}
/**
* Get call site location as array.
*/
function callSiteLocation(callSite) {
var file = callSite.getFileName() || '<anonymous>'
var line = callSite.getLineNumber()
var colm = callSite.getColumnNumber()
if (callSite.isEval()) {
file = callSite.getEvalOrigin() + ', ' + file
}
var site = [file, line, colm]
site.callSite = callSite
site.name = callSite.getFunctionName()
return site
}
/**
* Generate a default message from the site.
*/
function defaultMessage(site) {
var callSite = site.callSite
var funcName = site.name
var typeName = callSite.getTypeName()
// make useful anonymous name
if (!funcName) {
funcName = '<anonymous@' + formatLocation(site) + '>'
}
// make useful type name
if (typeName === 'Function') {
typeName = callSite.getThis().name || typeName
}
return callSite.getMethodName()
? typeName + '.' + funcName
: funcName
}
/**
* Format deprecation message without color.
*/
function formatPlain(msg, caller, stack) {
var timestamp = new Date().toUTCString()
var formatted = timestamp
+ ' ' + this._namespace
+ ' deprecated ' + msg
// add stack trace
if (this._traced) {
for (var i = 0; i < stack.length; i++) {
formatted += '\n at ' + stack[i].toString()
}
return formatted
}
if (caller) {
formatted += ' at ' + formatLocation(caller)
}
return formatted
}
/**
* Format deprecation message with color.
*/
function formatColor(msg, caller, stack) {
var formatted = '\x1b[36;1m' + this._namespace + '\x1b[22;39m' // bold cyan
+ ' \x1b[33;1mdeprecated\x1b[22;39m' // bold yellow
+ ' \x1b[0m' + msg + '\x1b[39m' // reset
// add stack trace
if (this._traced) {
for (var i = 0; i < stack.length; i++) {
formatted += '\n \x1b[36mat ' + stack[i].toString() + '\x1b[39m' // cyan
}
return formatted
}
if (caller) {
formatted += ' \x1b[36m' + formatLocation(caller) + '\x1b[39m' // cyan
}
return formatted
}
/**
* Format call site location.
*/
function formatLocation(callSite) {
return relative(basePath, callSite[0])
+ ':' + callSite[1]
+ ':' + callSite[2]
}
/**
* Get the stack as array of call sites.
*/
function getStack() {
var obj = {}
var prep = Error.prepareStackTrace
Error.prepareStackTrace = prepareObjectStackTrace
Error.captureStackTrace(obj, getStack)
var stack = obj.stack
Error.prepareStackTrace = prep
return stack
}
/**
* Capture call site stack from v8.
*/
function prepareObjectStackTrace(obj, stack) {
return stack
}
/**
* Return a wrapped function in a deprecation message.
*/
function wrapfunction(fn, message) {
if (typeof fn !== 'function') {
throw new TypeError('argument fn must be a function')
}
var args = createArgumentsString(fn.length)
var deprecate = this
var stack = getStack()
var site = callSiteLocation(stack[1])
site.name = fn.name
var deprecatedfn = eval('(function (' + args + ') {\n'
+ 'log.call(deprecate, message, site)\n'
+ 'return fn.apply(this, arguments)\n'
+ '})')
return deprecatedfn
}
/**
* Wrap property in a deprecation message.
*/
function wrapproperty(obj, prop, message) {
if (!obj || (typeof obj !== 'object' && typeof obj !== 'function')) {
throw new TypeError('argument obj must be object')
}
var descriptor = Object.getOwnPropertyDescriptor(obj, prop)
if (!descriptor) {
throw new TypeError('must call property on owner object')
}
if (!descriptor.configurable) {
throw new TypeError('property must be configurable')
}
var deprecate = this
var stack = getStack()
var site = callSiteLocation(stack[1])
// set site name
site.name = prop
// convert data descriptor
if ('value' in descriptor) {
descriptor = convertDataDescriptorToAccessor(obj, prop, message)
}
var get = descriptor.get
var set = descriptor.set
// wrap getter
if (typeof get === 'function') {
descriptor.get = function getter() {
log.call(deprecate, message, site)
return get.apply(this, arguments)
}
}
// wrap setter
if (typeof set === 'function') {
descriptor.set = function setter() {
log.call(deprecate, message, site)
return set.apply(this, arguments)
}
}
Object.defineProperty(obj, prop, descriptor)
}
/**
* Create DeprecationError for deprecation
*/
function DeprecationError(namespace, message, stack) {
var error = new Error()
var stackString
Object.defineProperty(error, 'constructor', {
value: DeprecationError
})
Object.defineProperty(error, 'message', {
configurable: true,
enumerable: false,
value: message,
writable: true
})
Object.defineProperty(error, 'name', {
enumerable: false,
configurable: true,
value: 'DeprecationError',
writable: true
})
Object.defineProperty(error, 'namespace', {
configurable: true,
enumerable: false,
value: namespace,
writable: true
})
Object.defineProperty(error, 'stack', {
configurable: true,
enumerable: false,
get: function () {
if (stackString !== undefined) {
return stackString
}
// prepare stack trace
return stackString = createStackString.call(this, stack)
},
set: function setter(val) {
stackString = val
}
})
return error
}
{
"name": "depd",
"description": "Deprecate all the things",
"version": "0.4.2",
"author": {
"name": "Douglas Christopher Wilson",
"email": "doug@somethingdoug.com"
},
"license": "MIT",
"keywords": [
"deprecate",
"deprecated"
],
"repository": {
"type": "git",
"url": "git://github.com/dougwilson/nodejs-depd"
},
"devDependencies": {
"istanbul": "0.3.0",
"mocha": "~1.20.1",
"should": "~4.0.4"
},
"engines": {
"node": ">= 0.8.0"
},
"scripts": {
"test": "mocha --reporter spec --bail --require should test/",
"test-cov": "istanbul cover node_modules/mocha/bin/_mocha -- --reporter dot --require should test/",
"test-travis": "istanbul cover node_modules/mocha/bin/_mocha --report lcovonly -- --reporter spec --require should test/"
},
"readme": "# depd\n\n[![NPM version](https://badge.fury.io/js/depd.svg)](http://badge.fury.io/js/depd)\n[![Build Status](https://travis-ci.org/dougwilson/nodejs-depd.svg?branch=master)](https://travis-ci.org/dougwilson/nodejs-depd)\n[![Coverage Status](https://img.shields.io/coveralls/dougwilson/nodejs-depd.svg?branch=master)](https://coveralls.io/r/dougwilson/nodejs-depd)\n\nDeprecate all the things\n\n> With great modules comes great responsibility; mark things deprecated!\n\n## Install\n\n```sh\n$ npm install depd\n```\n\n## API\n\n```js\nvar deprecate = require('depd')('my-module')\n```\n\nThis library allows you to display deprecation messages to your users.\nThis library goes above and beyond with deprecation warnings by\nintrospecting the call stack (but only the bits that it is interested\nin).\n\nInstead of just warning on the first invocation of a deprecated\nfunction and never again, this module will warn on the first invocation\nof a deprecated function per unique call site, making it ideal to alert\nusers of all deprecated uses across the code base, rather than just\nwhatever happens to execute first.\n\nThe deprecation warnings from this module also include the file and line\ninformation for the call into the module that the deprecated function was\nin.\n\n### depd(namespace)\n\nCreate a new deprecate function that uses the given namespace name in the\nmessages and will display the call site prior to the stack entering the\nfile this function was called from. It is highly suggested you use the\nname of your module as the namespace.\n\n### deprecate(message)\n\nCall this function from deprecated code to display a deprecation message.\nThis message will appear once per unique caller site. Caller site is the\nfirst call site in the stack in a different file from the caller of this\nfunction.\n\nIf the message is omitted, a message is generated for you based on the site\nof the `deprecate()` call and will display the name of the function called,\nsimilar to the name displayed in a stack trace.\n\n### deprecate.function(fn, message)\n\nCall this function to wrap a given function in a deprecation message on any\ncall to the function. An optional message can be supplied to provide a custom\nmessage.\n\n### deprecate.property(obj, prop, message)\n\nCall this function to wrap a given property on object in a deprecation message\non any accessing or setting of the property. An optional message can be supplied\nto provide a custom message.\n\nThe method must be called on the object where the property belongs (not\ninherited from the prototype).\n\nIf the property is a data descriptor, it will be converted to an accessor\ndescriptor in order to display the deprecation message.\n\n### process.on('deprecation', fn)\n\nThis module will allow easy capturing of deprecation errors by emitting the\nerrors as the type \"deprecation\" on the global `process`. If there are no\nlisteners for this type, the errors are written to STDERR as normal, but if\nthere are any listeners, nothing will be written to STDERR and instead only\nemitted. From there, you can write the errors in a different format or to a\nlogging source.\n\nThe error represents the deprecation and is emitted only once with the same\nrules as writing to STDERR. The error has the following properties:\n\n - `message` - This is the message given by the library\n - `name` - This is always `'DeprecationError'`\n - `namespace` - This is the namespace the deprecation came from\n - `stack` - This is the stack of the call to the deprecated thing\n\nExample `error.stack` output:\n\n```\nDeprecationError: my-cool-module deprecated oldfunction\n at Object.<anonymous> ([eval]-wrapper:6:22)\n at Module._compile (module.js:456:26)\n at evalScript (node.js:532:25)\n at startup (node.js:80:7)\n at node.js:902:3\n```\n\n### process.env.NO_DEPRECATION\n\nAs a user of modules that are deprecated, the environment variable `NO_DEPRECATION`\nis provided as a quick solution to silencing deprecation warnings from being\noutput. The format of this is similar to that of `DEBUG`:\n\n```sh\n$ NO_DEPRECATION=my-module,othermod node app.js\n```\n\nThis will suppress deprecations from being output for \"my-module\" and \"othermod\".\nThe value is a list of comma-separated namespaces. To suppress every warning\nacross all namespaces, use the value `*` for a namespace.\n\nProviding the argument `--no-deprecation` to the `node` executable will suppress\nall deprecations.\n\n**NOTE** This will not suppress the deperecations given to any \"deprecation\"\nevent listeners, just the output to STDERR.\n\n### process.env.TRACE_DEPRECATION\n\nAs a user of modules that are deprecated, the environment variable `TRACE_DEPRECATION`\nis provided as a solution to getting more detailed location information in deprecation\nwarnings by including the entire stack trace. The format of this is the same as\n`NO_DEPRECATION`:\n\n```sh\n$ TRACE_DEPRECATION=my-module,othermod node app.js\n```\n\nThis will include stack traces for deprecations being output for \"my-module\" and\n\"othermod\". The value is a list of comma-separated namespaces. To trace every\nwarning across all namespaces, use the value `*` for a namespace.\n\nProviding the argument `--trace-deprecation` to the `node` executable will trace\nall deprecations.\n\n**NOTE** This will not trace the deperecations silenced by `NO_DEPRECATION`.\n\n## Display\n\n![message](files/message.png)\n\nWhen a user calls a function in your library that you mark deprecated, they\nwill see the following written to STDERR (in the given colors, similar colors\nand layout to the `debug` module):\n\n```\nbright cyan bright yellow\n| | reset cyan\n| | | |\n▼ ▼ ▼ ▼\nmy-cool-module deprecated oldfunction [eval]-wrapper:6:22\n▲ ▲ ▲ ▲\n| | | |\nnamespace | | location of mycoolmod.oldfunction() call\n | deprecation message\n the word \"deprecated\"\n```\n\nIf the user redirects their STDERR to a file or somewhere that does not support\ncolors, they see (similar layout to the `debug` module):\n\n```\nSun, 15 Jun 2014 05:21:37 GMT my-cool-module deprecated oldfunction at [eval]-wrapper:6:22\n▲ ▲ ▲ ▲ ▲\n| | | | |\ntimestamp of message namespace | | location of mycoolmod.oldfunction() call\n | deprecation message\n the word \"deprecated\"\n```\n\n## Examples\n\n### Deprecating all calls to a function\n\nThis will display a deprecated message about \"oldfunction\" being deprecated\nfrom \"my-module\" on STDERR.\n\n```js\nvar deprecate = require('depd')('my-cool-module')\n\n// message automatically derived from function name\n// Object.oldfunction\nexports.oldfunction = deprecate.function(function oldfunction() {\n // all calls to function are deprecated\n})\n\n// specific message\nexports.oldfunction = deprecate.function(function () {\n // all calls to function are deprecated\n}, 'oldfunction')\n```\n\n### Conditionally deprecating a function call\n\nThis will display a deprecated message about \"weirdfunction\" being deprecated\nfrom \"my-module\" on STDERR when called with less than 2 arguments.\n\n```js\nvar deprecate = require('depd')('my-cool-module')\n\nexports.weirdfunction = function () {\n if (arguments.length < 2) {\n // calls with 0 or 1 args are deprecated\n deprecate('weirdfunction args < 2')\n }\n}\n```\n\nWhen calling `deprecate` as a function, the warning is counted per call site\nwithin your own module, so you can display different deprecations depending\non different situations and the users will still get all the warnings:\n\n```js\nvar deprecate = require('depd')('my-cool-module')\n\nexports.weirdfunction = function () {\n if (arguments.length < 2) {\n // calls with 0 or 1 args are deprecated\n deprecate('weirdfunction args < 2')\n } else if (typeof arguments[0] !== 'string') {\n // calls with non-string first argument are deprecated\n deprecate('weirdfunction non-string first arg')\n }\n}\n```\n\n### Deprecating property access\n\nThis will display a deprecated message about \"oldprop\" being deprecated\nfrom \"my-module\" on STDERR when accessed. A deprecation will be displayed\nwhen setting the value and when getting the value.\n\n```js\nvar deprecate = require('depd')('my-cool-module')\n\nexports.oldprop = 'something'\n\n// message automatically derives from property name\ndeprecate.property(exports, 'oldprop')\n\n// explicit message\ndeprecate.property(exports, 'oldprop', 'oldprop >= 0.10')\n```\n\n## License\n\nThe MIT License (MIT)\n\nCopyright (c) 2014 Douglas Christopher Wilson\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n",
"readmeFilename": "Readme.md",
"bugs": {
"url": "https://github.com/dougwilson/nodejs-depd/issues"
},
"_id": "depd@0.4.2",
"_from": "depd@0.4.2"
}
{ {
"name": "send", "name": "send",
"description": "Better streaming static file server with Range and conditional-GET support", "description": "Better streaming static file server with Range and conditional-GET support",
"version": "0.5.0", "version": "0.7.0",
"author": { "author": {
"name": "TJ Holowaychuk", "name": "TJ Holowaychuk",
"email": "tj@vision-media.ca" "email": "tj@vision-media.ca"
...@@ -23,7 +23,8 @@ ...@@ -23,7 +23,8 @@
"server" "server"
], ],
"dependencies": { "dependencies": {
"debug": "1.0.2", "debug": "1.0.4",
"depd": "0.4.2",
"escape-html": "1.0.1", "escape-html": "1.0.1",
"finished": "1.2.2", "finished": "1.2.2",
"fresh": "0.2.2", "fresh": "0.2.2",
...@@ -32,7 +33,7 @@ ...@@ -32,7 +33,7 @@
"range-parser": "~1.0.0" "range-parser": "~1.0.0"
}, },
"devDependencies": { "devDependencies": {
"istanbul": "0.2.10", "istanbul": "0.3.0",
"mocha": "~1.20.0", "mocha": "~1.20.0",
"should": "~4.0.0", "should": "~4.0.0",
"supertest": "~0.13.0" "supertest": "~0.13.0"
...@@ -41,15 +42,15 @@ ...@@ -41,15 +42,15 @@
"node": ">= 0.8.0" "node": ">= 0.8.0"
}, },
"scripts": { "scripts": {
"test": "mocha --reporter dot", "test": "mocha --check-leaks --reporter spec --bail",
"test-cov": "istanbul cover node_modules/mocha/bin/_mocha -- --reporter dot", "test-cov": "istanbul cover node_modules/mocha/bin/_mocha -- --check-leaks --reporter dot",
"test-travis": "istanbul cover node_modules/mocha/bin/_mocha --report lcovonly -- --reporter spec" "test-travis": "istanbul cover node_modules/mocha/bin/_mocha --report lcovonly -- --check-leaks --reporter spec"
}, },
"readme": "# send\n\n[![NPM version](https://badge.fury.io/js/send.svg)](https://badge.fury.io/js/send)\n[![Build Status](https://travis-ci.org/visionmedia/send.svg?branch=master)](https://travis-ci.org/visionmedia/send)\n[![Coverage Status](https://img.shields.io/coveralls/visionmedia/send.svg?branch=master)](https://coveralls.io/r/visionmedia/send)\n\n Send is Connect's `static()` extracted for generalized use, a streaming static file\n server supporting partial responses (Ranges), conditional-GET negotiation, high test coverage, and granular events which may be leveraged to take appropriate actions in your application or framework.\n\n## Installation\n\n $ npm install send\n\n## Examples\n\n Small:\n\n```js\nvar http = require('http');\nvar send = require('send');\n\nvar app = http.createServer(function(req, res){\n send(req, req.url).pipe(res);\n}).listen(3000);\n```\n\n Serving from a root directory with custom error-handling:\n\n```js\nvar http = require('http');\nvar send = require('send');\nvar url = require('url');\n\nvar app = http.createServer(function(req, res){\n // your custom error-handling logic:\n function error(err) {\n res.statusCode = err.status || 500;\n res.end(err.message);\n }\n\n // your custom headers\n function headers(res, path, stat) {\n // serve all files for download\n res.setHeader('Content-Disposition', 'attachment');\n }\n\n // your custom directory handling logic:\n function redirect() {\n res.statusCode = 301;\n res.setHeader('Location', req.url + '/');\n res.end('Redirecting to ' + req.url + '/');\n }\n\n // transfer arbitrary files from within\n // /www/example.com/public/*\n send(req, url.parse(req.url).pathname, {root: '/www/example.com/public'})\n .on('error', error)\n .on('directory', redirect)\n .on('headers', headers)\n .pipe(res);\n}).listen(3000);\n```\n\n## API\n\n### Options\n\n#### etag\n\n Enable or disable etag generation, defaults to true.\n\n#### hidden\n\n Enable or disable transfer of hidden files, defaults to false.\n\n#### index\n\n By default send supports \"index.html\" files, to disable this\n set `false` or to supply a new index pass a string or an array\n in preferred order.\n\n#### maxage\n\n Provide a max-age in milliseconds for http caching, defaults to 0.\n This can also be a string accepted by the\n [ms](https://www.npmjs.org/package/ms#readme) module.\n\n#### root\n\n Serve files relative to `path`.\n\n### Events\n\n - `error` an error occurred `(err)`\n - `directory` a directory was requested\n - `file` a file was requested `(path, stat)`\n - `headers` the headers are about to be set on a file `(res, path, stat)`\n - `stream` file streaming has started `(stream)`\n - `end` streaming has completed\n\n### .etag(bool)\n\n Enable or disable etag generation, defaults to true.\n\n### .root(dir)\n\n Serve files relative to `path`. Aliased as `.from(dir)`.\n\n### .index(paths)\n\n By default send supports \"index.html\" files, to disable this\n invoke `.index(false)` or to supply a new index pass a string\n or an array in preferred order.\n\n### .maxage(ms)\n\n Provide a max-age in milliseconds for http caching, defaults to 0.\n This can also be a string accepted by the\n [ms](https://www.npmjs.org/package/ms#readme) module.\n\n### .hidden(bool)\n\n Enable or disable transfer of hidden files, defaults to false.\n\n## Error-handling\n\n By default when no `error` listeners are present an automatic response will be made, otherwise you have full control over the response, aka you may show a 5xx page etc.\n\n## Caching\n\n It does _not_ perform internal caching, you should use a reverse proxy cache such\n as Varnish for this, or those fancy things called CDNs. If your application is small enough that it would benefit from single-node memory caching, it's small enough that it does not need caching at all ;).\n\n## Debugging\n\n To enable `debug()` instrumentation output export __DEBUG__:\n\n```\n$ DEBUG=send node app\n```\n\n## Running tests\n\n```\n$ npm install\n$ npm test\n```\n\n## License \n\n(The MIT License)\n\nCopyright (c) 2012 TJ Holowaychuk &lt;tj@vision-media.ca&gt;\n\nPermission is hereby granted, free of charge, to any person obtaining\na copy of this software and associated documentation files (the\n'Software'), to deal in the Software without restriction, including\nwithout limitation the rights to use, copy, modify, merge, publish,\ndistribute, sublicense, and/or sell copies of the Software, and to\npermit persons to whom the Software is furnished to do so, subject to\nthe following conditions:\n\nThe above copyright notice and this permission notice shall be\nincluded in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\nIN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\nTORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\nSOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n", "readme": "# send\n\n[![NPM version](https://badge.fury.io/js/send.svg)](https://badge.fury.io/js/send)\n[![Build Status](https://travis-ci.org/visionmedia/send.svg?branch=master)](https://travis-ci.org/visionmedia/send)\n[![Coverage Status](https://img.shields.io/coveralls/visionmedia/send.svg?branch=master)](https://coveralls.io/r/visionmedia/send)\n\n Send is Connect's `static()` extracted for generalized use, a streaming static file\n server supporting partial responses (Ranges), conditional-GET negotiation, high test coverage, and granular events which may be leveraged to take appropriate actions in your application or framework.\n\n## Installation\n\n $ npm install send\n\n## Examples\n\n Small:\n\n```js\nvar http = require('http');\nvar send = require('send');\n\nvar app = http.createServer(function(req, res){\n send(req, req.url).pipe(res);\n}).listen(3000);\n```\n\n Serving from a root directory with custom error-handling:\n\n```js\nvar http = require('http');\nvar send = require('send');\nvar url = require('url');\n\nvar app = http.createServer(function(req, res){\n // your custom error-handling logic:\n function error(err) {\n res.statusCode = err.status || 500;\n res.end(err.message);\n }\n\n // your custom headers\n function headers(res, path, stat) {\n // serve all files for download\n res.setHeader('Content-Disposition', 'attachment');\n }\n\n // your custom directory handling logic:\n function redirect() {\n res.statusCode = 301;\n res.setHeader('Location', req.url + '/');\n res.end('Redirecting to ' + req.url + '/');\n }\n\n // transfer arbitrary files from within\n // /www/example.com/public/*\n send(req, url.parse(req.url).pathname, {root: '/www/example.com/public'})\n .on('error', error)\n .on('directory', redirect)\n .on('headers', headers)\n .pipe(res);\n}).listen(3000);\n```\n\n## API\n\n### Options\n\n#### etag\n\n Enable or disable etag generation, defaults to true.\n\n#### dotfiles\n\n Set how \"dotfiles\" are treated when encountered. A dotfile is a file\n or directory that begins with a dot (\".\"). Note this check is done on\n the path itself without checking if the path actually exists on the\n disk. If `root` is specified, only the dotfiles above the root are\n checked (i.e. the root itself can be within a dotfile when when set\n to \"deny\").\n\n The default value is `'ignore'`.\n\n - `'allow'` No special treatment for dotfiles.\n - `'deny'` Send a 403 for any request for a dotfile.\n - `'ignore'` Pretend like the dotfile does not exist and 404.\n\n#### index\n\n By default send supports \"index.html\" files, to disable this\n set `false` or to supply a new index pass a string or an array\n in preferred order.\n\n#### maxAge\n\n Provide a max-age in milliseconds for http caching, defaults to 0.\n This can also be a string accepted by the\n [ms](https://www.npmjs.org/package/ms#readme) module.\n\n#### root\n\n Serve files relative to `path`.\n\n### Events\n\n - `error` an error occurred `(err)`\n - `directory` a directory was requested\n - `file` a file was requested `(path, stat)`\n - `headers` the headers are about to be set on a file `(res, path, stat)`\n - `stream` file streaming has started `(stream)`\n - `end` streaming has completed\n\n## Error-handling\n\n By default when no `error` listeners are present an automatic response will be made, otherwise you have full control over the response, aka you may show a 5xx page etc.\n\n## Caching\n\n It does _not_ perform internal caching, you should use a reverse proxy cache such\n as Varnish for this, or those fancy things called CDNs. If your application is small enough that it would benefit from single-node memory caching, it's small enough that it does not need caching at all ;).\n\n## Debugging\n\n To enable `debug()` instrumentation output export __DEBUG__:\n\n```\n$ DEBUG=send node app\n```\n\n## Running tests\n\n```\n$ npm install\n$ npm test\n```\n\n## License \n\n(The MIT License)\n\nCopyright (c) 2012 TJ Holowaychuk &lt;tj@vision-media.ca&gt;\n\nPermission is hereby granted, free of charge, to any person obtaining\na copy of this software and associated documentation files (the\n'Software'), to deal in the Software without restriction, including\nwithout limitation the rights to use, copy, modify, merge, publish,\ndistribute, sublicense, and/or sell copies of the Software, and to\npermit persons to whom the Software is furnished to do so, subject to\nthe following conditions:\n\nThe above copyright notice and this permission notice shall be\nincluded in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\nIN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\nTORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\nSOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n",
"readmeFilename": "Readme.md", "readmeFilename": "Readme.md",
"bugs": { "bugs": {
"url": "https://github.com/visionmedia/send/issues" "url": "https://github.com/visionmedia/send/issues"
}, },
"_id": "send@0.5.0", "_id": "send@0.7.0",
"_from": "send@0.5.0" "_from": "send@0.7.0"
} }
{ {
"name": "serve-static", "name": "serve-static",
"description": "Serve static files", "description": "Serve static files",
"version": "1.3.1", "version": "1.4.0",
"author": { "author": {
"name": "Douglas Christopher Wilson", "name": "Douglas Christopher Wilson",
"email": "doug@somethingdoug.com" "email": "doug@somethingdoug.com"
...@@ -13,8 +13,8 @@ ...@@ -13,8 +13,8 @@
}, },
"dependencies": { "dependencies": {
"escape-html": "1.0.1", "escape-html": "1.0.1",
"parseurl": "~1.1.3", "parseurl": "~1.2.0",
"send": "0.5.0" "send": "0.7.0"
}, },
"devDependencies": { "devDependencies": {
"istanbul": "0.3.0", "istanbul": "0.3.0",
...@@ -30,11 +30,11 @@ ...@@ -30,11 +30,11 @@
"test-cov": "istanbul cover node_modules/mocha/bin/_mocha -- --reporter dot --require should test/", "test-cov": "istanbul cover node_modules/mocha/bin/_mocha -- --reporter dot --require should test/",
"test-travis": "istanbul cover node_modules/mocha/bin/_mocha --report lcovonly -- --reporter spec --require should test/" "test-travis": "istanbul cover node_modules/mocha/bin/_mocha --report lcovonly -- --reporter spec --require should test/"
}, },
"readme": "# serve-static\n\n[![NPM version](https://badge.fury.io/js/serve-static.svg)](http://badge.fury.io/js/serve-static)\n[![Build Status](https://travis-ci.org/expressjs/serve-static.svg?branch=master)](https://travis-ci.org/expressjs/serve-static)\n[![Coverage Status](https://img.shields.io/coveralls/expressjs/serve-static.svg?branch=master)](https://coveralls.io/r/expressjs/serve-static)\n\nPreviously `connect.static()`.\n\n## Install\n\n```sh\n$ npm install serve-static\n```\n\n## API\n\n```js\nvar serveStatic = require('serve-static')\n```\n\n### serveStatic(root, options)\n\nCreate a new middleware function to serve files from within a given root\ndirectory. The file to serve will be determined by combining `req.url`\nwith the provided root directory.\n\nOptions:\n\n- `hidden` Allow transfer of hidden files. defaults to `false`\n- `index` Default file name, defaults to `'index.html'`\n- `maxAge` Browser cache maxAge in milliseconds. This can also be a string accepted by the [ms](https://www.npmjs.org/package/ms#readme) module. defaults to `0`\n- `redirect` Redirect to trailing \"/\" when the pathname is a dir. defaults to `true`\n- `setHeaders` Function to set custom headers on response.\n\n## Examples\n\n### Serve files with vanilla node.js http server\n\n```js\nvar finalhandler = require('finalhandler')\nvar http = require('http')\nvar serveStatic = require('serve-static')\n\n// Serve up public/ftp folder\nvar serve = serveStatic('public/ftp', {'index': ['index.html', 'index.htm']})\n\n// Create server\nvar server = http.createServer(function(req, res){\n var done = finalhandler(req, res)\n serve(req, res, done)\n})\n\n// Listen\nserver.listen(3000)\n```\n\n### Serve all files from ftp folder\n\n```js\nvar connect = require('connect')\nvar serveStatic = require('serve-static')\n\nvar app = connect()\n\napp.use(serveStatic('public/ftp', {'index': ['default.html', 'default.htm']}))\napp.listen(3000)\n```\n\n### Serve all files as downloads\n\n```js\nvar express = require('express')\nvar serveStatic = require('serve-static')\n\nvar app = express()\n\napp.use(serveStatic('public/ftp', {\n 'index': false,\n 'setHeaders': setHeaders\n}))\napp.listen(3000)\n\nfunction setHeaders(res, path) {\n res.attachment(path)\n}\n```\n\n## License\n\nThe MIT License (MIT)\n\nCopyright (c) 2014 Douglas Christopher Wilson\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n", "readme": "# serve-static\n\n[![NPM version](https://badge.fury.io/js/serve-static.svg)](http://badge.fury.io/js/serve-static)\n[![Build Status](https://travis-ci.org/expressjs/serve-static.svg?branch=master)](https://travis-ci.org/expressjs/serve-static)\n[![Coverage Status](https://img.shields.io/coveralls/expressjs/serve-static.svg?branch=master)](https://coveralls.io/r/expressjs/serve-static)\n\n## Install\n\n```sh\n$ npm install serve-static\n```\n\n## API\n\n```js\nvar serveStatic = require('serve-static')\n```\n\n### serveStatic(root, options)\n\nCreate a new middleware function to serve files from within a given root\ndirectory. The file to serve will be determined by combining `req.url`\nwith the provided root directory. When a file is not found, instead of\nsending a 404 response, this module will instead call `next()` to move on\nto the next middleware, allowing for stacking and fall-backs.\n\n#### Options\n\n##### dotfiles\n\n Set how \"dotfiles\" are treated when encountered. A dotfile is a file\nor directory that begins with a dot (\".\"). Note this check is done on\nthe path itself without checking if the path actually exists on the\ndisk. If `root` is specified, only the dotfiles above the root are\nchecked (i.e. the root itself can be within a dotfile when when set\nto \"deny\").\n\nThe default value is `'ignore'`.\n\n - `'allow'` No special treatment for dotfiles.\n - `'deny'` Send a 403 for any request for a dotfile.\n - `'ignore'` Pretend like the dotfile does not exist and call `next()`.\n\n##### etag\n\nEnable or disable etag generation, defaults to true.\n\n##### index\n\nBy default this module will send \"index.html\" files in response to a request\non a directory. To disable this set `false` or to supply a new index pass a\nstring or an array in preferred order.\n\n##### maxAge\n\nProvide a max-age in milliseconds for http caching, defaults to 0. This\ncan also be a string accepted by the [ms](https://www.npmjs.org/package/ms#readme)\nmodule.\n\n##### redirect\n\nRedirect to trailing \"/\" when the pathname is a dir. Defaults to `true`.\n\n##### setHeaders\n\nFunction to set custom headers on response.\n\n## Examples\n\n### Serve files with vanilla node.js http server\n\n```js\nvar finalhandler = require('finalhandler')\nvar http = require('http')\nvar serveStatic = require('serve-static')\n\n// Serve up public/ftp folder\nvar serve = serveStatic('public/ftp', {'index': ['index.html', 'index.htm']})\n\n// Create server\nvar server = http.createServer(function(req, res){\n var done = finalhandler(req, res)\n serve(req, res, done)\n})\n\n// Listen\nserver.listen(3000)\n```\n\n### Serve all files from ftp folder\n\n```js\nvar connect = require('connect')\nvar serveStatic = require('serve-static')\n\nvar app = connect()\n\napp.use(serveStatic('public/ftp', {'index': ['default.html', 'default.htm']}))\napp.listen(3000)\n```\n\n### Serve all files as downloads\n\n```js\nvar express = require('express')\nvar serveStatic = require('serve-static')\n\nvar app = express()\n\napp.use(serveStatic('public/ftp', {\n 'index': false,\n 'setHeaders': setHeaders\n}))\napp.listen(3000)\n\nfunction setHeaders(res, path) {\n res.attachment(path)\n}\n```\n\n## License\n\nThe MIT License (MIT)\n\nCopyright (c) 2014 Douglas Christopher Wilson\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n",
"readmeFilename": "Readme.md", "readmeFilename": "Readme.md",
"bugs": { "bugs": {
"url": "https://github.com/expressjs/serve-static/issues" "url": "https://github.com/expressjs/serve-static/issues"
}, },
"_id": "serve-static@1.3.1", "_id": "serve-static@1.4.0",
"_from": "serve-static@*" "_from": "serve-static@*"
} }
{ {
"name": "better-assert", "name": "better-assert",
"version": "1.0.0", "version": "1.0.1",
"description": "Better assertions for node, reporting the expr, filename, lineno etc", "description": "Better assertions for node, reporting the expr, filename, lineno etc",
"keywords": [ "keywords": [
"assert", "assert",
...@@ -12,16 +12,32 @@ ...@@ -12,16 +12,32 @@
"name": "TJ Holowaychuk", "name": "TJ Holowaychuk",
"email": "tj@vision-media.ca" "email": "tj@vision-media.ca"
}, },
"contributors": [
{
"name": "TonyHe",
"email": "coolhzb@163.com"
},
{
"name": "ForbesLindesay"
}
],
"dependencies": { "dependencies": {
"callsite": "1.0.0" "callsite": "1.0.0"
}, },
"repository": {
"type": "git",
"url": "https://github.com/visionmedia/better-assert.git"
},
"main": "index", "main": "index",
"engines": { "engines": {
"node": "*" "node": "*"
}, },
"readme": "\n# better-assert\n\n Better c-style assertions using [callsite](https://github.com/visionmedia/callsite) for\n self-documenting failure messages.\n\n## Installation\n\n $ npm install better-assert\n\n## Example\n\n By default assertions are enabled, however the __NO_ASSERT__ environment variable \n will deactivate them when truthy.\n\n```js\nvar assert = require('better-assert');\n\ntest();\n\nfunction test() {\n var user = { name: 'tobi' };\n assert('tobi' == user.name);\n assert('number' == typeof user.age);\n}\n\nAssertionError: 'number' == typeof user.age\n at test (/Users/tj/projects/better-assert/example.js:9:3)\n at Object.<anonymous> (/Users/tj/projects/better-assert/example.js:4:1)\n at Module._compile (module.js:449:26)\n at Object.Module._extensions..js (module.js:467:10)\n at Module.load (module.js:356:32)\n at Function.Module._load (module.js:312:12)\n at Module.runMain (module.js:492:10)\n at process.startup.processNextTick.process._tickCallback (node.js:244:9)\n```\n\n## License \n\n(The MIT License)\n\nCopyright (c) 2012 TJ Holowaychuk &lt;tj@vision-media.ca&gt;\n\nPermission is hereby granted, free of charge, to any person obtaining\na copy of this software and associated documentation files (the\n'Software'), to deal in the Software without restriction, including\nwithout limitation the rights to use, copy, modify, merge, publish,\ndistribute, sublicense, and/or sell copies of the Software, and to\npermit persons to whom the Software is furnished to do so, subject to\nthe following conditions:\n\nThe above copyright notice and this permission notice shall be\nincluded in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\nIN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\nTORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\nSOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.", "readme": "\n# better-assert\n\n Better c-style assertions using [callsite](https://github.com/visionmedia/callsite) for\n self-documenting failure messages.\n\n## Installation\n\n $ npm install better-assert\n\n## Example\n\n By default assertions are enabled, however the __NO_ASSERT__ environment variable \n will deactivate them when truthy.\n\n```js\nvar assert = require('better-assert');\n\ntest();\n\nfunction test() {\n var user = { name: 'tobi' };\n assert('tobi' == user.name);\n assert('number' == typeof user.age);\n}\n\nAssertionError: 'number' == typeof user.age\n at test (/Users/tj/projects/better-assert/example.js:9:3)\n at Object.<anonymous> (/Users/tj/projects/better-assert/example.js:4:1)\n at Module._compile (module.js:449:26)\n at Object.Module._extensions..js (module.js:467:10)\n at Module.load (module.js:356:32)\n at Function.Module._load (module.js:312:12)\n at Module.runMain (module.js:492:10)\n at process.startup.processNextTick.process._tickCallback (node.js:244:9)\n```\n\n## License \n\n(The MIT License)\n\nCopyright (c) 2012 TJ Holowaychuk &lt;tj@vision-media.ca&gt;\n\nPermission is hereby granted, free of charge, to any person obtaining\na copy of this software and associated documentation files (the\n'Software'), to deal in the Software without restriction, including\nwithout limitation the rights to use, copy, modify, merge, publish,\ndistribute, sublicense, and/or sell copies of the Software, and to\npermit persons to whom the Software is furnished to do so, subject to\nthe following conditions:\n\nThe above copyright notice and this permission notice shall be\nincluded in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\nIN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\nTORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\nSOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.",
"readmeFilename": "Readme.md", "readmeFilename": "Readme.md",
"_id": "better-assert@1.0.0", "bugs": {
"url": "https://github.com/visionmedia/better-assert/issues"
},
"_id": "better-assert@1.0.1",
"_from": "better-assert@~1.0.0", "_from": "better-assert@~1.0.0",
"scripts": {} "scripts": {}
} }
{ {
"name": "better-assert", "name": "better-assert",
"version": "1.0.0", "version": "1.0.1",
"description": "Better assertions for node, reporting the expr, filename, lineno etc", "description": "Better assertions for node, reporting the expr, filename, lineno etc",
"keywords": [ "keywords": [
"assert", "assert",
...@@ -12,16 +12,32 @@ ...@@ -12,16 +12,32 @@
"name": "TJ Holowaychuk", "name": "TJ Holowaychuk",
"email": "tj@vision-media.ca" "email": "tj@vision-media.ca"
}, },
"contributors": [
{
"name": "TonyHe",
"email": "coolhzb@163.com"
},
{
"name": "ForbesLindesay"
}
],
"dependencies": { "dependencies": {
"callsite": "1.0.0" "callsite": "1.0.0"
}, },
"repository": {
"type": "git",
"url": "https://github.com/visionmedia/better-assert.git"
},
"main": "index", "main": "index",
"engines": { "engines": {
"node": "*" "node": "*"
}, },
"readme": "\n# better-assert\n\n Better c-style assertions using [callsite](https://github.com/visionmedia/callsite) for\n self-documenting failure messages.\n\n## Installation\n\n $ npm install better-assert\n\n## Example\n\n By default assertions are enabled, however the __NO_ASSERT__ environment variable \n will deactivate them when truthy.\n\n```js\nvar assert = require('better-assert');\n\ntest();\n\nfunction test() {\n var user = { name: 'tobi' };\n assert('tobi' == user.name);\n assert('number' == typeof user.age);\n}\n\nAssertionError: 'number' == typeof user.age\n at test (/Users/tj/projects/better-assert/example.js:9:3)\n at Object.<anonymous> (/Users/tj/projects/better-assert/example.js:4:1)\n at Module._compile (module.js:449:26)\n at Object.Module._extensions..js (module.js:467:10)\n at Module.load (module.js:356:32)\n at Function.Module._load (module.js:312:12)\n at Module.runMain (module.js:492:10)\n at process.startup.processNextTick.process._tickCallback (node.js:244:9)\n```\n\n## License \n\n(The MIT License)\n\nCopyright (c) 2012 TJ Holowaychuk &lt;tj@vision-media.ca&gt;\n\nPermission is hereby granted, free of charge, to any person obtaining\na copy of this software and associated documentation files (the\n'Software'), to deal in the Software without restriction, including\nwithout limitation the rights to use, copy, modify, merge, publish,\ndistribute, sublicense, and/or sell copies of the Software, and to\npermit persons to whom the Software is furnished to do so, subject to\nthe following conditions:\n\nThe above copyright notice and this permission notice shall be\nincluded in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\nIN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\nTORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\nSOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.", "readme": "\n# better-assert\n\n Better c-style assertions using [callsite](https://github.com/visionmedia/callsite) for\n self-documenting failure messages.\n\n## Installation\n\n $ npm install better-assert\n\n## Example\n\n By default assertions are enabled, however the __NO_ASSERT__ environment variable \n will deactivate them when truthy.\n\n```js\nvar assert = require('better-assert');\n\ntest();\n\nfunction test() {\n var user = { name: 'tobi' };\n assert('tobi' == user.name);\n assert('number' == typeof user.age);\n}\n\nAssertionError: 'number' == typeof user.age\n at test (/Users/tj/projects/better-assert/example.js:9:3)\n at Object.<anonymous> (/Users/tj/projects/better-assert/example.js:4:1)\n at Module._compile (module.js:449:26)\n at Object.Module._extensions..js (module.js:467:10)\n at Module.load (module.js:356:32)\n at Function.Module._load (module.js:312:12)\n at Module.runMain (module.js:492:10)\n at process.startup.processNextTick.process._tickCallback (node.js:244:9)\n```\n\n## License \n\n(The MIT License)\n\nCopyright (c) 2012 TJ Holowaychuk &lt;tj@vision-media.ca&gt;\n\nPermission is hereby granted, free of charge, to any person obtaining\na copy of this software and associated documentation files (the\n'Software'), to deal in the Software without restriction, including\nwithout limitation the rights to use, copy, modify, merge, publish,\ndistribute, sublicense, and/or sell copies of the Software, and to\npermit persons to whom the Software is furnished to do so, subject to\nthe following conditions:\n\nThe above copyright notice and this permission notice shall be\nincluded in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\nIN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\nTORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\nSOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.",
"readmeFilename": "Readme.md", "readmeFilename": "Readme.md",
"_id": "better-assert@1.0.0", "bugs": {
"url": "https://github.com/visionmedia/better-assert/issues"
},
"_id": "better-assert@1.0.1",
"_from": "better-assert@~1.0.0", "_from": "better-assert@~1.0.0",
"scripts": {} "scripts": {}
} }
{ {
"name": "better-assert", "name": "better-assert",
"version": "1.0.0", "version": "1.0.1",
"description": "Better assertions for node, reporting the expr, filename, lineno etc", "description": "Better assertions for node, reporting the expr, filename, lineno etc",
"keywords": [ "keywords": [
"assert", "assert",
...@@ -12,15 +12,31 @@ ...@@ -12,15 +12,31 @@
"name": "TJ Holowaychuk", "name": "TJ Holowaychuk",
"email": "tj@vision-media.ca" "email": "tj@vision-media.ca"
}, },
"contributors": [
{
"name": "TonyHe",
"email": "coolhzb@163.com"
},
{
"name": "ForbesLindesay"
}
],
"dependencies": { "dependencies": {
"callsite": "1.0.0" "callsite": "1.0.0"
}, },
"repository": {
"type": "git",
"url": "https://github.com/visionmedia/better-assert.git"
},
"main": "index", "main": "index",
"engines": { "engines": {
"node": "*" "node": "*"
}, },
"readme": "\n# better-assert\n\n Better c-style assertions using [callsite](https://github.com/visionmedia/callsite) for\n self-documenting failure messages.\n\n## Installation\n\n $ npm install better-assert\n\n## Example\n\n By default assertions are enabled, however the __NO_ASSERT__ environment variable \n will deactivate them when truthy.\n\n```js\nvar assert = require('better-assert');\n\ntest();\n\nfunction test() {\n var user = { name: 'tobi' };\n assert('tobi' == user.name);\n assert('number' == typeof user.age);\n}\n\nAssertionError: 'number' == typeof user.age\n at test (/Users/tj/projects/better-assert/example.js:9:3)\n at Object.<anonymous> (/Users/tj/projects/better-assert/example.js:4:1)\n at Module._compile (module.js:449:26)\n at Object.Module._extensions..js (module.js:467:10)\n at Module.load (module.js:356:32)\n at Function.Module._load (module.js:312:12)\n at Module.runMain (module.js:492:10)\n at process.startup.processNextTick.process._tickCallback (node.js:244:9)\n```\n\n## License \n\n(The MIT License)\n\nCopyright (c) 2012 TJ Holowaychuk &lt;tj@vision-media.ca&gt;\n\nPermission is hereby granted, free of charge, to any person obtaining\na copy of this software and associated documentation files (the\n'Software'), to deal in the Software without restriction, including\nwithout limitation the rights to use, copy, modify, merge, publish,\ndistribute, sublicense, and/or sell copies of the Software, and to\npermit persons to whom the Software is furnished to do so, subject to\nthe following conditions:\n\nThe above copyright notice and this permission notice shall be\nincluded in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\nIN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\nTORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\nSOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.", "readme": "\n# better-assert\n\n Better c-style assertions using [callsite](https://github.com/visionmedia/callsite) for\n self-documenting failure messages.\n\n## Installation\n\n $ npm install better-assert\n\n## Example\n\n By default assertions are enabled, however the __NO_ASSERT__ environment variable \n will deactivate them when truthy.\n\n```js\nvar assert = require('better-assert');\n\ntest();\n\nfunction test() {\n var user = { name: 'tobi' };\n assert('tobi' == user.name);\n assert('number' == typeof user.age);\n}\n\nAssertionError: 'number' == typeof user.age\n at test (/Users/tj/projects/better-assert/example.js:9:3)\n at Object.<anonymous> (/Users/tj/projects/better-assert/example.js:4:1)\n at Module._compile (module.js:449:26)\n at Object.Module._extensions..js (module.js:467:10)\n at Module.load (module.js:356:32)\n at Function.Module._load (module.js:312:12)\n at Module.runMain (module.js:492:10)\n at process.startup.processNextTick.process._tickCallback (node.js:244:9)\n```\n\n## License \n\n(The MIT License)\n\nCopyright (c) 2012 TJ Holowaychuk &lt;tj@vision-media.ca&gt;\n\nPermission is hereby granted, free of charge, to any person obtaining\na copy of this software and associated documentation files (the\n'Software'), to deal in the Software without restriction, including\nwithout limitation the rights to use, copy, modify, merge, publish,\ndistribute, sublicense, and/or sell copies of the Software, and to\npermit persons to whom the Software is furnished to do so, subject to\nthe following conditions:\n\nThe above copyright notice and this permission notice shall be\nincluded in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\nIN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\nTORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\nSOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.",
"readmeFilename": "Readme.md", "readmeFilename": "Readme.md",
"_id": "better-assert@1.0.0", "bugs": {
"url": "https://github.com/visionmedia/better-assert/issues"
},
"_id": "better-assert@1.0.1",
"_from": "better-assert@~1.0.0" "_from": "better-assert@~1.0.0"
} }
...@@ -15,6 +15,10 @@ tail = new Tail("fileToTail"); ...@@ -15,6 +15,10 @@ tail = new Tail("fileToTail");
tail.on("line", function(data) { tail.on("line", function(data) {
console.log(data); console.log(data);
}); });
tail.on("error", function(error) {
console.log('ERROR: ', error);
});
```` ````
Tail accepts the line separator as second parameter. If nothing is passed it is defaulted to new line '\n'. Tail accepts the line separator as second parameter. If nothing is passed it is defaulted to new line '\n'.
......
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,7 @@
], ],
"name": "tail", "name": "tail",
"description": "tail a file in node", "description": "tail a file in node",
"version": "0.3.5", "version": "0.3.7",
"repository": { "repository": {
"type": "git", "type": "git",
"url": "git://github.com/lucagrulla/node-tail.git" "url": "git://github.com/lucagrulla/node-tail.git"
...@@ -26,13 +26,13 @@ ...@@ -26,13 +26,13 @@
}, },
"dependencies": {}, "dependencies": {},
"devDependencies": { "devDependencies": {
"coffee-script": "1.6.2" "coffee-script": "1.7.1"
}, },
"readme": "#tail\n\nTo install:\n\n```bash\nnpm install tail\n```\n\n#Use:\n```javascript\nTail = require('tail').Tail;\n\ntail = new Tail(\"fileToTail\");\n\ntail.on(\"line\", function(data) {\n console.log(data);\n});\n````\n\nTail accepts the line separator as second parameter. If nothing is passed it is defaulted to new line '\\n'.\n\n```javascript\n\nvar lineSeparator= \"-\";\n\nnew Tail(\"fileToTail\",lineSeparator)\n```\n\nTail emits two type of events:\n\n* line \n```\nfunction(data){}\n```\n* error\n```\nfunction(exception){}\n```\n\nIf you simply want to stop the tail:\n\n```javascript\ntail.unwatch()\n```\n\nAnd to start watching again:\n```javascript\ntail.watch()\n```\n\n#Want to fork ?\n\nTail is written in [CoffeeScript](http://jashkenas.github.com/coffee-script/).\n\nThe Cakefile generates the javascript that is then published to npm.\n\n#License\nMIT. Please see License file for more details.\n", "readme": "#tail\n\nTo install:\n\n```bash\nnpm install tail\n```\n\n#Use:\n```javascript\nTail = require('tail').Tail;\n\ntail = new Tail(\"fileToTail\");\n\ntail.on(\"line\", function(data) {\n console.log(data);\n});\n\ntail.on(\"error\", function(error) {\n console.log('ERROR: ', error);\n});\n````\n\nTail accepts the line separator as second parameter. If nothing is passed it is defaulted to new line '\\n'.\n\n```javascript\n\nvar lineSeparator= \"-\";\n\nnew Tail(\"fileToTail\",lineSeparator)\n```\n\nTail emits two type of events:\n\n* line \n```\nfunction(data){}\n```\n* error\n```\nfunction(exception){}\n```\n\nIf you simply want to stop the tail:\n\n```javascript\ntail.unwatch()\n```\n\nAnd to start watching again:\n```javascript\ntail.watch()\n```\n\n#Want to fork ?\n\nTail is written in [CoffeeScript](http://jashkenas.github.com/coffee-script/).\n\nThe Cakefile generates the javascript that is then published to npm.\n\n#License\nMIT. Please see License file for more details.\n",
"readmeFilename": "README.md", "readmeFilename": "README.md",
"bugs": { "bugs": {
"url": "https://github.com/lucagrulla/node-tail/issues" "url": "https://github.com/lucagrulla/node-tail/issues"
}, },
"_id": "tail@0.3.5", "_id": "tail@0.3.7",
"_from": "tail@*" "_from": "tail@*"
} }
...@@ -90,27 +90,24 @@ Tail = (function(_super) { ...@@ -90,27 +90,24 @@ Tail = (function(_super) {
}; };
Tail.prototype.watchEvent = function(e) { Tail.prototype.watchEvent = function(e) {
var _this = this; var stats,
_this = this;
if (e === 'change') { if (e === 'change') {
return fs.stat(this.filename, function(err, stats) { stats = fs.statSync(this.filename);
if (err) { if (stats.size < this.pos) {
_this.emit('error', err); this.pos = stats.size;
}
if (stats.size < _this.pos) {
_this.pos = stats.size;
} }
if (stats.size > _this.pos) { if (stats.size > this.pos) {
_this.queue.push({ this.queue.push({
start: _this.pos, start: this.pos,
end: stats.size end: stats.size
}); });
_this.pos = stats.size; this.pos = stats.size;
if (_this.queue.length === 1) { if (this.queue.length === 1) {
return _this.internalDispatcher.emit("next"); return this.internalDispatcher.emit("next");
} }
} }
});
} else if (e === 'rename') { } else if (e === 'rename') {
this.unwatch(); this.unwatch();
return setTimeout((function() { return setTimeout((function() {
......
...@@ -143,7 +143,7 @@ ProxyServer.prototype.before = function(type, passName, callback) { ...@@ -143,7 +143,7 @@ ProxyServer.prototype.before = function(type, passName, callback) {
if(v.name === passName) i = idx; if(v.name === passName) i = idx;
}) })
if(!i) throw new Error('No such pass'); if(i === false) throw new Error('No such pass');
passes.splice(i, 0, callback); passes.splice(i, 0, callback);
}; };
...@@ -158,7 +158,7 @@ ProxyServer.prototype.after = function(type, passName, callback) { ...@@ -158,7 +158,7 @@ ProxyServer.prototype.after = function(type, passName, callback) {
if(v.name === passName) i = idx; if(v.name === passName) i = idx;
}) })
if(!i) throw new Error('No such pass'); if(i === false) throw new Error('No such pass');
passes.splice(i++, 0, callback); passes.splice(i++, 0, callback);
}; };
{ {
"name": "http-proxy", "name": "http-proxy",
"version": "1.1.5", "version": "1.1.6",
"repository": { "repository": {
"type": "git", "type": "git",
"url": "https://github.com/nodejitsu/node-http-proxy.git" "url": "https://github.com/nodejitsu/node-http-proxy.git"
...@@ -50,10 +50,10 @@ ...@@ -50,10 +50,10 @@
"bugs": { "bugs": {
"url": "https://github.com/nodejitsu/node-http-proxy/issues" "url": "https://github.com/nodejitsu/node-http-proxy/issues"
}, },
"_id": "http-proxy@1.1.5", "_id": "http-proxy@1.1.6",
"dist": { "dist": {
"shasum": "918a323d1f14677976670f557add4d00b43f2214" "shasum": "aac9393db4d4bf9d548876148ccf2c0a92fcaad1"
}, },
"_from": "http-proxy@*", "_from": "http-proxy@*",
"_resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.1.5.tgz" "_resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.1.6.tgz"
} }
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment