@mila.solutions/express 5.2.1-mila.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,694 @@
1
+ /*!
2
+ * express
3
+ * Copyright(c) 2009-2013 TJ Holowaychuk
4
+ * Copyright(c) 2013 Roman Shtylman
5
+ * Copyright(c) 2014-2015 Douglas Christopher Wilson
6
+ * MIT Licensed
7
+ */
8
+
9
+ 'use strict';
10
+
11
+ /**
12
+ * Module dependencies.
13
+ * @private
14
+ */
15
+
16
+ var finalhandler = require('finalhandler');
17
+ var debug = require('debug')('express:application');
18
+ var View = require('./view');
19
+ var http = require('node:http');
20
+ var methods = require('./utils').methods;
21
+ var compileETag = require('./utils').compileETag;
22
+ var compileQueryParser = require('./utils').compileQueryParser;
23
+ var compileTrust = require('./utils').compileTrust;
24
+ var resolve = require('node:path').resolve;
25
+ var once = require('once')
26
+ var Router = require('router');
27
+ var schemaSerializer = require('./schema-serializer');
28
+
29
+ /**
30
+ * Module variables.
31
+ * @private
32
+ */
33
+
34
+ var slice = Array.prototype.slice;
35
+ var flatten = Array.prototype.flat;
36
+
37
+ /**
38
+ * Application prototype.
39
+ */
40
+
41
+ var app = exports = module.exports = {};
42
+
43
+ /**
44
+ * Variable for trust proxy inheritance back-compat
45
+ * @private
46
+ */
47
+
48
+ var trustProxyDefaultSymbol = '@@symbol:trust_proxy_default';
49
+
50
+ /**
51
+ * Initialize the server.
52
+ *
53
+ * - setup default configuration
54
+ * - setup default middleware
55
+ * - setup route reflection methods
56
+ *
57
+ * @private
58
+ */
59
+
60
+ app.init = function init() {
61
+ var router = null;
62
+
63
+ this.cache = Object.create(null);
64
+ this.engines = Object.create(null);
65
+ this.settings = Object.create(null);
66
+ this._jsonSettingsCache = null;
67
+ this._boundLogError = logerror.bind(this);
68
+
69
+ this.defaultConfiguration();
70
+
71
+ // Setup getting to lazily add base router
72
+ Object.defineProperty(this, 'router', {
73
+ configurable: true,
74
+ enumerable: true,
75
+ get: function getrouter() {
76
+ if (router === null) {
77
+ router = new Router({
78
+ caseSensitive: this.enabled('case sensitive routing'),
79
+ strict: this.enabled('strict routing')
80
+ });
81
+ }
82
+
83
+ return router;
84
+ }
85
+ });
86
+ };
87
+
88
+ /**
89
+ * Initialize application configuration.
90
+ * @private
91
+ */
92
+
93
+ app.defaultConfiguration = function defaultConfiguration() {
94
+ var env = process.env.NODE_ENV || 'development';
95
+
96
+ // default settings
97
+ this.enable('x-powered-by');
98
+ this.set('etag', 'weak');
99
+ this.set('env', env);
100
+ this.set('query parser', 'simple')
101
+ this.set('subdomain offset', 2);
102
+ this.set('trust proxy', false);
103
+
104
+ // trust proxy inherit back-compat
105
+ Object.defineProperty(this.settings, trustProxyDefaultSymbol, {
106
+ configurable: true,
107
+ value: true
108
+ });
109
+
110
+ debug('booting in %s mode', env);
111
+
112
+ this.on('mount', function onmount(parent) {
113
+ // inherit trust proxy
114
+ if (this.settings[trustProxyDefaultSymbol] === true
115
+ && typeof parent.settings['trust proxy fn'] === 'function') {
116
+ delete this.settings['trust proxy'];
117
+ delete this.settings['trust proxy fn'];
118
+ }
119
+
120
+ // inherit protos (for app.request/app.response config objects)
121
+ Object.setPrototypeOf(this.request, parent.request)
122
+ Object.setPrototypeOf(this.response, parent.response)
123
+ Object.setPrototypeOf(this.engines, parent.engines)
124
+ Object.setPrototypeOf(this.settings, parent.settings)
125
+ });
126
+
127
+ // setup locals
128
+ this.locals = Object.create(null);
129
+
130
+ // top-most app is mounted at /
131
+ this.mountpath = '/';
132
+
133
+ // default locals
134
+ this.locals.settings = this.settings;
135
+
136
+ // default configuration
137
+ this.set('view', View);
138
+ this.set('views', resolve('views'));
139
+ this.set('jsonp callback name', 'callback');
140
+
141
+ if (env === 'production') {
142
+ this.enable('view cache');
143
+ }
144
+ };
145
+
146
+ /**
147
+ * Dispatch a req, res pair into the application. Starts pipeline processing.
148
+ *
149
+ * If no callback is provided, then default error handlers will respond
150
+ * in the event of an error bubbling through the stack.
151
+ *
152
+ * @private
153
+ */
154
+
155
+ app.handle = function handle(req, res, callback) {
156
+ // final handler
157
+ var done = callback || finalhandler(req, res, {
158
+ env: this.get('env'),
159
+ onerror: this._boundLogError
160
+ });
161
+
162
+ // set powered by header
163
+ if (this.enabled('x-powered-by')) {
164
+ res.setHeader('X-Powered-By', 'Express');
165
+ }
166
+
167
+ // set circular references
168
+ req.res = res;
169
+ res.req = req;
170
+
171
+ // alter prototype of req and res objects to add Express methods
172
+ Object.setPrototypeOf(req, this.request);
173
+ Object.setPrototypeOf(res, this.response);
174
+
175
+ this.router.handle(req, res, done);
176
+ };
177
+
178
+ /**
179
+ * Proxy `Router#use()` to add middleware to the app router.
180
+ * See Router#use() documentation for details.
181
+ *
182
+ * If the _fn_ parameter is an express app, then it will be
183
+ * mounted at the _route_ specified.
184
+ *
185
+ * @public
186
+ */
187
+
188
+ app.use = function use(fn) {
189
+ var offset = 0;
190
+ var path = '/';
191
+
192
+ // default path to '/'
193
+ // disambiguate app.use([fn])
194
+ if (typeof fn !== 'function') {
195
+ var arg = fn;
196
+
197
+ while (Array.isArray(arg) && arg.length !== 0) {
198
+ arg = arg[0];
199
+ }
200
+
201
+ // first arg is the path
202
+ if (typeof arg !== 'function') {
203
+ offset = 1;
204
+ path = fn;
205
+ }
206
+ }
207
+
208
+ var fns = flatten.call(slice.call(arguments, offset), Infinity);
209
+
210
+ if (fns.length === 0) {
211
+ throw new TypeError('app.use() requires a middleware function')
212
+ }
213
+
214
+ // get router
215
+ var router = this.router;
216
+
217
+ fns.forEach(function (fn) {
218
+ // non-express app
219
+ if (!fn || !fn.handle || !fn.set) {
220
+ return router.use(path, fn);
221
+ }
222
+
223
+ debug('.use app under %s', path);
224
+ fn.mountpath = path;
225
+ fn.parent = this;
226
+
227
+ // restore parent app's req/res properties after sub-app
228
+ router.use(path, function mounted_app(req, res, next) {
229
+ var orig = req.app;
230
+ fn.handle(req, res, function (err) {
231
+ Object.setPrototypeOf(req, orig.request);
232
+ Object.setPrototypeOf(res, orig.response);
233
+ next(err);
234
+ });
235
+ });
236
+
237
+ // mounted an app
238
+ fn.emit('mount', this);
239
+ }, this);
240
+
241
+ return this;
242
+ };
243
+
244
+ /**
245
+ * Proxy to the app `Router#route()`
246
+ * Returns a new `Route` instance for the _path_.
247
+ *
248
+ * Routes are isolated middleware stacks for specific paths.
249
+ * See the Route api docs for details.
250
+ *
251
+ * @public
252
+ */
253
+
254
+ app.route = function route(path) {
255
+ return this.router.route(path);
256
+ };
257
+
258
+ /**
259
+ * Register the given template engine callback `fn`
260
+ * as `ext`.
261
+ *
262
+ * By default will `require()` the engine based on the
263
+ * file extension. For example if you try to render
264
+ * a "foo.ejs" file Express will invoke the following internally:
265
+ *
266
+ * app.engine('ejs', require('ejs').__express);
267
+ *
268
+ * For engines that do not provide `.__express` out of the box,
269
+ * or if you wish to "map" a different extension to the template engine
270
+ * you may use this method. For example mapping the EJS template engine to
271
+ * ".html" files:
272
+ *
273
+ * app.engine('html', require('ejs').renderFile);
274
+ *
275
+ * In this case EJS provides a `.renderFile()` method with
276
+ * the same signature that Express expects: `(path, options, callback)`,
277
+ * though note that it aliases this method as `ejs.__express` internally
278
+ * so if you're using ".ejs" extensions you don't need to do anything.
279
+ *
280
+ * Some template engines do not follow this convention, the
281
+ * [Consolidate.js](https://github.com/tj/consolidate.js)
282
+ * library was created to map all of node's popular template
283
+ * engines to follow this convention, thus allowing them to
284
+ * work seamlessly within Express.
285
+ *
286
+ * @param {String} ext
287
+ * @param {Function} fn
288
+ * @return {app} for chaining
289
+ * @public
290
+ */
291
+
292
+ app.engine = function engine(ext, fn) {
293
+ if (typeof fn !== 'function') {
294
+ throw new Error('callback function required');
295
+ }
296
+
297
+ // get file extension
298
+ var extension = ext[0] !== '.'
299
+ ? '.' + ext
300
+ : ext;
301
+
302
+ // store engine
303
+ this.engines[extension] = fn;
304
+
305
+ return this;
306
+ };
307
+
308
+ /**
309
+ * Proxy to `Router#param()` with one added api feature. The _name_ parameter
310
+ * can be an array of names.
311
+ *
312
+ * See the Router#param() docs for more details.
313
+ *
314
+ * @param {String|Array} name
315
+ * @param {Function} fn
316
+ * @return {app} for chaining
317
+ * @public
318
+ */
319
+
320
+ app.param = function param(name, fn) {
321
+ if (Array.isArray(name)) {
322
+ for (var i = 0; i < name.length; i++) {
323
+ this.param(name[i], fn);
324
+ }
325
+
326
+ return this;
327
+ }
328
+
329
+ this.router.param(name, fn);
330
+
331
+ return this;
332
+ };
333
+
334
+ /**
335
+ * Assign `setting` to `val`, or return `setting`'s value.
336
+ *
337
+ * app.set('foo', 'bar');
338
+ * app.set('foo');
339
+ * // => "bar"
340
+ *
341
+ * Mounted servers inherit their parent server's settings.
342
+ *
343
+ * @param {String} setting
344
+ * @param {*} [val]
345
+ * @return {Server} for chaining
346
+ * @public
347
+ */
348
+
349
+ app.set = function set(setting, val) {
350
+ if (arguments.length === 1) {
351
+ // app.get(setting)
352
+ return this.settings[setting];
353
+ }
354
+
355
+ debug('set "%s" to %o', setting, val);
356
+
357
+ // set value
358
+ this.settings[setting] = val;
359
+
360
+ // invalidate JSON settings cache
361
+ this._jsonSettingsCache = null;
362
+
363
+ // trigger matched settings
364
+ switch (setting) {
365
+ case 'etag':
366
+ this.set('etag fn', compileETag(val));
367
+ break;
368
+ case 'query parser':
369
+ this.set('query parser fn', compileQueryParser(val));
370
+ break;
371
+ case 'trust proxy':
372
+ this.set('trust proxy fn', compileTrust(val));
373
+
374
+ // trust proxy inherit back-compat
375
+ Object.defineProperty(this.settings, trustProxyDefaultSymbol, {
376
+ configurable: true,
377
+ value: false
378
+ });
379
+
380
+ break;
381
+ case 'json schema':
382
+ this.set('json serializer', val ? schemaSerializer.compileSerializer(val) : null);
383
+ break;
384
+ }
385
+
386
+ return this;
387
+ };
388
+
389
+ /**
390
+ * Get cached JSON-related settings to avoid per-request app.get() overhead.
391
+ * Cache is invalidated whenever app.set() is called.
392
+ *
393
+ * @return {Object}
394
+ * @private
395
+ */
396
+
397
+ app._getJsonSettings = function _getJsonSettings() {
398
+ if (this._jsonSettingsCache) return this._jsonSettingsCache;
399
+ this._jsonSettingsCache = {
400
+ escape: this.settings['json escape'],
401
+ replacer: this.settings['json replacer'],
402
+ spaces: this.settings['json spaces'],
403
+ etagFn: this.settings['etag fn'],
404
+ jsonSerializer: this.settings['json serializer'],
405
+ jsonEtag: this.settings['json etag']
406
+ };
407
+ return this._jsonSettingsCache;
408
+ };
409
+
410
+ /**
411
+ * Return the app's absolute pathname
412
+ * based on the parent(s) that have
413
+ * mounted it.
414
+ *
415
+ * For example if the application was
416
+ * mounted as "/admin", which itself
417
+ * was mounted as "/blog" then the
418
+ * return value would be "/blog/admin".
419
+ *
420
+ * @return {String}
421
+ * @private
422
+ */
423
+
424
+ app.path = function path() {
425
+ return this.parent
426
+ ? this.parent.path() + this.mountpath
427
+ : '';
428
+ };
429
+
430
+ /**
431
+ * Check if `setting` is enabled (truthy).
432
+ *
433
+ * app.enabled('foo')
434
+ * // => false
435
+ *
436
+ * app.enable('foo')
437
+ * app.enabled('foo')
438
+ * // => true
439
+ *
440
+ * @param {String} setting
441
+ * @return {Boolean}
442
+ * @public
443
+ */
444
+
445
+ app.enabled = function enabled(setting) {
446
+ return Boolean(this.set(setting));
447
+ };
448
+
449
+ /**
450
+ * Check if `setting` is disabled.
451
+ *
452
+ * app.disabled('foo')
453
+ * // => true
454
+ *
455
+ * app.enable('foo')
456
+ * app.disabled('foo')
457
+ * // => false
458
+ *
459
+ * @param {String} setting
460
+ * @return {Boolean}
461
+ * @public
462
+ */
463
+
464
+ app.disabled = function disabled(setting) {
465
+ return !this.set(setting);
466
+ };
467
+
468
+ /**
469
+ * Enable `setting`.
470
+ *
471
+ * @param {String} setting
472
+ * @return {app} for chaining
473
+ * @public
474
+ */
475
+
476
+ app.enable = function enable(setting) {
477
+ return this.set(setting, true);
478
+ };
479
+
480
+ /**
481
+ * Disable `setting`.
482
+ *
483
+ * @param {String} setting
484
+ * @return {app} for chaining
485
+ * @public
486
+ */
487
+
488
+ app.disable = function disable(setting) {
489
+ return this.set(setting, false);
490
+ };
491
+
492
+ /**
493
+ * Delegate `.VERB(...)` calls to `router.VERB(...)`.
494
+ */
495
+
496
+ methods.forEach(function (method) {
497
+ app[method] = function (path) {
498
+ if (method === 'get' && arguments.length === 1) {
499
+ // app.get(setting)
500
+ return this.set(path);
501
+ }
502
+
503
+ var args = slice.call(arguments, 1);
504
+
505
+ // Check if first arg after path is a schema options object
506
+ var schemaOpts = null;
507
+ if (args.length > 1 && typeof args[0] === 'object' && args[0] !== null
508
+ && !Array.isArray(args[0]) && typeof args[0] !== 'function'
509
+ && args[0].schema) {
510
+ schemaOpts = args.shift();
511
+ }
512
+
513
+ var route = this.route(path);
514
+
515
+ // If schema options provided, inject serializer middleware
516
+ if (schemaOpts) {
517
+ var serializers = schemaSerializer.compileRouteSerializers(schemaOpts.schema);
518
+ route[method].call(route, function attachSerializer(req, res, next) {
519
+ res._routeSerializers = serializers;
520
+ next();
521
+ });
522
+ }
523
+
524
+ route[method].apply(route, args);
525
+ return this;
526
+ };
527
+ });
528
+
529
+ /**
530
+ * Special-cased "all" method, applying the given route `path`,
531
+ * middleware, and callback to _every_ HTTP method.
532
+ *
533
+ * @param {String} path
534
+ * @param {Function} ...
535
+ * @return {app} for chaining
536
+ * @public
537
+ */
538
+
539
+ app.all = function all(path) {
540
+ var args = slice.call(arguments, 1);
541
+
542
+ // Check if first arg after path is a schema options object
543
+ var schemaOpts = null;
544
+ if (args.length > 1 && typeof args[0] === 'object' && args[0] !== null
545
+ && !Array.isArray(args[0]) && typeof args[0] !== 'function'
546
+ && args[0].schema) {
547
+ schemaOpts = args.shift();
548
+ }
549
+
550
+ var route = this.route(path);
551
+
552
+ // If schema options provided, inject serializer middleware via 'all'
553
+ if (schemaOpts) {
554
+ var serializers = schemaSerializer.compileRouteSerializers(schemaOpts.schema);
555
+ route.all(function attachSerializer(req, res, next) {
556
+ res._routeSerializers = serializers;
557
+ next();
558
+ });
559
+ }
560
+
561
+ for (var i = 0; i < methods.length; i++) {
562
+ route[methods[i]].apply(route, args);
563
+ }
564
+
565
+ return this;
566
+ };
567
+
568
+ /**
569
+ * Render the given view `name` name with `options`
570
+ * and a callback accepting an error and the
571
+ * rendered template string.
572
+ *
573
+ * Example:
574
+ *
575
+ * app.render('email', { name: 'Tobi' }, function(err, html){
576
+ * // ...
577
+ * })
578
+ *
579
+ * @param {String} name
580
+ * @param {Object|Function} options or fn
581
+ * @param {Function} callback
582
+ * @public
583
+ */
584
+
585
+ app.render = function render(name, options, callback) {
586
+ var cache = this.cache;
587
+ var done = callback;
588
+ var engines = this.engines;
589
+ var opts = options || {};
590
+ var view;
591
+
592
+ // support callback function as second arg
593
+ if (typeof options === 'function') {
594
+ done = options;
595
+ opts = {};
596
+ }
597
+
598
+ // merge options
599
+ var renderOptions = { ...this.locals, ...opts._locals, ...opts };
600
+
601
+ // set .cache unless explicitly provided
602
+ if (renderOptions.cache == null) {
603
+ renderOptions.cache = this.enabled('view cache');
604
+ }
605
+
606
+ // primed cache
607
+ if (renderOptions.cache) {
608
+ view = cache[name];
609
+ }
610
+
611
+ // view
612
+ if (!view) {
613
+ var View = this.get('view');
614
+
615
+ view = new View(name, {
616
+ defaultEngine: this.get('view engine'),
617
+ root: this.get('views'),
618
+ engines: engines
619
+ });
620
+
621
+ if (!view.path) {
622
+ var dirs = Array.isArray(view.root) && view.root.length > 1
623
+ ? 'directories "' + view.root.slice(0, -1).join('", "') + '" or "' + view.root[view.root.length - 1] + '"'
624
+ : 'directory "' + view.root + '"'
625
+ var err = new Error('Failed to lookup view "' + name + '" in views ' + dirs);
626
+ err.view = view;
627
+ return done(err);
628
+ }
629
+
630
+ // prime the cache
631
+ if (renderOptions.cache) {
632
+ cache[name] = view;
633
+ }
634
+ }
635
+
636
+ // render
637
+ tryRender(view, renderOptions, done);
638
+ };
639
+
640
+ /**
641
+ * Listen for connections.
642
+ *
643
+ * A node `http.Server` is returned, with this
644
+ * application (which is a `Function`) as its
645
+ * callback. If you wish to create both an HTTP
646
+ * and HTTPS server you may do so with the "http"
647
+ * and "https" modules as shown here:
648
+ *
649
+ * var http = require('node:http')
650
+ * , https = require('node:https')
651
+ * , express = require('express')
652
+ * , app = express();
653
+ *
654
+ * http.createServer(app).listen(80);
655
+ * https.createServer({ ... }, app).listen(443);
656
+ *
657
+ * @return {http.Server}
658
+ * @public
659
+ */
660
+
661
+ app.listen = function listen() {
662
+ var server = http.createServer(this)
663
+ var args = slice.call(arguments)
664
+ if (typeof args[args.length - 1] === 'function') {
665
+ var done = args[args.length - 1] = once(args[args.length - 1])
666
+ server.once('error', done)
667
+ }
668
+ return server.listen.apply(server, args)
669
+ }
670
+
671
+ /**
672
+ * Log error using console.error.
673
+ *
674
+ * @param {Error} err
675
+ * @private
676
+ */
677
+
678
+ function logerror(err) {
679
+ /* istanbul ignore next */
680
+ if (this.get('env') !== 'test') console.error(err.stack || err.toString());
681
+ }
682
+
683
+ /**
684
+ * Try rendering a view.
685
+ * @private
686
+ */
687
+
688
+ function tryRender(view, options, callback) {
689
+ try {
690
+ view.render(options, callback);
691
+ } catch (err) {
692
+ callback(err);
693
+ }
694
+ }