fulmine.js 5.19.0 → 5.19.2
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 +3 -1
- package/package.json +7 -5
- package/src/cli.js +79 -8
package/README.md
CHANGED
|
@@ -32,7 +32,7 @@ See [Migrating](#migrating) for what it handles and what it deliberately does no
|
|
|
32
32
|
|
|
33
33
|
[](https://www.npmjs.com/package/fulmine.js)
|
|
34
34
|
[](https://nodejs.org)
|
|
35
|
-
[](https://www.http-arena.com/#tuned=0)
|
|
35
|
+
[](https://www.http-arena.com/#tuned=0)
|
|
36
36
|
[](https://coveralls.io/github/nigrosimone/fulmine.js?branch=main)
|
|
37
37
|
[](https://github.com/nigrosimone/fulmine.js/actions/workflows/codeql.yml)
|
|
38
38
|
[](https://scorecard.dev/viewer/?uri=github.com/nigrosimone/fulmine.js)
|
|
@@ -624,6 +624,8 @@ app.listen(3000, () => console.log(`worker ${process.pid} listening`));
|
|
|
624
624
|
|
|
625
625
|
Anything held per process is now held per worker: an in-memory cache, a rate-limit counter, a session store or a `Map` of connected sockets is not shared, and needs Redis or something like it to be. `app.close()` in the primary stops the workers, and a `SIGTERM` or `SIGINT` that reaches only the primary, which is what a container sends, is passed on to them. Runnable: [`examples/cluster.js`](./examples/cluster.js).
|
|
626
626
|
|
|
627
|
+
10. `app.set("connection headers", false)` stops `Connection: keep-alive` and `Keep-Alive: timeout=10` going out on every response. Express sends both, so Fulmine sends both by default. An HTTP/1.1 connection stays open without being told, so to an HTTP/1.1 client the two headers say nothing it does not know already, and they cost 46 bytes and two header writes per response. A request that asked for `Connection: close` still gets `Connection: close`, and the connection is closed. Turn it off for an API behind a proxy or serving HTTP/1.1 clients; keep the default where a client or a proxy relies on the header to keep the connection open. Worth 2% to 3.5% here on a route that is not compiled, plus the bytes.
|
|
628
|
+
|
|
627
629
|
## WebSockets
|
|
628
630
|
|
|
629
631
|
`app.ws()` registers a WebSocket route, served by µWS itself. The upgrade never reaches node, so `server.on("upgrade")` and the libraries built on it have nothing to hear; this is the replacement.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fulmine.js",
|
|
3
|
-
"version": "5.19.
|
|
3
|
+
"version": "5.19.2",
|
|
4
4
|
"description": "Drop-in Express 5 replacement on uWebSockets.js. Your existing middleware keeps working.",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"exports": {
|
|
@@ -119,9 +119,9 @@
|
|
|
119
119
|
"@commitlint/cli": "^21.2.1",
|
|
120
120
|
"@commitlint/config-conventional": "^21.2.0",
|
|
121
121
|
"@eslint/js": "^10.0.1",
|
|
122
|
-
"@nestjs/common": "^
|
|
123
|
-
"@nestjs/core": "^
|
|
124
|
-
"@nestjs/platform-express": "^
|
|
122
|
+
"@nestjs/common": "^12.0.1",
|
|
123
|
+
"@nestjs/core": "^12.0.1",
|
|
124
|
+
"@nestjs/platform-express": "^12.0.1",
|
|
125
125
|
"@release-it/conventional-changelog": "^12.0.0",
|
|
126
126
|
"@types/accepts": "^1.3.7",
|
|
127
127
|
"@types/bytes": "^3.1.5",
|
|
@@ -182,7 +182,7 @@
|
|
|
182
182
|
"pako": "^3.0.1",
|
|
183
183
|
"passport": "^0.7.0",
|
|
184
184
|
"passport-local": "^1.0.0",
|
|
185
|
-
"pkg-pr-new": "^0.0.
|
|
185
|
+
"pkg-pr-new": "^0.0.88",
|
|
186
186
|
"prettier": "^3.9.6",
|
|
187
187
|
"pug": "^3.0.4",
|
|
188
188
|
"reflect-metadata": "^0.2.2",
|
|
@@ -198,6 +198,8 @@
|
|
|
198
198
|
"swagger-ui-express": "^5.0.1",
|
|
199
199
|
"swig": "^1.4.2",
|
|
200
200
|
"tsd": "^0.33.0",
|
|
201
|
+
"typescript": "^6.0.3",
|
|
202
|
+
"typescript7": "npm:typescript@^7.0.2",
|
|
201
203
|
"vhost": "^3.0.2"
|
|
202
204
|
},
|
|
203
205
|
"contributors": [
|
package/src/cli.js
CHANGED
|
@@ -143,19 +143,42 @@ function collectFiles(dir) {
|
|
|
143
143
|
}
|
|
144
144
|
|
|
145
145
|
/**
|
|
146
|
-
*
|
|
146
|
+
* A reader for the .ts files of the project being migrated, or null when it has no TypeScript.
|
|
147
147
|
*
|
|
148
148
|
* acorn cannot read TypeScript, and shipping a parser that can would put megabytes into this
|
|
149
149
|
* package for a command most people run once. A TypeScript project already has the compiler, so
|
|
150
150
|
* it is resolved from there. A project without one is told its .ts files were left alone rather
|
|
151
151
|
* than having them quietly skipped, which is what happened before they were looked at at all.
|
|
152
152
|
*
|
|
153
|
+
* typescript 7 is the compiler rewritten in Go, and it publishes no JavaScript parser any more:
|
|
154
|
+
* require("typescript") gives back a version number and nothing else. Its scanner survives, on an
|
|
155
|
+
* ESM-only subpath that require() reads on every node this package supports, so 7 gets the token
|
|
156
|
+
* walk and 6 keeps the tree.
|
|
157
|
+
*
|
|
153
158
|
* @param {string} target directory being migrated
|
|
154
|
-
* @returns {
|
|
159
|
+
* @returns {((source: string, fileName: string, seen?: Set<string>) => {start: number, end: number}[])|null}
|
|
155
160
|
*/
|
|
156
161
|
function loadTypeScript(target) {
|
|
162
|
+
/** @param {string} name */
|
|
163
|
+
const load = (name) => require(require.resolve(name, { paths: [target, process.cwd()] }));
|
|
164
|
+
|
|
157
165
|
try {
|
|
158
|
-
|
|
166
|
+
const ts = load("typescript");
|
|
167
|
+
if (typeof ts.createSourceFile === "function") {
|
|
168
|
+
return (source, fileName, seen) => findSpecifiersTypeScript(source, fileName, ts, seen);
|
|
169
|
+
}
|
|
170
|
+
} catch {
|
|
171
|
+
return null;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
try {
|
|
175
|
+
const { createScanner } = load("typescript/unstable/ast/scanner");
|
|
176
|
+
const { LanguageVariant, SyntaxKind } = load("typescript/unstable/ast");
|
|
177
|
+
// an unstable subpath, so the name the token loop stops on is checked here rather than read
|
|
178
|
+
// as undefined and scanned past the end of the file forever
|
|
179
|
+
if (SyntaxKind.EndOfFile === undefined) return null;
|
|
180
|
+
const ts = { createScanner, LanguageVariant, SyntaxKind };
|
|
181
|
+
return (source, fileName, seen) => findSpecifiersScanner(source, fileName, ts, seen);
|
|
159
182
|
} catch {
|
|
160
183
|
return null;
|
|
161
184
|
}
|
|
@@ -222,6 +245,53 @@ function findSpecifiersTypeScript(source, fileName, ts, seen) {
|
|
|
222
245
|
return found;
|
|
223
246
|
}
|
|
224
247
|
|
|
248
|
+
/**
|
|
249
|
+
* The same specifiers again, read from the token stream instead of from a tree. typescript 7 has
|
|
250
|
+
* no parser to give a tree, and its scanner already does the part that a search over the text gets
|
|
251
|
+
* wrong: comments, template literals and strings that only look like imports are not tokens here.
|
|
252
|
+
*
|
|
253
|
+
* @param {string} source
|
|
254
|
+
* @param {string} fileName decides whether JSX is allowed, as above
|
|
255
|
+
* @param {any} ts the scanner and the two enums loadTypeScript kept
|
|
256
|
+
* @param {Set<string>} [seen] as in findSpecifiers
|
|
257
|
+
* @returns {{start: number, end: number}[]}
|
|
258
|
+
*/
|
|
259
|
+
function findSpecifiersScanner(source, fileName, ts, seen) {
|
|
260
|
+
const kind = ts.SyntaxKind;
|
|
261
|
+
const scanner = ts.createScanner(
|
|
262
|
+
true,
|
|
263
|
+
fileName.endsWith(".tsx") ? ts.LanguageVariant.JSX : ts.LanguageVariant.Standard,
|
|
264
|
+
source
|
|
265
|
+
);
|
|
266
|
+
|
|
267
|
+
/** @type {{start: number, end: number}[]} */
|
|
268
|
+
const found = [];
|
|
269
|
+
// the three tokens before the one in hand, newest first
|
|
270
|
+
let back1 = -1;
|
|
271
|
+
let back2 = -1;
|
|
272
|
+
let back3 = -1;
|
|
273
|
+
|
|
274
|
+
for (let token = scanner.scan(); token !== kind.EndOfFile; token = scanner.scan()) {
|
|
275
|
+
// from "express", and import("express") or require("express") as a call. The dot rules out
|
|
276
|
+
// obj.require("express"), which is somebody else's require and not a module specifier
|
|
277
|
+
const isFrom = back1 === kind.FromKeyword;
|
|
278
|
+
const isRequire = back2 === kind.RequireKeyword && back3 !== kind.DotToken && back3 !== kind.QuestionDotToken;
|
|
279
|
+
const isCall = back1 === kind.OpenParenToken && (back2 === kind.ImportKeyword || isRequire);
|
|
280
|
+
if (token === kind.StringLiteral && (isFrom || isCall)) {
|
|
281
|
+
const text = scanner.getTokenValue();
|
|
282
|
+
if (text === FROM) {
|
|
283
|
+
found.push({ start: scanner.getTokenStart(), end: scanner.getTokenEnd() });
|
|
284
|
+
} else if (seen && BUILT_IN_INSTEAD[text]) {
|
|
285
|
+
seen.add(text);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
back3 = back2;
|
|
289
|
+
back2 = back1;
|
|
290
|
+
back1 = token;
|
|
291
|
+
}
|
|
292
|
+
return found;
|
|
293
|
+
}
|
|
294
|
+
|
|
225
295
|
/**
|
|
226
296
|
* The string literals naming the module, found through the parser rather than by searching the
|
|
227
297
|
* text. "express" appears inside express-session, inside comments and inside strings that are not
|
|
@@ -874,7 +944,7 @@ Options:
|
|
|
874
944
|
|
|
875
945
|
// resolved once, and only if there is anything to use it on
|
|
876
946
|
const hasTypeScriptFiles = files.some((file) => TYPESCRIPT_EXTENSIONS.has(path.extname(file)));
|
|
877
|
-
const
|
|
947
|
+
const readTypeScript = hasTypeScriptFiles ? loadTypeScript(target) : null;
|
|
878
948
|
|
|
879
949
|
for (const file of files) {
|
|
880
950
|
const source = fs.readFileSync(file, "utf8");
|
|
@@ -883,14 +953,15 @@ Options:
|
|
|
883
953
|
if (!source.includes(FROM) && !Object.keys(BUILT_IN_INSTEAD).some((name) => source.includes(name))) continue;
|
|
884
954
|
|
|
885
955
|
const isTypeScript = TYPESCRIPT_EXTENSIONS.has(path.extname(file));
|
|
886
|
-
if (isTypeScript && !
|
|
956
|
+
if (isTypeScript && !readTypeScript) {
|
|
887
957
|
needTypeScript.push(path.relative(target, file));
|
|
888
958
|
continue;
|
|
889
959
|
}
|
|
890
960
|
|
|
891
|
-
const specifiers =
|
|
892
|
-
|
|
893
|
-
|
|
961
|
+
const specifiers =
|
|
962
|
+
isTypeScript && readTypeScript
|
|
963
|
+
? readTypeScript(source, file, builtInInstead)
|
|
964
|
+
: findSpecifiers(source, builtInInstead);
|
|
894
965
|
if (specifiers === null) {
|
|
895
966
|
unparsed.push(path.relative(target, file));
|
|
896
967
|
continue;
|