fulmine.js 5.21.0 → 5.21.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fulmine.js",
3
- "version": "5.21.0",
3
+ "version": "5.21.1",
4
4
  "description": "Drop-in Express 5 replacement on uWebSockets.js, up to 20x faster. Same API, your middleware and framework keep working.",
5
5
  "main": "src/index.js",
6
6
  "exports": {
@@ -116,7 +116,7 @@
116
116
  "statuses": "^2.0.2",
117
117
  "tseep": "^1.3.1",
118
118
  "type-is": "^2.1.0",
119
- "uWebSockets.js": "github:uNetworking/uWebSockets.js#v20.69.0",
119
+ "uWebSockets.js": "github:uNetworking/uWebSockets.js#v20.71.0",
120
120
  "vary": "^1.1.2"
121
121
  },
122
122
  "peerDependencies": {
@@ -17,7 +17,7 @@ See the License for the specific language governing permissions and
17
17
  limitations under the License.
18
18
  */
19
19
 
20
- const uWS = require("uWebSockets.js");
20
+ const { loadUWS } = require("./uws.js");
21
21
  const Router = require("./router.js");
22
22
  const {
23
23
  removeDuplicateSlashes,
@@ -59,7 +59,10 @@ class FSWorker {
59
59
  */
60
60
  constructor() {
61
61
  this.busy = false;
62
- this.worker = new Worker(path.join(__dirname, "worker.js"));
62
+ // its own execArgv, not the parent thread's: a worker inherits them, and a --require or
63
+ // --import written for the parent (Angular's route extraction registers a loader that reads
64
+ // workerData) throws inside a thread that only reads files
65
+ this.worker = new Worker(path.join(__dirname, "worker.js"), { execArgv: [] });
63
66
 
64
67
  this.worker.on("message", (message) => {
65
68
  // node speaks on this channel too: under --watch a worker reports the files it loaded
@@ -113,6 +116,15 @@ class Application extends Router {
113
116
  */
114
117
  _testingCompiled;
115
118
 
119
+ /**
120
+ * The uWS app once made, or the one settings.uwsApp handed in. See the uwsApp getter.
121
+ * @type {any}
122
+ */
123
+ _uwsApp;
124
+
125
+ /** What uWS.App or uWS.SSLApp is given, kept until the app is made. */
126
+ _uwsOptions;
127
+
116
128
  /**
117
129
  * @param {object} [settings] the options express() takes. uwsOptions goes to uWS and decides
118
130
  * between an HTTP, an HTTPS and an HTTP/3 server; threads sizes the file-reading pool, and 0
@@ -135,9 +147,7 @@ class Application extends Router {
135
147
  if (this._clusterWorkers > 0 && cluster.isPrimary) {
136
148
  becomeSupervisor();
137
149
  }
138
- if (settings.uwsApp) {
139
- this.uwsApp = /** @type {import("uWebSockets.js").TemplatedApp} */ (settings.uwsApp);
140
- } else if (settings.http3) {
150
+ if (settings.http3) {
141
151
  // uWS.H3App exists in the pinned build but its QUIC stack does not: the constructor
142
152
  // segfaults on Linux and hangs forever on Windows before serving a single request,
143
153
  // verified 2026-08-05 with uWS alone. A clear throw beats a native crash; this
@@ -146,12 +156,12 @@ class Application extends Router {
146
156
  "http3 is not usable with the pinned uWebSockets.js build: its H3App crashes " +
147
157
  "during construction. Track uNetworking/uWebSockets.js for working QUIC support."
148
158
  );
149
- } else if (settings.uwsOptions.key_file_name && settings.uwsOptions.cert_file_name) {
150
- this.uwsApp = uWS.SSLApp(settings.uwsOptions);
151
- } else {
152
- this.uwsApp = uWS.App(settings.uwsOptions);
153
159
  }
154
160
  this.ssl = settings.uwsOptions.key_file_name && settings.uwsOptions.cert_file_name;
161
+ // the uWS app is made on first use, see the uwsApp getter: what listen(), ws() and the
162
+ // optimizer need, and what an app served through node's http never asks for
163
+ this._uwsApp = settings.uwsApp;
164
+ this._uwsOptions = settings.uwsOptions;
155
165
  this.cache = new NullObject();
156
166
  this.engines = { __proto__: null };
157
167
  // a null prototype, as express gives app.locals, so a local named like an Object method
@@ -509,6 +519,22 @@ class Application extends Router {
509
519
  return request;
510
520
  }
511
521
 
522
+ /**
523
+ * The µWS app underneath, for anything µWS offers that this does not: socket.io attaches to
524
+ * it. Made the first time it is asked for, so an application that only ever answers through
525
+ * node's http, a test through supertest or Angular's build extracting routes in a worker
526
+ * thread, never loads the binary. See src/uws.js for why that matters.
527
+ *
528
+ * @returns {any}
529
+ */
530
+ get uwsApp() {
531
+ if (this._uwsApp === undefined) {
532
+ const uWS = loadUWS();
533
+ this._uwsApp = this.ssl ? uWS.SSLApp(this._uwsOptions) : uWS.App(this._uwsOptions);
534
+ }
535
+ return this._uwsApp;
536
+ }
537
+
512
538
  /**
513
539
  * Registers the catch-all uWS handler, which is what serves every request that no optimized
514
540
  * route took natively. It walks this app's own chain and, when nothing in it answered, decides
@@ -617,7 +643,7 @@ class Application extends Router {
617
643
  // listen() returns. The callback is not: running it here would run it before listen()
618
644
  // had returned, and `const server = app.listen(p, () => server.address())` - the form
619
645
  // the Express docs use - would die on the temporal dead zone.
620
- this.port = uWS.us_socket_local_port(socket);
646
+ this.port = loadUWS().us_socket_local_port(socket);
621
647
  this.listening = true;
622
648
  this._listenHost = host;
623
649
  // kept so close() can stop accepting without dropping what is in flight
@@ -888,7 +914,7 @@ class Application extends Router {
888
914
  return this;
889
915
  }
890
916
  if (this._listenSocket) {
891
- uWS.us_listen_socket_close(this._listenSocket);
917
+ loadUWS().us_listen_socket_close(this._listenSocket);
892
918
  this._listenSocket = undefined;
893
919
  }
894
920
  this._draining = true;
@@ -21,8 +21,7 @@ const acorn = require("acorn");
21
21
  const { stringify, contentTypeSet, withUtf8Charset, contentTypeFor, headerIsWritable } = require("./utils.js");
22
22
  // H3App, DeclarativeResponse and _cfg exist at runtime but are missing from the .d.ts the
23
23
  // package ships, so the module is read through a loose alias
24
- const uWS = require("uWebSockets.js");
25
- const uWSAny = /** @type {any} */ (uWS);
24
+ const { loadUWS } = require("./uws.js");
26
25
  const statuses = require("statuses");
27
26
 
28
27
  /** @typedef {import("./application.js").Application} Application */
@@ -836,7 +835,7 @@ module.exports = function compileDeclarative(cb, app) {
836
835
  return false;
837
836
  }
838
837
 
839
- let decRes = new uWSAny.DeclarativeResponse();
838
+ let decRes = new (loadUWS().DeclarativeResponse)();
840
839
 
841
840
  if (statusCode !== 200) {
842
841
  const statusMessage = statuses.message[statusCode] ?? "unknown";
package/src/index.js CHANGED
@@ -17,11 +17,6 @@ See the License for the specific language governing permissions and
17
17
  limitations under the License.
18
18
  */
19
19
 
20
- // H3App, DeclarativeResponse and _cfg exist at runtime but are missing from the .d.ts the
21
- // package ships, so the module is read through a loose alias
22
- const uWS = require("uWebSockets.js");
23
- const uWSAny = /** @type {any} */ (uWS);
24
-
25
20
  // A project on pnpm owns the uWebSockets.js dependency itself, see `npx fulmine.js pnpm`, so the
26
21
  // one installed can drift from the one this package pins and was tested against. Said once, at
27
22
  // require time, where it reaches every deployment rather than only the ones that run verify.
@@ -50,13 +45,6 @@ const middlewares = require("./middlewares.js");
50
45
  const Request = require("./request.js");
51
46
  const Response = require("./response.js");
52
47
 
53
- try {
54
- // disable Uwebsockets header
55
- uWSAny._cfg("999999990007");
56
- } catch (error) {
57
- // older uWS builds do not expose _cfg; there is nothing to fall back to
58
- }
59
-
60
48
  try {
61
49
  // the compile cache, node 22.8 and up: the next boot skips compiling the same code. Respects
62
50
  // NODE_DISABLE_COMPILE_CACHE, and booting without a cache is not an error
package/src/router.js CHANGED
@@ -100,9 +100,11 @@ module.exports = class Router extends EventEmitter {
100
100
  * it have already established that, so the honest `TemplatedApp|undefined` would only add
101
101
  * casts where the guard already is.
102
102
  *
103
- * @type {any}
103
+ * @returns {any}
104
104
  */
105
- uwsApp;
105
+ get uwsApp() {
106
+ return undefined;
107
+ }
106
108
 
107
109
  /**
108
110
  * Whether an unset routing flag reads on through the mount parent. Only an application does,
package/src/types.d.ts CHANGED
@@ -18,6 +18,7 @@ declare module "fulmine.js" {
18
18
  import e from "express";
19
19
  import uWS from "uWebSockets.js";
20
20
  import { ZlibOptions, BrotliOptions } from "zlib";
21
+ import serveStatic = require("serve-static");
21
22
 
22
23
  type Settings = {
23
24
  uwsOptions?: uWS.AppOptions;
@@ -36,7 +37,16 @@ declare module "fulmine.js" {
36
37
  export import request = e.request;
37
38
  export import response = e.response;
38
39
 
39
- export import static = e.static;
40
+ /** serve-static's options plus preCompressed, which is this project's. */
41
+ interface StaticOptions extends serveStatic.ServeStaticOptions<e.Response> {
42
+ /**
43
+ * Serve `file.br` or `file.gz` in place of `file` when one is on disk and the client
44
+ * takes it. Off by default. Which twins a path has is remembered for a second;
45
+ * `{ cache: false }` asks the disk every time, a duration sets how long.
46
+ */
47
+ preCompressed?: boolean | { cache?: number | string | false };
48
+ }
49
+ function static(root: string, options?: StaticOptions): e.RequestHandler;
40
50
  // export import query = e.query;
41
51
 
42
52
  // express has no compression middleware, so there is nothing to re-export: these are the
package/src/uws.js ADDED
@@ -0,0 +1,45 @@
1
+ /*
2
+ Copyright 2024 dimden.dev
3
+ Copyright 2026 Nigro Simone
4
+
5
+ This file is derived from Ultimate Express and has been modified.
6
+
7
+ Licensed under the Apache License, Version 2.0 (the "License");
8
+ you may not use this file except in compliance with the License.
9
+ You may obtain a copy of the License at
10
+
11
+ http://www.apache.org/licenses/LICENSE-2.0
12
+
13
+ Unless required by applicable law or agreed to in writing, software
14
+ distributed under the License is distributed on an "AS IS" BASIS,
15
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+
17
+ // µWebSockets.js, loaded the first time something needs it rather than when this package is
18
+ // required. An application that is built and never listens never loads the binary. Angular's
19
+ // build imports server.ts in a worker thread to extract the routes and serves it through node's
20
+ // http, and on Windows the binary crashes the process when a thread that loaded it exits
21
+ // (uNetworking/uWebSockets.js#668), so the build only works if nothing in that thread loads it.
22
+
23
+ /** @type {any} */
24
+ let uWS;
25
+
26
+ /**
27
+ * The µWS module. H3App, DeclarativeResponse and _cfg exist at runtime but are missing from the
28
+ * .d.ts the package ships, so it is handed back loosely typed.
29
+ *
30
+ * @returns {any}
31
+ */
32
+ function loadUWS() {
33
+ if (uWS === undefined) {
34
+ uWS = /** @type {any} */ (require("uWebSockets.js"));
35
+ try {
36
+ // disable Uwebsockets header
37
+ uWS._cfg("999999990007");
38
+ } catch (error) {
39
+ // older uWS builds do not expose _cfg; there is nothing to fall back to
40
+ }
41
+ }
42
+ return uWS;
43
+ }
44
+
45
+ module.exports = { loadUWS };