ultimate-express 1.3.0 → 1.3.1

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.
package/README.md CHANGED
@@ -289,6 +289,7 @@ Almost all middlewares that are compatible with Express are compatible with µEx
289
289
  - ✅ [body-parser](https://npmjs.com/package/body-parser) (use `express.text()` etc instead for better performance)
290
290
  - ✅ [cookie-parser](https://npmjs.com/package/cookie-parser)
291
291
  - ✅ [cookie-session](https://npmjs.com/package/cookie-session)
292
+ - ✅ [compression](https://npmjs.com/package/compression)
292
293
  - ✅ [serve-static](https://npmjs.com/package/serve-static) (use `express.static()` instead for better performance)
293
294
  - ✅ [serve-index](https://npmjs.com/package/serve-index)
294
295
  - ✅ [cors](https://npmjs.com/package/cors)
@@ -304,7 +305,6 @@ Almost all middlewares that are compatible with Express are compatible with µEx
304
305
 
305
306
  Middlewares and modules that are confirmed to not work:
306
307
 
307
- - ❌ [compression](https://npmjs.com/package/compression) - doesn't error, but doesn't compress
308
308
  - ❌ [express-async-errors](https://npmjs.com/package/express-async-errors) - doesn't work, use `app.set('catch async errors', true)` instead.
309
309
 
310
310
  ## Tested view engines
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ultimate-express",
3
- "version": "1.3.0",
3
+ "version": "1.3.1",
4
4
  "description": "The Ultimate Express. Fastest http server with full Express compatibility, based on uWebSockets.",
5
5
  "main": "src/index.js",
6
6
  "scripts": {
@@ -42,7 +42,7 @@
42
42
  "@types/express": "^4.0.0",
43
43
  "accepts": "^1.3.8",
44
44
  "bytes": "^3.1.2",
45
- "cookie": "^0.6.0",
45
+ "cookie": "^1.0.1",
46
46
  "cookie-signature": "^1.2.1",
47
47
  "encodeurl": "^2.0.0",
48
48
  "etag": "^1.8.1",
@@ -62,6 +62,7 @@
62
62
  },
63
63
  "devDependencies": {
64
64
  "body-parser": "^1.20.3",
65
+ "compression": "^1.7.4",
65
66
  "cookie-parser": "^1.4.6",
66
67
  "cookie-session": "^2.1.0",
67
68
  "cors": "^2.8.5",
@@ -81,6 +82,7 @@
81
82
  "multer": "^1.4.5-lts.1",
82
83
  "mustache-express": "^1.3.2",
83
84
  "pako": "^2.1.0",
85
+ "pkg-pr-new": "^0.0.29",
84
86
  "pug": "^3.0.3",
85
87
  "response-time": "^2.3.2",
86
88
  "serve-index": "^1.9.1",
@@ -16,7 +16,7 @@ limitations under the License.
16
16
 
17
17
  const uWS = require("uWebSockets.js");
18
18
  const Router = require("./router.js");
19
- const { removeDuplicateSlashes, defaultSettings, compileTrust, createETagGenerator, fastQueryParse } = require("./utils.js");
19
+ const { removeDuplicateSlashes, defaultSettings, compileTrust, createETagGenerator, fastQueryParse, NullObject } = require("./utils.js");
20
20
  const querystring = require("fast-querystring");
21
21
  const ViewClass = require("./view.js");
22
22
  const path = require("path");
@@ -27,7 +27,7 @@ const cpuCount = os.cpus().length;
27
27
 
28
28
  let workers = [];
29
29
  let taskKey = 0;
30
- const workerTasks = {};
30
+ const workerTasks = new NullObject();
31
31
 
32
32
  function createWorker() {
33
33
  const worker = new Worker(path.join(__dirname, 'worker.js'));
@@ -47,7 +47,7 @@ function createWorker() {
47
47
  }
48
48
 
49
49
  class Application extends Router {
50
- constructor(settings = {}) {
50
+ constructor(settings = new NullObject()) {
51
51
  super(settings);
52
52
  if(!settings?.uwsOptions) {
53
53
  settings.uwsOptions = {};
@@ -62,8 +62,8 @@ class Application extends Router {
62
62
  this.uwsApp = uWS.App(settings.uwsOptions);
63
63
  this.ssl = false;
64
64
  }
65
- this.cache = {};
66
- this.engines = {};
65
+ this.cache = new NullObject();
66
+ this.engines = new NullObject();
67
67
  this.locals = {
68
68
  settings: this.settings
69
69
  };
@@ -206,7 +206,7 @@ class Application extends Router {
206
206
  throw err;
207
207
  }
208
208
  this.port = uWS.us_socket_local_port(socket);
209
- callback(this.port);
209
+ if(callback) callback(this.port);
210
210
  };
211
211
  let fn = 'listen';
212
212
  let args = [];
@@ -229,6 +229,7 @@ class Application extends Router {
229
229
  }
230
230
  this.listenCalled = true;
231
231
  this.uwsApp[fn](...args);
232
+ return this.uwsApp;
232
233
  }
233
234
 
234
235
  address() {
@@ -260,10 +261,10 @@ class Application extends Router {
260
261
  render(name, options, callback) {
261
262
  if(typeof options === 'function') {
262
263
  callback = options;
263
- options = {};
264
+ options = new NullObject();
264
265
  }
265
266
  if(!options) {
266
- options = {};
267
+ options = new NullObject();
267
268
  } else {
268
269
  options = Object.assign({}, options);
269
270
  }
@@ -318,4 +319,4 @@ class Application extends Router {
318
319
 
319
320
  module.exports = function(options) {
320
321
  return new Application(options);
321
- }
322
+ }
@@ -20,10 +20,10 @@ const bytes = require('bytes');
20
20
  const zlib = require('fast-zlib');
21
21
  const typeis = require('type-is');
22
22
  const querystring = require('fast-querystring');
23
- const { fastQueryParse } = require('./utils.js');
23
+ const { fastQueryParse, NullObject } = require('./utils.js');
24
24
 
25
25
  function static(root, options) {
26
- if(!options) options = {};
26
+ if(!options) options = new NullObject();
27
27
  if(typeof options.index === 'undefined') options.index = 'index.html';
28
28
  if(typeof options.redirect === 'undefined') options.redirect = true;
29
29
  if(typeof options.fallthrough === 'undefined') options.fallthrough = true;
@@ -132,7 +132,7 @@ function createInflate(contentEncoding) {
132
132
  function createBodyParser(defaultType, beforeReturn) {
133
133
  return function(options) {
134
134
  if(typeof options !== 'object') {
135
- options = {};
135
+ options = new NullObject();
136
136
  }
137
137
  if(typeof options.limit === 'undefined') options.limit = bytes('100kb');
138
138
  else options.limit = bytes(options.limit);
@@ -160,7 +160,7 @@ function createBodyParser(defaultType, beforeReturn) {
160
160
  return next();
161
161
  }
162
162
 
163
- req.body = {};
163
+ req.body = new NullObject();
164
164
 
165
165
  // skip reading body for non-json content type
166
166
  if(!type) {
package/src/request.js CHANGED
@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
14
14
  limitations under the License.
15
15
  */
16
16
 
17
- const { patternToRegex, deprecated } = require("./utils.js");
17
+ const { patternToRegex, deprecated, NullObject } = require("./utils.js");
18
18
  const accepts = require("accepts");
19
19
  const typeis = require("type-is");
20
20
  const parseRange = require("range-parser");
@@ -64,7 +64,7 @@ module.exports = class Request extends Readable {
64
64
  this._opPath = this._opPath.slice(0, -1);
65
65
  }
66
66
  this.method = req.getCaseSensitiveMethod().toUpperCase();
67
- this.params = {};
67
+ this.params = new NullObject();
68
68
 
69
69
  this._gotParams = new Set();
70
70
  this._stack = [];
@@ -206,7 +206,7 @@ module.exports = class Request extends Readable {
206
206
  if(qp) {
207
207
  this.#cachedQuery = qp(this.urlQuery.slice(1));
208
208
  } else {
209
- this.#cachedQuery = {};
209
+ this.#cachedQuery = new NullObject();
210
210
  }
211
211
  return this.#cachedQuery;
212
212
  }
@@ -351,7 +351,7 @@ module.exports = class Request extends Readable {
351
351
  if(this.#cachedHeaders) {
352
352
  return this.#cachedHeaders;
353
353
  }
354
- let headers = {};
354
+ let headers = new NullObject();
355
355
  this.#rawHeadersEntries.forEach((val) => {
356
356
  const key = val[0].toLowerCase();
357
357
  const value = val[1];
@@ -382,7 +382,7 @@ module.exports = class Request extends Readable {
382
382
  if(this.#cachedDistinctHeaders) {
383
383
  return this.#cachedDistinctHeaders;
384
384
  }
385
- let headers = {};
385
+ let headers = new NullObject();
386
386
  this.#rawHeadersEntries.forEach((val) => {
387
387
  if(!headers[val[0]]) {
388
388
  headers[val[0]] = [];
package/src/response.js CHANGED
@@ -20,7 +20,7 @@ const vary = require("vary");
20
20
  const encodeUrl = require("encodeurl");
21
21
  const {
22
22
  normalizeType, stringify, deprecated, UP_PATH_REGEXP, decode,
23
- containsDotFile, isPreconditionFailure, isRangeFresh, parseHttpDate
23
+ containsDotFile, isPreconditionFailure, isRangeFresh, NullObject
24
24
  } = require("./utils.js");
25
25
  const { Writable } = require("stream");
26
26
  const { isAbsolute } = require("path");
@@ -70,7 +70,7 @@ module.exports = class Response extends Writable {
70
70
  this._res = res;
71
71
  this.headersSent = false;
72
72
  this.app = app;
73
- this.locals = {};
73
+ this.locals = new NullObject();
74
74
  this.finished = false;
75
75
  this.aborted = false;
76
76
  this.statusCode = 200;
@@ -208,12 +208,9 @@ module.exports = class Response extends Writable {
208
208
  _implicitHeader() {
209
209
  // compatibility function
210
210
  // usually should send headers but this is useless for us
211
- return;
211
+ this.writeHead(this.statusCode);
212
212
  }
213
213
  status(code) {
214
- if(this.headersSent) {
215
- throw new Error('Can\'t set status: Response was already sent');
216
- }
217
214
  this.statusCode = parseInt(code);
218
215
  return this;
219
216
  }
@@ -282,21 +279,23 @@ module.exports = class Response extends Writable {
282
279
  }
283
280
  if(typeof body === 'string') {
284
281
  const contentType = this.headers['content-type'];
285
- if(contentType && !contentType.includes(';')) {
282
+ if(!contentType){
283
+ this.type('html'); // string defaulting to html
284
+ } else if(!contentType.includes(';')) {
286
285
  this.headers['content-type'] += '; charset=utf-8';
287
286
  }
288
287
  }
289
288
  return this.end(body);
290
289
  }
291
- sendFile(path, options = {}, callback) {
290
+ sendFile(path, options = new NullObject(), callback) {
292
291
  if(typeof path !== 'string') {
293
292
  throw new TypeError('path argument is required to res.sendFile');
294
293
  }
295
294
  if(typeof options === 'function') {
296
295
  callback = options;
297
- options = {};
296
+ options = new NullObject();
298
297
  }
299
- if(!options) options = {};
298
+ if(!options) options = new NullObject();
300
299
  let done = callback;
301
300
  if(!done) done = this.req.next;
302
301
  // default options
@@ -488,16 +487,16 @@ module.exports = class Response extends Writable {
488
487
  download(path, filename, options, callback) {
489
488
  let done = callback;
490
489
  let name = filename;
491
- let opts = options || {};
490
+ let opts = options || new NullObject();
492
491
 
493
492
  // support function as second or third arg
494
493
  if (typeof filename === 'function') {
495
494
  done = filename;
496
495
  name = null;
497
- opts = {};
496
+ opts = new NullObject();
498
497
  } else if (typeof options === 'function') {
499
498
  done = options;
500
- opts = {};
499
+ opts = new NullObject();
501
500
  }
502
501
 
503
502
  // support optional filename, where options may be in it's place
@@ -518,7 +517,7 @@ module.exports = class Response extends Writable {
518
517
  }
519
518
  set(field, value) {
520
519
  if(this.headersSent) {
521
- throw new Error('Can\'t write headers: Response was already sent');
520
+ throw new Error('Cannot set headers after they are sent to the client');
522
521
  }
523
522
  if(typeof field === 'object') {
524
523
  for(const header in field) {
@@ -577,10 +576,10 @@ module.exports = class Response extends Writable {
577
576
  render(view, options, callback) {
578
577
  if(typeof options === 'function') {
579
578
  callback = options;
580
- options = {};
579
+ options = new NullObject();
581
580
  }
582
581
  if(!options) {
583
- options = {};
582
+ options = new NullObject();
584
583
  } else {
585
584
  options = Object.assign({}, options);
586
585
  }
@@ -594,7 +593,7 @@ module.exports = class Response extends Writable {
594
593
  }
595
594
  cookie(name, value, options) {
596
595
  if(!options) {
597
- options = {};
596
+ options = new NullObject();
598
597
  }
599
598
  let val = typeof value === 'object' ? "j:"+JSON.stringify(value) : String(value);
600
599
  if(options.maxAge != null) {
@@ -707,9 +706,7 @@ module.exports = class Response extends Writable {
707
706
  let ct = type.indexOf('/') === -1
708
707
  ? (mime.contentType(type) || 'application/octet-stream')
709
708
  : type;
710
- if(ct.startsWith('text/') || ct === 'application/json' || ct === 'application/javascript') {
711
- ct += '; charset=UTF-8';
712
- }
709
+
713
710
  return this.set('content-type', ct);
714
711
  }
715
712
  contentType(type) {
package/src/router.js CHANGED
@@ -18,6 +18,7 @@ const { patternToRegex, needsConversionToRegex, deprecated, findIndexStartingFro
18
18
  const Response = require("./response.js");
19
19
  const Request = require("./request.js");
20
20
  const { EventEmitter } = require("tseep");
21
+ const { NullObject } = require("./utils.js");
21
22
 
22
23
  let routeKey = 0;
23
24
 
@@ -268,7 +269,7 @@ module.exports = class Router extends EventEmitter {
268
269
  const fn = async (res, req) => {
269
270
  const { request, response } = this.handleRequest(res, req);
270
271
  if(route.optimizedParams) {
271
- request.optimizedParams = {};
272
+ request.optimizedParams = new NullObject();
272
273
  for(let i = 0; i < route.optimizedParams.length; i++) {
273
274
  request.optimizedParams[route.optimizedParams[i]] = req.getParameter(i);
274
275
  }
@@ -319,7 +320,7 @@ module.exports = class Router extends EventEmitter {
319
320
 
320
321
  _extractParams(pattern, path) {
321
322
  let match = pattern.exec(path);
322
- const obj = match?.groups ?? {};
323
+ const obj = match?.groups ?? new NullObject();
323
324
  for(let i = 1; i < match.length; i++) {
324
325
  obj[i - 1] = match[i];
325
326
  }
@@ -342,7 +343,7 @@ module.exports = class Router extends EventEmitter {
342
343
  }
343
344
  }
344
345
  } else {
345
- req.params = {};
346
+ req.params = new NullObject();
346
347
  if(req._paramStack.length > 0) {
347
348
  for(let params of req._paramStack) {
348
349
  req.params = {...params, ...req.params};
@@ -533,7 +534,7 @@ module.exports = class Router extends EventEmitter {
533
534
  }
534
535
 
535
536
  route(path) {
536
- let fns = {};
537
+ let fns = new NullObject();
537
538
  for(let method of methods) {
538
539
  fns[method] = (...callbacks) => {
539
540
  return this.createRoute(method.toUpperCase(), path, fns, ...callbacks);
package/src/types.d.ts CHANGED
@@ -1,4 +1,49 @@
1
- declare module 'ultimate-express' {
2
- import express from '@types/express';
3
- export = express;
4
- }
1
+ declare module "ultimate-express" {
2
+ import e from "@types/express";
3
+ import { AppOptions } from "uWebSockets.js";
4
+
5
+ type Settings = {
6
+ uwsOptions?: AppOptions;
7
+ threads?: number;
8
+ };
9
+
10
+ namespace express {
11
+ export import json = e.json;
12
+ export import raw = e.raw;
13
+ export import text = e.text;
14
+
15
+ // export import application = e.application;
16
+ export import request = e.request;
17
+ export import response = e.response;
18
+
19
+ export import static = e.static;
20
+ // export import query = e.query;
21
+
22
+ export import urlencoded = e.urlencoded;
23
+
24
+ export import RouterOptions = e.RouterOptions;
25
+ export import Application = e.Application;
26
+ export import CookieOptions = e.CookieOptions;
27
+ export import Errback = e.Errback;
28
+ export import ErrorRequestHandler = e.ErrorRequestHandler;
29
+ export import Express = e.Express;
30
+ export import Handler = e.Handler;
31
+ export import IRoute = e.IRoute;
32
+ export import IRouter = e.IRouter;
33
+ export import IRouterHandler = e.IRouterHandler;
34
+ export import IRouterMatcher = e.IRouterMatcher;
35
+ export import MediaType = e.MediaType;
36
+ export import NextFunction = e.NextFunction;
37
+ export import Locals = e.Locals;
38
+ export import Request = e.Request;
39
+ export import RequestHandler = e.RequestHandler;
40
+ export import RequestParamHandler = e.RequestParamHandler;
41
+ export import Response = e.Response;
42
+ export import Router = e.Router;
43
+ export import Send = e.Send;
44
+ }
45
+
46
+ function express(settings?: Settings): e.Express;
47
+
48
+ export = express;
49
+ }
package/src/utils.js CHANGED
@@ -315,6 +315,10 @@ function isRangeFresh(req, res) {
315
315
  return parseHttpDate(lastModified) <= parseHttpDate(ifRange);
316
316
  }
317
317
 
318
+ // fast null object
319
+ const NullObject = function() {};
320
+ NullObject.prototype = Object.create(null);
321
+
318
322
  module.exports = {
319
323
  removeDuplicateSlashes,
320
324
  patternToRegex,
@@ -326,6 +330,7 @@ module.exports = {
326
330
  compileTrust,
327
331
  deprecated,
328
332
  UP_PATH_REGEXP,
333
+ NullObject,
329
334
  decode,
330
335
  containsDotFile,
331
336
  parseTokenList,
package/src/view.js CHANGED
@@ -16,11 +16,12 @@ limitations under the License.
16
16
 
17
17
  const path = require("path");
18
18
  const fs = require("fs");
19
+ const { NullObject } = require("./utils.js");
19
20
 
20
21
  module.exports = class View {
21
22
  constructor(name, options) {
22
23
  this.name = name;
23
- this.options = options ? Object.assign({}, options) : {};
24
+ this.options = options ? Object.assign({}, options) : new NullObject();
24
25
  this.defaultEngine = options.defaultEngine;
25
26
  this.ext = path.extname(name);
26
27
  this.root = options.root;