Socket
Socket
Sign inDemoInstall

express

Package Overview
Dependencies
18
Maintainers
0
Versions
276
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 2.3.6 to 2.3.7

lib/router/collection.js

9

History.md
2.3.7 / 2011-05-23
==================
* Added route `Collection`, ex: `app.get('/user/:id').remove();`
* Added support for `app.param(fn)` to define param logic
* Removed `app.param()` support for callback with return value
* Removed module.parent check from express(1) generated app. Closes #670
* Refactored router. Closes #639
2.3.6 / 2011-05-20

@@ -3,0 +12,0 @@ ==================

2

lib/express.js

@@ -31,3 +31,3 @@

exports.version = '2.3.6';
exports.version = '2.3.7';

@@ -34,0 +34,0 @@ /**

@@ -15,4 +15,6 @@

, router = require('./router')
, Router = require('./router')
, view = require('./view')
, toArray = require('./utils').toArray
, methods = router.methods.concat('del', 'all')
, view = require('./view')
, url = require('url')

@@ -90,7 +92,7 @@ , utils = connect.utils;

// use router, expose as app.get(), etc
var fn = router(function(app){ self.routes = app; }, this);
// router
this.routes = new Router(this);
this.__defineGetter__('router', function(){
this.__usedRouter = true;
return fn;
return self.routes.middleware;
});

@@ -118,37 +120,61 @@

// route lookup methods
this.remove = function(url){
return self.remove.all(url);
};
this.match = function(url){
return self.match.all(url);
};
this.lookup = function(url){
return self.lookup.all(url);
};
// route manipulation methods
methods.forEach(function(method){
self.match[method] = function(url){
return self.router.match(url, 'all' == method
? null
: method);
self.lookup[method] = function(path){
return self.routes.lookup(method, path);
};
self.remove[method] = function(url){
return self.router.remove(url, 'all' == method
? null
: method);
self.match[method] = function(path){
return self.routes.match(method, path);
};
self.lookup[method] = function(path){
return self.router.lookup(path, 'all' == method
? null
: method);
self.remove[method] = function(path){
return self.routes.lookup(method, path).remove();
};
});
// del -> delete
self.lookup.del = self.lookup.delete;
self.match.del = self.match.delete;
self.remove.del = self.remove.delete;
};
/**
* Remove routes matching the given `path`.
*
* @param {Route} path
* @return {Boolean}
* @api public
*/
app.remove = function(path){
return this.routes.lookup('all', path).remove();
};
/**
* Lookup routes defined with a path
* equivalent to `path`.
*
* @param {Stirng} path
* @return {Array}
* @api public
*/
app.lookup = function(path){
return this.routes.lookup('all', path);
};
/**
* Lookup routes matching the given `url`.
*
* @param {Stirng} url
* @return {Array}
* @api public
*/
app.match = function(url){
return this.routes.match('all', url);
};
/**
* When using the vhost() middleware register error handlers.

@@ -295,36 +321,20 @@ */

* Param mapping is used to provide pre-conditions to routes
* which us normalized placeholders. For example ":user_id" may
* attempt to load the user from the database, where as ":num" may
* pass the value through `parseInt(num, 10)`.
* which us normalized placeholders. This callback has the same
* signature as regular middleware, for example below when ":userId"
* is used this function will be invoked in an attempt to load the user.
*
* When the callback function accepts only a single argument, the
* value of placeholder is passed:
* app.param('userId', function(req, res, next, id){
* User.find(id, function(err, user){
* if (err) {
* next(err);
* } else if (user) {
* req.user = user;
* next();
* } else {
* next(new Error('failed to load user'));
* }
* });
* });
*
* app.param('page', function(n){ return parseInt(n, 10); });
*
* After which "/users/:page" would automatically provide us with
* an integer for `req.params.page`. If desired we could use the callback
* signature shown below, and immediately `next(new Error('invalid page'))`
* when `parseInt` fails.
*
* Alternatively the callback may accept the request, response, next, and
* the value, acting like middlware:
*
* app.param('userId', function(req, res, next, id){
* User.find(id, function(err, user){
* if (err) {
* next(err);
* } else if (user) {
* req.user = user;
* next();
* } else {
* next(new Error('failed to load user'));
* }
* });
* });
*
* Now every time ":userId" is present, the associated user object
* will be loaded and assigned before the route handler is invoked.
*
* @param {String|Array} name
* @param {String|Array|Function} name
* @param {Function} fn

@@ -336,2 +346,3 @@ * @return {Server} for chaining

app.param = function(name, fn){
// array
if (Array.isArray(name)) {

@@ -341,2 +352,6 @@ name.forEach(function(name){

}, this);
// param logic
} else if ('function' == typeof name) {
this.routes.param(name);
// single
} else {

@@ -346,2 +361,3 @@ if (':' == name[0]) name = name.substr(1);

}
return this;

@@ -474,35 +490,43 @@ };

app.configure = function(env, fn){
if ('function' == typeof env) {
fn = env, env = 'all';
}
if ('all' == env || env == this.settings.env) {
fn.call(this);
}
if ('function' == typeof env) fn = env, env = 'all';
if ('all' == env || env == this.settings.env) fn.call(this);
return this;
};
// Generate routing methods
/**
* Delegate `.VERB(...)` calls to `.route(VERB, ...)`.
*/
methods.forEach(function(method){
app[method] = function(path){
var self = this;
if (1 == arguments.length) return this.routes.lookup(method, path);
var args = [method].concat(toArray(arguments));
if (!this.__usedRouter) this.use(this.router);
return this.routes._route.apply(this.routes, args);
}
});
// Lookup
if (1 == arguments.length) {
return this.router.lookup(path, 'all' == method
? null
: method);
}
/**
* Special-cased "all" method, applying the given route `path`,
* middleware, and callback to _every_ HTTP method.
*
* @param {String} path
* @param {Function} ...
* @return {Server} for chaining
* @api public
*/
// Ensure router is mounted
if (!this.__usedRouter) this.use(this.router);
app.all = function(path){
var args = arguments;
if (1 == args.length) return this.routes.lookup('all', path);
methods.forEach(function(method){
if ('all' == method) return;
app[method].apply(this, args);
}, this);
return this;
};
// Generate the route
this.routes[method].apply(this, arguments);
return this;
};
});
// del -> delete alias
// Alias delete as "del"
app.del = app.delete;
app.del = app.delete;
/*!
* Express - router
* Express - Router
* Copyright(c) 2010 TJ Holowaychuk <tj@vision-media.ca>

@@ -12,256 +12,266 @@ * MIT Licensed

var utils = require('../utils')
var Route = require('./route')
, Collection = require('./collection')
, utils = require('../utils')
, parse = require('url').parse
, _methods = require('./methods')
, Route = require('./route');
, toArray = utils.toArray;
/**
* Expose router.
* Expose `Router` constructor.
*/
exports = module.exports = router;
exports = module.exports = Router;
/**
* Expose methods.
* Expose HTTP methods.
*/
exports.methods = _methods;
var methods = exports.methods = require('./methods');
/**
* Provides Sinatra-like routing capabilities.
*
* @param {Function} fn
* @return {Function}
* Initialize a new `Router` with the given `app`.
*
* @param {express.HTTPServer} app
* @api private
*/
function router(fn, app){
var self = this
, methods = {}
, routes = {}
, params = {};
function Router(app) {
var self = this;
this.app = app;
this.routes = {};
this.params = {};
this._params = [];
if (!fn) throw new Error('router provider requires a callback function');
this.middleware = function(req, res, next){
self._dispatch(req, res, next);
};
}
// Generate method functions
_methods.forEach(function(method){
methods[method] = generateMethodFunction(method.toUpperCase());
});
/**
* Register a param callback `fn` for the given `name`.
*
* @param {String|Function} name
* @param {Function} fn
* @return {Router} for chaining
* @api public
*/
// Alias del -> delete
methods.del = methods.delete;
Router.prototype.param = function(name, fn){
// param logic
if ('function' == typeof name) {
this._params.push(name);
return;
}
// Apply callback to all methods
methods.all = function(){
var args = arguments;
_methods.forEach(function(name){
methods[name].apply(this, args);
});
return self;
};
// apply param functions
var params = this._params
, len = params.length
, ret;
// Register param callback
methods.param = function(name, fn){
params[name] = fn;
};
fn.call(this, methods);
for (var i = 0; i < len; ++i) {
if (ret = params[i](name, fn)) {
fn = ret;
}
}
function generateMethodFunction(name) {
var localRoutes = routes[name] = routes[name] || [];
return function(path, fn){
var keys = []
, middleware = [];
// ensure we end up with a
// middleware function
if ('function' != typeof fn) {
throw new Error('invalid param() call for ' + name + ', got ' + fn);
}
// slice middleware
if (arguments.length > 2) {
middleware = Array.prototype.slice.call(arguments, 1, arguments.length);
fn = middleware.pop();
middleware = utils.flatten(middleware);
}
this.params[name] = fn;
return this;
};
if (!path) throw new Error(name + ' route requires a path');
if (!fn) throw new Error(name + ' route ' + path + ' requires a callback');
/**
* Remove the given `route`, returns
* a bool indicating if the route was present
* or not.
*
* @param {Route} route
* @return {Boolean}
* @api public
*/
var options = { sensitive: app.enabled('case sensitive routes') };
var route = new Route(name, path, fn, options);
route.middleware = middleware;
localRoutes.push(route);
return self;
};
Router.prototype.remove = function(route){
var routes = this.routes[route.method]
, len = routes.length;
for (var i = 0; i < len; ++i) {
if (route == routes[i]) {
routes.splice(i, 1);
return true;
}
}
};
function router(req, res, next){
var route
, self = this;
/**
* Return routes with route paths matching `path`.
*
* @param {String} method
* @param {String} path
* @return {Collection}
* @api public
*/
(function pass(i){
if (route = match(req, routes, i)) {
var i = 0
, keys = route.keys;
Router.prototype.lookup = function(method, path){
return this.find(function(route){
return path == route.path
&& (route.method == method
|| method == 'all');
});
};
req.params = route.params;
/**
* Return routes with regexps that match the given `url`.
*
* @param {String} method
* @param {String} url
* @return {Collection}
* @api public
*/
// Param preconditions
(function param(err) {
try {
var key = keys[i++]
, val = req.params[key]
, fn = params[key];
Router.prototype.match = function(method, url){
return this.find(function(route){
return route.match(url)
&& (route.method == method
|| method == 'all');
});
};
if ('route' == err) {
pass(req._route_index + 1);
// Error
} else if (err) {
next(err);
// Param has callback
} else if (fn) {
// Return style
if (1 == fn.length) {
req.params[key] = fn(val);
param();
// Middleware style
} else {
fn(req, res, param, val);
}
// Finished processing params
} else if (!key) {
// route middleware
i = 0;
(function nextMiddleware(err){
var fn = route.middleware[i++];
if ('route' == err) {
pass(req._route_index + 1);
} else if (err) {
next(err);
} else if (fn) {
fn(req, res, nextMiddleware);
} else {
route.callback.call(self, req, res, function(err){
if (err) {
next(err);
} else {
pass(req._route_index + 1);
}
});
}
})();
// More params
} else {
param();
}
} catch (err) {
next(err);
}
})();
} else if ('OPTIONS' == req.method) {
options(req, res, routes);
} else {
next();
}
})();
};
/**
* Find routes based on the return value of `fn`
* which is invoked once per route.
*
* @param {Function} fn
* @return {Collection}
* @api public
*/
router.remove = function(path, method, ret){
var ret = ret || []
, route;
Router.prototype.find = function(fn){
var len = methods.length
, ret = new Collection(this)
, method
, routes
, route;
// method specific remove
if (method) {
method = method.toUpperCase();
if (routes[method]) {
for (var i = 0; i < routes[method].length; ++i) {
route = routes[method][i];
if (path == route.path) {
route.index = i;
routes[method].splice(i, 1);
ret.push(route);
--i;
}
}
}
// global remove
} else {
_methods.forEach(function(method){
router.remove(path, method, ret);
});
for (var i = 0; i < len; ++i) {
method = methods[i];
routes = this.routes[method];
if (!routes) continue;
for (var j = 0, jlen = routes.length; j < jlen; ++j) {
route = routes[j];
if (fn(route)) ret.push(route);
}
}
return ret;
};
return ret;
};
router.lookup = function(path, method, ret){
ret = ret || [];
/**
* Route dispatcher aka the route "middleware".
*
* @param {IncomingMessage} req
* @param {ServerResponse} res
* @param {Function} next
* @api private
*/
// method specific lookup
if (method) {
method = method.toUpperCase();
if (routes[method]) {
routes[method].forEach(function(route, i){
if (path == route.path) {
route.index = i;
ret.push(route);
}
});
}
// global lookup
} else {
_methods.forEach(function(method){
router.lookup(path, method, ret);
});
Router.prototype._dispatch = function(req, res, next){
var params = this.params
, self = this;
// route dispatch
(function pass(i){
var route
, keys
, ret;
// match next route
function nextRoute() {
pass(req._route_index + 1);
}
return ret;
};
// match route
route = self._match(req, i);
router.match = function(url, method, ret){
var ret = ret || []
, i = 0
, route
, req;
// implied OPTIONS
if ('OPTIONS' == req.method) return self._options(req, res);
// method specific matches
if (method) {
method = method.toUpperCase();
req = { url: url, method: method };
while (route = match(req, routes, i)) {
i = req._route_index + 1;
route.index = i;
ret.push(route);
}
// global matches
} else {
_methods.forEach(function(method){
router.match(url, method, ret);
});
}
// no route
if (!route) return next();
return ret;
};
// we have a route
// start at param 0
req.params = route.params;
keys = route.keys;
i = 0;
router.routes = routes;
(function param(err) {
var key = keys[i++]
, val = req.params[key]
, fn = params[key]
, ret;
return router;
}
try {
if ('route' == err) {
nextRoute();
} else if (err) {
next(err);
} else if (fn) {
fn(req, res, param, val);
} else if (key) {
param();
} else {
i = 0;
middleware();
}
} catch (err) {
next(err);
}
})();
// invoke route middleware
function middleware(err) {
var fn = route.middleware[i++];
if ('route' == err) {
nextRoute();
} else if (err) {
next(err);
} else if (fn) {
fn(req, res, middleware);
} else {
done();
}
};
// invoke middleware callback
function done() {
route.callback.call(self, req, res, function(err){
if (err) return next(err);
pass(req._route_index + 1);
});
}
})(0);
};
/**
* Respond to OPTIONS.
* Respond to __OPTIONS__ method.
*
* @param {ServerRequest} req
* @param {ServerResponse} req
* @param {Array} routes
* @param {IncomingMessage} req
* @param {ServerResponse} res
* @api private
*/
function options(req, res, routes) {
var pathname = parse(req.url).pathname
, body = optionsFor(pathname, routes).join(',');
Router.prototype._options = function(req, res){
var path = parse(req.url).pathname
, body = this._optionsFor(path).join(',');
res.send(body, { Allow: body });
}
};
/**
* Return OPTIONS array for the given `path`, matching `routes`.
* Return an array of HTTP verbs or "options" for `path`.
*
* @param {String} path
* @param {Array} routes
* @return {Array}

@@ -271,7 +281,9 @@ * @api private

function optionsFor(path, routes) {
return _methods.filter(function(method){
var arr = routes[method.toUpperCase()];
for (var i = 0, len = arr.length; i < len; ++i) {
if (arr[i].regexp.test(path)) return true;
Router.prototype._optionsFor = function(path){
var self = this;
return methods.filter(function(method){
var routes = self.routes[method];
if (!routes) return;
for (var i = 0, len = routes.length; i < len; ++i) {
if (routes[i].match(path)) return true;
}

@@ -281,41 +293,40 @@ }).map(function(method){

});
}
};
/**
* Attempt to match the given request to
* one of the routes. When successful
* a route function is returned.
* Attempt to match a route for `req`
* starting from offset `i`.
*
* @param {ServerRequest} req
* @param {Object} routes
* @return {Function}
* @param {IncomingMessage} req
* @param {Number} i
* @return {Route}
* @api private
*/
function match(req, routes, i) {
var captures
, method = req.method
, i = i || 0;
Router.prototype._match = function(req, i){
var method = req.method.toLowerCase()
, url = parse(req.url)
, path = url.pathname
, routes = this.routes
, captures
, route
, keys;
// pass HEAD to GET routes
if ('HEAD' == method) method = 'GET';
if ('head' == method) method = 'get';
// routes for this method
if (routes = routes[method]) {
var url = parse(req.url)
, pathname = url.pathname;
// matching routes
for (var len = routes.length; i < len; ++i) {
var route = routes[i]
, fn = route.callback
, path = route.regexp
, keys = route.keys;
route = routes[i];
if (captures = route.match(path)) {
keys = route.keys;
route.params = [];
// match
if (captures = path.exec(pathname)) {
route.params = [];
for (var j = 1, l = captures.length; j < l; ++j) {
var key = keys[j-1],
val = 'string' == typeof captures[j]
// params from capture groups
for (var j = 1, jlen = captures.length; j < jlen; ++j) {
var key = keys[j-1]
, val = 'string' == typeof captures[j]
? decodeURIComponent(captures[j])

@@ -329,2 +340,4 @@ : captures[j];

}
// all done
req._route_index = i;

@@ -335,2 +348,41 @@ return route;

}
}
};
/**
* Route `method`, `path`, and optional middleware
* to the callback `fn`.
*
* @param {String} method
* @param {String} path
* @param {Function} ...
* @param {Function} fn
* @return {Router} for chaining
* @api private
*/
Router.prototype._route = function(method, path, fn){
var app = this.app
, middleware = [];
// slice middleware
if (arguments.length > 3) {
middleware = toArray(arguments, 2);
fn = middleware.pop();
middleware = utils.flatten(middleware);
}
// ensure path and callback are given
if (!path) throw new Error(method + 'route requires a path');
if (!fn) throw new Error(method + ' route ' + path + ' requires a callback');
// create the route
var route = new Route(method, path, fn, {
sensitive: app.enabled('case sensitive routes')
, middleware: middleware
});
// add it
(this.routes[method] = this.routes[method] || [])
.push(route);
return this;
};

@@ -21,2 +21,3 @@

* - `sensitive` enable case-sensitive routes
* - `middleware` array of middleware
*

@@ -34,7 +35,20 @@ * @param {String} method

this.path = path;
this.method = method;
this.regexp = normalize(path, this.keys = [], options.sensitive);
this.method = method;
this.middleware = options.middleware;
}
/**
* Check if this route matches `path` and return captures made.
*
* @param {String} path
* @return {Array}
* @api private
*/
Route.prototype.match = function(path){
return this.regexp.exec(path);
};
/**
* Normalize the given path string,

@@ -41,0 +55,0 @@ * returning a regular expression.

@@ -123,1 +123,18 @@

};
/**
* Fast alternative to `Array.prototype.slice.call()`.
*
* @param {Arguments} args
* @param {Number} n
* @return {Array}
* @api public
*/
exports.toArray = function(args, i){
var arr = []
, len = args.length
, i = i || 0;
for (; i < len; ++i) arr.push(args[i]);
return arr;
};
{
"name": "express",
"description": "Sinatra inspired web development framework",
"version": "2.3.6",
"version": "2.3.7",
"author": "TJ Holowaychuk <tj@vision-media.ca>",

@@ -6,0 +6,0 @@ "contributors": [

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap

Packages

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc