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": {}
} }
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 {
......
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.
{ {
"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