ultimate-express 1.4.7 → 1.4.9

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
@@ -101,7 +101,7 @@ app.listen(3000, () => {
101
101
  ```
102
102
 
103
103
  - This also applies to non-SSL HTTP too. Do not create http server manually, use `app.listen()` instead.
104
- - NodeJS max header size is 16384 byte, uWebSockets 4096 byte, if you need long headers set the env variable `UWS_HTTP_MAX_HEADERS_SIZE`
104
+ - Node.JS max header size is 16384 bytes, while uWebSockets by default is 4096 bytes, so if you need longer headers set the env variable `UWS_HTTP_MAX_HEADERS_SIZE` to max byte count you need.
105
105
 
106
106
  ## Performance tips
107
107
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ultimate-express",
3
- "version": "1.4.7",
3
+ "version": "1.4.9",
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": {
@@ -67,6 +67,7 @@
67
67
  },
68
68
  "devDependencies": {
69
69
  "@codechecks/client": "^0.1.12",
70
+ "better-sse": "^0.14.1",
70
71
  "body-parser": "^1.20.3",
71
72
  "compression": "^1.8.0",
72
73
  "cookie-parser": "^1.4.6",
@@ -74,6 +75,7 @@
74
75
  "cors": "^2.8.5",
75
76
  "ejs": "^3.1.10",
76
77
  "errorhandler": "^1.5.1",
78
+ "eventsource": "^3.0.6",
77
79
  "exit-hook": "^2.2.1",
78
80
  "express": "latest-4",
79
81
  "express-art-template": "^1.0.1",
@@ -86,6 +88,7 @@
86
88
  "express-session": "^1.18.0",
87
89
  "express-subdomain": "^1.0.6",
88
90
  "formdata-node": "^6.0.3",
91
+ "graphql-http": "^1.22.4",
89
92
  "helmet": "^8.1.0",
90
93
  "method-override": "^3.0.0",
91
94
  "morgan": "^1.10.0",
@@ -201,8 +201,12 @@ module.exports = function compileDeclarative(cb, app) {
201
201
  }
202
202
 
203
203
  // get body
204
+ let sendUsed = false;
204
205
  for(let call of callExprs) {
205
206
  if(call.obj.propertyName === 'send' || call.obj.propertyName === 'end') {
207
+ if(sendUsed) {
208
+ return false;
209
+ }
206
210
  const arg = call.arguments[0];
207
211
  if(arg) {
208
212
  if(arg.type === 'Literal') {
@@ -315,6 +319,7 @@ module.exports = function compileDeclarative(cb, app) {
315
319
  return false;
316
320
  }
317
321
  }
322
+ sendUsed = true;
318
323
  }
319
324
  }
320
325
 
package/src/response.js CHANGED
@@ -71,6 +71,7 @@ module.exports = class Response extends Writable {
71
71
  #socket = null;
72
72
  #pendingChunks = [];
73
73
  #lastWriteChunkTime = 0;
74
+ #writeTimeout = null;
74
75
  constructor(res, req, app) {
75
76
  super();
76
77
  this._req = req;
@@ -153,13 +154,29 @@ module.exports = class Response extends Writable {
153
154
  this.#pendingChunks.push(chunk);
154
155
  const size = this.#pendingChunks.reduce((acc, chunk) => acc + chunk.byteLength, 0);
155
156
  const now = Date.now();
156
- // the first chunk is set immediately (!this.#lastWriteChunkTime)
157
+ // the first chunk is sent immediately (!this.#lastWriteChunkTime)
157
158
  // the other chunks are sent when watermark is reached (size >= HIGH_WATERMARK)
158
- // or if elapsed 100ms of last send (now - this.#lastWriteChunkTime > 100)
159
- if (!this.#lastWriteChunkTime || size >= HIGH_WATERMARK || now - this.#lastWriteChunkTime > 100) {
159
+ // or if elapsed 50ms of last send (now - this.#lastWriteChunkTime > 50)
160
+ if (!this.#lastWriteChunkTime || size >= HIGH_WATERMARK || now - this.#lastWriteChunkTime > 50) {
160
161
  this._res.write(Buffer.concat(this.#pendingChunks, size));
161
162
  this.#pendingChunks = [];
162
163
  this.#lastWriteChunkTime = now;
164
+ if(this.#writeTimeout) {
165
+ clearTimeout(this.#writeTimeout);
166
+ this.#writeTimeout = null;
167
+ }
168
+ } else if(!this.#writeTimeout) {
169
+ this.#writeTimeout = setTimeout(() => {
170
+ this.#writeTimeout = null;
171
+ if(!this.finished && !this.aborted) this._res.cork(() => {
172
+ if(this.#pendingChunks.length) {
173
+ const size = this.#pendingChunks.reduce((acc, chunk) => acc + chunk.byteLength, 0);
174
+ this._res.write(Buffer.concat(this.#pendingChunks, size));
175
+ this.#pendingChunks = [];
176
+ this.#lastWriteChunkTime = now;
177
+ }
178
+ });
179
+ }, 50);
163
180
  }
164
181
  this.writingChunk = false;
165
182
  callback(null);
@@ -201,12 +218,13 @@ module.exports = class Response extends Writable {
201
218
  this.statusText = statusMessage;
202
219
  }
203
220
  if(!headers) {
204
- if(!statusMessage) return;
221
+ if(!statusMessage) return this;
205
222
  headers = statusMessage;
206
223
  }
207
224
  for(let header in headers) {
208
225
  this.set(header, headers[header]);
209
226
  }
227
+ return this;
210
228
  }
211
229
  writeHeaders(utf8) {
212
230
  for(const header in this.headers) {
@@ -766,4 +784,4 @@ module.exports = class Response extends Writable {
766
784
  get writableFinished() {
767
785
  return this.finished;
768
786
  }
769
- }
787
+ }
package/src/router.js CHANGED
@@ -365,7 +365,7 @@ module.exports = class Router extends EventEmitter {
365
365
  req.route = route;
366
366
  if(route.optimizedParams) {
367
367
  req.params = {...req.optimizedParams};
368
- } else if(typeof route.path === 'string' && (route.path.includes(':') || route.path.includes('*')) && route.pattern instanceof RegExp) {
368
+ } else if(typeof route.path === 'string' && (route.path.includes(':') || route.path.includes('*') || (route.path.includes('(') && route.path.includes(')'))) && route.pattern instanceof RegExp) {
369
369
  let path = req._originalPath;
370
370
  if(req._stack.length > 0) {
371
371
  path = path.replace(this.getFullMountpath(req), '');
@@ -526,6 +526,9 @@ module.exports = class Router extends EventEmitter {
526
526
  req._opPath += '/';
527
527
  }
528
528
  const routed = await callback._routeRequest(req, res, 0);
529
+ if (req._error) {
530
+ req._errorKey = route.routeKey;
531
+ }
529
532
  if(routed) return resolve(true);
530
533
  next();
531
534
  } else {
package/src/utils.js CHANGED
@@ -57,7 +57,7 @@ function patternToRegex(pattern, isPrefix = false) {
57
57
  .replaceAll('*', '(.*)') // Convert * to .*
58
58
  .replace(/\/:(\w+)(\(.+?\))?\??/g, (match, param, regex) => {
59
59
  const optional = match.endsWith('?');
60
- return `\\/${optional ? '?' : ''}?(?<${param}>${regex ? regex + '($|\\/)' : '[^/]+'})${optional ? '?' : ''}`;
60
+ return `\\/${optional ? '?' : ''}(?<${param}>${regex ? regex + '($|\\/)' : '[^/]+'})${optional ? '?' : ''}`;
61
61
  }); // Convert :param to capture group
62
62
 
63
63
  return new RegExp(`^${regexPattern}${isPrefix ? '(?=$|\/)' : '$'}`);
package/src/view.js CHANGED
@@ -59,26 +59,25 @@ module.exports = class View {
59
59
  this.path += this.ext;
60
60
  }
61
61
  } else {
62
- this.path = path.join(this.root, fileName);
62
+ this.path = this.lookup(fileName);
63
63
  }
64
64
  }
65
65
 
66
66
  lookup(name) {
67
- let path;
67
+ let _path;
68
68
  let roots = [].concat(this.root);
69
- for (let i = 0; i < roots.length && !path; i++) {
69
+ for (let i = 0; i < roots.length && !_path; i++) {
70
70
  const root = roots[i];
71
71
 
72
72
  // resolve the path
73
73
  const loc = path.resolve(root, name);
74
74
  const dir = path.dirname(loc);
75
75
  const file = path.basename(loc);
76
-
76
+
77
77
  // resolve the file
78
- path = this.resolve(dir, file);
78
+ _path = this.resolve(dir, file);
79
79
  }
80
-
81
- return path;
80
+ return _path;
82
81
  }
83
82
 
84
83
  // ill be real idk what exactly this does but express implements it this way
@@ -101,19 +100,19 @@ module.exports = class View {
101
100
  const ext = this.ext;
102
101
 
103
102
  // <path>.<ext>
104
- let path = path.join(dir, file);
105
- let stat = tryStat(path);
103
+ let _path = path.join(dir, file);
104
+ let stat = tryStat(_path);
106
105
 
107
106
  if(stat && stat.isFile()) {
108
- return path;
107
+ return _path;
109
108
  }
110
109
 
111
110
  // <path>/index.<ext>
112
- path = path.join(dir, path.basename(file, ext) + ext);
113
- stat = tryStat(path);
111
+ _path = path.join(dir, path.basename(file, ext), 'index' + ext);
112
+ stat = tryStat(_path);
114
113
 
115
114
  if(stat && stat.isFile()) {
116
- return path;
115
+ return _path;
117
116
  }
118
117
  }
119
118
  }