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/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 (const cb of callbacks) {
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
- if (mask & UNKNOWN || (mask & NEXT_PLAIN && terminal && !allowTerminalNext)) {
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
@@ -172,6 +172,7 @@ module.exports = class View {
172
172
  if (stat && stat.isFile()) {
173
173
  return _path;
174
174
  }
175
+ return undefined;
175
176
  }
176
177
  };
177
178
 
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 (behavior[name] !== undefined && typeof behavior[name] !== "function") {
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
@@ -61,6 +61,7 @@ function work(req, res) {
61
61
  }
62
62
 
63
63
  // The order both readers list them in, cheapest first, so a header and a failure message agree.
64
+ /** @type {[keyof Work, string][]} */
64
65
  const NAMES = [
65
66
  ["headers", "headers"],
66
67
  ["query", "query"],