diff --git a/benchmarks/Makefile b/benchmarks/Makefile deleted file mode 100644 index ed1ddfc4..00000000 --- a/benchmarks/Makefile +++ /dev/null @@ -1,17 +0,0 @@ - -all: - @./run 1 middleware 50 - @./run 5 middleware 50 - @./run 10 middleware 50 - @./run 15 middleware 50 - @./run 20 middleware 50 - @./run 30 middleware 50 - @./run 50 middleware 50 - @./run 100 middleware 50 - @./run 10 middleware 100 - @./run 10 middleware 250 - @./run 10 middleware 500 - @./run 10 middleware 1000 - @echo - -.PHONY: all diff --git a/benchmarks/middleware.js b/benchmarks/middleware.js deleted file mode 100644 index fed97ba8..00000000 --- a/benchmarks/middleware.js +++ /dev/null @@ -1,20 +0,0 @@ - -var express = require('..'); -var app = express(); - -// number of middleware - -var n = parseInt(process.env.MW || '1', 10); -console.log(' %s middleware', n); - -while (n--) { - app.use(function(req, res, next){ - next(); - }); -} - -app.use(function(req, res){ - res.send('Hello World') -}); - -app.listen(3333); diff --git a/benchmarks/run b/benchmarks/run deleted file mode 100755 index ec8f55d5..00000000 --- a/benchmarks/run +++ /dev/null @@ -1,18 +0,0 @@ -#!/usr/bin/env bash - -echo -MW=$1 node $2 & -pid=$! - -echo " $3 connections" - -sleep 2 - -wrk 'http://localhost:3333/?foo[bar]=baz' \ - -d 3 \ - -c $3 \ - -t 8 \ - | grep 'Requests/sec\|Latency' \ - | awk '{ print " " $2 }' - -kill $pid diff --git a/examples/auth/index.js b/examples/auth/index.js index 36205d0f..8ccb0b8a 100644 --- a/examples/auth/index.js +++ b/examples/auth/index.js @@ -4,7 +4,7 @@ * Module dependencies. */ -var express = require('../..'); +var express = require('express'); var hash = require('pbkdf2-password')() var path = require('path'); var session = require('express-session'); diff --git a/examples/content-negotiation/index.js b/examples/content-negotiation/index.js index 280a4e22..76879da2 100644 --- a/examples/content-negotiation/index.js +++ b/examples/content-negotiation/index.js @@ -1,6 +1,6 @@ 'use strict' -var express = require('../../'); +var express = require('express'); var app = module.exports = express(); var users = require('./db'); diff --git a/examples/cookie-sessions/index.js b/examples/cookie-sessions/index.js index 01c731c1..70526211 100644 --- a/examples/cookie-sessions/index.js +++ b/examples/cookie-sessions/index.js @@ -5,7 +5,7 @@ */ var cookieSession = require('cookie-session'); -var express = require('../../'); +var express = require('express'); var app = module.exports = express(); diff --git a/examples/cookies/index.js b/examples/cookies/index.js index 04093591..7e53cf70 100644 --- a/examples/cookies/index.js +++ b/examples/cookies/index.js @@ -4,7 +4,7 @@ * Module dependencies. */ -var express = require('../../'); +var express = require('express'); var app = module.exports = express(); var logger = require('morgan'); var cookieParser = require('cookie-parser'); diff --git a/examples/downloads/index.js b/examples/downloads/index.js index 6b67e0c8..adbe5576 100644 --- a/examples/downloads/index.js +++ b/examples/downloads/index.js @@ -4,7 +4,7 @@ * Module dependencies. */ -var express = require('../../'); +var express = require('express'); var path = require('path'); var app = module.exports = express(); diff --git a/examples/ejs/index.js b/examples/ejs/index.js index a39d805a..9a17a135 100644 --- a/examples/ejs/index.js +++ b/examples/ejs/index.js @@ -4,7 +4,7 @@ * Module dependencies. */ -var express = require('../../'); +var express = require('express'); var path = require('path'); var app = module.exports = express(); diff --git a/examples/error-pages/index.js b/examples/error-pages/index.js index efa815c4..3eb0df40 100644 --- a/examples/error-pages/index.js +++ b/examples/error-pages/index.js @@ -4,7 +4,7 @@ * Module dependencies. */ -var express = require('../../'); +var express = require('express'); var path = require('path'); var app = module.exports = express(); var logger = require('morgan'); diff --git a/examples/error/index.js b/examples/error/index.js index d733a811..6705311d 100644 --- a/examples/error/index.js +++ b/examples/error/index.js @@ -4,7 +4,7 @@ * Module dependencies. */ -var express = require('../../'); +var express = require('express'); var logger = require('morgan'); var app = module.exports = express(); var test = app.get('env') === 'test' diff --git a/examples/hello-world/index.js b/examples/hello-world/index.js index 8c1855c2..6dec3723 100644 --- a/examples/hello-world/index.js +++ b/examples/hello-world/index.js @@ -1,6 +1,6 @@ 'use strict' -var express = require('../../'); +var express = require('express'); var app = module.exports = express() diff --git a/examples/markdown/index.js b/examples/markdown/index.js index 23d645e6..4bc0b4db 100644 --- a/examples/markdown/index.js +++ b/examples/markdown/index.js @@ -5,7 +5,7 @@ */ var escapeHtml = require('escape-html'); -var express = require('../..'); +var express = require('express'); var fs = require('fs'); var marked = require('marked'); var path = require('path'); diff --git a/examples/multi-router/controllers/api_v1.js b/examples/multi-router/controllers/api_v1.js index a301e3ee..89e2510a 100644 --- a/examples/multi-router/controllers/api_v1.js +++ b/examples/multi-router/controllers/api_v1.js @@ -1,6 +1,6 @@ 'use strict' -var express = require('../../..'); +var express = require('express'); var apiv1 = express.Router(); diff --git a/examples/multi-router/controllers/api_v2.js b/examples/multi-router/controllers/api_v2.js index e997fb1f..f8d0202f 100644 --- a/examples/multi-router/controllers/api_v2.js +++ b/examples/multi-router/controllers/api_v2.js @@ -1,6 +1,6 @@ 'use strict' -var express = require('../../..'); +var express = require('express'); var apiv2 = express.Router(); diff --git a/examples/multi-router/index.js b/examples/multi-router/index.js index dbfd2841..755757c7 100644 --- a/examples/multi-router/index.js +++ b/examples/multi-router/index.js @@ -1,6 +1,6 @@ 'use strict' -var express = require('../..'); +var express = require('express'); var app = module.exports = express(); diff --git a/examples/multipart/index.js b/examples/multipart/index.js index ea7b86e0..cfdba905 100644 --- a/examples/multipart/index.js +++ b/examples/multipart/index.js @@ -4,7 +4,7 @@ * Module dependencies. */ -var express = require('../..'); +var express = require('express'); var multiparty = require('multiparty'); var format = require('util').format; diff --git a/examples/mvc/index.js b/examples/mvc/index.js index da4727b2..89084a75 100644 --- a/examples/mvc/index.js +++ b/examples/mvc/index.js @@ -4,7 +4,7 @@ * Module dependencies. */ -var express = require('../..'); +var express = require('express'); var logger = require('morgan'); var path = require('path'); var session = require('express-session'); diff --git a/examples/mvc/lib/boot.js b/examples/mvc/lib/boot.js index 0216e5d7..f42a7181 100644 --- a/examples/mvc/lib/boot.js +++ b/examples/mvc/lib/boot.js @@ -4,7 +4,7 @@ * Module dependencies. */ -var express = require('../../..'); +var express = require('express'); var fs = require('fs'); var path = require('path'); diff --git a/examples/online/index.js b/examples/online/index.js index 0b5fdffc..25606631 100644 --- a/examples/online/index.js +++ b/examples/online/index.js @@ -11,7 +11,7 @@ * Module dependencies. */ -var express = require('../..'); +var express = require('express'); var online = require('online'); var redis = require('redis'); var db = redis.createClient(); diff --git a/examples/params/index.js b/examples/params/index.js index f3cd8457..b5a7fc04 100644 --- a/examples/params/index.js +++ b/examples/params/index.js @@ -5,7 +5,7 @@ */ var createError = require('http-errors') -var express = require('../../'); +var express = require('express'); var app = module.exports = express(); // Faux database diff --git a/examples/resource/index.js b/examples/resource/index.js index ff1f6fe1..e38fe199 100644 --- a/examples/resource/index.js +++ b/examples/resource/index.js @@ -4,7 +4,7 @@ * Module dependencies. */ -var express = require('../../'); +var express = require('express'); var app = module.exports = express(); diff --git a/examples/route-map/index.js b/examples/route-map/index.js index 2bc28bd4..a154888b 100644 --- a/examples/route-map/index.js +++ b/examples/route-map/index.js @@ -5,7 +5,7 @@ */ var escapeHtml = require('escape-html') -var express = require('../../lib/express'); +var express = require('express'); var verbose = process.env.NODE_ENV !== 'test' diff --git a/examples/route-middleware/index.js b/examples/route-middleware/index.js index 44ec13a9..d4ca126d 100644 --- a/examples/route-middleware/index.js +++ b/examples/route-middleware/index.js @@ -4,7 +4,7 @@ * Module dependencies. */ -var express = require('../../lib/express'); +var express = require('express'); var app = express(); diff --git a/examples/route-separation/index.js b/examples/route-separation/index.js index 5d483811..0952e80d 100644 --- a/examples/route-separation/index.js +++ b/examples/route-separation/index.js @@ -4,7 +4,7 @@ * Module dependencies. */ -var express = require('../..'); +var express = require('express'); var path = require('path'); var app = express(); var logger = require('morgan'); diff --git a/examples/search/index.js b/examples/search/index.js index 0d19444e..cc6fa654 100644 --- a/examples/search/index.js +++ b/examples/search/index.js @@ -11,7 +11,7 @@ * Module dependencies. */ -var express = require('../..'); +var express = require('express'); var path = require('path'); var redis = require('redis'); diff --git a/examples/session/index.js b/examples/session/index.js index 2bb2b109..91417d71 100644 --- a/examples/session/index.js +++ b/examples/session/index.js @@ -7,7 +7,7 @@ // $ npm install redis // $ redis-server -var express = require('../..'); +var express = require('express'); var session = require('express-session'); var app = express(); diff --git a/examples/session/redis.js b/examples/session/redis.js index bbbdc7fd..a6183cca 100644 --- a/examples/session/redis.js +++ b/examples/session/redis.js @@ -4,7 +4,7 @@ * Module dependencies. */ -var express = require('../..'); +var express = require('express'); var logger = require('morgan'); var session = require('express-session'); diff --git a/examples/static-files/index.js b/examples/static-files/index.js index 609c546b..3981475e 100644 --- a/examples/static-files/index.js +++ b/examples/static-files/index.js @@ -4,7 +4,7 @@ * Module dependencies. */ -var express = require('../..'); +var express = require('express'); var logger = require('morgan'); var path = require('path'); var app = express(); diff --git a/examples/vhost/index.js b/examples/vhost/index.js index a9499356..d193198b 100644 --- a/examples/vhost/index.js +++ b/examples/vhost/index.js @@ -4,7 +4,7 @@ * Module dependencies. */ -var express = require('../..'); +var express = require('express'); var logger = require('morgan'); var vhost = require('vhost'); diff --git a/examples/view-constructor/index.js b/examples/view-constructor/index.js index 3d673670..1cd1d047 100644 --- a/examples/view-constructor/index.js +++ b/examples/view-constructor/index.js @@ -4,7 +4,7 @@ * Module dependencies. */ -var express = require('../../'); +var express = require('express'); var GithubView = require('./github-view'); var md = require('marked').parse; diff --git a/examples/view-locals/index.js b/examples/view-locals/index.js index a2af24f3..1ec01dbb 100644 --- a/examples/view-locals/index.js +++ b/examples/view-locals/index.js @@ -4,7 +4,7 @@ * Module dependencies. */ -var express = require('../..'); +var express = require('express'); var path = require('path'); var User = require('./user'); var app = express(); diff --git a/examples/web-service/index.js b/examples/web-service/index.js index d1a90362..273de43b 100644 --- a/examples/web-service/index.js +++ b/examples/web-service/index.js @@ -4,7 +4,7 @@ * Module dependencies. */ -var express = require('../../'); +var express = require('express'); var app = module.exports = express(); diff --git a/index.js b/index.js deleted file mode 100644 index d219b0c8..00000000 --- a/index.js +++ /dev/null @@ -1,11 +0,0 @@ -/*! - * express - * Copyright(c) 2009-2013 TJ Holowaychuk - * Copyright(c) 2013 Roman Shtylman - * Copyright(c) 2014-2015 Douglas Christopher Wilson - * MIT Licensed - */ - -'use strict'; - -module.exports = require('./lib/express'); diff --git a/lib/application.js b/lib/application.js deleted file mode 100644 index ebb30b51..00000000 --- a/lib/application.js +++ /dev/null @@ -1,661 +0,0 @@ -/*! - * express - * Copyright(c) 2009-2013 TJ Holowaychuk - * Copyright(c) 2013 Roman Shtylman - * Copyright(c) 2014-2015 Douglas Christopher Wilson - * MIT Licensed - */ - -'use strict'; - -/** - * Module dependencies. - * @private - */ - -var finalhandler = require('finalhandler'); -var Router = require('./router'); -var methods = require('methods'); -var middleware = require('./middleware/init'); -var query = require('./middleware/query'); -var debug = require('debug')('express:application'); -var View = require('./view'); -var http = require('http'); -var compileETag = require('./utils').compileETag; -var compileQueryParser = require('./utils').compileQueryParser; -var compileTrust = require('./utils').compileTrust; -var deprecate = require('depd')('express'); -var flatten = require('array-flatten'); -var merge = require('utils-merge'); -var resolve = require('path').resolve; -var setPrototypeOf = require('setprototypeof') - -/** - * Module variables. - * @private - */ - -var hasOwnProperty = Object.prototype.hasOwnProperty -var slice = Array.prototype.slice; - -/** - * Application prototype. - */ - -var app = exports = module.exports = {}; - -/** - * Variable for trust proxy inheritance back-compat - * @private - */ - -var trustProxyDefaultSymbol = '@@symbol:trust_proxy_default'; - -/** - * Initialize the server. - * - * - setup default configuration - * - setup default middleware - * - setup route reflection methods - * - * @private - */ - -app.init = function init() { - this.cache = {}; - this.engines = {}; - this.settings = {}; - - this.defaultConfiguration(); -}; - -/** - * Initialize application configuration. - * @private - */ - -app.defaultConfiguration = function defaultConfiguration() { - var env = process.env.NODE_ENV || 'development'; - - // default settings - this.enable('x-powered-by'); - this.set('etag', 'weak'); - this.set('env', env); - this.set('query parser', 'extended'); - this.set('subdomain offset', 2); - this.set('trust proxy', false); - - // trust proxy inherit back-compat - Object.defineProperty(this.settings, trustProxyDefaultSymbol, { - configurable: true, - value: true - }); - - debug('booting in %s mode', env); - - this.on('mount', function onmount(parent) { - // inherit trust proxy - if (this.settings[trustProxyDefaultSymbol] === true - && typeof parent.settings['trust proxy fn'] === 'function') { - delete this.settings['trust proxy']; - delete this.settings['trust proxy fn']; - } - - // inherit protos - setPrototypeOf(this.request, parent.request) - setPrototypeOf(this.response, parent.response) - setPrototypeOf(this.engines, parent.engines) - setPrototypeOf(this.settings, parent.settings) - }); - - // setup locals - this.locals = Object.create(null); - - // top-most app is mounted at / - this.mountpath = '/'; - - // default locals - this.locals.settings = this.settings; - - // default configuration - this.set('view', View); - this.set('views', resolve('views')); - this.set('jsonp callback name', 'callback'); - - if (env === 'production') { - this.enable('view cache'); - } - - Object.defineProperty(this, 'router', { - get: function() { - throw new Error('\'app.router\' is deprecated!\nPlease see the 3.x to 4.x migration guide for details on how to update your app.'); - } - }); -}; - -/** - * lazily adds the base router if it has not yet been added. - * - * We cannot add the base router in the defaultConfiguration because - * it reads app settings which might be set after that has run. - * - * @private - */ -app.lazyrouter = function lazyrouter() { - if (!this._router) { - this._router = new Router({ - caseSensitive: this.enabled('case sensitive routing'), - strict: this.enabled('strict routing') - }); - - this._router.use(query(this.get('query parser fn'))); - this._router.use(middleware.init(this)); - } -}; - -/** - * Dispatch a req, res pair into the application. Starts pipeline processing. - * - * If no callback is provided, then default error handlers will respond - * in the event of an error bubbling through the stack. - * - * @private - */ - -app.handle = function handle(req, res, callback) { - var router = this._router; - - // final handler - var done = callback || finalhandler(req, res, { - env: this.get('env'), - onerror: logerror.bind(this) - }); - - // no routes - if (!router) { - debug('no routes defined on app'); - done(); - return; - } - - router.handle(req, res, done); -}; - -/** - * Proxy `Router#use()` to add middleware to the app router. - * See Router#use() documentation for details. - * - * If the _fn_ parameter is an express app, then it will be - * mounted at the _route_ specified. - * - * @public - */ - -app.use = function use(fn) { - var offset = 0; - var path = '/'; - - // default path to '/' - // disambiguate app.use([fn]) - if (typeof fn !== 'function') { - var arg = fn; - - while (Array.isArray(arg) && arg.length !== 0) { - arg = arg[0]; - } - - // first arg is the path - if (typeof arg !== 'function') { - offset = 1; - path = fn; - } - } - - var fns = flatten(slice.call(arguments, offset)); - - if (fns.length === 0) { - throw new TypeError('app.use() requires a middleware function') - } - - // setup router - this.lazyrouter(); - var router = this._router; - - fns.forEach(function (fn) { - // non-express app - if (!fn || !fn.handle || !fn.set) { - return router.use(path, fn); - } - - debug('.use app under %s', path); - fn.mountpath = path; - fn.parent = this; - - // restore .app property on req and res - router.use(path, function mounted_app(req, res, next) { - var orig = req.app; - fn.handle(req, res, function (err) { - setPrototypeOf(req, orig.request) - setPrototypeOf(res, orig.response) - next(err); - }); - }); - - // mounted an app - fn.emit('mount', this); - }, this); - - return this; -}; - -/** - * Proxy to the app `Router#route()` - * Returns a new `Route` instance for the _path_. - * - * Routes are isolated middleware stacks for specific paths. - * See the Route api docs for details. - * - * @public - */ - -app.route = function route(path) { - this.lazyrouter(); - return this._router.route(path); -}; - -/** - * Register the given template engine callback `fn` - * as `ext`. - * - * By default will `require()` the engine based on the - * file extension. For example if you try to render - * a "foo.ejs" file Express will invoke the following internally: - * - * app.engine('ejs', require('ejs').__express); - * - * For engines that do not provide `.__express` out of the box, - * or if you wish to "map" a different extension to the template engine - * you may use this method. For example mapping the EJS template engine to - * ".html" files: - * - * app.engine('html', require('ejs').renderFile); - * - * In this case EJS provides a `.renderFile()` method with - * the same signature that Express expects: `(path, options, callback)`, - * though note that it aliases this method as `ejs.__express` internally - * so if you're using ".ejs" extensions you don't need to do anything. - * - * Some template engines do not follow this convention, the - * [Consolidate.js](https://github.com/tj/consolidate.js) - * library was created to map all of node's popular template - * engines to follow this convention, thus allowing them to - * work seamlessly within Express. - * - * @param {String} ext - * @param {Function} fn - * @return {app} for chaining - * @public - */ - -app.engine = function engine(ext, fn) { - if (typeof fn !== 'function') { - throw new Error('callback function required'); - } - - // get file extension - var extension = ext[0] !== '.' - ? '.' + ext - : ext; - - // store engine - this.engines[extension] = fn; - - return this; -}; - -/** - * Proxy to `Router#param()` with one added api feature. The _name_ parameter - * can be an array of names. - * - * See the Router#param() docs for more details. - * - * @param {String|Array} name - * @param {Function} fn - * @return {app} for chaining - * @public - */ - -app.param = function param(name, fn) { - this.lazyrouter(); - - if (Array.isArray(name)) { - for (var i = 0; i < name.length; i++) { - this.param(name[i], fn); - } - - return this; - } - - this._router.param(name, fn); - - return this; -}; - -/** - * Assign `setting` to `val`, or return `setting`'s value. - * - * app.set('foo', 'bar'); - * app.set('foo'); - * // => "bar" - * - * Mounted servers inherit their parent server's settings. - * - * @param {String} setting - * @param {*} [val] - * @return {Server} for chaining - * @public - */ - -app.set = function set(setting, val) { - if (arguments.length === 1) { - // app.get(setting) - var settings = this.settings - - while (settings && settings !== Object.prototype) { - if (hasOwnProperty.call(settings, setting)) { - return settings[setting] - } - - settings = Object.getPrototypeOf(settings) - } - - return undefined - } - - debug('set "%s" to %o', setting, val); - - // set value - this.settings[setting] = val; - - // trigger matched settings - switch (setting) { - case 'etag': - this.set('etag fn', compileETag(val)); - break; - case 'query parser': - this.set('query parser fn', compileQueryParser(val)); - break; - case 'trust proxy': - this.set('trust proxy fn', compileTrust(val)); - - // trust proxy inherit back-compat - Object.defineProperty(this.settings, trustProxyDefaultSymbol, { - configurable: true, - value: false - }); - - break; - } - - return this; -}; - -/** - * Return the app's absolute pathname - * based on the parent(s) that have - * mounted it. - * - * For example if the application was - * mounted as "/admin", which itself - * was mounted as "/blog" then the - * return value would be "/blog/admin". - * - * @return {String} - * @private - */ - -app.path = function path() { - return this.parent - ? this.parent.path() + this.mountpath - : ''; -}; - -/** - * Check if `setting` is enabled (truthy). - * - * app.enabled('foo') - * // => false - * - * app.enable('foo') - * app.enabled('foo') - * // => true - * - * @param {String} setting - * @return {Boolean} - * @public - */ - -app.enabled = function enabled(setting) { - return Boolean(this.set(setting)); -}; - -/** - * Check if `setting` is disabled. - * - * app.disabled('foo') - * // => true - * - * app.enable('foo') - * app.disabled('foo') - * // => false - * - * @param {String} setting - * @return {Boolean} - * @public - */ - -app.disabled = function disabled(setting) { - return !this.set(setting); -}; - -/** - * Enable `setting`. - * - * @param {String} setting - * @return {app} for chaining - * @public - */ - -app.enable = function enable(setting) { - return this.set(setting, true); -}; - -/** - * Disable `setting`. - * - * @param {String} setting - * @return {app} for chaining - * @public - */ - -app.disable = function disable(setting) { - return this.set(setting, false); -}; - -/** - * Delegate `.VERB(...)` calls to `router.VERB(...)`. - */ - -methods.forEach(function(method){ - app[method] = function(path){ - if (method === 'get' && arguments.length === 1) { - // app.get(setting) - return this.set(path); - } - - this.lazyrouter(); - - var route = this._router.route(path); - route[method].apply(route, slice.call(arguments, 1)); - return this; - }; -}); - -/** - * Special-cased "all" method, applying the given route `path`, - * middleware, and callback to _every_ HTTP method. - * - * @param {String} path - * @param {Function} ... - * @return {app} for chaining - * @public - */ - -app.all = function all(path) { - this.lazyrouter(); - - var route = this._router.route(path); - var args = slice.call(arguments, 1); - - for (var i = 0; i < methods.length; i++) { - route[methods[i]].apply(route, args); - } - - return this; -}; - -// del -> delete alias - -app.del = deprecate.function(app.delete, 'app.del: Use app.delete instead'); - -/** - * Render the given view `name` name with `options` - * and a callback accepting an error and the - * rendered template string. - * - * Example: - * - * app.render('email', { name: 'Tobi' }, function(err, html){ - * // ... - * }) - * - * @param {String} name - * @param {Object|Function} options or fn - * @param {Function} callback - * @public - */ - -app.render = function render(name, options, callback) { - var cache = this.cache; - var done = callback; - var engines = this.engines; - var opts = options; - var renderOptions = {}; - var view; - - // support callback function as second arg - if (typeof options === 'function') { - done = options; - opts = {}; - } - - // merge app.locals - merge(renderOptions, this.locals); - - // merge options._locals - if (opts._locals) { - merge(renderOptions, opts._locals); - } - - // merge options - merge(renderOptions, opts); - - // set .cache unless explicitly provided - if (renderOptions.cache == null) { - renderOptions.cache = this.enabled('view cache'); - } - - // primed cache - if (renderOptions.cache) { - view = cache[name]; - } - - // view - if (!view) { - var View = this.get('view'); - - view = new View(name, { - defaultEngine: this.get('view engine'), - root: this.get('views'), - engines: engines - }); - - if (!view.path) { - var dirs = Array.isArray(view.root) && view.root.length > 1 - ? 'directories "' + view.root.slice(0, -1).join('", "') + '" or "' + view.root[view.root.length - 1] + '"' - : 'directory "' + view.root + '"' - var err = new Error('Failed to lookup view "' + name + '" in views ' + dirs); - err.view = view; - return done(err); - } - - // prime the cache - if (renderOptions.cache) { - cache[name] = view; - } - } - - // render - tryRender(view, renderOptions, done); -}; - -/** - * Listen for connections. - * - * A node `http.Server` is returned, with this - * application (which is a `Function`) as its - * callback. If you wish to create both an HTTP - * and HTTPS server you may do so with the "http" - * and "https" modules as shown here: - * - * var http = require('http') - * , https = require('https') - * , express = require('express') - * , app = express(); - * - * http.createServer(app).listen(80); - * https.createServer({ ... }, app).listen(443); - * - * @return {http.Server} - * @public - */ - -app.listen = function listen() { - var server = http.createServer(this); - return server.listen.apply(server, arguments); -}; - -/** - * Log error using console.error. - * - * @param {Error} err - * @private - */ - -function logerror(err) { - /* istanbul ignore next */ - if (this.get('env') !== 'test') console.error(err.stack || err.toString()); -} - -/** - * Try rendering a view. - * @private - */ - -function tryRender(view, options, callback) { - try { - view.render(options, callback); - } catch (err) { - callback(err); - } -} diff --git a/lib/express.js b/lib/express.js deleted file mode 100644 index d188a16d..00000000 --- a/lib/express.js +++ /dev/null @@ -1,116 +0,0 @@ -/*! - * express - * Copyright(c) 2009-2013 TJ Holowaychuk - * Copyright(c) 2013 Roman Shtylman - * Copyright(c) 2014-2015 Douglas Christopher Wilson - * MIT Licensed - */ - -'use strict'; - -/** - * Module dependencies. - */ - -var bodyParser = require('body-parser') -var EventEmitter = require('events').EventEmitter; -var mixin = require('merge-descriptors'); -var proto = require('./application'); -var Route = require('./router/route'); -var Router = require('./router'); -var req = require('./request'); -var res = require('./response'); - -/** - * Expose `createApplication()`. - */ - -exports = module.exports = createApplication; - -/** - * Create an express application. - * - * @return {Function} - * @api public - */ - -function createApplication() { - var app = function(req, res, next) { - app.handle(req, res, next); - }; - - mixin(app, EventEmitter.prototype, false); - mixin(app, proto, false); - - // expose the prototype that will get set on requests - app.request = Object.create(req, { - app: { configurable: true, enumerable: true, writable: true, value: app } - }) - - // expose the prototype that will get set on responses - app.response = Object.create(res, { - app: { configurable: true, enumerable: true, writable: true, value: app } - }) - - app.init(); - return app; -} - -/** - * Expose the prototypes. - */ - -exports.application = proto; -exports.request = req; -exports.response = res; - -/** - * Expose constructors. - */ - -exports.Route = Route; -exports.Router = Router; - -/** - * Expose middleware - */ - -exports.json = bodyParser.json -exports.query = require('./middleware/query'); -exports.raw = bodyParser.raw -exports.static = require('serve-static'); -exports.text = bodyParser.text -exports.urlencoded = bodyParser.urlencoded - -/** - * Replace removed middleware with an appropriate error message. - */ - -var removedMiddlewares = [ - 'bodyParser', - 'compress', - 'cookieSession', - 'session', - 'logger', - 'cookieParser', - 'favicon', - 'responseTime', - 'errorHandler', - 'timeout', - 'methodOverride', - 'vhost', - 'csrf', - 'directory', - 'limit', - 'multipart', - 'staticCache' -] - -removedMiddlewares.forEach(function (name) { - Object.defineProperty(exports, name, { - get: function () { - throw new Error('Most middleware (like ' + name + ') is no longer bundled with Express and must be installed separately. Please see https://github.com/senchalabs/connect#middleware.'); - }, - configurable: true - }); -}); diff --git a/lib/middleware/init.js b/lib/middleware/init.js deleted file mode 100644 index dfd04274..00000000 --- a/lib/middleware/init.js +++ /dev/null @@ -1,43 +0,0 @@ -/*! - * express - * Copyright(c) 2009-2013 TJ Holowaychuk - * Copyright(c) 2013 Roman Shtylman - * Copyright(c) 2014-2015 Douglas Christopher Wilson - * MIT Licensed - */ - -'use strict'; - -/** - * Module dependencies. - * @private - */ - -var setPrototypeOf = require('setprototypeof') - -/** - * Initialization middleware, exposing the - * request and response to each other, as well - * as defaulting the X-Powered-By header field. - * - * @param {Function} app - * @return {Function} - * @api private - */ - -exports.init = function(app){ - return function expressInit(req, res, next){ - if (app.enabled('x-powered-by')) res.setHeader('X-Powered-By', 'Express'); - req.res = res; - res.req = req; - req.next = next; - - setPrototypeOf(req, app.request) - setPrototypeOf(res, app.response) - - res.locals = res.locals || Object.create(null); - - next(); - }; -}; - diff --git a/lib/middleware/query.js b/lib/middleware/query.js deleted file mode 100644 index 7e916694..00000000 --- a/lib/middleware/query.js +++ /dev/null @@ -1,47 +0,0 @@ -/*! - * express - * Copyright(c) 2009-2013 TJ Holowaychuk - * Copyright(c) 2013 Roman Shtylman - * Copyright(c) 2014-2015 Douglas Christopher Wilson - * MIT Licensed - */ - -'use strict'; - -/** - * Module dependencies. - */ - -var merge = require('utils-merge') -var parseUrl = require('parseurl'); -var qs = require('qs'); - -/** - * @param {Object} options - * @return {Function} - * @api public - */ - -module.exports = function query(options) { - var opts = merge({}, options) - var queryparse = qs.parse; - - if (typeof options === 'function') { - queryparse = options; - opts = undefined; - } - - if (opts !== undefined && opts.allowPrototypes === undefined) { - // back-compat for qs module - opts.allowPrototypes = true; - } - - return function query(req, res, next){ - if (!req.query) { - var val = parseUrl(req).query; - req.query = queryparse(val, opts); - } - - next(); - }; -}; diff --git a/lib/request.js b/lib/request.js deleted file mode 100644 index 3f1eeca6..00000000 --- a/lib/request.js +++ /dev/null @@ -1,525 +0,0 @@ -/*! - * express - * Copyright(c) 2009-2013 TJ Holowaychuk - * Copyright(c) 2013 Roman Shtylman - * Copyright(c) 2014-2015 Douglas Christopher Wilson - * MIT Licensed - */ - -'use strict'; - -/** - * Module dependencies. - * @private - */ - -var accepts = require('accepts'); -var deprecate = require('depd')('express'); -var isIP = require('net').isIP; -var typeis = require('type-is'); -var http = require('http'); -var fresh = require('fresh'); -var parseRange = require('range-parser'); -var parse = require('parseurl'); -var proxyaddr = require('proxy-addr'); - -/** - * Request prototype. - * @public - */ - -var req = Object.create(http.IncomingMessage.prototype) - -/** - * Module exports. - * @public - */ - -module.exports = req - -/** - * Return request header. - * - * The `Referrer` header field is special-cased, - * both `Referrer` and `Referer` are interchangeable. - * - * Examples: - * - * req.get('Content-Type'); - * // => "text/plain" - * - * req.get('content-type'); - * // => "text/plain" - * - * req.get('Something'); - * // => undefined - * - * Aliased as `req.header()`. - * - * @param {String} name - * @return {String} - * @public - */ - -req.get = -req.header = function header(name) { - if (!name) { - throw new TypeError('name argument is required to req.get'); - } - - if (typeof name !== 'string') { - throw new TypeError('name must be a string to req.get'); - } - - var lc = name.toLowerCase(); - - switch (lc) { - case 'referer': - case 'referrer': - return this.headers.referrer - || this.headers.referer; - default: - return this.headers[lc]; - } -}; - -/** - * To do: update docs. - * - * Check if the given `type(s)` is acceptable, returning - * the best match when true, otherwise `undefined`, in which - * case you should respond with 406 "Not Acceptable". - * - * The `type` value may be a single MIME type string - * such as "application/json", an extension name - * such as "json", a comma-delimited list such as "json, html, text/plain", - * an argument list such as `"json", "html", "text/plain"`, - * or an array `["json", "html", "text/plain"]`. When a list - * or array is given, the _best_ match, if any is returned. - * - * Examples: - * - * // Accept: text/html - * req.accepts('html'); - * // => "html" - * - * // Accept: text/*, application/json - * req.accepts('html'); - * // => "html" - * req.accepts('text/html'); - * // => "text/html" - * req.accepts('json, text'); - * // => "json" - * req.accepts('application/json'); - * // => "application/json" - * - * // Accept: text/*, application/json - * req.accepts('image/png'); - * req.accepts('png'); - * // => undefined - * - * // Accept: text/*;q=.5, application/json - * req.accepts(['html', 'json']); - * req.accepts('html', 'json'); - * req.accepts('html, json'); - * // => "json" - * - * @param {String|Array} type(s) - * @return {String|Array|Boolean} - * @public - */ - -req.accepts = function(){ - var accept = accepts(this); - return accept.types.apply(accept, arguments); -}; - -/** - * Check if the given `encoding`s are accepted. - * - * @param {String} ...encoding - * @return {String|Array} - * @public - */ - -req.acceptsEncodings = function(){ - var accept = accepts(this); - return accept.encodings.apply(accept, arguments); -}; - -req.acceptsEncoding = deprecate.function(req.acceptsEncodings, - 'req.acceptsEncoding: Use acceptsEncodings instead'); - -/** - * Check if the given `charset`s are acceptable, - * otherwise you should respond with 406 "Not Acceptable". - * - * @param {String} ...charset - * @return {String|Array} - * @public - */ - -req.acceptsCharsets = function(){ - var accept = accepts(this); - return accept.charsets.apply(accept, arguments); -}; - -req.acceptsCharset = deprecate.function(req.acceptsCharsets, - 'req.acceptsCharset: Use acceptsCharsets instead'); - -/** - * Check if the given `lang`s are acceptable, - * otherwise you should respond with 406 "Not Acceptable". - * - * @param {String} ...lang - * @return {String|Array} - * @public - */ - -req.acceptsLanguages = function(){ - var accept = accepts(this); - return accept.languages.apply(accept, arguments); -}; - -req.acceptsLanguage = deprecate.function(req.acceptsLanguages, - 'req.acceptsLanguage: Use acceptsLanguages instead'); - -/** - * Parse Range header field, capping to the given `size`. - * - * Unspecified ranges such as "0-" require knowledge of your resource length. In - * the case of a byte range this is of course the total number of bytes. If the - * Range header field is not given `undefined` is returned, `-1` when unsatisfiable, - * and `-2` when syntactically invalid. - * - * When ranges are returned, the array has a "type" property which is the type of - * range that is required (most commonly, "bytes"). Each array element is an object - * with a "start" and "end" property for the portion of the range. - * - * The "combine" option can be set to `true` and overlapping & adjacent ranges - * will be combined into a single range. - * - * NOTE: remember that ranges are inclusive, so for example "Range: users=0-3" - * should respond with 4 users when available, not 3. - * - * @param {number} size - * @param {object} [options] - * @param {boolean} [options.combine=false] - * @return {number|array} - * @public - */ - -req.range = function range(size, options) { - var range = this.get('Range'); - if (!range) return; - return parseRange(size, range, options); -}; - -/** - * Return the value of param `name` when present or `defaultValue`. - * - * - Checks route placeholders, ex: _/user/:id_ - * - Checks body params, ex: id=12, {"id":12} - * - Checks query string params, ex: ?id=12 - * - * To utilize request bodies, `req.body` - * should be an object. This can be done by using - * the `bodyParser()` middleware. - * - * @param {String} name - * @param {Mixed} [defaultValue] - * @return {String} - * @public - */ - -req.param = function param(name, defaultValue) { - var params = this.params || {}; - var body = this.body || {}; - var query = this.query || {}; - - var args = arguments.length === 1 - ? 'name' - : 'name, default'; - deprecate('req.param(' + args + '): Use req.params, req.body, or req.query instead'); - - if (null != params[name] && params.hasOwnProperty(name)) return params[name]; - if (null != body[name]) return body[name]; - if (null != query[name]) return query[name]; - - return defaultValue; -}; - -/** - * Check if the incoming request contains the "Content-Type" - * header field, and it contains the given mime `type`. - * - * Examples: - * - * // With Content-Type: text/html; charset=utf-8 - * req.is('html'); - * req.is('text/html'); - * req.is('text/*'); - * // => true - * - * // When Content-Type is application/json - * req.is('json'); - * req.is('application/json'); - * req.is('application/*'); - * // => true - * - * req.is('html'); - * // => false - * - * @param {String|Array} types... - * @return {String|false|null} - * @public - */ - -req.is = function is(types) { - var arr = types; - - // support flattened arguments - if (!Array.isArray(types)) { - arr = new Array(arguments.length); - for (var i = 0; i < arr.length; i++) { - arr[i] = arguments[i]; - } - } - - return typeis(this, arr); -}; - -/** - * Return the protocol string "http" or "https" - * when requested with TLS. When the "trust proxy" - * setting trusts the socket address, the - * "X-Forwarded-Proto" header field will be trusted - * and used if present. - * - * If you're running behind a reverse proxy that - * supplies https for you this may be enabled. - * - * @return {String} - * @public - */ - -defineGetter(req, 'protocol', function protocol(){ - var proto = this.connection.encrypted - ? 'https' - : 'http'; - var trust = this.app.get('trust proxy fn'); - - if (!trust(this.connection.remoteAddress, 0)) { - return proto; - } - - // Note: X-Forwarded-Proto is normally only ever a - // single value, but this is to be safe. - var header = this.get('X-Forwarded-Proto') || proto - var index = header.indexOf(',') - - return index !== -1 - ? header.substring(0, index).trim() - : header.trim() -}); - -/** - * Short-hand for: - * - * req.protocol === 'https' - * - * @return {Boolean} - * @public - */ - -defineGetter(req, 'secure', function secure(){ - return this.protocol === 'https'; -}); - -/** - * Return the remote address from the trusted proxy. - * - * The is the remote address on the socket unless - * "trust proxy" is set. - * - * @return {String} - * @public - */ - -defineGetter(req, 'ip', function ip(){ - var trust = this.app.get('trust proxy fn'); - return proxyaddr(this, trust); -}); - -/** - * When "trust proxy" is set, trusted proxy addresses + client. - * - * For example if the value were "client, proxy1, proxy2" - * you would receive the array `["client", "proxy1", "proxy2"]` - * where "proxy2" is the furthest down-stream and "proxy1" and - * "proxy2" were trusted. - * - * @return {Array} - * @public - */ - -defineGetter(req, 'ips', function ips() { - var trust = this.app.get('trust proxy fn'); - var addrs = proxyaddr.all(this, trust); - - // reverse the order (to farthest -> closest) - // and remove socket address - addrs.reverse().pop() - - return addrs -}); - -/** - * Return subdomains as an array. - * - * Subdomains are the dot-separated parts of the host before the main domain of - * the app. By default, the domain of the app is assumed to be the last two - * parts of the host. This can be changed by setting "subdomain offset". - * - * For example, if the domain is "tobi.ferrets.example.com": - * If "subdomain offset" is not set, req.subdomains is `["ferrets", "tobi"]`. - * If "subdomain offset" is 3, req.subdomains is `["tobi"]`. - * - * @return {Array} - * @public - */ - -defineGetter(req, 'subdomains', function subdomains() { - var hostname = this.hostname; - - if (!hostname) return []; - - var offset = this.app.get('subdomain offset'); - var subdomains = !isIP(hostname) - ? hostname.split('.').reverse() - : [hostname]; - - return subdomains.slice(offset); -}); - -/** - * Short-hand for `url.parse(req.url).pathname`. - * - * @return {String} - * @public - */ - -defineGetter(req, 'path', function path() { - return parse(this).pathname; -}); - -/** - * Parse the "Host" header field to a hostname. - * - * When the "trust proxy" setting trusts the socket - * address, the "X-Forwarded-Host" header field will - * be trusted. - * - * @return {String} - * @public - */ - -defineGetter(req, 'hostname', function hostname(){ - var trust = this.app.get('trust proxy fn'); - var host = this.get('X-Forwarded-Host'); - - if (!host || !trust(this.connection.remoteAddress, 0)) { - host = this.get('Host'); - } else if (host.indexOf(',') !== -1) { - // Note: X-Forwarded-Host is normally only ever a - // single value, but this is to be safe. - host = host.substring(0, host.indexOf(',')).trimRight() - } - - if (!host) return; - - // IPv6 literal support - var offset = host[0] === '[' - ? host.indexOf(']') + 1 - : 0; - var index = host.indexOf(':', offset); - - return index !== -1 - ? host.substring(0, index) - : host; -}); - -// TODO: change req.host to return host in next major - -defineGetter(req, 'host', deprecate.function(function host(){ - return this.hostname; -}, 'req.host: Use req.hostname instead')); - -/** - * Check if the request is fresh, aka - * Last-Modified and/or the ETag - * still match. - * - * @return {Boolean} - * @public - */ - -defineGetter(req, 'fresh', function(){ - var method = this.method; - var res = this.res - var status = res.statusCode - - // GET or HEAD for weak freshness validation only - if ('GET' !== method && 'HEAD' !== method) return false; - - // 2xx or 304 as per rfc2616 14.26 - if ((status >= 200 && status < 300) || 304 === status) { - return fresh(this.headers, { - 'etag': res.get('ETag'), - 'last-modified': res.get('Last-Modified') - }) - } - - return false; -}); - -/** - * Check if the request is stale, aka - * "Last-Modified" and / or the "ETag" for the - * resource has changed. - * - * @return {Boolean} - * @public - */ - -defineGetter(req, 'stale', function stale(){ - return !this.fresh; -}); - -/** - * Check if the request was an _XMLHttpRequest_. - * - * @return {Boolean} - * @public - */ - -defineGetter(req, 'xhr', function xhr(){ - var val = this.get('X-Requested-With') || ''; - return val.toLowerCase() === 'xmlhttprequest'; -}); - -/** - * Helper function for creating a getter on an object. - * - * @param {Object} obj - * @param {String} name - * @param {Function} getter - * @private - */ -function defineGetter(obj, name, getter) { - Object.defineProperty(obj, name, { - configurable: true, - enumerable: true, - get: getter - }); -} diff --git a/lib/response.js b/lib/response.js deleted file mode 100644 index fede486c..00000000 --- a/lib/response.js +++ /dev/null @@ -1,1169 +0,0 @@ -/*! - * express - * Copyright(c) 2009-2013 TJ Holowaychuk - * Copyright(c) 2014-2015 Douglas Christopher Wilson - * MIT Licensed - */ - -'use strict'; - -/** - * Module dependencies. - * @private - */ - -var Buffer = require('safe-buffer').Buffer -var contentDisposition = require('content-disposition'); -var createError = require('http-errors') -var deprecate = require('depd')('express'); -var encodeUrl = require('encodeurl'); -var escapeHtml = require('escape-html'); -var http = require('http'); -var isAbsolute = require('./utils').isAbsolute; -var onFinished = require('on-finished'); -var path = require('path'); -var statuses = require('statuses') -var merge = require('utils-merge'); -var sign = require('cookie-signature').sign; -var normalizeType = require('./utils').normalizeType; -var normalizeTypes = require('./utils').normalizeTypes; -var setCharset = require('./utils').setCharset; -var cookie = require('cookie'); -var send = require('send'); -var extname = path.extname; -var mime = send.mime; -var resolve = path.resolve; -var vary = require('vary'); - -/** - * Response prototype. - * @public - */ - -var res = Object.create(http.ServerResponse.prototype) - -/** - * Module exports. - * @public - */ - -module.exports = res - -/** - * Module variables. - * @private - */ - -var charsetRegExp = /;\s*charset\s*=/; - -/** - * Set status `code`. - * - * @param {Number} code - * @return {ServerResponse} - * @public - */ - -res.status = function status(code) { - if ((typeof code === 'string' || Math.floor(code) !== code) && code > 99 && code < 1000) { - deprecate('res.status(' + JSON.stringify(code) + '): use res.status(' + Math.floor(code) + ') instead') - } - this.statusCode = code; - return this; -}; - -/** - * Set Link header field with the given `links`. - * - * Examples: - * - * res.links({ - * next: 'http://api.example.com/users?page=2', - * last: 'http://api.example.com/users?page=5' - * }); - * - * @param {Object} links - * @return {ServerResponse} - * @public - */ - -res.links = function(links){ - var link = this.get('Link') || ''; - if (link) link += ', '; - return this.set('Link', link + Object.keys(links).map(function(rel){ - return '<' + links[rel] + '>; rel="' + rel + '"'; - }).join(', ')); -}; - -/** - * Send a response. - * - * Examples: - * - * res.send(Buffer.from('wahoo')); - * res.send({ some: 'json' }); - * res.send('

some html

'); - * - * @param {string|number|boolean|object|Buffer} body - * @public - */ - -res.send = function send(body) { - var chunk = body; - var encoding; - var req = this.req; - var type; - - // settings - var app = this.app; - - // allow status / body - if (arguments.length === 2) { - // res.send(body, status) backwards compat - if (typeof arguments[0] !== 'number' && typeof arguments[1] === 'number') { - deprecate('res.send(body, status): Use res.status(status).send(body) instead'); - this.statusCode = arguments[1]; - } else { - deprecate('res.send(status, body): Use res.status(status).send(body) instead'); - this.statusCode = arguments[0]; - chunk = arguments[1]; - } - } - - // disambiguate res.send(status) and res.send(status, num) - if (typeof chunk === 'number' && arguments.length === 1) { - // res.send(status) will set status message as text string - if (!this.get('Content-Type')) { - this.type('txt'); - } - - deprecate('res.send(status): Use res.sendStatus(status) instead'); - this.statusCode = chunk; - chunk = statuses.message[chunk] - } - - switch (typeof chunk) { - // string defaulting to html - case 'string': - if (!this.get('Content-Type')) { - this.type('html'); - } - break; - case 'boolean': - case 'number': - case 'object': - if (chunk === null) { - chunk = ''; - } else if (Buffer.isBuffer(chunk)) { - if (!this.get('Content-Type')) { - this.type('bin'); - } - } else { - return this.json(chunk); - } - break; - } - - // write strings in utf-8 - if (typeof chunk === 'string') { - encoding = 'utf8'; - type = this.get('Content-Type'); - - // reflect this in content-type - if (typeof type === 'string') { - this.set('Content-Type', setCharset(type, 'utf-8')); - } - } - - // determine if ETag should be generated - var etagFn = app.get('etag fn') - var generateETag = !this.get('ETag') && typeof etagFn === 'function' - - // populate Content-Length - var len - if (chunk !== undefined) { - if (Buffer.isBuffer(chunk)) { - // get length of Buffer - len = chunk.length - } else if (!generateETag && chunk.length < 1000) { - // just calculate length when no ETag + small chunk - len = Buffer.byteLength(chunk, encoding) - } else { - // convert chunk to Buffer and calculate - chunk = Buffer.from(chunk, encoding) - encoding = undefined; - len = chunk.length - } - - this.set('Content-Length', len); - } - - // populate ETag - var etag; - if (generateETag && len !== undefined) { - if ((etag = etagFn(chunk, encoding))) { - this.set('ETag', etag); - } - } - - // freshness - if (req.fresh) this.statusCode = 304; - - // strip irrelevant headers - if (204 === this.statusCode || 304 === this.statusCode) { - this.removeHeader('Content-Type'); - this.removeHeader('Content-Length'); - this.removeHeader('Transfer-Encoding'); - chunk = ''; - } - - // alter headers for 205 - if (this.statusCode === 205) { - this.set('Content-Length', '0') - this.removeHeader('Transfer-Encoding') - chunk = '' - } - - if (req.method === 'HEAD') { - // skip body for HEAD - this.end(); - } else { - // respond - this.end(chunk, encoding); - } - - return this; -}; - -/** - * Send JSON response. - * - * Examples: - * - * res.json(null); - * res.json({ user: 'tj' }); - * - * @param {string|number|boolean|object} obj - * @public - */ - -res.json = function json(obj) { - var val = obj; - - // allow status / body - if (arguments.length === 2) { - // res.json(body, status) backwards compat - if (typeof arguments[1] === 'number') { - deprecate('res.json(obj, status): Use res.status(status).json(obj) instead'); - this.statusCode = arguments[1]; - } else { - deprecate('res.json(status, obj): Use res.status(status).json(obj) instead'); - this.statusCode = arguments[0]; - val = arguments[1]; - } - } - - // settings - var app = this.app; - var escape = app.get('json escape') - var replacer = app.get('json replacer'); - var spaces = app.get('json spaces'); - var body = stringify(val, replacer, spaces, escape) - - // content-type - if (!this.get('Content-Type')) { - this.set('Content-Type', 'application/json'); - } - - return this.send(body); -}; - -/** - * Send JSON response with JSONP callback support. - * - * Examples: - * - * res.jsonp(null); - * res.jsonp({ user: 'tj' }); - * - * @param {string|number|boolean|object} obj - * @public - */ - -res.jsonp = function jsonp(obj) { - var val = obj; - - // allow status / body - if (arguments.length === 2) { - // res.jsonp(body, status) backwards compat - if (typeof arguments[1] === 'number') { - deprecate('res.jsonp(obj, status): Use res.status(status).jsonp(obj) instead'); - this.statusCode = arguments[1]; - } else { - deprecate('res.jsonp(status, obj): Use res.status(status).jsonp(obj) instead'); - this.statusCode = arguments[0]; - val = arguments[1]; - } - } - - // settings - var app = this.app; - var escape = app.get('json escape') - var replacer = app.get('json replacer'); - var spaces = app.get('json spaces'); - var body = stringify(val, replacer, spaces, escape) - var callback = this.req.query[app.get('jsonp callback name')]; - - // content-type - if (!this.get('Content-Type')) { - this.set('X-Content-Type-Options', 'nosniff'); - this.set('Content-Type', 'application/json'); - } - - // fixup callback - if (Array.isArray(callback)) { - callback = callback[0]; - } - - // jsonp - if (typeof callback === 'string' && callback.length !== 0) { - this.set('X-Content-Type-Options', 'nosniff'); - this.set('Content-Type', 'text/javascript'); - - // restrict callback charset - callback = callback.replace(/[^\[\]\w$.]/g, ''); - - if (body === undefined) { - // empty argument - body = '' - } else if (typeof body === 'string') { - // 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); -}; - -/** - * Send given HTTP status code. - * - * Sets the response status to `statusCode` and the body of the - * response to the standard description from node's http.STATUS_CODES - * or the statusCode number if no description. - * - * Examples: - * - * res.sendStatus(200); - * - * @param {number} statusCode - * @public - */ - -res.sendStatus = function sendStatus(statusCode) { - var body = statuses.message[statusCode] || String(statusCode) - - this.statusCode = statusCode; - this.type('txt'); - - return this.send(body); -}; - -/** - * Transfer the file at the given `path`. - * - * Automatically sets the _Content-Type_ response header field. - * The callback `callback(err)` is invoked when the transfer is complete - * or when an error occurs. Be sure to check `res.headersSent` - * if you wish to attempt responding, as the header and some data - * may have already been transferred. - * - * Options: - * - * - `maxAge` defaulting to 0 (can be string converted by `ms`) - * - `root` root directory for relative filenames - * - `headers` object of headers to serve with file - * - `dotfiles` serve dotfiles, defaulting to false; can be `"allow"` to send them - * - * Other options are passed along to `send`. - * - * Examples: - * - * The following example illustrates how `res.sendFile()` may - * be used as an alternative for the `static()` middleware for - * dynamic situations. The code backing `res.sendFile()` is actually - * the same code, so HTTP cache support etc is identical. - * - * app.get('/user/:uid/photos/:file', function(req, res){ - * var uid = req.params.uid - * , file = req.params.file; - * - * req.user.mayViewFilesFrom(uid, function(yes){ - * if (yes) { - * res.sendFile('/uploads/' + uid + '/' + file); - * } else { - * res.send(403, 'Sorry! you cant see that.'); - * } - * }); - * }); - * - * @public - */ - -res.sendFile = function sendFile(path, options, callback) { - var done = callback; - var req = this.req; - var res = this; - var next = req.next; - var opts = options || {}; - - if (!path) { - throw new TypeError('path argument is required to res.sendFile'); - } - - if (typeof path !== 'string') { - throw new TypeError('path must be a string to res.sendFile') - } - - // support function as second arg - if (typeof options === 'function') { - done = options; - opts = {}; - } - - if (!opts.root && !isAbsolute(path)) { - throw new TypeError('path must be absolute or specify root to res.sendFile'); - } - - // create file stream - var pathname = encodeURI(path); - var file = send(req, pathname, opts); - - // transfer - sendfile(res, file, opts, function (err) { - if (done) return done(err); - if (err && err.code === 'EISDIR') return next(); - - // next() all but write errors - if (err && err.code !== 'ECONNABORTED' && err.syscall !== 'write') { - next(err); - } - }); -}; - -/** - * Transfer the file at the given `path`. - * - * Automatically sets the _Content-Type_ response header field. - * The callback `callback(err)` is invoked when the transfer is complete - * or when an error occurs. Be sure to check `res.headersSent` - * if you wish to attempt responding, as the header and some data - * may have already been transferred. - * - * Options: - * - * - `maxAge` defaulting to 0 (can be string converted by `ms`) - * - `root` root directory for relative filenames - * - `headers` object of headers to serve with file - * - `dotfiles` serve dotfiles, defaulting to false; can be `"allow"` to send them - * - * Other options are passed along to `send`. - * - * Examples: - * - * The following example illustrates how `res.sendfile()` may - * be used as an alternative for the `static()` middleware for - * dynamic situations. The code backing `res.sendfile()` is actually - * the same code, so HTTP cache support etc is identical. - * - * app.get('/user/:uid/photos/:file', function(req, res){ - * var uid = req.params.uid - * , file = req.params.file; - * - * req.user.mayViewFilesFrom(uid, function(yes){ - * if (yes) { - * res.sendfile('/uploads/' + uid + '/' + file); - * } else { - * res.send(403, 'Sorry! you cant see that.'); - * } - * }); - * }); - * - * @public - */ - -res.sendfile = function (path, options, callback) { - var done = callback; - var req = this.req; - var res = this; - var next = req.next; - var opts = options || {}; - - // support function as second arg - if (typeof options === 'function') { - done = options; - opts = {}; - } - - // create file stream - var file = send(req, path, opts); - - // transfer - sendfile(res, file, opts, function (err) { - if (done) return done(err); - if (err && err.code === 'EISDIR') return next(); - - // next() all but write errors - if (err && err.code !== 'ECONNABORTED' && err.syscall !== 'write') { - next(err); - } - }); -}; - -res.sendfile = deprecate.function(res.sendfile, - 'res.sendfile: Use res.sendFile instead'); - -/** - * Transfer the file at the given `path` as an attachment. - * - * Optionally providing an alternate attachment `filename`, - * and optional callback `callback(err)`. The callback is invoked - * when the data transfer is complete, or when an error has - * occurred. Be sure to check `res.headersSent` if you plan to respond. - * - * Optionally providing an `options` object to use with `res.sendFile()`. - * This function will set the `Content-Disposition` header, overriding - * any `Content-Disposition` header passed as header options in order - * to set the attachment and filename. - * - * This method uses `res.sendFile()`. - * - * @public - */ - -res.download = function download (path, filename, options, callback) { - var done = callback; - var name = filename; - var opts = options || null - - // support function as second or third arg - if (typeof filename === 'function') { - done = filename; - name = null; - opts = null - } else if (typeof options === 'function') { - done = options - opts = null - } - - // support optional filename, where options may be in it's place - if (typeof filename === 'object' && - (typeof options === 'function' || options === undefined)) { - name = null - opts = filename - } - - // set Content-Disposition when file is sent - var headers = { - 'Content-Disposition': contentDisposition(name || path) - }; - - // merge user-provided headers - if (opts && opts.headers) { - var keys = Object.keys(opts.headers) - for (var i = 0; i < keys.length; i++) { - var key = keys[i] - if (key.toLowerCase() !== 'content-disposition') { - headers[key] = opts.headers[key] - } - } - } - - // merge user-provided options - opts = Object.create(opts) - opts.headers = headers - - // Resolve the full path for sendFile - var fullPath = !opts.root - ? resolve(path) - : path - - // send file - return this.sendFile(fullPath, opts, done) -}; - -/** - * Set _Content-Type_ response header with `type` through `mime.lookup()` - * when it does not contain "/", or set the Content-Type to `type` otherwise. - * - * Examples: - * - * res.type('.html'); - * res.type('html'); - * res.type('json'); - * res.type('application/json'); - * res.type('png'); - * - * @param {String} type - * @return {ServerResponse} for chaining - * @public - */ - -res.contentType = -res.type = function contentType(type) { - var ct = type.indexOf('/') === -1 - ? mime.lookup(type) - : type; - - return this.set('Content-Type', ct); -}; - -/** - * Respond to the Acceptable formats using an `obj` - * of mime-type callbacks. - * - * This method uses `req.accepted`, an array of - * acceptable types ordered by their quality values. - * When "Accept" is not present the _first_ callback - * is invoked, otherwise the first match is used. When - * no match is performed the server responds with - * 406 "Not Acceptable". - * - * Content-Type is set for you, however if you choose - * you may alter this within the callback using `res.type()` - * or `res.set('Content-Type', ...)`. - * - * res.format({ - * 'text/plain': function(){ - * res.send('hey'); - * }, - * - * 'text/html': function(){ - * res.send('

hey

'); - * }, - * - * 'application/json': function () { - * res.send({ message: 'hey' }); - * } - * }); - * - * In addition to canonicalized MIME types you may - * also use extnames mapped to these types: - * - * res.format({ - * text: function(){ - * res.send('hey'); - * }, - * - * html: function(){ - * res.send('

hey

'); - * }, - * - * json: function(){ - * res.send({ message: 'hey' }); - * } - * }); - * - * By default Express passes an `Error` - * with a `.status` of 406 to `next(err)` - * if a match is not made. If you provide - * a `.default` callback it will be invoked - * instead. - * - * @param {Object} obj - * @return {ServerResponse} for chaining - * @public - */ - -res.format = function(obj){ - var req = this.req; - var next = req.next; - - var keys = Object.keys(obj) - .filter(function (v) { return v !== 'default' }) - - var key = keys.length > 0 - ? req.accepts(keys) - : false; - - this.vary("Accept"); - - if (key) { - this.set('Content-Type', normalizeType(key).value); - obj[key](req, this, next); - } else if (obj.default) { - obj.default(req, this, next) - } else { - next(createError(406, { - types: normalizeTypes(keys).map(function (o) { return o.value }) - })) - } - - return this; -}; - -/** - * Set _Content-Disposition_ header to _attachment_ with optional `filename`. - * - * @param {String} filename - * @return {ServerResponse} - * @public - */ - -res.attachment = function attachment(filename) { - if (filename) { - this.type(extname(filename)); - } - - this.set('Content-Disposition', contentDisposition(filename)); - - return this; -}; - -/** - * Append additional header `field` with value `val`. - * - * Example: - * - * res.append('Link', ['', '']); - * res.append('Set-Cookie', 'foo=bar; Path=/; HttpOnly'); - * res.append('Warning', '199 Miscellaneous warning'); - * - * @param {String} field - * @param {String|Array} val - * @return {ServerResponse} for chaining - * @public - */ - -res.append = function append(field, val) { - var prev = this.get(field); - var value = val; - - if (prev) { - // concat the new and prev vals - value = Array.isArray(prev) ? prev.concat(val) - : Array.isArray(val) ? [prev].concat(val) - : [prev, val] - } - - return this.set(field, value); -}; - -/** - * Set header `field` to `val`, or pass - * an object of header fields. - * - * Examples: - * - * res.set('Foo', ['bar', 'baz']); - * res.set('Accept', 'application/json'); - * res.set({ Accept: 'text/plain', 'X-API-Key': 'tobi' }); - * - * Aliased as `res.header()`. - * - * @param {String|Object} field - * @param {String|Array} val - * @return {ServerResponse} for chaining - * @public - */ - -res.set = -res.header = function header(field, val) { - if (arguments.length === 2) { - var value = Array.isArray(val) - ? val.map(String) - : String(val); - - // add charset to content-type - if (field.toLowerCase() === 'content-type') { - if (Array.isArray(value)) { - throw new TypeError('Content-Type cannot be set to an Array'); - } - if (!charsetRegExp.test(value)) { - var charset = mime.charsets.lookup(value.split(';')[0]); - if (charset) value += '; charset=' + charset.toLowerCase(); - } - } - - this.setHeader(field, value); - } else { - for (var key in field) { - this.set(key, field[key]); - } - } - return this; -}; - -/** - * Get value for header `field`. - * - * @param {String} field - * @return {String} - * @public - */ - -res.get = function(field){ - return this.getHeader(field); -}; - -/** - * Clear cookie `name`. - * - * @param {String} name - * @param {Object} [options] - * @return {ServerResponse} for chaining - * @public - */ - -res.clearCookie = function clearCookie(name, options) { - var opts = merge({ expires: new Date(1), path: '/' }, options); - - return this.cookie(name, '', opts); -}; - -/** - * Set cookie `name` to `value`, with the given `options`. - * - * Options: - * - * - `maxAge` max-age in milliseconds, converted to `expires` - * - `signed` sign the cookie - * - `path` defaults to "/" - * - * Examples: - * - * // "Remember Me" for 15 minutes - * res.cookie('rememberme', '1', { expires: new Date(Date.now() + 900000), httpOnly: true }); - * - * // same as above - * res.cookie('rememberme', '1', { maxAge: 900000, httpOnly: true }) - * - * @param {String} name - * @param {String|Object} value - * @param {Object} [options] - * @return {ServerResponse} for chaining - * @public - */ - -res.cookie = function (name, value, options) { - var opts = merge({}, options); - var secret = this.req.secret; - var signed = opts.signed; - - if (signed && !secret) { - throw new Error('cookieParser("secret") required for signed cookies'); - } - - var val = typeof value === 'object' - ? 'j:' + JSON.stringify(value) - : String(value); - - if (signed) { - val = 's:' + sign(val, secret); - } - - if (opts.maxAge != null) { - var maxAge = opts.maxAge - 0 - - if (!isNaN(maxAge)) { - opts.expires = new Date(Date.now() + maxAge) - opts.maxAge = Math.floor(maxAge / 1000) - } - } - - if (opts.path == null) { - opts.path = '/'; - } - - this.append('Set-Cookie', cookie.serialize(name, String(val), opts)); - - return this; -}; - -/** - * Set the location header to `url`. - * - * The given `url` can also be "back", which redirects - * to the _Referrer_ or _Referer_ headers or "/". - * - * Examples: - * - * res.location('/foo/bar').; - * res.location('http://example.com'); - * res.location('../login'); - * - * @param {String} url - * @return {ServerResponse} for chaining - * @public - */ - -res.location = function location(url) { - var loc = url; - - // "back" is an alias for the referrer - if (url === 'back') { - loc = this.req.get('Referrer') || '/'; - } - - // set location - return this.set('Location', encodeUrl(loc)); -}; - -/** - * Redirect to the given `url` with optional response `status` - * defaulting to 302. - * - * The resulting `url` is determined by `res.location()`, so - * it will play nicely with mounted apps, relative paths, - * `"back"` etc. - * - * Examples: - * - * res.redirect('/foo/bar'); - * res.redirect('http://example.com'); - * res.redirect(301, 'http://example.com'); - * res.redirect('../login'); // /blog/post/1 -> /blog/login - * - * @public - */ - -res.redirect = function redirect(url) { - var address = url; - var body; - var status = 302; - - // allow status / url - if (arguments.length === 2) { - if (typeof arguments[0] === 'number') { - status = arguments[0]; - address = arguments[1]; - } else { - deprecate('res.redirect(url, status): Use res.redirect(status, url) instead'); - status = arguments[1]; - } - } - - // Set location header - address = this.location(address).get('Location'); - - // Support text/{plain,html} by default - this.format({ - text: function(){ - body = statuses.message[status] + '. Redirecting to ' + address - }, - - html: function(){ - var u = escapeHtml(address); - body = '

' + statuses.message[status] + '. Redirecting to ' + u + '

' - }, - - default: function(){ - body = ''; - } - }); - - // Respond - this.statusCode = status; - this.set('Content-Length', Buffer.byteLength(body)); - - if (this.req.method === 'HEAD') { - this.end(); - } else { - this.end(body); - } -}; - -/** - * Add `field` to Vary. If already present in the Vary set, then - * this call is simply ignored. - * - * @param {Array|String} field - * @return {ServerResponse} for chaining - * @public - */ - -res.vary = function(field){ - // checks for back-compat - if (!field || (Array.isArray(field) && !field.length)) { - deprecate('res.vary(): Provide a field name'); - return this; - } - - vary(this, field); - - return this; -}; - -/** - * Render `view` with the given `options` and optional callback `fn`. - * When a callback function is given a response will _not_ be made - * automatically, otherwise a response of _200_ and _text/html_ is given. - * - * Options: - * - * - `cache` boolean hinting to the engine it should cache - * - `filename` filename of the view being rendered - * - * @public - */ - -res.render = function render(view, options, callback) { - var app = this.req.app; - var done = callback; - var opts = options || {}; - var req = this.req; - var self = this; - - // support callback function as second arg - if (typeof options === 'function') { - done = options; - opts = {}; - } - - // merge res.locals - opts._locals = self.locals; - - // default callback to respond - done = done || function (err, str) { - if (err) return req.next(err); - self.send(str); - }; - - // render - app.render(view, opts, done); -}; - -// pipe the send file stream -function sendfile(res, file, options, callback) { - var done = false; - var streaming; - - // request aborted - function onaborted() { - if (done) return; - done = true; - - var err = new Error('Request aborted'); - err.code = 'ECONNABORTED'; - callback(err); - } - - // directory - function ondirectory() { - if (done) return; - done = true; - - var err = new Error('EISDIR, read'); - err.code = 'EISDIR'; - callback(err); - } - - // errors - function onerror(err) { - if (done) return; - done = true; - callback(err); - } - - // ended - function onend() { - if (done) return; - done = true; - callback(); - } - - // file - function onfile() { - streaming = false; - } - - // finished - function onfinish(err) { - if (err && err.code === 'ECONNRESET') return onaborted(); - if (err) return onerror(err); - if (done) return; - - setImmediate(function () { - if (streaming !== false && !done) { - onaborted(); - return; - } - - if (done) return; - done = true; - callback(); - }); - } - - // streaming - function onstream() { - streaming = true; - } - - file.on('directory', ondirectory); - file.on('end', onend); - file.on('error', onerror); - file.on('file', onfile); - file.on('stream', onstream); - onFinished(res, onfinish); - - if (options.headers) { - // set headers on successful transfer - file.on('headers', function headers(res) { - var obj = options.headers; - var keys = Object.keys(obj); - - for (var i = 0; i < keys.length; i++) { - var k = keys[i]; - res.setHeader(k, obj[k]); - } - }); - } - - // pipe - file.pipe(res); -} - -/** - * Stringify JSON, like JSON.stringify, but v8 optimized, with the - * ability to escape characters that can trigger HTML sniffing. - * - * @param {*} value - * @param {function} replacer - * @param {number} spaces - * @param {boolean} escape - * @returns {string} - * @private - */ - -function stringify (value, replacer, spaces, escape) { - // v8 checks arguments.length for optimizing simple call - // https://bugs.chromium.org/p/v8/issues/detail?id=4730 - var json = replacer || spaces - ? JSON.stringify(value, replacer, spaces) - : JSON.stringify(value); - - if (escape && typeof json === 'string') { - json = json.replace(/[<>&]/g, function (c) { - switch (c.charCodeAt(0)) { - case 0x3c: - return '\\u003c' - case 0x3e: - return '\\u003e' - case 0x26: - return '\\u0026' - /* istanbul ignore next: unreachable default */ - default: - return c - } - }) - } - - return json -} diff --git a/lib/router/index.js b/lib/router/index.js deleted file mode 100644 index abb3a6f5..00000000 --- a/lib/router/index.js +++ /dev/null @@ -1,673 +0,0 @@ -/*! - * express - * Copyright(c) 2009-2013 TJ Holowaychuk - * Copyright(c) 2013 Roman Shtylman - * Copyright(c) 2014-2015 Douglas Christopher Wilson - * MIT Licensed - */ - -'use strict'; - -/** - * Module dependencies. - * @private - */ - -var Route = require('./route'); -var Layer = require('./layer'); -var methods = require('methods'); -var mixin = require('utils-merge'); -var debug = require('debug')('express:router'); -var deprecate = require('depd')('express'); -var flatten = require('array-flatten'); -var parseUrl = require('parseurl'); -var setPrototypeOf = require('setprototypeof') - -/** - * Module variables. - * @private - */ - -var objectRegExp = /^\[object (\S+)\]$/; -var slice = Array.prototype.slice; -var toString = Object.prototype.toString; - -/** - * Initialize a new `Router` with the given `options`. - * - * @param {Object} [options] - * @return {Router} which is a callable function - * @public - */ - -var proto = module.exports = function(options) { - var opts = options || {}; - - function router(req, res, next) { - router.handle(req, res, next); - } - - // mixin Router class functions - setPrototypeOf(router, proto) - - router.params = {}; - router._params = []; - router.caseSensitive = opts.caseSensitive; - router.mergeParams = opts.mergeParams; - router.strict = opts.strict; - router.stack = []; - - return router; -}; - -/** - * Map the given param placeholder `name`(s) to the given callback. - * - * Parameter mapping is used to provide pre-conditions to routes - * which use normalized placeholders. For example a _:user_id_ parameter - * could automatically load a user's information from the database without - * any additional code, - * - * The callback uses the same signature as middleware, the only difference - * being that the value of the placeholder is passed, in this case the _id_ - * of the user. Once the `next()` function is invoked, just like middleware - * it will continue on to execute the route, or subsequent parameter functions. - * - * Just like in middleware, you must either respond to the request or call next - * to avoid stalling the request. - * - * app.param('user_id', function(req, res, next, id){ - * User.find(id, function(err, user){ - * if (err) { - * return next(err); - * } else if (!user) { - * return next(new Error('failed to load user')); - * } - * req.user = user; - * next(); - * }); - * }); - * - * @param {String} name - * @param {Function} fn - * @return {app} for chaining - * @public - */ - -proto.param = function param(name, fn) { - // param logic - if (typeof name === 'function') { - deprecate('router.param(fn): Refactor to use path params'); - this._params.push(name); - return; - } - - // apply param functions - var params = this._params; - var len = params.length; - var ret; - - if (name[0] === ':') { - deprecate('router.param(' + JSON.stringify(name) + ', fn): Use router.param(' + JSON.stringify(name.slice(1)) + ', fn) instead') - name = name.slice(1) - } - - for (var i = 0; i < len; ++i) { - if (ret = params[i](name, fn)) { - fn = ret; - } - } - - // ensure we end up with a - // middleware function - if ('function' !== typeof fn) { - throw new Error('invalid param() call for ' + name + ', got ' + fn); - } - - (this.params[name] = this.params[name] || []).push(fn); - return this; -}; - -/** - * Dispatch a req, res into the router. - * @private - */ - -proto.handle = function handle(req, res, out) { - var self = this; - - debug('dispatching %s %s', req.method, req.url); - - var idx = 0; - var protohost = getProtohost(req.url) || '' - var removed = ''; - var slashAdded = false; - var sync = 0 - var paramcalled = {}; - - // store options for OPTIONS request - // only used if OPTIONS request - var options = []; - - // middleware and routes - var stack = self.stack; - - // manage inter-router variables - var parentParams = req.params; - var parentUrl = req.baseUrl || ''; - var done = restore(out, req, 'baseUrl', 'next', 'params'); - - // setup next layer - req.next = next; - - // for options requests, respond with a default if nothing else responds - if (req.method === 'OPTIONS') { - done = wrap(done, function(old, err) { - if (err || options.length === 0) return old(err); - sendOptionsResponse(res, options, old); - }); - } - - // setup basic req values - req.baseUrl = parentUrl; - req.originalUrl = req.originalUrl || req.url; - - next(); - - function next(err) { - var layerError = err === 'route' - ? null - : err; - - // remove added slash - if (slashAdded) { - req.url = req.url.slice(1) - slashAdded = false; - } - - // restore altered req.url - if (removed.length !== 0) { - req.baseUrl = parentUrl; - req.url = protohost + removed + req.url.slice(protohost.length) - removed = ''; - } - - // signal to exit router - if (layerError === 'router') { - setImmediate(done, null) - return - } - - // no more matching layers - if (idx >= stack.length) { - setImmediate(done, layerError); - return; - } - - // max sync stack - if (++sync > 100) { - return setImmediate(next, err) - } - - // get pathname of request - var path = getPathname(req); - - if (path == null) { - return done(layerError); - } - - // find next matching layer - var layer; - var match; - var route; - - while (match !== true && idx < stack.length) { - layer = stack[idx++]; - match = matchLayer(layer, path); - route = layer.route; - - if (typeof match !== 'boolean') { - // hold on to layerError - layerError = layerError || match; - } - - if (match !== true) { - continue; - } - - if (!route) { - // process non-route handlers normally - continue; - } - - if (layerError) { - // routes do not match with a pending error - match = false; - continue; - } - - var method = req.method; - var has_method = route._handles_method(method); - - // build up automatic options response - if (!has_method && method === 'OPTIONS') { - appendMethods(options, route._options()); - } - - // don't even bother matching route - if (!has_method && method !== 'HEAD') { - match = false; - } - } - - // no match - if (match !== true) { - return done(layerError); - } - - // store route for dispatch on change - if (route) { - req.route = route; - } - - // Capture one-time layer values - req.params = self.mergeParams - ? mergeParams(layer.params, parentParams) - : layer.params; - var layerPath = layer.path; - - // this should be done for the layer - self.process_params(layer, paramcalled, req, res, function (err) { - if (err) { - next(layerError || err) - } else if (route) { - layer.handle_request(req, res, next) - } else { - trim_prefix(layer, layerError, layerPath, path) - } - - sync = 0 - }); - } - - function trim_prefix(layer, layerError, layerPath, path) { - if (layerPath.length !== 0) { - // Validate path is a prefix match - if (layerPath !== path.slice(0, layerPath.length)) { - next(layerError) - return - } - - // Validate path breaks on a path separator - var c = path[layerPath.length] - if (c && c !== '/' && c !== '.') return next(layerError) - - // Trim off the part of the url that matches the route - // middleware (.use stuff) needs to have the path stripped - debug('trim prefix (%s) from url %s', layerPath, req.url); - removed = layerPath; - req.url = protohost + req.url.slice(protohost.length + removed.length) - - // Ensure leading slash - if (!protohost && req.url[0] !== '/') { - req.url = '/' + req.url; - slashAdded = true; - } - - // Setup base URL (no trailing slash) - req.baseUrl = parentUrl + (removed[removed.length - 1] === '/' - ? removed.substring(0, removed.length - 1) - : removed); - } - - debug('%s %s : %s', layer.name, layerPath, req.originalUrl); - - if (layerError) { - layer.handle_error(layerError, req, res, next); - } else { - layer.handle_request(req, res, next); - } - } -}; - -/** - * Process any parameters for the layer. - * @private - */ - -proto.process_params = function process_params(layer, called, req, res, done) { - var params = this.params; - - // captured parameters from the layer, keys and values - var keys = layer.keys; - - // fast track - if (!keys || keys.length === 0) { - return done(); - } - - var i = 0; - var name; - var paramIndex = 0; - var key; - var paramVal; - var paramCallbacks; - var paramCalled; - - // process params in order - // param callbacks can be async - function param(err) { - if (err) { - return done(err); - } - - if (i >= keys.length ) { - return done(); - } - - paramIndex = 0; - key = keys[i++]; - name = key.name; - paramVal = req.params[name]; - paramCallbacks = params[name]; - paramCalled = called[name]; - - if (paramVal === undefined || !paramCallbacks) { - return param(); - } - - // param previously called with same value or error occurred - if (paramCalled && (paramCalled.match === paramVal - || (paramCalled.error && paramCalled.error !== 'route'))) { - // restore value - req.params[name] = paramCalled.value; - - // next param - return param(paramCalled.error); - } - - called[name] = paramCalled = { - error: null, - match: paramVal, - value: paramVal - }; - - paramCallback(); - } - - // single param callbacks - function paramCallback(err) { - var fn = paramCallbacks[paramIndex++]; - - // store updated value - paramCalled.value = req.params[key.name]; - - if (err) { - // store error - paramCalled.error = err; - param(err); - return; - } - - if (!fn) return param(); - - try { - fn(req, res, paramCallback, paramVal, key.name); - } catch (e) { - paramCallback(e); - } - } - - param(); -}; - -/** - * Use the given middleware function, with optional path, defaulting to "/". - * - * Use (like `.all`) will run for any http METHOD, but it will not add - * handlers for those methods so OPTIONS requests will not consider `.use` - * functions even if they could respond. - * - * The other difference is that _route_ path is stripped and not visible - * to the handler function. The main effect of this feature is that mounted - * handlers can operate without any code changes regardless of the "prefix" - * pathname. - * - * @public - */ - -proto.use = function use(fn) { - var offset = 0; - var path = '/'; - - // default path to '/' - // disambiguate router.use([fn]) - if (typeof fn !== 'function') { - var arg = fn; - - while (Array.isArray(arg) && arg.length !== 0) { - arg = arg[0]; - } - - // first arg is the path - if (typeof arg !== 'function') { - offset = 1; - path = fn; - } - } - - var callbacks = flatten(slice.call(arguments, offset)); - - if (callbacks.length === 0) { - throw new TypeError('Router.use() requires a middleware function') - } - - for (var i = 0; i < callbacks.length; i++) { - var fn = callbacks[i]; - - if (typeof fn !== 'function') { - throw new TypeError('Router.use() requires a middleware function but got a ' + gettype(fn)) - } - - // add the middleware - debug('use %o %s', path, fn.name || '') - - var layer = new Layer(path, { - sensitive: this.caseSensitive, - strict: false, - end: false - }, fn); - - layer.route = undefined; - - this.stack.push(layer); - } - - return this; -}; - -/** - * Create a new Route for the given path. - * - * Each route contains a separate middleware stack and VERB handlers. - * - * See the Route api documentation for details on adding handlers - * and middleware to routes. - * - * @param {String} path - * @return {Route} - * @public - */ - -proto.route = function route(path) { - var route = new Route(path); - - var layer = new Layer(path, { - sensitive: this.caseSensitive, - strict: this.strict, - end: true - }, route.dispatch.bind(route)); - - layer.route = route; - - this.stack.push(layer); - return route; -}; - -// create Router#VERB functions -methods.concat('all').forEach(function(method){ - proto[method] = function(path){ - var route = this.route(path) - route[method].apply(route, slice.call(arguments, 1)); - return this; - }; -}); - -// append methods to a list of methods -function appendMethods(list, addition) { - for (var i = 0; i < addition.length; i++) { - var method = addition[i]; - if (list.indexOf(method) === -1) { - list.push(method); - } - } -} - -// get pathname of request -function getPathname(req) { - try { - return parseUrl(req).pathname; - } catch (err) { - return undefined; - } -} - -// Get get protocol + host for a URL -function getProtohost(url) { - if (typeof url !== 'string' || url.length === 0 || url[0] === '/') { - return undefined - } - - var searchIndex = url.indexOf('?') - var pathLength = searchIndex !== -1 - ? searchIndex - : url.length - var fqdnIndex = url.slice(0, pathLength).indexOf('://') - - return fqdnIndex !== -1 - ? url.substring(0, url.indexOf('/', 3 + fqdnIndex)) - : undefined -} - -// get type for error message -function gettype(obj) { - var type = typeof obj; - - if (type !== 'object') { - return type; - } - - // inspect [[Class]] for objects - return toString.call(obj) - .replace(objectRegExp, '$1'); -} - -/** - * Match path to a layer. - * - * @param {Layer} layer - * @param {string} path - * @private - */ - -function matchLayer(layer, path) { - try { - return layer.match(path); - } catch (err) { - return err; - } -} - -// merge params with parent params -function mergeParams(params, parent) { - if (typeof parent !== 'object' || !parent) { - return params; - } - - // make copy of parent for base - var obj = mixin({}, parent); - - // simple non-numeric merging - if (!(0 in params) || !(0 in parent)) { - return mixin(obj, params); - } - - var i = 0; - var o = 0; - - // determine numeric gaps - while (i in params) { - i++; - } - - while (o in parent) { - o++; - } - - // offset numeric indices in params before merge - for (i--; i >= 0; i--) { - params[i + o] = params[i]; - - // create holes for the merge when necessary - if (i < o) { - delete params[i]; - } - } - - return mixin(obj, params); -} - -// restore obj props after function -function restore(fn, obj) { - var props = new Array(arguments.length - 2); - var vals = new Array(arguments.length - 2); - - for (var i = 0; i < props.length; i++) { - props[i] = arguments[i + 2]; - vals[i] = obj[props[i]]; - } - - return function () { - // restore vals - for (var i = 0; i < props.length; i++) { - obj[props[i]] = vals[i]; - } - - return fn.apply(this, arguments); - }; -} - -// send an OPTIONS response -function sendOptionsResponse(res, options, next) { - try { - var body = options.join(','); - res.set('Allow', body); - res.send(body); - } catch (err) { - next(err); - } -} - -// wrap a function -function wrap(old, fn) { - return function proxy() { - 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); - }; -} diff --git a/lib/router/layer.js b/lib/router/layer.js deleted file mode 100644 index 4dc8e86d..00000000 --- a/lib/router/layer.js +++ /dev/null @@ -1,181 +0,0 @@ -/*! - * express - * Copyright(c) 2009-2013 TJ Holowaychuk - * Copyright(c) 2013 Roman Shtylman - * Copyright(c) 2014-2015 Douglas Christopher Wilson - * MIT Licensed - */ - -'use strict'; - -/** - * Module dependencies. - * @private - */ - -var pathRegexp = require('path-to-regexp'); -var debug = require('debug')('express:router:layer'); - -/** - * Module variables. - * @private - */ - -var hasOwnProperty = Object.prototype.hasOwnProperty; - -/** - * Module exports. - * @public - */ - -module.exports = Layer; - -function Layer(path, options, fn) { - if (!(this instanceof Layer)) { - return new Layer(path, options, fn); - } - - debug('new %o', path) - var opts = options || {}; - - this.handle = fn; - this.name = fn.name || ''; - this.params = undefined; - this.path = undefined; - this.regexp = pathRegexp(path, this.keys = [], opts); - - // set fast path flags - this.regexp.fast_star = path === '*' - this.regexp.fast_slash = path === '/' && opts.end === false -} - -/** - * 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 - * populate `.params`. - * - * @param {String} path - * @return {Boolean} - * @api private - */ - -Layer.prototype.match = function match(path) { - var match - - if (path != null) { - // fast path non-ending match for / (any path matches) - if (this.regexp.fast_slash) { - this.params = {} - this.path = '' - return true - } - - // fast path for * (everything matched in a param) - if (this.regexp.fast_star) { - this.params = {'0': decode_param(path)} - this.path = path - return true - } - - // match the path - match = this.regexp.exec(path) - } - - if (!match) { - this.params = undefined; - this.path = undefined; - return false; - } - - // store values - this.params = {}; - this.path = match[0] - - var keys = this.keys; - var params = this.params; - - for (var i = 1; i < match.length; i++) { - var key = keys[i - 1]; - var prop = key.name; - var val = decode_param(match[i]) - - if (val !== undefined || !(hasOwnProperty.call(params, prop))) { - params[prop] = val; - } - } - - return true; -}; - -/** - * Decode param value. - * - * @param {string} val - * @return {string} - * @private - */ - -function decode_param(val) { - if (typeof val !== 'string' || val.length === 0) { - return val; - } - - try { - return decodeURIComponent(val); - } catch (err) { - if (err instanceof URIError) { - err.message = 'Failed to decode param \'' + val + '\''; - err.status = err.statusCode = 400; - } - - throw err; - } -} diff --git a/lib/router/route.js b/lib/router/route.js deleted file mode 100644 index a65756d6..00000000 --- a/lib/router/route.js +++ /dev/null @@ -1,230 +0,0 @@ -/*! - * express - * Copyright(c) 2009-2013 TJ Holowaychuk - * Copyright(c) 2013 Roman Shtylman - * Copyright(c) 2014-2015 Douglas Christopher Wilson - * MIT Licensed - */ - -'use strict'; - -/** - * Module dependencies. - * @private - */ - -var debug = require('debug')('express:router:route'); -var flatten = require('array-flatten'); -var Layer = require('./layer'); -var methods = require('methods'); - -/** - * Module variables. - * @private - */ - -var slice = Array.prototype.slice; -var toString = Object.prototype.toString; - -/** - * Module exports. - * @public - */ - -module.exports = Route; - -/** - * Initialize `Route` with the given `path`, - * - * @param {String} path - * @public - */ - -function Route(path) { - this.path = path; - this.stack = []; - - debug('new %o', path) - - // route handlers for various http methods - this.methods = {}; -} - -/** - * Determine if the route handles a given method. - * @private - */ - -Route.prototype._handles_method = function _handles_method(method) { - if (this.methods._all) { - return true; - } - - // normalize name - var name = typeof method === 'string' - ? method.toLowerCase() - : method - - if (name === 'head' && !this.methods['head']) { - name = 'get'; - } - - return Boolean(this.methods[name]); -}; - -/** - * @return {Array} supported HTTP methods - * @private - */ - -Route.prototype._options = function _options() { - var methods = Object.keys(this.methods); - - // append automatic head - if (this.methods.get && !this.methods.head) { - methods.push('head'); - } - - for (var i = 0; i < methods.length; i++) { - // make upper case - methods[i] = methods[i].toUpperCase(); - } - - return methods; -}; - -/** - * dispatch req, res into this route - * @private - */ - -Route.prototype.dispatch = function dispatch(req, res, done) { - var idx = 0; - var stack = this.stack; - var sync = 0 - - if (stack.length === 0) { - return done(); - } - var method = typeof req.method === 'string' - ? req.method.toLowerCase() - : req.method - - if (method === 'head' && !this.methods['head']) { - method = 'get'; - } - - req.route = this; - - next(); - - function next(err) { - // signal to exit route - if (err && err === 'route') { - return done(); - } - - // signal to exit router - if (err && err === 'router') { - return done(err) - } - - // max sync stack - if (++sync > 100) { - return setImmediate(next, err) - } - - var layer = stack[idx++] - - // end of layers - if (!layer) { - return done(err) - } - - if (layer.method && layer.method !== method) { - next(err) - } else if (err) { - layer.handle_error(err, req, res, next); - } else { - layer.handle_request(req, res, next); - } - - sync = 0 - } -}; - -/** - * Add a handler for all HTTP verbs to this route. - * - * Behaves just like middleware and can respond or call `next` - * to continue processing. - * - * You can use multiple `.all` call to add multiple handlers. - * - * function check_something(req, res, next){ - * next(); - * }; - * - * function validate_user(req, res, next){ - * next(); - * }; - * - * route - * .all(validate_user) - * .all(check_something) - * .get(function(req, res, next){ - * res.send('hello world'); - * }); - * - * @param {function} handler - * @return {Route} for chaining - * @api public - */ - -Route.prototype.all = function all() { - var handles = flatten(slice.call(arguments)); - - for (var i = 0; i < handles.length; i++) { - var handle = handles[i]; - - if (typeof handle !== 'function') { - var type = toString.call(handle); - var msg = 'Route.all() requires a callback function but got a ' + type - throw new TypeError(msg); - } - - var layer = Layer('/', {}, handle); - layer.method = undefined; - - this.methods._all = true; - this.stack.push(layer); - } - - return this; -}; - -methods.forEach(function(method){ - Route.prototype[method] = function(){ - var handles = flatten(slice.call(arguments)); - - for (var i = 0; i < handles.length; i++) { - var handle = handles[i]; - - if (typeof handle !== 'function') { - var type = toString.call(handle); - var msg = 'Route.' + method + '() requires a callback function but got a ' + type - throw new Error(msg); - } - - debug('%s %o', method, this.path) - - var layer = Layer('/', {}, handle); - layer.method = method; - - this.methods[method] = true; - this.stack.push(layer); - } - - return this; - }; -}); diff --git a/lib/utils.js b/lib/utils.js deleted file mode 100644 index 56e12b9b..00000000 --- a/lib/utils.js +++ /dev/null @@ -1,303 +0,0 @@ -/*! - * express - * Copyright(c) 2009-2013 TJ Holowaychuk - * Copyright(c) 2014-2015 Douglas Christopher Wilson - * MIT Licensed - */ - -'use strict'; - -/** - * Module dependencies. - * @api private - */ - -var Buffer = require('safe-buffer').Buffer -var contentDisposition = require('content-disposition'); -var contentType = require('content-type'); -var deprecate = require('depd')('express'); -var flatten = require('array-flatten'); -var mime = require('send').mime; -var etag = require('etag'); -var proxyaddr = require('proxy-addr'); -var qs = require('qs'); -var querystring = require('querystring'); - -/** - * Return strong ETag for `body`. - * - * @param {String|Buffer} body - * @param {String} [encoding] - * @return {String} - * @api private - */ - -exports.etag = createETagGenerator({ weak: false }) - -/** - * Return weak ETag for `body`. - * - * @param {String|Buffer} body - * @param {String} [encoding] - * @return {String} - * @api private - */ - -exports.wetag = createETagGenerator({ weak: true }) - -/** - * Check if `path` looks absolute. - * - * @param {String} path - * @return {Boolean} - * @api private - */ - -exports.isAbsolute = function(path){ - if ('/' === path[0]) return true; - if (':' === path[1] && ('\\' === path[2] || '/' === path[2])) return true; // Windows device path - if ('\\\\' === path.substring(0, 2)) return true; // Microsoft Azure absolute path -}; - -/** - * Flatten the given `arr`. - * - * @param {Array} arr - * @return {Array} - * @api private - */ - -exports.flatten = deprecate.function(flatten, - 'utils.flatten: use array-flatten npm module instead'); - -/** - * Normalize the given `type`, for example "html" becomes "text/html". - * - * @param {String} type - * @return {Object} - * @api private - */ - -exports.normalizeType = function(type){ - return ~type.indexOf('/') - ? acceptParams(type) - : { value: mime.lookup(type), params: {} }; -}; - -/** - * Normalize `types`, for example "html" becomes "text/html". - * - * @param {Array} types - * @return {Array} - * @api private - */ - -exports.normalizeTypes = function(types){ - var ret = []; - - for (var i = 0; i < types.length; ++i) { - ret.push(exports.normalizeType(types[i])); - } - - return ret; -}; - -/** - * Generate Content-Disposition header appropriate for the filename. - * non-ascii filenames are urlencoded and a filename* parameter is added - * - * @param {String} filename - * @return {String} - * @api private - */ - -exports.contentDisposition = deprecate.function(contentDisposition, - 'utils.contentDisposition: use content-disposition npm module instead'); - -/** - * Parse accept params `str` returning an - * object with `.value`, `.quality` and `.params`. - * - * @param {String} str - * @return {Object} - * @api private - */ - -function acceptParams (str) { - var parts = str.split(/ *; */); - var ret = { value: parts[0], quality: 1, params: {} } - - for (var i = 1; i < parts.length; ++i) { - var pms = parts[i].split(/ *= */); - if ('q' === pms[0]) { - ret.quality = parseFloat(pms[1]); - } else { - ret.params[pms[0]] = pms[1]; - } - } - - return ret; -} - -/** - * Compile "etag" value to function. - * - * @param {Boolean|String|Function} val - * @return {Function} - * @api private - */ - -exports.compileETag = function(val) { - var fn; - - if (typeof val === 'function') { - return val; - } - - switch (val) { - case true: - case 'weak': - fn = exports.wetag; - break; - case false: - break; - case 'strong': - fn = exports.etag; - break; - default: - throw new TypeError('unknown value for etag function: ' + val); - } - - return fn; -} - -/** - * Compile "query parser" value to function. - * - * @param {String|Function} val - * @return {Function} - * @api private - */ - -exports.compileQueryParser = function compileQueryParser(val) { - var fn; - - if (typeof val === 'function') { - return val; - } - - switch (val) { - case true: - case 'simple': - fn = querystring.parse; - break; - case false: - fn = newObject; - break; - case 'extended': - fn = parseExtendedQueryString; - break; - default: - throw new TypeError('unknown value for query parser function: ' + val); - } - - return fn; -} - -/** - * Compile "proxy trust" value to function. - * - * @param {Boolean|String|Number|Array|Function} val - * @return {Function} - * @api private - */ - -exports.compileTrust = function(val) { - if (typeof val === 'function') return val; - - if (val === true) { - // Support plain true/false - return function(){ return true }; - } - - if (typeof val === 'number') { - // Support trusting hop count - return function(a, i){ return i < val }; - } - - if (typeof val === 'string') { - // Support comma-separated values - val = val.split(',') - .map(function (v) { return v.trim() }) - } - - return proxyaddr.compile(val || []); -} - -/** - * Set the charset in a given Content-Type string. - * - * @param {String} type - * @param {String} charset - * @return {String} - * @api private - */ - -exports.setCharset = function setCharset(type, charset) { - if (!type || !charset) { - return type; - } - - // parse type - var parsed = contentType.parse(type); - - // set charset - parsed.parameters.charset = charset; - - // format type - return contentType.format(parsed); -}; - -/** - * Create an ETag generator function, generating ETags with - * the given options. - * - * @param {object} options - * @return {function} - * @private - */ - -function createETagGenerator (options) { - return function generateETag (body, encoding) { - var buf = !Buffer.isBuffer(body) - ? Buffer.from(body, encoding) - : body - - return etag(buf, options) - } -} - -/** - * Parse an extended query string with qs. - * - * @param {String} str - * @return {Object} - * @private - */ - -function parseExtendedQueryString(str) { - return qs.parse(str, { - allowPrototypes: true - }); -} - -/** - * Return new empty object. - * - * @return {Object} - * @api private - */ - -function newObject() { - return {}; -} diff --git a/lib/view.js b/lib/view.js deleted file mode 100644 index c08ab4d8..00000000 --- a/lib/view.js +++ /dev/null @@ -1,182 +0,0 @@ -/*! - * express - * Copyright(c) 2009-2013 TJ Holowaychuk - * Copyright(c) 2013 Roman Shtylman - * Copyright(c) 2014-2015 Douglas Christopher Wilson - * MIT Licensed - */ - -'use strict'; - -/** - * Module dependencies. - * @private - */ - -var debug = require('debug')('express:view'); -var path = require('path'); -var fs = require('fs'); - -/** - * Module variables. - * @private - */ - -var dirname = path.dirname; -var basename = path.basename; -var extname = path.extname; -var join = path.join; -var resolve = path.resolve; - -/** - * Module exports. - * @public - */ - -module.exports = View; - -/** - * Initialize a new `View` with the given `name`. - * - * Options: - * - * - `defaultEngine` the default template engine name - * - `engines` template engine require() cache - * - `root` root path for view lookup - * - * @param {string} name - * @param {object} options - * @public - */ - -function View(name, options) { - var opts = options || {}; - - this.defaultEngine = opts.defaultEngine; - this.ext = extname(name); - this.name = name; - this.root = opts.root; - - if (!this.ext && !this.defaultEngine) { - throw new Error('No default engine was specified and no extension was provided.'); - } - - var fileName = name; - - if (!this.ext) { - // get extension from default engine name - this.ext = this.defaultEngine[0] !== '.' - ? '.' + this.defaultEngine - : this.defaultEngine; - - fileName += this.ext; - } - - if (!opts.engines[this.ext]) { - // load engine - var mod = this.ext.slice(1) - debug('require "%s"', mod) - - // default engine export - var fn = require(mod).__express - - if (typeof fn !== 'function') { - throw new Error('Module "' + mod + '" does not provide a view engine.') - } - - opts.engines[this.ext] = fn - } - - // store loaded engine - this.engine = opts.engines[this.ext]; - - // lookup path - this.path = this.lookup(fileName); -} - -/** - * Lookup view by the given `name` - * - * @param {string} name - * @private - */ - -View.prototype.lookup = function lookup(name) { - var path; - var roots = [].concat(this.root); - - debug('lookup "%s"', name); - - for (var i = 0; i < roots.length && !path; i++) { - var root = roots[i]; - - // resolve the path - var loc = resolve(root, name); - var dir = dirname(loc); - var file = basename(loc); - - // resolve the file - path = this.resolve(dir, file); - } - - return path; -}; - -/** - * Render with the given options. - * - * @param {object} options - * @param {function} callback - * @private - */ - -View.prototype.render = function render(options, callback) { - debug('render "%s"', this.path); - this.engine(this.path, options, callback); -}; - -/** - * Resolve the file within the given directory. - * - * @param {string} dir - * @param {string} file - * @private - */ - -View.prototype.resolve = function resolve(dir, file) { - var ext = this.ext; - - // . - var path = join(dir, file); - var stat = tryStat(path); - - if (stat && stat.isFile()) { - return path; - } - - // /index. - path = join(dir, basename(file, ext), 'index' + ext); - stat = tryStat(path); - - if (stat && stat.isFile()) { - return path; - } -}; - -/** - * Return a stat, maybe. - * - * @param {string} path - * @return {fs.Stats} - * @private - */ - -function tryStat(path) { - debug('stat "%s"', path); - - try { - return fs.statSync(path); - } catch (e) { - return undefined; - } -} diff --git a/package-lock.json b/package-lock.json index 5a50a1d5..26e39dce 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,45 +9,13 @@ "version": "1.0.0", "license": "MIT", "dependencies": { - "accepts": "~1.3.8", - "array-flatten": "1.1.1", - "body-parser": "1.20.2", - "content-disposition": "0.5.4", - "content-type": "~1.0.4", - "cookie": "0.5.0", - "cookie-signature": "1.0.6", - "debug": "2.6.9", - "depd": "2.0.0", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "finalhandler": "1.2.0", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "merge-descriptors": "1.0.1", - "methods": "~1.1.2", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "path-to-regexp": "0.1.7", - "proxy-addr": "~2.0.7", - "qs": "6.11.0", - "range-parser": "~1.2.1", - "safe-buffer": "5.2.1", - "send": "0.18.0", - "serve-static": "1.15.0", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "type-is": "~1.6.18", - "utils-merge": "1.0.1", - "vary": "~1.1.2" - }, - "devDependencies": { "after": "0.8.2", "connect-redis": "3.4.2", "cookie-parser": "1.4.6", "cookie-session": "2.0.0", "ejs": "3.1.9", "eslint": "8.36.0", + "express": "4.18.2", "express-session": "1.17.2", "hbs": "4.2.0", "marked": "0.7.0", @@ -68,7 +36,6 @@ "version": "1.2.6", "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -77,7 +44,6 @@ "version": "2.2.1", "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz", "integrity": "sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==", - "dev": true, "dependencies": { "@jridgewell/gen-mapping": "^0.3.0", "@jridgewell/trace-mapping": "^0.3.9" @@ -90,7 +56,6 @@ "version": "7.22.13", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz", "integrity": "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==", - "dev": true, "dependencies": { "@babel/highlight": "^7.22.13", "chalk": "^2.4.2" @@ -103,7 +68,6 @@ "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, "dependencies": { "color-convert": "^1.9.0" }, @@ -115,7 +79,6 @@ "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, "dependencies": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", @@ -129,7 +92,6 @@ "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, "dependencies": { "color-name": "1.1.3" } @@ -137,14 +99,12 @@ "node_modules/@babel/code-frame/node_modules/color-name": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" }, "node_modules/@babel/code-frame/node_modules/escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true, "engines": { "node": ">=0.8.0" } @@ -153,7 +113,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true, "engines": { "node": ">=4" } @@ -162,7 +121,6 @@ "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, "dependencies": { "has-flag": "^3.0.0" }, @@ -174,7 +132,6 @@ "version": "7.23.3", "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.23.3.tgz", "integrity": "sha512-BmR4bWbDIoFJmJ9z2cZ8Gmm2MXgEDgjdWgpKmKWUt54UGFJdlj31ECtbaDvCG/qVdG3AQ1SfpZEs01lUFbzLOQ==", - "dev": true, "engines": { "node": ">=6.9.0" } @@ -183,7 +140,6 @@ "version": "7.23.3", "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.23.3.tgz", "integrity": "sha512-Jg+msLuNuCJDyBvFv5+OKOUjWMZgd85bKjbICd3zWrKAo+bJ49HJufi7CQE0q0uR8NGyO6xkCACScNqyjHSZew==", - "dev": true, "dependencies": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.22.13", @@ -212,14 +168,12 @@ "node_modules/@babel/core/node_modules/convert-source-map": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "dev": true + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==" }, "node_modules/@babel/core/node_modules/debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dev": true, "dependencies": { "ms": "2.1.2" }, @@ -235,14 +189,12 @@ "node_modules/@babel/core/node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, "node_modules/@babel/generator": { "version": "7.23.3", "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.3.tgz", "integrity": "sha512-keeZWAV4LU3tW0qRi19HRpabC/ilM0HRBBzf9/k8FFiG4KVpiv0FIy4hHfLfFQZNhziCTPTmd59zoyv6DNISzg==", - "dev": true, "dependencies": { "@babel/types": "^7.23.3", "@jridgewell/gen-mapping": "^0.3.2", @@ -257,7 +209,6 @@ "version": "7.22.15", "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.15.tgz", "integrity": "sha512-y6EEzULok0Qvz8yyLkCvVX+02ic+By2UdOhylwUOvOn9dvYc9mKICJuuU1n1XBI02YWsNsnrY1kc6DVbjcXbtw==", - "dev": true, "dependencies": { "@babel/compat-data": "^7.22.9", "@babel/helper-validator-option": "^7.22.15", @@ -273,7 +224,6 @@ "version": "7.22.20", "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==", - "dev": true, "engines": { "node": ">=6.9.0" } @@ -282,7 +232,6 @@ "version": "7.23.0", "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", - "dev": true, "dependencies": { "@babel/template": "^7.22.15", "@babel/types": "^7.23.0" @@ -295,7 +244,6 @@ "version": "7.22.5", "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", - "dev": true, "dependencies": { "@babel/types": "^7.22.5" }, @@ -307,7 +255,6 @@ "version": "7.22.15", "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.15.tgz", "integrity": "sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==", - "dev": true, "dependencies": { "@babel/types": "^7.22.15" }, @@ -319,7 +266,6 @@ "version": "7.23.3", "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.23.3.tgz", "integrity": "sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ==", - "dev": true, "dependencies": { "@babel/helper-environment-visitor": "^7.22.20", "@babel/helper-module-imports": "^7.22.15", @@ -338,7 +284,6 @@ "version": "7.22.5", "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz", "integrity": "sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==", - "dev": true, "dependencies": { "@babel/types": "^7.22.5" }, @@ -350,7 +295,6 @@ "version": "7.22.6", "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", - "dev": true, "dependencies": { "@babel/types": "^7.22.5" }, @@ -362,7 +306,6 @@ "version": "7.22.5", "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz", "integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==", - "dev": true, "engines": { "node": ">=6.9.0" } @@ -371,7 +314,6 @@ "version": "7.22.20", "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", - "dev": true, "engines": { "node": ">=6.9.0" } @@ -380,7 +322,6 @@ "version": "7.22.15", "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.22.15.tgz", "integrity": "sha512-bMn7RmyFjY/mdECUbgn9eoSY4vqvacUnS9i9vGAGttgFWesO6B4CYWA7XlpbWgBt71iv/hfbPlynohStqnu5hA==", - "dev": true, "engines": { "node": ">=6.9.0" } @@ -389,7 +330,6 @@ "version": "7.23.2", "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.23.2.tgz", "integrity": "sha512-lzchcp8SjTSVe/fPmLwtWVBFC7+Tbn8LGHDVfDp9JGxpAY5opSaEFgt8UQvrnECWOTdji2mOWMz1rOhkHscmGQ==", - "dev": true, "dependencies": { "@babel/template": "^7.22.15", "@babel/traverse": "^7.23.2", @@ -403,7 +343,6 @@ "version": "7.22.20", "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.20.tgz", "integrity": "sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==", - "dev": true, "dependencies": { "@babel/helper-validator-identifier": "^7.22.20", "chalk": "^2.4.2", @@ -417,7 +356,6 @@ "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, "dependencies": { "color-convert": "^1.9.0" }, @@ -429,7 +367,6 @@ "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, "dependencies": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", @@ -443,7 +380,6 @@ "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, "dependencies": { "color-name": "1.1.3" } @@ -451,14 +387,12 @@ "node_modules/@babel/highlight/node_modules/color-name": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" }, "node_modules/@babel/highlight/node_modules/escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true, "engines": { "node": ">=0.8.0" } @@ -467,7 +401,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true, "engines": { "node": ">=4" } @@ -476,7 +409,6 @@ "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, "dependencies": { "has-flag": "^3.0.0" }, @@ -488,7 +420,6 @@ "version": "7.23.3", "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.3.tgz", "integrity": "sha512-uVsWNvlVsIninV2prNz/3lHCb+5CJ+e+IUBfbjToAHODtfGYLfCFuY4AU7TskI+dAKk+njsPiBjq1gKTvZOBaw==", - "dev": true, "bin": { "parser": "bin/babel-parser.js" }, @@ -500,7 +431,6 @@ "version": "7.22.15", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==", - "dev": true, "dependencies": { "@babel/code-frame": "^7.22.13", "@babel/parser": "^7.22.15", @@ -514,7 +444,6 @@ "version": "7.23.3", "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.3.tgz", "integrity": "sha512-+K0yF1/9yR0oHdE0StHuEj3uTPzwwbrLGfNOndVJVV2TqA5+j3oljJUb4nmB954FLGjNem976+B+eDuLIjesiQ==", - "dev": true, "dependencies": { "@babel/code-frame": "^7.22.13", "@babel/generator": "^7.23.3", @@ -535,7 +464,6 @@ "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dev": true, "dependencies": { "ms": "2.1.2" }, @@ -552,7 +480,6 @@ "version": "11.12.0", "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "dev": true, "engines": { "node": ">=4" } @@ -560,14 +487,12 @@ "node_modules/@babel/traverse/node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, "node_modules/@babel/types": { "version": "7.23.3", "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.3.tgz", "integrity": "sha512-OZnvoH2l8PK5eUvEcUyCt/sXgr/h+UWpVuBbOljwcrAgUl6lpchoQ++PHGyQy1AtYnVA6CEq3y5xeEI10brpXw==", - "dev": true, "dependencies": { "@babel/helper-string-parser": "^7.22.5", "@babel/helper-validator-identifier": "^7.22.20", @@ -581,7 +506,6 @@ "version": "4.4.0", "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", - "dev": true, "dependencies": { "eslint-visitor-keys": "^3.3.0" }, @@ -596,7 +520,6 @@ "version": "4.10.0", "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz", "integrity": "sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==", - "dev": true, "engines": { "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } @@ -605,7 +528,6 @@ "version": "2.1.3", "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.3.tgz", "integrity": "sha512-yZzuIG+jnVu6hNSzFEN07e8BxF3uAzYtQb6uDkaYZLo6oYZDCq454c5kB8zxnzfCYyP4MIuyBn10L0DqwujTmA==", - "dev": true, "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", @@ -628,7 +550,6 @@ "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dev": true, "dependencies": { "ms": "2.1.2" }, @@ -644,14 +565,12 @@ "node_modules/@eslint/eslintrc/node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, "node_modules/@eslint/js": { "version": "8.36.0", "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.36.0.tgz", "integrity": "sha512-lxJ9R5ygVm8ZWgYdUweoq5ownDlJ4upvoWmO4eLxBYHdMo+vZ/Rx0EN6MbKWDJOSUGrqJy2Gt+Dyv/VKml0fjg==", - "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } @@ -660,7 +579,6 @@ "version": "0.11.13", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.13.tgz", "integrity": "sha512-JSBDMiDKSzQVngfRjOdFXgFfklaXI4K9nLF49Auh21lmBWRLIK3+xTErTWD4KU54pb6coM6ESE7Awz/FNU3zgQ==", - "dev": true, "dependencies": { "@humanwhocodes/object-schema": "^2.0.1", "debug": "^4.1.1", @@ -674,7 +592,6 @@ "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dev": true, "dependencies": { "ms": "2.1.2" }, @@ -690,14 +607,12 @@ "node_modules/@humanwhocodes/config-array/node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, "node_modules/@humanwhocodes/module-importer": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", - "dev": true, "engines": { "node": ">=12.22" }, @@ -709,14 +624,12 @@ "node_modules/@humanwhocodes/object-schema": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.1.tgz", - "integrity": "sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw==", - "dev": true + "integrity": "sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw==" }, "node_modules/@istanbuljs/load-nyc-config": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", - "dev": true, "dependencies": { "camelcase": "^5.3.1", "find-up": "^4.1.0", @@ -732,7 +645,6 @@ "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, "dependencies": { "sprintf-js": "~1.0.2" } @@ -741,7 +653,6 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, "dependencies": { "locate-path": "^5.0.0", "path-exists": "^4.0.0" @@ -754,7 +665,6 @@ "version": "3.14.1", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", - "dev": true, "dependencies": { "argparse": "^1.0.7", "esprima": "^4.0.0" @@ -767,7 +677,6 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, "dependencies": { "p-locate": "^4.1.0" }, @@ -779,7 +688,6 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, "dependencies": { "p-try": "^2.0.0" }, @@ -794,7 +702,6 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, "dependencies": { "p-limit": "^2.2.0" }, @@ -806,7 +713,6 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true, "engines": { "node": ">=8" } @@ -815,7 +721,6 @@ "version": "0.1.3", "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", - "dev": true, "engines": { "node": ">=8" } @@ -824,7 +729,6 @@ "version": "0.3.3", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", - "dev": true, "dependencies": { "@jridgewell/set-array": "^1.0.1", "@jridgewell/sourcemap-codec": "^1.4.10", @@ -838,7 +742,6 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==", - "dev": true, "engines": { "node": ">=6.0.0" } @@ -847,7 +750,6 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", - "dev": true, "engines": { "node": ">=6.0.0" } @@ -855,14 +757,12 @@ "node_modules/@jridgewell/sourcemap-codec": { "version": "1.4.15", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", - "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", - "dev": true + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==" }, "node_modules/@jridgewell/trace-mapping": { "version": "0.3.20", "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.20.tgz", "integrity": "sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q==", - "dev": true, "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" @@ -872,7 +772,6 @@ "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "dev": true, "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" @@ -885,7 +784,6 @@ "version": "2.0.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "dev": true, "engines": { "node": ">= 8" } @@ -894,7 +792,6 @@ "version": "1.2.8", "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "dev": true, "dependencies": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" @@ -919,7 +816,6 @@ "version": "8.11.2", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.2.tgz", "integrity": "sha512-nc0Axzp/0FILLEVsm4fNwLCwMttvhEI263QtVPQcbpfZZ3ts0hLsZGOpE6czNlid7CJ9MlyH8reXkpsf3YUY4w==", - "dev": true, "bin": { "acorn": "bin/acorn" }, @@ -931,7 +827,6 @@ "version": "5.3.2", "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "dev": true, "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } @@ -939,14 +834,12 @@ "node_modules/after": { "version": "0.8.2", "resolved": "https://registry.npmjs.org/after/-/after-0.8.2.tgz", - "integrity": "sha512-QbJ0NTQ/I9DI3uSJA4cbexiwQeRAfjPScqIbSjUDd9TOrcg6pTkdgziesOqxBMBzit8vFCTwrP27t13vFOORRA==", - "dev": true + "integrity": "sha512-QbJ0NTQ/I9DI3uSJA4cbexiwQeRAfjPScqIbSjUDd9TOrcg6pTkdgziesOqxBMBzit8vFCTwrP27t13vFOORRA==" }, "node_modules/aggregate-error": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", - "dev": true, "dependencies": { "clean-stack": "^2.0.0", "indent-string": "^4.0.0" @@ -959,7 +852,6 @@ "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -975,7 +867,6 @@ "version": "4.1.1", "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", - "dev": true, "engines": { "node": ">=6" } @@ -984,7 +875,6 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, "engines": { "node": ">=8" } @@ -993,7 +883,6 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, "dependencies": { "color-convert": "^2.0.1" }, @@ -1008,7 +897,6 @@ "version": "3.1.3", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "dev": true, "dependencies": { "normalize-path": "^3.0.0", "picomatch": "^2.0.4" @@ -1021,7 +909,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/append-transform/-/append-transform-2.0.0.tgz", "integrity": "sha512-7yeyCEurROLQJFv5Xj4lEGTy0borxepjFv1g22oAdqFu//SrAlDl1O1Nxx15SH1RoliUml6p8dwJW9jvZughhg==", - "dev": true, "dependencies": { "default-require-extensions": "^3.0.0" }, @@ -1032,14 +919,12 @@ "node_modules/archy": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz", - "integrity": "sha512-Xg+9RwCg/0p32teKdGMPTPnVXKD0w3DfHnFTficozsAgsvq2XenPJq/MYpzzQ/v8zrOyJn6Ds39VA4JIDwFfqw==", - "dev": true + "integrity": "sha512-Xg+9RwCg/0p32teKdGMPTPnVXKD0w3DfHnFTficozsAgsvq2XenPJq/MYpzzQ/v8zrOyJn6Ds39VA4JIDwFfqw==" }, "node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" }, "node_modules/array-flatten": { "version": "1.1.1", @@ -1049,32 +934,27 @@ "node_modules/asap": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", - "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", - "dev": true + "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==" }, "node_modules/async": { "version": "3.2.5", "resolved": "https://registry.npmjs.org/async/-/async-3.2.5.tgz", - "integrity": "sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==", - "dev": true + "integrity": "sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==" }, "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", - "dev": true + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, "node_modules/basic-auth": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz", "integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==", - "dev": true, "dependencies": { "safe-buffer": "5.1.2" }, @@ -1085,46 +965,20 @@ "node_modules/basic-auth/node_modules/safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" }, "node_modules/binary-extensions": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", - "dev": true, "engines": { "node": ">=8" } }, - "node_modules/body-parser": { - "version": "1.20.2", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz", - "integrity": "sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==", - "dependencies": { - "bytes": "3.1.2", - "content-type": "~1.0.5", - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "on-finished": "2.4.1", - "qs": "6.11.0", - "raw-body": "2.5.2", - "type-is": "~1.6.18", - "unpipe": "1.0.0" - }, - "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" - } - }, "node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -1134,7 +988,6 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, "dependencies": { "fill-range": "^7.0.1" }, @@ -1145,14 +998,12 @@ "node_modules/browser-stdout": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", - "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", - "dev": true + "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==" }, "node_modules/browserslist": { "version": "4.22.1", "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.22.1.tgz", "integrity": "sha512-FEVc202+2iuClEhZhrWy6ZiAcRLvNMyYcxZ8raemul1DYVOVdFsbqckWLdsixQZCpJlwe77Z3UTalE7jsjnKfQ==", - "dev": true, "funding": [ { "type": "opencollective", @@ -1192,7 +1043,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/caching-transform/-/caching-transform-4.0.0.tgz", "integrity": "sha512-kpqOvwXnjjN44D89K5ccQC+RUrsy7jB/XLlRrx0D7/2HNcTPqzsb6XgYoErwko6QsV184CA2YgS1fxDiiDZMWA==", - "dev": true, "dependencies": { "hasha": "^5.0.0", "make-dir": "^3.0.0", @@ -1220,7 +1070,6 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true, "engines": { "node": ">=6" } @@ -1229,7 +1078,6 @@ "version": "5.3.1", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true, "engines": { "node": ">=6" } @@ -1238,7 +1086,6 @@ "version": "1.0.30001561", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001561.tgz", "integrity": "sha512-NTt0DNoKe958Q0BE0j0c1V9jbUzhBxHIEJy7asmGrpE0yG63KTV7PLHPnK2E1O9RsQrQ081I3NLuXGS6zht3cw==", - "dev": true, "funding": [ { "type": "opencollective", @@ -1258,7 +1105,6 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -1274,7 +1120,6 @@ "version": "3.5.3", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", - "dev": true, "funding": [ { "type": "individual", @@ -1301,7 +1146,6 @@ "version": "5.1.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, "dependencies": { "is-glob": "^4.0.1" }, @@ -1313,7 +1157,6 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", - "dev": true, "engines": { "node": ">=6" } @@ -1322,7 +1165,6 @@ "version": "7.0.4", "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", - "dev": true, "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.0", @@ -1333,7 +1175,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, "dependencies": { "color-name": "~1.1.4" }, @@ -1344,14 +1185,12 @@ "node_modules/color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, "node_modules/combined-stream": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "dev": true, "dependencies": { "delayed-stream": "~1.0.0" }, @@ -1362,26 +1201,22 @@ "node_modules/commondir": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", - "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==", - "dev": true + "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==" }, "node_modules/component-emitter": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", - "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==", - "dev": true + "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==" }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" }, "node_modules/connect-redis": { "version": "3.4.2", "resolved": "https://registry.npmjs.org/connect-redis/-/connect-redis-3.4.2.tgz", "integrity": "sha512-ozA1Z0GDnsCJECfNyNJOqPuW3Fk43fUbKC65Sa/V9hkCBNtXsFU2xtTOVsQGUsflpywuJMgGOV4xrnKzIPFqvA==", - "dev": true, "dependencies": { "debug": "^4.1.1", "redis": "^2.8.0" @@ -1394,7 +1229,6 @@ "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dev": true, "dependencies": { "ms": "2.1.2" }, @@ -1410,8 +1244,7 @@ "node_modules/connect-redis/node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, "node_modules/content-disposition": { "version": "0.5.4", @@ -1435,8 +1268,7 @@ "node_modules/convert-source-map": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", - "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", - "dev": true + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==" }, "node_modules/cookie": { "version": "0.5.0", @@ -1450,7 +1282,6 @@ "version": "1.4.6", "resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.6.tgz", "integrity": "sha512-z3IzaNjdwUC2olLIB5/ITd0/setiaFMLYiZJle7xg5Fe9KWAceil7xszYfHHBtDFYLSgJduS2Ty0P1uJdPDJeA==", - "dev": true, "dependencies": { "cookie": "0.4.1", "cookie-signature": "1.0.6" @@ -1463,7 +1294,6 @@ "version": "0.4.1", "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz", "integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==", - "dev": true, "engines": { "node": ">= 0.6" } @@ -1472,7 +1302,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/cookie-session/-/cookie-session-2.0.0.tgz", "integrity": "sha512-hKvgoThbw00zQOleSlUr2qpvuNweoqBtxrmx0UFosx6AGi9lYtLoA+RbsvknrEX8Pr6MDbdWAb2j6SnMn+lPsg==", - "dev": true, "dependencies": { "cookies": "0.8.0", "debug": "3.2.7", @@ -1487,7 +1316,6 @@ "version": "3.2.7", "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, "dependencies": { "ms": "^2.1.1" } @@ -1495,8 +1323,7 @@ "node_modules/cookie-session/node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" }, "node_modules/cookie-signature": { "version": "1.0.6", @@ -1506,14 +1333,12 @@ "node_modules/cookiejar": { "version": "2.1.4", "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.4.tgz", - "integrity": "sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==", - "dev": true + "integrity": "sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==" }, "node_modules/cookies": { "version": "0.8.0", "resolved": "https://registry.npmjs.org/cookies/-/cookies-0.8.0.tgz", "integrity": "sha512-8aPsApQfebXnuI+537McwYsDtjVxGm8gTIzQI3FDW6t5t/DAhERxtnbEPN/8RX+uZthoz4eCOgloXaE5cYyNow==", - "dev": true, "dependencies": { "depd": "~2.0.0", "keygrip": "~1.1.0" @@ -1526,7 +1351,6 @@ "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dev": true, "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", @@ -1548,7 +1372,6 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -1556,14 +1379,12 @@ "node_modules/deep-is": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==" }, "node_modules/default-require-extensions": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-3.0.1.tgz", "integrity": "sha512-eXTJmRbm2TIt9MgWTsOH1wEuhew6XGZcMeGKCtLedIg/NCsg1iBePXkceTdK4Fii7pzmN9tGsZhKzZ4h7O/fxw==", - "dev": true, "dependencies": { "strip-bom": "^4.0.0" }, @@ -1591,7 +1412,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", - "dev": true, "engines": { "node": ">=0.4.0" } @@ -1617,7 +1437,6 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.4.tgz", "integrity": "sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==", - "dev": true, "dependencies": { "asap": "^2.0.0", "wrappy": "1" @@ -1627,7 +1446,6 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", - "dev": true, "engines": { "node": ">=0.3.1" } @@ -1636,7 +1454,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", - "dev": true, "dependencies": { "esutils": "^2.0.2" }, @@ -1647,8 +1464,7 @@ "node_modules/double-ended-queue": { "version": "2.1.0-0", "resolved": "https://registry.npmjs.org/double-ended-queue/-/double-ended-queue-2.1.0-0.tgz", - "integrity": "sha512-+BNfZ+deCo8hMNpDqDnvT+c0XpJ5cUa6mqYq89bho2Ifze4URTqRkcwR399hWoTrTkbZ/XJYDgP6rc7pRgffEQ==", - "dev": true + "integrity": "sha512-+BNfZ+deCo8hMNpDqDnvT+c0XpJ5cUa6mqYq89bho2Ifze4URTqRkcwR399hWoTrTkbZ/XJYDgP6rc7pRgffEQ==" }, "node_modules/ee-first": { "version": "1.1.1", @@ -1659,7 +1475,6 @@ "version": "3.1.9", "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.9.tgz", "integrity": "sha512-rC+QVNMJWv+MtPgkt0y+0rVEIdbtxVADApW9JXrUVlzHetgcyczP/E7DJmWJ4fJCZF2cPcBk0laWO9ZHMG3DmQ==", - "dev": true, "dependencies": { "jake": "^10.8.5" }, @@ -1673,14 +1488,12 @@ "node_modules/electron-to-chromium": { "version": "1.4.581", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.581.tgz", - "integrity": "sha512-6uhqWBIapTJUxgPTCHH9sqdbxIMPt7oXl0VcAL1kOtlU6aECdcMncCrX5Z7sHQ/invtrC9jUQUef7+HhO8vVFw==", - "dev": true + "integrity": "sha512-6uhqWBIapTJUxgPTCHH9sqdbxIMPt7oXl0VcAL1kOtlU6aECdcMncCrX5Z7sHQ/invtrC9jUQUef7+HhO8vVFw==" }, "node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" }, "node_modules/encodeurl": { "version": "1.0.2", @@ -1693,14 +1506,12 @@ "node_modules/es6-error": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", - "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==", - "dev": true + "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==" }, "node_modules/escalade": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", - "dev": true, "engines": { "node": ">=6" } @@ -1714,7 +1525,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true, "engines": { "node": ">=10" }, @@ -1726,7 +1536,6 @@ "version": "8.36.0", "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.36.0.tgz", "integrity": "sha512-Y956lmS7vDqomxlaaQAHVmeb4tNMp2FWIvU/RnU5BD3IKMD/MJPr76xdyr68P8tV1iNMvN2mRK0yy3c+UjL+bw==", - "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.4.0", @@ -1783,7 +1592,6 @@ "version": "7.2.2", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", - "dev": true, "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^5.2.0" @@ -1799,7 +1607,6 @@ "version": "3.4.3", "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", - "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, @@ -1811,7 +1618,6 @@ "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dev": true, "dependencies": { "ms": "2.1.2" }, @@ -1827,14 +1633,12 @@ "node_modules/eslint/node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, "node_modules/espree": { "version": "9.6.1", "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", - "dev": true, "dependencies": { "acorn": "^8.9.0", "acorn-jsx": "^5.3.2", @@ -1851,7 +1655,6 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true, "bin": { "esparse": "bin/esparse.js", "esvalidate": "bin/esvalidate.js" @@ -1864,7 +1667,6 @@ "version": "1.5.0", "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", - "dev": true, "dependencies": { "estraverse": "^5.1.0" }, @@ -1876,7 +1678,6 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "dev": true, "dependencies": { "estraverse": "^5.2.0" }, @@ -1888,7 +1689,6 @@ "version": "5.3.0", "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, "engines": { "node": ">=4.0" } @@ -1897,7 +1697,6 @@ "version": "2.0.3", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -1910,11 +1709,51 @@ "node": ">= 0.6" } }, + "node_modules/express": { + "version": "4.18.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz", + "integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==", + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.20.1", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.5.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.2.0", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.7", + "qs": "6.11.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.18.0", + "serve-static": "1.15.0", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + } + }, "node_modules/express-session": { "version": "1.17.2", "resolved": "https://registry.npmjs.org/express-session/-/express-session-1.17.2.tgz", "integrity": "sha512-mPcYcLA0lvh7D4Oqr5aNJFMtBMKPLl++OKKxkHzZ0U0oDq1rpKBnkR5f5vCHR26VeArlTOEF9td4x5IjICksRQ==", - "dev": true, "dependencies": { "cookie": "0.4.1", "cookie-signature": "1.0.6", @@ -1933,40 +1772,71 @@ "version": "0.4.1", "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz", "integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==", - "dev": true, "engines": { "node": ">= 0.6" } }, + "node_modules/express/node_modules/body-parser": { + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", + "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==", + "dependencies": { + "bytes": "3.1.2", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "on-finished": "2.4.1", + "qs": "6.11.0", + "raw-body": "2.5.1", + "type-is": "~1.6.18", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/express/node_modules/raw-body": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", + "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" }, "node_modules/fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" }, "node_modules/fast-levenshtein": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", - "dev": true + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==" }, "node_modules/fast-safe-stringify": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz", - "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==", - "dev": true + "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==" }, "node_modules/fastfall": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/fastfall/-/fastfall-1.5.1.tgz", "integrity": "sha512-KH6p+Z8AKPXnmA7+Iz2Lh8ARCMr+8WNPVludm1LGkZoD2MjY6LVnRMtTKhkdzI+jr0RzQWXKzKyBJm1zoHEL4Q==", - "dev": true, "dependencies": { "reusify": "^1.0.0" }, @@ -1978,7 +1848,6 @@ "version": "1.15.0", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", - "dev": true, "dependencies": { "reusify": "^1.0.4" } @@ -1987,7 +1856,6 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", - "dev": true, "dependencies": { "flat-cache": "^3.0.4" }, @@ -1999,7 +1867,6 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", "integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==", - "dev": true, "dependencies": { "minimatch": "^5.0.1" } @@ -2008,7 +1875,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, "dependencies": { "balanced-match": "^1.0.0" } @@ -2017,7 +1883,6 @@ "version": "5.1.6", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", - "dev": true, "dependencies": { "brace-expansion": "^2.0.1" }, @@ -2029,7 +1894,6 @@ "version": "7.0.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, "dependencies": { "to-regex-range": "^5.0.1" }, @@ -2058,7 +1922,6 @@ "version": "3.3.2", "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz", "integrity": "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==", - "dev": true, "dependencies": { "commondir": "^1.0.1", "make-dir": "^3.0.2", @@ -2075,7 +1938,6 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "dev": true, "dependencies": { "locate-path": "^6.0.0", "path-exists": "^4.0.0" @@ -2091,7 +1953,6 @@ "version": "5.0.2", "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", - "dev": true, "bin": { "flat": "cli.js" } @@ -2100,7 +1961,6 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.1.1.tgz", "integrity": "sha512-/qM2b3LUIaIgviBQovTLvijfyOQXPtSRnRK26ksj2J7rzPIecePUIpJsZ4T02Qg+xiAEKIs5K8dsHEd+VaKa/Q==", - "dev": true, "dependencies": { "flatted": "^3.2.9", "keyv": "^4.5.3", @@ -2113,20 +1973,17 @@ "node_modules/flatted": { "version": "3.2.9", "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.9.tgz", - "integrity": "sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==", - "dev": true + "integrity": "sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==" }, "node_modules/foreachasync": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/foreachasync/-/foreachasync-3.0.0.tgz", - "integrity": "sha512-J+ler7Ta54FwwNcx6wQRDhTIbNeyDcARMkOcguEqnEdtm0jKvN3Li3PDAb2Du3ubJYEWfYL83XMROXdsXAXycw==", - "dev": true + "integrity": "sha512-J+ler7Ta54FwwNcx6wQRDhTIbNeyDcARMkOcguEqnEdtm0jKvN3Li3PDAb2Du3ubJYEWfYL83XMROXdsXAXycw==" }, "node_modules/foreground-child": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-2.0.0.tgz", "integrity": "sha512-dCIq9FpEcyQyXKCkyzmlPTFNgrCzPudOe+mhvJU5zAtlBnGVy2yKxtfsxK2tQBThwq225jcvBjpw1Gr40uzZCA==", - "dev": true, "dependencies": { "cross-spawn": "^7.0.0", "signal-exit": "^3.0.2" @@ -2139,7 +1996,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", - "dev": true, "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", @@ -2153,7 +2009,6 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/formidable/-/formidable-2.1.2.tgz", "integrity": "sha512-CM3GuJ57US06mlpQ47YcunuUZ9jpm8Vx+P2CGt2j7HpgkKZO/DJYQ0Bobim8G6PFQmK5lOqOOdUXboU+h73A4g==", - "dev": true, "dependencies": { "dezalgo": "^1.0.4", "hexoid": "^1.0.0", @@ -2184,7 +2039,6 @@ "version": "1.3.2", "resolved": "https://registry.npmjs.org/fromentries/-/fromentries-1.3.2.tgz", "integrity": "sha512-cHEpEQHUg0f8XdtZCc2ZAhrHzKzT0MrFUTcvx+hfxYu7rGMDc5SKoXFh+n4YigxsHXRzc6OrCshdR1bWH6HHyg==", - "dev": true, "funding": [ { "type": "github", @@ -2203,14 +2057,12 @@ "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" }, "node_modules/fsevents": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, "hasInstallScript": true, "optional": true, "os": [ @@ -2232,7 +2084,6 @@ "version": "1.0.0-beta.2", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", - "dev": true, "engines": { "node": ">=6.9.0" } @@ -2241,7 +2092,6 @@ "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true, "engines": { "node": "6.* || 8.* || >= 10.*" } @@ -2264,7 +2114,6 @@ "version": "0.1.0", "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", - "dev": true, "engines": { "node": ">=8.0.0" } @@ -2273,7 +2122,6 @@ "version": "7.2.0", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", - "dev": true, "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -2293,7 +2141,6 @@ "version": "6.0.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "dev": true, "dependencies": { "is-glob": "^4.0.3" }, @@ -2305,7 +2152,6 @@ "version": "13.23.0", "resolved": "https://registry.npmjs.org/globals/-/globals-13.23.0.tgz", "integrity": "sha512-XAmF0RjlrjY23MA51q3HltdlGxUpXPvg0GioKiD9X6HD28iMjo2dKC8Vqwm7lne4GNr78+RHTfliktR6ZH09wA==", - "dev": true, "dependencies": { "type-fest": "^0.20.2" }, @@ -2330,20 +2176,17 @@ "node_modules/graceful-fs": { "version": "4.2.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "dev": true + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" }, "node_modules/grapheme-splitter": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz", - "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==", - "dev": true + "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==" }, "node_modules/handlebars": { "version": "4.7.7", "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.7.tgz", "integrity": "sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA==", - "dev": true, "dependencies": { "minimist": "^1.2.5", "neo-async": "^2.6.0", @@ -2364,7 +2207,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, "engines": { "node": ">=8" } @@ -2406,7 +2248,6 @@ "version": "5.2.2", "resolved": "https://registry.npmjs.org/hasha/-/hasha-5.2.2.tgz", "integrity": "sha512-Hrp5vIK/xr5SkeN2onO32H0MgNZ0f17HRNH39WfL0SYUNOTZ5Lz1TJ8Pajo/87dYGEFlLMm7mIc/k/s6Bvz9HQ==", - "dev": true, "dependencies": { "is-stream": "^2.0.0", "type-fest": "^0.8.0" @@ -2422,7 +2263,6 @@ "version": "0.8.1", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", - "dev": true, "engines": { "node": ">=8" } @@ -2442,7 +2282,6 @@ "version": "4.2.0", "resolved": "https://registry.npmjs.org/hbs/-/hbs-4.2.0.tgz", "integrity": "sha512-dQwHnrfWlTk5PvG9+a45GYpg0VpX47ryKF8dULVd6DtwOE6TEcYQXQ5QM6nyOx/h7v3bvEQbdn19EDAcfUAgZg==", - "dev": true, "dependencies": { "handlebars": "4.7.7", "walk": "2.3.15" @@ -2456,7 +2295,6 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", - "dev": true, "bin": { "he": "bin/he" } @@ -2465,7 +2303,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/hexoid/-/hexoid-1.0.0.tgz", "integrity": "sha512-QFLV0taWQOZtvIRIAdBChesmogZrtuXvVWsFHZTk2SU+anspqZ2vMnoLg7IE1+Uk16N19APic1BuF8bC8c2m5g==", - "dev": true, "engines": { "node": ">=8" } @@ -2473,8 +2310,7 @@ "node_modules/html-escaper": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", - "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", - "dev": true + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==" }, "node_modules/http-errors": { "version": "2.0.0", @@ -2506,7 +2342,6 @@ "version": "5.2.4", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", - "dev": true, "engines": { "node": ">= 4" } @@ -2515,7 +2350,6 @@ "version": "3.3.0", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", - "dev": true, "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" @@ -2531,7 +2365,6 @@ "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", - "dev": true, "engines": { "node": ">=0.8.19" } @@ -2540,7 +2373,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", - "dev": true, "engines": { "node": ">=8" } @@ -2549,7 +2381,6 @@ "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "dev": true, "dependencies": { "once": "^1.3.0", "wrappy": "1" @@ -2572,7 +2403,6 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, "dependencies": { "binary-extensions": "^2.0.0" }, @@ -2584,7 +2414,6 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -2593,7 +2422,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, "engines": { "node": ">=8" } @@ -2602,7 +2430,6 @@ "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, "dependencies": { "is-extglob": "^2.1.1" }, @@ -2614,7 +2441,6 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, "engines": { "node": ">=0.12.0" } @@ -2623,7 +2449,6 @@ "version": "3.0.3", "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", - "dev": true, "engines": { "node": ">=8" } @@ -2632,7 +2457,6 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", - "dev": true, "engines": { "node": ">=8" } @@ -2641,7 +2465,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", - "dev": true, "engines": { "node": ">=8" }, @@ -2652,14 +2475,12 @@ "node_modules/is-typedarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==", - "dev": true + "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==" }, "node_modules/is-unicode-supported": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", - "dev": true, "engines": { "node": ">=10" }, @@ -2671,7 +2492,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -2679,14 +2499,12 @@ "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" }, "node_modules/istanbul-lib-coverage": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", - "dev": true, "engines": { "node": ">=8" } @@ -2695,7 +2513,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/istanbul-lib-hook/-/istanbul-lib-hook-3.0.0.tgz", "integrity": "sha512-Pt/uge1Q9s+5VAZ+pCo16TYMWPBIl+oaNIjgLQxcX0itS6ueeaA+pEfThZpH8WxhFgCiEb8sAJY6MdUKgiIWaQ==", - "dev": true, "dependencies": { "append-transform": "^2.0.0" }, @@ -2707,7 +2524,6 @@ "version": "4.0.3", "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz", "integrity": "sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ==", - "dev": true, "dependencies": { "@babel/core": "^7.7.5", "@istanbuljs/schema": "^0.1.2", @@ -2722,7 +2538,6 @@ "version": "2.0.3", "resolved": "https://registry.npmjs.org/istanbul-lib-processinfo/-/istanbul-lib-processinfo-2.0.3.tgz", "integrity": "sha512-NkwHbo3E00oybX6NGJi6ar0B29vxyvNwoC7eJ4G4Yq28UfY758Hgn/heV8VRFhevPED4LXfFz0DQ8z/0kw9zMg==", - "dev": true, "dependencies": { "archy": "^1.0.0", "cross-spawn": "^7.0.3", @@ -2739,7 +2554,6 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", - "dev": true, "dependencies": { "istanbul-lib-coverage": "^3.0.0", "make-dir": "^4.0.0", @@ -2753,7 +2567,6 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, "dependencies": { "yallist": "^4.0.0" }, @@ -2765,7 +2578,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", - "dev": true, "dependencies": { "semver": "^7.5.3" }, @@ -2780,7 +2592,6 @@ "version": "7.5.4", "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "dev": true, "dependencies": { "lru-cache": "^6.0.0" }, @@ -2794,14 +2605,12 @@ "node_modules/istanbul-lib-report/node_modules/yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, "node_modules/istanbul-lib-source-maps": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", - "dev": true, "dependencies": { "debug": "^4.1.1", "istanbul-lib-coverage": "^3.0.0", @@ -2815,7 +2624,6 @@ "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dev": true, "dependencies": { "ms": "2.1.2" }, @@ -2831,14 +2639,12 @@ "node_modules/istanbul-lib-source-maps/node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, "node_modules/istanbul-reports": { "version": "3.1.6", "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.6.tgz", "integrity": "sha512-TLgnMkKg3iTDsQ9PbPTdpfAK2DzjF9mqUG7RMgcQl8oFjad8ob4laGxv5XV5U9MAfx8D6tSJiUyuAwzLicaxlg==", - "dev": true, "dependencies": { "html-escaper": "^2.0.0", "istanbul-lib-report": "^3.0.0" @@ -2851,7 +2657,6 @@ "version": "10.8.7", "resolved": "https://registry.npmjs.org/jake/-/jake-10.8.7.tgz", "integrity": "sha512-ZDi3aP+fG/LchyBzUM804VjddnwfSfsdeYkwt8NcbKRvo4rFkjhs456iLFn3k2ZUWvNe4i48WACDbza8fhq2+w==", - "dev": true, "dependencies": { "async": "^3.2.3", "chalk": "^4.0.2", @@ -2869,7 +2674,6 @@ "version": "4.4.2", "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.4.2.tgz", "integrity": "sha512-dwXFwByc/ajSV6m5bcKAPwe4yDDF6D614pxmIi5odytzxRlwqF6nwoiCek80Ixc7Cvma5awClxrzFtxCQvcM8w==", - "dev": true, "funding": { "type": "opencollective", "url": "https://opencollective.com/js-sdsl" @@ -2878,14 +2682,12 @@ "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" }, "node_modules/js-yaml": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, "dependencies": { "argparse": "^2.0.1" }, @@ -2897,7 +2699,6 @@ "version": "2.5.2", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", - "dev": true, "bin": { "jsesc": "bin/jsesc" }, @@ -2908,26 +2709,22 @@ "node_modules/json-buffer": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", - "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", - "dev": true + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==" }, "node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" }, "node_modules/json-stable-stringify-without-jsonify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", - "dev": true + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==" }, "node_modules/json5": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "dev": true, "bin": { "json5": "lib/cli.js" }, @@ -2939,7 +2736,6 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/keygrip/-/keygrip-1.1.0.tgz", "integrity": "sha512-iYSchDJ+liQ8iwbSI2QqsQOvqv58eJCEanyJPJi+Khyu8smkcKSFUCbPwzFcL7YVtZ6eONjqRX/38caJ7QjRAQ==", - "dev": true, "dependencies": { "tsscmp": "1.0.6" }, @@ -2951,7 +2747,6 @@ "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", - "dev": true, "dependencies": { "json-buffer": "3.0.1" } @@ -2960,7 +2755,6 @@ "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", - "dev": true, "dependencies": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" @@ -2973,7 +2767,6 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "dev": true, "dependencies": { "p-locate": "^5.0.0" }, @@ -2987,20 +2780,17 @@ "node_modules/lodash.flattendeep": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz", - "integrity": "sha512-uHaJFihxmJcEX3kT4I23ABqKKalJ/zDrDg0lsFtc1h+3uw49SIJ5beyhx5ExVRti3AvKoOJngIj7xz3oylPdWQ==", - "dev": true + "integrity": "sha512-uHaJFihxmJcEX3kT4I23ABqKKalJ/zDrDg0lsFtc1h+3uw49SIJ5beyhx5ExVRti3AvKoOJngIj7xz3oylPdWQ==" }, "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==" }, "node_modules/log-symbols": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", - "dev": true, "dependencies": { "chalk": "^4.1.0", "is-unicode-supported": "^0.1.0" @@ -3016,7 +2806,6 @@ "version": "5.1.1", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "dev": true, "dependencies": { "yallist": "^3.0.2" } @@ -3025,7 +2814,6 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", - "dev": true, "dependencies": { "semver": "^6.0.0" }, @@ -3040,7 +2828,6 @@ "version": "0.7.0", "resolved": "https://registry.npmjs.org/marked/-/marked-0.7.0.tgz", "integrity": "sha512-c+yYdCZJQrsRjTPhUx7VKkApw9bwDkNbHUKo1ovgcfDjb2kc8rLuRbIFyXL5WOEUwzSSKo3IXpph2K6DqB/KZg==", - "dev": true, "bin": { "marked": "bin/marked" }, @@ -3065,7 +2852,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/method-override/-/method-override-3.0.0.tgz", "integrity": "sha512-IJ2NNN/mSl9w3kzWB92rcdHpz+HjkxhDJWNDBqSlas+zQdP8wBiJzITPg08M/k2uVvMow7Sk41atndNtt/PHSA==", - "dev": true, "dependencies": { "debug": "3.1.0", "methods": "~1.1.2", @@ -3080,7 +2866,6 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", - "dev": true, "dependencies": { "ms": "2.0.0" } @@ -3127,7 +2912,6 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, "dependencies": { "brace-expansion": "^1.1.7" }, @@ -3139,7 +2923,6 @@ "version": "1.2.8", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "dev": true, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -3148,7 +2931,6 @@ "version": "10.2.0", "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.2.0.tgz", "integrity": "sha512-IDY7fl/BecMwFHzoqF2sg/SHHANeBoMMXFlS9r0OXKDssYE1M5O43wUY/9BVPeIvfH2zmEbBfseqN9gBQZzXkg==", - "dev": true, "dependencies": { "ansi-colors": "4.1.1", "browser-stdout": "1.3.1", @@ -3188,7 +2970,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, "dependencies": { "balanced-match": "^1.0.0" } @@ -3197,7 +2978,6 @@ "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dev": true, "dependencies": { "ms": "2.1.2" }, @@ -3213,14 +2993,12 @@ "node_modules/mocha/node_modules/debug/node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, "node_modules/mocha/node_modules/minimatch": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz", "integrity": "sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==", - "dev": true, "dependencies": { "brace-expansion": "^2.0.1" }, @@ -3231,14 +3009,12 @@ "node_modules/mocha/node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" }, "node_modules/mocha/node_modules/supports-color": { "version": "8.1.1", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, "dependencies": { "has-flag": "^4.0.0" }, @@ -3253,7 +3029,6 @@ "version": "1.10.0", "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.10.0.tgz", "integrity": "sha512-AbegBVI4sh6El+1gNwvD5YIck7nSA36weD7xvIxG4in80j/UoK8AEGaWnnz8v1GxonMCltmlNs5ZKbGvl9b1XQ==", - "dev": true, "dependencies": { "basic-auth": "~2.0.1", "debug": "2.6.9", @@ -3269,7 +3044,6 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", "integrity": "sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==", - "dev": true, "dependencies": { "ee-first": "1.1.1" }, @@ -3286,7 +3060,6 @@ "version": "4.2.3", "resolved": "https://registry.npmjs.org/multiparty/-/multiparty-4.2.3.tgz", "integrity": "sha512-Ak6EUJZuhGS8hJ3c2fY6UW5MbkGUPMBEGd13djUzoY/BHqV/gTuFWtC6IuVA7A2+v3yjBS6c4or50xhzTQZImQ==", - "dev": true, "dependencies": { "http-errors": "~1.8.1", "safe-buffer": "5.2.1", @@ -3300,7 +3073,6 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==", - "dev": true, "engines": { "node": ">= 0.6" } @@ -3309,7 +3081,6 @@ "version": "1.8.1", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.8.1.tgz", "integrity": "sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g==", - "dev": true, "dependencies": { "depd": "~1.1.2", "inherits": "2.0.4", @@ -3325,7 +3096,6 @@ "version": "1.5.0", "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==", - "dev": true, "engines": { "node": ">= 0.6" } @@ -3334,7 +3104,6 @@ "version": "3.3.3", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.3.tgz", "integrity": "sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w==", - "dev": true, "bin": { "nanoid": "bin/nanoid.cjs" }, @@ -3345,8 +3114,7 @@ "node_modules/natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", - "dev": true + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==" }, "node_modules/negotiator": { "version": "0.6.3", @@ -3359,14 +3127,12 @@ "node_modules/neo-async": { "version": "2.6.2", "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", - "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", - "dev": true + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==" }, "node_modules/node-preload": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/node-preload/-/node-preload-0.2.1.tgz", "integrity": "sha512-RM5oyBy45cLEoHqCeh+MNuFAxO0vTFBLskvQbOKnEE7YTTSN4tbN8QWDIPQ6L+WvKsB/qLEGpYe2ZZ9d4W9OIQ==", - "dev": true, "dependencies": { "process-on-spawn": "^1.0.0" }, @@ -3377,14 +3143,12 @@ "node_modules/node-releases": { "version": "2.0.13", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.13.tgz", - "integrity": "sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==", - "dev": true + "integrity": "sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==" }, "node_modules/normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -3393,7 +3157,6 @@ "version": "15.1.0", "resolved": "https://registry.npmjs.org/nyc/-/nyc-15.1.0.tgz", "integrity": "sha512-jMW04n9SxKdKi1ZMGhvUTHBN0EICCRkHemEoE5jm6mTYcqcdas0ATzgUgejlQUHMvpnOZqGB5Xxsv9KxJW1j8A==", - "dev": true, "dependencies": { "@istanbuljs/load-nyc-config": "^1.0.0", "@istanbuljs/schema": "^0.1.2", @@ -3434,7 +3197,6 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", - "dev": true, "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.0", @@ -3445,7 +3207,6 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, "dependencies": { "locate-path": "^5.0.0", "path-exists": "^4.0.0" @@ -3458,7 +3219,6 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, "dependencies": { "p-locate": "^4.1.0" }, @@ -3470,7 +3230,6 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, "dependencies": { "p-try": "^2.0.0" }, @@ -3485,7 +3244,6 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, "dependencies": { "p-limit": "^2.2.0" }, @@ -3497,7 +3255,6 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true, "engines": { "node": ">=8" } @@ -3506,7 +3263,6 @@ "version": "6.2.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", - "dev": true, "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", @@ -3519,14 +3275,12 @@ "node_modules/nyc/node_modules/y18n": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", - "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", - "dev": true + "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==" }, "node_modules/nyc/node_modules/yargs": { "version": "15.4.1", "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", - "dev": true, "dependencies": { "cliui": "^6.0.0", "decamelize": "^1.2.0", @@ -3548,7 +3302,6 @@ "version": "18.1.3", "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", - "dev": true, "dependencies": { "camelcase": "^5.0.0", "decamelize": "^1.2.0" @@ -3580,7 +3333,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", - "dev": true, "engines": { "node": ">= 0.8" } @@ -3589,7 +3341,6 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dev": true, "dependencies": { "wrappy": "1" } @@ -3598,7 +3349,6 @@ "version": "0.9.3", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", - "dev": true, "dependencies": { "@aashutoshrathi/word-wrap": "^1.2.3", "deep-is": "^0.1.3", @@ -3615,7 +3365,6 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, "dependencies": { "yocto-queue": "^0.1.0" }, @@ -3630,7 +3379,6 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "dev": true, "dependencies": { "p-limit": "^3.0.2" }, @@ -3645,7 +3393,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/p-map/-/p-map-3.0.0.tgz", "integrity": "sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ==", - "dev": true, "dependencies": { "aggregate-error": "^3.0.0" }, @@ -3657,7 +3404,6 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true, "engines": { "node": ">=6" } @@ -3666,7 +3412,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/package-hash/-/package-hash-4.0.0.tgz", "integrity": "sha512-whdkPIooSu/bASggZ96BWVvZTRMOFxnyUG5PnTSGKoJE2gd5mbVNmR2Nj20QFzxYYgAXpoqC+AiXzl+UMRh7zQ==", - "dev": true, "dependencies": { "graceful-fs": "^4.1.15", "hasha": "^5.0.0", @@ -3681,7 +3426,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, "dependencies": { "callsites": "^3.0.0" }, @@ -3701,7 +3445,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true, "engines": { "node": ">=8" } @@ -3710,7 +3453,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -3719,7 +3461,6 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, "engines": { "node": ">=8" } @@ -3733,7 +3474,6 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/pbkdf2-password/-/pbkdf2-password-1.2.1.tgz", "integrity": "sha512-I6ZiUi82uhYN47lOzi8P7O69e70LRopgS4TIkq0ecDAPHrlxABWOHxLEsTzCuogkxTofFJDj0eEbOALgrrxhKg==", - "dev": true, "dependencies": { "fastfall": "^1.2.3" } @@ -3741,14 +3481,12 @@ "node_modules/picocolors": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", - "dev": true + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" }, "node_modules/picomatch": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, "engines": { "node": ">=8.6" }, @@ -3760,7 +3498,6 @@ "version": "4.2.0", "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", - "dev": true, "dependencies": { "find-up": "^4.0.0" }, @@ -3772,7 +3509,6 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, "dependencies": { "locate-path": "^5.0.0", "path-exists": "^4.0.0" @@ -3785,7 +3521,6 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, "dependencies": { "p-locate": "^4.1.0" }, @@ -3797,7 +3532,6 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, "dependencies": { "p-try": "^2.0.0" }, @@ -3812,7 +3546,6 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, "dependencies": { "p-limit": "^2.2.0" }, @@ -3824,7 +3557,6 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", - "dev": true, "engines": { "node": ">= 0.8.0" } @@ -3833,7 +3565,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/process-on-spawn/-/process-on-spawn-1.0.0.tgz", "integrity": "sha512-1WsPDsUSMmZH5LeMLegqkPDrsGgsWwk1Exipy2hvB0o/F0ASzbpIctSCcZIK1ykJvtTJULEH+20WOFjMvGnCTg==", - "dev": true, "dependencies": { "fromentries": "^1.2.0" }, @@ -3857,7 +3588,6 @@ "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "dev": true, "engines": { "node": ">=6" } @@ -3880,7 +3610,6 @@ "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "dev": true, "funding": [ { "type": "github", @@ -3900,7 +3629,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/random-bytes/-/random-bytes-1.0.0.tgz", "integrity": "sha512-iv7LhNVO047HzYR3InF6pUcUsPQiHTM1Qal51DcGSuZFBil1aBBWG5eHPNek7bvILMaYJ/8RU1e8w1AMdHmLQQ==", - "dev": true, "engines": { "node": ">= 0.8" } @@ -3909,7 +3637,6 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", - "dev": true, "dependencies": { "safe-buffer": "^5.1.0" } @@ -3922,25 +3649,10 @@ "node": ">= 0.6" } }, - "node_modules/raw-body": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", - "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", - "dependencies": { - "bytes": "3.1.2", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, "node_modules/readdirp": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dev": true, "dependencies": { "picomatch": "^2.2.1" }, @@ -3952,7 +3664,6 @@ "version": "2.8.0", "resolved": "https://registry.npmjs.org/redis/-/redis-2.8.0.tgz", "integrity": "sha512-M1OkonEQwtRmZv4tEWF2VgpG0JWJ8Fv1PhlgT5+B+uNq2cA3Rt1Yt/ryoR+vQNOQcIEgdCdfH0jr3bDpihAw1A==", - "dev": true, "dependencies": { "double-ended-queue": "^2.1.0-0", "redis-commands": "^1.2.0", @@ -3965,14 +3676,12 @@ "node_modules/redis-commands": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/redis-commands/-/redis-commands-1.7.0.tgz", - "integrity": "sha512-nJWqw3bTFy21hX/CPKHth6sfhZbdiHP6bTawSgQBlKOVRG7EZkfHbbHwQJnrE4vsQf0CMNE+3gJ4Fmm16vdVlQ==", - "dev": true + "integrity": "sha512-nJWqw3bTFy21hX/CPKHth6sfhZbdiHP6bTawSgQBlKOVRG7EZkfHbbHwQJnrE4vsQf0CMNE+3gJ4Fmm16vdVlQ==" }, "node_modules/redis-parser": { "version": "2.6.0", "resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-2.6.0.tgz", "integrity": "sha512-9Hdw19gwXFBJdN8ENUoNVJFRyMDFrE/ZBClPicKYDPwNPJ4ST1TedAHYNSiGKElwh2vrmRGMoJYbVdJd+WQXIw==", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -3981,7 +3690,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/release-zalgo/-/release-zalgo-1.0.0.tgz", "integrity": "sha512-gUAyHVHPPC5wdqX/LG4LWtRYtgjxyX78oanFNTMMyFEfOqdC54s3eE82imuWKbOeqYht2CrNf64Qb8vgmmtZGA==", - "dev": true, "dependencies": { "es6-error": "^4.0.1" }, @@ -3993,7 +3701,6 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -4001,14 +3708,12 @@ "node_modules/require-main-filename": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", - "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", - "dev": true + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==" }, "node_modules/resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true, "engines": { "node": ">=4" } @@ -4017,7 +3722,6 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", - "dev": true, "engines": { "iojs": ">=1.0.0", "node": ">=0.10.0" @@ -4027,7 +3731,6 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, "dependencies": { "glob": "^7.1.3" }, @@ -4042,7 +3745,6 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "dev": true, "funding": [ { "type": "github", @@ -4089,7 +3791,6 @@ "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, "bin": { "semver": "bin/semver.js" } @@ -4126,7 +3827,6 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", - "dev": true, "dependencies": { "randombytes": "^2.1.0" } @@ -4148,8 +3848,7 @@ "node_modules/set-blocking": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", - "dev": true + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==" }, "node_modules/set-function-length": { "version": "1.1.1", @@ -4174,7 +3873,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, "dependencies": { "shebang-regex": "^3.0.0" }, @@ -4186,7 +3884,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, "engines": { "node": ">=8" } @@ -4207,14 +3904,12 @@ "node_modules/signal-exit": { "version": "3.0.7", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "dev": true + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" }, "node_modules/source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -4223,7 +3918,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/spawn-wrap/-/spawn-wrap-2.0.0.tgz", "integrity": "sha512-EeajNjfN9zMnULLwhZZQU3GWBoFNkbngTUPfaawT4RkMiviTxcX0qfhVbGey39mfctfDHkWtuecgQ8NJcyQWHg==", - "dev": true, "dependencies": { "foreground-child": "^2.0.0", "is-windows": "^1.0.2", @@ -4239,8 +3933,7 @@ "node_modules/sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", - "dev": true + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==" }, "node_modules/statuses": { "version": "2.0.1", @@ -4254,7 +3947,6 @@ "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -4268,7 +3960,6 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, "dependencies": { "ansi-regex": "^5.0.1" }, @@ -4280,7 +3971,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", - "dev": true, "engines": { "node": ">=8" } @@ -4289,7 +3979,6 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true, "engines": { "node": ">=8" }, @@ -4301,7 +3990,6 @@ "version": "8.1.2", "resolved": "https://registry.npmjs.org/superagent/-/superagent-8.1.2.tgz", "integrity": "sha512-6WTxW1EB6yCxV5VFOIPQruWGHqc3yI7hEmZK6h+pyk69Lk/Ut7rLUY6W/ONF2MjBuGjvmMiIpsrVJ2vjrHlslA==", - "dev": true, "dependencies": { "component-emitter": "^1.3.0", "cookiejar": "^2.1.4", @@ -4322,7 +4010,6 @@ "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dev": true, "dependencies": { "ms": "2.1.2" }, @@ -4339,7 +4026,6 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, "dependencies": { "yallist": "^4.0.0" }, @@ -4351,7 +4037,6 @@ "version": "2.6.0", "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", - "dev": true, "bin": { "mime": "cli.js" }, @@ -4362,14 +4047,12 @@ "node_modules/superagent/node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, "node_modules/superagent/node_modules/semver": { "version": "7.5.4", "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "dev": true, "dependencies": { "lru-cache": "^6.0.0" }, @@ -4383,14 +4066,12 @@ "node_modules/superagent/node_modules/yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, "node_modules/supertest": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/supertest/-/supertest-6.3.0.tgz", "integrity": "sha512-QgWju1cNoacP81Rv88NKkQ4oXTzGg0eNZtOoxp1ROpbS4OHY/eK5b8meShuFtdni161o5X0VQvgo7ErVyKK+Ow==", - "dev": true, "dependencies": { "methods": "^1.1.2", "superagent": "^8.0.0" @@ -4403,7 +4084,6 @@ "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, "dependencies": { "has-flag": "^4.0.0" }, @@ -4415,7 +4095,6 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", - "dev": true, "dependencies": { "@istanbuljs/schema": "^0.1.2", "glob": "^7.1.4", @@ -4428,14 +4107,12 @@ "node_modules/text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", - "dev": true + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==" }, "node_modules/to-fast-properties": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", - "dev": true, "engines": { "node": ">=4" } @@ -4444,7 +4121,6 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, "dependencies": { "is-number": "^7.0.0" }, @@ -4464,7 +4140,6 @@ "version": "1.0.6", "resolved": "https://registry.npmjs.org/tsscmp/-/tsscmp-1.0.6.tgz", "integrity": "sha512-LxhtAkPDTkVCMQjt2h6eBVY28KCjikZqZfMcC15YBeNjkgUpdCfBu5HoiOTDu86v6smE8yOjyEktJ8hlbANHQA==", - "dev": true, "engines": { "node": ">=0.6.x" } @@ -4473,7 +4148,6 @@ "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", - "dev": true, "dependencies": { "prelude-ls": "^1.2.1" }, @@ -4485,7 +4159,6 @@ "version": "0.20.2", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "dev": true, "engines": { "node": ">=10" }, @@ -4509,7 +4182,6 @@ "version": "3.1.5", "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", - "dev": true, "dependencies": { "is-typedarray": "^1.0.0" } @@ -4518,7 +4190,6 @@ "version": "3.17.4", "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.17.4.tgz", "integrity": "sha512-T9q82TJI9e/C1TAxYvfb16xO120tMVFZrGA3f9/P4424DNu6ypK103y0GPFVa17yotwSyZW5iYXgjYHkGrJW/g==", - "dev": true, "optional": true, "bin": { "uglifyjs": "bin/uglifyjs" @@ -4531,7 +4202,6 @@ "version": "2.1.5", "resolved": "https://registry.npmjs.org/uid-safe/-/uid-safe-2.1.5.tgz", "integrity": "sha512-KPHm4VL5dDXKz01UuEd88Df+KzynaohSL9fBh096KWAxSKZQDI2uBrVqtvRM4rwrIrRRKsdLNML/lnaaVSRioA==", - "dev": true, "dependencies": { "random-bytes": "~1.0.0" }, @@ -4551,7 +4221,6 @@ "version": "1.0.13", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz", "integrity": "sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==", - "dev": true, "funding": [ { "type": "opencollective", @@ -4581,7 +4250,6 @@ "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, "dependencies": { "punycode": "^2.1.0" } @@ -4598,7 +4266,6 @@ "version": "8.3.2", "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", - "dev": true, "bin": { "uuid": "dist/bin/uuid" } @@ -4615,7 +4282,6 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/vhost/-/vhost-3.0.2.tgz", "integrity": "sha512-S3pJdWrpFWrKMboRU4dLYgMrTgoPALsmYwOvyebK2M6X95b9kQrjZy5rwl3uzzpfpENe/XrNYu/2U+e7/bmT5g==", - "dev": true, "engines": { "node": ">= 0.8.0" } @@ -4624,7 +4290,6 @@ "version": "2.3.15", "resolved": "https://registry.npmjs.org/walk/-/walk-2.3.15.tgz", "integrity": "sha512-4eRTBZljBfIISK1Vnt69Gvr2w/wc3U6Vtrw7qiN5iqYJPH7LElcYh/iU4XWhdCy2dZqv1ToMyYlybDylfG/5Vg==", - "dev": true, "dependencies": { "foreachasync": "^3.0.0" } @@ -4633,7 +4298,6 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, "dependencies": { "isexe": "^2.0.0" }, @@ -4647,26 +4311,22 @@ "node_modules/which-module": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.1.tgz", - "integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==", - "dev": true + "integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==" }, "node_modules/wordwrap": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", - "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", - "dev": true + "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==" }, "node_modules/workerpool": { "version": "6.2.1", "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.1.tgz", - "integrity": "sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw==", - "dev": true + "integrity": "sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw==" }, "node_modules/wrap-ansi": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", @@ -4682,14 +4342,12 @@ "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" }, "node_modules/write-file-atomic": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", - "dev": true, "dependencies": { "imurmurhash": "^0.1.4", "is-typedarray": "^1.0.0", @@ -4701,7 +4359,6 @@ "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true, "engines": { "node": ">=10" } @@ -4709,14 +4366,12 @@ "node_modules/yallist": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" }, "node_modules/yargs": { "version": "16.2.0", "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", - "dev": true, "dependencies": { "cliui": "^7.0.2", "escalade": "^3.1.1", @@ -4734,7 +4389,6 @@ "version": "20.2.4", "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", - "dev": true, "engines": { "node": ">=10" } @@ -4743,7 +4397,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", - "dev": true, "dependencies": { "camelcase": "^6.0.0", "decamelize": "^4.0.0", @@ -4758,7 +4411,6 @@ "version": "6.3.0", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", - "dev": true, "engines": { "node": ">=10" }, @@ -4770,7 +4422,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", - "dev": true, "engines": { "node": ">=10" }, @@ -4782,7 +4433,6 @@ "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "dev": true, "engines": { "node": ">=10" }, diff --git a/package.json b/package.json index d9ce7315..0cbe46a0 100644 --- a/package.json +++ b/package.json @@ -17,39 +17,7 @@ "private": true, "homepage": "http://expressjs.com/", "dependencies": { - "accepts": "~1.3.8", - "array-flatten": "1.1.1", - "body-parser": "1.20.2", - "content-disposition": "0.5.4", - "content-type": "~1.0.4", - "cookie": "0.5.0", - "cookie-signature": "1.0.6", - "debug": "2.6.9", - "depd": "2.0.0", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "finalhandler": "1.2.0", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "merge-descriptors": "1.0.1", - "methods": "~1.1.2", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "path-to-regexp": "0.1.7", - "proxy-addr": "~2.0.7", - "qs": "6.11.0", - "range-parser": "~1.2.1", - "safe-buffer": "5.2.1", - "send": "0.18.0", - "serve-static": "1.15.0", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "type-is": "~1.6.18", - "utils-merge": "1.0.1", - "vary": "~1.1.2" - }, - "devDependencies": { + "express": "4.18.2", "after": "0.8.2", "connect-redis": "3.4.2", "cookie-parser": "1.4.6", diff --git a/test/Route.js b/test/Route.js deleted file mode 100644 index 2a37b9a4..00000000 --- a/test/Route.js +++ /dev/null @@ -1,274 +0,0 @@ -'use strict' - -var after = require('after'); -var assert = require('assert') -var express = require('../') - , Route = express.Route - , methods = require('methods') - -describe('Route', function(){ - it('should work without handlers', function(done) { - var req = { method: 'GET', url: '/' } - var route = new Route('/foo') - route.dispatch(req, {}, done) - }) - - it('should not stack overflow with a large sync stack', function (done) { - this.timeout(5000) // long-running test - - var req = { method: 'GET', url: '/' } - var route = new Route('/foo') - - route.get(function (req, res, next) { - req.counter = 0 - next() - }) - - for (var i = 0; i < 6000; i++) { - route.all(function (req, res, next) { - req.counter++ - next() - }) - } - - route.get(function (req, res, next) { - req.called = true - next() - }) - - route.dispatch(req, {}, function (err) { - if (err) return done(err) - assert.ok(req.called) - assert.strictEqual(req.counter, 6000) - done() - }) - }) - - describe('.all', function(){ - it('should add handler', function(done){ - var req = { method: 'GET', url: '/' }; - var route = new Route('/foo'); - - route.all(function(req, res, next) { - req.called = true; - next(); - }); - - route.dispatch(req, {}, function (err) { - if (err) return done(err); - assert.ok(req.called) - done(); - }); - }) - - it('should handle VERBS', function(done) { - var count = 0; - var route = new Route('/foo'); - var cb = after(methods.length, function (err) { - if (err) return done(err); - assert.strictEqual(count, methods.length) - done(); - }); - - route.all(function(req, res, next) { - count++; - next(); - }); - - methods.forEach(function testMethod(method) { - var req = { method: method, url: '/' }; - route.dispatch(req, {}, cb); - }); - }) - - it('should stack', function(done) { - var req = { count: 0, method: 'GET', url: '/' }; - var route = new Route('/foo'); - - route.all(function(req, res, next) { - req.count++; - next(); - }); - - route.all(function(req, res, next) { - req.count++; - next(); - }); - - route.dispatch(req, {}, function (err) { - if (err) return done(err); - assert.strictEqual(req.count, 2) - done(); - }); - }) - }) - - describe('.VERB', function(){ - it('should support .get', function(done){ - var req = { method: 'GET', url: '/' }; - var route = new Route(''); - - route.get(function(req, res, next) { - req.called = true; - next(); - }) - - route.dispatch(req, {}, function (err) { - if (err) return done(err); - assert.ok(req.called) - done(); - }); - }) - - it('should limit to just .VERB', function(done){ - var req = { method: 'POST', url: '/' }; - var route = new Route(''); - - route.get(function () { - throw new Error('not me!'); - }) - - route.post(function(req, res, next) { - req.called = true; - next(); - }) - - route.dispatch(req, {}, function (err) { - if (err) return done(err); - assert.ok(req.called) - done(); - }); - }) - - it('should allow fallthrough', function(done){ - var req = { order: '', method: 'GET', url: '/' }; - var route = new Route(''); - - route.get(function(req, res, next) { - req.order += 'a'; - next(); - }) - - route.all(function(req, res, next) { - req.order += 'b'; - next(); - }); - - route.get(function(req, res, next) { - req.order += 'c'; - next(); - }) - - route.dispatch(req, {}, function (err) { - if (err) return done(err); - assert.strictEqual(req.order, 'abc') - done(); - }); - }) - }) - - describe('errors', function(){ - it('should handle errors via arity 4 functions', function(done){ - var req = { order: '', method: 'GET', url: '/' }; - var route = new Route(''); - - route.all(function(req, res, next){ - next(new Error('foobar')); - }); - - route.all(function(req, res, next){ - req.order += '0'; - next(); - }); - - route.all(function(err, req, res, next){ - req.order += 'a'; - next(err); - }); - - route.dispatch(req, {}, function (err) { - assert.ok(err) - assert.strictEqual(err.message, 'foobar') - assert.strictEqual(req.order, 'a') - done(); - }); - }) - - it('should handle throw', function(done) { - var req = { order: '', method: 'GET', url: '/' }; - var route = new Route(''); - - route.all(function () { - throw new Error('foobar'); - }); - - route.all(function(req, res, next){ - req.order += '0'; - next(); - }); - - route.all(function(err, req, res, next){ - req.order += 'a'; - next(err); - }); - - route.dispatch(req, {}, function (err) { - assert.ok(err) - assert.strictEqual(err.message, 'foobar') - assert.strictEqual(req.order, 'a') - done(); - }); - }); - - it('should handle throwing inside error handlers', function(done) { - var req = { method: 'GET', url: '/' }; - var route = new Route(''); - - route.get(function () { - throw new Error('boom!'); - }); - - route.get(function(err, req, res, next){ - throw new Error('oops'); - }); - - route.get(function(err, req, res, next){ - req.message = err.message; - next(); - }); - - route.dispatch(req, {}, function (err) { - if (err) return done(err); - assert.strictEqual(req.message, 'oops') - done(); - }); - }); - - it('should handle throw in .all', function(done) { - var req = { method: 'GET', url: '/' }; - var route = new Route(''); - - route.all(function(req, res, next){ - throw new Error('boom!'); - }); - - route.dispatch(req, {}, function(err){ - assert.ok(err) - assert.strictEqual(err.message, 'boom!') - done(); - }); - }); - - it('should handle single error handler', function(done) { - var req = { method: 'GET', url: '/' }; - var route = new Route(''); - - route.all(function(err, req, res, next){ - // this should not execute - throw new Error('should not be called') - }); - - route.dispatch(req, {}, done); - }); - }) -}) diff --git a/test/Router.js b/test/Router.js deleted file mode 100644 index cf5b5c1f..00000000 --- a/test/Router.js +++ /dev/null @@ -1,640 +0,0 @@ -'use strict' - -var after = require('after'); -var express = require('../') - , Router = express.Router - , methods = require('methods') - , assert = require('assert'); - -describe('Router', function(){ - it('should return a function with router methods', function() { - var router = new Router(); - assert(typeof router === 'function') - - assert(typeof router.get === 'function') - assert(typeof router.handle === 'function') - assert(typeof router.use === 'function') - }); - - it('should support .use of other routers', function(done){ - var router = new Router(); - var another = new Router(); - - another.get('/bar', function(req, res){ - res.end(); - }); - router.use('/foo', another); - - router.handle({ url: '/foo/bar', method: 'GET' }, { end: done }); - }); - - it('should support dynamic routes', function(done){ - var router = new Router(); - var another = new Router(); - - another.get('/:bar', function(req, res){ - assert.strictEqual(req.params.bar, 'route') - res.end(); - }); - router.use('/:foo', another); - - router.handle({ url: '/test/route', method: 'GET' }, { end: done }); - }); - - it('should handle blank URL', function(done){ - var router = new Router(); - - router.use(function (req, res) { - throw new Error('should not be called') - }); - - router.handle({ url: '', method: 'GET' }, {}, done); - }); - - it('should handle missing URL', function (done) { - var router = new Router() - - router.use(function (req, res) { - throw new Error('should not be called') - }) - - router.handle({ method: 'GET' }, {}, done) - }) - - it('handle missing method', function (done) { - var all = false - var router = new Router() - var route = router.route('/foo') - var use = false - - route.post(function (req, res, next) { next(new Error('should not run')) }) - route.all(function (req, res, next) { - all = true - next() - }) - route.get(function (req, res, next) { next(new Error('should not run')) }) - - router.get('/foo', function (req, res, next) { next(new Error('should not run')) }) - router.use(function (req, res, next) { - use = true - next() - }) - - router.handle({ url: '/foo' }, {}, function (err) { - if (err) return done(err) - assert.ok(all) - assert.ok(use) - done() - }) - }) - - it('should not stack overflow with many registered routes', function(done){ - this.timeout(5000) // long-running test - - var handler = function(req, res){ res.end(new Error('wrong handler')) }; - var router = new Router(); - - for (var i = 0; i < 6000; i++) { - router.get('/thing' + i, handler) - } - - router.get('/', function (req, res) { - res.end(); - }); - - router.handle({ url: '/', method: 'GET' }, { end: done }); - }); - - it('should not stack overflow with a large sync route stack', function (done) { - this.timeout(5000) // long-running test - - var router = new Router() - - router.get('/foo', function (req, res, next) { - req.counter = 0 - next() - }) - - for (var i = 0; i < 6000; i++) { - router.get('/foo', function (req, res, next) { - req.counter++ - next() - }) - } - - router.get('/foo', function (req, res) { - assert.strictEqual(req.counter, 6000) - res.end() - }) - - router.handle({ url: '/foo', method: 'GET' }, { end: done }) - }) - - it('should not stack overflow with a large sync middleware stack', function (done) { - this.timeout(5000) // long-running test - - var router = new Router() - - router.use(function (req, res, next) { - req.counter = 0 - next() - }) - - for (var i = 0; i < 6000; i++) { - router.use(function (req, res, next) { - req.counter++ - next() - }) - } - - router.use(function (req, res) { - assert.strictEqual(req.counter, 6000) - res.end() - }) - - router.handle({ url: '/', method: 'GET' }, { end: done }) - }) - - describe('.handle', function(){ - it('should dispatch', function(done){ - var router = new Router(); - - router.route('/foo').get(function(req, res){ - res.send('foo'); - }); - - var res = { - send: function(val) { - assert.strictEqual(val, 'foo') - done(); - } - } - router.handle({ url: '/foo', method: 'GET' }, res); - }) - }) - - describe('.multiple callbacks', function(){ - it('should throw if a callback is null', function(){ - assert.throws(function () { - var router = new Router(); - router.route('/foo').all(null); - }) - }) - - it('should throw if a callback is undefined', function(){ - assert.throws(function () { - var router = new Router(); - router.route('/foo').all(undefined); - }) - }) - - it('should throw if a callback is not a function', function(){ - assert.throws(function () { - var router = new Router(); - router.route('/foo').all('not a function'); - }) - }) - - it('should not throw if all callbacks are functions', function(){ - var router = new Router(); - router.route('/foo').all(function(){}).all(function(){}); - }) - }) - - describe('error', function(){ - it('should skip non error middleware', function(done){ - var router = new Router(); - - router.get('/foo', function(req, res, next){ - next(new Error('foo')); - }); - - router.get('/bar', function(req, res, next){ - next(new Error('bar')); - }); - - router.use(function(req, res, next){ - assert(false); - }); - - router.use(function(err, req, res, next){ - assert.equal(err.message, 'foo'); - done(); - }); - - router.handle({ url: '/foo', method: 'GET' }, {}, done); - }); - - it('should handle throwing inside routes with params', function(done) { - var router = new Router(); - - router.get('/foo/:id', function () { - throw new Error('foo'); - }); - - router.use(function(req, res, next){ - assert(false); - }); - - router.use(function(err, req, res, next){ - assert.equal(err.message, 'foo'); - done(); - }); - - router.handle({ url: '/foo/2', method: 'GET' }, {}, function() {}); - }); - - it('should handle throwing in handler after async param', function(done) { - var router = new Router(); - - router.param('user', function(req, res, next, val){ - process.nextTick(function(){ - req.user = val; - next(); - }); - }); - - router.use('/:user', function(req, res, next){ - throw new Error('oh no!'); - }); - - router.use(function(err, req, res, next){ - assert.equal(err.message, 'oh no!'); - done(); - }); - - router.handle({ url: '/bob', method: 'GET' }, {}, function() {}); - }); - - it('should handle throwing inside error handlers', function(done) { - var router = new Router(); - - router.use(function(req, res, next){ - throw new Error('boom!'); - }); - - router.use(function(err, req, res, next){ - throw new Error('oops'); - }); - - router.use(function(err, req, res, next){ - assert.equal(err.message, 'oops'); - done(); - }); - - router.handle({ url: '/', method: 'GET' }, {}, done); - }); - }) - - describe('FQDN', function () { - it('should not obscure FQDNs', function (done) { - var request = { hit: 0, url: 'http://example.com/foo', method: 'GET' }; - var router = new Router(); - - router.use(function (req, res, next) { - assert.equal(req.hit++, 0); - assert.equal(req.url, 'http://example.com/foo'); - next(); - }); - - router.handle(request, {}, function (err) { - if (err) return done(err); - assert.equal(request.hit, 1); - done(); - }); - }); - - it('should ignore FQDN in search', function (done) { - var request = { hit: 0, url: '/proxy?url=http://example.com/blog/post/1', method: 'GET' }; - var router = new Router(); - - router.use('/proxy', function (req, res, next) { - assert.equal(req.hit++, 0); - assert.equal(req.url, '/?url=http://example.com/blog/post/1'); - next(); - }); - - router.handle(request, {}, function (err) { - if (err) return done(err); - assert.equal(request.hit, 1); - done(); - }); - }); - - it('should ignore FQDN in path', function (done) { - var request = { hit: 0, url: '/proxy/http://example.com/blog/post/1', method: 'GET' }; - var router = new Router(); - - router.use('/proxy', function (req, res, next) { - assert.equal(req.hit++, 0); - assert.equal(req.url, '/http://example.com/blog/post/1'); - next(); - }); - - router.handle(request, {}, function (err) { - if (err) return done(err); - assert.equal(request.hit, 1); - done(); - }); - }); - - it('should adjust FQDN req.url', function (done) { - var request = { hit: 0, url: 'http://example.com/blog/post/1', method: 'GET' }; - var router = new Router(); - - router.use('/blog', function (req, res, next) { - assert.equal(req.hit++, 0); - assert.equal(req.url, 'http://example.com/post/1'); - next(); - }); - - router.handle(request, {}, function (err) { - if (err) return done(err); - assert.equal(request.hit, 1); - done(); - }); - }); - - it('should adjust FQDN req.url with multiple handlers', function (done) { - var request = { hit: 0, url: 'http://example.com/blog/post/1', method: 'GET' }; - var router = new Router(); - - router.use(function (req, res, next) { - assert.equal(req.hit++, 0); - assert.equal(req.url, 'http://example.com/blog/post/1'); - next(); - }); - - router.use('/blog', function (req, res, next) { - assert.equal(req.hit++, 1); - assert.equal(req.url, 'http://example.com/post/1'); - next(); - }); - - router.handle(request, {}, function (err) { - if (err) return done(err); - assert.equal(request.hit, 2); - done(); - }); - }); - - it('should adjust FQDN req.url with multiple routed handlers', function (done) { - var request = { hit: 0, url: 'http://example.com/blog/post/1', method: 'GET' }; - var router = new Router(); - - router.use('/blog', function (req, res, next) { - assert.equal(req.hit++, 0); - assert.equal(req.url, 'http://example.com/post/1'); - next(); - }); - - router.use('/blog', function (req, res, next) { - assert.equal(req.hit++, 1); - assert.equal(req.url, 'http://example.com/post/1'); - next(); - }); - - router.use(function (req, res, next) { - assert.equal(req.hit++, 2); - assert.equal(req.url, 'http://example.com/blog/post/1'); - next(); - }); - - router.handle(request, {}, function (err) { - if (err) return done(err); - assert.equal(request.hit, 3); - done(); - }); - }); - }) - - describe('.all', function() { - it('should support using .all to capture all http verbs', function(done){ - var router = new Router(); - - var count = 0; - router.all('/foo', function(){ count++; }); - - var url = '/foo?bar=baz'; - - methods.forEach(function testMethod(method) { - router.handle({ url: url, method: method }, {}, function() {}); - }); - - assert.equal(count, methods.length); - done(); - }) - - it('should be called for any URL when "*"', function (done) { - var cb = after(4, done) - var router = new Router() - - function no () { - throw new Error('should not be called') - } - - router.all('*', function (req, res) { - res.end() - }) - - router.handle({ url: '/', method: 'GET' }, { end: cb }, no) - router.handle({ url: '/foo', method: 'GET' }, { end: cb }, no) - router.handle({ url: 'foo', method: 'GET' }, { end: cb }, no) - router.handle({ url: '*', method: 'GET' }, { end: cb }, no) - }) - }) - - describe('.use', function() { - it('should require middleware', function () { - var router = new Router() - assert.throws(function () { router.use('/') }, /requires a middleware function/) - }) - - it('should reject string as middleware', function () { - var router = new Router() - assert.throws(function () { router.use('/', 'foo') }, /requires a middleware function but got a string/) - }) - - it('should reject number as middleware', function () { - var router = new Router() - assert.throws(function () { router.use('/', 42) }, /requires a middleware function but got a number/) - }) - - it('should reject null as middleware', function () { - var router = new Router() - assert.throws(function () { router.use('/', null) }, /requires a middleware function but got a Null/) - }) - - it('should reject Date as middleware', function () { - var router = new Router() - assert.throws(function () { router.use('/', new Date()) }, /requires a middleware function but got a Date/) - }) - - it('should be called for any URL', function (done) { - var cb = after(4, done) - var router = new Router() - - function no () { - throw new Error('should not be called') - } - - router.use(function (req, res) { - res.end() - }) - - router.handle({ url: '/', method: 'GET' }, { end: cb }, no) - router.handle({ url: '/foo', method: 'GET' }, { end: cb }, no) - router.handle({ url: 'foo', method: 'GET' }, { end: cb }, no) - router.handle({ url: '*', method: 'GET' }, { end: cb }, no) - }) - - it('should accept array of middleware', function(done){ - var count = 0; - var router = new Router(); - - function fn1(req, res, next){ - assert.equal(++count, 1); - next(); - } - - function fn2(req, res, next){ - assert.equal(++count, 2); - next(); - } - - router.use([fn1, fn2], function(req, res){ - assert.equal(++count, 3); - done(); - }); - - router.handle({ url: '/foo', method: 'GET' }, {}, function(){}); - }) - }) - - describe('.param', function() { - it('should call param function when routing VERBS', function(done) { - var router = new Router(); - - router.param('id', function(req, res, next, id) { - assert.equal(id, '123'); - next(); - }); - - router.get('/foo/:id/bar', function(req, res, next) { - assert.equal(req.params.id, '123'); - next(); - }); - - router.handle({ url: '/foo/123/bar', method: 'get' }, {}, done); - }); - - it('should call param function when routing middleware', function(done) { - var router = new Router(); - - router.param('id', function(req, res, next, id) { - assert.equal(id, '123'); - next(); - }); - - router.use('/foo/:id/bar', function(req, res, next) { - assert.equal(req.params.id, '123'); - assert.equal(req.url, '/baz'); - next(); - }); - - router.handle({ url: '/foo/123/bar/baz', method: 'get' }, {}, done); - }); - - it('should only call once per request', function(done) { - var count = 0; - var req = { url: '/foo/bob/bar', method: 'get' }; - var router = new Router(); - var sub = new Router(); - - sub.get('/bar', function(req, res, next) { - next(); - }); - - router.param('user', function(req, res, next, user) { - count++; - req.user = user; - next(); - }); - - router.use('/foo/:user/', new Router()); - router.use('/foo/:user/', sub); - - router.handle(req, {}, function(err) { - if (err) return done(err); - assert.equal(count, 1); - assert.equal(req.user, 'bob'); - done(); - }); - }); - - it('should call when values differ', function(done) { - var count = 0; - var req = { url: '/foo/bob/bar', method: 'get' }; - var router = new Router(); - var sub = new Router(); - - sub.get('/bar', function(req, res, next) { - next(); - }); - - router.param('user', function(req, res, next, user) { - count++; - req.user = user; - next(); - }); - - router.use('/foo/:user/', new Router()); - router.use('/:user/bob/', sub); - - router.handle(req, {}, function(err) { - if (err) return done(err); - assert.equal(count, 2); - assert.equal(req.user, 'foo'); - done(); - }); - }); - }); - - describe('parallel requests', function() { - it('should not mix requests', function(done) { - var req1 = { url: '/foo/50/bar', method: 'get' }; - var req2 = { url: '/foo/10/bar', method: 'get' }; - var router = new Router(); - var sub = new Router(); - - done = after(2, done); - - sub.get('/bar', function(req, res, next) { - next(); - }); - - router.param('ms', function(req, res, next, ms) { - ms = parseInt(ms, 10); - req.ms = ms; - setTimeout(next, ms); - }); - - router.use('/foo/:ms/', new Router()); - router.use('/foo/:ms/', sub); - - router.handle(req1, {}, function(err) { - assert.ifError(err); - assert.equal(req1.ms, 50); - assert.equal(req1.originalUrl, '/foo/50/bar'); - done(); - }); - - router.handle(req2, {}, function(err) { - assert.ifError(err); - assert.equal(req2.ms, 10); - assert.equal(req2.originalUrl, '/foo/10/bar'); - done(); - }); - }); - }); -}) diff --git a/test/app.all.js b/test/app.all.js deleted file mode 100644 index 185a8332..00000000 --- a/test/app.all.js +++ /dev/null @@ -1,38 +0,0 @@ -'use strict' - -var after = require('after') -var express = require('../') - , request = require('supertest'); - -describe('app.all()', function(){ - it('should add a router per method', function(done){ - var app = express(); - var cb = after(2, done) - - app.all('/tobi', function(req, res){ - res.end(req.method); - }); - - request(app) - .put('/tobi') - .expect(200, 'PUT', cb) - - request(app) - .get('/tobi') - .expect(200, 'GET', cb) - }) - - it('should run the callback for a method just once', function(done){ - var app = express() - , n = 0; - - app.all('/*', function(req, res, next){ - if (n++) return done(new Error('DELETE called several times')); - next(); - }); - - request(app) - .del('/tobi') - .expect(404, done); - }) -}) diff --git a/test/app.del.js b/test/app.del.js deleted file mode 100644 index e9e5769d..00000000 --- a/test/app.del.js +++ /dev/null @@ -1,18 +0,0 @@ -'use strict' - -var express = require('../') - , request = require('supertest'); - -describe('app.del()', function(){ - it('should alias app.delete()', function(done){ - var app = express(); - - app.del('/tobi', function(req, res){ - res.end('deleted tobi!'); - }); - - request(app) - .del('/tobi') - .expect('deleted tobi!', done); - }) -}) diff --git a/test/app.engine.js b/test/app.engine.js deleted file mode 100644 index 214510a9..00000000 --- a/test/app.engine.js +++ /dev/null @@ -1,83 +0,0 @@ -'use strict' - -var assert = require('assert') -var express = require('../') - , fs = require('fs'); -var path = require('path') - -function render(path, options, fn) { - fs.readFile(path, 'utf8', function(err, str){ - if (err) return fn(err); - str = str.replace('{{user.name}}', options.user.name); - fn(null, str); - }); -} - -describe('app', function(){ - describe('.engine(ext, fn)', function(){ - it('should map a template engine', function(done){ - var app = express(); - - app.set('views', path.join(__dirname, 'fixtures')) - app.engine('.html', render); - app.locals.user = { name: 'tobi' }; - - app.render('user.html', function(err, str){ - if (err) return done(err); - assert.strictEqual(str, '

tobi

') - done(); - }) - }) - - it('should throw when the callback is missing', function(){ - var app = express(); - assert.throws(function () { - app.engine('.html', null); - }, /callback function required/) - }) - - it('should work without leading "."', function(done){ - var app = express(); - - app.set('views', path.join(__dirname, 'fixtures')) - app.engine('html', render); - app.locals.user = { name: 'tobi' }; - - app.render('user.html', function(err, str){ - if (err) return done(err); - assert.strictEqual(str, '

tobi

') - done(); - }) - }) - - it('should work "view engine" setting', function(done){ - var app = express(); - - app.set('views', path.join(__dirname, 'fixtures')) - app.engine('html', render); - app.set('view engine', 'html'); - app.locals.user = { name: 'tobi' }; - - app.render('user', function(err, str){ - if (err) return done(err); - assert.strictEqual(str, '

tobi

') - done(); - }) - }) - - it('should work "view engine" with leading "."', function(done){ - var app = express(); - - app.set('views', path.join(__dirname, 'fixtures')) - app.engine('.html', render); - app.set('view engine', '.html'); - app.locals.user = { name: 'tobi' }; - - app.render('user', function(err, str){ - if (err) return done(err); - assert.strictEqual(str, '

tobi

') - done(); - }) - }) - }) -}) diff --git a/test/app.head.js b/test/app.head.js deleted file mode 100644 index fabb9879..00000000 --- a/test/app.head.js +++ /dev/null @@ -1,66 +0,0 @@ -'use strict' - -var express = require('../'); -var request = require('supertest'); -var assert = require('assert'); - -describe('HEAD', function(){ - it('should default to GET', function(done){ - var app = express(); - - app.get('/tobi', function(req, res){ - // send() detects HEAD - res.send('tobi'); - }); - - request(app) - .head('/tobi') - .expect(200, done); - }) - - it('should output the same headers as GET requests', function(done){ - var app = express(); - - app.get('/tobi', function(req, res){ - // send() detects HEAD - res.send('tobi'); - }); - - request(app) - .head('/tobi') - .expect(200, function(err, res){ - if (err) return done(err); - var headers = res.headers; - request(app) - .get('/tobi') - .expect(200, function(err, res){ - if (err) return done(err); - delete headers.date; - delete res.headers.date; - assert.deepEqual(res.headers, headers); - done(); - }); - }); - }) -}) - -describe('app.head()', function(){ - it('should override', function(done){ - var app = express() - - app.head('/tobi', function(req, res){ - res.header('x-method', 'head') - res.end() - }); - - app.get('/tobi', function(req, res){ - res.header('x-method', 'get') - res.send('tobi'); - }); - - request(app) - .head('/tobi') - .expect('x-method', 'head') - .expect(200, done) - }) -}) diff --git a/test/app.js b/test/app.js deleted file mode 100644 index 6134717c..00000000 --- a/test/app.js +++ /dev/null @@ -1,132 +0,0 @@ -'use strict' - -var assert = require('assert') -var express = require('..') -var request = require('supertest') - -describe('app', function(){ - it('should inherit from event emitter', function(done){ - var app = express(); - app.on('foo', done); - app.emit('foo'); - }) - - it('should be callable', function(){ - var app = express(); - assert.equal(typeof app, 'function'); - }) - - it('should 404 without routes', function(done){ - request(express()) - .get('/') - .expect(404, done); - }) -}) - -describe('app.parent', function(){ - it('should return the parent when mounted', function(){ - var app = express() - , blog = express() - , blogAdmin = express(); - - app.use('/blog', blog); - blog.use('/admin', blogAdmin); - - assert(!app.parent, 'app.parent'); - assert.strictEqual(blog.parent, app) - assert.strictEqual(blogAdmin.parent, blog) - }) -}) - -describe('app.mountpath', function(){ - it('should return the mounted path', function(){ - var admin = express(); - var app = express(); - var blog = express(); - var fallback = express(); - - app.use('/blog', blog); - app.use(fallback); - blog.use('/admin', admin); - - assert.strictEqual(admin.mountpath, '/admin') - assert.strictEqual(app.mountpath, '/') - assert.strictEqual(blog.mountpath, '/blog') - assert.strictEqual(fallback.mountpath, '/') - }) -}) - -describe('app.router', function(){ - it('should throw with notice', function(done){ - var app = express() - - try { - app.router; - } catch(err) { - done(); - } - }) -}) - -describe('app.path()', function(){ - it('should return the canonical', function(){ - var app = express() - , blog = express() - , blogAdmin = express(); - - app.use('/blog', blog); - blog.use('/admin', blogAdmin); - - assert.strictEqual(app.path(), '') - assert.strictEqual(blog.path(), '/blog') - assert.strictEqual(blogAdmin.path(), '/blog/admin') - }) -}) - -describe('in development', function(){ - before(function () { - this.env = process.env.NODE_ENV - process.env.NODE_ENV = 'development' - }) - - after(function () { - process.env.NODE_ENV = this.env - }) - - it('should disable "view cache"', function(){ - var app = express(); - assert.ok(!app.enabled('view cache')) - }) -}) - -describe('in production', function(){ - before(function () { - this.env = process.env.NODE_ENV - process.env.NODE_ENV = 'production' - }) - - after(function () { - process.env.NODE_ENV = this.env - }) - - it('should enable "view cache"', function(){ - var app = express(); - assert.ok(app.enabled('view cache')) - }) -}) - -describe('without NODE_ENV', function(){ - before(function () { - this.env = process.env.NODE_ENV - process.env.NODE_ENV = '' - }) - - after(function () { - process.env.NODE_ENV = this.env - }) - - it('should default to development', function(){ - var app = express(); - assert.strictEqual(app.get('env'), 'development') - }) -}) diff --git a/test/app.listen.js b/test/app.listen.js deleted file mode 100644 index 5b150063..00000000 --- a/test/app.listen.js +++ /dev/null @@ -1,13 +0,0 @@ -'use strict' - -var express = require('../') - -describe('app.listen()', function(){ - it('should wrap with an HTTP server', function(done){ - var app = express(); - - var server = app.listen(0, function () { - server.close(done) - }); - }) -}) diff --git a/test/app.locals.js b/test/app.locals.js deleted file mode 100644 index 657b4b75..00000000 --- a/test/app.locals.js +++ /dev/null @@ -1,25 +0,0 @@ -'use strict' - -var assert = require('assert') -var express = require('../') - -describe('app', function(){ - describe('.locals', function () { - it('should default object', function () { - var app = express() - assert.ok(app.locals) - assert.strictEqual(typeof app.locals, 'object') - }) - - describe('.settings', function () { - it('should contain app settings ', function () { - var app = express() - app.set('title', 'Express') - assert.ok(app.locals.settings) - assert.strictEqual(typeof app.locals.settings, 'object') - assert.strictEqual(app.locals.settings, app.settings) - assert.strictEqual(app.locals.settings.title, 'Express') - }) - }) - }) -}) diff --git a/test/app.options.js b/test/app.options.js deleted file mode 100644 index fdfd38c8..00000000 --- a/test/app.options.js +++ /dev/null @@ -1,116 +0,0 @@ -'use strict' - -var express = require('../') - , request = require('supertest'); - -describe('OPTIONS', function(){ - it('should default to the routes defined', function(done){ - var app = express(); - - app.del('/', function(){}); - app.get('/users', function(req, res){}); - app.put('/users', function(req, res){}); - - request(app) - .options('/users') - .expect('Allow', 'GET,HEAD,PUT') - .expect(200, 'GET,HEAD,PUT', done); - }) - - it('should only include each method once', function(done){ - var app = express(); - - app.del('/', function(){}); - app.get('/users', function(req, res){}); - app.put('/users', function(req, res){}); - app.get('/users', function(req, res){}); - - request(app) - .options('/users') - .expect('Allow', 'GET,HEAD,PUT') - .expect(200, 'GET,HEAD,PUT', done); - }) - - it('should not be affected by app.all', function(done){ - var app = express(); - - app.get('/', function(){}); - app.get('/users', function(req, res){}); - app.put('/users', function(req, res){}); - app.all('/users', function(req, res, next){ - res.setHeader('x-hit', '1'); - next(); - }); - - request(app) - .options('/users') - .expect('x-hit', '1') - .expect('Allow', 'GET,HEAD,PUT') - .expect(200, 'GET,HEAD,PUT', done); - }) - - it('should not respond if the path is not defined', function(done){ - var app = express(); - - app.get('/users', function(req, res){}); - - request(app) - .options('/other') - .expect(404, done); - }) - - it('should forward requests down the middleware chain', function(done){ - var app = express(); - var router = new express.Router(); - - router.get('/users', function(req, res){}); - app.use(router); - app.get('/other', function(req, res){}); - - request(app) - .options('/other') - .expect('Allow', 'GET,HEAD') - .expect(200, 'GET,HEAD', done); - }) - - describe('when error occurs in response handler', function () { - it('should pass error to callback', function (done) { - var app = express(); - var router = express.Router(); - - router.get('/users', function(req, res){}); - - app.use(function (req, res, next) { - res.writeHead(200); - next(); - }); - app.use(router); - app.use(function (err, req, res, next) { - res.end('true'); - }); - - request(app) - .options('/users') - .expect(200, 'true', done) - }) - }) -}) - -describe('app.options()', function(){ - it('should override the default behavior', function(done){ - var app = express(); - - app.options('/users', function(req, res){ - res.set('Allow', 'GET'); - res.send('GET'); - }); - - app.get('/users', function(req, res){}); - app.put('/users', function(req, res){}); - - request(app) - .options('/users') - .expect('GET') - .expect('Allow', 'GET', done); - }) -}) diff --git a/test/app.param.js b/test/app.param.js deleted file mode 100644 index b4ccc8a2..00000000 --- a/test/app.param.js +++ /dev/null @@ -1,365 +0,0 @@ -'use strict' - -var assert = require('assert') -var express = require('../') - , request = require('supertest'); - -describe('app', function(){ - describe('.param(fn)', function(){ - it('should map app.param(name, ...) logic', function(done){ - var app = express(); - - app.param(function(name, regexp){ - if (Object.prototype.toString.call(regexp) === '[object RegExp]') { // See #1557 - return function(req, res, next, val){ - var captures; - if (captures = regexp.exec(String(val))) { - req.params[name] = captures[1]; - next(); - } else { - next('route'); - } - } - } - }) - - app.param(':name', /^([a-zA-Z]+)$/); - - app.get('/user/:name', function(req, res){ - res.send(req.params.name); - }); - - request(app) - .get('/user/tj') - .expect(200, 'tj', function (err) { - if (err) return done(err) - request(app) - .get('/user/123') - .expect(404, done); - }); - - }) - - it('should fail if not given fn', function(){ - var app = express(); - assert.throws(app.param.bind(app, ':name', 'bob')) - }) - }) - - describe('.param(names, fn)', function(){ - it('should map the array', function(done){ - var app = express(); - - app.param(['id', 'uid'], function(req, res, next, id){ - id = Number(id); - if (isNaN(id)) return next('route'); - req.params.id = id; - next(); - }); - - app.get('/post/:id', function(req, res){ - var id = req.params.id; - res.send((typeof id) + ':' + id) - }); - - app.get('/user/:uid', function(req, res){ - var id = req.params.id; - res.send((typeof id) + ':' + id) - }); - - request(app) - .get('/user/123') - .expect(200, 'number:123', function (err) { - if (err) return done(err) - request(app) - .get('/post/123') - .expect('number:123', done) - }) - }) - }) - - describe('.param(name, fn)', function(){ - it('should map logic for a single param', function(done){ - var app = express(); - - app.param('id', function(req, res, next, id){ - id = Number(id); - if (isNaN(id)) return next('route'); - req.params.id = id; - next(); - }); - - app.get('/user/:id', function(req, res){ - var id = req.params.id; - res.send((typeof id) + ':' + id) - }); - - request(app) - .get('/user/123') - .expect(200, 'number:123', done) - }) - - it('should only call once per request', function(done) { - var app = express(); - var called = 0; - var count = 0; - - app.param('user', function(req, res, next, user) { - called++; - req.user = user; - next(); - }); - - app.get('/foo/:user', function(req, res, next) { - count++; - next(); - }); - app.get('/foo/:user', function(req, res, next) { - count++; - next(); - }); - app.use(function(req, res) { - res.end([count, called, req.user].join(' ')); - }); - - request(app) - .get('/foo/bob') - .expect('2 1 bob', done); - }) - - it('should call when values differ', function(done) { - var app = express(); - var called = 0; - var count = 0; - - app.param('user', function(req, res, next, user) { - called++; - req.users = (req.users || []).concat(user); - next(); - }); - - app.get('/:user/bob', function(req, res, next) { - count++; - next(); - }); - app.get('/foo/:user', function(req, res, next) { - count++; - next(); - }); - app.use(function(req, res) { - res.end([count, called, req.users.join(',')].join(' ')); - }); - - request(app) - .get('/foo/bob') - .expect('2 2 foo,bob', done); - }) - - it('should support altering req.params across routes', function(done) { - var app = express(); - - app.param('user', function(req, res, next, user) { - req.params.user = 'loki'; - next(); - }); - - app.get('/:user', function(req, res, next) { - next('route'); - }); - app.get('/:user', function (req, res) { - res.send(req.params.user); - }); - - request(app) - .get('/bob') - .expect('loki', done); - }) - - it('should not invoke without route handler', function(done) { - var app = express(); - - app.param('thing', function(req, res, next, thing) { - req.thing = thing; - next(); - }); - - app.param('user', function(req, res, next, user) { - next(new Error('invalid invocation')) - }); - - app.post('/:user', function (req, res) { - res.send(req.params.user); - }); - - app.get('/:thing', function (req, res) { - res.send(req.thing); - }); - - request(app) - .get('/bob') - .expect(200, 'bob', done); - }) - - it('should work with encoded values', function(done){ - var app = express(); - - app.param('name', function(req, res, next, name){ - req.params.name = name; - next(); - }); - - app.get('/user/:name', function(req, res){ - var name = req.params.name; - res.send('' + name); - }); - - request(app) - .get('/user/foo%25bar') - .expect('foo%bar', done); - }) - - it('should catch thrown error', function(done){ - var app = express(); - - app.param('id', function(req, res, next, id){ - throw new Error('err!'); - }); - - app.get('/user/:id', function(req, res){ - var id = req.params.id; - res.send('' + id); - }); - - request(app) - .get('/user/123') - .expect(500, done); - }) - - it('should catch thrown secondary error', function(done){ - var app = express(); - - app.param('id', function(req, res, next, val){ - process.nextTick(next); - }); - - app.param('id', function(req, res, next, id){ - throw new Error('err!'); - }); - - app.get('/user/:id', function(req, res){ - var id = req.params.id; - res.send('' + id); - }); - - request(app) - .get('/user/123') - .expect(500, done); - }) - - it('should defer to next route', function(done){ - var app = express(); - - app.param('id', function(req, res, next, id){ - next('route'); - }); - - app.get('/user/:id', function(req, res){ - var id = req.params.id; - res.send('' + id); - }); - - app.get('/:name/123', function(req, res){ - res.send('name'); - }); - - request(app) - .get('/user/123') - .expect('name', done); - }) - - it('should defer all the param routes', function(done){ - var app = express(); - - app.param('id', function(req, res, next, val){ - if (val === 'new') return next('route'); - return next(); - }); - - app.all('/user/:id', function(req, res){ - res.send('all.id'); - }); - - app.get('/user/:id', function(req, res){ - res.send('get.id'); - }); - - app.get('/user/new', function(req, res){ - res.send('get.new'); - }); - - request(app) - .get('/user/new') - .expect('get.new', done); - }) - - it('should not call when values differ on error', function(done) { - var app = express(); - var called = 0; - var count = 0; - - app.param('user', function(req, res, next, user) { - called++; - if (user === 'foo') throw new Error('err!'); - req.user = user; - next(); - }); - - app.get('/:user/bob', function(req, res, next) { - count++; - next(); - }); - app.get('/foo/:user', function(req, res, next) { - count++; - next(); - }); - - app.use(function(err, req, res, next) { - res.status(500); - res.send([count, called, err.message].join(' ')); - }); - - request(app) - .get('/foo/bob') - .expect(500, '0 1 err!', done) - }); - - it('should call when values differ when using "next"', function(done) { - var app = express(); - var called = 0; - var count = 0; - - app.param('user', function(req, res, next, user) { - called++; - if (user === 'foo') return next('route'); - req.user = user; - next(); - }); - - app.get('/:user/bob', function(req, res, next) { - count++; - next(); - }); - app.get('/foo/:user', function(req, res, next) { - count++; - next(); - }); - app.use(function(req, res) { - res.end([count, called, req.user].join(' ')); - }); - - request(app) - .get('/foo/bob') - .expect('1 2 bob', done); - }) - }) -}) diff --git a/test/app.render.js b/test/app.render.js deleted file mode 100644 index 9d202acf..00000000 --- a/test/app.render.js +++ /dev/null @@ -1,374 +0,0 @@ -'use strict' - -var assert = require('assert') -var express = require('..'); -var path = require('path') -var tmpl = require('./support/tmpl'); - -describe('app', function(){ - describe('.render(name, fn)', function(){ - it('should support absolute paths', function(done){ - var app = createApp(); - - app.locals.user = { name: 'tobi' }; - - app.render(path.join(__dirname, 'fixtures', 'user.tmpl'), function (err, str) { - if (err) return done(err); - assert.strictEqual(str, '

tobi

') - done(); - }) - }) - - it('should support absolute paths with "view engine"', function(done){ - var app = createApp(); - - app.set('view engine', 'tmpl'); - app.locals.user = { name: 'tobi' }; - - app.render(path.join(__dirname, 'fixtures', 'user'), function (err, str) { - if (err) return done(err); - assert.strictEqual(str, '

tobi

') - done(); - }) - }) - - it('should expose app.locals', function(done){ - var app = createApp(); - - app.set('views', path.join(__dirname, 'fixtures')) - app.locals.user = { name: 'tobi' }; - - app.render('user.tmpl', function (err, str) { - if (err) return done(err); - assert.strictEqual(str, '

tobi

') - done(); - }) - }) - - it('should support index.', function(done){ - var app = createApp(); - - app.set('views', path.join(__dirname, 'fixtures')) - app.set('view engine', 'tmpl'); - - app.render('blog/post', function (err, str) { - if (err) return done(err); - assert.strictEqual(str, '

blog post

') - done(); - }) - }) - - it('should handle render error throws', function(done){ - var app = express(); - - function View(name, options){ - this.name = name; - this.path = 'fale'; - } - - View.prototype.render = function(options, fn){ - throw new Error('err!'); - }; - - app.set('view', View); - - app.render('something', function(err, str){ - assert.ok(err) - assert.strictEqual(err.message, 'err!') - done(); - }) - }) - - describe('when the file does not exist', function(){ - it('should provide a helpful error', function(done){ - var app = createApp(); - - app.set('views', path.join(__dirname, 'fixtures')) - app.render('rawr.tmpl', function (err) { - assert.ok(err) - assert.equal(err.message, 'Failed to lookup view "rawr.tmpl" in views directory "' + path.join(__dirname, 'fixtures') + '"') - done(); - }); - }) - }) - - describe('when an error occurs', function(){ - it('should invoke the callback', function(done){ - var app = createApp(); - - app.set('views', path.join(__dirname, 'fixtures')) - - app.render('user.tmpl', function (err) { - assert.ok(err) - assert.equal(err.name, 'RenderError') - done() - }) - }) - }) - - describe('when an extension is given', function(){ - it('should render the template', function(done){ - var app = createApp(); - - app.set('views', path.join(__dirname, 'fixtures')) - - app.render('email.tmpl', function (err, str) { - if (err) return done(err); - assert.strictEqual(str, '

This is an email

') - done(); - }) - }) - }) - - describe('when "view engine" is given', function(){ - it('should render the template', function(done){ - var app = createApp(); - - app.set('view engine', 'tmpl'); - app.set('views', path.join(__dirname, 'fixtures')) - - app.render('email', function(err, str){ - if (err) return done(err); - assert.strictEqual(str, '

This is an email

') - done(); - }) - }) - }) - - describe('when "views" is given', function(){ - it('should lookup the file in the path', function(done){ - var app = createApp(); - - app.set('views', path.join(__dirname, 'fixtures', 'default_layout')) - app.locals.user = { name: 'tobi' }; - - app.render('user.tmpl', function (err, str) { - if (err) return done(err); - assert.strictEqual(str, '

tobi

') - done(); - }) - }) - - describe('when array of paths', function(){ - it('should lookup the file in the path', function(done){ - var app = createApp(); - var views = [ - path.join(__dirname, 'fixtures', 'local_layout'), - path.join(__dirname, 'fixtures', 'default_layout') - ] - - app.set('views', views); - app.locals.user = { name: 'tobi' }; - - app.render('user.tmpl', function (err, str) { - if (err) return done(err); - assert.strictEqual(str, 'tobi') - done(); - }) - }) - - it('should lookup in later paths until found', function(done){ - var app = createApp(); - var views = [ - path.join(__dirname, 'fixtures', 'local_layout'), - path.join(__dirname, 'fixtures', 'default_layout') - ] - - app.set('views', views); - app.locals.name = 'tobi'; - - app.render('name.tmpl', function (err, str) { - if (err) return done(err); - assert.strictEqual(str, '

tobi

') - done(); - }) - }) - - it('should error if file does not exist', function(done){ - var app = createApp(); - var views = [ - path.join(__dirname, 'fixtures', 'local_layout'), - path.join(__dirname, 'fixtures', 'default_layout') - ] - - app.set('views', views); - app.locals.name = 'tobi'; - - app.render('pet.tmpl', function (err, str) { - assert.ok(err) - assert.equal(err.message, 'Failed to lookup view "pet.tmpl" in views directories "' + views[0] + '" or "' + views[1] + '"') - done(); - }) - }) - }) - }) - - describe('when a "view" constructor is given', function(){ - it('should create an instance of it', function(done){ - var app = express(); - - function View(name, options){ - this.name = name; - this.path = 'path is required by application.js as a signal of success even though it is not used there.'; - } - - View.prototype.render = function(options, fn){ - fn(null, 'abstract engine'); - }; - - app.set('view', View); - - app.render('something', function(err, str){ - if (err) return done(err); - assert.strictEqual(str, 'abstract engine') - done(); - }) - }) - }) - - describe('caching', function(){ - it('should always lookup view without cache', function(done){ - var app = express(); - var count = 0; - - function View(name, options){ - this.name = name; - this.path = 'fake'; - count++; - } - - View.prototype.render = function(options, fn){ - fn(null, 'abstract engine'); - }; - - app.set('view cache', false); - app.set('view', View); - - app.render('something', function(err, str){ - if (err) return done(err); - assert.strictEqual(count, 1) - assert.strictEqual(str, 'abstract engine') - app.render('something', function(err, str){ - if (err) return done(err); - assert.strictEqual(count, 2) - assert.strictEqual(str, 'abstract engine') - done(); - }) - }) - }) - - it('should cache with "view cache" setting', function(done){ - var app = express(); - var count = 0; - - function View(name, options){ - this.name = name; - this.path = 'fake'; - count++; - } - - View.prototype.render = function(options, fn){ - fn(null, 'abstract engine'); - }; - - app.set('view cache', true); - app.set('view', View); - - app.render('something', function(err, str){ - if (err) return done(err); - assert.strictEqual(count, 1) - assert.strictEqual(str, 'abstract engine') - app.render('something', function(err, str){ - if (err) return done(err); - assert.strictEqual(count, 1) - assert.strictEqual(str, 'abstract engine') - done(); - }) - }) - }) - }) - }) - - describe('.render(name, options, fn)', function(){ - it('should render the template', function(done){ - var app = createApp(); - - app.set('views', path.join(__dirname, 'fixtures')) - - var user = { name: 'tobi' }; - - app.render('user.tmpl', { user: user }, function (err, str) { - if (err) return done(err); - assert.strictEqual(str, '

tobi

') - done(); - }) - }) - - it('should expose app.locals', function(done){ - var app = createApp(); - - app.set('views', path.join(__dirname, 'fixtures')) - app.locals.user = { name: 'tobi' }; - - app.render('user.tmpl', {}, function (err, str) { - if (err) return done(err); - assert.strictEqual(str, '

tobi

') - done(); - }) - }) - - it('should give precedence to app.render() locals', function(done){ - var app = createApp(); - - app.set('views', path.join(__dirname, 'fixtures')) - app.locals.user = { name: 'tobi' }; - var jane = { name: 'jane' }; - - app.render('user.tmpl', { user: jane }, function (err, str) { - if (err) return done(err); - assert.strictEqual(str, '

jane

') - done(); - }) - }) - - describe('caching', function(){ - it('should cache with cache option', function(done){ - var app = express(); - var count = 0; - - function View(name, options){ - this.name = name; - this.path = 'fake'; - count++; - } - - View.prototype.render = function(options, fn){ - fn(null, 'abstract engine'); - }; - - app.set('view cache', false); - app.set('view', View); - - app.render('something', {cache: true}, function(err, str){ - if (err) return done(err); - assert.strictEqual(count, 1) - assert.strictEqual(str, 'abstract engine') - app.render('something', {cache: true}, function(err, str){ - if (err) return done(err); - assert.strictEqual(count, 1) - assert.strictEqual(str, 'abstract engine') - done(); - }) - }) - }) - }) - }) -}) - -function createApp() { - var app = express(); - - app.engine('.tmpl', tmpl); - - return app; -} diff --git a/test/app.request.js b/test/app.request.js deleted file mode 100644 index 4930af84..00000000 --- a/test/app.request.js +++ /dev/null @@ -1,143 +0,0 @@ -'use strict' - -var after = require('after') -var express = require('../') - , request = require('supertest'); - -describe('app', function(){ - describe('.request', function(){ - it('should extend the request prototype', function(done){ - var app = express(); - - app.request.querystring = function(){ - return require('url').parse(this.url).query; - }; - - app.use(function(req, res){ - res.end(req.querystring()); - }); - - request(app) - .get('/foo?name=tobi') - .expect('name=tobi', done); - }) - - it('should only extend for the referenced app', function (done) { - var app1 = express() - var app2 = express() - var cb = after(2, done) - - app1.request.foobar = function () { - return 'tobi' - } - - app1.get('/', function (req, res) { - res.send(req.foobar()) - }) - - app2.get('/', function (req, res) { - res.send(req.foobar()) - }) - - request(app1) - .get('/') - .expect(200, 'tobi', cb) - - request(app2) - .get('/') - .expect(500, /(?:not a function|has no method)/, cb) - }) - - it('should inherit to sub apps', function (done) { - var app1 = express() - var app2 = express() - var cb = after(2, done) - - app1.request.foobar = function () { - return 'tobi' - } - - app1.use('/sub', app2) - - app1.get('/', function (req, res) { - res.send(req.foobar()) - }) - - app2.get('/', function (req, res) { - res.send(req.foobar()) - }) - - request(app1) - .get('/') - .expect(200, 'tobi', cb) - - request(app1) - .get('/sub') - .expect(200, 'tobi', cb) - }) - - it('should allow sub app to override', function (done) { - var app1 = express() - var app2 = express() - var cb = after(2, done) - - app1.request.foobar = function () { - return 'tobi' - } - - app2.request.foobar = function () { - return 'loki' - } - - app1.use('/sub', app2) - - app1.get('/', function (req, res) { - res.send(req.foobar()) - }) - - app2.get('/', function (req, res) { - res.send(req.foobar()) - }) - - request(app1) - .get('/') - .expect(200, 'tobi', cb) - - request(app1) - .get('/sub') - .expect(200, 'loki', cb) - }) - - it('should not pollute parent app', function (done) { - var app1 = express() - var app2 = express() - var cb = after(2, done) - - app1.request.foobar = function () { - return 'tobi' - } - - app2.request.foobar = function () { - return 'loki' - } - - app1.use('/sub', app2) - - app1.get('/sub/foo', function (req, res) { - res.send(req.foobar()) - }) - - app2.get('/', function (req, res) { - res.send(req.foobar()) - }) - - request(app1) - .get('/sub') - .expect(200, 'loki', cb) - - request(app1) - .get('/sub/foo') - .expect(200, 'tobi', cb) - }) - }) -}) diff --git a/test/app.response.js b/test/app.response.js deleted file mode 100644 index 5fb69f62..00000000 --- a/test/app.response.js +++ /dev/null @@ -1,143 +0,0 @@ -'use strict' - -var after = require('after') -var express = require('../') - , request = require('supertest'); - -describe('app', function(){ - describe('.response', function(){ - it('should extend the response prototype', function(done){ - var app = express(); - - app.response.shout = function(str){ - this.send(str.toUpperCase()); - }; - - app.use(function(req, res){ - res.shout('hey'); - }); - - request(app) - .get('/') - .expect('HEY', done); - }) - - it('should only extend for the referenced app', function (done) { - var app1 = express() - var app2 = express() - var cb = after(2, done) - - app1.response.shout = function (str) { - this.send(str.toUpperCase()) - } - - app1.get('/', function (req, res) { - res.shout('foo') - }) - - app2.get('/', function (req, res) { - res.shout('foo') - }) - - request(app1) - .get('/') - .expect(200, 'FOO', cb) - - request(app2) - .get('/') - .expect(500, /(?:not a function|has no method)/, cb) - }) - - it('should inherit to sub apps', function (done) { - var app1 = express() - var app2 = express() - var cb = after(2, done) - - app1.response.shout = function (str) { - this.send(str.toUpperCase()) - } - - app1.use('/sub', app2) - - app1.get('/', function (req, res) { - res.shout('foo') - }) - - app2.get('/', function (req, res) { - res.shout('foo') - }) - - request(app1) - .get('/') - .expect(200, 'FOO', cb) - - request(app1) - .get('/sub') - .expect(200, 'FOO', cb) - }) - - it('should allow sub app to override', function (done) { - var app1 = express() - var app2 = express() - var cb = after(2, done) - - app1.response.shout = function (str) { - this.send(str.toUpperCase()) - } - - app2.response.shout = function (str) { - this.send(str + '!') - } - - app1.use('/sub', app2) - - app1.get('/', function (req, res) { - res.shout('foo') - }) - - app2.get('/', function (req, res) { - res.shout('foo') - }) - - request(app1) - .get('/') - .expect(200, 'FOO', cb) - - request(app1) - .get('/sub') - .expect(200, 'foo!', cb) - }) - - it('should not pollute parent app', function (done) { - var app1 = express() - var app2 = express() - var cb = after(2, done) - - app1.response.shout = function (str) { - this.send(str.toUpperCase()) - } - - app2.response.shout = function (str) { - this.send(str + '!') - } - - app1.use('/sub', app2) - - app1.get('/sub/foo', function (req, res) { - res.shout('foo') - }) - - app2.get('/', function (req, res) { - res.shout('foo') - }) - - request(app1) - .get('/sub') - .expect(200, 'foo!', cb) - - request(app1) - .get('/sub/foo') - .expect(200, 'FOO', cb) - }) - }) -}) diff --git a/test/app.route.js b/test/app.route.js deleted file mode 100644 index eaf8a120..00000000 --- a/test/app.route.js +++ /dev/null @@ -1,64 +0,0 @@ -'use strict' - -var express = require('../'); -var request = require('supertest'); - -describe('app.route', function(){ - it('should return a new route', function(done){ - var app = express(); - - app.route('/foo') - .get(function(req, res) { - res.send('get'); - }) - .post(function(req, res) { - res.send('post'); - }); - - request(app) - .post('/foo') - .expect('post', done); - }); - - it('should all .VERB after .all', function(done){ - var app = express(); - - app.route('/foo') - .all(function(req, res, next) { - next(); - }) - .get(function(req, res) { - res.send('get'); - }) - .post(function(req, res) { - res.send('post'); - }); - - request(app) - .post('/foo') - .expect('post', done); - }); - - it('should support dynamic routes', function(done){ - var app = express(); - - app.route('/:foo') - .get(function(req, res) { - res.send(req.params.foo); - }); - - request(app) - .get('/test') - .expect('test', done); - }); - - it('should not error on empty routes', function(done){ - var app = express(); - - app.route('/:foo'); - - request(app) - .get('/test') - .expect(404, done); - }); -}); diff --git a/test/app.router.js b/test/app.router.js deleted file mode 100644 index 4fde0310..00000000 --- a/test/app.router.js +++ /dev/null @@ -1,1111 +0,0 @@ -'use strict' - -var after = require('after'); -var express = require('../') - , request = require('supertest') - , assert = require('assert') - , methods = require('methods'); - -describe('app.router', function(){ - it('should restore req.params after leaving router', function(done){ - var app = express(); - var router = new express.Router(); - - function handler1(req, res, next){ - res.setHeader('x-user-id', String(req.params.id)); - next() - } - - function handler2(req, res){ - res.send(req.params.id); - } - - router.use(function(req, res, next){ - res.setHeader('x-router', String(req.params.id)); - next(); - }); - - app.get('/user/:id', handler1, router, handler2); - - request(app) - .get('/user/1') - .expect('x-router', 'undefined') - .expect('x-user-id', '1') - .expect(200, '1', done); - }) - - describe('methods', function(){ - methods.concat('del').forEach(function(method){ - if (method === 'connect') return; - - it('should include ' + method.toUpperCase(), function(done){ - var app = express(); - - app[method]('/foo', function(req, res){ - res.send(method) - }); - - request(app) - [method]('/foo') - .expect(200, done) - }) - - it('should reject numbers for app.' + method, function(){ - var app = express(); - assert.throws(app[method].bind(app, '/', 3), /Number/) - }) - }); - - it('should re-route when method is altered', function (done) { - var app = express(); - var cb = after(3, done); - - app.use(function (req, res, next) { - if (req.method !== 'POST') return next(); - req.method = 'DELETE'; - res.setHeader('X-Method-Altered', '1'); - next(); - }); - - app.delete('/', function (req, res) { - res.end('deleted everything'); - }); - - request(app) - .get('/') - .expect(404, cb) - - request(app) - .delete('/') - .expect(200, 'deleted everything', cb); - - request(app) - .post('/') - .expect('X-Method-Altered', '1') - .expect(200, 'deleted everything', cb); - }); - }) - - describe('decode params', function () { - it('should decode correct params', function(done){ - var app = express(); - - app.get('/:name', function (req, res) { - res.send(req.params.name); - }); - - request(app) - .get('/foo%2Fbar') - .expect('foo/bar', done); - }) - - it('should not accept params in malformed paths', function(done) { - var app = express(); - - app.get('/:name', function (req, res) { - res.send(req.params.name); - }); - - request(app) - .get('/%foobar') - .expect(400, done); - }) - - it('should not decode spaces', function(done) { - var app = express(); - - app.get('/:name', function (req, res) { - res.send(req.params.name); - }); - - request(app) - .get('/foo+bar') - .expect('foo+bar', done); - }) - - it('should work with unicode', function(done) { - var app = express(); - - app.get('/:name', function (req, res) { - res.send(req.params.name); - }); - - request(app) - .get('/%ce%b1') - .expect('\u03b1', done); - }) - }) - - it('should be .use()able', function(done){ - var app = express(); - - var calls = []; - - app.use(function(req, res, next){ - calls.push('before'); - next(); - }); - - app.get('/', function(req, res, next){ - calls.push('GET /') - next(); - }); - - app.use(function(req, res, next){ - calls.push('after'); - res.json(calls) - }); - - request(app) - .get('/') - .expect(200, ['before', 'GET /', 'after'], done) - }) - - describe('when given a regexp', function(){ - it('should match the pathname only', function(done){ - var app = express(); - - app.get(/^\/user\/[0-9]+$/, function(req, res){ - res.end('user'); - }); - - request(app) - .get('/user/12?foo=bar') - .expect('user', done); - }) - - it('should populate req.params with the captures', function(done){ - var app = express(); - - app.get(/^\/user\/([0-9]+)\/(view|edit)?$/, function(req, res){ - var id = req.params[0] - , op = req.params[1]; - res.end(op + 'ing user ' + id); - }); - - request(app) - .get('/user/10/edit') - .expect('editing user 10', done); - }) - - it('should ensure regexp matches path prefix', function (done) { - var app = express() - var p = [] - - app.use(/\/api.*/, function (req, res, next) { - p.push('a') - next() - }) - app.use(/api/, function (req, res, next) { - p.push('b') - next() - }) - app.use(/\/test/, function (req, res, next) { - p.push('c') - next() - }) - app.use(function (req, res) { - res.end() - }) - - request(app) - .get('/test/api/1234') - .expect(200, function (err) { - if (err) return done(err) - assert.deepEqual(p, ['c']) - done() - }) - }) - }) - - describe('case sensitivity', function(){ - it('should be disabled by default', function(done){ - var app = express(); - - app.get('/user', function(req, res){ - res.end('tj'); - }); - - request(app) - .get('/USER') - .expect('tj', done); - }) - - describe('when "case sensitive routing" is enabled', function(){ - it('should match identical casing', function(done){ - var app = express(); - - app.enable('case sensitive routing'); - - app.get('/uSer', function(req, res){ - res.end('tj'); - }); - - request(app) - .get('/uSer') - .expect('tj', done); - }) - - it('should not match otherwise', function(done){ - var app = express(); - - app.enable('case sensitive routing'); - - app.get('/uSer', function(req, res){ - res.end('tj'); - }); - - request(app) - .get('/user') - .expect(404, done); - }) - }) - }) - - describe('params', function(){ - it('should overwrite existing req.params by default', function(done){ - var app = express(); - var router = new express.Router(); - - router.get('/:action', function(req, res){ - res.send(req.params); - }); - - app.use('/user/:user', router); - - request(app) - .get('/user/1/get') - .expect(200, '{"action":"get"}', done); - }) - - it('should allow merging existing req.params', function(done){ - var app = express(); - var router = new express.Router({ mergeParams: true }); - - router.get('/:action', function(req, res){ - var keys = Object.keys(req.params).sort(); - res.send(keys.map(function(k){ return [k, req.params[k]] })); - }); - - app.use('/user/:user', router); - - request(app) - .get('/user/tj/get') - .expect(200, '[["action","get"],["user","tj"]]', done); - }) - - it('should use params from router', function(done){ - var app = express(); - var router = new express.Router({ mergeParams: true }); - - router.get('/:thing', function(req, res){ - var keys = Object.keys(req.params).sort(); - res.send(keys.map(function(k){ return [k, req.params[k]] })); - }); - - app.use('/user/:thing', router); - - request(app) - .get('/user/tj/get') - .expect(200, '[["thing","get"]]', done); - }) - - it('should merge numeric indices req.params', function(done){ - var app = express(); - var router = new express.Router({ mergeParams: true }); - - router.get('/*.*', function(req, res){ - var keys = Object.keys(req.params).sort(); - res.send(keys.map(function(k){ return [k, req.params[k]] })); - }); - - app.use('/user/id:(\\d+)', router); - - request(app) - .get('/user/id:10/profile.json') - .expect(200, '[["0","10"],["1","profile"],["2","json"]]', done); - }) - - it('should merge numeric indices req.params when more in parent', function(done){ - var app = express(); - var router = new express.Router({ mergeParams: true }); - - router.get('/*', function(req, res){ - var keys = Object.keys(req.params).sort(); - res.send(keys.map(function(k){ return [k, req.params[k]] })); - }); - - app.use('/user/id:(\\d+)/name:(\\w+)', router); - - request(app) - .get('/user/id:10/name:tj/profile') - .expect(200, '[["0","10"],["1","tj"],["2","profile"]]', done); - }) - - it('should merge numeric indices req.params when parent has same number', function(done){ - var app = express(); - var router = new express.Router({ mergeParams: true }); - - router.get('/name:(\\w+)', function(req, res){ - var keys = Object.keys(req.params).sort(); - res.send(keys.map(function(k){ return [k, req.params[k]] })); - }); - - app.use('/user/id:(\\d+)', router); - - request(app) - .get('/user/id:10/name:tj') - .expect(200, '[["0","10"],["1","tj"]]', done); - }) - - it('should ignore invalid incoming req.params', function(done){ - var app = express(); - var router = new express.Router({ mergeParams: true }); - - router.get('/:name', function(req, res){ - var keys = Object.keys(req.params).sort(); - res.send(keys.map(function(k){ return [k, req.params[k]] })); - }); - - app.use('/user/', function (req, res, next) { - req.params = 3; // wat? - router(req, res, next); - }); - - request(app) - .get('/user/tj') - .expect(200, '[["name","tj"]]', done); - }) - - it('should restore req.params', function(done){ - var app = express(); - var router = new express.Router({ mergeParams: true }); - - router.get('/user:(\\w+)/*', function (req, res, next) { - next(); - }); - - app.use('/user/id:(\\d+)', function (req, res, next) { - router(req, res, function (err) { - var keys = Object.keys(req.params).sort(); - res.send(keys.map(function(k){ return [k, req.params[k]] })); - }); - }); - - request(app) - .get('/user/id:42/user:tj/profile') - .expect(200, '[["0","42"]]', done); - }) - }) - - describe('trailing slashes', function(){ - it('should be optional by default', function(done){ - var app = express(); - - app.get('/user', function(req, res){ - res.end('tj'); - }); - - request(app) - .get('/user/') - .expect('tj', done); - }) - - describe('when "strict routing" is enabled', function(){ - it('should match trailing slashes', function(done){ - var app = express(); - - app.enable('strict routing'); - - app.get('/user/', function(req, res){ - res.end('tj'); - }); - - request(app) - .get('/user/') - .expect('tj', done); - }) - - it('should pass-though middleware', function(done){ - var app = express(); - - app.enable('strict routing'); - - app.use(function (req, res, next) { - res.setHeader('x-middleware', 'true'); - next(); - }); - - app.get('/user/', function(req, res){ - res.end('tj'); - }); - - request(app) - .get('/user/') - .expect('x-middleware', 'true') - .expect(200, 'tj', done); - }) - - it('should pass-though mounted middleware', function(done){ - var app = express(); - - app.enable('strict routing'); - - app.use('/user/', function (req, res, next) { - res.setHeader('x-middleware', 'true'); - next(); - }); - - app.get('/user/test/', function(req, res){ - res.end('tj'); - }); - - request(app) - .get('/user/test/') - .expect('x-middleware', 'true') - .expect(200, 'tj', done); - }) - - it('should match no slashes', function(done){ - var app = express(); - - app.enable('strict routing'); - - app.get('/user', function(req, res){ - res.end('tj'); - }); - - request(app) - .get('/user') - .expect('tj', done); - }) - - it('should match middleware when omitting the trailing slash', function(done){ - var app = express(); - - app.enable('strict routing'); - - app.use('/user/', function(req, res){ - res.end('tj'); - }); - - request(app) - .get('/user') - .expect(200, 'tj', done); - }) - - it('should match middleware', function(done){ - var app = express(); - - app.enable('strict routing'); - - app.use('/user', function(req, res){ - res.end('tj'); - }); - - request(app) - .get('/user') - .expect(200, 'tj', done); - }) - - it('should match middleware when adding the trailing slash', function(done){ - var app = express(); - - app.enable('strict routing'); - - app.use('/user', function(req, res){ - res.end('tj'); - }); - - request(app) - .get('/user/') - .expect(200, 'tj', done); - }) - - it('should fail when omitting the trailing slash', function(done){ - var app = express(); - - app.enable('strict routing'); - - app.get('/user/', function(req, res){ - res.end('tj'); - }); - - request(app) - .get('/user') - .expect(404, done); - }) - - it('should fail when adding the trailing slash', function(done){ - var app = express(); - - app.enable('strict routing'); - - app.get('/user', function(req, res){ - res.end('tj'); - }); - - request(app) - .get('/user/') - .expect(404, done); - }) - }) - }) - - it('should allow escaped regexp', function(done){ - var app = express(); - - app.get('/user/\\d+', function(req, res){ - res.end('woot'); - }); - - request(app) - .get('/user/10') - .expect(200, function (err) { - if (err) return done(err) - request(app) - .get('/user/tj') - .expect(404, done); - }); - }) - - it('should allow literal "."', function(done){ - var app = express(); - - app.get('/api/users/:from..:to', function(req, res){ - var from = req.params.from - , to = req.params.to; - - res.end('users from ' + from + ' to ' + to); - }); - - request(app) - .get('/api/users/1..50') - .expect('users from 1 to 50', done); - }) - - describe('*', function(){ - it('should capture everything', function (done) { - var app = express() - - app.get('*', function (req, res) { - res.end(req.params[0]) - }) - - request(app) - .get('/user/tobi.json') - .expect('/user/tobi.json', done) - }) - - it('should decode the capture', function (done) { - var app = express() - - app.get('*', function (req, res) { - res.end(req.params[0]) - }) - - request(app) - .get('/user/tobi%20and%20loki.json') - .expect('/user/tobi and loki.json', done) - }) - - it('should denote a greedy capture group', function(done){ - var app = express(); - - app.get('/user/*.json', function(req, res){ - res.end(req.params[0]); - }); - - request(app) - .get('/user/tj.json') - .expect('tj', done); - }) - - it('should work with several', function(done){ - var app = express(); - - app.get('/api/*.*', function(req, res){ - var resource = req.params[0] - , format = req.params[1]; - res.end(resource + ' as ' + format); - }); - - request(app) - .get('/api/users/foo.bar.json') - .expect('users/foo.bar as json', done); - }) - - it('should work cross-segment', function(done){ - var app = express(); - var cb = after(2, done) - - app.get('/api*', function(req, res){ - res.send(req.params[0]); - }); - - request(app) - .get('/api') - .expect(200, '', cb) - - request(app) - .get('/api/hey') - .expect(200, '/hey', cb) - }) - - it('should allow naming', function(done){ - var app = express(); - - app.get('/api/:resource(*)', function(req, res){ - var resource = req.params.resource; - res.end(resource); - }); - - request(app) - .get('/api/users/0.json') - .expect('users/0.json', done); - }) - - it('should not be greedy immediately after param', function(done){ - var app = express(); - - app.get('/user/:user*', function(req, res){ - res.end(req.params.user); - }); - - request(app) - .get('/user/122') - .expect('122', done); - }) - - it('should eat everything after /', function(done){ - var app = express(); - - app.get('/user/:user*', function(req, res){ - res.end(req.params.user); - }); - - request(app) - .get('/user/122/aaa') - .expect('122', done); - }) - - it('should span multiple segments', function(done){ - var app = express(); - - app.get('/file/*', function(req, res){ - res.end(req.params[0]); - }); - - request(app) - .get('/file/javascripts/jquery.js') - .expect('javascripts/jquery.js', done); - }) - - it('should be optional', function(done){ - var app = express(); - - app.get('/file/*', function(req, res){ - res.end(req.params[0]); - }); - - request(app) - .get('/file/') - .expect('', done); - }) - - it('should require a preceding /', function(done){ - var app = express(); - - app.get('/file/*', function(req, res){ - res.end(req.params[0]); - }); - - request(app) - .get('/file') - .expect(404, done); - }) - - it('should keep correct parameter indexes', function(done){ - var app = express(); - - app.get('/*/user/:id', function (req, res) { - res.send(req.params); - }); - - request(app) - .get('/1/user/2') - .expect(200, '{"0":"1","id":"2"}', done); - }) - - it('should work within arrays', function(done){ - var app = express(); - - app.get(['/user/:id', '/foo/*', '/:bar'], function (req, res) { - res.send(req.params.bar); - }); - - request(app) - .get('/test') - .expect(200, 'test', done); - }) - }) - - describe(':name', function(){ - it('should denote a capture group', function(done){ - var app = express(); - - app.get('/user/:user', function(req, res){ - res.end(req.params.user); - }); - - request(app) - .get('/user/tj') - .expect('tj', done); - }) - - it('should match a single segment only', function(done){ - var app = express(); - - app.get('/user/:user', function(req, res){ - res.end(req.params.user); - }); - - request(app) - .get('/user/tj/edit') - .expect(404, done); - }) - - it('should allow several capture groups', function(done){ - var app = express(); - - app.get('/user/:user/:op', function(req, res){ - res.end(req.params.op + 'ing ' + req.params.user); - }); - - request(app) - .get('/user/tj/edit') - .expect('editing tj', done); - }) - - it('should work following a partial capture group', function(done){ - var app = express(); - var cb = after(2, done); - - app.get('/user(s)?/:user/:op', function(req, res){ - res.end(req.params.op + 'ing ' + req.params.user + (req.params[0] ? ' (old)' : '')); - }); - - request(app) - .get('/user/tj/edit') - .expect('editing tj', cb); - - request(app) - .get('/users/tj/edit') - .expect('editing tj (old)', cb); - }) - - it('should work inside literal parenthesis', function(done){ - var app = express(); - - app.get('/:user\\(:op\\)', function(req, res){ - res.end(req.params.op + 'ing ' + req.params.user); - }); - - request(app) - .get('/tj(edit)') - .expect('editing tj', done); - }) - - it('should work in array of paths', function(done){ - var app = express(); - var cb = after(2, done); - - app.get(['/user/:user/poke', '/user/:user/pokes'], function(req, res){ - res.end('poking ' + req.params.user); - }); - - request(app) - .get('/user/tj/poke') - .expect('poking tj', cb); - - request(app) - .get('/user/tj/pokes') - .expect('poking tj', cb); - }) - }) - - describe(':name?', function(){ - it('should denote an optional capture group', function(done){ - var app = express(); - - app.get('/user/:user/:op?', function(req, res){ - var op = req.params.op || 'view'; - res.end(op + 'ing ' + req.params.user); - }); - - request(app) - .get('/user/tj') - .expect('viewing tj', done); - }) - - it('should populate the capture group', function(done){ - var app = express(); - - app.get('/user/:user/:op?', function(req, res){ - var op = req.params.op || 'view'; - res.end(op + 'ing ' + req.params.user); - }); - - request(app) - .get('/user/tj/edit') - .expect('editing tj', done); - }) - }) - - describe('.:name', function(){ - it('should denote a format', function(done){ - var app = express(); - var cb = after(2, done) - - app.get('/:name.:format', function(req, res){ - res.end(req.params.name + ' as ' + req.params.format); - }); - - request(app) - .get('/foo.json') - .expect(200, 'foo as json', cb) - - request(app) - .get('/foo') - .expect(404, cb) - }) - }) - - describe('.:name?', function(){ - it('should denote an optional format', function(done){ - var app = express(); - var cb = after(2, done) - - app.get('/:name.:format?', function(req, res){ - res.end(req.params.name + ' as ' + (req.params.format || 'html')); - }); - - request(app) - .get('/foo') - .expect(200, 'foo as html', cb) - - request(app) - .get('/foo.json') - .expect(200, 'foo as json', done) - }) - }) - - describe('when next() is called', function(){ - it('should continue lookup', function(done){ - var app = express() - , calls = []; - - app.get('/foo/:bar?', function(req, res, next){ - calls.push('/foo/:bar?'); - next(); - }); - - app.get('/bar', function () { - assert(0); - }); - - app.get('/foo', function(req, res, next){ - calls.push('/foo'); - next(); - }); - - app.get('/foo', function (req, res) { - calls.push('/foo 2'); - res.json(calls) - }); - - request(app) - .get('/foo') - .expect(200, ['/foo/:bar?', '/foo', '/foo 2'], done) - }) - }) - - describe('when next("route") is called', function(){ - it('should jump to next route', function(done){ - var app = express() - - function fn(req, res, next){ - res.set('X-Hit', '1') - next('route') - } - - app.get('/foo', fn, function (req, res) { - res.end('failure') - }); - - app.get('/foo', function(req, res){ - res.end('success') - }) - - request(app) - .get('/foo') - .expect('X-Hit', '1') - .expect(200, 'success', done) - }) - }) - - describe('when next("router") is called', function () { - it('should jump out of router', function (done) { - var app = express() - var router = express.Router() - - function fn (req, res, next) { - res.set('X-Hit', '1') - next('router') - } - - router.get('/foo', fn, function (req, res) { - res.end('failure') - }) - - router.get('/foo', function (req, res) { - res.end('failure') - }) - - app.use(router) - - app.get('/foo', function (req, res) { - res.end('success') - }) - - request(app) - .get('/foo') - .expect('X-Hit', '1') - .expect(200, 'success', done) - }) - }) - - describe('when next(err) is called', function(){ - it('should break out of app.router', function(done){ - var app = express() - , calls = []; - - app.get('/foo/:bar?', function(req, res, next){ - calls.push('/foo/:bar?'); - next(); - }); - - app.get('/bar', function () { - assert(0); - }); - - app.get('/foo', function(req, res, next){ - calls.push('/foo'); - next(new Error('fail')); - }); - - app.get('/foo', function () { - assert(0); - }); - - app.use(function(err, req, res, next){ - res.json({ - calls: calls, - error: err.message - }) - }) - - request(app) - .get('/foo') - .expect(200, { calls: ['/foo/:bar?', '/foo'], error: 'fail' }, done) - }) - - it('should call handler in same route, if exists', function(done){ - var app = express(); - - function fn1(req, res, next) { - next(new Error('boom!')); - } - - function fn2(req, res, next) { - res.send('foo here'); - } - - function fn3(err, req, res, next) { - res.send('route go ' + err.message); - } - - app.get('/foo', fn1, fn2, fn3); - - app.use(function (err, req, res, next) { - res.end('error!'); - }) - - request(app) - .get('/foo') - .expect('route go boom!', done) - }) - }) - - it('should allow rewriting of the url', function(done){ - var app = express(); - - app.get('/account/edit', function(req, res, next){ - req.user = { id: 12 }; // faux authenticated user - req.url = '/user/' + req.user.id + '/edit'; - next(); - }); - - app.get('/user/:id/edit', function(req, res){ - res.send('editing user ' + req.params.id); - }); - - request(app) - .get('/account/edit') - .expect('editing user 12', done); - }) - - it('should run in order added', function(done){ - var app = express(); - var path = []; - - app.get('*', function(req, res, next){ - path.push(0); - next(); - }); - - app.get('/user/:id', function(req, res, next){ - path.push(1); - next(); - }); - - app.use(function(req, res, next){ - path.push(2); - next(); - }); - - app.all('/user/:id', function(req, res, next){ - path.push(3); - next(); - }); - - app.get('*', function(req, res, next){ - path.push(4); - next(); - }); - - app.use(function(req, res, next){ - path.push(5); - res.end(path.join(',')) - }); - - request(app) - .get('/user/1') - .expect(200, '0,1,2,3,4,5', done); - }) - - it('should be chainable', function(){ - var app = express(); - assert.strictEqual(app.get('/', function () {}), app) - }) -}) diff --git a/test/app.routes.error.js b/test/app.routes.error.js deleted file mode 100644 index 56081b31..00000000 --- a/test/app.routes.error.js +++ /dev/null @@ -1,62 +0,0 @@ -'use strict' - -var assert = require('assert') -var express = require('../') - , request = require('supertest'); - -describe('app', function(){ - describe('.VERB()', function(){ - it('should not get invoked without error handler on error', function(done) { - var app = express(); - - app.use(function(req, res, next){ - next(new Error('boom!')) - }); - - app.get('/bar', function(req, res){ - res.send('hello, world!'); - }); - - request(app) - .post('/bar') - .expect(500, /Error: boom!/, done); - }); - - it('should only call an error handling routing callback when an error is propagated', function(done){ - var app = express(); - - var a = false; - var b = false; - var c = false; - var d = false; - - app.get('/', function(req, res, next){ - next(new Error('fabricated error')); - }, function(req, res, next) { - a = true; - next(); - }, function(err, req, res, next){ - b = true; - assert.strictEqual(err.message, 'fabricated error') - next(err); - }, function(err, req, res, next){ - c = true; - assert.strictEqual(err.message, 'fabricated error') - next(); - }, function(err, req, res, next){ - d = true; - next(); - }, function(req, res){ - assert.ok(!a) - assert.ok(b) - assert.ok(c) - assert.ok(!d) - res.send(204); - }); - - request(app) - .get('/') - .expect(204, done); - }) - }) -}) diff --git a/test/app.use.js b/test/app.use.js deleted file mode 100644 index fd9b1751..00000000 --- a/test/app.use.js +++ /dev/null @@ -1,542 +0,0 @@ -'use strict' - -var after = require('after'); -var assert = require('assert') -var express = require('..'); -var request = require('supertest'); - -describe('app', function(){ - it('should emit "mount" when mounted', function(done){ - var blog = express() - , app = express(); - - blog.on('mount', function(arg){ - assert.strictEqual(arg, app) - done(); - }); - - app.use(blog); - }) - - describe('.use(app)', function(){ - it('should mount the app', function(done){ - var blog = express() - , app = express(); - - blog.get('/blog', function(req, res){ - res.end('blog'); - }); - - app.use(blog); - - request(app) - .get('/blog') - .expect('blog', done); - }) - - it('should support mount-points', function(done){ - var blog = express() - , forum = express() - , app = express(); - var cb = after(2, done) - - blog.get('/', function(req, res){ - res.end('blog'); - }); - - forum.get('/', function(req, res){ - res.end('forum'); - }); - - app.use('/blog', blog); - app.use('/forum', forum); - - request(app) - .get('/blog') - .expect(200, 'blog', cb) - - request(app) - .get('/forum') - .expect(200, 'forum', done) - }) - - it('should set the child\'s .parent', function(){ - var blog = express() - , app = express(); - - app.use('/blog', blog); - assert.strictEqual(blog.parent, app) - }) - - it('should support dynamic routes', function(done){ - var blog = express() - , app = express(); - - blog.get('/', function(req, res){ - res.end('success'); - }); - - app.use('/post/:article', blog); - - request(app) - .get('/post/once-upon-a-time') - .expect('success', done); - }) - - it('should support mounted app anywhere', function(done){ - var cb = after(3, done); - var blog = express() - , other = express() - , app = express(); - - function fn1(req, res, next) { - res.setHeader('x-fn-1', 'hit'); - next(); - } - - function fn2(req, res, next) { - res.setHeader('x-fn-2', 'hit'); - next(); - } - - blog.get('/', function(req, res){ - res.end('success'); - }); - - blog.once('mount', function (parent) { - assert.strictEqual(parent, app) - cb(); - }); - other.once('mount', function (parent) { - assert.strictEqual(parent, app) - cb(); - }); - - app.use('/post/:article', fn1, other, fn2, blog); - - request(app) - .get('/post/once-upon-a-time') - .expect('x-fn-1', 'hit') - .expect('x-fn-2', 'hit') - .expect('success', cb); - }) - }) - - describe('.use(middleware)', function(){ - it('should accept multiple arguments', function (done) { - var app = express(); - - function fn1(req, res, next) { - res.setHeader('x-fn-1', 'hit'); - next(); - } - - function fn2(req, res, next) { - res.setHeader('x-fn-2', 'hit'); - next(); - } - - app.use(fn1, fn2, function fn3(req, res) { - res.setHeader('x-fn-3', 'hit'); - res.end(); - }); - - request(app) - .get('/') - .expect('x-fn-1', 'hit') - .expect('x-fn-2', 'hit') - .expect('x-fn-3', 'hit') - .expect(200, done); - }) - - it('should invoke middleware for all requests', function (done) { - var app = express(); - var cb = after(3, done); - - app.use(function (req, res) { - res.send('saw ' + req.method + ' ' + req.url); - }); - - request(app) - .get('/') - .expect(200, 'saw GET /', cb); - - request(app) - .options('/') - .expect(200, 'saw OPTIONS /', cb); - - request(app) - .post('/foo') - .expect(200, 'saw POST /foo', cb); - }) - - it('should accept array of middleware', function (done) { - var app = express(); - - function fn1(req, res, next) { - res.setHeader('x-fn-1', 'hit'); - next(); - } - - function fn2(req, res, next) { - res.setHeader('x-fn-2', 'hit'); - next(); - } - - function fn3(req, res, next) { - res.setHeader('x-fn-3', 'hit'); - res.end(); - } - - app.use([fn1, fn2, fn3]); - - request(app) - .get('/') - .expect('x-fn-1', 'hit') - .expect('x-fn-2', 'hit') - .expect('x-fn-3', 'hit') - .expect(200, done); - }) - - it('should accept multiple arrays of middleware', function (done) { - var app = express(); - - function fn1(req, res, next) { - res.setHeader('x-fn-1', 'hit'); - next(); - } - - function fn2(req, res, next) { - res.setHeader('x-fn-2', 'hit'); - next(); - } - - function fn3(req, res, next) { - res.setHeader('x-fn-3', 'hit'); - res.end(); - } - - app.use([fn1, fn2], [fn3]); - - request(app) - .get('/') - .expect('x-fn-1', 'hit') - .expect('x-fn-2', 'hit') - .expect('x-fn-3', 'hit') - .expect(200, done); - }) - - it('should accept nested arrays of middleware', function (done) { - var app = express(); - - function fn1(req, res, next) { - res.setHeader('x-fn-1', 'hit'); - next(); - } - - function fn2(req, res, next) { - res.setHeader('x-fn-2', 'hit'); - next(); - } - - function fn3(req, res, next) { - res.setHeader('x-fn-3', 'hit'); - res.end(); - } - - app.use([[fn1], fn2], [fn3]); - - request(app) - .get('/') - .expect('x-fn-1', 'hit') - .expect('x-fn-2', 'hit') - .expect('x-fn-3', 'hit') - .expect(200, done); - }) - }) - - describe('.use(path, middleware)', function(){ - it('should require middleware', function () { - var app = express() - assert.throws(function () { app.use('/') }, /requires a middleware function/) - }) - - it('should reject string as middleware', function () { - var app = express() - assert.throws(function () { app.use('/', 'foo') }, /requires a middleware function but got a string/) - }) - - it('should reject number as middleware', function () { - var app = express() - assert.throws(function () { app.use('/', 42) }, /requires a middleware function but got a number/) - }) - - it('should reject null as middleware', function () { - var app = express() - assert.throws(function () { app.use('/', null) }, /requires a middleware function but got a Null/) - }) - - it('should reject Date as middleware', function () { - var app = express() - assert.throws(function () { app.use('/', new Date()) }, /requires a middleware function but got a Date/) - }) - - it('should strip path from req.url', function (done) { - var app = express(); - - app.use('/foo', function (req, res) { - res.send('saw ' + req.method + ' ' + req.url); - }); - - request(app) - .get('/foo/bar') - .expect(200, 'saw GET /bar', done); - }) - - it('should accept multiple arguments', function (done) { - var app = express(); - - function fn1(req, res, next) { - res.setHeader('x-fn-1', 'hit'); - next(); - } - - function fn2(req, res, next) { - res.setHeader('x-fn-2', 'hit'); - next(); - } - - app.use('/foo', fn1, fn2, function fn3(req, res) { - res.setHeader('x-fn-3', 'hit'); - res.end(); - }); - - request(app) - .get('/foo') - .expect('x-fn-1', 'hit') - .expect('x-fn-2', 'hit') - .expect('x-fn-3', 'hit') - .expect(200, done); - }) - - it('should invoke middleware for all requests starting with path', function (done) { - var app = express(); - var cb = after(3, done); - - app.use('/foo', function (req, res) { - res.send('saw ' + req.method + ' ' + req.url); - }); - - request(app) - .get('/') - .expect(404, cb); - - request(app) - .post('/foo') - .expect(200, 'saw POST /', cb); - - request(app) - .post('/foo/bar') - .expect(200, 'saw POST /bar', cb); - }) - - it('should work if path has trailing slash', function (done) { - var app = express(); - var cb = after(3, done); - - app.use('/foo/', function (req, res) { - res.send('saw ' + req.method + ' ' + req.url); - }); - - request(app) - .get('/') - .expect(404, cb); - - request(app) - .post('/foo') - .expect(200, 'saw POST /', cb); - - request(app) - .post('/foo/bar') - .expect(200, 'saw POST /bar', cb); - }) - - it('should accept array of middleware', function (done) { - var app = express(); - - function fn1(req, res, next) { - res.setHeader('x-fn-1', 'hit'); - next(); - } - - function fn2(req, res, next) { - res.setHeader('x-fn-2', 'hit'); - next(); - } - - function fn3(req, res, next) { - res.setHeader('x-fn-3', 'hit'); - res.end(); - } - - app.use('/foo', [fn1, fn2, fn3]); - - request(app) - .get('/foo') - .expect('x-fn-1', 'hit') - .expect('x-fn-2', 'hit') - .expect('x-fn-3', 'hit') - .expect(200, done); - }) - - it('should accept multiple arrays of middleware', function (done) { - var app = express(); - - function fn1(req, res, next) { - res.setHeader('x-fn-1', 'hit'); - next(); - } - - function fn2(req, res, next) { - res.setHeader('x-fn-2', 'hit'); - next(); - } - - function fn3(req, res, next) { - res.setHeader('x-fn-3', 'hit'); - res.end(); - } - - app.use('/foo', [fn1, fn2], [fn3]); - - request(app) - .get('/foo') - .expect('x-fn-1', 'hit') - .expect('x-fn-2', 'hit') - .expect('x-fn-3', 'hit') - .expect(200, done); - }) - - it('should accept nested arrays of middleware', function (done) { - var app = express(); - - function fn1(req, res, next) { - res.setHeader('x-fn-1', 'hit'); - next(); - } - - function fn2(req, res, next) { - res.setHeader('x-fn-2', 'hit'); - next(); - } - - function fn3(req, res, next) { - res.setHeader('x-fn-3', 'hit'); - res.end(); - } - - app.use('/foo', [fn1, [fn2]], [fn3]); - - request(app) - .get('/foo') - .expect('x-fn-1', 'hit') - .expect('x-fn-2', 'hit') - .expect('x-fn-3', 'hit') - .expect(200, done); - }) - - it('should support array of paths', function (done) { - var app = express(); - var cb = after(3, done); - - app.use(['/foo/', '/bar'], function (req, res) { - res.send('saw ' + req.method + ' ' + req.url + ' through ' + req.originalUrl); - }); - - request(app) - .get('/') - .expect(404, cb); - - request(app) - .get('/foo') - .expect(200, 'saw GET / through /foo', cb); - - request(app) - .get('/bar') - .expect(200, 'saw GET / through /bar', cb); - }) - - it('should support array of paths with middleware array', function (done) { - var app = express(); - var cb = after(2, done); - - function fn1(req, res, next) { - res.setHeader('x-fn-1', 'hit'); - next(); - } - - function fn2(req, res, next) { - res.setHeader('x-fn-2', 'hit'); - next(); - } - - function fn3(req, res, next) { - res.setHeader('x-fn-3', 'hit'); - res.send('saw ' + req.method + ' ' + req.url + ' through ' + req.originalUrl); - } - - app.use(['/foo/', '/bar'], [[fn1], fn2], [fn3]); - - request(app) - .get('/foo') - .expect('x-fn-1', 'hit') - .expect('x-fn-2', 'hit') - .expect('x-fn-3', 'hit') - .expect(200, 'saw GET / through /foo', cb); - - request(app) - .get('/bar') - .expect('x-fn-1', 'hit') - .expect('x-fn-2', 'hit') - .expect('x-fn-3', 'hit') - .expect(200, 'saw GET / through /bar', cb); - }) - - it('should support regexp path', function (done) { - var app = express(); - var cb = after(4, done); - - app.use(/^\/[a-z]oo/, function (req, res) { - res.send('saw ' + req.method + ' ' + req.url + ' through ' + req.originalUrl); - }); - - request(app) - .get('/') - .expect(404, cb); - - request(app) - .get('/foo') - .expect(200, 'saw GET / through /foo', cb); - - request(app) - .get('/zoo/bear') - .expect(200, 'saw GET /bear through /zoo/bear', cb); - - request(app) - .get('/get/zoo') - .expect(404, cb); - }) - - it('should support empty string path', function (done) { - var app = express(); - - app.use('', function (req, res) { - res.send('saw ' + req.method + ' ' + req.url + ' through ' + req.originalUrl); - }); - - request(app) - .get('/') - .expect(200, 'saw GET / through /', done); - }) - }) -}) diff --git a/test/config.js b/test/config.js deleted file mode 100644 index b04367fd..00000000 --- a/test/config.js +++ /dev/null @@ -1,207 +0,0 @@ -'use strict' - -var assert = require('assert'); -var express = require('..'); - -describe('config', function () { - describe('.set()', function () { - it('should set a value', function () { - var app = express(); - app.set('foo', 'bar'); - assert.equal(app.get('foo'), 'bar'); - }) - - it('should set prototype values', function () { - var app = express() - app.set('hasOwnProperty', 42) - assert.strictEqual(app.get('hasOwnProperty'), 42) - }) - - it('should return the app', function () { - var app = express(); - assert.equal(app.set('foo', 'bar'), app); - }) - - it('should return the app when undefined', function () { - var app = express(); - assert.equal(app.set('foo', undefined), app); - }) - - it('should return set value', function () { - var app = express() - app.set('foo', 'bar') - assert.strictEqual(app.set('foo'), 'bar') - }) - - it('should return undefined for prototype values', function () { - var app = express() - assert.strictEqual(app.set('hasOwnProperty'), undefined) - }) - - describe('"etag"', function(){ - it('should throw on bad value', function(){ - var app = express(); - assert.throws(app.set.bind(app, 'etag', 42), /unknown value/); - }) - - it('should set "etag fn"', function(){ - var app = express() - var fn = function(){} - app.set('etag', fn) - assert.equal(app.get('etag fn'), fn) - }) - }) - - describe('"trust proxy"', function(){ - it('should set "trust proxy fn"', function(){ - var app = express() - var fn = function(){} - app.set('trust proxy', fn) - assert.equal(app.get('trust proxy fn'), fn) - }) - }) - }) - - describe('.get()', function(){ - it('should return undefined when unset', function(){ - var app = express(); - assert.strictEqual(app.get('foo'), undefined); - }) - - it('should return undefined for prototype values', function () { - var app = express() - assert.strictEqual(app.get('hasOwnProperty'), undefined) - }) - - it('should otherwise return the value', function(){ - var app = express(); - app.set('foo', 'bar'); - assert.equal(app.get('foo'), 'bar'); - }) - - describe('when mounted', function(){ - it('should default to the parent app', function(){ - var app = express(); - var blog = express(); - - app.set('title', 'Express'); - app.use(blog); - assert.equal(blog.get('title'), 'Express'); - }) - - it('should given precedence to the child', function(){ - var app = express(); - var blog = express(); - - app.use(blog); - app.set('title', 'Express'); - blog.set('title', 'Some Blog'); - - assert.equal(blog.get('title'), 'Some Blog'); - }) - - it('should inherit "trust proxy" setting', function () { - var app = express(); - var blog = express(); - - function fn() { return false } - - app.set('trust proxy', fn); - assert.equal(app.get('trust proxy'), fn); - assert.equal(app.get('trust proxy fn'), fn); - - app.use(blog); - - assert.equal(blog.get('trust proxy'), fn); - assert.equal(blog.get('trust proxy fn'), fn); - }) - - it('should prefer child "trust proxy" setting', function () { - var app = express(); - var blog = express(); - - function fn1() { return false } - function fn2() { return true } - - app.set('trust proxy', fn1); - assert.equal(app.get('trust proxy'), fn1); - assert.equal(app.get('trust proxy fn'), fn1); - - blog.set('trust proxy', fn2); - assert.equal(blog.get('trust proxy'), fn2); - assert.equal(blog.get('trust proxy fn'), fn2); - - app.use(blog); - - assert.equal(app.get('trust proxy'), fn1); - assert.equal(app.get('trust proxy fn'), fn1); - assert.equal(blog.get('trust proxy'), fn2); - assert.equal(blog.get('trust proxy fn'), fn2); - }) - }) - }) - - describe('.enable()', function(){ - it('should set the value to true', function(){ - var app = express(); - assert.equal(app.enable('tobi'), app); - assert.strictEqual(app.get('tobi'), true); - }) - - it('should set prototype values', function () { - var app = express() - app.enable('hasOwnProperty') - assert.strictEqual(app.get('hasOwnProperty'), true) - }) - }) - - describe('.disable()', function(){ - it('should set the value to false', function(){ - var app = express(); - assert.equal(app.disable('tobi'), app); - assert.strictEqual(app.get('tobi'), false); - }) - - it('should set prototype values', function () { - var app = express() - app.disable('hasOwnProperty') - assert.strictEqual(app.get('hasOwnProperty'), false) - }) - }) - - describe('.enabled()', function(){ - it('should default to false', function(){ - var app = express(); - assert.strictEqual(app.enabled('foo'), false); - }) - - it('should return true when set', function(){ - var app = express(); - app.set('foo', 'bar'); - assert.strictEqual(app.enabled('foo'), true); - }) - - it('should default to false for prototype values', function () { - var app = express() - assert.strictEqual(app.enabled('hasOwnProperty'), false) - }) - }) - - describe('.disabled()', function(){ - it('should default to true', function(){ - var app = express(); - assert.strictEqual(app.disabled('foo'), true); - }) - - it('should return false when set', function(){ - var app = express(); - app.set('foo', 'bar'); - assert.strictEqual(app.disabled('foo'), false); - }) - - it('should default to true for prototype values', function () { - var app = express() - assert.strictEqual(app.disabled('hasOwnProperty'), true) - }) - }) -}) diff --git a/test/exports.js b/test/exports.js deleted file mode 100644 index 5ab0f885..00000000 --- a/test/exports.js +++ /dev/null @@ -1,87 +0,0 @@ -'use strict' - -var assert = require('assert') -var express = require('../'); -var request = require('supertest'); - -describe('exports', function(){ - it('should expose Router', function(){ - assert.strictEqual(typeof express.Router, 'function') - }) - - it('should expose json middleware', function () { - assert.equal(typeof express.json, 'function') - assert.equal(express.json.length, 1) - }) - - it('should expose raw middleware', function () { - assert.equal(typeof express.raw, 'function') - assert.equal(express.raw.length, 1) - }) - - it('should expose static middleware', function () { - assert.equal(typeof express.static, 'function') - assert.equal(express.static.length, 2) - }) - - it('should expose text middleware', function () { - assert.equal(typeof express.text, 'function') - assert.equal(express.text.length, 1) - }) - - it('should expose urlencoded middleware', function () { - assert.equal(typeof express.urlencoded, 'function') - assert.equal(express.urlencoded.length, 1) - }) - - it('should expose the application prototype', function(){ - assert.strictEqual(typeof express.application, 'object') - assert.strictEqual(typeof express.application.set, 'function') - }) - - it('should expose the request prototype', function(){ - assert.strictEqual(typeof express.request, 'object') - assert.strictEqual(typeof express.request.accepts, 'function') - }) - - it('should expose the response prototype', function(){ - assert.strictEqual(typeof express.response, 'object') - assert.strictEqual(typeof express.response.send, 'function') - }) - - it('should permit modifying the .application prototype', function(){ - express.application.foo = function(){ return 'bar'; }; - assert.strictEqual(express().foo(), 'bar') - }) - - it('should permit modifying the .request prototype', function(done){ - express.request.foo = function(){ return 'bar'; }; - var app = express(); - - app.use(function(req, res, next){ - res.end(req.foo()); - }); - - request(app) - .get('/') - .expect('bar', done); - }) - - it('should permit modifying the .response prototype', function(done){ - express.response.foo = function(){ this.send('bar'); }; - var app = express(); - - app.use(function(req, res, next){ - res.foo(); - }); - - request(app) - .get('/') - .expect('bar', done); - }) - - it('should throw on old middlewares', function(){ - assert.throws(function () { express.bodyParser() }, /Error:.*middleware.*bodyParser/) - assert.throws(function () { express.limit() }, /Error:.*middleware.*limit/) - }) -}) diff --git a/test/express.json.js b/test/express.json.js deleted file mode 100644 index f6f536b1..00000000 --- a/test/express.json.js +++ /dev/null @@ -1,790 +0,0 @@ -'use strict' - -var assert = require('assert') -var asyncHooks = tryRequire('async_hooks') -var Buffer = require('safe-buffer').Buffer -var express = require('..') -var request = require('supertest') - -var describeAsyncHooks = typeof asyncHooks.AsyncLocalStorage === 'function' - ? describe - : describe.skip - -describe('express.json()', function () { - it('should parse JSON', function (done) { - request(createApp()) - .post('/') - .set('Content-Type', 'application/json') - .send('{"user":"tobi"}') - .expect(200, '{"user":"tobi"}', done) - }) - - it('should handle Content-Length: 0', function (done) { - request(createApp()) - .post('/') - .set('Content-Type', 'application/json') - .set('Content-Length', '0') - .expect(200, '{}', done) - }) - - it('should handle empty message-body', function (done) { - request(createApp()) - .post('/') - .set('Content-Type', 'application/json') - .set('Transfer-Encoding', 'chunked') - .expect(200, '{}', done) - }) - - it('should handle no message-body', function (done) { - request(createApp()) - .post('/') - .set('Content-Type', 'application/json') - .unset('Transfer-Encoding') - .expect(200, '{}', done) - }) - - it('should 400 when only whitespace', function (done) { - request(createApp()) - .post('/') - .set('Content-Type', 'application/json') - .send(' \n') - .expect(400, '[entity.parse.failed] ' + parseError(' '), done) - }) - - it('should 400 when invalid content-length', function (done) { - var app = express() - - app.use(function (req, res, next) { - req.headers['content-length'] = '20' // bad length - next() - }) - - app.use(express.json()) - - app.post('/', function (req, res) { - res.json(req.body) - }) - - request(app) - .post('/') - .set('Content-Type', 'application/json') - .send('{"str":') - .expect(400, /content length/, done) - }) - - it('should 500 if stream not readable', function (done) { - var app = express() - - app.use(function (req, res, next) { - req.on('end', next) - req.resume() - }) - - app.use(express.json()) - - app.use(function (err, req, res, next) { - res.status(err.status || 500) - res.send('[' + err.type + '] ' + err.message) - }) - - app.post('/', function (req, res) { - res.json(req.body) - }) - - request(app) - .post('/') - .set('Content-Type', 'application/json') - .send('{"user":"tobi"}') - .expect(500, '[stream.not.readable] stream is not readable', done) - }) - - it('should handle duplicated middleware', function (done) { - var app = express() - - app.use(express.json()) - app.use(express.json()) - - app.post('/', function (req, res) { - res.json(req.body) - }) - - request(app) - .post('/') - .set('Content-Type', 'application/json') - .send('{"user":"tobi"}') - .expect(200, '{"user":"tobi"}', done) - }) - - describe('when JSON is invalid', function () { - before(function () { - this.app = createApp() - }) - - it('should 400 for bad token', function (done) { - request(this.app) - .post('/') - .set('Content-Type', 'application/json') - .send('{:') - .expect(400, '[entity.parse.failed] ' + parseError('{:'), done) - }) - - it('should 400 for incomplete', function (done) { - request(this.app) - .post('/') - .set('Content-Type', 'application/json') - .send('{"user"') - .expect(400, '[entity.parse.failed] ' + parseError('{"user"'), done) - }) - - it('should include original body on error object', function (done) { - request(this.app) - .post('/') - .set('Content-Type', 'application/json') - .set('X-Error-Property', 'body') - .send(' {"user"') - .expect(400, ' {"user"', done) - }) - }) - - describe('with limit option', function () { - it('should 413 when over limit with Content-Length', function (done) { - var buf = Buffer.alloc(1024, '.') - request(createApp({ limit: '1kb' })) - .post('/') - .set('Content-Type', 'application/json') - .set('Content-Length', '1034') - .send(JSON.stringify({ str: buf.toString() })) - .expect(413, '[entity.too.large] request entity too large', done) - }) - - it('should 413 when over limit with chunked encoding', function (done) { - var app = createApp({ limit: '1kb' }) - var buf = Buffer.alloc(1024, '.') - var test = request(app).post('/') - test.set('Content-Type', 'application/json') - test.set('Transfer-Encoding', 'chunked') - test.write('{"str":') - test.write('"' + buf.toString() + '"}') - test.expect(413, done) - }) - - it('should 413 when inflated body over limit', function (done) { - var app = createApp({ limit: '1kb' }) - var test = request(app).post('/') - test.set('Content-Encoding', 'gzip') - test.set('Content-Type', 'application/json') - test.write(Buffer.from('1f8b080000000000000aab562a2e2952b252d21b05a360148c58a0540b0066f7ce1e0a040000', 'hex')) - test.expect(413, done) - }) - - it('should accept number of bytes', function (done) { - var buf = Buffer.alloc(1024, '.') - request(createApp({ limit: 1024 })) - .post('/') - .set('Content-Type', 'application/json') - .send(JSON.stringify({ str: buf.toString() })) - .expect(413, done) - }) - - it('should not change when options altered', function (done) { - var buf = Buffer.alloc(1024, '.') - var options = { limit: '1kb' } - var app = createApp(options) - - options.limit = '100kb' - - request(app) - .post('/') - .set('Content-Type', 'application/json') - .send(JSON.stringify({ str: buf.toString() })) - .expect(413, done) - }) - - it('should not hang response', function (done) { - var buf = Buffer.alloc(10240, '.') - var app = createApp({ limit: '8kb' }) - var test = request(app).post('/') - test.set('Content-Type', 'application/json') - test.write(buf) - test.write(buf) - test.write(buf) - test.expect(413, done) - }) - - it('should not error when inflating', function (done) { - var app = createApp({ limit: '1kb' }) - var test = request(app).post('/') - test.set('Content-Encoding', 'gzip') - test.set('Content-Type', 'application/json') - test.write(Buffer.from('1f8b080000000000000aab562a2e2952b252d21b05a360148c58a0540b0066f7ce1e0a0400', 'hex')) - test.expect(413, done) - }) - }) - - describe('with inflate option', function () { - describe('when false', function () { - before(function () { - this.app = createApp({ inflate: false }) - }) - - it('should not accept content-encoding', function (done) { - var test = request(this.app).post('/') - test.set('Content-Encoding', 'gzip') - test.set('Content-Type', 'application/json') - test.write(Buffer.from('1f8b080000000000000bab56ca4bcc4d55b2527ab16e97522d00515be1cc0e000000', 'hex')) - test.expect(415, '[encoding.unsupported] content encoding unsupported', done) - }) - }) - - describe('when true', function () { - before(function () { - this.app = createApp({ inflate: true }) - }) - - it('should accept content-encoding', function (done) { - var test = request(this.app).post('/') - test.set('Content-Encoding', 'gzip') - test.set('Content-Type', 'application/json') - test.write(Buffer.from('1f8b080000000000000bab56ca4bcc4d55b2527ab16e97522d00515be1cc0e000000', 'hex')) - test.expect(200, '{"name":"论"}', done) - }) - }) - }) - - describe('with strict option', function () { - describe('when undefined', function () { - before(function () { - this.app = createApp() - }) - - it('should 400 on primitives', function (done) { - request(this.app) - .post('/') - .set('Content-Type', 'application/json') - .send('true') - .expect(400, '[entity.parse.failed] ' + parseError('#rue').replace(/#/g, 't'), done) - }) - }) - - describe('when false', function () { - before(function () { - this.app = createApp({ strict: false }) - }) - - it('should parse primitives', function (done) { - request(this.app) - .post('/') - .set('Content-Type', 'application/json') - .send('true') - .expect(200, 'true', done) - }) - }) - - describe('when true', function () { - before(function () { - this.app = createApp({ strict: true }) - }) - - it('should not parse primitives', function (done) { - request(this.app) - .post('/') - .set('Content-Type', 'application/json') - .send('true') - .expect(400, '[entity.parse.failed] ' + parseError('#rue').replace(/#/g, 't'), done) - }) - - it('should not parse primitives with leading whitespaces', function (done) { - request(this.app) - .post('/') - .set('Content-Type', 'application/json') - .send(' true') - .expect(400, '[entity.parse.failed] ' + parseError(' #rue').replace(/#/g, 't'), done) - }) - - it('should allow leading whitespaces in JSON', function (done) { - request(this.app) - .post('/') - .set('Content-Type', 'application/json') - .send(' { "user": "tobi" }') - .expect(200, '{"user":"tobi"}', done) - }) - - it('should include correct message in stack trace', function (done) { - request(this.app) - .post('/') - .set('Content-Type', 'application/json') - .set('X-Error-Property', 'stack') - .send('true') - .expect(400) - .expect(shouldContainInBody(parseError('#rue').replace(/#/g, 't'))) - .end(done) - }) - }) - }) - - describe('with type option', function () { - describe('when "application/vnd.api+json"', function () { - before(function () { - this.app = createApp({ type: 'application/vnd.api+json' }) - }) - - it('should parse JSON for custom type', function (done) { - request(this.app) - .post('/') - .set('Content-Type', 'application/vnd.api+json') - .send('{"user":"tobi"}') - .expect(200, '{"user":"tobi"}', done) - }) - - it('should ignore standard type', function (done) { - request(this.app) - .post('/') - .set('Content-Type', 'application/json') - .send('{"user":"tobi"}') - .expect(200, '{}', done) - }) - }) - - describe('when ["application/json", "application/vnd.api+json"]', function () { - before(function () { - this.app = createApp({ - type: ['application/json', 'application/vnd.api+json'] - }) - }) - - it('should parse JSON for "application/json"', function (done) { - request(this.app) - .post('/') - .set('Content-Type', 'application/json') - .send('{"user":"tobi"}') - .expect(200, '{"user":"tobi"}', done) - }) - - it('should parse JSON for "application/vnd.api+json"', function (done) { - request(this.app) - .post('/') - .set('Content-Type', 'application/vnd.api+json') - .send('{"user":"tobi"}') - .expect(200, '{"user":"tobi"}', done) - }) - - it('should ignore "application/x-json"', function (done) { - request(this.app) - .post('/') - .set('Content-Type', 'application/x-json') - .send('{"user":"tobi"}') - .expect(200, '{}', done) - }) - }) - - describe('when a function', function () { - it('should parse when truthy value returned', function (done) { - var app = createApp({ type: accept }) - - function accept (req) { - return req.headers['content-type'] === 'application/vnd.api+json' - } - - request(app) - .post('/') - .set('Content-Type', 'application/vnd.api+json') - .send('{"user":"tobi"}') - .expect(200, '{"user":"tobi"}', done) - }) - - it('should work without content-type', function (done) { - var app = createApp({ type: accept }) - - function accept (req) { - return true - } - - var test = request(app).post('/') - test.write('{"user":"tobi"}') - test.expect(200, '{"user":"tobi"}', done) - }) - - it('should not invoke without a body', function (done) { - var app = createApp({ type: accept }) - - function accept (req) { - throw new Error('oops!') - } - - request(app) - .get('/') - .expect(404, done) - }) - }) - }) - - describe('with verify option', function () { - it('should assert value if function', function () { - assert.throws(createApp.bind(null, { verify: 'lol' }), - /TypeError: option verify must be function/) - }) - - it('should error from verify', function (done) { - var app = createApp({ - verify: function (req, res, buf) { - if (buf[0] === 0x5b) throw new Error('no arrays') - } - }) - - request(app) - .post('/') - .set('Content-Type', 'application/json') - .send('["tobi"]') - .expect(403, '[entity.verify.failed] no arrays', done) - }) - - it('should allow custom codes', function (done) { - var app = createApp({ - verify: function (req, res, buf) { - if (buf[0] !== 0x5b) return - var err = new Error('no arrays') - err.status = 400 - throw err - } - }) - - request(app) - .post('/') - .set('Content-Type', 'application/json') - .send('["tobi"]') - .expect(400, '[entity.verify.failed] no arrays', done) - }) - - it('should allow custom type', function (done) { - var app = createApp({ - verify: function (req, res, buf) { - if (buf[0] !== 0x5b) return - var err = new Error('no arrays') - err.type = 'foo.bar' - throw err - } - }) - - request(app) - .post('/') - .set('Content-Type', 'application/json') - .send('["tobi"]') - .expect(403, '[foo.bar] no arrays', done) - }) - - it('should include original body on error object', function (done) { - var app = createApp({ - verify: function (req, res, buf) { - if (buf[0] === 0x5b) throw new Error('no arrays') - } - }) - - request(app) - .post('/') - .set('Content-Type', 'application/json') - .set('X-Error-Property', 'body') - .send('["tobi"]') - .expect(403, '["tobi"]', done) - }) - - it('should allow pass-through', function (done) { - var app = createApp({ - verify: function (req, res, buf) { - if (buf[0] === 0x5b) throw new Error('no arrays') - } - }) - - request(app) - .post('/') - .set('Content-Type', 'application/json') - .send('{"user":"tobi"}') - .expect(200, '{"user":"tobi"}', done) - }) - - it('should work with different charsets', function (done) { - var app = createApp({ - verify: function (req, res, buf) { - if (buf[0] === 0x5b) throw new Error('no arrays') - } - }) - - var test = request(app).post('/') - test.set('Content-Type', 'application/json; charset=utf-16') - test.write(Buffer.from('feff007b0022006e0061006d00650022003a00228bba0022007d', 'hex')) - test.expect(200, '{"name":"论"}', done) - }) - - it('should 415 on unknown charset prior to verify', function (done) { - var app = createApp({ - verify: function (req, res, buf) { - throw new Error('unexpected verify call') - } - }) - - var test = request(app).post('/') - test.set('Content-Type', 'application/json; charset=x-bogus') - test.write(Buffer.from('00000000', 'hex')) - test.expect(415, '[charset.unsupported] unsupported charset "X-BOGUS"', done) - }) - }) - - describeAsyncHooks('async local storage', function () { - before(function () { - var app = express() - var store = { foo: 'bar' } - - app.use(function (req, res, next) { - req.asyncLocalStorage = new asyncHooks.AsyncLocalStorage() - req.asyncLocalStorage.run(store, next) - }) - - app.use(express.json()) - - app.use(function (req, res, next) { - var local = req.asyncLocalStorage.getStore() - - if (local) { - res.setHeader('x-store-foo', String(local.foo)) - } - - next() - }) - - app.use(function (err, req, res, next) { - var local = req.asyncLocalStorage.getStore() - - if (local) { - res.setHeader('x-store-foo', String(local.foo)) - } - - res.status(err.status || 500) - res.send('[' + err.type + '] ' + err.message) - }) - - app.post('/', function (req, res) { - res.json(req.body) - }) - - this.app = app - }) - - it('should presist store', function (done) { - request(this.app) - .post('/') - .set('Content-Type', 'application/json') - .send('{"user":"tobi"}') - .expect(200) - .expect('x-store-foo', 'bar') - .expect('{"user":"tobi"}') - .end(done) - }) - - it('should presist store when unmatched content-type', function (done) { - request(this.app) - .post('/') - .set('Content-Type', 'application/fizzbuzz') - .send('buzz') - .expect(200) - .expect('x-store-foo', 'bar') - .expect('{}') - .end(done) - }) - - it('should presist store when inflated', function (done) { - var test = request(this.app).post('/') - test.set('Content-Encoding', 'gzip') - test.set('Content-Type', 'application/json') - test.write(Buffer.from('1f8b080000000000000bab56ca4bcc4d55b2527ab16e97522d00515be1cc0e000000', 'hex')) - test.expect(200) - test.expect('x-store-foo', 'bar') - test.expect('{"name":"论"}') - test.end(done) - }) - - it('should presist store when inflate error', function (done) { - var test = request(this.app).post('/') - test.set('Content-Encoding', 'gzip') - test.set('Content-Type', 'application/json') - test.write(Buffer.from('1f8b080000000000000bab56cc4d55b2527ab16e97522d00515be1cc0e000000', 'hex')) - test.expect(400) - test.expect('x-store-foo', 'bar') - test.end(done) - }) - - it('should presist store when parse error', function (done) { - request(this.app) - .post('/') - .set('Content-Type', 'application/json') - .send('{"user":') - .expect(400) - .expect('x-store-foo', 'bar') - .end(done) - }) - - it('should presist store when limit exceeded', function (done) { - request(this.app) - .post('/') - .set('Content-Type', 'application/json') - .send('{"user":"' + Buffer.alloc(1024 * 100, '.').toString() + '"}') - .expect(413) - .expect('x-store-foo', 'bar') - .end(done) - }) - }) - - describe('charset', function () { - before(function () { - this.app = createApp() - }) - - it('should parse utf-8', function (done) { - var test = request(this.app).post('/') - test.set('Content-Type', 'application/json; charset=utf-8') - test.write(Buffer.from('7b226e616d65223a22e8aeba227d', 'hex')) - test.expect(200, '{"name":"论"}', done) - }) - - it('should parse utf-16', function (done) { - var test = request(this.app).post('/') - test.set('Content-Type', 'application/json; charset=utf-16') - test.write(Buffer.from('feff007b0022006e0061006d00650022003a00228bba0022007d', 'hex')) - test.expect(200, '{"name":"论"}', done) - }) - - it('should parse when content-length != char length', function (done) { - var test = request(this.app).post('/') - test.set('Content-Type', 'application/json; charset=utf-8') - test.set('Content-Length', '13') - test.write(Buffer.from('7b2274657374223a22c3a5227d', 'hex')) - test.expect(200, '{"test":"å"}', done) - }) - - it('should default to utf-8', function (done) { - var test = request(this.app).post('/') - test.set('Content-Type', 'application/json') - test.write(Buffer.from('7b226e616d65223a22e8aeba227d', 'hex')) - test.expect(200, '{"name":"论"}', done) - }) - - it('should fail on unknown charset', function (done) { - var test = request(this.app).post('/') - test.set('Content-Type', 'application/json; charset=koi8-r') - test.write(Buffer.from('7b226e616d65223a22cec5d4227d', 'hex')) - test.expect(415, '[charset.unsupported] unsupported charset "KOI8-R"', done) - }) - }) - - describe('encoding', function () { - before(function () { - this.app = createApp({ limit: '1kb' }) - }) - - it('should parse without encoding', function (done) { - var test = request(this.app).post('/') - test.set('Content-Type', 'application/json') - test.write(Buffer.from('7b226e616d65223a22e8aeba227d', 'hex')) - test.expect(200, '{"name":"论"}', done) - }) - - it('should support identity encoding', function (done) { - var test = request(this.app).post('/') - test.set('Content-Encoding', 'identity') - test.set('Content-Type', 'application/json') - test.write(Buffer.from('7b226e616d65223a22e8aeba227d', 'hex')) - test.expect(200, '{"name":"论"}', done) - }) - - it('should support gzip encoding', function (done) { - var test = request(this.app).post('/') - test.set('Content-Encoding', 'gzip') - test.set('Content-Type', 'application/json') - test.write(Buffer.from('1f8b080000000000000bab56ca4bcc4d55b2527ab16e97522d00515be1cc0e000000', 'hex')) - test.expect(200, '{"name":"论"}', done) - }) - - it('should support deflate encoding', function (done) { - var test = request(this.app).post('/') - test.set('Content-Encoding', 'deflate') - test.set('Content-Type', 'application/json') - test.write(Buffer.from('789cab56ca4bcc4d55b2527ab16e97522d00274505ac', 'hex')) - test.expect(200, '{"name":"论"}', done) - }) - - it('should be case-insensitive', function (done) { - var test = request(this.app).post('/') - test.set('Content-Encoding', 'GZIP') - test.set('Content-Type', 'application/json') - test.write(Buffer.from('1f8b080000000000000bab56ca4bcc4d55b2527ab16e97522d00515be1cc0e000000', 'hex')) - test.expect(200, '{"name":"论"}', done) - }) - - it('should 415 on unknown encoding', function (done) { - var test = request(this.app).post('/') - test.set('Content-Encoding', 'nulls') - test.set('Content-Type', 'application/json') - test.write(Buffer.from('000000000000', 'hex')) - test.expect(415, '[encoding.unsupported] unsupported content encoding "nulls"', done) - }) - - it('should 400 on malformed encoding', function (done) { - var test = request(this.app).post('/') - test.set('Content-Encoding', 'gzip') - test.set('Content-Type', 'application/json') - test.write(Buffer.from('1f8b080000000000000bab56cc4d55b2527ab16e97522d00515be1cc0e000000', 'hex')) - test.expect(400, done) - }) - - it('should 413 when inflated value exceeds limit', function (done) { - // gzip'd data exceeds 1kb, but deflated below 1kb - var test = request(this.app).post('/') - test.set('Content-Encoding', 'gzip') - test.set('Content-Type', 'application/json') - test.write(Buffer.from('1f8b080000000000000bedc1010d000000c2a0f74f6d0f071400000000000000', 'hex')) - test.write(Buffer.from('0000000000000000000000000000000000000000000000000000000000000000', 'hex')) - test.write(Buffer.from('0000000000000000004f0625b3b71650c30000', 'hex')) - test.expect(413, done) - }) - }) -}) - -function createApp (options) { - var app = express() - - app.use(express.json(options)) - - app.use(function (err, req, res, next) { - res.status(err.status || 500) - res.send(String(req.headers['x-error-property'] - ? err[req.headers['x-error-property']] - : ('[' + err.type + '] ' + err.message))) - }) - - app.post('/', function (req, res) { - res.json(req.body) - }) - - return app -} - -function parseError (str) { - try { - JSON.parse(str); throw new SyntaxError('strict violation') - } catch (e) { - return e.message - } -} - -function shouldContainInBody (str) { - return function (res) { - assert.ok(res.text.indexOf(str) !== -1, - 'expected \'' + res.text + '\' to contain \'' + str + '\'') - } -} - -function tryRequire (name) { - try { - return require(name) - } catch (e) { - return {} - } -} diff --git a/test/express.raw.js b/test/express.raw.js deleted file mode 100644 index 4aa62bb8..00000000 --- a/test/express.raw.js +++ /dev/null @@ -1,555 +0,0 @@ -'use strict' - -var assert = require('assert') -var asyncHooks = tryRequire('async_hooks') -var Buffer = require('safe-buffer').Buffer -var express = require('..') -var request = require('supertest') - -var describeAsyncHooks = typeof asyncHooks.AsyncLocalStorage === 'function' - ? describe - : describe.skip - -describe('express.raw()', function () { - before(function () { - this.app = createApp() - }) - - it('should parse application/octet-stream', function (done) { - request(this.app) - .post('/') - .set('Content-Type', 'application/octet-stream') - .send('the user is tobi') - .expect(200, { buf: '746865207573657220697320746f6269' }, done) - }) - - it('should 400 when invalid content-length', function (done) { - var app = express() - - app.use(function (req, res, next) { - req.headers['content-length'] = '20' // bad length - next() - }) - - app.use(express.raw()) - - app.post('/', function (req, res) { - if (Buffer.isBuffer(req.body)) { - res.json({ buf: req.body.toString('hex') }) - } else { - res.json(req.body) - } - }) - - request(app) - .post('/') - .set('Content-Type', 'application/octet-stream') - .send('stuff') - .expect(400, /content length/, done) - }) - - it('should handle Content-Length: 0', function (done) { - request(this.app) - .post('/') - .set('Content-Type', 'application/octet-stream') - .set('Content-Length', '0') - .expect(200, { buf: '' }, done) - }) - - it('should handle empty message-body', function (done) { - request(this.app) - .post('/') - .set('Content-Type', 'application/octet-stream') - .set('Transfer-Encoding', 'chunked') - .send('') - .expect(200, { buf: '' }, done) - }) - - it('should 500 if stream not readable', function (done) { - var app = express() - - app.use(function (req, res, next) { - req.on('end', next) - req.resume() - }) - - app.use(express.raw()) - - app.use(function (err, req, res, next) { - res.status(err.status || 500) - res.send('[' + err.type + '] ' + err.message) - }) - - app.post('/', function (req, res) { - if (Buffer.isBuffer(req.body)) { - res.json({ buf: req.body.toString('hex') }) - } else { - res.json(req.body) - } - }) - - request(app) - .post('/') - .set('Content-Type', 'application/octet-stream') - .send('the user is tobi') - .expect(500, '[stream.not.readable] stream is not readable', done) - }) - - it('should handle duplicated middleware', function (done) { - var app = express() - - app.use(express.raw()) - app.use(express.raw()) - - app.post('/', function (req, res) { - if (Buffer.isBuffer(req.body)) { - res.json({ buf: req.body.toString('hex') }) - } else { - res.json(req.body) - } - }) - - request(app) - .post('/') - .set('Content-Type', 'application/octet-stream') - .send('the user is tobi') - .expect(200, { buf: '746865207573657220697320746f6269' }, done) - }) - - describe('with limit option', function () { - it('should 413 when over limit with Content-Length', function (done) { - var buf = Buffer.alloc(1028, '.') - var app = createApp({ limit: '1kb' }) - var test = request(app).post('/') - test.set('Content-Type', 'application/octet-stream') - test.set('Content-Length', '1028') - test.write(buf) - test.expect(413, done) - }) - - it('should 413 when over limit with chunked encoding', function (done) { - var buf = Buffer.alloc(1028, '.') - var app = createApp({ limit: '1kb' }) - var test = request(app).post('/') - test.set('Content-Type', 'application/octet-stream') - test.set('Transfer-Encoding', 'chunked') - test.write(buf) - test.expect(413, done) - }) - - it('should 413 when inflated body over limit', function (done) { - var app = createApp({ limit: '1kb' }) - var test = request(app).post('/') - test.set('Content-Encoding', 'gzip') - test.set('Content-Type', 'application/octet-stream') - test.write(Buffer.from('1f8b080000000000000ad3d31b05a360148c64000087e5a14704040000', 'hex')) - test.expect(413, done) - }) - - it('should accept number of bytes', function (done) { - var buf = Buffer.alloc(1028, '.') - var app = createApp({ limit: 1024 }) - var test = request(app).post('/') - test.set('Content-Type', 'application/octet-stream') - test.write(buf) - test.expect(413, done) - }) - - it('should not change when options altered', function (done) { - var buf = Buffer.alloc(1028, '.') - var options = { limit: '1kb' } - var app = createApp(options) - - options.limit = '100kb' - - var test = request(app).post('/') - test.set('Content-Type', 'application/octet-stream') - test.write(buf) - test.expect(413, done) - }) - - it('should not hang response', function (done) { - var buf = Buffer.alloc(10240, '.') - var app = createApp({ limit: '8kb' }) - var test = request(app).post('/') - test.set('Content-Type', 'application/octet-stream') - test.write(buf) - test.write(buf) - test.write(buf) - test.expect(413, done) - }) - - it('should not error when inflating', function (done) { - var app = createApp({ limit: '1kb' }) - var test = request(app).post('/') - test.set('Content-Encoding', 'gzip') - test.set('Content-Type', 'application/octet-stream') - test.write(Buffer.from('1f8b080000000000000ad3d31b05a360148c64000087e5a147040400', 'hex')) - test.expect(413, done) - }) - }) - - describe('with inflate option', function () { - describe('when false', function () { - before(function () { - this.app = createApp({ inflate: false }) - }) - - it('should not accept content-encoding', function (done) { - var test = request(this.app).post('/') - test.set('Content-Encoding', 'gzip') - test.set('Content-Type', 'application/octet-stream') - test.write(Buffer.from('1f8b080000000000000bcb4bcc4db57db16e170099a4bad608000000', 'hex')) - test.expect(415, '[encoding.unsupported] content encoding unsupported', done) - }) - }) - - describe('when true', function () { - before(function () { - this.app = createApp({ inflate: true }) - }) - - it('should accept content-encoding', function (done) { - var test = request(this.app).post('/') - test.set('Content-Encoding', 'gzip') - test.set('Content-Type', 'application/octet-stream') - test.write(Buffer.from('1f8b080000000000000bcb4bcc4db57db16e170099a4bad608000000', 'hex')) - test.expect(200, { buf: '6e616d653de8aeba' }, done) - }) - }) - }) - - describe('with type option', function () { - describe('when "application/vnd+octets"', function () { - before(function () { - this.app = createApp({ type: 'application/vnd+octets' }) - }) - - it('should parse for custom type', function (done) { - var test = request(this.app).post('/') - test.set('Content-Type', 'application/vnd+octets') - test.write(Buffer.from('000102', 'hex')) - test.expect(200, { buf: '000102' }, done) - }) - - it('should ignore standard type', function (done) { - var test = request(this.app).post('/') - test.set('Content-Type', 'application/octet-stream') - test.write(Buffer.from('000102', 'hex')) - test.expect(200, '{}', done) - }) - }) - - describe('when ["application/octet-stream", "application/vnd+octets"]', function () { - before(function () { - this.app = createApp({ - type: ['application/octet-stream', 'application/vnd+octets'] - }) - }) - - it('should parse "application/octet-stream"', function (done) { - var test = request(this.app).post('/') - test.set('Content-Type', 'application/octet-stream') - test.write(Buffer.from('000102', 'hex')) - test.expect(200, { buf: '000102' }, done) - }) - - it('should parse "application/vnd+octets"', function (done) { - var test = request(this.app).post('/') - test.set('Content-Type', 'application/vnd+octets') - test.write(Buffer.from('000102', 'hex')) - test.expect(200, { buf: '000102' }, done) - }) - - it('should ignore "application/x-foo"', function (done) { - var test = request(this.app).post('/') - test.set('Content-Type', 'application/x-foo') - test.write(Buffer.from('000102', 'hex')) - test.expect(200, '{}', done) - }) - }) - - describe('when a function', function () { - it('should parse when truthy value returned', function (done) { - var app = createApp({ type: accept }) - - function accept (req) { - return req.headers['content-type'] === 'application/vnd.octet' - } - - var test = request(app).post('/') - test.set('Content-Type', 'application/vnd.octet') - test.write(Buffer.from('000102', 'hex')) - test.expect(200, { buf: '000102' }, done) - }) - - it('should work without content-type', function (done) { - var app = createApp({ type: accept }) - - function accept (req) { - return true - } - - var test = request(app).post('/') - test.write(Buffer.from('000102', 'hex')) - test.expect(200, { buf: '000102' }, done) - }) - - it('should not invoke without a body', function (done) { - var app = createApp({ type: accept }) - - function accept (req) { - throw new Error('oops!') - } - - request(app) - .get('/') - .expect(404, done) - }) - }) - }) - - describe('with verify option', function () { - it('should assert value is function', function () { - assert.throws(createApp.bind(null, { verify: 'lol' }), - /TypeError: option verify must be function/) - }) - - it('should error from verify', function (done) { - var app = createApp({ - verify: function (req, res, buf) { - if (buf[0] === 0x00) throw new Error('no leading null') - } - }) - - var test = request(app).post('/') - test.set('Content-Type', 'application/octet-stream') - test.write(Buffer.from('000102', 'hex')) - test.expect(403, '[entity.verify.failed] no leading null', done) - }) - - it('should allow custom codes', function (done) { - var app = createApp({ - verify: function (req, res, buf) { - if (buf[0] !== 0x00) return - var err = new Error('no leading null') - err.status = 400 - throw err - } - }) - - var test = request(app).post('/') - test.set('Content-Type', 'application/octet-stream') - test.write(Buffer.from('000102', 'hex')) - test.expect(400, '[entity.verify.failed] no leading null', done) - }) - - it('should allow pass-through', function (done) { - var app = createApp({ - verify: function (req, res, buf) { - if (buf[0] === 0x00) throw new Error('no leading null') - } - }) - - var test = request(app).post('/') - test.set('Content-Type', 'application/octet-stream') - test.write(Buffer.from('0102', 'hex')) - test.expect(200, { buf: '0102' }, done) - }) - }) - - describeAsyncHooks('async local storage', function () { - before(function () { - var app = express() - var store = { foo: 'bar' } - - app.use(function (req, res, next) { - req.asyncLocalStorage = new asyncHooks.AsyncLocalStorage() - req.asyncLocalStorage.run(store, next) - }) - - app.use(express.raw()) - - app.use(function (req, res, next) { - var local = req.asyncLocalStorage.getStore() - - if (local) { - res.setHeader('x-store-foo', String(local.foo)) - } - - next() - }) - - app.use(function (err, req, res, next) { - var local = req.asyncLocalStorage.getStore() - - if (local) { - res.setHeader('x-store-foo', String(local.foo)) - } - - res.status(err.status || 500) - res.send('[' + err.type + '] ' + err.message) - }) - - app.post('/', function (req, res) { - if (Buffer.isBuffer(req.body)) { - res.json({ buf: req.body.toString('hex') }) - } else { - res.json(req.body) - } - }) - - this.app = app - }) - - it('should presist store', function (done) { - request(this.app) - .post('/') - .set('Content-Type', 'application/octet-stream') - .send('the user is tobi') - .expect(200) - .expect('x-store-foo', 'bar') - .expect({ buf: '746865207573657220697320746f6269' }) - .end(done) - }) - - it('should presist store when unmatched content-type', function (done) { - request(this.app) - .post('/') - .set('Content-Type', 'application/fizzbuzz') - .send('buzz') - .expect(200) - .expect('x-store-foo', 'bar') - .expect('{}') - .end(done) - }) - - it('should presist store when inflated', function (done) { - var test = request(this.app).post('/') - test.set('Content-Encoding', 'gzip') - test.set('Content-Type', 'application/octet-stream') - test.write(Buffer.from('1f8b080000000000000bcb4bcc4db57db16e170099a4bad608000000', 'hex')) - test.expect(200) - test.expect('x-store-foo', 'bar') - test.expect({ buf: '6e616d653de8aeba' }) - test.end(done) - }) - - it('should presist store when inflate error', function (done) { - var test = request(this.app).post('/') - test.set('Content-Encoding', 'gzip') - test.set('Content-Type', 'application/octet-stream') - test.write(Buffer.from('1f8b080000000000000bcb4bcc4db57db16e170099a4bad6080000', 'hex')) - test.expect(400) - test.expect('x-store-foo', 'bar') - test.end(done) - }) - - it('should presist store when limit exceeded', function (done) { - request(this.app) - .post('/') - .set('Content-Type', 'application/octet-stream') - .send('the user is ' + Buffer.alloc(1024 * 100, '.').toString()) - .expect(413) - .expect('x-store-foo', 'bar') - .end(done) - }) - }) - - describe('charset', function () { - before(function () { - this.app = createApp() - }) - - it('should ignore charset', function (done) { - var test = request(this.app).post('/') - test.set('Content-Type', 'application/octet-stream; charset=utf-8') - test.write(Buffer.from('6e616d6520697320e8aeba', 'hex')) - test.expect(200, { buf: '6e616d6520697320e8aeba' }, done) - }) - }) - - describe('encoding', function () { - before(function () { - this.app = createApp({ limit: '10kb' }) - }) - - it('should parse without encoding', function (done) { - var test = request(this.app).post('/') - test.set('Content-Type', 'application/octet-stream') - test.write(Buffer.from('6e616d653de8aeba', 'hex')) - test.expect(200, { buf: '6e616d653de8aeba' }, done) - }) - - it('should support identity encoding', function (done) { - var test = request(this.app).post('/') - test.set('Content-Encoding', 'identity') - test.set('Content-Type', 'application/octet-stream') - test.write(Buffer.from('6e616d653de8aeba', 'hex')) - test.expect(200, { buf: '6e616d653de8aeba' }, done) - }) - - it('should support gzip encoding', function (done) { - var test = request(this.app).post('/') - test.set('Content-Encoding', 'gzip') - test.set('Content-Type', 'application/octet-stream') - test.write(Buffer.from('1f8b080000000000000bcb4bcc4db57db16e170099a4bad608000000', 'hex')) - test.expect(200, { buf: '6e616d653de8aeba' }, done) - }) - - it('should support deflate encoding', function (done) { - var test = request(this.app).post('/') - test.set('Content-Encoding', 'deflate') - test.set('Content-Type', 'application/octet-stream') - test.write(Buffer.from('789ccb4bcc4db57db16e17001068042f', 'hex')) - test.expect(200, { buf: '6e616d653de8aeba' }, done) - }) - - it('should be case-insensitive', function (done) { - var test = request(this.app).post('/') - test.set('Content-Encoding', 'GZIP') - test.set('Content-Type', 'application/octet-stream') - test.write(Buffer.from('1f8b080000000000000bcb4bcc4db57db16e170099a4bad608000000', 'hex')) - test.expect(200, { buf: '6e616d653de8aeba' }, done) - }) - - it('should 415 on unknown encoding', function (done) { - var test = request(this.app).post('/') - test.set('Content-Encoding', 'nulls') - test.set('Content-Type', 'application/octet-stream') - test.write(Buffer.from('000000000000', 'hex')) - test.expect(415, '[encoding.unsupported] unsupported content encoding "nulls"', done) - }) - }) -}) - -function createApp (options) { - var app = express() - - app.use(express.raw(options)) - - app.use(function (err, req, res, next) { - res.status(err.status || 500) - res.send(String(req.headers['x-error-property'] - ? err[req.headers['x-error-property']] - : ('[' + err.type + '] ' + err.message))) - }) - - app.post('/', function (req, res) { - if (Buffer.isBuffer(req.body)) { - res.json({ buf: req.body.toString('hex') }) - } else { - res.json(req.body) - } - }) - - return app -} - -function tryRequire (name) { - try { - return require(name) - } catch (e) { - return {} - } -} diff --git a/test/express.static.js b/test/express.static.js deleted file mode 100644 index 245fd592..00000000 --- a/test/express.static.js +++ /dev/null @@ -1,814 +0,0 @@ -'use strict' - -var assert = require('assert') -var Buffer = require('safe-buffer').Buffer -var express = require('..') -var path = require('path') -var request = require('supertest') -var utils = require('./support/utils') - -var fixtures = path.join(__dirname, '/fixtures') -var relative = path.relative(process.cwd(), fixtures) - -var skipRelative = ~relative.indexOf('..') || path.resolve(relative) === relative - -describe('express.static()', function () { - describe('basic operations', function () { - before(function () { - this.app = createApp() - }) - - it('should require root path', function () { - assert.throws(express.static.bind(), /root path required/) - }) - - it('should require root path to be string', function () { - assert.throws(express.static.bind(null, 42), /root path.*string/) - }) - - it('should serve static files', function (done) { - request(this.app) - .get('/todo.txt') - .expect(200, '- groceries', done) - }) - - it('should support nesting', function (done) { - request(this.app) - .get('/users/tobi.txt') - .expect(200, 'ferret', done) - }) - - it('should set Content-Type', function (done) { - request(this.app) - .get('/todo.txt') - .expect('Content-Type', 'text/plain; charset=UTF-8') - .expect(200, done) - }) - - it('should set Last-Modified', function (done) { - request(this.app) - .get('/todo.txt') - .expect('Last-Modified', /\d{2} \w{3} \d{4}/) - .expect(200, done) - }) - - it('should default max-age=0', function (done) { - request(this.app) - .get('/todo.txt') - .expect('Cache-Control', 'public, max-age=0') - .expect(200, done) - }) - - it('should support urlencoded pathnames', function (done) { - request(this.app) - .get('/%25%20of%20dogs.txt') - .expect(200, '20%', done) - }) - - it('should not choke on auth-looking URL', function (done) { - request(this.app) - .get('//todo@txt') - .expect(404, 'Not Found', done) - }) - - it('should support index.html', function (done) { - request(this.app) - .get('/users/') - .expect(200) - .expect('Content-Type', /html/) - .expect('

tobi, loki, jane

', done) - }) - - it('should support ../', function (done) { - request(this.app) - .get('/users/../todo.txt') - .expect(200, '- groceries', done) - }) - - it('should support HEAD', function (done) { - request(this.app) - .head('/todo.txt') - .expect(200) - .expect(utils.shouldNotHaveBody()) - .end(done) - }) - - it('should skip POST requests', function (done) { - request(this.app) - .post('/todo.txt') - .expect(404, 'Not Found', done) - }) - - it('should support conditional requests', function (done) { - var app = this.app - - request(app) - .get('/todo.txt') - .end(function (err, res) { - if (err) throw err - request(app) - .get('/todo.txt') - .set('If-None-Match', res.headers.etag) - .expect(304, done) - }) - }) - - it('should support precondition checks', function (done) { - request(this.app) - .get('/todo.txt') - .set('If-Match', '"foo"') - .expect(412, done) - }) - - it('should serve zero-length files', function (done) { - request(this.app) - .get('/empty.txt') - .expect(200, '', done) - }) - - it('should ignore hidden files', function (done) { - request(this.app) - .get('/.name') - .expect(404, 'Not Found', done) - }) - }); - - (skipRelative ? describe.skip : describe)('current dir', function () { - before(function () { - this.app = createApp('.') - }) - - it('should be served with "."', function (done) { - var dest = relative.split(path.sep).join('/') - request(this.app) - .get('/' + dest + '/todo.txt') - .expect(200, '- groceries', done) - }) - }) - - describe('acceptRanges', function () { - describe('when false', function () { - it('should not include Accept-Ranges', function (done) { - request(createApp(fixtures, { 'acceptRanges': false })) - .get('/nums.txt') - .expect(utils.shouldNotHaveHeader('Accept-Ranges')) - .expect(200, '123456789', done) - }) - - it('should ignore Rage request header', function (done) { - request(createApp(fixtures, { 'acceptRanges': false })) - .get('/nums.txt') - .set('Range', 'bytes=0-3') - .expect(utils.shouldNotHaveHeader('Accept-Ranges')) - .expect(utils.shouldNotHaveHeader('Content-Range')) - .expect(200, '123456789', done) - }) - }) - - describe('when true', function () { - it('should include Accept-Ranges', function (done) { - request(createApp(fixtures, { 'acceptRanges': true })) - .get('/nums.txt') - .expect('Accept-Ranges', 'bytes') - .expect(200, '123456789', done) - }) - - it('should obey Rage request header', function (done) { - request(createApp(fixtures, { 'acceptRanges': true })) - .get('/nums.txt') - .set('Range', 'bytes=0-3') - .expect('Accept-Ranges', 'bytes') - .expect('Content-Range', 'bytes 0-3/9') - .expect(206, '1234', done) - }) - }) - }) - - describe('cacheControl', function () { - describe('when false', function () { - it('should not include Cache-Control', function (done) { - request(createApp(fixtures, { 'cacheControl': false })) - .get('/nums.txt') - .expect(utils.shouldNotHaveHeader('Cache-Control')) - .expect(200, '123456789', done) - }) - - it('should ignore maxAge', function (done) { - request(createApp(fixtures, { 'cacheControl': false, 'maxAge': 12000 })) - .get('/nums.txt') - .expect(utils.shouldNotHaveHeader('Cache-Control')) - .expect(200, '123456789', done) - }) - }) - - describe('when true', function () { - it('should include Cache-Control', function (done) { - request(createApp(fixtures, { 'cacheControl': true })) - .get('/nums.txt') - .expect('Cache-Control', 'public, max-age=0') - .expect(200, '123456789', done) - }) - }) - }) - - describe('extensions', function () { - it('should be not be enabled by default', function (done) { - request(createApp(fixtures)) - .get('/todo') - .expect(404, done) - }) - - it('should be configurable', function (done) { - request(createApp(fixtures, { 'extensions': 'txt' })) - .get('/todo') - .expect(200, '- groceries', done) - }) - - it('should support disabling extensions', function (done) { - request(createApp(fixtures, { 'extensions': false })) - .get('/todo') - .expect(404, done) - }) - - it('should support fallbacks', function (done) { - request(createApp(fixtures, { 'extensions': ['htm', 'html', 'txt'] })) - .get('/todo') - .expect(200, '
  • groceries
  • ', done) - }) - - it('should 404 if nothing found', function (done) { - request(createApp(fixtures, { 'extensions': ['htm', 'html', 'txt'] })) - .get('/bob') - .expect(404, done) - }) - }) - - describe('fallthrough', function () { - it('should default to true', function (done) { - request(createApp()) - .get('/does-not-exist') - .expect(404, 'Not Found', done) - }) - - describe('when true', function () { - before(function () { - this.app = createApp(fixtures, { 'fallthrough': true }) - }) - - it('should fall-through when OPTIONS request', function (done) { - request(this.app) - .options('/todo.txt') - .expect(404, 'Not Found', done) - }) - - it('should fall-through when URL malformed', function (done) { - request(this.app) - .get('/%') - .expect(404, 'Not Found', done) - }) - - it('should fall-through when traversing past root', function (done) { - request(this.app) - .get('/users/../../todo.txt') - .expect(404, 'Not Found', done) - }) - - it('should fall-through when URL too long', function (done) { - var app = express() - var root = fixtures + Array(10000).join('/foobar') - - app.use(express.static(root, { 'fallthrough': true })) - app.use(function (req, res, next) { - res.sendStatus(404) - }) - - request(app) - .get('/') - .expect(404, 'Not Found', done) - }) - - describe('with redirect: true', function () { - before(function () { - this.app = createApp(fixtures, { 'fallthrough': true, 'redirect': true }) - }) - - it('should fall-through when directory', function (done) { - request(this.app) - .get('/pets/') - .expect(404, 'Not Found', done) - }) - - it('should redirect when directory without slash', function (done) { - request(this.app) - .get('/pets') - .expect(301, /Redirecting/, done) - }) - }) - - describe('with redirect: false', function () { - before(function () { - this.app = createApp(fixtures, { 'fallthrough': true, 'redirect': false }) - }) - - it('should fall-through when directory', function (done) { - request(this.app) - .get('/pets/') - .expect(404, 'Not Found', done) - }) - - it('should fall-through when directory without slash', function (done) { - request(this.app) - .get('/pets') - .expect(404, 'Not Found', done) - }) - }) - }) - - describe('when false', function () { - before(function () { - this.app = createApp(fixtures, { 'fallthrough': false }) - }) - - it('should 405 when OPTIONS request', function (done) { - request(this.app) - .options('/todo.txt') - .expect('Allow', 'GET, HEAD') - .expect(405, done) - }) - - it('should 400 when URL malformed', function (done) { - request(this.app) - .get('/%') - .expect(400, /BadRequestError/, done) - }) - - it('should 403 when traversing past root', function (done) { - request(this.app) - .get('/users/../../todo.txt') - .expect(403, /ForbiddenError/, done) - }) - - it('should 404 when URL too long', function (done) { - var app = express() - var root = fixtures + Array(10000).join('/foobar') - - app.use(express.static(root, { 'fallthrough': false })) - app.use(function (req, res, next) { - res.sendStatus(404) - }) - - request(app) - .get('/') - .expect(404, /ENAMETOOLONG/, done) - }) - - describe('with redirect: true', function () { - before(function () { - this.app = createApp(fixtures, { 'fallthrough': false, 'redirect': true }) - }) - - it('should 404 when directory', function (done) { - request(this.app) - .get('/pets/') - .expect(404, /NotFoundError|ENOENT/, done) - }) - - it('should redirect when directory without slash', function (done) { - request(this.app) - .get('/pets') - .expect(301, /Redirecting/, done) - }) - }) - - describe('with redirect: false', function () { - before(function () { - this.app = createApp(fixtures, { 'fallthrough': false, 'redirect': false }) - }) - - it('should 404 when directory', function (done) { - request(this.app) - .get('/pets/') - .expect(404, /NotFoundError|ENOENT/, done) - }) - - it('should 404 when directory without slash', function (done) { - request(this.app) - .get('/pets') - .expect(404, /NotFoundError|ENOENT/, done) - }) - }) - }) - }) - - describe('hidden files', function () { - before(function () { - this.app = createApp(fixtures, { 'dotfiles': 'allow' }) - }) - - it('should be served when dotfiles: "allow" is given', function (done) { - request(this.app) - .get('/.name') - .expect(200) - .expect(utils.shouldHaveBody(Buffer.from('tobi'))) - .end(done) - }) - }) - - describe('immutable', function () { - it('should default to false', function (done) { - request(createApp(fixtures)) - .get('/nums.txt') - .expect('Cache-Control', 'public, max-age=0', done) - }) - - it('should set immutable directive in Cache-Control', function (done) { - request(createApp(fixtures, { 'immutable': true, 'maxAge': '1h' })) - .get('/nums.txt') - .expect('Cache-Control', 'public, max-age=3600, immutable', done) - }) - }) - - describe('lastModified', function () { - describe('when false', function () { - it('should not include Last-Modified', function (done) { - request(createApp(fixtures, { 'lastModified': false })) - .get('/nums.txt') - .expect(utils.shouldNotHaveHeader('Last-Modified')) - .expect(200, '123456789', done) - }) - }) - - describe('when true', function () { - it('should include Last-Modified', function (done) { - request(createApp(fixtures, { 'lastModified': true })) - .get('/nums.txt') - .expect('Last-Modified', /^\w{3}, \d+ \w+ \d+ \d+:\d+:\d+ \w+$/) - .expect(200, '123456789', done) - }) - }) - }) - - describe('maxAge', function () { - it('should accept string', function (done) { - request(createApp(fixtures, { 'maxAge': '30d' })) - .get('/todo.txt') - .expect('cache-control', 'public, max-age=' + (60 * 60 * 24 * 30)) - .expect(200, done) - }) - - it('should be reasonable when infinite', function (done) { - request(createApp(fixtures, { 'maxAge': Infinity })) - .get('/todo.txt') - .expect('cache-control', 'public, max-age=' + (60 * 60 * 24 * 365)) - .expect(200, done) - }) - }) - - describe('redirect', function () { - before(function () { - this.app = express() - this.app.use(function (req, res, next) { - req.originalUrl = req.url = - req.originalUrl.replace(/\/snow(\/|$)/, '/snow \u2603$1') - next() - }) - this.app.use(express.static(fixtures)) - }) - - it('should redirect directories', function (done) { - request(this.app) - .get('/users') - .expect('Location', '/users/') - .expect(301, done) - }) - - it('should include HTML link', function (done) { - request(this.app) - .get('/users') - .expect('Location', '/users/') - .expect(301, //, done) - }) - - it('should redirect directories with query string', function (done) { - request(this.app) - .get('/users?name=john') - .expect('Location', '/users/?name=john') - .expect(301, done) - }) - - it('should not redirect to protocol-relative locations', function (done) { - request(this.app) - .get('//users') - .expect('Location', '/users/') - .expect(301, done) - }) - - it('should ensure redirect URL is properly encoded', function (done) { - request(this.app) - .get('/snow') - .expect('Location', '/snow%20%E2%98%83/') - .expect('Content-Type', /html/) - .expect(301, />Redirecting to \/snow%20%E2%98%83\/<\/a>tobi') - .expect(200, '"tobi"', done) - }) - - it('should ignore standard type', function (done) { - request(this.app) - .post('/') - .set('Content-Type', 'text/plain') - .send('user is tobi') - .expect(200, '{}', done) - }) - }) - - describe('when ["text/html", "text/plain"]', function () { - before(function () { - this.app = createApp({ type: ['text/html', 'text/plain'] }) - }) - - it('should parse "text/html"', function (done) { - request(this.app) - .post('/') - .set('Content-Type', 'text/html') - .send('tobi') - .expect(200, '"tobi"', done) - }) - - it('should parse "text/plain"', function (done) { - request(this.app) - .post('/') - .set('Content-Type', 'text/plain') - .send('tobi') - .expect(200, '"tobi"', done) - }) - - it('should ignore "text/xml"', function (done) { - request(this.app) - .post('/') - .set('Content-Type', 'text/xml') - .send('tobi') - .expect(200, '{}', done) - }) - }) - - describe('when a function', function () { - it('should parse when truthy value returned', function (done) { - var app = createApp({ type: accept }) - - function accept (req) { - return req.headers['content-type'] === 'text/vnd.something' - } - - request(app) - .post('/') - .set('Content-Type', 'text/vnd.something') - .send('user is tobi') - .expect(200, '"user is tobi"', done) - }) - - it('should work without content-type', function (done) { - var app = createApp({ type: accept }) - - function accept (req) { - return true - } - - var test = request(app).post('/') - test.write('user is tobi') - test.expect(200, '"user is tobi"', done) - }) - - it('should not invoke without a body', function (done) { - var app = createApp({ type: accept }) - - function accept (req) { - throw new Error('oops!') - } - - request(app) - .get('/') - .expect(404, done) - }) - }) - }) - - describe('with verify option', function () { - it('should assert value is function', function () { - assert.throws(createApp.bind(null, { verify: 'lol' }), - /TypeError: option verify must be function/) - }) - - it('should error from verify', function (done) { - var app = createApp({ - verify: function (req, res, buf) { - if (buf[0] === 0x20) throw new Error('no leading space') - } - }) - - request(app) - .post('/') - .set('Content-Type', 'text/plain') - .send(' user is tobi') - .expect(403, '[entity.verify.failed] no leading space', done) - }) - - it('should allow custom codes', function (done) { - var app = createApp({ - verify: function (req, res, buf) { - if (buf[0] !== 0x20) return - var err = new Error('no leading space') - err.status = 400 - throw err - } - }) - - request(app) - .post('/') - .set('Content-Type', 'text/plain') - .send(' user is tobi') - .expect(400, '[entity.verify.failed] no leading space', done) - }) - - it('should allow pass-through', function (done) { - var app = createApp({ - verify: function (req, res, buf) { - if (buf[0] === 0x20) throw new Error('no leading space') - } - }) - - request(app) - .post('/') - .set('Content-Type', 'text/plain') - .send('user is tobi') - .expect(200, '"user is tobi"', done) - }) - - it('should 415 on unknown charset prior to verify', function (done) { - var app = createApp({ - verify: function (req, res, buf) { - throw new Error('unexpected verify call') - } - }) - - var test = request(app).post('/') - test.set('Content-Type', 'text/plain; charset=x-bogus') - test.write(Buffer.from('00000000', 'hex')) - test.expect(415, '[charset.unsupported] unsupported charset "X-BOGUS"', done) - }) - }) - - describeAsyncHooks('async local storage', function () { - before(function () { - var app = express() - var store = { foo: 'bar' } - - app.use(function (req, res, next) { - req.asyncLocalStorage = new asyncHooks.AsyncLocalStorage() - req.asyncLocalStorage.run(store, next) - }) - - app.use(express.text()) - - app.use(function (req, res, next) { - var local = req.asyncLocalStorage.getStore() - - if (local) { - res.setHeader('x-store-foo', String(local.foo)) - } - - next() - }) - - app.use(function (err, req, res, next) { - var local = req.asyncLocalStorage.getStore() - - if (local) { - res.setHeader('x-store-foo', String(local.foo)) - } - - res.status(err.status || 500) - res.send('[' + err.type + '] ' + err.message) - }) - - app.post('/', function (req, res) { - res.json(req.body) - }) - - this.app = app - }) - - it('should presist store', function (done) { - request(this.app) - .post('/') - .set('Content-Type', 'text/plain') - .send('user is tobi') - .expect(200) - .expect('x-store-foo', 'bar') - .expect('"user is tobi"') - .end(done) - }) - - it('should presist store when unmatched content-type', function (done) { - request(this.app) - .post('/') - .set('Content-Type', 'application/fizzbuzz') - .send('buzz') - .expect(200) - .expect('x-store-foo', 'bar') - .expect('{}') - .end(done) - }) - - it('should presist store when inflated', function (done) { - var test = request(this.app).post('/') - test.set('Content-Encoding', 'gzip') - test.set('Content-Type', 'text/plain') - test.write(Buffer.from('1f8b080000000000000bcb4bcc4d55c82c5678b16e170072b3e0200b000000', 'hex')) - test.expect(200) - test.expect('x-store-foo', 'bar') - test.expect('"name is 论"') - test.end(done) - }) - - it('should presist store when inflate error', function (done) { - var test = request(this.app).post('/') - test.set('Content-Encoding', 'gzip') - test.set('Content-Type', 'text/plain') - test.write(Buffer.from('1f8b080000000000000bcb4bcc4d55c82c5678b16e170072b3e0200b0000', 'hex')) - test.expect(400) - test.expect('x-store-foo', 'bar') - test.end(done) - }) - - it('should presist store when limit exceeded', function (done) { - request(this.app) - .post('/') - .set('Content-Type', 'text/plain') - .send('user is ' + Buffer.alloc(1024 * 100, '.').toString()) - .expect(413) - .expect('x-store-foo', 'bar') - .end(done) - }) - }) - - describe('charset', function () { - before(function () { - this.app = createApp() - }) - - it('should parse utf-8', function (done) { - var test = request(this.app).post('/') - test.set('Content-Type', 'text/plain; charset=utf-8') - test.write(Buffer.from('6e616d6520697320e8aeba', 'hex')) - test.expect(200, '"name is 论"', done) - }) - - it('should parse codepage charsets', function (done) { - var test = request(this.app).post('/') - test.set('Content-Type', 'text/plain; charset=koi8-r') - test.write(Buffer.from('6e616d6520697320cec5d4', 'hex')) - test.expect(200, '"name is нет"', done) - }) - - it('should parse when content-length != char length', function (done) { - var test = request(this.app).post('/') - test.set('Content-Type', 'text/plain; charset=utf-8') - test.set('Content-Length', '11') - test.write(Buffer.from('6e616d6520697320e8aeba', 'hex')) - test.expect(200, '"name is 论"', done) - }) - - it('should default to utf-8', function (done) { - var test = request(this.app).post('/') - test.set('Content-Type', 'text/plain') - test.write(Buffer.from('6e616d6520697320e8aeba', 'hex')) - test.expect(200, '"name is 论"', done) - }) - - it('should 415 on unknown charset', function (done) { - var test = request(this.app).post('/') - test.set('Content-Type', 'text/plain; charset=x-bogus') - test.write(Buffer.from('00000000', 'hex')) - test.expect(415, '[charset.unsupported] unsupported charset "X-BOGUS"', done) - }) - }) - - describe('encoding', function () { - before(function () { - this.app = createApp({ limit: '10kb' }) - }) - - it('should parse without encoding', function (done) { - var test = request(this.app).post('/') - test.set('Content-Type', 'text/plain') - test.write(Buffer.from('6e616d6520697320e8aeba', 'hex')) - test.expect(200, '"name is 论"', done) - }) - - it('should support identity encoding', function (done) { - var test = request(this.app).post('/') - test.set('Content-Encoding', 'identity') - test.set('Content-Type', 'text/plain') - test.write(Buffer.from('6e616d6520697320e8aeba', 'hex')) - test.expect(200, '"name is 论"', done) - }) - - it('should support gzip encoding', function (done) { - var test = request(this.app).post('/') - test.set('Content-Encoding', 'gzip') - test.set('Content-Type', 'text/plain') - test.write(Buffer.from('1f8b080000000000000bcb4bcc4d55c82c5678b16e170072b3e0200b000000', 'hex')) - test.expect(200, '"name is 论"', done) - }) - - it('should support deflate encoding', function (done) { - var test = request(this.app).post('/') - test.set('Content-Encoding', 'deflate') - test.set('Content-Type', 'text/plain') - test.write(Buffer.from('789ccb4bcc4d55c82c5678b16e17001a6f050e', 'hex')) - test.expect(200, '"name is 论"', done) - }) - - it('should be case-insensitive', function (done) { - var test = request(this.app).post('/') - test.set('Content-Encoding', 'GZIP') - test.set('Content-Type', 'text/plain') - test.write(Buffer.from('1f8b080000000000000bcb4bcc4d55c82c5678b16e170072b3e0200b000000', 'hex')) - test.expect(200, '"name is 论"', done) - }) - - it('should 415 on unknown encoding', function (done) { - var test = request(this.app).post('/') - test.set('Content-Encoding', 'nulls') - test.set('Content-Type', 'text/plain') - test.write(Buffer.from('000000000000', 'hex')) - test.expect(415, '[encoding.unsupported] unsupported content encoding "nulls"', done) - }) - }) -}) - -function createApp (options) { - var app = express() - - app.use(express.text(options)) - - app.use(function (err, req, res, next) { - res.status(err.status || 500) - res.send(String(req.headers['x-error-property'] - ? err[req.headers['x-error-property']] - : ('[' + err.type + '] ' + err.message))) - }) - - app.post('/', function (req, res) { - res.json(req.body) - }) - - return app -} - -function tryRequire (name) { - try { - return require(name) - } catch (e) { - return {} - } -} diff --git a/test/express.urlencoded.js b/test/express.urlencoded.js deleted file mode 100644 index e07432c8..00000000 --- a/test/express.urlencoded.js +++ /dev/null @@ -1,866 +0,0 @@ -'use strict' - -var assert = require('assert') -var asyncHooks = tryRequire('async_hooks') -var Buffer = require('safe-buffer').Buffer -var express = require('..') -var request = require('supertest') - -var describeAsyncHooks = typeof asyncHooks.AsyncLocalStorage === 'function' - ? describe - : describe.skip - -describe('express.urlencoded()', function () { - before(function () { - this.app = createApp() - }) - - it('should parse x-www-form-urlencoded', function (done) { - request(this.app) - .post('/') - .set('Content-Type', 'application/x-www-form-urlencoded') - .send('user=tobi') - .expect(200, '{"user":"tobi"}', done) - }) - - it('should 400 when invalid content-length', function (done) { - var app = express() - - app.use(function (req, res, next) { - req.headers['content-length'] = '20' // bad length - next() - }) - - app.use(express.urlencoded()) - - app.post('/', function (req, res) { - res.json(req.body) - }) - - request(app) - .post('/') - .set('Content-Type', 'application/x-www-form-urlencoded') - .send('str=') - .expect(400, /content length/, done) - }) - - it('should handle Content-Length: 0', function (done) { - request(this.app) - .post('/') - .set('Content-Type', 'application/x-www-form-urlencoded') - .set('Content-Length', '0') - .send('') - .expect(200, '{}', done) - }) - - it('should handle empty message-body', function (done) { - request(createApp({ limit: '1kb' })) - .post('/') - .set('Content-Type', 'application/x-www-form-urlencoded') - .set('Transfer-Encoding', 'chunked') - .send('') - .expect(200, '{}', done) - }) - - it('should 500 if stream not readable', function (done) { - var app = express() - - app.use(function (req, res, next) { - req.on('end', next) - req.resume() - }) - - app.use(express.urlencoded()) - - app.use(function (err, req, res, next) { - res.status(err.status || 500) - res.send('[' + err.type + '] ' + err.message) - }) - - app.post('/', function (req, res) { - res.json(req.body) - }) - - request(app) - .post('/') - .set('Content-Type', 'application/x-www-form-urlencoded') - .send('user=tobi') - .expect(500, '[stream.not.readable] stream is not readable', done) - }) - - it('should handle duplicated middleware', function (done) { - var app = express() - - app.use(express.urlencoded()) - app.use(express.urlencoded()) - - app.post('/', function (req, res) { - res.json(req.body) - }) - - request(app) - .post('/') - .set('Content-Type', 'application/x-www-form-urlencoded') - .send('user=tobi') - .expect(200, '{"user":"tobi"}', done) - }) - - it('should parse extended syntax', function (done) { - request(this.app) - .post('/') - .set('Content-Type', 'application/x-www-form-urlencoded') - .send('user[name][first]=Tobi') - .expect(200, '{"user":{"name":{"first":"Tobi"}}}', done) - }) - - describe('with extended option', function () { - describe('when false', function () { - before(function () { - this.app = createApp({ extended: false }) - }) - - it('should not parse extended syntax', function (done) { - request(this.app) - .post('/') - .set('Content-Type', 'application/x-www-form-urlencoded') - .send('user[name][first]=Tobi') - .expect(200, '{"user[name][first]":"Tobi"}', done) - }) - - it('should parse multiple key instances', function (done) { - request(this.app) - .post('/') - .set('Content-Type', 'application/x-www-form-urlencoded') - .send('user=Tobi&user=Loki') - .expect(200, '{"user":["Tobi","Loki"]}', done) - }) - }) - - describe('when true', function () { - before(function () { - this.app = createApp({ extended: true }) - }) - - it('should parse multiple key instances', function (done) { - request(this.app) - .post('/') - .set('Content-Type', 'application/x-www-form-urlencoded') - .send('user=Tobi&user=Loki') - .expect(200, '{"user":["Tobi","Loki"]}', done) - }) - - it('should parse extended syntax', function (done) { - request(this.app) - .post('/') - .set('Content-Type', 'application/x-www-form-urlencoded') - .send('user[name][first]=Tobi') - .expect(200, '{"user":{"name":{"first":"Tobi"}}}', done) - }) - - it('should parse parameters with dots', function (done) { - request(this.app) - .post('/') - .set('Content-Type', 'application/x-www-form-urlencoded') - .send('user.name=Tobi') - .expect(200, '{"user.name":"Tobi"}', done) - }) - - it('should parse fully-encoded extended syntax', function (done) { - request(this.app) - .post('/') - .set('Content-Type', 'application/x-www-form-urlencoded') - .send('user%5Bname%5D%5Bfirst%5D=Tobi') - .expect(200, '{"user":{"name":{"first":"Tobi"}}}', done) - }) - - it('should parse array index notation', function (done) { - request(this.app) - .post('/') - .set('Content-Type', 'application/x-www-form-urlencoded') - .send('foo[0]=bar&foo[1]=baz') - .expect(200, '{"foo":["bar","baz"]}', done) - }) - - it('should parse array index notation with large array', function (done) { - var str = 'f[0]=0' - - for (var i = 1; i < 500; i++) { - str += '&f[' + i + ']=' + i.toString(16) - } - - request(this.app) - .post('/') - .set('Content-Type', 'application/x-www-form-urlencoded') - .send(str) - .expect(function (res) { - var obj = JSON.parse(res.text) - assert.strictEqual(Object.keys(obj).length, 1) - assert.strictEqual(Array.isArray(obj.f), true) - assert.strictEqual(obj.f.length, 500) - }) - .expect(200, done) - }) - - it('should parse array of objects syntax', function (done) { - request(this.app) - .post('/') - .set('Content-Type', 'application/x-www-form-urlencoded') - .send('foo[0][bar]=baz&foo[0][fizz]=buzz&foo[]=done!') - .expect(200, '{"foo":[{"bar":"baz","fizz":"buzz"},"done!"]}', done) - }) - - it('should parse deep object', function (done) { - var str = 'foo' - - for (var i = 0; i < 500; i++) { - str += '[p]' - } - - str += '=bar' - - request(this.app) - .post('/') - .set('Content-Type', 'application/x-www-form-urlencoded') - .send(str) - .expect(function (res) { - var obj = JSON.parse(res.text) - assert.strictEqual(Object.keys(obj).length, 1) - assert.strictEqual(typeof obj.foo, 'object') - - var depth = 0 - var ref = obj.foo - while ((ref = ref.p)) { depth++ } - assert.strictEqual(depth, 500) - }) - .expect(200, done) - }) - }) - }) - - describe('with inflate option', function () { - describe('when false', function () { - before(function () { - this.app = createApp({ inflate: false }) - }) - - it('should not accept content-encoding', function (done) { - var test = request(this.app).post('/') - test.set('Content-Encoding', 'gzip') - test.set('Content-Type', 'application/x-www-form-urlencoded') - test.write(Buffer.from('1f8b080000000000000bcb4bcc4db57db16e170099a4bad608000000', 'hex')) - test.expect(415, '[encoding.unsupported] content encoding unsupported', done) - }) - }) - - describe('when true', function () { - before(function () { - this.app = createApp({ inflate: true }) - }) - - it('should accept content-encoding', function (done) { - var test = request(this.app).post('/') - test.set('Content-Encoding', 'gzip') - test.set('Content-Type', 'application/x-www-form-urlencoded') - test.write(Buffer.from('1f8b080000000000000bcb4bcc4db57db16e170099a4bad608000000', 'hex')) - test.expect(200, '{"name":"论"}', done) - }) - }) - }) - - describe('with limit option', function () { - it('should 413 when over limit with Content-Length', function (done) { - var buf = Buffer.alloc(1024, '.') - request(createApp({ limit: '1kb' })) - .post('/') - .set('Content-Type', 'application/x-www-form-urlencoded') - .set('Content-Length', '1028') - .send('str=' + buf.toString()) - .expect(413, done) - }) - - it('should 413 when over limit with chunked encoding', function (done) { - var app = createApp({ limit: '1kb' }) - var buf = Buffer.alloc(1024, '.') - var test = request(app).post('/') - test.set('Content-Type', 'application/x-www-form-urlencoded') - test.set('Transfer-Encoding', 'chunked') - test.write('str=') - test.write(buf.toString()) - test.expect(413, done) - }) - - it('should 413 when inflated body over limit', function (done) { - var app = createApp({ limit: '1kb' }) - var test = request(app).post('/') - test.set('Content-Encoding', 'gzip') - test.set('Content-Type', 'application/x-www-form-urlencoded') - test.write(Buffer.from('1f8b080000000000000a2b2e29b2d51b05a360148c580000a0351f9204040000', 'hex')) - test.expect(413, done) - }) - - it('should accept number of bytes', function (done) { - var buf = Buffer.alloc(1024, '.') - request(createApp({ limit: 1024 })) - .post('/') - .set('Content-Type', 'application/x-www-form-urlencoded') - .send('str=' + buf.toString()) - .expect(413, done) - }) - - it('should not change when options altered', function (done) { - var buf = Buffer.alloc(1024, '.') - var options = { limit: '1kb' } - var app = createApp(options) - - options.limit = '100kb' - - request(app) - .post('/') - .set('Content-Type', 'application/x-www-form-urlencoded') - .send('str=' + buf.toString()) - .expect(413, done) - }) - - it('should not hang response', function (done) { - var app = createApp({ limit: '8kb' }) - var buf = Buffer.alloc(10240, '.') - var test = request(app).post('/') - test.set('Content-Type', 'application/x-www-form-urlencoded') - test.write(buf) - test.write(buf) - test.write(buf) - test.expect(413, done) - }) - - it('should not error when inflating', function (done) { - var app = createApp({ limit: '1kb' }) - var test = request(app).post('/') - test.set('Content-Encoding', 'gzip') - test.set('Content-Type', 'application/x-www-form-urlencoded') - test.write(Buffer.from('1f8b080000000000000a2b2e29b2d51b05a360148c580000a0351f92040400', 'hex')) - test.expect(413, done) - }) - }) - - describe('with parameterLimit option', function () { - describe('with extended: false', function () { - it('should reject 0', function () { - assert.throws(createApp.bind(null, { extended: false, parameterLimit: 0 }), - /TypeError: option parameterLimit must be a positive number/) - }) - - it('should reject string', function () { - assert.throws(createApp.bind(null, { extended: false, parameterLimit: 'beep' }), - /TypeError: option parameterLimit must be a positive number/) - }) - - it('should 413 if over limit', function (done) { - request(createApp({ extended: false, parameterLimit: 10 })) - .post('/') - .set('Content-Type', 'application/x-www-form-urlencoded') - .send(createManyParams(11)) - .expect(413, '[parameters.too.many] too many parameters', done) - }) - - it('should work when at the limit', function (done) { - request(createApp({ extended: false, parameterLimit: 10 })) - .post('/') - .set('Content-Type', 'application/x-www-form-urlencoded') - .send(createManyParams(10)) - .expect(expectKeyCount(10)) - .expect(200, done) - }) - - it('should work if number is floating point', function (done) { - request(createApp({ extended: false, parameterLimit: 10.1 })) - .post('/') - .set('Content-Type', 'application/x-www-form-urlencoded') - .send(createManyParams(11)) - .expect(413, /too many parameters/, done) - }) - - it('should work with large limit', function (done) { - request(createApp({ extended: false, parameterLimit: 5000 })) - .post('/') - .set('Content-Type', 'application/x-www-form-urlencoded') - .send(createManyParams(5000)) - .expect(expectKeyCount(5000)) - .expect(200, done) - }) - - it('should work with Infinity limit', function (done) { - request(createApp({ extended: false, parameterLimit: Infinity })) - .post('/') - .set('Content-Type', 'application/x-www-form-urlencoded') - .send(createManyParams(10000)) - .expect(expectKeyCount(10000)) - .expect(200, done) - }) - }) - - describe('with extended: true', function () { - it('should reject 0', function () { - assert.throws(createApp.bind(null, { extended: true, parameterLimit: 0 }), - /TypeError: option parameterLimit must be a positive number/) - }) - - it('should reject string', function () { - assert.throws(createApp.bind(null, { extended: true, parameterLimit: 'beep' }), - /TypeError: option parameterLimit must be a positive number/) - }) - - it('should 413 if over limit', function (done) { - request(createApp({ extended: true, parameterLimit: 10 })) - .post('/') - .set('Content-Type', 'application/x-www-form-urlencoded') - .send(createManyParams(11)) - .expect(413, '[parameters.too.many] too many parameters', done) - }) - - it('should work when at the limit', function (done) { - request(createApp({ extended: true, parameterLimit: 10 })) - .post('/') - .set('Content-Type', 'application/x-www-form-urlencoded') - .send(createManyParams(10)) - .expect(expectKeyCount(10)) - .expect(200, done) - }) - - it('should work if number is floating point', function (done) { - request(createApp({ extended: true, parameterLimit: 10.1 })) - .post('/') - .set('Content-Type', 'application/x-www-form-urlencoded') - .send(createManyParams(11)) - .expect(413, /too many parameters/, done) - }) - - it('should work with large limit', function (done) { - request(createApp({ extended: true, parameterLimit: 5000 })) - .post('/') - .set('Content-Type', 'application/x-www-form-urlencoded') - .send(createManyParams(5000)) - .expect(expectKeyCount(5000)) - .expect(200, done) - }) - - it('should work with Infinity limit', function (done) { - request(createApp({ extended: true, parameterLimit: Infinity })) - .post('/') - .set('Content-Type', 'application/x-www-form-urlencoded') - .send(createManyParams(10000)) - .expect(expectKeyCount(10000)) - .expect(200, done) - }) - }) - }) - - describe('with type option', function () { - describe('when "application/vnd.x-www-form-urlencoded"', function () { - before(function () { - this.app = createApp({ type: 'application/vnd.x-www-form-urlencoded' }) - }) - - it('should parse for custom type', function (done) { - request(this.app) - .post('/') - .set('Content-Type', 'application/vnd.x-www-form-urlencoded') - .send('user=tobi') - .expect(200, '{"user":"tobi"}', done) - }) - - it('should ignore standard type', function (done) { - request(this.app) - .post('/') - .set('Content-Type', 'application/x-www-form-urlencoded') - .send('user=tobi') - .expect(200, '{}', done) - }) - }) - - describe('when ["urlencoded", "application/x-pairs"]', function () { - before(function () { - this.app = createApp({ - type: ['urlencoded', 'application/x-pairs'] - }) - }) - - it('should parse "application/x-www-form-urlencoded"', function (done) { - request(this.app) - .post('/') - .set('Content-Type', 'application/x-www-form-urlencoded') - .send('user=tobi') - .expect(200, '{"user":"tobi"}', done) - }) - - it('should parse "application/x-pairs"', function (done) { - request(this.app) - .post('/') - .set('Content-Type', 'application/x-pairs') - .send('user=tobi') - .expect(200, '{"user":"tobi"}', done) - }) - - it('should ignore application/x-foo', function (done) { - request(this.app) - .post('/') - .set('Content-Type', 'application/x-foo') - .send('user=tobi') - .expect(200, '{}', done) - }) - }) - - describe('when a function', function () { - it('should parse when truthy value returned', function (done) { - var app = createApp({ type: accept }) - - function accept (req) { - return req.headers['content-type'] === 'application/vnd.something' - } - - request(app) - .post('/') - .set('Content-Type', 'application/vnd.something') - .send('user=tobi') - .expect(200, '{"user":"tobi"}', done) - }) - - it('should work without content-type', function (done) { - var app = createApp({ type: accept }) - - function accept (req) { - return true - } - - var test = request(app).post('/') - test.write('user=tobi') - test.expect(200, '{"user":"tobi"}', done) - }) - - it('should not invoke without a body', function (done) { - var app = createApp({ type: accept }) - - function accept (req) { - throw new Error('oops!') - } - - request(app) - .get('/') - .expect(404, done) - }) - }) - }) - - describe('with verify option', function () { - it('should assert value if function', function () { - assert.throws(createApp.bind(null, { verify: 'lol' }), - /TypeError: option verify must be function/) - }) - - it('should error from verify', function (done) { - var app = createApp({ - verify: function (req, res, buf) { - if (buf[0] === 0x20) throw new Error('no leading space') - } - }) - - request(app) - .post('/') - .set('Content-Type', 'application/x-www-form-urlencoded') - .send(' user=tobi') - .expect(403, '[entity.verify.failed] no leading space', done) - }) - - it('should allow custom codes', function (done) { - var app = createApp({ - verify: function (req, res, buf) { - if (buf[0] !== 0x20) return - var err = new Error('no leading space') - err.status = 400 - throw err - } - }) - - request(app) - .post('/') - .set('Content-Type', 'application/x-www-form-urlencoded') - .send(' user=tobi') - .expect(400, '[entity.verify.failed] no leading space', done) - }) - - it('should allow custom type', function (done) { - var app = createApp({ - verify: function (req, res, buf) { - if (buf[0] !== 0x20) return - var err = new Error('no leading space') - err.type = 'foo.bar' - throw err - } - }) - - request(app) - .post('/') - .set('Content-Type', 'application/x-www-form-urlencoded') - .send(' user=tobi') - .expect(403, '[foo.bar] no leading space', done) - }) - - it('should allow pass-through', function (done) { - var app = createApp({ - verify: function (req, res, buf) { - if (buf[0] === 0x5b) throw new Error('no arrays') - } - }) - - request(app) - .post('/') - .set('Content-Type', 'application/x-www-form-urlencoded') - .send('user=tobi') - .expect(200, '{"user":"tobi"}', done) - }) - - it('should 415 on unknown charset prior to verify', function (done) { - var app = createApp({ - verify: function (req, res, buf) { - throw new Error('unexpected verify call') - } - }) - - var test = request(app).post('/') - test.set('Content-Type', 'application/x-www-form-urlencoded; charset=x-bogus') - test.write(Buffer.from('00000000', 'hex')) - test.expect(415, '[charset.unsupported] unsupported charset "X-BOGUS"', done) - }) - }) - - describeAsyncHooks('async local storage', function () { - before(function () { - var app = express() - var store = { foo: 'bar' } - - app.use(function (req, res, next) { - req.asyncLocalStorage = new asyncHooks.AsyncLocalStorage() - req.asyncLocalStorage.run(store, next) - }) - - app.use(express.urlencoded()) - - app.use(function (req, res, next) { - var local = req.asyncLocalStorage.getStore() - - if (local) { - res.setHeader('x-store-foo', String(local.foo)) - } - - next() - }) - - app.use(function (err, req, res, next) { - var local = req.asyncLocalStorage.getStore() - - if (local) { - res.setHeader('x-store-foo', String(local.foo)) - } - - res.status(err.status || 500) - res.send('[' + err.type + '] ' + err.message) - }) - - app.post('/', function (req, res) { - res.json(req.body) - }) - - this.app = app - }) - - it('should presist store', function (done) { - request(this.app) - .post('/') - .set('Content-Type', 'application/x-www-form-urlencoded') - .send('user=tobi') - .expect(200) - .expect('x-store-foo', 'bar') - .expect('{"user":"tobi"}') - .end(done) - }) - - it('should presist store when unmatched content-type', function (done) { - request(this.app) - .post('/') - .set('Content-Type', 'application/fizzbuzz') - .send('buzz') - .expect(200) - .expect('x-store-foo', 'bar') - .expect('{}') - .end(done) - }) - - it('should presist store when inflated', function (done) { - var test = request(this.app).post('/') - test.set('Content-Encoding', 'gzip') - test.set('Content-Type', 'application/x-www-form-urlencoded') - test.write(Buffer.from('1f8b080000000000000bcb4bcc4db57db16e170099a4bad608000000', 'hex')) - test.expect(200) - test.expect('x-store-foo', 'bar') - test.expect('{"name":"论"}') - test.end(done) - }) - - it('should presist store when inflate error', function (done) { - var test = request(this.app).post('/') - test.set('Content-Encoding', 'gzip') - test.set('Content-Type', 'application/x-www-form-urlencoded') - test.write(Buffer.from('1f8b080000000000000bcb4bcc4db57db16e170099a4bad6080000', 'hex')) - test.expect(400) - test.expect('x-store-foo', 'bar') - test.end(done) - }) - - it('should presist store when limit exceeded', function (done) { - request(this.app) - .post('/') - .set('Content-Type', 'application/x-www-form-urlencoded') - .send('user=' + Buffer.alloc(1024 * 100, '.').toString()) - .expect(413) - .expect('x-store-foo', 'bar') - .end(done) - }) - }) - - describe('charset', function () { - before(function () { - this.app = createApp() - }) - - it('should parse utf-8', function (done) { - var test = request(this.app).post('/') - test.set('Content-Type', 'application/x-www-form-urlencoded; charset=utf-8') - test.write(Buffer.from('6e616d653de8aeba', 'hex')) - test.expect(200, '{"name":"论"}', done) - }) - - it('should parse when content-length != char length', function (done) { - var test = request(this.app).post('/') - test.set('Content-Type', 'application/x-www-form-urlencoded; charset=utf-8') - test.set('Content-Length', '7') - test.write(Buffer.from('746573743dc3a5', 'hex')) - test.expect(200, '{"test":"å"}', done) - }) - - it('should default to utf-8', function (done) { - var test = request(this.app).post('/') - test.set('Content-Type', 'application/x-www-form-urlencoded') - test.write(Buffer.from('6e616d653de8aeba', 'hex')) - test.expect(200, '{"name":"论"}', done) - }) - - it('should fail on unknown charset', function (done) { - var test = request(this.app).post('/') - test.set('Content-Type', 'application/x-www-form-urlencoded; charset=koi8-r') - test.write(Buffer.from('6e616d653dcec5d4', 'hex')) - test.expect(415, '[charset.unsupported] unsupported charset "KOI8-R"', done) - }) - }) - - describe('encoding', function () { - before(function () { - this.app = createApp({ limit: '10kb' }) - }) - - it('should parse without encoding', function (done) { - var test = request(this.app).post('/') - test.set('Content-Type', 'application/x-www-form-urlencoded') - test.write(Buffer.from('6e616d653de8aeba', 'hex')) - test.expect(200, '{"name":"论"}', done) - }) - - it('should support identity encoding', function (done) { - var test = request(this.app).post('/') - test.set('Content-Encoding', 'identity') - test.set('Content-Type', 'application/x-www-form-urlencoded') - test.write(Buffer.from('6e616d653de8aeba', 'hex')) - test.expect(200, '{"name":"论"}', done) - }) - - it('should support gzip encoding', function (done) { - var test = request(this.app).post('/') - test.set('Content-Encoding', 'gzip') - test.set('Content-Type', 'application/x-www-form-urlencoded') - test.write(Buffer.from('1f8b080000000000000bcb4bcc4db57db16e170099a4bad608000000', 'hex')) - test.expect(200, '{"name":"论"}', done) - }) - - it('should support deflate encoding', function (done) { - var test = request(this.app).post('/') - test.set('Content-Encoding', 'deflate') - test.set('Content-Type', 'application/x-www-form-urlencoded') - test.write(Buffer.from('789ccb4bcc4db57db16e17001068042f', 'hex')) - test.expect(200, '{"name":"论"}', done) - }) - - it('should be case-insensitive', function (done) { - var test = request(this.app).post('/') - test.set('Content-Encoding', 'GZIP') - test.set('Content-Type', 'application/x-www-form-urlencoded') - test.write(Buffer.from('1f8b080000000000000bcb4bcc4db57db16e170099a4bad608000000', 'hex')) - test.expect(200, '{"name":"论"}', done) - }) - - it('should 415 on unknown encoding', function (done) { - var test = request(this.app).post('/') - test.set('Content-Encoding', 'nulls') - test.set('Content-Type', 'application/x-www-form-urlencoded') - test.write(Buffer.from('000000000000', 'hex')) - test.expect(415, '[encoding.unsupported] unsupported content encoding "nulls"', done) - }) - }) -}) - -function createManyParams (count) { - var str = '' - - if (count === 0) { - return str - } - - str += '0=0' - - for (var i = 1; i < count; i++) { - var n = i.toString(36) - str += '&' + n + '=' + n - } - - return str -} - -function createApp (options) { - var app = express() - - app.use(express.urlencoded(options)) - - app.use(function (err, req, res, next) { - res.status(err.status || 500) - res.send(String(req.headers['x-error-property'] - ? err[req.headers['x-error-property']] - : ('[' + err.type + '] ' + err.message))) - }) - - app.post('/', function (req, res) { - res.json(req.body) - }) - - return app -} - -function expectKeyCount (count) { - return function (res) { - assert.strictEqual(Object.keys(JSON.parse(res.text)).length, count) - } -} - -function tryRequire (name) { - try { - return require(name) - } catch (e) { - return {} - } -} diff --git a/test/middleware.basic.js b/test/middleware.basic.js deleted file mode 100644 index 19f00d9a..00000000 --- a/test/middleware.basic.js +++ /dev/null @@ -1,42 +0,0 @@ -'use strict' - -var assert = require('assert') -var express = require('../'); -var request = require('supertest'); - -describe('middleware', function(){ - describe('.next()', function(){ - it('should behave like connect', function(done){ - var app = express() - , calls = []; - - app.use(function(req, res, next){ - calls.push('one'); - next(); - }); - - app.use(function(req, res, next){ - calls.push('two'); - next(); - }); - - app.use(function(req, res){ - var buf = ''; - res.setHeader('Content-Type', 'application/json'); - req.setEncoding('utf8'); - req.on('data', function(chunk){ buf += chunk }); - req.on('end', function(){ - res.end(buf); - }); - }); - - request(app) - .get('/') - .set('Content-Type', 'application/json') - .send('{"foo":"bar"}') - .expect('Content-Type', 'application/json') - .expect(function () { assert.deepEqual(calls, ['one', 'two']) }) - .expect(200, '{"foo":"bar"}', done) - }) - }) -}) diff --git a/test/regression.js b/test/regression.js deleted file mode 100644 index 4e99b306..00000000 --- a/test/regression.js +++ /dev/null @@ -1,20 +0,0 @@ -'use strict' - -var express = require('../') - , request = require('supertest'); - -describe('throw after .end()', function(){ - it('should fail gracefully', function(done){ - var app = express(); - - app.get('/', function(req, res){ - res.end('yay'); - throw new Error('boom'); - }); - - request(app) - .get('/') - .expect('yay') - .expect(200, done); - }) -}) diff --git a/test/req.accepts.js b/test/req.accepts.js deleted file mode 100644 index 2066fb51..00000000 --- a/test/req.accepts.js +++ /dev/null @@ -1,125 +0,0 @@ -'use strict' - -var express = require('../') - , request = require('supertest'); - -describe('req', function(){ - describe('.accepts(type)', function(){ - it('should return true when Accept is not present', function(done){ - var app = express(); - - app.use(function(req, res, next){ - res.end(req.accepts('json') ? 'yes' : 'no'); - }); - - request(app) - .get('/') - .expect('yes', done); - }) - - it('should return true when present', function(done){ - var app = express(); - - app.use(function(req, res, next){ - res.end(req.accepts('json') ? 'yes' : 'no'); - }); - - request(app) - .get('/') - .set('Accept', 'application/json') - .expect('yes', done); - }) - - it('should return false otherwise', function(done){ - var app = express(); - - app.use(function(req, res, next){ - res.end(req.accepts('json') ? 'yes' : 'no'); - }); - - request(app) - .get('/') - .set('Accept', 'text/html') - .expect('no', done); - }) - }) - - it('should accept an argument list of type names', function(done){ - var app = express(); - - app.use(function(req, res, next){ - res.end(req.accepts('json', 'html')); - }); - - request(app) - .get('/') - .set('Accept', 'application/json') - .expect('json', done); - }) - - describe('.accepts(types)', function(){ - it('should return the first when Accept is not present', function(done){ - var app = express(); - - app.use(function(req, res, next){ - res.end(req.accepts(['json', 'html'])); - }); - - request(app) - .get('/') - .expect('json', done); - }) - - it('should return the first acceptable type', function(done){ - var app = express(); - - app.use(function(req, res, next){ - res.end(req.accepts(['json', 'html'])); - }); - - request(app) - .get('/') - .set('Accept', 'text/html') - .expect('html', done); - }) - - it('should return false when no match is made', function(done){ - var app = express(); - - app.use(function(req, res, next){ - res.end(req.accepts(['text/html', 'application/json']) ? 'yup' : 'nope'); - }); - - request(app) - .get('/') - .set('Accept', 'foo/bar, bar/baz') - .expect('nope', done); - }) - - it('should take quality into account', function(done){ - var app = express(); - - app.use(function(req, res, next){ - res.end(req.accepts(['text/html', 'application/json'])); - }); - - request(app) - .get('/') - .set('Accept', '*/html; q=.5, application/json') - .expect('application/json', done); - }) - - it('should return the first acceptable type with canonical mime types', function(done){ - var app = express(); - - app.use(function(req, res, next){ - res.end(req.accepts(['application/json', 'text/html'])); - }); - - request(app) - .get('/') - .set('Accept', '*/html') - .expect('text/html', done); - }) - }) -}) diff --git a/test/req.acceptsCharset.js b/test/req.acceptsCharset.js deleted file mode 100644 index 6dbab439..00000000 --- a/test/req.acceptsCharset.js +++ /dev/null @@ -1,50 +0,0 @@ -'use strict' - -var express = require('../') - , request = require('supertest'); - -describe('req', function(){ - describe('.acceptsCharset(type)', function(){ - describe('when Accept-Charset is not present', function(){ - it('should return true', function(done){ - var app = express(); - - app.use(function(req, res, next){ - res.end(req.acceptsCharset('utf-8') ? 'yes' : 'no'); - }); - - request(app) - .get('/') - .expect('yes', done); - }) - }) - - describe('when Accept-Charset is present', function () { - it('should return true', function (done) { - var app = express(); - - app.use(function(req, res, next){ - res.end(req.acceptsCharset('utf-8') ? 'yes' : 'no'); - }); - - request(app) - .get('/') - .set('Accept-Charset', 'foo, bar, utf-8') - .expect('yes', done); - }) - - it('should return false otherwise', function(done){ - var app = express(); - - app.use(function(req, res, next){ - res.end(req.acceptsCharset('utf-8') ? 'yes' : 'no'); - }); - - request(app) - .get('/') - .set('Accept-Charset', 'foo, bar') - .expect('no', done); - }) - }) - }) -}) diff --git a/test/req.acceptsCharsets.js b/test/req.acceptsCharsets.js deleted file mode 100644 index 360a9878..00000000 --- a/test/req.acceptsCharsets.js +++ /dev/null @@ -1,50 +0,0 @@ -'use strict' - -var express = require('../') - , request = require('supertest'); - -describe('req', function(){ - describe('.acceptsCharsets(type)', function(){ - describe('when Accept-Charset is not present', function(){ - it('should return true', function(done){ - var app = express(); - - app.use(function(req, res, next){ - res.end(req.acceptsCharsets('utf-8') ? 'yes' : 'no'); - }); - - request(app) - .get('/') - .expect('yes', done); - }) - }) - - describe('when Accept-Charset is present', function () { - it('should return true', function (done) { - var app = express(); - - app.use(function(req, res, next){ - res.end(req.acceptsCharsets('utf-8') ? 'yes' : 'no'); - }); - - request(app) - .get('/') - .set('Accept-Charset', 'foo, bar, utf-8') - .expect('yes', done); - }) - - it('should return false otherwise', function(done){ - var app = express(); - - app.use(function(req, res, next){ - res.end(req.acceptsCharsets('utf-8') ? 'yes' : 'no'); - }); - - request(app) - .get('/') - .set('Accept-Charset', 'foo, bar') - .expect('no', done); - }) - }) - }) -}) diff --git a/test/req.acceptsEncoding.js b/test/req.acceptsEncoding.js deleted file mode 100644 index bcec2280..00000000 --- a/test/req.acceptsEncoding.js +++ /dev/null @@ -1,39 +0,0 @@ -'use strict' - -var express = require('../') - , request = require('supertest'); - -describe('req', function(){ - describe('.acceptsEncoding', function(){ - it('should return encoding if accepted', function (done) { - var app = express(); - - app.get('/', function (req, res) { - res.send({ - gzip: req.acceptsEncoding('gzip'), - deflate: req.acceptsEncoding('deflate') - }) - }) - - request(app) - .get('/') - .set('Accept-Encoding', ' gzip, deflate') - .expect(200, { gzip: 'gzip', deflate: 'deflate' }, done) - }) - - it('should be false if encoding not accepted', function(done){ - var app = express(); - - app.get('/', function (req, res) { - res.send({ - bogus: req.acceptsEncoding('bogus') - }) - }) - - request(app) - .get('/') - .set('Accept-Encoding', ' gzip, deflate') - .expect(200, { bogus: false }, done) - }) - }) -}) diff --git a/test/req.acceptsEncodings.js b/test/req.acceptsEncodings.js deleted file mode 100644 index 9f8973cd..00000000 --- a/test/req.acceptsEncodings.js +++ /dev/null @@ -1,39 +0,0 @@ -'use strict' - -var express = require('../') - , request = require('supertest'); - -describe('req', function(){ - describe('.acceptsEncodings', function () { - it('should return encoding if accepted', function (done) { - var app = express(); - - app.get('/', function (req, res) { - res.send({ - gzip: req.acceptsEncodings('gzip'), - deflate: req.acceptsEncodings('deflate') - }) - }) - - request(app) - .get('/') - .set('Accept-Encoding', ' gzip, deflate') - .expect(200, { gzip: 'gzip', deflate: 'deflate' }, done) - }) - - it('should be false if encoding not accepted', function(done){ - var app = express(); - - app.get('/', function (req, res) { - res.send({ - bogus: req.acceptsEncodings('bogus') - }) - }) - - request(app) - .get('/') - .set('Accept-Encoding', ' gzip, deflate') - .expect(200, { bogus: false }, done) - }) - }) -}) diff --git a/test/req.acceptsLanguage.js b/test/req.acceptsLanguage.js deleted file mode 100644 index 39bd73c4..00000000 --- a/test/req.acceptsLanguage.js +++ /dev/null @@ -1,57 +0,0 @@ -'use strict' - -var express = require('../') - , request = require('supertest'); - -describe('req', function(){ - describe('.acceptsLanguage', function(){ - it('should return language if accepted', function (done) { - var app = express(); - - app.get('/', function (req, res) { - res.send({ - 'en-us': req.acceptsLanguage('en-us'), - en: req.acceptsLanguage('en') - }) - }) - - request(app) - .get('/') - .set('Accept-Language', 'en;q=.5, en-us') - .expect(200, { 'en-us': 'en-us', en: 'en' }, done) - }) - - it('should be false if language not accepted', function(done){ - var app = express(); - - app.get('/', function (req, res) { - res.send({ - es: req.acceptsLanguage('es') - }) - }) - - request(app) - .get('/') - .set('Accept-Language', 'en;q=.5, en-us') - .expect(200, { es: false }, done) - }) - - describe('when Accept-Language is not present', function(){ - it('should always return language', function (done) { - var app = express(); - - app.get('/', function (req, res) { - res.send({ - en: req.acceptsLanguage('en'), - es: req.acceptsLanguage('es'), - jp: req.acceptsLanguage('jp') - }) - }) - - request(app) - .get('/') - .expect(200, { en: 'en', es: 'es', jp: 'jp' }, done) - }) - }) - }) -}) diff --git a/test/req.acceptsLanguages.js b/test/req.acceptsLanguages.js deleted file mode 100644 index e5629fbc..00000000 --- a/test/req.acceptsLanguages.js +++ /dev/null @@ -1,57 +0,0 @@ -'use strict' - -var express = require('../') - , request = require('supertest'); - -describe('req', function(){ - describe('.acceptsLanguages', function(){ - it('should return language if accepted', function (done) { - var app = express(); - - app.get('/', function (req, res) { - res.send({ - 'en-us': req.acceptsLanguages('en-us'), - en: req.acceptsLanguages('en') - }) - }) - - request(app) - .get('/') - .set('Accept-Language', 'en;q=.5, en-us') - .expect(200, { 'en-us': 'en-us', en: 'en' }, done) - }) - - it('should be false if language not accepted', function(done){ - var app = express(); - - app.get('/', function (req, res) { - res.send({ - es: req.acceptsLanguages('es') - }) - }) - - request(app) - .get('/') - .set('Accept-Language', 'en;q=.5, en-us') - .expect(200, { es: false }, done) - }) - - describe('when Accept-Language is not present', function(){ - it('should always return language', function (done) { - var app = express(); - - app.get('/', function (req, res) { - res.send({ - en: req.acceptsLanguages('en'), - es: req.acceptsLanguages('es'), - jp: req.acceptsLanguages('jp') - }) - }) - - request(app) - .get('/') - .expect(200, { en: 'en', es: 'es', jp: 'jp' }, done) - }) - }) - }) -}) diff --git a/test/req.baseUrl.js b/test/req.baseUrl.js deleted file mode 100644 index b70803ea..00000000 --- a/test/req.baseUrl.js +++ /dev/null @@ -1,88 +0,0 @@ -'use strict' - -var express = require('..') -var request = require('supertest') - -describe('req', function(){ - describe('.baseUrl', function(){ - it('should be empty for top-level route', function(done){ - var app = express() - - app.get('/:a', function(req, res){ - res.end(req.baseUrl) - }) - - request(app) - .get('/foo') - .expect(200, '', done) - }) - - it('should contain lower path', function(done){ - var app = express() - var sub = express.Router() - - sub.get('/:b', function(req, res){ - res.end(req.baseUrl) - }) - app.use('/:a', sub) - - request(app) - .get('/foo/bar') - .expect(200, '/foo', done); - }) - - it('should contain full lower path', function(done){ - var app = express() - var sub1 = express.Router() - var sub2 = express.Router() - var sub3 = express.Router() - - sub3.get('/:d', function(req, res){ - res.end(req.baseUrl) - }) - sub2.use('/:c', sub3) - sub1.use('/:b', sub2) - app.use('/:a', sub1) - - request(app) - .get('/foo/bar/baz/zed') - .expect(200, '/foo/bar/baz', done); - }) - - it('should travel through routers correctly', function(done){ - var urls = [] - var app = express() - var sub1 = express.Router() - var sub2 = express.Router() - var sub3 = express.Router() - - sub3.get('/:d', function(req, res, next){ - urls.push('0@' + req.baseUrl) - next() - }) - sub2.use('/:c', sub3) - sub1.use('/', function(req, res, next){ - urls.push('1@' + req.baseUrl) - next() - }) - sub1.use('/bar', sub2) - sub1.use('/bar', function(req, res, next){ - urls.push('2@' + req.baseUrl) - next() - }) - app.use(function(req, res, next){ - urls.push('3@' + req.baseUrl) - next() - }) - app.use('/:a', sub1) - app.use(function(req, res, next){ - urls.push('4@' + req.baseUrl) - res.end(urls.join(',')) - }) - - request(app) - .get('/foo/bar/baz/zed') - .expect(200, '3@,1@/foo,0@/foo/bar/baz,2@/foo/bar,4@', done); - }) - }) -}) diff --git a/test/req.fresh.js b/test/req.fresh.js deleted file mode 100644 index 9160e2ca..00000000 --- a/test/req.fresh.js +++ /dev/null @@ -1,50 +0,0 @@ -'use strict' - -var express = require('../') - , request = require('supertest'); - -describe('req', function(){ - describe('.fresh', function(){ - it('should return true when the resource is not modified', function(done){ - var app = express(); - var etag = '"12345"'; - - app.use(function(req, res){ - res.set('ETag', etag); - res.send(req.fresh); - }); - - request(app) - .get('/') - .set('If-None-Match', etag) - .expect(304, done); - }) - - it('should return false when the resource is modified', function(done){ - var app = express(); - - app.use(function(req, res){ - res.set('ETag', '"123"'); - res.send(req.fresh); - }); - - request(app) - .get('/') - .set('If-None-Match', '"12345"') - .expect(200, 'false', done); - }) - - it('should return false without response headers', function(done){ - var app = express(); - - app.disable('x-powered-by') - app.use(function(req, res){ - res.send(req.fresh); - }); - - request(app) - .get('/') - .expect(200, 'false', done); - }) - }) -}) diff --git a/test/req.get.js b/test/req.get.js deleted file mode 100644 index 16589b3f..00000000 --- a/test/req.get.js +++ /dev/null @@ -1,60 +0,0 @@ -'use strict' - -var express = require('../') - , request = require('supertest') - , assert = require('assert'); - -describe('req', function(){ - describe('.get(field)', function(){ - it('should return the header field value', function(done){ - var app = express(); - - app.use(function(req, res){ - assert(req.get('Something-Else') === undefined); - res.end(req.get('Content-Type')); - }); - - request(app) - .post('/') - .set('Content-Type', 'application/json') - .expect('application/json', done); - }) - - it('should special-case Referer', function(done){ - var app = express(); - - app.use(function(req, res){ - res.end(req.get('Referer')); - }); - - request(app) - .post('/') - .set('Referrer', 'http://foobar.com') - .expect('http://foobar.com', done); - }) - - it('should throw missing header name', function (done) { - var app = express() - - app.use(function (req, res) { - res.end(req.get()) - }) - - request(app) - .get('/') - .expect(500, /TypeError: name argument is required to req.get/, done) - }) - - it('should throw for non-string header name', function (done) { - var app = express() - - app.use(function (req, res) { - res.end(req.get(42)) - }) - - request(app) - .get('/') - .expect(500, /TypeError: name must be a string to req.get/, done) - }) - }) -}) diff --git a/test/req.host.js b/test/req.host.js deleted file mode 100644 index 2c051fb9..00000000 --- a/test/req.host.js +++ /dev/null @@ -1,156 +0,0 @@ -'use strict' - -var express = require('../') - , request = require('supertest') - -describe('req', function(){ - describe('.host', function(){ - it('should return the Host when present', function(done){ - var app = express(); - - app.use(function(req, res){ - res.end(req.host); - }); - - request(app) - .post('/') - .set('Host', 'example.com') - .expect('example.com', done); - }) - - it('should strip port number', function(done){ - var app = express(); - - app.use(function(req, res){ - res.end(req.host); - }); - - request(app) - .post('/') - .set('Host', 'example.com:3000') - .expect('example.com', done); - }) - - it('should return undefined otherwise', function(done){ - var app = express(); - - app.use(function(req, res){ - req.headers.host = null; - res.end(String(req.host)); - }); - - request(app) - .post('/') - .expect('undefined', done); - }) - - it('should work with IPv6 Host', function(done){ - var app = express(); - - app.use(function(req, res){ - res.end(req.host); - }); - - request(app) - .post('/') - .set('Host', '[::1]') - .expect('[::1]', done); - }) - - it('should work with IPv6 Host and port', function(done){ - var app = express(); - - app.use(function(req, res){ - res.end(req.host); - }); - - request(app) - .post('/') - .set('Host', '[::1]:3000') - .expect('[::1]', done); - }) - - describe('when "trust proxy" is enabled', function(){ - it('should respect X-Forwarded-Host', function(done){ - var app = express(); - - app.enable('trust proxy'); - - app.use(function(req, res){ - res.end(req.host); - }); - - request(app) - .get('/') - .set('Host', 'localhost') - .set('X-Forwarded-Host', 'example.com') - .expect('example.com', done); - }) - - it('should ignore X-Forwarded-Host if socket addr not trusted', function(done){ - var app = express(); - - app.set('trust proxy', '10.0.0.1'); - - app.use(function(req, res){ - res.end(req.host); - }); - - request(app) - .get('/') - .set('Host', 'localhost') - .set('X-Forwarded-Host', 'example.com') - .expect('localhost', done); - }) - - it('should default to Host', function(done){ - var app = express(); - - app.enable('trust proxy'); - - app.use(function(req, res){ - res.end(req.host); - }); - - request(app) - .get('/') - .set('Host', 'example.com') - .expect('example.com', done); - }) - - describe('when trusting hop count', function () { - it('should respect X-Forwarded-Host', function (done) { - var app = express(); - - app.set('trust proxy', 1); - - app.use(function (req, res) { - res.end(req.host); - }); - - request(app) - .get('/') - .set('Host', 'localhost') - .set('X-Forwarded-Host', 'example.com') - .expect('example.com', done); - }) - }) - }) - - describe('when "trust proxy" is disabled', function(){ - it('should ignore X-Forwarded-Host', function(done){ - var app = express(); - - app.use(function(req, res){ - res.end(req.host); - }); - - request(app) - .get('/') - .set('Host', 'localhost') - .set('X-Forwarded-Host', 'evil') - .expect('localhost', done); - }) - }) - }) -}) diff --git a/test/req.hostname.js b/test/req.hostname.js deleted file mode 100644 index b3716b56..00000000 --- a/test/req.hostname.js +++ /dev/null @@ -1,188 +0,0 @@ -'use strict' - -var express = require('../') - , request = require('supertest') - -describe('req', function(){ - describe('.hostname', function(){ - it('should return the Host when present', function(done){ - var app = express(); - - app.use(function(req, res){ - res.end(req.hostname); - }); - - request(app) - .post('/') - .set('Host', 'example.com') - .expect('example.com', done); - }) - - it('should strip port number', function(done){ - var app = express(); - - app.use(function(req, res){ - res.end(req.hostname); - }); - - request(app) - .post('/') - .set('Host', 'example.com:3000') - .expect('example.com', done); - }) - - it('should return undefined otherwise', function(done){ - var app = express(); - - app.use(function(req, res){ - req.headers.host = null; - res.end(String(req.hostname)); - }); - - request(app) - .post('/') - .expect('undefined', done); - }) - - it('should work with IPv6 Host', function(done){ - var app = express(); - - app.use(function(req, res){ - res.end(req.hostname); - }); - - request(app) - .post('/') - .set('Host', '[::1]') - .expect('[::1]', done); - }) - - it('should work with IPv6 Host and port', function(done){ - var app = express(); - - app.use(function(req, res){ - res.end(req.hostname); - }); - - request(app) - .post('/') - .set('Host', '[::1]:3000') - .expect('[::1]', done); - }) - - describe('when "trust proxy" is enabled', function(){ - it('should respect X-Forwarded-Host', function(done){ - var app = express(); - - app.enable('trust proxy'); - - app.use(function(req, res){ - res.end(req.hostname); - }); - - request(app) - .get('/') - .set('Host', 'localhost') - .set('X-Forwarded-Host', 'example.com:3000') - .expect('example.com', done); - }) - - it('should ignore X-Forwarded-Host if socket addr not trusted', function(done){ - var app = express(); - - app.set('trust proxy', '10.0.0.1'); - - app.use(function(req, res){ - res.end(req.hostname); - }); - - request(app) - .get('/') - .set('Host', 'localhost') - .set('X-Forwarded-Host', 'example.com') - .expect('localhost', done); - }) - - it('should default to Host', function(done){ - var app = express(); - - app.enable('trust proxy'); - - app.use(function(req, res){ - res.end(req.hostname); - }); - - request(app) - .get('/') - .set('Host', 'example.com') - .expect('example.com', done); - }) - - describe('when multiple X-Forwarded-Host', function () { - it('should use the first value', function (done) { - var app = express() - - app.enable('trust proxy') - - app.use(function (req, res) { - res.send(req.hostname) - }) - - request(app) - .get('/') - .set('Host', 'localhost') - .set('X-Forwarded-Host', 'example.com, foobar.com') - .expect(200, 'example.com', done) - }) - - it('should remove OWS around comma', function (done) { - var app = express() - - app.enable('trust proxy') - - app.use(function (req, res) { - res.send(req.hostname) - }) - - request(app) - .get('/') - .set('Host', 'localhost') - .set('X-Forwarded-Host', 'example.com , foobar.com') - .expect(200, 'example.com', done) - }) - - it('should strip port number', function (done) { - var app = express() - - app.enable('trust proxy') - - app.use(function (req, res) { - res.send(req.hostname) - }) - - request(app) - .get('/') - .set('Host', 'localhost') - .set('X-Forwarded-Host', 'example.com:8080 , foobar.com:8888') - .expect(200, 'example.com', done) - }) - }) - }) - - describe('when "trust proxy" is disabled', function(){ - it('should ignore X-Forwarded-Host', function(done){ - var app = express(); - - app.use(function(req, res){ - res.end(req.hostname); - }); - - request(app) - .get('/') - .set('Host', 'localhost') - .set('X-Forwarded-Host', 'evil') - .expect('localhost', done); - }) - }) - }) -}) diff --git a/test/req.ip.js b/test/req.ip.js deleted file mode 100644 index 6bb3c5ac..00000000 --- a/test/req.ip.js +++ /dev/null @@ -1,113 +0,0 @@ -'use strict' - -var express = require('../') - , request = require('supertest'); - -describe('req', function(){ - describe('.ip', function(){ - describe('when X-Forwarded-For is present', function(){ - describe('when "trust proxy" is enabled', function(){ - it('should return the client addr', function(done){ - var app = express(); - - app.enable('trust proxy'); - - app.use(function(req, res, next){ - res.send(req.ip); - }); - - request(app) - .get('/') - .set('X-Forwarded-For', 'client, p1, p2') - .expect('client', done); - }) - - it('should return the addr after trusted proxy based on count', function (done) { - var app = express(); - - app.set('trust proxy', 2); - - app.use(function(req, res, next){ - res.send(req.ip); - }); - - request(app) - .get('/') - .set('X-Forwarded-For', 'client, p1, p2') - .expect('p1', done); - }) - - it('should return the addr after trusted proxy based on list', function (done) { - var app = express() - - app.set('trust proxy', '10.0.0.1, 10.0.0.2, 127.0.0.1, ::1') - - app.get('/', function (req, res) { - res.send(req.ip) - }) - - request(app) - .get('/') - .set('X-Forwarded-For', '10.0.0.2, 10.0.0.3, 10.0.0.1', '10.0.0.4') - .expect('10.0.0.3', done) - }) - - it('should return the addr after trusted proxy, from sub app', function (done) { - var app = express(); - var sub = express(); - - app.set('trust proxy', 2); - app.use(sub); - - sub.use(function (req, res, next) { - res.send(req.ip); - }); - - request(app) - .get('/') - .set('X-Forwarded-For', 'client, p1, p2') - .expect(200, 'p1', done); - }) - }) - - describe('when "trust proxy" is disabled', function(){ - it('should return the remote address', function(done){ - var app = express(); - - app.use(function(req, res, next){ - res.send(req.ip); - }); - - var test = request(app).get('/') - test.set('X-Forwarded-For', 'client, p1, p2') - test.expect(200, getExpectedClientAddress(test._server), done); - }) - }) - }) - - describe('when X-Forwarded-For is not present', function(){ - it('should return the remote address', function(done){ - var app = express(); - - app.enable('trust proxy'); - - app.use(function(req, res, next){ - res.send(req.ip); - }); - - var test = request(app).get('/') - test.expect(200, getExpectedClientAddress(test._server), done) - }) - }) - }) -}) - -/** - * Get the local client address depending on AF_NET of server - */ - -function getExpectedClientAddress(server) { - return server.address().address === '::' - ? '::ffff:127.0.0.1' - : '127.0.0.1'; -} diff --git a/test/req.ips.js b/test/req.ips.js deleted file mode 100644 index 2f9a0736..00000000 --- a/test/req.ips.js +++ /dev/null @@ -1,71 +0,0 @@ -'use strict' - -var express = require('../') - , request = require('supertest'); - -describe('req', function(){ - describe('.ips', function(){ - describe('when X-Forwarded-For is present', function(){ - describe('when "trust proxy" is enabled', function(){ - it('should return an array of the specified addresses', function(done){ - var app = express(); - - app.enable('trust proxy'); - - app.use(function(req, res, next){ - res.send(req.ips); - }); - - request(app) - .get('/') - .set('X-Forwarded-For', 'client, p1, p2') - .expect('["client","p1","p2"]', done); - }) - - it('should stop at first untrusted', function(done){ - var app = express(); - - app.set('trust proxy', 2); - - app.use(function(req, res, next){ - res.send(req.ips); - }); - - request(app) - .get('/') - .set('X-Forwarded-For', 'client, p1, p2') - .expect('["p1","p2"]', done); - }) - }) - - describe('when "trust proxy" is disabled', function(){ - it('should return an empty array', function(done){ - var app = express(); - - app.use(function(req, res, next){ - res.send(req.ips); - }); - - request(app) - .get('/') - .set('X-Forwarded-For', 'client, p1, p2') - .expect('[]', done); - }) - }) - }) - - describe('when X-Forwarded-For is not present', function(){ - it('should return []', function(done){ - var app = express(); - - app.use(function(req, res, next){ - res.send(req.ips); - }); - - request(app) - .get('/') - .expect('[]', done); - }) - }) - }) -}) diff --git a/test/req.is.js b/test/req.is.js deleted file mode 100644 index c5904dd6..00000000 --- a/test/req.is.js +++ /dev/null @@ -1,169 +0,0 @@ -'use strict' - -var express = require('..') -var request = require('supertest') - -describe('req.is()', function () { - describe('when given a mime type', function () { - it('should return the type when matching', function (done) { - var app = express() - - app.use(function (req, res) { - res.json(req.is('application/json')) - }) - - request(app) - .post('/') - .type('application/json') - .send('{}') - .expect(200, '"application/json"', done) - }) - - it('should return false when not matching', function (done) { - var app = express() - - app.use(function (req, res) { - res.json(req.is('image/jpeg')) - }) - - request(app) - .post('/') - .type('application/json') - .send('{}') - .expect(200, 'false', done) - }) - - it('should ignore charset', function (done) { - var app = express() - - app.use(function (req, res) { - res.json(req.is('application/json')) - }) - - request(app) - .post('/') - .type('application/json; charset=UTF-8') - .send('{}') - .expect(200, '"application/json"', done) - }) - }) - - describe('when content-type is not present', function(){ - it('should return false', function (done) { - var app = express() - - app.use(function (req, res) { - res.json(req.is('application/json')) - }) - - request(app) - .post('/') - .send('{}') - .expect(200, 'false', done) - }) - }) - - describe('when given an extension', function(){ - it('should lookup the mime type', function (done) { - var app = express() - - app.use(function (req, res) { - res.json(req.is('json')) - }) - - request(app) - .post('/') - .type('application/json') - .send('{}') - .expect(200, '"json"', done) - }) - }) - - describe('when given */subtype', function(){ - it('should return the full type when matching', function (done) { - var app = express() - - app.use(function (req, res) { - res.json(req.is('*/json')) - }) - - request(app) - .post('/') - .type('application/json') - .send('{}') - .expect(200, '"application/json"', done) - }) - - it('should return false when not matching', function (done) { - var app = express() - - app.use(function (req, res) { - res.json(req.is('*/html')) - }) - - request(app) - .post('/') - .type('application/json') - .send('{}') - .expect(200, 'false', done) - }) - - it('should ignore charset', function (done) { - var app = express() - - app.use(function (req, res) { - res.json(req.is('*/json')) - }) - - request(app) - .post('/') - .type('application/json; charset=UTF-8') - .send('{}') - .expect(200, '"application/json"', done) - }) - }) - - describe('when given type/*', function(){ - it('should return the full type when matching', function (done) { - var app = express() - - app.use(function (req, res) { - res.json(req.is('application/*')) - }) - - request(app) - .post('/') - .type('application/json') - .send('{}') - .expect(200, '"application/json"', done) - }) - - it('should return false when not matching', function (done) { - var app = express() - - app.use(function (req, res) { - res.json(req.is('text/*')) - }) - - request(app) - .post('/') - .type('application/json') - .send('{}') - .expect(200, 'false', done) - }) - - it('should ignore charset', function (done) { - var app = express() - - app.use(function (req, res) { - res.json(req.is('application/*')) - }) - - request(app) - .post('/') - .type('application/json; charset=UTF-8') - .send('{}') - .expect(200, '"application/json"', done) - }) - }) -}) diff --git a/test/req.param.js b/test/req.param.js deleted file mode 100644 index b3748c02..00000000 --- a/test/req.param.js +++ /dev/null @@ -1,61 +0,0 @@ -'use strict' - -var express = require('../') - , request = require('supertest') - -describe('req', function(){ - describe('.param(name, default)', function(){ - it('should use the default value unless defined', function(done){ - var app = express(); - - app.use(function(req, res){ - res.end(req.param('name', 'tj')); - }); - - request(app) - .get('/') - .expect('tj', done); - }) - }) - - describe('.param(name)', function(){ - it('should check req.query', function(done){ - var app = express(); - - app.use(function(req, res){ - res.end(req.param('name')); - }); - - request(app) - .get('/?name=tj') - .expect('tj', done); - }) - - it('should check req.body', function(done){ - var app = express(); - - app.use(express.json()) - - app.use(function(req, res){ - res.end(req.param('name')); - }); - - request(app) - .post('/') - .send({ name: 'tj' }) - .expect('tj', done); - }) - - it('should check req.params', function(done){ - var app = express(); - - app.get('/user/:name', function(req, res){ - res.end(req.param('filter') + req.param('name')); - }); - - request(app) - .get('/user/tj') - .expect('undefinedtj', done); - }) - }) -}) diff --git a/test/req.path.js b/test/req.path.js deleted file mode 100644 index 3ff6177c..00000000 --- a/test/req.path.js +++ /dev/null @@ -1,20 +0,0 @@ -'use strict' - -var express = require('../') - , request = require('supertest'); - -describe('req', function(){ - describe('.path', function(){ - it('should return the parsed pathname', function(done){ - var app = express(); - - app.use(function(req, res){ - res.end(req.path); - }); - - request(app) - .get('/login?redirect=/post/1/comments') - .expect('/login', done); - }) - }) -}) diff --git a/test/req.protocol.js b/test/req.protocol.js deleted file mode 100644 index 61f76356..00000000 --- a/test/req.protocol.js +++ /dev/null @@ -1,113 +0,0 @@ -'use strict' - -var express = require('../') - , request = require('supertest'); - -describe('req', function(){ - describe('.protocol', function(){ - it('should return the protocol string', function(done){ - var app = express(); - - app.use(function(req, res){ - res.end(req.protocol); - }); - - request(app) - .get('/') - .expect('http', done); - }) - - describe('when "trust proxy" is enabled', function(){ - it('should respect X-Forwarded-Proto', function(done){ - var app = express(); - - app.enable('trust proxy'); - - app.use(function(req, res){ - res.end(req.protocol); - }); - - request(app) - .get('/') - .set('X-Forwarded-Proto', 'https') - .expect('https', done); - }) - - it('should default to the socket addr if X-Forwarded-Proto not present', function(done){ - var app = express(); - - app.enable('trust proxy'); - - app.use(function(req, res){ - req.connection.encrypted = true; - res.end(req.protocol); - }); - - request(app) - .get('/') - .expect('https', done); - }) - - it('should ignore X-Forwarded-Proto if socket addr not trusted', function(done){ - var app = express(); - - app.set('trust proxy', '10.0.0.1'); - - app.use(function(req, res){ - res.end(req.protocol); - }); - - request(app) - .get('/') - .set('X-Forwarded-Proto', 'https') - .expect('http', done); - }) - - it('should default to http', function(done){ - var app = express(); - - app.enable('trust proxy'); - - app.use(function(req, res){ - res.end(req.protocol); - }); - - request(app) - .get('/') - .expect('http', done); - }) - - describe('when trusting hop count', function () { - it('should respect X-Forwarded-Proto', function (done) { - var app = express(); - - app.set('trust proxy', 1); - - app.use(function (req, res) { - res.end(req.protocol); - }); - - request(app) - .get('/') - .set('X-Forwarded-Proto', 'https') - .expect('https', done); - }) - }) - }) - - describe('when "trust proxy" is disabled', function(){ - it('should ignore X-Forwarded-Proto', function(done){ - var app = express(); - - app.use(function(req, res){ - res.end(req.protocol); - }); - - request(app) - .get('/') - .set('X-Forwarded-Proto', 'https') - .expect('http', done); - }) - }) - }) -}) diff --git a/test/req.query.js b/test/req.query.js deleted file mode 100644 index 6fae592d..00000000 --- a/test/req.query.js +++ /dev/null @@ -1,123 +0,0 @@ -'use strict' - -var assert = require('assert') -var express = require('../') - , request = require('supertest'); - -describe('req', function(){ - describe('.query', function(){ - it('should default to {}', function(done){ - var app = createApp(); - - request(app) - .get('/') - .expect(200, '{}', done); - }); - - it('should default to parse complex keys', function (done) { - var app = createApp(); - - request(app) - .get('/?user[name]=tj') - .expect(200, '{"user":{"name":"tj"}}', done); - }); - - describe('when "query parser" is extended', function () { - it('should parse complex keys', function (done) { - var app = createApp('extended'); - - request(app) - .get('/?foo[0][bar]=baz&foo[0][fizz]=buzz&foo[]=done!') - .expect(200, '{"foo":[{"bar":"baz","fizz":"buzz"},"done!"]}', done); - }); - - it('should parse parameters with dots', function (done) { - var app = createApp('extended'); - - request(app) - .get('/?user.name=tj') - .expect(200, '{"user.name":"tj"}', done); - }); - }); - - describe('when "query parser" is simple', function () { - it('should not parse complex keys', function (done) { - var app = createApp('simple'); - - request(app) - .get('/?user%5Bname%5D=tj') - .expect(200, '{"user[name]":"tj"}', done); - }); - }); - - describe('when "query parser" is a function', function () { - it('should parse using function', function (done) { - var app = createApp(function (str) { - return {'length': (str || '').length}; - }); - - request(app) - .get('/?user%5Bname%5D=tj') - .expect(200, '{"length":17}', done); - }); - }); - - describe('when "query parser" disabled', function () { - it('should not parse query', function (done) { - var app = createApp(false); - - request(app) - .get('/?user%5Bname%5D=tj') - .expect(200, '{}', done); - }); - }); - - describe('when "query parser" enabled', function () { - it('should not parse complex keys', function (done) { - var app = createApp(true); - - request(app) - .get('/?user%5Bname%5D=tj') - .expect(200, '{"user[name]":"tj"}', done); - }); - }); - - describe('when "query parser fn" is missing', function () { - it('should act like "extended"', function (done) { - var app = express(); - - delete app.settings['query parser']; - delete app.settings['query parser fn']; - - app.use(function (req, res) { - res.send(req.query); - }); - - request(app) - .get('/?user[name]=tj&user.name=tj') - .expect(200, '{"user":{"name":"tj"},"user.name":"tj"}', done); - }); - }); - - describe('when "query parser" an unknown value', function () { - it('should throw', function () { - assert.throws(createApp.bind(null, 'bogus'), - /unknown value.*query parser/) - }); - }); - }) -}) - -function createApp(setting) { - var app = express(); - - if (setting !== undefined) { - app.set('query parser', setting); - } - - app.use(function (req, res) { - res.send(req.query); - }); - - return app; -} diff --git a/test/req.range.js b/test/req.range.js deleted file mode 100644 index 11144173..00000000 --- a/test/req.range.js +++ /dev/null @@ -1,104 +0,0 @@ -'use strict' - -var express = require('..'); -var request = require('supertest') - -describe('req', function(){ - describe('.range(size)', function(){ - it('should return parsed ranges', function (done) { - var app = express() - - app.use(function (req, res) { - res.json(req.range(120)) - }) - - request(app) - .get('/') - .set('Range', 'bytes=0-50,51-100') - .expect(200, '[{"start":0,"end":50},{"start":51,"end":100}]', done) - }) - - it('should cap to the given size', function (done) { - var app = express() - - app.use(function (req, res) { - res.json(req.range(75)) - }) - - request(app) - .get('/') - .set('Range', 'bytes=0-100') - .expect(200, '[{"start":0,"end":74}]', done) - }) - - it('should cap to the given size when open-ended', function (done) { - var app = express() - - app.use(function (req, res) { - res.json(req.range(75)) - }) - - request(app) - .get('/') - .set('Range', 'bytes=0-') - .expect(200, '[{"start":0,"end":74}]', done) - }) - - it('should have a .type', function (done) { - var app = express() - - app.use(function (req, res) { - res.json(req.range(120).type) - }) - - request(app) - .get('/') - .set('Range', 'bytes=0-100') - .expect(200, '"bytes"', done) - }) - - it('should accept any type', function (done) { - var app = express() - - app.use(function (req, res) { - res.json(req.range(120).type) - }) - - request(app) - .get('/') - .set('Range', 'users=0-2') - .expect(200, '"users"', done) - }) - - it('should return undefined if no range', function (done) { - var app = express() - - app.use(function (req, res) { - res.send(String(req.range(120))) - }) - - request(app) - .get('/') - .expect(200, 'undefined', done) - }) - }) - - describe('.range(size, options)', function(){ - describe('with "combine: true" option', function(){ - it('should return combined ranges', function (done) { - var app = express() - - app.use(function (req, res) { - res.json(req.range(120, { - combine: true - })) - }) - - request(app) - .get('/') - .set('Range', 'bytes=0-50,51-100') - .expect(200, '[{"start":0,"end":100}]', done) - }) - }) - }) -}) diff --git a/test/req.route.js b/test/req.route.js deleted file mode 100644 index 6c17fbb1..00000000 --- a/test/req.route.js +++ /dev/null @@ -1,28 +0,0 @@ -'use strict' - -var express = require('../') - , request = require('supertest'); - -describe('req', function(){ - describe('.route', function(){ - it('should be the executed Route', function(done){ - var app = express(); - - app.get('/user/:id/:op?', function(req, res, next){ - res.header('path-1', req.route.path) - next(); - }); - - app.get('/user/:id/edit', function(req, res){ - res.header('path-2', req.route.path) - res.end(); - }); - - request(app) - .get('/user/12/edit') - .expect('path-1', '/user/:id/:op?') - .expect('path-2', '/user/:id/edit') - .expect(200, done) - }) - }) -}) diff --git a/test/req.secure.js b/test/req.secure.js deleted file mode 100644 index 0097ed61..00000000 --- a/test/req.secure.js +++ /dev/null @@ -1,101 +0,0 @@ -'use strict' - -var express = require('../') - , request = require('supertest'); - -describe('req', function(){ - describe('.secure', function(){ - describe('when X-Forwarded-Proto is missing', function(){ - it('should return false when http', function(done){ - var app = express(); - - app.get('/', function(req, res){ - res.send(req.secure ? 'yes' : 'no'); - }); - - request(app) - .get('/') - .expect('no', done) - }) - }) - }) - - describe('.secure', function(){ - describe('when X-Forwarded-Proto is present', function(){ - it('should return false when http', function(done){ - var app = express(); - - app.get('/', function(req, res){ - res.send(req.secure ? 'yes' : 'no'); - }); - - request(app) - .get('/') - .set('X-Forwarded-Proto', 'https') - .expect('no', done) - }) - - it('should return true when "trust proxy" is enabled', function(done){ - var app = express(); - - app.enable('trust proxy'); - - app.get('/', function(req, res){ - res.send(req.secure ? 'yes' : 'no'); - }); - - request(app) - .get('/') - .set('X-Forwarded-Proto', 'https') - .expect('yes', done) - }) - - it('should return false when initial proxy is http', function(done){ - var app = express(); - - app.enable('trust proxy'); - - app.get('/', function(req, res){ - res.send(req.secure ? 'yes' : 'no'); - }); - - request(app) - .get('/') - .set('X-Forwarded-Proto', 'http, https') - .expect('no', done) - }) - - it('should return true when initial proxy is https', function(done){ - var app = express(); - - app.enable('trust proxy'); - - app.get('/', function(req, res){ - res.send(req.secure ? 'yes' : 'no'); - }); - - request(app) - .get('/') - .set('X-Forwarded-Proto', 'https, http') - .expect('yes', done) - }) - - describe('when "trust proxy" trusting hop count', function () { - it('should respect X-Forwarded-Proto', function (done) { - var app = express(); - - app.set('trust proxy', 1); - - app.get('/', function (req, res) { - res.send(req.secure ? 'yes' : 'no'); - }); - - request(app) - .get('/') - .set('X-Forwarded-Proto', 'https') - .expect('yes', done) - }) - }) - }) - }) -}) diff --git a/test/req.signedCookies.js b/test/req.signedCookies.js deleted file mode 100644 index db561951..00000000 --- a/test/req.signedCookies.js +++ /dev/null @@ -1,37 +0,0 @@ -'use strict' - -var express = require('../') - , request = require('supertest') - , cookieParser = require('cookie-parser') - -describe('req', function(){ - describe('.signedCookies', function(){ - it('should return a signed JSON cookie', function(done){ - var app = express(); - - app.use(cookieParser('secret')); - - app.use(function(req, res){ - if (req.path === '/set') { - res.cookie('obj', { foo: 'bar' }, { signed: true }); - res.end(); - } else { - res.send(req.signedCookies); - } - }); - - request(app) - .get('/set') - .end(function(err, res){ - if (err) return done(err); - var cookie = res.header['set-cookie']; - - request(app) - .get('/') - .set('Cookie', cookie) - .expect(200, { obj: { foo: 'bar' } }, done) - }); - }) - }) -}) - diff --git a/test/req.stale.js b/test/req.stale.js deleted file mode 100644 index cda77fa4..00000000 --- a/test/req.stale.js +++ /dev/null @@ -1,50 +0,0 @@ -'use strict' - -var express = require('../') - , request = require('supertest'); - -describe('req', function(){ - describe('.stale', function(){ - it('should return false when the resource is not modified', function(done){ - var app = express(); - var etag = '"12345"'; - - app.use(function(req, res){ - res.set('ETag', etag); - res.send(req.stale); - }); - - request(app) - .get('/') - .set('If-None-Match', etag) - .expect(304, done); - }) - - it('should return true when the resource is modified', function(done){ - var app = express(); - - app.use(function(req, res){ - res.set('ETag', '"123"'); - res.send(req.stale); - }); - - request(app) - .get('/') - .set('If-None-Match', '"12345"') - .expect(200, 'true', done); - }) - - it('should return true without response headers', function(done){ - var app = express(); - - app.disable('x-powered-by') - app.use(function(req, res){ - res.send(req.stale); - }); - - request(app) - .get('/') - .expect(200, 'true', done); - }) - }) -}) diff --git a/test/req.subdomains.js b/test/req.subdomains.js deleted file mode 100644 index e5600f2e..00000000 --- a/test/req.subdomains.js +++ /dev/null @@ -1,173 +0,0 @@ -'use strict' - -var express = require('../') - , request = require('supertest'); - -describe('req', function(){ - describe('.subdomains', function(){ - describe('when present', function(){ - it('should return an array', function(done){ - var app = express(); - - app.use(function(req, res){ - res.send(req.subdomains); - }); - - request(app) - .get('/') - .set('Host', 'tobi.ferrets.example.com') - .expect(200, ['ferrets', 'tobi'], done); - }) - - it('should work with IPv4 address', function(done){ - var app = express(); - - app.use(function(req, res){ - res.send(req.subdomains); - }); - - request(app) - .get('/') - .set('Host', '127.0.0.1') - .expect(200, [], done); - }) - - it('should work with IPv6 address', function(done){ - var app = express(); - - app.use(function(req, res){ - res.send(req.subdomains); - }); - - request(app) - .get('/') - .set('Host', '[::1]') - .expect(200, [], done); - }) - }) - - describe('otherwise', function(){ - it('should return an empty array', function(done){ - var app = express(); - - app.use(function(req, res){ - res.send(req.subdomains); - }); - - request(app) - .get('/') - .set('Host', 'example.com') - .expect(200, [], done); - }) - }) - - describe('with no host', function(){ - it('should return an empty array', function(done){ - var app = express(); - - app.use(function(req, res){ - req.headers.host = null; - res.send(req.subdomains); - }); - - request(app) - .get('/') - .expect(200, [], done); - }) - }) - - describe('with trusted X-Forwarded-Host', function () { - it('should return an array', function (done) { - var app = express(); - - app.set('trust proxy', true); - app.use(function (req, res) { - res.send(req.subdomains); - }); - - request(app) - .get('/') - .set('X-Forwarded-Host', 'tobi.ferrets.example.com') - .expect(200, ['ferrets', 'tobi'], done); - }) - }) - - describe('when subdomain offset is set', function(){ - describe('when subdomain offset is zero', function(){ - it('should return an array with the whole domain', function(done){ - var app = express(); - app.set('subdomain offset', 0); - - app.use(function(req, res){ - res.send(req.subdomains); - }); - - request(app) - .get('/') - .set('Host', 'tobi.ferrets.sub.example.com') - .expect(200, ['com', 'example', 'sub', 'ferrets', 'tobi'], done); - }) - - it('should return an array with the whole IPv4', function (done) { - var app = express(); - app.set('subdomain offset', 0); - - app.use(function(req, res){ - res.send(req.subdomains); - }); - - request(app) - .get('/') - .set('Host', '127.0.0.1') - .expect(200, ['127.0.0.1'], done); - }) - - it('should return an array with the whole IPv6', function (done) { - var app = express(); - app.set('subdomain offset', 0); - - app.use(function(req, res){ - res.send(req.subdomains); - }); - - request(app) - .get('/') - .set('Host', '[::1]') - .expect(200, ['[::1]'], done); - }) - }) - - describe('when present', function(){ - it('should return an array', function(done){ - var app = express(); - app.set('subdomain offset', 3); - - app.use(function(req, res){ - res.send(req.subdomains); - }); - - request(app) - .get('/') - .set('Host', 'tobi.ferrets.sub.example.com') - .expect(200, ['ferrets', 'tobi'], done); - }) - }) - - describe('otherwise', function(){ - it('should return an empty array', function(done){ - var app = express(); - app.set('subdomain offset', 3); - - app.use(function(req, res){ - res.send(req.subdomains); - }); - - request(app) - .get('/') - .set('Host', 'sub.example.com') - .expect(200, [], done); - }) - }) - }) - }) -}) diff --git a/test/req.xhr.js b/test/req.xhr.js deleted file mode 100644 index 99cf7f19..00000000 --- a/test/req.xhr.js +++ /dev/null @@ -1,42 +0,0 @@ -'use strict' - -var express = require('../') - , request = require('supertest'); - -describe('req', function(){ - describe('.xhr', function(){ - before(function () { - this.app = express() - this.app.get('/', function (req, res) { - res.send(req.xhr) - }) - }) - - it('should return true when X-Requested-With is xmlhttprequest', function(done){ - request(this.app) - .get('/') - .set('X-Requested-With', 'xmlhttprequest') - .expect(200, 'true', done) - }) - - it('should case-insensitive', function(done){ - request(this.app) - .get('/') - .set('X-Requested-With', 'XMLHttpRequest') - .expect(200, 'true', done) - }) - - it('should return false otherwise', function(done){ - request(this.app) - .get('/') - .set('X-Requested-With', 'blahblah') - .expect(200, 'false', done) - }) - - it('should return false when not present', function(done){ - request(this.app) - .get('/') - .expect(200, 'false', done) - }) - }) -}) diff --git a/test/res.append.js b/test/res.append.js deleted file mode 100644 index 8f72598b..00000000 --- a/test/res.append.js +++ /dev/null @@ -1,116 +0,0 @@ -'use strict' - -var assert = require('assert') -var express = require('..') -var request = require('supertest') - -describe('res', function () { - describe('.append(field, val)', function () { - it('should append multiple headers', function (done) { - var app = express() - - app.use(function (req, res, next) { - res.append('Set-Cookie', 'foo=bar') - next() - }) - - app.use(function (req, res) { - res.append('Set-Cookie', 'fizz=buzz') - res.end() - }) - - request(app) - .get('/') - .expect(200) - .expect(shouldHaveHeaderValues('Set-Cookie', ['foo=bar', 'fizz=buzz'])) - .end(done) - }) - - it('should accept array of values', function (done) { - var app = express() - - app.use(function (req, res, next) { - res.append('Set-Cookie', ['foo=bar', 'fizz=buzz']) - res.end() - }) - - request(app) - .get('/') - .expect(200) - .expect(shouldHaveHeaderValues('Set-Cookie', ['foo=bar', 'fizz=buzz'])) - .end(done) - }) - - it('should get reset by res.set(field, val)', function (done) { - var app = express() - - app.use(function (req, res, next) { - res.append('Set-Cookie', 'foo=bar') - res.append('Set-Cookie', 'fizz=buzz') - next() - }) - - app.use(function (req, res) { - res.set('Set-Cookie', 'pet=tobi') - res.end() - }); - - request(app) - .get('/') - .expect(200) - .expect(shouldHaveHeaderValues('Set-Cookie', ['pet=tobi'])) - .end(done) - }) - - it('should work with res.set(field, val) first', function (done) { - var app = express() - - app.use(function (req, res, next) { - res.set('Set-Cookie', 'foo=bar') - next() - }) - - app.use(function(req, res){ - res.append('Set-Cookie', 'fizz=buzz') - res.end() - }) - - request(app) - .get('/') - .expect(200) - .expect(shouldHaveHeaderValues('Set-Cookie', ['foo=bar', 'fizz=buzz'])) - .end(done) - }) - - it('should work together with res.cookie', function (done) { - var app = express() - - app.use(function (req, res, next) { - res.cookie('foo', 'bar') - next() - }) - - app.use(function (req, res) { - res.append('Set-Cookie', 'fizz=buzz') - res.end() - }) - - request(app) - .get('/') - .expect(200) - .expect(shouldHaveHeaderValues('Set-Cookie', ['foo=bar; Path=/', 'fizz=buzz'])) - .end(done) - }) - }) -}) - -function shouldHaveHeaderValues (key, values) { - return function (res) { - var headers = res.headers[key.toLowerCase()] - assert.ok(headers, 'should have header "' + key + '"') - assert.strictEqual(headers.length, values.length, 'should have ' + values.length + ' occurances of "' + key + '"') - for (var i = 0; i < values.length; i++) { - assert.strictEqual(headers[i], values[i]) - } - } -} diff --git a/test/res.attachment.js b/test/res.attachment.js deleted file mode 100644 index 6283ded0..00000000 --- a/test/res.attachment.js +++ /dev/null @@ -1,78 +0,0 @@ -'use strict' - -var Buffer = require('safe-buffer').Buffer -var express = require('../') - , request = require('supertest'); - -describe('res', function(){ - describe('.attachment()', function(){ - it('should Content-Disposition to attachment', function(done){ - var app = express(); - - app.use(function(req, res){ - res.attachment().send('foo'); - }); - - request(app) - .get('/') - .expect('Content-Disposition', 'attachment', done); - }) - }) - - describe('.attachment(filename)', function(){ - it('should add the filename param', function(done){ - var app = express(); - - app.use(function(req, res){ - res.attachment('/path/to/image.png'); - res.send('foo'); - }); - - request(app) - .get('/') - .expect('Content-Disposition', 'attachment; filename="image.png"', done); - }) - - it('should set the Content-Type', function(done){ - var app = express(); - - app.use(function(req, res){ - res.attachment('/path/to/image.png'); - res.send(Buffer.alloc(4, '.')) - }); - - request(app) - .get('/') - .expect('Content-Type', 'image/png', done); - }) - }) - - describe('.attachment(utf8filename)', function(){ - it('should add the filename and filename* params', function(done){ - var app = express(); - - app.use(function(req, res){ - res.attachment('/locales/日本語.txt'); - res.send('japanese'); - }); - - request(app) - .get('/') - .expect('Content-Disposition', 'attachment; filename="???.txt"; filename*=UTF-8\'\'%E6%97%A5%E6%9C%AC%E8%AA%9E.txt') - .expect(200, done); - }) - - it('should set the Content-Type', function(done){ - var app = express(); - - app.use(function(req, res){ - res.attachment('/locales/日本語.txt'); - res.send('japanese'); - }); - - request(app) - .get('/') - .expect('Content-Type', 'text/plain; charset=utf-8', done); - }) - }) -}) diff --git a/test/res.clearCookie.js b/test/res.clearCookie.js deleted file mode 100644 index fc0cfb99..00000000 --- a/test/res.clearCookie.js +++ /dev/null @@ -1,36 +0,0 @@ -'use strict' - -var express = require('../') - , request = require('supertest'); - -describe('res', function(){ - describe('.clearCookie(name)', function(){ - it('should set a cookie passed expiry', function(done){ - var app = express(); - - app.use(function(req, res){ - res.clearCookie('sid').end(); - }); - - request(app) - .get('/') - .expect('Set-Cookie', 'sid=; Path=/; Expires=Thu, 01 Jan 1970 00:00:00 GMT') - .expect(200, done) - }) - }) - - describe('.clearCookie(name, options)', function(){ - it('should set the given params', function(done){ - var app = express(); - - app.use(function(req, res){ - res.clearCookie('sid', { path: '/admin' }).end(); - }); - - request(app) - .get('/') - .expect('Set-Cookie', 'sid=; Path=/admin; Expires=Thu, 01 Jan 1970 00:00:00 GMT') - .expect(200, done) - }) - }) -}) diff --git a/test/res.cookie.js b/test/res.cookie.js deleted file mode 100644 index 93deb769..00000000 --- a/test/res.cookie.js +++ /dev/null @@ -1,280 +0,0 @@ -'use strict' - -var express = require('../') - , request = require('supertest') - , cookieParser = require('cookie-parser') -var merge = require('utils-merge'); - -describe('res', function(){ - describe('.cookie(name, object)', function(){ - it('should generate a JSON cookie', function(done){ - var app = express(); - - app.use(function(req, res){ - res.cookie('user', { name: 'tobi' }).end(); - }); - - request(app) - .get('/') - .expect('Set-Cookie', 'user=j%3A%7B%22name%22%3A%22tobi%22%7D; Path=/') - .expect(200, done) - }) - }) - - describe('.cookie(name, string)', function(){ - it('should set a cookie', function(done){ - var app = express(); - - app.use(function(req, res){ - res.cookie('name', 'tobi').end(); - }); - - request(app) - .get('/') - .expect('Set-Cookie', 'name=tobi; Path=/') - .expect(200, done) - }) - - it('should allow multiple calls', function(done){ - var app = express(); - - app.use(function(req, res){ - res.cookie('name', 'tobi'); - res.cookie('age', 1); - res.cookie('gender', '?'); - res.end(); - }); - - request(app) - .get('/') - .expect('Set-Cookie', 'name=tobi; Path=/,age=1; Path=/,gender=%3F; Path=/') - .expect(200, done) - }) - }) - - describe('.cookie(name, string, options)', function(){ - it('should set params', function(done){ - var app = express(); - - app.use(function(req, res){ - res.cookie('name', 'tobi', { httpOnly: true, secure: true }); - res.end(); - }); - - request(app) - .get('/') - .expect('Set-Cookie', 'name=tobi; Path=/; HttpOnly; Secure') - .expect(200, done) - }) - - describe('expires', function () { - it('should throw on invalid date', function (done) { - var app = express() - - app.use(function (req, res) { - res.cookie('name', 'tobi', { expires: new Date(NaN) }) - res.end() - }) - - request(app) - .get('/') - .expect(500, /option expires is invalid/, done) - }) - }) - - describe('maxAge', function(){ - it('should set relative expires', function(done){ - var app = express(); - - app.use(function(req, res){ - res.cookie('name', 'tobi', { maxAge: 1000 }); - res.end(); - }); - - request(app) - .get('/') - .expect('Set-Cookie', /name=tobi; Max-Age=1; Path=\/; Expires=/) - .expect(200, done) - }) - - it('should set max-age', function(done){ - var app = express(); - - app.use(function(req, res){ - res.cookie('name', 'tobi', { maxAge: 1000 }); - res.end(); - }); - - request(app) - .get('/') - .expect('Set-Cookie', /Max-Age=1/, done) - }) - - it('should not mutate the options object', function(done){ - var app = express(); - - var options = { maxAge: 1000 }; - var optionsCopy = merge({}, options); - - app.use(function(req, res){ - res.cookie('name', 'tobi', options) - res.json(options) - }); - - request(app) - .get('/') - .expect(200, optionsCopy, done) - }) - - it('should not throw on null', function (done) { - var app = express() - - app.use(function (req, res) { - res.cookie('name', 'tobi', { maxAge: null }) - res.end() - }) - - request(app) - .get('/') - .expect(200) - .expect('Set-Cookie', 'name=tobi; Path=/') - .end(done) - }) - - it('should not throw on undefined', function (done) { - var app = express() - - app.use(function (req, res) { - res.cookie('name', 'tobi', { maxAge: undefined }) - res.end() - }) - - request(app) - .get('/') - .expect(200) - .expect('Set-Cookie', 'name=tobi; Path=/') - .end(done) - }) - - it('should throw an error with invalid maxAge', function (done) { - var app = express() - - app.use(function (req, res) { - res.cookie('name', 'tobi', { maxAge: 'foobar' }) - res.end() - }) - - request(app) - .get('/') - .expect(500, /option maxAge is invalid/, done) - }) - }) - - describe('priority', function () { - it('should set low priority', function (done) { - var app = express() - - app.use(function (req, res) { - res.cookie('name', 'tobi', { priority: 'low' }) - res.end() - }) - - request(app) - .get('/') - .expect('Set-Cookie', /Priority=Low/) - .expect(200, done) - }) - - it('should set medium priority', function (done) { - var app = express() - - app.use(function (req, res) { - res.cookie('name', 'tobi', { priority: 'medium' }) - res.end() - }) - - request(app) - .get('/') - .expect('Set-Cookie', /Priority=Medium/) - .expect(200, done) - }) - - it('should set high priority', function (done) { - var app = express() - - app.use(function (req, res) { - res.cookie('name', 'tobi', { priority: 'high' }) - res.end() - }) - - request(app) - .get('/') - .expect('Set-Cookie', /Priority=High/) - .expect(200, done) - }) - - it('should throw with invalid priority', function (done) { - var app = express() - - app.use(function (req, res) { - res.cookie('name', 'tobi', { priority: 'foobar' }) - res.end() - }) - - request(app) - .get('/') - .expect(500, /option priority is invalid/, done) - }) - }) - - describe('signed', function(){ - it('should generate a signed JSON cookie', function(done){ - var app = express(); - - app.use(cookieParser('foo bar baz')); - - app.use(function(req, res){ - res.cookie('user', { name: 'tobi' }, { signed: true }).end(); - }); - - request(app) - .get('/') - .expect('Set-Cookie', 'user=s%3Aj%3A%7B%22name%22%3A%22tobi%22%7D.K20xcwmDS%2BPb1rsD95o5Jm5SqWs1KteqdnynnB7jkTE; Path=/') - .expect(200, done) - }) - }) - - describe('signed without secret', function(){ - it('should throw an error', function(done){ - var app = express(); - - app.use(cookieParser()); - - app.use(function(req, res){ - res.cookie('name', 'tobi', { signed: true }).end(); - }); - - request(app) - .get('/') - .expect(500, /secret\S+ required for signed cookies/, done); - }) - }) - - describe('.signedCookie(name, string)', function(){ - it('should set a signed cookie', function(done){ - var app = express(); - - app.use(cookieParser('foo bar baz')); - - app.use(function(req, res){ - res.cookie('name', 'tobi', { signed: true }).end(); - }); - - request(app) - .get('/') - .expect('Set-Cookie', 'name=s%3Atobi.xJjV2iZ6EI7C8E5kzwbfA9PVLl1ZR07UTnuTgQQ4EnQ; Path=/') - .expect(200, done) - }) - }) - }) -}) diff --git a/test/res.download.js b/test/res.download.js deleted file mode 100644 index b52e6680..00000000 --- a/test/res.download.js +++ /dev/null @@ -1,498 +0,0 @@ -'use strict' - -var after = require('after'); -var assert = require('assert') -var asyncHooks = tryRequire('async_hooks') -var Buffer = require('safe-buffer').Buffer -var express = require('..'); -var path = require('path') -var request = require('supertest'); -var utils = require('./support/utils') - -var FIXTURES_PATH = path.join(__dirname, 'fixtures') - -var describeAsyncHooks = typeof asyncHooks.AsyncLocalStorage === 'function' - ? describe - : describe.skip - -describe('res', function(){ - describe('.download(path)', function(){ - it('should transfer as an attachment', function(done){ - var app = express(); - - app.use(function(req, res){ - res.download('test/fixtures/user.html'); - }); - - request(app) - .get('/') - .expect('Content-Type', 'text/html; charset=UTF-8') - .expect('Content-Disposition', 'attachment; filename="user.html"') - .expect(200, '

    {{user.name}}

    ', done) - }) - - it('should accept range requests', function (done) { - var app = express() - - app.get('/', function (req, res) { - res.download('test/fixtures/user.html') - }) - - request(app) - .get('/') - .expect('Accept-Ranges', 'bytes') - .expect(200, '

    {{user.name}}

    ', done) - }) - - it('should respond with requested byte range', function (done) { - var app = express() - - app.get('/', function (req, res) { - res.download('test/fixtures/user.html') - }) - - request(app) - .get('/') - .set('Range', 'bytes=0-2') - .expect('Content-Range', 'bytes 0-2/20') - .expect(206, '

    ', done) - }) - }) - - describe('.download(path, filename)', function(){ - it('should provide an alternate filename', function(done){ - var app = express(); - - app.use(function(req, res){ - res.download('test/fixtures/user.html', 'document'); - }); - - request(app) - .get('/') - .expect('Content-Type', 'text/html; charset=UTF-8') - .expect('Content-Disposition', 'attachment; filename="document"') - .expect(200, done) - }) - }) - - describe('.download(path, fn)', function(){ - it('should invoke the callback', function(done){ - var app = express(); - var cb = after(2, done); - - app.use(function(req, res){ - res.download('test/fixtures/user.html', cb); - }); - - request(app) - .get('/') - .expect('Content-Type', 'text/html; charset=UTF-8') - .expect('Content-Disposition', 'attachment; filename="user.html"') - .expect(200, cb); - }) - - describeAsyncHooks('async local storage', function () { - it('should presist store', function (done) { - var app = express() - var cb = after(2, done) - var store = { foo: 'bar' } - - app.use(function (req, res, next) { - req.asyncLocalStorage = new asyncHooks.AsyncLocalStorage() - req.asyncLocalStorage.run(store, next) - }) - - app.use(function (req, res) { - res.download('test/fixtures/name.txt', function (err) { - if (err) return cb(err) - - var local = req.asyncLocalStorage.getStore() - - assert.strictEqual(local.foo, 'bar') - cb() - }) - }) - - request(app) - .get('/') - .expect('Content-Type', 'text/plain; charset=UTF-8') - .expect('Content-Disposition', 'attachment; filename="name.txt"') - .expect(200, 'tobi', cb) - }) - - it('should presist store on error', function (done) { - var app = express() - var store = { foo: 'bar' } - - app.use(function (req, res, next) { - req.asyncLocalStorage = new asyncHooks.AsyncLocalStorage() - req.asyncLocalStorage.run(store, next) - }) - - app.use(function (req, res) { - res.download('test/fixtures/does-not-exist', function (err) { - var local = req.asyncLocalStorage.getStore() - - if (local) { - res.setHeader('x-store-foo', String(local.foo)) - } - - res.send(err ? 'got ' + err.status + ' error' : 'no error') - }) - }) - - request(app) - .get('/') - .expect(200) - .expect('x-store-foo', 'bar') - .expect('got 404 error') - .end(done) - }) - }) - }) - - describe('.download(path, options)', function () { - it('should allow options to res.sendFile()', function (done) { - var app = express() - - app.use(function (req, res) { - res.download('test/fixtures/.name', { - dotfiles: 'allow', - maxAge: '4h' - }) - }) - - request(app) - .get('/') - .expect(200) - .expect('Content-Disposition', 'attachment; filename=".name"') - .expect('Cache-Control', 'public, max-age=14400') - .expect(utils.shouldHaveBody(Buffer.from('tobi'))) - .end(done) - }) - - describe('with "headers" option', function () { - it('should set headers on response', function (done) { - var app = express() - - app.use(function (req, res) { - res.download('test/fixtures/user.html', { - headers: { - 'X-Foo': 'Bar', - 'X-Bar': 'Foo' - } - }) - }) - - request(app) - .get('/') - .expect(200) - .expect('X-Foo', 'Bar') - .expect('X-Bar', 'Foo') - .end(done) - }) - - it('should use last header when duplicated', function (done) { - var app = express() - - app.use(function (req, res) { - res.download('test/fixtures/user.html', { - headers: { - 'X-Foo': 'Bar', - 'x-foo': 'bar' - } - }) - }) - - request(app) - .get('/') - .expect(200) - .expect('X-Foo', 'bar') - .end(done) - }) - - it('should override Content-Type', function (done) { - var app = express() - - app.use(function (req, res) { - res.download('test/fixtures/user.html', { - headers: { - 'Content-Type': 'text/x-custom' - } - }) - }) - - request(app) - .get('/') - .expect(200) - .expect('Content-Type', 'text/x-custom') - .end(done) - }) - - it('should not set headers on 404', function (done) { - var app = express() - - app.use(function (req, res) { - res.download('test/fixtures/does-not-exist', { - headers: { - 'X-Foo': 'Bar' - } - }) - }) - - request(app) - .get('/') - .expect(404) - .expect(utils.shouldNotHaveHeader('X-Foo')) - .end(done) - }) - - describe('when headers contains Content-Disposition', function () { - it('should be ignored', function (done) { - var app = express() - - app.use(function (req, res) { - res.download('test/fixtures/user.html', { - headers: { - 'Content-Disposition': 'inline' - } - }) - }) - - request(app) - .get('/') - .expect(200) - .expect('Content-Disposition', 'attachment; filename="user.html"') - .end(done) - }) - - it('should be ignored case-insensitively', function (done) { - var app = express() - - app.use(function (req, res) { - res.download('test/fixtures/user.html', { - headers: { - 'content-disposition': 'inline' - } - }) - }) - - request(app) - .get('/') - .expect(200) - .expect('Content-Disposition', 'attachment; filename="user.html"') - .end(done) - }) - }) - }) - - describe('with "root" option', function () { - it('should allow relative path', function (done) { - var app = express() - - app.use(function (req, res) { - res.download('name.txt', { - root: FIXTURES_PATH - }) - }) - - request(app) - .get('/') - .expect(200) - .expect('Content-Disposition', 'attachment; filename="name.txt"') - .expect(utils.shouldHaveBody(Buffer.from('tobi'))) - .end(done) - }) - - it('should allow up within root', function (done) { - var app = express() - - app.use(function (req, res) { - res.download('fake/../name.txt', { - root: FIXTURES_PATH - }) - }) - - request(app) - .get('/') - .expect(200) - .expect('Content-Disposition', 'attachment; filename="name.txt"') - .expect(utils.shouldHaveBody(Buffer.from('tobi'))) - .end(done) - }) - - it('should reject up outside root', function (done) { - var app = express() - - app.use(function (req, res) { - var p = '..' + path.sep + - path.relative(path.dirname(FIXTURES_PATH), path.join(FIXTURES_PATH, 'name.txt')) - - res.download(p, { - root: FIXTURES_PATH - }) - }) - - request(app) - .get('/') - .expect(403) - .expect(utils.shouldNotHaveHeader('Content-Disposition')) - .end(done) - }) - - it('should reject reading outside root', function (done) { - var app = express() - - app.use(function (req, res) { - res.download('../name.txt', { - root: FIXTURES_PATH - }) - }) - - request(app) - .get('/') - .expect(403) - .expect(utils.shouldNotHaveHeader('Content-Disposition')) - .end(done) - }) - }) - }) - - describe('.download(path, filename, fn)', function(){ - it('should invoke the callback', function(done){ - var app = express(); - var cb = after(2, done); - - app.use(function(req, res){ - res.download('test/fixtures/user.html', 'document', cb) - }); - - request(app) - .get('/') - .expect('Content-Type', 'text/html; charset=UTF-8') - .expect('Content-Disposition', 'attachment; filename="document"') - .expect(200, cb); - }) - }) - - describe('.download(path, filename, options, fn)', function () { - it('should invoke the callback', function (done) { - var app = express() - var cb = after(2, done) - var options = {} - - app.use(function (req, res) { - res.download('test/fixtures/user.html', 'document', options, cb) - }) - - request(app) - .get('/') - .expect(200) - .expect('Content-Type', 'text/html; charset=UTF-8') - .expect('Content-Disposition', 'attachment; filename="document"') - .end(cb) - }) - - it('should allow options to res.sendFile()', function (done) { - var app = express() - - app.use(function (req, res) { - res.download('test/fixtures/.name', 'document', { - dotfiles: 'allow', - maxAge: '4h' - }) - }) - - request(app) - .get('/') - .expect(200) - .expect('Content-Disposition', 'attachment; filename="document"') - .expect('Cache-Control', 'public, max-age=14400') - .expect(utils.shouldHaveBody(Buffer.from('tobi'))) - .end(done) - }) - - describe('when options.headers contains Content-Disposition', function () { - it('should be ignored', function (done) { - var app = express() - - app.use(function (req, res) { - res.download('test/fixtures/user.html', 'document', { - headers: { - 'Content-Type': 'text/x-custom', - 'Content-Disposition': 'inline' - } - }) - }) - - request(app) - .get('/') - .expect(200) - .expect('Content-Type', 'text/x-custom') - .expect('Content-Disposition', 'attachment; filename="document"') - .end(done) - }) - - it('should be ignored case-insensitively', function (done) { - var app = express() - - app.use(function (req, res) { - res.download('test/fixtures/user.html', 'document', { - headers: { - 'content-type': 'text/x-custom', - 'content-disposition': 'inline' - } - }) - }) - - request(app) - .get('/') - .expect(200) - .expect('Content-Type', 'text/x-custom') - .expect('Content-Disposition', 'attachment; filename="document"') - .end(done) - }) - }) - }) - - describe('on failure', function(){ - it('should invoke the callback', function(done){ - var app = express(); - - app.use(function (req, res, next) { - res.download('test/fixtures/foobar.html', function(err){ - if (!err) return next(new Error('expected error')); - res.send('got ' + err.status + ' ' + err.code); - }); - }); - - request(app) - .get('/') - .expect(200, 'got 404 ENOENT', done); - }) - - it('should remove Content-Disposition', function(done){ - var app = express() - - app.use(function (req, res, next) { - res.download('test/fixtures/foobar.html', function(err){ - if (!err) return next(new Error('expected error')); - res.end('failed'); - }); - }); - - request(app) - .get('/') - .expect(utils.shouldNotHaveHeader('Content-Disposition')) - .expect(200, 'failed', done) - }) - }) -}) - -function tryRequire (name) { - try { - return require(name) - } catch (e) { - return {} - } -} diff --git a/test/res.format.js b/test/res.format.js deleted file mode 100644 index cba6fe13..00000000 --- a/test/res.format.js +++ /dev/null @@ -1,243 +0,0 @@ -'use strict' - -var after = require('after') -var express = require('../') - , request = require('supertest') - , assert = require('assert'); - -var app1 = express(); - -app1.use(function(req, res, next){ - res.format({ - 'text/plain': function(){ - res.send('hey'); - }, - - 'text/html': function(){ - res.send('

    hey

    '); - }, - - 'application/json': function(a, b, c){ - assert(req === a) - assert(res === b) - assert(next === c) - res.send({ message: 'hey' }); - } - }); -}); - -app1.use(function(err, req, res, next){ - if (!err.types) throw err; - res.send(err.status, 'Supports: ' + err.types.join(', ')); -}) - -var app2 = express(); - -app2.use(function(req, res, next){ - res.format({ - text: function(){ res.send('hey') }, - html: function(){ res.send('

    hey

    ') }, - json: function(){ res.send({ message: 'hey' }) } - }); -}); - -app2.use(function(err, req, res, next){ - res.send(err.status, 'Supports: ' + err.types.join(', ')); -}) - -var app3 = express(); - -app3.use(function(req, res, next){ - res.format({ - text: function(){ res.send('hey') }, - default: function (a, b, c) { - assert(req === a) - assert(res === b) - assert(next === c) - res.send('default') - } - }) -}); - -var app4 = express(); - -app4.get('/', function (req, res) { - res.format({ - text: function(){ res.send('hey') }, - html: function(){ res.send('

    hey

    ') }, - json: function(){ res.send({ message: 'hey' }) } - }); -}); - -app4.use(function(err, req, res, next){ - res.send(err.status, 'Supports: ' + err.types.join(', ')); -}) - -var app5 = express(); - -app5.use(function (req, res, next) { - res.format({ - default: function () { res.send('hey') } - }); -}); - -describe('res', function(){ - describe('.format(obj)', function(){ - describe('with canonicalized mime types', function(){ - test(app1); - }) - - describe('with extnames', function(){ - test(app2); - }) - - describe('with parameters', function(){ - var app = express(); - - app.use(function(req, res, next){ - res.format({ - 'text/plain; charset=utf-8': function(){ res.send('hey') }, - 'text/html; foo=bar; bar=baz': function(){ res.send('

    hey

    ') }, - 'application/json; q=0.5': function(){ res.send({ message: 'hey' }) } - }); - }); - - app.use(function(err, req, res, next){ - res.send(err.status, 'Supports: ' + err.types.join(', ')); - }); - - test(app); - }) - - describe('given .default', function(){ - it('should be invoked instead of auto-responding', function(done){ - request(app3) - .get('/') - .set('Accept', 'text/html') - .expect('default', done); - }) - - it('should work when only .default is provided', function (done) { - request(app5) - .get('/') - .set('Accept', '*/*') - .expect('hey', done); - }) - - it('should be able to invoke other formatter', function (done) { - var app = express() - - app.use(function (req, res, next) { - res.format({ - json: function () { res.send('json') }, - default: function () { - res.header('x-default', '1') - this.json() - } - }) - }) - - request(app) - .get('/') - .set('Accept', 'text/plain') - .expect(200) - .expect('x-default', '1') - .expect('json') - .end(done) - }) - }) - - describe('in router', function(){ - test(app4); - }) - - describe('in router', function(){ - var app = express(); - var router = express.Router(); - - router.get('/', function (req, res) { - res.format({ - text: function(){ res.send('hey') }, - html: function(){ res.send('

    hey

    ') }, - json: function(){ res.send({ message: 'hey' }) } - }); - }); - - router.use(function(err, req, res, next){ - res.send(err.status, 'Supports: ' + err.types.join(', ')); - }) - - app.use(router) - - test(app) - }) - }) -}) - -function test(app) { - it('should utilize qvalues in negotiation', function(done){ - request(app) - .get('/') - .set('Accept', 'text/html; q=.5, application/json, */*; q=.1') - .expect({"message":"hey"}, done); - }) - - it('should allow wildcard type/subtypes', function(done){ - request(app) - .get('/') - .set('Accept', 'text/html; q=.5, application/*, */*; q=.1') - .expect({"message":"hey"}, done); - }) - - it('should default the Content-Type', function(done){ - request(app) - .get('/') - .set('Accept', 'text/html; q=.5, text/plain') - .expect('Content-Type', 'text/plain; charset=utf-8') - .expect('hey', done); - }) - - it('should set the correct charset for the Content-Type', function (done) { - var cb = after(3, done) - - request(app) - .get('/') - .set('Accept', 'text/html') - .expect('Content-Type', 'text/html; charset=utf-8', cb) - - request(app) - .get('/') - .set('Accept', 'text/plain') - .expect('Content-Type', 'text/plain; charset=utf-8', cb) - - request(app) - .get('/') - .set('Accept', 'application/json') - .expect('Content-Type', 'application/json; charset=utf-8', cb) - }) - - it('should Vary: Accept', function(done){ - request(app) - .get('/') - .set('Accept', 'text/html; q=.5, text/plain') - .expect('Vary', 'Accept', done); - }) - - describe('when Accept is not present', function(){ - it('should invoke the first callback', function(done){ - request(app) - .get('/') - .expect('hey', done); - }) - }) - - describe('when no match is made', function(){ - it('should should respond with 406 not acceptable', function(done){ - request(app) - .get('/') - .set('Accept', 'foo/bar') - .expect('Supports: text/plain, text/html, application/json') - .expect(406, done) - }) - }) -} diff --git a/test/res.get.js b/test/res.get.js deleted file mode 100644 index a5f12e2e..00000000 --- a/test/res.get.js +++ /dev/null @@ -1,21 +0,0 @@ -'use strict' - -var express = require('..'); -var request = require('supertest'); - -describe('res', function(){ - describe('.get(field)', function(){ - it('should get the response header field', function (done) { - var app = express(); - - app.use(function (req, res) { - res.setHeader('Content-Type', 'text/x-foo'); - res.send(res.get('Content-Type')); - }); - - request(app) - .get('/') - .expect(200, 'text/x-foo', done); - }) - }) -}) diff --git a/test/res.json.js b/test/res.json.js deleted file mode 100644 index dcaceae5..00000000 --- a/test/res.json.js +++ /dev/null @@ -1,229 +0,0 @@ -'use strict' - -var express = require('../') - , request = require('supertest') - , assert = require('assert'); - -describe('res', function(){ - describe('.json(object)', function(){ - it('should not support jsonp callbacks', function(done){ - var app = express(); - - app.use(function(req, res){ - res.json({ foo: 'bar' }); - }); - - request(app) - .get('/?callback=foo') - .expect('{"foo":"bar"}', done); - }) - - it('should not override previous Content-Types', function(done){ - var app = express(); - - app.get('/', function(req, res){ - res.type('application/vnd.example+json'); - res.json({ hello: 'world' }); - }); - - request(app) - .get('/') - .expect('Content-Type', 'application/vnd.example+json; charset=utf-8') - .expect(200, '{"hello":"world"}', done); - }) - - describe('when given primitives', function(){ - it('should respond with json for null', function(done){ - var app = express(); - - app.use(function(req, res){ - res.json(null); - }); - - request(app) - .get('/') - .expect('Content-Type', 'application/json; charset=utf-8') - .expect(200, 'null', done) - }) - - it('should respond with json for Number', function(done){ - var app = express(); - - app.use(function(req, res){ - res.json(300); - }); - - request(app) - .get('/') - .expect('Content-Type', 'application/json; charset=utf-8') - .expect(200, '300', done) - }) - - it('should respond with json for String', function(done){ - var app = express(); - - app.use(function(req, res){ - res.json('str'); - }); - - request(app) - .get('/') - .expect('Content-Type', 'application/json; charset=utf-8') - .expect(200, '"str"', done) - }) - }) - - describe('when given an array', function(){ - it('should respond with json', function(done){ - var app = express(); - - app.use(function(req, res){ - res.json(['foo', 'bar', 'baz']); - }); - - request(app) - .get('/') - .expect('Content-Type', 'application/json; charset=utf-8') - .expect(200, '["foo","bar","baz"]', done) - }) - }) - - describe('when given an object', function(){ - it('should respond with json', function(done){ - var app = express(); - - app.use(function(req, res){ - res.json({ name: 'tobi' }); - }); - - request(app) - .get('/') - .expect('Content-Type', 'application/json; charset=utf-8') - .expect(200, '{"name":"tobi"}', done) - }) - }) - - describe('"json escape" setting', function () { - it('should be undefined by default', function () { - var app = express() - assert.strictEqual(app.get('json escape'), undefined) - }) - - it('should unicode escape HTML-sniffing characters', function (done) { - var app = express() - - app.enable('json escape') - - app.use(function (req, res) { - res.json({ '&': ''); - }); - - request(app) - .get('/') - .set('Host', 'http://example.com') - .set('Accept', 'text/plain, */*') - .expect('Content-Type', /plain/) - .expect('Location', 'http://example.com/?param=%3Cscript%3Ealert(%22hax%22);%3C/script%3E') - .expect(302, 'Found. Redirecting to http://example.com/?param=%3Cscript%3Ealert(%22hax%22);%3C/script%3E', done) - }) - - it('should include the redirect type', function(done){ - var app = express(); - - app.use(function(req, res){ - res.redirect(301, 'http://google.com'); - }); - - request(app) - .get('/') - .set('Accept', 'text/plain, */*') - .expect('Content-Type', /plain/) - .expect('Location', 'http://google.com') - .expect(301, 'Moved Permanently. Redirecting to http://google.com', done); - }) - }) - - describe('when accepting neither text or html', function(){ - it('should respond with an empty body', function(done){ - var app = express(); - - app.use(function(req, res){ - res.redirect('http://google.com'); - }); - - request(app) - .get('/') - .set('Accept', 'application/octet-stream') - .expect(302) - .expect('location', 'http://google.com') - .expect('content-length', '0') - .expect(utils.shouldNotHaveHeader('Content-Type')) - .expect(utils.shouldNotHaveBody()) - .end(done) - }) - }) -}) diff --git a/test/res.render.js b/test/res.render.js deleted file mode 100644 index 50f0b0a7..00000000 --- a/test/res.render.js +++ /dev/null @@ -1,367 +0,0 @@ -'use strict' - -var express = require('..'); -var path = require('path') -var request = require('supertest'); -var tmpl = require('./support/tmpl'); - -describe('res', function(){ - describe('.render(name)', function(){ - it('should support absolute paths', function(done){ - var app = createApp(); - - app.locals.user = { name: 'tobi' }; - - app.use(function(req, res){ - res.render(path.join(__dirname, 'fixtures', 'user.tmpl')) - }); - - request(app) - .get('/') - .expect('

    tobi

    ', done); - }) - - it('should support absolute paths with "view engine"', function(done){ - var app = createApp(); - - app.locals.user = { name: 'tobi' }; - app.set('view engine', 'tmpl'); - - app.use(function(req, res){ - res.render(path.join(__dirname, 'fixtures', 'user')) - }); - - request(app) - .get('/') - .expect('

    tobi

    ', done); - }) - - it('should error without "view engine" set and file extension to a non-engine module', function (done) { - var app = createApp() - - app.locals.user = { name: 'tobi' } - - app.use(function (req, res) { - res.render(path.join(__dirname, 'fixtures', 'broken.send')) - }) - - request(app) - .get('/') - .expect(500, /does not provide a view engine/, done) - }) - - it('should error without "view engine" set and no file extension', function (done) { - var app = createApp(); - - app.locals.user = { name: 'tobi' }; - - app.use(function(req, res){ - res.render(path.join(__dirname, 'fixtures', 'user')) - }); - - request(app) - .get('/') - .expect(500, /No default engine was specified/, done); - }) - - it('should expose app.locals', function(done){ - var app = createApp(); - - app.set('views', path.join(__dirname, 'fixtures')) - app.locals.user = { name: 'tobi' }; - - app.use(function(req, res){ - res.render('user.tmpl'); - }); - - request(app) - .get('/') - .expect('

    tobi

    ', done); - }) - - it('should expose app.locals with `name` property', function(done){ - var app = createApp(); - - app.set('views', path.join(__dirname, 'fixtures')) - app.locals.name = 'tobi'; - - app.use(function(req, res){ - res.render('name.tmpl'); - }); - - request(app) - .get('/') - .expect('

    tobi

    ', done); - }) - - it('should support index.', function(done){ - var app = createApp(); - - app.set('views', path.join(__dirname, 'fixtures')) - app.set('view engine', 'tmpl'); - - app.use(function(req, res){ - res.render('blog/post'); - }); - - request(app) - .get('/') - .expect('

    blog post

    ', done); - }) - - describe('when an error occurs', function(){ - it('should next(err)', function(done){ - var app = createApp(); - - app.set('views', path.join(__dirname, 'fixtures')) - - app.use(function(req, res){ - res.render('user.tmpl'); - }); - - app.use(function(err, req, res, next){ - res.status(500).send('got error: ' + err.name) - }); - - request(app) - .get('/') - .expect(500, 'got error: RenderError', done) - }) - }) - - describe('when "view engine" is given', function(){ - it('should render the template', function(done){ - var app = createApp(); - - app.set('view engine', 'tmpl'); - app.set('views', path.join(__dirname, 'fixtures')) - - app.use(function(req, res){ - res.render('email'); - }); - - request(app) - .get('/') - .expect('

    This is an email

    ', done); - }) - }) - - describe('when "views" is given', function(){ - it('should lookup the file in the path', function(done){ - var app = createApp(); - - app.set('views', path.join(__dirname, 'fixtures', 'default_layout')) - - app.use(function(req, res){ - res.render('user.tmpl', { user: { name: 'tobi' } }); - }); - - request(app) - .get('/') - .expect('

    tobi

    ', done); - }) - - describe('when array of paths', function(){ - it('should lookup the file in the path', function(done){ - var app = createApp(); - var views = [ - path.join(__dirname, 'fixtures', 'local_layout'), - path.join(__dirname, 'fixtures', 'default_layout') - ] - - app.set('views', views); - - app.use(function(req, res){ - res.render('user.tmpl', { user: { name: 'tobi' } }); - }); - - request(app) - .get('/') - .expect('tobi', done); - }) - - it('should lookup in later paths until found', function(done){ - var app = createApp(); - var views = [ - path.join(__dirname, 'fixtures', 'local_layout'), - path.join(__dirname, 'fixtures', 'default_layout') - ] - - app.set('views', views); - - app.use(function(req, res){ - res.render('name.tmpl', { name: 'tobi' }); - }); - - request(app) - .get('/') - .expect('

    tobi

    ', done); - }) - }) - }) - }) - - describe('.render(name, option)', function(){ - it('should render the template', function(done){ - var app = createApp(); - - app.set('views', path.join(__dirname, 'fixtures')) - - var user = { name: 'tobi' }; - - app.use(function(req, res){ - res.render('user.tmpl', { user: user }); - }); - - request(app) - .get('/') - .expect('

    tobi

    ', done); - }) - - it('should expose app.locals', function(done){ - var app = createApp(); - - app.set('views', path.join(__dirname, 'fixtures')) - app.locals.user = { name: 'tobi' }; - - app.use(function(req, res){ - res.render('user.tmpl'); - }); - - request(app) - .get('/') - .expect('

    tobi

    ', done); - }) - - it('should expose res.locals', function(done){ - var app = createApp(); - - app.set('views', path.join(__dirname, 'fixtures')) - - app.use(function(req, res){ - res.locals.user = { name: 'tobi' }; - res.render('user.tmpl'); - }); - - request(app) - .get('/') - .expect('

    tobi

    ', done); - }) - - it('should give precedence to res.locals over app.locals', function(done){ - var app = createApp(); - - app.set('views', path.join(__dirname, 'fixtures')) - app.locals.user = { name: 'tobi' }; - - app.use(function(req, res){ - res.locals.user = { name: 'jane' }; - res.render('user.tmpl', {}); - }); - - request(app) - .get('/') - .expect('

    jane

    ', done); - }) - - it('should give precedence to res.render() locals over res.locals', function(done){ - var app = createApp(); - - app.set('views', path.join(__dirname, 'fixtures')) - var jane = { name: 'jane' }; - - app.use(function(req, res){ - res.locals.user = { name: 'tobi' }; - res.render('user.tmpl', { user: jane }); - }); - - request(app) - .get('/') - .expect('

    jane

    ', done); - }) - - it('should give precedence to res.render() locals over app.locals', function(done){ - var app = createApp(); - - app.set('views', path.join(__dirname, 'fixtures')) - app.locals.user = { name: 'tobi' }; - var jane = { name: 'jane' }; - - app.use(function(req, res){ - res.render('user.tmpl', { user: jane }); - }); - - request(app) - .get('/') - .expect('

    jane

    ', done); - }) - }) - - describe('.render(name, options, fn)', function(){ - it('should pass the resulting string', function(done){ - var app = createApp(); - - app.set('views', path.join(__dirname, 'fixtures')) - - app.use(function(req, res){ - var tobi = { name: 'tobi' }; - res.render('user.tmpl', { user: tobi }, function (err, html) { - html = html.replace('tobi', 'loki'); - res.end(html); - }); - }); - - request(app) - .get('/') - .expect('

    loki

    ', done); - }) - }) - - describe('.render(name, fn)', function(){ - it('should pass the resulting string', function(done){ - var app = createApp(); - - app.set('views', path.join(__dirname, 'fixtures')) - - app.use(function(req, res){ - res.locals.user = { name: 'tobi' }; - res.render('user.tmpl', function (err, html) { - html = html.replace('tobi', 'loki'); - res.end(html); - }); - }); - - request(app) - .get('/') - .expect('

    loki

    ', done); - }) - - describe('when an error occurs', function(){ - it('should pass it to the callback', function(done){ - var app = createApp(); - - app.set('views', path.join(__dirname, 'fixtures')) - - app.use(function(req, res){ - res.render('user.tmpl', function (err) { - if (err) { - res.status(500).send('got error: ' + err.name) - } - }); - }); - - request(app) - .get('/') - .expect(500, 'got error: RenderError', done) - }) - }) - }) -}) - -function createApp() { - var app = express(); - - app.engine('.tmpl', tmpl); - - return app; -} diff --git a/test/res.send.js b/test/res.send.js deleted file mode 100644 index c92568db..00000000 --- a/test/res.send.js +++ /dev/null @@ -1,596 +0,0 @@ -'use strict' - -var assert = require('assert') -var Buffer = require('safe-buffer').Buffer -var express = require('..'); -var methods = require('methods'); -var request = require('supertest'); -var utils = require('./support/utils'); - -describe('res', function(){ - describe('.send()', function(){ - it('should set body to ""', function(done){ - var app = express(); - - app.use(function(req, res){ - res.send(); - }); - - request(app) - .get('/') - .expect(200, '', done); - }) - }) - - describe('.send(null)', function(){ - it('should set body to ""', function(done){ - var app = express(); - - app.use(function(req, res){ - res.send(null); - }); - - request(app) - .get('/') - .expect('Content-Length', '0') - .expect(200, '', done); - }) - }) - - describe('.send(undefined)', function(){ - it('should set body to ""', function(done){ - var app = express(); - - app.use(function(req, res){ - res.send(undefined); - }); - - request(app) - .get('/') - .expect(200, '', done); - }) - }) - - describe('.send(code)', function(){ - it('should set .statusCode', function(done){ - var app = express(); - - app.use(function(req, res){ - res.send(201) - }); - - request(app) - .get('/') - .expect('Created') - .expect(201, done); - }) - }) - - describe('.send(code, body)', function(){ - it('should set .statusCode and body', function(done){ - var app = express(); - - app.use(function(req, res){ - res.send(201, 'Created :)'); - }); - - request(app) - .get('/') - .expect('Created :)') - .expect(201, done); - }) - }) - - describe('.send(body, code)', function(){ - it('should be supported for backwards compat', function(done){ - var app = express(); - - app.use(function(req, res){ - res.send('Bad!', 400); - }); - - request(app) - .get('/') - .expect('Bad!') - .expect(400, done); - }) - }) - - describe('.send(code, number)', function(){ - it('should send number as json', function(done){ - var app = express(); - - app.use(function(req, res){ - res.send(200, 0.123); - }); - - request(app) - .get('/') - .expect('Content-Type', 'application/json; charset=utf-8') - .expect(200, '0.123', done); - }) - }) - - describe('.send(String)', function(){ - it('should send as html', function(done){ - var app = express(); - - app.use(function(req, res){ - res.send('

    hey

    '); - }); - - request(app) - .get('/') - .expect('Content-Type', 'text/html; charset=utf-8') - .expect(200, '

    hey

    ', done); - }) - - it('should set ETag', function (done) { - var app = express(); - - app.use(function (req, res) { - var str = Array(1000).join('-'); - res.send(str); - }); - - request(app) - .get('/') - .expect('ETag', 'W/"3e7-qPnkJ3CVdVhFJQvUBfF10TmVA7g"') - .expect(200, done); - }) - - it('should not override Content-Type', function(done){ - var app = express(); - - app.use(function(req, res){ - res.set('Content-Type', 'text/plain').send('hey'); - }); - - request(app) - .get('/') - .expect('Content-Type', 'text/plain; charset=utf-8') - .expect(200, 'hey', done); - }) - - it('should override charset in Content-Type', function(done){ - var app = express(); - - app.use(function(req, res){ - res.set('Content-Type', 'text/plain; charset=iso-8859-1').send('hey'); - }); - - request(app) - .get('/') - .expect('Content-Type', 'text/plain; charset=utf-8') - .expect(200, 'hey', done); - }) - - it('should keep charset in Content-Type for Buffers', function(done){ - var app = express(); - - app.use(function(req, res){ - res.set('Content-Type', 'text/plain; charset=iso-8859-1').send(Buffer.from('hi')) - }); - - request(app) - .get('/') - .expect('Content-Type', 'text/plain; charset=iso-8859-1') - .expect(200, 'hi', done); - }) - }) - - describe('.send(Buffer)', function(){ - it('should send as octet-stream', function(done){ - var app = express(); - - app.use(function(req, res){ - res.send(Buffer.from('hello')) - }); - - request(app) - .get('/') - .expect(200) - .expect('Content-Type', 'application/octet-stream') - .expect(utils.shouldHaveBody(Buffer.from('hello'))) - .end(done) - }) - - it('should set ETag', function (done) { - var app = express(); - - app.use(function (req, res) { - res.send(Buffer.alloc(999, '-')) - }); - - request(app) - .get('/') - .expect('ETag', 'W/"3e7-qPnkJ3CVdVhFJQvUBfF10TmVA7g"') - .expect(200, done); - }) - - it('should not override Content-Type', function(done){ - var app = express(); - - app.use(function(req, res){ - res.set('Content-Type', 'text/plain').send(Buffer.from('hey')) - }); - - request(app) - .get('/') - .expect('Content-Type', 'text/plain; charset=utf-8') - .expect(200, 'hey', done); - }) - - it('should not override ETag', function (done) { - var app = express() - - app.use(function (req, res) { - res.type('text/plain').set('ETag', '"foo"').send(Buffer.from('hey')) - }) - - request(app) - .get('/') - .expect('ETag', '"foo"') - .expect(200, 'hey', done) - }) - }) - - describe('.send(Object)', function(){ - it('should send as application/json', function(done){ - var app = express(); - - app.use(function(req, res){ - res.send({ name: 'tobi' }); - }); - - request(app) - .get('/') - .expect('Content-Type', 'application/json; charset=utf-8') - .expect(200, '{"name":"tobi"}', done) - }) - }) - - describe('when the request method is HEAD', function(){ - it('should ignore the body', function(done){ - var app = express(); - - app.use(function(req, res){ - res.send('yay'); - }); - - request(app) - .head('/') - .expect(200) - .expect(utils.shouldNotHaveBody()) - .end(done) - }) - }) - - describe('when .statusCode is 204', function(){ - it('should strip Content-* fields, Transfer-Encoding field, and body', function(done){ - var app = express(); - - app.use(function(req, res){ - res.status(204).set('Transfer-Encoding', 'chunked').send('foo'); - }); - - request(app) - .get('/') - .expect(utils.shouldNotHaveHeader('Content-Type')) - .expect(utils.shouldNotHaveHeader('Content-Length')) - .expect(utils.shouldNotHaveHeader('Transfer-Encoding')) - .expect(204, '', done); - }) - }) - - describe('when .statusCode is 205', function () { - it('should strip Transfer-Encoding field and body, set Content-Length', function (done) { - var app = express() - - app.use(function (req, res) { - res.status(205).set('Transfer-Encoding', 'chunked').send('foo') - }) - - request(app) - .get('/') - .expect(utils.shouldNotHaveHeader('Transfer-Encoding')) - .expect('Content-Length', '0') - .expect(205, '', done) - }) - }) - - describe('when .statusCode is 304', function(){ - it('should strip Content-* fields, Transfer-Encoding field, and body', function(done){ - var app = express(); - - app.use(function(req, res){ - res.status(304).set('Transfer-Encoding', 'chunked').send('foo'); - }); - - request(app) - .get('/') - .expect(utils.shouldNotHaveHeader('Content-Type')) - .expect(utils.shouldNotHaveHeader('Content-Length')) - .expect(utils.shouldNotHaveHeader('Transfer-Encoding')) - .expect(304, '', done); - }) - }) - - it('should always check regardless of length', function(done){ - var app = express(); - var etag = '"asdf"'; - - app.use(function(req, res, next){ - res.set('ETag', etag); - res.send('hey'); - }); - - request(app) - .get('/') - .set('If-None-Match', etag) - .expect(304, done); - }) - - it('should respond with 304 Not Modified when fresh', function(done){ - var app = express(); - var etag = '"asdf"'; - - app.use(function(req, res){ - var str = Array(1000).join('-'); - res.set('ETag', etag); - res.send(str); - }); - - request(app) - .get('/') - .set('If-None-Match', etag) - .expect(304, done); - }) - - it('should not perform freshness check unless 2xx or 304', function(done){ - var app = express(); - var etag = '"asdf"'; - - app.use(function(req, res, next){ - res.status(500); - res.set('ETag', etag); - res.send('hey'); - }); - - request(app) - .get('/') - .set('If-None-Match', etag) - .expect('hey') - .expect(500, done); - }) - - it('should not support jsonp callbacks', function(done){ - var app = express(); - - app.use(function(req, res){ - res.send({ foo: 'bar' }); - }); - - request(app) - .get('/?callback=foo') - .expect('{"foo":"bar"}', done); - }) - - it('should be chainable', function (done) { - var app = express() - - app.use(function (req, res) { - assert.equal(res.send('hey'), res) - }) - - request(app) - .get('/') - .expect(200, 'hey', done) - }) - - describe('"etag" setting', function () { - describe('when enabled', function () { - it('should send ETag', function (done) { - var app = express(); - - app.use(function (req, res) { - res.send('kajdslfkasdf'); - }); - - app.enable('etag'); - - request(app) - .get('/') - .expect('ETag', 'W/"c-IgR/L5SF7CJQff4wxKGF/vfPuZ0"') - .expect(200, done); - }); - - methods.forEach(function (method) { - if (method === 'connect') return; - - it('should send ETag in response to ' + method.toUpperCase() + ' request', function (done) { - var app = express(); - - app[method]('/', function (req, res) { - res.send('kajdslfkasdf'); - }); - - request(app) - [method]('/') - .expect('ETag', 'W/"c-IgR/L5SF7CJQff4wxKGF/vfPuZ0"') - .expect(200, done); - }) - }); - - it('should send ETag for empty string response', function (done) { - var app = express(); - - app.use(function (req, res) { - res.send(''); - }); - - app.enable('etag'); - - request(app) - .get('/') - .expect('ETag', 'W/"0-2jmj7l5rSw0yVb/vlWAYkK/YBwk"') - .expect(200, done); - }) - - it('should send ETag for long response', function (done) { - var app = express(); - - app.use(function (req, res) { - var str = Array(1000).join('-'); - res.send(str); - }); - - app.enable('etag'); - - request(app) - .get('/') - .expect('ETag', 'W/"3e7-qPnkJ3CVdVhFJQvUBfF10TmVA7g"') - .expect(200, done); - }); - - it('should not override ETag when manually set', function (done) { - var app = express(); - - app.use(function (req, res) { - res.set('etag', '"asdf"'); - res.send(200); - }); - - app.enable('etag'); - - request(app) - .get('/') - .expect('ETag', '"asdf"') - .expect(200, done); - }); - - it('should not send ETag for res.send()', function (done) { - var app = express(); - - app.use(function (req, res) { - res.send(); - }); - - app.enable('etag'); - - request(app) - .get('/') - .expect(utils.shouldNotHaveHeader('ETag')) - .expect(200, done); - }) - }); - - describe('when disabled', function () { - it('should send no ETag', function (done) { - var app = express(); - - app.use(function (req, res) { - var str = Array(1000).join('-'); - res.send(str); - }); - - app.disable('etag'); - - request(app) - .get('/') - .expect(utils.shouldNotHaveHeader('ETag')) - .expect(200, done); - }); - - it('should send ETag when manually set', function (done) { - var app = express(); - - app.disable('etag'); - - app.use(function (req, res) { - res.set('etag', '"asdf"'); - res.send(200); - }); - - request(app) - .get('/') - .expect('ETag', '"asdf"') - .expect(200, done); - }); - }); - - describe('when "strong"', function () { - it('should send strong ETag', function (done) { - var app = express(); - - app.set('etag', 'strong'); - - app.use(function (req, res) { - res.send('hello, world!'); - }); - - request(app) - .get('/') - .expect('ETag', '"d-HwnTDHB9U/PRbFMN1z1wps51lqk"') - .expect(200, done); - }) - }) - - describe('when "weak"', function () { - it('should send weak ETag', function (done) { - var app = express(); - - app.set('etag', 'weak'); - - app.use(function (req, res) { - res.send('hello, world!'); - }); - - request(app) - .get('/') - .expect('ETag', 'W/"d-HwnTDHB9U/PRbFMN1z1wps51lqk"') - .expect(200, done) - }) - }) - - describe('when a function', function () { - it('should send custom ETag', function (done) { - var app = express(); - - app.set('etag', function (body, encoding) { - var chunk = !Buffer.isBuffer(body) - ? Buffer.from(body, encoding) - : body; - assert.strictEqual(chunk.toString(), 'hello, world!') - return '"custom"'; - }); - - app.use(function (req, res) { - res.send('hello, world!'); - }); - - request(app) - .get('/') - .expect('ETag', '"custom"') - .expect(200, done); - }) - - it('should not send falsy ETag', function (done) { - var app = express(); - - app.set('etag', function (body, encoding) { - return undefined; - }); - - app.use(function (req, res) { - res.send('hello, world!'); - }); - - request(app) - .get('/') - .expect(utils.shouldNotHaveHeader('ETag')) - .expect(200, done); - }) - }) - }) -}) diff --git a/test/res.sendFile.js b/test/res.sendFile.js deleted file mode 100644 index 4db0a3b6..00000000 --- a/test/res.sendFile.js +++ /dev/null @@ -1,1412 +0,0 @@ -'use strict' - -var after = require('after'); -var asyncHooks = tryRequire('async_hooks') -var Buffer = require('safe-buffer').Buffer -var express = require('../') - , request = require('supertest') - , assert = require('assert'); -var onFinished = require('on-finished'); -var path = require('path'); -var fixtures = path.join(__dirname, 'fixtures'); -var utils = require('./support/utils'); - -var describeAsyncHooks = typeof asyncHooks.AsyncLocalStorage === 'function' - ? describe - : describe.skip - -describe('res', function(){ - describe('.sendFile(path)', function () { - it('should error missing path', function (done) { - var app = createApp(); - - request(app) - .get('/') - .expect(500, /path.*required/, done); - }); - - it('should error for non-string path', function (done) { - var app = createApp(42) - - request(app) - .get('/') - .expect(500, /TypeError: path must be a string to res.sendFile/, done) - }) - - it('should error for non-absolute path', function (done) { - var app = createApp('name.txt') - - request(app) - .get('/') - .expect(500, /TypeError: path must be absolute/, done) - }) - - it('should transfer a file', function (done) { - var app = createApp(path.resolve(fixtures, 'name.txt')); - - request(app) - .get('/') - .expect(200, 'tobi', done); - }); - - it('should transfer a file with special characters in string', function (done) { - var app = createApp(path.resolve(fixtures, '% of dogs.txt')); - - request(app) - .get('/') - .expect(200, '20%', done); - }); - - it('should include ETag', function (done) { - var app = createApp(path.resolve(fixtures, 'name.txt')); - - request(app) - .get('/') - .expect('ETag', /^(?:W\/)?"[^"]+"$/) - .expect(200, 'tobi', done); - }); - - it('should 304 when ETag matches', function (done) { - var app = createApp(path.resolve(fixtures, 'name.txt')); - - request(app) - .get('/') - .expect('ETag', /^(?:W\/)?"[^"]+"$/) - .expect(200, 'tobi', function (err, res) { - if (err) return done(err); - var etag = res.headers.etag; - request(app) - .get('/') - .set('If-None-Match', etag) - .expect(304, done); - }); - }); - - it('should 404 for directory', function (done) { - var app = createApp(path.resolve(fixtures, 'blog')); - - request(app) - .get('/') - .expect(404, done); - }); - - it('should 404 when not found', function (done) { - var app = createApp(path.resolve(fixtures, 'does-no-exist')); - - app.use(function (req, res) { - res.statusCode = 200; - res.send('no!'); - }); - - request(app) - .get('/') - .expect(404, done); - }); - - it('should send cache-control by default', function (done) { - var app = createApp(path.resolve(__dirname, 'fixtures/name.txt')) - - request(app) - .get('/') - .expect('Cache-Control', 'public, max-age=0') - .expect(200, done) - }) - - it('should not serve dotfiles by default', function (done) { - var app = createApp(path.resolve(__dirname, 'fixtures/.name')) - - request(app) - .get('/') - .expect(404, done) - }) - - it('should not override manual content-types', function (done) { - var app = express(); - - app.use(function (req, res) { - res.contentType('application/x-bogus'); - res.sendFile(path.resolve(fixtures, 'name.txt')); - }); - - request(app) - .get('/') - .expect('Content-Type', 'application/x-bogus') - .end(done); - }) - - it('should not error if the client aborts', function (done) { - var app = express(); - var cb = after(2, done) - var error = null - - app.use(function (req, res) { - setImmediate(function () { - res.sendFile(path.resolve(fixtures, 'name.txt')); - setTimeout(function () { - cb(error) - }, 10) - }) - test.req.abort() - }); - - app.use(function (err, req, res, next) { - error = err - next(err) - }); - - var server = app.listen() - var test = request(server).get('/') - test.end(function (err) { - assert.ok(err) - server.close(cb) - }) - }) - }) - - describe('.sendFile(path, fn)', function () { - it('should invoke the callback when complete', function (done) { - var cb = after(2, done); - var app = createApp(path.resolve(fixtures, 'name.txt'), cb); - - request(app) - .get('/') - .expect(200, cb); - }) - - it('should invoke the callback when client aborts', function (done) { - var cb = after(2, done) - var app = express(); - - app.use(function (req, res) { - setImmediate(function () { - res.sendFile(path.resolve(fixtures, 'name.txt'), function (err) { - assert.ok(err) - assert.strictEqual(err.code, 'ECONNABORTED') - cb() - }); - }); - test.req.abort() - }); - - var server = app.listen() - var test = request(server).get('/') - test.end(function (err) { - assert.ok(err) - server.close(cb) - }) - }) - - it('should invoke the callback when client already aborted', function (done) { - var cb = after(2, done) - var app = express(); - - app.use(function (req, res) { - onFinished(res, function () { - res.sendFile(path.resolve(fixtures, 'name.txt'), function (err) { - assert.ok(err) - assert.strictEqual(err.code, 'ECONNABORTED') - cb() - }); - }); - test.req.abort() - }); - - var server = app.listen() - var test = request(server).get('/') - test.end(function (err) { - assert.ok(err) - server.close(cb) - }) - }) - - it('should invoke the callback without error when HEAD', function (done) { - var app = express(); - var cb = after(2, done); - - app.use(function (req, res) { - res.sendFile(path.resolve(fixtures, 'name.txt'), cb); - }); - - request(app) - .head('/') - .expect(200, cb); - }); - - it('should invoke the callback without error when 304', function (done) { - var app = express(); - var cb = after(3, done); - - app.use(function (req, res) { - res.sendFile(path.resolve(fixtures, 'name.txt'), cb); - }); - - request(app) - .get('/') - .expect('ETag', /^(?:W\/)?"[^"]+"$/) - .expect(200, 'tobi', function (err, res) { - if (err) return cb(err); - var etag = res.headers.etag; - request(app) - .get('/') - .set('If-None-Match', etag) - .expect(304, cb); - }); - }); - - it('should invoke the callback on 404', function(done){ - var app = express(); - - app.use(function (req, res) { - res.sendFile(path.resolve(fixtures, 'does-not-exist'), function (err) { - res.send(err ? 'got ' + err.status + ' error' : 'no error') - }); - }); - - request(app) - .get('/') - .expect(200, 'got 404 error', done) - }) - - describeAsyncHooks('async local storage', function () { - it('should presist store', function (done) { - var app = express() - var cb = after(2, done) - var store = { foo: 'bar' } - - app.use(function (req, res, next) { - req.asyncLocalStorage = new asyncHooks.AsyncLocalStorage() - req.asyncLocalStorage.run(store, next) - }) - - app.use(function (req, res) { - res.sendFile(path.resolve(fixtures, 'name.txt'), function (err) { - if (err) return cb(err) - - var local = req.asyncLocalStorage.getStore() - - assert.strictEqual(local.foo, 'bar') - cb() - }) - }) - - request(app) - .get('/') - .expect('Content-Type', 'text/plain; charset=UTF-8') - .expect(200, 'tobi', cb) - }) - - it('should presist store on error', function (done) { - var app = express() - var store = { foo: 'bar' } - - app.use(function (req, res, next) { - req.asyncLocalStorage = new asyncHooks.AsyncLocalStorage() - req.asyncLocalStorage.run(store, next) - }) - - app.use(function (req, res) { - res.sendFile(path.resolve(fixtures, 'does-not-exist'), function (err) { - var local = req.asyncLocalStorage.getStore() - - if (local) { - res.setHeader('x-store-foo', String(local.foo)) - } - - res.send(err ? 'got ' + err.status + ' error' : 'no error') - }) - }) - - request(app) - .get('/') - .expect(200) - .expect('x-store-foo', 'bar') - .expect('got 404 error') - .end(done) - }) - }) - }) - - describe('.sendFile(path, options)', function () { - it('should pass options to send module', function (done) { - request(createApp(path.resolve(fixtures, 'name.txt'), { start: 0, end: 1 })) - .get('/') - .expect(200, 'to', done) - }) - - describe('with "acceptRanges" option', function () { - describe('when true', function () { - it('should advertise byte range accepted', function (done) { - var app = express() - - app.use(function (req, res) { - res.sendFile(path.resolve(fixtures, 'nums.txt'), { - acceptRanges: true - }) - }) - - request(app) - .get('/') - .expect(200) - .expect('Accept-Ranges', 'bytes') - .expect('123456789') - .end(done) - }) - - it('should respond to range request', function (done) { - var app = express() - - app.use(function (req, res) { - res.sendFile(path.resolve(fixtures, 'nums.txt'), { - acceptRanges: true - }) - }) - - request(app) - .get('/') - .set('Range', 'bytes=0-4') - .expect(206, '12345', done) - }) - }) - - describe('when false', function () { - it('should not advertise accept-ranges', function (done) { - var app = express() - - app.use(function (req, res) { - res.sendFile(path.resolve(fixtures, 'nums.txt'), { - acceptRanges: false - }) - }) - - request(app) - .get('/') - .expect(200) - .expect(utils.shouldNotHaveHeader('Accept-Ranges')) - .end(done) - }) - - it('should not honor range requests', function (done) { - var app = express() - - app.use(function (req, res) { - res.sendFile(path.resolve(fixtures, 'nums.txt'), { - acceptRanges: false - }) - }) - - request(app) - .get('/') - .set('Range', 'bytes=0-4') - .expect(200, '123456789', done) - }) - }) - }) - - describe('with "cacheControl" option', function () { - describe('when true', function () { - it('should send cache-control header', function (done) { - var app = express() - - app.use(function (req, res) { - res.sendFile(path.resolve(fixtures, 'user.html'), { - cacheControl: true - }) - }) - - request(app) - .get('/') - .expect(200) - .expect('Cache-Control', 'public, max-age=0') - .end(done) - }) - }) - - describe('when false', function () { - it('should not send cache-control header', function (done) { - var app = express() - - app.use(function (req, res) { - res.sendFile(path.resolve(fixtures, 'user.html'), { - cacheControl: false - }) - }) - - request(app) - .get('/') - .expect(200) - .expect(utils.shouldNotHaveHeader('Cache-Control')) - .end(done) - }) - }) - }) - - describe('with "dotfiles" option', function () { - describe('when "allow"', function () { - it('should allow dotfiles', function (done) { - var app = express() - - app.use(function (req, res) { - res.sendFile(path.resolve(fixtures, '.name'), { - dotfiles: 'allow' - }) - }) - - request(app) - .get('/') - .expect(200) - .expect(utils.shouldHaveBody(Buffer.from('tobi'))) - .end(done) - }) - }) - - describe('when "deny"', function () { - it('should deny dotfiles', function (done) { - var app = express() - - app.use(function (req, res) { - res.sendFile(path.resolve(fixtures, '.name'), { - dotfiles: 'deny' - }) - }) - - request(app) - .get('/') - .expect(403) - .expect(/Forbidden/) - .end(done) - }) - }) - - describe('when "ignore"', function () { - it('should ignore dotfiles', function (done) { - var app = express() - - app.use(function (req, res) { - res.sendFile(path.resolve(fixtures, '.name'), { - dotfiles: 'ignore' - }) - }) - - request(app) - .get('/') - .expect(404) - .expect(/Not Found/) - .end(done) - }) - }) - }) - - describe('with "headers" option', function () { - it('should set headers on response', function (done) { - var app = express() - - app.use(function (req, res) { - res.sendFile(path.resolve(fixtures, 'user.html'), { - headers: { - 'X-Foo': 'Bar', - 'X-Bar': 'Foo' - } - }) - }) - - request(app) - .get('/') - .expect(200) - .expect('X-Foo', 'Bar') - .expect('X-Bar', 'Foo') - .end(done) - }) - - it('should use last header when duplicated', function (done) { - var app = express() - - app.use(function (req, res) { - res.sendFile(path.resolve(fixtures, 'user.html'), { - headers: { - 'X-Foo': 'Bar', - 'x-foo': 'bar' - } - }) - }) - - request(app) - .get('/') - .expect(200) - .expect('X-Foo', 'bar') - .end(done) - }) - - it('should override Content-Type', function (done) { - var app = express() - - app.use(function (req, res) { - res.sendFile(path.resolve(fixtures, 'user.html'), { - headers: { - 'Content-Type': 'text/x-custom' - } - }) - }) - - request(app) - .get('/') - .expect(200) - .expect('Content-Type', 'text/x-custom') - .end(done) - }) - - it('should not set headers on 404', function (done) { - var app = express() - - app.use(function (req, res) { - res.sendFile(path.resolve(fixtures, 'does-not-exist'), { - headers: { - 'X-Foo': 'Bar' - } - }) - }) - - request(app) - .get('/') - .expect(404) - .expect(utils.shouldNotHaveHeader('X-Foo')) - .end(done) - }) - }) - - describe('with "immutable" option', function () { - describe('when true', function () { - it('should send cache-control header with immutable', function (done) { - var app = express() - - app.use(function (req, res) { - res.sendFile(path.resolve(fixtures, 'user.html'), { - immutable: true - }) - }) - - request(app) - .get('/') - .expect(200) - .expect('Cache-Control', 'public, max-age=0, immutable') - .end(done) - }) - }) - - describe('when false', function () { - it('should not send cache-control header with immutable', function (done) { - var app = express() - - app.use(function (req, res) { - res.sendFile(path.resolve(fixtures, 'user.html'), { - immutable: false - }) - }) - - request(app) - .get('/') - .expect(200) - .expect('Cache-Control', 'public, max-age=0') - .end(done) - }) - }) - }) - - describe('with "lastModified" option', function () { - describe('when true', function () { - it('should send last-modified header', function (done) { - var app = express() - - app.use(function (req, res) { - res.sendFile(path.resolve(fixtures, 'user.html'), { - lastModified: true - }) - }) - - request(app) - .get('/') - .expect(200) - .expect(utils.shouldHaveHeader('Last-Modified')) - .end(done) - }) - - it('should conditionally respond with if-modified-since', function (done) { - var app = express() - - app.use(function (req, res) { - res.sendFile(path.resolve(fixtures, 'user.html'), { - lastModified: true - }) - }) - - request(app) - .get('/') - .set('If-Modified-Since', (new Date(Date.now() + 99999).toUTCString())) - .expect(304, done) - }) - }) - - describe('when false', function () { - it('should not have last-modified header', function (done) { - var app = express() - - app.use(function (req, res) { - res.sendFile(path.resolve(fixtures, 'user.html'), { - lastModified: false - }) - }) - - request(app) - .get('/') - .expect(200) - .expect(utils.shouldNotHaveHeader('Last-Modified')) - .end(done) - }) - - it('should not honor if-modified-since', function (done) { - var app = express() - - app.use(function (req, res) { - res.sendFile(path.resolve(fixtures, 'user.html'), { - lastModified: false - }) - }) - - request(app) - .get('/') - .set('If-Modified-Since', (new Date(Date.now() + 99999).toUTCString())) - .expect(200) - .expect(utils.shouldNotHaveHeader('Last-Modified')) - .end(done) - }) - }) - }) - - describe('with "maxAge" option', function () { - it('should set cache-control max-age to milliseconds', function (done) { - var app = express() - - app.use(function (req, res) { - res.sendFile(path.resolve(fixtures, 'user.html'), { - maxAge: 20000 - }) - }) - - request(app) - .get('/') - .expect(200) - .expect('Cache-Control', 'public, max-age=20') - .end(done) - }) - - it('should cap cache-control max-age to 1 year', function (done) { - var app = express() - - app.use(function (req, res) { - res.sendFile(path.resolve(fixtures, 'user.html'), { - maxAge: 99999999999 - }) - }) - - request(app) - .get('/') - .expect(200) - .expect('Cache-Control', 'public, max-age=31536000') - .end(done) - }) - - it('should min cache-control max-age to 0', function (done) { - var app = express() - - app.use(function (req, res) { - res.sendFile(path.resolve(fixtures, 'user.html'), { - maxAge: -20000 - }) - }) - - request(app) - .get('/') - .expect(200) - .expect('Cache-Control', 'public, max-age=0') - .end(done) - }) - - it('should floor cache-control max-age', function (done) { - var app = express() - - app.use(function (req, res) { - res.sendFile(path.resolve(fixtures, 'user.html'), { - maxAge: 21911.23 - }) - }) - - request(app) - .get('/') - .expect(200) - .expect('Cache-Control', 'public, max-age=21') - .end(done) - }) - - describe('when cacheControl: false', function () { - it('should not send cache-control', function (done) { - var app = express() - - app.use(function (req, res) { - res.sendFile(path.resolve(fixtures, 'user.html'), { - cacheControl: false, - maxAge: 20000 - }) - }) - - request(app) - .get('/') - .expect(200) - .expect(utils.shouldNotHaveHeader('Cache-Control')) - .end(done) - }) - }) - - describe('when string', function () { - it('should accept plain number as milliseconds', function (done) { - var app = express() - - app.use(function (req, res) { - res.sendFile(path.resolve(fixtures, 'user.html'), { - maxAge: '20000' - }) - }) - - request(app) - .get('/') - .expect(200) - .expect('Cache-Control', 'public, max-age=20') - .end(done) - }) - - it('should accept suffix "s" for seconds', function (done) { - var app = express() - - app.use(function (req, res) { - res.sendFile(path.resolve(fixtures, 'user.html'), { - maxAge: '20s' - }) - }) - - request(app) - .get('/') - .expect(200) - .expect('Cache-Control', 'public, max-age=20') - .end(done) - }) - - it('should accept suffix "m" for minutes', function (done) { - var app = express() - - app.use(function (req, res) { - res.sendFile(path.resolve(fixtures, 'user.html'), { - maxAge: '20m' - }) - }) - - request(app) - .get('/') - .expect(200) - .expect('Cache-Control', 'public, max-age=1200') - .end(done) - }) - - it('should accept suffix "d" for days', function (done) { - var app = express() - - app.use(function (req, res) { - res.sendFile(path.resolve(fixtures, 'user.html'), { - maxAge: '20d' - }) - }) - - request(app) - .get('/') - .expect(200) - .expect('Cache-Control', 'public, max-age=1728000') - .end(done) - }) - }) - }) - - describe('with "root" option', function () { - it('should allow relative path', function (done) { - var app = express() - - app.use(function (req, res) { - res.sendFile('name.txt', { - root: fixtures - }) - }) - - request(app) - .get('/') - .expect(200, 'tobi', done) - }) - - it('should allow up within root', function (done) { - var app = express() - - app.use(function (req, res) { - res.sendFile('fake/../name.txt', { - root: fixtures - }) - }) - - request(app) - .get('/') - .expect(200, 'tobi', done) - }) - - it('should reject up outside root', function (done) { - var app = express() - - app.use(function (req, res) { - res.sendFile('..' + path.sep + path.relative(path.dirname(fixtures), path.join(fixtures, 'name.txt')), { - root: fixtures - }) - }) - - request(app) - .get('/') - .expect(403, done) - }) - - it('should reject reading outside root', function (done) { - var app = express() - - app.use(function (req, res) { - res.sendFile('../name.txt', { - root: fixtures - }) - }) - - request(app) - .get('/') - .expect(403, done) - }) - }) - }) - - describe('.sendfile(path, fn)', function(){ - it('should invoke the callback when complete', function(done){ - var app = express(); - var cb = after(2, done); - - app.use(function(req, res){ - res.sendfile('test/fixtures/user.html', cb) - }); - - request(app) - .get('/') - .expect(200, cb); - }) - - it('should utilize the same options as express.static()', function(done){ - var app = express(); - - app.use(function(req, res){ - res.sendfile('test/fixtures/user.html', { maxAge: 60000 }); - }); - - request(app) - .get('/') - .expect('Cache-Control', 'public, max-age=60') - .end(done); - }) - - it('should invoke the callback when client aborts', function (done) { - var cb = after(2, done) - var app = express(); - - app.use(function (req, res) { - setImmediate(function () { - res.sendfile('test/fixtures/name.txt', function (err) { - assert.ok(err) - assert.strictEqual(err.code, 'ECONNABORTED') - cb() - }); - }); - test.req.abort() - }); - - var server = app.listen() - var test = request(server).get('/') - test.end(function (err) { - assert.ok(err) - server.close(cb) - }) - }) - - it('should invoke the callback when client already aborted', function (done) { - var cb = after(2, done) - var app = express(); - - app.use(function (req, res) { - onFinished(res, function () { - res.sendfile('test/fixtures/name.txt', function (err) { - assert.ok(err) - assert.strictEqual(err.code, 'ECONNABORTED') - cb() - }); - }); - test.req.abort() - }); - - var server = app.listen() - var test = request(server).get('/') - test.end(function (err) { - assert.ok(err) - server.close(cb) - }) - }) - - it('should invoke the callback without error when HEAD', function (done) { - var app = express(); - var cb = after(2, done); - - app.use(function (req, res) { - res.sendfile('test/fixtures/name.txt', cb); - }); - - request(app) - .head('/') - .expect(200, cb); - }); - - it('should invoke the callback without error when 304', function (done) { - var app = express(); - var cb = after(3, done); - - app.use(function (req, res) { - res.sendfile('test/fixtures/name.txt', cb); - }); - - request(app) - .get('/') - .expect('ETag', /^(?:W\/)?"[^"]+"$/) - .expect(200, 'tobi', function (err, res) { - if (err) return cb(err); - var etag = res.headers.etag; - request(app) - .get('/') - .set('If-None-Match', etag) - .expect(304, cb); - }); - }); - - it('should invoke the callback on 404', function(done){ - var app = express(); - var calls = 0; - - app.use(function(req, res){ - res.sendfile('test/fixtures/nope.html', function(err){ - assert.equal(calls++, 0); - assert(!res.headersSent); - res.send(err.message); - }); - }); - - request(app) - .get('/') - .expect(200, /^ENOENT.*?, stat/, done); - }) - - it('should not override manual content-types', function(done){ - var app = express(); - - app.use(function(req, res){ - res.contentType('txt'); - res.sendfile('test/fixtures/user.html'); - }); - - request(app) - .get('/') - .expect('Content-Type', 'text/plain; charset=utf-8') - .end(done); - }) - - it('should invoke the callback on 403', function(done){ - var app = express() - - app.use(function(req, res){ - res.sendfile('test/fixtures/foo/../user.html', function(err){ - assert(!res.headersSent); - res.send(err.message); - }); - }); - - request(app) - .get('/') - .expect('Forbidden') - .expect(200, done); - }) - - it('should invoke the callback on socket error', function(done){ - var app = express() - - app.use(function(req, res){ - res.sendfile('test/fixtures/user.html', function(err){ - assert.ok(err) - assert.ok(!res.headersSent) - assert.strictEqual(err.message, 'broken!') - done(); - }); - - req.socket.destroy(new Error('broken!')) - }); - - request(app) - .get('/') - .end(function(){}); - }) - - describeAsyncHooks('async local storage', function () { - it('should presist store', function (done) { - var app = express() - var cb = after(2, done) - var store = { foo: 'bar' } - - app.use(function (req, res, next) { - req.asyncLocalStorage = new asyncHooks.AsyncLocalStorage() - req.asyncLocalStorage.run(store, next) - }) - - app.use(function (req, res) { - res.sendfile('test/fixtures/name.txt', function (err) { - if (err) return cb(err) - - var local = req.asyncLocalStorage.getStore() - - assert.strictEqual(local.foo, 'bar') - cb() - }) - }) - - request(app) - .get('/') - .expect('Content-Type', 'text/plain; charset=UTF-8') - .expect(200, 'tobi', cb) - }) - - it('should presist store on error', function (done) { - var app = express() - var store = { foo: 'bar' } - - app.use(function (req, res, next) { - req.asyncLocalStorage = new asyncHooks.AsyncLocalStorage() - req.asyncLocalStorage.run(store, next) - }) - - app.use(function (req, res) { - res.sendfile('test/fixtures/does-not-exist', function (err) { - var local = req.asyncLocalStorage.getStore() - - if (local) { - res.setHeader('x-store-foo', String(local.foo)) - } - - res.send(err ? 'got ' + err.status + ' error' : 'no error') - }) - }) - - request(app) - .get('/') - .expect(200) - .expect('x-store-foo', 'bar') - .expect('got 404 error') - .end(done) - }) - }) - }) - - describe('.sendfile(path)', function(){ - it('should not serve dotfiles', function(done){ - var app = express(); - - app.use(function(req, res){ - res.sendfile('test/fixtures/.name'); - }); - - request(app) - .get('/') - .expect(404, done); - }) - - it('should accept dotfiles option', function(done){ - var app = express(); - - app.use(function(req, res){ - res.sendfile('test/fixtures/.name', { dotfiles: 'allow' }); - }); - - request(app) - .get('/') - .expect(200) - .expect(utils.shouldHaveBody(Buffer.from('tobi'))) - .end(done) - }) - - it('should accept headers option', function(done){ - var app = express(); - var headers = { - 'x-success': 'sent', - 'x-other': 'done' - }; - - app.use(function(req, res){ - res.sendfile('test/fixtures/user.html', { headers: headers }); - }); - - request(app) - .get('/') - .expect('x-success', 'sent') - .expect('x-other', 'done') - .expect(200, done); - }) - - it('should ignore headers option on 404', function(done){ - var app = express(); - var headers = { 'x-success': 'sent' }; - - app.use(function(req, res){ - res.sendfile('test/fixtures/user.nothing', { headers: headers }); - }); - - request(app) - .get('/') - .expect(utils.shouldNotHaveHeader('X-Success')) - .expect(404, done); - }) - - it('should transfer a file', function (done) { - var app = express(); - - app.use(function (req, res) { - res.sendfile('test/fixtures/name.txt'); - }); - - request(app) - .get('/') - .expect(200, 'tobi', done); - }); - - it('should transfer a directory index file', function (done) { - var app = express(); - - app.use(function (req, res) { - res.sendfile('test/fixtures/blog/'); - }); - - request(app) - .get('/') - .expect(200, 'index', done); - }); - - it('should 404 for directory without trailing slash', function (done) { - var app = express(); - - app.use(function (req, res) { - res.sendfile('test/fixtures/blog'); - }); - - request(app) - .get('/') - .expect(404, done); - }); - - it('should transfer a file with urlencoded name', function (done) { - var app = express(); - - app.use(function (req, res) { - res.sendfile('test/fixtures/%25%20of%20dogs.txt'); - }); - - request(app) - .get('/') - .expect(200, '20%', done); - }); - - it('should not error if the client aborts', function (done) { - var app = express(); - var cb = after(2, done) - var error = null - - app.use(function (req, res) { - setImmediate(function () { - res.sendfile(path.resolve(fixtures, 'name.txt')); - setTimeout(function () { - cb(error) - }, 10) - }); - test.req.abort() - }); - - app.use(function (err, req, res, next) { - error = err - next(err) - }); - - var server = app.listen() - var test = request(server).get('/') - test.end(function (err) { - assert.ok(err) - server.close(cb) - }) - }) - - describe('with an absolute path', function(){ - it('should transfer the file', function(done){ - var app = express(); - - app.use(function(req, res){ - res.sendfile(path.join(__dirname, '/fixtures/user.html')) - }); - - request(app) - .get('/') - .expect('Content-Type', 'text/html; charset=UTF-8') - .expect(200, '

    {{user.name}}

    ', done); - }) - }) - - describe('with a relative path', function(){ - it('should transfer the file', function(done){ - var app = express(); - - app.use(function(req, res){ - res.sendfile('test/fixtures/user.html'); - }); - - request(app) - .get('/') - .expect('Content-Type', 'text/html; charset=UTF-8') - .expect(200, '

    {{user.name}}

    ', done); - }) - - it('should serve relative to "root"', function(done){ - var app = express(); - - app.use(function(req, res){ - res.sendfile('user.html', { root: 'test/fixtures/' }); - }); - - request(app) - .get('/') - .expect('Content-Type', 'text/html; charset=UTF-8') - .expect(200, '

    {{user.name}}

    ', done); - }) - - it('should consider ../ malicious when "root" is not set', function(done){ - var app = express(); - - app.use(function(req, res){ - res.sendfile('test/fixtures/foo/../user.html'); - }); - - request(app) - .get('/') - .expect(403, done); - }) - - it('should allow ../ when "root" is set', function(done){ - var app = express(); - - app.use(function(req, res){ - res.sendfile('foo/../user.html', { root: 'test/fixtures' }); - }); - - request(app) - .get('/') - .expect(200, done); - }) - - it('should disallow requesting out of "root"', function(done){ - var app = express(); - - app.use(function(req, res){ - res.sendfile('foo/../../user.html', { root: 'test/fixtures' }); - }); - - request(app) - .get('/') - .expect(403, done); - }) - - it('should next(404) when not found', function(done){ - var app = express() - , calls = 0; - - app.use(function(req, res){ - res.sendfile('user.html'); - }); - - app.use(function(req, res){ - assert(0, 'this should not be called'); - }); - - app.use(function(err, req, res, next){ - ++calls; - next(err); - }); - - request(app) - .get('/') - .expect(404, function (err) { - if (err) return done(err) - assert.strictEqual(calls, 1) - done() - }) - }) - - describe('with non-GET', function(){ - it('should still serve', function(done){ - var app = express() - - app.use(function(req, res){ - res.sendfile(path.join(__dirname, '/fixtures/name.txt')) - }); - - request(app) - .get('/') - .expect('tobi', done); - }) - }) - }) - }) - - describe('.sendfile(path, options)', function () { - it('should pass options to send module', function (done) { - var app = express() - - app.use(function (req, res) { - res.sendfile(path.resolve(fixtures, 'name.txt'), { start: 0, end: 1 }) - }) - - request(app) - .get('/') - .expect(200, 'to', done) - }) - }) -}) - -function createApp(path, options, fn) { - var app = express(); - - app.use(function (req, res) { - res.sendFile(path, options, fn); - }); - - return app; -} - -function tryRequire (name) { - try { - return require(name) - } catch (e) { - return {} - } -} diff --git a/test/res.sendStatus.js b/test/res.sendStatus.js deleted file mode 100644 index 9b1de838..00000000 --- a/test/res.sendStatus.js +++ /dev/null @@ -1,32 +0,0 @@ -'use strict' - -var express = require('..') -var request = require('supertest') - -describe('res', function () { - describe('.sendStatus(statusCode)', function () { - it('should send the status code and message as body', function (done) { - var app = express(); - - app.use(function(req, res){ - res.sendStatus(201); - }); - - request(app) - .get('/') - .expect(201, 'Created', done); - }) - - it('should work with unknown code', function (done) { - var app = express(); - - app.use(function(req, res){ - res.sendStatus(599); - }); - - request(app) - .get('/') - .expect(599, '599', done); - }) - }) -}) diff --git a/test/res.set.js b/test/res.set.js deleted file mode 100644 index 04511c1c..00000000 --- a/test/res.set.js +++ /dev/null @@ -1,124 +0,0 @@ -'use strict' - -var express = require('..'); -var request = require('supertest'); - -describe('res', function(){ - describe('.set(field, value)', function(){ - it('should set the response header field', function(done){ - var app = express(); - - app.use(function(req, res){ - res.set('Content-Type', 'text/x-foo; charset=utf-8').end(); - }); - - request(app) - .get('/') - .expect('Content-Type', 'text/x-foo; charset=utf-8') - .end(done); - }) - - it('should coerce to a string', function (done) { - var app = express(); - - app.use(function (req, res) { - res.set('X-Number', 123); - res.end(typeof res.get('X-Number')); - }); - - request(app) - .get('/') - .expect('X-Number', '123') - .expect(200, 'string', done); - }) - }) - - describe('.set(field, values)', function(){ - it('should set multiple response header fields', function(done){ - var app = express(); - - app.use(function(req, res){ - res.set('Set-Cookie', ["type=ninja", "language=javascript"]); - res.send(res.get('Set-Cookie')); - }); - - request(app) - .get('/') - .expect('["type=ninja","language=javascript"]', done); - }) - - it('should coerce to an array of strings', function (done) { - var app = express(); - - app.use(function (req, res) { - res.set('X-Numbers', [123, 456]); - res.end(JSON.stringify(res.get('X-Numbers'))); - }); - - request(app) - .get('/') - .expect('X-Numbers', '123, 456') - .expect(200, '["123","456"]', done); - }) - - it('should not set a charset of one is already set', function (done) { - var app = express(); - - app.use(function (req, res) { - res.set('Content-Type', 'text/html; charset=lol'); - res.end(); - }); - - request(app) - .get('/') - .expect('Content-Type', 'text/html; charset=lol') - .expect(200, done); - }) - - it('should throw when Content-Type is an array', function (done) { - var app = express() - - app.use(function (req, res) { - res.set('Content-Type', ['text/html']) - res.end() - }); - - request(app) - .get('/') - .expect(500, /TypeError: Content-Type cannot be set to an Array/, done) - }) - }) - - describe('.set(object)', function(){ - it('should set multiple fields', function(done){ - var app = express(); - - app.use(function(req, res){ - res.set({ - 'X-Foo': 'bar', - 'X-Bar': 'baz' - }).end(); - }); - - request(app) - .get('/') - .expect('X-Foo', 'bar') - .expect('X-Bar', 'baz') - .end(done); - }) - - it('should coerce to a string', function (done) { - var app = express(); - - app.use(function (req, res) { - res.set({ 'X-Number': 123 }); - res.end(typeof res.get('X-Number')); - }); - - request(app) - .get('/') - .expect('X-Number', '123') - .expect(200, 'string', done); - }) - }) -}) diff --git a/test/res.status.js b/test/res.status.js deleted file mode 100644 index 1fe08344..00000000 --- a/test/res.status.js +++ /dev/null @@ -1,202 +0,0 @@ -'use strict' - -var express = require('../') -var request = require('supertest') - -var isIoJs = process.release - ? process.release.name === 'io.js' - : ['v1.', 'v2.', 'v3.'].indexOf(process.version.slice(0, 3)) !== -1 - -describe('res', function () { - describe('.status(code)', function () { - describe('when "code" is undefined', function () { - it('should raise error for invalid status code', function (done) { - var app = express() - - app.use(function (req, res) { - res.status(undefined).end() - }) - - request(app) - .get('/') - .expect(500, /Invalid status code/, function (err) { - if (isIoJs) { - done(err ? null : new Error('expected error')) - } else { - done(err) - } - }) - }) - }) - - describe('when "code" is null', function () { - it('should raise error for invalid status code', function (done) { - var app = express() - - app.use(function (req, res) { - res.status(null).end() - }) - - request(app) - .get('/') - .expect(500, /Invalid status code/, function (err) { - if (isIoJs) { - done(err ? null : new Error('expected error')) - } else { - done(err) - } - }) - }) - }) - - describe('when "code" is 201', function () { - it('should set the response status code to 201', function (done) { - var app = express() - - app.use(function (req, res) { - res.status(201).end() - }) - - request(app) - .get('/') - .expect(201, done) - }) - }) - - describe('when "code" is 302', function () { - it('should set the response status code to 302', function (done) { - var app = express() - - app.use(function (req, res) { - res.status(302).end() - }) - - request(app) - .get('/') - .expect(302, done) - }) - }) - - describe('when "code" is 403', function () { - it('should set the response status code to 403', function (done) { - var app = express() - - app.use(function (req, res) { - res.status(403).end() - }) - - request(app) - .get('/') - .expect(403, done) - }) - }) - - describe('when "code" is 501', function () { - it('should set the response status code to 501', function (done) { - var app = express() - - app.use(function (req, res) { - res.status(501).end() - }) - - request(app) - .get('/') - .expect(501, done) - }) - }) - - describe('when "code" is "410"', function () { - it('should set the response status code to 410', function (done) { - var app = express() - - app.use(function (req, res) { - res.status('410').end() - }) - - request(app) - .get('/') - .expect(410, done) - }) - }) - - describe('when "code" is 410.1', function () { - it('should set the response status code to 410', function (done) { - var app = express() - - app.use(function (req, res) { - res.status(410.1).end() - }) - - request(app) - .get('/') - .expect(410, function (err) { - if (isIoJs) { - done(err ? null : new Error('expected error')) - } else { - done(err) - } - }) - }) - }) - - describe('when "code" is 1000', function () { - it('should raise error for invalid status code', function (done) { - var app = express() - - app.use(function (req, res) { - res.status(1000).end() - }) - - request(app) - .get('/') - .expect(500, /Invalid status code/, function (err) { - if (isIoJs) { - done(err ? null : new Error('expected error')) - } else { - done(err) - } - }) - }) - }) - - describe('when "code" is 99', function () { - it('should raise error for invalid status code', function (done) { - var app = express() - - app.use(function (req, res) { - res.status(99).end() - }) - - request(app) - .get('/') - .expect(500, /Invalid status code/, function (err) { - if (isIoJs) { - done(err ? null : new Error('expected error')) - } else { - done(err) - } - }) - }) - }) - - describe('when "code" is -401', function () { - it('should raise error for invalid status code', function (done) { - var app = express() - - app.use(function (req, res) { - res.status(-401).end() - }) - - request(app) - .get('/') - .expect(500, /Invalid status code/, function (err) { - if (isIoJs) { - done(err ? null : new Error('expected error')) - } else { - done(err) - } - }) - }) - }) - }) -}) diff --git a/test/res.type.js b/test/res.type.js deleted file mode 100644 index 980717a6..00000000 --- a/test/res.type.js +++ /dev/null @@ -1,46 +0,0 @@ -'use strict' - -var express = require('../') - , request = require('supertest'); - -describe('res', function(){ - describe('.type(str)', function(){ - it('should set the Content-Type based on a filename', function(done){ - var app = express(); - - app.use(function(req, res){ - res.type('foo.js').end('var name = "tj";'); - }); - - request(app) - .get('/') - .expect('Content-Type', 'application/javascript; charset=utf-8') - .end(done) - }) - - it('should default to application/octet-stream', function(done){ - var app = express(); - - app.use(function(req, res){ - res.type('rawr').end('var name = "tj";'); - }); - - request(app) - .get('/') - .expect('Content-Type', 'application/octet-stream', done); - }) - - it('should set the Content-Type with type/subtype', function(done){ - var app = express(); - - app.use(function(req, res){ - res.type('application/vnd.amazon.ebook') - .end('var name = "tj";'); - }); - - request(app) - .get('/') - .expect('Content-Type', 'application/vnd.amazon.ebook', done); - }) - }) -}) diff --git a/test/res.vary.js b/test/res.vary.js deleted file mode 100644 index 1efc20b4..00000000 --- a/test/res.vary.js +++ /dev/null @@ -1,91 +0,0 @@ -'use strict' - -var express = require('..'); -var request = require('supertest'); -var utils = require('./support/utils'); - -describe('res.vary()', function(){ - describe('with no arguments', function(){ - it('should not set Vary', function (done) { - var app = express(); - - app.use(function (req, res) { - res.vary(); - res.end(); - }); - - request(app) - .get('/') - .expect(utils.shouldNotHaveHeader('Vary')) - .expect(200, done); - }) - }) - - describe('with an empty array', function(){ - it('should not set Vary', function (done) { - var app = express(); - - app.use(function (req, res) { - res.vary([]); - res.end(); - }); - - request(app) - .get('/') - .expect(utils.shouldNotHaveHeader('Vary')) - .expect(200, done); - }) - }) - - describe('with an array', function(){ - it('should set the values', function (done) { - var app = express(); - - app.use(function (req, res) { - res.vary(['Accept', 'Accept-Language', 'Accept-Encoding']); - res.end(); - }); - - request(app) - .get('/') - .expect('Vary', 'Accept, Accept-Language, Accept-Encoding') - .expect(200, done); - }) - }) - - describe('with a string', function(){ - it('should set the value', function (done) { - var app = express(); - - app.use(function (req, res) { - res.vary('Accept'); - res.end(); - }); - - request(app) - .get('/') - .expect('Vary', 'Accept') - .expect(200, done); - }) - }) - - describe('when the value is present', function(){ - it('should not add it again', function (done) { - var app = express(); - - app.use(function (req, res) { - res.vary('Accept'); - res.vary('Accept-Encoding'); - res.vary('Accept-Encoding'); - res.vary('Accept-Encoding'); - res.vary('Accept'); - res.end(); - }); - - request(app) - .get('/') - .expect('Vary', 'Accept, Accept-Encoding') - .expect(200, done); - }) - }) -}) diff --git a/test/utils.js b/test/utils.js deleted file mode 100644 index 9a38ede6..00000000 --- a/test/utils.js +++ /dev/null @@ -1,103 +0,0 @@ -'use strict' - -var assert = require('assert'); -var Buffer = require('safe-buffer').Buffer -var utils = require('../lib/utils'); - -describe('utils.etag(body, encoding)', function(){ - it('should support strings', function(){ - assert.strictEqual(utils.etag('express!'), - '"8-O2uVAFaQ1rZvlKLT14RnuvjPIdg"') - }) - - it('should support utf8 strings', function(){ - assert.strictEqual(utils.etag('express❤', 'utf8'), - '"a-JBiXf7GyzxwcrxY4hVXUwa7tmks"') - }) - - it('should support buffer', function(){ - assert.strictEqual(utils.etag(Buffer.from('express!')), - '"8-O2uVAFaQ1rZvlKLT14RnuvjPIdg"') - }) - - it('should support empty string', function(){ - assert.strictEqual(utils.etag(''), - '"0-2jmj7l5rSw0yVb/vlWAYkK/YBwk"') - }) -}) - -describe('utils.setCharset(type, charset)', function () { - it('should do anything without type', function () { - assert.strictEqual(utils.setCharset(), undefined); - }); - - it('should return type if not given charset', function () { - assert.strictEqual(utils.setCharset('text/html'), 'text/html'); - }); - - it('should keep charset if not given charset', function () { - assert.strictEqual(utils.setCharset('text/html; charset=utf-8'), 'text/html; charset=utf-8'); - }); - - it('should set charset', function () { - assert.strictEqual(utils.setCharset('text/html', 'utf-8'), 'text/html; charset=utf-8'); - }); - - it('should override charset', function () { - assert.strictEqual(utils.setCharset('text/html; charset=iso-8859-1', 'utf-8'), 'text/html; charset=utf-8'); - }); -}); - -describe('utils.wetag(body, encoding)', function(){ - it('should support strings', function(){ - assert.strictEqual(utils.wetag('express!'), - 'W/"8-O2uVAFaQ1rZvlKLT14RnuvjPIdg"') - }) - - it('should support utf8 strings', function(){ - assert.strictEqual(utils.wetag('express❤', 'utf8'), - 'W/"a-JBiXf7GyzxwcrxY4hVXUwa7tmks"') - }) - - it('should support buffer', function(){ - assert.strictEqual(utils.wetag(Buffer.from('express!')), - 'W/"8-O2uVAFaQ1rZvlKLT14RnuvjPIdg"') - }) - - it('should support empty string', function(){ - assert.strictEqual(utils.wetag(''), - 'W/"0-2jmj7l5rSw0yVb/vlWAYkK/YBwk"') - }) -}) - -describe('utils.isAbsolute()', function(){ - it('should support windows', function(){ - assert(utils.isAbsolute('c:\\')); - assert(utils.isAbsolute('c:/')); - assert(!utils.isAbsolute(':\\')); - }) - - it('should support windows unc', function(){ - assert(utils.isAbsolute('\\\\foo\\bar')) - }) - - it('should support unices', function(){ - assert(utils.isAbsolute('/foo/bar')); - assert(!utils.isAbsolute('foo/bar')); - }) -}) - -describe('utils.flatten(arr)', function(){ - it('should flatten an array', function(){ - var arr = ['one', ['two', ['three', 'four'], 'five']]; - var flat = utils.flatten(arr) - - assert.strictEqual(flat.length, 5) - assert.strictEqual(flat[0], 'one') - assert.strictEqual(flat[1], 'two') - assert.strictEqual(flat[2], 'three') - assert.strictEqual(flat[3], 'four') - assert.strictEqual(flat[4], 'five') - assert.ok(flat.every(function (v) { return typeof v === 'string' })) - }) -})