ultimate-express 1.2.29 → 1.3.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.
- package/README.md +5 -3
- package/package.json +4 -1
- package/src/application.js +2 -2
- package/src/response.js +12 -10
- package/src/router.js +7 -1
- package/src/types.d.ts +4 -0
package/README.md
CHANGED
|
@@ -16,7 +16,7 @@ To make sure µExpress matches behavior of Express in all cases, we run all test
|
|
|
16
16
|
|
|
17
17
|
Similar projects based on uWebSockets:
|
|
18
18
|
|
|
19
|
-
- `express` on Bun - since Bun uses uWS for its HTTP module, Express is about 2-3 times faster than on Node.js, but still slower than µExpress because it doesn't do uWS-specific optimizations.
|
|
19
|
+
- `express` on Bun - since Bun uses uWS for its HTTP module, Express is about 2-3 times faster than on Node.js, but still almost 2 times slower than µExpress because it doesn't do uWS-specific optimizations.
|
|
20
20
|
- `hyper-express` - while having a similar API to Express, it's very far from being a drop-in replacement, and implements most of the functionality differently. This creates a lot of random quirks and issues, making the switch quite difficult. Built in middlewares are also very different, middlewares for Express are mostly not supported.
|
|
21
21
|
- `uwebsockets-express` - this library is closer to being a drop-in replacement, but misses a lot of APIs, depends on Express by calling it's methods under the hood and doesn't try to optimize routing by using native uWS router.
|
|
22
22
|
|
|
@@ -61,6 +61,7 @@ Also tested on a [real-world application](https://nekoweb.org) with templates, s
|
|
|
61
61
|
In a lot of cases, you can just replace `require("express")` with `require("ultimate-express")` and everything works the same. But there are some differences:
|
|
62
62
|
|
|
63
63
|
- `case sensitive routing` is enabled by default.
|
|
64
|
+
- a new option `catch async errors` is added. If it's enabled, you don't need to use `express-async-errors` module.
|
|
64
65
|
- request body is only read for POST, PUT and PATCH requests by default. You can add additional methods by setting `body methods` to array with uppercased methods.
|
|
65
66
|
- For HTTPS, instead of doing this:
|
|
66
67
|
```js
|
|
@@ -301,9 +302,10 @@ Almost all middlewares that are compatible with Express are compatible with µEx
|
|
|
301
302
|
- ✅ [express-subdomain](https://npmjs.com/package/express-subdomain)
|
|
302
303
|
- ✅ [vhost](https://npmjs.com/package/vhost)
|
|
303
304
|
|
|
304
|
-
Middlewares that are confirmed to not work:
|
|
305
|
+
Middlewares and modules that are confirmed to not work:
|
|
305
306
|
|
|
306
|
-
- ❌ [compression](https://npmjs.com/package/compression)
|
|
307
|
+
- ❌ [compression](https://npmjs.com/package/compression) - doesn't error, but doesn't compress
|
|
308
|
+
- ❌ [express-async-errors](https://npmjs.com/package/express-async-errors) - doesn't work, use `app.set('catch async errors', true)` instead.
|
|
307
309
|
|
|
308
310
|
## Tested view engines
|
|
309
311
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ultimate-express",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.0",
|
|
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": {
|
|
@@ -31,6 +31,7 @@
|
|
|
31
31
|
"uwebsockets",
|
|
32
32
|
"uws"
|
|
33
33
|
],
|
|
34
|
+
"types": "src/types.d.ts",
|
|
34
35
|
"author": "dimden.dev",
|
|
35
36
|
"license": "Apache-2.0",
|
|
36
37
|
"bugs": {
|
|
@@ -38,6 +39,7 @@
|
|
|
38
39
|
},
|
|
39
40
|
"homepage": "https://github.com/dimdenGD/ultimate-express#readme",
|
|
40
41
|
"dependencies": {
|
|
42
|
+
"@types/express": "^4.0.0",
|
|
41
43
|
"accepts": "^1.3.8",
|
|
42
44
|
"bytes": "^3.1.2",
|
|
43
45
|
"cookie": "^0.6.0",
|
|
@@ -68,6 +70,7 @@
|
|
|
68
70
|
"exit-hook": "^2.2.1",
|
|
69
71
|
"express": "^4.19.2",
|
|
70
72
|
"express-art-template": "^1.0.1",
|
|
73
|
+
"express-async-errors": "^3.1.1",
|
|
71
74
|
"express-dot-engine": "^1.0.8",
|
|
72
75
|
"express-fileupload": "^1.5.1",
|
|
73
76
|
"express-handlebars": "^8.0.1",
|
package/src/application.js
CHANGED
|
@@ -159,12 +159,12 @@ class Application extends Router {
|
|
|
159
159
|
}
|
|
160
160
|
|
|
161
161
|
enable(key) {
|
|
162
|
-
this.
|
|
162
|
+
this.set(key, true);
|
|
163
163
|
return this;
|
|
164
164
|
}
|
|
165
165
|
|
|
166
166
|
disable(key) {
|
|
167
|
-
this.
|
|
167
|
+
this.set(key, false);
|
|
168
168
|
return this;
|
|
169
169
|
}
|
|
170
170
|
|
package/src/response.js
CHANGED
|
@@ -69,13 +69,12 @@ module.exports = class Response extends Writable {
|
|
|
69
69
|
this._req = req;
|
|
70
70
|
this._res = res;
|
|
71
71
|
this.headersSent = false;
|
|
72
|
-
this.aborted = false;
|
|
73
72
|
this.app = app;
|
|
74
73
|
this.locals = {};
|
|
74
|
+
this.finished = false;
|
|
75
75
|
this.aborted = false;
|
|
76
76
|
this.statusCode = 200;
|
|
77
77
|
this.chunkedTransfer = true;
|
|
78
|
-
this.finished = false;
|
|
79
78
|
this.totalSize = 0;
|
|
80
79
|
this.headers = {
|
|
81
80
|
'connection': 'keep-alive',
|
|
@@ -96,7 +95,7 @@ module.exports = class Response extends Writable {
|
|
|
96
95
|
});
|
|
97
96
|
this.body = undefined;
|
|
98
97
|
this.on('error', (err) => {
|
|
99
|
-
if(this.
|
|
98
|
+
if(this.finished) {
|
|
100
99
|
return;
|
|
101
100
|
}
|
|
102
101
|
this._res.cork(() => {
|
|
@@ -121,6 +120,10 @@ module.exports = class Response extends Writable {
|
|
|
121
120
|
err.code = 'ECONNABORTED';
|
|
122
121
|
return this.destroy(err);
|
|
123
122
|
}
|
|
123
|
+
if(this.finished) {
|
|
124
|
+
const err = new Error('Response already finished');
|
|
125
|
+
return this.destroy(err);
|
|
126
|
+
}
|
|
124
127
|
this._res.cork(() => {
|
|
125
128
|
if(!this.headersSent) {
|
|
126
129
|
this.writeHead(this.statusCode);
|
|
@@ -149,7 +152,7 @@ module.exports = class Response extends Writable {
|
|
|
149
152
|
if(!ok) {
|
|
150
153
|
// wait until uWS is ready to accept more data
|
|
151
154
|
this._res.onWritable((offset) => {
|
|
152
|
-
if(this.
|
|
155
|
+
if(this.finished) {
|
|
153
156
|
return true;
|
|
154
157
|
}
|
|
155
158
|
const [ok, done] = this._res.tryEnd(chunk.slice(offset - lastOffset), this.totalSize);
|
|
@@ -221,7 +224,7 @@ module.exports = class Response extends Writable {
|
|
|
221
224
|
if(this.finished) {
|
|
222
225
|
return;
|
|
223
226
|
}
|
|
224
|
-
|
|
227
|
+
this.writeHead(this.statusCode);
|
|
225
228
|
this._res.cork(() => {
|
|
226
229
|
if(!this.headersSent) {
|
|
227
230
|
const etagFn = this.app.get('etag fn');
|
|
@@ -283,7 +286,6 @@ module.exports = class Response extends Writable {
|
|
|
283
286
|
this.headers['content-type'] += '; charset=utf-8';
|
|
284
287
|
}
|
|
285
288
|
}
|
|
286
|
-
this.writeHead(this.statusCode);
|
|
287
289
|
return this.end(body);
|
|
288
290
|
}
|
|
289
291
|
sendFile(path, options = {}, callback) {
|
|
@@ -462,7 +464,7 @@ module.exports = class Response extends Writable {
|
|
|
462
464
|
// serve smaller files using workers
|
|
463
465
|
if(this.app.workers.length && stat.size < 768 * 1024 && !ranged) {
|
|
464
466
|
this.app.readFileWithWorker(fullpath).then((data) => {
|
|
465
|
-
if(this._res.
|
|
467
|
+
if(this._res.finished) {
|
|
466
468
|
return;
|
|
467
469
|
}
|
|
468
470
|
this.end(data);
|
|
@@ -730,7 +732,7 @@ module.exports = class Response extends Writable {
|
|
|
730
732
|
|
|
731
733
|
function pipeStreamOverResponse(res, readStream, totalSize, callback) {
|
|
732
734
|
readStream.on('data', (chunk) => {
|
|
733
|
-
if(res.
|
|
735
|
+
if(res.finished) {
|
|
734
736
|
return readStream.destroy();
|
|
735
737
|
}
|
|
736
738
|
res._res.cork(() => {
|
|
@@ -756,7 +758,7 @@ function pipeStreamOverResponse(res, readStream, totalSize, callback) {
|
|
|
756
758
|
res._res.abOffset = lastOffset;
|
|
757
759
|
|
|
758
760
|
res._res.onWritable((offset) => {
|
|
759
|
-
if(res.
|
|
761
|
+
if(res.finished) {
|
|
760
762
|
return true;
|
|
761
763
|
}
|
|
762
764
|
const [ok, done] = res._res.tryEnd(res._res.ab.slice(offset - res._res.abOffset), totalSize);
|
|
@@ -781,4 +783,4 @@ function pipeStreamOverResponse(res, readStream, totalSize, callback) {
|
|
|
781
783
|
if(res.socketExists) res.socket.emit('error', e);
|
|
782
784
|
}
|
|
783
785
|
});
|
|
784
|
-
}
|
|
786
|
+
}
|
package/src/router.js
CHANGED
|
@@ -248,6 +248,7 @@ module.exports = class Router extends EventEmitter {
|
|
|
248
248
|
const err = new Error('Connection closed');
|
|
249
249
|
err.code = 'ECONNRESET';
|
|
250
250
|
response.aborted = true;
|
|
251
|
+
response.finished = true;
|
|
251
252
|
response.socket.emit('error', err);
|
|
252
253
|
});
|
|
253
254
|
|
|
@@ -484,7 +485,12 @@ module.exports = class Router extends EventEmitter {
|
|
|
484
485
|
const out = callback(req, res, next);
|
|
485
486
|
if(out instanceof Promise) {
|
|
486
487
|
out.catch(err => {
|
|
487
|
-
|
|
488
|
+
if(this.get("catch async errors")) {
|
|
489
|
+
this._handleError(err, req, res);
|
|
490
|
+
return resolve(true);
|
|
491
|
+
} else {
|
|
492
|
+
throw err;
|
|
493
|
+
}
|
|
488
494
|
});
|
|
489
495
|
}
|
|
490
496
|
} catch(err) {
|
package/src/types.d.ts
ADDED