fulmine.js 5.21.2 → 5.21.4
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 -3
- package/package.json +5 -1
- package/src/adopt.js +4 -1
- package/src/application.js +11 -2
- package/src/cli.js +1 -0
- package/src/compression.js +40 -3
- package/src/declarative.js +66 -25
- package/src/lazy-readable.js +15 -15
- package/src/lazy-writable.js +15 -15
- package/src/middlewares.js +24 -11
- package/src/nest.js +2 -0
- package/src/optimizer.js +65 -7
- package/src/options.d.ts +1 -1
- package/src/request.js +22 -16
- package/src/response-utils.js +41 -4
- package/src/response.js +102 -62
- package/src/route.js +5 -3
- package/src/router-utils.js +40 -5
- package/src/router.js +23 -9
- package/src/server-shape.js +3 -3
- package/src/server-timing.js +21 -0
- package/src/testing.js +2 -1
- package/src/usage.js +40 -3
- package/src/utils.js +4 -1
- package/src/verify.js +2 -0
- package/src/view.js +1 -0
- package/src/websocket.js +2 -1
- package/src/work.js +1 -0
package/src/usage.js
CHANGED
|
@@ -74,7 +74,7 @@ const verdicts = new WeakMap();
|
|
|
74
74
|
* What one callback provably does, as a mask of the facts above. The default is UNKNOWN: any shape
|
|
75
75
|
* this walk does not understand, and any alias of req, res or next, could do anything.
|
|
76
76
|
*
|
|
77
|
-
* @param {Function} fn
|
|
77
|
+
* @param {Function & {[kGetSafe]?: boolean}} fn
|
|
78
78
|
* @returns {number}
|
|
79
79
|
*/
|
|
80
80
|
function callbackUsage(fn) {
|
|
@@ -143,6 +143,20 @@ function analyze(fn) {
|
|
|
143
143
|
if (mask & UNKNOWN) {
|
|
144
144
|
return;
|
|
145
145
|
}
|
|
146
|
+
// a member read off what a call returned: res.status(200).sendFile() is a sendFile on res,
|
|
147
|
+
// every chainable method returns res itself, so it is judged as one. Reading it off the
|
|
148
|
+
// call had let sendFile and redirect through, and a Range or an Accept then went unread
|
|
149
|
+
if (
|
|
150
|
+
node.type === "MemberExpression" &&
|
|
151
|
+
node.object.type === "CallExpression" &&
|
|
152
|
+
resName !== null &&
|
|
153
|
+
chainRoot(node.object) === resName
|
|
154
|
+
) {
|
|
155
|
+
if (node.computed || !RES_OK.has(node.property.name)) {
|
|
156
|
+
mask |= UNKNOWN;
|
|
157
|
+
}
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
146
160
|
if (node.type === "MemberExpression" && !node.computed && node.object.type === "Identifier") {
|
|
147
161
|
const owner = node.object.name;
|
|
148
162
|
if (owner !== reqName) {
|
|
@@ -227,6 +241,25 @@ function analyze(fn) {
|
|
|
227
241
|
return mask;
|
|
228
242
|
}
|
|
229
243
|
|
|
244
|
+
/**
|
|
245
|
+
* The identifier a chain of members and calls starts from: res for res.status(200).sendFile().
|
|
246
|
+
*
|
|
247
|
+
* @param {any} node
|
|
248
|
+
* @returns {string|null} null when the chain does not start from a plain identifier
|
|
249
|
+
*/
|
|
250
|
+
function chainRoot(node) {
|
|
251
|
+
while (node) {
|
|
252
|
+
if (node.type === "MemberExpression") {
|
|
253
|
+
node = node.object;
|
|
254
|
+
} else if (node.type === "CallExpression") {
|
|
255
|
+
node = node.callee;
|
|
256
|
+
} else {
|
|
257
|
+
return node.type === "Identifier" ? node.name : null;
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
return null;
|
|
261
|
+
}
|
|
262
|
+
|
|
230
263
|
/**
|
|
231
264
|
* Walks every node by key, handing each its parent.
|
|
232
265
|
*
|
|
@@ -275,12 +308,16 @@ function chainUsage(chain, allowTerminalNext) {
|
|
|
275
308
|
return none;
|
|
276
309
|
}
|
|
277
310
|
const terminal = i === chain.length - 1;
|
|
278
|
-
for (
|
|
311
|
+
for (let j = 0; j < callbacks.length; j++) {
|
|
312
|
+
const cb = callbacks[j];
|
|
279
313
|
if (typeof cb !== "function") {
|
|
280
314
|
return none;
|
|
281
315
|
}
|
|
282
316
|
const mask = callbackUsage(cb);
|
|
283
|
-
|
|
317
|
+
// a bare next() leaves the chain only from the route's last callback: from one in
|
|
318
|
+
// front of it, the step lands on the next callback of the same route, judged here too
|
|
319
|
+
const last = terminal && j === callbacks.length - 1;
|
|
320
|
+
if (mask & UNKNOWN || (mask & NEXT_PLAIN && last && !allowTerminalNext)) {
|
|
284
321
|
return none;
|
|
285
322
|
}
|
|
286
323
|
if (mask & QUERY) {
|
package/src/utils.js
CHANGED
|
@@ -896,6 +896,8 @@ function negotiateEncoding(accept, allowed) {
|
|
|
896
896
|
return best;
|
|
897
897
|
}
|
|
898
898
|
|
|
899
|
+
// a function here is called with the application at construction, see Application's constructor
|
|
900
|
+
/** @type {Record<string, unknown>} */
|
|
899
901
|
const defaultSettings = {
|
|
900
902
|
"jsonp callback name": "callback",
|
|
901
903
|
env: () => process.env.NODE_ENV ?? "development",
|
|
@@ -1501,6 +1503,7 @@ function headerIsWritable(name, value) {
|
|
|
1501
1503
|
}
|
|
1502
1504
|
|
|
1503
1505
|
// the status send picks for a failed stat; anything else is the server's 500
|
|
1506
|
+
/** @type {Record<string, number>} */
|
|
1504
1507
|
const STAT_ERROR_STATUS = { ENAMETOOLONG: 404, ENOTDIR: 404, ENOENT: 404 };
|
|
1505
1508
|
|
|
1506
1509
|
/**
|
|
@@ -1548,7 +1551,7 @@ function httpErrorName(status) {
|
|
|
1548
1551
|
*/
|
|
1549
1552
|
function asStatError(err) {
|
|
1550
1553
|
err.expose = false;
|
|
1551
|
-
err.statusCode = STAT_ERROR_STATUS[err.code] ?? 500;
|
|
1554
|
+
err.statusCode = STAT_ERROR_STATUS[/** @type {string} */ (err.code)] ?? 500;
|
|
1552
1555
|
err.status = err.statusCode;
|
|
1553
1556
|
return err;
|
|
1554
1557
|
}
|
package/src/verify.js
CHANGED
|
@@ -36,6 +36,7 @@ const SWAP_IMAGE = `node:${MIN_NODE_MAJOR}-trixie-slim`;
|
|
|
36
36
|
|
|
37
37
|
// What a project may carry that needs a different API here. Everything that just works is
|
|
38
38
|
// `npx fulmine migrate`'s business.
|
|
39
|
+
/** @type {Record<string, string>} */
|
|
39
40
|
const NEEDS_A_LOOK = {
|
|
40
41
|
"socket.io": "attach it with io.attachApp(app.uwsApp), not io.attach(server): there is no node socket to take over",
|
|
41
42
|
ws: "the websocket server is µWS's own, through app.ws(path, behavior)",
|
|
@@ -191,6 +192,7 @@ function checkBinary(platform = process.platform, arch = process.arch, abi = pro
|
|
|
191
192
|
* @returns {string}
|
|
192
193
|
*/
|
|
193
194
|
function abiToNode(abi) {
|
|
195
|
+
/** @type {Record<string, string>} */
|
|
194
196
|
const known = { 108: "18", 115: "20", 127: "22", 131: "23", 137: "24", 147: "26" };
|
|
195
197
|
return known[abi] ?? `ABI ${abi}`;
|
|
196
198
|
}
|
package/src/view.js
CHANGED
package/src/websocket.js
CHANGED
|
@@ -237,8 +237,9 @@ function checkBehavior(path, behavior) {
|
|
|
237
237
|
"parameters that are a whole segment, as in /room/:id."
|
|
238
238
|
);
|
|
239
239
|
}
|
|
240
|
+
const handlers = /** @type {Record<string, unknown>} */ (behavior);
|
|
240
241
|
for (const name of [...SOCKET_HANDLERS, "upgrade"]) {
|
|
241
|
-
if (
|
|
242
|
+
if (handlers[name] !== undefined && typeof handlers[name] !== "function") {
|
|
242
243
|
throw new TypeError(`app.ws() behavior.${name} must be a function`);
|
|
243
244
|
}
|
|
244
245
|
}
|
package/src/work.js
CHANGED